@schemx/vue 0.2.3 → 1.0.0-next.0

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.
Files changed (61) hide show
  1. package/README.md +290 -229
  2. package/dist/analyze.html +1 -1
  3. package/dist/components/FormGroup/index.d.ts +3 -4
  4. package/dist/components/FormGroup/index.d.ts.map +1 -1
  5. package/dist/components/FormItem/index.d.ts +3 -4
  6. package/dist/components/FormItem/index.d.ts.map +1 -1
  7. package/dist/config/appConfig.d.ts +19 -0
  8. package/dist/config/appConfig.d.ts.map +1 -0
  9. package/dist/config/index.d.ts +2 -0
  10. package/dist/config/index.d.ts.map +1 -0
  11. package/dist/form.d.ts +11 -8
  12. package/dist/form.d.ts.map +1 -1
  13. package/dist/hocs/withRemoteOptions.d.ts +3 -29
  14. package/dist/hocs/withRemoteOptions.d.ts.map +1 -1
  15. package/dist/hooks/index.d.ts +2 -2
  16. package/dist/hooks/index.d.ts.map +1 -1
  17. package/dist/hooks/provideFieldContext.d.ts +1 -2
  18. package/dist/hooks/provideFormConfigContext.d.ts +24 -19
  19. package/dist/hooks/provideFormConfigContext.d.ts.map +1 -1
  20. package/dist/hooks/provideFormContext.d.ts +0 -1
  21. package/dist/hooks/useDictionary.d.ts +13 -8
  22. package/dist/hooks/useDictionary.d.ts.map +1 -1
  23. package/dist/hooks/useEffect.d.ts +0 -1
  24. package/dist/hooks/useField.d.ts +1 -2
  25. package/dist/hooks/useField.d.ts.map +1 -1
  26. package/dist/hooks/useForm.d.ts +2 -3
  27. package/dist/hooks/useForm.d.ts.map +1 -1
  28. package/dist/hooks/useStableRef.d.ts +2 -3
  29. package/dist/hooks/useStableRef.d.ts.map +1 -1
  30. package/dist/hooks/useViewSchemas.d.ts +16 -2
  31. package/dist/hooks/useViewSchemas.d.ts.map +1 -1
  32. package/dist/hooks/useWatch.d.ts +64 -13
  33. package/dist/hooks/useWatch.d.ts.map +1 -1
  34. package/dist/index.cjs +1 -1
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +1 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.mjs +195 -82
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/types/dictionary.d.ts +18 -18
  41. package/dist/types/dictionary.d.ts.map +1 -1
  42. package/dist/types/field.d.ts +20 -11
  43. package/dist/types/field.d.ts.map +1 -1
  44. package/dist/types/form.d.ts +41 -4
  45. package/dist/types/form.d.ts.map +1 -1
  46. package/dist/types/index.d.ts +15 -0
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/utils/diff.d.ts +2 -2
  49. package/dist/utils/diff.d.ts.map +1 -1
  50. package/dist/utils/dynamic.d.ts +13 -14
  51. package/dist/utils/dynamic.d.ts.map +1 -1
  52. package/dist/utils/equal.d.ts.map +1 -1
  53. package/dist/utils/helpers.d.ts +5 -6
  54. package/dist/utils/helpers.d.ts.map +1 -1
  55. package/dist/utils/rendererProvider.d.ts +2 -3
  56. package/dist/utils/rendererProvider.d.ts.map +1 -1
  57. package/dist/utils/rulesProvider.d.ts +5 -6
  58. package/dist/utils/rulesProvider.d.ts.map +1 -1
  59. package/dist/utils/slot.d.ts.map +1 -1
  60. package/dist/utils/validation.d.ts +0 -1
  61. package/package.json +6 -5
@@ -1 +1 @@
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 name,\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, payload) => {\n * console.log('form changed:', snapshot, payload)\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, (latestSnapshot, _payload) => {\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 * formContextProps 类型排除值\n */\nexport const formConfigContextOmitKey = [\n \"form\",\n \"modelValue\",\n \"rendererRegistry\",\n \"validatorRegistry\",\n \"defaultRendererType\",\n \"onFinish\",\n \"onFinishFailed\",\n \"onValuesChange\",\n \"onFieldsChange\",\n] as const\n\n/**\n * formContextProps 类型排除值 - 类型\n */\nexport type FormConfigContextOmitKey = (typeof formConfigContextOmitKey)[number]\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 FormConfigContextOmitKey\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 style={schema.style}\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 = (v: FieldValue<Values, NamePath<Values>>) => {\n schema().componentProps?.onBlur?.(v)\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\")} style={schema().style}>\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 ?? {}) as CSSStyleValue) }}\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 { onUnmounted, watch, watchEffect } from \"vue\"\n\n import { omit } from \"es-toolkit\"\n\n import { createWatch } from \"@schemx/core\"\n\n import FormItem from \"./components/FormItem\"\n import {\n createFormConfigContext,\n formConfigContextOmitKey,\n useForm,\n useViewSchemas,\n createFormContext,\n } 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 visible: true,\n })\n\n const emit = defineEmits<{\n \"update:modelValue\": [value: T]\n }>()\n\n /**\n * 创建 FormContext 上下文\n *\n * 为子组件提供表单配置信息。\n */\n createFormConfigContext(omit(props, formConfigContextOmitKey))\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: Object.keys(props.modelValue).length > 0\n ? props.modelValue\n : props.initialValues,\n\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 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 let syncingFromModel = false\n\n watch(\n () => props.modelValue,\n (values) => {\n syncingFromModel = true\n form.setFieldsValue(values)\n syncingFromModel = false\n }\n )\n\n const disposeWatch = createWatch(form, (latestSnapshot) => {\n if (syncingFromModel) return\n\n emit(\"update:modelValue\", latestSnapshot)\n })\n\n onUnmounted(disposeWatch)\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]\" :style=\"props.style\">\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\"\nimport { useFieldContext } from \"../hooks/provideFieldContext\"\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 // 内嵌于 FormItem 时自动从字段 Context 获取目标字段;显式 fieldName\n // 保留给脱离 FormItem 的独立使用场景。\n const fieldName =\n props.fieldName ?? (props.dict ? useFieldContext().name : undefined)\n const dictResult = props.dict ? useDictionary(props.dict, 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","style","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","onBlur","useStableRef","currentSchema","placeholder","formItemProps","getValue","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","part","String","join","_createElementBlock","_normalizeClass","_normalizeStyle","_Fragment","_renderList","_unref","_createBlock","$slots","_withCtx","_renderSlot","_mergeProps","SchemxForm"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/config/appConfig.ts","../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 * Vue App 级 Schemx 配置的 provide/inject 适配。\n *\n * 安装配置属于当前 Vue App,而不是 Core 模块级共享状态;该模块只在 Vue\n * 适配层内部保存和读取安装配置,不将注入 key 暴露为公共 API。\n *\n * @module utils/appConfig\n */\n\nimport { type App, getCurrentInstance, inject, type InjectionKey } from \"vue\"\n\nimport type { SchemxConfig } from \"@schemx/core\"\n\n// Vue App 级配置在组件树中的私有注入 key。\nconst SCHEMX_APP_CONFIG_KEY: InjectionKey<SchemxConfig> = Symbol(\"schemx:app-config\")\n\n// 没有安装配置时使用的不可变空配置。\nconst EMPTY_SCHEMX_CONFIG: SchemxConfig = Object.freeze({\n schemaConfig: Object.freeze({}),\n validatorAdapters: Object.freeze([]),\n})\n\n/**\n * 将插件安装选项复制为可安全注入的 App 配置快照。\n *\n * Registry 保留其可变实例语义,使同一 App 内创建的多个 Form 继续共享注册结果;\n * Schema 配置和 adapter 列表则复制并冻结,避免安装后修改原对象影响 App 配置。\n *\n * @param config - 插件安装时传入的配置。\n * @returns 与调用方输入隔离的 App 配置快照。\n */\nfunction normalizeSchemxAppConfig(config: SchemxConfig): SchemxConfig {\n // 复制并冻结 App 级字段默认值,避免外部对象后续变更配置。\n const schemaConfig = Object.freeze({ ...(config.schemaConfig ?? {}) })\n\n // 复制并冻结 App 级 adapter 列表,保持列表与调用方数组相互独立。\n const validatorAdapters = Object.freeze([...(config.validatorAdapters ?? [])])\n\n return Object.freeze({\n schemaConfig,\n validatorAdapters,\n defaultRendererType: config.defaultRendererType,\n rendererRegistry: config.rendererRegistry,\n validationRuleRegistry: config.validationRuleRegistry,\n })\n}\n\n/**\n * 将 Schemx 安装配置绑定到指定 Vue App。\n *\n * @param app - 要安装 Schemx 的 Vue App 实例。\n * @param config - 当前 App 的默认配置。\n */\nexport function provideSchemxAppConfig(app: App, config: SchemxConfig = {}): void {\n // 当前 App 后续组件实例应读取的配置快照。\n const normalizedConfig = normalizeSchemxAppConfig(config)\n\n app.provide(SCHEMX_APP_CONFIG_KEY, normalizedConfig)\n}\n\n/**\n * 读取当前组件所属 Vue App 的 Schemx 安装配置。\n *\n * 在组件 setup() 之外调用时,Vue 没有可用的注入上下文;此时返回空配置,\n * 使 `useForm()` 仍可在独立 effect scope 中使用 Vue 模块级 Registry。\n *\n * @returns 当前 App 的安装配置,或无 App 上下文时的空配置。\n */\nexport function getSchemxAppConfig(): SchemxConfig {\n // 只有存在当前组件实例时才读取 Vue 注入上下文,避免 setup 外触发 Vue 警告。\n if (getCurrentInstance() === null) {\n return EMPTY_SCHEMX_CONFIG\n }\n\n // 当前组件树中最近的 App 级配置,未安装插件时回退到空配置。\n return inject(SCHEMX_APP_CONFIG_KEY, EMPTY_SCHEMX_CONFIG)\n}\n","/**\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 RendererRegistry,\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: RendererRegistry<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 { createValidationRuleRegistry, type ValidationRuleRegistry } from \"@schemx/core\"\n\n/**\n * 全局校验规则注册实例\n *\n * @example\n * ```ts\n * // 注册自定义规则\n * import { validationRuleRegistry } from '@schemx/vue'\n * validationRuleRegistry.register('phone', phoneRule)\n *\n * // useForm 内部自动使用\n * // props 传入的 validationRuleRegistry 优先级更高\n * ```\n */\nexport const validationRuleRegistry: ValidationRuleRegistry =\n createValidationRuleRegistry()\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, mergeSchemxConfig, type SchemxConfig } from \"@schemx/core\"\n\nimport { getSchemxAppConfig } from \"../config\"\nimport { rendererRegistry as globalRendererRegistry } from \"../utils/rendererProvider\"\nimport { validationRuleRegistry as globalValidationRuleRegistry } from \"../utils/rulesProvider\"\n\nimport type { CreateFormOptions, NamePath, SchemxInstance, Values } from \"@schemx/core\"\n\n/**\n * useForm 配置选项。\n *\n * 当前与 core 层 CreateFormOptions 保持一致,并在 Vue 层自动补充默认的\n * rendererRegistry 和 validationRuleRegistry。保留独立类型用于后续扩展\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. 合并表单显式、App 安装和 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 // 按表单、App、Vue 包默认值的优先级解析可继承配置。\n const configuredOptions = mergeSchemxConfig(\n getUseFormSchemxConfig(options),\n getSchemxAppConfig(),\n {\n rendererRegistry: globalRendererRegistry,\n validationRuleRegistry: globalValidationRuleRegistry,\n }\n )\n\n // 将已合并配置写入 Form 创建选项,避免 Core 再按较低优先级覆盖 Vue 结果。\n const mergedOptions: CreateFormOptions<TValues> = {\n ...options,\n ...configuredOptions,\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/**\n * 从 useForm 选项中提取需要参与 Vue 优先级计算的可继承配置。\n *\n * @param options - useForm 的完整创建选项。\n * @returns 仅包含调用方实际提供字段的 SchemxConfig。\n */\nfunction getUseFormSchemxConfig<TValues extends Values>(\n options: UseFormOptions<TValues>\n): SchemxConfig {\n const {\n schemaConfig = {},\n validatorAdapters = [],\n defaultRendererType = undefined,\n rendererRegistry = undefined,\n validationRuleRegistry = undefined,\n ...rest\n } = options\n\n return {\n schemaConfig,\n defaultRendererType,\n rendererRegistry,\n validationRuleRegistry,\n validatorAdapters,\n }\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\n const fieldErrors = shallowRef(field.getErrors())\n\n const fieldPending = shallowRef(field.isPending())\n\n const dispose = field.effect(() => {\n fieldValue.value = field.getValue()\n fieldErrors.value = field.getErrors()\n\n fieldPending.value = field.isPending()\n })\n\n const errors = computed(() => fieldErrors.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 name,\n value: fieldValue,\n errors,\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.errors.value // readonly string[]\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.errors.value // readonly string[]\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, payload) => {\n * console.log('form changed:', snapshot, payload)\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 * @typeParam TValues - 表单值类型。\n * @param callback - 全局变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * const dispose = useWatch((values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues, values)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n callback: WatchAllCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听单个字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param name - 字段路径\n * @param callback - 单字段变化回调;payload 为 `{ value, prevValue }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatch(\"email\", (_values, payload) => {\n * console.log(payload.prevValue, payload.value)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n name: NamePath<TValues>,\n callback: WatchFieldCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * 监听多个字段变化\n *\n * @typeParam TValues - 表单值类型。\n * @param names - 字段路径数组\n * @param callback - 多字段变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 监听选项\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatch([\"firstName\", \"lastName\"], (_values, payload) => {\n * console.log(payload.changedPaths, payload.changedValues, payload.prevValues)\n * })\n * ```\n */\nexport function useWatch<TValues extends Values>(\n names: NamePath<TValues>[],\n callback: WatchFieldsCallback<TValues>,\n options?: CreateWatchOptions\n): () => void\n/**\n * useWatch 实现 — 获取 form 实例后委托给 core 层的 createWatch\n *\n * @typeParam TValues - 表单值类型。\n */\nexport function useWatch<TValues extends Values>(\n nameOrNamesOrCallback:\n NamePath<TValues> | NamePath<TValues>[] | WatchAllCallback<TValues>,\n callbackOrOptions?:\n WatchFieldCallback<TValues> | WatchFieldsCallback<TValues> | CreateWatchOptions,\n maybeOptions?: CreateWatchOptions\n): () => void {\n const form = useFormContext<TValues>()\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 * @typeParam TValues - 表单值类型。\n * @param name - 要监听的字段路径\n * @param callback - 字段变化回调;payload 为 `{ value, prevValue }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchField(\"email\", (_values, payload) => {\n * const { value, prevValue } = payload\n * console.log(prevValue, value)\n * })\n * ```\n */\nexport function useWatchField<TValues extends Values = Values>(\n name: NamePath<TValues>,\n callback: WatchFieldCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<TValues>(name, callback, options)\n}\n\n/**\n * 监听多个字段变化的便捷方法\n *\n * 等价于 `useWatch(names, callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param names - 要监听的字段路径数组\n * @param callback - 字段变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchFields([\"firstName\", \"lastName\"], (_values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues)\n * })\n * ```\n */\nexport function useWatchFields<TValues extends Values>(\n names: NamePath<TValues>[],\n callback: WatchFieldsCallback<TValues>,\n options?: CreateWatchOptions\n): () => void {\n return useWatch<TValues>(names, callback, options)\n}\n\n/**\n * 监听所有字段变化的便捷方法\n *\n * 等价于 `useWatch(callback, options)`,语义更明确。\n * 自动绑定 formContext 并在组件卸载时取消监听。\n *\n * @typeParam TValues - 表单值类型。\n * @param callback - 全局变化回调;payload 为 `{ changedPaths, changedValues, prevValues }`\n * @param options - 可选配置\n * @returns 取消监听函数\n *\n * @example\n * ```ts\n * useWatchAll((_values, payload) => {\n * const { changedPaths, changedValues, prevValues } = payload\n * console.log(changedPaths, changedValues, prevValues)\n * })\n * ```\n */\nexport function useWatchAll<TValues extends Values = Values>(\n callback: WatchAllCallback<TValues>,\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, shallowRef } 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<TOption = unknown> {\n /** 远程加载的字典选项列表(响应式) */\n list: Ref<TOption[]>\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: TOption[]) => void\n}\n\n/**\n * @deprecated\n *\n * 已弃用,请使用 **UseDictionaryReturn**\n */\nexport type UseDictOptionsReturn<TOption = unknown> = UseDictionaryReturn<TOption>\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 `{ list, loading, error, loadDict, refresh, mutate }`:响应式选项、\n * 加载与错误状态,以及加载、刷新和本地更新方法\n *\n * @example\n * ```ts\n * // 基础用法\n * const { list, loading, error, loadDict, refresh, mutate } = useDictionary({\n * api: async () => {\n * const res = await fetch('/api/options')\n * return res.json()\n * },\n * })\n *\n * await loadDict() // 主动加载\n * await refresh() // 使用当前配置重新加载\n * mutate([{ label: \"手动选项\", value: \"manual\" }])\n * console.log(list.value, loading.value, error.value)\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 TResponse = unknown,\n TOption = unknown,\n>(\n options: SchemxDictionary<TValues, TResponse, TOption>,\n fieldName?: TName\n): UseDictionaryReturn<TOption> => {\n const instance = useFormContext<TValues>()\n\n const list = shallowRef<TOption[]>([])\n\n const loading = ref<boolean>(false)\n\n const error = ref<Error | undefined>(undefined)\n\n // 竞态控制:仅最新请求写入状态\n let requestCount = 0\n\n /**\n * 使用配置的 formatter 格式化原始响应数据\n */\n const format = async (res: Awaited<TResponse>): Promise<TOption[]> => {\n if (typeof options?.formatter === \"function\") {\n return await options.formatter(res, instance)\n }\n\n if (!Array.isArray(res)) {\n throw new Error(\n \"[schemx] Dictionary api must return an array when formatter is not provided.\"\n )\n }\n\n return res as TOption[]\n }\n\n /**\n * 带重试的执行\n */\n const executeWithRetry = async (formValues: TValues): Promise<Awaited<TResponse>> => {\n const maxRetries = options.retryCount ?? 0\n\n const retryDelay = options.retryInterval ?? 1000\n\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: TOption[]): void => {\n list.value = data\n }\n\n // ========== 依赖字段监听 ==========\n if (options.dependsOn?.length) {\n useWatchFields(options.dependsOn, (latestSnapshot, _payload) => {\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 { SchemxSchemaConfig, Values } from \"@schemx/core\"\n\n/** 表单级展示配置在 Vue provide/inject 中使用的注入 key。 */\nexport const SCHEMX_FORM_CONFIG_KEY = Symbol(\n \"schemx:form-config\"\n) as InjectionKey<FormContextProps>\n\n/**\n * 表单级默认配置。\n *\n * 该类型只描述 Schema 通用配置,不包含表单实例、表单数据、回调、样式或 Registry。\n *\n * @typeParam TValues - 表单值类型\n */\nexport interface FormContextProps {\n schemaConfig: Partial<SchemxSchemaConfig>\n}\n\n/**\n * 创建并注入表单级配置上下文。\n *\n * 应在 `SchemxForm` 或自定义 Provider 的 setup() 同步阶段调用,\n * 使后代字段组件能够读取 readonly、disabled、labelAlign 等默认配置。\n *\n * @typeParam TValues - 表单值类型\n * @param props - 要提供给后代组件的 Schema 配置\n *\n * @remarks\n * 该函数只注册上下文,不创建或销毁表单实例,也不改变传入配置的所有权。\n *\n * @example\n * ```ts\n * createFormConfigContext({ schemaConfig: { readonly: true, labelAlign: \"right\" } })\n * ```\n */\nexport const createFormConfigContext = <TValues extends Values = Values>(\n props: FormContextProps\n): void => {\n provide<FormContextProps>(SCHEMX_FORM_CONFIG_KEY, props)\n}\n\n/**\n * `createFormConfigContext` 的兼容别名。\n *\n * @deprecated 请使用 createFormConfigContext。\n *\n * @example\n * ```ts\n * createContext({ schemaConfig: { disabled: true } })\n * ```\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.schemaConfig.readonly, context.schemaConfig.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 *\n * @example\n * ```ts\n * const context = useContext()\n * ```\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\n const keysB = Object.keys(b)\n\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 TValue - 需要保持引用稳定的对象类型\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<TValue extends Record<string, any>>(\n factory: () => TValue\n): Readonly<ShallowRef<TValue>> {\n const stableRef = shallowRef({} as TValue) as ShallowRef<TValue>\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\n/**\n * 将表单 ViewSchema 映射为 Vue 响应式引用,并在当前作用域销毁时自动取消订阅。\n *\n * @typeParam TValues - 表单值类型。\n * @param form - 要订阅的表单实例。\n * @returns 当前 ViewSchema 列表的只读浅引用。\n *\n * @example\n * ```ts\n * const form = useForm()\n * const viewSchemas = useViewSchemas(form)\n *\n * watchEffect(() => console.log(viewSchemas.value))\n * ```\n */\nexport function useViewSchemas<TValues extends Values = Values>(\n form: SchemxInstance<TValues>\n): ShallowRef<readonly SchemxViewSchema<TValues>[]> {\n const viewSchemas = shallowRef<readonly SchemxViewSchema<TValues>[]>(\n form.getViewSchemas()\n )\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\n const camelPrefix = normalizeToCamel(String(fieldName)) + \":\"\n\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 * 用于渲染包含 children 的 Group ViewSchema。\n * 支持可折叠的分组容器,内部渲染子 columns。\n *\n * @module components/FormGroup\n */\n\nimport { computed, defineComponent, getCurrentInstance, PropType, ref, watch } 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 TValues - 表单值类型\n */\nexport interface SchemxGroupProps<TValues extends Values = Values> {\n schema: SchemxViewGroupSchema<TValues>\n}\n\nconst FormGroup = defineComponent(\n <TValues extends Values = Values>(\n props: SchemxGroupProps<TValues>,\n { slots }: SetupContext\n ) => {\n const internalCollapsed = ref(Boolean(props.schema.defaultCollapsed))\n\n const collapsed = computed(() => props.schema.collapsed ?? internalCollapsed.value)\n\n const componentId = getCurrentInstance()?.uid ?? 0\n\n watch(\n () => props.schema.collapsed,\n (nextCollapsed, previousCollapsed) => {\n if (nextCollapsed !== undefined) {\n internalCollapsed.value = nextCollapsed\n } else if (previousCollapsed !== undefined) {\n internalCollapsed.value = previousCollapsed\n }\n }\n )\n\n const toggle = () => {\n if (!props.schema.collapsible || props.schema.disabled) {\n return\n }\n\n const nextCollapsed = !collapsed.value\n\n if (props.schema.collapsed === undefined) {\n internalCollapsed.value = nextCollapsed\n }\n\n props.schema.onCollapsedChange?.(nextCollapsed)\n }\n\n return (): VNodeChild => {\n const schema = props.schema\n\n if (schema.visible === false) {\n return null\n }\n\n const collapsible = Boolean(schema.collapsible)\n\n const isCollapsed = collapsed.value\n\n const destroyOnCollapse = schema.destroyOnCollapse ?? true\n\n const uniqueId = schema.debug?.runtimeNodeId ?? `local-${componentId}`\n\n const idBase = `schemx-group-${uniqueId}-${normalizeId(schema.key)}`\n\n const headerId = `${idBase}-header`\n\n const bodyId = `${idBase}-body`\n\n const body = (\n <div\n id={bodyId}\n role=\"group\"\n aria-labelledby={schema.label ? headerId : undefined}\n aria-hidden={isCollapsed || undefined}\n class=\"schemx-group__body\"\n style={!destroyOnCollapse && isCollapsed ? { display: \"none\" } : undefined}\n >\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\n return (\n <div\n class={classnames(\n \"schemx-group\",\n {\n \"schemx-group--collapsed\": isCollapsed,\n \"is-readonly\": schema.readonly,\n \"is-disabled\": schema.disabled,\n },\n schema.class as string | undefined\n )}\n style={schema.style}\n data-key={schema.key}\n aria-disabled={schema.disabled || undefined}\n >\n {schema.label && (\n <div\n id={headerId}\n role={collapsible ? \"button\" : undefined}\n tabindex={collapsible ? (schema.disabled ? -1 : 0) : undefined}\n aria-expanded={collapsible ? !isCollapsed : undefined}\n aria-controls={collapsible ? bodyId : undefined}\n aria-disabled={collapsible ? schema.disabled || undefined : undefined}\n class={classnames(\"schemx-group__header\", {\n \"schemx-group__header--clickable\": collapsible && !schema.disabled,\n })}\n onClick={toggle}\n onKeydown={(e: KeyboardEvent) => {\n if (!schema.disabled && (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\": !isCollapsed,\n })}\n />\n )}\n </div>\n )}\n {destroyOnCollapse ? !isCollapsed && body : body}\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\nconst normalizeId = (key: string): string => {\n return String(key).replace(/[^a-zA-Z0-9_-]/g, \"-\")\n}\n","/**\n * FormItem\n *\n * schemx 和 FormGroup 实际渲染字段的组件。\n * 动态属性已由 core 解析到 ViewSchema 中;这里只负责消费\n * 已解析 schema、创建字段实例、组装渲染器属性与插槽。\n *\n * @module components/FormItem\n */\n\n/* eslint-disable vue/one-component-per-file */\nimport { computed, defineComponent, h, PropType, toRef } from \"vue\"\nimport type { VNodeChild } from \"vue\"\n\nimport classnames from \"classnames\"\n\nimport type { TriggerConfig } from \"@/utils\"\n\nimport {\n createFieldContext,\n useField,\n useFormConfigContext,\n useFormContext,\n useStableRef,\n} from \"../../hooks\"\nimport {\n extractChildSlots,\n mergeTrigger,\n resolveSlot,\n shouldValidateOn,\n} from \"../../utils\"\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 TValues - 表单值类型\n */\nexport interface SchemxItemProps<TValues extends Values = Values> {\n schema: SchemxViewSchema<TValues>\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\n const form = useFormContext<Values>()\n\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(\n schema().validationTrigger,\n formContext.schemaConfig.validationTrigger,\n \"onChange\"\n )\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 && (Boolean(schema().required) || 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 = (v: FieldValue<Values, NamePath<Values>>) => {\n schema().componentProps?.onBlur?.(v)\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 * `showRequiredMark` 未设置时回退到 `required`;该展示开关不参与校验逻辑。\n * 禁用或只读字段始终不显示星号。\n *\n * @returns 星号 VNode 或空片段\n */\n const renderRequired = (): VNodeChild => {\n const showRequiredMark = schema().showRequiredMark ?? Boolean(schema().required)\n\n if (!showRequiredMark || 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.schemaConfig.labelAlign\n\n const labelWidth = schema().labelWidth || formContext.schemaConfig.labelWidth\n\n const colon = schema().colon ?? formContext.schemaConfig.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\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.errors.value,\n })\n }\n\n if (field.errors.value.length === 0) {\n return null\n }\n\n return <div class=\"schemx-item__error\">{field.errors.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 =\n schema().labelPosition || formContext.schemaConfig.labelPosition\n\n return (\n <div class={classnames(\"schemx-item-wrapper\")} style={schema().style}>\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 ?? {}) as CSSStyleValue) }}\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 = <TValues extends Values>(\n schema: SchemxViewSchema<TValues>\n): schema is SchemxViewGroupSchema<TValues> => {\n return \"children\" in schema\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 是区段边界;不可见字段和不可见 Group 不参与计算。\n *\n * 使用示例:\n *\n * const list = [\n * { key: 'group-1', children: [] },\n * { key: 'a' },\n * { key: 'b' },\n * { key: 'group-2', children: [] },\n * { key: 'c' },\n * ]\n *\n * getSectionPosition(list, 'a')\n * // => { found: true, isFirst: true, isLast: false }\n */\nexport function getSectionPosition<TItem extends SchemxViewSchema>(\n list: TItem[],\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 return {\n found: true,\n isFirst: !hasPositionItemInSection(list, currentIndex, -1),\n isLast: !hasPositionItemInSection(list, currentIndex, 1),\n }\n}\n\n/**\n * 判断 schema 是否为 Group ViewSchema。\n */\nfunction isViewGroupSchema<TItem extends SchemxViewSchema>(\n item?: TItem\n): item is Extract<TItem, { children: readonly SchemxViewSchema[] }> {\n return !!item && \"children\" in item\n}\n\nfunction isPositionItem<TItem extends SchemxViewSchema>(item?: TItem) {\n return !!item && !isViewGroupSchema(item) && item.visible !== false\n}\n\nfunction hasPositionItemInSection<TItem extends SchemxViewSchema>(\n list: TItem[],\n startIndex: number,\n step: 1 | -1\n): boolean {\n for (let index = startIndex + step; index >= 0 && index < list.length; index += step) {\n const item = list[index]\n\n if (item.visible === false) {\n continue\n }\n\n if (isViewGroupSchema(item)) {\n return false\n }\n\n if (isPositionItem(item)) {\n return true\n }\n }\n\n return false\n}\n","<!--\n schemx - 主组件\n\n 基于 Schema 配置的表单组件,使用 useForm Hook 管理状态。\n\n @module schemx\n-->\n\n<script lang=\"ts\" setup generic=\"TValues extends Values = Values\">\n import { onUnmounted, reactive, watch, watchEffect } from \"vue\"\n\n import { createWatch, isSchemxSchemas, defaultSchemxConfigKeys } from \"@schemx/core\"\n import { pick } from \"es-toolkit\"\n\n import FormItem from \"./components/FormItem\"\n import {\n createFormConfigContext,\n createFormContext,\n useForm,\n useViewSchemas,\n } from \"./hooks\"\n import { getSectionPosition } from \"./utils/helpers\"\n\n import type { SchemxFormProps } from \"./types/index\"\n import type { SchemxSchemaConfig, SchemxViewSchema, Values } from \"@schemx/core\"\n\n import \"./styles/index.css\"\n\n defineOptions({ name: \"SchemxForm\" })\n\n const props = withDefaults(defineProps<SchemxFormProps<TValues>>(), {\n modelValue: () => ({}) as TValues,\n initialValues: () => ({}) as TValues,\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 validationRuleRegistry: undefined,\n visible: true,\n })\n\n const emit = defineEmits<{\n \"update:modelValue\": [value: TValues]\n }>()\n\n const pickSchemaConfig = (): Partial<SchemxSchemaConfig> => {\n // Vue 保留 `required` 对当前 TValues 的泛型约束;Core 的表单级配置使用\n // unknown 表示任意字段值。运行时该回调只会接收当前 Form 的字段值,因此在\n // Vue 到 Core 的适配边界收窄为 Core 配置类型。\n return pick(props, defaultSchemxConfigKeys) as Partial<SchemxSchemaConfig>\n }\n\n const formSchemaConfig = reactive<Partial<SchemxSchemaConfig>>(pickSchemaConfig())\n\n /**\n * 创建 FormContext 上下文\n *\n * 为子组件提供表单配置信息。\n */\n createFormConfigContext({ schemaConfig: formSchemaConfig })\n\n /**\n * 获取或创建表单实例\n *\n * 优先使用外部传入的 form,否则内部通过 useForm 创建。\n * 必须在 setup 同步阶段调用,确保 provide 正确注入。\n */\n const form = props.form\n ? props.form\n : useForm<TValues>({\n schemas: props.schemas,\n schemaConfig: pickSchemaConfig(),\n initialValues:\n Object.keys(props.modelValue).length > 0\n ? props.modelValue\n : props.initialValues,\n\n rendererRegistry: props.rendererRegistry,\n defaultRendererType: props.defaultRendererType,\n validationRuleRegistry: props.validationRuleRegistry,\n validatorAdapters: props.validatorAdapters,\n\n onFinish: async (values) => {\n props.onFinish?.(values)\n },\n onFinishFailed: async (errors) => {\n props.onFinishFailed?.(errors)\n },\n onValuesChange: (changedValues, latestSnapshot) => {\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 let syncingFromModel = false\n\n watch(\n () => props.modelValue,\n (values) => {\n syncingFromModel = true\n form.setFieldsValue(values)\n syncingFromModel = false\n }\n )\n\n const disposeWatch = createWatch(form, (latestSnapshot) => {\n if (syncingFromModel) return\n\n emit(\"update:modelValue\", latestSnapshot)\n })\n\n onUnmounted(disposeWatch)\n\n watch(\n () => props.schemas,\n (schemas) => {\n if (!isSchemxSchemas(schemas)) {\n form.setSchemas(schemas)\n }\n },\n { deep: false, immediate: !!props.form }\n )\n\n const viewSchemas = useViewSchemas(form)\n\n const getFormItemClass = (schema: SchemxViewSchema<TValues>) => {\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 const nextSchemaConfig = pickSchemaConfig()\n\n Object.assign(formSchemaConfig, nextSchemaConfig)\n form.updateSchemaConfig(nextSchemaConfig)\n })\n\n defineExpose({\n ...form,\n })\n</script>\n\n<template>\n <div :class=\"['schemx', props.class]\" :style=\"props.style\">\n <FormItem\n v-for=\"schema in viewSchemas\"\n :key=\"schema.key\"\n :schema=\"schema as SchemxViewSchema\"\n :class=\"getFormItemClass(schema as SchemxViewSchema<TValues>)\"\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 } from \"vue\"\n\nimport FormItem from \"./components/FormItem\"\nimport SchemxForm from \"./formRuntime.js\"\nimport { provideSchemxAppConfig } from \"./config\"\n\nimport type { SchemxConfig } from \"@schemx/core\"\n\n/**\n * SchemxForm 插件安装选项\n *\n * 在 `app.use(SchemxForm, options)` 时传入,用于配置当前 Vue App 的默认行为。\n * 安装配置不会写入 Core 的模块级全局配置,因此多个 Vue App 可以彼此隔离。\n *\n * @example\n * ```ts\n * import SchemxForm from '@schemx/vue'\n *\n * app.use(SchemxForm, {\n * schemaConfig: { readonly: true },\n * })\n * ```\n */\nexport interface SchemxInstallOptions extends SchemxConfig {}\n\n/**\n * 为组件挂载静态属性并保留原始类型\n *\n * @param comp - 原始组件\n * @param extra - 要挂载的静态属性\n * @typeParam TComponent - 原始组件类型。\n * @typeParam TExtras - 待挂载的静态属性类型。\n */\nexport function withInstall<\n TComponent extends object,\n TExtras extends Record<string, unknown>,\n>(comp: TComponent, extra: TExtras) {\n return Object.assign(comp, extra) as TComponent & TExtras\n}\n\nexport type SchemxFormPlugin = typeof SchemxForm & {\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 provideSchemxAppConfig(app, options)\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 { useFieldContext } from \"../hooks/provideFieldContext\"\nimport { useDictionary } from \"../hooks/useDictionary\"\n\nimport type { NamePath } from \"@schemx/core\"\n\n/**\n * WithRemoteOptions 注入给被包装组件的额外 Props\n */\nexport interface RemoteOptionsInjectedProps {\n /** 加载的选项列表 */\n options: unknown[]\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): 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 // 内嵌于 FormItem 时自动从字段 Context 获取目标字段;显式 fieldName\n // 保留给脱离 FormItem 的独立使用场景。\n const fieldName =\n props.fieldName ?? (props.dict ? useFieldContext().name : undefined)\n\n const dictResult = props.dict ? useDictionary(props.dict, 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":["rendererRegistry","validationRuleRegistry","FormGroup","defineComponent","props","slots","internalCollapsed","ref","Boolean","schema","defaultCollapsed","collapsed","computed","value","componentId","getCurrentInstance","uid","watch","nextCollapsed","previousCollapsed","undefined","toggle","collapsible","disabled","onCollapsedChange","visible","isCollapsed","destroyOnCollapse","uniqueId","debug","runtimeNodeId","idBase","normalizeId","key","headerId","bodyId","body","_createVNode","label","display","children","map","child","FormItem","classnames","readonly","class","style","e","preventDefault","name","type","Object","required","String","replace","setup","isViewGroupSchema","h","FieldFormItem","schemaRef","toRef","form","useFormContext","formContext","useFormConfigContext","field","useField","createFieldContext","trigger","mergeTrigger","validationTrigger","schemaConfig","canVerified","isOperate","rules","hasRules","Array","isArray","length","handleChange","v","setValue","componentProps","onChange","shouldValidateOn","validate","handleBlur","onBlur","useStableRef","currentSchema","placeholder","formItemProps","getValue","renderRequired","showRequiredMark","_createTextVNode","renderLabel","labelSlot","resolveSlot","labelAlign","labelWidth","colon","width","textAlign","renderContent","component","getRenderer","componentType","Error","childSlots","extractChildSlots","normalizeNameKey","columnElement","contentSlot","renderError","errorSlot","errors","itemSlot","labelPosition","part","join","_createElementBlock","_normalizeClass","_normalizeStyle","_Fragment","_renderList","_unref","_createBlock","$slots","_withCtx","_renderSlot","_mergeProps","SchemxForm"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
1
  import { NamePath, SchemxInstance, Values } from '@schemx/core';
2
-
3
2
  /**
4
3
  * 字典选项配置
5
4
  *
6
5
  * 通过 api 函数获取数据,支持完整泛型推导。
7
- * api 的返回值类型 R 会自动传递给 formatter 和 onSuccess。
6
+ * api 的返回值类型 TResponse 会自动传递给 formatter 和 onSuccess。
8
7
  *
9
- * @typeParam T - 表单值类型
10
- * @typeParam R - api 函数的返回值类型
8
+ * @typeParam TValues - 表单值类型
9
+ * @typeParam TResponse - api 函数的返回值类型
10
+ * @typeParam TOption - 格式化后的字典选项类型
11
11
  *
12
12
  * @example
13
13
  * ```ts
@@ -34,12 +34,12 @@ import { NamePath, SchemxInstance, Values } from '@schemx/core';
34
34
  * }
35
35
  * ```
36
36
  */
37
- export interface SchemxDictionary<T extends Values = Values, R = any> {
37
+ export interface SchemxDictionary<TValues extends Values = Values, TResponse = unknown, TOption = unknown> {
38
38
  /**
39
39
  * 数据获取函数
40
40
  *
41
41
  * 接收当前表单值和表单实例,返回数据(支持异步)。
42
- * 返回值类型 R 会自动传递给 formatter 的第一个参数。
42
+ * 返回值类型 TResponse 会自动传递给 formatter 的第一个参数。
43
43
  *
44
44
  * @param values - 当前表单值。
45
45
  * @param form - 当前表单实例。
@@ -57,7 +57,7 @@ export interface SchemxDictionary<T extends Values = Values, R = any> {
57
57
  * }
58
58
  * ```
59
59
  */
60
- api: (values: T, form: SchemxInstance<T>) => R | Promise<R>;
60
+ api: (values: TValues, form: SchemxInstance<TValues>) => TResponse | Promise<TResponse>;
61
61
  /**
62
62
  * 响应数据格式化函数
63
63
  *
@@ -67,14 +67,14 @@ export interface SchemxDictionary<T extends Values = Values, R = any> {
67
67
  * @param form - 当前表单实例。
68
68
  * @returns 标准化后的选项数组,支持同步或异步返回。
69
69
  */
70
- formatter?: (res: Awaited<R>, form: SchemxInstance<T>) => any[] | Promise<any[]>;
70
+ formatter?: (res: Awaited<TResponse>, form: SchemxInstance<TValues>) => TOption[] | Promise<TOption[]>;
71
71
  /**
72
72
  * 依赖的表单字段路径
73
73
  *
74
74
  * 当这些字段的值变化时,自动重新执行 api 函数。
75
75
  * 不配置则仅在 onMounted 时执行一次。
76
76
  */
77
- dependsOn?: NamePath<T>[];
77
+ dependsOn?: NamePath<TValues>[];
78
78
  /**
79
79
  * 是否应该执行 api
80
80
  *
@@ -86,7 +86,7 @@ export interface SchemxDictionary<T extends Values = Values, R = any> {
86
86
  * @param values - 当前表单值。
87
87
  * @returns 是否执行 api。
88
88
  */
89
- shouldFetch?: (values: T) => boolean;
89
+ shouldFetch?: (values: TValues) => boolean;
90
90
  /**
91
91
  * 是否在组件挂载时立即执行
92
92
  *
@@ -108,29 +108,29 @@ export interface SchemxDictionary<T extends Values = Values, R = any> {
108
108
  /** 重试间隔(毫秒),默认 `1000` */
109
109
  retryInterval?: number;
110
110
  /** 请求失败回调 */
111
- onError?: (error: Error, form: SchemxInstance<T>) => void;
111
+ onError?: (error: Error, form: SchemxInstance<TValues>) => void;
112
112
  /**
113
113
  * 请求成功回调
114
114
  *
115
115
  * 在 formatter 之后、写入 list 之后调用。
116
116
  */
117
- onSuccess?: (data: any[], form: SchemxInstance<T>) => void;
117
+ onSuccess?: (data: TOption[], form: SchemxInstance<TValues>) => void;
118
118
  /**
119
119
  * 依赖字段变化回调
120
120
  *
121
121
  * 在 shouldFetch 判断之前调用,无论是否执行 api 都会触发。
122
122
  * 可用于依赖变化时的额外清理逻辑(如清空下级字段)。
123
123
  */
124
- onDepsChange?: (values: T, form: SchemxInstance<T>) => void;
124
+ onDepsChange?: (values: TValues, form: SchemxInstance<TValues>) => void;
125
125
  }
126
126
  /**
127
127
  * 为 Props 类型注入可选的 dict 字段
128
128
  *
129
- * 将任意组件 Props 类型 A 扩展为包含 `dict?: SchemxDictionary<T>` 的新类型。
129
+ * 将任意组件 Props 类型 TProps 扩展为包含 `dict?: SchemxDictionary<TValues>` 的新类型。
130
130
  * 用于为渲染器组件的 Props 添加字典选项支持。
131
131
  *
132
- * @typeParam A - 原始组件 Props 类型
133
- * @typeParam T - 表单值类型
132
+ * @typeParam TProps - 原始组件 Props 类型
133
+ * @typeParam TValues - 表单值类型
134
134
  *
135
135
  * @example
136
136
  * ```ts
@@ -144,7 +144,7 @@ export interface SchemxDictionary<T extends Values = Values, R = any> {
144
144
  * // 等价于 MySelectProps & { dict?: SchemxDictionary<MyFormValues> }
145
145
  * ```
146
146
  */
147
- export type SchemxWithDictionary<A, T extends Values = Values> = A & {
148
- dict?: SchemxDictionary<T>;
147
+ export type SchemxWithDictionary<TProps, TValues extends Values = Values> = TProps & {
148
+ dict?: SchemxDictionary<TValues>;
149
149
  };
150
150
  //# sourceMappingURL=dictionary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dictionary.d.ts","sourceRoot":"","sources":["../../src/types/dictionary.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG;IAClE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAE3D;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAEhF;;;;;OAKG;IACH,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;IAEzB;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAA;IAEpC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAE3B,yBAAyB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yBAAyB;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,aAAa;IACb,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAEzD;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAE1D;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;CAC5D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG;IACnE,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;CAC3B,CAAA"}
1
+ {"version":3,"file":"dictionary.d.ts","sourceRoot":"","sources":["../../src/types/dictionary.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,WAAW,gBAAgB,CAC/B,OAAO,SAAS,MAAM,GAAG,MAAM,EAC/B,SAAS,GAAG,OAAO,EACnB,OAAO,GAAG,OAAO;IAEjB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,GAAG,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAA;IAEvF;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,CACV,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,EACvB,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,KAC1B,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IAEnC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAA;IAE/B;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAA;IAE1C;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAE3B,yBAAyB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yBAAyB;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,aAAa;IACb,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IAE/D;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;IAEpE;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,IAAI,CAAA;CACxE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,oBAAoB,CAAC,MAAM,EAAE,OAAO,SAAS,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG;IACnF,IAAI,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;CACjC,CAAA"}