cloud-web-corejs 1.0.54-dev.780 → 1.0.54-dev.782

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 (34) hide show
  1. package/package.json +1 -1
  2. package/src/components/VabUpload/index.vue +242 -239
  3. package/src/components/VabUpload/mixins.js +1971 -1888
  4. package/src/components/obsUpload/index.vue +234 -231
  5. package/src/components/obsUpload/mixins.js +1542 -1476
  6. package/src/components/vb-tabs/x-tabs.vue +7 -3
  7. package/src/components/xform/docs//346/240/221/350/241/250rowField/346/224/266/346/225/233-/350/220/275/345/234/260/346/226/207/346/241/243.md +1427 -0
  8. package/src/components/xform/form-designer/form-widget/dialog/formDialog.vue +2 -2
  9. package/src/components/xform/form-designer/form-widget/dialog/formDrawer.vue +2 -2
  10. package/src/components/xform/form-designer/form-widget/dialog/searchFormDialog.vue +2 -2
  11. package/src/components/xform/form-designer/form-widget/field-widget/baseAttachment-widget.vue +1 -0
  12. package/src/components/xform/form-designer/form-widget/field-widget/copy_button-widget.vue +11 -2
  13. package/src/components/xform/form-designer/form-widget/field-widget/vabUpload-widget.vue +369 -368
  14. package/src/components/xform/form-designer/setting-panel/property-editor/field-baseAttachment/baseAttachment-fileTypes-editor.vue +23 -0
  15. package/src/components/xform/form-designer/setting-panel/property-editor/field-vabUpload/vabUpload-fileTypes-editor.vue +23 -0
  16. package/src/components/xform/form-designer/setting-panel/property-editor/fileTypesEditorMixin.js +116 -0
  17. package/src/components/xform/form-designer/setting-panel/property-editor/fileTypesSelect.vue +176 -0
  18. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +7 -3
  19. package/src/components/xform/form-render/container-item/data-table-item.vue +1 -1
  20. package/src/components/xform/form-render/container-item/data-table-mixin.js +1469 -428
  21. package/src/components/xform/form-render/indexMixin.js +379 -240
  22. package/src/components/xform/lang/en-US.js +13 -0
  23. package/src/components/xform/lang/zh-CN.js +13 -0
  24. package/src/components/xform/mixins/defaultHandle.js +234 -28
  25. package/src/components/xform/mixins/scriptHttp.js +4 -4
  26. package/src/components/xform/utils/treeTableUtil.js +1265 -0
  27. package/src/components/xform/utils/util.js +29 -14
  28. package/src/microRouter/index.js +3 -0
  29. package/src/mixins/tableTree/index.js +77 -14
  30. package/src/utils/menuAdapter.js +277 -277
  31. package/src/utils/resolveViewComponent.js +203 -198
  32. package/src/utils/vab.js +6 -3
  33. package/src/views/user/form/vform/render.vue +17 -2
  34. package/src/views/user/form/view/list.vue +11 -10
@@ -52,9 +52,19 @@ import formulaDialog from "@base/components/xform/form-designer/form-widget/dial
52
52
  import baseFormulaDialog from "@base/components/xform/form-designer/form-widget/dialog/baseFormulaDialog.vue";
53
53
  import QRCode from "qrcode";
54
54
  import { openLuckysheetDialog } from "@base/components/luckysheet";
55
- import {
56
- openComponentDialog as mountComponentDialog,
57
- } from "@base/utils/componentDialog";
55
+ import {
56
+ openComponentDialog as mountComponentDialog,
57
+ } from "@base/utils/componentDialog";
58
+ import {
59
+ buildTreeSubmitData,
60
+ copyTreeData,
61
+ getTreeLoadState,
62
+ isTemporaryTreeKey,
63
+ resolveTreeCopyConfig,
64
+ resolveTreeFields,
65
+ resolveTreeIdentityConfig,
66
+ resolveTreeSubmitConfig,
67
+ } from "../utils/treeTableUtil";
58
68
 
59
69
  let modules = {};
60
70
  const baseRefUtil = {
@@ -1283,12 +1293,12 @@ modules = {
1283
1293
 
1284
1294
  return hasModifyItem;
1285
1295
  },
1286
- loadFormData(options) {
1287
- let config = options.config || {};
1296
+ loadFormData(options = {}) {
1297
+ let config = options.config || {};
1288
1298
  let that = this;
1289
1299
  let formConfig = this.formConfig;
1290
1300
  let entity = formConfig.entity;
1291
- let dataId = this.dataId;
1301
+ let dataId = options.dataId ?? this.dataId;
1292
1302
  let reportTemplate = this.reportTemplate;
1293
1303
  let scriptCode = formConfig.formScriptCode || "getOne";
1294
1304
 
@@ -1317,11 +1327,13 @@ modules = {
1317
1327
  ...formScriptParam,
1318
1328
  },
1319
1329
  },
1320
- ...defaultOption,
1321
- success: (res) => {
1322
- formData = res.objx || {};
1323
- this.setFormData(formData);
1324
- options.callback && options.callback(formData);
1330
+ ...defaultOption,
1331
+ success: (res) => {
1332
+ formData = res.objx || {};
1333
+ this.setFormData(formData, {
1334
+ replace: options.replace === true,
1335
+ });
1336
+ options.callback && options.callback(formData);
1325
1337
  },
1326
1338
  ...config,
1327
1339
  });
@@ -1989,12 +2001,22 @@ modules = {
1989
2001
  n
1990
2002
  );
1991
2003
  },
1992
- setFormData: function (e) {
1993
- let t = this;
2004
+ setFormData: function (e, options = {}) {
2005
+ let t = this;
1994
2006
  /*Object.keys(this.formDataModel).forEach(function (i) {
1995
2007
  e && e.hasOwnProperty(i) && (t.formDataModel[i] = baseRefUtil.deepClone(e[i]));
1996
2008
  });*/
1997
- Object.assign(t.formDataModel, baseRefUtil.deepClone(e));
2009
+ let nextFormData = baseRefUtil.deepClone(e || {});
2010
+ if (options.replace === true) {
2011
+ Object.keys(t.formDataModel).forEach((key) => {
2012
+ t.$delete(t.formDataModel, key);
2013
+ });
2014
+ Object.keys(nextFormData).forEach((key) => {
2015
+ t.$set(t.formDataModel, key, nextFormData[key]);
2016
+ });
2017
+ } else {
2018
+ Object.assign(t.formDataModel, nextFormData);
2019
+ }
1998
2020
  this.currentFormData = e || {};
1999
2021
  this.broadcast("ContainerItem", "setFormData", this.formDataModel),
2000
2022
  this.broadcast("FieldWidget", "setFormData", this.formDataModel);
@@ -3971,13 +3993,206 @@ modules = {
3971
3993
  reject(msg);
3972
3994
  });
3973
3995
  });
3974
- },
3975
- getSubData2(flag) {
3996
+ },
3997
+ getTreeTableUtilContext(tableTarget, $grid, source) {
3998
+ let treeConfig = $grid?.treeConfig || {};
3999
+ let originOption
4000
+ = $grid?.params?.originOption || $grid?.originOption || {};
4001
+ let fields = resolveTreeFields(treeConfig, {
4002
+ strict: true,
4003
+ });
4004
+ let identityConfig = resolveTreeIdentityConfig(
4005
+ treeConfig,
4006
+ originOption.treeIdentityConfig || {}
4007
+ );
4008
+ let copyConfig = resolveTreeCopyConfig(
4009
+ fields,
4010
+ identityConfig,
4011
+ originOption.treeCopyConfig || {}
4012
+ );
4013
+ let submitConfig = resolveTreeSubmitConfig(
4014
+ originOption.treeSubmitConfig || {}
4015
+ );
4016
+ let temporaryKeyRegistry
4017
+ = tableTarget?.treeTemporaryKeyRegistry || new Set();
4018
+ let knownFormalKeyRegistry
4019
+ = tableTarget?.treeKnownFormalKeyRegistry || new Set();
4020
+
4021
+ this.collectTreeRuntimeRows(source || [], treeConfig).forEach((row) => {
4022
+ let rowValue = row[fields.rowField];
4023
+ let marker = row._X_TREE_TEMP_KEY;
4024
+ if (marker !== undefined && marker !== null) {
4025
+ temporaryKeyRegistry.add(marker);
4026
+ }
4027
+ if (
4028
+ rowValue !== undefined
4029
+ && rowValue !== null
4030
+ && rowValue !== ""
4031
+ && !isTemporaryTreeKey(row, fields.rowField, identityConfig)
4032
+ ) {
4033
+ knownFormalKeyRegistry.add(rowValue);
4034
+ }
4035
+ });
4036
+
4037
+ return {
4038
+ fields,
4039
+ identityConfig,
4040
+ copyConfig,
4041
+ submitConfig,
4042
+ temporaryKeyRegistry,
4043
+ knownFormalKeyRegistry,
4044
+ };
4045
+ },
4046
+ collectTreeRuntimeRows(rows, treeConfig) {
4047
+ let result = [];
4048
+ let visited = new WeakSet();
4049
+ let childrenFields = [
4050
+ treeConfig?.children,
4051
+ treeConfig?.mapChildren || "_X_ROW_CHILD",
4052
+ ].filter(Boolean);
4053
+ let visit = (items) => {
4054
+ if (!Array.isArray(items)) return;
4055
+ items.forEach((item) => {
4056
+ if (!item || typeof item !== "object" || visited.has(item)) return;
4057
+ visited.add(item);
4058
+ result.push(item);
4059
+ childrenFields.forEach((field) => {
4060
+ visit(item[field]);
4061
+ });
4062
+ });
4063
+ };
4064
+ visit(rows);
4065
+ return result;
4066
+ },
4067
+ getTreeRuntimeSource($grid, source) {
4068
+ let result = Array.isArray(source) ? source.slice() : [];
4069
+ let runtimeSources = [
4070
+ $grid?.getTableData?.()?.fullData,
4071
+ $grid?.$refs?.xTable?.tableFullTreeData,
4072
+ ];
4073
+ runtimeSources.forEach((rows) => {
4074
+ if (!Array.isArray(rows)) return;
4075
+ rows.forEach((row) => {
4076
+ if (!result.includes(row)) result.push(row);
4077
+ });
4078
+ });
4079
+ return result;
4080
+ },
4081
+ getTreeUnloadedRows($grid, treeConfig) {
4082
+ let xTable = $grid?.$refs?.xTable;
4083
+ let fullData = this.getTreeRuntimeSource($grid, []);
4084
+ return this.collectTreeRuntimeRows(fullData, treeConfig).filter(
4085
+ (row) =>
4086
+ getTreeLoadState(row, {
4087
+ fields: resolveTreeFields(treeConfig),
4088
+ isTreeNodeLoaded:
4089
+ typeof xTable?.isTreeExpandLoaded === "function"
4090
+ ? (targetRow) => xTable.isTreeExpandLoaded(targetRow)
4091
+ : null,
4092
+ }).requiresLoad
4093
+ );
4094
+ },
4095
+ async ensureTreeTableLoadState(formTemplateTableDTO, operation) {
4096
+ let tableTarget = this.getWidgetRef(formTemplateTableDTO.uniqueName);
4097
+ let $grid = tableTarget?.getGridTable?.();
4098
+ let treeConfig = $grid?.treeConfig;
4099
+ if (!$grid || !treeConfig?.lazy) return;
4100
+
4101
+ let treeContext = this.getTreeTableUtilContext(
4102
+ tableTarget,
4103
+ $grid,
4104
+ this.formDataModel[formTemplateTableDTO.name] || []
4105
+ );
4106
+ let strategy = operation === "copy"
4107
+ ? treeContext.copyConfig.lazyStrategy
4108
+ : treeContext.submitConfig.unloadedChildrenStrategy;
4109
+ if (operation === "submit" && strategy === "omit") return;
4110
+
4111
+ let xTable = $grid.$refs?.xTable;
4112
+ if (
4113
+ !xTable
4114
+ || typeof xTable.isTreeExpandLoaded !== "function"
4115
+ || typeof $grid.setTreeExpand !== "function"
4116
+ ) {
4117
+ throw new Error(
4118
+ `[${formTemplateTableDTO.tableAlias}]无法读取树节点加载状态`
4119
+ );
4120
+ }
4121
+
4122
+ let unloadedRows = this.getTreeUnloadedRows($grid, treeConfig);
4123
+ if (!unloadedRows.length) return;
4124
+ if (strategy !== "loadAll") {
4125
+ let action = operation === "copy" ? "复制" : "保存";
4126
+ throw new Error(
4127
+ `[${formTemplateTableDTO.tableAlias}]存在尚未加载的树节点,请先展开后再${action}`
4128
+ );
4129
+ }
4130
+
4131
+ let loopCount = 0;
4132
+ while (unloadedRows.length) {
4133
+ if (loopCount++ > 100) {
4134
+ throw new Error(
4135
+ `[${formTemplateTableDTO.tableAlias}]树节点层级过深,无法完成加载`
4136
+ );
4137
+ }
4138
+ let currentRows = unloadedRows;
4139
+ await Promise.all(
4140
+ currentRows.map((row) => $grid.setTreeExpand(row, true))
4141
+ );
4142
+ unloadedRows = this.getTreeUnloadedRows($grid, treeConfig);
4143
+ if (
4144
+ unloadedRows.length === currentRows.length
4145
+ && unloadedRows.every((row) => currentRows.includes(row))
4146
+ ) {
4147
+ throw new Error(
4148
+ `[${formTemplateTableDTO.tableAlias}]树节点加载未完成,已停止保存`
4149
+ );
4150
+ }
4151
+ }
4152
+ },
4153
+ async ensureTreeTableReadyForSubmit(formTemplateTableDTO) {
4154
+ return this.ensureTreeTableLoadState(
4155
+ formTemplateTableDTO,
4156
+ "submit"
4157
+ );
4158
+ },
4159
+ async prepareTreeTablesForCopy() {
4160
+ let treeTables = this.getFormTemplateTableDTOs().filter(
4161
+ (item) => item.isTreeTable
4162
+ );
4163
+ for (let index = 0; index < treeTables.length; index++) {
4164
+ await this.ensureTreeTableLoadState(treeTables[index], "copy");
4165
+ }
4166
+ },
4167
+ async buildSubmitPayload(option) {
4168
+ let treeTables = this.getFormTemplateTableDTOs().filter(
4169
+ (item) => item.isTreeTable
4170
+ );
4171
+ for (let index = 0; index < treeTables.length; index++) {
4172
+ await this.ensureTreeTableReadyForSubmit(treeTables[index]);
4173
+ }
4174
+ let formDataSnapshot = this.$baseLodash.cloneDeep(this.formDataModel);
4175
+ let subData = this.getSubData2(false, {
4176
+ mode: "submit",
4177
+ formDataModel: formDataSnapshot,
4178
+ });
4179
+ if (option) {
4180
+ let realFormData = {};
4181
+ Object.keys(option).forEach((key) => {
4182
+ realFormData[option[key]] = subData[key];
4183
+ });
4184
+ return realFormData;
4185
+ }
4186
+ return subData;
4187
+ },
4188
+ getSubData2(flag, context = {}) {
3976
4189
  let that = this;
3977
4190
  let formCode = this.reportTemplate.formCode;
3978
4191
  let tableFiledMap = this.getTableFiledMap();
3979
4192
  let formTemplateTableDTOs = this.getFormTemplateTableDTOs();
3980
- let formDataModel = this.formDataModel;
4193
+ let formDataModel
4194
+ = context.formDataModel
4195
+ || this.$baseLodash.cloneDeep(this.formDataModel);
3981
4196
  let formConfig = this.formConfig;
3982
4197
 
3983
4198
  let res = {};
@@ -4012,40 +4227,32 @@ modules = {
4012
4227
  newFormData[tableAlias] = item ?? null;
4013
4228
  } else {
4014
4229
  if (formTemplateTableDTO.isTreeTable) {
4015
- let tableTarget = this.getWidgetRef(
4016
- formTemplateTableDTO.uniqueName
4017
- );
4018
- let $grid = tableTarget.getGridTable();
4019
- let parentField = $grid.treeConfig.parentField;
4020
- let treeTaBm = newFormData.treeTaBm || [];
4021
- treeTaBm.push(formTemplateTableDTO.tableAlias);
4022
- newFormData.treeTaBm = treeTaBm;
4023
- let tableDatas = this.$baseLodash.cloneDeep(
4024
- formDataModel[formTemplateTableDTO.name] || []
4025
- );
4026
- tableDatas.forEach((tableData) => {
4027
- if (
4028
- tableData.id &&
4029
- tableData.id.startsWith &&
4030
- tableData.id.startsWith("row_")
4031
- ) {
4032
- tableData.id = null;
4033
- }
4034
- if (
4035
- tableData[parentField] &&
4036
- tableData[parentField].startsWith &&
4037
- tableData[parentField].startsWith("row_")
4038
- ) {
4039
- tableData[parentField] = null;
4040
- }
4041
- if (tableData[parentField] === undefined)
4042
- tableData[parentField] = null;
4043
- });
4044
- tableDatas = tableDatas.filter((item) => {
4045
- return item[parentField] === 0 || item[parentField] === null;
4046
- });
4047
- newFormData[tableAlias] = tableDatas;
4048
- } else {
4230
+ let tableTarget = this.getWidgetRef(
4231
+ formTemplateTableDTO.uniqueName
4232
+ );
4233
+ let $grid = tableTarget.getGridTable();
4234
+ let treeTaBm = newFormData.treeTaBm || [];
4235
+ treeTaBm.push(formTemplateTableDTO.tableAlias);
4236
+ newFormData.treeTaBm = treeTaBm;
4237
+ let modelRows = formDataModel[formTemplateTableDTO.name];
4238
+ let sourceRows = Array.isArray(modelRows) ? modelRows : [];
4239
+ let treeContext = this.getTreeTableUtilContext(
4240
+ tableTarget,
4241
+ $grid,
4242
+ sourceRows
4243
+ );
4244
+ newFormData[tableAlias] = buildTreeSubmitData(sourceRows, {
4245
+ ...treeContext,
4246
+ submitConfig:
4247
+ context.mode === "submit"
4248
+ ? treeContext.submitConfig
4249
+ : {
4250
+ ...treeContext.submitConfig,
4251
+ orphanParentStrategy: "preserve",
4252
+ },
4253
+ strict: context.mode === "submit",
4254
+ });
4255
+ } else {
4049
4256
  newFormData[tableAlias] =
4050
4257
  formDataModel[formTemplateTableDTO.name] ?? null;
4051
4258
  }
@@ -4242,10 +4449,10 @@ modules = {
4242
4449
  this.$baseReload();
4243
4450
  }
4244
4451
  },
4245
- reloadForm(option) {
4246
- let target1 = this.$attrs["parent-target"];
4247
- let target2 = target1?.$attrs["parent-target"];
4248
- let target = !target2 ? this : target1;
4452
+ reloadForm(option) {
4453
+ let target1 = this.$attrs["parent-target"];
4454
+ let target2 = target1?.$attrs["parent-target"];
4455
+ let target = !target2 ? this : target1;
4249
4456
  /* if (this.$attrs['reloadDialog'] !== true) {
4250
4457
  target = this.$attrs['parent-target'];
4251
4458
  }else{
@@ -4255,8 +4462,44 @@ modules = {
4255
4462
  for (let key in updateParam) {
4256
4463
  target.$emit("update:" + key, updateParam[key]);
4257
4464
  }*/
4258
- target.$baseReload(option);
4259
- },
4465
+ try {
4466
+ let reloadListener = target?.$listeners?.reload;
4467
+ if (Array.isArray(reloadListener)) {
4468
+ return Promise.all(
4469
+ reloadListener.map((listener) => listener(target, option))
4470
+ );
4471
+ }
4472
+ if (typeof reloadListener === "function") {
4473
+ return Promise.resolve(reloadListener(target, option));
4474
+ }
4475
+ return Promise.resolve(target.$baseReload(option));
4476
+ } catch (error) {
4477
+ return Promise.reject(error);
4478
+ }
4479
+ },
4480
+ async reloadSavedFormData(option = {}) {
4481
+ let updateParam = option.updateParam || {};
4482
+ let nextDataId = updateParam.dataId ?? this.dataId;
4483
+ let nextFormCode = updateParam.formCode;
4484
+ if (nextDataId === undefined || nextDataId === null || nextDataId === "") {
4485
+ throw new Error("缺少保存后的数据 ID,无法重新加载表单。");
4486
+ }
4487
+ let response = await this.loadFormData({
4488
+ dataId: nextDataId,
4489
+ replace: true,
4490
+ });
4491
+ if (!response || response.type !== "success") {
4492
+ throw new Error(response?.content || "保存后的表单数据重新加载失败。");
4493
+ }
4494
+ if (nextDataId !== undefined && nextDataId !== null) {
4495
+ this.$emit("update:dataId", nextDataId);
4496
+ }
4497
+ if (nextFormCode) {
4498
+ this.$emit("update:formCode", nextFormCode);
4499
+ }
4500
+ await new Promise((resolve) => this.$nextTick(resolve));
4501
+ return response;
4502
+ },
4260
4503
  saveForm(callback) {
4261
4504
  this.validateForm((valid) => {
4262
4505
  if (!valid) return;
@@ -4651,181 +4894,69 @@ modules = {
4651
4894
  getFormRef() {
4652
4895
  return this;
4653
4896
  },
4654
- isObject(value) {
4655
- return Object.prototype.toString.call(value) === "[object Object]";
4656
- },
4657
- handleCopyTreeData(value, widget) {
4658
- if (!value || !widget || !Array.isArray(value) || value.length === 0)
4659
- return;
4660
-
4661
- // 获取树形表格配置
4662
- const widgetName = widget.options.name;
4663
- const gridRef = this.getWidgetRef(widgetName);
4664
- const $grid = gridRef.getGridTable();
4665
- const treeConfig = $grid.treeConfig;
4666
- const idField = treeConfig.rowField || "id";
4667
- const parentField = treeConfig.parentField;
4668
- const childrenField = treeConfig.children;
4669
-
4670
- // 初始化 id 生成器
4671
- const baseTime = new Date().valueOf();
4672
- let counter = 0;
4673
- const idMap = {}; // 旧 id 到新 id 的映射
4674
-
4675
- // 生成新的 id(以 'row_' 开头)
4676
- const generateNewId = () => {
4677
- counter++;
4678
- return `row_${baseTime}_${counter}_${Math.floor(
4679
- Math.random() * 1000000
4680
- )}`;
4681
- };
4682
-
4683
- // 收集所有节点(包括扁平数组和嵌套结构中的节点),使用 Set 去重
4684
- const allNodes = [];
4685
- const processedNodes = new WeakSet(); // 使用 WeakSet 来跟踪已处理的节点,避免重复处理
4686
-
4687
- const collectAllNodes = (nodes) => {
4688
- if (!Array.isArray(nodes)) return;
4689
- nodes.forEach((node) => {
4690
- if (node && this.isObject(node) && !processedNodes.has(node)) {
4691
- processedNodes.add(node);
4692
- allNodes.push(node);
4693
- // 递归收集嵌套在 childrenField 中的节点
4694
- if (node[childrenField] && Array.isArray(node[childrenField])) {
4695
- collectAllNodes(node[childrenField]);
4696
- }
4697
- }
4698
- });
4699
- };
4700
- collectAllNodes(value);
4701
-
4702
- // 第一步:为所有节点生成新的 id 并建立映射
4703
- // 注意:即使 id 为 null,也要生成新 id
4704
- allNodes.forEach((node) => {
4705
- if (node[idField] !== undefined && node[idField] !== null) {
4706
- // 如果节点已有 id,记录映射关系
4707
- const oldId = node[idField];
4708
- const newId = generateNewId();
4709
- idMap[oldId] = newId;
4710
- node[idField] = newId;
4711
- } else {
4712
- // 如果节点 id 为 null 或 undefined,直接生成新 id
4713
- const newId = generateNewId();
4714
- node[idField] = newId;
4715
- }
4716
- });
4717
-
4718
- // 第二步:更新所有节点的 parentField(使用映射的新 id)
4719
- allNodes.forEach((node) => {
4720
- if (
4721
- node[parentField] !== undefined &&
4722
- node[parentField] !== null &&
4723
- node[parentField] !== 0
4724
- ) {
4725
- const oldParentId = node[parentField];
4726
- // 如果父节点的旧 id 在映射中,更新为新 id
4727
- if (idMap[oldParentId]) {
4728
- node[parentField] = idMap[oldParentId];
4729
- } else {
4730
- // 如果父节点不在映射中(可能是外部引用或已处理),尝试在 allNodes 中查找
4731
- const parentNode = allNodes.find((n) => n[idField] === oldParentId);
4732
- if (parentNode) {
4733
- node[parentField] = parentNode[idField];
4734
- } else {
4735
- // 找不到父节点,设置为 0(根节点)
4736
- node[parentField] = 0;
4737
- }
4738
- }
4739
- }
4740
- });
4741
-
4742
- // 第三步:更新嵌套的 childrenField 结构中的 parentField
4743
- const updateNestedChildrenParent = (node) => {
4744
- if (
4745
- !node ||
4746
- !node[childrenField] ||
4747
- !Array.isArray(node[childrenField]) ||
4748
- node[childrenField].length === 0
4749
- ) {
4750
- return;
4751
- }
4752
-
4753
- node[childrenField].forEach((child) => {
4754
- if (child && node[idField]) {
4755
- child[parentField] = node[idField];
4756
- }
4757
- updateNestedChildrenParent(child);
4758
- });
4759
- };
4760
- allNodes.forEach((node) => updateNestedChildrenParent(node));
4761
-
4762
- // 第四步:处理节点中的其他数组字段(如子表单、附件列表等)
4763
- // 注意:需要跳过树形结构相关的字段,避免误处理
4764
- const treeRelatedFields = new Set([
4765
- idField,
4766
- parentField,
4767
- childrenField,
4768
- "_X_ROW_KEY",
4769
- "_X_ROW_CHILD",
4770
- "f_wbs",
4771
- ]);
4772
- const processedArrayNodes = new WeakSet(); // 跟踪已处理的数组节点,避免重复处理
4773
-
4774
- const processNodeArrayFields = (node) => {
4775
- if (!node || !this.isObject(node)) return;
4776
-
4777
- // 如果节点已经在 allNodes 中(是树形节点),跳过处理,避免覆盖已生成的 id
4778
- if (processedNodes.has(node)) {
4779
- return;
4780
- }
4781
-
4782
- Object.keys(node).forEach((key) => {
4783
- // 跳过树形结构相关字段
4784
- if (treeRelatedFields.has(key)) {
4785
- return;
4786
- }
4787
-
4788
- const fieldValue = node[key];
4789
-
4790
- // 处理数组字段
4791
- if (Array.isArray(fieldValue) && fieldValue.length > 0) {
4792
- fieldValue.forEach((item) => {
4793
- if (this.isObject(item)) {
4794
- // 如果这个对象是树形节点(在 allNodes 中),跳过处理
4795
- if (processedNodes.has(item)) {
4796
- return;
4797
- }
4798
-
4799
- // 只有非树形节点的数组元素才需要清空 id
4800
- if (item.id !== undefined) {
4801
- item.id = null;
4802
- }
4803
- this.handleCopyDefaultData(item);
4804
-
4805
- // 标记已处理,避免重复处理
4806
- if (!processedArrayNodes.has(item)) {
4807
- processedArrayNodes.add(item);
4808
- processNodeArrayFields(item);
4809
- }
4810
- }
4811
- });
4812
- return;
4813
- }
4814
-
4815
- // 处理嵌套对象
4816
- if (this.isObject(fieldValue)) {
4817
- // 如果这个对象是树形节点,跳过处理
4818
- if (!processedNodes.has(fieldValue)) {
4819
- processNodeArrayFields(fieldValue);
4820
- }
4821
- }
4822
- });
4823
- };
4824
-
4825
- // 处理所有节点中的其他数组字段
4826
- allNodes.forEach((node) => processNodeArrayFields(node));
4827
- },
4828
- handleCopyDefaultData(data) {
4897
+ isObject(value) {
4898
+ return Object.prototype.toString.call(value) === "[object Object]";
4899
+ },
4900
+ handleCopyTreeData(value, widget) {
4901
+ if (!Array.isArray(value) || !widget) return;
4902
+
4903
+ let widgetName = widget.options.name;
4904
+ let gridRef = this.getWidgetRef(widgetName);
4905
+ let $grid = gridRef?.getGridTable?.();
4906
+ if (!$grid) {
4907
+ throw new Error(`[${widgetName}]树表尚未初始化`);
4908
+ }
4909
+ if (
4910
+ $grid.treeConfig?.lazy
4911
+ && this.getTreeUnloadedRows($grid, $grid.treeConfig).length
4912
+ ) {
4913
+ throw new Error(
4914
+ `[${widgetName}]存在尚未加载的树节点,无法复制不完整子树`
4915
+ );
4916
+ }
4917
+
4918
+ // prepareTreeTablesForCopy 会在深拷贝及 copyDataHandle 之前完成 loadAll,
4919
+ // 此处只消费准备好的副本;不能再混入 VXE 活对象,否则会覆盖脚本修改并产生同键冲突。
4920
+ let sourceRows = value;
4921
+ if (!sourceRows.length) return;
4922
+
4923
+ let treeContext = this.getTreeTableUtilContext(
4924
+ gridRef,
4925
+ $grid,
4926
+ sourceRows
4927
+ );
4928
+ let copiedRows = copyTreeData(sourceRows, {
4929
+ ...treeContext,
4930
+ temporaryKeyRegistry: new Set(
4931
+ treeContext.temporaryKeyRegistry
4932
+ ),
4933
+ targetRows: [],
4934
+ });
4935
+ let fields = treeContext.fields;
4936
+ let ignoredFields = new Set([
4937
+ fields.rowField,
4938
+ fields.parentField,
4939
+ fields.childrenField,
4940
+ fields.mapChildrenField,
4941
+ fields.hasChildField,
4942
+ treeContext.identityConfig.physicalIdField,
4943
+ "_X_ROW_KEY",
4944
+ "_X_ROW_CHILD",
4945
+ "_X_TREE_TEMP_KEY",
4946
+ "f_wbs",
4947
+ ]);
4948
+
4949
+ copiedRows.forEach((row) => {
4950
+ this.handleCopyDefaultData(row);
4951
+ Object.keys(row).forEach((key) => {
4952
+ if (ignoredFields.has(key) || !Array.isArray(row[key])) return;
4953
+ this.handleCopyData(row[key], key);
4954
+ });
4955
+ });
4956
+
4957
+ value.splice(0, value.length, ...copiedRows);
4958
+ },
4959
+ handleCopyDefaultData(data) {
4829
4960
  data.create_by = null;
4830
4961
  data._create_by = null;
4831
4962
  data.create_date = null;
@@ -4880,14 +5011,22 @@ modules = {
4880
5011
  });
4881
5012
  }
4882
5013
  },
4883
- openCopyEditTab(copyData) {
4884
- let formData = this.$baseLodash.cloneDeep(copyData || this.formDataModel);
4885
- if (formData) {
4886
- this.handleCopyData(formData);
4887
- }
4888
-
4889
- this.$emit("openCopyEditTab", formData);
4890
- },
5014
+ async openCopyEditTab(copyData) {
5015
+ try {
5016
+ await this.prepareTreeTablesForCopy();
5017
+ let formData = this.$baseLodash.cloneDeep(
5018
+ copyData || this.formDataModel
5019
+ );
5020
+ if (formData) {
5021
+ this.handleCopyData(formData);
5022
+ }
5023
+ this.$emit("openCopyEditTab", formData);
5024
+ return true;
5025
+ } catch (error) {
5026
+ this.$message.error(error?.message || "树表数据尚未准备完成");
5027
+ return false;
5028
+ }
5029
+ },
4891
5030
 
4892
5031
  async exportPdf() {
4893
5032
  // statusEl.style.display = 'block';