@robot-admin/layout 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1181 @@
1
+ import * as pinia from 'pinia';
2
+ import * as vue from 'vue';
3
+ import { VNode, App, InjectionKey, ComputedRef, WritableComputedRef, Component, MaybeRefOrGetter, Ref } from 'vue';
4
+ import { RouteLocationNormalizedLoaded } from 'vue-router';
5
+
6
+ /** 与具体主题实现无关的标准主题模式。 */
7
+ type ThemeMode = "light" | "dark" | "system";
8
+ /** 内置布局模式。 */
9
+ type LayoutMode = "side" | "top" | "mix" | "mix-top" | "reverse-horizontal-mix" | "card-layout";
10
+ /** 菜单展开方式。 */
11
+ type MenuExpandMode = "inline" | "panel";
12
+ /** 页面动画类型。 */
13
+ type TransitionType = "fade" | "slide" | "zoom" | "none";
14
+ /** 圆角大小。 */
15
+ type BorderRadiusSize = "small" | "medium" | "large";
16
+ /** 标签页风格。 */
17
+ type TagsViewStyle = "default" | "card" | "smart";
18
+ /** UI 无关的布局设置协议。 */
19
+ interface SettingsState {
20
+ themeMode: ThemeMode;
21
+ primaryColor: string;
22
+ borderRadius: BorderRadiusSize;
23
+ transitionType: TransitionType;
24
+ enableTransition: boolean;
25
+ layoutMode: LayoutMode;
26
+ menuExpandMode: MenuExpandMode;
27
+ collapsed: boolean;
28
+ fixedHeader: boolean;
29
+ showBreadcrumb: boolean;
30
+ showBreadcrumbIcon: boolean;
31
+ showTagsView: boolean;
32
+ tagsViewHeight: number;
33
+ tagsViewStyle: TagsViewStyle;
34
+ showFooter: boolean;
35
+ sidebarWidth: number;
36
+ sidebarCollapsedWidth: number;
37
+ headerHeight: number;
38
+ enableHotkeys: boolean;
39
+ /** @deprecated 包版本不应参与配置迁移,请使用 SETTINGS_CONFIG_SCHEMA_VERSION。 */
40
+ version: string;
41
+ }
42
+ /** 主题预设方案。 */
43
+ interface ThemePreset {
44
+ name: string;
45
+ description?: string;
46
+ icon: string;
47
+ primaryColor: string;
48
+ settings?: Partial<Omit<SettingsState, "themeMode" | "layoutMode" | "primaryColor">>;
49
+ }
50
+ /** Settings Store 初始化选项。 */
51
+ interface SettingsStoreOptions {
52
+ id?: string;
53
+ defaults?: Partial<SettingsState>;
54
+ onThemeModeChange?: (mode: ThemeMode) => void | Promise<void>;
55
+ /** 是否自动同步 CSS 变量;默认 true,微前端可关闭后自行绑定作用域。 */
56
+ syncCssVariables?: boolean;
57
+ }
58
+ /** 由宿主应用实现的高副作用操作。 */
59
+ interface SettingsDrawerActions {
60
+ clearCache?: () => void | Promise<void>;
61
+ reloadPage?: () => void;
62
+ }
63
+ /** 布局模式展示信息。 */
64
+ interface LayoutInfo {
65
+ mode: LayoutMode;
66
+ label: string;
67
+ description: string;
68
+ icon: string;
69
+ }
70
+ /** 预设颜色。 */
71
+ interface PresetColor {
72
+ name: string;
73
+ value: string;
74
+ }
75
+
76
+ /** 当前导入导出配置格式版本。 */
77
+ declare const SETTINGS_CONFIG_SCHEMA_VERSION: 1;
78
+ interface LayoutVisualEffects {
79
+ gray?: boolean;
80
+ colorWeak?: boolean;
81
+ watermark?: {
82
+ enabled: boolean;
83
+ text: string;
84
+ };
85
+ }
86
+ /** 经校验后可原子应用的布局配置。 */
87
+ interface LayoutSettingsConfig extends LayoutVisualEffects {
88
+ schemaVersion?: number;
89
+ settings?: Partial<SettingsState>;
90
+ }
91
+ /** 校验需要跨字段判断的布局尺寸约束。 */
92
+ declare function assertLayoutSettingsRelationships(settings: Pick<SettingsState, "sidebarCollapsedWidth" | "sidebarWidth">): void;
93
+ /**
94
+ * 校验来自配置文件等不可信来源的设置。
95
+ * 未知键为向前兼容而忽略;已知键的非法值会让整次导入失败。
96
+ */
97
+ declare function sanitizeSettingsPatch(input: unknown): Partial<SettingsState>;
98
+ /**
99
+ * 一次性校验完整导入文件。此函数不修改任何响应式状态或浏览器 API,
100
+ * 消费方可以在成功返回后再原子应用结果。
101
+ */
102
+ declare function sanitizeLayoutSettingsConfig(input: unknown): LayoutSettingsConfig;
103
+ /**
104
+ * 调整颜色亮度。
105
+ * @returns 调整后的 #rrggbb;输入非法时原样返回。
106
+ */
107
+ declare function adjustColor(color: string, amount: number): string;
108
+
109
+ /**
110
+ * 布局和设置系统 - 常量定义
111
+ */
112
+
113
+ declare const PRESET_COLORS: PresetColor[];
114
+ declare const THEME_PRESETS: ThemePreset[];
115
+ declare const LAYOUT_MODES: LayoutInfo[];
116
+ declare const BORDER_RADIUS_MAP: {
117
+ readonly small: "4px";
118
+ readonly medium: "6px";
119
+ readonly large: "8px";
120
+ };
121
+ declare const TRANSITION_MAP: {
122
+ readonly fade: "fade-transform";
123
+ readonly slide: "fade-slide";
124
+ readonly zoom: "fade-zoom";
125
+ readonly none: "";
126
+ };
127
+ declare const DEFAULT_SETTINGS: SettingsState;
128
+ /** Color swatches for color picker */
129
+ declare const COLOR_SWATCHES: string[];
130
+
131
+ /**
132
+ * @robot-admin/layout
133
+ *
134
+ * 菜单路由相关类型定义
135
+ * 从主项目提取的通用菜单类型,供布局骨架和消费方共用
136
+ */
137
+
138
+ /**
139
+ * 菜单项类型
140
+ * - `group` 分组
141
+ * - `divider` 分割线
142
+ * - `item` 普通菜单项
143
+ */
144
+ type MenuItemType = "group" | "divider" | "item";
145
+ /** 可由宿主通过交叉类型安全扩展的菜单元数据。 */
146
+ interface MenuMeta extends Record<string, unknown> {
147
+ /** 页面标题 */
148
+ title?: string;
149
+ /** 菜单项的图标 */
150
+ icon?: string;
151
+ /** 是否在菜单中隐藏 */
152
+ hidden?: boolean;
153
+ /** 是否固定在标签栏 */
154
+ affix?: boolean;
155
+ /** 是否缓存该页面 */
156
+ keepAlive?: boolean;
157
+ /** 是否全屏显示 */
158
+ full?: boolean;
159
+ /** 外部链接 URL */
160
+ link?: string;
161
+ }
162
+ /**
163
+ * 菜单选项的详细类型定义
164
+ */
165
+ interface MenuOptions<TMeta extends MenuMeta = MenuMeta> {
166
+ /** 菜单项类型(分组/分割线) */
167
+ type?: MenuItemType;
168
+ /** 唯一标识 */
169
+ key?: string;
170
+ /** 菜单跳转路径 */
171
+ path?: string;
172
+ /** 菜单名称(可对应路由名) */
173
+ name?: string;
174
+ /** 菜单项对应的组件名称 */
175
+ component?: string;
176
+ /** 重定向路径 */
177
+ redirect?: string;
178
+ /** 菜单显示的文本 */
179
+ label?: string;
180
+ /** 图标 */
181
+ icon?: string | (() => VNode) | VNode;
182
+ /** 是否禁用此菜单项 */
183
+ disabled?: boolean;
184
+ /** 元数据 */
185
+ meta?: TMeta;
186
+ /** 子菜单 */
187
+ children?: MenuOptions<TMeta>[];
188
+ }
189
+ /**
190
+ * 标签栏项类型定义
191
+ */
192
+ interface MenuTag {
193
+ /** 标签项对应的路由路径 */
194
+ path: string;
195
+ /** 标签显示的标题(已翻译) */
196
+ title: string;
197
+ /** 原始标题(中文,用于重新翻译) */
198
+ originalTitle?: string;
199
+ /** 显示的图标 */
200
+ icon?: string;
201
+ /** 标签相关元数据 */
202
+ meta?: {
203
+ /** 是否固定在标签栏 */
204
+ affix?: boolean;
205
+ };
206
+ }
207
+
208
+ /**
209
+ * 初始化布局系统
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * import { setupLayout } from '@robot-admin/layout/vue'
214
+ * setupLayout(app, {
215
+ * onThemeModeChange: async (mode) => {
216
+ * const themeStore = useThemeStore()
217
+ * await themeStore.setMode(mode)
218
+ * }
219
+ * })
220
+ * ```
221
+ */
222
+ declare function setupLayout(app: App, options?: SettingsStoreOptions): pinia.StoreDefinition<string, Pick<{
223
+ defaults: Readonly<{
224
+ themeMode: ThemeMode;
225
+ primaryColor: string;
226
+ borderRadius: BorderRadiusSize;
227
+ transitionType: TransitionType;
228
+ enableTransition: boolean;
229
+ layoutMode: LayoutMode;
230
+ menuExpandMode: MenuExpandMode;
231
+ collapsed: boolean;
232
+ fixedHeader: boolean;
233
+ showBreadcrumb: boolean;
234
+ showBreadcrumbIcon: boolean;
235
+ showTagsView: boolean;
236
+ tagsViewHeight: number;
237
+ tagsViewStyle: TagsViewStyle;
238
+ showFooter: boolean;
239
+ sidebarWidth: number;
240
+ sidebarCollapsedWidth: number;
241
+ headerHeight: number;
242
+ enableHotkeys: boolean;
243
+ version: string;
244
+ }>;
245
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
246
+ primaryColor: vue.Ref<string, string>;
247
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
248
+ transitionType: vue.Ref<TransitionType, TransitionType>;
249
+ enableTransition: vue.Ref<boolean, boolean>;
250
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
251
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
252
+ collapsed: vue.Ref<boolean, boolean>;
253
+ fixedHeader: vue.Ref<boolean, boolean>;
254
+ showBreadcrumb: vue.Ref<boolean, boolean>;
255
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
256
+ showTagsView: vue.Ref<boolean, boolean>;
257
+ tagsViewHeight: vue.Ref<number, number>;
258
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
259
+ showFooter: vue.Ref<boolean, boolean>;
260
+ sidebarWidth: vue.Ref<number, number>;
261
+ sidebarCollapsedWidth: vue.Ref<number, number>;
262
+ headerHeight: vue.Ref<number, number>;
263
+ enableHotkeys: vue.Ref<boolean, boolean>;
264
+ version: vue.Ref<string, string>;
265
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
266
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
267
+ shouldEnableTransition: vue.ComputedRef<boolean>;
268
+ settingsState: vue.ComputedRef<SettingsState>;
269
+ syncCSSVariables: () => void | undefined;
270
+ applyPreset: (preset: ThemePreset) => Promise<void>;
271
+ resetSettings: () => void;
272
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
273
+ toggleCollapse: () => void;
274
+ }, "themeMode" | "layoutMode" | "primaryColor" | "borderRadius" | "transitionType" | "enableTransition" | "menuExpandMode" | "collapsed" | "fixedHeader" | "showBreadcrumb" | "showBreadcrumbIcon" | "showTagsView" | "tagsViewHeight" | "tagsViewStyle" | "showFooter" | "sidebarWidth" | "sidebarCollapsedWidth" | "headerHeight" | "enableHotkeys" | "version" | "defaults">, Pick<{
275
+ defaults: Readonly<{
276
+ themeMode: ThemeMode;
277
+ primaryColor: string;
278
+ borderRadius: BorderRadiusSize;
279
+ transitionType: TransitionType;
280
+ enableTransition: boolean;
281
+ layoutMode: LayoutMode;
282
+ menuExpandMode: MenuExpandMode;
283
+ collapsed: boolean;
284
+ fixedHeader: boolean;
285
+ showBreadcrumb: boolean;
286
+ showBreadcrumbIcon: boolean;
287
+ showTagsView: boolean;
288
+ tagsViewHeight: number;
289
+ tagsViewStyle: TagsViewStyle;
290
+ showFooter: boolean;
291
+ sidebarWidth: number;
292
+ sidebarCollapsedWidth: number;
293
+ headerHeight: number;
294
+ enableHotkeys: boolean;
295
+ version: string;
296
+ }>;
297
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
298
+ primaryColor: vue.Ref<string, string>;
299
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
300
+ transitionType: vue.Ref<TransitionType, TransitionType>;
301
+ enableTransition: vue.Ref<boolean, boolean>;
302
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
303
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
304
+ collapsed: vue.Ref<boolean, boolean>;
305
+ fixedHeader: vue.Ref<boolean, boolean>;
306
+ showBreadcrumb: vue.Ref<boolean, boolean>;
307
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
308
+ showTagsView: vue.Ref<boolean, boolean>;
309
+ tagsViewHeight: vue.Ref<number, number>;
310
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
311
+ showFooter: vue.Ref<boolean, boolean>;
312
+ sidebarWidth: vue.Ref<number, number>;
313
+ sidebarCollapsedWidth: vue.Ref<number, number>;
314
+ headerHeight: vue.Ref<number, number>;
315
+ enableHotkeys: vue.Ref<boolean, boolean>;
316
+ version: vue.Ref<string, string>;
317
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
318
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
319
+ shouldEnableTransition: vue.ComputedRef<boolean>;
320
+ settingsState: vue.ComputedRef<SettingsState>;
321
+ syncCSSVariables: () => void | undefined;
322
+ applyPreset: (preset: ThemePreset) => Promise<void>;
323
+ resetSettings: () => void;
324
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
325
+ toggleCollapse: () => void;
326
+ }, "borderRadiusValue" | "transitionName" | "shouldEnableTransition" | "settingsState">, Pick<{
327
+ defaults: Readonly<{
328
+ themeMode: ThemeMode;
329
+ primaryColor: string;
330
+ borderRadius: BorderRadiusSize;
331
+ transitionType: TransitionType;
332
+ enableTransition: boolean;
333
+ layoutMode: LayoutMode;
334
+ menuExpandMode: MenuExpandMode;
335
+ collapsed: boolean;
336
+ fixedHeader: boolean;
337
+ showBreadcrumb: boolean;
338
+ showBreadcrumbIcon: boolean;
339
+ showTagsView: boolean;
340
+ tagsViewHeight: number;
341
+ tagsViewStyle: TagsViewStyle;
342
+ showFooter: boolean;
343
+ sidebarWidth: number;
344
+ sidebarCollapsedWidth: number;
345
+ headerHeight: number;
346
+ enableHotkeys: boolean;
347
+ version: string;
348
+ }>;
349
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
350
+ primaryColor: vue.Ref<string, string>;
351
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
352
+ transitionType: vue.Ref<TransitionType, TransitionType>;
353
+ enableTransition: vue.Ref<boolean, boolean>;
354
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
355
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
356
+ collapsed: vue.Ref<boolean, boolean>;
357
+ fixedHeader: vue.Ref<boolean, boolean>;
358
+ showBreadcrumb: vue.Ref<boolean, boolean>;
359
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
360
+ showTagsView: vue.Ref<boolean, boolean>;
361
+ tagsViewHeight: vue.Ref<number, number>;
362
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
363
+ showFooter: vue.Ref<boolean, boolean>;
364
+ sidebarWidth: vue.Ref<number, number>;
365
+ sidebarCollapsedWidth: vue.Ref<number, number>;
366
+ headerHeight: vue.Ref<number, number>;
367
+ enableHotkeys: vue.Ref<boolean, boolean>;
368
+ version: vue.Ref<string, string>;
369
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
370
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
371
+ shouldEnableTransition: vue.ComputedRef<boolean>;
372
+ settingsState: vue.ComputedRef<SettingsState>;
373
+ syncCSSVariables: () => void | undefined;
374
+ applyPreset: (preset: ThemePreset) => Promise<void>;
375
+ resetSettings: () => void;
376
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
377
+ toggleCollapse: () => void;
378
+ }, "syncCSSVariables" | "applyPreset" | "resetSettings" | "updateThemeMode" | "toggleCollapse">>;
379
+
380
+ /**
381
+ * 创建设置管理 Store
382
+ * @param options - 配置选项
383
+ */
384
+ declare function createSettingsStore(options?: SettingsStoreOptions): pinia.StoreDefinition<string, Pick<{
385
+ /** 当前 Store 实例实际使用的默认值。 */
386
+ defaults: Readonly<{
387
+ themeMode: ThemeMode;
388
+ primaryColor: string;
389
+ borderRadius: BorderRadiusSize;
390
+ transitionType: TransitionType;
391
+ enableTransition: boolean;
392
+ layoutMode: LayoutMode;
393
+ menuExpandMode: MenuExpandMode;
394
+ collapsed: boolean;
395
+ fixedHeader: boolean;
396
+ showBreadcrumb: boolean;
397
+ showBreadcrumbIcon: boolean;
398
+ showTagsView: boolean;
399
+ tagsViewHeight: number;
400
+ tagsViewStyle: TagsViewStyle;
401
+ showFooter: boolean;
402
+ sidebarWidth: number;
403
+ sidebarCollapsedWidth: number;
404
+ headerHeight: number;
405
+ enableHotkeys: boolean;
406
+ version: string;
407
+ }>;
408
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
409
+ primaryColor: vue.Ref<string, string>;
410
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
411
+ transitionType: vue.Ref<TransitionType, TransitionType>;
412
+ enableTransition: vue.Ref<boolean, boolean>;
413
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
414
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
415
+ collapsed: vue.Ref<boolean, boolean>;
416
+ fixedHeader: vue.Ref<boolean, boolean>;
417
+ showBreadcrumb: vue.Ref<boolean, boolean>;
418
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
419
+ showTagsView: vue.Ref<boolean, boolean>;
420
+ tagsViewHeight: vue.Ref<number, number>;
421
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
422
+ showFooter: vue.Ref<boolean, boolean>;
423
+ sidebarWidth: vue.Ref<number, number>;
424
+ sidebarCollapsedWidth: vue.Ref<number, number>;
425
+ headerHeight: vue.Ref<number, number>;
426
+ enableHotkeys: vue.Ref<boolean, boolean>;
427
+ version: vue.Ref<string, string>;
428
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
429
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
430
+ shouldEnableTransition: vue.ComputedRef<boolean>;
431
+ settingsState: vue.ComputedRef<SettingsState>;
432
+ syncCSSVariables: () => void | undefined;
433
+ applyPreset: (preset: ThemePreset) => Promise<void>;
434
+ resetSettings: () => void;
435
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
436
+ toggleCollapse: () => void;
437
+ }, "themeMode" | "layoutMode" | "primaryColor" | "borderRadius" | "transitionType" | "enableTransition" | "menuExpandMode" | "collapsed" | "fixedHeader" | "showBreadcrumb" | "showBreadcrumbIcon" | "showTagsView" | "tagsViewHeight" | "tagsViewStyle" | "showFooter" | "sidebarWidth" | "sidebarCollapsedWidth" | "headerHeight" | "enableHotkeys" | "version" | "defaults">, Pick<{
438
+ /** 当前 Store 实例实际使用的默认值。 */
439
+ defaults: Readonly<{
440
+ themeMode: ThemeMode;
441
+ primaryColor: string;
442
+ borderRadius: BorderRadiusSize;
443
+ transitionType: TransitionType;
444
+ enableTransition: boolean;
445
+ layoutMode: LayoutMode;
446
+ menuExpandMode: MenuExpandMode;
447
+ collapsed: boolean;
448
+ fixedHeader: boolean;
449
+ showBreadcrumb: boolean;
450
+ showBreadcrumbIcon: boolean;
451
+ showTagsView: boolean;
452
+ tagsViewHeight: number;
453
+ tagsViewStyle: TagsViewStyle;
454
+ showFooter: boolean;
455
+ sidebarWidth: number;
456
+ sidebarCollapsedWidth: number;
457
+ headerHeight: number;
458
+ enableHotkeys: boolean;
459
+ version: string;
460
+ }>;
461
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
462
+ primaryColor: vue.Ref<string, string>;
463
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
464
+ transitionType: vue.Ref<TransitionType, TransitionType>;
465
+ enableTransition: vue.Ref<boolean, boolean>;
466
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
467
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
468
+ collapsed: vue.Ref<boolean, boolean>;
469
+ fixedHeader: vue.Ref<boolean, boolean>;
470
+ showBreadcrumb: vue.Ref<boolean, boolean>;
471
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
472
+ showTagsView: vue.Ref<boolean, boolean>;
473
+ tagsViewHeight: vue.Ref<number, number>;
474
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
475
+ showFooter: vue.Ref<boolean, boolean>;
476
+ sidebarWidth: vue.Ref<number, number>;
477
+ sidebarCollapsedWidth: vue.Ref<number, number>;
478
+ headerHeight: vue.Ref<number, number>;
479
+ enableHotkeys: vue.Ref<boolean, boolean>;
480
+ version: vue.Ref<string, string>;
481
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
482
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
483
+ shouldEnableTransition: vue.ComputedRef<boolean>;
484
+ settingsState: vue.ComputedRef<SettingsState>;
485
+ syncCSSVariables: () => void | undefined;
486
+ applyPreset: (preset: ThemePreset) => Promise<void>;
487
+ resetSettings: () => void;
488
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
489
+ toggleCollapse: () => void;
490
+ }, "borderRadiusValue" | "transitionName" | "shouldEnableTransition" | "settingsState">, Pick<{
491
+ /** 当前 Store 实例实际使用的默认值。 */
492
+ defaults: Readonly<{
493
+ themeMode: ThemeMode;
494
+ primaryColor: string;
495
+ borderRadius: BorderRadiusSize;
496
+ transitionType: TransitionType;
497
+ enableTransition: boolean;
498
+ layoutMode: LayoutMode;
499
+ menuExpandMode: MenuExpandMode;
500
+ collapsed: boolean;
501
+ fixedHeader: boolean;
502
+ showBreadcrumb: boolean;
503
+ showBreadcrumbIcon: boolean;
504
+ showTagsView: boolean;
505
+ tagsViewHeight: number;
506
+ tagsViewStyle: TagsViewStyle;
507
+ showFooter: boolean;
508
+ sidebarWidth: number;
509
+ sidebarCollapsedWidth: number;
510
+ headerHeight: number;
511
+ enableHotkeys: boolean;
512
+ version: string;
513
+ }>;
514
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
515
+ primaryColor: vue.Ref<string, string>;
516
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
517
+ transitionType: vue.Ref<TransitionType, TransitionType>;
518
+ enableTransition: vue.Ref<boolean, boolean>;
519
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
520
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
521
+ collapsed: vue.Ref<boolean, boolean>;
522
+ fixedHeader: vue.Ref<boolean, boolean>;
523
+ showBreadcrumb: vue.Ref<boolean, boolean>;
524
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
525
+ showTagsView: vue.Ref<boolean, boolean>;
526
+ tagsViewHeight: vue.Ref<number, number>;
527
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
528
+ showFooter: vue.Ref<boolean, boolean>;
529
+ sidebarWidth: vue.Ref<number, number>;
530
+ sidebarCollapsedWidth: vue.Ref<number, number>;
531
+ headerHeight: vue.Ref<number, number>;
532
+ enableHotkeys: vue.Ref<boolean, boolean>;
533
+ version: vue.Ref<string, string>;
534
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
535
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
536
+ shouldEnableTransition: vue.ComputedRef<boolean>;
537
+ settingsState: vue.ComputedRef<SettingsState>;
538
+ syncCSSVariables: () => void | undefined;
539
+ applyPreset: (preset: ThemePreset) => Promise<void>;
540
+ resetSettings: () => void;
541
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
542
+ toggleCollapse: () => void;
543
+ }, "syncCSSVariables" | "applyPreset" | "resetSettings" | "updateThemeMode" | "toggleCollapse">>;
544
+ /**
545
+ * 默认的设置 Store(使用默认配置)
546
+ */
547
+ declare const useSettingsStore: pinia.StoreDefinition<string, Pick<{
548
+ /** 当前 Store 实例实际使用的默认值。 */
549
+ defaults: Readonly<{
550
+ themeMode: ThemeMode;
551
+ primaryColor: string;
552
+ borderRadius: BorderRadiusSize;
553
+ transitionType: TransitionType;
554
+ enableTransition: boolean;
555
+ layoutMode: LayoutMode;
556
+ menuExpandMode: MenuExpandMode;
557
+ collapsed: boolean;
558
+ fixedHeader: boolean;
559
+ showBreadcrumb: boolean;
560
+ showBreadcrumbIcon: boolean;
561
+ showTagsView: boolean;
562
+ tagsViewHeight: number;
563
+ tagsViewStyle: TagsViewStyle;
564
+ showFooter: boolean;
565
+ sidebarWidth: number;
566
+ sidebarCollapsedWidth: number;
567
+ headerHeight: number;
568
+ enableHotkeys: boolean;
569
+ version: string;
570
+ }>;
571
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
572
+ primaryColor: vue.Ref<string, string>;
573
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
574
+ transitionType: vue.Ref<TransitionType, TransitionType>;
575
+ enableTransition: vue.Ref<boolean, boolean>;
576
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
577
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
578
+ collapsed: vue.Ref<boolean, boolean>;
579
+ fixedHeader: vue.Ref<boolean, boolean>;
580
+ showBreadcrumb: vue.Ref<boolean, boolean>;
581
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
582
+ showTagsView: vue.Ref<boolean, boolean>;
583
+ tagsViewHeight: vue.Ref<number, number>;
584
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
585
+ showFooter: vue.Ref<boolean, boolean>;
586
+ sidebarWidth: vue.Ref<number, number>;
587
+ sidebarCollapsedWidth: vue.Ref<number, number>;
588
+ headerHeight: vue.Ref<number, number>;
589
+ enableHotkeys: vue.Ref<boolean, boolean>;
590
+ version: vue.Ref<string, string>;
591
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
592
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
593
+ shouldEnableTransition: vue.ComputedRef<boolean>;
594
+ settingsState: vue.ComputedRef<SettingsState>;
595
+ syncCSSVariables: () => void | undefined;
596
+ applyPreset: (preset: ThemePreset) => Promise<void>;
597
+ resetSettings: () => void;
598
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
599
+ toggleCollapse: () => void;
600
+ }, "themeMode" | "layoutMode" | "primaryColor" | "borderRadius" | "transitionType" | "enableTransition" | "menuExpandMode" | "collapsed" | "fixedHeader" | "showBreadcrumb" | "showBreadcrumbIcon" | "showTagsView" | "tagsViewHeight" | "tagsViewStyle" | "showFooter" | "sidebarWidth" | "sidebarCollapsedWidth" | "headerHeight" | "enableHotkeys" | "version" | "defaults">, Pick<{
601
+ /** 当前 Store 实例实际使用的默认值。 */
602
+ defaults: Readonly<{
603
+ themeMode: ThemeMode;
604
+ primaryColor: string;
605
+ borderRadius: BorderRadiusSize;
606
+ transitionType: TransitionType;
607
+ enableTransition: boolean;
608
+ layoutMode: LayoutMode;
609
+ menuExpandMode: MenuExpandMode;
610
+ collapsed: boolean;
611
+ fixedHeader: boolean;
612
+ showBreadcrumb: boolean;
613
+ showBreadcrumbIcon: boolean;
614
+ showTagsView: boolean;
615
+ tagsViewHeight: number;
616
+ tagsViewStyle: TagsViewStyle;
617
+ showFooter: boolean;
618
+ sidebarWidth: number;
619
+ sidebarCollapsedWidth: number;
620
+ headerHeight: number;
621
+ enableHotkeys: boolean;
622
+ version: string;
623
+ }>;
624
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
625
+ primaryColor: vue.Ref<string, string>;
626
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
627
+ transitionType: vue.Ref<TransitionType, TransitionType>;
628
+ enableTransition: vue.Ref<boolean, boolean>;
629
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
630
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
631
+ collapsed: vue.Ref<boolean, boolean>;
632
+ fixedHeader: vue.Ref<boolean, boolean>;
633
+ showBreadcrumb: vue.Ref<boolean, boolean>;
634
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
635
+ showTagsView: vue.Ref<boolean, boolean>;
636
+ tagsViewHeight: vue.Ref<number, number>;
637
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
638
+ showFooter: vue.Ref<boolean, boolean>;
639
+ sidebarWidth: vue.Ref<number, number>;
640
+ sidebarCollapsedWidth: vue.Ref<number, number>;
641
+ headerHeight: vue.Ref<number, number>;
642
+ enableHotkeys: vue.Ref<boolean, boolean>;
643
+ version: vue.Ref<string, string>;
644
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
645
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
646
+ shouldEnableTransition: vue.ComputedRef<boolean>;
647
+ settingsState: vue.ComputedRef<SettingsState>;
648
+ syncCSSVariables: () => void | undefined;
649
+ applyPreset: (preset: ThemePreset) => Promise<void>;
650
+ resetSettings: () => void;
651
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
652
+ toggleCollapse: () => void;
653
+ }, "borderRadiusValue" | "transitionName" | "shouldEnableTransition" | "settingsState">, Pick<{
654
+ /** 当前 Store 实例实际使用的默认值。 */
655
+ defaults: Readonly<{
656
+ themeMode: ThemeMode;
657
+ primaryColor: string;
658
+ borderRadius: BorderRadiusSize;
659
+ transitionType: TransitionType;
660
+ enableTransition: boolean;
661
+ layoutMode: LayoutMode;
662
+ menuExpandMode: MenuExpandMode;
663
+ collapsed: boolean;
664
+ fixedHeader: boolean;
665
+ showBreadcrumb: boolean;
666
+ showBreadcrumbIcon: boolean;
667
+ showTagsView: boolean;
668
+ tagsViewHeight: number;
669
+ tagsViewStyle: TagsViewStyle;
670
+ showFooter: boolean;
671
+ sidebarWidth: number;
672
+ sidebarCollapsedWidth: number;
673
+ headerHeight: number;
674
+ enableHotkeys: boolean;
675
+ version: string;
676
+ }>;
677
+ themeMode: vue.Ref<ThemeMode, ThemeMode>;
678
+ primaryColor: vue.Ref<string, string>;
679
+ borderRadius: vue.Ref<BorderRadiusSize, BorderRadiusSize>;
680
+ transitionType: vue.Ref<TransitionType, TransitionType>;
681
+ enableTransition: vue.Ref<boolean, boolean>;
682
+ layoutMode: vue.Ref<LayoutMode, LayoutMode>;
683
+ menuExpandMode: vue.Ref<MenuExpandMode, MenuExpandMode>;
684
+ collapsed: vue.Ref<boolean, boolean>;
685
+ fixedHeader: vue.Ref<boolean, boolean>;
686
+ showBreadcrumb: vue.Ref<boolean, boolean>;
687
+ showBreadcrumbIcon: vue.Ref<boolean, boolean>;
688
+ showTagsView: vue.Ref<boolean, boolean>;
689
+ tagsViewHeight: vue.Ref<number, number>;
690
+ tagsViewStyle: vue.Ref<TagsViewStyle, TagsViewStyle>;
691
+ showFooter: vue.Ref<boolean, boolean>;
692
+ sidebarWidth: vue.Ref<number, number>;
693
+ sidebarCollapsedWidth: vue.Ref<number, number>;
694
+ headerHeight: vue.Ref<number, number>;
695
+ enableHotkeys: vue.Ref<boolean, boolean>;
696
+ version: vue.Ref<string, string>;
697
+ borderRadiusValue: vue.ComputedRef<"4px" | "6px" | "8px">;
698
+ transitionName: vue.ComputedRef<"" | "fade-transform" | "fade-slide" | "fade-zoom">;
699
+ shouldEnableTransition: vue.ComputedRef<boolean>;
700
+ settingsState: vue.ComputedRef<SettingsState>;
701
+ syncCSSVariables: () => void | undefined;
702
+ applyPreset: (preset: ThemePreset) => Promise<void>;
703
+ resetSettings: () => void;
704
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
705
+ toggleCollapse: () => void;
706
+ }, "syncCSSVariables" | "applyPreset" | "resetSettings" | "updateThemeMode" | "toggleCollapse">>;
707
+ /** 默认设置 Store 实例类型,供组件注入或显式传入。 */
708
+ type SettingsStoreInstance = ReturnType<typeof useSettingsStore>;
709
+
710
+ /** 布局内置导航可以安全消费的标准菜单项。 */
711
+ type LayoutMenuItem<TMeta extends MenuMeta = MenuMeta> = Omit<MenuOptions<TMeta>, "children" | "key" | "meta" | "path"> & {
712
+ key: string;
713
+ path: string;
714
+ meta: TMeta & {
715
+ title: string;
716
+ };
717
+ children?: LayoutMenuItem<TMeta>[];
718
+ };
719
+ /**
720
+ * 非破坏地统一 path/key、label/meta.title 与字符串图标来源。
721
+ * 缺少稳定路径或 key 的节点不会进入导航,避免意外跳转到根路由。
722
+ */
723
+ declare function normalizeLayoutMenus<TMeta extends MenuMeta = MenuMeta>(menus: readonly MenuOptions<TMeta>[]): LayoutMenuItem<TMeta>[];
724
+
725
+ /**
726
+ * @robot-admin/layout
727
+ *
728
+ * 布局上下文 - Provide/Inject 机制
729
+ * 消费方通过 provide 注入数据,骨架组件通过 inject 读取
730
+ * 实现布局骨架与业务数据的完全解耦
731
+ */
732
+
733
+ /**
734
+ * 品牌配置
735
+ */
736
+ interface LayoutBrandConfig {
737
+ /** 品牌名称,如 'Robot Admin' */
738
+ name?: string;
739
+ /** 品牌副标题,如 '机器人管理系统' */
740
+ subtitle?: string;
741
+ /** Logo 地址(图片或视频) */
742
+ logoSrc?: string;
743
+ /** Logo 类型 */
744
+ logoType?: "video" | "image";
745
+ /** Logo 尺寸 */
746
+ logoSize?: number;
747
+ /** 首页路径,默认 '/home' */
748
+ homePath?: string;
749
+ /** 页脚版权文本 */
750
+ copyright?: string;
751
+ }
752
+ /**
753
+ * 布局上下文数据接口
754
+ * 骨架组件通过 inject 获取这些数据
755
+ */
756
+ interface LayoutContext {
757
+ /** 菜单列表数据 */
758
+ menus: ComputedRef<MenuOptions[]>;
759
+ /** 是否为暗色模式 */
760
+ isDark: ComputedRef<boolean>;
761
+ /** 当前布局模式 */
762
+ layoutMode: ComputedRef<LayoutMode>;
763
+ /** 侧边栏折叠状态;提供后由容器和宿主头部共享同一数据源 */
764
+ collapsed?: WritableComputedRef<boolean>;
765
+ /** 菜单展开方式 */
766
+ menuExpandMode?: ComputedRef<MenuExpandMode>;
767
+ /** 侧边栏宽度 (px) */
768
+ sidebarWidth: ComputedRef<number>;
769
+ /** 侧边栏折叠后宽度 (px) */
770
+ sidebarCollapsedWidth: ComputedRef<number>;
771
+ /** 是否显示页脚 */
772
+ showFooter: ComputedRef<boolean>;
773
+ /** 是否显示标签页 */
774
+ showTagsView: ComputedRef<boolean>;
775
+ /** 标签页高度 (px) */
776
+ tagsViewHeight: ComputedRef<number>;
777
+ /** 头部高度 (px) */
778
+ headerHeight: ComputedRef<number>;
779
+ /** 过渡动画名称 */
780
+ transitionName: ComputedRef<string>;
781
+ /** 是否显示面包屑 */
782
+ showBreadcrumb: ComputedRef<boolean>;
783
+ /** 是否显示面包屑图标 */
784
+ showBreadcrumbIcon: ComputedRef<boolean>;
785
+ /** 是否固定头部 */
786
+ fixedHeader: ComputedRef<boolean>;
787
+ /** 品牌信息 */
788
+ brand: LayoutBrandConfig;
789
+ /**
790
+ * 自定义图标组件(如 @iconify/vue 的 Icon 组件)
791
+ * 用于布局内部组件渲染菜单图标
792
+ * 组件需接受 name (string) 和 size (number) 两个 props
793
+ * 如果不提供,将使用 CSS class 渲染图标
794
+ */
795
+ iconComponent?: Component;
796
+ }
797
+ /**
798
+ * 布局上下文 Injection Key
799
+ */
800
+ declare const LAYOUT_CONTEXT_KEY: InjectionKey<LayoutContext>;
801
+ /** 当前布局使用的设置 Store,支持自定义 Store id 与多实例。 */
802
+ declare const LAYOUT_SETTINGS_KEY: InjectionKey<SettingsStoreInstance>;
803
+ /** 侧边栏折叠控制器。 */
804
+ interface MenuCollapseHandlers {
805
+ isCollapsed: ComputedRef<boolean>;
806
+ handleCollapsedChange: (collapsed: boolean) => void;
807
+ }
808
+ declare const MENU_COLLAPSE_KEY: InjectionKey<MenuCollapseHandlers>;
809
+ /**
810
+ * 抽屉菜单统一控制器
811
+ * 解决 MenuTrigger 与 DrawerMenu 各自拥有独立定时器导致的
812
+ * "鼠标从 trigger 移到 drawer 过程中 drawer 被关闭"问题
813
+ */
814
+ interface DrawerHandlers {
815
+ /** 显示抽屉并取消所有待执行的隐藏操作 */
816
+ show: () => void;
817
+ /** 启动延迟隐藏(300ms) */
818
+ startHide: () => void;
819
+ /** 立即隐藏抽屉(如导航跳转后) */
820
+ hide: () => void;
821
+ }
822
+ /**
823
+ * 抽屉菜单控制器 Injection Key
824
+ */
825
+ declare const DRAWER_HANDLER_KEY: InjectionKey<DrawerHandlers>;
826
+ /**
827
+ * 在骨架组件中获取布局上下文
828
+ *
829
+ * @throws 如果没有 provide LayoutContext 会抛出错误
830
+ *
831
+ * @example
832
+ * ```vue
833
+ * <script setup>
834
+ * // 在包的骨架组件内部使用
835
+ * const ctx = useLayoutContext()
836
+ * const menus = ctx.menus
837
+ * const isDark = ctx.isDark
838
+ * </script>
839
+ * ```
840
+ */
841
+ declare function useLayoutContext(): LayoutContext;
842
+ /** 在组件 setup 中提供布局上下文,避免消费方直接操作 InjectionKey。 */
843
+ declare function provideLayoutContext(context: LayoutContext): LayoutContext;
844
+ /**
845
+ * 默认品牌配置
846
+ */
847
+ declare const DEFAULT_BRAND_CONFIG: Required<LayoutBrandConfig>;
848
+
849
+ /**
850
+ * 布局桥接所需的最小设置协议。
851
+ *
852
+ * 默认 Pinia Store 天然满足该协议,宿主也可以传入其他响应式 Store,
853
+ * 避免上下文助手与具体状态管理实现强绑定。
854
+ */
855
+ interface LayoutSettingsSource {
856
+ layoutMode: LayoutMode;
857
+ collapsed: boolean;
858
+ menuExpandMode: MenuExpandMode;
859
+ sidebarWidth: number;
860
+ sidebarCollapsedWidth: number;
861
+ showFooter: boolean;
862
+ showTagsView: boolean;
863
+ tagsViewHeight: number;
864
+ headerHeight: number;
865
+ transitionName: string;
866
+ showBreadcrumb: boolean;
867
+ showBreadcrumbIcon: boolean;
868
+ fixedHeader: boolean;
869
+ }
870
+ /** 创建标准布局上下文所需的最小宿主适配。 */
871
+ interface CreateLayoutContextOptions {
872
+ /** 布局设置 Store,可使用默认 Store 或 createSettingsStore() 创建的实例。 */
873
+ settings: LayoutSettingsSource;
874
+ /** 宿主菜单;支持普通值、Ref、ComputedRef 或 getter。 */
875
+ menus: MaybeRefOrGetter<MenuOptions[]>;
876
+ /** 宿主暗色状态;支持普通值、Ref、ComputedRef 或 getter。 */
877
+ isDark: MaybeRefOrGetter<boolean>;
878
+ /** 品牌信息,未提供的字段使用包内安全默认值。 */
879
+ brand?: LayoutBrandConfig;
880
+ /** 布局内部用于渲染菜单图标的宿主组件。 */
881
+ iconComponent?: Component;
882
+ }
883
+ /**
884
+ * 将最小宿主输入转换为完整 LayoutContext。
885
+ *
886
+ * 保留底层 LayoutContext 作为高级扩展协议,同时避免普通接入重复桥接 Store 字段。
887
+ */
888
+ declare function createLayoutContext(options: CreateLayoutContextOptions): LayoutContext;
889
+ /** 在组件 setup 中创建并提供标准布局上下文。 */
890
+ declare function provideLayout(options: CreateLayoutContextOptions): LayoutContext;
891
+
892
+ /** KeepAlive 缓存选项 */
893
+ interface LayoutCacheOptions {
894
+ /** 最大缓存数量,默认 20 */
895
+ maxCacheCount?: number;
896
+ /** 是否输出缓存调试日志,默认 false */
897
+ enableDevLog?: boolean;
898
+ /** 是否暴露调试方法到 window,默认 false */
899
+ exposeToWindow?: boolean;
900
+ }
901
+ /** KeepAlive 仅接受具名组件,并由路由 meta.keepAlive 明确开启。 */
902
+ declare function shouldCacheRoute(route: Pick<RouteLocationNormalizedLoaded, "name" | "meta">): route is Pick<RouteLocationNormalizedLoaded, "name" | "meta"> & {
903
+ name: string;
904
+ };
905
+ /**
906
+ * 统一的 KeepAlive 缓存管理 composable
907
+ *
908
+ * @param options - 缓存配置选项
909
+ * @returns 缓存视图列表和管理方法
910
+ *
911
+ * @example
912
+ * ```vue
913
+ * <script setup>
914
+ * import { useLayoutCache } from '@robot-admin/layout/vue'
915
+ *
916
+ * const { cachedViews, maxCacheCount } = useLayoutCache({ maxCacheCount: 30 })
917
+ * </script>
918
+ *
919
+ * <template>
920
+ * <KeepAlive :include="cachedViews" :max="maxCacheCount">
921
+ * <RouterView />
922
+ * </KeepAlive>
923
+ * </template>
924
+ * ```
925
+ */
926
+ declare function useLayoutCache(options?: LayoutCacheOptions): {
927
+ cachedViews: vue.Ref<string[], string[]>;
928
+ maxCacheCount: vue.Ref<number, number>;
929
+ addCache: (name: string) => void;
930
+ removeCache: (name: string) => void;
931
+ clearAllCache: () => void;
932
+ };
933
+
934
+ /**
935
+ * @robot-admin/layout
936
+ *
937
+ * 菜单拆分 Composable - 通用的一级/二级菜单拆分逻辑
938
+ * 用于 Mix / MixTop / ReverseHorizontalMix 等需要菜单拆分的布局
939
+ */
940
+
941
+ interface UseMenuSplitOptions {
942
+ /** 菜单数据(响应式) */
943
+ menus: MaybeRefOrGetter<readonly MenuOptions[]>;
944
+ /** 是否使用悬浮式二级菜单(C_MixLayout 的 toggle 模式) */
945
+ floatingSecondMenu?: MaybeRefOrGetter<boolean>;
946
+ }
947
+ interface UseMenuSplitReturn {
948
+ /** 当前激活的一级菜单路径 */
949
+ activeFirstMenu: Ref<string>;
950
+ /** 当前激活的一级菜单完整数据 */
951
+ activeFirstMenuItem: ComputedRef<MenuOptions | undefined>;
952
+ /** 当前一级菜单对应的二级菜单列表 */
953
+ currentSecondMenus: ComputedRef<MenuOptions[]>;
954
+ /** 悬浮的菜单项(C_MixLayout 使用) */
955
+ hoveredMenuItem: Ref<MenuOptions | null>;
956
+ /** 实际展示的菜单项(hovered 优先,fallback 到 active) */
957
+ displayMenuItem: ComputedRef<MenuOptions | null>;
958
+ /** 是否显示悬浮二级菜单(C_MixLayout 使用) */
959
+ showSecondMenu: Ref<boolean>;
960
+ /** 处理一级菜单点击 */
961
+ handleFirstMenuClick: (item: MenuOptions) => void;
962
+ /** 处理二级菜单点击 */
963
+ handleSecondMenuClick: (item: MenuOptions) => void;
964
+ /** 检查路由是否匹配菜单项 */
965
+ isMenuItemActive: (menuPath: string | undefined) => boolean;
966
+ }
967
+ /** 判断当前路径是否等于菜单路径或位于其子路径下。 */
968
+ declare function isPathSegmentPrefix(menuPath: string | undefined, currentPath: string): boolean;
969
+ /**
970
+ * 菜单拆分 Composable
971
+ *
972
+ * 提供一级/二级菜单拆分状态管理、路由自动匹配、菜单点击处理。
973
+ * 供 C_MixLayout / C_MixTopLayout / C_ReverseHorizontalMixLayout 共用。
974
+ *
975
+ * @example
976
+ * ```ts
977
+ * const menuSplit = useMenuSplit({
978
+ * menus: ctx.menus,
979
+ * floatingSecondMenu: computed(() => layoutMode.value === 'mix'),
980
+ * })
981
+ * ```
982
+ */
983
+ declare function useMenuSplit(options: UseMenuSplitOptions): UseMenuSplitReturn;
984
+
985
+ /** 响应式菜单测量选项。 */
986
+ interface UseResponsiveMenuOptions {
987
+ data: MaybeRefOrGetter<readonly MenuOptions[]>;
988
+ /** 溢出菜单按钮和安全间距占用的宽度,默认 95px。 */
989
+ overflowReserve?: number;
990
+ /** ResizeObserver 触发后的防抖时间,默认 100ms。 */
991
+ debounceMs?: number;
992
+ }
993
+ /** 按现有菜单字号与间距估算单项宽度。 */
994
+ declare function estimateMenuItemWidth(item: MenuOptions): number;
995
+ /** 计算指定容器内可直接展示的菜单项数量。 */
996
+ declare function calculateVisibleMenuCount(items: readonly MenuOptions[], containerWidth: number, overflowReserve?: number): number;
997
+ /**
998
+ * UI 框架无关的响应式菜单测量逻辑。
999
+ * Naive UI 和 Element Plus 适配器只负责把 visible/hidden 数据渲染出来。
1000
+ */
1001
+ declare function useResponsiveMenu(options: UseResponsiveMenuOptions): {
1002
+ containerRef: vue.Ref<HTMLElement | undefined, HTMLElement | undefined>;
1003
+ hiddenItems: vue.Ref<{
1004
+ type?: MenuItemType | undefined;
1005
+ key?: string | undefined;
1006
+ path?: string | undefined;
1007
+ name?: string | undefined;
1008
+ component?: string | undefined;
1009
+ redirect?: string | undefined;
1010
+ label?: string | undefined;
1011
+ icon?: (string | (() => vue.VNode) | vue.VNode) | undefined;
1012
+ disabled?: boolean | undefined;
1013
+ meta?: {
1014
+ [x: string]: unknown;
1015
+ title?: string | undefined;
1016
+ icon?: string | undefined;
1017
+ hidden?: boolean | undefined;
1018
+ affix?: boolean | undefined;
1019
+ keepAlive?: boolean | undefined;
1020
+ full?: boolean | undefined;
1021
+ link?: string | undefined;
1022
+ } | undefined;
1023
+ children?: /*elided*/ any[] | undefined;
1024
+ }[], MenuOptions<MenuMeta>[] | {
1025
+ type?: MenuItemType | undefined;
1026
+ key?: string | undefined;
1027
+ path?: string | undefined;
1028
+ name?: string | undefined;
1029
+ component?: string | undefined;
1030
+ redirect?: string | undefined;
1031
+ label?: string | undefined;
1032
+ icon?: (string | (() => vue.VNode) | vue.VNode) | undefined;
1033
+ disabled?: boolean | undefined;
1034
+ meta?: {
1035
+ [x: string]: unknown;
1036
+ title?: string | undefined;
1037
+ icon?: string | undefined;
1038
+ hidden?: boolean | undefined;
1039
+ affix?: boolean | undefined;
1040
+ keepAlive?: boolean | undefined;
1041
+ full?: boolean | undefined;
1042
+ link?: string | undefined;
1043
+ } | undefined;
1044
+ children?: /*elided*/ any[] | undefined;
1045
+ }[]>;
1046
+ refresh: () => void;
1047
+ visibleItems: vue.Ref<{
1048
+ type?: MenuItemType | undefined;
1049
+ key?: string | undefined;
1050
+ path?: string | undefined;
1051
+ name?: string | undefined;
1052
+ component?: string | undefined;
1053
+ redirect?: string | undefined;
1054
+ label?: string | undefined;
1055
+ icon?: (string | (() => vue.VNode) | vue.VNode) | undefined;
1056
+ disabled?: boolean | undefined;
1057
+ meta?: {
1058
+ [x: string]: unknown;
1059
+ title?: string | undefined;
1060
+ icon?: string | undefined;
1061
+ hidden?: boolean | undefined;
1062
+ affix?: boolean | undefined;
1063
+ keepAlive?: boolean | undefined;
1064
+ full?: boolean | undefined;
1065
+ link?: string | undefined;
1066
+ } | undefined;
1067
+ children?: /*elided*/ any[] | undefined;
1068
+ }[], MenuOptions<MenuMeta>[] | {
1069
+ type?: MenuItemType | undefined;
1070
+ key?: string | undefined;
1071
+ path?: string | undefined;
1072
+ name?: string | undefined;
1073
+ component?: string | undefined;
1074
+ redirect?: string | undefined;
1075
+ label?: string | undefined;
1076
+ icon?: (string | (() => vue.VNode) | vue.VNode) | undefined;
1077
+ disabled?: boolean | undefined;
1078
+ meta?: {
1079
+ [x: string]: unknown;
1080
+ title?: string | undefined;
1081
+ icon?: string | undefined;
1082
+ hidden?: boolean | undefined;
1083
+ affix?: boolean | undefined;
1084
+ keepAlive?: boolean | undefined;
1085
+ full?: boolean | undefined;
1086
+ link?: string | undefined;
1087
+ } | undefined;
1088
+ children?: /*elided*/ any[] | undefined;
1089
+ }[]>;
1090
+ };
1091
+
1092
+ /** 返回宿主图标组件;未配置时使用零依赖 CSS class 渲染器。 */
1093
+ declare function useLayoutIcon(): Component;
1094
+
1095
+ /** 宿主没有实现可选高副作用操作。 */
1096
+ declare class LayoutActionUnavailableError extends Error {
1097
+ constructor(action: keyof SettingsDrawerActions);
1098
+ }
1099
+ interface SettingsControllerOptions {
1100
+ store: SettingsStoreInstance;
1101
+ actions?: SettingsDrawerActions;
1102
+ maxImportBytes?: number;
1103
+ /** 灰度/色弱 class 的作用元素;默认 document.documentElement。 */
1104
+ visualRoot?: HTMLElement | null;
1105
+ /** 水印挂载容器;默认 document.body。 */
1106
+ watermarkContainer?: HTMLElement | null;
1107
+ watermarkText?: string;
1108
+ onVisualEffectError?: (message: string) => void;
1109
+ }
1110
+ interface ExportedLayoutSettings extends LayoutSettingsConfig {
1111
+ schemaVersion: typeof SETTINGS_CONFIG_SCHEMA_VERSION;
1112
+ exportTime: string;
1113
+ }
1114
+ /**
1115
+ * SettingsDrawer 的 UI 框架无关控制器。
1116
+ *
1117
+ * 负责配置事务、导入导出和页面级视觉效果;Naive UI / Element Plus
1118
+ * 适配器只处理组件渲染、消息和确认框。
1119
+ * 在 Vue setup/effect scope 内会自动清理;在作用域外创建时应调用返回值的 dispose()。
1120
+ */
1121
+ declare function useSettingsController(options: SettingsControllerOptions): {
1122
+ applyImportedConfig: (input: unknown) => Promise<LayoutSettingsConfig>;
1123
+ applyPreset: (preset: ThemePreset) => Promise<void>;
1124
+ clearCache: () => Promise<void>;
1125
+ colorWeakMode: vue.Ref<boolean, boolean>;
1126
+ createExportConfig: () => ExportedLayoutSettings;
1127
+ dispose: () => void;
1128
+ downloadConfig: () => boolean;
1129
+ grayMode: vue.Ref<boolean, boolean>;
1130
+ importFile: (file: Pick<File, "size" | "text">) => Promise<LayoutSettingsConfig>;
1131
+ reloadPage: () => void;
1132
+ resetAll: () => Promise<void>;
1133
+ resetAppearance: () => Promise<void>;
1134
+ resetLayout: () => void;
1135
+ systemInfo: vue.ComputedRef<{
1136
+ browser: string;
1137
+ os: string;
1138
+ resolution: string;
1139
+ pixelRatio: string;
1140
+ language: string;
1141
+ timezone: string;
1142
+ }>;
1143
+ updateThemeMode: (mode: ThemeMode) => Promise<void>;
1144
+ watermarkEnabled: vue.Ref<boolean, boolean>;
1145
+ watermarkText: vue.Ref<string, string>;
1146
+ };
1147
+
1148
+ interface LayoutCssVariableSource {
1149
+ primaryColor: MaybeRefOrGetter<string>;
1150
+ borderRadiusValue: MaybeRefOrGetter<string>;
1151
+ sidebarWidth: MaybeRefOrGetter<number>;
1152
+ sidebarCollapsedWidth: MaybeRefOrGetter<number>;
1153
+ headerHeight: MaybeRefOrGetter<number>;
1154
+ tagsViewHeight: MaybeRefOrGetter<number>;
1155
+ }
1156
+ interface LayoutStyleDeclaration {
1157
+ getPropertyValue(name: string): string;
1158
+ removeProperty(name: string): string;
1159
+ setProperty(name: string, value: string): void;
1160
+ }
1161
+ interface LayoutCssVariableTarget {
1162
+ style: LayoutStyleDeclaration;
1163
+ }
1164
+ interface LayoutCssVariableOptions {
1165
+ /** 变量写入目标;默认 document.documentElement。 */
1166
+ target?: MaybeRefOrGetter<LayoutCssVariableTarget | null | undefined>;
1167
+ /** 同时维护 3.x 无前缀变量,默认 false。 */
1168
+ legacyAliases?: boolean;
1169
+ }
1170
+ interface LayoutCssVariableBinding {
1171
+ dispose: () => void;
1172
+ sync: () => void;
1173
+ }
1174
+ /**
1175
+ * 将响应式设置绑定到指定样式作用域,并在 dispose 时精确恢复原值。
1176
+ */
1177
+ declare function bindLayoutCssVariables(source: LayoutCssVariableSource, options?: LayoutCssVariableOptions): LayoutCssVariableBinding;
1178
+ /** setup 作用域内使用的自动清理版本。 */
1179
+ declare function useLayoutCssVariables(source: LayoutCssVariableSource, options?: LayoutCssVariableOptions): LayoutCssVariableBinding;
1180
+
1181
+ export { BORDER_RADIUS_MAP, type BorderRadiusSize, COLOR_SWATCHES, type CreateLayoutContextOptions, DEFAULT_BRAND_CONFIG, DEFAULT_SETTINGS, DRAWER_HANDLER_KEY, type DrawerHandlers, type ExportedLayoutSettings, LAYOUT_CONTEXT_KEY, LAYOUT_MODES, LAYOUT_SETTINGS_KEY, LayoutActionUnavailableError, type LayoutBrandConfig, type LayoutCacheOptions, type LayoutContext, type LayoutCssVariableBinding, type LayoutCssVariableOptions, type LayoutCssVariableSource, type LayoutCssVariableTarget, type LayoutInfo, type LayoutMenuItem, type LayoutMode, type LayoutSettingsConfig, type LayoutSettingsSource, type LayoutStyleDeclaration, type LayoutVisualEffects, MENU_COLLAPSE_KEY, type MenuCollapseHandlers, type MenuExpandMode, type MenuItemType, type MenuMeta, type MenuOptions, type MenuTag, PRESET_COLORS, type PresetColor, SETTINGS_CONFIG_SCHEMA_VERSION, type SettingsControllerOptions, type SettingsDrawerActions, type SettingsState, type SettingsStoreInstance, type SettingsStoreOptions, THEME_PRESETS, TRANSITION_MAP, type TagsViewStyle, type ThemeMode, type ThemePreset, type TransitionType, type UseMenuSplitOptions, type UseMenuSplitReturn, type UseResponsiveMenuOptions, adjustColor, assertLayoutSettingsRelationships, bindLayoutCssVariables, calculateVisibleMenuCount, createLayoutContext, createSettingsStore, estimateMenuItemWidth, isPathSegmentPrefix, normalizeLayoutMenus, provideLayout, provideLayoutContext, sanitizeLayoutSettingsConfig, sanitizeSettingsPatch, setupLayout, shouldCacheRoute, useLayoutCache, useLayoutContext, useLayoutCssVariables, useLayoutIcon, useMenuSplit, useResponsiveMenu, useSettingsController, useSettingsStore };