index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  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, index) in computedNaves"
  27. :key="index"
  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-kefu_xian',
  123. svg: true,
  124. plugin: 'navCustomer'
  125. }
  126. ],
  127. websites: [
  128. {
  129. label: '标讯主站',
  130. icon: 'icon-a-Property1gray1'
  131. },
  132. {
  133. label: '剑鱼官网',
  134. icon: 'icon-a-Property1gray2'
  135. },
  136. {
  137. label: '商情门户',
  138. icon: 'icon-a-Property1gray'
  139. }
  140. ]
  141. }
  142. },
  143. computed: {
  144. ...mapState('work-bench/navbar', [
  145. 'messageCount',
  146. 'todoCount'
  147. ]),
  148. ...mapGetters('work-bench/user', [
  149. 'hasExclusiveCustomer',
  150. 'exclusiveCustomerInfo',
  151. 'userInfo'
  152. ]),
  153. ...mapGetters('work-bench/navbar', [
  154. 'messageList'
  155. ]),
  156. // 获取是否启用自定义顶部导航用户信息自定义
  157. ...mapGetters('work-bench', [
  158. 'useCustomStatus'
  159. ]),
  160. ...mapState('customer', [
  161. 'showCustomer',
  162. 'customerUrl'
  163. ]),
  164. computedNaves () {
  165. return this.navs.filter(item => {
  166. return this.todoCount ? item : item.label!== '待办'
  167. })
  168. }
  169. },
  170. created () {
  171. // 判断是否配置启用自定义用户信息模块,提供默认配置
  172. if (!this.useCustomStatus?.['nav-user-info']) {
  173. this.navs.push({
  174. label: '',
  175. icon: 'https://www.jianyu360.cn/common-module/public/image/auto.png',
  176. plugin: 'navInfo',
  177. class: 'nav-user-info'
  178. })
  179. }
  180. /**
  181. *
  182. * TODO 获取用户数据、消息数据 待优化至应用层处理
  183. */
  184. this.getPower()
  185. this.getUserSimpleInfo().then(() => {
  186. const userInfo = this.navs.find(v => v.plugin === 'navInfo')
  187. if (userInfo) {
  188. userInfo.label = this.userInfo.name || this.userInfo.phone
  189. userInfo.icon = this.userInfo.avatar
  190. }
  191. })
  192. this.getMessageInfo()
  193. },
  194. mounted () {
  195. window.addEventListener('message', (event) => {
  196. // 子页面发送来得消息是关闭或是全屏操作时 关闭当前侧边栏会话窗口
  197. if (event.data && event.data.action) {
  198. if (event.data.action === 'close') {
  199. this.setCustomerStatus(false)
  200. } else if (event.data.action ==='fullScreen') {
  201. this.setCustomerStatus(false)
  202. window.open(this.customerUrl.replace('&from=aside', '&mini=1'))
  203. }
  204. }
  205. }, false)
  206. },
  207. beforeDestroy () {
  208. window.removeEventListener('message', () => {})
  209. },
  210. methods: {
  211. ...mapActions('work-bench', [
  212. 'getPower',
  213. 'getUserSimpleInfo',
  214. 'setSignOut',
  215. 'getMessages',
  216. 'setClickMessage',
  217. 'setReadMessage',
  218. 'navbarConvergeSearch'
  219. ]),
  220. ...mapMutations('customer', [
  221. 'setCustomerStatus'
  222. ]),
  223. /**
  224. * 获取消息数据
  225. */
  226. async getMessageInfo () {
  227. await this.getMessages()
  228. this.navs.find(v => v.label === '消息中心').badge = this.messageCount > 99 ? '99+' : this.messageCount
  229. /**
  230. * 对外提供未读消息条数更新事件
  231. */
  232. this.$BRACE.$emit('update-message-count', this.messageCount)
  233. },
  234. /**
  235. * 点击退出登录
  236. */
  237. onClickOut () {
  238. this.setSignOut()
  239. },
  240. /**
  241. * 点击选中导航 Item
  242. */
  243. onSelectNav (nav) {
  244. switch (nav.label) {
  245. case '消息中心': {
  246. this.goSiteMessage()
  247. break
  248. }
  249. case '前往官网': {
  250. this.goSiteHome()
  251. break
  252. }
  253. case '待办': {
  254. this.goSiteTodo()
  255. break
  256. }
  257. }
  258. },
  259. /**
  260. * 前往消息中心页面
  261. */
  262. goSiteMessage () {
  263. this.$BRACE.$emit({
  264. fKey: 'goSiteMessage',
  265. spareFn: (link) => this.$BRACE.methods.open({
  266. route: { link }
  267. })
  268. }, '/swordfish/frontPage/messageCenter/sess/index')
  269. },
  270. /**
  271. * 前往官网首页
  272. */
  273. goSiteHome () {
  274. this.$BRACE.$emit({
  275. fKey: 'goSiteHome',
  276. spareFn: (link) => this.$BRACE.methods.open({
  277. route: { link }
  278. })
  279. }, this.home)
  280. },
  281. /**
  282. * 点击客服 Item
  283. * @param type - 类型
  284. */
  285. onClickCustomer (type) {
  286. this.$BRACE.$emit('click-nav-customer', type)
  287. },
  288. /**
  289. * 前往商情门户(资讯站群)
  290. */
  291. goSiteCms () {
  292. this.$BRACE.$emit({
  293. fKey: 'goSiteCms',
  294. spareFn: (link) => this.$BRACE.methods.open({
  295. route: { link }
  296. })
  297. }, '/jycms')
  298. },
  299. /**
  300. * 前往剑鱼官网(brand)
  301. */
  302. goSiteBrand () {
  303. this.$BRACE.$emit({
  304. fKey: 'goSiteBrand',
  305. spareFn: (link) => this.$BRACE.methods.open({
  306. route: { link }
  307. })
  308. }, '/brand')
  309. },
  310. onWebsiteNav (nav) {
  311. switch (nav.label) {
  312. case '标讯主站': {
  313. this.goSiteHome()
  314. break
  315. }
  316. case '剑鱼官网': {
  317. this.goSiteBrand()
  318. break
  319. }
  320. case '商情门户': {
  321. this.goSiteCms()
  322. break
  323. }
  324. }
  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 24px;
  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: flex;
  579. align-items: center;
  580. width: auto;
  581. height: 32px;
  582. cursor: pointer;
  583. img {
  584. height: 100%;
  585. }
  586. h1{
  587. margin-left: 8px;
  588. font-size: 18px;
  589. line-height: 24px;
  590. color: #2ABED1;
  591. }
  592. }
  593. .navbar-content-group {
  594. @extend .flex-horizontal;
  595. flex: 1;
  596. justify-content: space-between;
  597. .search-module {
  598. margin-left: 32px;
  599. margin-right: 20px;
  600. flex: 1;
  601. max-width: 440px;
  602. }
  603. &--right{
  604. flex: 1;
  605. @extend .flex-horizontal;
  606. justify-content: flex-end;
  607. }
  608. }
  609. .nav-group {
  610. @extend .flex-horizontal;
  611. font-size: 16px;
  612. font-weight: 400;
  613. color: #1D1D1D;
  614. line-height: 24px;
  615. .nav-item {
  616. height: 32px;
  617. }
  618. .nav-item + .nav-item {
  619. margin-left: 20px;
  620. }
  621. }
  622. .website-group{
  623. @extend .flex-horizontal;
  624. margin-left: 16px;
  625. .website-item{
  626. @extend .flex-horizontal;
  627. padding: 6px 8px;
  628. margin-right: 10px;
  629. font-size: 14px;
  630. border-radius: 6px;
  631. color: #1D1D1D;
  632. border: 1px solid rgba(0, 0, 0, 0.08);
  633. cursor: pointer;
  634. .iconfont{
  635. margin-right: 4px;
  636. color: #686868;
  637. font-size: 20px;
  638. }
  639. &:hover{
  640. color: #fff;
  641. background: #2ABED1;
  642. border-color: #2ABED1;
  643. i{
  644. color: #fff;
  645. }
  646. }
  647. }
  648. }
  649. .customer-container{
  650. // display: none;
  651. position: fixed;
  652. right: 84px;
  653. bottom: 80px;
  654. max-height: 674px;
  655. min-height: 400px;
  656. width: 460px;
  657. height: calc(100% - 40px);
  658. box-shadow: 0 0 20px 0 rgba(8, 31, 38, 0.12);
  659. border-radius: 8px;
  660. overflow: hidden;
  661. z-index: 9999;
  662. }
  663. @media screen and (max-height: 800px) {
  664. .customer-container{
  665. bottom: 20px;
  666. }
  667. }
  668. }
  669. </style>