auto-gen-crud 0.1.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 (62) hide show
  1. package/API.md +130 -0
  2. package/README.md +234 -0
  3. package/dist/adapters/ant-design-vue.d.ts +8 -0
  4. package/dist/adapters/element-plus.d.ts +8 -0
  5. package/dist/adapters/index.d.ts +4 -0
  6. package/dist/adapters/index.js +7 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/adapters/naive.d.ts +8 -0
  9. package/dist/adapters/types.d.ts +162 -0
  10. package/dist/components/AutoCrud.vue.d.ts +398 -0
  11. package/dist/components/CopyText.vue.d.ts +6 -0
  12. package/dist/components/CrudModal.vue.d.ts +47 -0
  13. package/dist/components/DecodeText.vue.d.ts +8 -0
  14. package/dist/components/DynamicForm.vue.d.ts +63 -0
  15. package/dist/components/DynamicFormItem.vue.d.ts +20 -0
  16. package/dist/components/DynamicTable.vue.d.ts +30 -0
  17. package/dist/context.d.ts +36 -0
  18. package/dist/core/date-format-utils.d.ts +8 -0
  19. package/dist/core/form-runtime-cache.d.ts +27 -0
  20. package/dist/core/form-utils.d.ts +51 -0
  21. package/dist/core/object-utils.d.ts +43 -0
  22. package/dist/core/table-utils.d.ts +19 -0
  23. package/dist/core/types.d.ts +443 -0
  24. package/dist/generator/fields.d.ts +27 -0
  25. package/dist/generator/index.d.ts +14 -0
  26. package/dist/generator/index.js +740 -0
  27. package/dist/generator/index.js.map +1 -0
  28. package/dist/generator/openapi.d.ts +10 -0
  29. package/dist/generator/operations.d.ts +8 -0
  30. package/dist/generator/schema.d.ts +32 -0
  31. package/dist/generator/string-utils.d.ts +11 -0
  32. package/dist/generator/templates.d.ts +6 -0
  33. package/dist/generator/types.d.ts +185 -0
  34. package/dist/generator/writer.d.ts +8 -0
  35. package/dist/hooks/index.d.ts +3 -0
  36. package/dist/hooks/index.js +7 -0
  37. package/dist/hooks/index.js.map +1 -0
  38. package/dist/hooks/useModalForm.d.ts +11 -0
  39. package/dist/hooks/useSearchForm.d.ts +10 -0
  40. package/dist/hooks/useTable.d.ts +11 -0
  41. package/dist/index-Dc-solHn.js +299 -0
  42. package/dist/index-Dc-solHn.js.map +1 -0
  43. package/dist/index.d.ts +12 -0
  44. package/dist/index.js +985 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/plugin.d.ts +17 -0
  47. package/dist/style.css +1 -0
  48. package/dist/useTable-BwDSqD1q.js +214 -0
  49. package/dist/useTable-BwDSqD1q.js.map +1 -0
  50. package/dist/utils/clipboard.d.ts +16 -0
  51. package/dist/utils/index.d.ts +4 -0
  52. package/dist/utils/index.js +532 -0
  53. package/dist/utils/index.js.map +1 -0
  54. package/dist/utils/message.d.ts +9 -0
  55. package/dist/utils/request.d.ts +8 -0
  56. package/dist/utils/sm4-core.d.ts +12 -0
  57. package/dist/utils/sm4.d.ts +29 -0
  58. package/docs/AI_AGENT_GUIDE.md +210 -0
  59. package/mcp/auto-crud-mcp.mjs +502 -0
  60. package/mcp/auto-gen-crud-mcp.mjs +586 -0
  61. package/package.json +93 -0
  62. package/skills/auto-gen-crud-agent/SKILL.md +114 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/context.ts","../src/components/CrudModal.vue","../src/core/form-runtime-cache.ts","../src/components/DynamicFormItem.vue","../src/components/DynamicForm.vue","../src/core/table-utils.ts","../src/components/CopyText.vue","../src/components/DecodeText.vue","../src/components/DynamicTable.vue","../src/components/AutoCrud.vue","../src/plugin.ts"],"sourcesContent":["import { inject, provide } from \"vue\";\nimport type { AutoCrudInstallOptions, CrudDecodeHandler, CrudDecodeLogHandler, CrudRequestHandler } from \"./core/types\";\nimport type { CrudUIAdapter } from \"./adapters/types\";\nimport { elementPlusAdapter } from \"./adapters/element-plus\";\n\n/** Auto CRUD 全局上下文。 */\nexport interface AutoCrudContext {\n /** 当前项目初始化时选择的 UI Adapter。 */\n adapter: CrudUIAdapter;\n /** 用户注入的请求函数,供 decode、日志等能力使用。 */\n request?: CrudRequestHandler;\n /** 用户注入的敏感信息解密函数。 */\n decode?: CrudDecodeHandler;\n /** 用户注入的敏感信息查看日志函数。 */\n decodeLog?: CrudDecodeLogHandler;\n /** 默认 SM4 密钥。 */\n sm4Key?: string;\n}\n\nexport const AUTO_CRUD_CONTEXT_KEY = Symbol(\"AUTO_CRUD_CONTEXT_KEY\");\n\nlet globalContext: AutoCrudContext = {\n adapter: elementPlusAdapter,\n};\n\n/**\n * 设置全局上下文。\n *\n * 插件安装时会调用它。额外保留模块级 globalContext,\n * 是为了让内部工具或没有处在组件 setup 注入链里的代码也能读取默认配置。\n */\nexport function setAutoCrudContext(context: AutoCrudContext): void {\n globalContext = context;\n}\n\n/** 获取模块级全局上下文。 */\nexport function getAutoCrudContext(): AutoCrudContext {\n return globalContext;\n}\n\n/** 在组件树中注入上下文。 */\nexport function provideAutoCrudContext(context: AutoCrudContext): void {\n setAutoCrudContext(context);\n provide(AUTO_CRUD_CONTEXT_KEY, context);\n}\n\n/**\n * 在组件中读取 Auto CRUD 上下文。\n *\n * 如果使用者忘记 `app.use(createAutoCrud())`,这里也会返回默认 Element Plus 配置,\n * 确保“默认 Element Plus”这个约定稳定可用。\n */\nexport function useAutoCrudContext(): AutoCrudContext {\n return inject<AutoCrudContext>(AUTO_CRUD_CONTEXT_KEY, globalContext);\n}\n\n/** 根据初始化参数合并上下文。 */\nexport function createAutoCrudContext(options: AutoCrudInstallOptions = {}, adapter: CrudUIAdapter): AutoCrudContext {\n return {\n adapter,\n request: options.request,\n decode: options.decode,\n decodeLog: options.decodeLog,\n sm4Key: options.sm4Key,\n };\n}\n","<template>\n <component v-if=\"drawerContentComponent\" :is=\"resolveAdapterComponent(modalComponent)\" v-bind=\"modalProps\">\n <!-- Naive UI 的抽屉 footer 在 NDrawerContent 上,这里按 adapter 能力选择是否包内容组件。 -->\n <component :is=\"resolveAdapterComponent(drawerContentComponent)\" :title=\"title\">\n <div class=\"vac-modal-body\">\n <slot />\n </div>\n <template #footer>\n <ModalFooter />\n </template>\n </component>\n </component>\n\n <component v-else :is=\"resolveAdapterComponent(modalComponent)\" v-bind=\"modalProps\">\n <div class=\"vac-modal-body\">\n <slot />\n </div>\n <!-- 使用真实弹窗/抽屉 footer 插槽,让底部按钮交给 UI 组件自身固定和排版。 -->\n <template #footer>\n <ModalFooter />\n </template>\n </component>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, defineComponent, h, resolveComponent, useSlots, type Slot } from \"vue\";\nimport type { CrudAdapterComponent } from \"../adapters/types\";\nimport type { CrudModalMode, CrudModalType } from \"../core/types\";\nimport { useAutoCrudContext } from \"../context\";\n\nconst props = withDefaults(\n defineProps<{\n /** 弹窗是否显示。 */\n visible: boolean;\n /** 弹窗标题。 */\n title: string;\n /** 弹窗模式,查看模式会隐藏提交按钮。 */\n mode: CrudModalMode;\n /** drawer 或 modal。 */\n modalType?: CrudModalType;\n /** 弹窗宽度。 */\n width?: string | number;\n /** 提交按钮文案。 */\n submitText?: string;\n /** 关闭按钮文案。 */\n cancelText?: string;\n }>(),\n {\n modalType: \"drawer\",\n submitText: \"提交\",\n cancelText: \"关闭\",\n }\n);\n\nconst emit = defineEmits<{\n \"update:visible\": [value: boolean];\n submit: [];\n close: [];\n}>();\n\nconst { adapter } = useAutoCrudContext();\nconst slots = useSlots() as { footer?: Slot };\n\nconst modalComponent = computed(() => (props.modalType === \"drawer\" ? adapter.components.drawer : adapter.components.modal));\nconst drawerContentComponent = computed(() => (props.modalType === \"drawer\" ? adapter.components.drawerContent : undefined));\nconst modalProps = computed(() =>\n adapter.modal.getModalProps({\n visible: props.visible,\n title: props.title,\n width: props.width,\n onUpdateVisible: value => emit(\"update:visible\", value),\n })\n);\n\n/**\n * 默认底部按钮区。\n *\n * 这里用渲染组件复用同一份 footer 内容,避免 Naive DrawerContent 和普通 Dialog/Modal 两条模板分支重复按钮代码。\n */\nconst ModalFooter = defineComponent({\n setup() {\n return () =>\n h(\"div\", { class: \"vac-modal-footer\" }, slots.footer?.() ?? [\n props.mode !== \"view\"\n ? h(\n resolveRuntimeComponent(adapter.components.button),\n { ...adapter.render.getButtonProps({ type: \"primary\" }), onClick: () => emit(\"submit\") },\n () => props.submitText\n )\n : null,\n h(\n resolveRuntimeComponent(adapter.components.button),\n { ...adapter.render.getButtonProps({ type: \"default\" }), onClick: () => emit(\"close\") },\n () => props.cancelText\n ),\n ]);\n },\n});\n\nfunction resolveAdapterComponent(component: CrudAdapterComponent): CrudAdapterComponent {\n return component;\n}\n\n/** 渲染函数里字符串组件名需要显式解析,否则会被当成原生标签渲染成 <nbutton>。 */\nfunction resolveRuntimeComponent(component: CrudAdapterComponent): string | object {\n return typeof component === \"string\" ? resolveComponent(component) : component;\n}\n</script>\n","import type { CrudFormItem, CrudFormRule, CrudOption } from \"./types\";\nimport {\n createRules,\n getFormItemLabel,\n getPlaceholder,\n normalizeOptions,\n shouldDisableFormItem,\n shouldShowFormItem,\n} from \"./form-utils\";\nimport { getByPath } from \"./object-utils\";\n\n/**\n * 表单里会被动态计算的配置类型。\n *\n * 这些配置既支持静态值,也支持函数:\n * - show:字段是否显示。\n * - disabled:字段是否禁用。\n * - rules:字段校验规则。\n * - options:下拉、单选、多选等选项。\n * - label:字段标题。\n * - placeholder:字段占位文案。\n *\n * 缓存以 kind 为维度区分,避免同一个字段的 show 结果被 options 误用。\n */\ntype DynamicValueKind = \"show\" | \"disabled\" | \"rules\" | \"options\" | \"label\" | \"placeholder\";\n\n/**\n * 单条缓存记录。\n *\n * depsSnapshot:当前缓存对应的依赖字段快照,例如 `status:string:disabled`。\n * value:真实计算结果,例如 show 的 boolean、options 的数组。\n */\ninterface CachedValue {\n depsSnapshot: string;\n value: unknown;\n}\n\n/**\n * 单个字段的性能统计。\n *\n * 这些统计只在开启调试开关后写入 `window.__VAC_PERF__`:\n * - calls:每类动态配置实际执行函数的次数。\n * - cacheHit:命中缓存次数。\n * - cacheMiss:未命中缓存次数。\n * - noDeps:字段没有声明 deps,所以无法使用缓存的次数。\n */\ninterface FieldPerfStats {\n /**\n * 每类动态配置实际执行函数的次数。\n */\n calls: Partial<Record<DynamicValueKind, number>>;\n cacheHit: number;\n cacheMiss: number;\n noDeps: number;\n}\n\n/**\n * 暴露给浏览器调试用的全局性能面板数据结构。\n *\n * 使用方式:\n * - 控制台执行 `window.__VAC_PERF__ = true`,然后操作表单。\n * - 或 localStorage 设置 `VAC_DEBUG_PERF=1`。\n * - 再查看 `window.__VAC_PERF__.fields`。\n */\ninterface VacPerfGlobal {\n enabled?: boolean;\n fields: Record<string, FieldPerfStats>;\n reset: () => void;\n}\n\n/**\n * 带有 auto-gen-crud 调试字段的 Window 类型。\n *\n * 这里单独声明类型,是为了避免直接访问 `window.__VAC_PERF__` 时 TypeScript 报错。\n */\ntype VacWindow = Window &\n typeof globalThis & {\n __VAC_PERF__?: boolean | VacPerfGlobal;\n };\n\n/**\n * 表单运行时缓存对外暴露的能力。\n *\n * DynamicForm / DynamicFormItem 不直接调用 form-utils 中的动态计算函数,\n * 而是统一通过这个对象读取结果。这样可以把“是否缓存、如何缓存、如何统计”\n * 全部收敛在本文件里。\n */\nexport interface FormRuntimeCache {\n getShow: (item: CrudFormItem, formData: Record<string, unknown>) => boolean;\n getDisabled: (item: CrudFormItem, formData: Record<string, unknown>, forceDisabled?: boolean) => boolean;\n getRules: (item: CrudFormItem, formData: Record<string, unknown>) => CrudFormRule[];\n getOptions: (item: CrudFormItem, formData: Record<string, unknown>) => CrudOption[];\n getLabel: (item: CrudFormItem, formData: Record<string, unknown>) => string;\n getPlaceholder: (item: CrudFormItem, formData: Record<string, unknown>) => string;\n clear: () => void;\n}\n\n/**\n * 判断字段是否允许使用缓存。\n *\n * 只有声明了 deps 的字段才缓存。原因是:\n * - 有 deps:框架知道哪些字段变化会影响结果,可以安全复用缓存。\n * - 没 deps:框架不知道函数里读了哪些字段,只能保守地实时计算。\n */\nfunction isCacheable(item: CrudFormItem): boolean {\n return Array.isArray(item.deps) && item.deps.length > 0;\n}\n\n/** 判断配置项是不是函数形式,只有函数形式才需要提示用户补 deps。 */\nfunction isDynamicValue(value: unknown): boolean {\n return typeof value === \"function\";\n}\n\n/**\n * 判断是否是开发环境。\n *\n * dev 提示只在开发环境输出,避免生产环境控制台出现性能建议类信息。\n */\nfunction isDevelopment(): boolean {\n const meta = import.meta as ImportMeta & { env?: { DEV?: boolean; MODE?: string } };\n return meta.env?.DEV === true || meta.env?.MODE === \"development\";\n}\n\n/**\n * 安全获取 window。\n *\n * 包可能在 SSR、Node 测试、构建阶段被导入,这些环境没有 window。\n * 所以所有浏览器调试能力都必须先做存在性判断。\n */\nfunction getVacWindow(): VacWindow | undefined {\n if (typeof window === \"undefined\") return undefined;\n return window as VacWindow;\n}\n\n/**\n * 判断是否启用性能统计。\n *\n * 支持两种开启方式:\n * - 控制台设置:`window.__VAC_PERF__ = true`\n * - localStorage 设置:`VAC_DEBUG_PERF=1`\n *\n * 默认不开启,避免普通使用时有额外调试数据写入。\n */\nfunction isPerfEnabled(): boolean {\n const targetWindow = getVacWindow();\n if (!targetWindow) return false;\n\n const globalPerf = targetWindow.__VAC_PERF__;\n if (globalPerf === true) return true;\n if (typeof globalPerf === \"object\" && globalPerf.enabled !== false) return true;\n\n try {\n return targetWindow.localStorage?.getItem(\"VAC_DEBUG_PERF\") === \"1\";\n } catch {\n return false;\n }\n}\n\n/**\n * 获取或初始化全局性能统计对象。\n *\n * 第一次启用时,如果用户只是写了 `window.__VAC_PERF__ = true`,\n * 这里会把它替换成真正的统计对象:\n * ```ts\n * {\n * enabled: true,\n * fields: {},\n * reset() {}\n * }\n * ```\n */\nfunction ensurePerfGlobal(): VacPerfGlobal | undefined {\n if (!isPerfEnabled()) return undefined;\n\n const targetWindow = getVacWindow();\n if (!targetWindow) return undefined;\n\n if (typeof targetWindow.__VAC_PERF__ !== \"object\" || targetWindow.__VAC_PERF__ === null) {\n const perfGlobal: VacPerfGlobal = {\n enabled: true,\n fields: {},\n reset() {\n perfGlobal.fields = {};\n },\n };\n targetWindow.__VAC_PERF__ = perfGlobal;\n }\n\n return targetWindow.__VAC_PERF__ as VacPerfGlobal;\n}\n\n/**\n * 生成性能统计字段 key。\n *\n * scope 用于区分不同表单区域:\n * - search:status 表示搜索表单里的 status 字段。\n * - modal:status 表示弹窗表单里的 status 字段。\n *\n * 这样搜索表单和弹窗表单字段同名时,统计不会混在一起。\n */\nfunction getPerfFieldKey(scope: string, item: CrudFormItem): string {\n return `${scope}:${item.prop || \"__unknown__\"}`;\n}\n\n/**\n * 记录一次性能事件。\n *\n * 这个函数只负责写统计数据,不参与业务计算:\n * - call:真实执行动态函数。\n * - cacheHit:缓存命中。\n * - cacheMiss:缓存未命中。\n * - noDeps:没有 deps,无法缓存。\n */\nfunction recordPerf(scope: string, kind: DynamicValueKind, item: CrudFormItem, event: \"call\" | \"cacheHit\" | \"cacheMiss\" | \"noDeps\"): void {\n const perfGlobal = ensurePerfGlobal();\n if (!perfGlobal) return;\n\n const fieldKey = getPerfFieldKey(scope, item);\n const fieldStats =\n perfGlobal.fields[fieldKey] ||\n (perfGlobal.fields[fieldKey] = {\n calls: {},\n cacheHit: 0,\n cacheMiss: 0,\n noDeps: 0,\n });\n\n if (event === \"call\") {\n fieldStats.calls[kind] = (fieldStats.calls[kind] || 0) + 1;\n return;\n }\n\n fieldStats[event] += 1;\n}\n\n/**\n * 把依赖字段值转成稳定字符串。\n *\n * 为什么需要快照:\n * - 缓存能不能复用,取决于 deps 对应的字段值有没有变化。\n * - 例如 deps 是 [\"status\"],status 没变时 show/required 结果可以复用。\n *\n * 处理策略:\n * - 基础类型直接拼接,成本最低。\n * - Date 转时间戳。\n * - 普通对象尝试 JSON.stringify。\n * - 循环引用或复杂对象 stringify 失败时,退回对象类型描述,避免表单渲染被异常打断。\n */\nfunction createStableValueSnapshot(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"undefined\";\n if ([\"string\", \"number\", \"boolean\", \"bigint\"].includes(typeof value)) return `${typeof value}:${String(value)}`;\n if (value instanceof Date) return `date:${value.getTime()}`;\n\n try {\n return `json:${JSON.stringify(value)}`;\n } catch {\n // 复杂对象或循环引用不适合作为高频 deps。这里退回引用描述,避免快照生成打断表单渲染。\n return `object:${Object.prototype.toString.call(value)}`;\n }\n}\n\n/**\n * 根据字段 deps 生成依赖快照。\n *\n * 示例:\n * ```ts\n * item.deps = [\"status\", \"user.type\"]\n * formData.status = \"disabled\"\n * formData.user.type = 1\n * ```\n *\n * 生成结果类似:\n * ```txt\n * status:string:disabled|user.type:number:1\n * ```\n */\nfunction createDepsSnapshot(item: CrudFormItem, formData: Record<string, unknown>): string {\n return (item.deps || [])\n .map(dep => {\n const value = getByPath(formData, dep);\n return `${dep}:${createStableValueSnapshot(value)}`;\n })\n .join(\"|\");\n}\n\n/**\n * 生成缓存 Map 的 key。\n *\n * key 不包含 depsSnapshot,原因是:\n * - 每个字段每类动态值只保留最近一次结果,避免输入型 deps 导致缓存无限增长。\n * - depsSnapshot 放在 CachedValue 里,用于判断当前缓存是否还能复用。\n *\n * 示例:\n * ```txt\n * modal:show:disabledReason\n * search:options:status\n * ```\n */\nfunction createCacheKey(scope: string, kind: DynamicValueKind, item: CrudFormItem): string {\n return `${scope}:${kind}:${item.prop}`;\n}\n\n/**\n * 判断字段在某类配置上是否真的用了函数。\n *\n * 作用:\n * - 只有动态函数没写 deps 时才给提示。\n * - 静态配置例如 `required: true`、`options: []` 不需要提示。\n */\nfunction hasDynamicConfig(kind: DynamicValueKind, item: CrudFormItem): boolean {\n if (kind === \"show\") return isDynamicValue(item.show);\n if (kind === \"disabled\") return isDynamicValue(item.disabled);\n if (kind === \"rules\") return isDynamicValue(item.required) || isDynamicValue(item.isReq) || isDynamicValue(item.rules);\n if (kind === \"options\") return isDynamicValue(item.options);\n if (kind === \"label\") return isDynamicValue(item.label);\n if (kind === \"placeholder\") return isDynamicValue(item.placeholder);\n return false;\n}\n\n/**\n * 创建表单运行时缓存。\n *\n * 设计说明:\n * - 只缓存声明了 deps 的字段,避免没有依赖信息时错误复用旧结果。\n * - deps 表示动态配置依赖哪些字段;无关字段变化不会改变缓存 key。\n * - scope 用于区分搜索表单、弹窗表单等不同缓存域,避免同 prop 字段共用缓存时互相覆盖。\n * - 该工具只负责动态配置计算结果缓存,不改变现有 CrudFormItem 配置写法。\n */\nexport function createFormRuntimeCache(scope = \"default\"): FormRuntimeCache {\n /** 保存每个字段、每类动态配置最近一次计算结果。 */\n const cache = new Map<string, CachedValue>();\n /** 记录已经提示过的缺失 deps 字段,避免控制台重复刷屏。 */\n const warnedKeys = new Set<string>();\n\n /**\n * 开发环境下提示用户补 deps。\n *\n * 例如用户写了:\n * ```ts\n * show: data => data.status === 1\n * ```\n * 但没有写:\n * ```ts\n * deps: [\"status\"]\n * ```\n *\n * 框架不知道 show 依赖 status,所以不能安全缓存,只能实时执行。\n */\n function warnMissingDeps(kind: DynamicValueKind, item: CrudFormItem): void {\n if (!isDevelopment() || !hasDynamicConfig(kind, item) || isCacheable(item)) return;\n\n const warningKey = `${scope}:${kind}:${item.prop}`;\n if (warnedKeys.has(warningKey)) return;\n warnedKeys.add(warningKey);\n console.warn(\n `[auto-gen-crud] 字段 ${item.prop} 使用了动态 ${kind} 配置,但没有声明 deps,无法启用缓存优化。`\n );\n }\n\n /**\n * 所有动态配置读取都走这里。\n *\n * 流程:\n * 1. 如果字段没有 deps:\n * - 开发环境提示用户补 deps。\n * - 记录 noDeps/call。\n * - 直接执行 resolver,保持旧行为。\n * 2. 如果字段有 deps:\n * - 生成缓存 key,例如 `modal:show:disabledReason`。\n * - 生成 deps 快照,例如 `status:string:disabled`。\n * - 如果缓存存在且快照一致,直接返回缓存值。\n * - 如果快照不同,重新执行 resolver,并覆盖旧缓存。\n */\n function resolveCached<T>(\n kind: DynamicValueKind,\n item: CrudFormItem,\n formData: Record<string, unknown>,\n resolver: () => T\n ): T {\n if (!isCacheable(item)) {\n warnMissingDeps(kind, item);\n recordPerf(scope, kind, item, \"noDeps\");\n recordPerf(scope, kind, item, \"call\");\n return resolver();\n }\n\n const key = createCacheKey(scope, kind, item);\n const depsSnapshot = createDepsSnapshot(item, formData);\n const cached = cache.get(key);\n if (cached && cached.depsSnapshot === depsSnapshot) {\n recordPerf(scope, kind, item, \"cacheHit\");\n return cached.value as T;\n }\n\n recordPerf(scope, kind, item, \"cacheMiss\");\n recordPerf(scope, kind, item, \"call\");\n const value = resolver();\n // 每个字段每类动态值只保留最近一次快照,避免输入型 deps 造成缓存无限增长。\n cache.set(key, { depsSnapshot, value });\n return value;\n }\n\n return {\n /** 获取字段是否显示。 */\n getShow(item, formData) {\n return resolveCached(\"show\", item, formData, () => shouldShowFormItem(item, formData));\n },\n /** 获取字段是否禁用。 */\n getDisabled(item, formData, forceDisabled = false) {\n // 全局禁用通常来自查看模式,优先级最高,不能被字段级缓存结果覆盖。\n if (forceDisabled) return true;\n return resolveCached(\"disabled\", item, formData, () => shouldDisableFormItem(item, formData, false));\n },\n /** 获取字段校验规则。 */\n getRules(item, formData) {\n return resolveCached(\"rules\", item, formData, () => createRules(item, formData));\n },\n /** 获取字段选项。 */\n getOptions(item, formData) {\n return resolveCached(\"options\", item, formData, () => normalizeOptions(item.options, formData, item));\n },\n /** 获取字段标题。 */\n getLabel(item, formData) {\n return resolveCached(\"label\", item, formData, () => getFormItemLabel(item, formData));\n },\n /** 获取字段占位文案。 */\n getPlaceholder(item, formData) {\n return resolveCached(\"placeholder\", item, formData, () => getPlaceholder(item, formData));\n },\n /** 清空运行时缓存和已提示记录,通常用于 columns 重建或调试时手动重置。 */\n clear() {\n cache.clear();\n warnedKeys.clear();\n },\n };\n}\n","<template>\n <!-- 单个表单项:字段级渲染被拆出,减少整个表单模板在字段变更时的重复执行成本。 -->\n <component :is=\"resolveAdapterComponent(adapter.components.formItem)\" v-bind=\"formItemProps\">\n <!-- 自定义渲染优先级最高,适合复杂业务字段或非标准组件。 -->\n <component v-if=\"item.render\" :is=\"RenderContent\" :renderer=\"() => item.render?.(modelValue, item)\" />\n <!-- 默认字段渲染:根据 component/input/select/date 等配置选择真实控件。 -->\n <component v-else :is=\"resolveAdapterComponent(fieldComponent)\" v-bind=\"fieldProps\">\n <!-- 部分 UI 库要求 select/radio/checkbox 选项作为子节点渲染,这里统一兜住。 -->\n <template v-if=\"shouldRenderOptionsAsChildren\">\n <component\n v-for=\"option in fieldOptions\"\n :is=\"resolveAdapterComponent(optionComponent)\"\n :key=\"String(option.value)\"\n :label=\"option.label\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </component>\n </template>\n </component>\n </component>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, defineComponent } from \"vue\";\nimport type { CrudAdapterComponent } from \"../adapters/types\";\nimport type { CrudFormFieldEventContext, CrudFormItem } from \"../core/types\";\nimport type { FormRuntimeCache } from \"../core/form-runtime-cache\";\nimport { getByPath, getFormComponent, setByPath } from \"../core/object-utils\";\nimport { useAutoCrudContext } from \"../context\";\nimport { normalizeDateFnsFormat } from \"../core/date-format-utils\";\n\ntype FieldEventHandler = (...args: unknown[]) => unknown;\n\nconst props = defineProps<{\n /** 当前字段配置。 */\n item: CrudFormItem;\n /** 整个表单数据,字段函数仍可读取其它字段。 */\n modelValue: Record<string, unknown>;\n /** 表单动态值缓存,由父组件统一创建并在搜索表单/弹窗表单间复用同一套逻辑。 */\n runtimeCache: FormRuntimeCache;\n /** 当前字段校验规则,父组件按字段预先聚合后传入。 */\n rules?: unknown[];\n /** 是否整体禁用,查看模式会传 true。 */\n disabled?: boolean;\n}>();\n\n/** 字段值更新事件,保持 DynamicForm 对外的 v-model 行为不变。 */\nconst emit = defineEmits<{\n \"update:modelValue\": [value: Record<string, unknown>];\n}>();\n\n/** 当前 UI adapter,负责字段组件、选项组件和 v-model 差异适配。 */\nconst { adapter } = useAutoCrudContext();\n\n/**\n * 自定义字段渲染桥接组件。\n *\n * item.render 返回的是 VNodeChild,通过该组件可以在模板中稳定渲染出来。\n */\nconst RenderContent = defineComponent({\n props: {\n renderer: {\n type: Function,\n required: true,\n },\n },\n setup(renderProps) {\n return () => renderProps.renderer();\n },\n});\n\n/** 当前字段的中立组件类型,例如 input/select/datePicker。 */\nconst fieldType = computed(() => getFormComponent(props.item));\n\n/** 根据字段 component 配置获取真实控件;未知类型默认降级为 input。 */\nconst fieldComponent = computed<CrudAdapterComponent>(() => {\n return adapter.components.fields[fieldType.value as keyof typeof adapter.components.fields] || adapter.components.fields.input;\n});\n\n/** 获取字段选项,支持静态数组,也支持根据当前表单数据动态生成。 */\nconst fieldOptions = computed(() => props.runtimeCache.getOptions(props.item, props.modelValue));\n\n/** 获取选项组件,兼容 radio、checkbox、select 在不同 UI 库下的子组件差异。 */\nconst optionComponent = computed<CrudAdapterComponent>(() => {\n if (fieldType.value === \"radio\") return adapter.components.radio || \"option\";\n if (fieldType.value === \"checkbox\") return adapter.components.checkbox || \"option\";\n return adapter.components.option || \"option\";\n});\n\n/** 判断选项是否需要作为子节点渲染,Element Plus 的 select 就属于这种模式。 */\nconst shouldRenderOptionsAsChildren = computed(() => {\n if ([\"radio\", \"checkbox\"].includes(fieldType.value)) return true;\n return adapter.name === \"element-plus\" && [\"select\", \"multipleSelect\"].includes(fieldType.value);\n});\n\n/** 生成表单项 props,包括字段路径、动态 label 和当前字段校验规则。 */\nconst formItemProps = computed(() =>\n adapter.form.getFormItemProps(props.item, {\n prop: props.item.prop,\n label: props.runtimeCache.getLabel(props.item, props.modelValue),\n rules: props.rules,\n })\n);\n\n/**\n * 生成字段控件 props。\n *\n * 不同 UI 库的 v-model 字段和事件名不同,例如 modelValue/update:modelValue、value/update:value。\n * adapter.form.getModelBinding 会返回当前控件需要的绑定字段和更新事件。\n */\nconst fieldProps = computed(() => {\n const binding = adapter.form.getModelBinding(fieldType.value as keyof typeof adapter.components.fields, props.item);\n const value = getByPath(props.modelValue, props.item.prop);\n const propsFromAdapter = adapter.form.getFieldProps(props.item, {\n value,\n placeholder: props.runtimeCache.getPlaceholder(props.item, props.modelValue),\n disabled: props.runtimeCache.getDisabled(props.item, props.modelValue, props.disabled),\n options: fieldOptions.value,\n });\n const normalizedFieldProps = normalizeNaiveDateFieldProps(propsFromAdapter);\n const nativeModelUpdate = getFieldEventHandler(normalizedFieldProps[binding.event]);\n const nativeChange = getFieldEventHandler(normalizedFieldProps.onChange);\n const nativeBlur = getFieldEventHandler(normalizedFieldProps.onBlur);\n const nativeFocus = getFieldEventHandler(normalizedFieldProps.onFocus);\n\n return {\n ...normalizedFieldProps,\n [binding.prop]: value,\n [binding.event]: (nextValue: unknown) => {\n const oldValue = getByPath(props.modelValue, props.item.prop);\n setByPath(props.modelValue, props.item.prop, nextValue);\n emit(\"update:modelValue\", props.modelValue);\n nativeModelUpdate?.(nextValue);\n nativeChange?.(nextValue);\n props.item.onChange?.(nextValue, createFieldEventContext(oldValue));\n },\n /**\n * props.onChange 已经在模型更新链路里执行。\n * 这里吞掉原生 change 事件,避免部分 UI 同时触发 update/change 时重复调用业务回调。\n */\n onChange: undefined,\n onBlur: (event: unknown) => {\n const oldValue = getByPath(props.modelValue, props.item.prop);\n nativeBlur?.(event);\n props.item.onBlur?.(event, createFieldEventContext(oldValue));\n },\n onFocus: (event: unknown) => {\n const oldValue = getByPath(props.modelValue, props.item.prop);\n nativeFocus?.(event);\n props.item.onFocus?.(event, createFieldEventContext(oldValue));\n },\n };\n});\n\n/**\n * 创建字段事件上下文。\n *\n * 这里不直接把 setByPath 暴露给业务,是为了统一补上 update:modelValue,\n * 让字段联动和用户手动输入走同一条数据更新链路。\n */\nfunction createFieldEventContext(oldValue: unknown): CrudFormFieldEventContext {\n return {\n prop: props.item.prop,\n item: props.item,\n component: fieldType.value as CrudFormFieldEventContext[\"component\"],\n formData: props.modelValue,\n oldValue,\n setFieldValue: (prop: string, value: unknown) => {\n setByPath(props.modelValue, prop, value);\n emit(\"update:modelValue\", props.modelValue);\n },\n };\n}\n\n/** 只包装函数类型的原生事件,避免把非函数 props 当成事件执行。 */\nfunction getFieldEventHandler(handler: unknown): FieldEventHandler | undefined {\n return typeof handler === \"function\" ? (handler as FieldEventHandler) : undefined;\n}\n\n/**\n * Naive UI 的日期组件底层使用 date-fns,不接受 dayjs / Element Plus 风格的 `YYYY-MM-DD`。\n *\n * adapter 层已经做了一次转换;这里在真实控件 props 出口再兜底一次,\n * 防止业务通过自定义 adapter、componentProps 或后续扩展路径绕过 adapter 处理。\n */\nfunction normalizeNaiveDateFieldProps(fieldProps: Record<string, unknown>): Record<string, unknown> {\n if (adapter.name !== \"naive\") return fieldProps;\n if (fieldType.value !== \"datePicker\" && fieldType.value !== \"timePicker\") return fieldProps;\n\n return {\n ...fieldProps,\n format: normalizeDateFnsFormat(fieldProps.format),\n valueFormat: normalizeDateFnsFormat(fieldProps.valueFormat),\n };\n}\n\n/** adapter component 在模板中原样返回即可,保留函数便于组件写法统一。 */\nfunction resolveAdapterComponent(component: CrudAdapterComponent): CrudAdapterComponent {\n return component;\n}\n</script>\n","<template>\n <!-- 表单根组件:真实表单组件由 adapter 决定,例如 ElForm、NForm、AForm。 -->\n <component :is=\"resolveAdapterComponent(adapter.components.form)\" ref=\"formRef\" v-bind=\"formProps\">\n <!-- 栅格布局:搜索表单通常多列展示,弹窗表单默认更适合单列展示。 -->\n <div class=\"vac-form-grid\" :style=\"layoutStyle\">\n <template v-for=\"item in visibleColumns\" :key=\"item.prop\">\n <!-- colSpan 作用在自有原生容器上,不依赖三方 FormItem 是否透传 style。 -->\n <div class=\"vac-form-grid-item\" :style=\"getFormItemGridStyle(item)\">\n <DynamicFormItem\n :item=\"item\"\n :model-value=\"formData\"\n :runtime-cache=\"runtimeCache\"\n :rules=\"rules[item.prop]\"\n :disabled=\"disabled\"\n @update:model-value=\"value => (formData = value)\"\n />\n </div>\n </template>\n\n <!-- 搜索表单专属按钮区;弹窗表单由 CrudModal footer 触发 submit。 -->\n <div v-if=\"formType === 'search'\" class=\"vac-form-actions\" :style=\"searchActionsStyle\">\n <component :is=\"resolveAdapterComponent(adapter.components.button)\" v-bind=\"adapter.render.getButtonProps({ type: 'primary' })\" @click=\"submit\">\n 搜索\n </component>\n <component :is=\"resolveAdapterComponent(adapter.components.button)\" v-bind=\"adapter.render.getButtonProps({ type: 'default' })\" @click=\"reset\">\n 重置\n </component>\n <slot name=\"searchButtonToolbar\" />\n </div>\n </div>\n </component>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport type { CrudAdapterComponent } from \"../adapters/types\";\nimport type { CrudFormItem, CrudFormLayout } from \"../core/types\";\nimport { useAutoCrudContext } from \"../context\";\nimport { createFormRuntimeCache } from \"../core/form-runtime-cache\";\nimport DynamicFormItem from \"./DynamicFormItem.vue\";\n\nconst props = withDefaults(\n defineProps<{\n /** 当前表单用途:搜索表单会显示搜索/重置按钮,弹窗表单只暴露 submit 给外部 footer 调用。 */\n formType?: \"search\" | \"modal\";\n /** 表单字段 schema。 */\n columns: CrudFormItem[];\n /** 表单数据,支持 v-model。 */\n modelValue: Record<string, unknown>;\n /** 表单布局配置。 */\n layout?: CrudFormLayout;\n /** 是否整体禁用,查看模式会传 true。 */\n disabled?: boolean;\n }>(),\n {\n formType: \"search\",\n disabled: false,\n }\n);\n\nconst emit = defineEmits<{\n /** v-model 更新事件,所有字段变更最终都会同步到父级配置对象。 */\n \"update:modelValue\": [value: Record<string, unknown>];\n /** 表单校验通过后的提交事件。 */\n submit: [value: Record<string, unknown>];\n /** 搜索表单重置事件,由上层 hook 决定如何恢复默认数据。 */\n reset: [];\n}>();\n\n/** 当前 UI adapter,负责表单 props、校验方法、字段绑定方式的差异适配。 */\nconst { adapter } = useAutoCrudContext();\n/** 真实 UI 表单实例引用,validate/clearValidate 会委托给 adapter 执行。 */\nconst formRef = ref<unknown>(null);\n\n/** 表单运行时缓存,scope 区分搜索表单和弹窗表单,避免同 prop 字段互相覆盖缓存。 */\nconst runtimeCache = createFormRuntimeCache(props.formType);\n\n/** 表单数据代理,既能读取父级 v-model,也能在字段变更时向外同步。 */\nconst formData = computed({\n get: () => props.modelValue,\n set: value => emit(\"update:modelValue\", value),\n});\n\n/** 支持 show 函数动态控制字段显隐,例如根据状态展示停用原因。 */\nconst visibleColumns = computed(() => props.columns.filter(item => runtimeCache.getShow(item, formData.value)));\n/** 根据字段 required/rules 等配置生成当前表单校验规则,隐藏字段不参与校验。 */\nconst rules = computed(() =>\n visibleColumns.value.reduce<Record<string, unknown[]>>((result, item) => {\n const itemRules = runtimeCache.getRules(item, formData.value);\n if (item.prop && itemRules.length > 0) result[item.prop] = itemRules;\n return result;\n }, {})\n);\n/** 表单根组件 props,由 adapter 转成不同 UI 库的 model/rules/disabled 等字段。 */\nconst formProps = computed(() => adapter.form.getFormProps({ model: formData.value, rules: rules.value, disabled: props.disabled }));\n/** 根据 formType 和 layout 计算 CSS grid 样式;弹窗表单默认单列,搜索表单默认四列。 */\nconst layoutStyle = computed(() => adapter.form.getLayoutStyle(getNormalizedLayout(), props.formType));\n/** 搜索按钮区所在列:根据可见字段数量补齐当前行剩余列,让按钮始终靠到栅格最右侧。 */\nconst searchActionsStyle = computed(() => {\n const columns = getLayoutColumns();\n const usedColumns = visibleColumns.value.reduce((total, item) => total + getItemColSpan(item), 0) % columns;\n const startColumn = usedColumns === 0 ? columns : usedColumns + 1;\n\n return {\n gridColumn: `${startColumn} / ${columns + 1}`,\n };\n});\n\n/** 当前表单一行被分成几份;默认跟 adapter 的布局默认值保持一致。 */\nfunction getLayoutColumns(): number {\n return Math.max(Number(getNormalizedLayout().columns || 1), 1);\n}\n\n/**\n * 归一化表单布局。\n *\n * 搜索表单需要横向铺开以提升查询效率,默认四列;\n * 弹窗表单更重视填写稳定性,默认一列,业务显式传 layout.columns 时仍然尊重业务配置。\n */\nfunction getNormalizedLayout(): CrudFormLayout {\n return {\n ...props.layout,\n columns: props.layout?.columns || (props.formType === \"modal\" ? 1 : 4),\n };\n}\n\n/** 获取字段实际占用份数,避免业务传入 0、负数或超过整行列数导致布局错位。 */\nfunction getItemColSpan(item: CrudFormItem): number {\n const columns = getLayoutColumns();\n const span = Number(item.colSpan || 1);\n if (!Number.isFinite(span)) return 1;\n return Math.min(Math.max(Math.floor(span), 1), columns);\n}\n\n/** 单个字段的栅格跨度,和搜索按钮占位计算使用同一套 colSpan 规则。 */\nfunction getFormItemGridStyle(item: CrudFormItem): Record<string, string> {\n return {\n gridColumn: `span ${getItemColSpan(item)}`,\n };\n}\n\n/** adapter component 在模板中原样返回即可,保留函数便于组件写法统一。 */\nfunction resolveAdapterComponent(component: CrudAdapterComponent): CrudAdapterComponent {\n return component;\n}\n\n/**\n * 校验表单。\n *\n * 校验方法由 adapter 调用当前 UI 的真实表单实例,\n * 因此这里不需要知道 `ElForm`、`NForm`、`AForm` 的方法细节。\n */\nasync function validate(): Promise<boolean> {\n return adapter.form.validate(formRef.value);\n}\n\n/** 清空表单校验状态。 */\nfunction clearValidate(): void {\n adapter.form.clearValidate(formRef.value);\n}\n\n/** 提交表单。 */\nasync function submit(): Promise<void> {\n const valid = await validate();\n if (!valid) return;\n emit(\"submit\", formData.value);\n}\n\n/** 重置表单。 */\nfunction reset(): void {\n clearValidate();\n emit(\"reset\");\n}\n\ndefineExpose({\n validate,\n clearValidate,\n submit,\n reset,\n});\n</script>\n","import dayjs from \"dayjs\";\nimport type { CrudImageOptions, CrudTableAction, CrudTableColumn } from \"./types\";\nimport { getByPath, resolveValue } from \"./object-utils\";\n\n/** 获取列字段路径,兼容新字段 `field` 和旧字段 `key`。 */\nexport function getColumnField<T extends Record<string, unknown>>(column: CrudTableColumn<T>): string {\n if (column.field) return String(column.field);\n if (typeof column.key === \"function\") return \"\";\n return column.key ? String(column.key) : \"\";\n}\n\n/** 获取行数据中的单元格值,支持点路径。 */\nexport function getCellValue<T extends Record<string, unknown>>(row: T, column: CrudTableColumn<T>): unknown {\n const field = getColumnField(column);\n return field ? getByPath(row, field) : undefined;\n}\n\n/** 判断列是否显示。 */\nexport function shouldShowColumn<T extends Record<string, unknown>>(column: CrudTableColumn<T>, data: T[], index: number): boolean {\n if (!(\"show\" in column)) return true;\n return Boolean(resolveValue(column.show as boolean | ((column: CrudTableColumn<T>, data: T[], index: number) => boolean), column, data, index));\n}\n\n/** 获取实际操作按钮列表,兼容旧字段 buttons。 */\nexport function getColumnActions<T extends Record<string, unknown>>(column: CrudTableColumn<T>): CrudTableAction<T>[] {\n return column.actions || column.buttons || [];\n}\n\n/** 判断操作按钮是否显示。 */\nexport function shouldShowAction<T>(row: T, action: CrudTableAction<T>): boolean {\n if (!(\"show\" in action)) return true;\n return Boolean(resolveValue(action.show as boolean | ((row: T, action: CrudTableAction<T>) => boolean), row, action));\n}\n\n/** 获取操作按钮文案,兼容函数写法。 */\nexport function getActionLabel<T>(row: T, action: CrudTableAction<T>): string {\n return String(resolveValue(action.label, row, action));\n}\n\n/** 获取确认文案,未配置时给出通用中文提示。 */\nexport function getActionConfirmText<T>(row: T, action: CrudTableAction<T>): string {\n if (!action.confirmText) return \"请确认是否继续操作?\";\n return String(resolveValue(action.confirmText, row, action));\n}\n\n/** 格式化日期列。 */\nexport function formatDateTime(value: unknown, format: boolean | string = true): string {\n if (value === null || value === undefined || value === \"\") return \"\";\n const pattern = typeof format === \"string\" ? format : \"YYYY-MM-DD HH:mm:ss\";\n return dayjs(value as string | number | Date).format(pattern);\n}\n\n/** 归一化图片列配置。 */\nexport function normalizeImageOptions(image: boolean | CrudImageOptions | undefined): Required<CrudImageOptions> {\n const options = typeof image === \"object\" ? image : {};\n return {\n width: options.width ?? 40,\n height: options.height ?? 40,\n objectFit: options.objectFit ?? \"cover\",\n preview: options.preview ?? true,\n };\n}\n","<template>\n <span class=\"vac-copy-text\">\n <span>{{ displayText }}</span>\n <button class=\"vac-link-button\" type=\"button\" @click=\"handleCopy\">复制</button>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { useAutoCrudContext } from \"../context\";\nimport { copyText } from \"../utils/clipboard\";\n\nconst props = defineProps<{\n /** 要展示和复制的文本。 */\n value: unknown;\n}>();\n\nconst { adapter } = useAutoCrudContext();\n\nconst displayText = computed(() => (props.value === null || props.value === undefined ? \"\" : String(props.value)));\n\n/**\n * 执行复制。\n *\n * 组件只负责交互,真正复制能力放在 `utils/clipboard.ts`,\n * 消息提示通过当前 adapter 发送,因此不依赖任何具体 UI 库。\n */\nasync function handleCopy(): Promise<void> {\n try {\n await copyText(displayText.value);\n adapter.feedback.success(\"复制成功\");\n } catch {\n adapter.feedback.error(\"复制失败\");\n }\n}\n</script>\n","<template>\n <span class=\"vac-decode-text\">\n <span>{{ visible ? decodedText : maskedText }}</span>\n <button class=\"vac-link-button\" type=\"button\" @click=\"handleDecode\">\n {{ visible ? \"隐藏\" : \"查看\" }}\n </button>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\nimport { useAutoCrudContext } from \"../context\";\n\nconst props = defineProps<{\n /** 原始敏感值,通常是手机号、证件号等密文或脱敏文本。 */\n value: unknown;\n /** 当前行数据,传给用户注入的 decode/decodeLog 函数。 */\n row?: Record<string, unknown>;\n}>();\n\nconst context = useAutoCrudContext();\nconst visible = ref(false);\nconst decodedText = ref(\"\");\n\nconst rawText = computed(() => (props.value === null || props.value === undefined ? \"\" : String(props.value)));\nconst maskedText = computed(() => (rawText.value ? \"******\" : \"\"));\n\n/**\n * 查看敏感信息。\n *\n * 旧包把解密和操作日志写死在组件里,新包改为初始化注入:\n * - `decode` 决定如何解密。\n * - `decodeLog` 决定如何记录查看日志。\n * 这样 npm 包不绑定任何业务接口。\n */\nasync function handleDecode(): Promise<void> {\n if (visible.value) {\n visible.value = false;\n return;\n }\n\n try {\n decodedText.value = context.decode ? await context.decode(props.value, props.row) : rawText.value;\n await context.decodeLog?.(props.value, props.row);\n visible.value = true;\n } catch {\n context.adapter.feedback.error(\"查看失败\");\n }\n}\n</script>\n","<template>\n <div ref=\"tableRootRef\" class=\"vac-table-root\">\n <!-- Tabs 区域:用于状态筛选、分类筛选等轻量切换,切换后由 tableConfig.setTabsValue 统一触发加载。 -->\n <div v-if=\"tableConfig.tabs\" class=\"vac-tabs\">\n <button\n v-for=\"tab in tableConfig.tabs.options\"\n :key=\"String(tab.value)\"\n class=\"vac-tab\"\n :class=\"{ 'is-active': tab.value === tableConfig.tabs.value }\"\n type=\"button\"\n :disabled=\"tab.disabled\"\n @click=\"handleTabsChange(tab.value)\"\n >\n {{ tab.label }}\n </button>\n </div>\n\n <!-- 表格工具栏:默认左侧留空,右侧通过插槽交给业务放导出、批量删除等操作。 -->\n <div class=\"vac-table-toolbar\">\n <div />\n <slot name=\"tableToolbar\" />\n </div>\n\n <!-- 表格主体:真实组件来自 adapter,例如 ElTable、NDataTable、ATable。 -->\n <div ref=\"tableBodyRef\" class=\"vac-table-body\" :style=\"tableBodyStyle\">\n <component :is=\"resolveAdapterComponent(adapter.components.table)\" v-bind=\"tableProps\">\n <!-- 非对象列配置模式:Element Plus 这类 UI 需要显式渲染 TableColumn 子组件。 -->\n <template v-if=\"!adapter.table.useColumnConfig\">\n <component\n v-for=\"(column, index) in visibleColumns\"\n :key=\"getColumnKey(column, index)\"\n :is=\"resolveAdapterComponent(adapter.components.tableColumn || 'div')\"\n v-bind=\"adapter.table.getTableColumnProps(column)\"\n >\n <template #default=\"scope\">\n <!-- 单元格内容通过渲染函数桥接,确保 copy/decode/image/actions 等能力都能统一复用。 -->\n <component :is=\"CellContent\" :renderer=\"() => renderCell(scope.row, column, scope.$index ?? index)\" />\n </template>\n </component>\n </template>\n </component>\n </div>\n\n <!-- 分页区域:showPagination 只控制显隐,分页数据对象始终由 tableConfig.pagination 维护。 -->\n <div v-if=\"tableConfig.showPagination\" ref=\"paginationRef\" class=\"vac-pagination\">\n <component\n :is=\"resolveAdapterComponent(adapter.components.pagination)\"\n v-bind=\"paginationProps\"\n v-on=\"paginationEvents\"\n />\n </div>\n </div>\n\n <!-- 高度调试标注通过 Teleport 浮在页面上方,不占用表格布局高度,也不参与自动高度计算。 -->\n <Teleport to=\"body\">\n <div v-if=\"heightDebug.visible\" class=\"vac-table-height-debug\">\n <span>adapter: {{ heightDebug.adapter }}</span>\n <span>边界底部: {{ heightDebug.boundaryBottom }}px</span>\n <span>表格top: {{ heightDebug.bodyTop }}px</span>\n <span>分页占位: {{ heightDebug.paginationHeight }}px</span>\n <span>底部预留: {{ heightDebug.bottomGap }}px</span>\n <span>UI回收: {{ heightDebug.adapterOffset }}px</span>\n <span>原始高度: {{ heightDebug.rawHeight }}px</span>\n <span>表格盒子: {{ heightDebug.tableBoxHeight }}px</span>\n <span>表头: {{ heightDebug.headerHeight }}px</span>\n <span>scrollY: {{ heightDebug.scrollY }}px</span>\n <span :class=\"{ 'is-warning': heightDebug.pageOverflow > 0 }\">页面溢出: {{ heightDebug.pageOverflow }}px</span>\n </div>\n </Teleport>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, defineComponent, h, nextTick, onBeforeUnmount, onMounted, ref, resolveComponent, watch } from \"vue\";\nimport type { CrudAdapterComponent } from \"../adapters/types\";\nimport type { CrudTableColumn, CrudTableConfig } from \"../core/types\";\nimport { useAutoCrudContext } from \"../context\";\nimport {\n formatDateTime,\n getActionConfirmText,\n getActionLabel,\n getCellValue,\n getColumnActions,\n getColumnField,\n normalizeImageOptions,\n shouldShowAction,\n shouldShowColumn,\n} from \"../core/table-utils\";\nimport CopyText from \"./CopyText.vue\";\nimport DecodeText from \"./DecodeText.vue\";\n\nconst props = defineProps<{\n /** useTable 返回的表格配置,包含列、数据、分页、加载状态和列表操作方法。 */\n tableConfig: CrudTableConfig<any>;\n}>();\n\n/** 当前 UI adapter,负责把中立配置转换成具体 UI 库的 props 和事件。 */\nconst { adapter } = useAutoCrudContext();\n/** 表格根节点,用于监听搜索区变化后表格所在位置是否改变。 */\nconst tableRootRef = ref<HTMLElement | null>(null);\n/** 表格主体容器,自动高度按它距离滚动边界底部的位置计算。 */\nconst tableBodyRef = ref<HTMLElement | null>(null);\n/** 分页固定在表格外部,自动高度需要扣掉它的占位。 */\nconst paginationRef = ref<HTMLElement | null>(null);\n/** 当前表格组件外层可使用的总高度,包含表头和表体。 */\nconst autoTableBoxHeight = ref<number>();\n/** Naive UI / Ant Design Vue 的 scroll.y 描述表体高度,需要扣除真实表头高度。 */\nconst tableHeaderHeight = ref(0);\n/** 页面可视化调试数据,帮助确认自动高度到底扣掉了哪些区域。 */\nconst heightDebug = ref({\n visible: false,\n adapter: adapter.name,\n boundaryBottom: 0,\n bodyTop: 0,\n paginationHeight: 0,\n bottomGap: 0,\n adapterOffset: 0,\n rawHeight: 0,\n tableBoxHeight: 0,\n headerHeight: 0,\n scrollY: 0,\n pageOverflow: 0,\n});\nlet resizeObserver: ResizeObserver | undefined;\n\n/**\n * 渲染函数桥接组件。\n *\n * Vue 模板里不能直接渲染一个函数返回的 VNode,因此用一个轻量组件包一层。\n */\nconst CellContent = defineComponent({\n props: {\n renderer: {\n type: Function,\n required: true,\n },\n },\n setup(cellProps) {\n return () => cellProps.renderer();\n },\n});\n\n/** 过滤掉 show 返回 false 的列,支持权限、数据状态等动态控制列显隐。 */\nconst visibleColumns = computed(() => props.tableConfig.columns.filter((column, index) => shouldShowColumn(column, props.tableConfig.data, index)));\n/** 对 Naive UI、Ant Design Vue 这类对象列配置模式,提前生成 columns 数组。 */\nconst objectColumns = computed(() => visibleColumns.value.map((column, index) => createObjectColumn(column, index)));\n/** 表格 props 由 adapter 统一生成,屏蔽不同 UI 库 data/loading/rowKey/columns 字段差异。 */\nconst tableProps = computed(() =>\n adapter.table.getTableProps({\n columns: objectColumns.value,\n data: props.tableConfig.data,\n loading: props.tableConfig.loading,\n rowKey: props.tableConfig.rowKey,\n scrollY: resolvedScrollY.value,\n scrollX: props.tableConfig.scroll.x,\n })\n);\n/** 自动高度开启时约束表格盒子,避免外层页面被表格内容继续撑出第二个滚动条。 */\nconst tableBodyStyle = computed(() => {\n const height = props.tableConfig.scroll.height;\n if (height !== true && height !== undefined) return undefined;\n if (!autoTableBoxHeight.value) return undefined;\n if (adapter.name === \"element-plus\") return undefined;\n return {\n height: `${autoTableBoxHeight.value}px`,\n overflow: \"hidden\",\n };\n});\n/** 最终传给 UI 表格的纵向滚动高度:业务传具体值时优先,true 时使用自动计算结果。 */\nconst resolvedScrollY = computed(() => {\n const height = props.tableConfig.scroll.height;\n if (height === false) return undefined;\n if (height === true || height === undefined) {\n if (!autoTableBoxHeight.value) return undefined;\n if (adapter.name === \"element-plus\") return autoTableBoxHeight.value;\n return Math.max(autoTableBoxHeight.value - tableHeaderHeight.value - getAdapterScrollOffset(), 180);\n }\n return height;\n});\n/** 分页 props 由 adapter 统一生成,屏蔽 total/current/pageSize 等字段差异。 */\nconst paginationProps = computed(() => adapter.table.getPaginationProps(props.tableConfig.pagination));\n/** 分页事件由 adapter 统一生成,最终回到 tableConfig 的分页方法并触发列表加载。 */\nconst paginationEvents = computed(() =>\n adapter.table.getPaginationEvents({\n onPageChange: page => void props.tableConfig.setPage(page),\n onPageSizeChange: pageSize => void props.tableConfig.setPageSize(pageSize),\n })\n);\n\n/** adapter component 在模板中原样返回即可,保留函数便于所有组件保持同一写法。 */\nfunction resolveAdapterComponent(component: CrudAdapterComponent): unknown {\n return component;\n}\n\n/** 渲染函数中需要把字符串组件名解析成真实组件,组件对象则直接返回。 */\nfunction resolveRuntimeComponent(component: CrudAdapterComponent): string | object {\n return typeof component === \"string\" ? resolveComponent(component) : component;\n}\n\n/** 生成列 key,优先使用字段名,避免动态列重排时出现渲染复用错误。 */\nfunction getColumnKey(column: CrudTableColumn<Record<string, unknown>>, index: number): string {\n return getColumnField(column) || String(index);\n}\n\n/**\n * 生成对象式列配置。\n *\n * Naive UI、Ant Design Vue 通常通过 columns 数组描述列,Element Plus 则通过子组件描述列。\n * 这里把同一份 CrudTableColumn 转换成当前 UI 能理解的列结构。\n */\nfunction createObjectColumn(column: CrudTableColumn<Record<string, unknown>>, index: number): Record<string, unknown> {\n const base = adapter.table.getTableColumnProps(column);\n\n if (adapter.name === \"ant-design-vue\") {\n return {\n ...base,\n customRender: ({ record, index: rowIndex }: { record: Record<string, unknown>; index: number }) => renderCell(record, column, rowIndex),\n };\n }\n\n return {\n ...base,\n render: (row: Record<string, unknown>, rowIndex: number) => renderCell(row, column, rowIndex ?? index),\n };\n}\n\n/**\n * 渲染单元格。\n *\n * 优先级说明:\n * 1. `render` 是业务完全自定义,优先级最高。\n * 2. `actions` 表示操作列。\n * 3. `decode/copy/image/dateTime` 是内置常见展示增强。\n * 4. 都没有时按普通文本展示。\n */\nfunction renderCell(row: Record<string, unknown>, column: CrudTableColumn<Record<string, unknown>>, index: number): unknown {\n const value = getCellValue(row, column);\n\n if (column.render) return column.render(row, column, index);\n if (getColumnActions(column).length > 0) return renderActions(row, column);\n if (column.decode) return h(DecodeText, { value, row });\n if (column.copy) return h(CopyText, { value });\n if (column.image) return renderImage(value, column);\n if (column.dateTime) return formatDateTime(value, column.dateTime);\n return value === null || value === undefined ? \"\" : String(value);\n}\n\n/**\n * 渲染操作列按钮。\n *\n * 操作列支持:\n * - show:按行数据动态控制按钮显隐。\n * - confirm/isConfirm:执行前二次确认。\n * - onClick:业务自定义操作,例如打开弹窗、删除数据、跳转详情。\n */\nfunction renderActions(row: Record<string, unknown>, column: CrudTableColumn<Record<string, unknown>>): unknown {\n const actions = getColumnActions(column).filter(action => shouldShowAction(row, action));\n\n return h(\n \"span\",\n { class: \"vac-actions\" },\n actions.map(action => {\n const buttonProps = adapter.render.getButtonProps({ type: adapter.render.normalizeActionType(action), size: action.size });\n const onClick = async (event: MouseEvent) => {\n const needConfirm = action.confirm ?? action.isConfirm;\n if (needConfirm && !window.confirm(getActionConfirmText(row, action))) return;\n await action.onClick(row, event);\n };\n\n return h(resolveRuntimeComponent(adapter.components.button), { ...buttonProps, onClick }, () => getActionLabel(row, action));\n })\n );\n}\n\n/** 渲染图片列,图片尺寸、预览等能力由 column.image 和 adapter 共同决定。 */\nfunction renderImage(value: unknown, column: CrudTableColumn<Record<string, unknown>>): unknown {\n const src = value === null || value === undefined ? \"\" : String(value);\n const options = normalizeImageOptions(column.image);\n const imageProps = adapter.render.getImageProps({ src, ...options });\n return h(resolveRuntimeComponent(adapter.components.image), { ...imageProps, class: \"vac-image\" });\n}\n\n/** tabs 切换入口,统一委托给 tableConfig,保证切换分类后分页和列表加载逻辑集中维护。 */\nasync function handleTabsChange(value: string | number): Promise<void> {\n await props.tableConfig.setTabsValue(value);\n}\n\n/**\n * 根据当前视口计算表格主体可用高度。\n *\n * 搜索表单、页面 padding、工具栏高度都会体现在 tableBodyRef.getBoundingClientRect().top 中。\n * 这里计算的是“表格组件盒子总高度”,adapter 再按 UI 库差异转换成具体的滚动属性。\n */\nfunction updateAutoTableHeight(): void {\n if (props.tableConfig.scroll.height !== true && props.tableConfig.scroll.height !== undefined) return;\n if (typeof window === \"undefined\") return;\n\n const bodyEl = tableBodyRef.value;\n if (!bodyEl) return;\n\n tableHeaderHeight.value = measureTableHeaderHeight(bodyEl);\n\n const bodyTop = bodyEl.getBoundingClientRect().top;\n const paginationHeight = props.tableConfig.showPagination ? getOuterHeight(paginationRef.value) : 0;\n const boundaryBottom = getScrollBoundaryBottom(bodyEl);\n const bottomGap = getBottomSafeGap(bodyEl);\n const adapterOffset = getAdapterBoxOffset();\n const nextHeight = Math.floor(boundaryBottom - bodyTop - paginationHeight - bottomGap - adapterOffset);\n\n autoTableBoxHeight.value = normalizeAutoTableHeight(nextHeight);\n updateHeightDebug({\n boundaryBottom,\n bodyTop,\n paginationHeight,\n bottomGap,\n adapterOffset,\n rawHeight: nextHeight,\n tableBoxHeight: autoTableBoxHeight.value,\n headerHeight: tableHeaderHeight.value,\n });\n}\n\nfunction scheduleUpdateAutoTableHeight(): void {\n void nextTick(() => {\n updateAutoTableHeight();\n // 表格首次拿到滚动高度后会重新渲染表头和分页,多帧校准可以避开 UI 首帧高度抖动。\n requestAnimationFrame(() => {\n updateAutoTableHeight();\n requestAnimationFrame(updateAutoTableHeight);\n });\n });\n}\n\n/** 避免布局首帧尚未稳定时,把表格高度错误锁定到最小兜底值。 */\nfunction normalizeAutoTableHeight(nextHeight: number): number {\n const minHeight = adapter.name === \"element-plus\" ? 360 : 240;\n if (nextHeight >= minHeight) return nextHeight;\n if (autoTableBoxHeight.value && autoTableBoxHeight.value > minHeight) return autoTableBoxHeight.value;\n return minHeight;\n}\n\n/** 获取元素高度并包含上下 margin,分页区固定在表格外部时需要一起扣除。 */\nfunction getOuterHeight(element: HTMLElement | null): number {\n if (!element) return 0;\n const style = window.getComputedStyle(element);\n return element.offsetHeight + Number.parseFloat(style.marginTop || \"0\") + Number.parseFloat(style.marginBottom || \"0\");\n}\n\n/** 自动寻找最近的滚动边界;没有独立滚动容器时使用当前视口底部。 */\nfunction getScrollBoundaryBottom(element: HTMLElement): number {\n if (adapter.name === \"element-plus\") return window.innerHeight;\n\n let parent = element.parentElement;\n while (parent && parent !== document.body && parent !== document.documentElement) {\n const style = window.getComputedStyle(parent);\n const hasVerticalScroll = parent.scrollHeight > parent.clientHeight + 1;\n if (/(auto|scroll|overlay)/.test(style.overflowY) && hasVerticalScroll && parent.clientHeight > 0) {\n return parent.getBoundingClientRect().bottom;\n }\n parent = parent.parentElement;\n }\n return window.innerHeight;\n}\n\n/** 预留页面和卡片底部空间,避免高度计算刚好顶满后触发页面级滚动条。 */\nfunction getBottomSafeGap(element: HTMLElement): number {\n const cardBody = element.closest(\".el-card__body, .n-card__content, .ant-card-body\") as HTMLElement | null;\n const pageContent = element.closest(\".demo-content, main, .page-content\") as HTMLElement | null;\n const cardPadding = cardBody ? Number.parseFloat(window.getComputedStyle(cardBody).paddingBottom || \"0\") : 0;\n const pagePadding = pageContent ? Number.parseFloat(window.getComputedStyle(pageContent).paddingBottom || \"0\") : 0;\n\n if (adapter.name === \"element-plus\") {\n return Math.max(16, cardPadding + pagePadding + 1);\n }\n\n return Math.max(16, cardPadding + pagePadding);\n}\n\n/** 不同 UI 表格外壳存在边框、滚动容器或取整误差,这里统一做小幅回收,避免页面级滚动条残留。 */\nfunction getAdapterBoxOffset(): number {\n if (adapter.name === \"naive\") return 21;\n if (adapter.name === \"ant-design-vue\") return 2;\n return 0;\n}\n\n/** AntD/Naive 的 scrollY 作用于表体,需要在表头之外继续扣除外壳误差。 */\nfunction getAdapterScrollOffset(): number {\n if (adapter.name === \"naive\") return 21;\n if (adapter.name === \"ant-design-vue\") return 2;\n return 0;\n}\n\n/** 不同 UI 库表头 DOM 结构不同,这里只测量表头高度,不参与业务数据逻辑。 */\nfunction measureTableHeaderHeight(element: HTMLElement): number {\n if (adapter.name === \"element-plus\") return 0;\n const header = element.querySelector(\".n-data-table-thead, .ant-table-thead\") as HTMLElement | null;\n return header ? Math.ceil(header.getBoundingClientRect().height) : 0;\n}\n\n/** 把自动高度公式中的每个关键数字暴露到页面,便于截图排查双滚动条来源。 */\nfunction updateHeightDebug(input: {\n boundaryBottom: number;\n bodyTop: number;\n paginationHeight: number;\n bottomGap: number;\n adapterOffset: number;\n rawHeight: number;\n tableBoxHeight: number;\n headerHeight: number;\n}): void {\n const scrollY = typeof resolvedScrollY.value === \"number\" ? resolvedScrollY.value : Number.parseFloat(String(resolvedScrollY.value || 0));\n const pageOverflow = Math.max(document.documentElement.scrollHeight - window.innerHeight, document.body.scrollHeight - window.innerHeight, 0);\n\n heightDebug.value = {\n visible: true,\n adapter: adapter.name,\n boundaryBottom: Math.round(input.boundaryBottom),\n bodyTop: Math.round(input.bodyTop),\n paginationHeight: Math.round(input.paginationHeight),\n bottomGap: Math.round(input.bottomGap),\n adapterOffset: Math.round(input.adapterOffset),\n rawHeight: Math.round(input.rawHeight),\n tableBoxHeight: Math.round(input.tableBoxHeight),\n headerHeight: Math.round(input.headerHeight),\n scrollY: Math.round(scrollY),\n pageOverflow: Math.round(pageOverflow),\n };\n}\n\nonMounted(() => {\n scheduleUpdateAutoTableHeight();\n window.addEventListener(\"resize\", updateAutoTableHeight);\n\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(scheduleUpdateAutoTableHeight);\n if (tableRootRef.value) resizeObserver.observe(tableRootRef.value);\n if (tableBodyRef.value) resizeObserver.observe(tableBodyRef.value);\n if (paginationRef.value) resizeObserver.observe(paginationRef.value);\n }\n});\n\nonBeforeUnmount(() => {\n window.removeEventListener(\"resize\", updateAutoTableHeight);\n resizeObserver?.disconnect();\n});\n\nwatch(\n () => [props.tableConfig.showPagination, props.tableConfig.data.length, props.tableConfig.scroll.height],\n scheduleUpdateAutoTableHeight\n);\n</script>\n","<template>\n <div class=\"vac-root\">\n <!-- 搜索区域:只在传入 searchFormConfig 时渲染,外层 card 由当前 UI adapter 决定真实组件。 -->\n <component v-if=\"searchFormConfig\" :is=\"resolveAdapterComponent(adapter.components.card)\" class=\"vac-search-card\">\n <DynamicForm\n ref=\"searchFormRef\"\n form-type=\"search\"\n :columns=\"autoSearchColumns\"\n v-model=\"searchFormConfig.data\"\n :layout=\"searchFormConfig.layout\"\n @submit=\"handleSearchSubmit\"\n @reset=\"handleSearchReset\"\n >\n <!-- 搜索按钮扩展区:默认提供新增按钮,同时保留插槽给业务追加导出、批量操作等按钮。 -->\n <template #searchButtonToolbar>\n <component\n v-if=\"searchFormConfig.addButton\"\n :is=\"resolveAdapterComponent(adapter.components.button)\"\n v-bind=\"adapter.render.getButtonProps({ type: 'primary' })\"\n @click=\"handleAdd\"\n >\n {{ searchFormConfig.addButtonText }}\n </component>\n <slot name=\"searchButtonToolbar\" />\n </template>\n </DynamicForm>\n </component>\n\n <!-- 表格区域:AutoCrud 只负责容器和插槽透传,真实列渲染、分页、tabs 由 DynamicTable 接管。 -->\n <component v-if=\"tableConfig\" :is=\"resolveAdapterComponent(adapter.components.card)\" class=\"vac-table-card\">\n <DynamicTable :table-config=\"tableConfig\">\n <template #tableToolbar>\n <slot name=\"tableToolbar\" />\n </template>\n </DynamicTable>\n </component>\n\n <!-- 弹窗区域:统一承载新增、编辑、查看三种模式,表单内容继续复用 DynamicForm。 -->\n <CrudModal\n v-if=\"modalFormConfig\"\n :visible=\"modalFormConfig.visible\"\n :title=\"modalTitle\"\n :mode=\"modalFormConfig.mode\"\n :modal-type=\"modalFormConfig.modalType\"\n :submit-text=\"modalSubmitText\"\n :cancel-text=\"modalCancelText\"\n @update:visible=\"handleModalVisibleUpdate\"\n @submit=\"handleModalSubmit\"\n @close=\"modalFormConfig.close\"\n >\n <!-- 弹窗表单:查看模式下整体禁用,并通过 modalColumns 把每个字段也标记为禁用。 -->\n <DynamicForm\n ref=\"modalFormRef\"\n form-type=\"modal\"\n :columns=\"modalColumns\"\n v-model=\"modalFormConfig.data\"\n :layout=\"modalFormConfig.layout\"\n :disabled=\"modalFormConfig.mode === 'view'\"\n @submit=\"data => modalFormConfig?.submit(data)\"\n />\n <!-- 自定义弹窗底部:业务可接管按钮区,但仍能拿到默认 submit/close 能力。 -->\n <template v-if=\"$slots.modalFooter\" #footer>\n <slot name=\"modalFooter\" :modal-form=\"modalFormConfig\" :submit=\"handleModalSubmit\" :close=\"modalFormConfig.close\" />\n </template>\n </CrudModal>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from \"vue\";\nimport type { CrudAdapterComponent } from \"../adapters/types\";\nimport type { AutoCrudProps, CrudFormFieldEventContext, CrudFormItem } from \"../core/types\";\nimport { getFormComponent } from \"../core/object-utils\";\nimport { useAutoCrudContext } from \"../context\";\nimport CrudModal from \"./CrudModal.vue\";\nimport DynamicForm from \"./DynamicForm.vue\";\nimport DynamicTable from \"./DynamicTable.vue\";\n\n/** AutoCrud 是组合容器,只负责把搜索表单、表格、弹窗表单三块配置串起来。 */\nconst props = defineProps<AutoCrudProps<any>>();\n\n/** 全局上下文提供当前 UI adapter,确保组件本身不绑定 Element Plus、Naive UI 或 Ant Design Vue。 */\nconst { adapter } = useAutoCrudContext();\n/** 搜索表单实例引用,用于后续需要主动触发表单方法时扩展;当前搜索提交由 DynamicForm 事件驱动。 */\nconst searchFormRef = ref<InstanceType<typeof DynamicForm> | null>(null);\n/** 弹窗表单实例引用,用于点击弹窗 footer 的提交按钮时主动触发 DynamicForm.submit。 */\nconst modalFormRef = ref<InstanceType<typeof DynamicForm> | null>(null);\n\n/** 将 props 包一层 computed,模板和方法里统一按响应式配置读取。 */\nconst searchFormConfig = computed(() => props.searchFormConfig);\nconst tableConfig = computed(() => props.tableConfig);\nconst modalFormConfig = computed(() => props.modalFormConfig);\n\n/**\n * 搜索表单字段配置。\n *\n * 下拉类字段变更后通常意味着筛选条件已经明确,因此这里自动触发一次查询。\n * 包装时会保留业务原本写在字段顶层的 onChange,避免自动查询吞掉业务联动逻辑。\n */\nconst autoSearchColumns = computed<CrudFormItem[]>(() => {\n const config = searchFormConfig.value;\n if (!config) return [];\n\n return config.columns.map(item => {\n if (!shouldAutoSearchOnChange(item)) return item;\n\n const originalOnChange = item.onChange;\n return {\n ...item,\n async onChange(value: unknown, context: CrudFormFieldEventContext) {\n await originalOnChange?.(value, context);\n await handleSearchSubmit(context.formData);\n },\n };\n });\n});\n\n/** 弹窗模式默认中文文案;业务可通过 modalFormConfig.titleMap 覆盖。 */\nconst modeTextMap = {\n add: \"新增\",\n edit: \"编辑\",\n view: \"查看\",\n};\n\n/** 根据当前模式生成弹窗标题,例如“新增用户”“编辑用户”。 */\nconst modalTitle = computed(() => {\n const config = modalFormConfig.value;\n if (!config) return \"\";\n const mappedTitle = config.titleMap?.[config.mode];\n if (mappedTitle) return mappedTitle;\n return `${modeTextMap[config.mode]}${props.title || \"\"}`;\n});\n\n/** 根据当前模式生成提交按钮文案,未配置时统一显示“提交”。 */\nconst modalSubmitText = computed(() => {\n const config = modalFormConfig.value;\n return config?.buttonTextMap?.[config.mode]?.submit || \"提交\";\n});\n\n/** 根据当前模式生成取消按钮文案,未配置时统一显示“关闭”。 */\nconst modalCancelText = computed(() => {\n const config = modalFormConfig.value;\n return config?.buttonTextMap?.[config.mode]?.cancel || \"关闭\";\n});\n\n/**\n * 弹窗字段配置。\n *\n * 查看模式下不允许编辑字段,因此在不修改原 columns 的前提下复制一份配置并统一 disabled。\n */\nconst modalColumns = computed<CrudFormItem[]>(() => {\n const config = modalFormConfig.value;\n if (!config) return [];\n if (config.mode !== \"view\") return config.columns;\n return config.columns.map(column => ({ ...column, disabled: true }));\n});\n\nonMounted(() => {\n // 首屏加载由组件生命周期触发,避免 useTable 初始化期间业务侧还拿不到 table 变量。\n if (tableConfig.value?.immediate) {\n void tableConfig.value.load();\n }\n});\n\n/** adapter component 可能是字符串组件名,也可能是组件对象;这里保留统一入口方便模板使用。 */\nfunction resolveAdapterComponent(component: CrudAdapterComponent): CrudAdapterComponent {\n return component;\n}\n\n/**\n * 搜索提交。\n *\n * 容器层负责把搜索表单和表格连接起来:\n * - 表单 hook 只处理搜索条件。\n * - 表格 hook 只处理列表加载。\n * - AutoCrud 负责在搜索后把条件传给 table.reset。\n */\nasync function handleSearchSubmit(data: Record<string, unknown>): Promise<void> {\n await searchFormConfig.value?.submit(data);\n await tableConfig.value?.reset();\n}\n\n/** 搜索重置后回到第一页并重新加载列表。 */\nasync function handleSearchReset(): Promise<void> {\n await searchFormConfig.value?.reset();\n await tableConfig.value?.reset();\n}\n\n/** 搜索表单里只有下拉类控件默认开启 change 自动查询,输入框仍交给搜索按钮统一提交。 */\nfunction shouldAutoSearchOnChange(item: CrudFormItem): boolean {\n return [\"select\", \"multipleSelect\"].includes(getFormComponent(item));\n}\n\n/**\n * 默认新增按钮点击。\n *\n * 有弹窗配置时直接打开新增弹窗;没有弹窗配置时给出 UI adapter 的统一提示。\n * onAddClick 保留给业务执行额外逻辑,例如埋点、预填默认值或跳转独立新增页。\n */\nfunction handleAdd(): void {\n const config = modalFormConfig.value;\n if (config) {\n config.openAdd();\n } else {\n adapter.feedback.warning(\"请先配置弹窗表单 modalFormConfig\");\n }\n searchFormConfig.value?.onAddClick?.();\n}\n\n/** 同步弹窗显隐状态,兼容不同 UI 组件的 v-model 字段差异。 */\nfunction handleModalVisibleUpdate(value: boolean): void {\n if (modalFormConfig.value) modalFormConfig.value.visible = value;\n}\n\n/** 弹窗 footer 的提交入口,最终会走 DynamicForm 校验,再触发 modalFormConfig.submit。 */\nasync function handleModalSubmit(): Promise<void> {\n await modalFormRef.value?.submit();\n}\n</script>\n","import type { App } from \"vue\";\nimport type { AutoCrudInstallOptions, AutoCrudPlugin, CrudUIName } from \"./core/types\";\nimport type { CrudUIAdapter } from \"./adapters/types\";\nimport { antDesignVueAdapter } from \"./adapters/ant-design-vue\";\nimport { elementPlusAdapter } from \"./adapters/element-plus\";\nimport { naiveAdapter } from \"./adapters/naive\";\nimport { AUTO_CRUD_CONTEXT_KEY, createAutoCrudContext, setAutoCrudContext } from \"./context\";\n\nconst adapterMap: Record<CrudUIName, CrudUIAdapter> = {\n \"element-plus\": elementPlusAdapter,\n naive: naiveAdapter,\n \"ant-design-vue\": antDesignVueAdapter,\n};\n\n/** 根据初始化参数选择 Adapter。 */\nfunction resolveAdapter(options: AutoCrudInstallOptions): CrudUIAdapter {\n if (options.adapter) return options.adapter as CrudUIAdapter;\n return adapterMap[options.ui || \"element-plus\"];\n}\n\n/**\n * 创建 Auto CRUD Vue 插件。\n *\n * @example 默认 Element Plus\n * ```ts\n * app.use(createAutoCrud());\n * ```\n *\n * @example 切换 Naive UI\n * ```ts\n * app.use(createAutoCrud({ ui: \"naive\" }));\n * ```\n *\n * 设计重点:UI 只在初始化时选择一次,页面 schema 不需要知道当前 UI 是哪套。\n */\nexport function createAutoCrud(options: AutoCrudInstallOptions = {}): AutoCrudPlugin {\n const plugin: AutoCrudPlugin = {\n install(app: App, installOptions?: AutoCrudInstallOptions) {\n const mergedOptions = { ...options, ...(installOptions || {}) };\n const context = createAutoCrudContext(mergedOptions, resolveAdapter(mergedOptions));\n setAutoCrudContext(context);\n app.provide(AUTO_CRUD_CONTEXT_KEY, context);\n },\n };\n\n return plugin;\n}\n"],"names":["AUTO_CRUD_CONTEXT_KEY","globalContext","elementPlusAdapter","setAutoCrudContext","context","useAutoCrudContext","inject","createAutoCrudContext","options","adapter","props","__props","emit","__emit","slots","useSlots","modalComponent","computed","drawerContentComponent","modalProps","value","ModalFooter","defineComponent","h","resolveRuntimeComponent","resolveAdapterComponent","component","resolveComponent","_openBlock","_createBlock","_resolveDynamicComponent","_createVNode","_unref","_createElementVNode","_hoisted_1","_renderSlot","_ctx","_hoisted_2","isCacheable","item","isDynamicValue","isDevelopment","meta","getVacWindow","isPerfEnabled","targetWindow","globalPerf","ensurePerfGlobal","perfGlobal","getPerfFieldKey","scope","recordPerf","kind","event","fieldKey","fieldStats","createStableValueSnapshot","createDepsSnapshot","formData","dep","getByPath","createCacheKey","hasDynamicConfig","createFormRuntimeCache","cache","warnedKeys","warnMissingDeps","warningKey","resolveCached","resolver","key","depsSnapshot","cached","shouldShowFormItem","forceDisabled","shouldDisableFormItem","createRules","normalizeOptions","getFormItemLabel","getPlaceholder","RenderContent","renderProps","fieldType","getFormComponent","fieldComponent","fieldOptions","optionComponent","shouldRenderOptionsAsChildren","formItemProps","fieldProps","binding","propsFromAdapter","normalizedFieldProps","normalizeNaiveDateFieldProps","nativeModelUpdate","getFieldEventHandler","nativeChange","nativeBlur","nativeFocus","nextValue","oldValue","setByPath","createFieldEventContext","prop","handler","normalizeDateFnsFormat","_normalizeProps","_guardReactiveProps","_createElementBlock","_Fragment","_renderList","option","_createTextVNode","_toDisplayString","formRef","ref","runtimeCache","visibleColumns","rules","result","itemRules","formProps","layoutStyle","getNormalizedLayout","searchActionsStyle","columns","getLayoutColumns","usedColumns","total","getItemColSpan","span","getFormItemGridStyle","validate","clearValidate","submit","reset","__expose","_mergeProps","_normalizeStyle","DynamicFormItem","_cache","getColumnField","column","getCellValue","row","field","shouldShowColumn","data","index","resolveValue","getColumnActions","shouldShowAction","action","getActionLabel","getActionConfirmText","formatDateTime","format","pattern","dayjs","normalizeImageOptions","image","displayText","handleCopy","copyText","visible","decodedText","rawText","maskedText","handleDecode","tableRootRef","tableBodyRef","paginationRef","autoTableBoxHeight","tableHeaderHeight","heightDebug","resizeObserver","CellContent","cellProps","objectColumns","createObjectColumn","tableProps","resolvedScrollY","tableBodyStyle","height","getAdapterScrollOffset","paginationProps","paginationEvents","page","pageSize","getColumnKey","base","record","rowIndex","renderCell","renderActions","DecodeText","CopyText","renderImage","actions","buttonProps","onClick","src","imageProps","handleTabsChange","updateAutoTableHeight","bodyEl","measureTableHeaderHeight","bodyTop","paginationHeight","getOuterHeight","boundaryBottom","getScrollBoundaryBottom","bottomGap","getBottomSafeGap","adapterOffset","getAdapterBoxOffset","nextHeight","normalizeAutoTableHeight","updateHeightDebug","scheduleUpdateAutoTableHeight","nextTick","minHeight","element","style","parent","hasVerticalScroll","cardBody","pageContent","cardPadding","pagePadding","header","input","scrollY","pageOverflow","onMounted","onBeforeUnmount","watch","tab","_normalizeClass","$event","_hoisted_3","_withCtx","_toHandlers","_Teleport","_hoisted_4","searchFormRef","modalFormRef","searchFormConfig","tableConfig","modalFormConfig","autoSearchColumns","config","shouldAutoSearchOnChange","originalOnChange","handleSearchSubmit","modeTextMap","modalTitle","mappedTitle","modalSubmitText","modalCancelText","modalColumns","handleSearchReset","handleAdd","handleModalVisibleUpdate","handleModalSubmit","DynamicForm","DynamicTable","CrudModal","$slots","adapterMap","naiveAdapter","antDesignVueAdapter","resolveAdapter","createAutoCrud","app","installOptions","mergedOptions"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { AutoCrudInstallOptions, AutoCrudPlugin } from './core/types';
2
+ /**
3
+ * 创建 Auto CRUD Vue 插件。
4
+ *
5
+ * @example 默认 Element Plus
6
+ * ```ts
7
+ * app.use(createAutoCrud());
8
+ * ```
9
+ *
10
+ * @example 切换 Naive UI
11
+ * ```ts
12
+ * app.use(createAutoCrud({ ui: "naive" }));
13
+ * ```
14
+ *
15
+ * 设计重点:UI 只在初始化时选择一次,页面 schema 不需要知道当前 UI 是哪套。
16
+ */
17
+ export declare function createAutoCrud(options?: AutoCrudInstallOptions): AutoCrudPlugin;
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .vac-root{display:flex;flex-direction:column;gap:12px;min-height:0}.vac-search-card,.vac-table-card{width:100%;min-width:0;min-height:0}.vac-table-root{display:flex;flex-direction:column;min-height:0}.vac-table-body{min-width:0;min-height:0}.vac-table-body>*{min-width:0}.vac-form-grid{display:grid;grid-template-columns:repeat(var(--vac-form-columns, 1),minmax(0,1fr));gap:12px 16px;width:100%}.vac-form-grid-item{min-width:0}.vac-form-grid-item>*{width:100%}.vac-form-actions{display:flex;justify-content:flex-end;gap:8px}.vac-table-toolbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.vac-table-height-debug{position:fixed;bottom:12px;left:50%;z-index:9999;display:flex;flex-wrap:nowrap;gap:6px;max-width:calc(100vw - 32px);overflow-x:auto;overflow-y:hidden;border:1px dashed #91caff;border-radius:6px;background:#f0f7ff;color:#1f3b57;padding:6px 8px;font-size:12px;line-height:1.5;pointer-events:none;transform:translate(-50%)}.vac-table-height-debug span{flex:0 0 auto;border-radius:4px;background:#fff;padding:2px 6px;white-space:nowrap}.vac-table-height-debug .is-warning{background:#fff1f0;color:#cf1322}.vac-tabs{display:flex;align-items:center;gap:8px;margin-bottom:8px}.vac-tab{border:1px solid #dcdfe6;border-radius:4px;background:#fff;color:#606266;cursor:pointer;padding:4px 12px}.vac-tab.is-active{border-color:#409eff;color:#409eff}.vac-pagination{display:flex;justify-content:flex-end;margin-top:12px}.vac-actions{display:inline-flex;align-items:center;gap:8px}.vac-copy-text,.vac-decode-text{display:inline-flex;align-items:center;gap:6px}.vac-link-button{border:0;background:transparent;color:#409eff;cursor:pointer;padding:0}.vac-image{border-radius:4px;object-fit:cover}.vac-modal-body{min-height:0;padding:16px 20px 8px}.vac-modal-footer{display:flex;justify-content:flex-end;align-items:center;gap:8px;width:100%}
@@ -0,0 +1,214 @@
1
+ import { reactive as f } from "vue";
2
+ function i(e) {
3
+ if (e === null || typeof e != "object") return e;
4
+ if (e instanceof Date) return new Date(e.getTime());
5
+ if (Array.isArray(e)) return e.map((t) => i(t));
6
+ const n = {};
7
+ return Object.keys(e).forEach((t) => {
8
+ n[t] = i(e[t]);
9
+ }), n;
10
+ }
11
+ function S(e, n) {
12
+ if (n)
13
+ return n.split(".").reduce((t, a) => {
14
+ if (t != null)
15
+ return t[a];
16
+ }, e);
17
+ }
18
+ function y(e, n, t) {
19
+ const a = n.split(".").filter(Boolean);
20
+ if (a.length === 0) return;
21
+ let r = e;
22
+ a.slice(0, -1).forEach((u) => {
23
+ const o = r[u];
24
+ (o === null || typeof o != "object" || Array.isArray(o)) && (r[u] = {}), r = r[u];
25
+ }), r[a[a.length - 1]] = t;
26
+ }
27
+ function l(e, ...n) {
28
+ return typeof e == "function" ? e(...n) : e;
29
+ }
30
+ function b(e) {
31
+ return e.component || e.valueKey || "input";
32
+ }
33
+ function F(e, n = {}, t) {
34
+ const a = typeof e == "function" ? e(n, t) : e;
35
+ return a ? Array.isArray(a) ? a : typeof a == "object" ? Object.entries(a).map(([r, u]) => ({ label: String(u), value: r })) : [] : [];
36
+ }
37
+ function h(e) {
38
+ return ["input", "textarea", "inputNumber"].includes(e);
39
+ }
40
+ function m(e) {
41
+ return ["multipleSelect", "checkbox", "cascader"].includes(e);
42
+ }
43
+ function A(e) {
44
+ return ["inputNumber", "slider"].includes(e);
45
+ }
46
+ function P(e, n) {
47
+ return n !== void 0 ? i(n) : m(e) ? [] : e === "switch" ? !1 : null;
48
+ }
49
+ function g(e, n = {}) {
50
+ const t = i(n);
51
+ return e.forEach((a) => {
52
+ !a.prop || S(t, a.prop) !== void 0 || y(t, a.prop, P(b(a), a.value));
53
+ }), t;
54
+ }
55
+ function V(e, n) {
56
+ return "show" in e ? !!l(e.show, n, e) : !0;
57
+ }
58
+ function z(e, n, t = !1) {
59
+ return t ? !0 : "disabled" in e ? !!l(e.disabled, n, e) : !1;
60
+ }
61
+ function x(e, n) {
62
+ return String(l(e.label, n, e));
63
+ }
64
+ function B(e, n) {
65
+ if (e.placeholder)
66
+ return String(l(e.placeholder, n, e));
67
+ const t = b(e);
68
+ return `${h(t) ? "请输入" : "请选择"}${x(e, n)}`;
69
+ }
70
+ function E(e, n) {
71
+ const t = b(e), a = e.required ?? e.isReq, r = [];
72
+ if (a && l(a, n, e)) {
73
+ const u = {
74
+ required: !0,
75
+ message: B(e, n),
76
+ trigger: h(t) ? ["blur", "input"] : ["blur", "change"]
77
+ };
78
+ m(t) && (u.type = "array"), A(t) && (u.type = "number"), r.push(u);
79
+ }
80
+ if (e.rules) {
81
+ const u = l(e.rules, n, e);
82
+ Array.isArray(u) && r.push(...u);
83
+ }
84
+ return r;
85
+ }
86
+ function O(e) {
87
+ const n = g(e.columns, e.data || {}), t = f({
88
+ columns: e.columns,
89
+ data: i(n),
90
+ visible: e.visible ?? !1,
91
+ mode: e.mode ?? "add",
92
+ modalType: e.modalType ?? "drawer",
93
+ titleMap: e.titleMap,
94
+ buttonTextMap: e.buttonTextMap,
95
+ layout: e.layout
96
+ });
97
+ function a(s) {
98
+ Object.keys(t.data).forEach((d) => delete t.data[d]), Object.assign(t.data, g(e.columns, s));
99
+ }
100
+ async function r(s) {
101
+ await e.onSubmit?.(i(s || t.data), t.mode);
102
+ }
103
+ function u() {
104
+ t.visible = !1, e.onClose?.();
105
+ }
106
+ function o(s) {
107
+ t.mode = "add", a({ ...i(n), ...s || {} }), t.visible = !0;
108
+ }
109
+ function c(s) {
110
+ t.mode = "edit", a(i(s)), t.visible = !0;
111
+ }
112
+ function p(s) {
113
+ t.mode = "view", a(i(s)), t.visible = !0;
114
+ }
115
+ function w(s, d) {
116
+ y(t.data, s, d);
117
+ }
118
+ return t.submit = r, t.close = u, t.openAdd = o, t.openEdit = c, t.openView = p, t.setFormData = w, t;
119
+ }
120
+ function k(e) {
121
+ const n = g(e.columns, e.data || {}), t = i(n), a = f(n);
122
+ async function r(o) {
123
+ const c = i(o || a);
124
+ await e.onSubmit?.(c);
125
+ }
126
+ async function u() {
127
+ Object.keys(a).forEach((c) => delete a[c]), Object.assign(a, i(t));
128
+ const o = i(a);
129
+ return await e.onReset?.(o), o;
130
+ }
131
+ return f({
132
+ columns: e.columns,
133
+ data: a,
134
+ addButton: e.addButton ?? !0,
135
+ addButtonText: e.addButtonText ?? "新增",
136
+ searchButtonSpan: e.searchButtonSpan,
137
+ layout: e.layout,
138
+ submit: r,
139
+ reset: u,
140
+ onAddClick: e.onAddClick
141
+ });
142
+ }
143
+ function T(e) {
144
+ return {
145
+ page: 1,
146
+ pageSize: 10,
147
+ total: 0,
148
+ pageSizes: [10, 20, 30, 50, 100],
149
+ showSizePicker: !0,
150
+ showQuickJumper: !0,
151
+ ...e === !1 ? {} : e
152
+ };
153
+ }
154
+ function D(e) {
155
+ return typeof e.showPagination == "boolean" ? e.showPagination : e.pagination !== !1;
156
+ }
157
+ function j(e) {
158
+ return e === !1 ? { height: !1 } : e === !0 || e === void 0 ? { height: !0 } : {
159
+ height: e.height ?? !0,
160
+ x: e.x
161
+ };
162
+ }
163
+ function v(e) {
164
+ const n = f({
165
+ columns: e.columns,
166
+ data: e.data || [],
167
+ showPagination: D(e),
168
+ pagination: T(e.pagination),
169
+ tabs: e.tabs,
170
+ loading: !1,
171
+ rowKey: e.rowKey,
172
+ immediate: "immediate" in e && e.immediate ? e.immediate : !0,
173
+ scroll: j(e.scroll)
174
+ });
175
+ async function t() {
176
+ if (e.getDataList) {
177
+ n.loading = !0;
178
+ try {
179
+ await e.getDataList();
180
+ } finally {
181
+ n.loading = !1;
182
+ }
183
+ }
184
+ }
185
+ async function a() {
186
+ n.pagination.page = 1, await t();
187
+ }
188
+ async function r(c) {
189
+ n.pagination.page = c, await t();
190
+ }
191
+ async function u(c) {
192
+ n.pagination.pageSize = c, n.pagination.page = 1, await t();
193
+ }
194
+ async function o(c) {
195
+ n.tabs && (n.tabs.value = c, await n.tabs.onChange?.(c)), n.pagination.page = 1, await t();
196
+ }
197
+ return n.load = t, n.reset = a, n.setPage = r, n.setPageSize = u, n.setTabsValue = o, n;
198
+ }
199
+ export {
200
+ k as a,
201
+ v as b,
202
+ B as c,
203
+ x as d,
204
+ E as e,
205
+ V as f,
206
+ S as g,
207
+ b as h,
208
+ y as i,
209
+ F as n,
210
+ l as r,
211
+ z as s,
212
+ O as u
213
+ };
214
+ //# sourceMappingURL=useTable-BwDSqD1q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTable-BwDSqD1q.js","sources":["../src/core/object-utils.ts","../src/core/form-utils.ts","../src/hooks/useModalForm.ts","../src/hooks/useSearchForm.ts","../src/hooks/useTable.ts"],"sourcesContent":["import type { CrudFormItem } from \"./types\";\n\n/**\n * 深度克隆任意数据。\n *\n * 使用场景:\n * - 搜索表单保存初始快照,重置时避免引用同一个对象。\n * - 弹窗编辑行数据时避免直接修改表格行对象。\n *\n * 说明:这里故意不依赖 lodash,方便学习递归克隆的核心思路。\n */\nexport function deepClone<T>(value: T): T {\n if (value === null || typeof value !== \"object\") return value;\n if (value instanceof Date) return new Date(value.getTime()) as T;\n if (Array.isArray(value)) return value.map(item => deepClone(item)) as T;\n\n const result = {} as T;\n Object.keys(value as Record<string, unknown>).forEach(key => {\n (result as Record<string, unknown>)[key] = deepClone((value as Record<string, unknown>)[key]);\n });\n return result;\n}\n\n/**\n * 按点路径读取对象值。\n *\n * @example\n * ```ts\n * getByPath({ user: { name: \"张三\" } }, \"user.name\") // \"张三\"\n * ```\n */\nexport function getByPath(source: unknown, path: string): unknown {\n if (!path) return undefined;\n return path.split(\".\").reduce<unknown>((current, key) => {\n if (current === null || current === undefined) return undefined;\n return (current as Record<string, unknown>)[key];\n }, source);\n}\n\n/**\n * 按点路径写入对象值。\n *\n * 这让表单字段可以声明 `user.name`,而不是只能声明一层字段。\n */\nexport function setByPath(target: Record<string, unknown>, path: string, value: unknown): void {\n const keys = path.split(\".\").filter(Boolean);\n if (keys.length === 0) return;\n\n let current = target;\n keys.slice(0, -1).forEach(key => {\n const next = current[key];\n if (next === null || typeof next !== \"object\" || Array.isArray(next)) {\n current[key] = {};\n }\n current = current[key] as Record<string, unknown>;\n });\n\n current[keys[keys.length - 1]] = value;\n}\n\n/**\n * 执行“值或函数”配置。\n *\n * 很多配置项都支持静态值和函数,例如:\n * - `show: true`\n * - `show: formData => formData.type === 1`\n *\n * 这个工具统一处理两种写法。\n */\nexport function resolveValue<T, Args extends unknown[]>(value: T | ((...args: Args) => T), ...args: Args): T {\n return typeof value === \"function\" ? (value as (...args: Args) => T)(...args) : value;\n}\n\n/**\n * 获取字段实际组件类型。\n *\n * 新包推荐使用 `component`,但为了保留旧 auto-gen-crud 的学习迁移价值,\n * 这里兼容旧字段 `valueKey`。\n */\nexport function getFormComponent(item: CrudFormItem): string {\n return item.component || item.valueKey || \"input\";\n}\n","import type { CrudFormComponent, CrudFormItem, CrudFormRule, CrudOption } from \"./types\";\nimport { deepClone, getByPath, getFormComponent, resolveValue, setByPath } from \"./object-utils\";\n\n/**\n * 将 options 归一化为数组。\n *\n * 支持两种常见写法:\n * - 数组:`[{ label: \"启用\", value: 1 }]`\n * - 对象:`{ 1: \"启用\", 0: \"禁用\" }`\n */\nexport function normalizeOptions(options: unknown, formData: Record<string, unknown> = {}, item?: CrudFormItem): CrudOption[] {\n const raw = typeof options === \"function\" ? (options as (data: Record<string, unknown>, item?: CrudFormItem) => unknown)(formData, item) : options;\n\n if (!raw) return [];\n if (Array.isArray(raw)) return raw as CrudOption[];\n\n if (typeof raw === \"object\") {\n return Object.entries(raw as Record<string, unknown>).map(([value, label]) => ({ label: String(label), value }));\n }\n\n return [];\n}\n\n/** 判断当前组件是否属于输入类,用于自动生成 placeholder 和校验 trigger。 */\nexport function isInputComponent(component: string): boolean {\n return [\"input\", \"textarea\", \"inputNumber\"].includes(component);\n}\n\n/** 判断当前组件是否通常使用数组值,用于初始化默认值和必填规则类型。 */\nexport function isArrayComponent(component: string): boolean {\n return [\"multipleSelect\", \"checkbox\", \"cascader\"].includes(component);\n}\n\n/** 判断当前组件是否通常使用数字值。 */\nexport function isNumberComponent(component: string): boolean {\n return [\"inputNumber\", \"slider\"].includes(component);\n}\n\n/**\n * 生成字段默认值。\n *\n * 目的:业务无需给每个字段都手写初始值;新增弹窗和搜索重置都可以复用这套规则。\n */\nexport function getDefaultValue(component: CrudFormComponent | string, value?: unknown): unknown {\n if (value !== undefined) return deepClone(value);\n if (isArrayComponent(component)) return [];\n if (component === \"switch\") return false;\n return null;\n}\n\n/**\n * 根据 columns 和外部初始 data 构建完整表单数据。\n *\n * 这个函数是新包状态初始化的核心:\n * - 搜索表单用它生成初始搜索条件。\n * - 新增弹窗用它重建干净表单,避免残留上一次编辑的数据。\n */\nexport function createFormData(columns: CrudFormItem[], data: Record<string, unknown> = {}): Record<string, unknown> {\n const result = deepClone(data);\n\n columns.forEach(item => {\n if (!item.prop) return;\n const currentValue = getByPath(result, item.prop);\n if (currentValue !== undefined) return;\n setByPath(result, item.prop, getDefaultValue(getFormComponent(item), item.value));\n });\n\n return result;\n}\n\n/** 判断表单项是否应该显示。 */\nexport function shouldShowFormItem(item: CrudFormItem, formData: Record<string, unknown>): boolean {\n if (!(\"show\" in item)) return true;\n return Boolean(resolveValue(item.show as boolean | ((data: Record<string, unknown>, item: CrudFormItem) => boolean), formData, item));\n}\n\n/** 判断表单项是否应该禁用。 */\nexport function shouldDisableFormItem(item: CrudFormItem, formData: Record<string, unknown>, forceDisabled = false): boolean {\n if (forceDisabled) return true;\n if (!(\"disabled\" in item)) return false;\n return Boolean(resolveValue(item.disabled as boolean | ((data: Record<string, unknown>, item: CrudFormItem) => boolean), formData, item));\n}\n\n/** 获取表单项 label,兼容函数写法。 */\nexport function getFormItemLabel(item: CrudFormItem, formData: Record<string, unknown>): string {\n return String(resolveValue(item.label, formData, item));\n}\n\n/**\n * 生成默认 placeholder。\n *\n * 输入类组件使用“请输入”,选择类组件使用“请选择”。\n */\nexport function getPlaceholder(item: CrudFormItem, formData: Record<string, unknown>): string {\n if (item.placeholder) {\n return String(resolveValue(item.placeholder, formData, item));\n }\n\n const component = getFormComponent(item);\n const prefix = isInputComponent(component) ? \"请输入\" : \"请选择\";\n return `${prefix}${getFormItemLabel(item, formData)}`;\n}\n\n/**\n * 生成字段校验规则。\n *\n * 规则来源有两类:\n * - `required/isReq` 自动生成必填规则。\n * - `rules` 追加业务自定义规则。\n */\nexport function createRules(item: CrudFormItem, formData: Record<string, unknown>): CrudFormRule[] {\n const component = getFormComponent(item);\n const required = item.required ?? item.isReq;\n const rules: CrudFormRule[] = [];\n\n if (required && resolveValue(required as boolean | ((data: Record<string, unknown>, item: CrudFormItem) => boolean), formData, item)) {\n const rule: CrudFormRule = {\n required: true,\n message: getPlaceholder(item, formData),\n trigger: isInputComponent(component) ? [\"blur\", \"input\"] : [\"blur\", \"change\"],\n };\n\n if (isArrayComponent(component)) rule.type = \"array\";\n if (isNumberComponent(component)) rule.type = \"number\";\n rules.push(rule);\n }\n\n if (item.rules) {\n const customRules = resolveValue(item.rules, formData, item);\n if (Array.isArray(customRules)) rules.push(...customRules);\n }\n\n return rules;\n}\n\n/** 为整个表单生成 rules 对象。 */\nexport function createFormRules(columns: CrudFormItem[], formData: Record<string, unknown>): Record<string, CrudFormRule[]> {\n return columns.reduce<Record<string, CrudFormRule[]>>((rules, item) => {\n const itemRules = createRules(item, formData);\n if (item.prop && itemRules.length > 0) rules[item.prop] = itemRules;\n return rules;\n }, {});\n}\n","import { reactive } from \"vue\";\nimport type { CrudModalFormConfig, CrudModalMode, UseModalFormOptions } from \"../core/types\";\nimport { createFormData } from \"../core/form-utils\";\nimport { deepClone, setByPath } from \"../core/object-utils\";\n\n/**\n * 创建弹窗表单配置。\n *\n * 设计说明:\n * - `add/edit/view` 三种模式是后台 CRUD 最常见的状态,本 hook 将它们统一管理。\n * - `openAdd` 每次都会根据 columns 和初始 data 重建表单,避免旧实现中编辑数据残留到新增弹窗。\n * - `openEdit/openView` 使用深拷贝,避免弹窗中修改嵌套对象时直接污染表格行数据。\n */\nexport function useModalForm<T extends Record<string, unknown> = Record<string, unknown>>(\n options: UseModalFormOptions<T>\n): CrudModalFormConfig<T> {\n const baseData = createFormData(options.columns, options.data || {}) as T;\n const state = reactive({\n columns: options.columns,\n data: deepClone(baseData) as T,\n visible: options.visible ?? false,\n mode: options.mode ?? \"add\",\n modalType: options.modalType ?? \"drawer\",\n titleMap: options.titleMap,\n buttonTextMap: options.buttonTextMap,\n layout: options.layout,\n }) as unknown as CrudModalFormConfig<T>;\n\n function replaceData(data: Partial<T>): void {\n Object.keys(state.data).forEach(key => delete (state.data as Record<string, unknown>)[key]);\n Object.assign(state.data, createFormData(options.columns, data as Record<string, unknown>));\n }\n\n async function submit(data?: T): Promise<void> {\n await options.onSubmit?.(deepClone(data || state.data), state.mode);\n }\n\n function close(): void {\n state.visible = false;\n options.onClose?.();\n }\n\n function openAdd(data?: Partial<T>): void {\n state.mode = \"add\";\n replaceData({ ...deepClone(baseData), ...(data || {}) });\n state.visible = true;\n }\n\n function openEdit(row: T): void {\n state.mode = \"edit\";\n replaceData(deepClone(row));\n state.visible = true;\n }\n\n function openView(row: T): void {\n state.mode = \"view\";\n replaceData(deepClone(row));\n state.visible = true;\n }\n\n function setFormData(prop: string, value: unknown): void {\n setByPath(state.data as Record<string, unknown>, prop, value);\n }\n\n state.submit = submit;\n state.close = close;\n state.openAdd = openAdd;\n state.openEdit = openEdit;\n state.openView = openView;\n state.setFormData = setFormData;\n\n return state;\n}\n\nexport type { CrudModalMode };\n","import { reactive } from \"vue\";\nimport type { CrudSearchFormConfig, UseSearchFormOptions } from \"../core/types\";\nimport { createFormData } from \"../core/form-utils\";\nimport { deepClone } from \"../core/object-utils\";\n\n/**\n * 创建搜索表单配置。\n *\n * 设计说明:\n * - 搜索表单本质是“查询条件状态容器”,不应该直接关心表格组件或接口格式。\n * - 这里会在初始化时保存一份快照,重置时恢复快照,而不是简单把字段清空。\n * - 是否刷新列表由 `AutoCrud` 组合层和 `useTable` 协调,避免旧实现中搜索提交和表格重置重复请求。\n */\nexport function useSearchForm<T extends Record<string, unknown> = Record<string, unknown>>(\n options: UseSearchFormOptions<T>\n): CrudSearchFormConfig<T> {\n const initialData = createFormData(options.columns, options.data || {});\n const originalData = deepClone(initialData);\n const data = reactive(initialData) as T;\n\n async function submit(submitData?: T): Promise<void> {\n // 对外提交深拷贝后的查询条件,避免业务回调误改内部响应式状态。\n const payload = deepClone(submitData || data) as T;\n await options.onSubmit?.(payload);\n }\n\n async function reset(): Promise<T> {\n Object.keys(data).forEach(key => delete data[key]);\n Object.assign(data, deepClone(originalData));\n // 重置后同样返回泛型 T,让业务页拿到的字段类型保持一致。\n const payload = deepClone(data) as T;\n await options.onReset?.(payload);\n return payload;\n }\n\n return reactive({\n columns: options.columns,\n data,\n addButton: options.addButton ?? true,\n addButtonText: options.addButtonText ?? \"新增\",\n searchButtonSpan: options.searchButtonSpan,\n layout: options.layout,\n submit,\n reset,\n onAddClick: options.onAddClick,\n }) as CrudSearchFormConfig<T>;\n}\n","import { reactive } from \"vue\";\nimport type { CrudPagination, CrudTableConfig, CrudTableScroll, UseTableOptions } from \"../core/types\";\n\n/** 创建默认分页配置。 */\nfunction createPagination(pagination?: Partial<CrudPagination> | false): CrudPagination {\n return {\n page: 1,\n pageSize: 10,\n total: 0,\n pageSizes: [10, 20, 30, 50, 100],\n showSizePicker: true,\n showQuickJumper: true,\n ...(pagination === false ? {} : pagination),\n };\n}\n\n/** 解析分页显隐开关,兼容旧版本 pagination: false 写法。 */\nfunction createShowPagination(options: Pick<UseTableOptions, \"showPagination\" | \"pagination\">): boolean {\n if (typeof options.showPagination === \"boolean\") return options.showPagination;\n return options.pagination !== false;\n}\n\n/** 表格默认开启自动高度;业务传 false 时才回到自然高度。 */\nfunction createTableScroll(scroll?: UseTableOptions[\"scroll\"]): CrudTableScroll {\n if (scroll === false) return { height: false };\n if (scroll === true || scroll === undefined) return { height: true };\n return {\n height: scroll.height ?? true,\n x: scroll.x,\n };\n}\n\n/**\n * 创建表格配置。\n *\n * 设计说明:\n * - hook 只负责状态,不渲染按钮、确认框、图片等 UI。\n * - `getDataList` 由业务传入,因为每个项目的接口格式、分页字段、返回结构都不一样。\n * - 分页统一使用 `page/pageSize/total`,具体 UI 字段由 adapter 映射。\n */\nexport function useTable<T extends Record<string, unknown> = Record<string, unknown>>(\n options: UseTableOptions<T>\n): CrudTableConfig<T> {\n const state = reactive({\n columns: options.columns,\n data: options.data || [],\n showPagination: createShowPagination(options),\n pagination: createPagination(options.pagination),\n tabs: options.tabs,\n loading: false,\n rowKey: options.rowKey,\n immediate: 'immediate' in options && options.immediate ? options.immediate : true,\n scroll: createTableScroll(options.scroll),\n }) as unknown as CrudTableConfig<T>;\n\n async function load(): Promise<void> {\n if (!options.getDataList) return;\n\n state.loading = true;\n try {\n await options.getDataList();\n } finally {\n state.loading = false;\n }\n }\n\n async function reset(): Promise<void> {\n state.pagination.page = 1;\n await load();\n }\n\n async function setPage(page: number): Promise<void> {\n state.pagination.page = page;\n await load();\n }\n\n async function setPageSize(pageSize: number): Promise<void> {\n state.pagination.pageSize = pageSize;\n state.pagination.page = 1;\n await load();\n }\n\n async function setTabsValue(value: string | number): Promise<void> {\n if (state.tabs) {\n state.tabs.value = value;\n await state.tabs.onChange?.(value);\n }\n state.pagination.page = 1;\n await load();\n }\n\n state.load = load;\n state.reset = reset;\n state.setPage = setPage;\n state.setPageSize = setPageSize;\n state.setTabsValue = setTabsValue;\n\n return state;\n}\n\nexport type { CrudPagination };\n"],"names":["deepClone","value","item","result","key","getByPath","source","path","current","setByPath","target","keys","next","resolveValue","args","getFormComponent","normalizeOptions","options","formData","raw","label","isInputComponent","component","isArrayComponent","isNumberComponent","getDefaultValue","createFormData","columns","data","shouldShowFormItem","shouldDisableFormItem","forceDisabled","getFormItemLabel","getPlaceholder","createRules","required","rules","rule","customRules","useModalForm","baseData","state","reactive","replaceData","submit","close","openAdd","openEdit","row","openView","setFormData","prop","useSearchForm","initialData","originalData","submitData","payload","reset","createPagination","pagination","createShowPagination","createTableScroll","scroll","useTable","load","setPage","page","setPageSize","pageSize","setTabsValue"],"mappings":";AAWO,SAASA,EAAaC,GAAa;AACxC,MAAIA,MAAU,QAAQ,OAAOA,KAAU,SAAU,QAAOA;AACxD,MAAIA,aAAiB,KAAM,QAAO,IAAI,KAAKA,EAAM,SAAS;AAC1D,MAAI,MAAM,QAAQA,CAAK,EAAG,QAAOA,EAAM,IAAI,CAAAC,MAAQF,EAAUE,CAAI,CAAC;AAElE,QAAMC,IAAS,CAAA;AACf,gBAAO,KAAKF,CAAgC,EAAE,QAAQ,CAAAG,MAAO;AAC1D,IAAAD,EAAmCC,CAAG,IAAIJ,EAAWC,EAAkCG,CAAG,CAAC;AAAA,EAC9F,CAAC,GACMD;AACT;AAUO,SAASE,EAAUC,GAAiBC,GAAuB;AAChE,MAAKA;AACL,WAAOA,EAAK,MAAM,GAAG,EAAE,OAAgB,CAACC,GAASJ,MAAQ;AACvD,UAAII,KAAY;AAChB,eAAQA,EAAoCJ,CAAG;AAAA,IACjD,GAAGE,CAAM;AACX;AAOO,SAASG,EAAUC,GAAiCH,GAAcN,GAAsB;AAC7F,QAAMU,IAAOJ,EAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAC3C,MAAII,EAAK,WAAW,EAAG;AAEvB,MAAIH,IAAUE;AACd,EAAAC,EAAK,MAAM,GAAG,EAAE,EAAE,QAAQ,CAAAP,MAAO;AAC/B,UAAMQ,IAAOJ,EAAQJ,CAAG;AACxB,KAAIQ,MAAS,QAAQ,OAAOA,KAAS,YAAY,MAAM,QAAQA,CAAI,OACjEJ,EAAQJ,CAAG,IAAI,CAAA,IAEjBI,IAAUA,EAAQJ,CAAG;AAAA,EACvB,CAAC,GAEDI,EAAQG,EAAKA,EAAK,SAAS,CAAC,CAAC,IAAIV;AACnC;AAWO,SAASY,EAAwCZ,MAAsCa,GAAe;AAC3G,SAAO,OAAOb,KAAU,aAAcA,EAA+B,GAAGa,CAAI,IAAIb;AAClF;AAQO,SAASc,EAAiBb,GAA4B;AAC3D,SAAOA,EAAK,aAAaA,EAAK,YAAY;AAC5C;ACvEO,SAASc,EAAiBC,GAAkBC,IAAoC,CAAA,GAAIhB,GAAmC;AAC5H,QAAMiB,IAAM,OAAOF,KAAY,aAAcA,EAA4EC,GAAUhB,CAAI,IAAIe;AAE3I,SAAKE,IACD,MAAM,QAAQA,CAAG,IAAUA,IAE3B,OAAOA,KAAQ,WACV,OAAO,QAAQA,CAA8B,EAAE,IAAI,CAAC,CAAClB,GAAOmB,CAAK,OAAO,EAAE,OAAO,OAAOA,CAAK,GAAG,OAAAnB,IAAQ,IAG1G,CAAA,IAPU,CAAA;AAQnB;AAGO,SAASoB,EAAiBC,GAA4B;AAC3D,SAAO,CAAC,SAAS,YAAY,aAAa,EAAE,SAASA,CAAS;AAChE;AAGO,SAASC,EAAiBD,GAA4B;AAC3D,SAAO,CAAC,kBAAkB,YAAY,UAAU,EAAE,SAASA,CAAS;AACtE;AAGO,SAASE,EAAkBF,GAA4B;AAC5D,SAAO,CAAC,eAAe,QAAQ,EAAE,SAASA,CAAS;AACrD;AAOO,SAASG,EAAgBH,GAAuCrB,GAA0B;AAC/F,SAAIA,MAAU,SAAkBD,EAAUC,CAAK,IAC3CsB,EAAiBD,CAAS,IAAU,CAAA,IACpCA,MAAc,WAAiB,KAC5B;AACT;AASO,SAASI,EAAeC,GAAyBC,IAAgC,IAA6B;AACnH,QAAMzB,IAASH,EAAU4B,CAAI;AAE7B,SAAAD,EAAQ,QAAQ,CAAAzB,MAAQ;AAGtB,IAFI,CAACA,EAAK,QACWG,EAAUF,GAAQD,EAAK,IAAI,MAC3B,UACrBO,EAAUN,GAAQD,EAAK,MAAMuB,EAAgBV,EAAiBb,CAAI,GAAGA,EAAK,KAAK,CAAC;AAAA,EAClF,CAAC,GAEMC;AACT;AAGO,SAAS0B,EAAmB3B,GAAoBgB,GAA4C;AACjG,SAAM,UAAUhB,IACT,EAAQW,EAAaX,EAAK,MAAoFgB,GAAUhB,CAAI,IADrG;AAEhC;AAGO,SAAS4B,EAAsB5B,GAAoBgB,GAAmCa,IAAgB,IAAgB;AAC3H,SAAIA,IAAsB,KACpB,cAAc7B,IACb,EAAQW,EAAaX,EAAK,UAAwFgB,GAAUhB,CAAI,IADrG;AAEpC;AAGO,SAAS8B,EAAiB9B,GAAoBgB,GAA2C;AAC9F,SAAO,OAAOL,EAAaX,EAAK,OAAOgB,GAAUhB,CAAI,CAAC;AACxD;AAOO,SAAS+B,EAAe/B,GAAoBgB,GAA2C;AAC5F,MAAIhB,EAAK;AACP,WAAO,OAAOW,EAAaX,EAAK,aAAagB,GAAUhB,CAAI,CAAC;AAG9D,QAAMoB,IAAYP,EAAiBb,CAAI;AAEvC,SAAO,GADQmB,EAAiBC,CAAS,IAAI,QAAQ,KACrC,GAAGU,EAAiB9B,GAAMgB,CAAQ,CAAC;AACrD;AASO,SAASgB,EAAYhC,GAAoBgB,GAAmD;AACjG,QAAMI,IAAYP,EAAiBb,CAAI,GACjCiC,IAAWjC,EAAK,YAAYA,EAAK,OACjCkC,IAAwB,CAAA;AAE9B,MAAID,KAAYtB,EAAasB,GAAwFjB,GAAUhB,CAAI,GAAG;AACpI,UAAMmC,IAAqB;AAAA,MACzB,UAAU;AAAA,MACV,SAASJ,EAAe/B,GAAMgB,CAAQ;AAAA,MACtC,SAASG,EAAiBC,CAAS,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,QAAQ,QAAQ;AAAA,IAAA;AAG9E,IAAIC,EAAiBD,CAAS,MAAGe,EAAK,OAAO,UACzCb,EAAkBF,CAAS,MAAGe,EAAK,OAAO,WAC9CD,EAAM,KAAKC,CAAI;AAAA,EACjB;AAEA,MAAInC,EAAK,OAAO;AACd,UAAMoC,IAAczB,EAAaX,EAAK,OAAOgB,GAAUhB,CAAI;AAC3D,IAAI,MAAM,QAAQoC,CAAW,KAAGF,EAAM,KAAK,GAAGE,CAAW;AAAA,EAC3D;AAEA,SAAOF;AACT;ACxHO,SAASG,EACdtB,GACwB;AACxB,QAAMuB,IAAWd,EAAeT,EAAQ,SAASA,EAAQ,QAAQ,EAAE,GAC7DwB,IAAQC,EAAS;AAAA,IACrB,SAASzB,EAAQ;AAAA,IACjB,MAAMjB,EAAUwC,CAAQ;AAAA,IACxB,SAASvB,EAAQ,WAAW;AAAA,IAC5B,MAAMA,EAAQ,QAAQ;AAAA,IACtB,WAAWA,EAAQ,aAAa;AAAA,IAChC,UAAUA,EAAQ;AAAA,IAClB,eAAeA,EAAQ;AAAA,IACvB,QAAQA,EAAQ;AAAA,EAAA,CACjB;AAED,WAAS0B,EAAYf,GAAwB;AAC3C,WAAO,KAAKa,EAAM,IAAI,EAAE,QAAQ,OAAO,OAAQA,EAAM,KAAiCrC,CAAG,CAAC,GAC1F,OAAO,OAAOqC,EAAM,MAAMf,EAAeT,EAAQ,SAASW,CAA+B,CAAC;AAAA,EAC5F;AAEA,iBAAegB,EAAOhB,GAAyB;AAC7C,UAAMX,EAAQ,WAAWjB,EAAU4B,KAAQa,EAAM,IAAI,GAAGA,EAAM,IAAI;AAAA,EACpE;AAEA,WAASI,IAAc;AACrB,IAAAJ,EAAM,UAAU,IAChBxB,EAAQ,UAAA;AAAA,EACV;AAEA,WAAS6B,EAAQlB,GAAyB;AACxC,IAAAa,EAAM,OAAO,OACbE,EAAY,EAAE,GAAG3C,EAAUwC,CAAQ,GAAG,GAAIZ,KAAQ,CAAA,GAAK,GACvDa,EAAM,UAAU;AAAA,EAClB;AAEA,WAASM,EAASC,GAAc;AAC9B,IAAAP,EAAM,OAAO,QACbE,EAAY3C,EAAUgD,CAAG,CAAC,GAC1BP,EAAM,UAAU;AAAA,EAClB;AAEA,WAASQ,EAASD,GAAc;AAC9B,IAAAP,EAAM,OAAO,QACbE,EAAY3C,EAAUgD,CAAG,CAAC,GAC1BP,EAAM,UAAU;AAAA,EAClB;AAEA,WAASS,EAAYC,GAAclD,GAAsB;AACvD,IAAAQ,EAAUgC,EAAM,MAAiCU,GAAMlD,CAAK;AAAA,EAC9D;AAEA,SAAAwC,EAAM,SAASG,GACfH,EAAM,QAAQI,GACdJ,EAAM,UAAUK,GAChBL,EAAM,WAAWM,GACjBN,EAAM,WAAWQ,GACjBR,EAAM,cAAcS,GAEbT;AACT;AC3DO,SAASW,EACdnC,GACyB;AACzB,QAAMoC,IAAc3B,EAAeT,EAAQ,SAASA,EAAQ,QAAQ,EAAE,GAChEqC,IAAetD,EAAUqD,CAAW,GACpCzB,IAAOc,EAASW,CAAW;AAEjC,iBAAeT,EAAOW,GAA+B;AAEnD,UAAMC,IAAUxD,EAAUuD,KAAc3B,CAAI;AAC5C,UAAMX,EAAQ,WAAWuC,CAAO;AAAA,EAClC;AAEA,iBAAeC,IAAoB;AACjC,WAAO,KAAK7B,CAAI,EAAE,QAAQ,OAAO,OAAOA,EAAKxB,CAAG,CAAC,GACjD,OAAO,OAAOwB,GAAM5B,EAAUsD,CAAY,CAAC;AAE3C,UAAME,IAAUxD,EAAU4B,CAAI;AAC9B,iBAAMX,EAAQ,UAAUuC,CAAO,GACxBA;AAAA,EACT;AAEA,SAAOd,EAAS;AAAA,IACd,SAASzB,EAAQ;AAAA,IACjB,MAAAW;AAAA,IACA,WAAWX,EAAQ,aAAa;AAAA,IAChC,eAAeA,EAAQ,iBAAiB;AAAA,IACxC,kBAAkBA,EAAQ;AAAA,IAC1B,QAAQA,EAAQ;AAAA,IAChB,QAAA2B;AAAA,IACA,OAAAa;AAAA,IACA,YAAYxC,EAAQ;AAAA,EAAA,CACrB;AACH;AC1CA,SAASyC,EAAiBC,GAA8D;AACtF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OAAO;AAAA,IACP,WAAW,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG;AAAA,IAC/B,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,GAAIA,MAAe,KAAQ,KAAKA;AAAA,EAAA;AAEpC;AAGA,SAASC,EAAqB3C,GAA0E;AACtG,SAAI,OAAOA,EAAQ,kBAAmB,YAAkBA,EAAQ,iBACzDA,EAAQ,eAAe;AAChC;AAGA,SAAS4C,EAAkBC,GAAqD;AAC9E,SAAIA,MAAW,KAAc,EAAE,QAAQ,GAAA,IACnCA,MAAW,MAAQA,MAAW,SAAkB,EAAE,QAAQ,GAAA,IACvD;AAAA,IACL,QAAQA,EAAO,UAAU;AAAA,IACzB,GAAGA,EAAO;AAAA,EAAA;AAEd;AAUO,SAASC,EACd9C,GACoB;AACpB,QAAMwB,IAAQC,EAAS;AAAA,IACrB,SAASzB,EAAQ;AAAA,IACjB,MAAMA,EAAQ,QAAQ,CAAA;AAAA,IACtB,gBAAgB2C,EAAqB3C,CAAO;AAAA,IAC5C,YAAYyC,EAAiBzC,EAAQ,UAAU;AAAA,IAC/C,MAAMA,EAAQ;AAAA,IACd,SAAS;AAAA,IACT,QAAQA,EAAQ;AAAA,IAChB,WAAW,eAAeA,KAAWA,EAAQ,YAAYA,EAAQ,YAAY;AAAA,IAC7E,QAAQ4C,EAAkB5C,EAAQ,MAAM;AAAA,EAAA,CACzC;AAED,iBAAe+C,IAAsB;AACnC,QAAK/C,EAAQ,aAEb;AAAA,MAAAwB,EAAM,UAAU;AAChB,UAAI;AACF,cAAMxB,EAAQ,YAAA;AAAA,MAChB,UAAA;AACE,QAAAwB,EAAM,UAAU;AAAA,MAClB;AAAA;AAAA,EACF;AAEA,iBAAegB,IAAuB;AACpC,IAAAhB,EAAM,WAAW,OAAO,GACxB,MAAMuB,EAAA;AAAA,EACR;AAEA,iBAAeC,EAAQC,GAA6B;AAClD,IAAAzB,EAAM,WAAW,OAAOyB,GACxB,MAAMF,EAAA;AAAA,EACR;AAEA,iBAAeG,EAAYC,GAAiC;AAC1D,IAAA3B,EAAM,WAAW,WAAW2B,GAC5B3B,EAAM,WAAW,OAAO,GACxB,MAAMuB,EAAA;AAAA,EACR;AAEA,iBAAeK,EAAapE,GAAuC;AACjE,IAAIwC,EAAM,SACRA,EAAM,KAAK,QAAQxC,GACnB,MAAMwC,EAAM,KAAK,WAAWxC,CAAK,IAEnCwC,EAAM,WAAW,OAAO,GACxB,MAAMuB,EAAA;AAAA,EACR;AAEA,SAAAvB,EAAM,OAAOuB,GACbvB,EAAM,QAAQgB,GACdhB,EAAM,UAAUwB,GAChBxB,EAAM,cAAc0B,GACpB1B,EAAM,eAAe4B,GAEd5B;AACT;"}
@@ -0,0 +1,16 @@
1
+ export interface CopyTextOptions {
2
+ /** 复制成功提示,组件层会用当前 UI 的 message 展示。 */
3
+ successMessage?: string;
4
+ /** 复制失败提示,组件层会用当前 UI 的 message 展示。 */
5
+ errorMessage?: string;
6
+ }
7
+ /**
8
+ * 复制文本到剪贴板。
9
+ *
10
+ * 实现策略:
11
+ * 1. 优先使用现代浏览器的 `navigator.clipboard.writeText`。
12
+ * 2. 如果当前环境不支持,则降级创建临时 textarea 执行复制命令。
13
+ *
14
+ * 这样新包不再依赖旧 `@libs/shared-components/CopyToClipboard`。
15
+ */
16
+ export declare function copyText(text: unknown): Promise<void>;
@@ -0,0 +1,4 @@
1
+ export * from './clipboard';
2
+ export * from './message';
3
+ export * from './request';
4
+ export * from './sm4';