@robot-admin/layout 3.2.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,761 +1,768 @@
1
- /**
2
- * @robot-admin/layout
3
- *
4
- * 布局全局样式 - 所有布局共享的基础样式
5
- * 包含:布局容器、内容区、主题适配、侧边栏基础、菜单毛玻璃效果、折叠菜单、过渡动画
6
- *
7
- * CSS 变量依赖(消费方需定义):
8
- * --app-bg-body : 页面背景色
9
- * --app-bg-content : 内容区背景色
10
- * --app-bg-surface : 卡片/面板背景色
11
- * --app-menu-bg : 菜单背景色
12
- * --app-text-primary : 主文字色
13
- * --app-text-secondary: 次文字色
14
- * --app-border-color : 边框色
15
- * --primary-color : 主题色
16
- */
17
-
18
- // ==================== 🎨 菜单样式自定义变量(可在主项目覆盖)====================
19
- .layout-container {
20
- // 菜单项圆角
21
- --menu-border-radius: 10px;
22
-
23
- // 一级菜单父项激活效果(子菜单展开状态)- 较淡
24
- --menu-parent-gradient-start: rgba(99, 102, 241, 0.1);
25
- --menu-parent-gradient-end: rgba(139, 92, 246, 0.06);
26
- --menu-parent-border-color: rgba(99, 102, 241, 0.45);
27
- --menu-parent-border-width: 2px;
28
- --menu-parent-shadow:
29
- 0 2px 6px rgba(99, 102, 241, 0.1), 0 4px 12px rgba(99, 102, 241, 0.06);
30
-
31
- // 二级菜单选中效果(当前页面)- 明显更亮,层级清晰
32
- --menu-selected-gradient-start: rgba(99, 102, 241, 0.22);
33
- --menu-selected-gradient-end: rgba(139, 92, 246, 0.16);
34
- --menu-selected-border-color: rgba(99, 102, 241, 0.75);
35
- --menu-selected-border-width: 3px;
36
- --menu-selected-shadow:
37
- 0 2px 12px rgba(99, 102, 241, 0.22), 0 4px 24px rgba(99, 102, 241, 0.15);
38
-
39
- // 毛玻璃效果强度
40
- --menu-parent-blur: 14px;
41
- --menu-parent-saturate: 130%;
42
- --menu-selected-blur: 16px;
43
- --menu-selected-saturate: 135%;
44
- }
45
-
46
- // ==================== 基础布局样式 ====================
47
- .layout-container {
48
- height: 100vh;
49
- overflow: hidden;
50
- display: flex;
51
- flex-direction: column;
52
- }
53
-
54
- // ==================== 内容区域样式 ====================
55
- .layout-container .content-with-header {
56
- margin-top: 100px;
57
- height: calc(100vh - 150px);
58
- }
59
-
60
- .layout-container .app-content {
61
- background-color: var(--app-bg-content);
62
- }
63
-
64
- .layout-container .n-layout-content {
65
- display: flex;
66
- flex-direction: column;
67
- flex: 1;
68
- overflow-x: hidden;
69
- }
70
-
71
- :where(.layout-container) .main-content {
72
- flex: 1;
73
- padding: 16px;
74
- overflow-y: auto;
75
- overflow-x: hidden;
76
- width: 100%;
77
- border-radius: 8px;
78
- }
79
-
80
- // ==================== 主题相关样式 ====================
81
- [data-theme="light"] .layout-container .light-theme,
82
- .layout-container .light-theme {
83
- background-color: var(--app-bg-body);
84
- }
85
-
86
- [data-theme="dark"] .layout-container .dark-theme,
87
- .layout-container .dark-theme {
88
- background-color: var(--app-bg-body);
89
- }
90
-
91
- .layout-container.light-mode .n-layout .n-layout-scroll-container {
92
- background-color: var(--app-bg-surface);
93
- border-radius: 10px;
94
- }
95
-
96
- .layout-container.dark-mode .n-layout .n-layout-scroll-container,
97
- .layout-container.system-mode .n-layout .n-layout-scroll-container {
98
- background-color: var(--app-bg-surface);
99
- }
100
-
101
- // ==================== 侧边栏和菜单样式 ====================
102
- :where(.layout-container) .layout-sider {
103
- transform: translateZ(0);
104
- backface-visibility: hidden;
105
- contain: layout style paint;
106
- }
107
-
108
- :where(.layout-container) .layout-sider.light-theme {
109
- background-color: var(--app-menu-bg);
110
- }
111
-
112
- :where(.layout-container) .layout-sider.dark-theme {
113
- background-color: var(--app-menu-bg);
114
- }
115
-
116
- :where(.layout-container) .layout-sider .n-scrollbar-rail {
117
- width: 0 !important;
118
- }
119
-
120
- :where(.layout-container) .layout-sider .n-scrollbar-content {
121
- padding-right: 0 !important;
122
- }
123
-
124
- .layout-container .n-layout-sider {
125
- flex-shrink: 0;
126
- height: 100%;
127
- display: flex;
128
- flex-direction: column;
129
- overflow: hidden;
130
- }
131
-
132
- :where(.layout-container) .menu-scroll-container {
133
- height: calc(100vh - 56px);
134
- overflow-y: scroll;
135
- scrollbar-width: none;
136
- -ms-overflow-style: none;
137
- flex: 1;
138
- display: flex;
139
- flex-direction: column;
140
- }
141
-
142
- :where(.layout-container) .menu-scroll-container::-webkit-scrollbar {
143
- display: none;
144
- }
145
-
146
- .layout-container .n-menu.n-menu--vertical {
147
- flex: 1;
148
- min-height: min-content;
149
- overflow-y: auto;
150
- overflow-x: hidden;
151
- }
152
-
153
- // ==================== 菜单图标和文字样式 ====================
154
- .layout-container .n-menu-item-content__icon {
155
- display: flex;
156
- align-items: center;
157
- justify-content: center;
158
- vertical-align: middle;
159
- width: 18px !important;
160
- height: 18px !important;
161
- margin-right: 10px;
162
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
163
- font-size: 18px !important;
164
- }
165
-
166
- .layout-container .n-menu-item-content__icon .n-icon {
167
- width: 18px !important;
168
- height: 18px !important;
169
- font-size: 18px !important;
170
- }
171
-
172
- .layout-container .n-menu-item-content__icon svg {
173
- width: 18px !important;
174
- height: 18px !important;
175
- }
176
-
177
- .layout-container .n-menu-item-content-header {
178
- font-size: 14px;
179
- font-weight: 500;
180
- letter-spacing: 0.2px;
181
- line-height: 1.5;
182
- }
183
-
184
- .layout-container .n-menu-item-content__arrow {
185
- font-size: 14px;
186
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
187
- }
188
-
189
- // ==================== 🎨 毛玻璃质感菜单效果(性能优化版)====================
190
-
191
- /* 菜单项基础样式 - 卡片质感默认状态 */
192
- .layout-container .n-menu-item {
193
- border-radius: 0;
194
- position: relative;
195
- overflow: visible;
196
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
197
- will-change: transform;
198
-
199
- &::before {
200
- content: "";
201
- position: absolute;
202
- top: 0;
203
- left: 0;
204
- right: 0;
205
- bottom: 0;
206
- background: linear-gradient(
207
- 135deg,
208
- rgba(255, 255, 255, 0.08) 0%,
209
- rgba(255, 255, 255, 0.04) 100%
210
- );
211
- border-radius: 10px;
212
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
213
- z-index: -1;
214
- box-shadow:
215
- 0 2px 4px rgba(0, 0, 0, 0.08),
216
- inset 0 1px 0 rgba(255, 255, 255, 0.1);
217
- }
218
- }
219
-
220
- /* 菜单项内容容器 - 卡片质感 */
221
- .layout-container .n-menu-item-content {
222
- border-radius: var(--menu-border-radius, 10px);
223
- background: transparent;
224
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
225
- position: relative;
226
- padding: 10px 16px;
227
-
228
- &::after {
229
- content: "";
230
- position: absolute;
231
- top: 0;
232
- left: 0;
233
- right: 0;
234
- bottom: 0;
235
- border: 1px solid rgba(255, 255, 255, 0.06);
236
- border-left: 2px solid transparent;
237
- border-radius: var(--menu-border-radius, 10px);
238
- pointer-events: none;
239
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
240
- }
241
- }
242
-
243
- /* Hover 状态 - 增强卡片效果 */
244
- .layout-container .n-menu-item:hover {
245
- transform: translateX(2px);
246
-
247
- &::before {
248
- background: rgba(255, 255, 255, 0.1);
249
- border-radius: 10px;
250
- box-shadow:
251
- 0 4px 16px rgba(0, 0, 0, 0.12),
252
- inset 0 1px 0 rgba(255, 255, 255, 0.12);
253
- }
254
- }
255
-
256
- .layout-container .n-menu-item:hover .n-menu-item-content {
257
- backdrop-filter: blur(12px);
258
- -webkit-backdrop-filter: blur(12px);
259
- border-radius: 10px;
260
-
261
- &::after {
262
- border-left-color: rgba(99, 102, 241, 0.4);
263
- border-radius: 10px;
264
- box-shadow:
265
- 0 0 15px rgba(99, 102, 241, 0.15),
266
- inset 0 1px 1px rgba(255, 255, 255, 0.1);
267
- }
268
- }
269
-
270
- .layout-container .n-menu-item:hover .n-menu-item-content__icon {
271
- transform: scale(1.08);
272
- color: var(--n-item-icon-color-active-hover);
273
- }
274
-
275
- /* Active/选中状态 - 二级菜单选中(当前页面)- 略强玻璃质感 */
276
- .layout-container .n-menu-item--selected,
277
- .layout-container .n-menu-item-content--selected {
278
- background: transparent !important; /* 覆盖 Naive UI 默认背景 */
279
-
280
- &::before {
281
- background: linear-gradient(
282
- 135deg,
283
- var(--menu-selected-gradient-start, rgba(99, 102, 241, 0.22)) 0%,
284
- var(--menu-selected-gradient-end, rgba(139, 92, 246, 0.16)) 100%
285
- ) !important;
286
- border-radius: var(--menu-border-radius, 10px);
287
- box-shadow:
288
- var(
289
- --menu-selected-shadow,
290
- 0 2px 12px rgba(99, 102, 241, 0.22),
291
- 0 4px 24px rgba(99, 102, 241, 0.15)
292
- ),
293
- inset 0 1px 2px rgba(99, 102, 241, 0.12),
294
- inset 0 0 0 1px rgba(255, 255, 255, 0.08);
295
- }
296
- }
297
-
298
- .layout-container .n-menu-item--selected .n-menu-item-content,
299
- .layout-container .n-menu-item-content--selected {
300
- background: transparent !important; /* 覆盖 Naive UI 默认背景 */
301
- backdrop-filter: blur(var(--menu-selected-blur, 16px))
302
- saturate(var(--menu-selected-saturate, 135%));
303
- -webkit-backdrop-filter: blur(var(--menu-selected-blur, 16px))
304
- saturate(var(--menu-selected-saturate, 135%));
305
-
306
- &::after {
307
- border-left: var(--menu-selected-border-width, 3px) solid
308
- var(--menu-selected-border-color, rgba(99, 102, 241, 0.75));
309
- border-radius: var(--menu-border-radius, 10px);
310
- box-shadow:
311
- 0 0 14px rgba(99, 102, 241, 0.25),
312
- inset 0 1px 2px rgba(255, 255, 255, 0.12);
313
- }
314
- }
315
-
316
- .layout-container .n-menu-item--selected .n-menu-item-content__icon {
317
- filter: drop-shadow(0 0 6px rgba(99, 102, 241, 0.5));
318
- transform: scale(1.05);
319
- color: var(--n-item-icon-color-active);
320
- }
321
-
322
- /* Active 按下状态 */
323
- .layout-container .n-menu-item:active {
324
- transform: scale(0.98);
325
- }
326
-
327
- /* 子菜单父项样式 - 一级菜单展开状态(较淡玻璃质感)*/
328
- .layout-container .n-menu-item-content--child-active {
329
- background: transparent !important; /* 覆盖 Naive UI 默认背景 */
330
-
331
- &::before {
332
- background: linear-gradient(
333
- 135deg,
334
- var(--menu-parent-gradient-start, rgba(99, 102, 241, 0.1)) 0%,
335
- var(--menu-parent-gradient-end, rgba(139, 92, 246, 0.06)) 100%
336
- ) !important;
337
- border-radius: var(--menu-border-radius, 10px);
338
- box-shadow:
339
- var(
340
- --menu-parent-shadow,
341
- 0 2px 6px rgba(99, 102, 241, 0.1),
342
- 0 4px 12px rgba(99, 102, 241, 0.06)
343
- ),
344
- inset 0 1px 2px rgba(99, 102, 241, 0.06),
345
- inset 0 0 0 1px rgba(255, 255, 255, 0.05);
346
- }
347
-
348
- &::after {
349
- border-left: var(--menu-parent-border-width, 2px) solid
350
- var(--menu-parent-border-color, rgba(99, 102, 241, 0.45));
351
- border-radius: var(--menu-border-radius, 10px);
352
- }
353
-
354
- backdrop-filter: blur(var(--menu-parent-blur, 14px))
355
- saturate(var(--menu-parent-saturate, 130%));
356
- -webkit-backdrop-filter: blur(var(--menu-parent-blur, 14px))
357
- saturate(var(--menu-parent-saturate, 130%));
358
- }
359
-
360
- /* 一级菜单项对齐 */
361
- .layout-container .n-menu > .n-menu-item,
362
- .layout-container .n-menu > .n-submenu {
363
- margin: 4px 12px;
364
- }
365
-
366
- /* 二级及以下菜单项 */
367
- .layout-container .n-submenu-children .n-menu-item {
368
- margin: 4px 0;
369
- }
370
-
371
- .layout-container .n-submenu-children .n-menu-item-content {
372
- padding-left: 46px;
373
- }
374
-
375
- // ==================== 主题优化 ====================
376
- :where(.layout-container) .dark-theme {
377
- color-scheme: dark;
378
-
379
- .layout-container .n-menu-item::before {
380
- background: linear-gradient(
381
- 135deg,
382
- rgba(255, 255, 255, 0.08) 0%,
383
- rgba(255, 255, 255, 0.04) 100%
384
- );
385
- box-shadow:
386
- 0 2px 4px rgba(0, 0, 0, 0.12),
387
- inset 0 1px 0 rgba(255, 255, 255, 0.08);
388
- }
389
-
390
- .layout-container .n-menu-item:hover::before {
391
- background: linear-gradient(
392
- 135deg,
393
- rgba(255, 255, 255, 0.12) 0%,
394
- rgba(255, 255, 255, 0.08) 100%
395
- );
396
- }
397
- }
398
-
399
- :where(.layout-container) .light-theme {
400
- color-scheme: light;
401
-
402
- .layout-container .n-menu-item::before {
403
- background: linear-gradient(
404
- 135deg,
405
- rgba(255, 255, 255, 0.6) 0%,
406
- rgba(255, 255, 255, 0.4) 100%
407
- );
408
- box-shadow:
409
- 0 2px 4px rgba(0, 0, 0, 0.04),
410
- inset 0 1px 0 rgba(255, 255, 255, 0.8);
411
- }
412
-
413
- .layout-container .n-menu-item:hover::before {
414
- background: linear-gradient(
415
- 135deg,
416
- rgba(255, 255, 255, 0.8) 0%,
417
- rgba(255, 255, 255, 0.6) 100%
418
- );
419
- }
420
-
421
- .layout-container .n-menu-item-content::after {
422
- border-color: rgba(0, 0, 0, 0.08);
423
- }
424
- }
425
-
426
- // ==================== 折叠菜单样式 ====================
427
- .layout-container .n-menu.n-menu--collapsed {
428
- width: 64px !important;
429
- display: flex;
430
- flex-direction: column;
431
- align-items: center;
432
- }
433
-
434
- .layout-container .n-menu.n-menu--collapsed .n-menu-item-content {
435
- justify-content: center !important;
436
- padding: 0 !important;
437
- display: flex;
438
- align-items: center;
439
- width: 62px;
440
- }
441
-
442
- .layout-container .n-menu.n-menu--collapsed .n-icon {
443
- margin-left: 40px !important;
444
- margin-top: 4px !important;
445
- }
446
-
447
- .layout-container .n-menu.n-menu--collapsed .n-menu-item {
448
- padding: 0 !important;
449
- display: flex;
450
- justify-content: center;
451
- width: 100%;
452
- }
453
-
454
- .layout-container .n-menu.n-menu--collapsed .n-menu-item-content-header {
455
- margin: 0 auto;
456
- margin-top: -10px;
457
- padding: 0;
458
- }
459
-
460
- .layout-container .n-menu.n-menu--collapsed .n-menu-item-content__icon {
461
- margin-right: 0;
462
- }
463
-
464
- .layout-container
465
- .n-menu.n-menu--collapsed
466
- .n-menu-item-content--child-active
467
- .n-menu-item-content__icon {
468
- color: var(--n-item-color-active) !important;
469
- }
470
-
471
- /* 折叠菜单选中状态 - 使用二级选中效果变量 */
472
- .layout-container .n-menu.n-menu--collapsed .n-menu-item-content--child-active {
473
- background: transparent !important; /* 覆盖 Naive UI 默认背景 */
474
-
475
- &::before {
476
- background: linear-gradient(
477
- 135deg,
478
- var(--menu-selected-gradient-start, rgba(99, 102, 241, 0.22)) 0%,
479
- var(--menu-selected-gradient-end, rgba(139, 92, 246, 0.16)) 100%
480
- ) !important;
481
- border-radius: var(--menu-border-radius, 10px);
482
- box-shadow:
483
- var(
484
- --menu-selected-shadow,
485
- 0 2px 12px rgba(99, 102, 241, 0.22),
486
- 0 4px 24px rgba(99, 102, 241, 0.15)
487
- ),
488
- inset 0 1px 2px rgba(99, 102, 241, 0.12),
489
- inset 0 0 0 1px rgba(255, 255, 255, 0.08);
490
- }
491
-
492
- backdrop-filter: blur(var(--menu-selected-blur, 16px))
493
- saturate(var(--menu-selected-saturate, 135%));
494
- -webkit-backdrop-filter: blur(var(--menu-selected-blur, 16px))
495
- saturate(var(--menu-selected-saturate, 135%));
496
-
497
- &::after {
498
- border-radius: var(--menu-border-radius, 10px);
499
- box-shadow:
500
- 0 0 14px rgba(99, 102, 241, 0.25),
501
- 0 0 20px rgba(99, 102, 241, 0.15);
502
- }
503
- }
504
-
505
- /* 折叠菜单 Hover 效果 */
506
- .layout-container .n-menu.n-menu--collapsed .n-menu-item:hover {
507
- transform: scale(1.05);
508
-
509
- &::before {
510
- background: rgba(255, 255, 255, 0.1);
511
- box-shadow:
512
- 0 4px 16px rgba(0, 0, 0, 0.15),
513
- inset 0 1px 0 rgba(255, 255, 255, 0.15);
514
- }
515
- }
516
-
517
- /* 折叠菜单图标颜色 */
518
- :where(.layout-container) .n-menu--collapsed .n-menu-item-content__icon {
519
- color: rgba(229, 231, 235, 0.9) !important;
520
- }
521
-
522
- // ==================== 其他辅助样式 ====================
523
- .layout-container .n-layout {
524
- width: 100%;
525
- min-width: 0;
526
- }
527
-
528
- :where(.layout-container) .no-horizontal-scroll {
529
- overflow-x: hidden !important;
530
- }
531
-
532
- // ==================== 子菜单和菜单组毛玻璃效果 ====================
533
- .layout-container .n-submenu {
534
- margin: 4px 0;
535
- }
536
-
537
- .layout-container .n-submenu > .n-menu-item-content {
538
- padding: 10px 16px;
539
- }
540
-
541
- .layout-container .n-submenu-children .n-menu-item {
542
- margin: 2px 0;
543
- position: relative;
544
-
545
- &::before {
546
- content: none;
547
- }
548
- }
549
-
550
- .layout-container .n-submenu-children .n-menu-item-content {
551
- padding: 8px 16px 8px 46px;
552
- font-size: 13px;
553
- }
554
-
555
- .layout-container .n-submenu-children .n-menu-item-content__icon {
556
- width: 16px !important;
557
- height: 16px !important;
558
- font-size: 16px !important;
559
- margin-right: 8px;
560
- }
561
-
562
- .layout-container .n-submenu-children .n-menu-item-content__icon .n-icon {
563
- width: 16px !important;
564
- height: 16px !important;
565
- font-size: 16px !important;
566
- }
567
-
568
- .layout-container .n-submenu-children .n-menu-item-content__icon svg {
569
- width: 16px !important;
570
- height: 16px !important;
571
- }
572
-
573
- .layout-container .n-submenu-children .n-menu-item:hover {
574
- transform: translateX(2px);
575
- }
576
-
577
- .layout-container .n-submenu-children {
578
- background: rgba(0, 0, 0, 0.05);
579
- backdrop-filter: blur(6px);
580
- -webkit-backdrop-filter: blur(6px);
581
- margin: 2px 0;
582
- padding: 4px 0;
583
- border-left: 2px solid rgba(99, 102, 241, 0.3);
584
- }
585
-
586
- /* 菜单组标题 */
587
- .layout-container .n-menu-item-group-title {
588
- font-size: 11px;
589
- font-weight: 600;
590
- text-transform: uppercase;
591
- letter-spacing: 1.2px;
592
- padding: 12px 16px 6px;
593
- opacity: 0.5;
594
- position: relative;
595
-
596
- &::after {
597
- content: "";
598
- position: absolute;
599
- bottom: 0;
600
- left: 16px;
601
- right: 16px;
602
- height: 1px;
603
- background: linear-gradient(
604
- to right,
605
- transparent,
606
- rgba(99, 102, 241, 0.2),
607
- transparent
608
- );
609
- }
610
- }
611
-
612
- /* 菜单分割线 */
613
- .layout-container .n-menu-divider {
614
- margin: 8px 16px;
615
- height: 1px;
616
- background: linear-gradient(
617
- to right,
618
- transparent,
619
- rgba(255, 255, 255, 0.1),
620
- transparent
621
- );
622
- backdrop-filter: blur(4px);
623
- -webkit-backdrop-filter: blur(4px);
624
- }
625
-
626
- /* 加载状态优化 */
627
- .layout-container .n-menu-item.n-menu-item--loading {
628
- &::before {
629
- background: linear-gradient(
630
- 90deg,
631
- rgba(99, 102, 241, 0.1) 0%,
632
- rgba(139, 92, 246, 0.2) 50%,
633
- rgba(99, 102, 241, 0.1) 100%
634
- );
635
- background-size: 200% 100%;
636
- animation: ra-layout-shimmer 2s infinite;
637
- }
638
- }
639
-
640
- @keyframes ra-layout-shimmer {
641
- 0% {
642
- background-position: -200% 0;
643
- }
644
- 100% {
645
- background-position: 200% 0;
646
- }
647
- }
648
-
649
- @keyframes ra-layout-pulse-glow {
650
- 0%,
651
- 100% {
652
- opacity: 0.6;
653
- transform: scale(1);
654
- }
655
- 50% {
656
- opacity: 1;
657
- transform: scale(1.1);
658
- }
659
- }
660
-
661
- /* 禁用状态 */
662
- .layout-container .n-menu-item.n-menu-item--disabled {
663
- opacity: 0.4;
664
- filter: grayscale(0.5);
665
- cursor: not-allowed;
666
-
667
- &::before {
668
- background: rgba(255, 255, 255, 0.01);
669
- }
670
-
671
- &:hover {
672
- transform: none;
673
- }
674
- }
675
-
676
- // ==================== ⚡ 页面切换过渡动画 ====================
677
-
678
- // 滑动效果
679
- :where(.layout-container) .fade-slide-enter-active,
680
- :where(.layout-container) .fade-slide-leave-active {
681
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
682
- }
683
-
684
- :where(.layout-container) .fade-slide-enter-from {
685
- opacity: 0;
686
- transform: translateX(20px);
687
- }
688
-
689
- :where(.layout-container) .fade-slide-leave-to {
690
- opacity: 0;
691
- transform: translateX(-20px);
692
- }
693
-
694
- // 淡入效果
695
- :where(.layout-container) .fade-transform-enter-active,
696
- :where(.layout-container) .fade-transform-leave-active {
697
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
698
- }
699
-
700
- :where(.layout-container) .fade-transform-enter-from {
701
- opacity: 0;
702
- transform: scale(0.95);
703
- }
704
-
705
- :where(.layout-container) .fade-transform-leave-to {
706
- opacity: 0;
707
- transform: scale(1.05);
708
- }
709
-
710
- // 缩放效果
711
- :where(.layout-container) .fade-zoom-enter-active,
712
- :where(.layout-container) .fade-zoom-leave-active {
713
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
714
- }
715
-
716
- :where(.layout-container) .fade-zoom-enter-from {
717
- opacity: 0;
718
- transform: scale(0.8);
719
- }
720
-
721
- :where(.layout-container) .fade-zoom-leave-to {
722
- opacity: 0;
723
- transform: scale(0.8);
724
- }
725
-
726
- // ==================== 🎨 布局开发中提示样式 ====================
727
-
728
- :where(.layout-container) .layout-coming-soon {
729
- display: flex;
730
- align-items: center;
731
- justify-content: center;
732
- height: 100vh;
733
- background-color: var(--app-bg-content);
734
- }
735
-
736
- :where(.layout-container) .coming-soon-content {
737
- text-align: center;
738
- max-width: 400px;
739
- padding: 40px;
740
- border-radius: 12px;
741
- background-color: var(--app-bg-surface);
742
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
743
- }
744
-
745
- :where(.layout-container) .coming-soon-icon {
746
- font-size: 64px;
747
- margin-bottom: 16px;
748
- }
749
-
750
- :where(.layout-container) .coming-soon-title {
751
- font-size: 24px;
752
- font-weight: 600;
753
- margin-bottom: 8px;
754
- color: var(--app-text-primary);
755
- }
756
-
757
- :where(.layout-container) .coming-soon-desc {
758
- font-size: 16px;
759
- color: var(--app-text-secondary);
760
- line-height: 1.5;
761
- }
1
+ /**
2
+ * @robot-admin/layout
3
+ *
4
+ * 布局全局样式 - 所有布局共享的基础样式
5
+ * 包含:布局容器、内容区、主题适配、侧边栏导航、历史菜单外观与过渡动画
6
+ *
7
+ * CSS 变量依赖(消费方需定义):
8
+ * --app-bg-body : 页面背景色
9
+ * --app-bg-content : 内容区背景色
10
+ * --app-bg-surface : 卡片/面板背景色
11
+ * --app-menu-bg : 菜单背景色
12
+ * --app-text-primary : 主文字色
13
+ * --app-text-secondary: 次文字色
14
+ * --app-border-color : 边框色
15
+ * --primary-color : 主题色
16
+ */
17
+
18
+ // 侧栏导航独立维护;菜单数据、原生缩进及弹出层仍由 NMenu 管理。
19
+ @use "./sidebar-navigation";
20
+
21
+ // 精致侧栏通过显式菜单标记启用;旧菜单外观仅作用于未接入的菜单。
22
+ // :where 保持原选择器权重,避免升级包后改变其他布局及内容区菜单的覆盖顺序。
23
+ $legacy-menu-scope: ":not(:where([data-ra-sidebar-menu], [data-ra-sidebar-menu] *))";
24
+
25
+ // ==================== 🎨 菜单样式自定义变量(可在主项目覆盖)====================
26
+ .layout-container {
27
+ // 菜单项圆角
28
+ --menu-border-radius: 10px;
29
+
30
+ // 一级菜单父项激活效果(子菜单展开状态)- 较淡
31
+ --menu-parent-gradient-start: rgba(99, 102, 241, 0.1);
32
+ --menu-parent-gradient-end: rgba(139, 92, 246, 0.06);
33
+ --menu-parent-border-color: rgba(99, 102, 241, 0.45);
34
+ --menu-parent-border-width: 2px;
35
+ --menu-parent-shadow:
36
+ 0 2px 6px rgba(99, 102, 241, 0.1), 0 4px 12px rgba(99, 102, 241, 0.06);
37
+
38
+ // 二级菜单选中效果(当前页面)- 明显更亮,层级清晰
39
+ --menu-selected-gradient-start: rgba(99, 102, 241, 0.22);
40
+ --menu-selected-gradient-end: rgba(139, 92, 246, 0.16);
41
+ --menu-selected-border-color: rgba(99, 102, 241, 0.75);
42
+ --menu-selected-border-width: 3px;
43
+ --menu-selected-shadow:
44
+ 0 2px 12px rgba(99, 102, 241, 0.22), 0 4px 24px rgba(99, 102, 241, 0.15);
45
+
46
+ // 毛玻璃效果强度
47
+ --menu-parent-blur: 14px;
48
+ --menu-parent-saturate: 130%;
49
+ --menu-selected-blur: 16px;
50
+ --menu-selected-saturate: 135%;
51
+ }
52
+
53
+ // ==================== 基础布局样式 ====================
54
+ .layout-container {
55
+ height: 100vh;
56
+ overflow: hidden;
57
+ display: flex;
58
+ flex-direction: column;
59
+ }
60
+
61
+ // ==================== 内容区域样式 ====================
62
+ .layout-container .content-with-header {
63
+ margin-top: 100px;
64
+ height: calc(100vh - 150px);
65
+ }
66
+
67
+ .layout-container .app-content {
68
+ background-color: var(--app-bg-content);
69
+ }
70
+
71
+ .layout-container .n-layout-content {
72
+ display: flex;
73
+ flex-direction: column;
74
+ flex: 1;
75
+ overflow-x: hidden;
76
+ }
77
+
78
+ :where(.layout-container) .main-content {
79
+ flex: 1;
80
+ padding: 16px;
81
+ overflow-y: auto;
82
+ overflow-x: hidden;
83
+ width: 100%;
84
+ border-radius: 8px;
85
+ }
86
+
87
+ // ==================== 主题相关样式 ====================
88
+ [data-theme="light"] .layout-container .light-theme,
89
+ .layout-container .light-theme {
90
+ background-color: var(--app-bg-body);
91
+ }
92
+
93
+ [data-theme="dark"] .layout-container .dark-theme,
94
+ .layout-container .dark-theme {
95
+ background-color: var(--app-bg-body);
96
+ }
97
+
98
+ .layout-container.light-mode .n-layout .n-layout-scroll-container {
99
+ background-color: var(--app-bg-surface);
100
+ border-radius: 10px;
101
+ }
102
+
103
+ .layout-container.dark-mode .n-layout .n-layout-scroll-container,
104
+ .layout-container.system-mode .n-layout .n-layout-scroll-container {
105
+ background-color: var(--app-bg-surface);
106
+ }
107
+
108
+ // ==================== 侧边栏和菜单样式 ====================
109
+ :where(.layout-container) .layout-sider {
110
+ transform: translateZ(0);
111
+ backface-visibility: hidden;
112
+ contain: layout style paint;
113
+ }
114
+
115
+ :where(.layout-container) .layout-sider.light-theme {
116
+ background-color: var(--app-menu-bg);
117
+ }
118
+
119
+ :where(.layout-container) .layout-sider.dark-theme {
120
+ background-color: var(--app-menu-bg);
121
+ }
122
+
123
+ :where(.layout-container) .layout-sider .n-scrollbar-rail {
124
+ width: 0 !important;
125
+ }
126
+
127
+ :where(.layout-container) .layout-sider .n-scrollbar-content {
128
+ padding-right: 0 !important;
129
+ }
130
+
131
+ .layout-container .n-layout-sider {
132
+ flex-shrink: 0;
133
+ height: 100%;
134
+ display: flex;
135
+ flex-direction: column;
136
+ overflow: hidden;
137
+ }
138
+
139
+ :where(.layout-container) .menu-scroll-container {
140
+ height: calc(100vh - 56px);
141
+ overflow-y: scroll;
142
+ scrollbar-width: none;
143
+ -ms-overflow-style: none;
144
+ flex: 1;
145
+ display: flex;
146
+ flex-direction: column;
147
+ }
148
+
149
+ :where(.layout-container) .menu-scroll-container::-webkit-scrollbar {
150
+ display: none;
151
+ }
152
+
153
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--vertical {
154
+ flex: 1;
155
+ min-height: min-content;
156
+ overflow-y: auto;
157
+ overflow-x: hidden;
158
+ }
159
+
160
+ // ==================== 菜单图标和文字样式 ====================
161
+ .layout-container .n-menu-item-content__icon#{$legacy-menu-scope} {
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ vertical-align: middle;
166
+ width: 18px !important;
167
+ height: 18px !important;
168
+ margin-right: 10px;
169
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
170
+ font-size: 18px !important;
171
+ }
172
+
173
+ .layout-container .n-menu-item-content__icon#{$legacy-menu-scope} .n-icon {
174
+ width: 18px !important;
175
+ height: 18px !important;
176
+ font-size: 18px !important;
177
+ }
178
+
179
+ .layout-container .n-menu-item-content__icon#{$legacy-menu-scope} svg {
180
+ width: 18px !important;
181
+ height: 18px !important;
182
+ }
183
+
184
+ .layout-container .n-menu-item-content-header#{$legacy-menu-scope} {
185
+ font-size: 14px;
186
+ font-weight: 500;
187
+ letter-spacing: 0.2px;
188
+ line-height: 1.5;
189
+ }
190
+
191
+ .layout-container .n-menu-item-content__arrow#{$legacy-menu-scope} {
192
+ font-size: 14px;
193
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
194
+ }
195
+
196
+ // ==================== 🎨 毛玻璃质感菜单效果(性能优化版)====================
197
+
198
+ /* 菜单项基础样式 - 卡片质感默认状态 */
199
+ .layout-container .n-menu-item#{$legacy-menu-scope} {
200
+ border-radius: 0;
201
+ position: relative;
202
+ overflow: visible;
203
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
204
+ will-change: transform;
205
+
206
+ &::before {
207
+ content: "";
208
+ position: absolute;
209
+ top: 0;
210
+ left: 0;
211
+ right: 0;
212
+ bottom: 0;
213
+ background: linear-gradient(
214
+ 135deg,
215
+ rgba(255, 255, 255, 0.08) 0%,
216
+ rgba(255, 255, 255, 0.04) 100%
217
+ );
218
+ border-radius: 10px;
219
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
220
+ z-index: -1;
221
+ box-shadow:
222
+ 0 2px 4px rgba(0, 0, 0, 0.08),
223
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
224
+ }
225
+ }
226
+
227
+ /* 菜单项内容容器 - 卡片质感 */
228
+ .layout-container .n-menu-item-content#{$legacy-menu-scope} {
229
+ border-radius: var(--menu-border-radius, 10px);
230
+ background: transparent;
231
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
232
+ position: relative;
233
+ padding: 10px 16px;
234
+
235
+ &::after {
236
+ content: "";
237
+ position: absolute;
238
+ top: 0;
239
+ left: 0;
240
+ right: 0;
241
+ bottom: 0;
242
+ border: 1px solid rgba(255, 255, 255, 0.06);
243
+ border-left: 2px solid transparent;
244
+ border-radius: var(--menu-border-radius, 10px);
245
+ pointer-events: none;
246
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
247
+ }
248
+ }
249
+
250
+ /* Hover 状态 - 增强卡片效果 */
251
+ .layout-container .n-menu-item#{$legacy-menu-scope}:hover {
252
+ transform: translateX(2px);
253
+
254
+ &::before {
255
+ background: rgba(255, 255, 255, 0.1);
256
+ border-radius: 10px;
257
+ box-shadow:
258
+ 0 4px 16px rgba(0, 0, 0, 0.12),
259
+ inset 0 1px 0 rgba(255, 255, 255, 0.12);
260
+ }
261
+ }
262
+
263
+ .layout-container .n-menu-item#{$legacy-menu-scope}:hover .n-menu-item-content {
264
+ backdrop-filter: blur(12px);
265
+ -webkit-backdrop-filter: blur(12px);
266
+ border-radius: 10px;
267
+
268
+ &::after {
269
+ border-left-color: rgba(99, 102, 241, 0.4);
270
+ border-radius: 10px;
271
+ box-shadow:
272
+ 0 0 15px rgba(99, 102, 241, 0.15),
273
+ inset 0 1px 1px rgba(255, 255, 255, 0.1);
274
+ }
275
+ }
276
+
277
+ .layout-container .n-menu-item#{$legacy-menu-scope}:hover .n-menu-item-content__icon {
278
+ transform: scale(1.08);
279
+ color: var(--n-item-icon-color-active-hover);
280
+ }
281
+
282
+ /* Active/选中状态 - 二级菜单选中(当前页面)- 略强玻璃质感 */
283
+ .layout-container .n-menu-item--selected#{$legacy-menu-scope},
284
+ .layout-container .n-menu-item-content--selected#{$legacy-menu-scope} {
285
+ background: transparent !important; /* 覆盖 Naive UI 默认背景 */
286
+
287
+ &::before {
288
+ background: linear-gradient(
289
+ 135deg,
290
+ var(--menu-selected-gradient-start, rgba(99, 102, 241, 0.22)) 0%,
291
+ var(--menu-selected-gradient-end, rgba(139, 92, 246, 0.16)) 100%
292
+ ) !important;
293
+ border-radius: var(--menu-border-radius, 10px);
294
+ box-shadow:
295
+ var(
296
+ --menu-selected-shadow,
297
+ 0 2px 12px rgba(99, 102, 241, 0.22),
298
+ 0 4px 24px rgba(99, 102, 241, 0.15)
299
+ ),
300
+ inset 0 1px 2px rgba(99, 102, 241, 0.12),
301
+ inset 0 0 0 1px rgba(255, 255, 255, 0.08);
302
+ }
303
+ }
304
+
305
+ .layout-container .n-menu-item--selected#{$legacy-menu-scope} .n-menu-item-content,
306
+ .layout-container .n-menu-item-content--selected#{$legacy-menu-scope} {
307
+ background: transparent !important; /* 覆盖 Naive UI 默认背景 */
308
+ backdrop-filter: blur(var(--menu-selected-blur, 16px))
309
+ saturate(var(--menu-selected-saturate, 135%));
310
+ -webkit-backdrop-filter: blur(var(--menu-selected-blur, 16px))
311
+ saturate(var(--menu-selected-saturate, 135%));
312
+
313
+ &::after {
314
+ border-left: var(--menu-selected-border-width, 3px) solid
315
+ var(--menu-selected-border-color, rgba(99, 102, 241, 0.75));
316
+ border-radius: var(--menu-border-radius, 10px);
317
+ box-shadow:
318
+ 0 0 14px rgba(99, 102, 241, 0.25),
319
+ inset 0 1px 2px rgba(255, 255, 255, 0.12);
320
+ }
321
+ }
322
+
323
+ .layout-container .n-menu-item--selected#{$legacy-menu-scope} .n-menu-item-content__icon {
324
+ filter: drop-shadow(0 0 6px rgba(99, 102, 241, 0.5));
325
+ transform: scale(1.05);
326
+ color: var(--n-item-icon-color-active);
327
+ }
328
+
329
+ /* Active 按下状态 */
330
+ .layout-container .n-menu-item#{$legacy-menu-scope}:active {
331
+ transform: scale(0.98);
332
+ }
333
+
334
+ /* 子菜单父项样式 - 一级菜单展开状态(较淡玻璃质感)*/
335
+ .layout-container .n-menu-item-content--child-active#{$legacy-menu-scope} {
336
+ background: transparent !important; /* 覆盖 Naive UI 默认背景 */
337
+
338
+ &::before {
339
+ background: linear-gradient(
340
+ 135deg,
341
+ var(--menu-parent-gradient-start, rgba(99, 102, 241, 0.1)) 0%,
342
+ var(--menu-parent-gradient-end, rgba(139, 92, 246, 0.06)) 100%
343
+ ) !important;
344
+ border-radius: var(--menu-border-radius, 10px);
345
+ box-shadow:
346
+ var(
347
+ --menu-parent-shadow,
348
+ 0 2px 6px rgba(99, 102, 241, 0.1),
349
+ 0 4px 12px rgba(99, 102, 241, 0.06)
350
+ ),
351
+ inset 0 1px 2px rgba(99, 102, 241, 0.06),
352
+ inset 0 0 0 1px rgba(255, 255, 255, 0.05);
353
+ }
354
+
355
+ &::after {
356
+ border-left: var(--menu-parent-border-width, 2px) solid
357
+ var(--menu-parent-border-color, rgba(99, 102, 241, 0.45));
358
+ border-radius: var(--menu-border-radius, 10px);
359
+ }
360
+
361
+ backdrop-filter: blur(var(--menu-parent-blur, 14px))
362
+ saturate(var(--menu-parent-saturate, 130%));
363
+ -webkit-backdrop-filter: blur(var(--menu-parent-blur, 14px))
364
+ saturate(var(--menu-parent-saturate, 130%));
365
+ }
366
+
367
+ /* 一级菜单项对齐 */
368
+ .layout-container .n-menu#{$legacy-menu-scope} > .n-menu-item,
369
+ .layout-container .n-menu#{$legacy-menu-scope} > .n-submenu {
370
+ margin: 4px 12px;
371
+ }
372
+
373
+ /* 二级及以下菜单项 */
374
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item {
375
+ margin: 4px 0;
376
+ }
377
+
378
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item-content {
379
+ padding-left: 46px;
380
+ }
381
+
382
+ // ==================== 主题优化 ====================
383
+ :where(.layout-container) .dark-theme {
384
+ color-scheme: dark;
385
+
386
+ .layout-container .n-menu-item#{$legacy-menu-scope}::before {
387
+ background: linear-gradient(
388
+ 135deg,
389
+ rgba(255, 255, 255, 0.08) 0%,
390
+ rgba(255, 255, 255, 0.04) 100%
391
+ );
392
+ box-shadow:
393
+ 0 2px 4px rgba(0, 0, 0, 0.12),
394
+ inset 0 1px 0 rgba(255, 255, 255, 0.08);
395
+ }
396
+
397
+ .layout-container .n-menu-item#{$legacy-menu-scope}:hover::before {
398
+ background: linear-gradient(
399
+ 135deg,
400
+ rgba(255, 255, 255, 0.12) 0%,
401
+ rgba(255, 255, 255, 0.08) 100%
402
+ );
403
+ }
404
+ }
405
+
406
+ :where(.layout-container) .light-theme {
407
+ color-scheme: light;
408
+
409
+ .layout-container .n-menu-item#{$legacy-menu-scope}::before {
410
+ background: linear-gradient(
411
+ 135deg,
412
+ rgba(255, 255, 255, 0.6) 0%,
413
+ rgba(255, 255, 255, 0.4) 100%
414
+ );
415
+ box-shadow:
416
+ 0 2px 4px rgba(0, 0, 0, 0.04),
417
+ inset 0 1px 0 rgba(255, 255, 255, 0.8);
418
+ }
419
+
420
+ .layout-container .n-menu-item#{$legacy-menu-scope}:hover::before {
421
+ background: linear-gradient(
422
+ 135deg,
423
+ rgba(255, 255, 255, 0.8) 0%,
424
+ rgba(255, 255, 255, 0.6) 100%
425
+ );
426
+ }
427
+
428
+ .layout-container .n-menu-item-content#{$legacy-menu-scope}::after {
429
+ border-color: rgba(0, 0, 0, 0.08);
430
+ }
431
+ }
432
+
433
+ // ==================== 折叠菜单样式 ====================
434
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed {
435
+ width: 64px !important;
436
+ display: flex;
437
+ flex-direction: column;
438
+ align-items: center;
439
+ }
440
+
441
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed .n-menu-item-content {
442
+ justify-content: center !important;
443
+ padding: 0 !important;
444
+ display: flex;
445
+ align-items: center;
446
+ width: 62px;
447
+ }
448
+
449
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed .n-icon {
450
+ margin-left: 40px !important;
451
+ margin-top: 4px !important;
452
+ }
453
+
454
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed .n-menu-item {
455
+ padding: 0 !important;
456
+ display: flex;
457
+ justify-content: center;
458
+ width: 100%;
459
+ }
460
+
461
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed .n-menu-item-content-header {
462
+ margin: 0 auto;
463
+ margin-top: -10px;
464
+ padding: 0;
465
+ }
466
+
467
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed .n-menu-item-content__icon {
468
+ margin-right: 0;
469
+ }
470
+
471
+ .layout-container
472
+ .n-menu#{$legacy-menu-scope}.n-menu--collapsed
473
+ .n-menu-item-content--child-active#{$legacy-menu-scope}
474
+ .n-menu-item-content__icon#{$legacy-menu-scope} {
475
+ color: var(--n-item-color-active) !important;
476
+ }
477
+
478
+ /* 折叠菜单选中状态 - 使用二级选中效果变量 */
479
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed .n-menu-item-content--child-active {
480
+ background: transparent !important; /* 覆盖 Naive UI 默认背景 */
481
+
482
+ &::before {
483
+ background: linear-gradient(
484
+ 135deg,
485
+ var(--menu-selected-gradient-start, rgba(99, 102, 241, 0.22)) 0%,
486
+ var(--menu-selected-gradient-end, rgba(139, 92, 246, 0.16)) 100%
487
+ ) !important;
488
+ border-radius: var(--menu-border-radius, 10px);
489
+ box-shadow:
490
+ var(
491
+ --menu-selected-shadow,
492
+ 0 2px 12px rgba(99, 102, 241, 0.22),
493
+ 0 4px 24px rgba(99, 102, 241, 0.15)
494
+ ),
495
+ inset 0 1px 2px rgba(99, 102, 241, 0.12),
496
+ inset 0 0 0 1px rgba(255, 255, 255, 0.08);
497
+ }
498
+
499
+ backdrop-filter: blur(var(--menu-selected-blur, 16px))
500
+ saturate(var(--menu-selected-saturate, 135%));
501
+ -webkit-backdrop-filter: blur(var(--menu-selected-blur, 16px))
502
+ saturate(var(--menu-selected-saturate, 135%));
503
+
504
+ &::after {
505
+ border-radius: var(--menu-border-radius, 10px);
506
+ box-shadow:
507
+ 0 0 14px rgba(99, 102, 241, 0.25),
508
+ 0 0 20px rgba(99, 102, 241, 0.15);
509
+ }
510
+ }
511
+
512
+ /* 折叠菜单 Hover 效果 */
513
+ .layout-container .n-menu#{$legacy-menu-scope}.n-menu--collapsed .n-menu-item:hover {
514
+ transform: scale(1.05);
515
+
516
+ &::before {
517
+ background: rgba(255, 255, 255, 0.1);
518
+ box-shadow:
519
+ 0 4px 16px rgba(0, 0, 0, 0.15),
520
+ inset 0 1px 0 rgba(255, 255, 255, 0.15);
521
+ }
522
+ }
523
+
524
+ /* 折叠菜单图标颜色 */
525
+ :where(.layout-container) .n-menu--collapsed#{$legacy-menu-scope} .n-menu-item-content__icon {
526
+ color: rgba(229, 231, 235, 0.9) !important;
527
+ }
528
+
529
+ // ==================== 其他辅助样式 ====================
530
+ .layout-container .n-layout {
531
+ width: 100%;
532
+ min-width: 0;
533
+ }
534
+
535
+ :where(.layout-container) .no-horizontal-scroll {
536
+ overflow-x: hidden !important;
537
+ }
538
+
539
+ // ==================== 子菜单和菜单组毛玻璃效果 ====================
540
+ .layout-container .n-submenu#{$legacy-menu-scope} {
541
+ margin: 4px 0;
542
+ }
543
+
544
+ .layout-container .n-submenu#{$legacy-menu-scope} > .n-menu-item-content {
545
+ padding: 10px 16px;
546
+ }
547
+
548
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item {
549
+ margin: 2px 0;
550
+ position: relative;
551
+
552
+ &::before {
553
+ content: none;
554
+ }
555
+ }
556
+
557
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item-content {
558
+ padding: 8px 16px 8px 46px;
559
+ font-size: 13px;
560
+ }
561
+
562
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item-content__icon {
563
+ width: 16px !important;
564
+ height: 16px !important;
565
+ font-size: 16px !important;
566
+ margin-right: 8px;
567
+ }
568
+
569
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item-content__icon .n-icon {
570
+ width: 16px !important;
571
+ height: 16px !important;
572
+ font-size: 16px !important;
573
+ }
574
+
575
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item-content__icon svg {
576
+ width: 16px !important;
577
+ height: 16px !important;
578
+ }
579
+
580
+ .layout-container .n-submenu-children#{$legacy-menu-scope} .n-menu-item:hover {
581
+ transform: translateX(2px);
582
+ }
583
+
584
+ .layout-container .n-submenu-children#{$legacy-menu-scope} {
585
+ background: rgba(0, 0, 0, 0.05);
586
+ backdrop-filter: blur(6px);
587
+ -webkit-backdrop-filter: blur(6px);
588
+ margin: 2px 0;
589
+ padding: 4px 0;
590
+ border-left: 2px solid rgba(99, 102, 241, 0.3);
591
+ }
592
+
593
+ /* 菜单组标题 */
594
+ .layout-container .n-menu-item-group-title#{$legacy-menu-scope} {
595
+ font-size: 11px;
596
+ font-weight: 600;
597
+ text-transform: uppercase;
598
+ letter-spacing: 1.2px;
599
+ padding: 12px 16px 6px;
600
+ opacity: 0.5;
601
+ position: relative;
602
+
603
+ &::after {
604
+ content: "";
605
+ position: absolute;
606
+ bottom: 0;
607
+ left: 16px;
608
+ right: 16px;
609
+ height: 1px;
610
+ background: linear-gradient(
611
+ to right,
612
+ transparent,
613
+ rgba(99, 102, 241, 0.2),
614
+ transparent
615
+ );
616
+ }
617
+ }
618
+
619
+ /* 菜单分割线 */
620
+ .layout-container .n-menu-divider#{$legacy-menu-scope} {
621
+ margin: 8px 16px;
622
+ height: 1px;
623
+ background: linear-gradient(
624
+ to right,
625
+ transparent,
626
+ rgba(255, 255, 255, 0.1),
627
+ transparent
628
+ );
629
+ backdrop-filter: blur(4px);
630
+ -webkit-backdrop-filter: blur(4px);
631
+ }
632
+
633
+ /* 加载状态优化 */
634
+ .layout-container .n-menu-item#{$legacy-menu-scope}.n-menu-item--loading {
635
+ &::before {
636
+ background: linear-gradient(
637
+ 90deg,
638
+ rgba(99, 102, 241, 0.1) 0%,
639
+ rgba(139, 92, 246, 0.2) 50%,
640
+ rgba(99, 102, 241, 0.1) 100%
641
+ );
642
+ background-size: 200% 100%;
643
+ animation: ra-layout-shimmer 2s infinite;
644
+ }
645
+ }
646
+
647
+ @keyframes ra-layout-shimmer {
648
+ 0% {
649
+ background-position: -200% 0;
650
+ }
651
+ 100% {
652
+ background-position: 200% 0;
653
+ }
654
+ }
655
+
656
+ @keyframes ra-layout-pulse-glow {
657
+ 0%,
658
+ 100% {
659
+ opacity: 0.6;
660
+ transform: scale(1);
661
+ }
662
+ 50% {
663
+ opacity: 1;
664
+ transform: scale(1.1);
665
+ }
666
+ }
667
+
668
+ /* 禁用状态 */
669
+ .layout-container .n-menu-item#{$legacy-menu-scope}.n-menu-item--disabled {
670
+ opacity: 0.4;
671
+ filter: grayscale(0.5);
672
+ cursor: not-allowed;
673
+
674
+ &::before {
675
+ background: rgba(255, 255, 255, 0.01);
676
+ }
677
+
678
+ &:hover {
679
+ transform: none;
680
+ }
681
+ }
682
+
683
+ // ==================== ⚡ 页面切换过渡动画 ====================
684
+
685
+ // 滑动效果
686
+ :where(.layout-container) .fade-slide-enter-active,
687
+ :where(.layout-container) .fade-slide-leave-active {
688
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
689
+ }
690
+
691
+ :where(.layout-container) .fade-slide-enter-from {
692
+ opacity: 0;
693
+ transform: translateX(20px);
694
+ }
695
+
696
+ :where(.layout-container) .fade-slide-leave-to {
697
+ opacity: 0;
698
+ transform: translateX(-20px);
699
+ }
700
+
701
+ // 淡入效果
702
+ :where(.layout-container) .fade-transform-enter-active,
703
+ :where(.layout-container) .fade-transform-leave-active {
704
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
705
+ }
706
+
707
+ :where(.layout-container) .fade-transform-enter-from {
708
+ opacity: 0;
709
+ transform: scale(0.95);
710
+ }
711
+
712
+ :where(.layout-container) .fade-transform-leave-to {
713
+ opacity: 0;
714
+ transform: scale(1.05);
715
+ }
716
+
717
+ // 缩放效果
718
+ :where(.layout-container) .fade-zoom-enter-active,
719
+ :where(.layout-container) .fade-zoom-leave-active {
720
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
721
+ }
722
+
723
+ :where(.layout-container) .fade-zoom-enter-from {
724
+ opacity: 0;
725
+ transform: scale(0.8);
726
+ }
727
+
728
+ :where(.layout-container) .fade-zoom-leave-to {
729
+ opacity: 0;
730
+ transform: scale(0.8);
731
+ }
732
+
733
+ // ==================== 🎨 布局开发中提示样式 ====================
734
+
735
+ :where(.layout-container) .layout-coming-soon {
736
+ display: flex;
737
+ align-items: center;
738
+ justify-content: center;
739
+ height: 100vh;
740
+ background-color: var(--app-bg-content);
741
+ }
742
+
743
+ :where(.layout-container) .coming-soon-content {
744
+ text-align: center;
745
+ max-width: 400px;
746
+ padding: 40px;
747
+ border-radius: 12px;
748
+ background-color: var(--app-bg-surface);
749
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
750
+ }
751
+
752
+ :where(.layout-container) .coming-soon-icon {
753
+ font-size: 64px;
754
+ margin-bottom: 16px;
755
+ }
756
+
757
+ :where(.layout-container) .coming-soon-title {
758
+ font-size: 24px;
759
+ font-weight: 600;
760
+ margin-bottom: 8px;
761
+ color: var(--app-text-primary);
762
+ }
763
+
764
+ :where(.layout-container) .coming-soon-desc {
765
+ font-size: 16px;
766
+ color: var(--app-text-secondary);
767
+ line-height: 1.5;
768
+ }