index.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <template>
  2. <div :class="{content: !fullScreen}">
  3. <!-- 全屏 -->
  4. <template v-if="fullScreen">
  5. <keep-alive>
  6. <router-view v-if="$route.meta.keepAlive" />
  7. </keep-alive>
  8. <router-view v-if="!$route.meta.keepAlive" />
  9. </template>
  10. <template v-else>
  11. <navbar :left="width">
  12. <template #tools>
  13. <!-- 判断按钮权限 -->
  14. <v-badge
  15. :value="bellMessage"
  16. bordered
  17. dot
  18. color="error"
  19. overlap
  20. >
  21. <v-btn
  22. fab
  23. x-small
  24. @click="drawer = true"
  25. >
  26. <v-icon size="24">mdi-bell-outline</v-icon>
  27. </v-btn>
  28. </v-badge>
  29. </template>
  30. </navbar>
  31. <div class="body" ref="body">
  32. <side class="stick" :style="`height: calc(100% - ${this.top}px)`" />
  33. <div class="view d-flex flex-column" :style="`padding-left: ${width}px;`">
  34. <div class="breadcrumbs_sticky">
  35. <div class=" d-flex align-center justify-space-between">
  36. <v-breadcrumbs :items="title" elevation="3">
  37. <template v-slot:item="{ item }">
  38. <span class="text" :class="{active: !item.disabled}" @click="toPath(item)">{{ item.text }}</span>
  39. </template>
  40. </v-breadcrumbs>
  41. <v-btn v-if="$route.meta.editModules" text color="primary" class="mr-5" @click="handleClickModules">{{ $t('common.moduleEditing') }}</v-btn>
  42. </div>
  43. <v-divider></v-divider>
  44. <!-- <div v-if="list.length && showChip">
  45. <scroll-tabs class="px-3" :items="list" @handleClick="$event => handleToHref(`${$event.name}${$event.id}`)"></scroll-tabs>
  46. <v-divider></v-divider>
  47. </div> -->
  48. </div>
  49. <div class="box pa-3 d-flex" :style="`width: calc(100vw - ${width}px); position: relative`">
  50. <m-knowledge v-if="showKnowledge"></m-knowledge>
  51. <!-- 避免容器因为文字影响 设置字体大小为0 -->
  52. <div class="box-content" style="font-size: 0;">
  53. <keep-alive>
  54. <router-view v-if="$route.meta.keepAlive" />
  55. </keep-alive>
  56. <router-view v-if="!$route.meta.keepAlive" />
  57. </div>
  58. </div>
  59. </div>
  60. <v-navigation-drawer
  61. v-model="drawer"
  62. absolute
  63. temporary
  64. right
  65. width="400"
  66. style="z-index: var(--zIndex-drawers);"
  67. >
  68. <the-drawer v-if="drawer" @checkRead="getMessageUnRead"></the-drawer>
  69. </v-navigation-drawer>
  70. </div>
  71. </template>
  72. </div>
  73. </template>
  74. <script>
  75. // import ScrollTabs from '@/components/ScrollTabs'
  76. import Navbar from '@/components/Header/navbar.vue'
  77. import Side from '@/components/Side/index.vue'
  78. import { mapGetters } from 'vuex'
  79. import TheDrawer from './components/bellDrawer.vue'
  80. // import cloneDeep from 'lodash/cloneDeep'
  81. import MKnowledge from '@/components/Knowledge'
  82. // import {
  83. // getMessageList
  84. // } from '@/api/system'
  85. const showAI = [
  86. '/data-book/data-metadata',
  87. '/data-book/data-resource',
  88. '/data-book/data-model',
  89. '/data-book/data-indicator',
  90. '/data-book/data-standard',
  91. '/data-book/data-label'
  92. ]
  93. export default {
  94. name: 'layoutIndex',
  95. components: { Navbar, Side, TheDrawer, MKnowledge },
  96. data () {
  97. return {
  98. showKnowledge: true,
  99. drawer: false,
  100. width: 230,
  101. selectTabs: 0,
  102. list: [],
  103. scrollTop: null,
  104. showChip: true,
  105. top: 0,
  106. bellMessage: false,
  107. timer: null
  108. }
  109. },
  110. computed: {
  111. ...mapGetters(['lang', 'userInfo']),
  112. fullScreen () {
  113. return this.$route.meta.fullScreen
  114. },
  115. title () {
  116. const route = this.$route.matched.map((item, index) => {
  117. if (item.path === this.$route.matched[index - 1]?.path) return false
  118. const text = this.lang === 'zh' ? item.meta.title : item.meta.enName
  119. const obj = {
  120. text,
  121. to: item.path
  122. }
  123. if (!item.meta?.allowClick) {
  124. obj.disabled = true
  125. obj.link = false
  126. }
  127. return obj
  128. }).filter(e => e)
  129. route[route.length - 1].disabled = true
  130. route[route.length - 1].link = true
  131. // route.unshift({ text: '首页', to: '/home' })
  132. return route
  133. }
  134. },
  135. watch: {
  136. $route: {
  137. handler: function (val) {
  138. const item = val.matched.find(e => showAI.includes(e.path))
  139. this.showKnowledge = !!item
  140. this.selectTabs = 0
  141. this.list = []
  142. const roles = val.meta.roles.filter(e => e.active === '1').sort((a, b) => a.sort - b.sort)
  143. if (!['/home'].includes(val.path) && roles && roles.length > 1) {
  144. this.list = roles.map(e => ({ label: e.label, name: e.name, id: e.id }))
  145. }
  146. },
  147. immediate: true
  148. }
  149. },
  150. created () {
  151. this.$eventBus.$on('scroll-top', (num) => {
  152. // console.log('scroll-top', num)
  153. this.scrollTop = num
  154. })
  155. this.$eventBus.$on('show-chip', (show) => {
  156. // console.log('show-chip', show)
  157. this.showChip = show
  158. })
  159. // this.getMessageUnRead()
  160. // if (this.timer) {
  161. // clearInterval(this.timer)
  162. // }
  163. // this.timer = setInterval(() => {
  164. // this.getMessageUnRead()
  165. // }, 10000)
  166. },
  167. mounted () {
  168. if (!this.$refs.body) return
  169. this.top = this.$refs.body.offsetTop
  170. window.addEventListener('scroll', this.handleScroll)
  171. },
  172. beforeDestroy () {
  173. clearInterval(this.timer)
  174. if (!this.$refs.body) return
  175. // 在组件销毁前移除滚动事件监听
  176. window.removeEventListener('scroll', this.handleScroll)
  177. },
  178. methods: {
  179. handleScroll () {
  180. if (!this.list.length) return
  181. this.list.forEach((section, index) => {
  182. const selector = document.getElementById(section.name + section.id)
  183. if (!selector) return
  184. if (window.scrollY - selector.offsetTop > -400) {
  185. this.selectTabs = index
  186. }
  187. })
  188. },
  189. handleToHref (id) {
  190. // 获取需要跳转到的元素
  191. const selector = document.getElementById(id)
  192. if (!selector) return
  193. // 使用scrollIntoView方法滚动到指定元素
  194. // selector.scrollIntoView({ behavior: 'smooth', block: 'center' })
  195. window.scrollTo({
  196. top: selector.offsetTop - (this.scrollTop ? this.scrollTop : 188), // 滚动到距离顶部280的位置
  197. behavior: 'smooth' // 平滑滚动
  198. })
  199. },
  200. handleToggle () {
  201. this.width = 300 - this.width
  202. },
  203. handleClick () {
  204. const path = this.$route.path
  205. this.$router.push(`${path}/edit`)
  206. },
  207. toPath ({ disabled, to }) {
  208. if (disabled) {
  209. event.preventDefault()
  210. return
  211. }
  212. this.$router.push(to)
  213. },
  214. handleClickModules () {
  215. this.$emit('$HANDLE_EDIT_MODULES')
  216. }
  217. // 查询是否存在未读消息
  218. // async getMessageUnRead () {
  219. // try {
  220. // const { data } = await getMessageList({ readState: 0, userId: this.userInfo.id })
  221. // this.bellMessage = data.total > 0
  222. // } catch (error) {
  223. // this.$snackbar.error(error)
  224. // }
  225. // }
  226. }
  227. }
  228. </script>
  229. <style lang="scss" scoped>
  230. .stick {
  231. position: fixed;
  232. overflow: auto;
  233. background: #FFF;
  234. transition: .1s;
  235. /* 针对 WebKit 浏览器(如 Chrome 和 Safari)使用伪元素 */
  236. &::-webkit-scrollbar {
  237. width: 0;
  238. }
  239. overflow: -moz-scrollbars-none; /* 针对 Firefox */
  240. // -ms-overflow-style: none; /* 针对 IE 10+ */
  241. scrollbar-width: none; /* 针对 Firefox */
  242. -webkit-overflow-scrolling: touch; /* 针对 iOS Safari */
  243. }
  244. .breadcrumbs_sticky {
  245. background: #FFF;
  246. z-index: var(--zIndex-breadcrumbs);
  247. border-left: 1px solid #eaecef;
  248. }
  249. .content {
  250. height: 100%;
  251. display: flex;
  252. flex-direction: column;
  253. flex: 1;
  254. .body {
  255. position: relative;
  256. overflow: hidden;
  257. height: 0;
  258. flex: 1;
  259. display: flex;
  260. .view {
  261. background: #f0f2f5;
  262. box-sizing: border-box;
  263. .box {
  264. height: 0;
  265. flex: 1;
  266. // overflow-x: hidden;
  267. &-content {
  268. width: 100%;
  269. min-height: 100%;
  270. position: relative;
  271. overflow-y: auto;
  272. overflow-x: hidden;
  273. }
  274. }
  275. }
  276. }
  277. }
  278. .text {
  279. color: #999;
  280. font-size: 14px;
  281. &.active {
  282. color: #1867c0;
  283. cursor: pointer;
  284. }
  285. }
  286. // .item {
  287. // padding: 0 8px;
  288. // cursor: auto;
  289. // &:focus {
  290. // color: inherit !important;
  291. // }
  292. // }
  293. </style>