xt-element-ui 1.2.0 → 1.2.2

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.
@@ -8,32 +8,40 @@
8
8
  </div>
9
9
  </div>
10
10
 
11
- <!-- 虚拟滚动固定表头(独立于滚动容器) -->
11
+ <!-- 虚拟滚动固定表头:复用 el-table 列布局,与 body 表头对齐 -->
12
12
  <div
13
- v-if="virtualScroll && headerRows.length"
13
+ v-if="virtualScroll && computedHeight"
14
14
  ref="fixedHeader"
15
15
  class="ex-table-fixed-header"
16
- :style="{ marginLeft: -scrollLeft + 'px' }"
17
16
  >
18
- <table class="ex-table-fixed-header-table">
19
- <colgroup>
20
- <col v-for="col in headerCols" :key="col._key" :name="col._key" :width="col._width" :min-width="col._minWidth" />
21
- <col name="gutter" width="12" />
22
- </colgroup>
23
- <thead>
24
- <tr v-for="(row, rowIdx) in headerRows" :key="rowIdx">
25
- <template v-for="cell in row">
26
- <th
27
- v-if="!cell._hidden"
28
- :key="cell._key"
29
- :rowspan="cell._rowspan"
30
- :colspan="cell._colspan"
31
- :style="cell._style || {}"
32
- >{{ cell.label }}</th>
33
- </template>
34
- </tr>
35
- </thead>
36
- </table>
17
+ <el-table
18
+ ref="headerTable"
19
+ :data="headerTableData"
20
+ class="ex-table ex-table-header-only"
21
+ >
22
+ <el-table-column v-if="selection" type="selection" width="55" :fixed="selectionFixed" />
23
+ <el-table-column
24
+ v-if="showIndex"
25
+ type="index"
26
+ width="60"
27
+ label="#"
28
+ :fixed="indexFixed"
29
+ />
30
+ <template v-for="col in flattenedColumns">
31
+ <el-table-column
32
+ v-if="col.children && col.children.length"
33
+ :key="col._key"
34
+ v-bind="getColumnProps(col)"
35
+ >
36
+ <el-table-column
37
+ v-for="child in col.children"
38
+ :key="child._key || child.prop"
39
+ v-bind="getColumnProps(child)"
40
+ />
41
+ </el-table-column>
42
+ <el-table-column v-else :key="col._key" v-bind="getColumnProps(col)" />
43
+ </template>
44
+ </el-table>
37
45
  </div>
38
46
 
39
47
  <!-- 表格主体 -->
@@ -106,6 +114,7 @@
106
114
  ref="table"
107
115
  :data="visibleData"
108
116
  :height="computedHeight"
117
+ :max-height="computedMaxHeight"
109
118
  :span-method="groupColumns.length ? handleSpanMethod : undefined"
110
119
  :row-class-name="getRowClassName"
111
120
  v-bind="$attrs"
@@ -171,11 +180,13 @@
171
180
 
172
181
  <script>
173
182
  import ExTableCell from './ExTableCell.vue'
183
+ // 内部导入 element-ui 组件,避免与外部 element-ui 冲突
184
+ import { Table as ElTable, TableColumn as ElTableColumn } from 'element-ui'
174
185
 
175
186
  export default {
176
187
  name: 'ExTable',
177
188
  inheritAttrs: false,
178
- components: { ExTableCell },
189
+ components: { ExTableCell, ElTable, ElTableColumn },
179
190
  props: {
180
191
  // 表格数据
181
192
  tableData: { type: Array, default: () => [] },
@@ -187,6 +198,8 @@ export default {
187
198
  title: { type: String, default: '' },
188
199
  // 高度
189
200
  height: { type: [Number, String], default: null },
201
+ // 最大高度(非虚拟滚动时与 height 二选一,均支持固定表头)
202
+ maxHeight: { type: [Number, String], default: null },
190
203
  // 虚拟滚动
191
204
  virtualScroll: { type: Boolean, default: false },
192
205
  // 预估行高
@@ -224,8 +237,6 @@ export default {
224
237
  visibleStartIndex: 0,
225
238
  visibleEndIndex: 0,
226
239
  visibleCount: 0,
227
- // 表格宽度(用于自适应列宽计算)
228
- tableWidth: 0,
229
240
  // 分组合并缓存
230
241
  spanCache: {},
231
242
  // 列扁平化缓存
@@ -242,203 +253,19 @@ export default {
242
253
 
243
254
  computedHeight() {
244
255
  if (this.virtualScroll) {
245
- return this.height || 400
256
+ return this.height || this.maxHeight || 400
246
257
  }
247
258
  return this.height || undefined
248
259
  },
249
260
 
250
- // 计算固定列总宽度(selection + index + fixed列)
251
- fixedColumnWidth() {
252
- let width = 0
253
- // 选择列
254
- if (this.selection) width += 55
255
- // 序号列
256
- if (this.showIndex) width += 60
257
- // fixed 列
258
- const sumFixedWidth = (cols) => {
259
- cols.forEach(c => {
260
- if (c.fixed && (c.width || c.minWidth)) {
261
- width += typeof c.width === 'number' ? c.width : (typeof c.minWidth === 'number' ? c.minWidth : 100)
262
- }
263
- if (c.children && c.children.length) sumFixedWidth(c.children)
264
- })
265
- }
266
- sumFixedWidth(this.columns)
267
- return width
261
+ computedMaxHeight() {
262
+ if (this.virtualScroll || this.height) return undefined
263
+ return this.maxHeight || undefined
268
264
  },
269
265
 
270
- // 计算非固定列中已设置width的总宽度
271
- nonFixedColumnWidthWithWidth() {
272
- let width = 0
273
- const sumWidth = (cols) => {
274
- cols.forEach(c => {
275
- if (!c.fixed && c.width) {
276
- width += typeof c.width === 'number' ? c.width : parseInt(c.width) || 0
277
- }
278
- if (c.children && c.children.length) sumWidth(c.children)
279
- })
280
- }
281
- sumWidth(this.columns)
282
- return width
283
- },
284
-
285
- // 计算需要自适应的列数(非固定列且未设置width)
286
- autoColumnCount() {
287
- let count = 0
288
- const countCols = (cols) => {
289
- cols.forEach(c => {
290
- if (!c.fixed) {
291
- if (c.children && c.children.length) {
292
- countCols(c.children)
293
- } else if (!c.width) {
294
- // 只有未设置width的非固定列才参与自适应
295
- count++
296
- }
297
- }
298
- })
299
- }
300
- countCols(this.columns)
301
- return count
302
- },
303
-
304
- // 自适应列宽(未设置width的非固定列均分剩余宽度)
305
- autoColumnWidth() {
306
- const totalWidth = this.tableWidth || 1000
307
- const gutter = 12 // el-table 默认 gutter
308
- const fixedWidth = this.fixedColumnWidth
309
- const nonFixedWithWidth = this.nonFixedColumnWidthWithWidth
310
- const autoCount = this.autoColumnCount
311
-
312
- if (autoCount <= 0) return 100 // 默认宽度
313
-
314
- // 自适应宽度 = (总宽度 - gutter - 固定列宽度 - 非固定列中已设置的width)/ 需要自适应的列数
315
- const availableWidth = totalWidth - gutter - fixedWidth - nonFixedWithWidth
316
- return Math.max(80, Math.floor(availableWidth / autoCount)) // 最小80px
317
- },
318
-
319
- // 表头列定义(用于 colgroup,与 headerRows 列数保持一致)
320
- headerCols() {
321
- const cols = []
322
-
323
- // 选择列
324
- if (this.selection) {
325
- cols.push({ _key: 'prepend_selection', _width: 55, _minWidth: 55 })
326
- }
327
-
328
- // 序号列
329
- if (this.showIndex) {
330
- cols.push({ _key: 'prepend_index', _width: 60, _minWidth: 60 })
331
- }
332
-
333
- // 数据列(只取叶子节点,使用一致的key生成)
334
- const addLeafCols = (columns, parentPath = '') => {
335
- columns.forEach((col, index) => {
336
- const path = parentPath ? `${parentPath}_${index}` : String(index)
337
- const key = this.generateColKey(col, path)
338
-
339
- if (col.children && col.children.length) {
340
- addLeafCols(col.children, path)
341
- } else {
342
- cols.push({
343
- _key: key,
344
- _width: this.getColumnWidth(col),
345
- _minWidth: col.minWidth
346
- })
347
- }
348
- })
349
- }
350
- addLeafCols(this.columns)
351
-
352
- return cols
353
- },
354
-
355
- // 表头层级结构(用于自定义固定表头渲染)
356
- headerRows() {
357
- const rows = []
358
- let maxDepth = 1
359
-
360
- // 计算最大深度
361
- const calcDepth = (cols, depth) => {
362
- maxDepth = Math.max(maxDepth, depth)
363
- cols.forEach(c => { if (c.children && c.children.length) calcDepth(c.children, depth + 1) })
364
- }
365
- if (this.columns.length) calcDepth(this.columns, 1)
366
-
367
- // 初始化行
368
- for (let i = 0; i < maxDepth; i++) rows.push([])
369
-
370
- // 计算叶子数量
371
- const countLeaves = (cols) => {
372
- let n = 0
373
- cols.forEach(c => {
374
- if (c.children && c.children.length) n += countLeaves(c.children)
375
- else n++
376
- })
377
- return n
378
- }
379
-
380
- // 选择列 / 序号列占位(仅第一行,rowspan 跨所有行)
381
- if (this.selection) {
382
- rows[0].push({
383
- _key: 'prepend_selection',
384
- _rowspan: maxDepth,
385
- _colspan: 1,
386
- _style: { width: '55px' },
387
- _hidden: false,
388
- label: ''
389
- })
390
- for (let i = 1; i < maxDepth; i++) {
391
- rows[i].push({ _key: 'prepend_selection_' + i, _rowspan: 0, _colspan: 1, _hidden: true, label: '' })
392
- }
393
- }
394
- if (this.showIndex) {
395
- rows[0].push({
396
- _key: 'prepend_index',
397
- _rowspan: maxDepth,
398
- _colspan: 1,
399
- _style: { width: '60px' },
400
- _hidden: false,
401
- label: '#'
402
- })
403
- for (let i = 1; i < maxDepth; i++) {
404
- rows[i].push({ _key: 'prepend_index_' + i, _rowspan: 0, _colspan: 1, _hidden: true, label: '' })
405
- }
406
- }
407
-
408
- // 填充列
409
- if (this.columns.length) {
410
- const fill = (cols, depth, parentPath = '') => {
411
- cols.forEach((c, index) => {
412
- const path = parentPath ? `${parentPath}_${index}` : String(index)
413
- const key = this.generateColKey(c, path)
414
- const hasKids = c.children && c.children.length
415
- const style = {}
416
-
417
- // 如果有固定宽度,使用固定宽度;否则使用自适应宽度
418
- if (c.width) {
419
- style.width = typeof c.width === 'number' ? c.width + 'px' : c.width
420
- } else if (!c.fixed && this.virtualScroll) {
421
- // 非固定列且虚拟滚动时使用自适应宽度
422
- style.width = this.autoColumnWidth + 'px'
423
- }
424
-
425
- if (c.minWidth) style.minWidth = typeof c.minWidth === 'number' ? c.minWidth + 'px' : c.minWidth
426
-
427
- rows[depth].push({
428
- _key: key,
429
- _rowspan: hasKids ? 1 : maxDepth - depth,
430
- _colspan: hasKids ? countLeaves(c.children) : 1,
431
- _style: style,
432
- _hidden: !c.label && !c.prop,
433
- label: c.label || ''
434
- })
435
- if (hasKids) fill(c.children, depth + 1, path)
436
- })
437
- }
438
- fill(this.columns, 0)
439
- }
440
-
441
- return rows
266
+ // 虚拟滚动固定表头占位数据(仅渲染表头,不渲染 body 行)
267
+ headerTableData() {
268
+ return []
442
269
  },
443
270
 
444
271
  // 处理后的数据(注入小计/总计行)
@@ -491,22 +318,25 @@ export default {
491
318
  return start * this.rowHeight
492
319
  },
493
320
 
494
- // 扁平化列配置(处理 children 嵌套)
321
+ // 扁平化列配置(为每级列补充 _key)
495
322
  flattenedColumns() {
496
323
  if (this.flattenedColumnsCache.length) return this.flattenedColumnsCache
497
- const result = []
498
- const flatten = (cols) => {
499
- cols.forEach(col => {
500
- const key = col.prop || col.label || Math.random().toString(36).slice(2)
501
- result.push({ ...col, _key: key })
324
+ const assignKeys = (cols, parentPath = '') => {
325
+ return cols.map((col, index) => {
326
+ const path = parentPath ? `${parentPath}_${index}` : String(index)
327
+ const key = col.prop || col.slot || col.label || path
328
+ const item = { ...col, _key: key }
329
+ if (col.children && col.children.length) {
330
+ item.children = assignKeys(col.children, path)
331
+ }
332
+ return item
502
333
  })
503
334
  }
504
- flatten(this.columns)
505
- this.flattenedColumnsCache = result
506
- return result
335
+ this.flattenedColumnsCache = assignKeys(this.columns)
336
+ return this.flattenedColumnsCache
507
337
  },
508
338
 
509
- // 虚拟滚动时的可见数据
339
+ // 虚拟滚动时的可见数据(附带 _realIndex 供序号列使用)
510
340
  visibleData() {
511
341
  const data = this.processedTableData
512
342
  if (!this.virtualScroll || data.length === 0) {
@@ -514,7 +344,10 @@ export default {
514
344
  }
515
345
  const start = Math.max(0, this.visibleStartIndex - this.bufferSize)
516
346
  const end = Math.min(data.length, this.visibleEndIndex + this.bufferSize)
517
- return data.slice(start, end)
347
+ return data.slice(start, end).map((row, i) => ({
348
+ ...row,
349
+ _realIndex: start + i
350
+ }))
518
351
  }
519
352
  },
520
353
 
@@ -527,6 +360,7 @@ export default {
527
360
  this.$nextTick(() => {
528
361
  this.measureRowHeight()
529
362
  this.updateVisibleRange()
363
+ this.syncTableLayout()
530
364
  })
531
365
  }
532
366
  },
@@ -536,10 +370,25 @@ export default {
536
370
  columns: {
537
371
  handler() {
538
372
  this.flattenedColumnsCache = []
373
+ if (this.virtualScroll) {
374
+ this.$nextTick(() => this.syncTableLayout())
375
+ }
539
376
  },
540
377
  deep: true
541
378
  },
542
379
 
380
+ height() {
381
+ if (this.virtualScroll) {
382
+ this.$nextTick(() => this.syncTableLayout())
383
+ }
384
+ },
385
+
386
+ maxHeight() {
387
+ if (this.virtualScroll) {
388
+ this.$nextTick(() => this.syncTableLayout())
389
+ }
390
+ },
391
+
543
392
  virtualScroll(val) {
544
393
  if (val) {
545
394
  this.$nextTick(() => this.initVirtualScroll())
@@ -556,7 +405,7 @@ export default {
556
405
  // 监听窗口resize,更新表格宽度
557
406
  this.resizeHandler = () => {
558
407
  if (this.virtualScroll) {
559
- this.$nextTick(() => this.updateTableWidth())
408
+ this.$nextTick(() => this.syncTableLayout())
560
409
  }
561
410
  }
562
411
  window.addEventListener('resize', this.resizeHandler)
@@ -795,41 +644,35 @@ export default {
795
644
  },
796
645
 
797
646
  doLayout() {
798
- this.$refs.table && this.$refs.table.doLayout()
647
+ this.syncTableLayout()
799
648
  },
800
649
 
801
- // 获取列宽度(支持自适应)
802
- getColumnWidth(col) {
803
- // 如果有固定宽度,使用固定宽度
804
- if (col.width) {
805
- return typeof col.width === 'number' ? col.width : col.width.replace('px', '')
806
- }
807
- // 非固定列且虚拟滚动时使用自适应宽度
808
- if (!col.fixed && this.virtualScroll) {
809
- return this.autoColumnWidth
810
- }
811
- // 默认宽度
812
- if (col.minWidth) {
813
- return typeof col.minWidth === 'number' ? col.minWidth : col.minWidth.replace('px', '')
814
- }
815
- return 100
650
+ // ==================== 虚拟滚动固定表头 ====================
651
+ syncTableLayout() {
652
+ const bodyTable = this.$refs.table
653
+ const headerTable = this.$refs.headerTable
654
+ if (bodyTable) bodyTable.doLayout()
655
+ if (headerTable) headerTable.doLayout()
656
+ this.syncHeaderScroll(this.scrollLeft)
816
657
  },
817
658
 
818
- // 更新表格宽度(用于自适应列宽计算)
819
- updateTableWidth() {
820
- const tableWrapper = this.$refs.tableBody
821
- if (tableWrapper) {
822
- this.tableWidth = tableWrapper.offsetWidth || 1000
659
+ syncHeaderScroll(scrollLeft) {
660
+ const headerTable = this.$refs.headerTable
661
+ if (!headerTable) return
662
+ const headerWrapper = headerTable.$el.querySelector('.el-table__header-wrapper')
663
+ if (headerWrapper) {
664
+ headerWrapper.scrollLeft = scrollLeft
823
665
  }
824
666
  },
825
667
 
826
668
  // ==================== 虚拟滚动 ====================
827
669
  initVirtualScroll() {
828
- this.updateTableWidth()
670
+ this.rowHeight = this.rowInitHeight
829
671
  this.measureRowHeight()
830
672
  if (this.processedTableData.length > 0) {
831
673
  this.updateVisibleRange()
832
674
  }
675
+ this.syncTableLayout()
833
676
  },
834
677
 
835
678
  measureRowHeight() {
@@ -847,6 +690,7 @@ export default {
847
690
  if (this.virtualScroll) {
848
691
  this.scrollTop = target.scrollTop
849
692
  this.updateVisibleRange()
693
+ this.syncHeaderScroll(target.scrollLeft)
850
694
  }
851
695
  },
852
696
 
@@ -945,8 +789,12 @@ export default {
945
789
 
946
790
  /* 虚拟滚动容器 */
947
791
  .ex-table-virtual {
948
- overflow: auto;
792
+ overflow: overlay;
949
793
  position: relative;
794
+ /* Firefox 隐藏滚动条 */
795
+ scrollbar-width: none;
796
+ /* IE/旧 Edge 隐藏滚动条 */
797
+ -ms-overflow-style: none;
950
798
  }
951
799
 
952
800
  .ex-table-virtual-inner {
@@ -964,39 +812,25 @@ export default {
964
812
  padding: 16px 0;
965
813
  }
966
814
 
967
- /* 虚拟滚动固定表头 */
815
+ /* 虚拟滚动固定表头(header-only el-table) */
968
816
  .ex-table-fixed-header {
969
817
  overflow: hidden;
970
- border-bottom: 1px solid #ebeef5;
971
- background: #fafafa;
818
+ flex-shrink: 0;
972
819
  }
973
820
 
974
- .ex-table-fixed-header-table {
975
- table-layout: fixed;
976
- width: 100%;
977
- border-collapse: collapse;
821
+ /* 虚拟滚动表头强制补上 gutter 占位 */
822
+ .ex-table-fixed-header >>> .el-table__header-wrapper thead tr .el-table__cell.gutter {
823
+ display: table-cell;
824
+ width: 8px; /* 和滚动条宽度一致 */
978
825
  }
979
826
 
980
- .ex-table-fixed-header-table th {
981
- padding: 12px 8px;
982
- text-align: left;
983
- font-weight: 500;
984
- font-size: 14px;
985
- color: #606266;
986
- border-bottom: 1px solid #ebeef5;
987
- border-right: 1px solid #ebeef5;
988
- white-space: nowrap;
989
- overflow: hidden;
990
- text-overflow: ellipsis;
991
- background: #fafafa;
992
- box-sizing: border-box;
993
- }
994
827
 
995
- .ex-table-fixed-header-table th:last-child {
996
- border-right: none;
828
+ .ex-table-header-only >>> .el-table__body-wrapper,
829
+ .ex-table-header-only >>> .el-table__empty-block {
830
+ display: none;
997
831
  }
998
832
 
999
- .ex-table-fixed-header-table th:first-child {
1000
- padding-left: 16px;
833
+ .ex-table-header-only >>> .el-table__header-wrapper {
834
+ overflow: hidden;
1001
835
  }
1002
836
  </style>
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 生成虚拟滚动演示数据
3
+ * @param {number} count 数据条数,默认 10000
4
+ * @returns {Array}
5
+ */
6
+ export function createVirtualScrollData(count = 10000) {
7
+ const departments = ['研发部', '产品部', '运营部', '财务部', '市场部']
8
+ const positions = ['工程师', '产品经理', '运营专员', '财务主管', '市场经理']
9
+ const statuses = ['在职', '休假', '离职']
10
+
11
+ return Array.from({ length: count }, (_, i) => ({
12
+ id: i + 1,
13
+ name: `用户${i + 1}`,
14
+ department: departments[i % departments.length],
15
+ position: positions[i % positions.length],
16
+ email: `user${i + 1}@example.com`,
17
+ phone: `13800138${String(i).padStart(4, '0')}`,
18
+ address: `北京市朝阳区第${i + 1}号街道`,
19
+ salary: 5000 + Math.floor(Math.random() * 10000),
20
+ status: statuses[i % statuses.length]
21
+ }))
22
+ }
23
+
24
+ /** 虚拟滚动演示列配置 */
25
+ export const virtualScrollColumns = [
26
+ { prop: 'id', label: 'ID', fixed: 'left', width: 80 },
27
+ { prop: 'name', label: '姓名', fixed: 'left', width: 120 },
28
+ { prop: 'department', label: '部门', width: 150 },
29
+ { prop: 'position', label: '职位', width: 150 },
30
+ { prop: 'email', label: '邮箱', width: 200 },
31
+ { prop: 'phone', label: '电话', width: 130 },
32
+ { prop: 'address', label: '地址', width: 200 },
33
+ { prop: 'salary', label: '薪资', width: 120 },
34
+ { prop: 'status', label: '状态', fixed: 'right', width: 100 }
35
+ ]
@@ -13,25 +13,25 @@ export default {
13
13
  },
14
14
  watch: {
15
15
  theme(newVal) {
16
- this.$provide('xtConfig', {
16
+ this.xtConfig ={
17
17
  theme: newVal,
18
18
  size: this.size,
19
19
  primaryColor: this.primaryColor
20
- })
20
+ }
21
21
  },
22
22
  size(newVal) {
23
- this.$provide('xtConfig', {
24
- theme: this.theme,
25
- size: newVal,
23
+ this.xtConfig ={
24
+ theme: newVal,
25
+ size: this.size,
26
26
  primaryColor: this.primaryColor
27
- })
27
+ }
28
28
  },
29
29
  primaryColor(newVal) {
30
- this.$provide('xtConfig', {
31
- theme: this.theme,
30
+ this.xtConfig ={
31
+ theme: newVal,
32
32
  size: this.size,
33
- primaryColor: newVal
34
- })
33
+ primaryColor: this.primaryColor
34
+ }
35
35
  }
36
36
  },
37
37
  render(h) {
@@ -76,17 +76,17 @@
76
76
  }
77
77
 
78
78
  // 暗色主题
79
- html[data-theme='dark'] .xt-text--primary {
79
+ [data-theme='dark'] .xt-text--primary {
80
80
  }
81
81
 
82
- html[data-theme='dark'] .xt-text--success {
82
+ [data-theme='dark'] .xt-text--success {
83
83
  color: $xt-dark-color-success;
84
84
  }
85
85
 
86
- html[data-theme='dark'] .xt-text--warning {
86
+ [data-theme='dark'] .xt-text--warning {
87
87
  color: $xt-dark-color-warning;
88
88
  }
89
89
 
90
- html[data-theme='dark'] .xt-text--danger {
90
+ [data-theme='dark'] .xt-text--danger {
91
91
  color: $xt-dark-color-danger;
92
92
  }
package/src/index.js CHANGED
@@ -96,15 +96,16 @@ const install = function (Vue, options = {}) {
96
96
  }
97
97
  }
98
98
 
99
- // 仅在开发环境下尝试注册 ElementUI 组件为 Ex 开头(方便文档和示例使用)
100
- // 生产环境发布时不需要,避免不必要的依赖
101
- if (process.env.NODE_ENV === 'development') {
102
- try {
103
- const ElementUI = require('element-ui')
104
- registerElementExComponents(Vue, ElementUI)
105
- } catch (e) {
106
- console.warn('[XT-UI] ElementUI not found, skipping Ex- prefix registration for Element components')
107
- }
99
+ if (process.env.NODE_ENV === 'development') {
100
+ // 导入内部 element-ui 依赖(作为内部依赖,不与外部冲突)
101
+ const ElementUI = require('element-ui')
102
+
103
+ // 注册 ElementUI 的基础组件(用于 ExTable 等封装组件内部使用)
104
+ // 不调用 Vue.use(ElementUI),避免全局污染
105
+ // ex-table 等组件内部会直接使用 element-ui 的组件
106
+
107
+ // 在开发环境下注册 ElementUI 组件为 Ex 开头(方便文档和示例使用)
108
+ registerElementExComponents(Vue, ElementUI)
108
109
  }
109
110
 
110
111
  // 调用统一的注册函数
@@ -139,6 +140,9 @@ export default {
139
140
  // ExChart 组件按需导出(使用时需自行安装 echarts 依赖)
140
141
  export { default as ExChart } from './components/ex-chart'
141
142
 
143
+ // ExTable 虚拟滚动演示数据工具
144
+ export { createVirtualScrollData, virtualScrollColumns } from './components/ex-table/virtualScrollData'
145
+
142
146
  // 导出工具函数和变量
143
147
  export {
144
148
  utils,
@@ -183,7 +183,6 @@
183
183
  // 暗色主题(参考 Element Plus)
184
184
  // ============================
185
185
  :root[data-theme="dark"],
186
- html[data-theme="dark"],
187
186
  [data-theme="dark"] {
188
187
  color-scheme: dark;
189
188
  --xt-color-primary: #409eff;