@tmagic/form 1.8.0-manmanyu.9 → 1.8.1

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 (149) hide show
  1. package/dist/es/Form.vue_vue_type_script_setup_true_lang.js +88 -107
  2. package/dist/es/FormBox.vue_vue_type_script_setup_true_lang.js +21 -5
  3. package/dist/es/FormDialog.vue_vue_type_script_setup_true_lang.js +21 -5
  4. package/dist/es/FormDrawer.vue_vue_type_script_setup_true_lang.js +46 -12
  5. package/dist/es/containers/Col.vue_vue_type_script_setup_true_lang.js +6 -2
  6. package/dist/es/containers/Container.vue_vue_type_script_setup_true_lang.js +44 -47
  7. package/dist/es/containers/Fieldset.vue_vue_type_script_setup_true_lang.js +7 -1
  8. package/dist/es/containers/FlexLayout.vue_vue_type_script_setup_true_lang.js +5 -2
  9. package/dist/es/containers/FormLabel.vue_vue_type_script_setup_true_lang.js +1 -1
  10. package/dist/es/containers/GroupList.vue_vue_type_script_setup_true_lang.js +59 -22
  11. package/dist/es/containers/GroupListItem.vue_vue_type_script_setup_true_lang.js +118 -100
  12. package/dist/es/containers/Panel.vue_vue_type_script_setup_true_lang.js +8 -3
  13. package/dist/es/containers/Row.vue_vue_type_script_setup_true_lang.js +4 -1
  14. package/dist/es/containers/Step.vue_vue_type_script_setup_true_lang.js +5 -2
  15. package/dist/es/containers/Tabs.vue_vue_type_script_setup_true_lang.js +8 -1
  16. package/dist/es/containers/table/ActionsColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  17. package/dist/es/containers/table/SortColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  18. package/dist/es/containers/table/Table.vue_vue_type_script_setup_true_lang.js +14 -10
  19. package/dist/es/containers/table/useTableColumns.js +10 -15
  20. package/dist/es/containers/table-group-list/TableGroupList.vue_vue_type_script_setup_true_lang.js +72 -79
  21. package/dist/es/containers/table-group-list/useAdd.js +26 -16
  22. package/dist/es/containers/table-group-list/useScrollLastItemIntoView.js +72 -0
  23. package/dist/es/fields/Cascader.vue_vue_type_script_setup_true_lang.js +2 -2
  24. package/dist/es/fields/Checkbox.vue_vue_type_script_setup_true_lang.js +1 -1
  25. package/dist/es/fields/CheckboxGroup/Index.js +5 -0
  26. package/dist/es/fields/{CheckboxGroup.vue_vue_type_script_setup_true_lang.js → CheckboxGroup/Index.vue_vue_type_script_setup_true_lang.js} +6 -7
  27. package/dist/es/fields/CheckboxGroup/effect.js +8 -0
  28. package/dist/es/fields/ColorPicker.vue_vue_type_script_setup_true_lang.js +1 -1
  29. package/dist/es/fields/Date/Index.js +5 -0
  30. package/dist/es/fields/{Date.vue_vue_type_script_setup_true_lang.js → Date/Index.vue_vue_type_script_setup_true_lang.js} +5 -7
  31. package/dist/es/fields/Date/effect.js +10 -0
  32. package/dist/es/fields/DateTime/Index.js +5 -0
  33. package/dist/es/fields/{DateTime.vue_vue_type_script_setup_true_lang.js → DateTime/Index.vue_vue_type_script_setup_true_lang.js} +5 -9
  34. package/dist/es/fields/DateTime/effect.js +15 -0
  35. package/dist/es/fields/Daterange.vue_vue_type_script_setup_true_lang.js +2 -2
  36. package/dist/es/fields/Display/Index.js +5 -0
  37. package/dist/es/fields/{Display.vue_vue_type_script_setup_true_lang.js → Display/Index.vue_vue_type_script_setup_true_lang.js} +6 -7
  38. package/dist/es/fields/Display/effect.js +8 -0
  39. package/dist/es/fields/DynamicField/Index.js +5 -0
  40. package/dist/es/fields/{DynamicField.vue_vue_type_script_setup_true_lang.js → DynamicField/Index.vue_vue_type_script_setup_true_lang.js} +11 -15
  41. package/dist/es/fields/DynamicField/effect.js +40 -0
  42. package/dist/es/fields/Hidden.vue_vue_type_script_setup_true_lang.js +1 -1
  43. package/dist/es/fields/Link.vue_vue_type_script_setup_true_lang.js +4 -4
  44. package/dist/es/fields/Number.vue_vue_type_script_setup_true_lang.js +4 -2
  45. package/dist/es/fields/NumberRange/Index.js +5 -0
  46. package/dist/es/fields/{NumberRange.vue_vue_type_script_setup_true_lang.js → NumberRange/Index.vue_vue_type_script_setup_true_lang.js} +5 -6
  47. package/dist/es/fields/NumberRange/effect.js +8 -0
  48. package/dist/es/fields/RadioGroup.vue_vue_type_script_setup_true_lang.js +5 -2
  49. package/dist/es/fields/Select.vue_vue_type_script_setup_true_lang.js +14 -6
  50. package/dist/es/fields/Switch.vue_vue_type_script_setup_true_lang.js +1 -1
  51. package/dist/es/fields/Text.vue_vue_type_script_setup_true_lang.js +13 -10
  52. package/dist/es/fields/Textarea.vue_vue_type_script_setup_true_lang.js +1 -1
  53. package/dist/es/fields/Time.vue_vue_type_script_setup_true_lang.js +1 -1
  54. package/dist/es/fields/Timerange.vue_vue_type_script_setup_true_lang.js +3 -3
  55. package/dist/es/headless.js +13 -0
  56. package/dist/es/index.js +19 -12
  57. package/dist/es/plugin.js +57 -39
  58. package/dist/es/schema.js +11 -2
  59. package/dist/es/style.css +123 -3
  60. package/dist/es/submitForm.js +270 -72
  61. package/dist/es/utils/builtInFields.js +49 -0
  62. package/dist/es/utils/collectFields.js +407 -0
  63. package/dist/es/utils/config.js +4 -8
  64. package/dist/es/utils/fieldInnerConfig.js +42 -0
  65. package/dist/es/utils/fieldValueEffects.js +80 -0
  66. package/dist/es/utils/form.js +154 -19
  67. package/dist/es/utils/formStateProxy.js +111 -0
  68. package/dist/es/utils/registerField.js +158 -0
  69. package/dist/es/utils/submitHeadless.js +54 -0
  70. package/dist/es/utils/tableGroupList.js +104 -0
  71. package/dist/es/utils/typeMatch.js +532 -0
  72. package/dist/es/utils/validateError.js +57 -0
  73. package/dist/es/utils/validateValues.js +115 -0
  74. package/dist/style.css +123 -3
  75. package/dist/themes/magic-admin.css +576 -14
  76. package/dist/tmagic-form-headless.umd.cjs +4515 -0
  77. package/dist/tmagic-form.umd.cjs +3444 -983
  78. package/package.json +16 -10
  79. package/src/Form.vue +113 -129
  80. package/src/FormBox.vue +16 -4
  81. package/src/FormDialog.vue +16 -5
  82. package/src/FormDrawer.vue +41 -13
  83. package/src/containers/Col.vue +3 -0
  84. package/src/containers/Container.vue +40 -45
  85. package/src/containers/Fieldset.vue +5 -0
  86. package/src/containers/FlexLayout.vue +2 -0
  87. package/src/containers/GroupList.vue +64 -13
  88. package/src/containers/GroupListItem.vue +88 -81
  89. package/src/containers/Panel.vue +3 -0
  90. package/src/containers/Row.vue +2 -0
  91. package/src/containers/Step.vue +2 -0
  92. package/src/containers/Tabs.vue +13 -0
  93. package/src/containers/table/ActionsColumn.vue +1 -1
  94. package/src/containers/table/Table.vue +5 -3
  95. package/src/containers/table/usePagination.ts +1 -1
  96. package/src/containers/table/useSortable.ts +1 -1
  97. package/src/containers/table/useTableColumns.ts +15 -22
  98. package/src/containers/table-group-list/TableGroupList.vue +46 -72
  99. package/src/containers/table-group-list/useAdd.ts +40 -25
  100. package/src/containers/table-group-list/useScrollLastItemIntoView.ts +94 -0
  101. package/src/fields/{CheckboxGroup.vue → CheckboxGroup/Index.vue} +3 -8
  102. package/src/fields/CheckboxGroup/effect.ts +27 -0
  103. package/src/fields/{Date.vue → Date/Index.vue} +2 -5
  104. package/src/fields/Date/effect.ts +29 -0
  105. package/src/fields/{DateTime.vue → DateTime/Index.vue} +2 -16
  106. package/src/fields/DateTime/effect.ts +36 -0
  107. package/src/fields/{Display.vue → Display/Index.vue} +3 -7
  108. package/src/fields/Display/effect.ts +28 -0
  109. package/src/fields/{DynamicField.vue → DynamicField/Index.vue} +11 -11
  110. package/src/fields/DynamicField/effect.ts +74 -0
  111. package/src/fields/Link.vue +3 -4
  112. package/src/fields/Number.vue +2 -1
  113. package/src/fields/{NumberRange.vue → NumberRange/Index.vue} +2 -6
  114. package/src/fields/NumberRange/effect.ts +27 -0
  115. package/src/fields/RadioGroup.vue +13 -1
  116. package/src/fields/Select.vue +13 -4
  117. package/src/headless.ts +78 -0
  118. package/src/index.ts +46 -9
  119. package/src/plugin.ts +68 -37
  120. package/src/schema.ts +13 -2
  121. package/src/submitForm.ts +410 -131
  122. package/src/theme/fieldset.scss +1 -0
  123. package/src/theme/form-box.scss +5 -0
  124. package/src/theme/form-dialog.scss +1 -1
  125. package/src/theme/group-list.scss +143 -2
  126. package/src/theme/table.scss +11 -1
  127. package/src/theme/themes/magic-admin/index.scss +27 -16
  128. package/src/utils/builtInFields.ts +69 -0
  129. package/src/utils/collectFields.ts +561 -0
  130. package/src/utils/config.ts +7 -15
  131. package/src/utils/fieldInnerConfig.ts +127 -0
  132. package/src/utils/fieldValueEffects.ts +140 -0
  133. package/src/utils/form.ts +230 -30
  134. package/src/utils/formStateProxy.ts +149 -0
  135. package/src/utils/registerField.ts +308 -0
  136. package/src/utils/submitHeadless.ts +206 -0
  137. package/src/utils/tableGroupList.ts +147 -0
  138. package/src/utils/typeMatch.ts +948 -0
  139. package/src/utils/validateError.ts +92 -0
  140. package/src/utils/validateValues.ts +177 -0
  141. package/types/headless.d.ts +682 -0
  142. package/types/index.d.ts +900 -180
  143. package/dist/es/_virtual/_rolldown/runtime.js +0 -27
  144. package/dist/es/fields/CheckboxGroup.js +0 -5
  145. package/dist/es/fields/Date.js +0 -5
  146. package/dist/es/fields/DateTime.js +0 -5
  147. package/dist/es/fields/Display.js +0 -5
  148. package/dist/es/fields/DynamicField.js +0 -5
  149. package/dist/es/fields/NumberRange.js +0 -5
@@ -1,13 +1,15 @@
1
- import { FORM_DIFF_CONFIG_KEY } from "./schema.js";
2
- import { getConfig } from "./utils/config.js";
3
- import { initValue } from "./utils/form.js";
1
+ import { FORM_CONTEXT_KEY, FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from "./schema.js";
2
+ import { createFormStateProxy, mergeFormContexts } from "./utils/formStateProxy.js";
3
+ import { createFormStateBase, initValue } from "./utils/form.js";
4
+ import { applyMountValueEffects } from "./utils/collectFields.js";
5
+ import { formatValidateError, getTextByName } from "./utils/validateError.js";
4
6
  import Container_default from "./containers/Container.js";
5
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createSlots, defineComponent, inject, mergeProps, normalizeClass, normalizeStyle, openBlock, provide, reactive, ref, renderList, renderSlot, shallowRef, toRaw, unref, useTemplateRef, watch, watchEffect, withCtx } from "vue";
6
- import { cloneDeep, isEqual } from "lodash-es";
7
- import { M_THEME_KEY, TMagicForm, tMagicMessage, tMagicMessageBox } from "@tmagic/design";
7
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createSlots, defineComponent, inject, mergeProps, nextTick, normalizeClass, normalizeStyle, openBlock, provide, reactive, ref, renderList, renderSlot, shallowRef, toRaw, unref, useTemplateRef, watch, withCtx } from "vue";
8
+ import { cloneDeep, isEqualWith } from "lodash-es";
8
9
  import { setValueByKeyPath } from "@tmagic/utils";
10
+ import { M_THEME_KEY, TMagicForm, tMagicMessage, tMagicMessageBox } from "@tmagic/design";
9
11
  //#region packages/form/src/Form.vue?vue&type=script&setup=true&lang.ts
10
- var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
12
+ var Form_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
11
13
  name: "MForm",
12
14
  __name: "Form",
13
15
  props: {
@@ -20,6 +22,11 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
20
22
  },
21
23
  parentValues: { default: () => ({}) },
22
24
  labelWidth: { default: "200px" },
25
+ typeMatchValid: { type: Boolean },
26
+ validateOnInit: {
27
+ type: Boolean,
28
+ default: false
29
+ },
23
30
  disabled: {
24
31
  type: Boolean,
25
32
  default: false
@@ -35,7 +42,11 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
35
42
  keyProp: { default: "__key" },
36
43
  popperClass: {},
37
44
  preventSubmitDefault: { type: Boolean },
38
- extendState: {},
45
+ useFieldTextInError: {
46
+ type: Boolean,
47
+ default: true
48
+ },
49
+ context: {},
39
50
  showDiff: {},
40
51
  selfDiffFieldTypes: {},
41
52
  theme: {}
@@ -50,12 +61,11 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
50
61
  setup(__props, { expose: __expose, emit: __emit }) {
51
62
  const props = __props;
52
63
  const emit = __emit;
64
+ provide(FORM_TYPE_MATCH_VALID_KEY, computed(() => props.typeMatchValid));
53
65
  const tMagicFormRef = useTemplateRef("tMagicForm");
54
66
  const initialized = ref(false);
55
67
  const values = ref({});
56
68
  const lastValuesProcessed = ref({});
57
- const fields = /* @__PURE__ */ new Map();
58
- const requestFuc = getConfig("request");
59
69
  /**
60
70
  * 当前表单生效的主题名称:
61
71
  * - 优先用本组件自己的 `props.theme`;
@@ -85,18 +95,14 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
85
95
  * 2. `values` / `lastValuesProcessed` 是 ref,Vue 的 `reactive` 会自动解包,因此每次
86
96
  * 访问 `formState.values` / `formState.lastValuesProcessed` 也都是当前 ref 值。
87
97
  *
88
- * 3. `extendState` 注入的字段在下方的 `watchEffect` 中合并到 `formState`:
89
- * - data 描述符(普通字段)通过 `formState[key] = value` 写入,走 reactive proxy 的
90
- * set,触发依赖通知;`extendState` 同步段读到的响应式数据变化时会自动重跑,
91
- * 把最新值刷进 formState。
92
- * - accessor 描述符(`{ get stage() { return ... } }`)按原样写入,调用方可以控制
93
- * 读时求值,每次读取都会重新执行 getter。
98
+ * 3. 宿主业务上下文不再 merge 进 coreState,而是单独放在 `contextRef` 上,由读穿
99
+ * Proxy 在 miss 时落到 context。核心字段结构性优先,`mForm.xxx` 永久兼容旧读法。
94
100
  *
95
101
  * 4. `popperClass` 会自动拼接 `themeClass`:调用方传入的 `popperClass` + 当前主题
96
102
  * 修饰类(含祖先 `<MEditor>` provide 的主题)。这样所有透传到 Element Plus 弹层
97
103
  * `popper-class` 的字段(Select / DateTime / Cascader 等)能自带主题作用域。
98
104
  */
99
- const formState = reactive({
105
+ const coreState = reactive({
100
106
  get keyProp() {
101
107
  return props.keyProp;
102
108
  },
@@ -125,68 +131,27 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
125
131
  values,
126
132
  lastValuesProcessed,
127
133
  $emit: emit,
128
- fields,
129
- setField: (prop, field) => fields.set(prop, field),
130
- getField: (prop) => fields.get(prop),
131
- deleteField: (prop) => fields.delete(prop),
132
- $messageBox: tMagicMessageBox,
133
- $message: tMagicMessage,
134
- post: (options) => {
135
- if (requestFuc) return requestFuc({
136
- method: "POST",
137
- ...options
138
- });
139
- }
134
+ ...createFormStateBase({
135
+ $message: tMagicMessage,
136
+ $messageBox: tMagicMessageBox
137
+ })
140
138
  });
139
+ const ancestorContext = inject(FORM_CONTEXT_KEY, void 0);
141
140
  /**
142
- * `extendState` 的同步段(直到第一个 `await` 之前)所访问的任何响应式数据,
143
- * 都会被 `watchEffect` 自动跟踪。这样可以兼容历史用法 ——
144
- *
145
- * extendState: (formState) => ({
146
- * username: store.username, // 同步读 store,会被跟踪
147
- * env: store.env,
148
- * })
149
- *
150
- * 当 `store.username` 变化时,整个 effect 重跑,新值会被刷进 `formState`。
151
- *
152
- * prop 派生字段(initValues / config / ...)已经在上方用 getter 定义,
153
- * 这里不再重复同步;因此 `props.initValues` 这类高频变化也不会再触发
154
- * `extendState` 重跑(旧版的性能问题修复点)。
155
- *
156
- * 实现细节:
157
- * - data 描述符:通过 `formState[key] = value` 走 reactive proxy 的 set,
158
- * 触发依赖通知;与旧版「逐项赋值」语义完全等价。
159
- * - accessor 描述符(`{ get stage() {...} }`)按原样写入 formState,调用方
160
- * 可以自行控制读时求值;强制 `configurable: true` 以便下一次重跑可再 define。
141
+ * 宿主业务上下文:`props.context` 覆盖祖先注入的同名字段。
142
+ * 嵌套表单(Link / FormBox / FormDialog)通过下面的 provide 自动继承。
161
143
  */
162
- watchEffect(async (onCleanup) => {
163
- const { extendState } = props;
164
- if (typeof extendState !== "function") return;
165
- let stale = false;
166
- onCleanup(() => {
167
- stale = true;
168
- });
169
- let state = {};
170
- try {
171
- state = await extendState(formState) || {};
172
- } catch (e) {
173
- console.error("[MForm] extendState failed:", e);
174
- return;
175
- }
176
- if (stale) return;
177
- for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(state))) if ("value" in descriptor) formState[key] = state[key];
178
- else {
179
- descriptor.configurable = true;
180
- Object.defineProperty(formState, key, descriptor);
181
- }
182
- });
144
+ const contextRef = computed(() => mergeFormContexts(ancestorContext?.value, props.context));
145
+ const formState = createFormStateProxy(coreState, () => contextRef.value);
183
146
  provide("mForm", formState);
147
+ provide(FORM_CONTEXT_KEY, contextRef);
184
148
  /**
185
149
  * 把生效主题(自身或祖先)再 provide 出去,供 form 子树内含 `Teleport` 的组件
186
150
  * (如 `TMagicPopover`)在传送目标上挂 `m-theme--<theme>` 类。
187
151
  * 详见 `@tmagic/design/theme.ts`。
188
152
  */
189
153
  provide(M_THEME_KEY, effectiveTheme);
154
+ provide("formInline", props.inline);
190
155
  provide(FORM_DIFF_CONFIG_KEY, {
191
156
  get showDiff() {
192
157
  return props.showDiff;
@@ -196,21 +161,35 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
196
161
  }
197
162
  });
198
163
  const changeRecords = shallowRef([]);
199
- watch([() => props.config, () => props.initValues], ([config], [preConfig]) => {
164
+ /**
165
+ * 两份配置的结构是否一致;函数一律视为相等。
166
+ *
167
+ * 宿主(如编辑器属性面板)往往在每次节点更新后整份重新生成配置,其中的
168
+ * `display` / `options` / `onChange` 都是新闭包,深比较必然判不等。若据此把 `initialized`
169
+ * 置 false,整棵表单会卸载重挂,滚动位置、展开态、输入焦点全部丢失。
170
+ * 配置是响应式 prop,闭包换了照样生效,只有结构变化(增删字段、换组件类型)才需要重挂。
171
+ */
172
+ const isSameConfigShape = (config, preConfig) => isEqualWith(config, preConfig, (a, b) => typeof a === "function" && typeof b === "function" ? true : void 0);
173
+ watch([() => props.config, () => props.initValues], async ([config], [preConfig]) => {
200
174
  changeRecords.value = [];
201
- if (!isEqual(toRaw(config), toRaw(preConfig))) initialized.value = false;
175
+ if (!isSameConfigShape(toRaw(config), toRaw(preConfig))) initialized.value = false;
202
176
  initValue(formState, {
203
177
  initValues: props.initValues,
204
178
  config: props.config
205
179
  }).then((value) => {
206
180
  values.value = value;
181
+ applyMountValueEffects(formState, props.config, values.value);
207
182
  initialized.value = !props.isCompare;
183
+ if (props.validateOnInit) nextTick(() => {
184
+ tMagicFormRef.value?.validate();
185
+ });
208
186
  });
209
187
  if (props.isCompare) initValue(formState, {
210
188
  initValues: props.lastValues,
211
189
  config: props.config
212
190
  }).then((value) => {
213
191
  lastValuesProcessed.value = value;
192
+ applyMountValueEffects(formState, props.config, lastValuesProcessed.value);
214
193
  initialized.value = true;
215
194
  });
216
195
  }, { immediate: true });
@@ -228,34 +207,17 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
228
207
  const submitHandler = (e) => {
229
208
  if (props.preventSubmitDefault) e.preventDefault();
230
209
  };
210
+ const getTextByName$1 = (name, config = props.config) => getTextByName(name, config);
231
211
  /**
232
- * 通过 name 从 config 中查找对应的 text
233
- * @param name - 字段名,支持点分隔的路径格式,如 'a.b.c'
234
- * @param config - 表单配置数组
235
- * @returns 找到的 text 值,如果未找到则返回 undefined
212
+ * 将校验返回的 invalidFields 汇总为可读的错误文案(多条以 `<br>` 拼接)。
213
+ *
214
+ * 实现收口在 `utils/validateError`,供渲染式校验(本组件的 `submitForm` / `validate`)与
215
+ * 无渲染校验(`validateValues`)共用,保证两条链路产出的错误文案格式完全一致。
236
216
  */
237
- const getTextByName = (name, config = props.config) => {
238
- if (!name || !Array.isArray(config)) return void 0;
239
- const nameParts = name.split(".");
240
- const findInConfig = (configs, parts) => {
241
- if (parts.length === 0) return void 0;
242
- const [currentPart, ...remainingParts] = parts;
243
- for (const item of configs) {
244
- if (item.name === currentPart) {
245
- if (remainingParts.length === 0) return typeof item.text === "string" ? item.text : void 0;
246
- if ("items" in item && Array.isArray(item.items)) {
247
- const result = findInConfig(item.items, remainingParts);
248
- if (result !== void 0) return result;
249
- }
250
- }
251
- if ("items" in item && Array.isArray(item.items)) {
252
- const result = findInConfig(item.items, parts);
253
- if (result !== void 0) return result;
254
- }
255
- }
256
- };
257
- return findInConfig(config, nameParts);
258
- };
217
+ const formatValidateError$1 = (invalidFields) => formatValidateError(invalidFields, {
218
+ config: props.config,
219
+ useFieldTextInError: props.useFieldTextInError
220
+ });
259
221
  __expose({
260
222
  values,
261
223
  lastValuesProcessed,
@@ -275,18 +237,35 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
275
237
  return native ? values.value : cloneDeep(toRaw(values.value));
276
238
  } catch (invalidFields) {
277
239
  emit("error", invalidFields);
278
- const error = [];
279
- Object.entries(invalidFields).forEach(([prop, ValidateError]) => {
280
- ValidateError.forEach(({ field, message }) => {
281
- const name = field || prop;
282
- const text = getTextByName(name, props.config) || name;
283
- error.push(`${text} -> ${message}`);
284
- });
285
- });
286
- throw new Error(error.join("<br>"));
240
+ throw new Error(formatValidateError$1(invalidFields));
241
+ }
242
+ },
243
+ /**
244
+ * 校验:对表单当前值执行校验,返回汇总后的错误文案。
245
+ *
246
+ * 与 `submitForm` 的区别:
247
+ * - 校验失败时不抛异常、不触发 `error` 事件,而是以返回值形式给出错误文案;
248
+ * - 不重置 `changeRecords`,不改变提交语义,仅用于「探测」当前配置是否合法。
249
+ *
250
+ * 注意:本方法只改变「校验结果的返回方式」,并不负责「不污染页面表单状态」——
251
+ * 若需对一份独立的「配置 + 值」做完全不影响页面上已渲染表单的校验,请使用 `validateForm`
252
+ * (内部会新建一个隐藏的 MForm 实例,通过 `initValues` 传入待校验值,用完即卸载)。
253
+ *
254
+ * 典型用途:作为 `validateForm` 内部复用的校验实现;也可在已渲染的表单实例上主动调用,
255
+ * 根据返回的错误文案自行决定后续处理(如记录节点错误状态)。
256
+ *
257
+ * @returns 校验通过返回空字符串 `''`,否则返回以 `<br>` 拼接的错误文案。
258
+ */
259
+ validate: async () => {
260
+ try {
261
+ const result = await tMagicFormRef.value?.validate();
262
+ if (result !== true) throw result;
263
+ return "";
264
+ } catch (invalidFields) {
265
+ return formatValidateError$1(invalidFields);
287
266
  }
288
267
  },
289
- getTextByName
268
+ getTextByName: getTextByName$1
290
269
  });
291
270
  return (_ctx, _cache) => {
292
271
  return openBlock(), createBlock(unref(TMagicForm), {
@@ -312,6 +291,7 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
312
291
  "last-values": lastValuesProcessed.value,
313
292
  "is-compare": __props.isCompare,
314
293
  "label-width": item.labelWidth || __props.labelWidth,
294
+ "label-position": item.labelPosition || __props.labelPosition,
315
295
  "step-active": __props.stepActive,
316
296
  size: __props.size,
317
297
  onChange: changeHandler
@@ -326,6 +306,7 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
326
306
  "last-values",
327
307
  "is-compare",
328
308
  "label-width",
309
+ "label-position",
329
310
  "step-active",
330
311
  "size"
331
312
  ]);
@@ -3,7 +3,7 @@ import { computed, createElementBlock, createElementVNode, createTextVNode, crea
3
3
  import { TMagicButton, TMagicScrollbar } from "@tmagic/design";
4
4
  //#region packages/form/src/FormBox.vue?vue&type=script&setup=true&lang.ts
5
5
  var footerHeight = 60;
6
- var FormBox_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
6
+ var FormBox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
7
7
  name: "MFormBox",
8
8
  __name: "FormBox",
9
9
  props: {
@@ -13,13 +13,22 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
13
13
  width: {},
14
14
  height: {},
15
15
  labelWidth: {},
16
+ typeMatchValid: { type: Boolean },
17
+ validateOnInit: {
18
+ type: Boolean,
19
+ default: false
20
+ },
16
21
  disabled: { type: Boolean },
17
22
  size: {},
18
23
  confirmText: { default: "确定" },
19
24
  inline: { type: Boolean },
20
25
  labelPosition: {},
21
26
  preventSubmitDefault: { type: Boolean },
22
- extendState: {}
27
+ useFieldTextInError: {
28
+ type: Boolean,
29
+ default: true
30
+ },
31
+ context: {}
23
32
  },
24
33
  emits: [
25
34
  "change",
@@ -45,7 +54,8 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
45
54
  const submitHandler = async () => {
46
55
  try {
47
56
  const changeRecords = form.value?.changeRecords;
48
- emit("submit", await form.value?.submitForm(), { changeRecords });
57
+ const values = await form.value?.submitForm();
58
+ emit("submit", values, { changeRecords });
49
59
  } catch (e) {
50
60
  emit("error", e);
51
61
  }
@@ -81,7 +91,10 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
81
91
  "label-position": __props.labelPosition,
82
92
  inline: __props.inline,
83
93
  "prevent-submit-default": __props.preventSubmitDefault,
84
- "extend-state": __props.extendState,
94
+ "use-field-text-in-error": __props.useFieldTextInError,
95
+ context: __props.context,
96
+ "type-match-valid": __props.typeMatchValid,
97
+ "validate-on-init": __props.validateOnInit,
85
98
  onChange: changeHandler
86
99
  }, null, 8, [
87
100
  "size",
@@ -93,7 +106,10 @@ var FormBox_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
93
106
  "label-position",
94
107
  "inline",
95
108
  "prevent-submit-default",
96
- "extend-state"
109
+ "use-field-text-in-error",
110
+ "context",
111
+ "type-match-valid",
112
+ "validate-on-init"
97
113
  ]), renderSlot(_ctx.$slots, "default")]),
98
114
  _: 3
99
115
  })], 4), createElementVNode("div", {
@@ -3,7 +3,7 @@ import { computed, createBlock, createCommentVNode, createElementBlock, createEl
3
3
  import { M_THEME_KEY, TMagicButton, TMagicCol, TMagicDialog, TMagicRow } from "@tmagic/design";
4
4
  //#region packages/form/src/FormDialog.vue?vue&type=script&setup=true&lang.ts
5
5
  var _hoisted_1 = { style: { "min-height": "1px" } };
6
- var FormDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
6
+ var FormDialog_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
7
7
  name: "MFormDialog",
8
8
  __name: "FormDialog",
9
9
  props: {
@@ -12,6 +12,11 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
12
12
  parentValues: {},
13
13
  width: {},
14
14
  labelWidth: {},
15
+ typeMatchValid: { type: Boolean },
16
+ validateOnInit: {
17
+ type: Boolean,
18
+ default: false
19
+ },
15
20
  fullscreen: { type: Boolean },
16
21
  disabled: { type: Boolean },
17
22
  title: {},
@@ -41,7 +46,11 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
41
46
  type: Boolean,
42
47
  default: true
43
48
  },
44
- extendState: {},
49
+ useFieldTextInError: {
50
+ type: Boolean,
51
+ default: true
52
+ },
53
+ context: {},
45
54
  theme: {}
46
55
  },
47
56
  emits: [
@@ -78,7 +87,8 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
78
87
  const save = async () => {
79
88
  try {
80
89
  const changeRecords = [...form.value?.changeRecords || []];
81
- emit("submit", await form.value?.submitForm(), { changeRecords });
90
+ const values = await form.value?.submitForm();
91
+ emit("submit", values, { changeRecords });
82
92
  } catch (e) {
83
93
  emit("error", e);
84
94
  }
@@ -195,7 +205,10 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
195
205
  "label-position": __props.labelPosition,
196
206
  inline: __props.inline,
197
207
  "prevent-submit-default": __props.preventSubmitDefault,
198
- "extend-state": __props.extendState,
208
+ "use-field-text-in-error": __props.useFieldTextInError,
209
+ "type-match-valid": __props.typeMatchValid,
210
+ "validate-on-init": __props.validateOnInit,
211
+ context: __props.context,
199
212
  theme: effectiveTheme.value,
200
213
  onChange: changeHandler
201
214
  }, null, 8, [
@@ -209,7 +222,10 @@ var FormDialog_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
209
222
  "label-position",
210
223
  "inline",
211
224
  "prevent-submit-default",
212
- "extend-state",
225
+ "use-field-text-in-error",
226
+ "type-match-valid",
227
+ "validate-on-init",
228
+ "context",
213
229
  "theme"
214
230
  ]), renderSlot(_ctx.$slots, "default")], 4)) : createCommentVNode("v-if", true)]),
215
231
  _: 3
@@ -3,8 +3,8 @@ import { computed, createBlock, createCommentVNode, createElementBlock, createEl
3
3
  import { M_THEME_KEY, TMagicButton, TMagicCol, TMagicDrawer, TMagicRow } from "@tmagic/design";
4
4
  //#region packages/form/src/FormDrawer.vue?vue&type=script&setup=true&lang.ts
5
5
  var _hoisted_1 = { style: { "min-height": "1px" } };
6
- var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
7
- name: "MFormDialog",
6
+ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
7
+ name: "MFormDrawer",
8
8
  __name: "FormDrawer",
9
9
  props: {
10
10
  config: { default: () => [] },
@@ -12,6 +12,11 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
12
12
  parentValues: {},
13
13
  width: {},
14
14
  labelWidth: {},
15
+ typeMatchValid: { type: Boolean },
16
+ validateOnInit: {
17
+ type: Boolean,
18
+ default: false
19
+ },
15
20
  disabled: { type: Boolean },
16
21
  closeOnPressEscape: {
17
22
  type: Boolean,
@@ -24,8 +29,12 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
24
29
  inline: { type: Boolean },
25
30
  labelPosition: {},
26
31
  preventSubmitDefault: { type: Boolean },
32
+ useFieldTextInError: {
33
+ type: Boolean,
34
+ default: true
35
+ },
27
36
  beforeClose: {},
28
- extendState: {},
37
+ context: {},
29
38
  theme: {}
30
39
  },
31
40
  emits: [
@@ -46,7 +55,7 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
46
55
  const drawer = ref();
47
56
  const form = ref();
48
57
  const drawerBody = ref();
49
- const visible = ref(false);
58
+ const dialogVisible = ref(false);
50
59
  const saveFetch = ref(false);
51
60
  const bodyHeight = ref(0);
52
61
  watchEffect(() => {
@@ -55,7 +64,8 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
55
64
  const submitHandler = async () => {
56
65
  try {
57
66
  const changeRecords = form.value?.changeRecords;
58
- emit("submit", await form.value?.submitForm(), { changeRecords });
67
+ const values = await form.value?.submitForm();
68
+ emit("submit", values, { changeRecords });
59
69
  } catch (e) {
60
70
  emit("error", e);
61
71
  }
@@ -76,21 +86,39 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
76
86
  emit("closed");
77
87
  };
78
88
  const show = () => {
79
- visible.value = true;
89
+ dialogVisible.value = true;
80
90
  };
81
91
  const hide = () => {
82
- visible.value = false;
92
+ dialogVisible.value = false;
93
+ };
94
+ const close = () => {
95
+ dialogVisible.value = false;
96
+ };
97
+ const cancel = () => {
98
+ hide();
83
99
  };
84
100
  /** 用于关闭 Drawer, 该方法会调用传入的 before-close 方法 */
85
101
  const handleClose = () => {
86
102
  drawer.value?.handleClose();
87
103
  };
104
+ const save = async () => {
105
+ try {
106
+ const changeRecords = [...form.value?.changeRecords || []];
107
+ const values = await form.value?.submitForm();
108
+ emit("submit", values, { changeRecords });
109
+ } catch (e) {
110
+ emit("error", e);
111
+ }
112
+ };
88
113
  __expose({
89
114
  form,
90
115
  saveFetch,
91
116
  bodyHeight,
117
+ close,
92
118
  show,
93
119
  hide,
120
+ save,
121
+ cancel,
94
122
  handleClose
95
123
  });
96
124
  return (_ctx, _cache) => {
@@ -98,8 +126,8 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
98
126
  class: normalizeClass(["m-form-drawer", effectiveTheme.value ? `m-theme--${effectiveTheme.value}` : ""]),
99
127
  ref_key: "drawer",
100
128
  ref: drawer,
101
- modelValue: visible.value,
102
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => visible.value = $event),
129
+ modelValue: dialogVisible.value,
130
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => dialogVisible.value = $event),
103
131
  title: __props.title,
104
132
  "close-on-press-escape": __props.closeOnPressEscape,
105
133
  "append-to-body": true,
@@ -137,7 +165,7 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
137
165
  })]),
138
166
  _: 3
139
167
  })]),
140
- default: withCtx(() => [visible.value ? (openBlock(), createElementBlock("div", {
168
+ default: withCtx(() => [dialogVisible.value ? (openBlock(), createElementBlock("div", {
141
169
  key: 0,
142
170
  ref_key: "drawerBody",
143
171
  ref: drawerBody,
@@ -154,7 +182,10 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
154
182
  "label-position": __props.labelPosition,
155
183
  inline: __props.inline,
156
184
  "prevent-submit-default": __props.preventSubmitDefault,
157
- "extend-state": __props.extendState,
185
+ "use-field-text-in-error": __props.useFieldTextInError,
186
+ "type-match-valid": __props.typeMatchValid,
187
+ "validate-on-init": __props.validateOnInit,
188
+ context: __props.context,
158
189
  theme: effectiveTheme.value,
159
190
  onChange: changeHandler
160
191
  }, null, 8, [
@@ -167,7 +198,10 @@ var FormDrawer_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
167
198
  "label-position",
168
199
  "inline",
169
200
  "prevent-submit-default",
170
- "extend-state",
201
+ "use-field-text-in-error",
202
+ "type-match-valid",
203
+ "validate-on-init",
204
+ "context",
171
205
  "theme"
172
206
  ]), renderSlot(_ctx.$slots, "default")], 512)) : createCommentVNode("v-if", true)]),
173
207
  _: 3
@@ -3,7 +3,7 @@ import Container_default from "./Container.js";
3
3
  import { computed, createBlock, createVNode, defineComponent, inject, openBlock, unref, vShow, withCtx, withDirectives } from "vue";
4
4
  import { TMagicCol } from "@tmagic/design";
5
5
  //#region packages/form/src/containers/Col.vue?vue&type=script&setup=true&lang.ts
6
- var Col_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
6
+ var Col_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
7
7
  name: "MFormCol",
8
8
  __name: "Col",
9
9
  props: {
@@ -12,6 +12,7 @@ var Col_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComp
12
12
  isCompare: { type: Boolean },
13
13
  config: {},
14
14
  labelWidth: {},
15
+ labelPosition: {},
15
16
  expandMore: { type: Boolean },
16
17
  span: {},
17
18
  size: {},
@@ -36,11 +37,13 @@ var Col_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComp
36
37
  config: __props.config,
37
38
  prop: __props.prop,
38
39
  "label-width": __props.config.labelWidth || __props.labelWidth,
40
+ "label-position": __props.config.labelPosition || __props.labelPosition,
39
41
  "expand-more": __props.expandMore,
40
42
  size: __props.size,
41
43
  disabled: __props.disabled,
42
44
  onChange: changeHandler,
43
- onAddDiffCount
45
+ onAddDiffCount,
46
+ "is-in-row": true
44
47
  }, null, 8, [
45
48
  "model",
46
49
  "lastValues",
@@ -48,6 +51,7 @@ var Col_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComp
48
51
  "config",
49
52
  "prop",
50
53
  "label-width",
54
+ "label-position",
51
55
  "expand-more",
52
56
  "size",
53
57
  "disabled"