index.vue 16 KB

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