@dazhicheng/ui 1.5.215 → 1.5.217
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/components/tt-form/src/config.js +44 -42
- package/dist/components/tt-form/src/form-render/FormField.vue2.js +67 -70
- package/dist/components/tt-form/src/form-render/FormFieldContact.d.ts +9 -0
- package/dist/components/tt-form/src/form-render/FormFieldContact.js +28 -25
- package/dist/components/tt-form/src/form-render/array-register/FormArrayRegister.d.ts +3 -0
- package/dist/components/tt-form/src/form-render/array-register/FormArrayRegister.js +20 -18
- package/dist/components/tt-form/src/form-render/array-register/props.d.ts +4 -0
- package/dist/components/tt-form/src/form-render/array-register/props.js +1 -0
- package/dist/components/tt-form/src/form-render/array-register/types.d.ts +4 -0
- package/dist/components/tt-form/src/form-render/dependencies.d.ts +5 -0
- package/dist/components/tt-form/src/form-render/dependencies.js +77 -39
- package/dist/components/tt-form/src/group-form/GroupForm.vue.d.ts +15 -7
- package/dist/components/tt-form/src/group-form/GroupForm.vue2.js +120 -104
- package/dist/components/tt-form/src/group-form/GroupSection.vue.d.ts +1 -0
- package/dist/components/tt-form/src/group-form/GroupSection.vue2.js +68 -64
- package/dist/components/tt-form/src/group-form/LazyFormField.vue.d.ts +1 -0
- package/dist/components/tt-form/src/group-form/LazyFormField.vue.js +131 -101
- package/dist/components/tt-form/src/group-form/groupFormApi.d.ts +110 -46
- package/dist/components/tt-form/src/group-form/groupFormApi.js +465 -314
- package/dist/components/tt-form/src/group-form/lazyContext.d.ts +15 -13
- package/dist/components/tt-form/src/group-form/lazyContext.js +42 -26
- package/dist/components/tt-form/src/group-form/types.d.ts +13 -3
- package/dist/components/tt-form/src/group-form/useGroupForm.js +47 -50
- package/dist/components/tt-form/src/hooks/useFieldArray.js +101 -93
- package/dist/components/tt-form/src/hooks/useSelectCollapse.js +47 -47
- package/dist/components/tt-form/src/types.d.ts +3 -5
- package/dist/components/tt-modal-form/index.vue.js +12 -11
- package/dist/components/tt-table/src/TableForm.vue.js +3 -2
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
1
|
+
var C = Object.defineProperty;
|
|
2
|
+
var V = (h, f, e) => f in h ? C(h, f, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[f] = e;
|
|
3
|
+
var l = (h, f, e) => V(h, typeof f != "symbol" ? f + "" : f, e);
|
|
4
4
|
import "axios";
|
|
5
5
|
import "element-plus";
|
|
6
|
-
import {
|
|
7
|
-
import { ref as
|
|
6
|
+
import { isString as F, isFunction as v, isBoolean as E } from "../../../../packages/utils/src/is.js";
|
|
7
|
+
import { ref as b, reactive as A, computed as T, nextTick as R } from "vue";
|
|
8
8
|
import "xe-utils";
|
|
9
9
|
import "dayjs";
|
|
10
10
|
import "dayjs/plugin/utc";
|
|
@@ -14,13 +14,24 @@ import "../../../../node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/ae
|
|
|
14
14
|
import "../../../../node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/enc-utf8.js";
|
|
15
15
|
import "../../../../node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ecb.js";
|
|
16
16
|
import "../../../../node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-pkcs7.js";
|
|
17
|
-
import { isPlainObject as
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
|
|
17
|
+
import { isPlainObject as S, get as N, cloneDeep as K, isEqual as H } from "lodash-es";
|
|
18
|
+
import { validate as k } from "vee-validate";
|
|
19
|
+
import { toTypedSchema as O } from "@vee-validate/zod";
|
|
20
|
+
import { FormApi as x } from "../formApi.js";
|
|
21
|
+
import { resolveRequiredRuleName as j, resolveValueByFieldName as P } from "../form-render/dependencies.js";
|
|
22
|
+
import { resolveArraySchemasForRuntime as W, resolveSchemaFieldInitialValue as $ } from "../useFormContext.js";
|
|
23
|
+
import { normalizeArrayFieldPath as _, childArrayPath as I } from "../utils/array-path.js";
|
|
24
|
+
import { extractFlatSchemas as g, collectDefaultCollapseStates as M, getGroupValues as q, cloneSchemaTree as y, insertFieldsByPosition as D, removeFieldsFromGroup as B, removeGroupsFromGroup as L, isGroupSchema as z, isRowSchema as X } from "./utils.js";
|
|
25
|
+
function Y(h) {
|
|
26
|
+
if (h) {
|
|
27
|
+
if (F(h))
|
|
28
|
+
return h;
|
|
29
|
+
if (v(h.safeParse))
|
|
30
|
+
return O(h);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const J = 20;
|
|
34
|
+
class Se extends x {
|
|
24
35
|
/**
|
|
25
36
|
* @param {TtFormProps} formProps - 底层表单配置(已展平的 schema、layout 等)
|
|
26
37
|
* @param {GroupFormApiOptions} groupOptions - 分组场景专属配置
|
|
@@ -28,75 +39,105 @@ class ae extends P {
|
|
|
28
39
|
constructor(e, t) {
|
|
29
40
|
super(e);
|
|
30
41
|
/** 是否启用虚拟化(IntersectionObserver 懒渲染) */
|
|
31
|
-
|
|
42
|
+
l(this, "virtual");
|
|
43
|
+
/** 是否启用渐进式挂载(分批后台挂载全部字段,挂载后常驻 DOM,用于操作全量 DOM 场景) */
|
|
44
|
+
l(this, "progressive");
|
|
32
45
|
/** 构造时的导航锚点默认配置,作为 store 未设置时的 fallback */
|
|
33
|
-
|
|
46
|
+
l(this, "_defaultNavAnchorProps");
|
|
34
47
|
/** 校验失败时是否自动滚动到第一个有错误的分组,默认 true */
|
|
35
|
-
|
|
48
|
+
l(this, "_scrollToFirstError");
|
|
36
49
|
/** 滚动容器高度 */
|
|
37
|
-
|
|
50
|
+
l(this, "_containerHeight");
|
|
38
51
|
/** IntersectionObserver rootMargin */
|
|
39
|
-
|
|
52
|
+
l(this, "_rootMargin");
|
|
40
53
|
/** 预估每个表单项高度(px) */
|
|
41
|
-
|
|
54
|
+
l(this, "_estimateFieldHeight");
|
|
42
55
|
/** 滚动到分组时的顶部偏移量(px),补偿固定元素遮挡 */
|
|
43
|
-
|
|
56
|
+
l(this, "_scrollOffset");
|
|
44
57
|
/** 各分组折叠状态(key → 是否折叠) */
|
|
45
|
-
|
|
58
|
+
l(this, "collapseStates");
|
|
46
59
|
/** 当前被 ifShow 隐藏的分组 key 集合 */
|
|
47
|
-
|
|
60
|
+
l(this, "hiddenGroupKeys");
|
|
48
61
|
/** 当前被 ifShow 隐藏的字段名集合 */
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
l(this, "hiddenFieldNames");
|
|
63
|
+
/**
|
|
64
|
+
* 导航各分组的错误数量(派生自 vee-validate 的 form.errors,已内置合并挂载 pathState 与未挂载 extraErrorsBag)。
|
|
65
|
+
* 通过 computed 惰性求值:读取时依赖收集 form.errors,任何 setFieldError / validate 之后
|
|
66
|
+
* 只要 form.errors 变化,导航栏渲染时自动重算,无需手动同步。
|
|
67
|
+
*/
|
|
68
|
+
l(this, "navErrorCounts");
|
|
69
|
+
/** navErrorCounts 的响应式来源,reactive Proxy 在读取时依赖注入到此 computed */
|
|
70
|
+
l(this, "_navErrorCountsRef");
|
|
56
71
|
/** 初始传入的分组 schema(用于 fallback) */
|
|
57
|
-
|
|
72
|
+
l(this, "_initialGroupSchema");
|
|
58
73
|
/** 初始展平后的 flatSchema(用于 fallback) */
|
|
59
|
-
|
|
74
|
+
l(this, "_initialFlatSchema");
|
|
60
75
|
/** 各分组 key → 其直属字段名(不含子分组的字段) */
|
|
61
|
-
|
|
76
|
+
l(this, "_groupFieldMap", {});
|
|
62
77
|
/** 各分组 key → 其直属子分组 key 列表 */
|
|
63
|
-
|
|
78
|
+
l(this, "_groupChildrenMap", {});
|
|
64
79
|
/** 字段名 → 所属分组 key 的反向映射,用于快速判断字段是否属于隐藏分组 */
|
|
65
|
-
|
|
80
|
+
l(this, "_fieldToGroupKey", {});
|
|
66
81
|
/** 子分组 key → 父分组 key 的映射,用于递归向上查找祖先分组是否被隐藏 */
|
|
67
|
-
|
|
82
|
+
l(this, "_groupParentMap", {});
|
|
68
83
|
/** 字段名 → 字段 schema 的映射,用于隐藏清空时读取默认值 */
|
|
69
|
-
|
|
84
|
+
l(this, "_fieldSchemaMap", {});
|
|
70
85
|
/** 分组 key 构建时的去重集合,用于检测重复 key */
|
|
71
|
-
|
|
86
|
+
l(this, "_groupKeySet", /* @__PURE__ */ new Set());
|
|
72
87
|
/** 是否已执行过 validate,用于控制导航栏错误同步是否需要重新校验未挂载字段 */
|
|
73
|
-
|
|
88
|
+
l(this, "_hasValidated", !1);
|
|
89
|
+
/** 导航错误同步在单个宏任务窗口内的累计趟数,用于防御非确定性动态规则导致的 errors↔navCount 回环 */
|
|
90
|
+
l(this, "_navSyncPassCount", 0);
|
|
91
|
+
/** 是否已安排本窗口的计数重置(宏任务后清零) */
|
|
92
|
+
l(this, "_navSyncResetScheduled", !1);
|
|
93
|
+
/** 本轮回环告警是否已输出,避免刷屏 */
|
|
94
|
+
l(this, "_navSyncLoopWarned", !1);
|
|
74
95
|
/** VirtualGroupForm 组件实例引用,由 useGroupForm 的 h() 渲染时赋值 */
|
|
75
|
-
|
|
96
|
+
l(this, "groupFormRef", b(null));
|
|
76
97
|
/**
|
|
77
98
|
* @description 响应式读取 store 状态,由 useGroupForm 在运行时注入实现
|
|
78
99
|
* @param {Function} [selector] - 可选的状态选择器
|
|
79
100
|
* @returns {Readonly<Ref<T>>} 响应式状态引用
|
|
80
101
|
*/
|
|
81
|
-
|
|
102
|
+
l(this, "useStore");
|
|
82
103
|
const {
|
|
83
|
-
groupSchema:
|
|
84
|
-
virtual:
|
|
104
|
+
groupSchema: s,
|
|
105
|
+
virtual: r = !0,
|
|
106
|
+
progressive: i = !1,
|
|
85
107
|
navAnchorProps: o,
|
|
86
|
-
scrollToFirstError:
|
|
87
|
-
containerHeight:
|
|
88
|
-
rootMargin:
|
|
89
|
-
estimateFieldHeight:
|
|
90
|
-
scrollOffset:
|
|
108
|
+
scrollToFirstError: a = !0,
|
|
109
|
+
containerHeight: n = "100%",
|
|
110
|
+
rootMargin: u = "400px 0px",
|
|
111
|
+
estimateFieldHeight: c = 52,
|
|
112
|
+
scrollOffset: p = 0
|
|
91
113
|
} = t;
|
|
92
|
-
this.virtual =
|
|
93
|
-
|
|
114
|
+
this.progressive = i, this.virtual = i ? !1 : r, this._defaultNavAnchorProps = { show: !0, ...o }, this._scrollToFirstError = a, this._containerHeight = n, this._rootMargin = u, this._estimateFieldHeight = c, this._scrollOffset = p, this._initialGroupSchema = s, this._initialFlatSchema = g(s), this.collapseStates = A({}), M(s, this.collapseStates), this.hiddenGroupKeys = A(/* @__PURE__ */ new Set()), this.hiddenFieldNames = A(/* @__PURE__ */ new Set()), this._navErrorCountsRef = T(() => this._computeNavErrorCounts()), this.navErrorCounts = new Proxy({}, {
|
|
115
|
+
get: (m, d) => {
|
|
116
|
+
if (F(d))
|
|
117
|
+
return this._navErrorCountsRef.value[d] ?? 0;
|
|
118
|
+
},
|
|
119
|
+
has: (m, d) => F(d) ? d in this._navErrorCountsRef.value : !1,
|
|
120
|
+
ownKeys: () => Reflect.ownKeys(this._navErrorCountsRef.value),
|
|
121
|
+
getOwnPropertyDescriptor: (m, d) => {
|
|
122
|
+
if (!F(d)) return;
|
|
123
|
+
const w = this._navErrorCountsRef.value;
|
|
124
|
+
if (d in w)
|
|
125
|
+
return {
|
|
126
|
+
configurable: !0,
|
|
127
|
+
enumerable: !0,
|
|
128
|
+
writable: !1,
|
|
129
|
+
value: w[d]
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}), this._buildGroupFieldMap(s), this.setState({
|
|
133
|
+
groupSchema: s,
|
|
94
134
|
navAnchorProps: this._defaultNavAnchorProps,
|
|
95
|
-
virtual:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
135
|
+
virtual: this.virtual,
|
|
136
|
+
progressive: i,
|
|
137
|
+
containerHeight: n,
|
|
138
|
+
rootMargin: u,
|
|
139
|
+
estimateFieldHeight: c,
|
|
140
|
+
scrollOffset: p
|
|
100
141
|
});
|
|
101
142
|
}
|
|
102
143
|
/**
|
|
@@ -111,11 +152,11 @@ class ae extends P {
|
|
|
111
152
|
* @param {Partial<GroupFormState> | ((prev: GroupFormState) => Partial<GroupFormState>)} stateOrFn - 新状态或更新函数
|
|
112
153
|
*/
|
|
113
154
|
setState(e) {
|
|
114
|
-
const t = this.store.state,
|
|
115
|
-
if (super.setState(e),
|
|
155
|
+
const t = this.store.state, s = v(e) ? e(t) : e;
|
|
156
|
+
if (super.setState(e), s.groupSchema === void 0)
|
|
116
157
|
return;
|
|
117
|
-
const
|
|
118
|
-
!
|
|
158
|
+
const r = this.store.state.groupSchema;
|
|
159
|
+
!r || r === t.groupSchema || (this._buildGroupFieldMap(r), super.setState({ schema: g(r) }));
|
|
119
160
|
}
|
|
120
161
|
/**
|
|
121
162
|
* @description 分组表单以 `groupSchema` 展平结果为字段全集。仅依赖 store 中的 `schema` 时,若与 `groupSchema` 不同步(例如外部只 `setState` 了 `groupSchema`、或合并顺序导致 `schema` 滞后),`setValues` 默认过滤会误删合法字段;此处优先按 `groupSchema` 展平生成白名单。
|
|
@@ -123,17 +164,17 @@ class ae extends P {
|
|
|
123
164
|
*/
|
|
124
165
|
getFieldNamesForSetValuesFilter() {
|
|
125
166
|
const e = this.store.state.groupSchema;
|
|
126
|
-
return e != null && e.length ?
|
|
167
|
+
return e != null && e.length ? g(e).map((t) => t.fieldName).filter((t) => F(t) && t.length > 0) : super.getFieldNamesForSetValuesFilter();
|
|
127
168
|
}
|
|
128
169
|
/**
|
|
129
|
-
* @description schema 中移除字段后清空 vee
|
|
170
|
+
* @description schema 中移除字段后清空 vee 值/错误,并注销对应数组字段句柄
|
|
130
171
|
* @param {string[]} removedFieldNames 已移除的 fieldName 列表
|
|
131
172
|
*/
|
|
132
173
|
onRemovedSchemaFields(e) {
|
|
133
174
|
if (e.length) {
|
|
134
175
|
super.onRemovedSchemaFields(e);
|
|
135
176
|
for (const t of e)
|
|
136
|
-
this.
|
|
177
|
+
this.unregisterArrayField(t);
|
|
137
178
|
}
|
|
138
179
|
}
|
|
139
180
|
/**
|
|
@@ -149,8 +190,77 @@ class ae extends P {
|
|
|
149
190
|
* await formApi.validate(undefined, false);
|
|
150
191
|
*/
|
|
151
192
|
async validate(e, t) {
|
|
152
|
-
|
|
153
|
-
|
|
193
|
+
var u;
|
|
194
|
+
const s = ((u = this.form) == null ? void 0 : u.values) ?? {}, r = await this._collectValidationFields(s), i = await this._runFieldValidations(r, s);
|
|
195
|
+
this._applyFieldErrors(r, i), this._hasValidated = !0;
|
|
196
|
+
const o = {};
|
|
197
|
+
for (const c of r) {
|
|
198
|
+
const p = i[c.name];
|
|
199
|
+
o[c.name] = { valid: !p, errors: p ? [p] : [] };
|
|
200
|
+
}
|
|
201
|
+
const a = {
|
|
202
|
+
valid: Object.keys(i).length === 0,
|
|
203
|
+
errors: i,
|
|
204
|
+
results: o
|
|
205
|
+
};
|
|
206
|
+
return (t ?? this._scrollToFirstError) && !a.valid && this._scrollToFirstErrorField(r, i), a;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* @description 对收集到的可见字段逐个用 vee-validate 底层 `validate()` 校验,独立字段并发执行。
|
|
210
|
+
* 规则引擎与挂载态字段完全一致(含 defineRule 自定义规则、zod typed schema、跨字段 target 解析)。
|
|
211
|
+
* @param {ResolvedValidationField[]} fields - 待校验字段清单
|
|
212
|
+
* @param {Record<string, any>} values - 整表值,透传给 vee-validate 做跨字段规则解析
|
|
213
|
+
* @returns {Promise<Record<string, string>>} 字段名 → 首条错误信息(仅含校验失败字段)
|
|
214
|
+
*/
|
|
215
|
+
async _runFieldValidations(e, t) {
|
|
216
|
+
const s = await Promise.all(
|
|
217
|
+
e.map(async (i) => {
|
|
218
|
+
const o = Y(i.rules);
|
|
219
|
+
if (!o)
|
|
220
|
+
return { name: i.name, error: void 0 };
|
|
221
|
+
const { valid: a, errors: n } = await k(i.value, o, {
|
|
222
|
+
name: i.name,
|
|
223
|
+
label: i.label,
|
|
224
|
+
values: t
|
|
225
|
+
});
|
|
226
|
+
return { name: i.name, error: a ? void 0 : n[0] };
|
|
227
|
+
})
|
|
228
|
+
), r = {};
|
|
229
|
+
for (const i of s)
|
|
230
|
+
i.error && (r[i.name] = i.error);
|
|
231
|
+
return r;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* @description 把校验结果写回 vee-validate 的错误状态(唯一错误 sink)。
|
|
235
|
+
* 带 diff 保护:只有当目标错误与当前 form.errors 不一致时才写,避免触发 useGroupForm 里
|
|
236
|
+
* `watch(form.errors)` → syncNavErrorCountsFromForm 的无谓回环,并保证回环在有限趟内收敛。
|
|
237
|
+
* @param {ResolvedValidationField[]} fields - 本轮参与校验的字段(决定要清空/设置哪些字段)
|
|
238
|
+
* @param {Record<string, string>} errorMap - 字段名 → 错误信息
|
|
239
|
+
*/
|
|
240
|
+
_applyFieldErrors(e, t) {
|
|
241
|
+
var r, i, o, a;
|
|
242
|
+
const s = ((i = (r = this.form) == null ? void 0 : r.errors) == null ? void 0 : i.value) ?? {};
|
|
243
|
+
for (const n of e) {
|
|
244
|
+
const u = t[n.name];
|
|
245
|
+
s[n.name] !== u && ((a = (o = this.form) == null ? void 0 : o.setFieldError) == null || a.call(o, n.name, u));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* @description 清空全部字段错误(含 vee-validate extraErrorsBag 里从未挂载过的残留)。
|
|
250
|
+
* vee-validate 的 resetForm 只遍历已挂载的 pathState,不会清 extraErrorsBag 里未挂载路径的错误,
|
|
251
|
+
* 因此这里显式遍历「展平 schema 顶层字段 + 当前 form.errors 的全部 key(含未挂载/数组行路径)」
|
|
252
|
+
* 逐个 setFieldError(undefined),保证重置后未挂载字段的残留错误也被清掉。
|
|
253
|
+
*/
|
|
254
|
+
_clearAllFieldErrors() {
|
|
255
|
+
var r, i, o, a;
|
|
256
|
+
const e = /* @__PURE__ */ new Set(), t = this.store.state.schema ?? this._initialFlatSchema;
|
|
257
|
+
for (const n of t)
|
|
258
|
+
n.fieldName && e.add(_(n.fieldName));
|
|
259
|
+
const s = ((i = (r = this.form) == null ? void 0 : r.errors) == null ? void 0 : i.value) ?? {};
|
|
260
|
+
for (const n of Object.keys(s))
|
|
261
|
+
e.add(n);
|
|
262
|
+
for (const n of e)
|
|
263
|
+
(a = (o = this.form) == null ? void 0 : o.setFieldError) == null || a.call(o, n, void 0);
|
|
154
264
|
}
|
|
155
265
|
/**
|
|
156
266
|
* @description 校验并提交表单
|
|
@@ -166,13 +276,13 @@ class ae extends P {
|
|
|
166
276
|
* @param {Partial<ResetFormOpts>} [opts] - 重置选项
|
|
167
277
|
*/
|
|
168
278
|
async resetForm(e, t) {
|
|
169
|
-
return this.
|
|
279
|
+
return this._clearAllFieldErrors(), this._hasValidated = !1, await super.resetForm(e, t);
|
|
170
280
|
}
|
|
171
281
|
/**
|
|
172
|
-
* @description
|
|
282
|
+
* @description 重置校验状态(同时清空虚拟字段错误缓存;navErrorCounts 由 form.errors 派生,自动更新)
|
|
173
283
|
*/
|
|
174
284
|
async resetValidate() {
|
|
175
|
-
return this.
|
|
285
|
+
return this._clearAllFieldErrors(), this._hasValidated = !1, await super.resetValidate();
|
|
176
286
|
}
|
|
177
287
|
/**
|
|
178
288
|
* @description 获取表单值
|
|
@@ -182,8 +292,8 @@ class ae extends P {
|
|
|
182
292
|
async getValues(e = !1) {
|
|
183
293
|
const t = await super.getValues();
|
|
184
294
|
if (e) {
|
|
185
|
-
const
|
|
186
|
-
return
|
|
295
|
+
const s = this.store.state.groupSchema ?? this._initialGroupSchema;
|
|
296
|
+
return q(s, t);
|
|
187
297
|
}
|
|
188
298
|
return t;
|
|
189
299
|
}
|
|
@@ -192,9 +302,29 @@ class ae extends P {
|
|
|
192
302
|
* @param {string} key - 分组 key
|
|
193
303
|
*/
|
|
194
304
|
scrollToGroup(e) {
|
|
195
|
-
var
|
|
305
|
+
var s;
|
|
196
306
|
const t = this.groupFormRef.value;
|
|
197
|
-
(
|
|
307
|
+
(s = t == null ? void 0 : t.scrollToGroup) == null || s.call(t, e);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* @description 滚动到指定字段(借助 LazyFormField 容器的 data-field-anchor 锚点,未挂载也可定位)。
|
|
311
|
+
* 主要用于「无分组、且虚拟懒渲染尚未挂载」的字段——它们没有分组锚点,基类按 [name] 也找不到 DOM。
|
|
312
|
+
* @param {string} fieldName - 字段名(完整路径)
|
|
313
|
+
*/
|
|
314
|
+
scrollToField(e) {
|
|
315
|
+
var s;
|
|
316
|
+
const t = this.groupFormRef.value;
|
|
317
|
+
(s = t == null ? void 0 : t.scrollToField) == null || s.call(t, e);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* @description 等待渐进式挂载完成(所有字段已渲染进 DOM)。用于需要操作全量 DOM 的场景
|
|
321
|
+
* (截图、导出、爬虫等)前调用。非 progressive 模式下立即 resolve。
|
|
322
|
+
* @returns {Promise<void>}
|
|
323
|
+
*/
|
|
324
|
+
whenAllRendered() {
|
|
325
|
+
var t;
|
|
326
|
+
const e = this.groupFormRef.value;
|
|
327
|
+
return ((t = e == null ? void 0 : e.whenAllRendered) == null ? void 0 : t.call(e)) ?? Promise.resolve();
|
|
198
328
|
}
|
|
199
329
|
/**
|
|
200
330
|
* @description 切换分组折叠状态
|
|
@@ -216,18 +346,18 @@ class ae extends P {
|
|
|
216
346
|
* group 隐藏时额外维护 {@link hiddenGroupKeys} 供导航和渲染判断;
|
|
217
347
|
* group / row 隐藏时都会清空其下字段校验,并把字段加入 {@link hiddenFieldNames},使隐藏字段不参与校验。
|
|
218
348
|
* 可见性变化后在下一帧调用 {@link syncNavErrorCountsFromForm},使「先 validate 再隐藏再显示」时
|
|
219
|
-
*
|
|
349
|
+
* 导航徽标与表单项错误展示保持一致。
|
|
220
350
|
* @param {string} key - 分组 key
|
|
221
351
|
* @param {boolean} visible - 是否可见
|
|
222
352
|
* @param {string[]} [fieldNames] - 兼容旧内部调用的字段名列表,传入时按字段集合显隐处理
|
|
223
353
|
*/
|
|
224
|
-
setGroupVisible(e, t,
|
|
225
|
-
if (
|
|
226
|
-
this.setFieldsVisible(
|
|
354
|
+
setGroupVisible(e, t, s) {
|
|
355
|
+
if (s) {
|
|
356
|
+
this.setFieldsVisible(s, t);
|
|
227
357
|
return;
|
|
228
358
|
}
|
|
229
|
-
const
|
|
230
|
-
t ? (this.hiddenGroupKeys.delete(e), this._setFieldsHidden(
|
|
359
|
+
const r = this.getGroupFieldNames(e);
|
|
360
|
+
t ? (this.hiddenGroupKeys.delete(e), this._setFieldsHidden(r, !1)) : (this._setFieldsHidden(r, !0), this._clearHiddenFieldValues(r), this.hiddenGroupKeys.add(e)), R(() => {
|
|
231
361
|
this.syncNavErrorCountsFromForm();
|
|
232
362
|
});
|
|
233
363
|
}
|
|
@@ -237,7 +367,7 @@ class ae extends P {
|
|
|
237
367
|
* @param {boolean} visible - 是否可见
|
|
238
368
|
*/
|
|
239
369
|
setFieldsVisible(e, t) {
|
|
240
|
-
t ? this._setFieldsHidden(e, !1) : (this._setFieldsHidden(e, !0), this._clearHiddenFieldValues(e)),
|
|
370
|
+
t ? this._setFieldsHidden(e, !1) : (this._setFieldsHidden(e, !0), this._clearHiddenFieldValues(e)), R(() => {
|
|
241
371
|
this.syncNavErrorCountsFromForm();
|
|
242
372
|
});
|
|
243
373
|
}
|
|
@@ -259,8 +389,8 @@ class ae extends P {
|
|
|
259
389
|
* @returns {boolean}
|
|
260
390
|
*/
|
|
261
391
|
_matchFieldOrNested(e, t) {
|
|
262
|
-
const
|
|
263
|
-
return
|
|
392
|
+
const s = _(e), r = _(t);
|
|
393
|
+
return s === r || s.startsWith(`${r}.`) || s.startsWith(`${r}[`);
|
|
264
394
|
}
|
|
265
395
|
/**
|
|
266
396
|
* @description 按当前 store 中的 groupSchema(若无则用初始 schema)重置折叠状态:
|
|
@@ -271,7 +401,7 @@ class ae extends P {
|
|
|
271
401
|
const e = this.store.state.groupSchema ?? this._initialGroupSchema;
|
|
272
402
|
for (const t of Object.keys(this.collapseStates))
|
|
273
403
|
delete this.collapseStates[t];
|
|
274
|
-
|
|
404
|
+
M(e, this.collapseStates);
|
|
275
405
|
}
|
|
276
406
|
/**
|
|
277
407
|
* @description 全量应用分组 schema 树到 store(扁平 schema、groupSchema、分组字段映射、折叠默认值)。
|
|
@@ -293,8 +423,8 @@ class ae extends P {
|
|
|
293
423
|
* formApi.appendFields([newGroup]);
|
|
294
424
|
*/
|
|
295
425
|
appendFields(e, t) {
|
|
296
|
-
const
|
|
297
|
-
t && (t.before || t.after) ?
|
|
426
|
+
const s = this.store.state.groupSchema ?? [], r = y(s);
|
|
427
|
+
t && (t.before || t.after) ? D(r, e, t) || r.push(...e) : r.push(...e), this._applyGroupSchema(r), this.seedDefaultsForAppendedFields(g(e));
|
|
298
428
|
}
|
|
299
429
|
/**
|
|
300
430
|
* @description 按标识从 groupSchema 树中移除节点,无需全量重建 schema。
|
|
@@ -305,22 +435,20 @@ class ae extends P {
|
|
|
305
435
|
*/
|
|
306
436
|
removeFields(e) {
|
|
307
437
|
if (!e.length) return;
|
|
308
|
-
const t = this.store.state.groupSchema ?? [],
|
|
309
|
-
this._applyGroupSchema(
|
|
438
|
+
const t = this.store.state.groupSchema ?? [], s = B(y(t), new Set(e));
|
|
439
|
+
this._applyGroupSchema(s);
|
|
310
440
|
}
|
|
311
441
|
/**
|
|
312
442
|
* @description 清理已移除分组的运行时状态(隐藏集、折叠、导航徽标等)
|
|
313
443
|
* @param keysToRemove - 待移除的分组 key(含子分组)
|
|
314
444
|
*/
|
|
315
445
|
_purgeRemovedGroupState(e) {
|
|
316
|
-
for (const
|
|
317
|
-
this._setFieldsHidden(this.getGroupFieldNames(
|
|
446
|
+
for (const s of e)
|
|
447
|
+
this._setFieldsHidden(this.getGroupFieldNames(s), !1), this.hiddenGroupKeys.delete(s), Reflect.deleteProperty(this.collapseStates, s);
|
|
318
448
|
const t = this.navAnchorProps.extraBadgeCounts;
|
|
319
449
|
if (t)
|
|
320
|
-
for (const
|
|
321
|
-
Reflect.deleteProperty(t,
|
|
322
|
-
for (const r of Object.keys(this.navErrorCounts))
|
|
323
|
-
r in this._groupFieldMap || Reflect.deleteProperty(this.navErrorCounts, r);
|
|
450
|
+
for (const s of e)
|
|
451
|
+
Reflect.deleteProperty(t, s);
|
|
324
452
|
}
|
|
325
453
|
/**
|
|
326
454
|
* @description 按分组 key 移除分组及其子分组(从 groupSchema 树删除),并清空其下字段值与相关状态
|
|
@@ -330,123 +458,118 @@ class ae extends P {
|
|
|
330
458
|
* formApi.removeGroups('group1');
|
|
331
459
|
*/
|
|
332
460
|
removeGroups(e) {
|
|
333
|
-
const t = Array.isArray(e) ? e : [e],
|
|
334
|
-
if (!
|
|
461
|
+
const t = Array.isArray(e) ? e : [e], s = this._collectGroupKeysWithDescendants(t);
|
|
462
|
+
if (!s.length)
|
|
335
463
|
return;
|
|
336
|
-
const
|
|
337
|
-
this._applyGroupSchema(
|
|
464
|
+
const r = [...new Set(s.flatMap((a) => this.getGroupFieldNames(a, !0)))], i = this.store.state.groupSchema ?? [], o = L(y(i), new Set(s));
|
|
465
|
+
this._applyGroupSchema(o), r.length && this.onRemovedSchemaFields(r), this._purgeRemovedGroupState(s), this.navAnchorProps.show && this.syncNavErrorCountsFromForm();
|
|
338
466
|
}
|
|
339
467
|
/**
|
|
340
|
-
* @description
|
|
341
|
-
*
|
|
468
|
+
* @description 读取 vee-validate 的实时 errors(已内置合并挂载字段 pathState 与未挂载字段 extraErrorsBag),
|
|
469
|
+
* 剔除隐藏字段后作为导航徽标计数来源。不再需要外挂 errorCacheMap——未挂载字段的错误由 setFieldError
|
|
470
|
+
* 直接写入 vee-validate 的 extraErrorsBag,天然进入 form.errors。
|
|
471
|
+
* @returns {Record<string, string>} 过滤隐藏字段后的错误映射
|
|
342
472
|
*/
|
|
343
473
|
_mergeErrorsForNav() {
|
|
344
|
-
var
|
|
345
|
-
const e = (
|
|
474
|
+
var s, r;
|
|
475
|
+
const e = (r = (s = this.form) == null ? void 0 : s.errors) == null ? void 0 : r.value, t = {};
|
|
346
476
|
if (e && typeof e == "object")
|
|
347
|
-
for (const
|
|
348
|
-
if (this._isFieldHidden(
|
|
349
|
-
const
|
|
350
|
-
|
|
477
|
+
for (const i of Object.keys(e)) {
|
|
478
|
+
if (this._isFieldHidden(i)) continue;
|
|
479
|
+
const o = e[i];
|
|
480
|
+
o && (t[i] = o);
|
|
351
481
|
}
|
|
352
|
-
return
|
|
353
|
-
this._isFieldHidden(i) || o && t[i] === void 0 && (t[i] = o);
|
|
354
|
-
}), t;
|
|
482
|
+
return t;
|
|
355
483
|
}
|
|
356
484
|
/**
|
|
357
|
-
* @description
|
|
358
|
-
*
|
|
359
|
-
*
|
|
485
|
+
* @description 刷新未挂载字段的校验错误,使 dependencies 条件变化后导航徽标即时反映。
|
|
486
|
+
* 注:`navErrorCounts` 是依赖 form.errors 的 computed 派生量,会自动重算,此方法只负责在
|
|
487
|
+
* 「已 validate 过」的虚拟列表场景下,重算可见字段错误并写回 form.errors(未挂载字段落 extraErrorsBag)。
|
|
488
|
+
* 非虚拟化 / 未 validate 过时直接返回,computed 会自然反映 form.errors 现状。
|
|
360
489
|
*/
|
|
361
490
|
async syncNavErrorCountsFromForm() {
|
|
362
|
-
|
|
363
|
-
if (!this.virtual || !this._hasValidated) {
|
|
364
|
-
this._updateNavErrorCounts(this._mergeErrorsForNav());
|
|
491
|
+
if (!this.virtual && !this.progressive || !this._hasValidated)
|
|
365
492
|
return;
|
|
366
|
-
}
|
|
367
493
|
const e = this.form;
|
|
368
|
-
if (!(e != null && e.values))
|
|
369
|
-
|
|
494
|
+
if (!(e != null && e.values))
|
|
495
|
+
return;
|
|
496
|
+
if (this._navSyncPassCount >= J) {
|
|
497
|
+
this._navSyncLoopWarned || (this._navSyncLoopWarned = !0);
|
|
370
498
|
return;
|
|
371
499
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
500
|
+
this._navSyncPassCount++, this._navSyncResetScheduled || (this._navSyncResetScheduled = !0, setTimeout(() => {
|
|
501
|
+
this._navSyncPassCount = 0, this._navSyncResetScheduled = !1, this._navSyncLoopWarned = !1;
|
|
502
|
+
}, 0));
|
|
503
|
+
const t = e.values, s = await this._collectValidationFields(t), r = await this._runFieldValidations(s, t);
|
|
504
|
+
this._applyFieldErrors(s, r);
|
|
505
|
+
}
|
|
506
|
+
// _clearNavErrorCounts 已移除:navErrorCounts 是派生量,resetForm/resetValidate
|
|
507
|
+
// 会让 vee-validate 清空 form.errors,computed 自动重算为全 0,无需主动清。
|
|
508
|
+
/**
|
|
509
|
+
* @description 校验失败后滚动到第一个错误字段所属的分组。
|
|
510
|
+
* 按 fields 的文档顺序取第一个出错字段(不是遍历分组"是否含任意错误",避免总是命中最外层祖先分组),
|
|
511
|
+
* 定位到该字段直接所属的最内层分组,并展开其整条折叠的祖先链,保证目标在 DOM 中且可见。
|
|
512
|
+
* @param {ResolvedValidationField[]} fields - 本轮参与校验的字段(已按文档顺序)
|
|
513
|
+
* @param {Record<string, string>} errorMap - 字段名 → 错误信息
|
|
514
|
+
*/
|
|
515
|
+
_scrollToFirstErrorField(e, t) {
|
|
516
|
+
var a;
|
|
517
|
+
const s = e.find((n) => t[n.name]);
|
|
518
|
+
if (!s) return;
|
|
519
|
+
const r = this._getFieldGroupKey(s.name);
|
|
520
|
+
if (!r) {
|
|
521
|
+
this.virtual && ((a = this.groupFormRef.value) != null && a.scrollToField) ? this.scrollToField(s.name) : this.scrollToFirstError(s.name);
|
|
522
|
+
return;
|
|
393
523
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
524
|
+
let i = r;
|
|
525
|
+
const o = [];
|
|
526
|
+
for (; i; )
|
|
527
|
+
o.push(i), i = this._groupParentMap[i];
|
|
528
|
+
for (const n of o.reverse())
|
|
529
|
+
this.collapseStates[n] && this.toggleCollapse(n);
|
|
530
|
+
this.scrollToGroup(r);
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* @description 解析字段在未挂载状态下应使用的有效校验规则:优先取 dependencies.rules 的动态结果,
|
|
534
|
+
* 为空时回退到静态 schema.rules;两者都为空时,若 dependencies.required 为真则视为 "required"。
|
|
535
|
+
* 与 FormField.vue 中 currentRules / fieldRules 的计算逻辑保持一致,避免虚拟列表下未挂载字段
|
|
536
|
+
* 因为只写了 dependencies.required(未写静态 rules)而被漏判为合法。
|
|
537
|
+
* @param {FormSchema} schema - 字段 schema
|
|
538
|
+
* @param {Record<string, any>} values - 当前表单值
|
|
539
|
+
* @returns {Promise<FormSchemaRuleType | undefined>} 有效校验规则
|
|
398
540
|
*/
|
|
399
|
-
_clearNavErrorCounts() {
|
|
400
|
-
for (const e of Object.keys(this.navErrorCounts))
|
|
401
|
-
this.navErrorCounts[e] = 0;
|
|
402
|
-
}
|
|
403
541
|
/**
|
|
404
|
-
* @description
|
|
405
|
-
*
|
|
542
|
+
* @description 安全执行用户提供的 dependencies 异步回调:捕获异常并降级,避免单个字段的回调抛错
|
|
543
|
+
* (或 reject)拖垮整表校验 / 导航同步。故障隔离到字段级,出错时用给定回退值并告警。
|
|
544
|
+
* @param {() => T | Promise<T>} fn - 用户回调
|
|
545
|
+
* @param {T} fallback - 出错时的回退值
|
|
546
|
+
* @param {string} context - 日志上下文(字段名 + 回调类型)
|
|
547
|
+
* @returns {Promise<T>}
|
|
406
548
|
*/
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
* @description 从校验结果中剔除属于隐藏字段集合的错误,并重新判定 valid 状态
|
|
413
|
-
* @param {object} result - 校验结果
|
|
414
|
-
* @returns {object} 过滤后的校验结果
|
|
415
|
-
*/
|
|
416
|
-
_stripHiddenFieldErrors(e) {
|
|
417
|
-
const t = {};
|
|
418
|
-
let r = !1;
|
|
419
|
-
for (const [s, o] of Object.entries(e.errors))
|
|
420
|
-
this._isFieldHidden(s) || (t[s] = o, o && (r = !0));
|
|
421
|
-
return { ...e, errors: t, valid: !r };
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* @description 对单个字段值执行 rules 校验(纯数据,不挂载组件)。
|
|
425
|
-
* 支持字符串规则("required" / "selectRequired")和 Zod schema。
|
|
426
|
-
* @param {unknown} value - 字段值
|
|
427
|
-
* @param {FormSchemaRuleType | undefined} rules - 校验规则
|
|
428
|
-
* @param {string} fieldName - 字段名
|
|
429
|
-
* @returns {Promise<string | undefined>} 错误信息,无错误返回 undefined
|
|
430
|
-
*/
|
|
431
|
-
async _validateFieldValue(e, t, r) {
|
|
432
|
-
var s;
|
|
433
|
-
if (t) {
|
|
434
|
-
if (b(t)) {
|
|
435
|
-
if (t === "required") {
|
|
436
|
-
if (e == null || e === "")
|
|
437
|
-
return "请输入";
|
|
438
|
-
} else if (t === "selectRequired" && (e == null || Array.isArray(e) && e.length === 0 || e === ""))
|
|
439
|
-
return "请选择";
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
try {
|
|
443
|
-
const i = t.safeParse(e);
|
|
444
|
-
if (!i.success)
|
|
445
|
-
return ((s = i.error.issues[0]) == null ? void 0 : s.message) || `${r} 校验失败`;
|
|
446
|
-
} catch {
|
|
447
|
-
}
|
|
549
|
+
async _safeInvoke(e, t, s) {
|
|
550
|
+
try {
|
|
551
|
+
return await e();
|
|
552
|
+
} catch {
|
|
553
|
+
return t;
|
|
448
554
|
}
|
|
449
555
|
}
|
|
556
|
+
async _resolveEffectiveRules(e, t) {
|
|
557
|
+
var a;
|
|
558
|
+
const s = e.dependencies;
|
|
559
|
+
let r;
|
|
560
|
+
s && v(s.rules) && (r = await this._safeInvoke(
|
|
561
|
+
() => s.rules(t, this.form),
|
|
562
|
+
void 0,
|
|
563
|
+
`${e.fieldName}.dependencies.rules`
|
|
564
|
+
));
|
|
565
|
+
const i = r || e.rules;
|
|
566
|
+
let o = !1;
|
|
567
|
+
return s && v(s.required) && (o = !!await this._safeInvoke(
|
|
568
|
+
() => s.required(t, this.form),
|
|
569
|
+
!1,
|
|
570
|
+
`${e.fieldName}.dependencies.required`
|
|
571
|
+
)), i ? F(i) ? i : o ? ((a = i == null ? void 0 : i.unwrap) == null ? void 0 : a.call(i)) ?? i : i : o ? j(e.component) : void 0;
|
|
572
|
+
}
|
|
450
573
|
/**
|
|
451
574
|
* @description 检查字段的 dependencies 条件,判断字段在当前值下是否应该被校验。
|
|
452
575
|
* 与 FormField.vue 保持一致:visible = !hide && isIf && isShow
|
|
@@ -454,59 +577,104 @@ class ae extends P {
|
|
|
454
577
|
* @param {Record<string, any>} values - 当前表单值
|
|
455
578
|
* @returns {Promise<boolean>} 字段是否可见
|
|
456
579
|
*/
|
|
457
|
-
async _isFieldVisible(e, t) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
580
|
+
async _isFieldVisible(e, t, s) {
|
|
581
|
+
const r = s ?? e.fieldName;
|
|
582
|
+
if (r && this._isFieldHidden(r) || e.hide) return !1;
|
|
583
|
+
const i = e.dependencies;
|
|
584
|
+
if (!i) return !0;
|
|
585
|
+
const o = i.if;
|
|
586
|
+
if (o != null && (E(o) && !o || v(o) && !await this._safeInvoke(() => o(t, this.form), !0, `${r}.dependencies.if`)))
|
|
463
587
|
return !1;
|
|
464
|
-
const
|
|
465
|
-
return !(
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* @description 对所有未挂载的字段做纯值校验,补充到 validate 结果中,
|
|
469
|
-
* 并将错误写入 errorCacheMap 以便 LazyFormField 挂载时恢复。
|
|
470
|
-
* 会跳过因 dependencies.if 条件不满足而不应渲染的字段,与原始表单校验行为一致。
|
|
471
|
-
* @param {object} validateResult - 父类 validate 返回的校验结果
|
|
472
|
-
* @returns {Promise<object>} 补充未挂载字段错误后的校验结果
|
|
473
|
-
*/
|
|
474
|
-
async _validateUnmountedFields(e) {
|
|
475
|
-
const t = this.form;
|
|
476
|
-
if (!(t != null && t.values)) return e;
|
|
477
|
-
const r = t.values, s = new Set(Object.keys(e.results || {})), o = {}, i = this.store.state.schema ?? this._initialFlatSchema;
|
|
478
|
-
for (const a of i) {
|
|
479
|
-
const l = a.fieldName;
|
|
480
|
-
if (!l || s.has(l) || !await this._isFieldVisible(a, r)) continue;
|
|
481
|
-
const h = E(r, l), d = await this._validateFieldValue(h, a.rules, l);
|
|
482
|
-
d && (o[l] = d, this.errorCacheMap.set(l, d));
|
|
483
|
-
}
|
|
484
|
-
return Object.keys(o).length > 0 ? {
|
|
485
|
-
valid: !1,
|
|
486
|
-
errors: { ...e.errors, ...o },
|
|
487
|
-
results: e.results
|
|
488
|
-
} : e;
|
|
588
|
+
const a = i.show;
|
|
589
|
+
return !(a != null && (E(a) && !a || v(a) && !await this._safeInvoke(() => a(t, this.form), !0, `${r}.dependencies.show`)));
|
|
489
590
|
}
|
|
490
591
|
/**
|
|
491
|
-
* @description
|
|
592
|
+
* @description 收集当前需要校验的全部可见字段(顶层字段 + 数组行递归展开的行内字段)。
|
|
593
|
+
* 统一 validate / syncNavErrorCountsFromForm 的字段来源,避免两份平行遍历逻辑走偏。
|
|
594
|
+
* - 顶层字段:遍历展平 schema,逐个走 {@link _isFieldVisible} 过滤(hide / dependencies.if / dependencies.show / 隐藏分组)。
|
|
595
|
+
* - 数组字段(useArraySchema):字段自身规则按普通字段收集(与未挂载路径旧行为一致,也顺带覆盖挂载态),
|
|
596
|
+
* 再从 form.values 取当前数组值,按行调用 {@link resolveArraySchemasForRuntime}(safeMutations 只读,无副作用)
|
|
597
|
+
* 展开行内字段,递归处理嵌套数组。行内字段路径与 FormArrayRegister 渲染时的注册键完全一致。
|
|
598
|
+
* @param {Record<string, any>} values - 当前表单值
|
|
599
|
+
* @returns {Promise<ResolvedValidationField[]>} 扁平化的待校验字段清单
|
|
492
600
|
*/
|
|
493
|
-
|
|
494
|
-
this.
|
|
601
|
+
async _collectValidationFields(e) {
|
|
602
|
+
const t = this.store.state.schema ?? this._initialFlatSchema;
|
|
603
|
+
return (await Promise.all(
|
|
604
|
+
t.map((r) => this._collectFieldRecursive(r, r.fieldName, e))
|
|
605
|
+
)).flat();
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* @description 递归收集单个 schema 节点的可校验字段。普通字段直接产出;
|
|
609
|
+
* 数组字段先产出自身规则,再按当前值逐行展开行内字段(含嵌套数组)。
|
|
610
|
+
* @param {FormSchema} schema - 字段 schema(顶层或行内)
|
|
611
|
+
* @param {string} fieldName - 该字段的完整路径(行内字段已拼好数组下标路径)
|
|
612
|
+
* @param {Record<string, any>} values - 当前表单值
|
|
613
|
+
* @returns {Promise<ResolvedValidationField[]>} 该节点(含数组行内字段)产出的字段清单,按文档顺序排列
|
|
614
|
+
*/
|
|
615
|
+
async _collectFieldRecursive(e, t, s) {
|
|
616
|
+
if (!t) return [];
|
|
617
|
+
if (!await this._isFieldVisible(e, s, t)) return [];
|
|
618
|
+
const r = [], i = await this._resolveEffectiveRules(e, s);
|
|
619
|
+
if (i && r.push({
|
|
620
|
+
name: t,
|
|
621
|
+
label: F(e.label) ? e.label : void 0,
|
|
622
|
+
rules: i,
|
|
623
|
+
value: P(s, t)
|
|
624
|
+
}), !e.useArraySchema) return r;
|
|
625
|
+
const o = _(t), a = P(s, o);
|
|
626
|
+
if (!Array.isArray(a) || a.length === 0) return r;
|
|
627
|
+
const n = t.includes(".") ? t.slice(0, t.lastIndexOf(".")) : void 0, u = await Promise.all(
|
|
628
|
+
a.map(async (c, p) => {
|
|
629
|
+
const m = _(`${o}[${p}]`);
|
|
630
|
+
let d;
|
|
631
|
+
try {
|
|
632
|
+
d = W({
|
|
633
|
+
item: e,
|
|
634
|
+
arrayPath: o,
|
|
635
|
+
itemPath: m,
|
|
636
|
+
index: p,
|
|
637
|
+
row: c ?? {},
|
|
638
|
+
arrayLength: a.length,
|
|
639
|
+
parentItemPath: n,
|
|
640
|
+
form: s,
|
|
641
|
+
// 传入 arrayAction:safeMutations 会把它包成只读(push/remove 等变为 no-op),
|
|
642
|
+
// 既让 arraySchema 里读 ctx.arrayHandle/arrayAction 不至于 NPE,又保证校验阶段无副作用。
|
|
643
|
+
arrayAction: this.arrayAction,
|
|
644
|
+
safeMutations: !0
|
|
645
|
+
});
|
|
646
|
+
} catch {
|
|
647
|
+
return [];
|
|
648
|
+
}
|
|
649
|
+
return (await Promise.all(
|
|
650
|
+
d.map(
|
|
651
|
+
(G) => this._collectFieldRecursive(
|
|
652
|
+
G,
|
|
653
|
+
_(`${m}.${G.fieldName}`),
|
|
654
|
+
s
|
|
655
|
+
)
|
|
656
|
+
)
|
|
657
|
+
)).flat();
|
|
658
|
+
})
|
|
659
|
+
);
|
|
660
|
+
for (const c of u)
|
|
661
|
+
r.push(...c);
|
|
662
|
+
return r;
|
|
495
663
|
}
|
|
496
664
|
/**
|
|
497
665
|
* @description 收集分组 key 及其所有子分组 key(去重)
|
|
498
666
|
* @param keys - 起始分组 key 列表
|
|
499
667
|
*/
|
|
500
668
|
_collectGroupKeysWithDescendants(e) {
|
|
501
|
-
const t = /* @__PURE__ */ new Set(),
|
|
502
|
-
if (!(!
|
|
503
|
-
t.add(
|
|
504
|
-
for (const
|
|
505
|
-
|
|
669
|
+
const t = /* @__PURE__ */ new Set(), s = (r) => {
|
|
670
|
+
if (!(!r || t.has(r))) {
|
|
671
|
+
t.add(r);
|
|
672
|
+
for (const i of this._groupChildrenMap[r] ?? [])
|
|
673
|
+
s(i);
|
|
506
674
|
}
|
|
507
675
|
};
|
|
508
|
-
for (const
|
|
509
|
-
r
|
|
676
|
+
for (const r of e)
|
|
677
|
+
s(r);
|
|
510
678
|
return [...t];
|
|
511
679
|
}
|
|
512
680
|
/**
|
|
@@ -515,42 +683,35 @@ class ae extends P {
|
|
|
515
683
|
* @param {string} [parentKey] - 父分组 key
|
|
516
684
|
*/
|
|
517
685
|
_buildGroupFieldMap(e, t) {
|
|
518
|
-
var
|
|
686
|
+
var s;
|
|
519
687
|
t || (this._groupFieldMap = {}, this._groupChildrenMap = {}, this._fieldToGroupKey = {}, this._groupParentMap = {}, this._fieldSchemaMap = {}, this._groupKeySet.clear());
|
|
520
|
-
for (const
|
|
521
|
-
if (
|
|
522
|
-
if (this._groupKeySet.has(
|
|
688
|
+
for (const r of e)
|
|
689
|
+
if (z(r)) {
|
|
690
|
+
if (this._groupKeySet.has(r.key))
|
|
523
691
|
throw new Error(
|
|
524
|
-
`[GroupFormApi] duplicate group key detected: "${
|
|
692
|
+
`[GroupFormApi] duplicate group key detected: "${r.key}". Group key must be unique in groupSchema tree.`
|
|
525
693
|
);
|
|
526
|
-
this._groupKeySet.add(
|
|
527
|
-
} else
|
|
694
|
+
this._groupKeySet.add(r.key), this._groupFieldMap[r.key] = [], this._groupChildrenMap[r.key] = [], t && (this._groupParentMap[r.key] = t, (s = this._groupChildrenMap)[t] ?? (s[t] = []), this._groupChildrenMap[t].push(r.key)), this._buildGroupFieldMap(r.children, r.key);
|
|
695
|
+
} else X(r) ? this._buildGroupFieldMap(r.children, t) : t && "fieldName" in r && r.fieldName ? (this._groupFieldMap[t].push(r.fieldName), this._fieldToGroupKey[r.fieldName] = t, this._fieldSchemaMap[r.fieldName] = r) : "fieldName" in r && r.fieldName && (this._fieldSchemaMap[r.fieldName] = r);
|
|
528
696
|
}
|
|
529
697
|
/**
|
|
530
698
|
* @description 根据错误映射更新导航各分组的错误计数
|
|
531
699
|
* @param {Record<string, string>} errors - 字段错误映射
|
|
532
700
|
*/
|
|
533
|
-
|
|
534
|
-
|
|
701
|
+
/**
|
|
702
|
+
* @description 计算导航各分组的错误数量(纯函数:读入 form.errors,返回 { groupKey: count })。
|
|
703
|
+
* 仅在 {@link _navErrorCountsRef} 的 computed getter 中调用;不再对外暴露 push 式更新入口。
|
|
704
|
+
* @returns {Record<string, number>} 各分组的错误数量
|
|
705
|
+
*/
|
|
706
|
+
_computeNavErrorCounts() {
|
|
707
|
+
const e = this._mergeErrorsForNav(), t = Object.keys(e).filter((r) => !!e[r]), s = {};
|
|
535
708
|
for (const r of Object.keys(this._groupFieldMap)) {
|
|
536
|
-
const
|
|
537
|
-
(
|
|
709
|
+
const i = this.getGroupFieldNames(r), o = t.filter(
|
|
710
|
+
(a) => i.some((n) => this._matchFieldOrNested(a, n))
|
|
538
711
|
).length;
|
|
539
|
-
|
|
712
|
+
s[r] = o;
|
|
540
713
|
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* @description 根据错误字段找到第一个包含错误的分组 key(按 schema 定义顺序)
|
|
544
|
-
* @param {Record<string, string>} errors - 错误字段映射
|
|
545
|
-
* @returns {string | undefined} 第一个有错误的分组 key
|
|
546
|
-
*/
|
|
547
|
-
_findFirstErrorGroupKey(e) {
|
|
548
|
-
const t = Object.keys(e).filter((r) => e[r]);
|
|
549
|
-
if (t.length !== 0)
|
|
550
|
-
for (const r of Object.keys(this._groupFieldMap)) {
|
|
551
|
-
const s = this.getGroupFieldNames(r);
|
|
552
|
-
if (s != null && s.some((o) => t.some((i) => this._matchFieldOrNested(i, o)))) return r;
|
|
553
|
-
}
|
|
714
|
+
return s;
|
|
554
715
|
}
|
|
555
716
|
/**
|
|
556
717
|
* @description 获取指定分组下的字段名列表,默认会获取该分组下的所有字段
|
|
@@ -564,17 +725,17 @@ class ae extends P {
|
|
|
564
725
|
/**
|
|
565
726
|
* @description 获取指定分组字段名(带防环保护),避免异常 schema 导致递归栈溢出
|
|
566
727
|
*/
|
|
567
|
-
_getGroupFieldNamesWithVisited(e, t,
|
|
568
|
-
if (
|
|
728
|
+
_getGroupFieldNamesWithVisited(e, t, s) {
|
|
729
|
+
if (s.has(e))
|
|
569
730
|
return [];
|
|
570
|
-
|
|
571
|
-
const
|
|
731
|
+
s.add(e);
|
|
732
|
+
const r = this._groupFieldMap[e] ?? [];
|
|
572
733
|
if (!t)
|
|
573
|
-
return [...
|
|
574
|
-
const
|
|
575
|
-
for (const a of
|
|
576
|
-
|
|
577
|
-
return
|
|
734
|
+
return [...r];
|
|
735
|
+
const i = [...r], o = this._groupChildrenMap[e] ?? [];
|
|
736
|
+
for (const a of o)
|
|
737
|
+
i.push(...this._getGroupFieldNamesWithVisited(a, !0, s));
|
|
738
|
+
return s.delete(e), Array.from(new Set(i));
|
|
578
739
|
}
|
|
579
740
|
/**
|
|
580
741
|
* @description 获取字段所属分组 key,支持数组/对象嵌套路径回溯到 schema 字段。
|
|
@@ -584,9 +745,9 @@ class ae extends P {
|
|
|
584
745
|
_getFieldGroupKey(e) {
|
|
585
746
|
const t = this._fieldToGroupKey[e];
|
|
586
747
|
if (t) return t;
|
|
587
|
-
for (const
|
|
588
|
-
if (this._matchFieldOrNested(e,
|
|
589
|
-
return this._fieldToGroupKey[
|
|
748
|
+
for (const s of Object.keys(this._fieldToGroupKey))
|
|
749
|
+
if (this._matchFieldOrNested(e, s))
|
|
750
|
+
return this._fieldToGroupKey[s];
|
|
590
751
|
}
|
|
591
752
|
/**
|
|
592
753
|
* @description 判断字段所属分组或祖先分组是否仍处于隐藏状态,用于避免字段显示时绕过仍隐藏的父级分组。
|
|
@@ -607,8 +768,8 @@ class ae extends P {
|
|
|
607
768
|
* @param {boolean} hidden 是否隐藏
|
|
608
769
|
*/
|
|
609
770
|
_setFieldsHidden(e, t) {
|
|
610
|
-
for (const
|
|
611
|
-
t ? this.hiddenFieldNames.add(
|
|
771
|
+
for (const s of e)
|
|
772
|
+
t ? this.hiddenFieldNames.add(s) : this._hasHiddenGroup(s) || this.hiddenFieldNames.delete(s);
|
|
612
773
|
}
|
|
613
774
|
/**
|
|
614
775
|
* @description 生成清空后的值:数组和对象保留结构,叶子值按默认值回填,否则置为 undefined
|
|
@@ -618,77 +779,67 @@ class ae extends P {
|
|
|
618
779
|
*/
|
|
619
780
|
_clearValueKeepStructure(e, t) {
|
|
620
781
|
if (Array.isArray(e)) {
|
|
621
|
-
const
|
|
622
|
-
return e.map((
|
|
782
|
+
const s = Array.isArray(t) ? t : [];
|
|
783
|
+
return e.map((r, i) => this._clearValueKeepStructure(r, s[i] ?? s[0]));
|
|
623
784
|
}
|
|
624
|
-
if (
|
|
625
|
-
const
|
|
785
|
+
if (S(e)) {
|
|
786
|
+
const s = S(t) ? t : {};
|
|
626
787
|
return Object.fromEntries(
|
|
627
|
-
Object.keys(e).map((
|
|
628
|
-
|
|
629
|
-
this._clearValueKeepStructure(
|
|
788
|
+
Object.keys(e).map((r) => [
|
|
789
|
+
r,
|
|
790
|
+
this._clearValueKeepStructure(N(e, r), N(s, r))
|
|
630
791
|
])
|
|
631
792
|
);
|
|
632
793
|
}
|
|
633
|
-
if (e === void 0 && (Array.isArray(t) ||
|
|
794
|
+
if (e === void 0 && (Array.isArray(t) || S(t)))
|
|
634
795
|
return this._clearValueKeepStructure(t, t);
|
|
635
|
-
if (t !== void 0 && !Array.isArray(t) && !
|
|
636
|
-
return
|
|
796
|
+
if (t !== void 0 && !Array.isArray(t) && !S(t))
|
|
797
|
+
return K(t);
|
|
637
798
|
}
|
|
638
799
|
/**
|
|
639
800
|
* @description 清空隐藏字段的值、校验状态和虚拟错误缓存,数组和对象保留结构
|
|
640
801
|
* @param {string[]} fieldNames 字段名列表
|
|
641
802
|
*/
|
|
642
803
|
_clearHiddenFieldValues(e) {
|
|
643
|
-
var t, r,
|
|
804
|
+
var t, s, r, i, o, a, n;
|
|
644
805
|
if (e.length)
|
|
645
|
-
for (const
|
|
646
|
-
const
|
|
647
|
-
(
|
|
806
|
+
for (const u of e) {
|
|
807
|
+
const c = N((t = this.form) == null ? void 0 : t.values, u), p = this._fieldSchemaMap[u] ? $(this._fieldSchemaMap[u]) : void 0, m = this._clearValueKeepStructure(c, p);
|
|
808
|
+
H(c, m) || (r = (s = this.form) == null ? void 0 : s.setFieldValue) == null || r.call(s, u, m, !1), (o = (i = this.form) == null ? void 0 : i.setFieldError) == null || o.call(i, u, void 0), (n = (a = this.form) == null ? void 0 : a.setFieldTouched) == null || n.call(a, u, !1);
|
|
648
809
|
}
|
|
649
810
|
}
|
|
650
811
|
/**
|
|
651
812
|
* @description 卸载分组表单时恢复初始schema
|
|
652
813
|
*/
|
|
653
814
|
get arrayAction() {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const r = () => {
|
|
658
|
-
var a;
|
|
659
|
-
if (!this.navAnchorProps.show) return;
|
|
660
|
-
const i = ((a = this.resetSignal) == null ? void 0 : a.value) ?? 0;
|
|
661
|
-
if (i !== t) {
|
|
662
|
-
t = i, this.syncNavErrorCountsFromForm();
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
this._hasValidated && this.syncNavErrorCountsFromForm();
|
|
666
|
-
}, s = (i) => y(i);
|
|
815
|
+
const e = super.arrayAction, t = () => {
|
|
816
|
+
this.navAnchorProps.show && this._hasValidated && this.syncNavErrorCountsFromForm();
|
|
817
|
+
}, s = (r) => _(r);
|
|
667
818
|
return {
|
|
668
|
-
get: (
|
|
669
|
-
push: (
|
|
670
|
-
e.push(s(
|
|
819
|
+
get: (r) => e.get(s(r)),
|
|
820
|
+
push: (r, i) => {
|
|
821
|
+
e.push(s(r), i), t();
|
|
671
822
|
},
|
|
672
|
-
remove: (
|
|
673
|
-
e.remove(s(
|
|
823
|
+
remove: (r, i) => {
|
|
824
|
+
e.remove(s(r), i), t();
|
|
674
825
|
},
|
|
675
|
-
replace: (
|
|
676
|
-
e.replace(s(
|
|
826
|
+
replace: (r, i) => {
|
|
827
|
+
e.replace(s(r), i), t();
|
|
677
828
|
},
|
|
678
|
-
update: (
|
|
679
|
-
e.update(s(
|
|
829
|
+
update: (r, i, o) => {
|
|
830
|
+
e.update(s(r), i, o), t();
|
|
680
831
|
},
|
|
681
|
-
insert: (
|
|
682
|
-
e.insert(s(
|
|
832
|
+
insert: (r, i, o) => {
|
|
833
|
+
e.insert(s(r), i, o), t();
|
|
683
834
|
},
|
|
684
|
-
prepend: (
|
|
685
|
-
e.prepend(s(
|
|
835
|
+
prepend: (r, i) => {
|
|
836
|
+
e.prepend(s(r), i), t();
|
|
686
837
|
},
|
|
687
|
-
move: (
|
|
688
|
-
e.move(s(
|
|
838
|
+
move: (r, i, o) => {
|
|
839
|
+
e.move(s(r), i, o), t();
|
|
689
840
|
},
|
|
690
|
-
swap: (
|
|
691
|
-
e.swap(s(
|
|
841
|
+
swap: (r, i, o) => {
|
|
842
|
+
e.swap(s(r), i, o), t();
|
|
692
843
|
},
|
|
693
844
|
/**
|
|
694
845
|
* @description 获取嵌套数组路径下的操作器
|
|
@@ -696,16 +847,16 @@ class ae extends P {
|
|
|
696
847
|
* @param index 父数组索引
|
|
697
848
|
* @param childField 子数组字段名
|
|
698
849
|
*/
|
|
699
|
-
at: (
|
|
700
|
-
const
|
|
701
|
-
return this._wrapNestedArrayHandle(
|
|
850
|
+
at: (r, i, o) => {
|
|
851
|
+
const a = e.at(I(s(r), i, o));
|
|
852
|
+
return this._wrapNestedArrayHandle(a, t);
|
|
702
853
|
}
|
|
703
854
|
};
|
|
704
855
|
}
|
|
705
856
|
unmount() {
|
|
706
|
-
this._applyGroupSchema(
|
|
857
|
+
this._applyGroupSchema(y(this._initialGroupSchema)), this.hiddenGroupKeys.clear(), this.hiddenFieldNames.clear(), this._clearAllFieldErrors(), this._hasValidated = !1, super.unmount();
|
|
707
858
|
}
|
|
708
859
|
}
|
|
709
860
|
export {
|
|
710
|
-
|
|
861
|
+
Se as GroupFormApi
|
|
711
862
|
};
|