@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idooel/components",
3
- "version": "0.0.3-beta.2",
3
+ "version": "0.0.3-beta.3",
4
4
  "description": "",
5
5
  "private": false,
6
6
  "main": "dist/@idooel/components.umd.js",
@@ -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
- let paginationHeight = 0
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
- <a-table
4
- :key="tableRenderKey"
5
- :bordered="bordered"
6
- :class="[isNoData && 'g-table__no-data']"
7
- :pagination="false"
8
- :loading="loading"
9
- size="middle"
10
- :columns="smartColumns"
11
- :row-selection="smartRowSelection"
12
- :row-class-name="setRowClassName"
13
- :data-source="dataSource"
14
- :scroll="smartScroll">
15
- <template slot="action" slot-scope="record">
16
- <Actions v-on="$listeners" :data-source="actions" :record="record"></Actions>
17
- </template>
18
- </a-table>
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
- return this.height - this.tableHeaderHeight - this.paginationHeight
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
- // 不需要横向滚动,只保留 y 方向(如果需要)
214
- if (this.height && this.needScrollY) {
215
- const availableHeight = this.tableHeaderHeight && this.paginationHeight
216
- ? this.getScrollHeightByHeight
217
- : this.height - 100
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
- return this.getScroll
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
- // 只有当所有列都明确给了宽度(total > 0)时才 clamp
264
- if (total > 0) {
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.tableHeaderHeight && this.paginationHeight
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 (Math.abs(newWidth - this.containerWidth) > 10) {
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
- // 如果状态变化了,watcher 会处理重新渲染
463
- if (this.needHorizontalScroll === oldNeedScroll) {
464
- // 防抖:延迟同步固定列,避免频繁调用
465
- if (this._resizeDebounceTimer) {
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
- ::v-deep .ant-table-scroll .ant-table-header table,
542
- ::v-deep .ant-table-scroll .ant-table-body table {
543
- min-width: 100%;
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: absolute;
590
- top: 50%;
591
- left: 50%;
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: hidden;
666
+ overflow: visible;
601
667
  border: unset;
602
668
  }
603
669
  }