@gct-paas/word 0.1.38 → 0.1.39

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.
@@ -57,6 +57,10 @@ export declare class DataManager {
57
57
  * @returns 指定路径的标签值或原值,不存在返回 undefined
58
58
  */
59
59
  getLabel<T = any>(path: JsonPath): T | undefined;
60
+ /** 子表数组路径下未软删行数 */
61
+ countActiveSubtableRows(arrayPath: JsonPath): number;
62
+ /** 版面可见行下标 → rawData 数组实际下标 */
63
+ resolveSubtableVisibleIndex(arrayPath: JsonPath, visibleIndex: number): number;
60
64
  private assignField;
61
65
  /**
62
66
  * 根据 jsonpath 设置数据
@@ -170,6 +174,17 @@ export declare class DataManager {
170
174
  * 这样可以保证:参数映射(`[n]`)先铺好默认值,自定义数据源(真实下标)再精确覆盖。
171
175
  */
172
176
  applyInitData(initDataMap: Record<string, any>): void;
177
+ /**
178
+ * 仅补齐检验表关联轴子表(如 f_jianyan2)到动态关联铺砖槽位数。
179
+ * 不处理检验项主体(f_jianyan1)与二维表,避免覆盖接口行或误扩纵向轴。
180
+ */
181
+ private syncSubTableRowsToLayout;
182
+ /** 子表在 rawData 中的行数(支持尚未转成数组的 { data: [] } 接口形态,此时返回 0 且不在此阶段强转) */
183
+ private countSubTableDataRows;
184
+ /**
185
+ * 为检验表关联轴补足铺砖行:占位标记 + __gw_x_uid(与 push/insert 行为一致)。
186
+ */
187
+ private ensureCheckTableLinkLayoutRows;
173
188
  private applyInitDataCrossWildcard;
174
189
  /**
175
190
  * 解析子表初始化行数:接口已有行数、版面数据分组槽位。
@@ -0,0 +1,17 @@
1
+ /** 子表行软删 / 可见下标与存储下标映射(与后端 deleted_ 约定一致) */
2
+ type AxisType = 'x' | 'y';
3
+ export type PersistedSubtableRowContext = {
4
+ rawData?: Record<string, any>;
5
+ /** 子表 key,如 f_ewb、f_ewblink(不含 $.) */
6
+ tableKey?: string;
7
+ axis?: AxisType;
8
+ };
9
+ /** 是否应软删(打 deleted_)而非 splice */
10
+ export declare function isPersistedSubtableRow(row: Record<string, any> | undefined | null, ctx?: PersistedSubtableRowContext): boolean;
11
+ /** 未软删的行数;空数组返回 0 */
12
+ export declare function countActiveSubtableRows(rows: unknown): number;
13
+ /** 版面可见行下标 → rawData 数组中的实际下标 */
14
+ export declare function resolveSubtableStorageIndex(rows: any[], visibleIndex: number): number;
15
+ /** 在可见下标处插入时对应的 splice 位置 */
16
+ export declare function resolveSubtableInsertIndex(rows: any[], visibleIndex: number): number;
17
+ export {};
@@ -48,6 +48,8 @@ export declare class LayoutContext {
48
48
  */
49
49
  getParagraphRemainingSize(): number;
50
50
  addRun(run: LayoutNode): void;
51
+ /** 子表可见行下标 → rawData 存储下标(跳过 deleted_ 行) */
52
+ private mapSubtableVisibleIndex;
51
53
  /**
52
54
  * 根据 dataIndex 替换 path 中的 [n] 为实际值
53
55
  * @param path 包含 [n] 的路径,如 "$.items[n].name"
@@ -69,8 +69,8 @@ export declare class DocModel {
69
69
  * 根据版面数据分组 / 动态关联铺砖,计算子表字段在 rawData 中应初始化的行数。
70
70
  *
71
71
  * - 固定表:槽位归 subFieldKey(bounded + itemRegion)
72
- * - 二维表 / 检验表:动态分组铺砖在关联轴,槽位归 linkFieldKey(如 f_ewblink);
73
- * 纵向 subFieldKey(如 f_ewb)不由 itemRegion 撑行,返回 0
72
+ * - 二维表 / 检验表:动态分组铺砖槽位归 linkFieldKey(如 f_jianyan2);
73
+ * 纵向 subFieldKey(如 f_jianyan1)由检验项条数撑行,返回 0
74
74
  * - 其它子表无分组:返回 1
75
75
  */
76
76
  getSubTableLayoutSlotCount(subFieldKey: string): number;
package/dist/index.es.js CHANGED
@@ -40919,6 +40919,22 @@ class LayoutContext {
40919
40919
  this.paragraph?.addChild(run);
40920
40920
  this.layoutRun = run;
40921
40921
  }
40922
+ /** 子表可见行下标 → rawData 存储下标(跳过 deleted_ 行) */
40923
+ mapSubtableVisibleIndex(templatePath, visibleIndex, axis) {
40924
+ if (visibleIndex === void 0) return void 0;
40925
+ const parsed = parseValuePath(templatePath);
40926
+ const dm = this.doc.dataManager;
40927
+ if (axis === "n_y" && parsed.parentFieldPath) {
40928
+ return dm.resolveSubtableVisibleIndex(parsed.parentFieldPath, visibleIndex);
40929
+ }
40930
+ if (axis === "n_x" && parsed.linkFieldPath) {
40931
+ return dm.resolveSubtableVisibleIndex(parsed.linkFieldPath, visibleIndex);
40932
+ }
40933
+ if (parsed.isSubTable && parsed.parentFieldPath) {
40934
+ return dm.resolveSubtableVisibleIndex(parsed.parentFieldPath, visibleIndex);
40935
+ }
40936
+ return visibleIndex;
40937
+ }
40922
40938
  /**
40923
40939
  * 根据 dataIndex 替换 path 中的 [n] 为实际值
40924
40940
  * @param path 包含 [n] 的路径,如 "$.items[n].name"
@@ -40928,7 +40944,11 @@ class LayoutContext {
40928
40944
  if (!path2) return void 0;
40929
40945
  const { type: type4, dataIndex, xDataIndex, yDataIndex, cellZone } = this.cell?.subRenderer || {};
40930
40946
  if (isLinkSubTableType(type4) && cellZone === "cross") {
40931
- return replacePathIndexPlaceholders({ x: xDataIndex, y: yDataIndex, templatePath: path2 });
40947
+ return replacePathIndexPlaceholders({
40948
+ x: this.mapSubtableVisibleIndex(path2, xDataIndex, "n_x"),
40949
+ y: this.mapSubtableVisibleIndex(path2, yDataIndex, "n_y"),
40950
+ templatePath: path2
40951
+ });
40932
40952
  }
40933
40953
  if (!path2.includes("[n]")) {
40934
40954
  return path2;
@@ -40936,6 +40956,10 @@ class LayoutContext {
40936
40956
  let n = dataIndex;
40937
40957
  if (isLinkSubTableType(type4)) {
40938
40958
  n = cellZone === "horizontal" ? yDataIndex : xDataIndex;
40959
+ const axis = cellZone === "horizontal" ? "n_y" : "n_x";
40960
+ n = this.mapSubtableVisibleIndex(path2, n, axis);
40961
+ } else {
40962
+ n = this.mapSubtableVisibleIndex(path2, n, "n");
40939
40963
  }
40940
40964
  if (n === void 0) return path2;
40941
40965
  return replacePathIndexPlaceholder(n, path2);
@@ -42166,8 +42190,12 @@ function walkOoxmlElementTree(node, visit) {
42166
42190
  function resolveItemCountByRegionId(runtimeJson, infos) {
42167
42191
  const regions = listCheckTableRegionsInRuntimeJson(runtimeJson);
42168
42192
  if (regions.length === 0) return {};
42169
- const count = infos.data?.length ?? 0;
42170
- return { [regions[0].regionId]: count };
42193
+ const apiCount = infos.data?.length ?? 0;
42194
+ const itemCountByRegionId = {};
42195
+ for (const region of regions) {
42196
+ itemCountByRegionId[region.regionId] = Math.max(apiCount, 1);
42197
+ }
42198
+ return itemCountByRegionId;
42171
42199
  }
42172
42200
  function mergeCheckTableItemInfosIntoDefaults(defaultsMap, runtimeJson, infos, options) {
42173
42201
  const regions = listCheckTableRegionsInRuntimeJson(runtimeJson);
@@ -42246,14 +42274,14 @@ const basicAttrs = {
42246
42274
  required: [
42247
42275
  {
42248
42276
  from: "required_",
42249
- to: "newSpecificConfig.newRequired",
42277
+ to: "required",
42250
42278
  transform: (value) => Boolean(value)
42251
- },
42252
- {
42253
- from: "_",
42254
- to: "newSpecificConfig.newFieldName",
42255
- transform: () => "值"
42256
42279
  }
42280
+ // {
42281
+ // from: '_',
42282
+ // to: 'newSpecificConfig.newFieldName',
42283
+ // transform: () => '值',
42284
+ // },
42257
42285
  ],
42258
42286
  placeholder: [{ from: "tip_text_", to: "placeholder" }],
42259
42287
  showType: [
@@ -42366,7 +42394,7 @@ const DYN_CONFIG_BY_TYPE = {
42366
42394
  },
42367
42395
  {
42368
42396
  from: "digits_",
42369
- to: "newSpecificConfig.newPrecision",
42397
+ to: "newSpecificConfig.digits",
42370
42398
  transform: (value) => {
42371
42399
  const n = parseInt(String(value), 10);
42372
42400
  return Number.isNaN(n) ? void 0 : n;
@@ -42384,11 +42412,57 @@ const DYN_CONFIG_BY_TYPE = {
42384
42412
  ...basicAttrs.showType,
42385
42413
  {
42386
42414
  from: "_",
42387
- to: "newSpecificConfig.newOptions",
42388
- transform: (_v, _t2, row) => [
42389
- { label: row.true_text_ || "是", value: true, refFields: [], _item: {} },
42390
- { label: row.false_text_ || "", value: false, refFields: [], _item: {} }
42391
- ]
42415
+ to: "optionsJson",
42416
+ transform: (_v, _t2, row) => {
42417
+ const options = [
42418
+ { label: row.true_text_ || "", value: true, refFields: [], _item: {} },
42419
+ { label: row.false_text_ || "否", value: false, refFields: [], _item: {} }
42420
+ ];
42421
+ const json = JSON.stringify(options);
42422
+ return json;
42423
+ }
42424
+ },
42425
+ {
42426
+ from: "_",
42427
+ to: "computedOptions",
42428
+ transform: (_v, _t2, row) => {
42429
+ const options = [
42430
+ { label: row.true_text_ || "是", value: true, refFields: [], _item: {} },
42431
+ { label: row.false_text_ || "否", value: false, refFields: [], _item: {} }
42432
+ ];
42433
+ const json = JSON.stringify(options);
42434
+ return json;
42435
+ }
42436
+ },
42437
+ {
42438
+ from: "_",
42439
+ to: "iconLabelSpace",
42440
+ transform: () => 8
42441
+ },
42442
+ {
42443
+ from: "_",
42444
+ to: "labelPosition",
42445
+ transform: () => "after"
42446
+ },
42447
+ {
42448
+ from: "_",
42449
+ to: "direction",
42450
+ transform: () => "landscape"
42451
+ },
42452
+ {
42453
+ from: "_",
42454
+ to: "controlSize",
42455
+ transform: () => 24
42456
+ },
42457
+ {
42458
+ from: "_",
42459
+ to: "controlEnumSpace",
42460
+ transform: () => 16
42461
+ },
42462
+ {
42463
+ from: "_",
42464
+ to: "showMode",
42465
+ transform: () => "both"
42392
42466
  },
42393
42467
  { from: "validate_true_", to: "validateTrue" },
42394
42468
  { from: "validate_false_", to: "validateFalse" }
@@ -42494,12 +42568,6 @@ function buildPropsFromRow(row, config, baseProps = {}) {
42494
42568
  const val = rowDef.transform ? rowDef.transform(raw, config.fieldType, row, baseProps) : raw;
42495
42569
  if (val !== void 0) set(patch, rowDef.to, val);
42496
42570
  }
42497
- const options = get(patch, "newSpecificConfig.newOptions");
42498
- if (Array.isArray(options) && options.length > 0) {
42499
- const json = JSON.stringify(options);
42500
- patch.computedOptions = json;
42501
- patch.optionsJson = json;
42502
- }
42503
42571
  return patch;
42504
42572
  }
42505
42573
  function resolveFwType(fieldType, props) {
@@ -43770,7 +43838,8 @@ class TableExpander {
43770
43838
  /** 编辑模式只展开一份;预览/填报模式按数据路径长度重复 */
43771
43839
  getRepeatCount(region) {
43772
43840
  if (this.doc.mode === DocModeTypeConst.Edit) return 1;
43773
- return this.data.get(region.subValuePath || region.valuePath)?.length || 1;
43841
+ const path2 = region.subValuePath || region.valuePath;
43842
+ return this.data.countActiveSubtableRows(path2) || 1;
43774
43843
  }
43775
43844
  // ─── 循环子表 / 二维子表(纵向重复)────────────────────────
43776
43845
  initializeRepeatingRowsInRange(region) {
@@ -44020,8 +44089,8 @@ class TableExpander {
44020
44089
  if (this.doc.mode === DocModeTypeConst.Edit) return 1;
44021
44090
  const path2 = region.subValuePath || region.valuePath;
44022
44091
  if (path2) {
44023
- const data = this.data.get(path2);
44024
- if (Array.isArray(data) && data.length > 0) return data.length;
44092
+ const activeCount = this.data.countActiveSubtableRows(path2);
44093
+ if (activeCount > 0) return activeCount;
44025
44094
  }
44026
44095
  return Math.max(1, region.end.row - region.start.row + 1);
44027
44096
  }
@@ -46861,6 +46930,61 @@ class PostChangePipeline {
46861
46930
  }
46862
46931
  }
46863
46932
  }
46933
+ function hasRowId(row) {
46934
+ const id = row.id_ ?? row.id;
46935
+ return id !== void 0 && id !== null && String(id).trim() !== "";
46936
+ }
46937
+ function hasPersistedCrossCell(rawData, tableKey, axis, row) {
46938
+ const uid = axis === "y" ? row.__gw_y_uid : row.__gw_x_uid;
46939
+ if (!uid) return false;
46940
+ const uidField = axis === "y" ? "__gw_y_uid" : "__gw_x_uid";
46941
+ for (const crossKey of Object.keys(rawData)) {
46942
+ if (!crossKey.includes(":")) continue;
46943
+ const [yKey, xKey] = crossKey.split(":");
46944
+ const onThisAxis = axis === "y" ? yKey === tableKey : xKey === tableKey;
46945
+ if (!onThisAxis) continue;
46946
+ const crossRows = rawData[crossKey];
46947
+ if (!Array.isArray(crossRows)) continue;
46948
+ if (crossRows.some((cell) => cell?.[uidField] === uid && hasRowId(cell))) {
46949
+ return true;
46950
+ }
46951
+ }
46952
+ return false;
46953
+ }
46954
+ function isPersistedSubtableRow(row, ctx) {
46955
+ if (!row || typeof row !== "object") return false;
46956
+ if (hasRowId(row)) return true;
46957
+ if (ctx?.rawData && ctx.tableKey && ctx.axis) {
46958
+ return hasPersistedCrossCell(ctx.rawData, ctx.tableKey, ctx.axis, row);
46959
+ }
46960
+ return false;
46961
+ }
46962
+ function countActiveSubtableRows(rows) {
46963
+ if (!Array.isArray(rows)) return 0;
46964
+ return rows.filter((row) => !row?.deleted_).length;
46965
+ }
46966
+ function resolveSubtableStorageIndex(rows, visibleIndex) {
46967
+ if (!Array.isArray(rows) || visibleIndex < 0) return visibleIndex;
46968
+ let seen = -1;
46969
+ for (let i = 0; i < rows.length; i++) {
46970
+ if (!rows[i]?.deleted_) {
46971
+ seen++;
46972
+ if (seen === visibleIndex) return i;
46973
+ }
46974
+ }
46975
+ return visibleIndex;
46976
+ }
46977
+ function resolveSubtableInsertIndex(rows, visibleIndex) {
46978
+ if (!Array.isArray(rows)) return 0;
46979
+ let seen = -1;
46980
+ for (let i = 0; i < rows.length; i++) {
46981
+ if (!rows[i]?.deleted_) {
46982
+ seen++;
46983
+ if (seen === visibleIndex) return i;
46984
+ }
46985
+ }
46986
+ return rows.length;
46987
+ }
46864
46988
  const DEFAULT_EMPTY_ITEM = Object.freeze({ __gw_default: true });
46865
46989
  class DataManager {
46866
46990
  doc;
@@ -46947,6 +47071,17 @@ class DataManager {
46947
47071
  getLabel(path2) {
46948
47072
  return this.get(this.getLabelPath(path2)) || this.get(path2);
46949
47073
  }
47074
+ /** 子表数组路径下未软删行数 */
47075
+ countActiveSubtableRows(arrayPath) {
47076
+ const rows = this.getByPath(this.rawData, arrayPath);
47077
+ return countActiveSubtableRows(rows);
47078
+ }
47079
+ /** 版面可见行下标 → rawData 数组实际下标 */
47080
+ resolveSubtableVisibleIndex(arrayPath, visibleIndex) {
47081
+ const rows = this.getByPath(this.rawData, arrayPath);
47082
+ if (!Array.isArray(rows)) return visibleIndex;
47083
+ return resolveSubtableStorageIndex(rows, visibleIndex);
47084
+ }
46950
47085
  assignField(target, fieldPath, value) {
46951
47086
  if (!fieldPath.includes(".") && !fieldPath.includes("[")) {
46952
47087
  target[fieldPath] = value;
@@ -47348,7 +47483,8 @@ class DataManager {
47348
47483
  arr = [{ ...DEFAULT_EMPTY_ITEM }];
47349
47484
  this.set(path2, arr);
47350
47485
  }
47351
- if (index2 < 0 || index2 > arr.length) {
47486
+ const storageIndex = resolveSubtableInsertIndex(arr, index2);
47487
+ if (storageIndex < 0 || storageIndex > arr.length) {
47352
47488
  throw new Error(`Index ${index2} out of bounds for array of length ${arr.length}`);
47353
47489
  }
47354
47490
  const oldValue = [...arr];
@@ -47357,9 +47493,9 @@ class DataManager {
47357
47493
  const clonedValues = values.map(
47358
47494
  (value) => axisInfo ? this.prepareAxisRowForInsert(arr, axisInfo, value) : this.deepClone(value)
47359
47495
  );
47360
- arr.splice(index2, 0, ...clonedValues);
47496
+ arr.splice(storageIndex, 0, ...clonedValues);
47361
47497
  if (axisInfo) {
47362
- for (let i = index2; i < arr.length; i++) {
47498
+ for (let i = storageIndex; i < arr.length; i++) {
47363
47499
  this.ensureAxisRowAndUid(tableKey, i, axisInfo.axis);
47364
47500
  }
47365
47501
  }
@@ -47382,14 +47518,26 @@ class DataManager {
47382
47518
  if (!Array.isArray(arr)) {
47383
47519
  throw new Error(`Path "${path2}" is not an array`);
47384
47520
  }
47385
- if (index2 < 0 || index2 >= arr.length) {
47521
+ const storageIndex = resolveSubtableStorageIndex(arr, index2);
47522
+ if (storageIndex < 0 || storageIndex >= arr.length) {
47386
47523
  throw new Error(`Index ${index2} out of bounds for array of length ${arr.length}`);
47387
47524
  }
47388
- const oldValue = [...arr];
47389
- const removedValue = arr.splice(index2, 1)[0];
47390
- this.emit(path2, arr, oldValue);
47391
47525
  const tableKey = path2.replace(/^\$\./, "").replace(/\[(n|\d+)\]/g, "");
47392
47526
  const axisInfo = this.resolveAxisByTableKey(tableKey);
47527
+ const row = arr[storageIndex];
47528
+ if (isPersistedSubtableRow(row, {
47529
+ rawData: this.rawData,
47530
+ tableKey,
47531
+ axis: axisInfo?.axis
47532
+ })) {
47533
+ const oldValue2 = this.deepClone(arr);
47534
+ row.deleted_ = true;
47535
+ this.emit(path2, arr, oldValue2);
47536
+ return row;
47537
+ }
47538
+ const oldValue = [...arr];
47539
+ const removedValue = arr.splice(storageIndex, 1)[0];
47540
+ this.emit(path2, arr, oldValue);
47393
47541
  if (axisInfo) {
47394
47542
  const uidField = axisInfo.axis === "y" ? "__gw_y_uid" : "__gw_x_uid";
47395
47543
  this.cleanupCrossByAxisUid(tableKey, axisInfo.axis, removedValue?.[uidField]);
@@ -47470,6 +47618,50 @@ class DataManager {
47470
47618
  }
47471
47619
  }
47472
47620
  }
47621
+ this.syncSubTableRowsToLayout();
47622
+ }
47623
+ /**
47624
+ * 仅补齐检验表关联轴子表(如 f_jianyan2)到动态关联铺砖槽位数。
47625
+ * 不处理检验项主体(f_jianyan1)与二维表,避免覆盖接口行或误扩纵向轴。
47626
+ */
47627
+ syncSubTableRowsToLayout() {
47628
+ const listSubTables = this.doc?.model?.getSubTableInfoList;
47629
+ if (typeof listSubTables !== "function") return;
47630
+ for (const info of listSubTables.call(this.doc.model)) {
47631
+ if (info.subType !== "check-table-2d-link" || !info.field) continue;
47632
+ const linkFieldKey = info.field;
47633
+ const layoutRows = this.doc?.model?.getSubTableLayoutSlotCount(linkFieldKey) ?? 0;
47634
+ if (layoutRows <= 0) continue;
47635
+ const dataRows = this.countSubTableDataRows(linkFieldKey);
47636
+ const targetRows = Math.max(dataRows, layoutRows);
47637
+ if (targetRows <= dataRows) continue;
47638
+ this.ensureCheckTableLinkLayoutRows(linkFieldKey, targetRows);
47639
+ }
47640
+ }
47641
+ /** 子表在 rawData 中的行数(支持尚未转成数组的 { data: [] } 接口形态,此时返回 0 且不在此阶段强转) */
47642
+ countSubTableDataRows(subTableKey) {
47643
+ const existing = this.rawData?.[subTableKey];
47644
+ if (Array.isArray(existing)) return existing.length;
47645
+ if (existing?.data && Array.isArray(existing.data)) return existing.data.length;
47646
+ return 0;
47647
+ }
47648
+ /**
47649
+ * 为检验表关联轴补足铺砖行:占位标记 + __gw_x_uid(与 push/insert 行为一致)。
47650
+ */
47651
+ ensureCheckTableLinkLayoutRows(linkFieldKey, rowCount) {
47652
+ const raw = this.rawData?.[linkFieldKey];
47653
+ if (raw != null && !Array.isArray(raw)) {
47654
+ return;
47655
+ }
47656
+ let arr = raw;
47657
+ if (!Array.isArray(arr)) {
47658
+ arr = [];
47659
+ this.rawData[linkFieldKey] = arr;
47660
+ }
47661
+ for (let i = arr.length; i < rowCount; i++) {
47662
+ arr.push({ ...DEFAULT_EMPTY_ITEM });
47663
+ this.ensureAxisRowAndUid(linkFieldKey, i, "x");
47664
+ }
47473
47665
  }
47474
47666
  applyInitDataCrossWildcard(path2, value) {
47475
47667
  const parse = parseValuePath(path2);
@@ -47512,8 +47704,7 @@ class DataManager {
47512
47704
  * layoutRows 为 0(如二维表纵向 f_ewb)时不按分组扩行,仅保证至少 1 行可绑定。
47513
47705
  */
47514
47706
  resolveInitRowCount(subTableKey) {
47515
- const existing = this.rawData?.[subTableKey];
47516
- const dataRows = Array.isArray(existing) ? existing.length : 0;
47707
+ const dataRows = this.countSubTableDataRows(subTableKey);
47517
47708
  const layoutRows = this.doc?.model?.getSubTableLayoutSlotCount(subTableKey) ?? 1;
47518
47709
  if (layoutRows > 0) {
47519
47710
  return Math.max(dataRows, layoutRows);
@@ -47525,7 +47716,11 @@ class DataManager {
47525
47716
  * @param asPlaceholder true:push/insert 用的系统占位行(带 __gw_default);false:applyInitData 物化行(空对象)
47526
47717
  */
47527
47718
  ensureSubTableRowCount(subTableKey, rowCount, asPlaceholder = true) {
47528
- let arr = this.rawData?.[subTableKey];
47719
+ const raw = this.rawData?.[subTableKey];
47720
+ if (raw != null && !Array.isArray(raw)) {
47721
+ return [];
47722
+ }
47723
+ let arr = raw;
47529
47724
  if (!Array.isArray(arr)) {
47530
47725
  arr = [];
47531
47726
  this.rawData[subTableKey] = arr;
@@ -50153,8 +50348,8 @@ class DocModel {
50153
50348
  * 根据版面数据分组 / 动态关联铺砖,计算子表字段在 rawData 中应初始化的行数。
50154
50349
  *
50155
50350
  * - 固定表:槽位归 subFieldKey(bounded + itemRegion)
50156
- * - 二维表 / 检验表:动态分组铺砖在关联轴,槽位归 linkFieldKey(如 f_ewblink);
50157
- * 纵向 subFieldKey(如 f_ewb)不由 itemRegion 撑行,返回 0
50351
+ * - 二维表 / 检验表:动态分组铺砖槽位归 linkFieldKey(如 f_jianyan2);
50352
+ * 纵向 subFieldKey(如 f_jianyan1)由检验项条数撑行,返回 0
50158
50353
  * - 其它子表无分组:返回 1
50159
50354
  */
50160
50355
  getSubTableLayoutSlotCount(subFieldKey) {
@@ -50166,15 +50361,25 @@ class DocModel {
50166
50361
  if (region.subFieldKey !== subFieldKey || !region.itemRegion) continue;
50167
50362
  const fillDirection = region.widgetMeta?.props?.fillDirection ?? "x";
50168
50363
  max = Math.max(max, countDataGroupSlots(region, region.itemRegion, fillDirection));
50169
- region.type;
50170
50364
  }
50171
- for (const region of [...table.checkTable, ...table._2DTable]) {
50365
+ for (const region of table.checkTable) {
50366
+ if (!region.itemRegion) continue;
50367
+ const range3 = expandDataGroupRowRange(region, region.itemRegion);
50368
+ const fillDirection = region.widgetMeta?.props?.fillDirection ?? "x";
50369
+ const slots = countDataGroupSlots(range3, region.itemRegion, fillDirection);
50370
+ if (region.linkFieldKey === subFieldKey) {
50371
+ max = Math.max(max, slots);
50372
+ }
50373
+ if (region.subFieldKey === subFieldKey) {
50374
+ isLinkSubTableSubAxisOnly = true;
50375
+ }
50376
+ }
50377
+ for (const region of table._2DTable) {
50172
50378
  if (!region.itemRegion) continue;
50173
50379
  const range3 = expandDataGroupRowRange(region, region.itemRegion);
50174
50380
  const slots = countDataGroupSlots(range3, region.itemRegion);
50175
50381
  if (region.linkFieldKey === subFieldKey) {
50176
50382
  max = Math.max(max, slots);
50177
- region.type;
50178
50383
  }
50179
50384
  if (region.subFieldKey === subFieldKey) {
50180
50385
  isLinkSubTableSubAxisOnly = true;
@@ -51581,34 +51786,34 @@ function transformSourceDataList(data, dict = {}) {
51581
51786
  return list || [];
51582
51787
  }
51583
51788
  function conversionFormState(payload) {
51584
- const { interfaceData, masterSlaveList, instances } = payload || {};
51789
+ const { interfaceData, masterSlaveList, widgetInstances } = payload || {};
51585
51790
  const formState = {};
51586
51791
  if (interfaceData && interfaceData.data) {
51587
51792
  merge(formState, transformSourceData(interfaceData.data, interfaceData.dict));
51588
51793
  }
51589
- let _2dKeys = [];
51590
- if (instances) {
51591
- const _2DFieldList = instances.map(({ valuePath }) => {
51794
+ let linkedKeyList = [];
51795
+ if (widgetInstances) {
51796
+ const linkedFieldList = widgetInstances.map(({ valuePath }) => {
51592
51797
  if (valuePath.includes(":")) {
51593
51798
  const { fieldKey, parentFieldKey, linkFieldKey } = parseValuePath(valuePath);
51594
- const group2DKey = `${parentFieldKey}:${linkFieldKey}`;
51799
+ const linkedKey = `${parentFieldKey}:${linkFieldKey}`;
51595
51800
  return {
51596
51801
  fieldKey,
51597
- group2DKey
51802
+ linkedKey
51598
51803
  };
51599
51804
  } else {
51600
51805
  return null;
51601
51806
  }
51602
51807
  }).filter(Boolean);
51603
- Object.entries(groupBy$1(_2DFieldList, "group2DKey")).forEach(([group2DKey, fields]) => {
51604
- _2dKeys.push(group2DKey);
51605
- const [parentFieldKey, linkFieldKey] = group2DKey.split(":");
51808
+ Object.entries(groupBy$1(linkedFieldList, "linkedKey")).forEach(([linkedKey, fields]) => {
51809
+ linkedKeyList.push(linkedKey);
51810
+ const [parentFieldKey, linkFieldKey] = linkedKey.split(":");
51606
51811
  const parentState = formState[parentFieldKey];
51607
51812
  const keys2 = ["id_", "group_", ...fields.map((f) => f.fieldKey)];
51608
51813
  formState[linkFieldKey]?.data.forEach((row) => {
51609
51814
  row.__gw_x_uid = row.group_;
51610
51815
  });
51611
- formState[group2DKey] = {
51816
+ formState[linkedKey] = {
51612
51817
  data: parentState?.data.map((row) => {
51613
51818
  const { group_: g_ } = row;
51614
51819
  const _row = pick(row, keys2);
@@ -51621,15 +51826,15 @@ function conversionFormState(payload) {
51621
51826
  }) || [],
51622
51827
  dict: parentState?.dict || []
51623
51828
  };
51624
- const addedYUids = [];
51829
+ const addedYUidList = [];
51625
51830
  formState[parentFieldKey] = {
51626
51831
  data: [],
51627
51832
  dict: formState[parentFieldKey]?.dict || []
51628
51833
  };
51629
51834
  parentState?.data.forEach((row) => {
51630
51835
  const yUid = row.group_.split(":")[0];
51631
- if (addedYUids.includes(yUid)) return;
51632
- addedYUids.push(yUid);
51836
+ if (addedYUidList.includes(yUid)) return;
51837
+ addedYUidList.push(yUid);
51633
51838
  formState[parentFieldKey].data.push({
51634
51839
  ...row,
51635
51840
  __gw_y_uid: yUid
@@ -51637,7 +51842,7 @@ function conversionFormState(payload) {
51637
51842
  });
51638
51843
  });
51639
51844
  }
51640
- masterSlaveList.map((item) => item.key).concat(_2dKeys).forEach((k) => {
51845
+ masterSlaveList.map((item) => item.key).concat(linkedKeyList).forEach((k) => {
51641
51846
  const subFormData = formState[k];
51642
51847
  if (subFormData) {
51643
51848
  formState[k] = transformSourceDataList([...subFormData.data], subFormData.dict);
@@ -51647,25 +51852,36 @@ function conversionFormState(payload) {
51647
51852
  }
51648
51853
  function getSubmitFormData(formData) {
51649
51854
  const _formData = { ...formData };
51650
- Object.keys(_formData).filter((k) => k.includes(":")).forEach((_2DKey) => {
51651
- const [subKey] = _2DKey.split(":");
51652
- const _2DData = _formData[_2DKey];
51653
- const subData = [..._formData[subKey] || []];
51654
- if ([_2DData, subData].every(Array.isArray)) {
51655
- _formData[subKey] = [];
51656
- _2DData.forEach((obj) => {
51657
- const { __gw_y_uid, __gw_x_uid } = obj;
51658
- const row = subData.find((row2) => row2.__gw_y_uid === __gw_y_uid);
51659
- if (!row) return;
51660
- _formData[subKey].push({
51661
- // 保留动态关联前端键值到 group_
51662
- group_: `${__gw_y_uid}:${__gw_x_uid}`,
51663
- ...row,
51664
- ...omit(obj, FILTER_KEYS)
51665
- });
51855
+ Object.keys(_formData).filter((k) => k.includes(":")).forEach((linkedKey) => {
51856
+ const [subKey] = linkedKey.split(":");
51857
+ const subData = _formData[subKey] || [];
51858
+ const crossData = _formData[linkedKey] || [];
51859
+ if ([crossData, subData].every(Array.isArray)) {
51860
+ const isCheckTable = subData[0] && Object.keys(subData[0]).includes("value_");
51861
+ if (!isCheckTable) {
51862
+ _formData[subKey] = [];
51863
+ }
51864
+ crossData.forEach((crossItem) => {
51865
+ const { __gw_y_uid, __gw_x_uid } = crossItem;
51866
+ const existedRow = subData.find((row) => row.__gw_y_uid === __gw_y_uid);
51867
+ const newRowInfo = omit(crossItem, FILTER_KEYS);
51868
+ const group_ = `${__gw_y_uid}:${__gw_x_uid}`;
51869
+ if (!existedRow) return;
51870
+ if (isCheckTable) {
51871
+ Object.assign(existedRow, {
51872
+ ...newRowInfo,
51873
+ group_
51874
+ });
51875
+ } else {
51876
+ _formData[subKey].push({
51877
+ ...existedRow,
51878
+ ...newRowInfo,
51879
+ group_
51880
+ });
51881
+ }
51666
51882
  });
51667
51883
  }
51668
- delete _formData[_2DKey];
51884
+ delete _formData[linkedKey];
51669
51885
  });
51670
51886
  const realFormData = Object.keys(_formData).reduce((reducedData, fieldKey) => {
51671
51887
  const fieldValue = _formData[fieldKey];
@@ -98183,8 +98399,18 @@ async function resolveCheckTableItemInfos(ctx) {
98183
98399
  protocolKey: checkTableDs.joinModelKey,
98184
98400
  instId: ctx.instanceId
98185
98401
  });
98402
+ res.data?.forEach((item) => {
98403
+ delete item.id_;
98404
+ });
98186
98405
  return res;
98187
98406
  }
98407
+ async function waitForDataManagerSettle() {
98408
+ await nextTick();
98409
+ await new Promise((resolve) => setTimeout(resolve, 0));
98410
+ }
98411
+ function syncInitRawDataSnapshot(doc) {
98412
+ doc.docRuntimeMeta.handleInfo.initRawDataSnapshot = cloneDeep(doc.dataManager.getRawData());
98413
+ }
98188
98414
  function snapshotDocInfo(docInst) {
98189
98415
  return {
98190
98416
  pages: docInst?.pages ?? [],
@@ -98251,8 +98477,7 @@ async function initializeDocumentEngine(props, payload, result) {
98251
98477
  });
98252
98478
  const rawData = conversionFormState({
98253
98479
  masterSlaveList,
98254
- interfaceData,
98255
- instances
98480
+ interfaceData
98256
98481
  });
98257
98482
  const docRuntimeMeta = {
98258
98483
  ...requestInfo,
@@ -98279,7 +98504,8 @@ async function initializeDocumentEngine(props, payload, result) {
98279
98504
  });
98280
98505
  doc.dataManager.setRawData(rawData, defaultDataMap);
98281
98506
  doc.dataManager.applyInitData({ ...paramMap, ...dsMap });
98282
- doc.docRuntimeMeta.handleInfo.initRawDataSnapshot = cloneDeep(doc.dataManager.getRawData());
98507
+ await waitForDataManagerSettle();
98508
+ syncInitRawDataSnapshot(doc);
98283
98509
  const docInfo = snapshotDocInfo(doc);
98284
98510
  return {
98285
98511
  doc,
@@ -98290,7 +98516,8 @@ async function finalizeDesignModelImport(doc, docModel) {
98290
98516
  doc.setModel(docModel);
98291
98517
  const mainModelKey = doc.mainModelKey;
98292
98518
  doc.dataManager.setRawData({}, {});
98293
- doc.docRuntimeMeta.handleInfo.initRawDataSnapshot = cloneDeep(doc.dataManager.getRawData());
98519
+ await waitForDataManagerSettle();
98520
+ syncInitRawDataSnapshot(doc);
98294
98521
  doc.commandManager.undoStack.length = 0;
98295
98522
  doc.commandManager.redoStack.length = 0;
98296
98523
  doc.eventManager.cursorController.clearCursor();
@@ -98353,6 +98580,7 @@ function useDocumentFactory(props, payload) {
98353
98580
  watch(
98354
98581
  () => finisher.value,
98355
98582
  () => {
98583
+ if (!docIns.value) return;
98356
98584
  console.log("Document request success");
98357
98585
  payload.onDocumentLoadSuccess?.();
98358
98586
  }
@@ -98419,10 +98647,10 @@ function useDocumentFactory(props, payload) {
98419
98647
  instances
98420
98648
  });
98421
98649
  doc.dataManager.setRawData(rawData);
98422
- await nextTick();
98423
- doc.docRuntimeMeta.handleInfo.initRawDataSnapshot = cloneDeep(doc.dataManager.getRawData());
98650
+ await waitForDataManagerSettle();
98651
+ syncInitRawDataSnapshot(doc);
98424
98652
  } finally {
98425
- await new Promise((resolve) => setTimeout(resolve, 0));
98653
+ await waitForDataManagerSettle();
98426
98654
  isRefreshingData.value = false;
98427
98655
  doc.eventManager.cursorController.clearCursor();
98428
98656
  }
@@ -103364,7 +103592,8 @@ const _sfc_main$2y = /* @__PURE__ */ defineComponent({
103364
103592
  };
103365
103593
  const copyRow = () => {
103366
103594
  const { path: path2, index: index2 } = getCtx();
103367
- const data = props.doc.dataManager.get(`${path2}[${index2}]`) || {};
103595
+ const storageIndex = props.doc.dataManager.resolveSubtableVisibleIndex(path2, index2);
103596
+ const data = props.doc.dataManager.get(`${path2}[${storageIndex}]`) || {};
103368
103597
  props.doc.dataManager.insertAt(path2, index2 + 1, data);
103369
103598
  };
103370
103599
  const actionHandlers = {
@@ -103506,7 +103735,7 @@ const _sfc_main$2y = /* @__PURE__ */ defineComponent({
103506
103735
  };
103507
103736
  }
103508
103737
  });
103509
- const SubTableAction = /* @__PURE__ */ _export_sfc(_sfc_main$2y, [["__scopeId", "data-v-a58c6b2e"]]);
103738
+ const SubTableAction = /* @__PURE__ */ _export_sfc(_sfc_main$2y, [["__scopeId", "data-v-da25771b"]]);
103510
103739
  const _sfc_main$2x = /* @__PURE__ */ defineComponent({
103511
103740
  __name: "index",
103512
103741
  props: {
@@ -104308,9 +104537,14 @@ function useDocLooperAutoSaveNotify(factory2, ops, options) {
104308
104537
  }
104309
104538
  function releaseSuppressOnLoad() {
104310
104539
  cancelFlushNotify();
104311
- Promise.resolve().then(() => {
104540
+ void (async () => {
104541
+ await waitForDataManagerSettle();
104542
+ const doc = factory2.docIns.value;
104543
+ if (doc) {
104544
+ syncInitRawDataSnapshot(doc);
104545
+ }
104312
104546
  suppress = false;
104313
- });
104547
+ })();
104314
104548
  }
104315
104549
  if (options.suppressSignal) {
104316
104550
  watch(
@@ -1,5 +1,5 @@
1
1
  import { CheckTableItemInfosPayload } from './types';
2
- /** 从检验项详情推导每张检验表需要扩的主体行数 */
2
+ /** 从检验项详情推导每张检验表需要扩的主体行数(仅检验项条数,不含关联轴铺砖槽位) */
3
3
  export declare function resolveItemCountByRegionId(runtimeJson: unknown, infos: CheckTableItemInfosPayload): Record<string, number>;
4
4
  /**
5
5
  * 将接口返回的检验项配置写入 defaults(展开为 $.f_jianyan1[0].type_ 等字段路径)。
@@ -0,0 +1,8 @@
1
+ import { Doc } from '../../../core';
2
+ /**
3
+ * 等待 Vue 响应式与 DataManager 后置流水线(依赖 / enrich / emit 队列)完成一轮 settle。
4
+ * 与 refreshData 收尾一致,避免过早读取 rawData 或误判 dirty。
5
+ */
6
+ export declare function waitForDataManagerSettle(): Promise<void>;
7
+ /** 将当前 rawData 写入 initRawDataSnapshot,供 getUnsavedChanges({ includeRawData: true }) 比对 */
8
+ export declare function syncInitRawDataSnapshot(doc: Doc): void;
@@ -198,12 +198,14 @@ export interface UseWordOptions {
198
198
  /** 指定渲染模式 */
199
199
  renderModeType?: DocModeType;
200
200
  /**
201
- * 文档接口请求成功且引擎挂载完成后的回调(每轮成功加载触发一次)。
201
+ * 文档接口请求成功且引擎挂载完成、dataManager 首轮 settle 后的回调(每轮成功加载触发一次)。
202
+ * 此时 controller.rawData() 已与内部 initRawDataSnapshot 对齐,适合做外部自动保存基线。
202
203
  * 不会在 requestId 为空、请求被丢弃、或初始化抛错时调用。
203
204
  */
204
205
  onDocumentLoadSuccess?: () => void;
205
206
  /**
206
- * 文档数据发生变化(防抖 300ms 后)的回调,可在此处触发外部自动保存通知。
207
+ * 文档数据发生变化(防抖 300ms 后)且存在未保存改动时的回调,可触发外部自动保存轮询。
208
+ * 保存成功后请调用 controller.markAsSaved(),避免重复通知。
207
209
  * 例:() => emitter.emit(EmitterEnum.__on_looper_auto_save)
208
210
  */
209
211
  onLooperAutoSave?: (payload?: {
@@ -13,7 +13,7 @@ export declare function conversionFormState(payload: {
13
13
  /** 渲染数据列表 */
14
14
  interfaceData: any;
15
15
  /** 主要是 WrField */
16
- instances: Wr[];
16
+ widgetInstances: Wr[];
17
17
  }): Record<string, any>;
18
18
  /** 数据转成真实提交数据 */
19
19
  export declare function getSubmitFormData(formData: any): any;
package/dist/word.css CHANGED
@@ -8170,17 +8170,17 @@ textarea[data-v-57fe54a3]::placeholder {
8170
8170
  .table-action .row-headers .row-header:last-child .row-add-btn[data-v-c40eef9f] .gct-icon.bottom {
8171
8171
  transform: translateY(-100%);
8172
8172
  }
8173
- .sub-table-action[data-v-a58c6b2e] {
8173
+ .sub-table-action[data-v-da25771b] {
8174
8174
  position: absolute;
8175
8175
  z-index: 999;
8176
8176
  }
8177
- .sub-table-action-content[data-v-a58c6b2e] {
8177
+ .sub-table-action-content[data-v-da25771b] {
8178
8178
  display: flex;
8179
8179
  align-items: center;
8180
8180
  gap: 4px;
8181
8181
  padding: 4px;
8182
8182
  }
8183
- .action-group[data-v-a58c6b2e] {
8183
+ .action-group[data-v-da25771b] {
8184
8184
  display: flex;
8185
8185
  align-items: center;
8186
8186
  gap: 4px;
@@ -8188,15 +8188,15 @@ textarea[data-v-57fe54a3]::placeholder {
8188
8188
  border-radius: 4px;
8189
8189
  overflow: hidden;
8190
8190
  }
8191
- .action-group .sub-table-btn[data-v-a58c6b2e] {
8191
+ .action-group .sub-table-btn[data-v-da25771b] {
8192
8192
  border-radius: 0;
8193
8193
  }
8194
- .sub-table-action-bar[data-v-a58c6b2e],
8195
- .sub-table-action-content[data-v-a58c6b2e] {
8194
+ .sub-table-action-bar[data-v-da25771b],
8195
+ .sub-table-action-content[data-v-da25771b] {
8196
8196
  pointer-events: auto;
8197
8197
  user-select: none;
8198
8198
  }
8199
- .sub-table-btn[data-v-a58c6b2e] {
8199
+ .sub-table-btn[data-v-da25771b] {
8200
8200
  display: flex;
8201
8201
  align-items: center;
8202
8202
  justify-content: center;
@@ -8206,25 +8206,25 @@ textarea[data-v-57fe54a3]::placeholder {
8206
8206
  border-radius: 4px;
8207
8207
  cursor: pointer;
8208
8208
  }
8209
- .sub-table-btn.delete-icon[data-v-a58c6b2e] {
8209
+ .sub-table-btn.delete-icon[data-v-da25771b] {
8210
8210
  border: none;
8211
8211
  background-color: #ff7875;
8212
8212
  }
8213
- .insert-row-container[data-v-a58c6b2e] {
8213
+ .insert-row-container[data-v-da25771b] {
8214
8214
  padding: 6px;
8215
8215
  }
8216
- .insert-row-container .insert-row-item[data-v-a58c6b2e] {
8216
+ .insert-row-container .insert-row-item[data-v-da25771b] {
8217
8217
  display: flex;
8218
8218
  align-items: center;
8219
8219
  gap: 8px;
8220
8220
  padding: 5px 8px;
8221
8221
  cursor: pointer;
8222
8222
  }
8223
- .insert-row-container .insert-row-item[data-v-a58c6b2e]:hover {
8223
+ .insert-row-container .insert-row-item[data-v-da25771b]:hover {
8224
8224
  background: rgba(13, 13, 13, 0.06);
8225
8225
  border-radius: 4px;
8226
8226
  }
8227
- .insert-row-container .insert-row-item .title[data-v-a58c6b2e] {
8227
+ .insert-row-container .insert-row-item .title[data-v-da25771b] {
8228
8228
  font-size: 14px;
8229
8229
  line-height: 28px;
8230
8230
  color: #0d0d0d;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gct-paas/word",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "description": "GCT 在线 word",
5
5
  "keywords": [
6
6
  "vue",