xt-element-ui 1.2.2 → 1.2.3
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/index.common.js +1211 -1154
- package/lib/index.css +1 -1
- package/lib/index.umd.js +1216 -1159
- package/lib/index.umd.min.js +5 -5
- package/package.json +1 -1
- package/src/components/ex-button/index.vue +1 -1
- package/src/components/ex-button/style/index.scss +10 -4
- package/src/components/ex-chart/theme/dark.js +1 -1
- package/src/components/ex-chart/utils.js +1 -1
- package/src/components/ex-page/index.js +2 -0
- package/src/components/ex-select-tree/index.js +2 -0
- package/src/components/{xt-select-tree → ex-select-tree}/index.vue +1 -1
- package/src/components/ex-table/ExTableCell.vue +3 -2
- package/src/components/ex-table/VirtualElTable.vue +234 -0
- package/src/components/ex-table/index.vue +176 -445
- package/src/components/ex-upload/index.js +2 -0
- package/src/components/xt-config-provider/index.js +2 -2
- package/src/components/xt-config-provider/index.vue +7 -3
- package/src/components/xt-text/style/index.scss +13 -13
- package/src/styles/css-variables.scss +1 -1
- package/src/styles/element-theme.scss +1 -1
- package/src/utils/index.js +11 -2
- package/src/components/xt-page/index.js +0 -0
- package/src/components/xt-select-tree/index.js +0 -0
- package/src/components/xt-upload/index.js +0 -2
- /package/src/components/{xt-page → ex-page}/index.vue +0 -0
- /package/src/components/{xt-upload → ex-upload}/index.vue +0 -0
- /package/src/components/{xt-upload → ex-upload}/preview.vue +0 -0
package/package.json
CHANGED
|
@@ -42,20 +42,26 @@
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/* small size - 字体大小 12px */
|
|
46
|
+
.ex-button-mini {
|
|
47
|
+
padding: 2px 4px;
|
|
48
|
+
font-size: calc(var(--xt-font-size-base) + $xt-font-size-increment-extra-small);
|
|
49
|
+
}
|
|
50
|
+
|
|
45
51
|
/* small size - 字体大小 14px */
|
|
46
52
|
.ex-button-small {
|
|
47
|
-
padding:
|
|
48
|
-
font-size: calc(var(--xt-font-size-base) + $xt-font-size-increment-
|
|
53
|
+
padding: 5px 10px;
|
|
54
|
+
font-size: calc(var(--xt-font-size-base) + $xt-font-size-increment-base);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
/* medium size - 字体大小 16px */
|
|
52
58
|
.ex-button-medium {
|
|
53
|
-
padding:
|
|
59
|
+
padding: 8px 16px;
|
|
54
60
|
font-size: calc(var(--xt-font-size-base) + $xt-font-size-increment-medium);
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
/* large size - 字体大小 18px */
|
|
58
64
|
.ex-button-large {
|
|
59
|
-
padding:
|
|
65
|
+
padding: 11px 22px;
|
|
60
66
|
font-size: calc(var(--xt-font-size-base) + $xt-font-size-increment-large);
|
|
61
67
|
}
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
import Emitter from "element-ui/lib/mixins/emitter";
|
|
57
57
|
import { addResizeListener, removeResizeListener } from "element-ui/lib/utils/resize-event";
|
|
58
58
|
export default {
|
|
59
|
-
name: "
|
|
59
|
+
name: "ExSelectTree",
|
|
60
60
|
mixins: [Emitter],
|
|
61
61
|
inheritAttrs: false,
|
|
62
62
|
model: {
|
|
@@ -14,9 +14,10 @@ export default {
|
|
|
14
14
|
index: ctx.props.index
|
|
15
15
|
}
|
|
16
16
|
if (ctx.props.column) params.column = ctx.props.column
|
|
17
|
-
const isVnode = (it) => typeof it === 'object' && it.hasOwnProperty('componentOptions')
|
|
17
|
+
const isVnode = (it) => it !== null && it !== undefined && typeof it === 'object' && it.hasOwnProperty('componentOptions')
|
|
18
18
|
const renderNode = ctx.props.formatter(h, params)
|
|
19
|
-
|
|
19
|
+
if (renderNode === null || renderNode === undefined || renderNode === '') return h('span', '')
|
|
20
|
+
return isVnode(renderNode) ? renderNode : h('span', String(renderNode))
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
</script>
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<el-table
|
|
3
|
+
ref="innerTable"
|
|
4
|
+
v-bind="$attrs"
|
|
5
|
+
v-on="$listeners"
|
|
6
|
+
:data="renderList"
|
|
7
|
+
>
|
|
8
|
+
<slot />
|
|
9
|
+
</el-table>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
export default {
|
|
14
|
+
name: 'VirtualElTable',
|
|
15
|
+
inheritAttrs: false,
|
|
16
|
+
|
|
17
|
+
props: {
|
|
18
|
+
virtualScroll: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: false
|
|
21
|
+
},
|
|
22
|
+
rowHeight: {
|
|
23
|
+
type: Number,
|
|
24
|
+
default: 48
|
|
25
|
+
},
|
|
26
|
+
bufferSize: {
|
|
27
|
+
type: Number,
|
|
28
|
+
default: 5
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
data() {
|
|
33
|
+
return {
|
|
34
|
+
scrollWrap: null,
|
|
35
|
+
phantomDom: null,
|
|
36
|
+
scrollTop: 0,
|
|
37
|
+
// 根据数据量与容器高度预先估算一个合理的 endIndex,避免首屏只渲染 0 行
|
|
38
|
+
startIndex: 0,
|
|
39
|
+
endIndex: 20,
|
|
40
|
+
rafId: null,
|
|
41
|
+
unwatchPhantom: null,
|
|
42
|
+
layoutTimer: null,
|
|
43
|
+
retryTimers: []
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
computed: {
|
|
48
|
+
originData() {
|
|
49
|
+
return this.$attrs.data || []
|
|
50
|
+
},
|
|
51
|
+
renderList() {
|
|
52
|
+
if (!this.virtualScroll) return this.originData
|
|
53
|
+
const start = Math.max(0, this.startIndex - this.bufferSize)
|
|
54
|
+
const end = Math.min(this.originData.length, this.endIndex + this.bufferSize)
|
|
55
|
+
return this.originData.slice(start, end)
|
|
56
|
+
},
|
|
57
|
+
totalListHeight() {
|
|
58
|
+
return this.originData.length * this.rowHeight
|
|
59
|
+
},
|
|
60
|
+
contentOffsetY() {
|
|
61
|
+
return Math.max(0, this.startIndex - this.bufferSize) * this.rowHeight
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
watch: {
|
|
66
|
+
renderList() {
|
|
67
|
+
this.scheduleLayout()
|
|
68
|
+
},
|
|
69
|
+
originData: {
|
|
70
|
+
handler(val) {
|
|
71
|
+
if (!this.virtualScroll) return
|
|
72
|
+
if (val && val.length) {
|
|
73
|
+
this.$nextTick(() => {
|
|
74
|
+
this.calcVisibleRange()
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
mounted() {
|
|
82
|
+
this.$nextTick(() => {
|
|
83
|
+
this.initVirtualScroll()
|
|
84
|
+
})
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
beforeDestroy() {
|
|
88
|
+
this.unbindScrollEvent()
|
|
89
|
+
if (this.rafId) {
|
|
90
|
+
cancelAnimationFrame(this.rafId)
|
|
91
|
+
this.rafId = null
|
|
92
|
+
}
|
|
93
|
+
if (this.unwatchPhantom) {
|
|
94
|
+
this.unwatchPhantom()
|
|
95
|
+
this.unwatchPhantom = null
|
|
96
|
+
}
|
|
97
|
+
if (this.layoutTimer) {
|
|
98
|
+
clearTimeout(this.layoutTimer)
|
|
99
|
+
this.layoutTimer = null
|
|
100
|
+
}
|
|
101
|
+
this.retryTimers.forEach(t => clearTimeout(t))
|
|
102
|
+
this.retryTimers = []
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
methods: {
|
|
106
|
+
initVirtualScroll() {
|
|
107
|
+
if (!this.virtualScroll) return
|
|
108
|
+
this.scrollWrap = this.$refs.innerTable.$el.querySelector('.el-table__body-wrapper')
|
|
109
|
+
if (!this.scrollWrap) {
|
|
110
|
+
console.warn('[VirtualElTable] 无法找到滚动容器,虚拟滚动功能已禁用')
|
|
111
|
+
this.virtualScroll = false
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.scrollWrap.style.overflowY = 'auto'
|
|
116
|
+
this.scrollWrap.style.overflowX = 'auto'
|
|
117
|
+
|
|
118
|
+
this.wrapTableBody()
|
|
119
|
+
this.scrollWrap.addEventListener('scroll', this.onScroll, { passive: true })
|
|
120
|
+
|
|
121
|
+
// 首次可见范围计算 + 多次重试:
|
|
122
|
+
// ElementUI 的 el-table 在 mounted 后仍会异步完成列宽/高度布局,
|
|
123
|
+
// 单次 $nextTick 可能拿到 clientHeight=0,导致计算失败且无法恢复。
|
|
124
|
+
// 以递增延迟多次调用,直到 scrollWrap.clientHeight 获得稳定值。
|
|
125
|
+
this.calcVisibleRange()
|
|
126
|
+
const delays = [100, 300, 600, 1000]
|
|
127
|
+
delays.forEach(ms => {
|
|
128
|
+
const timer = setTimeout(() => {
|
|
129
|
+
this.calcVisibleRange()
|
|
130
|
+
}, ms)
|
|
131
|
+
this.retryTimers.push(timer)
|
|
132
|
+
})
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
wrapTableBody() {
|
|
136
|
+
const bodyDom = this.scrollWrap.querySelector('.el-table__body')
|
|
137
|
+
if (!bodyDom || bodyDom.querySelector('.vs-phantom')) return
|
|
138
|
+
|
|
139
|
+
// phantom 采用标准 block 布局:
|
|
140
|
+
// - height = 总数据高度,确保撑开 body-wrapper 的滚动条
|
|
141
|
+
// - padding-top = 可见区域偏移量,将 table 推到正确位置
|
|
142
|
+
// - min-width: 100%(不设 width),允许 table 自然撑开,保证列宽生效
|
|
143
|
+
const phantom = document.createElement('div')
|
|
144
|
+
phantom.className = 'vs-phantom'
|
|
145
|
+
phantom.style.position = 'relative'
|
|
146
|
+
phantom.style.minWidth = '100%'
|
|
147
|
+
phantom.style.boxSizing = 'border-box'
|
|
148
|
+
phantom.style.zIndex = '1'
|
|
149
|
+
this.phantomDom = phantom
|
|
150
|
+
|
|
151
|
+
const table = bodyDom.querySelector('table')
|
|
152
|
+
phantom.appendChild(table)
|
|
153
|
+
bodyDom.appendChild(phantom)
|
|
154
|
+
|
|
155
|
+
this.unwatchPhantom = this.$watch(
|
|
156
|
+
['totalListHeight', 'contentOffsetY'],
|
|
157
|
+
() => {
|
|
158
|
+
if (this.phantomDom) {
|
|
159
|
+
this.phantomDom.style.height = `${this.totalListHeight}px`
|
|
160
|
+
this.phantomDom.style.paddingTop = `${this.contentOffsetY}px`
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
{ immediate: true }
|
|
164
|
+
)
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
onScroll() {
|
|
168
|
+
this.scrollTop = this.scrollWrap.scrollTop
|
|
169
|
+
if (this.rafId) return
|
|
170
|
+
this.rafId = requestAnimationFrame(() => {
|
|
171
|
+
this.calcVisibleRange()
|
|
172
|
+
this.rafId = null
|
|
173
|
+
})
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
calcVisibleRange() {
|
|
177
|
+
if (!this.scrollWrap) return
|
|
178
|
+
let viewHeight = this.scrollWrap.clientHeight
|
|
179
|
+
|
|
180
|
+
// 关键兜底:ElementUI 异步布局尚未完成时,clientHeight 可能为 0,
|
|
181
|
+
// 此时用组件接收到的 height / max-height 作为估算值,确保至少能渲染正确数量的行。
|
|
182
|
+
if (!viewHeight) {
|
|
183
|
+
const h = this.$attrs.height || this.$attrs.maxHeight
|
|
184
|
+
if (h) {
|
|
185
|
+
viewHeight = typeof h === 'number' ? h : parseInt(String(h), 10)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (!viewHeight || viewHeight <= 0) return
|
|
189
|
+
|
|
190
|
+
const visibleRowCount = Math.ceil(viewHeight / this.rowHeight)
|
|
191
|
+
const newStart = Math.floor(this.scrollTop / this.rowHeight)
|
|
192
|
+
const newEnd = Math.min(this.originData.length, newStart + visibleRowCount)
|
|
193
|
+
|
|
194
|
+
if (newStart !== this.startIndex || newEnd !== this.endIndex) {
|
|
195
|
+
this.startIndex = newStart
|
|
196
|
+
this.endIndex = newEnd
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
unbindScrollEvent() {
|
|
201
|
+
if (this.scrollWrap) {
|
|
202
|
+
this.scrollWrap.removeEventListener('scroll', this.onScroll)
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
scheduleLayout() {
|
|
207
|
+
if (this.layoutTimer) return
|
|
208
|
+
this.layoutTimer = setTimeout(() => {
|
|
209
|
+
if (this.$refs.innerTable) {
|
|
210
|
+
this.$refs.innerTable.doLayout()
|
|
211
|
+
}
|
|
212
|
+
this.layoutTimer = null
|
|
213
|
+
}, 0)
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
clearSelection() {
|
|
217
|
+
return this.$refs.innerTable.clearSelection()
|
|
218
|
+
},
|
|
219
|
+
toggleRowSelection() {
|
|
220
|
+
return this.$refs.innerTable.toggleRowSelection()
|
|
221
|
+
},
|
|
222
|
+
doLayout() {
|
|
223
|
+
return this.$refs.innerTable.doLayout()
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
</script>
|
|
228
|
+
|
|
229
|
+
<style scoped>
|
|
230
|
+
.vs-phantom {
|
|
231
|
+
position: relative;
|
|
232
|
+
box-sizing: border-box;
|
|
233
|
+
}
|
|
234
|
+
</style>
|