vue-stack-tabs 2.2.0 → 2.4.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 (57) hide show
  1. package/README.en.md +14 -0
  2. package/README.md +14 -0
  3. package/dist/StackTabs.vue.d.ts +2 -2
  4. package/dist/components/StackKeepAlive/StackCacheRenderer.vue.d.ts +0 -2
  5. package/dist/hooks/stackTabsContext.d.ts +0 -1
  6. package/dist/hooks/tabPageContext.d.ts +18 -0
  7. package/dist/hooks/tabPanel/evict.d.ts +2 -2
  8. package/dist/hooks/tabPanel/scroll.d.ts +0 -2
  9. package/dist/hooks/tabPanel/state.d.ts +0 -1
  10. package/dist/hooks/useIframeManager.d.ts +1 -1
  11. package/dist/hooks/useScrollShadow.d.ts +11 -2
  12. package/dist/hooks/useTabEventBus.d.ts +5 -0
  13. package/dist/hooks/useTabLoading.d.ts +2 -1
  14. package/dist/hooks/useTabRouter.d.ts +5 -1
  15. package/dist/model/TabModel.d.ts +12 -5
  16. package/dist/nuxt/module.mjs +1 -1
  17. package/dist/nuxt/runtime/plugin.mjs +8 -1598
  18. package/dist/style.css +1 -1
  19. package/dist/utils/scrollUtils.d.ts +0 -2
  20. package/dist/utils/tabInfoEncoder.d.ts +0 -2
  21. package/dist/utils/urlParser.d.ts +6 -7
  22. package/dist/vue-stack-tabs.es.js +1 -1
  23. package/package.json +2 -2
  24. package/src/lib/StackTabs.vue +16 -1
  25. package/src/lib/assets/image/open.svg +1 -1
  26. package/src/lib/assets/style/common/mixins.scss +6 -6
  27. package/src/lib/assets/style/common/theme.scss +148 -0
  28. package/src/lib/assets/style/common/var.scss +104 -1
  29. package/src/lib/assets/style/index.scss +1 -0
  30. package/src/lib/assets/style/loading.scss +3 -3
  31. package/src/lib/assets/style/stack-tab.scss +62 -56
  32. package/src/lib/components/ContextMenu/index.vue +3 -1
  33. package/src/lib/components/PageLoading.vue +10 -9
  34. package/src/lib/components/StackKeepAlive/StackCacheRenderer.vue +1 -5
  35. package/src/lib/components/TabHeader/TabHeaderScroll.vue +164 -90
  36. package/src/lib/components/TabHeader/index.vue +51 -10
  37. package/src/lib/hooks/stackTabsContext.ts +0 -1
  38. package/src/lib/hooks/tabPageContext.ts +50 -0
  39. package/src/lib/hooks/tabPanel/evict.ts +14 -6
  40. package/src/lib/hooks/tabPanel/pageComponentFactory.tsx +224 -47
  41. package/src/lib/hooks/tabPanel/scroll.ts +4 -15
  42. package/src/lib/hooks/tabPanel/session.ts +11 -4
  43. package/src/lib/hooks/tabPanel/state.ts +0 -2
  44. package/src/lib/hooks/tabPanel/tabStore.ts +28 -10
  45. package/src/lib/hooks/useIframeManager.ts +11 -15
  46. package/src/lib/hooks/useScrollShadow.ts +74 -19
  47. package/src/lib/hooks/useTabActions.ts +10 -4
  48. package/src/lib/hooks/useTabEventBus.ts +3 -0
  49. package/src/lib/hooks/useTabLoading.ts +6 -4
  50. package/src/lib/hooks/useTabPanel.tsx +46 -34
  51. package/src/lib/hooks/useTabRouter.ts +11 -14
  52. package/src/lib/model/TabModel.ts +14 -5
  53. package/src/lib/nuxt/module.ts +3 -1
  54. package/src/lib/nuxt/runtime/plugin.ts +7 -3
  55. package/src/lib/utils/scrollUtils.ts +0 -18
  56. package/src/lib/utils/tabInfoEncoder.ts +21 -2
  57. package/src/lib/utils/urlParser.ts +40 -37
@@ -8,6 +8,9 @@
8
8
  $stack-tab-color-white: #fff !default;
9
9
  $stack-tab-color-black: #000 !default;
10
10
 
11
+ // 主色上的文字/图标(深色主题下会反转为深色)
12
+ $stack-tab-color-accent-contrast: $stack-tab-color-white !default;
13
+
11
14
  // 主色
12
15
  $stack-tab-color-primary: #409eff !default;
13
16
  $stack-tab-color-primary-invert: #ffac30 !default;
@@ -42,6 +45,7 @@ $stack-tab-iframe-error-bg: rgb(255 255 255 / 92%) !default;
42
45
 
43
46
  $stack-tab-border-color: #f0f0f0 !default;
44
47
  $stack-tab-border: 1px solid $stack-tab-border-color !default;
48
+ $stack-tab-color-divider: #a9a9a9 !default;
45
49
  $stack-tab-tab-radius: 8px 8px 0 0 !default;
46
50
  $stack-tab-contextmenu-radius: 10px 0 !default;
47
51
 
@@ -88,13 +92,14 @@ $stack-tab-transition-slide:
88
92
  // ============================================================
89
93
 
90
94
  $stack-tab-shadow-color: rgb(0 0 0 / 60%) !default;
95
+ $stack-tab-shadow-soft-color: rgb(0 0 0 / 10%) !default;
91
96
  $stack-tab-shadow-left: -5px 0 5px -5px $stack-tab-shadow-color !default;
92
97
  $stack-tab-shadow-right: 5px 0 5px -5px $stack-tab-shadow-color !default;
93
98
  $stack-tab-shadow-right-inset: inset -5px 0 5px -5px $stack-tab-shadow-color !default;
94
99
  $stack-tab-shadow-left-inset: inset 5px 0 5px -5px $stack-tab-shadow-color !default;
95
100
  $stack-tab-shadow-top-inset: inset 0 5px 5px -5px $stack-tab-shadow-color !default;
96
101
  $stack-tab-shadow-bottom-inset: inset 0 -5px 5px -5px $stack-tab-shadow-color !default;
97
- $stack-tab-shadow-context-menu: 5px 5px 5px rgb(0 0 0 / 10%) !default;
102
+ $stack-tab-shadow-context-menu: 5px 5px 5px $stack-tab-shadow-soft-color !default;
98
103
 
99
104
  // ============================================================
100
105
  // 加载态
@@ -106,3 +111,101 @@ $stack-tab-loading-size: 50px !default;
106
111
  $stack-tab-loading-border-width: 8px !default;
107
112
  $stack-tab-loading-color: #007bff97 !default;
108
113
  $stack-tab-loading-duration: 1s !default;
114
+
115
+ // ============================================================
116
+ // 主题令牌(运行时 CSS 变量)
117
+ //
118
+ // 规则统一消费这一组变量;默认值定义在 theme.scss 的 :root 中。
119
+ // var() 的 fallback 使用上面的编译期变量,保证单独编译的 scoped
120
+ // 样式或未加载主题入口时仍有正确颜色,同时保留 SCSS 变量定制能力。
121
+ // ============================================================
122
+
123
+ // accent
124
+ $stack-tab-accent: var(--stack-tab-accent, #{$stack-tab-color-primary}) !default;
125
+ $stack-tab-accent-active: var(
126
+ --stack-tab-accent-active,
127
+ #{$stack-tab-color-primary-activate}
128
+ ) !default;
129
+ $stack-tab-accent-contrast: var(
130
+ --stack-tab-accent-contrast,
131
+ #{$stack-tab-color-accent-contrast}
132
+ ) !default;
133
+ $stack-tab-accent-shadow: var(
134
+ --stack-tab-accent-shadow,
135
+ #{rgba($stack-tab-color-primary, 0.8)}
136
+ ) !default;
137
+
138
+ // surface
139
+ $stack-tab-surface: var(--stack-tab-surface, #{$stack-tab-background-color-base}) !default;
140
+ $stack-tab-surface-muted: var(
141
+ --stack-tab-surface-muted,
142
+ #{$stack-tab-background-color-inactive}
143
+ ) !default;
144
+ $stack-tab-overlay: var(--stack-tab-overlay, #{$stack-tab-loading-mask-color}) !default;
145
+
146
+ // text / icon
147
+ $stack-tab-text: var(--stack-tab-text, #{$stack-tab-text-color}) !default;
148
+ $stack-tab-text-strong: var(--stack-tab-text-strong, #{$stack-tab-color-black}) !default;
149
+ $stack-tab-text-disabled: var(--stack-tab-text-disabled, #{$stack-tab-color-disabled}) !default;
150
+ $stack-tab-icon-mask: var(--stack-tab-mask, #{$stack-tab-text-color}) !default;
151
+ $stack-tab-font-size-token: var(--stack-tab-font-size, #{$stack-tab-font-size}) !default;
152
+
153
+ // feedback
154
+ $stack-tab-scrollbar: var(--stack-tab-scrollbar, #{$stack-tab-color-scrollbar}) !default;
155
+
156
+ // border
157
+ $stack-tab-border-color-token: var(--stack-tab-border, #{$stack-tab-border-color}) !default;
158
+ $stack-tab-border-token: 1px solid $stack-tab-border-color-token !default;
159
+ $stack-tab-divider: var(--stack-tab-divider, #{$stack-tab-color-divider}) !default;
160
+
161
+ // shadow:颜色层
162
+ $stack-tab-shadow-color-token: var(--stack-tab-shadow-color, #{$stack-tab-shadow-color}) !default;
163
+
164
+ // shadow:整体层
165
+ $stack-tab-shadow-tab: var(
166
+ --stack-tab-shadow-tab,
167
+ #{$stack-tab-shadow-left},
168
+ #{$stack-tab-shadow-right}
169
+ ) !default;
170
+ $stack-tab-shadow-tab-left: var(--stack-tab-shadow-tab-left, #{$stack-tab-shadow-left}) !default;
171
+ $stack-tab-shadow-tab-right: var(--stack-tab-shadow-tab-right, #{$stack-tab-shadow-right}) !default;
172
+ $stack-tab-shadow-menu: var(
173
+ --stack-tab-shadow-menu,
174
+ 5px 5px 5px #{$stack-tab-shadow-soft-color}
175
+ ) !default;
176
+ $stack-tab-shadow-drop: var(--stack-tab-shadow-drop, 0 0 6px #{$stack-tab-accent-shadow}) !default;
177
+
178
+ // shadow:inset 工具类
179
+ $stack-tab-shadow-inset-left: var(
180
+ --stack-tab-shadow-inset-left,
181
+ #{$stack-tab-shadow-left-inset}
182
+ ) !default;
183
+ $stack-tab-shadow-inset-right: var(
184
+ --stack-tab-shadow-inset-right,
185
+ #{$stack-tab-shadow-right-inset}
186
+ ) !default;
187
+ $stack-tab-shadow-inset-top: var(
188
+ --stack-tab-shadow-inset-top,
189
+ #{$stack-tab-shadow-top-inset}
190
+ ) !default;
191
+ $stack-tab-shadow-inset-bottom: var(
192
+ --stack-tab-shadow-inset-bottom,
193
+ #{$stack-tab-shadow-bottom-inset}
194
+ ) !default;
195
+ $stack-tab-shadow-inset-all: var(
196
+ --stack-tab-shadow-inset-all,
197
+ #{$stack-tab-shadow-left-inset},
198
+ #{$stack-tab-shadow-right-inset}
199
+ ) !default;
200
+ $stack-tab-shadow-inset-vertical: var(
201
+ --stack-tab-shadow-inset-vertical,
202
+ #{$stack-tab-shadow-top-inset},
203
+ #{$stack-tab-shadow-bottom-inset}
204
+ ) !default;
205
+
206
+ // badge
207
+ $stack-tab-badge-default: var(--stack-tab-badge-default, #{$stack-tab-color-danger}) !default;
208
+ $stack-tab-badge-primary: var(--stack-tab-badge-primary, #{$stack-tab-color-primary}) !default;
209
+
210
+ // loading
211
+ $stack-tab-loading-accent: var(--stack-tab-loading-accent, #{$stack-tab-loading-color}) !default;
@@ -1,3 +1,4 @@
1
+ @use 'common/theme.scss';
1
2
  @use 'stack-tab.scss';
2
3
  @use 'icon.scss';
3
4
  @use 'transition.scss';
@@ -6,7 +6,7 @@
6
6
  &__mask {
7
7
  @include absolute-fill;
8
8
  @include flex-center;
9
- background-color: $stack-tab-loading-mask-color;
9
+ background-color: $stack-tab-overlay;
10
10
  opacity: $stack-tab-loading-mask-opacity;
11
11
  }
12
12
 
@@ -16,7 +16,7 @@
16
16
  width: $stack-tab-loading-size;
17
17
  aspect-ratio: 1;
18
18
  border: $stack-tab-loading-border-width solid transparent;
19
- border-right-color: $stack-tab-loading-color;
19
+ border-right-color: $stack-tab-loading-accent;
20
20
  border-radius: 50%;
21
21
  animation: stack-tab-loading-rotate $stack-tab-loading-duration infinite linear;
22
22
 
@@ -26,7 +26,7 @@
26
26
  position: absolute;
27
27
  inset: -$stack-tab-loading-border-width;
28
28
  border: $stack-tab-loading-border-width solid transparent;
29
- border-right-color: $stack-tab-loading-color;
29
+ border-right-color: $stack-tab-loading-accent;
30
30
  border-radius: 50%;
31
31
  animation: stack-tab-loading-rotate $stack-tab-loading-duration * 2 infinite linear;
32
32
  }
@@ -5,7 +5,7 @@
5
5
  .stack-tab {
6
6
  display: flex;
7
7
  min-height: 300px;
8
- background-color: $stack-tab-background-color-base;
8
+ background-color: $stack-tab-surface;
9
9
  flex-direction: column;
10
10
  gap: 5px;
11
11
 
@@ -41,7 +41,7 @@
41
41
 
42
42
  .stack-tab__header-button[disabled] & {
43
43
  cursor: not-allowed;
44
- background-color: $stack-tab-color-disabled;
44
+ background-color: $stack-tab-text-disabled;
45
45
  transition: none;
46
46
  }
47
47
  }
@@ -57,7 +57,7 @@
57
57
  display: flex;
58
58
  width: 100%;
59
59
  height: $stack-tab-head-height;
60
- border-bottom: 1px solid $stack-tab-border-color;
60
+ border-bottom: 1px solid $stack-tab-border-color-token;
61
61
  box-sizing: border-box;
62
62
 
63
63
  // tab header 上面的按钮
@@ -66,7 +66,7 @@
66
66
  height: $stack-tab-head-height;
67
67
  width: fit-content;
68
68
  overflow: hidden;
69
- background-color: $stack-tab-background-color-base;
69
+ background-color: $stack-tab-surface;
70
70
  cursor: pointer;
71
71
  display: flex;
72
72
  align-items: center;
@@ -76,7 +76,7 @@
76
76
 
77
77
  // 按钮禁用样式
78
78
  &[disabled] {
79
- background-color: $stack-tab-background-color-inactive;
79
+ background-color: $stack-tab-surface-muted;
80
80
  box-shadow: none;
81
81
  cursor: not-allowed;
82
82
  }
@@ -93,12 +93,16 @@
93
93
  &-container {
94
94
  width: 100%;
95
95
  height: 100%;
96
- overflow: hidden;
97
-
98
- // 移动设备原生滚动
99
- &--mobile {
100
- overflow-x: auto;
101
- overflow-y: hidden;
96
+ // 使用原生横向滚动以保留触摸/触控板惯性与手感;
97
+ // 原生滚动条隐藏,视觉上仍由下方自绘滚动条呈现。
98
+ overflow-x: auto;
99
+ overflow-y: hidden;
100
+ overscroll-behavior-x: contain;
101
+ scrollbar-width: none;
102
+ -ms-overflow-style: none;
103
+
104
+ &::-webkit-scrollbar {
105
+ display: none;
102
106
  }
103
107
  }
104
108
  }
@@ -109,7 +113,7 @@
109
113
  bottom: 0;
110
114
  left: 0;
111
115
  height: $stack-tab-scroll-height;
112
- background-color: $stack-tab-color-scrollbar;
116
+ background-color: $stack-tab-scrollbar;
113
117
  border-radius: $stack-tab-scroll-border-radius;
114
118
  opacity: 0;
115
119
  transition: $stack-tab-transition-fast;
@@ -125,9 +129,11 @@
125
129
  top: 0;
126
130
  left: 0;
127
131
  height: 100%;
128
- background-color: $stack-tab-color-scrollbar;
132
+ background-color: $stack-tab-scrollbar;
129
133
  border-radius: $stack-tab-scroll-border-radius;
130
134
  transition: background-color $stack-tab-transition-duration ease-in-out;
135
+ // 拖拽由 Pointer Events 接管,避免触摸时被浏览器手势打断
136
+ touch-action: none;
131
137
 
132
138
  &:hover {
133
139
  cursor: grab;
@@ -139,7 +145,7 @@
139
145
 
140
146
  .stack-tab__scrollbar--dragging &,
141
147
  &:hover {
142
- background-color: $stack-tab-color-primary !important;
148
+ background-color: $stack-tab-accent !important;
143
149
  }
144
150
  }
145
151
 
@@ -160,11 +166,11 @@
160
166
  position: relative;
161
167
  display: flex;
162
168
  padding: 0 $stack-tab-padding;
163
- font-size: $stack-tab-font-size;
164
- color: $stack-tab-text-color;
169
+ font-size: $stack-tab-font-size-token;
170
+ color: $stack-tab-text;
165
171
  cursor: pointer;
166
- background-color: $stack-tab-background-color-inactive;
167
- border: $stack-tab-border;
172
+ background-color: $stack-tab-surface-muted;
173
+ border: $stack-tab-border-token;
168
174
  border-left: none;
169
175
  border-radius: $stack-tab-tab-radius;
170
176
  transition: $stack-tab-transition-fast;
@@ -176,7 +182,7 @@
176
182
  margin-top: 5px;
177
183
 
178
184
  &:first-child {
179
- border-left: $stack-tab-border;
185
+ border-left: $stack-tab-border-token;
180
186
  }
181
187
 
182
188
  &--dragging {
@@ -191,9 +197,9 @@
191
197
  bottom: 2px;
192
198
  width: 5px;
193
199
  content: '';
194
- background: $stack-tab-color-primary;
200
+ background: $stack-tab-accent;
195
201
  border-radius: 2px;
196
- box-shadow: 0 0 6px rgba($stack-tab-color-primary, 0.8);
202
+ box-shadow: $stack-tab-shadow-drop;
197
203
  }
198
204
 
199
205
  &--drop-before::before {
@@ -206,21 +212,21 @@
206
212
 
207
213
  // 激活右键菜单的页签
208
214
  &--context-menu {
209
- color: $stack-tab-color-black;
215
+ color: $stack-tab-text-strong;
210
216
  }
211
217
 
212
218
  // 激活的页签
213
219
  &--active {
214
220
  z-index: 1;
215
- background-color: $stack-tab-background-color-base;
216
- border-bottom-color: $stack-tab-background-color-base;
217
- box-shadow: $stack-tab-shadow-left, $stack-tab-shadow-right;
221
+ background-color: $stack-tab-surface;
222
+ border-bottom-color: $stack-tab-surface;
223
+ box-shadow: $stack-tab-shadow-tab;
218
224
  margin-top: unset;
219
225
  }
220
226
 
221
227
  &:hover,
222
228
  &--active {
223
- color: $stack-tab-color-primary;
229
+ color: $stack-tab-accent;
224
230
  }
225
231
 
226
232
  // 可关闭标签在悬停/激活时给关闭按钮腾出空间
@@ -294,19 +300,19 @@
294
300
  padding: 0.3rem;
295
301
  font-size: 0.5rem;
296
302
  line-height: 0.4rem;
297
- color: $stack-tab-color-white;
303
+ color: $stack-tab-accent-contrast;
298
304
  text-align: center;
299
- border: 1px solid $stack-tab-background-color-base;
305
+ border: 1px solid $stack-tab-surface;
300
306
  border-radius: 0.5rem;
301
307
  transform: translateX(100%) translateY(-50%);
302
308
  box-sizing: border-box;
303
309
 
304
310
  &--default {
305
- background: $stack-tab-color-danger;
311
+ background: $stack-tab-badge-default;
306
312
  }
307
313
 
308
314
  &--primary {
309
- background: $stack-tab-color-primary;
315
+ background: $stack-tab-badge-primary;
310
316
  }
311
317
  }
312
318
  }
@@ -355,11 +361,11 @@
355
361
 
356
362
  &-retry,
357
363
  &-open {
358
- border: $stack-tab-border;
364
+ border: $stack-tab-border-token;
359
365
  padding: 4px 10px;
360
- color: $stack-tab-text-color;
366
+ color: $stack-tab-text;
361
367
  cursor: pointer;
362
- background: $stack-tab-background-color-base;
368
+ background: $stack-tab-surface;
363
369
  border-radius: 6px;
364
370
  font: inherit;
365
371
  }
@@ -371,11 +377,11 @@
371
377
  z-index: 999;
372
378
  min-width: 120px;
373
379
  padding: 8px;
374
- font-size: $stack-tab-font-size;
375
- background: $stack-tab-background-color-base;
376
- border: $stack-tab-border;
380
+ font-size: $stack-tab-font-size-token;
381
+ background: $stack-tab-surface;
382
+ border: $stack-tab-border-token;
377
383
  border-radius: $stack-tab-contextmenu-radius;
378
- box-shadow: $stack-tab-shadow-context-menu;
384
+ box-shadow: $stack-tab-shadow-menu;
379
385
  transform-origin: left top;
380
386
 
381
387
  &-item {
@@ -390,7 +396,7 @@
390
396
  padding: 0 10px;
391
397
  line-height: 30px;
392
398
  box-sizing: border-box;
393
- color: $stack-tab-text-color;
399
+ color: $stack-tab-text;
394
400
  cursor: pointer;
395
401
  background: transparent;
396
402
  border-radius: $stack-tab-contextmenu-radius;
@@ -403,11 +409,11 @@
403
409
  &:hover,
404
410
  &:active,
405
411
  &:focus-visible {
406
- color: $stack-tab-color-white;
407
- background-color: $stack-tab-color-primary;
412
+ color: $stack-tab-accent-contrast;
413
+ background-color: $stack-tab-accent;
408
414
 
409
415
  .stack-tab__icon--mask {
410
- background-color: $stack-tab-color-white;
416
+ background-color: $stack-tab-accent-contrast;
411
417
  }
412
418
  }
413
419
 
@@ -416,62 +422,62 @@
416
422
  }
417
423
 
418
424
  &[disabled] {
419
- color: $stack-tab-color-disabled;
425
+ color: $stack-tab-text-disabled;
420
426
  pointer-events: none;
421
427
  cursor: default;
422
428
  background: none;
423
429
 
424
430
  .stack-tab__icon--mask {
425
- background-color: $stack-tab-color-disabled;
431
+ background-color: $stack-tab-text-disabled;
426
432
  }
427
433
  }
428
434
  }
429
435
 
430
436
  &-icon {
431
- width: $stack-tab-font-size;
432
- height: $stack-tab-font-size;
437
+ width: $stack-tab-font-size-token;
438
+ height: $stack-tab-font-size-token;
433
439
  transition: background-color 0.2s ease-in-out;
434
440
 
435
441
  &.stack-tab__icon--mask {
436
- width: $stack-tab-font-size;
437
- height: $stack-tab-font-size;
438
- background-color: $stack-tab-text-color;
442
+ width: $stack-tab-font-size-token;
443
+ height: $stack-tab-font-size-token;
444
+ background-color: $stack-tab-icon-mask;
439
445
  }
440
446
  }
441
447
  }
442
448
  }
443
449
 
444
450
  .stack-tab__shadow-left {
445
- box-shadow: $stack-tab-shadow-left;
451
+ box-shadow: $stack-tab-shadow-tab-left;
446
452
  }
447
453
 
448
454
  .stack-tab__shadow-right {
449
- box-shadow: $stack-tab-shadow-right;
455
+ box-shadow: $stack-tab-shadow-tab-right;
450
456
  }
451
457
 
452
458
  // 内阴影
453
459
  .stack-tab__shadow-inset {
454
460
  &-left {
455
- @include inset-shadow($stack-tab-shadow-left-inset);
461
+ @include inset-shadow($stack-tab-shadow-inset-left);
456
462
  }
457
463
 
458
464
  &-right {
459
- @include inset-shadow($stack-tab-shadow-right-inset);
465
+ @include inset-shadow($stack-tab-shadow-inset-right);
460
466
  }
461
467
 
462
468
  &-all {
463
- @include inset-shadow($stack-tab-shadow-left-inset, $stack-tab-shadow-right-inset);
469
+ @include inset-shadow($stack-tab-shadow-inset-all);
464
470
  }
465
471
 
466
472
  &-top {
467
- @include inset-shadow($stack-tab-shadow-top-inset);
473
+ @include inset-shadow($stack-tab-shadow-inset-top);
468
474
  }
469
475
 
470
476
  &-bottom {
471
- @include inset-shadow($stack-tab-shadow-bottom-inset);
477
+ @include inset-shadow($stack-tab-shadow-inset-bottom);
472
478
  }
473
479
 
474
480
  &-vertical {
475
- @include inset-shadow($stack-tab-shadow-top-inset, $stack-tab-shadow-bottom-inset);
481
+ @include inset-shadow($stack-tab-shadow-inset-vertical);
476
482
  }
477
483
  }
@@ -218,6 +218,8 @@ onMounted(() => {
218
218
  </script>
219
219
 
220
220
  <style lang="scss" scoped>
221
+ @use '../../assets/style/common/var' as *;
222
+
221
223
  .divider {
222
224
  position: relative;
223
225
  height: 1px;
@@ -231,7 +233,7 @@ onMounted(() => {
231
233
  left: 0;
232
234
  width: 100%;
233
235
  height: 1px;
234
- background-image: linear-gradient(to right, transparent, darkgrey, transparent);
236
+ background-image: linear-gradient(to right, transparent, $stack-tab-divider, transparent);
235
237
  content: '';
236
238
  }
237
239
  </style>
@@ -11,24 +11,19 @@
11
11
  role="status"
12
12
  aria-live="polite"
13
13
  :aria-label="t('VueStackTab.loading')"
14
- :style="{ zIndex: loadingZIndex() }"
14
+ :style="{ zIndex: loadingZIndex }"
15
15
  >
16
16
  <div class="stack-tab-loading__spin" aria-hidden="true" />
17
17
  </div>
18
18
  </template>
19
19
  <script setup lang="ts">
20
- import { ref, onUnmounted } from 'vue'
20
+ import { ref, onUnmounted, watch } from 'vue'
21
21
  import { useI18n } from 'vue-i18n-lite'
22
22
  import { TabEventType, useTabEmitter } from '../hooks/useTabEventBus'
23
23
  import { getMaxZIndex, invalidateZIndexCache } from '../utils/scrollUtils'
24
24
 
25
- /**
26
- * loading 遮罩每次显隐都会改变 .stack-tab__cache-page-wrapper 子树层叠,先失效缓存再读取避免旧值。
27
- */
28
- const loadingZIndex = () => {
29
- invalidateZIndexCache('.stack-tab__cache-page-wrapper *')
30
- return getMaxZIndex('.stack-tab__cache-page-wrapper *')
31
- }
25
+ /** 遮罩显示时的层叠值;只在显示时计算一次,避免每次重渲染都全量扫描 DOM。 */
26
+ const loadingZIndex = ref(0)
32
27
  const props = defineProps<{
33
28
  /** 当前标签 id,用于过滤 PAGE_LOADING 事件 */
34
29
  tabId: string
@@ -37,6 +32,12 @@ const { t } = useI18n()
37
32
  const emitter = useTabEmitter()
38
33
  /** 是否显示 loading 遮罩 */
39
34
  const isLoading = ref<boolean>(false)
35
+ watch(isLoading, (loading) => {
36
+ if (!loading) return
37
+ // 遮罩显隐会改变 .stack-tab__cache-page-wrapper 子树层叠,先失效缓存再读取避免旧值。
38
+ invalidateZIndexCache('.stack-tab__cache-page-wrapper *')
39
+ loadingZIndex.value = getMaxZIndex('.stack-tab__cache-page-wrapper *')
40
+ })
40
41
  /** 处理 PAGE_LOADING 事件,仅当 tId 匹配时更新 */
41
42
  const handleLoadingEvent = (payload: { tId: string; value: boolean }) => {
42
43
  if (payload.tId === props.tabId) {
@@ -25,8 +25,6 @@ interface StackCacheRendererProps {
25
25
  interface StackCacheRendererSlotProps {
26
26
  wrappedComponent: DefineComponent
27
27
  activeCacheKey: string
28
- refreshKey: number
29
- activePageRefreshVersion: number
30
28
  component?: VNode | null
31
29
  }
32
30
 
@@ -36,7 +34,7 @@ defineSlots<{
36
34
  default(props: StackCacheRendererSlotProps): unknown
37
35
  }>()
38
36
 
39
- const { refreshKey, activeCacheKey, activePageRefreshVersion, addPage } = useTabPanel()
37
+ const { refreshKey, activeCacheKey, addPage } = useTabPanel()
40
38
 
41
39
  const wrappedComponent = shallowRef<DefineComponent>(getEmptyPlaceholderComponent())
42
40
 
@@ -57,8 +55,6 @@ watch(
57
55
  <slot
58
56
  :wrapped-component="wrappedComponent"
59
57
  :active-cache-key="activeCacheKey"
60
- :refresh-key="refreshKey"
61
- :active-page-refresh-version="activePageRefreshVersion"
62
58
  :component="component"
63
59
  />
64
60
  </template>