cloud-web-corejs 1.1.0-dev.19 → 1.1.0-dev.20
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/baseInputExport/mixins.js +0 -6
- package/src/components/xform/docs/2026-09 /345/212/250/346/200/201/345/255/227/346/256/265/350/247/204/345/210/231/344/270/216/346/265/201/347/250/213/346/216/247/345/210/266/346/211/247/350/241/214/351/241/272/345/272/217/350/220/275/345/234/260/346/226/271/346/241/210.md" +409 -0
- package/src/components/xform/form-designer/designer.js +2101 -2084
- package/src/components/xform/form-designer/form-widget/dialog/importDialogMixin.js +34 -0
- package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +13 -1
- package/src/components/xform/form-designer/setting-panel/form-dynamicField-setting.vue +660 -602
- package/src/components/xform/form-designer/setting-panel/property-editor/field-import-button/import2-button-editor.vue +8 -0
- package/src/components/xform/form-render/container-item/data-table-mixin.js +6306 -6285
- package/src/components/xform/form-render/container-item/dynamicFieldMixin.js +54 -0
- package/src/components/xform/form-render/container-item/tab-item.vue +138 -129
- package/src/components/xform/form-render/fieldControlEngine.js +200 -0
- package/src/components/xform/form-render/fieldControlMixin.js +524 -0
- package/src/components/xform/form-render/formDynamicFieldMixin.js +2 -0
- package/src/components/xform/form-render/index.vue +161 -153
- package/src/components/xform/form-render/indexMixin.js +5404 -5331
- package/src/components/xform/mixins/defaultHandle.js +713 -707
- package/src/components/xform/utils/util.js +1711 -1708
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
2
|
+
import {
|
|
3
|
+
collectFieldControlTargets, readFieldState, writeStateOptions, computeFieldStates,
|
|
4
|
+
resolveScriptStates, resolveRunMode, applyFieldState, isEntryVisible, sameValue,
|
|
5
|
+
} from "@base/components/xform/form-render/fieldControlEngine";
|
|
6
|
+
|
|
7
|
+
const TIMEOUT = 30000;
|
|
8
|
+
function createRuntime() {
|
|
9
|
+
return {
|
|
10
|
+
epoch: 0, revision: 0, definitions: new WeakMap(), entries: [], byWidget: new WeakMap(),
|
|
11
|
+
mountStarted: false, mountedReady: false, readyCallback: null,
|
|
12
|
+
flowWidgets: [], schemas: new Set(), cancelRequests: new Set(), schemaErrors: new Map(),
|
|
13
|
+
watcher: null, timer: null, task: null, mountTask: null, mounting: false, destroyed: false,
|
|
14
|
+
dynamicStates: null, dirty: false, appliedRevision: -1, lastDependency: null,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
data() {
|
|
20
|
+
return {
|
|
21
|
+
wfParam: null,
|
|
22
|
+
fieldControlStatus: "idle",
|
|
23
|
+
fieldControlError: "",
|
|
24
|
+
fieldControlInitialized: false,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
created() {
|
|
28
|
+
this._fieldControl = createRuntime();
|
|
29
|
+
},
|
|
30
|
+
computed: {
|
|
31
|
+
fieldControlBlocked() {
|
|
32
|
+
// 仅准备期隐藏,避免用户在流程限制生效前操作。error 要保留已填内容,提交由 ensure 拦截。
|
|
33
|
+
return this.isFieldControlV2() && this.fieldControlStatus === "preparing";
|
|
34
|
+
},
|
|
35
|
+
fieldControlContextKey() {
|
|
36
|
+
const info = this.wfParam?.wfInfo || {};
|
|
37
|
+
return JSON.stringify([
|
|
38
|
+
!!this.wfParam?.hasWf, info.taskStep, info.taskDefinitionKey, info.nodeCode,
|
|
39
|
+
info.modelOrders, info.toModify, this.bdService, this.$store?.getters?.companyCode,
|
|
40
|
+
]);
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
watch: {
|
|
44
|
+
fieldControlContextKey() {
|
|
45
|
+
const runtime = this._fieldControl;
|
|
46
|
+
if (!runtime || !this.isFieldControlV2()) return;
|
|
47
|
+
if (!this.fieldControlInitialized) {
|
|
48
|
+
if (runtime.mounting || runtime.task) runtime.revision++;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const previousMode = runtime.mode;
|
|
52
|
+
runtime.mode = resolveRunMode(this.formConfig, !!this.wfParam?.hasWf);
|
|
53
|
+
this.stopFieldControlWatcher();
|
|
54
|
+
if (previousMode !== runtime.mode || runtime.mode === "continuous") {
|
|
55
|
+
this.invalidateFieldControl();
|
|
56
|
+
} else {
|
|
57
|
+
runtime.revision++;
|
|
58
|
+
this.applyFieldControlStates(runtime.dynamicStates);
|
|
59
|
+
}
|
|
60
|
+
this.setupFieldControlWatcher();
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
beforeDestroy() {
|
|
64
|
+
this.disposeFieldControl();
|
|
65
|
+
},
|
|
66
|
+
methods: {
|
|
67
|
+
isFieldControlV2() {
|
|
68
|
+
return !!this.formConfig?.dynamicFieldEnabled && Number(this.formConfig.dynamicFieldRuleVersion) === 2;
|
|
69
|
+
},
|
|
70
|
+
getFieldControlRuntime() {
|
|
71
|
+
if (!this._fieldControl) this._fieldControl = createRuntime();
|
|
72
|
+
return this._fieldControl;
|
|
73
|
+
},
|
|
74
|
+
prepareFieldControlTargets() {
|
|
75
|
+
const runtime = this.getFieldControlRuntime();
|
|
76
|
+
const entries = collectFieldControlTargets(this.widgetList);
|
|
77
|
+
const widgets = this.getFieldControlWidgets ? this.getFieldControlWidgets() : entries.map(e => e.widget);
|
|
78
|
+
runtime.entries = entries;
|
|
79
|
+
runtime.byWidget = new WeakMap();
|
|
80
|
+
entries.forEach(entry => runtime.byWidget.set(entry.widget, entry));
|
|
81
|
+
runtime.flowWidgets = Array.from(new Set([...widgets, ...entries.map(e => e.widget)]));
|
|
82
|
+
runtime.flowWidgets.forEach(widget => {
|
|
83
|
+
if (!widget.options || runtime.definitions.has(widget)) return;
|
|
84
|
+
const state = {};
|
|
85
|
+
["hidden", "required", "readonly", "disabled"].forEach(key => { state[key] = widget.options[key]; });
|
|
86
|
+
runtime.definitions.set(widget, state);
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
// 动态列/结构晚于首次采集时,先登记未改写的定义,再走动态→流程,避免 V1 旁路污染基线。
|
|
90
|
+
syncFieldControlAfterStructure() {
|
|
91
|
+
if (!this.isFieldControlV2()) return false;
|
|
92
|
+
this.prepareFieldControlTargets();
|
|
93
|
+
const runtime = this.getFieldControlRuntime();
|
|
94
|
+
if (this.fieldControlInitialized) {
|
|
95
|
+
this.applyFieldControlStates(runtime.dynamicStates);
|
|
96
|
+
} else if (runtime.mounting || runtime.task) {
|
|
97
|
+
runtime.revision++;
|
|
98
|
+
runtime.dirty = true;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
},
|
|
102
|
+
createFieldControlProjection(widget) {
|
|
103
|
+
const runtime = this.getFieldControlRuntime();
|
|
104
|
+
return { ...widget, options: { ...widget.options, ...runtime.definitions.get(widget) } };
|
|
105
|
+
},
|
|
106
|
+
getFieldControlBaseline() {
|
|
107
|
+
const result = Object.create(null);
|
|
108
|
+
this.getFieldControlRuntime().entries.forEach(entry => {
|
|
109
|
+
const projected = this.createFieldControlProjection(entry.widget);
|
|
110
|
+
if (this.handleWidgetShowRule) this.handleWidgetShowRule(projected);
|
|
111
|
+
result[entry.key] = readFieldState(projected);
|
|
112
|
+
});
|
|
113
|
+
return result;
|
|
114
|
+
},
|
|
115
|
+
async showControlledForm(callback) {
|
|
116
|
+
const runtime = this.getFieldControlRuntime();
|
|
117
|
+
this.fieldControlStatus = "preparing";
|
|
118
|
+
runtime.mounting = true;
|
|
119
|
+
runtime.mountStarted = true;
|
|
120
|
+
runtime.readyCallback = callback || runtime.readyCallback;
|
|
121
|
+
const task = (async () => {
|
|
122
|
+
await this.waitFieldControlTask(this.handleBeforeMounted());
|
|
123
|
+
if (runtime.destroyed || runtime !== this.getFieldControlRuntime()) return;
|
|
124
|
+
this.showFormContent = true;
|
|
125
|
+
await this.$nextTick();
|
|
126
|
+
await this.waitFieldControlTask(this.handleOnMounted());
|
|
127
|
+
if (this.formConfig.isLoadEntity) {
|
|
128
|
+
await this.waitFieldControlTask(this.handleCustomEvent(this.formConfig.formScriptSuccess));
|
|
129
|
+
}
|
|
130
|
+
if (runtime.destroyed || runtime !== this.getFieldControlRuntime()) return;
|
|
131
|
+
runtime.mountedReady = true;
|
|
132
|
+
await this.$nextTick();
|
|
133
|
+
await this.waitFieldControlSchemas();
|
|
134
|
+
this.prepareFieldControlTargets();
|
|
135
|
+
runtime.mounting = false;
|
|
136
|
+
await this.initializeFieldControl();
|
|
137
|
+
this.finishFieldControlMount();
|
|
138
|
+
})();
|
|
139
|
+
runtime.mountTask = task;
|
|
140
|
+
try { await task; } catch (error) {
|
|
141
|
+
this.failFieldControl(error, runtime);
|
|
142
|
+
} finally {
|
|
143
|
+
runtime.mounting = false;
|
|
144
|
+
if (runtime.mountTask === task) runtime.mountTask = null;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
finishFieldControlMount() {
|
|
148
|
+
const runtime = this.getFieldControlRuntime();
|
|
149
|
+
this.handleShowHideRule();
|
|
150
|
+
const callback = runtime.readyCallback;
|
|
151
|
+
runtime.readyCallback = null;
|
|
152
|
+
if (callback) callback();
|
|
153
|
+
},
|
|
154
|
+
waitFieldControlTask(task) {
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
const timer = setTimeout(() => reject(new Error("字段规则初始化超时,请重试")), TIMEOUT);
|
|
157
|
+
Promise.resolve(task).then(value => { clearTimeout(timer); resolve(value); },
|
|
158
|
+
error => { clearTimeout(timer); reject(error); });
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
trackFieldControlSchema(task, retry, owner) {
|
|
162
|
+
const runtime = this.getFieldControlRuntime();
|
|
163
|
+
const epoch = runtime.epoch;
|
|
164
|
+
// 注册时即阻挡操作并废弃旧规则结果,不能等结构请求完成。
|
|
165
|
+
this.fieldControlInitialized = false;
|
|
166
|
+
this.fieldControlStatus = "preparing";
|
|
167
|
+
runtime.revision++;
|
|
168
|
+
this.stopFieldControlWatcher();
|
|
169
|
+
const tracked = this.waitFieldControlTask(task).then(value => {
|
|
170
|
+
if (epoch !== runtime.epoch || runtime.destroyed) return value;
|
|
171
|
+
runtime.schemaErrors.delete(owner);
|
|
172
|
+
return value;
|
|
173
|
+
}).catch(error => {
|
|
174
|
+
if (epoch === runtime.epoch && !runtime.destroyed) {
|
|
175
|
+
runtime.schemaErrors.set(owner, { error, retry });
|
|
176
|
+
this.failFieldControl(error, runtime);
|
|
177
|
+
}
|
|
178
|
+
throw error;
|
|
179
|
+
}).finally(() => { runtime.schemas.delete(tracked); });
|
|
180
|
+
runtime.schemas.add(tracked);
|
|
181
|
+
tracked.catch(() => {});
|
|
182
|
+
if (!runtime.mounting) {
|
|
183
|
+
tracked.then(() => {
|
|
184
|
+
if (epoch === runtime.epoch && !runtime.destroyed) return this.initializeFieldControl();
|
|
185
|
+
}).catch(error => this.failFieldControl(error, runtime));
|
|
186
|
+
}
|
|
187
|
+
return tracked;
|
|
188
|
+
},
|
|
189
|
+
async waitFieldControlSchemas() {
|
|
190
|
+
const runtime = this.getFieldControlRuntime();
|
|
191
|
+
while (runtime.schemas.size) await Promise.all(Array.from(runtime.schemas));
|
|
192
|
+
if (runtime.schemaErrors.size) throw Array.from(runtime.schemaErrors.values())[0].error;
|
|
193
|
+
},
|
|
194
|
+
async initializeFieldControl() {
|
|
195
|
+
if (!this.isFieldControlV2()) return;
|
|
196
|
+
const runtime = this.getFieldControlRuntime();
|
|
197
|
+
if (runtime.destroyed) throw new Error("表单已关闭");
|
|
198
|
+
this.fieldControlStatus = "preparing";
|
|
199
|
+
this.fieldControlError = "";
|
|
200
|
+
await this.waitFieldControlSchemas();
|
|
201
|
+
if (runtime.destroyed || runtime !== this.getFieldControlRuntime()) throw new Error("表单已更新,请重试");
|
|
202
|
+
this.prepareFieldControlTargets();
|
|
203
|
+
runtime.mode = resolveRunMode(this.formConfig, !!this.wfParam?.hasWf);
|
|
204
|
+
runtime.dynamicStates = null;
|
|
205
|
+
runtime.revision++;
|
|
206
|
+
runtime.dirty = true;
|
|
207
|
+
await this.refreshFieldControl();
|
|
208
|
+
this.setupFieldControlWatcher();
|
|
209
|
+
},
|
|
210
|
+
async retryFieldControl() {
|
|
211
|
+
const runtime = this.getFieldControlRuntime();
|
|
212
|
+
this.fieldControlError = "";
|
|
213
|
+
this.fieldControlStatus = "preparing";
|
|
214
|
+
try {
|
|
215
|
+
if (!runtime.mountedReady && runtime.mountStarted) {
|
|
216
|
+
await this.showControlledForm(runtime.readyCallback);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const failed = Array.from(runtime.schemaErrors.values());
|
|
220
|
+
await Promise.all(failed.map(item => item.retry()));
|
|
221
|
+
await this.initializeFieldControl();
|
|
222
|
+
if (runtime.mountedReady) this.finishFieldControlMount();
|
|
223
|
+
} catch (error) { this.failFieldControl(error, runtime); }
|
|
224
|
+
},
|
|
225
|
+
fieldControlDependency() {
|
|
226
|
+
if (this.formConfig.dynamicFieldSourceType === "script") {
|
|
227
|
+
return (this.formConfig.dynamicFieldTriggerFields || []).map(key => this.formModel[key]);
|
|
228
|
+
}
|
|
229
|
+
const keys = new Set();
|
|
230
|
+
(this.formConfig.dynamicFieldRules || []).forEach(rule =>
|
|
231
|
+
(rule.conditions || []).forEach(condition => keys.add(condition.field)));
|
|
232
|
+
return Array.from(keys).map(key => this.formModel[key]);
|
|
233
|
+
},
|
|
234
|
+
stopFieldControlWatcher() {
|
|
235
|
+
const runtime = this.getFieldControlRuntime();
|
|
236
|
+
if (runtime.watcher) runtime.watcher();
|
|
237
|
+
if (runtime.timer) clearTimeout(runtime.timer);
|
|
238
|
+
runtime.watcher = null;
|
|
239
|
+
runtime.timer = null;
|
|
240
|
+
},
|
|
241
|
+
setupFieldControlWatcher() {
|
|
242
|
+
const runtime = this.getFieldControlRuntime();
|
|
243
|
+
this.stopFieldControlWatcher();
|
|
244
|
+
if (runtime.mode !== "continuous") return;
|
|
245
|
+
runtime.lastDependency = cloneDeep(this.fieldControlDependency());
|
|
246
|
+
runtime.watcher = this.$watch(() => this.fieldControlDependency(), () => {
|
|
247
|
+
if (!sameValue(runtime.lastDependency, this.fieldControlDependency())) this.invalidateFieldControl();
|
|
248
|
+
}, { deep: true, sync: true });
|
|
249
|
+
},
|
|
250
|
+
invalidateFieldControl() {
|
|
251
|
+
const runtime = this.getFieldControlRuntime();
|
|
252
|
+
runtime.revision++;
|
|
253
|
+
runtime.dirty = true;
|
|
254
|
+
if (runtime.timer) clearTimeout(runtime.timer);
|
|
255
|
+
runtime.timer = setTimeout(() => {
|
|
256
|
+
runtime.timer = null;
|
|
257
|
+
this.refreshFieldControl().catch(error => this.failFieldControl(error, runtime));
|
|
258
|
+
}, 120);
|
|
259
|
+
},
|
|
260
|
+
async refreshFieldControl() {
|
|
261
|
+
if (!this.isFieldControlV2()) return;
|
|
262
|
+
const runtime = this.getFieldControlRuntime();
|
|
263
|
+
if (runtime.timer) clearTimeout(runtime.timer);
|
|
264
|
+
runtime.timer = null;
|
|
265
|
+
if (runtime.task) return runtime.task;
|
|
266
|
+
const epoch = runtime.epoch;
|
|
267
|
+
const task = (async () => {
|
|
268
|
+
await this.waitFieldControlSchemas();
|
|
269
|
+
let attempts = 0;
|
|
270
|
+
do {
|
|
271
|
+
if (++attempts > 5) throw new Error("动态规则持续变化或循环赋值,请检查规则后重试");
|
|
272
|
+
if (runtime.destroyed || epoch !== runtime.epoch) throw new Error("表单已更新,请重试");
|
|
273
|
+
const revision = runtime.revision;
|
|
274
|
+
const baseline = this.getFieldControlBaseline();
|
|
275
|
+
const dependency = cloneDeep(this.fieldControlDependency());
|
|
276
|
+
let states;
|
|
277
|
+
let effects = {};
|
|
278
|
+
if (this.formConfig.dynamicFieldSourceType === "script") {
|
|
279
|
+
const result = await this.requestFieldControlStates();
|
|
280
|
+
if (runtime.destroyed || epoch !== runtime.epoch) throw new Error("表单已更新,请重试");
|
|
281
|
+
if (revision !== runtime.revision) continue;
|
|
282
|
+
if (runtime.mode === "continuous" && !sameValue(dependency, this.fieldControlDependency())) {
|
|
283
|
+
runtime.revision++;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
({ states, effects } = resolveScriptStates(result, baseline, !!this.formConfig.dynamicFieldAllowValue));
|
|
287
|
+
} else {
|
|
288
|
+
states = computeFieldStates(this.formConfig.dynamicFieldRules || [], baseline, this.formModel || {});
|
|
289
|
+
}
|
|
290
|
+
if (revision !== runtime.revision) continue;
|
|
291
|
+
this.applyFieldControlEffects(effects);
|
|
292
|
+
await this.$nextTick();
|
|
293
|
+
if (runtime.destroyed || epoch !== runtime.epoch) throw new Error("表单已更新,请重试");
|
|
294
|
+
// 首次只执行一次;持续模式才追踪赋值引发的依赖变化。
|
|
295
|
+
if (runtime.mode === "continuous" && !sameValue(dependency, this.fieldControlDependency())) {
|
|
296
|
+
runtime.revision++;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (revision !== runtime.revision) continue;
|
|
300
|
+
runtime.dynamicStates = states;
|
|
301
|
+
this.applyFieldControlStates(states);
|
|
302
|
+
await this.$nextTick();
|
|
303
|
+
this.syncFieldControlValidation();
|
|
304
|
+
runtime.appliedRevision = revision;
|
|
305
|
+
runtime.lastDependency = cloneDeep(this.fieldControlDependency());
|
|
306
|
+
runtime.dirty = revision !== runtime.revision;
|
|
307
|
+
} while (runtime.dirty || runtime.appliedRevision !== runtime.revision);
|
|
308
|
+
this.fieldControlInitialized = true;
|
|
309
|
+
this.fieldControlStatus = "ready";
|
|
310
|
+
this.fieldControlError = "";
|
|
311
|
+
})();
|
|
312
|
+
runtime.task = task;
|
|
313
|
+
try { return await task; } catch (error) {
|
|
314
|
+
if (epoch === runtime.epoch && !runtime.destroyed) this.failFieldControl(error, runtime);
|
|
315
|
+
throw error;
|
|
316
|
+
} finally {
|
|
317
|
+
if (runtime.task === task) runtime.task = null;
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
requestFieldControlStates() {
|
|
321
|
+
const runtime = this.getFieldControlRuntime();
|
|
322
|
+
const config = this.formConfig;
|
|
323
|
+
if (!config.dynamicFieldScriptCode) return Promise.reject(new Error("请配置动态规则脚本编码"));
|
|
324
|
+
const extra = this.handleCustomEvent(config.dynamicFieldScriptParam) || {};
|
|
325
|
+
const info = this.wfParam?.wfInfo || {};
|
|
326
|
+
return new Promise((resolve, reject) => {
|
|
327
|
+
let settled = false;
|
|
328
|
+
const finish = (error, value) => {
|
|
329
|
+
if (settled) return;
|
|
330
|
+
settled = true;
|
|
331
|
+
clearTimeout(timer);
|
|
332
|
+
runtime.cancelRequests.delete(cancel);
|
|
333
|
+
if (error) reject(error); else resolve(value);
|
|
334
|
+
};
|
|
335
|
+
const cancel = () => finish(new Error("表单已更新,请重试"));
|
|
336
|
+
const timer = setTimeout(() => finish(new Error("后台动态规则超时,请重试")), TIMEOUT);
|
|
337
|
+
runtime.cancelRequests.add(cancel);
|
|
338
|
+
try {
|
|
339
|
+
const request = this.formHttp({
|
|
340
|
+
scriptCode: config.dynamicFieldScriptCode,
|
|
341
|
+
isLoading: false,
|
|
342
|
+
data: {
|
|
343
|
+
...extra,
|
|
344
|
+
formData: cloneDeep(this.formModel),
|
|
345
|
+
context: {
|
|
346
|
+
hasWf: !!this.wfParam?.hasWf, taskStep: info.taskStep,
|
|
347
|
+
taskDefinitionKey: info.taskDefinitionKey || info.nodeCode,
|
|
348
|
+
modelOrders: info.modelOrders, toModify: info.toModify,
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
success: response => finish(null, response?.objx?.fieldStates),
|
|
352
|
+
fail: () => finish(new Error("后台动态规则执行失败,请重试")),
|
|
353
|
+
error: error => finish(error instanceof Error ? error : new Error("后台动态规则请求失败")),
|
|
354
|
+
});
|
|
355
|
+
if (request && request.catch) request.catch(error => finish(error));
|
|
356
|
+
} catch (error) { finish(error); }
|
|
357
|
+
});
|
|
358
|
+
},
|
|
359
|
+
applyFieldControlEffects(effects) {
|
|
360
|
+
const runtime = this.getFieldControlRuntime();
|
|
361
|
+
Object.keys(effects).forEach(key => {
|
|
362
|
+
const entry = runtime.entries.find(item => item.key === key);
|
|
363
|
+
if (!entry || entry.isContainer) throw new Error("容器不支持动态赋值或选项:" + key);
|
|
364
|
+
if (effects[key].options !== undefined
|
|
365
|
+
&& !["select", "checkbox", "radio", "cascader", "status"].includes(entry.widget.type)) {
|
|
366
|
+
throw new Error("组件不支持动态选项:" + key);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
Object.keys(effects).forEach(key => {
|
|
370
|
+
const entry = runtime.entries.find(item => item.key === key);
|
|
371
|
+
const effect = effects[key];
|
|
372
|
+
if (!entry || entry.isContainer) throw new Error("容器不支持动态赋值或选项:" + key);
|
|
373
|
+
const ref = this.getWidgetRef(entry.refKey);
|
|
374
|
+
if (effect.options !== undefined) {
|
|
375
|
+
if (!["select", "checkbox", "radio", "cascader", "status"].includes(entry.widget.type)) {
|
|
376
|
+
throw new Error("组件不支持动态选项:" + key);
|
|
377
|
+
}
|
|
378
|
+
const optionKey = entry.widget.type === "status" ? "statusParam" : "optionItems";
|
|
379
|
+
if (!sameValue(entry.widget.options[optionKey], effect.options)) {
|
|
380
|
+
if (ref && ref.setOptionItems) ref.setOptionItems(cloneDeep(effect.options));
|
|
381
|
+
else this.$set(entry.widget.options, optionKey, cloneDeep(effect.options));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (effect.value !== undefined && !sameValue(this.formModel[key], effect.value)) {
|
|
385
|
+
if (ref && ref.setValue) ref.setValue(cloneDeep(effect.value));
|
|
386
|
+
else this.$set(this.formModel, key, cloneDeep(effect.value));
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
},
|
|
390
|
+
applyFieldControlStates(states) {
|
|
391
|
+
const runtime = this.getFieldControlRuntime();
|
|
392
|
+
this.widgetEditOnWf = this.hanldeWfWidgetNew0 ? this.hanldeWfWidgetNew0() : false;
|
|
393
|
+
let editable = false;
|
|
394
|
+
runtime.flowWidgets.forEach(widget => {
|
|
395
|
+
const entry = runtime.byWidget.get(widget);
|
|
396
|
+
const projected = this.createFieldControlProjection(widget);
|
|
397
|
+
if (this.handleWidgetShowRule) this.handleWidgetShowRule(projected);
|
|
398
|
+
if (entry && states?.[entry.key]) writeStateOptions(projected.options, states[entry.key]);
|
|
399
|
+
if (this.applyWorkflowFieldControl) this.applyWorkflowFieldControl(projected);
|
|
400
|
+
if (widget.formItemFlag && this.disabledMode) projected.options.disabled = true;
|
|
401
|
+
const effective = readFieldState(projected);
|
|
402
|
+
// 非输入按钮不在动态目标范围,但仍应用流程自身的 disabled。
|
|
403
|
+
if (!widget.formItemFlag && widget.category !== "container" && projected.options.disabled !== undefined) {
|
|
404
|
+
effective.disabled = projected.options.disabled;
|
|
405
|
+
}
|
|
406
|
+
const target = entry || { widget, refKey: widget.options.name, isContainer: widget.category === "container" };
|
|
407
|
+
applyFieldState(target, effective, name => this.getWidgetRef(name));
|
|
408
|
+
if (widget.formItemFlag && !projected.options.hidden && !projected.options.disabled && !projected.options.readonly) editable = true;
|
|
409
|
+
});
|
|
410
|
+
this.wfModifyEnabled = !!this.wfParam?.wfInfo?.toModify && editable && !this.getReadMode?.();
|
|
411
|
+
this.syncFieldControlValidation();
|
|
412
|
+
},
|
|
413
|
+
syncFieldControlValidation() {
|
|
414
|
+
const runtime = this.getFieldControlRuntime();
|
|
415
|
+
runtime.entries.forEach(entry => {
|
|
416
|
+
if (entry.isContainer) return;
|
|
417
|
+
const ref = this.getWidgetRef(entry.refKey);
|
|
418
|
+
if (!ref || ref.field !== entry.widget) return;
|
|
419
|
+
if (!isEntryVisible(entry)) {
|
|
420
|
+
if (ref.clearFieldRules) ref.clearFieldRules();
|
|
421
|
+
} else if (ref.buildFieldRules) ref.buildFieldRules();
|
|
422
|
+
});
|
|
423
|
+
},
|
|
424
|
+
isFieldControlFieldVisible(widget) {
|
|
425
|
+
const entry = this.getFieldControlRuntime().byWidget.get(widget);
|
|
426
|
+
return !entry || isEntryVisible(entry);
|
|
427
|
+
},
|
|
428
|
+
failFieldControl(error, runtime = this.getFieldControlRuntime()) {
|
|
429
|
+
if (runtime.destroyed || runtime !== this.getFieldControlRuntime()) return;
|
|
430
|
+
this.fieldControlStatus = "error";
|
|
431
|
+
this.fieldControlError = error?.message || "字段规则执行失败,请重试";
|
|
432
|
+
// 无成功结果时至少应用已知流程限制。错误状态独立阻挡编辑/提交。
|
|
433
|
+
if (!runtime.dynamicStates && runtime.flowWidgets.length) this.applyFieldControlStates(null);
|
|
434
|
+
},
|
|
435
|
+
async ensureFieldControlReady() {
|
|
436
|
+
if (!this.isFieldControlV2()) return;
|
|
437
|
+
const runtime = this.getFieldControlRuntime();
|
|
438
|
+
if (runtime.mountTask) await runtime.mountTask;
|
|
439
|
+
if (runtime.destroyed) throw new Error("表单已关闭");
|
|
440
|
+
if (this.fieldControlStatus === "error") throw new Error(this.fieldControlError);
|
|
441
|
+
if (!this.fieldControlInitialized) await this.initializeFieldControl();
|
|
442
|
+
if (this.fieldControlStatus === "error") throw new Error(this.fieldControlError);
|
|
443
|
+
if (runtime.mode === "continuous" && !sameValue(runtime.lastDependency, this.fieldControlDependency())) {
|
|
444
|
+
runtime.revision++;
|
|
445
|
+
runtime.dirty = true;
|
|
446
|
+
}
|
|
447
|
+
// 提交不等防抖;旧 task 结束后若仍 dirty 必须再算一轮,不能把过期结果交给校验。
|
|
448
|
+
while (runtime.dirty || runtime.task) {
|
|
449
|
+
if (runtime.destroyed) throw new Error("表单已关闭");
|
|
450
|
+
if (this.fieldControlStatus === "error") throw new Error(this.fieldControlError);
|
|
451
|
+
if (runtime.timer) {
|
|
452
|
+
clearTimeout(runtime.timer);
|
|
453
|
+
runtime.timer = null;
|
|
454
|
+
}
|
|
455
|
+
await this.refreshFieldControl();
|
|
456
|
+
await this.$nextTick();
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
guardFieldControlWorkflow(option) {
|
|
460
|
+
if (!this.isFieldControlV2()) return option;
|
|
461
|
+
const snapshots = {};
|
|
462
|
+
const wrapped = { ...option };
|
|
463
|
+
[["onStart", "onBeforeStartSubmit", "start"], ["onClickAgree", "onBeforeAgree", "agree"]]
|
|
464
|
+
.forEach(([begin, submit, key]) => {
|
|
465
|
+
[begin, submit].forEach(hook => {
|
|
466
|
+
const original = option[hook];
|
|
467
|
+
wrapped[hook] = async (done, ...args) => {
|
|
468
|
+
try {
|
|
469
|
+
await this.ensureFieldControlReady();
|
|
470
|
+
if (hook === begin) snapshots[key] = this.captureFieldControlSubmission();
|
|
471
|
+
else await this.assertFieldControlSubmission(snapshots[key]);
|
|
472
|
+
let completed = false;
|
|
473
|
+
const once = (...values) => {
|
|
474
|
+
if (completed) return;
|
|
475
|
+
completed = true;
|
|
476
|
+
done(...values);
|
|
477
|
+
};
|
|
478
|
+
// 保留原钩子的校验范围,不为直接审批额外引入整单必填。
|
|
479
|
+
if (original) await original.call(option, once, ...args);
|
|
480
|
+
else once();
|
|
481
|
+
} catch (error) { this.$message.error(error.message); }
|
|
482
|
+
};
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
return wrapped;
|
|
486
|
+
},
|
|
487
|
+
captureFieldControlSubmission() {
|
|
488
|
+
if (!this.isFieldControlV2()) return null;
|
|
489
|
+
return {
|
|
490
|
+
data: cloneDeep(this.formModel), context: this.fieldControlContextKey,
|
|
491
|
+
revision: this.getFieldControlRuntime().revision,
|
|
492
|
+
};
|
|
493
|
+
},
|
|
494
|
+
async assertFieldControlSubmission(snapshot) {
|
|
495
|
+
if (!snapshot) return;
|
|
496
|
+
await this.ensureFieldControlReady();
|
|
497
|
+
if (!sameValue(snapshot.data, this.formModel) || snapshot.context !== this.fieldControlContextKey
|
|
498
|
+
|| snapshot.revision !== this.getFieldControlRuntime().revision) {
|
|
499
|
+
throw new Error("确认期间表单或流程已变化,请重新保存");
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
disposeFieldControl() {
|
|
503
|
+
const runtime = this.getFieldControlRuntime();
|
|
504
|
+
this.stopFieldControlWatcher();
|
|
505
|
+
runtime.epoch++;
|
|
506
|
+
runtime.destroyed = true;
|
|
507
|
+
runtime.cancelRequests.forEach(cancel => cancel());
|
|
508
|
+
runtime.cancelRequests.clear();
|
|
509
|
+
},
|
|
510
|
+
resetFieldControl() {
|
|
511
|
+
this.disposeFieldControl();
|
|
512
|
+
this._fieldControl = createRuntime();
|
|
513
|
+
this.fieldControlInitialized = false;
|
|
514
|
+
this.fieldControlStatus = "idle";
|
|
515
|
+
this.fieldControlError = "";
|
|
516
|
+
// 同时清理 V1,支持 setFormJson 在两个版本之间切换。
|
|
517
|
+
if (this._fdfWatcher) this._fdfWatcher();
|
|
518
|
+
if (this._fdfTimer) clearTimeout(this._fdfTimer);
|
|
519
|
+
this._fdfWatcher = null;
|
|
520
|
+
this._fdfTimer = null;
|
|
521
|
+
this._fdfInited = false;
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
};
|
|
@@ -43,6 +43,7 @@ export default {
|
|
|
43
43
|
},
|
|
44
44
|
methods: {
|
|
45
45
|
initFormDynamicFields() {
|
|
46
|
+
if (this.isFieldControlV2 && this.isFieldControlV2()) return;
|
|
46
47
|
const cfg = this.formConfig || {};
|
|
47
48
|
if (!cfg.dynamicFieldEnabled) return;
|
|
48
49
|
this._fdfInited = true;
|
|
@@ -54,6 +55,7 @@ export default {
|
|
|
54
55
|
},
|
|
55
56
|
|
|
56
57
|
applyFormDynamicFields() {
|
|
58
|
+
if (this.isFieldControlV2 && this.isFieldControlV2()) return this.refreshFieldControl();
|
|
57
59
|
const cfg = this.formConfig || {};
|
|
58
60
|
if (!cfg.dynamicFieldEnabled) return;
|
|
59
61
|
if (cfg.dynamicFieldSourceType === "script") {
|