@schemx/vue 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -14
- package/dist/analyze.html +1 -1
- package/dist/components/FormItem/index.d.ts +12 -5
- package/dist/components/FormItem/index.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +8 -4
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/provideFieldContext.d.ts +41 -0
- package/dist/hooks/provideFieldContext.d.ts.map +1 -0
- package/dist/hooks/provideFormConfigContext.d.ts +59 -0
- package/dist/hooks/provideFormConfigContext.d.ts.map +1 -0
- package/dist/hooks/provideFormContext.d.ts +57 -0
- package/dist/hooks/provideFormContext.d.ts.map +1 -0
- package/dist/hooks/useDictionary.d.ts.map +1 -1
- package/dist/hooks/useField.d.ts +0 -31
- package/dist/hooks/useField.d.ts.map +1 -1
- package/dist/hooks/useForm.d.ts +29 -31
- package/dist/hooks/useForm.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +188 -148
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -8
- package/dist/hooks/useContext.d.ts +0 -52
- package/dist/hooks/useContext.d.ts.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/utils/rendererProvider.ts","../src/utils/rulesProvider.ts","../src/hooks/useForm.ts","../src/hooks/useField.ts","../src/hooks/useWatch.ts","../src/hooks/useEffect.ts","../src/hooks/useDictionary.ts","../src/hooks/useContext.ts","../src/utils/equal.ts","../src/hooks/useStableRef.ts","../src/hooks/useViewSchemas.ts","../src/utils/validation.ts","../src/utils/slot.ts","../src/components/FormGroup/index.tsx","../src/components/FormItem/index.tsx","../src/utils/helpers.ts","../src/form.vue","../src/form.ts","../src/hocs/withRemoteOptions.ts"],"sourcesContent":["/**\n * 全局渲染器注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * vant 等 UI 适配层通过 import 后调用 registerAll 注册渲染器,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rendererProvider\n */\n\nimport {\n createRendererRegistry,\n type RendererRegistryType,\n type SchemxRendererKey,\n} from \"@schemx/core\"\n\n/**\n * 全局渲染器注册实例\n *\n * @example\n * ```ts\n * // UI 适配层注册渲染器\n * import { rendererRegistry } from '@schemx/vue'\n * rendererRegistry.registerAll({ input: InputRenderer, ... })\n *\n * // useForm 内部自动使用\n * // props 传入的 rendererRegistry 优先级更高\n * ```\n */\nexport const rendererRegistry: RendererRegistryType<SchemxRendererKey> =\n createRendererRegistry<SchemxRendererKey>(\"input\")\n","/**\n * 全局校验规则注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * UI 适配层通过 import 后调用 register/registerAll 注册自定义规则,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rulesProvider\n */\n\nimport { createValidatorsRegistry, type ValidatorsRegistryType } from \"@schemx/core\"\n\n/**\n * 全局校验规则注册实例\n *\n * @example\n * ```ts\n * // 注册自定义规则\n * import { validatorRegistry } from '@schemx/vue'\n * validatorRegistry.register('phone', phoneRule)\n *\n * // useForm 内部自动使用\n * // props 传入的 validatorRegistry 优先级更高\n * ```\n */\nexport const validatorRegistry: ValidatorsRegistryType = createValidatorsRegistry()\n","/**\n * SchemxInstance - 表单实例核心类\n *\n * 组合 Store、Validator,提供统一的表单操作接口。\n * 实现 SchemxInstance 接口,作为 useForm 的底层实现。\n *\n * @module hooks/useForm\n *\n * @example\n * ```typescript\n * import { useForm } from './useForm'\n *\n * // 在 Vue 组件中使用\n * const form = useForm({\n * schemas: [...],\n * initialValues: { name: '', age: 0 },\n * onFinish: (values) => console.log(values),\n * })\n *\n * // 或直接创建实例(非组件场景)\n * const instance = createForm({ initialValues: { name: '' } })\n * ```\n */\nimport { computed, inject, onUnmounted, provide } from \"vue\"\n\nimport { createForm } from \"@schemx/core\"\n\nimport { rendererRegistry as globalRendererRegistry } from \"../utils/rendererProvider\"\nimport { validatorRegistry as globalRulesRegistry } from \"../utils/rulesProvider\"\n\nimport type { CreateFormOptions, NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/** SchemxInstance 在 Vue provide/inject 中的注入 key */\nexport const SCHEMX_INSTANCE_KEY = Symbol(\"SCHEMX_INSTANCE\")\n\n/** @deprecated 请使用 SCHEMX_INSTANCE_KEY。保留给旧测试和旧适配代码兼容。 */\nexport const FORM_INSTANCE_KEY = SCHEMX_INSTANCE_KEY\n\n/**\n * useForm 配置选项\n *\n * 扩展 core 层的 CreateFormOptions,增加 Vue 层特有的 request 配置。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface UseFormOptions<TValues extends Values> extends CreateFormOptions<\n TValues,\n NamePath<TValues>\n> {}\n\n/**\n * 表单组合式函数\n *\n * 创建 SchemxInstance 并通过 Vue provide 注入到子组件树中,\n * 组件卸载时自动销毁实例。\n *\n * @typeParam TValues - 表单值类型\n *\n * @param options - 表单配置选项\n * @returns 表单实例(SchemxInstance 接口)\n *\n * @example\n * ```typescript\n * // 在 setup 中使用\n * const form = useForm({\n * initialValues: { name: '', email: '' },\n * onFinish: async (values) => {\n * await api.submit(values)\n * },\n * })\n *\n * form.setFieldValue('name', 'John')\n * ```\n */\nexport function useForm<TValues extends Values = Values>(\n options: UseFormOptions<TValues> = {}\n): SchemxInstance<TValues> {\n const { ...formOptions } = options\n\n const mergedOptions: CreateFormOptions<TValues> = {\n ...formOptions,\n rendererRegistry: formOptions.rendererRegistry ?? globalRendererRegistry,\n validatorRegistry: formOptions.validatorRegistry ?? globalRulesRegistry,\n }\n\n const instance = computed(() => createForm<TValues>(mergedOptions))\n\n provide<SchemxInstance<TValues>>(SCHEMX_INSTANCE_KEY, instance.value)\n\n onUnmounted(() => {\n instance.value.destroy()\n })\n\n return instance.value\n}\n\n/**\n * 获取表单实例\n *\n * 在子组件中获取 useForm 创建的 SchemxInstance,\n * 可用于读写字段值、校验、订阅等操作。\n *\n * @typeParam TValues - 表单值类型\n * @returns 表单实例\n *\n * @throws Error 如果不在 schemx 提供的上下文中调用\n *\n * @example\n * ```ts\n * const form = useFormContext()\n * form.setFieldValue('name', 'hello')\n * ```\n */\nexport function useFormContext<\n TValues extends Values = Values,\n>(): SchemxInstance<TValues> {\n const instance = inject<SchemxInstance<TValues>>(SCHEMX_INSTANCE_KEY)\n\n if (!instance) {\n throw new Error(\"useFormContext must be used within a Form\")\n }\n\n return instance\n}\n","/**\n * useField - 字段控制 Hook\n *\n * 基于 core 层的 {@link createField} 实现,\n * 通过 subscribe 回调将 Signal 变化桥接到 Vue 响应式系统。\n *\n * @module hooks/useField\n *\n * @remarks\n * core 层的 createField 封装了所有框架无关的字段操作逻辑,\n * useField 只负责:\n * 1. 从 Vue context 中获取 form 实例\n * 2. 创建 shallowRef 容器\n * 3. 通过 subscribe 将 Signal 变化同步到 shallowRef\n * 4. 包装 computed 只读状态\n * 5. 管理 onUnmounted 生命周期清理\n */\n\nimport { computed, inject, InjectionKey, onUnmounted, provide, shallowRef } from \"vue\"\n\nimport { createField } from \"@schemx/core\"\n\nimport { FieldInstance } from \"../types/field\"\n\nimport { useFormContext } from \"./useForm\"\n\nimport type { NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/** 字段上下文注入 key */\nconst FIELD_CONTEXT_KEY: InjectionKey<FieldInstance> = Symbol(\"schemx:field\")\n\n/**\n * useField 针对 (form, name) 的缓存条目\n *\n * 通过引用计数确保同一 (form, name) 的多次 useField 调用\n * 共享同一份 shallowRef 和 effect 桥接,避免重复订阅。\n *\n * @typeParam TValues - 表单值类型\n */\ninterface FieldHookCacheEntry<TValues extends Values = Values> {\n /** 当前活跃的引用计数,归零时释放 dispose 并从缓存中移除 */\n refCount: number\n /** 共享的字段控制器实例(含 Vue Ref 包装) */\n result: FieldInstance<TValues>\n /** 取消 Signal effect 订阅并清理资源 */\n dispose: () => void\n}\n\n/** 模块级缓存:form 实例 → 字段名 → 缓存条目 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst fieldHookCache = new WeakMap<any, Map<string, FieldHookCacheEntry>>()\n\n/**\n * 创建字段 hook 的响应式状态\n *\n * 从 createField 生成 FieldInstance,创建 shallowRef 容器\n * 并通过 field.effect 将 Signal 变化桥接到 Vue 响应式系统。\n */\nfunction createFieldHook<TValues extends Values = Values>(\n form: SchemxInstance<TValues>,\n name: NamePath<TValues>\n) {\n const field = createField<TValues>(form, name)\n\n const fieldValue = shallowRef(field.getValue())\n const fieldError = shallowRef(field.getError())\n const fieldPending = shallowRef(field.isPending())\n\n const dispose = field.effect(() => {\n fieldValue.value = field.getValue()\n fieldError.value = field.getError()\n\n fieldPending.value = field.isPending()\n })\n\n const error = computed(() => fieldError.value)\n\n const dirty = computed(() => {\n void fieldValue.value\n\n return field.isTouched()\n })\n\n const pending = computed(() => fieldPending.value)\n\n const result = {\n value: fieldValue,\n error,\n dirty,\n pending,\n ...field,\n getValue: () => fieldValue.value,\n }\n\n return { result, dispose }\n}\n\n/**\n * 获取单个字段的控制能力\n *\n * 通过 core 层 createField 提供字段操作方法,\n * 通过 subscribe 回调桥接 Signal 变化到 Vue shallowRef。\n *\n * @param name - 字段名(支持嵌套路径,如 'user.address.city')\n * @returns 字段状态和操作方法\n *\n * @example\n * ```typescript\n * const field = useField('username')\n *\n * // 响应式值(在 computed/watchEffect/template 中自动追踪)\n * field.getValue()\n *\n * // 响应式错误\n * field.error.value // string[] | undefined\n *\n * // 响应式脏状态\n * field.dirty.value // boolean\n *\n * // 响应式操作中状态\n * field.pending.value // boolean\n *\n * // 写入值\n * field.setValue('new value')\n *\n * // 校验\n * const result = await field.validate()\n * ```\n */\nexport const useField = <TValues extends Values = Values>(\n name: NamePath<TValues>\n): FieldInstance<TValues> => {\n const form = useFormContext<TValues>()\n\n const key = name\n\n let formCache = fieldHookCache.get(form)\n\n if (!formCache) {\n formCache = new Map()\n fieldHookCache.set(form, formCache)\n }\n\n const cachedEntry = formCache.get(key) as FieldHookCacheEntry<TValues> | undefined\n\n let activeEntry: FieldHookCacheEntry<TValues>\n\n if (cachedEntry) {\n cachedEntry.refCount++\n activeEntry = cachedEntry\n } else {\n const { result, dispose } = createFieldHook<TValues>(form, name)\n\n activeEntry = {\n refCount: 1,\n result,\n dispose,\n }\n\n formCache.set(key, activeEntry as any)\n }\n\n onUnmounted(() => {\n if (--activeEntry.refCount <= 0) {\n activeEntry.dispose()\n formCache.delete(key)\n }\n })\n\n return activeEntry.result\n}\n\n/**\n * 向子组件树注入当前字段上下文。\n *\n * 应在 FormItem(或任何创建了 useField 的组件)的 setup 中调用。\n *\n * @param field - useField 返回的字段实例\n *\n * @example\n * ```typescript\n * // FormItem 中\n * const field = useField('date')\n * createFieldContext(field)\n *\n * ```\n */\nexport function createFieldContext<TValues extends Values = Values>(\n field: FieldInstance<TValues>\n): void {\n provide(FIELD_CONTEXT_KEY, field as FieldInstance)\n}\n\n/**\n * 从最近的 FormItem 获取当前字段上下文。\n *\n * 必须在 FormItem 的组件子树内调用,否则抛出错误。\n *\n * @returns useField 返回的字段实例\n *\n * @throws 当不在 FormItem 子树内时抛出\n *\n * @example\n * const field = useFieldContext()\n * field.error.value // string[] | undefined\n * field.getValue() // 当前字段值(Vue 响应式)\n */\nexport function useFieldContext(): ReturnType<typeof useField> {\n const field = inject(FIELD_CONTEXT_KEY)\n\n if (!field) {\n throw new Error(\"[schemx] useFieldContext() must be used inside a FormItem tree\")\n }\n\n return field\n}\n\nexport default useField\n","/**\n * useWatch - 监听字段变化(Vue 组合式 API 版本)\n *\n * 基于 core 层的 {@link createWatch} 实现,\n * 自动通过 `useFormContext` 获取 formContext,\n * 并在 `onUnmounted` 时自动取消监听,无需手动管理生命周期。\n *\n * 提供三种监听模式:\n * - 全局监听:`useWatch(callback, options?)` — 监听表单中任意字段变化\n * - 单字段监听:`useWatch(name, callback, options?)` — 监听指定字段变化\n * - 多字段监听:`useWatch(names, callback, options?)` — 监听一组字段变化\n *\n * 同时提供语义化的便捷方法:\n * - {@link useWatchField} — 单字段监听\n * - {@link useWatchFields} — 多字段监听\n * - {@link useWatchAll} — 全局监听\n *\n * @module hooks/useWatch\n *\n * @example\n * ```ts\n * // 全局监听\n * useWatch((snapshot) => {\n * console.log('form changed:', snapshot)\n * })\n *\n * // 单字段监听\n * useWatch('username', (current, prev) => {\n * console.log('username changed:', prev, '->', current)\n * }, { immediate: true })\n *\n * // 多字段监听\n * useWatch(['firstName', 'lastName'], (currentValues, prevValues) => {\n * console.log('name changed:', currentValues)\n * }, { inequality: true })\n * ```\n */\n\nimport { onUnmounted } from \"vue\"\n\nimport { createWatch } from \"@schemx/core\"\n\nimport { useFormContext } from \"./useForm\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\nimport type {\n CreateWatchOptions,\n WatchAllCallback,\n WatchFieldCallback,\n WatchFieldsCallback,\n} from \"@schemx/core\"\n\n/**\n * 监听所有字段变化\n *\n * @param callback - 全局变化回调\n * @param options - 监听选项\n * @returns 取消监听函数\n */\nexport function useWatch<T extends Values>(\n callback: WatchAllCallback<T>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听单个字段变化\n *\n * @param name - 字段路径\n * @param callback - 单字段变化回调\n * @param options - 监听选项\n * @returns 取消监听函数\n */\nexport function useWatch<T extends Values>(\n name: NamePath<T>,\n callback: WatchFieldCallback<T>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听多个字段变化\n *\n * @param names - 字段路径数组\n * @param callback - 多字段变化回调\n * @param options - 监听选项\n * @returns 取消监听函数\n */\nexport function useWatch<T extends Values>(\n names: NamePath<T>[],\n callback: WatchFieldsCallback<T>,\n options?: CreateWatchOptions\n): () => void\n/**\n * useWatch 实现 — 获取 form 实例后委托给 core 层的 createWatch\n */\nexport function useWatch<T extends Values>(\n nameOrNamesOrCallback: NamePath<T> | NamePath<T>[] | WatchAllCallback<T>,\n callbackOrOptions?: WatchFieldCallback<T> | WatchFieldsCallback<T> | CreateWatchOptions,\n maybeOptions?: CreateWatchOptions\n): () => void {\n const form = useFormContext<T>()\n\n const dispose = (createWatch as any)(\n form,\n nameOrNamesOrCallback,\n callbackOrOptions,\n maybeOptions\n )\n\n onUnmounted(dispose)\n\n return dispose\n}\n\n/**\n * 监听单个字段变化的便捷方法\n *\n * 等价于 `useWatch(name, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @param name - 要监听的字段路径\n * @param callback - 字段变化回调\n * @param options - 可选配置\n * @returns 取消监听函数\n */\nexport function useWatchField<T extends Values = Values>(\n name: NamePath<T>,\n callback: WatchFieldCallback<T>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<T>(name, callback, options)\n}\n\n/**\n * 监听多个字段变化的便捷方法\n *\n * 等价于 `useWatch(names, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @param names - 要监听的字段路径数组\n * @param callback - 字段变化回调\n * @param options - 可选配置\n * @returns 取消监听函数\n */\nexport function useWatchFields<T extends Values>(\n names: NamePath<T>[],\n callback: WatchFieldsCallback<T>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<T>(names, callback, options)\n}\n\n/**\n * 监听所有字段变化的便捷方法\n *\n * 等价于 `useWatch(callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @param callback - 全局变化回调\n * @param options - 可选配置\n * @returns 取消监听函数\n */\nexport function useWatchAll<T extends Values = Values>(\n callback: WatchAllCallback<T>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch(callback, options)\n}\n\nexport default useWatch\n","/**\n * useEffect - Vue 组合式 API 版本的 Signal effect\n *\n * 封装 `@schemx/core` 的 `createEffect`,在组件卸载时自动 dispose,\n * 无需手动管理生命周期。\n *\n * @module hooks/useEffect\n *\n * @example\n * ```ts\n * import { useEffect } from '@schemx/vue'\n *\n * // 在 setup 中使用\n * useEffect(() => {\n * const name = form.getFieldValue('name')\n * console.log('name:', name)\n *\n * return () => {\n * // 清理逻辑\n * }\n * })\n * ```\n */\n\nimport { onUnmounted } from \"vue\"\n\nimport { createEffect } from \"@schemx/core\"\n\nimport type { CreateEffectReturn, EffectCallback } from \"@schemx/core\"\n\n/**\n * 创建 Signal effect 并在组件卸载时自动取消。\n *\n * 在 effect 回调中访问任何 Signal 值时自动追踪依赖,\n * 依赖变化时自动重新执行回调。\n * 回调可返回一个清理函数,在 effect 重新执行前或被 dispose 时调用。\n *\n * @param callback - effect 回调函数,可选返回清理函数\n *\n * @returns 取消 effect 的 dispose 函数(通常无需手动调用,组件卸载时自动取消)\n *\n * @example\n * ```ts\n * // 监听字段值变化\n * useEffect(() => {\n * const name = form.getFieldValue('name')\n * console.log('name changed:', name)\n * })\n *\n * // 带清理函数\n * useEffect(() => {\n * const timer = setInterval(() => {\n * console.log('value:', form.getFieldValue('count'))\n * }, 1000)\n *\n * return () => clearInterval(timer)\n * })\n * ```\n */\nexport function useEffect(callback: EffectCallback): CreateEffectReturn {\n const dispose = createEffect(callback)\n\n onUnmounted(dispose)\n\n return dispose\n}\n\nexport default useEffect\n","/**\n * useDictionary - 字典选项 Hook\n *\n * 通过 api 函数加载字典选项,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持通过 dependsOn 声明依赖字段,\n * 当依赖字段变化时自动重新请求。\n *\n * @module hooks/useDictionary\n */\n\nimport { onMounted, Ref, ref } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useFormContext } from \"./useForm\"\nimport { useWatchFields } from \"./useWatch\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\n\n\nexport type { SchemxDictionary, SchemxWithDictionary } from \"@/types/dictionary\"\n\n/**\n * useDictionary 返回值\n */\nexport interface UseDictionaryReturn {\n /** 远程加载的字典选项列表(响应式) */\n list: Ref<any[]>\n /** 请求加载状态(响应式) */\n loading: Ref<boolean>\n /** 请求错误信息(响应式) */\n error: Ref<Error | undefined>\n /** 触发字典选项加载 */\n loadDict: () => Promise<void>\n /** 使用当前配置重新执行 api */\n refresh: () => Promise<void>\n /** 直接修改 list 的值,不触发 api 调用 */\n mutate: (data: any[]) => void\n}\n\n/**\n * @deprecated\n *\n * 已弃用,请使用 **UseDictionaryReturn**\n */\nexport type UseDictOptionsReturn = UseDictionaryReturn\n\n/**\n * 将未知抛出值规范化为 `Error` 实例\n *\n * @param err - 捕获的值(可能是任意类型)\n * @returns 包装原始值的 `Error` 对象\n */\nexport function normalizeError(err: unknown): Error {\n if (err instanceof Error) return err\n\n return new Error(String(err))\n}\n\n/**\n * 加载字典选项\n *\n * 通过 api 函数获取数据,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持依赖字段联动、竞态控制、\n * 重试、错误处理等能力。\n *\n * @typeParam TValues - 表单值类型\n * @param options - 字典配置选项\n * @param fieldName - 当前字段名,用于 resetOnDepsChange 时清空字段值\n * @returns 响应式选项列表、loading/error 状态及控制方法\n *\n * @example\n * ```ts\n * // 基础用法\n * const { list } = useDictionary({\n * api: async () => {\n * const res = await fetch('/api/options')\n * return res.json()\n * },\n * })\n *\n * // 依赖联动(带泛型)\n * const { list } = useDictionary<MyFormValues>({\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * }, 'city')\n * ```\n */\nexport const useDictionary = <\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n>(\n options: SchemxDictionary<TValues>,\n fieldName?: TName\n): UseDictionaryReturn => {\n const instance = useFormContext<TValues>()\n\n const list = ref<any[]>([])\n const loading = ref<boolean>(false)\n const error = ref<Error | undefined>(undefined)\n\n // 竞态控制:仅最新请求写入状态\n let requestCount = 0\n\n /**\n * 使用配置的 formatter 格式化原始响应数据\n */\n const format = async (res: any): Promise<any> => {\n if (typeof options?.formatter === \"function\") {\n return await options.formatter(res, instance)\n }\n\n return res\n }\n\n /**\n * 带重试的执行\n */\n const executeWithRetry = async (formValues: TValues): Promise<any> => {\n const maxRetries = options.retryCount ?? 0\n const retryDelay = options.retryInterval ?? 1000\n let lastError: Error = new Error(\"Unknown error\")\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await options.api(formValues, instance)\n } catch (err) {\n lastError = normalizeError(err)\n\n if (attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, retryDelay))\n }\n }\n }\n\n throw lastError\n }\n\n /**\n * 执行 api 函数加载字典选项\n */\n const loadDict = async (): Promise<void> => {\n try {\n const formValues = instance.getFieldsValue()\n\n // shouldFetch 检查\n if (typeof options.shouldFetch === \"function\" && !options.shouldFetch(formValues)) {\n list.value = []\n loading.value = false\n\n return\n }\n\n loading.value = true\n error.value = undefined\n\n // 递增请求计数器用于竞态控制\n const currentCount = ++requestCount\n\n const res = await executeWithRetry(formValues)\n\n // 竞态检查:丢弃过期响应\n if (currentCount !== requestCount) return\n\n const formatted = await format(res)\n\n // 格式化后再次竞态检查(格式化可能是异步的)\n if (currentCount !== requestCount) return\n\n list.value = formatted\n error.value = undefined\n\n // onSuccess 回调\n if (typeof options.onSuccess === \"function\") {\n options.onSuccess(formatted, instance)\n }\n\n loading.value = false\n } catch (err) {\n const normalized = normalizeError(err)\n\n error.value = normalized\n list.value = []\n loading.value = false\n\n if (typeof options.onError === \"function\") {\n options.onError(normalized, instance)\n }\n }\n }\n\n const refresh = (): Promise<void> => loadDict()\n\n const mutate = (data: any[]): void => {\n list.value = data\n }\n\n // ========== 依赖字段监听 ==========\n if (options.dependsOn?.length) {\n useWatchFields(options.dependsOn, (_payload, latestSnapshot) => {\n // 1. 触发 onDepsChange 回调\n if (typeof options.onDepsChange === \"function\") {\n options.onDepsChange(latestSnapshot as TValues, instance)\n }\n\n // 2. 清空当前字段值\n if (options.resetOnDepsChange && fieldName) {\n instance.setFieldValue(fieldName, undefined)\n }\n\n // 3. 重新执行 api\n void loadDict()\n })\n }\n\n // ========== 挂载行为 ==========\n const immediate = options.immediate ?? true\n\n onMounted(() => {\n if (immediate) {\n void loadDict()\n }\n })\n\n return { list, loading, error, loadDict, refresh, mutate }\n}\n\nexport default useDictionary\n","/**\n * useConfigContext - 表单上下文 Hook\n *\n * 提供表单上下文的创建与消费能力。\n * schemx 通过 createConfigContext 注入全局配置,\n * 子组件通过 useConfigContext 获取。\n *\n * @module hooks/useContext\n */\n\nimport { inject, provide } from \"vue\"\n\nimport type { SchemxFormProps } from \"../types\"\nimport type { Values } from \"@schemx/core\"\n\n/** FormContext 注入 key */\nexport const FORM_CONTEXT_KEY = Symbol(\"FormContext\")\n\n/**\n * 表单上下文属性\n *\n * 由 schemx 组件注入,包含表单级别的全局配置。\n *\n * @typeParam T - 表单值类型\n */\nexport interface FormContextProps<T extends Values = Values> extends Omit<\n SchemxFormProps<T>,\n | \"form\"\n | \"modelValue\"\n | \"rendererRegistry\"\n | \"defaultRendererType\"\n | \"rulesRegistery\"\n | \"onFinish\"\n | \"onFinishFailed\"\n | \"onValuesChange\"\n | \"onFieldsChange\"\n> {}\n\n/**\n * 创建并注入表单上下文\n *\n * 在 schemx 的 setup 中调用,将表单级别配置注入到子组件树中。\n *\n * @typeParam T - 表单值类型\n * @param props - 表单上下文属性\n */\nexport const createConfigContext = <T extends Values = Values>(\n props: FormContextProps<T>\n) => {\n provide<FormContextProps<T>>(FORM_CONTEXT_KEY, props)\n}\n\n/**\n * @deprecated\n *\n * 已弃用,请使用 **createConfigContext**\n */\nexport const createContext = createConfigContext\n\n/**\n * 获取表单上下文配置\n *\n * 在子组件中获取 schemx 注入的全局配置(readonly、disabled、labelAlign 等)。\n *\n * @returns 表单上下文属性\n *\n * @throws Error 如果不在 schemx 提供的上下文中调用\n *\n * @example\n * ```ts\n * const context = useConfigContext()\n * console.log(context.readonly, context.disabled)\n * ```\n */\nexport function useConfigContext(): FormContextProps {\n const context = inject<FormContextProps>(FORM_CONTEXT_KEY)\n\n if (!context) {\n throw new Error(\"useConfigContext must be used within a Form\")\n }\n\n return context\n}\n\n/**\n * @deprecated\n *\n * 已弃用,请使用 **useConfigContext**\n */\nexport const useContext = useConfigContext\n","/**\n * 对象浅比较工具\n *\n * @module utils/equal\n */\n\n/**\n * 浅比较两个对象的自有属性值是否相同。\n *\n * 用于避免 computed / shallowRef 每次返回新对象引用导致子组件不必要的更新。\n * 仅比较第一层属性,函数引用通过 === 判断。\n *\n * @param a - 旧对象\n * @param b - 新对象\n *\n * @returns 两个对象浅相等时返回 true\n *\n * @example\n * shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 }) // => true\n * shallowEqual({ a: 1 }, { a: 1, b: 2 }) // => false\n */\nexport const isShallowEqual = (\n a: Record<string, any>,\n b: Record<string, any>\n): boolean => {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n\n return keysA.every((key) => a[key] === b[key])\n}\n","/**\n * useStableRef - 引用稳定化的 shallowRef\n *\n * 封装 `shallowRef` + `watchEffect` + `isShallowEqual`,\n * 仅在工厂函数返回值与当前值浅不相等时才更新引用,\n * 避免下游组件因对象引用变化而不必要地重渲染。\n *\n * @module hooks/useStableRef\n *\n * @example\n * ```ts\n * import { useStableRef } from '@schemx/vue'\n *\n * const stableProps = useStableRef(() => ({\n * visible: visible.value,\n * disabled: disabled.value,\n * value: field.getValue(),\n * }))\n * ```\n */\n\nimport { type ShallowRef, shallowRef, watchEffect } from \"vue\"\n\nimport { isShallowEqual } from \"../utils/equal\"\n\n/**\n * 创建一个引用稳定的 shallowRef。\n *\n * 在 watchEffect 中执行工厂函数,自动追踪响应式依赖。\n * 仅当新旧值浅比较不相等时才替换 `.value`,保持引用稳定。\n *\n * @typeParam T - 对象类型\n *\n * @param factory - 返回目标对象的工厂函数,在 watchEffect 内执行\n *\n * @returns 只读的 ShallowRef,引用仅在属性值真正变化时更新\n *\n * @example\n * ```ts\n * const props = useStableRef(() => ({\n * value: field.getValue(),\n * onChange: handleChange,\n * }))\n *\n * // props.value 的引用在属性值未变时保持不变\n * h(Component, props.value)\n * ```\n */\nexport function useStableRef<T extends Record<string, any>>(\n factory: () => T\n): Readonly<ShallowRef<T>> {\n const stableRef = shallowRef({} as T) as ShallowRef<T>\n\n watchEffect(() => {\n const next = factory()\n\n if (!isShallowEqual(stableRef.value, next)) {\n stableRef.value = next\n }\n })\n\n return stableRef\n}\n\nexport default useStableRef\n","/**\n * useViewSchemas - Vue ViewSchemas bridge.\n *\n * @module hooks/useViewSchemas\n */\n\nimport { onScopeDispose, shallowRef } from \"vue\"\nimport type { ShallowRef } from \"vue\"\n\nimport type { SchemxInstance, SchemxViewSchema, Values } from \"@schemx/core\"\n\nexport function useViewSchemas<T extends Values = Values>(\n form: SchemxInstance<T>\n): ShallowRef<readonly SchemxViewSchema<T>[]> {\n const viewSchemas = shallowRef<readonly SchemxViewSchema<T>[]>(form.getViewSchemas())\n\n const unsubscribe = form.subscribeViewSchemas((nextSchemas) => {\n viewSchemas.value = nextSchemas\n })\n\n onScopeDispose(unsubscribe)\n\n return viewSchemas\n}\n\nexport default useViewSchemas\n","/**\n * 校验触发工具\n *\n * 提供校验触发时机的合并、归一化和匹配判断功能。\n *\n * @module utils/validation\n */\n\nimport type { ValidationTrigger } from \"@schemx/core\"\n\n/** 触发时机类型(支持单个或数组) */\nexport type TriggerConfig = ValidationTrigger | ValidationTrigger[]\n\n/** 归一化后的触发类型 */\nexport type NormalizedTrigger = \"blur\" | \"change\" | \"submit\"\n\n/**\n * 判断触发时机配置是否有效。\n *\n * undefined 和空数组视为无效,其余值(包括单个字符串、非空数组)视为有效。\n *\n * @param v - 触发时机配置\n *\n * @returns 配置有效时返回 true\n */\nfunction isValidTrigger(v: TriggerConfig | undefined): v is TriggerConfig {\n if (v === undefined) return false\n if (Array.isArray(v) && v.length === 0) return false\n\n return true\n}\n\n/**\n * 合并校验触发时机配置。\n *\n * 按优先级从高到低取值:列级配置 > 表单上下文配置 > 默认值。\n * 跳过 undefined 和空数组,确保空数组不会意外覆盖后续配置。\n *\n * @param columnTrigger - 列级配置的触发时机\n * @param contextTrigger - 表单上下文配置的触发时机\n * @param defaultTrigger - 兜底默认值\n *\n * @returns 最终生效的触发时机\n *\n * @example\n * mergeTrigger(\"onBlur\", \"onChange\", \"onSubmit\") // => \"onBlur\"\n * mergeTrigger(undefined, \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger([], \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger(undefined, undefined, \"onSubmit\") // => \"onSubmit\"\n */\nexport function mergeTrigger(\n columnTrigger: TriggerConfig | undefined,\n contextTrigger: TriggerConfig | undefined,\n defaultTrigger: TriggerConfig\n): TriggerConfig {\n if (isValidTrigger(columnTrigger)) return columnTrigger\n\n if (isValidTrigger(contextTrigger)) return contextTrigger\n\n return defaultTrigger\n}\n\n/**\n * 归一化触发类型字符串。\n *\n * 将带 `on` 前缀的格式统一为短格式:\n * `\"onBlur\"` → `\"blur\"`、`\"onChange\"` → `\"change\"`、`\"onSubmit\"` → `\"submit\"`。\n *\n * @param t - 原始触发类型\n *\n * @returns 归一化后的触发类型\n */\nfunction normalizeTrigger(t: ValidationTrigger): NormalizedTrigger {\n const map: Record<ValidationTrigger, NormalizedTrigger> = {\n onBlur: \"blur\",\n onChange: \"change\",\n onSubmit: \"submit\",\n blur: \"blur\",\n change: \"change\",\n submit: \"submit\",\n }\n\n return map[t] ?? \"submit\"\n}\n\n/**\n * 判断当前事件是否应该触发校验。\n *\n * 将配置的触发时机归一化后,与当前事件类型进行匹配。\n *\n * @param event - 当前触发的事件类型\n * @param trigger - 配置的触发时机(支持单个或数组)\n *\n * @returns 是否应该触发校验\n *\n * @example\n * shouldValidateOn(\"change\", \"onChange\") // => true\n * shouldValidateOn(\"blur\", [\"onBlur\", \"onChange\"]) // => true\n * shouldValidateOn(\"change\", \"onSubmit\") // => false\n * shouldValidateOn(\"change\", undefined) // => false\n */\nexport function shouldValidateOn(\n event: NormalizedTrigger,\n trigger?: TriggerConfig\n): boolean {\n if (!trigger) return false\n const triggers = Array.isArray(trigger) ? trigger : [trigger]\n\n return triggers.some((t) => normalizeTrigger(t) === event)\n}\n","/**\n * 插槽解析与子插槽提取工具。\n *\n * 提供 camelCase / kebab-case 双向查找插槽,\n * 以及按字段名前缀提取子渲染器插槽。\n *\n * @module utils/slot\n */\n\n/**\n * 检查字符串是否包含大写字母(驼峰格式特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含大写字母\n */\nconst isCamelCase = (str: string): boolean => /[A-Z]/.test(str)\n\n/**\n * 检查字符串是否包含连字符(kebab-case 特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含连字符\n */\nconst isKebabCase = (str: string): boolean => str.includes(\"-\")\n\n/**\n * 将驼峰命名转换为连字符命名(kebab-case)。\n *\n * @param str - 驼峰格式的字符串\n * @returns 转换后的 kebab-case 字符串\n */\nconst camelToKebab = (str: string): string => {\n return str.replace(/([A-Z])/g, \"-$1\").toLowerCase()\n}\n\n/**\n * 将连字符命名转换为驼峰命名(camelCase)。\n *\n * @param str - kebab-case 格式的字符串\n * @returns 转换后的 camelCase 字符串\n */\nconst kebabToCamel = (str: string): string => {\n return str.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())\n}\n\n/**\n * 将命名格式归一化为 kebab-case。\n *\n * @param str - 任意命名格式的字符串\n * @returns kebab-case 格式的字符串\n */\nconst normalizeToKebab = (str: string): string => {\n if (isCamelCase(str)) return camelToKebab(str)\n\n return str\n}\n\n/**\n * 将命名格式归一化为 camelCase。\n *\n * @param str - 任意命名格式的字符串\n * @returns camelCase 格式的字符串\n */\nconst normalizeToCamel = (str: string): string => {\n if (isKebabCase(str)) return kebabToCamel(str)\n\n return str\n}\n\n/**\n * 从插槽对象中按名称查找插槽,同时支持 camelCase 和 kebab-case。\n *\n * 优先查找原始名称,未找到时尝试另一种命名格式。\n * 例如传入 `userName` 时,先查 `userName`,再查 `user-name`;\n * 传入 `user-name` 时,先查 `user-name`,再查 `userName`。\n *\n * @param slots - Vue 插槽对象\n * @param name - 插槽名称(camelCase 或 kebab-case)\n *\n * @returns 匹配的插槽函数,未找到返回 undefined\n *\n * @example\n * ```typescript\n * // slots 中定义了 'user-name' 插槽\n * resolveSlot(slots, 'userName') // => 找到 'user-name' 插槽\n * resolveSlot(slots, 'user-name') // => 找到 'user-name' 插槽\n * ```\n */\nexport const resolveSlot = (\n slots: Record<string, any>,\n name: string\n): ((...args: any[]) => any) | undefined => {\n if (slots[name]) return slots[name]\n\n const alt = isCamelCase(name)\n ? camelToKebab(name)\n : isKebabCase(name)\n ? kebabToCamel(name)\n : undefined\n\n if (alt && slots[alt]) return slots[alt]\n\n return undefined\n}\n\n/**\n * 从父级插槽中提取子渲染器插槽。\n *\n * 按 `fieldName:slotName` 和 `fieldName:slot-name` 格式匹配,\n * 提取后去掉前缀作为子渲染器的插槽名。\n * 同时支持 camelCase 和 kebab-case 的 fieldName。\n *\n * @param fieldName - 字段名\n * @param allSlots - 父级全部插槽\n *\n * @returns 子渲染器的插槽对象\n *\n * @example\n * ```typescript\n * // 父级插槽: { 'userName:prefix': fn, 'userName:suffix': fn, 'userNameLabel': fn }\n * extractChildSlots('userName', slots)\n * // => { prefix: fn, suffix: fn }\n * ```\n */\nexport const extractChildSlots = (\n fieldName: string,\n allSlots: Record<string, any>\n): Record<string, any> => {\n const result: Record<string, any> = {}\n const camelPrefix = normalizeToCamel(String(fieldName)) + \":\"\n const kebabPrefix = normalizeToKebab(String(fieldName)) + \":\"\n\n for (const [key, value] of Object.entries(allSlots)) {\n if (key.startsWith(camelPrefix)) {\n result[key.slice(camelPrefix.length)] = value\n } else if (key.startsWith(kebabPrefix) && kebabPrefix !== camelPrefix) {\n result[key.slice(kebabPrefix.length)] = value\n }\n }\n\n return result\n}\n","/**\n * FormGroup - 分组字段组件\n *\n * 用于渲染 componentType 为 \"group\" 的分组字段配置。\n * 支持可折叠的分组容器,内部渲染子 columns。\n *\n * @module components/FormGroup\n */\n\nimport { defineComponent, PropType, ref } from \"vue\"\nimport type { SetupContext, VNodeChild } from \"vue\"\n\nimport classnames from \"classnames\"\n\nimport FormItem from \"../FormItem\"\n\nimport type { SchemxViewGroupSchema, SchemxViewSchema, Values } from \"@schemx/core\"\n\n/**\n * FormGroup Props\n *\n * @typeParam T - 表单值类型\n */\nexport interface SchemxGroupProps<T extends Values = Values> {\n schema: SchemxViewGroupSchema<T>\n}\n\nconst FormGroup = defineComponent(\n <T extends Values = Values>(props: SchemxGroupProps<T>, { slots }: SetupContext) => {\n const collapsed = ref(Boolean(props.schema.defaultCollapsed))\n\n const toggle = () => {\n if (props.schema.collapsible) {\n collapsed.value = !collapsed.value\n }\n }\n\n return (): VNodeChild => {\n const schema = props.schema\n const collapsible = Boolean(schema.collapsible)\n\n return (\n <div\n class={classnames(\n \"schemx-group\",\n { \"schemx-group--collapsed\": collapsed.value },\n schema.class as string | undefined\n )}\n data-key={schema.key}\n >\n {schema.label && (\n <div\n role={collapsible ? \"button\" : undefined}\n tabindex={collapsible ? 0 : undefined}\n class={classnames(\"schemx-group__header\", {\n \"schemx-group__header--clickable\": collapsible,\n })}\n onClick={toggle}\n onKeydown={(e: KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault()\n toggle()\n }\n }}\n >\n <span class=\"schemx-group__title\">{schema.label}</span>\n {collapsible && (\n <span\n class={classnames(\"schemx-group__arrow\", {\n \"schemx-group__arrow--down\": !collapsed.value,\n })}\n />\n )}\n </div>\n )}\n {!collapsed.value && (\n <div class=\"schemx-group__body\">\n {schema.children.map((child) => (\n <FormItem\n key={child.key}\n schema={child as SchemxViewSchema}\n v-slots={slots}\n />\n ))}\n </div>\n )}\n </div>\n )\n }\n },\n {\n name: \"SchemxGroup\",\n\n props: {\n schema: {\n type: Object as PropType<SchemxViewGroupSchema>,\n required: true,\n },\n },\n }\n)\n\nexport default FormGroup\n","/**\n * FormItem\n *\n * schemx 和 FormGroup 实际渲染字段的组件。\n * 动态属性已由 core 解析到 ViewSchema 中;这里只负责消费\n * 已解析 schema、创建字段实例、组装渲染器属性与插槽。\n *\n * @module components/FormItem\n */\n\nimport { computed, defineComponent, h, PropType, toRef } from \"vue\"\nimport type { SetupContext, VNodeChild } from \"vue\"\n\nimport classnames from \"classnames\"\n\nimport {\n createFieldContext,\n useConfigContext,\n useField,\n useFormContext,\n useStableRef,\n} from \"@/hooks\"\nimport { extractChildSlots, mergeTrigger, resolveSlot, shouldValidateOn } from \"@/utils\"\nimport type { TriggerConfig } from \"@/utils\"\n\nimport FormGroup from \"../FormGroup\"\n\nimport type {\n FieldValue,\n NamePath,\n SchemxComponentProps,\n SchemxViewFieldSchema,\n SchemxViewGroupSchema,\n SchemxViewSchema,\n Values,\n} from \"@schemx/core\"\n\n/**\n * FormItem 属性。\n *\n * @typeParam T - 表单值类型\n */\nexport interface SchemxItemProps<T extends Values = Values> {\n schema: SchemxViewSchema<T>\n}\n\nconst FormItem = defineComponent(\n <T extends Values = Values>(props: SchemxItemProps<T>, { slots }: SetupContext) => {\n const schemaRef = toRef(props, \"schema\")\n\n if (isViewGroupSchema(schemaRef.value)) {\n return (): VNodeChild => {\n return h(FormGroup, { schema: schemaRef.value as SchemxViewGroupSchema }, slots)\n }\n }\n\n const form = useFormContext<T>()\n const formContext = useConfigContext()\n\n const schema = (): SchemxViewFieldSchema<T> =>\n schemaRef.value as SchemxViewFieldSchema<T>\n\n const field = useField(schema().name)\n\n createFieldContext(field)\n\n const trigger = computed<TriggerConfig>(() =>\n mergeTrigger(schema().validationTrigger, formContext.validationTrigger, \"onChange\")\n )\n\n /**\n * 是否需要进行校验。\n *\n * 当字段不可见、详情展示、只读或禁用时,无需进行校验。\n */\n const canVerified = computed(() => {\n const isOperate = schema().visible && !schema().readonly && !schema().disabled\n\n const rules = schema().rules\n\n const hasRules = Array.isArray(rules) ? rules?.length > 0 : !!schema().rules\n\n return isOperate && hasRules\n })\n\n /** 值变化处理,设置值后根据触发时机决定是否校验 */\n const handleChange = (v: FieldValue<T, NamePath<T>>) => {\n field.setValue(v)\n schema().componentProps?.onChange?.(v)\n\n if (canVerified.value && shouldValidateOn(\"change\", trigger.value)) {\n field.validate()\n }\n }\n\n /** 失焦处理,根据触发时机决定是否校验 */\n const handleBlur = () => {\n // schema().componentProps?.onBlur?.()\n\n if (canVerified.value && shouldValidateOn(\"blur\", trigger.value)) {\n field.validate()\n }\n }\n\n // 使用 useStableRef 避免每次生成新对象引用\n const componentProps = useStableRef<SchemxComponentProps<T>>(\n (): SchemxComponentProps<T> => ({\n ...schema().componentProps,\n value: field.getValue(),\n onChange: handleChange,\n onBlur: handleBlur,\n \"onUpdate:value\": (v) => field.setValue(v),\n })\n )\n\n /**\n * 渲染 required 星号。\n *\n * 当字段为必填且非详情展示/禁用/只读状态时,在 label 前显示红色星号标记。\n *\n * @returns 星号 VNode 或空片段\n */\n const renderRequired = (): VNodeChild => {\n if (!schema().required || schema().disabled || schema().readonly) {\n return null\n }\n\n return <span class=\"schemx-item__required\">*</span>\n }\n\n /**\n * 渲染 formItem label 区域。\n *\n * 优先使用 `{name}Label` 插槽(支持 camelCase / kebab-case),\n * 未提供时渲染默认 label(含 required 星号、label 文本、冒号)。\n *\n * @returns label VNode\n */\n const renderLabel = (): VNodeChild => {\n const labelSlot = resolveSlot(slots, `${schema().name}Label`)\n\n if (labelSlot) {\n return labelSlot(schema())\n }\n\n const labelAlign = schema().labelAlign || formContext.labelAlign\n const labelWidth = schema().labelWidth || formContext.labelWidth\n const colon = schema().colon ?? formContext.colon\n\n return (\n <label\n class=\"schemx-item__label\"\n style={{ width: labelWidth, textAlign: labelAlign }}\n >\n {renderRequired()}\n <span class=\"schemx-item__label-text\">\n {schema().label}\n {colon ? \":\" : \"\"}\n </span>\n </label>\n )\n }\n\n /**\n * 渲染 formItem content 区域(仅控件)。\n *\n * 优先使用 `{name}Content` 插槽(支持 camelCase / kebab-case),\n * 插槽参数包含 formItemProps 和 columnElement(渲染器 VNode)。\n * 未提供插槽时,渲染默认控件布局。\n *\n * @returns content VNode\n */\n const renderContent = (): VNodeChild => {\n const component = form.getRenderer(schema().componentType)\n\n if (!component) {\n throw new Error(\n `[schemx] Can not find component renderer of \"${schema().componentType}\".`\n )\n }\n\n // 提取子渲染器插槽(fieldName:slotName 格式)\n const childSlots = extractChildSlots(normalizeNameKey(schema().name), slots)\n const columnElement = h(component, componentProps.value, childSlots)\n\n const contentSlot = resolveSlot(slots, `${schema().name}Content`)\n\n if (contentSlot) {\n return contentSlot({\n ...schema(),\n columnElement,\n })\n }\n\n return <div class=\"schemx-item__control\">{columnElement}</div>\n }\n\n /**\n * 渲染 formItem error 区域。\n *\n * 优先使用 `{name}Error` 插槽(支持 camelCase / kebab-case),\n * 插槽参数包含 formItemProps 和 errors 数组。\n * 未提供插槽时,仅在存在错误时显示第一条错误信息。\n *\n * @returns error VNode 或 null\n */\n const renderError = (): VNodeChild => {\n const errorSlot = resolveSlot(slots, `${schema().name}Error`)\n\n if (errorSlot) {\n return errorSlot({\n ...schema(),\n errors: field.error.value,\n })\n }\n\n if (!Array.isArray(field.error.value) || field.error.value.length === 0) {\n return null\n }\n\n return <div class=\"schemx-item__error\">{field.error.value[0]}</div>\n }\n\n return (): VNodeChild => {\n if (!schema().visible) {\n return null\n }\n\n // 整体插槽:完全接管渲染,不包裹任何默认结构\n const itemSlot = resolveSlot(slots, normalizeNameKey(schema().name))\n\n if (itemSlot) {\n return itemSlot(schema())\n }\n\n const labelPosition = schema().labelPosition || formContext.labelPosition\n\n return (\n <div class={classnames(\"schemx-item-wrapper\")}>\n <div\n class={classnames(\n \"schemx-item\",\n `schemx-item--label-${labelPosition}`,\n schema().class,\n {\n \"is-readonly\": schema().readonly,\n \"is-disabled\": schema().disabled,\n }\n )}\n style={{ ...(schema().style ?? {}) }}\n >\n {renderLabel()}\n\n <div class=\"schemx-item__content\">\n {renderContent()}\n {renderError()}\n </div>\n </div>\n </div>\n )\n }\n },\n {\n name: \"SchemxItem\",\n\n props: {\n schema: {\n type: Object as PropType<SchemxViewSchema>,\n required: true,\n },\n },\n }\n)\n\nexport default FormItem\n\nconst isViewGroupSchema = <T extends Values>(\n schema: SchemxViewSchema<T>\n): schema is SchemxViewGroupSchema<T> => {\n return schema.componentType === \"group\"\n}\n\nconst normalizeNameKey = (name: unknown): string => {\n if (Array.isArray(name)) {\n return name.map((part) => String(part)).join(\".\")\n }\n\n return String(name)\n}\n","import { SchemxViewSchema } from \"@schemx/core\"\n\n/**\n * 判断当前项是否为顶层可见普通字段中的第一个或最后一个。\n *\n * `group` 和不可见字段不参与顶层卡片圆角判断。\n *\n * 使用示例:\n *\n * const list = [\n * { key: 'group-1', type: 'group' },\n * { key: 'a' },\n * { key: 'b' },\n * { key: 'group-2', type: 'group' },\n * { key: 'c' },\n * ]\n *\n * getSectionPosition(list, 'a')\n * // => { found: true, isFirst: true, isLast: false }\n */\nexport function getSectionPosition<T extends SchemxViewSchema>(\n list: T[],\n currentKey: string\n) {\n const currentIndex = list.findIndex((item) => item.key === currentKey)\n\n if (currentIndex === -1) {\n return {\n found: false,\n isFirst: false,\n isLast: false,\n }\n }\n\n const currentItem = list[currentIndex]\n\n if (!isPositionItem(currentItem)) {\n return {\n found: true,\n isFirst: false,\n isLast: false,\n }\n }\n\n const prevItem = findPositionItem(list, currentIndex, -1)\n const nextItem = findPositionItem(list, currentIndex, 1)\n\n return {\n found: true,\n isFirst: !prevItem,\n isLast: !nextItem,\n }\n}\n\n/**\n * 判断当前项是否参与顶层首尾样式计算。\n */\nfunction isPositionItem<T extends SchemxViewSchema>(item?: T) {\n return (\n !!item &&\n item.componentType !== \"group\" &&\n (!(\"visible\" in item) || item.visible !== false)\n )\n}\n\nfunction findPositionItem<T extends SchemxViewSchema>(\n list: T[],\n startIndex: number,\n step: 1 | -1\n) {\n for (let index = startIndex + step; index >= 0 && index < list.length; index += step) {\n if (isPositionItem(list[index])) {\n return list[index]\n }\n }\n\n return undefined\n}\n","<!--\n schemx - 主组件\n\n 基于 Schema 配置的表单组件,使用 useForm Hook 管理状态。\n\n @module schemx\n-->\n\n<script lang=\"ts\" setup generic=\"T extends Values = Values\">\n import { watch, watchEffect } from \"vue\"\n\n import { omit } from \"es-toolkit\"\n\n import FormItem from \"./components/FormItem\"\n import { createConfigContext, useForm, useViewSchemas } from \"./hooks\"\n import { getSectionPosition } from \"./utils/helpers\"\n\n import type { SchemxFormProps } from \"./types/index\"\n import type { SchemxViewSchema, Values } from \"@schemx/core\"\n\n import \"./styles/index.css\"\n\n defineOptions({ name: \"SchemxForm\" })\n\n const props = withDefaults(defineProps<SchemxFormProps<T>>(), {\n modelValue: () => ({}) as T,\n initialValues: () => ({}) as T,\n form: undefined,\n schemas: () => [],\n class: \"\",\n style: () => ({}),\n onFinish: undefined,\n onFinishFailed: undefined,\n onValuesChange: undefined,\n onFieldsChange: undefined,\n rendererRegistry: undefined,\n validatorRegistry: undefined,\n })\n\n const emit = defineEmits<{\n \"update:modelValue\": [value: T]\n }>()\n\n /**\n * 创建 FormContext 上下文\n *\n * 为子组件提供表单配置信息。\n */\n createConfigContext(\n omit(props, [\n \"form\",\n \"modelValue\",\n \"rendererRegistry\",\n \"defaultRendererType\",\n \"validatorRegistry\",\n \"onFinish\",\n \"onFinishFailed\",\n \"onValuesChange\",\n \"onFieldsChange\",\n ])\n )\n\n /**\n * 获取或创建表单实例\n *\n * 优先使用外部传入的 form,否则内部通过 useForm 创建。\n * 必须在 setup 同步阶段调用,确保 provide 正确注入。\n */\n const form = props.form\n ? props.form\n : useForm<T>({\n schemas: props.schemas,\n initialValues: props.initialValues,\n rendererRegistry: props.rendererRegistry,\n defaultRendererType: props.defaultRendererType,\n validatorRegistry: props.validatorRegistry,\n\n readonly: props.readonly,\n disabled: props.disabled,\n\n onFinish: async (values) => {\n props.onFinish?.(values)\n },\n onFinishFailed: async (errors) => {\n props.onFinishFailed?.(errors)\n },\n onValuesChange: (changedValues, latestSnapshot) => {\n emit(\"update:modelValue\", latestSnapshot as T)\n props.onValuesChange?.(changedValues, latestSnapshot)\n },\n onFieldsChange: (changedPaths, allPaths) => {\n props.onFieldsChange?.(changedPaths, allPaths)\n },\n })\n\n watch(\n () => props.schemas,\n (schemas) => {\n form.setSchemas(schemas)\n },\n { deep: false, immediate: !!props.form }\n )\n\n const viewSchemas = useViewSchemas(form)\n\n const getFormItemClass = (schema: SchemxViewSchema<T>) => {\n const { isFirst, isLast } = getSectionPosition(\n viewSchemas.value as SchemxViewSchema[],\n schema.key\n )\n\n return {\n \"schemx-item-wrapper--first\": isFirst,\n \"schemx-item-wrapper--last\": isLast,\n }\n }\n\n watchEffect(() => {\n form.updateDefaultProps(props)\n })\n\n defineExpose({\n ...form,\n })\n</script>\n\n<template>\n <div :class=\"['schemx', props.class]\">\n <FormItem\n v-for=\"schema in viewSchemas\"\n :key=\"schema.key\"\n :schema=\"schema as SchemxViewSchema\"\n :class=\"getFormItemClass(schema as SchemxViewSchema<T>)\"\n >\n <template v-for=\"(_, slotName) in $slots\" #[slotName]=\"slotProps\">\n <slot :name=\"slotName\" v-bind=\"slotProps ?? {}\" />\n </template>\n </FormItem>\n </div>\n</template>\n","/**\n * schemx 组件导出\n *\n * 为 SchemxForm 组件挂载静态方法(install、FormItem、registerRequest 等),\n * 并作为默认导出。\n *\n * @module formExport\n */\n\nimport type { App, DefineComponent } from \"vue\"\n\nimport FormItem from \"./components/FormItem\"\nimport SchemxForm from \"./formRuntime.js\"\n\nimport type { SchemxFormProps } from \"./types\"\n\n/**\n * SchemxForm 插件安装选项\n *\n * 在 `app.use(SchemxForm, options)` 时传入,用于配置全局默认行为。\n *\n * @example\n * ```ts\n * import SchemxForm from '@schemx/vue'\n *\n * app.use(SchemxForm, {\n * request: (url) => fetch(url).then(r => r.json()),\n * })\n * ```\n */\nexport interface SchemxInstallOptions {}\n\n/**\n * 为组件挂载静态属性并保留原始类型\n *\n * @param comp - 原始组件\n * @param extra - 要挂载的静态属性\n */\nexport function withInstall<T extends object, E extends Record<string, unknown>>(\n comp: T,\n extra: E\n) {\n return Object.assign(comp, extra) as T & E\n}\n\nexport type SchemxFormPlugin = DefineComponent<SchemxFormProps> & {\n install: (app: App, options?: SchemxInstallOptions) => void\n FormItem: typeof FormItem\n}\n\nconst SchemxFormExport = withInstall(SchemxForm, {\n /** Vue 插件安装方法 */\n install(app: App, _options?: SchemxInstallOptions) {\n app.component(\"SchemxForm\", SchemxForm)\n },\n /** FormItem 子组件引用 */\n FormItem,\n}) as unknown as SchemxFormPlugin\n\nexport default SchemxFormExport\n","/**\n * WithRemoteOptions - 选项高阶组件\n *\n * 包装任意渲染器组件,自动注入 useDictionary 的选项加载能力。\n * 被包装组件会额外收到 options、loading 两个 props,\n * 无需自行调用 useDictionary。\n *\n * @module hocs/withRemoteOptions\n */\n\nimport { Component, computed, defineComponent, h, PropType, SetupContext } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useDictionary } from \"../hooks/useDictionary\"\n\nimport type { NamePath } from \"@schemx/core\"\n\n\n/**\n * WithRemoteOptions 注入给被包装组件的额外 Props\n */\nexport interface RemoteOptionsInjectedProps {\n /** 加载的选项列表 */\n options: any[]\n /** 加载状态 */\n loading: boolean\n}\n\n/**\n * 选项高阶组件\n *\n * 包装一个渲染器组件,当 dict prop 存在时,\n * 调用 useDictionary 加载选项,并将结果作为 options prop 注入。\n *\n * 当组件自身已传入非空 options 且无 dict 时,优先使用静态 options。\n *\n * @param WrappedComponent - 被包装的渲染器组件\n * @returns 增强后的组件,自动具备选项加载能力\n *\n * @example\n * ```ts\n * import { WithRemoteOptions } from '@schemx/vue'\n * import MySelect from './MySelect.vue'\n *\n * const RemoteSelect = WithRemoteOptions(MySelect)\n *\n * // 在 schema 中使用\n * const schema = {\n * name: 'city',\n * label: '城市',\n * componentType: 'select',\n * componentProps: {\n * dict: {\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * },\n * },\n * }\n * ```\n */\nexport function WithRemoteOptions(WrappedComponent: Component) {\n return defineComponent({\n name: `WithRemoteOptions(${WrappedComponent.name || \"Anonymous\"})`,\n inheritAttrs: false,\n props: {\n dict: {\n type: Object as PropType<SchemxDictionary>,\n default: undefined,\n },\n fieldName: {\n type: [String, Array] as PropType<NamePath>,\n default: undefined,\n },\n },\n setup(props, { attrs, slots }: SetupContext) {\n // 仅在 dict 配置存在时调用 useDictionary\n const dictResult = props.dict ? useDictionary(props.dict, props.fieldName) : null\n\n const childrenProps = computed(() => {\n return {\n ...attrs,\n dict: props.dict,\n options: props.dict ? dictResult?.list.value : attrs.options,\n loading: props.dict ? dictResult?.loading.value : attrs.loading,\n }\n })\n\n return () => h(WrappedComponent, childrenProps.value, slots)\n },\n })\n}\n\nexport default WithRemoteOptions\n"],"names":["globalRendererRegistry","globalRulesRegistry","FormGroup","defineComponent","props","slots","collapsed","ref","Boolean","schema","defaultCollapsed","toggle","collapsible","value","_createVNode","classnames","class","key","label","undefined","e","preventDefault","children","map","child","FormItem","name","type","Object","required","schemaRef","toRef","isViewGroupSchema","h","form","useFormContext","formContext","useConfigContext","field","useField","createFieldContext","trigger","computed","mergeTrigger","validationTrigger","canVerified","isOperate","visible","readonly","disabled","rules","hasRules","Array","isArray","length","handleChange","v","setValue","componentProps","onChange","shouldValidateOn","validate","handleBlur","useStableRef","getValue","onBlur","renderRequired","_createTextVNode","renderLabel","labelSlot","resolveSlot","labelAlign","labelWidth","colon","width","textAlign","renderContent","component","getRenderer","componentType","Error","childSlots","extractChildSlots","normalizeNameKey","columnElement","contentSlot","renderError","errorSlot","errors","error","itemSlot","labelPosition","style","part","String","join","_createElementBlock","_normalizeClass","_Fragment","_renderList","_unref","_createBlock","$slots","_withCtx","_renderSlot","_mergeProps","SchemxForm"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/utils/rendererProvider.ts","../src/utils/rulesProvider.ts","../src/hooks/useForm.ts","../src/hooks/provideFormContext.ts","../src/hooks/useField.ts","../src/hooks/provideFieldContext.ts","../src/hooks/useWatch.ts","../src/hooks/useEffect.ts","../src/hooks/useDictionary.ts","../src/hooks/provideFormConfigContext.ts","../src/utils/equal.ts","../src/hooks/useStableRef.ts","../src/hooks/useViewSchemas.ts","../src/utils/validation.ts","../src/utils/slot.ts","../src/components/FormGroup/index.tsx","../src/components/FormItem/index.tsx","../src/utils/helpers.ts","../src/form.vue","../src/form.ts","../src/hocs/withRemoteOptions.ts"],"sourcesContent":["/**\n * 全局渲染器注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * vant 等 UI 适配层通过 import 后调用 registerAll 注册渲染器,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rendererProvider\n */\n\nimport {\n createRendererRegistry,\n type RendererRegistryType,\n type SchemxRendererKey,\n} from \"@schemx/core\"\n\n/**\n * 全局渲染器注册实例\n *\n * @example\n * ```ts\n * // UI 适配层注册渲染器\n * import { rendererRegistry } from '@schemx/vue'\n * rendererRegistry.registerAll({ input: InputRenderer, ... })\n *\n * // useForm 内部自动使用\n * // props 传入的 rendererRegistry 优先级更高\n * ```\n */\nexport const rendererRegistry: RendererRegistryType<SchemxRendererKey> =\n createRendererRegistry<SchemxRendererKey>(\"input\")\n","/**\n * 全局校验规则注册实例\n *\n * 模块级单例,由 ES Module 保证全局唯一。\n * UI 适配层通过 import 后调用 register/registerAll 注册自定义规则,\n * useForm 内部自动使用此实例作为 fallback。\n *\n * @module utils/rulesProvider\n */\n\nimport { createValidatorsRegistry, type ValidatorsRegistryType } from \"@schemx/core\"\n\n/**\n * 全局校验规则注册实例\n *\n * @example\n * ```ts\n * // 注册自定义规则\n * import { validatorRegistry } from '@schemx/vue'\n * validatorRegistry.register('phone', phoneRule)\n *\n * // useForm 内部自动使用\n * // props 传入的 validatorRegistry 优先级更高\n * ```\n */\nexport const validatorRegistry: ValidatorsRegistryType = createValidatorsRegistry()\n","/**\n * useForm - Schemx 表单实例的 Vue 生命周期适配。\n *\n * 负责创建由当前 Vue effect scope 持有的 SchemxInstance,并在 scope 销毁时\n * 自动释放实例。该模块不负责 provide/inject;表单上下文统一由\n * createFormContext() 注册,避免外部传入 form 时跳过上下文注册。\n *\n * @module hooks/useForm\n */\nimport { onScopeDispose } from \"vue\"\n\nimport { createForm } from \"@schemx/core\"\n\nimport { rendererRegistry as globalRendererRegistry } from \"../utils/rendererProvider\"\nimport { validatorRegistry as globalRulesRegistry } from \"../utils/rulesProvider\"\n\nimport type { CreateFormOptions, NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * useForm 配置选项。\n *\n * 当前与 core 层 CreateFormOptions 保持一致,并在 Vue 层自动补充默认的\n * rendererRegistry 和 validatorRegistry。保留独立类型用于后续扩展\n * Vue 专属配置,而不污染 core 层接口。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface UseFormOptions<TValues extends Values> extends CreateFormOptions<\n TValues,\n NamePath<TValues>\n> {}\n\n/**\n * 创建由当前 Vue effect scope 持有的表单实例。\n *\n * useForm 只负责以下职责:\n * 1. 合并 Vue 层默认注册表;\n * 2. 同步创建 SchemxInstance;\n * 3. 在当前 effect scope 销毁时调用 instance.destroy()。\n *\n * useForm 不再自动调用 provide()。需要向后代组件暴露实例时,应由\n * <SchemxForm> 或其他 Provider 组件显式调用 createFormContext(form)。\n * 这样无论实例是内部创建还是通过 props.form 外部传入,都能走同一条\n * 上下文注册路径,并保持清晰的实例所有权:谁创建,谁销毁。\n *\n * 该函数应在组件 setup() 或其他有效的 Vue effect scope 中同步调用。\n * 非 Vue 生命周期场景请直接使用 @schemx/core 的 createForm(),并自行销毁实例。\n *\n * @typeParam TValues - 表单值类型\n * @param options - 表单创建配置\n * @returns 当前 scope 持有的 SchemxInstance\n *\n * @example\n * ```ts\n * const form = useForm({\n * initialValues: {\n * name: \"\",\n * email: \"\",\n * },\n * onFinish: async (values) => {\n * await api.submit(values)\n * },\n * })\n *\n * form.setFieldValue(\"name\", \"Schemx\")\n * ```\n *\n * @example\n * ```ts\n * // Provider 组件中统一注册上下文。\n * const form = props.form ?? useForm(options)\n * createFormContext(form)\n * ```\n */\nexport function useForm<TValues extends Values = Values>(\n options: UseFormOptions<TValues> = {}\n): SchemxInstance<TValues> {\n const mergedOptions: CreateFormOptions<TValues> = {\n ...options,\n rendererRegistry: options.rendererRegistry ?? globalRendererRegistry,\n validatorRegistry: options.validatorRegistry ?? globalRulesRegistry,\n }\n\n // 表单实例是当前 scope 内的一次性资源,不需要使用 computed 包装。\n const instance = createForm<TValues>(mergedOptions)\n\n // useForm 创建的实例归当前 effect scope 所有,因此由当前 scope 负责销毁。\n onScopeDispose(() => {\n instance.destroy()\n })\n\n return instance\n}\n","/**\n * Schemx 表单实例的 Vue provide/inject 适配。\n *\n * 负责在组件树中注册和读取 SchemxInstance,不负责创建或销毁实例。\n * 表单实例无论由 <SchemxForm> 内部创建,还是通过 form 属性从外部传入,\n * 都应由 <SchemxForm> 在 setup() 同步阶段调用 createFormContext() 注册。\n *\n * @module hooks/provideFormContext\n */\nimport { inject, type InjectionKey, provide } from \"vue\"\n\nimport type { SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * SchemxInstance 在 Vue provide/inject 中使用的注入 key。\n *\n * 该 key 只应与同一份包代码导出的 createFormContext() 和 useFormContext()\n * 配套使用;重复安装不兼容版本的包不会共享此上下文。\n */\nexport const SCHEMX_FORM_INSTANCE_KEY = Symbol(\"schemx:instance\") as InjectionKey<\n SchemxInstance<Values>\n>\n\n/**\n * 旧版表单实例注入 key。\n *\n * @deprecated 请使用 SCHEMX_FORM_INSTANCE_KEY。该别名仅用于兼容旧测试和旧适配代码。\n */\nexport const FORM_INSTANCE_KEY = SCHEMX_FORM_INSTANCE_KEY\n\n/**\n * 向当前组件的后代组件提供表单实例。\n *\n * 必须在组件 setup() 的同步执行阶段调用;不要放入 onMounted、nextTick、\n * Promise 或其他异步回调中,否则 Vue 无法把上下文关联到当前组件实例。\n *\n * @typeParam TValues - 表单值类型\n * @param instance - 要提供给后代组件的表单实例\n *\n * @remarks\n * 该函数不接管实例所有权,也不会在组件卸载时销毁实例。\n *\n * @example\n * ```ts\n * const form = props.form ?? useForm(options)\n *\n * // 无论 form 来自外部还是内部,都统一注册上下文。\n * createFormContext(form)\n * ```\n */\nexport function createFormContext<TValues extends Values = Values>(\n instance: SchemxInstance<TValues>\n): void {\n provide<SchemxInstance<TValues>>(SCHEMX_FORM_INSTANCE_KEY, instance)\n}\n\n/**\n * 获取最近祖先组件提供的表单实例。\n *\n * 该函数只能读取祖先组件注册的上下文,不能读取当前组件自身或后代组件\n * 调用 createFormContext() 提供的值。通常由 useField()、useWatch() 等其他\n * 表单 hook 间接调用。\n *\n * @typeParam TValues - 表单值类型\n * @returns 当前组件所属表单树中的 SchemxInstance\n *\n * @throws Error 当前组件不在已注册表单实例上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const form = useFormContext<FormValues>()\n * form.setFieldValue(\"name\", \"Schemx\")\n * ```\n */\nexport function useFormContext<\n TValues extends Values = Values,\n>(): SchemxInstance<TValues> {\n const instance = inject<SchemxInstance<TValues> | null>(SCHEMX_FORM_INSTANCE_KEY, null)\n\n if (!instance) {\n throw new Error(\n \"[schemx] useFormContext() must be called inside a <SchemxForm> descendant. \" +\n \"Ensure createFormContext(form) is called synchronously during setup().\"\n )\n }\n\n return instance\n}\n","/**\n * useField - 字段控制 Hook\n *\n * 基于 core 层的 {@link createField} 实现,\n * 通过 subscribe 回调将 Signal 变化桥接到 Vue 响应式系统。\n *\n * @module hooks/useField\n *\n * @remarks\n * core 层的 createField 封装了所有框架无关的字段操作逻辑,\n * useField 只负责:\n * 1. 从 Vue context 中获取 form 实例\n * 2. 创建 shallowRef 容器\n * 3. 通过 subscribe 将 Signal 变化同步到 shallowRef\n * 4. 包装 computed 只读状态\n * 5. 管理 onUnmounted 生命周期清理\n */\n\nimport { computed, onUnmounted, shallowRef } from \"vue\"\n\nimport { createField } from \"@schemx/core\"\n\nimport { FieldInstance } from \"../types/field\"\n\nimport { useFormContext } from \"./provideFormContext\"\n\nimport type { NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * useField 针对 (form, name) 的缓存条目\n *\n * 通过引用计数确保同一 (form, name) 的多次 useField 调用\n * 共享同一份 shallowRef 和 effect 桥接,避免重复订阅。\n *\n * @typeParam TValues - 表单值类型\n */\ninterface FieldHookCacheEntry<TValues extends Values = Values> {\n /** 当前活跃的引用计数,归零时释放 dispose 并从缓存中移除 */\n refCount: number\n /** 共享的字段控制器实例(含 Vue Ref 包装) */\n result: FieldInstance<TValues>\n /** 取消 Signal effect 订阅并清理资源 */\n dispose: () => void\n}\n\n/** 模块级缓存:form 实例 → 字段名 → 缓存条目 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst fieldHookCache = new WeakMap<any, Map<string, FieldHookCacheEntry>>()\n\n/**\n * 创建字段 hook 的响应式状态\n *\n * 从 createField 生成 FieldInstance,创建 shallowRef 容器\n * 并通过 field.effect 将 Signal 变化桥接到 Vue 响应式系统。\n */\nfunction createFieldHook<TValues extends Values = Values>(\n form: SchemxInstance<TValues>,\n name: NamePath<TValues>\n) {\n const field = createField<TValues>(form, name)\n\n const fieldValue = shallowRef(field.getValue())\n const fieldError = shallowRef(field.getError())\n const fieldPending = shallowRef(field.isPending())\n\n const dispose = field.effect(() => {\n fieldValue.value = field.getValue()\n fieldError.value = field.getError()\n\n fieldPending.value = field.isPending()\n })\n\n const error = computed(() => fieldError.value)\n\n const dirty = computed(() => {\n void fieldValue.value\n\n return field.isTouched()\n })\n\n const pending = computed(() => fieldPending.value)\n\n const result = {\n value: fieldValue,\n error,\n dirty,\n pending,\n ...field,\n getValue: () => fieldValue.value,\n }\n\n return { result, dispose }\n}\n\n/**\n * 获取单个字段的控制能力\n *\n * 通过 core 层 createField 提供字段操作方法,\n * 通过 subscribe 回调桥接 Signal 变化到 Vue shallowRef。\n *\n * @param name - 字段名(支持嵌套路径,如 'user.address.city')\n * @returns 字段状态和操作方法\n *\n * @example\n * ```typescript\n * const field = useField('username')\n *\n * // 响应式值(在 computed/watchEffect/template 中自动追踪)\n * field.getValue()\n *\n * // 响应式错误\n * field.error.value // string[] | undefined\n *\n * // 响应式脏状态\n * field.dirty.value // boolean\n *\n * // 响应式操作中状态\n * field.pending.value // boolean\n *\n * // 写入值\n * field.setValue('new value')\n *\n * // 校验\n * const result = await field.validate()\n * ```\n */\nexport const useField = <TValues extends Values = Values>(\n name: NamePath<TValues>\n): FieldInstance<TValues> => {\n const form = useFormContext<TValues>()\n\n const key = name\n\n let formCache = fieldHookCache.get(form)\n\n if (!formCache) {\n formCache = new Map()\n fieldHookCache.set(form, formCache)\n }\n\n const cachedEntry = formCache.get(key) as FieldHookCacheEntry<TValues> | undefined\n\n let activeEntry: FieldHookCacheEntry<TValues>\n\n if (cachedEntry) {\n cachedEntry.refCount++\n activeEntry = cachedEntry\n } else {\n const { result, dispose } = createFieldHook<TValues>(form, name)\n\n activeEntry = {\n refCount: 1,\n result,\n dispose,\n }\n\n formCache.set(key, activeEntry as any)\n }\n\n onUnmounted(() => {\n if (--activeEntry.refCount <= 0) {\n activeEntry.dispose()\n formCache.delete(key)\n }\n })\n\n return activeEntry.result\n}\n\nexport default useField\n","import { inject, type InjectionKey, provide } from \"vue\"\n\nimport type { FieldInstance } from \"../types/field\"\nimport type { Values } from \"@schemx/core\"\n\n/** 当前字段实例在 Vue provide/inject 中使用的注入 key。 */\nconst SCHEMX_FORM_FIELD_KEY: InjectionKey<FieldInstance> = Symbol(\n \"schemx:field\"\n) as InjectionKey<FieldInstance<Values>>\n\n/**\n * 向后代组件提供当前字段实例。\n *\n * 应在创建字段实例的组件 setup() 同步阶段调用,通常由 FormItem\n * 在调用 useField() 后使用,使字段插槽或自定义子组件能够读取同一实例。\n *\n * @param field - useField() 返回的字段实例\n *\n * @remarks\n * 该函数只注册字段上下文,不创建字段实例,也不负责字段实例的生命周期。\n *\n * @example\n * ```typescript\n * // FormItem 中\n * const field = useField('date')\n * createFieldContext(field)\n *\n * ```\n */\nexport function createFieldContext<TValues extends Values = Values>(\n field: FieldInstance<TValues>\n): void {\n provide<FieldInstance<TValues>>(SCHEMX_FORM_FIELD_KEY, field)\n}\n\n/**\n * 获取最近祖先组件提供的当前字段实例。\n *\n * 必须在已调用 createFieldContext() 的组件子树内调用,否则抛出错误。\n *\n * @returns useField() 返回的字段实例\n *\n * @throws Error 当前组件不在字段上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const field = useFieldContext()\n * field.error.value // string[] | undefined\n * field.getValue() // 当前字段值\n * ```\n */\nexport function useFieldContext(): FieldInstance<Values> {\n const field = inject<FieldInstance<Values>>(SCHEMX_FORM_FIELD_KEY)\n\n if (!field) {\n throw new Error(\n \"[schemx] useFieldContext() must be called inside a component tree where \" +\n \"createFieldContext(field) has been called.\"\n )\n }\n\n return field\n}\n","/**\n * useWatch - 监听字段变化(Vue 组合式 API 版本)\n *\n * 基于 core 层的 {@link createWatch} 实现,\n * 自动通过 `useFormContext` 获取 formContext,\n * 并在 `onUnmounted` 时自动取消监听,无需手动管理生命周期。\n *\n * 提供三种监听模式:\n * - 全局监听:`useWatch(callback, options?)` — 监听表单中任意字段变化\n * - 单字段监听:`useWatch(name, callback, options?)` — 监听指定字段变化\n * - 多字段监听:`useWatch(names, callback, options?)` — 监听一组字段变化\n *\n * 同时提供语义化的便捷方法:\n * - {@link useWatchField} — 单字段监听\n * - {@link useWatchFields} — 多字段监听\n * - {@link useWatchAll} — 全局监听\n *\n * @module hooks/useWatch\n *\n * @example\n * ```ts\n * // 全局监听\n * useWatch((snapshot) => {\n * console.log('form changed:', snapshot)\n * })\n *\n * // 单字段监听\n * useWatch('username', (current, prev) => {\n * console.log('username changed:', prev, '->', current)\n * }, { immediate: true })\n *\n * // 多字段监听\n * useWatch(['firstName', 'lastName'], (currentValues, prevValues) => {\n * console.log('name changed:', currentValues)\n * }, { inequality: true })\n * ```\n */\n\nimport { onUnmounted } from \"vue\"\n\nimport { createWatch } from \"@schemx/core\"\n\nimport { useFormContext } from \"./provideFormContext\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\nimport type {\n CreateWatchOptions,\n WatchAllCallback,\n WatchFieldCallback,\n WatchFieldsCallback,\n} from \"@schemx/core\"\n\n/**\n * 监听所有字段变化\n *\n * @param callback - 全局变化回调\n * @param options - 监听选项\n * @returns 取消监听函数\n */\nexport function useWatch<T extends Values>(\n callback: WatchAllCallback<T>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听单个字段变化\n *\n * @param name - 字段路径\n * @param callback - 单字段变化回调\n * @param options - 监听选项\n * @returns 取消监听函数\n */\nexport function useWatch<T extends Values>(\n name: NamePath<T>,\n callback: WatchFieldCallback<T>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听多个字段变化\n *\n * @param names - 字段路径数组\n * @param callback - 多字段变化回调\n * @param options - 监听选项\n * @returns 取消监听函数\n */\nexport function useWatch<T extends Values>(\n names: NamePath<T>[],\n callback: WatchFieldsCallback<T>,\n options?: CreateWatchOptions\n): () => void\n/**\n * useWatch 实现 — 获取 form 实例后委托给 core 层的 createWatch\n */\nexport function useWatch<T extends Values>(\n nameOrNamesOrCallback: NamePath<T> | NamePath<T>[] | WatchAllCallback<T>,\n callbackOrOptions?: WatchFieldCallback<T> | WatchFieldsCallback<T> | CreateWatchOptions,\n maybeOptions?: CreateWatchOptions\n): () => void {\n const form = useFormContext<T>()\n\n const dispose = (createWatch as any)(\n form,\n nameOrNamesOrCallback,\n callbackOrOptions,\n maybeOptions\n )\n\n onUnmounted(dispose)\n\n return dispose\n}\n\n/**\n * 监听单个字段变化的便捷方法\n *\n * 等价于 `useWatch(name, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @param name - 要监听的字段路径\n * @param callback - 字段变化回调\n * @param options - 可选配置\n * @returns 取消监听函数\n */\nexport function useWatchField<T extends Values = Values>(\n name: NamePath<T>,\n callback: WatchFieldCallback<T>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<T>(name, callback, options)\n}\n\n/**\n * 监听多个字段变化的便捷方法\n *\n * 等价于 `useWatch(names, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @param names - 要监听的字段路径数组\n * @param callback - 字段变化回调\n * @param options - 可选配置\n * @returns 取消监听函数\n */\nexport function useWatchFields<T extends Values>(\n names: NamePath<T>[],\n callback: WatchFieldsCallback<T>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<T>(names, callback, options)\n}\n\n/**\n * 监听所有字段变化的便捷方法\n *\n * 等价于 `useWatch(callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @param callback - 全局变化回调\n * @param options - 可选配置\n * @returns 取消监听函数\n */\nexport function useWatchAll<T extends Values = Values>(\n callback: WatchAllCallback<T>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch(callback, options)\n}\n\nexport default useWatch\n","/**\n * useEffect - Vue 组合式 API 版本的 Signal effect\n *\n * 封装 `@schemx/core` 的 `createEffect`,在组件卸载时自动 dispose,\n * 无需手动管理生命周期。\n *\n * @module hooks/useEffect\n *\n * @example\n * ```ts\n * import { useEffect } from '@schemx/vue'\n *\n * // 在 setup 中使用\n * useEffect(() => {\n * const name = form.getFieldValue('name')\n * console.log('name:', name)\n *\n * return () => {\n * // 清理逻辑\n * }\n * })\n * ```\n */\n\nimport { onUnmounted } from \"vue\"\n\nimport { createEffect } from \"@schemx/core\"\n\nimport type { CreateEffectReturn, EffectCallback } from \"@schemx/core\"\n\n/**\n * 创建 Signal effect 并在组件卸载时自动取消。\n *\n * 在 effect 回调中访问任何 Signal 值时自动追踪依赖,\n * 依赖变化时自动重新执行回调。\n * 回调可返回一个清理函数,在 effect 重新执行前或被 dispose 时调用。\n *\n * @param callback - effect 回调函数,可选返回清理函数\n *\n * @returns 取消 effect 的 dispose 函数(通常无需手动调用,组件卸载时自动取消)\n *\n * @example\n * ```ts\n * // 监听字段值变化\n * useEffect(() => {\n * const name = form.getFieldValue('name')\n * console.log('name changed:', name)\n * })\n *\n * // 带清理函数\n * useEffect(() => {\n * const timer = setInterval(() => {\n * console.log('value:', form.getFieldValue('count'))\n * }, 1000)\n *\n * return () => clearInterval(timer)\n * })\n * ```\n */\nexport function useEffect(callback: EffectCallback): CreateEffectReturn {\n const dispose = createEffect(callback)\n\n onUnmounted(dispose)\n\n return dispose\n}\n\nexport default useEffect\n","/**\n * useDictionary - 字典选项 Hook\n *\n * 通过 api 函数加载字典选项,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持通过 dependsOn 声明依赖字段,\n * 当依赖字段变化时自动重新请求。\n *\n * @module hooks/useDictionary\n */\n\nimport { onMounted, Ref, ref } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useFormContext } from \"./provideFormContext\"\nimport { useWatchFields } from \"./useWatch\"\n\nimport type { NamePath, Values } from \"@schemx/core\"\n\nexport type { SchemxDictionary, SchemxWithDictionary } from \"@/types/dictionary\"\n\n/**\n * useDictionary 返回值\n */\nexport interface UseDictionaryReturn {\n /** 远程加载的字典选项列表(响应式) */\n list: Ref<any[]>\n /** 请求加载状态(响应式) */\n loading: Ref<boolean>\n /** 请求错误信息(响应式) */\n error: Ref<Error | undefined>\n /** 触发字典选项加载 */\n loadDict: () => Promise<void>\n /** 使用当前配置重新执行 api */\n refresh: () => Promise<void>\n /** 直接修改 list 的值,不触发 api 调用 */\n mutate: (data: any[]) => void\n}\n\n/**\n * @deprecated\n *\n * 已弃用,请使用 **UseDictionaryReturn**\n */\nexport type UseDictOptionsReturn = UseDictionaryReturn\n\n/**\n * 将未知抛出值规范化为 `Error` 实例\n *\n * @param err - 捕获的值(可能是任意类型)\n * @returns 包装原始值的 `Error` 对象\n */\nexport function normalizeError(err: unknown): Error {\n if (err instanceof Error) return err\n\n return new Error(String(err))\n}\n\n/**\n * 加载字典选项\n *\n * 通过 api 函数获取数据,支持完整的表单值泛型推导。\n * 自动在组件挂载时加载,支持依赖字段联动、竞态控制、\n * 重试、错误处理等能力。\n *\n * @typeParam TValues - 表单值类型\n * @param options - 字典配置选项\n * @param fieldName - 当前字段名,用于 resetOnDepsChange 时清空字段值\n * @returns 响应式选项列表、loading/error 状态及控制方法\n *\n * @example\n * ```ts\n * // 基础用法\n * const { list } = useDictionary({\n * api: async () => {\n * const res = await fetch('/api/options')\n * return res.json()\n * },\n * })\n *\n * // 依赖联动(带泛型)\n * const { list } = useDictionary<MyFormValues>({\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * }, 'city')\n * ```\n */\nexport const useDictionary = <\n TValues extends Values = Values,\n TName extends NamePath<TValues> = NamePath<TValues>,\n>(\n options: SchemxDictionary<TValues>,\n fieldName?: TName\n): UseDictionaryReturn => {\n const instance = useFormContext<TValues>()\n\n const list = ref<any[]>([])\n const loading = ref<boolean>(false)\n const error = ref<Error | undefined>(undefined)\n\n // 竞态控制:仅最新请求写入状态\n let requestCount = 0\n\n /**\n * 使用配置的 formatter 格式化原始响应数据\n */\n const format = async (res: any): Promise<any> => {\n if (typeof options?.formatter === \"function\") {\n return await options.formatter(res, instance)\n }\n\n return res\n }\n\n /**\n * 带重试的执行\n */\n const executeWithRetry = async (formValues: TValues): Promise<any> => {\n const maxRetries = options.retryCount ?? 0\n const retryDelay = options.retryInterval ?? 1000\n let lastError: Error = new Error(\"Unknown error\")\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await options.api(formValues, instance)\n } catch (err) {\n lastError = normalizeError(err)\n\n if (attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, retryDelay))\n }\n }\n }\n\n throw lastError\n }\n\n /**\n * 执行 api 函数加载字典选项\n */\n const loadDict = async (): Promise<void> => {\n try {\n const formValues = instance.getFieldsValue()\n\n // shouldFetch 检查\n if (typeof options.shouldFetch === \"function\" && !options.shouldFetch(formValues)) {\n list.value = []\n loading.value = false\n\n return\n }\n\n loading.value = true\n error.value = undefined\n\n // 递增请求计数器用于竞态控制\n const currentCount = ++requestCount\n\n const res = await executeWithRetry(formValues)\n\n // 竞态检查:丢弃过期响应\n if (currentCount !== requestCount) return\n\n const formatted = await format(res)\n\n // 格式化后再次竞态检查(格式化可能是异步的)\n if (currentCount !== requestCount) return\n\n list.value = formatted\n error.value = undefined\n\n // onSuccess 回调\n if (typeof options.onSuccess === \"function\") {\n options.onSuccess(formatted, instance)\n }\n\n loading.value = false\n } catch (err) {\n const normalized = normalizeError(err)\n\n error.value = normalized\n list.value = []\n loading.value = false\n\n if (typeof options.onError === \"function\") {\n options.onError(normalized, instance)\n }\n }\n }\n\n const refresh = (): Promise<void> => loadDict()\n\n const mutate = (data: any[]): void => {\n list.value = data\n }\n\n // ========== 依赖字段监听 ==========\n if (options.dependsOn?.length) {\n useWatchFields(options.dependsOn, (_payload, latestSnapshot) => {\n // 1. 触发 onDepsChange 回调\n if (typeof options.onDepsChange === \"function\") {\n options.onDepsChange(latestSnapshot as TValues, instance)\n }\n\n // 2. 清空当前字段值\n if (options.resetOnDepsChange && fieldName) {\n instance.setFieldValue(fieldName, undefined)\n }\n\n // 3. 重新执行 api\n void loadDict()\n })\n }\n\n // ========== 挂载行为 ==========\n const immediate = options.immediate ?? true\n\n onMounted(() => {\n if (immediate) {\n void loadDict()\n }\n })\n\n return { list, loading, error, loadDict, refresh, mutate }\n}\n\nexport default useDictionary\n","/**\n * 表单级配置的 Vue provide/inject 适配。\n *\n * `SchemxForm` 通过 createFormConfigContext() 提供默认展示配置,\n * 字段组件通过 useFormConfigContext() 读取这些配置。\n *\n * @module hooks/provideFormConfigContext\n */\n\nimport { inject, type InjectionKey, provide } from \"vue\"\n\nimport type { SchemxFormProps } from \"../types\"\nimport type { Values } from \"@schemx/core\"\n\n/** 表单级展示配置在 Vue provide/inject 中使用的注入 key。 */\nexport const SCHEMX_FORM_CONFIG_KEY = Symbol(\"schemx:form-config\") as InjectionKey<\n FormContextProps<Values>\n>\n\n/**\n * 表单级默认配置。\n *\n * 该类型描述由 SchemxForm 提供给字段组件的配置上下文,排除表单实例、\n * 表单数据和回调等运行时处理属性,供字段组件读取默认展示行为。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface FormContextProps<TValues extends Values = Values> extends Omit<\n SchemxFormProps<TValues>,\n | \"form\"\n | \"modelValue\"\n | \"rendererRegistry\"\n | \"defaultRendererTValuesype\"\n | \"rulesRegistery\"\n | \"onFinish\"\n | \"onFinishFailed\"\n | \"onValuesChange\"\n | \"onFieldsChange\"\n> {}\n\n/**\n * 创建并注入表单级配置上下文。\n *\n * 应在 `SchemxForm` 或自定义 Provider 的 setup() 同步阶段调用,\n * 使后代字段组件能够读取 readonly、disabled、labelAlign 等默认配置。\n *\n * @typeParam TValues - 表单值类型\n * @param props - 要提供给后代组件的表单级默认配置\n *\n * @remarks\n * 该函数只注册上下文,不创建或销毁表单实例,也不改变传入配置的所有权。\n */\nexport const createFormConfigContext = <TValues extends Values = Values>(\n props: FormContextProps<TValues>\n): void => {\n provide<FormContextProps<TValues>>(SCHEMX_FORM_CONFIG_KEY, props)\n}\n\n/**\n * `createFormConfigContext` 的兼容别名。\n *\n * @deprecated 请使用 createFormConfigContext。\n */\nexport const createContext = createFormConfigContext\n\n/**\n * 获取最近祖先组件提供的表单级配置。\n *\n * 只能读取祖先组件通过 createFormConfigContext() 注册的配置,\n * 不能读取当前组件或后代组件提供的值。\n *\n * @returns 当前表单树中的表单级默认配置\n *\n * @throws Error 当前组件不在已注册表单配置上下文的后代组件树中时抛出\n *\n * @example\n * ```ts\n * const context = useFormConfigContext()\n * console.log(context.readonly, context.disabled)\n * ```\n */\nexport function useFormConfigContext(): FormContextProps {\n const context = inject<FormContextProps>(SCHEMX_FORM_CONFIG_KEY)\n\n if (!context) {\n throw new Error(\n \"[schemx] useFormConfigContext() must be called inside a <SchemxForm> descendant. \" +\n \"Ensure createFormConfigContext(props) is called synchronously during setup().\"\n )\n }\n\n return context\n}\n\n/**\n * `useFormConfigContext` 的兼容别名。\n *\n * @deprecated 请使用 useFormConfigContext。\n */\nexport const useContext = useFormConfigContext\n","/**\n * 对象浅比较工具\n *\n * @module utils/equal\n */\n\n/**\n * 浅比较两个对象的自有属性值是否相同。\n *\n * 用于避免 computed / shallowRef 每次返回新对象引用导致子组件不必要的更新。\n * 仅比较第一层属性,函数引用通过 === 判断。\n *\n * @param a - 旧对象\n * @param b - 新对象\n *\n * @returns 两个对象浅相等时返回 true\n *\n * @example\n * shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2 }) // => true\n * shallowEqual({ a: 1 }, { a: 1, b: 2 }) // => false\n */\nexport const isShallowEqual = (\n a: Record<string, any>,\n b: Record<string, any>\n): boolean => {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n\n return keysA.every((key) => a[key] === b[key])\n}\n","/**\n * useStableRef - 引用稳定化的 shallowRef\n *\n * 封装 `shallowRef` + `watchEffect` + `isShallowEqual`,\n * 仅在工厂函数返回值与当前值浅不相等时才更新引用,\n * 避免下游组件因对象引用变化而不必要地重渲染。\n *\n * @module hooks/useStableRef\n *\n * @example\n * ```ts\n * import { useStableRef } from '@schemx/vue'\n *\n * const stableProps = useStableRef(() => ({\n * visible: visible.value,\n * disabled: disabled.value,\n * value: field.getValue(),\n * }))\n * ```\n */\n\nimport { type ShallowRef, shallowRef, watchEffect } from \"vue\"\n\nimport { isShallowEqual } from \"../utils/equal\"\n\n/**\n * 创建一个引用稳定的 shallowRef。\n *\n * 在 watchEffect 中执行工厂函数,自动追踪响应式依赖。\n * 仅当新旧值浅比较不相等时才替换 `.value`,保持引用稳定。\n *\n * @typeParam T - 对象类型\n *\n * @param factory - 返回目标对象的工厂函数,在 watchEffect 内执行\n *\n * @returns 只读的 ShallowRef,引用仅在属性值真正变化时更新\n *\n * @example\n * ```ts\n * const props = useStableRef(() => ({\n * value: field.getValue(),\n * onChange: handleChange,\n * }))\n *\n * // props.value 的引用在属性值未变时保持不变\n * h(Component, props.value)\n * ```\n */\nexport function useStableRef<T extends Record<string, any>>(\n factory: () => T\n): Readonly<ShallowRef<T>> {\n const stableRef = shallowRef({} as T) as ShallowRef<T>\n\n watchEffect(() => {\n const next = factory()\n\n if (!isShallowEqual(stableRef.value, next)) {\n stableRef.value = next\n }\n })\n\n return stableRef\n}\n\nexport default useStableRef\n","/**\n * useViewSchemas - Vue ViewSchemas bridge.\n *\n * @module hooks/useViewSchemas\n */\n\nimport { onScopeDispose, shallowRef } from \"vue\"\nimport type { ShallowRef } from \"vue\"\n\nimport type { SchemxInstance, SchemxViewSchema, Values } from \"@schemx/core\"\n\nexport function useViewSchemas<T extends Values = Values>(\n form: SchemxInstance<T>\n): ShallowRef<readonly SchemxViewSchema<T>[]> {\n const viewSchemas = shallowRef<readonly SchemxViewSchema<T>[]>(form.getViewSchemas())\n\n const unsubscribe = form.subscribeViewSchemas((nextSchemas) => {\n viewSchemas.value = nextSchemas\n })\n\n onScopeDispose(unsubscribe)\n\n return viewSchemas\n}\n\nexport default useViewSchemas\n","/**\n * 校验触发工具\n *\n * 提供校验触发时机的合并、归一化和匹配判断功能。\n *\n * @module utils/validation\n */\n\nimport type { ValidationTrigger } from \"@schemx/core\"\n\n/** 触发时机类型(支持单个或数组) */\nexport type TriggerConfig = ValidationTrigger | ValidationTrigger[]\n\n/** 归一化后的触发类型 */\nexport type NormalizedTrigger = \"blur\" | \"change\" | \"submit\"\n\n/**\n * 判断触发时机配置是否有效。\n *\n * undefined 和空数组视为无效,其余值(包括单个字符串、非空数组)视为有效。\n *\n * @param v - 触发时机配置\n *\n * @returns 配置有效时返回 true\n */\nfunction isValidTrigger(v: TriggerConfig | undefined): v is TriggerConfig {\n if (v === undefined) return false\n if (Array.isArray(v) && v.length === 0) return false\n\n return true\n}\n\n/**\n * 合并校验触发时机配置。\n *\n * 按优先级从高到低取值:列级配置 > 表单上下文配置 > 默认值。\n * 跳过 undefined 和空数组,确保空数组不会意外覆盖后续配置。\n *\n * @param columnTrigger - 列级配置的触发时机\n * @param contextTrigger - 表单上下文配置的触发时机\n * @param defaultTrigger - 兜底默认值\n *\n * @returns 最终生效的触发时机\n *\n * @example\n * mergeTrigger(\"onBlur\", \"onChange\", \"onSubmit\") // => \"onBlur\"\n * mergeTrigger(undefined, \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger([], \"onChange\", \"onSubmit\") // => \"onChange\"\n * mergeTrigger(undefined, undefined, \"onSubmit\") // => \"onSubmit\"\n */\nexport function mergeTrigger(\n columnTrigger: TriggerConfig | undefined,\n contextTrigger: TriggerConfig | undefined,\n defaultTrigger: TriggerConfig\n): TriggerConfig {\n if (isValidTrigger(columnTrigger)) return columnTrigger\n\n if (isValidTrigger(contextTrigger)) return contextTrigger\n\n return defaultTrigger\n}\n\n/**\n * 归一化触发类型字符串。\n *\n * 将带 `on` 前缀的格式统一为短格式:\n * `\"onBlur\"` → `\"blur\"`、`\"onChange\"` → `\"change\"`、`\"onSubmit\"` → `\"submit\"`。\n *\n * @param t - 原始触发类型\n *\n * @returns 归一化后的触发类型\n */\nfunction normalizeTrigger(t: ValidationTrigger): NormalizedTrigger {\n const map: Record<ValidationTrigger, NormalizedTrigger> = {\n onBlur: \"blur\",\n onChange: \"change\",\n onSubmit: \"submit\",\n blur: \"blur\",\n change: \"change\",\n submit: \"submit\",\n }\n\n return map[t] ?? \"submit\"\n}\n\n/**\n * 判断当前事件是否应该触发校验。\n *\n * 将配置的触发时机归一化后,与当前事件类型进行匹配。\n *\n * @param event - 当前触发的事件类型\n * @param trigger - 配置的触发时机(支持单个或数组)\n *\n * @returns 是否应该触发校验\n *\n * @example\n * shouldValidateOn(\"change\", \"onChange\") // => true\n * shouldValidateOn(\"blur\", [\"onBlur\", \"onChange\"]) // => true\n * shouldValidateOn(\"change\", \"onSubmit\") // => false\n * shouldValidateOn(\"change\", undefined) // => false\n */\nexport function shouldValidateOn(\n event: NormalizedTrigger,\n trigger?: TriggerConfig\n): boolean {\n if (!trigger) return false\n const triggers = Array.isArray(trigger) ? trigger : [trigger]\n\n return triggers.some((t) => normalizeTrigger(t) === event)\n}\n","/**\n * 插槽解析与子插槽提取工具。\n *\n * 提供 camelCase / kebab-case 双向查找插槽,\n * 以及按字段名前缀提取子渲染器插槽。\n *\n * @module utils/slot\n */\n\n/**\n * 检查字符串是否包含大写字母(驼峰格式特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含大写字母\n */\nconst isCamelCase = (str: string): boolean => /[A-Z]/.test(str)\n\n/**\n * 检查字符串是否包含连字符(kebab-case 特征)。\n *\n * @param str - 待检测的字符串\n * @returns 是否包含连字符\n */\nconst isKebabCase = (str: string): boolean => str.includes(\"-\")\n\n/**\n * 将驼峰命名转换为连字符命名(kebab-case)。\n *\n * @param str - 驼峰格式的字符串\n * @returns 转换后的 kebab-case 字符串\n */\nconst camelToKebab = (str: string): string => {\n return str.replace(/([A-Z])/g, \"-$1\").toLowerCase()\n}\n\n/**\n * 将连字符命名转换为驼峰命名(camelCase)。\n *\n * @param str - kebab-case 格式的字符串\n * @returns 转换后的 camelCase 字符串\n */\nconst kebabToCamel = (str: string): string => {\n return str.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())\n}\n\n/**\n * 将命名格式归一化为 kebab-case。\n *\n * @param str - 任意命名格式的字符串\n * @returns kebab-case 格式的字符串\n */\nconst normalizeToKebab = (str: string): string => {\n if (isCamelCase(str)) return camelToKebab(str)\n\n return str\n}\n\n/**\n * 将命名格式归一化为 camelCase。\n *\n * @param str - 任意命名格式的字符串\n * @returns camelCase 格式的字符串\n */\nconst normalizeToCamel = (str: string): string => {\n if (isKebabCase(str)) return kebabToCamel(str)\n\n return str\n}\n\n/**\n * 从插槽对象中按名称查找插槽,同时支持 camelCase 和 kebab-case。\n *\n * 优先查找原始名称,未找到时尝试另一种命名格式。\n * 例如传入 `userName` 时,先查 `userName`,再查 `user-name`;\n * 传入 `user-name` 时,先查 `user-name`,再查 `userName`。\n *\n * @param slots - Vue 插槽对象\n * @param name - 插槽名称(camelCase 或 kebab-case)\n *\n * @returns 匹配的插槽函数,未找到返回 undefined\n *\n * @example\n * ```typescript\n * // slots 中定义了 'user-name' 插槽\n * resolveSlot(slots, 'userName') // => 找到 'user-name' 插槽\n * resolveSlot(slots, 'user-name') // => 找到 'user-name' 插槽\n * ```\n */\nexport const resolveSlot = (\n slots: Record<string, any>,\n name: string\n): ((...args: any[]) => any) | undefined => {\n if (slots[name]) return slots[name]\n\n const alt = isCamelCase(name)\n ? camelToKebab(name)\n : isKebabCase(name)\n ? kebabToCamel(name)\n : undefined\n\n if (alt && slots[alt]) return slots[alt]\n\n return undefined\n}\n\n/**\n * 从父级插槽中提取子渲染器插槽。\n *\n * 按 `fieldName:slotName` 和 `fieldName:slot-name` 格式匹配,\n * 提取后去掉前缀作为子渲染器的插槽名。\n * 同时支持 camelCase 和 kebab-case 的 fieldName。\n *\n * @param fieldName - 字段名\n * @param allSlots - 父级全部插槽\n *\n * @returns 子渲染器的插槽对象\n *\n * @example\n * ```typescript\n * // 父级插槽: { 'userName:prefix': fn, 'userName:suffix': fn, 'userNameLabel': fn }\n * extractChildSlots('userName', slots)\n * // => { prefix: fn, suffix: fn }\n * ```\n */\nexport const extractChildSlots = (\n fieldName: string,\n allSlots: Record<string, any>\n): Record<string, any> => {\n const result: Record<string, any> = {}\n const camelPrefix = normalizeToCamel(String(fieldName)) + \":\"\n const kebabPrefix = normalizeToKebab(String(fieldName)) + \":\"\n\n for (const [key, value] of Object.entries(allSlots)) {\n if (key.startsWith(camelPrefix)) {\n result[key.slice(camelPrefix.length)] = value\n } else if (key.startsWith(kebabPrefix) && kebabPrefix !== camelPrefix) {\n result[key.slice(kebabPrefix.length)] = value\n }\n }\n\n return result\n}\n","/**\n * FormGroup - 分组字段组件\n *\n * 用于渲染 componentType 为 \"group\" 的分组字段配置。\n * 支持可折叠的分组容器,内部渲染子 columns。\n *\n * @module components/FormGroup\n */\n\nimport { defineComponent, PropType, ref } from \"vue\"\nimport type { SetupContext, VNodeChild } from \"vue\"\n\nimport classnames from \"classnames\"\n\nimport FormItem from \"../FormItem\"\n\nimport type { SchemxViewGroupSchema, SchemxViewSchema, Values } from \"@schemx/core\"\n\n/**\n * FormGroup Props\n *\n * @typeParam T - 表单值类型\n */\nexport interface SchemxGroupProps<T extends Values = Values> {\n schema: SchemxViewGroupSchema<T>\n}\n\nconst FormGroup = defineComponent(\n <T extends Values = Values>(props: SchemxGroupProps<T>, { slots }: SetupContext) => {\n const collapsed = ref(Boolean(props.schema.defaultCollapsed))\n\n const toggle = () => {\n if (props.schema.collapsible) {\n collapsed.value = !collapsed.value\n }\n }\n\n return (): VNodeChild => {\n const schema = props.schema\n const collapsible = Boolean(schema.collapsible)\n\n return (\n <div\n class={classnames(\n \"schemx-group\",\n { \"schemx-group--collapsed\": collapsed.value },\n schema.class as string | undefined\n )}\n data-key={schema.key}\n >\n {schema.label && (\n <div\n role={collapsible ? \"button\" : undefined}\n tabindex={collapsible ? 0 : undefined}\n class={classnames(\"schemx-group__header\", {\n \"schemx-group__header--clickable\": collapsible,\n })}\n onClick={toggle}\n onKeydown={(e: KeyboardEvent) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault()\n toggle()\n }\n }}\n >\n <span class=\"schemx-group__title\">{schema.label}</span>\n {collapsible && (\n <span\n class={classnames(\"schemx-group__arrow\", {\n \"schemx-group__arrow--down\": !collapsed.value,\n })}\n />\n )}\n </div>\n )}\n {!collapsed.value && (\n <div class=\"schemx-group__body\">\n {schema.children.map((child) => (\n <FormItem\n key={child.key}\n schema={child as SchemxViewSchema}\n v-slots={slots}\n />\n ))}\n </div>\n )}\n </div>\n )\n }\n },\n {\n name: \"SchemxGroup\",\n\n props: {\n schema: {\n type: Object as PropType<SchemxViewGroupSchema>,\n required: true,\n },\n },\n }\n)\n\nexport default FormGroup\n","/**\n * FormItem\n *\n * schemx 和 FormGroup 实际渲染字段的组件。\n * 动态属性已由 core 解析到 ViewSchema 中;这里只负责消费\n * 已解析 schema、创建字段实例、组装渲染器属性与插槽。\n *\n * @module components/FormItem\n */\n\nimport { computed, defineComponent, h, PropType, toRef } from \"vue\"\nimport type { VNodeChild } from \"vue\"\n\nimport classnames from \"classnames\"\n\nimport {\n createFieldContext,\n useFormConfigContext,\n useField,\n useFormContext,\n useStableRef,\n} from \"../../hooks\"\nimport {\n extractChildSlots,\n mergeTrigger,\n resolveSlot,\n shouldValidateOn,\n} from \"../../utils\"\nimport type { TriggerConfig } from \"@/utils\"\n\nimport FormGroup from \"../FormGroup\"\n\nimport type {\n FieldValue,\n NamePath,\n SchemxComponentProps,\n SchemxViewFieldSchema,\n SchemxViewGroupSchema,\n SchemxViewSchema,\n Values,\n} from \"@schemx/core\"\n\n/**\n * FormItem 属性。\n *\n * @typeParam T - 表单值类型\n */\nexport interface SchemxItemProps<T extends Values = Values> {\n schema: SchemxViewSchema<T>\n}\n\nconst FormItem = defineComponent({\n name: \"SchemxItem\",\n\n props: {\n schema: {\n type: Object as PropType<SchemxViewSchema>,\n required: true,\n },\n },\n\n setup(props, { slots }) {\n return (): VNodeChild => {\n const schema = props.schema\n\n if (isViewGroupSchema(schema)) {\n return h(FormGroup, { schema }, slots)\n }\n\n return h(FieldFormItem, { schema }, slots)\n }\n },\n})\n\nconst FieldFormItem = defineComponent({\n name: \"SchemxFieldItem\",\n\n props: {\n schema: {\n type: Object as PropType<SchemxViewFieldSchema>,\n required: true,\n },\n },\n\n setup(props, { slots }) {\n const schemaRef = toRef(props, \"schema\")\n const form = useFormContext<Values>()\n const formContext = useFormConfigContext()\n\n const schema = () => schemaRef.value\n\n const field = useField(schema().name)\n\n createFieldContext(field)\n\n const trigger = computed<TriggerConfig>(() =>\n mergeTrigger(schema().validationTrigger, formContext.validationTrigger, \"onChange\")\n )\n\n /**\n * 是否需要进行校验。\n *\n * 当字段不可见、详情展示、只读或禁用时,无需进行校验。\n */\n const canVerified = computed(() => {\n const isOperate = schema().visible && !schema().readonly && !schema().disabled\n\n const rules = schema().rules\n\n const hasRules = Array.isArray(rules) ? rules?.length > 0 : !!schema().rules\n\n return isOperate && hasRules\n })\n\n /** 值变化处理,设置值后根据触发时机决定是否校验 */\n const handleChange = (v: FieldValue<Values, NamePath<Values>>) => {\n field.setValue(v)\n schema().componentProps?.onChange?.(v)\n\n if (canVerified.value && shouldValidateOn(\"change\", trigger.value)) {\n field.validate()\n }\n }\n\n /** 失焦处理,根据触发时机决定是否校验 */\n const handleBlur = () => {\n // schema().componentProps?.onBlur?.()\n\n if (canVerified.value && shouldValidateOn(\"blur\", trigger.value)) {\n field.validate()\n }\n }\n\n // 使用 useStableRef 避免每次生成新对象引用\n const componentProps = useStableRef<SchemxComponentProps<Values>>(\n (): SchemxComponentProps<Values> => {\n const currentSchema = schema()\n\n return {\n ...currentSchema.componentProps,\n readonly: currentSchema.readonly,\n disabled: currentSchema.disabled,\n placeholder: currentSchema.placeholder,\n formItemProps: currentSchema,\n value: field.getValue(),\n onChange: handleChange,\n onBlur: handleBlur,\n \"onUpdate:value\": (v) => field.setValue(v),\n }\n }\n )\n\n /**\n * 渲染 required 星号。\n *\n * 当字段为必填且非详情展示/禁用/只读状态时,在 label 前显示红色星号标记。\n *\n * @returns 星号 VNode 或空片段\n */\n const renderRequired = (): VNodeChild => {\n if (!schema().required || schema().disabled || schema().readonly) {\n return null\n }\n\n return <span class=\"schemx-item__required\">*</span>\n }\n\n /**\n * 渲染 formItem label 区域。\n *\n * 优先使用 `{name}Label` 插槽(支持 camelCase / kebab-case),\n * 未提供时渲染默认 label(含 required 星号、label 文本、冒号)。\n *\n * @returns label VNode\n */\n const renderLabel = (): VNodeChild => {\n const labelSlot = resolveSlot(slots, `${schema().name}Label`)\n\n if (labelSlot) {\n return labelSlot(schema())\n }\n\n const labelAlign = schema().labelAlign || formContext.labelAlign\n const labelWidth = schema().labelWidth || formContext.labelWidth\n const colon = schema().colon ?? formContext.colon\n\n return (\n <label\n class=\"schemx-item__label\"\n style={{ width: labelWidth, textAlign: labelAlign }}\n >\n {renderRequired()}\n <span class=\"schemx-item__label-text\">\n {schema().label}\n {colon ? \":\" : \"\"}\n </span>\n </label>\n )\n }\n\n /**\n * 渲染 formItem content 区域(仅控件)。\n *\n * 优先使用 `{name}Content` 插槽(支持 camelCase / kebab-case),\n * 插槽参数包含 formItemProps 和 columnElement(渲染器 VNode)。\n * 未提供插槽时,渲染默认控件布局。\n *\n * @returns content VNode\n */\n const renderContent = (): VNodeChild => {\n const component = form.getRenderer(schema().componentType)\n\n if (!component) {\n throw new Error(\n `[schemx] Can not find component renderer of \"${schema().componentType}\".`\n )\n }\n\n // 提取子渲染器插槽(fieldName:slotName 格式)\n const childSlots = extractChildSlots(normalizeNameKey(schema().name), slots)\n const columnElement = h(component, componentProps.value, childSlots)\n\n const contentSlot = resolveSlot(slots, `${schema().name}Content`)\n if (contentSlot) {\n return contentSlot({\n ...schema(),\n columnElement,\n })\n }\n\n return <div class=\"schemx-item__control\">{columnElement}</div>\n }\n\n /**\n * 渲染 formItem error 区域。\n *\n * 优先使用 `{name}Error` 插槽(支持 camelCase / kebab-case),\n * 插槽参数包含 formItemProps 和 errors 数组。\n * 未提供插槽时,仅在存在错误时显示第一条错误信息。\n *\n * @returns error VNode 或 null\n */\n const renderError = (): VNodeChild => {\n const errorSlot = resolveSlot(slots, `${schema().name}Error`)\n\n if (errorSlot) {\n return errorSlot({\n ...schema(),\n errors: field.error.value,\n })\n }\n\n if (!Array.isArray(field.error.value) || field.error.value.length === 0) {\n return null\n }\n\n return <div class=\"schemx-item__error\">{field.error.value[0]}</div>\n }\n\n return (): VNodeChild => {\n if (!schema().visible) {\n return null\n }\n\n // 整体插槽:完全接管渲染,不包裹任何默认结构\n const itemSlot = resolveSlot(slots, normalizeNameKey(schema().name))\n\n if (itemSlot) {\n return itemSlot(schema())\n }\n\n const labelPosition = schema().labelPosition || formContext.labelPosition\n\n return (\n <div class={classnames(\"schemx-item-wrapper\")}>\n <div\n class={classnames(\n \"schemx-item\",\n `schemx-item--label-${labelPosition}`,\n schema().class,\n {\n \"is-readonly\": schema().readonly,\n \"is-disabled\": schema().disabled,\n }\n )}\n style={{ ...(schema().style ?? {}) }}\n >\n {renderLabel()}\n\n <div class=\"schemx-item__content\">\n {renderContent()}\n {renderError()}\n </div>\n </div>\n </div>\n )\n }\n },\n})\n\nexport default FormItem\n\nconst isViewGroupSchema = <T extends Values>(\n schema: SchemxViewSchema<T>\n): schema is SchemxViewGroupSchema<T> => {\n return schema.componentType === \"group\"\n}\n\nconst normalizeNameKey = (name: unknown): string => {\n if (Array.isArray(name)) {\n return name.map((part) => String(part)).join(\".\")\n }\n\n return String(name)\n}\n","import { SchemxViewSchema } from \"@schemx/core\"\n\n/**\n * 判断当前项是否为顶层可见普通字段中的第一个或最后一个。\n *\n * `group` 和不可见字段不参与顶层卡片圆角判断。\n *\n * 使用示例:\n *\n * const list = [\n * { key: 'group-1', type: 'group' },\n * { key: 'a' },\n * { key: 'b' },\n * { key: 'group-2', type: 'group' },\n * { key: 'c' },\n * ]\n *\n * getSectionPosition(list, 'a')\n * // => { found: true, isFirst: true, isLast: false }\n */\nexport function getSectionPosition<T extends SchemxViewSchema>(\n list: T[],\n currentKey: string\n) {\n const currentIndex = list.findIndex((item) => item.key === currentKey)\n\n if (currentIndex === -1) {\n return {\n found: false,\n isFirst: false,\n isLast: false,\n }\n }\n\n const currentItem = list[currentIndex]\n\n if (!isPositionItem(currentItem)) {\n return {\n found: true,\n isFirst: false,\n isLast: false,\n }\n }\n\n const prevItem = findPositionItem(list, currentIndex, -1)\n const nextItem = findPositionItem(list, currentIndex, 1)\n\n return {\n found: true,\n isFirst: !prevItem,\n isLast: !nextItem,\n }\n}\n\n/**\n * 判断当前项是否参与顶层首尾样式计算。\n */\nfunction isPositionItem<T extends SchemxViewSchema>(item?: T) {\n return (\n !!item &&\n item.componentType !== \"group\" &&\n (!(\"visible\" in item) || item.visible !== false)\n )\n}\n\nfunction findPositionItem<T extends SchemxViewSchema>(\n list: T[],\n startIndex: number,\n step: 1 | -1\n) {\n for (let index = startIndex + step; index >= 0 && index < list.length; index += step) {\n if (isPositionItem(list[index])) {\n return list[index]\n }\n }\n\n return undefined\n}\n","<!--\n schemx - 主组件\n\n 基于 Schema 配置的表单组件,使用 useForm Hook 管理状态。\n\n @module schemx\n-->\n\n<script lang=\"ts\" setup generic=\"T extends Values = Values\">\n import { watch, watchEffect } from \"vue\"\n\n import { omit } from \"es-toolkit\"\n\n import FormItem from \"./components/FormItem\"\n import { createFormConfigContext, useForm, useViewSchemas } from \"./hooks\"\n import { getSectionPosition } from \"./utils/helpers\"\n\n import type { SchemxFormProps } from \"./types/index\"\n import type { SchemxViewSchema, Values } from \"@schemx/core\"\n\n import \"./styles/index.css\"\n import { createFormContext } from \"./hooks/provideFormContext\"\n\n defineOptions({ name: \"SchemxForm\" })\n\n const props = withDefaults(defineProps<SchemxFormProps<T>>(), {\n modelValue: () => ({}) as T,\n initialValues: () => ({}) as T,\n form: undefined,\n schemas: () => [],\n class: \"\",\n style: () => ({}),\n onFinish: undefined,\n onFinishFailed: undefined,\n onValuesChange: undefined,\n onFieldsChange: undefined,\n rendererRegistry: undefined,\n validatorRegistry: undefined,\n })\n\n const emit = defineEmits<{\n \"update:modelValue\": [value: T]\n }>()\n\n /**\n * 创建 FormContext 上下文\n *\n * 为子组件提供表单配置信息。\n */\n createFormConfigContext(\n omit(props, [\n \"form\",\n \"modelValue\",\n \"rendererRegistry\",\n \"defaultRendererType\",\n \"validatorRegistry\",\n \"onFinish\",\n \"onFinishFailed\",\n \"onValuesChange\",\n \"onFieldsChange\",\n ])\n )\n\n /**\n * 获取或创建表单实例\n *\n * 优先使用外部传入的 form,否则内部通过 useForm 创建。\n * 必须在 setup 同步阶段调用,确保 provide 正确注入。\n */\n const form = props.form\n ? props.form\n : useForm<T>({\n schemas: props.schemas,\n initialValues: props.initialValues,\n rendererRegistry: props.rendererRegistry,\n defaultRendererType: props.defaultRendererType,\n validatorRegistry: props.validatorRegistry,\n\n readonly: props.readonly,\n disabled: props.disabled,\n\n onFinish: async (values) => {\n props.onFinish?.(values)\n },\n onFinishFailed: async (errors) => {\n props.onFinishFailed?.(errors)\n },\n onValuesChange: (changedValues, latestSnapshot) => {\n emit(\"update:modelValue\", latestSnapshot as T)\n props.onValuesChange?.(changedValues, latestSnapshot)\n },\n onFieldsChange: (changedPaths, allPaths) => {\n props.onFieldsChange?.(changedPaths, allPaths)\n },\n })\n\n /**\n * 注册表单上下文。\n *\n * 无论实例来自 props.form 还是 useForm,都必须同步注册,\n * 从而保证 FormItem、useField 等后代逻辑能够获取同一个实例。\n */\n createFormContext(form)\n\n watch(\n () => props.schemas,\n (schemas) => {\n form.setSchemas(schemas)\n },\n { deep: false, immediate: !!props.form }\n )\n\n const viewSchemas = useViewSchemas(form)\n\n const getFormItemClass = (schema: SchemxViewSchema<T>) => {\n const { isFirst, isLast } = getSectionPosition(\n viewSchemas.value as SchemxViewSchema[],\n schema.key\n )\n\n return {\n \"schemx-item-wrapper--first\": isFirst,\n \"schemx-item-wrapper--last\": isLast,\n }\n }\n\n watchEffect(() => {\n form.updateDefaultProps(props)\n })\n\n defineExpose({\n ...form,\n })\n</script>\n\n<template>\n <div :class=\"['schemx', props.class]\">\n <FormItem\n v-for=\"schema in viewSchemas\"\n :key=\"schema.key\"\n :schema=\"schema as SchemxViewSchema\"\n :class=\"getFormItemClass(schema as SchemxViewSchema<T>)\"\n >\n <template v-for=\"(_, slotName) in $slots\" #[slotName]=\"slotProps\">\n <slot :name=\"slotName\" v-bind=\"slotProps ?? {}\" />\n </template>\n </FormItem>\n </div>\n</template>\n","/**\n * schemx 组件导出\n *\n * 为 SchemxForm 组件挂载静态方法(install、FormItem、registerRequest 等),\n * 并作为默认导出。\n *\n * @module formExport\n */\n\nimport type { App, DefineComponent } from \"vue\"\n\nimport FormItem from \"./components/FormItem\"\nimport SchemxForm from \"./formRuntime.js\"\n\nimport type { SchemxFormProps } from \"./types\"\n\n/**\n * SchemxForm 插件安装选项\n *\n * 在 `app.use(SchemxForm, options)` 时传入,用于配置全局默认行为。\n *\n * @example\n * ```ts\n * import SchemxForm from '@schemx/vue'\n *\n * app.use(SchemxForm, {\n * request: (url) => fetch(url).then(r => r.json()),\n * })\n * ```\n */\nexport interface SchemxInstallOptions {}\n\n/**\n * 为组件挂载静态属性并保留原始类型\n *\n * @param comp - 原始组件\n * @param extra - 要挂载的静态属性\n */\nexport function withInstall<T extends object, E extends Record<string, unknown>>(\n comp: T,\n extra: E\n) {\n return Object.assign(comp, extra) as T & E\n}\n\nexport type SchemxFormPlugin = DefineComponent<SchemxFormProps> & {\n install: (app: App, options?: SchemxInstallOptions) => void\n FormItem: typeof FormItem\n}\n\nconst SchemxFormExport = withInstall(SchemxForm, {\n /** Vue 插件安装方法 */\n install(app: App, _options?: SchemxInstallOptions) {\n app.component(\"SchemxForm\", SchemxForm)\n },\n /** FormItem 子组件引用 */\n FormItem,\n}) as unknown as SchemxFormPlugin\n\nexport default SchemxFormExport\n","/**\n * WithRemoteOptions - 选项高阶组件\n *\n * 包装任意渲染器组件,自动注入 useDictionary 的选项加载能力。\n * 被包装组件会额外收到 options、loading 两个 props,\n * 无需自行调用 useDictionary。\n *\n * @module hocs/withRemoteOptions\n */\n\nimport { Component, computed, defineComponent, h, PropType, SetupContext } from \"vue\"\n\nimport type { SchemxDictionary } from \"@/types/dictionary\"\n\nimport { useDictionary } from \"../hooks/useDictionary\"\n\nimport type { NamePath } from \"@schemx/core\"\n\n\n/**\n * WithRemoteOptions 注入给被包装组件的额外 Props\n */\nexport interface RemoteOptionsInjectedProps {\n /** 加载的选项列表 */\n options: any[]\n /** 加载状态 */\n loading: boolean\n}\n\n/**\n * 选项高阶组件\n *\n * 包装一个渲染器组件,当 dict prop 存在时,\n * 调用 useDictionary 加载选项,并将结果作为 options prop 注入。\n *\n * 当组件自身已传入非空 options 且无 dict 时,优先使用静态 options。\n *\n * @param WrappedComponent - 被包装的渲染器组件\n * @returns 增强后的组件,自动具备选项加载能力\n *\n * @example\n * ```ts\n * import { WithRemoteOptions } from '@schemx/vue'\n * import MySelect from './MySelect.vue'\n *\n * const RemoteSelect = WithRemoteOptions(MySelect)\n *\n * // 在 schema 中使用\n * const schema = {\n * name: 'city',\n * label: '城市',\n * componentType: 'select',\n * componentProps: {\n * dict: {\n * api: async (values) => fetchCities(values.province),\n * dependsOn: ['province'],\n * shouldFetch: (values) => !!values.province,\n * resetOnDepsChange: true,\n * immediate: false,\n * },\n * },\n * }\n * ```\n */\nexport function WithRemoteOptions(WrappedComponent: Component) {\n return defineComponent({\n name: `WithRemoteOptions(${WrappedComponent.name || \"Anonymous\"})`,\n inheritAttrs: false,\n props: {\n dict: {\n type: Object as PropType<SchemxDictionary>,\n default: undefined,\n },\n fieldName: {\n type: [String, Array] as PropType<NamePath>,\n default: undefined,\n },\n },\n setup(props, { attrs, slots }: SetupContext) {\n // 仅在 dict 配置存在时调用 useDictionary\n const dictResult = props.dict ? useDictionary(props.dict, props.fieldName) : null\n\n const childrenProps = computed(() => {\n return {\n ...attrs,\n dict: props.dict,\n options: props.dict ? dictResult?.list.value : attrs.options,\n loading: props.dict ? dictResult?.loading.value : attrs.loading,\n }\n })\n\n return () => h(WrappedComponent, childrenProps.value, slots)\n },\n })\n}\n\nexport default WithRemoteOptions\n"],"names":["globalRendererRegistry","globalRulesRegistry","FormGroup","defineComponent","props","slots","collapsed","ref","Boolean","schema","defaultCollapsed","toggle","collapsible","value","_createVNode","classnames","class","key","label","undefined","e","preventDefault","children","map","child","FormItem","name","type","Object","required","setup","isViewGroupSchema","h","FieldFormItem","schemaRef","toRef","form","useFormContext","formContext","useFormConfigContext","field","useField","createFieldContext","trigger","computed","mergeTrigger","validationTrigger","canVerified","isOperate","visible","readonly","disabled","rules","hasRules","Array","isArray","length","handleChange","v","setValue","componentProps","onChange","shouldValidateOn","validate","handleBlur","useStableRef","currentSchema","placeholder","formItemProps","getValue","onBlur","renderRequired","_createTextVNode","renderLabel","labelSlot","resolveSlot","labelAlign","labelWidth","colon","width","textAlign","renderContent","component","getRenderer","componentType","Error","childSlots","extractChildSlots","normalizeNameKey","columnElement","contentSlot","renderError","errorSlot","errors","error","itemSlot","labelPosition","style","part","String","join","_createElementBlock","_normalizeClass","_Fragment","_renderList","_unref","_createBlock","$slots","_withCtx","_renderSlot","_mergeProps","SchemxForm"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@schemx/vue",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"description": "基于 JSON Schema 配置的 Vue 3 动态表单引擎(UI 无关核心)",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"schemx",
|
|
@@ -47,26 +47,30 @@
|
|
|
47
47
|
"**/index.ts"
|
|
48
48
|
],
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"vue": "^3.0.0"
|
|
50
|
+
"vue": "^3.0.0",
|
|
51
|
+
"@schemx/core": "0.2.1"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
54
|
"classnames": "^2.5.1",
|
|
54
55
|
"csstype": "^3.2.3",
|
|
55
|
-
"
|
|
56
|
-
"es-toolkit": "^1.47.0",
|
|
57
|
-
"@schemx/core": "0.2.0"
|
|
56
|
+
"es-toolkit": "^1.47.0"
|
|
58
57
|
},
|
|
59
58
|
"license": "MIT",
|
|
60
59
|
"devDependencies": {
|
|
61
60
|
"@vue/test-utils": "^2.4.6",
|
|
62
61
|
"vue": "^3.4.0",
|
|
63
|
-
"zod": "^4.3.6"
|
|
62
|
+
"zod": "^4.3.6",
|
|
63
|
+
"@schemx/core": "0.2.1"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "vite build",
|
|
67
|
-
"build:analyze": "
|
|
67
|
+
"build:analyze": "vite build",
|
|
68
68
|
"test": "vitest run",
|
|
69
69
|
"lint": "eslint \"src/**/*.{ts,tsx,vue,js,jsx}\"",
|
|
70
|
-
"
|
|
70
|
+
"lint:fix": "eslint \"src/**/*.{ts,tsx,vue,js,jsx}\" --fix",
|
|
71
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,vue,js,jsx,json,css,scss}\"",
|
|
72
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx,vue,js,jsx,json,css,scss}\"",
|
|
73
|
+
"type-check": "tsc -p tsconfig.json --noEmit",
|
|
74
|
+
"check": "pnpm run lint && pnpm run format:check && pnpm run type-check"
|
|
71
75
|
}
|
|
72
76
|
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { SchemxFormProps } from '../types';
|
|
2
|
-
import { Values } from '@schemx/core';
|
|
3
|
-
|
|
4
|
-
/** FormContext 注入 key */
|
|
5
|
-
export declare const FORM_CONTEXT_KEY: unique symbol;
|
|
6
|
-
/**
|
|
7
|
-
* 表单上下文属性
|
|
8
|
-
*
|
|
9
|
-
* 由 schemx 组件注入,包含表单级别的全局配置。
|
|
10
|
-
*
|
|
11
|
-
* @typeParam T - 表单值类型
|
|
12
|
-
*/
|
|
13
|
-
export interface FormContextProps<T extends Values = Values> extends Omit<SchemxFormProps<T>, "form" | "modelValue" | "rendererRegistry" | "defaultRendererType" | "rulesRegistery" | "onFinish" | "onFinishFailed" | "onValuesChange" | "onFieldsChange"> {
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* 创建并注入表单上下文
|
|
17
|
-
*
|
|
18
|
-
* 在 schemx 的 setup 中调用,将表单级别配置注入到子组件树中。
|
|
19
|
-
*
|
|
20
|
-
* @typeParam T - 表单值类型
|
|
21
|
-
* @param props - 表单上下文属性
|
|
22
|
-
*/
|
|
23
|
-
export declare const createConfigContext: <T extends Values = Values>(props: FormContextProps<T>) => void;
|
|
24
|
-
/**
|
|
25
|
-
* @deprecated
|
|
26
|
-
*
|
|
27
|
-
* 已弃用,请使用 **createConfigContext**
|
|
28
|
-
*/
|
|
29
|
-
export declare const createContext: <T extends Values = Values>(props: FormContextProps<T>) => void;
|
|
30
|
-
/**
|
|
31
|
-
* 获取表单上下文配置
|
|
32
|
-
*
|
|
33
|
-
* 在子组件中获取 schemx 注入的全局配置(readonly、disabled、labelAlign 等)。
|
|
34
|
-
*
|
|
35
|
-
* @returns 表单上下文属性
|
|
36
|
-
*
|
|
37
|
-
* @throws Error 如果不在 schemx 提供的上下文中调用
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```ts
|
|
41
|
-
* const context = useConfigContext()
|
|
42
|
-
* console.log(context.readonly, context.disabled)
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
export declare function useConfigContext(): FormContextProps;
|
|
46
|
-
/**
|
|
47
|
-
* @deprecated
|
|
48
|
-
*
|
|
49
|
-
* 已弃用,请使用 **useConfigContext**
|
|
50
|
-
*/
|
|
51
|
-
export declare const useContext: typeof useConfigContext;
|
|
52
|
-
//# sourceMappingURL=useContext.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,yBAAyB;AACzB,eAAO,MAAM,gBAAgB,eAAwB,CAAA;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,IAAI,CACvE,eAAe,CAAC,CAAC,CAAC,EAChB,MAAM,GACN,YAAY,GACZ,kBAAkB,GAClB,qBAAqB,GACrB,gBAAgB,GAChB,UAAU,GACV,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,CACnB;CAAG;AAEJ;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,MAAM,GAAG,MAAM,EAC3D,OAAO,gBAAgB,CAAC,CAAC,CAAC,SAG3B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAXU,CAAC,SAAS,MAAM,kBAC3C,gBAAgB,CAAC,CAAC,CAAC,SAUoB,CAAA;AAEhD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,IAAI,gBAAgB,CAQnD;AAED;;;;GAIG;AACH,eAAO,MAAM,UAAU,yBAAmB,CAAA"}
|