@veltra/desktop 1.0.8 → 1.0.9

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 (183) hide show
  1. package/dist/components/action/action-group.vue.d.ts +2 -2
  2. package/dist/components/action/action.vue.d.ts +3 -3
  3. package/dist/components/auto-complete/auto-complete.vue.d.ts +1 -1
  4. package/dist/components/batch-edit/style.js +2 -2
  5. package/dist/components/button/button.vue.d.ts +1 -1
  6. package/dist/components/cascade/cascade.js +15 -13
  7. package/dist/components/cascade/cascade.js.map +1 -1
  8. package/dist/components/cascade/cascade.vue.d.ts +4 -4
  9. package/dist/components/cascade/node.js.map +1 -1
  10. package/dist/components/cascade/use-check.js +3 -2
  11. package/dist/components/cascade/use-check.js.map +1 -1
  12. package/dist/components/cascade/use-select.js +3 -3
  13. package/dist/components/cascade/use-select.js.map +1 -1
  14. package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
  15. package/dist/components/date-picker/date-picker.js +9 -9
  16. package/dist/components/date-picker/date-picker.js.map +1 -1
  17. package/dist/components/date-picker/date-picker.vue.d.ts +1 -1
  18. package/dist/components/date-range-picker/date-range-picker.js +9 -9
  19. package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
  20. package/dist/components/date-range-picker/date-range-picker.vue.d.ts +1 -1
  21. package/dist/components/empty/empty.vue.d.ts +1 -1
  22. package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
  23. package/dist/components/file-picker/helper.js +19 -7
  24. package/dist/components/file-picker/helper.js.map +1 -1
  25. package/dist/components/file-viewer/file-viewer.js +294 -0
  26. package/dist/components/file-viewer/file-viewer.js.map +1 -0
  27. package/dist/components/file-viewer/file-viewer.vue.d.ts +42 -0
  28. package/dist/components/file-viewer/helper.js +185 -0
  29. package/dist/components/file-viewer/helper.js.map +1 -0
  30. package/dist/components/file-viewer/index.d.ts +1 -0
  31. package/dist/components/file-viewer/previewers/docx-previewer.js +76 -0
  32. package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -0
  33. package/dist/components/file-viewer/previewers/image-previewer.js +64 -0
  34. package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -0
  35. package/dist/components/file-viewer/previewers/pdf-previewer.js +90 -0
  36. package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -0
  37. package/dist/components/file-viewer/previewers/sheet-previewer.js +175 -0
  38. package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -0
  39. package/dist/components/file-viewer/previewers/text-previewer.js +74 -0
  40. package/dist/components/file-viewer/previewers/text-previewer.js.map +1 -0
  41. package/dist/components/file-viewer/previewers/video-previewer.js +62 -0
  42. package/dist/components/file-viewer/previewers/video-previewer.js.map +1 -0
  43. package/dist/components/file-viewer/style.d.ts +1 -0
  44. package/dist/components/file-viewer/style.js +4 -0
  45. package/dist/components/file-viewer/style2.css +445 -0
  46. package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
  47. package/dist/components/index.d.ts +12 -9
  48. package/dist/components/input/input.vue.d.ts +1 -1
  49. package/dist/components/multi-select/multi-select-option.js +14 -10
  50. package/dist/components/multi-select/multi-select-option.js.map +1 -1
  51. package/dist/components/multi-select/multi-select.js +16 -19
  52. package/dist/components/multi-select/multi-select.js.map +1 -1
  53. package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
  54. package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +4 -4
  55. package/dist/components/notification/notification.vue.d.ts +1 -1
  56. package/dist/components/number-input/number-input.vue.d.ts +1 -1
  57. package/dist/components/number-range-input/number-range-input.vue.d.ts +2 -2
  58. package/dist/components/palette/di.js.map +1 -1
  59. package/dist/components/palette/palette-alpha.js +7 -10
  60. package/dist/components/palette/palette-alpha.js.map +1 -1
  61. package/dist/components/palette/palette-hue.js +5 -7
  62. package/dist/components/palette/palette-hue.js.map +1 -1
  63. package/dist/components/palette/palette-sv.js +8 -10
  64. package/dist/components/palette/palette-sv.js.map +1 -1
  65. package/dist/components/palette/palette.js +22 -24
  66. package/dist/components/palette/palette.js.map +1 -1
  67. package/dist/components/password-input/password-input.vue.d.ts +1 -1
  68. package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
  69. package/dist/components/progress-nodes/progress-nodes.vue.d.ts +3 -3
  70. package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +2 -2
  71. package/dist/components/select/select.js +14 -12
  72. package/dist/components/select/select.js.map +1 -1
  73. package/dist/components/select/select.vue.d.ts +2 -2
  74. package/dist/components/slider/slider-thumb.js +10 -13
  75. package/dist/components/slider/slider-thumb.js.map +1 -1
  76. package/dist/components/slider/slider.js +12 -15
  77. package/dist/components/slider/slider.js.map +1 -1
  78. package/dist/components/table/di.js.map +1 -1
  79. package/dist/components/table/node/row.d.ts +10 -1
  80. package/dist/components/table/node/row.js +9 -0
  81. package/dist/components/table/node/row.js.map +1 -1
  82. package/dist/components/table/style.js +1 -1
  83. package/dist/components/table/table-body.js +25 -26
  84. package/dist/components/table/table-body.js.map +1 -1
  85. package/dist/components/table/table-cell.js +16 -9
  86. package/dist/components/table/table-cell.js.map +1 -1
  87. package/dist/components/table/table-row.js +78 -19
  88. package/dist/components/table/table-row.js.map +1 -1
  89. package/dist/components/table/table.js +76 -34
  90. package/dist/components/table/table.js.map +1 -1
  91. package/dist/components/table/use-fixed-columns.js +27 -8
  92. package/dist/components/table/use-fixed-columns.js.map +1 -1
  93. package/dist/components/table/use-rows.js +2 -2
  94. package/dist/components/table/use-rows.js.map +1 -1
  95. package/dist/components/table/use-table.js +25 -3
  96. package/dist/components/table/use-table.js.map +1 -1
  97. package/dist/components/tabs/index.d.ts +3 -1
  98. package/dist/components/tabs/style2.css +169 -107
  99. package/dist/components/tabs/tabs-horizontal.js +133 -0
  100. package/dist/components/tabs/tabs-horizontal.js.map +1 -0
  101. package/dist/components/tabs/tabs-horizontal.vue.d.ts +29 -0
  102. package/dist/components/tabs/tabs-vertical.js +89 -0
  103. package/dist/components/tabs/tabs-vertical.js.map +1 -0
  104. package/dist/components/tabs/tabs-vertical.vue.d.ts +28 -0
  105. package/dist/components/tabs/tabs.js +75 -105
  106. package/dist/components/tabs/tabs.js.map +1 -1
  107. package/dist/components/tabs/tabs.vue.d.ts +9 -23
  108. package/dist/components/tabs/use-tabs-bar.js +105 -0
  109. package/dist/components/tabs/use-tabs-bar.js.map +1 -0
  110. package/dist/components/textarea/textarea.vue.d.ts +1 -1
  111. package/dist/components/tip/tip.vue.d.ts +2 -2
  112. package/dist/components/tree/style2.css +45 -23
  113. package/dist/components/tree/tree-node.js +50 -35
  114. package/dist/components/tree/tree-node.js.map +1 -1
  115. package/dist/components/tree/tree.js +27 -20
  116. package/dist/components/tree/tree.js.map +1 -1
  117. package/dist/components/tree/tree.vue.d.ts +1 -1
  118. package/dist/components/tree/use-check.js +52 -14
  119. package/dist/components/tree/use-check.js.map +1 -1
  120. package/dist/components/tree/use-tree-nodes.js +51 -24
  121. package/dist/components/tree/use-tree-nodes.js.map +1 -1
  122. package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
  123. package/dist/components/watermark/watermark.vue.d.ts +1 -1
  124. package/dist/index.d.ts +57 -53
  125. package/dist/index.js +51 -48
  126. package/dist/types/file-viewer.d.ts +71 -0
  127. package/dist/types/index.d.ts +3 -2
  128. package/dist/types/tabs.d.ts +106 -14
  129. package/dist/types/tree.d.ts +6 -1
  130. package/package.json +32 -31
  131. package/src/components/cascade/cascade.vue +18 -18
  132. package/src/components/cascade/node.ts +4 -1
  133. package/src/components/cascade/use-check.ts +5 -4
  134. package/src/components/cascade/use-select.ts +4 -5
  135. package/src/components/date-picker/date-picker.vue +10 -9
  136. package/src/components/date-range-picker/date-range-picker.vue +12 -12
  137. package/src/components/file-picker/helper.ts +21 -10
  138. package/src/components/file-viewer/file-viewer.vue +311 -0
  139. package/src/components/file-viewer/helper.ts +193 -0
  140. package/src/components/file-viewer/index.ts +1 -0
  141. package/src/components/file-viewer/previewers/docx-previewer.vue +78 -0
  142. package/src/components/file-viewer/previewers/image-previewer.vue +64 -0
  143. package/src/components/file-viewer/previewers/pdf-previewer.vue +90 -0
  144. package/src/components/file-viewer/previewers/sheet-previewer.vue +210 -0
  145. package/src/components/file-viewer/previewers/text-previewer.vue +76 -0
  146. package/src/components/file-viewer/previewers/video-previewer.vue +72 -0
  147. package/src/components/file-viewer/style.scss +504 -0
  148. package/src/components/file-viewer/style.ts +4 -0
  149. package/src/components/index.ts +2 -0
  150. package/src/components/multi-select/multi-select-option.vue +16 -3
  151. package/src/components/multi-select/multi-select.vue +17 -9
  152. package/src/components/palette/di.ts +5 -3
  153. package/src/components/palette/palette-alpha.vue +7 -10
  154. package/src/components/palette/palette-hue.vue +5 -7
  155. package/src/components/palette/palette-sv.vue +12 -14
  156. package/src/components/palette/palette.vue +19 -21
  157. package/src/components/select/select.vue +21 -19
  158. package/src/components/slider/slider-thumb.vue +11 -14
  159. package/src/components/slider/slider.vue +15 -15
  160. package/src/components/table/di.ts +20 -3
  161. package/src/components/table/node/row.ts +10 -1
  162. package/src/components/table/table-body.vue +28 -33
  163. package/src/components/table/table-cell.vue +17 -9
  164. package/src/components/table/table-row.tsx +83 -17
  165. package/src/components/table/table.vue +73 -45
  166. package/src/components/table/use-fixed-columns.ts +46 -12
  167. package/src/components/table/use-rows.ts +14 -3
  168. package/src/components/table/use-table.ts +27 -3
  169. package/src/components/tabs/index.ts +2 -0
  170. package/src/components/tabs/style.scss +217 -134
  171. package/src/components/tabs/tabs-horizontal.vue +125 -0
  172. package/src/components/tabs/tabs-vertical.vue +75 -0
  173. package/src/components/tabs/tabs.vue +56 -127
  174. package/src/components/tabs/use-tabs-bar.ts +119 -0
  175. package/src/components/tree/style.scss +74 -29
  176. package/src/components/tree/tree-node.vue +10 -4
  177. package/src/components/tree/tree.vue +39 -12
  178. package/src/components/tree/use-check.ts +79 -23
  179. package/src/components/tree/use-tree-nodes.ts +85 -34
  180. package/src/types/file-viewer.ts +69 -0
  181. package/src/types/index.ts +1 -0
  182. package/src/types/tabs.ts +113 -19
  183. package/src/types/tree.ts +6 -1
@@ -24,7 +24,7 @@ defineOptions({
24
24
  name: 'PaletteSV'
25
25
  })
26
26
 
27
- const { cls, hueRGB, HSV, updateSV, updater } = inject(PaletteDIKey)!
27
+ const { cls, hueRGB, HSV, updateSV, userAction } = inject(PaletteDIKey)!
28
28
 
29
29
  const svRef = shallowRef<HTMLDivElement>()
30
30
 
@@ -58,21 +58,19 @@ const svThumbStyle = computed(() => {
58
58
  }
59
59
  })
60
60
 
61
- function updateThumb(offsetX: number, offsetY: number) {
62
- updater.updateAndLock(() => {
63
- transform.x = offsetX
64
- transform.y = offsetY
61
+ const updateThumb = userAction((offsetX: number, offsetY: number) => {
62
+ transform.x = offsetX
63
+ transform.y = offsetY
65
64
 
66
- // 根据画布位置计算饱和度和亮度
67
- // 水平方向表示饱和度,从左到右饱和度逐渐增高
68
- // 垂直方向表示亮度,上亮下暗
69
- const s = Math.max(0, Math.min(1, offsetX / canvasSize.width))
70
- const v = Math.max(0, Math.min(1, 1 - offsetY / canvasSize.height))
65
+ // 根据画布位置计算饱和度和亮度
66
+ // 水平方向表示饱和度,从左到右饱和度逐渐增高
67
+ // 垂直方向表示亮度,上亮下暗
68
+ const s = Math.max(0, Math.min(1, offsetX / canvasSize.width))
69
+ const v = Math.max(0, Math.min(1, 1 - offsetY / canvasSize.height))
71
70
 
72
- // 更新饱和度和亮度
73
- updateSV({ s, v })
74
- })
75
- }
71
+ // 更新饱和度和亮度
72
+ updateSV({ s, v })
73
+ })
76
74
 
77
75
  const svDragger = useDrag({
78
76
  target: svRef,
@@ -24,7 +24,7 @@
24
24
  </template>
25
25
 
26
26
  <script lang="ts" setup>
27
- import { useFormComponent, useFormFallbackProps, useUpdateLock } from '@veltra/compositions'
27
+ import { useFormComponent, useFormFallbackProps, useUserAction } from '@veltra/compositions'
28
28
  import { bem } from '@veltra/utils'
29
29
  import { computed, provide, shallowRef, useTemplateRef, watch } from 'vue'
30
30
 
@@ -61,7 +61,7 @@ const { HSV, alpha, ...rest } = useHSV()
61
61
 
62
62
  const color = defineModel<string>()
63
63
  const RGB = computed(() => HSV2RGB(HSV))
64
- const updater = useUpdateLock()
64
+ const { userAction, isUserActive } = useUserAction()
65
65
 
66
66
  watch([alpha, RGB], ([alpha, RGB]) => {
67
67
  color.value = `#${RGB2HEX(RGB, alpha)}`
@@ -69,13 +69,11 @@ watch([alpha, RGB], ([alpha, RGB]) => {
69
69
 
70
70
  const visible = shallowRef(false)
71
71
 
72
- function handleClear() {
73
- updater.updateAndLock(() => {
74
- color.value = ''
75
- rest.updateAlpha(1)
76
- visible.value = false
77
- })
78
- }
72
+ const handleClear = userAction(() => {
73
+ color.value = ''
74
+ rest.updateAlpha(1)
75
+ visible.value = false
76
+ })
79
77
 
80
78
  const paletteSVRef = useTemplateRef('palette-sv')
81
79
  const paletteHueRef = useTemplateRef('palette-hue')
@@ -85,18 +83,17 @@ watch(
85
83
  color,
86
84
  (color) => {
87
85
  if (!color) return
86
+ if (isUserActive()) return
87
+
88
+ const { RGB, alpha } = HEX2RGBA(color)
89
+ const hsv = RGB2HSV(RGB)
90
+ rest.updateHue(hsv.h)
91
+ rest.updateSV({ s: hsv.s, v: hsv.v })
92
+ rest.updateAlpha(alpha)
88
93
 
89
- updater.update(() => {
90
- const { RGB, alpha } = HEX2RGBA(color)
91
- const hsv = RGB2HSV(RGB)
92
- rest.updateHue(hsv.h)
93
- rest.updateSV({ s: hsv.s, v: hsv.v })
94
- rest.updateAlpha(alpha)
95
-
96
- paletteSVRef.value?.init()
97
- paletteHueRef.value?.init()
98
- paletteAlphaRef.value?.init()
99
- })
94
+ paletteSVRef.value?.init()
95
+ paletteHueRef.value?.init()
96
+ paletteAlphaRef.value?.init()
100
97
  },
101
98
  { immediate: true }
102
99
  )
@@ -106,7 +103,8 @@ provide(PaletteDIKey, {
106
103
  HSV,
107
104
  RGB,
108
105
  alpha,
109
- updater,
106
+ userAction,
107
+ isUserActive,
110
108
  ...rest
111
109
  })
112
110
  </script>
@@ -50,7 +50,10 @@
50
50
  </u-input>
51
51
  </div>
52
52
 
53
- <!-- 单选列表 -->
53
+ <!--
54
+ 单选列表。虚拟化启用时,内容容器 height 由 useVirtualizer 命令式写入;
55
+ 此处 content-style 仅承担 grid 相关样式,避免 height 变化触发模板重渲染。
56
+ -->
54
57
  <u-scroll
55
58
  v-if="options.length"
56
59
  tag="ul"
@@ -62,7 +65,6 @@
62
65
  bem.is('grid', !!grid)
63
66
  ]"
64
67
  :content-style="{
65
- height: virtualEnabled ? withUnit(totalHeight, 'px') : undefined,
66
68
  gridTemplateColumns: grid ? `repeat(${grid.cols}, minmax(0px, 1fr))` : undefined,
67
69
  gridGap: grid ? withUnit(grid.gap, 'px') : undefined
68
70
  }"
@@ -76,7 +78,7 @@
76
78
  :key="key"
77
79
  :style="{ transform: `translateY(${offset}px)` }"
78
80
  :data-index="index"
79
- :ref="measureElement"
81
+ :ref="(el) => measureElement(index, el as Element | null)"
80
82
  >
81
83
  <slot v-bind="{ option, index }">
82
84
  {{ label }}
@@ -113,7 +115,7 @@
113
115
 
114
116
  <script lang="ts" setup>
115
117
  import { o } from '@cat-kit/core'
116
- import { useFormComponent, useFormFallbackProps, useVirtual } from '@veltra/compositions'
118
+ import { useFormComponent, useFormFallbackProps, useVirtualizer } from '@veltra/compositions'
117
119
  import { vFocus } from '@veltra/directives'
118
120
  import { ArrowDown, Search } from '@veltra/icons/normal'
119
121
  import { bem, FORM_EMPTY_CONTENT, scrollIntoContainerView, withUnit } from '@veltra/utils'
@@ -212,40 +214,40 @@ watch(
212
214
  { immediate: true }
213
215
  )
214
216
 
215
- const {
216
- virtualList,
217
- totalHeight,
218
- virtualEnabled: _virtualEnabled,
219
- scrollTo,
220
- measureElement
221
- } = useVirtual({
217
+ const baseVirtualEnabled = computed(() => options.value.length > 80)
218
+ const virtualEnabled = computed(() => baseVirtualEnabled.value && !props.grid)
219
+
220
+ const { virtualizer, items } = useVirtualizer({
222
221
  count: computed(() => options.value.length),
223
- virtualThreshold: 80,
224
- scrollEl: computed(() => scrollRef.value?.containerRef ?? null),
222
+ scrollEl: () => scrollRef.value?.containerRef ?? null,
223
+ contentEl: () => (virtualEnabled.value ? (scrollRef.value?.contentRef ?? null) : null),
225
224
  gap: 4,
226
225
  estimateSize: () => 40
227
226
  })
228
227
 
229
- const virtualEnabled = computed(() => {
230
- return _virtualEnabled.value && !props.grid
231
- })
232
-
233
228
  const virtualOptions = computed(() => {
234
229
  const _options = options.value
235
230
  const { labelKey, valueKey } = props
236
- return virtualList.value.map((item) => {
231
+ return items.value.map((item) => {
237
232
  const option = _options[item.index]!
238
233
  return {
239
234
  option,
240
235
  index: item.index,
241
236
  label: o(option).get(labelKey),
242
237
  val: o(option).get(valueKey),
243
- key: item.key,
238
+ key: item.index,
244
239
  offset: item.start
245
240
  }
246
241
  })
247
242
  })
248
243
 
244
+ const measureElement: (index: number, el: Element | null) => void = (index, el) =>
245
+ virtualizer.measureElement(index, el)
246
+
247
+ function scrollTo(index: number): void {
248
+ virtualizer.scrollToIndex(index, { align: 'center' })
249
+ }
250
+
249
251
  watch([scrollRef, virtualEnabled], ([scroll, virtualEnabled]) => {
250
252
  if (!scroll || !props.modelValue) return
251
253
 
@@ -2,7 +2,7 @@
2
2
  <div ref="thumb" :class="cls.e('thumb')" :style></div>
3
3
  </template>
4
4
  <script lang="ts" setup>
5
- import { useDrag, useUpdateLock } from '@veltra/compositions'
5
+ import { useDrag, useUserAction } from '@veltra/compositions'
6
6
  import { computed, inject, useTemplateRef, watch } from 'vue'
7
7
 
8
8
  import { sliderContextKey } from './di'
@@ -30,7 +30,7 @@ const style = computed(() => {
30
30
  }
31
31
  })
32
32
 
33
- const { updateAndLock, update } = useUpdateLock()
33
+ const { userAction, isUserActive } = useUserAction()
34
34
 
35
35
  function updateModel(offset: number) {
36
36
  offset = getOffsetByStep(offset)
@@ -41,19 +41,15 @@ function updateModel(offset: number) {
41
41
  const dragger = useDrag({
42
42
  target: computed(() => (disabled.value ? null : thumbRef.value)),
43
43
 
44
- onDrag({ offsetX, offsetY }) {
45
- updateAndLock(() => {
46
- updateModel(sliderProps.vertical ? offsetY : offsetX)
47
- })
48
- },
44
+ onDrag: userAction(({ offsetX, offsetY }) => {
45
+ updateModel(sliderProps.vertical ? offsetY : offsetX)
46
+ }),
49
47
  rangeX: range,
50
48
  rangeY: range,
51
- onDragEnd({ offsetX, offsetY }) {
52
- updateAndLock(() => {
53
- const offset = updateModel(sliderProps.vertical ? offsetY : offsetX)
54
- updateDragger(offset)
55
- })
56
- }
49
+ onDragEnd: userAction(({ offsetX, offsetY }) => {
50
+ const offset = updateModel(sliderProps.vertical ? offsetY : offsetX)
51
+ updateDragger(offset)
52
+ })
57
53
  })
58
54
 
59
55
  function updateDragger(offset: number) {
@@ -67,7 +63,8 @@ function updateDragger(offset: number) {
67
63
  watch(
68
64
  () => props.modelValue,
69
65
  (offset) => {
70
- update(() => updateDragger(offset))
66
+ if (isUserActive()) return
67
+ updateDragger(offset)
71
68
  }
72
69
  )
73
70
  </script>
@@ -20,7 +20,7 @@
20
20
  </template>
21
21
 
22
22
  <script lang="ts" setup generic="T extends number | [number, number]">
23
- import { useFormComponent, useFormFallbackProps, useUpdateLock } from '@veltra/compositions'
23
+ import { useFormComponent, useFormFallbackProps, useUserAction } from '@veltra/compositions'
24
24
  import { bem } from '@veltra/utils'
25
25
  import { computed, provide, watch } from 'vue'
26
26
 
@@ -87,21 +87,20 @@ const barStyles = computed(() => {
87
87
  }
88
88
  })
89
89
 
90
- const { updateAndLock, update } = useUpdateLock()
90
+ const { userAction, isUserActive } = useUserAction()
91
91
 
92
92
  watch(
93
93
  [sliderSize, ...['modelValue', 'range', 'max', 'min', 'vertical'].map((k) => () => props[k])],
94
94
  ([size, value, range]) => {
95
- update(() => {
96
- if (value === undefined || size === 0) return
97
-
98
- if (range) {
99
- offset1.value = value2SliderOffset((value as [number, number])[0])
100
- offset2.value = value2SliderOffset((value as [number, number])[1])
101
- } else {
102
- offset1.value = value2SliderOffset(value as number)
103
- }
104
- })
95
+ if (isUserActive()) return
96
+ if (value === undefined || size === 0) return
97
+
98
+ if (range) {
99
+ offset1.value = value2SliderOffset((value as [number, number])[0])
100
+ offset2.value = value2SliderOffset((value as [number, number])[1])
101
+ } else {
102
+ offset1.value = value2SliderOffset(value as number)
103
+ }
105
104
  },
106
105
  { immediate: true }
107
106
  )
@@ -115,15 +114,16 @@ const ticks = computed(() => {
115
114
  return [...Array.from({ length: tickLength }, (_, i) => i * offsetPercent), 100]
116
115
  })
117
116
 
118
- watch([offset1, offset2], (v) => {
119
- updateAndLock(() => {
117
+ watch(
118
+ [offset1, offset2],
119
+ userAction((v) => {
120
120
  if (props.range) {
121
121
  emit('update:modelValue', v.map(sliderOffset2Value).sort() as T)
122
122
  } else {
123
123
  emit('update:modelValue', sliderOffset2Value(v[0]) as T)
124
124
  }
125
125
  })
126
- })
126
+ )
127
127
 
128
128
  function handleClickSlider(e: MouseEvent) {
129
129
  if (disabled.value || props.range) return
@@ -1,6 +1,6 @@
1
- import type { VirtualReturned } from '@veltra/compositions'
1
+ import type { VirtualItem } from '@veltra/compositions'
2
2
  import type { BEM } from '@veltra/utils'
3
- import type { InjectionKey, ShallowRef, Slots } from 'vue'
3
+ import type { ComputedRef, InjectionKey, ShallowRef, Slots } from 'vue'
4
4
 
5
5
  import type {
6
6
  TableProps,
@@ -53,7 +53,24 @@ export const TableDIKey: InjectionKey<
53
53
  row: TableRow,
54
54
  column: ColumnNode
55
55
  ) => TableColumnSlotsScope | TableColumnRenderContext
56
- } & VirtualReturned
56
+
57
+ /**
58
+ * 当前渲染窗口的虚拟项列表。
59
+ *
60
+ * 由 `useVirtualizer` 拆分出的 `items` shallowRef:仅在底层 `items`
61
+ * 引用变化时才更新,`isScrolling` / 纯尺寸变化不会导致此 ref 更新。
62
+ */
63
+ virtualList: ShallowRef<VirtualItem[]>
64
+ /** 是否启用虚拟列表 */
65
+ virtualEnabled: ComputedRef<boolean>
66
+ /**
67
+ * 测量行元素尺寸(参数顺序与底层 `Virtualizer.measureElement` 一致)。
68
+ *
69
+ * 元素卸载(`el === null`)时会从底层解绑,避免被移除的 `<tr>` 继续触发
70
+ * size=0 测量回调。
71
+ */
72
+ measureElement: (index: number, el: Element | null) => void
73
+ }
57
74
  > = Symbol('TableDIKey')
58
75
 
59
76
  export const TableResizeKey: InjectionKey<{
@@ -28,7 +28,16 @@ export class TableRowNode<Data extends Record<string, any> = Record<string, any>
28
28
  /** 是否选中 */
29
29
  checked = false
30
30
 
31
- uid: number | string
31
+ /**
32
+ * 行的稳定唯一标识。
33
+ *
34
+ * E2 不变式:`uid` 在单个 `TableRowNode` 实例的生命周期内不可变更。
35
+ * `useVirtualizer` 的 `getItemKey: i => rows[i].uid` 依赖该不变式来复用测量缓存;
36
+ * 若 uid 在运行时变更,虚拟列表的尺寸表会错配导致滚动抖动。
37
+ * 当前代码路径(`createRow` / `copy`)始终为每个新节点分配一次 uid,
38
+ * 所以该不变式天然成立;此处使用 `readonly` 做编译期锁定。
39
+ */
40
+ readonly uid: number | string
32
41
 
33
42
  override children?: TableRowNode<Data>[] = undefined
34
43
 
@@ -1,20 +1,15 @@
1
1
  <template>
2
- <tbody :class="cls.e('body')" ref="bodyRef">
2
+ <tbody :class="bodyCls">
3
3
  <template v-for="{ row, index, stripeIndex } of tableRows" :key="row.uid">
4
- <UExpandTableRow v-if="row.isExpandRow" :row="row" :data-index="index" />
5
- <UTableRow
6
- v-else
7
- :row="row"
8
- :data-index="index"
9
- :class="[bem.is('current', row.isCurrent), getStripeCls(stripeIndex)]"
10
- />
4
+ <UExpandTableRow v-if="row.isExpandRow" :row="row" :index="index" />
5
+ <UTableRow v-else :row="row" :index="index" :class="getStripeCls(stripeIndex)" />
11
6
  </template>
12
7
 
13
8
  <!-- 空 -->
14
- <tr v-if="!rows.length" :class="cls.e('row')">
9
+ <tr v-if="!rows.length" :class="emptyRowCls">
15
10
  <td :colspan="leafColumns.length">
16
11
  <slot name="empty">
17
- <UEmpty :class="cls.e('empty')" />
12
+ <UEmpty :class="emptyCls" />
18
13
  </slot>
19
14
  </td>
20
15
  </tr>
@@ -24,8 +19,8 @@
24
19
  </template>
25
20
 
26
21
  <script lang="ts" setup>
27
- import { bem, setStyles } from '@veltra/utils'
28
- import { computed, inject, shallowRef, watch } from 'vue'
22
+ import { bem } from '@veltra/utils'
23
+ import { computed, inject } from 'vue'
29
24
 
30
25
  import { UEmpty } from '../empty'
31
26
  import { TableDIKey } from './di'
@@ -38,9 +33,26 @@ defineOptions({
38
33
  const { cls, rows, virtualList, columnConfig, tableProps, virtualEnabled } = inject(TableDIKey)!
39
34
  const { leafColumns } = columnConfig
40
35
 
41
- const getStripeCls = computed(() => {
42
- return tableProps.stripe ? (index: number) => bem.is('stripe', index % 2 === 1) : () => ''
43
- })
36
+ // A1 的同族优化:把 body / empty / empty-row 等静态 class 一次性取出,
37
+ // 避免每次 render 触发 `cls.e` 的字符串拼接。
38
+ const bodyCls = cls.e('body')
39
+ const emptyRowCls = cls.e('row')
40
+ const emptyCls = cls.e('empty')
41
+
42
+ /**
43
+ * C1:stripe class 查找表。
44
+ *
45
+ * `bem.is('stripe', true) === 'is-stripe'`,`bem.is('stripe', false) === ''`。
46
+ * 两种输出是全局常量,无需每次调用 `bem.is`;按奇偶直接查表即可。
47
+ * 非 stripe 模式下两个位置均返回空串。
48
+ */
49
+ const STRIPE_CLS_ON: readonly [string, string] = [bem.is('stripe', false), bem.is('stripe', true)]
50
+ const STRIPE_CLS_OFF: readonly [string, string] = ['', '']
51
+
52
+ const stripeTable = computed<readonly [string, string]>(() =>
53
+ tableProps.stripe ? STRIPE_CLS_ON : STRIPE_CLS_OFF
54
+ )
55
+ const getStripeCls = (index: number): string => stripeTable.value[index & 1]!
44
56
 
45
57
  const tableRows = computed(() => {
46
58
  if (!virtualEnabled.value) {
@@ -53,7 +65,7 @@ const tableRows = computed(() => {
53
65
  })
54
66
  }
55
67
 
56
- // 虚拟列表
68
+ // 虚拟列表:仅渲染可视窗口内的行,位置由 table.vue 中的 before/after 占位 tbody 撑开
57
69
  return virtualList.value.map((item) => {
58
70
  return {
59
71
  row: rows.value[item.index]!,
@@ -62,21 +74,4 @@ const tableRows = computed(() => {
62
74
  }
63
75
  })
64
76
  })
65
-
66
- const bodyRef = shallowRef<HTMLElement>()
67
-
68
- function setBodyTransform(transformY: number) {
69
- bodyRef.value &&
70
- setStyles(bodyRef.value, {
71
- transform: `translate3d(0, ${transformY}px, 0)`
72
- })
73
- }
74
-
75
- watch(virtualList, (list) => {
76
- setBodyTransform(list[0]?.start ?? 0)
77
- })
78
-
79
- defineExpose({
80
- setBodyTransform
81
- })
82
77
  </script>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <td
3
- :class="getCellClass(column)"
4
- :style="{ left: withUnit(left, 'px'), right: withUnit(right, 'px') }"
3
+ :class="cellClass"
4
+ :style="style"
5
5
  :rowspan="rowspan"
6
6
  :colspan="colspan"
7
7
  v-if="rowspan !== 0 && colspan !== 0"
@@ -12,22 +12,30 @@
12
12
 
13
13
  <script lang="ts" setup>
14
14
  import { withUnit } from '@veltra/utils'
15
- import { inject } from 'vue'
16
-
17
- import { TableDIKey } from './di'
18
- import type { ColumnNode } from './node/col'
15
+ import { computed } from 'vue'
19
16
 
20
17
  defineOptions({
21
18
  name: 'TableCell'
22
19
  })
23
20
 
24
- const { column } = defineProps<{
25
- column: ColumnNode
21
+ /**
22
+ * B2: TableCell 去 inject 化。
23
+ *
24
+ * 原先每个可见单元格都会执行 `inject(TableDIKey)` 并在模板中调用 `getCellClass(column)`,
25
+ * 在 30 列场景下每次滚动每行产生 30 次 inject / 30 次类名拼接。
26
+ * 父组件 `TableRow` 已经持有注入上下文,直接把 `cellClass` 传下来即可;
27
+ * 这里变成「完全受控」的纯渲染单元。
28
+ */
29
+ const { left, right } = defineProps<{
30
+ cellClass: string
26
31
  left?: number
27
32
  right?: number
28
33
  rowspan?: number
29
34
  colspan?: number
30
35
  }>()
31
36
 
32
- const { getCellClass } = inject(TableDIKey)!
37
+ const style = computed(() => ({
38
+ left: withUnit(left, 'px'),
39
+ right: withUnit(right, 'px')
40
+ }))
33
41
  </script>