@dazhicheng/ui 1.5.40 → 1.5.41

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.
@@ -50,10 +50,17 @@ export declare class FormApi {
50
50
  setFieldValue(field: string, value: any, shouldValidate?: boolean): Promise<void>;
51
51
  setLatestSubmissionValues(values: null | Recordable<any>): void;
52
52
  setState(stateOrFn: ((prev: TtFormProps) => Partial<TtFormProps>) | Partial<TtFormProps>): void;
53
+ /**
54
+ * 供子类覆盖:在 `setValues(fields, true)` 时用于白名单过滤的 fieldName 列表(默认取自 store 中的 `schema`)。
55
+ * @returns {string[]} 允许写入的字段名
56
+ */
57
+ protected getFieldNamesForSetValuesFilter(): string[];
53
58
  /**
54
59
  * 设置表单值
55
60
  * @param fields record
56
- * @param filterFields 过滤不在schema中定义的字段 默认为true
61
+ * @param filterFields 为 true 时仅处理当前 schema 中已声明的 fieldName,并保留与现有值的深度合并规则;为 false 时原样交给 vee-validate。
62
+ * 配置了 `fieldMappingTime` 时,会先执行拆解→区间合并,再按本参数过滤:只要**区间主字段**在 schema(与 {@link FormApi.getFieldNamesForSetValuesFilter} 一致)中,合并后的主字段会留在 `mergedInput` 里,**默认 true 即可**。
63
+ * 仅当合并未发生(例如主字段不在 schema、`format` 为函数跳过)且入参里只剩**未在 schema 声明**的拆解键时,才需要传 `false` 才能把拆解字段写入 vee-validate。
57
64
  * @param shouldValidate
58
65
  */
59
66
  setValues(fields: Record<string, any>, filterFields?: boolean, shouldValidate?: boolean): Promise<void>;
@@ -64,8 +71,46 @@ export declare class FormApi {
64
71
  validateAndSubmitForm(): Promise<Recordable<any> | undefined>;
65
72
  validateField(fieldName: string, opts?: Partial<ValidationOptions>): Promise<import('vee-validate').ValidationResult<any>>;
66
73
  private getForm;
74
+ /**
75
+ * 将拆解后的起止时间解析为区间组件可用的值(优先 dayjs,失败则保留原值)。
76
+ * @param {unknown} raw 接口或拆解字段的值
77
+ * @param {string} _format 与 fieldMappingTime 中 format 对应,预留与 formatDate 对齐
78
+ * @returns {unknown} 解析后的值
79
+ */
80
+ private parseSplitTimeForRange;
81
+ /**
82
+ * 判断主字段是否已为「合法」日期区间 [start, end](两项均可解析为有效日期)。合法则不再用拆解字段覆盖。
83
+ * @param {unknown} value 主字段值
84
+ * @returns {boolean} 是否为合法区间
85
+ */
86
+ private isValidRangeTimePair;
87
+ /**
88
+ * 与 {@link FormApi.handleRangeTimeValue} 相反:在 `fieldMappingTime` 配置下,将拆解起止拼成区间字段。
89
+ * - 仅当**区间主字段**在当前 schema({@link FormApi.getFieldNamesForSetValuesFilter})中有定义时处理。
90
+ * - 若主字段**已是合法日期区间**,不合并,保持原有值与行为。
91
+ * - 否则(未赋值、0、空数组、或非合法区间等)在拆解字段有有效数据时合并,并移除拆解键。
92
+ * @param {Record<string, any>} fields 调用方传入的 setValues 数据
93
+ * @returns {Record<string, any>} 合并后的浅拷贝对象
94
+ */
95
+ private mergeRangeTimeFieldsForSetValues;
96
+ /**
97
+ * 拆解字段是否为空(与「无有效起止」一致时不参与拼区间)。
98
+ * @param {unknown} value 拆解字段值
99
+ * @returns {boolean} 是否视为空
100
+ */
101
+ private isSplitTimeEmptyValue;
67
102
  private handleMultiFields;
68
103
  private handleRangeTimeValue;
69
104
  private processFields;
105
+ /**
106
+ * schema 变更后若存在从上一版 schema 中消失的 fieldName,会在 {@link FormApi.updateState} 中调用。
107
+ * 子类(如分组表单)可覆盖以同步清理虚拟校验缓存等。
108
+ * @param {string[]} removedFieldNames 已从 schema 中移除的字段名
109
+ */
110
+ protected onRemovedSchemaFields(_removedFieldNames: string[]): void;
111
+ /**
112
+ * store 更新后对比前后 `schema` 的 fieldName 集合,对已从 schema 中移除的字段清空 vee-validate 值与错误,
113
+ * 避免 `removeFields` / 删项后 `getValues` 仍带出旧字段。
114
+ */
70
115
  private updateState;
71
116
  }
@@ -0,0 +1,198 @@
1
+ import { FormState, GenericObject, ResetFormOpts, ValidationOptions } from 'vee-validate';
2
+ import { FormApi } from '../formApi';
3
+ import { TtFormProps } from '../types';
4
+ import { GroupFormApiOptions, GroupFormSchema } from './types';
5
+ import { InsertPosition } from './utils';
6
+ /**
7
+ * @description 分组表单 API 类,继承 {@link FormApi},封装分组场景下的校验、导航错误计数、
8
+ */
9
+ export declare class GroupFormApi extends FormApi {
10
+ /** 是否启用虚拟化(IntersectionObserver 懒渲染) */
11
+ readonly enableVirtual: boolean;
12
+ /** 是否显示悬浮导航锚点 */
13
+ readonly showNav: boolean;
14
+ /** 校验失败时是否自动滚动到第一个有错误的分组 */
15
+ readonly enableScrollToFirstError: boolean;
16
+ /** 各分组折叠状态(key → 是否折叠) */
17
+ readonly collapseStates: Record<string, boolean>;
18
+ /** 导航各分组的错误数量,validate 后自动更新 */
19
+ readonly navErrorCounts: Record<string, number>;
20
+ /** 共享错误缓存,validate 写入 → LazyFormField 挂载时读取恢复 */
21
+ readonly errorCacheMap: Map<string, string>;
22
+ /** 重置信号,递增时通知所有 LazyFormField 清空 cachedError */
23
+ readonly resetSignal: import('vue').Ref<number, number>;
24
+ /** 初始传入的分组 schema(用于 fallback) */
25
+ private readonly _initialGroupSchema;
26
+ /** 初始展平后的 flatSchema(用于 fallback) */
27
+ private readonly _initialFlatSchema;
28
+ /** 各分组 key → 其直属字段名(不含子分组的字段) */
29
+ private _groupFieldMap;
30
+ /** 是否已执行过 validate,用于控制导航栏错误同步是否需要重新校验未挂载字段 */
31
+ private _hasValidated;
32
+ /** VirtualGroupForm 组件实例引用,由 useGroupForm 的 h() 渲染时赋值 */
33
+ groupFormRef: import('vue').Ref<{
34
+ scrollToGroup?: ((key: string) => void) | undefined;
35
+ } | null, {
36
+ scrollToGroup?: (key: string) => void;
37
+ } | {
38
+ scrollToGroup?: ((key: string) => void) | undefined;
39
+ } | null>;
40
+ /**
41
+ * @param {TtFormProps} formProps - 底层表单配置(已展平的 schema、layout 等)
42
+ * @param {GroupFormApiOptions} groupOptions - 分组场景专属配置
43
+ */
44
+ constructor(formProps: TtFormProps, groupOptions: GroupFormApiOptions);
45
+ /**
46
+ * @description 分组表单以 `groupSchema` 展平结果为字段全集。仅依赖 store 中的 `schema` 时,若与 `groupSchema` 不同步(例如外部只 `setState` 了 `groupSchema`、或合并顺序导致 `schema` 滞后),`setValues` 默认过滤会误删合法字段;此处优先按 `groupSchema` 展平生成白名单。
47
+ * @returns {string[]} 允许 `setValues` 写入的 fieldName 列表
48
+ */
49
+ protected getFieldNamesForSetValuesFilter(): string[];
50
+ /**
51
+ * @description schema 中移除字段后清空虚拟列表场景下缓存的校验错误,与 {@link FormApi} 中清空 vee 值保持一致。
52
+ * @param {string[]} removedFieldNames 已移除的 fieldName 列表
53
+ */
54
+ protected onRemovedSchemaFields(removedFieldNames: string[]): void;
55
+ /**
56
+ * @description 校验表单(含未挂载的虚拟字段),支持通过第二个参数控制是否滚动到第一个错误分组
57
+ * @param {Partial<ValidationOptions>} [opts] - 原始 validate 选项
58
+ * @param {boolean} [scrollToError] - 是否滚动到第一个错误分组,不传时使用全局配置 enableScrollToFirstError
59
+ * @returns {Promise<{valid: boolean; errors: Record<string, string>; results?: any}>} 校验结果
60
+ * @example
61
+ * await formApi.validate();
62
+ * @example
63
+ * await formApi.validate(undefined, true);
64
+ * @example
65
+ * await formApi.validate(undefined, false);
66
+ */
67
+ validate(opts?: Partial<ValidationOptions>, scrollToError?: boolean): Promise<import('vee-validate').FormValidationResult<GenericObject, GenericObject>>;
68
+ /**
69
+ * @description 校验并提交表单
70
+ * @returns {Promise<any>} 提交结果,校验失败时返回 undefined
71
+ */
72
+ validateAndSubmitForm(): Promise<import('../../../../../../utils/src').Recordable<any> | undefined>;
73
+ /**
74
+ * @description 重置表单(含清空虚拟字段错误缓存和导航错误计数)
75
+ * @param {Partial<FormState<GenericObject>>} [state] - 重置后的表单状态
76
+ * @param {Partial<ResetFormOpts>} [opts] - 重置选项
77
+ */
78
+ resetForm(state?: Partial<FormState<GenericObject>>, opts?: Partial<ResetFormOpts>): Promise<void>;
79
+ /**
80
+ * @description 重置校验状态(含清空虚拟字段错误缓存和导航错误计数)
81
+ */
82
+ resetValidate(): Promise<void>;
83
+ /**
84
+ * @description 获取表单值
85
+ * @param {boolean} [isGroup] - 为 true 时返回按分组 key 嵌套的对象,默认 false 返回扁平值
86
+ * @returns {Promise<T>} 表单值
87
+ */
88
+ getValues<T = Record<string, any>>(isGroup?: boolean): Promise<T>;
89
+ /**
90
+ * @description 滚动到指定分组(外部可通过 formApi.scrollToGroup 调用)
91
+ * @param {string} key - 分组 key
92
+ */
93
+ scrollToGroup(key: string): void;
94
+ /**
95
+ * @description 切换分组折叠状态
96
+ * @param {string} key - 分组 key
97
+ */
98
+ toggleCollapse(key: string): void;
99
+ /**
100
+ * @description 按当前 store 中的 groupSchema(若无则用初始 schema)重置折叠状态:
101
+ * 先清空已有 key,再按 `defaultCollapsed` 写入默认值。
102
+ * 用于同一 `formApi` 实例下分组表单卸载后再次挂载(如弹框 `destroy-on-close`)时,避免沿用上次折叠状态。
103
+ */
104
+ resetCollapseStatesToDefault(): void;
105
+ /**
106
+ * @description 动态更新分组 schema(同时更新 UI 渲染和底层字段注册)
107
+ * @param {GroupFormSchema[]} newSchema - 新的分组 schema
108
+ */
109
+ updateGroupSchema(newSchema: GroupFormSchema[]): void;
110
+ /**
111
+ * @description 在指定分组中增量插入字段,无需全量重建 schema。
112
+ * 支持在 group 的 children 或 row 的 children 中定位插入
113
+ * @param {string} groupKey - 目标分组的 key
114
+ * @param {GroupFormSchema[]} fields - 要插入的字段或子结构(字段、slot、row 等均可)
115
+ * @param {InsertPosition} [position] - 插入位置,省略则追加到分组末尾
116
+ * @example
117
+ * formApi.appendFields('basic', [newField], { before: 'phone' });
118
+ * @example
119
+ * formApi.appendFields('basic', [newField], { after: 'email' });
120
+ * @example
121
+ * formApi.appendFields('basic', [newField]);
122
+ */
123
+ appendFields(groupKey: string, fields: GroupFormSchema[], position?: InsertPosition): void;
124
+ /**
125
+ * @description 按标识从 groupSchema 树中移除节点,无需全量重建 schema。
126
+ * 支持匹配 fieldName(普通字段 / row 内字段)和 slot 的字符串 content
127
+ * @param {string[]} fieldNames - 要移除的标识列表(fieldName 或 slot content 字符串)
128
+ * @example
129
+ * formApi.removeFields(['v_phone', 'v_email']);
130
+ */
131
+ removeFields(fieldNames: string[]): void;
132
+ /**
133
+ * @description 合并 vee-validate 实时 errors 与虚拟列表未挂载字段的 errorCacheMap,用于导航徽标与输入联动
134
+ * @returns {Record<string, string>} 合并后的错误映射
135
+ */
136
+ mergeErrorsForNav(): Record<string, string>;
137
+ /**
138
+ * @description 同步导航错误计数。
139
+ * 当开启虚拟列表且已执行过 validate 时,会重新对未挂载字段做可见性检查和校验,
140
+ * 确保 dependencies 条件变化后错误计数实时更新。
141
+ */
142
+ syncNavErrorCountsFromForm(): Promise<void>;
143
+ /**
144
+ * @description 清空导航各分组的错误计数
145
+ */
146
+ clearNavErrorCounts(): void;
147
+ /**
148
+ * @description 校验失败后展开包含错误的分组,并滚动到该分组标题
149
+ * @param {Record<string, string>} errors - 合并后的所有错误
150
+ */
151
+ scrollToFirstErrorGroup(errors: Record<string, string>): void;
152
+ /**
153
+ * @description 对单个字段值执行 rules 校验(纯数据,不挂载组件)。
154
+ * 支持字符串规则("required" / "selectRequired")和 Zod schema。
155
+ * @param {unknown} value - 字段值
156
+ * @param {FormSchemaRuleType | undefined} rules - 校验规则
157
+ * @param {string} fieldName - 字段名
158
+ * @returns {Promise<string | undefined>} 错误信息,无错误返回 undefined
159
+ */
160
+ private _validateFieldValue;
161
+ /**
162
+ * @description 检查字段的 dependencies 条件,判断字段在当前值下是否应该被校验。
163
+ * 与 FormField.vue 保持一致:visible = !hide && isIf && isShow
164
+ * @param {FormSchema} schema - 字段 schema
165
+ * @param {Record<string, any>} values - 当前表单值
166
+ * @returns {Promise<boolean>} 字段是否可见
167
+ */
168
+ private _isFieldVisible;
169
+ /**
170
+ * @description 对所有未挂载的字段做纯值校验,补充到 validate 结果中,
171
+ * 并将错误写入 errorCacheMap 以便 LazyFormField 挂载时恢复。
172
+ * 会跳过因 dependencies.if 条件不满足而不应渲染的字段,与原始表单校验行为一致。
173
+ * @param {object} validateResult - 父类 validate 返回的校验结果
174
+ * @returns {Promise<object>} 补充未挂载字段错误后的校验结果
175
+ */
176
+ private _validateUnmountedFields;
177
+ /**
178
+ * @description 清空所有缓存的错误并递增重置信号
179
+ */
180
+ private _clearAllCachedErrors;
181
+ /**
182
+ * @description 递归构建各分组 key → 其直属字段名的映射
183
+ * @param {GroupFormSchema[]} schemas - 分组 schema 数组
184
+ * @param {string} [parentKey] - 父分组 key
185
+ */
186
+ private _buildGroupFieldMap;
187
+ /**
188
+ * @description 根据错误映射更新导航各分组的错误计数
189
+ * @param {Record<string, string>} errors - 字段错误映射
190
+ */
191
+ private _updateNavErrorCounts;
192
+ /**
193
+ * @description 根据错误字段找到第一个包含错误的分组 key(按 schema 定义顺序)
194
+ * @param {Record<string, string>} errors - 错误字段映射
195
+ * @returns {string | undefined} 第一个有错误的分组 key
196
+ */
197
+ private _findFirstErrorGroupKey;
198
+ }
@@ -1,3 +1,4 @@
1
+ export { GroupFormApi } from './groupFormApi';
1
2
  export { useGroupForm } from './useGroupForm';
2
- export type { FormGroupSchema, FormRowSchema, GroupFormSchema, UseGroupFormOptions } from './types';
3
+ export type { FormGroupSchema, FormRowSchema, GroupFormApiOptions, GroupFormSchema, UseGroupFormOptions, } from './types';
3
4
  export { isGroupSchema, isRowSchema, isSlotSchema, extractFlatSchemas } from './utils';
@@ -7,8 +7,8 @@ import { InjectionKey, Ref } from 'vue';
7
7
  */
8
8
  export declare const VIRTUAL_ERROR_CACHE_KEY: InjectionKey<Map<string, string>>;
9
9
  /**
10
- * 重置信号(递增计数器),resetForm / resetValidate +1。
11
- * LazyFormField watch 此值来清空自身 cachedError
10
+ * 重置信号(递增计数器),resetForm / resetValidate 清空虚拟字段错误缓存时 +1。
11
+ * useGroupForm watch 此值以同步导航错误计数;LazyFormField 不依赖 inject 该 key
12
12
  */
13
13
  export declare const VIRTUAL_RESET_SIGNAL_KEY: InjectionKey<Ref<number>>;
14
14
  /**
@@ -145,3 +145,20 @@ export interface GroupSectionProps {
145
145
  virtualEstimateHeight?: number;
146
146
  }
147
147
  export type AutoScrollPosition = "top" | "bottom";
148
+ /**
149
+ * @description GroupFormApi 构造函数的分组配置选项
150
+ * @property {GroupFormSchema[]} groupSchema - 分组 schema 树形结构
151
+ * @property {boolean} [enableVirtual] - 是否启用虚拟化(IntersectionObserver 懒渲染),默认 true
152
+ * @property {boolean} [showNav] - 是否显示悬浮导航锚点,默认 true
153
+ * @property {boolean} [enableScrollToFirstError] - 校验失败时是否自动滚动到第一个有错误的分组,默认 true
154
+ */
155
+ export interface GroupFormApiOptions {
156
+ /** 分组 schema 树形结构 */
157
+ groupSchema: GroupFormSchema[];
158
+ /** 是否启用虚拟化(IntersectionObserver 懒渲染),默认 true */
159
+ enableVirtual?: boolean;
160
+ /** 是否显示悬浮导航锚点,默认 true */
161
+ showNav?: boolean;
162
+ /** 校验失败时是否自动滚动到第一个有错误的分组,默认 true */
163
+ enableScrollToFirstError?: boolean;
164
+ }
@@ -1,5 +1,15 @@
1
1
  import { ExtendedFormApi } from '../types';
2
2
  import { UseGroupFormOptions } from './types';
3
+ /**
4
+ * @description 创建分组表单:返回可渲染的包装组件、扩展后的表单 API,以及分组折叠状态对象。
5
+ * @param {UseGroupFormOptions} options - 分组 schema 与表单配置;`schema` 为树形结构,内部会展平后写入底层 store
6
+ * @returns {readonly [import("vue").DefineComponent, ExtendedFormApi, Record<string, boolean>]} 元组:`[GroupForm, formApi, collapseStates]`
7
+ * @example
8
+ * const [GroupForm, formApi] = useGroupForm({
9
+ * schema: [{ type: "group", key: "basic", title: "基础", children: [{ fieldName: "name", component: "ElInput", label: "姓名" }] }],
10
+ * layout: "horizontal",
11
+ * });
12
+ */
3
13
  export declare function useGroupForm(options: UseGroupFormOptions): readonly [import('vue').DefineComponent<{}, () => import('vue').VNode<import('vue').RendererNode, import('vue').RendererElement, {
4
14
  [key: string]: any;
5
15
  }>, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>, ExtendedFormApi, Record<string, boolean>];
@@ -11,6 +11,8 @@ export type TtModalFormProps = {
11
11
  infoApi?: (...arg: any[]) => Promise<any>;
12
12
  /** 详情接口前置钩子 */
13
13
  infoApiBefore?: (val: Recordable) => Promise<Recordable> | Recordable;
14
+ /** 详情接口后置钩子 */
15
+ infoApiAfter?: (val: Recordable) => Promise<Recordable> | Recordable;
14
16
  /** 保存接口前置钩子 */
15
17
  saveApiBefore?: (val: Recordable) => Promise<Recordable> | Recordable;
16
18
  /** 表单静态数据回显的key */