perf: Improve the dynamic routing and automatically close the Tab function (#1264)
* 增加动态路由最大打开Tab数控制
* 增加动态路由打开数控制Router参数
* feat(Tab): 新增动态路由打开数限制Demo
* fix(multipleTab.ts): 将原来的打开数限制从固定的 5 修改为读取配置
Co-authored-by: Haceral <18274416193@163.com>
| | |
| | | breadcrumb: 'Breadcrumbs', |
| | | breadcrumbIcon: 'Breadcrumbs Icon', |
| | | tabs: 'Tabs', |
| | | tabDetail: 'Tab Detail', |
| | | tabsQuickBtn: 'Tabs quick button', |
| | | tabsRedoBtn: 'Tabs redo button', |
| | | tabsFoldBtn: 'Tabs flod button', |
| | |
| | | feat: 'Page Function', |
| | | icon: 'Icon', |
| | | tabs: 'Tabs', |
| | | tabDetail: 'Tab Detail', |
| | | sessionTimeout: 'Session Timeout', |
| | | print: 'Print', |
| | | contextMenu: 'Context Menu', |
| | |
| | | breadcrumb: '面包屑', |
| | | breadcrumbIcon: '面包屑图标', |
| | | tabs: '标签页', |
| | | tabDetail: '标签详情页', |
| | | tabsQuickBtn: '标签页快捷按钮', |
| | | tabsRedoBtn: '标签页刷新按钮', |
| | | tabsFoldBtn: '标签页折叠按钮', |
| | |
| | | icon: '图标', |
| | | sessionTimeout: '登录过期', |
| | | tabs: '标签页操作', |
| | | tabDetail: '标签详情页', |
| | | print: '打印', |
| | | contextMenu: '右键菜单', |
| | | download: '文件下载', |
| | |
| | | component: () => import('/@/views/demo/feat/tabs/index.vue'), |
| | | meta: { |
| | | title: t('routes.demo.feat.tabs'), |
| | | hideChildrenInMenu: true, |
| | | }, |
| | | children: [ |
| | | { |
| | | path: 'detail/:id', |
| | | name: 'TabDetail', |
| | | component: () => import('/@/views/demo/feat/tabs/TabDetail.vue'), |
| | | meta: { |
| | | currentActiveMenu: '/feat/tabs', |
| | | title: t('routes.demo.feat.tabDetail'), |
| | | hideMenu: true, |
| | | dynamicLevel: 3, |
| | | realPath: '/feat/tabs/detail', |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | | path: 'breadcrumb', |
| | |
| | | this.tabList.splice(updateIndex, 1, curTab); |
| | | } else { |
| | | // Add tab |
| | | // 获取动态路由层级 |
| | | // 获取动态路由打开数,超过 0 即代表需要控制打开数 |
| | | const dynamicLevel = meta?.dynamicLevel ?? -1; |
| | | if (dynamicLevel > 0) { |
| | | // 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了 |
| | |
| | | // const realName: string = path.match(/(\S*)\//)![1]; |
| | | const realPath = meta?.realPath ?? ''; |
| | | // 获取到已经打开的动态路由数, 判断是否大于某一个值 |
| | | // 这里先固定为 每个动态路由最大能打开【5】个Tab |
| | | if (this.tabList.filter((e) => e.meta?.realPath ?? '' === realPath).length >= 5) { |
| | | if ( |
| | | this.tabList.filter((e) => e.meta?.realPath ?? '' === realPath).length >= dynamicLevel |
| | | ) { |
| | | // 关闭第一个 |
| | | const index = this.tabList.findIndex((item) => item.meta.realPath === realPath); |
| | | index !== -1 && this.tabList.splice(index, 1); |
New file |
| | |
| | | <template> |
| | | <PageWrapper title="Tab详情页面"> |
| | | <div>{{ index }} - 详情页内容在此</div> |
| | | </PageWrapper> |
| | | </template> |
| | | |
| | | <script lang="ts"> |
| | | import { defineComponent } from 'vue'; |
| | | import { PageWrapper } from '/@/components/Page'; |
| | | import { useTabs } from '/@/hooks/web/useTabs'; |
| | | import { useRoute } from 'vue-router'; |
| | | |
| | | export default defineComponent({ |
| | | name: 'TabDetail', |
| | | components: { PageWrapper }, |
| | | setup() { |
| | | const route = useRoute(); |
| | | const index = route.params?.id ?? -1; |
| | | const { setTitle } = useTabs(); |
| | | |
| | | // 设置标识 |
| | | setTitle(`No.${index} - 详情信息`); |
| | | return { |
| | | index, |
| | | }; |
| | | }, |
| | | }); |
| | | </script> |
| | |
| | | <a-button class="mr-2" @click="closeCurrent"> 关闭当前 </a-button> |
| | | <a-button class="mr-2" @click="refreshPage"> 刷新当前 </a-button> |
| | | </CollapseContainer> |
| | | |
| | | <CollapseContainer class="mt-4" title="标签页复用超出限制自动关闭(使用场景: 动态路由)"> |
| | | <a-button v-for="index in 6" :key="index" class="mr-2" @click="toDetail(index)"> |
| | | 打开{{ index }}详情页 |
| | | </a-button> |
| | | </CollapseContainer> |
| | | </PageWrapper> |
| | | </template> |
| | | <script lang="ts"> |
| | | import { defineComponent, ref } from 'vue'; |
| | | import { CollapseContainer } from '/@/components/Container/index'; |
| | | import { CollapseContainer } from '/@/components/Container'; |
| | | import { useTabs } from '/@/hooks/web/useTabs'; |
| | | import { PageWrapper } from '/@/components/Page'; |
| | | import { Input, Alert } from 'ant-design-vue'; |
| | | import { useMessage } from '/@/hooks/web/useMessage'; |
| | | import { useGo } from '/@/hooks/web/usePage'; |
| | | |
| | | export default defineComponent({ |
| | | name: 'TabsDemo', |
| | | components: { CollapseContainer, PageWrapper, [Input.name]: Input, [Alert.name]: Alert }, |
| | | setup() { |
| | | const go = useGo(); |
| | | const title = ref<string>(''); |
| | | const { closeAll, closeLeft, closeRight, closeOther, closeCurrent, refreshPage, setTitle } = |
| | | useTabs(); |
| | |
| | | createMessage.error('请输入要设置的Tab标题!'); |
| | | } |
| | | } |
| | | |
| | | function toDetail(index: number) { |
| | | go(`/feat/tabs/detail/${index}`); |
| | | } |
| | | return { |
| | | closeAll, |
| | | closeLeft, |
| | | closeRight, |
| | | closeOther, |
| | | closeCurrent, |
| | | toDetail, |
| | | refreshPage, |
| | | setTabTitle, |
| | | title, |