index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. <template>
  2. <!-- 经典布局 -->
  3. <ClassicalMenu
  4. v-if="layout === layoutEnum.CLASSICAL"
  5. :layout="layout"
  6. :isMobile="isMobile"
  7. :menuIsCollapse="menuIsCollapse"
  8. :sideTheme="sideTheme"
  9. :sysBaseConfig="sysBaseConfig"
  10. :openKeys="openKeys"
  11. :selectedKeys="selectedKeys"
  12. :menu="menu"
  13. :breadcrumbOpen="breadcrumbOpen"
  14. :layoutTagsOpen="layoutTagsOpen"
  15. :kStore="kStore"
  16. :footerCopyrightOpen="footerCopyrightOpen"
  17. :moduleMenuShow="moduleMenuShow"
  18. @onSelect="onSelect"
  19. @onOpenChange="onOpenChange"
  20. @switchModule="switchModule"
  21. @menuIsCollapseClick="menuIsCollapseClick"
  22. />
  23. <!-- 双排菜单布局 -->
  24. <DoubleRowMenu
  25. v-else-if="layout === layoutEnum.DOUBLEROW"
  26. :layout="layout"
  27. :isMobile="isMobile"
  28. :sideTheme="sideTheme"
  29. :secondMenuSideTheme="secondMenuSideTheme"
  30. :sysBaseConfig="sysBaseConfig"
  31. :openKeys="openKeys"
  32. :selectedKeys="selectedKeys"
  33. :menuIsCollapse="menuIsCollapse"
  34. :doublerowSelectedKey="doublerowSelectedKey"
  35. :menu="menu"
  36. :nextMenu="nextMenu"
  37. :breadcrumbOpen="breadcrumbOpen"
  38. :layoutTagsOpen="layoutTagsOpen"
  39. :layoutSiderDowbleMenu="layoutSiderDowbleMenu"
  40. :kStore="kStore"
  41. :footerCopyrightOpen="footerCopyrightOpen"
  42. :moduleMenuShow="moduleMenuShow"
  43. @onSelect="onSelect"
  44. @switchModule="switchModule"
  45. @showMenu="showMenu"
  46. />
  47. <!-- 顶部菜单布局 -->
  48. <TopMenu
  49. v-else-if="layout === layoutEnum.TOP"
  50. :layout="layout"
  51. :menuList="menuList"
  52. :menu="menu"
  53. :sysBaseConfig="sysBaseConfig"
  54. :moduleMenuShow="moduleMenuShow"
  55. :openKeys="openKeys"
  56. :selectedKeys="selectedKeys"
  57. :breadcrumbOpen="breadcrumbOpen"
  58. :footerCopyrightOpen="footerCopyrightOpen"
  59. :sideTheme="sideTheme"
  60. :isMobile="isMobile"
  61. :kStore="kStore"
  62. :layoutTagsOpen="layoutTagsOpen"
  63. @switchModule="switchModule"
  64. @onOpenChange="onOpenChange"
  65. @onSelect="onSelect"
  66. />
  67. <!-- 退出最大化 -->
  68. <div class="main-maximize-exit" @click="exitMaximize">
  69. <fullscreen-exit-outlined class="xn-color-fff" />
  70. </div>
  71. </template>
  72. <script setup>
  73. import { globalStore, keepAliveStore, viewTagsStore } from '@/store'
  74. import { themeEnum } from '@/layout/enum/themeEnum'
  75. import { layoutEnum } from '@/layout/enum/layoutEnum'
  76. import { useRoute, useRouter } from 'vue-router'
  77. import tool from '@/utils/tool'
  78. import { notification, Button } from 'ant-design-vue'
  79. import ClassicalMenu from '@/layout/menu/classicalMenu.vue'
  80. import DoubleRowMenu from '@/layout/menu/doubleRowMenu.vue'
  81. import TopMenu from '@/layout/menu/topMenu.vue'
  82. import { NextLoading } from '@/utils/loading'
  83. import { useMenuStore } from '@/store/menu'
  84. import { userStore } from '@/store/user'
  85. import { getLocalHash, checkHash } from '@/utils/version'
  86. import sysConfig from '@/config/index'
  87. import dictApi from '@/api/dev/dictApi'
  88. const store = globalStore()
  89. const kStore = keepAliveStore()
  90. const route = useRoute()
  91. const router = useRouter()
  92. const menu = ref([])
  93. const pMenu = ref({})
  94. const nextMenu = ref([])
  95. const selectedKeys = ref([])
  96. const openKeys = ref([])
  97. const onSelectTag = ref(false)
  98. const moduleMenu = ref([])
  99. const moduleMenuShow = ref(true)
  100. const doublerowSelectedKey = ref([])
  101. const layoutSiderDowbleMenu = ref(true)
  102. const menuList = ref([])
  103. // computed计算方法 - start
  104. const layout = computed(() => {
  105. return store.layout
  106. })
  107. const isMobile = computed(() => {
  108. return store.isMobile
  109. })
  110. const menuIsCollapse = computed(() => {
  111. return store.menuIsCollapse
  112. })
  113. const theme = computed(() => {
  114. return store.theme
  115. })
  116. const themeColor = computed(() => {
  117. return store.themeColor
  118. })
  119. const layoutTagsOpen = computed(() => {
  120. // 当关闭多标签时,清理keepAlive的缓存
  121. if (!store.layoutTagsOpen) {
  122. kStore.keepLiveRoute = []
  123. }
  124. return store.layoutTagsOpen
  125. })
  126. const breadcrumbOpen = computed(() => {
  127. return store.breadcrumbOpen
  128. })
  129. const fixedWidth = computed(() => {
  130. return store.fixedWidth
  131. })
  132. const topHeaderThemeColorOpen = computed(() => {
  133. return store.topHeaderThemeColorOpen
  134. })
  135. const topHeaderThemeColorSpread = computed(() => {
  136. return store.topHeaderThemeColorSpread
  137. })
  138. const sideUniqueOpen = computed(() => {
  139. return store.sideUniqueOpen
  140. })
  141. const footerCopyrightOpen = computed(() => {
  142. return store.footerCopyrightOpen
  143. })
  144. const sysBaseConfig = computed(() => {
  145. return store.sysBaseConfig
  146. })
  147. const module = computed(() => {
  148. return store.module
  149. })
  150. const sideTheme = computed(() => {
  151. return theme.value === themeEnum.REAL_DARK ? themeEnum.DARK : theme.value
  152. })
  153. const secondMenuSideTheme = computed(() => {
  154. return theme.value === themeEnum.REAL_DARK ? themeEnum.DARK : themeEnum.LIGHT
  155. })
  156. const roundedCornerStyleOpen = computed(() => {
  157. return store.roundedCornerStyleOpen
  158. })
  159. // 路由监听高亮
  160. const showThis = () => {
  161. // route是一个只读路由对象。需要使用 useRouter 函数来获取路由实例
  162. router.getRoutes().filter((item) => {
  163. if (item.path === route.path) {
  164. pMenu.value = item.meta.breadcrumb ? item.meta.breadcrumb[0] : {}
  165. }
  166. })
  167. // pMenu.value = route.meta.breadcrumb ? route.meta.breadcrumb[0] : {}
  168. // 展开的
  169. nextTick(() => {
  170. // 取得默认路由地址并设置展开
  171. let active = route.meta.active || route.path
  172. // 如果是目录,必须往下找
  173. if (route.meta.type === 'catalog') {
  174. active = traverseChild(pMenu.value.children, active).path
  175. }
  176. selectedKeys.value = new Array(active)
  177. const pidKey = getParentKeys(pMenu.value.children, active)
  178. // 判断是隐藏的路由,找其上级
  179. if (route.meta.hidden && pidKey) {
  180. if (pidKey.length > 1) {
  181. selectedKeys.value = new Array(pidKey[1])
  182. }
  183. }
  184. const nextTickMenu = pMenu.value.children
  185. if (pidKey) {
  186. const modelPidKey = getParentKeys(moduleMenu.value, route.path)
  187. moduleMenu.value.forEach((item) => {
  188. if (modelPidKey.includes(item.path)) {
  189. tagSwitchModule(item.id)
  190. }
  191. })
  192. const parentPath = pidKey[pidKey.length - 1]
  193. if (layout.value === layoutEnum.DOUBLEROW) {
  194. // 这一串操作下来只为取到最上面的路由的孩子们,最后成为双排菜单的第二排
  195. const nextMenuTemp = nextTickMenu.filter((item) => item.path === parentPath)[0].children
  196. if (nextMenuTemp) {
  197. nextMenu.value = nextTickMenu.filter((item) => item.path === parentPath)[0].children
  198. }
  199. }
  200. }
  201. if (!onSelectTag.value || sideUniqueOpen.value) {
  202. openKeys.value = pidKey
  203. }
  204. // 双排菜单下
  205. if (layout.value === layoutEnum.DOUBLEROW) {
  206. setDoubleRowSelectedKey()
  207. }
  208. })
  209. }
  210. const init = () => {
  211. // 执行-start
  212. moduleMenu.value = router.getMenu()
  213. // 获取缓存中的菜单模块是哪个
  214. const menuModuleId = tool.data.get('SNOWY_MENU_MODULE_ID')
  215. if (menuModuleId) {
  216. // 防止切换一个无此应用的人
  217. const module = router.getMenu().filter((item) => item.id === menuModuleId)
  218. if (module.length > 0) {
  219. menu.value = module[0].children
  220. } else {
  221. menu.value = router.getMenu()[0].children
  222. }
  223. } else {
  224. menu.value = router.getMenu()[0].children
  225. }
  226. showThis()
  227. }
  228. watchEffect(() => {
  229. const menuStore = useMenuStore()
  230. if (menuStore.refreshFlag) {
  231. init()
  232. // 更新标签
  233. viewTagsStore().updateOrRemoveViewTags(router.getRoutes())
  234. menuStore.changeRefreshFlag(false)
  235. }
  236. })
  237. onMounted(() => {
  238. // 取消loading
  239. NextLoading.done()
  240. showThis()
  241. onLayoutResize()
  242. window.addEventListener('resize', onLayoutResize)
  243. window.addEventListener('resize', getNav)
  244. switchoverTopHeaderThemeColor()
  245. settingTopHeaderThemeOrColor(theme.value, layout.value)
  246. settingFixedWidth()
  247. updateVersion()
  248. nextTick(() => {
  249. getNav()
  250. // 刷新登录信息,不影响其他
  251. userStore().refreshUserLoginUserInfo()
  252. // 刷新菜单信息,不影响其他
  253. useMenuStore().refreshApiMenu()
  254. // 刷新字典信息,不影响其他
  255. dictApi.dictTree().then((data) => {
  256. // 设置字典到store中
  257. tool.data.set('DICT_TYPE_TREE_DATA', data)
  258. })
  259. })
  260. })
  261. onBeforeUnmount(() => {
  262. window.removeEventListener('resize', onLayoutResize)
  263. window.removeEventListener('resize', getNav)
  264. })
  265. // 新版检测
  266. const updateVersion = () => {
  267. const updateVersionOpen = import.meta.env.VITE_VERSION_UPDATE
  268. if (updateVersionOpen) {
  269. setTimeout(async () => {
  270. // 本地
  271. let localVersion = getLocalHash()
  272. // 线上
  273. let onlineVersion = await checkHash()
  274. // 如果不一样,提示更新
  275. if (localVersion !== onlineVersion) {
  276. if (document.querySelector('.notification-update-version')) {
  277. return
  278. }
  279. const key = `open${Date.now()}`
  280. notification.open({
  281. type: 'info',
  282. message: '发现新版本',
  283. description: '检测到新版本,请刷新后使用',
  284. duration: 0,
  285. class: 'notification-update-version',
  286. btn: () =>
  287. h(
  288. Button,
  289. {
  290. type: 'primary',
  291. size: 'small',
  292. onClick: () => {
  293. notification.close(key)
  294. window.location.reload()
  295. }
  296. },
  297. { default: () => '立即更新' }
  298. ),
  299. key
  300. })
  301. }
  302. }, 3000)
  303. }
  304. }
  305. // 动态获取横向导航栏隐藏数量
  306. const getNav = () => {
  307. // 判断一下是不是顶部导航栏的模式
  308. if (layout.value !== 'top') return
  309. const menuNavList = menu.value
  310. menuList.value = menuNavList
  311. nextTick(() => {
  312. // 获取所有的导航菜单
  313. let liArr = document.querySelector('#topHeaderMenu').querySelectorAll('li')
  314. let allWidth = document.querySelector('#xn-line-nav').offsetWidth // 可以显示区域的宽度
  315. // 计算显示的宽度
  316. let num = 0
  317. let startIndex = 0
  318. for (const [index, item] of liArr.entries()) {
  319. num += item.offsetWidth
  320. if (num > allWidth) {
  321. startIndex = index - 1
  322. break
  323. }
  324. }
  325. // 判断显示出来的导航栏长度是否小于可以显示的区域
  326. if (num < allWidth) {
  327. menuList.value = menuNavList
  328. return
  329. }
  330. // 如果大于了显示的区域,就将其隐藏
  331. const showNav = menuNavList.slice(0, startIndex)
  332. const hiddenNav = menuNavList.slice(startIndex, menuNavList.length)
  333. menuList.value = showNav
  334. menuList.value.push({
  335. meta: {
  336. icon: 'rightCircle-outlined',
  337. title: '更多',
  338. type: 'catalog'
  339. },
  340. children: hiddenNav
  341. })
  342. })
  343. }
  344. // 鼠标滚动事件
  345. const handleMouseWheel = (event) => {
  346. let element = document.querySelector('#xn-line-nav')
  347. let element2 = document.querySelector('#topHeaderMenu')
  348. // 判断鼠标是向上滚动还是向下滚动的
  349. let delta = event.deltaY
  350. // 滚动菜单时变换的一个大小
  351. const num = 20
  352. // 滚动的距离
  353. let leftMove = Number(element2.style.left.slice(0, -2))
  354. // 父盒子相对子盒子滚动的一个判断,用来给盒子加一个临界点
  355. let remove = element.offsetWidth - element2.scrollWidth
  356. // 鼠标向下滚动
  357. // 满足子元素移动的距离大于两个元素相差的距离时
  358. if (delta < 0 && leftMove > remove) {
  359. element2.style.left = leftMove - num + 'px'
  360. } else if (delta > 0 && leftMove < 0) {
  361. // 鼠标向上滚动
  362. // 当移动的距离小于0的时候,可以向后滚动
  363. element2.style.left = leftMove + num + 'px'
  364. }
  365. }
  366. watch(route, () => {
  367. // 清理选中的
  368. selectedKeys.value = []
  369. showThis()
  370. })
  371. // 监听是否开启了顶栏颜色
  372. watch(layout, (newValue) => {
  373. document.body.setAttribute('data-layout', newValue)
  374. if (newValue.includes(layoutEnum.DOUBLEROW)) {
  375. showThis()
  376. setDoubleRowSelectedKey()
  377. }
  378. nextTick(() => {
  379. // 顶栏主题色
  380. switchoverTopHeaderThemeColor()
  381. // top下的顶栏
  382. settingTopHeaderThemeOrColor(theme.value, newValue)
  383. getNav()
  384. settingFixedWidth()
  385. let element = document.querySelector('#xn-line-nav')
  386. if (element) {
  387. element.addEventListener('mousewheel', handleMouseWheel, false)
  388. }
  389. })
  390. })
  391. watch(topHeaderThemeColorOpen, () => {
  392. switchoverTopHeaderThemeColor()
  393. })
  394. watch(fixedWidth, () => {
  395. settingFixedWidth()
  396. })
  397. watch(layoutTagsOpen, () => {
  398. settingFixedWidth()
  399. })
  400. watch(breadcrumbOpen, () => {
  401. settingFixedWidth()
  402. })
  403. watch(topHeaderThemeColorSpread, () => {
  404. switchoverTopHeaderThemeColor()
  405. })
  406. watch(theme, (newValue) => {
  407. settingTopHeaderThemeOrColor(newValue, layout.value)
  408. })
  409. watch(themeColor, () => {
  410. settingTopHeaderThemeOrColor(theme.value, layout.value)
  411. })
  412. watch(topHeaderThemeColorOpen, (newValue) => {
  413. const header = document.getElementById('snowyHeader')
  414. const topHeaderMenu = document.getElementById('topHeaderMenu')
  415. if (layout.value === layoutEnum.TOP) {
  416. if (newValue) {
  417. header.classList.add('top-snowy-header-layout')
  418. topHeaderMenu.classList.add('top-snowy-header-layout')
  419. } else {
  420. header.classList.remove('top-snowy-header-layout')
  421. topHeaderMenu.classList.remove('top-snowy-header-layout')
  422. }
  423. }
  424. })
  425. watch(roundedCornerStyleOpen, () => {
  426. settingTopHeaderThemeOrColor(theme.value, layout.value)
  427. })
  428. // 设置固定宽度
  429. const settingFixedWidth = () => {
  430. nextTick(() => {
  431. const breadcrumbWidth = document.querySelector('.admin-ui-breadcrumb')
  432. const showWidth = document.querySelector('.snowy-tags')
  433. const mainWidth = document.querySelector('.ant-layout-content')
  434. if (fixedWidth.value && layout.value === layoutEnum.TOP) {
  435. breadcrumbWidth?.classList.add('xn-mg050')
  436. showWidth?.classList.add('xn-mg050')
  437. mainWidth?.classList.add('xn-pd1180')
  438. } else {
  439. breadcrumbWidth?.classList.remove('xn-mg050')
  440. showWidth?.classList.remove('xn-mg050')
  441. mainWidth?.classList.remove('xn-pd1180')
  442. }
  443. })
  444. }
  445. // 设置顶栏颜色或者主题
  446. const settingTopHeaderThemeOrColor = (theme, layout) => {
  447. const header = document.getElementById('snowyHeader')
  448. const topHeaderMenu = document.getElementById('topHeaderMenu')
  449. if (topHeaderThemeColorOpen.value && layout === layoutEnum.TOP) {
  450. nextTick(() => {
  451. topHeaderMenu.classList.add('top-snowy-header-layout')
  452. header.classList.add('top-snowy-header-layout')
  453. })
  454. } else if (!topHeaderThemeColorOpen.value && layout === layoutEnum.TOP) {
  455. nextTick(() => {
  456. topHeaderMenu.classList.remove('top-snowy-header-layout')
  457. header.classList.remove('top-snowy-header-layout')
  458. })
  459. }
  460. if (theme === themeEnum.LIGHT && layout === layoutEnum.TOP) {
  461. header.classList.remove('top-snowy-header')
  462. header.classList.add('top-snowy-header-light')
  463. } else {
  464. header.classList.remove('top-snowy-header-light')
  465. if (layout === layoutEnum.TOP) {
  466. header.classList.add('top-snowy-header')
  467. } else {
  468. if (theme === themeEnum.REAL_DARK) {
  469. header.classList.add('top-snowy-header')
  470. } else {
  471. header.classList.remove('top-snowy-header')
  472. }
  473. }
  474. }
  475. }
  476. const menuIsCollapseClick = () => {
  477. store.toggleConfig('menuIsCollapse')
  478. }
  479. // 切换顶栏颜色
  480. const switchoverTopHeaderThemeColor = () => {
  481. try {
  482. // 界面顶栏设置颜色
  483. const header = document.getElementById('snowyHeader')
  484. topHeaderThemeColorOpen.value
  485. ? header.classList.add('snowy-header-primary-color')
  486. : header.classList.remove('snowy-header-primary-color')
  487. // 判断是否开启了通栏
  488. const headerLogin = document.getElementById('snowyHeaderLogo')
  489. topHeaderThemeColorSpread.value
  490. ? headerLogin.classList.add('snowy-header-logo-primary-color')
  491. : headerLogin.classList.remove('snowy-header-logo-primary-color')
  492. // eslint-disable-next-line no-empty
  493. } catch (e) {}
  494. }
  495. // 设置双排菜单下的首列默认选中
  496. const setDoubleRowSelectedKey = () => {
  497. const pidKey = getParentKeys(menu.value, selectedKeys.value.toString())
  498. nextTick(() => {
  499. const pidKeyArray = []
  500. for (const key in pidKey) {
  501. pidKeyArray.push(key)
  502. }
  503. layoutSiderDowbleMenu.value = pidKeyArray.length > 1
  504. })
  505. // 设置第一排选中的
  506. menu.value.forEach((item) => {
  507. if (pidKey !== undefined) {
  508. if (pidKey[pidKey.length - 1].toString() === item.path) {
  509. doublerowSelectedKey.value = [item.path]
  510. }
  511. }
  512. })
  513. }
  514. // 菜单展开/关闭的回调
  515. const onOpenChange = (keys) => {
  516. if (sideUniqueOpen.value) {
  517. // 获取最新的
  518. const openKey = keys[keys.length - 1]
  519. if (keys.length > 1) {
  520. // 获取上级
  521. openKeys.value = getParentKeys(menu.value, openKey)
  522. } else {
  523. openKeys.value = Array.of(openKey) // new Array(openKey);
  524. }
  525. } else {
  526. openKeys.value = keys
  527. }
  528. }
  529. // 获取上级keys
  530. const getParentKeys = (data, val) => {
  531. const traverse = (array, val) => {
  532. // 递归父级key
  533. for (const element of array) {
  534. if (element.path === val) {
  535. return [element.path]
  536. }
  537. if (element.children) {
  538. const far = traverse(element.children, val)
  539. if (far) {
  540. return far.concat(element.path)
  541. }
  542. }
  543. }
  544. }
  545. return traverse(data, val)
  546. }
  547. // 双排菜单下点击显示右侧分栏
  548. const showMenu = (route) => {
  549. pMenu.value = route
  550. if (pMenu.value.children) {
  551. nextMenu.value = pMenu.value.children
  552. }
  553. if (!route.children || route.children.length === 0) {
  554. layoutSiderDowbleMenu.value = false
  555. router.push({ path: route.path })
  556. } else {
  557. if (route.children) {
  558. let hidden = 0
  559. route.children.forEach((item) => {
  560. if (item.meta.hidden && item.meta.hidden === true) {
  561. hidden++
  562. }
  563. })
  564. // 如果全部都隐藏了,就跳转这个,不展开另一排
  565. if (hidden === route.children.length) {
  566. layoutSiderDowbleMenu.value = false
  567. router.push({ path: route.path })
  568. } else {
  569. layoutSiderDowbleMenu.value = true
  570. }
  571. } else {
  572. layoutSiderDowbleMenu.value = false
  573. }
  574. }
  575. if (layout.value === layoutEnum.DOUBLEROW) {
  576. doublerowSelectedKey.value = [route.path]
  577. }
  578. }
  579. // 当菜单被选中时
  580. const onSelect = (obj) => {
  581. onSelectTag.value = true
  582. const pathLength = obj.keyPath.length
  583. const path = obj.keyPath[pathLength - 1]
  584. router.push({ path })
  585. // 设置选中
  586. selectedKeys.value = obj.selectedKeys
  587. }
  588. const onLayoutResize = () => {
  589. const clientWidth = document.body.clientWidth
  590. store.setIsMobile(clientWidth < 992)
  591. }
  592. // 切换应用
  593. const switchModule = (id) => {
  594. if (moduleMenu.value.length > 0) {
  595. showThis()
  596. const menus = moduleMenu.value.filter((item) => item.id === id)[0].children
  597. if (menus.length > 0) {
  598. // 正儿八百的菜单
  599. menu.value = menus
  600. const firstMenu = traverseChild(menu.value)
  601. const path = firstMenu.path
  602. // 如果是外链
  603. if (firstMenu.menuType === 'LINK') {
  604. window.open(path)
  605. } else {
  606. // 将此模块的唯一值加入缓存
  607. tool.data.set('SNOWY_MENU_MODULE_ID', id)
  608. // 然后将其跳转至指定界面,默认始终取排序第一的
  609. router.push({ path })
  610. }
  611. } else {
  612. message.warning('该模块下无任何菜单')
  613. }
  614. }
  615. getNav()
  616. }
  617. // 通过标签切换应用
  618. const tagSwitchModule = (id) => {
  619. // 将此模块的唯一值加入缓存
  620. tool.data.set('SNOWY_MENU_MODULE_ID', id)
  621. store.setModule(id)
  622. // 正儿八百的菜单
  623. menu.value = moduleMenu.value.filter((item) => item.id === id)[0].children
  624. }
  625. // 遍历获取子集
  626. const traverseChild = (menu) => {
  627. if (menu[0] && menu[0].children !== undefined) {
  628. if (menu[0].children.length > 0) {
  629. if (menu[0].children[0] && menu[0].children[0].meta.hidden && menu[0].children[0].meta.hidden === true) {
  630. return menu[0]
  631. } else {
  632. return traverseChild(menu[0].children)
  633. }
  634. }
  635. } else {
  636. return menu[0]
  637. }
  638. }
  639. // 退出最大化
  640. const exitMaximize = () => {
  641. document.getElementById('app').classList.remove('main-maximize')
  642. moduleMenuShow.value = false
  643. nextTick(() => {
  644. moduleMenuShow.value = true
  645. })
  646. }
  647. </script>
  648. <style lang="less" scoped>
  649. .xn-color-fff {
  650. color: #fff;
  651. }
  652. .xn-pdl25 {
  653. padding-left: 11px;
  654. }
  655. .xn-menu-line {
  656. text-align: center;
  657. height: auto;
  658. line-height: 20px;
  659. flex: none;
  660. display: block;
  661. padding: 12px 0 !important;
  662. }
  663. .xn-navmenu-line {
  664. min-width: 0;
  665. flex: 1 1 0%;
  666. // padding: 0 20px;
  667. overflow: hidden;
  668. }
  669. .xn-bb0 {
  670. border-bottom: none;
  671. position: relative;
  672. }
  673. .ant-layout-content {
  674. display: flex;
  675. flex-direction: column;
  676. }
  677. .xn-pd1180 {
  678. padding: 10px 150px 0 150px;
  679. }
  680. .xn-pd050 {
  681. padding: 0 50px;
  682. }
  683. .xn-pl10 {
  684. padding-left: 10px;
  685. }
  686. .xn-mg050 {
  687. margin: 0px 150px;
  688. }
  689. </style>