@tmagic/form 1.8.0-beta.11 → 1.8.0-beta.12

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.
@@ -1,8 +1,8 @@
1
- import { FORM_DIFF_CONFIG_KEY } from "./schema.js";
1
+ import { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from "./schema.js";
2
2
  import { getConfig } from "./utils/config.js";
3
3
  import { initValue } from "./utils/form.js";
4
4
  import Container_default from "./containers/Container.js";
5
- import { Fragment, createBlock, createCommentVNode, createElementBlock, createSlots, defineComponent, mergeProps, normalizeStyle, openBlock, provide, reactive, ref, renderList, renderSlot, shallowRef, toRaw, unref, useTemplateRef, watch, watchEffect, withCtx } from "vue";
5
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createSlots, defineComponent, mergeProps, nextTick, normalizeStyle, openBlock, provide, reactive, ref, renderList, renderSlot, shallowRef, toRaw, unref, useTemplateRef, watch, watchEffect, withCtx } from "vue";
6
6
  import { cloneDeep, isEqual } from "lodash-es";
7
7
  import { TMagicForm, tMagicMessage, tMagicMessageBox } from "@tmagic/design";
8
8
  import { setValueByKeyPath } from "@tmagic/utils";
@@ -20,6 +20,7 @@ var Form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
20
20
  },
21
21
  parentValues: { default: () => ({}) },
22
22
  labelWidth: { default: "200px" },
23
+ typeMatchValid: { type: Boolean },
23
24
  disabled: {
24
25
  type: Boolean,
25
26
  default: false
@@ -53,6 +54,7 @@ var Form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
53
54
  setup(__props, { expose: __expose, emit: __emit }) {
54
55
  const props = __props;
55
56
  const emit = __emit;
57
+ provide(FORM_TYPE_MATCH_VALID_KEY, computed(() => props.typeMatchValid));
56
58
  const tMagicFormRef = useTemplateRef("tMagicForm");
57
59
  const initialized = ref(false);
58
60
  const values = ref({});
@@ -175,6 +177,9 @@ var Form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
175
177
  }).then((value) => {
176
178
  values.value = value;
177
179
  initialized.value = !props.isCompare;
180
+ nextTick(() => {
181
+ tMagicFormRef.value?.validate();
182
+ });
178
183
  });
179
184
  if (props.isCompare) initValue(formState, {
180
185
  initValues: props.lastValues,
@@ -226,6 +231,23 @@ var Form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
226
231
  };
227
232
  return findInConfig(config, nameParts);
228
233
  };
234
+ /**
235
+ * 将校验返回的 invalidFields 汇总为可读的错误文案(多条以 `<br>` 拼接)。
236
+ *
237
+ * 抽离为独立方法,供 `submitForm`(提交校验)与 `validate`(返回错误文案的校验)复用,
238
+ * 保证两种校验入口产出的错误文案格式完全一致。
239
+ */
240
+ const formatValidateError = (invalidFields) => {
241
+ const error = [];
242
+ Object.entries(invalidFields).forEach(([prop, validateError]) => {
243
+ validateError.forEach(({ field, message }) => {
244
+ const name = field || prop;
245
+ const text = (props.useFieldTextInError ? getTextByName(name, props.config) : void 0) || name;
246
+ error.push(`${text} -> ${message}`);
247
+ });
248
+ });
249
+ return error.join("<br>");
250
+ };
229
251
  __expose({
230
252
  values,
231
253
  lastValuesProcessed,
@@ -245,15 +267,32 @@ var Form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
245
267
  return native ? values.value : cloneDeep(toRaw(values.value));
246
268
  } catch (invalidFields) {
247
269
  emit("error", invalidFields);
248
- const error = [];
249
- Object.entries(invalidFields).forEach(([prop, ValidateError]) => {
250
- ValidateError.forEach(({ field, message }) => {
251
- const name = field || prop;
252
- const text = (props.useFieldTextInError ? getTextByName(name, props.config) : void 0) || name;
253
- error.push(`${text} -> ${message}`);
254
- });
255
- });
256
- throw new Error(error.join("<br>"));
270
+ throw new Error(formatValidateError(invalidFields));
271
+ }
272
+ },
273
+ /**
274
+ * 校验:对表单当前值执行校验,返回汇总后的错误文案。
275
+ *
276
+ * 与 `submitForm` 的区别:
277
+ * - 校验失败时不抛异常、不触发 `error` 事件,而是以返回值形式给出错误文案;
278
+ * - 不重置 `changeRecords`,不改变提交语义,仅用于「探测」当前配置是否合法。
279
+ *
280
+ * 注意:本方法只改变「校验结果的返回方式」,并不负责「不污染页面表单状态」——
281
+ * 若需对一份独立的「配置 + 值」做完全不影响页面上已渲染表单的校验,请使用 `validateForm`
282
+ * (内部会新建一个隐藏的 MForm 实例,通过 `initValues` 传入待校验值,用完即卸载)。
283
+ *
284
+ * 典型用途:作为 `validateForm` 内部复用的校验实现;也可在已渲染的表单实例上主动调用,
285
+ * 根据返回的错误文案自行决定后续处理(如记录节点错误状态)。
286
+ *
287
+ * @returns 校验通过返回空字符串 `''`,否则返回以 `<br>` 拼接的错误文案。
288
+ */
289
+ validate: async () => {
290
+ try {
291
+ const result = await tMagicFormRef.value?.validate();
292
+ if (result !== true) throw result;
293
+ return "";
294
+ } catch (invalidFields) {
295
+ return formatValidateError(invalidFields);
257
296
  }
258
297
  },
259
298
  getTextByName
@@ -13,6 +13,7 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCo
13
13
  width: {},
14
14
  height: {},
15
15
  labelWidth: {},
16
+ typeMatchValid: { type: Boolean },
16
17
  disabled: { type: Boolean },
17
18
  size: {},
18
19
  confirmText: { default: "确定" },
@@ -49,7 +50,8 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCo
49
50
  const submitHandler = async () => {
50
51
  try {
51
52
  const changeRecords = form.value?.changeRecords;
52
- emit("submit", await form.value?.submitForm(), { changeRecords });
53
+ const values = await form.value?.submitForm();
54
+ emit("submit", values, { changeRecords });
53
55
  } catch (e) {
54
56
  emit("error", e);
55
57
  }
@@ -87,6 +89,7 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCo
87
89
  "prevent-submit-default": __props.preventSubmitDefault,
88
90
  "use-field-text-in-error": __props.useFieldTextInError,
89
91
  "extend-state": __props.extendState,
92
+ "type-match-valid": __props.typeMatchValid,
90
93
  onChange: changeHandler
91
94
  }, null, 8, [
92
95
  "size",
@@ -99,7 +102,8 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCo
99
102
  "inline",
100
103
  "prevent-submit-default",
101
104
  "use-field-text-in-error",
102
- "extend-state"
105
+ "extend-state",
106
+ "type-match-valid"
103
107
  ]), renderSlot(_ctx.$slots, "default")]),
104
108
  _: 3
105
109
  })], 4), createElementVNode("div", {
@@ -12,6 +12,7 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
12
12
  parentValues: {},
13
13
  width: {},
14
14
  labelWidth: {},
15
+ typeMatchValid: { type: Boolean },
15
16
  fullscreen: { type: Boolean },
16
17
  disabled: { type: Boolean },
17
18
  title: {},
@@ -78,7 +79,8 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
78
79
  const save = async () => {
79
80
  try {
80
81
  const changeRecords = [...form.value?.changeRecords || []];
81
- emit("submit", await form.value?.submitForm(), { changeRecords });
82
+ const values = await form.value?.submitForm();
83
+ emit("submit", values, { changeRecords });
82
84
  } catch (e) {
83
85
  emit("error", e);
84
86
  }
@@ -196,6 +198,7 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
196
198
  inline: __props.inline,
197
199
  "prevent-submit-default": __props.preventSubmitDefault,
198
200
  "use-field-text-in-error": __props.useFieldTextInError,
201
+ "type-match-valid": __props.typeMatchValid,
199
202
  "extend-state": __props.extendState,
200
203
  onChange: changeHandler
201
204
  }, null, 8, [
@@ -210,6 +213,7 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
210
213
  "inline",
211
214
  "prevent-submit-default",
212
215
  "use-field-text-in-error",
216
+ "type-match-valid",
213
217
  "extend-state"
214
218
  ]), renderSlot(_ctx.$slots, "default")], 4)) : createCommentVNode("v-if", true)]),
215
219
  _: 3
@@ -12,6 +12,7 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
12
12
  parentValues: {},
13
13
  width: {},
14
14
  labelWidth: {},
15
+ typeMatchValid: { type: Boolean },
15
16
  disabled: { type: Boolean },
16
17
  closeOnPressEscape: {
17
18
  type: Boolean,
@@ -54,7 +55,8 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
54
55
  const submitHandler = async () => {
55
56
  try {
56
57
  const changeRecords = form.value?.changeRecords;
57
- emit("submit", await form.value?.submitForm(), { changeRecords });
58
+ const values = await form.value?.submitForm();
59
+ emit("submit", values, { changeRecords });
58
60
  } catch (e) {
59
61
  emit("error", e);
60
62
  }
@@ -154,6 +156,7 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
154
156
  inline: __props.inline,
155
157
  "prevent-submit-default": __props.preventSubmitDefault,
156
158
  "use-field-text-in-error": __props.useFieldTextInError,
159
+ "type-match-valid": __props.typeMatchValid,
157
160
  "extend-state": __props.extendState,
158
161
  onChange: changeHandler
159
162
  }, null, 8, [
@@ -167,6 +170,7 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
167
170
  "inline",
168
171
  "prevent-submit-default",
169
172
  "use-field-text-in-error",
173
+ "type-match-valid",
170
174
  "extend-state"
171
175
  ]), renderSlot(_ctx.$slots, "default")], 512)) : createCommentVNode("v-if", true)]),
172
176
  _: 3
@@ -1,4 +1,4 @@
1
- import { FORM_DIFF_CONFIG_KEY } from "../schema.js";
1
+ import { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from "../schema.js";
2
2
  import Hidden_default from "../fields/Hidden.js";
3
3
  import { getField } from "../utils/config.js";
4
4
  import { createObjectProp, display, filterFunction, getRules } from "../utils/form.js";
@@ -48,6 +48,7 @@ var Container_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
48
48
  const emit = __emit;
49
49
  const mForm = inject("mForm");
50
50
  const diffConfig = inject(FORM_DIFF_CONFIG_KEY, {});
51
+ const typeMatchValid = inject(FORM_TYPE_MATCH_VALID_KEY);
51
52
  const expand = ref(false);
52
53
  const name = computed(() => props.config.name || "");
53
54
  const showDiff = computed(() => {
@@ -124,7 +125,7 @@ var Container_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
124
125
  placement: config?.placement || "top"
125
126
  };
126
127
  });
127
- const rule = computed(() => getRules(mForm, props.config.rules, props));
128
+ const rule = computed(() => getRules(mForm, props.config.rules, props, typeMatchValid));
128
129
  const display$1 = computed(() => {
129
130
  const value = display(mForm, props.config.display, props);
130
131
  if (value === "expand") return expand.value;
@@ -185,7 +186,10 @@ var Container_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ define
185
186
  return new Proxy(target, {
186
187
  get: (obj, key) => {
187
188
  const value = obj[key];
188
- if (value && typeof value === "object") return createModelProxy(value, setModelFn, pathPrefix ? `${pathPrefix}.${key}` : key);
189
+ if (value && typeof value === "object") {
190
+ const newPath = pathPrefix ? `${pathPrefix}.${key}` : key;
191
+ return createModelProxy(value, setModelFn, newPath);
192
+ }
189
193
  return value;
190
194
  },
191
195
  set: (obj, key, value) => {
@@ -112,7 +112,7 @@ var Table_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
112
112
  }, 8, ["disabled"]),
113
113
  renderSlot(_ctx.$slots, "default"),
114
114
  createElementVNode("div", _hoisted_2, [createElementVNode("div", _hoisted_3, [
115
- !unref(isFullscreen) ? renderSlot(_ctx.$slots, "toggle-button", { key: 0 }) : createCommentVNode("v-if", true),
115
+ !unref(isFullscreen) ? renderSlot(_ctx.$slots, "toggle-button", {}, void 0, void 0, 0) : createCommentVNode("v-if", true),
116
116
  __props.config.enableFullscreen !== false ? (openBlock(), createBlock(unref(TMagicButton), {
117
117
  key: 1,
118
118
  icon: unref(FullScreen),
@@ -153,7 +153,7 @@ var Table_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComp
153
153
  default: withCtx(() => [..._cache[1] || (_cache[1] = [createTextVNode("清空", -1)])]),
154
154
  _: 1
155
155
  }, 8, ["disabled", "onClick"])) : createCommentVNode("v-if", true)
156
- ]), !__props.isCompare ? renderSlot(_ctx.$slots, "add-button", { key: 0 }) : createCommentVNode("v-if", true)]),
156
+ ]), !__props.isCompare ? renderSlot(_ctx.$slots, "add-button", {}, void 0, void 0, 0) : createCommentVNode("v-if", true)]),
157
157
  __props.config.pagination ? (openBlock(), createElementBlock("div", _hoisted_4, [createVNode(unref(TMagicPagination), {
158
158
  layout: "total, sizes, prev, pager, next, jumper",
159
159
  "hide-on-single-page": __props.model[modelName.value].length < unref(pageSize),
@@ -24,6 +24,7 @@ var RadioGroup_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
24
24
  const itemComponent = computed(() => props.config.childType === "button" ? TMagicRadioButton : TMagicRadio);
25
25
  const emit = __emit;
26
26
  const clickHandler = (item) => {
27
+ if (props.disabled) return;
27
28
  emit("change", props.model[props.name] === item ? "" : item);
28
29
  };
29
30
  useAddField(props.prop);
@@ -227,7 +227,8 @@ var Select_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
227
227
  const v = props.model[props.name];
228
228
  if (Array.isArray(v) ? !v.length : typeof v === "undefined") return;
229
229
  if (typeof v === "undefined" || hasOption(v)) return;
230
- setOptions(await getInitOption());
230
+ const data = await getInitOption();
231
+ setOptions(data);
231
232
  };
232
233
  watch(() => props.model?.[props.name], () => {
233
234
  initOption();
package/dist/es/index.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import "./_virtual/_rolldown/runtime.js";
2
- import { FORM_DIFF_CONFIG_KEY } from "./schema.js";
2
+ import { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from "./schema.js";
3
3
  import { useAddField } from "./utils/useAddField.js";
4
4
  import Hidden_default from "./fields/Hidden.js";
5
5
  import { deleteField, getField, registerField } from "./utils/config.js";
6
- import { createObjectProp, createValues, datetimeFormatter, display, filterFunction, getDataByPage, getRules, initValue, sortArray, sortChange } from "./utils/form.js";
6
+ import { clearTypeMatchRules, deleteTypeMatchRule, getTypeMatchRule, registerTypeMatchRule, registerTypeMatchRules, validateTypeMatch } from "./utils/typeMatch.js";
7
+ import { adaptFormValidator, createObjectProp, createValues, datetimeFormatter, display, filterFunction, getDataByPage, getRules, initValue, sortArray, sortChange } from "./utils/form.js";
7
8
  import Container_default from "./containers/Container.js";
8
9
  import Form_default from "./Form.js";
9
- import { submitForm } from "./submitForm.js";
10
+ import { submitForm, validateForm } from "./submitForm.js";
10
11
  import FormDialog_default from "./FormDialog.js";
11
12
  import FormDrawer_default from "./FormDrawer.js";
12
13
  import FormBox_default from "./FormBox.js";
@@ -39,4 +40,4 @@ import plugin_default from "./plugin.js";
39
40
  export * from "@tmagic/form-schema";
40
41
  var createForm = (config) => config;
41
42
  //#endregion
42
- export { FORM_DIFF_CONFIG_KEY, Cascader_default as MCascader, Checkbox_default as MCheckbox, CheckboxGroup_default as MCheckboxGroup, ColorPicker_default as MColorPicker, Container_default as MContainer, Date_default as MDate, DateTime_default as MDateTime, Daterange_default as MDaterange, Display_default as MDisplay, DynamicField_default as MDynamicField, Fieldset_default as MFieldset, FlexLayout_default as MFlexLayout, Form_default as MForm, FormBox_default as MFormBox, FormDialog_default as MFormDialog, FormDrawer_default as MFormDrawer, TableGroupList_default as MGroupList, Hidden_default as MHidden, Link_default as MLink, Number_default as MNumber, NumberRange_default as MNumberRange, Panel_default as MPanel, RadioGroup_default as MRadioGroup, Row_default as MRow, Select_default as MSelect, Switch_default as MSwitch, TableGroupList_default as MTable, TableGroupList_default as MTableGroupList, Tabs_default as MTabs, Text_default as MText, Textarea_default as MTextarea, Time_default as MTime, Timerange_default as MTimerange, createForm, createObjectProp, createValues, datetimeFormatter, plugin_default as default, deleteField as deleteFormField, display, filterFunction, getDataByPage, getField as getFormField, getRules, initValue, registerField as registerFormField, sortArray, sortChange, submitForm, useAddField };
43
+ export { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY, Cascader_default as MCascader, Checkbox_default as MCheckbox, CheckboxGroup_default as MCheckboxGroup, ColorPicker_default as MColorPicker, Container_default as MContainer, Date_default as MDate, DateTime_default as MDateTime, Daterange_default as MDaterange, Display_default as MDisplay, DynamicField_default as MDynamicField, Fieldset_default as MFieldset, FlexLayout_default as MFlexLayout, Form_default as MForm, FormBox_default as MFormBox, FormDialog_default as MFormDialog, FormDrawer_default as MFormDrawer, TableGroupList_default as MGroupList, Hidden_default as MHidden, Link_default as MLink, Number_default as MNumber, NumberRange_default as MNumberRange, Panel_default as MPanel, RadioGroup_default as MRadioGroup, Row_default as MRow, Select_default as MSelect, Switch_default as MSwitch, TableGroupList_default as MTable, TableGroupList_default as MTableGroupList, Tabs_default as MTabs, Text_default as MText, Textarea_default as MTextarea, Time_default as MTime, Timerange_default as MTimerange, adaptFormValidator, clearTypeMatchRules, createForm, createObjectProp, createValues, datetimeFormatter, plugin_default as default, deleteField as deleteFormField, deleteTypeMatchRule, display, filterFunction, getDataByPage, getField as getFormField, getRules, getTypeMatchRule, initValue, registerField as registerFormField, registerTypeMatchRule, registerTypeMatchRules, sortArray, sortChange, submitForm, useAddField, validateForm, validateTypeMatch };
package/dist/es/plugin.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import Hidden_default from "./fields/Hidden.js";
2
2
  import { setConfig } from "./utils/config.js";
3
+ import { registerTypeMatchRules } from "./utils/typeMatch.js";
3
4
  import Container_default from "./containers/Container.js";
4
5
  import Form_default from "./Form.js";
5
6
  import FormDialog_default from "./FormDialog.js";
@@ -36,6 +37,7 @@ var plugin_default = { install(app, opt = {}) {
36
37
  const option = Object.assign(defaultInstallOpt, opt);
37
38
  app.config.globalProperties.$MAGIC_FORM = option;
38
39
  setConfig(option);
40
+ if (option.typeMatchRules) registerTypeMatchRules(option.typeMatchRules);
39
41
  app.component("m-form", Form_default);
40
42
  app.component("m-form-dialog", FormDialog_default);
41
43
  app.component("m-form-container", Container_default);
package/dist/es/schema.js CHANGED
@@ -2,5 +2,6 @@ import "./_virtual/_rolldown/runtime.js";
2
2
  export * from "@tmagic/form-schema";
3
3
  //#region packages/form/src/schema.ts
4
4
  var FORM_DIFF_CONFIG_KEY = Symbol("mFormDiffConfig");
5
+ var FORM_TYPE_MATCH_VALID_KEY = Symbol("mFormTypeMatchValid");
5
6
  //#endregion
6
- export { FORM_DIFF_CONFIG_KEY };
7
+ export { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY };
package/dist/es/style.css CHANGED
@@ -23,7 +23,7 @@
23
23
  .m-form-dialog .m-dialog-body {
24
24
  padding: 0 20px;
25
25
  }
26
- .m-form-dialog .el-table .m-form-item .el-form-item {
26
+ .m-form-dialog .el-table .m-form-item .el-form-item:not(.is-error) {
27
27
  margin-bottom: 0;
28
28
  }
29
29
 
@@ -247,6 +247,10 @@ fieldset.m-fieldset .m-form-tip {
247
247
  width: 95vw !important;
248
248
  }
249
249
 
250
+ .el-form-item .m-fields-table .el-form-item.is-error {
251
+ margin-bottom: 18px;
252
+ }
253
+
250
254
  .m-fields-table {
251
255
  width: 100%;
252
256
  }
@@ -269,7 +273,7 @@ fieldset.m-fieldset .m-form-tip {
269
273
  .m-fields-table .el-table__expanded-cell .m-form-tip {
270
274
  margin-left: 80px;
271
275
  }
272
- .m-fields-table .el-form-item {
276
+ .m-fields-table .el-form-item:not(.is-error) {
273
277
  margin-bottom: 0;
274
278
  }
275
279
  .m-fields-table .tmagic-form-table-drag-target {