🗂️
- Naive UI 2.x
- Vue 3.5.x
- Vite 5.x + Vitest
- Pinia 2.x
- TypeScript 5.x
- ESLint 9.x + Stylistic
- Husky + lint-staged
- Lodash
- VueUse
- Unplugin + UnoCSS
- Iconify + Unplugin-Icons
-
支持 按照一 级 ID 进行划分的 缓存空 间集合 ,本 示 例 的 一 级ID为 projectId,可 加 以自 行 改造 和 将 其耦合 到 组件内 -
支持 命名 空 间的 Tab集合 -
支持 Caches 缓存,用 于 Keep Alive -
支持 缓存空 间的切 换及内部 Tab的 添加 /关闭 -
支持 一键关闭其他标签页 -
支持 多 层级动态路 由 参 数 页面的 缓存(如/xxxx/:id1
,/xxxx/:id1/xxx/:id2
...) -
支持 关闭 Tab之 前 的 Hook 钩子,用 于手动处理 阻止 关闭 Tab的 逻辑 -
支持 同 时打开多个 Tab -
支持 Tab打 开时自 定 义命名 -
支持 Pinia 统一数据管理和本地 Session存 储 (可 自由 改造 为 LocalStorage)
- Vue 3.5.x
- Node >= 18.12.x
- Pnpm 9.x
- VS Code 插件
dbaeumer.vscode-eslint
>= v3.0.5 (pre-release)
安 装 依 赖
pnpm i
本地 开发
pnpm dev
打 开单个 Tab
const router = useTabRouter()
router.push({
name: 'Xxxxxx',
params: {
datasetId: row.xxxId
}
}, `自 定 义名称 -${row.xxxId}`)
同 时打开多个 Tab
const router = useTabRouter()
router.pushMultiple(
// 路 由 一 级动态ID
'xxxxprojectId',
[
{
to: {
name: 'Xxxx1',
params: {
datasetId: row.id
},
query: {
query1: '123456'
}
},
tabName: '自 定 义名称 1'
},
{
to: {
name: 'Xxxx2',
},
tabName: '自 定 义名称 2'
},
]
)
// 路 由 一 级动态ID
const prefixKey = route.params.projectId // 'xxxxprojectId'
router.pushMultiple(
prefixKey,
[
`/group-project/${prefixKey}/dashboard/monitor`,
`/group-project/${prefixKey}/work-platform`,
`/group-project/${prefixKey}/work-platform/nested-level/level-1-2`,
]
)
路 由 Layout 布 局 配置
Tab 组件TabContent
TabsController
为确meta
// src/router/frontend/test-routes.ts
export const testRoutesExample = {
path: 'example-component',
name: 'ExampleComponentRoot',
component: LayoutWork, // 配置 Tab 路 由布 局
redirect: {
name: 'ExampleComponentBasic'
},
children: [
{
path: 'basic',
name: 'ExampleComponentBasic',
meta: {
title: '组件示 例 -基 础组件 ',
cacheSpaceKey: CacheSpaceKeys.exampleComponent // 配置 缓存空 间 Key
},
component: () => import('@/modules/ExampleComponent/pages/basic.vue')
},
{
path: 'form',
name: 'ExampleComponentForm',
meta: {
title: '组件示 例 -表 单',
cacheSpaceKey: CacheSpaceKeys.exampleComponent // 配置 缓存空 间 Key
},
component: () => import('@/modules/ExampleComponent/pages/form.vue')
},
// ...
]
}
这样也就WorkTab
)CacheSpace
缓存空 间与 Tab 的 缓存
为尽route.fullPath
为了满足useTabRouter
Hook useRouter
push
replace
useTabRouter
其核
- 单个缓存
空 间
export interface CacheSpace {
cacheSpaceKey: string
tabs: Array<WorkTab>
activeTabKey: string | null
}
- 缓存
空 间集合
Map<cacheSpaceKey, CacheSpace>
- 单个 Tab 页签
数 据
export interface WorkTab {
// 取 自 meta.title 的 tab 标题名称
label: string
// 自 定 义 tab 标题名称
customLabel: string
tabKey: string
link: string
routeName: RouteRecordName
}
- Vue 组件
名称 需要 与 对应路 由 名称 保持 一致 ,否 则 Keep Alive将 会 失效 由 于 Tab 组件自身 解 耦了所有 的 业务逻辑,所以 涉 及到路 由 一 级动态 ID 这种跟业务路由 强 耦合的 地方 都 需要 再 自 行 传入,嫌 麻 烦的可 以直接 修 改 源 码中的 所有 dynamicCacheSpacePrefixKey
字 段 或 将 相 关 hook二 次 封 装 一 下 。具体 参考 这两个位置 :源 码1和 源 码2若 Husky未生 效 ,可能 是 由 于未完成 初 始 化 ,执行pnpm run prepare
进行初 始 化 再 尝试- 推荐
使用 本 项目进行二次开发和定制实际的业务项目
-
如果此开
源 对您有 帮助,您可以点右 上角 "Star"支持 一 下 谢谢! ^_^ 🌹 -
或 者 您可以 "follow"一 下 ,我 会 不断 开源更 多 有 趣 和 实用的 项目 -
开发环境 MacOS Ventura, VSCode
-
如有问题请
直接 在 Issues中 提 ,或 者 您发现问题并有 非常 好 的 解 决方案 , 欢迎 PR 👍 -
推荐
一 个 Vite5 + Vue3 + TS + Element Plus 开源入 门项目 , 对 Element Plus UI 库感兴趣的 朋友 可 以去看 看 。地 址 在 这里 -
另外
一 个 Vite5 + Vue3 + Naive UI + TS的 入 门项目 ,比 当 前 项目简洁很多,非常 适合入 门练习和二 次 开发。地 址 在 这里