cloud-web-corejs 1.0.54-dev.685 → 1.0.54-dev.687

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.
@@ -0,0 +1,322 @@
1
+ import { generateId } from "../../../../components/xform/utils/util";
2
+
3
+ /**
4
+ * 表格布局容器(table/h5-table)专用混入。
5
+ *
6
+ * 提供两类「布局表格」特有能力(动态字段状态控制已统一收敛到表单设置,见 formDynamicFieldMixin):
7
+ * 1. 后台字段定义(dynamicSchemaEnabled):字段定义由后台查询下发,运行时生成并插入到行布局中。
8
+ * 2. 行折叠(默认行为):整行字段均隐藏时折叠该行(响应式,随字段显隐自动更新)。
9
+ *
10
+ * 依赖宿主组件已混入:containerItemMixin(formModel/formHttp/handleCustomEvent/designState)
11
+ * 与 refMixin(getFormRef)。
12
+ */
13
+ const dynamicFieldMixin = {
14
+ data() {
15
+ return {
16
+ // anchor=replace 时被隐藏的占位行 id(响应式,驱动 rowHiddenMap 重算)
17
+ anchorHiddenRowId: null,
18
+ };
19
+ },
20
+ created() {
21
+ // 后台动态生成的行 id 集合(用于去重/清理)
22
+ this._generatedRowIds = [];
23
+ },
24
+ mounted() {
25
+ this.initDynamicSchema();
26
+ },
27
+ computed: {
28
+ // 行折叠状态:rowIdx -> true(折叠/隐藏)。响应式依赖字段 hidden 与 anchorHiddenRowId
29
+ // 行折叠为默认行为:整行字段均隐藏时自动折叠,避免布局空洞
30
+ rowHiddenMap() {
31
+ const map = {};
32
+ if (!this.supportsRowLayout()) return map;
33
+ const rows = this.widget.rows || [];
34
+ rows.forEach((row, rowIdx) => {
35
+ if (this.anchorHiddenRowId && row.id === this.anchorHiddenRowId) {
36
+ map[rowIdx] = true;
37
+ return;
38
+ }
39
+ const fields = [];
40
+ (row.cols || []).forEach((cell) => {
41
+ if (cell.merged) return;
42
+ this.collectFieldsDeep(cell.widgetList || [], fields);
43
+ });
44
+ if (!fields.length) {
45
+ map[rowIdx] = false;
46
+ return;
47
+ }
48
+ map[rowIdx] = fields.every((w) => !!w.options.hidden);
49
+ });
50
+ return map;
51
+ },
52
+ },
53
+ methods: {
54
+ // 容器是否为行布局(table/h5-table)
55
+ supportsRowLayout() {
56
+ return Array.isArray(this.widget && this.widget.rows);
57
+ },
58
+
59
+ isRowVisible(rowIdx) {
60
+ return !this.rowHiddenMap[rowIdx];
61
+ },
62
+
63
+ /* ------------------- 后台字段定义模式(行布局容器) ------------------- */
64
+ initDynamicSchema() {
65
+ if (this.designState || !this.widget) return;
66
+ const options = this.widget.options || {};
67
+ if (!options.dynamicSchemaEnabled || !this.supportsRowLayout()) return;
68
+ this.$nextTick(() => {
69
+ this.loadDynamicSchema();
70
+ });
71
+ },
72
+
73
+ loadDynamicSchema() {
74
+ const options = this.widget.options;
75
+ if (
76
+ this.designState
77
+ || !options.dynamicSchemaEnabled
78
+ || !options.dynamicSchemaScriptCode
79
+ || !this.supportsRowLayout()
80
+ ) {
81
+ return Promise.resolve();
82
+ }
83
+ const extra = this.handleCustomEvent(options.dynamicSchemaScriptParam) || {};
84
+ return new Promise((resolve) => {
85
+ this.formHttp({
86
+ scriptCode: options.dynamicSchemaScriptCode,
87
+ isLoading: false,
88
+ data: {
89
+ formData: this.formModel,
90
+ ...extra,
91
+ },
92
+ success: (res) => {
93
+ const defs = this.resolveSchemaDefs(res);
94
+ this.buildDynamicRows(defs);
95
+ this.$nextTick(() => resolve());
96
+ },
97
+ fail: () => resolve(),
98
+ error: () => resolve(),
99
+ });
100
+ });
101
+ },
102
+
103
+ resolveSchemaDefs(res) {
104
+ const options = this.widget.options;
105
+ let defs = [];
106
+ if (options.dynamicSchemaConvert) {
107
+ const result = this.handleCustomEvent(
108
+ options.dynamicSchemaConvert,
109
+ ["res"],
110
+ [res]
111
+ );
112
+ if (Array.isArray(result)) {
113
+ defs = result;
114
+ } else if (result && Array.isArray(result.fields)) {
115
+ defs = result.fields;
116
+ }
117
+ } else {
118
+ const objx = res && res.objx;
119
+ if (Array.isArray(objx)) {
120
+ defs = objx;
121
+ } else if (objx && Array.isArray(objx.fields)) {
122
+ defs = objx.fields;
123
+ } else if (objx && Array.isArray(objx.columns)) {
124
+ defs = objx.columns;
125
+ }
126
+ }
127
+ return defs || [];
128
+ },
129
+
130
+ buildDynamicRows(defs) {
131
+ const options = this.widget.options;
132
+ const formRef = this.getFormRef ? this.getFormRef() : null;
133
+ if (!formRef || !Array.isArray(defs) || !Array.isArray(this.widget.rows)) {
134
+ return;
135
+ }
136
+
137
+ this.removeGeneratedRows();
138
+ this.anchorHiddenRowId = null;
139
+
140
+ const widgets = [];
141
+ defs.forEach((def) => {
142
+ const w = this.createFieldWidgetFromDef(def, formRef);
143
+ if (w) widgets.push({ widget: w, colspan: def.colspan || 1 });
144
+ });
145
+
146
+ const mode = options.dynamicSchemaMode || "append";
147
+ let anchorIdx = -1;
148
+ let cellTemplates = null;
149
+ if (mode === "anchor" && options.dynamicSchemaAnchorRowId) {
150
+ anchorIdx = (this.widget.rows || []).findIndex(
151
+ (r) => r.id === options.dynamicSchemaAnchorRowId
152
+ );
153
+ if (anchorIdx > -1 && options.dynamicSchemaInheritLayout) {
154
+ const anchorRow = this.widget.rows[anchorIdx];
155
+ cellTemplates = (anchorRow.cols || []).filter((c) => !c.merged);
156
+ if (!cellTemplates.length) cellTemplates = null;
157
+ }
158
+ }
159
+
160
+ const rows = cellTemplates
161
+ ? this.layoutByTemplate(widgets, cellTemplates)
162
+ : this.layoutByColumns(
163
+ widgets,
164
+ Math.max(1, parseInt(options.dynamicSchemaColumns, 10) || 4)
165
+ );
166
+
167
+ if (mode === "replace") {
168
+ this.widget.rows.splice(0, this.widget.rows.length, ...rows);
169
+ } else if (mode === "anchor" && anchorIdx > -1) {
170
+ const position = options.dynamicSchemaAnchorPosition || "replace";
171
+ if (position === "before") {
172
+ this.widget.rows.splice(anchorIdx, 0, ...rows);
173
+ } else if (position === "after") {
174
+ this.widget.rows.splice(anchorIdx + 1, 0, ...rows);
175
+ } else {
176
+ this.anchorHiddenRowId = this.widget.rows[anchorIdx].id;
177
+ this.widget.rows.splice(anchorIdx + 1, 0, ...rows);
178
+ }
179
+ } else {
180
+ this.widget.rows.push(...rows);
181
+ }
182
+ this._generatedRowIds = rows.map((r) => r.id);
183
+ },
184
+
185
+ layoutByColumns(widgets, colCount) {
186
+ const rows = [];
187
+ let current = null;
188
+ let used = 0;
189
+ widgets.forEach((item) => {
190
+ const span = Math.min(colCount, item.colspan || 1);
191
+ if (!current || used + span > colCount) {
192
+ current = {
193
+ id: "table-row-dyn-" + generateId(),
194
+ merged: false,
195
+ cols: [],
196
+ };
197
+ rows.push(current);
198
+ used = 0;
199
+ }
200
+ current.cols.push(this.buildDynCell(item.widget, { colspan: span }));
201
+ used += span;
202
+ });
203
+ return rows;
204
+ },
205
+
206
+ layoutByTemplate(widgets, cellTemplates) {
207
+ const rows = [];
208
+ const perRow = cellTemplates.length;
209
+ for (let i = 0; i < widgets.length; i += perRow) {
210
+ const row = {
211
+ id: "table-row-dyn-" + generateId(),
212
+ merged: false,
213
+ cols: [],
214
+ };
215
+ for (let j = 0; j < perRow; j++) {
216
+ const tpl = cellTemplates[j];
217
+ const item = widgets[i + j];
218
+ row.cols.push(
219
+ this.buildDynCell(item ? item.widget : null, {
220
+ colspan: (tpl.options && tpl.options.colspan) || 1,
221
+ cellWidth: (tpl.options && tpl.options.cellWidth) || "",
222
+ cellHeight: (tpl.options && tpl.options.cellHeight) || "",
223
+ customClass: (tpl.options && tpl.options.customClass) || "",
224
+ })
225
+ );
226
+ }
227
+ rows.push(row);
228
+ }
229
+ return rows;
230
+ },
231
+
232
+ buildDynCell(fieldWidget, opt) {
233
+ const cellId = "table-cell-dyn-" + generateId();
234
+ return {
235
+ id: cellId,
236
+ type: "table-cell",
237
+ category: "container",
238
+ internal: true,
239
+ merged: false,
240
+ widgetList: fieldWidget ? [fieldWidget] : [],
241
+ options: {
242
+ name: cellId,
243
+ cellWidth: opt.cellWidth || "",
244
+ cellHeight: opt.cellHeight || "",
245
+ colspan: opt.colspan || 1,
246
+ rowspan: 1,
247
+ customClass: opt.customClass || "",
248
+ },
249
+ };
250
+ },
251
+
252
+ createFieldWidgetFromDef(def, formRef) {
253
+ if (!def) return null;
254
+ const type = def.type || "input";
255
+ const template = formRef.getFieldWidgetByType(type);
256
+ if (!template) return null;
257
+ const key = def.keyName || def.field || def.name;
258
+ if (!key) return null;
259
+ const widget = formRef.copyNewFieldWidget(template);
260
+ widget.options.name = key;
261
+ widget.options.keyName = key;
262
+ widget.options.keyNameEnabled = true;
263
+ if (def.label !== undefined) widget.options.label = def.label;
264
+ if (def.required !== undefined) widget.options.required = !!def.required;
265
+ if (def.readonly !== undefined) widget.options.readonly = !!def.readonly;
266
+ if (def.disabled !== undefined) widget.options.disabled = !!def.disabled;
267
+ if (def.hidden !== undefined) widget.options.hidden = !!def.hidden;
268
+ if (def.defaultValue !== undefined) {
269
+ widget.options.defaultValue = def.defaultValue;
270
+ }
271
+ if (def.placeholder !== undefined && "placeholder" in widget.options) {
272
+ widget.options.placeholder = def.placeholder;
273
+ }
274
+ if (Array.isArray(def.optionItems) && "optionItems" in widget.options) {
275
+ widget.options.optionItems = def.optionItems;
276
+ }
277
+ if (def.options && typeof def.options === "object") {
278
+ Object.keys(def.options).forEach((k) => {
279
+ widget.options[k] = def.options[k];
280
+ });
281
+ }
282
+ return widget;
283
+ },
284
+
285
+ removeGeneratedRows() {
286
+ if (!this._generatedRowIds || !this._generatedRowIds.length) return;
287
+ if (!Array.isArray(this.widget.rows)) {
288
+ this._generatedRowIds = [];
289
+ return;
290
+ }
291
+ const ids = this._generatedRowIds;
292
+ for (let i = this.widget.rows.length - 1; i >= 0; i--) {
293
+ if (ids.indexOf(this.widget.rows[i].id) > -1) {
294
+ this.widget.rows.splice(i, 1);
295
+ }
296
+ }
297
+ this._generatedRowIds = [];
298
+ },
299
+
300
+ collectFieldsDeep(list, out) {
301
+ (list || []).forEach((w) => {
302
+ if (!w) return;
303
+ if (w.category === "container") {
304
+ if (Array.isArray(w.rows)) {
305
+ w.rows.forEach((row) =>
306
+ this.collectFieldsDeep((row && row.cols) || [], out)
307
+ );
308
+ }
309
+ ["cols", "tabs", "widgetList", "panes", "buttonWidgetList"].forEach(
310
+ (k) => {
311
+ if (Array.isArray(w[k])) this.collectFieldsDeep(w[k], out);
312
+ }
313
+ );
314
+ } else if (w.formItemFlag) {
315
+ out.push(w);
316
+ }
317
+ });
318
+ },
319
+ },
320
+ };
321
+
322
+ export default dynamicFieldMixin;
@@ -6,7 +6,7 @@
6
6
  <table :ref="widget.id" class="table-layout table-d-box" :class="[customClass,widget.options.styleTableClass]"
7
7
  :style="tableStyle">
8
8
  <tbody>
9
- <tr v-for="(row, rowIdx) in widget.rows" :key="row.id">
9
+ <tr v-for="(row, rowIdx) in widget.rows" :key="row.id" v-show="isRowVisible(rowIdx)">
10
10
  <template v-for="(colWidget, colIdx) in row.cols">
11
11
  <table-cell-item v-if="!colWidget.merged" :widget="colWidget" :key="colIdx" :parent-list="widget.cols"
12
12
  :row-index="rowIdx" :col-index="colIdx" :parent-widget="widget" :tableParam="tableParam"
@@ -32,11 +32,12 @@ import refMixin from "../../../../components/xform/form-render/refMixin"
32
32
  import ContainerItemWrapper from './container-item-wrapper'
33
33
  import TableCellItem from './table-cell-item'
34
34
  import containerItemMixin from "./containerItemMixin";
35
+ import dynamicFieldMixin from "./dynamicFieldMixin";
35
36
 
36
37
  export default {
37
38
  name: "table-item",
38
39
  componentName: 'ContainerItem',
39
- mixins: [emitter, i18n, refMixin, containerItemMixin],
40
+ mixins: [emitter, i18n, refMixin, containerItemMixin, dynamicFieldMixin],
40
41
  components: {
41
42
  ContainerItemWrapper,
42
43
  TableCellItem,
@@ -52,9 +53,6 @@ export default {
52
53
  },
53
54
  created() {
54
55
  this.initRefList()
55
- },
56
- mounted() {
57
-
58
56
  },
59
57
  beforeDestroy() {
60
58
  this.unregisterFromRefList()
@@ -0,0 +1,131 @@
1
+ /**
2
+ * 表单级「动态字段规则」运行时混入(挂载于 VFormRender 根组件)。
3
+ *
4
+ * 配置来源:formConfig.dynamicFieldEnabled / dynamicFieldSourceType / dynamicFieldRules /
5
+ * dynamicFieldScriptCode / dynamicFieldScriptParam(额外参数) / dynamicFieldTriggerFields
6
+ * 作用范围:整个表单内任意字段 / 命名容器的 显隐/必填/只读/禁用/取值。
7
+ *
8
+ * 依赖根组件(indexMixin)已提供:formConfig、widgetList、formModel、getWidgetRef、formHttp。
9
+ */
10
+ import {
11
+ buildFormTargetMap,
12
+ computeLocalStates,
13
+ applyTargetState,
14
+ } from "./container-item/dynamicFieldEngine";
15
+
16
+ export default {
17
+ created() {
18
+ // 目标key -> { widget, refKey, isContainer }
19
+ this._fdfMap = {};
20
+ // 设计期基线,未命中规则时还原
21
+ this._fdfBaseline = {};
22
+ this._fdfWatcher = null;
23
+ this._fdfTimer = null;
24
+ this._fdfInited = false;
25
+ },
26
+ watch: {
27
+ showFormContent(val) {
28
+ // 表单内容渲染(字段ref注册)完成后再初始化
29
+ if (val && !this._fdfInited) {
30
+ this.$nextTick(() => this.initFormDynamicFields());
31
+ }
32
+ },
33
+ },
34
+ beforeDestroy() {
35
+ if (this._fdfWatcher) {
36
+ this._fdfWatcher();
37
+ this._fdfWatcher = null;
38
+ }
39
+ if (this._fdfTimer) {
40
+ clearTimeout(this._fdfTimer);
41
+ this._fdfTimer = null;
42
+ }
43
+ },
44
+ methods: {
45
+ initFormDynamicFields() {
46
+ const cfg = this.formConfig || {};
47
+ if (!cfg.dynamicFieldEnabled) return;
48
+ this._fdfInited = true;
49
+ const { map, baseline } = buildFormTargetMap(this.widgetList);
50
+ this._fdfMap = map;
51
+ this._fdfBaseline = baseline;
52
+ this.applyFormDynamicFields();
53
+ this.setupFormDynamicWatcher();
54
+ },
55
+
56
+ applyFormDynamicFields() {
57
+ const cfg = this.formConfig || {};
58
+ if (!cfg.dynamicFieldEnabled) return;
59
+ if (cfg.dynamicFieldSourceType === "script") {
60
+ this.applyFormScriptStates();
61
+ } else {
62
+ const states = computeLocalStates(
63
+ cfg.dynamicFieldRules || [],
64
+ this._fdfBaseline,
65
+ this.formModel
66
+ );
67
+ Object.keys(states).forEach((key) =>
68
+ this.applyFormFieldState(key, states[key])
69
+ );
70
+ }
71
+ },
72
+
73
+ applyFormFieldState(targetKey, state) {
74
+ const entry = this._fdfMap[targetKey];
75
+ if (!entry) return;
76
+ applyTargetState(entry, state, (name) => this.getWidgetRef(name));
77
+ },
78
+
79
+ setupFormDynamicWatcher() {
80
+ const cfg = this.formConfig || {};
81
+ if (cfg.dynamicFieldSourceType === "script") {
82
+ const triggers = cfg.dynamicFieldTriggerFields || [];
83
+ if (!triggers.length) return;
84
+ this._fdfWatcher = this.$watch(
85
+ () =>
86
+ triggers
87
+ .map((f) => this.formModel && this.formModel[f])
88
+ .join("\u0001"),
89
+ () => this.debouncedFormApply()
90
+ );
91
+ } else {
92
+ this._fdfWatcher = this.$watch(
93
+ () => this.formModel,
94
+ () => this.debouncedFormApply(),
95
+ { deep: true }
96
+ );
97
+ }
98
+ },
99
+
100
+ debouncedFormApply() {
101
+ if (this._fdfTimer) clearTimeout(this._fdfTimer);
102
+ this._fdfTimer = setTimeout(() => this.applyFormDynamicFields(), 120);
103
+ },
104
+
105
+ applyFormScriptStates() {
106
+ const cfg = this.formConfig || {};
107
+ if (!cfg.dynamicFieldScriptCode) return;
108
+ const extra = this.handleCustomEvent(cfg.dynamicFieldScriptParam) || {};
109
+ this.formHttp({
110
+ scriptCode: cfg.dynamicFieldScriptCode,
111
+ isLoading: false,
112
+ data: {
113
+ formData: this.formModel,
114
+ ...extra,
115
+ },
116
+ success: (res) => {
117
+ const result = (res && res.objx) || {};
118
+ const fieldStates = result.fieldStates || {};
119
+ Object.keys(this._fdfBaseline).forEach((key) => {
120
+ if (!fieldStates[key]) {
121
+ this.applyFormFieldState(key, this._fdfBaseline[key]);
122
+ }
123
+ });
124
+ Object.keys(fieldStates).forEach((key) =>
125
+ this.applyFormFieldState(key, fieldStates[key])
126
+ );
127
+ },
128
+ });
129
+ },
130
+ },
131
+ };
@@ -113,6 +113,7 @@
113
113
  import "./container-item/index";
114
114
  import FieldComponents from "../../../components/xform/form-designer/form-widget/field-widget/index";
115
115
  import indexMixin from "../../../components/xform/form-render/indexMixin";
116
+ import formDynamicFieldMixin from "../../../components/xform/form-render/formDynamicFieldMixin";
116
117
 
117
118
  export default {
118
119
  name: "VFormRender",
@@ -135,7 +136,7 @@ export default {
135
136
  "../../../components/xform/form-designer/form-widget/dialog/fileReferenceDialog.vue"
136
137
  )
137
138
  },
138
- mixins: [indexMixin],
139
+ mixins: [indexMixin, formDynamicFieldMixin],
139
140
  };
140
141
  </script>
141
142
 
@@ -1,4 +1,6 @@
1
1
  import moment from "moment";
2
+ import { saveUserLog } from "@base/api/user";
3
+ import settingConfig from "@/settings";
2
4
 
3
5
  let modules = {};
4
6
  modules = {
@@ -81,6 +83,32 @@ modules = {
81
83
  };
82
84
  return reqData;
83
85
  },
86
+ onAfterSaveHandle(reqData, res, response) {
87
+ //保存成功后处理
88
+ this.writeSaveLog(reqData, res, response); //写入保存日志
89
+ },
90
+ writeSaveLog(reqData, res, response) {
91
+ //写入保存日志
92
+ if (settingConfig.formSaveLogEnabled && res.type === "success") {
93
+ let responseConfig = response.config || {};
94
+ let formRef = this.getFormRef ? this.getFormRef() : this;
95
+ let reportTemplate = formRef ? formRef.reportTemplate : null;
96
+ if (!reportTemplate) return;
97
+ let isAdd = !formRef.dataId;
98
+ let formName = reportTemplate?.formName;
99
+ let formCode = reportTemplate?.formCode;
100
+ let action = isAdd ? "新增成功" : "更新成功";
101
+ let content = `${formName}(${formCode}),${action}`;
102
+ let path = responseConfig.url.replace(responseConfig.baseURL, "");
103
+ saveUserLog({
104
+ data: {
105
+ path: path,
106
+ businessCode: content,
107
+ content: content,
108
+ },
109
+ });
110
+ }
111
+ },
84
112
  saveDefaultHandle(option) {
85
113
  let formRef = this.getFormRef ? this.getFormRef() : this;
86
114
  let formConfig = formRef.formConfig;
@@ -98,6 +126,10 @@ modules = {
98
126
  formRef.validate((valid) => {
99
127
  if (valid) {
100
128
  let reqData = this.getReqFormData();
129
+ let callback = (res, response) => {
130
+ this.onAfterSaveHandle(reqData, res, response); //保存成功后处理
131
+ option?.callback && option.callback(res); //回调
132
+ };
101
133
  this.formHttp({
102
134
  // url: "/" + reportTemplate.serviceName + "/form_ins/saveUpdate",
103
135
  scriptCode: scriptCode,
@@ -119,6 +151,7 @@ modules = {
119
151
  });
120
152
  },
121
153
  ...config,
154
+ callback: callback,
122
155
  });
123
156
  }
124
157
  });
@@ -880,6 +880,13 @@ export function getDefaultFormConfig() {
880
880
  otherTabList: [],
881
881
  customListTabLabel: null,
882
882
  globalConfig: null,
883
+ // 表单级动态字段规则:按本地规则/后台脚本动态控制整个表单内任意字段/容器的显隐/必填/只读/禁用/取值
884
+ dynamicFieldEnabled: false,
885
+ dynamicFieldSourceType: "local",
886
+ dynamicFieldRules: [],
887
+ dynamicFieldScriptCode: null,
888
+ dynamicFieldScriptParam: null,
889
+ dynamicFieldTriggerFields: [],
883
890
  };
884
891
  }
885
892