@xuekl/cli-components 1.6.1 → 1.9.145
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/PageContainer.vue +67 -32
- package/XklButton.vue +76 -18
- package/XklDatePicker.vue +115 -7
- package/XklDict.vue +109 -24
- package/XklDropdownItem.vue +111 -0
- package/XklForm.vue +427 -73
- package/XklFormInfo.vue +41 -17
- package/XklLink.vue +96 -16
- package/XklSelect.vue +349 -26
- package/XklTable.vue +104 -29
- package/XklTableV2.vue +54 -0
- package/XklUpload.vue +2 -1
- package/XklVirtualScroll.vue +332 -0
- package/XklVirtualTable.vue +161 -0
- package/package.json +2 -2
package/XklTable.vue
CHANGED
|
@@ -1,41 +1,76 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<el-table ref="
|
|
3
|
-
:header-cell-style="table.headerCellStyle || recieveConf.headerCellStyle"
|
|
4
|
-
:header-cell-class-name="table.headerCellClassName" :cell-class-name="table.cellClassName"
|
|
2
|
+
<el-table ref="XklTableRef" class="xkl-table" :data="table.virtual ? table.virtualList : table.list"
|
|
3
|
+
style="width: 100%" :header-cell-style="table.headerCellStyle || recieveConf.headerCellStyle"
|
|
4
|
+
:header-cell-class-name="table.headerCellClassName" :cell-class-name="table.cellClassName"
|
|
5
|
+
v-bind="{ ...table.element, ...$attrs }" v-on="table.events || {}">
|
|
5
6
|
<el-table-column v-if="table.expand" v-bind="table.expandConfig" type="expand" align="center">
|
|
6
7
|
<template v-slot="{ row }">
|
|
7
8
|
<slot name="expand" :row="row"></slot>
|
|
8
9
|
</template>
|
|
9
10
|
</el-table-column>
|
|
10
|
-
<el-table-column v-if="table.
|
|
11
|
-
|
|
11
|
+
<el-table-column v-if="!!table.virtual" align="center" :show-overflow-tooltip="false"
|
|
12
|
+
class-name="el-table-column--selection" fixed="left" width="50px">
|
|
13
|
+
<template #header>
|
|
14
|
+
<el-checkbox @change="virtualSelectAll()" v-model="virtualAllSelected"
|
|
15
|
+
:indeterminate="virtualIsIndeterminate"></el-checkbox>
|
|
16
|
+
</template>
|
|
17
|
+
<template #default="{ row }">
|
|
18
|
+
<el-checkbox @change="virtualSelect(row)" v-model="row.selected" @click.stop></el-checkbox>
|
|
19
|
+
</template>
|
|
20
|
+
</el-table-column>
|
|
21
|
+
<template v-else>
|
|
22
|
+
<el-table-column v-if="table.disabledSelectionPlaceholder" class-name="el-table-column--selection"
|
|
23
|
+
fixed="left" align="center" width="50px">
|
|
24
|
+
<template #header>
|
|
25
|
+
<el-checkbox :disabled="true" :checked="true"></el-checkbox>
|
|
26
|
+
</template>
|
|
27
|
+
<el-checkbox :disabled="true" :checked="true"></el-checkbox>
|
|
28
|
+
</el-table-column>
|
|
29
|
+
<el-table-column v-if="!table.disabledSelectionPlaceholder && table.selection"
|
|
30
|
+
v-bind="table.selectionConfig" :show-overflow-tooltip="false" type="selection"
|
|
31
|
+
align="center"></el-table-column>
|
|
32
|
+
</template>
|
|
12
33
|
<el-table-column v-if="table.index" type="index" align="center" v-bind="table.indexConfig"></el-table-column>
|
|
13
|
-
<el-table-column v-
|
|
14
|
-
|
|
34
|
+
<el-table-column v-if="table.customColumn" align="center" v-bind="table.customColumnConfig">
|
|
35
|
+
<template #default="{ row, $index }">
|
|
36
|
+
<slot name="customColumn" :row="row" :index="$index"></slot>
|
|
37
|
+
</template>
|
|
38
|
+
</el-table-column>
|
|
39
|
+
<el-table-column v-for="item in table.columns" :key="item.prop" :prop="item.prop" :label="item.label"
|
|
40
|
+
:width="item.width" :align="item.align || recieveConf.defaultAlignment || 'center'"
|
|
41
|
+
:sortable="item.sortable" v-bind="item.element">
|
|
15
42
|
<template v-if="item.reflect" v-slot="{ row }">
|
|
16
43
|
{{ row[item.reflect] }}
|
|
17
44
|
</template>
|
|
18
45
|
<template v-if="item.dict" v-slot="{ row }">
|
|
19
|
-
<el-tag v-if="item.tags"
|
|
46
|
+
<el-tag v-if="item.tags && dictLabel(item, row)" :key="row[item.prop]"
|
|
20
47
|
:style="{ backgroundColor: tagColor(item, row) + '22', borderColor: tagColor(item, row) + '22', color: tagColor(item, row) }">
|
|
21
48
|
{{ dictLabel(item, row) }}
|
|
22
49
|
</el-tag>
|
|
23
50
|
<span v-else>{{ dictLabel(item, row) }}</span>
|
|
24
51
|
</template>
|
|
25
52
|
<template v-else-if="item.tags" v-slot="{ row }">
|
|
26
|
-
<el-tag
|
|
53
|
+
<el-tag v-if="row[item.prop]" :key="row[item.prop]"
|
|
27
54
|
:style="{ backgroundColor: tagColor(item, row) + '22', borderColor: tagColor(item, row) + '22', color: tagColor(item, row) }">
|
|
28
55
|
{{ row[item.prop] }}
|
|
29
56
|
</el-tag>
|
|
30
57
|
</template>
|
|
31
|
-
<template v-else-if="item.
|
|
58
|
+
<template v-else-if="slots[item.prop]" v-slot="{ row, $index }">
|
|
32
59
|
<slot :name="item.prop" :row="row" :index="$index"></slot>
|
|
33
60
|
</template>
|
|
61
|
+
<!-- <template v-else-if="item.slot" v-slot="{ row, $index }">
|
|
62
|
+
<slot :name="item.prop" :row="row" :index="$index"></slot>
|
|
63
|
+
</template> -->
|
|
34
64
|
<template v-else-if="item.format" v-slot="{ row }">
|
|
35
|
-
{{moment(row[item.prop]).format(item.format)}}
|
|
65
|
+
{{ moment(row[item.prop]).format(item.format) }}
|
|
36
66
|
</template>
|
|
37
67
|
</el-table-column>
|
|
38
|
-
<el-table-column v-
|
|
68
|
+
<el-table-column v-for="item in table.endColumns" :key="item.prop" :prop="item.prop" :label="item.label"
|
|
69
|
+
:width="item.width" :align="item.align || recieveConf.defaultAlignment || 'center'"
|
|
70
|
+
:sortable="item.sortable" v-bind="item.element">
|
|
71
|
+
</el-table-column>
|
|
72
|
+
<el-table-column v-if="table.operation.operation !== false" align="center" :resizable="false"
|
|
73
|
+
v-bind="table.operation">
|
|
39
74
|
<template #default="{ row, $index }">
|
|
40
75
|
<slot name="_operation" :row="row" :index="$index"></slot>
|
|
41
76
|
</template>
|
|
@@ -44,6 +79,8 @@
|
|
|
44
79
|
<el-empty description="暂无数据"></el-empty>
|
|
45
80
|
</template>
|
|
46
81
|
</el-table>
|
|
82
|
+
|
|
83
|
+
<!-- <xkl-table-v2 v-else ref="XklTable" :table="table"></xkl-table-v2> -->
|
|
47
84
|
</template>
|
|
48
85
|
<script lang="ts">
|
|
49
86
|
export default {
|
|
@@ -51,15 +88,18 @@ export default {
|
|
|
51
88
|
}
|
|
52
89
|
</script>
|
|
53
90
|
<script setup lang="ts">
|
|
54
|
-
import
|
|
55
|
-
import
|
|
91
|
+
import { computed, onBeforeMount, onBeforeUnmount, onMounted, ref, useSlots } from "vue";
|
|
92
|
+
import useApp from "@/store/app";
|
|
93
|
+
import { tableConf } from './index'
|
|
94
|
+
import DictStore from "@/utils/DictStore";
|
|
56
95
|
import moment from 'dayjs'
|
|
57
|
-
import { baseConf, tableConf } from './index'
|
|
58
96
|
const props = defineProps(['table'])
|
|
97
|
+
const emit = defineEmits(['virtual-select', 'virtual-select-all'])
|
|
98
|
+
const slots = useSlots()
|
|
59
99
|
const { table } = props
|
|
60
100
|
const dictTypes = table.columns.map(res => res.dict).filter(res => !!res)
|
|
61
101
|
const dictStore = ref({})
|
|
62
|
-
const
|
|
102
|
+
const XklTableRef = ref(null)
|
|
63
103
|
table.element = table.element || {}
|
|
64
104
|
|
|
65
105
|
const recieveConf = tableConf || {}
|
|
@@ -67,16 +107,30 @@ if (tableConf) {
|
|
|
67
107
|
table.element = { ...tableConf.element, ...table.element }
|
|
68
108
|
}
|
|
69
109
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
|
|
111
|
+
const ratio = computed(() => {
|
|
112
|
+
const docEl = document.documentElement
|
|
113
|
+
const width = docEl.getBoundingClientRect().width;
|
|
114
|
+
return width / 1920
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
if (tableConf && tableConf.element) {
|
|
118
|
+
if (tableConf.element.maxHeight) {
|
|
119
|
+
table.element.maxHeight = tableConf.element.maxHeight * ratio.value
|
|
120
|
+
}
|
|
121
|
+
if (tableConf.element.height) {
|
|
122
|
+
table.element.height = tableConf.element.height * ratio.value
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
onBeforeMount(async () => {
|
|
127
|
+
for (let dict of dictTypes) {
|
|
128
|
+
dictStore.value[dict] = await DictStore.getDict(dict)
|
|
129
|
+
}
|
|
78
130
|
})
|
|
79
131
|
|
|
132
|
+
|
|
133
|
+
|
|
80
134
|
const dictLabel = computed(() => {
|
|
81
135
|
return function (col: any, row: any) {
|
|
82
136
|
if (dictStore.value[col.dict]) {
|
|
@@ -87,6 +141,7 @@ const dictLabel = computed(() => {
|
|
|
87
141
|
}
|
|
88
142
|
})
|
|
89
143
|
|
|
144
|
+
|
|
90
145
|
const tagColor = computed(() => {
|
|
91
146
|
return function (col, row) {
|
|
92
147
|
if (col.tags) {
|
|
@@ -101,15 +156,35 @@ const tagColor = computed(() => {
|
|
|
101
156
|
}
|
|
102
157
|
})
|
|
103
158
|
|
|
104
|
-
|
|
105
|
-
|
|
159
|
+
table.getRef = () => XklTableRef.value
|
|
160
|
+
|
|
161
|
+
const virtualAllSelected = ref(false)
|
|
162
|
+
const virtualIsIndeterminate = ref(false)
|
|
163
|
+
|
|
164
|
+
const setCheckboxState = (isAllSelected, isIndeterminate) => {
|
|
165
|
+
virtualAllSelected.value = isAllSelected
|
|
166
|
+
virtualIsIndeterminate.value = isIndeterminate
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const virtualSelectAll = () => {
|
|
170
|
+
emit('virtual-select-all', virtualAllSelected.value, setCheckboxState)
|
|
106
171
|
}
|
|
107
172
|
|
|
108
|
-
|
|
173
|
+
const virtualSelect = (row) => {
|
|
174
|
+
emit('virtual-select', row)
|
|
175
|
+
}
|
|
109
176
|
|
|
110
|
-
|
|
111
|
-
|
|
177
|
+
onMounted(() => {
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
onBeforeUnmount(() => {
|
|
182
|
+
table.getRef = null
|
|
112
183
|
})
|
|
113
184
|
|
|
114
185
|
|
|
186
|
+
defineExpose({
|
|
187
|
+
getRef: table.getRef,
|
|
188
|
+
setCheckboxState
|
|
189
|
+
})
|
|
115
190
|
</script>
|
package/XklTableV2.vue
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="height: 400px">
|
|
3
|
+
<el-auto-resizer>
|
|
4
|
+
<template #default="{ height, width }">
|
|
5
|
+
<el-table-v2 v-model:sort-state="sortState" :header-class="table.headerClass || recieveConf.headerClass"
|
|
6
|
+
:columns="table.vColumns" :data="table.list" :width="width" :height="height" fixed
|
|
7
|
+
@column-sort="onSort" />
|
|
8
|
+
</template>
|
|
9
|
+
</el-auto-resizer>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
export default {
|
|
14
|
+
name: 'XklTableV2'
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
<script setup lang="ts">
|
|
18
|
+
import { ref } from 'vue';
|
|
19
|
+
import { baseConf, tableConf } from './index'
|
|
20
|
+
import { SortState, TableV2SortOrder } from 'element-plus';
|
|
21
|
+
const props = defineProps(['table'])
|
|
22
|
+
const { table } = props
|
|
23
|
+
const recieveConf = tableConf || {}
|
|
24
|
+
const sortState = ref<SortState>({})
|
|
25
|
+
table.vColumns = table.columns.map(col => {
|
|
26
|
+
let sortable = false
|
|
27
|
+
if (col.extra) {
|
|
28
|
+
sortable = col.extra.sortable
|
|
29
|
+
} else {
|
|
30
|
+
sortable = col.element.sortable
|
|
31
|
+
}
|
|
32
|
+
if (sortable) {
|
|
33
|
+
sortState.value[col.prop] = TableV2SortOrder.ASC
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
key: col.prop,
|
|
37
|
+
dataKey: col.prop,
|
|
38
|
+
title: col.label,
|
|
39
|
+
width: 150,
|
|
40
|
+
sortable
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
const onSort = ({ key, order }) => {
|
|
47
|
+
sortState.value[key] = order
|
|
48
|
+
table.events.sortChange({ prop: key, order: order + 'ending' })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const clearSelection = () => { }
|
|
52
|
+
|
|
53
|
+
defineExpose({ clearSelection })
|
|
54
|
+
</script>
|
package/XklUpload.vue
CHANGED
|
@@ -26,7 +26,8 @@ const { config, modelValue } = props
|
|
|
26
26
|
config.params = config.params || (() => { })
|
|
27
27
|
|
|
28
28
|
const headers = {
|
|
29
|
-
[config.authorization || 'Authorization']: getToken()
|
|
29
|
+
[config.authorization || 'Authorization']: getToken(),
|
|
30
|
+
...config.headers
|
|
30
31
|
}
|
|
31
32
|
const actionUrl = http.adornUrl(config.url || '/common/upload')
|
|
32
33
|
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="XklVirtualScrollRef">
|
|
3
|
+
<slot></slot>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
export default {
|
|
8
|
+
name: 'XklVirtualScroll'
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import { debounce } from '@xuekl/cli-utils'
|
|
13
|
+
import { computed, nextTick, onMounted, reactive, ref, watch, onBeforeUnmount, onActivated } from 'vue';
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
data: {
|
|
16
|
+
type: Array,
|
|
17
|
+
required: true
|
|
18
|
+
},
|
|
19
|
+
height: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 60
|
|
22
|
+
},
|
|
23
|
+
buffer: {
|
|
24
|
+
type: Number,
|
|
25
|
+
default: 200
|
|
26
|
+
},
|
|
27
|
+
keyProp: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: 'id'
|
|
30
|
+
},
|
|
31
|
+
throttleTime: {
|
|
32
|
+
type: Number,
|
|
33
|
+
default: 100
|
|
34
|
+
},
|
|
35
|
+
target: {
|
|
36
|
+
type: String
|
|
37
|
+
},
|
|
38
|
+
itemTarget: {
|
|
39
|
+
type: String
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const emit = defineEmits(['change'])
|
|
44
|
+
|
|
45
|
+
const XklVirtualScrollRef = ref()
|
|
46
|
+
let ThisEl
|
|
47
|
+
|
|
48
|
+
const sizes: any = ref({})
|
|
49
|
+
const itemSize = ref(40)
|
|
50
|
+
|
|
51
|
+
const offsetMap = computed(() => {
|
|
52
|
+
const { keyProp, height, data }: any = props
|
|
53
|
+
const res = {}
|
|
54
|
+
let total = 0
|
|
55
|
+
for (let i = 0; i < data.length; i++) {
|
|
56
|
+
const key = data[i][keyProp]
|
|
57
|
+
res[key] = total
|
|
58
|
+
|
|
59
|
+
// const curSize = sizes.value[key]
|
|
60
|
+
const curSize = itemSize.value
|
|
61
|
+
const size = typeof curSize === 'number' ? curSize : height
|
|
62
|
+
total += size
|
|
63
|
+
}
|
|
64
|
+
return res
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const rootForm = reactive<{
|
|
68
|
+
renderData: any[],
|
|
69
|
+
top: number,
|
|
70
|
+
bottom: number,
|
|
71
|
+
start: number,
|
|
72
|
+
end: number,
|
|
73
|
+
height: number,
|
|
74
|
+
timer?: NodeJS.Timeout,
|
|
75
|
+
onScroll?: any
|
|
76
|
+
}>({
|
|
77
|
+
renderData: [],
|
|
78
|
+
top: 0,
|
|
79
|
+
bottom: 0,
|
|
80
|
+
start: 0,
|
|
81
|
+
end: 0,
|
|
82
|
+
height: 360
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
const scroller = ref()
|
|
87
|
+
|
|
88
|
+
const updateSizes = () => {
|
|
89
|
+
const row = ThisEl.querySelector(props.itemTarget)
|
|
90
|
+
if (row) {
|
|
91
|
+
itemSize.value = row.offsetHeight
|
|
92
|
+
}
|
|
93
|
+
// const rows = ThisEl.querySelectorAll(props.itemTarget)
|
|
94
|
+
|
|
95
|
+
// Array.from(rows).forEach((row: any, index) => {
|
|
96
|
+
// const item = rootForm.renderData[index]
|
|
97
|
+
// if (!item) return
|
|
98
|
+
|
|
99
|
+
// const key = item[props.keyProp]
|
|
100
|
+
// const offsetHeight = row.offsetHeight
|
|
101
|
+
|
|
102
|
+
// if (sizes.value[key] !== offsetHeight) {
|
|
103
|
+
// sizes.value[key] = offsetHeight
|
|
104
|
+
// }
|
|
105
|
+
// })
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const getOffsetTop = (index) => {
|
|
109
|
+
// const item = props.data[index]
|
|
110
|
+
// if (item) {
|
|
111
|
+
// return offsetMap.value[item[props.keyProp]] || 0
|
|
112
|
+
// }
|
|
113
|
+
// return 0
|
|
114
|
+
return index * itemSize.value
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 获取某条数据的尺寸
|
|
118
|
+
const getSize = (index) => {
|
|
119
|
+
return itemSize.value || props.height
|
|
120
|
+
// const item = props.data[index]
|
|
121
|
+
// if (item) {
|
|
122
|
+
// const key = item[props.keyProp]
|
|
123
|
+
// return sizes.value[key] || rootForm.height
|
|
124
|
+
// }
|
|
125
|
+
// return rootForm.height
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let beforeScrollTop = 0
|
|
129
|
+
|
|
130
|
+
// 计算只在视图上渲染的数据
|
|
131
|
+
const calcRenderData = () => {
|
|
132
|
+
let startTime = Date.now()
|
|
133
|
+
const { data, buffer } = props
|
|
134
|
+
// 计算可视范围顶部、底部
|
|
135
|
+
const top = scroller.value.scrollTop - buffer
|
|
136
|
+
const bottom = scroller.value.scrollTop + scroller.value.offsetHeight + buffer
|
|
137
|
+
beforeScrollTop = scroller.value.scrollTop
|
|
138
|
+
|
|
139
|
+
// 二分法计算可视范围内的开始的第一个内容
|
|
140
|
+
let l = 0
|
|
141
|
+
let r = data.length - 1
|
|
142
|
+
let mid = 0
|
|
143
|
+
while (l <= r) {
|
|
144
|
+
mid = Math.floor((l + r) / 2)
|
|
145
|
+
const midVal = getOffsetTop(mid)
|
|
146
|
+
if (midVal < top) {
|
|
147
|
+
const midNextVal = getOffsetTop(mid + 1)
|
|
148
|
+
if (midNextVal > top) break
|
|
149
|
+
l = mid + 1
|
|
150
|
+
} else {
|
|
151
|
+
r = mid - 1
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 计算渲染内容的开始、结束索引
|
|
156
|
+
let start = mid
|
|
157
|
+
let end = data.length - 1
|
|
158
|
+
for (let i = start + 1; i < data.length; i++) {
|
|
159
|
+
const offsetTop = getOffsetTop(i)
|
|
160
|
+
if (offsetTop >= bottom) {
|
|
161
|
+
end = i
|
|
162
|
+
break
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 开始索引始终保持偶数,如果为奇数,则加1使其保持偶数【确保表格行的偶数数一致,不会导致斑马纹乱序显示】
|
|
167
|
+
if (start % 2) {
|
|
168
|
+
start = start - 1
|
|
169
|
+
}
|
|
170
|
+
// //console.log(start, end, 'start end')
|
|
171
|
+
|
|
172
|
+
rootForm.top = top
|
|
173
|
+
rootForm.bottom = bottom
|
|
174
|
+
rootForm.start = start
|
|
175
|
+
rootForm.end = end
|
|
176
|
+
rootForm.renderData = data.slice(start, end + 1)
|
|
177
|
+
//console.log("calcRenderData。。。", start, end, bottom, top, scroller.value.offsetHeight)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 计算位置
|
|
181
|
+
const calcPosition = () => {
|
|
182
|
+
// const last = props.data.length - 1
|
|
183
|
+
const length = props.data.length
|
|
184
|
+
// 计算内容总高度
|
|
185
|
+
// const wrapHeight = last < 0 ? rootForm.height : getOffsetTop(last) + getSize(last)
|
|
186
|
+
|
|
187
|
+
const wrapHeight = (length * itemSize.value) || rootForm.height
|
|
188
|
+
// 计算当前滚动位置需要撑起的高度
|
|
189
|
+
const offsetTop = getOffsetTop(rootForm.start)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
// 设置dom位置
|
|
193
|
+
// const classNames = [props.target]
|
|
194
|
+
// classNames.forEach(className => {
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
// })
|
|
198
|
+
const el = scroller.value
|
|
199
|
+
if (!el) return
|
|
200
|
+
|
|
201
|
+
// 创建wrapEl、innerEl
|
|
202
|
+
if (!el.wrapEl) {
|
|
203
|
+
const wrapEl = document.createElement('div')
|
|
204
|
+
const innerEl = document.createElement('div')
|
|
205
|
+
wrapEl.appendChild(innerEl)
|
|
206
|
+
innerEl.appendChild(el.children[0])
|
|
207
|
+
el.insertBefore(wrapEl, el.firstChild)
|
|
208
|
+
el.wrapEl = wrapEl
|
|
209
|
+
el.innerEl = innerEl
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (el.wrapEl) {
|
|
213
|
+
// 设置高度
|
|
214
|
+
el.wrapEl.style.height = wrapHeight + 'px'
|
|
215
|
+
// 设置transform撑起高度
|
|
216
|
+
// el.innerEl.style.transform = `translateY(${offsetTop}px)`
|
|
217
|
+
el.innerEl.style.setProperty('transform', `translateY(${offsetTop}px)`);
|
|
218
|
+
|
|
219
|
+
// 设置paddingTop撑起高度
|
|
220
|
+
// el.innerEl.style.paddingTop = `${offsetTop}px`
|
|
221
|
+
//console.log("calcPosition。。。")
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// 空闲时更新位置
|
|
226
|
+
const updatePosition = () => {
|
|
227
|
+
//console.log('我就一直走')
|
|
228
|
+
rootForm.timer && clearTimeout(rootForm.timer)
|
|
229
|
+
rootForm.timer = setTimeout(() => {
|
|
230
|
+
rootForm.timer && clearTimeout(rootForm.timer)
|
|
231
|
+
// 传入false,避免一直循环调用
|
|
232
|
+
handleScroll()
|
|
233
|
+
}, props.throttleTime + 10)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 【外部调用】更新
|
|
237
|
+
const update = () => {
|
|
238
|
+
//console.log('update');
|
|
239
|
+
|
|
240
|
+
updateAll()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 【外部调用】滚动到第几行
|
|
244
|
+
const scrollTo = (index, stop = false) => {
|
|
245
|
+
const item = props.data[index]
|
|
246
|
+
if (item && scroller.value) {
|
|
247
|
+
updateSizes()
|
|
248
|
+
calcRenderData()
|
|
249
|
+
|
|
250
|
+
nextTick(() => {
|
|
251
|
+
const offsetTop = getOffsetTop(index)
|
|
252
|
+
scroller.value.scrollTop = offsetTop
|
|
253
|
+
|
|
254
|
+
// 调用两次scrollTo,第一次滚动时,如果表格行初次渲染高度发生变化时,会导致滚动位置有偏差,此时需要第二次执行滚动,确保滚动位置无误
|
|
255
|
+
if (!stop) {
|
|
256
|
+
setTimeout(() => {
|
|
257
|
+
scrollTo(index, true)
|
|
258
|
+
}, 50)
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 【外部调用】重置
|
|
265
|
+
const reset = () => {
|
|
266
|
+
sizes.value = {}
|
|
267
|
+
scrollTo(0, false)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const updateAll = () => {
|
|
271
|
+
updateSizes()
|
|
272
|
+
calcRenderData()
|
|
273
|
+
calcPosition()
|
|
274
|
+
emit('change', rootForm.renderData, rootForm.start, rootForm.end)
|
|
275
|
+
//console.log('data change', rootForm.renderData);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
let renderData = debounce(updateAll, 100, false)
|
|
279
|
+
|
|
280
|
+
const handleScroll = () => {
|
|
281
|
+
|
|
282
|
+
// 更新当前尺寸(高度)
|
|
283
|
+
// updateSizes()
|
|
284
|
+
// 计算renderData
|
|
285
|
+
// calcRenderData()
|
|
286
|
+
renderData()
|
|
287
|
+
// 计算位置
|
|
288
|
+
// calcPosition()
|
|
289
|
+
// shouldUpdate && updatePosition()
|
|
290
|
+
// 触发事件
|
|
291
|
+
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const initData = () => {
|
|
295
|
+
|
|
296
|
+
// 初次执行
|
|
297
|
+
// setTimeout(() => {
|
|
298
|
+
// handleScroll()
|
|
299
|
+
// }, 100)
|
|
300
|
+
|
|
301
|
+
// 监听事件
|
|
302
|
+
// rootForm.onScroll = throttle(handleScroll, props.throttleTime)
|
|
303
|
+
scroller.value.addEventListener('scroll', handleScroll);
|
|
304
|
+
window.addEventListener('resize', handleScroll);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
watch(() => props.data, () => {
|
|
308
|
+
update()
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
onMounted(() => {
|
|
312
|
+
ThisEl = XklVirtualScrollRef.value
|
|
313
|
+
scroller.value = ThisEl.querySelector(props.target)
|
|
314
|
+
|
|
315
|
+
initData()
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
onActivated(() => {
|
|
319
|
+
scroller.value.scrollTop = beforeScrollTop
|
|
320
|
+
update()
|
|
321
|
+
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
onBeforeUnmount(() => {
|
|
325
|
+
if (scroller.value) {
|
|
326
|
+
scroller.value.removeEventListener('scroll', handleScroll)
|
|
327
|
+
window.removeEventListener('resize', handleScroll)
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
</script>
|
|
331
|
+
|
|
332
|
+
<style lang='less' scoped></style>
|