index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  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. console.log('localVersion:',localVersion,",onlineVersion:",onlineVersion)
  275. // 如果不一样,提示更新
  276. if (localVersion !== onlineVersion) {
  277. if (document.querySelector('.notification-update-version')) {
  278. return
  279. }
  280. const key = `open${Date.now()}`
  281. notification.open({
  282. type: 'info',
  283. message: '发现新版本',
  284. description: '检测到新版本,请刷新后使用',
  285. duration: 0,
  286. class: 'notification-update-version',
  287. btn: () =>
  288. h(
  289. Button,
  290. {
  291. type: 'primary',
  292. size: 'small',
  293. onClick: () => {
  294. notification.close(key)
  295. window.location.reload()
  296. }
  297. },
  298. { default: () => '立即更新' }
  299. ),
  300. key
  301. })
  302. }
  303. }, 3000)
  304. }
  305. }
  306. // 动态获取横向导航栏隐藏数量
  307. const getNav = () => {
  308. // 判断一下是不是顶部导航栏的模式
  309. if (layout.value !== 'top') return
  310. const menuNavList = menu.value
  311. menuList.value = menuNavList
  312. nextTick(() => {
  313. // 获取所有的导航菜单
  314. let liArr = document.querySelector('#topHeaderMenu').querySelectorAll('li')
  315. let allWidth = document.querySelector('#xn-line-nav').offsetWidth // 可以显示区域的宽度
  316. // 计算显示的宽度
  317. let num = 0
  318. let startIndex = 0
  319. for (const [index, item] of liArr.entries()) {
  320. num += item.offsetWidth
  321. if (num > allWidth) {
  322. startIndex = index - 1
  323. break
  324. }
  325. }
  326. // 判断显示出来的导航栏长度是否小于可以显示的区域
  327. if (num < allWidth) {
  328. menuList.value = menuNavList
  329. return
  330. }
  331. // 如果大于了显示的区域,就将其隐藏
  332. const showNav = menuNavList.slice(0, startIndex)
  333. const hiddenNav = menuNavList.slice(startIndex, menuNavList.length)
  334. menuList.value = showNav
  335. menuList.value.push({
  336. meta: {
  337. icon: 'rightCircle-outlined',
  338. title: '更多',
  339. type: 'catalog'
  340. },
  341. children: hiddenNav
  342. })
  343. })
  344. }
  345. // 鼠标滚动事件
  346. const handleMouseWheel = (event) => {
  347. let element = document.querySelector('#xn-line-nav')
  348. let element2 = document.querySelector('#topHeaderMenu')
  349. // 判断鼠标是向上滚动还是向下滚动的
  350. let delta = event.deltaY
  351. // 滚动菜单时变换的一个大小
  352. const num = 20
  353. // 滚动的距离
  354. let leftMove = Number(element2.style.left.slice(0, -2))
  355. // 父盒子相对子盒子滚动的一个判断,用来给盒子加一个临界点
  356. let remove = element.offsetWidth - element2.scrollWidth
  357. // 鼠标向下滚动
  358. // 满足子元素移动的距离大于两个元素相差的距离时
  359. if (delta < 0 && leftMove > remove) {
  360. element2.style.left = leftMove - num + 'px'
  361. } else if (delta > 0 && leftMove < 0) {
  362. // 鼠标向上滚动
  363. // 当移动的距离小于0的时候,可以向后滚动
  364. element2.style.left = leftMove + num + 'px'
  365. }
  366. }
  367. watch(route, () => {
  368. // 清理选中的
  369. selectedKeys.value = []
  370. showThis()
  371. })
  372. // 监听是否开启了顶栏颜色
  373. watch(layout, (newValue) => {
  374. document.body.setAttribute('data-layout', newValue)
  375. if (newValue.includes(layoutEnum.DOUBLEROW)) {
  376. showThis()
  377. setDoubleRowSelectedKey()
  378. }
  379. nextTick(() => {
  380. // 顶栏主题色
  381. switchoverTopHeaderThemeColor()
  382. // top下的顶栏
  383. settingTopHeaderThemeOrColor(theme.value, newValue)
  384. getNav()
  385. settingFixedWidth()
  386. let element = document.querySelector('#xn-line-nav')
  387. if (element) {
  388. element.addEventListener('mousewheel', handleMouseWheel, false)
  389. }
  390. })
  391. })
  392. watch(topHeaderThemeColorOpen, () => {
  393. switchoverTopHeaderThemeColor()
  394. })
  395. watch(fixedWidth, () => {
  396. settingFixedWidth()
  397. })
  398. watch(layoutTagsOpen, () => {
  399. settingFixedWidth()
  400. })
  401. watch(breadcrumbOpen, () => {
  402. settingFixedWidth()
  403. })
  404. watch(topHeaderThemeColorSpread, () => {
  405. switchoverTopHeaderThemeColor()
  406. })
  407. watch(theme, (newValue) => {
  408. settingTopHeaderThemeOrColor(newValue, layout.value)
  409. })
  410. watch(themeColor, () => {
  411. settingTopHeaderThemeOrColor(theme.value, layout.value)
  412. })
  413. watch(topHeaderThemeColorOpen, (newValue) => {
  414. const header = document.getElementById('snowyHeader')
  415. const topHeaderMenu = document.getElementById('topHeaderMenu')
  416. if (layout.value === layoutEnum.TOP) {
  417. if (newValue) {
  418. header.classList.add('top-snowy-header-layout')
  419. topHeaderMenu.classList.add('top-snowy-header-layout')
  420. } else {
  421. header.classList.remove('top-snowy-header-layout')
  422. topHeaderMenu.classList.remove('top-snowy-header-layout')
  423. }
  424. }
  425. })
  426. watch(roundedCornerStyleOpen, () => {
  427. settingTopHeaderThemeOrColor(theme.value, layout.value)
  428. })
  429. // 设置固定宽度
  430. const settingFixedWidth = () => {
  431. nextTick(() => {
  432. const breadcrumbWidth = document.querySelector('.admin-ui-breadcrumb')
  433. const showWidth = document.querySelector('.snowy-tags')
  434. const mainWidth = document.querySelector('.ant-layout-content')
  435. if (fixedWidth.value && layout.value === layoutEnum.TOP) {
  436. breadcrumbWidth?.classList.add('xn-mg050')
  437. showWidth?.classList.add('xn-mg050')
  438. mainWidth?.classList.add('xn-pd1180')
  439. } else {
  440. breadcrumbWidth?.classList.remove('xn-mg050')
  441. showWidth?.classList.remove('xn-mg050')
  442. mainWidth?.classList.remove('xn-pd1180')
  443. }
  444. })
  445. }
  446. // 设置顶栏颜色或者主题
  447. const settingTopHeaderThemeOrColor = (theme, layout) => {
  448. const header = document.getElementById('snowyHeader')
  449. const topHeaderMenu = document.getElementById('topHeaderMenu')
  450. if (topHeaderThemeColorOpen.value && layout === layoutEnum.TOP) {
  451. nextTick(() => {
  452. topHeaderMenu.classList.add('top-snowy-header-layout')
  453. header.classList.add('top-snowy-header-layout')
  454. })
  455. } else if (!topHeaderThemeColorOpen.value && layout === layoutEnum.TOP) {
  456. nextTick(() => {
  457. topHeaderMenu.classList.remove('top-snowy-header-layout')
  458. header.classList.remove('top-snowy-header-layout')
  459. })
  460. }
  461. if (theme === themeEnum.LIGHT && layout === layoutEnum.TOP) {
  462. header.classList.remove('top-snowy-header')
  463. header.classList.add('top-snowy-header-light')
  464. } else {
  465. header.classList.remove('top-snowy-header-light')
  466. if (layout === layoutEnum.TOP) {
  467. header.classList.add('top-snowy-header')
  468. } else {
  469. if (theme === themeEnum.REAL_DARK) {
  470. header.classList.add('top-snowy-header')
  471. } else {
  472. header.classList.remove('top-snowy-header')
  473. }
  474. }
  475. }
  476. }
  477. const menuIsCollapseClick = () => {
  478. store.toggleConfig('menuIsCollapse')
  479. }
  480. // 切换顶栏颜色
  481. const switchoverTopHeaderThemeColor = () => {
  482. try {
  483. // 界面顶栏设置颜色
  484. const header = document.getElementById('snowyHeader')
  485. topHeaderThemeColorOpen.value
  486. ? header.classList.add('snowy-header-primary-color')
  487. : header.classList.remove('snowy-header-primary-color')
  488. // 判断是否开启了通栏
  489. const headerLogin = document.getElementById('snowyHeaderLogo')
  490. topHeaderThemeColorSpread.value
  491. ? headerLogin.classList.add('snowy-header-logo-primary-color')
  492. : headerLogin.classList.remove('snowy-header-logo-primary-color')
  493. // eslint-disable-next-line no-empty
  494. } catch (e) {}
  495. }
  496. // 设置双排菜单下的首列默认选中
  497. const setDoubleRowSelectedKey = () => {
  498. const pidKey = getParentKeys(menu.value, selectedKeys.value.toString())
  499. nextTick(() => {
  500. const pidKeyArray = []
  501. for (const key in pidKey) {
  502. pidKeyArray.push(key)
  503. }
  504. layoutSiderDowbleMenu.value = pidKeyArray.length > 1
  505. })
  506. // 设置第一排选中的
  507. menu.value.forEach((item) => {
  508. if (pidKey !== undefined) {
  509. if (pidKey[pidKey.length - 1].toString() === item.path) {
  510. doublerowSelectedKey.value = [item.path]
  511. }
  512. }
  513. })
  514. }
  515. // 菜单展开/关闭的回调
  516. const onOpenChange = (keys) => {
  517. if (sideUniqueOpen.value) {
  518. // 获取最新的
  519. const openKey = keys[keys.length - 1]
  520. if (keys.length > 1) {
  521. // 获取上级
  522. openKeys.value = getParentKeys(menu.value, openKey)
  523. } else {
  524. openKeys.value = Array.of(openKey) // new Array(openKey);
  525. }
  526. } else {
  527. openKeys.value = keys
  528. }
  529. }
  530. // 获取上级keys
  531. const getParentKeys = (data, val) => {
  532. const traverse = (array, val) => {
  533. // 递归父级key
  534. for (const element of array) {
  535. if (element.path === val) {
  536. return [element.path]
  537. }
  538. if (element.children) {
  539. const far = traverse(element.children, val)
  540. if (far) {
  541. return far.concat(element.path)
  542. }
  543. }
  544. }
  545. }
  546. return traverse(data, val)
  547. }
  548. // 双排菜单下点击显示右侧分栏
  549. const showMenu = (route) => {
  550. pMenu.value = route
  551. if (pMenu.value.children) {
  552. nextMenu.value = pMenu.value.children
  553. }
  554. if (!route.children || route.children.length === 0) {
  555. layoutSiderDowbleMenu.value = false
  556. router.push({ path: route.path })
  557. } else {
  558. if (route.children) {
  559. let hidden = 0
  560. route.children.forEach((item) => {
  561. if (item.meta.hidden && item.meta.hidden === true) {
  562. hidden++
  563. }
  564. })
  565. // 如果全部都隐藏了,就跳转这个,不展开另一排
  566. if (hidden === route.children.length) {
  567. layoutSiderDowbleMenu.value = false
  568. router.push({ path: route.path })
  569. } else {
  570. layoutSiderDowbleMenu.value = true
  571. }
  572. } else {
  573. layoutSiderDowbleMenu.value = false
  574. }
  575. }
  576. if (layout.value === layoutEnum.DOUBLEROW) {
  577. doublerowSelectedKey.value = [route.path]
  578. }
  579. }
  580. // 当菜单被选中时
  581. const onSelect = (obj) => {
  582. onSelectTag.value = true
  583. const pathLength = obj.keyPath.length
  584. const path = obj.keyPath[pathLength - 1]
  585. router.push({ path })
  586. // 设置选中
  587. selectedKeys.value = obj.selectedKeys
  588. }
  589. const onLayoutResize = () => {
  590. const clientWidth = document.body.clientWidth
  591. store.setIsMobile(clientWidth < 992)
  592. }
  593. // 切换应用
  594. const switchModule = (id) => {
  595. if (moduleMenu.value.length > 0) {
  596. showThis()
  597. const menus = moduleMenu.value.filter((item) => item.id === id)[0].children
  598. if (menus.length > 0) {
  599. // 正儿八百的菜单
  600. menu.value = menus
  601. const firstMenu = traverseChild(menu.value)
  602. const path = firstMenu.path
  603. // 如果是外链
  604. if (firstMenu.menuType === 'LINK') {
  605. window.open(path)
  606. } else {
  607. // 将此模块的唯一值加入缓存
  608. tool.data.set('SNOWY_MENU_MODULE_ID', id)
  609. // 然后将其跳转至指定界面,默认始终取排序第一的
  610. router.push({ path })
  611. }
  612. } else {
  613. message.warning('该模块下无任何菜单')
  614. }
  615. }
  616. getNav()
  617. }
  618. // 通过标签切换应用
  619. const tagSwitchModule = (id) => {
  620. // 将此模块的唯一值加入缓存
  621. tool.data.set('SNOWY_MENU_MODULE_ID', id)
  622. store.setModule(id)
  623. // 正儿八百的菜单
  624. menu.value = moduleMenu.value.filter((item) => item.id === id)[0].children
  625. }
  626. // 遍历获取子集
  627. const traverseChild = (menu) => {
  628. if (menu[0] && menu[0].children !== undefined) {
  629. if (menu[0].children.length > 0) {
  630. if (menu[0].children[0] && menu[0].children[0].meta.hidden && menu[0].children[0].meta.hidden === true) {
  631. return menu[0]
  632. } else {
  633. return traverseChild(menu[0].children)
  634. }
  635. }
  636. } else {
  637. return menu[0]
  638. }
  639. }
  640. // 退出最大化
  641. const exitMaximize = () => {
  642. document.getElementById('app').classList.remove('main-maximize')
  643. moduleMenuShow.value = false
  644. nextTick(() => {
  645. moduleMenuShow.value = true
  646. })
  647. }
  648. </script>
  649. <style lang="less" scoped>
  650. .xn-color-fff {
  651. color: #fff;
  652. }
  653. .xn-pdl25 {
  654. padding-left: 11px;
  655. }
  656. .xn-menu-line {
  657. text-align: center;
  658. height: auto;
  659. line-height: 20px;
  660. flex: none;
  661. display: block;
  662. padding: 12px 0 !important;
  663. }
  664. .xn-navmenu-line {
  665. min-width: 0;
  666. flex: 1 1 0%;
  667. // padding: 0 20px;
  668. overflow: hidden;
  669. }
  670. .xn-bb0 {
  671. border-bottom: none;
  672. position: relative;
  673. }
  674. .ant-layout-content {
  675. display: flex;
  676. flex-direction: column;
  677. }
  678. .xn-pd1180 {
  679. padding: 10px 150px 0 150px;
  680. }
  681. .xn-pd050 {
  682. padding: 0 50px;
  683. }
  684. .xn-pl10 {
  685. padding-left: 10px;
  686. }
  687. .xn-mg050 {
  688. margin: 0px 150px;
  689. }
  690. </style>