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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sone-ui-component-3.2.4",
3
- "version": "2.1.62",
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, { VirtualColumn } from 'el-table-virtual-scroll'
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,14 @@
1
+ <script>
2
+ // 此组件仅用于显示VNode
3
+ export default {
4
+ name: 'el-table-virtual-column-formatter',
5
+ props: {
6
+ vNode: {
7
+ type: Object
8
+ }
9
+ },
10
+ render () {
11
+ return this.vNode
12
+ }
13
+ }
14
+ </script>
@@ -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>