index.vue 13 KB

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