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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/C_Breadcrumb.cjs +1 -0
  3. package/dist/C_Breadcrumb.d.cts +2 -0
  4. package/dist/C_Breadcrumb.d.ts +3 -0
  5. package/dist/C_Breadcrumb.js +1 -0
  6. package/dist/C_Breadcrumb.js.map +1 -0
  7. package/dist/C_Breadcrumb2.js +2 -0
  8. package/dist/C_Breadcrumb2.js.map +1 -0
  9. package/dist/C_Captcha.d.cts +1 -1
  10. package/dist/C_Captcha.d.ts +1 -1
  11. package/dist/C_Cascade.d.cts +1 -1
  12. package/dist/C_Cascade.d.ts +1 -1
  13. package/dist/C_City.d.cts +1 -1
  14. package/dist/C_City.d.ts +1 -1
  15. package/dist/C_Code.d.cts +1 -1
  16. package/dist/C_Code.d.ts +1 -1
  17. package/dist/C_Date.d.cts +1 -1
  18. package/dist/C_Date.d.ts +1 -1
  19. package/dist/C_Editor.d.cts +1 -1
  20. package/dist/C_Editor.d.ts +1 -1
  21. package/dist/C_Guide.d.cts +1 -1
  22. package/dist/C_Guide.d.ts +1 -1
  23. package/dist/C_Icon.d.cts +1 -1
  24. package/dist/C_Icon.d.ts +1 -1
  25. package/dist/C_Language.d.cts +1 -1
  26. package/dist/C_Language.d.ts +1 -1
  27. package/dist/C_Markdown.d.cts +1 -1
  28. package/dist/C_Markdown.d.ts +1 -1
  29. package/dist/C_Menu.cjs +1 -0
  30. package/dist/C_Menu.d.cts +2 -0
  31. package/dist/C_Menu.d.ts +3 -0
  32. package/dist/C_Menu.js +1 -0
  33. package/dist/C_Menu.js.map +1 -0
  34. package/dist/C_Menu2.js +2 -0
  35. package/dist/C_Menu2.js.map +1 -0
  36. package/dist/C_Progress.d.cts +1 -1
  37. package/dist/C_Progress.d.ts +1 -1
  38. package/dist/C_Steps.d.cts +1 -1
  39. package/dist/C_Steps.d.ts +1 -1
  40. package/dist/C_TagsView-DX3LecK1.css.map +1 -0
  41. package/dist/C_TagsView-DqOTa8bL.css.map +1 -0
  42. package/dist/C_TagsView.cjs +1 -0
  43. package/dist/C_TagsView.css +194 -0
  44. package/dist/C_TagsView.d.cts +2 -0
  45. package/dist/C_TagsView.d.ts +3 -0
  46. package/dist/C_TagsView.js +1 -0
  47. package/dist/C_TagsView.js.map +1 -0
  48. package/dist/C_TagsView2.js +2 -0
  49. package/dist/C_TagsView2.js.map +1 -0
  50. package/dist/C_Theme.d.cts +1 -1
  51. package/dist/C_Theme.d.ts +1 -1
  52. package/dist/C_VtableGantt.d.cts +1 -1
  53. package/dist/C_VtableGantt.d.ts +1 -1
  54. package/dist/_shared.cjs +1 -0
  55. package/dist/_shared.d.cts +2 -0
  56. package/dist/_shared.d.ts +2 -0
  57. package/dist/_shared.js +1 -0
  58. package/dist/constants2.d.ts +1 -1
  59. package/dist/constants4.d.ts +3 -3
  60. package/dist/constants5.d.ts +2 -2
  61. package/dist/data.d.ts +1 -1
  62. package/dist/index.cjs +1 -1
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +40 -36
  65. package/dist/index.d.ts +44 -36
  66. package/dist/index.js +1 -1
  67. package/dist/index.js.map +1 -1
  68. package/dist/index.vue.d.ts +1 -1
  69. package/dist/index10.vue.d.ts +23 -65
  70. package/dist/index11.vue.d.ts +64 -18
  71. package/dist/index12.vue.d.ts +17 -72
  72. package/dist/index13.vue.d.ts +72 -46
  73. package/dist/index14.vue.d.ts +57 -23
  74. package/dist/index15.vue.d.ts +48 -11
  75. package/dist/index16.vue.d.ts +23 -652
  76. package/dist/index17.vue.d.ts +88 -0
  77. package/dist/index18.vue.d.ts +18 -0
  78. package/dist/index19.vue.d.ts +662 -0
  79. package/dist/index2.vue.d.ts +1 -1
  80. package/dist/index3.vue.d.ts +20 -37
  81. package/dist/index4.vue.d.ts +37 -23
  82. package/dist/index5.vue.d.ts +22 -26
  83. package/dist/index6.vue.d.ts +24 -37
  84. package/dist/index7.vue.d.ts +36 -44
  85. package/dist/index8.vue.d.ts +39 -24
  86. package/dist/index9.vue.d.ts +34 -23
  87. package/dist/menuAdapter.d.ts +167 -0
  88. package/dist/menuAdapter.js +2 -0
  89. package/dist/menuAdapter.js.map +1 -0
  90. package/dist/resolver.cjs +1 -1
  91. package/dist/resolver.cjs.map +1 -1
  92. package/dist/resolver.d.cts +1 -1
  93. package/dist/resolver.d.ts +1 -1
  94. package/dist/resolver.js +1 -1
  95. package/dist/resolver.js.map +1 -1
  96. package/dist/style.css +197 -1
  97. package/dist/useCalendarEvents.d.ts +1 -1
  98. package/dist/useCollapsePanel.d.ts +4 -4
  99. package/dist/useCropperCore.d.ts +5 -5
  100. package/dist/useDraggableLayout.d.ts +8 -8
  101. package/dist/useDynamicFormState.d.ts +8 -8
  102. package/dist/useInfiniteScroll.d.ts +2 -2
  103. package/dist/useModalEdit.d.ts +4 -4
  104. package/dist/useQRCode.d.ts +2 -2
  105. package/dist/useSignatureHistory.d.ts +2 -2
  106. package/dist/useSplitResize.d.ts +5 -5
  107. package/dist/useTimeSelection.d.ts +2 -2
  108. package/dist/useTreeOperations.d.ts +4 -4
  109. package/dist/useWorkflowValidation.d.ts +2 -2
  110. package/package.json +3 -2
@@ -0,0 +1,194 @@
1
+ /* unplugin-vue-components disabled *//* ===========================================
2
+ * 标签组件样式 - 精简版
3
+ * =========================================== */
4
+ /* --------- 标签视图容器 --------- */
5
+ .tags-view-container[data-v-66047096] {
6
+ border-bottom: 1px solid var(--color-border);
7
+ padding: 8px 16px 0;
8
+ position: relative;
9
+ width: 100%;
10
+ }
11
+
12
+ /* --------- 滚动轨道容器 --------- */
13
+ .tags-track[data-v-66047096] {
14
+ width: 100%;
15
+ overflow: hidden;
16
+ position: relative;
17
+ }
18
+
19
+ /* --------- 滚动容器 --------- */
20
+ .tags-container[data-v-66047096] {
21
+ display: flex;
22
+ overflow-x: auto;
23
+ overflow-y: hidden;
24
+ scrollbar-width: none;
25
+ -ms-overflow-style: none;
26
+ padding: 0 20px;
27
+ align-items: center;
28
+ min-height: 40px;
29
+ }
30
+ .tags-container[data-v-66047096]::-webkit-scrollbar {
31
+ display: none;
32
+ }
33
+
34
+ /* --------- 现代化标签样式 --------- */
35
+ .n-tag[data-v-66047096] {
36
+ position: relative;
37
+ display: inline-flex;
38
+ align-items: center;
39
+ padding: 0 12px;
40
+ height: 28px;
41
+ margin-right: 4px;
42
+ font-size: 12px;
43
+ font-weight: 500;
44
+ border-radius: 14px;
45
+ border: 1px solid var(--color-border);
46
+ cursor: pointer;
47
+ transition: all 0.22s cubic-bezier(0.4, 0, 0.2, 1);
48
+ flex-shrink: 0;
49
+ white-space: nowrap;
50
+ /* 内部高光效果 */
51
+ /* --------- 悬停效果 --------- */
52
+ /* --------- 激活状态 --------- */
53
+ /* --------- 关闭按钮 --------- */
54
+ }
55
+ .n-tag[data-v-66047096]::before {
56
+ content: "";
57
+ position: absolute;
58
+ top: 1px;
59
+ left: 1px;
60
+ right: 1px;
61
+ height: 50%;
62
+ border-radius: 13px 13px 6px 6px;
63
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.3), transparent);
64
+ opacity: 0.5;
65
+ transition: opacity 0.22s ease;
66
+ }
67
+ .n-tag[data-v-66047096]:hover:not([type=primary]) {
68
+ color: var(--color-primary) !important;
69
+ border-color: var(--color-primary);
70
+ transform: scale(1.02);
71
+ }
72
+ .n-tag[data-v-66047096]:hover:not([type=primary])::before {
73
+ opacity: 0.8;
74
+ }
75
+ .n-tag[data-v-66047096]:hover:not([type=primary]) .c-icon,
76
+ .n-tag[data-v-66047096]:hover:not([type=primary]) [class*="c-icon"] {
77
+ color: var(--color-primary) !important;
78
+ }
79
+ .n-tag:hover:not([type=primary]) i[data-v-66047096],
80
+ .n-tag:hover:not([type=primary]) .icon[data-v-66047096] {
81
+ color: var(--color-primary) !important;
82
+ }
83
+ .n-tag[data-v-66047096]:hover:not([type=primary]) .n-tag__close {
84
+ color: var(--color-primary) !important;
85
+ opacity: 0.8;
86
+ transform: scale(1.05);
87
+ }
88
+ .n-tag[type=primary][data-v-66047096] {
89
+ background: var(--tag-active-bg, rgba(32, 128, 240, 0.08)) !important;
90
+ color: var(--color-primary) !important;
91
+ border-color: var(--color-primary) !important;
92
+ font-weight: 600;
93
+ transform: scale(1.02);
94
+ }
95
+ .n-tag[type=primary][data-v-66047096]::before {
96
+ opacity: 0.6;
97
+ }
98
+ .n-tag[type=primary][data-v-66047096] .n-tag__content,
99
+ .n-tag[type=primary][data-v-66047096] *:not(.n-tag__close) {
100
+ color: var(--color-primary) !important;
101
+ }
102
+ .n-tag[type=primary][data-v-66047096] .c-icon,
103
+ .n-tag[type=primary][data-v-66047096] [class*="c-icon"] {
104
+ color: var(--color-primary) !important;
105
+ }
106
+ .n-tag[type=primary] i[data-v-66047096],
107
+ .n-tag[type=primary] .icon[data-v-66047096] {
108
+ color: var(--color-primary) !important;
109
+ }
110
+ .n-tag[type=primary][data-v-66047096]:hover {
111
+ background: var(--tag-active-hover-bg, rgba(32, 128, 240, 0.12)) !important;
112
+ transform: scale(1.03);
113
+ }
114
+ .n-tag[type=primary][data-v-66047096] .n-tag__close {
115
+ color: var(--color-primary) !important;
116
+ opacity: 0.8;
117
+ }
118
+ .n-tag[data-v-66047096] .n-tag__close {
119
+ width: 16px !important;
120
+ height: 16px !important;
121
+ border-radius: 8px !important;
122
+ display: flex !important;
123
+ align-items: center !important;
124
+ justify-content: center !important;
125
+ margin-left: 6px !important;
126
+ font-size: 12px !important;
127
+ font-weight: 600 !important;
128
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
129
+ opacity: 0.6;
130
+ background: rgba(0, 0, 0, 0.02) !important;
131
+ border: 1px solid transparent;
132
+ }
133
+ .n-tag[data-v-66047096] .n-tag__close:not(:hover) {
134
+ transform: scale(0.9);
135
+ }
136
+ .n-tag[data-v-66047096] .n-tag__close:hover {
137
+ background: linear-gradient(135deg, rgba(248, 113, 113, 0.1), rgba(239, 68, 68, 0.08)) !important;
138
+ border-color: rgba(239, 68, 68, 0.15) !important;
139
+ color: #ef4444 !important;
140
+ opacity: 1 !important;
141
+ transform: scale(0.6) !important;
142
+ box-shadow: 0 2px 6px rgba(239, 68, 68, 0.2) !important;
143
+ }
144
+ .n-tag[data-v-66047096] .n-tag__close:active {
145
+ transform: scale(0.95) !important;
146
+ transition-duration: 0.1s !important;
147
+ }
148
+
149
+ /* --------- C_Icon --------- */
150
+ [data-v-66047096] .c-icon {
151
+ margin-right: 4px;
152
+ font-size: 12px;
153
+ transition: color 0.22s ease;
154
+ display: inline-flex;
155
+ align-items: center;
156
+ }
157
+
158
+ /* --------- 响应式 --------- */
159
+ @media (max-width: 768px) {
160
+ .tags-view-container[data-v-66047096] {
161
+ padding: 6px 12px 0;
162
+ }
163
+ .n-tag[data-v-66047096] {
164
+ height: 26px;
165
+ padding: 0 10px;
166
+ font-size: 11px;
167
+ margin-right: 3px;
168
+ border-radius: 13px;
169
+ }
170
+ .n-tag[data-v-66047096] .n-tag__close {
171
+ width: 14px;
172
+ height: 14px;
173
+ font-size: 11px;
174
+ margin-left: 4px;
175
+ }
176
+ .tags-container[data-v-66047096] {
177
+ padding: 0 12px;
178
+ min-height: 36px;
179
+ }
180
+ [data-v-66047096] .c-icon {
181
+ font-size: 11px;
182
+ margin-right: 3px;
183
+ }
184
+ }
185
+ /* --------- 暗色主题 --------- */
186
+ [data-theme=dark] .n-tag[data-v-66047096] .n-tag__close {
187
+ background: rgba(255, 255, 255, 0.03) !important;
188
+ }
189
+ [data-theme=dark] .n-tag[data-v-66047096] .n-tag__close:hover {
190
+ background: linear-gradient(135deg, rgba(248, 113, 113, 0.12), rgba(239, 68, 68, 0.1)) !important;
191
+ box-shadow: 0 2px 6px rgba(239, 68, 68, 0.25) !important;
192
+ }
193
+
194
+ /*# sourceMappingURL=C_TagsView-DX3LecK1.css.map*/
@@ -0,0 +1,2 @@
1
+ import { i as useTagsView, n as _default, r as UseTagsViewOptions, t as ContextMenuLabels } from "./index17.vue.js";
2
+ export { _default as C_TagsView, type ContextMenuLabels, type UseTagsViewOptions, useTagsView };
@@ -0,0 +1,3 @@
1
+ import "./menuAdapter.js";
2
+ import { i as useTagsView, n as _default, r as UseTagsViewOptions, t as ContextMenuLabels } from "./index17.vue.js";
3
+ export { _default as C_TagsView, type ContextMenuLabels, type UseTagsViewOptions, useTagsView };
@@ -0,0 +1 @@
1
+ import"./C_Icon2.js";import{n as e,t}from"./C_TagsView2.js";export{t as C_TagsView,e as useTagsView};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"C_TagsView.js","names":[],"sources":["../src/components/C_TagsView/useTagsView.ts","../src/components/C_TagsView/index.vue","../src/components/C_TagsView/index.vue","../src/components/C_TagsView/index.vue"],"sourcesContent":["import { ref, type Ref } from 'vue'\nimport type { TagItem } from '../_shared'\n\nexport interface UseTagsViewOptions {\n /** localStorage 持久化 key,传空字符串则不持久化 */\n persistKey?: string\n /** 首页路径,用于 removeAll 后的回退 */\n homePath?: string\n}\n\nconst STORAGE_KEY_DEFAULT = '__tags_view_list__'\n\n/**\n * 标签页管理 composable\n *\n * 独立于组件使用 — 可在任何地方管理标签状态\n *\n * @example 独立使用\n * ```ts\n * const tags = useTagsView({ persistKey: 'my-tags' })\n * tags.addTag({ path: '/home', title: '首页', meta: { affix: true } })\n * ```\n */\nexport function useTagsView(options: UseTagsViewOptions = {}) {\n const { persistKey = STORAGE_KEY_DEFAULT, homePath = '/home' } = options\n\n // ====== 持久化 ======\n const loadPersistedTags = (): TagItem[] => {\n if (!persistKey) return []\n try {\n const raw = localStorage.getItem(persistKey)\n return raw ? JSON.parse(raw) : []\n } catch {\n return []\n }\n }\n\n const persistTags = (tags: TagItem[]) => {\n if (!persistKey) return\n try {\n localStorage.setItem(persistKey, JSON.stringify(tags))\n } catch {\n /* silently fail */\n }\n }\n\n // ====== State ======\n const tagsViewList: Ref<TagItem[]> = ref(loadPersistedTags())\n const activeTag = ref('')\n\n const syncPersist = () => persistTags(tagsViewList.value)\n\n // ====== Actions ======\n\n const initTags = (tags?: TagItem[]) => {\n if (tags) {\n tagsViewList.value = tags\n } else {\n // 去重\n tagsViewList.value = [\n ...new Map(tagsViewList.value.map(tag => [tag.path, tag])).values(),\n ]\n }\n syncPersist()\n }\n\n const setActiveTag = (path: string) => {\n activeTag.value = path\n }\n\n const addTag = (tag: TagItem) => {\n const existingIndex = tagsViewList.value.findIndex(\n item => item.path === tag.path\n )\n\n if (existingIndex === -1) {\n tagsViewList.value.push(tag)\n } else {\n const existing = tagsViewList.value[existingIndex]\n if (existing.title !== tag.title || !existing.originalTitle) {\n existing.title = tag.title\n existing.originalTitle = tag.originalTitle || existing.originalTitle\n }\n }\n\n setActiveTag(tag.path)\n syncPersist()\n }\n\n const removeTag = (index: number): string | null => {\n if (index >= 0 && index < tagsViewList.value.length) {\n const removed = tagsViewList.value.splice(index, 1)[0]?.path ?? null\n syncPersist()\n return removed\n }\n return null\n }\n\n const removeOtherTags = (index: number) => {\n if (index === 0) {\n tagsViewList.value = [tagsViewList.value[0]].filter(Boolean)\n } else {\n tagsViewList.value = [\n tagsViewList.value[0],\n tagsViewList.value[index],\n ].filter(Boolean)\n }\n syncPersist()\n }\n\n const removeLeftTags = (index: number) => {\n tagsViewList.value = [\n tagsViewList.value[0],\n ...tagsViewList.value.slice(index),\n ]\n syncPersist()\n }\n\n const removeRightTags = (index: number) => {\n tagsViewList.value = tagsViewList.value.slice(0, index + 1)\n syncPersist()\n }\n\n const removeAllTags = () => {\n tagsViewList.value = tagsViewList.value.filter(tag => tag.meta?.affix)\n syncPersist()\n }\n\n return {\n tagsViewList,\n activeTag,\n homePath,\n initTags,\n setActiveTag,\n addTag,\n removeTag,\n removeOtherTags,\n removeLeftTags,\n removeRightTags,\n removeAllTags,\n }\n}\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用标签页导航组件 — 路由感知的多标签管理\n *\n * 内置功能:\n * - 路由变化自动添加标签\n * - 持久化(localStorage,可关闭)\n * - 右键菜单(关闭/关闭其他/关闭左侧/关闭右侧/关闭所有)\n * - 水平滚轮滚动\n * - i18n 支持(labelFormatter)\n *\n * @example 基础用法\n * ```vue\n * <C_TagsView\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 自定义首页 + 菜单文案\n * ```vue\n * <C_TagsView\n * home-path=\"/dashboard\"\n * home-title=\"Dashboard\"\n * :context-menu-labels=\"{ close: 'Close', closeAll: 'Close All', ... }\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"tags-view-container\">\n <div\n class=\"tags-track\"\n ref=\"tagsTrack\"\n @wheel=\"handleWheel\"\n >\n <div\n class=\"tags-container\"\n ref=\"tagsContainer\"\n >\n <NTag\n v-for=\"(tag, index) in tags.tagsViewList.value\"\n :key=\"tag.path\"\n :type=\"isActive(tag) ? 'primary' : 'default'\"\n :closable=\"!isAffix(tag)\"\n :data-path=\"tag.path\"\n @close.stop=\"handleClose(tag, index)\"\n @click=\"navigateToTag(tag)\"\n @contextmenu.prevent=\"\n (e: MouseEvent) => showContextMenu(e, tag, index)\n \"\n >\n <template #icon>\n <C_Icon\n :name=\"tag.icon\"\n :size=\"12\"\n />\n </template>\n {{ tag.title }}\n </NTag>\n </div>\n </div>\n <NDropdown\n v-if=\"contextMenuVisible\"\n :show=\"contextMenuVisible\"\n :options=\"contextMenuOptions\"\n :x=\"contextMenuX\"\n :y=\"contextMenuY\"\n @clickoutside=\"closeContextMenu\"\n @select=\"handleContextMenuSelect\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, h } from 'vue'\n import { useRoute, useRouter } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { TagItem } from '../_shared'\n import { useTagsView, type UseTagsViewOptions } from './useTagsView'\n\n defineOptions({ name: 'C_TagsView' })\n\n export interface ContextMenuLabels {\n close?: string\n closeOthers?: string\n closeLeft?: string\n closeRight?: string\n closeAll?: string\n }\n\n const props = withDefaults(\n defineProps<{\n /**\n * 标签文本格式化函数(用于 i18n)\n */\n labelFormatter?: (label: string) => string\n /** 首页路径 */\n homePath?: string\n /** 首页标题(原始文本) */\n homeTitle?: string\n /** 首页图标 */\n homeIcon?: string\n /** 持久化 localStorage key,传空字符串禁用 */\n persistKey?: string\n /** 右键菜单文案(国际化适配) */\n contextMenuLabels?: ContextMenuLabels\n /** useTagsView 配置项(优先级最高) */\n tagsViewOptions?: UseTagsViewOptions\n }>(),\n {\n homePath: '/home',\n homeTitle: '首页',\n homeIcon: 'mdi:home',\n persistKey: '__tags_view_list__',\n contextMenuLabels: () => ({\n close: '关闭',\n closeOthers: '关闭其他',\n closeLeft: '关闭左侧',\n closeRight: '关闭右侧',\n closeAll: '关闭所有',\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 标签被关闭后跳转 */\n close: [fallbackPath: string]\n /** 关闭全部后 */\n closeAll: []\n }>()\n\n const route = useRoute()\n const router = useRouter()\n\n // ====== Tags composable ======\n const tags = useTagsView(\n props.tagsViewOptions ?? {\n persistKey: props.persistKey,\n homePath: props.homePath,\n }\n )\n\n // ====== 首次渲染前重新翻译持久化标签 ======\n if (tags.tagsViewList.value.length > 0 && props.labelFormatter) {\n const allRoutes = router.getRoutes()\n const translatedTags = tags.tagsViewList.value.map(tag => {\n const matchedRoute = allRoutes.find(r => r.path === tag.path)\n const originalTitle =\n tag.originalTitle || (matchedRoute?.meta?.title as string) || tag.title\n return {\n ...tag,\n originalTitle,\n title: props.labelFormatter!(originalTitle),\n }\n })\n tags.initTags(translatedTags)\n }\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n // ====== UI State ======\n\n const contextMenuVisible = ref(false)\n const contextMenuX = ref(0)\n const contextMenuY = ref(0)\n const selectedTag = ref<TagItem | null>(null)\n const selectedIndex = ref(-1)\n const tagsTrack = ref<HTMLElement>()\n const tagsContainer = ref<HTMLElement>()\n\n // ====== Helpers ======\n const isActive = (tag: TagItem): boolean => tag.path === route.path\n const isAffix = (tag: TagItem) => tag.meta?.affix\n\n const handleWheel = (e: WheelEvent) => {\n const container = tagsContainer.value\n if (!container) return\n e.preventDefault()\n container.scrollTo({\n left: container.scrollLeft + e.deltaY,\n behavior: 'smooth',\n })\n }\n\n const scrollToTag = (path: string) => {\n nextTick(() => {\n const targetTag = tagsContainer.value?.querySelector(\n `[data-path=\"${path}\"]`\n ) as HTMLElement\n if (targetTag) {\n targetTag.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'center',\n })\n }\n })\n }\n\n // ====== Actions ======\n\n const navigateToTag = (tag: TagItem) => {\n if (tag.path !== route.path) {\n router.push(tag.path)\n }\n scrollToTag(tag.path)\n }\n\n const handleClose = (tag: TagItem, index: number) => {\n if (isAffix(tag)) return\n\n const wasActive = isActive(tag)\n const tagsBeforeRemove = tags.tagsViewList.value.length\n tags.removeTag(index)\n\n if (wasActive) {\n const remaining = tags.tagsViewList.value\n const wasLastTag = index === tagsBeforeRemove - 1\n let fallbackPath: string\n\n if (wasLastTag) {\n fallbackPath = remaining[index - 1]?.path || props.homePath\n } else {\n fallbackPath = remaining[remaining.length - 1]?.path || props.homePath\n }\n\n router.push(fallbackPath)\n emit('close', fallbackPath)\n }\n }\n\n const showContextMenu = (event: MouseEvent, tag: TagItem, index: number) => {\n event.preventDefault()\n selectedTag.value = tag\n selectedIndex.value = index\n contextMenuVisible.value = true\n contextMenuX.value = event.clientX\n contextMenuY.value = event.clientY\n }\n\n const closeContextMenu = () => (contextMenuVisible.value = false)\n\n const contextMenuOptions = computed(() => {\n const labels = props.contextMenuLabels\n return [\n {\n type: 'option' as const,\n label: labels.close ?? '关闭',\n key: 'close',\n disabled: isAffix(selectedTag.value as TagItem),\n icon: () => h('span', { class: 'i-mdi:close' }),\n },\n {\n type: 'option' as const,\n label: labels.closeOthers ?? '关闭其他',\n key: 'closeOthers',\n icon: () => h('span', { class: 'i-mdi:arrow-left-right-bold' }),\n },\n {\n type: 'option' as const,\n label: labels.closeLeft ?? '关闭左侧',\n key: 'closeLeft',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-left' }),\n },\n {\n type: 'option' as const,\n label: labels.closeRight ?? '关闭右侧',\n key: 'closeRight',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-right' }),\n },\n {\n type: 'option' as const,\n label: labels.closeAll ?? '关闭所有',\n key: 'closeAll',\n icon: () => h('span', { class: 'i-mdi:tally-mark-5' }),\n },\n ]\n })\n\n const handleContextMenuSelect = (key: string) => {\n if (!selectedTag.value || selectedIndex.value === -1) return\n\n switch (key) {\n case 'close':\n handleClose(selectedTag.value, selectedIndex.value)\n break\n case 'closeOthers':\n tags.removeOtherTags(selectedIndex.value)\n break\n case 'closeLeft':\n tags.removeLeftTags(selectedIndex.value)\n break\n case 'closeRight':\n tags.removeRightTags(selectedIndex.value)\n break\n case 'closeAll':\n tags.removeAllTags()\n router.push(props.homePath)\n emit('closeAll')\n break\n }\n closeContextMenu()\n }\n\n // ====== 路由自动跟踪 ======\n watch(\n () => route.path,\n (newPath: string) => {\n const originalTitle =\n newPath === props.homePath\n ? props.homeTitle\n : (route.meta.title as string) || 'Unnamed Page'\n\n tags.addTag({\n path: newPath,\n originalTitle,\n title: formatLabel(originalTitle),\n icon:\n newPath === props.homePath\n ? props.homeIcon\n : (route.meta.icon as string),\n meta: { affix: route.meta.affix as boolean },\n })\n\n tags.setActiveTag(newPath)\n scrollToTag(newPath)\n },\n { immediate: true }\n )\n\n // ====== 暴露 ======\n defineExpose({\n /** useTagsView composable 实例 */\n tags,\n /** 滚动到指定标签 */\n scrollToTag,\n })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用标签页导航组件 — 路由感知的多标签管理\n *\n * 内置功能:\n * - 路由变化自动添加标签\n * - 持久化(localStorage,可关闭)\n * - 右键菜单(关闭/关闭其他/关闭左侧/关闭右侧/关闭所有)\n * - 水平滚轮滚动\n * - i18n 支持(labelFormatter)\n *\n * @example 基础用法\n * ```vue\n * <C_TagsView\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 自定义首页 + 菜单文案\n * ```vue\n * <C_TagsView\n * home-path=\"/dashboard\"\n * home-title=\"Dashboard\"\n * :context-menu-labels=\"{ close: 'Close', closeAll: 'Close All', ... }\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"tags-view-container\">\n <div\n class=\"tags-track\"\n ref=\"tagsTrack\"\n @wheel=\"handleWheel\"\n >\n <div\n class=\"tags-container\"\n ref=\"tagsContainer\"\n >\n <NTag\n v-for=\"(tag, index) in tags.tagsViewList.value\"\n :key=\"tag.path\"\n :type=\"isActive(tag) ? 'primary' : 'default'\"\n :closable=\"!isAffix(tag)\"\n :data-path=\"tag.path\"\n @close.stop=\"handleClose(tag, index)\"\n @click=\"navigateToTag(tag)\"\n @contextmenu.prevent=\"\n (e: MouseEvent) => showContextMenu(e, tag, index)\n \"\n >\n <template #icon>\n <C_Icon\n :name=\"tag.icon\"\n :size=\"12\"\n />\n </template>\n {{ tag.title }}\n </NTag>\n </div>\n </div>\n <NDropdown\n v-if=\"contextMenuVisible\"\n :show=\"contextMenuVisible\"\n :options=\"contextMenuOptions\"\n :x=\"contextMenuX\"\n :y=\"contextMenuY\"\n @clickoutside=\"closeContextMenu\"\n @select=\"handleContextMenuSelect\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, h } from 'vue'\n import { useRoute, useRouter } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { TagItem } from '../_shared'\n import { useTagsView, type UseTagsViewOptions } from './useTagsView'\n\n defineOptions({ name: 'C_TagsView' })\n\n export interface ContextMenuLabels {\n close?: string\n closeOthers?: string\n closeLeft?: string\n closeRight?: string\n closeAll?: string\n }\n\n const props = withDefaults(\n defineProps<{\n /**\n * 标签文本格式化函数(用于 i18n)\n */\n labelFormatter?: (label: string) => string\n /** 首页路径 */\n homePath?: string\n /** 首页标题(原始文本) */\n homeTitle?: string\n /** 首页图标 */\n homeIcon?: string\n /** 持久化 localStorage key,传空字符串禁用 */\n persistKey?: string\n /** 右键菜单文案(国际化适配) */\n contextMenuLabels?: ContextMenuLabels\n /** useTagsView 配置项(优先级最高) */\n tagsViewOptions?: UseTagsViewOptions\n }>(),\n {\n homePath: '/home',\n homeTitle: '首页',\n homeIcon: 'mdi:home',\n persistKey: '__tags_view_list__',\n contextMenuLabels: () => ({\n close: '关闭',\n closeOthers: '关闭其他',\n closeLeft: '关闭左侧',\n closeRight: '关闭右侧',\n closeAll: '关闭所有',\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 标签被关闭后跳转 */\n close: [fallbackPath: string]\n /** 关闭全部后 */\n closeAll: []\n }>()\n\n const route = useRoute()\n const router = useRouter()\n\n // ====== Tags composable ======\n const tags = useTagsView(\n props.tagsViewOptions ?? {\n persistKey: props.persistKey,\n homePath: props.homePath,\n }\n )\n\n // ====== 首次渲染前重新翻译持久化标签 ======\n if (tags.tagsViewList.value.length > 0 && props.labelFormatter) {\n const allRoutes = router.getRoutes()\n const translatedTags = tags.tagsViewList.value.map(tag => {\n const matchedRoute = allRoutes.find(r => r.path === tag.path)\n const originalTitle =\n tag.originalTitle || (matchedRoute?.meta?.title as string) || tag.title\n return {\n ...tag,\n originalTitle,\n title: props.labelFormatter!(originalTitle),\n }\n })\n tags.initTags(translatedTags)\n }\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n // ====== UI State ======\n\n const contextMenuVisible = ref(false)\n const contextMenuX = ref(0)\n const contextMenuY = ref(0)\n const selectedTag = ref<TagItem | null>(null)\n const selectedIndex = ref(-1)\n const tagsTrack = ref<HTMLElement>()\n const tagsContainer = ref<HTMLElement>()\n\n // ====== Helpers ======\n const isActive = (tag: TagItem): boolean => tag.path === route.path\n const isAffix = (tag: TagItem) => tag.meta?.affix\n\n const handleWheel = (e: WheelEvent) => {\n const container = tagsContainer.value\n if (!container) return\n e.preventDefault()\n container.scrollTo({\n left: container.scrollLeft + e.deltaY,\n behavior: 'smooth',\n })\n }\n\n const scrollToTag = (path: string) => {\n nextTick(() => {\n const targetTag = tagsContainer.value?.querySelector(\n `[data-path=\"${path}\"]`\n ) as HTMLElement\n if (targetTag) {\n targetTag.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'center',\n })\n }\n })\n }\n\n // ====== Actions ======\n\n const navigateToTag = (tag: TagItem) => {\n if (tag.path !== route.path) {\n router.push(tag.path)\n }\n scrollToTag(tag.path)\n }\n\n const handleClose = (tag: TagItem, index: number) => {\n if (isAffix(tag)) return\n\n const wasActive = isActive(tag)\n const tagsBeforeRemove = tags.tagsViewList.value.length\n tags.removeTag(index)\n\n if (wasActive) {\n const remaining = tags.tagsViewList.value\n const wasLastTag = index === tagsBeforeRemove - 1\n let fallbackPath: string\n\n if (wasLastTag) {\n fallbackPath = remaining[index - 1]?.path || props.homePath\n } else {\n fallbackPath = remaining[remaining.length - 1]?.path || props.homePath\n }\n\n router.push(fallbackPath)\n emit('close', fallbackPath)\n }\n }\n\n const showContextMenu = (event: MouseEvent, tag: TagItem, index: number) => {\n event.preventDefault()\n selectedTag.value = tag\n selectedIndex.value = index\n contextMenuVisible.value = true\n contextMenuX.value = event.clientX\n contextMenuY.value = event.clientY\n }\n\n const closeContextMenu = () => (contextMenuVisible.value = false)\n\n const contextMenuOptions = computed(() => {\n const labels = props.contextMenuLabels\n return [\n {\n type: 'option' as const,\n label: labels.close ?? '关闭',\n key: 'close',\n disabled: isAffix(selectedTag.value as TagItem),\n icon: () => h('span', { class: 'i-mdi:close' }),\n },\n {\n type: 'option' as const,\n label: labels.closeOthers ?? '关闭其他',\n key: 'closeOthers',\n icon: () => h('span', { class: 'i-mdi:arrow-left-right-bold' }),\n },\n {\n type: 'option' as const,\n label: labels.closeLeft ?? '关闭左侧',\n key: 'closeLeft',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-left' }),\n },\n {\n type: 'option' as const,\n label: labels.closeRight ?? '关闭右侧',\n key: 'closeRight',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-right' }),\n },\n {\n type: 'option' as const,\n label: labels.closeAll ?? '关闭所有',\n key: 'closeAll',\n icon: () => h('span', { class: 'i-mdi:tally-mark-5' }),\n },\n ]\n })\n\n const handleContextMenuSelect = (key: string) => {\n if (!selectedTag.value || selectedIndex.value === -1) return\n\n switch (key) {\n case 'close':\n handleClose(selectedTag.value, selectedIndex.value)\n break\n case 'closeOthers':\n tags.removeOtherTags(selectedIndex.value)\n break\n case 'closeLeft':\n tags.removeLeftTags(selectedIndex.value)\n break\n case 'closeRight':\n tags.removeRightTags(selectedIndex.value)\n break\n case 'closeAll':\n tags.removeAllTags()\n router.push(props.homePath)\n emit('closeAll')\n break\n }\n closeContextMenu()\n }\n\n // ====== 路由自动跟踪 ======\n watch(\n () => route.path,\n (newPath: string) => {\n const originalTitle =\n newPath === props.homePath\n ? props.homeTitle\n : (route.meta.title as string) || 'Unnamed Page'\n\n tags.addTag({\n path: newPath,\n originalTitle,\n title: formatLabel(originalTitle),\n icon:\n newPath === props.homePath\n ? props.homeIcon\n : (route.meta.icon as string),\n meta: { affix: route.meta.affix as boolean },\n })\n\n tags.setActiveTag(newPath)\n scrollToTag(newPath)\n },\n { immediate: true }\n )\n\n // ====== 暴露 ======\n defineExpose({\n /** useTagsView composable 实例 */\n tags,\n /** 滚动到指定标签 */\n scrollToTag,\n })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n","<!--\n * @Description: 通用标签页导航组件 — 路由感知的多标签管理\n *\n * 内置功能:\n * - 路由变化自动添加标签\n * - 持久化(localStorage,可关闭)\n * - 右键菜单(关闭/关闭其他/关闭左侧/关闭右侧/关闭所有)\n * - 水平滚轮滚动\n * - i18n 支持(labelFormatter)\n *\n * @example 基础用法\n * ```vue\n * <C_TagsView\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 自定义首页 + 菜单文案\n * ```vue\n * <C_TagsView\n * home-path=\"/dashboard\"\n * home-title=\"Dashboard\"\n * :context-menu-labels=\"{ close: 'Close', closeAll: 'Close All', ... }\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"tags-view-container\">\n <div\n class=\"tags-track\"\n ref=\"tagsTrack\"\n @wheel=\"handleWheel\"\n >\n <div\n class=\"tags-container\"\n ref=\"tagsContainer\"\n >\n <NTag\n v-for=\"(tag, index) in tags.tagsViewList.value\"\n :key=\"tag.path\"\n :type=\"isActive(tag) ? 'primary' : 'default'\"\n :closable=\"!isAffix(tag)\"\n :data-path=\"tag.path\"\n @close.stop=\"handleClose(tag, index)\"\n @click=\"navigateToTag(tag)\"\n @contextmenu.prevent=\"\n (e: MouseEvent) => showContextMenu(e, tag, index)\n \"\n >\n <template #icon>\n <C_Icon\n :name=\"tag.icon\"\n :size=\"12\"\n />\n </template>\n {{ tag.title }}\n </NTag>\n </div>\n </div>\n <NDropdown\n v-if=\"contextMenuVisible\"\n :show=\"contextMenuVisible\"\n :options=\"contextMenuOptions\"\n :x=\"contextMenuX\"\n :y=\"contextMenuY\"\n @clickoutside=\"closeContextMenu\"\n @select=\"handleContextMenuSelect\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, h } from 'vue'\n import { useRoute, useRouter } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { TagItem } from '../_shared'\n import { useTagsView, type UseTagsViewOptions } from './useTagsView'\n\n defineOptions({ name: 'C_TagsView' })\n\n export interface ContextMenuLabels {\n close?: string\n closeOthers?: string\n closeLeft?: string\n closeRight?: string\n closeAll?: string\n }\n\n const props = withDefaults(\n defineProps<{\n /**\n * 标签文本格式化函数(用于 i18n)\n */\n labelFormatter?: (label: string) => string\n /** 首页路径 */\n homePath?: string\n /** 首页标题(原始文本) */\n homeTitle?: string\n /** 首页图标 */\n homeIcon?: string\n /** 持久化 localStorage key,传空字符串禁用 */\n persistKey?: string\n /** 右键菜单文案(国际化适配) */\n contextMenuLabels?: ContextMenuLabels\n /** useTagsView 配置项(优先级最高) */\n tagsViewOptions?: UseTagsViewOptions\n }>(),\n {\n homePath: '/home',\n homeTitle: '首页',\n homeIcon: 'mdi:home',\n persistKey: '__tags_view_list__',\n contextMenuLabels: () => ({\n close: '关闭',\n closeOthers: '关闭其他',\n closeLeft: '关闭左侧',\n closeRight: '关闭右侧',\n closeAll: '关闭所有',\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 标签被关闭后跳转 */\n close: [fallbackPath: string]\n /** 关闭全部后 */\n closeAll: []\n }>()\n\n const route = useRoute()\n const router = useRouter()\n\n // ====== Tags composable ======\n const tags = useTagsView(\n props.tagsViewOptions ?? {\n persistKey: props.persistKey,\n homePath: props.homePath,\n }\n )\n\n // ====== 首次渲染前重新翻译持久化标签 ======\n if (tags.tagsViewList.value.length > 0 && props.labelFormatter) {\n const allRoutes = router.getRoutes()\n const translatedTags = tags.tagsViewList.value.map(tag => {\n const matchedRoute = allRoutes.find(r => r.path === tag.path)\n const originalTitle =\n tag.originalTitle || (matchedRoute?.meta?.title as string) || tag.title\n return {\n ...tag,\n originalTitle,\n title: props.labelFormatter!(originalTitle),\n }\n })\n tags.initTags(translatedTags)\n }\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n // ====== UI State ======\n\n const contextMenuVisible = ref(false)\n const contextMenuX = ref(0)\n const contextMenuY = ref(0)\n const selectedTag = ref<TagItem | null>(null)\n const selectedIndex = ref(-1)\n const tagsTrack = ref<HTMLElement>()\n const tagsContainer = ref<HTMLElement>()\n\n // ====== Helpers ======\n const isActive = (tag: TagItem): boolean => tag.path === route.path\n const isAffix = (tag: TagItem) => tag.meta?.affix\n\n const handleWheel = (e: WheelEvent) => {\n const container = tagsContainer.value\n if (!container) return\n e.preventDefault()\n container.scrollTo({\n left: container.scrollLeft + e.deltaY,\n behavior: 'smooth',\n })\n }\n\n const scrollToTag = (path: string) => {\n nextTick(() => {\n const targetTag = tagsContainer.value?.querySelector(\n `[data-path=\"${path}\"]`\n ) as HTMLElement\n if (targetTag) {\n targetTag.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'center',\n })\n }\n })\n }\n\n // ====== Actions ======\n\n const navigateToTag = (tag: TagItem) => {\n if (tag.path !== route.path) {\n router.push(tag.path)\n }\n scrollToTag(tag.path)\n }\n\n const handleClose = (tag: TagItem, index: number) => {\n if (isAffix(tag)) return\n\n const wasActive = isActive(tag)\n const tagsBeforeRemove = tags.tagsViewList.value.length\n tags.removeTag(index)\n\n if (wasActive) {\n const remaining = tags.tagsViewList.value\n const wasLastTag = index === tagsBeforeRemove - 1\n let fallbackPath: string\n\n if (wasLastTag) {\n fallbackPath = remaining[index - 1]?.path || props.homePath\n } else {\n fallbackPath = remaining[remaining.length - 1]?.path || props.homePath\n }\n\n router.push(fallbackPath)\n emit('close', fallbackPath)\n }\n }\n\n const showContextMenu = (event: MouseEvent, tag: TagItem, index: number) => {\n event.preventDefault()\n selectedTag.value = tag\n selectedIndex.value = index\n contextMenuVisible.value = true\n contextMenuX.value = event.clientX\n contextMenuY.value = event.clientY\n }\n\n const closeContextMenu = () => (contextMenuVisible.value = false)\n\n const contextMenuOptions = computed(() => {\n const labels = props.contextMenuLabels\n return [\n {\n type: 'option' as const,\n label: labels.close ?? '关闭',\n key: 'close',\n disabled: isAffix(selectedTag.value as TagItem),\n icon: () => h('span', { class: 'i-mdi:close' }),\n },\n {\n type: 'option' as const,\n label: labels.closeOthers ?? '关闭其他',\n key: 'closeOthers',\n icon: () => h('span', { class: 'i-mdi:arrow-left-right-bold' }),\n },\n {\n type: 'option' as const,\n label: labels.closeLeft ?? '关闭左侧',\n key: 'closeLeft',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-left' }),\n },\n {\n type: 'option' as const,\n label: labels.closeRight ?? '关闭右侧',\n key: 'closeRight',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-right' }),\n },\n {\n type: 'option' as const,\n label: labels.closeAll ?? '关闭所有',\n key: 'closeAll',\n icon: () => h('span', { class: 'i-mdi:tally-mark-5' }),\n },\n ]\n })\n\n const handleContextMenuSelect = (key: string) => {\n if (!selectedTag.value || selectedIndex.value === -1) return\n\n switch (key) {\n case 'close':\n handleClose(selectedTag.value, selectedIndex.value)\n break\n case 'closeOthers':\n tags.removeOtherTags(selectedIndex.value)\n break\n case 'closeLeft':\n tags.removeLeftTags(selectedIndex.value)\n break\n case 'closeRight':\n tags.removeRightTags(selectedIndex.value)\n break\n case 'closeAll':\n tags.removeAllTags()\n router.push(props.homePath)\n emit('closeAll')\n break\n }\n closeContextMenu()\n }\n\n // ====== 路由自动跟踪 ======\n watch(\n () => route.path,\n (newPath: string) => {\n const originalTitle =\n newPath === props.homePath\n ? props.homeTitle\n : (route.meta.title as string) || 'Unnamed Page'\n\n tags.addTag({\n path: newPath,\n originalTitle,\n title: formatLabel(originalTitle),\n icon:\n newPath === props.homePath\n ? props.homeIcon\n : (route.meta.icon as string),\n meta: { affix: route.meta.affix as boolean },\n })\n\n tags.setActiveTag(newPath)\n scrollToTag(newPath)\n },\n { immediate: true }\n )\n\n // ====== 暴露 ======\n defineExpose({\n /** useTagsView composable 实例 */\n tags,\n /** 滚动到指定标签 */\n scrollToTag,\n })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n"],"mappings":"wJAuBA,SAAgB,EAAY,EAA8B,EAAE,CAAE,CAC5D,GAAM,CAAE,aAAa,qBAAqB,WAAW,SAAY,EAG3D,MAAqC,CACzC,GAAI,CAAC,EAAY,MAAO,EAAE,CAC1B,GAAI,CACF,IAAM,EAAM,aAAa,QAAQ,EAAW,CAC5C,OAAO,EAAM,KAAK,MAAM,EAAI,CAAG,EAAE,MAC3B,CACN,MAAO,EAAE,GAIP,EAAe,GAAoB,CAClC,KACL,GAAI,CACF,aAAa,QAAQ,EAAY,KAAK,UAAU,EAAK,CAAC,MAChD,IAMJ,GAAA,EAAA,EAAA,KAAmC,GAAmB,CAAC,CACvD,GAAA,EAAA,EAAA,KAAgB,GAAG,CAEnB,MAAoB,EAAY,EAAa,MAAM,CAInD,EAAY,GAAqB,CACjC,EACF,EAAa,MAAQ,EAGrB,EAAa,MAAQ,CACnB,GAAG,IAAI,IAAI,EAAa,MAAM,IAAI,GAAO,CAAC,EAAI,KAAM,EAAI,CAAC,CAAC,CAAC,QAAQ,CACpE,CAEH,GAAa,EAGT,EAAgB,GAAiB,CACrC,EAAU,MAAQ,GA6DpB,MAAO,CACL,eACA,YACA,WACA,WACA,eACA,OAhEc,GAAiB,CAC/B,IAAM,EAAgB,EAAa,MAAM,UACvC,GAAQ,EAAK,OAAS,EAAI,KAC3B,CAED,GAAI,IAAkB,GACpB,EAAa,MAAM,KAAK,EAAI,KACvB,CACL,IAAM,EAAW,EAAa,MAAM,IAChC,EAAS,QAAU,EAAI,OAAS,CAAC,EAAS,iBAC5C,EAAS,MAAQ,EAAI,MACrB,EAAS,cAAgB,EAAI,eAAiB,EAAS,eAI3D,EAAa,EAAI,KAAK,CACtB,GAAa,EAiDb,UA9CiB,GAAiC,CAClD,GAAI,GAAS,GAAK,EAAQ,EAAa,MAAM,OAAQ,CACnD,IAAM,EAAU,EAAa,MAAM,OAAO,EAAO,EAAE,CAAC,IAAI,MAAQ,KAEhE,OADA,GAAa,CACN,EAET,OAAO,MAyCP,gBAtCuB,GAAkB,CACrC,IAAU,EACZ,EAAa,MAAQ,CAAC,EAAa,MAAM,GAAG,CAAC,OAAO,QAAQ,CAE5D,EAAa,MAAQ,CACnB,EAAa,MAAM,GACnB,EAAa,MAAM,GACpB,CAAC,OAAO,QAAQ,CAEnB,GAAa,EA8Bb,eA3BsB,GAAkB,CACxC,EAAa,MAAQ,CACnB,EAAa,MAAM,GACnB,GAAG,EAAa,MAAM,MAAM,EAAM,CACnC,CACD,GAAa,EAuBb,gBApBuB,GAAkB,CACzC,EAAa,MAAQ,EAAa,MAAM,MAAM,EAAG,EAAQ,EAAE,CAC3D,GAAa,EAmBb,kBAhB0B,CAC1B,EAAa,MAAQ,EAAa,MAAM,OAAO,GAAO,EAAI,MAAM,MAAM,CACtE,GAAa,EAed,ybGjDD,IAAM,EAAQ,EAkCR,EAAO,EAOP,GAAA,EAAA,EAAA,WAAiB,CACjB,GAAA,EAAA,EAAA,YAAmB,CAGnB,EAAO,EACX,EAAM,iBAAmB,CACvB,WAAY,EAAM,WAClB,SAAU,EAAM,SAClB,CACF,CAGA,GAAI,EAAK,aAAa,MAAM,OAAS,GAAK,EAAM,eAAgB,CAC9D,IAAM,EAAY,EAAO,WAAU,CAC7B,EAAiB,EAAK,aAAa,MAAM,IAAI,GAAO,CACxD,IAAM,EAAe,EAAU,KAAK,GAAK,EAAE,OAAS,EAAI,KAAI,CACtD,EACJ,EAAI,eAAkB,GAAc,MAAM,OAAoB,EAAI,MACpE,MAAO,CACL,GAAG,EACH,gBACA,MAAO,EAAM,eAAgB,EAAc,CAC7C,EACD,CACD,EAAK,SAAS,EAAc,CAG9B,IAAM,EAAe,GACnB,EAAM,eAAiB,EAAM,eAAe,EAAM,CAAG,EAIjD,GAAA,EAAA,EAAA,KAAyB,GAAK,CAC9B,GAAA,EAAA,EAAA,KAAmB,EAAC,CACpB,GAAA,EAAA,EAAA,KAAmB,EAAC,CACpB,GAAA,EAAA,EAAA,KAAkC,KAAI,CACtC,GAAA,EAAA,EAAA,KAAoB,GAAE,CACtB,GAAA,EAAA,EAAA,MAA6B,CAC7B,GAAA,EAAA,EAAA,MAAiC,CAGjC,EAAY,GAA0B,EAAI,OAAS,EAAM,KACzD,EAAW,GAAiB,EAAI,MAAM,MAEtC,EAAe,GAAkB,CACrC,IAAM,EAAY,EAAc,MAC3B,IACL,EAAE,gBAAe,CACjB,EAAU,SAAS,CACjB,KAAM,EAAU,WAAa,EAAE,OAC/B,SAAU,SACX,CAAA,GAGG,EAAe,GAAiB,EACpC,EAAA,EAAA,cAAe,CACb,IAAM,EAAY,EAAc,OAAO,cACrC,eAAe,EAAK,IACrB,CACG,GACF,EAAU,eAAe,CACvB,SAAU,SACV,MAAO,UACP,OAAQ,SACT,CAAA,EAEJ,EAKG,EAAiB,GAAiB,CAClC,EAAI,OAAS,EAAM,MACrB,EAAO,KAAK,EAAI,KAAI,CAEtB,EAAY,EAAI,KAAI,EAGhB,GAAe,EAAc,IAAkB,CACnD,GAAI,EAAQ,EAAI,CAAE,OAElB,IAAM,EAAY,EAAS,EAAG,CACxB,EAAmB,EAAK,aAAa,MAAM,OAGjD,GAFA,EAAK,UAAU,EAAK,CAEhB,EAAW,CACb,IAAM,EAAY,EAAK,aAAa,MAC9B,EAAa,IAAU,EAAmB,EAC5C,EAEJ,AAGE,EAHE,EACa,EAAU,EAAQ,IAAI,MAAQ,EAAM,SAEpC,EAAU,EAAU,OAAS,IAAI,MAAQ,EAAM,SAGhE,EAAO,KAAK,EAAY,CACxB,EAAK,QAAS,EAAY,GAIxB,GAAmB,EAAmB,EAAc,IAAkB,CAC1E,EAAM,gBAAe,CACrB,EAAY,MAAQ,EACpB,EAAc,MAAQ,EACtB,EAAmB,MAAQ,GAC3B,EAAa,MAAQ,EAAM,QAC3B,EAAa,MAAQ,EAAM,SAGvB,MAA0B,EAAmB,MAAQ,GAErD,GAAA,EAAA,EAAA,cAAoC,CACxC,IAAM,EAAS,EAAM,kBACrB,MAAO,CACL,CACE,KAAM,SACN,MAAO,EAAO,OAAS,KACvB,IAAK,QACL,SAAU,EAAQ,EAAY,MAAiB,CAC/C,UAAA,EAAA,EAAA,GAAc,OAAQ,CAAE,MAAO,cAAe,CAAC,CAChD,CACD,CACE,KAAM,SACN,MAAO,EAAO,aAAe,OAC7B,IAAK,cACL,UAAA,EAAA,EAAA,GAAc,OAAQ,CAAE,MAAO,8BAA+B,CAAC,CAChE,CACD,CACE,KAAM,SACN,MAAO,EAAO,WAAa,OAC3B,IAAK,YACL,UAAA,EAAA,EAAA,GAAc,OAAQ,CAAE,MAAO,8BAA+B,CAAC,CAChE,CACD,CACE,KAAM,SACN,MAAO,EAAO,YAAc,OAC5B,IAAK,aACL,UAAA,EAAA,EAAA,GAAc,OAAQ,CAAE,MAAO,+BAAgC,CAAC,CACjE,CACD,CACE,KAAM,SACN,MAAO,EAAO,UAAY,OAC1B,IAAK,WACL,UAAA,EAAA,EAAA,GAAc,OAAQ,CAAE,MAAO,qBAAsB,CAAC,CACvD,CACH,EACD,CAEK,EAA2B,GAAgB,CAC3C,MAAC,EAAY,OAAS,EAAc,QAAU,IAElD,QAAQ,EAAR,CACE,IAAK,QACH,EAAY,EAAY,MAAO,EAAc,MAAK,CAClD,MACF,IAAK,cACH,EAAK,gBAAgB,EAAc,MAAK,CACxC,MACF,IAAK,YACH,EAAK,eAAe,EAAc,MAAK,CACvC,MACF,IAAK,aACH,EAAK,gBAAgB,EAAc,MAAK,CACxC,MACF,IAAK,WACH,EAAK,eAAc,CACnB,EAAO,KAAK,EAAM,SAAQ,CAC1B,EAAK,WAAU,CACf,MAEJ,GAAiB,UAInB,EAAA,EAAA,WACQ,EAAM,KACX,GAAoB,CACnB,IAAM,EACJ,IAAY,EAAM,SACd,EAAM,UACL,EAAM,KAAK,OAAoB,eAEtC,EAAK,OAAO,CACV,KAAM,EACN,gBACA,MAAO,EAAY,EAAc,CACjC,KACE,IAAY,EAAM,SACd,EAAM,SACL,EAAM,KAAK,KAClB,KAAM,CAAE,MAAO,EAAM,KAAK,MAAkB,CAC7C,CAAA,CAED,EAAK,aAAa,EAAO,CACzB,EAAY,EAAO,EAErB,CAAE,UAAW,GAAK,CACpB,CAGA,EAAa,CAEX,OAEA,cACD,CAAA,qFA3QK,MAzCN,EAyCM,EAAA,EAAA,EAAA,oBAVE,MAAA,CA7BJ,MAAM,qBACF,YAAJ,IAAI,EACH,QAAO,6BA0BF,MAAA,CAvBJ,MAAM,yBACF,gBAAJ,IAAI,kDAqBG,EAAA,SAAA,MAAA,EAAA,EAAA,aAAA,EAAA,EAAA,OAlBkB,EAAI,CAAC,aAAa,OAAjC,EAAK,yCAkBR,EAAA,CAjBJ,IAAK,EAAI,KACT,KAAM,EAAS,EAAG,CAAA,UAAA,UAClB,SAAQ,CAAG,EAAQ,EAAG,CACtB,YAAW,EAAI,KACf,SAAA,EAAA,EAAA,eAAK,GAAO,EAAY,EAAK,EAAK,CAAA,CAAA,OAAA,CAAA,CAClC,QAAK,GAAE,EAAc,EAAG,CACxB,eAAA,EAAA,EAAA,eAAmC,GAAkB,EAAgB,EAAG,EAAK,EAAK,CAAA,CAAA,UAAA,CAAA,GAIxE,MAAA,EAAA,EAAA,aAIP,EAAA,EAAA,EAAA,cAAA,EAAA,EAAA,OAAA,EAAA,EAAA,CAAA,CAFC,KAAM,EAAI,KACV,KAAM,iDAGX,EAAA,EAAA,EAAA,iBADW,KAAA,EAAA,EAAA,iBACR,EAAI,MAAK,CAAA,EAAA,CAAA,CAAA,oGAKV,EAAA,QAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,aAON,EAAA,OANC,KAAM,EAAA,MACN,QAAS,EAAA,MACT,EAAG,EAAA,MACH,EAAG,EAAA,MACH,eAAc,EACd,SAAQ"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./C_Icon2.js";import{t}from"./export-helper.js";import{NDropdown as n,NTag as r}from"naive-ui";import{Fragment as i,computed as a,createBlock as o,createCommentVNode as s,createElementBlock as c,createElementVNode as l,createTextVNode as u,createVNode as d,defineComponent as f,h as p,nextTick as m,openBlock as h,ref as g,renderList as _,toDisplayString as v,unref as y,watch as b,withCtx as x,withModifiers as S}from"vue";import{useRoute as C,useRouter as w}from"vue-router";function T(e={}){let{persistKey:t=`__tags_view_list__`,homePath:n=`/home`}=e,r=()=>{if(!t)return[];try{let e=localStorage.getItem(t);return e?JSON.parse(e):[]}catch{return[]}},i=e=>{if(t)try{localStorage.setItem(t,JSON.stringify(e))}catch{}},a=g(r()),o=g(``),s=()=>i(a.value),c=e=>{e?a.value=e:a.value=[...new Map(a.value.map(e=>[e.path,e])).values()],s()},l=e=>{o.value=e};return{tagsViewList:a,activeTag:o,homePath:n,initTags:c,setActiveTag:l,addTag:e=>{let t=a.value.findIndex(t=>t.path===e.path);if(t===-1)a.value.push(e);else{let n=a.value[t];(n.title!==e.title||!n.originalTitle)&&(n.title=e.title,n.originalTitle=e.originalTitle||n.originalTitle)}l(e.path),s()},removeTag:e=>{if(e>=0&&e<a.value.length){let t=a.value.splice(e,1)[0]?.path??null;return s(),t}return null},removeOtherTags:e=>{e===0?a.value=[a.value[0]].filter(Boolean):a.value=[a.value[0],a.value[e]].filter(Boolean),s()},removeLeftTags:e=>{a.value=[a.value[0],...a.value.slice(e)],s()},removeRightTags:e=>{a.value=a.value.slice(0,e+1),s()},removeAllTags:()=>{a.value=a.value.filter(e=>e.meta?.affix),s()}}}const E={class:`tags-view-container`};var D=t(f({name:`C_TagsView`,__name:`index`,props:{labelFormatter:{},homePath:{default:`/home`},homeTitle:{default:`首页`},homeIcon:{default:`mdi:home`},persistKey:{default:`__tags_view_list__`},contextMenuLabels:{default:()=>({close:`关闭`,closeOthers:`关闭其他`,closeLeft:`关闭左侧`,closeRight:`关闭右侧`,closeAll:`关闭所有`})},tagsViewOptions:{}},emits:[`close`,`closeAll`],setup(t,{expose:f,emit:D}){let O=t,k=D,A=C(),j=w(),M=T(O.tagsViewOptions??{persistKey:O.persistKey,homePath:O.homePath});if(M.tagsViewList.value.length>0&&O.labelFormatter){let e=j.getRoutes(),t=M.tagsViewList.value.map(t=>{let n=e.find(e=>e.path===t.path),r=t.originalTitle||n?.meta?.title||t.title;return{...t,originalTitle:r,title:O.labelFormatter(r)}});M.initTags(t)}let N=e=>O.labelFormatter?O.labelFormatter(e):e,P=g(!1),F=g(0),I=g(0),L=g(null),R=g(-1),z=g(),B=g(),V=e=>e.path===A.path,H=e=>e.meta?.affix,U=e=>{let t=B.value;t&&(e.preventDefault(),t.scrollTo({left:t.scrollLeft+e.deltaY,behavior:`smooth`}))},W=e=>{m(()=>{let t=B.value?.querySelector(`[data-path="${e}"]`);t&&t.scrollIntoView({behavior:`smooth`,block:`nearest`,inline:`center`})})},G=e=>{e.path!==A.path&&j.push(e.path),W(e.path)},K=(e,t)=>{if(H(e))return;let n=V(e),r=M.tagsViewList.value.length;if(M.removeTag(t),n){let e=M.tagsViewList.value,n=t===r-1,i;i=n?e[t-1]?.path||O.homePath:e[e.length-1]?.path||O.homePath,j.push(i),k(`close`,i)}},q=(e,t,n)=>{e.preventDefault(),L.value=t,R.value=n,P.value=!0,F.value=e.clientX,I.value=e.clientY},J=()=>P.value=!1,Y=a(()=>{let e=O.contextMenuLabels;return[{type:`option`,label:e.close??`关闭`,key:`close`,disabled:H(L.value),icon:()=>p(`span`,{class:`i-mdi:close`})},{type:`option`,label:e.closeOthers??`关闭其他`,key:`closeOthers`,icon:()=>p(`span`,{class:`i-mdi:arrow-left-right-bold`})},{type:`option`,label:e.closeLeft??`关闭左侧`,key:`closeLeft`,icon:()=>p(`span`,{class:`i-mdi:align-horizontal-left`})},{type:`option`,label:e.closeRight??`关闭右侧`,key:`closeRight`,icon:()=>p(`span`,{class:`i-mdi:align-horizontal-right`})},{type:`option`,label:e.closeAll??`关闭所有`,key:`closeAll`,icon:()=>p(`span`,{class:`i-mdi:tally-mark-5`})}]}),X=e=>{if(!(!L.value||R.value===-1)){switch(e){case`close`:K(L.value,R.value);break;case`closeOthers`:M.removeOtherTags(R.value);break;case`closeLeft`:M.removeLeftTags(R.value);break;case`closeRight`:M.removeRightTags(R.value);break;case`closeAll`:M.removeAllTags(),j.push(O.homePath),k(`closeAll`);break}J()}};return b(()=>A.path,e=>{let t=e===O.homePath?O.homeTitle:A.meta.title||`Unnamed Page`;M.addTag({path:e,originalTitle:t,title:N(t),icon:e===O.homePath?O.homeIcon:A.meta.icon,meta:{affix:A.meta.affix}}),M.setActiveTag(e),W(e)},{immediate:!0}),f({tags:M,scrollToTag:W}),(t,a)=>{let f=r,p=n;return h(),c(`div`,E,[l(`div`,{class:`tags-track`,ref_key:`tagsTrack`,ref:z,onWheel:U},[l(`div`,{class:`tags-container`,ref_key:`tagsContainer`,ref:B},[(h(!0),c(i,null,_(y(M).tagsViewList.value,(t,n)=>(h(),o(f,{key:t.path,type:V(t)?`primary`:`default`,closable:!H(t),"data-path":t.path,onClose:S(e=>K(t,n),[`stop`]),onClick:e=>G(t),onContextmenu:S(e=>q(e,t,n),[`prevent`])},{icon:x(()=>[d(y(e),{name:t.icon,size:12},null,8,[`name`])]),default:x(()=>[u(` `+v(t.title),1)]),_:2},1032,[`type`,`closable`,`data-path`,`onClose`,`onClick`,`onContextmenu`]))),128))],512)],544),P.value?(h(),o(p,{key:0,show:P.value,options:Y.value,x:F.value,y:I.value,onClickoutside:J,onSelect:X},null,8,[`show`,`options`,`x`,`y`])):s(`v-if`,!0)])}}}),[[`__scopeId`,`data-v-66047096`]]);export{T as n,D as t};
2
+ //# sourceMappingURL=C_TagsView2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"C_TagsView2.js","names":[],"sources":["../src/components/C_TagsView/useTagsView.ts","../src/components/C_TagsView/index.vue","../src/components/C_TagsView/index.vue","../src/components/C_TagsView/index.vue"],"sourcesContent":["import { ref, type Ref } from 'vue'\nimport type { TagItem } from '../_shared'\n\nexport interface UseTagsViewOptions {\n /** localStorage 持久化 key,传空字符串则不持久化 */\n persistKey?: string\n /** 首页路径,用于 removeAll 后的回退 */\n homePath?: string\n}\n\nconst STORAGE_KEY_DEFAULT = '__tags_view_list__'\n\n/**\n * 标签页管理 composable\n *\n * 独立于组件使用 — 可在任何地方管理标签状态\n *\n * @example 独立使用\n * ```ts\n * const tags = useTagsView({ persistKey: 'my-tags' })\n * tags.addTag({ path: '/home', title: '首页', meta: { affix: true } })\n * ```\n */\nexport function useTagsView(options: UseTagsViewOptions = {}) {\n const { persistKey = STORAGE_KEY_DEFAULT, homePath = '/home' } = options\n\n // ====== 持久化 ======\n const loadPersistedTags = (): TagItem[] => {\n if (!persistKey) return []\n try {\n const raw = localStorage.getItem(persistKey)\n return raw ? JSON.parse(raw) : []\n } catch {\n return []\n }\n }\n\n const persistTags = (tags: TagItem[]) => {\n if (!persistKey) return\n try {\n localStorage.setItem(persistKey, JSON.stringify(tags))\n } catch {\n /* silently fail */\n }\n }\n\n // ====== State ======\n const tagsViewList: Ref<TagItem[]> = ref(loadPersistedTags())\n const activeTag = ref('')\n\n const syncPersist = () => persistTags(tagsViewList.value)\n\n // ====== Actions ======\n\n const initTags = (tags?: TagItem[]) => {\n if (tags) {\n tagsViewList.value = tags\n } else {\n // 去重\n tagsViewList.value = [\n ...new Map(tagsViewList.value.map(tag => [tag.path, tag])).values(),\n ]\n }\n syncPersist()\n }\n\n const setActiveTag = (path: string) => {\n activeTag.value = path\n }\n\n const addTag = (tag: TagItem) => {\n const existingIndex = tagsViewList.value.findIndex(\n item => item.path === tag.path\n )\n\n if (existingIndex === -1) {\n tagsViewList.value.push(tag)\n } else {\n const existing = tagsViewList.value[existingIndex]\n if (existing.title !== tag.title || !existing.originalTitle) {\n existing.title = tag.title\n existing.originalTitle = tag.originalTitle || existing.originalTitle\n }\n }\n\n setActiveTag(tag.path)\n syncPersist()\n }\n\n const removeTag = (index: number): string | null => {\n if (index >= 0 && index < tagsViewList.value.length) {\n const removed = tagsViewList.value.splice(index, 1)[0]?.path ?? null\n syncPersist()\n return removed\n }\n return null\n }\n\n const removeOtherTags = (index: number) => {\n if (index === 0) {\n tagsViewList.value = [tagsViewList.value[0]].filter(Boolean)\n } else {\n tagsViewList.value = [\n tagsViewList.value[0],\n tagsViewList.value[index],\n ].filter(Boolean)\n }\n syncPersist()\n }\n\n const removeLeftTags = (index: number) => {\n tagsViewList.value = [\n tagsViewList.value[0],\n ...tagsViewList.value.slice(index),\n ]\n syncPersist()\n }\n\n const removeRightTags = (index: number) => {\n tagsViewList.value = tagsViewList.value.slice(0, index + 1)\n syncPersist()\n }\n\n const removeAllTags = () => {\n tagsViewList.value = tagsViewList.value.filter(tag => tag.meta?.affix)\n syncPersist()\n }\n\n return {\n tagsViewList,\n activeTag,\n homePath,\n initTags,\n setActiveTag,\n addTag,\n removeTag,\n removeOtherTags,\n removeLeftTags,\n removeRightTags,\n removeAllTags,\n }\n}\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用标签页导航组件 — 路由感知的多标签管理\n *\n * 内置功能:\n * - 路由变化自动添加标签\n * - 持久化(localStorage,可关闭)\n * - 右键菜单(关闭/关闭其他/关闭左侧/关闭右侧/关闭所有)\n * - 水平滚轮滚动\n * - i18n 支持(labelFormatter)\n *\n * @example 基础用法\n * ```vue\n * <C_TagsView\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 自定义首页 + 菜单文案\n * ```vue\n * <C_TagsView\n * home-path=\"/dashboard\"\n * home-title=\"Dashboard\"\n * :context-menu-labels=\"{ close: 'Close', closeAll: 'Close All', ... }\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"tags-view-container\">\n <div\n class=\"tags-track\"\n ref=\"tagsTrack\"\n @wheel=\"handleWheel\"\n >\n <div\n class=\"tags-container\"\n ref=\"tagsContainer\"\n >\n <NTag\n v-for=\"(tag, index) in tags.tagsViewList.value\"\n :key=\"tag.path\"\n :type=\"isActive(tag) ? 'primary' : 'default'\"\n :closable=\"!isAffix(tag)\"\n :data-path=\"tag.path\"\n @close.stop=\"handleClose(tag, index)\"\n @click=\"navigateToTag(tag)\"\n @contextmenu.prevent=\"\n (e: MouseEvent) => showContextMenu(e, tag, index)\n \"\n >\n <template #icon>\n <C_Icon\n :name=\"tag.icon\"\n :size=\"12\"\n />\n </template>\n {{ tag.title }}\n </NTag>\n </div>\n </div>\n <NDropdown\n v-if=\"contextMenuVisible\"\n :show=\"contextMenuVisible\"\n :options=\"contextMenuOptions\"\n :x=\"contextMenuX\"\n :y=\"contextMenuY\"\n @clickoutside=\"closeContextMenu\"\n @select=\"handleContextMenuSelect\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, h } from 'vue'\n import { useRoute, useRouter } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { TagItem } from '../_shared'\n import { useTagsView, type UseTagsViewOptions } from './useTagsView'\n\n defineOptions({ name: 'C_TagsView' })\n\n export interface ContextMenuLabels {\n close?: string\n closeOthers?: string\n closeLeft?: string\n closeRight?: string\n closeAll?: string\n }\n\n const props = withDefaults(\n defineProps<{\n /**\n * 标签文本格式化函数(用于 i18n)\n */\n labelFormatter?: (label: string) => string\n /** 首页路径 */\n homePath?: string\n /** 首页标题(原始文本) */\n homeTitle?: string\n /** 首页图标 */\n homeIcon?: string\n /** 持久化 localStorage key,传空字符串禁用 */\n persistKey?: string\n /** 右键菜单文案(国际化适配) */\n contextMenuLabels?: ContextMenuLabels\n /** useTagsView 配置项(优先级最高) */\n tagsViewOptions?: UseTagsViewOptions\n }>(),\n {\n homePath: '/home',\n homeTitle: '首页',\n homeIcon: 'mdi:home',\n persistKey: '__tags_view_list__',\n contextMenuLabels: () => ({\n close: '关闭',\n closeOthers: '关闭其他',\n closeLeft: '关闭左侧',\n closeRight: '关闭右侧',\n closeAll: '关闭所有',\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 标签被关闭后跳转 */\n close: [fallbackPath: string]\n /** 关闭全部后 */\n closeAll: []\n }>()\n\n const route = useRoute()\n const router = useRouter()\n\n // ====== Tags composable ======\n const tags = useTagsView(\n props.tagsViewOptions ?? {\n persistKey: props.persistKey,\n homePath: props.homePath,\n }\n )\n\n // ====== 首次渲染前重新翻译持久化标签 ======\n if (tags.tagsViewList.value.length > 0 && props.labelFormatter) {\n const allRoutes = router.getRoutes()\n const translatedTags = tags.tagsViewList.value.map(tag => {\n const matchedRoute = allRoutes.find(r => r.path === tag.path)\n const originalTitle =\n tag.originalTitle || (matchedRoute?.meta?.title as string) || tag.title\n return {\n ...tag,\n originalTitle,\n title: props.labelFormatter!(originalTitle),\n }\n })\n tags.initTags(translatedTags)\n }\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n // ====== UI State ======\n\n const contextMenuVisible = ref(false)\n const contextMenuX = ref(0)\n const contextMenuY = ref(0)\n const selectedTag = ref<TagItem | null>(null)\n const selectedIndex = ref(-1)\n const tagsTrack = ref<HTMLElement>()\n const tagsContainer = ref<HTMLElement>()\n\n // ====== Helpers ======\n const isActive = (tag: TagItem): boolean => tag.path === route.path\n const isAffix = (tag: TagItem) => tag.meta?.affix\n\n const handleWheel = (e: WheelEvent) => {\n const container = tagsContainer.value\n if (!container) return\n e.preventDefault()\n container.scrollTo({\n left: container.scrollLeft + e.deltaY,\n behavior: 'smooth',\n })\n }\n\n const scrollToTag = (path: string) => {\n nextTick(() => {\n const targetTag = tagsContainer.value?.querySelector(\n `[data-path=\"${path}\"]`\n ) as HTMLElement\n if (targetTag) {\n targetTag.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'center',\n })\n }\n })\n }\n\n // ====== Actions ======\n\n const navigateToTag = (tag: TagItem) => {\n if (tag.path !== route.path) {\n router.push(tag.path)\n }\n scrollToTag(tag.path)\n }\n\n const handleClose = (tag: TagItem, index: number) => {\n if (isAffix(tag)) return\n\n const wasActive = isActive(tag)\n const tagsBeforeRemove = tags.tagsViewList.value.length\n tags.removeTag(index)\n\n if (wasActive) {\n const remaining = tags.tagsViewList.value\n const wasLastTag = index === tagsBeforeRemove - 1\n let fallbackPath: string\n\n if (wasLastTag) {\n fallbackPath = remaining[index - 1]?.path || props.homePath\n } else {\n fallbackPath = remaining[remaining.length - 1]?.path || props.homePath\n }\n\n router.push(fallbackPath)\n emit('close', fallbackPath)\n }\n }\n\n const showContextMenu = (event: MouseEvent, tag: TagItem, index: number) => {\n event.preventDefault()\n selectedTag.value = tag\n selectedIndex.value = index\n contextMenuVisible.value = true\n contextMenuX.value = event.clientX\n contextMenuY.value = event.clientY\n }\n\n const closeContextMenu = () => (contextMenuVisible.value = false)\n\n const contextMenuOptions = computed(() => {\n const labels = props.contextMenuLabels\n return [\n {\n type: 'option' as const,\n label: labels.close ?? '关闭',\n key: 'close',\n disabled: isAffix(selectedTag.value as TagItem),\n icon: () => h('span', { class: 'i-mdi:close' }),\n },\n {\n type: 'option' as const,\n label: labels.closeOthers ?? '关闭其他',\n key: 'closeOthers',\n icon: () => h('span', { class: 'i-mdi:arrow-left-right-bold' }),\n },\n {\n type: 'option' as const,\n label: labels.closeLeft ?? '关闭左侧',\n key: 'closeLeft',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-left' }),\n },\n {\n type: 'option' as const,\n label: labels.closeRight ?? '关闭右侧',\n key: 'closeRight',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-right' }),\n },\n {\n type: 'option' as const,\n label: labels.closeAll ?? '关闭所有',\n key: 'closeAll',\n icon: () => h('span', { class: 'i-mdi:tally-mark-5' }),\n },\n ]\n })\n\n const handleContextMenuSelect = (key: string) => {\n if (!selectedTag.value || selectedIndex.value === -1) return\n\n switch (key) {\n case 'close':\n handleClose(selectedTag.value, selectedIndex.value)\n break\n case 'closeOthers':\n tags.removeOtherTags(selectedIndex.value)\n break\n case 'closeLeft':\n tags.removeLeftTags(selectedIndex.value)\n break\n case 'closeRight':\n tags.removeRightTags(selectedIndex.value)\n break\n case 'closeAll':\n tags.removeAllTags()\n router.push(props.homePath)\n emit('closeAll')\n break\n }\n closeContextMenu()\n }\n\n // ====== 路由自动跟踪 ======\n watch(\n () => route.path,\n (newPath: string) => {\n const originalTitle =\n newPath === props.homePath\n ? props.homeTitle\n : (route.meta.title as string) || 'Unnamed Page'\n\n tags.addTag({\n path: newPath,\n originalTitle,\n title: formatLabel(originalTitle),\n icon:\n newPath === props.homePath\n ? props.homeIcon\n : (route.meta.icon as string),\n meta: { affix: route.meta.affix as boolean },\n })\n\n tags.setActiveTag(newPath)\n scrollToTag(newPath)\n },\n { immediate: true }\n )\n\n // ====== 暴露 ======\n defineExpose({\n /** useTagsView composable 实例 */\n tags,\n /** 滚动到指定标签 */\n scrollToTag,\n })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n","/* unplugin-vue-components disabled */<!--\n * @Description: 通用标签页导航组件 — 路由感知的多标签管理\n *\n * 内置功能:\n * - 路由变化自动添加标签\n * - 持久化(localStorage,可关闭)\n * - 右键菜单(关闭/关闭其他/关闭左侧/关闭右侧/关闭所有)\n * - 水平滚轮滚动\n * - i18n 支持(labelFormatter)\n *\n * @example 基础用法\n * ```vue\n * <C_TagsView\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 自定义首页 + 菜单文案\n * ```vue\n * <C_TagsView\n * home-path=\"/dashboard\"\n * home-title=\"Dashboard\"\n * :context-menu-labels=\"{ close: 'Close', closeAll: 'Close All', ... }\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"tags-view-container\">\n <div\n class=\"tags-track\"\n ref=\"tagsTrack\"\n @wheel=\"handleWheel\"\n >\n <div\n class=\"tags-container\"\n ref=\"tagsContainer\"\n >\n <NTag\n v-for=\"(tag, index) in tags.tagsViewList.value\"\n :key=\"tag.path\"\n :type=\"isActive(tag) ? 'primary' : 'default'\"\n :closable=\"!isAffix(tag)\"\n :data-path=\"tag.path\"\n @close.stop=\"handleClose(tag, index)\"\n @click=\"navigateToTag(tag)\"\n @contextmenu.prevent=\"\n (e: MouseEvent) => showContextMenu(e, tag, index)\n \"\n >\n <template #icon>\n <C_Icon\n :name=\"tag.icon\"\n :size=\"12\"\n />\n </template>\n {{ tag.title }}\n </NTag>\n </div>\n </div>\n <NDropdown\n v-if=\"contextMenuVisible\"\n :show=\"contextMenuVisible\"\n :options=\"contextMenuOptions\"\n :x=\"contextMenuX\"\n :y=\"contextMenuY\"\n @clickoutside=\"closeContextMenu\"\n @select=\"handleContextMenuSelect\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, h } from 'vue'\n import { useRoute, useRouter } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { TagItem } from '../_shared'\n import { useTagsView, type UseTagsViewOptions } from './useTagsView'\n\n defineOptions({ name: 'C_TagsView' })\n\n export interface ContextMenuLabels {\n close?: string\n closeOthers?: string\n closeLeft?: string\n closeRight?: string\n closeAll?: string\n }\n\n const props = withDefaults(\n defineProps<{\n /**\n * 标签文本格式化函数(用于 i18n)\n */\n labelFormatter?: (label: string) => string\n /** 首页路径 */\n homePath?: string\n /** 首页标题(原始文本) */\n homeTitle?: string\n /** 首页图标 */\n homeIcon?: string\n /** 持久化 localStorage key,传空字符串禁用 */\n persistKey?: string\n /** 右键菜单文案(国际化适配) */\n contextMenuLabels?: ContextMenuLabels\n /** useTagsView 配置项(优先级最高) */\n tagsViewOptions?: UseTagsViewOptions\n }>(),\n {\n homePath: '/home',\n homeTitle: '首页',\n homeIcon: 'mdi:home',\n persistKey: '__tags_view_list__',\n contextMenuLabels: () => ({\n close: '关闭',\n closeOthers: '关闭其他',\n closeLeft: '关闭左侧',\n closeRight: '关闭右侧',\n closeAll: '关闭所有',\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 标签被关闭后跳转 */\n close: [fallbackPath: string]\n /** 关闭全部后 */\n closeAll: []\n }>()\n\n const route = useRoute()\n const router = useRouter()\n\n // ====== Tags composable ======\n const tags = useTagsView(\n props.tagsViewOptions ?? {\n persistKey: props.persistKey,\n homePath: props.homePath,\n }\n )\n\n // ====== 首次渲染前重新翻译持久化标签 ======\n if (tags.tagsViewList.value.length > 0 && props.labelFormatter) {\n const allRoutes = router.getRoutes()\n const translatedTags = tags.tagsViewList.value.map(tag => {\n const matchedRoute = allRoutes.find(r => r.path === tag.path)\n const originalTitle =\n tag.originalTitle || (matchedRoute?.meta?.title as string) || tag.title\n return {\n ...tag,\n originalTitle,\n title: props.labelFormatter!(originalTitle),\n }\n })\n tags.initTags(translatedTags)\n }\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n // ====== UI State ======\n\n const contextMenuVisible = ref(false)\n const contextMenuX = ref(0)\n const contextMenuY = ref(0)\n const selectedTag = ref<TagItem | null>(null)\n const selectedIndex = ref(-1)\n const tagsTrack = ref<HTMLElement>()\n const tagsContainer = ref<HTMLElement>()\n\n // ====== Helpers ======\n const isActive = (tag: TagItem): boolean => tag.path === route.path\n const isAffix = (tag: TagItem) => tag.meta?.affix\n\n const handleWheel = (e: WheelEvent) => {\n const container = tagsContainer.value\n if (!container) return\n e.preventDefault()\n container.scrollTo({\n left: container.scrollLeft + e.deltaY,\n behavior: 'smooth',\n })\n }\n\n const scrollToTag = (path: string) => {\n nextTick(() => {\n const targetTag = tagsContainer.value?.querySelector(\n `[data-path=\"${path}\"]`\n ) as HTMLElement\n if (targetTag) {\n targetTag.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'center',\n })\n }\n })\n }\n\n // ====== Actions ======\n\n const navigateToTag = (tag: TagItem) => {\n if (tag.path !== route.path) {\n router.push(tag.path)\n }\n scrollToTag(tag.path)\n }\n\n const handleClose = (tag: TagItem, index: number) => {\n if (isAffix(tag)) return\n\n const wasActive = isActive(tag)\n const tagsBeforeRemove = tags.tagsViewList.value.length\n tags.removeTag(index)\n\n if (wasActive) {\n const remaining = tags.tagsViewList.value\n const wasLastTag = index === tagsBeforeRemove - 1\n let fallbackPath: string\n\n if (wasLastTag) {\n fallbackPath = remaining[index - 1]?.path || props.homePath\n } else {\n fallbackPath = remaining[remaining.length - 1]?.path || props.homePath\n }\n\n router.push(fallbackPath)\n emit('close', fallbackPath)\n }\n }\n\n const showContextMenu = (event: MouseEvent, tag: TagItem, index: number) => {\n event.preventDefault()\n selectedTag.value = tag\n selectedIndex.value = index\n contextMenuVisible.value = true\n contextMenuX.value = event.clientX\n contextMenuY.value = event.clientY\n }\n\n const closeContextMenu = () => (contextMenuVisible.value = false)\n\n const contextMenuOptions = computed(() => {\n const labels = props.contextMenuLabels\n return [\n {\n type: 'option' as const,\n label: labels.close ?? '关闭',\n key: 'close',\n disabled: isAffix(selectedTag.value as TagItem),\n icon: () => h('span', { class: 'i-mdi:close' }),\n },\n {\n type: 'option' as const,\n label: labels.closeOthers ?? '关闭其他',\n key: 'closeOthers',\n icon: () => h('span', { class: 'i-mdi:arrow-left-right-bold' }),\n },\n {\n type: 'option' as const,\n label: labels.closeLeft ?? '关闭左侧',\n key: 'closeLeft',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-left' }),\n },\n {\n type: 'option' as const,\n label: labels.closeRight ?? '关闭右侧',\n key: 'closeRight',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-right' }),\n },\n {\n type: 'option' as const,\n label: labels.closeAll ?? '关闭所有',\n key: 'closeAll',\n icon: () => h('span', { class: 'i-mdi:tally-mark-5' }),\n },\n ]\n })\n\n const handleContextMenuSelect = (key: string) => {\n if (!selectedTag.value || selectedIndex.value === -1) return\n\n switch (key) {\n case 'close':\n handleClose(selectedTag.value, selectedIndex.value)\n break\n case 'closeOthers':\n tags.removeOtherTags(selectedIndex.value)\n break\n case 'closeLeft':\n tags.removeLeftTags(selectedIndex.value)\n break\n case 'closeRight':\n tags.removeRightTags(selectedIndex.value)\n break\n case 'closeAll':\n tags.removeAllTags()\n router.push(props.homePath)\n emit('closeAll')\n break\n }\n closeContextMenu()\n }\n\n // ====== 路由自动跟踪 ======\n watch(\n () => route.path,\n (newPath: string) => {\n const originalTitle =\n newPath === props.homePath\n ? props.homeTitle\n : (route.meta.title as string) || 'Unnamed Page'\n\n tags.addTag({\n path: newPath,\n originalTitle,\n title: formatLabel(originalTitle),\n icon:\n newPath === props.homePath\n ? props.homeIcon\n : (route.meta.icon as string),\n meta: { affix: route.meta.affix as boolean },\n })\n\n tags.setActiveTag(newPath)\n scrollToTag(newPath)\n },\n { immediate: true }\n )\n\n // ====== 暴露 ======\n defineExpose({\n /** useTagsView composable 实例 */\n tags,\n /** 滚动到指定标签 */\n scrollToTag,\n })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n","<!--\n * @Description: 通用标签页导航组件 — 路由感知的多标签管理\n *\n * 内置功能:\n * - 路由变化自动添加标签\n * - 持久化(localStorage,可关闭)\n * - 右键菜单(关闭/关闭其他/关闭左侧/关闭右侧/关闭所有)\n * - 水平滚轮滚动\n * - i18n 支持(labelFormatter)\n *\n * @example 基础用法\n * ```vue\n * <C_TagsView\n * :label-formatter=\"$t\"\n * @select=\"router.push\"\n * />\n * ```\n *\n * @example 自定义首页 + 菜单文案\n * ```vue\n * <C_TagsView\n * home-path=\"/dashboard\"\n * home-title=\"Dashboard\"\n * :context-menu-labels=\"{ close: 'Close', closeAll: 'Close All', ... }\"\n * />\n * ```\n *\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"tags-view-container\">\n <div\n class=\"tags-track\"\n ref=\"tagsTrack\"\n @wheel=\"handleWheel\"\n >\n <div\n class=\"tags-container\"\n ref=\"tagsContainer\"\n >\n <NTag\n v-for=\"(tag, index) in tags.tagsViewList.value\"\n :key=\"tag.path\"\n :type=\"isActive(tag) ? 'primary' : 'default'\"\n :closable=\"!isAffix(tag)\"\n :data-path=\"tag.path\"\n @close.stop=\"handleClose(tag, index)\"\n @click=\"navigateToTag(tag)\"\n @contextmenu.prevent=\"\n (e: MouseEvent) => showContextMenu(e, tag, index)\n \"\n >\n <template #icon>\n <C_Icon\n :name=\"tag.icon\"\n :size=\"12\"\n />\n </template>\n {{ tag.title }}\n </NTag>\n </div>\n </div>\n <NDropdown\n v-if=\"contextMenuVisible\"\n :show=\"contextMenuVisible\"\n :options=\"contextMenuOptions\"\n :x=\"contextMenuX\"\n :y=\"contextMenuY\"\n @clickoutside=\"closeContextMenu\"\n @select=\"handleContextMenuSelect\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed, watch, nextTick, h } from 'vue'\n import { useRoute, useRouter } from 'vue-router'\n import { C_Icon } from '../C_Icon'\n import type { TagItem } from '../_shared'\n import { useTagsView, type UseTagsViewOptions } from './useTagsView'\n\n defineOptions({ name: 'C_TagsView' })\n\n export interface ContextMenuLabels {\n close?: string\n closeOthers?: string\n closeLeft?: string\n closeRight?: string\n closeAll?: string\n }\n\n const props = withDefaults(\n defineProps<{\n /**\n * 标签文本格式化函数(用于 i18n)\n */\n labelFormatter?: (label: string) => string\n /** 首页路径 */\n homePath?: string\n /** 首页标题(原始文本) */\n homeTitle?: string\n /** 首页图标 */\n homeIcon?: string\n /** 持久化 localStorage key,传空字符串禁用 */\n persistKey?: string\n /** 右键菜单文案(国际化适配) */\n contextMenuLabels?: ContextMenuLabels\n /** useTagsView 配置项(优先级最高) */\n tagsViewOptions?: UseTagsViewOptions\n }>(),\n {\n homePath: '/home',\n homeTitle: '首页',\n homeIcon: 'mdi:home',\n persistKey: '__tags_view_list__',\n contextMenuLabels: () => ({\n close: '关闭',\n closeOthers: '关闭其他',\n closeLeft: '关闭左侧',\n closeRight: '关闭右侧',\n closeAll: '关闭所有',\n }),\n }\n )\n\n const emit = defineEmits<{\n /** 标签被关闭后跳转 */\n close: [fallbackPath: string]\n /** 关闭全部后 */\n closeAll: []\n }>()\n\n const route = useRoute()\n const router = useRouter()\n\n // ====== Tags composable ======\n const tags = useTagsView(\n props.tagsViewOptions ?? {\n persistKey: props.persistKey,\n homePath: props.homePath,\n }\n )\n\n // ====== 首次渲染前重新翻译持久化标签 ======\n if (tags.tagsViewList.value.length > 0 && props.labelFormatter) {\n const allRoutes = router.getRoutes()\n const translatedTags = tags.tagsViewList.value.map(tag => {\n const matchedRoute = allRoutes.find(r => r.path === tag.path)\n const originalTitle =\n tag.originalTitle || (matchedRoute?.meta?.title as string) || tag.title\n return {\n ...tag,\n originalTitle,\n title: props.labelFormatter!(originalTitle),\n }\n })\n tags.initTags(translatedTags)\n }\n\n const formatLabel = (title: string): string =>\n props.labelFormatter ? props.labelFormatter(title) : title\n\n // ====== UI State ======\n\n const contextMenuVisible = ref(false)\n const contextMenuX = ref(0)\n const contextMenuY = ref(0)\n const selectedTag = ref<TagItem | null>(null)\n const selectedIndex = ref(-1)\n const tagsTrack = ref<HTMLElement>()\n const tagsContainer = ref<HTMLElement>()\n\n // ====== Helpers ======\n const isActive = (tag: TagItem): boolean => tag.path === route.path\n const isAffix = (tag: TagItem) => tag.meta?.affix\n\n const handleWheel = (e: WheelEvent) => {\n const container = tagsContainer.value\n if (!container) return\n e.preventDefault()\n container.scrollTo({\n left: container.scrollLeft + e.deltaY,\n behavior: 'smooth',\n })\n }\n\n const scrollToTag = (path: string) => {\n nextTick(() => {\n const targetTag = tagsContainer.value?.querySelector(\n `[data-path=\"${path}\"]`\n ) as HTMLElement\n if (targetTag) {\n targetTag.scrollIntoView({\n behavior: 'smooth',\n block: 'nearest',\n inline: 'center',\n })\n }\n })\n }\n\n // ====== Actions ======\n\n const navigateToTag = (tag: TagItem) => {\n if (tag.path !== route.path) {\n router.push(tag.path)\n }\n scrollToTag(tag.path)\n }\n\n const handleClose = (tag: TagItem, index: number) => {\n if (isAffix(tag)) return\n\n const wasActive = isActive(tag)\n const tagsBeforeRemove = tags.tagsViewList.value.length\n tags.removeTag(index)\n\n if (wasActive) {\n const remaining = tags.tagsViewList.value\n const wasLastTag = index === tagsBeforeRemove - 1\n let fallbackPath: string\n\n if (wasLastTag) {\n fallbackPath = remaining[index - 1]?.path || props.homePath\n } else {\n fallbackPath = remaining[remaining.length - 1]?.path || props.homePath\n }\n\n router.push(fallbackPath)\n emit('close', fallbackPath)\n }\n }\n\n const showContextMenu = (event: MouseEvent, tag: TagItem, index: number) => {\n event.preventDefault()\n selectedTag.value = tag\n selectedIndex.value = index\n contextMenuVisible.value = true\n contextMenuX.value = event.clientX\n contextMenuY.value = event.clientY\n }\n\n const closeContextMenu = () => (contextMenuVisible.value = false)\n\n const contextMenuOptions = computed(() => {\n const labels = props.contextMenuLabels\n return [\n {\n type: 'option' as const,\n label: labels.close ?? '关闭',\n key: 'close',\n disabled: isAffix(selectedTag.value as TagItem),\n icon: () => h('span', { class: 'i-mdi:close' }),\n },\n {\n type: 'option' as const,\n label: labels.closeOthers ?? '关闭其他',\n key: 'closeOthers',\n icon: () => h('span', { class: 'i-mdi:arrow-left-right-bold' }),\n },\n {\n type: 'option' as const,\n label: labels.closeLeft ?? '关闭左侧',\n key: 'closeLeft',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-left' }),\n },\n {\n type: 'option' as const,\n label: labels.closeRight ?? '关闭右侧',\n key: 'closeRight',\n icon: () => h('span', { class: 'i-mdi:align-horizontal-right' }),\n },\n {\n type: 'option' as const,\n label: labels.closeAll ?? '关闭所有',\n key: 'closeAll',\n icon: () => h('span', { class: 'i-mdi:tally-mark-5' }),\n },\n ]\n })\n\n const handleContextMenuSelect = (key: string) => {\n if (!selectedTag.value || selectedIndex.value === -1) return\n\n switch (key) {\n case 'close':\n handleClose(selectedTag.value, selectedIndex.value)\n break\n case 'closeOthers':\n tags.removeOtherTags(selectedIndex.value)\n break\n case 'closeLeft':\n tags.removeLeftTags(selectedIndex.value)\n break\n case 'closeRight':\n tags.removeRightTags(selectedIndex.value)\n break\n case 'closeAll':\n tags.removeAllTags()\n router.push(props.homePath)\n emit('closeAll')\n break\n }\n closeContextMenu()\n }\n\n // ====== 路由自动跟踪 ======\n watch(\n () => route.path,\n (newPath: string) => {\n const originalTitle =\n newPath === props.homePath\n ? props.homeTitle\n : (route.meta.title as string) || 'Unnamed Page'\n\n tags.addTag({\n path: newPath,\n originalTitle,\n title: formatLabel(originalTitle),\n icon:\n newPath === props.homePath\n ? props.homeIcon\n : (route.meta.icon as string),\n meta: { affix: route.meta.affix as boolean },\n })\n\n tags.setActiveTag(newPath)\n scrollToTag(newPath)\n },\n { immediate: true }\n )\n\n // ====== 暴露 ======\n defineExpose({\n /** useTagsView composable 实例 */\n tags,\n /** 滚动到指定标签 */\n scrollToTag,\n })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { n as _default, t as ThemeMode } from "./index15.vue.js";
1
+ import { n as _default, t as ThemeMode } from "./index18.vue.js";
2
2
  export { _default as C_Theme, type ThemeMode };
package/dist/C_Theme.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as _default, t as ThemeMode } from "./index15.vue.js";
1
+ import { n as _default, t as ThemeMode } from "./index18.vue.js";
2
2
  export { _default as C_Theme, type ThemeMode };
@@ -1,2 +1,2 @@
1
- import { a as presetConfigs, i as GanttTask, n as GanttOptions, r as GanttPreset, t as _default } from "./index16.vue.js";
1
+ import { a as presetConfigs, i as GanttTask, n as GanttOptions, r as GanttPreset, t as _default } from "./index19.vue.js";
2
2
  export { _default as C_VtableGantt, type GanttOptions, type GanttPreset, type GanttTask, presetConfigs as ganttPresetConfigs };
@@ -1,2 +1,2 @@
1
- import { a as presetConfigs, i as GanttTask, n as GanttOptions, r as GanttPreset, t as _default } from "./index16.vue.js";
1
+ import { a as presetConfigs, i as GanttTask, n as GanttOptions, r as GanttPreset, t as _default } from "./index19.vue.js";
2
2
  export { _default as C_VtableGantt, type GanttOptions, type GanttPreset, type GanttTask, presetConfigs as ganttPresetConfigs };
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./menuAdapter.js`);exports.createMenuOptions=e.t,exports.flattenRoutes=e.n;
@@ -0,0 +1,2 @@
1
+ import { a as RouteItem, i as BreadcrumbItem, n as createMenuOptions, o as RouteMeta, r as flattenRoutes, s as TagItem, t as MenuAdapterConfig } from "./menuAdapter.js";
2
+ export { type BreadcrumbItem, type MenuAdapterConfig, type RouteItem, type RouteMeta, type TagItem, createMenuOptions, flattenRoutes };
@@ -0,0 +1,2 @@
1
+ import { a as RouteItem, i as BreadcrumbItem, n as createMenuOptions, o as RouteMeta, r as flattenRoutes, s as TagItem, t as MenuAdapterConfig } from "./menuAdapter.js";
2
+ export { type BreadcrumbItem, type MenuAdapterConfig, type RouteItem, type RouteMeta, type TagItem, createMenuOptions, flattenRoutes };
@@ -0,0 +1 @@
1
+ import{n as e,t}from"./menuAdapter.js";export{t as createMenuOptions,e as flattenRoutes};
@@ -124,10 +124,10 @@ declare const __VLS_export: vue.DefineComponent<FormulaEditorProps, FormulaEdito
124
124
  modelValue: string;
125
125
  placeholder: string;
126
126
  disabled: boolean;
127
+ showPreview: boolean;
127
128
  variables: FormulaVariable[];
128
129
  functions: FormulaFunction[];
129
130
  sampleData: Record<string, number | string | boolean>;
130
- showPreview: boolean;
131
131
  showKeyboard: boolean;
132
132
  showVariablePanel: boolean;
133
133
  }, {}, {}, {}, string, vue.ComponentProvideOptions, false, {}, any>;
@@ -196,17 +196,17 @@ type __VLS_Slots = {} & {
196
196
  area?: (props: typeof __VLS_11) => any;
197
197
  };
198
198
  declare const __VLS_base: vue.DefineComponent<UploadProps, UploadExpose, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
199
- error: (file: UploadFileItem, error: Error) => any;
200
199
  change: (fileList: UploadFileItem[]) => any;
201
200
  success: (file: UploadFileItem, response: any) => any;
201
+ error: (file: UploadFileItem, error: Error) => any;
202
202
  progress: (file: UploadFileItem, percent: number) => any;
203
203
  remove: (file: UploadFileItem) => any;
204
204
  finish: (fileList: UploadFileItem[]) => any;
205
205
  exceed: (files: File[], fileList: UploadFileItem[]) => any;
206
206
  }, string, vue.PublicProps, Readonly<UploadProps> & Readonly<{
207
- onError?: ((file: UploadFileItem, error: Error) => any) | undefined;
208
207
  onChange?: ((fileList: UploadFileItem[]) => any) | undefined;
209
208
  onSuccess?: ((file: UploadFileItem, response: any) => any) | undefined;
209
+ onError?: ((file: UploadFileItem, error: Error) => any) | undefined;
210
210
  onProgress?: ((file: UploadFileItem, percent: number) => any) | undefined;
211
211
  onRemove?: ((file: UploadFileItem) => any) | undefined;
212
212
  onFinish?: ((fileList: UploadFileItem[]) => any) | undefined;
@@ -215,8 +215,8 @@ declare const __VLS_base: vue.DefineComponent<UploadProps, UploadExpose, {}, {},
215
215
  data: Record<string, any>;
216
216
  disabled: boolean;
217
217
  multiple: boolean;
218
- draggable: boolean;
219
218
  maxSize: number;
219
+ draggable: boolean;
220
220
  action: string;
221
221
  headers: Record<string, string>;
222
222
  directory: boolean;
@@ -259,8 +259,8 @@ type PlayerInstance = InstanceType<typeof xgplayer.default>;
259
259
  //#endregion
260
260
  //#region src/components/C_VideoPlayer/index.vue.d.ts
261
261
  declare const __VLS_export: vue.DefineComponent<VideoPlayerProps, VideoPlayerExpose, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, {
262
- error: (error: Error) => any;
263
262
  ready: () => any;
263
+ error: (error: Error) => any;
264
264
  ended: () => any;
265
265
  stateChange: (state: PlayerState) => any;
266
266
  timeUpdate: (currentTime: number, duration: number) => any;
@@ -272,8 +272,8 @@ declare const __VLS_export: vue.DefineComponent<VideoPlayerProps, VideoPlayerExp
272
272
  chapterChange: (chapter: Chapter) => any;
273
273
  progressUpdate: (data: ProgressData) => any;
274
274
  }, string, vue.PublicProps, Readonly<VideoPlayerProps> & Readonly<{
275
- onError?: ((error: Error) => any) | undefined;
276
275
  onReady?: (() => any) | undefined;
276
+ onError?: ((error: Error) => any) | undefined;
277
277
  onEnded?: (() => any) | undefined;
278
278
  onStateChange?: ((state: PlayerState) => any) | undefined;
279
279
  onTimeUpdate?: ((currentTime: number, duration: number) => any) | undefined;
package/dist/data.d.ts CHANGED
@@ -23,8 +23,8 @@ declare const __VLS_export: vue.DefineComponent<__VLS_Props, {}, {}, {}, {}, vue
23
23
  onMarkerClick?: ((marker: MapMarker$1, event: any) => any) | undefined;
24
24
  }>, {
25
25
  height: string;
26
- center: [number, number];
27
26
  zoom: number;
27
+ center: [number, number];
28
28
  markers: MapMarker$1[];
29
29
  amapKey: string;
30
30
  mapType: "osm" | "amap";
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require(`./chunk.js`),t=require(`./C_Icon.js`),n=require(`./C_Code.js`),r=require(`./C_Upload.js`),ee=require(`./C_Barcode.js`),i=require(`./C_Captcha.js`),a=require(`./C_Cascade.js`),o=require(`./C_Guide.js`),s=require(`./C_Progress.js`),c=require(`./C_Steps.js`),l=require(`./C_ActionBar.js`),u=require(`./C_Theme.js`),d=require(`./C_Language.js`),f=require(`./C_Date.js`),p=require(`./C_Editor.js`),te=require(`./C_Markdown.js`),m=require(`./C_Map.js`),ne=require(`./C_City.js`),h=require(`./C_VtableGantt.js`),g=require(`./C_Signature.js`),_=require(`./C_SplitPane.js`),v=require(`./C_CollapsePanel.js`),y=require(`./C_Time.js`),b=require(`./storage.js`),x=require(`./C_FormSearch.js`),S=require(`./C_Tree.js`),C=require(`./C_QRCode.js`),w=require(`./C_ImageCropper.js`),T=require(`./C_Draggable.js`),E=require(`./C_WaterFall.js`),D=require(`./C_FullCalendar.js`),O=require(`./C_NotificationCenter.js`),k=require(`./C_Cron.js`),A=require(`./C_FormulaEditor.js`),j=require(`./C_AntV.js`),M=require(`./C_VideoPlayer.js`),N=require(`./C_FilePreview.js`),P=require(`./C_WorkFlow.js`),F=require(`./C_Form.js`),I=require(`./C_Table.js`),L=require(`./C_GlobalSearch.js`);let R=require(`highlight.js/lib/core`);R=e.t(R),require(`highlight.js/styles/github.css`);let z=require(`highlight.js/lib/languages/javascript`);z=e.t(z);let B=require(`highlight.js/lib/languages/typescript`);B=e.t(B);let V=require(`highlight.js/lib/languages/json`);V=e.t(V);let H=require(`highlight.js/lib/languages/xml`);H=e.t(H);let U=require(`highlight.js/lib/languages/css`);U=e.t(U);let W=require(`highlight.js/lib/languages/bash`);W=e.t(W);let G=require(`highlight.js/lib/languages/yaml`);G=e.t(G);let K=require(`highlight.js/lib/languages/markdown`);K=e.t(K);let q=require(`highlight.js/lib/languages/java`);q=e.t(q);let J=require(`highlight.js/lib/languages/csharp`);J=e.t(J);let Y=require(`highlight.js/lib/languages/go`);Y=e.t(Y);let X=require(`highlight.js/lib/languages/python`);X=e.t(X);const re={cpp:()=>import(`highlight.js/lib/languages/cpp`),c:()=>import(`highlight.js/lib/languages/c`),php:()=>import(`highlight.js/lib/languages/php`),ruby:()=>import(`highlight.js/lib/languages/ruby`),rust:()=>import(`highlight.js/lib/languages/rust`),swift:()=>import(`highlight.js/lib/languages/swift`),kotlin:()=>import(`highlight.js/lib/languages/kotlin`),scss:()=>import(`highlight.js/lib/languages/scss`),less:()=>import(`highlight.js/lib/languages/less`),sql:()=>import(`highlight.js/lib/languages/sql`),dockerfile:()=>import(`highlight.js/lib/languages/dockerfile`),powershell:()=>import(`highlight.js/lib/languages/powershell`)},Z=new Set,Q=R.default;function ie(e,t){Q.registerLanguage(e,t),Z.add(e)}function ae(e){[[`javascript`,z.default],[`typescript`,B.default],[`json`,V.default],[`html`,H.default],[`xml`,H.default],[`css`,U.default],[`bash`,W.default],[`shell`,W.default],[`yaml`,G.default],[`yml`,G.default],[`markdown`,K.default],[`java`,q.default],[`csharp`,J.default],[`go`,Y.default],[`python`,X.default]].forEach(([e,t])=>{ie(e,t)}),Q.configure({ignoreUnescapedHTML:!0,languages:e.autoDetect?void 0:[]}),e.extraLanguages?.length&&oe(e.extraLanguages)}async function oe(e){let t=e.map(e=>$(e));return(await Promise.allSettled(t)).map((t,n)=>t.status===`fulfilled`?e[n]:null).filter(Boolean)}async function $(e){if(Z.has(e))return!0;let t=re[e.toLowerCase()];if(!t)return console.warn(`[HighlightPlugin] Language '${e}' not supported`),!1;try{return ie(e,(await t()).default),console.log(`[HighlightPlugin] Language '${e}' loaded successfully`),!0}catch(t){return console.error(`[HighlightPlugin] Failed to load language '${e}':`,t),!1}}const se=()=>({getHljs:()=>Q,loadLanguage:e=>$(e),loadLanguages:e=>oe(e),getLoadedLanguages:()=>Array.from(Z)});function ce(e,t={}){ae({autoDetect:!1,extraLanguages:[],...t}),typeof window<`u`&&(window.hljs=Q);let n=se();e.provide(`highlightManager`,n),e.config.globalProperties.$highlight=n,console.log(`[HighlightPlugin] 🎨 highlight.js plugin installed successfully`)}const le=[n.t,t.t,r.t,ee.t,i.t,a.t,o.t,s.t,c.t,l.t,u.t,d.t,f.t,p.t,te.t,m.t,ne.t,h.t,g.t,_.t,v.t,y.t,x.t,S.t,C.t,w.t,T.t,E.t,D.t,O.t,k.t,A.t,j.t,M.t,N.t,P.t,F.t,I.t,L.t];var ue={install:(e,t={})=>{le.forEach(t=>{let n=t.__name||t.name||`UnknownComponent`;e.component(n,t)}),t.highlight!==void 0&&ce(e,t.highlight)}};exports.ACTION_KEYS=A.i,exports.BUTTON_TEXT=D.r,exports.CATEGORY_MAP=O.a,exports.CATEGORY_TABS=O.o,exports.CRON_FIELD_META=k.a,exports.CRON_TEMPLATES=k.o,exports.C_ActionBar=l.t,exports.C_AntV=j.t,exports.C_Barcode=ee.t,exports.C_Captcha=i.t,exports.C_Cascade=a.t,exports.C_City=ne.t,exports.C_Code=n.t,exports.C_CollapsePanel=v.t,exports.C_Cron=k.t,exports.C_Date=f.t,exports.C_Draggable=T.t,exports.C_Editor=p.t,exports.C_FilePreview=N.t,exports.C_Form=F.t,exports.C_FormSearch=x.t,exports.C_FormulaEditor=A.t,exports.C_FullCalendar=D.t,exports.C_GlobalSearch=L.t,exports.C_Guide=o.t,exports.C_Icon=t.t,exports.C_ImageCropper=w.t,exports.C_Language=d.t,exports.C_Map=m.t,exports.C_Markdown=te.t,exports.C_NotificationCenter=O.t,exports.C_Progress=s.t,exports.C_QRCode=C.t,exports.C_Signature=g.t,exports.C_SplitPane=_.t,exports.C_Steps=c.t,exports.C_Table=I.t,exports.C_Theme=u.t,exports.C_Time=y.t,exports.C_Tree=S.t,exports.C_Upload=r.t,exports.C_VideoPlayer=M.t,exports.C_VtableGantt=h.t,exports.C_WaterFall=E.t,exports.C_WorkFlow=P.t,exports.DEFAULT_ANIMATION_DURATION=E.a,exports.DEFAULT_BREAKPOINTS=E.o,exports.DEFAULT_CRON_EXPRESSION=k.s,exports.DEFAULT_CRON_VALUE=k.c,exports.DEFAULT_EDIT_FORM=D.i,exports.DEFAULT_FIELD_VALUE=k.l,exports.DEFAULT_FUNCTIONS=A.a,exports.DEFAULT_GAP=E.s,exports.DEFAULT_HEARTBEAT_INTERVAL=M.c,exports.DEFAULT_MAP_CONFIG=m.n,exports.DEFAULT_MAX_BADGE_COUNT=O.s,exports.DEFAULT_PAGE_SIZE=O.c,exports.DEFAULT_PLAYBACK_RATE=M.l,exports.DEFAULT_PLAYBACK_RATES=M.u,exports.DEFAULT_POLLING_INTERVAL=O.l,exports.DEFAULT_SKELETON_COUNT=E.c,exports.DEFAULT_STORAGE_KEY=O.u,exports.DEFAULT_VOLUME=M.d,exports.DEFAULT_WS_HEARTBEAT_INTERVAL=O.d,exports.DEFAULT_WS_HEARTBEAT_MESSAGE=O.f,exports.DEFAULT_WS_MAX_RECONNECT=O.p,exports.DEFAULT_WS_RECONNECT_INTERVAL=O.m,exports.DYNAMIC_FORM_STATE_KEY=F.n,exports.EVENT_COLORS=D.a,exports.FIELD_TYPE_OPTIONS=F.r,exports.FORM_DEFAULTS=F.c,exports.HEADER_TOOLBAR=D.o,exports.INFINITE_SCROLL_THRESHOLD=E.l,exports.KEYBOARD_SHORTCUTS=M.f,exports.LAYOUTS_WITH_OWN_CONTROLS=F.l,exports.MAP_TYPES=m.r,exports.MOCK_MESSAGES=O.h,exports.MONTH_LABELS=k.u,exports.NUMBER_KEYS=A.o,exports.OPERATORS=A.s,exports.OPERATOR_KEYS=A.c,exports.OSM_TILE_CONFIG=m.i,exports.PRIORITY_MAP=O.g,exports.PROGRESS_THROTTLE_INTERVAL=M.p,exports.QUALITY_LABEL_MAP=M.m,exports.SEEK_STEP=M.h,exports.SKELETON_HEIGHT_RANGE=E.u,exports.SOURCE_TYPE_MAP=M.g,exports.SPACED_OPERATORS=A.l,exports.STORAGE_KEYS=M._,exports.STORAGE_PREFIX=M.v,exports.VOLUME_STEP=M.y,exports.WATERMARK_DEFAULT_STYLE=M.b,exports.WEEK_LABELS=k.d,exports.createEditModeChecker=I.p,exports.default=ue,exports.formatFileSize=r.s,exports.ganttPresetConfigs=h.n,exports.getFileIcon=r.c,exports.getItem=b.t,exports.printPresets=I.s,exports.registerRenderer=F.a,exports.removeAllItem=b.n,exports.removeItem=b.r,exports.resolveConfig=I.m,exports.resolveFormConfig=F.u,exports.setItem=b.i,exports.setupHighlight=ce,exports.shouldShowActions=F.d,exports.treePresetConfigs=S.r,exports.useBookmarks=M.r,exports.useCalendarEvents=D.n,exports.useCellEdit=I.d,exports.useChapters=M.i,exports.useChunkUpload=r.a,exports.useCollapsePanel=v.n,exports.useCronDescription=k.n,exports.useCronParser=k.i,exports.useCronPreview=k.r,exports.useCropperCore=w.n,exports.useDragDrop=r.n,exports.useDraggableLayout=T.n,exports.useDynamicFormState=F.i,exports.useDynamicRows=I.o,exports.useEdgeInteraction=j.n,exports.useFileHash=r.o,exports.useFilePreview=N.r,exports.useFormRenderer=F.o,exports.useFormState=F.s,exports.useFormulaEvaluator=A.n,exports.useFormulaParser=A.r,exports.useFullscreen=N.n,exports.useGlobalSearch=L.n,exports.useGraphBase=j.i,exports.useGraphExport=j.r,exports.useHighlight=se,exports.useInfiniteScroll=E.n,exports.useModalEdit=I.u,exports.useNotificationCore=O.r,exports.useNotificationFormat=O.n,exports.useNotificationWS=O.i,exports.usePagination=I.i,exports.usePlaybackControl=M.o,exports.usePlayerCore=M.s,exports.usePrintWatermark=I.c,exports.useProgressTracker=M.a,exports.useQRCode=C.n,exports.useResponsiveColumns=E.i,exports.useRowEdit=I.f,exports.useSearchHistory=x.r,exports.useSearchState=x.n,exports.useSignatureCanvas=g.i,exports.useSignatureExport=g.n,exports.useSignatureHistory=g.r,exports.useSplitResize=_.n,exports.useSubtitle=M.n,exports.useTableActions=I.r,exports.useTableColumns=I.n,exports.useTableExpand=I.l,exports.useTableManager=I.a,exports.useTimeSelection=y.n,exports.useTreeOperations=S.n,exports.useUploadCore=r.r,exports.useUploadQueue=r.i,exports.useWaterFallLayout=E.r,exports.useWorkflowNodes=P.i,exports.useWorkflowPreview=P.n,exports.useWorkflowValidation=P.r;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require(`./chunk.js`),t=require(`./C_Icon.js`),n=require(`./C_Code.js`),r=require(`./C_Upload.js`),i=require(`./C_Barcode.js`),a=require(`./C_Captcha.js`),ee=require(`./C_Cascade.js`),o=require(`./C_Guide.js`),te=require(`./C_Progress.js`),s=require(`./C_Steps.js`),c=require(`./C_ActionBar.js`),ne=require(`./C_Theme.js`),l=require(`./C_Language.js`),u=require(`./C_Date.js`),d=require(`./C_Editor.js`),f=require(`./C_Markdown.js`),p=require(`./C_Map.js`),m=require(`./C_City.js`),h=require(`./C_VtableGantt.js`),g=require(`./C_Signature.js`),_=require(`./C_SplitPane.js`),v=require(`./C_CollapsePanel.js`),y=require(`./C_Time.js`),b=require(`./storage.js`),x=require(`./C_FormSearch.js`),S=require(`./C_Tree.js`),C=require(`./C_QRCode.js`),w=require(`./C_ImageCropper.js`),T=require(`./C_Draggable.js`),E=require(`./C_WaterFall.js`),D=require(`./C_FullCalendar.js`),O=require(`./C_NotificationCenter.js`),k=require(`./C_Cron.js`),A=require(`./C_FormulaEditor.js`),j=require(`./C_AntV.js`),M=require(`./C_VideoPlayer.js`),N=require(`./C_FilePreview.js`),P=require(`./C_WorkFlow.js`),F=require(`./C_Form.js`),I=require(`./C_Table.js`),L=require(`./C_GlobalSearch.js`),re=require(`./menuAdapter.js`),ie=require(`./C_Menu.js`),ae=require(`./C_Breadcrumb.js`),R=require(`./C_TagsView.js`);let z=require(`highlight.js/lib/core`);z=e.t(z),require(`highlight.js/styles/github.css`);let B=require(`highlight.js/lib/languages/javascript`);B=e.t(B);let V=require(`highlight.js/lib/languages/typescript`);V=e.t(V);let H=require(`highlight.js/lib/languages/json`);H=e.t(H);let U=require(`highlight.js/lib/languages/xml`);U=e.t(U);let W=require(`highlight.js/lib/languages/css`);W=e.t(W);let G=require(`highlight.js/lib/languages/bash`);G=e.t(G);let K=require(`highlight.js/lib/languages/yaml`);K=e.t(K);let q=require(`highlight.js/lib/languages/markdown`);q=e.t(q);let J=require(`highlight.js/lib/languages/java`);J=e.t(J);let Y=require(`highlight.js/lib/languages/csharp`);Y=e.t(Y);let X=require(`highlight.js/lib/languages/go`);X=e.t(X);let Z=require(`highlight.js/lib/languages/python`);Z=e.t(Z);const oe={cpp:()=>import(`highlight.js/lib/languages/cpp`),c:()=>import(`highlight.js/lib/languages/c`),php:()=>import(`highlight.js/lib/languages/php`),ruby:()=>import(`highlight.js/lib/languages/ruby`),rust:()=>import(`highlight.js/lib/languages/rust`),swift:()=>import(`highlight.js/lib/languages/swift`),kotlin:()=>import(`highlight.js/lib/languages/kotlin`),scss:()=>import(`highlight.js/lib/languages/scss`),less:()=>import(`highlight.js/lib/languages/less`),sql:()=>import(`highlight.js/lib/languages/sql`),dockerfile:()=>import(`highlight.js/lib/languages/dockerfile`),powershell:()=>import(`highlight.js/lib/languages/powershell`)},Q=new Set,$=z.default;function se(e,t){$.registerLanguage(e,t),Q.add(e)}function ce(e){[[`javascript`,B.default],[`typescript`,V.default],[`json`,H.default],[`html`,U.default],[`xml`,U.default],[`css`,W.default],[`bash`,G.default],[`shell`,G.default],[`yaml`,K.default],[`yml`,K.default],[`markdown`,q.default],[`java`,J.default],[`csharp`,Y.default],[`go`,X.default],[`python`,Z.default]].forEach(([e,t])=>{se(e,t)}),$.configure({ignoreUnescapedHTML:!0,languages:e.autoDetect?void 0:[]}),e.extraLanguages?.length&&le(e.extraLanguages)}async function le(e){let t=e.map(e=>ue(e));return(await Promise.allSettled(t)).map((t,n)=>t.status===`fulfilled`?e[n]:null).filter(Boolean)}async function ue(e){if(Q.has(e))return!0;let t=oe[e.toLowerCase()];if(!t)return console.warn(`[HighlightPlugin] Language '${e}' not supported`),!1;try{return se(e,(await t()).default),console.log(`[HighlightPlugin] Language '${e}' loaded successfully`),!0}catch(t){return console.error(`[HighlightPlugin] Failed to load language '${e}':`,t),!1}}const de=()=>({getHljs:()=>$,loadLanguage:e=>ue(e),loadLanguages:e=>le(e),getLoadedLanguages:()=>Array.from(Q)});function fe(e,t={}){ce({autoDetect:!1,extraLanguages:[],...t}),typeof window<`u`&&(window.hljs=$);let n=de();e.provide(`highlightManager`,n),e.config.globalProperties.$highlight=n,console.log(`[HighlightPlugin] 🎨 highlight.js plugin installed successfully`)}const pe=[n.t,t.t,r.t,i.t,a.t,ee.t,o.t,te.t,s.t,c.t,ne.t,l.t,u.t,d.t,f.t,p.t,m.t,h.t,g.t,_.t,v.t,y.t,x.t,S.t,C.t,w.t,T.t,E.t,D.t,O.t,k.t,A.t,j.t,M.t,N.t,P.t,F.t,I.t,L.t,ie.t,ae.t,R.t];var me={install:(e,t={})=>{pe.forEach(t=>{let n=t.__name||t.name||`UnknownComponent`;e.component(n,t)}),t.highlight!==void 0&&fe(e,t.highlight)}};exports.ACTION_KEYS=A.i,exports.BUTTON_TEXT=D.r,exports.CATEGORY_MAP=O.a,exports.CATEGORY_TABS=O.o,exports.CRON_FIELD_META=k.a,exports.CRON_TEMPLATES=k.o,exports.C_ActionBar=c.t,exports.C_AntV=j.t,exports.C_Barcode=i.t,exports.C_Breadcrumb=ae.t,exports.C_Captcha=a.t,exports.C_Cascade=ee.t,exports.C_City=m.t,exports.C_Code=n.t,exports.C_CollapsePanel=v.t,exports.C_Cron=k.t,exports.C_Date=u.t,exports.C_Draggable=T.t,exports.C_Editor=d.t,exports.C_FilePreview=N.t,exports.C_Form=F.t,exports.C_FormSearch=x.t,exports.C_FormulaEditor=A.t,exports.C_FullCalendar=D.t,exports.C_GlobalSearch=L.t,exports.C_Guide=o.t,exports.C_Icon=t.t,exports.C_ImageCropper=w.t,exports.C_Language=l.t,exports.C_Map=p.t,exports.C_Markdown=f.t,exports.C_Menu=ie.t,exports.C_NotificationCenter=O.t,exports.C_Progress=te.t,exports.C_QRCode=C.t,exports.C_Signature=g.t,exports.C_SplitPane=_.t,exports.C_Steps=s.t,exports.C_Table=I.t,exports.C_TagsView=R.t,exports.C_Theme=ne.t,exports.C_Time=y.t,exports.C_Tree=S.t,exports.C_Upload=r.t,exports.C_VideoPlayer=M.t,exports.C_VtableGantt=h.t,exports.C_WaterFall=E.t,exports.C_WorkFlow=P.t,exports.DEFAULT_ANIMATION_DURATION=E.a,exports.DEFAULT_BREAKPOINTS=E.o,exports.DEFAULT_CRON_EXPRESSION=k.s,exports.DEFAULT_CRON_VALUE=k.c,exports.DEFAULT_EDIT_FORM=D.i,exports.DEFAULT_FIELD_VALUE=k.l,exports.DEFAULT_FUNCTIONS=A.a,exports.DEFAULT_GAP=E.s,exports.DEFAULT_HEARTBEAT_INTERVAL=M.c,exports.DEFAULT_MAP_CONFIG=p.n,exports.DEFAULT_MAX_BADGE_COUNT=O.s,exports.DEFAULT_PAGE_SIZE=O.c,exports.DEFAULT_PLAYBACK_RATE=M.l,exports.DEFAULT_PLAYBACK_RATES=M.u,exports.DEFAULT_POLLING_INTERVAL=O.l,exports.DEFAULT_SKELETON_COUNT=E.c,exports.DEFAULT_STORAGE_KEY=O.u,exports.DEFAULT_VOLUME=M.d,exports.DEFAULT_WS_HEARTBEAT_INTERVAL=O.d,exports.DEFAULT_WS_HEARTBEAT_MESSAGE=O.f,exports.DEFAULT_WS_MAX_RECONNECT=O.p,exports.DEFAULT_WS_RECONNECT_INTERVAL=O.m,exports.DYNAMIC_FORM_STATE_KEY=F.n,exports.EVENT_COLORS=D.a,exports.FIELD_TYPE_OPTIONS=F.r,exports.FORM_DEFAULTS=F.c,exports.HEADER_TOOLBAR=D.o,exports.INFINITE_SCROLL_THRESHOLD=E.l,exports.KEYBOARD_SHORTCUTS=M.f,exports.LAYOUTS_WITH_OWN_CONTROLS=F.l,exports.MAP_TYPES=p.r,exports.MOCK_MESSAGES=O.h,exports.MONTH_LABELS=k.u,exports.NUMBER_KEYS=A.o,exports.OPERATORS=A.s,exports.OPERATOR_KEYS=A.c,exports.OSM_TILE_CONFIG=p.i,exports.PRIORITY_MAP=O.g,exports.PROGRESS_THROTTLE_INTERVAL=M.p,exports.QUALITY_LABEL_MAP=M.m,exports.SEEK_STEP=M.h,exports.SKELETON_HEIGHT_RANGE=E.u,exports.SOURCE_TYPE_MAP=M.g,exports.SPACED_OPERATORS=A.l,exports.STORAGE_KEYS=M._,exports.STORAGE_PREFIX=M.v,exports.VOLUME_STEP=M.y,exports.WATERMARK_DEFAULT_STYLE=M.b,exports.WEEK_LABELS=k.d,exports.createEditModeChecker=I.p,exports.createMenuOptions=re.t,exports.default=me,exports.flattenRoutes=re.n,exports.formatFileSize=r.s,exports.ganttPresetConfigs=h.n,exports.getFileIcon=r.c,exports.getItem=b.t,exports.printPresets=I.s,exports.registerRenderer=F.a,exports.removeAllItem=b.n,exports.removeItem=b.r,exports.resolveConfig=I.m,exports.resolveFormConfig=F.u,exports.setItem=b.i,exports.setupHighlight=fe,exports.shouldShowActions=F.d,exports.treePresetConfigs=S.r,exports.useBookmarks=M.r,exports.useCalendarEvents=D.n,exports.useCellEdit=I.d,exports.useChapters=M.i,exports.useChunkUpload=r.a,exports.useCollapsePanel=v.n,exports.useCronDescription=k.n,exports.useCronParser=k.i,exports.useCronPreview=k.r,exports.useCropperCore=w.n,exports.useDragDrop=r.n,exports.useDraggableLayout=T.n,exports.useDynamicFormState=F.i,exports.useDynamicRows=I.o,exports.useEdgeInteraction=j.n,exports.useFileHash=r.o,exports.useFilePreview=N.r,exports.useFormRenderer=F.o,exports.useFormState=F.s,exports.useFormulaEvaluator=A.n,exports.useFormulaParser=A.r,exports.useFullscreen=N.n,exports.useGlobalSearch=L.n,exports.useGraphBase=j.i,exports.useGraphExport=j.r,exports.useHighlight=de,exports.useInfiniteScroll=E.n,exports.useModalEdit=I.u,exports.useNotificationCore=O.r,exports.useNotificationFormat=O.n,exports.useNotificationWS=O.i,exports.usePagination=I.i,exports.usePlaybackControl=M.o,exports.usePlayerCore=M.s,exports.usePrintWatermark=I.c,exports.useProgressTracker=M.a,exports.useQRCode=C.n,exports.useResponsiveColumns=E.i,exports.useRowEdit=I.f,exports.useSearchHistory=x.r,exports.useSearchState=x.n,exports.useSignatureCanvas=g.i,exports.useSignatureExport=g.n,exports.useSignatureHistory=g.r,exports.useSplitResize=_.n,exports.useSubtitle=M.n,exports.useTableActions=I.r,exports.useTableColumns=I.n,exports.useTableExpand=I.l,exports.useTableManager=I.a,exports.useTagsView=R.n,exports.useTimeSelection=y.n,exports.useTreeOperations=S.n,exports.useUploadCore=r.r,exports.useUploadQueue=r.i,exports.useWaterFallLayout=E.r,exports.useWorkflowNodes=P.i,exports.useWorkflowPreview=P.n,exports.useWorkflowValidation=P.r;
2
2
  //# sourceMappingURL=index.cjs.map