@robot-admin/layout 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naive-Bd8pxcrF.js","names":["$slots"],"sources":["../src/components/LayoutRouterView/index.vue","../src/components/LayoutRouterView/index.vue","../src/components/layouts/SideLayout/index.vue","../src/components/layouts/SideLayout/index.vue","../src/components/ResponsiveMenu/index.vue","../src/components/ResponsiveMenu/index.vue","../src/components/layouts/TopLayout/index.vue","../src/components/layouts/TopLayout/index.vue","../src/components/layouts/MixLayout/index.vue","../src/components/layouts/MixLayout/index.vue","../src/components/layouts/MixTopLayout/index.vue","../src/components/layouts/MixTopLayout/index.vue","../src/components/layouts/ReverseHorizontalMixLayout/index.vue","../src/components/layouts/ReverseHorizontalMixLayout/index.vue","../src/components/layouts/CardLayout/index.vue","../src/components/layouts/CardLayout/index.vue","../src/components/C_LayoutContainer/index.vue","../src/components/C_LayoutContainer/index.vue","../src/components/SettingsDrawer/data.ts","../src/components/SettingsDrawer/index.vue","../src/components/SettingsDrawer/index.vue","../src/components/BrandLogo/index.vue","../src/components/BrandLogo/index.vue","../src/components/IconMenu/index.vue","../src/components/IconMenu/index.vue","../src/components/FloatingMenu/index.vue","../src/components/FloatingMenu/index.vue","../src/components/SideMenu/index.vue","../src/components/SideMenu/index.vue","../src/components/DrawerMenu/index.vue","../src/components/DrawerMenu/index.vue","../src/components/MenuTrigger/index.vue","../src/components/MenuTrigger/index.vue","../src/naive/index.ts"],"sourcesContent":["<!-- UI 框架无关的路由内容、过渡与 KeepAlive 统一边界。 -->\r\n<template>\r\n <RouterView v-slot=\"{ Component, route }\">\r\n <Transition :name=\"ctx.transitionName.value\" mode=\"out-in\">\r\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\r\n <component :is=\"Component\" :key=\"route.path\" />\r\n </KeepAlive>\r\n </Transition>\r\n </RouterView>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useLayoutCache } from \"../../composables/useLayoutCache\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"LayoutRouterView\" });\r\n\r\nconst ctx = useLayoutContext();\r\nconst { cachedViews, maxCacheCount } = useLayoutCache();\r\n</script>\r\n","<!-- UI 框架无关的路由内容、过渡与 KeepAlive 统一边界。 -->\r\n<template>\r\n <RouterView v-slot=\"{ Component, route }\">\r\n <Transition :name=\"ctx.transitionName.value\" mode=\"out-in\">\r\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\r\n <component :is=\"Component\" :key=\"route.path\" />\r\n </KeepAlive>\r\n </Transition>\r\n </RouterView>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useLayoutCache } from \"../../composables/useLayoutCache\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"LayoutRouterView\" });\r\n\r\nconst ctx = useLayoutContext();\r\nconst { cachedViews, maxCacheCount } = useLayoutCache();\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_SideLayout\n *\r\n * 左侧菜单布局骨架\r\n * 经典的左侧导航 + 右侧内容布局,使用 NLayout/NLayoutSider 实现\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域(侧边栏顶部)\r\n * #menu - 垂直菜单区域(侧边栏)\r\n * #header - 头部区域(含面包屑、导航栏右侧操作等)\r\n * #default - 页面内容(自动包含 RouterView + KeepAlive + Transition)\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <NLayout has-sider>\r\n <NLayoutSider\r\n bordered\r\n collapse-mode=\"width\"\r\n :collapsed-width=\"ctx.sidebarCollapsedWidth.value\"\r\n :width=\"ctx.sidebarWidth.value\"\r\n :native-scrollbar=\"false\"\r\n :collapsed=\"collapsed\"\r\n @update:collapsed=\"handleCollapsedUpdate\"\r\n :class=\"[\r\n 'layout-sider',\r\n 'no-horizontal-scroll',\r\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\r\n ]\"\r\n >\r\n <slot name=\"logo\" />\r\n <slot name=\"menu\" :collapsed=\"collapsed\" />\r\n </NLayoutSider>\r\n\r\n <NLayout>\r\n <slot name=\"header\" />\r\n\r\n <NLayoutContent class=\"content-with-header p16px app-content\">\r\n <LayoutRouterView />\r\n </NLayoutContent>\r\n\r\n <slot name=\"footer\" />\r\n </NLayout>\r\n </NLayout>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, ref, watch } from \"vue\";\r\nimport { NLayout, NLayoutSider, NLayoutContent } from \"naive-ui\";\r\nimport { useLayoutContext } from \"../../../composables/useLayoutContext\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\n\r\ndefineOptions({ name: \"C_SideLayout\" });\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 控制侧边栏折叠状态 (支持 v-model:collapsed) */\r\n collapsed?: boolean;\r\n }>(),\r\n { collapsed: false },\r\n);\r\n\r\nconst emit = defineEmits<{\r\n \"update:collapsed\": [value: boolean];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\n\r\n// 内部折叠状态,与 prop 双向同步\r\nconst _collapsed = ref(props.collapsed);\r\nwatch(\r\n () => props.collapsed,\r\n (v) => {\r\n _collapsed.value = v;\r\n },\r\n);\r\n\r\nconst collapsed = computed(() => _collapsed.value);\r\n\r\nconst handleCollapsedUpdate = (val: boolean) => {\r\n _collapsed.value = val;\r\n emit(\"update:collapsed\", val);\r\n};\r\n\r\n// 折叠切换\r\nconst toggleCollapse = () => {\r\n const next = !_collapsed.value;\r\n _collapsed.value = next;\r\n emit(\"update:collapsed\", next);\r\n};\r\n\r\ndefineExpose({\r\n collapsed,\r\n toggleCollapse,\r\n});\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_SideLayout\n *\r\n * 左侧菜单布局骨架\r\n * 经典的左侧导航 + 右侧内容布局,使用 NLayout/NLayoutSider 实现\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域(侧边栏顶部)\r\n * #menu - 垂直菜单区域(侧边栏)\r\n * #header - 头部区域(含面包屑、导航栏右侧操作等)\r\n * #default - 页面内容(自动包含 RouterView + KeepAlive + Transition)\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <NLayout has-sider>\r\n <NLayoutSider\r\n bordered\r\n collapse-mode=\"width\"\r\n :collapsed-width=\"ctx.sidebarCollapsedWidth.value\"\r\n :width=\"ctx.sidebarWidth.value\"\r\n :native-scrollbar=\"false\"\r\n :collapsed=\"collapsed\"\r\n @update:collapsed=\"handleCollapsedUpdate\"\r\n :class=\"[\r\n 'layout-sider',\r\n 'no-horizontal-scroll',\r\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\r\n ]\"\r\n >\r\n <slot name=\"logo\" />\r\n <slot name=\"menu\" :collapsed=\"collapsed\" />\r\n </NLayoutSider>\r\n\r\n <NLayout>\r\n <slot name=\"header\" />\r\n\r\n <NLayoutContent class=\"content-with-header p16px app-content\">\r\n <LayoutRouterView />\r\n </NLayoutContent>\r\n\r\n <slot name=\"footer\" />\r\n </NLayout>\r\n </NLayout>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, ref, watch } from \"vue\";\r\nimport { NLayout, NLayoutSider, NLayoutContent } from \"naive-ui\";\r\nimport { useLayoutContext } from \"../../../composables/useLayoutContext\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\n\r\ndefineOptions({ name: \"C_SideLayout\" });\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 控制侧边栏折叠状态 (支持 v-model:collapsed) */\r\n collapsed?: boolean;\r\n }>(),\r\n { collapsed: false },\r\n);\r\n\r\nconst emit = defineEmits<{\r\n \"update:collapsed\": [value: boolean];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\n\r\n// 内部折叠状态,与 prop 双向同步\r\nconst _collapsed = ref(props.collapsed);\r\nwatch(\r\n () => props.collapsed,\r\n (v) => {\r\n _collapsed.value = v;\r\n },\r\n);\r\n\r\nconst collapsed = computed(() => _collapsed.value);\r\n\r\nconst handleCollapsedUpdate = (val: boolean) => {\r\n _collapsed.value = val;\r\n emit(\"update:collapsed\", val);\r\n};\r\n\r\n// 折叠切换\r\nconst toggleCollapse = () => {\r\n const next = !_collapsed.value;\r\n _collapsed.value = next;\r\n emit(\"update:collapsed\", next);\r\n};\r\n\r\ndefineExpose({\r\n collapsed,\r\n toggleCollapse,\r\n});\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - ResponsiveMenu\r\n *\r\n * 响应式水平菜单\r\n * 用于 C_TopLayout / C_MixTopLayout / C_ReverseHorizontalMixLayout 的水平菜单\n * 自动检测容器宽度,溢出项放入“更多”下拉菜单,使用 NMenu (naive-ui) 渲染\r\n -->\r\n<template>\r\n <div :ref=\"setContainerRef\" class=\"responsive-menu\">\r\n <div class=\"responsive-menu__visible\">\r\n <NMenu\r\n v-if=\"visibleOptions.length > 0\"\r\n mode=\"horizontal\"\r\n :value=\"activeKey\"\r\n :options=\"visibleOptions\"\r\n :theme-overrides=\"menuThemeOverrides\"\r\n @update:value=\"handleSelect\"\r\n />\r\n </div>\r\n\r\n <NDropdown\r\n v-if=\"hiddenItems.length > 0\"\r\n :options=\"dropdownOptions\"\r\n placement=\"bottom-end\"\r\n trigger=\"hover\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"responsive-menu__more\">\r\n <NButton text class=\"responsive-menu__more-btn\">\r\n <!-- 纯 CSS 三个点图标 -->\r\n <span class=\"dots-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n </NButton>\r\n </div>\r\n </NDropdown>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, h } from \"vue\";\r\nimport { useRoute, useRouter } from \"vue-router\";\r\nimport { NMenu, NDropdown, NButton } from \"naive-ui\";\r\nimport type { MenuOption, DropdownOption } from \"naive-ui\";\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\nimport { useResponsiveMenu } from \"../../composables/useResponsiveMenu\";\r\n\r\ndefineOptions({ name: \"ResponsiveMenu\" });\r\n\r\nconst props = defineProps<{\r\n /** 菜单数据 */\r\n data: MenuOptions[];\r\n}>();\r\n\r\nconst route = useRoute();\r\nconst router = useRouter();\r\nconst ctx = useLayoutContext();\r\n\r\nconst activeKey = computed(() => route.path);\r\nconst { containerRef, visibleItems, hiddenItems } = useResponsiveMenu({\r\n data: () => props.data || [],\r\n});\r\nconst setContainerRef = (element: unknown) => {\r\n containerRef.value = element instanceof HTMLElement ? element : undefined;\r\n};\r\n\r\n// 根据主题选择菜单样式\r\nconst isDarkMode = ctx.isDark;\r\n\r\nconst lightTheme = {\r\n itemTextColor: \"rgba(51, 65, 85, 0.85)\",\r\n itemTextColorHover: \"rgba(30, 64, 175, 0.95)\",\r\n itemTextColorActive: \"rgba(30, 64, 175, 1)\",\r\n itemTextColorActiveHover: \"rgba(30, 64, 175, 1)\",\r\n itemTextColorChildActive: \"rgba(30, 64, 175, 1)\",\r\n itemIconColor: \"rgba(51, 65, 85, 0.7)\",\r\n itemIconColorHover: \"rgba(30, 64, 175, 0.85)\",\r\n itemIconColorActive: \"rgba(30, 64, 175, 1)\",\r\n itemIconColorActiveHover: \"rgba(30, 64, 175, 1)\",\r\n itemIconColorChildActive: \"rgba(30, 64, 175, 1)\",\r\n itemColorHover: \"rgba(30, 64, 175, 0.06)\",\r\n itemColorActive: \"rgba(30, 64, 175, 0.1)\",\r\n itemColorActiveHover: \"rgba(30, 64, 175, 0.12)\",\r\n itemColorActiveCollapsed: \"rgba(30, 64, 175, 0.1)\",\r\n arrowColor: \"rgba(51, 65, 85, 0.6)\",\r\n arrowColorHover: \"rgba(30, 64, 175, 0.8)\",\r\n arrowColorActive: \"rgba(30, 64, 175, 1)\",\r\n arrowColorChildActive: \"rgba(30, 64, 175, 1)\",\r\n itemHeight: \"42px\",\r\n itemPadding: \"0 18px\",\r\n itemBorderRadius: \"10px\",\r\n groupTextColor: \"rgba(51, 65, 85, 0.65)\",\r\n};\r\n\r\nconst darkTheme = {\r\n itemTextColor: \"rgba(226, 232, 240, 0.85)\",\r\n itemTextColorHover: \"rgba(255, 255, 255, 0.95)\",\r\n itemTextColorActive: \"rgba(147, 197, 253, 1)\",\r\n itemTextColorActiveHover: \"rgba(147, 197, 253, 1)\",\r\n itemTextColorChildActive: \"rgba(147, 197, 253, 1)\",\r\n itemIconColor: \"rgba(226, 232, 240, 0.7)\",\r\n itemIconColorHover: \"rgba(255, 255, 255, 0.85)\",\r\n itemIconColorActive: \"rgba(147, 197, 253, 1)\",\r\n itemIconColorActiveHover: \"rgba(147, 197, 253, 1)\",\r\n itemIconColorChildActive: \"rgba(147, 197, 253, 1)\",\r\n itemColorHover: \"rgba(59, 130, 246, 0.1)\",\r\n itemColorActive: \"rgba(59, 130, 246, 0.15)\",\r\n itemColorActiveHover: \"rgba(59, 130, 246, 0.18)\",\r\n itemColorActiveCollapsed: \"rgba(59, 130, 246, 0.15)\",\r\n arrowColor: \"rgba(226, 232, 240, 0.5)\",\r\n arrowColorHover: \"rgba(255, 255, 255, 0.7)\",\r\n arrowColorActive: \"rgba(147, 197, 253, 1)\",\r\n arrowColorChildActive: \"rgba(147, 197, 253, 1)\",\r\n itemHeight: \"42px\",\r\n itemPadding: \"0 18px\",\r\n itemBorderRadius: \"10px\",\r\n groupTextColor: \"rgba(226, 232, 240, 0.6)\",\r\n};\r\n\r\nconst menuThemeOverrides = computed(() =>\r\n isDarkMode.value ? darkTheme : lightTheme,\r\n);\r\n\r\n// ============ 图标渲染 ============\r\n\r\nconst renderIcon = (icon: string, size = 18) => {\r\n if (ctx.iconComponent) {\r\n return () => h(ctx.iconComponent!, { name: icon, size });\r\n }\r\n return () =>\r\n h(\"i\", {\r\n class: icon,\r\n style: {\r\n fontSize: `${size}px`,\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n },\r\n });\r\n};\r\n\r\n// ============ 菜单转换 ============\r\n\r\nconst getStringIcon = (item: MenuOptions) =>\r\n item.meta?.icon ?? (typeof item.icon === \"string\" ? item.icon : undefined);\r\n\r\nconst convertToMenuOption = (item: MenuOptions): MenuOption => {\r\n const icon = getStringIcon(item);\r\n return {\r\n key: item.path || item.key || \"\",\r\n label: item.meta?.title || item.label || item.name || \"\",\r\n icon: icon ? renderIcon(icon, 18) : undefined,\r\n children: item.children?.length\r\n ? item.children.map(convertToMenuOption)\r\n : undefined,\r\n };\r\n};\r\n\r\nconst visibleOptions = computed<MenuOption[]>(() =>\r\n visibleItems.value.map(convertToMenuOption),\r\n);\r\n\r\nconst dropdownOptions = computed<DropdownOption[]>(() =>\r\n hiddenItems.value.map((item) => {\r\n const icon = getStringIcon(item);\r\n return {\r\n key: item.path || item.key || \"\",\r\n label: item.meta?.title || item.label || item.name || \"\",\r\n icon: icon ? renderIcon(icon, 16) : undefined,\r\n children: item.children?.length\r\n ? item.children.map((child) => {\r\n const childIcon = getStringIcon(child);\r\n return {\r\n key: child.path || child.key || \"\",\r\n label: child.meta?.title || child.label || child.name || \"\",\r\n icon: childIcon ? renderIcon(childIcon, 16) : undefined,\r\n };\r\n })\r\n : undefined,\r\n };\r\n }),\r\n);\r\n\r\nconst handleSelect = (key: string) => {\r\n if (key) void router.push(key);\r\n};\r\n</script>\r\n\r\n<style scoped>\r\n.responsive-menu {\r\n display: flex;\r\n align-items: center;\r\n width: 100%;\r\n gap: 0;\r\n}\r\n\r\n.responsive-menu__visible {\r\n flex: 0 1 auto;\r\n min-width: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu) {\r\n background-color: transparent !important;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item) {\r\n position: relative;\r\n font-weight: 500;\r\n letter-spacing: 0.01em;\r\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n/* 防止 NMenu 内部截断菜单项文字 */\r\n.responsive-menu__visible :deep(.n-menu-item-content-header) {\r\n overflow: visible !important;\r\n text-overflow: clip !important;\r\n white-space: nowrap !important;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item-content) {\r\n overflow: visible !important;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item::after) {\r\n content: \"\";\r\n position: absolute;\r\n bottom: 0;\r\n left: 50%;\r\n transform: translateX(-50%) scaleX(0);\r\n width: 60%;\r\n height: 2px;\r\n background: linear-gradient(90deg, transparent, currentColor, transparent);\r\n opacity: 0;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item.n-menu-item--selected::after) {\r\n transform: translateX(-50%) scaleX(1);\r\n opacity: 0.5;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item:hover) {\r\n transform: translateY(-1px);\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item.n-menu-item--selected) {\r\n box-shadow:\r\n 0 2px 8px -2px rgba(30, 64, 175, 0.15),\r\n 0 1px 3px rgba(30, 64, 175, 0.1);\r\n}\r\n\r\n.responsive-menu__more {\r\n flex-shrink: 0;\r\n margin-left: 4px;\r\n}\r\n\r\n.responsive-menu__more-btn {\r\n min-width: 42px;\r\n height: 42px;\r\n padding: 0 18px !important;\r\n border-radius: 10px !important;\r\n background-color: transparent !important;\r\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.responsive-menu__more-btn::before,\r\n.responsive-menu__more-btn :deep(.n-button__border),\r\n.responsive-menu__more-btn :deep(.n-button__state-border) {\r\n display: none !important;\r\n}\r\n\r\n.responsive-menu__more-btn:hover {\r\n background-color: rgba(30, 64, 175, 0.06) !important;\r\n transform: translateY(-1px);\r\n}\r\n\r\n/* 纯 CSS 三个点图标 */\r\n.dots-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 3px;\r\n width: 20px;\r\n height: 20px;\r\n}\r\n\r\n.dots-icon span {\r\n width: 4px;\r\n height: 4px;\r\n border-radius: 50%;\r\n background-color: currentColor;\r\n opacity: 0.85;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - ResponsiveMenu\r\n *\r\n * 响应式水平菜单\r\n * 用于 C_TopLayout / C_MixTopLayout / C_ReverseHorizontalMixLayout 的水平菜单\n * 自动检测容器宽度,溢出项放入“更多”下拉菜单,使用 NMenu (naive-ui) 渲染\r\n -->\r\n<template>\r\n <div :ref=\"setContainerRef\" class=\"responsive-menu\">\r\n <div class=\"responsive-menu__visible\">\r\n <NMenu\r\n v-if=\"visibleOptions.length > 0\"\r\n mode=\"horizontal\"\r\n :value=\"activeKey\"\r\n :options=\"visibleOptions\"\r\n :theme-overrides=\"menuThemeOverrides\"\r\n @update:value=\"handleSelect\"\r\n />\r\n </div>\r\n\r\n <NDropdown\r\n v-if=\"hiddenItems.length > 0\"\r\n :options=\"dropdownOptions\"\r\n placement=\"bottom-end\"\r\n trigger=\"hover\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"responsive-menu__more\">\r\n <NButton text class=\"responsive-menu__more-btn\">\r\n <!-- 纯 CSS 三个点图标 -->\r\n <span class=\"dots-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n </NButton>\r\n </div>\r\n </NDropdown>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, h } from \"vue\";\r\nimport { useRoute, useRouter } from \"vue-router\";\r\nimport { NMenu, NDropdown, NButton } from \"naive-ui\";\r\nimport type { MenuOption, DropdownOption } from \"naive-ui\";\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\nimport { useResponsiveMenu } from \"../../composables/useResponsiveMenu\";\r\n\r\ndefineOptions({ name: \"ResponsiveMenu\" });\r\n\r\nconst props = defineProps<{\r\n /** 菜单数据 */\r\n data: MenuOptions[];\r\n}>();\r\n\r\nconst route = useRoute();\r\nconst router = useRouter();\r\nconst ctx = useLayoutContext();\r\n\r\nconst activeKey = computed(() => route.path);\r\nconst { containerRef, visibleItems, hiddenItems } = useResponsiveMenu({\r\n data: () => props.data || [],\r\n});\r\nconst setContainerRef = (element: unknown) => {\r\n containerRef.value = element instanceof HTMLElement ? element : undefined;\r\n};\r\n\r\n// 根据主题选择菜单样式\r\nconst isDarkMode = ctx.isDark;\r\n\r\nconst lightTheme = {\r\n itemTextColor: \"rgba(51, 65, 85, 0.85)\",\r\n itemTextColorHover: \"rgba(30, 64, 175, 0.95)\",\r\n itemTextColorActive: \"rgba(30, 64, 175, 1)\",\r\n itemTextColorActiveHover: \"rgba(30, 64, 175, 1)\",\r\n itemTextColorChildActive: \"rgba(30, 64, 175, 1)\",\r\n itemIconColor: \"rgba(51, 65, 85, 0.7)\",\r\n itemIconColorHover: \"rgba(30, 64, 175, 0.85)\",\r\n itemIconColorActive: \"rgba(30, 64, 175, 1)\",\r\n itemIconColorActiveHover: \"rgba(30, 64, 175, 1)\",\r\n itemIconColorChildActive: \"rgba(30, 64, 175, 1)\",\r\n itemColorHover: \"rgba(30, 64, 175, 0.06)\",\r\n itemColorActive: \"rgba(30, 64, 175, 0.1)\",\r\n itemColorActiveHover: \"rgba(30, 64, 175, 0.12)\",\r\n itemColorActiveCollapsed: \"rgba(30, 64, 175, 0.1)\",\r\n arrowColor: \"rgba(51, 65, 85, 0.6)\",\r\n arrowColorHover: \"rgba(30, 64, 175, 0.8)\",\r\n arrowColorActive: \"rgba(30, 64, 175, 1)\",\r\n arrowColorChildActive: \"rgba(30, 64, 175, 1)\",\r\n itemHeight: \"42px\",\r\n itemPadding: \"0 18px\",\r\n itemBorderRadius: \"10px\",\r\n groupTextColor: \"rgba(51, 65, 85, 0.65)\",\r\n};\r\n\r\nconst darkTheme = {\r\n itemTextColor: \"rgba(226, 232, 240, 0.85)\",\r\n itemTextColorHover: \"rgba(255, 255, 255, 0.95)\",\r\n itemTextColorActive: \"rgba(147, 197, 253, 1)\",\r\n itemTextColorActiveHover: \"rgba(147, 197, 253, 1)\",\r\n itemTextColorChildActive: \"rgba(147, 197, 253, 1)\",\r\n itemIconColor: \"rgba(226, 232, 240, 0.7)\",\r\n itemIconColorHover: \"rgba(255, 255, 255, 0.85)\",\r\n itemIconColorActive: \"rgba(147, 197, 253, 1)\",\r\n itemIconColorActiveHover: \"rgba(147, 197, 253, 1)\",\r\n itemIconColorChildActive: \"rgba(147, 197, 253, 1)\",\r\n itemColorHover: \"rgba(59, 130, 246, 0.1)\",\r\n itemColorActive: \"rgba(59, 130, 246, 0.15)\",\r\n itemColorActiveHover: \"rgba(59, 130, 246, 0.18)\",\r\n itemColorActiveCollapsed: \"rgba(59, 130, 246, 0.15)\",\r\n arrowColor: \"rgba(226, 232, 240, 0.5)\",\r\n arrowColorHover: \"rgba(255, 255, 255, 0.7)\",\r\n arrowColorActive: \"rgba(147, 197, 253, 1)\",\r\n arrowColorChildActive: \"rgba(147, 197, 253, 1)\",\r\n itemHeight: \"42px\",\r\n itemPadding: \"0 18px\",\r\n itemBorderRadius: \"10px\",\r\n groupTextColor: \"rgba(226, 232, 240, 0.6)\",\r\n};\r\n\r\nconst menuThemeOverrides = computed(() =>\r\n isDarkMode.value ? darkTheme : lightTheme,\r\n);\r\n\r\n// ============ 图标渲染 ============\r\n\r\nconst renderIcon = (icon: string, size = 18) => {\r\n if (ctx.iconComponent) {\r\n return () => h(ctx.iconComponent!, { name: icon, size });\r\n }\r\n return () =>\r\n h(\"i\", {\r\n class: icon,\r\n style: {\r\n fontSize: `${size}px`,\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n },\r\n });\r\n};\r\n\r\n// ============ 菜单转换 ============\r\n\r\nconst getStringIcon = (item: MenuOptions) =>\r\n item.meta?.icon ?? (typeof item.icon === \"string\" ? item.icon : undefined);\r\n\r\nconst convertToMenuOption = (item: MenuOptions): MenuOption => {\r\n const icon = getStringIcon(item);\r\n return {\r\n key: item.path || item.key || \"\",\r\n label: item.meta?.title || item.label || item.name || \"\",\r\n icon: icon ? renderIcon(icon, 18) : undefined,\r\n children: item.children?.length\r\n ? item.children.map(convertToMenuOption)\r\n : undefined,\r\n };\r\n};\r\n\r\nconst visibleOptions = computed<MenuOption[]>(() =>\r\n visibleItems.value.map(convertToMenuOption),\r\n);\r\n\r\nconst dropdownOptions = computed<DropdownOption[]>(() =>\r\n hiddenItems.value.map((item) => {\r\n const icon = getStringIcon(item);\r\n return {\r\n key: item.path || item.key || \"\",\r\n label: item.meta?.title || item.label || item.name || \"\",\r\n icon: icon ? renderIcon(icon, 16) : undefined,\r\n children: item.children?.length\r\n ? item.children.map((child) => {\r\n const childIcon = getStringIcon(child);\r\n return {\r\n key: child.path || child.key || \"\",\r\n label: child.meta?.title || child.label || child.name || \"\",\r\n icon: childIcon ? renderIcon(childIcon, 16) : undefined,\r\n };\r\n })\r\n : undefined,\r\n };\r\n }),\r\n);\r\n\r\nconst handleSelect = (key: string) => {\r\n if (key) void router.push(key);\r\n};\r\n</script>\r\n\r\n<style scoped>\r\n.responsive-menu {\r\n display: flex;\r\n align-items: center;\r\n width: 100%;\r\n gap: 0;\r\n}\r\n\r\n.responsive-menu__visible {\r\n flex: 0 1 auto;\r\n min-width: 0;\r\n overflow: hidden;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu) {\r\n background-color: transparent !important;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item) {\r\n position: relative;\r\n font-weight: 500;\r\n letter-spacing: 0.01em;\r\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n/* 防止 NMenu 内部截断菜单项文字 */\r\n.responsive-menu__visible :deep(.n-menu-item-content-header) {\r\n overflow: visible !important;\r\n text-overflow: clip !important;\r\n white-space: nowrap !important;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item-content) {\r\n overflow: visible !important;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item::after) {\r\n content: \"\";\r\n position: absolute;\r\n bottom: 0;\r\n left: 50%;\r\n transform: translateX(-50%) scaleX(0);\r\n width: 60%;\r\n height: 2px;\r\n background: linear-gradient(90deg, transparent, currentColor, transparent);\r\n opacity: 0;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item.n-menu-item--selected::after) {\r\n transform: translateX(-50%) scaleX(1);\r\n opacity: 0.5;\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item:hover) {\r\n transform: translateY(-1px);\r\n}\r\n\r\n.responsive-menu__visible :deep(.n-menu-item.n-menu-item--selected) {\r\n box-shadow:\r\n 0 2px 8px -2px rgba(30, 64, 175, 0.15),\r\n 0 1px 3px rgba(30, 64, 175, 0.1);\r\n}\r\n\r\n.responsive-menu__more {\r\n flex-shrink: 0;\r\n margin-left: 4px;\r\n}\r\n\r\n.responsive-menu__more-btn {\r\n min-width: 42px;\r\n height: 42px;\r\n padding: 0 18px !important;\r\n border-radius: 10px !important;\r\n background-color: transparent !important;\r\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.responsive-menu__more-btn::before,\r\n.responsive-menu__more-btn :deep(.n-button__border),\r\n.responsive-menu__more-btn :deep(.n-button__state-border) {\r\n display: none !important;\r\n}\r\n\r\n.responsive-menu__more-btn:hover {\r\n background-color: rgba(30, 64, 175, 0.06) !important;\r\n transform: translateY(-1px);\r\n}\r\n\r\n/* 纯 CSS 三个点图标 */\r\n.dots-icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 3px;\r\n width: 20px;\r\n height: 20px;\r\n}\r\n\r\n.dots-icon span {\r\n width: 4px;\r\n height: 4px;\r\n border-radius: 50%;\r\n background-color: currentColor;\r\n opacity: 0.85;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - C_TopLayout\n *\r\n * 顶部导航布局骨架\r\n * Logo+品牌 | 水平菜单 | 操作区 → 标签页 → 内容区 → 页脚\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域\r\n * #menu - 水平菜单(默认使用 ResponsiveMenu)\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"top-layout-container\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 左侧:Logo 和品牌 -->\r\n <div class=\"navbar-left\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-container\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </div>\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 中间:水平菜单 -->\r\n <div class=\"navbar-center\">\r\n <slot name=\"menu\">\r\n <ResponsiveMenu :data=\"menus\" />\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 主内容区域 -->\r\n <NLayout>\r\n <NLayoutContent class=\"main-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <!-- 页脚 -->\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\n\r\ndefineOptions({ name: \"C_TopLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_TopLayout\n *\r\n * 顶部导航布局骨架\r\n * Logo+品牌 | 水平菜单 | 操作区 → 标签页 → 内容区 → 页脚\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域\r\n * #menu - 水平菜单(默认使用 ResponsiveMenu)\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"top-layout-container\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 左侧:Logo 和品牌 -->\r\n <div class=\"navbar-left\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-container\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </div>\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 中间:水平菜单 -->\r\n <div class=\"navbar-center\">\r\n <slot name=\"menu\">\r\n <ResponsiveMenu :data=\"menus\" />\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 主内容区域 -->\r\n <NLayout>\r\n <NLayoutContent class=\"main-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <!-- 页脚 -->\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\n\r\ndefineOptions({ name: \"C_TopLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_MixLayout\n *\r\n * 混合布局骨架\r\n * 左侧一级菜单图标栏 + 悬浮二级菜单弹出面板 + 右侧内容区\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域(侧边栏顶部)\r\n * #header - 头部区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"mix-layout-container\">\r\n <!-- 左侧一级菜单 -->\r\n <div\r\n class=\"first-level-menu\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- Logo -->\r\n <div class=\"logo-container\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </slot>\r\n </div>\r\n\r\n <!-- 一级菜单列表 -->\r\n <div class=\"first-menu-list\">\r\n <div\r\n v-for=\"item in menus\"\r\n :key=\"item.path\"\r\n class=\"first-menu-item\"\r\n :class=\"{\r\n active: menuSplit.activeFirstMenu.value === item.path,\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"item.disabled ? -1 : 0\"\r\n :aria-disabled=\"item.disabled || undefined\"\r\n @click=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.enter=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.space.prevent=\"menuSplit.handleFirstMenuClick(item)\"\r\n >\r\n <div class=\"menu-item-content\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"22\"\r\n />\r\n <span v-else class=\"menu-item-text\">{{\r\n (item.meta?.title || \"\")[0]\r\n }}</span>\r\n </div>\r\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 悬浮二级菜单 -->\r\n <Transition name=\"slide-fade\">\r\n <div\r\n v-if=\"menuSplit.showSecondMenu.value\"\r\n class=\"second-level-menu-popup\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <div class=\"second-menu-header\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"menuSplit.displayMenuItem.value?.meta?.icon\"\r\n :name=\"menuSplit.displayMenuItem.value.meta.icon\"\r\n :size=\"20\"\r\n />\r\n <span class=\"menu-title\">{{\r\n menuSplit.displayMenuItem.value?.meta?.title\r\n }}</span>\r\n </div>\r\n <div class=\"second-menu-list\">\r\n <div\r\n v-for=\"child in menuSplit.displayMenuItem.value?.children || []\"\r\n :key=\"child.path\"\r\n class=\"second-menu-item\"\r\n :class=\"{ active: menuSplit.isMenuItemActive(child.path) }\"\r\n role=\"link\"\r\n :tabindex=\"child.disabled ? -1 : 0\"\r\n :aria-disabled=\"child.disabled || undefined\"\r\n @click=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.enter=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.space.prevent=\"menuSplit.handleSecondMenuClick(child)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"menu-item-label\">{{ child.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </Transition>\r\n\r\n <!-- 右侧主内容区 -->\r\n <div class=\"main-layout\">\r\n <slot name=\"header\" />\r\n\r\n <NLayout>\r\n <NLayoutContent class=\"content-with-header p16px app-content\">\r\n <LayoutRouterView />\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_MixLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\nconst menuSplit = useMenuSplit({\r\n menus: ctx.menus,\r\n floatingSecondMenu: computed(() => true),\r\n});\r\n\r\n// 图标组件:优先使用消费方提供的,否则用 CSS class 渲染\r\nconst LayoutIcon = useLayoutIcon();\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_MixLayout\n *\r\n * 混合布局骨架\r\n * 左侧一级菜单图标栏 + 悬浮二级菜单弹出面板 + 右侧内容区\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域(侧边栏顶部)\r\n * #header - 头部区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"mix-layout-container\">\r\n <!-- 左侧一级菜单 -->\r\n <div\r\n class=\"first-level-menu\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- Logo -->\r\n <div class=\"logo-container\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </slot>\r\n </div>\r\n\r\n <!-- 一级菜单列表 -->\r\n <div class=\"first-menu-list\">\r\n <div\r\n v-for=\"item in menus\"\r\n :key=\"item.path\"\r\n class=\"first-menu-item\"\r\n :class=\"{\r\n active: menuSplit.activeFirstMenu.value === item.path,\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"item.disabled ? -1 : 0\"\r\n :aria-disabled=\"item.disabled || undefined\"\r\n @click=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.enter=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.space.prevent=\"menuSplit.handleFirstMenuClick(item)\"\r\n >\r\n <div class=\"menu-item-content\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"22\"\r\n />\r\n <span v-else class=\"menu-item-text\">{{\r\n (item.meta?.title || \"\")[0]\r\n }}</span>\r\n </div>\r\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 悬浮二级菜单 -->\r\n <Transition name=\"slide-fade\">\r\n <div\r\n v-if=\"menuSplit.showSecondMenu.value\"\r\n class=\"second-level-menu-popup\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <div class=\"second-menu-header\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"menuSplit.displayMenuItem.value?.meta?.icon\"\r\n :name=\"menuSplit.displayMenuItem.value.meta.icon\"\r\n :size=\"20\"\r\n />\r\n <span class=\"menu-title\">{{\r\n menuSplit.displayMenuItem.value?.meta?.title\r\n }}</span>\r\n </div>\r\n <div class=\"second-menu-list\">\r\n <div\r\n v-for=\"child in menuSplit.displayMenuItem.value?.children || []\"\r\n :key=\"child.path\"\r\n class=\"second-menu-item\"\r\n :class=\"{ active: menuSplit.isMenuItemActive(child.path) }\"\r\n role=\"link\"\r\n :tabindex=\"child.disabled ? -1 : 0\"\r\n :aria-disabled=\"child.disabled || undefined\"\r\n @click=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.enter=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.space.prevent=\"menuSplit.handleSecondMenuClick(child)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"menu-item-label\">{{ child.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </Transition>\r\n\r\n <!-- 右侧主内容区 -->\r\n <div class=\"main-layout\">\r\n <slot name=\"header\" />\r\n\r\n <NLayout>\r\n <NLayoutContent class=\"content-with-header p16px app-content\">\r\n <LayoutRouterView />\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_MixLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\nconst menuSplit = useMenuSplit({\r\n menus: ctx.menus,\r\n floatingSecondMenu: computed(() => true),\r\n});\r\n\r\n// 图标组件:优先使用消费方提供的,否则用 CSS class 渲染\r\nconst LayoutIcon = useLayoutIcon();\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_MixTopLayout\n *\r\n * 顶部混合布局骨架\r\n * 左侧一级菜单 + 顶部二级水平菜单 + 内容区\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域(侧边栏顶部)\r\n * #brand - 顶部导航品牌区\r\n * #top-menu - 二级水平菜单(默认使用 ResponsiveMenu)\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"mix-top-layout-container\">\r\n <!-- 左侧一级菜单 -->\r\n <div\r\n class=\"first-level-sidebar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- Logo -->\r\n <div class=\"logo-container\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </slot>\r\n </div>\r\n\r\n <!-- 一级菜单列表 -->\r\n <div class=\"first-menu-list\">\r\n <div\r\n v-for=\"item in menus\"\r\n :key=\"item.path\"\r\n class=\"first-menu-item\"\r\n :class=\"{\r\n active: menuSplit.activeFirstMenu.value === item.path,\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"item.disabled ? -1 : 0\"\r\n :aria-disabled=\"item.disabled || undefined\"\r\n :aria-current=\"\r\n menuSplit.activeFirstMenu.value === item.path ? 'page' : undefined\r\n \"\r\n @click=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.enter=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.space.prevent=\"menuSplit.handleFirstMenuClick(item)\"\r\n >\r\n <div class=\"menu-item-content\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"22\"\r\n />\r\n <span v-else class=\"menu-item-text\">{{\r\n (item.meta?.title || \"\")[0]\r\n }}</span>\r\n </div>\r\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 右侧主区域 -->\r\n <div class=\"main-area\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 左侧:品牌 -->\r\n <div class=\"navbar-left\">\r\n <slot name=\"brand\">\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 中间:二级水平菜单 -->\r\n <div class=\"navbar-center\">\r\n <slot name=\"top-menu\">\r\n <ResponsiveMenu\r\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\r\n :data=\"menuSplit.currentSecondMenus.value\"\r\n />\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 内容区 -->\r\n <NLayout class=\"content-layout\">\r\n <NLayoutContent class=\"main-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\r\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_MixTopLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\nconst menuSplit = useMenuSplit({\r\n menus: ctx.menus,\r\n floatingSecondMenu: computed(() => false),\r\n});\r\n\r\nconst LayoutIcon = useLayoutIcon();\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_MixTopLayout\n *\r\n * 顶部混合布局骨架\r\n * 左侧一级菜单 + 顶部二级水平菜单 + 内容区\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域(侧边栏顶部)\r\n * #brand - 顶部导航品牌区\r\n * #top-menu - 二级水平菜单(默认使用 ResponsiveMenu)\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"mix-top-layout-container\">\r\n <!-- 左侧一级菜单 -->\r\n <div\r\n class=\"first-level-sidebar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- Logo -->\r\n <div class=\"logo-container\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 40\"\r\n :height=\"brand.logoSize || 40\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </slot>\r\n </div>\r\n\r\n <!-- 一级菜单列表 -->\r\n <div class=\"first-menu-list\">\r\n <div\r\n v-for=\"item in menus\"\r\n :key=\"item.path\"\r\n class=\"first-menu-item\"\r\n :class=\"{\r\n active: menuSplit.activeFirstMenu.value === item.path,\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"item.disabled ? -1 : 0\"\r\n :aria-disabled=\"item.disabled || undefined\"\r\n :aria-current=\"\r\n menuSplit.activeFirstMenu.value === item.path ? 'page' : undefined\r\n \"\r\n @click=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.enter=\"menuSplit.handleFirstMenuClick(item)\"\r\n @keydown.space.prevent=\"menuSplit.handleFirstMenuClick(item)\"\r\n >\r\n <div class=\"menu-item-content\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"22\"\r\n />\r\n <span v-else class=\"menu-item-text\">{{\r\n (item.meta?.title || \"\")[0]\r\n }}</span>\r\n </div>\r\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 右侧主区域 -->\r\n <div class=\"main-area\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 左侧:品牌 -->\r\n <div class=\"navbar-left\">\r\n <slot name=\"brand\">\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 中间:二级水平菜单 -->\r\n <div class=\"navbar-center\">\r\n <slot name=\"top-menu\">\r\n <ResponsiveMenu\r\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\r\n :data=\"menuSplit.currentSecondMenus.value\"\r\n />\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 内容区 -->\r\n <NLayout class=\"content-layout\">\r\n <NLayoutContent class=\"main-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\r\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_MixTopLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\nconst menuSplit = useMenuSplit({\r\n menus: ctx.menus,\r\n floatingSecondMenu: computed(() => false),\r\n});\r\n\r\nconst LayoutIcon = useLayoutIcon();\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_ReverseHorizontalMixLayout\n *\r\n * 反转混合布局骨架\r\n * 顶部一级菜单 + 右侧二级菜单侧边栏 + 左侧内容区\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域\r\n * #top-menu - 一级水平菜单(默认使用 ResponsiveMenu)\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"reverse-horizontal-mix-layout-container\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 左侧:Logo 和品牌 -->\r\n <div class=\"navbar-left\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-container\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </div>\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 中间:一级水平菜单 -->\r\n <div class=\"navbar-center\">\r\n <slot name=\"top-menu\">\r\n <ResponsiveMenu :data=\"menus\" />\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 主区域:左侧内容 + 右侧菜单 -->\r\n <div class=\"main-area\">\r\n <!-- 左侧:主内容区 -->\r\n <NLayout class=\"content-layout\">\r\n <NLayoutContent class=\"main-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n\r\n <!-- 右侧折叠/展开按钮 - 始终可见 -->\r\n <div\r\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\r\n class=\"sidebar-toggle\"\r\n :class=\"[\r\n isDarkMode ? 'dark-theme' : 'light-theme',\r\n { active: !isCollapsed },\r\n ]\"\r\n @click=\"toggleCollapse\"\r\n role=\"button\"\r\n tabindex=\"0\"\r\n :aria-expanded=\"!isCollapsed\"\r\n @keydown.enter=\"toggleCollapse\"\r\n @keydown.space.prevent=\"toggleCollapse\"\r\n >\r\n <span\r\n :class=\"[\r\n 'collapse-arrow',\r\n 'toggle-icon',\r\n isCollapsed ? 'collapsed' : 'expanded',\r\n ]\"\r\n >\r\n <span class=\"arrow-line\"></span>\r\n <span class=\"arrow-line\"></span>\r\n </span>\r\n </div>\r\n\r\n <!-- 右侧:二级菜单侧边栏 -->\r\n <div\r\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\r\n class=\"right-sidebar-wrapper\"\r\n :class=\"{ collapsed: isCollapsed }\"\r\n >\r\n <div\r\n class=\"right-sidebar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 侧边栏标题 -->\r\n <div\r\n class=\"sidebar-header\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <div class=\"header-icon\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"menuSplit.activeFirstMenuItem.value?.meta?.icon\"\r\n :name=\"menuSplit.activeFirstMenuItem.value.meta.icon\"\r\n :size=\"20\"\r\n />\r\n </div>\r\n <span class=\"header-title\">{{\r\n menuSplit.activeFirstMenuItem.value?.meta?.title || \"菜单\"\r\n }}</span>\r\n </div>\r\n\r\n <!-- 二级菜单列表 -->\r\n <div class=\"sidebar-menu-list\">\r\n <template\r\n v-for=\"child in menuSplit.currentSecondMenus.value\"\r\n :key=\"child.path\"\r\n >\r\n <!-- 有子菜单的项 -->\r\n <div\r\n v-if=\"child.children && child.children.length > 0\"\r\n class=\"menu-group\"\r\n >\r\n <div class=\"group-title\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span>{{ child.meta?.title }}</span>\r\n </div>\r\n <div\r\n v-for=\"subChild in child.children\"\r\n :key=\"subChild.path\"\r\n class=\"menu-item sub-item\"\r\n :class=\"{\r\n active: menuSplit.isMenuItemActive(subChild.path),\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"subChild.disabled ? -1 : 0\"\r\n :aria-disabled=\"subChild.disabled || undefined\"\r\n :aria-current=\"\r\n menuSplit.isMenuItemActive(subChild.path)\r\n ? 'page'\r\n : undefined\r\n \"\r\n @click=\"menuSplit.handleSecondMenuClick(subChild)\"\r\n @keydown.enter=\"menuSplit.handleSecondMenuClick(subChild)\"\r\n @keydown.space.prevent=\"\r\n menuSplit.handleSecondMenuClick(subChild)\r\n \"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"subChild.meta?.icon\"\r\n :name=\"subChild.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"item-title\">{{ subChild.meta?.title }}</span>\r\n </div>\r\n </div>\r\n <!-- 没有子菜单的项 -->\r\n <div\r\n v-else\r\n class=\"menu-item\"\r\n :class=\"{\r\n active: menuSplit.isMenuItemActive(child.path),\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"child.disabled ? -1 : 0\"\r\n :aria-disabled=\"child.disabled || undefined\"\r\n :aria-current=\"\r\n menuSplit.isMenuItemActive(child.path) ? 'page' : undefined\r\n \"\r\n @click=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.enter=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.space.prevent=\"menuSplit.handleSecondMenuClick(child)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span class=\"item-title\">{{ child.meta?.title }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { ref, computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\r\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_ReverseHorizontalMixLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\nconst menuSplit = useMenuSplit({\r\n menus: ctx.menus,\r\n floatingSecondMenu: computed(() => false),\r\n});\r\n\r\n// 右侧边栏折叠状态\r\nconst isCollapsed = ref(false);\r\nconst toggleCollapse = () => {\r\n isCollapsed.value = !isCollapsed.value;\r\n};\r\n\r\nconst LayoutIcon = useLayoutIcon();\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_ReverseHorizontalMixLayout\n *\r\n * 反转混合布局骨架\r\n * 顶部一级菜单 + 右侧二级菜单侧边栏 + 左侧内容区\r\n *\r\n * Slots:\r\n * #logo - Logo/品牌区域\r\n * #top-menu - 一级水平菜单(默认使用 ResponsiveMenu)\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"reverse-horizontal-mix-layout-container\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 左侧:Logo 和品牌 -->\r\n <div class=\"navbar-left\">\r\n <slot name=\"logo\">\r\n <div class=\"logo-container\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </div>\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 中间:一级水平菜单 -->\r\n <div class=\"navbar-center\">\r\n <slot name=\"top-menu\">\r\n <ResponsiveMenu :data=\"menus\" />\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 主区域:左侧内容 + 右侧菜单 -->\r\n <div class=\"main-area\">\r\n <!-- 左侧:主内容区 -->\r\n <NLayout class=\"content-layout\">\r\n <NLayoutContent class=\"main-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n\r\n <!-- 右侧折叠/展开按钮 - 始终可见 -->\r\n <div\r\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\r\n class=\"sidebar-toggle\"\r\n :class=\"[\r\n isDarkMode ? 'dark-theme' : 'light-theme',\r\n { active: !isCollapsed },\r\n ]\"\r\n @click=\"toggleCollapse\"\r\n role=\"button\"\r\n tabindex=\"0\"\r\n :aria-expanded=\"!isCollapsed\"\r\n @keydown.enter=\"toggleCollapse\"\r\n @keydown.space.prevent=\"toggleCollapse\"\r\n >\r\n <span\r\n :class=\"[\r\n 'collapse-arrow',\r\n 'toggle-icon',\r\n isCollapsed ? 'collapsed' : 'expanded',\r\n ]\"\r\n >\r\n <span class=\"arrow-line\"></span>\r\n <span class=\"arrow-line\"></span>\r\n </span>\r\n </div>\r\n\r\n <!-- 右侧:二级菜单侧边栏 -->\r\n <div\r\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\r\n class=\"right-sidebar-wrapper\"\r\n :class=\"{ collapsed: isCollapsed }\"\r\n >\r\n <div\r\n class=\"right-sidebar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 侧边栏标题 -->\r\n <div\r\n class=\"sidebar-header\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <div class=\"header-icon\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"menuSplit.activeFirstMenuItem.value?.meta?.icon\"\r\n :name=\"menuSplit.activeFirstMenuItem.value.meta.icon\"\r\n :size=\"20\"\r\n />\r\n </div>\r\n <span class=\"header-title\">{{\r\n menuSplit.activeFirstMenuItem.value?.meta?.title || \"菜单\"\r\n }}</span>\r\n </div>\r\n\r\n <!-- 二级菜单列表 -->\r\n <div class=\"sidebar-menu-list\">\r\n <template\r\n v-for=\"child in menuSplit.currentSecondMenus.value\"\r\n :key=\"child.path\"\r\n >\r\n <!-- 有子菜单的项 -->\r\n <div\r\n v-if=\"child.children && child.children.length > 0\"\r\n class=\"menu-group\"\r\n >\r\n <div class=\"group-title\">\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span>{{ child.meta?.title }}</span>\r\n </div>\r\n <div\r\n v-for=\"subChild in child.children\"\r\n :key=\"subChild.path\"\r\n class=\"menu-item sub-item\"\r\n :class=\"{\r\n active: menuSplit.isMenuItemActive(subChild.path),\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"subChild.disabled ? -1 : 0\"\r\n :aria-disabled=\"subChild.disabled || undefined\"\r\n :aria-current=\"\r\n menuSplit.isMenuItemActive(subChild.path)\r\n ? 'page'\r\n : undefined\r\n \"\r\n @click=\"menuSplit.handleSecondMenuClick(subChild)\"\r\n @keydown.enter=\"menuSplit.handleSecondMenuClick(subChild)\"\r\n @keydown.space.prevent=\"\r\n menuSplit.handleSecondMenuClick(subChild)\r\n \"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"subChild.meta?.icon\"\r\n :name=\"subChild.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"item-title\">{{ subChild.meta?.title }}</span>\r\n </div>\r\n </div>\r\n <!-- 没有子菜单的项 -->\r\n <div\r\n v-else\r\n class=\"menu-item\"\r\n :class=\"{\r\n active: menuSplit.isMenuItemActive(child.path),\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"child.disabled ? -1 : 0\"\r\n :aria-disabled=\"child.disabled || undefined\"\r\n :aria-current=\"\r\n menuSplit.isMenuItemActive(child.path) ? 'page' : undefined\r\n \"\r\n @click=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.enter=\"menuSplit.handleSecondMenuClick(child)\"\r\n @keydown.space.prevent=\"menuSplit.handleSecondMenuClick(child)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span class=\"item-title\">{{ child.meta?.title }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { ref, computed } from \"vue\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\r\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_ReverseHorizontalMixLayout\" });\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\nconst menuSplit = useMenuSplit({\r\n menus: ctx.menus,\r\n floatingSecondMenu: computed(() => false),\r\n});\r\n\r\n// 右侧边栏折叠状态\r\nconst isCollapsed = ref(false);\r\nconst toggleCollapse = () => {\r\n isCollapsed.value = !isCollapsed.value;\r\n};\r\n\r\nconst LayoutIcon = useLayoutIcon();\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_CardLayout\n *\r\n * 卡片式布局骨架\r\n * 顶部导航 + hover 触发抽屉式菜单 + 内容区\r\n *\r\n * Slots:\r\n * #menu-trigger - 菜单触发区域(hover 弹出抽屉)\r\n * #logo - Logo/品牌区域\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #drawer-menu - 抽屉式菜单内容\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"card-layout-container\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <div class=\"navbar-left\">\r\n <!-- 悬停触发区域 -->\r\n <div\r\n class=\"hover-trigger-area\"\r\n @mouseenter=\"cancelHideTimer\"\r\n @mouseleave=\"hideDrawerMenu\"\r\n >\r\n <slot name=\"menu-trigger\">\r\n <div class=\"menu-indicator\">\r\n <!-- 纯 CSS 三横线菜单图标 -->\r\n <span class=\"hamburger-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n </div>\r\n </slot>\r\n </div>\r\n\r\n <slot name=\"logo\">\r\n <div class=\"logo-container\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </div>\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 抽屉式菜单 -->\r\n <div\r\n class=\"drawer-menu\"\r\n :class=\"[\r\n isDarkMode ? 'dark-theme' : 'light-theme',\r\n { visible: showDrawerMenu },\r\n ]\"\r\n @mouseenter=\"cancelHideTimer\"\r\n @mouseleave=\"hideDrawerMenu\"\r\n >\r\n <slot name=\"drawer-menu\">\r\n <!-- 菜单头部 -->\r\n <div class=\"drawer-header\">\r\n <div class=\"drawer-title\">\r\n <!-- 纯 CSS 网格图标 -->\r\n <span class=\"grid-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n <span>功能导航</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 菜单内容 - 阿里云风格铺开布局 -->\r\n <div class=\"drawer-content\">\r\n <div class=\"menu-grid\">\r\n <div\r\n v-for=\"category in menus\"\r\n :key=\"category.path\"\r\n class=\"menu-category\"\r\n >\r\n <!-- 分类标题 -->\r\n <div\r\n class=\"category-header\"\r\n role=\"link\"\r\n :tabindex=\"category.disabled ? -1 : 0\"\r\n :aria-disabled=\"category.disabled || undefined\"\r\n @click=\"navigateToPage(category)\"\r\n @keydown.enter=\"navigateToPage(category)\"\r\n @keydown.space.prevent=\"navigateToPage(category)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"category.meta?.icon\"\r\n :name=\"category.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span>{{ category.meta?.title }}</span>\r\n </div>\r\n\r\n <!-- 分类下的菜单项 -->\r\n <div\r\n class=\"category-items\"\r\n v-if=\"category.children && category.children.length > 0\"\r\n >\r\n <div\r\n v-for=\"item in category.children\"\r\n :key=\"item.path\"\r\n class=\"menu-item\"\r\n role=\"link\"\r\n :tabindex=\"item.disabled ? -1 : 0\"\r\n :aria-disabled=\"item.disabled || undefined\"\r\n @click=\"navigateToPage(item)\"\r\n @keydown.enter=\"navigateToPage(item)\"\r\n @keydown.space.prevent=\"navigateToPage(item)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"item-title\">{{ item.meta?.title }}</span>\r\n </div>\r\n\r\n <!-- 3级菜单项 -->\r\n <template\r\n v-for=\"item in category.children\"\r\n :key=\"'sub-' + item.path\"\r\n >\r\n <div\r\n v-if=\"item.children && item.children.length > 0\"\r\n class=\"submenu-items\"\r\n >\r\n <div class=\"submenu-title\">{{ item.meta?.title }}</div>\r\n <div\r\n v-for=\"subItem in item.children\"\r\n :key=\"subItem.path\"\r\n class=\"menu-item submenu-item\"\r\n role=\"link\"\r\n :tabindex=\"subItem.disabled ? -1 : 0\"\r\n :aria-disabled=\"subItem.disabled || undefined\"\r\n @click=\"navigateToPage(subItem)\"\r\n @keydown.enter=\"navigateToPage(subItem)\"\r\n @keydown.space.prevent=\"navigateToPage(subItem)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"subItem.meta?.icon\"\r\n :name=\"subItem.meta.icon\"\r\n :size=\"14\"\r\n />\r\n <span class=\"item-title\">{{ subItem.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 主内容区域 -->\r\n <div class=\"main-content-area\">\r\n <NLayout class=\"content-layout\">\r\n <NLayoutContent class=\"main-content p16px app-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { ref, computed, onUnmounted } from \"vue\";\r\nimport { useRouter } from \"vue-router\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport type { MenuOptions } from \"../../../types/menu\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_CardLayout\" });\n\r\nconst ctx = useLayoutContext();\r\nconst router = useRouter();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\n// 抽屉菜单状态\r\nconst showDrawerMenu = ref(false);\r\nconst hideTimer = ref<ReturnType<typeof setTimeout> | null>(null);\r\n\r\nconst hideDrawerMenu = () => {\r\n if (hideTimer.value) clearTimeout(hideTimer.value);\r\n hideTimer.value = setTimeout(() => {\r\n showDrawerMenu.value = false;\r\n }, 300);\r\n};\r\n\r\nconst cancelHideTimer = () => {\r\n if (hideTimer.value) {\r\n clearTimeout(hideTimer.value);\r\n hideTimer.value = null;\r\n }\r\n showDrawerMenu.value = true;\r\n};\r\n\r\nconst navigateToPage = (item: MenuOptions) => {\r\n if (!item.disabled && item.path) {\r\n void router.push(item.path);\r\n showDrawerMenu.value = false;\r\n }\r\n};\r\n\r\nconst LayoutIcon = useLayoutIcon();\r\n\r\nonUnmounted(() => {\r\n if (hideTimer.value) clearTimeout(hideTimer.value);\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n/* 纯 CSS 网格图标 (2x2) */\r\n.grid-icon {\r\n display: inline-grid;\r\n grid-template-columns: repeat(2, 1fr);\r\n gap: 2.5px;\r\n width: 18px;\r\n height: 18px;\r\n padding: 1px;\r\n}\r\n\r\n.grid-icon span {\r\n background-color: currentColor;\r\n border-radius: 2px;\r\n opacity: 0.85;\r\n}\r\n\r\n/* 纯 CSS 三横线汉堡菜单图标 */\r\n.hamburger-icon {\r\n display: inline-flex;\r\n flex-direction: column;\r\n justify-content: space-between;\r\n width: 20px;\r\n height: 16px;\r\n padding: 2px 0;\r\n}\r\n\r\n.hamburger-icon span {\r\n display: block;\r\n width: 100%;\r\n height: 2px;\r\n background-color: currentColor;\r\n border-radius: 1px;\r\n opacity: 0.85;\r\n transition: all 0.2s ease;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - C_CardLayout\n *\r\n * 卡片式布局骨架\r\n * 顶部导航 + hover 触发抽屉式菜单 + 内容区\r\n *\r\n * Slots:\r\n * #menu-trigger - 菜单触发区域(hover 弹出抽屉)\r\n * #logo - Logo/品牌区域\r\n * #header-extra - 头部右侧操作区\r\n * #tags-view - 标签页区域\r\n * #drawer-menu - 抽屉式菜单内容\r\n * #footer - 页脚区域\r\n -->\r\n<template>\r\n <div class=\"card-layout-container\">\r\n <!-- 顶部导航栏 -->\r\n <div\r\n class=\"top-navbar\"\r\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <div class=\"navbar-left\">\r\n <!-- 悬停触发区域 -->\r\n <div\r\n class=\"hover-trigger-area\"\r\n @mouseenter=\"cancelHideTimer\"\r\n @mouseleave=\"hideDrawerMenu\"\r\n >\r\n <slot name=\"menu-trigger\">\r\n <div class=\"menu-indicator\">\r\n <!-- 纯 CSS 三横线菜单图标 -->\r\n <span class=\"hamburger-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n </div>\r\n </slot>\r\n </div>\r\n\r\n <slot name=\"logo\">\r\n <div class=\"logo-container\">\r\n <div class=\"logo-glow\"></div>\r\n <video\r\n v-if=\"brand.logoType === 'video' && brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"logo-video\"\r\n >\r\n 您的浏览器不支持 video 标签。\r\n </video>\r\n <img\r\n v-else-if=\"brand.logoSrc\"\r\n :src=\"brand.logoSrc\"\r\n :width=\"brand.logoSize || 36\"\r\n :height=\"brand.logoSize || 36\"\r\n class=\"logo-video\"\r\n :alt=\"brand.name || 'Logo'\"\r\n />\r\n </div>\r\n <div class=\"brand-name\">\r\n <span class=\"brand-title\">{{ brand.name }}</span>\r\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\r\n </div>\r\n <div class=\"navbar-divider\"></div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 右侧:操作区 -->\r\n <slot name=\"header-extra\" />\r\n </div>\r\n\r\n <!-- 标签页区域 -->\r\n <div\r\n v-if=\"showTagsView\"\r\n class=\"tags-view-container\"\r\n :style=\"{ height: `${tagsViewHeight}px` }\"\r\n >\r\n <slot name=\"tags-view\" />\r\n </div>\r\n\r\n <!-- 抽屉式菜单 -->\r\n <div\r\n class=\"drawer-menu\"\r\n :class=\"[\r\n isDarkMode ? 'dark-theme' : 'light-theme',\r\n { visible: showDrawerMenu },\r\n ]\"\r\n @mouseenter=\"cancelHideTimer\"\r\n @mouseleave=\"hideDrawerMenu\"\r\n >\r\n <slot name=\"drawer-menu\">\r\n <!-- 菜单头部 -->\r\n <div class=\"drawer-header\">\r\n <div class=\"drawer-title\">\r\n <!-- 纯 CSS 网格图标 -->\r\n <span class=\"grid-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n <span>功能导航</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 菜单内容 - 阿里云风格铺开布局 -->\r\n <div class=\"drawer-content\">\r\n <div class=\"menu-grid\">\r\n <div\r\n v-for=\"category in menus\"\r\n :key=\"category.path\"\r\n class=\"menu-category\"\r\n >\r\n <!-- 分类标题 -->\r\n <div\r\n class=\"category-header\"\r\n role=\"link\"\r\n :tabindex=\"category.disabled ? -1 : 0\"\r\n :aria-disabled=\"category.disabled || undefined\"\r\n @click=\"navigateToPage(category)\"\r\n @keydown.enter=\"navigateToPage(category)\"\r\n @keydown.space.prevent=\"navigateToPage(category)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"category.meta?.icon\"\r\n :name=\"category.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span>{{ category.meta?.title }}</span>\r\n </div>\r\n\r\n <!-- 分类下的菜单项 -->\r\n <div\r\n class=\"category-items\"\r\n v-if=\"category.children && category.children.length > 0\"\r\n >\r\n <div\r\n v-for=\"item in category.children\"\r\n :key=\"item.path\"\r\n class=\"menu-item\"\r\n role=\"link\"\r\n :tabindex=\"item.disabled ? -1 : 0\"\r\n :aria-disabled=\"item.disabled || undefined\"\r\n @click=\"navigateToPage(item)\"\r\n @keydown.enter=\"navigateToPage(item)\"\r\n @keydown.space.prevent=\"navigateToPage(item)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"item-title\">{{ item.meta?.title }}</span>\r\n </div>\r\n\r\n <!-- 3级菜单项 -->\r\n <template\r\n v-for=\"item in category.children\"\r\n :key=\"'sub-' + item.path\"\r\n >\r\n <div\r\n v-if=\"item.children && item.children.length > 0\"\r\n class=\"submenu-items\"\r\n >\r\n <div class=\"submenu-title\">{{ item.meta?.title }}</div>\r\n <div\r\n v-for=\"subItem in item.children\"\r\n :key=\"subItem.path\"\r\n class=\"menu-item submenu-item\"\r\n role=\"link\"\r\n :tabindex=\"subItem.disabled ? -1 : 0\"\r\n :aria-disabled=\"subItem.disabled || undefined\"\r\n @click=\"navigateToPage(subItem)\"\r\n @keydown.enter=\"navigateToPage(subItem)\"\r\n @keydown.space.prevent=\"navigateToPage(subItem)\"\r\n >\r\n <component\r\n :is=\"LayoutIcon\"\r\n v-if=\"subItem.meta?.icon\"\r\n :name=\"subItem.meta.icon\"\r\n :size=\"14\"\r\n />\r\n <span class=\"item-title\">{{ subItem.meta?.title }}</span>\r\n </div>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </slot>\r\n </div>\r\n\r\n <!-- 主内容区域 -->\r\n <div class=\"main-content-area\">\r\n <NLayout class=\"content-layout\">\r\n <NLayoutContent class=\"main-content p16px app-content\">\r\n <div class=\"page-content\">\r\n <LayoutRouterView />\r\n </div>\r\n </NLayoutContent>\r\n\r\n <template v-if=\"showFooter\">\r\n <slot name=\"footer\" />\r\n </template>\r\n </NLayout>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport \"./index.scss\";\r\nimport { ref, computed, onUnmounted } from \"vue\";\r\nimport { useRouter } from \"vue-router\";\r\nimport { NLayout, NLayoutContent } from \"naive-ui\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../../composables/useLayoutContext\";\r\nimport type { MenuOptions } from \"../../../types/menu\";\r\nimport LayoutRouterView from \"../../LayoutRouterView/index.vue\";\r\nimport { useLayoutIcon } from \"../../../composables/useLayoutIcon\";\r\n\r\ndefineOptions({ name: \"C_CardLayout\" });\n\r\nconst ctx = useLayoutContext();\r\nconst router = useRouter();\r\n\r\nconst isDarkMode = ctx.isDark;\r\nconst menus = computed(() => ctx.menus.value);\r\nconst showTagsView = computed(() => ctx.showTagsView.value);\r\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\r\nconst showFooter = computed(() => ctx.showFooter.value);\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n\r\n// 抽屉菜单状态\r\nconst showDrawerMenu = ref(false);\r\nconst hideTimer = ref<ReturnType<typeof setTimeout> | null>(null);\r\n\r\nconst hideDrawerMenu = () => {\r\n if (hideTimer.value) clearTimeout(hideTimer.value);\r\n hideTimer.value = setTimeout(() => {\r\n showDrawerMenu.value = false;\r\n }, 300);\r\n};\r\n\r\nconst cancelHideTimer = () => {\r\n if (hideTimer.value) {\r\n clearTimeout(hideTimer.value);\r\n hideTimer.value = null;\r\n }\r\n showDrawerMenu.value = true;\r\n};\r\n\r\nconst navigateToPage = (item: MenuOptions) => {\r\n if (!item.disabled && item.path) {\r\n void router.push(item.path);\r\n showDrawerMenu.value = false;\r\n }\r\n};\r\n\r\nconst LayoutIcon = useLayoutIcon();\r\n\r\nonUnmounted(() => {\r\n if (hideTimer.value) clearTimeout(hideTimer.value);\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n/* 纯 CSS 网格图标 (2x2) */\r\n.grid-icon {\r\n display: inline-grid;\r\n grid-template-columns: repeat(2, 1fr);\r\n gap: 2.5px;\r\n width: 18px;\r\n height: 18px;\r\n padding: 1px;\r\n}\r\n\r\n.grid-icon span {\r\n background-color: currentColor;\r\n border-radius: 2px;\r\n opacity: 0.85;\r\n}\r\n\r\n/* 纯 CSS 三横线汉堡菜单图标 */\r\n.hamburger-icon {\r\n display: inline-flex;\r\n flex-direction: column;\r\n justify-content: space-between;\r\n width: 20px;\r\n height: 16px;\r\n padding: 2px 0;\r\n}\r\n\r\n.hamburger-icon span {\r\n display: block;\r\n width: 100%;\r\n height: 2px;\r\n background-color: currentColor;\r\n border-radius: 1px;\r\n opacity: 0.85;\r\n transition: all 0.2s ease;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - C_LayoutContainer\r\n *\r\n * 智能布局容器\r\n * 根据 layoutMode 自动切换 6 种布局骨架,消费方仅需提供业务组件\r\n *\r\n * Slots:\r\n * #logo - 品牌 Logo 区域(各骨架有内置默认实现)\r\n * #menu - Side 布局的垂直菜单(需消费方提供)\r\n * #header - Side/Mix 布局的完整头部(需消费方提供)\r\n * #header-extra - Top/MixTop/Reverse/Card 头部右侧操作区\r\n * #tags-view - 标签页区域(需消费方提供)\r\n * #footer - 页脚区域(需消费方提供)\r\n * #top-menu - Top/MixTop/Reverse 水平菜单(骨架有默认)\r\n * #brand - MixTop 顶部导航品牌区(骨架有默认)\r\n * #menu-trigger - Card 菜单触发区域(骨架有默认)\r\n * #drawer-menu - Card 抽屉式菜单(骨架有默认)\r\n -->\r\n<template>\r\n <div\r\n data-ra-layout\r\n :class=\"['layout-container', isDark ? 'dark-mode' : 'light-mode']\"\r\n >\r\n <!-- ═══════ Side 左侧菜单布局 ═══════ -->\r\n <C_SideLayout\n v-if=\"layoutMode === 'side'\"\n v-model:collapsed=\"sideCollapsed\"\n >\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template #menu>\r\n <slot name=\"menu\" :collapsed=\"sideCollapsed\" />\r\n </template>\r\n <template #header><slot name=\"header\" /></template>\r\n <template #footer><slot name=\"footer\" /></template>\r\n </C_SideLayout>\n\r\n <!-- ═══════ Top 顶部菜单布局 ═══════ -->\r\n <C_TopLayout v-else-if=\"layoutMode === 'top'\">\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots['top-menu']\" #menu\r\n ><slot name=\"top-menu\"\r\n /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_TopLayout>\n\r\n <!-- ═══════ Mix 混合布局 ═══════ -->\r\n <C_MixLayout v-else-if=\"layoutMode === 'mix'\">\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template #header><slot name=\"header\" /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_MixLayout>\n\r\n <!-- ═══════ MixTop 顶部混合布局 ═══════ -->\r\n <C_MixTopLayout v-else-if=\"layoutMode === 'mix-top'\">\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots.brand\" #brand><slot name=\"brand\" /></template>\r\n <template v-if=\"$slots['top-menu']\" #top-menu\r\n ><slot name=\"top-menu\"\r\n /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_MixTopLayout>\n\r\n <!-- ═══════ ReverseHorizontalMix 反转混合布局 ═══════ -->\r\n <C_ReverseHorizontalMixLayout\n v-else-if=\"layoutMode === 'reverse-horizontal-mix'\"\r\n >\r\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots['top-menu']\" #top-menu\r\n ><slot name=\"top-menu\"\r\n /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_ReverseHorizontalMixLayout>\n\r\n <!-- ═══════ C_CardLayout 卡片布局 ═══════ -->\n <C_CardLayout v-else-if=\"layoutMode === 'card-layout'\">\n <template v-if=\"$slots['menu-trigger']\" #menu-trigger\r\n ><slot name=\"menu-trigger\"\r\n /></template>\r\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots['drawer-menu']\" #drawer-menu\r\n ><slot name=\"drawer-menu\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_CardLayout>\n\r\n <!-- ═══════ Fallback ═══════ -->\r\n <div v-else class=\"layout-coming-soon\">\r\n <div class=\"coming-soon-content\">\r\n <div class=\"coming-soon-icon\">🚧</div>\r\n <div class=\"coming-soon-title\">布局开发中</div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, ref, provide } from \"vue\";\r\nimport {\r\n MENU_COLLAPSE_KEY,\r\n useLayoutContext,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\n// 骨架布局全局样式\r\nimport \"../../styles/layouts.scss\";\r\n\r\n// 骨架组件\r\nimport C_SideLayout from \"../layouts/SideLayout/index.vue\";\nimport C_TopLayout from \"../layouts/TopLayout/index.vue\";\nimport C_MixLayout from \"../layouts/MixLayout/index.vue\";\nimport C_MixTopLayout from \"../layouts/MixTopLayout/index.vue\";\nimport C_ReverseHorizontalMixLayout from \"../layouts/ReverseHorizontalMixLayout/index.vue\";\nimport C_CardLayout from \"../layouts/CardLayout/index.vue\";\n\r\ndefineOptions({ name: \"C_LayoutContainer\" });\r\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst layoutMode = ctx.layoutMode;\r\nconst isDark = ctx.isDark;\r\n\r\n// 上下文提供 collapsed 时与宿主共享;否则保留内部状态以兼容旧接入。\r\nconst internalCollapsed = ref(false);\r\nconst sideCollapsed = computed({\r\n get: () => ctx.collapsed?.value ?? internalCollapsed.value,\r\n set: (value: boolean) => {\r\n if (ctx.collapsed) ctx.collapsed.value = value;\r\n else internalCollapsed.value = value;\r\n },\r\n});\r\n\r\n// 提供折叠状态给子组件(如 C_Header 的汉堡按钮)\r\nconst collapseHandlers = {\r\n isCollapsed: sideCollapsed,\r\n handleCollapsedChange: (collapsed: boolean) => {\r\n sideCollapsed.value = collapsed;\r\n },\r\n};\r\nprovide(MENU_COLLAPSE_KEY, collapseHandlers);\r\n// 3.x 兼容层:旧消费方仍可使用字符串 key,计划在 4.0 移除。\r\nprovide(\"menuCollapse\", collapseHandlers);\r\n</script>\r\n","<!--\r\n * @robot-admin/layout - C_LayoutContainer\r\n *\r\n * 智能布局容器\r\n * 根据 layoutMode 自动切换 6 种布局骨架,消费方仅需提供业务组件\r\n *\r\n * Slots:\r\n * #logo - 品牌 Logo 区域(各骨架有内置默认实现)\r\n * #menu - Side 布局的垂直菜单(需消费方提供)\r\n * #header - Side/Mix 布局的完整头部(需消费方提供)\r\n * #header-extra - Top/MixTop/Reverse/Card 头部右侧操作区\r\n * #tags-view - 标签页区域(需消费方提供)\r\n * #footer - 页脚区域(需消费方提供)\r\n * #top-menu - Top/MixTop/Reverse 水平菜单(骨架有默认)\r\n * #brand - MixTop 顶部导航品牌区(骨架有默认)\r\n * #menu-trigger - Card 菜单触发区域(骨架有默认)\r\n * #drawer-menu - Card 抽屉式菜单(骨架有默认)\r\n -->\r\n<template>\r\n <div\r\n data-ra-layout\r\n :class=\"['layout-container', isDark ? 'dark-mode' : 'light-mode']\"\r\n >\r\n <!-- ═══════ Side 左侧菜单布局 ═══════ -->\r\n <C_SideLayout\n v-if=\"layoutMode === 'side'\"\n v-model:collapsed=\"sideCollapsed\"\n >\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template #menu>\r\n <slot name=\"menu\" :collapsed=\"sideCollapsed\" />\r\n </template>\r\n <template #header><slot name=\"header\" /></template>\r\n <template #footer><slot name=\"footer\" /></template>\r\n </C_SideLayout>\n\r\n <!-- ═══════ Top 顶部菜单布局 ═══════ -->\r\n <C_TopLayout v-else-if=\"layoutMode === 'top'\">\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots['top-menu']\" #menu\r\n ><slot name=\"top-menu\"\r\n /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_TopLayout>\n\r\n <!-- ═══════ Mix 混合布局 ═══════ -->\r\n <C_MixLayout v-else-if=\"layoutMode === 'mix'\">\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template #header><slot name=\"header\" /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_MixLayout>\n\r\n <!-- ═══════ MixTop 顶部混合布局 ═══════ -->\r\n <C_MixTopLayout v-else-if=\"layoutMode === 'mix-top'\">\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots.brand\" #brand><slot name=\"brand\" /></template>\r\n <template v-if=\"$slots['top-menu']\" #top-menu\r\n ><slot name=\"top-menu\"\r\n /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_MixTopLayout>\n\r\n <!-- ═══════ ReverseHorizontalMix 反转混合布局 ═══════ -->\r\n <C_ReverseHorizontalMixLayout\n v-else-if=\"layoutMode === 'reverse-horizontal-mix'\"\r\n >\r\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots['top-menu']\" #top-menu\r\n ><slot name=\"top-menu\"\r\n /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_ReverseHorizontalMixLayout>\n\r\n <!-- ═══════ C_CardLayout 卡片布局 ═══════ -->\n <C_CardLayout v-else-if=\"layoutMode === 'card-layout'\">\n <template v-if=\"$slots['menu-trigger']\" #menu-trigger\r\n ><slot name=\"menu-trigger\"\r\n /></template>\r\n <template v-if=\"$slots.logo\" #logo><slot name=\"logo\" /></template>\r\n <template v-if=\"$slots['header-extra']\" #header-extra\r\n ><slot name=\"header-extra\"\r\n /></template>\r\n <template v-if=\"$slots['tags-view']\" #tags-view\r\n ><slot name=\"tags-view\"\r\n /></template>\r\n <template v-if=\"$slots['drawer-menu']\" #drawer-menu\r\n ><slot name=\"drawer-menu\"\r\n /></template>\r\n <template v-if=\"$slots.footer\" #footer><slot name=\"footer\" /></template>\r\n </C_CardLayout>\n\r\n <!-- ═══════ Fallback ═══════ -->\r\n <div v-else class=\"layout-coming-soon\">\r\n <div class=\"coming-soon-content\">\r\n <div class=\"coming-soon-icon\">🚧</div>\r\n <div class=\"coming-soon-title\">布局开发中</div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, ref, provide } from \"vue\";\r\nimport {\r\n MENU_COLLAPSE_KEY,\r\n useLayoutContext,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\n// 骨架布局全局样式\r\nimport \"../../styles/layouts.scss\";\r\n\r\n// 骨架组件\r\nimport C_SideLayout from \"../layouts/SideLayout/index.vue\";\nimport C_TopLayout from \"../layouts/TopLayout/index.vue\";\nimport C_MixLayout from \"../layouts/MixLayout/index.vue\";\nimport C_MixTopLayout from \"../layouts/MixTopLayout/index.vue\";\nimport C_ReverseHorizontalMixLayout from \"../layouts/ReverseHorizontalMixLayout/index.vue\";\nimport C_CardLayout from \"../layouts/CardLayout/index.vue\";\n\r\ndefineOptions({ name: \"C_LayoutContainer\" });\r\n\r\nconst ctx = useLayoutContext();\r\n\r\nconst layoutMode = ctx.layoutMode;\r\nconst isDark = ctx.isDark;\r\n\r\n// 上下文提供 collapsed 时与宿主共享;否则保留内部状态以兼容旧接入。\r\nconst internalCollapsed = ref(false);\r\nconst sideCollapsed = computed({\r\n get: () => ctx.collapsed?.value ?? internalCollapsed.value,\r\n set: (value: boolean) => {\r\n if (ctx.collapsed) ctx.collapsed.value = value;\r\n else internalCollapsed.value = value;\r\n },\r\n});\r\n\r\n// 提供折叠状态给子组件(如 C_Header 的汉堡按钮)\r\nconst collapseHandlers = {\r\n isCollapsed: sideCollapsed,\r\n handleCollapsedChange: (collapsed: boolean) => {\r\n sideCollapsed.value = collapsed;\r\n },\r\n};\r\nprovide(MENU_COLLAPSE_KEY, collapseHandlers);\r\n// 3.x 兼容层:旧消费方仍可使用字符串 key,计划在 4.0 移除。\r\nprovide(\"menuCollapse\", collapseHandlers);\r\n</script>\r\n","/**\n * SettingsDrawer - 常量数据\n */\n\nimport { THEME_PRESETS, COLOR_SWATCHES } from \"../../constants\";\nimport type { LayoutMode, ThemePreset } from \"../../types\";\n\n// 重新导出(内部使用)\nexport { COLOR_SWATCHES, THEME_PRESETS };\nexport type { ThemePreset };\n\n// 布局模式选项 - 完整的 6 种布局(含 SVG 图标,仅 UI 内部使用)\nexport const LAYOUT_MODE_OPTIONS = [\n {\n label: \"左侧菜单(默认)\",\n value: \"side\",\n disabled: false,\n svg: `\n <rect x=\"0\" y=\"0\" width=\"16\" height=\"48\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.9\"/>\n <rect x=\"18\" y=\"0\" width=\"38\" height=\"10\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.7\"/>\n <rect x=\"18\" y=\"12\" width=\"38\" height=\"34\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.4\"/>\n `,\n },\n {\n label: \"顶部菜单(清爽)\",\n value: \"top\",\n disabled: false,\n svg: `\n <rect x=\"0\" y=\"0\" width=\"56\" height=\"10\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.9\"/>\n <rect x=\"0\" y=\"12\" width=\"56\" height=\"8\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.7\"/>\n <rect x=\"0\" y=\"22\" width=\"56\" height=\"24\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.4\"/>\n `,\n },\n {\n label: \"左侧混合(灵巧)\",\n value: \"mix\",\n disabled: false,\n svg: `\n <rect x=\"0\" y=\"0\" width=\"8\" height=\"48\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.9\"/>\n <rect x=\"10\" y=\"0\" width=\"14\" height=\"48\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.7\"/>\n <rect x=\"26\" y=\"0\" width=\"30\" height=\"10\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.6\"/>\n <rect x=\"26\" y=\"12\" width=\"30\" height=\"34\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.4\"/>\n `,\n },\n {\n label: \"顶部混合(侧优)\",\n value: \"mix-top\",\n disabled: false,\n svg: `\n <rect x=\"0\" y=\"0\" width=\"8\" height=\"48\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.9\"/>\n <rect x=\"10\" y=\"0\" width=\"46\" height=\"10\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.7\"/>\n <rect x=\"10\" y=\"12\" width=\"46\" height=\"34\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.4\"/>\n `,\n },\n {\n label: \"反转混合(另类)\",\n value: \"reverse-horizontal-mix\",\n disabled: false,\n svg: `\n <rect x=\"0\" y=\"0\" width=\"56\" height=\"10\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.9\"/>\n <rect x=\"44\" y=\"12\" width=\"12\" height=\"34\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.7\"/>\n <rect x=\"0\" y=\"12\" width=\"42\" height=\"34\" rx=\"1\" fill=\"currentColor\" fill-opacity=\"0.4\"/>\n `,\n },\n {\n label: \"卡片网格(新颖)\",\n value: \"card-layout\",\n disabled: false,\n svg: `\n <rect x=\"0\" y=\"0\" width=\"24\" height=\"20\" rx=\"2\" fill=\"currentColor\" fill-opacity=\"0.9\"/>\n <rect x=\"32\" y=\"0\" width=\"24\" height=\"20\" rx=\"2\" fill=\"currentColor\" fill-opacity=\"0.7\"/>\n <rect x=\"0\" y=\"24\" width=\"24\" height=\"20\" rx=\"2\" fill=\"currentColor\" fill-opacity=\"0.6\"/>\n <rect x=\"32\" y=\"24\" width=\"24\" height=\"20\" rx=\"2\" fill=\"currentColor\" fill-opacity=\"0.4\"/>\n `,\n },\n] satisfies Array<{\n label: string;\n value: LayoutMode;\n disabled: boolean;\n svg: string;\n}>;\n","<!--\r\n * @robot-admin/layout - SettingsDrawer\r\n *\r\n * 布局配置抽屉\r\n * 包含外观/布局/功能三大模块,支持主题预设、布局切换、配置导入导出等\r\n -->\r\n<template>\r\n <NDrawer\r\n v-model:show=\"visible\"\r\n :width=\"width\"\r\n placement=\"right\"\r\n :trap-focus=\"true\"\r\n :block-scroll=\"true\"\r\n >\r\n <NDrawerContent title=\"⚙️ 布局配置\" closable>\r\n <NTabs v-model:value=\"activeTab\" animated class=\"settings-tabs\">\r\n <!-- 外观 Tab -->\r\n <NTabPane name=\"appearance\" tab=\"🎨 外观\">\r\n <slot name=\"appearance-prepend\" :settings=\"settingsStore\" />\r\n <!-- 预设方案网格 -->\r\n <div class=\"settings-section\">\r\n <div class=\"preset-grid\">\r\n <div\r\n v-for=\"preset in THEME_PRESETS\"\r\n :key=\"preset.name\"\r\n class=\"preset-card\"\r\n :class=\"{ active: isCurrentPreset(preset) }\"\r\n role=\"button\"\r\n tabindex=\"0\"\r\n :aria-pressed=\"isCurrentPreset(preset)\"\r\n @click=\"handleApplyPreset(preset)\"\r\n @keydown.enter=\"handleApplyPreset(preset)\"\r\n @keydown.space.prevent=\"handleApplyPreset(preset)\"\r\n >\r\n <div class=\"preset-icon\">{{ preset.icon }}</div>\r\n <div class=\"preset-name\">{{ preset.name }}</div>\r\n <div class=\"preset-color\">\r\n <span\r\n class=\"color-dot\"\r\n :style=\"{ backgroundColor: preset.primaryColor }\"\r\n ></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主题模式 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">主题模式</div>\r\n <NRadioGroup\r\n :value=\"settingsStore.themeMode\"\r\n class=\"mode-group\"\r\n @update:value=\"handleThemeModeChange\"\r\n >\r\n <NRadioButton value=\"light\">\r\n <span class=\"i-mdi:white-balance-sunny mr-1\"></span>\r\n 浅色\r\n </NRadioButton>\r\n <NRadioButton value=\"dark\">\r\n <span class=\"i-mdi:moon-waning-crescent mr-1\"></span>\r\n 深色\r\n </NRadioButton>\r\n <NRadioButton value=\"system\">\r\n <span class=\"i-mdi:theme-light-dark mr-1\"></span>\r\n 自动\r\n </NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 主题色 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">主题色</div>\r\n <div class=\"color-picker-wrapper\">\r\n <NColorPicker\r\n v-model:value=\"settingsStore.primaryColor\"\r\n :show-alpha=\"false\"\r\n :swatches=\"COLOR_SWATCHES\"\r\n :actions=\"['confirm']\"\r\n />\r\n <span class=\"color-value\">{{ settingsStore.primaryColor }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 圆角大小 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">圆角大小</div>\r\n <NRadioGroup\r\n v-model:value=\"settingsStore.borderRadius\"\r\n class=\"radius-group\"\r\n >\r\n <NRadioButton value=\"small\">小 (4px)</NRadioButton>\r\n <NRadioButton value=\"medium\">中 (6px)</NRadioButton>\r\n <NRadioButton value=\"large\">大 (8px)</NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 页面动画 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">页面动画</div>\r\n <div class=\"flex items-center justify-between mb-2\">\r\n <span class=\"text-sm\">启用动画</span>\r\n <NSwitch v-model:value=\"settingsStore.enableTransition\" />\r\n </div>\r\n <NRadioGroup\r\n v-model:value=\"settingsStore.transitionType\"\r\n :disabled=\"!settingsStore.enableTransition\"\r\n class=\"transition-group\"\r\n >\r\n <NRadioButton value=\"fade\">淡入</NRadioButton>\r\n <NRadioButton value=\"slide\">滑动</NRadioButton>\r\n <NRadioButton value=\"zoom\">缩放</NRadioButton>\r\n <NRadioButton value=\"none\">无</NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 恢复默认 -->\r\n <div class=\"settings-section\">\r\n <NButton block secondary @click=\"handleResetAppearance\">\r\n <template #icon>\r\n <span class=\"i-mdi:restore\"></span>\r\n </template>\r\n 恢复外观默认设置\r\n </NButton>\r\n </div>\r\n <slot name=\"appearance-append\" :settings=\"settingsStore\" />\r\n </NTabPane>\r\n\r\n <!-- 布局 Tab -->\r\n <NTabPane name=\"layout\" tab=\"📐 布局\">\r\n <slot name=\"layout-prepend\" :settings=\"settingsStore\" />\r\n <!-- 布局模式 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">布局模式</div>\r\n <div class=\"layout-grid\">\r\n <div\r\n v-for=\"mode in LAYOUT_MODE_OPTIONS\"\r\n :key=\"mode.value\"\r\n class=\"layout-item\"\r\n :class=\"{\r\n active: settingsStore.layoutMode === mode.value,\r\n disabled: mode.disabled,\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"mode.disabled ? -1 : 0\"\r\n :aria-disabled=\"mode.disabled\"\r\n :aria-pressed=\"settingsStore.layoutMode === mode.value\"\r\n @click.stop=\"handleLayoutChange(mode.value, mode.disabled)\"\r\n @keydown.enter.stop=\"\r\n handleLayoutChange(mode.value, mode.disabled)\r\n \"\r\n @keydown.space.stop.prevent=\"\r\n handleLayoutChange(mode.value, mode.disabled)\r\n \"\r\n >\r\n <div class=\"layout-screenshot\">\r\n <svg\r\n class=\"layout-svg\"\r\n viewBox=\"0 0 56 48\"\r\n fill=\"none\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <g v-html=\"mode.svg\"></g>\r\n </svg>\r\n </div>\r\n <div class=\"layout-label\">\r\n {{ mode.label }}\r\n <span v-if=\"mode.disabled\" class=\"badge-soon\">敬请期待</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <slot name=\"layout-after-mode\" :settings=\"settingsStore\" />\r\n\r\n <div\r\n class=\"settings-section menu-expand-section\"\r\n data-menu-expand-mode=\"built-in\"\r\n >\r\n <div class=\"section-title\">菜单展开方式</div>\r\n <NRadioGroup\r\n v-model:value=\"settingsStore.menuExpandMode\"\r\n class=\"menu-expand-group\"\r\n >\r\n <NRadioButton value=\"inline\">传统展开</NRadioButton>\r\n <NRadioButton value=\"panel\">右侧面板</NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 界面元素 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">界面元素</div>\r\n <div class=\"setting-item\">\r\n <span>显示面包屑</span>\r\n <NSwitch v-model:value=\"settingsStore.showBreadcrumb\" />\r\n </div>\r\n <div class=\"setting-item\">\r\n <span>显示面包屑图标</span>\r\n <NSwitch\r\n v-model:value=\"settingsStore.showBreadcrumbIcon\"\r\n :disabled=\"!settingsStore.showBreadcrumb\"\r\n />\r\n </div>\r\n <div class=\"setting-item\">\r\n <span>显示标签页</span>\r\n <NSwitch v-model:value=\"settingsStore.showTagsView\" />\r\n </div>\r\n <div class=\"setting-item\">\r\n <span>显示页脚</span>\r\n <NSwitch v-model:value=\"settingsStore.showFooter\" />\r\n </div>\r\n </div>\r\n\r\n <!-- 尺寸调整 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">尺寸调整</div>\r\n <div class=\"slider-item\">\r\n <div class=\"slider-label\">\r\n <span>侧边栏宽度</span>\r\n <span class=\"slider-value\"\r\n >{{ settingsStore.sidebarWidth }}px</span\r\n >\r\n </div>\r\n <NSlider\r\n v-model:value=\"settingsStore.sidebarWidth\"\r\n :min=\"180\"\r\n :max=\"280\"\r\n :step=\"10\"\r\n :marks=\"{ 180: '180', 220: '220', 280: '280' }\"\r\n />\r\n </div>\r\n <div class=\"slider-item\">\r\n <div class=\"slider-label\">\r\n <span>头部高度</span>\r\n <span class=\"slider-value\"\r\n >{{ settingsStore.headerHeight }}px</span\r\n >\r\n </div>\r\n <NSlider\r\n v-model:value=\"settingsStore.headerHeight\"\r\n :min=\"48\"\r\n :max=\"64\"\r\n :step=\"4\"\r\n :marks=\"{ 48: '48', 56: '56', 64: '64' }\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 恢复默认 -->\r\n <div class=\"settings-section\">\r\n <NButton block secondary @click=\"handleResetLayout\">\r\n <template #icon>\r\n <span class=\"i-mdi:restore\"></span>\r\n </template>\r\n 恢复布局默认设置\r\n </NButton>\r\n </div>\r\n <slot name=\"layout-append\" :settings=\"settingsStore\" />\r\n </NTabPane>\r\n\r\n <!-- 功能 Tab -->\r\n <NTabPane name=\"features\" tab=\"🔧 功能\">\r\n <slot name=\"features-prepend\" :settings=\"settingsStore\" />\r\n <!-- 缓存管理 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">缓存管理</div>\r\n <div class=\"action-buttons-grid\">\r\n <NButton secondary @click=\"handleClearCache\">\r\n <template #icon>\r\n <span class=\"i-mdi:delete-sweep\"></span>\r\n </template>\r\n 清除缓存\r\n </NButton>\r\n <NButton secondary @click=\"handleReload\">\r\n <template #icon>\r\n <span class=\"i-mdi:refresh\"></span>\r\n </template>\r\n 重新加载\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- 配置管理 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">配置管理</div>\r\n <div class=\"action-buttons-grid\">\r\n <NButton secondary @click=\"handleExportConfig\">\r\n <template #icon>\r\n <span class=\"i-mdi:download\"></span>\r\n </template>\r\n 导出配置\r\n </NButton>\r\n <NButton secondary @click=\"handleImportConfig\">\r\n <template #icon>\r\n <span class=\"i-mdi:upload\"></span>\r\n </template>\r\n 导入配置\r\n </NButton>\r\n </div>\r\n <NButton\r\n block\r\n type=\"error\"\r\n secondary\r\n class=\"mt-2\"\r\n @click=\"handleReset\"\r\n >\r\n <template #icon>\r\n <span class=\"i-mdi:restore\"></span>\r\n </template>\r\n 重置所有配置\r\n </NButton>\r\n </div>\r\n\r\n <!-- 系统模式 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">系统模式</div>\r\n <div class=\"setting-item\">\r\n <div class=\"setting-label\">\r\n <span>灰色模式</span>\r\n <span class=\"setting-desc\">用于哀悼日场景</span>\r\n </div>\r\n <NSwitch v-model:value=\"grayMode\" />\r\n </div>\r\n <div class=\"setting-item\">\r\n <div class=\"setting-label\">\r\n <span>色弱模式</span>\r\n <span class=\"setting-desc\">提供无障碍访问支持</span>\r\n </div>\r\n <NSwitch v-model:value=\"colorWeakMode\" />\r\n </div>\r\n </div>\r\n\r\n <!-- 水印设置 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">水印设置</div>\r\n <div class=\"setting-item\">\r\n <span>启用水印</span>\r\n <NSwitch v-model:value=\"watermarkEnabled\" />\r\n </div>\r\n <div v-if=\"watermarkEnabled\" class=\"mt-2\">\r\n <NInput\r\n v-model:value=\"watermarkText\"\r\n placeholder=\"请输入水印内容\"\r\n size=\"small\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 系统信息 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">系统信息</div>\r\n <div class=\"system-info\">\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">浏览器</span>\r\n <span class=\"info-value\">{{ systemInfo.browser }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">操作系统</span>\r\n <span class=\"info-value\">{{ systemInfo.os }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">屏幕分辨率</span>\r\n <span class=\"info-value\">{{ systemInfo.resolution }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">设备像素比</span>\r\n <span class=\"info-value\">{{ systemInfo.pixelRatio }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">语言</span>\r\n <span class=\"info-value\">{{ systemInfo.language }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">时区</span>\r\n <span class=\"info-value\">{{ systemInfo.timezone }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <slot name=\"features-append\" :settings=\"settingsStore\" />\r\n </NTabPane>\r\n </NTabs>\r\n </NDrawerContent>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, inject } from \"vue\";\r\nimport {\r\n NDrawer,\r\n NDrawerContent,\r\n NTabs,\r\n NTabPane,\r\n NRadioGroup,\r\n NRadioButton,\r\n NColorPicker,\r\n NSwitch,\r\n NButton,\r\n NSlider,\r\n NInput,\r\n useMessage,\r\n useDialog,\r\n} from \"naive-ui\";\r\nimport {\r\n useSettingsStore,\r\n type SettingsStoreInstance,\r\n} from \"../../stores/settings\";\r\nimport {\r\n LayoutActionUnavailableError,\r\n useSettingsController,\r\n} from \"../../composables/useSettingsController\";\r\nimport { LAYOUT_SETTINGS_KEY } from \"../../composables/useLayoutContext\";\r\nimport { COLOR_SWATCHES, LAYOUT_MODE_OPTIONS, THEME_PRESETS } from \"./data\";\r\nimport type {\r\n SettingsDrawerActions,\r\n LayoutMode,\r\n ThemeMode,\r\n ThemePreset,\r\n} from \"../../types\";\r\n\r\n// ============ 数据定义 ============\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 抽屉宽度,默认 380px */\r\n width?: number;\r\n /** 显式设置 Store;优先级高于 setupLayout 注入和默认 Store */\r\n store?: SettingsStoreInstance;\r\n /** 由宿主实现的缓存清理、页面刷新等高副作用操作 */\r\n actions?: SettingsDrawerActions;\r\n /** 导入文件最大字节数,默认 1 MiB */\r\n maxImportBytes?: number;\r\n }>(),\r\n {\r\n width: 380,\r\n maxImportBytes: 1024 * 1024,\r\n },\r\n);\r\n\r\nconst message = useMessage();\r\nconst dialog = useDialog();\r\nconst injectedSettingsStore = inject(LAYOUT_SETTINGS_KEY, null);\r\nconst settingsStore =\r\n props.store ?? injectedSettingsStore ?? useSettingsStore();\r\nconst visible = defineModel<boolean>(\"show\", { default: false });\r\nconst activeTab = ref(\"appearance\");\r\nconst settingsController = useSettingsController({\r\n store: settingsStore,\r\n actions: props.actions,\r\n maxImportBytes: props.maxImportBytes,\r\n onVisualEffectError: (text) => message.error(text),\r\n});\r\nconst { colorWeakMode, grayMode, systemInfo, watermarkEnabled, watermarkText } =\r\n settingsController;\r\n\r\n// 处理布局切换 - 阻止抽屉关闭\r\nconst handleLayoutChange = (value: LayoutMode, disabled?: boolean) => {\r\n if (disabled) return;\r\n settingsStore.layoutMode = value;\r\n};\r\n\r\n// ============ 方法 ============\r\n\r\n/**\r\n * 判断是否是当前预设\r\n */\r\nconst isCurrentPreset = (preset: ThemePreset) => {\r\n return settingsStore.primaryColor === preset.primaryColor;\r\n};\r\n\r\n/**\r\n * 应用预设方案\r\n */\r\nconst handleApplyPreset = (preset: ThemePreset) => {\r\n void settingsController.applyPreset(preset);\r\n message.success(`已应用「${preset.name}」主题方案`);\r\n};\r\n\r\nconst handleThemeModeChange = async (mode: ThemeMode) => {\r\n try {\r\n await settingsController.updateThemeMode(mode);\r\n } catch {\r\n message.error(\"主题模式切换失败\");\r\n }\r\n};\r\n\r\n/**\r\n * 恢复外观默认设置\r\n */\r\nconst handleResetAppearance = async () => {\r\n try {\r\n await settingsController.resetAppearance();\r\n message.success(\"已恢复外观默认设置\");\r\n } catch {\r\n message.error(\"恢复外观默认设置失败\");\r\n }\r\n};\r\n\r\n/**\r\n * 恢复布局默认设置\r\n */\r\nconst handleResetLayout = () => {\r\n settingsController.resetLayout();\r\n message.success(\"已恢复布局默认设置\");\r\n};\r\n\r\n/**\r\n * 重置配置\r\n */\r\nconst handleReset = () => {\r\n dialog.warning({\r\n title: \"确认重置\",\r\n content: \"确定要恢复默认配置吗?此操作不可撤销。\",\r\n positiveText: \"确认\",\r\n negativeText: \"取消\",\r\n onPositiveClick: async () => {\r\n try {\r\n await settingsController.resetAll();\r\n message.success(\"已恢复默认配置\");\r\n } catch {\r\n message.error(\"恢复默认配置失败\");\r\n }\r\n },\r\n });\r\n};\r\n\r\n/**\r\n * 清除缓存\r\n */\r\nconst handleClearCache = () => {\r\n dialog.warning({\r\n title: \"确认清除\",\r\n content: \"确定要清除浏览器缓存吗?\",\r\n positiveText: \"确认\",\r\n negativeText: \"取消\",\r\n onPositiveClick: async () => {\r\n try {\r\n await settingsController.clearCache();\r\n message.success(\"缓存已清除\");\r\n } catch (error) {\r\n if (error instanceof LayoutActionUnavailableError) {\r\n message.warning(\"宿主应用未配置可安全清理的缓存\");\r\n return;\r\n }\r\n message.error(\"缓存清除失败\");\r\n }\r\n },\r\n });\r\n};\r\n\r\n/**\r\n * 重新加载页面\r\n */\r\nconst handleReload = () => {\r\n settingsController.reloadPage();\r\n};\r\n\r\n/**\r\n * 导出配置\r\n */\r\nconst handleExportConfig = () => {\r\n if (settingsController.downloadConfig()) message.success(\"配置已导出\");\r\n else message.error(\"当前环境不支持配置导出\");\r\n};\r\n\r\n/**\r\n * 导入配置\r\n */\r\nconst handleImportConfig = () => {\r\n if (typeof document === \"undefined\") return;\r\n const input = document.createElement(\"input\");\r\n input.type = \"file\";\r\n input.accept = \"application/json\";\r\n input.onchange = async (e: Event) => {\r\n const file = (e.target as HTMLInputElement).files?.[0];\r\n if (!file) return;\r\n\r\n try {\r\n await settingsController.importFile(file);\r\n message.success(\"配置已导入\");\r\n } catch (error) {\r\n message.error(\"配置文件格式错误\");\r\n }\r\n };\r\n input.click();\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n@use \"../../styles/settings.scss\" as *;\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - SettingsDrawer\r\n *\r\n * 布局配置抽屉\r\n * 包含外观/布局/功能三大模块,支持主题预设、布局切换、配置导入导出等\r\n -->\r\n<template>\r\n <NDrawer\r\n v-model:show=\"visible\"\r\n :width=\"width\"\r\n placement=\"right\"\r\n :trap-focus=\"true\"\r\n :block-scroll=\"true\"\r\n >\r\n <NDrawerContent title=\"⚙️ 布局配置\" closable>\r\n <NTabs v-model:value=\"activeTab\" animated class=\"settings-tabs\">\r\n <!-- 外观 Tab -->\r\n <NTabPane name=\"appearance\" tab=\"🎨 外观\">\r\n <slot name=\"appearance-prepend\" :settings=\"settingsStore\" />\r\n <!-- 预设方案网格 -->\r\n <div class=\"settings-section\">\r\n <div class=\"preset-grid\">\r\n <div\r\n v-for=\"preset in THEME_PRESETS\"\r\n :key=\"preset.name\"\r\n class=\"preset-card\"\r\n :class=\"{ active: isCurrentPreset(preset) }\"\r\n role=\"button\"\r\n tabindex=\"0\"\r\n :aria-pressed=\"isCurrentPreset(preset)\"\r\n @click=\"handleApplyPreset(preset)\"\r\n @keydown.enter=\"handleApplyPreset(preset)\"\r\n @keydown.space.prevent=\"handleApplyPreset(preset)\"\r\n >\r\n <div class=\"preset-icon\">{{ preset.icon }}</div>\r\n <div class=\"preset-name\">{{ preset.name }}</div>\r\n <div class=\"preset-color\">\r\n <span\r\n class=\"color-dot\"\r\n :style=\"{ backgroundColor: preset.primaryColor }\"\r\n ></span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- 主题模式 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">主题模式</div>\r\n <NRadioGroup\r\n :value=\"settingsStore.themeMode\"\r\n class=\"mode-group\"\r\n @update:value=\"handleThemeModeChange\"\r\n >\r\n <NRadioButton value=\"light\">\r\n <span class=\"i-mdi:white-balance-sunny mr-1\"></span>\r\n 浅色\r\n </NRadioButton>\r\n <NRadioButton value=\"dark\">\r\n <span class=\"i-mdi:moon-waning-crescent mr-1\"></span>\r\n 深色\r\n </NRadioButton>\r\n <NRadioButton value=\"system\">\r\n <span class=\"i-mdi:theme-light-dark mr-1\"></span>\r\n 自动\r\n </NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 主题色 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">主题色</div>\r\n <div class=\"color-picker-wrapper\">\r\n <NColorPicker\r\n v-model:value=\"settingsStore.primaryColor\"\r\n :show-alpha=\"false\"\r\n :swatches=\"COLOR_SWATCHES\"\r\n :actions=\"['confirm']\"\r\n />\r\n <span class=\"color-value\">{{ settingsStore.primaryColor }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 圆角大小 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">圆角大小</div>\r\n <NRadioGroup\r\n v-model:value=\"settingsStore.borderRadius\"\r\n class=\"radius-group\"\r\n >\r\n <NRadioButton value=\"small\">小 (4px)</NRadioButton>\r\n <NRadioButton value=\"medium\">中 (6px)</NRadioButton>\r\n <NRadioButton value=\"large\">大 (8px)</NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 页面动画 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">页面动画</div>\r\n <div class=\"flex items-center justify-between mb-2\">\r\n <span class=\"text-sm\">启用动画</span>\r\n <NSwitch v-model:value=\"settingsStore.enableTransition\" />\r\n </div>\r\n <NRadioGroup\r\n v-model:value=\"settingsStore.transitionType\"\r\n :disabled=\"!settingsStore.enableTransition\"\r\n class=\"transition-group\"\r\n >\r\n <NRadioButton value=\"fade\">淡入</NRadioButton>\r\n <NRadioButton value=\"slide\">滑动</NRadioButton>\r\n <NRadioButton value=\"zoom\">缩放</NRadioButton>\r\n <NRadioButton value=\"none\">无</NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 恢复默认 -->\r\n <div class=\"settings-section\">\r\n <NButton block secondary @click=\"handleResetAppearance\">\r\n <template #icon>\r\n <span class=\"i-mdi:restore\"></span>\r\n </template>\r\n 恢复外观默认设置\r\n </NButton>\r\n </div>\r\n <slot name=\"appearance-append\" :settings=\"settingsStore\" />\r\n </NTabPane>\r\n\r\n <!-- 布局 Tab -->\r\n <NTabPane name=\"layout\" tab=\"📐 布局\">\r\n <slot name=\"layout-prepend\" :settings=\"settingsStore\" />\r\n <!-- 布局模式 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">布局模式</div>\r\n <div class=\"layout-grid\">\r\n <div\r\n v-for=\"mode in LAYOUT_MODE_OPTIONS\"\r\n :key=\"mode.value\"\r\n class=\"layout-item\"\r\n :class=\"{\r\n active: settingsStore.layoutMode === mode.value,\r\n disabled: mode.disabled,\r\n }\"\r\n role=\"button\"\r\n :tabindex=\"mode.disabled ? -1 : 0\"\r\n :aria-disabled=\"mode.disabled\"\r\n :aria-pressed=\"settingsStore.layoutMode === mode.value\"\r\n @click.stop=\"handleLayoutChange(mode.value, mode.disabled)\"\r\n @keydown.enter.stop=\"\r\n handleLayoutChange(mode.value, mode.disabled)\r\n \"\r\n @keydown.space.stop.prevent=\"\r\n handleLayoutChange(mode.value, mode.disabled)\r\n \"\r\n >\r\n <div class=\"layout-screenshot\">\r\n <svg\r\n class=\"layout-svg\"\r\n viewBox=\"0 0 56 48\"\r\n fill=\"none\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <g v-html=\"mode.svg\"></g>\r\n </svg>\r\n </div>\r\n <div class=\"layout-label\">\r\n {{ mode.label }}\r\n <span v-if=\"mode.disabled\" class=\"badge-soon\">敬请期待</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <slot name=\"layout-after-mode\" :settings=\"settingsStore\" />\r\n\r\n <div\r\n class=\"settings-section menu-expand-section\"\r\n data-menu-expand-mode=\"built-in\"\r\n >\r\n <div class=\"section-title\">菜单展开方式</div>\r\n <NRadioGroup\r\n v-model:value=\"settingsStore.menuExpandMode\"\r\n class=\"menu-expand-group\"\r\n >\r\n <NRadioButton value=\"inline\">传统展开</NRadioButton>\r\n <NRadioButton value=\"panel\">右侧面板</NRadioButton>\r\n </NRadioGroup>\r\n </div>\r\n\r\n <!-- 界面元素 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">界面元素</div>\r\n <div class=\"setting-item\">\r\n <span>显示面包屑</span>\r\n <NSwitch v-model:value=\"settingsStore.showBreadcrumb\" />\r\n </div>\r\n <div class=\"setting-item\">\r\n <span>显示面包屑图标</span>\r\n <NSwitch\r\n v-model:value=\"settingsStore.showBreadcrumbIcon\"\r\n :disabled=\"!settingsStore.showBreadcrumb\"\r\n />\r\n </div>\r\n <div class=\"setting-item\">\r\n <span>显示标签页</span>\r\n <NSwitch v-model:value=\"settingsStore.showTagsView\" />\r\n </div>\r\n <div class=\"setting-item\">\r\n <span>显示页脚</span>\r\n <NSwitch v-model:value=\"settingsStore.showFooter\" />\r\n </div>\r\n </div>\r\n\r\n <!-- 尺寸调整 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">尺寸调整</div>\r\n <div class=\"slider-item\">\r\n <div class=\"slider-label\">\r\n <span>侧边栏宽度</span>\r\n <span class=\"slider-value\"\r\n >{{ settingsStore.sidebarWidth }}px</span\r\n >\r\n </div>\r\n <NSlider\r\n v-model:value=\"settingsStore.sidebarWidth\"\r\n :min=\"180\"\r\n :max=\"280\"\r\n :step=\"10\"\r\n :marks=\"{ 180: '180', 220: '220', 280: '280' }\"\r\n />\r\n </div>\r\n <div class=\"slider-item\">\r\n <div class=\"slider-label\">\r\n <span>头部高度</span>\r\n <span class=\"slider-value\"\r\n >{{ settingsStore.headerHeight }}px</span\r\n >\r\n </div>\r\n <NSlider\r\n v-model:value=\"settingsStore.headerHeight\"\r\n :min=\"48\"\r\n :max=\"64\"\r\n :step=\"4\"\r\n :marks=\"{ 48: '48', 56: '56', 64: '64' }\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 恢复默认 -->\r\n <div class=\"settings-section\">\r\n <NButton block secondary @click=\"handleResetLayout\">\r\n <template #icon>\r\n <span class=\"i-mdi:restore\"></span>\r\n </template>\r\n 恢复布局默认设置\r\n </NButton>\r\n </div>\r\n <slot name=\"layout-append\" :settings=\"settingsStore\" />\r\n </NTabPane>\r\n\r\n <!-- 功能 Tab -->\r\n <NTabPane name=\"features\" tab=\"🔧 功能\">\r\n <slot name=\"features-prepend\" :settings=\"settingsStore\" />\r\n <!-- 缓存管理 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">缓存管理</div>\r\n <div class=\"action-buttons-grid\">\r\n <NButton secondary @click=\"handleClearCache\">\r\n <template #icon>\r\n <span class=\"i-mdi:delete-sweep\"></span>\r\n </template>\r\n 清除缓存\r\n </NButton>\r\n <NButton secondary @click=\"handleReload\">\r\n <template #icon>\r\n <span class=\"i-mdi:refresh\"></span>\r\n </template>\r\n 重新加载\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- 配置管理 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">配置管理</div>\r\n <div class=\"action-buttons-grid\">\r\n <NButton secondary @click=\"handleExportConfig\">\r\n <template #icon>\r\n <span class=\"i-mdi:download\"></span>\r\n </template>\r\n 导出配置\r\n </NButton>\r\n <NButton secondary @click=\"handleImportConfig\">\r\n <template #icon>\r\n <span class=\"i-mdi:upload\"></span>\r\n </template>\r\n 导入配置\r\n </NButton>\r\n </div>\r\n <NButton\r\n block\r\n type=\"error\"\r\n secondary\r\n class=\"mt-2\"\r\n @click=\"handleReset\"\r\n >\r\n <template #icon>\r\n <span class=\"i-mdi:restore\"></span>\r\n </template>\r\n 重置所有配置\r\n </NButton>\r\n </div>\r\n\r\n <!-- 系统模式 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">系统模式</div>\r\n <div class=\"setting-item\">\r\n <div class=\"setting-label\">\r\n <span>灰色模式</span>\r\n <span class=\"setting-desc\">用于哀悼日场景</span>\r\n </div>\r\n <NSwitch v-model:value=\"grayMode\" />\r\n </div>\r\n <div class=\"setting-item\">\r\n <div class=\"setting-label\">\r\n <span>色弱模式</span>\r\n <span class=\"setting-desc\">提供无障碍访问支持</span>\r\n </div>\r\n <NSwitch v-model:value=\"colorWeakMode\" />\r\n </div>\r\n </div>\r\n\r\n <!-- 水印设置 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">水印设置</div>\r\n <div class=\"setting-item\">\r\n <span>启用水印</span>\r\n <NSwitch v-model:value=\"watermarkEnabled\" />\r\n </div>\r\n <div v-if=\"watermarkEnabled\" class=\"mt-2\">\r\n <NInput\r\n v-model:value=\"watermarkText\"\r\n placeholder=\"请输入水印内容\"\r\n size=\"small\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 系统信息 -->\r\n <div class=\"settings-section\">\r\n <div class=\"section-title\">系统信息</div>\r\n <div class=\"system-info\">\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">浏览器</span>\r\n <span class=\"info-value\">{{ systemInfo.browser }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">操作系统</span>\r\n <span class=\"info-value\">{{ systemInfo.os }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">屏幕分辨率</span>\r\n <span class=\"info-value\">{{ systemInfo.resolution }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">设备像素比</span>\r\n <span class=\"info-value\">{{ systemInfo.pixelRatio }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">语言</span>\r\n <span class=\"info-value\">{{ systemInfo.language }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">时区</span>\r\n <span class=\"info-value\">{{ systemInfo.timezone }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n <slot name=\"features-append\" :settings=\"settingsStore\" />\r\n </NTabPane>\r\n </NTabs>\r\n </NDrawerContent>\r\n </NDrawer>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, inject } from \"vue\";\r\nimport {\r\n NDrawer,\r\n NDrawerContent,\r\n NTabs,\r\n NTabPane,\r\n NRadioGroup,\r\n NRadioButton,\r\n NColorPicker,\r\n NSwitch,\r\n NButton,\r\n NSlider,\r\n NInput,\r\n useMessage,\r\n useDialog,\r\n} from \"naive-ui\";\r\nimport {\r\n useSettingsStore,\r\n type SettingsStoreInstance,\r\n} from \"../../stores/settings\";\r\nimport {\r\n LayoutActionUnavailableError,\r\n useSettingsController,\r\n} from \"../../composables/useSettingsController\";\r\nimport { LAYOUT_SETTINGS_KEY } from \"../../composables/useLayoutContext\";\r\nimport { COLOR_SWATCHES, LAYOUT_MODE_OPTIONS, THEME_PRESETS } from \"./data\";\r\nimport type {\r\n SettingsDrawerActions,\r\n LayoutMode,\r\n ThemeMode,\r\n ThemePreset,\r\n} from \"../../types\";\r\n\r\n// ============ 数据定义 ============\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 抽屉宽度,默认 380px */\r\n width?: number;\r\n /** 显式设置 Store;优先级高于 setupLayout 注入和默认 Store */\r\n store?: SettingsStoreInstance;\r\n /** 由宿主实现的缓存清理、页面刷新等高副作用操作 */\r\n actions?: SettingsDrawerActions;\r\n /** 导入文件最大字节数,默认 1 MiB */\r\n maxImportBytes?: number;\r\n }>(),\r\n {\r\n width: 380,\r\n maxImportBytes: 1024 * 1024,\r\n },\r\n);\r\n\r\nconst message = useMessage();\r\nconst dialog = useDialog();\r\nconst injectedSettingsStore = inject(LAYOUT_SETTINGS_KEY, null);\r\nconst settingsStore =\r\n props.store ?? injectedSettingsStore ?? useSettingsStore();\r\nconst visible = defineModel<boolean>(\"show\", { default: false });\r\nconst activeTab = ref(\"appearance\");\r\nconst settingsController = useSettingsController({\r\n store: settingsStore,\r\n actions: props.actions,\r\n maxImportBytes: props.maxImportBytes,\r\n onVisualEffectError: (text) => message.error(text),\r\n});\r\nconst { colorWeakMode, grayMode, systemInfo, watermarkEnabled, watermarkText } =\r\n settingsController;\r\n\r\n// 处理布局切换 - 阻止抽屉关闭\r\nconst handleLayoutChange = (value: LayoutMode, disabled?: boolean) => {\r\n if (disabled) return;\r\n settingsStore.layoutMode = value;\r\n};\r\n\r\n// ============ 方法 ============\r\n\r\n/**\r\n * 判断是否是当前预设\r\n */\r\nconst isCurrentPreset = (preset: ThemePreset) => {\r\n return settingsStore.primaryColor === preset.primaryColor;\r\n};\r\n\r\n/**\r\n * 应用预设方案\r\n */\r\nconst handleApplyPreset = (preset: ThemePreset) => {\r\n void settingsController.applyPreset(preset);\r\n message.success(`已应用「${preset.name}」主题方案`);\r\n};\r\n\r\nconst handleThemeModeChange = async (mode: ThemeMode) => {\r\n try {\r\n await settingsController.updateThemeMode(mode);\r\n } catch {\r\n message.error(\"主题模式切换失败\");\r\n }\r\n};\r\n\r\n/**\r\n * 恢复外观默认设置\r\n */\r\nconst handleResetAppearance = async () => {\r\n try {\r\n await settingsController.resetAppearance();\r\n message.success(\"已恢复外观默认设置\");\r\n } catch {\r\n message.error(\"恢复外观默认设置失败\");\r\n }\r\n};\r\n\r\n/**\r\n * 恢复布局默认设置\r\n */\r\nconst handleResetLayout = () => {\r\n settingsController.resetLayout();\r\n message.success(\"已恢复布局默认设置\");\r\n};\r\n\r\n/**\r\n * 重置配置\r\n */\r\nconst handleReset = () => {\r\n dialog.warning({\r\n title: \"确认重置\",\r\n content: \"确定要恢复默认配置吗?此操作不可撤销。\",\r\n positiveText: \"确认\",\r\n negativeText: \"取消\",\r\n onPositiveClick: async () => {\r\n try {\r\n await settingsController.resetAll();\r\n message.success(\"已恢复默认配置\");\r\n } catch {\r\n message.error(\"恢复默认配置失败\");\r\n }\r\n },\r\n });\r\n};\r\n\r\n/**\r\n * 清除缓存\r\n */\r\nconst handleClearCache = () => {\r\n dialog.warning({\r\n title: \"确认清除\",\r\n content: \"确定要清除浏览器缓存吗?\",\r\n positiveText: \"确认\",\r\n negativeText: \"取消\",\r\n onPositiveClick: async () => {\r\n try {\r\n await settingsController.clearCache();\r\n message.success(\"缓存已清除\");\r\n } catch (error) {\r\n if (error instanceof LayoutActionUnavailableError) {\r\n message.warning(\"宿主应用未配置可安全清理的缓存\");\r\n return;\r\n }\r\n message.error(\"缓存清除失败\");\r\n }\r\n },\r\n });\r\n};\r\n\r\n/**\r\n * 重新加载页面\r\n */\r\nconst handleReload = () => {\r\n settingsController.reloadPage();\r\n};\r\n\r\n/**\r\n * 导出配置\r\n */\r\nconst handleExportConfig = () => {\r\n if (settingsController.downloadConfig()) message.success(\"配置已导出\");\r\n else message.error(\"当前环境不支持配置导出\");\r\n};\r\n\r\n/**\r\n * 导入配置\r\n */\r\nconst handleImportConfig = () => {\r\n if (typeof document === \"undefined\") return;\r\n const input = document.createElement(\"input\");\r\n input.type = \"file\";\r\n input.accept = \"application/json\";\r\n input.onchange = async (e: Event) => {\r\n const file = (e.target as HTMLInputElement).files?.[0];\r\n if (!file) return;\r\n\r\n try {\r\n await settingsController.importFile(file);\r\n message.success(\"配置已导入\");\r\n } catch (error) {\r\n message.error(\"配置文件格式错误\");\r\n }\r\n };\r\n input.click();\r\n};\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n@use \"../../styles/settings.scss\" as *;\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - BrandLogo\r\n *\r\n * 品牌 Logo 组件\r\n * 从 LayoutContext.brand 读取配置,自动渲染 Logo + 品牌名\r\n * 支持折叠状态、横向/纵向布局\r\n -->\r\n<template>\r\n <div\r\n class=\"brand-logo\"\r\n :class=\"{\r\n 'brand-logo--collapsed': collapsed,\r\n 'brand-logo--horizontal': variant === 'horizontal',\r\n }\"\r\n >\r\n <div class=\"brand-logo__icon\">\r\n <div class=\"brand-logo__glow\"></div>\r\n <video\r\n v-if=\"brandConfig.logoType === 'video' && brandConfig.logoSrc\"\r\n :src=\"brandConfig.logoSrc\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"brand-logo__media\"\r\n />\r\n <img\r\n v-else-if=\"brandConfig.logoSrc\"\r\n :src=\"brandConfig.logoSrc\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n class=\"brand-logo__media\"\r\n alt=\"Logo\"\r\n />\r\n </div>\r\n <template v-if=\"showName && !collapsed\">\r\n <div class=\"brand-logo__name\">\r\n <span class=\"brand-logo__title\">{{ brandConfig.name }}</span>\r\n <span v-if=\"brandConfig.subtitle\" class=\"brand-logo__subtitle\">{{\r\n brandConfig.subtitle\r\n }}</span>\r\n </div>\r\n <div v-if=\"showDivider\" class=\"brand-logo__divider\"></div>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"BrandLogo\" });\r\n\r\nwithDefaults(\r\n defineProps<{\r\n /** 是否显示品牌名称 */\r\n showName?: boolean;\r\n /** 是否显示分隔线 */\r\n showDivider?: boolean;\r\n /** Logo 尺寸 */\r\n size?: number;\r\n /** 是否折叠状态 */\r\n collapsed?: boolean;\r\n /** 布局方向 */\r\n variant?: \"vertical\" | \"horizontal\";\r\n }>(),\r\n {\r\n showName: false,\r\n showDivider: false,\r\n size: 36,\r\n collapsed: false,\r\n variant: \"horizontal\",\r\n },\r\n);\r\n\r\nconst ctx = useLayoutContext();\r\nconst brandConfig = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n</script>\r\n\r\n<style scoped>\r\n.brand-logo {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.brand-logo--collapsed {\r\n justify-content: center;\r\n}\r\n\r\n.brand-logo__icon {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n}\r\n\r\n.brand-logo__glow {\r\n position: absolute;\r\n inset: -4px;\r\n border-radius: 50%;\r\n background: radial-gradient(\r\n circle,\r\n rgba(99, 102, 241, 0.15) 0%,\r\n transparent 70%\r\n );\r\n pointer-events: none;\r\n}\r\n\r\n.brand-logo__media {\r\n border-radius: 8px;\r\n object-fit: contain;\r\n}\r\n\r\n.brand-logo__name {\r\n display: flex;\r\n flex-direction: column;\r\n line-height: 1.2;\r\n white-space: nowrap;\r\n}\r\n\r\n.brand-logo__title {\r\n font-size: 15px;\r\n font-weight: 700;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n background-clip: text;\r\n -webkit-background-clip: text;\r\n -webkit-text-fill-color: transparent;\r\n}\r\n\r\n.brand-logo__subtitle {\r\n font-size: 10px;\r\n opacity: 0.6;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.brand-logo__divider {\r\n width: 1px;\r\n height: 24px;\r\n margin: 0 6px;\r\n background: linear-gradient(\r\n 180deg,\r\n transparent 0%,\r\n rgba(128, 128, 128, 0.3) 50%,\r\n transparent 100%\r\n );\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - BrandLogo\r\n *\r\n * 品牌 Logo 组件\r\n * 从 LayoutContext.brand 读取配置,自动渲染 Logo + 品牌名\r\n * 支持折叠状态、横向/纵向布局\r\n -->\r\n<template>\r\n <div\r\n class=\"brand-logo\"\r\n :class=\"{\r\n 'brand-logo--collapsed': collapsed,\r\n 'brand-logo--horizontal': variant === 'horizontal',\r\n }\"\r\n >\r\n <div class=\"brand-logo__icon\">\r\n <div class=\"brand-logo__glow\"></div>\r\n <video\r\n v-if=\"brandConfig.logoType === 'video' && brandConfig.logoSrc\"\r\n :src=\"brandConfig.logoSrc\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n autoplay\r\n loop\r\n muted\r\n playsinline\r\n class=\"brand-logo__media\"\r\n />\r\n <img\r\n v-else-if=\"brandConfig.logoSrc\"\r\n :src=\"brandConfig.logoSrc\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n class=\"brand-logo__media\"\r\n alt=\"Logo\"\r\n />\r\n </div>\r\n <template v-if=\"showName && !collapsed\">\r\n <div class=\"brand-logo__name\">\r\n <span class=\"brand-logo__title\">{{ brandConfig.name }}</span>\r\n <span v-if=\"brandConfig.subtitle\" class=\"brand-logo__subtitle\">{{\r\n brandConfig.subtitle\r\n }}</span>\r\n </div>\r\n <div v-if=\"showDivider\" class=\"brand-logo__divider\"></div>\r\n </template>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"BrandLogo\" });\r\n\r\nwithDefaults(\r\n defineProps<{\r\n /** 是否显示品牌名称 */\r\n showName?: boolean;\r\n /** 是否显示分隔线 */\r\n showDivider?: boolean;\r\n /** Logo 尺寸 */\r\n size?: number;\r\n /** 是否折叠状态 */\r\n collapsed?: boolean;\r\n /** 布局方向 */\r\n variant?: \"vertical\" | \"horizontal\";\r\n }>(),\r\n {\r\n showName: false,\r\n showDivider: false,\r\n size: 36,\r\n collapsed: false,\r\n variant: \"horizontal\",\r\n },\r\n);\r\n\r\nconst ctx = useLayoutContext();\r\nconst brandConfig = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n</script>\r\n\r\n<style scoped>\r\n.brand-logo {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n cursor: pointer;\r\n user-select: none;\r\n}\r\n\r\n.brand-logo--collapsed {\r\n justify-content: center;\r\n}\r\n\r\n.brand-logo__icon {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n}\r\n\r\n.brand-logo__glow {\r\n position: absolute;\r\n inset: -4px;\r\n border-radius: 50%;\r\n background: radial-gradient(\r\n circle,\r\n rgba(99, 102, 241, 0.15) 0%,\r\n transparent 70%\r\n );\r\n pointer-events: none;\r\n}\r\n\r\n.brand-logo__media {\r\n border-radius: 8px;\r\n object-fit: contain;\r\n}\r\n\r\n.brand-logo__name {\r\n display: flex;\r\n flex-direction: column;\r\n line-height: 1.2;\r\n white-space: nowrap;\r\n}\r\n\r\n.brand-logo__title {\r\n font-size: 15px;\r\n font-weight: 700;\r\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\r\n background-clip: text;\r\n -webkit-background-clip: text;\r\n -webkit-text-fill-color: transparent;\r\n}\r\n\r\n.brand-logo__subtitle {\r\n font-size: 10px;\r\n opacity: 0.6;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.brand-logo__divider {\r\n width: 1px;\r\n height: 24px;\r\n margin: 0 6px;\r\n background: linear-gradient(\r\n 180deg,\r\n transparent 0%,\r\n rgba(128, 128, 128, 0.3) 50%,\r\n transparent 100%\r\n );\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - IconMenu\r\n *\r\n * 一级图标菜单\r\n * 用于 Mix / MixTop 布局的左侧一级菜单栏\r\n * 显示图标 + 标签,支持激活状态\r\n -->\r\n<template>\r\n <div class=\"icon-menu\">\r\n <div\r\n v-for=\"item in menus\"\r\n :key=\"item.path\"\r\n class=\"icon-menu__item\"\r\n :class=\"{ 'icon-menu__item--active': active === item.path }\"\r\n @click=\"emit('click', item)\"\r\n >\r\n <div class=\"icon-menu__icon\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"20\"\r\n />\r\n <span v-else class=\"icon-menu__fallback\">\r\n {{ item.meta?.title?.charAt(0) || \"?\" }}\r\n </span>\r\n </div>\r\n <div class=\"icon-menu__label\">\r\n {{ item.meta?.title }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"IconMenu\" });\r\n\r\ndefineProps<{\r\n /** 菜单数据 */\r\n menus: MenuOptions[];\r\n /** 当前激活的菜单路径 */\r\n active?: string;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n click: [item: MenuOptions];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\n</script>\r\n\r\n<style scoped>\r\n.icon-menu {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 4px 0;\r\n width: 100%;\r\n}\r\n\r\n.icon-menu__item {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 4px;\r\n width: calc(100% - 12px);\r\n padding: 10px 4px 8px;\r\n margin: 0 6px;\r\n border-radius: 10px;\r\n cursor: pointer;\r\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n position: relative;\r\n opacity: 0.7;\r\n}\r\n\r\n.icon-menu__item:hover {\r\n opacity: 1;\r\n background: rgba(99, 102, 241, 0.08);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.icon-menu__item--active {\r\n opacity: 1;\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.15) 0%,\r\n rgba(139, 92, 246, 0.1) 100%\r\n );\r\n box-shadow: 0 2px 8px rgba(99, 102, 241, 0.2);\r\n}\r\n\r\n.icon-menu__item--active::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n width: 3px;\r\n height: 60%;\r\n border-radius: 0 3px 3px 0;\r\n background: linear-gradient(180deg, #667eea, #764ba2);\r\n}\r\n\r\n.icon-menu__icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n font-size: 20px;\r\n}\r\n\r\n.icon-menu__fallback {\r\n font-size: 14px;\r\n font-weight: 600;\r\n width: 28px;\r\n height: 28px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: 8px;\r\n background: rgba(128, 128, 128, 0.1);\r\n}\r\n\r\n.icon-menu__label {\r\n font-size: 11px;\r\n font-weight: 500;\r\n line-height: 1.2;\r\n text-align: center;\r\n max-width: 100%;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - IconMenu\r\n *\r\n * 一级图标菜单\r\n * 用于 Mix / MixTop 布局的左侧一级菜单栏\r\n * 显示图标 + 标签,支持激活状态\r\n -->\r\n<template>\r\n <div class=\"icon-menu\">\r\n <div\r\n v-for=\"item in menus\"\r\n :key=\"item.path\"\r\n class=\"icon-menu__item\"\r\n :class=\"{ 'icon-menu__item--active': active === item.path }\"\r\n @click=\"emit('click', item)\"\r\n >\r\n <div class=\"icon-menu__icon\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"20\"\r\n />\r\n <span v-else class=\"icon-menu__fallback\">\r\n {{ item.meta?.title?.charAt(0) || \"?\" }}\r\n </span>\r\n </div>\r\n <div class=\"icon-menu__label\">\r\n {{ item.meta?.title }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"IconMenu\" });\r\n\r\ndefineProps<{\r\n /** 菜单数据 */\r\n menus: MenuOptions[];\r\n /** 当前激活的菜单路径 */\r\n active?: string;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n click: [item: MenuOptions];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\n</script>\r\n\r\n<style scoped>\r\n.icon-menu {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 4px 0;\r\n width: 100%;\r\n}\r\n\r\n.icon-menu__item {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 4px;\r\n width: calc(100% - 12px);\r\n padding: 10px 4px 8px;\r\n margin: 0 6px;\r\n border-radius: 10px;\r\n cursor: pointer;\r\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\r\n position: relative;\r\n opacity: 0.7;\r\n}\r\n\r\n.icon-menu__item:hover {\r\n opacity: 1;\r\n background: rgba(99, 102, 241, 0.08);\r\n transform: translateY(-1px);\r\n}\r\n\r\n.icon-menu__item--active {\r\n opacity: 1;\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.15) 0%,\r\n rgba(139, 92, 246, 0.1) 100%\r\n );\r\n box-shadow: 0 2px 8px rgba(99, 102, 241, 0.2);\r\n}\r\n\r\n.icon-menu__item--active::before {\r\n content: \"\";\r\n position: absolute;\r\n left: 0;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n width: 3px;\r\n height: 60%;\r\n border-radius: 0 3px 3px 0;\r\n background: linear-gradient(180deg, #667eea, #764ba2);\r\n}\r\n\r\n.icon-menu__icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n font-size: 20px;\r\n}\r\n\r\n.icon-menu__fallback {\r\n font-size: 14px;\r\n font-weight: 600;\r\n width: 28px;\r\n height: 28px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: 8px;\r\n background: rgba(128, 128, 128, 0.1);\r\n}\r\n\r\n.icon-menu__label {\r\n font-size: 11px;\r\n font-weight: 500;\r\n line-height: 1.2;\r\n text-align: center;\r\n max-width: 100%;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - FloatingMenu\r\n *\r\n * 悬浮二级菜单面板\r\n * 用于 C_MixLayout 的悬浮二级菜单(点击一级菜单后展开)\n -->\r\n<template>\r\n <Transition name=\"slide-fade\">\r\n <div\r\n v-if=\"show && menuItem\"\r\n class=\"floating-menu\"\n :class=\"[ctx.isDark.value ? 'dark-theme' : 'light-theme']\"\n role=\"navigation\"\n aria-label=\"二级功能导航\"\n >\r\n <!-- 标题 -->\r\n <div class=\"floating-menu__header\">\r\n <div class=\"floating-menu__brand\">\r\n <span class=\"floating-menu__brand-title\">{{ brand.name }}</span>\r\n <span class=\"floating-menu__brand-subtitle\">{{\r\n brand.subtitle\r\n }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 菜单列表 -->\r\n <div class=\"floating-menu__list\">\r\n <template v-for=\"child in menuItem.children\" :key=\"child.path\">\r\n <!-- 有子菜单的项 -->\r\n <div\r\n v-if=\"child.children && child.children.length > 0\"\r\n class=\"floating-menu__group\"\r\n >\r\n <div class=\"floating-menu__group-title\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span>{{ child.meta?.title }}</span>\r\n </div>\r\n <div\r\n v-for=\"subChild in child.children\"\r\n :key=\"subChild.path\"\r\n class=\"floating-menu__item floating-menu__item--sub\"\n :class=\"{ active: isActive?.(subChild.path) }\"\n role=\"link\"\n tabindex=\"0\"\n @click=\"emit('click', subChild)\"\n @keydown.enter=\"emit('click', subChild)\"\n @keydown.space.prevent=\"emit('click', subChild)\"\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && subChild.meta?.icon\"\r\n :name=\"subChild.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"floating-menu__item-text\">{{\r\n subChild.meta?.title\r\n }}</span>\r\n </div>\r\n </div>\r\n <!-- 无子菜单的项 -->\r\n <div\r\n v-else\r\n class=\"floating-menu__item\"\n :class=\"{ active: isActive?.(child.path) }\"\n role=\"link\"\n tabindex=\"0\"\n @click=\"emit('click', child)\"\n @keydown.enter=\"emit('click', child)\"\n @keydown.space.prevent=\"emit('click', child)\"\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span class=\"floating-menu__item-text\">{{\r\n child.meta?.title\r\n }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </Transition>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"FloatingMenu\" });\r\n\r\ndefineProps<{\r\n /** 是否显示 */\r\n show: boolean;\r\n /** 当前悬浮的一级菜单项(包含 children) */\r\n menuItem: MenuOptions | null;\r\n /** 判断菜单项是否激活 */\r\n isActive?: (path: string | undefined) => boolean;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n click: [item: MenuOptions];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n</script>\r\n\r\n<style scoped>\r\n.floating-menu {\r\n position: absolute;\r\n top: 0;\r\n left: 100%;\r\n width: 240px;\r\n height: 100%;\r\n z-index: 100;\r\n overflow-y: auto;\r\n border-right: 1px solid rgba(128, 128, 128, 0.1);\r\n backdrop-filter: blur(20px);\r\n -webkit-backdrop-filter: blur(20px);\r\n}\r\n\r\n.floating-menu.light-theme {\r\n background: rgba(255, 255, 255, 0.95);\r\n box-shadow: 4px 0 24px rgba(0, 0, 0, 0.08);\r\n}\r\n\r\n.floating-menu.dark-theme {\r\n background: rgba(30, 30, 36, 0.95);\r\n box-shadow: 4px 0 24px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.floating-menu__header {\r\n padding: 16px 20px 12px;\r\n border-bottom: 1px solid rgba(128, 128, 128, 0.1);\r\n}\r\n\r\n.floating-menu__brand-title {\r\n font-size: 14px;\r\n font-weight: 700;\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n background-clip: text;\r\n -webkit-background-clip: text;\r\n -webkit-text-fill-color: transparent;\r\n}\r\n\r\n.floating-menu__brand-subtitle {\r\n display: block;\r\n font-size: 11px;\r\n opacity: 0.5;\r\n margin-top: 2px;\r\n}\r\n\r\n.floating-menu__list {\r\n padding: 8px 12px;\r\n}\r\n\r\n.floating-menu__group {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.floating-menu__group-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 8px 4px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n opacity: 0.5;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.floating-menu__item {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n padding: 10px 12px;\r\n margin: 2px 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n font-size: 14px;\r\n}\r\n\r\n.floating-menu__item:hover {\n background: rgba(99, 102, 241, 0.08);\r\n}\n\n.floating-menu__item:focus-visible {\n outline: 2px solid #667eea;\n outline-offset: 2px;\n}\n\r\n.floating-menu__item.active {\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.12),\r\n rgba(139, 92, 246, 0.08)\r\n );\r\n color: #667eea;\r\n}\r\n\r\n.floating-menu__item--sub {\r\n padding-left: 20px;\r\n font-size: 13px;\r\n}\r\n\r\n.floating-menu__item-text {\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Transition */\r\n.slide-fade-enter-active,\r\n.slide-fade-leave-active {\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.slide-fade-enter-from {\r\n opacity: 0;\r\n transform: translateX(-12px);\r\n}\r\n\r\n.slide-fade-leave-to {\r\n opacity: 0;\r\n transform: translateX(-12px);\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - FloatingMenu\r\n *\r\n * 悬浮二级菜单面板\r\n * 用于 C_MixLayout 的悬浮二级菜单(点击一级菜单后展开)\n -->\r\n<template>\r\n <Transition name=\"slide-fade\">\r\n <div\r\n v-if=\"show && menuItem\"\r\n class=\"floating-menu\"\n :class=\"[ctx.isDark.value ? 'dark-theme' : 'light-theme']\"\n role=\"navigation\"\n aria-label=\"二级功能导航\"\n >\r\n <!-- 标题 -->\r\n <div class=\"floating-menu__header\">\r\n <div class=\"floating-menu__brand\">\r\n <span class=\"floating-menu__brand-title\">{{ brand.name }}</span>\r\n <span class=\"floating-menu__brand-subtitle\">{{\r\n brand.subtitle\r\n }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 菜单列表 -->\r\n <div class=\"floating-menu__list\">\r\n <template v-for=\"child in menuItem.children\" :key=\"child.path\">\r\n <!-- 有子菜单的项 -->\r\n <div\r\n v-if=\"child.children && child.children.length > 0\"\r\n class=\"floating-menu__group\"\r\n >\r\n <div class=\"floating-menu__group-title\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span>{{ child.meta?.title }}</span>\r\n </div>\r\n <div\r\n v-for=\"subChild in child.children\"\r\n :key=\"subChild.path\"\r\n class=\"floating-menu__item floating-menu__item--sub\"\n :class=\"{ active: isActive?.(subChild.path) }\"\n role=\"link\"\n tabindex=\"0\"\n @click=\"emit('click', subChild)\"\n @keydown.enter=\"emit('click', subChild)\"\n @keydown.space.prevent=\"emit('click', subChild)\"\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && subChild.meta?.icon\"\r\n :name=\"subChild.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"floating-menu__item-text\">{{\r\n subChild.meta?.title\r\n }}</span>\r\n </div>\r\n </div>\r\n <!-- 无子菜单的项 -->\r\n <div\r\n v-else\r\n class=\"floating-menu__item\"\n :class=\"{ active: isActive?.(child.path) }\"\n role=\"link\"\n tabindex=\"0\"\n @click=\"emit('click', child)\"\n @keydown.enter=\"emit('click', child)\"\n @keydown.space.prevent=\"emit('click', child)\"\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span class=\"floating-menu__item-text\">{{\r\n child.meta?.title\r\n }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </Transition>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport {\r\n useLayoutContext,\r\n DEFAULT_BRAND_CONFIG,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"FloatingMenu\" });\r\n\r\ndefineProps<{\r\n /** 是否显示 */\r\n show: boolean;\r\n /** 当前悬浮的一级菜单项(包含 children) */\r\n menuItem: MenuOptions | null;\r\n /** 判断菜单项是否激活 */\r\n isActive?: (path: string | undefined) => boolean;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n click: [item: MenuOptions];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\r\n</script>\r\n\r\n<style scoped>\r\n.floating-menu {\r\n position: absolute;\r\n top: 0;\r\n left: 100%;\r\n width: 240px;\r\n height: 100%;\r\n z-index: 100;\r\n overflow-y: auto;\r\n border-right: 1px solid rgba(128, 128, 128, 0.1);\r\n backdrop-filter: blur(20px);\r\n -webkit-backdrop-filter: blur(20px);\r\n}\r\n\r\n.floating-menu.light-theme {\r\n background: rgba(255, 255, 255, 0.95);\r\n box-shadow: 4px 0 24px rgba(0, 0, 0, 0.08);\r\n}\r\n\r\n.floating-menu.dark-theme {\r\n background: rgba(30, 30, 36, 0.95);\r\n box-shadow: 4px 0 24px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.floating-menu__header {\r\n padding: 16px 20px 12px;\r\n border-bottom: 1px solid rgba(128, 128, 128, 0.1);\r\n}\r\n\r\n.floating-menu__brand-title {\r\n font-size: 14px;\r\n font-weight: 700;\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n background-clip: text;\r\n -webkit-background-clip: text;\r\n -webkit-text-fill-color: transparent;\r\n}\r\n\r\n.floating-menu__brand-subtitle {\r\n display: block;\r\n font-size: 11px;\r\n opacity: 0.5;\r\n margin-top: 2px;\r\n}\r\n\r\n.floating-menu__list {\r\n padding: 8px 12px;\r\n}\r\n\r\n.floating-menu__group {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.floating-menu__group-title {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 8px 8px 4px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n opacity: 0.5;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.floating-menu__item {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n padding: 10px 12px;\r\n margin: 2px 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n font-size: 14px;\r\n}\r\n\r\n.floating-menu__item:hover {\n background: rgba(99, 102, 241, 0.08);\r\n}\n\n.floating-menu__item:focus-visible {\n outline: 2px solid #667eea;\n outline-offset: 2px;\n}\n\r\n.floating-menu__item.active {\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.12),\r\n rgba(139, 92, 246, 0.08)\r\n );\r\n color: #667eea;\r\n}\r\n\r\n.floating-menu__item--sub {\r\n padding-left: 20px;\r\n font-size: 13px;\r\n}\r\n\r\n.floating-menu__item-text {\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Transition */\r\n.slide-fade-enter-active,\r\n.slide-fade-leave-active {\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.slide-fade-enter-from {\r\n opacity: 0;\r\n transform: translateX(-12px);\r\n}\r\n\r\n.slide-fade-leave-to {\r\n opacity: 0;\r\n transform: translateX(-12px);\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - SideMenu\r\n *\r\n * 右侧二级菜单侧边栏\r\n * 用于 C_ReverseHorizontalMixLayout 的右侧二级菜单\n -->\r\n<template>\r\n <template v-if=\"menus.length > 0\">\r\n <!-- 折叠/展开按钮 -->\r\n <div\r\n class=\"side-menu-toggle\"\r\n :class=\"[\r\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\r\n { active: !isCollapsed },\r\n ]\"\r\n @click=\"isCollapsed = !isCollapsed\"\r\n >\r\n <i\r\n :class=\"[\r\n 'toggle-icon',\r\n isCollapsed ? 'i-ri:side-bar-line' : 'i-ri:arrow-right-s-line',\r\n ]\"\r\n ></i>\r\n </div>\r\n\r\n <!-- 侧边栏内容 -->\r\n <Transition name=\"glass-slide\">\r\n <div\r\n v-if=\"!isCollapsed\"\r\n class=\"side-menu\"\r\n :class=\"[ctx.isDark.value ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 标题 -->\r\n <div class=\"side-menu__header\">\r\n <div class=\"side-menu__header-icon\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && activeItem?.meta?.icon\"\r\n :name=\"activeItem.meta.icon\"\r\n :size=\"17\"\r\n />\r\n </div>\r\n <span class=\"side-menu__header-title\">{{\r\n activeItem?.meta?.title || \"菜单\"\r\n }}</span>\r\n </div>\r\n\r\n <!-- 菜单列表 -->\r\n <div class=\"side-menu__list\">\r\n <template v-for=\"child in menus\" :key=\"child.path\">\r\n <!-- 有子菜单 -->\r\n <div\r\n v-if=\"child.children && child.children.length > 0\"\r\n class=\"side-menu__group\"\r\n >\r\n <div class=\"side-menu__group-label\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"14\"\r\n />\r\n <span>{{ child.meta?.title }}</span>\r\n </div>\r\n <div\r\n v-for=\"subChild in child.children\"\r\n :key=\"subChild.path\"\r\n class=\"side-menu__item\"\r\n :class=\"{ active: isActive?.(subChild.path) }\"\r\n @click=\"emit('click', subChild)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && subChild.meta?.icon\"\r\n :name=\"subChild.meta.icon\"\r\n :size=\"15\"\r\n />\r\n <span class=\"side-menu__item-text\">{{\r\n subChild.meta?.title\r\n }}</span>\r\n </div>\r\n </div>\r\n <!-- 无子菜单 -->\r\n <div\r\n v-else\r\n class=\"side-menu__item\"\r\n :class=\"{ active: isActive?.(child.path) }\"\r\n @click=\"emit('click', child)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"side-menu__item-text\">{{ child.meta?.title }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </Transition>\r\n </template>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref } from \"vue\";\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"SideMenu\" });\r\n\r\ndefineProps<{\r\n /** 二级菜单列表 */\r\n menus: MenuOptions[];\r\n /** 当前一级菜单项(显示标题和图标) */\r\n activeItem?: MenuOptions;\r\n /** 判断菜单项是否激活 */\r\n isActive?: (path: string | undefined) => boolean;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n click: [item: MenuOptions];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\nconst isCollapsed = ref(false);\r\n</script>\r\n\r\n<style scoped>\r\n.side-menu-toggle {\r\n position: absolute;\r\n right: 0;\r\n top: 50%;\r\n transform: translateY(-50%) translateX(50%);\r\n width: 24px;\r\n height: 48px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: 12px;\r\n cursor: pointer;\r\n z-index: 10;\r\n transition: all 0.25s ease;\r\n font-size: 14px;\r\n}\r\n\r\n.side-menu-toggle.light-theme {\r\n background: rgba(255, 255, 255, 0.9);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n.side-menu-toggle.dark-theme {\r\n background: rgba(40, 40, 46, 0.9);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.side-menu-toggle:hover {\r\n transform: translateY(-50%) translateX(50%) scale(1.1);\r\n}\r\n\r\n.side-menu {\r\n width: 100%;\r\n height: 100%;\r\n overflow-y: auto;\r\n padding: 0 8px;\r\n}\r\n\r\n.side-menu.light-theme {\r\n background: rgba(255, 255, 255, 0.95);\r\n border-left: 1px solid rgba(0, 0, 0, 0.06);\r\n}\r\n\r\n.side-menu.dark-theme {\r\n background: rgba(30, 30, 36, 0.95);\r\n border-left: 1px solid rgba(255, 255, 255, 0.06);\r\n}\r\n\r\n.side-menu__header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 16px 12px 12px;\r\n border-bottom: 1px solid rgba(128, 128, 128, 0.1);\r\n margin-bottom: 8px;\r\n}\r\n\r\n.side-menu__header-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n border-radius: 8px;\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.12),\r\n rgba(139, 92, 246, 0.08)\r\n );\r\n}\r\n\r\n.side-menu__header-title {\r\n font-size: 14px;\r\n font-weight: 600;\r\n}\r\n\r\n.side-menu__list {\r\n padding: 4px 0;\r\n}\r\n\r\n.side-menu__group {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.side-menu__group-label {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 12px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n opacity: 0.5;\r\n}\r\n\r\n.side-menu__item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 9px 12px;\r\n margin: 2px 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.side-menu__item:hover {\r\n background: rgba(99, 102, 241, 0.06);\r\n}\r\n\r\n.side-menu__item.active {\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.1),\r\n rgba(139, 92, 246, 0.06)\r\n );\r\n color: #667eea;\r\n font-weight: 500;\r\n}\r\n\r\n.side-menu__item-text {\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Transition */\r\n.glass-slide-enter-active,\r\n.glass-slide-leave-active {\r\n transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.glass-slide-enter-from,\r\n.glass-slide-leave-to {\r\n opacity: 0;\r\n transform: translateX(20px);\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - SideMenu\r\n *\r\n * 右侧二级菜单侧边栏\r\n * 用于 C_ReverseHorizontalMixLayout 的右侧二级菜单\n -->\r\n<template>\r\n <template v-if=\"menus.length > 0\">\r\n <!-- 折叠/展开按钮 -->\r\n <div\r\n class=\"side-menu-toggle\"\r\n :class=\"[\r\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\r\n { active: !isCollapsed },\r\n ]\"\r\n @click=\"isCollapsed = !isCollapsed\"\r\n >\r\n <i\r\n :class=\"[\r\n 'toggle-icon',\r\n isCollapsed ? 'i-ri:side-bar-line' : 'i-ri:arrow-right-s-line',\r\n ]\"\r\n ></i>\r\n </div>\r\n\r\n <!-- 侧边栏内容 -->\r\n <Transition name=\"glass-slide\">\r\n <div\r\n v-if=\"!isCollapsed\"\r\n class=\"side-menu\"\r\n :class=\"[ctx.isDark.value ? 'dark-theme' : 'light-theme']\"\r\n >\r\n <!-- 标题 -->\r\n <div class=\"side-menu__header\">\r\n <div class=\"side-menu__header-icon\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && activeItem?.meta?.icon\"\r\n :name=\"activeItem.meta.icon\"\r\n :size=\"17\"\r\n />\r\n </div>\r\n <span class=\"side-menu__header-title\">{{\r\n activeItem?.meta?.title || \"菜单\"\r\n }}</span>\r\n </div>\r\n\r\n <!-- 菜单列表 -->\r\n <div class=\"side-menu__list\">\r\n <template v-for=\"child in menus\" :key=\"child.path\">\r\n <!-- 有子菜单 -->\r\n <div\r\n v-if=\"child.children && child.children.length > 0\"\r\n class=\"side-menu__group\"\r\n >\r\n <div class=\"side-menu__group-label\">\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"14\"\r\n />\r\n <span>{{ child.meta?.title }}</span>\r\n </div>\r\n <div\r\n v-for=\"subChild in child.children\"\r\n :key=\"subChild.path\"\r\n class=\"side-menu__item\"\r\n :class=\"{ active: isActive?.(subChild.path) }\"\r\n @click=\"emit('click', subChild)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && subChild.meta?.icon\"\r\n :name=\"subChild.meta.icon\"\r\n :size=\"15\"\r\n />\r\n <span class=\"side-menu__item-text\">{{\r\n subChild.meta?.title\r\n }}</span>\r\n </div>\r\n </div>\r\n <!-- 无子菜单 -->\r\n <div\r\n v-else\r\n class=\"side-menu__item\"\r\n :class=\"{ active: isActive?.(child.path) }\"\r\n @click=\"emit('click', child)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && child.meta?.icon\"\r\n :name=\"child.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"side-menu__item-text\">{{ child.meta?.title }}</span>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </Transition>\r\n </template>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref } from \"vue\";\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"SideMenu\" });\r\n\r\ndefineProps<{\r\n /** 二级菜单列表 */\r\n menus: MenuOptions[];\r\n /** 当前一级菜单项(显示标题和图标) */\r\n activeItem?: MenuOptions;\r\n /** 判断菜单项是否激活 */\r\n isActive?: (path: string | undefined) => boolean;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n click: [item: MenuOptions];\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\nconst isCollapsed = ref(false);\r\n</script>\r\n\r\n<style scoped>\r\n.side-menu-toggle {\r\n position: absolute;\r\n right: 0;\r\n top: 50%;\r\n transform: translateY(-50%) translateX(50%);\r\n width: 24px;\r\n height: 48px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border-radius: 12px;\r\n cursor: pointer;\r\n z-index: 10;\r\n transition: all 0.25s ease;\r\n font-size: 14px;\r\n}\r\n\r\n.side-menu-toggle.light-theme {\r\n background: rgba(255, 255, 255, 0.9);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\r\n}\r\n\r\n.side-menu-toggle.dark-theme {\r\n background: rgba(40, 40, 46, 0.9);\r\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.side-menu-toggle:hover {\r\n transform: translateY(-50%) translateX(50%) scale(1.1);\r\n}\r\n\r\n.side-menu {\r\n width: 100%;\r\n height: 100%;\r\n overflow-y: auto;\r\n padding: 0 8px;\r\n}\r\n\r\n.side-menu.light-theme {\r\n background: rgba(255, 255, 255, 0.95);\r\n border-left: 1px solid rgba(0, 0, 0, 0.06);\r\n}\r\n\r\n.side-menu.dark-theme {\r\n background: rgba(30, 30, 36, 0.95);\r\n border-left: 1px solid rgba(255, 255, 255, 0.06);\r\n}\r\n\r\n.side-menu__header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 16px 12px 12px;\r\n border-bottom: 1px solid rgba(128, 128, 128, 0.1);\r\n margin-bottom: 8px;\r\n}\r\n\r\n.side-menu__header-icon {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n border-radius: 8px;\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.12),\r\n rgba(139, 92, 246, 0.08)\r\n );\r\n}\r\n\r\n.side-menu__header-title {\r\n font-size: 14px;\r\n font-weight: 600;\r\n}\r\n\r\n.side-menu__list {\r\n padding: 4px 0;\r\n}\r\n\r\n.side-menu__group {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.side-menu__group-label {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 12px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n opacity: 0.5;\r\n}\r\n\r\n.side-menu__item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 9px 12px;\r\n margin: 2px 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.side-menu__item:hover {\r\n background: rgba(99, 102, 241, 0.06);\r\n}\r\n\r\n.side-menu__item.active {\r\n background: linear-gradient(\r\n 135deg,\r\n rgba(99, 102, 241, 0.1),\r\n rgba(139, 92, 246, 0.06)\r\n );\r\n color: #667eea;\r\n font-weight: 500;\r\n}\r\n\r\n.side-menu__item-text {\r\n flex: 1;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n/* Transition */\r\n.glass-slide-enter-active,\r\n.glass-slide-leave-active {\r\n transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);\r\n}\r\n\r\n.glass-slide-enter-from,\r\n.glass-slide-leave-to {\r\n opacity: 0;\r\n transform: translateX(20px);\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - DrawerMenu\r\n *\r\n * 抽屉式网格菜单\r\n * 用于 C_CardLayout 的 hover 触发的抽屉式功能导航\n -->\r\n<template>\r\n <div\r\n class=\"drawer-menu\"\r\n :class=\"[\r\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\r\n { visible: show },\r\n ]\"\r\n role=\"navigation\"\r\n aria-label=\"功能导航\"\r\n :aria-hidden=\"!show\"\r\n @mouseenter=\"handlers.show()\"\r\n @mouseleave=\"handlers.startHide()\"\r\n >\r\n <div class=\"drawer-menu__header\">\r\n <div class=\"drawer-menu__title\">\r\n <!-- 纯 CSS 网格图标 -->\r\n <span class=\"grid-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n <span>功能导航</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"drawer-menu__content\">\r\n <div class=\"drawer-menu__grid\">\r\n <div\r\n v-for=\"category in menus\"\r\n :key=\"category.path\"\r\n class=\"drawer-menu__category\"\r\n >\r\n <div\r\n class=\"drawer-menu__category-header\"\r\n role=\"link\"\r\n tabindex=\"0\"\r\n @click=\"navigate(category)\"\r\n @keydown.enter=\"navigate(category)\"\r\n @keydown.space.prevent=\"navigate(category)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && category.meta?.icon\"\r\n :name=\"category.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span>{{ category.meta?.title }}</span>\r\n </div>\r\n\r\n <div\r\n v-if=\"category.children && category.children.length > 0\"\r\n class=\"drawer-menu__items\"\r\n >\r\n <div\r\n v-for=\"item in category.children\"\r\n :key=\"item.path\"\r\n class=\"drawer-menu__item\"\r\n role=\"link\"\r\n tabindex=\"0\"\r\n @click=\"navigate(item)\"\r\n @keydown.enter=\"navigate(item)\"\r\n @keydown.space.prevent=\"navigate(item)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"drawer-menu__item-text\">{{ item.meta?.title }}</span>\r\n </div>\r\n\r\n <template v-for=\"item in category.children\" :key=\"item.path\">\r\n <div\r\n v-if=\"item.children && item.children.length > 0\"\r\n class=\"drawer-menu__sub-items\"\r\n >\r\n <div class=\"drawer-menu__sub-title\">\r\n {{ item.meta?.title }}\r\n </div>\r\n <div\r\n v-for=\"subItem in item.children\"\r\n :key=\"subItem.path\"\r\n class=\"drawer-menu__item drawer-menu__item--sub\"\r\n role=\"link\"\r\n tabindex=\"0\"\r\n @click=\"navigate(subItem)\"\r\n @keydown.enter=\"navigate(subItem)\"\r\n @keydown.space.prevent=\"navigate(subItem)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && subItem.meta?.icon\"\r\n :name=\"subItem.meta.icon\"\r\n :size=\"14\"\r\n />\r\n <span class=\"drawer-menu__item-text\">{{\r\n subItem.meta?.title\r\n }}</span>\r\n </div>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { inject } from \"vue\";\r\nimport { useRouter } from \"vue-router\";\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\nimport {\r\n DRAWER_HANDLER_KEY,\r\n type DrawerHandlers,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"DrawerMenu\" });\r\n\r\ndefineProps<{\r\n /** 菜单数据 */\r\n menus: MenuOptions[];\r\n /** 是否显示 */\r\n show: boolean;\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\nconst router = useRouter();\r\n\r\n// 使用共享的 drawer 控制器(由 C_LayoutContainer provide)\r\nconst handlers = inject<DrawerHandlers>(DRAWER_HANDLER_KEY, {\r\n show: () => {},\r\n startHide: () => {},\r\n hide: () => {},\r\n});\r\n\r\nconst navigate = (item: MenuOptions) => {\r\n if (item.path) {\r\n void router.push(item.path);\r\n handlers.hide();\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped>\r\n.drawer-menu {\r\n position: absolute;\r\n top: 100%;\r\n left: 0;\r\n right: 0;\r\n z-index: 100;\r\n max-height: 60vh;\r\n overflow-y: auto;\r\n opacity: 0;\r\n visibility: hidden;\r\n transform: translateY(-8px);\r\n pointer-events: none;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n backdrop-filter: blur(20px);\r\n -webkit-backdrop-filter: blur(20px);\r\n border-bottom-left-radius: 16px;\r\n border-bottom-right-radius: 16px;\r\n}\r\n\r\n.drawer-menu.visible {\r\n opacity: 1;\r\n visibility: visible;\r\n transform: translateY(0);\r\n pointer-events: all;\r\n}\r\n\r\n.drawer-menu.light-theme {\r\n background: rgba(255, 255, 255, 0.96);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);\r\n border: 1px solid rgba(0, 0, 0, 0.06);\r\n border-top: none;\r\n}\r\n\r\n.drawer-menu.dark-theme {\r\n background: rgba(30, 30, 36, 0.96);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\r\n border: 1px solid rgba(255, 255, 255, 0.06);\r\n border-top: none;\r\n}\r\n\r\n.drawer-menu__header {\r\n padding: 16px 24px 8px;\r\n}\r\n\r\n.drawer-menu__title {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n font-size: 15px;\r\n font-weight: 600;\r\n}\r\n\r\n.drawer-menu__content {\r\n padding: 8px 24px 20px;\r\n}\r\n\r\n.drawer-menu__grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\r\n gap: 16px;\r\n}\r\n\r\n.drawer-menu__category {\r\n padding: 0;\r\n}\r\n\r\n.drawer-menu__category-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 8px 12px;\r\n font-size: 14px;\r\n font-weight: 600;\r\n cursor: pointer;\r\n border-radius: 8px;\r\n transition: background 0.2s ease;\r\n}\r\n\r\n.drawer-menu__category-header:hover {\r\n background: rgba(99, 102, 241, 0.06);\r\n}\r\n\r\n.drawer-menu__category-header:focus-visible,\r\n.drawer-menu__item:focus-visible {\r\n outline: 2px solid #667eea;\r\n outline-offset: 2px;\r\n}\r\n\r\n.drawer-menu__items {\r\n padding-left: 8px;\r\n}\r\n\r\n.drawer-menu__item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 7px 12px;\r\n margin: 1px 0;\r\n border-radius: 6px;\r\n font-size: 13px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.drawer-menu__item:hover {\r\n background: rgba(99, 102, 241, 0.08);\r\n color: #667eea;\r\n}\r\n\r\n.drawer-menu__item--sub {\r\n padding-left: 16px;\r\n font-size: 12px;\r\n}\r\n\r\n.drawer-menu__item-text {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.drawer-menu__sub-items {\r\n padding: 4px 0;\r\n}\r\n\r\n.drawer-menu__sub-title {\r\n padding: 4px 12px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n opacity: 0.4;\r\n}\r\n\r\n/* 纯 CSS 网格图标 (2x2) */\r\n.grid-icon {\r\n display: inline-grid;\r\n grid-template-columns: repeat(2, 1fr);\r\n gap: 2.5px;\r\n width: 18px;\r\n height: 18px;\r\n padding: 1px;\r\n}\r\n\r\n.grid-icon span {\r\n background-color: currentColor;\r\n border-radius: 2px;\r\n opacity: 0.85;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - DrawerMenu\r\n *\r\n * 抽屉式网格菜单\r\n * 用于 C_CardLayout 的 hover 触发的抽屉式功能导航\n -->\r\n<template>\r\n <div\r\n class=\"drawer-menu\"\r\n :class=\"[\r\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\r\n { visible: show },\r\n ]\"\r\n role=\"navigation\"\r\n aria-label=\"功能导航\"\r\n :aria-hidden=\"!show\"\r\n @mouseenter=\"handlers.show()\"\r\n @mouseleave=\"handlers.startHide()\"\r\n >\r\n <div class=\"drawer-menu__header\">\r\n <div class=\"drawer-menu__title\">\r\n <!-- 纯 CSS 网格图标 -->\r\n <span class=\"grid-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n <span>功能导航</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"drawer-menu__content\">\r\n <div class=\"drawer-menu__grid\">\r\n <div\r\n v-for=\"category in menus\"\r\n :key=\"category.path\"\r\n class=\"drawer-menu__category\"\r\n >\r\n <div\r\n class=\"drawer-menu__category-header\"\r\n role=\"link\"\r\n tabindex=\"0\"\r\n @click=\"navigate(category)\"\r\n @keydown.enter=\"navigate(category)\"\r\n @keydown.space.prevent=\"navigate(category)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && category.meta?.icon\"\r\n :name=\"category.meta.icon\"\r\n :size=\"18\"\r\n />\r\n <span>{{ category.meta?.title }}</span>\r\n </div>\r\n\r\n <div\r\n v-if=\"category.children && category.children.length > 0\"\r\n class=\"drawer-menu__items\"\r\n >\r\n <div\r\n v-for=\"item in category.children\"\r\n :key=\"item.path\"\r\n class=\"drawer-menu__item\"\r\n role=\"link\"\r\n tabindex=\"0\"\r\n @click=\"navigate(item)\"\r\n @keydown.enter=\"navigate(item)\"\r\n @keydown.space.prevent=\"navigate(item)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && item.meta?.icon\"\r\n :name=\"item.meta.icon\"\r\n :size=\"16\"\r\n />\r\n <span class=\"drawer-menu__item-text\">{{ item.meta?.title }}</span>\r\n </div>\r\n\r\n <template v-for=\"item in category.children\" :key=\"item.path\">\r\n <div\r\n v-if=\"item.children && item.children.length > 0\"\r\n class=\"drawer-menu__sub-items\"\r\n >\r\n <div class=\"drawer-menu__sub-title\">\r\n {{ item.meta?.title }}\r\n </div>\r\n <div\r\n v-for=\"subItem in item.children\"\r\n :key=\"subItem.path\"\r\n class=\"drawer-menu__item drawer-menu__item--sub\"\r\n role=\"link\"\r\n tabindex=\"0\"\r\n @click=\"navigate(subItem)\"\r\n @keydown.enter=\"navigate(subItem)\"\r\n @keydown.space.prevent=\"navigate(subItem)\"\r\n >\r\n <component\r\n :is=\"ctx.iconComponent\"\r\n v-if=\"ctx.iconComponent && subItem.meta?.icon\"\r\n :name=\"subItem.meta.icon\"\r\n :size=\"14\"\r\n />\r\n <span class=\"drawer-menu__item-text\">{{\r\n subItem.meta?.title\r\n }}</span>\r\n </div>\r\n </div>\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { inject } from \"vue\";\r\nimport { useRouter } from \"vue-router\";\r\nimport type { MenuOptions } from \"../../types/menu\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\nimport {\r\n DRAWER_HANDLER_KEY,\r\n type DrawerHandlers,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"DrawerMenu\" });\r\n\r\ndefineProps<{\r\n /** 菜单数据 */\r\n menus: MenuOptions[];\r\n /** 是否显示 */\r\n show: boolean;\r\n}>();\r\n\r\nconst ctx = useLayoutContext();\r\nconst router = useRouter();\r\n\r\n// 使用共享的 drawer 控制器(由 C_LayoutContainer provide)\r\nconst handlers = inject<DrawerHandlers>(DRAWER_HANDLER_KEY, {\r\n show: () => {},\r\n startHide: () => {},\r\n hide: () => {},\r\n});\r\n\r\nconst navigate = (item: MenuOptions) => {\r\n if (item.path) {\r\n void router.push(item.path);\r\n handlers.hide();\r\n }\r\n};\r\n</script>\r\n\r\n<style scoped>\r\n.drawer-menu {\r\n position: absolute;\r\n top: 100%;\r\n left: 0;\r\n right: 0;\r\n z-index: 100;\r\n max-height: 60vh;\r\n overflow-y: auto;\r\n opacity: 0;\r\n visibility: hidden;\r\n transform: translateY(-8px);\r\n pointer-events: none;\r\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\r\n backdrop-filter: blur(20px);\r\n -webkit-backdrop-filter: blur(20px);\r\n border-bottom-left-radius: 16px;\r\n border-bottom-right-radius: 16px;\r\n}\r\n\r\n.drawer-menu.visible {\r\n opacity: 1;\r\n visibility: visible;\r\n transform: translateY(0);\r\n pointer-events: all;\r\n}\r\n\r\n.drawer-menu.light-theme {\r\n background: rgba(255, 255, 255, 0.96);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);\r\n border: 1px solid rgba(0, 0, 0, 0.06);\r\n border-top: none;\r\n}\r\n\r\n.drawer-menu.dark-theme {\r\n background: rgba(30, 30, 36, 0.96);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);\r\n border: 1px solid rgba(255, 255, 255, 0.06);\r\n border-top: none;\r\n}\r\n\r\n.drawer-menu__header {\r\n padding: 16px 24px 8px;\r\n}\r\n\r\n.drawer-menu__title {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n font-size: 15px;\r\n font-weight: 600;\r\n}\r\n\r\n.drawer-menu__content {\r\n padding: 8px 24px 20px;\r\n}\r\n\r\n.drawer-menu__grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\r\n gap: 16px;\r\n}\r\n\r\n.drawer-menu__category {\r\n padding: 0;\r\n}\r\n\r\n.drawer-menu__category-header {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 8px 12px;\r\n font-size: 14px;\r\n font-weight: 600;\r\n cursor: pointer;\r\n border-radius: 8px;\r\n transition: background 0.2s ease;\r\n}\r\n\r\n.drawer-menu__category-header:hover {\r\n background: rgba(99, 102, 241, 0.06);\r\n}\r\n\r\n.drawer-menu__category-header:focus-visible,\r\n.drawer-menu__item:focus-visible {\r\n outline: 2px solid #667eea;\r\n outline-offset: 2px;\r\n}\r\n\r\n.drawer-menu__items {\r\n padding-left: 8px;\r\n}\r\n\r\n.drawer-menu__item {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 7px 12px;\r\n margin: 1px 0;\r\n border-radius: 6px;\r\n font-size: 13px;\r\n cursor: pointer;\r\n transition: all 0.2s ease;\r\n}\r\n\r\n.drawer-menu__item:hover {\r\n background: rgba(99, 102, 241, 0.08);\r\n color: #667eea;\r\n}\r\n\r\n.drawer-menu__item--sub {\r\n padding-left: 16px;\r\n font-size: 12px;\r\n}\r\n\r\n.drawer-menu__item-text {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.drawer-menu__sub-items {\r\n padding: 4px 0;\r\n}\r\n\r\n.drawer-menu__sub-title {\r\n padding: 4px 12px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n opacity: 0.4;\r\n}\r\n\r\n/* 纯 CSS 网格图标 (2x2) */\r\n.grid-icon {\r\n display: inline-grid;\r\n grid-template-columns: repeat(2, 1fr);\r\n gap: 2.5px;\r\n width: 18px;\r\n height: 18px;\r\n padding: 1px;\r\n}\r\n\r\n.grid-icon span {\r\n background-color: currentColor;\r\n border-radius: 2px;\r\n opacity: 0.85;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - MenuTrigger\r\n *\r\n * 菜单触发区域\r\n * 用于 C_CardLayout 的 hover 触发抽屉式菜单\n * 使用共享的 drawer 控制器避免与 DrawerMenu 定时器竞争\r\n -->\r\n<template>\r\n <div\r\n class=\"menu-trigger\"\r\n @mouseenter=\"handlers.show()\"\r\n @mouseleave=\"handlers.startHide()\"\r\n >\r\n <div class=\"menu-trigger__indicator\">\r\n <!-- 纯 CSS 三横线菜单图标 -->\r\n <span class=\"hamburger-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { inject } from \"vue\";\r\nimport {\r\n DRAWER_HANDLER_KEY,\r\n type DrawerHandlers,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"MenuTrigger\" });\r\n\r\ndefineProps<{\r\n show?: boolean;\r\n}>();\r\n\r\n// 使用共享控制器(由 C_LayoutContainer provide)\r\nconst handlers = inject<DrawerHandlers>(DRAWER_HANDLER_KEY, {\r\n show: () => {},\r\n startHide: () => {},\r\n hide: () => {},\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.menu-trigger {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 0 8px;\r\n height: 100%;\r\n cursor: pointer;\r\n}\r\n\r\n.menu-trigger__indicator {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 36px;\r\n height: 36px;\r\n border-radius: 8px;\r\n font-size: 18px;\r\n transition: all 0.25s ease;\r\n opacity: 0.6;\r\n}\r\n\r\n.menu-trigger__indicator:hover {\r\n opacity: 1;\r\n background: rgba(99, 102, 241, 0.08);\r\n}\r\n\r\n/* 纯 CSS 三横线汉堡菜单图标 */\r\n.hamburger-icon {\r\n display: inline-flex;\r\n flex-direction: column;\r\n justify-content: space-between;\r\n width: 20px;\r\n height: 16px;\r\n padding: 2px 0;\r\n}\r\n\r\n.hamburger-icon span {\r\n display: block;\r\n width: 100%;\r\n height: 2px;\r\n background-color: currentColor;\r\n border-radius: 1px;\r\n opacity: 0.85;\r\n transition: all 0.2s ease;\r\n}\r\n</style>\r\n","<!--\r\n * @robot-admin/layout - MenuTrigger\r\n *\r\n * 菜单触发区域\r\n * 用于 C_CardLayout 的 hover 触发抽屉式菜单\n * 使用共享的 drawer 控制器避免与 DrawerMenu 定时器竞争\r\n -->\r\n<template>\r\n <div\r\n class=\"menu-trigger\"\r\n @mouseenter=\"handlers.show()\"\r\n @mouseleave=\"handlers.startHide()\"\r\n >\r\n <div class=\"menu-trigger__indicator\">\r\n <!-- 纯 CSS 三横线菜单图标 -->\r\n <span class=\"hamburger-icon\">\r\n <span></span>\r\n <span></span>\r\n <span></span>\r\n </span>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { inject } from \"vue\";\r\nimport {\r\n DRAWER_HANDLER_KEY,\r\n type DrawerHandlers,\r\n} from \"../../composables/useLayoutContext\";\r\n\r\ndefineOptions({ name: \"MenuTrigger\" });\r\n\r\ndefineProps<{\r\n show?: boolean;\r\n}>();\r\n\r\n// 使用共享控制器(由 C_LayoutContainer provide)\r\nconst handlers = inject<DrawerHandlers>(DRAWER_HANDLER_KEY, {\r\n show: () => {},\r\n startHide: () => {},\r\n hide: () => {},\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.menu-trigger {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 0 8px;\r\n height: 100%;\r\n cursor: pointer;\r\n}\r\n\r\n.menu-trigger__indicator {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 36px;\r\n height: 36px;\r\n border-radius: 8px;\r\n font-size: 18px;\r\n transition: all 0.25s ease;\r\n opacity: 0.6;\r\n}\r\n\r\n.menu-trigger__indicator:hover {\r\n opacity: 1;\r\n background: rgba(99, 102, 241, 0.08);\r\n}\r\n\r\n/* 纯 CSS 三横线汉堡菜单图标 */\r\n.hamburger-icon {\r\n display: inline-flex;\r\n flex-direction: column;\r\n justify-content: space-between;\r\n width: 20px;\r\n height: 16px;\r\n padding: 2px 0;\r\n}\r\n\r\n.hamburger-icon span {\r\n display: block;\r\n width: 100%;\r\n height: 2px;\r\n background-color: currentColor;\r\n border-radius: 1px;\r\n opacity: 0.85;\r\n transition: all 0.2s ease;\r\n}\r\n</style>\r\n","import C_SideLayoutComponent from \"../components/layouts/SideLayout/index.vue\";\nimport C_TopLayoutComponent from \"../components/layouts/TopLayout/index.vue\";\nimport C_MixLayoutComponent from \"../components/layouts/MixLayout/index.vue\";\nimport C_MixTopLayoutComponent from \"../components/layouts/MixTopLayout/index.vue\";\nimport C_ReverseHorizontalMixLayoutComponent from \"../components/layouts/ReverseHorizontalMixLayout/index.vue\";\nimport C_CardLayoutComponent from \"../components/layouts/CardLayout/index.vue\";\n\n/** Naive UI 完整适配入口:聚合 Vue Headless 能力与 Naive 视图。 */\nexport * from \"../vue\";\n\nexport { default as C_LayoutContainer } from \"../components/C_LayoutContainer/index.vue\";\nexport { default as SettingsDrawer } from \"../components/SettingsDrawer/index.vue\";\n\nexport { default as BrandLogo } from \"../components/BrandLogo/index.vue\";\nexport { default as IconMenu } from \"../components/IconMenu/index.vue\";\nexport { default as FloatingMenu } from \"../components/FloatingMenu/index.vue\";\nexport { default as SideMenu } from \"../components/SideMenu/index.vue\";\nexport { default as DrawerMenu } from \"../components/DrawerMenu/index.vue\";\nexport { default as MenuTrigger } from \"../components/MenuTrigger/index.vue\";\nexport { default as ResponsiveMenu } from \"../components/ResponsiveMenu/index.vue\";\n\nexport const C_SideLayout = C_SideLayoutComponent;\nexport const C_TopLayout = C_TopLayoutComponent;\nexport const C_MixLayout = C_MixLayoutComponent;\nexport const C_MixTopLayout = C_MixTopLayoutComponent;\nexport const C_ReverseHorizontalMixLayout =\n C_ReverseHorizontalMixLayoutComponent;\nexport const C_CardLayout = C_CardLayoutComponent;\n\n/** @deprecated 请使用 C_SideLayout;计划在 4.0 移除。 */\nexport const SideLayout = C_SideLayout;\n/** @deprecated 请使用 C_TopLayout;计划在 4.0 移除。 */\nexport const TopLayout = C_TopLayout;\n/** @deprecated 请使用 C_MixLayout;计划在 4.0 移除。 */\nexport const MixLayout = C_MixLayout;\n/** @deprecated 请使用 C_MixTopLayout;计划在 4.0 移除。 */\nexport const MixTopLayout = C_MixTopLayout;\n/** @deprecated 请使用 C_ReverseHorizontalMixLayout;计划在 4.0 移除。 */\nexport const ReverseHorizontalMixLayout = C_ReverseHorizontalMixLayout;\n/** @deprecated 请使用 C_CardLayout;计划在 4.0 移除。 */\nexport const CardLayout = C_CardLayout;\n"],"mappings":"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"}