Compare commits
3 Commits
0e051f321c
...
a003f3e277
Author | SHA1 | Date | |
---|---|---|---|
a003f3e277 | |||
5487dc321e | |||
ec4906f441 |
@ -48,7 +48,7 @@
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@intlify/unplugin-vue-i18n": "^6.0.8",
|
||||
"@pinia/colada": "^0.17.1",
|
||||
"@primeuix/themes": "^1.1.2",
|
||||
"@primeuix/themes": "^1.2.1",
|
||||
"@splinetool/runtime": "^1.10.19",
|
||||
"@types/p5": "^1.7.6",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
@ -75,7 +75,7 @@
|
||||
"plotly.js-dist-min": "^3.0.1",
|
||||
"primeicons": "^7.0.0",
|
||||
"primelocale": "^2.1.4",
|
||||
"primevue": "^4.3.5",
|
||||
"primevue": "^4.3.6",
|
||||
"radash": "^12.1.1",
|
||||
"radix-vue": "^1.9.17",
|
||||
"reka-ui": "^2.3.1",
|
||||
@ -104,7 +104,7 @@
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@iconify/utils": "^2.3.0",
|
||||
"@playwright/test": "^1.53.2",
|
||||
"@primevue/auto-import-resolver": "^4.3.5",
|
||||
"@primevue/auto-import-resolver": "^4.3.6",
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/mockjs": "^1.0.10",
|
||||
|
100
pnpm-lock.yaml
generated
100
pnpm-lock.yaml
generated
@ -7,7 +7,7 @@ settings:
|
||||
overrides:
|
||||
vite: ^7.0.0
|
||||
vue-tsc: ^3.0.0
|
||||
'@primevue/auto-import-resolver': ^4.3.5
|
||||
'@primevue/auto-import-resolver': ^4.3.6
|
||||
|
||||
importers:
|
||||
|
||||
@ -26,8 +26,8 @@ importers:
|
||||
specifier: ^0.17.1
|
||||
version: 0.17.1(pinia@3.0.3(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)))
|
||||
'@primeuix/themes':
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
'@splinetool/runtime':
|
||||
specifier: ^1.10.19
|
||||
version: 1.10.19
|
||||
@ -107,8 +107,8 @@ importers:
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4
|
||||
primevue:
|
||||
specifier: ^4.3.5
|
||||
version: 4.3.5(vue@3.5.17(typescript@5.8.3))
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6(vue@3.5.17(typescript@5.8.3))
|
||||
radash:
|
||||
specifier: ^12.1.1
|
||||
version: 12.1.1
|
||||
@ -138,7 +138,7 @@ importers:
|
||||
version: 4.1.0
|
||||
utils4u:
|
||||
specifier: ^4.2.3
|
||||
version: 4.2.3(@vueuse/core@13.4.0(vue@3.5.17(typescript@5.8.3)))(dayjs@1.11.13)(nprogress@0.2.0)(primevue@4.3.5(vue@3.5.17(typescript@5.8.3)))(vant@4.9.20(vue@3.5.17(typescript@5.8.3)))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
|
||||
version: 4.2.3(@vueuse/core@13.4.0(vue@3.5.17(typescript@5.8.3)))(dayjs@1.11.13)(nprogress@0.2.0)(primevue@4.3.6(vue@3.5.17(typescript@5.8.3)))(vant@4.9.20(vue@3.5.17(typescript@5.8.3)))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
|
||||
vant:
|
||||
specifier: ^4.9.20
|
||||
version: 4.9.20(vue@3.5.17(typescript@5.8.3))
|
||||
@ -189,8 +189,8 @@ importers:
|
||||
specifier: ^1.53.2
|
||||
version: 1.53.2
|
||||
'@primevue/auto-import-resolver':
|
||||
specifier: ^4.3.5
|
||||
version: 4.3.5
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6
|
||||
'@tsconfig/node22':
|
||||
specifier: ^22.0.2
|
||||
version: 22.0.2
|
||||
@ -1440,36 +1440,36 @@ packages:
|
||||
'@polka/url@1.0.0-next.29':
|
||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||
|
||||
'@primeuix/styled@0.6.4':
|
||||
resolution: {integrity: sha512-7ePLwqazLV0x269YlPMeE4wtQKT0NScY2/gEin0/96krTiGiElmlzKMMbH69bVApm/sfen5DZGuCEEwPiBJJ5g==}
|
||||
'@primeuix/styled@0.7.0':
|
||||
resolution: {integrity: sha512-xUqMdQb75izeDkNWFK1QlU15aUl5LIU97Fq68IXOhrqqLsKEBnj5ftntFZrENQW70jAHwALdWP4EOGi/poc9Tg==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primeuix/styles@1.1.1':
|
||||
resolution: {integrity: sha512-oguFY2Rs4ZqdcFqEKxBt2d8trTkAjJ5BGTaDV0zbwdqRCRcZp9+di3K3Wv57CW/+tFDA0z1Dg7Dpm7wmkOII9Q==}
|
||||
'@primeuix/styles@1.2.1':
|
||||
resolution: {integrity: sha512-Tri7pPgZgxrVmhJG8ijZZFolQ6vu27xnkGoAB9EFY8YlaKTM5iqkWzEcqdxy2KmgFWMXi+BrPHwO0RdQ6JCT+g==}
|
||||
|
||||
'@primeuix/themes@1.1.2':
|
||||
resolution: {integrity: sha512-yxnmMqMct6RYWX6m9gS9AIG8QArjnROQzBqQf8UDQggAedX3My1fxr9FKEpvZMoY39WD494kG0pU9zIa1k2XyA==}
|
||||
'@primeuix/themes@1.2.1':
|
||||
resolution: {integrity: sha512-DVCFDncvag47tpag3TdufDTuvUbfKnkgzmlxAQkwuMS0IlPA3wChrf9VlL2wETg/ZpJm/tHobkJBnB9FmkiqnA==}
|
||||
|
||||
'@primeuix/utils@0.5.4':
|
||||
resolution: {integrity: sha512-8LggV3Jz59pymHQD10e/u63z/GemQ22RBeu2Gb1eJgBYVwn1iOb82LR+daeAc/LxrXCC5pHnftnCmnZO6vInLA==}
|
||||
'@primeuix/utils@0.6.0':
|
||||
resolution: {integrity: sha512-ULpB87ImNAiX36OMtyDeRceWB7N/mVlh6gGLqp/lx8UMKZlLIQH/UAFND86hYXHwNpXeNKcWfMGreb0Oc0hcZA==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primevue/auto-import-resolver@4.3.5':
|
||||
resolution: {integrity: sha512-cxBfNcgjyYGiutrAp+4qqRoK3XWyRtGl4HMeHKf4Twnv0JZASqaC0TOlfbQeymWVIA4VzBDnwoNk/eYLz1ohfw==}
|
||||
'@primevue/auto-import-resolver@4.3.6':
|
||||
resolution: {integrity: sha512-xrncC+mnqpdyv8oePpFGGPYyhOZHV3rRlVMAzif0l9rlyJ4r/gzRI7PRLxIPmC83UqGz+PG50LvNK/5L3VEagQ==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primevue/core@4.3.5':
|
||||
resolution: {integrity: sha512-YBlSr/EbXsnsTOyfgqmbrJQ7AI5EThaeGZvfDFjPIIEpokEK+Q32++9xPn3MH8rcM8zPsfMeBOWi4/OJkOqG4w==}
|
||||
'@primevue/core@4.3.6':
|
||||
resolution: {integrity: sha512-ZuP0gqpEbIkpz9Em/O4Du+fRj0qyOl2YYuxhlELAtRg8+YkMsSJDd2ai2GM623sYRWOIwMr5rWevZGB1WqukzQ==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
peerDependencies:
|
||||
vue: ^3.5.0
|
||||
|
||||
'@primevue/icons@4.3.5':
|
||||
resolution: {integrity: sha512-+V8XG6MEvczw3Ufz7+ABSSCaVdFCYKRHvVDmXpS65AUeQTDEqmJz3xx2UiYYdASA6Gb2yIKdVztTcRjHFtiAnw==}
|
||||
'@primevue/icons@4.3.6':
|
||||
resolution: {integrity: sha512-QVFmfikMpo4/DObMSbB5kS8MH1OoQrx8N9prEZaMvfFzD3hixxK24l2VrcS5x5/0NnP3szwZwTCmEAutCygX6A==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@primevue/metadata@4.3.5':
|
||||
resolution: {integrity: sha512-R1urDTqWyjRZyVF+8WFYc/N6fRuPdOMDtA+zTfTeMRW/BzDvakbpJKZDGI6DcOz4NR/MZFEWi8hFyT87aFSKow==}
|
||||
'@primevue/metadata@4.3.6':
|
||||
resolution: {integrity: sha512-dncVsMYxFvrx7FYDH8MQMtd6xkNQMVuCwpnTMLIOf9BRC7zVhRQug/IWZzsbS3jgVrPsTI3KmOn4LMDhH+fapg==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
'@protobufjs/aspromise@1.1.2':
|
||||
@ -4398,8 +4398,8 @@ packages:
|
||||
resolution: {integrity: sha512-4nzWKVX4d7qiaZEVebQd8+/EFArUt1TMYpLbNCTbOWF2Q6xjEEUM6Stz7DRvqp707Y4HQuaGPIlEEud+IjrA3A==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.6.0'}
|
||||
|
||||
primevue@4.3.5:
|
||||
resolution: {integrity: sha512-KYjLrf7W96qVOFdX2nyap5IrJIEF8qEfLaHpMPw+H3SCd7zV6uiIrOYBNvovk677rhjBGpSjEbxTFY/K+i/DMA==}
|
||||
primevue@4.3.6:
|
||||
resolution: {integrity: sha512-Wwg2dH6pBmOdkj9L/OnrCQf9AKPHfY5CcfnDyWeh0tNlR+XjYKGl8qvMdJOvGO9jjg6UdsX5MSaU8vDDsSG+sg==}
|
||||
engines: {node: '>=12.11.0'}
|
||||
|
||||
process-nextick-args@2.0.1:
|
||||
@ -6640,38 +6640,38 @@ snapshots:
|
||||
|
||||
'@polka/url@1.0.0-next.29': {}
|
||||
|
||||
'@primeuix/styled@0.6.4':
|
||||
'@primeuix/styled@0.7.0':
|
||||
dependencies:
|
||||
'@primeuix/utils': 0.5.4
|
||||
'@primeuix/utils': 0.6.0
|
||||
|
||||
'@primeuix/styles@1.1.1':
|
||||
'@primeuix/styles@1.2.1':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.6.4
|
||||
'@primeuix/styled': 0.7.0
|
||||
|
||||
'@primeuix/themes@1.1.2':
|
||||
'@primeuix/themes@1.2.1':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.6.4
|
||||
'@primeuix/styled': 0.7.0
|
||||
|
||||
'@primeuix/utils@0.5.4': {}
|
||||
'@primeuix/utils@0.6.0': {}
|
||||
|
||||
'@primevue/auto-import-resolver@4.3.5':
|
||||
'@primevue/auto-import-resolver@4.3.6':
|
||||
dependencies:
|
||||
'@primevue/metadata': 4.3.5
|
||||
'@primevue/metadata': 4.3.6
|
||||
|
||||
'@primevue/core@4.3.5(vue@3.5.17(typescript@5.8.3))':
|
||||
'@primevue/core@4.3.6(vue@3.5.17(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.6.4
|
||||
'@primeuix/utils': 0.5.4
|
||||
'@primeuix/styled': 0.7.0
|
||||
'@primeuix/utils': 0.6.0
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
|
||||
'@primevue/icons@4.3.5(vue@3.5.17(typescript@5.8.3))':
|
||||
'@primevue/icons@4.3.6(vue@3.5.17(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@primeuix/utils': 0.5.4
|
||||
'@primevue/core': 4.3.5(vue@3.5.17(typescript@5.8.3))
|
||||
'@primeuix/utils': 0.6.0
|
||||
'@primevue/core': 4.3.6(vue@3.5.17(typescript@5.8.3))
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
'@primevue/metadata@4.3.5': {}
|
||||
'@primevue/metadata@4.3.6': {}
|
||||
|
||||
'@protobufjs/aspromise@1.1.2': {}
|
||||
|
||||
@ -9871,13 +9871,13 @@ snapshots:
|
||||
|
||||
primelocale@2.1.4: {}
|
||||
|
||||
primevue@4.3.5(vue@3.5.17(typescript@5.8.3)):
|
||||
primevue@4.3.6(vue@3.5.17(typescript@5.8.3)):
|
||||
dependencies:
|
||||
'@primeuix/styled': 0.6.4
|
||||
'@primeuix/styles': 1.1.1
|
||||
'@primeuix/utils': 0.5.4
|
||||
'@primevue/core': 4.3.5(vue@3.5.17(typescript@5.8.3))
|
||||
'@primevue/icons': 4.3.5(vue@3.5.17(typescript@5.8.3))
|
||||
'@primeuix/styled': 0.7.0
|
||||
'@primeuix/styles': 1.2.1
|
||||
'@primeuix/utils': 0.6.0
|
||||
'@primevue/core': 4.3.6(vue@3.5.17(typescript@5.8.3))
|
||||
'@primevue/icons': 4.3.6(vue@3.5.17(typescript@5.8.3))
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
@ -10766,12 +10766,12 @@ snapshots:
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
utils4u@4.2.3(@vueuse/core@13.4.0(vue@3.5.17(typescript@5.8.3)))(dayjs@1.11.13)(nprogress@0.2.0)(primevue@4.3.5(vue@3.5.17(typescript@5.8.3)))(vant@4.9.20(vue@3.5.17(typescript@5.8.3)))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)):
|
||||
utils4u@4.2.3(@vueuse/core@13.4.0(vue@3.5.17(typescript@5.8.3)))(dayjs@1.11.13)(nprogress@0.2.0)(primevue@4.3.6(vue@3.5.17(typescript@5.8.3)))(vant@4.9.20(vue@3.5.17(typescript@5.8.3)))(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)):
|
||||
optionalDependencies:
|
||||
'@vueuse/core': 13.4.0(vue@3.5.17(typescript@5.8.3))
|
||||
dayjs: 1.11.13
|
||||
nprogress: 0.2.0
|
||||
primevue: 4.3.5(vue@3.5.17(typescript@5.8.3))
|
||||
primevue: 4.3.6(vue@3.5.17(typescript@5.8.3))
|
||||
vant: 4.9.20(vue@3.5.17(typescript@5.8.3))
|
||||
vue: 3.5.17(typescript@5.8.3)
|
||||
vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3))
|
||||
|
@ -15,9 +15,11 @@ const themeConfig = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-config-provider preflight-style-disabled>
|
||||
<a-config-provider :theme="themeConfig">
|
||||
<RouterView />
|
||||
</a-config-provider>
|
||||
</n-config-provider>
|
||||
|
||||
<DynamicDialog /> <ConfirmDialog /> <Toast />
|
||||
</template>
|
||||
|
321
src/layouts/naive-ui/AppLayout.vue
Normal file
321
src/layouts/naive-ui/AppLayout.vue
Normal file
@ -0,0 +1,321 @@
|
||||
<script setup lang="ts">
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
|
||||
import { createGetRoutes } from '@/plugins/router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 响应式断点检测
|
||||
const isMobile = ref(false);
|
||||
const isTablet = ref(false);
|
||||
|
||||
// 检测屏幕尺寸
|
||||
const updateScreenSize = () => {
|
||||
const width = window.innerWidth;
|
||||
const wasMobile = isMobile.value;
|
||||
|
||||
isMobile.value = width < 768;
|
||||
isTablet.value = width >= 768 && width < 1024;
|
||||
|
||||
// 当从移动端切换到桌面端时,关闭抽屉并重置折叠状态
|
||||
if (wasMobile && !isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
collapsed.value = false;
|
||||
}
|
||||
// 当从桌面端切换到移动端时,关闭抽屉
|
||||
else if (!wasMobile && isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 侧边栏状态管理
|
||||
const collapsed = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
// 初始化屏幕尺寸检测
|
||||
onMounted(() => {
|
||||
updateScreenSize();
|
||||
window.addEventListener('resize', updateScreenSize);
|
||||
|
||||
// 移动端默认收起侧边栏
|
||||
if (isMobile.value) {
|
||||
collapsed.value = true;
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateScreenSize);
|
||||
});
|
||||
|
||||
// 菜单项类型定义
|
||||
type MenuItemWithRoute = MenuOption & {
|
||||
routeName?: string;
|
||||
parentId?: string;
|
||||
originalPath?: string;
|
||||
};
|
||||
|
||||
// 生成菜单项
|
||||
const menuOptions = computed(() => {
|
||||
let flatArray: MenuItemWithRoute[] = createGetRoutes(router)()
|
||||
.filter((route) => !route.path.includes('/:'))
|
||||
.filter((route) => !route.meta.hidden)
|
||||
.map((route) => ({
|
||||
key: route.path,
|
||||
label: route.meta.title || `${(route.name as string) || route.path}`,
|
||||
routeName: route.name as string,
|
||||
}));
|
||||
|
||||
flatArray = flatArray.map((item) => {
|
||||
const originalPath = item.key as string; // 保存原始路径
|
||||
let id = item.key as string;
|
||||
if (flatArray.some((item) => (item.key as string).startsWith(`${id}/`))) {
|
||||
id = `${id}/index`;
|
||||
}
|
||||
// 去掉最前面的 /
|
||||
id = id.replace(/^\//, '');
|
||||
|
||||
let parentId = id.replace(/\/[^/]+$/, '');
|
||||
if (parentId === id) {
|
||||
parentId = '_ROOT_';
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
key: id,
|
||||
parentId,
|
||||
originalPath, // 保存原始路径用于后续映射
|
||||
};
|
||||
});
|
||||
|
||||
const groupItems: MenuItemWithRoute[] = [];
|
||||
for (const flatArrayItem of flatArray) {
|
||||
if (!groupItems.some((item) => item.key === flatArrayItem.parentId) && flatArrayItem.parentId !== '_ROOT_') {
|
||||
let groupItemParentId = flatArrayItem.parentId!.replace(/\/[^/]+$/, '');
|
||||
if (groupItemParentId === flatArrayItem.parentId) groupItemParentId = '_ROOT_';
|
||||
groupItems.push({
|
||||
key: flatArrayItem.parentId!,
|
||||
label: `Group ${flatArrayItem.parentId}`,
|
||||
parentId: groupItemParentId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const tree = arrayToTree([...flatArray, ...groupItems], {
|
||||
id: 'key',
|
||||
parentId: 'parentId',
|
||||
rootId: '_ROOT_',
|
||||
});
|
||||
|
||||
// 递归转换树形结构为 naive-ui menu 格式
|
||||
function convertToMenuOptions(tree: MenuItemWithRoute[]): MenuOption[] {
|
||||
return tree.map((item) => {
|
||||
const menuItem: MenuOption = {
|
||||
key: item.key,
|
||||
label: item.label,
|
||||
};
|
||||
|
||||
if (item.children && item.children.length > 0) {
|
||||
menuItem.children = convertToMenuOptions(item.children);
|
||||
} else if (item.routeName) {
|
||||
// 叶子节点,存储路由映射
|
||||
menuRouteMap.set(item.key as string, item.routeName);
|
||||
// 同时存储路径到 key 的映射(用于高亮显示)
|
||||
if (item.originalPath) {
|
||||
pathToKeyMap.set(item.originalPath, item.key as string);
|
||||
}
|
||||
(menuItem as MenuItemWithRoute).routeName = item.routeName;
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
});
|
||||
}
|
||||
|
||||
// 清空之前的映射
|
||||
menuRouteMap.clear();
|
||||
pathToKeyMap.clear();
|
||||
const result = convertToMenuOptions(tree);
|
||||
|
||||
// 菜单生成后,重新设置当前选中的菜单项
|
||||
nextTick(() => {
|
||||
const currentPath = router.currentRoute.value.path;
|
||||
const menuKey = pathToKeyMap.get(currentPath);
|
||||
if (menuKey) {
|
||||
selectedKey.value = menuKey;
|
||||
} else {
|
||||
const pathWithoutSlash = currentPath.replace(/^\//, '');
|
||||
selectedKey.value = pathWithoutSlash;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// 当前选中的菜单项
|
||||
const selectedKey = ref<string>();
|
||||
|
||||
// 存储菜单项与路由名称的映射
|
||||
const menuRouteMap = new Map<string, string>();
|
||||
|
||||
// 存储路由路径与菜单 key 的映射(用于高亮显示)
|
||||
const pathToKeyMap = new Map<string, string>();
|
||||
|
||||
// 处理菜单点击
|
||||
const handleMenuSelect = (key: string, item: MenuOption) => {
|
||||
const routeName = menuRouteMap.get(key) || (item as MenuItemWithRoute).routeName;
|
||||
if (routeName) {
|
||||
router.push({ name: routeName as never });
|
||||
|
||||
// 移动端点击菜单项后自动收起侧边栏
|
||||
if (isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 监听路由变化,更新选中的菜单项
|
||||
watch(
|
||||
() => router.currentRoute.value.path,
|
||||
(newPath) => {
|
||||
// 使用路径到 key 的映射来找到对应的菜单项
|
||||
const menuKey = pathToKeyMap.get(newPath);
|
||||
if (menuKey) {
|
||||
selectedKey.value = menuKey;
|
||||
} else {
|
||||
// 如果没有找到精确匹配,尝试去掉前面的 / 再匹配
|
||||
const pathWithoutSlash = newPath.replace(/^\//, '');
|
||||
selectedKey.value = pathWithoutSlash;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 切换侧边栏状态
|
||||
const toggleSidebar = () => {
|
||||
if (isMobile.value) {
|
||||
// 移动端使用抽屉模式
|
||||
drawerVisible.value = !drawerVisible.value;
|
||||
} else {
|
||||
// 桌面端使用折叠模式
|
||||
collapsed.value = !collapsed.value;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-layout :has-sider="!isMobile">
|
||||
<!-- 移动端抽屉 -->
|
||||
<n-drawer
|
||||
v-if="isMobile"
|
||||
v-model:show="drawerVisible"
|
||||
:width="280"
|
||||
placement="left"
|
||||
:trap-focus="false"
|
||||
:block-scroll="false"
|
||||
>
|
||||
<n-drawer-content title="菜单" :native-scrollbar="false">
|
||||
<n-menu :options="menuOptions" :value="selectedKey" @update:value="handleMenuSelect" />
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
|
||||
<!-- 桌面端侧边栏 -->
|
||||
<n-layout-sider
|
||||
v-if="!isMobile"
|
||||
:collapsed="collapsed"
|
||||
:native-scrollbar="false"
|
||||
bordered
|
||||
collapse-mode="width"
|
||||
:collapsed-width="64"
|
||||
:width="240"
|
||||
show-trigger
|
||||
@collapse="collapsed = true"
|
||||
@expand="collapsed = false"
|
||||
>
|
||||
<n-menu
|
||||
:collapsed="collapsed"
|
||||
:collapsed-width="64"
|
||||
:collapsed-icon-size="22"
|
||||
:options="menuOptions"
|
||||
:value="selectedKey"
|
||||
@update:value="handleMenuSelect"
|
||||
/>
|
||||
</n-layout-sider>
|
||||
|
||||
<n-layout>
|
||||
<n-layout-header
|
||||
bordered
|
||||
:style="{
|
||||
height: '64px',
|
||||
padding: isMobile ? '0 16px' : '0 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}"
|
||||
>
|
||||
<n-button
|
||||
quaternary
|
||||
@click="toggleSidebar"
|
||||
:style="{
|
||||
marginRight: isMobile ? '8px' : '12px',
|
||||
padding: isMobile ? '8px' : '6px',
|
||||
}"
|
||||
:size="isMobile ? 'medium' : 'small'"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon :size="isMobile ? 20 : 18">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
v-if="!isMobile && collapsed"
|
||||
fill="currentColor"
|
||||
d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"
|
||||
/>
|
||||
<path
|
||||
v-else-if="!isMobile && !collapsed"
|
||||
fill="currentColor"
|
||||
d="M3 18h13v-2H3v2zm0-5h10v-2H3v2zm0-7v2h13V6H3zm18 9.59L17.42 12 21 8.41 19.59 7l-5 5 5 5L21 15.59z"
|
||||
/>
|
||||
<!-- 移动端始终显示菜单图标 -->
|
||||
<path v-else fill="currentColor" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
|
||||
</svg>
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
<span
|
||||
:style="{
|
||||
fontSize: isMobile ? '16px' : '18px',
|
||||
fontWeight: '500',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}"
|
||||
>
|
||||
Vue TS Example
|
||||
</span>
|
||||
</n-layout-header>
|
||||
|
||||
<n-layout-content
|
||||
:content-style="{
|
||||
padding: isMobile ? '16px' : '24px',
|
||||
minHeight: 'calc(100vh - 64px - 72px)', // 减去头部和底部高度
|
||||
}"
|
||||
>
|
||||
<router-view />
|
||||
</n-layout-content>
|
||||
|
||||
<n-layout-footer
|
||||
bordered
|
||||
:style="{
|
||||
padding: isMobile ? '16px' : '24px',
|
||||
textAlign: 'center',
|
||||
}"
|
||||
>
|
||||
<span
|
||||
:style="{
|
||||
color: 'var(--n-text-color-disabled)',
|
||||
fontSize: isMobile ? '12px' : '14px',
|
||||
}"
|
||||
>
|
||||
© 2025 Vue TS Example. All rights reserved.
|
||||
</span>
|
||||
</n-layout-footer>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
</template>
|
@ -66,7 +66,8 @@ export function Plugins() {
|
||||
|
||||
// https://github.com/dishait/vite-plugin-vue-meta-layouts
|
||||
MetaLayouts({
|
||||
defaultLayout: 'sakai-vue/AppLayout',
|
||||
// defaultLayout: 'sakai-vue/AppLayout',
|
||||
defaultLayout: 'naive-ui/AppLayout',
|
||||
skipTopLevelRouteLayout: false, // 打开修复 https://github.com/JohnCampionJr/vite-plugin-vue-layouts/issues/134,默认为 false 关闭
|
||||
}),
|
||||
|
||||
|
Reference in New Issue
Block a user