feat: refactor sidebar management and routing metadata
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 2m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 4m39s

This commit is contained in:
严浩
2025-10-22 14:38:31 +08:00
parent e95d883c23
commit 917301dea6
10 changed files with 164 additions and 124 deletions

View File

@@ -1,12 +1,12 @@
<script setup lang="ts">
const collapsed = defineModel<boolean>('collapsed');
const buttonRef = useTemplateRef('buttonRef');
const appStore = useAppStore();
function toggleCollapsed() {
// https://github.com/tusen-ai/naive-ui/issues/3688
// hover style 鼠标移出就会消失 如果是点击 button 会聚焦需要失去焦点才会恢复
buttonRef.value?.$el.blur();
collapsed.value = !collapsed.value;
appStore.toggleSidebar();
}
const themeLabels: Record<AppThemeMode, string> = {
@@ -19,10 +19,10 @@ const themeLabels: Record<AppThemeMode, string> = {
<template>
<div class="h-full flex items-center justify-between px-12px shadow-header dark:shadow-gray-700">
<NTooltip :disabled="appStore.isMobile" placement="bottom-start">
{{ collapsed ? '展开菜单' : '收起菜单' }}
{{ appStore.sidebarCollapsed ? '展开菜单' : '收起菜单' }}
<template #trigger>
<NButton ref="buttonRef" quaternary @click="toggleCollapsed">
<icon-line-md:menu-fold-right v-if="collapsed" w-4.5 h-4.5 />
<icon-line-md:menu-fold-right v-if="appStore.sidebarCollapsed" w-4.5 h-4.5 />
<icon-line-md:menu-fold-left v-else w-4.5 h-4.5 />
</NButton>
</template>

View File

@@ -2,7 +2,8 @@
import { createGetRoutes, router } from '@/plugins/router-plugin';
import type { MenuOption } from 'naive-ui';
import { RouterLink, type RouteRecordRaw } from 'vue-router';
import IconMenuRounded from '~icons/material-symbols/menu-rounded';
import { useAppStore } from '../../stores/app-store';
// 路由转换为菜单树的辅助函数
function convertRoutesToMenuOptions(routes: Readonly<RouteRecordRaw[]>): MenuOption[] {
const menuMap = new Map<string, MenuOption>();
@@ -12,7 +13,7 @@ function convertRoutesToMenuOptions(routes: Readonly<RouteRecordRaw[]>): MenuOpt
const validRoutes = routes
.filter((route) => {
// 过滤掉不需要显示的路由
if (route.meta?.hidden === true || route.meta?.layout === false) {
if (route.meta?.hideInMenu === true || route.meta?.layout === false) {
return false;
}
// 过滤掉通配符路径
@@ -26,11 +27,11 @@ function convertRoutesToMenuOptions(routes: Readonly<RouteRecordRaw[]>): MenuOpt
// 构建菜单树
for (const route of validRoutes) {
const pathSegments = route.path.split('/').filter(Boolean);
const text = route.meta?.title || route.name;
const menuOption: MenuOption = {
label: () => (
<RouterLink to={route}>{route.meta?.title || (route.name as string)}</RouterLink>
),
label: () => (route.meta?.link === false ? text : <RouterLink to={route}>{text}</RouterLink>),
key: route.path,
icon: () => <IconMenuRounded style="width: 1em; height: 1em;" />,
};
// 如果是根路径或只有一级路径,直接添加到根菜单
@@ -52,6 +53,8 @@ function convertRoutesToMenuOptions(routes: Readonly<RouteRecordRaw[]>): MenuOpt
parent.children = [];
}
parent.children.push(menuOption);
} else {
console.warn(`未找到父菜单项: ${parentPath},无法将子菜单项添加到其下。`);
}
menuMap.set(route.path, menuOption);
@@ -79,14 +82,22 @@ watch(
},
{ immediate: true },
);
const appStore = useAppStore();
</script>
<template>
<!-- @update:value="handleMenuUpdate" -->
<NMenu
v-model:value="selectedKey"
ref="menuInstRef"
mode="vertical"
:collapsed="appStore.sidebarCollapsed"
:collapsed-width="64"
:icon-size="20"
:collapsed-icon-size="24"
v-model:value="selectedKey"
:options="menuOptions"
:inverted="false"
:root-indent="32"
:indent="32"
/>

View File

@@ -3,14 +3,13 @@ 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();
</script>
<template>
<AdminLayout :is-mobile="appStore.isMobile" v-model:sider-collapse="siderCollapse">
<AdminLayout :is-mobile="appStore.isMobile" v-model:sider-collapse="appStore.sidebarCollapsed">
<template #header>
<BaseLayoutHeader v-model:collapsed="siderCollapse" />
<BaseLayoutHeader />
</template>
<template #tab>
<div class="bg-green-100/28 dark:bg-green-900/28 text-green-900 dark:text-green-100 p-4">