cloud-web-corejs 1.1.0-dev.19 → 1.1.0-dev.21

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