@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
package/dist/es/style.css CHANGED
@@ -26,7 +26,7 @@
26
26
  .m-form-dialog .m-dialog-body {
27
27
  padding: 0 20px;
28
28
  }
29
- .m-form-dialog .el-table .m-form-item .el-form-item {
29
+ .m-form-dialog .el-table .m-form-item .el-form-item:not(.is-error) {
30
30
  margin-bottom: 0;
31
31
  }
32
32
 
@@ -226,6 +226,7 @@ a.magic-admin-link i {
226
226
  .m-container-fieldset.fieldset-in-card-fit > fieldset.m-fieldset legend {
227
227
  position: relative;
228
228
  top: 10px;
229
+ background-color: transparent;
229
230
  }
230
231
  .m-container-fieldset.fieldset-in-card-fit.fieldset-in-card-fit-last {
231
232
  padding-bottom: 0 !important;
@@ -268,6 +269,21 @@ fieldset.m-fieldset .m-form-tip {
268
269
  margin-left: 5px;
269
270
  }
270
271
 
272
+ .m-fields-group-list {
273
+ --m-group-list-footer-padding-top: 12px;
274
+ --m-group-list-footer-padding-bottom: 16px;
275
+ --m-group-list-footer-button-height: 32px;
276
+ --m-group-list-footer-height: calc(
277
+ var(--m-group-list-footer-padding-top) +
278
+ var(--m-group-list-footer-button-height) +
279
+ var(--m-group-list-footer-padding-bottom)
280
+ );
281
+ --m-group-list-footer-bg: var(--el-bg-color, #fff);
282
+ --m-group-list-header-height: 65px;
283
+ --m-group-list-header-bg: #fff;
284
+ width: 100%;
285
+ min-width: 0;
286
+ }
271
287
  .m-fields-group-list .m-fields-group-list-item.tmagic-design-card--flat:last-child {
272
288
  border: 0;
273
289
  }
@@ -283,21 +299,117 @@ fieldset.m-fieldset .m-form-tip {
283
299
  transform: rotate(90deg);
284
300
  }
285
301
  .m-fields-group-list .m-fields-group-list-item {
302
+ overflow: visible;
286
303
  border-bottom: 1px solid #ebeef5;
287
304
  margin-bottom: 7px;
288
305
  }
289
306
  .m-fields-group-list .m-fields-group-list-item:last-of-type {
290
307
  border-bottom: 0;
291
308
  }
309
+ .m-fields-group-list .m-fields-group-list-item .el-card__body,
310
+ .m-fields-group-list .m-fields-group-list-item .t-card__body {
311
+ overflow: visible;
312
+ }
313
+ .m-fields-group-list .m-fields-group-list-item .el-card__body > .m-container-row,
314
+ .m-fields-group-list .m-fields-group-list-item .t-card__body > .m-container-row {
315
+ overflow: visible;
316
+ }
317
+ .m-fields-group-list .m-fields-group-list-item .el-card__body .tmagic-design-form-item,
318
+ .m-fields-group-list .m-fields-group-list-item .t-card__body .tmagic-design-form-item {
319
+ margin-bottom: 16px;
320
+ }
292
321
  .m-fields-group-list .m-fields-group-list-item .m-fields-group-list-item-header {
293
322
  display: flex;
294
323
  align-items: center;
295
324
  gap: 8px;
296
325
  }
297
- .m-fields-group-list .m-fields-group-list-footer {
326
+ .m-fields-group-list .m-fields-group-list-item .m-fields-group-list-item-title {
327
+ flex: 1;
328
+ min-width: 0;
329
+ }
330
+ .m-fields-group-list .m-fields-group-list-item .m-fields-group-list-item-actions {
331
+ display: flex;
332
+ align-items: center;
333
+ flex-shrink: 0;
334
+ gap: 8px;
335
+ }
336
+ .m-fields-group-list > .m-fields-group-list-item {
337
+ --m-group-list-item-footer-bottom: var(
338
+ --m-group-list-nested-footer-bottom,
339
+ 0px
340
+ );
341
+ }
342
+ .m-fields-group-list.is-footer-sticky > .m-fields-group-list-item {
343
+ --m-group-list-item-footer-bottom: calc(
344
+ var(--m-group-list-nested-footer-bottom, 0px) +
345
+ var(--m-group-list-footer-height)
346
+ );
347
+ }
348
+ .m-fields-group-list .m-fields-group-list-item .m-fields-group-list {
349
+ --m-group-list-nested-footer-bottom: var(
350
+ --m-group-list-item-footer-bottom,
351
+ 0px
352
+ );
353
+ }
354
+ .m-fields-group-list.is-header-sticky {
355
+ --m-group-list-nested-sticky-top: var(--m-group-list-child-list-sticky-top);
356
+ --m-group-list-nested-header-z: var(--m-group-list-child-list-header-z);
357
+ }
358
+ .m-fields-group-list.is-header-sticky > .m-fields-group-list-item {
359
+ --m-group-list-item-sticky-top: var(
360
+ --m-group-list-nested-sticky-top,
361
+ var(--m-group-list-header-sticky-top, 0px)
362
+ );
363
+ --m-group-list-item-header-z: var(--m-group-list-nested-header-z, 7);
364
+ --m-group-list-child-list-sticky-top: calc(
365
+ var(--m-group-list-item-sticky-top) + var(--m-group-list-header-height)
366
+ );
367
+ --m-group-list-child-list-header-z: calc(
368
+ var(--m-group-list-item-header-z) - 1
369
+ );
370
+ }
371
+ .m-fields-group-list.is-header-sticky > .m-fields-group-list-item > .el-card__header,
372
+ .m-fields-group-list.is-header-sticky > .m-fields-group-list-item > .t-card__header {
373
+ position: sticky;
374
+ top: var(--m-group-list-item-sticky-top);
375
+ z-index: var(--m-group-list-item-header-z);
376
+ background-color: var(--m-group-list-header-bg, #fff);
377
+ }
378
+ .m-fields-group-list .el-table__empty-block {
379
+ width: 100%;
380
+ min-height: 0;
381
+ }
382
+ .m-fields-group-list .el-table__empty-text {
383
+ display: block;
384
+ box-sizing: border-box;
385
+ width: 100%;
386
+ }
387
+ .m-fields-group-list > .m-fields-group-list-footer {
298
388
  display: flex;
299
389
  justify-content: space-between;
300
390
  margin-top: 10px;
391
+ margin-bottom: 16px;
392
+ }
393
+ .m-fields-group-list > .m-fields-group-list-footer.is-sticky-full {
394
+ position: sticky;
395
+ bottom: var(--m-group-list-nested-footer-bottom, 0px);
396
+ z-index: 7;
397
+ margin-bottom: 0;
398
+ padding: var(--m-group-list-footer-padding-top) 0 var(--m-group-list-footer-padding-bottom);
399
+ background-color: var(--m-group-list-footer-bg);
400
+ }
401
+ .m-fields-group-list > .m-fields-group-list-footer.is-sticky-full > div {
402
+ justify-content: stretch;
403
+ }
404
+ .m-fields-group-list > .m-fields-group-list-footer.is-sticky-full .el-button,
405
+ .m-fields-group-list > .m-fields-group-list-footer.is-sticky-full .tmagic-design-button {
406
+ width: 100%;
407
+ height: var(--m-group-list-footer-button-height);
408
+ }
409
+
410
+ /** 最外层的groupList需要每个item需要增加空白区域界限时,增加outer-gorup_list可以实现 */
411
+ .m-container-group-list.outer-gorup_list > .m-fields-group-list > .m-fields-group-list-item {
412
+ margin-bottom: 10px;
301
413
  }
302
414
 
303
415
  .m-form-panel .el-card__header:hover {
@@ -343,6 +455,10 @@ fieldset.m-fieldset .m-form-tip {
343
455
  width: 95vw !important;
344
456
  }
345
457
 
458
+ .el-form-item .m-fields-table .el-form-item.is-error {
459
+ margin-bottom: 18px;
460
+ }
461
+
346
462
  .m-fields-table {
347
463
  width: 100%;
348
464
  }
@@ -365,7 +481,7 @@ fieldset.m-fieldset .m-form-tip {
365
481
  .m-fields-table .el-table__expanded-cell .m-form-tip {
366
482
  margin-left: 80px;
367
483
  }
368
- .m-fields-table .el-form-item {
484
+ .m-fields-table .el-form-item:not(.is-error) {
369
485
  margin-bottom: 0;
370
486
  }
371
487
  .m-fields-table .tmagic-form-table-drag-target {
@@ -417,7 +533,11 @@ fieldset.m-fieldset .m-form-tip {
417
533
  .m-form-box .el-box__header {
418
534
  margin: 0;
419
535
  }
536
+ .m-form-box .m-box-body {
537
+ min-height: 0;
538
+ }
420
539
  .m-form-box .dialog-footer {
540
+ flex-shrink: 0;
421
541
  display: flex;
422
542
  align-items: center;
423
543
  justify-content: space-between;
@@ -1,93 +1,59 @@
1
+ import { submitForm as submitForm$1, validateForm as validateForm$1 } from "./utils/submitHeadless.js";
1
2
  import Form_default from "./Form.js";
2
- import { createApp, defineComponent, h, nextTick, ref, watch } from "vue";
3
+ import { createApp, defineComponent, h, nextTick, ref } from "vue";
3
4
  //#region packages/form/src/submitForm.ts
4
5
  /**
5
- * 以命令式方式调用 Form.vue 完成一次表单校验/提交。
6
+ * `dialog: true` 时 submitForm / validateForm 的公共脚手架:
6
7
  *
7
- * 类似 ElMessage 的用法:传入 props(包含 `config`/`initValues` 等),函数内部会临时挂载
8
- * 一个不可见的 Form 组件实例,等待初始化完成后调用其 `submitForm` 方法,
9
- * 校验通过则 resolve 表单值,校验失败则 reject 错误信息,最后自动卸载实例。
8
+ * 创建临时容器 → 挂载一个 wrapper 组件(内含 MForm)→ 提供统一的 cleanup / appContext 继承 →
9
+ * 由 `createWrapper` 决定「何时调用 MForm 的哪个方法、如何 resolve/reject」。
10
10
  *
11
- * @example
12
- * ```ts
13
- * import { submitForm } from '@tmagic/form';
14
- *
15
- * try {
16
- * const values = await submitForm({
17
- * config: [...],
18
- * initValues: { name: 'foo' },
19
- * });
20
- * console.log(values);
21
- * } catch (e) {
22
- * console.error(e);
23
- * }
24
- *
25
- * // 需要同时获取变更记录时:
26
- * const { values, changeRecords } = await submitForm({
27
- * config: [...],
28
- * initValues: { name: 'foo' },
29
- * returnChangeRecords: true,
30
- * });
31
- * ```
11
+ * 挂载出来的表单等待人工点击,因此没有超时兜底,靠「确定」/「取消」/`signal` 结束。
32
12
  */
33
- var submitForm = (options) => {
34
- const { native, appContext, timeout = 1e4, returnChangeRecords, ...formProps } = options;
13
+ var mountFormInstance = (options) => {
14
+ const { formProps, appContext, signal, createWrapper } = options;
35
15
  return new Promise((resolve, reject) => {
16
+ if (signal?.aborted) {
17
+ reject(signal.reason ?? /* @__PURE__ */ new Error("mountFormInstance aborted"));
18
+ return;
19
+ }
20
+ let cleaned = false;
21
+ let onAbort = null;
22
+ const instance = { app: null };
36
23
  const container = document.createElement("div");
37
- container.style.display = "none";
38
24
  document.body.appendChild(container);
39
- let cleaned = false;
40
- let timer = null;
41
- const app = createApp(defineComponent({
42
- name: "MFormSubmitWrapper",
43
- setup() {
44
- const formRef = ref(null);
45
- const stop = watch(() => formRef.value?.initialized, async (initialized) => {
46
- if (!initialized) return;
47
- stop();
48
- try {
49
- await nextTick();
50
- const changeRecords = [...formRef.value.changeRecords ?? []];
51
- const result = await formRef.value.submitForm(native);
52
- resolve(returnChangeRecords ? {
53
- values: result,
54
- changeRecords
55
- } : result);
56
- } catch (err) {
57
- reject(err);
58
- } finally {
59
- cleanup();
60
- }
61
- }, {
62
- flush: "post",
63
- immediate: true
64
- });
65
- return () => h(Form_default, {
66
- ...formProps,
67
- ref: formRef
68
- });
69
- }
70
- }));
71
- if (appContext) Object.assign(app._context, appContext);
72
25
  const cleanup = () => {
73
26
  if (cleaned) return;
74
27
  cleaned = true;
75
- if (timer) {
76
- clearTimeout(timer);
77
- timer = null;
28
+ if (signal && onAbort) {
29
+ signal.removeEventListener("abort", onAbort);
30
+ onAbort = null;
78
31
  }
79
32
  try {
80
- app.unmount();
33
+ instance.app?.unmount();
81
34
  } catch {}
82
35
  container.parentNode?.removeChild(container);
83
36
  };
84
- if (timeout > 0) timer = setTimeout(() => {
85
- if (!cleaned) {
86
- reject(/* @__PURE__ */ new Error(`submitForm timeout after ${timeout}ms: form is not initialized.`));
37
+ if (signal) {
38
+ onAbort = () => {
39
+ if (cleaned) return;
40
+ reject(signal.reason ?? /* @__PURE__ */ new Error("mountFormInstance aborted"));
87
41
  cleanup();
88
- }
89
- }, timeout);
42
+ };
43
+ signal.addEventListener("abort", onAbort);
44
+ }
90
45
  try {
46
+ const formRef = ref(null);
47
+ const wrapperComponent = createWrapper({
48
+ formRef,
49
+ formProps,
50
+ cleanup,
51
+ resolve,
52
+ reject
53
+ });
54
+ const app = createApp(wrapperComponent);
55
+ instance.app = app;
56
+ if (appContext) Object.assign(app._context, appContext);
91
57
  app.mount(container);
92
58
  } catch (err) {
93
59
  reject(err);
@@ -95,5 +61,237 @@ var submitForm = (options) => {
95
61
  }
96
62
  });
97
63
  };
64
+ /**
65
+ * 构造可见弹层 wrapper:以 fixed 遮罩居中渲染 MForm,提供「确定 / 取消」按钮与错误展示区。
66
+ *
67
+ * submitForm 与 validateForm 的弹层 UI 完全一致,仅「确定」时调用的实例方法、错误处理与取消文案不同,
68
+ * 这些差异通过 `onConfirm` / `onCancel` 注入,弹层结构在此统一收口。
69
+ */
70
+ var createDialogWrapper = (options) => {
71
+ const { formRef, formProps, title, name, onConfirm, onCancel } = options;
72
+ const btnBase = {
73
+ padding: "8px 20px",
74
+ fontSize: "14px",
75
+ lineHeight: "1",
76
+ border: "1px solid #dcdfe6",
77
+ borderRadius: "4px",
78
+ cursor: "pointer"
79
+ };
80
+ return defineComponent({
81
+ name,
82
+ setup() {
83
+ const errorMsg = ref("");
84
+ const setError = (msg) => {
85
+ errorMsg.value = msg;
86
+ };
87
+ return () => h("div", { style: {
88
+ position: "fixed",
89
+ inset: "0",
90
+ zIndex: "10000",
91
+ display: "flex",
92
+ alignItems: "center",
93
+ justifyContent: "center",
94
+ background: "rgba(0, 0, 0, 0.5)"
95
+ } }, [h("div", { style: {
96
+ display: "flex",
97
+ flexDirection: "column",
98
+ width: "600px",
99
+ maxWidth: "90vw",
100
+ maxHeight: "85vh",
101
+ background: "#fff",
102
+ borderRadius: "8px",
103
+ boxShadow: "0 8px 24px rgba(0, 0, 0, 0.2)",
104
+ overflow: "hidden"
105
+ } }, [
106
+ h("div", { style: {
107
+ padding: "16px 20px",
108
+ fontSize: "16px",
109
+ fontWeight: "600",
110
+ borderBottom: "1px solid #ebeef5"
111
+ } }, title),
112
+ h("div", { style: {
113
+ flex: "1",
114
+ padding: "20px",
115
+ overflow: "auto"
116
+ } }, [h(Form_default, {
117
+ ...formProps,
118
+ ref: formRef
119
+ }), errorMsg.value ? h("div", {
120
+ style: {
121
+ marginTop: "12px",
122
+ color: "#f56c6c",
123
+ fontSize: "13px",
124
+ lineHeight: "1.5"
125
+ },
126
+ innerHTML: errorMsg.value
127
+ }) : null]),
128
+ h("div", { style: {
129
+ display: "flex",
130
+ justifyContent: "flex-end",
131
+ gap: "12px",
132
+ padding: "12px 20px",
133
+ borderTop: "1px solid #ebeef5"
134
+ } }, [h("button", {
135
+ type: "button",
136
+ onClick: onCancel,
137
+ style: { ...btnBase }
138
+ }, "取消"), h("button", {
139
+ type: "button",
140
+ onClick: () => onConfirm(setError),
141
+ style: {
142
+ ...btnBase,
143
+ color: "#fff",
144
+ background: "#409eff",
145
+ borderColor: "#409eff"
146
+ }
147
+ }, "确定")])
148
+ ])]);
149
+ }
150
+ });
151
+ };
152
+ /**
153
+ * 可见弹层提交:把表单渲染在弹层里,点击「确定」才调用挂载实例的 `submitForm`。
154
+ *
155
+ * 用于需要用户填写/确认的场景;默认路径仍是无渲染校验(见 `submitForm` 的说明)。
156
+ */
157
+ var submitFormByDialogRender = (options) => {
158
+ const { native, appContext, returnChangeRecords, signal, dialog, title, ...formProps } = options;
159
+ return mountFormInstance({
160
+ formProps,
161
+ appContext,
162
+ signal,
163
+ createWrapper: ({ formRef, formProps, cleanup, resolve, reject }) => {
164
+ const doSubmit = async (onValidateError) => {
165
+ try {
166
+ await nextTick();
167
+ const changeRecords = [...formRef.value?.changeRecords ?? []];
168
+ const result = await formRef.value.submitForm(native);
169
+ resolve(returnChangeRecords ? {
170
+ values: result,
171
+ changeRecords
172
+ } : result);
173
+ cleanup();
174
+ } catch (err) {
175
+ onValidateError(err);
176
+ }
177
+ };
178
+ return createDialogWrapper({
179
+ formRef,
180
+ formProps,
181
+ name: "MFormSubmitWrapper",
182
+ title: title ?? "submitForm",
183
+ onConfirm: (setError) => doSubmit((err) => {
184
+ setError(err instanceof Error ? err.message : String(err));
185
+ }),
186
+ onCancel: () => {
187
+ reject(/* @__PURE__ */ new Error("submitForm canceled."));
188
+ cleanup();
189
+ }
190
+ });
191
+ }
192
+ });
193
+ };
194
+ /**
195
+ * 深拷贝配置值,保留函数(display / onTabClick 等回调)引用,避免破坏配置中的回调。
196
+ */
197
+ var cloneConfigValue = (value) => {
198
+ if (Array.isArray(value)) return value.map(cloneConfigValue);
199
+ if (value && typeof value === "object") return Object.keys(value).reduce((acc, key) => {
200
+ acc[key] = cloneConfigValue(value[key]);
201
+ return acc;
202
+ }, {});
203
+ return value;
204
+ };
205
+ /**
206
+ * 深度遍历配置,去掉所有 type 为 'tab' 的容器中各标签页(items)的 lazy 配置。
207
+ */
208
+ var removeTabItemsLazy = (node) => {
209
+ if (Array.isArray(node)) {
210
+ node.forEach(removeTabItemsLazy);
211
+ return;
212
+ }
213
+ if (!node || typeof node !== "object") return;
214
+ if (node.type === "tab" && Array.isArray(node.items)) node.items.forEach((pane) => {
215
+ if (pane && typeof pane === "object") delete pane.lazy;
216
+ });
217
+ Object.keys(node).forEach((key) => {
218
+ removeTabItemsLazy(node[key]);
219
+ });
220
+ };
221
+ /**
222
+ * 返回一份去除了 tab 标签页 lazy 的配置副本。
223
+ *
224
+ * tab 容器开启 lazy 时,非激活标签页的内容不会渲染,导致 validateForm 静默挂载后
225
+ * 无法校验到这些标签页内的字段。校验场景需要一次性渲染全部字段,故在此统一去除 lazy。
226
+ * 处理基于深拷贝,不会污染调用方传入的原始 config。
227
+ */
228
+ var stripTabItemsLazy = (config) => {
229
+ const cloned = cloneConfigValue(config);
230
+ removeTabItemsLazy(cloned);
231
+ return cloned;
232
+ };
233
+ /**
234
+ * 可见弹层校验:把表单渲染在弹层里,点击「确定」才调用挂载实例的 `validate`。
235
+ *
236
+ * 用于需要用户填写/确认的场景;默认路径仍是无渲染校验(见 `validateForm` 的说明)。
237
+ */
238
+ var validateFormByDialogRender = (options) => {
239
+ const { appContext, config, signal, dialog, title, ...rest } = options;
240
+ return mountFormInstance({
241
+ formProps: {
242
+ ...rest,
243
+ config: stripTabItemsLazy(config)
244
+ },
245
+ appContext,
246
+ signal,
247
+ createWrapper: ({ formRef, formProps, cleanup, resolve, reject }) => {
248
+ const doValidate = async (onInvalid) => {
249
+ try {
250
+ await nextTick();
251
+ const error = await formRef.value.validate();
252
+ if (error) onInvalid(error);
253
+ else {
254
+ resolve("");
255
+ cleanup();
256
+ }
257
+ } catch (err) {
258
+ reject(err);
259
+ cleanup();
260
+ }
261
+ };
262
+ return createDialogWrapper({
263
+ formRef,
264
+ formProps,
265
+ name: "MFormValidateWrapper",
266
+ title: title ?? "validateForm",
267
+ onConfirm: (setError) => doValidate((error) => {
268
+ setError(error);
269
+ }),
270
+ onCancel: () => {
271
+ reject(/* @__PURE__ */ new Error("validateForm canceled."));
272
+ cleanup();
273
+ }
274
+ });
275
+ }
276
+ });
277
+ };
278
+ /**
279
+ * 以命令式方式对一份「表单配置 + 值」做一次校验并取回表单值。
280
+ *
281
+ * 默认走无渲染实现(见 `@tmagic/form/headless`)。`dialog: true` 会把表单以弹层渲染出来。
282
+ */
283
+ var submitForm = async (options) => {
284
+ if (options.dialog) return submitFormByDialogRender(options);
285
+ return submitForm$1(options);
286
+ };
287
+ /**
288
+ * 以命令式方式对一份「表单配置 + 值」做一次静默校验。
289
+ *
290
+ * 默认走无渲染实现。`dialog: true` 的处理详见 `submitForm`。
291
+ */
292
+ var validateForm = async (options) => {
293
+ if (options.dialog) return validateFormByDialogRender(options);
294
+ return validateForm$1(options);
295
+ };
98
296
  //#endregion
99
- export { submitForm };
297
+ export { stripTabItemsLazy, submitForm, validateForm };
@@ -0,0 +1,49 @@
1
+ import { expandFieldset, expandPanel, expandRow, expandStep, expandTab, expandTableGroupList } from "./collectFields.js";
2
+ import { effect } from "../fields/DynamicField/effect.js";
3
+ import { effect as effect$1 } from "../fields/CheckboxGroup/effect.js";
4
+ import { effect as effect$2 } from "../fields/Date/effect.js";
5
+ import { effect as effect$3 } from "../fields/DateTime/effect.js";
6
+ import { effect as effect$4 } from "../fields/Display/effect.js";
7
+ import { effect as effect$5 } from "../fields/NumberRange/effect.js";
8
+ //#region packages/form/src/utils/builtInFields.ts
9
+ /**
10
+ * 内置字段的无渲染登记表(不含 Vue 组件)。
11
+ *
12
+ * 本模块只导出数据,不登记。调用方自行 `registerBuiltInFields(builtInFields)`,
13
+ * 或 `app.use(MagicForm)`(install 里会登记叠上 Vue 组件后的表)。
14
+ */
15
+ var builtInFields = {
16
+ text: {},
17
+ "img-upload": {},
18
+ number: {},
19
+ "number-range": { effect: effect$5 },
20
+ textarea: {},
21
+ hidden: {},
22
+ date: { effect: effect$2 },
23
+ datetime: { effect: effect$3 },
24
+ daterange: {},
25
+ timerange: {},
26
+ time: {},
27
+ checkbox: {},
28
+ switch: {},
29
+ "color-picker": {},
30
+ "checkbox-group": { effect: effect$1 },
31
+ "radio-group": {},
32
+ display: { effect: effect$4 },
33
+ link: {},
34
+ select: {},
35
+ cascader: {},
36
+ "dynamic-field": { effect },
37
+ component: {},
38
+ tab: { walk: expandTab },
39
+ row: { walk: expandRow },
40
+ "flex-layout": { walk: expandRow },
41
+ fieldset: { walk: expandFieldset },
42
+ panel: { walk: expandPanel },
43
+ step: { walk: expandStep },
44
+ table: { walk: expandTableGroupList },
45
+ "group-list": { walk: expandTableGroupList },
46
+ "table-group-list": { walk: expandTableGroupList }
47
+ };
48
+ //#endregion
49
+ export { builtInFields };