@veltra/desktop 1.0.7 → 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.
- package/dist/components/action/action-group.vue.d.ts +1 -1
- package/dist/components/action/action.vue.d.ts +3 -3
- package/dist/components/action/style.js +3 -0
- package/dist/components/auto-complete/auto-complete.vue.d.ts +2 -2
- package/dist/components/auto-complete/style.js +5 -0
- package/dist/components/batch-edit/style.js +9 -0
- package/dist/components/breadcrumb/style2.css +0 -6
- package/dist/components/button/style.js +1 -0
- package/dist/components/cascade/cascade.js +15 -13
- package/dist/components/cascade/cascade.js.map +1 -1
- package/dist/components/cascade/cascade.vue.d.ts +3 -3
- package/dist/components/cascade/node.js.map +1 -1
- package/dist/components/cascade/style.js +7 -0
- package/dist/components/cascade/use-check.js +3 -2
- package/dist/components/cascade/use-check.js.map +1 -1
- package/dist/components/cascade/use-select.js +3 -3
- package/dist/components/cascade/use-select.js.map +1 -1
- package/dist/components/checkbox/style.js +1 -0
- package/dist/components/checkbox/style2.css +0 -8
- package/dist/components/checkbox-group/checkbox-group.vue.d.ts +2 -2
- package/dist/components/checkbox-group/style.js +1 -0
- package/dist/components/context-menu/style.js +1 -0
- package/dist/components/date-panel/date-panel.vue.d.ts +1 -1
- package/dist/components/date-picker/date-picker.js +9 -9
- package/dist/components/date-picker/date-picker.js.map +1 -1
- package/dist/components/date-picker/date-picker.vue.d.ts +2 -2
- package/dist/components/date-picker/style.js +3 -0
- package/dist/components/date-range-picker/date-range-picker.js +9 -9
- package/dist/components/date-range-picker/date-range-picker.js.map +1 -1
- package/dist/components/date-range-picker/date-range-picker.vue.d.ts +2 -2
- package/dist/components/date-range-picker/style.js +3 -0
- package/dist/components/dialog/style.js +2 -0
- package/dist/components/drawer/style.js +1 -0
- package/dist/components/drawer/style2.css +4 -16
- package/dist/components/empty/style.js +1 -0
- package/dist/components/expression-editor/style.js +6 -0
- package/dist/components/file-picker/file-picker.vue.d.ts +1 -1
- package/dist/components/file-picker/helper.js +19 -7
- package/dist/components/file-picker/helper.js.map +1 -1
- package/dist/components/file-viewer/file-viewer.js +294 -0
- package/dist/components/file-viewer/file-viewer.js.map +1 -0
- package/dist/components/file-viewer/file-viewer.vue.d.ts +42 -0
- package/dist/components/file-viewer/helper.js +185 -0
- package/dist/components/file-viewer/helper.js.map +1 -0
- package/dist/components/file-viewer/index.d.ts +1 -0
- package/dist/components/file-viewer/previewers/docx-previewer.js +76 -0
- package/dist/components/file-viewer/previewers/docx-previewer.js.map +1 -0
- package/dist/components/file-viewer/previewers/image-previewer.js +64 -0
- package/dist/components/file-viewer/previewers/image-previewer.js.map +1 -0
- package/dist/components/file-viewer/previewers/pdf-previewer.js +90 -0
- package/dist/components/file-viewer/previewers/pdf-previewer.js.map +1 -0
- package/dist/components/file-viewer/previewers/sheet-previewer.js +175 -0
- package/dist/components/file-viewer/previewers/sheet-previewer.js.map +1 -0
- package/dist/components/file-viewer/previewers/text-previewer.js +74 -0
- package/dist/components/file-viewer/previewers/text-previewer.js.map +1 -0
- package/dist/components/file-viewer/previewers/video-previewer.js +62 -0
- package/dist/components/file-viewer/previewers/video-previewer.js.map +1 -0
- package/dist/components/file-viewer/style.d.ts +1 -0
- package/dist/components/file-viewer/style.js +4 -0
- package/dist/components/file-viewer/style2.css +445 -0
- package/dist/components/float-button/style.js +1 -0
- package/dist/components/form/style.js +2 -0
- package/dist/components/form-item/style.js +1 -0
- package/dist/components/form-item/style2.css +2 -3
- package/dist/components/grid-input/grid-input.vue.d.ts +1 -1
- package/dist/components/index.d.ts +12 -9
- package/dist/components/input/input.vue.d.ts +2 -2
- package/dist/components/input/style.js +1 -0
- package/dist/components/layout/style.js +1 -0
- package/dist/components/list/style.js +1 -0
- package/dist/components/loading/style2.css +16 -20
- package/dist/components/menu/style.js +2 -0
- package/dist/components/menu/style2.css +0 -78
- package/dist/components/multi-select/multi-select-option.js +14 -10
- package/dist/components/multi-select/multi-select-option.js.map +1 -1
- package/dist/components/multi-select/multi-select.js +16 -19
- package/dist/components/multi-select/multi-select.js.map +1 -1
- package/dist/components/multi-select/multi-select.vue.d.ts +3 -3
- package/dist/components/multi-select/style.js +7 -0
- package/dist/components/multi-tree-select/multi-tree-select.vue.d.ts +4 -4
- package/dist/components/multi-tree-select/style.js +7 -0
- package/dist/components/notification/notification.vue.d.ts +1 -1
- package/dist/components/number-input/number-input.vue.d.ts +2 -2
- package/dist/components/number-input/style.js +1 -0
- package/dist/components/number-range-input/number-range-input.vue.d.ts +2 -2
- package/dist/components/number-range-input/style.js +1 -0
- package/dist/components/paginator/style.js +3 -0
- package/dist/components/palette/di.js.map +1 -1
- package/dist/components/palette/palette-alpha.js +7 -10
- package/dist/components/palette/palette-alpha.js.map +1 -1
- package/dist/components/palette/palette-hue.js +5 -7
- package/dist/components/palette/palette-hue.js.map +1 -1
- package/dist/components/palette/palette-sv.js +8 -10
- package/dist/components/palette/palette-sv.js.map +1 -1
- package/dist/components/palette/palette.js +22 -24
- package/dist/components/palette/palette.js.map +1 -1
- package/dist/components/palette/style.js +3 -0
- package/dist/components/password-input/password-input.vue.d.ts +1 -1
- package/dist/components/password-input/style.js +2 -0
- package/dist/components/pop-confirm/pop-confirm.vue.d.ts +1 -1
- package/dist/components/pop-confirm/style.js +3 -0
- package/dist/components/progress-nodes/progress-nodes.vue.d.ts +2 -2
- package/dist/components/progress-nodes/style2.css +79 -33
- package/dist/components/radio/style2.css +0 -8
- package/dist/components/radio-group/radio-group.vue.d.ts +2 -2
- package/dist/components/radio-group/style.js +1 -0
- package/dist/components/rich-text-editor/rich-text-editor.vue.d.ts +2 -2
- package/dist/components/select/select.js +14 -12
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/select/select.vue.d.ts +2 -2
- package/dist/components/select/style.js +5 -0
- package/dist/components/select/style2.css +0 -20
- package/dist/components/slider/slider-thumb.js +10 -13
- package/dist/components/slider/slider-thumb.js.map +1 -1
- package/dist/components/slider/slider.js +12 -15
- package/dist/components/slider/slider.js.map +1 -1
- package/dist/components/steps/style.js +2 -0
- package/dist/components/steps/style2.css +28 -35
- package/dist/components/switch/style2.css +0 -3
- package/dist/components/table/di.js.map +1 -1
- package/dist/components/table/node/row.d.ts +10 -1
- package/dist/components/table/node/row.js +9 -0
- package/dist/components/table/node/row.js.map +1 -1
- package/dist/components/table/style.js +7 -0
- package/dist/components/table/style2.css +0 -41
- package/dist/components/table/table-body.js +25 -26
- package/dist/components/table/table-body.js.map +1 -1
- package/dist/components/table/table-cell.js +16 -9
- package/dist/components/table/table-cell.js.map +1 -1
- package/dist/components/table/table-row.js +78 -19
- package/dist/components/table/table-row.js.map +1 -1
- package/dist/components/table/table.js +76 -34
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/table/use-fixed-columns.js +27 -8
- package/dist/components/table/use-fixed-columns.js.map +1 -1
- package/dist/components/table/use-rows.js +2 -2
- package/dist/components/table/use-rows.js.map +1 -1
- package/dist/components/table/use-table.js +25 -3
- package/dist/components/table/use-table.js.map +1 -1
- package/dist/components/table-editor/style.js +2 -0
- package/dist/components/tabs/index.d.ts +3 -1
- package/dist/components/tabs/style.js +2 -0
- package/dist/components/tabs/style2.css +169 -107
- package/dist/components/tabs/tabs-horizontal.js +133 -0
- package/dist/components/tabs/tabs-horizontal.js.map +1 -0
- package/dist/components/tabs/tabs-horizontal.vue.d.ts +29 -0
- package/dist/components/tabs/tabs-vertical.js +89 -0
- package/dist/components/tabs/tabs-vertical.js.map +1 -0
- package/dist/components/tabs/tabs-vertical.vue.d.ts +28 -0
- package/dist/components/tabs/tabs.js +75 -105
- package/dist/components/tabs/tabs.js.map +1 -1
- package/dist/components/tabs/tabs.vue.d.ts +9 -23
- package/dist/components/tabs/use-tabs-bar.js +105 -0
- package/dist/components/tabs/use-tabs-bar.js.map +1 -0
- package/dist/components/tag/style2.css +0 -6
- package/dist/components/textarea/style.js +1 -0
- package/dist/components/textarea/textarea.vue.d.ts +2 -2
- package/dist/components/theme/style.js +5 -0
- package/dist/components/tip/tip.vue.d.ts +2 -2
- package/dist/components/tree/style.js +4 -0
- package/dist/components/tree/style2.css +45 -23
- package/dist/components/tree/tree-node.js +50 -35
- package/dist/components/tree/tree-node.js.map +1 -1
- package/dist/components/tree/tree.js +27 -20
- package/dist/components/tree/tree.js.map +1 -1
- package/dist/components/tree/tree.vue.d.ts +1 -1
- package/dist/components/tree/use-check.js +52 -14
- package/dist/components/tree/use-check.js.map +1 -1
- package/dist/components/tree/use-tree-nodes.js +51 -24
- package/dist/components/tree/use-tree-nodes.js.map +1 -1
- package/dist/components/tree-select/style.js +4 -0
- package/dist/components/tree-select/tree-select.vue.d.ts +3 -3
- package/dist/components/watermark/watermark.vue.d.ts +1 -1
- package/dist/index.d.ts +57 -53
- package/dist/index.js +51 -48
- package/dist/types/file-viewer.d.ts +71 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/tabs.d.ts +106 -14
- package/dist/types/tree.d.ts +6 -1
- package/package.json +32 -31
- package/src/components/breadcrumb/style.scss +0 -6
- package/src/components/cascade/cascade.vue +18 -18
- package/src/components/cascade/node.ts +4 -1
- package/src/components/cascade/use-check.ts +5 -4
- package/src/components/cascade/use-select.ts +4 -5
- package/src/components/checkbox/style.scss +0 -4
- package/src/components/date-picker/date-picker.vue +10 -9
- package/src/components/date-range-picker/date-range-picker.vue +12 -12
- package/src/components/drawer/style.scss +18 -11
- package/src/components/file-picker/helper.ts +21 -10
- package/src/components/file-viewer/file-viewer.vue +311 -0
- package/src/components/file-viewer/helper.ts +193 -0
- package/src/components/file-viewer/index.ts +1 -0
- package/src/components/file-viewer/previewers/docx-previewer.vue +78 -0
- package/src/components/file-viewer/previewers/image-previewer.vue +64 -0
- package/src/components/file-viewer/previewers/pdf-previewer.vue +90 -0
- package/src/components/file-viewer/previewers/sheet-previewer.vue +210 -0
- package/src/components/file-viewer/previewers/text-previewer.vue +76 -0
- package/src/components/file-viewer/previewers/video-previewer.vue +72 -0
- package/src/components/file-viewer/style.scss +504 -0
- package/src/components/file-viewer/style.ts +4 -0
- package/src/components/form-item/style.scss +2 -3
- package/src/components/index.ts +2 -0
- package/src/components/loading/style.scss +125 -126
- package/src/components/menu/style.scss +0 -31
- package/src/components/multi-select/multi-select-option.vue +16 -3
- package/src/components/multi-select/multi-select.vue +17 -9
- package/src/components/palette/di.ts +5 -3
- package/src/components/palette/palette-alpha.vue +7 -10
- package/src/components/palette/palette-hue.vue +5 -7
- package/src/components/palette/palette-sv.vue +12 -14
- package/src/components/palette/palette.vue +19 -21
- package/src/components/progress-nodes/style.scss +28 -22
- package/src/components/radio/style.scss +0 -4
- package/src/components/select/select.vue +21 -19
- package/src/components/select/style.scss +0 -12
- package/src/components/slider/slider-thumb.vue +11 -14
- package/src/components/slider/slider.vue +15 -15
- package/src/components/steps/style.scss +10 -10
- package/src/components/switch/style.scss +0 -4
- package/src/components/table/di.ts +20 -3
- package/src/components/table/node/row.ts +10 -1
- package/src/components/table/style.scss +0 -26
- package/src/components/table/table-body.vue +28 -33
- package/src/components/table/table-cell.vue +17 -9
- package/src/components/table/table-row.tsx +83 -17
- package/src/components/table/table.vue +73 -45
- package/src/components/table/use-fixed-columns.ts +46 -12
- package/src/components/table/use-rows.ts +14 -3
- package/src/components/table/use-table.ts +27 -3
- package/src/components/tabs/index.ts +2 -0
- package/src/components/tabs/style.scss +217 -134
- package/src/components/tabs/tabs-horizontal.vue +125 -0
- package/src/components/tabs/tabs-vertical.vue +75 -0
- package/src/components/tabs/tabs.vue +56 -127
- package/src/components/tabs/use-tabs-bar.ts +119 -0
- package/src/components/tag/style.scss +0 -6
- package/src/components/tree/style.scss +74 -29
- package/src/components/tree/tree-node.vue +10 -4
- package/src/components/tree/tree.vue +39 -12
- package/src/components/tree/use-check.ts +79 -23
- package/src/components/tree/use-tree-nodes.ts +85 -34
- package/src/types/file-viewer.ts +69 -0
- package/src/types/index.ts +1 -0
- package/src/types/tabs.ts +113 -19
- package/src/types/tree.ts +6 -1
- package/dist/types/index.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<td
|
|
3
|
-
:class="
|
|
4
|
-
:style="
|
|
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 {
|
|
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
|
-
|
|
25
|
-
|
|
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
|
|
37
|
+
const style = computed(() => ({
|
|
38
|
+
left: withUnit(left, 'px'),
|
|
39
|
+
right: withUnit(right, 'px')
|
|
40
|
+
}))
|
|
33
41
|
</script>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ArrowRight } from '@veltra/icons/normal'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { inject } from 'vue'
|
|
2
|
+
import { defineComponent, type DefineComponent } from 'vue'
|
|
3
|
+
import { inject, onUnmounted } from 'vue'
|
|
5
4
|
import type { JSX } from 'vue/jsx-runtime'
|
|
6
5
|
|
|
7
6
|
import type { TableRow as ITableRow } from '../../types'
|
|
@@ -10,14 +9,40 @@ import { UIcon } from '../icon'
|
|
|
10
9
|
import { TableDIKey } from './di'
|
|
11
10
|
import UTabelCell from './table-cell.vue'
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* A3:行修饰类组合静态查找表。
|
|
14
|
+
*
|
|
15
|
+
* 位模式:bit2 = isCurrent, bit1 = checked, bit0 = expanded。
|
|
16
|
+
* 与 `bem.is('...')` 对于 `'current' | 'checked' | 'expanded'` 的输出完全等价,
|
|
17
|
+
* 直接从 index 读字符串,消除每行渲染期间的字符串拼接和 3 次 bem.is 调用。
|
|
18
|
+
*
|
|
19
|
+
* 放在模块顶层使所有 TableRow 实例共享一份引用(不会随 setup 重建)。
|
|
20
|
+
*/
|
|
21
|
+
const ROW_MOD_CLASS: readonly string[] = [
|
|
22
|
+
'',
|
|
23
|
+
'is-expanded',
|
|
24
|
+
'is-checked',
|
|
25
|
+
'is-checked is-expanded',
|
|
26
|
+
'is-current',
|
|
27
|
+
'is-current is-expanded',
|
|
28
|
+
'is-current is-checked',
|
|
29
|
+
'is-current is-checked is-expanded'
|
|
30
|
+
] as const
|
|
31
|
+
|
|
32
|
+
function rowModCls(row: ITableRow): string {
|
|
33
|
+
const i = (row.isCurrent ? 4 : 0) | (row.checked ? 2 : 0) | (row.expanded ? 1 : 0)
|
|
34
|
+
return ROW_MOD_CLASS[i]!
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const UTableRow: DefineComponent<{ row: ITableRow; index: number }> = defineComponent({
|
|
14
38
|
name: 'TableRow',
|
|
15
39
|
|
|
16
|
-
props:
|
|
40
|
+
props: {
|
|
41
|
+
row: { type: Object as () => ITableRow, required: true },
|
|
42
|
+
index: { type: Number, required: true }
|
|
43
|
+
},
|
|
17
44
|
|
|
18
45
|
setup(props) {
|
|
19
|
-
const { row } = props
|
|
20
|
-
|
|
21
46
|
const {
|
|
22
47
|
cls,
|
|
23
48
|
columnConfig,
|
|
@@ -27,12 +52,39 @@ export const UTableRow: DefineComponent<{ row: ITableRow }> = defineComponent({
|
|
|
27
52
|
handleRowClick,
|
|
28
53
|
handleCellClick,
|
|
29
54
|
tableProps,
|
|
30
|
-
measureElement
|
|
55
|
+
measureElement,
|
|
56
|
+
getCellClass
|
|
31
57
|
} = inject(TableDIKey)!
|
|
32
58
|
|
|
59
|
+
// A1:把 cls.e('row') / cls.e('expand-toggle') 等静态字符串提升到 setup 一次性计算,
|
|
60
|
+
// 避免每次 render 重新走 `cls.e` 拼接。
|
|
61
|
+
const rowBaseCls = cls.e('row')
|
|
62
|
+
const expandToggleCls = cls.e('expand-toggle')
|
|
63
|
+
const expandSpaceCls = cls.e('expand-space')
|
|
64
|
+
|
|
65
|
+
// A2:measureRef 按 index 缓存闭包。
|
|
66
|
+
// 行虚拟化下,同一 TableRow 实例的 props.index 大多稳定,但在虚拟化重排或展开行切换时
|
|
67
|
+
// 仍会发生变化;把 (index) → refFn 缓存起来,既保证引用稳定(利于 Vue 的 ref diff),
|
|
68
|
+
// 又避免在未来可能的"同实例跨 index 复用"情形下重复分配闭包。
|
|
69
|
+
const measureRefCache = new Map<number, (el: unknown) => void>()
|
|
70
|
+
const getMeasureRef = (index: number): ((el: unknown) => void) => {
|
|
71
|
+
let ref = measureRefCache.get(index)
|
|
72
|
+
if (ref === undefined) {
|
|
73
|
+
ref = (el: unknown) => {
|
|
74
|
+
measureElement(index, el as Element | null)
|
|
75
|
+
}
|
|
76
|
+
measureRefCache.set(index, ref)
|
|
77
|
+
}
|
|
78
|
+
return ref
|
|
79
|
+
}
|
|
80
|
+
onUnmounted(() => {
|
|
81
|
+
measureRefCache.clear()
|
|
82
|
+
})
|
|
83
|
+
|
|
33
84
|
const { columns, expandColumn } = columnConfig
|
|
34
85
|
|
|
35
86
|
return () => {
|
|
87
|
+
const { row } = props
|
|
36
88
|
const _expandColumn = expandColumn.value
|
|
37
89
|
|
|
38
90
|
let expandCell: JSX.Element | null = null
|
|
@@ -44,7 +96,7 @@ export const UTableRow: DefineComponent<{ row: ITableRow }> = defineComponent({
|
|
|
44
96
|
const marginLeft = tableProps.tree ? (row.depth - 1) * 14 : 0
|
|
45
97
|
expandCell = (
|
|
46
98
|
<UTabelCell
|
|
47
|
-
|
|
99
|
+
cellClass={getCellClass(_expandColumn)}
|
|
48
100
|
left={_expandColumn.style.left}
|
|
49
101
|
right={_expandColumn.style.right}
|
|
50
102
|
key={row.uid + _expandColumn.key}
|
|
@@ -53,7 +105,7 @@ export const UTableRow: DefineComponent<{ row: ITableRow }> = defineComponent({
|
|
|
53
105
|
{!row.isLeaf || (tableProps.expandable && !row.isExpandRow) ? (
|
|
54
106
|
<UButton
|
|
55
107
|
text
|
|
56
|
-
class={
|
|
108
|
+
class={expandToggleCls}
|
|
57
109
|
type='primary'
|
|
58
110
|
size='small'
|
|
59
111
|
circle
|
|
@@ -68,7 +120,7 @@ export const UTableRow: DefineComponent<{ row: ITableRow }> = defineComponent({
|
|
|
68
120
|
</UIcon>
|
|
69
121
|
</UButton>
|
|
70
122
|
) : (
|
|
71
|
-
<i class={
|
|
123
|
+
<i class={expandSpaceCls} style={`margin-left: ${marginLeft}px`}></i>
|
|
72
124
|
)}
|
|
73
125
|
|
|
74
126
|
{getColumnSlotsNode(expandCtx)}
|
|
@@ -77,11 +129,15 @@ export const UTableRow: DefineComponent<{ row: ITableRow }> = defineComponent({
|
|
|
77
129
|
}
|
|
78
130
|
}
|
|
79
131
|
|
|
132
|
+
// A3:行 class 由 base + 3-bit 组合查找产出,避免 Vue 的 class 数组归一化开销。
|
|
133
|
+
const mod = rowModCls(row)
|
|
134
|
+
const rowCls = mod ? rowBaseCls + ' ' + mod : rowBaseCls
|
|
135
|
+
|
|
80
136
|
return (
|
|
81
137
|
<tr
|
|
82
|
-
class={
|
|
138
|
+
class={rowCls}
|
|
83
139
|
onClick={(e) => handleRowClick(row, e)}
|
|
84
|
-
ref={
|
|
140
|
+
ref={getMeasureRef(props.index)}
|
|
85
141
|
key={row.uid}
|
|
86
142
|
>
|
|
87
143
|
{expandCell}
|
|
@@ -97,7 +153,7 @@ export const UTableRow: DefineComponent<{ row: ITableRow }> = defineComponent({
|
|
|
97
153
|
|
|
98
154
|
return (
|
|
99
155
|
<UTabelCell
|
|
100
|
-
|
|
156
|
+
cellClass={getCellClass(column)}
|
|
101
157
|
left={column.style.left}
|
|
102
158
|
right={column.style.right}
|
|
103
159
|
key={row.uid + column.key}
|
|
@@ -117,11 +173,21 @@ export const UTableRow: DefineComponent<{ row: ITableRow }> = defineComponent({
|
|
|
117
173
|
|
|
118
174
|
export const UExpandTableRow = defineComponent({
|
|
119
175
|
name: 'ExpandTableRow',
|
|
120
|
-
props:
|
|
176
|
+
props: {
|
|
177
|
+
row: { type: Object as () => ITableRow, required: true },
|
|
178
|
+
index: { type: Number, required: true }
|
|
179
|
+
},
|
|
121
180
|
setup(props) {
|
|
122
181
|
const { getExpandRowSlotsNode, cls, measureElement, columnConfig } = inject(TableDIKey)!
|
|
123
182
|
const { leafColumns } = columnConfig
|
|
124
183
|
|
|
184
|
+
const rowBaseCls = cls.e('row')
|
|
185
|
+
const cellCls = cls.e('cell')
|
|
186
|
+
|
|
187
|
+
const measureRef = (el: unknown) => {
|
|
188
|
+
measureElement(props.index, el as Element | null)
|
|
189
|
+
}
|
|
190
|
+
|
|
125
191
|
return () => {
|
|
126
192
|
const { row } = props
|
|
127
193
|
const node = getExpandRowSlotsNode({
|
|
@@ -131,8 +197,8 @@ export const UExpandTableRow = defineComponent({
|
|
|
131
197
|
rowData: row.data
|
|
132
198
|
})
|
|
133
199
|
return (
|
|
134
|
-
<tr class={
|
|
135
|
-
<td colspan={leafColumns.value.length} class={
|
|
200
|
+
<tr class={rowBaseCls} ref={measureRef}>
|
|
201
|
+
<td colspan={leafColumns.value.length} class={cellCls}>
|
|
136
202
|
{node}
|
|
137
203
|
</td>
|
|
138
204
|
</tr>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
:content-class="cls.e('content')"
|
|
12
12
|
@scroll="handleScroll"
|
|
13
13
|
>
|
|
14
|
+
{{ console.log(1) }}
|
|
14
15
|
<table :class="cls.e('wrap')">
|
|
15
16
|
<colgroup ref="colgroupRef">
|
|
16
17
|
<col
|
|
@@ -25,7 +26,18 @@
|
|
|
25
26
|
</colgroup>
|
|
26
27
|
<UTableHead />
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
<!--
|
|
30
|
+
虚拟滚动上占位:替代 tbody transform,避免 table-layout: fixed 下列宽抖动。
|
|
31
|
+
高度由 useVirtualizer 命令式写入 `style.height`,不经过模板响应式绑定,
|
|
32
|
+
因此滚动窗口变化不会引起表格模板重渲染。
|
|
33
|
+
-->
|
|
34
|
+
<tbody v-if="virtualEnabled" aria-hidden="true">
|
|
35
|
+
<tr ref="beforeSpacerRef">
|
|
36
|
+
<td :colspan="leafColumns.length" style="padding: 0; border: none"></td>
|
|
37
|
+
</tr>
|
|
38
|
+
</tbody>
|
|
39
|
+
|
|
40
|
+
<u-table-body>
|
|
29
41
|
<slot name="body" :columns="leafColumns" :rows="rows" />
|
|
30
42
|
|
|
31
43
|
<template #empty v-if="slots.empty">
|
|
@@ -33,8 +45,12 @@
|
|
|
33
45
|
</template>
|
|
34
46
|
</u-table-body>
|
|
35
47
|
|
|
36
|
-
<!--
|
|
37
|
-
<tbody v-if="virtualEnabled"
|
|
48
|
+
<!-- 虚拟滚动下占位(高度由 useVirtualizer 命令式写入) -->
|
|
49
|
+
<tbody v-if="virtualEnabled" aria-hidden="true">
|
|
50
|
+
<tr ref="afterSpacerRef">
|
|
51
|
+
<td :colspan="leafColumns.length" style="padding: 0; border: none"></td>
|
|
52
|
+
</tr>
|
|
53
|
+
</tbody>
|
|
38
54
|
|
|
39
55
|
<u-table-foot ref="tableFoot">
|
|
40
56
|
<slot name="foot" :columns="leafColumns" :rows="rows" />
|
|
@@ -50,9 +66,9 @@
|
|
|
50
66
|
</template>
|
|
51
67
|
|
|
52
68
|
<script lang="ts" setup>
|
|
53
|
-
import { useFallbackProps,
|
|
54
|
-
import { bem,
|
|
55
|
-
import { computed,
|
|
69
|
+
import { useFallbackProps, useVirtualizer } from '@veltra/compositions'
|
|
70
|
+
import { bem, withUnit } from '@veltra/utils'
|
|
71
|
+
import { computed, provide, shallowRef, toRef, useTemplateRef, watch } from 'vue'
|
|
56
72
|
|
|
57
73
|
import type {
|
|
58
74
|
TableProps,
|
|
@@ -102,7 +118,16 @@ const cls = bem('table')
|
|
|
102
118
|
|
|
103
119
|
const { size } = useFallbackProps([props], { size: 'default' as ComponentSize })
|
|
104
120
|
|
|
105
|
-
|
|
121
|
+
const scrollRef = shallowRef<ScrollExposed>()
|
|
122
|
+
|
|
123
|
+
// 占位 tr 的 DOM 句柄;交给 useVirtualizer 直接命令式写 style.height。
|
|
124
|
+
const beforeSpacerRef = useTemplateRef<HTMLElement>('beforeSpacerRef')
|
|
125
|
+
const afterSpacerRef = useTemplateRef<HTMLElement>('afterSpacerRef')
|
|
126
|
+
|
|
127
|
+
// 行(useRows / useFixedColumns 需要 isScrolling,而虚拟化 hook 又依赖 rows.length;
|
|
128
|
+
// 先用中继占位,下方 isScrolling 到位后 watch 回灌)。
|
|
129
|
+
const isScrollingRelay = shallowRef(false)
|
|
130
|
+
|
|
106
131
|
const {
|
|
107
132
|
rows,
|
|
108
133
|
toggleTreeRowExpand,
|
|
@@ -112,13 +137,11 @@ const {
|
|
|
112
137
|
handleRowClick,
|
|
113
138
|
handleCellClick,
|
|
114
139
|
getRowByData
|
|
115
|
-
} = useRows({ props, emit })
|
|
140
|
+
} = useRows({ props, emit, isScrolling: isScrollingRelay })
|
|
116
141
|
|
|
117
|
-
// 选中
|
|
118
142
|
const { createCheckColumn, createSelectColumn, clearChecked, clearSelected, checkedRows } =
|
|
119
143
|
useCheck({ size, props, rows, rowForest, emit, cls })
|
|
120
144
|
|
|
121
|
-
// 列
|
|
122
145
|
const columnConfig = useColumns({
|
|
123
146
|
props,
|
|
124
147
|
createCheckColumn,
|
|
@@ -129,9 +152,10 @@ const columnConfig = useColumns({
|
|
|
129
152
|
|
|
130
153
|
const { leafColumns } = columnConfig
|
|
131
154
|
|
|
132
|
-
const { handleScroll, leftFixed, rightFixed } = useFixedColumns(
|
|
155
|
+
const { handleScroll, leftFixed, rightFixed } = useFixedColumns({
|
|
156
|
+
isScrolling: isScrollingRelay
|
|
157
|
+
})
|
|
133
158
|
|
|
134
|
-
// 在表格中提供的通用方法和属性
|
|
135
159
|
const {
|
|
136
160
|
getColumnSlotsNode,
|
|
137
161
|
getExpandRowSlotsNode,
|
|
@@ -141,49 +165,51 @@ const {
|
|
|
141
165
|
getHeaderCellClass
|
|
142
166
|
} = useTable({ props, cls, leftFixed, rightFixed })
|
|
143
167
|
|
|
144
|
-
const scrollRef = shallowRef<ScrollExposed>()
|
|
145
|
-
|
|
146
168
|
const { showResizeLine, resizeLineRef, colgroupRef } = useColResize({ scrollRef, leafColumns })
|
|
147
169
|
|
|
148
|
-
const
|
|
170
|
+
const { virtualizer, items, isScrolling } = useVirtualizer({
|
|
149
171
|
count: computed(() => rows.value.length),
|
|
150
|
-
scrollEl:
|
|
151
|
-
|
|
152
|
-
|
|
172
|
+
scrollEl: () => scrollRef.value?.containerRef ?? null,
|
|
173
|
+
beforeEl: beforeSpacerRef,
|
|
174
|
+
afterEl: afterSpacerRef,
|
|
175
|
+
estimateSize: () => 41,
|
|
176
|
+
// 注意:getItemKey 在构造期一次性写入底层 Virtualizer;闭包每次读的是当前 rows.value,
|
|
177
|
+
// rows 引用整体替换后 key 仍有效(无需再 setOptions)。
|
|
178
|
+
getItemKey: (i) => rows.value[i]?.uid ?? i
|
|
153
179
|
})
|
|
154
180
|
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (virtualList.value.length) {
|
|
159
|
-
return (
|
|
160
|
-
totalHeight.value -
|
|
161
|
-
virtualList.value[virtualList.value.length - 1]!.end +
|
|
162
|
-
virtualList.value[0]!.start
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
return totalHeight.value
|
|
181
|
+
const virtualEnabled = computed(() => {
|
|
182
|
+
const t = props.virtualThreshold
|
|
183
|
+
return t ? rows.value.length > t : true
|
|
166
184
|
})
|
|
167
185
|
|
|
168
|
-
|
|
186
|
+
// 把虚拟化 isScrolling 回灌到中继,供 useRows / useFixedColumns 读取。
|
|
187
|
+
watch(isScrolling, (v) => {
|
|
188
|
+
isScrollingRelay.value = v
|
|
189
|
+
})
|
|
169
190
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
191
|
+
/**
|
|
192
|
+
* E1:数据替换时的条件置顶。
|
|
193
|
+
*
|
|
194
|
+
* 旧实现无差别地 `scrollTo({ y: 0 })`,在「行内增删 / 排序 / 单行更新」等
|
|
195
|
+
* 场景会把用户当前阅读位置跳走;仅在「翻页 / 显著数据集替换」时才需要复位。
|
|
196
|
+
*
|
|
197
|
+
* 判定阈值:|Δlen| / max(newLen, oldLen) ≥ 0.5,即行数变化量超过更大侧的一半时视为显著变化。
|
|
198
|
+
* 选择 0.5 是因为典型的分页/搜索场景会带来 50% 以上的行数差(通常完全替换数据集),
|
|
199
|
+
* 而行内追加 / 删除个位数通常只带来 <5% 的变化;两者区分度足够宽。
|
|
200
|
+
*/
|
|
201
|
+
const LENGTH_DELTA_RATIO_TO_RESET_SCROLL = 0.5
|
|
180
202
|
|
|
181
|
-
const tableBodyRef = useTemplateRef('tableBody')
|
|
182
203
|
watch(
|
|
183
204
|
() => props.data,
|
|
184
|
-
() => {
|
|
185
|
-
|
|
186
|
-
|
|
205
|
+
(next, prev) => {
|
|
206
|
+
const nextLen = next?.length ?? 0
|
|
207
|
+
const prevLen = prev?.length ?? 0
|
|
208
|
+
const denom = Math.max(nextLen, prevLen, 1)
|
|
209
|
+
const ratio = Math.abs(nextLen - prevLen) / denom
|
|
210
|
+
if (ratio >= LENGTH_DELTA_RATIO_TO_RESET_SCROLL) {
|
|
211
|
+
scrollRef.value?.scrollTo({ y: 0 })
|
|
212
|
+
}
|
|
187
213
|
}
|
|
188
214
|
)
|
|
189
215
|
|
|
@@ -206,7 +232,9 @@ provide(TableDIKey, {
|
|
|
206
232
|
getHeaderCellClass,
|
|
207
233
|
// tipRef,
|
|
208
234
|
toggleTreeRowExpand,
|
|
209
|
-
|
|
235
|
+
virtualList: items,
|
|
236
|
+
virtualEnabled,
|
|
237
|
+
measureElement: (index, el) => virtualizer.measureElement(index, el)
|
|
210
238
|
})
|
|
211
239
|
|
|
212
240
|
const tableFootRef = useTemplateRef('tableFoot')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { debounce } from '@cat-kit/core'
|
|
2
|
-
import { type ShallowRef, ref } from 'vue'
|
|
2
|
+
import { type ShallowRef, type Ref, ref, watch } from 'vue'
|
|
3
3
|
|
|
4
4
|
import type { ScrollPosition } from '../../types'
|
|
5
5
|
|
|
@@ -9,28 +9,62 @@ interface UseColumnFixedReturned {
|
|
|
9
9
|
handleScroll: (e: Required<ScrollPosition>) => void
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
interface Options {
|
|
13
|
+
/**
|
|
14
|
+
* D1:滚动中 (`true`) 暂缓 `leftFixed` / `rightFixed` 的切换,滚动结束后
|
|
15
|
+
* 一次性补算。
|
|
16
|
+
*
|
|
17
|
+
* 背景:固定列的阴影是通过在 cell 上切换 `is-last-fixed` / `is-first-fixed`
|
|
18
|
+
* 等 class 实现的,这些 class 会穿透到 `useTable.getCellClass` 的缓存键,
|
|
19
|
+
* 任何一次切换都会让全表的单元格类名失效重算。滚动本身并不需要实时更新阴影,
|
|
20
|
+
* 因此把切换推迟到 `isScrolling` 归零时做一次即可。
|
|
21
|
+
*
|
|
22
|
+
* 未传入时退化到旧行为(实时切换)。
|
|
23
|
+
*/
|
|
24
|
+
isScrolling?: Ref<boolean>
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
/**
|
|
13
28
|
* 固定列
|
|
14
29
|
* @returns
|
|
15
30
|
*/
|
|
16
|
-
export function useFixedColumns(): UseColumnFixedReturned {
|
|
31
|
+
export function useFixedColumns(options: Options = {}): UseColumnFixedReturned {
|
|
32
|
+
const { isScrolling } = options
|
|
33
|
+
|
|
17
34
|
const leftFixed = ref(false)
|
|
18
35
|
const rightFixed = ref(false)
|
|
19
36
|
|
|
37
|
+
let pendingLeft: boolean | null = null
|
|
38
|
+
let pendingRight: boolean | null = null
|
|
39
|
+
|
|
40
|
+
const applyImmediate = (nextLeft: boolean, nextRight: boolean): void => {
|
|
41
|
+
if (nextLeft !== leftFixed.value) leftFixed.value = nextLeft
|
|
42
|
+
if (nextRight !== rightFixed.value) rightFixed.value = nextRight
|
|
43
|
+
pendingLeft = null
|
|
44
|
+
pendingRight = null
|
|
45
|
+
}
|
|
46
|
+
|
|
20
47
|
const handleScroll = debounce((e: Required<ScrollPosition>) => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
leftFixed.value = false
|
|
25
|
-
}
|
|
48
|
+
const nextLeft = e.x > 0
|
|
49
|
+
// 精度修正:浏览器滚动条最后 1px 的误差
|
|
50
|
+
const nextRight = !(e.cw + e.x + 1 >= e.sw)
|
|
26
51
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
rightFixed.value = true
|
|
52
|
+
if (isScrolling?.value) {
|
|
53
|
+
pendingLeft = nextLeft
|
|
54
|
+
pendingRight = nextRight
|
|
55
|
+
return
|
|
32
56
|
}
|
|
57
|
+
|
|
58
|
+
applyImmediate(nextLeft, nextRight)
|
|
33
59
|
}, 50)
|
|
34
60
|
|
|
61
|
+
if (isScrolling) {
|
|
62
|
+
watch(isScrolling, (v) => {
|
|
63
|
+
if (v) return
|
|
64
|
+
if (pendingLeft === null && pendingRight === null) return
|
|
65
|
+
applyImmediate(pendingLeft ?? leftFixed.value, pendingRight ?? rightFixed.value)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
35
69
|
return { leftFixed, rightFixed, handleScroll }
|
|
36
70
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Forest, o } from '@cat-kit/core'
|
|
2
2
|
import { useModel } from '@veltra/compositions'
|
|
3
|
-
import { shallowRef, watch, type ShallowRef } from 'vue'
|
|
3
|
+
import { shallowRef, watch, type Ref, type ShallowRef } from 'vue'
|
|
4
4
|
|
|
5
5
|
import type { TableColumn, TableEmits, TableProps, TableRow } from '../../types'
|
|
6
6
|
import { TableRowNode } from './node/row'
|
|
@@ -8,6 +8,14 @@ import { TableRowNode } from './node/row'
|
|
|
8
8
|
interface Options {
|
|
9
9
|
props: TableProps
|
|
10
10
|
emit: TableEmits
|
|
11
|
+
/**
|
|
12
|
+
* D2:滚动中 (`true`) 跳过 `currentRow` 高亮写入。
|
|
13
|
+
*
|
|
14
|
+
* 行点击处理会通过 `currentRow` watcher 把 `isCurrent` 切换到新行 / 旧行上,
|
|
15
|
+
* 触发对应 `<tr>` 的 class 重绘。在滚动期间 (移动端/触控板惯性滚动常见)
|
|
16
|
+
* 意外点击不应改变高亮,因此直接跳过;滚动结束后的正常点击仍会生效。
|
|
17
|
+
*/
|
|
18
|
+
isScrolling?: Ref<boolean>
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
interface UseRowsReturned {
|
|
@@ -30,7 +38,7 @@ interface UseRowsReturned {
|
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
export function useRows(options: Options): UseRowsReturned {
|
|
33
|
-
const { props, emit } = options
|
|
41
|
+
const { props, emit, isScrolling } = options
|
|
34
42
|
|
|
35
43
|
// --- 状态定义 ---
|
|
36
44
|
|
|
@@ -217,7 +225,10 @@ export function useRows(options: Options): UseRowsReturned {
|
|
|
217
225
|
}
|
|
218
226
|
|
|
219
227
|
function handleRowClick(row: TableRow, e: MouseEvent): void {
|
|
220
|
-
|
|
228
|
+
// D2:滚动中的点击(多为惯性滚动误触)不应改变 currentRow 高亮。
|
|
229
|
+
if (!isScrolling?.value) {
|
|
230
|
+
currentRow.value = currentRow.value === row ? undefined : row
|
|
231
|
+
}
|
|
221
232
|
emit('row-click', row, e)
|
|
222
233
|
}
|
|
223
234
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o } from '@cat-kit/core'
|
|
2
2
|
import { bem, type BEM } from '@veltra/utils'
|
|
3
|
-
import { useSlots, type Ref } from 'vue'
|
|
3
|
+
import { useSlots, watch, type Ref } from 'vue'
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
TableColumnRenderContext,
|
|
@@ -87,12 +87,36 @@ export function useTable(options: Options): UseTableReturn {
|
|
|
87
87
|
return classList.join(' ')
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* B1: 单元格类名按 ColumnNode 身份缓存。
|
|
92
|
+
*
|
|
93
|
+
* 对同一列,类名只取决于 (column.fixed, column.isLastFixed, column.isFirstFixed,
|
|
94
|
+
* column.align, leftFixed, rightFixed)。其中前四个绑定在 ColumnNode 实例上,
|
|
95
|
+
* 后两个在 leftFixed/rightFixed 变化时失效缓存即可。用 WeakMap 以 ColumnNode
|
|
96
|
+
* 为键可避免字符串 key 的拼接/哈希开销,且列树重建时节点随 GC 自动释放。
|
|
97
|
+
*/
|
|
98
|
+
let cellClassCache = new WeakMap<ColumnNode, string>()
|
|
99
|
+
let headerCellClassCache = new WeakMap<ColumnNode, string>()
|
|
100
|
+
|
|
101
|
+
watch([leftFixed, rightFixed], () => {
|
|
102
|
+
cellClassCache = new WeakMap()
|
|
103
|
+
headerCellClassCache = new WeakMap()
|
|
104
|
+
})
|
|
105
|
+
|
|
90
106
|
const getCellClass = (column: ColumnNode): string => {
|
|
91
|
-
|
|
107
|
+
const cached = cellClassCache.get(column)
|
|
108
|
+
if (cached !== undefined) return cached
|
|
109
|
+
const result = getCommonClassName(column) + ` ${bem.is(column.align)}`
|
|
110
|
+
cellClassCache.set(column, result)
|
|
111
|
+
return result
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
const getHeaderCellClass = (column: ColumnNode): string => {
|
|
95
|
-
|
|
115
|
+
const cached = headerCellClassCache.get(column)
|
|
116
|
+
if (cached !== undefined) return cached
|
|
117
|
+
const result = getCommonClassName(column) + ` ${bem.is(column.headerAlign)}`
|
|
118
|
+
headerCellClassCache.set(column, result)
|
|
119
|
+
return result
|
|
96
120
|
}
|
|
97
121
|
|
|
98
122
|
const getCellCtx = (
|