index.vue 15 KB

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