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.
Files changed (18) 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 +2101 -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 +660 -602
  8. package/src/components/xform/form-designer/setting-panel/property-editor/field-import-button/import2-button-editor.vue +8 -0
  9. package/src/components/xform/form-render/container-item/data-table-mixin.js +6306 -6285
  10. package/src/components/xform/form-render/container-item/dynamicFieldMixin.js +54 -0
  11. package/src/components/xform/form-render/container-item/tab-item.vue +138 -129
  12. package/src/components/xform/form-render/fieldControlEngine.js +200 -0
  13. package/src/components/xform/form-render/fieldControlMixin.js +524 -0
  14. package/src/components/xform/form-render/formDynamicFieldMixin.js +2 -0
  15. package/src/components/xform/form-render/index.vue +161 -153
  16. package/src/components/xform/form-render/indexMixin.js +5404 -5331
  17. package/src/components/xform/mixins/defaultHandle.js +713 -707
  18. package/src/components/xform/utils/util.js +1711 -1708
@@ -108,6 +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
112
  return Promise.resolve(false);
112
113
  }
113
114
  },
@@ -116,6 +117,12 @@ const dynamicFieldMixin = {
116
117
  if (this.designState || !this.widget) return;
117
118
  if (!this.supportsRowLayout()) return;
118
119
  if (this.getDynamicSchemaSourceType() === "none") return;
120
+ const formRef = this.getFormRef();
121
+ if (formRef && formRef.isFieldControlV2 && formRef.isFieldControlV2()) {
122
+ const task = this.$nextTick().then(() => this.resolveShouldInitDynamicSchema())
123
+ .then(shouldInit => shouldInit ? this.loadControlledDynamicSchema() : undefined);
124
+ return formRef.trackFieldControlSchema(task, () => this.loadDynamicSchema(), this);
125
+ }
119
126
  this.$nextTick(() => {
120
127
  this.resolveShouldInitDynamicSchema().then((shouldInit) => {
121
128
  if (shouldInit) {
@@ -125,7 +132,54 @@ const dynamicFieldMixin = {
125
132
  });
126
133
  },
127
134
 
135
+ loadControlledDynamicSchema() {
136
+ const formRef = this.getFormRef();
137
+ const runtime = formRef.getFieldControlRuntime();
138
+ const sequence = (this._dynamicSchemaRun || 0) + 1;
139
+ this._dynamicSchemaRun = sequence;
140
+ const current = () => this._dynamicSchemaRun === sequence &&
141
+ formRef.getFieldControlRuntime() === runtime && !runtime.destroyed && !this._isDestroyed;
142
+ const build = result => {
143
+ if (current()) this.buildDynamicRows(result);
144
+ return this.$nextTick();
145
+ };
146
+ const options = this.widget.options;
147
+ if (this.getDynamicSchemaSourceType() === "script") {
148
+ const code = getDynamicSchemaOption(options, "frontendScript");
149
+ if (!code) return Promise.reject(new Error("请配置动态字段脚本"));
150
+ return Promise.resolve().then(() => this.handleCustomEvent(code))
151
+ .then(result => build(this.normalizeSchemaScriptResult(result)));
152
+ }
153
+ const scriptCode = getDynamicSchemaOption(options, "scriptCode");
154
+ if (!scriptCode) return Promise.reject(new Error("请配置动态字段接口编码"));
155
+ return new Promise((resolve, reject) => {
156
+ try {
157
+ const task = this.formHttp({
158
+ scriptCode,
159
+ isLoading: false,
160
+ data: buildDynamicSchemaRequestData(this.formModel,
161
+ getDynamicSchemaOption(options, "scriptParam"), this.handleCustomEvent.bind(this)),
162
+ success: response => {
163
+ if (!current()) { resolve(); return; }
164
+ try { Promise.resolve(build(this.resolveSchemaDefs(response))).then(resolve, reject); }
165
+ catch (error) { reject(error); }
166
+ },
167
+ fail: () => current() ? reject(new Error("动态字段加载失败,请重试")) : resolve(),
168
+ error: error => current() ? reject(error) : resolve(),
169
+ });
170
+ if (task && task.catch) task.catch(reject);
171
+ } catch (error) { reject(error); }
172
+ });
173
+ },
174
+
128
175
  loadDynamicSchema() {
176
+ const formRef = this.getFormRef && this.getFormRef();
177
+ if (!this.designState && this.supportsRowLayout() &&
178
+ this.getDynamicSchemaSourceType() !== "none" &&
179
+ formRef && formRef.isFieldControlV2 && formRef.isFieldControlV2()) {
180
+ return formRef.trackFieldControlSchema(this.loadControlledDynamicSchema(),
181
+ () => this.loadDynamicSchema(), this);
182
+ }
129
183
  if (this.designState || !this.supportsRowLayout()) {
130
184
  return Promise.resolve();
131
185
  }
@@ -1,129 +1,138 @@
1
- <template>
2
- <container-item-wrapper
3
- :widget="widget"
4
- :class="widget.options.isFullscreen ? 'full-height' : ''"
5
- >
6
- <div :key="widget.id" class="tab-container mHeight" v-show="!widget.options.hidden">
7
- <!-- 高度默认 auto(跟随内容):没在属性面板配 height 就不设。
8
- 原来兜底成固定 300px,内容多了内部滚动、少了留白,装详情内容模块时尤其别扭 -->
9
- <el-tabs
10
- v-model="activeTabName"
11
- :type="widget.displayType"
12
- :ref="widget.id"
13
- :class="[customClass, widget.options.tabClass]"
14
- :style="{
15
- height: widget.options.isFullscreen ? '' : widget.options.height,
16
- }"
17
- >
18
- <el-tab-pane
19
- v-for="tab in visibleTabs"
20
- :key="tab.options.name"
21
- :label="getI18nLabel(tab.options.label)"
22
- :disabled="tab.options.disabled"
23
- :name="tab.options.name"
24
- >
25
- <template v-for="(subWidget, swIdx) in tab.widgetList">
26
- <template v-if="'container' === subWidget.category">
27
- <component
28
- :is="subWidget.type + '-item'"
29
- :widget="subWidget"
30
- :key="subWidget.id || swIdx"
31
- :parent-list="tab.widgetList"
32
- :index-of-parent-list="swIdx"
33
- :parent-widget="widget"
34
- :tableParam="tableParam"
35
- :formItemProp="formItemProp"
36
- >
37
- <!-- 递归传递插槽!!! -->
38
- <template v-for="slot in Object.keys($scopedSlots)" v-slot:[slot]="scope">
39
- <slot :name="slot" v-bind="scope" />
40
- </template>
41
- </component>
42
- </template>
43
- <template v-else>
44
- <component
45
- :is="subWidget.type + '-widget'"
46
- :field="subWidget"
47
- :key="subWidget.id || swIdx"
48
- :parent-list="tab.widgetList"
49
- :index-of-parent-list="swIdx"
50
- :parent-widget="widget"
51
- :tableParam="tableParam"
52
- :formItemProp="formItemProp"
53
- >
54
- <!-- 递归传递插槽!!! -->
55
- <template v-for="slot in Object.keys($scopedSlots)" v-slot:[slot]="scope">
56
- <slot :name="slot" v-bind="scope" />
57
- </template>
58
- </component>
59
- </template>
60
- </template>
61
- </el-tab-pane>
62
- </el-tabs>
63
- </div>
64
- </container-item-wrapper>
65
- </template>
66
-
67
- <script>
68
- import emitter from "../../../../components/xform/utils/emitter";
69
- import i18n from "../../../../components/xform/utils/i18n";
70
- import refMixin from "../../../../components/xform/form-render/refMixin";
71
- import ContainerItemWrapper from "./container-item-wrapper";
72
- import containerItemMixin from "./containerItemMixin";
73
- import FieldComponents from "../../../../components/xform/form-designer/form-widget/field-widget/index";
74
-
75
- export default {
76
- name: "tab-item",
77
- componentName: "ContainerItem",
78
- mixins: [emitter, i18n, refMixin, containerItemMixin],
79
- components: {
80
- ContainerItemWrapper,
81
- ...FieldComponents,
82
- },
83
- props: {
84
- widget: Object,
85
- },
86
- inject: ["refList", "sfRefList", "globalModel"],
87
- data() {
88
- return {
89
- activeTabName: "",
90
- };
91
- },
92
- computed: {
93
- visibleTabs() {
94
- return this.widget.tabs.filter((tp) => {
95
- return !tp.options.hidden;
96
- });
97
- },
98
- },
99
- created() {
100
- this.initRefList();
101
- },
102
- mounted() {
103
- this.initActiveTab();
104
- },
105
- beforeDestroy() {
106
- this.unregisterFromRefList();
107
- },
108
- methods: {
109
- initActiveTab() {
110
- if (this.widget.type === "tab" && this.widget.tabs.length > 0) {
111
- let activePanes = this.widget.tabs.filter((tp) => {
112
- return tp.options.active === true;
113
- });
114
- if (activePanes.length > 0) {
115
- this.activeTabName = activePanes[0].options.name;
116
- } else {
117
- this.activeTabName = this.widget.tabs[0].options.name;
118
- }
119
- }
120
- },
121
- },
122
- };
123
- </script>
124
-
125
- <style lang="scss" scoped>
126
- /* 内容区高度跟随内容。原来写死 min-height:312px,会把上面 height:auto 的效果整个抵消掉
127
- (无论内容多少都至少 312px)。这里只留一个小兜底,保证空标签页不塌成一条线。 */
128
- ::v-deep .mHeight .el-tabs .el-tabs__content{min-height:60px}
129
- </style>
1
+ <template>
2
+ <container-item-wrapper
3
+ :widget="widget"
4
+ :class="widget.options.isFullscreen ? 'full-height' : ''"
5
+ >
6
+ <div :key="widget.id" class="tab-container mHeight" v-show="!widget.options.hidden">
7
+ <!-- 高度默认 auto(跟随内容):没在属性面板配 height 就不设。
8
+ 原来兜底成固定 300px,内容多了内部滚动、少了留白,装详情内容模块时尤其别扭 -->
9
+ <el-tabs
10
+ v-model="activeTabName"
11
+ :type="widget.displayType"
12
+ :ref="widget.id"
13
+ :class="[customClass, widget.options.tabClass]"
14
+ :style="{
15
+ height: widget.options.isFullscreen ? '' : widget.options.height,
16
+ }"
17
+ >
18
+ <el-tab-pane
19
+ v-for="tab in visibleTabs"
20
+ :key="tab.options.name"
21
+ :label="getI18nLabel(tab.options.label)"
22
+ :disabled="tab.options.disabled"
23
+ :name="tab.options.name"
24
+ >
25
+ <template v-for="(subWidget, swIdx) in tab.widgetList">
26
+ <template v-if="'container' === subWidget.category">
27
+ <component
28
+ :is="subWidget.type + '-item'"
29
+ :widget="subWidget"
30
+ :key="subWidget.id || swIdx"
31
+ :parent-list="tab.widgetList"
32
+ :index-of-parent-list="swIdx"
33
+ :parent-widget="widget"
34
+ :tableParam="tableParam"
35
+ :formItemProp="formItemProp"
36
+ >
37
+ <!-- 递归传递插槽!!! -->
38
+ <template v-for="slot in Object.keys($scopedSlots)" v-slot:[slot]="scope">
39
+ <slot :name="slot" v-bind="scope" />
40
+ </template>
41
+ </component>
42
+ </template>
43
+ <template v-else>
44
+ <component
45
+ :is="subWidget.type + '-widget'"
46
+ :field="subWidget"
47
+ :key="subWidget.id || swIdx"
48
+ :parent-list="tab.widgetList"
49
+ :index-of-parent-list="swIdx"
50
+ :parent-widget="widget"
51
+ :tableParam="tableParam"
52
+ :formItemProp="formItemProp"
53
+ >
54
+ <!-- 递归传递插槽!!! -->
55
+ <template v-for="slot in Object.keys($scopedSlots)" v-slot:[slot]="scope">
56
+ <slot :name="slot" v-bind="scope" />
57
+ </template>
58
+ </component>
59
+ </template>
60
+ </template>
61
+ </el-tab-pane>
62
+ </el-tabs>
63
+ </div>
64
+ </container-item-wrapper>
65
+ </template>
66
+
67
+ <script>
68
+ import emitter from "../../../../components/xform/utils/emitter";
69
+ import i18n from "../../../../components/xform/utils/i18n";
70
+ import refMixin from "../../../../components/xform/form-render/refMixin";
71
+ import ContainerItemWrapper from "./container-item-wrapper";
72
+ import containerItemMixin from "./containerItemMixin";
73
+ import FieldComponents from "../../../../components/xform/form-designer/form-widget/field-widget/index";
74
+
75
+ export default {
76
+ name: "tab-item",
77
+ componentName: "ContainerItem",
78
+ mixins: [emitter, i18n, refMixin, containerItemMixin],
79
+ components: {
80
+ ContainerItemWrapper,
81
+ ...FieldComponents,
82
+ },
83
+ props: {
84
+ widget: Object,
85
+ },
86
+ inject: ["refList", "sfRefList", "globalModel"],
87
+ data() {
88
+ return {
89
+ activeTabName: "",
90
+ };
91
+ },
92
+ computed: {
93
+ visibleTabs() {
94
+ return this.widget.tabs.filter((tp) => {
95
+ return !tp.options.hidden;
96
+ });
97
+ },
98
+ },
99
+ watch: {
100
+ visibleTabs(tabs) {
101
+ const formRef = this.getFormRef();
102
+ if (formRef && formRef.isFieldControlV2 && formRef.isFieldControlV2() &&
103
+ !tabs.some(tab => tab.options.name === this.activeTabName)) {
104
+ this.activeTabName = tabs.length ? tabs[0].options.name : "";
105
+ }
106
+ },
107
+ },
108
+ created() {
109
+ this.initRefList();
110
+ },
111
+ mounted() {
112
+ this.initActiveTab();
113
+ },
114
+ beforeDestroy() {
115
+ this.unregisterFromRefList();
116
+ },
117
+ methods: {
118
+ initActiveTab() {
119
+ if (this.widget.type === "tab" && this.widget.tabs.length > 0) {
120
+ let activePanes = this.widget.tabs.filter((tp) => {
121
+ return tp.options.active === true;
122
+ });
123
+ if (activePanes.length > 0) {
124
+ this.activeTabName = activePanes[0].options.name;
125
+ } else {
126
+ this.activeTabName = this.widget.tabs[0].options.name;
127
+ }
128
+ }
129
+ },
130
+ },
131
+ };
132
+ </script>
133
+
134
+ <style lang="scss" scoped>
135
+ /* 内容区高度跟随内容。原来写死 min-height:312px,会把上面 height:auto 的效果整个抵消掉
136
+ (无论内容多少都至少 312px)。这里只留一个小兜底,保证空标签页不塌成一条线。 */
137
+ ::v-deep .mHeight .el-tabs .el-tabs__content{min-height:60px}
138
+ </style>
@@ -0,0 +1,200 @@
1
+ import isEqual from "lodash/isEqual";
2
+
3
+ export const STATE_KEYS = ["visible", "required", "readonly", "disabled"];
4
+ const TRANSPARENT = ["grid-col", "table-cell", "h5-table-cell"];
5
+ const ISOLATED = ["vf-dialog", "vf-drawer"];
6
+ const ROW_CONTAINERS = ["data-table", "sub-form", "grid-sub-form", "list-h5"];
7
+
8
+ export function readFieldState(widget) {
9
+ const options = widget.options || {};
10
+ const state = { visible: !options.hidden };
11
+ if (widget.formItemFlag) {
12
+ state.required = !!options.required;
13
+ state.readonly = !!options.readonly;
14
+ state.disabled = !!options.disabled;
15
+ }
16
+ return state;
17
+ }
18
+
19
+ export function writeStateOptions(options, state) {
20
+ STATE_KEYS.forEach(key => {
21
+ if (typeof state[key] === "boolean") {
22
+ options[key === "visible" ? "hidden" : key] = key === "visible" ? !state[key] : state[key];
23
+ }
24
+ });
25
+ }
26
+
27
+ // 设计器和运行时共用同一范围;明细行由独立作用域处理,不混入主表 key。
28
+ export function collectFieldControlTargets(list) {
29
+ const entries = [];
30
+ const keys = new Set();
31
+ const walk = (widgets, parent, depth) => {
32
+ (widgets || []).forEach(widget => {
33
+ if (!widget || !widget.options || ISOLATED.includes(widget.type)) return;
34
+ const container = widget.category === "container";
35
+ let entry = parent;
36
+ if ((container && !TRANSPARENT.includes(widget.type)) || (!container && widget.formItemFlag)) {
37
+ const key = container ? widget.options.name
38
+ : ((widget.options.keyNameEnabled && widget.options.keyName) || widget.options.name);
39
+ if (key) {
40
+ if (keys.has(key)) throw new Error("动态规则目标名称重复:" + key);
41
+ keys.add(key);
42
+ entry = { key, widget, parent, depth, isContainer: container, refKey: widget.options.name };
43
+ entries.push(entry);
44
+ }
45
+ }
46
+ if (!container || ROW_CONTAINERS.includes(widget.type)) return;
47
+ const nextDepth = entry === parent ? depth : depth + 1;
48
+ const children = [];
49
+ (widget.rows || []).forEach(row => children.push(...(row.cols || [])));
50
+ ["cols", "tabs", "panes", "widgetList", "buttonWidgetList"].forEach(key => {
51
+ if (Array.isArray(widget[key])) children.push(...widget[key]);
52
+ });
53
+ walk(children, entry, nextDepth);
54
+ });
55
+ };
56
+ walk(list, null, 0);
57
+ return entries;
58
+ }
59
+
60
+ export function fieldControlTreeOptions(list) {
61
+ return collectFieldControlTargets(list).map(entry => ({
62
+ value: entry.key,
63
+ baseLabel: entry.widget.options.label || entry.key,
64
+ isContainer: entry.isContainer,
65
+ hasName: true,
66
+ type: entry.widget.type,
67
+ depth: entry.depth,
68
+ }));
69
+ }
70
+
71
+ export function normalizeState(state, label = "") {
72
+ if (!state || typeof state !== "object" || Array.isArray(state)) {
73
+ throw new Error("动态规则状态格式错误:" + label);
74
+ }
75
+ const result = {};
76
+ STATE_KEYS.forEach(key => {
77
+ const value = state[key];
78
+ if (value === undefined || value === null || value === "") return;
79
+ if (typeof value !== "boolean") throw new Error("动态规则状态必须为布尔值:" + label + "." + key);
80
+ result[key] = value;
81
+ });
82
+ return result;
83
+ }
84
+
85
+ export function normalizeLocalRules(rules) {
86
+ if (!Array.isArray(rules)) throw new Error("动态规则必须为数组");
87
+ return rules.map(rule => {
88
+ if (!rule || !Array.isArray(rule.conditions) || !Array.isArray(rule.targets)) {
89
+ throw new Error("动态规则缺少条件或目标");
90
+ }
91
+ if (rule.logic && !["and", "or"].includes(rule.logic)) throw new Error("动态规则条件逻辑无效");
92
+ return {
93
+ ...rule,
94
+ targets: rule.targets.map(target => {
95
+ if (!target.field) throw new Error("请选择动态规则目标");
96
+ return { field: target.field, ...normalizeState(target, target.field) };
97
+ }),
98
+ };
99
+ });
100
+ }
101
+
102
+ export function evaluateCondition(condition, model) {
103
+ if (!condition || !condition.field) throw new Error("请选择动态规则条件字段");
104
+ const actual = model[condition.field];
105
+ const expected = condition.value;
106
+ const empty = value => value === undefined || value === null || value === ""
107
+ || (Array.isArray(value) && value.length === 0);
108
+ const operators = {
109
+ eq: () => String(actual) === String(expected),
110
+ ne: () => String(actual) !== String(expected),
111
+ empty: () => empty(actual),
112
+ notEmpty: () => !empty(actual),
113
+ in: () => String(expected == null ? "" : expected).split(",").map(x => x.trim()).filter(Boolean).includes(String(actual)),
114
+ nin: () => !operators.in(),
115
+ includes: () => Array.isArray(actual) ? actual.map(String).includes(String(expected))
116
+ : String(actual == null ? "" : actual).includes(String(expected)),
117
+ };
118
+ if (["gt", "ge", "lt", "le"].includes(condition.operator)) {
119
+ if (empty(actual) || empty(expected) || typeof actual === "boolean" || Array.isArray(actual)) return false;
120
+ const a = Number(actual);
121
+ const b = Number(expected);
122
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return false;
123
+ return { gt: a > b, ge: a >= b, lt: a < b, le: a <= b }[condition.operator];
124
+ }
125
+ if (!operators[condition.operator]) throw new Error("动态规则操作符无效:" + condition.operator);
126
+ return operators[condition.operator]();
127
+ }
128
+
129
+ export function computeFieldStates(rules, baseline, model) {
130
+ const states = Object.create(null);
131
+ Object.keys(baseline).forEach(key => { states[key] = { ...baseline[key] }; });
132
+ normalizeLocalRules(rules).forEach(rule => {
133
+ rule.targets.forEach(target => {
134
+ if (!Object.prototype.hasOwnProperty.call(states, target.field)) {
135
+ throw new Error("动态规则目标不存在或不支持:" + target.field);
136
+ }
137
+ });
138
+ // 先检查所有条件,避免短路计算掩盖配置错误。
139
+ const matches = rule.conditions.map(condition => evaluateCondition(condition, model));
140
+ const match = !matches.length || (rule.logic === "or" ? matches.some(Boolean) : matches.every(Boolean));
141
+ if (match) rule.targets.forEach(target => Object.assign(states[target.field], normalizeState(target)));
142
+ });
143
+ return states;
144
+ }
145
+
146
+ export function resolveScriptStates(fieldStates, baseline, allowValue) {
147
+ if (!fieldStates || typeof fieldStates !== "object" || Array.isArray(fieldStates)) {
148
+ throw new Error("后台动态规则缺少有效的 fieldStates");
149
+ }
150
+ const states = Object.create(null);
151
+ const effects = Object.create(null);
152
+ Object.keys(baseline).forEach(key => { states[key] = { ...baseline[key] }; });
153
+ Object.keys(fieldStates).forEach(key => {
154
+ if (!Object.prototype.hasOwnProperty.call(states, key)) throw new Error("动态规则目标不存在或不支持:" + key);
155
+ const state = fieldStates[key];
156
+ Object.assign(states[key], normalizeState(state, key));
157
+ if (Object.prototype.hasOwnProperty.call(state, "value")) {
158
+ if (!allowValue) throw new Error("动态规则返回了 value,请先明确启用自动赋值:" + key);
159
+ effects[key] = { value: state.value };
160
+ }
161
+ if (state.options !== undefined) {
162
+ if (!Array.isArray(state.options)) throw new Error("动态选项必须为数组:" + key);
163
+ effects[key] = { ...effects[key], options: state.options };
164
+ }
165
+ });
166
+ return { states, effects };
167
+ }
168
+
169
+ export function isEntryVisible(entry) {
170
+ for (let current = entry; current; current = current.parent) {
171
+ if (current.widget.options.hidden) return false;
172
+ }
173
+ return true;
174
+ }
175
+
176
+ export function applyFieldState(entry, state, getRef) {
177
+ const widget = entry.widget;
178
+ const ref = getRef(entry.refKey);
179
+ const target = ref && (ref.field === widget || ref.widget === widget) ? ref : null;
180
+ const setters = { visible: "setHidden", required: "setRequired", readonly: "setReadonly", disabled: "setDisabled" };
181
+ STATE_KEYS.forEach(key => {
182
+ if (typeof state[key] !== "boolean" || (entry.isContainer && key !== "visible")) return;
183
+ const option = key === "visible" ? "hidden" : key;
184
+ const value = key === "visible" ? !state[key] : state[key];
185
+ if (widget.options[option] !== value || (target && ["hidden", "disabled", "readonly"].includes(option) && target[option] !== value)) {
186
+ if (target && typeof target[setters[key]] === "function") target[setters[key]](value);
187
+ else widget.options[option] = value;
188
+ }
189
+ });
190
+ }
191
+
192
+ export function sameValue(a, b) {
193
+ return isEqual(a, b);
194
+ }
195
+
196
+ export function resolveRunMode(config, hasWf) {
197
+ const mode = config.dynamicFieldRunMode || "auto";
198
+ if (!["auto", "initialOnly", "continuous"].includes(mode)) throw new Error("动态规则执行方式无效");
199
+ return mode === "auto" ? (hasWf ? "initialOnly" : "continuous") : mode;
200
+ }