cloud-web-corejs 1.0.54-dev.786 → 1.0.54-dev.787

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 (121) hide show
  1. package/package.json +2 -1
  2. package/src/components/VabUpload/index.vue +1 -1
  3. package/src/components/excelImport/index.js +69 -7
  4. package/src/components/excelImport/mixins.js +841 -9
  5. package/src/components/fileLibrary/fileHistoryDialog.vue +1 -1
  6. package/src/components/fileLibrary/fileObjAuthEdit.vue +1 -1
  7. package/src/components/fileLibrary/filterDialog.vue +1 -1
  8. package/src/components/formDialog/README.md +324 -241
  9. package/src/components/formDialog/actionButtons.js +90 -0
  10. package/src/components/formDialog/index.js +48 -35
  11. package/src/components/formDialog/index.vue +802 -724
  12. package/src/components/formDrawer/drawer.vue +719 -0
  13. package/src/components/formDrawer/index.js +50 -0
  14. package/src/components/onlineTalk/talkUserDialog.vue +1 -1
  15. package/src/components/tempStorage/tempStorageDialog.vue +1 -1
  16. package/src/components/wf/addTaskUserdialog.vue +1 -1
  17. package/src/components/xform/form-designer/form-widget/container-widget/data-table-widget.vue +1 -1
  18. package/src/components/xform/form-designer/form-widget/dialog/formDialog.vue +115 -4
  19. package/src/components/xform/form-designer/form-widget/dialog/formDrawer.vue +102 -5
  20. package/src/components/xform/form-designer/form-widget/dialog/formFieldDialog.vue +1 -1
  21. package/src/components/xform/form-designer/form-widget/dialog/vabSearchDialog.vue +1 -1
  22. package/src/components/xform/form-designer/setting-panel/action-button-config-dialog.vue +241 -0
  23. package/src/components/xform/form-designer/setting-panel/form-setting.vue +116 -0
  24. package/src/components/xform/form-designer/setting-panel/property-editor/container-detail/detail-editor.vue +6 -0
  25. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +1 -0
  26. package/src/components/xform/form-render/container-item/data-table-mixin.js +1460 -1460
  27. package/src/components/xform/form-render/container-item/detail-item.vue +11 -2
  28. package/src/components/xform/form-render/indexMixin.js +5 -0
  29. package/src/index.js +506 -472
  30. package/src/utils/lazyPlugin.js +66 -0
  31. package/src/views/bd/setting/bd_attach_setting/dialog.vue +1 -1
  32. package/src/views/bd/setting/bd_attach_setting/list.vue +1 -1
  33. package/src/views/bd/setting/bd_company_env/dialog.vue +1 -1
  34. package/src/views/bd/setting/bd_company_env/list.vue +1 -1
  35. package/src/views/bd/setting/formVersion/ftHistoryDialog.vue +1 -1
  36. package/src/views/bd/setting/form_import_log/list.vue +1 -1
  37. package/src/views/bd/setting/form_script/dialog.vue +1 -1
  38. package/src/views/bd/setting/form_script/form_list.vue +1 -1
  39. package/src/views/bd/setting/form_script/list.vue +1 -1
  40. package/src/views/bd/setting/form_script/list1.vue +1 -1
  41. package/src/views/bd/setting/form_template/dialog.vue +1 -1
  42. package/src/views/bd/setting/form_template/ftHistoryDialog.vue +1 -1
  43. package/src/views/bd/setting/form_template/itemList.vue +1 -1
  44. package/src/views/bd/setting/form_template/list.vue +1 -1
  45. package/src/views/bd/setting/form_template/wf_list.vue +1 -1
  46. package/src/views/bd/setting/logic_param/list.vue +1 -1
  47. package/src/views/bd/setting/menu_kind/dialog.vue +1 -1
  48. package/src/views/bd/setting/menu_kind/list.vue +1 -1
  49. package/src/views/bd/setting/push_data/list.vue +1 -1
  50. package/src/views/bd/setting/push_data_h/list.vue +1 -1
  51. package/src/views/bd/setting/request_async_setting/list.vue +1 -1
  52. package/src/views/bd/setting/request_setting/list.vue +1 -1
  53. package/src/views/bd/setting/table_model/dialog.vue +1 -1
  54. package/src/views/bd/setting/table_model/list.vue +1 -1
  55. package/src/views/bd/setting/table_model/zdDialog.vue +1 -1
  56. package/src/views/support/export_template/list.vue +1 -1
  57. package/src/views/user/access_log/list.vue +1 -1
  58. package/src/views/user/access_log/list2.vue +1 -1
  59. package/src/views/user/api_request/list.vue +1 -1
  60. package/src/views/user/area/dialog.vue +1 -1
  61. package/src/views/user/area/list.vue +1 -1
  62. package/src/views/user/bill_setting/list.vue +1 -1
  63. package/src/views/user/bill_setting/userDialog.vue +1 -1
  64. package/src/views/user/code_rules/list.vue +1 -1
  65. package/src/views/user/common_attribute/list.vue +1 -1
  66. package/src/views/user/common_script/list.vue +1 -1
  67. package/src/views/user/company_info/dialog.vue +1 -1
  68. package/src/views/user/country/dialog.vue +1 -1
  69. package/src/views/user/country/list.vue +1 -1
  70. package/src/views/user/data_type_setting/dialog.vue +1 -1
  71. package/src/views/user/extend_datasource/dialog.vue +1 -1
  72. package/src/views/user/extend_datasource/list.vue +1 -1
  73. package/src/views/user/fieldTranslation/list.vue +1 -1
  74. package/src/views/user/field_values_invisible/list.vue +1 -1
  75. package/src/views/user/file_type/list.vue +1 -1
  76. package/src/views/user/file_view_area/list.vue +1 -1
  77. package/src/views/user/form/form_template/dialog.vue +1 -1
  78. package/src/views/user/form/form_template/itemList.vue +1 -1
  79. package/src/views/user/form/form_template/list.vue +1 -1
  80. package/src/views/user/form/form_template_field/dialog.vue +1 -1
  81. package/src/views/user/form/form_type/dialog.vue +1 -1
  82. package/src/views/user/form/form_type/list.vue +1 -1
  83. package/src/views/user/form/report_requestaccess/dialog.vue +1 -1
  84. package/src/views/user/form/report_requestaccess/list.vue +1 -1
  85. package/src/views/user/groups/dialog.vue +1 -1
  86. package/src/views/user/groups/list.vue +1 -1
  87. package/src/views/user/lang_tag/dialog.vue +1 -1
  88. package/src/views/user/lang_tag/list.vue +1 -1
  89. package/src/views/user/language_setting/list.vue +1 -1
  90. package/src/views/user/menu/list.vue +1 -1
  91. package/src/views/user/mobile_menu/list.vue +1 -1
  92. package/src/views/user/notify_message/list.vue +1 -1
  93. package/src/views/user/notify_template/list.vue +1 -1
  94. package/src/views/user/notify_template/list2.vue +1 -1
  95. package/src/views/user/oplog/list.vue +1 -1
  96. package/src/views/user/position/dialog.vue +1 -1
  97. package/src/views/user/position/list.vue +1 -1
  98. package/src/views/user/project_tag/dialog.vue +1 -1
  99. package/src/views/user/project_tag/list.vue +1 -1
  100. package/src/views/user/push_setting/list.vue +1 -1
  101. package/src/views/user/request_setting/list.vue +1 -1
  102. package/src/views/user/role/dialog.vue +1 -1
  103. package/src/views/user/role/list.vue +1 -1
  104. package/src/views/user/sale_org/dialog.vue +1 -1
  105. package/src/views/user/sale_org/list.vue +1 -1
  106. package/src/views/user/system_notice/list.vue +1 -1
  107. package/src/views/user/system_parameter/list.vue +1 -1
  108. package/src/views/user/user/dialog.vue +1 -1
  109. package/src/views/user/user/form_dialog.vue +1 -1
  110. package/src/views/user/user/form_list.vue +1 -1
  111. package/src/views/user/user/list.vue +2 -2
  112. package/src/views/user/user_log_classify/list.vue +1 -1
  113. package/src/views/user/wf/wfReport/index.vue +2 -2
  114. package/src/views/user/wf/wf_auto_submit_data/list.vue +2 -2
  115. package/src/views/user/wf/wf_manage/list.vue +2 -2
  116. package/src/views/user/wf/wf_manage/list2.vue +2 -2
  117. package/src/views/user/wf/wf_obj_config/dialog.vue +1 -1
  118. package/src/views/user/wf/wf_obj_config/list.vue +1 -1
  119. package/src/views/user/wf/wf_obj_config/list_form.vue +1 -1
  120. package/src/views/user/wf/wf_transfer_setting/list.vue +1 -1
  121. package/src/views/user/workbench_menu/list.vue +1 -1
@@ -33,30 +33,30 @@ import {
33
33
  isEmptyCellValueInRow,
34
34
  shouldSkipCellValidation,
35
35
  } from "../../../../components/xform/utils/tableCellValidateUtil";
36
- import {
37
- buildDynamicSchemaRequestData,
38
- getDynamicSchemaOption,
39
- isActionColumnFormat,
40
- isNonDataExportColumn,
41
- normalizeDynamicColumns as normalizeDynamicColumnList,
42
- resolveDynamicSchemaResponse,
43
- } from "../../utils/dynamicSchemaUtil";
44
- import {
45
- resolveTreeFields,
46
- resolveTreeIdentityConfig,
47
- resolveTreeCopyConfig,
48
- resolveTreeSubmitConfig,
49
- canonicalizeTreeInput,
50
- isRootParent,
51
- normalizeTreeKey,
52
- treeKeyEquals,
53
- makeTreeTempKey,
54
- markTemporaryTreeKey,
55
- isTemporaryTreeKey,
56
- syncTreeKeyReferences,
57
- sanitizeTreeRow,
58
- } from "../../utils/treeTableUtil";
59
- import { tableTreeMixins } from "../../../../mixins/tableTree/index.js";
36
+ import {
37
+ buildDynamicSchemaRequestData,
38
+ getDynamicSchemaOption,
39
+ isActionColumnFormat,
40
+ isNonDataExportColumn,
41
+ normalizeDynamicColumns as normalizeDynamicColumnList,
42
+ resolveDynamicSchemaResponse,
43
+ } from "../../utils/dynamicSchemaUtil";
44
+ import {
45
+ resolveTreeFields,
46
+ resolveTreeIdentityConfig,
47
+ resolveTreeCopyConfig,
48
+ resolveTreeSubmitConfig,
49
+ canonicalizeTreeInput,
50
+ isRootParent,
51
+ normalizeTreeKey,
52
+ treeKeyEquals,
53
+ makeTreeTempKey,
54
+ markTemporaryTreeKey,
55
+ isTemporaryTreeKey,
56
+ syncTreeKeyReferences,
57
+ sanitizeTreeRow,
58
+ } from "../../utils/treeTableUtil";
59
+ import { tableTreeMixins } from "../../../../mixins/tableTree/index.js";
60
60
 
61
61
  let modules = {};
62
62
  const baseRefUtil = {
@@ -163,17 +163,17 @@ modules = {
163
163
  dynamicTableColumns: [],
164
164
  dynamicColumnsPreloaded: false,
165
165
  dynamicColumnsInitResolved: false,
166
- dynamicColumnsInitSkipped: false,
167
- resolvedTreeConfig: null,
168
- resolvedTreeIdentityConfig: null,
169
- resolvedTreeCopyConfig: null,
170
- resolvedTreeSubmitConfig: null,
171
- treeTemporaryKeyRegistry: new Set(),
172
- treeKnownFormalKeyRegistry: new Set(),
173
- treeSingleSavePending: false,
174
- treeSingleSaveReconcileTask: null,
175
- // 列/网格未就绪时 setValue 只缓存数据,置此标志延后行 schema 构建
176
- pendingSchemaInit: false,
166
+ dynamicColumnsInitSkipped: false,
167
+ resolvedTreeConfig: null,
168
+ resolvedTreeIdentityConfig: null,
169
+ resolvedTreeCopyConfig: null,
170
+ resolvedTreeSubmitConfig: null,
171
+ treeTemporaryKeyRegistry: new Set(),
172
+ treeKnownFormalKeyRegistry: new Set(),
173
+ treeSingleSavePending: false,
174
+ treeSingleSaveReconcileTask: null,
175
+ // 列/网格未就绪时 setValue 只缓存数据,置此标志延后行 schema 构建
176
+ pendingSchemaInit: false,
177
177
  };
178
178
  },
179
179
  watch: {
@@ -631,22 +631,22 @@ modules = {
631
631
  this.initRowIdData(true);
632
632
  this.initFieldSchemaData();
633
633
  },
634
- /** @param {Array} val 外部传入数据。@returns {Array} 表单与 VXE 共用的标准化行。 */
635
- initValue(val) {
636
- // this.clearRowWidgets()
637
- if (this.widget.options.isTreeTable) {
638
- this.treeTemporaryKeyRegistry.clear();
639
- this.treeKnownFormalKeyRegistry.clear();
640
- }
641
- let rows = this.canonicalizeTreeRows(val || []);
642
- rows = this.normalizeTableRows(rows, false);
643
- // this.handleNullValue(rows);
644
- rows.forEach((row) => {
645
- row._X_ROW_KEY = "row_" + generateId();
646
- });
647
- this.registerTreeKeys(rows);
648
- this.formModel[this.fieldKeyName] = rows;
649
- this.fieldModel = rows;
634
+ /** @param {Array} val 外部传入数据。@returns {Array} 表单与 VXE 共用的标准化行。 */
635
+ initValue(val) {
636
+ // this.clearRowWidgets()
637
+ if (this.widget.options.isTreeTable) {
638
+ this.treeTemporaryKeyRegistry.clear();
639
+ this.treeKnownFormalKeyRegistry.clear();
640
+ }
641
+ let rows = this.canonicalizeTreeRows(val || []);
642
+ rows = this.normalizeTableRows(rows, false);
643
+ // this.handleNullValue(rows);
644
+ rows.forEach((row) => {
645
+ row._X_ROW_KEY = "row_" + generateId();
646
+ });
647
+ this.registerTreeKeys(rows);
648
+ this.formModel[this.fieldKeyName] = rows;
649
+ this.fieldModel = rows;
650
650
  this.initRowIdData(true);
651
651
  this.initFieldSchemaData(true);
652
652
  return rows;
@@ -656,38 +656,38 @@ modules = {
656
656
  * @param {Array} rows 原始行数据。
657
657
  * @returns {Array|undefined} 根节点优先、子节点紧随父节点的扁平数组。
658
658
  */
659
- handleTreeData(rows) {
660
- let isTreeTable = this.widget.options.isTreeTable;
661
- if (isTreeTable) {
662
- let $grid = this.getGridTable();
663
- rows = this.canonicalizeTreeRows(rows || [], $grid);
664
- let { rowField, parentField } = this.getTreeFields($grid);
665
- if (parentField) {
659
+ handleTreeData(rows) {
660
+ let isTreeTable = this.widget.options.isTreeTable;
661
+ if (isTreeTable) {
662
+ let $grid = this.getGridTable();
663
+ rows = this.canonicalizeTreeRows(rows || [], $grid);
664
+ let { rowField, parentField } = this.getTreeFields($grid);
665
+ if (parentField) {
666
666
  // 构建树结构并排序
667
667
  let sortedRows = [];
668
668
  let nodeMap = new Map();
669
669
 
670
- // 第一步:构建节点映射
671
- rows.forEach((row) => {
672
- if (row[rowField] !== undefined && row[rowField] !== null) {
673
- nodeMap.set(row[rowField], row);
674
- }
675
- });
670
+ // 第一步:构建节点映射
671
+ rows.forEach((row) => {
672
+ if (row[rowField] !== undefined && row[rowField] !== null) {
673
+ nodeMap.set(row[rowField], row);
674
+ }
675
+ });
676
676
 
677
677
  // 第二步:按层次排序,先根节点,然后子节点
678
678
  rows.forEach((row) => {
679
- let parentValue = row[parentField];
680
- // 根节点读取口径统一由树工具维护。
681
- if (isRootParent(parentValue)) {
682
- sortedRows.push(row);
683
- // 递归添加子节点
684
- this.addChildNodes(
685
- row[rowField],
686
- nodeMap,
687
- parentField,
688
- sortedRows
689
- );
690
- }
679
+ let parentValue = row[parentField];
680
+ // 根节点读取口径统一由树工具维护。
681
+ if (isRootParent(parentValue)) {
682
+ sortedRows.push(row);
683
+ // 递归添加子节点
684
+ this.addChildNodes(
685
+ row[rowField],
686
+ nodeMap,
687
+ parentField,
688
+ sortedRows
689
+ );
690
+ }
691
691
  });
692
692
 
693
693
  // 如果没有找到根节点,返回原始数组
@@ -712,12 +712,12 @@ modules = {
712
712
  return rows;
713
713
  },
714
714
  /** @param {*} parentId 父节点 id。@param {Map} nodeMap 节点映射。@param {String} parentField 父字段。@param {Array} sortedRows 输出数组。 */
715
- addChildNodes(parentId, nodeMap, parentField, sortedRows) {
716
- // 遍历所有节点,找出当前父节点的子节点
717
- for (let [id, node] of nodeMap.entries()) {
718
- let nodeParentValue = node[parentField];
719
- if (treeKeyEquals(nodeParentValue, parentId)) {
720
- sortedRows.push(node);
715
+ addChildNodes(parentId, nodeMap, parentField, sortedRows) {
716
+ // 遍历所有节点,找出当前父节点的子节点
717
+ for (let [id, node] of nodeMap.entries()) {
718
+ let nodeParentValue = node[parentField];
719
+ if (treeKeyEquals(nodeParentValue, parentId)) {
720
+ sortedRows.push(node);
721
721
  // 递归添加子节点的子节点
722
722
  this.addChildNodes(id, nodeMap, parentField, sortedRows);
723
723
  }
@@ -729,7 +729,7 @@ modules = {
729
729
  * @param {Boolean} defaultValueEnabled 是否应用字段默认值。
730
730
  * @returns {Array} 标准化后的新数组。
731
731
  */
732
- normalizeTableRows(rows, defaultValueEnabled = false) {
732
+ normalizeTableRows(rows, defaultValueEnabled = false) {
733
733
  if (!Array.isArray(rows) || !rows.length) {
734
734
  return rows || [];
735
735
  }
@@ -745,44 +745,44 @@ modules = {
745
745
  nextRow._X_ROW_KEY = "row_" + generateId();
746
746
  }
747
747
  return nextRow;
748
- });
749
- },
750
- /**
751
- * 树数据进入 VXE 前统一转换为唯一平铺模型;非树表保持原样。
752
- * @param {Array} rows 原始平铺或嵌套数据。
753
- * @param {Object} $grid 可选的 VXE Grid。
754
- * @returns {Array} 规范化后的行数组。
755
- */
756
- canonicalizeTreeRows(rows, $grid) {
757
- if (!this.widget.options.isTreeTable) {
758
- return rows || [];
759
- }
760
- let treeConfig = this.resolvedTreeConfig
761
- || $grid?.treeConfig
762
- || $grid?.params?.originOption?.config?.treeConfig;
763
- if (!treeConfig) {
764
- return rows || [];
765
- }
766
- let context = this.getTreeContext($grid);
767
- let nextRows = canonicalizeTreeInput(rows || [], context);
768
- this.registerTreeKeys(nextRows, context);
769
- return nextRows;
770
- },
748
+ });
749
+ },
750
+ /**
751
+ * 树数据进入 VXE 前统一转换为唯一平铺模型;非树表保持原样。
752
+ * @param {Array} rows 原始平铺或嵌套数据。
753
+ * @param {Object} $grid 可选的 VXE Grid。
754
+ * @returns {Array} 规范化后的行数组。
755
+ */
756
+ canonicalizeTreeRows(rows, $grid) {
757
+ if (!this.widget.options.isTreeTable) {
758
+ return rows || [];
759
+ }
760
+ let treeConfig = this.resolvedTreeConfig
761
+ || $grid?.treeConfig
762
+ || $grid?.params?.originOption?.config?.treeConfig;
763
+ if (!treeConfig) {
764
+ return rows || [];
765
+ }
766
+ let context = this.getTreeContext($grid);
767
+ let nextRows = canonicalizeTreeInput(rows || [], context);
768
+ this.registerTreeKeys(nextRows, context);
769
+ return nextRows;
770
+ },
771
771
  /**
772
772
  * 全量替换表格数据并重建行级 schema。
773
- * 注意:该操作会触发 vxe-grid 重载;编辑表仅在服务端正式身份回填后使用。
773
+ * 注意:该操作会触发 vxe-grid 重载;编辑表仅在服务端正式身份回填后使用。
774
774
  * @param {Array} val 新的完整数据集。
775
775
  */
776
- setValue(val) {
777
- // console.log("rows:",val);
778
- this.clearRowWidgets();
779
- if (this.widget.options.isTreeTable) {
780
- this.treeTemporaryKeyRegistry.clear();
781
- this.treeKnownFormalKeyRegistry.clear();
782
- }
783
- let rows = this.canonicalizeTreeRows(val || []);
784
- rows = this.normalizeTableRows(rows, false);
785
- this.registerTreeKeys(rows);
776
+ setValue(val) {
777
+ // console.log("rows:",val);
778
+ this.clearRowWidgets();
779
+ if (this.widget.options.isTreeTable) {
780
+ this.treeTemporaryKeyRegistry.clear();
781
+ this.treeKnownFormalKeyRegistry.clear();
782
+ }
783
+ let rows = this.canonicalizeTreeRows(val || []);
784
+ rows = this.normalizeTableRows(rows, false);
785
+ this.registerTreeKeys(rows);
786
786
  this.formModel[this.fieldKeyName] = rows;
787
787
  let $grid = this.getGridTable();
788
788
  this.fieldModel = rows;
@@ -1563,7 +1563,7 @@ modules = {
1563
1563
  }
1564
1564
  this.searchEvent();
1565
1565
  },
1566
- /** 查询区“重置”按钮入口。 */
1566
+ /** 查询区“刷新”按钮入口。 */
1567
1567
  handleResetClick() {
1568
1568
  if (this.widget.options.onResetTable) {
1569
1569
  this.handleCustomEvent(this.widget.options.onResetTable);
@@ -2140,11 +2140,11 @@ modules = {
2140
2140
  lazy: false,
2141
2141
  // 树表主键列显式声明为唯一事实源:vxe transform 建树(toArrayTree)
2142
2142
  // 以 rowField 为 key,父子关联代码一律经 getTreeFields 取列名,
2143
- // 不允许在别处写死 id,否则配置变化时两条链分叉、层级错乱。
2144
- rowField: "id",
2145
- children: "f_children",
2146
- mapChildren: "_X_ROW_CHILD",
2147
- hasChild: "f_has_child", // 设置是否有子节点标识
2143
+ // 不允许在别处写死 id,否则配置变化时两条链分叉、层级错乱。
2144
+ rowField: "id",
2145
+ children: "f_children",
2146
+ mapChildren: "_X_ROW_CHILD",
2147
+ hasChild: "f_has_child", // 设置是否有子节点标识
2148
2148
  parentField: "f_parent",
2149
2149
  transform: true,
2150
2150
  expandAll: true,
@@ -2153,18 +2153,18 @@ modules = {
2153
2153
  // 恢复展开状态(克隆保留该键),查询重载会重新生成键,互不干扰。
2154
2154
  reserve: true,
2155
2155
  loadMethod: ({ $table, row }) => {
2156
- // 模拟后台接口
2157
- let $grid = that.getGridTable();
2158
- let { rowField, parentField } = that.getTreeFields($grid);
2156
+ // 模拟后台接口
2157
+ let $grid = that.getGridTable();
2158
+ let { rowField, parentField } = that.getTreeFields($grid);
2159
2159
  return new Promise((resolve, reject) => {
2160
2160
  let treeParam = tableConfig?.treeNodeParam;
2161
2161
  let reqPram = {};
2162
2162
  if (treeParam) {
2163
2163
  reqPram = treeParam(row) || {};
2164
- }
2165
- Object.assign(reqPram, {
2166
- [parentField]: row[rowField],
2167
- });
2164
+ }
2165
+ Object.assign(reqPram, {
2166
+ [parentField]: row[rowField],
2167
+ });
2168
2168
  let accessParam = that.getAccessParam() || {};
2169
2169
  let defaultOption = that.getHttpConfigForUser();
2170
2170
  that.formHttp({
@@ -2178,112 +2178,112 @@ modules = {
2178
2178
  ...otherParam,
2179
2179
  ...accessParam,
2180
2180
  },
2181
- },
2182
- callback: (res) => {
2183
- if (res.type === "success") {
2184
- let rows;
2185
- try {
2186
- let treeContext = that.getTreeContext($grid);
2187
- rows = canonicalizeTreeInput(
2188
- res.objx?.records || res.objx || [],
2189
- treeContext
2190
- );
2191
- rows.forEach((childRow) => {
2192
- let parentValue = childRow[parentField];
2193
- if (isRootParent(parentValue)) {
2194
- childRow[parentField] = row[rowField];
2195
- return;
2196
- }
2197
- if (treeKeyEquals(parentValue, row[rowField])) {
2198
- childRow[parentField] = row[rowField];
2199
- return;
2200
- }
2201
- let batchParent = rows.find((item) =>
2202
- treeKeyEquals(item[rowField], parentValue)
2203
- );
2204
- if (!batchParent) {
2205
- throw new Error(
2206
- `懒加载节点 ${childRow[rowField]} 的父键 ${parentValue} 与请求父节点不一致`
2207
- );
2208
- }
2209
- childRow[parentField] = batchParent[rowField];
2210
- });
2211
-
2212
- let currentRows
2213
- = that.formModel[that.fieldKeyName] || [];
2214
- let currentCanonical = canonicalizeTreeInput(
2215
- currentRows,
2216
- treeContext
2217
- );
2218
- rows.forEach((childRow) => {
2219
- if (
2220
- currentCanonical.some((item) =>
2221
- treeKeyEquals(
2222
- item[rowField],
2223
- childRow[rowField]
2224
- )
2225
- )
2226
- ) {
2227
- throw new Error(
2228
- `懒加载节点 ${childRow[rowField]} 已存在`
2229
- );
2230
- }
2231
- });
2232
- let candidateRows = canonicalizeTreeInput(
2233
- currentRows.concat(rows),
2234
- treeContext
2235
- );
2236
- let newRowKeys = new Set(
2237
- rows.map((item) =>
2238
- normalizeTreeKey(item[rowField])
2239
- )
2240
- );
2241
- // canonicalizeTreeInput 已深克隆并移除 children /
2242
- // mapChildren。formModel 只保存这份扁平快照,不能重新
2243
- // 填回 VXE 的活动行对象,否则其内部树容器会泄漏到提交数据。
2244
- let canonicalRows = candidateRows;
2245
- rows = canonicalRows.filter((item) =>
2246
- newRowKeys.has(normalizeTreeKey(item[rowField]))
2247
- );
2248
- that.initExportFieldSchemaData(rows);
2249
- that.formModel[that.fieldKeyName] = canonicalRows;
2250
- that.registerTreeKeys(rows, treeContext);
2251
- } catch (error) {
2252
- reject(error);
2253
- return;
2254
- }
2255
-
2256
- if (res.objx?.records !== undefined) {
2257
- res.objx.records = rows;
2258
- } else {
2259
- res.objx = rows;
2260
- }
2261
-
2262
- let hasChildField = $grid.treeConfig.hasChild;
2263
- if (row[hasChildField] && !rows.length) {
2264
- row[hasChildField] = false;
2265
- }
2266
-
2267
- resolve(rows);
2268
- /* let treeCallback = tableConfig?.treeCallback;
2269
- if (treeCallback) {
2270
- that.$nextTick(() => {
2271
- setTimeout(function () {
2272
- treeCallback(rows);
2273
- }, 0);
2274
- });
2275
- } */
2276
- that.$nextTick(() => {
2277
- setTimeout(function () {
2278
- dataTableConfig.callback &&
2279
- dataTableConfig.callback(rows);
2280
- tableConfig.treeCallback &&
2281
- tableConfig.treeCallback(rows);
2282
- }, 0);
2283
- });
2284
- } else {
2285
- reject(res);
2286
- }
2181
+ },
2182
+ callback: (res) => {
2183
+ if (res.type === "success") {
2184
+ let rows;
2185
+ try {
2186
+ let treeContext = that.getTreeContext($grid);
2187
+ rows = canonicalizeTreeInput(
2188
+ res.objx?.records || res.objx || [],
2189
+ treeContext
2190
+ );
2191
+ rows.forEach((childRow) => {
2192
+ let parentValue = childRow[parentField];
2193
+ if (isRootParent(parentValue)) {
2194
+ childRow[parentField] = row[rowField];
2195
+ return;
2196
+ }
2197
+ if (treeKeyEquals(parentValue, row[rowField])) {
2198
+ childRow[parentField] = row[rowField];
2199
+ return;
2200
+ }
2201
+ let batchParent = rows.find((item) =>
2202
+ treeKeyEquals(item[rowField], parentValue)
2203
+ );
2204
+ if (!batchParent) {
2205
+ throw new Error(
2206
+ `懒加载节点 ${childRow[rowField]} 的父键 ${parentValue} 与请求父节点不一致`
2207
+ );
2208
+ }
2209
+ childRow[parentField] = batchParent[rowField];
2210
+ });
2211
+
2212
+ let currentRows
2213
+ = that.formModel[that.fieldKeyName] || [];
2214
+ let currentCanonical = canonicalizeTreeInput(
2215
+ currentRows,
2216
+ treeContext
2217
+ );
2218
+ rows.forEach((childRow) => {
2219
+ if (
2220
+ currentCanonical.some((item) =>
2221
+ treeKeyEquals(
2222
+ item[rowField],
2223
+ childRow[rowField]
2224
+ )
2225
+ )
2226
+ ) {
2227
+ throw new Error(
2228
+ `懒加载节点 ${childRow[rowField]} 已存在`
2229
+ );
2230
+ }
2231
+ });
2232
+ let candidateRows = canonicalizeTreeInput(
2233
+ currentRows.concat(rows),
2234
+ treeContext
2235
+ );
2236
+ let newRowKeys = new Set(
2237
+ rows.map((item) =>
2238
+ normalizeTreeKey(item[rowField])
2239
+ )
2240
+ );
2241
+ // canonicalizeTreeInput 已深克隆并移除 children /
2242
+ // mapChildren。formModel 只保存这份扁平快照,不能重新
2243
+ // 填回 VXE 的活动行对象,否则其内部树容器会泄漏到提交数据。
2244
+ let canonicalRows = candidateRows;
2245
+ rows = canonicalRows.filter((item) =>
2246
+ newRowKeys.has(normalizeTreeKey(item[rowField]))
2247
+ );
2248
+ that.initExportFieldSchemaData(rows);
2249
+ that.formModel[that.fieldKeyName] = canonicalRows;
2250
+ that.registerTreeKeys(rows, treeContext);
2251
+ } catch (error) {
2252
+ reject(error);
2253
+ return;
2254
+ }
2255
+
2256
+ if (res.objx?.records !== undefined) {
2257
+ res.objx.records = rows;
2258
+ } else {
2259
+ res.objx = rows;
2260
+ }
2261
+
2262
+ let hasChildField = $grid.treeConfig.hasChild;
2263
+ if (row[hasChildField] && !rows.length) {
2264
+ row[hasChildField] = false;
2265
+ }
2266
+
2267
+ resolve(rows);
2268
+ /* let treeCallback = tableConfig?.treeCallback;
2269
+ if (treeCallback) {
2270
+ that.$nextTick(() => {
2271
+ setTimeout(function () {
2272
+ treeCallback(rows);
2273
+ }, 0);
2274
+ });
2275
+ } */
2276
+ that.$nextTick(() => {
2277
+ setTimeout(function () {
2278
+ dataTableConfig.callback &&
2279
+ dataTableConfig.callback(rows);
2280
+ tableConfig.treeCallback &&
2281
+ tableConfig.treeCallback(rows);
2282
+ }, 0);
2283
+ });
2284
+ } else {
2285
+ reject(res);
2286
+ }
2287
2287
  },
2288
2288
  });
2289
2289
  });
@@ -2291,13 +2291,13 @@ modules = {
2291
2291
  };
2292
2292
  // 懒加载子节点的请求参数:父键/主键列名取自 treeConfig。
2293
2293
  // (此前误读 widget.options.parentField——该选项不存在,恒为 undefined,
2294
- // 会生成 {undefined: id} 的脏参数)
2295
- treeNodeParam = (row) => {
2296
- let fields = this.getTreeFields();
2297
- return {
2298
- [fields.parentField]: row[fields.rowField],
2299
- };
2300
- };
2294
+ // 会生成 {undefined: id} 的脏参数)
2295
+ treeNodeParam = (row) => {
2296
+ let fields = this.getTreeFields();
2297
+ return {
2298
+ [fields.parentField]: row[fields.rowField],
2299
+ };
2300
+ };
2301
2301
  }
2302
2302
  let editOpts = {};
2303
2303
  if (this.widget.options.isEditTable) {
@@ -2455,25 +2455,25 @@ modules = {
2455
2455
  // parentField/children 一并丢掉,使树主键链与建树逻辑分叉。
2456
2456
  // 不直接改 dataTableConfig——它是宿主经 $attrs 传入的对象。
2457
2457
  let hostTableConfig = dataTableConfig.config || {};
2458
- if (treeConfig && hostTableConfig.treeConfig) {
2459
- hostTableConfig = {
2460
- ...hostTableConfig,
2461
- treeConfig: { ...treeConfig, ...hostTableConfig.treeConfig },
2462
- };
2463
- }
2464
- let hostTreeIdentityConfig = {
2465
- ...(dataTableConfig.otherConfig?.treeIdentityConfig || {}),
2466
- ...(dataTableConfig.treeIdentityConfig || {}),
2467
- };
2468
- let hostTreeCopyConfig = {
2469
- ...(dataTableConfig.otherConfig?.treeCopyConfig || {}),
2470
- ...(dataTableConfig.treeCopyConfig || {}),
2471
- };
2472
- let hostTreeSubmitConfig = {
2473
- ...(dataTableConfig.otherConfig?.treeSubmitConfig || {}),
2474
- ...(dataTableConfig.treeSubmitConfig || {}),
2475
- };
2476
- tableOption = {
2458
+ if (treeConfig && hostTableConfig.treeConfig) {
2459
+ hostTableConfig = {
2460
+ ...hostTableConfig,
2461
+ treeConfig: { ...treeConfig, ...hostTableConfig.treeConfig },
2462
+ };
2463
+ }
2464
+ let hostTreeIdentityConfig = {
2465
+ ...(dataTableConfig.otherConfig?.treeIdentityConfig || {}),
2466
+ ...(dataTableConfig.treeIdentityConfig || {}),
2467
+ };
2468
+ let hostTreeCopyConfig = {
2469
+ ...(dataTableConfig.otherConfig?.treeCopyConfig || {}),
2470
+ ...(dataTableConfig.treeCopyConfig || {}),
2471
+ };
2472
+ let hostTreeSubmitConfig = {
2473
+ ...(dataTableConfig.otherConfig?.treeSubmitConfig || {}),
2474
+ ...(dataTableConfig.treeSubmitConfig || {}),
2475
+ };
2476
+ tableOption = {
2477
2477
  // vue: this,
2478
2478
  tableRef: tableRef,
2479
2479
  tableName: this.getGridTableName(),
@@ -2484,18 +2484,18 @@ modules = {
2484
2484
  isQueryAllPage,
2485
2485
  exportItemConfig,
2486
2486
  vform: true,
2487
- ...(this.getDynamicColumnSourceType() !== "none"
2488
- ? { visibleSync: false }
2489
- : {}),
2490
- ...(dataTableConfig.otherConfig || {}),
2491
- ...(isTreeTable
2492
- ? {
2493
- treeIdentityConfig: hostTreeIdentityConfig,
2494
- treeCopyConfig: hostTreeCopyConfig,
2495
- treeSubmitConfig: hostTreeSubmitConfig,
2496
- }
2497
- : {}),
2498
- config: {
2487
+ ...(this.getDynamicColumnSourceType() !== "none"
2488
+ ? { visibleSync: false }
2489
+ : {}),
2490
+ ...(dataTableConfig.otherConfig || {}),
2491
+ ...(isTreeTable
2492
+ ? {
2493
+ treeIdentityConfig: hostTreeIdentityConfig,
2494
+ treeCopyConfig: hostTreeCopyConfig,
2495
+ treeSubmitConfig: hostTreeSubmitConfig,
2496
+ }
2497
+ : {}),
2498
+ config: {
2499
2499
  height: height,
2500
2500
  showFooter,
2501
2501
  pagerConfig,
@@ -2649,26 +2649,26 @@ modules = {
2649
2649
  if (res.type === "success") {
2650
2650
  if (that.widget.options.isTreeTable) {
2651
2651
  if (rows.length > 0) {
2652
- let fullAllDataRowMap =
2653
- $grid.$refs.xTable.fullAllDataRowMap;
2654
- let treeFields = that.getTreeFields($grid);
2655
- let rowField = treeFields.rowField;
2656
- let parentField = treeFields.parentField;
2652
+ let fullAllDataRowMap =
2653
+ $grid.$refs.xTable.fullAllDataRowMap;
2654
+ let treeFields = that.getTreeFields($grid);
2655
+ let rowField = treeFields.rowField;
2656
+ let parentField = treeFields.parentField;
2657
2657
  let expandAll = $grid.treeConfig.expandAll || false;
2658
- let isLazy = $grid.treeConfig.lazy;
2659
- let expandIds = rows
2660
- .filter(
2661
- (item) =>
2662
- !isRootParent(item[parentField])
2663
- )
2664
- .map((item) => item[parentField]);
2665
- let expandRows = expandIds.length
2666
- ? rows.filter((item) =>
2667
- expandIds.some((id) =>
2668
- treeKeyEquals(id, item[rowField])
2669
- )
2670
- )
2671
- : [];
2658
+ let isLazy = $grid.treeConfig.lazy;
2659
+ let expandIds = rows
2660
+ .filter(
2661
+ (item) =>
2662
+ !isRootParent(item[parentField])
2663
+ )
2664
+ .map((item) => item[parentField]);
2665
+ let expandRows = expandIds.length
2666
+ ? rows.filter((item) =>
2667
+ expandIds.some((id) =>
2668
+ treeKeyEquals(id, item[rowField])
2669
+ )
2670
+ )
2671
+ : [];
2672
2672
  if (expandAll && expandRows.length) {
2673
2673
  that.$nextTick(() => {
2674
2674
  setTimeout(function () {
@@ -2691,16 +2691,16 @@ modules = {
2691
2691
  });
2692
2692
  }
2693
2693
 
2694
- let row = rows.find((item) =>
2695
- isRootParent(item[parentField])
2696
- );
2697
- if (
2698
- row
2699
- && (!expandAll
2700
- || !expandIds.some((id) =>
2701
- treeKeyEquals(id, row[rowField])
2702
- ))
2703
- ) {
2694
+ let row = rows.find((item) =>
2695
+ isRootParent(item[parentField])
2696
+ );
2697
+ if (
2698
+ row
2699
+ && (!expandAll
2700
+ || !expandIds.some((id) =>
2701
+ treeKeyEquals(id, row[rowField])
2702
+ ))
2703
+ ) {
2704
2704
  let hasChild = $grid.treeConfig.hasChild;
2705
2705
  if (row[hasChild]) {
2706
2706
  that.$nextTick(() => {
@@ -2748,63 +2748,63 @@ modules = {
2748
2748
  },
2749
2749
  },
2750
2750
  };
2751
- tableOption.callback = (rows) => {
2752
- // this.fieldModel = rows;
2753
- this.formModel[this.fieldKeyName] = rows;
2754
- };
2755
- }
2756
-
2757
- if (isTreeTable) {
2758
- tableOption.filterType = false;
2759
- }
2760
- tableOption = extendDeeply(tableOption, tableConfig);
2761
- if (isTreeTable) {
2762
- let mergedTreeConfig = {
2763
- ...(tableOption.config?.treeConfig || {}),
2764
- };
2765
- if (mergedTreeConfig.transform !== true) {
2766
- // eslint-disable-next-line no-console
2767
- console.error(
2768
- "[xform tree-table] transform 必须为 true,已恢复为规范平铺模式。"
2769
- );
2770
- }
2771
- mergedTreeConfig.transform = true;
2772
- let resolvedFields = resolveTreeFields(mergedTreeConfig, {
2773
- strict: true,
2774
- });
2775
- let resolvedTreeConfig = {
2776
- ...mergedTreeConfig,
2777
- rowField: resolvedFields.rowField,
2778
- parentField: resolvedFields.parentField,
2779
- children: resolvedFields.childrenField,
2780
- mapChildren: resolvedFields.mapChildrenField,
2781
- hasChild: resolvedFields.hasChildField,
2782
- lazy: resolvedFields.lazy,
2783
- transform: true,
2784
- };
2785
- let resolvedIdentityConfig = resolveTreeIdentityConfig(
2786
- resolvedTreeConfig,
2787
- tableOption.treeIdentityConfig || {}
2788
- );
2789
- let resolvedCopyConfig = resolveTreeCopyConfig(
2790
- resolvedFields,
2791
- resolvedIdentityConfig,
2792
- tableOption.treeCopyConfig || {}
2793
- );
2794
- let resolvedSubmitConfig = resolveTreeSubmitConfig(
2795
- tableOption.treeSubmitConfig || {}
2796
- );
2797
-
2798
- this.resolvedTreeConfig = resolvedTreeConfig;
2799
- this.resolvedTreeIdentityConfig = resolvedIdentityConfig;
2800
- this.resolvedTreeCopyConfig = resolvedCopyConfig;
2801
- this.resolvedTreeSubmitConfig = resolvedSubmitConfig;
2802
- tableOption.config.treeConfig = resolvedTreeConfig;
2803
- tableOption.treeIdentityConfig = resolvedIdentityConfig;
2804
- tableOption.treeCopyConfig = resolvedCopyConfig;
2805
- tableOption.treeSubmitConfig = resolvedSubmitConfig;
2806
- }
2807
- /* if (this.widget.options.tableConfig) {
2751
+ tableOption.callback = (rows) => {
2752
+ // this.fieldModel = rows;
2753
+ this.formModel[this.fieldKeyName] = rows;
2754
+ };
2755
+ }
2756
+
2757
+ if (isTreeTable) {
2758
+ tableOption.filterType = false;
2759
+ }
2760
+ tableOption = extendDeeply(tableOption, tableConfig);
2761
+ if (isTreeTable) {
2762
+ let mergedTreeConfig = {
2763
+ ...(tableOption.config?.treeConfig || {}),
2764
+ };
2765
+ if (mergedTreeConfig.transform !== true) {
2766
+ // eslint-disable-next-line no-console
2767
+ console.error(
2768
+ "[xform tree-table] transform 必须为 true,已恢复为规范平铺模式。"
2769
+ );
2770
+ }
2771
+ mergedTreeConfig.transform = true;
2772
+ let resolvedFields = resolveTreeFields(mergedTreeConfig, {
2773
+ strict: true,
2774
+ });
2775
+ let resolvedTreeConfig = {
2776
+ ...mergedTreeConfig,
2777
+ rowField: resolvedFields.rowField,
2778
+ parentField: resolvedFields.parentField,
2779
+ children: resolvedFields.childrenField,
2780
+ mapChildren: resolvedFields.mapChildrenField,
2781
+ hasChild: resolvedFields.hasChildField,
2782
+ lazy: resolvedFields.lazy,
2783
+ transform: true,
2784
+ };
2785
+ let resolvedIdentityConfig = resolveTreeIdentityConfig(
2786
+ resolvedTreeConfig,
2787
+ tableOption.treeIdentityConfig || {}
2788
+ );
2789
+ let resolvedCopyConfig = resolveTreeCopyConfig(
2790
+ resolvedFields,
2791
+ resolvedIdentityConfig,
2792
+ tableOption.treeCopyConfig || {}
2793
+ );
2794
+ let resolvedSubmitConfig = resolveTreeSubmitConfig(
2795
+ tableOption.treeSubmitConfig || {}
2796
+ );
2797
+
2798
+ this.resolvedTreeConfig = resolvedTreeConfig;
2799
+ this.resolvedTreeIdentityConfig = resolvedIdentityConfig;
2800
+ this.resolvedTreeCopyConfig = resolvedCopyConfig;
2801
+ this.resolvedTreeSubmitConfig = resolvedSubmitConfig;
2802
+ tableOption.config.treeConfig = resolvedTreeConfig;
2803
+ tableOption.treeIdentityConfig = resolvedIdentityConfig;
2804
+ tableOption.treeCopyConfig = resolvedCopyConfig;
2805
+ tableOption.treeSubmitConfig = resolvedSubmitConfig;
2806
+ }
2807
+ /* if (this.widget.options.tableConfig) {
2808
2808
  let tableConfig =
2809
2809
  this.handleCustomEvent(this.widget.options.tableConfig) || {};
2810
2810
  tableOption = extendDeeply(tableOption, tableConfig);
@@ -2862,15 +2862,15 @@ modules = {
2862
2862
  if (mergeRowEnabled && unikey) {
2863
2863
  tableOption.config.spanMethod = this.mergeRowMethod;
2864
2864
  }
2865
- this.$vxeTableUtil.initVxeTable(tableOption).then((opts) => {
2866
- this.vxeOption = opts;
2867
- this.applyCompareRowClassName();
2868
- if (isTreeTable) {
2869
- this.$nextTick(() => {
2870
- this.validateResolvedTreeConfig(this.getGridTable());
2871
- });
2872
- }
2873
- // 列已就绪,补建 created 阶段被延后的行 schema
2865
+ this.$vxeTableUtil.initVxeTable(tableOption).then((opts) => {
2866
+ this.vxeOption = opts;
2867
+ this.applyCompareRowClassName();
2868
+ if (isTreeTable) {
2869
+ this.$nextTick(() => {
2870
+ this.validateResolvedTreeConfig(this.getGridTable());
2871
+ });
2872
+ }
2873
+ // 列已就绪,补建 created 阶段被延后的行 schema
2874
2874
  this.flushPendingSchemaInit();
2875
2875
  this.tryAutoLoadDynamicColumns().finally(() => {
2876
2876
  if (!isQueryTable) {
@@ -3550,411 +3550,411 @@ modules = {
3550
3550
  let $grid = this.$refs[this.getTableRef()];
3551
3551
  return $grid;
3552
3552
  },
3553
- /**
3554
- * 树表字段统一读取口。rowField 是树关系键,idField 仅作为旧调用方别名保留。
3555
- * @param {Object} $grid VXE Grid 实例;缺省取当前组件的表格。
3556
- * @returns {Object} 解析后的树字段以及兼容别名 idField。
3557
- */
3558
- getTreeFields($grid) {
3559
- let grid = $grid || this.getGridTable();
3560
- let treeConfig = this.resolvedTreeConfig
3561
- || grid?.treeConfig
3562
- || grid?.params?.originOption?.config?.treeConfig
3563
- || {};
3564
- let fields = resolveTreeFields(treeConfig, {
3565
- strict: !!this.widget.options.isTreeTable,
3566
- });
3567
- return {
3568
- ...fields,
3569
- // 兼容存量调用;新增代码只使用 rowField。
3570
- idField: fields.rowField,
3571
- };
3572
- },
3573
- /**
3574
- * 汇总当前树表纯函数所需上下文。挂载后优先读取 originOption 中的已解析配置。
3575
- * @param {Object} $grid 可选的 VXE Grid。
3576
- * @param {Object} extra 调用场景附加上下文。
3577
- * @returns {Object} 树工具上下文。
3578
- */
3579
- getTreeContext($grid, extra) {
3580
- let grid = $grid || this.getGridTable();
3581
- let originOption = grid?.params?.originOption || grid?.originOption || {};
3582
- let treeConfig = this.resolvedTreeConfig
3583
- || grid?.treeConfig
3584
- || originOption.config?.treeConfig
3585
- || {};
3586
- let fields = this.getTreeFields(grid);
3587
- let identityConfig = this.resolvedTreeIdentityConfig
3588
- || originOption.treeIdentityConfig
3589
- || resolveTreeIdentityConfig(treeConfig, {});
3590
- let copyConfig = this.resolvedTreeCopyConfig
3591
- || originOption.treeCopyConfig
3592
- || resolveTreeCopyConfig(fields, identityConfig, {});
3593
- let submitConfig = this.resolvedTreeSubmitConfig
3594
- || originOption.treeSubmitConfig
3595
- || resolveTreeSubmitConfig({});
3596
- return {
3597
- treeConfig,
3598
- fields,
3599
- identityConfig,
3600
- copyConfig,
3601
- submitConfig,
3602
- temporaryKeyRegistry: this.treeTemporaryKeyRegistry,
3603
- knownFormalKeyRegistry: this.treeKnownFormalKeyRegistry,
3604
- ...(extra || {}),
3605
- };
3606
- },
3607
- /**
3608
- * VXE 挂载后确认实际字段没有偏离初始化阶段唯一解析结果。
3609
- * @param {Object} $grid VXE Grid。
3610
- * @returns {Boolean} 校验通过或尚未挂载。
3611
- */
3612
- validateResolvedTreeConfig($grid) {
3613
- if (!$grid || !this.resolvedTreeConfig) {
3614
- return true;
3615
- }
3616
- let actual = resolveTreeFields($grid.treeConfig || {}, {
3617
- strict: true,
3618
- });
3619
- let expected = resolveTreeFields(this.resolvedTreeConfig, {
3620
- strict: true,
3621
- });
3622
- let fieldNames = [
3623
- "rowField",
3624
- "parentField",
3625
- "childrenField",
3626
- "mapChildrenField",
3627
- "hasChildField",
3628
- ];
3629
- let mismatch = fieldNames.filter(
3630
- (name) => actual[name] !== expected[name]
3631
- );
3632
- if (
3633
- mismatch.length
3634
- || $grid.treeConfig?.transform !== true
3635
- ) {
3636
- throw new Error(
3637
- `[xform tree-table] VXE 树配置与 resolvedTreeConfig 不一致:${mismatch.join(
3638
- ", "
3639
- ) || "transform"}`
3640
- );
3641
- }
3642
- return true;
3643
- },
3644
- /**
3645
- * 记录当前表已知的临时键和正式键,供单行保存的外部父引用守卫使用。
3646
- * @param {Array} rows 规范平铺行。
3647
- * @param {Object} context 可选树上下文。
3648
- */
3649
- registerTreeKeys(rows, context) {
3650
- if (!this.widget.options.isTreeTable || !Array.isArray(rows)) {
3651
- return;
3652
- }
3653
- let grid = this.getGridTable();
3654
- if (
3655
- !context
3656
- && !grid?.treeConfig
3657
- && !this.resolvedTreeConfig
3658
- ) {
3659
- return;
3660
- }
3661
- let treeContext = context || this.getTreeContext();
3662
- let { rowField, parentField } = treeContext.fields;
3663
- rows.forEach((row) => {
3664
- if (!row) return;
3665
- let rowValue = row[rowField];
3666
- if (isTemporaryTreeKey(row, rowField, treeContext.identityConfig)) {
3667
- this.treeTemporaryKeyRegistry.add(rowValue);
3668
- } else if (!isRootParent(rowValue)) {
3669
- this.treeKnownFormalKeyRegistry.add(rowValue);
3670
- }
3671
- let parentValue = row[parentField];
3672
- if (
3673
- !isRootParent(parentValue)
3674
- && !this.registryHasTreeKey(
3675
- this.treeTemporaryKeyRegistry,
3676
- parentValue
3677
- )
3678
- ) {
3679
- this.treeKnownFormalKeyRegistry.add(parentValue);
3680
- }
3681
- });
3682
- },
3683
- /** @param {Set} registry 键注册表。@param {*} value 待匹配值。 */
3684
- registryHasTreeKey(registry, value) {
3685
- if (!registry || isRootParent(value)) {
3686
- return false;
3687
- }
3688
- return Array.from(registry).some((item) =>
3689
- treeKeyEquals(item, value)
3690
- );
3691
- },
3692
- /** @param {Set} registry 键注册表。@param {*} value 待注销值。 */
3693
- unregisterTreeKey(registry, value) {
3694
- if (!registry) return;
3695
- Array.from(registry).forEach((item) => {
3696
- if (treeKeyEquals(item, value)) {
3697
- registry.delete(item);
3698
- }
3699
- });
3700
- },
3701
- /**
3702
- * 确保新增树行具有有效关系键;只有实际生成临时值时才写临时标记。
3703
- * @param {Object} row 新行。
3704
- * @param {Object} context 树上下文。
3705
- * @returns {*} 最终 rowField 值。
3706
- */
3707
- ensureNewTreeRowKey(row, context) {
3708
- let treeContext = context || this.getTreeContext();
3709
- let rowField = treeContext.fields.rowField;
3710
- let rowValue = row[rowField];
3711
- let isMarkedTemporary = isTemporaryTreeKey(
3712
- row,
3713
- rowField,
3714
- treeContext.identityConfig
3715
- );
3716
- if (
3717
- isRootParent(rowValue)
3718
- || (rowField === treeContext.identityConfig.physicalIdField
3719
- && !isMarkedTemporary)
3720
- ) {
3721
- rowValue = makeTreeTempKey();
3722
- markTemporaryTreeKey(row, rowField, rowValue);
3723
- } else if (
3724
- row._X_TREE_TEMP_KEY != null
3725
- && !treeKeyEquals(row._X_TREE_TEMP_KEY, rowValue)
3726
- ) {
3727
- delete row._X_TREE_TEMP_KEY;
3728
- }
3729
- if (isTemporaryTreeKey(row, rowField, treeContext.identityConfig)) {
3730
- this.treeTemporaryKeyRegistry.add(rowValue);
3731
- } else {
3732
- this.treeKnownFormalKeyRegistry.add(rowValue);
3733
- }
3734
- return rowValue;
3735
- },
3736
- /**
3737
- * 在修改当前表数组前验证新增行不会造成树键冲突、父边冲突或环。
3738
- * @param {Array} rows 待插入行。
3739
- * @param {Object} context 树上下文。
3740
- */
3741
- validateNewTreeRows(rows, context) {
3742
- let tableRows = this.formModel[this.fieldKeyName] || [];
3743
- canonicalizeTreeInput(tableRows.concat(rows || []), context);
3744
- },
3745
- /**
3746
- * 树关系键变更唯一入口。负责持久化策略、唯一性、临时标记和直接子引用同步。
3747
- * 业务选择器可直接 await 调用;单行保存也会用编辑快照兜底调用。
3748
- * @param {Object} row 当前行。
3749
- * @param {*} newValue 新树键。
3750
- * @param {Object} options 可传 oldValue、$grid 和 reason。
3751
- * @returns {Promise<Object>} 变更结果。
3752
- */
3753
- async changeTreeRowField(row, newValue, options = {}) {
3754
- if (!this.widget.options.isTreeTable || !row) {
3755
- return { changed: false, transactional: false };
3756
- }
3757
- let $grid = options.$grid || this.getGridTable();
3758
- let context = this.getTreeContext($grid);
3759
- let { rowField } = context.fields;
3760
- let hasExplicitOldValue = Object.prototype.hasOwnProperty.call(
3761
- options,
3762
- "oldValue"
3763
- );
3764
- let oldValue = hasExplicitOldValue
3765
- ? options.oldValue
3766
- : row[rowField];
3767
- if (
3768
- !hasExplicitOldValue
3769
- && row._X_TREE_TEMP_KEY != null
3770
- && !treeKeyEquals(row._X_TREE_TEMP_KEY, newValue)
3771
- ) {
3772
- oldValue = row._X_TREE_TEMP_KEY;
3773
- }
3774
- let rejectChange = async (messageOrError) => {
3775
- row[rowField] = oldValue;
3776
- if ($grid?.updateData) {
3777
- await $grid.updateData();
3778
- }
3779
- throw messageOrError instanceof Error
3780
- ? messageOrError
3781
- : new Error(messageOrError);
3782
- };
3783
- if (isRootParent(newValue)) {
3784
- return rejectChange(`${rowField} 不能为空或根哨兵值。`);
3785
- }
3786
- if (treeKeyEquals(oldValue, newValue)) {
3787
- row[rowField] = newValue;
3788
- if (
3789
- row._X_TREE_TEMP_KEY != null
3790
- && !treeKeyEquals(row._X_TREE_TEMP_KEY, newValue)
3791
- ) {
3792
- delete row._X_TREE_TEMP_KEY;
3793
- }
3794
- return { changed: false, transactional: false };
3795
- }
3796
-
3797
- let tableRows = this.formModel[this.fieldKeyName] || [];
3798
- let conflict = tableRows.some(
3799
- (item) =>
3800
- item !== row
3801
- && treeKeyEquals(item?.[rowField], newValue)
3802
- );
3803
- if (conflict) {
3804
- return rejectChange(`树键 ${newValue} 已存在。`);
3805
- }
3806
-
3807
- let physicalIdField = context.identityConfig.physicalIdField;
3808
- let physicalId = row[physicalIdField];
3809
- let persisted = physicalId !== undefined
3810
- && physicalId !== null
3811
- && physicalId !== "";
3812
- if (
3813
- physicalIdField === rowField
3814
- && treeKeyEquals(physicalId, oldValue)
3815
- && isTemporaryTreeKey(
3816
- {
3817
- ...row,
3818
- [rowField]: oldValue,
3819
- },
3820
- rowField,
3821
- context.identityConfig
3822
- )
3823
- ) {
3824
- persisted = false;
3825
- }
3826
-
3827
- let transactional = false;
3828
- if (physicalIdField === rowField) {
3829
- return rejectChange(
3830
- persisted
3831
- ? "已持久化的物理主键不可修改。"
3832
- : "物理主键兼作树键时只能由保存响应回填正式值。"
3833
- );
3834
- }
3835
- if (persisted) {
3836
- if (
3837
- context.identityConfig
3838
- .persistedRowFieldChangeStrategy !== "transactional"
3839
- ) {
3840
- return rejectChange("已持久化节点的树键不允许直接修改。");
3841
- }
3842
- let changeHandler = context.identityConfig.changePersistedRowField;
3843
- try {
3844
- let result = await changeHandler({
3845
- row: this.$baseLodash.cloneDeep(row),
3846
- oldValue,
3847
- newValue,
3848
- fields: context.fields,
3849
- reason: options.reason || "edit",
3850
- });
3851
- if (result === false) {
3852
- throw new Error("树键事务更新未成功。");
3853
- }
3854
- transactional = true;
3855
- } catch (error) {
3856
- return rejectChange(error);
3857
- }
3858
- }
3859
-
3860
- row[rowField] = newValue;
3861
- syncTreeKeyReferences(tableRows, oldValue, newValue, {
3862
- fields: context.fields,
3863
- });
3864
- delete row._X_TREE_TEMP_KEY;
3865
- this.unregisterTreeKey(
3866
- this.treeTemporaryKeyRegistry,
3867
- oldValue
3868
- );
3869
- this.treeKnownFormalKeyRegistry.add(newValue);
3870
- if ($grid?.updateData) {
3871
- await $grid.updateData();
3872
- }
3873
- if (transactional) {
3874
- let formRef = this.getFormRef();
3875
- let reloadMethod
3876
- = typeof formRef?.reloadSavedFormData === "function"
3877
- ? formRef.reloadSavedFormData
3878
- : formRef?.reloadForm;
3879
- if (typeof reloadMethod === "function") {
3880
- try {
3881
- await reloadMethod.call(formRef);
3882
- } catch (error) {
3883
- let reloadError = error instanceof Error
3884
- ? error
3885
- : new Error(error || "树表重新加载失败。");
3886
- reloadError.treeReloadFailed = true;
3887
- throw reloadError;
3888
- }
3889
- }
3890
- }
3891
- return { changed: true, transactional };
3892
- },
3893
- /**
3894
- * 单行保存前确认父节点已持久化,或外部父键已被登记为正式键。
3895
- * @param {Object} row 当前行。
3896
- * @param {Object} $grid VXE Grid。
3897
- * @returns {String|null} 阻断原因。
3898
- */
3899
- getTreeSingleSaveBlockReason(row, $grid) {
3900
- if (!this.widget.options.isTreeTable || !row) {
3901
- return null;
3902
- }
3903
- let context = this.getTreeContext($grid);
3904
- let { rowField, parentField } = context.fields;
3905
- let parentValue = row[parentField];
3906
- if (isRootParent(parentValue)) {
3907
- return null;
3908
- }
3909
- let tableRows = this.formModel[this.fieldKeyName] || [];
3910
- let parentRow = tableRows.find(
3911
- (item) =>
3912
- item && treeKeyEquals(item[rowField], parentValue)
3913
- );
3914
- if (parentRow) {
3915
- let physicalId = parentRow[context.identityConfig.physicalIdField];
3916
- if (
3917
- physicalId == null
3918
- || physicalId === ""
3919
- || isTemporaryTreeKey(
3920
- parentRow,
3921
- rowField,
3922
- context.identityConfig
3923
- )
3924
- ) {
3925
- return "请先保存父节点,再保存当前子节点。";
3926
- }
3927
- return null;
3928
- }
3929
- if (
3930
- this.registryHasTreeKey(
3931
- this.treeTemporaryKeyRegistry,
3932
- parentValue
3933
- )
3934
- ) {
3935
- return "当前行引用了尚未保存的父节点,请先保存父节点。";
3936
- }
3937
- if (
3938
- this.registryHasTreeKey(
3939
- this.treeKnownFormalKeyRegistry,
3940
- parentValue
3941
- )
3942
- ) {
3943
- return null;
3944
- }
3945
- let validator = context.identityConfig.validateExternalParentKey;
3946
- if (typeof validator === "function") {
3947
- let result = validator(parentValue, row, "single");
3948
- if (result && typeof result.then === "function") {
3949
- return "外部父节点校验必须同步完成。";
3950
- }
3951
- if (result === true) {
3952
- this.treeKnownFormalKeyRegistry.add(parentValue);
3953
- return null;
3954
- }
3955
- }
3956
- return "无法确认当前行的父节点已保存,请先保存或加载父节点。";
3957
- },
3553
+ /**
3554
+ * 树表字段统一读取口。rowField 是树关系键,idField 仅作为旧调用方别名保留。
3555
+ * @param {Object} $grid VXE Grid 实例;缺省取当前组件的表格。
3556
+ * @returns {Object} 解析后的树字段以及兼容别名 idField。
3557
+ */
3558
+ getTreeFields($grid) {
3559
+ let grid = $grid || this.getGridTable();
3560
+ let treeConfig = this.resolvedTreeConfig
3561
+ || grid?.treeConfig
3562
+ || grid?.params?.originOption?.config?.treeConfig
3563
+ || {};
3564
+ let fields = resolveTreeFields(treeConfig, {
3565
+ strict: !!this.widget.options.isTreeTable,
3566
+ });
3567
+ return {
3568
+ ...fields,
3569
+ // 兼容存量调用;新增代码只使用 rowField。
3570
+ idField: fields.rowField,
3571
+ };
3572
+ },
3573
+ /**
3574
+ * 汇总当前树表纯函数所需上下文。挂载后优先读取 originOption 中的已解析配置。
3575
+ * @param {Object} $grid 可选的 VXE Grid。
3576
+ * @param {Object} extra 调用场景附加上下文。
3577
+ * @returns {Object} 树工具上下文。
3578
+ */
3579
+ getTreeContext($grid, extra) {
3580
+ let grid = $grid || this.getGridTable();
3581
+ let originOption = grid?.params?.originOption || grid?.originOption || {};
3582
+ let treeConfig = this.resolvedTreeConfig
3583
+ || grid?.treeConfig
3584
+ || originOption.config?.treeConfig
3585
+ || {};
3586
+ let fields = this.getTreeFields(grid);
3587
+ let identityConfig = this.resolvedTreeIdentityConfig
3588
+ || originOption.treeIdentityConfig
3589
+ || resolveTreeIdentityConfig(treeConfig, {});
3590
+ let copyConfig = this.resolvedTreeCopyConfig
3591
+ || originOption.treeCopyConfig
3592
+ || resolveTreeCopyConfig(fields, identityConfig, {});
3593
+ let submitConfig = this.resolvedTreeSubmitConfig
3594
+ || originOption.treeSubmitConfig
3595
+ || resolveTreeSubmitConfig({});
3596
+ return {
3597
+ treeConfig,
3598
+ fields,
3599
+ identityConfig,
3600
+ copyConfig,
3601
+ submitConfig,
3602
+ temporaryKeyRegistry: this.treeTemporaryKeyRegistry,
3603
+ knownFormalKeyRegistry: this.treeKnownFormalKeyRegistry,
3604
+ ...(extra || {}),
3605
+ };
3606
+ },
3607
+ /**
3608
+ * VXE 挂载后确认实际字段没有偏离初始化阶段唯一解析结果。
3609
+ * @param {Object} $grid VXE Grid。
3610
+ * @returns {Boolean} 校验通过或尚未挂载。
3611
+ */
3612
+ validateResolvedTreeConfig($grid) {
3613
+ if (!$grid || !this.resolvedTreeConfig) {
3614
+ return true;
3615
+ }
3616
+ let actual = resolveTreeFields($grid.treeConfig || {}, {
3617
+ strict: true,
3618
+ });
3619
+ let expected = resolveTreeFields(this.resolvedTreeConfig, {
3620
+ strict: true,
3621
+ });
3622
+ let fieldNames = [
3623
+ "rowField",
3624
+ "parentField",
3625
+ "childrenField",
3626
+ "mapChildrenField",
3627
+ "hasChildField",
3628
+ ];
3629
+ let mismatch = fieldNames.filter(
3630
+ (name) => actual[name] !== expected[name]
3631
+ );
3632
+ if (
3633
+ mismatch.length
3634
+ || $grid.treeConfig?.transform !== true
3635
+ ) {
3636
+ throw new Error(
3637
+ `[xform tree-table] VXE 树配置与 resolvedTreeConfig 不一致:${mismatch.join(
3638
+ ", "
3639
+ ) || "transform"}`
3640
+ );
3641
+ }
3642
+ return true;
3643
+ },
3644
+ /**
3645
+ * 记录当前表已知的临时键和正式键,供单行保存的外部父引用守卫使用。
3646
+ * @param {Array} rows 规范平铺行。
3647
+ * @param {Object} context 可选树上下文。
3648
+ */
3649
+ registerTreeKeys(rows, context) {
3650
+ if (!this.widget.options.isTreeTable || !Array.isArray(rows)) {
3651
+ return;
3652
+ }
3653
+ let grid = this.getGridTable();
3654
+ if (
3655
+ !context
3656
+ && !grid?.treeConfig
3657
+ && !this.resolvedTreeConfig
3658
+ ) {
3659
+ return;
3660
+ }
3661
+ let treeContext = context || this.getTreeContext();
3662
+ let { rowField, parentField } = treeContext.fields;
3663
+ rows.forEach((row) => {
3664
+ if (!row) return;
3665
+ let rowValue = row[rowField];
3666
+ if (isTemporaryTreeKey(row, rowField, treeContext.identityConfig)) {
3667
+ this.treeTemporaryKeyRegistry.add(rowValue);
3668
+ } else if (!isRootParent(rowValue)) {
3669
+ this.treeKnownFormalKeyRegistry.add(rowValue);
3670
+ }
3671
+ let parentValue = row[parentField];
3672
+ if (
3673
+ !isRootParent(parentValue)
3674
+ && !this.registryHasTreeKey(
3675
+ this.treeTemporaryKeyRegistry,
3676
+ parentValue
3677
+ )
3678
+ ) {
3679
+ this.treeKnownFormalKeyRegistry.add(parentValue);
3680
+ }
3681
+ });
3682
+ },
3683
+ /** @param {Set} registry 键注册表。@param {*} value 待匹配值。 */
3684
+ registryHasTreeKey(registry, value) {
3685
+ if (!registry || isRootParent(value)) {
3686
+ return false;
3687
+ }
3688
+ return Array.from(registry).some((item) =>
3689
+ treeKeyEquals(item, value)
3690
+ );
3691
+ },
3692
+ /** @param {Set} registry 键注册表。@param {*} value 待注销值。 */
3693
+ unregisterTreeKey(registry, value) {
3694
+ if (!registry) return;
3695
+ Array.from(registry).forEach((item) => {
3696
+ if (treeKeyEquals(item, value)) {
3697
+ registry.delete(item);
3698
+ }
3699
+ });
3700
+ },
3701
+ /**
3702
+ * 确保新增树行具有有效关系键;只有实际生成临时值时才写临时标记。
3703
+ * @param {Object} row 新行。
3704
+ * @param {Object} context 树上下文。
3705
+ * @returns {*} 最终 rowField 值。
3706
+ */
3707
+ ensureNewTreeRowKey(row, context) {
3708
+ let treeContext = context || this.getTreeContext();
3709
+ let rowField = treeContext.fields.rowField;
3710
+ let rowValue = row[rowField];
3711
+ let isMarkedTemporary = isTemporaryTreeKey(
3712
+ row,
3713
+ rowField,
3714
+ treeContext.identityConfig
3715
+ );
3716
+ if (
3717
+ isRootParent(rowValue)
3718
+ || (rowField === treeContext.identityConfig.physicalIdField
3719
+ && !isMarkedTemporary)
3720
+ ) {
3721
+ rowValue = makeTreeTempKey();
3722
+ markTemporaryTreeKey(row, rowField, rowValue);
3723
+ } else if (
3724
+ row._X_TREE_TEMP_KEY != null
3725
+ && !treeKeyEquals(row._X_TREE_TEMP_KEY, rowValue)
3726
+ ) {
3727
+ delete row._X_TREE_TEMP_KEY;
3728
+ }
3729
+ if (isTemporaryTreeKey(row, rowField, treeContext.identityConfig)) {
3730
+ this.treeTemporaryKeyRegistry.add(rowValue);
3731
+ } else {
3732
+ this.treeKnownFormalKeyRegistry.add(rowValue);
3733
+ }
3734
+ return rowValue;
3735
+ },
3736
+ /**
3737
+ * 在修改当前表数组前验证新增行不会造成树键冲突、父边冲突或环。
3738
+ * @param {Array} rows 待插入行。
3739
+ * @param {Object} context 树上下文。
3740
+ */
3741
+ validateNewTreeRows(rows, context) {
3742
+ let tableRows = this.formModel[this.fieldKeyName] || [];
3743
+ canonicalizeTreeInput(tableRows.concat(rows || []), context);
3744
+ },
3745
+ /**
3746
+ * 树关系键变更唯一入口。负责持久化策略、唯一性、临时标记和直接子引用同步。
3747
+ * 业务选择器可直接 await 调用;单行保存也会用编辑快照兜底调用。
3748
+ * @param {Object} row 当前行。
3749
+ * @param {*} newValue 新树键。
3750
+ * @param {Object} options 可传 oldValue、$grid 和 reason。
3751
+ * @returns {Promise<Object>} 变更结果。
3752
+ */
3753
+ async changeTreeRowField(row, newValue, options = {}) {
3754
+ if (!this.widget.options.isTreeTable || !row) {
3755
+ return { changed: false, transactional: false };
3756
+ }
3757
+ let $grid = options.$grid || this.getGridTable();
3758
+ let context = this.getTreeContext($grid);
3759
+ let { rowField } = context.fields;
3760
+ let hasExplicitOldValue = Object.prototype.hasOwnProperty.call(
3761
+ options,
3762
+ "oldValue"
3763
+ );
3764
+ let oldValue = hasExplicitOldValue
3765
+ ? options.oldValue
3766
+ : row[rowField];
3767
+ if (
3768
+ !hasExplicitOldValue
3769
+ && row._X_TREE_TEMP_KEY != null
3770
+ && !treeKeyEquals(row._X_TREE_TEMP_KEY, newValue)
3771
+ ) {
3772
+ oldValue = row._X_TREE_TEMP_KEY;
3773
+ }
3774
+ let rejectChange = async (messageOrError) => {
3775
+ row[rowField] = oldValue;
3776
+ if ($grid?.updateData) {
3777
+ await $grid.updateData();
3778
+ }
3779
+ throw messageOrError instanceof Error
3780
+ ? messageOrError
3781
+ : new Error(messageOrError);
3782
+ };
3783
+ if (isRootParent(newValue)) {
3784
+ return rejectChange(`${rowField} 不能为空或根哨兵值。`);
3785
+ }
3786
+ if (treeKeyEquals(oldValue, newValue)) {
3787
+ row[rowField] = newValue;
3788
+ if (
3789
+ row._X_TREE_TEMP_KEY != null
3790
+ && !treeKeyEquals(row._X_TREE_TEMP_KEY, newValue)
3791
+ ) {
3792
+ delete row._X_TREE_TEMP_KEY;
3793
+ }
3794
+ return { changed: false, transactional: false };
3795
+ }
3796
+
3797
+ let tableRows = this.formModel[this.fieldKeyName] || [];
3798
+ let conflict = tableRows.some(
3799
+ (item) =>
3800
+ item !== row
3801
+ && treeKeyEquals(item?.[rowField], newValue)
3802
+ );
3803
+ if (conflict) {
3804
+ return rejectChange(`树键 ${newValue} 已存在。`);
3805
+ }
3806
+
3807
+ let physicalIdField = context.identityConfig.physicalIdField;
3808
+ let physicalId = row[physicalIdField];
3809
+ let persisted = physicalId !== undefined
3810
+ && physicalId !== null
3811
+ && physicalId !== "";
3812
+ if (
3813
+ physicalIdField === rowField
3814
+ && treeKeyEquals(physicalId, oldValue)
3815
+ && isTemporaryTreeKey(
3816
+ {
3817
+ ...row,
3818
+ [rowField]: oldValue,
3819
+ },
3820
+ rowField,
3821
+ context.identityConfig
3822
+ )
3823
+ ) {
3824
+ persisted = false;
3825
+ }
3826
+
3827
+ let transactional = false;
3828
+ if (physicalIdField === rowField) {
3829
+ return rejectChange(
3830
+ persisted
3831
+ ? "已持久化的物理主键不可修改。"
3832
+ : "物理主键兼作树键时只能由保存响应回填正式值。"
3833
+ );
3834
+ }
3835
+ if (persisted) {
3836
+ if (
3837
+ context.identityConfig
3838
+ .persistedRowFieldChangeStrategy !== "transactional"
3839
+ ) {
3840
+ return rejectChange("已持久化节点的树键不允许直接修改。");
3841
+ }
3842
+ let changeHandler = context.identityConfig.changePersistedRowField;
3843
+ try {
3844
+ let result = await changeHandler({
3845
+ row: this.$baseLodash.cloneDeep(row),
3846
+ oldValue,
3847
+ newValue,
3848
+ fields: context.fields,
3849
+ reason: options.reason || "edit",
3850
+ });
3851
+ if (result === false) {
3852
+ throw new Error("树键事务更新未成功。");
3853
+ }
3854
+ transactional = true;
3855
+ } catch (error) {
3856
+ return rejectChange(error);
3857
+ }
3858
+ }
3859
+
3860
+ row[rowField] = newValue;
3861
+ syncTreeKeyReferences(tableRows, oldValue, newValue, {
3862
+ fields: context.fields,
3863
+ });
3864
+ delete row._X_TREE_TEMP_KEY;
3865
+ this.unregisterTreeKey(
3866
+ this.treeTemporaryKeyRegistry,
3867
+ oldValue
3868
+ );
3869
+ this.treeKnownFormalKeyRegistry.add(newValue);
3870
+ if ($grid?.updateData) {
3871
+ await $grid.updateData();
3872
+ }
3873
+ if (transactional) {
3874
+ let formRef = this.getFormRef();
3875
+ let reloadMethod
3876
+ = typeof formRef?.reloadSavedFormData === "function"
3877
+ ? formRef.reloadSavedFormData
3878
+ : formRef?.reloadForm;
3879
+ if (typeof reloadMethod === "function") {
3880
+ try {
3881
+ await reloadMethod.call(formRef);
3882
+ } catch (error) {
3883
+ let reloadError = error instanceof Error
3884
+ ? error
3885
+ : new Error(error || "树表重新加载失败。");
3886
+ reloadError.treeReloadFailed = true;
3887
+ throw reloadError;
3888
+ }
3889
+ }
3890
+ }
3891
+ return { changed: true, transactional };
3892
+ },
3893
+ /**
3894
+ * 单行保存前确认父节点已持久化,或外部父键已被登记为正式键。
3895
+ * @param {Object} row 当前行。
3896
+ * @param {Object} $grid VXE Grid。
3897
+ * @returns {String|null} 阻断原因。
3898
+ */
3899
+ getTreeSingleSaveBlockReason(row, $grid) {
3900
+ if (!this.widget.options.isTreeTable || !row) {
3901
+ return null;
3902
+ }
3903
+ let context = this.getTreeContext($grid);
3904
+ let { rowField, parentField } = context.fields;
3905
+ let parentValue = row[parentField];
3906
+ if (isRootParent(parentValue)) {
3907
+ return null;
3908
+ }
3909
+ let tableRows = this.formModel[this.fieldKeyName] || [];
3910
+ let parentRow = tableRows.find(
3911
+ (item) =>
3912
+ item && treeKeyEquals(item[rowField], parentValue)
3913
+ );
3914
+ if (parentRow) {
3915
+ let physicalId = parentRow[context.identityConfig.physicalIdField];
3916
+ if (
3917
+ physicalId == null
3918
+ || physicalId === ""
3919
+ || isTemporaryTreeKey(
3920
+ parentRow,
3921
+ rowField,
3922
+ context.identityConfig
3923
+ )
3924
+ ) {
3925
+ return "请先保存父节点,再保存当前子节点。";
3926
+ }
3927
+ return null;
3928
+ }
3929
+ if (
3930
+ this.registryHasTreeKey(
3931
+ this.treeTemporaryKeyRegistry,
3932
+ parentValue
3933
+ )
3934
+ ) {
3935
+ return "当前行引用了尚未保存的父节点,请先保存父节点。";
3936
+ }
3937
+ if (
3938
+ this.registryHasTreeKey(
3939
+ this.treeKnownFormalKeyRegistry,
3940
+ parentValue
3941
+ )
3942
+ ) {
3943
+ return null;
3944
+ }
3945
+ let validator = context.identityConfig.validateExternalParentKey;
3946
+ if (typeof validator === "function") {
3947
+ let result = validator(parentValue, row, "single");
3948
+ if (result && typeof result.then === "function") {
3949
+ return "外部父节点校验必须同步完成。";
3950
+ }
3951
+ if (result === true) {
3952
+ this.treeKnownFormalKeyRegistry.add(parentValue);
3953
+ return null;
3954
+ }
3955
+ }
3956
+ return "无法确认当前行的父节点已保存,请先保存或加载父节点。";
3957
+ },
3958
3958
  /** @param {Object} e 容器组件 schema。@returns {String} 动态组件名称。 */
3959
3959
  getContainerWidgetName: function (e) {
3960
3960
  return "grid" === e.type ? "grid-item" : e.type + "-item";
@@ -4029,431 +4029,431 @@ modules = {
4029
4029
 
4030
4030
  //editTable begin
4031
4031
  /**
4032
- * 保存当前编辑行。持久化行原位合并;新身份以回查结果重建树关系。
4032
+ * 保存当前编辑行。持久化行原位合并;新身份以回查结果重建树关系。
4033
4033
  * @param {Object} obj VXE 插槽参数,必须包含 row 和 $table。
4034
4034
  */
4035
- async saveEditRow(obj) {
4036
- let formRef = this.getFormRef();
4037
- let formConfig = this.formConfig;
4038
- let entity = formConfig.entity;
4039
- if (!entity) return;
4040
- let reportTemplate = formRef?.reportTemplate;
4041
- let formCode = reportTemplate?.formCode;
4042
- let scriptCode = formConfig.saveScriptCode || "saveUpdate";
4043
-
4044
- let $grid = obj.$table.$xegrid;
4045
- if (typeof this.treeSingleSaveReconcileTask === "function") {
4046
- return this.treeSingleSaveReconcileTask();
4047
- }
4048
- if (this.treeSingleSavePending) {
4049
- this.$message.warning("当前行正在保存,请勿重复操作。");
4050
- return false;
4051
- }
4052
- this.treeSingleSavePending = true;
4053
- let keepSaveLocked = false;
4054
- let reloadMethod
4055
- = typeof formRef.reloadSavedFormData === "function"
4056
- ? formRef.reloadSavedFormData
4057
- : formRef.reloadForm;
4058
- let installReloadRetry = () => {
4059
- let retryPromise = null;
4060
- this.treeSingleSaveReconcileTask = () => {
4061
- if (retryPromise) return retryPromise;
4062
- retryPromise = (async () => {
4063
- try {
4064
- await reloadMethod.call(formRef);
4065
- delete obj.$table.editCloneRow;
4066
- this.treeSingleSaveReconcileTask = null;
4067
- this.treeSingleSavePending = false;
4068
- return true;
4069
- } catch (error) {
4070
- this.$message.error(
4071
- "重新加载仍未成功,请手动刷新后再操作。"
4072
- );
4073
- return false;
4074
- } finally {
4075
- retryPromise = null;
4076
- }
4077
- })();
4078
- return retryPromise;
4079
- };
4080
- };
4081
- let isTreeTable = this.widget.options.isTreeTable || false;
4082
- let treeContext = isTreeTable
4083
- ? this.getTreeContext($grid, { mode: "single" })
4084
- : null;
4085
- let oldTreeKey = null;
4086
- let rebuildTreeAfterSave = false;
4087
- let treePhysicalIdWasMissing = false;
4088
- let treeKeyWasTemporary = false;
4089
- let treeSnapshotApplied = false;
4090
- let logicalRowKey = obj.row?._X_ROW_KEY || null;
4091
- let originalPhysicalId = null;
4092
- try {
4093
- let valid = await new Promise((resolve, reject) => {
4094
- try {
4095
- formRef.validate(resolve);
4096
- } catch (error) {
4097
- reject(error);
4098
- }
4099
- });
4100
- if (!valid) return false;
4101
-
4102
- let requestRow = obj.row;
4103
- if (treeContext) {
4104
- let blockReason = this.getTreeSingleSaveBlockReason(
4105
- obj.row,
4106
- $grid
4107
- );
4108
- if (blockReason) {
4109
- this.$message.error(blockReason);
4110
- return false;
4111
- }
4112
- let rowField = treeContext.fields.rowField;
4113
- let currentTreeKey = obj.row[rowField];
4114
- let editCloneRow = obj.$table.editCloneRow;
4115
- let previousTreeKey = editCloneRow?.[rowField];
4116
- if (
4117
- obj.row._X_TREE_TEMP_KEY != null
4118
- && !treeKeyEquals(
4119
- obj.row._X_TREE_TEMP_KEY,
4120
- currentTreeKey
4121
- )
4122
- ) {
4123
- previousTreeKey = obj.row._X_TREE_TEMP_KEY;
4124
- }
4125
- if (
4126
- previousTreeKey !== undefined
4127
- && previousTreeKey !== null
4128
- && !treeKeyEquals(previousTreeKey, currentTreeKey)
4129
- ) {
4130
- let changeResult = await this.changeTreeRowField(
4131
- obj.row,
4132
- currentTreeKey,
4133
- {
4134
- oldValue: previousTreeKey,
4135
- $grid,
4136
- reason: "single-save",
4137
- }
4138
- );
4139
- if (changeResult.transactional) {
4140
- delete obj.$table.editCloneRow;
4141
- return true;
4142
- }
4143
- }
4144
- oldTreeKey = obj.row[rowField];
4145
- let physicalIdField
4146
- = treeContext.identityConfig.physicalIdField;
4147
- let physicalId = obj.row[physicalIdField];
4148
- originalPhysicalId = physicalId;
4149
- treeKeyWasTemporary = isTemporaryTreeKey(
4150
- obj.row,
4151
- rowField,
4152
- treeContext.identityConfig
4153
- );
4154
- treePhysicalIdWasMissing
4155
- = physicalId == null
4156
- || physicalId === ""
4157
- || (
4158
- physicalIdField === rowField
4159
- && treeKeyWasTemporary
4160
- );
4161
- rebuildTreeAfterSave
4162
- = treePhysicalIdWasMissing
4163
- || treeKeyWasTemporary;
4164
- requestRow = sanitizeTreeRow(
4165
- this.$baseLodash.cloneDeep(obj.row),
4166
- treeContext
4167
- );
4168
- }
4169
-
4170
- try {
4171
- await this.$baseConfirm("您确定要保存吗?");
4172
- } catch (error) {
4173
- return false;
4174
- }
4175
-
4176
- await $grid.clearActived();
4177
- await $grid.setActiveRow(obj.row);
4178
- let mainData = commonDataUtil.handleForm(requestRow);
4179
-
4180
- delete mainData._createNickName;
4181
- delete mainData._createMobile;
4182
- delete mainData._modifyNickName;
4183
- delete mainData._modifyMobile;
4184
- delete mainData._createBy;
4185
- delete mainData._modifyBy;
4186
-
4187
- let formData = {
4188
- [entity]: mainData,
4189
- };
4190
- Object.keys(mainData).forEach((key) => {
4191
- if (key.startsWith("attachments_")) {
4192
- formData[key] = mainData[key];
4193
- }
4194
- });
4195
-
4196
- let reqData = {
4197
- formCode: formCode,
4198
- formVersion: reportTemplate.formVersion,
4199
- taBm: entity,
4200
- data: formData,
4201
- };
4202
- let saveResponse = await new Promise((resolve, reject) => {
4203
- let requestTask = this.formHttp({
4204
- scriptCode: scriptCode,
4205
- data: reqData,
4206
- successMsg: true,
4207
- isConfirm: false,
4208
- success: resolve,
4209
- fail: (res) => {
4210
- reject(new Error(res?.content || "当前行保存失败。"));
4211
- },
4212
- error: reject,
4213
- });
4214
- if (requestTask && typeof requestTask.catch === "function") {
4215
- requestTask.catch(reject);
4216
- }
4217
- });
4218
-
4219
- let querySavedRow = () =>
4220
- new Promise((resolve, reject) => {
4221
- let requestTask = this.getRowData(
4222
- saveResponse.objx,
4223
- resolve,
4224
- (res) => {
4225
- reject(new Error(res?.content || "保存结果重新查询失败。"));
4226
- },
4227
- reject
4228
- );
4229
- if (requestTask && typeof requestTask.catch === "function") {
4230
- requestTask.catch(reject);
4231
- }
4232
- });
4233
- let reconcileSavedRow = async (rowResponse) => {
4234
- let responseRow = { ...(rowResponse?.objx || {}) };
4235
- if (treeContext) {
4236
- let { rowField } = treeContext.fields;
4237
- let physicalIdField
4238
- = treeContext.identityConfig.physicalIdField;
4239
- let hasIdentityValue = (value) =>
4240
- value !== null
4241
- && value !== undefined
4242
- && value !== "";
4243
- if (
4244
- treePhysicalIdWasMissing
4245
- && !hasIdentityValue(responseRow[physicalIdField])
4246
- ) {
4247
- throw new Error(
4248
- `保存响应未返回正式物理 ID 字段 ${physicalIdField}。`
4249
- );
4250
- }
4251
-
4252
- let treeTableRows = this.formModel[this.fieldKeyName] || [];
4253
- let currentLogicalRow = null;
4254
- if (hasIdentityValue(logicalRowKey)) {
4255
- currentLogicalRow = treeTableRows.find(
4256
- (item) => item?._X_ROW_KEY === logicalRowKey
4257
- );
4258
- }
4259
- if (!currentLogicalRow && treeTableRows.includes(obj.row)) {
4260
- currentLogicalRow = obj.row;
4261
- }
4262
- if (!currentLogicalRow) {
4263
- let physicalCandidates = [
4264
- originalPhysicalId,
4265
- responseRow[physicalIdField],
4266
- obj.row?.[physicalIdField],
4267
- ].filter(hasIdentityValue);
4268
- currentLogicalRow = treeTableRows.find(
4269
- (item) =>
4270
- item
4271
- && physicalCandidates.some((physicalId) =>
4272
- treeKeyEquals(
4273
- item[physicalIdField],
4274
- physicalId
4275
- )
4276
- )
4277
- );
4278
- }
4279
- if (!currentLogicalRow) {
4280
- throw new Error("无法定位保存响应对应的当前树表行。");
4281
- }
4282
- if (!hasIdentityValue(logicalRowKey)) {
4283
- logicalRowKey = currentLogicalRow._X_ROW_KEY || null;
4284
- }
4285
- obj.row = currentLogicalRow;
4286
-
4287
- let responseHasTreeKey = Object.prototype.hasOwnProperty.call(
4288
- responseRow,
4289
- rowField
4290
- );
4291
- let nextTreeKey = responseHasTreeKey
4292
- ? responseRow[rowField]
4293
- : currentLogicalRow[rowField];
4294
- let wasTemporary
4295
- = treeKeyWasTemporary
4296
- || isTemporaryTreeKey(
4297
- currentLogicalRow,
4298
- rowField,
4299
- treeContext.identityConfig
4300
- );
4301
- if (
4302
- wasTemporary
4303
- && treeContext.identityConfig.serverGeneratesRowField
4304
- && (isRootParent(nextTreeKey)
4305
- || treeKeyEquals(nextTreeKey, oldTreeKey))
4306
- ) {
4307
- throw new Error(
4308
- `保存响应未返回正式树键字段 ${rowField}。`
4309
- );
4310
- }
4311
-
4312
- let isCurrentLogicalRow = (item) =>
4313
- item === currentLogicalRow
4314
- || (
4315
- hasIdentityValue(logicalRowKey)
4316
- && item?._X_ROW_KEY === logicalRowKey
4317
- );
4318
- let keyConflict = treeTableRows.some(
4319
- (item) =>
4320
- !isCurrentLogicalRow(item)
4321
- && treeKeyEquals(item?.[rowField], nextTreeKey)
4322
- );
4323
- if (keyConflict) {
4324
- throw new Error(
4325
- `保存响应中的树键 ${nextTreeKey} 已存在。`
4326
- );
4327
- }
4328
-
4329
- let currentTreeKey = currentLogicalRow[rowField];
4330
- let treeKeyChanged = !treeKeyEquals(
4331
- currentTreeKey,
4332
- nextTreeKey
4333
- );
4334
- rebuildTreeAfterSave
4335
- = rebuildTreeAfterSave || treeKeyChanged;
4336
- if (
4337
- responseHasTreeKey
4338
- && currentTreeKey !== nextTreeKey
4339
- && !treeKeyChanged
4340
- ) {
4341
- responseRow[rowField] = currentTreeKey;
4342
- nextTreeKey = currentTreeKey;
4343
- }
4344
- let responsePhysicalId = responseRow[physicalIdField];
4345
- let responseAlreadyApplied
4346
- = treeSnapshotApplied
4347
- && !treeKeyChanged
4348
- && (
4349
- !hasIdentityValue(responsePhysicalId)
4350
- || treeKeyEquals(
4351
- currentLogicalRow[physicalIdField],
4352
- responsePhysicalId
4353
- )
4354
- )
4355
- && currentLogicalRow._X_TREE_TEMP_KEY == null;
4356
- let stableRowKey
4357
- = currentLogicalRow._X_ROW_KEY || logicalRowKey;
4358
- Object.assign(currentLogicalRow, responseRow);
4359
- if (hasIdentityValue(stableRowKey)) {
4360
- currentLogicalRow._X_ROW_KEY = stableRowKey;
4361
- logicalRowKey = stableRowKey;
4362
- }
4363
- if (treeKeyChanged) {
4364
- syncTreeKeyReferences(
4365
- treeTableRows,
4366
- currentTreeKey,
4367
- nextTreeKey,
4368
- { fields: treeContext.fields }
4369
- );
4370
- }
4371
- delete currentLogicalRow._X_TREE_TEMP_KEY;
4372
- this.unregisterTreeKey(
4373
- this.treeTemporaryKeyRegistry,
4374
- oldTreeKey
4375
- );
4376
- this.treeKnownFormalKeyRegistry.add(
4377
- currentLogicalRow[rowField]
4378
- );
4379
-
4380
- let reconcileGrid = this.getGridTable?.() || $grid;
4381
- if (reconcileGrid?.clearActived) {
4382
- await reconcileGrid.clearActived();
4383
- }
4384
- if (rebuildTreeAfterSave && !responseAlreadyApplied) {
4385
- this.setValue(treeTableRows);
4386
- treeSnapshotApplied = true;
4387
- await new Promise((resolve) => this.$nextTick(resolve));
4388
- }
4389
- reconcileGrid = this.getGridTable?.() || reconcileGrid;
4390
- if (reconcileGrid?.updateData) {
4391
- await reconcileGrid.updateData();
4392
- }
4393
- } else {
4394
- Object.assign(obj.row, responseRow);
4395
- await $grid.clearActived();
4396
- await $grid.updateData();
4397
- }
4398
- delete obj.$table.editCloneRow;
4399
- return true;
4400
- };
4401
- let runRowReconcile = async () =>
4402
- reconcileSavedRow(await querySavedRow());
4403
- try {
4404
- return await runRowReconcile();
4405
- } catch (error) {
4406
- keepSaveLocked = true;
4407
- let retryPromise = null;
4408
- this.treeSingleSaveReconcileTask = () => {
4409
- if (retryPromise) return retryPromise;
4410
- retryPromise = runRowReconcile()
4411
- .then((result) => {
4412
- this.treeSingleSaveReconcileTask = null;
4413
- this.treeSingleSavePending = false;
4414
- return result;
4415
- })
4416
- .catch((retryError) => {
4417
- this.$message.error(
4418
- retryError?.message
4419
- || "保存结果仍未能重新加载,请手动刷新后再操作。"
4420
- );
4421
- return false;
4422
- })
4423
- .finally(() => {
4424
- retryPromise = null;
4425
- });
4426
- return retryPromise;
4427
- };
4428
- this.$message.error(
4429
- error?.message
4430
- || "保存已成功,但结果重新加载失败,请重试。"
4431
- );
4432
- return false;
4433
- }
4434
- } catch (error) {
4435
- if (error?.treeReloadFailed) {
4436
- keepSaveLocked = true;
4437
- installReloadRetry();
4438
- this.$message.error(
4439
- "树键已更新,但重新加载失败,请手动刷新后再操作。"
4440
- );
4441
- return false;
4442
- }
4443
- if (!keepSaveLocked) {
4444
- this.$message.error(
4445
- error?.message || "当前行保存失败。"
4446
- );
4447
- }
4448
- return false;
4449
- } finally {
4450
- if (!keepSaveLocked) {
4451
- this.treeSingleSavePending = false;
4452
- }
4453
- }
4454
- },
4455
- /** @param {*} dataId 行数据 id。@param {Function} callback 查询成功回调。加载保存后的完整行。 */
4456
- getRowData(dataId, callback, fail, error) {
4035
+ async saveEditRow(obj) {
4036
+ let formRef = this.getFormRef();
4037
+ let formConfig = this.formConfig;
4038
+ let entity = formConfig.entity;
4039
+ if (!entity) return;
4040
+ let reportTemplate = formRef?.reportTemplate;
4041
+ let formCode = reportTemplate?.formCode;
4042
+ let scriptCode = formConfig.saveScriptCode || "saveUpdate";
4043
+
4044
+ let $grid = obj.$table.$xegrid;
4045
+ if (typeof this.treeSingleSaveReconcileTask === "function") {
4046
+ return this.treeSingleSaveReconcileTask();
4047
+ }
4048
+ if (this.treeSingleSavePending) {
4049
+ this.$message.warning("当前行正在保存,请勿重复操作。");
4050
+ return false;
4051
+ }
4052
+ this.treeSingleSavePending = true;
4053
+ let keepSaveLocked = false;
4054
+ let reloadMethod
4055
+ = typeof formRef.reloadSavedFormData === "function"
4056
+ ? formRef.reloadSavedFormData
4057
+ : formRef.reloadForm;
4058
+ let installReloadRetry = () => {
4059
+ let retryPromise = null;
4060
+ this.treeSingleSaveReconcileTask = () => {
4061
+ if (retryPromise) return retryPromise;
4062
+ retryPromise = (async () => {
4063
+ try {
4064
+ await reloadMethod.call(formRef);
4065
+ delete obj.$table.editCloneRow;
4066
+ this.treeSingleSaveReconcileTask = null;
4067
+ this.treeSingleSavePending = false;
4068
+ return true;
4069
+ } catch (error) {
4070
+ this.$message.error(
4071
+ "重新加载仍未成功,请手动刷新后再操作。"
4072
+ );
4073
+ return false;
4074
+ } finally {
4075
+ retryPromise = null;
4076
+ }
4077
+ })();
4078
+ return retryPromise;
4079
+ };
4080
+ };
4081
+ let isTreeTable = this.widget.options.isTreeTable || false;
4082
+ let treeContext = isTreeTable
4083
+ ? this.getTreeContext($grid, { mode: "single" })
4084
+ : null;
4085
+ let oldTreeKey = null;
4086
+ let rebuildTreeAfterSave = false;
4087
+ let treePhysicalIdWasMissing = false;
4088
+ let treeKeyWasTemporary = false;
4089
+ let treeSnapshotApplied = false;
4090
+ let logicalRowKey = obj.row?._X_ROW_KEY || null;
4091
+ let originalPhysicalId = null;
4092
+ try {
4093
+ let valid = await new Promise((resolve, reject) => {
4094
+ try {
4095
+ formRef.validate(resolve);
4096
+ } catch (error) {
4097
+ reject(error);
4098
+ }
4099
+ });
4100
+ if (!valid) return false;
4101
+
4102
+ let requestRow = obj.row;
4103
+ if (treeContext) {
4104
+ let blockReason = this.getTreeSingleSaveBlockReason(
4105
+ obj.row,
4106
+ $grid
4107
+ );
4108
+ if (blockReason) {
4109
+ this.$message.error(blockReason);
4110
+ return false;
4111
+ }
4112
+ let rowField = treeContext.fields.rowField;
4113
+ let currentTreeKey = obj.row[rowField];
4114
+ let editCloneRow = obj.$table.editCloneRow;
4115
+ let previousTreeKey = editCloneRow?.[rowField];
4116
+ if (
4117
+ obj.row._X_TREE_TEMP_KEY != null
4118
+ && !treeKeyEquals(
4119
+ obj.row._X_TREE_TEMP_KEY,
4120
+ currentTreeKey
4121
+ )
4122
+ ) {
4123
+ previousTreeKey = obj.row._X_TREE_TEMP_KEY;
4124
+ }
4125
+ if (
4126
+ previousTreeKey !== undefined
4127
+ && previousTreeKey !== null
4128
+ && !treeKeyEquals(previousTreeKey, currentTreeKey)
4129
+ ) {
4130
+ let changeResult = await this.changeTreeRowField(
4131
+ obj.row,
4132
+ currentTreeKey,
4133
+ {
4134
+ oldValue: previousTreeKey,
4135
+ $grid,
4136
+ reason: "single-save",
4137
+ }
4138
+ );
4139
+ if (changeResult.transactional) {
4140
+ delete obj.$table.editCloneRow;
4141
+ return true;
4142
+ }
4143
+ }
4144
+ oldTreeKey = obj.row[rowField];
4145
+ let physicalIdField
4146
+ = treeContext.identityConfig.physicalIdField;
4147
+ let physicalId = obj.row[physicalIdField];
4148
+ originalPhysicalId = physicalId;
4149
+ treeKeyWasTemporary = isTemporaryTreeKey(
4150
+ obj.row,
4151
+ rowField,
4152
+ treeContext.identityConfig
4153
+ );
4154
+ treePhysicalIdWasMissing
4155
+ = physicalId == null
4156
+ || physicalId === ""
4157
+ || (
4158
+ physicalIdField === rowField
4159
+ && treeKeyWasTemporary
4160
+ );
4161
+ rebuildTreeAfterSave
4162
+ = treePhysicalIdWasMissing
4163
+ || treeKeyWasTemporary;
4164
+ requestRow = sanitizeTreeRow(
4165
+ this.$baseLodash.cloneDeep(obj.row),
4166
+ treeContext
4167
+ );
4168
+ }
4169
+
4170
+ try {
4171
+ await this.$baseConfirm("您确定要保存吗?");
4172
+ } catch (error) {
4173
+ return false;
4174
+ }
4175
+
4176
+ await $grid.clearActived();
4177
+ await $grid.setActiveRow(obj.row);
4178
+ let mainData = commonDataUtil.handleForm(requestRow);
4179
+
4180
+ delete mainData._createNickName;
4181
+ delete mainData._createMobile;
4182
+ delete mainData._modifyNickName;
4183
+ delete mainData._modifyMobile;
4184
+ delete mainData._createBy;
4185
+ delete mainData._modifyBy;
4186
+
4187
+ let formData = {
4188
+ [entity]: mainData,
4189
+ };
4190
+ Object.keys(mainData).forEach((key) => {
4191
+ if (key.startsWith("attachments_")) {
4192
+ formData[key] = mainData[key];
4193
+ }
4194
+ });
4195
+
4196
+ let reqData = {
4197
+ formCode: formCode,
4198
+ formVersion: reportTemplate.formVersion,
4199
+ taBm: entity,
4200
+ data: formData,
4201
+ };
4202
+ let saveResponse = await new Promise((resolve, reject) => {
4203
+ let requestTask = this.formHttp({
4204
+ scriptCode: scriptCode,
4205
+ data: reqData,
4206
+ successMsg: true,
4207
+ isConfirm: false,
4208
+ success: resolve,
4209
+ fail: (res) => {
4210
+ reject(new Error(res?.content || "当前行保存失败。"));
4211
+ },
4212
+ error: reject,
4213
+ });
4214
+ if (requestTask && typeof requestTask.catch === "function") {
4215
+ requestTask.catch(reject);
4216
+ }
4217
+ });
4218
+
4219
+ let querySavedRow = () =>
4220
+ new Promise((resolve, reject) => {
4221
+ let requestTask = this.getRowData(
4222
+ saveResponse.objx,
4223
+ resolve,
4224
+ (res) => {
4225
+ reject(new Error(res?.content || "保存结果重新查询失败。"));
4226
+ },
4227
+ reject
4228
+ );
4229
+ if (requestTask && typeof requestTask.catch === "function") {
4230
+ requestTask.catch(reject);
4231
+ }
4232
+ });
4233
+ let reconcileSavedRow = async (rowResponse) => {
4234
+ let responseRow = { ...(rowResponse?.objx || {}) };
4235
+ if (treeContext) {
4236
+ let { rowField } = treeContext.fields;
4237
+ let physicalIdField
4238
+ = treeContext.identityConfig.physicalIdField;
4239
+ let hasIdentityValue = (value) =>
4240
+ value !== null
4241
+ && value !== undefined
4242
+ && value !== "";
4243
+ if (
4244
+ treePhysicalIdWasMissing
4245
+ && !hasIdentityValue(responseRow[physicalIdField])
4246
+ ) {
4247
+ throw new Error(
4248
+ `保存响应未返回正式物理 ID 字段 ${physicalIdField}。`
4249
+ );
4250
+ }
4251
+
4252
+ let treeTableRows = this.formModel[this.fieldKeyName] || [];
4253
+ let currentLogicalRow = null;
4254
+ if (hasIdentityValue(logicalRowKey)) {
4255
+ currentLogicalRow = treeTableRows.find(
4256
+ (item) => item?._X_ROW_KEY === logicalRowKey
4257
+ );
4258
+ }
4259
+ if (!currentLogicalRow && treeTableRows.includes(obj.row)) {
4260
+ currentLogicalRow = obj.row;
4261
+ }
4262
+ if (!currentLogicalRow) {
4263
+ let physicalCandidates = [
4264
+ originalPhysicalId,
4265
+ responseRow[physicalIdField],
4266
+ obj.row?.[physicalIdField],
4267
+ ].filter(hasIdentityValue);
4268
+ currentLogicalRow = treeTableRows.find(
4269
+ (item) =>
4270
+ item
4271
+ && physicalCandidates.some((physicalId) =>
4272
+ treeKeyEquals(
4273
+ item[physicalIdField],
4274
+ physicalId
4275
+ )
4276
+ )
4277
+ );
4278
+ }
4279
+ if (!currentLogicalRow) {
4280
+ throw new Error("无法定位保存响应对应的当前树表行。");
4281
+ }
4282
+ if (!hasIdentityValue(logicalRowKey)) {
4283
+ logicalRowKey = currentLogicalRow._X_ROW_KEY || null;
4284
+ }
4285
+ obj.row = currentLogicalRow;
4286
+
4287
+ let responseHasTreeKey = Object.prototype.hasOwnProperty.call(
4288
+ responseRow,
4289
+ rowField
4290
+ );
4291
+ let nextTreeKey = responseHasTreeKey
4292
+ ? responseRow[rowField]
4293
+ : currentLogicalRow[rowField];
4294
+ let wasTemporary
4295
+ = treeKeyWasTemporary
4296
+ || isTemporaryTreeKey(
4297
+ currentLogicalRow,
4298
+ rowField,
4299
+ treeContext.identityConfig
4300
+ );
4301
+ if (
4302
+ wasTemporary
4303
+ && treeContext.identityConfig.serverGeneratesRowField
4304
+ && (isRootParent(nextTreeKey)
4305
+ || treeKeyEquals(nextTreeKey, oldTreeKey))
4306
+ ) {
4307
+ throw new Error(
4308
+ `保存响应未返回正式树键字段 ${rowField}。`
4309
+ );
4310
+ }
4311
+
4312
+ let isCurrentLogicalRow = (item) =>
4313
+ item === currentLogicalRow
4314
+ || (
4315
+ hasIdentityValue(logicalRowKey)
4316
+ && item?._X_ROW_KEY === logicalRowKey
4317
+ );
4318
+ let keyConflict = treeTableRows.some(
4319
+ (item) =>
4320
+ !isCurrentLogicalRow(item)
4321
+ && treeKeyEquals(item?.[rowField], nextTreeKey)
4322
+ );
4323
+ if (keyConflict) {
4324
+ throw new Error(
4325
+ `保存响应中的树键 ${nextTreeKey} 已存在。`
4326
+ );
4327
+ }
4328
+
4329
+ let currentTreeKey = currentLogicalRow[rowField];
4330
+ let treeKeyChanged = !treeKeyEquals(
4331
+ currentTreeKey,
4332
+ nextTreeKey
4333
+ );
4334
+ rebuildTreeAfterSave
4335
+ = rebuildTreeAfterSave || treeKeyChanged;
4336
+ if (
4337
+ responseHasTreeKey
4338
+ && currentTreeKey !== nextTreeKey
4339
+ && !treeKeyChanged
4340
+ ) {
4341
+ responseRow[rowField] = currentTreeKey;
4342
+ nextTreeKey = currentTreeKey;
4343
+ }
4344
+ let responsePhysicalId = responseRow[physicalIdField];
4345
+ let responseAlreadyApplied
4346
+ = treeSnapshotApplied
4347
+ && !treeKeyChanged
4348
+ && (
4349
+ !hasIdentityValue(responsePhysicalId)
4350
+ || treeKeyEquals(
4351
+ currentLogicalRow[physicalIdField],
4352
+ responsePhysicalId
4353
+ )
4354
+ )
4355
+ && currentLogicalRow._X_TREE_TEMP_KEY == null;
4356
+ let stableRowKey
4357
+ = currentLogicalRow._X_ROW_KEY || logicalRowKey;
4358
+ Object.assign(currentLogicalRow, responseRow);
4359
+ if (hasIdentityValue(stableRowKey)) {
4360
+ currentLogicalRow._X_ROW_KEY = stableRowKey;
4361
+ logicalRowKey = stableRowKey;
4362
+ }
4363
+ if (treeKeyChanged) {
4364
+ syncTreeKeyReferences(
4365
+ treeTableRows,
4366
+ currentTreeKey,
4367
+ nextTreeKey,
4368
+ { fields: treeContext.fields }
4369
+ );
4370
+ }
4371
+ delete currentLogicalRow._X_TREE_TEMP_KEY;
4372
+ this.unregisterTreeKey(
4373
+ this.treeTemporaryKeyRegistry,
4374
+ oldTreeKey
4375
+ );
4376
+ this.treeKnownFormalKeyRegistry.add(
4377
+ currentLogicalRow[rowField]
4378
+ );
4379
+
4380
+ let reconcileGrid = this.getGridTable?.() || $grid;
4381
+ if (reconcileGrid?.clearActived) {
4382
+ await reconcileGrid.clearActived();
4383
+ }
4384
+ if (rebuildTreeAfterSave && !responseAlreadyApplied) {
4385
+ this.setValue(treeTableRows);
4386
+ treeSnapshotApplied = true;
4387
+ await new Promise((resolve) => this.$nextTick(resolve));
4388
+ }
4389
+ reconcileGrid = this.getGridTable?.() || reconcileGrid;
4390
+ if (reconcileGrid?.updateData) {
4391
+ await reconcileGrid.updateData();
4392
+ }
4393
+ } else {
4394
+ Object.assign(obj.row, responseRow);
4395
+ await $grid.clearActived();
4396
+ await $grid.updateData();
4397
+ }
4398
+ delete obj.$table.editCloneRow;
4399
+ return true;
4400
+ };
4401
+ let runRowReconcile = async () =>
4402
+ reconcileSavedRow(await querySavedRow());
4403
+ try {
4404
+ return await runRowReconcile();
4405
+ } catch (error) {
4406
+ keepSaveLocked = true;
4407
+ let retryPromise = null;
4408
+ this.treeSingleSaveReconcileTask = () => {
4409
+ if (retryPromise) return retryPromise;
4410
+ retryPromise = runRowReconcile()
4411
+ .then((result) => {
4412
+ this.treeSingleSaveReconcileTask = null;
4413
+ this.treeSingleSavePending = false;
4414
+ return result;
4415
+ })
4416
+ .catch((retryError) => {
4417
+ this.$message.error(
4418
+ retryError?.message
4419
+ || "保存结果仍未能重新加载,请手动刷新后再操作。"
4420
+ );
4421
+ return false;
4422
+ })
4423
+ .finally(() => {
4424
+ retryPromise = null;
4425
+ });
4426
+ return retryPromise;
4427
+ };
4428
+ this.$message.error(
4429
+ error?.message
4430
+ || "保存已成功,但结果重新加载失败,请重试。"
4431
+ );
4432
+ return false;
4433
+ }
4434
+ } catch (error) {
4435
+ if (error?.treeReloadFailed) {
4436
+ keepSaveLocked = true;
4437
+ installReloadRetry();
4438
+ this.$message.error(
4439
+ "树键已更新,但重新加载失败,请手动刷新后再操作。"
4440
+ );
4441
+ return false;
4442
+ }
4443
+ if (!keepSaveLocked) {
4444
+ this.$message.error(
4445
+ error?.message || "当前行保存失败。"
4446
+ );
4447
+ }
4448
+ return false;
4449
+ } finally {
4450
+ if (!keepSaveLocked) {
4451
+ this.treeSingleSavePending = false;
4452
+ }
4453
+ }
4454
+ },
4455
+ /** @param {*} dataId 行数据 id。@param {Function} callback 查询成功回调。加载保存后的完整行。 */
4456
+ getRowData(dataId, callback, fail, error) {
4457
4457
  let reportTemplate = this.getFormRef()?.reportTemplate;
4458
4458
  let formCode = reportTemplate.formCode;
4459
4459
  let formConfig = this.formConfig;
@@ -4461,7 +4461,7 @@ modules = {
4461
4461
  let entity = formConfig.entity;
4462
4462
  let scriptCode = formConfig.formScriptCode || "getOne";
4463
4463
  let defaultOption = this.getHttpConfigForUser();
4464
- return this.formHttp({
4464
+ return this.formHttp({
4465
4465
  scriptCode: scriptCode,
4466
4466
  data: {
4467
4467
  formCode: formCode,
@@ -4472,12 +4472,12 @@ modules = {
4472
4472
  },
4473
4473
  },
4474
4474
  ...defaultOption,
4475
- success: (res) => {
4476
- callback && callback(res);
4477
- },
4478
- fail,
4479
- error,
4480
- });
4475
+ success: (res) => {
4476
+ callback && callback(res);
4477
+ },
4478
+ fail,
4479
+ error,
4480
+ });
4481
4481
  },
4482
4482
  /** @param {Object} rowData 新行数据。@param {Object} obj 父行参数。@param {Boolean} toTop 是否插到顶部。 */
4483
4483
  addChildTreeRow(rowData, obj, toTop) {
@@ -4504,12 +4504,12 @@ modules = {
4504
4504
  let isEditTable = this.widget.options.isEditTable || false;
4505
4505
  let parent = obj?.row;
4506
4506
  let $grid = this.getGridTable();
4507
- if (isEditTable && this.hasEditingRow($grid, 1)) {
4508
- return false;
4509
- }
4510
- let treeContext = this.getTreeContext($grid);
4511
- let { rowField, parentField } = treeContext.fields;
4512
- let newRow = {};
4507
+ if (isEditTable && this.hasEditingRow($grid, 1)) {
4508
+ return false;
4509
+ }
4510
+ let treeContext = this.getTreeContext($grid);
4511
+ let { rowField, parentField } = treeContext.fields;
4512
+ let newRow = {};
4513
4513
  let originOption = $grid.params.originOption;
4514
4514
  let editDefaultRow;
4515
4515
  if (originOption.editDefaultRow) {
@@ -4520,27 +4520,27 @@ modules = {
4520
4520
  }
4521
4521
  }
4522
4522
 
4523
- let newData = this.createNewTableData(isEditTable);
4524
- newRow = Object.assign({}, newData, editDefaultRow, rowData);
4525
- newRow[treeContext.identityConfig.physicalIdField] = null;
4526
- this.ensureNewTreeRowKey(newRow, treeContext);
4527
- if (!toSibling) {
4528
- if (parent && isRootParent(parent[rowField])) {
4529
- this.$message.error("父节点缺少有效树键,无法新增子节点。");
4530
- return false;
4531
- }
4532
- newRow[parentField] = parent
4533
- ? parent[rowField]
4534
- : treeContext.identityConfig.rootParentValue;
4535
- } else {
4536
- let siblingParentValue = obj?.row[parentField];
4537
- newRow[parentField] = isRootParent(siblingParentValue)
4538
- ? treeContext.identityConfig.rootParentValue
4539
- : siblingParentValue;
4540
- }
4541
- this.validateNewTreeRows([newRow], treeContext);
4542
-
4543
- let tableRows = this.formModel[this.fieldKeyName] || [];
4523
+ let newData = this.createNewTableData(isEditTable);
4524
+ newRow = Object.assign({}, newData, editDefaultRow, rowData);
4525
+ newRow[treeContext.identityConfig.physicalIdField] = null;
4526
+ this.ensureNewTreeRowKey(newRow, treeContext);
4527
+ if (!toSibling) {
4528
+ if (parent && isRootParent(parent[rowField])) {
4529
+ this.$message.error("父节点缺少有效树键,无法新增子节点。");
4530
+ return false;
4531
+ }
4532
+ newRow[parentField] = parent
4533
+ ? parent[rowField]
4534
+ : treeContext.identityConfig.rootParentValue;
4535
+ } else {
4536
+ let siblingParentValue = obj?.row[parentField];
4537
+ newRow[parentField] = isRootParent(siblingParentValue)
4538
+ ? treeContext.identityConfig.rootParentValue
4539
+ : siblingParentValue;
4540
+ }
4541
+ this.validateNewTreeRows([newRow], treeContext);
4542
+
4543
+ let tableRows = this.formModel[this.fieldKeyName] || [];
4544
4544
  if (!parent) {
4545
4545
  if (toEnd === true) {
4546
4546
  tableRows.push(newRow);
@@ -4556,9 +4556,9 @@ modules = {
4556
4556
  }
4557
4557
  this.setValue(tableRows);
4558
4558
  this.$nextTick(() => {
4559
- this.handleWbs();
4560
- if (isEditTable) {
4561
- this.setActiveRowById($grid, newRow[rowField]);
4559
+ this.handleWbs();
4560
+ if (isEditTable) {
4561
+ this.setActiveRowById($grid, newRow[rowField]);
4562
4562
  }
4563
4563
  });
4564
4564
 
@@ -4580,9 +4580,9 @@ modules = {
4580
4580
  this.$nextTick(() => {
4581
4581
  this.handleWbs();
4582
4582
  // setValue 已浅克隆所有行,obj.row 是失效引用,须按 _X_ROW_KEY 找回新行
4583
- this.setTreeExpandByRowKey($grid, obj.row._X_ROW_KEY);
4584
- if (isEditTable) {
4585
- this.setActiveRowById($grid, newRow[rowField]);
4583
+ this.setTreeExpandByRowKey($grid, obj.row._X_ROW_KEY);
4584
+ if (isEditTable) {
4585
+ this.setActiveRowById($grid, newRow[rowField]);
4586
4586
  }
4587
4587
  });
4588
4588
  });
@@ -4616,16 +4616,16 @@ modules = {
4616
4616
  addTreeRows(rowsData, obj, field, sanitize) {
4617
4617
  if (!Array.isArray(rowsData) || !rowsData.length) {
4618
4618
  return;
4619
- }
4620
- let $grid = this.getGridTable();
4621
- let parent = obj?.row;
4622
- let treeContext = this.getTreeContext($grid);
4623
- let { rowField, parentField } = treeContext.fields;
4624
- if (parent && isRootParent(parent[rowField])) {
4625
- this.$message.error("父节点缺少有效树键,无法新增子节点。");
4626
- return false;
4627
- }
4628
- let tableRows = this.formModel[this.fieldKeyName] || [];
4619
+ }
4620
+ let $grid = this.getGridTable();
4621
+ let parent = obj?.row;
4622
+ let treeContext = this.getTreeContext($grid);
4623
+ let { rowField, parentField } = treeContext.fields;
4624
+ if (parent && isRootParent(parent[rowField])) {
4625
+ this.$message.error("父节点缺少有效树键,无法新增子节点。");
4626
+ return false;
4627
+ }
4628
+ let tableRows = this.formModel[this.fieldKeyName] || [];
4629
4629
  let items = rowsData;
4630
4630
  if (field) {
4631
4631
  let keys = tableRows.map((item) => item[field] + "");
@@ -4642,29 +4642,29 @@ modules = {
4642
4642
  } else {
4643
4643
  editDefaultRow = originOption.editDefaultRow || {};
4644
4644
  }
4645
- }
4646
- let newData = this.createNewTableData(false);
4647
- let extraStrip = Array.isArray(sanitize) ? sanitize : null;
4648
- let newRows = [];
4649
- items.forEach((rowData) => {
4650
- // 复制来的数据先剔除系统字段,避免旧审计/外键随 assign 泄入
4651
- let source = rowData;
4652
- if (sanitize) {
4653
- source = this.stripRowSystemFields(rowData, extraStrip);
4654
- }
4655
- let newRow = Object.assign({}, newData, editDefaultRow, source);
4656
- newRow[treeContext.identityConfig.physicalIdField] = null;
4657
- this.ensureNewTreeRowKey(newRow, treeContext);
4658
- if (parentField) {
4659
- newRow[parentField] = parent
4660
- ? parent[rowField]
4661
- : treeContext.identityConfig.rootParentValue;
4662
- }
4663
- newRows.push(newRow);
4664
- });
4665
- this.validateNewTreeRows(newRows, treeContext);
4666
- tableRows.push(...newRows);
4667
- this.setValue(tableRows);
4645
+ }
4646
+ let newData = this.createNewTableData(false);
4647
+ let extraStrip = Array.isArray(sanitize) ? sanitize : null;
4648
+ let newRows = [];
4649
+ items.forEach((rowData) => {
4650
+ // 复制来的数据先剔除系统字段,避免旧审计/外键随 assign 泄入
4651
+ let source = rowData;
4652
+ if (sanitize) {
4653
+ source = this.stripRowSystemFields(rowData, extraStrip);
4654
+ }
4655
+ let newRow = Object.assign({}, newData, editDefaultRow, source);
4656
+ newRow[treeContext.identityConfig.physicalIdField] = null;
4657
+ this.ensureNewTreeRowKey(newRow, treeContext);
4658
+ if (parentField) {
4659
+ newRow[parentField] = parent
4660
+ ? parent[rowField]
4661
+ : treeContext.identityConfig.rootParentValue;
4662
+ }
4663
+ newRows.push(newRow);
4664
+ });
4665
+ this.validateNewTreeRows(newRows, treeContext);
4666
+ tableRows.push(...newRows);
4667
+ this.setValue(tableRows);
4668
4668
  this.$nextTick(() => {
4669
4669
  this.handleWbs();
4670
4670
  if (parent) {
@@ -4681,36 +4681,36 @@ modules = {
4681
4681
  * @returns {Set} 后代行的 _X_ROW_KEY 集合。
4682
4682
  */
4683
4683
  collectDescendantKeys(row, tableRows) {
4684
- let $grid = this.getGridTable();
4685
- let isTreeTable = this.widget.options.isTreeTable || false;
4686
- let treeFields = this.getTreeFields($grid);
4687
- let childFields = [
4688
- treeFields.childrenField,
4689
- treeFields.mapChildrenField,
4690
- ].filter((field, index, values) => field && values.indexOf(field) === index);
4691
- let keys = new Set();
4692
- let visited = new WeakSet();
4693
- // 若数据是嵌套结构,收集 children 下的子节点
4694
- let loopDo = (item) => {
4695
- if (!item || typeof item !== "object" || visited.has(item)) {
4696
- return;
4697
- }
4698
- visited.add(item);
4699
- childFields.forEach((childrenField) => {
4700
- if (!item || !Array.isArray(item[childrenField])) {
4701
- return;
4702
- }
4703
- item[childrenField].forEach((subItem) => {
4704
- if (subItem && subItem._X_ROW_KEY) keys.add(subItem._X_ROW_KEY);
4705
- loopDo(subItem);
4706
- });
4707
- });
4708
- };
4684
+ let $grid = this.getGridTable();
4685
+ let isTreeTable = this.widget.options.isTreeTable || false;
4686
+ let treeFields = this.getTreeFields($grid);
4687
+ let childFields = [
4688
+ treeFields.childrenField,
4689
+ treeFields.mapChildrenField,
4690
+ ].filter((field, index, values) => field && values.indexOf(field) === index);
4691
+ let keys = new Set();
4692
+ let visited = new WeakSet();
4693
+ // 若数据是嵌套结构,收集 children 下的子节点
4694
+ let loopDo = (item) => {
4695
+ if (!item || typeof item !== "object" || visited.has(item)) {
4696
+ return;
4697
+ }
4698
+ visited.add(item);
4699
+ childFields.forEach((childrenField) => {
4700
+ if (!item || !Array.isArray(item[childrenField])) {
4701
+ return;
4702
+ }
4703
+ item[childrenField].forEach((subItem) => {
4704
+ if (subItem && subItem._X_ROW_KEY) keys.add(subItem._X_ROW_KEY);
4705
+ loopDo(subItem);
4706
+ });
4707
+ });
4708
+ };
4709
4709
  loopDo(row);
4710
4710
  // 树表使用扁平数据(transform),子节点通过 parentField 关联,
4711
- // 需按 parentField 递归收集所有后代,避免删除父节点后子节点残留
4712
- if (isTreeTable && $grid.treeConfig) {
4713
- let { rowField, parentField } = treeFields;
4711
+ // 需按 parentField 递归收集所有后代,避免删除父节点后子节点残留
4712
+ if (isTreeTable && $grid.treeConfig) {
4713
+ let { rowField, parentField } = treeFields;
4714
4714
  let pending = [row];
4715
4715
  while (pending.length) {
4716
4716
  let cur = pending.shift();
@@ -4722,9 +4722,9 @@ modules = {
4722
4722
  // 数据成环时防止把起始节点收进后代集合
4723
4723
  if (item._X_ROW_KEY === row._X_ROW_KEY) {
4724
4724
  return;
4725
- }
4726
- let parentValue = item[parentField];
4727
- if (treeKeyEquals(parentValue, curId)) {
4725
+ }
4726
+ let parentValue = item[parentField];
4727
+ if (treeKeyEquals(parentValue, curId)) {
4728
4728
  keys.add(item._X_ROW_KEY);
4729
4729
  pending.push(item);
4730
4730
  }
@@ -4739,19 +4739,19 @@ modules = {
4739
4739
  * @param {Array} tableRows 当前扁平数据。
4740
4740
  * @param {Set} delKeys 待删除行的 _X_ROW_KEY 集合。
4741
4741
  */
4742
- removeTreeRowsByKeys(tableRows, delKeys) {
4743
- if (this.widget.options.isTreeTable) {
4744
- let { rowField } = this.getTreeFields();
4745
- tableRows.forEach((item) => {
4746
- if (item && delKeys.has(item._X_ROW_KEY)) {
4747
- this.unregisterTreeKey(
4748
- this.treeTemporaryKeyRegistry,
4749
- item[rowField]
4750
- );
4751
- }
4752
- });
4753
- }
4754
- let newRows = tableRows.filter((item) => !delKeys.has(item._X_ROW_KEY));
4742
+ removeTreeRowsByKeys(tableRows, delKeys) {
4743
+ if (this.widget.options.isTreeTable) {
4744
+ let { rowField } = this.getTreeFields();
4745
+ tableRows.forEach((item) => {
4746
+ if (item && delKeys.has(item._X_ROW_KEY)) {
4747
+ this.unregisterTreeKey(
4748
+ this.treeTemporaryKeyRegistry,
4749
+ item[rowField]
4750
+ );
4751
+ }
4752
+ });
4753
+ }
4754
+ let newRows = tableRows.filter((item) => !delKeys.has(item._X_ROW_KEY));
4755
4755
  this.setValue(newRows);
4756
4756
  this.$nextTick(() => {
4757
4757
  this.handleWbs();
@@ -4905,43 +4905,43 @@ modules = {
4905
4905
  }
4906
4906
  }
4907
4907
 
4908
- let treeContext = isTreeTable
4909
- ? this.getTreeContext($grid)
4910
- : null;
4911
- let { rowField, parentField } = treeContext
4912
- ? treeContext.fields
4913
- : this.getTreeFields($grid);
4914
-
4915
- let newData = this.createNewTableData(isEditTable);
4916
- newRow = Object.assign({}, newData, editDefaultRow, rowData);
4917
- if (isTreeTable) {
4918
- newRow[treeContext.identityConfig.physicalIdField] = null;
4919
- this.ensureNewTreeRowKey(newRow, treeContext);
4920
- if (!toSibling) {
4921
- if (parent && isRootParent(parent[rowField])) {
4922
- this.$message.error("父节点缺少有效树键,无法新增子节点。");
4923
- return false;
4924
- }
4925
- newRow[parentField] = parent
4926
- ? parent[rowField]
4927
- : treeContext.identityConfig.rootParentValue;
4928
- } else {
4929
- let siblingParentValue = obj?.row[parentField];
4930
- newRow[parentField] = isRootParent(siblingParentValue)
4931
- ? treeContext.identityConfig.rootParentValue
4932
- : siblingParentValue;
4933
- }
4934
- } else {
4935
- // 普通编辑表沿用原有临时 id 协议。
4936
- newRow[rowField] = "row_"
4937
- + new Date().valueOf()
4938
- + Math.floor(Math.random() * 1000000);
4939
- }
4940
- if (treeContext) {
4941
- this.validateNewTreeRows([newRow], treeContext);
4942
- }
4943
-
4944
- let tableRows = this.formModel[this.fieldKeyName] || [];
4908
+ let treeContext = isTreeTable
4909
+ ? this.getTreeContext($grid)
4910
+ : null;
4911
+ let { rowField, parentField } = treeContext
4912
+ ? treeContext.fields
4913
+ : this.getTreeFields($grid);
4914
+
4915
+ let newData = this.createNewTableData(isEditTable);
4916
+ newRow = Object.assign({}, newData, editDefaultRow, rowData);
4917
+ if (isTreeTable) {
4918
+ newRow[treeContext.identityConfig.physicalIdField] = null;
4919
+ this.ensureNewTreeRowKey(newRow, treeContext);
4920
+ if (!toSibling) {
4921
+ if (parent && isRootParent(parent[rowField])) {
4922
+ this.$message.error("父节点缺少有效树键,无法新增子节点。");
4923
+ return false;
4924
+ }
4925
+ newRow[parentField] = parent
4926
+ ? parent[rowField]
4927
+ : treeContext.identityConfig.rootParentValue;
4928
+ } else {
4929
+ let siblingParentValue = obj?.row[parentField];
4930
+ newRow[parentField] = isRootParent(siblingParentValue)
4931
+ ? treeContext.identityConfig.rootParentValue
4932
+ : siblingParentValue;
4933
+ }
4934
+ } else {
4935
+ // 普通编辑表沿用原有临时 id 协议。
4936
+ newRow[rowField] = "row_"
4937
+ + new Date().valueOf()
4938
+ + Math.floor(Math.random() * 1000000);
4939
+ }
4940
+ if (treeContext) {
4941
+ this.validateNewTreeRows([newRow], treeContext);
4942
+ }
4943
+
4944
+ let tableRows = this.formModel[this.fieldKeyName] || [];
4945
4945
  // 编辑表统一使用 vxe 内部插入,避免 setValue 重载整表并丢失编辑状态。
4946
4946
  if (isEditTable) {
4947
4947
  let insertTarget = toEnd === true ? -1 : null;
@@ -4954,14 +4954,14 @@ modules = {
4954
4954
  insertTarget = -1;
4955
4955
  } else if (isTreeTable) {
4956
4956
  let nextSibling = tableRows
4957
- .slice(currentIndex + 1)
4958
- .find(
4959
- (item) =>
4960
- treeKeyEquals(
4961
- item[parentField],
4962
- obj.row[parentField]
4963
- )
4964
- );
4957
+ .slice(currentIndex + 1)
4958
+ .find(
4959
+ (item) =>
4960
+ treeKeyEquals(
4961
+ item[parentField],
4962
+ obj.row[parentField]
4963
+ )
4964
+ );
4965
4965
  insertTarget = nextSibling || -1;
4966
4966
  } else {
4967
4967
  insertTarget = tableRows[currentIndex + 1] || -1;
@@ -5047,10 +5047,10 @@ modules = {
5047
5047
  if (!$grid || rowId == null) {
5048
5048
  return;
5049
5049
  }
5050
- let { rowField } = this.getTreeFields($grid);
5051
- let sourceRow = (this.getValue() || []).find(
5052
- (item) => item && treeKeyEquals(item[rowField], rowId)
5053
- );
5050
+ let { rowField } = this.getTreeFields($grid);
5051
+ let sourceRow = (this.getValue() || []).find(
5052
+ (item) => item && treeKeyEquals(item[rowField], rowId)
5053
+ );
5054
5054
  if (!sourceRow) {
5055
5055
  return;
5056
5056
  }
@@ -5106,18 +5106,18 @@ modules = {
5106
5106
  * @param {Object} obj VXE 行参数。
5107
5107
  */
5108
5108
  async removeEditRow(obj) {
5109
- let row = obj.row;
5110
- let $grid = this.getGridTable();
5111
- await $grid.remove(row);
5112
- if (this.widget.options.isTreeTable) {
5113
- let { rowField } = this.getTreeFields($grid);
5114
- this.unregisterTreeKey(
5115
- this.treeTemporaryKeyRegistry,
5116
- row[rowField]
5117
- );
5118
- }
5119
-
5120
- // 临时编辑行由 vxe 内部删除;仅同步表单模型,避免 setValue 重载整表。
5109
+ let row = obj.row;
5110
+ let $grid = this.getGridTable();
5111
+ await $grid.remove(row);
5112
+ if (this.widget.options.isTreeTable) {
5113
+ let { rowField } = this.getTreeFields($grid);
5114
+ this.unregisterTreeKey(
5115
+ this.treeTemporaryKeyRegistry,
5116
+ row[rowField]
5117
+ );
5118
+ }
5119
+
5120
+ // 临时编辑行由 vxe 内部删除;仅同步表单模型,避免 setValue 重载整表。
5121
5121
  let tableRows = this.getValue() || [];
5122
5122
  this.formModel[this.fieldKeyName] = tableRows.filter(
5123
5123
  (item) => item._X_ROW_KEY !== row._X_ROW_KEY
@@ -5147,9 +5147,9 @@ modules = {
5147
5147
  * @returns {Promise} VXE 数据刷新结果。
5148
5148
  */
5149
5149
  updateWbs() {
5150
- let that = this;
5151
- let $grid = that.getGridTable();
5152
- let { childrenField, mapChildrenField } = this.getTreeFields($grid);
5150
+ let that = this;
5151
+ let $grid = that.getGridTable();
5152
+ let { childrenField, mapChildrenField } = this.getTreeFields($grid);
5153
5153
  let fullData = $grid.getTableData().fullData;
5154
5154
  let isEditTable = this.widget.options.isEditTable || false;
5155
5155
  let isTreeTable = this.widget.options.isTreeTable || false;
@@ -5161,26 +5161,26 @@ modules = {
5161
5161
  fullData = $grid.$refs.xTable.tableFullTreeData;
5162
5162
  }
5163
5163
  let tableRows = this.formModel[that.fieldKeyName] || [];
5164
- let map = {};
5165
- let loopDo = (item, wbs) => {
5166
- item.f_wbs = wbs;
5167
- map[item._X_ROW_KEY] = wbs;
5168
- let mappedChildren = mapChildrenField
5169
- && Array.isArray(item[mapChildrenField])
5170
- ? item[mapChildrenField]
5171
- : [];
5172
- let sourceChildren = childrenField
5173
- && Array.isArray(item[childrenField])
5174
- ? item[childrenField]
5175
- : [];
5176
- let children = mappedChildren.length
5177
- ? mappedChildren
5178
- : sourceChildren;
5179
- children.forEach((subitem, subindex) => {
5180
- let sub_wbs = wbs + "." + (subindex + 1) + "";
5181
- loopDo(subitem, sub_wbs);
5182
- });
5183
- };
5164
+ let map = {};
5165
+ let loopDo = (item, wbs) => {
5166
+ item.f_wbs = wbs;
5167
+ map[item._X_ROW_KEY] = wbs;
5168
+ let mappedChildren = mapChildrenField
5169
+ && Array.isArray(item[mapChildrenField])
5170
+ ? item[mapChildrenField]
5171
+ : [];
5172
+ let sourceChildren = childrenField
5173
+ && Array.isArray(item[childrenField])
5174
+ ? item[childrenField]
5175
+ : [];
5176
+ let children = mappedChildren.length
5177
+ ? mappedChildren
5178
+ : sourceChildren;
5179
+ children.forEach((subitem, subindex) => {
5180
+ let sub_wbs = wbs + "." + (subindex + 1) + "";
5181
+ loopDo(subitem, sub_wbs);
5182
+ });
5183
+ };
5184
5184
  fullData.forEach((item, index) => {
5185
5185
  let wbs = index + 1 + "";
5186
5186
  loopDo(item, wbs);
@@ -5202,26 +5202,26 @@ modules = {
5202
5202
  let row = obj.row;
5203
5203
  let $grid = this.getGridTable();
5204
5204
  let tableRows = this.formModel[this.fieldKeyName] || [];
5205
- let isTreeTable = this.widget.options.isTreeTable;
5206
- if (isTreeTable) {
5207
- let { rowField, parentField } = this.getTreeFields($grid);
5208
- let paretValue = row[parentField];
5209
- let siblingRows = tableRows.filter(
5210
- (item) => treeKeyEquals(item[parentField], paretValue)
5211
- );
5212
- if (siblingRows.length > 1) {
5213
- let currentIndex = tableRows.findIndex(
5214
- (item) => treeKeyEquals(item[rowField], row[rowField])
5215
- );
5216
- let index1 = siblingRows.findIndex(
5217
- (item) => treeKeyEquals(item[rowField], row[rowField])
5218
- );
5205
+ let isTreeTable = this.widget.options.isTreeTable;
5206
+ if (isTreeTable) {
5207
+ let { rowField, parentField } = this.getTreeFields($grid);
5208
+ let paretValue = row[parentField];
5209
+ let siblingRows = tableRows.filter(
5210
+ (item) => treeKeyEquals(item[parentField], paretValue)
5211
+ );
5212
+ if (siblingRows.length > 1) {
5213
+ let currentIndex = tableRows.findIndex(
5214
+ (item) => treeKeyEquals(item[rowField], row[rowField])
5215
+ );
5216
+ let index1 = siblingRows.findIndex(
5217
+ (item) => treeKeyEquals(item[rowField], row[rowField])
5218
+ );
5219
5219
  if (index1 > 0) {
5220
- let prevRow = siblingRows[index1 - 1];
5221
- let prevIndex = tableRows.findIndex(
5222
- (item) =>
5223
- treeKeyEquals(item[rowField], prevRow[rowField])
5224
- );
5220
+ let prevRow = siblingRows[index1 - 1];
5221
+ let prevIndex = tableRows.findIndex(
5222
+ (item) =>
5223
+ treeKeyEquals(item[rowField], prevRow[rowField])
5224
+ );
5225
5225
  tableRows.splice(currentIndex, 1);
5226
5226
  tableRows.splice(prevIndex, 0, row);
5227
5227
  this.setValue(tableRows);
@@ -5251,26 +5251,26 @@ modules = {
5251
5251
  let row = obj.row;
5252
5252
  let $grid = this.getGridTable();
5253
5253
  let tableRows = this.formModel[this.fieldKeyName] || [];
5254
- let isTreeTable = this.widget.options.isTreeTable;
5255
- if (isTreeTable) {
5256
- let { rowField, parentField } = this.getTreeFields($grid);
5257
- let paretValue = row[parentField];
5258
- let siblingRows = tableRows.filter(
5259
- (item) => treeKeyEquals(item[parentField], paretValue)
5260
- );
5261
- if (siblingRows.length > 1) {
5262
- let currentIndex = tableRows.findIndex(
5263
- (item) => treeKeyEquals(item[rowField], row[rowField])
5264
- );
5265
- let index1 = siblingRows.findIndex(
5266
- (item) => treeKeyEquals(item[rowField], row[rowField])
5267
- );
5254
+ let isTreeTable = this.widget.options.isTreeTable;
5255
+ if (isTreeTable) {
5256
+ let { rowField, parentField } = this.getTreeFields($grid);
5257
+ let paretValue = row[parentField];
5258
+ let siblingRows = tableRows.filter(
5259
+ (item) => treeKeyEquals(item[parentField], paretValue)
5260
+ );
5261
+ if (siblingRows.length > 1) {
5262
+ let currentIndex = tableRows.findIndex(
5263
+ (item) => treeKeyEquals(item[rowField], row[rowField])
5264
+ );
5265
+ let index1 = siblingRows.findIndex(
5266
+ (item) => treeKeyEquals(item[rowField], row[rowField])
5267
+ );
5268
5268
  if (index1 < siblingRows.length - 1) {
5269
- let nextRow = siblingRows[index1 + 1];
5270
- let nextIndex = tableRows.findIndex(
5271
- (item) =>
5272
- treeKeyEquals(item[rowField], nextRow[rowField])
5273
- );
5269
+ let nextRow = siblingRows[index1 + 1];
5270
+ let nextIndex = tableRows.findIndex(
5271
+ (item) =>
5272
+ treeKeyEquals(item[rowField], nextRow[rowField])
5273
+ );
5274
5274
  tableRows.splice(currentIndex, 1);
5275
5275
  tableRows.splice(nextIndex, 0, row);
5276
5276
  this.setValue(tableRows);
@@ -5293,19 +5293,19 @@ modules = {
5293
5293
  },
5294
5294
 
5295
5295
  /** @param {Object} obj VXE 行参数。打开当前层级的树节点排序弹窗。 */
5296
- openSortRowDialog(obj) {
5297
- let $grid = this.getGridTable();
5298
- let { rowField, parentField } = this.getTreeFields($grid);
5296
+ openSortRowDialog(obj) {
5297
+ let $grid = this.getGridTable();
5298
+ let { rowField, parentField } = this.getTreeFields($grid);
5299
5299
  let row = obj.row;
5300
5300
  let tableRows = this.formModel[this.fieldKeyName] || [];
5301
5301
 
5302
- let sortParentRow = tableRows.find(
5303
- (item) => treeKeyEquals(item[rowField], row[parentField])
5304
- );
5305
- let sortRows = tableRows.filter(
5306
- (item) =>
5307
- treeKeyEquals(item[parentField], row[parentField])
5308
- );
5302
+ let sortParentRow = tableRows.find(
5303
+ (item) => treeKeyEquals(item[rowField], row[parentField])
5304
+ );
5305
+ let sortRows = tableRows.filter(
5306
+ (item) =>
5307
+ treeKeyEquals(item[parentField], row[parentField])
5308
+ );
5309
5309
  this.sortRows = sortRows;
5310
5310
  this.sortParentRow = sortParentRow;
5311
5311
  this.showTreeMoveDialog = true;
@@ -5333,20 +5333,20 @@ modules = {
5333
5333
  this.getGridTable().reloadTreeExpand(this.sortParentRow);
5334
5334
  },
5335
5335
  });
5336
- } else {
5337
- let $grid = this.getGridTable();
5338
- let { rowField } = this.getTreeFields($grid);
5339
- let tableRows = this.formModel[this.fieldKeyName] || [];
5340
-
5341
- let ids = rows.map((item) => item[rowField]);
5342
- let hasRowKey = (value) =>
5343
- ids.some((id) => treeKeyEquals(id, value));
5344
- let index = tableRows.findIndex((item) =>
5345
- hasRowKey(item[rowField])
5346
- );
5347
- let items = tableRows.filter(
5348
- (item) => !hasRowKey(item[rowField])
5349
- );
5336
+ } else {
5337
+ let $grid = this.getGridTable();
5338
+ let { rowField } = this.getTreeFields($grid);
5339
+ let tableRows = this.formModel[this.fieldKeyName] || [];
5340
+
5341
+ let ids = rows.map((item) => item[rowField]);
5342
+ let hasRowKey = (value) =>
5343
+ ids.some((id) => treeKeyEquals(id, value));
5344
+ let index = tableRows.findIndex((item) =>
5345
+ hasRowKey(item[rowField])
5346
+ );
5347
+ let items = tableRows.filter(
5348
+ (item) => !hasRowKey(item[rowField])
5349
+ );
5350
5350
  rows.forEach((row, index1) => {
5351
5351
  items.splice(index + index1, 0, row);
5352
5352
  });