@tdesign/uniapp 0.8.0 → 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 (134) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +43 -34
  3. package/dist/action-sheet/README.md +8 -3
  4. package/dist/avatar/README.md +3 -1
  5. package/dist/back-top/README.md +3 -1
  6. package/dist/badge/README.en-US.md +1 -0
  7. package/dist/badge/README.md +4 -1
  8. package/dist/badge/badge.css +1 -1
  9. package/dist/button/README.md +3 -1
  10. package/dist/button/button.vue +5 -0
  11. package/dist/calendar/README.md +3 -1
  12. package/dist/calendar/calendar.vue +6 -2
  13. package/dist/cascader/README.md +3 -1
  14. package/dist/cell/README.md +3 -1
  15. package/dist/checkbox/README.md +3 -1
  16. package/dist/col/README.md +3 -1
  17. package/dist/collapse/README.md +3 -1
  18. package/dist/color-picker/README.md +3 -1
  19. package/dist/common/common.ts +121 -5
  20. package/dist/common/src/index.js +0 -1
  21. package/dist/common/style/theme/index-light.css +282 -0
  22. package/dist/common/style/theme/index-light.less +9 -0
  23. package/dist/common/style/theme/raw/_components-light.less +8 -0
  24. package/dist/common/style/theme/raw/_light-only.less +181 -0
  25. package/dist/common/validator.ts +496 -0
  26. package/dist/config-provider/README.md +3 -1
  27. package/dist/count-down/README.md +3 -1
  28. package/dist/date-time-picker/README.md +3 -1
  29. package/dist/dialog/README.md +3 -1
  30. package/dist/divider/README.md +3 -1
  31. package/dist/drawer/README.md +3 -1
  32. package/dist/dropdown-item/dropdown-item.vue +2 -0
  33. package/dist/dropdown-menu/README.md +2 -2
  34. package/dist/dropdown-menu/dropdown-menu.vue +1 -1
  35. package/dist/empty/README.md +3 -1
  36. package/dist/fab/README.md +3 -1
  37. package/dist/fab/fab.vue +2 -2
  38. package/dist/fab/props.ts +1 -1
  39. package/dist/fab/type.ts +1 -1
  40. package/dist/footer/README.md +3 -1
  41. package/dist/form/README.en-US.md +2 -2
  42. package/dist/form/README.md +5 -3
  43. package/dist/form/form.vue +1 -1
  44. package/dist/form/type.ts +3 -3
  45. package/dist/form-item/form-item.css +6 -2
  46. package/dist/form-item/form-item.vue +30 -24
  47. package/dist/form-item/type.ts +2 -2
  48. package/dist/grid/README.md +3 -1
  49. package/dist/guide/README.md +3 -1
  50. package/dist/icon/README.md +10 -7
  51. package/dist/icon/icon.css +1633 -1624
  52. package/dist/image/README.md +3 -1
  53. package/dist/image-viewer/README.md +3 -1
  54. package/dist/index.js +13 -0
  55. package/dist/indexes/README.md +3 -1
  56. package/dist/indexes/computed.js +6 -2
  57. package/dist/indexes/indexes.css +7 -2
  58. package/dist/indexes/indexes.vue +1 -1
  59. package/dist/indexes/props.ts +5 -0
  60. package/dist/indexes/type.ts +5 -0
  61. package/dist/input/README.md +3 -1
  62. package/dist/input/input.vue +8 -32
  63. package/dist/link/README.md +3 -1
  64. package/dist/loading/README.md +3 -1
  65. package/dist/message/README.md +8 -3
  66. package/dist/mixins/page-scroll.d.ts +19 -0
  67. package/dist/mixins/skyline.js +1 -1
  68. package/dist/navbar/README.md +3 -1
  69. package/dist/notice-bar/README.md +3 -1
  70. package/dist/overlay/README.md +3 -1
  71. package/dist/picker/README.md +3 -1
  72. package/dist/popover/README.md +3 -1
  73. package/dist/popup/README.md +3 -1
  74. package/dist/progress/README.md +2 -2
  75. package/dist/pull-down-refresh/README.md +3 -1
  76. package/dist/qrcode/README.md +3 -1
  77. package/dist/radio/README.md +3 -1
  78. package/dist/rate/README.md +3 -1
  79. package/dist/rate/rate.css +1 -0
  80. package/dist/result/README.md +3 -1
  81. package/dist/search/README.md +3 -1
  82. package/dist/search/search.css +5 -0
  83. package/dist/search/search.vue +7 -12
  84. package/dist/segmented/README.en-US.md +42 -0
  85. package/dist/segmented/README.md +75 -0
  86. package/dist/segmented/props.ts +31 -0
  87. package/dist/segmented/segmented.css +66 -0
  88. package/dist/segmented/segmented.vue +174 -0
  89. package/dist/segmented/type.ts +41 -0
  90. package/dist/side-bar/README.md +3 -1
  91. package/dist/skeleton/README.md +3 -1
  92. package/dist/slider/README.md +3 -1
  93. package/dist/stepper/README.md +3 -1
  94. package/dist/steps/README.md +3 -1
  95. package/dist/sticky/README.md +3 -1
  96. package/dist/swipe-cell/README.md +3 -1
  97. package/dist/swiper/README.md +3 -1
  98. package/dist/switch/README.md +3 -1
  99. package/dist/tab-bar/README.md +3 -1
  100. package/dist/tab-bar-item/tab-bar-item.vue +4 -6
  101. package/dist/table/README.en-US.md +72 -0
  102. package/dist/table/README.md +117 -0
  103. package/dist/table/base-table-props.ts +105 -0
  104. package/dist/table/props.ts +94 -0
  105. package/dist/table/table.css +251 -0
  106. package/dist/table/table.vue +551 -0
  107. package/dist/table/type.ts +180 -0
  108. package/dist/tabs/README.md +3 -1
  109. package/dist/tabs/tabs.css +4 -0
  110. package/dist/tag/README.md +3 -1
  111. package/dist/textarea/README.md +3 -1
  112. package/dist/textarea/textarea.vue +1 -25
  113. package/dist/theme-light.css +282 -0
  114. package/dist/theme-light.css.d.ts +2 -0
  115. package/dist/theme-light.less +1 -0
  116. package/dist/theme-light.less.d.ts +2 -0
  117. package/dist/theme.css +467 -0
  118. package/dist/theme.css.d.ts +2 -0
  119. package/dist/theme.less +1 -0
  120. package/dist/theme.less.d.ts +2 -0
  121. package/dist/toast/README.md +3 -1
  122. package/dist/transition/README.md +4 -6
  123. package/dist/tree-select/README.md +3 -1
  124. package/dist/types/index.d.ts +17 -0
  125. package/dist/types/segmented.d.ts +7 -0
  126. package/dist/types/table.d.ts +7 -0
  127. package/dist/upload/README.md +3 -1
  128. package/dist/watermark/README.md +3 -1
  129. package/global.d.ts +2 -0
  130. package/package.json +66 -8
  131. package/script/postinstall.js +87 -0
  132. package/dist/common/src/superComponent.js +0 -5
  133. package/dist/common/validator.js +0 -210
  134. package/dist/script/postinstall.js +0 -46
@@ -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>