cloud-web-corejs 1.1.0-dev.20 → 1.1.0-dev.22

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 (27) hide show
  1. package/package.json +1 -1
  2. package/src/components/excelExport/conditionExportConfig.js +112 -0
  3. package/src/components/excelExport/exportType.js +8 -0
  4. package/src/components/excelExport/index.vue +7 -1
  5. package/src/components/excelExport/mixins.js +410 -259
  6. package/src/components/xform/form-designer/designer.js +0 -15
  7. package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +2 -2
  8. package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +2 -1
  9. package/src/components/xform/form-designer/form-widget/field-widget/table-export-button-widget.vue +2 -1
  10. package/src/components/xform/form-designer/setting-panel/form-dynamicField-setting.vue +163 -136
  11. package/src/components/xform/form-designer/setting-panel/form-setting.vue +74 -9
  12. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/empty-number-input.vue +51 -0
  13. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +27 -2
  14. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/table-export-button-editor.vue +33 -3
  15. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +6 -0
  16. package/src/components/xform/form-render/container-item/data-table-mixin.js +6 -6
  17. package/src/components/xform/form-render/container-item/dynamicFieldMixin.js +4 -4
  18. package/src/components/xform/form-render/container-item/tab-item.vue +1 -1
  19. package/src/components/xform/form-render/fieldControlEngine.js +18 -0
  20. package/src/components/xform/form-render/fieldControlMixin.js +39 -29
  21. package/src/components/xform/form-render/index.vue +7 -4
  22. package/src/components/xform/form-render/indexMixin.js +26 -15
  23. package/src/components/xform/utils/util.js +0 -1
  24. package/src/views/user/home/default.vue +6 -3
  25. package/src/views/user/home/wjl/content.vue +3 -2
  26. package/src/components/xform/form-render/container-item/dynamicFieldEngine.js +0 -285
  27. package/src/components/xform/form-render/formDynamicFieldMixin.js +0 -133
@@ -19,10 +19,29 @@
19
19
  ></el-switch>
20
20
  </el-form-item>
21
21
  <el-form-item label="导出每页查询数量" class="form-item-label-top">
22
- <base-input-number
22
+ <empty-number-input
23
23
  v-model="optionModel.exportPageSize"
24
24
  :max="optionModel.showImageAtTable ? 150 : 1000"
25
- ></base-input-number>
25
+ :placeholder="'默认 ' + defaultPageSize"
26
+ ></empty-number-input>
27
+ </el-form-item>
28
+ <el-form-item label="最大查询总数" class="form-item-label-top">
29
+ <empty-number-input
30
+ v-model="optionModel.maxQueryTotal"
31
+ :placeholder="'默认 ' + defaultMaxQueryTotal"
32
+ ></empty-number-input>
33
+ <div class="export-item-columns-tip">
34
+ 条件导出最多拉取的行数。留空则使用全局逻辑参数(默认 {{ defaultMaxQueryTotal }})
35
+ </div>
36
+ </el-form-item>
37
+ <el-form-item label="每个文件最大数据量" class="form-item-label-top">
38
+ <empty-number-input
39
+ v-model="optionModel.limitFileSize"
40
+ :placeholder="'默认 ' + defaultLimitFileSize"
41
+ ></empty-number-input>
42
+ <div class="export-item-columns-tip">
43
+ 单个 Excel 最多行数,超出则分多个文件。留空则使用全局逻辑参数(默认 {{ defaultLimitFileSize }})
44
+ </div>
26
45
  </el-form-item>
27
46
  <el-form-item label="导出参数" label-width="150px">
28
47
  <a
@@ -80,12 +99,15 @@
80
99
  import i18n from "../../../../utils/i18n";
81
100
  import eventMixin from "../event-handler/eventMixin";
82
101
  import exportItemColumnsDialog from "../container-data-table/exportItemColumns-dialog.vue";
102
+ import { DEFAULT_CONDITION_EXPORT_CONFIG } from "@base/components/excelExport/conditionExportConfig";
103
+ import emptyNumberInput from "./empty-number-input.vue";
83
104
 
84
105
  export default {
85
106
  name: "select-export-item-button-editor",
86
107
  mixins: [i18n, eventMixin],
87
108
  components: {
88
109
  exportItemColumnsDialog,
110
+ emptyNumberInput,
89
111
  },
90
112
  props: {
91
113
  designer: Object,
@@ -98,6 +120,9 @@ export default {
98
120
  // 与数据表格属性面板同一套:脚本里可直接用 dataId / formCode
99
121
  tableConfigParams: ["dataId", "formCode"],
100
122
  showExportItemColumnsDialog: false,
123
+ defaultPageSize: DEFAULT_CONDITION_EXPORT_CONFIG.pageSize,
124
+ defaultMaxQueryTotal: DEFAULT_CONDITION_EXPORT_CONFIG.maxQueryTotal,
125
+ defaultLimitFileSize: DEFAULT_CONDITION_EXPORT_CONFIG.limitFileSize,
101
126
  };
102
127
  },
103
128
  methods: {
@@ -13,8 +13,23 @@
13
13
  <el-switch v-model="optionModel.showImageAtTable" @change="changeShowImageAtTable"></el-switch>
14
14
  </el-form-item>
15
15
  <el-form-item label="导出每页查询数量" class="form-item-label-top">
16
- <base-input-number v-model="optionModel.exportPageSize"
17
- :max="optionModel.showImageAtTable?150:1000"></base-input-number>
16
+ <empty-number-input v-model="optionModel.exportPageSize"
17
+ :max="optionModel.showImageAtTable?150:1000"
18
+ :placeholder="'默认 ' + defaultPageSize"></empty-number-input>
19
+ </el-form-item>
20
+ <el-form-item label="最大查询总数" class="form-item-label-top">
21
+ <empty-number-input v-model="optionModel.maxQueryTotal"
22
+ :placeholder="'默认 ' + defaultMaxQueryTotal"></empty-number-input>
23
+ <div class="export-limit-tip">
24
+ 条件导出最多拉取的行数。留空则使用全局逻辑参数(默认 {{ defaultMaxQueryTotal }})
25
+ </div>
26
+ </el-form-item>
27
+ <el-form-item label="每个文件最大数据量" class="form-item-label-top">
28
+ <empty-number-input v-model="optionModel.limitFileSize"
29
+ :placeholder="'默认 ' + defaultLimitFileSize"></empty-number-input>
30
+ <div class="export-limit-tip">
31
+ 单个 Excel 最多行数,超出则分多个文件。留空则使用全局逻辑参数(默认 {{ defaultLimitFileSize }})
32
+ </div>
18
33
  </el-form-item>
19
34
  <el-form-item label="导出参数" label-width="150px">
20
35
  <a href="javascript:void(0);" class="a-link link-oneLind"
@@ -30,10 +45,15 @@
30
45
  import i18n from "../../../../../../components/xform/utils/i18n";
31
46
  import eventMixin
32
47
  from "../../../../../../components/xform/form-designer/setting-panel/property-editor/event-handler/eventMixin";
48
+ import { DEFAULT_CONDITION_EXPORT_CONFIG } from "@base/components/excelExport/conditionExportConfig";
49
+ import emptyNumberInput from "./empty-number-input.vue";
33
50
 
34
51
  export default {
35
52
  name: "table-export-button-editor",
36
53
  mixins: [i18n, eventMixin],
54
+ components: {
55
+ emptyNumberInput,
56
+ },
37
57
  props: {
38
58
  designer: Object,
39
59
  selectedWidget: Object,
@@ -42,6 +62,9 @@ export default {
42
62
  data() {
43
63
  return {
44
64
  eventParams: [],
65
+ defaultPageSize: DEFAULT_CONDITION_EXPORT_CONFIG.pageSize,
66
+ defaultMaxQueryTotal: DEFAULT_CONDITION_EXPORT_CONFIG.maxQueryTotal,
67
+ defaultLimitFileSize: DEFAULT_CONDITION_EXPORT_CONFIG.limitFileSize,
45
68
  };
46
69
  },
47
70
  methods: {
@@ -57,4 +80,11 @@ export default {
57
80
  };
58
81
  </script>
59
82
 
60
- <style scoped></style>
83
+ <style scoped>
84
+ .export-limit-tip {
85
+ line-height: 18px;
86
+ color: #909399;
87
+ font-size: 12px;
88
+ margin-top: 4px;
89
+ }
90
+ </style>
@@ -3287,6 +3287,9 @@ export const advancedFields = [
3287
3287
  tableRef: "",
3288
3288
  tableExportParam: "",
3289
3289
  showImageAtTable: false,
3290
+ exportPageSize: null,
3291
+ maxQueryTotal: null,
3292
+ limitFileSize: null,
3290
3293
  tableExportFlag: 1,
3291
3294
 
3292
3295
  onCreated: "",
@@ -3351,6 +3354,9 @@ export const advancedFields = [
3351
3354
  tableRef: "",
3352
3355
  tableExportParam: "",
3353
3356
  showImageAtTable: false,
3357
+ exportPageSize: null,
3358
+ maxQueryTotal: null,
3359
+ limitFileSize: null,
3354
3360
  selectExportItemFlag: 1,
3355
3361
  exportItemConditionEnabled: !1,
3356
3362
  // 按钮级明细导出配置:三项各自独立,留空的那项沿用数据表格属性上维护的
@@ -1442,10 +1442,8 @@ modules = {
1442
1442
  const formRef = this.getFormRef();
1443
1443
  if (!formRef || formRef.designState) return;
1444
1444
  this.$nextTick(() => {
1445
- if (formRef.syncFieldControlAfterStructure && formRef.syncFieldControlAfterStructure()) return;
1446
- if (formRef.hanldeCommonWidgetForTableColumns) {
1447
- formRef.hanldeCommonWidgetForTableColumns(tableColumns);
1448
- }
1445
+ if (formRef.syncFieldControlAfterStructure()) return;
1446
+ formRef.hanldeCommonWidgetForTableColumns(tableColumns);
1449
1447
  });
1450
1448
  },
1451
1449
  /** @returns {Boolean} 首次初始化是否跳过动态列加载。 */
@@ -2800,7 +2798,9 @@ modules = {
2800
2798
  if (
2801
2799
  customParam?.exportParam?.type !== EXPORT_TYPE.ITEM_SELECTED
2802
2800
  ) {
2803
- formData = tableOption.param ? tableOption.param() || {} : {};
2801
+ formData = customParam?.exportParam?.exportQuerySnapshot !== undefined
2802
+ ? this.$baseLodash.cloneDeep(customParam.exportParam.exportQuerySnapshot)
2803
+ : tableOption.param ? tableOption.param() || {} : {};
2804
2804
  }
2805
2805
  const queryParams = Object.assign({}, formData);
2806
2806
 
@@ -4539,7 +4539,7 @@ modules = {
4539
4539
  try {
4540
4540
  // 行内保存只提交当前行,校验范围同步收敛到当前行:其他行与表外字段
4541
4541
  // 的必填不该拦住单行保存(整体保存仍走 formRef.validate 的全量校验)
4542
- const controlled = formRef.isFieldControlV2 && formRef.isFieldControlV2();
4542
+ const controlled = formRef.isFieldControlEnabled();
4543
4543
  const rowValid = controlled ? await this.validateEditRowAsync(obj.row) : this.validateEditRow(obj.row);
4544
4544
  if (!rowValid) return false;
4545
4545
  const fieldControlSnapshot = controlled ? formRef.captureFieldControlSubmission() : null;
@@ -10,7 +10,7 @@ import {
10
10
  /**
11
11
  * 表格布局容器(table/h5-table)专用混入。
12
12
  *
13
- * 提供两类「布局表格」特有能力(动态字段状态控制已统一收敛到表单设置,见 formDynamicFieldMixin):
13
+ * 提供两类「布局表格」特有能力(动态字段状态控制由表单级 fieldControlMixin 统一处理):
14
14
  * 1. 动态字段(dynamicSchemaSourceType):字段定义由脚本或接口下发,运行时生成并插入到行布局中。
15
15
  * 2. 行折叠(默认行为):整行字段均隐藏时折叠该行(响应式,随字段显隐自动更新)。
16
16
  *
@@ -108,7 +108,7 @@ const dynamicFieldMixin = {
108
108
  }
109
109
  return Promise.resolve(true);
110
110
  } catch (e) {
111
- if (formRef.isFieldControlV2 && formRef.isFieldControlV2()) return Promise.reject(e);
111
+ if (formRef.isFieldControlEnabled()) return Promise.reject(e);
112
112
  return Promise.resolve(false);
113
113
  }
114
114
  },
@@ -118,7 +118,7 @@ const dynamicFieldMixin = {
118
118
  if (!this.supportsRowLayout()) return;
119
119
  if (this.getDynamicSchemaSourceType() === "none") return;
120
120
  const formRef = this.getFormRef();
121
- if (formRef && formRef.isFieldControlV2 && formRef.isFieldControlV2()) {
121
+ if (formRef && formRef.isFieldControlEnabled()) {
122
122
  const task = this.$nextTick().then(() => this.resolveShouldInitDynamicSchema())
123
123
  .then(shouldInit => shouldInit ? this.loadControlledDynamicSchema() : undefined);
124
124
  return formRef.trackFieldControlSchema(task, () => this.loadDynamicSchema(), this);
@@ -176,7 +176,7 @@ const dynamicFieldMixin = {
176
176
  const formRef = this.getFormRef && this.getFormRef();
177
177
  if (!this.designState && this.supportsRowLayout() &&
178
178
  this.getDynamicSchemaSourceType() !== "none" &&
179
- formRef && formRef.isFieldControlV2 && formRef.isFieldControlV2()) {
179
+ formRef && formRef.isFieldControlEnabled()) {
180
180
  return formRef.trackFieldControlSchema(this.loadControlledDynamicSchema(),
181
181
  () => this.loadDynamicSchema(), this);
182
182
  }
@@ -99,7 +99,7 @@ export default {
99
99
  watch: {
100
100
  visibleTabs(tabs) {
101
101
  const formRef = this.getFormRef();
102
- if (formRef && formRef.isFieldControlV2 && formRef.isFieldControlV2() &&
102
+ if (formRef && formRef.isFieldControlEnabled() &&
103
103
  !tabs.some(tab => tab.options.name === this.activeTabName)) {
104
104
  this.activeTabName = tabs.length ? tabs[0].options.name : "";
105
105
  }
@@ -99,6 +99,24 @@ export function normalizeLocalRules(rules) {
99
99
  });
100
100
  }
101
101
 
102
+ export function commitLocalFieldRules(rules, widgetList) {
103
+ const list = Array.isArray(rules) ? rules : [];
104
+ if (!list.length) return [];
105
+ const options = fieldControlTreeOptions(widgetList || []);
106
+ const targets = new Set(options.map(item => item.value));
107
+ const fields = new Set(options.filter(item => !item.isContainer).map(item => item.value));
108
+ const normalized = normalizeLocalRules(list);
109
+ normalized.forEach(rule => {
110
+ rule.conditions.forEach(condition => {
111
+ if (!fields.has(condition.field)) throw new Error("条件字段不存在或不支持:" + condition.field);
112
+ });
113
+ rule.targets.forEach(target => {
114
+ if (!targets.has(target.field)) throw new Error("目标不存在或不支持:" + target.field);
115
+ });
116
+ });
117
+ return normalized;
118
+ }
119
+
102
120
  export function evaluateCondition(condition, model) {
103
121
  if (!condition || !condition.field) throw new Error("请选择动态规则条件字段");
104
122
  const actual = model[condition.field];
@@ -30,7 +30,12 @@ export default {
30
30
  computed: {
31
31
  fieldControlBlocked() {
32
32
  // 仅准备期隐藏,避免用户在流程限制生效前操作。error 要保留已填内容,提交由 ensure 拦截。
33
- return this.isFieldControlV2() && this.fieldControlStatus === "preparing";
33
+ return this.isFieldControlEnabled() && this.fieldControlStatus === "preparing";
34
+ },
35
+ fieldControlInteractionBlocked() {
36
+ // 错误时保留表单内容可见,但不允许继续编辑或触发字段/按钮副作用。
37
+ return this.isFieldControlEnabled()
38
+ && ["preparing", "error"].includes(this.fieldControlStatus);
34
39
  },
35
40
  fieldControlContextKey() {
36
41
  const info = this.wfParam?.wfInfo || {};
@@ -43,7 +48,7 @@ export default {
43
48
  watch: {
44
49
  fieldControlContextKey() {
45
50
  const runtime = this._fieldControl;
46
- if (!runtime || !this.isFieldControlV2()) return;
51
+ if (!runtime || !this.isFieldControlEnabled()) return;
47
52
  if (!this.fieldControlInitialized) {
48
53
  if (runtime.mounting || runtime.task) runtime.revision++;
49
54
  return;
@@ -52,7 +57,8 @@ export default {
52
57
  runtime.mode = resolveRunMode(this.formConfig, !!this.wfParam?.hasWf);
53
58
  this.stopFieldControlWatcher();
54
59
  if (previousMode !== runtime.mode || runtime.mode === "continuous") {
55
- this.invalidateFieldControl();
60
+ // 流程节点是权限边界,不能在防抖/后台请求期间继续使用旧节点状态。
61
+ this.invalidateFieldControl({ immediate: true, blockInteraction: true });
56
62
  } else {
57
63
  runtime.revision++;
58
64
  this.applyFieldControlStates(runtime.dynamicStates);
@@ -64,8 +70,8 @@ export default {
64
70
  this.disposeFieldControl();
65
71
  },
66
72
  methods: {
67
- isFieldControlV2() {
68
- return !!this.formConfig?.dynamicFieldEnabled && Number(this.formConfig.dynamicFieldRuleVersion) === 2;
73
+ isFieldControlEnabled() {
74
+ return !!this.formConfig?.dynamicFieldEnabled;
69
75
  },
70
76
  getFieldControlRuntime() {
71
77
  if (!this._fieldControl) this._fieldControl = createRuntime();
@@ -74,7 +80,7 @@ export default {
74
80
  prepareFieldControlTargets() {
75
81
  const runtime = this.getFieldControlRuntime();
76
82
  const entries = collectFieldControlTargets(this.widgetList);
77
- const widgets = this.getFieldControlWidgets ? this.getFieldControlWidgets() : entries.map(e => e.widget);
83
+ const widgets = this.getFieldControlWidgets();
78
84
  runtime.entries = entries;
79
85
  runtime.byWidget = new WeakMap();
80
86
  entries.forEach(entry => runtime.byWidget.set(entry.widget, entry));
@@ -86,9 +92,9 @@ export default {
86
92
  runtime.definitions.set(widget, state);
87
93
  });
88
94
  },
89
- // 动态列/结构晚于首次采集时,先登记未改写的定义,再走动态→流程,避免 V1 旁路污染基线。
95
+ // 动态列/结构晚于首次采集时,先登记未改写的定义,再走动态→流程。
90
96
  syncFieldControlAfterStructure() {
91
- if (!this.isFieldControlV2()) return false;
97
+ if (!this.isFieldControlEnabled()) return false;
92
98
  this.prepareFieldControlTargets();
93
99
  const runtime = this.getFieldControlRuntime();
94
100
  if (this.fieldControlInitialized) {
@@ -107,7 +113,7 @@ export default {
107
113
  const result = Object.create(null);
108
114
  this.getFieldControlRuntime().entries.forEach(entry => {
109
115
  const projected = this.createFieldControlProjection(entry.widget);
110
- if (this.handleWidgetShowRule) this.handleWidgetShowRule(projected);
116
+ this.handleWidgetShowRule(projected);
111
117
  result[entry.key] = readFieldState(projected);
112
118
  });
113
119
  return result;
@@ -192,7 +198,7 @@ export default {
192
198
  if (runtime.schemaErrors.size) throw Array.from(runtime.schemaErrors.values())[0].error;
193
199
  },
194
200
  async initializeFieldControl() {
195
- if (!this.isFieldControlV2()) return;
201
+ if (!this.isFieldControlEnabled()) return;
196
202
  const runtime = this.getFieldControlRuntime();
197
203
  if (runtime.destroyed) throw new Error("表单已关闭");
198
204
  this.fieldControlStatus = "preparing";
@@ -247,18 +253,25 @@ export default {
247
253
  if (!sameValue(runtime.lastDependency, this.fieldControlDependency())) this.invalidateFieldControl();
248
254
  }, { deep: true, sync: true });
249
255
  },
250
- invalidateFieldControl() {
256
+ invalidateFieldControl(options = {}) {
251
257
  const runtime = this.getFieldControlRuntime();
258
+ const immediate = options.immediate === true;
259
+ if (options.blockInteraction) {
260
+ this.fieldControlStatus = "preparing";
261
+ this.fieldControlError = "";
262
+ }
252
263
  runtime.revision++;
253
264
  runtime.dirty = true;
254
265
  if (runtime.timer) clearTimeout(runtime.timer);
255
- runtime.timer = setTimeout(() => {
266
+ const refresh = () => {
256
267
  runtime.timer = null;
257
268
  this.refreshFieldControl().catch(error => this.failFieldControl(error, runtime));
258
- }, 120);
269
+ };
270
+ if (immediate) refresh();
271
+ else runtime.timer = setTimeout(refresh, 120);
259
272
  },
260
273
  async refreshFieldControl() {
261
- if (!this.isFieldControlV2()) return;
274
+ if (!this.isFieldControlEnabled()) return;
262
275
  const runtime = this.getFieldControlRuntime();
263
276
  if (runtime.timer) clearTimeout(runtime.timer);
264
277
  runtime.timer = null;
@@ -389,14 +402,14 @@ export default {
389
402
  },
390
403
  applyFieldControlStates(states) {
391
404
  const runtime = this.getFieldControlRuntime();
392
- this.widgetEditOnWf = this.hanldeWfWidgetNew0 ? this.hanldeWfWidgetNew0() : false;
405
+ this.widgetEditOnWf = this.hanldeWfWidgetNew0();
393
406
  let editable = false;
394
407
  runtime.flowWidgets.forEach(widget => {
395
408
  const entry = runtime.byWidget.get(widget);
396
409
  const projected = this.createFieldControlProjection(widget);
397
- if (this.handleWidgetShowRule) this.handleWidgetShowRule(projected);
410
+ this.handleWidgetShowRule(projected);
398
411
  if (entry && states?.[entry.key]) writeStateOptions(projected.options, states[entry.key]);
399
- if (this.applyWorkflowFieldControl) this.applyWorkflowFieldControl(projected);
412
+ this.applyWorkflowFieldControl(projected);
400
413
  if (widget.formItemFlag && this.disabledMode) projected.options.disabled = true;
401
414
  const effective = readFieldState(projected);
402
415
  // 非输入按钮不在动态目标范围,但仍应用流程自身的 disabled。
@@ -407,7 +420,7 @@ export default {
407
420
  applyFieldState(target, effective, name => this.getWidgetRef(name));
408
421
  if (widget.formItemFlag && !projected.options.hidden && !projected.options.disabled && !projected.options.readonly) editable = true;
409
422
  });
410
- this.wfModifyEnabled = !!this.wfParam?.wfInfo?.toModify && editable && !this.getReadMode?.();
423
+ this.wfModifyEnabled = !!this.wfParam?.wfInfo?.toModify && editable && !this.getReadMode();
411
424
  this.syncFieldControlValidation();
412
425
  },
413
426
  syncFieldControlValidation() {
@@ -429,11 +442,14 @@ export default {
429
442
  if (runtime.destroyed || runtime !== this.getFieldControlRuntime()) return;
430
443
  this.fieldControlStatus = "error";
431
444
  this.fieldControlError = error?.message || "字段规则执行失败,请重试";
432
- // 无成功结果时至少应用已知流程限制。错误状态独立阻挡编辑/提交。
433
- if (!runtime.dynamicStates && runtime.flowWidgets.length) this.applyFieldControlStates(null);
445
+ // 即使动态重算失败,也要用最后一份可信动态状态重新收口当前流程限制。
446
+ // 交互由 fieldControlInteractionBlocked 独立阻断,不污染定义基线。
447
+ if (runtime.flowWidgets.length) {
448
+ try { this.applyFieldControlStates(runtime.dynamicStates); } catch (applyError) { /* 保留原始失败 */ }
449
+ }
434
450
  },
435
451
  async ensureFieldControlReady() {
436
- if (!this.isFieldControlV2()) return;
452
+ if (!this.isFieldControlEnabled()) return;
437
453
  const runtime = this.getFieldControlRuntime();
438
454
  if (runtime.mountTask) await runtime.mountTask;
439
455
  if (runtime.destroyed) throw new Error("表单已关闭");
@@ -457,7 +473,7 @@ export default {
457
473
  }
458
474
  },
459
475
  guardFieldControlWorkflow(option) {
460
- if (!this.isFieldControlV2()) return option;
476
+ if (!this.isFieldControlEnabled()) return option;
461
477
  const snapshots = {};
462
478
  const wrapped = { ...option };
463
479
  [["onStart", "onBeforeStartSubmit", "start"], ["onClickAgree", "onBeforeAgree", "agree"]]
@@ -485,7 +501,7 @@ export default {
485
501
  return wrapped;
486
502
  },
487
503
  captureFieldControlSubmission() {
488
- if (!this.isFieldControlV2()) return null;
504
+ if (!this.isFieldControlEnabled()) return null;
489
505
  return {
490
506
  data: cloneDeep(this.formModel), context: this.fieldControlContextKey,
491
507
  revision: this.getFieldControlRuntime().revision,
@@ -513,12 +529,6 @@ export default {
513
529
  this.fieldControlInitialized = false;
514
530
  this.fieldControlStatus = "idle";
515
531
  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
532
  },
523
533
  },
524
534
  };
@@ -10,7 +10,7 @@
10
10
 
11
11
  <template>
12
12
  <div>
13
- <el-alert v-if="isFieldControlV2() && fieldControlError" :title="fieldControlError" type="error" :closable="false" show-icon>
13
+ <el-alert v-if="fieldControlError" :title="fieldControlError" type="error" :closable="false" show-icon>
14
14
  <el-button type="text" @click="retryFieldControl">重新计算字段规则</el-button>
15
15
  </el-alert>
16
16
  <div v-if="fieldControlBlocked" role="status">正在准备表单,请稍候…</div>
@@ -19,8 +19,12 @@
19
19
  :size="size"
20
20
  :class="[customClass, h5RootClass]"
21
21
  class="render-form"
22
- :style="fieldControlBlocked ? { visibility: 'hidden' } : null"
22
+ :style="fieldControlBlocked
23
+ ? { visibility: 'hidden' }
24
+ : (fieldControlInteractionBlocked ? { pointerEvents: 'none' } : null)"
25
+ :inert="fieldControlInteractionBlocked ? '' : null"
23
26
  :aria-busy="fieldControlBlocked ? 'true' : 'false'"
27
+ :aria-disabled="fieldControlInteractionBlocked ? 'true' : 'false'"
24
28
  :label-width="labelWidth"
25
29
  :validate-on-rule-change="false"
26
30
  :model="formDataModel"
@@ -119,7 +123,6 @@
119
123
  import "./container-item/index";
120
124
  import FieldComponents from "../../../components/xform/form-designer/form-widget/field-widget/index";
121
125
  import indexMixin from "../../../components/xform/form-render/indexMixin";
122
- import formDynamicFieldMixin from "../../../components/xform/form-render/formDynamicFieldMixin";
123
126
 
124
127
  import fieldControlMixin from "@base/components/xform/form-render/fieldControlMixin";
125
128
 
@@ -144,7 +147,7 @@ export default {
144
147
  compareDelList: () =>
145
148
  import("../../../components/xform/form-render/compareDelList.vue"),
146
149
  },
147
- mixins: [indexMixin, formDynamicFieldMixin, fieldControlMixin],
150
+ mixins: [indexMixin, fieldControlMixin],
148
151
  };
149
152
  </script>
150
153
 
@@ -386,7 +386,7 @@ modules = {
386
386
  let formConfig = this.formConfig;
387
387
  if (formConfig.isLoadEntity) {
388
388
  this.loadDefaultEntityData(() => {
389
- if (!(this.isFieldControlV2 && this.isFieldControlV2())) {
389
+ if (!this.isFieldControlEnabled()) {
390
390
  this.handleCustomEvent(formConfig.formScriptSuccess);
391
391
  }
392
392
  });
@@ -574,7 +574,7 @@ modules = {
574
574
  : null;
575
575
  },
576
576
  handleShowContent(callback) {
577
- if (this.isFieldControlV2 && this.isFieldControlV2()) return this.showControlledForm(callback);
577
+ if (this.isFieldControlEnabled()) return this.showControlledForm(callback);
578
578
  // this.buildFormModel(!this.formJsonObj ? null : this.formJsonObj.widgetList);
579
579
  this.handleBeforeMounted().then(() => {
580
580
  this.showFormContent = true;
@@ -742,8 +742,14 @@ modules = {
742
742
  })*/
743
743
  },
744
744
  hanldeCommonWidget2() {
745
- if (this.isFieldControlV2 && this.isFieldControlV2()) {
746
- this.prepareFieldControlTargets();
745
+ if (this.isFieldControlEnabled()) {
746
+ // 渲染前仍先补齐列 widget,但配置异常不能跳出受控初始化:
747
+ // showControlledForm 会再次采集并把错误落到 error/重试界面。
748
+ try {
749
+ this.prepareFieldControlTargets();
750
+ } catch (error) {
751
+ this.failFieldControl(error);
752
+ }
747
753
  return;
748
754
  }
749
755
  let wfParam = this.wfParam || {};
@@ -1561,7 +1567,7 @@ modules = {
1561
1567
  if (wfConfig1) {
1562
1568
  resOption = extendDeeply(resOption, wfConfig1);
1563
1569
  }
1564
- return this.guardFieldControlWorkflow ? this.guardFieldControlWorkflow(resOption) : resOption;
1570
+ return this.guardFieldControlWorkflow(resOption);
1565
1571
  };
1566
1572
  }
1567
1573
  return this.loadFormData({
@@ -2093,7 +2099,7 @@ modules = {
2093
2099
  !i.formConfig || !i.widgetList)
2094
2100
  )
2095
2101
  return void this.$message.error("Invalid format of form json.");
2096
- if (this.resetFieldControl) this.resetFieldControl();
2102
+ this.resetFieldControl();
2097
2103
  this.clearFormDataModel(),
2098
2104
  this.buildFormModel(i.widgetList),
2099
2105
  this.$set(this.formJsonObj, "formConfig", i.formConfig),
@@ -2103,11 +2109,8 @@ modules = {
2103
2109
  this.insertCustomStyleAndScriptNode(),
2104
2110
  this.$nextTick(function () {
2105
2111
  t.initFormObject(!1);
2106
- if (t.isFieldControlV2 && t.isFieldControlV2()) t.showControlledForm();
2107
- else {
2108
- t.handleOnMounted();
2109
- if (t.initFormDynamicFields) t.initFormDynamicFields();
2110
- }
2112
+ if (t.isFieldControlEnabled()) t.showControlledForm();
2113
+ else t.handleOnMounted();
2111
2114
  });
2112
2115
  } else this.$message.error("Set form json failed.");
2113
2116
  },
@@ -2120,7 +2123,7 @@ modules = {
2120
2123
  },
2121
2124
  getFormData: function () {
2122
2125
  const validateData = arguments.length === 0 || arguments[0] === undefined || arguments[0];
2123
- if (validateData && this.isFieldControlV2 && this.isFieldControlV2()) {
2126
+ if (validateData && this.isFieldControlEnabled()) {
2124
2127
  return this.ensureFieldControlReady().then(() => new Promise((resolve, reject) => {
2125
2128
  const snapshot = this.captureFieldControlSubmission();
2126
2129
  this.validateCurrentForm(async valid => {
@@ -2275,7 +2278,7 @@ modules = {
2275
2278
  this.$refs.renderForm.clearValidate(e);
2276
2279
  },
2277
2280
  validateForm: function (e) {
2278
- if (this.isFieldControlV2 && this.isFieldControlV2()) return this.validate(e);
2281
+ if (this.isFieldControlEnabled()) return this.validate(e);
2279
2282
 
2280
2283
  this.$refs["renderForm"].$baseValidate((valid) => {
2281
2284
  if (!valid) {
@@ -4662,6 +4665,8 @@ modules = {
4662
4665
  saveForm(callback) {
4663
4666
  this.validateForm((valid) => {
4664
4667
  if (!valid) return;
4668
+ // 从校验通过起锁定数据/规则版本;getSubData 和确认框都是异步窗口。
4669
+ const fieldControlSnapshot = this.captureFieldControlSubmission();
4665
4670
  this.getSubData().then((formData) => {
4666
4671
  let reportTemplate = this.reportTemplate || {};
4667
4672
  let formConfig = this.formConfig;
@@ -4676,7 +4681,13 @@ modules = {
4676
4681
  data: realFormData,
4677
4682
  };
4678
4683
 
4679
- this.$baseConfirm("您确定要保存吗?").then(() => {
4684
+ this.$baseConfirm("您确定要保存吗?").then(async () => {
4685
+ try {
4686
+ await this.assertFieldControlSubmission(fieldControlSnapshot);
4687
+ } catch (error) {
4688
+ this.$message.error(error.message);
4689
+ return;
4690
+ }
4680
4691
  let saveConfig = formConfig.saveConfig || {};
4681
4692
  this.formHttp({
4682
4693
  options: saveConfig,
@@ -4932,7 +4943,7 @@ modules = {
4932
4943
  }
4933
4944
  },
4934
4945
  validate(callback) {
4935
- if (this.isFieldControlV2 && this.isFieldControlV2()) {
4946
+ if (this.isFieldControlEnabled()) {
4936
4947
  return this.ensureFieldControlReady().then(() => new Promise(resolve => {
4937
4948
  const snapshot = this.captureFieldControlSubmission();
4938
4949
  this.validateCurrentForm(async valid => {
@@ -1135,7 +1135,6 @@ export function getDefaultFormConfig() {
1135
1135
  globalConfig: null,
1136
1136
  // 表单级动态字段规则:按本地规则/后台脚本动态控制整个表单内任意字段/容器的显隐/必填/只读/禁用/取值
1137
1137
  dynamicFieldEnabled: false,
1138
- dynamicFieldRuleVersion: 2,
1139
1138
  dynamicFieldRunMode: "auto",
1140
1139
  dynamicFieldAllowValue: false,
1141
1140
  dynamicFieldSourceType: "local",
@@ -737,15 +737,18 @@ body #app .index-home {
737
737
  .time {
738
738
  color: #b9b9b9;
739
739
  font-size: 12px;
740
- right: 12px;
740
+ right: 12px;
741
+ background: #FFF;
742
+ padding-left: 8px;
741
743
  }
742
744
 
743
745
  &:hover {
744
746
  color: $baseColor;
745
- background-color: transparentize($baseColor, 0.97);
747
+ background-color: #F8FAFC;
746
748
 
747
749
  .time {
748
- color: $baseColor;
750
+ color: $baseColor;
751
+ background-color: #F8FAFC;
749
752
  }
750
753
  }
751
754
 
@@ -483,7 +483,8 @@ export default {
483
483
  let userInfo = res.objx;
484
484
  this.headPhotoUrl = userInfo.headPhotoUrl
485
485
  ? userInfo.headPhotoUrl
486
- : (require("@/resources/images/default-header.png").default || require("@/resources/images/default-header.png"));
486
+ : require("@/resources/images/default-header.png").default ||
487
+ require("@/resources/images/default-header.png");
487
488
 
488
489
  this.userInfo = userInfo;
489
490
  this.initServiceProviderRole();
@@ -709,7 +710,7 @@ export default {
709
710
  this.showDealDialog = true;
710
711
  },
711
712
  openOrderView(row) {
712
- let dataId = row.id;
713
+ let dataId = row.order_id;
713
714
  if (dataId == null) {
714
715
  this.$message.warning(this.$t1("未找到工单"));
715
716
  return;