@zat-design/sisyphus-react 4.5.0-beta.2 → 4.5.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.
@@ -22,7 +22,7 @@ const Validator = ({
22
22
  const {
23
23
  hasError,
24
24
  firstErrorIndex,
25
- firstErrorRowKey
25
+ firstErrorColumnKey
26
26
  } = await runUnifiedValidation({
27
27
  form: config.form,
28
28
  name: config.name,
@@ -38,9 +38,9 @@ const Validator = ({
38
38
  config?.tableRef?.current?.scrollTo?.({
39
39
  index: firstErrorIndex
40
40
  });
41
- // 横向:等错误行渲染后,下一帧把错误列滚入视口(虚拟表格横向需 scrollTo({ left }))
41
+ // 横向:把报错列滚入视口(虚拟表格列被虚拟化,按表头列宽算 scrollLeft,与行渲染无关)
42
42
  requestAnimationFrame(() => {
43
- scrollVirtualToErrorColumn(config?.tableRef, firstErrorRowKey);
43
+ scrollVirtualToErrorColumn(config?.tableRef, columns || [], firstErrorColumnKey);
44
44
  });
45
45
  });
46
46
  }
@@ -90,16 +90,22 @@ export declare const getDisabled: ({ globalControl, formDisabled, column, tabled
90
90
  */
91
91
  export declare const handleScrollToError: () => void;
92
92
  /**
93
- * 虚拟表格:横向滚动定位到「首个错误行」内的报错单元格。
93
+ * 把「首个报错列的 cellKey」映射成它在表头 th 中的可见列索引。
94
+ * 列序 = [选择列?(hasSelection 时占位 1), ...用户列];names 列的 cellKey 用 splitNames 结果。
95
+ * @returns 表头 th 下标;未命中返回 -1
96
+ */
97
+ export declare const resolveErrorColumnIndex: (columns: any[], cellKey: string, hasSelection: boolean) => number;
98
+ /**
99
+ * 虚拟表格:横向滚动定位到「首个报错列」。
94
100
  *
95
- * 背景:虚拟表格的横向滚动不是原生 DOM scroll(holder 的 overflow-x hidden),
96
- * rc-virtual-list 用 transform 模拟,只能通过 antd Table ref 的 scrollTo({ left }) 驱动;
97
- * 而 scrollTo({ index }) 仅做纵向行定位。保存校验失败后纵向已滚到错误行,但错误列可能仍在
98
- * 横向视口外(如错误列靠左、表格当前滚到最右)→ 看不到红字。此函数补齐横向定位。
101
+ * 背景:rc-table 虚拟表格对「列」也做了虚拟化(BodyGrid 仅渲染横向视口内的列),
102
+ * 保存校验失败时报错列可能根本未渲染进 body DOM,无法用单元格测量定位(旧实现据此失效)。
103
+ * 而表头(thead)不被列虚拟化——渲染全部列且为真实测量宽度,故改以表头为列宽来源:
104
+ * 累加报错列之前各列宽、减去 fixed-left 列宽(固定列悬浮,不占滚动偏移)得目标 scrollLeft。
99
105
  *
100
- * 目标 scrollLeft = 错误单元格前置兄弟列宽累计 - fixed-left 列宽(fixed 列悬浮,不占滚动偏移)。
106
+ * 注意:ref.nativeElement 仅为 body 滚动容器,表头在其外层 table-container 内,需向上查找。
101
107
  */
102
- export declare const scrollVirtualToErrorColumn: (tableRef: React.MutableRefObject<any>, rowKey?: string | null) => void;
108
+ export declare const scrollVirtualToErrorColumn: (tableRef: React.MutableRefObject<any>, columns: any[], cellKey?: string | null) => void;
103
109
  /**
104
110
  * 深copy一个对象,并过滤掉其中的React节点
105
111
  * @param value 需要深拷贝的对象
@@ -333,41 +333,62 @@ export const handleScrollToError = () => {
333
333
  };
334
334
 
335
335
  /**
336
- * 虚拟表格:横向滚动定位到「首个错误行」内的报错单元格。
336
+ * 把「首个报错列的 cellKey」映射成它在表头 th 中的可见列索引。
337
+ * 列序 = [选择列?(hasSelection 时占位 1), ...用户列];names 列的 cellKey 用 splitNames 结果。
338
+ * @returns 表头 th 下标;未命中返回 -1
339
+ */
340
+ export const resolveErrorColumnIndex = (columns = [], cellKey, hasSelection) => {
341
+ const idx = (columns || []).findIndex(col => {
342
+ if (Array.isArray(col?.names)) {
343
+ return splitNames(col.names) === cellKey;
344
+ }
345
+ return String(col?.name ?? col?.dataIndex ?? col?.key) === cellKey;
346
+ });
347
+ if (idx < 0) {
348
+ return -1;
349
+ }
350
+ return hasSelection ? idx + 1 : idx;
351
+ };
352
+
353
+ /**
354
+ * 虚拟表格:横向滚动定位到「首个报错列」。
337
355
  *
338
- * 背景:虚拟表格的横向滚动不是原生 DOM scroll(holder 的 overflow-x hidden),
339
- * rc-virtual-list 用 transform 模拟,只能通过 antd Table ref 的 scrollTo({ left }) 驱动;
340
- * 而 scrollTo({ index }) 仅做纵向行定位。保存校验失败后纵向已滚到错误行,但错误列可能仍在
341
- * 横向视口外(如错误列靠左、表格当前滚到最右)→ 看不到红字。此函数补齐横向定位。
356
+ * 背景:rc-table 虚拟表格对「列」也做了虚拟化(BodyGrid 仅渲染横向视口内的列),
357
+ * 保存校验失败时报错列可能根本未渲染进 body DOM,无法用单元格测量定位(旧实现据此失效)。
358
+ * 而表头(thead)不被列虚拟化——渲染全部列且为真实测量宽度,故改以表头为列宽来源:
359
+ * 累加报错列之前各列宽、减去 fixed-left 列宽(固定列悬浮,不占滚动偏移)得目标 scrollLeft。
342
360
  *
343
- * 目标 scrollLeft = 错误单元格前置兄弟列宽累计 - fixed-left 列宽(fixed 列悬浮,不占滚动偏移)。
361
+ * 注意:ref.nativeElement 仅为 body 滚动容器,表头在其外层 table-container 内,需向上查找。
344
362
  */
345
- export const scrollVirtualToErrorColumn = (tableRef, rowKey) => {
363
+ export const scrollVirtualToErrorColumn = (tableRef, columns, cellKey) => {
346
364
  const ref = tableRef?.current;
347
365
  const root = ref?.nativeElement;
348
- if (!ref?.scrollTo || !root || rowKey == null) {
366
+ if (!ref?.scrollTo || !root || !cellKey) {
367
+ return;
368
+ }
369
+ // body 容器在外层 table-container 内,表头与其同级,需从 container 取表头
370
+ const container = root.closest('[class*="table-container"]') || root.parentElement;
371
+ const headerCells = container?.querySelectorAll('thead [class*="table-cell"]');
372
+ if (!headerCells?.length) {
349
373
  return;
350
374
  }
351
- const rowEl = root.querySelector(`[data-row-key="${rowKey}"]`);
352
- // 取错误行内首个报错单元格(DOM 顺序 = 列顺序,首个即最靠左的错误列)
353
- const errorCell = rowEl?.querySelector('[class*="form-item-has-error"]')?.closest('[class*="table-cell"]');
354
- if (!errorCell) {
375
+ const hasSelection = !!container?.querySelector('thead [class*="selection-column"]');
376
+ const targetIndex = resolveErrorColumnIndex(columns, cellKey, hasSelection);
377
+ // -1 未命中 / 0 为首列(含 fixed-left),无需横向滚动
378
+ if (targetIndex <= 0 || targetIndex >= headerCells.length) {
355
379
  return;
356
380
  }
357
381
  let offsetLeft = 0;
358
382
  let fixedLeft = 0;
359
- let previousSibling = errorCell.previousElementSibling;
360
- while (previousSibling) {
361
- if (previousSibling.nodeType === 1) {
362
- const {
363
- width = 0
364
- } = previousSibling.getBoundingClientRect() || {};
365
- offsetLeft += width;
366
- if (isFixStartCell(previousSibling)) {
367
- fixedLeft += width;
368
- }
383
+ for (let i = 0; i < targetIndex; i += 1) {
384
+ const cell = headerCells[i];
385
+ const {
386
+ width = 0
387
+ } = cell.getBoundingClientRect() || {};
388
+ offsetLeft += width;
389
+ if (isFixStartCell(cell)) {
390
+ fixedLeft += width;
369
391
  }
370
- previousSibling = previousSibling.previousElementSibling;
371
392
  }
372
393
  ref.scrollTo({
373
394
  left: Math.max(0, offsetLeft - fixedLeft)
@@ -46,6 +46,8 @@ export interface ValidateAllResult {
46
46
  firstErrorPath: (string | number)[] | null;
47
47
  /** 首个错误行的 rowKey(用于横向滚动时定位错误行 DOM),无错误为 null */
48
48
  firstErrorRowKey: string | null;
49
+ /** 首个错误列的 cellKey(names 列为 splitNames,普通列为列名),用于横向滚动定位列,无错误为 null */
50
+ firstErrorColumnKey: string | null;
49
51
  }
50
52
  /**
51
53
  * 对 form store 中 name 路径下的全量数组数据,逐行逐列用真实规则校验(脱离 DOM)。
@@ -82,5 +84,6 @@ export declare const runUnifiedValidation: ({ form, name, columns, config, error
82
84
  hasError: boolean;
83
85
  firstErrorIndex: number | null;
84
86
  firstErrorRowKey: string | null;
87
+ firstErrorColumnKey: string | null;
85
88
  }>;
86
89
  export {};
@@ -199,6 +199,7 @@ export const validateAllRows = async ({
199
199
  const errorMap = {};
200
200
  let firstErrorPath = null;
201
201
  let firstErrorRowKey = null;
202
+ let firstErrorColumnKey = null;
202
203
  for (let index = 0; index < list.length; index += 1) {
203
204
  const rowData = list[index];
204
205
  // 行可能为空位(单行编辑增删残留),跳过
@@ -244,6 +245,7 @@ export const validateAllRows = async ({
244
245
  if (!firstErrorPath) {
245
246
  firstErrorPath = built.namePath;
246
247
  firstErrorRowKey = rowKey;
248
+ firstErrorColumnKey = built.cellKey;
247
249
  }
248
250
  }
249
251
  }
@@ -252,7 +254,8 @@ export const validateAllRows = async ({
252
254
  hasError: !!firstErrorPath,
253
255
  errorMap,
254
256
  firstErrorPath,
255
- firstErrorRowKey
257
+ firstErrorRowKey,
258
+ firstErrorColumnKey
256
259
  };
257
260
  };
258
261
 
@@ -301,7 +304,8 @@ export const runUnifiedValidation = async ({
301
304
  hasError,
302
305
  errorMap,
303
306
  firstErrorPath,
304
- firstErrorRowKey
307
+ firstErrorRowKey,
308
+ firstErrorColumnKey
305
309
  } = await validateAllRows({
306
310
  form,
307
311
  name,
@@ -314,6 +318,7 @@ export const runUnifiedValidation = async ({
314
318
  return {
315
319
  hasError,
316
320
  firstErrorIndex,
317
- firstErrorRowKey
321
+ firstErrorRowKey,
322
+ firstErrorColumnKey
318
323
  };
319
324
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zat-design/sisyphus-react",
3
- "version": "4.5.0-beta.2",
3
+ "version": "4.5.0",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "es",