feat(menu): base-layout-sider.vue
This commit is contained in:
@@ -57,7 +57,7 @@
|
||||
"utils4u": "^4.2.3",
|
||||
"vue": "^3.5.21",
|
||||
"vue-i18n": "^11.1.12",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/vite-plugin": "^1.13.2",
|
||||
|
||||
30
pnpm-lock.yaml
generated
30
pnpm-lock.yaml
generated
@@ -52,7 +52,7 @@ importers:
|
||||
version: 4.1.0
|
||||
utils4u:
|
||||
specifier: ^4.2.3
|
||||
version: 4.2.3(@vueuse/core@13.9.0(vue@3.5.22(typescript@5.9.2)))(nprogress@0.2.0)(primevue@4.4.0(vue@3.5.22(typescript@5.9.2)))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2))
|
||||
version: 4.2.3(@vueuse/core@13.9.0(vue@3.5.22(typescript@5.9.2)))(nprogress@0.2.0)(primevue@4.4.0(vue@3.5.22(typescript@5.9.2)))(vue-router@4.6.3(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2))
|
||||
vue:
|
||||
specifier: ^3.5.21
|
||||
version: 3.5.22(typescript@5.9.2)
|
||||
@@ -60,8 +60,8 @@ importers:
|
||||
specifier: ^11.1.12
|
||||
version: 11.1.12(vue@3.5.22(typescript@5.9.2))
|
||||
vue-router:
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(vue@3.5.22(typescript@5.9.2))
|
||||
specifier: ^4.6.3
|
||||
version: 4.6.3(vue@3.5.22(typescript@5.9.2))
|
||||
devDependencies:
|
||||
'@cloudflare/vite-plugin':
|
||||
specifier: ^1.13.2
|
||||
@@ -218,7 +218,7 @@ importers:
|
||||
version: 29.2.0(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))
|
||||
unplugin-vue-router:
|
||||
specifier: ^0.16.0
|
||||
version: 0.16.0(@vue/compiler-sfc@3.5.22)(typescript@5.9.2)(vue-router@4.5.1(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2))
|
||||
version: 0.16.0(@vue/compiler-sfc@3.5.22)(typescript@5.9.2)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2))
|
||||
vite:
|
||||
specifier: ^7.1.5
|
||||
version: 7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
|
||||
@@ -236,7 +236,7 @@ importers:
|
||||
version: 8.0.3(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.2))
|
||||
vite-plugin-vue-meta-layouts:
|
||||
specifier: ^0.6.1
|
||||
version: 0.6.1(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.2)))
|
||||
version: 0.6.1(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.22(typescript@5.9.2)))
|
||||
vite-plugin-webfont-dl:
|
||||
specifier: ^3.11.1
|
||||
version: 3.11.1(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))
|
||||
@@ -5243,10 +5243,10 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^2.7.0 || ^3.2.25
|
||||
|
||||
vue-router@4.5.1:
|
||||
resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==}
|
||||
vue-router@4.6.3:
|
||||
resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
vue: ^3.5.0
|
||||
|
||||
vue-tsc@3.1.0:
|
||||
resolution: {integrity: sha512-fbMynMG7kXSnqZTRBSCh9ROYaVpXfCZbEO0gY3lqOjLbp361uuS88n6BDajiUriDIF+SGLWoinjvf6stS2J3Gg==}
|
||||
@@ -10149,7 +10149,7 @@ snapshots:
|
||||
unplugin-utils: 0.3.1
|
||||
vite: 7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
|
||||
|
||||
unplugin-vue-router@0.16.0(@vue/compiler-sfc@3.5.22)(typescript@5.9.2)(vue-router@4.5.1(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2)):
|
||||
unplugin-vue-router@0.16.0(@vue/compiler-sfc@3.5.22)(typescript@5.9.2)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2)):
|
||||
dependencies:
|
||||
'@babel/generator': 7.28.3
|
||||
'@vue-macros/common': 3.1.1(vue@3.5.22(typescript@5.9.2))
|
||||
@@ -10170,7 +10170,7 @@ snapshots:
|
||||
unplugin-utils: 0.3.1
|
||||
yaml: 2.8.1
|
||||
optionalDependencies:
|
||||
vue-router: 4.5.1(vue@3.5.22(typescript@5.9.2))
|
||||
vue-router: 4.6.3(vue@3.5.22(typescript@5.9.2))
|
||||
transitivePeerDependencies:
|
||||
- typescript
|
||||
- vue
|
||||
@@ -10194,13 +10194,13 @@ snapshots:
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
utils4u@4.2.3(@vueuse/core@13.9.0(vue@3.5.22(typescript@5.9.2)))(nprogress@0.2.0)(primevue@4.4.0(vue@3.5.22(typescript@5.9.2)))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2)):
|
||||
utils4u@4.2.3(@vueuse/core@13.9.0(vue@3.5.22(typescript@5.9.2)))(nprogress@0.2.0)(primevue@4.4.0(vue@3.5.22(typescript@5.9.2)))(vue-router@4.6.3(vue@3.5.22(typescript@5.9.2)))(vue@3.5.22(typescript@5.9.2)):
|
||||
optionalDependencies:
|
||||
'@vueuse/core': 13.9.0(vue@3.5.22(typescript@5.9.2))
|
||||
nprogress: 0.2.0
|
||||
primevue: 4.4.0(vue@3.5.22(typescript@5.9.2))
|
||||
vue: 3.5.22(typescript@5.9.2)
|
||||
vue-router: 4.5.1(vue@3.5.22(typescript@5.9.2))
|
||||
vue-router: 4.6.3(vue@3.5.22(typescript@5.9.2))
|
||||
|
||||
varint@6.0.0: {}
|
||||
|
||||
@@ -10318,11 +10318,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vite-plugin-vue-meta-layouts@0.6.1(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.2))):
|
||||
vite-plugin-vue-meta-layouts@0.6.1(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.6.3(vue@3.5.22(typescript@5.9.2))):
|
||||
dependencies:
|
||||
local-pkg: 0.5.1
|
||||
vite: 7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
|
||||
vue-router: 4.5.1(vue@3.5.22(typescript@5.9.2))
|
||||
vue-router: 4.6.3(vue@3.5.22(typescript@5.9.2))
|
||||
|
||||
vite-plugin-webfont-dl@3.11.1(vite@7.1.10(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)):
|
||||
dependencies:
|
||||
@@ -10475,7 +10475,7 @@ snapshots:
|
||||
- vue-tsc
|
||||
- webpack
|
||||
|
||||
vue-router@4.5.1(vue@3.5.22(typescript@5.9.2)):
|
||||
vue-router@4.6.3(vue@3.5.22(typescript@5.9.2)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.22(typescript@5.9.2)
|
||||
|
||||
106
src/layouts/base-layout/base-layout-sider.vue
Normal file
106
src/layouts/base-layout/base-layout-sider.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<script setup lang="ts">
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import type { RouteRecordNormalized } from 'vue-router';
|
||||
import { createGetRoutes, router } from '@/plugins/router-plugin';
|
||||
|
||||
// 路由转换为菜单树的辅助函数
|
||||
function convertRoutesToMenuOptions(routes: RouteRecordNormalized[]): MenuOption[] {
|
||||
const menuMap = new Map<string, MenuOption>();
|
||||
const rootMenus: MenuOption[] = [];
|
||||
|
||||
// 过滤和排序路由
|
||||
const validRoutes = routes
|
||||
.filter((route) => {
|
||||
// 过滤掉不需要显示的路由
|
||||
if (!route.name || route.meta?.hidden === true || route.meta?.layout === false) {
|
||||
return false;
|
||||
}
|
||||
// 过滤掉根路径和通配符路径
|
||||
if (route.path === '/' || route.path.includes('*')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sort((a, b) => a.path.localeCompare(b.path));
|
||||
|
||||
// 构建菜单树
|
||||
for (const route of validRoutes) {
|
||||
const pathSegments = route.path.split('/').filter(Boolean);
|
||||
const menuOption: MenuOption = {
|
||||
label: route.meta?.title || (route.name as string),
|
||||
key: route.path,
|
||||
};
|
||||
|
||||
// 如果只有一级路径,直接添加到根菜单
|
||||
if (pathSegments.length === 1) {
|
||||
rootMenus.push(menuOption);
|
||||
menuMap.set(route.path, menuOption);
|
||||
} else {
|
||||
// 多级路径,需要创建或找到父菜单
|
||||
let currentPath = '';
|
||||
for (let i = 0; i < pathSegments.length - 1; i++) {
|
||||
currentPath += `/${pathSegments[i]}`;
|
||||
|
||||
if (!menuMap.has(currentPath)) {
|
||||
// 创建父菜单节点
|
||||
const parentMenu: MenuOption = {
|
||||
label: pathSegments[i],
|
||||
key: currentPath,
|
||||
children: [],
|
||||
};
|
||||
|
||||
if (i === 0) {
|
||||
// 顶级父节点
|
||||
rootMenus.push(parentMenu);
|
||||
} else {
|
||||
// 找到祖父节点并添加
|
||||
const grandParentPath = currentPath.substring(0, currentPath.lastIndexOf('/'));
|
||||
const grandParent = menuMap.get(grandParentPath);
|
||||
if (grandParent) {
|
||||
if (!grandParent.children) {
|
||||
grandParent.children = [];
|
||||
}
|
||||
grandParent.children.push(parentMenu);
|
||||
}
|
||||
}
|
||||
|
||||
menuMap.set(currentPath, parentMenu);
|
||||
}
|
||||
}
|
||||
|
||||
// 将当前菜单项添加到父菜单
|
||||
const parentPath = currentPath;
|
||||
const parent = menuMap.get(parentPath);
|
||||
if (parent) {
|
||||
if (!parent.children) {
|
||||
parent.children = [];
|
||||
}
|
||||
parent.children.push(menuOption);
|
||||
}
|
||||
|
||||
menuMap.set(route.path, menuOption);
|
||||
}
|
||||
}
|
||||
|
||||
return rootMenus;
|
||||
}
|
||||
|
||||
const routes = createGetRoutes(router)();
|
||||
const menuOptions = computed(() => convertRoutesToMenuOptions(routes));
|
||||
|
||||
console.debug('原始路由:', JSON.stringify(routes, null, 2));
|
||||
console.debug('转换后的菜单:', JSON.stringify(menuOptions.value, null, 2));
|
||||
|
||||
// 处理菜单点击,导航到对应路由
|
||||
const handleMenuUpdate = (key: string) => {
|
||||
// 只有当 key 对应一个实际路由时才导航
|
||||
const route = routes.find((r) => r.path === key);
|
||||
if (route && !route.children?.length) {
|
||||
router.push(key);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NMenu :options="menuOptions" @update:value="handleMenuUpdate" />
|
||||
</template>
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { AdminLayout } from '@sa/materials';
|
||||
import BaseLayoutHeader from './base-layout-header.vue';
|
||||
import BaseLayoutSider from './base-layout-sider.vue';
|
||||
|
||||
const siderCollapse = ref(false);
|
||||
const appStore = useAppStore();
|
||||
@@ -18,9 +19,9 @@ const appStore = useAppStore();
|
||||
</template>
|
||||
<template #sider>
|
||||
<div
|
||||
class="bg-purple-100/28 dark:bg-purple-900/28 text-purple-900 dark:text-purple-100 p-4 h-full overflow-hidden"
|
||||
class="bg-purple-100/28 dark:bg-purple-900/28 text-purple-900 dark:text-purple-100 h-full overflow-hidden"
|
||||
>
|
||||
3#GlobalSider
|
||||
<BaseLayoutSider />
|
||||
</div>
|
||||
</template>
|
||||
<div class="bg-yellow-100/28 dark:bg-yellow-900/28 text-yellow-900 dark:text-yellow-100 p-4">
|
||||
|
||||
12
src/pages/Home.page.vue
Normal file
12
src/pages/Home.page.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
definePage({
|
||||
meta: {
|
||||
title: '首页',
|
||||
hidden: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>Home Page</div>
|
||||
</template>
|
||||
5
src/pages/page/demo-api.page.vue
Normal file
5
src/pages/page/demo-api.page.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div>Demo-API-Page</div>
|
||||
</template>
|
||||
5
src/pages/page/demo-icons.page.vue
Normal file
5
src/pages/page/demo-icons.page.vue
Normal file
@@ -0,0 +1,5 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div>Demo-Icons-Page</div>
|
||||
</template>
|
||||
76
typed-router.d.ts
vendored
76
typed-router.d.ts
vendored
@@ -1,10 +1,15 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection ES6UnusedImports
|
||||
// Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️
|
||||
// It's recommended to commit this file.
|
||||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
|
||||
|
||||
declare module 'vue-router/auto-resolver' {
|
||||
export type ParamParserCustom = never
|
||||
}
|
||||
|
||||
declare module 'vue-router/auto-routes' {
|
||||
import type {
|
||||
RouteRecordInfo,
|
||||
@@ -18,13 +23,46 @@ declare module 'vue-router/auto-routes' {
|
||||
* Route name map generated by unplugin-vue-router
|
||||
*/
|
||||
export interface RouteNamedMap {
|
||||
'Root': RouteRecordInfo<'Root', '/', Record<never, never>, Record<never, never>>,
|
||||
'$Path': RouteRecordInfo<'$Path', '/:path(.*)', { path: ParamValue<true> }, { path: ParamValue<false> }>,
|
||||
'Root': RouteRecordInfo<
|
||||
'Root',
|
||||
'/',
|
||||
Record<never, never>,
|
||||
Record<never, never>,
|
||||
| never
|
||||
>,
|
||||
'$Path': RouteRecordInfo<
|
||||
'$Path',
|
||||
'/:path(.*)',
|
||||
{ path: ParamValue<true> },
|
||||
{ path: ParamValue<false> },
|
||||
| never
|
||||
>,
|
||||
'Home': RouteRecordInfo<
|
||||
'Home',
|
||||
'/Home',
|
||||
Record<never, never>,
|
||||
Record<never, never>,
|
||||
| never
|
||||
>,
|
||||
'PageDemoApi': RouteRecordInfo<
|
||||
'PageDemoApi',
|
||||
'/page/demo-api',
|
||||
Record<never, never>,
|
||||
Record<never, never>,
|
||||
| never
|
||||
>,
|
||||
'PageDemoIcons': RouteRecordInfo<
|
||||
'PageDemoIcons',
|
||||
'/page/demo-icons',
|
||||
Record<never, never>,
|
||||
Record<never, never>,
|
||||
| never
|
||||
>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Route file to route info map by unplugin-vue-router.
|
||||
* Used by the volar plugin to automatically type useRoute()
|
||||
* Used by the \`sfc-typed-router\` Volar plugin to automatically type \`useRoute()\`.
|
||||
*
|
||||
* Each key is a file path relative to the project root with 2 properties:
|
||||
* - routes: union of route names of the possible routes when in this page (passed to useRoute<...>())
|
||||
@@ -34,18 +72,40 @@ declare module 'vue-router/auto-routes' {
|
||||
*/
|
||||
export interface _RouteFileInfoMap {
|
||||
'src/pages/index.page.vue': {
|
||||
routes: 'Root'
|
||||
views: never
|
||||
routes:
|
||||
| 'Root'
|
||||
views:
|
||||
| never
|
||||
}
|
||||
'src/pages/[...path].page.vue': {
|
||||
routes: '$Path'
|
||||
views: never
|
||||
routes:
|
||||
| '$Path'
|
||||
views:
|
||||
| never
|
||||
}
|
||||
'src/pages/Home.page.vue': {
|
||||
routes:
|
||||
| 'Home'
|
||||
views:
|
||||
| never
|
||||
}
|
||||
'src/pages/page/demo-api.page.vue': {
|
||||
routes:
|
||||
| 'PageDemoApi'
|
||||
views:
|
||||
| never
|
||||
}
|
||||
'src/pages/page/demo-icons.page.vue': {
|
||||
routes:
|
||||
| 'PageDemoIcons'
|
||||
views:
|
||||
| never
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a union of possible route names in a certain route component file.
|
||||
* Used by the volar plugin to automatically type useRoute()
|
||||
* Used by the \`sfc-typed-router\` Volar plugin to automatically type \`useRoute()\`.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
7
worker-configuration.d.ts
vendored
7
worker-configuration.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable */
|
||||
// Generated by Wrangler by running `wrangler types` (hash: c8d566f9236103c3d936718f23f1bb71)
|
||||
// Generated by Wrangler by running `wrangler types` (hash: 7094267439eea3789640d49ba1e25377)
|
||||
// Runtime types generated with workerd@1.20251008.0 2025-09-09
|
||||
declare namespace Cloudflare {
|
||||
interface GlobalProps {
|
||||
@@ -7,11 +7,6 @@ declare namespace Cloudflare {
|
||||
}
|
||||
interface Env {
|
||||
KV: KVNamespace;
|
||||
VITE_APP_TITLE: string;
|
||||
VITE_APP_BASE: string;
|
||||
VITE_APP_BUILD_SOURCE_MAP: string;
|
||||
VITE_APP_BUILD_COMMIT: string;
|
||||
VITE_APP_BUILD_TIME: string;
|
||||
}
|
||||
}
|
||||
interface Env extends Cloudflare.Env {}
|
||||
|
||||
Reference in New Issue
Block a user