@tmagic/form 1.8.0-beta.26 → 1.8.0-beta.27
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/fields/Select.vue_vue_type_script_setup_true_lang.js +11 -4
- package/dist/es/utils/formStateProxy.js +40 -4
- package/dist/tmagic-form-headless.umd.cjs +40 -4
- package/dist/tmagic-form.umd.cjs +51 -8
- package/package.json +4 -4
- package/src/fields/Select.vue +13 -4
- package/src/utils/formStateProxy.ts +50 -5
|
@@ -199,7 +199,10 @@ var Select_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
199
199
|
prop: props.prop,
|
|
200
200
|
postOptions
|
|
201
201
|
});
|
|
202
|
-
let initData
|
|
202
|
+
let initData;
|
|
203
|
+
try {
|
|
204
|
+
initData = getValueByKeyPath(initRoot || root, res);
|
|
205
|
+
} catch {}
|
|
203
206
|
if (initData) {
|
|
204
207
|
if (!Array.isArray(initData)) initData = [initData];
|
|
205
208
|
if (typeof option.item === "function") options = option.item(initData);
|
|
@@ -230,8 +233,12 @@ var Select_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
230
233
|
const v = props.model[props.name];
|
|
231
234
|
if (Array.isArray(v) ? !v.length : typeof v === "undefined") return;
|
|
232
235
|
if (typeof v === "undefined" || hasOption(v)) return;
|
|
233
|
-
|
|
234
|
-
|
|
236
|
+
try {
|
|
237
|
+
const data = await getInitOption();
|
|
238
|
+
setOptions(data);
|
|
239
|
+
} catch {
|
|
240
|
+
setOptions([]);
|
|
241
|
+
}
|
|
235
242
|
};
|
|
236
243
|
watch(() => props.model?.[props.name], () => {
|
|
237
244
|
initOption();
|
|
@@ -326,7 +333,7 @@ var Select_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
326
333
|
}), 128)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(options.value, (option) => {
|
|
327
334
|
return openBlock(), createBlock(resolveDynamicComponent(unref(optionComponent)?.component || "el-option"), mergeProps({
|
|
328
335
|
class: "tmagic-design-option",
|
|
329
|
-
key: __props.config.valueKey ? option.value[__props.config.valueKey] : option.value
|
|
336
|
+
key: __props.config.valueKey ? option.value?.[__props.config.valueKey] : option.value
|
|
330
337
|
}, { ref_for: true }, unref(optionComponent)?.props({
|
|
331
338
|
label: option.text,
|
|
332
339
|
value: option.value,
|
|
@@ -30,6 +30,36 @@ var mergeFormContexts = (...layers) => {
|
|
|
30
30
|
});
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
|
+
* 存量下发配置沿用 Vue2 时代的写法,把回调第一个参数当组件实例用,靠 `vm.mForm.xxx`
|
|
34
|
+
* 取表单状态。这类配置除了读,还会往上面挂方法做跨字段通信,例如:
|
|
35
|
+
*
|
|
36
|
+
* ```js
|
|
37
|
+
* vm.mForm.checkPropertyLimit = async (...) => { ... } // 一个字段的 validator 里挂
|
|
38
|
+
* await vm.mForm.checkPropertyLimit(...) // 另一处再取出来调用
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* 所以 `mForm` 必须能指回某个 formState:读能落到 core / context,写能经 `set` trap
|
|
42
|
+
* 落到 coreState 并持久化。
|
|
43
|
+
*
|
|
44
|
+
* 优先级是 core > context > 合成自引用,三者都是刻意的:
|
|
45
|
+
*
|
|
46
|
+
* - **core**:`formState.mForm = formState` 这类直写(如 FormPreview 用自建 services 时)
|
|
47
|
+
* 必须最优先。
|
|
48
|
+
* - **context**:唯一的生产者是把父 formState 整体当 context 传下来的嵌套表单
|
|
49
|
+
* (ComponentForm)。此时 `mForm` 命中 context 指向**父表单**,与 `extendState` 时代
|
|
50
|
+
* 把父 formState 并入子状态的结果一致,跨字段通信仍落在同一份对象上。父 formState
|
|
51
|
+
* 本身也是 Proxy,写入照样持久化,所以这里让 context 赢是对的,别「修正」成指向子表单。
|
|
52
|
+
* - **合成自引用**:前两者都没有时才兜底,让 `vm.mForm.xxx = fn` 落到自己的 coreState。
|
|
53
|
+
*
|
|
54
|
+
* 由此推出一条约束:**不要往 context 里塞普通对象充当 `mForm`**。context 通常是 computed
|
|
55
|
+
* 产物,依赖一变就重建,挂上去的方法会静默丢失。要么直写 core,要么什么都不放交给兜底。
|
|
56
|
+
*
|
|
57
|
+
* 枚举语义上合成的自引用表现得像原型链上的属性:`'mForm' in state` 为真,但不出现在
|
|
58
|
+
* `Object.keys(state)` 里,`getOwnPropertyDescriptor` 也返回 undefined。这是故意的——
|
|
59
|
+
* 按扩展字段打包 formState 的调用方(如发给 AI 的逻辑)会因循环引用炸掉。
|
|
60
|
+
*/
|
|
61
|
+
var SELF_REF_KEY = "mForm";
|
|
62
|
+
/**
|
|
33
63
|
* 将 coreState 与宿主业务上下文关联:读取时优先 core,miss 再读穿到 context。
|
|
34
64
|
*
|
|
35
65
|
* - `get` 用属性访问而非 `Reflect.get(t, k, receiver)`,避免破坏 Vue reactive 的 `__v_raw`;
|
|
@@ -42,19 +72,24 @@ var mergeFormContexts = (...layers) => {
|
|
|
42
72
|
* 独立成文件,避免与 `form.ts` ↔ `typeMatch.ts` 形成循环依赖。
|
|
43
73
|
*/
|
|
44
74
|
var createFormStateProxy = (coreState, getContext) => {
|
|
45
|
-
const resolve = () =>
|
|
46
|
-
|
|
75
|
+
const resolve = () => {
|
|
76
|
+
const ctx = (typeof getContext === "function" ? getContext() : unref(getContext)) || EMPTY_CONTEXT;
|
|
77
|
+
return ctx === proxy ? EMPTY_CONTEXT : ctx;
|
|
78
|
+
};
|
|
79
|
+
const proxy = new Proxy(coreState, {
|
|
47
80
|
get(t, k) {
|
|
48
81
|
if (typeof k === "symbol") return Reflect.get(t, k);
|
|
49
82
|
const v = t[k];
|
|
50
83
|
if (v !== void 0 || Reflect.has(t, k)) return v;
|
|
51
|
-
|
|
84
|
+
const ctx = resolve();
|
|
85
|
+
if (k in ctx) return ctx[k];
|
|
86
|
+
return k === SELF_REF_KEY ? proxy : void 0;
|
|
52
87
|
},
|
|
53
88
|
set(t, k, value) {
|
|
54
89
|
t[k] = value;
|
|
55
90
|
return true;
|
|
56
91
|
},
|
|
57
|
-
has: (t, k) => Reflect.has(t, k) || typeof k !== "symbol" && k in resolve(),
|
|
92
|
+
has: (t, k) => Reflect.has(t, k) || typeof k !== "symbol" && (k in resolve() || k === SELF_REF_KEY),
|
|
58
93
|
ownKeys: (t) => [.../* @__PURE__ */ new Set([...Reflect.ownKeys(t), ...Reflect.ownKeys(resolve()).filter((k) => typeof k !== "symbol")])],
|
|
59
94
|
getOwnPropertyDescriptor: (t, k) => {
|
|
60
95
|
const own = Reflect.getOwnPropertyDescriptor(t, k);
|
|
@@ -70,6 +105,7 @@ var createFormStateProxy = (coreState, getContext) => {
|
|
|
70
105
|
};
|
|
71
106
|
}
|
|
72
107
|
});
|
|
108
|
+
return proxy;
|
|
73
109
|
};
|
|
74
110
|
//#endregion
|
|
75
111
|
export { createFormStateProxy, mergeFormContexts };
|
|
@@ -2557,6 +2557,36 @@
|
|
|
2557
2557
|
});
|
|
2558
2558
|
};
|
|
2559
2559
|
/**
|
|
2560
|
+
* 存量下发配置沿用 Vue2 时代的写法,把回调第一个参数当组件实例用,靠 `vm.mForm.xxx`
|
|
2561
|
+
* 取表单状态。这类配置除了读,还会往上面挂方法做跨字段通信,例如:
|
|
2562
|
+
*
|
|
2563
|
+
* ```js
|
|
2564
|
+
* vm.mForm.checkPropertyLimit = async (...) => { ... } // 一个字段的 validator 里挂
|
|
2565
|
+
* await vm.mForm.checkPropertyLimit(...) // 另一处再取出来调用
|
|
2566
|
+
* ```
|
|
2567
|
+
*
|
|
2568
|
+
* 所以 `mForm` 必须能指回某个 formState:读能落到 core / context,写能经 `set` trap
|
|
2569
|
+
* 落到 coreState 并持久化。
|
|
2570
|
+
*
|
|
2571
|
+
* 优先级是 core > context > 合成自引用,三者都是刻意的:
|
|
2572
|
+
*
|
|
2573
|
+
* - **core**:`formState.mForm = formState` 这类直写(如 FormPreview 用自建 services 时)
|
|
2574
|
+
* 必须最优先。
|
|
2575
|
+
* - **context**:唯一的生产者是把父 formState 整体当 context 传下来的嵌套表单
|
|
2576
|
+
* (ComponentForm)。此时 `mForm` 命中 context 指向**父表单**,与 `extendState` 时代
|
|
2577
|
+
* 把父 formState 并入子状态的结果一致,跨字段通信仍落在同一份对象上。父 formState
|
|
2578
|
+
* 本身也是 Proxy,写入照样持久化,所以这里让 context 赢是对的,别「修正」成指向子表单。
|
|
2579
|
+
* - **合成自引用**:前两者都没有时才兜底,让 `vm.mForm.xxx = fn` 落到自己的 coreState。
|
|
2580
|
+
*
|
|
2581
|
+
* 由此推出一条约束:**不要往 context 里塞普通对象充当 `mForm`**。context 通常是 computed
|
|
2582
|
+
* 产物,依赖一变就重建,挂上去的方法会静默丢失。要么直写 core,要么什么都不放交给兜底。
|
|
2583
|
+
*
|
|
2584
|
+
* 枚举语义上合成的自引用表现得像原型链上的属性:`'mForm' in state` 为真,但不出现在
|
|
2585
|
+
* `Object.keys(state)` 里,`getOwnPropertyDescriptor` 也返回 undefined。这是故意的——
|
|
2586
|
+
* 按扩展字段打包 formState 的调用方(如发给 AI 的逻辑)会因循环引用炸掉。
|
|
2587
|
+
*/
|
|
2588
|
+
var SELF_REF_KEY = "mForm";
|
|
2589
|
+
/**
|
|
2560
2590
|
* 将 coreState 与宿主业务上下文关联:读取时优先 core,miss 再读穿到 context。
|
|
2561
2591
|
*
|
|
2562
2592
|
* - `get` 用属性访问而非 `Reflect.get(t, k, receiver)`,避免破坏 Vue reactive 的 `__v_raw`;
|
|
@@ -2569,19 +2599,24 @@
|
|
|
2569
2599
|
* 独立成文件,避免与 `form.ts` ↔ `typeMatch.ts` 形成循环依赖。
|
|
2570
2600
|
*/
|
|
2571
2601
|
var createFormStateProxy = (coreState, getContext) => {
|
|
2572
|
-
const resolve = () =>
|
|
2573
|
-
|
|
2602
|
+
const resolve = () => {
|
|
2603
|
+
const ctx = (typeof getContext === "function" ? getContext() : (0, vue.unref)(getContext)) || EMPTY_CONTEXT;
|
|
2604
|
+
return ctx === proxy ? EMPTY_CONTEXT : ctx;
|
|
2605
|
+
};
|
|
2606
|
+
const proxy = new Proxy(coreState, {
|
|
2574
2607
|
get(t, k) {
|
|
2575
2608
|
if (typeof k === "symbol") return Reflect.get(t, k);
|
|
2576
2609
|
const v = t[k];
|
|
2577
2610
|
if (v !== void 0 || Reflect.has(t, k)) return v;
|
|
2578
|
-
|
|
2611
|
+
const ctx = resolve();
|
|
2612
|
+
if (k in ctx) return ctx[k];
|
|
2613
|
+
return k === SELF_REF_KEY ? proxy : void 0;
|
|
2579
2614
|
},
|
|
2580
2615
|
set(t, k, value) {
|
|
2581
2616
|
t[k] = value;
|
|
2582
2617
|
return true;
|
|
2583
2618
|
},
|
|
2584
|
-
has: (t, k) => Reflect.has(t, k) || typeof k !== "symbol" && k in resolve(),
|
|
2619
|
+
has: (t, k) => Reflect.has(t, k) || typeof k !== "symbol" && (k in resolve() || k === SELF_REF_KEY),
|
|
2585
2620
|
ownKeys: (t) => [.../* @__PURE__ */ new Set([...Reflect.ownKeys(t), ...Reflect.ownKeys(resolve()).filter((k) => typeof k !== "symbol")])],
|
|
2586
2621
|
getOwnPropertyDescriptor: (t, k) => {
|
|
2587
2622
|
const own = Reflect.getOwnPropertyDescriptor(t, k);
|
|
@@ -2597,6 +2632,7 @@
|
|
|
2597
2632
|
};
|
|
2598
2633
|
}
|
|
2599
2634
|
});
|
|
2635
|
+
return proxy;
|
|
2600
2636
|
};
|
|
2601
2637
|
//#endregion
|
|
2602
2638
|
//#region packages/form/src/utils/form.ts
|
package/dist/tmagic-form.umd.cjs
CHANGED
|
@@ -3411,6 +3411,36 @@
|
|
|
3411
3411
|
});
|
|
3412
3412
|
};
|
|
3413
3413
|
/**
|
|
3414
|
+
* 存量下发配置沿用 Vue2 时代的写法,把回调第一个参数当组件实例用,靠 `vm.mForm.xxx`
|
|
3415
|
+
* 取表单状态。这类配置除了读,还会往上面挂方法做跨字段通信,例如:
|
|
3416
|
+
*
|
|
3417
|
+
* ```js
|
|
3418
|
+
* vm.mForm.checkPropertyLimit = async (...) => { ... } // 一个字段的 validator 里挂
|
|
3419
|
+
* await vm.mForm.checkPropertyLimit(...) // 另一处再取出来调用
|
|
3420
|
+
* ```
|
|
3421
|
+
*
|
|
3422
|
+
* 所以 `mForm` 必须能指回某个 formState:读能落到 core / context,写能经 `set` trap
|
|
3423
|
+
* 落到 coreState 并持久化。
|
|
3424
|
+
*
|
|
3425
|
+
* 优先级是 core > context > 合成自引用,三者都是刻意的:
|
|
3426
|
+
*
|
|
3427
|
+
* - **core**:`formState.mForm = formState` 这类直写(如 FormPreview 用自建 services 时)
|
|
3428
|
+
* 必须最优先。
|
|
3429
|
+
* - **context**:唯一的生产者是把父 formState 整体当 context 传下来的嵌套表单
|
|
3430
|
+
* (ComponentForm)。此时 `mForm` 命中 context 指向**父表单**,与 `extendState` 时代
|
|
3431
|
+
* 把父 formState 并入子状态的结果一致,跨字段通信仍落在同一份对象上。父 formState
|
|
3432
|
+
* 本身也是 Proxy,写入照样持久化,所以这里让 context 赢是对的,别「修正」成指向子表单。
|
|
3433
|
+
* - **合成自引用**:前两者都没有时才兜底,让 `vm.mForm.xxx = fn` 落到自己的 coreState。
|
|
3434
|
+
*
|
|
3435
|
+
* 由此推出一条约束:**不要往 context 里塞普通对象充当 `mForm`**。context 通常是 computed
|
|
3436
|
+
* 产物,依赖一变就重建,挂上去的方法会静默丢失。要么直写 core,要么什么都不放交给兜底。
|
|
3437
|
+
*
|
|
3438
|
+
* 枚举语义上合成的自引用表现得像原型链上的属性:`'mForm' in state` 为真,但不出现在
|
|
3439
|
+
* `Object.keys(state)` 里,`getOwnPropertyDescriptor` 也返回 undefined。这是故意的——
|
|
3440
|
+
* 按扩展字段打包 formState 的调用方(如发给 AI 的逻辑)会因循环引用炸掉。
|
|
3441
|
+
*/
|
|
3442
|
+
var SELF_REF_KEY = "mForm";
|
|
3443
|
+
/**
|
|
3414
3444
|
* 将 coreState 与宿主业务上下文关联:读取时优先 core,miss 再读穿到 context。
|
|
3415
3445
|
*
|
|
3416
3446
|
* - `get` 用属性访问而非 `Reflect.get(t, k, receiver)`,避免破坏 Vue reactive 的 `__v_raw`;
|
|
@@ -3423,19 +3453,24 @@
|
|
|
3423
3453
|
* 独立成文件,避免与 `form.ts` ↔ `typeMatch.ts` 形成循环依赖。
|
|
3424
3454
|
*/
|
|
3425
3455
|
var createFormStateProxy = (coreState, getContext) => {
|
|
3426
|
-
const resolve = () =>
|
|
3427
|
-
|
|
3456
|
+
const resolve = () => {
|
|
3457
|
+
const ctx = (typeof getContext === "function" ? getContext() : (0, vue.unref)(getContext)) || EMPTY_CONTEXT;
|
|
3458
|
+
return ctx === proxy ? EMPTY_CONTEXT : ctx;
|
|
3459
|
+
};
|
|
3460
|
+
const proxy = new Proxy(coreState, {
|
|
3428
3461
|
get(t, k) {
|
|
3429
3462
|
if (typeof k === "symbol") return Reflect.get(t, k);
|
|
3430
3463
|
const v = t[k];
|
|
3431
3464
|
if (v !== void 0 || Reflect.has(t, k)) return v;
|
|
3432
|
-
|
|
3465
|
+
const ctx = resolve();
|
|
3466
|
+
if (k in ctx) return ctx[k];
|
|
3467
|
+
return k === SELF_REF_KEY ? proxy : void 0;
|
|
3433
3468
|
},
|
|
3434
3469
|
set(t, k, value) {
|
|
3435
3470
|
t[k] = value;
|
|
3436
3471
|
return true;
|
|
3437
3472
|
},
|
|
3438
|
-
has: (t, k) => Reflect.has(t, k) || typeof k !== "symbol" && k in resolve(),
|
|
3473
|
+
has: (t, k) => Reflect.has(t, k) || typeof k !== "symbol" && (k in resolve() || k === SELF_REF_KEY),
|
|
3439
3474
|
ownKeys: (t) => [.../* @__PURE__ */ new Set([...Reflect.ownKeys(t), ...Reflect.ownKeys(resolve()).filter((k) => typeof k !== "symbol")])],
|
|
3440
3475
|
getOwnPropertyDescriptor: (t, k) => {
|
|
3441
3476
|
const own = Reflect.getOwnPropertyDescriptor(t, k);
|
|
@@ -3451,6 +3486,7 @@
|
|
|
3451
3486
|
};
|
|
3452
3487
|
}
|
|
3453
3488
|
});
|
|
3489
|
+
return proxy;
|
|
3454
3490
|
};
|
|
3455
3491
|
//#endregion
|
|
3456
3492
|
//#region packages/form/src/utils/form.ts
|
|
@@ -9882,7 +9918,10 @@
|
|
|
9882
9918
|
prop: props.prop,
|
|
9883
9919
|
postOptions
|
|
9884
9920
|
});
|
|
9885
|
-
let initData
|
|
9921
|
+
let initData;
|
|
9922
|
+
try {
|
|
9923
|
+
initData = (0, _tmagic_utils.getValueByKeyPath)(initRoot || root, res);
|
|
9924
|
+
} catch {}
|
|
9886
9925
|
if (initData) {
|
|
9887
9926
|
if (!Array.isArray(initData)) initData = [initData];
|
|
9888
9927
|
if (typeof option.item === "function") options = option.item(initData);
|
|
@@ -9913,8 +9952,12 @@
|
|
|
9913
9952
|
const v = props.model[props.name];
|
|
9914
9953
|
if (Array.isArray(v) ? !v.length : typeof v === "undefined") return;
|
|
9915
9954
|
if (typeof v === "undefined" || hasOption(v)) return;
|
|
9916
|
-
|
|
9917
|
-
|
|
9955
|
+
try {
|
|
9956
|
+
const data = await getInitOption();
|
|
9957
|
+
setOptions(data);
|
|
9958
|
+
} catch {
|
|
9959
|
+
setOptions([]);
|
|
9960
|
+
}
|
|
9918
9961
|
};
|
|
9919
9962
|
(0, vue.watch)(() => props.model?.[props.name], () => {
|
|
9920
9963
|
initOption();
|
|
@@ -10009,7 +10052,7 @@
|
|
|
10009
10052
|
}), 128)) : ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, (0, vue.renderList)(options.value, (option) => {
|
|
10010
10053
|
return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(optionComponent)?.component || "el-option"), (0, vue.mergeProps)({
|
|
10011
10054
|
class: "tmagic-design-option",
|
|
10012
|
-
key: __props.config.valueKey ? option.value[__props.config.valueKey] : option.value
|
|
10055
|
+
key: __props.config.valueKey ? option.value?.[__props.config.valueKey] : option.value
|
|
10013
10056
|
}, { ref_for: true }, (0, vue.unref)(optionComponent)?.props({
|
|
10014
10057
|
label: option.text,
|
|
10015
10058
|
value: option.value,
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.27",
|
|
3
3
|
"name": "@tmagic/form",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"typescript": "^6.0.3",
|
|
60
60
|
"vue": "^3.5.41",
|
|
61
|
-
"@tmagic/design": "1.8.0-beta.
|
|
62
|
-
"@tmagic/
|
|
63
|
-
"@tmagic/
|
|
61
|
+
"@tmagic/design": "1.8.0-beta.27",
|
|
62
|
+
"@tmagic/form-schema": "1.8.0-beta.27",
|
|
63
|
+
"@tmagic/utils": "1.8.0-beta.27"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"typescript": {
|
package/src/fields/Select.vue
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
<component
|
|
58
58
|
v-for="option in options as SelectOption[]"
|
|
59
59
|
class="tmagic-design-option"
|
|
60
|
-
:key="config.valueKey ? option.value[config.valueKey] : option.value"
|
|
60
|
+
:key="config.valueKey ? option.value?.[config.valueKey] : option.value"
|
|
61
61
|
:is="optionComponent?.component || 'el-option'"
|
|
62
62
|
v-bind="
|
|
63
63
|
optionComponent?.props({
|
|
@@ -372,7 +372,12 @@ const getInitOption = async () => {
|
|
|
372
372
|
});
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
-
let initData
|
|
375
|
+
let initData: any;
|
|
376
|
+
try {
|
|
377
|
+
initData = getValueByKeyPath(initRoot || root, res);
|
|
378
|
+
} catch {
|
|
379
|
+
// 响应中不存在 initRoot/root 路径时按无数据处理
|
|
380
|
+
}
|
|
376
381
|
if (initData) {
|
|
377
382
|
if (!Array.isArray(initData)) {
|
|
378
383
|
initData = [initData];
|
|
@@ -417,8 +422,12 @@ if (typeof props.config.options === 'function') {
|
|
|
417
422
|
const v = props.model[props.name];
|
|
418
423
|
if (Array.isArray(v) ? !v.length : typeof v === 'undefined') return;
|
|
419
424
|
if (typeof v === 'undefined' || hasOption(v)) return;
|
|
420
|
-
|
|
421
|
-
|
|
425
|
+
try {
|
|
426
|
+
const data = await getInitOption();
|
|
427
|
+
setOptions(data);
|
|
428
|
+
} catch {
|
|
429
|
+
setOptions([]);
|
|
430
|
+
}
|
|
422
431
|
};
|
|
423
432
|
|
|
424
433
|
watch(
|
|
@@ -56,6 +56,37 @@ export const mergeFormContexts = (...layers: (FormContext | undefined | null)[])
|
|
|
56
56
|
}) as FormContext;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* 存量下发配置沿用 Vue2 时代的写法,把回调第一个参数当组件实例用,靠 `vm.mForm.xxx`
|
|
61
|
+
* 取表单状态。这类配置除了读,还会往上面挂方法做跨字段通信,例如:
|
|
62
|
+
*
|
|
63
|
+
* ```js
|
|
64
|
+
* vm.mForm.checkPropertyLimit = async (...) => { ... } // 一个字段的 validator 里挂
|
|
65
|
+
* await vm.mForm.checkPropertyLimit(...) // 另一处再取出来调用
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* 所以 `mForm` 必须能指回某个 formState:读能落到 core / context,写能经 `set` trap
|
|
69
|
+
* 落到 coreState 并持久化。
|
|
70
|
+
*
|
|
71
|
+
* 优先级是 core > context > 合成自引用,三者都是刻意的:
|
|
72
|
+
*
|
|
73
|
+
* - **core**:`formState.mForm = formState` 这类直写(如 FormPreview 用自建 services 时)
|
|
74
|
+
* 必须最优先。
|
|
75
|
+
* - **context**:唯一的生产者是把父 formState 整体当 context 传下来的嵌套表单
|
|
76
|
+
* (ComponentForm)。此时 `mForm` 命中 context 指向**父表单**,与 `extendState` 时代
|
|
77
|
+
* 把父 formState 并入子状态的结果一致,跨字段通信仍落在同一份对象上。父 formState
|
|
78
|
+
* 本身也是 Proxy,写入照样持久化,所以这里让 context 赢是对的,别「修正」成指向子表单。
|
|
79
|
+
* - **合成自引用**:前两者都没有时才兜底,让 `vm.mForm.xxx = fn` 落到自己的 coreState。
|
|
80
|
+
*
|
|
81
|
+
* 由此推出一条约束:**不要往 context 里塞普通对象充当 `mForm`**。context 通常是 computed
|
|
82
|
+
* 产物,依赖一变就重建,挂上去的方法会静默丢失。要么直写 core,要么什么都不放交给兜底。
|
|
83
|
+
*
|
|
84
|
+
* 枚举语义上合成的自引用表现得像原型链上的属性:`'mForm' in state` 为真,但不出现在
|
|
85
|
+
* `Object.keys(state)` 里,`getOwnPropertyDescriptor` 也返回 undefined。这是故意的——
|
|
86
|
+
* 按扩展字段打包 formState 的调用方(如发给 AI 的逻辑)会因循环引用炸掉。
|
|
87
|
+
*/
|
|
88
|
+
const SELF_REF_KEY = 'mForm';
|
|
89
|
+
|
|
59
90
|
/**
|
|
60
91
|
* 将 coreState 与宿主业务上下文关联:读取时优先 core,miss 再读穿到 context。
|
|
61
92
|
*
|
|
@@ -72,21 +103,33 @@ export const createFormStateProxy = (
|
|
|
72
103
|
coreState: FormState,
|
|
73
104
|
getContext: (() => FormContext) | Ref<FormContext>,
|
|
74
105
|
): FormState => {
|
|
75
|
-
const resolve = (): Record<string | symbol, any> =>
|
|
76
|
-
(typeof getContext === 'function' ? getContext() : unref(getContext)) || EMPTY_CONTEXT;
|
|
106
|
+
const resolve = (): Record<string | symbol, any> => {
|
|
107
|
+
const ctx = (typeof getContext === 'function' ? getContext() : unref(getContext)) || EMPTY_CONTEXT;
|
|
108
|
+
// 把 formState 自己当 context 传回来(`:context="formState"`)会让 get / has 无限递归,
|
|
109
|
+
// 直接爆栈。这种自引用本就提供不了任何额外字段,断掉即可。
|
|
110
|
+
return ctx === proxy ? EMPTY_CONTEXT : ctx;
|
|
111
|
+
};
|
|
77
112
|
|
|
78
|
-
|
|
113
|
+
const proxy = new Proxy(coreState as object, {
|
|
79
114
|
get(t, k) {
|
|
80
115
|
if (typeof k === 'symbol') return Reflect.get(t, k);
|
|
81
116
|
const v = (t as any)[k];
|
|
82
117
|
if (v !== undefined || Reflect.has(t, k)) return v;
|
|
83
|
-
|
|
118
|
+
|
|
119
|
+
const ctx = resolve();
|
|
120
|
+
if (k in ctx) return ctx[k];
|
|
121
|
+
// 自引用不进 ownKeys:枚举 formState 的调用方(如按扩展字段打包发给 AI 的逻辑)
|
|
122
|
+
// 会因为循环引用炸掉,这里只在显式读取时才合成
|
|
123
|
+
return k === SELF_REF_KEY ? proxy : undefined;
|
|
84
124
|
},
|
|
85
125
|
set(t, k, value) {
|
|
86
126
|
(t as any)[k] = value;
|
|
87
127
|
return true;
|
|
88
128
|
},
|
|
89
|
-
|
|
129
|
+
// `mForm` 在这里为真,但不进 ownKeys、getOwnPropertyDescriptor 也返回 undefined
|
|
130
|
+
// (见 SELF_REF_KEY 注释)。因此展开 / Object.entries 不会循环引用,
|
|
131
|
+
// 但对 proxy 直接做递归遍历(`for...in`、深拷贝、直接 JSON.stringify(formState))仍会。
|
|
132
|
+
has: (t, k) => Reflect.has(t, k) || (typeof k !== 'symbol' && (k in resolve() || k === SELF_REF_KEY)),
|
|
90
133
|
ownKeys: (t) => [
|
|
91
134
|
...new Set([...Reflect.ownKeys(t), ...Reflect.ownKeys(resolve()).filter((k) => typeof k !== 'symbol')]),
|
|
92
135
|
],
|
|
@@ -101,4 +144,6 @@ export const createFormStateProxy = (
|
|
|
101
144
|
return { configurable: true, enumerable: true, writable: true, value: ctx[k] };
|
|
102
145
|
},
|
|
103
146
|
}) as FormState;
|
|
147
|
+
|
|
148
|
+
return proxy;
|
|
104
149
|
};
|