diff --git a/frontend/src/assets/styles.css b/frontend/src/assets/styles.css index a4388f3..532a21a 100644 --- a/frontend/src/assets/styles.css +++ b/frontend/src/assets/styles.css @@ -163,6 +163,7 @@ a { :root[data-sidebar="edge"] .brand-title, :root[data-sidebar="edge"] .brand-subtitle, +:root[data-sidebar="edge"] .nav-section-title, :root[data-sidebar="edge"] .nav-link { color: white; } @@ -292,7 +293,8 @@ a { } .app-shell.is-sidebar-collapsed .brand-copy, -.app-shell.is-sidebar-collapsed .nav-link span { +.app-shell.is-sidebar-collapsed .nav-link span, +.app-shell.is-sidebar-collapsed .nav-section-title { max-width: 0; opacity: 0; pointer-events: none; @@ -315,6 +317,39 @@ a { padding-inline: 0; } +.app-shell.is-sidebar-collapsed .nav-section-title { + margin: 0; +} + +.nav-section { + display: grid; + gap: 5px; +} + +.nav-section + .nav-section { + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid var(--line-soft); +} + +.nav-section-title { + max-width: 180px; + margin: 0 8px 2px; + overflow: hidden; + color: var(--muted); + font-size: 12px; + font-weight: 700; + line-height: 1.4; + opacity: 0.78; + transform: translateX(0); + white-space: nowrap; + transition: + max-width var(--motion-duration) var(--motion-easing), + margin var(--motion-duration) var(--motion-easing), + opacity var(--motion-duration-fast) ease, + transform var(--motion-duration) var(--motion-easing); +} + .nav-link { display: flex; align-items: center; @@ -325,7 +360,7 @@ a { border-radius: var(--radius-md); background: transparent; color: var(--nav-text); - padding: 0 16px; + padding: 0 14px; text-align: left; transition: background var(--motion-duration-fast) ease, diff --git a/frontend/src/components/SidebarNav.vue b/frontend/src/components/SidebarNav.vue index 615806a..5f2749e 100644 --- a/frontend/src/components/SidebarNav.vue +++ b/frontend/src/components/SidebarNav.vue @@ -21,6 +21,7 @@ import { import { computed } from "vue"; import { useRouter } from "vue-router"; +import { buildSidebarSections } from "../navigation/sidebarMenu"; import { useAuthStore } from "../stores/auth"; defineProps<{ @@ -51,18 +52,15 @@ const backendIconMap = { operation_logs: ScrollText, }; -const menuItems = computed(() => [ - { - code: "dashboard", - name: "工作台", - path: "/dashboard", - icon: LayoutDashboard, - }, - ...auth.menus.map((menu) => ({ - ...menu, - icon: backendIconMap[menu.code as keyof typeof backendIconMap] || ClipboardList, +const menuSections = computed(() => + buildSidebarSections(auth.menus).map((section) => ({ + ...section, + items: section.items.map((menu) => ({ + ...menu, + icon: menu.code === "dashboard" ? LayoutDashboard : backendIconMap[menu.code as keyof typeof backendIconMap] || ClipboardList, + })), })), -]); +); function logout(): void { auth.logout(); @@ -86,18 +84,21 @@ function logout(): void {