vue2-client 1.20.69 → 1.20.71
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/package.json +2 -2
- package/src/base-client/components/common/XFormTable/demo.vue +0 -1
- package/src/base-client/components/common/XTable/XTable.vue +0 -42
- package/src/base-client/components/common/XTable/XTableWrapper.vue +7 -12
- package/src/base-client/plugins/shortcutMixin.js +20 -17
- package/src/base-client/utils/shortcutManager.js +3 -8
- package/src/components/STable/index.js +10 -57
- package/src/components/index.js +0 -2
- package/src/router/async/router.map.js +0 -1
- package/vue.config.js +2 -1
- package/src/components/AVirtualTable/a-virtual-table.vue +0 -658
- package/src/components/AVirtualTable/demo.vue +0 -140
- package/src/components/AVirtualTable/index.js +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue2-client",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.71",
|
|
4
4
|
"private": false,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@microsoft/fetch-event-source": "^2.0.1",
|
|
32
32
|
"@vue/babel-preset-jsx": "^1.4.0",
|
|
33
33
|
"animate.css": "^4.1.1",
|
|
34
|
-
"ant-design-vue": "
|
|
34
|
+
"ant-design-vue": "npm:@afwenming123/ant-design-vue@1.7.10",
|
|
35
35
|
"axios": "^0.27.2",
|
|
36
36
|
"clipboard": "^2.0.11",
|
|
37
37
|
"core-js": "^3.33.0",
|
|
@@ -318,32 +318,6 @@ import moment from 'moment/moment'
|
|
|
318
318
|
import XTableWrapper from './XTableWrapper.vue'
|
|
319
319
|
import { isDebugUser } from '@vue2-client/utils/common'
|
|
320
320
|
|
|
321
|
-
/**
|
|
322
|
-
* 为表格列补齐唯一 key(只写 col.key,不改 dataIndex),避免 Ant Design Table 表头 ColGroup 因 key/dataIndex 重复触发 Vue duplicate key。
|
|
323
|
-
* 冲突时在同一基准名上递增 _2、_3 …
|
|
324
|
-
*/
|
|
325
|
-
function ensureUniqueColumnKeys (columns) {
|
|
326
|
-
if (!Array.isArray(columns) || columns.length === 0) return
|
|
327
|
-
const used = Object.create(null)
|
|
328
|
-
columns.forEach((col, index) => {
|
|
329
|
-
const base = String(
|
|
330
|
-
col.key != null && col.key !== ''
|
|
331
|
-
? col.key
|
|
332
|
-
: (col.dataIndex != null && col.dataIndex !== '')
|
|
333
|
-
? col.dataIndex
|
|
334
|
-
: `col_${index}`
|
|
335
|
-
)
|
|
336
|
-
let unique = base
|
|
337
|
-
let n = 2
|
|
338
|
-
while (used[unique]) {
|
|
339
|
-
unique = `${base}_${n}`
|
|
340
|
-
n += 1
|
|
341
|
-
}
|
|
342
|
-
used[unique] = true
|
|
343
|
-
col.key = unique
|
|
344
|
-
})
|
|
345
|
-
}
|
|
346
|
-
|
|
347
321
|
export default {
|
|
348
322
|
name: 'XTable',
|
|
349
323
|
components: {
|
|
@@ -604,21 +578,6 @@ export default {
|
|
|
604
578
|
pageSizeArray: {
|
|
605
579
|
type: Array,
|
|
606
580
|
default: () => ['10', '20', '30', '40']
|
|
607
|
-
},
|
|
608
|
-
/** 虚拟表预估行高(px),透传 STable;不传则用 STable 内按 tableSize 推导 */
|
|
609
|
-
virtualItemSize: {
|
|
610
|
-
type: Number,
|
|
611
|
-
default: undefined
|
|
612
|
-
},
|
|
613
|
-
/** 虚拟表上下缓冲(像素),透传 STable;不传则用 STable 默认 */
|
|
614
|
-
virtualBuffer: {
|
|
615
|
-
type: Number,
|
|
616
|
-
default: undefined
|
|
617
|
-
},
|
|
618
|
-
/** 虚拟表滚动是否 rAF 合并,透传 STable;不传则用 STable 默认 true */
|
|
619
|
-
virtualUseRafScroll: {
|
|
620
|
-
type: Boolean,
|
|
621
|
-
default: undefined
|
|
622
581
|
}
|
|
623
582
|
},
|
|
624
583
|
computed: {
|
|
@@ -970,7 +929,6 @@ export default {
|
|
|
970
929
|
this.queryParams = queryParams
|
|
971
930
|
this.realQueryParams = realQueryParams
|
|
972
931
|
this.tableColumns = JSON.parse(JSON.stringify(tableColumns))
|
|
973
|
-
ensureUniqueColumnKeys(this.tableColumns)
|
|
974
932
|
if (this.tableColumns.length === 0) {
|
|
975
933
|
return
|
|
976
934
|
}
|
|
@@ -23,9 +23,6 @@
|
|
|
23
23
|
:size="tableContext.tableSize"
|
|
24
24
|
:components="components"
|
|
25
25
|
:rowStyleFunction="tableContext.rowStyleFunction"
|
|
26
|
-
:virtual-item-size="tableContext.virtualItemSize"
|
|
27
|
-
:virtual-buffer="tableContext.virtualBuffer"
|
|
28
|
-
:virtual-use-raf-scroll="tableContext.virtualUseRafScroll"
|
|
29
26
|
@beforeDataChange="beforeDataChange"
|
|
30
27
|
@expand="onExpand"
|
|
31
28
|
@rowClick="handleRowClick"
|
|
@@ -188,9 +185,6 @@
|
|
|
188
185
|
:size="tableContext.tableSize"
|
|
189
186
|
:components="components"
|
|
190
187
|
:rowStyleFunction="tableContext.rowStyleFunction"
|
|
191
|
-
:virtual-item-size="tableContext.virtualItemSize"
|
|
192
|
-
:virtual-buffer="tableContext.virtualBuffer"
|
|
193
|
-
:virtual-use-raf-scroll="tableContext.virtualUseRafScroll"
|
|
194
188
|
@rowClick="handleRowClick"
|
|
195
189
|
@rowDblClick="handleRowDblClick"
|
|
196
190
|
@beforeDataChange="beforeDataChange"
|
|
@@ -255,8 +249,8 @@
|
|
|
255
249
|
<!-- websocket-id (配置名称-字段名称-数据行id) -->
|
|
256
250
|
<x-web-socket-progress
|
|
257
251
|
v-if="item.webSocket"
|
|
258
|
-
:key="`${tableContext.requestParameters?.pageNo || 1}-${item.
|
|
259
|
-
:websocket-id="`${queryParamsName}-${item.
|
|
252
|
+
:key="`${tableContext.requestParameters?.pageNo || 1}-${item.dataIndex}-${record[tableContext.rowKey] || index}`"
|
|
253
|
+
:websocket-id="`${queryParamsName}-${item.dataIndex}-${record[tableContext.rowKey] || index}`"
|
|
260
254
|
:initial-value="text || 0"
|
|
261
255
|
@progress-updated="(data) => handleProgressUpdated(data, record, item.dataIndex)"
|
|
262
256
|
/>
|
|
@@ -417,9 +411,10 @@ export default {
|
|
|
417
411
|
cell: (h, props, children) => {
|
|
418
412
|
const { key, ...restProps } = props
|
|
419
413
|
// 此处的this.realTableColumns 是定义的table的表头属性变量
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
414
|
+
const col = this.realTableColumns.find((col) => {
|
|
415
|
+
const k = col.dataIndex || col.key
|
|
416
|
+
return k === key
|
|
417
|
+
})
|
|
423
418
|
if (!col || !col.width || col.slotType === 'action') {
|
|
424
419
|
if (children) {
|
|
425
420
|
return h('th', { ...restProps }, [...children])
|
|
@@ -448,7 +443,7 @@ export default {
|
|
|
448
443
|
}
|
|
449
444
|
|
|
450
445
|
const dragProps = {
|
|
451
|
-
key: col.
|
|
446
|
+
key: col.dataIndex || col.key,
|
|
452
447
|
class: 'table-draggable-handle',
|
|
453
448
|
attrs: {
|
|
454
449
|
w: 10,
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { shortcutManager } from '@vue2-client/base-client/utils/shortcutManager'
|
|
25
|
-
import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
|
|
25
|
+
// import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
|
|
26
26
|
|
|
27
27
|
const log = (env => {
|
|
28
28
|
if (env !== 'development') return () => {}
|
|
@@ -356,23 +356,26 @@ export const shortcutMixin = {
|
|
|
356
356
|
log('reg', `✅ 注册 ${key} → scope=${scopeId} cellRef=${cellConfig.slotRef || '-'}`)
|
|
357
357
|
},
|
|
358
358
|
|
|
359
|
+
// createShortcutContext(cellConfig, eventConfig, eventIndex, shortcutKey) {
|
|
360
|
+
// return {
|
|
361
|
+
// ...this,
|
|
362
|
+
// getComponent: (ref) => this.shortcutGetComponent(ref),
|
|
363
|
+
// findComponent: (ref) => this.shortcutFindComponent(ref),
|
|
364
|
+
// cellConfig,
|
|
365
|
+
// eventConfig,
|
|
366
|
+
// eventIndex,
|
|
367
|
+
// slotRef: cellConfig.slotRef,
|
|
368
|
+
// shortcutKey, // ✅ 新增:触发的快捷键
|
|
369
|
+
// triggerEvent: (type, ...args) => {
|
|
370
|
+
// const event = cellConfig.events?.find(e => e.type === type)
|
|
371
|
+
// if (event?.customFunction) {
|
|
372
|
+
// executeStrFunctionByContext(this, event.customFunction, args)
|
|
373
|
+
// }
|
|
374
|
+
// }
|
|
375
|
+
// }
|
|
376
|
+
// },
|
|
359
377
|
createShortcutContext(cellConfig, eventConfig, eventIndex, shortcutKey) {
|
|
360
|
-
return
|
|
361
|
-
...this,
|
|
362
|
-
getComponent: (ref) => this.shortcutGetComponent(ref),
|
|
363
|
-
findComponent: (ref) => this.shortcutFindComponent(ref),
|
|
364
|
-
cellConfig,
|
|
365
|
-
eventConfig,
|
|
366
|
-
eventIndex,
|
|
367
|
-
slotRef: cellConfig.slotRef,
|
|
368
|
-
shortcutKey, // ✅ 新增:触发的快捷键
|
|
369
|
-
triggerEvent: (type, ...args) => {
|
|
370
|
-
const event = cellConfig.events?.find(e => e.type === type)
|
|
371
|
-
if (event?.customFunction) {
|
|
372
|
-
executeStrFunctionByContext(this, event.customFunction, args)
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
378
|
+
return this
|
|
376
379
|
},
|
|
377
380
|
|
|
378
381
|
// ========== 组件查找 ==========
|
|
@@ -757,7 +757,7 @@ class ShortcutManager {
|
|
|
757
757
|
if (!normalizedKey) return
|
|
758
758
|
|
|
759
759
|
const entry = {
|
|
760
|
-
action, scope, context, cellRef, isActive, instanceId,
|
|
760
|
+
action, scope, context, cellRef, isActive, instanceId, key,
|
|
761
761
|
registeredAt: Date.now()
|
|
762
762
|
}
|
|
763
763
|
|
|
@@ -918,16 +918,11 @@ class ShortcutManager {
|
|
|
918
918
|
* @param {KeyboardEvent} keyboardEvent 原始键盘事件
|
|
919
919
|
*/
|
|
920
920
|
executeEvent(entry, keyboardEvent) {
|
|
921
|
-
const { action, context } = entry
|
|
921
|
+
const { action, context, key } = entry
|
|
922
922
|
if (!action?.customFunction) return
|
|
923
923
|
|
|
924
924
|
try {
|
|
925
|
-
|
|
926
|
-
executeStrFunctionByContext(
|
|
927
|
-
context,
|
|
928
|
-
action.customFunction,
|
|
929
|
-
[keyboardEvent, context.shortcutKey || entry.key]
|
|
930
|
-
)
|
|
925
|
+
executeStrFunctionByContext(context, action.customFunction, [keyboardEvent, key])
|
|
931
926
|
} catch (err) {
|
|
932
927
|
log('error', `❌ 执行失败: ${err.message}`)
|
|
933
928
|
}
|
|
@@ -2,28 +2,8 @@ import T from 'ant-design-vue/es/table/Table'
|
|
|
2
2
|
import get from 'lodash.get'
|
|
3
3
|
import { mapState } from 'vuex'
|
|
4
4
|
import { executeStrFunctionByContext } from '@vue2-client/utils/runEvalFunction'
|
|
5
|
-
import AVirtualTable from '@vue2-client/components/AVirtualTable'
|
|
6
|
-
|
|
7
|
-
/** 仅这些键走 AVirtualTable 的声明式 props;其余(scroll、rowKey、rowSelection 等)必须走 attrs 才能进入 $attrs 并透传到内部 a-table */
|
|
8
|
-
const VTABLE_OWN_PROP_KEYS = new Set([
|
|
9
|
-
'columns',
|
|
10
|
-
'dataSource',
|
|
11
|
-
'keyProp',
|
|
12
|
-
'itemSize',
|
|
13
|
-
'scrollBox',
|
|
14
|
-
'buffer',
|
|
15
|
-
'throttleTime',
|
|
16
|
-
'dynamic',
|
|
17
|
-
'virtualized',
|
|
18
|
-
'autoVirtualizeThreshold',
|
|
19
|
-
'isTree',
|
|
20
|
-
'useRafScroll'
|
|
21
|
-
])
|
|
22
5
|
|
|
23
6
|
export default {
|
|
24
|
-
components: {
|
|
25
|
-
AVirtualTable
|
|
26
|
-
},
|
|
27
7
|
data() {
|
|
28
8
|
return {
|
|
29
9
|
clickedRowKey: null,
|
|
@@ -142,21 +122,6 @@ export default {
|
|
|
142
122
|
pageSizeArray: {
|
|
143
123
|
type: Array,
|
|
144
124
|
default: () => ['10', '20', '30', '40']
|
|
145
|
-
},
|
|
146
|
-
/** 虚拟表预估行高(px);不传则 small→48,其余→54(与 Ant Design 行高接近,减轻 offsetMap 累计误差) */
|
|
147
|
-
virtualItemSize: {
|
|
148
|
-
type: Number,
|
|
149
|
-
default: null
|
|
150
|
-
},
|
|
151
|
-
/** 虚拟表上下缓冲(像素),增大可减少快速滚动露白,略增 DOM */
|
|
152
|
-
virtualBuffer: {
|
|
153
|
-
type: Number,
|
|
154
|
-
default: 500
|
|
155
|
-
},
|
|
156
|
-
/** 虚拟表滚动用 rAF 合并更新(默认 true) */
|
|
157
|
-
virtualUseRafScroll: {
|
|
158
|
-
type: Boolean,
|
|
159
|
-
default: true
|
|
160
125
|
}
|
|
161
126
|
}),
|
|
162
127
|
computed: {
|
|
@@ -657,6 +622,13 @@ export default {
|
|
|
657
622
|
})
|
|
658
623
|
// 取消原有的分页组件 产品设计分页组件和汇总在一行 重新分页逻辑
|
|
659
624
|
props.pagination = false
|
|
625
|
+
const tableData = Array.isArray(props.dataSource) ? props.dataSource : []
|
|
626
|
+
if (props.scroll && props.scroll.y && tableData.length > 40) {
|
|
627
|
+
props.scroll = {
|
|
628
|
+
...props.scroll,
|
|
629
|
+
virtual: { itemHeight: 60, overscan: 8 }
|
|
630
|
+
}
|
|
631
|
+
}
|
|
660
632
|
|
|
661
633
|
// 使用 rowClassName 实现响应式行样式
|
|
662
634
|
props.rowClassName = (record, index) => {
|
|
@@ -853,35 +825,16 @@ export default {
|
|
|
853
825
|
</a-col>
|
|
854
826
|
</a-row>
|
|
855
827
|
)
|
|
856
|
-
const tableSize = this.size || 'default'
|
|
857
|
-
const resolvedVirtualItemSize =
|
|
858
|
-
this.virtualItemSize != null ? this.virtualItemSize : tableSize === 'small' ? 48 : 55
|
|
859
|
-
const vtableOwnProps = {
|
|
860
|
-
keyProp: '__v_key',
|
|
861
|
-
autoVirtualizeThreshold: 40,
|
|
862
|
-
itemSize: resolvedVirtualItemSize,
|
|
863
|
-
buffer: this.virtualBuffer,
|
|
864
|
-
useRafScroll: this.virtualUseRafScroll
|
|
865
|
-
}
|
|
866
|
-
const vtableAttrs = {}
|
|
867
|
-
Object.keys(props).forEach(k => {
|
|
868
|
-
if (VTABLE_OWN_PROP_KEYS.has(k)) {
|
|
869
|
-
vtableOwnProps[k] = props[k]
|
|
870
|
-
} else {
|
|
871
|
-
vtableAttrs[k] = props[k]
|
|
872
|
-
}
|
|
873
|
-
})
|
|
874
828
|
const table = (
|
|
875
|
-
<
|
|
876
|
-
|
|
877
|
-
{...{ props: vtableOwnProps, attrs: vtableAttrs, scopedSlots: { ...this.$scopedSlots } }}
|
|
829
|
+
<a-table
|
|
830
|
+
{...{ props, scopedSlots: { ...this.$scopedSlots } }}
|
|
878
831
|
onChange={this.loadData}
|
|
879
832
|
onExpand={(expanded, record) => this.onExpand(expanded, record)}
|
|
880
833
|
>
|
|
881
834
|
{Object.keys(this.$slots).map(name => (
|
|
882
835
|
<template slot={name}>{this.$slots[name]}</template>
|
|
883
836
|
))}
|
|
884
|
-
</
|
|
837
|
+
</a-table>
|
|
885
838
|
)
|
|
886
839
|
return (
|
|
887
840
|
<div class="table-wrapper">
|
package/src/components/index.js
CHANGED
|
@@ -16,7 +16,6 @@ import Trend from '@vue2-client/components/Trend'
|
|
|
16
16
|
import Ellipsis from '@vue2-client/components/Ellipsis'
|
|
17
17
|
import NumberInfo from '@vue2-client/components/NumberInfo'
|
|
18
18
|
import STable from '@vue2-client/components/STable'
|
|
19
|
-
import AVirtualTable from '@vue2-client/components/AVirtualTable'
|
|
20
19
|
import XScrollBox from '@vue2-client/components/xScrollBox'
|
|
21
20
|
|
|
22
21
|
export {
|
|
@@ -34,7 +33,6 @@ export {
|
|
|
34
33
|
Ellipsis,
|
|
35
34
|
NumberInfo,
|
|
36
35
|
STable,
|
|
37
|
-
AVirtualTable,
|
|
38
36
|
Trend,
|
|
39
37
|
XScrollBox
|
|
40
38
|
}
|
|
@@ -59,7 +59,6 @@ routerResource.example = {
|
|
|
59
59
|
// component: () => import('@vue2-client/base-client/components/his/HChart/demo.vue'),
|
|
60
60
|
// component: () => import('@vue2-client/pages/WorkflowDetail/WorkFlowDemo.vue'),
|
|
61
61
|
component: () => import('@vue2-client/base-client/components/common/XFormTable/demo.vue')
|
|
62
|
-
// component: () => import('@vue2-client/components/AVirtualTable/demo.vue')
|
|
63
62
|
// component: () => import('@vue2-client/base-client/components/common/AfMap/demo.vue')
|
|
64
63
|
// component: () => import('@vue2-client/base-client/components/common/ImagePreviewModal/demo.vue'),
|
|
65
64
|
// component: () => import('@vue2-client/base-client/components/common/XImagePreview/demo.vue'),
|
package/vue.config.js
CHANGED
|
@@ -1,658 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import throttle from 'lodash/throttle'
|
|
3
|
-
import Checkbox from 'ant-design-vue/lib/checkbox'
|
|
4
|
-
import Table from 'ant-design-vue/lib/table'
|
|
5
|
-
|
|
6
|
-
function isScroller (el) {
|
|
7
|
-
const style = window.getComputedStyle(el, null)
|
|
8
|
-
const scrollValues = ['auto', 'scroll']
|
|
9
|
-
return scrollValues.includes(style.overflow) || scrollValues.includes(style['overflow-y'])
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function getParentScroller (el) {
|
|
13
|
-
let parent = el
|
|
14
|
-
while (parent) {
|
|
15
|
-
if ([window, document, document.documentElement].includes(parent)) {
|
|
16
|
-
return window
|
|
17
|
-
}
|
|
18
|
-
if (isScroller(parent)) {
|
|
19
|
-
return parent
|
|
20
|
-
}
|
|
21
|
-
parent = parent.parentNode
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return parent || window
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getScrollTop (el) {
|
|
28
|
-
return el === window ? window.pageYOffset : el.scrollTop
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function getScrollLeft (el) {
|
|
32
|
-
return el === window ? window.pageXOffset : el.scrollLeft
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function setScrollTop (el, pos) {
|
|
36
|
-
return el === window ? window.scroll(window.pageXOffset, pos) : (el.scrollTop = pos)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function setScrollLeft (el, pos) {
|
|
40
|
-
return el === window ? window.scroll(pos, window.pageYOffset) : (el.scrollLeft = pos)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getOffsetHeight (el) {
|
|
44
|
-
return el === window ? window.innerHeight : el.offsetHeight
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function scrollToY (el, y) {
|
|
48
|
-
if (el === window) {
|
|
49
|
-
window.scroll(0, y)
|
|
50
|
-
} else {
|
|
51
|
-
el.scrollTop = y
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function hasScrollX (el) {
|
|
56
|
-
return el.scrollWidth > el.clientWidth
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const TableBodyClassNames = ['.ant-table-scroll .ant-table-body', '.ant-table-fixed-left .ant-table-body-inner', '.ant-table-fixed-right .ant-table-body-inner']
|
|
60
|
-
|
|
61
|
-
let checkOrder = 0
|
|
62
|
-
|
|
63
|
-
export default {
|
|
64
|
-
inheritAttrs: false,
|
|
65
|
-
name: 'AVirtualTable',
|
|
66
|
-
components: {
|
|
67
|
-
ACheckbox: Checkbox,
|
|
68
|
-
ATable: Table
|
|
69
|
-
},
|
|
70
|
-
props: {
|
|
71
|
-
dataSource: {
|
|
72
|
-
type: Array,
|
|
73
|
-
default: () => []
|
|
74
|
-
},
|
|
75
|
-
columns: {
|
|
76
|
-
type: Array,
|
|
77
|
-
default: () => []
|
|
78
|
-
},
|
|
79
|
-
keyProp: {
|
|
80
|
-
type: String,
|
|
81
|
-
default: 'id'
|
|
82
|
-
},
|
|
83
|
-
itemSize: {
|
|
84
|
-
type: Number,
|
|
85
|
-
default: 60
|
|
86
|
-
},
|
|
87
|
-
scrollBox: {
|
|
88
|
-
type: String,
|
|
89
|
-
default: undefined
|
|
90
|
-
},
|
|
91
|
-
buffer: {
|
|
92
|
-
type: Number,
|
|
93
|
-
default: 100
|
|
94
|
-
},
|
|
95
|
-
/** 为 false 时用 lodash throttle(throttleTime);为 true 时用 rAF 合并滚动回调,跟手更好 */
|
|
96
|
-
useRafScroll: {
|
|
97
|
-
type: Boolean,
|
|
98
|
-
default: true
|
|
99
|
-
},
|
|
100
|
-
throttleTime: {
|
|
101
|
-
type: Number,
|
|
102
|
-
default: 10
|
|
103
|
-
},
|
|
104
|
-
dynamic: {
|
|
105
|
-
type: Boolean,
|
|
106
|
-
default: true
|
|
107
|
-
},
|
|
108
|
-
virtualized: {
|
|
109
|
-
type: Boolean,
|
|
110
|
-
default: true
|
|
111
|
-
},
|
|
112
|
-
/** 数据行数大于该数值时启用虚拟滚动(默认 40 表示 41 行及以上) */
|
|
113
|
-
autoVirtualizeThreshold: {
|
|
114
|
-
type: Number,
|
|
115
|
-
default: 40
|
|
116
|
-
},
|
|
117
|
-
isTree: {
|
|
118
|
-
type: Boolean,
|
|
119
|
-
default: false
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
provide () {
|
|
123
|
-
return {
|
|
124
|
-
virtualTable: this
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
data () {
|
|
128
|
-
return {
|
|
129
|
-
start: 0,
|
|
130
|
-
end: undefined,
|
|
131
|
-
sizes: {},
|
|
132
|
-
renderData: [],
|
|
133
|
-
isCheckedAll: false,
|
|
134
|
-
isCheckedImn: false,
|
|
135
|
-
isHideAppend: false
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
computed: {
|
|
139
|
-
/** virtualized 为 false 时关闭;否则数据行数「大于」阈值时启用(默认 40 即至少 41 行) */
|
|
140
|
-
effectiveVirtualized () {
|
|
141
|
-
return this.virtualized && this.dataSource.length > this.autoVirtualizeThreshold
|
|
142
|
-
},
|
|
143
|
-
tableColumns () {
|
|
144
|
-
return this.columns.map(column => {
|
|
145
|
-
if (column.type === 'selection') {
|
|
146
|
-
return {
|
|
147
|
-
title: () => {
|
|
148
|
-
return (
|
|
149
|
-
<a-checkbox
|
|
150
|
-
checked={this.isCheckedAll}
|
|
151
|
-
indeterminate={this.isCheckedImn}
|
|
152
|
-
onchange={() => this.onCheckAllRows(!this.isCheckedAll)}>
|
|
153
|
-
</a-checkbox>
|
|
154
|
-
)
|
|
155
|
-
},
|
|
156
|
-
customRender: (text, row) => {
|
|
157
|
-
return (
|
|
158
|
-
<a-checkbox
|
|
159
|
-
checked={row.$v_checked}
|
|
160
|
-
onchange={() => this.onCheckRow(row, !row.$v_checked)}>
|
|
161
|
-
</a-checkbox>
|
|
162
|
-
)
|
|
163
|
-
},
|
|
164
|
-
width: 60,
|
|
165
|
-
...column
|
|
166
|
-
}
|
|
167
|
-
} else if (column.index) {
|
|
168
|
-
return {
|
|
169
|
-
customRender: (text, row, index) => {
|
|
170
|
-
const curIndex = this.start + index
|
|
171
|
-
return typeof column.index === 'function' ? column.index(curIndex) : curIndex + 1
|
|
172
|
-
},
|
|
173
|
-
...column
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return column
|
|
177
|
-
})
|
|
178
|
-
},
|
|
179
|
-
offsetMap () {
|
|
180
|
-
if (!this.dynamic) return {}
|
|
181
|
-
|
|
182
|
-
const { keyProp, itemSize, sizes, dataSource } = this
|
|
183
|
-
const res = {}
|
|
184
|
-
let total = 0
|
|
185
|
-
for (let i = 0; i < dataSource.length; i++) {
|
|
186
|
-
const key = dataSource[i][keyProp]
|
|
187
|
-
res[key] = total
|
|
188
|
-
|
|
189
|
-
const curSize = sizes[key]
|
|
190
|
-
const size = typeof curSize === 'number' ? curSize : itemSize
|
|
191
|
-
total += size
|
|
192
|
-
}
|
|
193
|
-
return res
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
methods: {
|
|
197
|
-
initData () {
|
|
198
|
-
this.isInnerScroll = false
|
|
199
|
-
this.scrollPos = [0, 0, 0, 0]
|
|
200
|
-
this.isDeactivated = false
|
|
201
|
-
this._scrollRafId = null
|
|
202
|
-
|
|
203
|
-
this.scroller = this.getScroller()
|
|
204
|
-
this.setToTop()
|
|
205
|
-
this.recordTablePos()
|
|
206
|
-
|
|
207
|
-
this.handleScroll()
|
|
208
|
-
this.$nextTick(() => {
|
|
209
|
-
this.handleScroll()
|
|
210
|
-
})
|
|
211
|
-
// 滚动:rAF 每帧最多更新一次,减少快速滚动时空白露底;resize 同步调度
|
|
212
|
-
this._throttledFallbackScroll = throttle(this.handleScroll, this.throttleTime)
|
|
213
|
-
if (this.useRafScroll) {
|
|
214
|
-
this.onScroll = this.scheduleScrollUpdate
|
|
215
|
-
this.onResizeOrScroll = this.scheduleScrollUpdate
|
|
216
|
-
} else {
|
|
217
|
-
this.onScroll = this._throttledFallbackScroll
|
|
218
|
-
this.onResizeOrScroll = this._throttledFallbackScroll
|
|
219
|
-
}
|
|
220
|
-
this.scroller.addEventListener('scroll', this.onScroll, { passive: true })
|
|
221
|
-
window.addEventListener('resize', this.onResizeOrScroll)
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
/** 将多次 scroll 合并到下一帧,贴近显示器刷新,减轻「滚过了行才贴上」的体感 */
|
|
225
|
-
scheduleScrollUpdate () {
|
|
226
|
-
if (this._scrollRafId != null) return
|
|
227
|
-
this._scrollRafId = requestAnimationFrame(() => {
|
|
228
|
-
this._scrollRafId = null
|
|
229
|
-
this.handleScroll()
|
|
230
|
-
})
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
cancelPendingScrollRaf () {
|
|
234
|
-
if (this._scrollRafId == null) return
|
|
235
|
-
cancelAnimationFrame(this._scrollRafId)
|
|
236
|
-
this._scrollRafId = null
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
setToTop () {
|
|
240
|
-
if (this.isInnerScroll) {
|
|
241
|
-
this.toTop = 0
|
|
242
|
-
} else {
|
|
243
|
-
this.toTop = this.$el.getBoundingClientRect().top - (this.scroller === window ? 0 : this.scroller.getBoundingClientRect().top) + getScrollTop(this.scroller)
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
getScroller () {
|
|
248
|
-
let el
|
|
249
|
-
if (this.scrollBox) {
|
|
250
|
-
if (this.scrollBox === 'window' || this.scrollBox === window) return window
|
|
251
|
-
|
|
252
|
-
el = document.querySelector(this.scrollBox)
|
|
253
|
-
if (!el) throw new Error(` scrollBox prop: '${this.scrollBox}' is not a valid selector`)
|
|
254
|
-
if (!isScroller(el)) {
|
|
255
|
-
// eslint-disable-next-line no-console
|
|
256
|
-
console.warn(`Warning! scrollBox prop: '${this.scrollBox}' is not a scroll element`)
|
|
257
|
-
}
|
|
258
|
-
return el
|
|
259
|
-
}
|
|
260
|
-
if (this.$attrs.scroll && this.$attrs.scroll.y) {
|
|
261
|
-
this.isInnerScroll = true
|
|
262
|
-
return this.$el.querySelector('.ant-table-body')
|
|
263
|
-
} else {
|
|
264
|
-
return getParentScroller(this.$el)
|
|
265
|
-
}
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
handleScroll () {
|
|
269
|
-
if (this.isDeactivated) return
|
|
270
|
-
this.scrollPos[0] = getScrollTop(this.scroller)
|
|
271
|
-
this.scrollPos[1] = getScrollLeft(this.scroller)
|
|
272
|
-
|
|
273
|
-
if (!this.effectiveVirtualized) return
|
|
274
|
-
|
|
275
|
-
this.updateSizes()
|
|
276
|
-
this.calcRenderData()
|
|
277
|
-
this.calcPosition()
|
|
278
|
-
},
|
|
279
|
-
|
|
280
|
-
updateSizes () {
|
|
281
|
-
if (!this.dynamic) return
|
|
282
|
-
|
|
283
|
-
let rows = []
|
|
284
|
-
if (this.isTree) {
|
|
285
|
-
rows = this.$el.querySelectorAll('.ant-table-body .ant-table-row-level-0')
|
|
286
|
-
} else {
|
|
287
|
-
rows = this.$el.querySelectorAll('.ant-table-body .ant-table-tbody .ant-table-row')
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
Array.from(rows).forEach((row, index) => {
|
|
291
|
-
const item = this.renderData[index]
|
|
292
|
-
if (!item) return
|
|
293
|
-
|
|
294
|
-
let offsetHeight = row.offsetHeight
|
|
295
|
-
const nextEl = row.nextSibling
|
|
296
|
-
if (nextEl && nextEl.classList && nextEl.classList.contains('ant-table-expanded-row')) {
|
|
297
|
-
offsetHeight += row.nextSibling.offsetHeight
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (this.isTree) {
|
|
301
|
-
let next = row.nextSibling
|
|
302
|
-
while (next && next !== rows[index + 1]) {
|
|
303
|
-
offsetHeight += next?.offsetHeight || 0
|
|
304
|
-
next = next.nextSibling
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const key = item[this.keyProp]
|
|
309
|
-
if (this.sizes[key] !== offsetHeight) {
|
|
310
|
-
this.$set(this.sizes, key, offsetHeight)
|
|
311
|
-
row._offsetHeight = offsetHeight
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
},
|
|
315
|
-
|
|
316
|
-
calcRenderData () {
|
|
317
|
-
const { scroller, buffer, dataSource: data } = this
|
|
318
|
-
const top = this.scrollPos[0] - buffer - this.toTop
|
|
319
|
-
// 兜底:scroll.y 可能是数字,也可能是 'calc(...)' / '300px' 等 CSS 字符串
|
|
320
|
-
// 字符串场景下无法直接参与算术,需要退化为从真实 DOM 读取实际可视高度
|
|
321
|
-
let scrollerHeight
|
|
322
|
-
if (this.isInnerScroll) {
|
|
323
|
-
const rawY = this.$attrs.scroll && this.$attrs.scroll.y
|
|
324
|
-
if (typeof rawY === 'number' && !isNaN(rawY)) {
|
|
325
|
-
scrollerHeight = rawY
|
|
326
|
-
} else if (scroller && typeof scroller.clientHeight === 'number') {
|
|
327
|
-
scrollerHeight = scroller.clientHeight
|
|
328
|
-
} else {
|
|
329
|
-
scrollerHeight = 0
|
|
330
|
-
}
|
|
331
|
-
} else {
|
|
332
|
-
scrollerHeight = getOffsetHeight(scroller)
|
|
333
|
-
}
|
|
334
|
-
const bottom = this.scrollPos[0] + scrollerHeight + buffer - this.toTop
|
|
335
|
-
|
|
336
|
-
let start
|
|
337
|
-
let end
|
|
338
|
-
if (!this.dynamic) {
|
|
339
|
-
start = top <= 0 ? 0 : Math.floor(top / this.itemSize)
|
|
340
|
-
end = bottom <= 0 ? 0 : Math.ceil(bottom / this.itemSize)
|
|
341
|
-
} else {
|
|
342
|
-
let l = 0
|
|
343
|
-
let r = data.length - 1
|
|
344
|
-
let mid = 0
|
|
345
|
-
while (l <= r) {
|
|
346
|
-
mid = Math.floor((l + r) / 2)
|
|
347
|
-
const midVal = this.getItemOffsetTop(mid)
|
|
348
|
-
if (midVal < top) {
|
|
349
|
-
const midNextVal = this.getItemOffsetTop(mid + 1)
|
|
350
|
-
if (midNextVal > top) break
|
|
351
|
-
l = mid + 1
|
|
352
|
-
} else {
|
|
353
|
-
r = mid - 1
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
start = mid
|
|
358
|
-
end = data.length - 1
|
|
359
|
-
for (let i = start + 1; i < data.length; i++) {
|
|
360
|
-
const offsetTop = this.getItemOffsetTop(i)
|
|
361
|
-
if (offsetTop >= bottom) {
|
|
362
|
-
end = i
|
|
363
|
-
break
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (start % 2) {
|
|
369
|
-
start = start - 1
|
|
370
|
-
}
|
|
371
|
-
this.top = top
|
|
372
|
-
this.bottom = bottom
|
|
373
|
-
this.start = start
|
|
374
|
-
this.end = end
|
|
375
|
-
this.renderData = data.slice(start, end + 1)
|
|
376
|
-
this.$emit('renderChange', this.renderData, this.start, this.end)
|
|
377
|
-
},
|
|
378
|
-
|
|
379
|
-
calcPosition () {
|
|
380
|
-
const len = this.dataSource.length
|
|
381
|
-
const last = len - 1
|
|
382
|
-
const wrapHeight = this.getItemOffsetTop(last) + this.getItemSize(last)
|
|
383
|
-
const offsetTop = this.getItemOffsetTop(this.start)
|
|
384
|
-
|
|
385
|
-
TableBodyClassNames.forEach(className => {
|
|
386
|
-
const el = this.$el.querySelector(className)
|
|
387
|
-
if (!el) return
|
|
388
|
-
|
|
389
|
-
if (!el.wrapEl) {
|
|
390
|
-
const wrapEl = document.createElement('div')
|
|
391
|
-
const innerEl = document.createElement('div')
|
|
392
|
-
wrapEl.appendChild(innerEl)
|
|
393
|
-
innerEl.appendChild(el.children[0])
|
|
394
|
-
el.insertBefore(wrapEl, el.firstChild)
|
|
395
|
-
el.wrapEl = wrapEl
|
|
396
|
-
el.innerEl = innerEl
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (el.wrapEl) {
|
|
400
|
-
el.wrapEl.style.display = len === 0 ? 'block' : 'inline-block'
|
|
401
|
-
el.innerEl.style.display = len === 0 ? 'block' : 'inline-block'
|
|
402
|
-
|
|
403
|
-
if (this.isInnerScroll && !hasScrollX(this.scroller)) {
|
|
404
|
-
el.wrapEl.style.width = '100%'
|
|
405
|
-
el.innerEl.style.width = '100%'
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
el.wrapEl.style.height = wrapHeight + 'px'
|
|
409
|
-
el.innerEl.style.transform = `translateY(${offsetTop}px)`
|
|
410
|
-
}
|
|
411
|
-
})
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
getItemOffsetTop (index) {
|
|
415
|
-
if (!this.dynamic) {
|
|
416
|
-
return this.itemSize * index
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const item = this.dataSource[index]
|
|
420
|
-
if (item) {
|
|
421
|
-
return this.offsetMap[item[this.keyProp]] || 0
|
|
422
|
-
}
|
|
423
|
-
return 0
|
|
424
|
-
},
|
|
425
|
-
|
|
426
|
-
getItemSize (index) {
|
|
427
|
-
if (index <= -1) return 0
|
|
428
|
-
const item = this.dataSource[index]
|
|
429
|
-
if (item) {
|
|
430
|
-
const key = item[this.keyProp]
|
|
431
|
-
return this.sizes[key] || this.itemSize
|
|
432
|
-
}
|
|
433
|
-
return this.itemSize
|
|
434
|
-
},
|
|
435
|
-
|
|
436
|
-
update () {
|
|
437
|
-
this.setToTop()
|
|
438
|
-
this.handleScroll()
|
|
439
|
-
},
|
|
440
|
-
|
|
441
|
-
scrollTo (index, offsetY = 0, stop = false) {
|
|
442
|
-
const item = this.dataSource[index]
|
|
443
|
-
if (item && this.scroller) {
|
|
444
|
-
this.updateSizes()
|
|
445
|
-
this.calcRenderData()
|
|
446
|
-
|
|
447
|
-
this.$nextTick(() => {
|
|
448
|
-
const offsetTop = this.getItemOffsetTop(index) - offsetY
|
|
449
|
-
scrollToY(this.scroller, offsetTop)
|
|
450
|
-
|
|
451
|
-
if (!stop) {
|
|
452
|
-
setTimeout(() => {
|
|
453
|
-
this.scrollTo(index, offsetY, true)
|
|
454
|
-
}, 50)
|
|
455
|
-
}
|
|
456
|
-
})
|
|
457
|
-
}
|
|
458
|
-
},
|
|
459
|
-
|
|
460
|
-
renderAllData () {
|
|
461
|
-
this.renderData = this.dataSource
|
|
462
|
-
const len = this.dataSource.length
|
|
463
|
-
const last = len > 0 ? len - 1 : 0
|
|
464
|
-
this.$emit('renderChange', this.dataSource, 0, last)
|
|
465
|
-
|
|
466
|
-
this.$nextTick(() => {
|
|
467
|
-
TableBodyClassNames.forEach(className => {
|
|
468
|
-
const el = this.$el.querySelector(className)
|
|
469
|
-
if (!el) return
|
|
470
|
-
|
|
471
|
-
if (el.wrapEl) {
|
|
472
|
-
el.wrapEl.style.height = 'auto'
|
|
473
|
-
el.innerEl.style.transform = `translateY(${0}px)`
|
|
474
|
-
}
|
|
475
|
-
})
|
|
476
|
-
})
|
|
477
|
-
},
|
|
478
|
-
|
|
479
|
-
doUpdate () {
|
|
480
|
-
if (this.hasDoUpdate) return
|
|
481
|
-
if (!this.scroller) return
|
|
482
|
-
|
|
483
|
-
this.isHideAppend = true
|
|
484
|
-
this.update()
|
|
485
|
-
this.hasDoUpdate = true
|
|
486
|
-
this.$nextTick(() => {
|
|
487
|
-
this.hasDoUpdate = false
|
|
488
|
-
this.isHideAppend = false
|
|
489
|
-
})
|
|
490
|
-
},
|
|
491
|
-
|
|
492
|
-
onCheckAllRows (val) {
|
|
493
|
-
val = this.isCheckedImn ? true : val
|
|
494
|
-
this.dataSource.forEach(row => {
|
|
495
|
-
if (row.$v_checked === val) return
|
|
496
|
-
|
|
497
|
-
this.$set(row, '$v_checked', val)
|
|
498
|
-
this.$set(row, '$v_checkedOrder', val ? checkOrder++ : undefined)
|
|
499
|
-
})
|
|
500
|
-
this.isCheckedAll = val
|
|
501
|
-
this.isCheckedImn = false
|
|
502
|
-
this.emitSelectionChange()
|
|
503
|
-
if (val === false) checkOrder = 0
|
|
504
|
-
},
|
|
505
|
-
|
|
506
|
-
onCheckRow (row, val) {
|
|
507
|
-
if (row.$v_checked === val) return
|
|
508
|
-
|
|
509
|
-
this.$set(row, '$v_checked', val)
|
|
510
|
-
this.$set(row, '$v_checkedOrder', val ? checkOrder++ : undefined)
|
|
511
|
-
|
|
512
|
-
const checkedLen = this.dataSource.filter(row => row.$v_checked === true).length
|
|
513
|
-
if (checkedLen === 0) {
|
|
514
|
-
this.isCheckedAll = false
|
|
515
|
-
this.isCheckedImn = false
|
|
516
|
-
} else if (checkedLen === this.dataSource.length) {
|
|
517
|
-
this.isCheckedAll = true
|
|
518
|
-
this.isCheckedImn = false
|
|
519
|
-
} else {
|
|
520
|
-
this.isCheckedAll = false
|
|
521
|
-
this.isCheckedImn = true
|
|
522
|
-
}
|
|
523
|
-
this.emitSelectionChange()
|
|
524
|
-
},
|
|
525
|
-
|
|
526
|
-
emitSelectionChange () {
|
|
527
|
-
const selection = this.dataSource.filter(row => row.$v_checked).sort((a, b) => a.$v_checkedOrder - b.$v_checkedOrder)
|
|
528
|
-
this.$emit('selection-change', selection)
|
|
529
|
-
},
|
|
530
|
-
|
|
531
|
-
toggleRowSelection (row, selected) {
|
|
532
|
-
const val = typeof selected === 'boolean' ? selected : !row.$v_checked
|
|
533
|
-
this.onCheckRow(row, val)
|
|
534
|
-
},
|
|
535
|
-
|
|
536
|
-
clearSelection () {
|
|
537
|
-
this.isCheckedImn = false
|
|
538
|
-
this.onCheckAllRows(false)
|
|
539
|
-
},
|
|
540
|
-
|
|
541
|
-
recordTablePos () {
|
|
542
|
-
if (!this.isInnerScroll) return
|
|
543
|
-
|
|
544
|
-
this.tableBodyEl = this.$el.querySelector('.ant-table-body')
|
|
545
|
-
this.onTableScroll = throttle(() => {
|
|
546
|
-
this.scrollPos[2] = getScrollTop(this.tableBodyEl)
|
|
547
|
-
this.scrollPos[3] = getScrollLeft(this.tableBodyEl)
|
|
548
|
-
}, 100)
|
|
549
|
-
this.tableBodyEl.addEventListener('scroll', this.onTableScroll, { passive: true })
|
|
550
|
-
},
|
|
551
|
-
|
|
552
|
-
restoreScrollY () {
|
|
553
|
-
if (!this.scroller) return
|
|
554
|
-
|
|
555
|
-
const [top, left, top2, left2] = this.scrollPos
|
|
556
|
-
setScrollTop(this.scroller, top)
|
|
557
|
-
setScrollLeft(this.scroller, left)
|
|
558
|
-
|
|
559
|
-
if (this.isInnerScroll) {
|
|
560
|
-
const leftScroller = document.querySelector(TableBodyClassNames[1])
|
|
561
|
-
const rightScroller = document.querySelector(TableBodyClassNames[2])
|
|
562
|
-
if (leftScroller) setScrollTop(leftScroller, top)
|
|
563
|
-
if (rightScroller) setScrollTop(rightScroller, top)
|
|
564
|
-
} else {
|
|
565
|
-
setScrollTop(this.tableBodyEl, top2)
|
|
566
|
-
setScrollLeft(this.tableBodyEl, left2)
|
|
567
|
-
}
|
|
568
|
-
},
|
|
569
|
-
|
|
570
|
-
updateData (data = []) {
|
|
571
|
-
this.list = data
|
|
572
|
-
this.$emit('update:dataSource', this.list)
|
|
573
|
-
},
|
|
574
|
-
|
|
575
|
-
getData () {
|
|
576
|
-
return this.list || this.dataSource
|
|
577
|
-
}
|
|
578
|
-
},
|
|
579
|
-
watch: {
|
|
580
|
-
dataSource (data, oldData) {
|
|
581
|
-
if (!this.effectiveVirtualized) {
|
|
582
|
-
this.renderAllData()
|
|
583
|
-
} else {
|
|
584
|
-
this.doUpdate()
|
|
585
|
-
}
|
|
586
|
-
if (this.list && data !== oldData) {
|
|
587
|
-
this.list = data
|
|
588
|
-
}
|
|
589
|
-
},
|
|
590
|
-
effectiveVirtualized: {
|
|
591
|
-
immediate: true,
|
|
592
|
-
handler (val) {
|
|
593
|
-
if (!val) {
|
|
594
|
-
this.renderAllData()
|
|
595
|
-
} else {
|
|
596
|
-
this.doUpdate()
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
},
|
|
601
|
-
created () {
|
|
602
|
-
this.$nextTick(() => {
|
|
603
|
-
this.initData()
|
|
604
|
-
})
|
|
605
|
-
},
|
|
606
|
-
mounted () {
|
|
607
|
-
const appendEl = this.$refs.append
|
|
608
|
-
const body = this.$el && this.$el.querySelector('.ant-table-body')
|
|
609
|
-
if (body && appendEl) {
|
|
610
|
-
body.appendChild(appendEl)
|
|
611
|
-
}
|
|
612
|
-
},
|
|
613
|
-
beforeDestroy () {
|
|
614
|
-
this.cancelPendingScrollRaf()
|
|
615
|
-
if (this.scroller) {
|
|
616
|
-
this.scroller.removeEventListener('scroll', this.onScroll)
|
|
617
|
-
window.removeEventListener('resize', this.onResizeOrScroll || this.onScroll)
|
|
618
|
-
}
|
|
619
|
-
if (this.tableBodyEl) {
|
|
620
|
-
this.tableBodyEl.removeEventListener('scroll', this.onTableScroll)
|
|
621
|
-
}
|
|
622
|
-
},
|
|
623
|
-
activated () {
|
|
624
|
-
this.isDeactivated = false
|
|
625
|
-
this.restoreScrollY()
|
|
626
|
-
},
|
|
627
|
-
deactivated () {
|
|
628
|
-
this.isDeactivated = true
|
|
629
|
-
this.cancelPendingScrollRaf()
|
|
630
|
-
},
|
|
631
|
-
render (h) {
|
|
632
|
-
const slotChildren = Object.keys(this.$slots).map(name =>
|
|
633
|
-
h('template', { slot: name, key: `named-slot-${name}` }, this.$slots[name])
|
|
634
|
-
)
|
|
635
|
-
return h('div', [
|
|
636
|
-
h(Table, {
|
|
637
|
-
props: {
|
|
638
|
-
...this.$attrs,
|
|
639
|
-
pagination: false,
|
|
640
|
-
columns: this.tableColumns,
|
|
641
|
-
dataSource: this.renderData
|
|
642
|
-
},
|
|
643
|
-
on: this.$listeners,
|
|
644
|
-
scopedSlots: this.$scopedSlots,
|
|
645
|
-
ref: 'innerTable'
|
|
646
|
-
}, slotChildren.length ? slotChildren : undefined),
|
|
647
|
-
h('div', {
|
|
648
|
-
class: 'ant-table-append',
|
|
649
|
-
ref: 'append',
|
|
650
|
-
directives: [{ name: 'show', value: !this.isHideAppend }]
|
|
651
|
-
}, this.$slots.append || [])
|
|
652
|
-
])
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
</script>
|
|
656
|
-
|
|
657
|
-
<style lang="less">
|
|
658
|
-
</style>
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="a-virtual-table-basic-demo">
|
|
3
|
-
<a-alert
|
|
4
|
-
show-icon
|
|
5
|
-
type="info"
|
|
6
|
-
message="AVirtualTable 基础示例(对齐上游 BasicDemo)"
|
|
7
|
-
style="margin-bottom: 16px"
|
|
8
|
-
>
|
|
9
|
-
<template slot="description">
|
|
10
|
-
共 {{ list.length }} 条数据;虚拟列表约渲染少量行。
|
|
11
|
-
插槽使用 <code>slot-scope="text, record, index"</code>(与 ant-design-vue Table 一致)。
|
|
12
|
-
<span v-if="renderInfo">当前切片:第 {{ renderInfo.start }}–{{ renderInfo.end }} 行,本帧 {{ renderInfo.count }} 行 DOM。</span>
|
|
13
|
-
</template>
|
|
14
|
-
</a-alert>
|
|
15
|
-
|
|
16
|
-
<a-virtual-table
|
|
17
|
-
:columns="columns"
|
|
18
|
-
:data-source="list"
|
|
19
|
-
:item-size="54"
|
|
20
|
-
key-prop="id"
|
|
21
|
-
row-key="id"
|
|
22
|
-
:buffer="280"
|
|
23
|
-
:scroll="{ x: 1300, y: 600 }"
|
|
24
|
-
@render-change="onRenderChange"
|
|
25
|
-
>
|
|
26
|
-
<span slot="customTitle"><a-icon type="smile-o" /> Name (自定义头)</span>
|
|
27
|
-
<template slot="name" slot-scope="text, record">
|
|
28
|
-
{{ text }} — id: {{ record.id }}
|
|
29
|
-
</template>
|
|
30
|
-
</a-virtual-table>
|
|
31
|
-
</div>
|
|
32
|
-
</template>
|
|
33
|
-
|
|
34
|
-
<script>
|
|
35
|
-
import AVirtualTable from '@vue2-client/components/AVirtualTable'
|
|
36
|
-
|
|
37
|
-
/** 生成与上游 demo 相近的大列表测试数据 */
|
|
38
|
-
function mockRows (count) {
|
|
39
|
-
const contents = [
|
|
40
|
-
'这是一条测试数据',
|
|
41
|
-
'君不见黄河之水天上来,奔流到海不复回。',
|
|
42
|
-
'十年生死两茫茫',
|
|
43
|
-
'寻寻觅觅,冷冷清清,凄凄惨惨戚戚。',
|
|
44
|
-
'桃花坞里桃花庵,桃花庵里桃花仙;桃花仙人种桃树,又摘桃花卖酒钱。',
|
|
45
|
-
'明月几时有,把酒问青天。',
|
|
46
|
-
'槛菊愁烟兰泣露,罗幕轻寒,',
|
|
47
|
-
'红豆生南国,春来发几枝。',
|
|
48
|
-
'黄鹂'
|
|
49
|
-
]
|
|
50
|
-
const rows = []
|
|
51
|
-
for (let i = 0; i < count; i++) {
|
|
52
|
-
rows.push({
|
|
53
|
-
id: i,
|
|
54
|
-
name: '王小虎',
|
|
55
|
-
text: contents[i % contents.length],
|
|
56
|
-
address: '上海市普陀区金沙江路 1518 弄'
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
return rows
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export default {
|
|
63
|
-
name: 'AVirtualTableBasicDemo',
|
|
64
|
-
components: {
|
|
65
|
-
AVirtualTable
|
|
66
|
-
},
|
|
67
|
-
data () {
|
|
68
|
-
return {
|
|
69
|
-
renderInfo: null,
|
|
70
|
-
columns: [
|
|
71
|
-
{
|
|
72
|
-
dataIndex: 'name',
|
|
73
|
-
key: 'name',
|
|
74
|
-
slots: { title: 'customTitle' },
|
|
75
|
-
scopedSlots: { customRender: 'name' },
|
|
76
|
-
width: 200
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
title: 'id',
|
|
80
|
-
dataIndex: 'id',
|
|
81
|
-
key: 'id',
|
|
82
|
-
width: 100
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
title: 'text',
|
|
86
|
-
dataIndex: 'text',
|
|
87
|
-
key: 'text',
|
|
88
|
-
width: 400
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
title: 'Address',
|
|
92
|
-
dataIndex: 'address',
|
|
93
|
-
key: 'address 1',
|
|
94
|
-
ellipsis: true,
|
|
95
|
-
width: 400
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
title: 'Long Column Long Column Long Column',
|
|
99
|
-
dataIndex: 'address',
|
|
100
|
-
key: 'address 2',
|
|
101
|
-
ellipsis: true,
|
|
102
|
-
width: 300
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
title: 'Long Column Long Column',
|
|
106
|
-
dataIndex: 'address',
|
|
107
|
-
key: 'address 3',
|
|
108
|
-
ellipsis: true,
|
|
109
|
-
width: 300
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
title: 'Long Column',
|
|
113
|
-
dataIndex: 'address',
|
|
114
|
-
key: 'address 4',
|
|
115
|
-
ellipsis: true,
|
|
116
|
-
width: 150,
|
|
117
|
-
fixed: 'right'
|
|
118
|
-
}
|
|
119
|
-
],
|
|
120
|
-
list: mockRows(2000)
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
methods: {
|
|
124
|
-
onRenderChange (renderData, start, end) {
|
|
125
|
-
this.renderInfo = {
|
|
126
|
-
start,
|
|
127
|
-
end,
|
|
128
|
-
count: renderData ? renderData.length : 0
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
</script>
|
|
134
|
-
|
|
135
|
-
<style lang="less" scoped>
|
|
136
|
-
.a-virtual-table-basic-demo {
|
|
137
|
-
padding: 16px;
|
|
138
|
-
max-width: 100%;
|
|
139
|
-
}
|
|
140
|
-
</style>
|