index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. <template>
  2. <div class="navbar-group">
  3. <div class="logo">
  4. <img :src="logo" alt="logo" @click="goSiteHome">
  5. </div>
  6. <div class="navbar-content-group">
  7. <!-- 头部搜索模块 -->
  8. <navbar-search class="search-module"></navbar-search>
  9. <div class="nav-group">
  10. <div
  11. class="nav-item"
  12. :class="nav.class"
  13. v-for="(nav, index) in navs"
  14. :key="index"
  15. @click="onSelectNav(nav)"
  16. >
  17. <template v-if="nav.plugin">
  18. <navbar-item :badge="nav.badge" :svg="nav.svg" :nav="nav" v-popover:[nav.plugin]></navbar-item>
  19. </template>
  20. <navbar-item :badge="nav.badge" :svg="nav.svg" :nav="nav" v-else></navbar-item>
  21. </div>
  22. <!-- 提供自定义用户信息插槽 -->
  23. <slot name="nav-user-info" v-bind:info="userInfo"></slot>
  24. <el-popover
  25. popper-class="nav-popover"
  26. ref="navInfo"
  27. placement="bottom"
  28. trigger="hover"
  29. :visible-arrow="false"
  30. >
  31. <div class="nav-user-info-group">
  32. <navbar-item :nav="{ icon: userInfo.avatar }" mode="vertical">
  33. <div class="info-group">
  34. <span v-if="userInfo.name">{{ userInfo.name }}</span>
  35. <span v-if="userInfo.phone">{{ userInfo.phone }}</span>
  36. </div>
  37. </navbar-item>
  38. <button @click="onClickOut">退出登录</button>
  39. </div>
  40. </el-popover>
  41. <el-popover
  42. popper-class="nav-popover"
  43. ref="navCustomer"
  44. placement="bottom"
  45. trigger="hover"
  46. :visible-arrow="false"
  47. >
  48. <div class="nav-custom-info-group">
  49. <div class="info-item" v-if="hasExclusiveCustomer">
  50. <div class="after-tag-box recommend-tag">推荐</div>
  51. <navbar-item
  52. :nav="{ label: exclusiveCustomerInfo.name, icon: 'icon-weixin_line' }"></navbar-item>
  53. <navbar-item
  54. class="qrcode-group"
  55. :nav="{
  56. label: '微信扫一扫',
  57. icon: exclusiveCustomerInfo.qrcode
  58. }"
  59. mode="vertical"
  60. >
  61. </navbar-item>
  62. </div>
  63. <div class="info-item" @click="onClickCustomer('在线咨询')">
  64. <navbar-item :nav="{ label: '在线咨询', icon: 'icon-kefu_xian' }" :svg="true"></navbar-item>
  65. </div>
  66. <div class="info-item" @click="onClickCustomer('客服热线')">
  67. <navbar-item :nav="{ label: '客服热线:400-108-6670', icon: 'icon-telphone_line' }"></navbar-item>
  68. </div>
  69. </div>
  70. </el-popover>
  71. <el-popover
  72. popper-class="nav-popover"
  73. ref="navMessage"
  74. placement="bottom"
  75. trigger="hover"
  76. :visible-arrow="false"
  77. >
  78. <div class="nav-message-group">
  79. <div class="message-header">
  80. <div class="title">
  81. 消息中心 <span>(<span class="highlight-text">{{ messageCount }}</span> 条未读)</span>
  82. </div>
  83. <JyIcon @click="closeMessagePopover" name="close"></JyIcon>
  84. </div>
  85. <div class="message-group" v-if="messageList.length">
  86. <div
  87. v-for="(message, index) in messageList"
  88. :key="index"
  89. class="message-item"
  90. @click="onClickMessage(message)"
  91. >
  92. <el-badge :isDot="!message.read" class="message-icon">
  93. <img :src="message.icon" alt="icon">
  94. </el-badge>
  95. <div class="message-content">
  96. <div class="message-content--header">
  97. <div class="message-content--title ellipsis">
  98. {{ message.title }}
  99. </div>
  100. <div class="message-content--time">
  101. {{ message.time }}
  102. </div>
  103. </div>
  104. <div class="message-content--content">
  105. {{ message.content }}
  106. </div>
  107. </div>
  108. </div>
  109. </div>
  110. <empty-tip v-else tip="暂未接收到信息"></empty-tip>
  111. <button @click="onClickMessageAll">查看全部</button>
  112. </div>
  113. </el-popover>
  114. </div>
  115. </div>
  116. </div>
  117. </template>
  118. <script>
  119. import NavbarItem from './components/item'
  120. import NavbarSearch from './components/search'
  121. import { mapActions, mapGetters, mapState } from 'vuex'
  122. import EmptyTip from '../Empty'
  123. import {debounce} from 'lodash'
  124. export default {
  125. name: 'header-navbar',
  126. components: {
  127. NavbarSearch,
  128. [NavbarItem.name]: NavbarItem,
  129. [NavbarSearch.name]: NavbarSearch,
  130. [EmptyTip.name]: EmptyTip
  131. },
  132. data () {
  133. return {
  134. home: process.env.VUE_APP_BASE_SITE,
  135. logo: process.env.VUE_APP_BASE_LOGO,
  136. navs: [
  137. {
  138. label: '前往官网',
  139. icon: 'icon-houtui'
  140. },
  141. {
  142. label: '消息中心',
  143. icon: 'icon-a-Property1gongzuozhuomianProperty2xiaoxizhongxinProperty3grey',
  144. badge: 0,
  145. plugin: 'navMessage'
  146. },
  147. {
  148. label: '客服',
  149. icon: 'icon-kefu_xian',
  150. svg: true,
  151. plugin: 'navCustomer'
  152. }
  153. ]
  154. }
  155. },
  156. computed: {
  157. ...mapState('work-bench/navbar', [
  158. 'messageCount'
  159. ]),
  160. ...mapGetters('work-bench/user', [
  161. 'hasExclusiveCustomer',
  162. 'exclusiveCustomerInfo',
  163. 'userInfo'
  164. ]),
  165. ...mapGetters('work-bench/navbar', [
  166. 'messageList'
  167. ]),
  168. // 获取是否启用自定义顶部导航用户信息自定义
  169. ...mapGetters('work-bench', [
  170. 'useCustomStatus'
  171. ])
  172. },
  173. created () {
  174. // 判断是否配置启用自定义用户信息模块,提供默认配置
  175. if (!this.useCustomStatus?.['nav-user-info']) {
  176. this.navs.push({
  177. label: '',
  178. icon: 'https://www.jianyu360.cn/common-module/public/image/auto.png',
  179. plugin: 'navInfo',
  180. class: 'nav-user-info'
  181. })
  182. }
  183. /**
  184. *
  185. * TODO 获取用户数据、消息数据 待优化至应用层处理
  186. */
  187. this.getPower()
  188. this.getUserSimpleInfo().then(() => {
  189. const userInfo = this.navs.find(v => v.plugin === 'navInfo')
  190. if (userInfo) {
  191. userInfo.label = this.userInfo.name || this.userInfo.phone
  192. userInfo.icon = this.userInfo.avatar
  193. }
  194. })
  195. this.getMessageInfo()
  196. },
  197. methods: {
  198. ...mapActions('work-bench', [
  199. 'getPower',
  200. 'getUserSimpleInfo',
  201. 'setSignOut',
  202. 'getMessageCount',
  203. 'getMessages',
  204. 'setClickMessage',
  205. 'setReadMessage'
  206. ]),
  207. /**
  208. * 获取消息数据
  209. */
  210. getMessageInfo () {
  211. this.getMessageCount().then(() => {
  212. this.navs.find(v => v.label === '消息中心').badge = this.messageCount
  213. /**
  214. * 对外提供未读消息条数更新事件
  215. */
  216. this.$BRACE.$emit('update-message-count', this.messageCount)
  217. })
  218. this.getMessages()
  219. },
  220. /**
  221. * 点击退出登录
  222. */
  223. onClickOut () {
  224. this.setSignOut()
  225. },
  226. /**
  227. * 点击选中导航 Item
  228. */
  229. onSelectNav (nav) {
  230. switch (nav.label) {
  231. case '消息中心': {
  232. this.goSiteMessage()
  233. break
  234. }
  235. case '前往官网': {
  236. this.goSiteHome()
  237. break
  238. }
  239. }
  240. },
  241. /**
  242. * 前往消息中心页面
  243. */
  244. goSiteMessage () {
  245. this.$BRACE.$emit({
  246. fKey: 'goSiteMessage',
  247. spareFn: (link) => this.$BRACE.methods.open({
  248. route: { link }
  249. })
  250. }, '/swordfish/frontPage/messageCenter/sess/index')
  251. },
  252. /**
  253. * 前往官网首页
  254. */
  255. goSiteHome () {
  256. this.$BRACE.$emit({
  257. fKey: 'goSiteHome',
  258. spareFn: (link) => this.$BRACE.methods.open({
  259. route: { link }
  260. })
  261. }, this.home)
  262. },
  263. /**
  264. * 关闭消息中心 popover
  265. */
  266. closeMessagePopover () {
  267. this.$refs.navMessage.doClose()
  268. },
  269. /**
  270. * 点击消息 Item, 设置已读、打开消息详情
  271. * @param message
  272. */
  273. onClickMessage: debounce(async function (message) {
  274. await this.setClickMessage({
  275. id: message.origin.msgLogId
  276. })
  277. if (message.read) {
  278. if (message.link) {
  279. location.href = message.link
  280. }
  281. } else {
  282. /**
  283. * 对外提供消息更新事件
  284. */
  285. const params = {
  286. id: message.origin.id,
  287. type: message.origin.msg_type
  288. }
  289. this.$BRACE.$emit('read-message', {
  290. type: 'start',
  291. params
  292. })
  293. await this.setReadMessage(params).then(() => {
  294. this.$BRACE.$emit('read-message', {
  295. type: 'success',
  296. params
  297. })
  298. if (message.link) {
  299. location.href = message.link
  300. } else {
  301. this.getMessageInfo()
  302. }
  303. }).catch(e => {
  304. this.$BRACE.$emit('read-message', {
  305. type: 'error',
  306. params
  307. })
  308. })
  309. }
  310. }, 300),
  311. /**
  312. * 关闭消息中心
  313. */
  314. onClickMessageAll () {
  315. this.closeMessagePopover()
  316. this.onSelectNav({
  317. label: '消息中心'
  318. })
  319. },
  320. /**
  321. * 点击客服 Item
  322. * @param type - 类型
  323. */
  324. onClickCustomer (type) {
  325. this.$BRACE.$emit('click-nav-customer', type)
  326. }
  327. }
  328. }
  329. </script>
  330. <style lang="scss">
  331. .flex-horizontal {
  332. display: flex;
  333. flex-direction: row;
  334. align-items: center;
  335. }
  336. .flex-vertical {
  337. display: flex;
  338. flex-direction: column;
  339. align-items: center;
  340. justify-content: center;
  341. }
  342. .nav-popover {
  343. &.el-popper[x-placement^=bottom] {
  344. margin-top: 20px;
  345. }
  346. padding: 0;
  347. border: none;
  348. background: #FFFFFF;
  349. box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.1000), 0px 8px 10px 1px rgba(0, 0, 0, 0.0600), 0px 3px 14px 2px rgba(0, 0, 0, 0.0500);
  350. border-radius: 8px;
  351. overflow: hidden;
  352. }
  353. .nav-custom-info-group {
  354. min-width: 229px;
  355. .info-item {
  356. position: relative;
  357. padding: 12px 16px;
  358. .after-tag-box {
  359. position: absolute;
  360. right: 0;
  361. top: 16px;
  362. }
  363. .qrcode-group {
  364. padding-bottom: 4px;
  365. .img-icon {
  366. width: 90px;
  367. height: 90px;
  368. border-radius: unset;
  369. margin-right: 0;
  370. }
  371. span {
  372. margin-left: 0;
  373. }
  374. }
  375. span {
  376. margin-left: 4px;
  377. font-size: 14px;
  378. color: #686868;
  379. line-height: 22px;
  380. }
  381. & + .info-item {
  382. border-top: 1px solid #ECECEC;
  383. }
  384. }
  385. }
  386. .nav-user-info-group {
  387. width: 150px;
  388. padding-top: 18px;
  389. .info-group {
  390. width: 100%;
  391. @extend .flex-vertical;
  392. padding: 0 16px 8px 18px;
  393. box-sizing: border-box;
  394. span {
  395. width: 100%;
  396. text-align: center;
  397. display: inline-block;
  398. overflow: hidden;
  399. text-overflow: ellipsis;
  400. white-space: nowrap;
  401. }
  402. }
  403. button {
  404. width: 100%;
  405. height: 34px;
  406. background: #F5F6F7;
  407. text-align: center;
  408. font-size: 12px;
  409. font-weight: 400;
  410. color: #686868;
  411. cursor: pointer;
  412. }
  413. }
  414. .nav-message-group {
  415. width: 400px;
  416. .message-header {
  417. @extend .flex-horizontal;
  418. padding: 16px 24px;
  419. font-size: 16px;
  420. color: #1D1D1D;
  421. line-height: 24px;
  422. box-shadow: inset 0px -1px 0px 1px rgba(0, 0, 0, 0.0500);
  423. .title {
  424. flex: 1;
  425. }
  426. span {
  427. font-size: 14px;
  428. color: #686868;
  429. &.highlight-text {
  430. color: #2ABED1;
  431. }
  432. }
  433. .icon-close {
  434. font-size: 20px;
  435. cursor: pointer;
  436. color: #AAAAAA;
  437. &:hover {
  438. color: #2cb7ca;
  439. }
  440. }
  441. }
  442. .message-group {
  443. flex: 1;
  444. max-width: 100%;
  445. max-height: 263px;
  446. overflow-y: scroll;
  447. }
  448. .message-item {
  449. @extend .flex-horizontal;
  450. align-items: flex-start;
  451. width: 100%;
  452. max-width: 100%;
  453. padding: 12px 24px;
  454. box-sizing: border-box;
  455. border-bottom: 1px solid #ECECEC;
  456. cursor: pointer;
  457. &:last-child {
  458. border-bottom: unset;
  459. }
  460. &:hover {
  461. background-color: #f5f6f7;
  462. }
  463. }
  464. .message-icon {
  465. flex-shrink: 0;
  466. width: 32px;
  467. height: 32px;
  468. border-radius: 50%;
  469. img {
  470. border-radius: 50%;
  471. overflow: hidden;
  472. }
  473. border: 1px solid rgba(0, 0, 0, 0.0500);
  474. margin-right: 12px;
  475. .el-badge__content.is-fixed.is-dot {
  476. right: 8px;
  477. top: 2px;
  478. }
  479. }
  480. .message-content {
  481. width: 316px;
  482. display: flex;
  483. flex: 1;
  484. flex-wrap: wrap;
  485. flex-direction: column;
  486. &--header {
  487. @extend .flex-horizontal;
  488. width: 100%;
  489. flex: 1;
  490. justify-content: space-between;
  491. }
  492. &--content {
  493. margin-top: 4px;
  494. font-size: 13px;
  495. color: #686868;
  496. line-height: 20px;
  497. }
  498. &--title {
  499. font-size: 14px;
  500. color: #1D1D1D;
  501. line-height: 22px;
  502. }
  503. &--time {
  504. flex-shrink: 0;
  505. font-size: 12px;
  506. color: #999999;
  507. line-height: 18px;
  508. }
  509. }
  510. button {
  511. background-color: transparent;
  512. border-top: 1px solid #ECECEC;
  513. width: 100%;
  514. font-size: 16px;
  515. color: #1D1D1D;
  516. line-height: 56px;
  517. cursor: pointer;
  518. &:hover {
  519. color: #2ABED1FF;
  520. }
  521. }
  522. }
  523. .recommend-tag {
  524. position: relative;
  525. @extend .flex-horizontal;
  526. display: inline-flex;
  527. height: 20px;
  528. padding-left: 16px;
  529. padding-right: 8px;
  530. background: linear-gradient(136deg, #FF9F40 0%, #FF3A20 100%);
  531. font-size: 12px;
  532. font-weight: 400;
  533. color: #FFFFFF;
  534. &::before {
  535. content: "";
  536. position: absolute;
  537. top: 3px;
  538. left: -8px;
  539. background: #fff;
  540. width: 14px;
  541. height: 14px;
  542. transform: rotate(45deg);
  543. border-radius: 2px;
  544. }
  545. }
  546. </style>
  547. <style scoped lang="scss">
  548. .flex-horizontal {
  549. display: flex;
  550. flex-direction: row;
  551. align-items: center;
  552. }
  553. .flex-vertical {
  554. display: flex;
  555. flex-direction: column;
  556. align-items: center;
  557. justify-content: center;
  558. }
  559. .navbar-group {
  560. position: relative;
  561. z-index: 50;
  562. @extend .flex-horizontal;
  563. justify-content: space-between;
  564. min-height: 64px;
  565. min-width: 800px;
  566. padding: 0 36px;
  567. box-sizing: border-box;
  568. background-color: #ffffff;
  569. box-shadow: 0px 2px 8px -1px rgba(0, 0, 0, 0.0800);
  570. .nav-user-info {
  571. ::v-deep .navbar-item--label {
  572. max-width: 118px;
  573. overflow: hidden;
  574. text-overflow: ellipsis;
  575. white-space: nowrap;
  576. }
  577. }
  578. .logo {
  579. display: inline-block;
  580. width: auto;
  581. height: 32px;
  582. cursor: pointer;
  583. img {
  584. height: 100%;
  585. }
  586. }
  587. .navbar-content-group {
  588. @extend .flex-horizontal;
  589. flex: 1;
  590. justify-content: space-between;
  591. .search-module {
  592. margin-left: 82px;
  593. margin-right: 16px;
  594. flex: 1;
  595. max-width: 440px;
  596. }
  597. }
  598. .nav-group {
  599. @extend .flex-horizontal;
  600. font-size: 16px;
  601. font-weight: 400;
  602. color: #1D1D1D;
  603. line-height: 24px;
  604. .nav-item {
  605. height: 32px;
  606. }
  607. .nav-item + .nav-item {
  608. margin-left: 48px;
  609. }
  610. }
  611. }
  612. </style>