diff --git a/auto-imports.d.ts b/auto-imports.d.ts index d90ac97..a1e2a71 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -266,6 +266,7 @@ declare global { const useRoute: typeof import('vue-router')['useRoute'] const useRouter: typeof import('vue-router')['useRouter'] const useSSRWidth: typeof import('@vueuse/core')['useSSRWidth'] + const useSafeNForm: typeof import('./src/utils/use-safe-n-form-auto-imports')['useSafeNForm'] const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation'] const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] @@ -608,6 +609,7 @@ declare module 'vue' { readonly useRoute: UnwrapRef readonly useRouter: UnwrapRef readonly useSSRWidth: UnwrapRef + readonly useSafeNForm: UnwrapRef readonly useScreenOrientation: UnwrapRef readonly useScreenSafeArea: UnwrapRef readonly useScriptTag: UnwrapRef diff --git a/package.json b/package.json index 39c4334..fa1aec4 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@unhead/vue": "^2.0.14", "@vueuse/core": "^13.9.0", "highlight.js": "^11.11.1", + "lodash-es": "^4.17.21", "naive-ui": "^2.43.1", "pinia": "^3.0.3", "primeicons": "^7.0.0", @@ -89,6 +90,7 @@ "@tsconfig/node22": "^22.0.2", "@types/html-minifier-terser": "^7.0.2", "@types/jsdom": "^27.0.0", + "@types/lodash-es": "^4.17.12", "@types/node": "^22.18.1", "@vant/auto-import-resolver": "^1.3.0", "@vitejs/plugin-vue": "^6.0.1", @@ -124,6 +126,7 @@ "stylelint-define-config": "^16.24.0", "svgo": "^4.0.0", "tinyglobby": "^0.2.15", + "type-fest": "^5.1.0", "typescript": "~5.9.2", "unocss": "^66.5.1", "unocss-preset-animations": "^1.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3906dbd..588cc77 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: highlight.js: specifier: ^11.11.1 version: 11.11.1 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 naive-ui: specifier: ^2.43.1 version: 2.43.1(vue@3.5.22(typescript@5.9.2)) @@ -120,6 +123,9 @@ importers: '@types/jsdom': specifier: ^27.0.0 version: 27.0.0 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 '@types/node': specifier: ^22.18.1 version: 22.18.11 @@ -225,6 +231,9 @@ importers: tinyglobby: specifier: ^0.2.15 version: 0.2.15 + type-fest: + specifier: ^5.1.0 + version: 5.1.0 typescript: specifier: ~5.9.2 version: 5.9.2 @@ -5167,6 +5176,10 @@ packages: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + terser@5.44.0: resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} engines: {node: '>=10'} @@ -5259,6 +5272,10 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + type-fest@5.1.0: + resolution: {integrity: sha512-wQ531tuWvB6oK+pchHIu5lHe5f5wpSCqB8Kf4dWQRbOYc9HTge7JL0G4Qd44bh6QuJCccIzL3bugb8GI0MwHrg==} + engines: {node: '>=20'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -11011,6 +11028,8 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + tagged-tag@1.0.0: {} + terser@5.44.0: dependencies: '@jridgewell/source-map': 0.3.11 @@ -11088,6 +11107,10 @@ snapshots: type-fest@0.20.2: {} + type-fest@5.1.0: + dependencies: + tagged-tag: 1.0.0 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 diff --git a/src/App.vue b/src/App.vue index 6440045..64c7aae 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,6 +1,7 @@ diff --git a/src/utils/a2use.vue b/src/utils/a2use.vue new file mode 100644 index 0000000..b5dc098 --- /dev/null +++ b/src/utils/a2use.vue @@ -0,0 +1,112 @@ + + + diff --git a/src/utils/use-safe-n-form-auto-imports.tsx b/src/utils/use-safe-n-form-auto-imports.tsx new file mode 100644 index 0000000..ba2193c --- /dev/null +++ b/src/utils/use-safe-n-form-auto-imports.tsx @@ -0,0 +1,94 @@ +// https://www.naiveui.com/zh-CN/os-theme/components/form +// initialFormValue + +import { get } from 'lodash-es'; +import type { FormInst } from 'naive-ui'; +import type { Get, Paths } from 'type-fest'; +import type { SlotsType } from 'vue'; + +type UseSafeNFormOptions = { + initialFormValue?: T; +}; + +export function useSafeNForm = Record>( + options: UseSafeNFormOptions = {}, +) { + const formRef = ref(null); + const formValue = ref(structuredClone(toRaw(options.initialFormValue)) || ({} as T)); + + interface SafeNFormProps { + disabled?: boolean; + } + type SafeNFormSlots = SlotsType<{ + default?: { count?: number }; + }>; + + // 创建类型安全的 Form 组件 + const SafeNForm = defineComponent( + (props, ctx) => { + return () => ( + { + formRef.value = inst as unknown as FormInst; + }} + > + {ctx.slots.default?.({})} + + ); + }, + { + name: 'SafeNForm', + inheritAttrs: false, + props: ['disabled'], + }, + ); + // <<<<< + + // >>>>> 创建类型安全的 FormItem 组件 + interface SafeNFormItemProps { + label?: string; + path: Paths /* & string */; + placeholder?: string; + } + type SafeNFormItemSlots = SlotsType<{ + default: { + value: Get; + }; + }>; + + const SafeNFormItem = defineComponent< + SafeNFormItemProps, + /* Emits */ [], + /* EE */ never, + SafeNFormItemSlots + >( + (props, ctx) => { + return () => { + const value = get(formValue.value, props.path) as Get; + return ( + + {ctx.slots.default?.({ + value, + })} + + ); + }; + }, + { + name: 'SafeNFormItem', + inheritAttrs: false, + props: ['label', 'path', 'placeholder'], + }, + ); + // <<<<< + + return { + formValue, + SafeNForm, + SafeNFormItem, + formRef, + }; +} diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 03bc000..a02f1a1 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// Generated by Wrangler by running `wrangler types` (hash: b18d7aec4937222767b077e627f9f927) +// Generated by Wrangler by running `wrangler types` (hash: 0003c1b3cd56e3cc5549efef6b4c6d3d) // Runtime types generated with workerd@1.20251008.0 2025-09-09 declare namespace Cloudflare { interface GlobalProps { @@ -7,13 +7,13 @@ declare namespace Cloudflare { } interface Env { KV: KVNamespace; + VITE_APP_BUILD_TIME: string; + VITE_APP_BUILD_COMMIT: string; VITE_BUILD_SOURCE_MAP: string; VITE_BUILD_MINIFY: string; VITE_CLOUDFLARE_SERVER_ENABLED: string; VITE_APP_TITLE: string; VITE_APP_BASE: string; - VITE_APP_BUILD_COMMIT: string; - VITE_APP_BUILD_TIME: string; VITE_APP_ENABLE_VUE_DEVTOOLS: string; VITE_APP_MENU_SHOW_DEMOS: string; VITE_APP_MENU_SHOW_ORDER: string;