@idooel/components 0.0.3-beta.2 → 0.0.3-beta.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/package.json
CHANGED
|
@@ -140,6 +140,7 @@ export default {
|
|
|
140
140
|
tableQuerys: {},
|
|
141
141
|
resizeObserverModelTableWrapper: null,
|
|
142
142
|
resizeObserverModelTableContainer: null,
|
|
143
|
+
resizeObserverSearchArea: null,
|
|
143
144
|
modelTableWrapperHeight: 0,
|
|
144
145
|
currentTreeNodeData: {},
|
|
145
146
|
currentRowData: {},
|
|
@@ -1149,19 +1150,9 @@ export default {
|
|
|
1149
1150
|
|
|
1150
1151
|
const { top: tableToTop, width } = tableRef.$el.getBoundingClientRect()
|
|
1151
1152
|
this.tableWidth = width
|
|
1152
|
-
|
|
1153
|
-
//
|
|
1154
|
-
|
|
1155
|
-
const paginationEl = tableRef.$el.querySelector('.g-table__pagination')
|
|
1156
|
-
if (paginationEl) {
|
|
1157
|
-
paginationHeight = paginationEl.getBoundingClientRect().height || 50
|
|
1158
|
-
} else {
|
|
1159
|
-
// 如果分页组件还未渲染,使用默认高度
|
|
1160
|
-
paginationHeight = 50
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
// 计算表格高度:视口高度 - 表格顶部距离 - 分页高度 - 额外高度
|
|
1164
|
-
const calculatedHeight = currentViewportHeight - tableToTop - paginationHeight - this.overHeight - 20
|
|
1153
|
+
|
|
1154
|
+
// 计算表格高度:外层容器高度应覆盖到视口底部;分页高度由 ele-table 内部自行扣减
|
|
1155
|
+
const calculatedHeight = currentViewportHeight - tableToTop - this.overHeight
|
|
1165
1156
|
// 确保最小高度,避免表格过小
|
|
1166
1157
|
this.tableHeight = Math.max(calculatedHeight, 200)
|
|
1167
1158
|
},
|
|
@@ -1181,6 +1172,23 @@ export default {
|
|
|
1181
1172
|
this.treeWrapperHeight = height - titleHeight
|
|
1182
1173
|
}
|
|
1183
1174
|
},
|
|
1175
|
+
observeSearchAreaHeight () {
|
|
1176
|
+
const searchAreaRef = this.$refs[this.searchArea]
|
|
1177
|
+
const searchAreaEl = searchAreaRef && searchAreaRef.$el
|
|
1178
|
+
if (!searchAreaEl || typeof ResizeObserver === 'undefined') return
|
|
1179
|
+
|
|
1180
|
+
this.resizeObserverSearchArea = new ResizeObserver(entries => {
|
|
1181
|
+
for (const entry of entries) {
|
|
1182
|
+
requestAnimationFrame(() => {
|
|
1183
|
+
this.calculateTableHeight()
|
|
1184
|
+
if (this.showTree) {
|
|
1185
|
+
this.calculateTreeHeight()
|
|
1186
|
+
}
|
|
1187
|
+
})
|
|
1188
|
+
}
|
|
1189
|
+
})
|
|
1190
|
+
this.resizeObserverSearchArea.observe(searchAreaEl)
|
|
1191
|
+
},
|
|
1184
1192
|
async keepAliveRefresh () {
|
|
1185
1193
|
return this.withPerformanceMonitoring('keepAliveRefresh', async () => {
|
|
1186
1194
|
// 重新计算表格高度(应对窗口大小变化)
|
|
@@ -1223,6 +1231,7 @@ export default {
|
|
|
1223
1231
|
setTimeout(() => {
|
|
1224
1232
|
this.calculateTableHeight()
|
|
1225
1233
|
this.calculateTreeHeight()
|
|
1234
|
+
this.observeSearchAreaHeight()
|
|
1226
1235
|
}, 200)
|
|
1227
1236
|
})
|
|
1228
1237
|
|
|
@@ -1277,6 +1286,9 @@ export default {
|
|
|
1277
1286
|
if (this.resizeObserverModelTableContainer) {
|
|
1278
1287
|
this.resizeObserverModelTableContainer.disconnect()
|
|
1279
1288
|
}
|
|
1289
|
+
if (this.resizeObserverSearchArea) {
|
|
1290
|
+
this.resizeObserverSearchArea.disconnect()
|
|
1291
|
+
}
|
|
1280
1292
|
if (this.handleResize) {
|
|
1281
1293
|
window.removeEventListener('resize', this.handleResize)
|
|
1282
1294
|
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="g-table__wrapper" ref="tableWrapper" :style="wrapperStyle">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
2
|
+
<div class="g-table__wrapper" :class="{ 'g-table__wrapper--need-h-scroll': needHorizontalScroll }" ref="tableWrapper" :style="wrapperStyle">
|
|
3
|
+
<div class="g-table__main">
|
|
4
|
+
<a-table
|
|
5
|
+
:key="tableRenderKey"
|
|
6
|
+
:bordered="bordered"
|
|
7
|
+
:class="[isNoData && 'g-table__no-data']"
|
|
8
|
+
:pagination="false"
|
|
9
|
+
:loading="loading"
|
|
10
|
+
size="middle"
|
|
11
|
+
:columns="smartColumns"
|
|
12
|
+
:row-selection="smartRowSelection"
|
|
13
|
+
:row-class-name="setRowClassName"
|
|
14
|
+
:data-source="dataSource"
|
|
15
|
+
:scroll="smartScroll">
|
|
16
|
+
<template slot="action" slot-scope="record">
|
|
17
|
+
<Actions v-on="$listeners" :data-source="actions" :record="record"></Actions>
|
|
18
|
+
</template>
|
|
19
|
+
</a-table>
|
|
20
|
+
</div>
|
|
19
21
|
<div class="g-table__pagination">
|
|
20
22
|
<a-pagination
|
|
21
23
|
:show-total="all => `共 ${all} 条数据`"
|
|
@@ -152,8 +154,12 @@ export default {
|
|
|
152
154
|
return !this.dataSource.length
|
|
153
155
|
},
|
|
154
156
|
getScrollHeightByHeight () {
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
+
// 始终基于稳定的表头/分页高度计算可用区域,避免 fallback 造成内容压到分页区
|
|
158
|
+
const headerHeight = this.tableHeaderHeight || 64
|
|
159
|
+
const paginationHeight = this.paginationHeight || 64
|
|
160
|
+
const rawHeight = this.height - headerHeight - paginationHeight
|
|
161
|
+
// 规避小数像素导致的 1px 裁切,给表体预留 1px 安全边距
|
|
162
|
+
return Math.max(Math.floor(rawHeight) - 1, 0)
|
|
157
163
|
},
|
|
158
164
|
isFlexColumn () {
|
|
159
165
|
return this.columns.every(col => !col.width)
|
|
@@ -175,12 +181,43 @@ export default {
|
|
|
175
181
|
}
|
|
176
182
|
return total
|
|
177
183
|
},
|
|
184
|
+
estimatedMinContentWidth () {
|
|
185
|
+
const cols = this.innerColumns || []
|
|
186
|
+
let specifiedWidthSum = 0
|
|
187
|
+
let unspecifiedCount = 0
|
|
188
|
+
cols.forEach(col => {
|
|
189
|
+
const w = col && col.width
|
|
190
|
+
if (typeof w === 'number') {
|
|
191
|
+
specifiedWidthSum += w
|
|
192
|
+
} else {
|
|
193
|
+
unspecifiedCount += 1
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
const selectionWidth = this.rowSelection ? 60 : 0
|
|
197
|
+
const operationWidth = (this.operations && typeof this.operations.width === 'number') ? this.operations.width : 0
|
|
198
|
+
// 未配置宽度列给更保守的最小宽度,贴近运行时真实占宽,
|
|
199
|
+
// 避免“容器已明显缩小但横向滚动仍不触发”。
|
|
200
|
+
const unspecifiedMinWidth = unspecifiedCount * 320
|
|
201
|
+
return specifiedWidthSum + unspecifiedMinWidth + selectionWidth + operationWidth
|
|
202
|
+
},
|
|
203
|
+
hasIncompleteColumnWidth () {
|
|
204
|
+
const cols = this.innerColumns || []
|
|
205
|
+
if (!cols.length) return false
|
|
206
|
+
return cols.some(col => typeof (col && col.width) !== 'number')
|
|
207
|
+
},
|
|
178
208
|
/**
|
|
179
209
|
* 是否需要横向滚动:容器宽度 < 列总宽度
|
|
180
210
|
*/
|
|
181
211
|
needHorizontalScroll () {
|
|
182
212
|
// 未获取容器宽度前,先假定需要滚动(保守策略)
|
|
183
213
|
if (!this.containerWidth) return true
|
|
214
|
+
// 存在未配置 width 的列时,totalColumnsWidth 会低估真实内容宽度;
|
|
215
|
+
// 此时改用保守阈值判定,避免误判导致列被压缩变形。
|
|
216
|
+
if (this.hasIncompleteColumnWidth) {
|
|
217
|
+
const baseFallbackX = typeof this.x === 'number' ? this.x : 1200
|
|
218
|
+
const fallbackX = Math.max(baseFallbackX, this.estimatedMinContentWidth)
|
|
219
|
+
return this.containerWidth < fallbackX - 5
|
|
220
|
+
}
|
|
184
221
|
// 加一点容差
|
|
185
222
|
return this.containerWidth < this.totalColumnsWidth - 5
|
|
186
223
|
},
|
|
@@ -210,19 +247,29 @@ export default {
|
|
|
210
247
|
*/
|
|
211
248
|
smartScroll () {
|
|
212
249
|
if (!this.needHorizontalScroll) {
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
250
|
+
// 不需要横向滚动时,只要存在高度约束就下发 y。
|
|
251
|
+
// 旧逻辑依赖估算行高 needScrollY,遇到多行文本/变高行时会误判,
|
|
252
|
+
// 导致 body 自然撑高(overflow: visible),内容超出可视区却没有纵向滚动。
|
|
253
|
+
if (this.height) {
|
|
254
|
+
const availableHeight = this.getScrollHeightByHeight
|
|
218
255
|
if (availableHeight > 50) {
|
|
219
256
|
return { y: availableHeight }
|
|
220
257
|
}
|
|
221
258
|
}
|
|
222
259
|
return {}
|
|
223
260
|
}
|
|
224
|
-
//
|
|
225
|
-
|
|
261
|
+
// 需要横向滚动时,若未设置 y,会导致横向滚动条落在超高表体底部,
|
|
262
|
+
// 外层容器 overflow:hidden 场景下用户看不到滚动条。
|
|
263
|
+
// 这里在有高度约束时强制注入 y,保证横向滚动条始终在可视区域内。
|
|
264
|
+
const baseScroll = this.getScroll || {}
|
|
265
|
+
if (this.height && (baseScroll.y === undefined || baseScroll.y === null)) {
|
|
266
|
+
const availableHeight = this.getScrollHeightByHeight
|
|
267
|
+
if (availableHeight > 50) {
|
|
268
|
+
return { ...baseScroll, y: availableHeight }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// 需要横向滚动,其他情况使用原有逻辑
|
|
272
|
+
return baseScroll
|
|
226
273
|
},
|
|
227
274
|
/**
|
|
228
275
|
* 智能 rowSelection 配置:
|
|
@@ -260,17 +307,20 @@ export default {
|
|
|
260
307
|
// rowSelection 的 checkbox/radio 列是 antd 自动加的,给一个经验宽度避免误差
|
|
261
308
|
const selectionWidth = this.rowSelection ? 60 : 0
|
|
262
309
|
|
|
263
|
-
//
|
|
264
|
-
|
|
310
|
+
// 只有所有列都明确给了 width 时才 clamp,避免低估导致表格被挤压
|
|
311
|
+
const allColsHaveWidth = cols.length > 0 && cols.every(col => typeof (col && col.width) === 'number')
|
|
312
|
+
if (allColsHaveWidth && total > 0) {
|
|
265
313
|
const minX = total + selectionWidth
|
|
266
314
|
if (baseX > minX) baseX = minX
|
|
267
315
|
}
|
|
316
|
+
// 存在未配置宽度列时,x 至少取估算最小内容宽度,
|
|
317
|
+
// 避免出现 needHorizontalScroll=true 但 scrollWidth==clientWidth 的“无滚动条”状态。
|
|
318
|
+
if (this.hasIncompleteColumnWidth) {
|
|
319
|
+
baseX = Math.max(baseX, this.estimatedMinContentWidth)
|
|
320
|
+
}
|
|
268
321
|
}
|
|
269
|
-
|
|
270
322
|
if (this.height && this.needScrollY) {
|
|
271
|
-
const availableHeight = this.
|
|
272
|
-
? this.getScrollHeightByHeight
|
|
273
|
-
: this.height - 100
|
|
323
|
+
const availableHeight = this.getScrollHeightByHeight
|
|
274
324
|
|
|
275
325
|
if (availableHeight > 50) {
|
|
276
326
|
return { x: baseX, y: availableHeight }
|
|
@@ -319,7 +369,7 @@ export default {
|
|
|
319
369
|
},
|
|
320
370
|
setTableHeaderHeight () {
|
|
321
371
|
this.$nextTick(() => {
|
|
322
|
-
const el = this.$el.querySelector('.ant-table-header')
|
|
372
|
+
const el = this.$el.querySelector('.ant-table-header') || this.$el.querySelector('.ant-table-thead')
|
|
323
373
|
if (!el) return
|
|
324
374
|
const { height } = el.getBoundingClientRect()
|
|
325
375
|
this.tableHeaderHeight = height
|
|
@@ -442,10 +492,13 @@ export default {
|
|
|
442
492
|
this._containerResizeObserver = new ResizeObserver((entries) => {
|
|
443
493
|
for (const entry of entries) {
|
|
444
494
|
const newWidth = entry.contentRect.width
|
|
495
|
+
const delta = Math.abs(newWidth - this.containerWidth)
|
|
496
|
+
const oldNeedScroll = this.needHorizontalScroll
|
|
497
|
+
// 始终更新容器宽度,避免临界区被阈值过滤后状态滞留。
|
|
498
|
+
this.containerWidth = newWidth
|
|
499
|
+
const newNeedScroll = this.needHorizontalScroll
|
|
445
500
|
// 只有宽度变化超过阈值才触发更新,避免微小变化导致频繁重渲染
|
|
446
|
-
if (
|
|
447
|
-
const oldNeedScroll = this.needHorizontalScroll
|
|
448
|
-
this.containerWidth = newWidth
|
|
501
|
+
if (delta > 10 || newNeedScroll !== oldNeedScroll) {
|
|
449
502
|
// 容器宽度变化会触发 needHorizontalScroll 的重新计算
|
|
450
503
|
// needHorizontalScroll 的 watcher 会处理表格重新渲染和列宽同步
|
|
451
504
|
// 这里不需要直接调用 syncFixedColumns,避免与 watcher 的执行时机冲突
|
|
@@ -459,18 +512,16 @@ export default {
|
|
|
459
512
|
})
|
|
460
513
|
|
|
461
514
|
// 如果 needHorizontalScroll 状态没有变化,说明只是列宽需要调整,直接同步
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
clearTimeout(this._resizeDebounceTimer)
|
|
467
|
-
}
|
|
468
|
-
this._resizeDebounceTimer = setTimeout(() => {
|
|
469
|
-
this.syncFixedColumns()
|
|
470
|
-
this.syncHeaderTableWidth()
|
|
471
|
-
this.bindScrollSync()
|
|
472
|
-
}, 150)
|
|
515
|
+
// 状态变化时 watcher 会重渲染,但这里仍补一次同步,避免出现
|
|
516
|
+
// “缩小后没滚动,刷新后才出现”的时序窗口。
|
|
517
|
+
if (this._resizeDebounceTimer) {
|
|
518
|
+
clearTimeout(this._resizeDebounceTimer)
|
|
473
519
|
}
|
|
520
|
+
this._resizeDebounceTimer = setTimeout(() => {
|
|
521
|
+
this.syncFixedColumns()
|
|
522
|
+
this.syncHeaderTableWidth()
|
|
523
|
+
this.bindScrollSync()
|
|
524
|
+
}, 150)
|
|
474
525
|
})
|
|
475
526
|
}
|
|
476
527
|
}
|
|
@@ -532,15 +583,27 @@ export default {
|
|
|
532
583
|
|
|
533
584
|
<style lang="scss" scoped>
|
|
534
585
|
.g-table__wrapper {
|
|
586
|
+
display: flex;
|
|
587
|
+
flex-direction: column;
|
|
588
|
+
min-height: 0;
|
|
589
|
+
|
|
590
|
+
.g-table__main {
|
|
591
|
+
flex: 1;
|
|
592
|
+
min-height: 0;
|
|
593
|
+
overflow: hidden;
|
|
594
|
+
}
|
|
595
|
+
|
|
535
596
|
/**
|
|
536
597
|
* 修复"宽屏下表格两侧出现空白"问题:
|
|
537
598
|
* 当视口宽度大于表格内容宽度时,主表(ant-table-scroll)不会自动拉伸,
|
|
538
599
|
* 而固定列(fixed-left/fixed-right)是 position:absolute 定位在容器边缘,中间就出现空白。
|
|
539
600
|
* 解决方案:让主表的 table 元素 min-width:100%,使其始终填满滚动容器。
|
|
540
601
|
*/
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
602
|
+
&:not(.g-table__wrapper--need-h-scroll) {
|
|
603
|
+
::v-deep .ant-table-scroll .ant-table-header table,
|
|
604
|
+
::v-deep .ant-table-scroll .ant-table-body table {
|
|
605
|
+
min-width: 100%;
|
|
606
|
+
}
|
|
544
607
|
}
|
|
545
608
|
|
|
546
609
|
/**
|
|
@@ -585,19 +648,22 @@ export default {
|
|
|
585
648
|
/* 空数据状态顶部显示 */
|
|
586
649
|
.g-table__no-data {
|
|
587
650
|
position: relative;
|
|
651
|
+
::v-deep .ant-table-placeholder > td {
|
|
652
|
+
height: auto !important;
|
|
653
|
+
line-height: normal !important;
|
|
654
|
+
padding-top: 24px !important;
|
|
655
|
+
padding-bottom: 24px !important;
|
|
656
|
+
}
|
|
588
657
|
::v-deep .ant-table-placeholder {
|
|
589
|
-
position:
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
transform: translate(-50%, -40%);
|
|
593
|
-
width: 100%;
|
|
594
|
-
height: 100%;
|
|
658
|
+
position: static;
|
|
659
|
+
width: auto;
|
|
660
|
+
height: auto;
|
|
595
661
|
text-align: center;
|
|
596
662
|
color: #999;
|
|
597
663
|
font-size: 14px;
|
|
598
664
|
font-weight: normal;
|
|
599
665
|
line-height: 20px;
|
|
600
|
-
overflow:
|
|
666
|
+
overflow: visible;
|
|
601
667
|
border: unset;
|
|
602
668
|
}
|
|
603
669
|
}
|