index.jsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { defineComponent, watch, getCurrentInstance } from 'vue'
  2. import { useRoute } from 'vue-router'
  3. import { ElTabs, ElTabPane, ElTooltip } from 'element-plus'
  4. import {
  5. getRouteTitle,
  6. getMenuTitle,
  7. getRouteCache,
  8. isCacheView,
  9. matchMenuByRouteName,
  10. getRouteIcon,
  11. getMenuIcon,
  12. getFullPathWithoutHash
  13. } from '../helper'
  14. import { useOpenedView, useCacheView } from '@cip/components/main/cip-main-tabs/hooks'
  15. import { useCipConfig } from '../../hooks/use-cip-config'
  16. import CipMainIcon from '../cip-main-icon'
  17. import './index.less'
  18. import { isEmpty } from '@cip/utils/util'
  19. export default defineComponent({
  20. name: 'CipTabs',
  21. props: {
  22. menu: Array, // 菜单配置
  23. cache: Array, // 缓存fullPath列表
  24. menuTitleMap: Object, // 自定义fullPath对应的视图title
  25. homeView: Object // 首页视图配置
  26. },
  27. emits: ['update:cache'],
  28. setup (props, { emit }) {
  29. const cipConfig = useCipConfig()
  30. const route = useRoute()
  31. const instance = getCurrentInstance()
  32. // 已打开的view(包含未缓存及已缓存的视图)
  33. const { openedViewList, activeView, addOpenedView, updateOpenedView, removeOpenedView, findViewByOpenedViewList, setActiveView } = useOpenedView(props.homeView)
  34. const updateCache = (cache) => { emit('update:cache', cache) }
  35. const { cacheViewList, addCacheView, removeCacheView } = useCacheView(updateCache)
  36. // menu 配置的优先级高于 route
  37. const getViewConfig = (map, menu, view) => {
  38. let viewTitle = getRouteTitle(view) // 获取路由信息中的title
  39. let isCache = getRouteCache(view) // 获取路由信息中的cache
  40. let icon = getRouteIcon(view)
  41. const customTitle = map[view.fullPath]
  42. const menuMatch = matchMenuByRouteName(menu, view.name)
  43. if (menuMatch) {
  44. const innerIcon = getMenuIcon(menuMatch) // 开启递归查找 倒着查找
  45. if (!isEmpty(innerIcon)) icon = innerIcon
  46. const currentMenu = menuMatch.pop()
  47. if (!isEmpty(getMenuTitle(currentMenu))) viewTitle = getMenuTitle(currentMenu) // 匹配到菜单项后获取菜单项的title
  48. if (!isEmpty(isCacheView(currentMenu))) isCache = isCacheView(currentMenu)
  49. }
  50. const title = [viewTitle, customTitle].filter(v => !!v).join('-')
  51. return {
  52. title,
  53. isCache,
  54. icon
  55. }
  56. }
  57. const handlerPathChange = (path) => {
  58. const { matched, ...currentView } = route
  59. currentView.fullPath = path // 重写fullPath
  60. const primaryKey = cipConfig.withQuery === false ? 'path' : 'fullPath'
  61. const index = openedViewList.value.findIndex(openedView => openedView[primaryKey] === currentView[primaryKey])
  62. if (index === -1 || index === 0) {
  63. const { title, isCache, icon } = getViewConfig(props.menuTitleMap, props.menu, currentView)
  64. title && (currentView.title = title)
  65. icon && (currentView.icon = icon)
  66. currentView.isCache = isCache
  67. if (index === -1) addOpenedView(currentView)
  68. if (index === 0) updateOpenedView(index, currentView)
  69. if (isCache) {
  70. addCacheView(currentView.fullPath)
  71. emit('update:cache', cacheViewList.value)
  72. }
  73. } else {
  74. if (primaryKey === 'path') {
  75. openedViewList.value[index].fullPath = currentView.fullPath
  76. }
  77. setActiveView(openedViewList.value[index])
  78. }
  79. }
  80. watch(() => getFullPathWithoutHash(route.fullPath), (val) => {
  81. handlerPathChange(val)
  82. }, { immediate: true })
  83. // 打开view 缓存 tab名称
  84. // 控制tab名称及缓存
  85. watch(() => props.menu, (val) => { // menu变化是对整个openedViewList进行viewName计算
  86. openedViewList.value.forEach((openedView) => {
  87. const { title, isCache, icon } = getViewConfig(props.menuTitleMap, val, openedView)
  88. title && (openedView.title = title)
  89. icon && (openedView.icon = icon)
  90. if (openedView.isCache !== isCache) {
  91. // 存在变化的openedView
  92. openedView.isCache = isCache
  93. isCache
  94. ? addCacheView(openedView.fullPath)
  95. : removeCacheView(openedView.fullPath)
  96. }
  97. })
  98. }, { immediate: true })
  99. // 控制tab名称
  100. watch(() => props.menuTitleMap, (val) => {
  101. Object.keys(val).forEach(fullPath => {
  102. const matchRoute = openedViewList.value.find(view => view.fullPath === fullPath)
  103. if (matchRoute) {
  104. const { title, icon } = getViewConfig(val, props.menu, matchRoute)
  105. matchRoute.title = title
  106. matchRoute.icon = icon
  107. }
  108. })
  109. }, { deep: true, immediate: true })
  110. // 前往Tab视图
  111. const toTab = (fullPath) => {
  112. const view = findViewByOpenedViewList(fullPath)
  113. setActiveView(view)
  114. }
  115. // 删除当前Tab视图
  116. const removeTab = (fullPath) => {
  117. const view = findViewByOpenedViewList(fullPath)
  118. removeCacheView(fullPath)
  119. removeOpenedView(view)
  120. }
  121. instance.ctx.removeTab = removeTab
  122. instance.ctx.handlerPathChange = handlerPathChange
  123. return () => (
  124. <div class={['cip-main-tabs']}>
  125. <ElTabs
  126. type="card"
  127. modelValue={activeView.value.fullPath}
  128. onTabClick={({ paneName }) => toTab(paneName)}
  129. onTabRemove={(paneName) => { removeTab(paneName) }}>
  130. {openedViewList.value.map(openedView => (
  131. <ElTabPane
  132. key={openedView.fullPath}
  133. name={openedView.fullPath}
  134. label={openedView.title || openedView.name}
  135. closable={openedView.fullPath !== props.homeView?.fullPath}
  136. onClick={() => toTab(openedView)}
  137. onClose={() => removeOpenedView(openedView)} >
  138. {{
  139. label: () => <>
  140. {openedView.icon && <CipMainIcon style={'margin-right: 4px'} name={openedView.icon}/>}
  141. <ElTooltip content={openedView.title || openedView.name} >
  142. <span class={'cip-main-tab__label'}>{openedView.title || openedView.name}</span>
  143. </ElTooltip>
  144. </>
  145. }}
  146. </ElTabPane>
  147. ))}
  148. </ElTabs>
  149. </div>
  150. )
  151. }
  152. })