@robot-admin/naive-ui-components 0.6.0 → 0.6.2

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +48 -32
  2. package/dist/C_Breadcrumb.js.map +1 -1
  3. package/dist/C_Breadcrumb2.js.map +1 -1
  4. package/dist/C_Menu.js.map +1 -1
  5. package/dist/C_Menu2.js.map +1 -1
  6. package/dist/{C_Table-Dcam_tRs.css.map → C_Table-vZlcgH6x.css.map} +1 -1
  7. package/dist/{C_Table-tbUx-R4H.css.map → C_Table-wzAO43l0.css.map} +1 -1
  8. package/dist/C_Table.css +69 -69
  9. package/dist/C_Table.js.map +1 -1
  10. package/dist/C_Table2.js +1 -1
  11. package/dist/C_Table2.js.map +1 -1
  12. package/dist/{C_TagsView-DX3LecK1.css.map → C_TagsView-BsutyC3U.css.map} +1 -1
  13. package/dist/{C_TagsView-DqOTa8bL.css.map → C_TagsView-Crm7yWg9.css.map} +1 -1
  14. package/dist/C_TagsView.css +36 -36
  15. package/dist/C_TagsView.js.map +1 -1
  16. package/dist/C_TagsView2.js +1 -1
  17. package/dist/C_TagsView2.js.map +1 -1
  18. package/dist/constants.d.ts +3 -3
  19. package/dist/constants2.d.ts +5 -5
  20. package/dist/constants3.d.ts +2 -2
  21. package/dist/constants4.d.ts +6 -6
  22. package/dist/constants5.d.ts +2 -2
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/index11.vue.d.ts +4 -4
  26. package/dist/index12.vue.d.ts +2 -2
  27. package/dist/index13.vue.d.ts +5 -5
  28. package/dist/index15.vue.d.ts +1 -1
  29. package/dist/index17.vue.d.ts +1 -1
  30. package/dist/index19.vue.d.ts +3 -3
  31. package/dist/index2.vue.d.ts +1 -1
  32. package/dist/index4.vue.d.ts +4 -4
  33. package/dist/index5.vue.d.ts +2 -2
  34. package/dist/index6.vue.d.ts +2 -2
  35. package/dist/index7.vue.d.ts +1 -1
  36. package/dist/index8.vue.d.ts +1 -1
  37. package/dist/index9.vue.d.ts +3 -3
  38. package/dist/menuAdapter.js +1 -1
  39. package/dist/menuAdapter.js.map +1 -1
  40. package/dist/resolver.cjs.map +1 -1
  41. package/dist/resolver.js.map +1 -1
  42. package/dist/style.css +107 -107
  43. package/dist/useCollapsePanel.d.ts +3 -3
  44. package/dist/useCropperCore.d.ts +7 -7
  45. package/dist/useDraggableLayout.d.ts +2 -2
  46. package/dist/useDynamicFormState.d.ts +118 -118
  47. package/dist/useEdgeInteraction.d.ts +1 -1
  48. package/dist/useInfiniteScroll.d.ts +2 -2
  49. package/dist/useModalEdit.d.ts +0 -1
  50. package/dist/useQRCode.d.ts +2 -2
  51. package/dist/useSignatureHistory.d.ts +1 -1
  52. package/dist/useSplitResize.d.ts +2 -2
  53. package/dist/useTimeSelection.d.ts +2 -2
  54. package/dist/useTreeOperations.d.ts +3 -3
  55. package/dist/useWorkflowValidation.d.ts +3 -3
  56. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,32 +1,48 @@
1
- # @robot-admin/naive-ui-components
2
-
3
- ## 0.6.0
4
-
5
- ### Minor Changes
6
-
7
- - 依赖架构优化 + CSS 按需导入 + Changesets 集成
8
- - 将 optionalDependencies 迁移至 dependencies,消费端自动传递安装
9
- - 新增按组件 CSS 导出 (`C_*/style.css`),支持样式按需导入
10
- - 修复 merge-css.js 中 esm/cjs CSS 重复合并问题
11
- - Resolver 新增 `importStyle` 选项,自动注入组件样式
12
- - 集成 @changesets/cli 自动化版本管理
13
-
14
- - 提取 C_Menu / C_Breadcrumb / C_TagsView 布局组件 + 适配器架构
15
- - 新增 `_shared` 模块:统一类型定义(RouteItem, TagItem, BreadcrumbItem)+ menuAdapter 适配器
16
- - `createMenuOptions(routes, config)` 工厂函数:将 RouteItem[] 转换为 NMenu MenuOption[]
17
- - `flattenRoutes()` 辅助:扁平化嵌套路由用于标签页匹配
18
- - C_Menu:支持双输入(options / routes),内置 labelFormatter 回调解耦 i18n
19
- - C_Breadcrumb:auto(route.matched)+ manual(items)双模式
20
- - C_TagsView:useTagsView composable + localStorage 持久化 + 右键菜单
21
- - vue-router ^4.0.0 新增为 peerDependency
22
-
23
- ## 0.5.0
24
-
25
- ### Minor Changes
26
-
27
- - 依赖架构优化 + CSS 按需导入 + Changesets 集成
28
- - 将 optionalDependencies 迁移至 dependencies,消费端自动传递安装
29
- - 新增按组件 CSS 导出 (`C_*/style.css`),支持样式按需导入
30
- - 修复 merge-css.js 中 esm/cjs CSS 重复合并问题
31
- - Resolver 新增 `importStyle` 选项,自动注入组件样式
32
- - 集成 @changesets/cli 自动化版本管理
1
+ # @robot-admin/naive-ui-components
2
+
3
+ ## 0.6.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 修复菜单图标在生产环境下错位问题
8
+ - menuAdapter 的 defaultRenderIcon 中 `inline-flex items-center` 原子类改为内联 style
9
+ - 组件库不应依赖宿主项目的 UnoCSS/Tailwind 原子类,否则生产构建时可能丢失样式
10
+
11
+ ## 0.6.1
12
+
13
+ ### Patch Changes
14
+
15
+ - 修复 C_Table loading 状态在 crud 模式下失效的问题
16
+ - `loading` prop 默认值从 `false` 改为 `undefined`,使 `??` 运算符能正确回退到 `crud.loading.value`
17
+ - 之前 `false ?? crud.loading.value` 始终返回 `false`(`??` 不回退 falsy 值,只回退 null/undefined)
18
+
19
+ ## 0.6.0
20
+
21
+ ### Minor Changes
22
+
23
+ - 依赖架构优化 + CSS 按需导入 + Changesets 集成
24
+ - 将 optionalDependencies 迁移至 dependencies,消费端自动传递安装
25
+ - 新增按组件 CSS 导出 (`C_*/style.css`),支持样式按需导入
26
+ - 修复 merge-css.js 中 esm/cjs CSS 重复合并问题
27
+ - Resolver 新增 `importStyle` 选项,自动注入组件样式
28
+ - 集成 @changesets/cli 自动化版本管理
29
+
30
+ - 提取 C_Menu / C_Breadcrumb / C_TagsView 布局组件 + 适配器架构
31
+ - 新增 `_shared` 模块:统一类型定义(RouteItem, TagItem, BreadcrumbItem)+ menuAdapter 适配器
32
+ - `createMenuOptions(routes, config)` 工厂函数:将 RouteItem[] 转换为 NMenu MenuOption[]
33
+ - `flattenRoutes()` 辅助:扁平化嵌套路由用于标签页匹配
34
+ - C_Menu:支持双输入(options / routes),内置 labelFormatter 回调解耦 i18n
35
+ - C_Breadcrumb:auto(route.matched)+ manual(items)双模式
36
+ - C_TagsView:useTagsView composable + localStorage 持久化 + 右键菜单
37
+ - vue-router ^4.0.0 新增为 peerDependency
38
+
39
+ ## 0.5.0
40
+
41
+ ### Minor Changes
42
+
43
+ - 依赖架构优化 + CSS 按需导入 + Changesets 集成
44
+ - 将 optionalDependencies 迁移至 dependencies,消费端自动传递安装
45
+ - 新增按组件 CSS 导出 (`C_*/style.css`),支持样式按需导入
46
+ - 修复 merge-css.js 中 esm/cjs CSS 重复合并问题
47
+ - Resolver 新增 `importStyle` 选项,自动注入组件样式
48
+ - 集成 @changesets/cli 自动化版本管理
@@ -1 +1 @@
1
- {"version":3,"file":"C_Breadcrumb.js","names":["showIcon"],"sources":["../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-breadcrumb flex-1 min-w-0\">\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb-link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top mr-1\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-breadcrumb flex-1 min-w-0\">\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb-link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top mr-1\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n","<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-breadcrumb flex-1 min-w-0\">\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb-link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top mr-1\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n"],"mappings":"iXEuEE,IAAM,EAAQ,EAoBR,EAAO,EAKP,GAAA,EAAA,EAAA,WAAiB,CAEjB,EAAe,GACnB,EAAM,eAAiB,EAAM,eAAe,EAAM,CAAG,EAGjD,GAAA,EAAA,EAAA,cACG,EAAM,QACV,OAAQ,GAAgB,EAAO,MAAM,MAAK,CAC1C,IAAK,IAAiB,CACrB,IAAK,EAAO,KACZ,MAAO,EAAa,EAAO,KAAK,OAAS,GAAc,CACvD,KAAM,EAAO,KAAK,KAClB,SAAU,EAAO,UAAU,OACvB,EAAO,SAAS,IAAK,IAAgB,CACnC,IAAK,EAAM,KACX,MAAO,EAAa,EAAM,MAAM,OAAS,GAAc,CACvD,GAAI,EAAM,UAAU,QAAU,CAC5B,SAAU,EAAM,SAAS,IAAK,IAAqB,CACjD,IAAK,EAAW,KAChB,MAAO,EAAa,EAAW,MAAM,OAAS,GAAc,CAC7D,EAAE,CACJ,CACF,EAAC,CACF,EAAE,CACP,EAAC,CACL,CAEK,GAAA,EAAA,EAAA,cACE,EAAM,OAAS,EAAe,MACtC,CAEM,EAAgB,GAAgB,CACpC,EAAK,SAAU,EAAG,uHArEd,MAlCN,EAkCM,EAAA,EAAA,EAAA,aADU,EAAA,KAAA,2BA9B8B,GAAA,EAAA,EAAA,WAAA,GAAA,EAAA,EAAA,EAAA,oBA6BxB,EAAA,SAAA,MAAA,EAAA,EAAA,YA7BQ,EAAA,OAAhB,EAAM,yCA6BE,EAAA,CA5Bf,IAAK,EAAK,CAAA,2BAkBb,CAfU,EAAK,UAAU,SAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAYX,EAAA,OAXT,QAAS,EAAK,SACd,SAAQ,8BASH,EAAA,EAAA,EAAA,oBAAA,MAPN,EAOM,CALIA,EAAAA,WAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,cAAAA,EAAAA,EAAAA,OAGN,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,4FACN,KAAA,EAAA,EAAA,iBACC,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,qEAcV,OAAA,OATL,MAAM,oBACL,QAAK,GAAE,EAAa,EAAK,IAAG,GAGrBA,EAAAA,WAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,cAAAA,EAAAA,EAAAA,OAGN,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,iGACN,KAAA,EAAA,EAAA,iBACC,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA"}
1
+ {"version":3,"file":"C_Breadcrumb.js","names":["showIcon"],"sources":["../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\r\n *\r\n * 支持两种模式:\r\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\r\n * 2. 手动模式 — 传入 `items` 完全自定义\r\n *\r\n * @example 自动模式(零配置)\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 手动模式\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :items=\"customBreadcrumbs\"\r\n * @select=\"handleSelect\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-breadcrumb flex-1 min-w-0\">\r\n <NBreadcrumb>\r\n <NBreadcrumbItem\r\n v-for=\"(item, index) in breadcrumbItems\"\r\n :key=\"index\"\r\n >\r\n <NDropdown\r\n v-if=\"item.children?.length\"\r\n :options=\"item.children\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"trigger\">\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top\"\r\n />\r\n {{ item.label }}\r\n </div>\r\n </NDropdown>\r\n <span\r\n v-else\r\n class=\"c-breadcrumb-link\"\r\n @click=\"handleSelect(item.key)\"\r\n >\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top mr-1\"\r\n />\r\n {{ item.label }}\r\n </span>\r\n </NBreadcrumbItem>\r\n </NBreadcrumb>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed } from 'vue'\r\n import { useRoute } from 'vue-router'\r\n import { C_Icon } from '../C_Icon'\r\n import type { BreadcrumbItem } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Breadcrumb' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 手动传入面包屑数据\r\n * 不传时自动从 `route.matched` 生成(零配置)\r\n */\r\n items?: BreadcrumbItem[]\r\n /** 是否显示图标 */\r\n showIcon?: boolean\r\n /**\r\n * 标签文本格式化函数(用于 i18n)\r\n * 仅在自动模式下生效\r\n */\r\n labelFormatter?: (label: string) => string\r\n }>(),\r\n {\r\n showIcon: true,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 面包屑项被选中(返回路径 key) */\r\n select: [path: string]\r\n }>()\r\n\r\n const route = useRoute()\r\n\r\n const formatLabel = (title: string): string =>\r\n props.labelFormatter ? props.labelFormatter(title) : title\r\n\r\n /** 从 route.matched 自动生成面包屑 */\r\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\r\n return route.matched\r\n .filter((record: any) => record.meta?.title)\r\n .map((record: any) => ({\r\n key: record.path,\r\n label: formatLabel((record.meta.title || '') as string),\r\n icon: record.meta.icon as string | undefined,\r\n children: record.children?.length\r\n ? record.children.map((child: any) => ({\r\n key: child.path,\r\n label: formatLabel((child.meta?.title || '') as string),\r\n ...(child.children?.length && {\r\n children: child.children.map((grandChild: any) => ({\r\n key: grandChild.path,\r\n label: formatLabel((grandChild.meta?.title || '') as string),\r\n })),\r\n }),\r\n }))\r\n : [],\r\n }))\r\n })\r\n\r\n const breadcrumbItems = computed<BreadcrumbItem[]>(\r\n () => props.items ?? autoBreadcrumb.value\r\n )\r\n\r\n const handleSelect = (key: string) => {\r\n emit('select', key)\r\n }\r\n</script>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\r\n *\r\n * 支持两种模式:\r\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\r\n * 2. 手动模式 — 传入 `items` 完全自定义\r\n *\r\n * @example 自动模式(零配置)\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 手动模式\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :items=\"customBreadcrumbs\"\r\n * @select=\"handleSelect\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-breadcrumb flex-1 min-w-0\">\r\n <NBreadcrumb>\r\n <NBreadcrumbItem\r\n v-for=\"(item, index) in breadcrumbItems\"\r\n :key=\"index\"\r\n >\r\n <NDropdown\r\n v-if=\"item.children?.length\"\r\n :options=\"item.children\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"trigger\">\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top\"\r\n />\r\n {{ item.label }}\r\n </div>\r\n </NDropdown>\r\n <span\r\n v-else\r\n class=\"c-breadcrumb-link\"\r\n @click=\"handleSelect(item.key)\"\r\n >\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top mr-1\"\r\n />\r\n {{ item.label }}\r\n </span>\r\n </NBreadcrumbItem>\r\n </NBreadcrumb>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed } from 'vue'\r\n import { useRoute } from 'vue-router'\r\n import { C_Icon } from '../C_Icon'\r\n import type { BreadcrumbItem } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Breadcrumb' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 手动传入面包屑数据\r\n * 不传时自动从 `route.matched` 生成(零配置)\r\n */\r\n items?: BreadcrumbItem[]\r\n /** 是否显示图标 */\r\n showIcon?: boolean\r\n /**\r\n * 标签文本格式化函数(用于 i18n)\r\n * 仅在自动模式下生效\r\n */\r\n labelFormatter?: (label: string) => string\r\n }>(),\r\n {\r\n showIcon: true,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 面包屑项被选中(返回路径 key) */\r\n select: [path: string]\r\n }>()\r\n\r\n const route = useRoute()\r\n\r\n const formatLabel = (title: string): string =>\r\n props.labelFormatter ? props.labelFormatter(title) : title\r\n\r\n /** 从 route.matched 自动生成面包屑 */\r\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\r\n return route.matched\r\n .filter((record: any) => record.meta?.title)\r\n .map((record: any) => ({\r\n key: record.path,\r\n label: formatLabel((record.meta.title || '') as string),\r\n icon: record.meta.icon as string | undefined,\r\n children: record.children?.length\r\n ? record.children.map((child: any) => ({\r\n key: child.path,\r\n label: formatLabel((child.meta?.title || '') as string),\r\n ...(child.children?.length && {\r\n children: child.children.map((grandChild: any) => ({\r\n key: grandChild.path,\r\n label: formatLabel((grandChild.meta?.title || '') as string),\r\n })),\r\n }),\r\n }))\r\n : [],\r\n }))\r\n })\r\n\r\n const breadcrumbItems = computed<BreadcrumbItem[]>(\r\n () => props.items ?? autoBreadcrumb.value\r\n )\r\n\r\n const handleSelect = (key: string) => {\r\n emit('select', key)\r\n }\r\n</script>\r\n","<!--\r\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\r\n *\r\n * 支持两种模式:\r\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\r\n * 2. 手动模式 — 传入 `items` 完全自定义\r\n *\r\n * @example 自动模式(零配置)\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 手动模式\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :items=\"customBreadcrumbs\"\r\n * @select=\"handleSelect\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-breadcrumb flex-1 min-w-0\">\r\n <NBreadcrumb>\r\n <NBreadcrumbItem\r\n v-for=\"(item, index) in breadcrumbItems\"\r\n :key=\"index\"\r\n >\r\n <NDropdown\r\n v-if=\"item.children?.length\"\r\n :options=\"item.children\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"trigger\">\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top\"\r\n />\r\n {{ item.label }}\r\n </div>\r\n </NDropdown>\r\n <span\r\n v-else\r\n class=\"c-breadcrumb-link\"\r\n @click=\"handleSelect(item.key)\"\r\n >\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top mr-1\"\r\n />\r\n {{ item.label }}\r\n </span>\r\n </NBreadcrumbItem>\r\n </NBreadcrumb>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed } from 'vue'\r\n import { useRoute } from 'vue-router'\r\n import { C_Icon } from '../C_Icon'\r\n import type { BreadcrumbItem } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Breadcrumb' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 手动传入面包屑数据\r\n * 不传时自动从 `route.matched` 生成(零配置)\r\n */\r\n items?: BreadcrumbItem[]\r\n /** 是否显示图标 */\r\n showIcon?: boolean\r\n /**\r\n * 标签文本格式化函数(用于 i18n)\r\n * 仅在自动模式下生效\r\n */\r\n labelFormatter?: (label: string) => string\r\n }>(),\r\n {\r\n showIcon: true,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 面包屑项被选中(返回路径 key) */\r\n select: [path: string]\r\n }>()\r\n\r\n const route = useRoute()\r\n\r\n const formatLabel = (title: string): string =>\r\n props.labelFormatter ? props.labelFormatter(title) : title\r\n\r\n /** 从 route.matched 自动生成面包屑 */\r\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\r\n return route.matched\r\n .filter((record: any) => record.meta?.title)\r\n .map((record: any) => ({\r\n key: record.path,\r\n label: formatLabel((record.meta.title || '') as string),\r\n icon: record.meta.icon as string | undefined,\r\n children: record.children?.length\r\n ? record.children.map((child: any) => ({\r\n key: child.path,\r\n label: formatLabel((child.meta?.title || '') as string),\r\n ...(child.children?.length && {\r\n children: child.children.map((grandChild: any) => ({\r\n key: grandChild.path,\r\n label: formatLabel((grandChild.meta?.title || '') as string),\r\n })),\r\n }),\r\n }))\r\n : [],\r\n }))\r\n })\r\n\r\n const breadcrumbItems = computed<BreadcrumbItem[]>(\r\n () => props.items ?? autoBreadcrumb.value\r\n )\r\n\r\n const handleSelect = (key: string) => {\r\n emit('select', key)\r\n }\r\n</script>\r\n"],"mappings":"iXEuEE,IAAM,EAAQ,EAoBR,EAAO,EAKP,GAAA,EAAA,EAAA,WAAkB,CAElB,EAAe,GACnB,EAAM,eAAiB,EAAM,eAAe,EAAM,CAAG,EAGjD,GAAA,EAAA,EAAA,cACG,EAAM,QACV,OAAQ,GAAgB,EAAO,MAAM,MAAM,CAC3C,IAAK,IAAiB,CACrB,IAAK,EAAO,KACZ,MAAO,EAAa,EAAO,KAAK,OAAS,GAAc,CACvD,KAAM,EAAO,KAAK,KAClB,SAAU,EAAO,UAAU,OACvB,EAAO,SAAS,IAAK,IAAgB,CACnC,IAAK,EAAM,KACX,MAAO,EAAa,EAAM,MAAM,OAAS,GAAc,CACvD,GAAI,EAAM,UAAU,QAAU,CAC5B,SAAU,EAAM,SAAS,IAAK,IAAqB,CACjD,IAAK,EAAW,KAChB,MAAO,EAAa,EAAW,MAAM,OAAS,GAAc,CAC7D,EAAE,CACJ,CACF,EAAE,CACH,EAAE,CACP,EAAE,CACL,CAEI,GAAA,EAAA,EAAA,cACE,EAAM,OAAS,EAAe,MACrC,CAEK,EAAgB,GAAgB,CACpC,EAAK,SAAU,EAAI,uHArEf,MAlCN,EAkCM,EAAA,EAAA,EAAA,aADU,EAAA,KAAA,2BA9B8B,GAAA,EAAA,EAAA,WAAA,GAAA,EAAA,EAAA,EAAA,oBA6BxB,EAAA,SAAA,MAAA,EAAA,EAAA,YA7BQ,EAAA,OAAhB,EAAM,yCA6BE,EAAA,CA5Bf,IAAK,EAAK,CAAA,2BAkBhB,CAfa,EAAK,UAAU,SAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAYX,EAAA,OAXT,QAAS,EAAK,SACd,SAAQ,8BASH,EAAA,EAAA,EAAA,oBAAA,MAPN,EAOM,CALIA,EAAAA,WAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,cAAAA,EAAAA,EAAAA,OAGN,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,4FACN,KAAA,EAAA,EAAA,iBACC,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,qEAcV,OAAA,OATL,MAAM,oBACL,QAAK,GAAE,EAAa,EAAK,IAAG,GAGrBA,EAAAA,WAAAA,EAAAA,EAAAA,YAAAA,EAAAA,EAAAA,EAAAA,cAAAA,EAAAA,EAAAA,OAGN,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,iGACN,KAAA,EAAA,EAAA,iBACC,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"C_Breadcrumb2.js","names":["showIcon"],"sources":["../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-breadcrumb flex-1 min-w-0\">\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb-link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top mr-1\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-breadcrumb flex-1 min-w-0\">\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb-link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top mr-1\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n","<!--\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\n *\n * 支持两种模式:\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\n * 2. 手动模式 — 传入 `items` 完全自定义\n *\n * @example 自动模式(零配置)\n * ```vue\n * <C_Breadcrumb\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 手动模式\n * ```vue\n * <C_Breadcrumb\n * :items=\"customBreadcrumbs\"\n * @select=\"handleSelect\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-breadcrumb flex-1 min-w-0\">\n <NBreadcrumb>\n <NBreadcrumbItem\n v-for=\"(item, index) in breadcrumbItems\"\n :key=\"index\"\n >\n <NDropdown\n v-if=\"item.children?.length\"\n :options=\"item.children\"\n @select=\"handleSelect\"\n >\n <div class=\"trigger\">\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top\"\n />\n {{ item.label }}\n </div>\n </NDropdown>\n <span\n v-else\n class=\"c-breadcrumb-link\"\n @click=\"handleSelect(item.key)\"\n >\n <C_Icon\n v-if=\"showIcon\"\n :name=\"item.icon\"\n class=\"vertical-top mr-1\"\n />\n {{ item.label }}\n </span>\n </NBreadcrumbItem>\n </NBreadcrumb>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { useRoute } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { BreadcrumbItem } from '../_shared'\n\n defineOptions({ name: 'C_Breadcrumb' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 手动传入面包屑数据\n * 不传时自动从 `route.matched` 生成(零配置)\n */\n items?: BreadcrumbItem[]\n /** 是否显示图标 */\n showIcon?: boolean\n /**\n * 标签文本格式化函数(用于 i18n)\n * 仅在自动模式下生效\n */\n labelFormatter?: (label: string) => string\n }>(),\n {\n showIcon: true,\n }\n )\n\n const emit = defineEmits<{\n /** 面包屑项被选中(返回路径 key) */\n select: [path: string]\n }>()\n\n const route = useRoute()\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n /** 从 route.matched 自动生成面包屑 */\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\n return route.matched\n .filter((record: any) => record.meta?.title)\n .map((record: any) => ({\n key: record.path,\n label: formatLabel((record.meta.title || '') as string),\n icon: record.meta.icon as string | undefined,\n children: record.children?.length\n ? record.children.map((child: any) => ({\n key: child.path,\n label: formatLabel((child.meta?.title || '') as string),\n ...(child.children?.length && {\n children: child.children.map((grandChild: any) => ({\n key: grandChild.path,\n label: formatLabel((grandChild.meta?.title || '') as string),\n })),\n }),\n }))\n : [],\n }))\n })\n\n const breadcrumbItems = computed<BreadcrumbItem[]>(\n () => props.items ?? autoBreadcrumb.value\n )\n\n const handleSelect = (key: string) => {\n emit('select', key)\n }\n</script>\n"],"mappings":"ooBEuEE,IAAM,EAAQ,EAoBR,EAAO,EAKP,EAAQ,GAAS,CAEjB,EAAe,GACnB,EAAM,eAAiB,EAAM,eAAe,EAAM,CAAG,EAGjD,EAAiB,MACd,EAAM,QACV,OAAQ,GAAgB,EAAO,MAAM,MAAK,CAC1C,IAAK,IAAiB,CACrB,IAAK,EAAO,KACZ,MAAO,EAAa,EAAO,KAAK,OAAS,GAAc,CACvD,KAAM,EAAO,KAAK,KAClB,SAAU,EAAO,UAAU,OACvB,EAAO,SAAS,IAAK,IAAgB,CACnC,IAAK,EAAM,KACX,MAAO,EAAa,EAAM,MAAM,OAAS,GAAc,CACvD,GAAI,EAAM,UAAU,QAAU,CAC5B,SAAU,EAAM,SAAS,IAAK,IAAqB,CACjD,IAAK,EAAW,KAChB,MAAO,EAAa,EAAW,MAAM,OAAS,GAAc,CAC7D,EAAE,CACJ,CACF,EAAC,CACF,EAAE,CACP,EAAC,CACL,CAEK,EAAkB,MAChB,EAAM,OAAS,EAAe,MACtC,CAEM,EAAgB,GAAgB,CACpC,EAAK,SAAU,EAAG,2CAvGpB,EAkCM,MAlCN,EAkCM,CAjCJ,EAgCc,EAAA,KAAA,eA9B8B,EAAA,EAAA,GAAA,CAD1C,EA8BkB,EAAA,KAAA,EA7BQ,EAAA,OAAhB,EAAM,SADhB,EA8BkB,EAAA,CA5Bf,IAAK,EAAK,CAAA,eAkBb,CAfU,EAAK,UAAU,QAAA,GAAA,CADvB,EAaY,EAAA,OAXT,QAAS,EAAK,SACd,SAAQ,kBASH,CAPN,EAOM,MAPN,EAOM,CALIA,EAAAA,UAAAA,GAAAA,CADR,EAIE,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,iDACN,IACF,EAAG,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,8BAGjB,EAWO,OAAA,OATL,MAAM,oBACL,QAAK,GAAE,EAAa,EAAK,IAAG,GAGrBA,EAAAA,UAAAA,GAAAA,CADR,EAIE,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,sDACN,IACF,EAAG,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA"}
1
+ {"version":3,"file":"C_Breadcrumb2.js","names":["showIcon"],"sources":["../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue","../src/components/C_Breadcrumb/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\r\n *\r\n * 支持两种模式:\r\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\r\n * 2. 手动模式 — 传入 `items` 完全自定义\r\n *\r\n * @example 自动模式(零配置)\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 手动模式\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :items=\"customBreadcrumbs\"\r\n * @select=\"handleSelect\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-breadcrumb flex-1 min-w-0\">\r\n <NBreadcrumb>\r\n <NBreadcrumbItem\r\n v-for=\"(item, index) in breadcrumbItems\"\r\n :key=\"index\"\r\n >\r\n <NDropdown\r\n v-if=\"item.children?.length\"\r\n :options=\"item.children\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"trigger\">\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top\"\r\n />\r\n {{ item.label }}\r\n </div>\r\n </NDropdown>\r\n <span\r\n v-else\r\n class=\"c-breadcrumb-link\"\r\n @click=\"handleSelect(item.key)\"\r\n >\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top mr-1\"\r\n />\r\n {{ item.label }}\r\n </span>\r\n </NBreadcrumbItem>\r\n </NBreadcrumb>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed } from 'vue'\r\n import { useRoute } from 'vue-router'\r\n import { C_Icon } from '../C_Icon'\r\n import type { BreadcrumbItem } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Breadcrumb' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 手动传入面包屑数据\r\n * 不传时自动从 `route.matched` 生成(零配置)\r\n */\r\n items?: BreadcrumbItem[]\r\n /** 是否显示图标 */\r\n showIcon?: boolean\r\n /**\r\n * 标签文本格式化函数(用于 i18n)\r\n * 仅在自动模式下生效\r\n */\r\n labelFormatter?: (label: string) => string\r\n }>(),\r\n {\r\n showIcon: true,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 面包屑项被选中(返回路径 key) */\r\n select: [path: string]\r\n }>()\r\n\r\n const route = useRoute()\r\n\r\n const formatLabel = (title: string): string =>\r\n props.labelFormatter ? props.labelFormatter(title) : title\r\n\r\n /** 从 route.matched 自动生成面包屑 */\r\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\r\n return route.matched\r\n .filter((record: any) => record.meta?.title)\r\n .map((record: any) => ({\r\n key: record.path,\r\n label: formatLabel((record.meta.title || '') as string),\r\n icon: record.meta.icon as string | undefined,\r\n children: record.children?.length\r\n ? record.children.map((child: any) => ({\r\n key: child.path,\r\n label: formatLabel((child.meta?.title || '') as string),\r\n ...(child.children?.length && {\r\n children: child.children.map((grandChild: any) => ({\r\n key: grandChild.path,\r\n label: formatLabel((grandChild.meta?.title || '') as string),\r\n })),\r\n }),\r\n }))\r\n : [],\r\n }))\r\n })\r\n\r\n const breadcrumbItems = computed<BreadcrumbItem[]>(\r\n () => props.items ?? autoBreadcrumb.value\r\n )\r\n\r\n const handleSelect = (key: string) => {\r\n emit('select', key)\r\n }\r\n</script>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\r\n *\r\n * 支持两种模式:\r\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\r\n * 2. 手动模式 — 传入 `items` 完全自定义\r\n *\r\n * @example 自动模式(零配置)\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 手动模式\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :items=\"customBreadcrumbs\"\r\n * @select=\"handleSelect\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-breadcrumb flex-1 min-w-0\">\r\n <NBreadcrumb>\r\n <NBreadcrumbItem\r\n v-for=\"(item, index) in breadcrumbItems\"\r\n :key=\"index\"\r\n >\r\n <NDropdown\r\n v-if=\"item.children?.length\"\r\n :options=\"item.children\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"trigger\">\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top\"\r\n />\r\n {{ item.label }}\r\n </div>\r\n </NDropdown>\r\n <span\r\n v-else\r\n class=\"c-breadcrumb-link\"\r\n @click=\"handleSelect(item.key)\"\r\n >\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top mr-1\"\r\n />\r\n {{ item.label }}\r\n </span>\r\n </NBreadcrumbItem>\r\n </NBreadcrumb>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed } from 'vue'\r\n import { useRoute } from 'vue-router'\r\n import { C_Icon } from '../C_Icon'\r\n import type { BreadcrumbItem } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Breadcrumb' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 手动传入面包屑数据\r\n * 不传时自动从 `route.matched` 生成(零配置)\r\n */\r\n items?: BreadcrumbItem[]\r\n /** 是否显示图标 */\r\n showIcon?: boolean\r\n /**\r\n * 标签文本格式化函数(用于 i18n)\r\n * 仅在自动模式下生效\r\n */\r\n labelFormatter?: (label: string) => string\r\n }>(),\r\n {\r\n showIcon: true,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 面包屑项被选中(返回路径 key) */\r\n select: [path: string]\r\n }>()\r\n\r\n const route = useRoute()\r\n\r\n const formatLabel = (title: string): string =>\r\n props.labelFormatter ? props.labelFormatter(title) : title\r\n\r\n /** 从 route.matched 自动生成面包屑 */\r\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\r\n return route.matched\r\n .filter((record: any) => record.meta?.title)\r\n .map((record: any) => ({\r\n key: record.path,\r\n label: formatLabel((record.meta.title || '') as string),\r\n icon: record.meta.icon as string | undefined,\r\n children: record.children?.length\r\n ? record.children.map((child: any) => ({\r\n key: child.path,\r\n label: formatLabel((child.meta?.title || '') as string),\r\n ...(child.children?.length && {\r\n children: child.children.map((grandChild: any) => ({\r\n key: grandChild.path,\r\n label: formatLabel((grandChild.meta?.title || '') as string),\r\n })),\r\n }),\r\n }))\r\n : [],\r\n }))\r\n })\r\n\r\n const breadcrumbItems = computed<BreadcrumbItem[]>(\r\n () => props.items ?? autoBreadcrumb.value\r\n )\r\n\r\n const handleSelect = (key: string) => {\r\n emit('select', key)\r\n }\r\n</script>\r\n","<!--\r\n * @Description: 通用面包屑组件 — 基于 NBreadcrumb 的路由面包屑导航\r\n *\r\n * 支持两种模式:\r\n * 1. 自动模式(默认)— 从 `route.matched` 自动生成面包屑\r\n * 2. 手动模式 — 传入 `items` 完全自定义\r\n *\r\n * @example 自动模式(零配置)\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 手动模式\r\n * ```vue\r\n * <C_Breadcrumb\r\n * :items=\"customBreadcrumbs\"\r\n * @select=\"handleSelect\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div class=\"c-breadcrumb flex-1 min-w-0\">\r\n <NBreadcrumb>\r\n <NBreadcrumbItem\r\n v-for=\"(item, index) in breadcrumbItems\"\r\n :key=\"index\"\r\n >\r\n <NDropdown\r\n v-if=\"item.children?.length\"\r\n :options=\"item.children\"\r\n @select=\"handleSelect\"\r\n >\r\n <div class=\"trigger\">\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top\"\r\n />\r\n {{ item.label }}\r\n </div>\r\n </NDropdown>\r\n <span\r\n v-else\r\n class=\"c-breadcrumb-link\"\r\n @click=\"handleSelect(item.key)\"\r\n >\r\n <C_Icon\r\n v-if=\"showIcon\"\r\n :name=\"item.icon\"\r\n class=\"vertical-top mr-1\"\r\n />\r\n {{ item.label }}\r\n </span>\r\n </NBreadcrumbItem>\r\n </NBreadcrumb>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { computed } from 'vue'\r\n import { useRoute } from 'vue-router'\r\n import { C_Icon } from '../C_Icon'\r\n import type { BreadcrumbItem } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Breadcrumb' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 手动传入面包屑数据\r\n * 不传时自动从 `route.matched` 生成(零配置)\r\n */\r\n items?: BreadcrumbItem[]\r\n /** 是否显示图标 */\r\n showIcon?: boolean\r\n /**\r\n * 标签文本格式化函数(用于 i18n)\r\n * 仅在自动模式下生效\r\n */\r\n labelFormatter?: (label: string) => string\r\n }>(),\r\n {\r\n showIcon: true,\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 面包屑项被选中(返回路径 key) */\r\n select: [path: string]\r\n }>()\r\n\r\n const route = useRoute()\r\n\r\n const formatLabel = (title: string): string =>\r\n props.labelFormatter ? props.labelFormatter(title) : title\r\n\r\n /** 从 route.matched 自动生成面包屑 */\r\n const autoBreadcrumb = computed<BreadcrumbItem[]>(() => {\r\n return route.matched\r\n .filter((record: any) => record.meta?.title)\r\n .map((record: any) => ({\r\n key: record.path,\r\n label: formatLabel((record.meta.title || '') as string),\r\n icon: record.meta.icon as string | undefined,\r\n children: record.children?.length\r\n ? record.children.map((child: any) => ({\r\n key: child.path,\r\n label: formatLabel((child.meta?.title || '') as string),\r\n ...(child.children?.length && {\r\n children: child.children.map((grandChild: any) => ({\r\n key: grandChild.path,\r\n label: formatLabel((grandChild.meta?.title || '') as string),\r\n })),\r\n }),\r\n }))\r\n : [],\r\n }))\r\n })\r\n\r\n const breadcrumbItems = computed<BreadcrumbItem[]>(\r\n () => props.items ?? autoBreadcrumb.value\r\n )\r\n\r\n const handleSelect = (key: string) => {\r\n emit('select', key)\r\n }\r\n</script>\r\n"],"mappings":"ooBEuEE,IAAM,EAAQ,EAoBR,EAAO,EAKP,EAAQ,GAAU,CAElB,EAAe,GACnB,EAAM,eAAiB,EAAM,eAAe,EAAM,CAAG,EAGjD,EAAiB,MACd,EAAM,QACV,OAAQ,GAAgB,EAAO,MAAM,MAAM,CAC3C,IAAK,IAAiB,CACrB,IAAK,EAAO,KACZ,MAAO,EAAa,EAAO,KAAK,OAAS,GAAc,CACvD,KAAM,EAAO,KAAK,KAClB,SAAU,EAAO,UAAU,OACvB,EAAO,SAAS,IAAK,IAAgB,CACnC,IAAK,EAAM,KACX,MAAO,EAAa,EAAM,MAAM,OAAS,GAAc,CACvD,GAAI,EAAM,UAAU,QAAU,CAC5B,SAAU,EAAM,SAAS,IAAK,IAAqB,CACjD,IAAK,EAAW,KAChB,MAAO,EAAa,EAAW,MAAM,OAAS,GAAc,CAC7D,EAAE,CACJ,CACF,EAAE,CACH,EAAE,CACP,EAAE,CACL,CAEI,EAAkB,MAChB,EAAM,OAAS,EAAe,MACrC,CAEK,EAAgB,GAAgB,CACpC,EAAK,SAAU,EAAI,2CAvGrB,EAkCM,MAlCN,EAkCM,CAjCJ,EAgCc,EAAA,KAAA,eA9B8B,EAAA,EAAA,GAAA,CAD1C,EA8BkB,EAAA,KAAA,EA7BQ,EAAA,OAAhB,EAAM,SADhB,EA8BkB,EAAA,CA5Bf,IAAK,EAAK,CAAA,eAkBhB,CAfa,EAAK,UAAU,QAAA,GAAA,CADvB,EAaY,EAAA,OAXT,QAAS,EAAK,SACd,SAAQ,kBASH,CAPN,EAOM,MAPN,EAOM,CALIA,EAAAA,UAAAA,GAAAA,CADR,EAIE,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,iDACN,IACF,EAAG,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,CAAA,CAAA,8BAGjB,EAWO,OAAA,OATL,MAAM,oBACL,QAAK,GAAE,EAAa,EAAK,IAAG,GAGrBA,EAAAA,UAAAA,GAAAA,CADR,EAIE,EAAA,EAAA,CAAA,OAFC,KAAM,EAAK,KACZ,MAAM,sDACN,IACF,EAAG,EAAK,MAAK,CAAA,EAAA,CAAA,CAAA,EAAA,EAAA,EAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"C_Menu.js","names":["mode","collapsed","collapsedWidth","collapsedIconSize","inverted","themeOverrides","dropdownProps","indent","rootIndent"],"sources":["../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\n *\n * 支持两种数据输入模式:\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\n *\n * @example 路由模式(推荐)\n * ```vue\n * <C_Menu\n * :routes=\"menuData\"\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 原生模式\n * ```vue\n * <C_Menu\n * :options=\"nMenuOptions\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NMenu\n ref=\"menuRef\"\n :options=\"mergedOptions\"\n :expanded-keys=\"expandedKeys\"\n :value=\"activeKey\"\n :mode=\"mode\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"collapsedWidth\"\n :collapsed-icon-size=\"collapsedIconSize\"\n :inverted=\"inverted\"\n :theme-overrides=\"themeOverrides\"\n :dropdown-props=\"dropdownProps\"\n :indent=\"indent\"\n :root-indent=\"rootIndent\"\n @update:value=\"handleMenuClick\"\n @update:expanded-keys=\"onExpandedKeysChange\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\n import {\n type MenuOption,\n type MenuInst,\n type DropdownProps,\n } from 'naive-ui/es'\n import type { RouteItem, MenuAdapterConfig } from '../_shared'\n import { createMenuOptions } from '../_shared'\n\n defineOptions({ name: 'C_Menu' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 模式一:直传 NMenu 原生 MenuOption[]\n * 与 `routes` 二选一,`options` 优先级更高\n */\n options?: MenuOption[]\n /**\n * 模式二:传入路由数据,自动转换为 MenuOption[]\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\n */\n routes?: RouteItem[]\n /**\n * 适配器配置(仅 routes 模式生效)\n * 可自定义 key/label/icon 取值、过滤逻辑等\n */\n adapterConfig?: MenuAdapterConfig\n /**\n * 标签文本格式化函数(routes 模式的快捷方式)\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\n */\n labelFormatter?: (label: string) => string\n /** 当前激活的菜单 key */\n value?: string\n /** 菜单模式 */\n mode?: 'vertical' | 'horizontal'\n /** 是否折叠 */\n collapsed?: boolean\n /** 折叠宽度 */\n collapsedWidth?: number\n /** 折叠图标大小 */\n collapsedIconSize?: number\n /** 是否反色 */\n inverted?: boolean\n /** 主题覆盖 */\n themeOverrides?: Record<string, any>\n /** 缩进像素 */\n indent?: number\n /** 根缩进像素 */\n rootIndent?: number\n /** 下拉菜单属性(折叠模式) */\n dropdownProps?: DropdownProps\n }>(),\n {\n mode: 'vertical',\n collapsed: false,\n collapsedWidth: 64,\n collapsedIconSize: 22,\n inverted: false,\n indent: 24,\n rootIndent: 16,\n dropdownProps: () => ({\n placement: 'right-start' as const,\n trigger: 'hover' as const,\n arrowStyle: { color: 'var(--n-color)' },\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 菜单项被选中 */\n select: [key: string]\n /** 展开项变化 */\n 'update:expandedKeys': [keys: string[]]\n }>()\n\n const menuRef = ref<MenuInst | null>(null)\n const expandedKeys = ref<string[]>([])\n\n const activeKey = computed(() => props.value ?? '')\n\n // ====== 数据合成 ======\n\n /** 合并后的最终 MenuOption[] */\n const mergedOptions = computed<MenuOption[]>(() => {\n // 优先使用 options 直传模式\n if (props.options) return props.options\n\n // routes 模式 → 适配器转换\n if (props.routes) {\n const config: MenuAdapterConfig = {\n ...props.adapterConfig,\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\n }\n return createMenuOptions(props.routes, config)\n }\n\n return []\n })\n\n // ====== 展开逻辑 ======\n\n /** 从 MenuOption[] 中查找所有祖先 key */\n const findParentKeys = (\n items: MenuOption[],\n targetKey: string,\n parentKeys: string[] = []\n ): string[] => {\n for (const item of items) {\n const children = item.children as MenuOption[] | undefined\n if (children?.length) {\n const currentKeys = [...parentKeys, item.key as string]\n if (children.some(child => child.key === targetKey)) {\n return currentKeys\n }\n const result = findParentKeys(children, targetKey, currentKeys)\n if (result.length > 0) return result\n }\n }\n return []\n }\n\n /** 通过路径段推导展开 key + 查找祖先 key */\n const computeExpandedKeys = (path: string): Set<string> => {\n const segments = path.split('/').filter(Boolean)\n const keys = new Set<string>()\n let currentPath = ''\n segments.forEach(segment => {\n currentPath += `/${segment}`\n keys.add(currentPath)\n })\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\n return keys\n }\n\n // ====== 事件处理 ======\n\n const handleMenuClick = (key: string) => {\n if (key && key !== activeKey.value) {\n emit('select', key)\n }\n }\n\n const onExpandedKeysChange = (keys: string[]) => {\n expandedKeys.value = keys\n emit('update:expandedKeys', keys)\n }\n\n // ====== 暴露方法 ======\n const showOption = (key: string) => menuRef.value?.showOption(key)\n defineExpose({ showOption, menuRef })\n\n // ====== 路由跟踪 ======\n\n watch(\n () => activeKey.value,\n newKey => {\n if (!newKey) return\n const newKeys = computeExpandedKeys(newKey)\n expandedKeys.value = Array.from(\n new Set([...expandedKeys.value, ...newKeys])\n )\n nextTick(() => menuRef.value?.showOption(newKey))\n },\n { immediate: true }\n )\n\n onMounted(() => {\n nextTick(() => {\n if (activeKey.value) {\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\n menuRef.value?.showOption(activeKey.value)\n }\n })\n })\n</script>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\n *\n * 支持两种数据输入模式:\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\n *\n * @example 路由模式(推荐)\n * ```vue\n * <C_Menu\n * :routes=\"menuData\"\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 原生模式\n * ```vue\n * <C_Menu\n * :options=\"nMenuOptions\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NMenu\n ref=\"menuRef\"\n :options=\"mergedOptions\"\n :expanded-keys=\"expandedKeys\"\n :value=\"activeKey\"\n :mode=\"mode\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"collapsedWidth\"\n :collapsed-icon-size=\"collapsedIconSize\"\n :inverted=\"inverted\"\n :theme-overrides=\"themeOverrides\"\n :dropdown-props=\"dropdownProps\"\n :indent=\"indent\"\n :root-indent=\"rootIndent\"\n @update:value=\"handleMenuClick\"\n @update:expanded-keys=\"onExpandedKeysChange\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\n import {\n type MenuOption,\n type MenuInst,\n type DropdownProps,\n } from 'naive-ui/es'\n import type { RouteItem, MenuAdapterConfig } from '../_shared'\n import { createMenuOptions } from '../_shared'\n\n defineOptions({ name: 'C_Menu' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 模式一:直传 NMenu 原生 MenuOption[]\n * 与 `routes` 二选一,`options` 优先级更高\n */\n options?: MenuOption[]\n /**\n * 模式二:传入路由数据,自动转换为 MenuOption[]\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\n */\n routes?: RouteItem[]\n /**\n * 适配器配置(仅 routes 模式生效)\n * 可自定义 key/label/icon 取值、过滤逻辑等\n */\n adapterConfig?: MenuAdapterConfig\n /**\n * 标签文本格式化函数(routes 模式的快捷方式)\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\n */\n labelFormatter?: (label: string) => string\n /** 当前激活的菜单 key */\n value?: string\n /** 菜单模式 */\n mode?: 'vertical' | 'horizontal'\n /** 是否折叠 */\n collapsed?: boolean\n /** 折叠宽度 */\n collapsedWidth?: number\n /** 折叠图标大小 */\n collapsedIconSize?: number\n /** 是否反色 */\n inverted?: boolean\n /** 主题覆盖 */\n themeOverrides?: Record<string, any>\n /** 缩进像素 */\n indent?: number\n /** 根缩进像素 */\n rootIndent?: number\n /** 下拉菜单属性(折叠模式) */\n dropdownProps?: DropdownProps\n }>(),\n {\n mode: 'vertical',\n collapsed: false,\n collapsedWidth: 64,\n collapsedIconSize: 22,\n inverted: false,\n indent: 24,\n rootIndent: 16,\n dropdownProps: () => ({\n placement: 'right-start' as const,\n trigger: 'hover' as const,\n arrowStyle: { color: 'var(--n-color)' },\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 菜单项被选中 */\n select: [key: string]\n /** 展开项变化 */\n 'update:expandedKeys': [keys: string[]]\n }>()\n\n const menuRef = ref<MenuInst | null>(null)\n const expandedKeys = ref<string[]>([])\n\n const activeKey = computed(() => props.value ?? '')\n\n // ====== 数据合成 ======\n\n /** 合并后的最终 MenuOption[] */\n const mergedOptions = computed<MenuOption[]>(() => {\n // 优先使用 options 直传模式\n if (props.options) return props.options\n\n // routes 模式 → 适配器转换\n if (props.routes) {\n const config: MenuAdapterConfig = {\n ...props.adapterConfig,\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\n }\n return createMenuOptions(props.routes, config)\n }\n\n return []\n })\n\n // ====== 展开逻辑 ======\n\n /** 从 MenuOption[] 中查找所有祖先 key */\n const findParentKeys = (\n items: MenuOption[],\n targetKey: string,\n parentKeys: string[] = []\n ): string[] => {\n for (const item of items) {\n const children = item.children as MenuOption[] | undefined\n if (children?.length) {\n const currentKeys = [...parentKeys, item.key as string]\n if (children.some(child => child.key === targetKey)) {\n return currentKeys\n }\n const result = findParentKeys(children, targetKey, currentKeys)\n if (result.length > 0) return result\n }\n }\n return []\n }\n\n /** 通过路径段推导展开 key + 查找祖先 key */\n const computeExpandedKeys = (path: string): Set<string> => {\n const segments = path.split('/').filter(Boolean)\n const keys = new Set<string>()\n let currentPath = ''\n segments.forEach(segment => {\n currentPath += `/${segment}`\n keys.add(currentPath)\n })\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\n return keys\n }\n\n // ====== 事件处理 ======\n\n const handleMenuClick = (key: string) => {\n if (key && key !== activeKey.value) {\n emit('select', key)\n }\n }\n\n const onExpandedKeysChange = (keys: string[]) => {\n expandedKeys.value = keys\n emit('update:expandedKeys', keys)\n }\n\n // ====== 暴露方法 ======\n const showOption = (key: string) => menuRef.value?.showOption(key)\n defineExpose({ showOption, menuRef })\n\n // ====== 路由跟踪 ======\n\n watch(\n () => activeKey.value,\n newKey => {\n if (!newKey) return\n const newKeys = computeExpandedKeys(newKey)\n expandedKeys.value = Array.from(\n new Set([...expandedKeys.value, ...newKeys])\n )\n nextTick(() => menuRef.value?.showOption(newKey))\n },\n { immediate: true }\n )\n\n onMounted(() => {\n nextTick(() => {\n if (activeKey.value) {\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\n menuRef.value?.showOption(activeKey.value)\n }\n })\n })\n</script>\n","<!--\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\n *\n * 支持两种数据输入模式:\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\n *\n * @example 路由模式(推荐)\n * ```vue\n * <C_Menu\n * :routes=\"menuData\"\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 原生模式\n * ```vue\n * <C_Menu\n * :options=\"nMenuOptions\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NMenu\n ref=\"menuRef\"\n :options=\"mergedOptions\"\n :expanded-keys=\"expandedKeys\"\n :value=\"activeKey\"\n :mode=\"mode\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"collapsedWidth\"\n :collapsed-icon-size=\"collapsedIconSize\"\n :inverted=\"inverted\"\n :theme-overrides=\"themeOverrides\"\n :dropdown-props=\"dropdownProps\"\n :indent=\"indent\"\n :root-indent=\"rootIndent\"\n @update:value=\"handleMenuClick\"\n @update:expanded-keys=\"onExpandedKeysChange\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\n import {\n type MenuOption,\n type MenuInst,\n type DropdownProps,\n } from 'naive-ui/es'\n import type { RouteItem, MenuAdapterConfig } from '../_shared'\n import { createMenuOptions } from '../_shared'\n\n defineOptions({ name: 'C_Menu' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 模式一:直传 NMenu 原生 MenuOption[]\n * 与 `routes` 二选一,`options` 优先级更高\n */\n options?: MenuOption[]\n /**\n * 模式二:传入路由数据,自动转换为 MenuOption[]\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\n */\n routes?: RouteItem[]\n /**\n * 适配器配置(仅 routes 模式生效)\n * 可自定义 key/label/icon 取值、过滤逻辑等\n */\n adapterConfig?: MenuAdapterConfig\n /**\n * 标签文本格式化函数(routes 模式的快捷方式)\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\n */\n labelFormatter?: (label: string) => string\n /** 当前激活的菜单 key */\n value?: string\n /** 菜单模式 */\n mode?: 'vertical' | 'horizontal'\n /** 是否折叠 */\n collapsed?: boolean\n /** 折叠宽度 */\n collapsedWidth?: number\n /** 折叠图标大小 */\n collapsedIconSize?: number\n /** 是否反色 */\n inverted?: boolean\n /** 主题覆盖 */\n themeOverrides?: Record<string, any>\n /** 缩进像素 */\n indent?: number\n /** 根缩进像素 */\n rootIndent?: number\n /** 下拉菜单属性(折叠模式) */\n dropdownProps?: DropdownProps\n }>(),\n {\n mode: 'vertical',\n collapsed: false,\n collapsedWidth: 64,\n collapsedIconSize: 22,\n inverted: false,\n indent: 24,\n rootIndent: 16,\n dropdownProps: () => ({\n placement: 'right-start' as const,\n trigger: 'hover' as const,\n arrowStyle: { color: 'var(--n-color)' },\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 菜单项被选中 */\n select: [key: string]\n /** 展开项变化 */\n 'update:expandedKeys': [keys: string[]]\n }>()\n\n const menuRef = ref<MenuInst | null>(null)\n const expandedKeys = ref<string[]>([])\n\n const activeKey = computed(() => props.value ?? '')\n\n // ====== 数据合成 ======\n\n /** 合并后的最终 MenuOption[] */\n const mergedOptions = computed<MenuOption[]>(() => {\n // 优先使用 options 直传模式\n if (props.options) return props.options\n\n // routes 模式 → 适配器转换\n if (props.routes) {\n const config: MenuAdapterConfig = {\n ...props.adapterConfig,\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\n }\n return createMenuOptions(props.routes, config)\n }\n\n return []\n })\n\n // ====== 展开逻辑 ======\n\n /** 从 MenuOption[] 中查找所有祖先 key */\n const findParentKeys = (\n items: MenuOption[],\n targetKey: string,\n parentKeys: string[] = []\n ): string[] => {\n for (const item of items) {\n const children = item.children as MenuOption[] | undefined\n if (children?.length) {\n const currentKeys = [...parentKeys, item.key as string]\n if (children.some(child => child.key === targetKey)) {\n return currentKeys\n }\n const result = findParentKeys(children, targetKey, currentKeys)\n if (result.length > 0) return result\n }\n }\n return []\n }\n\n /** 通过路径段推导展开 key + 查找祖先 key */\n const computeExpandedKeys = (path: string): Set<string> => {\n const segments = path.split('/').filter(Boolean)\n const keys = new Set<string>()\n let currentPath = ''\n segments.forEach(segment => {\n currentPath += `/${segment}`\n keys.add(currentPath)\n })\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\n return keys\n }\n\n // ====== 事件处理 ======\n\n const handleMenuClick = (key: string) => {\n if (key && key !== activeKey.value) {\n emit('select', key)\n }\n }\n\n const onExpandedKeysChange = (keys: string[]) => {\n expandedKeys.value = keys\n emit('update:expandedKeys', keys)\n }\n\n // ====== 暴露方法 ======\n const showOption = (key: string) => menuRef.value?.showOption(key)\n defineExpose({ showOption, menuRef })\n\n // ====== 路由跟踪 ======\n\n watch(\n () => activeKey.value,\n newKey => {\n if (!newKey) return\n const newKeys = computeExpandedKeys(newKey)\n expandedKeys.value = Array.from(\n new Set([...expandedKeys.value, ...newKeys])\n )\n nextTick(() => menuRef.value?.showOption(newKey))\n },\n { immediate: true }\n )\n\n onMounted(() => {\n nextTick(() => {\n if (activeKey.value) {\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\n menuRef.value?.showOption(activeKey.value)\n }\n })\n })\n</script>\n"],"mappings":"gnBE0DE,IAAM,EAAQ,EA2DR,EAAO,EAOP,GAAA,EAAA,EAAA,KAA+B,KAAI,CACnC,GAAA,EAAA,EAAA,KAA6B,EAAE,CAAA,CAE/B,GAAA,EAAA,EAAA,cAA2B,EAAM,OAAS,GAAE,CAK5C,GAAA,EAAA,EAAA,cAA6C,CAEjD,GAAI,EAAM,QAAS,OAAO,EAAM,QAGhC,GAAI,EAAM,OAAQ,CAChB,IAAM,EAA4B,CAChC,GAAG,EAAM,cACT,GAAI,EAAM,gBAAkB,CAAE,eAAgB,EAAM,eAAgB,CACtE,CACA,OAAO,EAAA,EAAkB,EAAM,OAAQ,EAAM,CAG/C,MAAO,EAAC,EACT,CAKK,GACJ,EACA,EACA,EAAuB,EAAC,GACX,CACb,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAW,EAAK,SACtB,GAAI,GAAU,OAAQ,CACpB,IAAM,EAAc,CAAC,GAAG,EAAY,EAAK,IAAa,CACtD,GAAI,EAAS,KAAK,GAAS,EAAM,MAAQ,EAAU,CACjD,OAAO,EAET,IAAM,EAAS,EAAe,EAAU,EAAW,EAAW,CAC9D,GAAI,EAAO,OAAS,EAAG,OAAO,GAGlC,MAAO,EAAC,EAIJ,EAAuB,GAA8B,CACzD,IAAM,EAAW,EAAK,MAAM,IAAI,CAAC,OAAO,QAAO,CACzC,EAAO,IAAI,IACb,EAAc,GAMlB,OALA,EAAS,QAAQ,GAAW,CAC1B,GAAe,IAAI,IACnB,EAAK,IAAI,EAAW,EACrB,CACD,EAAe,EAAc,MAAO,EAAK,CAAC,QAAQ,GAAO,EAAK,IAAI,EAAI,CAAA,CAC/D,GAKH,EAAmB,GAAgB,CACnC,GAAO,IAAQ,EAAU,OAC3B,EAAK,SAAU,EAAG,EAIhB,EAAwB,GAAmB,CAC/C,EAAa,MAAQ,EACrB,EAAK,sBAAuB,EAAI,SAKlC,EAAa,CAAE,WADK,GAAgB,EAAQ,OAAO,WAAW,EAAG,CACtC,UAAS,CAAA,EAIpC,EAAA,EAAA,WACQ,EAAU,MAChB,GAAU,CACR,GAAI,CAAC,EAAQ,OACb,IAAM,EAAU,EAAoB,EAAM,CAC1C,EAAa,MAAQ,MAAM,KACzB,IAAI,IAAI,CAAC,GAAG,EAAa,MAAO,GAAG,EAAQ,CAAA,CAC7C,EACA,EAAA,EAAA,cAAe,EAAQ,OAAO,WAAW,EAAO,CAAA,EAElD,CAAE,UAAW,GAAK,CACpB,EAEA,EAAA,EAAA,eAAgB,EACd,EAAA,EAAA,cAAe,CACT,EAAU,QACZ,EAAa,MAAQ,MAAM,KAAK,EAAoB,EAAU,MAAM,CAAA,CACpE,EAAQ,OAAO,WAAW,EAAU,MAAK,GAE5C,EACF,iEAnLC,EAAA,SAfI,UAAJ,IAAI,EACH,QAAS,EAAA,MACT,gBAAe,EAAA,MACf,MAAO,EAAA,MACP,KAAMA,EAAAA,KACN,UAAWC,EAAAA,UACX,kBAAiBC,EAAAA,eACjB,sBAAqBC,EAAAA,kBACrB,SAAUC,EAAAA,SACV,kBAAiBC,EAAAA,eACjB,iBAAgBC,EAAAA,cAChB,OAAQC,EAAAA,OACR,cAAaC,EAAAA,WACb,iBAAc,EACd,wBAAsB"}
1
+ {"version":3,"file":"C_Menu.js","names":["mode","collapsed","collapsedWidth","collapsedIconSize","inverted","themeOverrides","dropdownProps","indent","rootIndent"],"sources":["../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\r\n *\r\n * 支持两种数据输入模式:\r\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\r\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\r\n *\r\n * @example 路由模式(推荐)\r\n * ```vue\r\n * <C_Menu\r\n * :routes=\"menuData\"\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 原生模式\r\n * ```vue\r\n * <C_Menu\r\n * :options=\"nMenuOptions\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NMenu\r\n ref=\"menuRef\"\r\n :options=\"mergedOptions\"\r\n :expanded-keys=\"expandedKeys\"\r\n :value=\"activeKey\"\r\n :mode=\"mode\"\r\n :collapsed=\"collapsed\"\r\n :collapsed-width=\"collapsedWidth\"\r\n :collapsed-icon-size=\"collapsedIconSize\"\r\n :inverted=\"inverted\"\r\n :theme-overrides=\"themeOverrides\"\r\n :dropdown-props=\"dropdownProps\"\r\n :indent=\"indent\"\r\n :root-indent=\"rootIndent\"\r\n @update:value=\"handleMenuClick\"\r\n @update:expanded-keys=\"onExpandedKeysChange\"\r\n />\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\r\n import {\r\n type MenuOption,\r\n type MenuInst,\r\n type DropdownProps,\r\n } from 'naive-ui/es'\r\n import {\r\n createMenuOptions,\r\n type RouteItem,\r\n type MenuAdapterConfig,\r\n } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Menu' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 模式一:直传 NMenu 原生 MenuOption[]\r\n * 与 `routes` 二选一,`options` 优先级更高\r\n */\r\n options?: MenuOption[]\r\n /**\r\n * 模式二:传入路由数据,自动转换为 MenuOption[]\r\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\r\n */\r\n routes?: RouteItem[]\r\n /**\r\n * 适配器配置(仅 routes 模式生效)\r\n * 可自定义 key/label/icon 取值、过滤逻辑等\r\n */\r\n adapterConfig?: MenuAdapterConfig\r\n /**\r\n * 标签文本格式化函数(routes 模式的快捷方式)\r\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\r\n */\r\n labelFormatter?: (label: string) => string\r\n /** 当前激活的菜单 key */\r\n value?: string\r\n /** 菜单模式 */\r\n mode?: 'vertical' | 'horizontal'\r\n /** 是否折叠 */\r\n collapsed?: boolean\r\n /** 折叠宽度 */\r\n collapsedWidth?: number\r\n /** 折叠图标大小 */\r\n collapsedIconSize?: number\r\n /** 是否反色 */\r\n inverted?: boolean\r\n /** 主题覆盖 */\r\n themeOverrides?: Record<string, any>\r\n /** 缩进像素 */\r\n indent?: number\r\n /** 根缩进像素 */\r\n rootIndent?: number\r\n /** 下拉菜单属性(折叠模式) */\r\n dropdownProps?: DropdownProps\r\n }>(),\r\n {\r\n mode: 'vertical',\r\n collapsed: false,\r\n collapsedWidth: 64,\r\n collapsedIconSize: 22,\r\n inverted: false,\r\n indent: 24,\r\n rootIndent: 16,\r\n dropdownProps: () => ({\r\n placement: 'right-start' as const,\r\n trigger: 'hover' as const,\r\n arrowStyle: { color: 'var(--n-color)' },\r\n }),\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 菜单项被选中 */\r\n select: [key: string]\r\n /** 展开项变化 */\r\n 'update:expandedKeys': [keys: string[]]\r\n }>()\r\n\r\n const menuRef = ref<MenuInst | null>(null)\r\n const expandedKeys = ref<string[]>([])\r\n\r\n const activeKey = computed(() => props.value ?? '')\r\n\r\n // ====== 数据合成 ======\r\n\r\n /** 合并后的最终 MenuOption[] */\r\n const mergedOptions = computed<MenuOption[]>(() => {\r\n // 优先使用 options 直传模式\r\n if (props.options) return props.options\r\n\r\n // routes 模式 → 适配器转换\r\n if (props.routes) {\r\n const config: MenuAdapterConfig = {\r\n ...props.adapterConfig,\r\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\r\n }\r\n return createMenuOptions(props.routes, config)\r\n }\r\n\r\n return []\r\n })\r\n\r\n // ====== 展开逻辑 ======\r\n\r\n /** 从 MenuOption[] 中查找所有祖先 key */\r\n const findParentKeys = (\r\n items: MenuOption[],\r\n targetKey: string,\r\n parentKeys: string[] = []\r\n ): string[] => {\r\n for (const item of items) {\r\n const children = item.children as MenuOption[] | undefined\r\n if (children?.length) {\r\n const currentKeys = [...parentKeys, item.key as string]\r\n if (children.some(child => child.key === targetKey)) {\r\n return currentKeys\r\n }\r\n const result = findParentKeys(children, targetKey, currentKeys)\r\n if (result.length > 0) return result\r\n }\r\n }\r\n return []\r\n }\r\n\r\n /** 通过路径段推导展开 key + 查找祖先 key */\r\n const computeExpandedKeys = (path: string): Set<string> => {\r\n const segments = path.split('/').filter(Boolean)\r\n const keys = new Set<string>()\r\n let currentPath = ''\r\n segments.forEach(segment => {\r\n currentPath += `/${segment}`\r\n keys.add(currentPath)\r\n })\r\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\r\n return keys\r\n }\r\n\r\n // ====== 事件处理 ======\r\n\r\n const handleMenuClick = (key: string) => {\r\n if (key && key !== activeKey.value) {\r\n emit('select', key)\r\n }\r\n }\r\n\r\n const onExpandedKeysChange = (keys: string[]) => {\r\n expandedKeys.value = keys\r\n emit('update:expandedKeys', keys)\r\n }\r\n\r\n // ====== 暴露方法 ======\r\n const showOption = (key: string) => menuRef.value?.showOption(key)\r\n defineExpose({ showOption, menuRef })\r\n\r\n // ====== 路由跟踪 ======\r\n\r\n watch(\r\n () => activeKey.value,\r\n newKey => {\r\n if (!newKey) return\r\n const newKeys = computeExpandedKeys(newKey)\r\n expandedKeys.value = Array.from(\r\n new Set([...expandedKeys.value, ...newKeys])\r\n )\r\n nextTick(() => menuRef.value?.showOption(newKey))\r\n },\r\n { immediate: true }\r\n )\r\n\r\n onMounted(() => {\r\n nextTick(() => {\r\n if (activeKey.value) {\r\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\r\n menuRef.value?.showOption(activeKey.value)\r\n }\r\n })\r\n })\r\n</script>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\r\n *\r\n * 支持两种数据输入模式:\r\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\r\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\r\n *\r\n * @example 路由模式(推荐)\r\n * ```vue\r\n * <C_Menu\r\n * :routes=\"menuData\"\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 原生模式\r\n * ```vue\r\n * <C_Menu\r\n * :options=\"nMenuOptions\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NMenu\r\n ref=\"menuRef\"\r\n :options=\"mergedOptions\"\r\n :expanded-keys=\"expandedKeys\"\r\n :value=\"activeKey\"\r\n :mode=\"mode\"\r\n :collapsed=\"collapsed\"\r\n :collapsed-width=\"collapsedWidth\"\r\n :collapsed-icon-size=\"collapsedIconSize\"\r\n :inverted=\"inverted\"\r\n :theme-overrides=\"themeOverrides\"\r\n :dropdown-props=\"dropdownProps\"\r\n :indent=\"indent\"\r\n :root-indent=\"rootIndent\"\r\n @update:value=\"handleMenuClick\"\r\n @update:expanded-keys=\"onExpandedKeysChange\"\r\n />\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\r\n import {\r\n type MenuOption,\r\n type MenuInst,\r\n type DropdownProps,\r\n } from 'naive-ui/es'\r\n import {\r\n createMenuOptions,\r\n type RouteItem,\r\n type MenuAdapterConfig,\r\n } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Menu' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 模式一:直传 NMenu 原生 MenuOption[]\r\n * 与 `routes` 二选一,`options` 优先级更高\r\n */\r\n options?: MenuOption[]\r\n /**\r\n * 模式二:传入路由数据,自动转换为 MenuOption[]\r\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\r\n */\r\n routes?: RouteItem[]\r\n /**\r\n * 适配器配置(仅 routes 模式生效)\r\n * 可自定义 key/label/icon 取值、过滤逻辑等\r\n */\r\n adapterConfig?: MenuAdapterConfig\r\n /**\r\n * 标签文本格式化函数(routes 模式的快捷方式)\r\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\r\n */\r\n labelFormatter?: (label: string) => string\r\n /** 当前激活的菜单 key */\r\n value?: string\r\n /** 菜单模式 */\r\n mode?: 'vertical' | 'horizontal'\r\n /** 是否折叠 */\r\n collapsed?: boolean\r\n /** 折叠宽度 */\r\n collapsedWidth?: number\r\n /** 折叠图标大小 */\r\n collapsedIconSize?: number\r\n /** 是否反色 */\r\n inverted?: boolean\r\n /** 主题覆盖 */\r\n themeOverrides?: Record<string, any>\r\n /** 缩进像素 */\r\n indent?: number\r\n /** 根缩进像素 */\r\n rootIndent?: number\r\n /** 下拉菜单属性(折叠模式) */\r\n dropdownProps?: DropdownProps\r\n }>(),\r\n {\r\n mode: 'vertical',\r\n collapsed: false,\r\n collapsedWidth: 64,\r\n collapsedIconSize: 22,\r\n inverted: false,\r\n indent: 24,\r\n rootIndent: 16,\r\n dropdownProps: () => ({\r\n placement: 'right-start' as const,\r\n trigger: 'hover' as const,\r\n arrowStyle: { color: 'var(--n-color)' },\r\n }),\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 菜单项被选中 */\r\n select: [key: string]\r\n /** 展开项变化 */\r\n 'update:expandedKeys': [keys: string[]]\r\n }>()\r\n\r\n const menuRef = ref<MenuInst | null>(null)\r\n const expandedKeys = ref<string[]>([])\r\n\r\n const activeKey = computed(() => props.value ?? '')\r\n\r\n // ====== 数据合成 ======\r\n\r\n /** 合并后的最终 MenuOption[] */\r\n const mergedOptions = computed<MenuOption[]>(() => {\r\n // 优先使用 options 直传模式\r\n if (props.options) return props.options\r\n\r\n // routes 模式 → 适配器转换\r\n if (props.routes) {\r\n const config: MenuAdapterConfig = {\r\n ...props.adapterConfig,\r\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\r\n }\r\n return createMenuOptions(props.routes, config)\r\n }\r\n\r\n return []\r\n })\r\n\r\n // ====== 展开逻辑 ======\r\n\r\n /** 从 MenuOption[] 中查找所有祖先 key */\r\n const findParentKeys = (\r\n items: MenuOption[],\r\n targetKey: string,\r\n parentKeys: string[] = []\r\n ): string[] => {\r\n for (const item of items) {\r\n const children = item.children as MenuOption[] | undefined\r\n if (children?.length) {\r\n const currentKeys = [...parentKeys, item.key as string]\r\n if (children.some(child => child.key === targetKey)) {\r\n return currentKeys\r\n }\r\n const result = findParentKeys(children, targetKey, currentKeys)\r\n if (result.length > 0) return result\r\n }\r\n }\r\n return []\r\n }\r\n\r\n /** 通过路径段推导展开 key + 查找祖先 key */\r\n const computeExpandedKeys = (path: string): Set<string> => {\r\n const segments = path.split('/').filter(Boolean)\r\n const keys = new Set<string>()\r\n let currentPath = ''\r\n segments.forEach(segment => {\r\n currentPath += `/${segment}`\r\n keys.add(currentPath)\r\n })\r\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\r\n return keys\r\n }\r\n\r\n // ====== 事件处理 ======\r\n\r\n const handleMenuClick = (key: string) => {\r\n if (key && key !== activeKey.value) {\r\n emit('select', key)\r\n }\r\n }\r\n\r\n const onExpandedKeysChange = (keys: string[]) => {\r\n expandedKeys.value = keys\r\n emit('update:expandedKeys', keys)\r\n }\r\n\r\n // ====== 暴露方法 ======\r\n const showOption = (key: string) => menuRef.value?.showOption(key)\r\n defineExpose({ showOption, menuRef })\r\n\r\n // ====== 路由跟踪 ======\r\n\r\n watch(\r\n () => activeKey.value,\r\n newKey => {\r\n if (!newKey) return\r\n const newKeys = computeExpandedKeys(newKey)\r\n expandedKeys.value = Array.from(\r\n new Set([...expandedKeys.value, ...newKeys])\r\n )\r\n nextTick(() => menuRef.value?.showOption(newKey))\r\n },\r\n { immediate: true }\r\n )\r\n\r\n onMounted(() => {\r\n nextTick(() => {\r\n if (activeKey.value) {\r\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\r\n menuRef.value?.showOption(activeKey.value)\r\n }\r\n })\r\n })\r\n</script>\r\n","<!--\r\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\r\n *\r\n * 支持两种数据输入模式:\r\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\r\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\r\n *\r\n * @example 路由模式(推荐)\r\n * ```vue\r\n * <C_Menu\r\n * :routes=\"menuData\"\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 原生模式\r\n * ```vue\r\n * <C_Menu\r\n * :options=\"nMenuOptions\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NMenu\r\n ref=\"menuRef\"\r\n :options=\"mergedOptions\"\r\n :expanded-keys=\"expandedKeys\"\r\n :value=\"activeKey\"\r\n :mode=\"mode\"\r\n :collapsed=\"collapsed\"\r\n :collapsed-width=\"collapsedWidth\"\r\n :collapsed-icon-size=\"collapsedIconSize\"\r\n :inverted=\"inverted\"\r\n :theme-overrides=\"themeOverrides\"\r\n :dropdown-props=\"dropdownProps\"\r\n :indent=\"indent\"\r\n :root-indent=\"rootIndent\"\r\n @update:value=\"handleMenuClick\"\r\n @update:expanded-keys=\"onExpandedKeysChange\"\r\n />\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\r\n import {\r\n type MenuOption,\r\n type MenuInst,\r\n type DropdownProps,\r\n } from 'naive-ui/es'\r\n import {\r\n createMenuOptions,\r\n type RouteItem,\r\n type MenuAdapterConfig,\r\n } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Menu' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 模式一:直传 NMenu 原生 MenuOption[]\r\n * 与 `routes` 二选一,`options` 优先级更高\r\n */\r\n options?: MenuOption[]\r\n /**\r\n * 模式二:传入路由数据,自动转换为 MenuOption[]\r\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\r\n */\r\n routes?: RouteItem[]\r\n /**\r\n * 适配器配置(仅 routes 模式生效)\r\n * 可自定义 key/label/icon 取值、过滤逻辑等\r\n */\r\n adapterConfig?: MenuAdapterConfig\r\n /**\r\n * 标签文本格式化函数(routes 模式的快捷方式)\r\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\r\n */\r\n labelFormatter?: (label: string) => string\r\n /** 当前激活的菜单 key */\r\n value?: string\r\n /** 菜单模式 */\r\n mode?: 'vertical' | 'horizontal'\r\n /** 是否折叠 */\r\n collapsed?: boolean\r\n /** 折叠宽度 */\r\n collapsedWidth?: number\r\n /** 折叠图标大小 */\r\n collapsedIconSize?: number\r\n /** 是否反色 */\r\n inverted?: boolean\r\n /** 主题覆盖 */\r\n themeOverrides?: Record<string, any>\r\n /** 缩进像素 */\r\n indent?: number\r\n /** 根缩进像素 */\r\n rootIndent?: number\r\n /** 下拉菜单属性(折叠模式) */\r\n dropdownProps?: DropdownProps\r\n }>(),\r\n {\r\n mode: 'vertical',\r\n collapsed: false,\r\n collapsedWidth: 64,\r\n collapsedIconSize: 22,\r\n inverted: false,\r\n indent: 24,\r\n rootIndent: 16,\r\n dropdownProps: () => ({\r\n placement: 'right-start' as const,\r\n trigger: 'hover' as const,\r\n arrowStyle: { color: 'var(--n-color)' },\r\n }),\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 菜单项被选中 */\r\n select: [key: string]\r\n /** 展开项变化 */\r\n 'update:expandedKeys': [keys: string[]]\r\n }>()\r\n\r\n const menuRef = ref<MenuInst | null>(null)\r\n const expandedKeys = ref<string[]>([])\r\n\r\n const activeKey = computed(() => props.value ?? '')\r\n\r\n // ====== 数据合成 ======\r\n\r\n /** 合并后的最终 MenuOption[] */\r\n const mergedOptions = computed<MenuOption[]>(() => {\r\n // 优先使用 options 直传模式\r\n if (props.options) return props.options\r\n\r\n // routes 模式 → 适配器转换\r\n if (props.routes) {\r\n const config: MenuAdapterConfig = {\r\n ...props.adapterConfig,\r\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\r\n }\r\n return createMenuOptions(props.routes, config)\r\n }\r\n\r\n return []\r\n })\r\n\r\n // ====== 展开逻辑 ======\r\n\r\n /** 从 MenuOption[] 中查找所有祖先 key */\r\n const findParentKeys = (\r\n items: MenuOption[],\r\n targetKey: string,\r\n parentKeys: string[] = []\r\n ): string[] => {\r\n for (const item of items) {\r\n const children = item.children as MenuOption[] | undefined\r\n if (children?.length) {\r\n const currentKeys = [...parentKeys, item.key as string]\r\n if (children.some(child => child.key === targetKey)) {\r\n return currentKeys\r\n }\r\n const result = findParentKeys(children, targetKey, currentKeys)\r\n if (result.length > 0) return result\r\n }\r\n }\r\n return []\r\n }\r\n\r\n /** 通过路径段推导展开 key + 查找祖先 key */\r\n const computeExpandedKeys = (path: string): Set<string> => {\r\n const segments = path.split('/').filter(Boolean)\r\n const keys = new Set<string>()\r\n let currentPath = ''\r\n segments.forEach(segment => {\r\n currentPath += `/${segment}`\r\n keys.add(currentPath)\r\n })\r\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\r\n return keys\r\n }\r\n\r\n // ====== 事件处理 ======\r\n\r\n const handleMenuClick = (key: string) => {\r\n if (key && key !== activeKey.value) {\r\n emit('select', key)\r\n }\r\n }\r\n\r\n const onExpandedKeysChange = (keys: string[]) => {\r\n expandedKeys.value = keys\r\n emit('update:expandedKeys', keys)\r\n }\r\n\r\n // ====== 暴露方法 ======\r\n const showOption = (key: string) => menuRef.value?.showOption(key)\r\n defineExpose({ showOption, menuRef })\r\n\r\n // ====== 路由跟踪 ======\r\n\r\n watch(\r\n () => activeKey.value,\r\n newKey => {\r\n if (!newKey) return\r\n const newKeys = computeExpandedKeys(newKey)\r\n expandedKeys.value = Array.from(\r\n new Set([...expandedKeys.value, ...newKeys])\r\n )\r\n nextTick(() => menuRef.value?.showOption(newKey))\r\n },\r\n { immediate: true }\r\n )\r\n\r\n onMounted(() => {\r\n nextTick(() => {\r\n if (activeKey.value) {\r\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\r\n menuRef.value?.showOption(activeKey.value)\r\n }\r\n })\r\n })\r\n</script>\r\n"],"mappings":"gnBE6DE,IAAM,EAAQ,EA2DR,EAAO,EAOP,GAAA,EAAA,EAAA,KAA+B,KAAK,CACpC,GAAA,EAAA,EAAA,KAA6B,EAAE,CAAC,CAEhC,GAAA,EAAA,EAAA,cAA2B,EAAM,OAAS,GAAG,CAK7C,GAAA,EAAA,EAAA,cAA6C,CAEjD,GAAI,EAAM,QAAS,OAAO,EAAM,QAGhC,GAAI,EAAM,OAAQ,CAChB,IAAM,EAA4B,CAChC,GAAG,EAAM,cACT,GAAI,EAAM,gBAAkB,CAAE,eAAgB,EAAM,eAAgB,CACrE,CACD,OAAO,EAAA,EAAkB,EAAM,OAAQ,EAAO,CAGhD,MAAO,EAAE,EACT,CAKI,GACJ,EACA,EACA,EAAuB,EAAE,GACZ,CACb,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAW,EAAK,SACtB,GAAI,GAAU,OAAQ,CACpB,IAAM,EAAc,CAAC,GAAG,EAAY,EAAK,IAAc,CACvD,GAAI,EAAS,KAAK,GAAS,EAAM,MAAQ,EAAU,CACjD,OAAO,EAET,IAAM,EAAS,EAAe,EAAU,EAAW,EAAY,CAC/D,GAAI,EAAO,OAAS,EAAG,OAAO,GAGlC,MAAO,EAAE,EAIL,EAAuB,GAA8B,CACzD,IAAM,EAAW,EAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAC1C,EAAO,IAAI,IACb,EAAc,GAMlB,OALA,EAAS,QAAQ,GAAW,CAC1B,GAAe,IAAI,IACnB,EAAK,IAAI,EAAY,EACrB,CACF,EAAe,EAAc,MAAO,EAAK,CAAC,QAAQ,GAAO,EAAK,IAAI,EAAI,CAAC,CAChE,GAKH,EAAmB,GAAgB,CACnC,GAAO,IAAQ,EAAU,OAC3B,EAAK,SAAU,EAAI,EAIjB,EAAwB,GAAmB,CAC/C,EAAa,MAAQ,EACrB,EAAK,sBAAuB,EAAK,SAKnC,EAAa,CAAE,WADK,GAAgB,EAAQ,OAAO,WAAW,EAAI,CACvC,UAAS,CAAC,EAIrC,EAAA,EAAA,WACQ,EAAU,MAChB,GAAU,CACR,GAAI,CAAC,EAAQ,OACb,IAAM,EAAU,EAAoB,EAAO,CAC3C,EAAa,MAAQ,MAAM,KACzB,IAAI,IAAI,CAAC,GAAG,EAAa,MAAO,GAAG,EAAQ,CAAC,CAC7C,EACD,EAAA,EAAA,cAAe,EAAQ,OAAO,WAAW,EAAO,CAAC,EAEnD,CAAE,UAAW,GAAM,CACpB,EAED,EAAA,EAAA,eAAgB,EACd,EAAA,EAAA,cAAe,CACT,EAAU,QACZ,EAAa,MAAQ,MAAM,KAAK,EAAoB,EAAU,MAAM,CAAC,CACrE,EAAQ,OAAO,WAAW,EAAU,MAAM,GAE5C,EACF,iEAtLA,EAAA,SAfI,UAAJ,IAAI,EACH,QAAS,EAAA,MACT,gBAAe,EAAA,MACf,MAAO,EAAA,MACP,KAAMA,EAAAA,KACN,UAAWC,EAAAA,UACX,kBAAiBC,EAAAA,eACjB,sBAAqBC,EAAAA,kBACrB,SAAUC,EAAAA,SACV,kBAAiBC,EAAAA,eACjB,iBAAgBC,EAAAA,cAChB,OAAQC,EAAAA,OACR,cAAaC,EAAAA,WACb,iBAAc,EACd,wBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"C_Menu2.js","names":["mode","collapsed","collapsedWidth","collapsedIconSize","inverted","themeOverrides","dropdownProps","indent","rootIndent"],"sources":["../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\n *\n * 支持两种数据输入模式:\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\n *\n * @example 路由模式(推荐)\n * ```vue\n * <C_Menu\n * :routes=\"menuData\"\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 原生模式\n * ```vue\n * <C_Menu\n * :options=\"nMenuOptions\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NMenu\n ref=\"menuRef\"\n :options=\"mergedOptions\"\n :expanded-keys=\"expandedKeys\"\n :value=\"activeKey\"\n :mode=\"mode\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"collapsedWidth\"\n :collapsed-icon-size=\"collapsedIconSize\"\n :inverted=\"inverted\"\n :theme-overrides=\"themeOverrides\"\n :dropdown-props=\"dropdownProps\"\n :indent=\"indent\"\n :root-indent=\"rootIndent\"\n @update:value=\"handleMenuClick\"\n @update:expanded-keys=\"onExpandedKeysChange\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\n import {\n type MenuOption,\n type MenuInst,\n type DropdownProps,\n } from 'naive-ui/es'\n import type { RouteItem, MenuAdapterConfig } from '../_shared'\n import { createMenuOptions } from '../_shared'\n\n defineOptions({ name: 'C_Menu' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 模式一:直传 NMenu 原生 MenuOption[]\n * 与 `routes` 二选一,`options` 优先级更高\n */\n options?: MenuOption[]\n /**\n * 模式二:传入路由数据,自动转换为 MenuOption[]\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\n */\n routes?: RouteItem[]\n /**\n * 适配器配置(仅 routes 模式生效)\n * 可自定义 key/label/icon 取值、过滤逻辑等\n */\n adapterConfig?: MenuAdapterConfig\n /**\n * 标签文本格式化函数(routes 模式的快捷方式)\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\n */\n labelFormatter?: (label: string) => string\n /** 当前激活的菜单 key */\n value?: string\n /** 菜单模式 */\n mode?: 'vertical' | 'horizontal'\n /** 是否折叠 */\n collapsed?: boolean\n /** 折叠宽度 */\n collapsedWidth?: number\n /** 折叠图标大小 */\n collapsedIconSize?: number\n /** 是否反色 */\n inverted?: boolean\n /** 主题覆盖 */\n themeOverrides?: Record<string, any>\n /** 缩进像素 */\n indent?: number\n /** 根缩进像素 */\n rootIndent?: number\n /** 下拉菜单属性(折叠模式) */\n dropdownProps?: DropdownProps\n }>(),\n {\n mode: 'vertical',\n collapsed: false,\n collapsedWidth: 64,\n collapsedIconSize: 22,\n inverted: false,\n indent: 24,\n rootIndent: 16,\n dropdownProps: () => ({\n placement: 'right-start' as const,\n trigger: 'hover' as const,\n arrowStyle: { color: 'var(--n-color)' },\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 菜单项被选中 */\n select: [key: string]\n /** 展开项变化 */\n 'update:expandedKeys': [keys: string[]]\n }>()\n\n const menuRef = ref<MenuInst | null>(null)\n const expandedKeys = ref<string[]>([])\n\n const activeKey = computed(() => props.value ?? '')\n\n // ====== 数据合成 ======\n\n /** 合并后的最终 MenuOption[] */\n const mergedOptions = computed<MenuOption[]>(() => {\n // 优先使用 options 直传模式\n if (props.options) return props.options\n\n // routes 模式 → 适配器转换\n if (props.routes) {\n const config: MenuAdapterConfig = {\n ...props.adapterConfig,\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\n }\n return createMenuOptions(props.routes, config)\n }\n\n return []\n })\n\n // ====== 展开逻辑 ======\n\n /** 从 MenuOption[] 中查找所有祖先 key */\n const findParentKeys = (\n items: MenuOption[],\n targetKey: string,\n parentKeys: string[] = []\n ): string[] => {\n for (const item of items) {\n const children = item.children as MenuOption[] | undefined\n if (children?.length) {\n const currentKeys = [...parentKeys, item.key as string]\n if (children.some(child => child.key === targetKey)) {\n return currentKeys\n }\n const result = findParentKeys(children, targetKey, currentKeys)\n if (result.length > 0) return result\n }\n }\n return []\n }\n\n /** 通过路径段推导展开 key + 查找祖先 key */\n const computeExpandedKeys = (path: string): Set<string> => {\n const segments = path.split('/').filter(Boolean)\n const keys = new Set<string>()\n let currentPath = ''\n segments.forEach(segment => {\n currentPath += `/${segment}`\n keys.add(currentPath)\n })\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\n return keys\n }\n\n // ====== 事件处理 ======\n\n const handleMenuClick = (key: string) => {\n if (key && key !== activeKey.value) {\n emit('select', key)\n }\n }\n\n const onExpandedKeysChange = (keys: string[]) => {\n expandedKeys.value = keys\n emit('update:expandedKeys', keys)\n }\n\n // ====== 暴露方法 ======\n const showOption = (key: string) => menuRef.value?.showOption(key)\n defineExpose({ showOption, menuRef })\n\n // ====== 路由跟踪 ======\n\n watch(\n () => activeKey.value,\n newKey => {\n if (!newKey) return\n const newKeys = computeExpandedKeys(newKey)\n expandedKeys.value = Array.from(\n new Set([...expandedKeys.value, ...newKeys])\n )\n nextTick(() => menuRef.value?.showOption(newKey))\n },\n { immediate: true }\n )\n\n onMounted(() => {\n nextTick(() => {\n if (activeKey.value) {\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\n menuRef.value?.showOption(activeKey.value)\n }\n })\n })\n</script>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\n *\n * 支持两种数据输入模式:\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\n *\n * @example 路由模式(推荐)\n * ```vue\n * <C_Menu\n * :routes=\"menuData\"\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 原生模式\n * ```vue\n * <C_Menu\n * :options=\"nMenuOptions\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NMenu\n ref=\"menuRef\"\n :options=\"mergedOptions\"\n :expanded-keys=\"expandedKeys\"\n :value=\"activeKey\"\n :mode=\"mode\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"collapsedWidth\"\n :collapsed-icon-size=\"collapsedIconSize\"\n :inverted=\"inverted\"\n :theme-overrides=\"themeOverrides\"\n :dropdown-props=\"dropdownProps\"\n :indent=\"indent\"\n :root-indent=\"rootIndent\"\n @update:value=\"handleMenuClick\"\n @update:expanded-keys=\"onExpandedKeysChange\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\n import {\n type MenuOption,\n type MenuInst,\n type DropdownProps,\n } from 'naive-ui/es'\n import type { RouteItem, MenuAdapterConfig } from '../_shared'\n import { createMenuOptions } from '../_shared'\n\n defineOptions({ name: 'C_Menu' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 模式一:直传 NMenu 原生 MenuOption[]\n * 与 `routes` 二选一,`options` 优先级更高\n */\n options?: MenuOption[]\n /**\n * 模式二:传入路由数据,自动转换为 MenuOption[]\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\n */\n routes?: RouteItem[]\n /**\n * 适配器配置(仅 routes 模式生效)\n * 可自定义 key/label/icon 取值、过滤逻辑等\n */\n adapterConfig?: MenuAdapterConfig\n /**\n * 标签文本格式化函数(routes 模式的快捷方式)\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\n */\n labelFormatter?: (label: string) => string\n /** 当前激活的菜单 key */\n value?: string\n /** 菜单模式 */\n mode?: 'vertical' | 'horizontal'\n /** 是否折叠 */\n collapsed?: boolean\n /** 折叠宽度 */\n collapsedWidth?: number\n /** 折叠图标大小 */\n collapsedIconSize?: number\n /** 是否反色 */\n inverted?: boolean\n /** 主题覆盖 */\n themeOverrides?: Record<string, any>\n /** 缩进像素 */\n indent?: number\n /** 根缩进像素 */\n rootIndent?: number\n /** 下拉菜单属性(折叠模式) */\n dropdownProps?: DropdownProps\n }>(),\n {\n mode: 'vertical',\n collapsed: false,\n collapsedWidth: 64,\n collapsedIconSize: 22,\n inverted: false,\n indent: 24,\n rootIndent: 16,\n dropdownProps: () => ({\n placement: 'right-start' as const,\n trigger: 'hover' as const,\n arrowStyle: { color: 'var(--n-color)' },\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 菜单项被选中 */\n select: [key: string]\n /** 展开项变化 */\n 'update:expandedKeys': [keys: string[]]\n }>()\n\n const menuRef = ref<MenuInst | null>(null)\n const expandedKeys = ref<string[]>([])\n\n const activeKey = computed(() => props.value ?? '')\n\n // ====== 数据合成 ======\n\n /** 合并后的最终 MenuOption[] */\n const mergedOptions = computed<MenuOption[]>(() => {\n // 优先使用 options 直传模式\n if (props.options) return props.options\n\n // routes 模式 → 适配器转换\n if (props.routes) {\n const config: MenuAdapterConfig = {\n ...props.adapterConfig,\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\n }\n return createMenuOptions(props.routes, config)\n }\n\n return []\n })\n\n // ====== 展开逻辑 ======\n\n /** 从 MenuOption[] 中查找所有祖先 key */\n const findParentKeys = (\n items: MenuOption[],\n targetKey: string,\n parentKeys: string[] = []\n ): string[] => {\n for (const item of items) {\n const children = item.children as MenuOption[] | undefined\n if (children?.length) {\n const currentKeys = [...parentKeys, item.key as string]\n if (children.some(child => child.key === targetKey)) {\n return currentKeys\n }\n const result = findParentKeys(children, targetKey, currentKeys)\n if (result.length > 0) return result\n }\n }\n return []\n }\n\n /** 通过路径段推导展开 key + 查找祖先 key */\n const computeExpandedKeys = (path: string): Set<string> => {\n const segments = path.split('/').filter(Boolean)\n const keys = new Set<string>()\n let currentPath = ''\n segments.forEach(segment => {\n currentPath += `/${segment}`\n keys.add(currentPath)\n })\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\n return keys\n }\n\n // ====== 事件处理 ======\n\n const handleMenuClick = (key: string) => {\n if (key && key !== activeKey.value) {\n emit('select', key)\n }\n }\n\n const onExpandedKeysChange = (keys: string[]) => {\n expandedKeys.value = keys\n emit('update:expandedKeys', keys)\n }\n\n // ====== 暴露方法 ======\n const showOption = (key: string) => menuRef.value?.showOption(key)\n defineExpose({ showOption, menuRef })\n\n // ====== 路由跟踪 ======\n\n watch(\n () => activeKey.value,\n newKey => {\n if (!newKey) return\n const newKeys = computeExpandedKeys(newKey)\n expandedKeys.value = Array.from(\n new Set([...expandedKeys.value, ...newKeys])\n )\n nextTick(() => menuRef.value?.showOption(newKey))\n },\n { immediate: true }\n )\n\n onMounted(() => {\n nextTick(() => {\n if (activeKey.value) {\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\n menuRef.value?.showOption(activeKey.value)\n }\n })\n })\n</script>\n","<!--\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\n *\n * 支持两种数据输入模式:\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\n *\n * @example 路由模式(推荐)\n * ```vue\n * <C_Menu\n * :routes=\"menuData\"\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 原生模式\n * ```vue\n * <C_Menu\n * :options=\"nMenuOptions\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <NMenu\n ref=\"menuRef\"\n :options=\"mergedOptions\"\n :expanded-keys=\"expandedKeys\"\n :value=\"activeKey\"\n :mode=\"mode\"\n :collapsed=\"collapsed\"\n :collapsed-width=\"collapsedWidth\"\n :collapsed-icon-size=\"collapsedIconSize\"\n :inverted=\"inverted\"\n :theme-overrides=\"themeOverrides\"\n :dropdown-props=\"dropdownProps\"\n :indent=\"indent\"\n :root-indent=\"rootIndent\"\n @update:value=\"handleMenuClick\"\n @update:expanded-keys=\"onExpandedKeysChange\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\n import {\n type MenuOption,\n type MenuInst,\n type DropdownProps,\n } from 'naive-ui/es'\n import type { RouteItem, MenuAdapterConfig } from '../_shared'\n import { createMenuOptions } from '../_shared'\n\n defineOptions({ name: 'C_Menu' })\n\n const props = withDefaults(\n defineProps<{\n /**\n * 模式一:直传 NMenu 原生 MenuOption[]\n * 与 `routes` 二选一,`options` 优先级更高\n */\n options?: MenuOption[]\n /**\n * 模式二:传入路由数据,自动转换为 MenuOption[]\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\n */\n routes?: RouteItem[]\n /**\n * 适配器配置(仅 routes 模式生效)\n * 可自定义 key/label/icon 取值、过滤逻辑等\n */\n adapterConfig?: MenuAdapterConfig\n /**\n * 标签文本格式化函数(routes 模式的快捷方式)\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\n */\n labelFormatter?: (label: string) => string\n /** 当前激活的菜单 key */\n value?: string\n /** 菜单模式 */\n mode?: 'vertical' | 'horizontal'\n /** 是否折叠 */\n collapsed?: boolean\n /** 折叠宽度 */\n collapsedWidth?: number\n /** 折叠图标大小 */\n collapsedIconSize?: number\n /** 是否反色 */\n inverted?: boolean\n /** 主题覆盖 */\n themeOverrides?: Record<string, any>\n /** 缩进像素 */\n indent?: number\n /** 根缩进像素 */\n rootIndent?: number\n /** 下拉菜单属性(折叠模式) */\n dropdownProps?: DropdownProps\n }>(),\n {\n mode: 'vertical',\n collapsed: false,\n collapsedWidth: 64,\n collapsedIconSize: 22,\n inverted: false,\n indent: 24,\n rootIndent: 16,\n dropdownProps: () => ({\n placement: 'right-start' as const,\n trigger: 'hover' as const,\n arrowStyle: { color: 'var(--n-color)' },\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 菜单项被选中 */\n select: [key: string]\n /** 展开项变化 */\n 'update:expandedKeys': [keys: string[]]\n }>()\n\n const menuRef = ref<MenuInst | null>(null)\n const expandedKeys = ref<string[]>([])\n\n const activeKey = computed(() => props.value ?? '')\n\n // ====== 数据合成 ======\n\n /** 合并后的最终 MenuOption[] */\n const mergedOptions = computed<MenuOption[]>(() => {\n // 优先使用 options 直传模式\n if (props.options) return props.options\n\n // routes 模式 → 适配器转换\n if (props.routes) {\n const config: MenuAdapterConfig = {\n ...props.adapterConfig,\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\n }\n return createMenuOptions(props.routes, config)\n }\n\n return []\n })\n\n // ====== 展开逻辑 ======\n\n /** 从 MenuOption[] 中查找所有祖先 key */\n const findParentKeys = (\n items: MenuOption[],\n targetKey: string,\n parentKeys: string[] = []\n ): string[] => {\n for (const item of items) {\n const children = item.children as MenuOption[] | undefined\n if (children?.length) {\n const currentKeys = [...parentKeys, item.key as string]\n if (children.some(child => child.key === targetKey)) {\n return currentKeys\n }\n const result = findParentKeys(children, targetKey, currentKeys)\n if (result.length > 0) return result\n }\n }\n return []\n }\n\n /** 通过路径段推导展开 key + 查找祖先 key */\n const computeExpandedKeys = (path: string): Set<string> => {\n const segments = path.split('/').filter(Boolean)\n const keys = new Set<string>()\n let currentPath = ''\n segments.forEach(segment => {\n currentPath += `/${segment}`\n keys.add(currentPath)\n })\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\n return keys\n }\n\n // ====== 事件处理 ======\n\n const handleMenuClick = (key: string) => {\n if (key && key !== activeKey.value) {\n emit('select', key)\n }\n }\n\n const onExpandedKeysChange = (keys: string[]) => {\n expandedKeys.value = keys\n emit('update:expandedKeys', keys)\n }\n\n // ====== 暴露方法 ======\n const showOption = (key: string) => menuRef.value?.showOption(key)\n defineExpose({ showOption, menuRef })\n\n // ====== 路由跟踪 ======\n\n watch(\n () => activeKey.value,\n newKey => {\n if (!newKey) return\n const newKeys = computeExpandedKeys(newKey)\n expandedKeys.value = Array.from(\n new Set([...expandedKeys.value, ...newKeys])\n )\n nextTick(() => menuRef.value?.showOption(newKey))\n },\n { immediate: true }\n )\n\n onMounted(() => {\n nextTick(() => {\n if (activeKey.value) {\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\n menuRef.value?.showOption(activeKey.value)\n }\n })\n })\n</script>\n"],"mappings":"ksBE0DE,IAAM,EAAQ,EA2DR,EAAO,EAOP,EAAU,EAAqB,KAAI,CACnC,EAAe,EAAc,EAAE,CAAA,CAE/B,EAAY,MAAe,EAAM,OAAS,GAAE,CAK5C,EAAgB,MAA6B,CAEjD,GAAI,EAAM,QAAS,OAAO,EAAM,QAGhC,GAAI,EAAM,OAAQ,CAChB,IAAM,EAA4B,CAChC,GAAG,EAAM,cACT,GAAI,EAAM,gBAAkB,CAAE,eAAgB,EAAM,eAAgB,CACtE,CACA,OAAO,EAAkB,EAAM,OAAQ,EAAM,CAG/C,MAAO,EAAC,EACT,CAKK,GACJ,EACA,EACA,EAAuB,EAAC,GACX,CACb,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAW,EAAK,SACtB,GAAI,GAAU,OAAQ,CACpB,IAAM,EAAc,CAAC,GAAG,EAAY,EAAK,IAAa,CACtD,GAAI,EAAS,KAAK,GAAS,EAAM,MAAQ,EAAU,CACjD,OAAO,EAET,IAAM,EAAS,EAAe,EAAU,EAAW,EAAW,CAC9D,GAAI,EAAO,OAAS,EAAG,OAAO,GAGlC,MAAO,EAAC,EAIJ,EAAuB,GAA8B,CACzD,IAAM,EAAW,EAAK,MAAM,IAAI,CAAC,OAAO,QAAO,CACzC,EAAO,IAAI,IACb,EAAc,GAMlB,OALA,EAAS,QAAQ,GAAW,CAC1B,GAAe,IAAI,IACnB,EAAK,IAAI,EAAW,EACrB,CACD,EAAe,EAAc,MAAO,EAAK,CAAC,QAAQ,GAAO,EAAK,IAAI,EAAI,CAAA,CAC/D,GAKH,EAAmB,GAAgB,CACnC,GAAO,IAAQ,EAAU,OAC3B,EAAK,SAAU,EAAG,EAIhB,EAAwB,GAAmB,CAC/C,EAAa,MAAQ,EACrB,EAAK,sBAAuB,EAAI,SAKlC,EAAa,CAAE,WADK,GAAgB,EAAQ,OAAO,WAAW,EAAG,CACtC,UAAS,CAAA,CAIpC,MACQ,EAAU,MAChB,GAAU,CACR,GAAI,CAAC,EAAQ,OACb,IAAM,EAAU,EAAoB,EAAM,CAC1C,EAAa,MAAQ,MAAM,KACzB,IAAI,IAAI,CAAC,GAAG,EAAa,MAAO,GAAG,EAAQ,CAAA,CAC7C,CACA,MAAe,EAAQ,OAAO,WAAW,EAAO,CAAA,EAElD,CAAE,UAAW,GAAK,CACpB,CAEA,MAAgB,CACd,MAAe,CACT,EAAU,QACZ,EAAa,MAAQ,MAAM,KAAK,EAAoB,EAAU,MAAM,CAAA,CACpE,EAAQ,OAAO,WAAW,EAAU,MAAK,GAE5C,EACF,4BAnMD,EAgBE,EAAA,SAfI,UAAJ,IAAI,EACH,QAAS,EAAA,MACT,gBAAe,EAAA,MACf,MAAO,EAAA,MACP,KAAMA,EAAAA,KACN,UAAWC,EAAAA,UACX,kBAAiBC,EAAAA,eACjB,sBAAqBC,EAAAA,kBACrB,SAAUC,EAAAA,SACV,kBAAiBC,EAAAA,eACjB,iBAAgBC,EAAAA,cAChB,OAAQC,EAAAA,OACR,cAAaC,EAAAA,WACb,iBAAc,EACd,wBAAsB"}
1
+ {"version":3,"file":"C_Menu2.js","names":["mode","collapsed","collapsedWidth","collapsedIconSize","inverted","themeOverrides","dropdownProps","indent","rootIndent"],"sources":["../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue","../src/components/C_Menu/index.vue"],"sourcesContent":["/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\r\n *\r\n * 支持两种数据输入模式:\r\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\r\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\r\n *\r\n * @example 路由模式(推荐)\r\n * ```vue\r\n * <C_Menu\r\n * :routes=\"menuData\"\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 原生模式\r\n * ```vue\r\n * <C_Menu\r\n * :options=\"nMenuOptions\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NMenu\r\n ref=\"menuRef\"\r\n :options=\"mergedOptions\"\r\n :expanded-keys=\"expandedKeys\"\r\n :value=\"activeKey\"\r\n :mode=\"mode\"\r\n :collapsed=\"collapsed\"\r\n :collapsed-width=\"collapsedWidth\"\r\n :collapsed-icon-size=\"collapsedIconSize\"\r\n :inverted=\"inverted\"\r\n :theme-overrides=\"themeOverrides\"\r\n :dropdown-props=\"dropdownProps\"\r\n :indent=\"indent\"\r\n :root-indent=\"rootIndent\"\r\n @update:value=\"handleMenuClick\"\r\n @update:expanded-keys=\"onExpandedKeysChange\"\r\n />\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\r\n import {\r\n type MenuOption,\r\n type MenuInst,\r\n type DropdownProps,\r\n } from 'naive-ui/es'\r\n import {\r\n createMenuOptions,\r\n type RouteItem,\r\n type MenuAdapterConfig,\r\n } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Menu' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 模式一:直传 NMenu 原生 MenuOption[]\r\n * 与 `routes` 二选一,`options` 优先级更高\r\n */\r\n options?: MenuOption[]\r\n /**\r\n * 模式二:传入路由数据,自动转换为 MenuOption[]\r\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\r\n */\r\n routes?: RouteItem[]\r\n /**\r\n * 适配器配置(仅 routes 模式生效)\r\n * 可自定义 key/label/icon 取值、过滤逻辑等\r\n */\r\n adapterConfig?: MenuAdapterConfig\r\n /**\r\n * 标签文本格式化函数(routes 模式的快捷方式)\r\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\r\n */\r\n labelFormatter?: (label: string) => string\r\n /** 当前激活的菜单 key */\r\n value?: string\r\n /** 菜单模式 */\r\n mode?: 'vertical' | 'horizontal'\r\n /** 是否折叠 */\r\n collapsed?: boolean\r\n /** 折叠宽度 */\r\n collapsedWidth?: number\r\n /** 折叠图标大小 */\r\n collapsedIconSize?: number\r\n /** 是否反色 */\r\n inverted?: boolean\r\n /** 主题覆盖 */\r\n themeOverrides?: Record<string, any>\r\n /** 缩进像素 */\r\n indent?: number\r\n /** 根缩进像素 */\r\n rootIndent?: number\r\n /** 下拉菜单属性(折叠模式) */\r\n dropdownProps?: DropdownProps\r\n }>(),\r\n {\r\n mode: 'vertical',\r\n collapsed: false,\r\n collapsedWidth: 64,\r\n collapsedIconSize: 22,\r\n inverted: false,\r\n indent: 24,\r\n rootIndent: 16,\r\n dropdownProps: () => ({\r\n placement: 'right-start' as const,\r\n trigger: 'hover' as const,\r\n arrowStyle: { color: 'var(--n-color)' },\r\n }),\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 菜单项被选中 */\r\n select: [key: string]\r\n /** 展开项变化 */\r\n 'update:expandedKeys': [keys: string[]]\r\n }>()\r\n\r\n const menuRef = ref<MenuInst | null>(null)\r\n const expandedKeys = ref<string[]>([])\r\n\r\n const activeKey = computed(() => props.value ?? '')\r\n\r\n // ====== 数据合成 ======\r\n\r\n /** 合并后的最终 MenuOption[] */\r\n const mergedOptions = computed<MenuOption[]>(() => {\r\n // 优先使用 options 直传模式\r\n if (props.options) return props.options\r\n\r\n // routes 模式 → 适配器转换\r\n if (props.routes) {\r\n const config: MenuAdapterConfig = {\r\n ...props.adapterConfig,\r\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\r\n }\r\n return createMenuOptions(props.routes, config)\r\n }\r\n\r\n return []\r\n })\r\n\r\n // ====== 展开逻辑 ======\r\n\r\n /** 从 MenuOption[] 中查找所有祖先 key */\r\n const findParentKeys = (\r\n items: MenuOption[],\r\n targetKey: string,\r\n parentKeys: string[] = []\r\n ): string[] => {\r\n for (const item of items) {\r\n const children = item.children as MenuOption[] | undefined\r\n if (children?.length) {\r\n const currentKeys = [...parentKeys, item.key as string]\r\n if (children.some(child => child.key === targetKey)) {\r\n return currentKeys\r\n }\r\n const result = findParentKeys(children, targetKey, currentKeys)\r\n if (result.length > 0) return result\r\n }\r\n }\r\n return []\r\n }\r\n\r\n /** 通过路径段推导展开 key + 查找祖先 key */\r\n const computeExpandedKeys = (path: string): Set<string> => {\r\n const segments = path.split('/').filter(Boolean)\r\n const keys = new Set<string>()\r\n let currentPath = ''\r\n segments.forEach(segment => {\r\n currentPath += `/${segment}`\r\n keys.add(currentPath)\r\n })\r\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\r\n return keys\r\n }\r\n\r\n // ====== 事件处理 ======\r\n\r\n const handleMenuClick = (key: string) => {\r\n if (key && key !== activeKey.value) {\r\n emit('select', key)\r\n }\r\n }\r\n\r\n const onExpandedKeysChange = (keys: string[]) => {\r\n expandedKeys.value = keys\r\n emit('update:expandedKeys', keys)\r\n }\r\n\r\n // ====== 暴露方法 ======\r\n const showOption = (key: string) => menuRef.value?.showOption(key)\r\n defineExpose({ showOption, menuRef })\r\n\r\n // ====== 路由跟踪 ======\r\n\r\n watch(\r\n () => activeKey.value,\r\n newKey => {\r\n if (!newKey) return\r\n const newKeys = computeExpandedKeys(newKey)\r\n expandedKeys.value = Array.from(\r\n new Set([...expandedKeys.value, ...newKeys])\r\n )\r\n nextTick(() => menuRef.value?.showOption(newKey))\r\n },\r\n { immediate: true }\r\n )\r\n\r\n onMounted(() => {\r\n nextTick(() => {\r\n if (activeKey.value) {\r\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\r\n menuRef.value?.showOption(activeKey.value)\r\n }\r\n })\r\n })\r\n</script>\r\n","/* unplugin-vue-components disabled */<!--\r\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\r\n *\r\n * 支持两种数据输入模式:\r\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\r\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\r\n *\r\n * @example 路由模式(推荐)\r\n * ```vue\r\n * <C_Menu\r\n * :routes=\"menuData\"\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 原生模式\r\n * ```vue\r\n * <C_Menu\r\n * :options=\"nMenuOptions\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NMenu\r\n ref=\"menuRef\"\r\n :options=\"mergedOptions\"\r\n :expanded-keys=\"expandedKeys\"\r\n :value=\"activeKey\"\r\n :mode=\"mode\"\r\n :collapsed=\"collapsed\"\r\n :collapsed-width=\"collapsedWidth\"\r\n :collapsed-icon-size=\"collapsedIconSize\"\r\n :inverted=\"inverted\"\r\n :theme-overrides=\"themeOverrides\"\r\n :dropdown-props=\"dropdownProps\"\r\n :indent=\"indent\"\r\n :root-indent=\"rootIndent\"\r\n @update:value=\"handleMenuClick\"\r\n @update:expanded-keys=\"onExpandedKeysChange\"\r\n />\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\r\n import {\r\n type MenuOption,\r\n type MenuInst,\r\n type DropdownProps,\r\n } from 'naive-ui/es'\r\n import {\r\n createMenuOptions,\r\n type RouteItem,\r\n type MenuAdapterConfig,\r\n } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Menu' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 模式一:直传 NMenu 原生 MenuOption[]\r\n * 与 `routes` 二选一,`options` 优先级更高\r\n */\r\n options?: MenuOption[]\r\n /**\r\n * 模式二:传入路由数据,自动转换为 MenuOption[]\r\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\r\n */\r\n routes?: RouteItem[]\r\n /**\r\n * 适配器配置(仅 routes 模式生效)\r\n * 可自定义 key/label/icon 取值、过滤逻辑等\r\n */\r\n adapterConfig?: MenuAdapterConfig\r\n /**\r\n * 标签文本格式化函数(routes 模式的快捷方式)\r\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\r\n */\r\n labelFormatter?: (label: string) => string\r\n /** 当前激活的菜单 key */\r\n value?: string\r\n /** 菜单模式 */\r\n mode?: 'vertical' | 'horizontal'\r\n /** 是否折叠 */\r\n collapsed?: boolean\r\n /** 折叠宽度 */\r\n collapsedWidth?: number\r\n /** 折叠图标大小 */\r\n collapsedIconSize?: number\r\n /** 是否反色 */\r\n inverted?: boolean\r\n /** 主题覆盖 */\r\n themeOverrides?: Record<string, any>\r\n /** 缩进像素 */\r\n indent?: number\r\n /** 根缩进像素 */\r\n rootIndent?: number\r\n /** 下拉菜单属性(折叠模式) */\r\n dropdownProps?: DropdownProps\r\n }>(),\r\n {\r\n mode: 'vertical',\r\n collapsed: false,\r\n collapsedWidth: 64,\r\n collapsedIconSize: 22,\r\n inverted: false,\r\n indent: 24,\r\n rootIndent: 16,\r\n dropdownProps: () => ({\r\n placement: 'right-start' as const,\r\n trigger: 'hover' as const,\r\n arrowStyle: { color: 'var(--n-color)' },\r\n }),\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 菜单项被选中 */\r\n select: [key: string]\r\n /** 展开项变化 */\r\n 'update:expandedKeys': [keys: string[]]\r\n }>()\r\n\r\n const menuRef = ref<MenuInst | null>(null)\r\n const expandedKeys = ref<string[]>([])\r\n\r\n const activeKey = computed(() => props.value ?? '')\r\n\r\n // ====== 数据合成 ======\r\n\r\n /** 合并后的最终 MenuOption[] */\r\n const mergedOptions = computed<MenuOption[]>(() => {\r\n // 优先使用 options 直传模式\r\n if (props.options) return props.options\r\n\r\n // routes 模式 → 适配器转换\r\n if (props.routes) {\r\n const config: MenuAdapterConfig = {\r\n ...props.adapterConfig,\r\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\r\n }\r\n return createMenuOptions(props.routes, config)\r\n }\r\n\r\n return []\r\n })\r\n\r\n // ====== 展开逻辑 ======\r\n\r\n /** 从 MenuOption[] 中查找所有祖先 key */\r\n const findParentKeys = (\r\n items: MenuOption[],\r\n targetKey: string,\r\n parentKeys: string[] = []\r\n ): string[] => {\r\n for (const item of items) {\r\n const children = item.children as MenuOption[] | undefined\r\n if (children?.length) {\r\n const currentKeys = [...parentKeys, item.key as string]\r\n if (children.some(child => child.key === targetKey)) {\r\n return currentKeys\r\n }\r\n const result = findParentKeys(children, targetKey, currentKeys)\r\n if (result.length > 0) return result\r\n }\r\n }\r\n return []\r\n }\r\n\r\n /** 通过路径段推导展开 key + 查找祖先 key */\r\n const computeExpandedKeys = (path: string): Set<string> => {\r\n const segments = path.split('/').filter(Boolean)\r\n const keys = new Set<string>()\r\n let currentPath = ''\r\n segments.forEach(segment => {\r\n currentPath += `/${segment}`\r\n keys.add(currentPath)\r\n })\r\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\r\n return keys\r\n }\r\n\r\n // ====== 事件处理 ======\r\n\r\n const handleMenuClick = (key: string) => {\r\n if (key && key !== activeKey.value) {\r\n emit('select', key)\r\n }\r\n }\r\n\r\n const onExpandedKeysChange = (keys: string[]) => {\r\n expandedKeys.value = keys\r\n emit('update:expandedKeys', keys)\r\n }\r\n\r\n // ====== 暴露方法 ======\r\n const showOption = (key: string) => menuRef.value?.showOption(key)\r\n defineExpose({ showOption, menuRef })\r\n\r\n // ====== 路由跟踪 ======\r\n\r\n watch(\r\n () => activeKey.value,\r\n newKey => {\r\n if (!newKey) return\r\n const newKeys = computeExpandedKeys(newKey)\r\n expandedKeys.value = Array.from(\r\n new Set([...expandedKeys.value, ...newKeys])\r\n )\r\n nextTick(() => menuRef.value?.showOption(newKey))\r\n },\r\n { immediate: true }\r\n )\r\n\r\n onMounted(() => {\r\n nextTick(() => {\r\n if (activeKey.value) {\r\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\r\n menuRef.value?.showOption(activeKey.value)\r\n }\r\n })\r\n })\r\n</script>\r\n","<!--\r\n * @Description: 通用菜单组件 — 基于 NMenu 的树形导航菜单\r\n *\r\n * 支持两种数据输入模式:\r\n * 1. `options` — 直传 NMenu 原生 MenuOption[](零转换,高级用户)\r\n * 2. `routes` — 传入路由数据,自动通过内置适配器转换为 MenuOption[]\r\n *\r\n * @example 路由模式(推荐)\r\n * ```vue\r\n * <C_Menu\r\n * :routes=\"menuData\"\r\n * :label-formatter=\"$t\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * @example 原生模式\r\n * ```vue\r\n * <C_Menu\r\n * :options=\"nMenuOptions\"\r\n * @select=\"router.push\"\r\n * />\r\n * ```\r\n *\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NMenu\r\n ref=\"menuRef\"\r\n :options=\"mergedOptions\"\r\n :expanded-keys=\"expandedKeys\"\r\n :value=\"activeKey\"\r\n :mode=\"mode\"\r\n :collapsed=\"collapsed\"\r\n :collapsed-width=\"collapsedWidth\"\r\n :collapsed-icon-size=\"collapsedIconSize\"\r\n :inverted=\"inverted\"\r\n :theme-overrides=\"themeOverrides\"\r\n :dropdown-props=\"dropdownProps\"\r\n :indent=\"indent\"\r\n :root-indent=\"rootIndent\"\r\n @update:value=\"handleMenuClick\"\r\n @update:expanded-keys=\"onExpandedKeysChange\"\r\n />\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n import { ref, computed, watch, nextTick, onMounted } from 'vue'\r\n import {\r\n type MenuOption,\r\n type MenuInst,\r\n type DropdownProps,\r\n } from 'naive-ui/es'\r\n import {\r\n createMenuOptions,\r\n type RouteItem,\r\n type MenuAdapterConfig,\r\n } from '../_shared'\r\n\r\n defineOptions({ name: 'C_Menu' })\r\n\r\n const props = withDefaults(\r\n defineProps<{\r\n /**\r\n * 模式一:直传 NMenu 原生 MenuOption[]\r\n * 与 `routes` 二选一,`options` 优先级更高\r\n */\r\n options?: MenuOption[]\r\n /**\r\n * 模式二:传入路由数据,自动转换为 MenuOption[]\r\n * 兼容 Vue Router RouteRecordRaw / 后端 JSON 路由表\r\n */\r\n routes?: RouteItem[]\r\n /**\r\n * 适配器配置(仅 routes 模式生效)\r\n * 可自定义 key/label/icon 取值、过滤逻辑等\r\n */\r\n adapterConfig?: MenuAdapterConfig\r\n /**\r\n * 标签文本格式化函数(routes 模式的快捷方式)\r\n * 等同于 `adapterConfig.labelFormatter`,但优先级更高\r\n */\r\n labelFormatter?: (label: string) => string\r\n /** 当前激活的菜单 key */\r\n value?: string\r\n /** 菜单模式 */\r\n mode?: 'vertical' | 'horizontal'\r\n /** 是否折叠 */\r\n collapsed?: boolean\r\n /** 折叠宽度 */\r\n collapsedWidth?: number\r\n /** 折叠图标大小 */\r\n collapsedIconSize?: number\r\n /** 是否反色 */\r\n inverted?: boolean\r\n /** 主题覆盖 */\r\n themeOverrides?: Record<string, any>\r\n /** 缩进像素 */\r\n indent?: number\r\n /** 根缩进像素 */\r\n rootIndent?: number\r\n /** 下拉菜单属性(折叠模式) */\r\n dropdownProps?: DropdownProps\r\n }>(),\r\n {\r\n mode: 'vertical',\r\n collapsed: false,\r\n collapsedWidth: 64,\r\n collapsedIconSize: 22,\r\n inverted: false,\r\n indent: 24,\r\n rootIndent: 16,\r\n dropdownProps: () => ({\r\n placement: 'right-start' as const,\r\n trigger: 'hover' as const,\r\n arrowStyle: { color: 'var(--n-color)' },\r\n }),\r\n }\r\n )\r\n\r\n const emit = defineEmits<{\r\n /** 菜单项被选中 */\r\n select: [key: string]\r\n /** 展开项变化 */\r\n 'update:expandedKeys': [keys: string[]]\r\n }>()\r\n\r\n const menuRef = ref<MenuInst | null>(null)\r\n const expandedKeys = ref<string[]>([])\r\n\r\n const activeKey = computed(() => props.value ?? '')\r\n\r\n // ====== 数据合成 ======\r\n\r\n /** 合并后的最终 MenuOption[] */\r\n const mergedOptions = computed<MenuOption[]>(() => {\r\n // 优先使用 options 直传模式\r\n if (props.options) return props.options\r\n\r\n // routes 模式 → 适配器转换\r\n if (props.routes) {\r\n const config: MenuAdapterConfig = {\r\n ...props.adapterConfig,\r\n ...(props.labelFormatter && { labelFormatter: props.labelFormatter }),\r\n }\r\n return createMenuOptions(props.routes, config)\r\n }\r\n\r\n return []\r\n })\r\n\r\n // ====== 展开逻辑 ======\r\n\r\n /** 从 MenuOption[] 中查找所有祖先 key */\r\n const findParentKeys = (\r\n items: MenuOption[],\r\n targetKey: string,\r\n parentKeys: string[] = []\r\n ): string[] => {\r\n for (const item of items) {\r\n const children = item.children as MenuOption[] | undefined\r\n if (children?.length) {\r\n const currentKeys = [...parentKeys, item.key as string]\r\n if (children.some(child => child.key === targetKey)) {\r\n return currentKeys\r\n }\r\n const result = findParentKeys(children, targetKey, currentKeys)\r\n if (result.length > 0) return result\r\n }\r\n }\r\n return []\r\n }\r\n\r\n /** 通过路径段推导展开 key + 查找祖先 key */\r\n const computeExpandedKeys = (path: string): Set<string> => {\r\n const segments = path.split('/').filter(Boolean)\r\n const keys = new Set<string>()\r\n let currentPath = ''\r\n segments.forEach(segment => {\r\n currentPath += `/${segment}`\r\n keys.add(currentPath)\r\n })\r\n findParentKeys(mergedOptions.value, path).forEach(key => keys.add(key))\r\n return keys\r\n }\r\n\r\n // ====== 事件处理 ======\r\n\r\n const handleMenuClick = (key: string) => {\r\n if (key && key !== activeKey.value) {\r\n emit('select', key)\r\n }\r\n }\r\n\r\n const onExpandedKeysChange = (keys: string[]) => {\r\n expandedKeys.value = keys\r\n emit('update:expandedKeys', keys)\r\n }\r\n\r\n // ====== 暴露方法 ======\r\n const showOption = (key: string) => menuRef.value?.showOption(key)\r\n defineExpose({ showOption, menuRef })\r\n\r\n // ====== 路由跟踪 ======\r\n\r\n watch(\r\n () => activeKey.value,\r\n newKey => {\r\n if (!newKey) return\r\n const newKeys = computeExpandedKeys(newKey)\r\n expandedKeys.value = Array.from(\r\n new Set([...expandedKeys.value, ...newKeys])\r\n )\r\n nextTick(() => menuRef.value?.showOption(newKey))\r\n },\r\n { immediate: true }\r\n )\r\n\r\n onMounted(() => {\r\n nextTick(() => {\r\n if (activeKey.value) {\r\n expandedKeys.value = Array.from(computeExpandedKeys(activeKey.value))\r\n menuRef.value?.showOption(activeKey.value)\r\n }\r\n })\r\n })\r\n</script>\r\n"],"mappings":"ksBE6DE,IAAM,EAAQ,EA2DR,EAAO,EAOP,EAAU,EAAqB,KAAK,CACpC,EAAe,EAAc,EAAE,CAAC,CAEhC,EAAY,MAAe,EAAM,OAAS,GAAG,CAK7C,EAAgB,MAA6B,CAEjD,GAAI,EAAM,QAAS,OAAO,EAAM,QAGhC,GAAI,EAAM,OAAQ,CAChB,IAAM,EAA4B,CAChC,GAAG,EAAM,cACT,GAAI,EAAM,gBAAkB,CAAE,eAAgB,EAAM,eAAgB,CACrE,CACD,OAAO,EAAkB,EAAM,OAAQ,EAAO,CAGhD,MAAO,EAAE,EACT,CAKI,GACJ,EACA,EACA,EAAuB,EAAE,GACZ,CACb,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAW,EAAK,SACtB,GAAI,GAAU,OAAQ,CACpB,IAAM,EAAc,CAAC,GAAG,EAAY,EAAK,IAAc,CACvD,GAAI,EAAS,KAAK,GAAS,EAAM,MAAQ,EAAU,CACjD,OAAO,EAET,IAAM,EAAS,EAAe,EAAU,EAAW,EAAY,CAC/D,GAAI,EAAO,OAAS,EAAG,OAAO,GAGlC,MAAO,EAAE,EAIL,EAAuB,GAA8B,CACzD,IAAM,EAAW,EAAK,MAAM,IAAI,CAAC,OAAO,QAAQ,CAC1C,EAAO,IAAI,IACb,EAAc,GAMlB,OALA,EAAS,QAAQ,GAAW,CAC1B,GAAe,IAAI,IACnB,EAAK,IAAI,EAAY,EACrB,CACF,EAAe,EAAc,MAAO,EAAK,CAAC,QAAQ,GAAO,EAAK,IAAI,EAAI,CAAC,CAChE,GAKH,EAAmB,GAAgB,CACnC,GAAO,IAAQ,EAAU,OAC3B,EAAK,SAAU,EAAI,EAIjB,EAAwB,GAAmB,CAC/C,EAAa,MAAQ,EACrB,EAAK,sBAAuB,EAAK,SAKnC,EAAa,CAAE,WADK,GAAgB,EAAQ,OAAO,WAAW,EAAI,CACvC,UAAS,CAAC,CAIrC,MACQ,EAAU,MAChB,GAAU,CACR,GAAI,CAAC,EAAQ,OACb,IAAM,EAAU,EAAoB,EAAO,CAC3C,EAAa,MAAQ,MAAM,KACzB,IAAI,IAAI,CAAC,GAAG,EAAa,MAAO,GAAG,EAAQ,CAAC,CAC7C,CACD,MAAe,EAAQ,OAAO,WAAW,EAAO,CAAC,EAEnD,CAAE,UAAW,GAAM,CACpB,CAED,MAAgB,CACd,MAAe,CACT,EAAU,QACZ,EAAa,MAAQ,MAAM,KAAK,EAAoB,EAAU,MAAM,CAAC,CACrE,EAAQ,OAAO,WAAW,EAAU,MAAM,GAE5C,EACF,4BAtMF,EAgBE,EAAA,SAfI,UAAJ,IAAI,EACH,QAAS,EAAA,MACT,gBAAe,EAAA,MACf,MAAO,EAAA,MACP,KAAMA,EAAAA,KACN,UAAWC,EAAAA,UACX,kBAAiBC,EAAAA,eACjB,sBAAqBC,EAAAA,kBACrB,SAAUC,EAAAA,SACV,kBAAiBC,EAAAA,eACjB,iBAAgBC,EAAAA,cAChB,OAAQC,EAAAA,OACR,cAAaC,EAAAA,WACb,iBAAc,EACd,wBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"C_Table-Dcam_tRs.css","names":[],"sources":["../src/components/C_Table/components/ColumnSettings/index.vue?vue&type=style&index=0&scoped=1a3d4df6&lang.scss","../src/components/C_Table/index.vue?vue&type=style&index=0&scoped=18a2952a&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.column-management-tab .search-row[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n.column-management-tab .search-row .search-input[data-v-1a3d4df6] {\n flex: 1;\n min-width: 0;\n}\n.column-management-tab .search-row .resizable-control[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n padding: 0 10px;\n height: 34px;\n background: var(--n-color-target);\n border-radius: 6px;\n white-space: nowrap;\n}\n.column-management-tab .top-actions-bar[data-v-1a3d4df6] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding: 10px 12px;\n background: var(--n-color-target);\n border-radius: 6px;\n}\n.column-management-tab .top-actions-bar .stats-info[data-v-1a3d4df6] {\n flex: 1;\n}\n.column-management-tab .top-actions-bar .quick-actions[data-v-1a3d4df6] {\n flex-shrink: 0;\n}\n.column-management-tab .column-list .column-item[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 4px;\n margin-bottom: 4px;\n border-radius: 4px;\n background: transparent;\n cursor: move;\n transition: all 0.2s ease;\n}\n.column-management-tab .column-list .column-item[data-v-1a3d4df6]:hover {\n background: var(--n-color-target);\n}\n.column-management-tab .column-list .column-item.disabled[data-v-1a3d4df6] {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.column-management-tab .column-list .column-item.fixed-left[data-v-1a3d4df6] {\n background: rgba(24, 160, 88, 0.08);\n}\n.column-management-tab .column-list .column-item.fixed-right[data-v-1a3d4df6] {\n background: rgba(240, 138, 0, 0.08);\n}\n.column-management-tab .column-list .column-item .column-info[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n flex: 1;\n gap: 10px;\n min-width: 0;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls .drag-handle[data-v-1a3d4df6] {\n color: var(--n-text-color-3);\n cursor: grab;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls .drag-handle[data-v-1a3d4df6]:active {\n cursor: grabbing;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls .drag-handle.disabled[data-v-1a3d4df6] {\n cursor: not-allowed;\n opacity: 0.3;\n}\n.column-management-tab .column-list .column-item .column-info .column-details[data-v-1a3d4df6] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n}\n.column-management-tab .column-list .column-item .column-actions[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls[data-v-1a3d4df6] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls .c-icon[data-v-1a3d4df6] {\n opacity: 0.6;\n transition: all 0.2s ease;\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls .c-icon[data-v-1a3d4df6]:not(.disabled):hover {\n opacity: 1;\n transform: scale(1.15);\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls .c-icon.disabled[data-v-1a3d4df6] {\n opacity: 0.3;\n cursor: not-allowed;\n}\n.column-management-tab .column-list .column-item .column-actions .c-icon[data-v-1a3d4df6] {\n transition: all 0.2s ease;\n}\n.column-management-tab .column-list .column-item .column-actions .c-icon[data-v-1a3d4df6]:hover {\n transform: scale(1.15);\n}","/* unplugin-vue-components disabled *//* ================= 1. 组件根容器 ================= */\n.c-table-wrapper[data-v-18a2952a] {\n width: 100%;\n position: relative;\n}\n\n/* ================= 2. 表格工具栏 ================= */\n.table-toolbar[data-v-18a2952a] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n.table-toolbar .toolbar-left[data-v-18a2952a] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.table-toolbar .toolbar-right[data-v-18a2952a] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.table-toolbar .toolbar-right .column-settings-btn[data-v-18a2952a] {\n opacity: 0.6;\n transition: opacity 0.2s ease, transform 0.2s ease;\n cursor: pointer;\n}\n.table-toolbar .toolbar-right .column-settings-btn[data-v-18a2952a]:hover {\n opacity: 1;\n transform: scale(1.1);\n}\n\n/* ================= 3. 动态行工具栏 ================= */\n.dynamic-rows-toolbar[data-v-18a2952a] {\n margin-bottom: 16px;\n padding: 16px;\n border-radius: 8px;\n border: 1px solid var(--n-border-color);\n background-color: var(--n-card-color);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n transition: all 0.2s ease;\n}\n.dynamic-rows-toolbar[data-v-18a2952a]:hover {\n border-color: var(--n-primary-color-suppl);\n}\n\n/* ================= 4. 表格布局基础 ================= */\n[data-v-18a2952a] .n-data-table {\n table-layout: auto !important;\n width: 100% !important;\n border-radius: 8px;\n overflow: hidden;\n}\n\n/* 确保表格容器允许横向滚动(固定列必需) */\n[data-v-18a2952a] .n-data-table-wrapper {\n overflow-x: auto !important;\n}\n\n/* 分页组件样式 */\n.pagination-wrapper[data-v-18a2952a] {\n margin-top: 16px;\n display: flex;\n justify-content: flex-end;\n}\n\n/* ================= 5. 表格列宽度管理 ================= */\n/* 普通列的最小宽度保护 */\n[data-v-18a2952a] .n-data-table-th:not(:last-child) {\n min-width: 80px !important;\n}\n\n/* 操作列固定宽度(适应4个按钮的布局) */\n[data-v-18a2952a] .n-data-table-th:last-child {\n width: 180px !important;\n min-width: 160px !important;\n max-width: 200px !important;\n text-align: center !important;\n padding: 8px 12px !important;\n}\n\n/* 操作按钮容器居中 */\n[data-v-18a2952a] .n-data-table-td:last-child .n-space {\n justify-content: center !important;\n align-items: center !important;\n}\n\n/* ================= 6. 可编辑单元格样式 ================= */\n/* 单元格编辑容器 - 精致化设计 */\n.cell-edit-wrapper[data-v-18a2952a] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 6px 8px;\n min-height: 36px;\n border-radius: 4px;\n transition: all 0.2s ease;\n cursor: pointer;\n position: relative;\n /* 悬停效果 */\n /* 激活状态 */\n}\n.cell-edit-wrapper[data-v-18a2952a]:hover {\n background-color: var(--n-color-target);\n}\n.cell-edit-wrapper:hover .cell-edit-icon[data-v-18a2952a] {\n opacity: 1;\n color: var(--n-color-success);\n transform: scale(1.05);\n}\n.cell-edit-wrapper[data-v-18a2952a]:active {\n transform: translateY(1px);\n}\n\n/* 单元格内容 */\n.cell-value[data-v-18a2952a] {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 16px;\n line-height: 1.4;\n font-size: 13px;\n}\n\n/* 编辑图标 - 使用全局选择器确保动态创建的元素也能被选中 */\n.cell-edit-icon[data-v-18a2952a],.cell-edit-icon {\n flex-shrink: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n cursor: pointer;\n margin-left: 4px;\n transition: all 0.25s ease;\n color: var(--n-color-success);\n opacity: 0;\n position: relative;\n /* 悬停效果 */\n /* 激活效果 */\n /* 图标尺寸 */\n}\n.cell-edit-icon[data-v-18a2952a]:hover,.cell-edit-icon {\n opacity: 1 !important;\n color: var(--n-color-success-hover) !important;\n background-color: var(--n-color-success-pressed);\n transform: scale(1.1);\n}\n.cell-edit-icon[data-v-18a2952a]:active,.cell-edit-icon {\n transform: scale(0.95);\n}\n.cell-edit-icon svg[data-v-18a2952a],.cell-edit-icon {\n width: 14px;\n height: 14px;\n}\n\n/* ================= 7. 单元格编辑状态样式 ================= */\n/* 编辑容器 - 确保水平布局不换行 */\n.cell-editing-container[data-v-18a2952a],[data-v-18a2952a] .cell-editing-container {\n display: flex !important;\n align-items: center !important;\n width: 100% !important;\n min-height: 36px !important;\n background-color: var(--n-input-color) !important;\n border: 1px solid var(--n-primary-color) !important;\n border-radius: 6px !important;\n padding: 4px 6px !important;\n box-shadow: 0 0 0 2px var(--n-primary-color-suppl) !important;\n gap: 8px !important;\n}\n\n/* 编辑输入区域 - 占据剩余空间 */\n.cell-editing-input[data-v-18a2952a],[data-v-18a2952a] .cell-editing-input {\n flex: 1 !important;\n min-width: 0 !important;\n}\n\n/* 深度选择器处理输入框样式 */\n[data-v-18a2952a] .cell-editing-input .n-input {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n[data-v-18a2952a] .cell-editing-input .n-input.n-input--focus {\n border: none !important;\n box-shadow: none !important;\n}\n[data-v-18a2952a] .cell-editing-input .n-input-number {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n[data-v-18a2952a] .cell-editing-input .n-select {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n[data-v-18a2952a] .cell-editing-input .n-select .n-base-selection {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n\n/* 编辑操作按钮容器 */\n.cell-editing-actions[data-v-18a2952a],[data-v-18a2952a] .cell-editing-actions {\n display: flex !important;\n flex-shrink: 0 !important;\n align-items: center !important;\n gap: 4px !important;\n background: var(--n-color-popover) !important;\n border: 1px solid var(--n-border-color) !important;\n border-radius: 4px !important;\n padding: 2px !important;\n box-shadow: var(--n-box-shadow) !important;\n}\n\n/* 编辑操作按钮 */\n.cell-action-btn[data-v-18a2952a],[data-v-18a2952a] .cell-action-btn {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n width: 20px !important;\n height: 20px !important;\n border: none !important;\n border-radius: 3px !important;\n cursor: pointer !important;\n transition: all 0.2s ease !important;\n background: transparent !important;\n /* 保存按钮 */\n /* 取消按钮 */\n /* 图标尺寸 */\n}\n.cell-action-btn.cell-action-save[data-v-18a2952a],[data-v-18a2952a] .cell-action-btn.cell-action-save {\n color: var(--n-color-success) !important;\n}\n.cell-action-btn.cell-action-save[data-v-18a2952a]:hover,[data-v-18a2952a] .cell-action-btn.cell-action-save:hover {\n background: var(--n-color-success-pressed) !important;\n transform: scale(1.1) !important;\n}\n.cell-action-btn.cell-action-cancel[data-v-18a2952a],[data-v-18a2952a] .cell-action-btn.cell-action-cancel {\n color: var(--n-color-error) !important;\n}\n.cell-action-btn.cell-action-cancel[data-v-18a2952a]:hover,[data-v-18a2952a] .cell-action-btn.cell-action-cancel:hover {\n background: var(--n-color-error-pressed) !important;\n transform: scale(1.1) !important;\n}\n.cell-action-btn svg[data-v-18a2952a],[data-v-18a2952a] .cell-action-btn svg {\n width: 11px !important;\n height: 11px !important;\n}\n\n/* ================= 7. 操作按钮样式 ================= */\n/* 通用按钮样式 */\n[data-v-18a2952a] .n-button {\n white-space: nowrap;\n font-size: 12px !important;\n flex-shrink: 0 !important;\n border-radius: 6px;\n transition: all 0.2s ease;\n font-weight: 500;\n /* 图标间距 */\n /* 小尺寸按钮优化 */\n /* 四元按钮样式 */\n}\n[data-v-18a2952a] .n-button .n-icon {\n margin-right: 4px !important;\n}\n[data-v-18a2952a] .n-button.n-button--small-type {\n padding: 4px 8px;\n height: 28px;\n line-height: 20px;\n}\n[data-v-18a2952a] .n-button.n-button--quaternary-type:hover {\n transform: translateY(-1px);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n[data-v-18a2952a] .n-button.n-button--quaternary-type:active {\n transform: translateY(0);\n}\n\n/* ================= 8. 按钮组间距优化 ================= */\n/* 操作按钮组 */\n[data-v-18a2952a] .n-space.n-space--horizontal .n-space-item:not(:last-child) {\n margin-right: 6px !important;\n}\n\n/* ================= 9. 模态框样式优化 ================= */\n/* 编辑模态框 */\n[data-v-18a2952a] .n-modal .n-card {\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);\n}\n[data-v-18a2952a] .n-modal .n-card__header {\n padding: 20px 24px 16px;\n font-weight: 600;\n font-size: 16px;\n}\n[data-v-18a2952a] .n-modal .n-card__content {\n padding: 0 24px 16px;\n}\n[data-v-18a2952a] .n-modal .n-card__action {\n padding: 16px 24px 20px;\n border-top: 1px solid var(--n-divider-color);\n}\n\n/* ================= 10. 描述列表样式 ================= */\n/* 详情模态框描述列表 */\n[data-v-18a2952a] .n-descriptions .n-descriptions-item-label {\n font-weight: 500;\n color: var(--n-text-color-2);\n}\n[data-v-18a2952a] .n-descriptions .n-descriptions-item-content {\n color: var(--n-text-color-1);\n word-break: break-all;\n}\n\n/* ================= 11. 响应式设计 ================= */\n/* 移动端适配 */\n@media (max-width: 768px) {\n.c-table-wrapper[data-v-18a2952a] {\n overflow-x: auto;\n}\n /* 操作列在移动端的适配 */\n[data-v-18a2952a] .n-data-table-th:last-child {\n width: 160px !important;\n min-width: 160px !important;\n}\n /* 按钮在移动端的优化 */\n[data-v-18a2952a] .n-button {\n font-size: 11px !important;\n padding: 3px 6px;\n}\n[data-v-18a2952a] .n-button .n-icon {\n margin-right: 2px !important;\n}\n /* 单元格编辑图标在移动端始终显示 */\n.cell-edit-icon[data-v-18a2952a] {\n opacity: 0.6;\n}\n}\n/* ================= 12. 主题适配 ================= */\n/* 暗色主题适配 - 已通过 CSS 变量自动适配 */\n[data-theme=dark][data-v-18a2952a] {\n /* CSS 变量已自动适配,如有特殊需求可在此添加 */\n}\n\n/* ================= 13. 响应式优化 ================= */\n/* 小屏幕下的单元格编辑优化 */\n@media (max-width: 768px) {\n.cell-editing-container[data-v-18a2952a],[data-v-18a2952a] .cell-editing-container {\n gap: 6px !important;\n}\n.cell-editing-actions[data-v-18a2952a],[data-v-18a2952a] .cell-editing-actions {\n gap: 3px !important;\n padding: 2px !important;\n}\n.cell-action-btn[data-v-18a2952a],[data-v-18a2952a] .cell-action-btn {\n width: 18px !important;\n height: 18px !important;\n}\n.cell-action-btn svg[data-v-18a2952a],[data-v-18a2952a] .cell-action-btn svg {\n width: 10px !important;\n height: 10px !important;\n}\n /* 移动端编辑图标 */\n.cell-edit-icon[data-v-18a2952a] {\n opacity: 0.6 !important;\n}\n.cell-value[data-v-18a2952a] {\n margin-right: 12px !important;\n}\n}\n/* ================= 14. 性能优化 ================= */\n/* 使用 GPU 加速的动画 */\n.cell-edit-icon[data-v-18a2952a],\n.cell-action-btn[data-v-18a2952a] {\n will-change: transform;\n backface-visibility: hidden;\n}\n[data-v-18a2952a] .n-button {\n will-change: transform;\n backface-visibility: hidden;\n}\n\n/* ================= 15. 可访问性优化 ================= */\n/* 聚焦状态 */\n.cell-edit-wrapper[data-v-18a2952a]:focus-within,\n.cell-editing-container[data-v-18a2952a]:focus-within {\n outline: 2px solid var(--n-primary-color);\n outline-offset: 2px;\n}\n\n/* 高对比度模式支持 */\n@media (prefers-contrast: high) {\n.cell-edit-icon[data-v-18a2952a] {\n opacity: 0.8;\n}\n.cell-editing-actions[data-v-18a2952a] {\n border-width: 2px;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvHA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
1
+ {"version":3,"file":"C_Table-vZlcgH6x.css","names":[],"sources":["../src/components/C_Table/components/ColumnSettings/index.vue?vue&type=style&index=0&scoped=1a3d4df6&lang.scss","../src/components/C_Table/index.vue?vue&type=style&index=0&scoped=11678369&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.column-management-tab .search-row[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 12px;\n}\n.column-management-tab .search-row .search-input[data-v-1a3d4df6] {\n flex: 1;\n min-width: 0;\n}\n.column-management-tab .search-row .resizable-control[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n padding: 0 10px;\n height: 34px;\n background: var(--n-color-target);\n border-radius: 6px;\n white-space: nowrap;\n}\n.column-management-tab .top-actions-bar[data-v-1a3d4df6] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding: 10px 12px;\n background: var(--n-color-target);\n border-radius: 6px;\n}\n.column-management-tab .top-actions-bar .stats-info[data-v-1a3d4df6] {\n flex: 1;\n}\n.column-management-tab .top-actions-bar .quick-actions[data-v-1a3d4df6] {\n flex-shrink: 0;\n}\n.column-management-tab .column-list .column-item[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 4px;\n margin-bottom: 4px;\n border-radius: 4px;\n background: transparent;\n cursor: move;\n transition: all 0.2s ease;\n}\n.column-management-tab .column-list .column-item[data-v-1a3d4df6]:hover {\n background: var(--n-color-target);\n}\n.column-management-tab .column-list .column-item.disabled[data-v-1a3d4df6] {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.column-management-tab .column-list .column-item.fixed-left[data-v-1a3d4df6] {\n background: rgba(24, 160, 88, 0.08);\n}\n.column-management-tab .column-list .column-item.fixed-right[data-v-1a3d4df6] {\n background: rgba(240, 138, 0, 0.08);\n}\n.column-management-tab .column-list .column-item .column-info[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n flex: 1;\n gap: 10px;\n min-width: 0;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls .drag-handle[data-v-1a3d4df6] {\n color: var(--n-text-color-3);\n cursor: grab;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls .drag-handle[data-v-1a3d4df6]:active {\n cursor: grabbing;\n}\n.column-management-tab .column-list .column-item .column-info .column-controls .drag-handle.disabled[data-v-1a3d4df6] {\n cursor: not-allowed;\n opacity: 0.3;\n}\n.column-management-tab .column-list .column-item .column-info .column-details[data-v-1a3d4df6] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n}\n.column-management-tab .column-list .column-item .column-actions[data-v-1a3d4df6] {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls[data-v-1a3d4df6] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls .c-icon[data-v-1a3d4df6] {\n opacity: 0.6;\n transition: all 0.2s ease;\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls .c-icon[data-v-1a3d4df6]:not(.disabled):hover {\n opacity: 1;\n transform: scale(1.15);\n}\n.column-management-tab .column-list .column-item .column-actions .drag-controls .c-icon.disabled[data-v-1a3d4df6] {\n opacity: 0.3;\n cursor: not-allowed;\n}\n.column-management-tab .column-list .column-item .column-actions .c-icon[data-v-1a3d4df6] {\n transition: all 0.2s ease;\n}\n.column-management-tab .column-list .column-item .column-actions .c-icon[data-v-1a3d4df6]:hover {\n transform: scale(1.15);\n}","/* unplugin-vue-components disabled *//* ================= 1. 组件根容器 ================= */\n.c-table-wrapper[data-v-11678369] {\n width: 100%;\n position: relative;\n}\n\n/* ================= 2. 表格工具栏 ================= */\n.table-toolbar[data-v-11678369] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n}\n.table-toolbar .toolbar-left[data-v-11678369] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n.table-toolbar .toolbar-right[data-v-11678369] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.table-toolbar .toolbar-right .column-settings-btn[data-v-11678369] {\n opacity: 0.6;\n transition: opacity 0.2s ease, transform 0.2s ease;\n cursor: pointer;\n}\n.table-toolbar .toolbar-right .column-settings-btn[data-v-11678369]:hover {\n opacity: 1;\n transform: scale(1.1);\n}\n\n/* ================= 3. 动态行工具栏 ================= */\n.dynamic-rows-toolbar[data-v-11678369] {\n margin-bottom: 16px;\n padding: 16px;\n border-radius: 8px;\n border: 1px solid var(--n-border-color);\n background-color: var(--n-card-color);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n transition: all 0.2s ease;\n}\n.dynamic-rows-toolbar[data-v-11678369]:hover {\n border-color: var(--n-primary-color-suppl);\n}\n\n/* ================= 4. 表格布局基础 ================= */\n[data-v-11678369] .n-data-table {\n table-layout: auto !important;\n width: 100% !important;\n border-radius: 8px;\n overflow: hidden;\n}\n\n/* 确保表格容器允许横向滚动(固定列必需) */\n[data-v-11678369] .n-data-table-wrapper {\n overflow-x: auto !important;\n}\n\n/* 分页组件样式 */\n.pagination-wrapper[data-v-11678369] {\n margin-top: 16px;\n display: flex;\n justify-content: flex-end;\n}\n\n/* ================= 5. 表格列宽度管理 ================= */\n/* 普通列的最小宽度保护 */\n[data-v-11678369] .n-data-table-th:not(:last-child) {\n min-width: 80px !important;\n}\n\n/* 操作列固定宽度(适应4个按钮的布局) */\n[data-v-11678369] .n-data-table-th:last-child {\n width: 180px !important;\n min-width: 160px !important;\n max-width: 200px !important;\n text-align: center !important;\n padding: 8px 12px !important;\n}\n\n/* 操作按钮容器居中 */\n[data-v-11678369] .n-data-table-td:last-child .n-space {\n justify-content: center !important;\n align-items: center !important;\n}\n\n/* ================= 6. 可编辑单元格样式 ================= */\n/* 单元格编辑容器 - 精致化设计 */\n.cell-edit-wrapper[data-v-11678369] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 6px 8px;\n min-height: 36px;\n border-radius: 4px;\n transition: all 0.2s ease;\n cursor: pointer;\n position: relative;\n /* 悬停效果 */\n /* 激活状态 */\n}\n.cell-edit-wrapper[data-v-11678369]:hover {\n background-color: var(--n-color-target);\n}\n.cell-edit-wrapper:hover .cell-edit-icon[data-v-11678369] {\n opacity: 1;\n color: var(--n-color-success);\n transform: scale(1.05);\n}\n.cell-edit-wrapper[data-v-11678369]:active {\n transform: translateY(1px);\n}\n\n/* 单元格内容 */\n.cell-value[data-v-11678369] {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 16px;\n line-height: 1.4;\n font-size: 13px;\n}\n\n/* 编辑图标 - 使用全局选择器确保动态创建的元素也能被选中 */\n.cell-edit-icon[data-v-11678369],.cell-edit-icon {\n flex-shrink: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n cursor: pointer;\n margin-left: 4px;\n transition: all 0.25s ease;\n color: var(--n-color-success);\n opacity: 0;\n position: relative;\n /* 悬停效果 */\n /* 激活效果 */\n /* 图标尺寸 */\n}\n.cell-edit-icon[data-v-11678369]:hover,.cell-edit-icon {\n opacity: 1 !important;\n color: var(--n-color-success-hover) !important;\n background-color: var(--n-color-success-pressed);\n transform: scale(1.1);\n}\n.cell-edit-icon[data-v-11678369]:active,.cell-edit-icon {\n transform: scale(0.95);\n}\n.cell-edit-icon svg[data-v-11678369],.cell-edit-icon {\n width: 14px;\n height: 14px;\n}\n\n/* ================= 7. 单元格编辑状态样式 ================= */\n/* 编辑容器 - 确保水平布局不换行 */\n.cell-editing-container[data-v-11678369],[data-v-11678369] .cell-editing-container {\n display: flex !important;\n align-items: center !important;\n width: 100% !important;\n min-height: 36px !important;\n background-color: var(--n-input-color) !important;\n border: 1px solid var(--n-primary-color) !important;\n border-radius: 6px !important;\n padding: 4px 6px !important;\n box-shadow: 0 0 0 2px var(--n-primary-color-suppl) !important;\n gap: 8px !important;\n}\n\n/* 编辑输入区域 - 占据剩余空间 */\n.cell-editing-input[data-v-11678369],[data-v-11678369] .cell-editing-input {\n flex: 1 !important;\n min-width: 0 !important;\n}\n\n/* 深度选择器处理输入框样式 */\n[data-v-11678369] .cell-editing-input .n-input {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n[data-v-11678369] .cell-editing-input .n-input.n-input--focus {\n border: none !important;\n box-shadow: none !important;\n}\n[data-v-11678369] .cell-editing-input .n-input-number {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n[data-v-11678369] .cell-editing-input .n-select {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n[data-v-11678369] .cell-editing-input .n-select .n-base-selection {\n border: none !important;\n box-shadow: none !important;\n background: transparent !important;\n}\n\n/* 编辑操作按钮容器 */\n.cell-editing-actions[data-v-11678369],[data-v-11678369] .cell-editing-actions {\n display: flex !important;\n flex-shrink: 0 !important;\n align-items: center !important;\n gap: 4px !important;\n background: var(--n-color-popover) !important;\n border: 1px solid var(--n-border-color) !important;\n border-radius: 4px !important;\n padding: 2px !important;\n box-shadow: var(--n-box-shadow) !important;\n}\n\n/* 编辑操作按钮 */\n.cell-action-btn[data-v-11678369],[data-v-11678369] .cell-action-btn {\n display: flex !important;\n align-items: center !important;\n justify-content: center !important;\n width: 20px !important;\n height: 20px !important;\n border: none !important;\n border-radius: 3px !important;\n cursor: pointer !important;\n transition: all 0.2s ease !important;\n background: transparent !important;\n /* 保存按钮 */\n /* 取消按钮 */\n /* 图标尺寸 */\n}\n.cell-action-btn.cell-action-save[data-v-11678369],[data-v-11678369] .cell-action-btn.cell-action-save {\n color: var(--n-color-success) !important;\n}\n.cell-action-btn.cell-action-save[data-v-11678369]:hover,[data-v-11678369] .cell-action-btn.cell-action-save:hover {\n background: var(--n-color-success-pressed) !important;\n transform: scale(1.1) !important;\n}\n.cell-action-btn.cell-action-cancel[data-v-11678369],[data-v-11678369] .cell-action-btn.cell-action-cancel {\n color: var(--n-color-error) !important;\n}\n.cell-action-btn.cell-action-cancel[data-v-11678369]:hover,[data-v-11678369] .cell-action-btn.cell-action-cancel:hover {\n background: var(--n-color-error-pressed) !important;\n transform: scale(1.1) !important;\n}\n.cell-action-btn svg[data-v-11678369],[data-v-11678369] .cell-action-btn svg {\n width: 11px !important;\n height: 11px !important;\n}\n\n/* ================= 7. 操作按钮样式 ================= */\n/* 通用按钮样式 */\n[data-v-11678369] .n-button {\n white-space: nowrap;\n font-size: 12px !important;\n flex-shrink: 0 !important;\n border-radius: 6px;\n transition: all 0.2s ease;\n font-weight: 500;\n /* 图标间距 */\n /* 小尺寸按钮优化 */\n /* 四元按钮样式 */\n}\n[data-v-11678369] .n-button .n-icon {\n margin-right: 4px !important;\n}\n[data-v-11678369] .n-button.n-button--small-type {\n padding: 4px 8px;\n height: 28px;\n line-height: 20px;\n}\n[data-v-11678369] .n-button.n-button--quaternary-type:hover {\n transform: translateY(-1px);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n}\n[data-v-11678369] .n-button.n-button--quaternary-type:active {\n transform: translateY(0);\n}\n\n/* ================= 8. 按钮组间距优化 ================= */\n/* 操作按钮组 */\n[data-v-11678369] .n-space.n-space--horizontal .n-space-item:not(:last-child) {\n margin-right: 6px !important;\n}\n\n/* ================= 9. 模态框样式优化 ================= */\n/* 编辑模态框 */\n[data-v-11678369] .n-modal .n-card {\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);\n}\n[data-v-11678369] .n-modal .n-card__header {\n padding: 20px 24px 16px;\n font-weight: 600;\n font-size: 16px;\n}\n[data-v-11678369] .n-modal .n-card__content {\n padding: 0 24px 16px;\n}\n[data-v-11678369] .n-modal .n-card__action {\n padding: 16px 24px 20px;\n border-top: 1px solid var(--n-divider-color);\n}\n\n/* ================= 10. 描述列表样式 ================= */\n/* 详情模态框描述列表 */\n[data-v-11678369] .n-descriptions .n-descriptions-item-label {\n font-weight: 500;\n color: var(--n-text-color-2);\n}\n[data-v-11678369] .n-descriptions .n-descriptions-item-content {\n color: var(--n-text-color-1);\n word-break: break-all;\n}\n\n/* ================= 11. 响应式设计 ================= */\n/* 移动端适配 */\n@media (max-width: 768px) {\n.c-table-wrapper[data-v-11678369] {\n overflow-x: auto;\n}\n /* 操作列在移动端的适配 */\n[data-v-11678369] .n-data-table-th:last-child {\n width: 160px !important;\n min-width: 160px !important;\n}\n /* 按钮在移动端的优化 */\n[data-v-11678369] .n-button {\n font-size: 11px !important;\n padding: 3px 6px;\n}\n[data-v-11678369] .n-button .n-icon {\n margin-right: 2px !important;\n}\n /* 单元格编辑图标在移动端始终显示 */\n.cell-edit-icon[data-v-11678369] {\n opacity: 0.6;\n}\n}\n/* ================= 12. 主题适配 ================= */\n/* 暗色主题适配 - 已通过 CSS 变量自动适配 */\n[data-theme=dark][data-v-11678369] {\n /* CSS 变量已自动适配,如有特殊需求可在此添加 */\n}\n\n/* ================= 13. 响应式优化 ================= */\n/* 小屏幕下的单元格编辑优化 */\n@media (max-width: 768px) {\n.cell-editing-container[data-v-11678369],[data-v-11678369] .cell-editing-container {\n gap: 6px !important;\n}\n.cell-editing-actions[data-v-11678369],[data-v-11678369] .cell-editing-actions {\n gap: 3px !important;\n padding: 2px !important;\n}\n.cell-action-btn[data-v-11678369],[data-v-11678369] .cell-action-btn {\n width: 18px !important;\n height: 18px !important;\n}\n.cell-action-btn svg[data-v-11678369],[data-v-11678369] .cell-action-btn svg {\n width: 10px !important;\n height: 10px !important;\n}\n /* 移动端编辑图标 */\n.cell-edit-icon[data-v-11678369] {\n opacity: 0.6 !important;\n}\n.cell-value[data-v-11678369] {\n margin-right: 12px !important;\n}\n}\n/* ================= 14. 性能优化 ================= */\n/* 使用 GPU 加速的动画 */\n.cell-edit-icon[data-v-11678369],\n.cell-action-btn[data-v-11678369] {\n will-change: transform;\n backface-visibility: hidden;\n}\n[data-v-11678369] .n-button {\n will-change: transform;\n backface-visibility: hidden;\n}\n\n/* ================= 15. 可访问性优化 ================= */\n/* 聚焦状态 */\n.cell-edit-wrapper[data-v-11678369]:focus-within,\n.cell-editing-container[data-v-11678369]:focus-within {\n outline: 2px solid var(--n-primary-color);\n outline-offset: 2px;\n}\n\n/* 高对比度模式支持 */\n@media (prefers-contrast: high) {\n.cell-edit-icon[data-v-11678369] {\n opacity: 0.8;\n}\n.cell-editing-actions[data-v-11678369] {\n border-width: 2px;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvHA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}