cloud-web-corejs 1.0.281 → 1.1.0-dev.5
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 +6 -2
- package/src/components/VabUpload/image-viewer.vue +442 -442
- package/src/components/VabUpload/index.vue +44 -2
- package/src/components/VabUpload/mixins.js +126 -19
- package/src/components/VabUpload/view.vue +247 -209
- package/src/components/baseTabs/mixins.js +181 -181
- package/src/components/bizTab/BizBillActions.vue +129 -0
- package/src/components/bizTab/BizTabListPage.vue +391 -0
- package/src/components/bizTab/README.md +378 -0
- package/src/components/bizTab/billTableUtil.js +48 -0
- package/src/components/bizTab/billValidators.js +136 -0
- package/src/components/bizTab/bizBillDetailMixin.js +705 -0
- package/src/components/bizTab/index.js +24 -0
- package/src/components/excelExport/exportFieldDialog.vue +23 -12
- package/src/components/excelExport/exportItemConfigUtil.js +30 -0
- package/src/components/excelExport/mixins.js +3 -3
- package/src/components/mobile/statusTag/mixins.js +1 -61
- package/src/components/mobile/wf/content.vue +1662 -1556
- package/src/components/mobile/wf/mixins/wfModifyDialogMixin.js +8 -6
- package/src/components/table/config.js +78 -78
- package/src/components/table/index.js +1276 -1276
- package/src/components/table/plugins/cell-area/index.js +1055 -0
- package/src/components/table/plugins/cell-area/index.scss +77 -0
- package/src/components/table/plugins/cell-area/util.js +224 -0
- package/src/components/table/tableForm.vue +330 -330
- package/src/components/table/tableFormMixin.js +318 -318
- package/src/components/table/vxeFilter/index.js +163 -163
- package/src/components/table/vxeFilter/mixin.js +316 -316
- package/src/components/wf/content.vue +1188 -1188
- package/src/components/wf/wf.js +40 -20
- package/src/components/wf/wfActionDropdown.vue +1 -1
- package/src/components/wf/wfActionItems.js +1 -1
- package/src/components/xform/docs/2026-08 xform H5 P0 /345/256/236/347/216/260/350/247/204/346/240/274.md" +528 -0
- package/src/components/xform/docs/2026-08 xform H5 P1 /345/256/236/347/216/260/350/247/204/346/240/274.md" +412 -0
- package/src/components/xform/docs/2026-08 xform H5 P2 /345/256/236/347/216/260/350/247/204/346/240/274.md" +287 -0
- package/src/components/xform/docs/2026-08 xform /347/247/273/345/212/250/347/253/257/345/270/203/345/261/200/345/257/271/351/275/220 bill_setting /346/226/271/346/241/210.md" +400 -0
- package/src/components/xform/form-designer/designer.js +15 -0
- package/src/components/xform/form-designer/form-widget/container-widget/data-table-widget.vue +269 -269
- package/src/components/xform/form-designer/form-widget/container-widget/detail-temp-widget.vue +132 -0
- package/src/components/xform/form-designer/form-widget/container-widget/detail-widget.vue +250 -250
- package/src/components/xform/form-designer/form-widget/dialog/fileReferenceDialog.vue +319 -320
- package/src/components/xform/form-designer/form-widget/dialog/formDialog.vue +365 -365
- package/src/components/xform/form-designer/form-widget/dialog/searchFormDialog.vue +554 -555
- package/src/components/xform/form-designer/form-widget/dialog/vabSearchDialog.vue +409 -409
- package/src/components/xform/form-designer/form-widget/field-widget/a-link-widget.vue +150 -132
- package/src/components/xform/form-designer/form-widget/field-widget/date-range-widget.vue +114 -114
- package/src/components/xform/form-designer/form-widget/field-widget/date-widget.vue +154 -154
- package/src/components/xform/form-designer/form-widget/field-widget/form-item-wrapper.vue +47 -1
- package/src/components/xform/form-designer/form-widget/field-widget/mixins/date-limit-mixin.js +178 -178
- package/src/components/xform/form-designer/form-widget/field-widget/mixins/time-limit-mixin.js +77 -77
- package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +6 -0
- package/src/components/xform/form-designer/form-widget/field-widget/select-widget.vue +139 -139
- package/src/components/xform/form-designer/form-widget/field-widget/time-range-widget.vue +106 -106
- package/src/components/xform/form-designer/form-widget/field-widget/time-widget.vue +134 -134
- package/src/components/xform/form-designer/form-widget/field-widget/vabUpload-widget.vue +1 -0
- package/src/components/xform/form-designer/setting-panel/form-setting.vue +39 -39
- package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/data-table-editor.vue +10 -10
- package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/exportItemColumns-dialog.vue +438 -432
- package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/table-column-dialog.vue +2103 -2103
- package/src/components/xform/form-designer/setting-panel/property-editor/container-detail/detail-temp-edito.vue +90 -0
- package/src/components/xform/form-designer/setting-panel/property-editor/container-grid/gutter-editor.vue +82 -82
- package/src/components/xform/form-designer/setting-panel/property-editor/container-list-h5/list-h5-editor.vue +19 -5
- package/src/components/xform/form-designer/setting-panel/property-editor/container-table/table-dynamicField-editor.vue +8 -8
- package/src/components/xform/form-designer/setting-panel/property-editor/dropdownFlag-editor.vue +137 -137
- package/src/components/xform/form-designer/setting-panel/property-editor/field-button/search-dialog-event-editor.vue +237 -237
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date/date-defaultValue-editor.vue +43 -43
- package/src/components/xform/form-designer/setting-panel/property-editor/field-date-range/date-range-defaultValue-editor.vue +43 -43
- package/src/components/xform/form-designer/setting-panel/property-editor/field-import-button/import2-button-editor.vue +96 -96
- package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +51 -0
- package/src/components/xform/form-designer/setting-panel/property-editor/field-time/time-defaultValue-editor.vue +42 -42
- package/src/components/xform/form-designer/setting-panel/property-editor/field-time-range/time-range-defaultValue-editor.vue +43 -43
- package/src/components/xform/form-designer/setting-panel/property-editor/formScriptEnabled-editor.vue +158 -158
- package/src/components/xform/form-designer/setting-panel/property-editor/h5Span-editor.vue +51 -0
- package/src/components/xform/form-designer/setting-panel/property-editor/itemStyle-editor.vue +39 -0
- package/src/components/xform/form-designer/setting-panel/property-editor/name-editor.vue +188 -188
- package/src/components/xform/form-designer/setting-panel/property-editor/styleTempClass-editor.vue +51 -0
- package/src/components/xform/form-designer/setting-panel/property-editor/textFlag-editor.vue +347 -347
- package/src/components/xform/form-designer/setting-panel/propertyRegister.js +4 -0
- package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +59 -3
- package/src/components/xform/form-render/container-item/data-table-mixin.js +151 -93
- package/src/components/xform/form-render/container-item/detail-h5-item.vue +183 -32
- package/src/components/xform/form-render/container-item/detail-temp-item.vue +110 -0
- package/src/components/xform/form-render/container-item/h5-card-pane-item.vue +160 -140
- package/src/components/xform/form-render/container-item/list-h5-item.vue +1688 -1700
- package/src/components/xform/form-render/index.vue +153 -147
- package/src/components/xform/form-render/indexMixin.js +12 -0
- package/src/components/xform/lang/en-US.js +10 -0
- package/src/components/xform/lang/zh-CN.js +12 -0
- package/src/components/xform/mixins/defaultHandle.js +707 -707
- package/src/components/xform/styles/h5.scss +1992 -0
- package/src/components/xform/utils/textMaskUtil.js +182 -179
- package/src/components/xform/utils/treeTableUtil.js +1292 -1265
- package/src/components/xform/utils/util.js +56 -11
- package/src/index.js +9 -4
- package/src/layout/components/Sidebar/default.vue +1 -1
- package/src/layout/components/extractedCode/viewDialog.vue +7 -7
- package/src/router/index.js +21 -0
- package/src/router/modules/customer.js +71 -14
- package/src/store/modules/permission.js +17 -18
- package/src/utils/pdfUtil.js +429 -429
- package/src/utils/resolveViewComponent.js +142 -16
- package/src/views/bd/project/branch/bd_branch/edit.vue +145 -0
- package/src/views/bd/project/branch/bd_branch/list.vue +72 -0
- package/src/views/bd/project/branch/bd_branch/menuKindDialog.vue +106 -0
- package/src/views/bd/project/branch/bd_branch/mixins/edit.js +189 -0
- package/src/views/bd/project/branch/bd_branch/mixins/list.js +124 -0
- package/src/views/bd/project/branch/bd_branch/mixins/menuKindDialog.js +85 -0
- package/src/views/bd/project/branch/form_script/list.vue +31 -0
- package/src/views/bd/project/branch/form_template/list.vue +30 -0
- package/src/views/bd/project/branch/menu_kind/list.vue +31 -0
- package/src/views/bd/project/common/form_script/list.vue +16 -0
- package/src/views/bd/project/common/form_template/list.vue +15 -0
- package/src/views/bd/project/common/menu_kind/list.vue +15 -0
- package/src/views/bd/project/core/branchContext.js +100 -0
- package/src/views/bd/project/core/branchPage.js +78 -0
- package/src/views/bd/project/core/branchPageWrap.vue +225 -0
- package/src/views/bd/project/core/branchScope.js +44 -0
- package/src/views/bd/project/core/branchSearchPage.js +285 -0
- package/src/views/bd/project/core/branchSelect.vue +85 -0
- package/src/views/bd/project/core/branchTreeBar.vue +81 -0
- package/src/views/bd/project/core/httpBranchPatch.js +58 -0
- package/src/views/bd/project/datatable/menu_kind/list.vue +15 -0
- package/src/views/bd/project/datatable/table_model/list.vue +15 -0
- package/src/views/bd/setting/bd_company_env/dialog.vue +174 -174
- package/src/views/bd/setting/table_model/edit.vue +1066 -1066
- package/src/views/bd/setting/table_model/mixins/edit.js +1455 -1455
- package/src/views/bd/setting/table_model/mixins/zdDialog.js +148 -148
- package/src/views/demo/bizTab/bill/billConfig.js +41 -0
- package/src/views/demo/bizTab/bill/detail.vue +436 -0
- package/src/views/demo/bizTab/bill/list.vue +288 -0
- package/src/views/demo/bizTab/bill/productDialog.vue +176 -0
- package/src/views/demo/bizTab/simple/billApi.js +123 -0
- package/src/views/demo/bizTab/simple/detail.vue +407 -0
- package/src/views/demo/bizTab/simple/list.vue +217 -0
- package/src/views/demo/bizTab/standalone/index.vue +121 -0
- package/src/views/demo/bizTab/wf/billConfig.js +44 -0
- package/src/views/demo/bizTab/wf/detail.vue +661 -0
- package/src/views/demo/bizTab/wf/list.vue +242 -0
- package/src/views/support/form-dialog-demo/demoPickerDialog.vue +72 -0
- package/src/views/support/form-dialog-demo/index.vue +537 -0
- package/src/views/user/file_view_ins/list.vue +3 -3
- package/src/views/user/home/default.vue +1 -1
- package/src/views/user/home/default2.vue +1 -1
- package/src/views/user/home/index.vue +8 -6
- package/src/views/user/home/wjl/content.vue +1569 -0
- package/src/views/user/notify_message/dialog.vue +7 -5
- package/src/views/user/outLink/cc_form_view.vue +188 -0
- package/src/views/user/outLink/form_view.vue +2 -2
- package/src/views/user/outLink/index.vue +4 -4
- package/src/views/user/outLink/view.vue +3 -3
- package/src/views/user/role/edit.vue +632 -632
- package/src/views/user/role/list.vue +260 -260
- package/src/views/user/wf/iframe/index.vue +3 -3
- package/src/views/user/wf/iframe/index2.vue +3 -3
- package/src/views/user/wf/wf_manage/list.vue +1112 -1116
- package/src/views/user/wf/wf_manage/list2.vue +9 -13
- package/src/views/user/wf/wf_manage/wfContentDialog.vue +8 -5
- package/src/components/xform/form-designer/form-widget/field-widget/dropdown-menu-widget.vue +0 -106
- package/src/components/xform/form-designer/form-widget/field-widget/tableexportbuttonwidget.vue +0 -99
- package/src/components/xform/form-render/container-item/list-h5-item2.vue +0 -1340
- package/src/utils/wf.js +0 -766
|
@@ -1,707 +1,707 @@
|
|
|
1
|
-
import moment from "moment";
|
|
2
|
-
import { saveUserLog } from "@base/api/user";
|
|
3
|
-
import settingConfig from "@/settings";
|
|
4
|
-
import { openStartWfDialog } from "../../../components/wf/wfUtil";
|
|
5
|
-
import { getWfButtonNames } from "../../../components/wf/wfButtonName";
|
|
6
|
-
import {
|
|
7
|
-
isWfAutoConfirm,
|
|
8
|
-
loadWfAutoConfirm,
|
|
9
|
-
} from "../../../components/wf/wfAutoConfirm";
|
|
10
|
-
import {
|
|
11
|
-
isWfUserRangeEnabled,
|
|
12
|
-
loadWfUserRange,
|
|
13
|
-
} from "../../../components/wf/wfUserRange";
|
|
14
|
-
|
|
15
|
-
let modules = {};
|
|
16
|
-
modules = {
|
|
17
|
-
data() {
|
|
18
|
-
return {
|
|
19
|
-
timerMap: {},
|
|
20
|
-
formSavePending: false,
|
|
21
|
-
formSaveReconcileTask: null,
|
|
22
|
-
};
|
|
23
|
-
},
|
|
24
|
-
beforeDestroy() {
|
|
25
|
-
for (let timer in this.timerMap) {
|
|
26
|
-
this.clearInterval(timer);
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
methods: {
|
|
30
|
-
getMoment(date) {
|
|
31
|
-
return moment(date);
|
|
32
|
-
},
|
|
33
|
-
getMomentUtil() {
|
|
34
|
-
return moment;
|
|
35
|
-
},
|
|
36
|
-
utcToCst(utcStr) {
|
|
37
|
-
if (!utcStr) return utcStr;
|
|
38
|
-
return moment.utc(utcStr).local().format("YYYY-MM-DD HH:mm:ss");
|
|
39
|
-
},
|
|
40
|
-
cstToUtc(localTime) {
|
|
41
|
-
if (!localTime) return localTime;
|
|
42
|
-
return moment(localTime).utc().format("YYYY-MM-DD HH:mm:ss");
|
|
43
|
-
},
|
|
44
|
-
addFormatDateTime(date, num, type, format) {
|
|
45
|
-
if (!date) return date;
|
|
46
|
-
if (!type) type = "days";
|
|
47
|
-
if (!format) format = "YYYY-MM-DD HH:mm:ss";
|
|
48
|
-
return moment(date).add(num, type).format(format);
|
|
49
|
-
},
|
|
50
|
-
addFormatDate(date, num) {
|
|
51
|
-
if (!date) return date;
|
|
52
|
-
return moment(date).add(num, "days").format("YYYY-MM-DD");
|
|
53
|
-
},
|
|
54
|
-
formatDate(date) {
|
|
55
|
-
if (!date) return date;
|
|
56
|
-
return moment(date).format("YYYY-MM-DD");
|
|
57
|
-
},
|
|
58
|
-
formatDateTime(date, format = "YYYY-MM-DD HH:mm:ss") {
|
|
59
|
-
if (!date) return date;
|
|
60
|
-
return moment(date).format(format);
|
|
61
|
-
},
|
|
62
|
-
getFormEntityData(reqFormData) {
|
|
63
|
-
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
64
|
-
let formConfig = formRef.formConfig;
|
|
65
|
-
let entity = formConfig.entity;
|
|
66
|
-
if (!reqFormData) reqFormData = this.getReqFormData();
|
|
67
|
-
return reqFormData.data[entity];
|
|
68
|
-
},
|
|
69
|
-
getReqFormData(formDataOverride) {
|
|
70
|
-
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
71
|
-
let formConfig = formRef.formConfig;
|
|
72
|
-
let entity = formConfig.entity;
|
|
73
|
-
if (!entity) return;
|
|
74
|
-
let dataId = formRef?.dataId;
|
|
75
|
-
let reportTemplate = formRef?.reportTemplate;
|
|
76
|
-
let formCode = reportTemplate?.formCode;
|
|
77
|
-
|
|
78
|
-
let sourceFormData
|
|
79
|
-
= arguments.length > 0
|
|
80
|
-
? formDataOverride
|
|
81
|
-
: formRef.getRealFormData();
|
|
82
|
-
let formData = this.$baseLodash.cloneDeep(sourceFormData);
|
|
83
|
-
let mainData = formData[formCode] || {};
|
|
84
|
-
delete formData[formCode];
|
|
85
|
-
formData[entity] = mainData;
|
|
86
|
-
|
|
87
|
-
delete mainData._createNickName;
|
|
88
|
-
delete mainData._createMobile;
|
|
89
|
-
delete mainData._modifyNickName;
|
|
90
|
-
delete mainData._modifyMobile;
|
|
91
|
-
delete mainData._createBy;
|
|
92
|
-
delete mainData._modifyBy;
|
|
93
|
-
|
|
94
|
-
// 移除对比功能给各子表行加的快照字段 hData,避免提交到后端
|
|
95
|
-
let stripHData = (obj) => {
|
|
96
|
-
if (!obj || typeof obj !== "object") return;
|
|
97
|
-
if (Array.isArray(obj)) {
|
|
98
|
-
obj.forEach(stripHData);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
if (Object.prototype.hasOwnProperty.call(obj, "hData")) {
|
|
102
|
-
delete obj.hData;
|
|
103
|
-
}
|
|
104
|
-
Object.keys(obj).forEach((k) => {
|
|
105
|
-
let v = obj[k];
|
|
106
|
-
if (v && typeof v === "object") stripHData(v);
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
stripHData(formData);
|
|
110
|
-
|
|
111
|
-
let reqData = {
|
|
112
|
-
formCode: formCode,
|
|
113
|
-
formVersion: reportTemplate.formVersion,
|
|
114
|
-
taBm: entity,
|
|
115
|
-
data: formData,
|
|
116
|
-
};
|
|
117
|
-
return reqData;
|
|
118
|
-
},
|
|
119
|
-
onAfterSaveHandle(reqData, res, response) {
|
|
120
|
-
//保存成功后处理
|
|
121
|
-
this.writeSaveLog(reqData, res, response); //写入保存日志
|
|
122
|
-
},
|
|
123
|
-
writeSaveLog(reqData, res, response) {
|
|
124
|
-
//写入保存日志
|
|
125
|
-
if (settingConfig.formSaveLogEnabled && res.type === "success") {
|
|
126
|
-
let responseConfig = response.config || {};
|
|
127
|
-
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
128
|
-
let reportTemplate = formRef ? formRef.reportTemplate : null;
|
|
129
|
-
if (!reportTemplate) return;
|
|
130
|
-
let isAdd = !formRef.dataId;
|
|
131
|
-
let formName = reportTemplate?.formName;
|
|
132
|
-
let formCode = reportTemplate?.formCode;
|
|
133
|
-
let action = isAdd ? "新增成功" : "更新成功";
|
|
134
|
-
let content = `${formName}(${formCode}),${action}`;
|
|
135
|
-
let path = responseConfig.url.replace(responseConfig.baseURL, "");
|
|
136
|
-
saveUserLog({
|
|
137
|
-
data: {
|
|
138
|
-
path: path,
|
|
139
|
-
businessCode: content,
|
|
140
|
-
content: content,
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
async saveAndSubmitWfHandle(option) {
|
|
146
|
-
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
147
|
-
let formConfig = formRef.formConfig;
|
|
148
|
-
let reportTemplate = formRef.reportTemplate;
|
|
149
|
-
let wfParam = formRef.wfParam || {};
|
|
150
|
-
|
|
151
|
-
if (!formConfig?.wfEnabled) return;
|
|
152
|
-
if (
|
|
153
|
-
formRef.hasWf ||
|
|
154
|
-
wfParam.hasWf ||
|
|
155
|
-
wfParam.wfInfo?.procInstId
|
|
156
|
-
) {
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 提示语跟随后台 param18 下发的按钮名;没配就保持原文案不变。
|
|
161
|
-
// 只能用 $t1(找不到 key 原样返回),$t2 在 i18n 命中 key 时会把拼进去的名字吞掉。
|
|
162
|
-
let btnName = getWfButtonNames().saveSubmitBtn;
|
|
163
|
-
let confirmText =
|
|
164
|
-
option?.confirmText ||
|
|
165
|
-
option?.config?.confirmText ||
|
|
166
|
-
(btnName
|
|
167
|
-
? this.$t1(`您确定要${btnName}吗?`)
|
|
168
|
-
: this.$t1("您确定要保存并提交流程吗?"));
|
|
169
|
-
|
|
170
|
-
// param19 + param12 都开:这道「保存前」的确认框不弹,把确认交给 wfStartDialog——
|
|
171
|
-
// 它在保存之后才探 preStart,探到候选人就由「修改候选人」弹框顶替,探不到才弹确认框。
|
|
172
|
-
// 任一为 false 都当场弹:param12 关着后面不可能有候选人弹框,延后没有意义。
|
|
173
|
-
await Promise.all([loadWfAutoConfirm(this), loadWfUserRange(this)]);
|
|
174
|
-
let autoConfirm = isWfAutoConfirm() && isWfUserRangeEnabled();
|
|
175
|
-
|
|
176
|
-
return this.saveDefaultHandle({
|
|
177
|
-
...option,
|
|
178
|
-
config: {
|
|
179
|
-
...option?.config,
|
|
180
|
-
successMsg: option?.config?.successMsg ?? true,
|
|
181
|
-
isConfirm: option?.config?.isConfirm ?? !autoConfirm,
|
|
182
|
-
confirmText: option?.config?.confirmText || confirmText,
|
|
183
|
-
skipHostReloadAfterSave: true,
|
|
184
|
-
//提交前置处理(与「提交流程」按钮共用同一个表单脚本)
|
|
185
|
-
beforeConfirm: () =>
|
|
186
|
-
formRef.runBeforeSubmitWf
|
|
187
|
-
? formRef.runBeforeSubmitWf("saveSubmit")
|
|
188
|
-
: true,
|
|
189
|
-
success: (res) => {
|
|
190
|
-
// ★ 保存成功后,只升级新增页签的「元数据」为编辑态(设 dataId/isAdd/formCode/标题),
|
|
191
|
-
// 不重渲染详情内容。这样即便随后提交失败/取消候选人弹框,页签也已是编辑态,
|
|
192
|
-
// 再次提交只会更新、不会重复入库;而详情内容的刷新交给最终的提交后行为(避免刷两次)。
|
|
193
|
-
let editFormCode = formConfig.editFormCode;
|
|
194
|
-
let listPageRef = formRef.getListPageRef && formRef.getListPageRef();
|
|
195
|
-
if (listPageRef && listPageRef.promoteActiveAddTab) {
|
|
196
|
-
listPageRef.promoteActiveAddTab(res.objx, editFormCode, formRef);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
openStartWfDialog(formRef, {
|
|
200
|
-
objId: res.objx,
|
|
201
|
-
wfCode: reportTemplate.objTypeCode,
|
|
202
|
-
formCode: reportTemplate.formCode,
|
|
203
|
-
serviceId: reportTemplate.serviceName,
|
|
204
|
-
// 上面确认过就不再弹第二次;上面因 param19 跳过了,就得让弹框自己去确认
|
|
205
|
-
skipStartConfirm: !autoConfirm,
|
|
206
|
-
// 提交后行为复用 xform 侧统一方法:
|
|
207
|
-
// 成功 → onWfStartSuccess(关页签/刷新本页);失败/取消 → onWfStartAbort(刷新到编辑态)
|
|
208
|
-
onStartSuccess: () =>
|
|
209
|
-
formRef.onWfStartSuccess && formRef.onWfStartSuccess(),
|
|
210
|
-
onStartFail: () =>
|
|
211
|
-
formRef.onWfStartAbort && formRef.onWfStartAbort(),
|
|
212
|
-
onCancel: () => formRef.onWfStartAbort && formRef.onWfStartAbort(),
|
|
213
|
-
});
|
|
214
|
-
option?.config?.success && option.config.success(res);
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
},
|
|
219
|
-
async saveDefaultHandle(option) {
|
|
220
|
-
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
221
|
-
let formConfig = formRef.formConfig;
|
|
222
|
-
let entity = formConfig.entity;
|
|
223
|
-
if (!entity) return;
|
|
224
|
-
let scriptCode = formConfig.saveScriptCode || "saveUpdate";
|
|
225
|
-
let config = { ...(option?.config || {}) };
|
|
226
|
-
let skipHostReloadAfterSave
|
|
227
|
-
= config.skipHostReloadAfterSave === true;
|
|
228
|
-
delete config.skipHostReloadAfterSave;
|
|
229
|
-
if (typeof formRef.formSaveReconcileTask === "function") {
|
|
230
|
-
return formRef.formSaveReconcileTask();
|
|
231
|
-
}
|
|
232
|
-
if (formRef.formSavePending) {
|
|
233
|
-
this.$message.warning(this.$t1("数据正在保存,请勿重复操作"));
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
formRef.formSavePending = true;
|
|
238
|
-
let keepSaveLocked = false;
|
|
239
|
-
try {
|
|
240
|
-
let valid = await new Promise((resolve, reject) => {
|
|
241
|
-
try {
|
|
242
|
-
formRef.validate(resolve);
|
|
243
|
-
} catch (error) {
|
|
244
|
-
reject(error);
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
if (!valid) return false;
|
|
248
|
-
|
|
249
|
-
// 提交前置处理:校验通过后、确认框之前拦截。只有「保存并提交流程」入口会传,
|
|
250
|
-
// 普通保存不受影响。返回 false 即中止(finally 里会释放 formSavePending)。
|
|
251
|
-
let beforeConfirm = config.beforeConfirm;
|
|
252
|
-
delete config.beforeConfirm; //不能透传给 formHttp
|
|
253
|
-
if (typeof beforeConfirm === "function") {
|
|
254
|
-
let pass = await beforeConfirm();
|
|
255
|
-
if (pass === false) return false;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
let submitFormData
|
|
259
|
-
= typeof formRef.buildSubmitPayload === "function"
|
|
260
|
-
? await formRef.buildSubmitPayload()
|
|
261
|
-
: formRef.getRealFormData();
|
|
262
|
-
let reqData = this.getReqFormData(submitFormData);
|
|
263
|
-
let shouldConfirm = config.isConfirm ?? true;
|
|
264
|
-
let confirmText
|
|
265
|
-
= config.confirmText || this.$t1("您确定要保存吗?");
|
|
266
|
-
if (shouldConfirm) {
|
|
267
|
-
try {
|
|
268
|
-
await this.$baseConfirm(confirmText);
|
|
269
|
-
} catch (error) {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
let userSuccess = config.success;
|
|
275
|
-
let userFail = config.fail;
|
|
276
|
-
let userError = config.error;
|
|
277
|
-
let userCallback = config.callback;
|
|
278
|
-
|
|
279
|
-
return await new Promise((resolve, reject) => {
|
|
280
|
-
let settled = false;
|
|
281
|
-
let resolveOnce = (value) => {
|
|
282
|
-
if (settled) return;
|
|
283
|
-
settled = true;
|
|
284
|
-
resolve(value);
|
|
285
|
-
};
|
|
286
|
-
let rejectOnce = (error) => {
|
|
287
|
-
if (settled) return;
|
|
288
|
-
settled = true;
|
|
289
|
-
reject(error);
|
|
290
|
-
};
|
|
291
|
-
let requestTask = this.formHttp({
|
|
292
|
-
// url: "/" + reportTemplate.serviceName + "/form_ins/saveUpdate",
|
|
293
|
-
scriptCode: scriptCode,
|
|
294
|
-
data: reqData,
|
|
295
|
-
successMsg: true,
|
|
296
|
-
...config,
|
|
297
|
-
isConfirm: false,
|
|
298
|
-
confirmText,
|
|
299
|
-
callback: undefined,
|
|
300
|
-
success: async (res, response) => {
|
|
301
|
-
let editFormCode = formConfig.editFormCode;
|
|
302
|
-
let updateParam = {
|
|
303
|
-
dataId: res.objx,
|
|
304
|
-
};
|
|
305
|
-
if (editFormCode) {
|
|
306
|
-
updateParam.formCode = editFormCode;
|
|
307
|
-
}
|
|
308
|
-
// 保存成功后一律让宿主整页重挂载(toggle v-if):新增/编辑都需要出流程信息、
|
|
309
|
-
// 切编辑模板、按新状态重算显隐与只读——这些就地取数做不到。
|
|
310
|
-
// 唯一例外是「保存并提交」路径(skipHostReloadAfterSave=true),
|
|
311
|
-
// 它的刷新交给流程结束后的 onWfStartSuccess / onWfStartAbort,避免刷两次。
|
|
312
|
-
//
|
|
313
|
-
// 刷新前只同步「元数据」,绝不做整单 setFormData:表单已挂载时那是逐字段同步
|
|
314
|
-
// 广播赋值,每个字段的 onChange 会当场执行,先赋值组件的脚本读到的是后赋值
|
|
315
|
-
// 组件的旧值,公式与 keyName 联动乱序,子表还会被整体清空(getOne 不返回子表)。
|
|
316
|
-
// 首屏之所以正确,靠的是「先填满 formDataModel 再挂载组件」,就地整单赋值破坏
|
|
317
|
-
// 了这个前提,故整单刷新只能走重挂载。
|
|
318
|
-
let syncSavedMeta = () => {
|
|
319
|
-
let nextDataId = updateParam.dataId;
|
|
320
|
-
if (
|
|
321
|
-
nextDataId !== undefined
|
|
322
|
-
&& nextDataId !== null
|
|
323
|
-
&& nextDataId !== ""
|
|
324
|
-
) {
|
|
325
|
-
// 提交报文的主表以 currentFormData 打底(见 form-render getSubData2),
|
|
326
|
-
// 主键 id 不是表单控件,只能在此回写;否则 skip 路径下再次保存会重复入库。
|
|
327
|
-
if (
|
|
328
|
-
formRef.currentFormData
|
|
329
|
-
&& formRef.currentFormData.id == null
|
|
330
|
-
) {
|
|
331
|
-
formRef.currentFormData.id = nextDataId;
|
|
332
|
-
}
|
|
333
|
-
formRef.$emit("update:dataId", nextDataId);
|
|
334
|
-
}
|
|
335
|
-
if (updateParam.formCode) {
|
|
336
|
-
formRef.$emit("update:formCode", updateParam.formCode);
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
let runHostReload = async () => {
|
|
340
|
-
if (skipHostReloadAfterSave) return;
|
|
341
|
-
await formRef.reloadForm({ updateParam });
|
|
342
|
-
};
|
|
343
|
-
let callbacksStarted = false;
|
|
344
|
-
let runPostReloadCallbacks = async () => {
|
|
345
|
-
if (callbacksStarted) return res;
|
|
346
|
-
callbacksStarted = true;
|
|
347
|
-
let callbacks = [
|
|
348
|
-
() => this.onAfterSaveHandle(reqData, res, response),
|
|
349
|
-
typeof userSuccess === "function"
|
|
350
|
-
? () => userSuccess(res)
|
|
351
|
-
: null,
|
|
352
|
-
typeof option?.callback === "function"
|
|
353
|
-
? () => option.callback(res)
|
|
354
|
-
: null,
|
|
355
|
-
typeof userCallback === "function"
|
|
356
|
-
? () => userCallback(res, response)
|
|
357
|
-
: null,
|
|
358
|
-
].filter(Boolean);
|
|
359
|
-
let hasCallbackError = false;
|
|
360
|
-
let firstCallbackError;
|
|
361
|
-
for (let index = 0; index < callbacks.length; index++) {
|
|
362
|
-
try {
|
|
363
|
-
await callbacks[index]();
|
|
364
|
-
} catch (error) {
|
|
365
|
-
if (!hasCallbackError) {
|
|
366
|
-
hasCallbackError = true;
|
|
367
|
-
firstCallbackError = error;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (hasCallbackError) {
|
|
372
|
-
let detail
|
|
373
|
-
= firstCallbackError?.message || firstCallbackError;
|
|
374
|
-
let message = this.$t1("已保存,后续处理失败");
|
|
375
|
-
this.$message.error(
|
|
376
|
-
detail ? `${message}:${detail}` : message
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
return res;
|
|
380
|
-
};
|
|
381
|
-
let clearReconcileState = () => {
|
|
382
|
-
formRef.formSaveReconcileTask = null;
|
|
383
|
-
formRef.formSavePending = false;
|
|
384
|
-
};
|
|
385
|
-
let showHostReloadFailure = (retry, error) => {
|
|
386
|
-
let detail = error?.message;
|
|
387
|
-
let message = this.$t1(
|
|
388
|
-
retry
|
|
389
|
-
? "重新加载仍未成功,请手动刷新后再操作"
|
|
390
|
-
: "保存成功,但重新加载失败,请手动刷新后再操作"
|
|
391
|
-
);
|
|
392
|
-
this.$message.error(
|
|
393
|
-
detail ? `${message}:${detail}` : message
|
|
394
|
-
);
|
|
395
|
-
};
|
|
396
|
-
let installHostReloadRetry = (error) => {
|
|
397
|
-
keepSaveLocked = true;
|
|
398
|
-
let hostRetryPromise = null;
|
|
399
|
-
formRef.formSaveReconcileTask = () => {
|
|
400
|
-
if (hostRetryPromise) return hostRetryPromise;
|
|
401
|
-
hostRetryPromise = (async () => {
|
|
402
|
-
try {
|
|
403
|
-
await runHostReload();
|
|
404
|
-
clearReconcileState();
|
|
405
|
-
return res;
|
|
406
|
-
} catch (retryError) {
|
|
407
|
-
showHostReloadFailure(true, retryError);
|
|
408
|
-
return false;
|
|
409
|
-
} finally {
|
|
410
|
-
hostRetryPromise = null;
|
|
411
|
-
}
|
|
412
|
-
})();
|
|
413
|
-
return hostRetryPromise;
|
|
414
|
-
};
|
|
415
|
-
showHostReloadFailure(false, error);
|
|
416
|
-
};
|
|
417
|
-
syncSavedMeta();
|
|
418
|
-
await runPostReloadCallbacks();
|
|
419
|
-
try {
|
|
420
|
-
await runHostReload();
|
|
421
|
-
} catch (hostReloadError) {
|
|
422
|
-
installHostReloadRetry(hostReloadError);
|
|
423
|
-
rejectOnce(hostReloadError);
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
resolveOnce(res);
|
|
427
|
-
},
|
|
428
|
-
fail: (res, response) => {
|
|
429
|
-
this.onAfterSaveHandle(reqData, res, response);
|
|
430
|
-
if (typeof option?.callback === "function") {
|
|
431
|
-
option.callback(res);
|
|
432
|
-
}
|
|
433
|
-
if (typeof userCallback === "function") {
|
|
434
|
-
userCallback(res, response);
|
|
435
|
-
}
|
|
436
|
-
Promise.resolve(
|
|
437
|
-
typeof userFail === "function" ? userFail(res) : null
|
|
438
|
-
).then(() => resolveOnce(res), rejectOnce);
|
|
439
|
-
},
|
|
440
|
-
error: (error) => {
|
|
441
|
-
Promise.resolve(
|
|
442
|
-
typeof userError === "function" ? userError(error) : null
|
|
443
|
-
).then(() => rejectOnce(error), rejectOnce);
|
|
444
|
-
},
|
|
445
|
-
});
|
|
446
|
-
if (requestTask && typeof requestTask.catch === "function") {
|
|
447
|
-
requestTask.catch(rejectOnce);
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
} catch (error) {
|
|
451
|
-
if (!keepSaveLocked) {
|
|
452
|
-
let message
|
|
453
|
-
= error?.message || error || this.$t1("保存失败");
|
|
454
|
-
this.$message.error(message);
|
|
455
|
-
}
|
|
456
|
-
return false;
|
|
457
|
-
} finally {
|
|
458
|
-
if (!keepSaveLocked) {
|
|
459
|
-
formRef.formSavePending = false;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
},
|
|
463
|
-
setInterval(callback, millis) {
|
|
464
|
-
let timer = setInterval(callback, millis);
|
|
465
|
-
this.timerMap[timer] = timer;
|
|
466
|
-
},
|
|
467
|
-
clearInterval(timer) {
|
|
468
|
-
clearInterval(timer);
|
|
469
|
-
delete this.timerMap[timer];
|
|
470
|
-
},
|
|
471
|
-
getGlobalConfig() {
|
|
472
|
-
let that = this.getFormRef ? this.getFormRef() : this;
|
|
473
|
-
return that.globalConfig;
|
|
474
|
-
},
|
|
475
|
-
getFormParam() {
|
|
476
|
-
let that = this.getFormRef ? this.getFormRef() : this;
|
|
477
|
-
return that.param;
|
|
478
|
-
},
|
|
479
|
-
setGlobalParam(key, value) {
|
|
480
|
-
let that = this.getFormRef ? this.getFormRef() : this;
|
|
481
|
-
that.globalParam[key] = value;
|
|
482
|
-
},
|
|
483
|
-
getGlobalParam(key) {
|
|
484
|
-
let that = this.getFormRef ? this.getFormRef() : this;
|
|
485
|
-
return that.globalParam[key];
|
|
486
|
-
},
|
|
487
|
-
|
|
488
|
-
//1个数组的行号处理 begin
|
|
489
|
-
/**
|
|
490
|
-
* 删除列表中的行并刷新序列
|
|
491
|
-
* @param {Array} list - 数据数组
|
|
492
|
-
* @param {number} deleteIndex - 要删除的索引
|
|
493
|
-
* @param {string} [seqField='seq'] - 序列字段名
|
|
494
|
-
* @returns {Array} 更新后的数组
|
|
495
|
-
*/
|
|
496
|
-
deleteNumRows(list, deleteIndex, seqField = "seq") {
|
|
497
|
-
if (deleteIndex < 0 || deleteIndex >= list.length) {
|
|
498
|
-
return [...list]; // 返回副本
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// 创建副本避免修改原数组
|
|
502
|
-
const newList = [...list];
|
|
503
|
-
|
|
504
|
-
// 删除指定行
|
|
505
|
-
newList.splice(deleteIndex, 1);
|
|
506
|
-
|
|
507
|
-
// 重新计算序列
|
|
508
|
-
newList.forEach((item, index) => {
|
|
509
|
-
item[seqField] = index + 1;
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
return newList;
|
|
513
|
-
},
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* 在列表中插入新行并刷新序列
|
|
517
|
-
* @param {Array} list - 数据数组
|
|
518
|
-
* @param {Array|Object} newItems - 要插入的新项(单个或多个)
|
|
519
|
-
* @param {number} [insertIndex] - 插入位置(默认末尾)
|
|
520
|
-
* @param {string} [seqField='seq'] - 序列字段名
|
|
521
|
-
* @returns {Array} 更新后的数组
|
|
522
|
-
*/
|
|
523
|
-
insertNumRows(list, newItems, insertIndex = list.length, seqField = "seq") {
|
|
524
|
-
// 参数标准化
|
|
525
|
-
const itemsArray = Array.isArray(newItems) ? newItems : [newItems];
|
|
526
|
-
|
|
527
|
-
// 边界检查
|
|
528
|
-
insertIndex = Math.max(0, Math.min(insertIndex, list.length));
|
|
529
|
-
|
|
530
|
-
// 创建副本
|
|
531
|
-
const newList = [...list];
|
|
532
|
-
|
|
533
|
-
// 准备要插入的项(移除可能的旧序列)
|
|
534
|
-
const itemsToInsert = itemsArray.map((item) => {
|
|
535
|
-
const newItem = { ...item };
|
|
536
|
-
if (newItem[seqField] !== undefined) {
|
|
537
|
-
delete newItem[seqField];
|
|
538
|
-
}
|
|
539
|
-
return newItem;
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
// 插入新项
|
|
543
|
-
newList.splice(insertIndex, 0, ...itemsToInsert);
|
|
544
|
-
|
|
545
|
-
// 重新计算所有序列
|
|
546
|
-
newList.forEach((item, index) => {
|
|
547
|
-
item[seqField] = index + 1;
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
return newList;
|
|
551
|
-
},
|
|
552
|
-
//1个数组的行号处理 end
|
|
553
|
-
|
|
554
|
-
//2个数组的行号处理 begin
|
|
555
|
-
/**
|
|
556
|
-
* 通用新增方法(支持单条/多条)
|
|
557
|
-
* @param {Array} list1 - 主数据数组
|
|
558
|
-
* @param {Array} list2 - 关联数据数组
|
|
559
|
-
* @param {Array|Object} newItems - 要插入的新项(单个对象或多个对象的数组)
|
|
560
|
-
* @param {Array|Array[]} subItems - 关联数据(单个数组或多个数组的数组)
|
|
561
|
-
* @param {number} [insertIndex] - 插入位置(默认末尾)
|
|
562
|
-
* @param {string} [seqField='seq'] - list1中的序列字段名
|
|
563
|
-
* @param {string} [superSeqField='super_seq'] - list2中的关联字段名
|
|
564
|
-
* @returns {Object} 包含更新后的list1和list2
|
|
565
|
-
*/
|
|
566
|
-
insertRelRows(
|
|
567
|
-
list1,
|
|
568
|
-
list2,
|
|
569
|
-
newItems,
|
|
570
|
-
subItems = [],
|
|
571
|
-
insertIndex,
|
|
572
|
-
seqField = "seq",
|
|
573
|
-
superSeqField = "super_seq"
|
|
574
|
-
) {
|
|
575
|
-
// 参数标准化
|
|
576
|
-
const itemsArray = Array.isArray(newItems) ? newItems : [newItems];
|
|
577
|
-
const subItemsArray =
|
|
578
|
-
Array.isArray(subItems) &&
|
|
579
|
-
subItems.length > 0 &&
|
|
580
|
-
Array.isArray(subItems[0])
|
|
581
|
-
? subItems
|
|
582
|
-
: [subItems];
|
|
583
|
-
|
|
584
|
-
if (insertIndex === null || insertIndex === undefined) {
|
|
585
|
-
insertIndex = list1.length;
|
|
586
|
-
}
|
|
587
|
-
// 边界检查
|
|
588
|
-
insertIndex = Math.max(0, Math.min(insertIndex, list1.length));
|
|
589
|
-
|
|
590
|
-
// 创建副本
|
|
591
|
-
const newList1 = [...list1];
|
|
592
|
-
const newList2 = [...list2];
|
|
593
|
-
|
|
594
|
-
// 1. 插入新项(不设置序列)
|
|
595
|
-
const itemsToInsert = itemsArray.map((item) => {
|
|
596
|
-
const newItem = { ...item };
|
|
597
|
-
if (newItem[seqField] !== undefined) {
|
|
598
|
-
delete newItem[seqField]; // 确保不保留旧序列
|
|
599
|
-
}
|
|
600
|
-
return newItem;
|
|
601
|
-
});
|
|
602
|
-
|
|
603
|
-
newList1.splice(insertIndex, 0, ...itemsToInsert);
|
|
604
|
-
|
|
605
|
-
// 2. 重新计算序列并创建映射表
|
|
606
|
-
const seqMapping = {};
|
|
607
|
-
newList1.forEach((item, index) => {
|
|
608
|
-
const newSeq = index + 1;
|
|
609
|
-
const oldSeq = item[seqField];
|
|
610
|
-
|
|
611
|
-
// 记录旧序列到新序列的映射
|
|
612
|
-
if (oldSeq !== undefined) {
|
|
613
|
-
seqMapping[oldSeq] = newSeq;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
// 更新当前项序列
|
|
617
|
-
item[seqField] = newSeq;
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
// 3. 获取新插入项的序列
|
|
621
|
-
const newSeqs = [];
|
|
622
|
-
for (let i = 0; i < itemsArray.length; i++) {
|
|
623
|
-
newSeqs.push(insertIndex + i + 1);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// 4. 更新现有关联字段
|
|
627
|
-
newList2.forEach((item) => {
|
|
628
|
-
const currentSuperSeq = item[superSeqField];
|
|
629
|
-
if (seqMapping[currentSuperSeq] !== undefined) {
|
|
630
|
-
item[superSeqField] = seqMapping[currentSuperSeq];
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
|
|
634
|
-
// 5. 添加新关联数据
|
|
635
|
-
subItemsArray.forEach((subItems, idx) => {
|
|
636
|
-
const superSeq = newSeqs[idx];
|
|
637
|
-
if (Array.isArray(subItems) && subItems.length > 0) {
|
|
638
|
-
subItems.forEach((item) => {
|
|
639
|
-
const newItem = { ...item };
|
|
640
|
-
newItem[superSeqField] = superSeq;
|
|
641
|
-
newList2.push(newItem);
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
return { list1: newList1, list2: newList2 };
|
|
647
|
-
},
|
|
648
|
-
|
|
649
|
-
/**
|
|
650
|
-
* 删除关联行(从属数据打平传参)
|
|
651
|
-
* @param {Array} masters - 主数据数组
|
|
652
|
-
* @param {Array} slaves - 从属数据数组(打平的一维数组)
|
|
653
|
-
* @param {number} rowIdx - 要删除的主数据行索引
|
|
654
|
-
* @param {string} [seqField='seq'] - 行号字段名
|
|
655
|
-
* @param {string} [refField='super_seq'] - 关联字段名
|
|
656
|
-
* @returns {Object} 包含更新后的masters和slaves
|
|
657
|
-
*/
|
|
658
|
-
deleteRelRows(
|
|
659
|
-
masters,
|
|
660
|
-
slaves,
|
|
661
|
-
rowIdx,
|
|
662
|
-
seqField = "seq",
|
|
663
|
-
refField = "super_seq"
|
|
664
|
-
) {
|
|
665
|
-
// 边界检查(返回结构与正常分支保持一致:list1/list2)
|
|
666
|
-
if (rowIdx < 0 || rowIdx >= masters.length) {
|
|
667
|
-
return { list1: [...masters], list2: [...slaves] };
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// 创建副本
|
|
671
|
-
const newMasters = [...masters];
|
|
672
|
-
const newSlaves = [...slaves];
|
|
673
|
-
|
|
674
|
-
// 1. 删除主行并获取序列值
|
|
675
|
-
const [deleted] = newMasters.splice(rowIdx, 1);
|
|
676
|
-
const deletedSeq = deleted[seqField];
|
|
677
|
-
|
|
678
|
-
// 2. 删除关联的从属行
|
|
679
|
-
for (let i = newSlaves.length - 1; i >= 0; i--) {
|
|
680
|
-
if (newSlaves[i][refField] === deletedSeq) {
|
|
681
|
-
newSlaves.splice(i, 1);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// 3. 重新计算主数据序列
|
|
686
|
-
const seqMap = {};
|
|
687
|
-
newMasters.forEach((master, idx) => {
|
|
688
|
-
const oldSeq = master[seqField];
|
|
689
|
-
const newSeq = idx + 1;
|
|
690
|
-
seqMap[oldSeq] = newSeq;
|
|
691
|
-
master[seqField] = newSeq;
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
// 4. 更新从属数据引用
|
|
695
|
-
newSlaves.forEach((slave) => {
|
|
696
|
-
const ref = slave[refField];
|
|
697
|
-
if (seqMap[ref] !== undefined) {
|
|
698
|
-
slave[refField] = seqMap[ref];
|
|
699
|
-
}
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
return { list1: newMasters, list2: newSlaves };
|
|
703
|
-
},
|
|
704
|
-
//2个数组的行号处理 end
|
|
705
|
-
},
|
|
706
|
-
};
|
|
707
|
-
export default modules;
|
|
1
|
+
import moment from "moment";
|
|
2
|
+
import { saveUserLog } from "@base/api/user";
|
|
3
|
+
import settingConfig from "@/settings";
|
|
4
|
+
import { openStartWfDialog } from "../../../components/wf/wfUtil";
|
|
5
|
+
import { getWfButtonNames } from "../../../components/wf/wfButtonName";
|
|
6
|
+
import {
|
|
7
|
+
isWfAutoConfirm,
|
|
8
|
+
loadWfAutoConfirm,
|
|
9
|
+
} from "../../../components/wf/wfAutoConfirm";
|
|
10
|
+
import {
|
|
11
|
+
isWfUserRangeEnabled,
|
|
12
|
+
loadWfUserRange,
|
|
13
|
+
} from "../../../components/wf/wfUserRange";
|
|
14
|
+
|
|
15
|
+
let modules = {};
|
|
16
|
+
modules = {
|
|
17
|
+
data() {
|
|
18
|
+
return {
|
|
19
|
+
timerMap: {},
|
|
20
|
+
formSavePending: false,
|
|
21
|
+
formSaveReconcileTask: null,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
beforeDestroy() {
|
|
25
|
+
for (let timer in this.timerMap) {
|
|
26
|
+
this.clearInterval(timer);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
methods: {
|
|
30
|
+
getMoment(date) {
|
|
31
|
+
return moment(date);
|
|
32
|
+
},
|
|
33
|
+
getMomentUtil() {
|
|
34
|
+
return moment;
|
|
35
|
+
},
|
|
36
|
+
utcToCst(utcStr) {
|
|
37
|
+
if (!utcStr) return utcStr;
|
|
38
|
+
return moment.utc(utcStr).local().format("YYYY-MM-DD HH:mm:ss");
|
|
39
|
+
},
|
|
40
|
+
cstToUtc(localTime) {
|
|
41
|
+
if (!localTime) return localTime;
|
|
42
|
+
return moment(localTime).utc().format("YYYY-MM-DD HH:mm:ss");
|
|
43
|
+
},
|
|
44
|
+
addFormatDateTime(date, num, type, format) {
|
|
45
|
+
if (!date) return date;
|
|
46
|
+
if (!type) type = "days";
|
|
47
|
+
if (!format) format = "YYYY-MM-DD HH:mm:ss";
|
|
48
|
+
return moment(date).add(num, type).format(format);
|
|
49
|
+
},
|
|
50
|
+
addFormatDate(date, num) {
|
|
51
|
+
if (!date) return date;
|
|
52
|
+
return moment(date).add(num, "days").format("YYYY-MM-DD");
|
|
53
|
+
},
|
|
54
|
+
formatDate(date) {
|
|
55
|
+
if (!date) return date;
|
|
56
|
+
return moment(date).format("YYYY-MM-DD");
|
|
57
|
+
},
|
|
58
|
+
formatDateTime(date, format = "YYYY-MM-DD HH:mm:ss") {
|
|
59
|
+
if (!date) return date;
|
|
60
|
+
return moment(date).format(format);
|
|
61
|
+
},
|
|
62
|
+
getFormEntityData(reqFormData) {
|
|
63
|
+
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
64
|
+
let formConfig = formRef.formConfig;
|
|
65
|
+
let entity = formConfig.entity;
|
|
66
|
+
if (!reqFormData) reqFormData = this.getReqFormData();
|
|
67
|
+
return reqFormData.data[entity];
|
|
68
|
+
},
|
|
69
|
+
getReqFormData(formDataOverride) {
|
|
70
|
+
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
71
|
+
let formConfig = formRef.formConfig;
|
|
72
|
+
let entity = formConfig.entity;
|
|
73
|
+
if (!entity) return;
|
|
74
|
+
let dataId = formRef?.dataId;
|
|
75
|
+
let reportTemplate = formRef?.reportTemplate;
|
|
76
|
+
let formCode = reportTemplate?.formCode;
|
|
77
|
+
|
|
78
|
+
let sourceFormData
|
|
79
|
+
= arguments.length > 0
|
|
80
|
+
? formDataOverride
|
|
81
|
+
: formRef.getRealFormData();
|
|
82
|
+
let formData = this.$baseLodash.cloneDeep(sourceFormData);
|
|
83
|
+
let mainData = formData[formCode] || {};
|
|
84
|
+
delete formData[formCode];
|
|
85
|
+
formData[entity] = mainData;
|
|
86
|
+
|
|
87
|
+
delete mainData._createNickName;
|
|
88
|
+
delete mainData._createMobile;
|
|
89
|
+
delete mainData._modifyNickName;
|
|
90
|
+
delete mainData._modifyMobile;
|
|
91
|
+
delete mainData._createBy;
|
|
92
|
+
delete mainData._modifyBy;
|
|
93
|
+
|
|
94
|
+
// 移除对比功能给各子表行加的快照字段 hData,避免提交到后端
|
|
95
|
+
let stripHData = (obj) => {
|
|
96
|
+
if (!obj || typeof obj !== "object") return;
|
|
97
|
+
if (Array.isArray(obj)) {
|
|
98
|
+
obj.forEach(stripHData);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (Object.prototype.hasOwnProperty.call(obj, "hData")) {
|
|
102
|
+
delete obj.hData;
|
|
103
|
+
}
|
|
104
|
+
Object.keys(obj).forEach((k) => {
|
|
105
|
+
let v = obj[k];
|
|
106
|
+
if (v && typeof v === "object") stripHData(v);
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
stripHData(formData);
|
|
110
|
+
|
|
111
|
+
let reqData = {
|
|
112
|
+
formCode: formCode,
|
|
113
|
+
formVersion: reportTemplate.formVersion,
|
|
114
|
+
taBm: entity,
|
|
115
|
+
data: formData,
|
|
116
|
+
};
|
|
117
|
+
return reqData;
|
|
118
|
+
},
|
|
119
|
+
onAfterSaveHandle(reqData, res, response) {
|
|
120
|
+
//保存成功后处理
|
|
121
|
+
this.writeSaveLog(reqData, res, response); //写入保存日志
|
|
122
|
+
},
|
|
123
|
+
writeSaveLog(reqData, res, response) {
|
|
124
|
+
//写入保存日志
|
|
125
|
+
if (settingConfig.formSaveLogEnabled && res.type === "success") {
|
|
126
|
+
let responseConfig = response.config || {};
|
|
127
|
+
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
128
|
+
let reportTemplate = formRef ? formRef.reportTemplate : null;
|
|
129
|
+
if (!reportTemplate) return;
|
|
130
|
+
let isAdd = !formRef.dataId;
|
|
131
|
+
let formName = reportTemplate?.formName;
|
|
132
|
+
let formCode = reportTemplate?.formCode;
|
|
133
|
+
let action = isAdd ? "新增成功" : "更新成功";
|
|
134
|
+
let content = `${formName}(${formCode}),${action}`;
|
|
135
|
+
let path = responseConfig.url.replace(responseConfig.baseURL, "");
|
|
136
|
+
saveUserLog({
|
|
137
|
+
data: {
|
|
138
|
+
path: path,
|
|
139
|
+
businessCode: content,
|
|
140
|
+
content: content,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
async saveAndSubmitWfHandle(option) {
|
|
146
|
+
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
147
|
+
let formConfig = formRef.formConfig;
|
|
148
|
+
let reportTemplate = formRef.reportTemplate;
|
|
149
|
+
let wfParam = formRef.wfParam || {};
|
|
150
|
+
|
|
151
|
+
if (!formConfig?.wfEnabled) return;
|
|
152
|
+
if (
|
|
153
|
+
formRef.hasWf ||
|
|
154
|
+
wfParam.hasWf ||
|
|
155
|
+
wfParam.wfInfo?.procInstId
|
|
156
|
+
) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 提示语跟随后台 param18 下发的按钮名;没配就保持原文案不变。
|
|
161
|
+
// 只能用 $t1(找不到 key 原样返回),$t2 在 i18n 命中 key 时会把拼进去的名字吞掉。
|
|
162
|
+
let btnName = getWfButtonNames().saveSubmitBtn;
|
|
163
|
+
let confirmText =
|
|
164
|
+
option?.confirmText ||
|
|
165
|
+
option?.config?.confirmText ||
|
|
166
|
+
(btnName
|
|
167
|
+
? this.$t1(`您确定要${btnName}吗?`)
|
|
168
|
+
: this.$t1("您确定要保存并提交流程吗?"));
|
|
169
|
+
|
|
170
|
+
// param19 + param12 都开:这道「保存前」的确认框不弹,把确认交给 wfStartDialog——
|
|
171
|
+
// 它在保存之后才探 preStart,探到候选人就由「修改候选人」弹框顶替,探不到才弹确认框。
|
|
172
|
+
// 任一为 false 都当场弹:param12 关着后面不可能有候选人弹框,延后没有意义。
|
|
173
|
+
await Promise.all([loadWfAutoConfirm(this), loadWfUserRange(this)]);
|
|
174
|
+
let autoConfirm = isWfAutoConfirm() && isWfUserRangeEnabled();
|
|
175
|
+
|
|
176
|
+
return this.saveDefaultHandle({
|
|
177
|
+
...option,
|
|
178
|
+
config: {
|
|
179
|
+
...option?.config,
|
|
180
|
+
successMsg: option?.config?.successMsg ?? true,
|
|
181
|
+
isConfirm: option?.config?.isConfirm ?? !autoConfirm,
|
|
182
|
+
confirmText: option?.config?.confirmText || confirmText,
|
|
183
|
+
skipHostReloadAfterSave: true,
|
|
184
|
+
//提交前置处理(与「提交流程」按钮共用同一个表单脚本)
|
|
185
|
+
beforeConfirm: () =>
|
|
186
|
+
formRef.runBeforeSubmitWf
|
|
187
|
+
? formRef.runBeforeSubmitWf("saveSubmit")
|
|
188
|
+
: true,
|
|
189
|
+
success: (res) => {
|
|
190
|
+
// ★ 保存成功后,只升级新增页签的「元数据」为编辑态(设 dataId/isAdd/formCode/标题),
|
|
191
|
+
// 不重渲染详情内容。这样即便随后提交失败/取消候选人弹框,页签也已是编辑态,
|
|
192
|
+
// 再次提交只会更新、不会重复入库;而详情内容的刷新交给最终的提交后行为(避免刷两次)。
|
|
193
|
+
let editFormCode = formConfig.editFormCode;
|
|
194
|
+
let listPageRef = formRef.getListPageRef && formRef.getListPageRef();
|
|
195
|
+
if (listPageRef && listPageRef.promoteActiveAddTab) {
|
|
196
|
+
listPageRef.promoteActiveAddTab(res.objx, editFormCode, formRef);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
openStartWfDialog(formRef, {
|
|
200
|
+
objId: res.objx,
|
|
201
|
+
wfCode: reportTemplate.objTypeCode,
|
|
202
|
+
formCode: reportTemplate.formCode,
|
|
203
|
+
serviceId: reportTemplate.serviceName,
|
|
204
|
+
// 上面确认过就不再弹第二次;上面因 param19 跳过了,就得让弹框自己去确认
|
|
205
|
+
skipStartConfirm: !autoConfirm,
|
|
206
|
+
// 提交后行为复用 xform 侧统一方法:
|
|
207
|
+
// 成功 → onWfStartSuccess(关页签/刷新本页);失败/取消 → onWfStartAbort(刷新到编辑态)
|
|
208
|
+
onStartSuccess: () =>
|
|
209
|
+
formRef.onWfStartSuccess && formRef.onWfStartSuccess(),
|
|
210
|
+
onStartFail: () =>
|
|
211
|
+
formRef.onWfStartAbort && formRef.onWfStartAbort(),
|
|
212
|
+
onCancel: () => formRef.onWfStartAbort && formRef.onWfStartAbort(),
|
|
213
|
+
});
|
|
214
|
+
option?.config?.success && option.config.success(res);
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
async saveDefaultHandle(option) {
|
|
220
|
+
let formRef = this.getFormRef ? this.getFormRef() : this;
|
|
221
|
+
let formConfig = formRef.formConfig;
|
|
222
|
+
let entity = formConfig.entity;
|
|
223
|
+
if (!entity) return;
|
|
224
|
+
let scriptCode = formConfig.saveScriptCode || "saveUpdate";
|
|
225
|
+
let config = { ...(option?.config || {}) };
|
|
226
|
+
let skipHostReloadAfterSave
|
|
227
|
+
= config.skipHostReloadAfterSave === true;
|
|
228
|
+
delete config.skipHostReloadAfterSave;
|
|
229
|
+
if (typeof formRef.formSaveReconcileTask === "function") {
|
|
230
|
+
return formRef.formSaveReconcileTask();
|
|
231
|
+
}
|
|
232
|
+
if (formRef.formSavePending) {
|
|
233
|
+
this.$message.warning(this.$t1("数据正在保存,请勿重复操作"));
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
formRef.formSavePending = true;
|
|
238
|
+
let keepSaveLocked = false;
|
|
239
|
+
try {
|
|
240
|
+
let valid = await new Promise((resolve, reject) => {
|
|
241
|
+
try {
|
|
242
|
+
formRef.validate(resolve);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
reject(error);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
if (!valid) return false;
|
|
248
|
+
|
|
249
|
+
// 提交前置处理:校验通过后、确认框之前拦截。只有「保存并提交流程」入口会传,
|
|
250
|
+
// 普通保存不受影响。返回 false 即中止(finally 里会释放 formSavePending)。
|
|
251
|
+
let beforeConfirm = config.beforeConfirm;
|
|
252
|
+
delete config.beforeConfirm; //不能透传给 formHttp
|
|
253
|
+
if (typeof beforeConfirm === "function") {
|
|
254
|
+
let pass = await beforeConfirm();
|
|
255
|
+
if (pass === false) return false;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let submitFormData
|
|
259
|
+
= typeof formRef.buildSubmitPayload === "function"
|
|
260
|
+
? await formRef.buildSubmitPayload()
|
|
261
|
+
: formRef.getRealFormData();
|
|
262
|
+
let reqData = this.getReqFormData(submitFormData);
|
|
263
|
+
let shouldConfirm = config.isConfirm ?? true;
|
|
264
|
+
let confirmText
|
|
265
|
+
= config.confirmText || this.$t1("您确定要保存吗?");
|
|
266
|
+
if (shouldConfirm) {
|
|
267
|
+
try {
|
|
268
|
+
await this.$baseConfirm(confirmText);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let userSuccess = config.success;
|
|
275
|
+
let userFail = config.fail;
|
|
276
|
+
let userError = config.error;
|
|
277
|
+
let userCallback = config.callback;
|
|
278
|
+
|
|
279
|
+
return await new Promise((resolve, reject) => {
|
|
280
|
+
let settled = false;
|
|
281
|
+
let resolveOnce = (value) => {
|
|
282
|
+
if (settled) return;
|
|
283
|
+
settled = true;
|
|
284
|
+
resolve(value);
|
|
285
|
+
};
|
|
286
|
+
let rejectOnce = (error) => {
|
|
287
|
+
if (settled) return;
|
|
288
|
+
settled = true;
|
|
289
|
+
reject(error);
|
|
290
|
+
};
|
|
291
|
+
let requestTask = this.formHttp({
|
|
292
|
+
// url: "/" + reportTemplate.serviceName + "/form_ins/saveUpdate",
|
|
293
|
+
scriptCode: scriptCode,
|
|
294
|
+
data: reqData,
|
|
295
|
+
successMsg: true,
|
|
296
|
+
...config,
|
|
297
|
+
isConfirm: false,
|
|
298
|
+
confirmText,
|
|
299
|
+
callback: undefined,
|
|
300
|
+
success: async (res, response) => {
|
|
301
|
+
let editFormCode = formConfig.editFormCode;
|
|
302
|
+
let updateParam = {
|
|
303
|
+
dataId: res.objx,
|
|
304
|
+
};
|
|
305
|
+
if (editFormCode) {
|
|
306
|
+
updateParam.formCode = editFormCode;
|
|
307
|
+
}
|
|
308
|
+
// 保存成功后一律让宿主整页重挂载(toggle v-if):新增/编辑都需要出流程信息、
|
|
309
|
+
// 切编辑模板、按新状态重算显隐与只读——这些就地取数做不到。
|
|
310
|
+
// 唯一例外是「保存并提交」路径(skipHostReloadAfterSave=true),
|
|
311
|
+
// 它的刷新交给流程结束后的 onWfStartSuccess / onWfStartAbort,避免刷两次。
|
|
312
|
+
//
|
|
313
|
+
// 刷新前只同步「元数据」,绝不做整单 setFormData:表单已挂载时那是逐字段同步
|
|
314
|
+
// 广播赋值,每个字段的 onChange 会当场执行,先赋值组件的脚本读到的是后赋值
|
|
315
|
+
// 组件的旧值,公式与 keyName 联动乱序,子表还会被整体清空(getOne 不返回子表)。
|
|
316
|
+
// 首屏之所以正确,靠的是「先填满 formDataModel 再挂载组件」,就地整单赋值破坏
|
|
317
|
+
// 了这个前提,故整单刷新只能走重挂载。
|
|
318
|
+
let syncSavedMeta = () => {
|
|
319
|
+
let nextDataId = updateParam.dataId;
|
|
320
|
+
if (
|
|
321
|
+
nextDataId !== undefined
|
|
322
|
+
&& nextDataId !== null
|
|
323
|
+
&& nextDataId !== ""
|
|
324
|
+
) {
|
|
325
|
+
// 提交报文的主表以 currentFormData 打底(见 form-render getSubData2),
|
|
326
|
+
// 主键 id 不是表单控件,只能在此回写;否则 skip 路径下再次保存会重复入库。
|
|
327
|
+
if (
|
|
328
|
+
formRef.currentFormData
|
|
329
|
+
&& formRef.currentFormData.id == null
|
|
330
|
+
) {
|
|
331
|
+
formRef.currentFormData.id = nextDataId;
|
|
332
|
+
}
|
|
333
|
+
formRef.$emit("update:dataId", nextDataId);
|
|
334
|
+
}
|
|
335
|
+
if (updateParam.formCode) {
|
|
336
|
+
formRef.$emit("update:formCode", updateParam.formCode);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
let runHostReload = async () => {
|
|
340
|
+
if (skipHostReloadAfterSave) return;
|
|
341
|
+
await formRef.reloadForm({ updateParam });
|
|
342
|
+
};
|
|
343
|
+
let callbacksStarted = false;
|
|
344
|
+
let runPostReloadCallbacks = async () => {
|
|
345
|
+
if (callbacksStarted) return res;
|
|
346
|
+
callbacksStarted = true;
|
|
347
|
+
let callbacks = [
|
|
348
|
+
() => this.onAfterSaveHandle(reqData, res, response),
|
|
349
|
+
typeof userSuccess === "function"
|
|
350
|
+
? () => userSuccess(res)
|
|
351
|
+
: null,
|
|
352
|
+
typeof option?.callback === "function"
|
|
353
|
+
? () => option.callback(res)
|
|
354
|
+
: null,
|
|
355
|
+
typeof userCallback === "function"
|
|
356
|
+
? () => userCallback(res, response)
|
|
357
|
+
: null,
|
|
358
|
+
].filter(Boolean);
|
|
359
|
+
let hasCallbackError = false;
|
|
360
|
+
let firstCallbackError;
|
|
361
|
+
for (let index = 0; index < callbacks.length; index++) {
|
|
362
|
+
try {
|
|
363
|
+
await callbacks[index]();
|
|
364
|
+
} catch (error) {
|
|
365
|
+
if (!hasCallbackError) {
|
|
366
|
+
hasCallbackError = true;
|
|
367
|
+
firstCallbackError = error;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (hasCallbackError) {
|
|
372
|
+
let detail
|
|
373
|
+
= firstCallbackError?.message || firstCallbackError;
|
|
374
|
+
let message = this.$t1("已保存,后续处理失败");
|
|
375
|
+
this.$message.error(
|
|
376
|
+
detail ? `${message}:${detail}` : message
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
return res;
|
|
380
|
+
};
|
|
381
|
+
let clearReconcileState = () => {
|
|
382
|
+
formRef.formSaveReconcileTask = null;
|
|
383
|
+
formRef.formSavePending = false;
|
|
384
|
+
};
|
|
385
|
+
let showHostReloadFailure = (retry, error) => {
|
|
386
|
+
let detail = error?.message;
|
|
387
|
+
let message = this.$t1(
|
|
388
|
+
retry
|
|
389
|
+
? "重新加载仍未成功,请手动刷新后再操作"
|
|
390
|
+
: "保存成功,但重新加载失败,请手动刷新后再操作"
|
|
391
|
+
);
|
|
392
|
+
this.$message.error(
|
|
393
|
+
detail ? `${message}:${detail}` : message
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
let installHostReloadRetry = (error) => {
|
|
397
|
+
keepSaveLocked = true;
|
|
398
|
+
let hostRetryPromise = null;
|
|
399
|
+
formRef.formSaveReconcileTask = () => {
|
|
400
|
+
if (hostRetryPromise) return hostRetryPromise;
|
|
401
|
+
hostRetryPromise = (async () => {
|
|
402
|
+
try {
|
|
403
|
+
await runHostReload();
|
|
404
|
+
clearReconcileState();
|
|
405
|
+
return res;
|
|
406
|
+
} catch (retryError) {
|
|
407
|
+
showHostReloadFailure(true, retryError);
|
|
408
|
+
return false;
|
|
409
|
+
} finally {
|
|
410
|
+
hostRetryPromise = null;
|
|
411
|
+
}
|
|
412
|
+
})();
|
|
413
|
+
return hostRetryPromise;
|
|
414
|
+
};
|
|
415
|
+
showHostReloadFailure(false, error);
|
|
416
|
+
};
|
|
417
|
+
syncSavedMeta();
|
|
418
|
+
await runPostReloadCallbacks();
|
|
419
|
+
try {
|
|
420
|
+
await runHostReload();
|
|
421
|
+
} catch (hostReloadError) {
|
|
422
|
+
installHostReloadRetry(hostReloadError);
|
|
423
|
+
rejectOnce(hostReloadError);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
resolveOnce(res);
|
|
427
|
+
},
|
|
428
|
+
fail: (res, response) => {
|
|
429
|
+
this.onAfterSaveHandle(reqData, res, response);
|
|
430
|
+
if (typeof option?.callback === "function") {
|
|
431
|
+
option.callback(res);
|
|
432
|
+
}
|
|
433
|
+
if (typeof userCallback === "function") {
|
|
434
|
+
userCallback(res, response);
|
|
435
|
+
}
|
|
436
|
+
Promise.resolve(
|
|
437
|
+
typeof userFail === "function" ? userFail(res) : null
|
|
438
|
+
).then(() => resolveOnce(res), rejectOnce);
|
|
439
|
+
},
|
|
440
|
+
error: (error) => {
|
|
441
|
+
Promise.resolve(
|
|
442
|
+
typeof userError === "function" ? userError(error) : null
|
|
443
|
+
).then(() => rejectOnce(error), rejectOnce);
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
if (requestTask && typeof requestTask.catch === "function") {
|
|
447
|
+
requestTask.catch(rejectOnce);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
} catch (error) {
|
|
451
|
+
if (!keepSaveLocked) {
|
|
452
|
+
let message
|
|
453
|
+
= error?.message || error || this.$t1("保存失败");
|
|
454
|
+
this.$message.error(message);
|
|
455
|
+
}
|
|
456
|
+
return false;
|
|
457
|
+
} finally {
|
|
458
|
+
if (!keepSaveLocked) {
|
|
459
|
+
formRef.formSavePending = false;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
setInterval(callback, millis) {
|
|
464
|
+
let timer = setInterval(callback, millis);
|
|
465
|
+
this.timerMap[timer] = timer;
|
|
466
|
+
},
|
|
467
|
+
clearInterval(timer) {
|
|
468
|
+
clearInterval(timer);
|
|
469
|
+
delete this.timerMap[timer];
|
|
470
|
+
},
|
|
471
|
+
getGlobalConfig() {
|
|
472
|
+
let that = this.getFormRef ? this.getFormRef() : this;
|
|
473
|
+
return that.globalConfig;
|
|
474
|
+
},
|
|
475
|
+
getFormParam() {
|
|
476
|
+
let that = this.getFormRef ? this.getFormRef() : this;
|
|
477
|
+
return that.param;
|
|
478
|
+
},
|
|
479
|
+
setGlobalParam(key, value) {
|
|
480
|
+
let that = this.getFormRef ? this.getFormRef() : this;
|
|
481
|
+
that.globalParam[key] = value;
|
|
482
|
+
},
|
|
483
|
+
getGlobalParam(key) {
|
|
484
|
+
let that = this.getFormRef ? this.getFormRef() : this;
|
|
485
|
+
return that.globalParam[key];
|
|
486
|
+
},
|
|
487
|
+
|
|
488
|
+
//1个数组的行号处理 begin
|
|
489
|
+
/**
|
|
490
|
+
* 删除列表中的行并刷新序列
|
|
491
|
+
* @param {Array} list - 数据数组
|
|
492
|
+
* @param {number} deleteIndex - 要删除的索引
|
|
493
|
+
* @param {string} [seqField='seq'] - 序列字段名
|
|
494
|
+
* @returns {Array} 更新后的数组
|
|
495
|
+
*/
|
|
496
|
+
deleteNumRows(list, deleteIndex, seqField = "seq") {
|
|
497
|
+
if (deleteIndex < 0 || deleteIndex >= list.length) {
|
|
498
|
+
return [...list]; // 返回副本
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// 创建副本避免修改原数组
|
|
502
|
+
const newList = [...list];
|
|
503
|
+
|
|
504
|
+
// 删除指定行
|
|
505
|
+
newList.splice(deleteIndex, 1);
|
|
506
|
+
|
|
507
|
+
// 重新计算序列
|
|
508
|
+
newList.forEach((item, index) => {
|
|
509
|
+
item[seqField] = index + 1;
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
return newList;
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* 在列表中插入新行并刷新序列
|
|
517
|
+
* @param {Array} list - 数据数组
|
|
518
|
+
* @param {Array|Object} newItems - 要插入的新项(单个或多个)
|
|
519
|
+
* @param {number} [insertIndex] - 插入位置(默认末尾)
|
|
520
|
+
* @param {string} [seqField='seq'] - 序列字段名
|
|
521
|
+
* @returns {Array} 更新后的数组
|
|
522
|
+
*/
|
|
523
|
+
insertNumRows(list, newItems, insertIndex = list.length, seqField = "seq") {
|
|
524
|
+
// 参数标准化
|
|
525
|
+
const itemsArray = Array.isArray(newItems) ? newItems : [newItems];
|
|
526
|
+
|
|
527
|
+
// 边界检查
|
|
528
|
+
insertIndex = Math.max(0, Math.min(insertIndex, list.length));
|
|
529
|
+
|
|
530
|
+
// 创建副本
|
|
531
|
+
const newList = [...list];
|
|
532
|
+
|
|
533
|
+
// 准备要插入的项(移除可能的旧序列)
|
|
534
|
+
const itemsToInsert = itemsArray.map((item) => {
|
|
535
|
+
const newItem = { ...item };
|
|
536
|
+
if (newItem[seqField] !== undefined) {
|
|
537
|
+
delete newItem[seqField];
|
|
538
|
+
}
|
|
539
|
+
return newItem;
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// 插入新项
|
|
543
|
+
newList.splice(insertIndex, 0, ...itemsToInsert);
|
|
544
|
+
|
|
545
|
+
// 重新计算所有序列
|
|
546
|
+
newList.forEach((item, index) => {
|
|
547
|
+
item[seqField] = index + 1;
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
return newList;
|
|
551
|
+
},
|
|
552
|
+
//1个数组的行号处理 end
|
|
553
|
+
|
|
554
|
+
//2个数组的行号处理 begin
|
|
555
|
+
/**
|
|
556
|
+
* 通用新增方法(支持单条/多条)
|
|
557
|
+
* @param {Array} list1 - 主数据数组
|
|
558
|
+
* @param {Array} list2 - 关联数据数组
|
|
559
|
+
* @param {Array|Object} newItems - 要插入的新项(单个对象或多个对象的数组)
|
|
560
|
+
* @param {Array|Array[]} subItems - 关联数据(单个数组或多个数组的数组)
|
|
561
|
+
* @param {number} [insertIndex] - 插入位置(默认末尾)
|
|
562
|
+
* @param {string} [seqField='seq'] - list1中的序列字段名
|
|
563
|
+
* @param {string} [superSeqField='super_seq'] - list2中的关联字段名
|
|
564
|
+
* @returns {Object} 包含更新后的list1和list2
|
|
565
|
+
*/
|
|
566
|
+
insertRelRows(
|
|
567
|
+
list1,
|
|
568
|
+
list2,
|
|
569
|
+
newItems,
|
|
570
|
+
subItems = [],
|
|
571
|
+
insertIndex,
|
|
572
|
+
seqField = "seq",
|
|
573
|
+
superSeqField = "super_seq"
|
|
574
|
+
) {
|
|
575
|
+
// 参数标准化
|
|
576
|
+
const itemsArray = Array.isArray(newItems) ? newItems : [newItems];
|
|
577
|
+
const subItemsArray =
|
|
578
|
+
Array.isArray(subItems) &&
|
|
579
|
+
subItems.length > 0 &&
|
|
580
|
+
Array.isArray(subItems[0])
|
|
581
|
+
? subItems
|
|
582
|
+
: [subItems];
|
|
583
|
+
|
|
584
|
+
if (insertIndex === null || insertIndex === undefined) {
|
|
585
|
+
insertIndex = list1.length;
|
|
586
|
+
}
|
|
587
|
+
// 边界检查
|
|
588
|
+
insertIndex = Math.max(0, Math.min(insertIndex, list1.length));
|
|
589
|
+
|
|
590
|
+
// 创建副本
|
|
591
|
+
const newList1 = [...list1];
|
|
592
|
+
const newList2 = [...list2];
|
|
593
|
+
|
|
594
|
+
// 1. 插入新项(不设置序列)
|
|
595
|
+
const itemsToInsert = itemsArray.map((item) => {
|
|
596
|
+
const newItem = { ...item };
|
|
597
|
+
if (newItem[seqField] !== undefined) {
|
|
598
|
+
delete newItem[seqField]; // 确保不保留旧序列
|
|
599
|
+
}
|
|
600
|
+
return newItem;
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
newList1.splice(insertIndex, 0, ...itemsToInsert);
|
|
604
|
+
|
|
605
|
+
// 2. 重新计算序列并创建映射表
|
|
606
|
+
const seqMapping = {};
|
|
607
|
+
newList1.forEach((item, index) => {
|
|
608
|
+
const newSeq = index + 1;
|
|
609
|
+
const oldSeq = item[seqField];
|
|
610
|
+
|
|
611
|
+
// 记录旧序列到新序列的映射
|
|
612
|
+
if (oldSeq !== undefined) {
|
|
613
|
+
seqMapping[oldSeq] = newSeq;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// 更新当前项序列
|
|
617
|
+
item[seqField] = newSeq;
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// 3. 获取新插入项的序列
|
|
621
|
+
const newSeqs = [];
|
|
622
|
+
for (let i = 0; i < itemsArray.length; i++) {
|
|
623
|
+
newSeqs.push(insertIndex + i + 1);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// 4. 更新现有关联字段
|
|
627
|
+
newList2.forEach((item) => {
|
|
628
|
+
const currentSuperSeq = item[superSeqField];
|
|
629
|
+
if (seqMapping[currentSuperSeq] !== undefined) {
|
|
630
|
+
item[superSeqField] = seqMapping[currentSuperSeq];
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// 5. 添加新关联数据
|
|
635
|
+
subItemsArray.forEach((subItems, idx) => {
|
|
636
|
+
const superSeq = newSeqs[idx];
|
|
637
|
+
if (Array.isArray(subItems) && subItems.length > 0) {
|
|
638
|
+
subItems.forEach((item) => {
|
|
639
|
+
const newItem = { ...item };
|
|
640
|
+
newItem[superSeqField] = superSeq;
|
|
641
|
+
newList2.push(newItem);
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
return { list1: newList1, list2: newList2 };
|
|
647
|
+
},
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* 删除关联行(从属数据打平传参)
|
|
651
|
+
* @param {Array} masters - 主数据数组
|
|
652
|
+
* @param {Array} slaves - 从属数据数组(打平的一维数组)
|
|
653
|
+
* @param {number} rowIdx - 要删除的主数据行索引
|
|
654
|
+
* @param {string} [seqField='seq'] - 行号字段名
|
|
655
|
+
* @param {string} [refField='super_seq'] - 关联字段名
|
|
656
|
+
* @returns {Object} 包含更新后的masters和slaves
|
|
657
|
+
*/
|
|
658
|
+
deleteRelRows(
|
|
659
|
+
masters,
|
|
660
|
+
slaves,
|
|
661
|
+
rowIdx,
|
|
662
|
+
seqField = "seq",
|
|
663
|
+
refField = "super_seq"
|
|
664
|
+
) {
|
|
665
|
+
// 边界检查(返回结构与正常分支保持一致:list1/list2)
|
|
666
|
+
if (rowIdx < 0 || rowIdx >= masters.length) {
|
|
667
|
+
return { list1: [...masters], list2: [...slaves] };
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// 创建副本
|
|
671
|
+
const newMasters = [...masters];
|
|
672
|
+
const newSlaves = [...slaves];
|
|
673
|
+
|
|
674
|
+
// 1. 删除主行并获取序列值
|
|
675
|
+
const [deleted] = newMasters.splice(rowIdx, 1);
|
|
676
|
+
const deletedSeq = deleted[seqField];
|
|
677
|
+
|
|
678
|
+
// 2. 删除关联的从属行
|
|
679
|
+
for (let i = newSlaves.length - 1; i >= 0; i--) {
|
|
680
|
+
if (newSlaves[i][refField] === deletedSeq) {
|
|
681
|
+
newSlaves.splice(i, 1);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// 3. 重新计算主数据序列
|
|
686
|
+
const seqMap = {};
|
|
687
|
+
newMasters.forEach((master, idx) => {
|
|
688
|
+
const oldSeq = master[seqField];
|
|
689
|
+
const newSeq = idx + 1;
|
|
690
|
+
seqMap[oldSeq] = newSeq;
|
|
691
|
+
master[seqField] = newSeq;
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// 4. 更新从属数据引用
|
|
695
|
+
newSlaves.forEach((slave) => {
|
|
696
|
+
const ref = slave[refField];
|
|
697
|
+
if (seqMap[ref] !== undefined) {
|
|
698
|
+
slave[refField] = seqMap[ref];
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
return { list1: newMasters, list2: newSlaves };
|
|
703
|
+
},
|
|
704
|
+
//2个数组的行号处理 end
|
|
705
|
+
},
|
|
706
|
+
};
|
|
707
|
+
export default modules;
|