feat(projects): 支持同一路由根据不同query和hash同时显示不同Tab

ISSUES CLOSED: #64
This commit is contained in:
Soybean 2022-06-07 00:56:25 +08:00
parent 434ab1c560
commit 4122685803
20 changed files with 364 additions and 89 deletions

View File

@ -302,6 +302,53 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
order: 5 order: 5
} }
}, },
{
name: 'function',
path: '/function',
component: 'basic',
children: [
{
name: 'function_tab',
path: '/function/tab',
component: 'self',
meta: {
title: 'Tab',
requiresAuth: true,
icon: 'ic:round-tab'
}
},
{
name: 'function_tab-detail',
path: '/function/tab-detail',
component: 'self',
meta: {
title: 'Tab Detail',
requiresAuth: true,
hide: true,
activeMenu: 'function_tab',
icon: 'ic:round-tab'
}
},
{
name: 'function_tab-multi-detail',
path: '/function/tab-multi-detail',
component: 'self',
meta: {
title: 'Tab Multi Detail',
requiresAuth: true,
hide: true,
multiTab: true,
activeMenu: 'function_tab',
icon: 'ic:round-tab'
}
}
],
meta: {
title: '功能',
icon: 'ri:function-line',
order: 6
}
},
{ {
name: 'exception', name: 'exception',
path: '/exception', path: '/exception',
@ -341,7 +388,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '异常页', title: '异常页',
icon: 'ant-design:exception-outlined', icon: 'ant-design:exception-outlined',
order: 6 order: 7
} }
}, },
{ {
@ -395,7 +442,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '多级菜单', title: '多级菜单',
icon: 'carbon:menu', icon: 'carbon:menu',
order: 7 order: 8
} }
}, },
{ {
@ -407,7 +454,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
requiresAuth: true, requiresAuth: true,
singleLayout: 'basic', singleLayout: 'basic',
icon: 'fluent:book-information-24-regular', icon: 'fluent:book-information-24-regular',
order: 8 order: 9
} }
} }
], ],
@ -704,6 +751,53 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
order: 5 order: 5
} }
}, },
{
name: 'function',
path: '/function',
component: 'basic',
children: [
{
name: 'function_tab',
path: '/function/tab',
component: 'self',
meta: {
title: 'Tab',
requiresAuth: true,
icon: 'ic:round-tab'
}
},
{
name: 'function_tab-detail',
path: '/function/tab-detail',
component: 'self',
meta: {
title: 'Tab Detail',
requiresAuth: true,
hide: true,
activeMenu: 'function_tab',
icon: 'ic:round-tab'
}
},
{
name: 'function_tab-multi-detail',
path: '/function/tab-multi-detail',
component: 'self',
meta: {
title: 'Tab Multi Detail',
requiresAuth: true,
hide: true,
multiTab: true,
activeMenu: 'function_tab',
icon: 'ic:round-tab'
}
}
],
meta: {
title: '功能',
icon: 'ri:function-line',
order: 6
}
},
{ {
name: 'exception', name: 'exception',
path: '/exception', path: '/exception',
@ -743,7 +837,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '异常页', title: '异常页',
icon: 'ant-design:exception-outlined', icon: 'ant-design:exception-outlined',
order: 6 order: 7
} }
}, },
{ {
@ -797,7 +891,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
meta: { meta: {
title: '多级菜单', title: '多级菜单',
icon: 'carbon:menu', icon: 'carbon:menu',
order: 7 order: 8
} }
}, },
{ {
@ -809,7 +903,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
requiresAuth: true, requiresAuth: true,
singleLayout: 'basic', singleLayout: 'basic',
icon: 'fluent:book-information-24-regular', icon: 'fluent:book-information-24-regular',
order: 8 order: 9
} }
} }
], ],

View File

@ -16,7 +16,7 @@ export enum EnumStorageKey {
/** 用户信息 */ /** 用户信息 */
'user-info' = '__USER_INFO__', 'user-info' = '__USER_INFO__',
/** 多页签路由信息 */ /** 多页签路由信息 */
'tab-routes' = '__TAB_ROUTES__' 'multi-tab-routes' = '__MULTI_TAB_ROUTES__'
} }
/** 数据类型 */ /** 数据类型 */

View File

@ -12,7 +12,7 @@
@after-enter="handleAfterEnter" @after-enter="handleAfterEnter"
> >
<keep-alive :include="routeStore.cacheRoutes"> <keep-alive :include="routeStore.cacheRoutes">
<component :is="Component" v-if="app.reloadFlag" :key="route.path" /> <component :is="Component" v-if="app.reloadFlag" :key="route.fullPath" />
</keep-alive> </keep-alive>
</transition> </transition>
</router-view> </router-view>

View File

@ -69,7 +69,7 @@ const options = computed<Option[]>(() => [
{ {
label: '关闭', label: '关闭',
key: 'close-current', key: 'close-current',
disabled: props.currentPath === tab.homeTab.path, disabled: props.currentPath === tab.homeTab.fullPath,
icon: iconifyRender('ant-design:close-outlined') icon: iconifyRender('ant-design:close-outlined')
}, },
{ {

View File

@ -3,15 +3,15 @@
<component <component
:is="activeComponent" :is="activeComponent"
v-for="(item, index) in tab.tabs" v-for="(item, index) in tab.tabs"
:key="item.path" :key="item.fullPath"
:is-active="tab.activeTab === item.path" :is-active="tab.activeTab === item.fullPath"
:primary-color="theme.themeColor" :primary-color="theme.themeColor"
:closable="item.path !== tab.homeTab.path" :closable="item.name !== tab.homeTab.name"
:dark-mode="theme.darkMode" :dark-mode="theme.darkMode"
:class="{ '!mr-0': isChromeMode && index === tab.tabs.length - 1, 'mr-10px': !isChromeMode }" :class="{ '!mr-0': isChromeMode && index === tab.tabs.length - 1, 'mr-10px': !isChromeMode }"
@click="tab.handleClickTab(item.path)" @click="tab.handleClickTab(item.fullPath)"
@close="tab.removeTab(item.path)" @close="tab.removeTab(item.fullPath)"
@contextmenu="handleContextMenu($event, item.path)" @contextmenu="handleContextMenu($event, item.fullPath)"
> >
<Icon v-if="item.meta.icon" :icon="item.meta.icon" class="inline-block align-text-bottom mr-4px text-16px" /> <Icon v-if="item.meta.icon" :icon="item.meta.icon" class="inline-block align-text-bottom mr-4px text-16px" />
{{ item.meta.title }} {{ item.meta.title }}
@ -77,11 +77,11 @@ function setDropdown(x: number, y: number, currentPath: string) {
} }
/** 点击右键菜单 */ /** 点击右键菜单 */
async function handleContextMenu(e: MouseEvent, path: string) { async function handleContextMenu(e: MouseEvent, fullPath: string) {
e.preventDefault(); e.preventDefault();
const { clientX, clientY } = e; const { clientX, clientY } = e;
hideDropdown(); hideDropdown();
setDropdown(clientX, clientY, path); setDropdown(clientX, clientY, fullPath);
await nextTick(); await nextTick();
showDropdown(); showDropdown();
} }

View File

@ -45,10 +45,10 @@ function init() {
} }
watch( watch(
() => route.path, () => route.fullPath,
() => { () => {
tab.addTab(route); tab.addTab(route);
tab.setActiveTab(route.path); tab.setActiveTab(route.fullPath);
} }
); );

View File

@ -31,15 +31,10 @@ export async function createDynamicRouteGuard(
if (to.name === routeName('not-found-page')) { if (to.name === routeName('not-found-page')) {
// 动态路由没有加载导致被not-found-page路由捕获等待权限路由加载好了回到之前的路由 // 动态路由没有加载导致被not-found-page路由捕获等待权限路由加载好了回到之前的路由
// 若路由是从根路由重定向过来的,重新回到根路由 // 若路由是从根路由重定向过来的,重新回到根路由
const ROOT_ROUTE_NAME: AuthRoute.RouteKey = 'root'; const ROOT_ROUTE_NAME: AuthRoute.RouteKey = 'root';
if (to.redirectedFrom?.name === ROOT_ROUTE_NAME) { const path = to.redirectedFrom?.name === ROOT_ROUTE_NAME ? '/' : to.fullPath;
next({ path: '/', replace: true, query: to.query }); next({ path, replace: true, query: to.query, hash: to.hash });
return false;
}
next({ path: to.fullPath, replace: true, query: to.query });
return false; return false;
} }
} }

View File

@ -6,11 +6,14 @@ export const scrollBehavior: RouterScrollBehavior = (to, from) => {
const tab = useTabStore(); const tab = useTabStore();
if (to.hash) { if (to.hash) {
const el = document.querySelector(to.hash);
if (el) {
resolve({ resolve({
el: to.hash, el,
behavior: 'smooth' behavior: 'smooth'
}); });
} }
}
const { left, top } = tab.getTabScrollPosition(to.path); const { left, top } = tab.getTabScrollPosition(to.path);
const scrollPosition = { const scrollPosition = {

View File

@ -8,7 +8,7 @@ const about: AuthRoute.Route = {
singleLayout: 'basic', singleLayout: 'basic',
permissions: ['super', 'admin', 'user'], permissions: ['super', 'admin', 'user'],
icon: 'fluent:book-information-24-regular', icon: 'fluent:book-information-24-regular',
order: 8 order: 9
} }
}; };

View File

@ -37,7 +37,7 @@ const exception: AuthRoute.Route = {
meta: { meta: {
title: '异常页', title: '异常页',
icon: 'ant-design:exception-outlined', icon: 'ant-design:exception-outlined',
order: 6 order: 7
} }
}; };

View File

@ -0,0 +1,49 @@
const functionRoute: AuthRoute.Route = {
name: 'function',
path: '/function',
component: 'basic',
children: [
{
name: 'function_tab',
path: '/function/tab',
component: 'self',
meta: {
title: 'Tab',
requiresAuth: true,
icon: 'ic:round-tab'
}
},
{
name: 'function_tab-detail',
path: '/function/tab-detail',
component: 'self',
meta: {
title: 'Tab Detail',
requiresAuth: true,
hide: true,
activeMenu: 'function_tab',
icon: 'ic:round-tab'
}
},
{
name: 'function_tab-multi-detail',
path: '/function/tab-multi-detail',
component: 'self',
meta: {
title: 'Tab Multi Detail',
requiresAuth: true,
hide: true,
multiTab: true,
activeMenu: 'function_tab',
icon: 'ic:round-tab'
}
}
],
meta: {
title: '功能',
icon: 'ri:function-line',
order: 6
}
};
export default functionRoute;

View File

@ -49,7 +49,7 @@ const multiMenu: AuthRoute.Route = {
meta: { meta: {
title: '多级菜单', title: '多级菜单',
icon: 'carbon:menu', icon: 'carbon:menu',
order: 6 order: 8
} }
}; };

View File

@ -5,9 +5,11 @@ import type { RouteRecordNormalized, RouteLocationNormalizedLoaded } from 'vue-r
* @param route * @param route
*/ */
export function getTabRouteByVueRoute(route: RouteRecordNormalized | RouteLocationNormalizedLoaded) { export function getTabRouteByVueRoute(route: RouteRecordNormalized | RouteLocationNormalizedLoaded) {
const fullPath = hasFullPath(route) ? route.fullPath : route.path;
const tabRoute: GlobalTabRoute = { const tabRoute: GlobalTabRoute = {
name: route.name, name: route.name,
path: route.path, fullPath,
meta: route.meta, meta: route.meta,
scrollPosition: { scrollPosition: {
left: 0, left: 0,
@ -20,17 +22,36 @@ export function getTabRouteByVueRoute(route: RouteRecordNormalized | RouteLocati
/** /**
* *
* @param tabs - * @param tabs -
* @param path - * @param fullPath -
*/ */
export function getIndexInTabRoutes(tabs: GlobalTabRoute[], path: string) { export function getIndexInTabRoutes(tabs: GlobalTabRoute[], fullPath: string) {
return tabs.findIndex(tab => tab.path === path); return tabs.findIndex(tab => tab.fullPath === fullPath);
} }
/** /**
* *
* @param tabs - * @param tabs -
* @param path - * @param fullPath -
*/ */
export function isInTabRoutes(tabs: GlobalTabRoute[], path: string) { export function isInTabRoutes(tabs: GlobalTabRoute[], fullPath: string) {
return getIndexInTabRoutes(tabs, path) > -1; return getIndexInTabRoutes(tabs, fullPath) > -1;
}
/**
*
* @param tabs -
* @param routeName -
*/
export function getIndexInTabRoutesByRouteName(tabs: GlobalTabRoute[], routeName: string) {
return tabs.findIndex(tab => tab.name === routeName);
}
/**
* fullPath属性
* @param route
*/
function hasFullPath(
route: RouteRecordNormalized | RouteLocationNormalizedLoaded
): route is RouteLocationNormalizedLoaded {
return Boolean((route as RouteLocationNormalizedLoaded).fullPath);
} }

View File

@ -3,14 +3,14 @@ import { defineStore } from 'pinia';
import { useRouterPush } from '@/composables'; import { useRouterPush } from '@/composables';
import { getTabRoutes, clearTabRoutes } from '@/utils'; import { getTabRoutes, clearTabRoutes } from '@/utils';
import { useThemeStore } from '../theme'; import { useThemeStore } from '../theme';
import { getTabRouteByVueRoute, isInTabRoutes, getIndexInTabRoutes } from './helpers'; import { getTabRouteByVueRoute, isInTabRoutes, getIndexInTabRoutes, getIndexInTabRoutesByRouteName } from './helpers';
interface TabState { interface TabState {
/** 多页签数据 */ /** 多页签数据 */
tabs: GlobalTabRoute[]; tabs: GlobalTabRoute[];
/** 多页签首页 */ /** 多页签首页 */
homeTab: GlobalTabRoute; homeTab: GlobalTabRoute;
/** 当前激活状态的页签(路由path) */ /** 当前激活状态的页签(路由fullPath) */
activeTab: string; activeTab: string;
} }
@ -19,7 +19,7 @@ export const useTabStore = defineStore('tab-store', {
tabs: [], tabs: [],
homeTab: { homeTab: {
name: 'root', name: 'root',
path: '/', fullPath: '/',
meta: { meta: {
title: 'Root' title: 'Root'
}, },
@ -34,7 +34,7 @@ export const useTabStore = defineStore('tab-store', {
/** 当前激活状态的页签索引 */ /** 当前激活状态的页签索引 */
activeTabIndex(state) { activeTabIndex(state) {
const { tabs, activeTab } = state; const { tabs, activeTab } = state;
return tabs.findIndex(tab => tab.path === activeTab); return tabs.findIndex(tab => tab.fullPath === activeTab);
} }
}, },
actions: { actions: {
@ -45,10 +45,10 @@ export const useTabStore = defineStore('tab-store', {
}, },
/** /**
* *
* @param path - path * @param fullPath - fullPath
*/ */
setActiveTab(path: string) { setActiveTab(fullPath: string) {
this.activeTab = path; this.activeTab = fullPath;
}, },
/** /**
* *
@ -68,23 +68,39 @@ export const useTabStore = defineStore('tab-store', {
* @param route - * @param route -
*/ */
addTab(route: RouteLocationNormalizedLoaded) { addTab(route: RouteLocationNormalizedLoaded) {
if (!isInTabRoutes(this.tabs, route.path)) {
const tab = getTabRouteByVueRoute(route); const tab = getTabRouteByVueRoute(route);
this.tabs.push(tab);
if (isInTabRoutes(this.tabs, tab.fullPath)) {
return;
} }
const index = getIndexInTabRoutesByRouteName(this.tabs, route.name as string);
if (index === -1) {
this.tabs.push(tab);
return;
}
const { multiTab = false } = route.meta;
if (!multiTab) {
this.tabs.splice(index, 1, tab);
return;
}
this.tabs.push(tab);
}, },
/** /**
* *
* @param path - path * @param fullPath - fullPath
*/ */
removeTab(path: string) { removeTab(fullPath: string) {
const { routerPush } = useRouterPush(false); const { routerPush } = useRouterPush(false);
const isActive = this.activeTab === path; const isActive = this.activeTab === fullPath;
const updateTabs = this.tabs.filter(tab => tab.path !== path); const updateTabs = this.tabs.filter(tab => tab.fullPath !== fullPath);
this.tabs = updateTabs; this.tabs = updateTabs;
if (isActive && updateTabs.length) { if (isActive && updateTabs.length) {
const activePath = updateTabs[updateTabs.length - 1].path; const activePath = updateTabs[updateTabs.length - 1].fullPath;
this.setActiveTab(activePath); this.setActiveTab(activePath);
routerPush(activePath); routerPush(activePath);
} }
@ -96,73 +112,73 @@ export const useTabStore = defineStore('tab-store', {
clearTab(excludes: string[] = []) { clearTab(excludes: string[] = []) {
const { routerPush } = useRouterPush(false); const { routerPush } = useRouterPush(false);
const homePath = this.homeTab.path; const homePath = this.homeTab.fullPath;
const remain = [homePath, ...excludes]; const remain = [homePath, ...excludes];
const hasActive = remain.includes(this.activeTab); const hasActive = remain.includes(this.activeTab);
const updateTabs = this.tabs.filter(tab => remain.includes(tab.path)); const updateTabs = this.tabs.filter(tab => remain.includes(tab.fullPath));
this.tabs = updateTabs; this.tabs = updateTabs;
if (!hasActive && updateTabs.length) { if (!hasActive && updateTabs.length) {
const activePath = updateTabs[updateTabs.length - 1].path; const activePath = updateTabs[updateTabs.length - 1].fullPath;
this.setActiveTab(activePath); this.setActiveTab(activePath);
routerPush(activePath); routerPush(activePath);
} }
}, },
/** /**
* *
* @param path - path * @param fullPath - fullPath
*/ */
clearLeftTab(path: string) { clearLeftTab(fullPath: string) {
const index = getIndexInTabRoutes(this.tabs, path); const index = getIndexInTabRoutes(this.tabs, fullPath);
if (index > -1) { if (index > -1) {
const excludes = this.tabs.slice(index).map(item => item.path); const excludes = this.tabs.slice(index).map(item => item.fullPath);
this.clearTab(excludes); this.clearTab(excludes);
} }
}, },
/** /**
* *
* @param path - path * @param fullPath - fullPath
*/ */
clearRightTab(path: string) { clearRightTab(fullPath: string) {
const index = getIndexInTabRoutes(this.tabs, path); const index = getIndexInTabRoutes(this.tabs, fullPath);
if (index > -1) { if (index > -1) {
const excludes = this.tabs.slice(0, index + 1).map(item => item.path); const excludes = this.tabs.slice(0, index + 1).map(item => item.fullPath);
this.clearTab(excludes); this.clearTab(excludes);
} }
}, },
/** /**
* tab * tab
* @param path - path * @param fullPath - fullPath
*/ */
handleClickTab(path: string) { handleClickTab(fullPath: string) {
const { routerPush } = useRouterPush(false); const { routerPush } = useRouterPush(false);
const isActive = this.activeTab === path; const isActive = this.activeTab === fullPath;
if (!isActive) { if (!isActive) {
this.setActiveTab(path); this.setActiveTab(fullPath);
routerPush(path); routerPush(fullPath);
} }
}, },
/** /**
* tab滚动位置 * tab滚动位置
* @param path - path * @param fullPath - fullPath
* @param position - tab当前页的滚动位置 * @param position - tab当前页的滚动位置
*/ */
recordTabScrollPosition(path: string, position: { left: number; top: number }) { recordTabScrollPosition(fullPath: string, position: { left: number; top: number }) {
const index = getIndexInTabRoutes(this.tabs, path); const index = getIndexInTabRoutes(this.tabs, fullPath);
if (index > -1) { if (index > -1) {
this.tabs[index].scrollPosition = position; this.tabs[index].scrollPosition = position;
} }
}, },
/** /**
* tab滚动位置 * tab滚动位置
* @param path - path * @param fullPath - fullPath
*/ */
getTabScrollPosition(path: string) { getTabScrollPosition(fullPath: string) {
const position = { const position = {
left: 0, left: 0,
top: 0 top: 0
}; };
const index = getIndexInTabRoutes(this.tabs, path); const index = getIndexInTabRoutes(this.tabs, fullPath);
if (index > -1) { if (index > -1) {
Object.assign(position, this.tabs[index].scrollPosition); Object.assign(position, this.tabs[index].scrollPosition);
} }
@ -174,20 +190,27 @@ export const useTabStore = defineStore('tab-store', {
const tabs: GlobalTabRoute[] = theme.tab.isCache ? getTabRoutes() : []; const tabs: GlobalTabRoute[] = theme.tab.isCache ? getTabRoutes() : [];
const hasHome = isInTabRoutes(tabs, this.homeTab.path); const hasHome = getIndexInTabRoutesByRouteName(tabs, this.homeTab.name as string) > -1;
if (!hasHome && this.homeTab.name !== 'root') { if (!hasHome && this.homeTab.name !== 'root') {
tabs.unshift(this.homeTab); tabs.unshift(this.homeTab);
} }
const isHome = currentRoute.path === this.homeTab.path; const isHome = currentRoute.fullPath === this.homeTab.fullPath;
const hasCurrent = isInTabRoutes(tabs, currentRoute.path); const index = getIndexInTabRoutesByRouteName(tabs, currentRoute.name as string);
if (!isHome && !hasCurrent) { if (!isHome) {
const currentTab = getTabRouteByVueRoute(currentRoute); const currentTab = getTabRouteByVueRoute(currentRoute);
if (!currentRoute.meta.multiTab) {
tabs.splice(index, 1, currentTab);
} else {
const hasCurrent = isInTabRoutes(tabs, currentRoute.fullPath);
if (!hasCurrent) {
tabs.push(currentTab); tabs.push(currentTab);
} }
}
}
this.tabs = tabs; this.tabs = tabs;
this.setActiveTab(currentRoute.path); this.setActiveTab(currentRoute.fullPath);
} }
} }
}); });

View File

@ -44,6 +44,10 @@ declare namespace AuthRoute {
| 'auth-demo' | 'auth-demo'
| 'auth-demo_permission' | 'auth-demo_permission'
| 'auth-demo_super' | 'auth-demo_super'
| 'function'
| 'function_tab'
| 'function_tab-detail'
| 'function_tab-multi-detail'
| 'exception' | 'exception'
| 'exception_403' | 'exception_403'
| 'exception_404' | 'exception_404'
@ -94,12 +98,14 @@ declare namespace AuthRoute {
hide?: boolean; hide?: boolean;
/** 外链链接 */ /** 外链链接 */
href?: string; href?: string;
/** 是否支持多个tab页签(默认一个即相同name的路由会被替换) */
multiTab?: boolean;
/** 路由顺序,可用于菜单的排序 */ /** 路由顺序,可用于菜单的排序 */
order?: number; order?: number;
/** 表示是否是多级路由的中间级路由(用于转换路由数据时筛选多级路由的标识,定义路由时不用填写) */
multi?: boolean;
/** 当前路由需要选中的菜单项(用于跳转至不在左侧菜单显示的路由且需要高亮某个菜单的情况) */ /** 当前路由需要选中的菜单项(用于跳转至不在左侧菜单显示的路由且需要高亮某个菜单的情况) */
activeMenu?: RouteKey; activeMenu?: RouteKey;
/** 表示是否是多级路由的中间级路由(用于转换路由数据时筛选多级路由的标识,定义路由时不用填写) */
multi?: boolean;
}; };
/** 单个路由的类型结构(动态路由模式:后端返回此类型结构的路由) */ /** 单个路由的类型结构(动态路由模式:后端返回此类型结构的路由) */

View File

@ -276,7 +276,8 @@ type GlobalBreadcrumb = import('naive-ui').DropdownOption & {
}; };
/** 多页签Tab的路由 */ /** 多页签Tab的路由 */
interface GlobalTabRoute extends Pick<import('vue-router').RouteLocationNormalizedLoaded, 'name' | 'path' | 'meta'> { interface GlobalTabRoute
extends Pick<import('vue-router').RouteLocationNormalizedLoaded, 'name' | 'fullPath' | 'meta'> {
/** 滚动的位置 */ /** 滚动的位置 */
scrollPosition: { scrollPosition: {
left: number; left: number;

View File

@ -3,13 +3,13 @@ import { setLocal, getLocal } from '../storage';
/** 缓存多页签数据 */ /** 缓存多页签数据 */
export function setTabRoutes(data: GlobalTabRoute[]) { export function setTabRoutes(data: GlobalTabRoute[]) {
setLocal(EnumStorageKey['tab-routes'], data); setLocal(EnumStorageKey['multi-tab-routes'], data);
} }
/** 获取缓存的多页签数据 */ /** 获取缓存的多页签数据 */
export function getTabRoutes() { export function getTabRoutes() {
const routes: GlobalTabRoute[] = []; const routes: GlobalTabRoute[] = [];
const data = getLocal<GlobalTabRoute[]>(EnumStorageKey['tab-routes']); const data = getLocal<GlobalTabRoute[]>(EnumStorageKey['multi-tab-routes']);
if (data) { if (data) {
const defaultTabRoutes = data.map(item => ({ const defaultTabRoutes = data.map(item => ({
...item, ...item,

View File

@ -0,0 +1,27 @@
<template>
<n-space :vertical="true" :size="16">
<n-card title="Tab Detail" :bordered="false" size="small" class="shadow-sm rounded-16px">
<n-space :vertical="true" :size="12">
<div>当前路由的描述数据(meta)</div>
<div>{{ route.meta }}</div>
<n-button @click="handleToTab">返回Tab</n-button>
</n-space>
</n-card>
</n-space>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { routeName } from '@/router';
import { useRouterPush } from '@/composables';
const { routerPush } = useRouterPush();
const route = useRoute();
function handleToTab() {
routerPush({ name: routeName('function_tab') });
}
</script>
<style scoped></style>

View File

@ -0,0 +1,28 @@
<template>
<n-space :vertical="true" :size="16">
<n-card title="Tab Detail" :bordered="false" size="small" class="shadow-sm rounded-16px">
<n-space :vertical="true" :size="12">
<div>当前路由的描述数据(meta)</div>
<div>{{ route.meta }}</div>
<div>当前路由的查询数据(query)</div>
<div>{{ route.query }}</div>
<n-button @click="handleToTab">返回Tab</n-button>
</n-space>
</n-card>
</n-space>
</template>
<script setup lang="ts">
import { useRoute } from 'vue-router';
import { routeName } from '@/router';
import { useRouterPush } from '@/composables';
const route = useRoute();
const { routerPush } = useRouterPush();
function handleToTab() {
routerPush({ name: routeName('function_tab') });
}
</script>
<style scoped></style>

View File

@ -0,0 +1,28 @@
<template>
<n-space :vertical="true" :size="16">
<n-card title="Tab Home" :bordered="false" size="small" class="shadow-sm rounded-16px">
<n-space :vertical="true" :size="12">
<n-button @click="handleToTabDetail">跳转Tab Detail</n-button>
<n-button @click="handleToTabMultiDetail(1)">跳转Tab Multi Detail 1</n-button>
<n-button @click="handleToTabMultiDetail(2)">跳转Tab Multi Detail 2</n-button>
</n-space>
</n-card>
</n-space>
</template>
<script setup lang="ts">
import { routeName } from '@/router';
import { useRouterPush } from '@/composables';
const { routerPush } = useRouterPush();
function handleToTabDetail() {
routerPush({ name: routeName('function_tab-detail'), query: { name: 'abc' }, hash: '#DEMO_HASH' });
}
function handleToTabMultiDetail(num: number) {
routerPush({ name: routeName('function_tab-multi-detail'), query: { name: 'abc', num }, hash: '#DEMO_HASH' });
}
</script>
<style scoped></style>