cloud-web-corejs 1.0.54-dev.714 → 1.0.54-dev.716
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/xform/XFormTabContainer//346/226/260/345/242/236/351/241/265/347/255/276/346/250/241/345/274/217-/350/220/275/345/234/260/345/256/236/346/226/275/346/226/207/346/241/243.md +232 -0
- package/src/components/xform/docs//345/257/271/346/257/224/345/212/237/350/203/275/350/220/275/345/234/260/346/226/271/346/241/210.md +563 -0
- package/src/components/xform/form-designer/form-widget/field-widget/cascader-widget.vue +48 -0
- package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +60 -0
- package/src/components/xform/form-designer/form-widget/field-widget/form-item-wrapper.vue +116 -13
- package/src/components/xform/form-designer/form-widget/field-widget/mixins/autocomplete-mixin.js +5 -0
- package/src/components/xform/form-designer/form-widget/field-widget/mixins/vabsearch-mixin.js +13 -0
- package/src/components/xform/form-designer/form-widget/field-widget/status-widget.vue +11 -0
- package/src/components/xform/form-designer/form-widget/field-widget/switch-widget.vue +9 -1
- package/src/components/xform/form-designer/widget-panel/index.vue +29 -0
- package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +183 -0
- package/src/components/xform/form-render/compareDelList.vue +83 -0
- package/src/components/xform/form-render/compareView.vue +76 -0
- package/src/components/xform/form-render/container-item/data-table-item.vue +15 -0
- package/src/components/xform/form-render/container-item/data-table-mixin.js +19 -0
- package/src/components/xform/form-render/index.vue +8 -1
- package/src/components/xform/form-render/indexMixin.js +798 -3
- package/src/components/xform/lang/en-US_extension.js +4 -0
- package/src/components/xform/lang/zh-CN.js +4 -0
- package/src/components/xform/mixins/defaultHandle.js +17 -0
|
@@ -180,6 +180,13 @@ modules = {
|
|
|
180
180
|
clearCETriggerFlag: () => {
|
|
181
181
|
this.changeEventTriggerFlag = false;
|
|
182
182
|
},
|
|
183
|
+
// ===== 表单对比:下发给字段侧(form-item-wrapper)=====
|
|
184
|
+
getIsCompare: () => e.isCompare,
|
|
185
|
+
getOldFormData: () => e.oldFormData,
|
|
186
|
+
getCompareRowKey: () => e.compareRowKey,
|
|
187
|
+
getCompareExcludeTypes: () => e.compareExcludeTypes,
|
|
188
|
+
// 字段级排除判定(系统审计字段默认排除 + 业务配置的 compareExcludeFields)
|
|
189
|
+
isCompareExcludeField: (name) => e.isCompareExcludeField(name),
|
|
183
190
|
};
|
|
184
191
|
},
|
|
185
192
|
data() {
|
|
@@ -235,6 +242,24 @@ modules = {
|
|
|
235
242
|
univerDialogOption: null,
|
|
236
243
|
showUniverDialog: false,
|
|
237
244
|
changeEventTriggerFlag: false, //记录onChange事件是否由用户操作触发
|
|
245
|
+
|
|
246
|
+
// ===== 表单对比(变更前/变更后)=====
|
|
247
|
+
isCompare: false, // 对比开关
|
|
248
|
+
oldFormData: {}, // 旧整单数据(主表字段 + 各子表数组)
|
|
249
|
+
compareRowKey: "uuid", // 子表行匹配主键,可被 enableCompare(options)/formConfig 覆盖
|
|
250
|
+
compareExcludeTypes: [
|
|
251
|
+
// 不参与对比的字段类型(附件/上传类等)
|
|
252
|
+
"file-upload",
|
|
253
|
+
"picture-upload",
|
|
254
|
+
"baseAttachment",
|
|
255
|
+
"vabUpload",
|
|
256
|
+
"vabUpload2",
|
|
257
|
+
"singleUpload",
|
|
258
|
+
],
|
|
259
|
+
// 按「字段名(keyName)」不参与对比,可被 enableCompare(options.excludeFields)/formConfig 覆盖追加
|
|
260
|
+
compareExcludeFields: [],
|
|
261
|
+
showCompareDelList: false,
|
|
262
|
+
compareDelListOption: {},
|
|
238
263
|
};
|
|
239
264
|
},
|
|
240
265
|
computed: {
|
|
@@ -2107,6 +2132,778 @@ modules = {
|
|
|
2107
2132
|
? baseRefUtil.getAllContainerWidgets(e)
|
|
2108
2133
|
: baseRefUtil.getAllContainerWidgets(this.formJsonObj.widgetList);
|
|
2109
2134
|
},
|
|
2135
|
+
|
|
2136
|
+
// ===================== 表单对比(变更前/变更后)=====================
|
|
2137
|
+
/**
|
|
2138
|
+
* 开启对比。可只传主表旧值,也可传"主表+任意数量子表数组"的整单旧值。
|
|
2139
|
+
* @param {Object} oldData 旧整单数据(结构与 getFormData 一致)
|
|
2140
|
+
* @param {Object} [options] { compareRowKey, excludeTypes }
|
|
2141
|
+
*/
|
|
2142
|
+
enableCompare: function (oldData, options) {
|
|
2143
|
+
options = options || {};
|
|
2144
|
+
oldData = oldData || {};
|
|
2145
|
+
let rowKey =
|
|
2146
|
+
options.compareRowKey ||
|
|
2147
|
+
(this.formConfig && this.formConfig.compareRowKey) ||
|
|
2148
|
+
this.compareRowKey;
|
|
2149
|
+
this.compareRowKey = rowKey;
|
|
2150
|
+
if (Array.isArray(options.excludeTypes)) {
|
|
2151
|
+
this.compareExcludeTypes = options.excludeTypes;
|
|
2152
|
+
}
|
|
2153
|
+
// 字段级排除:优先 options.excludeFields,其次 formConfig.compareExcludeFields
|
|
2154
|
+
let excludeFields =
|
|
2155
|
+
options.excludeFields ||
|
|
2156
|
+
(this.formConfig && this.formConfig.compareExcludeFields);
|
|
2157
|
+
if (Array.isArray(excludeFields)) {
|
|
2158
|
+
this.compareExcludeFields = excludeFields;
|
|
2159
|
+
}
|
|
2160
|
+
this.oldFormData = deepClone(oldData);
|
|
2161
|
+
this.isCompare = true;
|
|
2162
|
+
// 对旧数据中已带的子表数组,逐个做行快照(单包/混用场景)
|
|
2163
|
+
Object.keys(this.oldFormData).forEach((key) => {
|
|
2164
|
+
if (Array.isArray(this.oldFormData[key])) {
|
|
2165
|
+
this.applyCompareSubSnapshot(key);
|
|
2166
|
+
}
|
|
2167
|
+
});
|
|
2168
|
+
},
|
|
2169
|
+
/**
|
|
2170
|
+
* 增量设置某子表的旧数据(多包场景,各子表旧数据回包后分别调用)。
|
|
2171
|
+
* 可被任意次调用,重复设置以最后一次为准(幂等)。
|
|
2172
|
+
*/
|
|
2173
|
+
setOldSubData: function (subFormName, oldRows) {
|
|
2174
|
+
if (!subFormName) return;
|
|
2175
|
+
if (!this.isCompare) this.isCompare = true;
|
|
2176
|
+
this.$set(this.oldFormData, subFormName, deepClone(oldRows || []));
|
|
2177
|
+
this.applyCompareSubSnapshot(subFormName);
|
|
2178
|
+
},
|
|
2179
|
+
/**
|
|
2180
|
+
* 把 oldFormData[subFormName] 按主键匹配到当前行,写入每行 hData 快照。
|
|
2181
|
+
* 当前子表尚未加载时仅保留旧数据,待其加载后可再次调用本方法补做。
|
|
2182
|
+
*/
|
|
2183
|
+
applyCompareSubSnapshot: function (subFormName) {
|
|
2184
|
+
let rowKey = this.compareRowKey;
|
|
2185
|
+
let oldRows = this.oldFormData[subFormName] || [];
|
|
2186
|
+
let curRows = this.formDataModel[subFormName];
|
|
2187
|
+
if (!Array.isArray(curRows)) return;
|
|
2188
|
+
curRows.forEach((row) => {
|
|
2189
|
+
let old = oldRows.find((o) => this.matchCompareRowKey(o, row, rowKey));
|
|
2190
|
+
this.$set(row, "hData", old ? deepClone(old) : {});
|
|
2191
|
+
});
|
|
2192
|
+
},
|
|
2193
|
+
matchCompareRowKey: function (a, b, rowKey) {
|
|
2194
|
+
rowKey = rowKey || this.compareRowKey;
|
|
2195
|
+
if (!a || !b) return false;
|
|
2196
|
+
return a[rowKey] != null && a[rowKey] === b[rowKey];
|
|
2197
|
+
},
|
|
2198
|
+
/** 关闭对比,复位状态并移除各行 hData */
|
|
2199
|
+
disableCompare: function () {
|
|
2200
|
+
let rowKey = this.compareRowKey;
|
|
2201
|
+
Object.keys(this.oldFormData || {}).forEach((key) => {
|
|
2202
|
+
let curRows = this.formDataModel[key];
|
|
2203
|
+
if (Array.isArray(curRows)) {
|
|
2204
|
+
curRows.forEach((row) => {
|
|
2205
|
+
if (row && Object.prototype.hasOwnProperty.call(row, "hData")) {
|
|
2206
|
+
this.$delete(row, "hData");
|
|
2207
|
+
}
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
});
|
|
2211
|
+
this.isCompare = false;
|
|
2212
|
+
this.oldFormData = {};
|
|
2213
|
+
this.showCompareDelList = false;
|
|
2214
|
+
this.compareDelListOption = {};
|
|
2215
|
+
},
|
|
2216
|
+
/**
|
|
2217
|
+
* 按「数据属性名称(keyName)」拿到 data-table 实例的实时行数据。
|
|
2218
|
+
* data-table 删除行时 $grid.remove() 是异步的,formDataModel[subName] 可能滞后残留,
|
|
2219
|
+
* 因此优先读表格实例的 getTableData().fullData(点击查看时 vxe 已结算),退回 formDataModel。
|
|
2220
|
+
*/
|
|
2221
|
+
getSubTableInstance: function (subFormName) {
|
|
2222
|
+
let refs = this.subFormRefList || {};
|
|
2223
|
+
return (
|
|
2224
|
+
Object.keys(refs)
|
|
2225
|
+
.map((k) => refs[k])
|
|
2226
|
+
.find((r) => r && r.fieldKeyName === subFormName) || null
|
|
2227
|
+
);
|
|
2228
|
+
},
|
|
2229
|
+
getSubTableCurrentRows: function (subFormName) {
|
|
2230
|
+
let inst = this.getSubTableInstance(subFormName);
|
|
2231
|
+
if (inst && typeof inst.getGridTable === "function") {
|
|
2232
|
+
let $grid = inst.getGridTable();
|
|
2233
|
+
if ($grid && typeof $grid.getTableData === "function") {
|
|
2234
|
+
let td = $grid.getTableData() || {};
|
|
2235
|
+
return td.fullData || td.tableData || [];
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
return this.formDataModel[subFormName] || [];
|
|
2239
|
+
},
|
|
2240
|
+
/** 空值归一比较(与 fieldMixin.compareEquals 一致,供行级差异判定用) */
|
|
2241
|
+
compareValueEquals: function (a, b) {
|
|
2242
|
+
const isEmpty = (v) =>
|
|
2243
|
+
v === null ||
|
|
2244
|
+
v === undefined ||
|
|
2245
|
+
v === "" ||
|
|
2246
|
+
(Array.isArray(v) && v.length === 0);
|
|
2247
|
+
if (isEmpty(a) && isEmpty(b)) return true;
|
|
2248
|
+
if (isEmpty(a) || isEmpty(b)) return false;
|
|
2249
|
+
if (typeof a === "object" || typeof b === "object") {
|
|
2250
|
+
try {
|
|
2251
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
2252
|
+
} catch (e) {
|
|
2253
|
+
return a === b;
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
return String(a) === String(b);
|
|
2257
|
+
},
|
|
2258
|
+
/**
|
|
2259
|
+
* 系统审计/外键字段:在变更单里被清空、原单仍有值,比较会误判为“变更”,默认不对比。
|
|
2260
|
+
* create_date/modify_date 等即在此列表内。
|
|
2261
|
+
*/
|
|
2262
|
+
compareSysExcludeFields: function () {
|
|
2263
|
+
return [
|
|
2264
|
+
"id",
|
|
2265
|
+
"createBy",
|
|
2266
|
+
"createDate",
|
|
2267
|
+
"modifyBy",
|
|
2268
|
+
"modifyDate",
|
|
2269
|
+
"_createBy",
|
|
2270
|
+
"_modifyBy",
|
|
2271
|
+
"create_by",
|
|
2272
|
+
"create_date",
|
|
2273
|
+
"modify_by",
|
|
2274
|
+
"modify_date",
|
|
2275
|
+
"head_table_id",
|
|
2276
|
+
"headTableId",
|
|
2277
|
+
"alias_id",
|
|
2278
|
+
"object_foreign_id",
|
|
2279
|
+
];
|
|
2280
|
+
},
|
|
2281
|
+
/** 字段是否排除对比:系统默认排除字段 + 业务配置的 compareExcludeFields。 */
|
|
2282
|
+
isCompareExcludeField: function (name) {
|
|
2283
|
+
if (!name) return false;
|
|
2284
|
+
if (this.compareSysExcludeFields().indexOf(name) !== -1) return true;
|
|
2285
|
+
return (this.compareExcludeFields || []).indexOf(name) !== -1;
|
|
2286
|
+
},
|
|
2287
|
+
/** 行级差异:比较当前行与原单行的标量字段(跳过内部字段、排除字段与附件列) */
|
|
2288
|
+
isCompareRowValuesChanged: function (oldRow, row, skipKeys) {
|
|
2289
|
+
let skip = skipKeys || {};
|
|
2290
|
+
let keys = {};
|
|
2291
|
+
Object.keys(oldRow || {}).forEach((k) => (keys[k] = 1));
|
|
2292
|
+
Object.keys(row || {}).forEach((k) => (keys[k] = 1));
|
|
2293
|
+
return Object.keys(keys).some((k) => {
|
|
2294
|
+
if (
|
|
2295
|
+
k === "hData" ||
|
|
2296
|
+
k.charAt(0) === "_" ||
|
|
2297
|
+
skip[k] ||
|
|
2298
|
+
this.isCompareExcludeField(k)
|
|
2299
|
+
) {
|
|
2300
|
+
return false;
|
|
2301
|
+
}
|
|
2302
|
+
return !this.compareValueEquals(oldRow[k], row[k]);
|
|
2303
|
+
});
|
|
2304
|
+
},
|
|
2305
|
+
/**
|
|
2306
|
+
* 返回某子表行的对比行级样式类:新增行 / 变更行 / 无。供 data-table 的 rowClassName 使用。
|
|
2307
|
+
* @returns {String} 'xform-compare-row-new' | 'xform-compare-row-changed' | ''
|
|
2308
|
+
*/
|
|
2309
|
+
getCompareRowClass: function (subFormName, row) {
|
|
2310
|
+
if (!this.isCompare || !row) return "";
|
|
2311
|
+
let rowKey = this.compareRowKey;
|
|
2312
|
+
let oldRows = (this.oldFormData && this.oldFormData[subFormName]) || [];
|
|
2313
|
+
let old = oldRows.find((o) => this.matchCompareRowKey(o, row, rowKey));
|
|
2314
|
+
if (!old) return "xform-compare-row-new";
|
|
2315
|
+
// 附件列不参与对比:从子表实例收集需跳过的附件 prop(系统字段由 isCompareExcludeField 统一处理)
|
|
2316
|
+
let skip = {};
|
|
2317
|
+
let inst = this.getSubTableInstance(subFormName);
|
|
2318
|
+
if (inst && typeof inst.getAttachmentType === "function") {
|
|
2319
|
+
(inst.getAttachmentType() || []).forEach((p) => (skip[p] = 1));
|
|
2320
|
+
}
|
|
2321
|
+
return this.isCompareRowValuesChanged(old, row, skip)
|
|
2322
|
+
? "xform-compare-row-changed"
|
|
2323
|
+
: "";
|
|
2324
|
+
},
|
|
2325
|
+
/** 返回某子表被删除的行(旧有、当前按主键找不到)。实时计算。 */
|
|
2326
|
+
getDelData: function (subFormName) {
|
|
2327
|
+
let rowKey = this.compareRowKey;
|
|
2328
|
+
let oldRows = (this.oldFormData && this.oldFormData[subFormName]) || [];
|
|
2329
|
+
let curRows = this.getSubTableCurrentRows(subFormName);
|
|
2330
|
+
return oldRows.filter(
|
|
2331
|
+
(o) => !curRows.some((c) => this.matchCompareRowKey(o, c, rowKey))
|
|
2332
|
+
);
|
|
2333
|
+
},
|
|
2334
|
+
/** 取某子表当前表格的展示列 [{ field, title }](叶子列,走 $t1 国际化标题) */
|
|
2335
|
+
getSubTableDisplayColumns: function (subFormName) {
|
|
2336
|
+
let inst = this.getSubTableInstance(subFormName);
|
|
2337
|
+
let tableColumns =
|
|
2338
|
+
inst && inst.widget && inst.widget.options
|
|
2339
|
+
? inst.widget.options.tableColumns
|
|
2340
|
+
: null;
|
|
2341
|
+
if (!tableColumns) return [];
|
|
2342
|
+
let result = [];
|
|
2343
|
+
let walk = (cols) => {
|
|
2344
|
+
(cols || []).forEach((item) => {
|
|
2345
|
+
if (item.children && item.children.length) {
|
|
2346
|
+
walk(item.children);
|
|
2347
|
+
} else if (
|
|
2348
|
+
item.prop &&
|
|
2349
|
+
item.label &&
|
|
2350
|
+
item.formatS !== "editAttachment" // 附件列为数组/对象,纯文本表格显示 undefined,排除
|
|
2351
|
+
) {
|
|
2352
|
+
result.push({
|
|
2353
|
+
field: item.prop,
|
|
2354
|
+
title: this.$t1 ? this.$t1(item.label) : item.label,
|
|
2355
|
+
width: item.width,
|
|
2356
|
+
minWidth: item.minWidth,
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
};
|
|
2361
|
+
walk(tableColumns);
|
|
2362
|
+
return result;
|
|
2363
|
+
},
|
|
2364
|
+
/**
|
|
2365
|
+
* 「变更」跳转:从原单跳到变更单列表并携带 sourceId 打开新增。
|
|
2366
|
+
* 列表宿主(list.vue)按「路由最后一段=formCode、query.param(JSON)=打开参数」解析,
|
|
2367
|
+
* 因此默认复用当前路由基路径、把最后一段替换为变更单 formCode(同基兄弟列表约定)。
|
|
2368
|
+
* @param {String|Number} sourceId 原单 id(不传取当前 dataId)
|
|
2369
|
+
* @param {String} formCode 变更单表单编号(作为目标列表路由最后一段)
|
|
2370
|
+
* @param {Object} [options]
|
|
2371
|
+
* listPath 显式目标列表路由(覆盖默认的“替换最后一段”推导)
|
|
2372
|
+
* param 额外打开参数(与 { sourceId, autoAdd:1 } 合并,走 query.param)
|
|
2373
|
+
* query 额外 query 合并
|
|
2374
|
+
*/
|
|
2375
|
+
goChangeBill: function (sourceId, formCode, options) {
|
|
2376
|
+
let a = 1;
|
|
2377
|
+
debugger;
|
|
2378
|
+
options = options || {};
|
|
2379
|
+
let router = this.$router || (window.$vueRoot && window.$vueRoot.$router);
|
|
2380
|
+
let store = this.$store || (window.$vueRoot && window.$vueRoot.$store);
|
|
2381
|
+
if (!router) return;
|
|
2382
|
+
let sid = sourceId != null ? sourceId : this.dataId;
|
|
2383
|
+
// 目标列表路径:优先 options.listPath;否则复用当前路径、替换最后一段为 formCode
|
|
2384
|
+
let path = options.listPath;
|
|
2385
|
+
if (!path) {
|
|
2386
|
+
let curPath = (this.$route && this.$route.path) || "";
|
|
2387
|
+
let segs = curPath.split("/");
|
|
2388
|
+
if (segs.length) segs[segs.length - 1] = formCode;
|
|
2389
|
+
path = segs.join("/");
|
|
2390
|
+
}
|
|
2391
|
+
let param = Object.assign({}, options.param, {
|
|
2392
|
+
sourceId: sid,
|
|
2393
|
+
autoAdd: 1,
|
|
2394
|
+
});
|
|
2395
|
+
let query = Object.assign({}, options.query, param);
|
|
2396
|
+
let view = { path: path, query: query };
|
|
2397
|
+
// 先清掉目标列表的缓存视图,再 replace,确保列表按新参数重新加载
|
|
2398
|
+
let doNav = () => router.replace(view);
|
|
2399
|
+
if (store) {
|
|
2400
|
+
store.dispatch("tagsView/delCachedView", view).then(doNav, doNav);
|
|
2401
|
+
} else {
|
|
2402
|
+
doNav();
|
|
2403
|
+
}
|
|
2404
|
+
},
|
|
2405
|
+
/** 打开"查看删除数据"弹窗。columns 不传时默认取当前数据表格的列。 */
|
|
2406
|
+
openCompareDelList: function (subFormName, columns) {
|
|
2407
|
+
this.compareDelListOption = {
|
|
2408
|
+
subFormName: subFormName,
|
|
2409
|
+
columns:
|
|
2410
|
+
columns && columns.length
|
|
2411
|
+
? columns
|
|
2412
|
+
: this.getSubTableDisplayColumns(subFormName),
|
|
2413
|
+
data: this.getDelData(subFormName),
|
|
2414
|
+
};
|
|
2415
|
+
this.showCompareDelList = true;
|
|
2416
|
+
},
|
|
2417
|
+
/**
|
|
2418
|
+
* 枚举需要独立发包获取的子表(data-table 容器)。sub-form/table2 的数组随主表
|
|
2419
|
+
* objx 一并返回,无需单独发包。返回 [{ name, scriptCode }]。
|
|
2420
|
+
*/
|
|
2421
|
+
getCompareSubTables: function () {
|
|
2422
|
+
return this.getCompareSubTablesFrom(this.formJsonObj.widgetList);
|
|
2423
|
+
},
|
|
2424
|
+
getCompareSubTablesFrom: function (widgetList) {
|
|
2425
|
+
let result = [];
|
|
2426
|
+
this.getContainerWidgets(widgetList).forEach((c) => {
|
|
2427
|
+
if (c.type === "data-table" && c.container && c.container.options) {
|
|
2428
|
+
result.push({
|
|
2429
|
+
// 与 data-table 实际发包(taBm)/表单存储键保持一致:优先「数据属性名称」keyName
|
|
2430
|
+
name: this.getFieldKeyName(c.container),
|
|
2431
|
+
scriptCode: c.container.options.formScriptCode || "getList",
|
|
2432
|
+
// 与 loadDefaultViewList 一致:附件列/三级表列需随包上送,后端才会返回附件
|
|
2433
|
+
attachmentType: this.collectAttachmentTypeFromColumns(
|
|
2434
|
+
c.container.options.tableColumns
|
|
2435
|
+
),
|
|
2436
|
+
thirdtabm: this.collectThirdtabmFromColumns(
|
|
2437
|
+
c.container.options.tableColumns
|
|
2438
|
+
),
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
2442
|
+
return result;
|
|
2443
|
+
},
|
|
2444
|
+
// 递归遍历数据表列,收集 formatS==='editAttachment' 的列 prop(附件列)
|
|
2445
|
+
collectAttachmentTypeFromColumns: function (columns) {
|
|
2446
|
+
let result = [];
|
|
2447
|
+
let walk = (cols) => {
|
|
2448
|
+
(cols || []).forEach((item) => {
|
|
2449
|
+
if (item.children && item.children.length) {
|
|
2450
|
+
walk(item.children);
|
|
2451
|
+
} else if (
|
|
2452
|
+
item.prop &&
|
|
2453
|
+
item.label &&
|
|
2454
|
+
item.formatS === "editAttachment"
|
|
2455
|
+
) {
|
|
2456
|
+
result.push(item.prop);
|
|
2457
|
+
}
|
|
2458
|
+
});
|
|
2459
|
+
};
|
|
2460
|
+
walk(columns);
|
|
2461
|
+
return result;
|
|
2462
|
+
},
|
|
2463
|
+
// 递归遍历数据表列,收集 editSearch + 多选(三级表)的列 prop
|
|
2464
|
+
collectThirdtabmFromColumns: function (columns) {
|
|
2465
|
+
let result = [];
|
|
2466
|
+
let walk = (cols) => {
|
|
2467
|
+
(cols || []).forEach((item) => {
|
|
2468
|
+
if (item.children && item.children.length) {
|
|
2469
|
+
walk(item.children);
|
|
2470
|
+
} else if (
|
|
2471
|
+
item.prop &&
|
|
2472
|
+
item.label &&
|
|
2473
|
+
item.formatS === "editSearch" &&
|
|
2474
|
+
item.widget &&
|
|
2475
|
+
item.widget.options &&
|
|
2476
|
+
item.widget.options.multipleChoices
|
|
2477
|
+
) {
|
|
2478
|
+
result.push(item.prop);
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2481
|
+
};
|
|
2482
|
+
walk(columns);
|
|
2483
|
+
return result;
|
|
2484
|
+
},
|
|
2485
|
+
// 递归遍历数据表列,把列 prop → 列组件 defaultValue 写入 map(供 defaultFields 取默认值)
|
|
2486
|
+
collectColumnDefaults: function (columns, map) {
|
|
2487
|
+
let walk = (cols) => {
|
|
2488
|
+
(cols || []).forEach((item) => {
|
|
2489
|
+
if (item.children && item.children.length) {
|
|
2490
|
+
walk(item.children);
|
|
2491
|
+
} else if (item.prop) {
|
|
2492
|
+
let w = item.editWidget || item.widget;
|
|
2493
|
+
if (w && w.options) map[item.prop] = w.options.defaultValue;
|
|
2494
|
+
}
|
|
2495
|
+
});
|
|
2496
|
+
};
|
|
2497
|
+
walk(columns);
|
|
2498
|
+
},
|
|
2499
|
+
// 收集表头(主表)附件字段。表头附件不随 getOne 返回,每个附件字段需按 taBm=keyName
|
|
2500
|
+
// 独立发 getList 包(与 baseAttachment-widget.loadDataDefaultHandle 一致)。
|
|
2501
|
+
getHeaderAttachmentFields: function (widgetList) {
|
|
2502
|
+
let excludeTypes = this.compareExcludeTypes || [];
|
|
2503
|
+
let result = [];
|
|
2504
|
+
getAllFieldWidgets(widgetList).forEach((w) => {
|
|
2505
|
+
if (w.field && excludeTypes.indexOf(w.type) !== -1) {
|
|
2506
|
+
result.push({
|
|
2507
|
+
name: this.getFieldKeyName(w.field),
|
|
2508
|
+
scriptCode: w.field.options.formScriptCode || "getList",
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
});
|
|
2512
|
+
return result;
|
|
2513
|
+
},
|
|
2514
|
+
/**
|
|
2515
|
+
* 按 formCode 拉取表单模板定义,返回 { reportTemplate, formConfig, widgetList }。
|
|
2516
|
+
* 用于「用原单模板拉原单数据」——原单与变更单模板不同时。
|
|
2517
|
+
*/
|
|
2518
|
+
fetchTemplateByFormCode: function (formCode) {
|
|
2519
|
+
return new Promise((resolve, reject) => {
|
|
2520
|
+
this.$http({
|
|
2521
|
+
url: USER_PREFIX + `/formTemplate/getByFormCode`,
|
|
2522
|
+
method: `post`,
|
|
2523
|
+
data: { stringOne: formCode },
|
|
2524
|
+
isLoading: false,
|
|
2525
|
+
success: (res) => {
|
|
2526
|
+
let t = res.objx || {};
|
|
2527
|
+
let fj = t.formViewContent ? JSON.parse(t.formViewContent) : {};
|
|
2528
|
+
resolve({
|
|
2529
|
+
reportTemplate: t,
|
|
2530
|
+
formConfig: fj.formConfig || {},
|
|
2531
|
+
widgetList: fj.widgetList || [],
|
|
2532
|
+
});
|
|
2533
|
+
},
|
|
2534
|
+
error: reject,
|
|
2535
|
+
});
|
|
2536
|
+
});
|
|
2537
|
+
},
|
|
2538
|
+
/**
|
|
2539
|
+
* 通用:按「原单模板」自动多包获取原单整单数据(主表 + 所有 data-table 子表),
|
|
2540
|
+
* 组装成与 getFormData 一致的对象返回。默认复用当前表单模板(变更单通常与原单
|
|
2541
|
+
* 结构一致);跨模板可用 options 覆盖。
|
|
2542
|
+
* @param {String|Number} sourceId 原单 id
|
|
2543
|
+
* @param {Object} [options] { reportTemplate, formCode, formVersion, entity, masterScriptCode, isLoading }
|
|
2544
|
+
* @returns {Promise<Object>} 原单整单数据
|
|
2545
|
+
*/
|
|
2546
|
+
loadOldData: function (sourceId, options) {
|
|
2547
|
+
options = options || {};
|
|
2548
|
+
let isLoading = options.isLoading !== false;
|
|
2549
|
+
// 解析「原单模板」:优先按 sourceFormCode 拉原单模板;否则复用当前模板
|
|
2550
|
+
let resolveTpl = options.sourceFormCode
|
|
2551
|
+
? this.fetchTemplateByFormCode(options.sourceFormCode)
|
|
2552
|
+
: Promise.resolve({
|
|
2553
|
+
reportTemplate: options.reportTemplate || this.reportTemplate || {},
|
|
2554
|
+
formConfig: this.formConfig || {},
|
|
2555
|
+
widgetList: this.formJsonObj.widgetList,
|
|
2556
|
+
});
|
|
2557
|
+
return resolveTpl.then((tpl) => {
|
|
2558
|
+
let rt = tpl.reportTemplate || {};
|
|
2559
|
+
let tplFormConfig = tpl.formConfig || {};
|
|
2560
|
+
let formCode = options.formCode || rt.formCode;
|
|
2561
|
+
let formVersion = options.formVersion || rt.formVersion;
|
|
2562
|
+
let entity = options.entity || tplFormConfig.entity;
|
|
2563
|
+
let masterScript =
|
|
2564
|
+
options.masterScriptCode || tplFormConfig.formScriptCode || "getOne";
|
|
2565
|
+
let result = {};
|
|
2566
|
+
let tasks = [];
|
|
2567
|
+
// 主表(含 sub-form/table2 内嵌数组)
|
|
2568
|
+
tasks.push(
|
|
2569
|
+
this.formHttp({
|
|
2570
|
+
vue: this,
|
|
2571
|
+
scriptCode: masterScript,
|
|
2572
|
+
data: {
|
|
2573
|
+
formCode: formCode,
|
|
2574
|
+
formVersion: formVersion,
|
|
2575
|
+
taBm: entity,
|
|
2576
|
+
data: { id: sourceId },
|
|
2577
|
+
},
|
|
2578
|
+
isLoading: isLoading,
|
|
2579
|
+
}).then((res) => {
|
|
2580
|
+
Object.assign(result, (res && res.objx) || {});
|
|
2581
|
+
})
|
|
2582
|
+
);
|
|
2583
|
+
// 表头附件:每个附件字段按 taBm=keyName 独立发 getList 包(getOne 不返回表头附件)
|
|
2584
|
+
this.getHeaderAttachmentFields(tpl.widgetList).forEach((att) => {
|
|
2585
|
+
tasks.push(
|
|
2586
|
+
this.formHttp({
|
|
2587
|
+
vue: this,
|
|
2588
|
+
scriptCode: att.scriptCode,
|
|
2589
|
+
data: {
|
|
2590
|
+
formCode: formCode,
|
|
2591
|
+
formVersion: formVersion,
|
|
2592
|
+
taBm: att.name,
|
|
2593
|
+
data: { id: sourceId },
|
|
2594
|
+
},
|
|
2595
|
+
isLoading: isLoading,
|
|
2596
|
+
}).then((res) => {
|
|
2597
|
+
let o = res && res.objx;
|
|
2598
|
+
result[att.name] = (o && o.records) || o || [];
|
|
2599
|
+
})
|
|
2600
|
+
);
|
|
2601
|
+
});
|
|
2602
|
+
// 各 data-table 子表独立发包(按原单模板枚举),附件/三级表列随包上送
|
|
2603
|
+
this.getCompareSubTablesFrom(tpl.widgetList).forEach((sub) => {
|
|
2604
|
+
let subData = { id: sourceId };
|
|
2605
|
+
if (sub.attachmentType && sub.attachmentType.length) {
|
|
2606
|
+
subData.attachmentType = sub.attachmentType;
|
|
2607
|
+
}
|
|
2608
|
+
if (sub.thirdtabm && sub.thirdtabm.length) {
|
|
2609
|
+
subData.thirdtabm = sub.thirdtabm;
|
|
2610
|
+
}
|
|
2611
|
+
tasks.push(
|
|
2612
|
+
this.formHttp({
|
|
2613
|
+
vue: this,
|
|
2614
|
+
scriptCode: sub.scriptCode,
|
|
2615
|
+
data: {
|
|
2616
|
+
formCode: formCode,
|
|
2617
|
+
formVersion: formVersion,
|
|
2618
|
+
taBm: sub.name,
|
|
2619
|
+
data: subData,
|
|
2620
|
+
},
|
|
2621
|
+
isLoading: isLoading,
|
|
2622
|
+
}).then((res) => {
|
|
2623
|
+
let o = res && res.objx;
|
|
2624
|
+
result[sub.name] = (o && o.records) || o || [];
|
|
2625
|
+
})
|
|
2626
|
+
);
|
|
2627
|
+
});
|
|
2628
|
+
return Promise.all(tasks).then(() => result);
|
|
2629
|
+
});
|
|
2630
|
+
},
|
|
2631
|
+
/**
|
|
2632
|
+
* 通用:自动获取原单并开启对比(模式 B「对比」按钮一行接入)。
|
|
2633
|
+
* @returns {Promise<Object>} 原单数据
|
|
2634
|
+
*/
|
|
2635
|
+
enableCompareBySource: function (sourceId, options) {
|
|
2636
|
+
options = options || {};
|
|
2637
|
+
return this.loadOldData(sourceId, options).then((oldData) => {
|
|
2638
|
+
this.enableCompare(oldData, options);
|
|
2639
|
+
return oldData;
|
|
2640
|
+
});
|
|
2641
|
+
},
|
|
2642
|
+
/**
|
|
2643
|
+
* 通用:新建变更单时,自动获取原单 → 清洗 → 填充新单 → 开启对比(模式 A 一行接入)。
|
|
2644
|
+
* @param {String|Number} sourceId 原单 id
|
|
2645
|
+
* @param {Object} [options] loadOldData 的选项 + { sanitize } 传给 sanitizeChangeData
|
|
2646
|
+
* @returns {Promise<Object>} 原单数据(对比基准)
|
|
2647
|
+
*/
|
|
2648
|
+
initChangeFromSource: function (sourceId, options) {
|
|
2649
|
+
options = options || {};
|
|
2650
|
+
return this.loadOldData(sourceId, options).then((oldData) => {
|
|
2651
|
+
let newData = this.sanitizeChangeData(oldData, options.sanitize || {});
|
|
2652
|
+
this.setFormData(newData);
|
|
2653
|
+
this.enableCompare(oldData, options);
|
|
2654
|
+
return oldData;
|
|
2655
|
+
});
|
|
2656
|
+
},
|
|
2657
|
+
/**
|
|
2658
|
+
* 「变更」时用原单据数据初始化新变更单前的清洗:返回清洗后的克隆,不修改入参。
|
|
2659
|
+
* 清理主表与所有子表行的系统字段(id/创建人/创建时间/更新人/更新时间等),
|
|
2660
|
+
* 子表额外清理外键 head_table_id;支持将指定字段重置为「变更单默认值」(不吃原单值)。
|
|
2661
|
+
* @param {Object} oldData 原单据整单数据
|
|
2662
|
+
* @param {Object} [options]
|
|
2663
|
+
* masterClearFields 覆盖主表默认清理字段
|
|
2664
|
+
* subExtraClearFields 仅子表额外清理的字段,默认 head_table_id/headTableId
|
|
2665
|
+
* defaultFields 主表:不吃原单值、取变更单默认值的字段名数组
|
|
2666
|
+
* subDefaultFields 明细表:{ 明细表keyName: ['列prop', ...] },精确到某明细表的某列
|
|
2667
|
+
* @returns {Object} 清洗后的数据克隆
|
|
2668
|
+
*/
|
|
2669
|
+
sanitizeChangeData: function (oldData, options) {
|
|
2670
|
+
options = options || {};
|
|
2671
|
+
const SYS_FIELDS = [
|
|
2672
|
+
"id",
|
|
2673
|
+
"createBy",
|
|
2674
|
+
"createDate",
|
|
2675
|
+
"modifyBy",
|
|
2676
|
+
"modifyDate",
|
|
2677
|
+
"_createBy",
|
|
2678
|
+
"_modifyBy",
|
|
2679
|
+
"create_by",
|
|
2680
|
+
"create_date",
|
|
2681
|
+
"modify_by",
|
|
2682
|
+
"modify_date",
|
|
2683
|
+
];
|
|
2684
|
+
const SUB_FK_FIELDS = [
|
|
2685
|
+
"head_table_id",
|
|
2686
|
+
"headTableId",
|
|
2687
|
+
"alias_id",
|
|
2688
|
+
"object_foreign_id",
|
|
2689
|
+
];
|
|
2690
|
+
let masterClear = options.masterClearFields || SYS_FIELDS;
|
|
2691
|
+
let subExtra = options.subExtraClearFields || SUB_FK_FIELDS;
|
|
2692
|
+
let data = deepClone(oldData || {});
|
|
2693
|
+
|
|
2694
|
+
let clearOn = (obj, fields) => {
|
|
2695
|
+
if (!obj || typeof obj !== "object") return;
|
|
2696
|
+
fields.forEach((f) => {
|
|
2697
|
+
if (Object.prototype.hasOwnProperty.call(obj, f)) obj[f] = null;
|
|
2698
|
+
});
|
|
2699
|
+
};
|
|
2700
|
+
|
|
2701
|
+
// 附件字段:不参与对比、但需拷贝过来。拷到变更单时,附件内部的文件对象只保留
|
|
2702
|
+
// 展示/存储必需字段(白名单),其余(id/外键/创建信息等)一律丢弃,让保存时生成新附件记录。
|
|
2703
|
+
let cleanAttachment = options.cleanAttachment !== false;
|
|
2704
|
+
// 文件对象保留字段白名单
|
|
2705
|
+
const ATTACH_KEEP_FIELDS = options.attachmentKeepFields || [
|
|
2706
|
+
"name",
|
|
2707
|
+
"thumbnail",
|
|
2708
|
+
"extension",
|
|
2709
|
+
"large",
|
|
2710
|
+
"fileSize",
|
|
2711
|
+
"domain",
|
|
2712
|
+
"source",
|
|
2713
|
+
"medium",
|
|
2714
|
+
"url",
|
|
2715
|
+
];
|
|
2716
|
+
let attachKeySet = {};
|
|
2717
|
+
if (cleanAttachment) {
|
|
2718
|
+
let excludeTypes =
|
|
2719
|
+
options.excludeTypes || this.compareExcludeTypes || [];
|
|
2720
|
+
this.getFieldWidgets().forEach((w) => {
|
|
2721
|
+
if (excludeTypes.indexOf(w.type) === -1) return;
|
|
2722
|
+
attachKeySet[this.getFieldKeyName(w.field)] = true;
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
// 只保留白名单字段,返回精简后的新文件对象
|
|
2726
|
+
let pickAttachFields = (f) => {
|
|
2727
|
+
if (!f || typeof f !== "object") return f;
|
|
2728
|
+
let picked = {};
|
|
2729
|
+
ATTACH_KEEP_FIELDS.forEach((k) => {
|
|
2730
|
+
if (Object.prototype.hasOwnProperty.call(f, k)) picked[k] = f[k];
|
|
2731
|
+
});
|
|
2732
|
+
return picked;
|
|
2733
|
+
};
|
|
2734
|
+
// 精简某对象上所有附件字段内部文件对象的字段。keySet 不传时用主表/子表单附件字段集。
|
|
2735
|
+
let cleanAttachmentOn = (obj, keySet) => {
|
|
2736
|
+
if (!cleanAttachment || !obj || typeof obj !== "object") return;
|
|
2737
|
+
Object.keys(keySet || attachKeySet).forEach((key) => {
|
|
2738
|
+
let val = obj[key];
|
|
2739
|
+
if (Array.isArray(val)) {
|
|
2740
|
+
obj[key] = val.map(pickAttachFields);
|
|
2741
|
+
} else if (val && typeof val === "object") {
|
|
2742
|
+
obj[key] = pickAttachFields(val);
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
};
|
|
2746
|
+
// data-table 的附件是「列」(tableColumns 里 formatS==='editAttachment'),不在字段里,
|
|
2747
|
+
// 需按每个 data-table 的 keyName 收集其附件列 prop,单独用于清理其行数据。
|
|
2748
|
+
let subAttachMap = {}; // subKeyName -> { prop: true }
|
|
2749
|
+
if (cleanAttachment) {
|
|
2750
|
+
this.getContainerWidgets().forEach((c) => {
|
|
2751
|
+
if (c.type !== "data-table" || !c.container.options) return;
|
|
2752
|
+
let subName = this.getFieldKeyName(c.container);
|
|
2753
|
+
let set = {};
|
|
2754
|
+
this.collectAttachmentTypeFromColumns(
|
|
2755
|
+
c.container.options.tableColumns
|
|
2756
|
+
).forEach((p) => (set[p] = true));
|
|
2757
|
+
subAttachMap[subName] = set;
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
// 树表配置:{ name, idField='id', parentField='parent_id' }。这些子表的
|
|
2762
|
+
// id/parent_id 不能简单清空,需重新生成 id 并把 parent_id 重映射到新 id 以保留层级。
|
|
2763
|
+
let treeTables = options.treeSubTables || [];
|
|
2764
|
+
let treeMap = {}; // subFormName -> { idField, parentField }
|
|
2765
|
+
treeTables.forEach((t) => {
|
|
2766
|
+
let cfg = typeof t === "string" ? { name: t } : t;
|
|
2767
|
+
treeMap[cfg.name] = {
|
|
2768
|
+
idField: cfg.idField || "id",
|
|
2769
|
+
parentField: cfg.parentField || "parent_id",
|
|
2770
|
+
};
|
|
2771
|
+
});
|
|
2772
|
+
|
|
2773
|
+
// 主表
|
|
2774
|
+
clearOn(data, masterClear);
|
|
2775
|
+
cleanAttachmentOn(data);
|
|
2776
|
+
// 从原明细行/原单表头回填/自定义赋值到明细业务字段。按明细keyName配置,值支持:
|
|
2777
|
+
// '目标字段' → 把原明细行 id 拷到目标字段
|
|
2778
|
+
// { from:'明细来源字段', to:'目标字段' } → 明细字段回填(from 默认 'id')
|
|
2779
|
+
// { fromHeader:'表头字段', to:'目标字段' } → 原单表头字段回填到明细
|
|
2780
|
+
// [ {..}, {..}, ... ] → 多字段回填
|
|
2781
|
+
// (row, header) => { ... } → 自定义函数,row 原始明细行、header 原单表头,自行赋值
|
|
2782
|
+
// 必须在清空 id / 树表重生成 id 之前执行,否则拿不到原始值。
|
|
2783
|
+
// header 取 data 根对象:master 业务字段未被清理,仍是原单原值。
|
|
2784
|
+
let subSourceFields =
|
|
2785
|
+
options.subSourceFields || options.subSourceIdFields || {};
|
|
2786
|
+
let header = data;
|
|
2787
|
+
let applySourceField = (row, cfg) => {
|
|
2788
|
+
if (!row || typeof row !== "object" || !cfg) return;
|
|
2789
|
+
if (typeof cfg === "function") {
|
|
2790
|
+
cfg(row, header);
|
|
2791
|
+
} else if (Array.isArray(cfg)) {
|
|
2792
|
+
cfg.forEach((c) => applySourceField(row, c));
|
|
2793
|
+
} else if (typeof cfg === "object") {
|
|
2794
|
+
if (cfg.to) {
|
|
2795
|
+
row[cfg.to] = cfg.fromHeader
|
|
2796
|
+
? header[cfg.fromHeader]
|
|
2797
|
+
: row[cfg.from || "id"];
|
|
2798
|
+
}
|
|
2799
|
+
} else {
|
|
2800
|
+
row[cfg] = row.id; // 字符串简写:id → 该字段
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
// 所有子表数组
|
|
2804
|
+
let subClear = masterClear.concat(subExtra);
|
|
2805
|
+
Object.keys(data).forEach((k) => {
|
|
2806
|
+
if (!Array.isArray(data[k])) return;
|
|
2807
|
+
// ① 先回填原明细字段(早于 clearOn / regenerateTreeIds)
|
|
2808
|
+
let srcCfg = subSourceFields[k];
|
|
2809
|
+
if (srcCfg) {
|
|
2810
|
+
data[k].forEach((row) => applySourceField(row, srcCfg));
|
|
2811
|
+
}
|
|
2812
|
+
// data-table 用其附件列集;sub-form/table2 行内字段用主表附件字段集(attachKeySet)
|
|
2813
|
+
let rowAttachSet = subAttachMap[k] || attachKeySet;
|
|
2814
|
+
let treeCfg = treeMap[k];
|
|
2815
|
+
if (treeCfg) {
|
|
2816
|
+
this.regenerateTreeIds(data[k], treeCfg.idField, treeCfg.parentField);
|
|
2817
|
+
// 树表清理时排除 id/parent_id(已重映射),其余系统字段照清
|
|
2818
|
+
let treeClear = subClear.filter(
|
|
2819
|
+
(f) => f !== treeCfg.idField && f !== treeCfg.parentField
|
|
2820
|
+
);
|
|
2821
|
+
data[k].forEach((row) => {
|
|
2822
|
+
clearOn(row, treeClear);
|
|
2823
|
+
cleanAttachmentOn(row, rowAttachSet);
|
|
2824
|
+
});
|
|
2825
|
+
} else {
|
|
2826
|
+
data[k].forEach((row) => {
|
|
2827
|
+
clearOn(row, subClear);
|
|
2828
|
+
cleanAttachmentOn(row, rowAttachSet);
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
});
|
|
2832
|
+
|
|
2833
|
+
// 「不被原单值覆盖」的字段:保留变更单自身的值,不用原单值。主表与明细表分开指定:
|
|
2834
|
+
// defaultFields 主表字段名数组,如 ['f_status', 'f_no']
|
|
2835
|
+
// subDefaultFields 明细表字段,按「明细表属性名(keyName)」分组:
|
|
2836
|
+
// { 明细表keyName: ['列prop1', '列prop2'] }
|
|
2837
|
+
|
|
2838
|
+
// 主表:从清洗数据里删掉该 key。setFormData 用 Object.assign 合并,缺失的 key
|
|
2839
|
+
// 不会覆盖 formDataModel,从而保留变更单已初始化的默认值。
|
|
2840
|
+
let defaultFields = options.defaultFields || [];
|
|
2841
|
+
defaultFields.forEach((f) => {
|
|
2842
|
+
if (!Array.isArray(data[f])) delete data[f];
|
|
2843
|
+
});
|
|
2844
|
+
|
|
2845
|
+
// 明细表:明细行整体来自原单(无变更单已有值可保留),故对指定列取「该列的变更单默认值」,
|
|
2846
|
+
// 等同于新建一行时该列应有的值。精确到「哪个明细表」的「哪个列」。
|
|
2847
|
+
let subDefaultFields = options.subDefaultFields || {};
|
|
2848
|
+
if (Object.keys(subDefaultFields).length) {
|
|
2849
|
+
this.getContainerWidgets().forEach((c) => {
|
|
2850
|
+
if (c.type !== "data-table" || !c.container.options) return;
|
|
2851
|
+
let subName = this.getFieldKeyName(c.container);
|
|
2852
|
+
let cols = subDefaultFields[subName];
|
|
2853
|
+
if (!cols || !cols.length || !Array.isArray(data[subName])) return;
|
|
2854
|
+
let colMap = {};
|
|
2855
|
+
this.collectColumnDefaults(c.container.options.tableColumns, colMap);
|
|
2856
|
+
let takeDefault = (f) =>
|
|
2857
|
+
Object.prototype.hasOwnProperty.call(colMap, f)
|
|
2858
|
+
? deepClone(colMap[f])
|
|
2859
|
+
: null;
|
|
2860
|
+
data[subName].forEach((row) => {
|
|
2861
|
+
if (!row || typeof row !== "object") return;
|
|
2862
|
+
cols.forEach((f) => {
|
|
2863
|
+
row[f] = takeDefault(f);
|
|
2864
|
+
});
|
|
2865
|
+
});
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
return data;
|
|
2869
|
+
},
|
|
2870
|
+
/**
|
|
2871
|
+
* 树表 id/parent_id 重新生成:为每行生成新 id,并把 parent_id 按旧→新映射重指向;
|
|
2872
|
+
* 原父节点不在本表内(外部父级)的 parent_id 置空作为根。就地修改 rows。
|
|
2873
|
+
*/
|
|
2874
|
+
regenerateTreeIds: function (rows, idField, parentField) {
|
|
2875
|
+
idField = idField || "id";
|
|
2876
|
+
parentField = parentField || "parent_id";
|
|
2877
|
+
if (!Array.isArray(rows) || !rows.length) return;
|
|
2878
|
+
let idMap = {}; // 旧id -> 新id
|
|
2879
|
+
let used = {}; // 保证本表内新 id 唯一,避免碰撞破坏父子映射
|
|
2880
|
+
let nextId = () => {
|
|
2881
|
+
let id;
|
|
2882
|
+
do {
|
|
2883
|
+
id = "_row" + generateId(); // 以 _row 开头标识新建行
|
|
2884
|
+
} while (used[id]);
|
|
2885
|
+
used[id] = true;
|
|
2886
|
+
return id;
|
|
2887
|
+
};
|
|
2888
|
+
rows.forEach((row) => {
|
|
2889
|
+
if (!row || typeof row !== "object") return;
|
|
2890
|
+
let oldId = row[idField];
|
|
2891
|
+
let newId = nextId();
|
|
2892
|
+
if (oldId !== null && oldId !== undefined) idMap[oldId] = newId;
|
|
2893
|
+
row.__newId = newId;
|
|
2894
|
+
});
|
|
2895
|
+
rows.forEach((row) => {
|
|
2896
|
+
if (!row || typeof row !== "object") return;
|
|
2897
|
+
row[idField] = row.__newId;
|
|
2898
|
+
delete row.__newId;
|
|
2899
|
+
let oldParent = row[parentField];
|
|
2900
|
+
row[parentField] =
|
|
2901
|
+
oldParent != null &&
|
|
2902
|
+
Object.prototype.hasOwnProperty.call(idMap, oldParent)
|
|
2903
|
+
? idMap[oldParent]
|
|
2904
|
+
: null;
|
|
2905
|
+
});
|
|
2906
|
+
},
|
|
2110
2907
|
addEC: function (e, t) {
|
|
2111
2908
|
this.externalComponents[e] = t;
|
|
2112
2909
|
},
|
|
@@ -2338,9 +3135,7 @@ modules = {
|
|
|
2338
3135
|
typeof ref.getCurrentValue === "function"
|
|
2339
3136
|
? ref.getCurrentValue()
|
|
2340
3137
|
: undefined;
|
|
2341
|
-
if (
|
|
2342
|
-
isEmptyArrayFieldValue(actualValue, field.type, field.options)
|
|
2343
|
-
) {
|
|
3138
|
+
if (isEmptyArrayFieldValue(actualValue, field.type, field.options)) {
|
|
2344
3139
|
error = ref.getRequiredHintMsg
|
|
2345
3140
|
? ref.getRequiredHintMsg()
|
|
2346
3141
|
: this.$t2("必填项不能为空", "system.message.required");
|