@tdesign/uniapp 0.8.1 → 0.9.0

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/badge/README.en-US.md +1 -0
  3. package/dist/badge/README.md +1 -0
  4. package/dist/badge/badge.css +1 -1
  5. package/dist/button/button.vue +5 -0
  6. package/dist/calendar/calendar.vue +6 -2
  7. package/dist/common/style/theme/index-light.css +282 -0
  8. package/dist/common/style/theme/index-light.less +9 -0
  9. package/dist/common/style/theme/raw/_components-light.less +8 -0
  10. package/dist/common/style/theme/raw/_light-only.less +181 -0
  11. package/dist/dropdown-item/dropdown-item.vue +2 -0
  12. package/dist/dropdown-menu/dropdown-menu.vue +1 -1
  13. package/dist/fab/fab.vue +2 -2
  14. package/dist/fab/props.ts +1 -1
  15. package/dist/fab/type.ts +1 -1
  16. package/dist/form-item/form-item.css +2 -2
  17. package/dist/form-item/form-item.vue +28 -21
  18. package/dist/indexes/computed.js +6 -2
  19. package/dist/indexes/indexes.css +7 -2
  20. package/dist/indexes/indexes.vue +1 -1
  21. package/dist/indexes/props.ts +5 -0
  22. package/dist/indexes/type.ts +5 -0
  23. package/dist/input/input.vue +8 -6
  24. package/dist/search/search.css +5 -0
  25. package/dist/search/search.vue +7 -12
  26. package/dist/segmented/README.en-US.md +42 -0
  27. package/dist/segmented/README.md +75 -0
  28. package/dist/segmented/props.ts +31 -0
  29. package/dist/segmented/segmented.css +66 -0
  30. package/dist/segmented/segmented.vue +174 -0
  31. package/dist/segmented/type.ts +41 -0
  32. package/dist/tab-bar-item/tab-bar-item.vue +4 -6
  33. package/dist/table/README.en-US.md +72 -0
  34. package/dist/table/README.md +117 -0
  35. package/dist/table/base-table-props.ts +105 -0
  36. package/dist/table/props.ts +94 -0
  37. package/dist/table/table.css +251 -0
  38. package/dist/table/table.vue +551 -0
  39. package/dist/table/type.ts +180 -0
  40. package/dist/tabs/tabs.css +4 -0
  41. package/dist/theme-light.css +282 -0
  42. package/dist/theme-light.css.d.ts +2 -0
  43. package/dist/theme-light.less +1 -0
  44. package/dist/theme-light.less.d.ts +2 -0
  45. package/dist/types/index.d.ts +2 -0
  46. package/dist/types/segmented.d.ts +7 -0
  47. package/dist/types/table.d.ts +7 -0
  48. package/global.d.ts +2 -0
  49. package/package.json +33 -9
  50. package/{dist/script → script}/postinstall.js +18 -2
@@ -0,0 +1,551 @@
1
+ <template>
2
+ <view
3
+ :class="[
4
+ classPrefix,
5
+ tClass,
6
+ classPrefix + '--layout-' + tableLayout,
7
+ prefix + '-vertical-align-' + verticalAlign,
8
+ bordered ? classPrefix + '--bordered' : '',
9
+ stripe ? classPrefix + '--striped' : '',
10
+ (stripe && (maxHeight || height)) ? classPrefix + '--header-fixed' : '',
11
+ loading ? classPrefix + '--loading' : '',
12
+ rowspanAndColspan ? classPrefix + '--rowspan-colspan' : '',
13
+ hasFixedColumn ? classPrefix + '--column-fixed' : '',
14
+ ]"
15
+ :style="'' + tools._style([customStyle])"
16
+ >
17
+ <scroll-view
18
+ :class="contentClasses"
19
+ :style="tableContentStylesStr"
20
+ scroll-x
21
+ :scroll-y="!!(height || maxHeight)"
22
+ @scroll="onScroll"
23
+ >
24
+ <view
25
+ :class="classPrefix + '__table-elm'"
26
+ :style="tableElementStylesStr"
27
+ >
28
+ <!-- 表头 -->
29
+ <view
30
+ v-if="showHeader"
31
+ :class="[
32
+ classPrefix + '__header',
33
+ (maxHeight || height) ? classPrefix + '__header--fixed' : '',
34
+ ]"
35
+ >
36
+ <view :class="classPrefix + '__header-tr'">
37
+ <view
38
+ v-for="(col, colIndex) in columns"
39
+ :key="col.colKey || colIndex"
40
+ :class="[
41
+ classPrefix + '__th',
42
+ getThClassName(col, colIndex),
43
+ ]"
44
+ :style="'' + getColStyle(col, colIndex)"
45
+ >
46
+ <view :class="classPrefix + '__th-content'">
47
+ {{ col.title }}
48
+ </view>
49
+ </view>
50
+ </view>
51
+ </view>
52
+
53
+ <!-- 表体 -->
54
+ <view :class="classPrefix + '__body'">
55
+ <!-- 空数据 -->
56
+ <view
57
+ v-if="isEmpty"
58
+ :class="classPrefix + '__empty-row'"
59
+ >
60
+ <view :class="classPrefix + '__empty'">
61
+ <template v-if="empty">
62
+ {{ empty }}
63
+ </template>
64
+ <slot
65
+ v-else
66
+ name="empty"
67
+ />
68
+ </view>
69
+ </view>
70
+
71
+ <!-- 数据行 -->
72
+ <view
73
+ v-for="rowItem in renderData"
74
+ :key="rowItem.rowId"
75
+ :class="[classPrefix + '__tr', rowItem.rowClass]"
76
+ :style="rowItem.rowStyle"
77
+ @click="onRowClick(rowItem.rowIndex)"
78
+ >
79
+ <!-- #ifdef VUE2 -->
80
+ <!-- eslint-disable-next-line vue/no-unused-vars -->
81
+ <template v-for="(cell, tdIndex) in rowItem.cells">
82
+ <!-- #endif -->
83
+ <!-- #ifdef VUE3 -->
84
+ <template
85
+ v-for="(cell, tdIndex) in rowItem.cells"
86
+ :key="cell.colKey"
87
+ >
88
+ <!-- #endif -->
89
+
90
+ <view
91
+ v-if="!cell.skipped"
92
+ :key="cell.colKey"
93
+ :class="[
94
+ classPrefix + '__td',
95
+ cell.className,
96
+ cell.isLastRow ? classPrefix + '__td-last-row' : '',
97
+ cell.isFirstCol ? classPrefix + '__td-first-col' : '',
98
+ ]"
99
+ :style="'' + getColStyle(columns[tdIndex], tdIndex)"
100
+ @click.stop="onCellClick(rowItem.rowIndex, tdIndex)"
101
+ >
102
+ <view :class="classPrefix + '__td-content'">
103
+ {{ cell.content }}
104
+ </view>
105
+ </view>
106
+ <!-- #ifdef VUE3 -->
107
+ </template>
108
+ <!-- #endif -->
109
+
110
+ <!-- #ifdef VUE2 -->
111
+ </template>
112
+ <!-- #endif -->
113
+ </view>
114
+ </view>
115
+ </view>
116
+
117
+ <!-- 加载中 -->
118
+ <view
119
+ v-if="loading"
120
+ :class="classPrefix + '__loading--full'"
121
+ >
122
+ <slot name="loading">
123
+ <t-loading v-bind="loadingProps" />
124
+ </slot>
125
+ </view>
126
+ </scroll-view>
127
+
128
+ <!-- 表尾总结行 -->
129
+ <view
130
+ v-if="footerSummary"
131
+ :class="classPrefix + '__bottom-content'"
132
+ >
133
+ {{ footerSummary }}
134
+ </view>
135
+ <slot name="footer-summary" />
136
+ </view>
137
+ </template>
138
+ <script>
139
+ import TLoading from '../loading/loading.vue';
140
+ import { uniComponent } from '../common/src/index';
141
+ import { prefix } from '../common/config';
142
+ import props from './base-table-props';
143
+ import tools from '../common/utils.wxs';
144
+
145
+ const name = `${prefix}-table`;
146
+
147
+ function getVal(obj, path) {
148
+ if (!obj || !path) return undefined;
149
+ const keys = path.split('.');
150
+ let result = obj;
151
+ keys.forEach((key) => {
152
+ if (result !== undefined && result !== null) {
153
+ result = result[key];
154
+ }
155
+ });
156
+ return result;
157
+ }
158
+
159
+ function formatCSSUnit(unit) {
160
+ if (!unit) return unit;
161
+ return Number.isNaN(Number(unit)) ? unit : `${unit}px`;
162
+ }
163
+
164
+ export default {
165
+ components: {
166
+ TLoading,
167
+ },
168
+ emits: [
169
+ 'row-click',
170
+ 'cell-click',
171
+ 'scroll',
172
+ 'scroll-to-bottom',
173
+ ],
174
+ ...uniComponent({
175
+ name,
176
+ options: {
177
+ styleIsolation: 'shared',
178
+ },
179
+ externalClasses: [
180
+ `${prefix}-class`,
181
+ ],
182
+ props: {
183
+ ...props,
184
+ },
185
+ data() {
186
+ return {
187
+ prefix,
188
+ classPrefix: name,
189
+ tools,
190
+ renderData: [],
191
+ isEmpty: false,
192
+ hasFixedColumn: false,
193
+ scrollableToLeft: false,
194
+ scrollableToRight: false,
195
+ fixedLeftOffsets: [],
196
+ fixedRightOffsets: [],
197
+ lastFixedLeftIndex: -1,
198
+ firstFixedRightIndex: -1,
199
+ fixedTopRows: 0,
200
+ fixedBottomRows: 0,
201
+ };
202
+ },
203
+ computed: {
204
+ contentClasses() {
205
+ const classes = [`${this.classPrefix}__content`];
206
+ if (this.scrollableToLeft) classes.push(`${this.classPrefix}__content--scrollable-to-left`);
207
+ if (this.scrollableToRight) classes.push(`${this.classPrefix}__content--scrollable-to-right`);
208
+ return classes.join(' ');
209
+ },
210
+ tableContentStylesStr() {
211
+ const styles = [];
212
+ if (this.height) styles.push(`height: ${formatCSSUnit(this.height)}`);
213
+ if (this.maxHeight) styles.push(`max-height: ${formatCSSUnit(this.maxHeight)}`);
214
+ return styles.join('; ');
215
+ },
216
+ tableElementStylesStr() {
217
+ // 优先使用用户指定的 tableContentWidth
218
+ if (this.tableContentWidth) {
219
+ return `width: ${formatCSSUnit(this.tableContentWidth)}`;
220
+ }
221
+ // 有固定列时,自动根据列宽之和计算 tableContentWidth
222
+ if (this.hasFixedColumn && this.columns && this.columns.length > 0) {
223
+ const totalWidth = this.columns.reduce((sum, col) => sum + parseFloat(String(col.width || 80)), 0);
224
+ return `width: ${totalWidth}px`;
225
+ }
226
+ return '';
227
+ },
228
+ },
229
+ watch: {
230
+ columns: {
231
+ handler() {
232
+ this.updateRenderData();
233
+ },
234
+ deep: true,
235
+ immediate: true,
236
+ },
237
+ data: {
238
+ handler() {
239
+ this.updateRenderData();
240
+ },
241
+ deep: true,
242
+ immediate: true,
243
+ },
244
+ cellEmptyContent() {
245
+ this.updateRenderData();
246
+ },
247
+ rowKey() {
248
+ this.updateRenderData();
249
+ },
250
+ rowspanAndColspan() {
251
+ this.updateRenderData();
252
+ },
253
+ fixedRows: {
254
+ handler() {
255
+ this.updateRenderData();
256
+ },
257
+ deep: true,
258
+ },
259
+ },
260
+ methods: {
261
+ getColStyle(col, colIndex) {
262
+ if (!col) return '';
263
+ const defaultColWidth = this.tableLayout === 'fixed' ? '80px' : undefined;
264
+ const width = formatCSSUnit(col.width || defaultColWidth);
265
+ const minWidth = !formatCSSUnit(col.width || defaultColWidth) && !col.minWidth && this.tableLayout === 'fixed' ? '80px' : formatCSSUnit(col.minWidth);
266
+ const styles = [];
267
+ // 当列设置了固定宽度时,使用 flex: 0 0 auto 防止被 flex 压缩,支持横向滚动
268
+ if (col.width) {
269
+ styles.push('flex: 0 0 auto');
270
+ }
271
+ if (width) styles.push(`width: ${width}`);
272
+ if (minWidth) styles.push(`min-width: ${minWidth}`);
273
+ // 固定列定位
274
+ if (col.fixed === 'left' && this.fixedLeftOffsets[colIndex] !== undefined) {
275
+ styles.push(`left: ${this.fixedLeftOffsets[colIndex]}px`);
276
+ } else if (col.fixed === 'right' && this.fixedRightOffsets[colIndex] !== undefined) {
277
+ styles.push(`right: ${this.fixedRightOffsets[colIndex]}px`);
278
+ }
279
+ return styles.join('; ');
280
+ },
281
+
282
+ getThClassName(col, colIndex) {
283
+ const classes = [];
284
+ if (col.colKey) classes.push(`${name}__th-${col.colKey}`);
285
+ if (col.align && col.align !== 'left') classes.push(`${prefix}-align-${col.align}`);
286
+ if (col.fixed === 'left') classes.push(`${name}__cell--fixed-left`);
287
+ if (col.fixed === 'right') classes.push(`${name}__cell--fixed-right`);
288
+ if (colIndex === this.lastFixedLeftIndex) classes.push(`${name}__cell--fixed-left-last`);
289
+ if (colIndex === this.firstFixedRightIndex) classes.push(`${name}__cell--fixed-right-first`);
290
+ return classes.join(' ');
291
+ },
292
+
293
+ updateRenderData() {
294
+ const { columns, data, cellEmptyContent, rowKey, rowspanAndColspan, fixedRows } = this;
295
+
296
+ // 解析 fixedRows
297
+ const fixedTopRows = (fixedRows && fixedRows[0]) || 0;
298
+ const fixedBottomRows = (fixedRows && fixedRows[1]) || 0;
299
+ this.fixedTopRows = fixedTopRows;
300
+ this.fixedBottomRows = fixedBottomRows;
301
+
302
+ // 是否有固定表头
303
+ const hasFixedHeader = !!(this.showHeader && (this.maxHeight || this.height));
304
+
305
+ const dataLen = (data || []).length;
306
+
307
+ // 检测是否有固定列
308
+ const hasFixedColumn = (columns || []).some(col => !!col.fixed);
309
+ this.hasFixedColumn = hasFixedColumn;
310
+
311
+ // 计算固定列偏移量
312
+ if (hasFixedColumn) {
313
+ const fixedLeftOffsets = [];
314
+ const fixedRightOffsets = [];
315
+ let leftOffset = 0;
316
+ for (let i = 0; i < (columns || []).length; i += 1) {
317
+ fixedLeftOffsets[i] = leftOffset;
318
+ const col = columns[i];
319
+ if (col.fixed === 'left') {
320
+ leftOffset += parseFloat(String(col.width || 80));
321
+ }
322
+ }
323
+ let rightOffset = 0;
324
+ for (let i = (columns || []).length - 1; i >= 0; i -= 1) {
325
+ fixedRightOffsets[i] = rightOffset;
326
+ const col = columns[i];
327
+ if (col.fixed === 'right') {
328
+ rightOffset += parseFloat(String(col.width || 80));
329
+ }
330
+ }
331
+ this.fixedLeftOffsets = fixedLeftOffsets;
332
+ this.fixedRightOffsets = fixedRightOffsets;
333
+
334
+ // 找到最后一个左固定列和第一个右固定列的索引
335
+ let lastLeft = -1;
336
+ let firstRight = -1;
337
+ (columns || []).forEach((col, index) => {
338
+ if (col.fixed === 'left') lastLeft = index;
339
+ if (col.fixed === 'right' && firstRight === -1) firstRight = index;
340
+ });
341
+ this.lastFixedLeftIndex = lastLeft;
342
+ this.firstFixedRightIndex = firstRight;
343
+
344
+ // 初始状态:可向右滚动
345
+ this.scrollableToRight = true;
346
+ this.scrollableToLeft = false;
347
+ }
348
+
349
+ // 计算合并单元格
350
+ const skipSpansMap = new Map();
351
+ if (rowspanAndColspan && data?.length && columns?.length) {
352
+ for (let i = 0; i < data.length; i += 1) {
353
+ const row = data[i];
354
+ for (let j = 0; j < columns.length; j += 1) {
355
+ const col = columns[j];
356
+ const cellKey = `${getVal(row, rowKey || 'id')}_${col.colKey || j}`;
357
+ const state = skipSpansMap.get(cellKey) || {};
358
+ const o = rowspanAndColspan({ row, col, rowIndex: i, colIndex: j }) || {};
359
+ if (o.rowspan || o.colspan || state.rowspan || state.colspan) {
360
+ if (o.rowspan) state.rowspan = o.rowspan;
361
+ if (o.colspan) state.colspan = o.colspan;
362
+ skipSpansMap.set(cellKey, state);
363
+ }
364
+ if (state.rowspan || state.colspan) {
365
+ const maxRowIndex = i + (state.rowspan || 1);
366
+ const maxColIndex = j + (state.colspan || 1);
367
+ for (let ri = i; ri < maxRowIndex; ri += 1) {
368
+ for (let ci = j; ci < maxColIndex; ci += 1) {
369
+ if (ri !== i || ci !== j) {
370
+ if (data[ri] && columns[ci]) {
371
+ const key = `${getVal(data[ri], rowKey || 'id')}_${columns[ci].colKey || ci}`;
372
+ const s = skipSpansMap.get(key) || {};
373
+ s.skipped = true;
374
+ skipSpansMap.set(key, s);
375
+ }
376
+ }
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ // 构建渲染数据
385
+ const renderData = (data || []).map((row, rowIndex) => {
386
+ const cells = (columns || []).map((col, colIndex) => {
387
+ const cellKey = `${getVal(row, rowKey || 'id')}_${col.colKey || colIndex}`;
388
+ const spanState = skipSpansMap.get(cellKey);
389
+
390
+ const tdClasses = [];
391
+ if (col.align && col.align !== 'left') tdClasses.push(`${prefix}-align-${col.align}`);
392
+ if (col.fixed === 'left') tdClasses.push(`${name}__cell--fixed-left`);
393
+ if (col.fixed === 'right') tdClasses.push(`${name}__cell--fixed-right`);
394
+ if (colIndex === this.lastFixedLeftIndex) tdClasses.push(`${name}__cell--fixed-left-last`);
395
+ if (colIndex === this.firstFixedRightIndex) tdClasses.push(`${name}__cell--fixed-right-first`);
396
+
397
+ let cellContent = '';
398
+ if (col.colKey === 'serial-number') {
399
+ cellContent = String(rowIndex + 1);
400
+ } else if (typeof col.cell === 'function') {
401
+ cellContent = col.cell({ row, col, rowIndex, colIndex });
402
+ } else {
403
+ const val = getVal(row, col.colKey || '');
404
+ if (val !== undefined && val !== null && val !== '') {
405
+ cellContent = String(val);
406
+ } else if (cellEmptyContent) {
407
+ cellContent = cellEmptyContent;
408
+ }
409
+ }
410
+
411
+ return {
412
+ colKey: col.colKey || String(colIndex),
413
+ content: cellContent,
414
+ className: tdClasses.join(' '),
415
+ skipped: spanState?.skipped || false,
416
+ rowspan: spanState?.rowspan || 0,
417
+ colspan: spanState?.colspan || 0,
418
+ isLastRow: !!(spanState?.rowspan && rowIndex + spanState.rowspan === data.length),
419
+ isFirstCol: !!(rowspanAndColspan && colIndex === 0),
420
+ };
421
+ });
422
+
423
+ // 固定行类名和样式
424
+ const rowClasses = [];
425
+ let rowStyle = '';
426
+ // 固定行的 z-index 需要高于固定列的 z-index(右侧固定列 z-index 为 31),否则横向滚动时固定列内容会覆盖固定行
427
+ const fixedRowZIndex = hasFixedColumn ? 32 : 2;
428
+ if (fixedTopRows > 0 && rowIndex < fixedTopRows) {
429
+ rowClasses.push(`${name}__row--fixed-top`);
430
+ // top/bottom 值将在 nextTick 中通过实际 DOM 测量来设置
431
+ rowStyle = `position: sticky; top: 0px; z-index: ${fixedRowZIndex};`;
432
+ }
433
+ if (fixedBottomRows > 0 && rowIndex >= dataLen - fixedBottomRows) {
434
+ rowClasses.push(`${name}__row--fixed-bottom`);
435
+ if (rowIndex === dataLen - fixedBottomRows) {
436
+ rowClasses.push(`${name}__row--fixed-bottom-first`);
437
+ }
438
+ rowStyle = `position: sticky; bottom: 0px; z-index: ${fixedRowZIndex};`;
439
+ }
440
+ // 冻结表尾行时,最后一行非冻结行去除下边框
441
+ if (fixedBottomRows > 0 && rowIndex === dataLen - fixedBottomRows - 1) {
442
+ rowClasses.push(`${name}__row--without-border-bottom`);
443
+ }
444
+
445
+ return {
446
+ rowIndex,
447
+ rowId: getVal(row, rowKey || 'id'),
448
+ cells,
449
+ row,
450
+ rowClass: rowClasses.join(' '),
451
+ rowStyle,
452
+ };
453
+ });
454
+
455
+ this.renderData = renderData;
456
+ this.isEmpty = !data || data.length === 0;
457
+
458
+ // 动态测量行高,修正固定行的 top/bottom 值
459
+ if (fixedTopRows > 0 || fixedBottomRows > 0) {
460
+ this.$nextTick(() => {
461
+ this.measureAndFixStickyPositions(hasFixedHeader, fixedTopRows, fixedBottomRows, dataLen);
462
+ });
463
+ }
464
+ },
465
+
466
+ measureAndFixStickyPositions(hasFixedHeader, fixedTopRows, fixedBottomRows, dataLen) {
467
+ const query = uni.createSelectorQuery().in(this);
468
+ // 查询表头高度
469
+ const headerSelector = `.${name}__header`;
470
+ // 查询所有数据行
471
+ const rowSelector = `.${name}__tr`;
472
+
473
+ query.select(headerSelector).boundingClientRect();
474
+ query.selectAll(rowSelector).boundingClientRect();
475
+ query.exec((res) => {
476
+ if (!res) return;
477
+ const headerRect = res[0];
478
+ const rowRects = res[1];
479
+ if (!rowRects || rowRects.length === 0) return;
480
+
481
+ const headerHeight = hasFixedHeader && headerRect ? headerRect.height : 0;
482
+
483
+ const fixedRowZIndex = this.hasFixedColumn ? 32 : 2;
484
+
485
+ // 计算固定顶部行的 top 值
486
+ let topOffset = headerHeight;
487
+ for (let i = 0; i < fixedTopRows && i < rowRects.length; i += 1) {
488
+ const rd = this.renderData[i];
489
+ if (rd) {
490
+ rd.rowStyle = `position: sticky; top: ${topOffset}px; z-index: ${fixedRowZIndex};`;
491
+ topOffset += rowRects[i].height;
492
+ }
493
+ }
494
+
495
+ // 计算固定底部行的 bottom 值
496
+ let bottomOffset = 0;
497
+ for (let i = dataLen - 1; i >= dataLen - fixedBottomRows && i >= 0; i -= 1) {
498
+ const rd = this.renderData[i];
499
+ if (rd && i < rowRects.length) {
500
+ rd.rowStyle = `position: sticky; bottom: ${bottomOffset}px; z-index: ${fixedRowZIndex};`;
501
+ bottomOffset += rowRects[i].height;
502
+ }
503
+ }
504
+
505
+ // 触发视图更新
506
+ this.renderData = [...this.renderData];
507
+ });
508
+ },
509
+
510
+ onRowClick(rowIndex) {
511
+ const { data } = this;
512
+ if (data && data[rowIndex]) {
513
+ this.$emit('row-click', {
514
+ row: data[rowIndex],
515
+ index: rowIndex,
516
+ });
517
+ }
518
+ },
519
+
520
+ onCellClick(rowIndex, colIndex) {
521
+ const { data, columns } = this;
522
+ if (data && data[rowIndex] && columns && columns[colIndex]) {
523
+ this.$emit('cell-click', {
524
+ row: data[rowIndex],
525
+ col: columns[colIndex],
526
+ rowIndex,
527
+ colIndex,
528
+ });
529
+ }
530
+ },
531
+
532
+ onScroll(e) {
533
+ this.$emit('scroll', { e });
534
+
535
+ // 更新固定列滚动阴影状态
536
+ if (this.hasFixedColumn) {
537
+ const detail = e.detail || e.target || {};
538
+ const scrollLeft = detail.scrollLeft || 0;
539
+ const scrollWidth = detail.scrollWidth || 0;
540
+ const clientWidth = detail.clientWidth || detail.offsetWidth || 0;
541
+ if (scrollWidth > 0 && clientWidth > 0) {
542
+ this.scrollableToLeft = scrollLeft > 1;
543
+ this.scrollableToRight = scrollWidth - scrollLeft - clientWidth > 1;
544
+ }
545
+ }
546
+ },
547
+ },
548
+ }),
549
+ };
550
+ </script>
551
+ <style scoped src="./table.css"></style>