@robot-admin/layout 2.1.0 → 2.3.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.
- package/README.md +5 -2
- package/dist/index.cjs +9 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +2 -1
- package/dist/index.d.ts +23 -3
- package/dist/index.js +1969 -2596
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/styles/layouts.scss +78 -34
- package/src/styles/settings.scss +5 -5
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/constants/index.ts","../src/stores/settings.ts","../src/composables/useLayoutContext.ts","../src/composables/useLayoutCache.ts","../src/components/layouts/SideLayout/index.vue","../src/components/ResponsiveMenu/index.vue","../src/components/layouts/TopLayout/index.vue","../src/composables/useMenuSplit.ts","../src/components/layouts/MixLayout/index.vue","../src/components/layouts/MixTopLayout/index.vue","../src/components/layouts/ReverseHorizontalMixLayout/index.vue","../src/components/layouts/CardLayout/index.vue","../src/components/C_LayoutContainer/index.vue","../src/components/SettingsDrawer/data.ts","../src/components/SettingsDrawer/index.vue","../src/components/BrandLogo/index.vue","../src/components/IconMenu/index.vue","../src/components/FloatingMenu/index.vue","../src/components/SideMenu/index.vue","../src/components/DrawerMenu/index.vue","../src/components/MenuTrigger/index.vue","../src/setup.ts"],"sourcesContent":["/**\n * 布局和设置系统 - 常量定义\n */\n\nimport type {\n ThemePreset,\n SettingsState,\n LayoutInfo,\n PresetColor,\n} from \"../types\";\n\n// ============ 主题色预设 ============\nexport const PRESET_COLORS: PresetColor[] = [\n { name: \"拂晓蓝\", value: \"#409eff\" },\n { name: \"薄暮红\", value: \"#f5222d\" },\n { name: \"火山橙\", value: \"#fa541c\" },\n { name: \"日暮黄\", value: \"#faad14\" },\n { name: \"极光绿\", value: \"#52c41a\" },\n { name: \"明青\", value: \"#13c2c2\" },\n { name: \"极客蓝\", value: \"#2f54eb\" },\n { name: \"酱紫\", value: \"#722ed1\" },\n] as const;\n\n// ============ 主题预设方案 ============\nexport const THEME_PRESETS: ThemePreset[] = [\n {\n name: \"科技蓝\",\n icon: \"💙\",\n primaryColor: \"#409eff\",\n },\n {\n name: \"清新绿\",\n icon: \"💚\",\n primaryColor: \"#52c41a\",\n },\n {\n name: \"商务灰\",\n icon: \"🖤\",\n primaryColor: \"#595959\",\n },\n {\n name: \"活力橙\",\n icon: \"🧡\",\n primaryColor: \"#fa8c16\",\n },\n {\n name: \"优雅紫\",\n icon: \"💜\",\n primaryColor: \"#722ed1\",\n },\n {\n name: \"经典红\",\n icon: \"❤️\",\n primaryColor: \"#f5222d\",\n },\n];\n\n// ============ 布局模式信息 ============\nexport const LAYOUT_MODES: LayoutInfo[] = [\n {\n mode: \"side\",\n label: \"左侧菜单\",\n description: \"经典的左侧导航布局\",\n icon: \"i-mdi:page-layout-sidebar-left\",\n },\n {\n mode: \"top\",\n label: \"顶部菜单\",\n description: \"顶部水平导航布局\",\n icon: \"i-mdi:page-layout-header\",\n },\n {\n mode: \"mix\",\n label: \"混合菜单\",\n description: \"顶部 + 侧边混合布局\",\n icon: \"i-mdi:page-layout-header-sidebar-left\",\n },\n {\n mode: \"mix-top\",\n label: \"顶部混合\",\n description: \"侧边优先的混合布局\",\n icon: \"i-mdi:page-layout-sidebar-left-header\",\n },\n {\n mode: \"reverse-horizontal-mix\",\n label: \"反转混合\",\n description: \"反转的水平混合布局\",\n icon: \"i-mdi:page-layout-header-footer\",\n },\n {\n mode: \"card-layout\",\n label: \"卡片布局\",\n description: \"卡片式布局风格\",\n icon: \"i-mdi:card-outline\",\n },\n];\n\n// ============ 圆角映射 ============\nexport const BORDER_RADIUS_MAP = {\n small: \"4px\",\n medium: \"6px\",\n large: \"8px\",\n} as const;\n\n// ============ 动画映射 ============\nexport const TRANSITION_MAP = {\n fade: \"fade-transform\",\n slide: \"fade-slide\",\n zoom: \"fade-zoom\",\n none: \"\",\n} as const;\n\n// ============ 默认配置 ============\nexport const DEFAULT_SETTINGS: SettingsState = {\n // 外观\n themeMode: \"light\",\n primaryColor: \"#409eff\",\n borderRadius: \"medium\",\n transitionType: \"slide\",\n enableTransition: true,\n\n // 布局\n layoutMode: \"side\",\n collapsed: false,\n fixedHeader: true,\n showBreadcrumb: true,\n showBreadcrumbIcon: true,\n showTagsView: true,\n tagsViewHeight: 44,\n tagsViewStyle: \"default\",\n showFooter: true,\n sidebarWidth: 220,\n sidebarCollapsedWidth: 64,\n headerHeight: 56,\n\n // 高级\n enableHotkeys: true,\n version: \"2.0.2\",\n};\n\n// ============ 其他常量 ============\n\n/** Color swatches for color picker */\nexport const COLOR_SWATCHES = PRESET_COLORS.map((c) => c.value);\n","import { defineStore } from \"pinia\";\nimport { ref, computed, watch } from \"vue\";\nimport type { ThemeMode } from \"@robot-admin/theme\";\nimport type {\n SettingsState,\n ThemePreset,\n LayoutMode,\n TransitionType,\n BorderRadiusSize,\n TagsViewStyle,\n SettingsStoreOptions,\n} from \"../types\";\nimport {\n DEFAULT_SETTINGS,\n BORDER_RADIUS_MAP,\n TRANSITION_MAP,\n} from \"../constants\";\n\n/**\n * 调整颜色亮度\n * @param color - 十六进制颜色值\n * @param amount - 调整量,正数变亮,负数变暗\n */\nexport function adjustColor(color: string, amount: number): string {\n const num = parseInt(color.replace(\"#\", \"\"), 16);\n const r = Math.min(255, Math.max(0, (num >> 16) + amount));\n const g = Math.min(255, Math.max(0, ((num >> 8) & 0x00ff) + amount));\n const b = Math.min(255, Math.max(0, (num & 0x0000ff) + amount));\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, \"0\")}`;\n}\n\n/**\n * 创建设置管理 Store\n * @param options - 配置选项\n */\nexport function createSettingsStore(options: SettingsStoreOptions = {}) {\n const { defaults = {}, onThemeModeChange } = options;\n\n // 合并默认配置\n const finalDefaults = {\n ...DEFAULT_SETTINGS,\n ...defaults,\n };\n\n return defineStore(\"settings\", () => {\n // ============ 状态定义 ============\n\n // 外观设置\n const themeMode = ref<ThemeMode>(finalDefaults.themeMode);\n const primaryColor = ref<string>(finalDefaults.primaryColor);\n const borderRadius = ref<BorderRadiusSize>(finalDefaults.borderRadius);\n const transitionType = ref<TransitionType>(finalDefaults.transitionType);\n const enableTransition = ref<boolean>(finalDefaults.enableTransition);\n\n // 布局设置\n const layoutMode = ref<LayoutMode>(finalDefaults.layoutMode);\n const collapsed = ref<boolean>(finalDefaults.collapsed);\n const fixedHeader = ref<boolean>(finalDefaults.fixedHeader);\n const showBreadcrumb = ref<boolean>(finalDefaults.showBreadcrumb);\n const showBreadcrumbIcon = ref<boolean>(finalDefaults.showBreadcrumbIcon);\n const showTagsView = ref<boolean>(finalDefaults.showTagsView);\n const tagsViewHeight = ref<number>(finalDefaults.tagsViewHeight);\n const tagsViewStyle = ref<TagsViewStyle>(finalDefaults.tagsViewStyle);\n const showFooter = ref<boolean>(finalDefaults.showFooter);\n const sidebarWidth = ref<number>(finalDefaults.sidebarWidth);\n const sidebarCollapsedWidth = ref<number>(\n finalDefaults.sidebarCollapsedWidth,\n );\n const headerHeight = ref<number>(finalDefaults.headerHeight);\n\n // 高级设置\n const enableHotkeys = ref<boolean>(finalDefaults.enableHotkeys);\n const version = ref<string>(finalDefaults.version);\n\n // ============ 计算属性 ============\n\n /** 获取圆角值 */\n const borderRadiusValue = computed(\n () => BORDER_RADIUS_MAP[borderRadius.value],\n );\n\n /** 获取过渡动画名称 */\n const transitionName = computed(() => TRANSITION_MAP[transitionType.value]);\n\n /** 是否启用过渡动画 */\n const shouldEnableTransition = computed(\n () => enableTransition.value && transitionType.value !== \"none\",\n );\n\n /** 获取完整的设置状态 */\n const settingsState = computed<SettingsState>(() => ({\n themeMode: themeMode.value,\n primaryColor: primaryColor.value,\n borderRadius: borderRadius.value,\n transitionType: transitionType.value,\n enableTransition: enableTransition.value,\n layoutMode: layoutMode.value,\n collapsed: collapsed.value,\n fixedHeader: fixedHeader.value,\n showBreadcrumb: showBreadcrumb.value,\n showBreadcrumbIcon: showBreadcrumbIcon.value,\n showTagsView: showTagsView.value,\n tagsViewHeight: tagsViewHeight.value,\n tagsViewStyle: tagsViewStyle.value,\n showFooter: showFooter.value,\n sidebarWidth: sidebarWidth.value,\n sidebarCollapsedWidth: sidebarCollapsedWidth.value,\n headerHeight: headerHeight.value,\n enableHotkeys: enableHotkeys.value,\n version: version.value,\n }));\n\n // ============ 方法 ============\n\n /**\n * 同步 CSS Variables\n */\n const syncCSSVariables = () => {\n if (typeof document === \"undefined\") return;\n\n const root = document.documentElement;\n\n // 主题色\n root.style.setProperty(\"--primary-color\", primaryColor.value);\n root.style.setProperty(\n \"--primary-color-hover\",\n adjustColor(primaryColor.value, 10),\n );\n root.style.setProperty(\n \"--primary-color-pressed\",\n adjustColor(primaryColor.value, -10),\n );\n\n // 圆角\n root.style.setProperty(\"--border-radius\", borderRadiusValue.value);\n\n // 尺寸\n root.style.setProperty(\"--sidebar-width\", `${sidebarWidth.value}px`);\n root.style.setProperty(\n \"--sidebar-collapsed-width\",\n `${sidebarCollapsedWidth.value}px`,\n );\n root.style.setProperty(\"--header-height\", `${headerHeight.value}px`);\n root.style.setProperty(\"--tags-view-height\", `${tagsViewHeight.value}px`);\n };\n\n /**\n * 应用主题预设方案\n * 注意:只应用颜色配置,不改变布局模式和主题模式\n */\n const applyPreset = async (preset: ThemePreset) => {\n // 只应用主题色\n primaryColor.value = preset.primaryColor;\n\n // 应用其他细节配置(如果存在)\n if (preset.settings) {\n if (preset.settings.borderRadius)\n borderRadius.value = preset.settings.borderRadius;\n if (preset.settings.transitionType)\n transitionType.value = preset.settings.transitionType;\n if (preset.settings.showBreadcrumb !== undefined)\n showBreadcrumb.value = preset.settings.showBreadcrumb;\n if (preset.settings.showTagsView !== undefined)\n showTagsView.value = preset.settings.showTagsView;\n if (preset.settings.tagsViewStyle)\n tagsViewStyle.value = preset.settings.tagsViewStyle;\n }\n };\n\n /**\n * 重置配置\n */\n const resetSettings = () => {\n themeMode.value = finalDefaults.themeMode;\n primaryColor.value = finalDefaults.primaryColor;\n borderRadius.value = finalDefaults.borderRadius;\n transitionType.value = finalDefaults.transitionType;\n enableTransition.value = finalDefaults.enableTransition;\n\n layoutMode.value = finalDefaults.layoutMode;\n collapsed.value = finalDefaults.collapsed;\n fixedHeader.value = finalDefaults.fixedHeader;\n showBreadcrumb.value = finalDefaults.showBreadcrumb;\n showBreadcrumbIcon.value = finalDefaults.showBreadcrumbIcon;\n showTagsView.value = finalDefaults.showTagsView;\n tagsViewHeight.value = finalDefaults.tagsViewHeight;\n tagsViewStyle.value = finalDefaults.tagsViewStyle;\n showFooter.value = finalDefaults.showFooter;\n sidebarWidth.value = finalDefaults.sidebarWidth;\n sidebarCollapsedWidth.value = finalDefaults.sidebarCollapsedWidth;\n headerHeight.value = finalDefaults.headerHeight;\n\n enableHotkeys.value = finalDefaults.enableHotkeys;\n };\n\n /**\n * 更新主题模式\n */\n const updateThemeMode = async (mode: ThemeMode) => {\n themeMode.value = mode;\n if (onThemeModeChange) {\n await onThemeModeChange(mode);\n }\n };\n\n /**\n * 切换侧边栏折叠状态\n */\n const toggleCollapse = () => {\n collapsed.value = !collapsed.value;\n };\n\n // ============ 监听器 ============\n\n // 监听配置变化,同步 CSS Variables\n watch(\n [\n primaryColor,\n borderRadiusValue,\n sidebarWidth,\n sidebarCollapsedWidth,\n headerHeight,\n tagsViewHeight,\n ],\n () => {\n syncCSSVariables();\n },\n { immediate: true },\n );\n\n // ============ 返回 ============\n\n return {\n // 状态\n themeMode,\n primaryColor,\n borderRadius,\n transitionType,\n enableTransition,\n layoutMode,\n collapsed,\n fixedHeader,\n showBreadcrumb,\n showBreadcrumbIcon,\n showTagsView,\n tagsViewHeight,\n tagsViewStyle,\n showFooter,\n sidebarWidth,\n sidebarCollapsedWidth,\n headerHeight,\n enableHotkeys,\n version,\n\n // 计算属性\n borderRadiusValue,\n transitionName,\n shouldEnableTransition,\n settingsState,\n\n // 方法\n syncCSSVariables,\n applyPreset,\n resetSettings,\n updateThemeMode,\n toggleCollapse,\n };\n });\n}\n\n/**\n * 默认的设置 Store(使用默认配置)\n */\nexport const useSettingsStore = createSettingsStore();\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 布局上下文 - Provide/Inject 机制\r\n * 消费方通过 provide 注入数据,骨架组件通过 inject 读取\r\n * 实现布局骨架与业务数据的完全解耦\r\n */\r\n\r\nimport type { InjectionKey, ComputedRef, Component } from \"vue\";\r\nimport { inject } from \"vue\";\r\nimport type { MenuOptions } from \"../types/menu\";\r\nimport type { LayoutMode } from \"../types\";\r\n\r\n/**\r\n * 品牌配置\r\n */\r\nexport interface LayoutBrandConfig {\r\n /** 品牌名称,如 'Robot Admin' */\r\n name?: string;\r\n /** 品牌副标题,如 '机器人管理系统' */\r\n subtitle?: string;\r\n /** Logo 地址(图片或视频) */\r\n logoSrc?: string;\r\n /** Logo 类型 */\r\n logoType?: \"video\" | \"image\";\r\n /** Logo 尺寸 */\r\n logoSize?: number;\r\n /** 首页路径,默认 '/home' */\r\n homePath?: string;\r\n /** 页脚版权文本 */\r\n copyright?: string;\r\n}\r\n\r\n/**\r\n * 布局上下文数据接口\r\n * 骨架组件通过 inject 获取这些数据\r\n */\r\nexport interface LayoutContext {\r\n // ============ 菜单数据 ============\r\n /** 菜单列表数据 */\r\n menus: ComputedRef<MenuOptions[]>;\r\n\r\n // ============ 主题状态 ============\r\n /** 是否为暗色模式 */\r\n isDark: ComputedRef<boolean>;\r\n\r\n // ============ 布局配置(来自 settings store) ============\r\n /** 当前布局模式 */\r\n layoutMode: ComputedRef<LayoutMode>;\r\n /** 侧边栏宽度 (px) */\r\n sidebarWidth: ComputedRef<number>;\r\n /** 侧边栏折叠后宽度 (px) */\r\n sidebarCollapsedWidth: ComputedRef<number>;\r\n /** 是否显示页脚 */\r\n showFooter: ComputedRef<boolean>;\r\n /** 是否显示标签页 */\r\n showTagsView: ComputedRef<boolean>;\r\n /** 标签页高度 (px) */\r\n tagsViewHeight: ComputedRef<number>;\r\n /** 头部高度 (px) */\r\n headerHeight: ComputedRef<number>;\r\n /** 过渡动画名称 */\r\n transitionName: ComputedRef<string>;\r\n /** 是否显示面包屑 */\r\n showBreadcrumb: ComputedRef<boolean>;\r\n /** 是否显示面包屑图标 */\r\n showBreadcrumbIcon: ComputedRef<boolean>;\r\n /** 是否固定头部 */\r\n fixedHeader: ComputedRef<boolean>;\r\n\r\n // ============ 品牌配置 ============\r\n /** 品牌信息 */\r\n brand: LayoutBrandConfig;\r\n\r\n // ============ 可选:图标组件 ============\r\n /**\r\n * 自定义图标组件(如 @iconify/vue 的 Icon 组件)\r\n * 用于布局内部组件渲染菜单图标\r\n * 组件需接受 name (string) 和 size (number) 两个 props\r\n * 如果不提供,将使用 CSS class 渲染图标\r\n */\r\n iconComponent?: Component;\r\n}\r\n\r\n/**\r\n * 布局上下文 Injection Key\r\n */\r\nexport const LAYOUT_CONTEXT_KEY: InjectionKey<LayoutContext> =\r\n Symbol(\"layout-context\");\r\n\r\n/**\r\n * 抽屉菜单统一控制器\r\n * 解决 MenuTrigger 与 DrawerMenu 各自拥有独立定时器导致的\r\n * \"鼠标从 trigger 移到 drawer 过程中 drawer 被关闭\"问题\r\n */\r\nexport interface DrawerHandlers {\r\n /** 显示抽屉并取消所有待执行的隐藏操作 */\r\n show: () => void;\r\n /** 启动延迟隐藏(300ms) */\r\n startHide: () => void;\r\n /** 立即隐藏抽屉(如导航跳转后) */\r\n hide: () => void;\r\n}\r\n\r\n/**\r\n * 抽屉菜单控制器 Injection Key\r\n */\r\nexport const DRAWER_HANDLER_KEY: InjectionKey<DrawerHandlers> =\r\n Symbol(\"drawer-handlers\");\r\n\r\n/**\r\n * 在骨架组件中获取布局上下文\r\n *\r\n * @throws 如果没有 provide LayoutContext 会抛出错误\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * // 在包的骨架组件内部使用\r\n * const ctx = useLayoutContext()\r\n * const menus = ctx.menus\r\n * const isDark = ctx.isDark\r\n * </script>\r\n * ```\r\n */\r\nexport function useLayoutContext(): LayoutContext {\r\n const ctx = inject(LAYOUT_CONTEXT_KEY);\r\n if (!ctx) {\r\n throw new Error(\r\n \"[Layout] LayoutContext not provided. \" +\r\n \"请在上层组件中使用 provide(LAYOUT_CONTEXT_KEY, ...) 注入布局数据。\",\r\n );\r\n }\r\n return ctx;\r\n}\r\n\r\n/**\r\n * 默认品牌配置\r\n */\r\nexport const DEFAULT_BRAND_CONFIG: Required<LayoutBrandConfig> = {\r\n name: \"Admin\",\r\n subtitle: \"\",\r\n logoSrc: \"\",\r\n logoType: \"image\",\r\n logoSize: 36,\r\n homePath: \"/home\",\r\n copyright: \"\",\r\n};\r\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 统一的 KeepAlive 缓存管理\r\n * 从主项目提取的通用 composable,零业务依赖\r\n */\r\n\r\nimport { ref, watch } from \"vue\";\r\nimport { useRoute } from \"vue-router\";\r\n\r\n/** KeepAlive 缓存选项 */\r\nexport interface LayoutCacheOptions {\r\n /** 最大缓存数量,默认 20 */\r\n maxCacheCount?: number;\r\n /** 是否在开发模式下输出日志,默认 true */\r\n enableDevLog?: boolean;\r\n /** 是否暴露调试方法到 window,默认 true */\r\n exposeToWindow?: boolean;\r\n}\r\n\r\n/**\r\n * 统一的 KeepAlive 缓存管理 composable\r\n *\r\n * @param options - 缓存配置选项\r\n * @returns 缓存视图列表和管理方法\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * import { useLayoutCache } from '@robot-admin/layout'\r\n *\r\n * const { cachedViews, maxCacheCount } = useLayoutCache({ maxCacheCount: 30 })\r\n * </script>\r\n *\r\n * <template>\r\n * <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\r\n * <RouterView />\r\n * </KeepAlive>\r\n * </template>\r\n * ```\r\n */\r\nexport function useLayoutCache(options: LayoutCacheOptions = {}) {\r\n const {\r\n maxCacheCount: maxCount = 20,\r\n enableDevLog = true,\r\n exposeToWindow: expose = true,\r\n } = options;\r\n\r\n const route = useRoute();\r\n const cachedViews = ref<string[]>([]);\r\n const maxCacheCount = ref(maxCount);\r\n\r\n const isDev =\r\n typeof import.meta !== \"undefined\" &&\r\n (import.meta as any).env?.DEV === true;\r\n\r\n /**\r\n * 判断页面是否应该被缓存\r\n * 极简策略:只有明确配置 meta.keepAlive = true 才缓存\r\n */\r\n const shouldCache = (routeName: string | symbol | undefined | null) => {\r\n if (!routeName || typeof routeName !== \"string\") return false;\r\n return route.meta?.keepAlive === true;\r\n };\r\n\r\n /** 添加缓存 */\r\n const addCache = (name: string) => {\r\n if (!cachedViews.value.includes(name) && shouldCache(name)) {\r\n cachedViews.value.push(name);\r\n\r\n // 控制缓存数量\r\n if (cachedViews.value.length > maxCacheCount.value) {\r\n cachedViews.value.shift();\r\n }\r\n\r\n if (isDev && enableDevLog) {\r\n console.debug(\r\n `[KeepAlive] ✅ 缓存: ${name} (${cachedViews.value.length}/${maxCacheCount.value})`,\r\n );\r\n }\r\n }\r\n };\r\n\r\n /** 移除缓存 */\r\n const removeCache = (name: string) => {\r\n const index = cachedViews.value.indexOf(name);\r\n if (index > -1) {\r\n cachedViews.value.splice(index, 1);\r\n if (isDev && enableDevLog) {\r\n console.debug(`[KeepAlive] ❌ 移除: ${name}`);\r\n }\r\n }\r\n };\r\n\r\n /** 清空所有缓存 */\r\n const clearAllCache = () => {\r\n cachedViews.value = [];\r\n if (isDev && enableDevLog) {\r\n console.debug(\"[KeepAlive] 🗑️ 清空所有缓存\");\r\n }\r\n };\r\n\r\n // 暴露调试方法到 window\r\n if (isDev && expose && typeof window !== \"undefined\") {\r\n (window as any).__clearCache__ = clearAllCache;\r\n (window as any).__removeCache__ = removeCache;\r\n (window as any).__getCachedViews__ = () => cachedViews.value;\r\n }\r\n\r\n // 监听路由变化,动态管理缓存\r\n watch(\r\n () => route.name,\r\n (newName) => {\r\n if (newName && typeof newName === \"string\") {\r\n addCache(newName);\r\n }\r\n },\r\n { immediate: true },\r\n );\r\n\r\n return {\r\n cachedViews,\r\n maxCacheCount,\r\n addCache,\r\n removeCache,\r\n clearAllCache,\r\n };\r\n}\r\n","<!--\n * @robot-admin/layout - SideLayout\n *\n * 左侧菜单布局骨架\n * 经典的左侧导航 + 右侧内容布局,使用 NLayout/NLayoutSider 实现\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #menu - 垂直菜单区域(侧边栏)\n * #header - 头部区域(含面包屑、导航栏右侧操作等)\n * #default - 页面内容(自动包含 RouterView + KeepAlive + Transition)\n * #footer - 页脚区域\n -->\n<template>\n <NLayout has-sider>\n <NLayoutSider\n bordered\n collapse-mode=\"width\"\n :collapsed-width=\"ctx.sidebarCollapsedWidth.value\"\n :width=\"ctx.sidebarWidth.value\"\n :native-scrollbar=\"false\"\n :collapsed=\"collapsed\"\n @update:collapsed=\"handleCollapsedUpdate\"\n :class=\"[\n 'layout-sider',\n 'no-horizontal-scroll',\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\n ]\"\n >\n <slot name=\"logo\" />\n <slot name=\"menu\" :collapsed=\"collapsed\" />\n </NLayoutSider>\n\n <NLayout>\n <slot name=\"header\" />\n\n <NLayoutContent class=\"content-with-header p16px app-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"ctx.transitionName.value\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </NLayoutContent>\n\n <slot name=\"footer\" />\n </NLayout>\n </NLayout>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from \"vue\";\nimport { NLayout, NLayoutSider, NLayoutContent } from \"naive-ui\";\nimport { useLayoutContext } from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\n\ndefineOptions({ name: \"SideLayout\" });\n\nconst props = withDefaults(\n defineProps<{\n /** 控制侧边栏折叠状态 (支持 v-model:collapsed) */\n collapsed?: boolean;\n }>(),\n { collapsed: false },\n);\n\nconst emit = defineEmits<{\n \"update:collapsed\": [value: boolean];\n}>();\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\n// 内部折叠状态,与 prop 双向同步\nconst _collapsed = ref(props.collapsed);\nwatch(\n () => props.collapsed,\n (v) => {\n _collapsed.value = v;\n },\n);\n\nconst collapsed = computed(() => _collapsed.value);\n\nconst handleCollapsedUpdate = (val: boolean) => {\n _collapsed.value = val;\n emit(\"update:collapsed\", val);\n};\n\n// 折叠切换\nconst toggleCollapse = () => {\n const next = !_collapsed.value;\n _collapsed.value = next;\n emit(\"update:collapsed\", next);\n};\n\ndefineExpose({\n collapsed,\n toggleCollapse,\n});\n</script>\n","<!--\r\n * @robot-admin/layout - ResponsiveMenu\r\n *\r\n * 响应式水平菜单\r\n * 用于 TopLayout / MixTopLayout / ReverseHorizontalMixLayout 的水平菜单\r\n * 自动检测容器宽度,溢出项放入“更多”下拉菜单,使用 NMenu (naive-ui) 渲染\r\n -->\r\n<template>\r\n <div ref=\"containerRef\" class=\"responsive-menu\">\r\n <div ref=\"visibleRef\" 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 <span class=\"i-mdi:dots-horizontal\" style=\"font-size: 20px\"></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 { ref, computed, watch, onMounted, onUnmounted, nextTick, 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\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\n// 安全包装\r\nconst menuData = computed(() => props.data || []);\r\nconst activeKey = computed(() => route.path);\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 convertToMenuOption = (item: MenuOptions): MenuOption => ({\r\n key: item.path || \"\",\r\n label: item.meta?.title || item.name || \"\",\r\n icon: item.meta?.icon ? renderIcon(item.meta.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\n\r\nconst containerRef = ref<HTMLElement>();\r\nconst visibleItems = ref<MenuOptions[]>([]);\r\nconst hiddenItems = ref<MenuOptions[]>([]);\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 key: item.path || \"\",\r\n label: item.meta?.title || item.name || \"\",\r\n icon: item.meta?.icon ? renderIcon(item.meta.icon, 16) : undefined,\r\n children: item.children?.length\r\n ? item.children.map((child) => ({\r\n key: child.path || \"\",\r\n label: child.meta?.title || child.name || \"\",\r\n icon: child.meta?.icon ? renderIcon(child.meta.icon, 16) : undefined,\r\n }))\r\n : undefined,\r\n })),\r\n);\r\n\r\nconst handleSelect = (key: string) => {\r\n if (key) router.push(key);\r\n};\r\n\r\n// ============ 宽度估算 ============\r\n\r\nconst estimateItemWidth = (item: MenuOptions): number => {\r\n const ITEM_PADDING = 36;\r\n const ITEM_MARGIN = 8;\r\n const CHAR_WIDTH = 12;\r\n const ICON_WIDTH = 26;\r\n const EXTRA = 5;\r\n\r\n const title = item.meta?.title || item.name || \"\";\r\n const textWidth = title.length * CHAR_WIDTH;\r\n const iconWidth = item.meta?.icon ? ICON_WIDTH : 0;\r\n\r\n return textWidth + iconWidth + ITEM_PADDING + ITEM_MARGIN + EXTRA;\r\n};\r\n\r\nconst calculateVisibleCount = (containerWidth: number): number => {\r\n if (!menuData.value.length) return 0;\r\n\r\n const MORE_BTN_WIDTH = 80;\r\n const SAFETY = 15;\r\n\r\n let totalWidth = 0;\r\n for (const item of menuData.value) {\r\n totalWidth += estimateItemWidth(item);\r\n }\r\n\r\n if (totalWidth + SAFETY <= containerWidth) {\r\n return menuData.value.length;\r\n }\r\n\r\n let usedWidth = 0;\r\n let count = 0;\r\n const available = containerWidth - MORE_BTN_WIDTH - SAFETY;\r\n\r\n for (const item of menuData.value) {\r\n const w = estimateItemWidth(item);\r\n if (usedWidth + w <= available) {\r\n usedWidth += w;\r\n count++;\r\n } else break;\r\n }\r\n\r\n return Math.max(count, 1);\r\n};\r\n\r\nconst calculateVisibleItems = () => {\r\n if (!containerRef.value || !menuData.value.length) return;\r\n\r\n const containerWidth = containerRef.value.offsetWidth;\r\n if (containerWidth === 0) {\r\n nextTick(() => calculateVisibleItems());\r\n return;\r\n }\r\n\r\n const count = calculateVisibleCount(containerWidth);\r\n visibleItems.value = menuData.value.slice(0, count);\r\n hiddenItems.value = menuData.value.slice(count);\r\n};\r\n\r\n// ============ 防抖 & ResizeObserver ============\r\n\r\nlet resizeTimer: number | null = null;\r\nconst debouncedCalculate = () => {\r\n if (resizeTimer) clearTimeout(resizeTimer);\r\n resizeTimer = window.setTimeout(() => calculateVisibleItems(), 100);\r\n};\r\n\r\nlet resizeObserver: ResizeObserver | null = null;\r\n\r\nonMounted(() => {\r\n nextTick(() => calculateVisibleItems());\r\n if (containerRef.value) {\r\n resizeObserver = new ResizeObserver(() => debouncedCalculate());\r\n resizeObserver.observe(containerRef.value);\r\n }\r\n});\r\n\r\nonUnmounted(() => {\r\n resizeObserver?.disconnect();\r\n if (resizeTimer) clearTimeout(resizeTimer);\r\n});\r\n\r\nwatch(\r\n () => props.data,\r\n () => nextTick(() => calculateVisibleItems()),\r\n {\r\n deep: true,\r\n },\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.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</style>\r\n","<!--\n * @robot-admin/layout - TopLayout\n *\n * 顶部导航布局骨架\n * Logo+品牌 | 水平菜单 | 操作区 → 标签页 → 内容区 → 页脚\n *\n * Slots:\n * #logo - Logo/品牌区域\n * #menu - 水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"top-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:Logo 和品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"menu\">\n <ResponsiveMenu :data=\"menus\" />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 主内容区域 -->\n <NLayout>\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <!-- 页脚 -->\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"TopLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n</script>\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 菜单拆分 Composable - 通用的一级/二级菜单拆分逻辑\r\n * 用于 Mix / MixTop / ReverseHorizontalMix 等需要菜单拆分的布局\r\n */\r\n\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n toValue,\r\n type ComputedRef,\r\n type Ref,\r\n type MaybeRefOrGetter,\r\n} from \"vue\";\r\nimport { useRoute, useRouter } from \"vue-router\";\r\nimport type { MenuOptions } from \"../types/menu\";\r\n\r\nexport interface UseMenuSplitOptions {\r\n /** 菜单数据(响应式) */\r\n menus: ComputedRef<MenuOptions[]>;\r\n /** 是否使用悬浮式二级菜单(MixLayout 的 toggle 模式) */\r\n floatingSecondMenu?: MaybeRefOrGetter<boolean>;\r\n}\r\n\r\nexport interface UseMenuSplitReturn {\r\n /** 当前激活的一级菜单路径 */\r\n activeFirstMenu: Ref<string>;\r\n /** 当前激活的一级菜单完整数据 */\r\n activeFirstMenuItem: ComputedRef<MenuOptions | undefined>;\r\n /** 当前一级菜单对应的二级菜单列表 */\r\n currentSecondMenus: ComputedRef<MenuOptions[]>;\r\n /** 悬浮的菜单项(MixLayout 使用) */\r\n hoveredMenuItem: Ref<MenuOptions | null>;\r\n /** 是否显示悬浮二级菜单(MixLayout 使用) */\r\n showSecondMenu: Ref<boolean>;\r\n /** 处理一级菜单点击 */\r\n handleFirstMenuClick: (item: MenuOptions) => void;\r\n /** 处理二级菜单点击 */\r\n handleSecondMenuClick: (item: MenuOptions) => void;\r\n /** 检查路由是否匹配菜单项 */\r\n isMenuItemActive: (menuPath: string | undefined) => boolean;\r\n}\r\n\r\n/**\r\n * 菜单拆分 Composable\r\n *\r\n * 提供一级/二级菜单拆分状态管理、路由自动匹配、菜单点击处理。\r\n * 供 MixLayout / MixTopLayout / ReverseHorizontalMixLayout 共用。\r\n *\r\n * @example\r\n * ```ts\r\n * const menuSplit = useMenuSplit({\r\n * menus: ctx.menus,\r\n * floatingSecondMenu: computed(() => layoutMode.value === 'mix'),\r\n * })\r\n * ```\r\n */\r\nexport function useMenuSplit(options: UseMenuSplitOptions): UseMenuSplitReturn {\r\n const { menus, floatingSecondMenu = false } = options;\r\n const route = useRoute();\r\n const router = useRouter();\r\n\r\n const activeFirstMenu = ref<string>(\"\");\r\n const hoveredMenuItem = ref<MenuOptions | null>(null);\r\n const showSecondMenu = ref(false);\r\n\r\n // ============ 路径匹配 ============\r\n\r\n const normalizePath = (path: string) =>\r\n path.startsWith(\"/\") ? path : `/${path}`;\r\n\r\n const isMenuItemActive = (menuPath: string | undefined): boolean => {\r\n if (!menuPath) return false;\r\n const currentPath = route.path;\r\n if (currentPath === menuPath) return true;\r\n const normalizedMenuPath = normalizePath(menuPath);\r\n const normalizedCurrentPath = normalizePath(currentPath);\r\n if (normalizedCurrentPath === normalizedMenuPath) return true;\r\n return normalizedCurrentPath.includes(`/${menuPath}`);\r\n };\r\n\r\n // ============ 查找一级菜单 ============\r\n\r\n const findActiveTopMenu = (items: MenuOptions[]): MenuOptions | null => {\r\n for (const item of items) {\r\n if (isMenuItemActive(item.path)) return item;\r\n if (item.children?.length) {\r\n const found = findActiveTopMenu(item.children);\r\n if (found) return item;\r\n }\r\n }\r\n return null;\r\n };\r\n\r\n // ============ 计算属性 ============\r\n\r\n const activeFirstMenuItem = computed(() =>\r\n menus.value.find((item) => item.path === activeFirstMenu.value),\r\n );\r\n\r\n const currentSecondMenus = computed(\r\n () => activeFirstMenuItem.value?.children || [],\r\n );\r\n\r\n // ============ 交互处理 ============\r\n\r\n const handleFirstMenuClick = (item: MenuOptions) => {\r\n activeFirstMenu.value = item.path || \"\";\r\n\r\n if (toValue(floatingSecondMenu)) {\r\n // MixLayout: toggle 悬浮二级菜单\r\n if (item.children && item.children.length > 0) {\r\n if (showSecondMenu.value && hoveredMenuItem.value?.path === item.path) {\r\n showSecondMenu.value = false;\r\n hoveredMenuItem.value = null;\r\n } else {\r\n hoveredMenuItem.value = item;\r\n showSecondMenu.value = true;\r\n }\r\n } else {\r\n showSecondMenu.value = false;\r\n hoveredMenuItem.value = null;\r\n if (item.path) router.push(item.path);\r\n }\r\n } else {\r\n // MixTop / Reverse: 直接切换\r\n if (!item.children || item.children.length === 0) {\r\n if (item.path) router.push(item.path);\r\n }\r\n }\r\n };\r\n\r\n const handleSecondMenuClick = (item: MenuOptions) => {\r\n if (item.path) router.push(item.path);\r\n };\r\n\r\n // ============ 路由自动匹配 ============\r\n\r\n const updateActiveMenuByRoute = () => {\r\n const menuList = menus.value;\r\n const matchedFirstMenu = findActiveTopMenu(menuList);\r\n if (matchedFirstMenu) {\r\n activeFirstMenu.value = matchedFirstMenu.path || \"\";\r\n } else if (menuList.length > 0 && !activeFirstMenu.value) {\r\n activeFirstMenu.value = menuList[0].path || \"\";\r\n }\r\n };\r\n\r\n watch(\r\n () => route.path,\r\n () => updateActiveMenuByRoute(),\r\n { immediate: true },\r\n );\r\n\r\n watch(\r\n menus,\r\n (newMenus) => {\r\n if (newMenus && newMenus.length > 0) updateActiveMenuByRoute();\r\n },\r\n { immediate: true },\r\n );\r\n\r\n onMounted(() => updateActiveMenuByRoute());\r\n\r\n return {\r\n activeFirstMenu,\r\n activeFirstMenuItem,\r\n currentSecondMenus,\r\n hoveredMenuItem,\r\n showSecondMenu,\r\n handleFirstMenuClick,\r\n handleSecondMenuClick,\r\n isMenuItemActive,\r\n };\r\n}\r\n","<!--\n * @robot-admin/layout - MixLayout\n *\n * 混合布局骨架\n * 左侧一级菜单图标栏 + 悬浮二级菜单弹出面板 + 右侧内容区\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #header - 头部区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"mix-layout-container\">\n <!-- 左侧一级菜单 -->\n <div\n class=\"first-level-menu\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- Logo -->\n <div class=\"logo-container\">\n <slot name=\"logo\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n class=\"logo-video\"\n />\n </slot>\n </div>\n\n <!-- 一级菜单列表 -->\n <div class=\"first-menu-list\">\n <div\n v-for=\"item in menus\"\n :key=\"item.path\"\n class=\"first-menu-item\"\n :class=\"{\n active: menuSplit.activeFirstMenu.value === item.path,\n }\"\n @click=\"menuSplit.handleFirstMenuClick(item)\"\n @mouseenter=\"menuSplit.hoveredMenuItem.value = item\"\n >\n <div class=\"menu-item-content\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"22\"\n />\n <span v-else class=\"menu-item-text\">{{\n (item.meta?.title || \"\")[0]\n }}</span>\n </div>\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\n </div>\n </div>\n </div>\n\n <!-- 悬浮二级菜单 -->\n <Transition name=\"slide-fade\">\n <div\n v-if=\"menuSplit.showSecondMenu.value\"\n class=\"second-level-menu-popup\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n @mouseleave=\"menuSplit.hoveredMenuItem.value = null\"\n >\n <div class=\"second-menu-header\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"menuSplit.hoveredMenuItem.value?.meta?.icon\"\n :name=\"menuSplit.hoveredMenuItem.value.meta.icon\"\n :size=\"20\"\n />\n <span class=\"menu-title\">{{\n menuSplit.hoveredMenuItem.value?.meta?.title\n }}</span>\n </div>\n <div class=\"second-menu-list\">\n <div\n v-for=\"child in menuSplit.hoveredMenuItem.value?.children || []\"\n :key=\"child.path\"\n class=\"second-menu-item\"\n :class=\"{ active: menuSplit.isMenuItemActive(child.path) }\"\n @click=\"menuSplit.handleSecondMenuClick(child)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"16\"\n />\n <span class=\"menu-item-label\">{{ child.meta?.title }}</span>\n </div>\n </div>\n </div>\n </Transition>\n\n <!-- 右侧主内容区 -->\n <div class=\"main-layout\">\n <slot name=\"header\" />\n\n <NLayout>\n <NLayoutContent class=\"content-with-header p16px app-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\n\ndefineOptions({ name: \"MixLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => true),\n});\n\n// 图标组件:优先使用消费方提供的,否则用 CSS class 渲染\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - MixTopLayout\n *\n * 顶部混合布局骨架\n * 左侧一级菜单 + 顶部二级水平菜单 + 内容区\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #brand - 顶部导航品牌区\n * #top-menu - 二级水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"mix-top-layout-container\">\n <!-- 左侧一级菜单 -->\n <div\n class=\"first-level-sidebar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- Logo -->\n <div class=\"logo-container\">\n <slot name=\"logo\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n class=\"logo-video\"\n />\n </slot>\n </div>\n\n <!-- 一级菜单列表 -->\n <div class=\"first-menu-list\">\n <div\n v-for=\"item in menus\"\n :key=\"item.path\"\n class=\"first-menu-item\"\n :class=\"{\n active: menuSplit.activeFirstMenu.value === item.path,\n }\"\n @click=\"menuSplit.handleFirstMenuClick(item)\"\n >\n <div class=\"menu-item-content\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"22\"\n />\n <span v-else class=\"menu-item-text\">{{\n (item.meta?.title || \"\")[0]\n }}</span>\n </div>\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\n </div>\n </div>\n </div>\n\n <!-- 右侧主区域 -->\n <div class=\"main-area\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"brand\">\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:二级水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"top-menu\">\n <ResponsiveMenu\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n :data=\"menuSplit.currentSecondMenus.value\"\n />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 内容区 -->\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"MixTopLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => false),\n});\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - ReverseHorizontalMixLayout\n *\n * 反转混合布局骨架\n * 顶部一级菜单 + 右侧二级菜单侧边栏 + 左侧内容区\n *\n * Slots:\n * #logo - Logo/品牌区域\n * #top-menu - 一级水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"reverse-horizontal-mix-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:Logo 和品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:一级水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"top-menu\">\n <ResponsiveMenu :data=\"menus\" />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 主区域:左侧内容 + 右侧菜单 -->\n <div class=\"main-area\">\n <!-- 左侧:主内容区 -->\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n\n <!-- 右侧:二级菜单侧边栏 -->\n <div\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n class=\"right-sidebar\"\n :class=\"[\n isDarkMode ? 'dark-theme' : 'light-theme',\n { collapsed: isCollapsed },\n ]\"\n >\n <!-- 折叠按钮 -->\n <div class=\"collapse-trigger\" @click=\"toggleCollapse\">\n <i\n :class=\"[\n 'transition-all duration-300 ease-in-out',\n isCollapsed ? 'i-ri:menu-fold-4-fill' : 'i-ri:menu-fold-3-fill',\n ]\"\n ></i>\n </div>\n\n <!-- 菜单内容 -->\n <div v-show=\"!isCollapsed\" class=\"sidebar-content\">\n <!-- 侧边栏标题 -->\n <div\n class=\"sidebar-header\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"header-icon\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"menuSplit.activeFirstMenuItem.value?.meta?.icon\"\n :name=\"menuSplit.activeFirstMenuItem.value.meta.icon\"\n :size=\"20\"\n />\n </div>\n <span class=\"header-title\">{{\n menuSplit.activeFirstMenuItem.value?.meta?.title || \"菜单\"\n }}</span>\n </div>\n\n <!-- 二级菜单列表 -->\n <div class=\"sidebar-menu-list\">\n <template\n v-for=\"child in menuSplit.currentSecondMenus.value\"\n :key=\"child.path\"\n >\n <!-- 有子菜单的项 -->\n <div\n v-if=\"child.children && child.children.length > 0\"\n class=\"menu-group\"\n >\n <div class=\"group-title\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"16\"\n />\n <span>{{ child.meta?.title }}</span>\n </div>\n <div\n v-for=\"subChild in child.children\"\n :key=\"subChild.path\"\n class=\"menu-item sub-item\"\n :class=\"{\n active: menuSplit.isMenuItemActive(subChild.path),\n }\"\n @click=\"menuSplit.handleSecondMenuClick(subChild)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"subChild.meta?.icon\"\n :name=\"subChild.meta.icon\"\n :size=\"16\"\n />\n <span class=\"item-title\">{{ subChild.meta?.title }}</span>\n </div>\n </div>\n <!-- 没有子菜单的项 -->\n <div\n v-else\n class=\"menu-item\"\n :class=\"{\n active: menuSplit.isMenuItemActive(child.path),\n }\"\n @click=\"menuSplit.handleSecondMenuClick(child)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"18\"\n />\n <span class=\"item-title\">{{ child.meta?.title }}</span>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { ref, computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"ReverseHorizontalMixLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => false),\n});\n\n// 右侧边栏折叠状态\nconst isCollapsed = ref(false);\nconst toggleCollapse = () => {\n isCollapsed.value = !isCollapsed.value;\n};\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - CardLayout\n *\n * 卡片式布局骨架\n * 顶部导航 + hover 触发抽屉式菜单 + 内容区\n *\n * Slots:\n * #menu-trigger - 菜单触发区域(hover 弹出抽屉)\n * #logo - Logo/品牌区域\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #drawer-menu - 抽屉式菜单内容\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"card-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"navbar-left\">\n <!-- 悬停触发区域 -->\n <div\n class=\"hover-trigger-area\"\n @mouseenter=\"cancelHideTimer\"\n @mouseleave=\"hideDrawerMenu\"\n >\n <slot name=\"menu-trigger\">\n <div class=\"menu-indicator\">\n <i class=\"i-ri:menu-3-line\"></i>\n </div>\n </slot>\n </div>\n\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 抽屉式菜单 -->\n <div\n class=\"drawer-menu\"\n :class=\"[\n isDarkMode ? 'dark-theme' : 'light-theme',\n { visible: showDrawerMenu },\n ]\"\n @mouseenter=\"cancelHideTimer\"\n @mouseleave=\"hideDrawerMenu\"\n >\n <slot name=\"drawer-menu\">\n <!-- 菜单头部 -->\n <div class=\"drawer-header\">\n <div class=\"drawer-title\">\n <i class=\"i-ri:apps-2-line\"></i>\n <span>功能导航</span>\n </div>\n </div>\n\n <!-- 菜单内容 - 阿里云风格铺开布局 -->\n <div class=\"drawer-content\">\n <div class=\"menu-grid\">\n <div\n v-for=\"category in menus\"\n :key=\"category.path\"\n class=\"menu-category\"\n >\n <!-- 分类标题 -->\n <div class=\"category-header\" @click=\"navigateToPage(category)\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"category.meta?.icon\"\n :name=\"category.meta.icon\"\n :size=\"18\"\n />\n <span>{{ category.meta?.title }}</span>\n </div>\n\n <!-- 分类下的菜单项 -->\n <div\n class=\"category-items\"\n v-if=\"category.children && category.children.length > 0\"\n >\n <div\n v-for=\"item in category.children\"\n :key=\"item.path\"\n class=\"menu-item\"\n @click=\"navigateToPage(item)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"16\"\n />\n <span class=\"item-title\">{{ item.meta?.title }}</span>\n </div>\n\n <!-- 3级菜单项 -->\n <template\n v-for=\"item in category.children\"\n :key=\"'sub-' + item.path\"\n >\n <div\n v-if=\"item.children && item.children.length > 0\"\n class=\"submenu-items\"\n >\n <div class=\"submenu-title\">{{ item.meta?.title }}</div>\n <div\n v-for=\"subItem in item.children\"\n :key=\"subItem.path\"\n class=\"menu-item submenu-item\"\n @click=\"navigateToPage(subItem)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"subItem.meta?.icon\"\n :name=\"subItem.meta.icon\"\n :size=\"14\"\n />\n <span class=\"item-title\">{{ subItem.meta?.title }}</span>\n </div>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- 主内容区域 -->\n <div class=\"main-content-area\">\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content p16px app-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { ref, computed, onUnmounted, h, defineComponent } from \"vue\";\nimport { useRouter } from \"vue-router\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport type { MenuOptions } from \"../../../types/menu\";\n\ndefineOptions({ name: \"CardLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\nconst router = useRouter();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\n// 抽屉菜单状态\nconst showDrawerMenu = ref(false);\nconst hideTimer = ref<ReturnType<typeof setTimeout> | null>(null);\n\nconst hideDrawerMenu = () => {\n if (hideTimer.value) clearTimeout(hideTimer.value);\n hideTimer.value = setTimeout(() => {\n showDrawerMenu.value = false;\n }, 300);\n};\n\nconst cancelHideTimer = () => {\n if (hideTimer.value) {\n clearTimeout(hideTimer.value);\n hideTimer.value = null;\n }\n showDrawerMenu.value = true;\n};\n\nconst navigateToPage = (item: MenuOptions) => {\n if (item.path) {\n router.push(item.path);\n showDrawerMenu.value = false;\n }\n};\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n\nonUnmounted(() => {\n if (hideTimer.value) clearTimeout(hideTimer.value);\n});\n</script>\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 :class=\"['layout-container', isDark ? 'dark-mode' : 'light-mode']\">\r\n <!-- ═══════ Side 左侧菜单布局 ═══════ -->\r\n <SideLayout v-if=\"layoutMode === 'side'\" v-model:collapsed=\"sideCollapsed\">\r\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 </SideLayout>\r\n\r\n <!-- ═══════ Top 顶部菜单布局 ═══════ -->\r\n <TopLayout v-else-if=\"layoutMode === 'top'\">\r\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 </TopLayout>\r\n\r\n <!-- ═══════ Mix 混合布局 ═══════ -->\r\n <MixLayout v-else-if=\"layoutMode === 'mix'\">\r\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 </MixLayout>\r\n\r\n <!-- ═══════ MixTop 顶部混合布局 ═══════ -->\r\n <MixTopLayout v-else-if=\"layoutMode === 'mix-top'\">\r\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 </MixTopLayout>\r\n\r\n <!-- ═══════ ReverseHorizontalMix 反转混合布局 ═══════ -->\r\n <ReverseHorizontalMixLayout\r\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 </ReverseHorizontalMixLayout>\r\n\r\n <!-- ═══════ CardLayout 卡片布局 ═══════ -->\r\n <CardLayout v-else-if=\"layoutMode === 'card-layout'\">\r\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 </CardLayout>\r\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 { useSettingsStore } from \"../../stores/settings\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\n// 骨架布局全局样式\r\nimport \"../../styles/layouts.scss\";\r\n\r\n// 骨架组件\r\nimport SideLayout from \"../layouts/SideLayout/index.vue\";\r\nimport TopLayout from \"../layouts/TopLayout/index.vue\";\r\nimport MixLayout from \"../layouts/MixLayout/index.vue\";\r\nimport MixTopLayout from \"../layouts/MixTopLayout/index.vue\";\r\nimport ReverseHorizontalMixLayout from \"../layouts/ReverseHorizontalMixLayout/index.vue\";\r\nimport CardLayout from \"../layouts/CardLayout/index.vue\";\r\n\r\ndefineOptions({ name: \"C_LayoutContainer\" });\r\n\r\nconst settingsStore = useSettingsStore();\r\nconst ctx = useLayoutContext();\r\n\r\nconst layoutMode = computed(() => settingsStore.layoutMode);\r\nconst isDark = ctx.isDark;\r\n\r\n// Side 布局折叠状态\r\nconst sideCollapsed = ref(false);\r\n\r\n// 提供折叠状态给子组件(如 C_Header 的汉堡按钮)\r\nprovide(\"menuCollapse\", {\r\n isCollapsed: sideCollapsed,\r\n handleCollapsedChange: (collapsed: boolean) => {\r\n sideCollapsed.value = collapsed;\r\n },\r\n});\r\n</script>\r\n","/**\n * SettingsDrawer - 常量数据\n */\n\nimport { THEME_PRESETS, COLOR_SWATCHES } from \"../../constants\";\nimport type { 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];\n","<!--\n * @robot-admin/layout - SettingsDrawer\n *\n * 布局配置抽屉\n * 包含外观/布局/功能三大模块,支持主题预设、布局切换、配置导入导出等\n -->\n<template>\n <NDrawer\n v-model:show=\"visible\"\n :width=\"380\"\n placement=\"right\"\n :trap-focus=\"false\"\n :block-scroll=\"false\"\n >\n <NDrawerContent title=\"⚙️ 布局配置\" closable>\n <NTabs v-model:value=\"activeTab\" animated class=\"settings-tabs\">\n <!-- 外观 Tab -->\n <NTabPane name=\"appearance\" tab=\"🎨 外观\">\n <!-- 预设方案网格 -->\n <div class=\"settings-section\">\n <div class=\"preset-grid\">\n <div\n v-for=\"preset in THEME_PRESETS\"\n :key=\"preset.name\"\n class=\"preset-card\"\n :class=\"{ active: isCurrentPreset(preset) }\"\n @click=\"handleApplyPreset(preset)\"\n >\n <div class=\"preset-icon\">{{ preset.icon }}</div>\n <div class=\"preset-name\">{{ preset.name }}</div>\n <div class=\"preset-color\">\n <span\n class=\"color-dot\"\n :style=\"{ backgroundColor: preset.primaryColor }\"\n ></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 主题模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">主题模式</div>\n <NRadioGroup\n v-model:value=\"settingsStore.themeMode\"\n class=\"mode-group\"\n >\n <NRadioButton value=\"light\">\n <span class=\"i-mdi:white-balance-sunny mr-1\"></span>\n 浅色\n </NRadioButton>\n <NRadioButton value=\"dark\">\n <span class=\"i-mdi:moon-waning-crescent mr-1\"></span>\n 深色\n </NRadioButton>\n <NRadioButton value=\"auto\">\n <span class=\"i-mdi:theme-light-dark mr-1\"></span>\n 自动\n </NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 主题色 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">主题色</div>\n <div class=\"color-picker-wrapper\">\n <NColorPicker\n v-model:value=\"settingsStore.primaryColor\"\n :show-alpha=\"false\"\n :swatches=\"COLOR_SWATCHES\"\n :actions=\"['confirm']\"\n />\n <span class=\"color-value\">{{ settingsStore.primaryColor }}</span>\n </div>\n </div>\n\n <!-- 圆角大小 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">圆角大小</div>\n <NRadioGroup\n v-model:value=\"settingsStore.borderRadius\"\n class=\"radius-group\"\n >\n <NRadioButton value=\"small\">小 (4px)</NRadioButton>\n <NRadioButton value=\"medium\">中 (6px)</NRadioButton>\n <NRadioButton value=\"large\">大 (8px)</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 页面动画 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">页面动画</div>\n <div class=\"flex items-center justify-between mb-2\">\n <span class=\"text-sm\">启用动画</span>\n <NSwitch v-model:value=\"settingsStore.enableTransition\" />\n </div>\n <NRadioGroup\n v-model:value=\"settingsStore.transitionType\"\n :disabled=\"!settingsStore.enableTransition\"\n class=\"transition-group\"\n >\n <NRadioButton value=\"fade\">淡入</NRadioButton>\n <NRadioButton value=\"slide\">滑动</NRadioButton>\n <NRadioButton value=\"zoom\">缩放</NRadioButton>\n <NRadioButton value=\"none\">无</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 恢复默认 -->\n <div class=\"settings-section\">\n <NButton block secondary @click=\"handleResetAppearance\">\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 恢复外观默认设置\n </NButton>\n </div>\n </NTabPane>\n\n <!-- 布局 Tab -->\n <NTabPane name=\"layout\" tab=\"📐 布局\">\n <!-- 布局模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">布局模式</div>\n <div class=\"layout-grid\">\n <div\n v-for=\"mode in LAYOUT_MODE_OPTIONS\"\n :key=\"mode.value\"\n class=\"layout-item\"\n :class=\"{\n active: settingsStore.layoutMode === mode.value,\n disabled: mode.disabled,\n }\"\n @click.stop=\"handleLayoutChange(mode.value, mode.disabled)\"\n >\n <div class=\"layout-screenshot\">\n <svg\n class=\"layout-svg\"\n viewBox=\"0 0 56 48\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g v-html=\"mode.svg\"></g>\n </svg>\n </div>\n <div class=\"layout-label\">\n {{ mode.label }}\n <span v-if=\"mode.disabled\" class=\"badge-soon\">敬请期待</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 界面元素 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">界面元素</div>\n <div class=\"setting-item\">\n <span>显示面包屑</span>\n <NSwitch v-model:value=\"settingsStore.showBreadcrumb\" />\n </div>\n <div class=\"setting-item\">\n <span>显示面包屑图标</span>\n <NSwitch\n v-model:value=\"settingsStore.showBreadcrumbIcon\"\n :disabled=\"!settingsStore.showBreadcrumb\"\n />\n </div>\n <div class=\"setting-item\">\n <span>显示标签页</span>\n <NSwitch v-model:value=\"settingsStore.showTagsView\" />\n </div>\n <div class=\"setting-item\">\n <span>显示页脚</span>\n <NSwitch v-model:value=\"settingsStore.showFooter\" />\n </div>\n </div>\n\n <!-- 尺寸调整 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">尺寸调整</div>\n <div class=\"slider-item\">\n <div class=\"slider-label\">\n <span>侧边栏宽度</span>\n <span class=\"slider-value\"\n >{{ settingsStore.sidebarWidth }}px</span\n >\n </div>\n <NSlider\n v-model:value=\"settingsStore.sidebarWidth\"\n :min=\"180\"\n :max=\"280\"\n :step=\"10\"\n :marks=\"{ 180: '180', 220: '220', 280: '280' }\"\n />\n </div>\n <div class=\"slider-item\">\n <div class=\"slider-label\">\n <span>头部高度</span>\n <span class=\"slider-value\"\n >{{ settingsStore.headerHeight }}px</span\n >\n </div>\n <NSlider\n v-model:value=\"settingsStore.headerHeight\"\n :min=\"48\"\n :max=\"64\"\n :step=\"4\"\n :marks=\"{ 48: '48', 56: '56', 64: '64' }\"\n />\n </div>\n </div>\n\n <!-- 恢复默认 -->\n <div class=\"settings-section\">\n <NButton block secondary @click=\"handleResetLayout\">\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 恢复布局默认设置\n </NButton>\n </div>\n </NTabPane>\n\n <!-- 功能 Tab -->\n <NTabPane name=\"features\" tab=\"🔧 功能\">\n <!-- 缓存管理 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">缓存管理</div>\n <div class=\"action-buttons-grid\">\n <NButton secondary @click=\"handleClearCache\">\n <template #icon>\n <span class=\"i-mdi:delete-sweep\"></span>\n </template>\n 清除缓存\n </NButton>\n <NButton secondary @click=\"handleReload\">\n <template #icon>\n <span class=\"i-mdi:refresh\"></span>\n </template>\n 重新加载\n </NButton>\n </div>\n </div>\n\n <!-- 配置管理 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">配置管理</div>\n <div class=\"action-buttons-grid\">\n <NButton secondary @click=\"handleExportConfig\">\n <template #icon>\n <span class=\"i-mdi:download\"></span>\n </template>\n 导出配置\n </NButton>\n <NButton secondary @click=\"handleImportConfig\">\n <template #icon>\n <span class=\"i-mdi:upload\"></span>\n </template>\n 导入配置\n </NButton>\n </div>\n <NButton\n block\n type=\"error\"\n secondary\n class=\"mt-2\"\n @click=\"handleReset\"\n >\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 重置所有配置\n </NButton>\n </div>\n\n <!-- 系统模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">系统模式</div>\n <div class=\"setting-item\">\n <div class=\"setting-label\">\n <span>灰色模式</span>\n <span class=\"setting-desc\">用于哀悼日场景</span>\n </div>\n <NSwitch v-model:value=\"grayMode\" />\n </div>\n <div class=\"setting-item\">\n <div class=\"setting-label\">\n <span>色弱模式</span>\n <span class=\"setting-desc\">提供无障碍访问支持</span>\n </div>\n <NSwitch v-model:value=\"colorWeakMode\" />\n </div>\n </div>\n\n <!-- 水印设置 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">水印设置</div>\n <div class=\"setting-item\">\n <span>启用水印</span>\n <NSwitch v-model:value=\"watermarkEnabled\" />\n </div>\n <div v-if=\"watermarkEnabled\" class=\"mt-2\">\n <NInput\n v-model:value=\"watermarkText\"\n placeholder=\"请输入水印内容\"\n size=\"small\"\n />\n </div>\n </div>\n\n <!-- 系统信息 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">系统信息</div>\n <div class=\"system-info\">\n <div class=\"info-item\">\n <span class=\"info-label\">浏览器</span>\n <span class=\"info-value\">{{ systemInfo.browser }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">操作系统</span>\n <span class=\"info-value\">{{ systemInfo.os }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">屏幕分辨率</span>\n <span class=\"info-value\">{{ systemInfo.resolution }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">设备像素比</span>\n <span class=\"info-value\">{{ systemInfo.pixelRatio }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">语言</span>\n <span class=\"info-value\">{{ systemInfo.language }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">时区</span>\n <span class=\"info-value\">{{ systemInfo.timezone }}</span>\n </div>\n </div>\n </div>\n </NTabPane>\n </NTabs>\n </NDrawerContent>\n </NDrawer>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch, onUnmounted } from \"vue\";\nimport {\n NDrawer,\n NDrawerContent,\n NTabs,\n NTabPane,\n NRadioGroup,\n NRadioButton,\n NColorPicker,\n NSwitch,\n NButton,\n NSlider,\n NInput,\n useMessage,\n useDialog,\n} from \"naive-ui\";\nimport { useSettingsStore } from \"../../stores/settings\";\nimport { DEFAULT_SETTINGS } from \"../../constants\";\nimport { COLOR_SWATCHES, LAYOUT_MODE_OPTIONS, THEME_PRESETS } from \"./data\";\nimport type { ThemePreset } from \"../../types\";\n\n// ============ 数据定义 ============\n\nconst message = useMessage();\nconst dialog = useDialog();\nconst settingsStore = useSettingsStore();\nconst visible = defineModel<boolean>(\"show\", { default: false });\nconst activeTab = ref(\"appearance\");\n\n// 功能开关\nconst grayMode = ref(false);\nconst colorWeakMode = ref(false);\nconst watermarkEnabled = ref(false);\nconst watermarkText = ref(\"Robot Admin\");\n\n// 水印元素引用\nlet watermarkEl: HTMLElement | null = null;\n\n// 创建水印函数\nconst createWatermark = (text: string) => {\n // 移除旧水印\n if (watermarkEl) {\n document.body.removeChild(watermarkEl);\n watermarkEl = null;\n }\n\n // 创建 canvas\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\")!;\n canvas.width = 200;\n canvas.height = 150;\n\n ctx.font = \"14px Microsoft JhengHei\";\n ctx.fillStyle = \"rgba(120, 120, 120, 0.15)\";\n ctx.rotate((-20 * Math.PI) / 180);\n ctx.fillText(text, 0, 80);\n\n // 创建水印容器\n watermarkEl = document.createElement(\"div\");\n watermarkEl.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n background-image: url(${canvas.toDataURL()});\n background-repeat: repeat;\n z-index: 9999;\n user-select: none;\n `;\n\n document.body.appendChild(watermarkEl);\n};\n\n// 移除水印函数\nconst removeWatermark = () => {\n if (watermarkEl) {\n document.body.removeChild(watermarkEl);\n watermarkEl = null;\n }\n};\n\n// 监听灰色模式\nwatch(grayMode, (val) => {\n document.documentElement.classList.toggle(\"gray-mode\", val);\n});\n\n// 监听色弱模式\nwatch(colorWeakMode, (val) => {\n document.documentElement.classList.toggle(\"color-weak-mode\", val);\n});\n\n// 监听水印开关\nwatch(watermarkEnabled, (val) => {\n if (val) {\n createWatermark(watermarkText.value);\n } else {\n removeWatermark();\n }\n});\n\n// 监听水印文本\nwatch(watermarkText, (val) => {\n if (watermarkEnabled.value) {\n createWatermark(val);\n }\n});\n\n// 获取系统信息\nconst systemInfo = computed(() => {\n const ua = navigator.userAgent;\n let browser = \"Unknown\";\n let os = \"Unknown\";\n\n // 检测浏览器\n if (ua.includes(\"Chrome\")) browser = \"Chrome\";\n else if (ua.includes(\"Firefox\")) browser = \"Firefox\";\n else if (ua.includes(\"Safari\")) browser = \"Safari\";\n else if (ua.includes(\"Edge\")) browser = \"Edge\";\n\n // 检测操作系统\n if (ua.includes(\"Windows\")) os = \"Windows\";\n else if (ua.includes(\"Mac\")) os = \"macOS\";\n else if (ua.includes(\"Linux\")) os = \"Linux\";\n else if (ua.includes(\"Android\")) os = \"Android\";\n else if (ua.includes(\"iOS\")) os = \"iOS\";\n\n return {\n browser,\n os,\n resolution: `${window.screen.width} × ${window.screen.height}`,\n pixelRatio: window.devicePixelRatio + \"x\",\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n };\n});\n\n// 组件卸载时清理水印\nonUnmounted(() => {\n removeWatermark();\n});\n\n// 处理布局切换 - 阻止抽屉关闭\nconst handleLayoutChange = (value: string, disabled?: boolean) => {\n if (disabled) return;\n settingsStore.layoutMode = value as any;\n};\n\n// ============ 方法 ============\n\n/**\n * 判断是否是当前预设\n */\nconst isCurrentPreset = (preset: ThemePreset) => {\n return settingsStore.primaryColor === preset.primaryColor;\n};\n\n/**\n * 应用预设方案\n */\nconst handleApplyPreset = (preset: ThemePreset) => {\n settingsStore.applyPreset(preset);\n message.success(`已应用「${preset.name}」主题方案`);\n};\n\n/**\n * 恢复外观默认设置\n */\nconst handleResetAppearance = () => {\n settingsStore.themeMode = DEFAULT_SETTINGS.themeMode;\n settingsStore.primaryColor = DEFAULT_SETTINGS.primaryColor;\n settingsStore.borderRadius = DEFAULT_SETTINGS.borderRadius;\n settingsStore.transitionType = DEFAULT_SETTINGS.transitionType;\n settingsStore.enableTransition = DEFAULT_SETTINGS.enableTransition;\n message.success(\"已恢复外观默认设置\");\n};\n\n/**\n * 恢复布局默认设置\n */\nconst handleResetLayout = () => {\n settingsStore.layoutMode = DEFAULT_SETTINGS.layoutMode;\n settingsStore.fixedHeader = DEFAULT_SETTINGS.fixedHeader;\n settingsStore.showBreadcrumb = DEFAULT_SETTINGS.showBreadcrumb;\n settingsStore.showBreadcrumbIcon = DEFAULT_SETTINGS.showBreadcrumbIcon;\n settingsStore.showTagsView = DEFAULT_SETTINGS.showTagsView;\n settingsStore.tagsViewHeight = DEFAULT_SETTINGS.tagsViewHeight;\n settingsStore.tagsViewStyle = DEFAULT_SETTINGS.tagsViewStyle;\n settingsStore.showFooter = DEFAULT_SETTINGS.showFooter;\n settingsStore.sidebarWidth = DEFAULT_SETTINGS.sidebarWidth;\n settingsStore.sidebarCollapsedWidth = DEFAULT_SETTINGS.sidebarCollapsedWidth;\n settingsStore.headerHeight = DEFAULT_SETTINGS.headerHeight;\n message.success(\"已恢复布局默认设置\");\n};\n\n/**\n * 重置配置\n */\nconst handleReset = () => {\n dialog.warning({\n title: \"确认重置\",\n content: \"确定要恢复默认配置吗?此操作不可撤销。\",\n positiveText: \"确认\",\n negativeText: \"取消\",\n onPositiveClick: () => {\n settingsStore.resetSettings();\n message.success(\"已恢复默认配置\");\n },\n });\n};\n\n/**\n * 清除缓存\n */\nconst handleClearCache = () => {\n dialog.warning({\n title: \"确认清除\",\n content: \"确定要清除浏览器缓存吗?\",\n positiveText: \"确认\",\n negativeText: \"取消\",\n onPositiveClick: () => {\n // 清除 localStorage\n const keysToKeep = [\"theme-mode\", \"robot-admin-settings\"];\n const allKeys = Object.keys(localStorage);\n allKeys.forEach((key) => {\n if (!keysToKeep.includes(key)) {\n localStorage.removeItem(key);\n }\n });\n // 清除 sessionStorage\n sessionStorage.clear();\n message.success(\"缓存已清除\");\n },\n });\n};\n\n/**\n * 重新加载页面\n */\nconst handleReload = () => {\n location.reload();\n};\n\n/**\n * 导出配置\n */\nconst handleExportConfig = () => {\n const config = {\n settings: settingsStore.settingsState,\n gray: grayMode.value,\n colorWeak: colorWeakMode.value,\n watermark: {\n enabled: watermarkEnabled.value,\n text: watermarkText.value,\n },\n exportTime: new Date().toISOString(),\n };\n const blob = new Blob([JSON.stringify(config, null, 2)], {\n type: \"application/json\",\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `robot-admin-config-${Date.now()}.json`;\n a.click();\n URL.revokeObjectURL(url);\n message.success(\"配置已导出\");\n};\n\n/**\n * 导入配置\n */\nconst handleImportConfig = () => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = \"application/json\";\n input.onchange = async (e: Event) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (!file) return;\n\n try {\n const text = await file.text();\n const config = JSON.parse(text);\n\n // 应用配置\n if (config.settings) {\n settingsStore.$patch(config.settings);\n }\n if (config.gray !== undefined) {\n grayMode.value = config.gray;\n }\n if (config.colorWeak !== undefined) {\n colorWeakMode.value = config.colorWeak;\n }\n if (config.watermark) {\n watermarkEnabled.value = config.watermark.enabled;\n watermarkText.value = config.watermark.text;\n }\n\n message.success(\"配置已导入\");\n } catch (error) {\n message.error(\"配置文件格式错误\");\n }\n };\n input.click();\n};\n</script>\n\n<style scoped lang=\"scss\">\n@use \"../../styles/settings.scss\" as *;\n</style>\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 - FloatingMenu\r\n *\r\n * 悬浮二级菜单面板\r\n * 用于 MixLayout 的悬浮二级菜单(点击一级菜单后展开)\r\n -->\r\n<template>\r\n <Transition name=\"slide-fade\">\r\n <div\r\n v-if=\"show && menuItem\"\r\n class=\"floating-menu\"\r\n :class=\"[ctx.isDark.value ? 'dark-theme' : 'light-theme']\"\r\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\"\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=\"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\"\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=\"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 {\r\n background: rgba(99, 102, 241, 0.08);\r\n}\r\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 * 用于 ReverseHorizontalMixLayout 的右侧二级菜单\r\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 * 用于 CardLayout 的 hover 触发的抽屉式功能导航\r\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 @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 <i class=\"i-ri:apps-2-line\"></i>\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 class=\"drawer-menu__category-header\" @click=\"navigate(category)\">\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 @click=\"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 @click=\"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 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 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 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__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</style>\r\n","<!--\r\n * @robot-admin/layout - MenuTrigger\r\n *\r\n * 菜单触发区域\r\n * 用于 CardLayout 的 hover 触发抽屉式菜单\r\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 <i class=\"i-ri:menu-3-line\"></i>\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</style>\r\n","/**\n * @robot-admin/layout\n *\n * 布局系统一键初始化\n */\n\nimport type { App } from \"vue\";\nimport type { SettingsStoreOptions } from \"./types\";\nimport { createSettingsStore } from \"./stores/settings\";\n\n/**\n * 初始化布局系统\n *\n * @example\n * ```ts\n * import { setupLayout } from '@robot-admin/layout'\n * setupLayout(app, {\n * onThemeModeChange: async (mode) => {\n * const themeStore = useThemeStore()\n * await themeStore.setMode(mode)\n * }\n * })\n * ```\n */\nexport function setupLayout(_app: App, options: SettingsStoreOptions = {}) {\n // 注册 settings store\n const settingsStore = createSettingsStore(options);\n\n // 初始化 CSS Variables\n settingsStore().syncCSSVariables();\n\n return settingsStore;\n}\n"],"names":["PRESET_COLORS","THEME_PRESETS","LAYOUT_MODES","BORDER_RADIUS_MAP","TRANSITION_MAP","DEFAULT_SETTINGS","COLOR_SWATCHES","c","adjustColor","color","amount","num","r","g","b","createSettingsStore","options","defaults","onThemeModeChange","finalDefaults","defineStore","themeMode","ref","primaryColor","borderRadius","transitionType","enableTransition","layoutMode","collapsed","fixedHeader","showBreadcrumb","showBreadcrumbIcon","showTagsView","tagsViewHeight","tagsViewStyle","showFooter","sidebarWidth","sidebarCollapsedWidth","headerHeight","enableHotkeys","version","borderRadiusValue","computed","transitionName","shouldEnableTransition","settingsState","syncCSSVariables","root","applyPreset","preset","resetSettings","updateThemeMode","mode","toggleCollapse","watch","useSettingsStore","LAYOUT_CONTEXT_KEY","DRAWER_HANDLER_KEY","useLayoutContext","ctx","inject","DEFAULT_BRAND_CONFIG","useLayoutCache","maxCount","enableDevLog","expose","route","useRoute","cachedViews","maxCacheCount","shouldCache","routeName","addCache","name","removeCache","index","clearAllCache","newName","props","__props","emit","__emit","_collapsed","v","handleCollapsedUpdate","val","__expose","next","_createBlock","_unref","NLayout","_createVNode","NLayoutSider","_normalizeClass","_renderSlot","_ctx","NLayoutContent","_component_RouterView","Component","_Transition","_KeepAlive","_openBlock","_resolveDynamicComponent","router","useRouter","menuData","activeKey","isDarkMode","lightTheme","darkTheme","menuThemeOverrides","renderIcon","icon","size","h","convertToMenuOption","item","containerRef","visibleItems","hiddenItems","visibleOptions","dropdownOptions","child","handleSelect","key","estimateItemWidth","textWidth","iconWidth","calculateVisibleCount","containerWidth","MORE_BTN_WIDTH","SAFETY","totalWidth","usedWidth","count","available","w","calculateVisibleItems","nextTick","resizeTimer","debouncedCalculate","resizeObserver","onMounted","onUnmounted","_createElementBlock","_createElementVNode","_hoisted_1","NMenu","NDropdown","_hoisted_2","NButton","_cache","menus","brand","_hoisted_3","_hoisted_4","_hoisted_6","_hoisted_7","_toDisplayString","_hoisted_8","_hoisted_9","ResponsiveMenu","_hoisted_10","useMenuSplit","floatingSecondMenu","activeFirstMenu","hoveredMenuItem","showSecondMenu","normalizePath","path","isMenuItemActive","menuPath","currentPath","normalizedMenuPath","normalizedCurrentPath","findActiveTopMenu","items","activeFirstMenuItem","currentSecondMenus","handleFirstMenuClick","toValue","handleSecondMenuClick","updateActiveMenuByRoute","menuList","matchedFirstMenu","newMenus","menuSplit","LayoutIcon","defineComponent","_hoisted_5","_Fragment","_renderList","$event","_hoisted_11","_hoisted_12","_hoisted_14","_hoisted_15","_hoisted_13","_hoisted_16","isCollapsed","_withDirectives","_hoisted_17","subChild","_hoisted_19","_hoisted_21","showDrawerMenu","hideTimer","hideDrawerMenu","cancelHideTimer","navigateToPage","category","subItem","_hoisted_18","_hoisted_20","settingsStore","isDark","sideCollapsed","provide","SideLayout","$slots","TopLayout","_createSlots","MixLayout","MixTopLayout","ReverseHorizontalMixLayout","CardLayout","LAYOUT_MODE_OPTIONS","message","useMessage","dialog","useDialog","visible","_useModel","activeTab","grayMode","colorWeakMode","watermarkEnabled","watermarkText","watermarkEl","createWatermark","text","canvas","removeWatermark","systemInfo","ua","browser","os","handleLayoutChange","value","disabled","isCurrentPreset","handleApplyPreset","handleResetAppearance","handleResetLayout","handleReset","handleClearCache","keysToKeep","handleReload","handleExportConfig","config","blob","url","a","handleImportConfig","input","e","file","NDrawer","NDrawerContent","NTabs","NTabPane","_normalizeStyle","NRadioGroup","NRadioButton","NColorPicker","NSwitch","_withModifiers","_hoisted_22","_hoisted_23","_hoisted_24","_hoisted_25","_hoisted_26","_hoisted_27","_hoisted_28","_hoisted_29","_hoisted_30","_hoisted_31","NSlider","_hoisted_32","_hoisted_33","_hoisted_34","_hoisted_35","_hoisted_36","_hoisted_37","_hoisted_38","_hoisted_39","_hoisted_40","_hoisted_41","_hoisted_42","_hoisted_43","_hoisted_44","_hoisted_45","NInput","_hoisted_46","_hoisted_47","_hoisted_48","_hoisted_49","_hoisted_50","_hoisted_51","_hoisted_52","_hoisted_53","_hoisted_54","_hoisted_55","_hoisted_56","_hoisted_57","_hoisted_58","_hoisted_59","brandConfig","handlers","navigate","setupLayout","_app"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["$slots"],"sources":["../src/constants/index.ts","../src/stores/settings.ts","../src/composables/useLayoutContext.ts","../src/composables/useLayoutCache.ts","../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/composables/useMenuSplit.ts","../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/setup.ts"],"sourcesContent":["/**\n * 布局和设置系统 - 常量定义\n */\n\nimport type {\n ThemePreset,\n SettingsState,\n LayoutInfo,\n PresetColor,\n} from \"../types\";\n\n// ============ 主题色预设 ============\nexport const PRESET_COLORS: PresetColor[] = [\n { name: \"拂晓蓝\", value: \"#409eff\" },\n { name: \"薄暮红\", value: \"#f5222d\" },\n { name: \"火山橙\", value: \"#fa541c\" },\n { name: \"日暮黄\", value: \"#faad14\" },\n { name: \"极光绿\", value: \"#52c41a\" },\n { name: \"明青\", value: \"#13c2c2\" },\n { name: \"极客蓝\", value: \"#2f54eb\" },\n { name: \"酱紫\", value: \"#722ed1\" },\n] as const;\n\n// ============ 主题预设方案 ============\nexport const THEME_PRESETS: ThemePreset[] = [\n {\n name: \"科技蓝\",\n icon: \"💙\",\n primaryColor: \"#409eff\",\n },\n {\n name: \"清新绿\",\n icon: \"💚\",\n primaryColor: \"#52c41a\",\n },\n {\n name: \"商务灰\",\n icon: \"🖤\",\n primaryColor: \"#595959\",\n },\n {\n name: \"活力橙\",\n icon: \"🧡\",\n primaryColor: \"#fa8c16\",\n },\n {\n name: \"优雅紫\",\n icon: \"💜\",\n primaryColor: \"#722ed1\",\n },\n {\n name: \"经典红\",\n icon: \"❤️\",\n primaryColor: \"#f5222d\",\n },\n];\n\n// ============ 布局模式信息 ============\nexport const LAYOUT_MODES: LayoutInfo[] = [\n {\n mode: \"side\",\n label: \"左侧菜单\",\n description: \"经典的左侧导航布局\",\n icon: \"i-mdi:page-layout-sidebar-left\",\n },\n {\n mode: \"top\",\n label: \"顶部菜单\",\n description: \"顶部水平导航布局\",\n icon: \"i-mdi:page-layout-header\",\n },\n {\n mode: \"mix\",\n label: \"混合菜单\",\n description: \"顶部 + 侧边混合布局\",\n icon: \"i-mdi:view-dashboard-outline\",\n },\n {\n mode: \"mix-top\",\n label: \"顶部混合\",\n description: \"侧边优先的混合布局\",\n icon: \"i-mdi:view-dashboard-variant-outline\",\n },\n {\n mode: \"reverse-horizontal-mix\",\n label: \"反转混合\",\n description: \"反转的水平混合布局\",\n icon: \"i-mdi:page-layout-header-footer\",\n },\n {\n mode: \"card-layout\",\n label: \"卡片布局\",\n description: \"卡片式布局风格\",\n icon: \"i-mdi:card-outline\",\n },\n];\n\n// ============ 圆角映射 ============\nexport const BORDER_RADIUS_MAP = {\n small: \"4px\",\n medium: \"6px\",\n large: \"8px\",\n} as const;\n\n// ============ 动画映射 ============\nexport const TRANSITION_MAP = {\n fade: \"fade-transform\",\n slide: \"fade-slide\",\n zoom: \"fade-zoom\",\n none: \"\",\n} as const;\n\n// ============ 默认配置 ============\nexport const DEFAULT_SETTINGS: SettingsState = {\n // 外观\n themeMode: \"light\",\n primaryColor: \"#409eff\",\n borderRadius: \"medium\",\n transitionType: \"slide\",\n enableTransition: true,\n\n // 布局\n layoutMode: \"side\",\n menuExpandMode: \"inline\",\n collapsed: false,\n fixedHeader: true,\n showBreadcrumb: true,\n showBreadcrumbIcon: true,\n showTagsView: true,\n tagsViewHeight: 44,\n tagsViewStyle: \"default\",\n showFooter: true,\n sidebarWidth: 220,\n sidebarCollapsedWidth: 64,\n headerHeight: 56,\n\n // 高级\n enableHotkeys: true,\n version: \"2.0.2\",\n};\n\n// ============ 其他常量 ============\n\n/** Color swatches for color picker */\nexport const COLOR_SWATCHES = PRESET_COLORS.map((c) => c.value);\n","import { defineStore } from \"pinia\";\nimport { ref, computed, watch } from \"vue\";\nimport type { ThemeMode } from \"@robot-admin/theme\";\nimport type {\n SettingsState,\n ThemePreset,\n LayoutMode,\n MenuExpandMode,\n TransitionType,\n BorderRadiusSize,\n TagsViewStyle,\n SettingsStoreOptions,\n} from \"../types\";\nimport {\n DEFAULT_SETTINGS,\n BORDER_RADIUS_MAP,\n TRANSITION_MAP,\n} from \"../constants\";\n\n/**\n * 调整颜色亮度\n * @param color - 十六进制颜色值\n * @param amount - 调整量,正数变亮,负数变暗\n */\nexport function adjustColor(color: string, amount: number): string {\n const num = parseInt(color.replace(\"#\", \"\"), 16);\n const r = Math.min(255, Math.max(0, (num >> 16) + amount));\n const g = Math.min(255, Math.max(0, ((num >> 8) & 0x00ff) + amount));\n const b = Math.min(255, Math.max(0, (num & 0x0000ff) + amount));\n return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, \"0\")}`;\n}\n\n/**\n * 创建设置管理 Store\n * @param options - 配置选项\n */\nexport function createSettingsStore(options: SettingsStoreOptions = {}) {\n const { defaults = {}, onThemeModeChange } = options;\n\n // 合并默认配置\n const finalDefaults = {\n ...DEFAULT_SETTINGS,\n ...defaults,\n };\n\n return defineStore(\"settings\", () => {\n // ============ 状态定义 ============\n\n // 外观设置\n const themeMode = ref<ThemeMode>(finalDefaults.themeMode);\n const primaryColor = ref<string>(finalDefaults.primaryColor);\n const borderRadius = ref<BorderRadiusSize>(finalDefaults.borderRadius);\n const transitionType = ref<TransitionType>(finalDefaults.transitionType);\n const enableTransition = ref<boolean>(finalDefaults.enableTransition);\n\n // 布局设置\n const layoutMode = ref<LayoutMode>(finalDefaults.layoutMode);\n const menuExpandMode = ref<MenuExpandMode>(finalDefaults.menuExpandMode);\n const collapsed = ref<boolean>(finalDefaults.collapsed);\n const fixedHeader = ref<boolean>(finalDefaults.fixedHeader);\n const showBreadcrumb = ref<boolean>(finalDefaults.showBreadcrumb);\n const showBreadcrumbIcon = ref<boolean>(finalDefaults.showBreadcrumbIcon);\n const showTagsView = ref<boolean>(finalDefaults.showTagsView);\n const tagsViewHeight = ref<number>(finalDefaults.tagsViewHeight);\n const tagsViewStyle = ref<TagsViewStyle>(finalDefaults.tagsViewStyle);\n const showFooter = ref<boolean>(finalDefaults.showFooter);\n const sidebarWidth = ref<number>(finalDefaults.sidebarWidth);\n const sidebarCollapsedWidth = ref<number>(\n finalDefaults.sidebarCollapsedWidth,\n );\n const headerHeight = ref<number>(finalDefaults.headerHeight);\n\n // 高级设置\n const enableHotkeys = ref<boolean>(finalDefaults.enableHotkeys);\n const version = ref<string>(finalDefaults.version);\n\n // ============ 计算属性 ============\n\n /** 获取圆角值 */\n const borderRadiusValue = computed(\n () => BORDER_RADIUS_MAP[borderRadius.value],\n );\n\n /** 获取过渡动画名称 */\n const transitionName = computed(() => TRANSITION_MAP[transitionType.value]);\n\n /** 是否启用过渡动画 */\n const shouldEnableTransition = computed(\n () => enableTransition.value && transitionType.value !== \"none\",\n );\n\n /** 获取完整的设置状态 */\n const settingsState = computed<SettingsState>(() => ({\n themeMode: themeMode.value,\n primaryColor: primaryColor.value,\n borderRadius: borderRadius.value,\n transitionType: transitionType.value,\n enableTransition: enableTransition.value,\n layoutMode: layoutMode.value,\n menuExpandMode: menuExpandMode.value,\n collapsed: collapsed.value,\n fixedHeader: fixedHeader.value,\n showBreadcrumb: showBreadcrumb.value,\n showBreadcrumbIcon: showBreadcrumbIcon.value,\n showTagsView: showTagsView.value,\n tagsViewHeight: tagsViewHeight.value,\n tagsViewStyle: tagsViewStyle.value,\n showFooter: showFooter.value,\n sidebarWidth: sidebarWidth.value,\n sidebarCollapsedWidth: sidebarCollapsedWidth.value,\n headerHeight: headerHeight.value,\n enableHotkeys: enableHotkeys.value,\n version: version.value,\n }));\n\n // ============ 方法 ============\n\n /**\n * 同步 CSS Variables\n */\n const syncCSSVariables = () => {\n if (typeof document === \"undefined\") return;\n\n const root = document.documentElement;\n\n // 主题色\n root.style.setProperty(\"--primary-color\", primaryColor.value);\n root.style.setProperty(\n \"--primary-color-hover\",\n adjustColor(primaryColor.value, 10),\n );\n root.style.setProperty(\n \"--primary-color-pressed\",\n adjustColor(primaryColor.value, -10),\n );\n\n // 圆角\n root.style.setProperty(\"--border-radius\", borderRadiusValue.value);\n\n // 尺寸\n root.style.setProperty(\"--sidebar-width\", `${sidebarWidth.value}px`);\n root.style.setProperty(\n \"--sidebar-collapsed-width\",\n `${sidebarCollapsedWidth.value}px`,\n );\n root.style.setProperty(\"--header-height\", `${headerHeight.value}px`);\n root.style.setProperty(\"--tags-view-height\", `${tagsViewHeight.value}px`);\n };\n\n /**\n * 应用主题预设方案\n * 注意:只应用颜色配置,不改变布局模式和主题模式\n */\n const applyPreset = async (preset: ThemePreset) => {\n // 只应用主题色\n primaryColor.value = preset.primaryColor;\n\n // 应用其他细节配置(如果存在)\n if (preset.settings) {\n if (preset.settings.borderRadius)\n borderRadius.value = preset.settings.borderRadius;\n if (preset.settings.transitionType)\n transitionType.value = preset.settings.transitionType;\n if (preset.settings.showBreadcrumb !== undefined)\n showBreadcrumb.value = preset.settings.showBreadcrumb;\n if (preset.settings.showTagsView !== undefined)\n showTagsView.value = preset.settings.showTagsView;\n if (preset.settings.tagsViewStyle)\n tagsViewStyle.value = preset.settings.tagsViewStyle;\n }\n };\n\n /**\n * 重置配置\n */\n const resetSettings = () => {\n themeMode.value = finalDefaults.themeMode;\n primaryColor.value = finalDefaults.primaryColor;\n borderRadius.value = finalDefaults.borderRadius;\n transitionType.value = finalDefaults.transitionType;\n enableTransition.value = finalDefaults.enableTransition;\n\n layoutMode.value = finalDefaults.layoutMode;\n menuExpandMode.value = finalDefaults.menuExpandMode;\n collapsed.value = finalDefaults.collapsed;\n fixedHeader.value = finalDefaults.fixedHeader;\n showBreadcrumb.value = finalDefaults.showBreadcrumb;\n showBreadcrumbIcon.value = finalDefaults.showBreadcrumbIcon;\n showTagsView.value = finalDefaults.showTagsView;\n tagsViewHeight.value = finalDefaults.tagsViewHeight;\n tagsViewStyle.value = finalDefaults.tagsViewStyle;\n showFooter.value = finalDefaults.showFooter;\n sidebarWidth.value = finalDefaults.sidebarWidth;\n sidebarCollapsedWidth.value = finalDefaults.sidebarCollapsedWidth;\n headerHeight.value = finalDefaults.headerHeight;\n\n enableHotkeys.value = finalDefaults.enableHotkeys;\n };\n\n /**\n * 更新主题模式\n */\n const updateThemeMode = async (mode: ThemeMode) => {\n themeMode.value = mode;\n if (onThemeModeChange) {\n await onThemeModeChange(mode);\n }\n };\n\n /**\n * 切换侧边栏折叠状态\n */\n const toggleCollapse = () => {\n collapsed.value = !collapsed.value;\n };\n\n // ============ 监听器 ============\n\n // 监听配置变化,同步 CSS Variables\n watch(\n [\n primaryColor,\n borderRadiusValue,\n sidebarWidth,\n sidebarCollapsedWidth,\n headerHeight,\n tagsViewHeight,\n ],\n () => {\n syncCSSVariables();\n },\n { immediate: true },\n );\n\n // ============ 返回 ============\n\n return {\n // 状态\n themeMode,\n primaryColor,\n borderRadius,\n transitionType,\n enableTransition,\n layoutMode,\n menuExpandMode,\n collapsed,\n fixedHeader,\n showBreadcrumb,\n showBreadcrumbIcon,\n showTagsView,\n tagsViewHeight,\n tagsViewStyle,\n showFooter,\n sidebarWidth,\n sidebarCollapsedWidth,\n headerHeight,\n enableHotkeys,\n version,\n\n // 计算属性\n borderRadiusValue,\n transitionName,\n shouldEnableTransition,\n settingsState,\n\n // 方法\n syncCSSVariables,\n applyPreset,\n resetSettings,\n updateThemeMode,\n toggleCollapse,\n };\n });\n}\n\n/**\n * 默认的设置 Store(使用默认配置)\n */\nexport const useSettingsStore = createSettingsStore();\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 布局上下文 - Provide/Inject 机制\r\n * 消费方通过 provide 注入数据,骨架组件通过 inject 读取\r\n * 实现布局骨架与业务数据的完全解耦\r\n */\r\n\r\nimport type { InjectionKey, ComputedRef, Component } from \"vue\";\r\nimport { inject } from \"vue\";\r\nimport type { MenuOptions } from \"../types/menu\";\r\nimport type { LayoutMode, MenuExpandMode } from \"../types\";\r\n\r\n/**\r\n * 品牌配置\r\n */\r\nexport interface LayoutBrandConfig {\r\n /** 品牌名称,如 'Robot Admin' */\r\n name?: string;\r\n /** 品牌副标题,如 '机器人管理系统' */\r\n subtitle?: string;\r\n /** Logo 地址(图片或视频) */\r\n logoSrc?: string;\r\n /** Logo 类型 */\r\n logoType?: \"video\" | \"image\";\r\n /** Logo 尺寸 */\r\n logoSize?: number;\r\n /** 首页路径,默认 '/home' */\r\n homePath?: string;\r\n /** 页脚版权文本 */\r\n copyright?: string;\r\n}\r\n\r\n/**\r\n * 布局上下文数据接口\r\n * 骨架组件通过 inject 获取这些数据\r\n */\r\nexport interface LayoutContext {\r\n // ============ 菜单数据 ============\r\n /** 菜单列表数据 */\r\n menus: ComputedRef<MenuOptions[]>;\r\n\r\n // ============ 主题状态 ============\r\n /** 是否为暗色模式 */\r\n isDark: ComputedRef<boolean>;\r\n\r\n // ============ 布局配置(来自 settings store) ============\r\n /** 当前布局模式 */\r\n layoutMode: ComputedRef<LayoutMode>;\r\n /** 菜单展开方式 */\r\n menuExpandMode?: ComputedRef<MenuExpandMode>;\r\n /** 侧边栏宽度 (px) */\r\n sidebarWidth: ComputedRef<number>;\r\n /** 侧边栏折叠后宽度 (px) */\r\n sidebarCollapsedWidth: ComputedRef<number>;\r\n /** 是否显示页脚 */\r\n showFooter: ComputedRef<boolean>;\r\n /** 是否显示标签页 */\r\n showTagsView: ComputedRef<boolean>;\r\n /** 标签页高度 (px) */\r\n tagsViewHeight: ComputedRef<number>;\r\n /** 头部高度 (px) */\r\n headerHeight: ComputedRef<number>;\r\n /** 过渡动画名称 */\r\n transitionName: ComputedRef<string>;\r\n /** 是否显示面包屑 */\r\n showBreadcrumb: ComputedRef<boolean>;\r\n /** 是否显示面包屑图标 */\r\n showBreadcrumbIcon: ComputedRef<boolean>;\r\n /** 是否固定头部 */\r\n fixedHeader: ComputedRef<boolean>;\r\n\r\n // ============ 品牌配置 ============\r\n /** 品牌信息 */\r\n brand: LayoutBrandConfig;\r\n\r\n // ============ 可选:图标组件 ============\r\n /**\r\n * 自定义图标组件(如 @iconify/vue 的 Icon 组件)\r\n * 用于布局内部组件渲染菜单图标\r\n * 组件需接受 name (string) 和 size (number) 两个 props\r\n * 如果不提供,将使用 CSS class 渲染图标\r\n */\r\n iconComponent?: Component;\r\n}\r\n\r\n/**\r\n * 布局上下文 Injection Key\r\n */\r\nexport const LAYOUT_CONTEXT_KEY: InjectionKey<LayoutContext> =\r\n Symbol(\"layout-context\");\r\n\r\n/**\r\n * 抽屉菜单统一控制器\r\n * 解决 MenuTrigger 与 DrawerMenu 各自拥有独立定时器导致的\r\n * \"鼠标从 trigger 移到 drawer 过程中 drawer 被关闭\"问题\r\n */\r\nexport interface DrawerHandlers {\r\n /** 显示抽屉并取消所有待执行的隐藏操作 */\r\n show: () => void;\r\n /** 启动延迟隐藏(300ms) */\r\n startHide: () => void;\r\n /** 立即隐藏抽屉(如导航跳转后) */\r\n hide: () => void;\r\n}\r\n\r\n/**\r\n * 抽屉菜单控制器 Injection Key\r\n */\r\nexport const DRAWER_HANDLER_KEY: InjectionKey<DrawerHandlers> =\r\n Symbol(\"drawer-handlers\");\r\n\r\n/**\r\n * 在骨架组件中获取布局上下文\r\n *\r\n * @throws 如果没有 provide LayoutContext 会抛出错误\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * // 在包的骨架组件内部使用\r\n * const ctx = useLayoutContext()\r\n * const menus = ctx.menus\r\n * const isDark = ctx.isDark\r\n * </script>\r\n * ```\r\n */\r\nexport function useLayoutContext(): LayoutContext {\r\n const ctx = inject(LAYOUT_CONTEXT_KEY);\r\n if (!ctx) {\r\n throw new Error(\r\n \"[Layout] LayoutContext not provided. \" +\r\n \"请在上层组件中使用 provide(LAYOUT_CONTEXT_KEY, ...) 注入布局数据。\",\r\n );\r\n }\r\n return ctx;\r\n}\r\n\r\n/**\r\n * 默认品牌配置\r\n */\r\nexport const DEFAULT_BRAND_CONFIG: Required<LayoutBrandConfig> = {\r\n name: \"Admin\",\r\n subtitle: \"\",\r\n logoSrc: \"\",\r\n logoType: \"image\",\r\n logoSize: 36,\r\n homePath: \"/home\",\r\n copyright: \"\",\r\n};\r\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 统一的 KeepAlive 缓存管理\r\n * 从主项目提取的通用 composable,零业务依赖\r\n */\r\n\r\nimport { ref, watch } from \"vue\";\r\nimport { useRoute } from \"vue-router\";\r\n\r\n/** KeepAlive 缓存选项 */\r\nexport interface LayoutCacheOptions {\r\n /** 最大缓存数量,默认 20 */\r\n maxCacheCount?: number;\r\n /** 是否在开发模式下输出日志,默认 true */\r\n enableDevLog?: boolean;\r\n /** 是否暴露调试方法到 window,默认 true */\r\n exposeToWindow?: boolean;\r\n}\r\n\r\n/**\r\n * 统一的 KeepAlive 缓存管理 composable\r\n *\r\n * @param options - 缓存配置选项\r\n * @returns 缓存视图列表和管理方法\r\n *\r\n * @example\r\n * ```vue\r\n * <script setup>\r\n * import { useLayoutCache } from '@robot-admin/layout'\r\n *\r\n * const { cachedViews, maxCacheCount } = useLayoutCache({ maxCacheCount: 30 })\r\n * </script>\r\n *\r\n * <template>\r\n * <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\r\n * <RouterView />\r\n * </KeepAlive>\r\n * </template>\r\n * ```\r\n */\r\nexport function useLayoutCache(options: LayoutCacheOptions = {}) {\r\n const {\r\n maxCacheCount: maxCount = 20,\r\n enableDevLog = true,\r\n exposeToWindow: expose = true,\r\n } = options;\r\n\r\n const route = useRoute();\r\n const cachedViews = ref<string[]>([]);\r\n const maxCacheCount = ref(maxCount);\r\n\r\n const isDev =\r\n typeof import.meta !== \"undefined\" &&\r\n (import.meta as any).env?.DEV === true;\r\n\r\n /**\r\n * 判断页面是否应该被缓存\r\n * 极简策略:只有明确配置 meta.keepAlive = true 才缓存\r\n */\r\n const shouldCache = (routeName: string | symbol | undefined | null) => {\r\n if (!routeName || typeof routeName !== \"string\") return false;\r\n return route.meta?.keepAlive === true;\r\n };\r\n\r\n /** 添加缓存 */\r\n const addCache = (name: string) => {\r\n if (!cachedViews.value.includes(name) && shouldCache(name)) {\r\n cachedViews.value.push(name);\r\n\r\n // 控制缓存数量\r\n if (cachedViews.value.length > maxCacheCount.value) {\r\n cachedViews.value.shift();\r\n }\r\n\r\n if (isDev && enableDevLog) {\r\n console.debug(\r\n `[KeepAlive] ✅ 缓存: ${name} (${cachedViews.value.length}/${maxCacheCount.value})`,\r\n );\r\n }\r\n }\r\n };\r\n\r\n /** 移除缓存 */\r\n const removeCache = (name: string) => {\r\n const index = cachedViews.value.indexOf(name);\r\n if (index > -1) {\r\n cachedViews.value.splice(index, 1);\r\n if (isDev && enableDevLog) {\r\n console.debug(`[KeepAlive] ❌ 移除: ${name}`);\r\n }\r\n }\r\n };\r\n\r\n /** 清空所有缓存 */\r\n const clearAllCache = () => {\r\n cachedViews.value = [];\r\n if (isDev && enableDevLog) {\r\n console.debug(\"[KeepAlive] 🗑️ 清空所有缓存\");\r\n }\r\n };\r\n\r\n // 暴露调试方法到 window\r\n if (isDev && expose && typeof window !== \"undefined\") {\r\n (window as any).__clearCache__ = clearAllCache;\r\n (window as any).__removeCache__ = removeCache;\r\n (window as any).__getCachedViews__ = () => cachedViews.value;\r\n }\r\n\r\n // 监听路由变化,动态管理缓存\r\n watch(\r\n () => route.name,\r\n (newName) => {\r\n if (newName && typeof newName === \"string\") {\r\n addCache(newName);\r\n }\r\n },\r\n { immediate: true },\r\n );\r\n\r\n return {\r\n cachedViews,\r\n maxCacheCount,\r\n addCache,\r\n removeCache,\r\n clearAllCache,\r\n };\r\n}\r\n","<!--\n * @robot-admin/layout - SideLayout\n *\n * 左侧菜单布局骨架\n * 经典的左侧导航 + 右侧内容布局,使用 NLayout/NLayoutSider 实现\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #menu - 垂直菜单区域(侧边栏)\n * #header - 头部区域(含面包屑、导航栏右侧操作等)\n * #default - 页面内容(自动包含 RouterView + KeepAlive + Transition)\n * #footer - 页脚区域\n -->\n<template>\n <NLayout has-sider>\n <NLayoutSider\n bordered\n collapse-mode=\"width\"\n :collapsed-width=\"ctx.sidebarCollapsedWidth.value\"\n :width=\"ctx.sidebarWidth.value\"\n :native-scrollbar=\"false\"\n :collapsed=\"collapsed\"\n @update:collapsed=\"handleCollapsedUpdate\"\n :class=\"[\n 'layout-sider',\n 'no-horizontal-scroll',\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\n ]\"\n >\n <slot name=\"logo\" />\n <slot name=\"menu\" :collapsed=\"collapsed\" />\n </NLayoutSider>\n\n <NLayout>\n <slot name=\"header\" />\n\n <NLayoutContent class=\"content-with-header p16px app-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"ctx.transitionName.value\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </NLayoutContent>\n\n <slot name=\"footer\" />\n </NLayout>\n </NLayout>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from \"vue\";\nimport { NLayout, NLayoutSider, NLayoutContent } from \"naive-ui\";\nimport { useLayoutContext } from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\n\ndefineOptions({ name: \"SideLayout\" });\n\nconst props = withDefaults(\n defineProps<{\n /** 控制侧边栏折叠状态 (支持 v-model:collapsed) */\n collapsed?: boolean;\n }>(),\n { collapsed: false },\n);\n\nconst emit = defineEmits<{\n \"update:collapsed\": [value: boolean];\n}>();\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\n// 内部折叠状态,与 prop 双向同步\nconst _collapsed = ref(props.collapsed);\nwatch(\n () => props.collapsed,\n (v) => {\n _collapsed.value = v;\n },\n);\n\nconst collapsed = computed(() => _collapsed.value);\n\nconst handleCollapsedUpdate = (val: boolean) => {\n _collapsed.value = val;\n emit(\"update:collapsed\", val);\n};\n\n// 折叠切换\nconst toggleCollapse = () => {\n const next = !_collapsed.value;\n _collapsed.value = next;\n emit(\"update:collapsed\", next);\n};\n\ndefineExpose({\n collapsed,\n toggleCollapse,\n});\n</script>\n","<!--\n * @robot-admin/layout - SideLayout\n *\n * 左侧菜单布局骨架\n * 经典的左侧导航 + 右侧内容布局,使用 NLayout/NLayoutSider 实现\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #menu - 垂直菜单区域(侧边栏)\n * #header - 头部区域(含面包屑、导航栏右侧操作等)\n * #default - 页面内容(自动包含 RouterView + KeepAlive + Transition)\n * #footer - 页脚区域\n -->\n<template>\n <NLayout has-sider>\n <NLayoutSider\n bordered\n collapse-mode=\"width\"\n :collapsed-width=\"ctx.sidebarCollapsedWidth.value\"\n :width=\"ctx.sidebarWidth.value\"\n :native-scrollbar=\"false\"\n :collapsed=\"collapsed\"\n @update:collapsed=\"handleCollapsedUpdate\"\n :class=\"[\n 'layout-sider',\n 'no-horizontal-scroll',\n ctx.isDark.value ? 'dark-theme' : 'light-theme',\n ]\"\n >\n <slot name=\"logo\" />\n <slot name=\"menu\" :collapsed=\"collapsed\" />\n </NLayoutSider>\n\n <NLayout>\n <slot name=\"header\" />\n\n <NLayoutContent class=\"content-with-header p16px app-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"ctx.transitionName.value\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </NLayoutContent>\n\n <slot name=\"footer\" />\n </NLayout>\n </NLayout>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from \"vue\";\nimport { NLayout, NLayoutSider, NLayoutContent } from \"naive-ui\";\nimport { useLayoutContext } from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\n\ndefineOptions({ name: \"SideLayout\" });\n\nconst props = withDefaults(\n defineProps<{\n /** 控制侧边栏折叠状态 (支持 v-model:collapsed) */\n collapsed?: boolean;\n }>(),\n { collapsed: false },\n);\n\nconst emit = defineEmits<{\n \"update:collapsed\": [value: boolean];\n}>();\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\n// 内部折叠状态,与 prop 双向同步\nconst _collapsed = ref(props.collapsed);\nwatch(\n () => props.collapsed,\n (v) => {\n _collapsed.value = v;\n },\n);\n\nconst collapsed = computed(() => _collapsed.value);\n\nconst handleCollapsedUpdate = (val: boolean) => {\n _collapsed.value = val;\n emit(\"update:collapsed\", val);\n};\n\n// 折叠切换\nconst toggleCollapse = () => {\n const next = !_collapsed.value;\n _collapsed.value = next;\n emit(\"update:collapsed\", next);\n};\n\ndefineExpose({\n collapsed,\n toggleCollapse,\n});\n</script>\n","<!--\r\n * @robot-admin/layout - ResponsiveMenu\r\n *\r\n * 响应式水平菜单\r\n * 用于 TopLayout / MixTopLayout / ReverseHorizontalMixLayout 的水平菜单\r\n * 自动检测容器宽度,溢出项放入“更多”下拉菜单,使用 NMenu (naive-ui) 渲染\r\n -->\r\n<template>\r\n <div ref=\"containerRef\" class=\"responsive-menu\">\r\n <div ref=\"visibleRef\" 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 { ref, computed, watch, onMounted, onUnmounted, nextTick, 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\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\n// 安全包装\r\nconst menuData = computed(() => props.data || []);\r\nconst activeKey = computed(() => route.path);\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 convertToMenuOption = (item: MenuOptions): MenuOption => ({\r\n key: item.path || \"\",\r\n label: item.meta?.title || item.name || \"\",\r\n icon: item.meta?.icon ? renderIcon(item.meta.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\n\r\nconst containerRef = ref<HTMLElement>();\r\nconst visibleItems = ref<MenuOptions[]>([]);\r\nconst hiddenItems = ref<MenuOptions[]>([]);\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 key: item.path || \"\",\r\n label: item.meta?.title || item.name || \"\",\r\n icon: item.meta?.icon ? renderIcon(item.meta.icon, 16) : undefined,\r\n children: item.children?.length\r\n ? item.children.map((child) => ({\r\n key: child.path || \"\",\r\n label: child.meta?.title || child.name || \"\",\r\n icon: child.meta?.icon ? renderIcon(child.meta.icon, 16) : undefined,\r\n }))\r\n : undefined,\r\n })),\r\n);\r\n\r\nconst handleSelect = (key: string) => {\r\n if (key) router.push(key);\r\n};\r\n\r\n// ============ 宽度估算 ============\r\n\r\nconst estimateItemWidth = (item: MenuOptions): number => {\r\n const ITEM_PADDING = 36; // itemPadding: \"0 18px\" → 18*2\r\n const ITEM_MARGIN = 8;\r\n const ICON_WIDTH = 26;\r\n const EXTRA = 8;\r\n\r\n const title = item.meta?.title || item.name || \"\";\r\n let textWidth = 0;\r\n for (const char of title) {\r\n const code = char.charCodeAt(0);\r\n // CJK / 全角字符约 15px(14px 字号下的实际宽度)\r\n if (\r\n (code >= 0x4e00 && code <= 0x9fff) || // CJK 统一汉字\r\n (code >= 0x3400 && code <= 0x4dbf) || // CJK 扩展 A\r\n (code >= 0x3000 && code <= 0x303f) || // CJK 符号\r\n (code >= 0xff00 && code <= 0xffef) || // 全角字符\r\n (code >= 0x3040 && code <= 0x309f) || // 平假名\r\n (code >= 0x30a0 && code <= 0x30ff) // 片假名\r\n ) {\r\n textWidth += 15;\r\n } else {\r\n textWidth += 8;\r\n }\r\n }\r\n const iconWidth = item.meta?.icon ? ICON_WIDTH : 0;\r\n\r\n return textWidth + iconWidth + ITEM_PADDING + ITEM_MARGIN + EXTRA;\r\n};\r\n\r\nconst calculateVisibleCount = (containerWidth: number): number => {\r\n if (!menuData.value.length) return 0;\r\n\r\n const MORE_BTN_WIDTH = 80;\r\n const SAFETY = 15;\r\n\r\n let totalWidth = 0;\r\n for (const item of menuData.value) {\r\n totalWidth += estimateItemWidth(item);\r\n }\r\n\r\n if (totalWidth + SAFETY <= containerWidth) {\r\n return menuData.value.length;\r\n }\r\n\r\n let usedWidth = 0;\r\n let count = 0;\r\n const available = containerWidth - MORE_BTN_WIDTH - SAFETY;\r\n\r\n for (const item of menuData.value) {\r\n const w = estimateItemWidth(item);\r\n if (usedWidth + w <= available) {\r\n usedWidth += w;\r\n count++;\r\n } else break;\r\n }\r\n\r\n return Math.max(count, 1);\r\n};\r\n\r\nconst calculateVisibleItems = () => {\r\n if (!containerRef.value || !menuData.value.length) return;\r\n\r\n const containerWidth = containerRef.value.offsetWidth;\r\n if (containerWidth === 0) {\r\n nextTick(() => calculateVisibleItems());\r\n return;\r\n }\r\n\r\n const count = calculateVisibleCount(containerWidth);\r\n visibleItems.value = menuData.value.slice(0, count);\r\n hiddenItems.value = menuData.value.slice(count);\r\n};\r\n\r\n// ============ 防抖 & ResizeObserver ============\r\n\r\nlet resizeTimer: number | null = null;\r\nconst debouncedCalculate = () => {\r\n if (resizeTimer) clearTimeout(resizeTimer);\r\n resizeTimer = window.setTimeout(() => calculateVisibleItems(), 100);\r\n};\r\n\r\nlet resizeObserver: ResizeObserver | null = null;\r\n\r\nonMounted(() => {\r\n nextTick(() => calculateVisibleItems());\r\n if (containerRef.value) {\r\n resizeObserver = new ResizeObserver(() => debouncedCalculate());\r\n resizeObserver.observe(containerRef.value);\r\n }\r\n});\r\n\r\nonUnmounted(() => {\r\n resizeObserver?.disconnect();\r\n if (resizeTimer) clearTimeout(resizeTimer);\r\n});\r\n\r\nwatch(\r\n () => props.data,\r\n () => nextTick(() => calculateVisibleItems()),\r\n {\r\n deep: true,\r\n },\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 * 用于 TopLayout / MixTopLayout / ReverseHorizontalMixLayout 的水平菜单\r\n * 自动检测容器宽度,溢出项放入“更多”下拉菜单,使用 NMenu (naive-ui) 渲染\r\n -->\r\n<template>\r\n <div ref=\"containerRef\" class=\"responsive-menu\">\r\n <div ref=\"visibleRef\" 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 { ref, computed, watch, onMounted, onUnmounted, nextTick, 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\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\n// 安全包装\r\nconst menuData = computed(() => props.data || []);\r\nconst activeKey = computed(() => route.path);\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 convertToMenuOption = (item: MenuOptions): MenuOption => ({\r\n key: item.path || \"\",\r\n label: item.meta?.title || item.name || \"\",\r\n icon: item.meta?.icon ? renderIcon(item.meta.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\n\r\nconst containerRef = ref<HTMLElement>();\r\nconst visibleItems = ref<MenuOptions[]>([]);\r\nconst hiddenItems = ref<MenuOptions[]>([]);\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 key: item.path || \"\",\r\n label: item.meta?.title || item.name || \"\",\r\n icon: item.meta?.icon ? renderIcon(item.meta.icon, 16) : undefined,\r\n children: item.children?.length\r\n ? item.children.map((child) => ({\r\n key: child.path || \"\",\r\n label: child.meta?.title || child.name || \"\",\r\n icon: child.meta?.icon ? renderIcon(child.meta.icon, 16) : undefined,\r\n }))\r\n : undefined,\r\n })),\r\n);\r\n\r\nconst handleSelect = (key: string) => {\r\n if (key) router.push(key);\r\n};\r\n\r\n// ============ 宽度估算 ============\r\n\r\nconst estimateItemWidth = (item: MenuOptions): number => {\r\n const ITEM_PADDING = 36; // itemPadding: \"0 18px\" → 18*2\r\n const ITEM_MARGIN = 8;\r\n const ICON_WIDTH = 26;\r\n const EXTRA = 8;\r\n\r\n const title = item.meta?.title || item.name || \"\";\r\n let textWidth = 0;\r\n for (const char of title) {\r\n const code = char.charCodeAt(0);\r\n // CJK / 全角字符约 15px(14px 字号下的实际宽度)\r\n if (\r\n (code >= 0x4e00 && code <= 0x9fff) || // CJK 统一汉字\r\n (code >= 0x3400 && code <= 0x4dbf) || // CJK 扩展 A\r\n (code >= 0x3000 && code <= 0x303f) || // CJK 符号\r\n (code >= 0xff00 && code <= 0xffef) || // 全角字符\r\n (code >= 0x3040 && code <= 0x309f) || // 平假名\r\n (code >= 0x30a0 && code <= 0x30ff) // 片假名\r\n ) {\r\n textWidth += 15;\r\n } else {\r\n textWidth += 8;\r\n }\r\n }\r\n const iconWidth = item.meta?.icon ? ICON_WIDTH : 0;\r\n\r\n return textWidth + iconWidth + ITEM_PADDING + ITEM_MARGIN + EXTRA;\r\n};\r\n\r\nconst calculateVisibleCount = (containerWidth: number): number => {\r\n if (!menuData.value.length) return 0;\r\n\r\n const MORE_BTN_WIDTH = 80;\r\n const SAFETY = 15;\r\n\r\n let totalWidth = 0;\r\n for (const item of menuData.value) {\r\n totalWidth += estimateItemWidth(item);\r\n }\r\n\r\n if (totalWidth + SAFETY <= containerWidth) {\r\n return menuData.value.length;\r\n }\r\n\r\n let usedWidth = 0;\r\n let count = 0;\r\n const available = containerWidth - MORE_BTN_WIDTH - SAFETY;\r\n\r\n for (const item of menuData.value) {\r\n const w = estimateItemWidth(item);\r\n if (usedWidth + w <= available) {\r\n usedWidth += w;\r\n count++;\r\n } else break;\r\n }\r\n\r\n return Math.max(count, 1);\r\n};\r\n\r\nconst calculateVisibleItems = () => {\r\n if (!containerRef.value || !menuData.value.length) return;\r\n\r\n const containerWidth = containerRef.value.offsetWidth;\r\n if (containerWidth === 0) {\r\n nextTick(() => calculateVisibleItems());\r\n return;\r\n }\r\n\r\n const count = calculateVisibleCount(containerWidth);\r\n visibleItems.value = menuData.value.slice(0, count);\r\n hiddenItems.value = menuData.value.slice(count);\r\n};\r\n\r\n// ============ 防抖 & ResizeObserver ============\r\n\r\nlet resizeTimer: number | null = null;\r\nconst debouncedCalculate = () => {\r\n if (resizeTimer) clearTimeout(resizeTimer);\r\n resizeTimer = window.setTimeout(() => calculateVisibleItems(), 100);\r\n};\r\n\r\nlet resizeObserver: ResizeObserver | null = null;\r\n\r\nonMounted(() => {\r\n nextTick(() => calculateVisibleItems());\r\n if (containerRef.value) {\r\n resizeObserver = new ResizeObserver(() => debouncedCalculate());\r\n resizeObserver.observe(containerRef.value);\r\n }\r\n});\r\n\r\nonUnmounted(() => {\r\n resizeObserver?.disconnect();\r\n if (resizeTimer) clearTimeout(resizeTimer);\r\n});\r\n\r\nwatch(\r\n () => props.data,\r\n () => nextTick(() => calculateVisibleItems()),\r\n {\r\n deep: true,\r\n },\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","<!--\n * @robot-admin/layout - TopLayout\n *\n * 顶部导航布局骨架\n * Logo+品牌 | 水平菜单 | 操作区 → 标签页 → 内容区 → 页脚\n *\n * Slots:\n * #logo - Logo/品牌区域\n * #menu - 水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"top-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:Logo 和品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"menu\">\n <ResponsiveMenu :data=\"menus\" />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 主内容区域 -->\n <NLayout>\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <!-- 页脚 -->\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"TopLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n</script>\n","<!--\n * @robot-admin/layout - TopLayout\n *\n * 顶部导航布局骨架\n * Logo+品牌 | 水平菜单 | 操作区 → 标签页 → 内容区 → 页脚\n *\n * Slots:\n * #logo - Logo/品牌区域\n * #menu - 水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"top-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:Logo 和品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"menu\">\n <ResponsiveMenu :data=\"menus\" />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 主内容区域 -->\n <NLayout>\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <!-- 页脚 -->\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"TopLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n</script>\n","/**\r\n * @robot-admin/layout\r\n *\r\n * 菜单拆分 Composable - 通用的一级/二级菜单拆分逻辑\r\n * 用于 Mix / MixTop / ReverseHorizontalMix 等需要菜单拆分的布局\r\n */\r\n\r\nimport {\r\n ref,\r\n computed,\r\n watch,\r\n onMounted,\r\n toValue,\r\n type ComputedRef,\r\n type Ref,\r\n type MaybeRefOrGetter,\r\n} from \"vue\";\r\nimport { useRoute, useRouter } from \"vue-router\";\r\nimport type { MenuOptions } from \"../types/menu\";\r\n\r\nexport interface UseMenuSplitOptions {\r\n /** 菜单数据(响应式) */\r\n menus: ComputedRef<MenuOptions[]>;\r\n /** 是否使用悬浮式二级菜单(MixLayout 的 toggle 模式) */\r\n floatingSecondMenu?: MaybeRefOrGetter<boolean>;\r\n}\r\n\r\nexport interface UseMenuSplitReturn {\r\n /** 当前激活的一级菜单路径 */\r\n activeFirstMenu: Ref<string>;\r\n /** 当前激活的一级菜单完整数据 */\r\n activeFirstMenuItem: ComputedRef<MenuOptions | undefined>;\r\n /** 当前一级菜单对应的二级菜单列表 */\r\n currentSecondMenus: ComputedRef<MenuOptions[]>;\r\n /** 悬浮的菜单项(MixLayout 使用) */\r\n hoveredMenuItem: Ref<MenuOptions | null>;\r\n /** 实际展示的菜单项(hovered 优先,fallback 到 active) */\r\n displayMenuItem: ComputedRef<MenuOptions | null>;\r\n /** 是否显示悬浮二级菜单(MixLayout 使用) */\r\n showSecondMenu: Ref<boolean>;\r\n /** 处理一级菜单点击 */\r\n handleFirstMenuClick: (item: MenuOptions) => void;\r\n /** 处理二级菜单点击 */\r\n handleSecondMenuClick: (item: MenuOptions) => void;\r\n /** 检查路由是否匹配菜单项 */\r\n isMenuItemActive: (menuPath: string | undefined) => boolean;\r\n}\r\n\r\n/**\r\n * 菜单拆分 Composable\r\n *\r\n * 提供一级/二级菜单拆分状态管理、路由自动匹配、菜单点击处理。\r\n * 供 MixLayout / MixTopLayout / ReverseHorizontalMixLayout 共用。\r\n *\r\n * @example\r\n * ```ts\r\n * const menuSplit = useMenuSplit({\r\n * menus: ctx.menus,\r\n * floatingSecondMenu: computed(() => layoutMode.value === 'mix'),\r\n * })\r\n * ```\r\n */\r\nexport function useMenuSplit(options: UseMenuSplitOptions): UseMenuSplitReturn {\r\n const { menus, floatingSecondMenu = false } = options;\r\n const route = useRoute();\r\n const router = useRouter();\r\n\r\n const activeFirstMenu = ref<string>(\"\");\r\n const hoveredMenuItem = ref<MenuOptions | null>(null);\r\n const showSecondMenu = ref(false);\r\n\r\n /** 当前展示在悬浮面板上的菜单项(优先 hovered,fallback 到 active) */\r\n const displayMenuItem = computed<MenuOptions | null>(\r\n () => hoveredMenuItem.value || activeFirstMenuItem.value || null,\r\n );\r\n\r\n // ============ 路径匹配 ============\r\n\r\n const normalizePath = (path: string) =>\r\n path.startsWith(\"/\") ? path : `/${path}`;\r\n\r\n const isMenuItemActive = (menuPath: string | undefined): boolean => {\r\n if (!menuPath) return false;\r\n const currentPath = route.path;\r\n if (currentPath === menuPath) return true;\r\n const normalizedMenuPath = normalizePath(menuPath);\r\n const normalizedCurrentPath = normalizePath(currentPath);\r\n if (normalizedCurrentPath === normalizedMenuPath) return true;\r\n return normalizedCurrentPath.includes(`/${menuPath}`);\r\n };\r\n\r\n // ============ 查找一级菜单 ============\r\n\r\n const findActiveTopMenu = (items: MenuOptions[]): MenuOptions | null => {\r\n for (const item of items) {\r\n if (isMenuItemActive(item.path)) return item;\r\n if (item.children?.length) {\r\n const found = findActiveTopMenu(item.children);\r\n if (found) return item;\r\n }\r\n }\r\n return null;\r\n };\r\n\r\n // ============ 计算属性 ============\r\n\r\n const activeFirstMenuItem = computed(() =>\r\n menus.value.find((item) => item.path === activeFirstMenu.value),\r\n );\r\n\r\n const currentSecondMenus = computed(\r\n () => activeFirstMenuItem.value?.children || [],\r\n );\r\n\r\n // ============ 交互处理 ============\r\n\r\n const handleFirstMenuClick = (item: MenuOptions) => {\r\n if (toValue(floatingSecondMenu)) {\r\n // MixLayout: 纯点击模式\r\n if (item.children && item.children.length > 0) {\r\n if (showSecondMenu.value && activeFirstMenu.value === item.path) {\r\n // 点击同一个已展开的菜单 → 关闭\r\n showSecondMenu.value = false;\r\n } else {\r\n // 点击另一个或未展开 → 展开/切换\r\n activeFirstMenu.value = item.path || \"\";\r\n showSecondMenu.value = true;\r\n }\r\n } else {\r\n // 没有子菜单 → 关闭二级面板并跳转\r\n activeFirstMenu.value = item.path || \"\";\r\n showSecondMenu.value = false;\r\n if (item.path) router.push(item.path);\r\n }\r\n } else {\r\n // MixTop / Reverse: 直接切换\r\n if (!item.children || item.children.length === 0) {\r\n if (item.path) router.push(item.path);\r\n }\r\n }\r\n };\r\n\r\n const handleSecondMenuClick = (item: MenuOptions) => {\r\n if (item.path) router.push(item.path);\r\n };\r\n\r\n // ============ 路由自动匹配 ============\r\n\r\n const updateActiveMenuByRoute = () => {\r\n const menuList = menus.value;\r\n const matchedFirstMenu = findActiveTopMenu(menuList);\r\n if (matchedFirstMenu) {\r\n activeFirstMenu.value = matchedFirstMenu.path || \"\";\r\n // 悬浮模式下,如果当前一级菜单有子菜单,自动展示二级菜单\r\n if (toValue(floatingSecondMenu) && matchedFirstMenu.children?.length) {\r\n showSecondMenu.value = true;\r\n }\r\n } else if (menuList.length > 0 && !activeFirstMenu.value) {\r\n activeFirstMenu.value = menuList[0].path || \"\";\r\n }\r\n };\r\n\r\n watch(\r\n () => route.path,\r\n () => updateActiveMenuByRoute(),\r\n { immediate: true },\r\n );\r\n\r\n watch(\r\n menus,\r\n (newMenus) => {\r\n if (newMenus && newMenus.length > 0) updateActiveMenuByRoute();\r\n },\r\n { immediate: true },\r\n );\r\n\r\n onMounted(() => updateActiveMenuByRoute());\r\n\r\n return {\r\n activeFirstMenu,\r\n activeFirstMenuItem,\r\n currentSecondMenus,\r\n hoveredMenuItem,\r\n /** 实际在悬浮面板上展示的菜单项(hovered 优先,fallback 到 active) */\r\n displayMenuItem,\r\n showSecondMenu,\r\n handleFirstMenuClick,\r\n handleSecondMenuClick,\r\n isMenuItemActive,\r\n };\r\n}\r\n","<!--\n * @robot-admin/layout - MixLayout\n *\n * 混合布局骨架\n * 左侧一级菜单图标栏 + 悬浮二级菜单弹出面板 + 右侧内容区\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #header - 头部区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"mix-layout-container\">\n <!-- 左侧一级菜单 -->\n <div\n class=\"first-level-menu\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- Logo -->\n <div class=\"logo-container\">\n <slot name=\"logo\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n class=\"logo-video\"\n />\n </slot>\n </div>\n\n <!-- 一级菜单列表 -->\n <div class=\"first-menu-list\">\n <div\n v-for=\"item in menus\"\n :key=\"item.path\"\n class=\"first-menu-item\"\n :class=\"{\n active: menuSplit.activeFirstMenu.value === item.path,\n }\"\n @click=\"menuSplit.handleFirstMenuClick(item)\"\n >\n <div class=\"menu-item-content\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"22\"\n />\n <span v-else class=\"menu-item-text\">{{\n (item.meta?.title || \"\")[0]\n }}</span>\n </div>\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\n </div>\n </div>\n </div>\n\n <!-- 悬浮二级菜单 -->\n <Transition name=\"slide-fade\">\n <div\n v-if=\"menuSplit.showSecondMenu.value\"\n class=\"second-level-menu-popup\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"second-menu-header\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"menuSplit.displayMenuItem.value?.meta?.icon\"\n :name=\"menuSplit.displayMenuItem.value.meta.icon\"\n :size=\"20\"\n />\n <span class=\"menu-title\">{{\n menuSplit.displayMenuItem.value?.meta?.title\n }}</span>\n </div>\n <div class=\"second-menu-list\">\n <div\n v-for=\"child in menuSplit.displayMenuItem.value?.children || []\"\n :key=\"child.path\"\n class=\"second-menu-item\"\n :class=\"{ active: menuSplit.isMenuItemActive(child.path) }\"\n @click=\"menuSplit.handleSecondMenuClick(child)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"16\"\n />\n <span class=\"menu-item-label\">{{ child.meta?.title }}</span>\n </div>\n </div>\n </div>\n </Transition>\n\n <!-- 右侧主内容区 -->\n <div class=\"main-layout\">\n <slot name=\"header\" />\n\n <NLayout>\n <NLayoutContent class=\"content-with-header p16px app-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\n\ndefineOptions({ name: \"MixLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => true),\n});\n\n// 图标组件:优先使用消费方提供的,否则用 CSS class 渲染\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - MixLayout\n *\n * 混合布局骨架\n * 左侧一级菜单图标栏 + 悬浮二级菜单弹出面板 + 右侧内容区\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #header - 头部区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"mix-layout-container\">\n <!-- 左侧一级菜单 -->\n <div\n class=\"first-level-menu\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- Logo -->\n <div class=\"logo-container\">\n <slot name=\"logo\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n class=\"logo-video\"\n />\n </slot>\n </div>\n\n <!-- 一级菜单列表 -->\n <div class=\"first-menu-list\">\n <div\n v-for=\"item in menus\"\n :key=\"item.path\"\n class=\"first-menu-item\"\n :class=\"{\n active: menuSplit.activeFirstMenu.value === item.path,\n }\"\n @click=\"menuSplit.handleFirstMenuClick(item)\"\n >\n <div class=\"menu-item-content\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"22\"\n />\n <span v-else class=\"menu-item-text\">{{\n (item.meta?.title || \"\")[0]\n }}</span>\n </div>\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\n </div>\n </div>\n </div>\n\n <!-- 悬浮二级菜单 -->\n <Transition name=\"slide-fade\">\n <div\n v-if=\"menuSplit.showSecondMenu.value\"\n class=\"second-level-menu-popup\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"second-menu-header\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"menuSplit.displayMenuItem.value?.meta?.icon\"\n :name=\"menuSplit.displayMenuItem.value.meta.icon\"\n :size=\"20\"\n />\n <span class=\"menu-title\">{{\n menuSplit.displayMenuItem.value?.meta?.title\n }}</span>\n </div>\n <div class=\"second-menu-list\">\n <div\n v-for=\"child in menuSplit.displayMenuItem.value?.children || []\"\n :key=\"child.path\"\n class=\"second-menu-item\"\n :class=\"{ active: menuSplit.isMenuItemActive(child.path) }\"\n @click=\"menuSplit.handleSecondMenuClick(child)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"16\"\n />\n <span class=\"menu-item-label\">{{ child.meta?.title }}</span>\n </div>\n </div>\n </div>\n </Transition>\n\n <!-- 右侧主内容区 -->\n <div class=\"main-layout\">\n <slot name=\"header\" />\n\n <NLayout>\n <NLayoutContent class=\"content-with-header p16px app-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\n\ndefineOptions({ name: \"MixLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => true),\n});\n\n// 图标组件:优先使用消费方提供的,否则用 CSS class 渲染\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - MixTopLayout\n *\n * 顶部混合布局骨架\n * 左侧一级菜单 + 顶部二级水平菜单 + 内容区\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #brand - 顶部导航品牌区\n * #top-menu - 二级水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"mix-top-layout-container\">\n <!-- 左侧一级菜单 -->\n <div\n class=\"first-level-sidebar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- Logo -->\n <div class=\"logo-container\">\n <slot name=\"logo\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n class=\"logo-video\"\n />\n </slot>\n </div>\n\n <!-- 一级菜单列表 -->\n <div class=\"first-menu-list\">\n <div\n v-for=\"item in menus\"\n :key=\"item.path\"\n class=\"first-menu-item\"\n :class=\"{\n active: menuSplit.activeFirstMenu.value === item.path,\n }\"\n @click=\"menuSplit.handleFirstMenuClick(item)\"\n >\n <div class=\"menu-item-content\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"22\"\n />\n <span v-else class=\"menu-item-text\">{{\n (item.meta?.title || \"\")[0]\n }}</span>\n </div>\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\n </div>\n </div>\n </div>\n\n <!-- 右侧主区域 -->\n <div class=\"main-area\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"brand\">\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:二级水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"top-menu\">\n <ResponsiveMenu\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n :data=\"menuSplit.currentSecondMenus.value\"\n />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 内容区 -->\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"MixTopLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => false),\n});\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - MixTopLayout\n *\n * 顶部混合布局骨架\n * 左侧一级菜单 + 顶部二级水平菜单 + 内容区\n *\n * Slots:\n * #logo - Logo/品牌区域(侧边栏顶部)\n * #brand - 顶部导航品牌区\n * #top-menu - 二级水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"mix-top-layout-container\">\n <!-- 左侧一级菜单 -->\n <div\n class=\"first-level-sidebar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- Logo -->\n <div class=\"logo-container\">\n <slot name=\"logo\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 40\"\n :height=\"brand.logoSize || 40\"\n class=\"logo-video\"\n />\n </slot>\n </div>\n\n <!-- 一级菜单列表 -->\n <div class=\"first-menu-list\">\n <div\n v-for=\"item in menus\"\n :key=\"item.path\"\n class=\"first-menu-item\"\n :class=\"{\n active: menuSplit.activeFirstMenu.value === item.path,\n }\"\n @click=\"menuSplit.handleFirstMenuClick(item)\"\n >\n <div class=\"menu-item-content\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"22\"\n />\n <span v-else class=\"menu-item-text\">{{\n (item.meta?.title || \"\")[0]\n }}</span>\n </div>\n <span class=\"menu-item-label\">{{ item.meta?.title }}</span>\n </div>\n </div>\n </div>\n\n <!-- 右侧主区域 -->\n <div class=\"main-area\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"brand\">\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:二级水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"top-menu\">\n <ResponsiveMenu\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n :data=\"menuSplit.currentSecondMenus.value\"\n />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 内容区 -->\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"MixTopLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => false),\n});\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - ReverseHorizontalMixLayout\n *\n * 反转混合布局骨架\n * 顶部一级菜单 + 右侧二级菜单侧边栏 + 左侧内容区\n *\n * Slots:\n * #logo - Logo/品牌区域\n * #top-menu - 一级水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"reverse-horizontal-mix-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:Logo 和品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:一级水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"top-menu\">\n <ResponsiveMenu :data=\"menus\" />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 主区域:左侧内容 + 右侧菜单 -->\n <div class=\"main-area\">\n <!-- 左侧:主内容区 -->\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n\n <!-- 右侧折叠/展开按钮 - 始终可见 -->\n <div\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n class=\"sidebar-toggle\"\n :class=\"[\n isDarkMode ? 'dark-theme' : 'light-theme',\n { active: !isCollapsed },\n ]\"\n @click=\"toggleCollapse\"\n >\n <span\n :class=\"[\n 'collapse-arrow',\n 'toggle-icon',\n isCollapsed ? 'collapsed' : 'expanded',\n ]\"\n >\n <span class=\"arrow-line\"></span>\n <span class=\"arrow-line\"></span>\n </span>\n </div>\n\n <!-- 右侧:二级菜单侧边栏 -->\n <div\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n class=\"right-sidebar-wrapper\"\n :class=\"{ collapsed: isCollapsed }\"\n >\n <div\n class=\"right-sidebar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 侧边栏标题 -->\n <div\n class=\"sidebar-header\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"header-icon\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"menuSplit.activeFirstMenuItem.value?.meta?.icon\"\n :name=\"menuSplit.activeFirstMenuItem.value.meta.icon\"\n :size=\"20\"\n />\n </div>\n <span class=\"header-title\">{{\n menuSplit.activeFirstMenuItem.value?.meta?.title || \"菜单\"\n }}</span>\n </div>\n\n <!-- 二级菜单列表 -->\n <div class=\"sidebar-menu-list\">\n <template\n v-for=\"child in menuSplit.currentSecondMenus.value\"\n :key=\"child.path\"\n >\n <!-- 有子菜单的项 -->\n <div\n v-if=\"child.children && child.children.length > 0\"\n class=\"menu-group\"\n >\n <div class=\"group-title\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"16\"\n />\n <span>{{ child.meta?.title }}</span>\n </div>\n <div\n v-for=\"subChild in child.children\"\n :key=\"subChild.path\"\n class=\"menu-item sub-item\"\n :class=\"{\n active: menuSplit.isMenuItemActive(subChild.path),\n }\"\n @click=\"menuSplit.handleSecondMenuClick(subChild)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"subChild.meta?.icon\"\n :name=\"subChild.meta.icon\"\n :size=\"16\"\n />\n <span class=\"item-title\">{{ subChild.meta?.title }}</span>\n </div>\n </div>\n <!-- 没有子菜单的项 -->\n <div\n v-else\n class=\"menu-item\"\n :class=\"{\n active: menuSplit.isMenuItemActive(child.path),\n }\"\n @click=\"menuSplit.handleSecondMenuClick(child)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"18\"\n />\n <span class=\"item-title\">{{ child.meta?.title }}</span>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { ref, computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"ReverseHorizontalMixLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => false),\n});\n\n// 右侧边栏折叠状态\nconst isCollapsed = ref(false);\nconst toggleCollapse = () => {\n isCollapsed.value = !isCollapsed.value;\n};\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - ReverseHorizontalMixLayout\n *\n * 反转混合布局骨架\n * 顶部一级菜单 + 右侧二级菜单侧边栏 + 左侧内容区\n *\n * Slots:\n * #logo - Logo/品牌区域\n * #top-menu - 一级水平菜单(默认使用 ResponsiveMenu)\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"reverse-horizontal-mix-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 左侧:Logo 和品牌 -->\n <div class=\"navbar-left\">\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 中间:一级水平菜单 -->\n <div class=\"navbar-center\">\n <slot name=\"top-menu\">\n <ResponsiveMenu :data=\"menus\" />\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 主区域:左侧内容 + 右侧菜单 -->\n <div class=\"main-area\">\n <!-- 左侧:主内容区 -->\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n\n <!-- 右侧折叠/展开按钮 - 始终可见 -->\n <div\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n class=\"sidebar-toggle\"\n :class=\"[\n isDarkMode ? 'dark-theme' : 'light-theme',\n { active: !isCollapsed },\n ]\"\n @click=\"toggleCollapse\"\n >\n <span\n :class=\"[\n 'collapse-arrow',\n 'toggle-icon',\n isCollapsed ? 'collapsed' : 'expanded',\n ]\"\n >\n <span class=\"arrow-line\"></span>\n <span class=\"arrow-line\"></span>\n </span>\n </div>\n\n <!-- 右侧:二级菜单侧边栏 -->\n <div\n v-if=\"menuSplit.currentSecondMenus.value.length > 0\"\n class=\"right-sidebar-wrapper\"\n :class=\"{ collapsed: isCollapsed }\"\n >\n <div\n class=\"right-sidebar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <!-- 侧边栏标题 -->\n <div\n class=\"sidebar-header\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"header-icon\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"menuSplit.activeFirstMenuItem.value?.meta?.icon\"\n :name=\"menuSplit.activeFirstMenuItem.value.meta.icon\"\n :size=\"20\"\n />\n </div>\n <span class=\"header-title\">{{\n menuSplit.activeFirstMenuItem.value?.meta?.title || \"菜单\"\n }}</span>\n </div>\n\n <!-- 二级菜单列表 -->\n <div class=\"sidebar-menu-list\">\n <template\n v-for=\"child in menuSplit.currentSecondMenus.value\"\n :key=\"child.path\"\n >\n <!-- 有子菜单的项 -->\n <div\n v-if=\"child.children && child.children.length > 0\"\n class=\"menu-group\"\n >\n <div class=\"group-title\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"16\"\n />\n <span>{{ child.meta?.title }}</span>\n </div>\n <div\n v-for=\"subChild in child.children\"\n :key=\"subChild.path\"\n class=\"menu-item sub-item\"\n :class=\"{\n active: menuSplit.isMenuItemActive(subChild.path),\n }\"\n @click=\"menuSplit.handleSecondMenuClick(subChild)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"subChild.meta?.icon\"\n :name=\"subChild.meta.icon\"\n :size=\"16\"\n />\n <span class=\"item-title\">{{ subChild.meta?.title }}</span>\n </div>\n </div>\n <!-- 没有子菜单的项 -->\n <div\n v-else\n class=\"menu-item\"\n :class=\"{\n active: menuSplit.isMenuItemActive(child.path),\n }\"\n @click=\"menuSplit.handleSecondMenuClick(child)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"child.meta?.icon\"\n :name=\"child.meta.icon\"\n :size=\"18\"\n />\n <span class=\"item-title\">{{ child.meta?.title }}</span>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { ref, computed, h, defineComponent } from \"vue\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport { useMenuSplit } from \"../../../composables/useMenuSplit\";\nimport ResponsiveMenu from \"../../ResponsiveMenu/index.vue\";\n\ndefineOptions({ name: \"ReverseHorizontalMixLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\nconst menuSplit = useMenuSplit({\n menus: ctx.menus,\n floatingSecondMenu: computed(() => false),\n});\n\n// 右侧边栏折叠状态\nconst isCollapsed = ref(false);\nconst toggleCollapse = () => {\n isCollapsed.value = !isCollapsed.value;\n};\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n</script>\n","<!--\n * @robot-admin/layout - CardLayout\n *\n * 卡片式布局骨架\n * 顶部导航 + hover 触发抽屉式菜单 + 内容区\n *\n * Slots:\n * #menu-trigger - 菜单触发区域(hover 弹出抽屉)\n * #logo - Logo/品牌区域\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #drawer-menu - 抽屉式菜单内容\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"card-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"navbar-left\">\n <!-- 悬停触发区域 -->\n <div\n class=\"hover-trigger-area\"\n @mouseenter=\"cancelHideTimer\"\n @mouseleave=\"hideDrawerMenu\"\n >\n <slot name=\"menu-trigger\">\n <div class=\"menu-indicator\">\n <!-- 纯 CSS 三横线菜单图标 -->\n <span class=\"hamburger-icon\">\n <span></span>\n <span></span>\n <span></span>\n </span>\n </div>\n </slot>\n </div>\n\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 抽屉式菜单 -->\n <div\n class=\"drawer-menu\"\n :class=\"[\n isDarkMode ? 'dark-theme' : 'light-theme',\n { visible: showDrawerMenu },\n ]\"\n @mouseenter=\"cancelHideTimer\"\n @mouseleave=\"hideDrawerMenu\"\n >\n <slot name=\"drawer-menu\">\n <!-- 菜单头部 -->\n <div class=\"drawer-header\">\n <div class=\"drawer-title\">\n <!-- 纯 CSS 网格图标 -->\n <span class=\"grid-icon\">\n <span></span>\n <span></span>\n <span></span>\n <span></span>\n </span>\n <span>功能导航</span>\n </div>\n </div>\n\n <!-- 菜单内容 - 阿里云风格铺开布局 -->\n <div class=\"drawer-content\">\n <div class=\"menu-grid\">\n <div\n v-for=\"category in menus\"\n :key=\"category.path\"\n class=\"menu-category\"\n >\n <!-- 分类标题 -->\n <div class=\"category-header\" @click=\"navigateToPage(category)\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"category.meta?.icon\"\n :name=\"category.meta.icon\"\n :size=\"18\"\n />\n <span>{{ category.meta?.title }}</span>\n </div>\n\n <!-- 分类下的菜单项 -->\n <div\n class=\"category-items\"\n v-if=\"category.children && category.children.length > 0\"\n >\n <div\n v-for=\"item in category.children\"\n :key=\"item.path\"\n class=\"menu-item\"\n @click=\"navigateToPage(item)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"16\"\n />\n <span class=\"item-title\">{{ item.meta?.title }}</span>\n </div>\n\n <!-- 3级菜单项 -->\n <template\n v-for=\"item in category.children\"\n :key=\"'sub-' + item.path\"\n >\n <div\n v-if=\"item.children && item.children.length > 0\"\n class=\"submenu-items\"\n >\n <div class=\"submenu-title\">{{ item.meta?.title }}</div>\n <div\n v-for=\"subItem in item.children\"\n :key=\"subItem.path\"\n class=\"menu-item submenu-item\"\n @click=\"navigateToPage(subItem)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"subItem.meta?.icon\"\n :name=\"subItem.meta.icon\"\n :size=\"14\"\n />\n <span class=\"item-title\">{{ subItem.meta?.title }}</span>\n </div>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- 主内容区域 -->\n <div class=\"main-content-area\">\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content p16px app-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { ref, computed, onUnmounted, h, defineComponent } from \"vue\";\nimport { useRouter } from \"vue-router\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport type { MenuOptions } from \"../../../types/menu\";\n\ndefineOptions({ name: \"CardLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\nconst router = useRouter();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\n// 抽屉菜单状态\nconst showDrawerMenu = ref(false);\nconst hideTimer = ref<ReturnType<typeof setTimeout> | null>(null);\n\nconst hideDrawerMenu = () => {\n if (hideTimer.value) clearTimeout(hideTimer.value);\n hideTimer.value = setTimeout(() => {\n showDrawerMenu.value = false;\n }, 300);\n};\n\nconst cancelHideTimer = () => {\n if (hideTimer.value) {\n clearTimeout(hideTimer.value);\n hideTimer.value = null;\n }\n showDrawerMenu.value = true;\n};\n\nconst navigateToPage = (item: MenuOptions) => {\n if (item.path) {\n router.push(item.path);\n showDrawerMenu.value = false;\n }\n};\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n\nonUnmounted(() => {\n if (hideTimer.value) clearTimeout(hideTimer.value);\n});\n</script>\n\n<style scoped lang=\"scss\">\n/* 纯 CSS 网格图标 (2x2) */\n.grid-icon {\n display: inline-grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 2.5px;\n width: 18px;\n height: 18px;\n padding: 1px;\n}\n\n.grid-icon span {\n background-color: currentColor;\n border-radius: 2px;\n opacity: 0.85;\n}\n\n/* 纯 CSS 三横线汉堡菜单图标 */\n.hamburger-icon {\n display: inline-flex;\n flex-direction: column;\n justify-content: space-between;\n width: 20px;\n height: 16px;\n padding: 2px 0;\n}\n\n.hamburger-icon span {\n display: block;\n width: 100%;\n height: 2px;\n background-color: currentColor;\n border-radius: 1px;\n opacity: 0.85;\n transition: all 0.2s ease;\n}\n</style>\n","<!--\n * @robot-admin/layout - CardLayout\n *\n * 卡片式布局骨架\n * 顶部导航 + hover 触发抽屉式菜单 + 内容区\n *\n * Slots:\n * #menu-trigger - 菜单触发区域(hover 弹出抽屉)\n * #logo - Logo/品牌区域\n * #header-extra - 头部右侧操作区\n * #tags-view - 标签页区域\n * #drawer-menu - 抽屉式菜单内容\n * #footer - 页脚区域\n -->\n<template>\n <div class=\"card-layout-container\">\n <!-- 顶部导航栏 -->\n <div\n class=\"top-navbar\"\n :class=\"[isDarkMode ? 'dark-theme' : 'light-theme']\"\n >\n <div class=\"navbar-left\">\n <!-- 悬停触发区域 -->\n <div\n class=\"hover-trigger-area\"\n @mouseenter=\"cancelHideTimer\"\n @mouseleave=\"hideDrawerMenu\"\n >\n <slot name=\"menu-trigger\">\n <div class=\"menu-indicator\">\n <!-- 纯 CSS 三横线菜单图标 -->\n <span class=\"hamburger-icon\">\n <span></span>\n <span></span>\n <span></span>\n </span>\n </div>\n </slot>\n </div>\n\n <slot name=\"logo\">\n <div class=\"logo-container\">\n <div class=\"logo-glow\"></div>\n <video\n v-if=\"brand.logoType === 'video'\"\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n autoplay\n loop\n muted\n playsinline\n class=\"logo-video\"\n >\n 您的浏览器不支持 video 标签。\n </video>\n <img\n v-else\n :src=\"brand.logoSrc\"\n :width=\"brand.logoSize || 36\"\n :height=\"brand.logoSize || 36\"\n class=\"logo-video\"\n />\n </div>\n <div class=\"brand-name\">\n <span class=\"brand-title\">{{ brand.name }}</span>\n <span class=\"brand-subtitle\">{{ brand.subtitle }}</span>\n </div>\n <div class=\"navbar-divider\"></div>\n </slot>\n </div>\n\n <!-- 右侧:操作区 -->\n <slot name=\"header-extra\" />\n </div>\n\n <!-- 标签页区域 -->\n <div\n v-if=\"showTagsView\"\n class=\"tags-view-container\"\n :style=\"{ height: `${tagsViewHeight}px` }\"\n >\n <slot name=\"tags-view\" />\n </div>\n\n <!-- 抽屉式菜单 -->\n <div\n class=\"drawer-menu\"\n :class=\"[\n isDarkMode ? 'dark-theme' : 'light-theme',\n { visible: showDrawerMenu },\n ]\"\n @mouseenter=\"cancelHideTimer\"\n @mouseleave=\"hideDrawerMenu\"\n >\n <slot name=\"drawer-menu\">\n <!-- 菜单头部 -->\n <div class=\"drawer-header\">\n <div class=\"drawer-title\">\n <!-- 纯 CSS 网格图标 -->\n <span class=\"grid-icon\">\n <span></span>\n <span></span>\n <span></span>\n <span></span>\n </span>\n <span>功能导航</span>\n </div>\n </div>\n\n <!-- 菜单内容 - 阿里云风格铺开布局 -->\n <div class=\"drawer-content\">\n <div class=\"menu-grid\">\n <div\n v-for=\"category in menus\"\n :key=\"category.path\"\n class=\"menu-category\"\n >\n <!-- 分类标题 -->\n <div class=\"category-header\" @click=\"navigateToPage(category)\">\n <component\n :is=\"LayoutIcon\"\n v-if=\"category.meta?.icon\"\n :name=\"category.meta.icon\"\n :size=\"18\"\n />\n <span>{{ category.meta?.title }}</span>\n </div>\n\n <!-- 分类下的菜单项 -->\n <div\n class=\"category-items\"\n v-if=\"category.children && category.children.length > 0\"\n >\n <div\n v-for=\"item in category.children\"\n :key=\"item.path\"\n class=\"menu-item\"\n @click=\"navigateToPage(item)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"item.meta?.icon\"\n :name=\"item.meta.icon\"\n :size=\"16\"\n />\n <span class=\"item-title\">{{ item.meta?.title }}</span>\n </div>\n\n <!-- 3级菜单项 -->\n <template\n v-for=\"item in category.children\"\n :key=\"'sub-' + item.path\"\n >\n <div\n v-if=\"item.children && item.children.length > 0\"\n class=\"submenu-items\"\n >\n <div class=\"submenu-title\">{{ item.meta?.title }}</div>\n <div\n v-for=\"subItem in item.children\"\n :key=\"subItem.path\"\n class=\"menu-item submenu-item\"\n @click=\"navigateToPage(subItem)\"\n >\n <component\n :is=\"LayoutIcon\"\n v-if=\"subItem.meta?.icon\"\n :name=\"subItem.meta.icon\"\n :size=\"14\"\n />\n <span class=\"item-title\">{{ subItem.meta?.title }}</span>\n </div>\n </div>\n </template>\n </div>\n </div>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- 主内容区域 -->\n <div class=\"main-content-area\">\n <NLayout class=\"content-layout\">\n <NLayoutContent class=\"main-content p16px app-content\">\n <div class=\"page-content\">\n <RouterView v-slot=\"{ Component, route }\">\n <Transition :name=\"transitionName\" mode=\"out-in\">\n <KeepAlive :include=\"cachedViews\" :max=\"maxCacheCount\">\n <component :is=\"Component\" :key=\"route.path\" />\n </KeepAlive>\n </Transition>\n </RouterView>\n </div>\n </NLayoutContent>\n\n <template v-if=\"showFooter\">\n <slot name=\"footer\" />\n </template>\n </NLayout>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport \"./index.scss\";\nimport { ref, computed, onUnmounted, h, defineComponent } from \"vue\";\nimport { useRouter } from \"vue-router\";\nimport { NLayout, NLayoutContent } from \"naive-ui\";\nimport {\n useLayoutContext,\n DEFAULT_BRAND_CONFIG,\n} from \"../../../composables/useLayoutContext\";\nimport { useLayoutCache } from \"../../../composables/useLayoutCache\";\nimport type { MenuOptions } from \"../../../types/menu\";\n\ndefineOptions({ name: \"CardLayout\" });\n\nconst ctx = useLayoutContext();\nconst { cachedViews, maxCacheCount } = useLayoutCache();\nconst router = useRouter();\n\nconst isDarkMode = ctx.isDark;\nconst menus = computed(() => ctx.menus.value);\nconst showTagsView = computed(() => ctx.showTagsView.value);\nconst tagsViewHeight = computed(() => ctx.tagsViewHeight.value);\nconst showFooter = computed(() => ctx.showFooter.value);\nconst transitionName = computed(() => ctx.transitionName.value);\nconst brand = { ...DEFAULT_BRAND_CONFIG, ...ctx.brand };\n\n// 抽屉菜单状态\nconst showDrawerMenu = ref(false);\nconst hideTimer = ref<ReturnType<typeof setTimeout> | null>(null);\n\nconst hideDrawerMenu = () => {\n if (hideTimer.value) clearTimeout(hideTimer.value);\n hideTimer.value = setTimeout(() => {\n showDrawerMenu.value = false;\n }, 300);\n};\n\nconst cancelHideTimer = () => {\n if (hideTimer.value) {\n clearTimeout(hideTimer.value);\n hideTimer.value = null;\n }\n showDrawerMenu.value = true;\n};\n\nconst navigateToPage = (item: MenuOptions) => {\n if (item.path) {\n router.push(item.path);\n showDrawerMenu.value = false;\n }\n};\n\nconst LayoutIcon =\n ctx.iconComponent ??\n defineComponent({\n name: \"LayoutIcon\",\n props: { name: String, size: { type: Number, default: 18 } },\n setup(props) {\n return () =>\n h(\"i\", {\n class: props.name,\n style: {\n fontSize: `${props.size}px`,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n });\n },\n });\n\nonUnmounted(() => {\n if (hideTimer.value) clearTimeout(hideTimer.value);\n});\n</script>\n\n<style scoped lang=\"scss\">\n/* 纯 CSS 网格图标 (2x2) */\n.grid-icon {\n display: inline-grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 2.5px;\n width: 18px;\n height: 18px;\n padding: 1px;\n}\n\n.grid-icon span {\n background-color: currentColor;\n border-radius: 2px;\n opacity: 0.85;\n}\n\n/* 纯 CSS 三横线汉堡菜单图标 */\n.hamburger-icon {\n display: inline-flex;\n flex-direction: column;\n justify-content: space-between;\n width: 20px;\n height: 16px;\n padding: 2px 0;\n}\n\n.hamburger-icon span {\n display: block;\n width: 100%;\n height: 2px;\n background-color: currentColor;\n border-radius: 1px;\n opacity: 0.85;\n transition: all 0.2s ease;\n}\n</style>\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 :class=\"['layout-container', isDark ? 'dark-mode' : 'light-mode']\">\r\n <!-- ═══════ Side 左侧菜单布局 ═══════ -->\r\n <SideLayout v-if=\"layoutMode === 'side'\" v-model:collapsed=\"sideCollapsed\">\r\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 </SideLayout>\r\n\r\n <!-- ═══════ Top 顶部菜单布局 ═══════ -->\r\n <TopLayout v-else-if=\"layoutMode === 'top'\">\r\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 </TopLayout>\r\n\r\n <!-- ═══════ Mix 混合布局 ═══════ -->\r\n <MixLayout v-else-if=\"layoutMode === 'mix'\">\r\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 </MixLayout>\r\n\r\n <!-- ═══════ MixTop 顶部混合布局 ═══════ -->\r\n <MixTopLayout v-else-if=\"layoutMode === 'mix-top'\">\r\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 </MixTopLayout>\r\n\r\n <!-- ═══════ ReverseHorizontalMix 反转混合布局 ═══════ -->\r\n <ReverseHorizontalMixLayout\r\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 </ReverseHorizontalMixLayout>\r\n\r\n <!-- ═══════ CardLayout 卡片布局 ═══════ -->\r\n <CardLayout v-else-if=\"layoutMode === 'card-layout'\">\r\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 </CardLayout>\r\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 { useSettingsStore } from \"../../stores/settings\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\n// 骨架布局全局样式\r\nimport \"../../styles/layouts.scss\";\r\n\r\n// 骨架组件\r\nimport SideLayout from \"../layouts/SideLayout/index.vue\";\r\nimport TopLayout from \"../layouts/TopLayout/index.vue\";\r\nimport MixLayout from \"../layouts/MixLayout/index.vue\";\r\nimport MixTopLayout from \"../layouts/MixTopLayout/index.vue\";\r\nimport ReverseHorizontalMixLayout from \"../layouts/ReverseHorizontalMixLayout/index.vue\";\r\nimport CardLayout from \"../layouts/CardLayout/index.vue\";\r\n\r\ndefineOptions({ name: \"C_LayoutContainer\" });\r\n\r\nconst settingsStore = useSettingsStore();\r\nconst ctx = useLayoutContext();\r\n\r\nconst layoutMode = computed(() => settingsStore.layoutMode);\r\nconst isDark = ctx.isDark;\r\n\r\n// Side 布局折叠状态\r\nconst sideCollapsed = ref(false);\r\n\r\n// 提供折叠状态给子组件(如 C_Header 的汉堡按钮)\r\nprovide(\"menuCollapse\", {\r\n isCollapsed: sideCollapsed,\r\n handleCollapsedChange: (collapsed: boolean) => {\r\n sideCollapsed.value = collapsed;\r\n },\r\n});\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 :class=\"['layout-container', isDark ? 'dark-mode' : 'light-mode']\">\r\n <!-- ═══════ Side 左侧菜单布局 ═══════ -->\r\n <SideLayout v-if=\"layoutMode === 'side'\" v-model:collapsed=\"sideCollapsed\">\r\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 </SideLayout>\r\n\r\n <!-- ═══════ Top 顶部菜单布局 ═══════ -->\r\n <TopLayout v-else-if=\"layoutMode === 'top'\">\r\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 </TopLayout>\r\n\r\n <!-- ═══════ Mix 混合布局 ═══════ -->\r\n <MixLayout v-else-if=\"layoutMode === 'mix'\">\r\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 </MixLayout>\r\n\r\n <!-- ═══════ MixTop 顶部混合布局 ═══════ -->\r\n <MixTopLayout v-else-if=\"layoutMode === 'mix-top'\">\r\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 </MixTopLayout>\r\n\r\n <!-- ═══════ ReverseHorizontalMix 反转混合布局 ═══════ -->\r\n <ReverseHorizontalMixLayout\r\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 </ReverseHorizontalMixLayout>\r\n\r\n <!-- ═══════ CardLayout 卡片布局 ═══════ -->\r\n <CardLayout v-else-if=\"layoutMode === 'card-layout'\">\r\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 </CardLayout>\r\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 { useSettingsStore } from \"../../stores/settings\";\r\nimport { useLayoutContext } from \"../../composables/useLayoutContext\";\r\n\r\n// 骨架布局全局样式\r\nimport \"../../styles/layouts.scss\";\r\n\r\n// 骨架组件\r\nimport SideLayout from \"../layouts/SideLayout/index.vue\";\r\nimport TopLayout from \"../layouts/TopLayout/index.vue\";\r\nimport MixLayout from \"../layouts/MixLayout/index.vue\";\r\nimport MixTopLayout from \"../layouts/MixTopLayout/index.vue\";\r\nimport ReverseHorizontalMixLayout from \"../layouts/ReverseHorizontalMixLayout/index.vue\";\r\nimport CardLayout from \"../layouts/CardLayout/index.vue\";\r\n\r\ndefineOptions({ name: \"C_LayoutContainer\" });\r\n\r\nconst settingsStore = useSettingsStore();\r\nconst ctx = useLayoutContext();\r\n\r\nconst layoutMode = computed(() => settingsStore.layoutMode);\r\nconst isDark = ctx.isDark;\r\n\r\n// Side 布局折叠状态\r\nconst sideCollapsed = ref(false);\r\n\r\n// 提供折叠状态给子组件(如 C_Header 的汉堡按钮)\r\nprovide(\"menuCollapse\", {\r\n isCollapsed: sideCollapsed,\r\n handleCollapsedChange: (collapsed: boolean) => {\r\n sideCollapsed.value = collapsed;\r\n },\r\n});\r\n</script>\r\n","/**\n * SettingsDrawer - 常量数据\n */\n\nimport { THEME_PRESETS, COLOR_SWATCHES } from \"../../constants\";\nimport type { 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];\n","<!--\n * @robot-admin/layout - SettingsDrawer\n *\n * 布局配置抽屉\n * 包含外观/布局/功能三大模块,支持主题预设、布局切换、配置导入导出等\n -->\n<template>\n <NDrawer\n v-model:show=\"visible\"\n :width=\"380\"\n placement=\"right\"\n :trap-focus=\"false\"\n :block-scroll=\"false\"\n >\n <NDrawerContent title=\"⚙️ 布局配置\" closable>\n <NTabs v-model:value=\"activeTab\" animated class=\"settings-tabs\">\n <!-- 外观 Tab -->\n <NTabPane name=\"appearance\" tab=\"🎨 外观\">\n <!-- 预设方案网格 -->\n <div class=\"settings-section\">\n <div class=\"preset-grid\">\n <div\n v-for=\"preset in THEME_PRESETS\"\n :key=\"preset.name\"\n class=\"preset-card\"\n :class=\"{ active: isCurrentPreset(preset) }\"\n @click=\"handleApplyPreset(preset)\"\n >\n <div class=\"preset-icon\">{{ preset.icon }}</div>\n <div class=\"preset-name\">{{ preset.name }}</div>\n <div class=\"preset-color\">\n <span\n class=\"color-dot\"\n :style=\"{ backgroundColor: preset.primaryColor }\"\n ></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 主题模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">主题模式</div>\n <NRadioGroup\n v-model:value=\"settingsStore.themeMode\"\n class=\"mode-group\"\n >\n <NRadioButton value=\"light\">\n <span class=\"i-mdi:white-balance-sunny mr-1\"></span>\n 浅色\n </NRadioButton>\n <NRadioButton value=\"dark\">\n <span class=\"i-mdi:moon-waning-crescent mr-1\"></span>\n 深色\n </NRadioButton>\n <NRadioButton value=\"auto\">\n <span class=\"i-mdi:theme-light-dark mr-1\"></span>\n 自动\n </NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 主题色 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">主题色</div>\n <div class=\"color-picker-wrapper\">\n <NColorPicker\n v-model:value=\"settingsStore.primaryColor\"\n :show-alpha=\"false\"\n :swatches=\"COLOR_SWATCHES\"\n :actions=\"['confirm']\"\n />\n <span class=\"color-value\">{{ settingsStore.primaryColor }}</span>\n </div>\n </div>\n\n <!-- 圆角大小 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">圆角大小</div>\n <NRadioGroup\n v-model:value=\"settingsStore.borderRadius\"\n class=\"radius-group\"\n >\n <NRadioButton value=\"small\">小 (4px)</NRadioButton>\n <NRadioButton value=\"medium\">中 (6px)</NRadioButton>\n <NRadioButton value=\"large\">大 (8px)</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 页面动画 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">页面动画</div>\n <div class=\"flex items-center justify-between mb-2\">\n <span class=\"text-sm\">启用动画</span>\n <NSwitch v-model:value=\"settingsStore.enableTransition\" />\n </div>\n <NRadioGroup\n v-model:value=\"settingsStore.transitionType\"\n :disabled=\"!settingsStore.enableTransition\"\n class=\"transition-group\"\n >\n <NRadioButton value=\"fade\">淡入</NRadioButton>\n <NRadioButton value=\"slide\">滑动</NRadioButton>\n <NRadioButton value=\"zoom\">缩放</NRadioButton>\n <NRadioButton value=\"none\">无</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 恢复默认 -->\n <div class=\"settings-section\">\n <NButton block secondary @click=\"handleResetAppearance\">\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 恢复外观默认设置\n </NButton>\n </div>\n </NTabPane>\n\n <!-- 布局 Tab -->\n <NTabPane name=\"layout\" tab=\"📐 布局\">\n <!-- 布局模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">布局模式</div>\n <div class=\"layout-grid\">\n <div\n v-for=\"mode in LAYOUT_MODE_OPTIONS\"\n :key=\"mode.value\"\n class=\"layout-item\"\n :class=\"{\n active: settingsStore.layoutMode === mode.value,\n disabled: mode.disabled,\n }\"\n @click.stop=\"handleLayoutChange(mode.value, mode.disabled)\"\n >\n <div class=\"layout-screenshot\">\n <svg\n class=\"layout-svg\"\n viewBox=\"0 0 56 48\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g v-html=\"mode.svg\"></g>\n </svg>\n </div>\n <div class=\"layout-label\">\n {{ mode.label }}\n <span v-if=\"mode.disabled\" class=\"badge-soon\">敬请期待</span>\n </div>\n </div>\n </div>\n </div>\n\n <div\n class=\"settings-section menu-expand-section\"\n data-menu-expand-mode=\"built-in\"\n >\n <div class=\"section-title\">菜单展开方式</div>\n <NRadioGroup\n v-model:value=\"settingsStore.menuExpandMode\"\n class=\"menu-expand-group\"\n >\n <NRadioButton value=\"inline\">传统展开</NRadioButton>\n <NRadioButton value=\"panel\">右侧面板</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 界面元素 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">界面元素</div>\n <div class=\"setting-item\">\n <span>显示面包屑</span>\n <NSwitch v-model:value=\"settingsStore.showBreadcrumb\" />\n </div>\n <div class=\"setting-item\">\n <span>显示面包屑图标</span>\n <NSwitch\n v-model:value=\"settingsStore.showBreadcrumbIcon\"\n :disabled=\"!settingsStore.showBreadcrumb\"\n />\n </div>\n <div class=\"setting-item\">\n <span>显示标签页</span>\n <NSwitch v-model:value=\"settingsStore.showTagsView\" />\n </div>\n <div class=\"setting-item\">\n <span>显示页脚</span>\n <NSwitch v-model:value=\"settingsStore.showFooter\" />\n </div>\n </div>\n\n <!-- 尺寸调整 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">尺寸调整</div>\n <div class=\"slider-item\">\n <div class=\"slider-label\">\n <span>侧边栏宽度</span>\n <span class=\"slider-value\"\n >{{ settingsStore.sidebarWidth }}px</span\n >\n </div>\n <NSlider\n v-model:value=\"settingsStore.sidebarWidth\"\n :min=\"180\"\n :max=\"280\"\n :step=\"10\"\n :marks=\"{ 180: '180', 220: '220', 280: '280' }\"\n />\n </div>\n <div class=\"slider-item\">\n <div class=\"slider-label\">\n <span>头部高度</span>\n <span class=\"slider-value\"\n >{{ settingsStore.headerHeight }}px</span\n >\n </div>\n <NSlider\n v-model:value=\"settingsStore.headerHeight\"\n :min=\"48\"\n :max=\"64\"\n :step=\"4\"\n :marks=\"{ 48: '48', 56: '56', 64: '64' }\"\n />\n </div>\n </div>\n\n <!-- 恢复默认 -->\n <div class=\"settings-section\">\n <NButton block secondary @click=\"handleResetLayout\">\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 恢复布局默认设置\n </NButton>\n </div>\n </NTabPane>\n\n <!-- 功能 Tab -->\n <NTabPane name=\"features\" tab=\"🔧 功能\">\n <!-- 缓存管理 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">缓存管理</div>\n <div class=\"action-buttons-grid\">\n <NButton secondary @click=\"handleClearCache\">\n <template #icon>\n <span class=\"i-mdi:delete-sweep\"></span>\n </template>\n 清除缓存\n </NButton>\n <NButton secondary @click=\"handleReload\">\n <template #icon>\n <span class=\"i-mdi:refresh\"></span>\n </template>\n 重新加载\n </NButton>\n </div>\n </div>\n\n <!-- 配置管理 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">配置管理</div>\n <div class=\"action-buttons-grid\">\n <NButton secondary @click=\"handleExportConfig\">\n <template #icon>\n <span class=\"i-mdi:download\"></span>\n </template>\n 导出配置\n </NButton>\n <NButton secondary @click=\"handleImportConfig\">\n <template #icon>\n <span class=\"i-mdi:upload\"></span>\n </template>\n 导入配置\n </NButton>\n </div>\n <NButton\n block\n type=\"error\"\n secondary\n class=\"mt-2\"\n @click=\"handleReset\"\n >\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 重置所有配置\n </NButton>\n </div>\n\n <!-- 系统模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">系统模式</div>\n <div class=\"setting-item\">\n <div class=\"setting-label\">\n <span>灰色模式</span>\n <span class=\"setting-desc\">用于哀悼日场景</span>\n </div>\n <NSwitch v-model:value=\"grayMode\" />\n </div>\n <div class=\"setting-item\">\n <div class=\"setting-label\">\n <span>色弱模式</span>\n <span class=\"setting-desc\">提供无障碍访问支持</span>\n </div>\n <NSwitch v-model:value=\"colorWeakMode\" />\n </div>\n </div>\n\n <!-- 水印设置 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">水印设置</div>\n <div class=\"setting-item\">\n <span>启用水印</span>\n <NSwitch v-model:value=\"watermarkEnabled\" />\n </div>\n <div v-if=\"watermarkEnabled\" class=\"mt-2\">\n <NInput\n v-model:value=\"watermarkText\"\n placeholder=\"请输入水印内容\"\n size=\"small\"\n />\n </div>\n </div>\n\n <!-- 系统信息 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">系统信息</div>\n <div class=\"system-info\">\n <div class=\"info-item\">\n <span class=\"info-label\">浏览器</span>\n <span class=\"info-value\">{{ systemInfo.browser }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">操作系统</span>\n <span class=\"info-value\">{{ systemInfo.os }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">屏幕分辨率</span>\n <span class=\"info-value\">{{ systemInfo.resolution }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">设备像素比</span>\n <span class=\"info-value\">{{ systemInfo.pixelRatio }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">语言</span>\n <span class=\"info-value\">{{ systemInfo.language }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">时区</span>\n <span class=\"info-value\">{{ systemInfo.timezone }}</span>\n </div>\n </div>\n </div>\n </NTabPane>\n </NTabs>\n </NDrawerContent>\n </NDrawer>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch, onUnmounted } from \"vue\";\nimport {\n NDrawer,\n NDrawerContent,\n NTabs,\n NTabPane,\n NRadioGroup,\n NRadioButton,\n NColorPicker,\n NSwitch,\n NButton,\n NSlider,\n NInput,\n useMessage,\n useDialog,\n} from \"naive-ui\";\nimport { useSettingsStore } from \"../../stores/settings\";\nimport { DEFAULT_SETTINGS } from \"../../constants\";\nimport { COLOR_SWATCHES, LAYOUT_MODE_OPTIONS, THEME_PRESETS } from \"./data\";\nimport type { ThemePreset } from \"../../types\";\n\n// ============ 数据定义 ============\n\nconst message = useMessage();\nconst dialog = useDialog();\nconst settingsStore = useSettingsStore();\nconst visible = defineModel<boolean>(\"show\", { default: false });\nconst activeTab = ref(\"appearance\");\n\n// 功能开关\nconst grayMode = ref(false);\nconst colorWeakMode = ref(false);\nconst watermarkEnabled = ref(false);\nconst watermarkText = ref(\"Robot Admin\");\n\n// 水印元素引用\nlet watermarkEl: HTMLElement | null = null;\n\n// 创建水印函数\nconst createWatermark = (text: string) => {\n // 移除旧水印\n if (watermarkEl) {\n document.body.removeChild(watermarkEl);\n watermarkEl = null;\n }\n\n // 创建 canvas\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\")!;\n canvas.width = 200;\n canvas.height = 150;\n\n ctx.font = \"14px Microsoft JhengHei\";\n ctx.fillStyle = \"rgba(120, 120, 120, 0.15)\";\n ctx.rotate((-20 * Math.PI) / 180);\n ctx.fillText(text, 0, 80);\n\n // 创建水印容器\n watermarkEl = document.createElement(\"div\");\n watermarkEl.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n background-image: url(${canvas.toDataURL()});\n background-repeat: repeat;\n z-index: 9999;\n user-select: none;\n `;\n\n document.body.appendChild(watermarkEl);\n};\n\n// 移除水印函数\nconst removeWatermark = () => {\n if (watermarkEl) {\n document.body.removeChild(watermarkEl);\n watermarkEl = null;\n }\n};\n\n// 监听灰色模式\nwatch(grayMode, (val) => {\n document.documentElement.classList.toggle(\"gray-mode\", val);\n});\n\n// 监听色弱模式\nwatch(colorWeakMode, (val) => {\n document.documentElement.classList.toggle(\"color-weak-mode\", val);\n});\n\n// 监听水印开关\nwatch(watermarkEnabled, (val) => {\n if (val) {\n createWatermark(watermarkText.value);\n } else {\n removeWatermark();\n }\n});\n\n// 监听水印文本\nwatch(watermarkText, (val) => {\n if (watermarkEnabled.value) {\n createWatermark(val);\n }\n});\n\n// 获取系统信息\nconst systemInfo = computed(() => {\n const ua = navigator.userAgent;\n let browser = \"Unknown\";\n let os = \"Unknown\";\n\n // 检测浏览器\n if (ua.includes(\"Chrome\")) browser = \"Chrome\";\n else if (ua.includes(\"Firefox\")) browser = \"Firefox\";\n else if (ua.includes(\"Safari\")) browser = \"Safari\";\n else if (ua.includes(\"Edge\")) browser = \"Edge\";\n\n // 检测操作系统\n if (ua.includes(\"Windows\")) os = \"Windows\";\n else if (ua.includes(\"Mac\")) os = \"macOS\";\n else if (ua.includes(\"Linux\")) os = \"Linux\";\n else if (ua.includes(\"Android\")) os = \"Android\";\n else if (ua.includes(\"iOS\")) os = \"iOS\";\n\n return {\n browser,\n os,\n resolution: `${window.screen.width} × ${window.screen.height}`,\n pixelRatio: window.devicePixelRatio + \"x\",\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n };\n});\n\n// 组件卸载时清理水印\nonUnmounted(() => {\n removeWatermark();\n});\n\n// 处理布局切换 - 阻止抽屉关闭\nconst handleLayoutChange = (value: string, disabled?: boolean) => {\n if (disabled) return;\n settingsStore.layoutMode = value as any;\n};\n\n// ============ 方法 ============\n\n/**\n * 判断是否是当前预设\n */\nconst isCurrentPreset = (preset: ThemePreset) => {\n return settingsStore.primaryColor === preset.primaryColor;\n};\n\n/**\n * 应用预设方案\n */\nconst handleApplyPreset = (preset: ThemePreset) => {\n settingsStore.applyPreset(preset);\n message.success(`已应用「${preset.name}」主题方案`);\n};\n\n/**\n * 恢复外观默认设置\n */\nconst handleResetAppearance = () => {\n settingsStore.themeMode = DEFAULT_SETTINGS.themeMode;\n settingsStore.primaryColor = DEFAULT_SETTINGS.primaryColor;\n settingsStore.borderRadius = DEFAULT_SETTINGS.borderRadius;\n settingsStore.transitionType = DEFAULT_SETTINGS.transitionType;\n settingsStore.enableTransition = DEFAULT_SETTINGS.enableTransition;\n message.success(\"已恢复外观默认设置\");\n};\n\n/**\n * 恢复布局默认设置\n */\nconst handleResetLayout = () => {\n settingsStore.layoutMode = DEFAULT_SETTINGS.layoutMode;\n settingsStore.menuExpandMode = DEFAULT_SETTINGS.menuExpandMode;\n settingsStore.fixedHeader = DEFAULT_SETTINGS.fixedHeader;\n settingsStore.showBreadcrumb = DEFAULT_SETTINGS.showBreadcrumb;\n settingsStore.showBreadcrumbIcon = DEFAULT_SETTINGS.showBreadcrumbIcon;\n settingsStore.showTagsView = DEFAULT_SETTINGS.showTagsView;\n settingsStore.tagsViewHeight = DEFAULT_SETTINGS.tagsViewHeight;\n settingsStore.tagsViewStyle = DEFAULT_SETTINGS.tagsViewStyle;\n settingsStore.showFooter = DEFAULT_SETTINGS.showFooter;\n settingsStore.sidebarWidth = DEFAULT_SETTINGS.sidebarWidth;\n settingsStore.sidebarCollapsedWidth = DEFAULT_SETTINGS.sidebarCollapsedWidth;\n settingsStore.headerHeight = DEFAULT_SETTINGS.headerHeight;\n message.success(\"已恢复布局默认设置\");\n};\n\n/**\n * 重置配置\n */\nconst handleReset = () => {\n dialog.warning({\n title: \"确认重置\",\n content: \"确定要恢复默认配置吗?此操作不可撤销。\",\n positiveText: \"确认\",\n negativeText: \"取消\",\n onPositiveClick: () => {\n settingsStore.resetSettings();\n message.success(\"已恢复默认配置\");\n },\n });\n};\n\n/**\n * 清除缓存\n */\nconst handleClearCache = () => {\n dialog.warning({\n title: \"确认清除\",\n content: \"确定要清除浏览器缓存吗?\",\n positiveText: \"确认\",\n negativeText: \"取消\",\n onPositiveClick: () => {\n // 清除 localStorage\n const keysToKeep = [\"theme-mode\", \"robot-admin-settings\"];\n const allKeys = Object.keys(localStorage);\n allKeys.forEach((key) => {\n if (!keysToKeep.includes(key)) {\n localStorage.removeItem(key);\n }\n });\n // 清除 sessionStorage\n sessionStorage.clear();\n message.success(\"缓存已清除\");\n },\n });\n};\n\n/**\n * 重新加载页面\n */\nconst handleReload = () => {\n location.reload();\n};\n\n/**\n * 导出配置\n */\nconst handleExportConfig = () => {\n const config = {\n settings: settingsStore.settingsState,\n gray: grayMode.value,\n colorWeak: colorWeakMode.value,\n watermark: {\n enabled: watermarkEnabled.value,\n text: watermarkText.value,\n },\n exportTime: new Date().toISOString(),\n };\n const blob = new Blob([JSON.stringify(config, null, 2)], {\n type: \"application/json\",\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `robot-admin-config-${Date.now()}.json`;\n a.click();\n URL.revokeObjectURL(url);\n message.success(\"配置已导出\");\n};\n\n/**\n * 导入配置\n */\nconst handleImportConfig = () => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = \"application/json\";\n input.onchange = async (e: Event) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (!file) return;\n\n try {\n const text = await file.text();\n const config = JSON.parse(text);\n\n // 应用配置\n if (config.settings) {\n settingsStore.$patch(config.settings);\n }\n if (config.gray !== undefined) {\n grayMode.value = config.gray;\n }\n if (config.colorWeak !== undefined) {\n colorWeakMode.value = config.colorWeak;\n }\n if (config.watermark) {\n watermarkEnabled.value = config.watermark.enabled;\n watermarkText.value = config.watermark.text;\n }\n\n message.success(\"配置已导入\");\n } catch (error) {\n message.error(\"配置文件格式错误\");\n }\n };\n input.click();\n};\n</script>\n\n<style scoped lang=\"scss\">\n@use \"../../styles/settings.scss\" as *;\n</style>\n","<!--\n * @robot-admin/layout - SettingsDrawer\n *\n * 布局配置抽屉\n * 包含外观/布局/功能三大模块,支持主题预设、布局切换、配置导入导出等\n -->\n<template>\n <NDrawer\n v-model:show=\"visible\"\n :width=\"380\"\n placement=\"right\"\n :trap-focus=\"false\"\n :block-scroll=\"false\"\n >\n <NDrawerContent title=\"⚙️ 布局配置\" closable>\n <NTabs v-model:value=\"activeTab\" animated class=\"settings-tabs\">\n <!-- 外观 Tab -->\n <NTabPane name=\"appearance\" tab=\"🎨 外观\">\n <!-- 预设方案网格 -->\n <div class=\"settings-section\">\n <div class=\"preset-grid\">\n <div\n v-for=\"preset in THEME_PRESETS\"\n :key=\"preset.name\"\n class=\"preset-card\"\n :class=\"{ active: isCurrentPreset(preset) }\"\n @click=\"handleApplyPreset(preset)\"\n >\n <div class=\"preset-icon\">{{ preset.icon }}</div>\n <div class=\"preset-name\">{{ preset.name }}</div>\n <div class=\"preset-color\">\n <span\n class=\"color-dot\"\n :style=\"{ backgroundColor: preset.primaryColor }\"\n ></span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- 主题模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">主题模式</div>\n <NRadioGroup\n v-model:value=\"settingsStore.themeMode\"\n class=\"mode-group\"\n >\n <NRadioButton value=\"light\">\n <span class=\"i-mdi:white-balance-sunny mr-1\"></span>\n 浅色\n </NRadioButton>\n <NRadioButton value=\"dark\">\n <span class=\"i-mdi:moon-waning-crescent mr-1\"></span>\n 深色\n </NRadioButton>\n <NRadioButton value=\"auto\">\n <span class=\"i-mdi:theme-light-dark mr-1\"></span>\n 自动\n </NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 主题色 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">主题色</div>\n <div class=\"color-picker-wrapper\">\n <NColorPicker\n v-model:value=\"settingsStore.primaryColor\"\n :show-alpha=\"false\"\n :swatches=\"COLOR_SWATCHES\"\n :actions=\"['confirm']\"\n />\n <span class=\"color-value\">{{ settingsStore.primaryColor }}</span>\n </div>\n </div>\n\n <!-- 圆角大小 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">圆角大小</div>\n <NRadioGroup\n v-model:value=\"settingsStore.borderRadius\"\n class=\"radius-group\"\n >\n <NRadioButton value=\"small\">小 (4px)</NRadioButton>\n <NRadioButton value=\"medium\">中 (6px)</NRadioButton>\n <NRadioButton value=\"large\">大 (8px)</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 页面动画 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">页面动画</div>\n <div class=\"flex items-center justify-between mb-2\">\n <span class=\"text-sm\">启用动画</span>\n <NSwitch v-model:value=\"settingsStore.enableTransition\" />\n </div>\n <NRadioGroup\n v-model:value=\"settingsStore.transitionType\"\n :disabled=\"!settingsStore.enableTransition\"\n class=\"transition-group\"\n >\n <NRadioButton value=\"fade\">淡入</NRadioButton>\n <NRadioButton value=\"slide\">滑动</NRadioButton>\n <NRadioButton value=\"zoom\">缩放</NRadioButton>\n <NRadioButton value=\"none\">无</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 恢复默认 -->\n <div class=\"settings-section\">\n <NButton block secondary @click=\"handleResetAppearance\">\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 恢复外观默认设置\n </NButton>\n </div>\n </NTabPane>\n\n <!-- 布局 Tab -->\n <NTabPane name=\"layout\" tab=\"📐 布局\">\n <!-- 布局模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">布局模式</div>\n <div class=\"layout-grid\">\n <div\n v-for=\"mode in LAYOUT_MODE_OPTIONS\"\n :key=\"mode.value\"\n class=\"layout-item\"\n :class=\"{\n active: settingsStore.layoutMode === mode.value,\n disabled: mode.disabled,\n }\"\n @click.stop=\"handleLayoutChange(mode.value, mode.disabled)\"\n >\n <div class=\"layout-screenshot\">\n <svg\n class=\"layout-svg\"\n viewBox=\"0 0 56 48\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <g v-html=\"mode.svg\"></g>\n </svg>\n </div>\n <div class=\"layout-label\">\n {{ mode.label }}\n <span v-if=\"mode.disabled\" class=\"badge-soon\">敬请期待</span>\n </div>\n </div>\n </div>\n </div>\n\n <div\n class=\"settings-section menu-expand-section\"\n data-menu-expand-mode=\"built-in\"\n >\n <div class=\"section-title\">菜单展开方式</div>\n <NRadioGroup\n v-model:value=\"settingsStore.menuExpandMode\"\n class=\"menu-expand-group\"\n >\n <NRadioButton value=\"inline\">传统展开</NRadioButton>\n <NRadioButton value=\"panel\">右侧面板</NRadioButton>\n </NRadioGroup>\n </div>\n\n <!-- 界面元素 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">界面元素</div>\n <div class=\"setting-item\">\n <span>显示面包屑</span>\n <NSwitch v-model:value=\"settingsStore.showBreadcrumb\" />\n </div>\n <div class=\"setting-item\">\n <span>显示面包屑图标</span>\n <NSwitch\n v-model:value=\"settingsStore.showBreadcrumbIcon\"\n :disabled=\"!settingsStore.showBreadcrumb\"\n />\n </div>\n <div class=\"setting-item\">\n <span>显示标签页</span>\n <NSwitch v-model:value=\"settingsStore.showTagsView\" />\n </div>\n <div class=\"setting-item\">\n <span>显示页脚</span>\n <NSwitch v-model:value=\"settingsStore.showFooter\" />\n </div>\n </div>\n\n <!-- 尺寸调整 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">尺寸调整</div>\n <div class=\"slider-item\">\n <div class=\"slider-label\">\n <span>侧边栏宽度</span>\n <span class=\"slider-value\"\n >{{ settingsStore.sidebarWidth }}px</span\n >\n </div>\n <NSlider\n v-model:value=\"settingsStore.sidebarWidth\"\n :min=\"180\"\n :max=\"280\"\n :step=\"10\"\n :marks=\"{ 180: '180', 220: '220', 280: '280' }\"\n />\n </div>\n <div class=\"slider-item\">\n <div class=\"slider-label\">\n <span>头部高度</span>\n <span class=\"slider-value\"\n >{{ settingsStore.headerHeight }}px</span\n >\n </div>\n <NSlider\n v-model:value=\"settingsStore.headerHeight\"\n :min=\"48\"\n :max=\"64\"\n :step=\"4\"\n :marks=\"{ 48: '48', 56: '56', 64: '64' }\"\n />\n </div>\n </div>\n\n <!-- 恢复默认 -->\n <div class=\"settings-section\">\n <NButton block secondary @click=\"handleResetLayout\">\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 恢复布局默认设置\n </NButton>\n </div>\n </NTabPane>\n\n <!-- 功能 Tab -->\n <NTabPane name=\"features\" tab=\"🔧 功能\">\n <!-- 缓存管理 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">缓存管理</div>\n <div class=\"action-buttons-grid\">\n <NButton secondary @click=\"handleClearCache\">\n <template #icon>\n <span class=\"i-mdi:delete-sweep\"></span>\n </template>\n 清除缓存\n </NButton>\n <NButton secondary @click=\"handleReload\">\n <template #icon>\n <span class=\"i-mdi:refresh\"></span>\n </template>\n 重新加载\n </NButton>\n </div>\n </div>\n\n <!-- 配置管理 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">配置管理</div>\n <div class=\"action-buttons-grid\">\n <NButton secondary @click=\"handleExportConfig\">\n <template #icon>\n <span class=\"i-mdi:download\"></span>\n </template>\n 导出配置\n </NButton>\n <NButton secondary @click=\"handleImportConfig\">\n <template #icon>\n <span class=\"i-mdi:upload\"></span>\n </template>\n 导入配置\n </NButton>\n </div>\n <NButton\n block\n type=\"error\"\n secondary\n class=\"mt-2\"\n @click=\"handleReset\"\n >\n <template #icon>\n <span class=\"i-mdi:restore\"></span>\n </template>\n 重置所有配置\n </NButton>\n </div>\n\n <!-- 系统模式 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">系统模式</div>\n <div class=\"setting-item\">\n <div class=\"setting-label\">\n <span>灰色模式</span>\n <span class=\"setting-desc\">用于哀悼日场景</span>\n </div>\n <NSwitch v-model:value=\"grayMode\" />\n </div>\n <div class=\"setting-item\">\n <div class=\"setting-label\">\n <span>色弱模式</span>\n <span class=\"setting-desc\">提供无障碍访问支持</span>\n </div>\n <NSwitch v-model:value=\"colorWeakMode\" />\n </div>\n </div>\n\n <!-- 水印设置 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">水印设置</div>\n <div class=\"setting-item\">\n <span>启用水印</span>\n <NSwitch v-model:value=\"watermarkEnabled\" />\n </div>\n <div v-if=\"watermarkEnabled\" class=\"mt-2\">\n <NInput\n v-model:value=\"watermarkText\"\n placeholder=\"请输入水印内容\"\n size=\"small\"\n />\n </div>\n </div>\n\n <!-- 系统信息 -->\n <div class=\"settings-section\">\n <div class=\"section-title\">系统信息</div>\n <div class=\"system-info\">\n <div class=\"info-item\">\n <span class=\"info-label\">浏览器</span>\n <span class=\"info-value\">{{ systemInfo.browser }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">操作系统</span>\n <span class=\"info-value\">{{ systemInfo.os }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">屏幕分辨率</span>\n <span class=\"info-value\">{{ systemInfo.resolution }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">设备像素比</span>\n <span class=\"info-value\">{{ systemInfo.pixelRatio }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">语言</span>\n <span class=\"info-value\">{{ systemInfo.language }}</span>\n </div>\n <div class=\"info-item\">\n <span class=\"info-label\">时区</span>\n <span class=\"info-value\">{{ systemInfo.timezone }}</span>\n </div>\n </div>\n </div>\n </NTabPane>\n </NTabs>\n </NDrawerContent>\n </NDrawer>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch, onUnmounted } from \"vue\";\nimport {\n NDrawer,\n NDrawerContent,\n NTabs,\n NTabPane,\n NRadioGroup,\n NRadioButton,\n NColorPicker,\n NSwitch,\n NButton,\n NSlider,\n NInput,\n useMessage,\n useDialog,\n} from \"naive-ui\";\nimport { useSettingsStore } from \"../../stores/settings\";\nimport { DEFAULT_SETTINGS } from \"../../constants\";\nimport { COLOR_SWATCHES, LAYOUT_MODE_OPTIONS, THEME_PRESETS } from \"./data\";\nimport type { ThemePreset } from \"../../types\";\n\n// ============ 数据定义 ============\n\nconst message = useMessage();\nconst dialog = useDialog();\nconst settingsStore = useSettingsStore();\nconst visible = defineModel<boolean>(\"show\", { default: false });\nconst activeTab = ref(\"appearance\");\n\n// 功能开关\nconst grayMode = ref(false);\nconst colorWeakMode = ref(false);\nconst watermarkEnabled = ref(false);\nconst watermarkText = ref(\"Robot Admin\");\n\n// 水印元素引用\nlet watermarkEl: HTMLElement | null = null;\n\n// 创建水印函数\nconst createWatermark = (text: string) => {\n // 移除旧水印\n if (watermarkEl) {\n document.body.removeChild(watermarkEl);\n watermarkEl = null;\n }\n\n // 创建 canvas\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\")!;\n canvas.width = 200;\n canvas.height = 150;\n\n ctx.font = \"14px Microsoft JhengHei\";\n ctx.fillStyle = \"rgba(120, 120, 120, 0.15)\";\n ctx.rotate((-20 * Math.PI) / 180);\n ctx.fillText(text, 0, 80);\n\n // 创建水印容器\n watermarkEl = document.createElement(\"div\");\n watermarkEl.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n pointer-events: none;\n background-image: url(${canvas.toDataURL()});\n background-repeat: repeat;\n z-index: 9999;\n user-select: none;\n `;\n\n document.body.appendChild(watermarkEl);\n};\n\n// 移除水印函数\nconst removeWatermark = () => {\n if (watermarkEl) {\n document.body.removeChild(watermarkEl);\n watermarkEl = null;\n }\n};\n\n// 监听灰色模式\nwatch(grayMode, (val) => {\n document.documentElement.classList.toggle(\"gray-mode\", val);\n});\n\n// 监听色弱模式\nwatch(colorWeakMode, (val) => {\n document.documentElement.classList.toggle(\"color-weak-mode\", val);\n});\n\n// 监听水印开关\nwatch(watermarkEnabled, (val) => {\n if (val) {\n createWatermark(watermarkText.value);\n } else {\n removeWatermark();\n }\n});\n\n// 监听水印文本\nwatch(watermarkText, (val) => {\n if (watermarkEnabled.value) {\n createWatermark(val);\n }\n});\n\n// 获取系统信息\nconst systemInfo = computed(() => {\n const ua = navigator.userAgent;\n let browser = \"Unknown\";\n let os = \"Unknown\";\n\n // 检测浏览器\n if (ua.includes(\"Chrome\")) browser = \"Chrome\";\n else if (ua.includes(\"Firefox\")) browser = \"Firefox\";\n else if (ua.includes(\"Safari\")) browser = \"Safari\";\n else if (ua.includes(\"Edge\")) browser = \"Edge\";\n\n // 检测操作系统\n if (ua.includes(\"Windows\")) os = \"Windows\";\n else if (ua.includes(\"Mac\")) os = \"macOS\";\n else if (ua.includes(\"Linux\")) os = \"Linux\";\n else if (ua.includes(\"Android\")) os = \"Android\";\n else if (ua.includes(\"iOS\")) os = \"iOS\";\n\n return {\n browser,\n os,\n resolution: `${window.screen.width} × ${window.screen.height}`,\n pixelRatio: window.devicePixelRatio + \"x\",\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n };\n});\n\n// 组件卸载时清理水印\nonUnmounted(() => {\n removeWatermark();\n});\n\n// 处理布局切换 - 阻止抽屉关闭\nconst handleLayoutChange = (value: string, disabled?: boolean) => {\n if (disabled) return;\n settingsStore.layoutMode = value as any;\n};\n\n// ============ 方法 ============\n\n/**\n * 判断是否是当前预设\n */\nconst isCurrentPreset = (preset: ThemePreset) => {\n return settingsStore.primaryColor === preset.primaryColor;\n};\n\n/**\n * 应用预设方案\n */\nconst handleApplyPreset = (preset: ThemePreset) => {\n settingsStore.applyPreset(preset);\n message.success(`已应用「${preset.name}」主题方案`);\n};\n\n/**\n * 恢复外观默认设置\n */\nconst handleResetAppearance = () => {\n settingsStore.themeMode = DEFAULT_SETTINGS.themeMode;\n settingsStore.primaryColor = DEFAULT_SETTINGS.primaryColor;\n settingsStore.borderRadius = DEFAULT_SETTINGS.borderRadius;\n settingsStore.transitionType = DEFAULT_SETTINGS.transitionType;\n settingsStore.enableTransition = DEFAULT_SETTINGS.enableTransition;\n message.success(\"已恢复外观默认设置\");\n};\n\n/**\n * 恢复布局默认设置\n */\nconst handleResetLayout = () => {\n settingsStore.layoutMode = DEFAULT_SETTINGS.layoutMode;\n settingsStore.menuExpandMode = DEFAULT_SETTINGS.menuExpandMode;\n settingsStore.fixedHeader = DEFAULT_SETTINGS.fixedHeader;\n settingsStore.showBreadcrumb = DEFAULT_SETTINGS.showBreadcrumb;\n settingsStore.showBreadcrumbIcon = DEFAULT_SETTINGS.showBreadcrumbIcon;\n settingsStore.showTagsView = DEFAULT_SETTINGS.showTagsView;\n settingsStore.tagsViewHeight = DEFAULT_SETTINGS.tagsViewHeight;\n settingsStore.tagsViewStyle = DEFAULT_SETTINGS.tagsViewStyle;\n settingsStore.showFooter = DEFAULT_SETTINGS.showFooter;\n settingsStore.sidebarWidth = DEFAULT_SETTINGS.sidebarWidth;\n settingsStore.sidebarCollapsedWidth = DEFAULT_SETTINGS.sidebarCollapsedWidth;\n settingsStore.headerHeight = DEFAULT_SETTINGS.headerHeight;\n message.success(\"已恢复布局默认设置\");\n};\n\n/**\n * 重置配置\n */\nconst handleReset = () => {\n dialog.warning({\n title: \"确认重置\",\n content: \"确定要恢复默认配置吗?此操作不可撤销。\",\n positiveText: \"确认\",\n negativeText: \"取消\",\n onPositiveClick: () => {\n settingsStore.resetSettings();\n message.success(\"已恢复默认配置\");\n },\n });\n};\n\n/**\n * 清除缓存\n */\nconst handleClearCache = () => {\n dialog.warning({\n title: \"确认清除\",\n content: \"确定要清除浏览器缓存吗?\",\n positiveText: \"确认\",\n negativeText: \"取消\",\n onPositiveClick: () => {\n // 清除 localStorage\n const keysToKeep = [\"theme-mode\", \"robot-admin-settings\"];\n const allKeys = Object.keys(localStorage);\n allKeys.forEach((key) => {\n if (!keysToKeep.includes(key)) {\n localStorage.removeItem(key);\n }\n });\n // 清除 sessionStorage\n sessionStorage.clear();\n message.success(\"缓存已清除\");\n },\n });\n};\n\n/**\n * 重新加载页面\n */\nconst handleReload = () => {\n location.reload();\n};\n\n/**\n * 导出配置\n */\nconst handleExportConfig = () => {\n const config = {\n settings: settingsStore.settingsState,\n gray: grayMode.value,\n colorWeak: colorWeakMode.value,\n watermark: {\n enabled: watermarkEnabled.value,\n text: watermarkText.value,\n },\n exportTime: new Date().toISOString(),\n };\n const blob = new Blob([JSON.stringify(config, null, 2)], {\n type: \"application/json\",\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `robot-admin-config-${Date.now()}.json`;\n a.click();\n URL.revokeObjectURL(url);\n message.success(\"配置已导出\");\n};\n\n/**\n * 导入配置\n */\nconst handleImportConfig = () => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = \"application/json\";\n input.onchange = async (e: Event) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (!file) return;\n\n try {\n const text = await file.text();\n const config = JSON.parse(text);\n\n // 应用配置\n if (config.settings) {\n settingsStore.$patch(config.settings);\n }\n if (config.gray !== undefined) {\n grayMode.value = config.gray;\n }\n if (config.colorWeak !== undefined) {\n colorWeakMode.value = config.colorWeak;\n }\n if (config.watermark) {\n watermarkEnabled.value = config.watermark.enabled;\n watermarkText.value = config.watermark.text;\n }\n\n message.success(\"配置已导入\");\n } catch (error) {\n message.error(\"配置文件格式错误\");\n }\n };\n input.click();\n};\n</script>\n\n<style scoped lang=\"scss\">\n@use \"../../styles/settings.scss\" as *;\n</style>\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 * 用于 MixLayout 的悬浮二级菜单(点击一级菜单后展开)\r\n -->\r\n<template>\r\n <Transition name=\"slide-fade\">\r\n <div\r\n v-if=\"show && menuItem\"\r\n class=\"floating-menu\"\r\n :class=\"[ctx.isDark.value ? 'dark-theme' : 'light-theme']\"\r\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\"\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=\"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\"\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=\"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 {\r\n background: rgba(99, 102, 241, 0.08);\r\n}\r\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 * 用于 MixLayout 的悬浮二级菜单(点击一级菜单后展开)\r\n -->\r\n<template>\r\n <Transition name=\"slide-fade\">\r\n <div\r\n v-if=\"show && menuItem\"\r\n class=\"floating-menu\"\r\n :class=\"[ctx.isDark.value ? 'dark-theme' : 'light-theme']\"\r\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\"\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=\"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\"\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=\"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 {\r\n background: rgba(99, 102, 241, 0.08);\r\n}\r\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 * 用于 ReverseHorizontalMixLayout 的右侧二级菜单\r\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 * 用于 ReverseHorizontalMixLayout 的右侧二级菜单\r\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 * 用于 CardLayout 的 hover 触发的抽屉式功能导航\r\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 @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 class=\"drawer-menu__category-header\" @click=\"navigate(category)\">\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 @click=\"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 @click=\"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 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 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 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__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 * 用于 CardLayout 的 hover 触发的抽屉式功能导航\r\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 @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 class=\"drawer-menu__category-header\" @click=\"navigate(category)\">\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 @click=\"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 @click=\"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 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 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 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__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 * 用于 CardLayout 的 hover 触发抽屉式菜单\r\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 * 用于 CardLayout 的 hover 触发抽屉式菜单\r\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","/**\n * @robot-admin/layout\n *\n * 布局系统一键初始化\n */\n\nimport type { App } from \"vue\";\nimport type { SettingsStoreOptions } from \"./types\";\nimport { createSettingsStore } from \"./stores/settings\";\n\n/**\n * 初始化布局系统\n *\n * @example\n * ```ts\n * import { setupLayout } from '@robot-admin/layout'\n * setupLayout(app, {\n * onThemeModeChange: async (mode) => {\n * const themeStore = useThemeStore()\n * await themeStore.setMode(mode)\n * }\n * })\n * ```\n */\nexport function setupLayout(_app: App, options: SettingsStoreOptions = {}) {\n // 注册 settings store\n const settingsStore = createSettingsStore(options);\n\n // 初始化 CSS Variables\n settingsStore().syncCSSVariables();\n\n return settingsStore;\n}\n"],"mappings":"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"}
|