index.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <template>
  2. <main-framework
  3. :class="`main-theme main-theme--${theme}`"
  4. :hide-footer="hideFooter"
  5. :hide-aside-switch="hideAsideSwitch"
  6. :layout="usingLayout"
  7. :with-tabs="withTabs"
  8. :hide-aside="dynamicMenu.length === 0"
  9. >
  10. <template #name="{isCollapse}">
  11. <slot name="collapse" v-if="isCollapse">
  12. <span style="margin-left: 8px;">{{name?.[0]}}</span>
  13. </slot>
  14. <slot name="expand" v-else>
  15. <span style="margin-left: 8px;">{{name}}</span>
  16. </slot>
  17. <slot name="name" :is-collapse="isCollapse"></slot>
  18. </template>
  19. <template #nav="{isCollapse}">
  20. <slot name="nav">
  21. <cip-main-nav :nav-menu="dynamicMenu"
  22. :theme="theme"
  23. :icon-depth="iconDepth"
  24. :mode="navMode"
  25. :isCollapse="isCollapse"
  26. :ellipsis="true"
  27. :badgeMap="badgeMap"
  28. @triggerGetBadge="consumptionBadgeFn"/>
  29. </slot>
  30. </template>
  31. <template #headerNav>
  32. <!-- 平铺模式的菜单[注:仅显示一层]-->
  33. <cip-main-nav :nav-menu="menu"
  34. v-model:activeMenu="activeMenu"
  35. :icon-depth="iconDepth"
  36. mode="horizontal"
  37. :ellipsis="true"
  38. :top-menu-only="true"
  39. :badgeMap="badgeMap"
  40. @triggerGetBadge="consumptionBadgeFn"/>
  41. </template>
  42. <template #tabs>
  43. <cip-tabs
  44. ref="tabsRef"
  45. :menu="menu"
  46. :menu-title-map="menuPathMap"
  47. :home-view="homeView"
  48. :with-tabs="usingTabs"
  49. v-model:cache="cache"
  50. />
  51. </template>
  52. <template #breadcrumb>
  53. <cip-main-breadcrumb />
  54. </template>
  55. <template #header>
  56. <header-bar>
  57. <template #header-plugin><slot name="header-plugin" /></template>
  58. <template #header-user><slot name="header-user" /></template>
  59. <template v-if="$slots.dropdown" #dropdown><slot name="dropdown" /></template>
  60. <template v-if="$slots['pre-dropdown']" #pre-dropdown><slot name="pre-dropdown" /></template>
  61. </header-bar>
  62. </template>
  63. <cip-router-view :viewKey="viewKey" :cacheList="cache"/>
  64. <template #footer>
  65. <slot name="footer"></slot>
  66. </template>
  67. </main-framework>
  68. </template>
  69. <script>
  70. import { computed, ref, watch, provide, reactive, onUnmounted } from 'vue'
  71. import { useCipConfig } from '../hooks/use-cip-config'
  72. import MainFramework from './framework/framework'
  73. import CipMainNav from './cip-main-nav'
  74. import CipMainBreadcrumb from './cip-main-breadcrumb'
  75. import HeaderBar from './header-bar'
  76. import CipTabs from './cip-main-tabs'
  77. import CipRouterView from './cip-router-view'
  78. import { useRoute } from 'vue-router'
  79. import { useBadge, useMenuTitle } from './main-hooks'
  80. import { getFullPathWithoutHash } from './helper'
  81. import { isSubApp } from '../cip-subapp-container/micro-app/util'
  82. export default {
  83. name: 'Main',
  84. components: { MainFramework, CipMainNav, HeaderBar, CipMainBreadcrumb, CipTabs, CipRouterView },
  85. props: {
  86. logo: String, // 平台LOGO
  87. platformName: String, // 平台名称
  88. iconDepth: Number, // 菜单的icon展示的最大层级
  89. navMenu: Array, // 导航菜单
  90. // privileges: Array, // 权限
  91. hideFooter: Boolean, // 是否隐藏footer
  92. hideAsideSwitch: Boolean, // 是否隐藏手风琴折叠开关
  93. theme: {
  94. type: String,
  95. default: 'standard' // 默认使用数据中台的主题
  96. // validator: (value) => {
  97. // return ['data-center', 'light', 'dark'].includes(value)
  98. // }
  99. }, // 主题
  100. layout: {
  101. type: String,
  102. default: 'left-2',
  103. validator: (value) => {
  104. return ['left', 'left-2', 'top', 'top-left', 'hide'].includes(value)
  105. }
  106. }, // 布局
  107. withTabs: Boolean, // 是否使用tabs)
  108. homeView: Object, // 首页配置(开启tabs时建议开启)
  109. badgeInterval: Number, // 获取badge的时间间隔 单位min(分钟)
  110. noViewKey: { type: Boolean, default: undefined }
  111. },
  112. setup (props) {
  113. const cipConfig = useCipConfig()
  114. const route = useRoute()
  115. const activeMenu = ref([])
  116. const cache = ref([])
  117. const { menuNameMap, menuPathMap } = useMenuTitle()
  118. // 菜单
  119. const menu = computed(() => {
  120. return props.navMenu
  121. })
  122. const layoutHide = ref(false)
  123. const setFrameHide = (val) => {
  124. layoutHide.value = val
  125. }
  126. onUnmounted(() => {
  127. console.log('Main Unmounted')
  128. })
  129. const usingLayout = computed(() => {
  130. return layoutHide.value ? 'hide' : props.layout
  131. })
  132. const usingTabs = computed(() => {
  133. if (!props.withTabs) return false
  134. return !isSubApp(route.name)
  135. })
  136. const dynamicMenu = computed(() => {
  137. if (usingLayout.value !== 'top-left') return menu.value
  138. if (!activeMenu.value || activeMenu.value.length === 0) return []
  139. // 明确标记没有children时返回当前menu
  140. if ((activeMenu.value.children || []).length === 0) return [activeMenu.value]
  141. // 传入当前激活路由top menu的children
  142. return activeMenu.value.children
  143. })
  144. const oneMinute = 60 * 1000
  145. const { badgeMap, collectBadgeFn, consumptionBadgeFn } = useBadge(props.badgeInterval * oneMinute)
  146. watch(menu, (val) => {
  147. collectBadgeFn(val, { key: 'name', fnKey: 'getBadge' })
  148. }, { immediate: true })
  149. // 平台名称
  150. const name = computed(() => {
  151. return props.platformName ?? process.env.VUE_APP_PLATFORM_NAME
  152. })
  153. // 菜单模式
  154. const navMode = computed(() => {
  155. return props.layout === 'top' ? 'horizontal' : 'vertical'
  156. })
  157. // 兼容KeepAlivePages组件的运行
  158. const viewKey = computed(() => {
  159. if (props.noViewKey) return 'viewKey'
  160. if (isSubApp(route.name)) return 'SubApp'
  161. const routeMatch = route.matched
  162. const length = routeMatch.length
  163. const cacheName = length > 1 ? routeMatch[routeMatch.length - 2].name : undefined
  164. if (cacheName?.indexOf('_cache') > -1) {
  165. return cacheName
  166. }
  167. return cipConfig.withQuery === false ? route.path : getFullPathWithoutHash(route.fullPath)
  168. })
  169. const tabsRef = ref()
  170. const closeTab = () => {
  171. const fullPath = getFullPathWithoutHash(route.fullPath)
  172. // eslint-disable-next-line no-unused-expressions
  173. tabsRef.value?.removeTab(fullPath)
  174. }
  175. const handlerPathChange = (fullPath) => {
  176. fullPath = getFullPathWithoutHash(fullPath)
  177. // eslint-disable-next-line no-unused-expressions
  178. tabsRef.value?.handlerPathChange(fullPath)
  179. }
  180. provide('closeTab', closeTab)
  181. provide('pathChange', handlerPathChange)
  182. provide('cip-menu', reactive({
  183. menuNameMap,
  184. navMenu: menu
  185. }))
  186. provide('setFrameHide', setFrameHide)
  187. const setChangeSign = () => {
  188. // const fullPath = getFullPathWithoutHash(route.fullPath)
  189. }
  190. provide('setChangeSign', setChangeSign)
  191. return {
  192. cache,
  193. menu,
  194. name,
  195. menuNameMap,
  196. menuPathMap,
  197. navMode,
  198. dynamicMenu,
  199. activeMenu,
  200. viewKey,
  201. badgeMap,
  202. consumptionBadgeFn,
  203. tabsRef,
  204. usingLayout,
  205. usingTabs,
  206. closeTab
  207. }
  208. }
  209. }
  210. </script>
  211. <style lang="less">
  212. @import './theme';
  213. </style>