sone-ui-component-3.2.4 2.1.62 → 2.1.63
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/lib/sone-ui.common.js +25328 -25834
- package/lib/sone-ui.common.js.map +1 -1
- package/lib/sone-ui.css +1 -1
- package/lib/sone-ui.umd.js +25328 -25834
- package/lib/sone-ui.umd.js.map +1 -1
- package/lib/sone-ui.umd.min.js +4 -4
- package/lib/sone-ui.umd.min.js.map +1 -1
- package/package.json +1 -2
- package/packages/normalTable/src/mainNew.vue +2 -1
- package/packages/normalTable/src/virtual/el-table-virtual-column-formatter.vue +14 -0
- package/packages/normalTable/src/virtual/el-table-virtual-column.vue +416 -0
- package/packages/normalTable/src/virtual/el-table-virtual-scroll.vue +1548 -0
- package/packages/normalTable/src/virtual/util.js +147 -0
- package/src/index.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sone-ui-component-3.2.4",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.63",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "lib/sone-ui.common.js",
|
|
6
6
|
"files": [
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
"axios": "^0.21.4",
|
|
25
25
|
"core-js": "^3.6.5",
|
|
26
26
|
"deepmerge": "^1.2.0",
|
|
27
|
-
"el-table-virtual-scroll": "^2.0.2",
|
|
28
27
|
"element-ui": "^2.15.5",
|
|
29
28
|
"vue": "^2.6.11",
|
|
30
29
|
"vue-grid-layout": "^2.3.12",
|
|
@@ -442,7 +442,8 @@
|
|
|
442
442
|
import { deepClone } from 'sone-ui-component/src/utils/util'
|
|
443
443
|
import ColumnTransfer from './columnTransferNew.vue'
|
|
444
444
|
import IconButton from "sone-ui-component/packages/IconButton/index.vue";
|
|
445
|
-
import VirtualScroll
|
|
445
|
+
import VirtualScroll from './virtual/el-table-virtual-scroll.vue'
|
|
446
|
+
import VirtualColumn from './virtual/el-table-virtual-column.vue'
|
|
446
447
|
export default {
|
|
447
448
|
name: "SoneNormalTable",
|
|
448
449
|
components: { ColumnTransfer, IconButton, VirtualScroll, VirtualColumn },
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-table-column
|
|
3
|
+
v-bind="$attrs"
|
|
4
|
+
v-on="$listeners"
|
|
5
|
+
:type="getColumnType()"
|
|
6
|
+
:class-name="getClassName">
|
|
7
|
+
<!-- 列头部 -->
|
|
8
|
+
<template slot="header" slot-scope="scope">
|
|
9
|
+
<slot v-if="$scopedSlots['header']" name="header" v-bind="scope"></slot>
|
|
10
|
+
<template v-else>
|
|
11
|
+
<!-- 多选类型-全选 -->
|
|
12
|
+
<el-checkbox
|
|
13
|
+
v-if="$attrs.type === 'selection'"
|
|
14
|
+
:value="isCheckedAll"
|
|
15
|
+
:indeterminate="isCheckedImn"
|
|
16
|
+
@change="onCheckAllRows">
|
|
17
|
+
</el-checkbox>
|
|
18
|
+
<template v-else>
|
|
19
|
+
{{scope.column.label}}
|
|
20
|
+
</template>
|
|
21
|
+
</template>
|
|
22
|
+
</template>
|
|
23
|
+
<!-- 列嵌套 -->
|
|
24
|
+
<template v-if="isNested">
|
|
25
|
+
<slot></slot>
|
|
26
|
+
</template>
|
|
27
|
+
<!-- 列内容 -->
|
|
28
|
+
<template slot-scope="scope">
|
|
29
|
+
<!-- v-tree类型 -->
|
|
30
|
+
<template v-if="isTree">
|
|
31
|
+
<span class="el-table__indent" :style="{ paddingLeft: `${getTreeState(scope.row).level * indent}px` }"></span>
|
|
32
|
+
<!-- treeNodeKey 用来更新视图的 -->
|
|
33
|
+
<div
|
|
34
|
+
v-if="canExpand(scope.row) && treeNodeKey"
|
|
35
|
+
class="el-table__expand-icon"
|
|
36
|
+
:class="getTreeState(scope.row).expanded ? 'el-table__expand-icon--expanded' : ''"
|
|
37
|
+
@click="onTreeNodeExpand(scope.row)">
|
|
38
|
+
<i class="el-icon-loading" v-if="getTreeState(scope.row).loading"></i>
|
|
39
|
+
<i class="el-icon-arrow-right" v-else></i>
|
|
40
|
+
</div>
|
|
41
|
+
<span v-else class="el-table__placeholder"></span>
|
|
42
|
+
</template>
|
|
43
|
+
<slot v-if="$scopedSlots['default']" v-bind="setScope(scope)"></slot>
|
|
44
|
+
<template v-else>
|
|
45
|
+
<!-- 多选类型 -->
|
|
46
|
+
<el-checkbox
|
|
47
|
+
@click.native.stop
|
|
48
|
+
v-if="$attrs.type === 'selection'"
|
|
49
|
+
:value="scope.row.$v_checked || false"
|
|
50
|
+
:disabled="getDisabled(scope)"
|
|
51
|
+
@change="onCheckRow(scope, !scope.row.$v_checked)">
|
|
52
|
+
</el-checkbox>
|
|
53
|
+
<!-- 单选类型 -->
|
|
54
|
+
<el-radio
|
|
55
|
+
v-if="$attrs.type === 'radio'"
|
|
56
|
+
:label="true"
|
|
57
|
+
:value="virtualScroll.curRow === scope.row"
|
|
58
|
+
@change="onRadioChange(scope.row)">
|
|
59
|
+
<span></span>
|
|
60
|
+
</el-radio>
|
|
61
|
+
<!-- v-index类型 -->
|
|
62
|
+
<span v-else-if="$attrs.type === 'index'">
|
|
63
|
+
{{ getIndex(scope) }}
|
|
64
|
+
</span>
|
|
65
|
+
<!-- 有formatter参数的情况 -->
|
|
66
|
+
<template v-else-if="scope.column.formatter">
|
|
67
|
+
<!-- formatter结果为VNode的情况 -->
|
|
68
|
+
<el-table-virtual-column-formatter
|
|
69
|
+
v-if="isVNode(getFormatterResult(scope))"
|
|
70
|
+
:v-node="getFormatterResult(scope)"
|
|
71
|
+
/>
|
|
72
|
+
<!-- formatter结果为String的情况 -->
|
|
73
|
+
<template v-else>{{ getFormatterResult(scope) }}</template>
|
|
74
|
+
</template>
|
|
75
|
+
<template v-else>
|
|
76
|
+
{{scope.row[scope.column.property]}}
|
|
77
|
+
</template>
|
|
78
|
+
</template>
|
|
79
|
+
</template>
|
|
80
|
+
</el-table-column>
|
|
81
|
+
</template>
|
|
82
|
+
|
|
83
|
+
<script>
|
|
84
|
+
// import {
|
|
85
|
+
// TableColumn,
|
|
86
|
+
// Checkbox,
|
|
87
|
+
// Radio
|
|
88
|
+
// } from 'element-ui'
|
|
89
|
+
import ElTableVirtualColumnFormatter from './el-table-virtual-column-formatter.vue'
|
|
90
|
+
|
|
91
|
+
// 用于获取formatter结果的临时对象,不可挂载到vue实例中,否则会导致循环更新的问题
|
|
92
|
+
const formatterTempObj = {
|
|
93
|
+
scope: null, // 缓存键 当前scope参数
|
|
94
|
+
result: null // 缓存值 formatter的调用结果
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default {
|
|
98
|
+
name: 'el-table-virtual-column',
|
|
99
|
+
components: {
|
|
100
|
+
// ElCheckbox: Checkbox,
|
|
101
|
+
// ElRadio: Radio,
|
|
102
|
+
ElTableVirtualColumnFormatter
|
|
103
|
+
},
|
|
104
|
+
inject: ['virtualScroll'],
|
|
105
|
+
props: {
|
|
106
|
+
selectable: {
|
|
107
|
+
type: Function
|
|
108
|
+
},
|
|
109
|
+
reserveSelection: {
|
|
110
|
+
type: Boolean,
|
|
111
|
+
default: false
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
data () {
|
|
115
|
+
return {
|
|
116
|
+
isCheckedAll: false, // 全选
|
|
117
|
+
isCheckedImn: false, // 控制半选样式
|
|
118
|
+
isTree: false, // 树结构
|
|
119
|
+
isNested: false, // 是否列嵌套
|
|
120
|
+
treeNodeKey: 1 // 更新树节点视图
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
computed: {
|
|
124
|
+
getClassName () {
|
|
125
|
+
const classnames = []
|
|
126
|
+
const classname = this.$attrs['class-name'] || this.$attrs.className
|
|
127
|
+
classname && classnames.push(classname)
|
|
128
|
+
this.isTree && classnames.push('el-table__row--level')
|
|
129
|
+
let vfixed = this.$attrs.vfixed
|
|
130
|
+
if (vfixed === true || vfixed === '') vfixed = 'left'
|
|
131
|
+
if (vfixed) classnames.push('virtual-column__fixed-' + vfixed)
|
|
132
|
+
return classnames.join(' ')
|
|
133
|
+
},
|
|
134
|
+
indent () {
|
|
135
|
+
return (this.virtualScroll.elTable || {}).indent || 16
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
methods: {
|
|
139
|
+
getColumnType () {
|
|
140
|
+
const { type } = this.$attrs
|
|
141
|
+
// 自定义的类型需转为空传到 el-table-column,否则会导致某些bug,如筛选按钮不亮
|
|
142
|
+
return ['index', 'selection', 'radio', 'tree'].includes(type) ? '' : type
|
|
143
|
+
},
|
|
144
|
+
// 获取多选禁用状态
|
|
145
|
+
getDisabled (scope) {
|
|
146
|
+
if (this.selectable) {
|
|
147
|
+
const index = this.getIndex(scope, false)
|
|
148
|
+
return !this.selectable(scope.row, index)
|
|
149
|
+
}
|
|
150
|
+
return false
|
|
151
|
+
},
|
|
152
|
+
// 选择表格所有行
|
|
153
|
+
onCheckAllRows (val) {
|
|
154
|
+
const { selectOnIndeterminate = true } = this.virtualScroll.elTable
|
|
155
|
+
if (selectOnIndeterminate) {
|
|
156
|
+
val = this.isCheckedImn ? true : !this.isCheckedAll
|
|
157
|
+
} else {
|
|
158
|
+
val = this.isCheckedImn ? false : !this.isCheckedAll
|
|
159
|
+
}
|
|
160
|
+
if (this.selectable) {
|
|
161
|
+
// 筛选出可选的行
|
|
162
|
+
const selectableList = []
|
|
163
|
+
const list = this.virtualScroll.getData(false)
|
|
164
|
+
list.forEach((row, index) => {
|
|
165
|
+
this.selectable(row, index) && selectableList.push(row)
|
|
166
|
+
})
|
|
167
|
+
this.virtualScroll.checkAll(val, selectableList, true)
|
|
168
|
+
} else {
|
|
169
|
+
this.virtualScroll.checkAll(val)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const selection = this.virtualScroll.getSelection()
|
|
173
|
+
this.isCheckedImn = !val && selection.length > 0
|
|
174
|
+
this.isCheckedAll = val
|
|
175
|
+
},
|
|
176
|
+
// 选择表格某行
|
|
177
|
+
onCheckRow (scope, val) {
|
|
178
|
+
const index = this.getIndex(scope, false)
|
|
179
|
+
if (this.selectable) {
|
|
180
|
+
const isSelectable = this.selectable(scope.row, index)
|
|
181
|
+
if (!isSelectable) return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.virtualScroll.checkRow(scope.row, val, true, true)
|
|
185
|
+
this.syncCheckStatus()
|
|
186
|
+
},
|
|
187
|
+
// 是否自定义多选
|
|
188
|
+
isSelection () {
|
|
189
|
+
return this.$attrs.type === 'selection'
|
|
190
|
+
},
|
|
191
|
+
// 同步全选、半选框状态
|
|
192
|
+
syncCheckStatus () {
|
|
193
|
+
const list = this.virtualScroll.getData(false) // 筛选后的列表或原列表
|
|
194
|
+
const checkedLen = list.filter(row => row.$v_checked === true).length
|
|
195
|
+
const selection = this.virtualScroll.getSelection()
|
|
196
|
+
|
|
197
|
+
if (checkedLen && checkedLen === list.length) {
|
|
198
|
+
// 全部选中
|
|
199
|
+
this.isCheckedAll = true
|
|
200
|
+
} else if (checkedLen && this.selectable) {
|
|
201
|
+
// 可选的全部选中
|
|
202
|
+
this.isCheckedAll = list.every((row, index) => {
|
|
203
|
+
const enable = this.selectable(row, index)
|
|
204
|
+
if (!enable) return true
|
|
205
|
+
return row.$v_checked
|
|
206
|
+
})
|
|
207
|
+
} else {
|
|
208
|
+
// 没有选中
|
|
209
|
+
this.isCheckedAll = false
|
|
210
|
+
}
|
|
211
|
+
this.isCheckedImn = !this.isCheckedAll && selection.length > 0
|
|
212
|
+
},
|
|
213
|
+
// 单选
|
|
214
|
+
onRadioChange (row) {
|
|
215
|
+
this.virtualScroll.setCurrentRow(row)
|
|
216
|
+
},
|
|
217
|
+
// 获取索引值; add1 - 是否加1
|
|
218
|
+
getIndex (scope, add1 = true) {
|
|
219
|
+
const index = this.virtualScroll.start + scope.$index
|
|
220
|
+
if (typeof this.$attrs.index === 'function') {
|
|
221
|
+
return this.$attrs.index(index)
|
|
222
|
+
}
|
|
223
|
+
return index + (add1 ? 1 : 0)
|
|
224
|
+
},
|
|
225
|
+
// 设置正确索引值
|
|
226
|
+
setScope (scope) {
|
|
227
|
+
scope.$index = this.virtualScroll.start + scope.$index
|
|
228
|
+
return scope
|
|
229
|
+
},
|
|
230
|
+
// 获取树节点状态
|
|
231
|
+
getTreeState (row) {
|
|
232
|
+
return row.$v_tree || {}
|
|
233
|
+
},
|
|
234
|
+
// 是否可展开
|
|
235
|
+
canExpand (row) {
|
|
236
|
+
const { children = 'children', hasChildren = 'hasChildren' } = this.virtualScroll.treeProps || {}
|
|
237
|
+
const treeState = row.$v_tree || {}
|
|
238
|
+
return (row[children] || []).length > 0 || (row[hasChildren] && !treeState.loaded)
|
|
239
|
+
},
|
|
240
|
+
// 展开树节点
|
|
241
|
+
async onTreeNodeExpand (row) {
|
|
242
|
+
const treeState = row.$v_tree
|
|
243
|
+
if (treeState) {
|
|
244
|
+
const { treeProps } = this.virtualScroll
|
|
245
|
+
const { hasChildren = 'hasChildren' } = treeProps
|
|
246
|
+
if (treeState.loading) return
|
|
247
|
+
if (!treeState.expanded && row[hasChildren] && !treeState.loaded) {
|
|
248
|
+
await this.loadChildNodes(row)
|
|
249
|
+
} else {
|
|
250
|
+
treeState.expanded = !treeState.expanded
|
|
251
|
+
this.renderTreeNode()
|
|
252
|
+
this.virtualScroll.update()
|
|
253
|
+
}
|
|
254
|
+
this.virtualScroll.elTable.$emit('expand-change', row, treeState.expanded)
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
// 加载子节点
|
|
258
|
+
async loadChildNodes (row) {
|
|
259
|
+
return new Promise(resolve => {
|
|
260
|
+
const { treeProps } = this.virtualScroll
|
|
261
|
+
const { load } = this.virtualScroll.elTable
|
|
262
|
+
if (!load) return resolve()
|
|
263
|
+
|
|
264
|
+
const { children = 'children' } = treeProps
|
|
265
|
+
const treeState = row.$v_tree
|
|
266
|
+
treeState.loading = true
|
|
267
|
+
this.renderTreeNode()
|
|
268
|
+
|
|
269
|
+
function resolveFn (data) {
|
|
270
|
+
if (!Array.isArray(data)) {
|
|
271
|
+
throw new Error('[ElTable] data must be an array')
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
treeState.loading = false
|
|
275
|
+
treeState.loaded = true
|
|
276
|
+
treeState.expanded = true
|
|
277
|
+
this.renderTreeNode()
|
|
278
|
+
if (!Array.isArray(data)) {
|
|
279
|
+
resolve()
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
this.$set(row, children, data)
|
|
283
|
+
this.virtualScroll.update()
|
|
284
|
+
resolve()
|
|
285
|
+
}
|
|
286
|
+
load(row, treeState, resolveFn.bind(this))
|
|
287
|
+
})
|
|
288
|
+
},
|
|
289
|
+
// 由于$v_tree不是响应式数据,$v_tree状态的变更需要手动触发视图的更新
|
|
290
|
+
renderTreeNode () {
|
|
291
|
+
this.treeNodeKey = this.treeNodeKey === 1 ? 2 : 1
|
|
292
|
+
},
|
|
293
|
+
// 判断内容是否为VNode
|
|
294
|
+
isVNode (vNode) {
|
|
295
|
+
// return this._vnode.constructor === vNode?.constructor
|
|
296
|
+
return this._vnode.constructor === (vNode ? vNode.constructor : vNode)
|
|
297
|
+
},
|
|
298
|
+
// 获取formatter结果,相同的scope使用缓存的结果,避免重复调用formatter函数
|
|
299
|
+
// 当前与formatter有关的template的写法会使同一个scope参数被formatter连续使用两次
|
|
300
|
+
// 后续的formatter调用参数都是一个全新的scope对象
|
|
301
|
+
// 因此,只需判断当前scope与上一个scope是否相同即可决定是否需要使用缓存
|
|
302
|
+
getFormatterResult (scope) {
|
|
303
|
+
if (formatterTempObj.scope === scope) {
|
|
304
|
+
// 获取缓存的formatter调用结果
|
|
305
|
+
return formatterTempObj.result
|
|
306
|
+
} else {
|
|
307
|
+
// scope不匹配,更新缓存键scope与值result
|
|
308
|
+
formatterTempObj.scope = scope
|
|
309
|
+
formatterTempObj.result = scope.column.formatter(scope.row, scope.column, scope.row[scope.column.property], scope.$index)
|
|
310
|
+
return formatterTempObj.result
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
beforeCreate () {
|
|
315
|
+
const globalComponents = this.$root.$options.components
|
|
316
|
+
// 当全局有引入ElTableColumn,就不用局部再引入了(修复Duplicate keys detected: 'el-table_1_column_1'. This may cause an update error. #63)
|
|
317
|
+
if (!globalComponents.ElTableColumn) {
|
|
318
|
+
// this.$options.components.ElTableColumn = TableColumn
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
created () {
|
|
322
|
+
this.isNested = !!this.$slots.default // 是否列嵌套
|
|
323
|
+
this.virtualScroll.addColumn(this)
|
|
324
|
+
|
|
325
|
+
const { type } = this.$attrs
|
|
326
|
+
if (type === 'expand') {
|
|
327
|
+
this.virtualScroll.useExpandTable()
|
|
328
|
+
} else if (type === 'tree') {
|
|
329
|
+
this.isTree = true
|
|
330
|
+
this.virtualScroll.useCustomTree()
|
|
331
|
+
} else if (type === 'selection') {
|
|
332
|
+
this.virtualScroll.useCustomSelection()
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
beforeDestroy () {
|
|
336
|
+
this.virtualScroll.removeColumn(this)
|
|
337
|
+
// 清除formatter相关的缓存
|
|
338
|
+
formatterTempObj.scope = null
|
|
339
|
+
formatterTempObj.result = null
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
</script>
|
|
343
|
+
|
|
344
|
+
<style lang='scss'>
|
|
345
|
+
.el-table-virtual-scroll {
|
|
346
|
+
.virtual-column__fixed-left,
|
|
347
|
+
.virtual-column__fixed-right {
|
|
348
|
+
position: sticky !important;
|
|
349
|
+
z-index: 2 !important;
|
|
350
|
+
background: #fff;
|
|
351
|
+
}
|
|
352
|
+
&.is-scrolling-left {
|
|
353
|
+
.is-last-column {
|
|
354
|
+
&:before {
|
|
355
|
+
box-shadow: none;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
&.is-scrolling-right,
|
|
360
|
+
&.is-scrolling-middle {
|
|
361
|
+
.is-last-column {
|
|
362
|
+
border-right: none;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
&.is-scrolling-right {
|
|
367
|
+
.is-first-column {
|
|
368
|
+
&:before {
|
|
369
|
+
box-shadow: none;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
&.is-scrolling-left,
|
|
374
|
+
&.is-scrolling-middle {
|
|
375
|
+
.is-first-column {
|
|
376
|
+
border-left: none;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
.is-last-column,
|
|
380
|
+
.is-first-column {
|
|
381
|
+
overflow: visible !important;
|
|
382
|
+
|
|
383
|
+
&:before {
|
|
384
|
+
content: "";
|
|
385
|
+
position: absolute;
|
|
386
|
+
top: 0px;
|
|
387
|
+
width: 10px;
|
|
388
|
+
bottom: -1px;
|
|
389
|
+
overflow-x: hidden;
|
|
390
|
+
overflow-y: hidden;
|
|
391
|
+
touch-action: none;
|
|
392
|
+
pointer-events: none;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
.is-last-column {
|
|
396
|
+
&:before {
|
|
397
|
+
right: -10px;
|
|
398
|
+
box-shadow: inset 10px 0 10px -10px rgba(0, 0, 0, .12);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
.is-first-column {
|
|
402
|
+
&:before {
|
|
403
|
+
left: -10px;
|
|
404
|
+
box-shadow: inset -10px 0 10px -10px rgba(0, 0, 0, .12);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
&.is-scrolling-none {
|
|
408
|
+
.is-last-column,
|
|
409
|
+
.is-first-column {
|
|
410
|
+
&:before {
|
|
411
|
+
content: none;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
</style>
|