@tmagic/form 1.8.0-beta.25 → 1.8.0-beta.26
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.
- package/dist/es/Form.vue_vue_type_script_setup_true_lang.js +47 -120
- package/dist/es/FormBox.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/es/FormDialog.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/es/FormDrawer.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/es/containers/Col.vue_vue_type_script_setup_true_lang.js +3 -0
- package/dist/es/containers/Container.vue_vue_type_script_setup_true_lang.js +36 -51
- package/dist/es/containers/Fieldset.vue_vue_type_script_setup_true_lang.js +6 -0
- package/dist/es/containers/FlexLayout.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/containers/GroupList.vue_vue_type_script_setup_true_lang.js +37 -15
- package/dist/es/containers/GroupListItem.vue_vue_type_script_setup_true_lang.js +109 -97
- package/dist/es/containers/Panel.vue_vue_type_script_setup_true_lang.js +7 -2
- package/dist/es/containers/Row.vue_vue_type_script_setup_true_lang.js +3 -0
- package/dist/es/containers/Step.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/containers/Tabs.vue_vue_type_script_setup_true_lang.js +7 -0
- package/dist/es/containers/table/Table.vue_vue_type_script_setup_true_lang.js +7 -5
- package/dist/es/containers/table/useTableColumns.js +5 -13
- package/dist/es/containers/table-group-list/TableGroupList.vue_vue_type_script_setup_true_lang.js +71 -78
- package/dist/es/containers/table-group-list/useAdd.js +26 -16
- package/dist/es/containers/table-group-list/useScrollLastItemIntoView.js +72 -0
- package/dist/es/fields/Cascader.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/CheckboxGroup/Index.js +5 -0
- 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
- package/dist/es/fields/CheckboxGroup/effect.js +8 -0
- package/dist/es/fields/Date/Index.js +5 -0
- 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
- package/dist/es/fields/Date/effect.js +10 -0
- package/dist/es/fields/DateTime/Index.js +5 -0
- 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
- package/dist/es/fields/DateTime/effect.js +15 -0
- package/dist/es/fields/Display/Index.js +5 -0
- 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
- package/dist/es/fields/Display/effect.js +8 -0
- package/dist/es/fields/DynamicField/Index.js +5 -0
- 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
- package/dist/es/fields/DynamicField/effect.js +40 -0
- package/dist/es/fields/Link.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/Number.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/NumberRange/Index.js +5 -0
- 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
- package/dist/es/fields/NumberRange/effect.js +8 -0
- package/dist/es/fields/Select.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/fields/Text.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/headless.js +13 -0
- package/dist/es/index.js +17 -12
- package/dist/es/plugin.js +57 -41
- package/dist/es/schema.js +6 -8
- package/dist/es/style.css +81 -1
- package/dist/es/submitForm.js +44 -194
- package/dist/es/utils/builtInFields.js +49 -0
- package/dist/es/utils/collectFields.js +407 -0
- package/dist/es/utils/config.js +1 -8
- package/dist/es/utils/fieldInnerConfig.js +42 -0
- package/dist/es/utils/fieldValueEffects.js +80 -0
- package/dist/es/utils/form.js +96 -43
- package/dist/es/utils/formStateProxy.js +75 -0
- package/dist/es/utils/registerField.js +158 -0
- package/dist/es/utils/submitHeadless.js +54 -0
- package/dist/es/utils/tableGroupList.js +85 -0
- package/dist/es/utils/typeMatch.js +17 -19
- package/dist/es/utils/validateError.js +57 -0
- package/dist/es/utils/validateValues.js +115 -0
- package/dist/style.css +81 -1
- package/dist/themes/magic-admin.css +83 -7
- package/dist/tmagic-form-headless.umd.cjs +4044 -0
- package/dist/tmagic-form.umd.cjs +1975 -874
- package/package.json +10 -4
- package/src/Form.vue +49 -149
- package/src/FormBox.vue +5 -4
- package/src/FormDialog.vue +5 -5
- package/src/FormDrawer.vue +5 -5
- package/src/containers/Col.vue +2 -0
- package/src/containers/Container.vue +86 -157
- package/src/containers/Fieldset.vue +5 -0
- package/src/containers/FlexLayout.vue +2 -0
- package/src/containers/GroupList.vue +41 -11
- package/src/containers/GroupListItem.vue +83 -80
- package/src/containers/Panel.vue +3 -0
- package/src/containers/Row.vue +2 -0
- package/src/containers/Step.vue +2 -0
- package/src/containers/Tabs.vue +13 -0
- package/src/containers/table/ActionsColumn.vue +1 -1
- package/src/containers/table/Table.vue +4 -3
- package/src/containers/table/usePagination.ts +1 -1
- package/src/containers/table/useSortable.ts +1 -1
- package/src/containers/table/useTableColumns.ts +9 -19
- package/src/containers/table-group-list/TableGroupList.vue +43 -71
- package/src/containers/table-group-list/useAdd.ts +40 -25
- package/src/containers/table-group-list/useScrollLastItemIntoView.ts +94 -0
- package/src/fields/{CheckboxGroup.vue → CheckboxGroup/Index.vue} +3 -8
- package/src/fields/CheckboxGroup/effect.ts +27 -0
- package/src/fields/{Date.vue → Date/Index.vue} +2 -5
- package/src/fields/Date/effect.ts +29 -0
- package/src/fields/{DateTime.vue → DateTime/Index.vue} +2 -16
- package/src/fields/DateTime/effect.ts +36 -0
- package/src/fields/{Display.vue → Display/Index.vue} +3 -7
- package/src/fields/Display/effect.ts +28 -0
- package/src/fields/{DynamicField.vue → DynamicField/Index.vue} +11 -11
- package/src/fields/DynamicField/effect.ts +74 -0
- package/src/fields/{NumberRange.vue → NumberRange/Index.vue} +2 -6
- package/src/fields/NumberRange/effect.ts +27 -0
- package/src/headless.ts +78 -0
- package/src/index.ts +32 -15
- package/src/plugin.ts +63 -42
- package/src/schema.ts +6 -8
- package/src/submitForm.ts +81 -409
- package/src/theme/group-list.scss +107 -1
- package/src/theme/themes/magic-admin/index.scss +2 -8
- package/src/utils/builtInFields.ts +69 -0
- package/src/utils/collectFields.ts +561 -0
- package/src/utils/config.ts +1 -15
- package/src/utils/fieldInnerConfig.ts +127 -0
- package/src/utils/fieldValueEffects.ts +140 -0
- package/src/utils/form.ts +141 -75
- package/src/utils/formStateProxy.ts +104 -0
- package/src/utils/registerField.ts +308 -0
- package/src/utils/submitHeadless.ts +206 -0
- package/src/utils/tableGroupList.ts +120 -0
- package/src/utils/typeMatch.ts +19 -15
- package/src/utils/validateError.ts +92 -0
- package/src/utils/validateValues.ts +177 -0
- package/types/headless.d.ts +682 -0
- package/types/index.d.ts +631 -272
- package/dist/es/fields/CheckboxGroup.js +0 -5
- package/dist/es/fields/Date.js +0 -5
- package/dist/es/fields/DateTime.js +0 -5
- package/dist/es/fields/Display.js +0 -5
- package/dist/es/fields/DynamicField.js +0 -5
- package/dist/es/fields/NumberRange.js +0 -5
- package/dist/es/utils/silentLeafFieldTypes.js +0 -49
- package/src/utils/silentLeafFieldTypes.ts +0 -71
package/dist/tmagic-form.umd.cjs
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
(function(global, factory) {
|
|
2
|
-
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue"), require("dayjs"), require("dayjs/plugin/utc"), require("@tmagic/design"), require("
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue"), require("async-validator"), require("@tmagic/utils"), require("dayjs"), require("dayjs/plugin/utc.js"), require("@tmagic/design/headless"), require("dayjs/plugin/customParseFormat.js"), require("@tmagic/design"), require("@element-plus/icons-vue"), require("@popperjs/core")) : typeof define === "function" && define.amd ? define([
|
|
3
3
|
"exports",
|
|
4
4
|
"vue",
|
|
5
|
+
"async-validator",
|
|
6
|
+
"@tmagic/utils",
|
|
5
7
|
"dayjs",
|
|
6
|
-
"dayjs/plugin/utc",
|
|
8
|
+
"dayjs/plugin/utc.js",
|
|
9
|
+
"@tmagic/design/headless",
|
|
10
|
+
"dayjs/plugin/customParseFormat.js",
|
|
7
11
|
"@tmagic/design",
|
|
8
|
-
"@tmagic/utils",
|
|
9
|
-
"dayjs/plugin/customParseFormat",
|
|
10
12
|
"@element-plus/icons-vue",
|
|
11
13
|
"@popperjs/core"
|
|
12
|
-
], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.TMagicForm = {}, global.Vue, global.dayjs, global.
|
|
13
|
-
})(this, function(exports, vue, dayjs,
|
|
14
|
+
], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.TMagicForm = {}, global.Vue, global.async_validator, global._tmagic_utils, global.dayjs, global.dayjs_plugin_utc_js, global._tmagic_design_headless, global.dayjs_plugin_customParseFormat_js, global._tmagic_design, global._element_plus_icons_vue, global._popperjs_core));
|
|
15
|
+
})(this, function(exports, vue, async_validator, _tmagic_utils, dayjs, dayjs_plugin_utc_js, _tmagic_design_headless, dayjs_plugin_customParseFormat_js, _tmagic_design, _element_plus_icons_vue, _popperjs_core) {
|
|
14
16
|
Object.defineProperties(exports, {
|
|
15
17
|
__esModule: { value: true },
|
|
16
18
|
[Symbol.toStringTag]: { value: "Module" }
|
|
@@ -37,23 +39,22 @@
|
|
|
37
39
|
enumerable: true
|
|
38
40
|
}) : target, mod));
|
|
39
41
|
//#endregion
|
|
42
|
+
async_validator = __toESM(async_validator, 1);
|
|
40
43
|
dayjs = __toESM(dayjs, 1);
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
dayjs_plugin_utc_js = __toESM(dayjs_plugin_utc_js, 1);
|
|
45
|
+
dayjs_plugin_customParseFormat_js = __toESM(dayjs_plugin_customParseFormat_js, 1);
|
|
43
46
|
//#region packages/form/src/schema.ts
|
|
44
47
|
var FORM_DIFF_CONFIG_KEY = Symbol("mFormDiffConfig");
|
|
45
48
|
var FORM_TYPE_MATCH_VALID_KEY = Symbol("mFormTypeMatchValid");
|
|
46
49
|
/**
|
|
47
|
-
*
|
|
50
|
+
* 宿主业务上下文,由 `MForm`(或更外层的 Editor)通过 `provide` 下发。
|
|
48
51
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* 重型字段组件(如 vs-code)也可 inject 该标记跳过自身渲染,省去无谓的实例化开销。
|
|
52
|
+
* 嵌套表单(Link 子表单、FormBox、临时校验表单等)会自动继承最近祖先的 context,
|
|
53
|
+
* 无需层层透传。
|
|
52
54
|
*
|
|
53
|
-
*
|
|
54
|
-
* 字段组件可以安全跳过,接入前需逐一审计;业务扩展请使用 `registerSilentLeafFieldTypes`。
|
|
55
|
+
* 配置回调统一通过 `mForm.xxx` 读取,由 formState 的读穿 Proxy 落到这里。
|
|
55
56
|
*/
|
|
56
|
-
var
|
|
57
|
+
var FORM_CONTEXT_KEY = Symbol("mFormContext");
|
|
57
58
|
//#endregion
|
|
58
59
|
//#region node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/_freeGlobal.js
|
|
59
60
|
/** Detect free variable `global` from Node.js. */
|
|
@@ -2731,27 +2732,191 @@
|
|
|
2731
2732
|
return baseIsEqual(value, other);
|
|
2732
2733
|
}
|
|
2733
2734
|
//#endregion
|
|
2735
|
+
//#region node_modules/.pnpm/lodash-es@4.18.1/node_modules/lodash-es/isEqualWith.js
|
|
2736
|
+
/**
|
|
2737
|
+
* This method is like `_.isEqual` except that it accepts `customizer` which
|
|
2738
|
+
* is invoked to compare values. If `customizer` returns `undefined`, comparisons
|
|
2739
|
+
* are handled by the method instead. The `customizer` is invoked with up to
|
|
2740
|
+
* six arguments: (objValue, othValue [, index|key, object, other, stack]).
|
|
2741
|
+
*
|
|
2742
|
+
* @static
|
|
2743
|
+
* @memberOf _
|
|
2744
|
+
* @since 4.0.0
|
|
2745
|
+
* @category Lang
|
|
2746
|
+
* @param {*} value The value to compare.
|
|
2747
|
+
* @param {*} other The other value to compare.
|
|
2748
|
+
* @param {Function} [customizer] The function to customize comparisons.
|
|
2749
|
+
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
|
|
2750
|
+
* @example
|
|
2751
|
+
*
|
|
2752
|
+
* function isGreeting(value) {
|
|
2753
|
+
* return /^h(?:i|ello)$/.test(value);
|
|
2754
|
+
* }
|
|
2755
|
+
*
|
|
2756
|
+
* function customizer(objValue, othValue) {
|
|
2757
|
+
* if (isGreeting(objValue) && isGreeting(othValue)) {
|
|
2758
|
+
* return true;
|
|
2759
|
+
* }
|
|
2760
|
+
* }
|
|
2761
|
+
*
|
|
2762
|
+
* var array = ['hello', 'goodbye'];
|
|
2763
|
+
* var other = ['hi', 'goodbye'];
|
|
2764
|
+
*
|
|
2765
|
+
* _.isEqualWith(array, other, customizer);
|
|
2766
|
+
* // => true
|
|
2767
|
+
*/
|
|
2768
|
+
function isEqualWith(value, other, customizer) {
|
|
2769
|
+
customizer = typeof customizer == "function" ? customizer : void 0;
|
|
2770
|
+
var result = customizer ? customizer(value, other) : void 0;
|
|
2771
|
+
return result === void 0 ? baseIsEqual(value, other, void 0, customizer) : !!result;
|
|
2772
|
+
}
|
|
2773
|
+
//#endregion
|
|
2774
|
+
//#region packages/form/src/utils/fieldInnerConfig.ts
|
|
2775
|
+
/** 内置内部配置(由 `registerBuiltInFields` 写入;`clearFields` 不会清掉) */
|
|
2776
|
+
var builtInInnerConfigs = /* @__PURE__ */ new Map();
|
|
2777
|
+
/** 业务侧登记的内部配置 */
|
|
2778
|
+
var extraInnerConfigs = /* @__PURE__ */ new Map();
|
|
2779
|
+
/**
|
|
2780
|
+
* 登记复合字段的内部配置:无渲染校验遇到该 type 时,用返回值继续遍历内部字段。
|
|
2781
|
+
*
|
|
2782
|
+
* `type` 会按 Container 的规则归一化为中划线形式(`codeSelect` 与 `code-select` 等价)。
|
|
2783
|
+
* 重复登记以最后一次为准,便于业务侧覆盖内置实现。
|
|
2784
|
+
* `builtIn` 登记不受 `deleteFieldInnerConfig` / `clearFieldInnerConfigs` 影响。
|
|
2785
|
+
*
|
|
2786
|
+
* @param type - 字段 type
|
|
2787
|
+
* @param resolve - innerConfig 回调
|
|
2788
|
+
* @param [builtIn=false] - 是否写入内置表
|
|
2789
|
+
*/
|
|
2790
|
+
var registerFieldInnerConfig = (type, resolve, builtIn = false) => {
|
|
2791
|
+
if (typeof type !== "string" || !type || typeof resolve !== "function") return;
|
|
2792
|
+
(builtIn ? builtInInnerConfigs : extraInnerConfigs).set((0, _tmagic_utils.toLine)(type), resolve);
|
|
2793
|
+
};
|
|
2794
|
+
/**
|
|
2795
|
+
* 获取指定 type 的 innerConfig 回调(业务侧优先于内置)。
|
|
2796
|
+
*
|
|
2797
|
+
* @param type - 字段 type
|
|
2798
|
+
* @returns innerConfig 回调;未登记则为 `undefined`
|
|
2799
|
+
*/
|
|
2800
|
+
var getFieldInnerConfig = (type) => {
|
|
2801
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
2802
|
+
return extraInnerConfigs.get(key) ?? builtInInnerConfigs.get(key);
|
|
2803
|
+
};
|
|
2804
|
+
/**
|
|
2805
|
+
* 删除业务侧登记的 innerConfig(不影响内置)。
|
|
2806
|
+
*
|
|
2807
|
+
* @param type - 字段 type
|
|
2808
|
+
* @returns 是否删除成功
|
|
2809
|
+
*/
|
|
2810
|
+
var deleteFieldInnerConfig = (type) => extraInnerConfigs.delete((0, _tmagic_utils.toLine)(type));
|
|
2811
|
+
/** 清空业务侧登记的 innerConfig(不影响内置;主要用于单测)。 */
|
|
2812
|
+
var clearFieldInnerConfigs = () => extraInnerConfigs.clear();
|
|
2813
|
+
//#endregion
|
|
2814
|
+
//#region packages/form/src/utils/fieldValueEffects.ts
|
|
2815
|
+
/**
|
|
2816
|
+
* @fileoverview 叶子字段登记表:哪些 type 是叶子,以及该字段对表单值的初始化写入。
|
|
2817
|
+
*
|
|
2818
|
+
* 无渲染校验据此判定该 type 没有属于父表单的嵌套字段。
|
|
2819
|
+
* 业务自定义字段通过 `registerField` / `registerFields` 登记。
|
|
2820
|
+
* 内置叶子字段由 `MagicForm.install` 的 `registerBuiltInFields` 写入。
|
|
2821
|
+
* 未登记且没有 innerConfig 的 type:有 `items` 会下钻子项,自身有 `rules` 会校验自身。
|
|
2822
|
+
*
|
|
2823
|
+
* 这里只是登记表。各字段的 effect 写在对应组件同目录的 `fields/<Field>/effect.ts`,
|
|
2824
|
+
* 执行收口在 `collectFields` 的 `applyMountValueEffects`:渲染(`Form.vue`)
|
|
2825
|
+
* 与无渲染(`validateValues`)都在表单值初始化完成后调用它一次,
|
|
2826
|
+
* 字段组件自身不再在 setup 里改写 model。
|
|
2827
|
+
*
|
|
2828
|
+
* @module fieldValueEffects
|
|
2829
|
+
*/
|
|
2830
|
+
/** 内置叶子字段(由 `MagicForm.install` 写入;clearFields 不会清掉) */
|
|
2831
|
+
var builtInLeafFieldTypes = /* @__PURE__ */ new Set();
|
|
2832
|
+
var builtInMountValueEffects = /* @__PURE__ */ new Map();
|
|
2833
|
+
/** 业务侧登记的叶子字段与其挂载副作用 */
|
|
2834
|
+
var extraLeafFieldTypes = /* @__PURE__ */ new Set();
|
|
2835
|
+
var extraMountValueEffects = /* @__PURE__ */ new Map();
|
|
2836
|
+
/**
|
|
2837
|
+
* 登记一个叶子字段 type。由 `registerField` 调用,业务侧请走 `registerField`。
|
|
2838
|
+
*
|
|
2839
|
+
* 传了 `effect` 会覆盖该 type 已有的 extra effect(含覆盖内置);不传则清掉 extra effect,
|
|
2840
|
+
* 内置 effect 仍生效。
|
|
2841
|
+
*
|
|
2842
|
+
* @param type - 字段 type
|
|
2843
|
+
* @param [effect] - 挂载时改写 model 的副作用
|
|
2844
|
+
* @param [builtIn=false] - 是否写入内置表
|
|
2845
|
+
*/
|
|
2846
|
+
var registerLeafFieldType = (type, effect, builtIn = false) => {
|
|
2847
|
+
if (typeof type !== "string" || !type) return;
|
|
2848
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
2849
|
+
const types = builtIn ? builtInLeafFieldTypes : extraLeafFieldTypes;
|
|
2850
|
+
const effects = builtIn ? builtInMountValueEffects : extraMountValueEffects;
|
|
2851
|
+
types.add(key);
|
|
2852
|
+
if (typeof effect === "function") effects.set(key, effect);
|
|
2853
|
+
else if (!builtIn) effects.delete(key);
|
|
2854
|
+
};
|
|
2855
|
+
/**
|
|
2856
|
+
* 是否为叶子字段 type(内置 ∪ 已登记)。
|
|
2857
|
+
*
|
|
2858
|
+
* @param type - 字段 type
|
|
2859
|
+
* @returns 是否为叶子
|
|
2860
|
+
*/
|
|
2861
|
+
var isLeafFieldType = (type) => {
|
|
2862
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
2863
|
+
return extraLeafFieldTypes.has(key) || builtInLeafFieldTypes.has(key);
|
|
2864
|
+
};
|
|
2865
|
+
/**
|
|
2866
|
+
* 获取指定 type 挂载时改写 model 的副作用(已登记的优先于内置)。
|
|
2867
|
+
*
|
|
2868
|
+
* @param type - 字段 type
|
|
2869
|
+
* @returns 副作用函数;未登记则为 `undefined`
|
|
2870
|
+
*/
|
|
2871
|
+
var getFieldMountValueEffect = (type) => {
|
|
2872
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
2873
|
+
return extraMountValueEffects.get(key) ?? builtInMountValueEffects.get(key);
|
|
2874
|
+
};
|
|
2875
|
+
/**
|
|
2876
|
+
* 删除业务侧登记的叶子字段(不影响内置;主要用于单测)。
|
|
2877
|
+
*
|
|
2878
|
+
* @param type - 字段 type
|
|
2879
|
+
* @returns 是否删除成功
|
|
2880
|
+
*/
|
|
2881
|
+
var deleteLeafFieldType = (type) => {
|
|
2882
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
2883
|
+
extraMountValueEffects.delete(key);
|
|
2884
|
+
return extraLeafFieldTypes.delete(key);
|
|
2885
|
+
};
|
|
2886
|
+
/** 清空业务侧登记的叶子字段(不影响内置;主要用于单测)。 */
|
|
2887
|
+
var clearLeafFieldTypes = () => {
|
|
2888
|
+
extraLeafFieldTypes.clear();
|
|
2889
|
+
extraMountValueEffects.clear();
|
|
2890
|
+
};
|
|
2891
|
+
//#endregion
|
|
2892
|
+
//#region packages/form/src/utils/config.ts
|
|
2893
|
+
var $MAGIC_FORM = {};
|
|
2894
|
+
var isGlobalFlat = (0, vue.ref)(false);
|
|
2895
|
+
var setConfig = (option) => {
|
|
2896
|
+
$MAGIC_FORM = option;
|
|
2897
|
+
isGlobalFlat.value = option.flat ?? false;
|
|
2898
|
+
};
|
|
2899
|
+
var getConfig = (key) => $MAGIC_FORM[key];
|
|
2900
|
+
//#endregion
|
|
2734
2901
|
//#region packages/form/src/utils/typeMatch.ts
|
|
2735
|
-
dayjs.default.extend(
|
|
2736
|
-
var
|
|
2902
|
+
dayjs.default.extend(dayjs_plugin_customParseFormat_js.default);
|
|
2903
|
+
var extraTypeMatchRules = /* @__PURE__ */ new Map();
|
|
2904
|
+
var builtInTypeMatchRules = /* @__PURE__ */ new Map();
|
|
2737
2905
|
var isPromise = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
2738
|
-
/** 注册或覆盖某个字段 type 的 typeMatch
|
|
2739
|
-
var registerTypeMatchRule = (type, validator) => {
|
|
2740
|
-
|
|
2741
|
-
};
|
|
2742
|
-
/** 批量注册 typeMatch 校验规则 */
|
|
2743
|
-
var registerTypeMatchRules = (rules) => {
|
|
2744
|
-
Object.entries(rules).forEach(([type, validator]) => {
|
|
2745
|
-
registerTypeMatchRule(type, validator);
|
|
2746
|
-
});
|
|
2906
|
+
/** 注册或覆盖某个字段 type 的 typeMatch 校验规则。`builtIn` 登记不受 `clearTypeMatchRules` / `deleteTypeMatchRule` 影响。 */
|
|
2907
|
+
var registerTypeMatchRule = (type, validator, builtIn = false) => {
|
|
2908
|
+
(builtIn ? builtInTypeMatchRules : extraTypeMatchRules).set((0, _tmagic_utils.toLine)(type), validator);
|
|
2747
2909
|
};
|
|
2748
|
-
/** 获取某个字段 type
|
|
2749
|
-
var getTypeMatchRule = (type) =>
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2910
|
+
/** 获取某个字段 type 的 typeMatch 校验规则(业务侧优先于内置) */
|
|
2911
|
+
var getTypeMatchRule = (type) => {
|
|
2912
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
2913
|
+
return extraTypeMatchRules.get(key) ?? builtInTypeMatchRules.get(key);
|
|
2914
|
+
};
|
|
2915
|
+
/** 删除业务侧 typeMatch 校验规则(不影响内置) */
|
|
2916
|
+
var deleteTypeMatchRule = (type) => extraTypeMatchRules.delete((0, _tmagic_utils.toLine)(type));
|
|
2917
|
+
/** 清空业务侧 typeMatch 校验规则(不影响内置) */
|
|
2753
2918
|
var clearTypeMatchRules = () => {
|
|
2754
|
-
|
|
2919
|
+
extraTypeMatchRules.clear();
|
|
2755
2920
|
};
|
|
2756
2921
|
/** 本地解析配置函数,避免与 form.ts 循环依赖 */
|
|
2757
2922
|
var resolveConfig = (mForm, config, props) => {
|
|
@@ -2884,7 +3049,7 @@
|
|
|
2884
3049
|
*/
|
|
2885
3050
|
var defaultMessage = (message, fallback, suggestion) => {
|
|
2886
3051
|
if (message) return message;
|
|
2887
|
-
return (0,
|
|
3052
|
+
return (0, _tmagic_design_headless.appendValidateSuggestion)(fallback, suggestion);
|
|
2888
3053
|
};
|
|
2889
3054
|
var isEmptyValue = (value) => value === void 0 || value === null || value === "";
|
|
2890
3055
|
var isEmptyArray = (value) => Array.isArray(value) && value.length === 0;
|
|
@@ -3215,8 +3380,81 @@
|
|
|
3215
3380
|
};
|
|
3216
3381
|
};
|
|
3217
3382
|
//#endregion
|
|
3383
|
+
//#region packages/form/src/utils/formStateProxy.ts
|
|
3384
|
+
/** 无上下文时共用的空对象,避免每次读取都分配新对象 */
|
|
3385
|
+
var EMPTY_CONTEXT = Object.freeze({});
|
|
3386
|
+
/**
|
|
3387
|
+
* 按优先级分层合并上下文,**靠后的层优先**。
|
|
3388
|
+
*
|
|
3389
|
+
* 用读穿 Proxy 而非 `{ ...a, ...b }`:展开会立即执行 accessor,
|
|
3390
|
+
* 而宿主允许用 `{ get stage() { ... } }` 这类读时求值的描述符。
|
|
3391
|
+
*/
|
|
3392
|
+
var mergeFormContexts = (...layers) => {
|
|
3393
|
+
const stack = layers.filter((layer) => Boolean(layer));
|
|
3394
|
+
if (stack.length === 0) return EMPTY_CONTEXT;
|
|
3395
|
+
if (stack.length === 1) return stack[0];
|
|
3396
|
+
const ordered = stack.slice().reverse();
|
|
3397
|
+
const owner = (k) => ordered.find((layer) => Reflect.has(layer, k));
|
|
3398
|
+
return new Proxy({}, {
|
|
3399
|
+
get: (_t, k) => owner(k)?.[k],
|
|
3400
|
+
has: (_t, k) => Boolean(owner(k)),
|
|
3401
|
+
ownKeys: () => [...new Set(ordered.flatMap((layer) => Reflect.ownKeys(layer)))],
|
|
3402
|
+
getOwnPropertyDescriptor: (_t, k) => {
|
|
3403
|
+
for (const layer of ordered) {
|
|
3404
|
+
const descriptor = Object.getOwnPropertyDescriptor(layer, k);
|
|
3405
|
+
if (descriptor) return {
|
|
3406
|
+
...descriptor,
|
|
3407
|
+
configurable: true
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
});
|
|
3412
|
+
};
|
|
3413
|
+
/**
|
|
3414
|
+
* 将 coreState 与宿主业务上下文关联:读取时优先 core,miss 再读穿到 context。
|
|
3415
|
+
*
|
|
3416
|
+
* - `get` 用属性访问而非 `Reflect.get(t, k, receiver)`,避免破坏 Vue reactive 的 `__v_raw`;
|
|
3417
|
+
* - symbol 键一律只走 core,四个 trap 保持一致。context 是业务数据袋,不承载 symbol 键,
|
|
3418
|
+
* 把 Vue / 工具链的内部 symbol 隔离在 core 上才能让 `toRaw` / `isReactive` 判定正确;
|
|
3419
|
+
* - `ownKeys` + `getOwnPropertyDescriptor` 保证 `Object.entries(formState)` 能枚举到扩展字段
|
|
3420
|
+
* (admin-web-next 的 `pickPanelFormStateExtendFields` 依赖此语义);
|
|
3421
|
+
* - `set` 写入 coreState,第三方 `formState.xxx = v` 仍生效且优先于 context。
|
|
3422
|
+
*
|
|
3423
|
+
* 独立成文件,避免与 `form.ts` ↔ `typeMatch.ts` 形成循环依赖。
|
|
3424
|
+
*/
|
|
3425
|
+
var createFormStateProxy = (coreState, getContext) => {
|
|
3426
|
+
const resolve = () => (typeof getContext === "function" ? getContext() : (0, vue.unref)(getContext)) || EMPTY_CONTEXT;
|
|
3427
|
+
return new Proxy(coreState, {
|
|
3428
|
+
get(t, k) {
|
|
3429
|
+
if (typeof k === "symbol") return Reflect.get(t, k);
|
|
3430
|
+
const v = t[k];
|
|
3431
|
+
if (v !== void 0 || Reflect.has(t, k)) return v;
|
|
3432
|
+
return resolve()[k];
|
|
3433
|
+
},
|
|
3434
|
+
set(t, k, value) {
|
|
3435
|
+
t[k] = value;
|
|
3436
|
+
return true;
|
|
3437
|
+
},
|
|
3438
|
+
has: (t, k) => Reflect.has(t, k) || typeof k !== "symbol" && k in resolve(),
|
|
3439
|
+
ownKeys: (t) => [.../* @__PURE__ */ new Set([...Reflect.ownKeys(t), ...Reflect.ownKeys(resolve()).filter((k) => typeof k !== "symbol")])],
|
|
3440
|
+
getOwnPropertyDescriptor: (t, k) => {
|
|
3441
|
+
const own = Reflect.getOwnPropertyDescriptor(t, k);
|
|
3442
|
+
if (own) return own;
|
|
3443
|
+
if (typeof k === "symbol") return void 0;
|
|
3444
|
+
const ctx = resolve();
|
|
3445
|
+
if (!(k in ctx)) return void 0;
|
|
3446
|
+
return {
|
|
3447
|
+
configurable: true,
|
|
3448
|
+
enumerable: true,
|
|
3449
|
+
writable: true,
|
|
3450
|
+
value: ctx[k]
|
|
3451
|
+
};
|
|
3452
|
+
}
|
|
3453
|
+
});
|
|
3454
|
+
};
|
|
3455
|
+
//#endregion
|
|
3218
3456
|
//#region packages/form/src/utils/form.ts
|
|
3219
|
-
var isTDesignAdapter = () => (0,
|
|
3457
|
+
var isTDesignAdapter = () => (0, _tmagic_design_headless.getDesignConfig)("adapterType") === "tdesign-vue-next";
|
|
3220
3458
|
/**
|
|
3221
3459
|
* 将 async-validator(Element Plus)风格的 validator 适配到当前 UI 库。
|
|
3222
3460
|
* TDesign 调用签名为 `(val) => boolean | CustomValidateObj | Promise`,无 callback。
|
|
@@ -3326,6 +3564,79 @@
|
|
|
3326
3564
|
if (type === "daterange" && !names) return [];
|
|
3327
3565
|
return "";
|
|
3328
3566
|
};
|
|
3567
|
+
/**
|
|
3568
|
+
* formState 中与表单 props 无关的公共部分:字段注册表、消息组件、post 请求。
|
|
3569
|
+
*
|
|
3570
|
+
* 由渲染式表单(`Form.vue`)与无渲染校验(`createHeadlessFormState`)共用,
|
|
3571
|
+
* 保证两者提供给字段配置(`onChange` / `validator` / 异步 options 等)的能力一致。
|
|
3572
|
+
*/
|
|
3573
|
+
var fallbackMessage = {
|
|
3574
|
+
error: (msg) => console.error(msg),
|
|
3575
|
+
success: (msg) => console.log(msg),
|
|
3576
|
+
warning: (msg) => console.warn(msg),
|
|
3577
|
+
info: (msg) => console.info(msg),
|
|
3578
|
+
closeAll: () => void 0
|
|
3579
|
+
};
|
|
3580
|
+
var fallbackMessageBox = {
|
|
3581
|
+
alert: (msg) => console.log(msg),
|
|
3582
|
+
confirm: (msg) => console.log(msg),
|
|
3583
|
+
close: (msg) => console.log(msg)
|
|
3584
|
+
};
|
|
3585
|
+
var createFormStateBase = (ui) => {
|
|
3586
|
+
const fields = /* @__PURE__ */ new Map();
|
|
3587
|
+
const requestFuc = getConfig("request");
|
|
3588
|
+
return {
|
|
3589
|
+
fields,
|
|
3590
|
+
setField: (prop, field) => fields.set(prop, field),
|
|
3591
|
+
getField: (prop) => fields.get(prop),
|
|
3592
|
+
deleteField: (prop) => fields.delete(prop),
|
|
3593
|
+
$messageBox: ui?.$messageBox ?? fallbackMessageBox,
|
|
3594
|
+
$message: ui?.$message ?? fallbackMessage,
|
|
3595
|
+
post: (options) => {
|
|
3596
|
+
if (requestFuc) return requestFuc({
|
|
3597
|
+
method: "POST",
|
|
3598
|
+
...options
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3601
|
+
};
|
|
3602
|
+
};
|
|
3603
|
+
/**
|
|
3604
|
+
* 配置项的 `name` 是否可用于从 model 中下钻取值。
|
|
3605
|
+
*
|
|
3606
|
+
* 供 `Container.vue` 与无渲染校验(`collectValidatableFields`)共用,两者对「该不该下钻」
|
|
3607
|
+
* 的判断必须一致。
|
|
3608
|
+
*/
|
|
3609
|
+
var isValidName = (name) => {
|
|
3610
|
+
const valueType = typeof name;
|
|
3611
|
+
if (valueType !== "string" && valueType !== "symbol" && valueType !== "number") return false;
|
|
3612
|
+
if (name === "") return false;
|
|
3613
|
+
if (valueType === "number") return name >= 0;
|
|
3614
|
+
return true;
|
|
3615
|
+
};
|
|
3616
|
+
/**
|
|
3617
|
+
* 在 `prop` 后追加一段路径。
|
|
3618
|
+
*
|
|
3619
|
+
* 用于容器把 `prop` 透传给子级时再拼上索引 / key(如 group-list 的行、table 的行)。
|
|
3620
|
+
*/
|
|
3621
|
+
var appendProp = (prop = "", key) => `${prop}${prop ? "." : ""}${key}`;
|
|
3622
|
+
/**
|
|
3623
|
+
* 由父级 `prop` 与配置项 `name` 拼出该配置项的完整 `prop`(即 FormItem 的 prop)。
|
|
3624
|
+
*
|
|
3625
|
+
* 调用方传入的 name 已按 `config.name || ''` 归一化,空 name 表示该配置项不占据值路径上的一层,
|
|
3626
|
+
* 此时沿用父级 `prop`。
|
|
3627
|
+
*/
|
|
3628
|
+
var getItemProp = (prop = "", name = "") => name === "" ? prop ?? "" : appendProp(prop, name);
|
|
3629
|
+
/**
|
|
3630
|
+
* 归一化配置项的 `type`:求值动态 type、剥离容器语义的 type、驼峰转中划线、补默认值。
|
|
3631
|
+
*
|
|
3632
|
+
* 供 `Container.vue` 与无渲染校验共用,保证两者分派到的 type 完全一致。
|
|
3633
|
+
*/
|
|
3634
|
+
var resolveItemType = (mForm, config, props) => {
|
|
3635
|
+
let type = "type" in (config || {}) ? config.type : "";
|
|
3636
|
+
type = type && filterFunction(mForm, type, props);
|
|
3637
|
+
if (type === "form" || type === "container") return "";
|
|
3638
|
+
return type?.replace(/([A-Z])/g, "-$1").toLowerCase() || (config?.items ? "" : "text");
|
|
3639
|
+
};
|
|
3329
3640
|
var filterFunction = (mForm, config, props) => {
|
|
3330
3641
|
if (typeof config === "function") return config(mForm, {
|
|
3331
3642
|
values: (0, vue.readonly)(mForm?.initValues || {}),
|
|
@@ -3345,18 +3656,18 @@
|
|
|
3345
3656
|
if (config === false) return false;
|
|
3346
3657
|
return true;
|
|
3347
3658
|
};
|
|
3348
|
-
var
|
|
3659
|
+
var buildRules = function(mForm, r = [], props, typeMatchValid, adapt = (validator) => validator) {
|
|
3349
3660
|
let rules = cloneDeep(r);
|
|
3350
3661
|
if (typeof rules === "object" && !Array.isArray(rules)) rules = [rules];
|
|
3351
|
-
if (typeMatchValid
|
|
3662
|
+
if ((0, vue.unref)(typeMatchValid) && !rules.some((r) => typeof r.typeMatch !== "undefined")) rules.push({ typeMatch: true });
|
|
3352
3663
|
return rules.map((item) => {
|
|
3353
3664
|
if (item.typeMatch) {
|
|
3354
|
-
item.validator =
|
|
3665
|
+
item.validator = adapt(createTypeMatchValidator(mForm, props, item));
|
|
3355
3666
|
return item;
|
|
3356
3667
|
}
|
|
3357
3668
|
if (typeof item.validator === "function") {
|
|
3358
3669
|
const fnc = item.validator;
|
|
3359
|
-
item.validator =
|
|
3670
|
+
item.validator = adapt((rule, value, callback, source, options) => fnc({
|
|
3360
3671
|
rule,
|
|
3361
3672
|
value: props.config.names ? props.model : value,
|
|
3362
3673
|
callback,
|
|
@@ -3377,6 +3688,18 @@
|
|
|
3377
3688
|
return true;
|
|
3378
3689
|
});
|
|
3379
3690
|
};
|
|
3691
|
+
var getRules = function(mForm, r = [], props, typeMatchValid) {
|
|
3692
|
+
return buildRules(mForm, r, props, typeMatchValid, adaptFormValidator);
|
|
3693
|
+
};
|
|
3694
|
+
/**
|
|
3695
|
+
* 与 `getRules` 相同,但 validator 保持 async-validator(Element Plus)原生签名,不做 UI 库适配。
|
|
3696
|
+
*
|
|
3697
|
+
* 供无渲染校验(`validateValues`)使用:它直接把规则交给 async-validator 执行,
|
|
3698
|
+
* 不经过 TDesign 的 `validator(val)` 调用约定,因此不能套 `adaptFormValidator`。
|
|
3699
|
+
*/
|
|
3700
|
+
var getNativeRules = function(mForm, r = [], props, typeMatchValid) {
|
|
3701
|
+
return buildRules(mForm, r, props, typeMatchValid);
|
|
3702
|
+
};
|
|
3380
3703
|
var initValue = async (mForm, { initValues, config }) => {
|
|
3381
3704
|
if (!Array.isArray(config)) throw new Error("config应该为数组");
|
|
3382
3705
|
const initValuesCopy = cloneDeep(initValues);
|
|
@@ -3393,7 +3716,7 @@
|
|
|
3393
3716
|
let time;
|
|
3394
3717
|
if (["x", "timestamp"].includes(format)) time = (0, dayjs.default)(Number.isNaN(Number(v)) ? v : Number(v)).valueOf();
|
|
3395
3718
|
else if (typeof v === "string" && v.includes("Z") || v instanceof Date) {
|
|
3396
|
-
dayjs.default.extend(
|
|
3719
|
+
dayjs.default.extend(dayjs_plugin_utc_js.default);
|
|
3397
3720
|
time = (0, dayjs.default)(v).utcOffset(8).format(format);
|
|
3398
3721
|
} else time = (0, dayjs.default)(v).format(format);
|
|
3399
3722
|
if (time !== "Invalid Date") return time;
|
|
@@ -3417,47 +3740,712 @@
|
|
|
3417
3740
|
if (order === "ascending") data.sort((a, b) => a[prop] - b[prop]);
|
|
3418
3741
|
else if (order === "descending") data.sort((a, b) => b[prop] - a[prop]);
|
|
3419
3742
|
};
|
|
3743
|
+
var createObjectProp = (prop, key, name) => {
|
|
3744
|
+
if (prop === "") return key;
|
|
3745
|
+
const itemPath = `${prop}`.split(".");
|
|
3746
|
+
if (name) {
|
|
3747
|
+
if (`${itemPath[itemPath.length - 1]}` === `${name}`) return `${[...itemPath.slice(0, -1), key].join(".")}`;
|
|
3748
|
+
}
|
|
3749
|
+
return `${[...itemPath, key].join(".")}`;
|
|
3750
|
+
};
|
|
3751
|
+
//#endregion
|
|
3752
|
+
//#region packages/form/src/utils/tableGroupList.ts
|
|
3420
3753
|
/**
|
|
3421
|
-
*
|
|
3754
|
+
* table / group-list 是同一份配置的两种展示形态,可以互相切换(`TableGroupList.vue`)。
|
|
3422
3755
|
*
|
|
3423
|
-
*
|
|
3424
|
-
*
|
|
3425
|
-
*
|
|
3426
|
-
|
|
3756
|
+
* 本模块收口两种形态之间的配置派生,以及各形态下「子项配置怎么算出来」的规则,
|
|
3757
|
+
* 供渲染(`TableGroupList.vue` / `GroupListItem.vue` / `useTableColumns.ts`)与无渲染校验
|
|
3758
|
+
* (`collectValidatableFields`)共用,避免两条链路各写一份而产生偏差。
|
|
3759
|
+
*/
|
|
3760
|
+
/** group-list 形态的 type(兼容驼峰与中划线两种写法) */
|
|
3761
|
+
var isGroupListType = (type) => type === "groupList" || type === "group-list";
|
|
3762
|
+
/** 按 label 文案长度估算 label 宽度(中文按 20px、其他按 8px,最小 80px) */
|
|
3763
|
+
var calcLabelWidth = (label) => {
|
|
3764
|
+
if (!label) return "0px";
|
|
3765
|
+
const zhLength = label.match(/[^\x00-\xff]/g)?.length || 0;
|
|
3766
|
+
const chLength = label.length - zhLength;
|
|
3767
|
+
return `${Math.max(chLength * 8 + zhLength * 20, 80)}px`;
|
|
3768
|
+
};
|
|
3769
|
+
/** 由 group-list 形态的配置派生出 table 形态所需的配置;本身已是 table 形态则原样返回 */
|
|
3770
|
+
var toTableConfig = (config) => {
|
|
3771
|
+
if (!isGroupListType(config.type)) return config;
|
|
3772
|
+
const source = config;
|
|
3773
|
+
return {
|
|
3774
|
+
...config,
|
|
3775
|
+
type: "table",
|
|
3776
|
+
groupItems: source.items,
|
|
3777
|
+
items: source.tableItems || source.items.map((item) => ({
|
|
3778
|
+
...item,
|
|
3779
|
+
label: item.label || item.text,
|
|
3780
|
+
text: null
|
|
3781
|
+
}))
|
|
3782
|
+
};
|
|
3783
|
+
};
|
|
3784
|
+
/** 由 table 形态的配置派生出 group-list 形态所需的配置;本身已是 group-list 形态则原样返回 */
|
|
3785
|
+
var toGroupListConfig = (config) => {
|
|
3786
|
+
if (isGroupListType(config.type)) return config;
|
|
3787
|
+
const source = config;
|
|
3788
|
+
return {
|
|
3789
|
+
...config,
|
|
3790
|
+
type: "groupList",
|
|
3791
|
+
tableItems: source.items,
|
|
3792
|
+
items: source.groupItems || source.items.map((item) => {
|
|
3793
|
+
const text = item.text || item.label;
|
|
3794
|
+
return {
|
|
3795
|
+
...item,
|
|
3796
|
+
text,
|
|
3797
|
+
labelWidth: calcLabelWidth(text),
|
|
3798
|
+
span: item.span || 12
|
|
3799
|
+
};
|
|
3800
|
+
})
|
|
3801
|
+
};
|
|
3802
|
+
};
|
|
3803
|
+
/**
|
|
3804
|
+
* group-list 每一行渲染的 row 容器配置(`GroupListItem.vue` 渲染的 `Container` 的 config)。
|
|
3427
3805
|
*
|
|
3428
|
-
*
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3806
|
+
* `keyProp` 由调用方从 `mForm.keyProp` 取,默认 `__key`。
|
|
3807
|
+
*/
|
|
3808
|
+
var getGroupListRowConfig = (config, index, keyProp) => {
|
|
3809
|
+
const key = keyProp || "__key";
|
|
3810
|
+
return {
|
|
3811
|
+
type: "row",
|
|
3812
|
+
span: config.span || 24,
|
|
3813
|
+
items: config.items,
|
|
3814
|
+
labelWidth: config.labelWidth,
|
|
3815
|
+
labelPosition: config.labelPosition,
|
|
3816
|
+
[key]: `${config[key]}${String(index)}`
|
|
3817
|
+
};
|
|
3818
|
+
};
|
|
3819
|
+
/**
|
|
3820
|
+
* 列是否会被渲染成一列(`hidden` 列不渲染,`display` 为假的列也不渲染)。
|
|
3432
3821
|
*
|
|
3433
|
-
*
|
|
3434
|
-
* 否则以普通字段形式赋值会让 proxy 的 set trap 抛出
|
|
3435
|
-
* `TypeError: 'set' on proxy: trap returned falsish`。
|
|
3822
|
+
* `display` 的求值上下文是表格自身的 props,由调用方通过 `evalDisplay` 注入。
|
|
3436
3823
|
*/
|
|
3437
|
-
var
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3824
|
+
var isTableColumnRendered = (column, evalDisplay) => column.type !== "hidden" && Boolean(evalDisplay(column.display));
|
|
3825
|
+
/**
|
|
3826
|
+
* 单元格内 `Container` 实际收到的列配置:按行求值 `itemsFunction`,并去掉已在列层面消费过的 `display`。
|
|
3827
|
+
*/
|
|
3828
|
+
var makeTableColumnConfig = (column, row) => {
|
|
3829
|
+
const newConfig = cloneDeep(column);
|
|
3830
|
+
if (typeof column.itemsFunction === "function") newConfig.items = column.itemsFunction(row);
|
|
3831
|
+
delete newConfig.display;
|
|
3832
|
+
return newConfig;
|
|
3833
|
+
};
|
|
3834
|
+
//#endregion
|
|
3835
|
+
//#region packages/form/src/utils/collectFields.ts
|
|
3836
|
+
/** 已登记的 innerConfig 回调自身抛错时抛出(机制故障,不是漏登记) */
|
|
3837
|
+
var FieldInnerConfigError = class extends Error {
|
|
3838
|
+
code = "FIELD_INNER_CONFIG";
|
|
3839
|
+
type;
|
|
3840
|
+
prop;
|
|
3841
|
+
constructor(type, prop, cause) {
|
|
3842
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
3843
|
+
super(`[MForm] innerConfig for "${type}" at "${prop}" failed: ${reason}`);
|
|
3844
|
+
this.name = "FieldInnerConfigError";
|
|
3845
|
+
this.type = type;
|
|
3846
|
+
this.prop = prop;
|
|
3847
|
+
if (cause instanceof Error) this.cause = cause;
|
|
3848
|
+
}
|
|
3849
|
+
};
|
|
3850
|
+
var isFieldInnerConfigError = (error) => error instanceof FieldInnerConfigError || typeof error === "object" && error !== null && error.code === "FIELD_INNER_CONFIG";
|
|
3851
|
+
/** `effects` 模式下遍历全部配置,不受 display / 折叠状态影响 */
|
|
3852
|
+
var ignoresDisplay = (ctx) => ctx.mode === "effects";
|
|
3853
|
+
var extraContainerWalkers = /* @__PURE__ */ new Map();
|
|
3854
|
+
var builtInContainerWalkers = /* @__PURE__ */ new Map();
|
|
3855
|
+
var registerContainerWalker = (type, walker, builtIn = false) => {
|
|
3856
|
+
if (typeof type !== "string" || !type || typeof walker !== "function") return;
|
|
3857
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
3858
|
+
if (builtIn) {
|
|
3859
|
+
builtInContainerWalkers.set(key, walker);
|
|
3860
|
+
return;
|
|
3861
|
+
}
|
|
3862
|
+
extraContainerWalkers.set(key, walker);
|
|
3863
|
+
};
|
|
3864
|
+
var getContainerWalker = (type) => {
|
|
3865
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
3866
|
+
return extraContainerWalkers.get(key) ?? builtInContainerWalkers.get(key);
|
|
3867
|
+
};
|
|
3868
|
+
var deleteContainerWalker = (type) => extraContainerWalkers.delete((0, _tmagic_utils.toLine)(type));
|
|
3869
|
+
var clearContainerWalkers = () => extraContainerWalkers.clear();
|
|
3870
|
+
var getItems = (config) => config?.items;
|
|
3871
|
+
/**
|
|
3872
|
+
* `resolveItemType` 的静态版本:不求值函数型 `type`,由调用方按「未知」处理。
|
|
3873
|
+
*
|
|
3874
|
+
* 两者必须保持一致,否则预扫描判定的 type 与实际遍历的不同,会漏执行 effect。
|
|
3875
|
+
*/
|
|
3876
|
+
var staticItemType = (config) => {
|
|
3877
|
+
const type = "type" in config ? config.type : "";
|
|
3878
|
+
if (type === "form" || type === "container") return "";
|
|
3879
|
+
return `${type || ""}`.replace(/([A-Z])/g, "-$1").toLowerCase() || (config.items ? "" : "text");
|
|
3880
|
+
};
|
|
3881
|
+
/**
|
|
3882
|
+
* 静态预判一份配置子树是否可能触发值初始化写入。
|
|
3883
|
+
*
|
|
3884
|
+
* 只有「确定不会」时才返回 false。函数型 `type`、`itemsFunction`、业务登记的容器遍历器、
|
|
3885
|
+
* 登记了 innerConfig 的复合字段,其运行期结构静态看不出来,一律按可能触发处理。
|
|
3886
|
+
*
|
|
3887
|
+
* 只用在同一份 `items` 会被逐行 / 逐标签页重复展开的地方(table、group-list、dynamic tab):
|
|
3888
|
+
* 预判成本是 O(子项数),跳过省下的是 O(行数 × 子项数),无 effect 的多行表格能省一个量级。
|
|
3889
|
+
* 其余位置不做预判——扫描与遍历的单节点成本相当,扫完再遍历只会更慢。
|
|
3890
|
+
*
|
|
3891
|
+
* 只看 `items`:table / group-list 只遍历当前形态的子项,另一形态的
|
|
3892
|
+
* `tableItems` / `groupItems` 不参与遍历,判定范围与遍历保持一致。
|
|
3893
|
+
*/
|
|
3894
|
+
var mayRunEffects = (config) => {
|
|
3895
|
+
if (!config) return false;
|
|
3896
|
+
if (typeof config.type === "function" || typeof config.itemsFunction === "function") return true;
|
|
3897
|
+
const type = staticItemType(config);
|
|
3898
|
+
if (type === "hidden") return false;
|
|
3899
|
+
if (type) {
|
|
3900
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
3901
|
+
if (extraContainerWalkers.has(key)) return true;
|
|
3902
|
+
if (!builtInContainerWalkers.has(key)) {
|
|
3903
|
+
if (getFieldInnerConfig(type) || getFieldMountValueEffect(type)) return true;
|
|
3904
|
+
if (isLeafFieldType(type)) return false;
|
|
3445
3905
|
}
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3906
|
+
}
|
|
3907
|
+
return itemsMayRunEffects(config.items);
|
|
3908
|
+
};
|
|
3909
|
+
var itemsMayRunEffects = (items) => Array.isArray(items) && items.some((item) => mayRunEffects(item));
|
|
3910
|
+
/**
|
|
3911
|
+
* 复刻 `Container.vue` 的 `display`。
|
|
3912
|
+
*
|
|
3913
|
+
* 与渲染的唯一差异:`display: 'expand'`(「展开更多配置」按钮背后的字段)在此视为可见。
|
|
3914
|
+
* 「是否已展开」纯粹是交互状态,不改变字段是否属于这份配置,无渲染校验按配置声明的范围校验。
|
|
3915
|
+
*/
|
|
3916
|
+
var resolveDisplay = (ctx, config, nodeProps) => {
|
|
3917
|
+
if (ignoresDisplay(ctx)) return true;
|
|
3918
|
+
const value = display(ctx.mForm, config?.display, nodeProps);
|
|
3919
|
+
if (value === "expand") return true;
|
|
3920
|
+
return Boolean(value);
|
|
3921
|
+
};
|
|
3922
|
+
var addField = (ctx, node, itemProp, nodeProps) => {
|
|
3923
|
+
if (ctx.mode !== "collect") return;
|
|
3924
|
+
const rules = getNativeRules(ctx.mForm, node.config.rules, nodeProps, ctx.typeMatchValid);
|
|
3925
|
+
if (!rules.length) return;
|
|
3926
|
+
ctx.fields.push({
|
|
3927
|
+
prop: itemProp,
|
|
3928
|
+
rules,
|
|
3929
|
+
config: node.config,
|
|
3930
|
+
model: node.model
|
|
3931
|
+
});
|
|
3932
|
+
};
|
|
3933
|
+
var walkChildren = (ctx, items, model, prop) => {
|
|
3934
|
+
if (!Array.isArray(items)) return;
|
|
3935
|
+
for (const item of items) {
|
|
3936
|
+
if (!item) continue;
|
|
3937
|
+
walkNode(ctx, {
|
|
3938
|
+
config: item,
|
|
3939
|
+
model,
|
|
3940
|
+
prop
|
|
3941
|
+
});
|
|
3942
|
+
}
|
|
3943
|
+
};
|
|
3944
|
+
var getContainerScope = (node) => {
|
|
3945
|
+
const { config, model } = node;
|
|
3946
|
+
const name = config.name || "";
|
|
3947
|
+
return {
|
|
3948
|
+
config,
|
|
3949
|
+
model,
|
|
3950
|
+
name,
|
|
3951
|
+
items: getItems(config),
|
|
3952
|
+
childModel: name ? model?.[name] : model
|
|
3953
|
+
};
|
|
3954
|
+
};
|
|
3955
|
+
/** containers/Tabs.vue */
|
|
3956
|
+
var expandTab = (ctx, node, itemProp) => {
|
|
3957
|
+
const { config, model, name, items, childModel } = getContainerScope(node);
|
|
3958
|
+
const tabsProps = {
|
|
3959
|
+
model,
|
|
3960
|
+
config,
|
|
3961
|
+
prop: itemProp
|
|
3962
|
+
};
|
|
3963
|
+
if (config.dynamic) {
|
|
3964
|
+
if (!name) return;
|
|
3965
|
+
const tabs = model?.[name] || [];
|
|
3966
|
+
if (!tabs.length) return;
|
|
3967
|
+
if (ctx.mode === "effects" && !itemsMayRunEffects(items)) return;
|
|
3968
|
+
tabs.forEach((_tab, tabIndex) => {
|
|
3969
|
+
walkChildren(ctx, items, childModel?.[tabIndex], appendProp(itemProp, tabIndex));
|
|
3970
|
+
});
|
|
3971
|
+
return;
|
|
3972
|
+
}
|
|
3973
|
+
const tabs = ignoresDisplay(ctx) ? items || [] : (items || []).filter((item) => display(ctx.mForm, item?.display, tabsProps));
|
|
3974
|
+
for (const tab of tabs) {
|
|
3975
|
+
const tabName = tab.name;
|
|
3976
|
+
walkChildren(ctx, getItems(tab), tabName ? childModel?.[tabName] : childModel, tabName ? appendProp(itemProp, tabName) : itemProp);
|
|
3977
|
+
}
|
|
3978
|
+
};
|
|
3979
|
+
/** containers/Row.vue → Col.vue:col 用 v-show 控制显隐,始终渲染 */
|
|
3980
|
+
var expandRow = (ctx, node, itemProp) => {
|
|
3981
|
+
const { items, childModel } = getContainerScope(node);
|
|
3982
|
+
walkChildren(ctx, items, childModel, itemProp);
|
|
3983
|
+
};
|
|
3984
|
+
/** containers/Fieldset.vue */
|
|
3985
|
+
var expandFieldset = (ctx, node, itemProp) => {
|
|
3986
|
+
const { config, items, childModel } = getContainerScope(node);
|
|
3987
|
+
if (!childModel) return;
|
|
3988
|
+
const { checkbox } = config;
|
|
3989
|
+
const checkboxName = typeof checkbox === "object" && typeof checkbox.name === "string" ? checkbox.name : "value";
|
|
3990
|
+
const checkboxTrueValue = typeof checkbox === "object" && typeof checkbox.trueValue !== "undefined" ? checkbox.trueValue : 1;
|
|
3991
|
+
if (!ignoresDisplay(ctx) && config.expand && childModel?.[checkboxName] !== checkboxTrueValue) return;
|
|
3992
|
+
walkChildren(ctx, items, childModel, itemProp);
|
|
3993
|
+
};
|
|
3994
|
+
/** containers/Panel.vue:折叠仅 display:none,子项照常渲染 */
|
|
3995
|
+
var expandPanel = (ctx, node, itemProp) => {
|
|
3996
|
+
const { items, childModel } = getContainerScope(node);
|
|
3997
|
+
if (!items?.length) return;
|
|
3998
|
+
walkChildren(ctx, items, childModel, itemProp);
|
|
3999
|
+
};
|
|
4000
|
+
/** containers/Step.vue:非当前步仅 v-show 隐藏,子项照常渲染;prop 基准被重置为 step.name */
|
|
4001
|
+
var expandStep = (ctx, node, _itemProp) => {
|
|
4002
|
+
const { model, items } = getContainerScope(node);
|
|
4003
|
+
for (const step of items || []) {
|
|
4004
|
+
const stepName = step?.name;
|
|
4005
|
+
walkChildren(ctx, getItems(step), stepName ? model?.[stepName] : model, `${stepName}`);
|
|
4006
|
+
}
|
|
4007
|
+
};
|
|
4008
|
+
/**
|
|
4009
|
+
* 遍历 table / group-list。
|
|
4010
|
+
*
|
|
4011
|
+
* `TableGroupList.vue` 按原始 `config.type` 决定初始展示形态(table 或 groupList),
|
|
4012
|
+
* 两种形态的子项结构不同,这里分别复刻(形态间的配置派生复用 `utils/tableGroupList`,
|
|
4013
|
+
* 与渲染式实现同源):
|
|
4014
|
+
* - table:逐列(跳过 hidden 列与 display 为假的列)× 逐行渲染 Container,行 prop 为 `${prop}.${index}`;
|
|
4015
|
+
* - groupList:逐行渲染一个 row 容器,包含 `items` 全部字段。
|
|
4016
|
+
*
|
|
4017
|
+
* 与渲染的差异:不做分页截断(table)、不跳过折叠行(groupList)。二者都是列表的视图状态,
|
|
4018
|
+
* 与「这些行的值是否合法」无关,无渲染校验覆盖全部行。
|
|
4019
|
+
*/
|
|
4020
|
+
var expandTableGroupList = (ctx, node, itemProp) => {
|
|
4021
|
+
const { config, model } = node;
|
|
4022
|
+
const name = config.name || "";
|
|
4023
|
+
const rows = model?.[name];
|
|
4024
|
+
if (!Array.isArray(rows) || !rows.length) return;
|
|
4025
|
+
if (isGroupListType(config.type)) {
|
|
4026
|
+
const groupListConfig = toGroupListConfig(config);
|
|
4027
|
+
if (ctx.mode === "effects" && !itemsMayRunEffects(groupListConfig.items)) return;
|
|
4028
|
+
rows.forEach((row, index) => {
|
|
4029
|
+
walkNode(ctx, {
|
|
4030
|
+
config: getGroupListRowConfig(groupListConfig, index, ctx.mForm?.keyProp),
|
|
4031
|
+
model: row,
|
|
4032
|
+
prop: appendProp(itemProp, index)
|
|
4033
|
+
});
|
|
4034
|
+
});
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
const tableItems = toTableConfig(config).items;
|
|
4038
|
+
if (!Array.isArray(tableItems)) return;
|
|
4039
|
+
if (ctx.mode === "effects" && !itemsMayRunEffects(tableItems)) return;
|
|
4040
|
+
const tableProps = {
|
|
4041
|
+
model,
|
|
4042
|
+
config,
|
|
4043
|
+
prop: itemProp
|
|
4044
|
+
};
|
|
4045
|
+
const evalDisplay = (display$4) => display(ctx.mForm, display$4, tableProps);
|
|
4046
|
+
const isRendered = (column) => ignoresDisplay(ctx) || isTableColumnRendered(column, evalDisplay);
|
|
4047
|
+
rows.forEach((row, index) => {
|
|
4048
|
+
for (const column of tableItems) {
|
|
4049
|
+
if (!column || !isRendered(column)) continue;
|
|
4050
|
+
walkNode(ctx, {
|
|
4051
|
+
config: makeTableColumnConfig(column, row),
|
|
4052
|
+
model: row,
|
|
4053
|
+
prop: appendProp(itemProp, index)
|
|
4054
|
+
});
|
|
3450
4055
|
}
|
|
3451
|
-
|
|
4056
|
+
});
|
|
4057
|
+
};
|
|
4058
|
+
/**
|
|
4059
|
+
* 执行某个叶子字段登记的值初始化写入。
|
|
4060
|
+
*
|
|
4061
|
+
* 跑在表单初始化路径上,单个字段的 effect 抛错不应该让整张表单渲染不出来,因此只记录并继续。
|
|
4062
|
+
*/
|
|
4063
|
+
var runMountValueEffect = (ctx, type, node, itemProp) => {
|
|
4064
|
+
const effect = getFieldMountValueEffect(type);
|
|
4065
|
+
if (!effect) return;
|
|
4066
|
+
try {
|
|
4067
|
+
effect({
|
|
4068
|
+
config: node.config,
|
|
4069
|
+
model: node.model,
|
|
4070
|
+
prop: itemProp,
|
|
4071
|
+
mForm: ctx.mForm,
|
|
4072
|
+
values: ctx.values
|
|
4073
|
+
});
|
|
4074
|
+
} catch (e) {
|
|
4075
|
+
console.error(`[MForm] mount value effect for "${type}" at "${itemProp}" failed:`, e);
|
|
3452
4076
|
}
|
|
3453
4077
|
};
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
4078
|
+
/**
|
|
4079
|
+
* 遍历已登记 innerConfig 的复合字段。
|
|
4080
|
+
*
|
|
4081
|
+
* `collect` 模式下回调抛错视为机制故障,包装成 `FieldInnerConfigError` 抛出,由校验流程暴露。
|
|
4082
|
+
* `effects` 模式跑在表单初始化路径上,抛出会让整张表单渲染不出来,因此只记录并跳过该子树。
|
|
4083
|
+
*/
|
|
4084
|
+
var walkInnerConfig = (ctx, type, node, itemProp) => {
|
|
4085
|
+
const resolve = getFieldInnerConfig(type);
|
|
4086
|
+
if (!resolve) return false;
|
|
4087
|
+
let result;
|
|
4088
|
+
try {
|
|
4089
|
+
result = resolve({
|
|
4090
|
+
config: node.config,
|
|
4091
|
+
model: node.model,
|
|
4092
|
+
prop: itemProp,
|
|
4093
|
+
parentProp: node.prop,
|
|
4094
|
+
mForm: ctx.mForm
|
|
4095
|
+
});
|
|
4096
|
+
} catch (e) {
|
|
4097
|
+
if (ctx.mode === "collect") throw new FieldInnerConfigError(type, itemProp, e);
|
|
4098
|
+
console.error(new FieldInnerConfigError(type, itemProp, e));
|
|
4099
|
+
return true;
|
|
3459
4100
|
}
|
|
3460
|
-
|
|
4101
|
+
if (!result) return true;
|
|
4102
|
+
const innerModel = result.model ?? node.model;
|
|
4103
|
+
const innerProp = result.prop ?? itemProp;
|
|
4104
|
+
walkChildren(ctx, Array.isArray(result.config) ? result.config : [result.config], innerModel, innerProp);
|
|
4105
|
+
return true;
|
|
4106
|
+
};
|
|
4107
|
+
/**
|
|
4108
|
+
* 遍历一个 Container 节点,分支顺序与 `Container.vue` 的模板保持一致。
|
|
4109
|
+
*
|
|
4110
|
+
* 对比模式(`isCompare`)在此被忽略:它只改变同一个 prop 渲染几份 FormItem,
|
|
4111
|
+
* 规则与取值完全相同,对校验结果没有影响。
|
|
4112
|
+
*/
|
|
4113
|
+
var walkNode = (ctx, node) => {
|
|
4114
|
+
const { config, prop } = node;
|
|
4115
|
+
const model = node.model ?? {};
|
|
4116
|
+
const nodeProps = {
|
|
4117
|
+
model,
|
|
4118
|
+
config,
|
|
4119
|
+
prop
|
|
4120
|
+
};
|
|
4121
|
+
const name = config.name || "";
|
|
4122
|
+
const items = getItems(config);
|
|
4123
|
+
const type = resolveItemType(ctx.mForm, config, nodeProps);
|
|
4124
|
+
const itemProp = getItemProp(prop, name);
|
|
4125
|
+
const text = filterFunction(ctx.mForm, config.text, nodeProps);
|
|
4126
|
+
if (type === "hidden") {
|
|
4127
|
+
addField(ctx, {
|
|
4128
|
+
...node,
|
|
4129
|
+
model
|
|
4130
|
+
}, itemProp, nodeProps);
|
|
4131
|
+
return;
|
|
4132
|
+
}
|
|
4133
|
+
const display = resolveDisplay(ctx, config, nodeProps);
|
|
4134
|
+
if (items && !text && type && display) {
|
|
4135
|
+
dispatchByType(ctx, type, {
|
|
4136
|
+
...node,
|
|
4137
|
+
model
|
|
4138
|
+
}, itemProp);
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4141
|
+
if (type && display) {
|
|
4142
|
+
addField(ctx, {
|
|
4143
|
+
...node,
|
|
4144
|
+
model
|
|
4145
|
+
}, itemProp, nodeProps);
|
|
4146
|
+
dispatchByType(ctx, type, {
|
|
4147
|
+
...node,
|
|
4148
|
+
model
|
|
4149
|
+
}, itemProp);
|
|
4150
|
+
return;
|
|
4151
|
+
}
|
|
4152
|
+
if (items && display) {
|
|
4153
|
+
const childModel = isValidName(name) ? model[name] : model;
|
|
4154
|
+
if (!childModel) return;
|
|
4155
|
+
walkChildren(ctx, items, childModel, itemProp);
|
|
4156
|
+
}
|
|
4157
|
+
};
|
|
4158
|
+
/**
|
|
4159
|
+
* 按 type 分派:已登记 walk 的容器 → 按容器模板遍历;
|
|
4160
|
+
* `effects` 模式下先执行本字段的 effect(可与 innerConfig 并存,如 `code-select` 先归一化再下钻);
|
|
4161
|
+
* 登记了 innerConfig 的复合字段 → 遍历其内部配置;叶子字段无子树。
|
|
4162
|
+
*
|
|
4163
|
+
* 未登记的 type:配置里若有 `items` 则按普通容器下钻;否则视为没有嵌套表单项。
|
|
4164
|
+
* 自身 rules 已在 FormItem 分支收集,有 rules 就会校验。
|
|
4165
|
+
*/
|
|
4166
|
+
var dispatchByType = (ctx, type, node, itemProp) => {
|
|
4167
|
+
const walkContainer = getContainerWalker(type);
|
|
4168
|
+
if (walkContainer) {
|
|
4169
|
+
walkContainer(ctx, node, itemProp);
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
if (ctx.mode === "effects") runMountValueEffect(ctx, type, node, itemProp);
|
|
4173
|
+
if (walkInnerConfig(ctx, type, node, itemProp)) return;
|
|
4174
|
+
if (isLeafFieldType(type)) return;
|
|
4175
|
+
const items = getItems(node.config);
|
|
4176
|
+
if (items?.length) {
|
|
4177
|
+
const { childModel } = getContainerScope(node);
|
|
4178
|
+
walkChildren(ctx, items, childModel, itemProp);
|
|
4179
|
+
}
|
|
4180
|
+
};
|
|
4181
|
+
/**
|
|
4182
|
+
* 纯逻辑收集「一份 config + 一份 values」中所有参与校验的字段。
|
|
4183
|
+
*
|
|
4184
|
+
* 遍历规则与 `Container.vue` 及各容器组件的模板一一对应,因此产出的 prop / rules
|
|
4185
|
+
* 与渲染式校验(挂载 MForm 后调用 `validate()`)等价,无需任何 DOM 或组件实例。
|
|
4186
|
+
*
|
|
4187
|
+
* 本函数只读不写。字段的值初始化副作用由 `applyMountValueEffects` 负责,
|
|
4188
|
+
* 调用方需在初始化表单值之后、收集字段之前执行一次(`validateValues` 已经这么做)。
|
|
4189
|
+
*
|
|
4190
|
+
* @param mForm - 表单状态;无渲染时由 `createHeadlessFormState` 构造
|
|
4191
|
+
* @param config - 表单配置
|
|
4192
|
+
* @param values - 表单值
|
|
4193
|
+
* @param [typeMatchValid] - 是否自动注入 typeMatch 规则;为 true 时,未显式声明
|
|
4194
|
+
* `typeMatch` 的字段会补上 `{ typeMatch: true }`,按字段 type 校验值形态
|
|
4195
|
+
* @returns 带规则的字段列表
|
|
4196
|
+
*/
|
|
4197
|
+
var collectValidatableFields = (mForm, config, values, typeMatchValid) => {
|
|
4198
|
+
const ctx = {
|
|
4199
|
+
mForm,
|
|
4200
|
+
typeMatchValid,
|
|
4201
|
+
fields: [],
|
|
4202
|
+
values,
|
|
4203
|
+
mode: "collect"
|
|
4204
|
+
};
|
|
4205
|
+
if (Array.isArray(config)) walkChildren(ctx, config, values, "");
|
|
4206
|
+
return ctx.fields;
|
|
4207
|
+
};
|
|
4208
|
+
/**
|
|
4209
|
+
* 执行「一份 config + 一份 values」中所有叶子字段登记的值初始化写入,就地改写 `values`。
|
|
4210
|
+
*
|
|
4211
|
+
* 渲染与无渲染两条链路共用的唯一执行点:字段组件自身不再在 setup 里改写 model,
|
|
4212
|
+
* 由持有完整表单值的一方(`Form.vue` / `validateValues`)在表单值初始化完成、
|
|
4213
|
+
* 且已挂到 `mForm.values` 之后调用一次。这样 effect 与动态 `type` 回调读到的
|
|
4214
|
+
* `formValue` 就是最新值,而不是上一轮的旧值。
|
|
4215
|
+
*
|
|
4216
|
+
* 与 `collectValidatableFields` 的差异:不看 `display`(`display: false` 的字段也会规整)。
|
|
4217
|
+
* `type: 'hidden'` 会在该节点停止遍历,内部字段不执行 effect。
|
|
4218
|
+
*
|
|
4219
|
+
* `prop` 与 `values` 都以传入的 `values` 为根,因此也可以用于 tab / table 新增行这类
|
|
4220
|
+
* 「先构造一份子树的值,再挂到表单上」的场景:传入子树配置与该行的值即可。
|
|
4221
|
+
*
|
|
4222
|
+
* @param mForm - 表单状态;无渲染时由 `createHeadlessFormState` 构造
|
|
4223
|
+
* @param config - 表单配置
|
|
4224
|
+
* @param values - 表单值(会被就地修改)
|
|
4225
|
+
*/
|
|
4226
|
+
var applyMountValueEffects = (mForm, config, values) => {
|
|
4227
|
+
if (!Array.isArray(config)) return;
|
|
4228
|
+
walkChildren({
|
|
4229
|
+
mForm,
|
|
4230
|
+
fields: [],
|
|
4231
|
+
values,
|
|
4232
|
+
mode: "effects"
|
|
4233
|
+
}, config, values, "");
|
|
4234
|
+
};
|
|
4235
|
+
//#endregion
|
|
4236
|
+
//#region packages/form/src/utils/validateError.ts
|
|
4237
|
+
/**
|
|
4238
|
+
* 通过 name 从 config 中查找对应的 text
|
|
4239
|
+
*
|
|
4240
|
+
* @param name - 字段名,支持点分隔的路径格式,如 'a.b.c'
|
|
4241
|
+
* @param config - 表单配置数组
|
|
4242
|
+
* @returns 找到的 text 值,如果未找到则返回 undefined
|
|
4243
|
+
*/
|
|
4244
|
+
var getTextByName = (name, config = []) => {
|
|
4245
|
+
if (!name || !Array.isArray(config)) return void 0;
|
|
4246
|
+
const nameParts = name.split(".");
|
|
4247
|
+
const findInConfig = (configs, parts) => {
|
|
4248
|
+
if (parts.length === 0) return void 0;
|
|
4249
|
+
const [currentPart, ...remainingParts] = parts;
|
|
4250
|
+
for (const item of configs) {
|
|
4251
|
+
if (item.name === currentPart) {
|
|
4252
|
+
if (remainingParts.length === 0) return typeof item.text === "string" ? item.text : void 0;
|
|
4253
|
+
if ("items" in item && Array.isArray(item.items)) {
|
|
4254
|
+
const result = findInConfig(item.items, remainingParts);
|
|
4255
|
+
if (result !== void 0) return result;
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
if ("items" in item && Array.isArray(item.items)) {
|
|
4259
|
+
const result = findInConfig(item.items, parts);
|
|
4260
|
+
if (result !== void 0) return result;
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
};
|
|
4264
|
+
return findInConfig(config, nameParts);
|
|
4265
|
+
};
|
|
4266
|
+
/**
|
|
4267
|
+
* 将校验返回的 invalidFields 汇总为可读的错误文案(多条以 `<br>` 拼接)。
|
|
4268
|
+
*
|
|
4269
|
+
* `useFieldTextInError` 为 `true`(默认)时用字段的 text 文案作为错误前缀,找不到则回退为字段 name。
|
|
4270
|
+
*
|
|
4271
|
+
* 由渲染式校验(Form.vue 的 `submitForm` / `validate`)与无渲染校验(`validateValues`)共用,
|
|
4272
|
+
* 保证两条校验链路产出的错误文案格式完全一致。
|
|
4273
|
+
*
|
|
4274
|
+
* @param invalidFields - async-validator 产出的字段错误表
|
|
4275
|
+
* @param [options] - 文案选项
|
|
4276
|
+
* @param [options.config] - 用于查找 text 的表单配置
|
|
4277
|
+
* @param [options.useFieldTextInError=true] - 错误前缀是否使用字段 text
|
|
4278
|
+
* @returns 拼接后的错误文案;无错误时为空串
|
|
4279
|
+
*/
|
|
4280
|
+
var formatValidateError = (invalidFields, { config = [], useFieldTextInError = true } = {}) => {
|
|
4281
|
+
const error = [];
|
|
4282
|
+
Object.entries(invalidFields).forEach(([prop, validateError]) => {
|
|
4283
|
+
validateError.forEach(({ field, message }) => {
|
|
4284
|
+
const name = field || prop;
|
|
4285
|
+
const text = (useFieldTextInError ? getTextByName(name, config) : void 0) || name;
|
|
4286
|
+
error.push(`${text} -> ${message}`);
|
|
4287
|
+
});
|
|
4288
|
+
});
|
|
4289
|
+
return error.join("<br>");
|
|
4290
|
+
};
|
|
4291
|
+
//#endregion
|
|
4292
|
+
//#region packages/form/src/utils/validateValues.ts
|
|
4293
|
+
/**
|
|
4294
|
+
* 构造一个不依赖组件实例的 `formState`。
|
|
4295
|
+
*
|
|
4296
|
+
* 字段结构与 `Form.vue` 中 provide 给子树的 `mForm` 保持一致,使配置里的
|
|
4297
|
+
* `display` / `rules.validator` / `filter` 等回调拿到的上下文与渲染式校验相同。
|
|
4298
|
+
*
|
|
4299
|
+
* 差异:没有真实的表单实例,因此 `$emit` 为空实现、`fields` 注册表始终为空
|
|
4300
|
+
* (该注册表在整个仓库中只写不读,不参与校验)。
|
|
4301
|
+
*/
|
|
4302
|
+
var createHeadlessFormState = (options) => {
|
|
4303
|
+
const context = options.context ?? {};
|
|
4304
|
+
const state = {
|
|
4305
|
+
keyProp: options.keyProp ?? "__key",
|
|
4306
|
+
popperClass: options.popperClass ?? "",
|
|
4307
|
+
config: options.config,
|
|
4308
|
+
initValues: options.initValues ?? {},
|
|
4309
|
+
isCompare: false,
|
|
4310
|
+
lastValues: {},
|
|
4311
|
+
parentValues: options.parentValues ?? {},
|
|
4312
|
+
values: {},
|
|
4313
|
+
lastValuesProcessed: {},
|
|
4314
|
+
$emit: () => void 0,
|
|
4315
|
+
...createFormStateBase()
|
|
4316
|
+
};
|
|
4317
|
+
return createFormStateProxy((0, vue.reactive)(state), () => context);
|
|
4318
|
+
};
|
|
4319
|
+
/**
|
|
4320
|
+
* 按 prop 路径从表单值中取字段值,路径中断时返回 `undefined`。
|
|
4321
|
+
*
|
|
4322
|
+
* 不复用 `@tmagic/utils` 的 `getValueByKeyPath`:后者在路径中断时抛错,
|
|
4323
|
+
* 而 Element Plus 的 FormItem 取值是宽松的(拿不到就是 `undefined`),此处与之对齐。
|
|
4324
|
+
*/
|
|
4325
|
+
var getFieldValue = (prop, values) => {
|
|
4326
|
+
if (!prop) return void 0;
|
|
4327
|
+
return prop.split(".").reduce((acc, key) => {
|
|
4328
|
+
if (acc === null || typeof acc !== "object") return void 0;
|
|
4329
|
+
return acc[key];
|
|
4330
|
+
}, values);
|
|
4331
|
+
};
|
|
4332
|
+
/**
|
|
4333
|
+
* 校验单个字段,返回 async-validator 的 `fields` 错误映射(通过则返回 `null`)。
|
|
4334
|
+
*
|
|
4335
|
+
* 与 Element Plus 的 FormItem 逐字段校验保持一致:
|
|
4336
|
+
* 每个字段独立构造一个 `Schema({ [prop]: rules })`,以字段自身的值为 source,
|
|
4337
|
+
* 并开启 `firstFields` 只取每个字段的首条错误;`trigger` 是 FormItem 用来筛选规则的
|
|
4338
|
+
* 元信息,交给 async-validator 前必须剔除,否则会改变规则的匹配方式。
|
|
4339
|
+
*/
|
|
4340
|
+
var validateField = async (field, values) => {
|
|
4341
|
+
const rules = field.rules.map(({ trigger, ...rule }) => rule);
|
|
4342
|
+
if (!rules.length) return null;
|
|
4343
|
+
const value = getFieldValue(field.prop, values);
|
|
4344
|
+
try {
|
|
4345
|
+
await new async_validator.default({ [field.prop]: rules }).validate({ [field.prop]: value }, { firstFields: true });
|
|
4346
|
+
return null;
|
|
4347
|
+
} catch (err) {
|
|
4348
|
+
if (err?.fields) return err.fields;
|
|
4349
|
+
return { [field.prop]: [{
|
|
4350
|
+
field: field.prop,
|
|
4351
|
+
message: err?.message ?? `${err}`
|
|
4352
|
+
}] };
|
|
4353
|
+
}
|
|
4354
|
+
};
|
|
4355
|
+
/**
|
|
4356
|
+
* 不挂载任何组件,纯逻辑地对「一份 config + 一份值」执行一次完整校验。
|
|
4357
|
+
*
|
|
4358
|
+
* 流程与渲染式校验一一对应,但不需要 DOM,也不会实例化任何字段组件:
|
|
4359
|
+
*
|
|
4360
|
+
* 1. 构造 headless `formState` 并挂上 `context` 读穿;
|
|
4361
|
+
* 2. `initValue` 初始化表单值(默认值、嵌套结构、`onInitValue` 等);
|
|
4362
|
+
* 3. `applyMountValueEffects` 执行字段登记的值初始化写入(与渲染式共用同一份登记表);
|
|
4363
|
+
* 4. 遍历 config 树收集所有带规则的字段(等价于渲染出的 FormItem 集合);
|
|
4364
|
+
* 5. 逐字段交给 async-validator 执行,汇总错误文案。
|
|
4365
|
+
*
|
|
4366
|
+
* @example
|
|
4367
|
+
* ```ts
|
|
4368
|
+
* const { error, values } = await validateValues({
|
|
4369
|
+
* config: [...],
|
|
4370
|
+
* initValues: { name: '' },
|
|
4371
|
+
* typeMatchValid: true,
|
|
4372
|
+
* });
|
|
4373
|
+
* ```
|
|
4374
|
+
*/
|
|
4375
|
+
var validateValues = async (options) => {
|
|
4376
|
+
const { config, initValues = {}, typeMatchValid, useFieldTextInError = true } = options;
|
|
4377
|
+
const formState = createHeadlessFormState(options);
|
|
4378
|
+
const values = await initValue(formState, {
|
|
4379
|
+
initValues,
|
|
4380
|
+
config
|
|
4381
|
+
});
|
|
4382
|
+
formState.values = values;
|
|
4383
|
+
applyMountValueEffects(formState, config, values);
|
|
4384
|
+
const fields = collectValidatableFields(formState, config, values, Boolean(typeMatchValid));
|
|
4385
|
+
const invalidFields = {};
|
|
4386
|
+
for (const field of fields) {
|
|
4387
|
+
const fieldErrors = await validateField(field, values);
|
|
4388
|
+
if (fieldErrors) Object.assign(invalidFields, fieldErrors);
|
|
4389
|
+
}
|
|
4390
|
+
return {
|
|
4391
|
+
values,
|
|
4392
|
+
invalidFields,
|
|
4393
|
+
error: formatValidateError(invalidFields, {
|
|
4394
|
+
config,
|
|
4395
|
+
useFieldTextInError
|
|
4396
|
+
})
|
|
4397
|
+
};
|
|
4398
|
+
};
|
|
4399
|
+
//#endregion
|
|
4400
|
+
//#region packages/form/src/utils/submitHeadless.ts
|
|
4401
|
+
var throwIfAborted = (signal, fnName) => {
|
|
4402
|
+
if (signal?.aborted) throw signal.reason ?? /* @__PURE__ */ new Error(`${fnName} aborted`);
|
|
4403
|
+
};
|
|
4404
|
+
var throwIfDialog = (dialog, fnName) => {
|
|
4405
|
+
if (dialog) throw new Error(`[MForm] ${fnName}({ dialog: true }) is not available from @tmagic/form/headless. Import from @tmagic/form instead.`);
|
|
4406
|
+
};
|
|
4407
|
+
/**
|
|
4408
|
+
* `submitForm` 与 `validateForm` 共用的无渲染校验流程:中断检查 → `validateValues`。
|
|
4409
|
+
*/
|
|
4410
|
+
var validateWithoutRender = async (fnName, options) => {
|
|
4411
|
+
const { signal, config, initValues, parentValues, keyProp, typeMatchValid, useFieldTextInError, context } = options;
|
|
4412
|
+
throwIfAborted(signal, fnName);
|
|
4413
|
+
const result = await validateValues({
|
|
4414
|
+
config,
|
|
4415
|
+
initValues,
|
|
4416
|
+
parentValues,
|
|
4417
|
+
keyProp,
|
|
4418
|
+
popperClass: options.popperClass,
|
|
4419
|
+
typeMatchValid,
|
|
4420
|
+
useFieldTextInError,
|
|
4421
|
+
context
|
|
4422
|
+
});
|
|
4423
|
+
throwIfAborted(signal, fnName);
|
|
4424
|
+
return result;
|
|
4425
|
+
};
|
|
4426
|
+
/**
|
|
4427
|
+
* 以命令式方式对一份「表单配置 + 值」做一次校验并取回表单值(无渲染)。
|
|
4428
|
+
*
|
|
4429
|
+
* `@tmagic/form/headless` 不支持 `dialog: true`。
|
|
4430
|
+
*/
|
|
4431
|
+
var submitForm$1 = async (options) => {
|
|
4432
|
+
throwIfDialog(options.dialog, "submitForm");
|
|
4433
|
+
const validated = await validateWithoutRender("submitForm", options);
|
|
4434
|
+
if (validated.error) throw new Error(validated.error);
|
|
4435
|
+
const values = options.native ? validated.values : cloneDeep(validated.values);
|
|
4436
|
+
return options.returnChangeRecords ? {
|
|
4437
|
+
values,
|
|
4438
|
+
changeRecords: []
|
|
4439
|
+
} : values;
|
|
4440
|
+
};
|
|
4441
|
+
/**
|
|
4442
|
+
* 以命令式方式对一份「表单配置 + 值」做一次静默校验(无渲染)。
|
|
4443
|
+
*
|
|
4444
|
+
* `@tmagic/form/headless` 不支持 `dialog: true`。
|
|
4445
|
+
*/
|
|
4446
|
+
var validateForm$1 = async (options) => {
|
|
4447
|
+
throwIfDialog(options.dialog, "validateForm");
|
|
4448
|
+
return (await validateWithoutRender("validateForm", options)).error;
|
|
3461
4449
|
};
|
|
3462
4450
|
//#endregion
|
|
3463
4451
|
//#region packages/form/src/utils/useAddField.ts
|
|
@@ -3492,67 +4480,156 @@
|
|
|
3492
4480
|
}
|
|
3493
4481
|
});
|
|
3494
4482
|
//#endregion
|
|
3495
|
-
//#region packages/form/src/utils/
|
|
3496
|
-
var
|
|
3497
|
-
var
|
|
3498
|
-
var
|
|
3499
|
-
|
|
3500
|
-
|
|
4483
|
+
//#region packages/form/src/utils/registerField.ts
|
|
4484
|
+
var extraComponents = /* @__PURE__ */ new Map();
|
|
4485
|
+
var builtInComponents = /* @__PURE__ */ new Map();
|
|
4486
|
+
var applyVueComponent = (type, component, app, builtIn, kind) => {
|
|
4487
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
4488
|
+
if (builtIn) builtInComponents.set(key, component);
|
|
4489
|
+
else extraComponents.set(key, component);
|
|
4490
|
+
app?.component(`m-${kind}-${key}`, component);
|
|
3501
4491
|
};
|
|
3502
|
-
var getConfig = (key) => $MAGIC_FORM[key];
|
|
3503
|
-
var fieldRegistry = /* @__PURE__ */ new Map();
|
|
3504
|
-
var registerField = (tagName, component) => {
|
|
3505
|
-
if (fieldRegistry.has(tagName)) return;
|
|
3506
|
-
fieldRegistry.set(tagName, component);
|
|
3507
|
-
};
|
|
3508
|
-
var getField = (tagName) => fieldRegistry.get(tagName);
|
|
3509
|
-
var deleteField = (tagName) => fieldRegistry.delete(tagName);
|
|
3510
|
-
//#endregion
|
|
3511
|
-
//#region packages/form/src/utils/silentLeafFieldTypes.ts
|
|
3512
4492
|
/**
|
|
3513
|
-
*
|
|
4493
|
+
* 把多份字段登记表按 type 浅合并。后一份只覆盖自己带了的 key,未出现的 key 保留前一份。
|
|
3514
4494
|
*
|
|
3515
|
-
*
|
|
3516
|
-
* 字段值由初始化逻辑处理(包括 defaultValue),因此跳过不影响校验结果。
|
|
4495
|
+
* 安装插件时用:Node 侧只登记 `headless`,浏览器再补 `component` / `container`。
|
|
3517
4496
|
*
|
|
3518
|
-
*
|
|
4497
|
+
* @param tables - 待合并的登记表,`undefined` 会被跳过
|
|
4498
|
+
* @returns 合并后的登记表
|
|
3519
4499
|
*/
|
|
3520
|
-
var
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
"
|
|
3533
|
-
"
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
4500
|
+
var mergeFieldOptions = (...tables) => {
|
|
4501
|
+
const result = {};
|
|
4502
|
+
for (const table of tables) {
|
|
4503
|
+
if (!table) continue;
|
|
4504
|
+
for (const [type, options] of Object.entries(table)) result[type] = {
|
|
4505
|
+
...result[type],
|
|
4506
|
+
...pickDefinedFieldOptions(options)
|
|
4507
|
+
};
|
|
4508
|
+
}
|
|
4509
|
+
return result;
|
|
4510
|
+
};
|
|
4511
|
+
var FIELD_OPTION_KEYS = [
|
|
4512
|
+
"component",
|
|
4513
|
+
"container",
|
|
4514
|
+
"effect",
|
|
4515
|
+
"walk",
|
|
4516
|
+
"innerConfig",
|
|
4517
|
+
"typeMatch"
|
|
4518
|
+
];
|
|
4519
|
+
var pickDefinedFieldOptions = (options) => {
|
|
4520
|
+
if (!options) return {};
|
|
4521
|
+
const next = {};
|
|
4522
|
+
for (const key of FIELD_OPTION_KEYS) if (options[key] !== void 0) next[key] = options[key];
|
|
4523
|
+
return next;
|
|
4524
|
+
};
|
|
4525
|
+
var extraFieldOptions = /* @__PURE__ */ new Map();
|
|
4526
|
+
var builtInFieldOptions = /* @__PURE__ */ new Map();
|
|
4527
|
+
var removeFormComponent = (type) => {
|
|
4528
|
+
extraComponents.delete((0, _tmagic_utils.toLine)(type));
|
|
4529
|
+
};
|
|
4530
|
+
var clearFormComponents = () => {
|
|
4531
|
+
extraComponents.clear();
|
|
4532
|
+
};
|
|
3537
4533
|
/**
|
|
3538
|
-
*
|
|
4534
|
+
* 按字段 type 取已登记的渲染组件(`codeSelect` 与 `code-select` 等价)。
|
|
3539
4535
|
*
|
|
3540
|
-
*
|
|
4536
|
+
* @param type - 字段 type
|
|
4537
|
+
* @returns 已登记的 Vue 组件;未登记则为 `undefined`
|
|
3541
4538
|
*/
|
|
3542
|
-
var
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
4539
|
+
var getField = (type) => {
|
|
4540
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
4541
|
+
return extraComponents.get(key) ?? builtInComponents.get(key);
|
|
4542
|
+
};
|
|
4543
|
+
var registerFieldImpl = (type, options, app, builtIn) => {
|
|
4544
|
+
if (typeof type !== "string" || !type) return;
|
|
4545
|
+
const key = (0, _tmagic_utils.toLine)(type);
|
|
4546
|
+
const store = builtIn ? builtInFieldOptions : extraFieldOptions;
|
|
4547
|
+
const incoming = pickDefinedFieldOptions(options);
|
|
4548
|
+
const merged = {
|
|
4549
|
+
...store.get(key),
|
|
4550
|
+
...incoming
|
|
4551
|
+
};
|
|
4552
|
+
store.set(key, merged);
|
|
4553
|
+
if (incoming.walk && (incoming.innerConfig || typeof incoming.effect === "function")) console.warn(`[MForm] registerField("${key}"): walk is set together with innerConfig/effect; headless validation will use walk and innerConfig/effect will be ignored.`);
|
|
4554
|
+
if (incoming.component && incoming.container) console.warn(`[MForm] registerField("${key}"): component and container are both set; getField will use container, and both m-fields-* / m-form-* will be registered.`);
|
|
4555
|
+
if (merged.component) applyVueComponent(type, merged.component, app, builtIn, "fields");
|
|
4556
|
+
if (merged.container) applyVueComponent(type, merged.container, app, builtIn, "form");
|
|
4557
|
+
if (merged.typeMatch) registerTypeMatchRule(type, merged.typeMatch, builtIn);
|
|
4558
|
+
if (merged.walk) {
|
|
4559
|
+
registerContainerWalker(type, merged.walk, builtIn);
|
|
4560
|
+
if (!builtIn) {
|
|
4561
|
+
deleteLeafFieldType(type);
|
|
4562
|
+
deleteFieldInnerConfig(type);
|
|
4563
|
+
}
|
|
4564
|
+
return;
|
|
4565
|
+
}
|
|
4566
|
+
if (!builtIn) deleteContainerWalker(type);
|
|
4567
|
+
if (merged.innerConfig) {
|
|
4568
|
+
registerFieldInnerConfig(type, merged.innerConfig, builtIn);
|
|
4569
|
+
if (typeof merged.effect === "function") registerLeafFieldType(type, merged.effect, builtIn);
|
|
4570
|
+
else if (!builtIn) deleteLeafFieldType(type);
|
|
4571
|
+
return;
|
|
4572
|
+
}
|
|
4573
|
+
if (merged.container && !merged.component && typeof merged.effect !== "function") {
|
|
4574
|
+
if (!builtIn) deleteLeafFieldType(type);
|
|
4575
|
+
return;
|
|
3546
4576
|
}
|
|
4577
|
+
if (!builtIn) deleteFieldInnerConfig(type);
|
|
4578
|
+
registerLeafFieldType(type, merged.effect, builtIn);
|
|
4579
|
+
};
|
|
4580
|
+
/**
|
|
4581
|
+
* 登记一个字段 type 在无渲染校验里的行为,以及可选的渲染组件。
|
|
4582
|
+
*
|
|
4583
|
+
* `type` 会按 Container 的规则归一化为中划线形式(`codeSelect` 与 `code-select` 等价)。
|
|
4584
|
+
* 对同一 type 重复登记按字段浅合并:后一次只覆盖自己传入的 key,未传入的 key 保留。
|
|
4585
|
+
* 传入 `app` 且带了 `component` / `container` 时,会同步 `app.component('m-fields-*'` / `'m-form-*')`。
|
|
4586
|
+
*
|
|
4587
|
+
* @param type - 字段 type
|
|
4588
|
+
* @param [options] - 登记项;省略或空对象视为叶子
|
|
4589
|
+
* @param [app] - Vue 应用;省略则不调用 `app.component`
|
|
4590
|
+
*/
|
|
4591
|
+
var registerField = (type, options, app) => {
|
|
4592
|
+
registerFieldImpl(type, options, app, false);
|
|
4593
|
+
};
|
|
4594
|
+
/**
|
|
4595
|
+
* 批量登记字段。
|
|
4596
|
+
*
|
|
4597
|
+
* @param fields - type 到登记项的映射
|
|
4598
|
+
* @param [app] - Vue 应用;省略则不调用 `app.component`
|
|
4599
|
+
*/
|
|
4600
|
+
var registerFields = (fields, app) => {
|
|
4601
|
+
for (const [type, options] of Object.entries(fields)) registerFieldImpl(type, options, app, false);
|
|
4602
|
+
};
|
|
4603
|
+
/**
|
|
4604
|
+
* 登记内置字段(`clearFields` / `unregisterField` 不会清掉)。
|
|
4605
|
+
*
|
|
4606
|
+
* @param fields - type 到登记项的映射
|
|
4607
|
+
* @param [app] - Vue 应用;省略则不调用 `app.component`
|
|
4608
|
+
*/
|
|
4609
|
+
var registerBuiltInFields = (fields, app) => {
|
|
4610
|
+
for (const [type, options] of Object.entries(fields)) registerFieldImpl(type, options, app, true);
|
|
3547
4611
|
};
|
|
3548
|
-
/**
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
4612
|
+
/**
|
|
4613
|
+
* 删除业务侧对指定 type 的登记(不影响内置;主要用于单测)。
|
|
4614
|
+
*
|
|
4615
|
+
* @param type - 字段 type
|
|
4616
|
+
*/
|
|
4617
|
+
var unregisterField = (type) => {
|
|
4618
|
+
extraFieldOptions.delete((0, _tmagic_utils.toLine)(type));
|
|
4619
|
+
deleteLeafFieldType(type);
|
|
4620
|
+
deleteFieldInnerConfig(type);
|
|
4621
|
+
deleteTypeMatchRule(type);
|
|
4622
|
+
deleteContainerWalker(type);
|
|
4623
|
+
removeFormComponent(type);
|
|
3552
4624
|
};
|
|
3553
|
-
/**
|
|
3554
|
-
var
|
|
3555
|
-
|
|
4625
|
+
/** 清空业务侧登记(不影响内置;主要用于单测)。 */
|
|
4626
|
+
var clearFields = () => {
|
|
4627
|
+
extraFieldOptions.clear();
|
|
4628
|
+
clearLeafFieldTypes();
|
|
4629
|
+
clearFieldInnerConfigs();
|
|
4630
|
+
clearTypeMatchRules();
|
|
4631
|
+
clearContainerWalkers();
|
|
4632
|
+
clearFormComponents();
|
|
3556
4633
|
};
|
|
3557
4634
|
//#endregion
|
|
3558
4635
|
//#region packages/form/src/containers/FormLabel.vue?vue&type=script&setup=true&lang.ts
|
|
@@ -3612,7 +4689,7 @@
|
|
|
3612
4689
|
};
|
|
3613
4690
|
var _hoisted_3$7 = ["innerHTML"];
|
|
3614
4691
|
var _hoisted_4$6 = ["innerHTML"];
|
|
3615
|
-
var _hoisted_5$
|
|
4692
|
+
var _hoisted_5$5 = ["innerHTML"];
|
|
3616
4693
|
var _hoisted_6$3 = ["innerHTML"];
|
|
3617
4694
|
var _hoisted_7$2 = ["innerHTML"];
|
|
3618
4695
|
var _hoisted_8$2 = ["innerHTML"];
|
|
@@ -3633,6 +4710,7 @@
|
|
|
3633
4710
|
prop: { default: "" },
|
|
3634
4711
|
disabled: { type: Boolean },
|
|
3635
4712
|
labelWidth: {},
|
|
4713
|
+
labelPosition: {},
|
|
3636
4714
|
expandMore: {
|
|
3637
4715
|
type: Boolean,
|
|
3638
4716
|
default: false
|
|
@@ -3668,20 +4746,8 @@
|
|
|
3668
4746
|
return !isEqual(curValue, lastValue);
|
|
3669
4747
|
});
|
|
3670
4748
|
const items = (0, vue.computed)(() => props.config.items);
|
|
3671
|
-
const itemProp = (0, vue.computed)(() =>
|
|
3672
|
-
|
|
3673
|
-
if (name.value) n = name.value;
|
|
3674
|
-
else return props.prop;
|
|
3675
|
-
if (typeof props.prop !== "undefined" && props.prop !== "") return `${props.prop}.${n}`;
|
|
3676
|
-
return `${n}`;
|
|
3677
|
-
});
|
|
3678
|
-
const type = (0, vue.computed)(() => {
|
|
3679
|
-
let type = "type" in props.config ? props.config.type : "";
|
|
3680
|
-
type = type && filterFunction(mForm, type, props);
|
|
3681
|
-
if (type === "form") return "";
|
|
3682
|
-
if (type === "container") return "";
|
|
3683
|
-
return type?.replace(/([A-Z])/g, "-$1").toLowerCase() || (items.value ? "" : "text");
|
|
3684
|
-
});
|
|
4749
|
+
const itemProp = (0, vue.computed)(() => getItemProp(props.prop, name.value));
|
|
4750
|
+
const type = (0, vue.computed)(() => resolveItemType(mForm, props.config, props));
|
|
3685
4751
|
const tagName = (0, vue.computed)(() => {
|
|
3686
4752
|
if (type.value === "component" && props.config.component) return props.config.component;
|
|
3687
4753
|
return getField(type.value || "container") || `m-${items.value ? "form" : "fields"}-${type.value}`;
|
|
@@ -3715,8 +4781,6 @@
|
|
|
3715
4781
|
return new Set(DEFAULT_SELF_DIFF_FIELD_TYPES);
|
|
3716
4782
|
});
|
|
3717
4783
|
const isSelfDiffField = (0, vue.computed)(() => effectiveSelfDiffFieldTypes.value.has(type.value));
|
|
3718
|
-
const silentMode = (0, vue.inject)(FORM_SILENT_MODE_KEY, false);
|
|
3719
|
-
const shouldRenderLeafField = (0, vue.computed)(() => !silentMode || !getSilentLeafFieldTypes().has(type.value));
|
|
3720
4784
|
const disabled = (0, vue.computed)(() => props.disabled || filterFunction(mForm, props.config.disabled, props));
|
|
3721
4785
|
const text = (0, vue.computed)(() => filterFunction(mForm, props.config.text, props));
|
|
3722
4786
|
const tooltip = (0, vue.computed)(() => {
|
|
@@ -3748,12 +4812,13 @@
|
|
|
3748
4812
|
const formItemProps = (0, vue.computed)(() => ({
|
|
3749
4813
|
prop: itemProp.value,
|
|
3750
4814
|
labelWidth: itemLabelWidth.value,
|
|
3751
|
-
labelPosition:
|
|
4815
|
+
labelPosition: itemLabelPosition.value,
|
|
3752
4816
|
rules: rule.value,
|
|
3753
4817
|
extra: filterFunction(mForm, props.config.extra, props),
|
|
3754
4818
|
extraTips: props.config.extraTips
|
|
3755
4819
|
}));
|
|
3756
4820
|
const itemLabelWidth = (0, vue.computed)(() => props.config.labelWidth ?? props.labelWidth);
|
|
4821
|
+
const itemLabelPosition = (0, vue.computed)(() => props.config.labelPosition ?? props.labelPosition);
|
|
3757
4822
|
(0, vue.watchEffect)(() => {
|
|
3758
4823
|
expand.value = props.expandMore;
|
|
3759
4824
|
});
|
|
@@ -3782,13 +4847,7 @@
|
|
|
3782
4847
|
};
|
|
3783
4848
|
const onAddDiffCount = () => emit("addDiffCount");
|
|
3784
4849
|
const hasModifyKey = (eventDataItem) => typeof eventDataItem?.modifyKey !== "undefined" && eventDataItem.modifyKey !== "";
|
|
3785
|
-
const isValidName = () =>
|
|
3786
|
-
const valueType = typeof name.value;
|
|
3787
|
-
if (valueType !== "string" && valueType !== "symbol" && valueType !== "number") return false;
|
|
3788
|
-
if (name.value === "") return false;
|
|
3789
|
-
if (typeof name.value === "number") return name.value >= 0;
|
|
3790
|
-
return true;
|
|
3791
|
-
};
|
|
4850
|
+
const isValidName$1 = () => isValidName(name.value);
|
|
3792
4851
|
const createModelProxy = (target, setModelFn, pathPrefix = "") => {
|
|
3793
4852
|
return new Proxy(target, {
|
|
3794
4853
|
get: (obj, key) => {
|
|
@@ -3880,6 +4939,7 @@
|
|
|
3880
4939
|
"step-active": __props.stepActive,
|
|
3881
4940
|
"expand-more": expand.value,
|
|
3882
4941
|
"label-width": itemLabelWidth.value,
|
|
4942
|
+
"label-position": itemLabelPosition.value,
|
|
3883
4943
|
style: __props.config.fieldStyle,
|
|
3884
4944
|
onChange: onChangeHandler,
|
|
3885
4945
|
onAddDiffCount
|
|
@@ -3890,9 +4950,10 @@
|
|
|
3890
4950
|
"step-active",
|
|
3891
4951
|
"expand-more",
|
|
3892
4952
|
"label-width",
|
|
4953
|
+
"label-position",
|
|
3893
4954
|
"style"
|
|
3894
4955
|
])) : type.value && display$3.value && !showDiff.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicFormItem), (0, vue.mergeProps)(formItemProps.value, { class: { "tmagic-form-hidden": `${itemLabelWidth.value}` === "0" || !text.value } }), {
|
|
3895
|
-
label: (0, vue.withCtx)(() => [
|
|
4956
|
+
label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
|
|
3896
4957
|
config: __props.config,
|
|
3897
4958
|
type: type.value,
|
|
3898
4959
|
text: text.value,
|
|
@@ -3910,8 +4971,8 @@
|
|
|
3910
4971
|
"use-label",
|
|
3911
4972
|
"label-title",
|
|
3912
4973
|
"text"
|
|
3913
|
-
]), __props.config.titleExtra &&
|
|
3914
|
-
default: (0, vue.withCtx)(() => [
|
|
4974
|
+
]), __props.config.titleExtra && itemLabelPosition.value === "top" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_2$8, (0, vue.toDisplayString)(__props.config.titleExtra), 1)) : (0, vue.createCommentVNode)("v-if", true)])]),
|
|
4975
|
+
default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
3915
4976
|
key: 0,
|
|
3916
4977
|
placement: tooltip.value.placement
|
|
3917
4978
|
}, {
|
|
@@ -3938,9 +4999,9 @@
|
|
|
3938
4999
|
"model",
|
|
3939
5000
|
"last-values",
|
|
3940
5001
|
"is-compare"
|
|
3941
|
-
]))]
|
|
5002
|
+
]))]),
|
|
3942
5003
|
_: 3
|
|
3943
|
-
}, 16, ["class"]),
|
|
5004
|
+
}, 16, ["class"]), __props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
3944
5005
|
key: 0,
|
|
3945
5006
|
placement: "top"
|
|
3946
5007
|
}, {
|
|
@@ -3960,7 +5021,7 @@
|
|
|
3960
5021
|
"tmagic-form-hidden": `${itemLabelWidth.value}` === "0" || !text.value,
|
|
3961
5022
|
"self-diff": true
|
|
3962
5023
|
} }), {
|
|
3963
|
-
label: (0, vue.withCtx)(() => [
|
|
5024
|
+
label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
|
|
3964
5025
|
config: __props.config,
|
|
3965
5026
|
type: type.value,
|
|
3966
5027
|
text: text.value,
|
|
@@ -3978,12 +5039,12 @@
|
|
|
3978
5039
|
"use-label",
|
|
3979
5040
|
"label-title",
|
|
3980
5041
|
"text"
|
|
3981
|
-
])]
|
|
3982
|
-
default: (0, vue.withCtx)(() => [
|
|
5042
|
+
])])]),
|
|
5043
|
+
default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
3983
5044
|
key: 0,
|
|
3984
5045
|
placement: tooltip.value.placement
|
|
3985
5046
|
}, {
|
|
3986
|
-
content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: tooltip.value.text }, null, 8, _hoisted_5$
|
|
5047
|
+
content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: tooltip.value.text }, null, 8, _hoisted_5$5)]),
|
|
3987
5048
|
default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)(fieldsProps.value, {
|
|
3988
5049
|
model: __props.model,
|
|
3989
5050
|
"last-values": __props.lastValues,
|
|
@@ -4004,7 +5065,7 @@
|
|
|
4004
5065
|
"model",
|
|
4005
5066
|
"last-values",
|
|
4006
5067
|
"is-compare"
|
|
4007
|
-
]))]
|
|
5068
|
+
]))]),
|
|
4008
5069
|
_: 3
|
|
4009
5070
|
}, 16, ["class"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
|
|
4010
5071
|
(0, vue.createCommentVNode)(" 普通字段:渲染前后两份独立的组件用于对比 "),
|
|
@@ -4013,7 +5074,7 @@
|
|
|
4013
5074
|
"tmagic-form-hidden": `${itemLabelWidth.value}` === "0" || !text.value,
|
|
4014
5075
|
"show-before-diff": true
|
|
4015
5076
|
} }), {
|
|
4016
|
-
label: (0, vue.withCtx)(() => [
|
|
5077
|
+
label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
|
|
4017
5078
|
config: __props.config,
|
|
4018
5079
|
type: type.value,
|
|
4019
5080
|
text: text.value,
|
|
@@ -4031,8 +5092,8 @@
|
|
|
4031
5092
|
"use-label",
|
|
4032
5093
|
"label-title",
|
|
4033
5094
|
"text"
|
|
4034
|
-
])]
|
|
4035
|
-
default: (0, vue.withCtx)(() => [
|
|
5095
|
+
])])]),
|
|
5096
|
+
default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
4036
5097
|
key: 0,
|
|
4037
5098
|
placement: tooltip.value.placement
|
|
4038
5099
|
}, {
|
|
@@ -4045,10 +5106,10 @@
|
|
|
4045
5106
|
}, 8, ["placement"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)({ key: 1 }, fieldsProps.value, {
|
|
4046
5107
|
model: __props.lastValues,
|
|
4047
5108
|
onChange: onChangeHandler
|
|
4048
|
-
}), null, 16, ["model"]))]
|
|
5109
|
+
}), null, 16, ["model"]))]),
|
|
4049
5110
|
_: 3
|
|
4050
5111
|
}, 16, ["class"]),
|
|
4051
|
-
|
|
5112
|
+
__props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
4052
5113
|
key: 0,
|
|
4053
5114
|
placement: "top"
|
|
4054
5115
|
}, {
|
|
@@ -4070,7 +5131,7 @@
|
|
|
4070
5131
|
"show-after-diff": true
|
|
4071
5132
|
}
|
|
4072
5133
|
}), {
|
|
4073
|
-
label: (0, vue.withCtx)(() => [
|
|
5134
|
+
label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
|
|
4074
5135
|
config: __props.config,
|
|
4075
5136
|
type: type.value,
|
|
4076
5137
|
text: text.value,
|
|
@@ -4088,8 +5149,8 @@
|
|
|
4088
5149
|
"use-label",
|
|
4089
5150
|
"label-title",
|
|
4090
5151
|
"text"
|
|
4091
|
-
])]
|
|
4092
|
-
default: (0, vue.withCtx)(() => [
|
|
5152
|
+
])])]),
|
|
5153
|
+
default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
4093
5154
|
key: 0,
|
|
4094
5155
|
placement: tooltip.value.placement
|
|
4095
5156
|
}, {
|
|
@@ -4102,10 +5163,10 @@
|
|
|
4102
5163
|
}, 8, ["placement"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)({ key: 1 }, fieldsProps.value, {
|
|
4103
5164
|
model: __props.model,
|
|
4104
5165
|
onChange: onChangeHandler
|
|
4105
|
-
}), null, 16, ["model"]))]
|
|
5166
|
+
}), null, 16, ["model"]))]),
|
|
4106
5167
|
_: 3
|
|
4107
5168
|
}, 16, ["style", "class"]),
|
|
4108
|
-
|
|
5169
|
+
__props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
4109
5170
|
key: 1,
|
|
4110
5171
|
placement: "top"
|
|
4111
5172
|
}, {
|
|
@@ -4120,11 +5181,11 @@
|
|
|
4120
5181
|
_: 1
|
|
4121
5182
|
})) : (0, vue.createCommentVNode)("v-if", true)
|
|
4122
5183
|
], 64))
|
|
4123
|
-
], 64)) : items.value && display$3.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 4 }, [(isValidName() ? __props.model[name.value] : __props.model) ? ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, (0, vue.renderList)(items.value, (item) => {
|
|
5184
|
+
], 64)) : items.value && display$3.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 4 }, [(isValidName$1() ? __props.model[name.value] : __props.model) ? ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, (0, vue.renderList)(items.value, (item) => {
|
|
4124
5185
|
return (0, vue.openBlock)(), (0, vue.createBlock)(_component_Container, {
|
|
4125
5186
|
key: key(item),
|
|
4126
|
-
model: isValidName() ? __props.model[name.value] : __props.model,
|
|
4127
|
-
"last-values": isValidName() ? __props.lastValues[name.value] || {} : __props.lastValues,
|
|
5187
|
+
model: isValidName$1() ? __props.model[name.value] : __props.model,
|
|
5188
|
+
"last-values": isValidName$1() ? __props.lastValues[name.value] || {} : __props.lastValues,
|
|
4128
5189
|
"is-compare": __props.isCompare,
|
|
4129
5190
|
config: item,
|
|
4130
5191
|
size: __props.size,
|
|
@@ -4132,6 +5193,7 @@
|
|
|
4132
5193
|
"step-active": __props.stepActive,
|
|
4133
5194
|
"expand-more": expand.value,
|
|
4134
5195
|
"label-width": itemLabelWidth.value,
|
|
5196
|
+
"label-position": itemLabelPosition.value,
|
|
4135
5197
|
prop: itemProp.value,
|
|
4136
5198
|
onChange: onChangeHandler,
|
|
4137
5199
|
onAddDiffCount
|
|
@@ -4149,6 +5211,7 @@
|
|
|
4149
5211
|
"step-active",
|
|
4150
5212
|
"expand-more",
|
|
4151
5213
|
"label-width",
|
|
5214
|
+
"label-position",
|
|
4152
5215
|
"prop"
|
|
4153
5216
|
]);
|
|
4154
5217
|
}), 128)) : (0, vue.createCommentVNode)("v-if", true)], 64)) : (0, vue.createCommentVNode)("v-if", true), __props.config.expand && type.value !== "fieldset" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_10, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
@@ -4204,7 +5267,7 @@
|
|
|
4204
5267
|
type: Boolean,
|
|
4205
5268
|
default: true
|
|
4206
5269
|
},
|
|
4207
|
-
|
|
5270
|
+
context: {},
|
|
4208
5271
|
showDiff: {},
|
|
4209
5272
|
selfDiffFieldTypes: {},
|
|
4210
5273
|
theme: {}
|
|
@@ -4224,8 +5287,6 @@
|
|
|
4224
5287
|
const initialized = (0, vue.ref)(false);
|
|
4225
5288
|
const values = (0, vue.ref)({});
|
|
4226
5289
|
const lastValuesProcessed = (0, vue.ref)({});
|
|
4227
|
-
const fields = /* @__PURE__ */ new Map();
|
|
4228
|
-
const requestFuc = getConfig("request");
|
|
4229
5290
|
/**
|
|
4230
5291
|
* 当前表单生效的主题名称:
|
|
4231
5292
|
* - 优先用本组件自己的 `props.theme`;
|
|
@@ -4255,18 +5316,14 @@
|
|
|
4255
5316
|
* 2. `values` / `lastValuesProcessed` 是 ref,Vue 的 `reactive` 会自动解包,因此每次
|
|
4256
5317
|
* 访问 `formState.values` / `formState.lastValuesProcessed` 也都是当前 ref 值。
|
|
4257
5318
|
*
|
|
4258
|
-
* 3.
|
|
4259
|
-
*
|
|
4260
|
-
* set,触发依赖通知;`extendState` 同步段读到的响应式数据变化时会自动重跑,
|
|
4261
|
-
* 把最新值刷进 formState。
|
|
4262
|
-
* - accessor 描述符(`{ get stage() { return ... } }`)按原样写入,调用方可以控制
|
|
4263
|
-
* 读时求值,每次读取都会重新执行 getter。
|
|
5319
|
+
* 3. 宿主业务上下文不再 merge 进 coreState,而是单独放在 `contextRef` 上,由读穿
|
|
5320
|
+
* Proxy 在 miss 时落到 context。核心字段结构性优先,`mForm.xxx` 永久兼容旧读法。
|
|
4264
5321
|
*
|
|
4265
5322
|
* 4. `popperClass` 会自动拼接 `themeClass`:调用方传入的 `popperClass` + 当前主题
|
|
4266
5323
|
* 修饰类(含祖先 `<MEditor>` provide 的主题)。这样所有透传到 Element Plus 弹层
|
|
4267
5324
|
* `popper-class` 的字段(Select / DateTime / Cascader 等)能自带主题作用域。
|
|
4268
5325
|
*/
|
|
4269
|
-
const
|
|
5326
|
+
const coreState = (0, vue.reactive)({
|
|
4270
5327
|
get keyProp() {
|
|
4271
5328
|
return props.keyProp;
|
|
4272
5329
|
},
|
|
@@ -4295,68 +5352,20 @@
|
|
|
4295
5352
|
values,
|
|
4296
5353
|
lastValuesProcessed,
|
|
4297
5354
|
$emit: emit,
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
$messageBox: _tmagic_design.tMagicMessageBox,
|
|
4303
|
-
$message: _tmagic_design.tMagicMessage,
|
|
4304
|
-
post: (options) => {
|
|
4305
|
-
if (requestFuc) return requestFuc({
|
|
4306
|
-
method: "POST",
|
|
4307
|
-
...options
|
|
4308
|
-
});
|
|
4309
|
-
}
|
|
5355
|
+
...createFormStateBase({
|
|
5356
|
+
$message: _tmagic_design.tMagicMessage,
|
|
5357
|
+
$messageBox: _tmagic_design.tMagicMessageBox
|
|
5358
|
+
})
|
|
4310
5359
|
});
|
|
5360
|
+
const ancestorContext = (0, vue.inject)(FORM_CONTEXT_KEY, void 0);
|
|
4311
5361
|
/**
|
|
4312
|
-
*
|
|
4313
|
-
*
|
|
4314
|
-
* 在 `extendState` 首次合并前捕获,`applyExtendState` 会据此禁止 `extendState`
|
|
4315
|
-
* 覆盖这些已有字段(只能新增字段),避免表单核心状态被外部意外改写。
|
|
4316
|
-
*
|
|
4317
|
-
* 之所以在此处(effect 之外)捕获而不是在 `applyExtendState` 内动态取:
|
|
4318
|
-
* `watchEffect` 会在依赖变化时重跑,若动态取,`extendState` 自己新增的字段在第二次
|
|
4319
|
-
* 合并时也会被当成「已有 key」而拒绝刷新;这里只锁定内置字段即可规避该问题。
|
|
5362
|
+
* 宿主业务上下文:`props.context` 覆盖祖先注入的同名字段。
|
|
5363
|
+
* 嵌套表单(Link / FormBox / FormDialog)通过下面的 provide 自动继承。
|
|
4320
5364
|
*/
|
|
4321
|
-
const
|
|
4322
|
-
|
|
4323
|
-
* `extendState` 的同步段(直到第一个 `await` 之前)所访问的任何响应式数据,
|
|
4324
|
-
* 都会被 `watchEffect` 自动跟踪。这样可以兼容历史用法 ——
|
|
4325
|
-
*
|
|
4326
|
-
* extendState: (formState) => ({
|
|
4327
|
-
* username: store.username, // 同步读 store,会被跟踪
|
|
4328
|
-
* env: store.env,
|
|
4329
|
-
* })
|
|
4330
|
-
*
|
|
4331
|
-
* 当 `store.username` 变化时,整个 effect 重跑,新值会被刷进 `formState`。
|
|
4332
|
-
*
|
|
4333
|
-
* prop 派生字段(initValues / config / ...)已经在上方用 getter 定义,
|
|
4334
|
-
* 这里不再重复同步;因此 `props.initValues` 这类高频变化也不会再触发
|
|
4335
|
-
* `extendState` 重跑(旧版的性能问题修复点)。
|
|
4336
|
-
*
|
|
4337
|
-
* 实现细节:合并逻辑统一收口在 `applyExtendState`(utils/form)——
|
|
4338
|
-
* data 描述符走 reactive proxy 的 set 触发依赖通知(与旧版「逐项赋值」语义等价),
|
|
4339
|
-
* accessor 描述符按原样 defineProperty 支持读时求值;
|
|
4340
|
-
* props 派生的只读 getter 字段(keyProp 等)以普通字段形式返回时会被跳过并告警。
|
|
4341
|
-
*/
|
|
4342
|
-
(0, vue.watchEffect)(async (onCleanup) => {
|
|
4343
|
-
const { extendState } = props;
|
|
4344
|
-
if (typeof extendState !== "function") return;
|
|
4345
|
-
let stale = false;
|
|
4346
|
-
onCleanup(() => {
|
|
4347
|
-
stale = true;
|
|
4348
|
-
});
|
|
4349
|
-
let state = {};
|
|
4350
|
-
try {
|
|
4351
|
-
state = await extendState(formState) || {};
|
|
4352
|
-
} catch (e) {
|
|
4353
|
-
console.error("[MForm] extendState failed:", e);
|
|
4354
|
-
return;
|
|
4355
|
-
}
|
|
4356
|
-
if (stale) return;
|
|
4357
|
-
applyExtendState(formState, state, reservedStateKeys);
|
|
4358
|
-
});
|
|
5365
|
+
const contextRef = (0, vue.computed)(() => mergeFormContexts(ancestorContext?.value, props.context));
|
|
5366
|
+
const formState = createFormStateProxy(coreState, () => contextRef.value);
|
|
4359
5367
|
(0, vue.provide)("mForm", formState);
|
|
5368
|
+
(0, vue.provide)(FORM_CONTEXT_KEY, contextRef);
|
|
4360
5369
|
/**
|
|
4361
5370
|
* 把生效主题(自身或祖先)再 provide 出去,供 form 子树内含 `Teleport` 的组件
|
|
4362
5371
|
* (如 `TMagicPopover`)在传送目标上挂 `m-theme--<theme>` 类。
|
|
@@ -4373,14 +5382,24 @@
|
|
|
4373
5382
|
}
|
|
4374
5383
|
});
|
|
4375
5384
|
const changeRecords = (0, vue.shallowRef)([]);
|
|
4376
|
-
|
|
5385
|
+
/**
|
|
5386
|
+
* 两份配置的结构是否一致;函数一律视为相等。
|
|
5387
|
+
*
|
|
5388
|
+
* 宿主(如编辑器属性面板)往往在每次节点更新后整份重新生成配置,其中的
|
|
5389
|
+
* `display` / `options` / `onChange` 都是新闭包,深比较必然判不等。若据此把 `initialized`
|
|
5390
|
+
* 置 false,整棵表单会卸载重挂,滚动位置、展开态、输入焦点全部丢失。
|
|
5391
|
+
* 配置是响应式 prop,闭包换了照样生效,只有结构变化(增删字段、换组件类型)才需要重挂。
|
|
5392
|
+
*/
|
|
5393
|
+
const isSameConfigShape = (config, preConfig) => isEqualWith(config, preConfig, (a, b) => typeof a === "function" && typeof b === "function" ? true : void 0);
|
|
5394
|
+
(0, vue.watch)([() => props.config, () => props.initValues], async ([config], [preConfig]) => {
|
|
4377
5395
|
changeRecords.value = [];
|
|
4378
|
-
if (!
|
|
5396
|
+
if (!isSameConfigShape((0, vue.toRaw)(config), (0, vue.toRaw)(preConfig))) initialized.value = false;
|
|
4379
5397
|
initValue(formState, {
|
|
4380
5398
|
initValues: props.initValues,
|
|
4381
5399
|
config: props.config
|
|
4382
5400
|
}).then((value) => {
|
|
4383
5401
|
values.value = value;
|
|
5402
|
+
applyMountValueEffects(formState, props.config, values.value);
|
|
4384
5403
|
initialized.value = !props.isCompare;
|
|
4385
5404
|
if (props.validateOnInit) (0, vue.nextTick)(() => {
|
|
4386
5405
|
tMagicFormRef.value?.validate();
|
|
@@ -4391,6 +5410,7 @@
|
|
|
4391
5410
|
config: props.config
|
|
4392
5411
|
}).then((value) => {
|
|
4393
5412
|
lastValuesProcessed.value = value;
|
|
5413
|
+
applyMountValueEffects(formState, props.config, lastValuesProcessed.value);
|
|
4394
5414
|
initialized.value = true;
|
|
4395
5415
|
});
|
|
4396
5416
|
}, { immediate: true });
|
|
@@ -4408,51 +5428,17 @@
|
|
|
4408
5428
|
const submitHandler = (e) => {
|
|
4409
5429
|
if (props.preventSubmitDefault) e.preventDefault();
|
|
4410
5430
|
};
|
|
4411
|
-
|
|
4412
|
-
* 通过 name 从 config 中查找对应的 text
|
|
4413
|
-
* @param name - 字段名,支持点分隔的路径格式,如 'a.b.c'
|
|
4414
|
-
* @param config - 表单配置数组
|
|
4415
|
-
* @returns 找到的 text 值,如果未找到则返回 undefined
|
|
4416
|
-
*/
|
|
4417
|
-
const getTextByName = (name, config = props.config) => {
|
|
4418
|
-
if (!name || !Array.isArray(config)) return void 0;
|
|
4419
|
-
const nameParts = name.split(".");
|
|
4420
|
-
const findInConfig = (configs, parts) => {
|
|
4421
|
-
if (parts.length === 0) return void 0;
|
|
4422
|
-
const [currentPart, ...remainingParts] = parts;
|
|
4423
|
-
for (const item of configs) {
|
|
4424
|
-
if (item.name === currentPart) {
|
|
4425
|
-
if (remainingParts.length === 0) return typeof item.text === "string" ? item.text : void 0;
|
|
4426
|
-
if ("items" in item && Array.isArray(item.items)) {
|
|
4427
|
-
const result = findInConfig(item.items, remainingParts);
|
|
4428
|
-
if (result !== void 0) return result;
|
|
4429
|
-
}
|
|
4430
|
-
}
|
|
4431
|
-
if ("items" in item && Array.isArray(item.items)) {
|
|
4432
|
-
const result = findInConfig(item.items, parts);
|
|
4433
|
-
if (result !== void 0) return result;
|
|
4434
|
-
}
|
|
4435
|
-
}
|
|
4436
|
-
};
|
|
4437
|
-
return findInConfig(config, nameParts);
|
|
4438
|
-
};
|
|
5431
|
+
const getTextByName$1 = (name, config = props.config) => getTextByName(name, config);
|
|
4439
5432
|
/**
|
|
4440
5433
|
* 将校验返回的 invalidFields 汇总为可读的错误文案(多条以 `<br>` 拼接)。
|
|
4441
5434
|
*
|
|
4442
|
-
*
|
|
4443
|
-
*
|
|
5435
|
+
* 实现收口在 `utils/validateError`,供渲染式校验(本组件的 `submitForm` / `validate`)与
|
|
5436
|
+
* 无渲染校验(`validateValues`)共用,保证两条链路产出的错误文案格式完全一致。
|
|
4444
5437
|
*/
|
|
4445
|
-
const formatValidateError = (invalidFields) => {
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
const name = field || prop;
|
|
4450
|
-
const text = (props.useFieldTextInError ? getTextByName(name, props.config) : void 0) || name;
|
|
4451
|
-
error.push(`${text} -> ${message}`);
|
|
4452
|
-
});
|
|
4453
|
-
});
|
|
4454
|
-
return error.join("<br>");
|
|
4455
|
-
};
|
|
5438
|
+
const formatValidateError$1 = (invalidFields) => formatValidateError(invalidFields, {
|
|
5439
|
+
config: props.config,
|
|
5440
|
+
useFieldTextInError: props.useFieldTextInError
|
|
5441
|
+
});
|
|
4456
5442
|
__expose({
|
|
4457
5443
|
values,
|
|
4458
5444
|
lastValuesProcessed,
|
|
@@ -4472,7 +5458,7 @@
|
|
|
4472
5458
|
return native ? values.value : cloneDeep((0, vue.toRaw)(values.value));
|
|
4473
5459
|
} catch (invalidFields) {
|
|
4474
5460
|
emit("error", invalidFields);
|
|
4475
|
-
throw new Error(formatValidateError(invalidFields));
|
|
5461
|
+
throw new Error(formatValidateError$1(invalidFields));
|
|
4476
5462
|
}
|
|
4477
5463
|
},
|
|
4478
5464
|
/**
|
|
@@ -4497,10 +5483,10 @@
|
|
|
4497
5483
|
if (result !== true) throw result;
|
|
4498
5484
|
return "";
|
|
4499
5485
|
} catch (invalidFields) {
|
|
4500
|
-
return formatValidateError(invalidFields);
|
|
5486
|
+
return formatValidateError$1(invalidFields);
|
|
4501
5487
|
}
|
|
4502
5488
|
},
|
|
4503
|
-
getTextByName
|
|
5489
|
+
getTextByName: getTextByName$1
|
|
4504
5490
|
});
|
|
4505
5491
|
return (_ctx, _cache) => {
|
|
4506
5492
|
return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicForm), {
|
|
@@ -4526,6 +5512,7 @@
|
|
|
4526
5512
|
"last-values": lastValuesProcessed.value,
|
|
4527
5513
|
"is-compare": __props.isCompare,
|
|
4528
5514
|
"label-width": item.labelWidth || __props.labelWidth,
|
|
5515
|
+
"label-position": item.labelPosition || __props.labelPosition,
|
|
4529
5516
|
"step-active": __props.stepActive,
|
|
4530
5517
|
size: __props.size,
|
|
4531
5518
|
onChange: changeHandler
|
|
@@ -4540,6 +5527,7 @@
|
|
|
4540
5527
|
"last-values",
|
|
4541
5528
|
"is-compare",
|
|
4542
5529
|
"label-width",
|
|
5530
|
+
"label-position",
|
|
4543
5531
|
"step-active",
|
|
4544
5532
|
"size"
|
|
4545
5533
|
]);
|
|
@@ -4558,38 +5546,29 @@
|
|
|
4558
5546
|
});
|
|
4559
5547
|
//#endregion
|
|
4560
5548
|
//#region packages/form/src/submitForm.ts
|
|
4561
|
-
/** 未指定或传入非正数 timeout 时的兜底超时(毫秒),保证非 debug 挂载始终能被清理 */
|
|
4562
|
-
var DEFAULT_MOUNT_TIMEOUT = 1e4;
|
|
4563
5549
|
/**
|
|
4564
|
-
* submitForm / validateForm 的公共脚手架:
|
|
5550
|
+
* `dialog: true` 时 submitForm / validateForm 的公共脚手架:
|
|
4565
5551
|
*
|
|
4566
|
-
* 创建临时容器 → 挂载一个 wrapper 组件(内含 MForm)→ 提供统一的 cleanup /
|
|
5552
|
+
* 创建临时容器 → 挂载一个 wrapper 组件(内含 MForm)→ 提供统一的 cleanup / appContext 继承 →
|
|
4567
5553
|
* 由 `createWrapper` 决定「何时调用 MForm 的哪个方法、如何 resolve/reject」。
|
|
4568
5554
|
*
|
|
4569
|
-
*
|
|
4570
|
-
* 容器创建、卸载、超时、上下文注入等模板代码在此统一收口。
|
|
5555
|
+
* 挂载出来的表单等待人工点击,因此没有超时兜底,靠「确定」/「取消」/`signal` 结束。
|
|
4571
5556
|
*/
|
|
4572
5557
|
var mountFormInstance = (options) => {
|
|
4573
|
-
const { formProps, appContext,
|
|
5558
|
+
const { formProps, appContext, signal, createWrapper } = options;
|
|
4574
5559
|
return new Promise((resolve, reject) => {
|
|
4575
5560
|
if (signal?.aborted) {
|
|
4576
5561
|
reject(signal.reason ?? /* @__PURE__ */ new Error("mountFormInstance aborted"));
|
|
4577
5562
|
return;
|
|
4578
5563
|
}
|
|
4579
5564
|
let cleaned = false;
|
|
4580
|
-
let timer = null;
|
|
4581
5565
|
let onAbort = null;
|
|
4582
5566
|
const instance = { app: null };
|
|
4583
5567
|
const container = document.createElement("div");
|
|
4584
|
-
if (hidden) container.style.display = "none";
|
|
4585
5568
|
document.body.appendChild(container);
|
|
4586
5569
|
const cleanup = () => {
|
|
4587
5570
|
if (cleaned) return;
|
|
4588
5571
|
cleaned = true;
|
|
4589
|
-
if (timer) {
|
|
4590
|
-
clearTimeout(timer);
|
|
4591
|
-
timer = null;
|
|
4592
|
-
}
|
|
4593
5572
|
if (signal && onAbort) {
|
|
4594
5573
|
signal.removeEventListener("abort", onAbort);
|
|
4595
5574
|
onAbort = null;
|
|
@@ -4609,53 +5588,16 @@
|
|
|
4609
5588
|
}
|
|
4610
5589
|
try {
|
|
4611
5590
|
const formRef = (0, vue.ref)(null);
|
|
4612
|
-
const
|
|
4613
|
-
const userWrapper = createWrapper({
|
|
5591
|
+
const wrapperComponent = createWrapper({
|
|
4614
5592
|
formRef,
|
|
4615
|
-
formProps
|
|
5593
|
+
formProps,
|
|
4616
5594
|
cleanup,
|
|
4617
5595
|
resolve,
|
|
4618
5596
|
reject
|
|
4619
5597
|
});
|
|
4620
|
-
const
|
|
4621
|
-
name: "MFormExtendStateInjector",
|
|
4622
|
-
setup() {
|
|
4623
|
-
(0, vue.watch)(() => formRef.value, (form) => {
|
|
4624
|
-
if (!form) return;
|
|
4625
|
-
let result;
|
|
4626
|
-
try {
|
|
4627
|
-
result = extendState(form.formState);
|
|
4628
|
-
} catch (e) {
|
|
4629
|
-
console.error("[MForm] extendState failed:", e);
|
|
4630
|
-
return;
|
|
4631
|
-
}
|
|
4632
|
-
const reservedStateKeys = new Set(Reflect.ownKeys(form.formState));
|
|
4633
|
-
const apply = (state) => applyExtendState(form.formState, state, reservedStateKeys);
|
|
4634
|
-
if (result && typeof result.then === "function") result.then(apply, (e) => console.error("[MForm] extendState failed:", e));
|
|
4635
|
-
else apply(result);
|
|
4636
|
-
}, {
|
|
4637
|
-
flush: "sync",
|
|
4638
|
-
immediate: true
|
|
4639
|
-
});
|
|
4640
|
-
return () => (0, vue.h)(userWrapper);
|
|
4641
|
-
}
|
|
4642
|
-
}) : userWrapper;
|
|
4643
|
-
const rootComponent = hidden ? (0, vue.defineComponent)({
|
|
4644
|
-
name: "MFormSilentProvider",
|
|
4645
|
-
setup() {
|
|
4646
|
-
(0, vue.provide)(FORM_SILENT_MODE_KEY, true);
|
|
4647
|
-
return () => (0, vue.h)(wrapperComponent);
|
|
4648
|
-
}
|
|
4649
|
-
}) : wrapperComponent;
|
|
4650
|
-
const app = (0, vue.createApp)(rootComponent);
|
|
5598
|
+
const app = (0, vue.createApp)(wrapperComponent);
|
|
4651
5599
|
instance.app = app;
|
|
4652
5600
|
if (appContext) Object.assign(app._context, appContext);
|
|
4653
|
-
if (!skipTimeout) timer = setTimeout(() => {
|
|
4654
|
-
if (!cleaned) {
|
|
4655
|
-
reject(new Error(timeoutMessage));
|
|
4656
|
-
cleanup();
|
|
4657
|
-
}
|
|
4658
|
-
}, timeout > 0 ? timeout : DEFAULT_MOUNT_TIMEOUT);
|
|
4659
5601
|
app.mount(container);
|
|
4660
5602
|
} catch (err) {
|
|
4661
5603
|
reject(err);
|
|
@@ -4664,12 +5606,12 @@
|
|
|
4664
5606
|
});
|
|
4665
5607
|
};
|
|
4666
5608
|
/**
|
|
4667
|
-
*
|
|
5609
|
+
* 构造可见弹层 wrapper:以 fixed 遮罩居中渲染 MForm,提供「确定 / 取消」按钮与错误展示区。
|
|
4668
5610
|
*
|
|
4669
|
-
* submitForm 与 validateForm
|
|
5611
|
+
* submitForm 与 validateForm 的弹层 UI 完全一致,仅「确定」时调用的实例方法、错误处理与取消文案不同,
|
|
4670
5612
|
* 这些差异通过 `onConfirm` / `onCancel` 注入,弹层结构在此统一收口。
|
|
4671
5613
|
*/
|
|
4672
|
-
var
|
|
5614
|
+
var createDialogWrapper = (options) => {
|
|
4673
5615
|
const { formRef, formProps, title, name, onConfirm, onCancel } = options;
|
|
4674
5616
|
const btnBase = {
|
|
4675
5617
|
padding: "8px 20px",
|
|
@@ -4752,57 +5694,17 @@
|
|
|
4752
5694
|
});
|
|
4753
5695
|
};
|
|
4754
5696
|
/**
|
|
4755
|
-
*
|
|
5697
|
+
* 可见弹层提交:把表单渲染在弹层里,点击「确定」才调用挂载实例的 `submitForm`。
|
|
4756
5698
|
*
|
|
4757
|
-
*
|
|
4758
|
-
* 一个不可见的 Form 组件实例,等待初始化完成后调用其 `submitForm` 方法,
|
|
4759
|
-
* 校验通过则 resolve 表单值,校验失败则 reject 错误信息,最后自动卸载实例。
|
|
4760
|
-
*
|
|
4761
|
-
* @example
|
|
4762
|
-
* ```ts
|
|
4763
|
-
* import { submitForm } from '@tmagic/form';
|
|
4764
|
-
*
|
|
4765
|
-
* try {
|
|
4766
|
-
* const values = await submitForm({
|
|
4767
|
-
* config: [...],
|
|
4768
|
-
* initValues: { name: 'foo' },
|
|
4769
|
-
* });
|
|
4770
|
-
* console.log(values);
|
|
4771
|
-
* } catch (e) {
|
|
4772
|
-
* console.error(e);
|
|
4773
|
-
* }
|
|
4774
|
-
*
|
|
4775
|
-
* // 需要同时获取变更记录时:
|
|
4776
|
-
* const { values, changeRecords } = await submitForm({
|
|
4777
|
-
* config: [...],
|
|
4778
|
-
* initValues: { name: 'foo' },
|
|
4779
|
-
* returnChangeRecords: true,
|
|
4780
|
-
* });
|
|
4781
|
-
*
|
|
4782
|
-
* // 调试模式:可见地渲染表单,点击「确定」才提交:
|
|
4783
|
-
* const values = await submitForm({
|
|
4784
|
-
* config: [...],
|
|
4785
|
-
* initValues: { name: 'foo' },
|
|
4786
|
-
* debug: true,
|
|
4787
|
-
* });
|
|
4788
|
-
* ```
|
|
5699
|
+
* 用于需要用户填写/确认的场景;默认路径仍是无渲染校验(见 `submitForm` 的说明)。
|
|
4789
5700
|
*/
|
|
4790
|
-
var
|
|
4791
|
-
const { native, appContext,
|
|
5701
|
+
var submitFormByDialogRender = (options) => {
|
|
5702
|
+
const { native, appContext, returnChangeRecords, signal, dialog, title, ...formProps } = options;
|
|
4792
5703
|
return mountFormInstance({
|
|
4793
5704
|
formProps,
|
|
4794
5705
|
appContext,
|
|
4795
|
-
timeout,
|
|
4796
5706
|
signal,
|
|
4797
|
-
hidden: !debug,
|
|
4798
|
-
skipTimeout: debug,
|
|
4799
|
-
timeoutMessage: `submitForm timeout after ${timeout}ms: form is not initialized.`,
|
|
4800
5707
|
createWrapper: ({ formRef, formProps, cleanup, resolve, reject }) => {
|
|
4801
|
-
/**
|
|
4802
|
-
* 执行一次提交:nextTick 等待子组件渲染 → 快照 changeRecords → 调用实例 submitForm → resolve。
|
|
4803
|
-
* 校验失败时交给 `onValidateError` 决定「保留弹层展示错误(debug)」还是「reject 并清理(普通)」,
|
|
4804
|
-
* 从而让 debug 与普通模式共享同一份提交逻辑。
|
|
4805
|
-
*/
|
|
4806
5708
|
const doSubmit = async (onValidateError) => {
|
|
4807
5709
|
try {
|
|
4808
5710
|
await (0, vue.nextTick)();
|
|
@@ -4817,39 +5719,19 @@
|
|
|
4817
5719
|
onValidateError(err);
|
|
4818
5720
|
}
|
|
4819
5721
|
};
|
|
4820
|
-
|
|
5722
|
+
return createDialogWrapper({
|
|
4821
5723
|
formRef,
|
|
4822
5724
|
formProps,
|
|
4823
5725
|
name: "MFormSubmitWrapper",
|
|
4824
|
-
title: "submitForm
|
|
5726
|
+
title: title ?? "submitForm",
|
|
4825
5727
|
onConfirm: (setError) => doSubmit((err) => {
|
|
4826
5728
|
setError(err instanceof Error ? err.message : String(err));
|
|
4827
5729
|
}),
|
|
4828
5730
|
onCancel: () => {
|
|
4829
|
-
reject(/* @__PURE__ */ new Error("submitForm canceled
|
|
5731
|
+
reject(/* @__PURE__ */ new Error("submitForm canceled."));
|
|
4830
5732
|
cleanup();
|
|
4831
5733
|
}
|
|
4832
5734
|
});
|
|
4833
|
-
return (0, vue.defineComponent)({
|
|
4834
|
-
name: "MFormSubmitWrapper",
|
|
4835
|
-
setup() {
|
|
4836
|
-
const stop = (0, vue.watch)(() => formRef.value?.initialized, (initialized) => {
|
|
4837
|
-
if (!initialized) return;
|
|
4838
|
-
stop();
|
|
4839
|
-
doSubmit((err) => {
|
|
4840
|
-
reject(err);
|
|
4841
|
-
cleanup();
|
|
4842
|
-
});
|
|
4843
|
-
}, {
|
|
4844
|
-
flush: "post",
|
|
4845
|
-
immediate: true
|
|
4846
|
-
});
|
|
4847
|
-
return () => (0, vue.h)(Form_default, {
|
|
4848
|
-
...formProps,
|
|
4849
|
-
ref: formRef
|
|
4850
|
-
});
|
|
4851
|
-
}
|
|
4852
|
-
});
|
|
4853
5735
|
}
|
|
4854
5736
|
});
|
|
4855
5737
|
};
|
|
@@ -4890,64 +5772,23 @@
|
|
|
4890
5772
|
var stripTabItemsLazy = (config) => {
|
|
4891
5773
|
const cloned = cloneConfigValue(config);
|
|
4892
5774
|
removeTabItemsLazy(cloned);
|
|
4893
|
-
return cloned;
|
|
4894
|
-
};
|
|
4895
|
-
/**
|
|
4896
|
-
*
|
|
4897
|
-
*
|
|
4898
|
-
* 与 `submitForm` 类似,内部会临时挂载一个不可见的 MForm 实例,等待其初始化完成后调用
|
|
4899
|
-
* 实例的 `validate` 方法,返回汇总后的错误文案,随后自动卸载实例。
|
|
4900
|
-
*
|
|
4901
|
-
* 与 `submitForm` 的区别:
|
|
4902
|
-
* - 「静默」:校验失败不抛异常、不触发 `error` 事件、不返回表单值;
|
|
4903
|
-
* - 仅用于「探测」配置是否合法,适合源码保存后校验、批量校验组件配置等场景。
|
|
4904
|
-
*
|
|
4905
|
-
* 由于每次都新建一个独立的 MForm 实例,调用方无需持有任何表单 ref,也不会污染
|
|
4906
|
-
* 页面上正在展示的表单状态。
|
|
4907
|
-
*
|
|
4908
|
-
* @returns 校验通过返回空字符串 `''`,否则返回以 `<br>` 拼接的错误文案。
|
|
4909
|
-
* 仅在初始化超时或挂载失败等异常情况下才会 reject。
|
|
4910
|
-
*
|
|
4911
|
-
* @example
|
|
4912
|
-
* ```ts
|
|
4913
|
-
* import { validateForm } from '@tmagic/form';
|
|
4914
|
-
*
|
|
4915
|
-
* const error = await validateForm({
|
|
4916
|
-
* config: [...],
|
|
4917
|
-
* initValues: { name: 'foo' },
|
|
4918
|
-
* appContext: getCurrentInstance()?.appContext,
|
|
4919
|
-
* });
|
|
4920
|
-
* if (error) {
|
|
4921
|
-
* // 配置不合法,error 为错误文案
|
|
4922
|
-
* }
|
|
5775
|
+
return cloned;
|
|
5776
|
+
};
|
|
5777
|
+
/**
|
|
5778
|
+
* 可见弹层校验:把表单渲染在弹层里,点击「确定」才调用挂载实例的 `validate`。
|
|
4923
5779
|
*
|
|
4924
|
-
*
|
|
4925
|
-
* const error = await validateForm({
|
|
4926
|
-
* config: [...],
|
|
4927
|
-
* initValues: { name: 'foo' },
|
|
4928
|
-
* debug: true,
|
|
4929
|
-
* });
|
|
4930
|
-
* ```
|
|
5780
|
+
* 用于需要用户填写/确认的场景;默认路径仍是无渲染校验(见 `validateForm` 的说明)。
|
|
4931
5781
|
*/
|
|
4932
|
-
var
|
|
4933
|
-
const { appContext,
|
|
5782
|
+
var validateFormByDialogRender = (options) => {
|
|
5783
|
+
const { appContext, config, signal, dialog, title, ...rest } = options;
|
|
4934
5784
|
return mountFormInstance({
|
|
4935
5785
|
formProps: {
|
|
4936
5786
|
...rest,
|
|
4937
5787
|
config: stripTabItemsLazy(config)
|
|
4938
5788
|
},
|
|
4939
5789
|
appContext,
|
|
4940
|
-
timeout,
|
|
4941
5790
|
signal,
|
|
4942
|
-
hidden: !debug,
|
|
4943
|
-
skipTimeout: debug,
|
|
4944
|
-
timeoutMessage: `validateForm timeout after ${timeout}ms: form is not initialized.`,
|
|
4945
5791
|
createWrapper: ({ formRef, formProps, cleanup, resolve, reject }) => {
|
|
4946
|
-
/**
|
|
4947
|
-
* 执行一次校验:nextTick 等待子组件渲染 → 调用实例 validate → 通过则 resolve ''。
|
|
4948
|
-
* 校验失败(返回非空错误文案)时交给 `onInvalid` 决定「静默 resolve 错误文案(普通)」
|
|
4949
|
-
* 还是「在弹层展示错误并保留供重试(debug)」,从而让两种模式共享同一份校验逻辑。
|
|
4950
|
-
*/
|
|
4951
5792
|
const doValidate = async (onInvalid) => {
|
|
4952
5793
|
try {
|
|
4953
5794
|
await (0, vue.nextTick)();
|
|
@@ -4962,42 +5803,40 @@
|
|
|
4962
5803
|
cleanup();
|
|
4963
5804
|
}
|
|
4964
5805
|
};
|
|
4965
|
-
|
|
5806
|
+
return createDialogWrapper({
|
|
4966
5807
|
formRef,
|
|
4967
5808
|
formProps,
|
|
4968
5809
|
name: "MFormValidateWrapper",
|
|
4969
|
-
title: "validateForm
|
|
5810
|
+
title: title ?? "validateForm",
|
|
4970
5811
|
onConfirm: (setError) => doValidate((error) => {
|
|
4971
5812
|
setError(error);
|
|
4972
5813
|
}),
|
|
4973
5814
|
onCancel: () => {
|
|
4974
|
-
reject(/* @__PURE__ */ new Error("validateForm canceled
|
|
5815
|
+
reject(/* @__PURE__ */ new Error("validateForm canceled."));
|
|
4975
5816
|
cleanup();
|
|
4976
5817
|
}
|
|
4977
5818
|
});
|
|
4978
|
-
return (0, vue.defineComponent)({
|
|
4979
|
-
name: "MFormValidateWrapper",
|
|
4980
|
-
setup() {
|
|
4981
|
-
const stop = (0, vue.watch)(() => formRef.value?.initialized, (initialized) => {
|
|
4982
|
-
if (!initialized) return;
|
|
4983
|
-
stop();
|
|
4984
|
-
doValidate((error) => {
|
|
4985
|
-
resolve(error);
|
|
4986
|
-
cleanup();
|
|
4987
|
-
});
|
|
4988
|
-
}, {
|
|
4989
|
-
flush: "post",
|
|
4990
|
-
immediate: true
|
|
4991
|
-
});
|
|
4992
|
-
return () => (0, vue.h)(Form_default, {
|
|
4993
|
-
...formProps,
|
|
4994
|
-
ref: formRef
|
|
4995
|
-
});
|
|
4996
|
-
}
|
|
4997
|
-
});
|
|
4998
5819
|
}
|
|
4999
5820
|
});
|
|
5000
5821
|
};
|
|
5822
|
+
/**
|
|
5823
|
+
* 以命令式方式对一份「表单配置 + 值」做一次校验并取回表单值。
|
|
5824
|
+
*
|
|
5825
|
+
* 默认走无渲染实现(见 `@tmagic/form/headless`)。`dialog: true` 会把表单以弹层渲染出来。
|
|
5826
|
+
*/
|
|
5827
|
+
var submitForm = async (options) => {
|
|
5828
|
+
if (options.dialog) return submitFormByDialogRender(options);
|
|
5829
|
+
return submitForm$1(options);
|
|
5830
|
+
};
|
|
5831
|
+
/**
|
|
5832
|
+
* 以命令式方式对一份「表单配置 + 值」做一次静默校验。
|
|
5833
|
+
*
|
|
5834
|
+
* 默认走无渲染实现。`dialog: true` 的处理详见 `submitForm`。
|
|
5835
|
+
*/
|
|
5836
|
+
var validateForm = async (options) => {
|
|
5837
|
+
if (options.dialog) return validateFormByDialogRender(options);
|
|
5838
|
+
return validateForm$1(options);
|
|
5839
|
+
};
|
|
5001
5840
|
//#endregion
|
|
5002
5841
|
//#region packages/form/src/FormDialog.vue?vue&type=script&setup=true&lang.ts
|
|
5003
5842
|
var _hoisted_1$13 = { style: { "min-height": "1px" } };
|
|
@@ -5050,7 +5889,7 @@
|
|
|
5050
5889
|
type: Boolean,
|
|
5051
5890
|
default: true
|
|
5052
5891
|
},
|
|
5053
|
-
|
|
5892
|
+
context: {},
|
|
5054
5893
|
theme: {}
|
|
5055
5894
|
},
|
|
5056
5895
|
emits: [
|
|
@@ -5208,7 +6047,7 @@
|
|
|
5208
6047
|
"use-field-text-in-error": __props.useFieldTextInError,
|
|
5209
6048
|
"type-match-valid": __props.typeMatchValid,
|
|
5210
6049
|
"validate-on-init": __props.validateOnInit,
|
|
5211
|
-
|
|
6050
|
+
context: __props.context,
|
|
5212
6051
|
theme: effectiveTheme.value,
|
|
5213
6052
|
onChange: changeHandler
|
|
5214
6053
|
}, null, 8, [
|
|
@@ -5225,7 +6064,7 @@
|
|
|
5225
6064
|
"use-field-text-in-error",
|
|
5226
6065
|
"type-match-valid",
|
|
5227
6066
|
"validate-on-init",
|
|
5228
|
-
"
|
|
6067
|
+
"context",
|
|
5229
6068
|
"theme"
|
|
5230
6069
|
]), (0, vue.renderSlot)(_ctx.$slots, "default")], 4)) : (0, vue.createCommentVNode)("v-if", true)]),
|
|
5231
6070
|
_: 3
|
|
@@ -5280,7 +6119,7 @@
|
|
|
5280
6119
|
default: true
|
|
5281
6120
|
},
|
|
5282
6121
|
beforeClose: {},
|
|
5283
|
-
|
|
6122
|
+
context: {},
|
|
5284
6123
|
theme: {}
|
|
5285
6124
|
},
|
|
5286
6125
|
emits: [
|
|
@@ -5431,7 +6270,7 @@
|
|
|
5431
6270
|
"use-field-text-in-error": __props.useFieldTextInError,
|
|
5432
6271
|
"type-match-valid": __props.typeMatchValid,
|
|
5433
6272
|
"validate-on-init": __props.validateOnInit,
|
|
5434
|
-
|
|
6273
|
+
context: __props.context,
|
|
5435
6274
|
theme: effectiveTheme.value,
|
|
5436
6275
|
onChange: changeHandler
|
|
5437
6276
|
}, null, 8, [
|
|
@@ -5447,7 +6286,7 @@
|
|
|
5447
6286
|
"use-field-text-in-error",
|
|
5448
6287
|
"type-match-valid",
|
|
5449
6288
|
"validate-on-init",
|
|
5450
|
-
"
|
|
6289
|
+
"context",
|
|
5451
6290
|
"theme"
|
|
5452
6291
|
]), (0, vue.renderSlot)(_ctx.$slots, "default")], 512)) : (0, vue.createCommentVNode)("v-if", true)]),
|
|
5453
6292
|
_: 3
|
|
@@ -5493,7 +6332,7 @@
|
|
|
5493
6332
|
type: Boolean,
|
|
5494
6333
|
default: true
|
|
5495
6334
|
},
|
|
5496
|
-
|
|
6335
|
+
context: {}
|
|
5497
6336
|
},
|
|
5498
6337
|
emits: [
|
|
5499
6338
|
"change",
|
|
@@ -5557,7 +6396,7 @@
|
|
|
5557
6396
|
inline: __props.inline,
|
|
5558
6397
|
"prevent-submit-default": __props.preventSubmitDefault,
|
|
5559
6398
|
"use-field-text-in-error": __props.useFieldTextInError,
|
|
5560
|
-
|
|
6399
|
+
context: __props.context,
|
|
5561
6400
|
"type-match-valid": __props.typeMatchValid,
|
|
5562
6401
|
"validate-on-init": __props.validateOnInit,
|
|
5563
6402
|
onChange: changeHandler
|
|
@@ -5572,7 +6411,7 @@
|
|
|
5572
6411
|
"inline",
|
|
5573
6412
|
"prevent-submit-default",
|
|
5574
6413
|
"use-field-text-in-error",
|
|
5575
|
-
"
|
|
6414
|
+
"context",
|
|
5576
6415
|
"type-match-valid",
|
|
5577
6416
|
"validate-on-init"
|
|
5578
6417
|
]), (0, vue.renderSlot)(_ctx.$slots, "default")]),
|
|
@@ -5603,7 +6442,7 @@
|
|
|
5603
6442
|
var _hoisted_2$7 = ["innerHTML"];
|
|
5604
6443
|
var _hoisted_3$6 = { key: 1 };
|
|
5605
6444
|
var _hoisted_4$5 = ["innerHTML"];
|
|
5606
|
-
var _hoisted_5$
|
|
6445
|
+
var _hoisted_5$4 = ["innerHTML"];
|
|
5607
6446
|
var _hoisted_6$2 = {
|
|
5608
6447
|
key: 2,
|
|
5609
6448
|
style: { "display": "flex" }
|
|
@@ -5617,6 +6456,7 @@
|
|
|
5617
6456
|
__name: "Fieldset",
|
|
5618
6457
|
props: {
|
|
5619
6458
|
labelWidth: {},
|
|
6459
|
+
labelPosition: {},
|
|
5620
6460
|
prop: { default: "" },
|
|
5621
6461
|
size: {},
|
|
5622
6462
|
model: {},
|
|
@@ -5656,6 +6496,7 @@
|
|
|
5656
6496
|
if (props.config.items) return props.config.labelWidth || props.labelWidth;
|
|
5657
6497
|
return props.config.labelWidth || props.labelWidth || (props.config.text ? void 0 : "0");
|
|
5658
6498
|
});
|
|
6499
|
+
const lPosition = (0, vue.computed)(() => props.config.labelPosition || props.labelPosition);
|
|
5659
6500
|
const valueChangeHandler = (value) => {
|
|
5660
6501
|
emit("change", value, { modifyKey: checkboxName.value });
|
|
5661
6502
|
};
|
|
@@ -5692,7 +6533,7 @@
|
|
|
5692
6533
|
key: 0,
|
|
5693
6534
|
innerHTML: __props.config.extra,
|
|
5694
6535
|
class: "m-form-tip"
|
|
5695
|
-
}, null, 8, _hoisted_5$
|
|
6536
|
+
}, null, 8, _hoisted_5$4)) : (0, vue.createCommentVNode)("v-if", true)])), __props.config.schematic && show.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$2, [(0, vue.createElementVNode)("div", _hoisted_7$1, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.config.items, (item, index) => {
|
|
5696
6537
|
return (0, vue.openBlock)(), (0, vue.createBlock)(Container_default, {
|
|
5697
6538
|
key: key(item, index),
|
|
5698
6539
|
model: name.value ? __props.model[name.value] : __props.model,
|
|
@@ -5703,6 +6544,7 @@
|
|
|
5703
6544
|
prop: __props.prop,
|
|
5704
6545
|
disabled: __props.disabled,
|
|
5705
6546
|
labelWidth: lWidth.value,
|
|
6547
|
+
labelPosition: lPosition.value,
|
|
5706
6548
|
size: __props.size,
|
|
5707
6549
|
onChange: changeHandler,
|
|
5708
6550
|
onAddDiffCount: _cache[0] || (_cache[0] = ($event) => onAddDiffCount())
|
|
@@ -5715,6 +6557,7 @@
|
|
|
5715
6557
|
"prop",
|
|
5716
6558
|
"disabled",
|
|
5717
6559
|
"labelWidth",
|
|
6560
|
+
"labelPosition",
|
|
5718
6561
|
"size"
|
|
5719
6562
|
]);
|
|
5720
6563
|
}), 128))]), (0, vue.createElementVNode)("img", {
|
|
@@ -5730,6 +6573,7 @@
|
|
|
5730
6573
|
config: item,
|
|
5731
6574
|
prop: __props.prop,
|
|
5732
6575
|
labelWidth: lWidth.value,
|
|
6576
|
+
labelPosition: lPosition.value,
|
|
5733
6577
|
size: __props.size,
|
|
5734
6578
|
disabled: __props.disabled,
|
|
5735
6579
|
onChange: changeHandler,
|
|
@@ -5742,6 +6586,7 @@
|
|
|
5742
6586
|
"config",
|
|
5743
6587
|
"prop",
|
|
5744
6588
|
"labelWidth",
|
|
6589
|
+
"labelPosition",
|
|
5745
6590
|
"size",
|
|
5746
6591
|
"disabled"
|
|
5747
6592
|
]);
|
|
@@ -5761,6 +6606,7 @@
|
|
|
5761
6606
|
config: {},
|
|
5762
6607
|
name: {},
|
|
5763
6608
|
labelWidth: {},
|
|
6609
|
+
labelPosition: {},
|
|
5764
6610
|
prop: {},
|
|
5765
6611
|
size: {},
|
|
5766
6612
|
disabled: { type: Boolean }
|
|
@@ -5794,6 +6640,7 @@
|
|
|
5794
6640
|
size: __props.size,
|
|
5795
6641
|
disabled: __props.disabled,
|
|
5796
6642
|
"label-width": __props.config.labelWidth || __props.labelWidth,
|
|
6643
|
+
"label-position": __props.config.labelPosition || __props.labelPosition,
|
|
5797
6644
|
onChange: changeHandler,
|
|
5798
6645
|
onAddDiffCount: _cache[0] || (_cache[0] = ($event) => onAddDiffCount())
|
|
5799
6646
|
}, null, 8, [
|
|
@@ -5804,7 +6651,8 @@
|
|
|
5804
6651
|
"prop",
|
|
5805
6652
|
"size",
|
|
5806
6653
|
"disabled",
|
|
5807
|
-
"label-width"
|
|
6654
|
+
"label-width",
|
|
6655
|
+
"label-position"
|
|
5808
6656
|
]);
|
|
5809
6657
|
}), 128))], 4);
|
|
5810
6658
|
};
|
|
@@ -5823,7 +6671,7 @@
|
|
|
5823
6671
|
style: { "display": "flex" }
|
|
5824
6672
|
};
|
|
5825
6673
|
var _hoisted_4$4 = { style: { "flex": "1" } };
|
|
5826
|
-
var _hoisted_5$
|
|
6674
|
+
var _hoisted_5$3 = ["src"];
|
|
5827
6675
|
//#endregion
|
|
5828
6676
|
//#region packages/form/src/containers/Panel.vue
|
|
5829
6677
|
var Panel_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
@@ -5836,6 +6684,7 @@
|
|
|
5836
6684
|
config: {},
|
|
5837
6685
|
name: {},
|
|
5838
6686
|
labelWidth: {},
|
|
6687
|
+
labelPosition: {},
|
|
5839
6688
|
prop: {},
|
|
5840
6689
|
size: {},
|
|
5841
6690
|
disabled: { type: Boolean },
|
|
@@ -5898,6 +6747,7 @@
|
|
|
5898
6747
|
size: __props.size,
|
|
5899
6748
|
disabled: __props.disabled,
|
|
5900
6749
|
"label-width": __props.config.labelWidth || __props.labelWidth,
|
|
6750
|
+
"label-position": __props.config.labelPosition || __props.labelPosition,
|
|
5901
6751
|
onChange: changeHandler,
|
|
5902
6752
|
onAddDiffCount: _cache[2] || (_cache[2] = ($event) => onAddDiffCount())
|
|
5903
6753
|
}, null, 8, [
|
|
@@ -5908,12 +6758,13 @@
|
|
|
5908
6758
|
"prop",
|
|
5909
6759
|
"size",
|
|
5910
6760
|
"disabled",
|
|
5911
|
-
"label-width"
|
|
6761
|
+
"label-width",
|
|
6762
|
+
"label-position"
|
|
5912
6763
|
]);
|
|
5913
6764
|
}), 128))]), (0, vue.createElementVNode)("img", {
|
|
5914
6765
|
class: "m-form-schematic",
|
|
5915
6766
|
src: __props.config.schematic
|
|
5916
|
-
}, null, 8, _hoisted_5$
|
|
6767
|
+
}, null, 8, _hoisted_5$3)])) : ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, (0, vue.renderList)(items.value, (item, index) => {
|
|
5917
6768
|
return (0, vue.openBlock)(), (0, vue.createBlock)(Container_default, {
|
|
5918
6769
|
key: item[(0, vue.unref)(mForm)?.keyProp || "__key"] ?? index,
|
|
5919
6770
|
config: item,
|
|
@@ -5924,6 +6775,7 @@
|
|
|
5924
6775
|
size: __props.size,
|
|
5925
6776
|
disabled: __props.disabled,
|
|
5926
6777
|
"label-width": __props.config.labelWidth || __props.labelWidth,
|
|
6778
|
+
"label-position": __props.config.labelPosition || __props.labelPosition,
|
|
5927
6779
|
onChange: changeHandler,
|
|
5928
6780
|
onAddDiffCount: _cache[3] || (_cache[3] = ($event) => onAddDiffCount())
|
|
5929
6781
|
}, null, 8, [
|
|
@@ -5934,7 +6786,8 @@
|
|
|
5934
6786
|
"prop",
|
|
5935
6787
|
"size",
|
|
5936
6788
|
"disabled",
|
|
5937
|
-
"label-width"
|
|
6789
|
+
"label-width",
|
|
6790
|
+
"label-position"
|
|
5938
6791
|
]);
|
|
5939
6792
|
}), 128))])]),
|
|
5940
6793
|
_: 3
|
|
@@ -5953,6 +6806,7 @@
|
|
|
5953
6806
|
isCompare: { type: Boolean },
|
|
5954
6807
|
config: {},
|
|
5955
6808
|
labelWidth: {},
|
|
6809
|
+
labelPosition: {},
|
|
5956
6810
|
expandMore: { type: Boolean },
|
|
5957
6811
|
span: {},
|
|
5958
6812
|
size: {},
|
|
@@ -5977,6 +6831,7 @@
|
|
|
5977
6831
|
config: __props.config,
|
|
5978
6832
|
prop: __props.prop,
|
|
5979
6833
|
"label-width": __props.config.labelWidth || __props.labelWidth,
|
|
6834
|
+
"label-position": __props.config.labelPosition || __props.labelPosition,
|
|
5980
6835
|
"expand-more": __props.expandMore,
|
|
5981
6836
|
size: __props.size,
|
|
5982
6837
|
disabled: __props.disabled,
|
|
@@ -5990,6 +6845,7 @@
|
|
|
5990
6845
|
"config",
|
|
5991
6846
|
"prop",
|
|
5992
6847
|
"label-width",
|
|
6848
|
+
"label-position",
|
|
5993
6849
|
"expand-more",
|
|
5994
6850
|
"size",
|
|
5995
6851
|
"disabled"
|
|
@@ -6011,6 +6867,7 @@
|
|
|
6011
6867
|
config: {},
|
|
6012
6868
|
name: {},
|
|
6013
6869
|
labelWidth: {},
|
|
6870
|
+
labelPosition: {},
|
|
6014
6871
|
prop: {},
|
|
6015
6872
|
size: {},
|
|
6016
6873
|
expandMore: { type: Boolean },
|
|
@@ -6033,6 +6890,7 @@
|
|
|
6033
6890
|
span: col.span || __props.config.span || 24 / __props.config.items.length,
|
|
6034
6891
|
config: col,
|
|
6035
6892
|
labelWidth: __props.config.labelWidth || __props.labelWidth,
|
|
6893
|
+
labelPosition: __props.config.labelPosition || __props.labelPosition,
|
|
6036
6894
|
expandMore: __props.expandMore,
|
|
6037
6895
|
model: __props.name ? __props.model[__props.name] : __props.model,
|
|
6038
6896
|
lastValues: __props.name ? __props.lastValues[__props.name] : __props.lastValues,
|
|
@@ -6046,6 +6904,7 @@
|
|
|
6046
6904
|
"span",
|
|
6047
6905
|
"config",
|
|
6048
6906
|
"labelWidth",
|
|
6907
|
+
"labelPosition",
|
|
6049
6908
|
"expandMore",
|
|
6050
6909
|
"model",
|
|
6051
6910
|
"lastValues",
|
|
@@ -6076,6 +6935,7 @@
|
|
|
6076
6935
|
name: {},
|
|
6077
6936
|
size: {},
|
|
6078
6937
|
labelWidth: {},
|
|
6938
|
+
labelPosition: {},
|
|
6079
6939
|
prop: { default: "" },
|
|
6080
6940
|
expandMore: { type: Boolean },
|
|
6081
6941
|
disabled: { type: Boolean }
|
|
@@ -6152,12 +7012,15 @@
|
|
|
6152
7012
|
prop: props.prop,
|
|
6153
7013
|
config: props.config
|
|
6154
7014
|
});
|
|
7015
|
+
const tabsModel = props.model[props.name];
|
|
7016
|
+
if (Array.isArray(tabsModel) && Array.isArray(props.config.items)) for (const tab of tabsModel) applyMountValueEffects(mForm, props.config.items, tab);
|
|
6155
7017
|
emit("change", props.model[props.name]);
|
|
6156
7018
|
} else {
|
|
6157
7019
|
const newObj = await initValue(mForm, {
|
|
6158
7020
|
config: props.config.items,
|
|
6159
7021
|
initValues: {}
|
|
6160
7022
|
});
|
|
7023
|
+
applyMountValueEffects(mForm, props.config.items, newObj);
|
|
6161
7024
|
newObj.title = `标签${tabs.value.length + 1}`;
|
|
6162
7025
|
props.model[props.name].push(newObj);
|
|
6163
7026
|
emit("change", props.model[props.name], { changeRecords: [{
|
|
@@ -6227,6 +7090,7 @@
|
|
|
6227
7090
|
prop: __props.config.dynamic ? `${__props.prop}${__props.prop ? "." : ""}${String(tabIndex)}` : tab.name ? `${__props.prop}${__props.prop ? "." : ""}${tab.name}` : __props.prop,
|
|
6228
7091
|
size: __props.size,
|
|
6229
7092
|
"label-width": tab.labelWidth || __props.labelWidth,
|
|
7093
|
+
"label-position": tab.labelPosition || __props.labelPosition,
|
|
6230
7094
|
"expand-more": __props.expandMore,
|
|
6231
7095
|
onChange: changeHandler,
|
|
6232
7096
|
onAddDiffCount: ($event) => onAddDiffCount(Number(tabIndex))
|
|
@@ -6239,6 +7103,7 @@
|
|
|
6239
7103
|
"prop",
|
|
6240
7104
|
"size",
|
|
6241
7105
|
"label-width",
|
|
7106
|
+
"label-position",
|
|
6242
7107
|
"expand-more",
|
|
6243
7108
|
"onAddDiffCount"
|
|
6244
7109
|
]);
|
|
@@ -6254,12 +7119,14 @@
|
|
|
6254
7119
|
//#endregion
|
|
6255
7120
|
//#region packages/form/src/containers/GroupListItem.vue?vue&type=script&setup=true&lang.ts
|
|
6256
7121
|
var _hoisted_1$9 = { class: "m-fields-group-list-item-header" };
|
|
6257
|
-
var _hoisted_2$5 =
|
|
6258
|
-
var _hoisted_3$4 =
|
|
7122
|
+
var _hoisted_2$5 = { class: "m-fields-group-list-item-title" };
|
|
7123
|
+
var _hoisted_3$4 = ["innerHTML"];
|
|
7124
|
+
var _hoisted_4$3 = { class: "m-fields-group-list-item-actions" };
|
|
7125
|
+
var _hoisted_5$2 = { style: {
|
|
6259
7126
|
"text-align": "right",
|
|
6260
7127
|
"margin-top": "20px"
|
|
6261
7128
|
} };
|
|
6262
|
-
var
|
|
7129
|
+
var _hoisted_6$1 = ["innerHTML"];
|
|
6263
7130
|
//#endregion
|
|
6264
7131
|
//#region packages/form/src/containers/GroupListItem.vue
|
|
6265
7132
|
var GroupListItem_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
@@ -6272,6 +7139,7 @@
|
|
|
6272
7139
|
groupModel: {},
|
|
6273
7140
|
config: {},
|
|
6274
7141
|
labelWidth: {},
|
|
7142
|
+
labelPosition: {},
|
|
6275
7143
|
prop: {},
|
|
6276
7144
|
size: {},
|
|
6277
7145
|
index: {},
|
|
@@ -6290,13 +7158,8 @@
|
|
|
6290
7158
|
const mForm = (0, vue.inject)("mForm");
|
|
6291
7159
|
const defaultExpandQuantity = props.config.defaultExpandQuantity ?? 7;
|
|
6292
7160
|
const expand = (0, vue.ref)(props.config.expandAll || defaultExpandQuantity > props.index);
|
|
6293
|
-
const rowConfig = (0, vue.computed)(() => (
|
|
6294
|
-
|
|
6295
|
-
span: props.config.span || 24,
|
|
6296
|
-
items: props.config.items,
|
|
6297
|
-
labelWidth: props.config.labelWidth,
|
|
6298
|
-
[mForm?.keyProp || "__key"]: `${props.config[mForm?.keyProp || "__key"]}${String(props.index)}`
|
|
6299
|
-
}));
|
|
7161
|
+
const rowConfig = (0, vue.computed)(() => getGroupListRowConfig(props.config, props.index, mForm?.keyProp));
|
|
7162
|
+
const rowProp = (0, vue.computed)(() => appendProp(props.prop, props.index));
|
|
6300
7163
|
const title = (0, vue.computed)(() => {
|
|
6301
7164
|
if (props.config.titleKey && props.model[props.config.titleKey]) {
|
|
6302
7165
|
const { titlePrefix } = props.config;
|
|
@@ -6356,101 +7219,112 @@
|
|
|
6356
7219
|
})]),
|
|
6357
7220
|
_: 1
|
|
6358
7221
|
}, 8, ["disabled"]),
|
|
6359
|
-
(0, vue.createElementVNode)("
|
|
6360
|
-
|
|
6361
|
-
|
|
7222
|
+
(0, vue.createElementVNode)("div", _hoisted_2$5, [(0, vue.renderSlot)(_ctx.$slots, "title", {
|
|
7223
|
+
model: __props.model,
|
|
7224
|
+
index: __props.index,
|
|
7225
|
+
lastValues: __props.lastValues,
|
|
7226
|
+
prop: rowProp.value,
|
|
7227
|
+
title: title.value
|
|
7228
|
+
}, () => [(0, vue.createElementVNode)("span", { innerHTML: title.value }, null, 8, _hoisted_3$4)])]),
|
|
7229
|
+
(0, vue.createElementVNode)("div", _hoisted_4$3, [
|
|
7230
|
+
copyable.value && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
6362
7231
|
key: 0,
|
|
7232
|
+
link: "",
|
|
6363
7233
|
size: "default",
|
|
7234
|
+
type: "primary",
|
|
7235
|
+
icon: (0, vue.unref)(_element_plus_icons_vue.DocumentCopy),
|
|
7236
|
+
disabled: __props.disabled,
|
|
7237
|
+
onClick: copyHandler
|
|
7238
|
+
}, {
|
|
7239
|
+
default: (0, vue.withCtx)(() => [..._cache[6] || (_cache[6] = [(0, vue.createTextVNode)("复制", -1)])]),
|
|
7240
|
+
_: 1
|
|
7241
|
+
}, 8, ["icon", "disabled"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
7242
|
+
movable.value && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [(0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
6364
7243
|
link: "",
|
|
6365
|
-
|
|
6366
|
-
icon: (0, vue.unref)(_element_plus_icons_vue.Delete),
|
|
7244
|
+
size: "default",
|
|
6367
7245
|
disabled: __props.disabled,
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
link: "",
|
|
6375
|
-
size: "default",
|
|
6376
|
-
type: "primary",
|
|
6377
|
-
icon: (0, vue.unref)(_element_plus_icons_vue.DocumentCopy),
|
|
6378
|
-
disabled: __props.disabled,
|
|
6379
|
-
onClick: copyHandler
|
|
6380
|
-
}, {
|
|
6381
|
-
default: (0, vue.withCtx)(() => [..._cache[6] || (_cache[6] = [(0, vue.createTextVNode)("复制", -1)])]),
|
|
6382
|
-
_: 1
|
|
6383
|
-
}, 8, ["icon", "disabled"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
6384
|
-
movable.value && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [(0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
6385
|
-
link: "",
|
|
6386
|
-
size: "default",
|
|
6387
|
-
disabled: __props.disabled,
|
|
6388
|
-
icon: (0, vue.unref)(_element_plus_icons_vue.Top),
|
|
6389
|
-
onClick: _cache[0] || (_cache[0] = ($event) => changeOrder(-1))
|
|
6390
|
-
}, {
|
|
6391
|
-
default: (0, vue.withCtx)(() => [..._cache[7] || (_cache[7] = [(0, vue.createTextVNode)("上移", -1)])]),
|
|
6392
|
-
_: 1
|
|
6393
|
-
}, 8, ["disabled", "icon"]), [[vue.vShow, __props.index !== 0]]), (0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
6394
|
-
link: "",
|
|
6395
|
-
size: "default",
|
|
6396
|
-
disabled: __props.disabled,
|
|
6397
|
-
icon: (0, vue.unref)(_element_plus_icons_vue.Bottom),
|
|
6398
|
-
onClick: _cache[1] || (_cache[1] = ($event) => changeOrder(1))
|
|
6399
|
-
}, {
|
|
6400
|
-
default: (0, vue.withCtx)(() => [..._cache[8] || (_cache[8] = [(0, vue.createTextVNode)("下移", -1)])]),
|
|
6401
|
-
_: 1
|
|
6402
|
-
}, 8, ["disabled", "icon"]), [[vue.vShow, __props.index !== length.value - 1]])], 64)) : (0, vue.createCommentVNode)("v-if", true),
|
|
6403
|
-
__props.config.moveSpecifyLocation && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicPopover), {
|
|
6404
|
-
key: 2,
|
|
6405
|
-
trigger: "click",
|
|
6406
|
-
placement: "top",
|
|
6407
|
-
width: "200",
|
|
6408
|
-
visible: moveSpecifyLocationVisible.value
|
|
6409
|
-
}, {
|
|
6410
|
-
reference: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
7246
|
+
icon: (0, vue.unref)(_element_plus_icons_vue.Top),
|
|
7247
|
+
onClick: _cache[0] || (_cache[0] = ($event) => changeOrder(-1))
|
|
7248
|
+
}, {
|
|
7249
|
+
default: (0, vue.withCtx)(() => [..._cache[7] || (_cache[7] = [(0, vue.createTextVNode)("上移", -1)])]),
|
|
7250
|
+
_: 1
|
|
7251
|
+
}, 8, ["disabled", "icon"]), [[vue.vShow, __props.index !== 0]]), (0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
6411
7252
|
link: "",
|
|
6412
|
-
size: "
|
|
6413
|
-
type: "primary",
|
|
6414
|
-
icon: (0, vue.unref)(_element_plus_icons_vue.Position),
|
|
7253
|
+
size: "default",
|
|
6415
7254
|
disabled: __props.disabled,
|
|
6416
|
-
|
|
7255
|
+
icon: (0, vue.unref)(_element_plus_icons_vue.Bottom),
|
|
7256
|
+
onClick: _cache[1] || (_cache[1] = ($event) => changeOrder(1))
|
|
6417
7257
|
}, {
|
|
6418
|
-
default: (0, vue.withCtx)(() => [..._cache[
|
|
7258
|
+
default: (0, vue.withCtx)(() => [..._cache[8] || (_cache[8] = [(0, vue.createTextVNode)("下移", -1)])]),
|
|
6419
7259
|
_: 1
|
|
6420
|
-
}, 8, ["
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
size: "small",
|
|
6428
|
-
min: 1,
|
|
6429
|
-
disabled: __props.disabled
|
|
6430
|
-
}, null, 8, ["modelValue", "disabled"]),
|
|
6431
|
-
_cache[11] || (_cache[11] = (0, vue.createTextVNode)("行 ", -1))
|
|
6432
|
-
]), (0, vue.createElementVNode)("div", _hoisted_3$4, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
6433
|
-
size: "small",
|
|
6434
|
-
text: "",
|
|
6435
|
-
onClick: _cache[4] || (_cache[4] = ($event) => moveSpecifyLocationVisible.value = false)
|
|
7260
|
+
}, 8, ["disabled", "icon"]), [[vue.vShow, __props.index !== length.value - 1]])], 64)) : (0, vue.createCommentVNode)("v-if", true),
|
|
7261
|
+
__props.config.moveSpecifyLocation && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicPopover), {
|
|
7262
|
+
key: 2,
|
|
7263
|
+
trigger: "click",
|
|
7264
|
+
placement: "top",
|
|
7265
|
+
width: "200",
|
|
7266
|
+
visible: moveSpecifyLocationVisible.value
|
|
6436
7267
|
}, {
|
|
6437
|
-
|
|
7268
|
+
reference: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
7269
|
+
link: "",
|
|
7270
|
+
size: "small",
|
|
7271
|
+
type: "primary",
|
|
7272
|
+
icon: (0, vue.unref)(_element_plus_icons_vue.Position),
|
|
7273
|
+
disabled: __props.disabled,
|
|
7274
|
+
onClick: _cache[2] || (_cache[2] = ($event) => moveSpecifyLocationVisible.value = true)
|
|
7275
|
+
}, {
|
|
7276
|
+
default: (0, vue.withCtx)(() => [..._cache[9] || (_cache[9] = [(0, vue.createTextVNode)("移动至", -1)])]),
|
|
7277
|
+
_: 1
|
|
7278
|
+
}, 8, ["icon", "disabled"])]),
|
|
7279
|
+
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", null, [(0, vue.createElementVNode)("div", null, [
|
|
7280
|
+
_cache[10] || (_cache[10] = (0, vue.createTextVNode)(" 第", -1)),
|
|
7281
|
+
(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicInputNumber), {
|
|
7282
|
+
style: { "margin": "0 5px" },
|
|
7283
|
+
modelValue: moveSpecifyLocationIndex.value,
|
|
7284
|
+
"onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => moveSpecifyLocationIndex.value = $event),
|
|
7285
|
+
size: "small",
|
|
7286
|
+
min: 1,
|
|
7287
|
+
disabled: __props.disabled
|
|
7288
|
+
}, null, 8, ["modelValue", "disabled"]),
|
|
7289
|
+
_cache[11] || (_cache[11] = (0, vue.createTextVNode)("行 ", -1))
|
|
7290
|
+
]), (0, vue.createElementVNode)("div", _hoisted_5$2, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
7291
|
+
size: "small",
|
|
7292
|
+
text: "",
|
|
7293
|
+
onClick: _cache[4] || (_cache[4] = ($event) => moveSpecifyLocationVisible.value = false)
|
|
7294
|
+
}, {
|
|
7295
|
+
default: (0, vue.withCtx)(() => [..._cache[12] || (_cache[12] = [(0, vue.createTextVNode)("取消", -1)])]),
|
|
7296
|
+
_: 1
|
|
7297
|
+
}), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
7298
|
+
size: "small",
|
|
7299
|
+
type: "primary",
|
|
7300
|
+
onClick: moveSpecifyLocationHandler
|
|
7301
|
+
}, {
|
|
7302
|
+
default: (0, vue.withCtx)(() => [..._cache[13] || (_cache[13] = [(0, vue.createTextVNode)("确认", -1)])]),
|
|
7303
|
+
_: 1
|
|
7304
|
+
})])])]),
|
|
6438
7305
|
_: 1
|
|
6439
|
-
}
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
7306
|
+
}, 8, ["visible"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
7307
|
+
(0, vue.createCommentVNode)(" v-if 必须挂在 Tooltip 上:子节点为 false 时 ElOnlyChild 会报 no valid child node found "),
|
|
7308
|
+
!__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
7309
|
+
key: 3,
|
|
7310
|
+
content: `删除 ${title.value}`
|
|
6443
7311
|
}, {
|
|
6444
|
-
default: (0, vue.withCtx)(() => [
|
|
7312
|
+
default: (0, vue.withCtx)(() => [(0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
7313
|
+
size: "default",
|
|
7314
|
+
link: "",
|
|
7315
|
+
class: "delete-button",
|
|
7316
|
+
icon: (0, vue.unref)(_element_plus_icons_vue.Delete),
|
|
7317
|
+
disabled: __props.disabled,
|
|
7318
|
+
onClick: removeHandler
|
|
7319
|
+
}, null, 8, ["icon", "disabled"]), [[vue.vShow, showDelete.value]])]),
|
|
6445
7320
|
_: 1
|
|
6446
|
-
}
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
}, null, 8, _hoisted_4$3)) : (0, vue.createCommentVNode)("v-if", true)
|
|
7321
|
+
}, 8, ["content"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
7322
|
+
itemExtra.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
|
|
7323
|
+
key: 4,
|
|
7324
|
+
innerHTML: itemExtra.value,
|
|
7325
|
+
class: "m-form-tip"
|
|
7326
|
+
}, null, 8, _hoisted_6$1)) : (0, vue.createCommentVNode)("v-if", true)
|
|
7327
|
+
])
|
|
6454
7328
|
])]),
|
|
6455
7329
|
default: (0, vue.withCtx)(() => [expand.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(Container_default, {
|
|
6456
7330
|
key: 0,
|
|
@@ -6459,7 +7333,8 @@
|
|
|
6459
7333
|
lastValues: __props.lastValues,
|
|
6460
7334
|
"is-compare": __props.isCompare,
|
|
6461
7335
|
labelWidth: __props.labelWidth,
|
|
6462
|
-
|
|
7336
|
+
labelPosition: __props.labelPosition,
|
|
7337
|
+
prop: rowProp.value,
|
|
6463
7338
|
size: __props.size,
|
|
6464
7339
|
disabled: __props.disabled,
|
|
6465
7340
|
onChange: changeHandler,
|
|
@@ -6470,11 +7345,12 @@
|
|
|
6470
7345
|
"lastValues",
|
|
6471
7346
|
"is-compare",
|
|
6472
7347
|
"labelWidth",
|
|
7348
|
+
"labelPosition",
|
|
6473
7349
|
"prop",
|
|
6474
7350
|
"size",
|
|
6475
7351
|
"disabled"
|
|
6476
7352
|
])) : (0, vue.createCommentVNode)("v-if", true)]),
|
|
6477
|
-
_:
|
|
7353
|
+
_: 3
|
|
6478
7354
|
}, 8, ["flat", "body-style"]);
|
|
6479
7355
|
};
|
|
6480
7356
|
}
|
|
@@ -6488,11 +7364,7 @@
|
|
|
6488
7364
|
class: "el-table__empty-block"
|
|
6489
7365
|
};
|
|
6490
7366
|
var _hoisted_4$2 = { class: "el-table__empty-text t-table__empty" };
|
|
6491
|
-
var _hoisted_5$1 = {
|
|
6492
|
-
key: 3,
|
|
6493
|
-
class: "m-fields-group-list-footer"
|
|
6494
|
-
};
|
|
6495
|
-
var _hoisted_6$1 = { style: {
|
|
7367
|
+
var _hoisted_5$1 = { style: {
|
|
6496
7368
|
"display": "flex",
|
|
6497
7369
|
"justify-content": "flex-end",
|
|
6498
7370
|
"flex": "1"
|
|
@@ -6509,6 +7381,7 @@
|
|
|
6509
7381
|
config: {},
|
|
6510
7382
|
name: {},
|
|
6511
7383
|
labelWidth: {},
|
|
7384
|
+
labelPosition: {},
|
|
6512
7385
|
prop: {},
|
|
6513
7386
|
size: {},
|
|
6514
7387
|
disabled: { type: Boolean },
|
|
@@ -6537,7 +7410,23 @@
|
|
|
6537
7410
|
emit("change", props.model[props.name]);
|
|
6538
7411
|
};
|
|
6539
7412
|
const onAddDiffCount = () => emit("addDiffCount");
|
|
6540
|
-
const
|
|
7413
|
+
const asList = (value) => Array.isArray(value) ? value : [];
|
|
7414
|
+
const currentList = (0, vue.computed)(() => asList(props.model[props.name]));
|
|
7415
|
+
/** 对比时按当前/历史较长一侧对齐,已删除的项也能渲染出来 */
|
|
7416
|
+
const displayItems = (0, vue.computed)(() => {
|
|
7417
|
+
const current = currentList.value;
|
|
7418
|
+
if (!props.isCompare) return current.map((item, index) => ({
|
|
7419
|
+
item: item ?? {},
|
|
7420
|
+
last: {},
|
|
7421
|
+
index
|
|
7422
|
+
}));
|
|
7423
|
+
const last = asList(props.lastValues?.[props.name]);
|
|
7424
|
+
return Array.from({ length: Math.max(current.length, last.length) }, (_, index) => ({
|
|
7425
|
+
item: current[index] ?? {},
|
|
7426
|
+
last: last[index] ?? {},
|
|
7427
|
+
index
|
|
7428
|
+
}));
|
|
7429
|
+
});
|
|
6541
7430
|
return (_ctx, _cache) => {
|
|
6542
7431
|
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$8, [
|
|
6543
7432
|
__props.config.extra ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
@@ -6545,25 +7434,30 @@
|
|
|
6545
7434
|
innerHTML: __props.config.extra,
|
|
6546
7435
|
style: { "color": "rgba(0, 0, 0, 0.45)" }
|
|
6547
7436
|
}, null, 8, _hoisted_2$4)) : (0, vue.createCommentVNode)("v-if", true),
|
|
6548
|
-
!
|
|
7437
|
+
!displayItems.value.length ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_3$3, [(0, vue.createElementVNode)("span", _hoisted_4$2, "暂无" + (0, vue.toDisplayString)(__props.config.titlePrefix || "") + "数据", 1)])) : ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, (0, vue.renderList)(displayItems.value, (entry) => {
|
|
6549
7438
|
return (0, vue.openBlock)(), (0, vue.createBlock)(GroupListItem_default, {
|
|
6550
|
-
key: index,
|
|
6551
|
-
model: item,
|
|
6552
|
-
lastValues:
|
|
7439
|
+
key: entry.index,
|
|
7440
|
+
model: entry.item,
|
|
7441
|
+
lastValues: entry.last,
|
|
6553
7442
|
"is-compare": __props.isCompare,
|
|
6554
7443
|
config: __props.config,
|
|
6555
7444
|
prop: __props.prop,
|
|
6556
|
-
index:
|
|
7445
|
+
index: entry.index,
|
|
6557
7446
|
"label-width": __props.labelWidth,
|
|
7447
|
+
"label-position": __props.labelPosition,
|
|
6558
7448
|
size: __props.size,
|
|
6559
7449
|
disabled: __props.disabled,
|
|
6560
|
-
"group-model":
|
|
7450
|
+
"group-model": currentList.value,
|
|
6561
7451
|
onRemoveItem: removeHandler,
|
|
6562
7452
|
onCopyItem: copyHandler,
|
|
6563
7453
|
onSwapItem: swapHandler,
|
|
6564
7454
|
onChange: changeHandler,
|
|
6565
7455
|
onAddDiffCount: _cache[0] || (_cache[0] = ($event) => onAddDiffCount())
|
|
6566
|
-
},
|
|
7456
|
+
}, (0, vue.createSlots)({ _: 2 }, [_ctx.$slots.title ? {
|
|
7457
|
+
name: "title",
|
|
7458
|
+
fn: (0, vue.withCtx)((slotProps) => [(0, vue.renderSlot)(_ctx.$slots, "title", (0, vue.mergeProps)({ ref_for: true }, slotProps))]),
|
|
7459
|
+
key: "0"
|
|
7460
|
+
} : void 0]), 1032, [
|
|
6567
7461
|
"model",
|
|
6568
7462
|
"lastValues",
|
|
6569
7463
|
"is-compare",
|
|
@@ -6571,12 +7465,16 @@
|
|
|
6571
7465
|
"prop",
|
|
6572
7466
|
"index",
|
|
6573
7467
|
"label-width",
|
|
7468
|
+
"label-position",
|
|
6574
7469
|
"size",
|
|
6575
7470
|
"disabled",
|
|
6576
7471
|
"group-model"
|
|
6577
7472
|
]);
|
|
6578
7473
|
}), 128)),
|
|
6579
|
-
!__props.isCompare
|
|
7474
|
+
!__props.isCompare && (_ctx.$slots["toggle-button"] || _ctx.$slots["add-button"]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
7475
|
+
key: 3,
|
|
7476
|
+
class: (0, vue.normalizeClass)(["m-fields-group-list-footer", { "is-sticky-full": Boolean(__props.config.addButtonConfig?.sticky) }])
|
|
7477
|
+
}, [(0, vue.renderSlot)(_ctx.$slots, "toggle-button"), (0, vue.createElementVNode)("div", _hoisted_5$1, [(0, vue.renderSlot)(_ctx.$slots, "add-button")])], 2)) : (0, vue.createCommentVNode)("v-if", true)
|
|
6580
7478
|
]);
|
|
6581
7479
|
};
|
|
6582
7480
|
}
|
|
@@ -6946,15 +7844,7 @@
|
|
|
6946
7844
|
if (typeof props.config.selection === "function") return props.config.selection(mForm, { model: props.model[modelName.value] });
|
|
6947
7845
|
return props.config.selection;
|
|
6948
7846
|
});
|
|
6949
|
-
const getProp = (index) =>
|
|
6950
|
-
return `${props.prop}${props.prop ? "." : ""}${index + 1 + currentPage.value * pageSize.value - 1}`;
|
|
6951
|
-
};
|
|
6952
|
-
const makeConfig = (config, row) => {
|
|
6953
|
-
const newConfig = cloneDeep(config);
|
|
6954
|
-
if (typeof config.itemsFunction === "function") newConfig.items = config.itemsFunction(row);
|
|
6955
|
-
delete newConfig.display;
|
|
6956
|
-
return newConfig;
|
|
6957
|
-
};
|
|
7847
|
+
const getProp = (index) => appendProp(props.prop, index + currentPage.value * pageSize.value);
|
|
6958
7848
|
const changeHandler = (v, eventData) => {
|
|
6959
7849
|
emit("change", props.model, eventData);
|
|
6960
7850
|
};
|
|
@@ -7033,7 +7923,7 @@
|
|
|
7033
7923
|
},
|
|
7034
7924
|
cell: ({ $index }) => (0, vue.h)("span", $index + 1 + currentPage.value * pageSize.value)
|
|
7035
7925
|
});
|
|
7036
|
-
for (const column of props.config.items) if (column
|
|
7926
|
+
for (const column of props.config.items) if (isTableColumnRendered(column, display$1)) {
|
|
7037
7927
|
const titleTipValue = titleTip(column.titleTip);
|
|
7038
7928
|
columns.push({
|
|
7039
7929
|
props: {
|
|
@@ -7049,7 +7939,7 @@
|
|
|
7049
7939
|
disabled: props.disabled,
|
|
7050
7940
|
prop: getProp($index),
|
|
7051
7941
|
rules: column.rules,
|
|
7052
|
-
config:
|
|
7942
|
+
config: makeTableColumnConfig(column, row),
|
|
7053
7943
|
model: row,
|
|
7054
7944
|
lastValues: lastData.value[$index],
|
|
7055
7945
|
isCompare: props.isCompare,
|
|
@@ -7081,7 +7971,7 @@
|
|
|
7081
7971
|
} };
|
|
7082
7972
|
var _hoisted_3$2 = { style: { "display": "flex" } };
|
|
7083
7973
|
var _hoisted_4$1 = {
|
|
7084
|
-
key:
|
|
7974
|
+
key: 2,
|
|
7085
7975
|
class: "bottom",
|
|
7086
7976
|
style: { "text-align": "right" }
|
|
7087
7977
|
};
|
|
@@ -7148,12 +8038,14 @@
|
|
|
7148
8038
|
style: { "color": "rgba(0, 0, 0, 0.45)" },
|
|
7149
8039
|
innerHTML: __props.config.extra
|
|
7150
8040
|
}, null, 8, _hoisted_1$7)) : (0, vue.createCommentVNode)("v-if", true),
|
|
7151
|
-
(0, vue.
|
|
8041
|
+
(0, vue.createCommentVNode)(" v-if 必须挂在 Tooltip 上:表格未就绪时 ElOnlyChild 会报 no valid child node found "),
|
|
8042
|
+
__props.model[modelName.value] ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
|
|
8043
|
+
key: 1,
|
|
7152
8044
|
content: "拖拽可排序",
|
|
7153
8045
|
placement: "left-start",
|
|
7154
8046
|
disabled: __props.config.dropSort !== true || __props.config.dropSortHandle
|
|
7155
8047
|
}, {
|
|
7156
|
-
default: (0, vue.withCtx)(() => [
|
|
8048
|
+
default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTable), {
|
|
7157
8049
|
ref: "tMagicTable",
|
|
7158
8050
|
style: { "width": "100%" },
|
|
7159
8051
|
"show-header": "",
|
|
@@ -7175,9 +8067,9 @@
|
|
|
7175
8067
|
"max-height",
|
|
7176
8068
|
"onSelect",
|
|
7177
8069
|
"empty-text"
|
|
7178
|
-
]))
|
|
8070
|
+
]))]),
|
|
7179
8071
|
_: 1
|
|
7180
|
-
}, 8, ["disabled"]),
|
|
8072
|
+
}, 8, ["disabled"])) : (0, vue.createCommentVNode)("v-if", true),
|
|
7181
8073
|
(0, vue.renderSlot)(_ctx.$slots, "default"),
|
|
7182
8074
|
(0, vue.createElementVNode)("div", _hoisted_2$3, [(0, vue.createElementVNode)("div", _hoisted_3$2, [
|
|
7183
8075
|
!(0, vue.unref)(isFullscreen) ? (0, vue.renderSlot)(_ctx.$slots, "toggle-button", {}, void 0, void 0, 0) : (0, vue.createCommentVNode)("v-if", true),
|
|
@@ -7256,39 +8148,46 @@
|
|
|
7256
8148
|
const addable = (0, vue.computed)(() => {
|
|
7257
8149
|
const modelName = props.name || props.config.name || "";
|
|
7258
8150
|
if (!modelName) return false;
|
|
7259
|
-
if (
|
|
7260
|
-
if (typeof props.config.addable === "function") return props.config.addable(mForm, {
|
|
8151
|
+
if (typeof props.config.addable === "function") return Boolean(props.config.addable(mForm, {
|
|
7261
8152
|
model: props.model[modelName],
|
|
7262
8153
|
formValue: mForm?.values,
|
|
7263
8154
|
prop: props.prop,
|
|
7264
8155
|
config: props.config
|
|
7265
|
-
});
|
|
7266
|
-
|
|
8156
|
+
}));
|
|
8157
|
+
if (!props.model[modelName]?.length) return true;
|
|
8158
|
+
return typeof props.config.addable === "undefined" ? true : Boolean(props.config.addable);
|
|
7267
8159
|
});
|
|
8160
|
+
/**
|
|
8161
|
+
* 新增一项,返回新增后列表应有的长度;没有新增(超上限 / 被 beforeAddRow 拦下 / enum 用尽)返回 null。
|
|
8162
|
+
*
|
|
8163
|
+
* 只抛 change,不直接改 `props.model`:写回由 `MForm` 按 changeRecords 的 propPath 完成。
|
|
8164
|
+
* 返回长度是给调用方用的——新项要等写回后才出现在 DOM 里,靠它才能判断等到了没有。
|
|
8165
|
+
*/
|
|
7268
8166
|
const newHandler = async (row) => {
|
|
7269
|
-
const modelName = props.name || props.config.name || ""
|
|
7270
|
-
|
|
8167
|
+
const modelName = `${props.name || props.config.name || ""}`;
|
|
8168
|
+
const list = Array.isArray(props.model[modelName]) ? props.model[modelName] : [];
|
|
8169
|
+
if (props.config.max && list.length >= props.config.max) {
|
|
7271
8170
|
_tmagic_design.tMagicMessage.error(`最多新增配置不能超过${props.config.max}条`);
|
|
7272
|
-
return;
|
|
8171
|
+
return null;
|
|
7273
8172
|
}
|
|
7274
8173
|
if (typeof props.config.beforeAddRow === "function") {
|
|
7275
8174
|
if (!await props.config.beforeAddRow(mForm, {
|
|
7276
|
-
model:
|
|
8175
|
+
model: list,
|
|
7277
8176
|
formValue: mForm?.values,
|
|
7278
8177
|
prop: props.prop
|
|
7279
|
-
})) return;
|
|
8178
|
+
})) return null;
|
|
7280
8179
|
}
|
|
7281
8180
|
const columns = props.config.items;
|
|
7282
8181
|
const enumValues = props.config.enum || [];
|
|
7283
8182
|
let enumV = [];
|
|
7284
|
-
const { length } =
|
|
8183
|
+
const { length } = list;
|
|
7285
8184
|
const key = props.config.key || "id";
|
|
7286
8185
|
let inputs = {};
|
|
7287
8186
|
if (enumValues.length) {
|
|
7288
|
-
if (length >= enumValues.length) return;
|
|
8187
|
+
if (length >= enumValues.length) return null;
|
|
7289
8188
|
enumV = enumValues.filter((item) => {
|
|
7290
8189
|
let i = 0;
|
|
7291
|
-
for (; i < length; i++) if (item[key] ===
|
|
8190
|
+
for (; i < length; i++) if (item[key] === list[i][key]) break;
|
|
7292
8191
|
return i === length;
|
|
7293
8192
|
});
|
|
7294
8193
|
if (enumV.length > 0) inputs = enumV[0];
|
|
@@ -7297,7 +8196,7 @@
|
|
|
7297
8196
|
});
|
|
7298
8197
|
else {
|
|
7299
8198
|
if (typeof props.config.defaultAdd === "function") inputs = await props.config.defaultAdd(mForm, {
|
|
7300
|
-
model:
|
|
8199
|
+
model: list,
|
|
7301
8200
|
prop: props.prop,
|
|
7302
8201
|
formValue: mForm?.values
|
|
7303
8202
|
});
|
|
@@ -7307,11 +8206,13 @@
|
|
|
7307
8206
|
initValues: inputs
|
|
7308
8207
|
});
|
|
7309
8208
|
}
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
8209
|
+
applyMountValueEffects(mForm, columns, inputs);
|
|
8210
|
+
if (props.sortKey && length) inputs[props.sortKey] = list[length - 1][props.sortKey] - 1;
|
|
8211
|
+
emit("change", [...list, inputs], { changeRecords: [{
|
|
8212
|
+
propPath: `${props.prop}.${length}`,
|
|
7313
8213
|
value: inputs
|
|
7314
8214
|
}] });
|
|
8215
|
+
return length + 1;
|
|
7315
8216
|
};
|
|
7316
8217
|
return {
|
|
7317
8218
|
addable,
|
|
@@ -7319,6 +8220,76 @@
|
|
|
7319
8220
|
};
|
|
7320
8221
|
};
|
|
7321
8222
|
//#endregion
|
|
8223
|
+
//#region packages/form/src/containers/table-group-list/useScrollLastItemIntoView.ts
|
|
8224
|
+
var nextFrame = () => new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
|
8225
|
+
/** 滚动要用的列表根与最后一项;组件已卸载(宿主重建了整棵表单)时返回 null */
|
|
8226
|
+
var resolveScrollTarget = (listRef) => {
|
|
8227
|
+
const inst = listRef.value;
|
|
8228
|
+
const root = inst instanceof HTMLElement ? inst : inst?.$el;
|
|
8229
|
+
if (!(root instanceof HTMLElement)) return null;
|
|
8230
|
+
const items = root.querySelectorAll(":scope > .m-fields-group-list-item");
|
|
8231
|
+
const last = items[items.length - 1];
|
|
8232
|
+
return {
|
|
8233
|
+
root,
|
|
8234
|
+
last: last instanceof HTMLElement ? last : null,
|
|
8235
|
+
count: items.length
|
|
8236
|
+
};
|
|
8237
|
+
};
|
|
8238
|
+
/** 吸顶标题自身的高度:滚动后新项的标题不能被上一层标题压住 */
|
|
8239
|
+
var getStickyHeaderOffset = (item) => {
|
|
8240
|
+
const header = item.querySelector(":scope > .el-card__header, :scope > .t-card__header");
|
|
8241
|
+
if (!(header instanceof HTMLElement)) return 0;
|
|
8242
|
+
return Number.parseFloat(getComputedStyle(header).top) || 0;
|
|
8243
|
+
};
|
|
8244
|
+
/** 吸底「新增」按钮盖住的高度;嵌套列表的 footer 还用 bottom 给外层按钮留了位 */
|
|
8245
|
+
var getStickyFooterOffset = (root) => {
|
|
8246
|
+
const footer = root.querySelector(":scope > .m-fields-group-list-footer.is-sticky-full");
|
|
8247
|
+
if (!(footer instanceof HTMLElement)) return 0;
|
|
8248
|
+
return footer.getBoundingClientRect().height + (Number.parseFloat(getComputedStyle(footer).bottom) || 0);
|
|
8249
|
+
};
|
|
8250
|
+
/**
|
|
8251
|
+
* 等列表渲染出至少 `expectedCount` 项、且最后一项的 DOM 节点连续两帧不变再返回。
|
|
8252
|
+
*
|
|
8253
|
+
* 新增只抛 change,写回要经过宿主(如编辑器属性面板)的异步校验与表单值重建,
|
|
8254
|
+
* 新项不一定当帧就出现;期间宿主还可能自己再追加项,末尾节点会换掉。
|
|
8255
|
+
* 只判「节点不变」会滚到旧的最后一项上,所以先等够数量再等稳定。
|
|
8256
|
+
*/
|
|
8257
|
+
var waitForStableTarget = async (listRef, expectedCount, timeout = 600) => {
|
|
8258
|
+
const deadline = Date.now() + timeout;
|
|
8259
|
+
let previous = null;
|
|
8260
|
+
while (Date.now() < deadline) {
|
|
8261
|
+
const target = resolveScrollTarget(listRef);
|
|
8262
|
+
if (!target) return null;
|
|
8263
|
+
if (target.count >= expectedCount && target.last && target.last === previous) return target;
|
|
8264
|
+
previous = target.last;
|
|
8265
|
+
await nextFrame();
|
|
8266
|
+
}
|
|
8267
|
+
return resolveScrollTarget(listRef);
|
|
8268
|
+
};
|
|
8269
|
+
/**
|
|
8270
|
+
* 新增后把最后一项滚进视口,避开吸顶标题与吸底按钮。
|
|
8271
|
+
*
|
|
8272
|
+
* `enabled` 为 false(未开配置、或不在 group-list 形态)时直接返回。
|
|
8273
|
+
*/
|
|
8274
|
+
var useScrollLastItemIntoView = (listRef, enabled) => {
|
|
8275
|
+
const scrollLastItemIntoView = async (expectedCount) => {
|
|
8276
|
+
if (!enabled()) return;
|
|
8277
|
+
await (0, vue.nextTick)();
|
|
8278
|
+
const target = await waitForStableTarget(listRef, expectedCount);
|
|
8279
|
+
if (!target?.last) return;
|
|
8280
|
+
const { root, last } = target;
|
|
8281
|
+
const top = getStickyHeaderOffset(last);
|
|
8282
|
+
const bottom = getStickyFooterOffset(root);
|
|
8283
|
+
last.style.scrollMarginTop = top ? `${top}px` : "";
|
|
8284
|
+
last.style.scrollMarginBottom = bottom ? `${bottom}px` : "";
|
|
8285
|
+
last.scrollIntoView({
|
|
8286
|
+
behavior: "auto",
|
|
8287
|
+
block: "nearest"
|
|
8288
|
+
});
|
|
8289
|
+
};
|
|
8290
|
+
return { scrollLastItemIntoView };
|
|
8291
|
+
};
|
|
8292
|
+
//#endregion
|
|
7322
8293
|
//#region packages/form/src/containers/table-group-list/TableGroupList.vue
|
|
7323
8294
|
var TableGroupList_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
7324
8295
|
name: "MFormTableGroupList",
|
|
@@ -7332,6 +8303,7 @@
|
|
|
7332
8303
|
name: {},
|
|
7333
8304
|
prop: {},
|
|
7334
8305
|
labelWidth: {},
|
|
8306
|
+
labelPosition: {},
|
|
7335
8307
|
disabled: { type: Boolean },
|
|
7336
8308
|
size: {},
|
|
7337
8309
|
enableToggleMode: { type: Boolean },
|
|
@@ -7348,63 +8320,47 @@
|
|
|
7348
8320
|
const props = __props;
|
|
7349
8321
|
const emit = __emit;
|
|
7350
8322
|
const { addable, newHandler } = useAdd(props, emit);
|
|
7351
|
-
const isGroupListType = (type) => type === "groupList" || type === "group-list";
|
|
7352
8323
|
const displayMode = (0, vue.ref)(isGroupListType(props.config.type) ? "groupList" : "table");
|
|
7353
|
-
const
|
|
7354
|
-
|
|
7355
|
-
const zhLength = label.match(/[^\x00-\xff]/g)?.length || 0;
|
|
7356
|
-
const chLength = label.length - zhLength;
|
|
7357
|
-
return `${Math.max(chLength * 8 + zhLength * 20, 80)}px`;
|
|
7358
|
-
};
|
|
7359
|
-
const tableConfig = (0, vue.computed)(() => {
|
|
7360
|
-
if (!isGroupListType(props.config.type)) return props.config;
|
|
7361
|
-
const source = props.config;
|
|
7362
|
-
return {
|
|
7363
|
-
...props.config,
|
|
7364
|
-
type: "table",
|
|
7365
|
-
groupItems: source.items,
|
|
7366
|
-
items: source.tableItems || source.items.map((item) => ({
|
|
7367
|
-
...item,
|
|
7368
|
-
label: item.label || item.text,
|
|
7369
|
-
text: null
|
|
7370
|
-
}))
|
|
7371
|
-
};
|
|
7372
|
-
});
|
|
7373
|
-
const groupListConfig = (0, vue.computed)(() => {
|
|
7374
|
-
if (isGroupListType(props.config.type)) return props.config;
|
|
7375
|
-
const source = props.config;
|
|
7376
|
-
return {
|
|
7377
|
-
...props.config,
|
|
7378
|
-
type: "groupList",
|
|
7379
|
-
tableItems: source.items,
|
|
7380
|
-
items: source.groupItems || source.items.map((item) => {
|
|
7381
|
-
const text = item.text || item.label;
|
|
7382
|
-
return {
|
|
7383
|
-
...item,
|
|
7384
|
-
text,
|
|
7385
|
-
labelWidth: calcLabelWidth(text),
|
|
7386
|
-
span: item.span || 12
|
|
7387
|
-
};
|
|
7388
|
-
})
|
|
7389
|
-
};
|
|
7390
|
-
});
|
|
8324
|
+
const tableConfig = (0, vue.computed)(() => toTableConfig(props.config));
|
|
8325
|
+
const groupListConfig = (0, vue.computed)(() => toGroupListConfig(props.config));
|
|
7391
8326
|
const currentConfig = (0, vue.computed)(() => displayMode.value === "table" ? tableConfig.value : groupListConfig.value);
|
|
7392
8327
|
const addButtonSize = (0, vue.computed)(() => {
|
|
8328
|
+
if (currentConfig.value.addButtonConfig?.sticky) return "default";
|
|
7393
8329
|
if (displayMode.value === "table") return "small";
|
|
7394
8330
|
return props.config.enableToggleMode !== false ? "small" : "default";
|
|
7395
8331
|
});
|
|
8332
|
+
const addButtonProps = (0, vue.computed)(() => {
|
|
8333
|
+
const custom = currentConfig.value.addButtonConfig?.props;
|
|
8334
|
+
if (custom) return {
|
|
8335
|
+
type: "primary",
|
|
8336
|
+
...custom
|
|
8337
|
+
};
|
|
8338
|
+
if (displayMode.value === "table") return {
|
|
8339
|
+
type: "primary",
|
|
8340
|
+
plain: true
|
|
8341
|
+
};
|
|
8342
|
+
return {
|
|
8343
|
+
type: "primary",
|
|
8344
|
+
text: true
|
|
8345
|
+
};
|
|
8346
|
+
});
|
|
7396
8347
|
const toggleDisplayMode = () => {
|
|
7397
8348
|
displayMode.value = displayMode.value === "table" ? "groupList" : "table";
|
|
7398
8349
|
};
|
|
8350
|
+
const tableGroupListRef = (0, vue.useTemplateRef)("tableGroupList");
|
|
8351
|
+
const { scrollLastItemIntoView } = useScrollLastItemIntoView(tableGroupListRef, () => Boolean(currentConfig.value.scrollLastItemIntoView) && displayMode.value === "groupList");
|
|
8352
|
+
const handleAdd = async () => {
|
|
8353
|
+
const expectedCount = await newHandler();
|
|
8354
|
+
if (expectedCount !== null) await scrollLastItemIntoView(expectedCount);
|
|
8355
|
+
};
|
|
7399
8356
|
const onChange = (v, eventData) => emit("change", v, eventData);
|
|
7400
8357
|
const onSelect = (...args) => emit("select", ...args);
|
|
7401
8358
|
const onAddDiffCount = () => emit("addDiffCount");
|
|
7402
|
-
const onAdd = (rows) => {
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
8359
|
+
const onAdd = async (rows) => {
|
|
8360
|
+
let expectedCount = null;
|
|
8361
|
+
for (const row of rows) expectedCount = await newHandler(row) ?? expectedCount;
|
|
8362
|
+
if (expectedCount !== null) await scrollLastItemIntoView(expectedCount);
|
|
7406
8363
|
};
|
|
7407
|
-
const tableGroupListRef = (0, vue.useTemplateRef)("tableGroupList");
|
|
7408
8364
|
__expose({ toggleRowSelection: (row, selected) => tableGroupListRef.value?.toggleRowSelection?.(row, selected) });
|
|
7409
8365
|
return (_ctx, _cache) => {
|
|
7410
8366
|
return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(displayMode.value === "table" ? Table_default : GroupList_default), (0, vue.mergeProps)({ ref: "tableGroupList" }, _ctx.$attrs, {
|
|
@@ -7417,6 +8373,7 @@
|
|
|
7417
8373
|
"last-values": __props.lastValues,
|
|
7418
8374
|
prop: __props.prop,
|
|
7419
8375
|
"label-width": __props.labelWidth,
|
|
8376
|
+
"label-position": __props.labelPosition,
|
|
7420
8377
|
"show-index": __props.showIndex,
|
|
7421
8378
|
"sort-key": __props.sortKey,
|
|
7422
8379
|
sort: __props.sort,
|
|
@@ -7424,39 +8381,43 @@
|
|
|
7424
8381
|
onSelect,
|
|
7425
8382
|
onAddDiffCount,
|
|
7426
8383
|
onAdd
|
|
7427
|
-
}), (0, vue.createSlots)({
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
"
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
8384
|
+
}), (0, vue.createSlots)({ _: 2 }, [
|
|
8385
|
+
__props.config.enableToggleMode || __props.enableToggleMode ? {
|
|
8386
|
+
name: "toggle-button",
|
|
8387
|
+
fn: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
|
|
8388
|
+
icon: (0, vue.unref)(_element_plus_icons_vue.Grid),
|
|
8389
|
+
size: "small",
|
|
8390
|
+
onClick: toggleDisplayMode
|
|
8391
|
+
}, {
|
|
8392
|
+
default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(displayMode.value === "table" ? "展开配置" : "切换为表格"), 1)]),
|
|
8393
|
+
_: 1
|
|
8394
|
+
}, 8, ["icon"])]),
|
|
8395
|
+
key: "0"
|
|
8396
|
+
} : void 0,
|
|
8397
|
+
(0, vue.unref)(addable) ? {
|
|
8398
|
+
name: "add-button",
|
|
8399
|
+
fn: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), (0, vue.mergeProps)({
|
|
8400
|
+
class: displayMode.value === "table" ? "m-form-table-add-button" : "",
|
|
8401
|
+
size: addButtonSize.value,
|
|
8402
|
+
icon: (0, vue.unref)(_element_plus_icons_vue.Plus),
|
|
8403
|
+
disabled: __props.disabled
|
|
8404
|
+
}, addButtonProps.value, { onClick: handleAdd }), {
|
|
8405
|
+
default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(currentConfig.value.addButtonConfig?.text || (displayMode.value === "table" ? `新增一行${groupListConfig.value.titlePrefix || ""}` : `新增${groupListConfig.value.titlePrefix || ""}`)), 1)]),
|
|
8406
|
+
_: 1
|
|
8407
|
+
}, 16, [
|
|
8408
|
+
"class",
|
|
8409
|
+
"size",
|
|
8410
|
+
"icon",
|
|
8411
|
+
"disabled"
|
|
8412
|
+
])]),
|
|
8413
|
+
key: "1"
|
|
8414
|
+
} : void 0,
|
|
8415
|
+
_ctx.$slots.title ? {
|
|
8416
|
+
name: "title",
|
|
8417
|
+
fn: (0, vue.withCtx)((slotProps) => [(0, vue.renderSlot)(_ctx.$slots, "title", (0, vue.normalizeProps)((0, vue.guardReactiveProps)(slotProps)))]),
|
|
8418
|
+
key: "2"
|
|
8419
|
+
} : void 0
|
|
8420
|
+
]), 1040, [
|
|
7460
8421
|
"model",
|
|
7461
8422
|
"name",
|
|
7462
8423
|
"config",
|
|
@@ -7466,6 +8427,7 @@
|
|
|
7466
8427
|
"last-values",
|
|
7467
8428
|
"prop",
|
|
7468
8429
|
"label-width",
|
|
8430
|
+
"label-position",
|
|
7469
8431
|
"show-index",
|
|
7470
8432
|
"sort-key",
|
|
7471
8433
|
"sort"
|
|
@@ -7784,13 +8746,13 @@
|
|
|
7784
8746
|
}
|
|
7785
8747
|
});
|
|
7786
8748
|
//#endregion
|
|
7787
|
-
//#region packages/form/src/fields/NumberRange.vue?vue&type=script&setup=true&lang.ts
|
|
8749
|
+
//#region packages/form/src/fields/NumberRange/Index.vue?vue&type=script&setup=true&lang.ts
|
|
7788
8750
|
var _hoisted_1$5 = { class: "m-fields-number-range" };
|
|
7789
8751
|
//#endregion
|
|
7790
|
-
//#region packages/form/src/fields/NumberRange.vue
|
|
7791
|
-
var
|
|
8752
|
+
//#region packages/form/src/fields/NumberRange/Index.vue
|
|
8753
|
+
var Index_default$5 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
7792
8754
|
name: "MFormNumberRange",
|
|
7793
|
-
__name: "
|
|
8755
|
+
__name: "Index",
|
|
7794
8756
|
props: {
|
|
7795
8757
|
config: {},
|
|
7796
8758
|
model: {},
|
|
@@ -7820,7 +8782,6 @@
|
|
|
7820
8782
|
deep: true
|
|
7821
8783
|
});
|
|
7822
8784
|
useAddField(props.prop);
|
|
7823
|
-
if (!Array.isArray(props.model[props.name])) props.model[props.name] = [];
|
|
7824
8785
|
const minChangeHandler = (v) => {
|
|
7825
8786
|
emit("change", [Number(v), props.model[props.name][1]]);
|
|
7826
8787
|
};
|
|
@@ -7920,10 +8881,10 @@
|
|
|
7920
8881
|
}
|
|
7921
8882
|
});
|
|
7922
8883
|
//#endregion
|
|
7923
|
-
//#region packages/form/src/fields/Date.vue
|
|
7924
|
-
var
|
|
8884
|
+
//#region packages/form/src/fields/Date/Index.vue
|
|
8885
|
+
var Index_default$1 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
7925
8886
|
name: "MFormDate",
|
|
7926
|
-
__name: "
|
|
8887
|
+
__name: "Index",
|
|
7927
8888
|
props: {
|
|
7928
8889
|
config: {},
|
|
7929
8890
|
model: {},
|
|
@@ -7944,7 +8905,6 @@
|
|
|
7944
8905
|
const props = __props;
|
|
7945
8906
|
const emit = __emit;
|
|
7946
8907
|
useAddField(props.prop);
|
|
7947
|
-
props.model[props.name] = datetimeFormatter(props.model[props.name], "", props.config.valueFormat || "YYYY/MM/DD");
|
|
7948
8908
|
const changeHandler = (v) => {
|
|
7949
8909
|
emit("change", v);
|
|
7950
8910
|
};
|
|
@@ -7970,10 +8930,10 @@
|
|
|
7970
8930
|
}
|
|
7971
8931
|
});
|
|
7972
8932
|
//#endregion
|
|
7973
|
-
//#region packages/form/src/fields/DateTime.vue
|
|
7974
|
-
var
|
|
8933
|
+
//#region packages/form/src/fields/DateTime/Index.vue
|
|
8934
|
+
var Index_default$2 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
7975
8935
|
name: "MFormDateTime",
|
|
7976
|
-
__name: "
|
|
8936
|
+
__name: "Index",
|
|
7977
8937
|
props: {
|
|
7978
8938
|
config: {},
|
|
7979
8939
|
model: {},
|
|
@@ -7994,9 +8954,6 @@
|
|
|
7994
8954
|
const props = __props;
|
|
7995
8955
|
const emit = __emit;
|
|
7996
8956
|
useAddField(props.prop);
|
|
7997
|
-
const value = props.model?.[props.name]?.toString();
|
|
7998
|
-
if (props.model) if (!value || value === "Invalid Date") props.model[props.name] = "";
|
|
7999
|
-
else props.model[props.name] = datetimeFormatter(props.model[props.name], "", props.config.valueFormat || "YYYY/MM/DD HH:mm:ss");
|
|
8000
8957
|
const changeHandler = (v) => {
|
|
8001
8958
|
emit("change", v);
|
|
8002
8959
|
};
|
|
@@ -8426,10 +9383,10 @@
|
|
|
8426
9383
|
}
|
|
8427
9384
|
});
|
|
8428
9385
|
//#endregion
|
|
8429
|
-
//#region packages/form/src/fields/CheckboxGroup.vue
|
|
8430
|
-
var
|
|
9386
|
+
//#region packages/form/src/fields/CheckboxGroup/Index.vue
|
|
9387
|
+
var Index_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
8431
9388
|
name: "MFormCheckGroup",
|
|
8432
|
-
__name: "
|
|
9389
|
+
__name: "Index",
|
|
8433
9390
|
props: {
|
|
8434
9391
|
config: {},
|
|
8435
9392
|
model: {},
|
|
@@ -8450,7 +9407,6 @@
|
|
|
8450
9407
|
const props = __props;
|
|
8451
9408
|
const emit = __emit;
|
|
8452
9409
|
useAddField(props.prop);
|
|
8453
|
-
if (props.model && !props.model[props.name]) props.model[props.name] = [];
|
|
8454
9410
|
const changeHandler = (v) => {
|
|
8455
9411
|
emit("change", v);
|
|
8456
9412
|
};
|
|
@@ -8571,13 +9527,13 @@
|
|
|
8571
9527
|
}
|
|
8572
9528
|
});
|
|
8573
9529
|
//#endregion
|
|
8574
|
-
//#region packages/form/src/fields/Display.vue?vue&type=script&setup=true&lang.ts
|
|
9530
|
+
//#region packages/form/src/fields/Display/Index.vue?vue&type=script&setup=true&lang.ts
|
|
8575
9531
|
var _hoisted_1$3 = { key: 0 };
|
|
8576
9532
|
//#endregion
|
|
8577
|
-
//#region packages/form/src/fields/Display.vue
|
|
8578
|
-
var
|
|
9533
|
+
//#region packages/form/src/fields/Display/Index.vue
|
|
9534
|
+
var Index_default$3 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
8579
9535
|
name: "MFormDisplay",
|
|
8580
|
-
__name: "
|
|
9536
|
+
__name: "Index",
|
|
8581
9537
|
props: {
|
|
8582
9538
|
config: {},
|
|
8583
9539
|
model: {},
|
|
@@ -8596,7 +9552,6 @@
|
|
|
8596
9552
|
setup(__props) {
|
|
8597
9553
|
const props = __props;
|
|
8598
9554
|
const mForm = (0, vue.inject)("mForm");
|
|
8599
|
-
if (props.config.initValue && props.model) props.model[props.name] = props.config.initValue;
|
|
8600
9555
|
const text = (0, vue.computed)(() => {
|
|
8601
9556
|
if (props.config.displayText) return filterFunction(mForm, props.config.displayText, props);
|
|
8602
9557
|
return props.model[props.name];
|
|
@@ -9204,13 +10159,50 @@
|
|
|
9204
10159
|
}
|
|
9205
10160
|
});
|
|
9206
10161
|
//#endregion
|
|
9207
|
-
//#region packages/form/src/fields/DynamicField.
|
|
10162
|
+
//#region packages/form/src/fields/DynamicField/effect.ts
|
|
10163
|
+
/**
|
|
10164
|
+
* 遍历动态字段列表,按「原值为空且声明了 defaultValue 则取默认值」求出每个字段当前的值。
|
|
10165
|
+
*
|
|
10166
|
+
* `isDefaultApplied` 为真表示该值来自 `defaultValue`,需要写回表单值
|
|
10167
|
+
* (组件通过 emit change 写回,初始化写入直接写 `values`)。
|
|
10168
|
+
*/
|
|
10169
|
+
var eachDynamicField = (fields, model, onField) => {
|
|
10170
|
+
for (const field of fields) {
|
|
10171
|
+
if (typeof field !== "object" || field?.name === void 0) continue;
|
|
10172
|
+
let value = model?.[field.name] || "";
|
|
10173
|
+
let isDefaultApplied = false;
|
|
10174
|
+
if (!value && field.defaultValue !== void 0) {
|
|
10175
|
+
value = field.defaultValue;
|
|
10176
|
+
isDefaultApplied = true;
|
|
10177
|
+
}
|
|
10178
|
+
onField(field, value, isDefaultApplied);
|
|
10179
|
+
}
|
|
10180
|
+
};
|
|
10181
|
+
/**
|
|
10182
|
+
* 同步 `returnFields` 的 defaultValue 按 prop 写入值根对象。
|
|
10183
|
+
*
|
|
10184
|
+
* 该组件读取的是同级 model,但写入走 Container 的 modifyKey 分支,落在 `${prop}.${key}`,
|
|
10185
|
+
* 这里保持与渲染一致(含这层不对称),避免两条链路产出不同的值。
|
|
10186
|
+
* 异步 returnFields 由组件挂载后的 watch 负责,此处不等待。
|
|
10187
|
+
*/
|
|
10188
|
+
var effect$5 = ({ config, model, prop, values }) => {
|
|
10189
|
+
const { returnFields, dynamicKey } = config;
|
|
10190
|
+
if (typeof returnFields !== "function" || !model) return;
|
|
10191
|
+
if (model[dynamicKey] === "") return;
|
|
10192
|
+
const result = returnFields(config, model, getConfig("request"));
|
|
10193
|
+
if (!result || typeof result.then === "function" || !Array.isArray(result)) return;
|
|
10194
|
+
eachDynamicField(result, model, (field, value, isDefaultApplied) => {
|
|
10195
|
+
if (isDefaultApplied) (0, _tmagic_utils.setValueByKeyPath)(`${prop}.${field.name}`, value, values || model);
|
|
10196
|
+
});
|
|
10197
|
+
};
|
|
10198
|
+
//#endregion
|
|
10199
|
+
//#region packages/form/src/fields/DynamicField/Index.vue?vue&type=script&setup=true&lang.ts
|
|
9208
10200
|
var _hoisted_1 = { class: "m-fields-dynamic-field" };
|
|
9209
10201
|
//#endregion
|
|
9210
|
-
//#region packages/form/src/fields/DynamicField.vue
|
|
9211
|
-
var
|
|
10202
|
+
//#region packages/form/src/fields/DynamicField/Index.vue
|
|
10203
|
+
var Index_default$4 = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
9212
10204
|
name: "MFormDynamicField",
|
|
9213
|
-
__name: "
|
|
10205
|
+
__name: "Index",
|
|
9214
10206
|
props: {
|
|
9215
10207
|
config: {},
|
|
9216
10208
|
model: {},
|
|
@@ -9248,15 +10240,10 @@
|
|
|
9248
10240
|
const fields = await props.config.returnFields(props.config, props.model, request);
|
|
9249
10241
|
fieldMap.value = {};
|
|
9250
10242
|
fieldLabelMap.value = {};
|
|
9251
|
-
fields.
|
|
9252
|
-
if (
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
oldVal = v.defaultValue;
|
|
9256
|
-
emit("change", oldVal, { modifyKey: v.name });
|
|
9257
|
-
}
|
|
9258
|
-
fieldMap.value[v.name] = oldVal;
|
|
9259
|
-
fieldLabelMap.value[v.name] = v.label || "";
|
|
10243
|
+
eachDynamicField(fields, props.model, (field, value, isDefaultApplied) => {
|
|
10244
|
+
if (isDefaultApplied) emit("change", value, { modifyKey: field.name });
|
|
10245
|
+
fieldMap.value[field.name] = value;
|
|
10246
|
+
fieldLabelMap.value[field.name] = field.label || "";
|
|
9260
10247
|
});
|
|
9261
10248
|
};
|
|
9262
10249
|
const unwatch = (0, vue.watch)(() => props.model?.[props.config.dynamicKey], (val) => {
|
|
@@ -9295,146 +10282,245 @@
|
|
|
9295
10282
|
}
|
|
9296
10283
|
});
|
|
9297
10284
|
//#endregion
|
|
9298
|
-
//#region packages/form/src/
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
const stepClick = (index) => {
|
|
9323
|
-
active.value = index + 1;
|
|
9324
|
-
mForm?.$emit("update:stepActive", active.value);
|
|
9325
|
-
};
|
|
9326
|
-
const changeHandler = (v, eventData) => {
|
|
9327
|
-
emit("change", props.model, eventData);
|
|
9328
|
-
};
|
|
9329
|
-
const onAddDiffCount = () => emit("addDiffCount");
|
|
9330
|
-
return (_ctx, _cache) => {
|
|
9331
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", null, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicSteps), {
|
|
9332
|
-
active: active.value,
|
|
9333
|
-
"align-center": "",
|
|
9334
|
-
space: __props.config.space
|
|
9335
|
-
}, {
|
|
9336
|
-
default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.config.items, (item, index) => {
|
|
9337
|
-
return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicStep), {
|
|
9338
|
-
key: item.__key,
|
|
9339
|
-
title: item.title,
|
|
9340
|
-
active: active.value,
|
|
9341
|
-
onClick: ($event) => stepClick(index)
|
|
9342
|
-
}, null, 8, [
|
|
9343
|
-
"title",
|
|
9344
|
-
"active",
|
|
9345
|
-
"onClick"
|
|
9346
|
-
]);
|
|
9347
|
-
}), 128))]),
|
|
9348
|
-
_: 1
|
|
9349
|
-
}, 8, ["active", "space"]), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.config.items, (step, index) => {
|
|
9350
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(step.items, (item) => {
|
|
9351
|
-
return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [item ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)(Container_default, {
|
|
9352
|
-
key: item[(0, vue.unref)(mForm)?.keyProp || "__key"],
|
|
9353
|
-
config: item,
|
|
9354
|
-
model: step.name ? __props.model[step.name] : __props.model,
|
|
9355
|
-
lastValues: step.name ? __props.lastValues[step.name] : __props.lastValues,
|
|
9356
|
-
"is-compare": __props.isCompare,
|
|
9357
|
-
prop: `${step.name}`,
|
|
9358
|
-
size: __props.size,
|
|
9359
|
-
disabled: __props.disabled,
|
|
9360
|
-
"label-width": __props.config.labelWidth || __props.labelWidth,
|
|
9361
|
-
onChange: changeHandler,
|
|
9362
|
-
onAddDiffCount: _cache[0] || (_cache[0] = ($event) => onAddDiffCount())
|
|
9363
|
-
}, null, 8, [
|
|
9364
|
-
"config",
|
|
9365
|
-
"model",
|
|
9366
|
-
"lastValues",
|
|
9367
|
-
"is-compare",
|
|
9368
|
-
"prop",
|
|
9369
|
-
"size",
|
|
9370
|
-
"disabled",
|
|
9371
|
-
"label-width"
|
|
9372
|
-
])), [[vue.vShow, active.value - 1 === index]]) : (0, vue.createCommentVNode)("v-if", true)], 64);
|
|
9373
|
-
}), 256))], 64);
|
|
9374
|
-
}), 256))]);
|
|
9375
|
-
};
|
|
10285
|
+
//#region packages/form/src/fields/CheckboxGroup/effect.ts
|
|
10286
|
+
/** 空值初始化为空数组。 */
|
|
10287
|
+
var effect$4 = ({ config, model }) => {
|
|
10288
|
+
const { name } = config;
|
|
10289
|
+
if (model && !model[name]) model[name] = [];
|
|
10290
|
+
};
|
|
10291
|
+
//#endregion
|
|
10292
|
+
//#region packages/form/src/fields/Date/effect.ts
|
|
10293
|
+
/** 按 `valueFormat` 归一化日期值。 */
|
|
10294
|
+
var effect$3 = ({ config, model }) => {
|
|
10295
|
+
if (!model) return;
|
|
10296
|
+
const { name, valueFormat } = config;
|
|
10297
|
+
model[name] = datetimeFormatter(model[name], "", valueFormat || "YYYY/MM/DD");
|
|
10298
|
+
};
|
|
10299
|
+
//#endregion
|
|
10300
|
+
//#region packages/form/src/fields/DateTime/effect.ts
|
|
10301
|
+
/** 按 `valueFormat` 归一化日期时间值,空值与非法值统一为空字符串。 */
|
|
10302
|
+
var effect$2 = ({ config, model }) => {
|
|
10303
|
+
if (!model) return;
|
|
10304
|
+
const { name, valueFormat } = config;
|
|
10305
|
+
const value = model[name]?.toString();
|
|
10306
|
+
if (!value || value === "Invalid Date") {
|
|
10307
|
+
model[name] = "";
|
|
10308
|
+
return;
|
|
9376
10309
|
}
|
|
9377
|
-
|
|
10310
|
+
model[name] = datetimeFormatter(model[name], "", valueFormat || "YYYY/MM/DD HH:mm:ss");
|
|
10311
|
+
};
|
|
10312
|
+
//#endregion
|
|
10313
|
+
//#region packages/form/src/fields/Display/effect.ts
|
|
10314
|
+
/** 把 `initValue` 写入 model。 */
|
|
10315
|
+
var effect$1 = ({ config, model }) => {
|
|
10316
|
+
const { initValue, name } = config;
|
|
10317
|
+
if (initValue && model) model[name] = initValue;
|
|
10318
|
+
};
|
|
10319
|
+
//#endregion
|
|
10320
|
+
//#region packages/form/src/fields/NumberRange/effect.ts
|
|
10321
|
+
/** 值不是数组时修正为空数组。 */
|
|
10322
|
+
var effect = ({ config, model }) => {
|
|
10323
|
+
const { name } = config;
|
|
10324
|
+
if (model && !Array.isArray(model[name])) model[name] = [];
|
|
10325
|
+
};
|
|
10326
|
+
//#endregion
|
|
10327
|
+
//#region packages/form/src/utils/builtInFields.ts
|
|
10328
|
+
/**
|
|
10329
|
+
* 内置字段的无渲染登记表(不含 Vue 组件)。
|
|
10330
|
+
*
|
|
10331
|
+
* 本模块只导出数据,不登记。调用方自行 `registerBuiltInFields(builtInFields)`,
|
|
10332
|
+
* 或 `app.use(MagicForm)`(install 里会登记叠上 Vue 组件后的表)。
|
|
10333
|
+
*/
|
|
10334
|
+
var builtInFields = {
|
|
10335
|
+
text: {},
|
|
10336
|
+
"img-upload": {},
|
|
10337
|
+
number: {},
|
|
10338
|
+
"number-range": { effect },
|
|
10339
|
+
textarea: {},
|
|
10340
|
+
hidden: {},
|
|
10341
|
+
date: { effect: effect$3 },
|
|
10342
|
+
datetime: { effect: effect$2 },
|
|
10343
|
+
daterange: {},
|
|
10344
|
+
timerange: {},
|
|
10345
|
+
time: {},
|
|
10346
|
+
checkbox: {},
|
|
10347
|
+
switch: {},
|
|
10348
|
+
"color-picker": {},
|
|
10349
|
+
"checkbox-group": { effect: effect$4 },
|
|
10350
|
+
"radio-group": {},
|
|
10351
|
+
display: { effect: effect$1 },
|
|
10352
|
+
link: {},
|
|
10353
|
+
select: {},
|
|
10354
|
+
cascader: {},
|
|
10355
|
+
"dynamic-field": { effect: effect$5 },
|
|
10356
|
+
component: {},
|
|
10357
|
+
tab: { walk: expandTab },
|
|
10358
|
+
row: { walk: expandRow },
|
|
10359
|
+
"flex-layout": { walk: expandRow },
|
|
10360
|
+
fieldset: { walk: expandFieldset },
|
|
10361
|
+
panel: { walk: expandPanel },
|
|
10362
|
+
step: { walk: expandStep },
|
|
10363
|
+
table: { walk: expandTableGroupList },
|
|
10364
|
+
"group-list": { walk: expandTableGroupList },
|
|
10365
|
+
"table-group-list": { walk: expandTableGroupList }
|
|
10366
|
+
};
|
|
9378
10367
|
//#endregion
|
|
9379
10368
|
//#region packages/form/src/plugin.ts
|
|
10369
|
+
var builtInFieldVue = {
|
|
10370
|
+
text: { component: Text_default },
|
|
10371
|
+
"img-upload": { component: Text_default },
|
|
10372
|
+
number: { component: Number_default },
|
|
10373
|
+
"number-range": { component: Index_default$5 },
|
|
10374
|
+
textarea: { component: Textarea_default },
|
|
10375
|
+
hidden: { component: Hidden_default },
|
|
10376
|
+
date: { component: Index_default$1 },
|
|
10377
|
+
datetime: { component: Index_default$2 },
|
|
10378
|
+
daterange: { component: Daterange_default },
|
|
10379
|
+
timerange: { component: Timerange_default },
|
|
10380
|
+
time: { component: Time_default },
|
|
10381
|
+
checkbox: { component: Checkbox_default },
|
|
10382
|
+
switch: { component: Switch_default },
|
|
10383
|
+
"color-picker": { component: ColorPicker_default },
|
|
10384
|
+
"checkbox-group": { component: Index_default },
|
|
10385
|
+
"radio-group": { component: RadioGroup_default },
|
|
10386
|
+
display: { component: Index_default$3 },
|
|
10387
|
+
link: { component: Link_default },
|
|
10388
|
+
select: { component: Select_default },
|
|
10389
|
+
cascader: { component: Cascader_default },
|
|
10390
|
+
"dynamic-field": { component: Index_default$4 },
|
|
10391
|
+
container: { container: Container_default },
|
|
10392
|
+
tab: { container: Tabs_default },
|
|
10393
|
+
row: { container: Row_default },
|
|
10394
|
+
"flex-layout": { container: FlexLayout_default },
|
|
10395
|
+
fieldset: { container: Fieldset_default },
|
|
10396
|
+
panel: { container: Panel_default },
|
|
10397
|
+
step: { container: /* @__PURE__ */ (0, vue.defineComponent)({
|
|
10398
|
+
name: "MFormStep",
|
|
10399
|
+
__name: "Step",
|
|
10400
|
+
props: {
|
|
10401
|
+
model: {},
|
|
10402
|
+
name: {},
|
|
10403
|
+
lastValues: {},
|
|
10404
|
+
isCompare: { type: Boolean },
|
|
10405
|
+
config: {},
|
|
10406
|
+
stepActive: { default: 1 },
|
|
10407
|
+
labelWidth: {},
|
|
10408
|
+
labelPosition: {},
|
|
10409
|
+
size: {},
|
|
10410
|
+
disabled: { type: Boolean }
|
|
10411
|
+
},
|
|
10412
|
+
emits: ["change", "addDiffCount"],
|
|
10413
|
+
setup(__props, { emit: __emit }) {
|
|
10414
|
+
const props = __props;
|
|
10415
|
+
const emit = __emit;
|
|
10416
|
+
const mForm = (0, vue.inject)("mForm");
|
|
10417
|
+
const active = (0, vue.ref)(1);
|
|
10418
|
+
(0, vue.watchEffect)(() => {
|
|
10419
|
+
active.value = props.stepActive;
|
|
10420
|
+
});
|
|
10421
|
+
const stepClick = (index) => {
|
|
10422
|
+
active.value = index + 1;
|
|
10423
|
+
mForm?.$emit("update:stepActive", active.value);
|
|
10424
|
+
};
|
|
10425
|
+
const changeHandler = (v, eventData) => {
|
|
10426
|
+
emit("change", props.model, eventData);
|
|
10427
|
+
};
|
|
10428
|
+
const onAddDiffCount = () => emit("addDiffCount");
|
|
10429
|
+
return (_ctx, _cache) => {
|
|
10430
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", null, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicSteps), {
|
|
10431
|
+
active: active.value,
|
|
10432
|
+
"align-center": "",
|
|
10433
|
+
space: __props.config.space
|
|
10434
|
+
}, {
|
|
10435
|
+
default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.config.items, (item, index) => {
|
|
10436
|
+
return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicStep), {
|
|
10437
|
+
key: item.__key,
|
|
10438
|
+
title: item.title,
|
|
10439
|
+
active: active.value,
|
|
10440
|
+
onClick: ($event) => stepClick(index)
|
|
10441
|
+
}, null, 8, [
|
|
10442
|
+
"title",
|
|
10443
|
+
"active",
|
|
10444
|
+
"onClick"
|
|
10445
|
+
]);
|
|
10446
|
+
}), 128))]),
|
|
10447
|
+
_: 1
|
|
10448
|
+
}, 8, ["active", "space"]), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.config.items, (step, index) => {
|
|
10449
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(step.items, (item) => {
|
|
10450
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, null, [item ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)(Container_default, {
|
|
10451
|
+
key: item[(0, vue.unref)(mForm)?.keyProp || "__key"],
|
|
10452
|
+
config: item,
|
|
10453
|
+
model: step.name ? __props.model[step.name] : __props.model,
|
|
10454
|
+
lastValues: step.name ? __props.lastValues[step.name] : __props.lastValues,
|
|
10455
|
+
"is-compare": __props.isCompare,
|
|
10456
|
+
prop: `${step.name}`,
|
|
10457
|
+
size: __props.size,
|
|
10458
|
+
disabled: __props.disabled,
|
|
10459
|
+
"label-width": __props.config.labelWidth || __props.labelWidth,
|
|
10460
|
+
"label-position": __props.config.labelPosition || __props.labelPosition,
|
|
10461
|
+
onChange: changeHandler,
|
|
10462
|
+
onAddDiffCount: _cache[0] || (_cache[0] = ($event) => onAddDiffCount())
|
|
10463
|
+
}, null, 8, [
|
|
10464
|
+
"config",
|
|
10465
|
+
"model",
|
|
10466
|
+
"lastValues",
|
|
10467
|
+
"is-compare",
|
|
10468
|
+
"prop",
|
|
10469
|
+
"size",
|
|
10470
|
+
"disabled",
|
|
10471
|
+
"label-width",
|
|
10472
|
+
"label-position"
|
|
10473
|
+
])), [[vue.vShow, active.value - 1 === index]]) : (0, vue.createCommentVNode)("v-if", true)], 64);
|
|
10474
|
+
}), 256))], 64);
|
|
10475
|
+
}), 256))]);
|
|
10476
|
+
};
|
|
10477
|
+
}
|
|
10478
|
+
}) },
|
|
10479
|
+
table: { container: TableGroupList_default },
|
|
10480
|
+
"group-list": { container: TableGroupList_default },
|
|
10481
|
+
"table-group-list": { container: TableGroupList_default }
|
|
10482
|
+
};
|
|
9380
10483
|
var defaultInstallOpt = {};
|
|
9381
|
-
var plugin_default = {
|
|
9382
|
-
|
|
10484
|
+
var plugin_default = {
|
|
10485
|
+
/**
|
|
10486
|
+
* 安装 `@tmagic/form`:登记内置字段、挂载 `m-form` / `m-form-dialog` / `m-form-drawer`。
|
|
10487
|
+
*
|
|
10488
|
+
* @param app - Vue 应用
|
|
10489
|
+
* @param [opt] - 安装选项
|
|
10490
|
+
*/
|
|
10491
|
+
install(app, opt = {}) {
|
|
10492
|
+
const option = {
|
|
10493
|
+
...defaultInstallOpt,
|
|
10494
|
+
...opt
|
|
10495
|
+
};
|
|
9383
10496
|
app.config.globalProperties.$MAGIC_FORM = option;
|
|
9384
10497
|
setConfig(option);
|
|
9385
|
-
|
|
10498
|
+
registerBuiltInFields(builtInFields);
|
|
10499
|
+
registerBuiltInFields(builtInFieldVue, app);
|
|
10500
|
+
if (option.fields) registerFields(option.fields, app);
|
|
9386
10501
|
app.component("m-form", Form_default);
|
|
9387
10502
|
app.component("m-form-dialog", FormDialog_default);
|
|
9388
10503
|
app.component("m-form-drawer", FormDrawer_default);
|
|
9389
|
-
app.component("m-form-container", Container_default);
|
|
9390
|
-
app.component("m-form-fieldset", Fieldset_default);
|
|
9391
|
-
app.component("m-form-group-list", TableGroupList_default);
|
|
9392
|
-
app.component("m-form-panel", Panel_default);
|
|
9393
|
-
app.component("m-form-row", Row_default);
|
|
9394
|
-
app.component("m-form-step", Step_default);
|
|
9395
|
-
app.component("m-form-table", TableGroupList_default);
|
|
9396
|
-
app.component("m-form-tab", Tabs_default);
|
|
9397
|
-
app.component("m-form-flex-layout", FlexLayout_default);
|
|
9398
|
-
app.component("m-fields-text", Text_default);
|
|
9399
|
-
app.component("m-fields-img-upload", Text_default);
|
|
9400
|
-
app.component("m-fields-number", Number_default);
|
|
9401
|
-
app.component("m-fields-number-range", NumberRange_default);
|
|
9402
|
-
app.component("m-fields-textarea", Textarea_default);
|
|
9403
|
-
app.component("m-fields-hidden", Hidden_default);
|
|
9404
|
-
app.component("m-fields-date", Date_default);
|
|
9405
|
-
app.component("m-fields-datetime", DateTime_default);
|
|
9406
|
-
app.component("m-fields-daterange", Daterange_default);
|
|
9407
|
-
app.component("m-fields-timerange", Timerange_default);
|
|
9408
|
-
app.component("m-fields-time", Time_default);
|
|
9409
|
-
app.component("m-fields-checkbox", Checkbox_default);
|
|
9410
|
-
app.component("m-fields-switch", Switch_default);
|
|
9411
|
-
app.component("m-fields-color-picker", ColorPicker_default);
|
|
9412
|
-
app.component("m-fields-checkbox-group", CheckboxGroup_default);
|
|
9413
|
-
app.component("m-fields-radio-group", RadioGroup_default);
|
|
9414
|
-
app.component("m-fields-display", Display_default);
|
|
9415
|
-
app.component("m-fields-link", Link_default);
|
|
9416
|
-
app.component("m-fields-select", Select_default);
|
|
9417
|
-
app.component("m-fields-cascader", Cascader_default);
|
|
9418
|
-
app.component("m-fields-dynamic-field", DynamicField_default);
|
|
9419
10504
|
} };
|
|
9420
10505
|
//#endregion
|
|
9421
10506
|
//#region packages/form/src/index.ts
|
|
9422
10507
|
var createForm = (config) => config;
|
|
9423
10508
|
//#endregion
|
|
10509
|
+
exports.FORM_CONTEXT_KEY = FORM_CONTEXT_KEY;
|
|
9424
10510
|
exports.FORM_DIFF_CONFIG_KEY = FORM_DIFF_CONFIG_KEY;
|
|
9425
|
-
exports.FORM_SILENT_MODE_KEY = FORM_SILENT_MODE_KEY;
|
|
9426
10511
|
exports.FORM_TYPE_MATCH_VALID_KEY = FORM_TYPE_MATCH_VALID_KEY;
|
|
10512
|
+
exports.FieldInnerConfigError = FieldInnerConfigError;
|
|
9427
10513
|
exports.MAX_SUGGESTION_OPTIONS = MAX_SUGGESTION_OPTIONS;
|
|
9428
10514
|
exports.MCascader = Cascader_default;
|
|
9429
10515
|
exports.MCheckbox = Checkbox_default;
|
|
9430
|
-
exports.MCheckboxGroup =
|
|
10516
|
+
exports.MCheckboxGroup = Index_default;
|
|
9431
10517
|
exports.MColorPicker = ColorPicker_default;
|
|
9432
10518
|
exports.MContainer = Container_default;
|
|
9433
|
-
exports.MDate =
|
|
9434
|
-
exports.MDateTime =
|
|
10519
|
+
exports.MDate = Index_default$1;
|
|
10520
|
+
exports.MDateTime = Index_default$2;
|
|
9435
10521
|
exports.MDaterange = Daterange_default;
|
|
9436
|
-
exports.MDisplay =
|
|
9437
|
-
exports.MDynamicField =
|
|
10522
|
+
exports.MDisplay = Index_default$3;
|
|
10523
|
+
exports.MDynamicField = Index_default$4;
|
|
9438
10524
|
exports.MFieldset = Fieldset_default;
|
|
9439
10525
|
exports.MFlexLayout = FlexLayout_default;
|
|
9440
10526
|
exports.MForm = Form_default;
|
|
@@ -9445,7 +10531,7 @@
|
|
|
9445
10531
|
exports.MHidden = Hidden_default;
|
|
9446
10532
|
exports.MLink = Link_default;
|
|
9447
10533
|
exports.MNumber = Number_default;
|
|
9448
|
-
exports.MNumberRange =
|
|
10534
|
+
exports.MNumberRange = Index_default$5;
|
|
9449
10535
|
exports.MPanel = Panel_default;
|
|
9450
10536
|
exports.MRadioGroup = RadioGroup_default;
|
|
9451
10537
|
exports.MRow = Row_default;
|
|
@@ -9459,37 +10545,52 @@
|
|
|
9459
10545
|
exports.MTime = Time_default;
|
|
9460
10546
|
exports.MTimerange = Timerange_default;
|
|
9461
10547
|
exports.adaptFormValidator = adaptFormValidator;
|
|
9462
|
-
exports.
|
|
9463
|
-
exports.
|
|
10548
|
+
exports.appendProp = appendProp;
|
|
10549
|
+
exports.applyMountValueEffects = applyMountValueEffects;
|
|
10550
|
+
exports.builtInFields = builtInFields;
|
|
10551
|
+
exports.clearFields = clearFields;
|
|
9464
10552
|
exports.clearTypeMatchRules = clearTypeMatchRules;
|
|
10553
|
+
exports.collectValidatableFields = collectValidatableFields;
|
|
9465
10554
|
exports.createForm = createForm;
|
|
10555
|
+
exports.createFormStateBase = createFormStateBase;
|
|
10556
|
+
exports.createFormStateProxy = createFormStateProxy;
|
|
10557
|
+
exports.createHeadlessFormState = createHeadlessFormState;
|
|
9466
10558
|
exports.createObjectProp = createObjectProp;
|
|
9467
10559
|
exports.createValues = createValues;
|
|
9468
10560
|
exports.datetimeFormatter = datetimeFormatter;
|
|
9469
10561
|
exports.default = plugin_default;
|
|
9470
|
-
exports.deleteFormField = deleteField;
|
|
9471
10562
|
exports.deleteTypeMatchRule = deleteTypeMatchRule;
|
|
9472
10563
|
exports.display = display;
|
|
9473
10564
|
exports.filterFunction = filterFunction;
|
|
10565
|
+
exports.formatValidateError = formatValidateError;
|
|
9474
10566
|
exports.getDataByPage = getDataByPage;
|
|
9475
10567
|
exports.getFormField = getField;
|
|
10568
|
+
exports.getItemProp = getItemProp;
|
|
10569
|
+
exports.getNativeRules = getNativeRules;
|
|
9476
10570
|
exports.getRules = getRules;
|
|
9477
|
-
exports.
|
|
10571
|
+
exports.getTextByName = getTextByName;
|
|
9478
10572
|
exports.getTypeMatchRule = getTypeMatchRule;
|
|
9479
10573
|
exports.initValue = initValue;
|
|
10574
|
+
exports.isFieldInnerConfigError = isFieldInnerConfigError;
|
|
10575
|
+
exports.isLeafFieldType = isLeafFieldType;
|
|
10576
|
+
exports.isValidName = isValidName;
|
|
10577
|
+
exports.mergeFieldOptions = mergeFieldOptions;
|
|
10578
|
+
exports.mergeFormContexts = mergeFormContexts;
|
|
9480
10579
|
exports.optionSuggestion = optionSuggestion;
|
|
9481
|
-
exports.
|
|
9482
|
-
exports.
|
|
9483
|
-
exports.
|
|
9484
|
-
exports.
|
|
10580
|
+
exports.registerBuiltInFields = registerBuiltInFields;
|
|
10581
|
+
exports.registerField = registerField;
|
|
10582
|
+
exports.registerFields = registerFields;
|
|
10583
|
+
exports.resolveItemType = resolveItemType;
|
|
9485
10584
|
exports.sortArray = sortArray;
|
|
9486
10585
|
exports.sortChange = sortChange;
|
|
9487
10586
|
exports.stringifyExampleValue = stringifyExampleValue;
|
|
9488
10587
|
exports.stripTabItemsLazy = stripTabItemsLazy;
|
|
9489
10588
|
exports.submitForm = submitForm;
|
|
10589
|
+
exports.unregisterField = unregisterField;
|
|
9490
10590
|
exports.useAddField = useAddField;
|
|
9491
10591
|
exports.validateForm = validateForm;
|
|
9492
10592
|
exports.validateTypeMatch = validateTypeMatch;
|
|
10593
|
+
exports.validateValues = validateValues;
|
|
9493
10594
|
var _tmagic_form_schema = require("@tmagic/form-schema");
|
|
9494
10595
|
Object.keys(_tmagic_form_schema).forEach(function(k) {
|
|
9495
10596
|
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|