@movk/nuxt 0.1.1 → 1.0.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.
- package/README.md +84 -9
- package/dist/module.d.mts +11 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +29 -3
- package/dist/runtime/components/AutoForm.d.vue.ts +12 -6
- package/dist/runtime/components/AutoForm.vue +3 -1
- package/dist/runtime/components/AutoForm.vue.d.ts +12 -6
- package/dist/runtime/components/ColorChooser.d.vue.ts +11 -5
- package/dist/runtime/components/ColorChooser.vue.d.ts +11 -5
- package/dist/runtime/components/DatePicker.d.vue.ts +14 -5
- package/dist/runtime/components/DatePicker.vue.d.ts +14 -5
- package/dist/runtime/components/StarRating.d.vue.ts +7 -7
- package/dist/runtime/components/StarRating.vue.d.ts +7 -7
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererArray.d.vue.ts +6 -4
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererArray.vue.d.ts +6 -4
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererField.d.vue.ts +6 -4
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererField.vue.d.ts +6 -4
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererLayout.d.vue.ts +6 -4
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererLayout.vue.d.ts +6 -4
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererNested.d.vue.ts +6 -4
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererNested.vue.d.ts +6 -4
- package/dist/runtime/components/input/WithCharacterLimit.d.vue.ts +11 -5
- package/dist/runtime/components/input/WithCharacterLimit.vue.d.ts +11 -5
- package/dist/runtime/components/input/WithClear.d.vue.ts +12 -5
- package/dist/runtime/components/input/WithClear.vue.d.ts +12 -5
- package/dist/runtime/components/input/WithCopy.d.vue.ts +12 -5
- package/dist/runtime/components/input/WithCopy.vue.d.ts +12 -5
- package/dist/runtime/components/input/WithPasswordToggle.d.vue.ts +11 -5
- package/dist/runtime/components/input/WithPasswordToggle.vue.d.ts +11 -5
- package/dist/runtime/composables/useApiAuth.d.ts +47 -0
- package/dist/runtime/composables/useApiAuth.js +66 -0
- package/dist/runtime/composables/useApiFetch.d.ts +42 -0
- package/dist/runtime/composables/useApiFetch.js +43 -0
- package/dist/runtime/composables/useAutoForm.d.ts +874 -54
- package/dist/runtime/composables/useClientApiFetch.d.ts +24 -0
- package/dist/runtime/composables/useClientApiFetch.js +8 -0
- package/dist/runtime/composables/useDateFormatter.d.ts +21 -7
- package/dist/runtime/composables/useDateFormatter.js +92 -57
- package/dist/runtime/composables/useDownloadWithProgress.d.ts +48 -0
- package/dist/runtime/composables/useDownloadWithProgress.js +85 -0
- package/dist/runtime/composables/useUploadWithProgress.d.ts +52 -0
- package/dist/runtime/composables/useUploadWithProgress.js +117 -0
- package/dist/runtime/plugins/api.factory.d.ts +2 -0
- package/dist/runtime/plugins/api.factory.js +188 -0
- package/dist/runtime/schemas/api.d.ts +354 -0
- package/dist/runtime/schemas/api.js +212 -0
- package/dist/runtime/server/api/_movk/session.post.d.ts +10 -0
- package/dist/runtime/server/api/_movk/session.post.js +18 -0
- package/dist/runtime/types/api.d.ts +218 -0
- package/dist/runtime/types/api.js +8 -0
- package/dist/runtime/types/auth.d.ts +34 -0
- package/dist/runtime/types/auto-form-renderer.d.ts +14 -22
- package/dist/runtime/types/auto-form-renderer.js +0 -0
- package/dist/runtime/types/components.d.ts +29 -41
- package/dist/runtime/types/components.js +0 -0
- package/dist/runtime/types/index.d.ts +1 -0
- package/dist/runtime/types/index.js +3 -2
- package/dist/runtime/utils/api-utils.d.ts +64 -0
- package/dist/runtime/utils/api-utils.js +127 -0
- package/package.json +32 -25
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
export const apiSuccessConfigSchema = z.object({
|
|
3
|
+
/**
|
|
4
|
+
* 成功状态码列表
|
|
5
|
+
* @defaultValue [200, 0]
|
|
6
|
+
*/
|
|
7
|
+
successCodes: z.array(z.union([z.number(), z.string()])).default([200, 0]),
|
|
8
|
+
/**
|
|
9
|
+
* 状态码字段名
|
|
10
|
+
* @defaultValue 'code'
|
|
11
|
+
*/
|
|
12
|
+
codeKey: z.string().default("code"),
|
|
13
|
+
/**
|
|
14
|
+
* 消息字段名
|
|
15
|
+
* @defaultValue 'message'
|
|
16
|
+
*/
|
|
17
|
+
messageKey: z.string().default("message"),
|
|
18
|
+
/**
|
|
19
|
+
* 数据字段名
|
|
20
|
+
* @defaultValue 'data'
|
|
21
|
+
*/
|
|
22
|
+
dataKey: z.string().default("data")
|
|
23
|
+
});
|
|
24
|
+
export const apiSessionConfigSchema = z.object({
|
|
25
|
+
/**
|
|
26
|
+
* 是否启用 session 自动管理
|
|
27
|
+
* @defaultValue false
|
|
28
|
+
*/
|
|
29
|
+
enabled: z.boolean().default(false),
|
|
30
|
+
/**
|
|
31
|
+
* 用户信息接口路径
|
|
32
|
+
* 登录成功后自动调用此接口获取用户信息
|
|
33
|
+
* 如果不设置,则从登录响应中提取用户信息
|
|
34
|
+
*/
|
|
35
|
+
userInfoPath: z.string().optional(),
|
|
36
|
+
/**
|
|
37
|
+
* 从登录响应中提取 token 的路径
|
|
38
|
+
* @defaultValue 'data.token'
|
|
39
|
+
*/
|
|
40
|
+
tokenPath: z.string().default("data.token"),
|
|
41
|
+
/**
|
|
42
|
+
* 用户信息在响应中的路径
|
|
43
|
+
* @defaultValue 'data'
|
|
44
|
+
*/
|
|
45
|
+
userDataPath: z.string().default("data"),
|
|
46
|
+
/**
|
|
47
|
+
* session 中存储 token 的路径
|
|
48
|
+
* @defaultValue 'token'
|
|
49
|
+
*/
|
|
50
|
+
sessionTokenPath: z.string().default("token")
|
|
51
|
+
});
|
|
52
|
+
export const apiAuthConfigSchema = z.object({
|
|
53
|
+
/**
|
|
54
|
+
* 是否启用认证
|
|
55
|
+
* @defaultValue false
|
|
56
|
+
*/
|
|
57
|
+
enabled: z.boolean().default(false),
|
|
58
|
+
/**
|
|
59
|
+
* Token 来源
|
|
60
|
+
* - 'session': 从 nuxt-auth-utils 的 session 中获取 (推荐)
|
|
61
|
+
* - 'custom': 使用自定义 tokenGetter
|
|
62
|
+
* @defaultValue 'session'
|
|
63
|
+
*/
|
|
64
|
+
tokenSource: z.enum(["session", "custom"]).default("session"),
|
|
65
|
+
/**
|
|
66
|
+
* Session 中 token 的路径
|
|
67
|
+
*
|
|
68
|
+
* 例如:
|
|
69
|
+
* - 'token' -> session.token
|
|
70
|
+
* - 'user.token' -> session.user.token
|
|
71
|
+
*
|
|
72
|
+
* @defaultValue 'token'
|
|
73
|
+
*/
|
|
74
|
+
sessionTokenPath: z.string().default("token"),
|
|
75
|
+
/**
|
|
76
|
+
* Token 类型
|
|
77
|
+
* @defaultValue 'Bearer'
|
|
78
|
+
*/
|
|
79
|
+
tokenType: z.enum(["Bearer", "Basic", "Custom"]).default("Bearer"),
|
|
80
|
+
/**
|
|
81
|
+
* 自定义 Token 类型值
|
|
82
|
+
* 当 tokenType 为 'Custom' 时使用
|
|
83
|
+
*/
|
|
84
|
+
customTokenType: z.string().optional(),
|
|
85
|
+
/**
|
|
86
|
+
* 自定义 Header 名称
|
|
87
|
+
* @defaultValue 'Authorization'
|
|
88
|
+
*/
|
|
89
|
+
headerName: z.string().default("Authorization"),
|
|
90
|
+
/**
|
|
91
|
+
* 401 错误时是否自动跳转登录页
|
|
92
|
+
* @defaultValue false
|
|
93
|
+
*/
|
|
94
|
+
redirectOnUnauthorized: z.boolean().default(false),
|
|
95
|
+
/**
|
|
96
|
+
* 登录页路径
|
|
97
|
+
* @defaultValue '/login'
|
|
98
|
+
*/
|
|
99
|
+
loginPath: z.string().default("/login"),
|
|
100
|
+
/**
|
|
101
|
+
* 是否在 401 时自动清除 session
|
|
102
|
+
* @defaultValue false
|
|
103
|
+
*/
|
|
104
|
+
clearSessionOnUnauthorized: z.boolean().default(false)
|
|
105
|
+
});
|
|
106
|
+
export const apiToastConfigSchema = z.object({
|
|
107
|
+
/**
|
|
108
|
+
* 是否全局启用提示
|
|
109
|
+
* @defaultValue true
|
|
110
|
+
*/
|
|
111
|
+
enabled: z.boolean().default(true),
|
|
112
|
+
/** 成功提示配置 */
|
|
113
|
+
success: z.object({
|
|
114
|
+
/**
|
|
115
|
+
* 是否显示成功提示
|
|
116
|
+
* @defaultValue true
|
|
117
|
+
*/
|
|
118
|
+
show: z.boolean().default(true),
|
|
119
|
+
/**
|
|
120
|
+
* 默认颜色
|
|
121
|
+
* @defaultValue 'success'
|
|
122
|
+
*/
|
|
123
|
+
color: z.string().default("success"),
|
|
124
|
+
/**
|
|
125
|
+
* 默认图标
|
|
126
|
+
*/
|
|
127
|
+
icon: z.string().optional(),
|
|
128
|
+
/**
|
|
129
|
+
* 持续时间 (ms)
|
|
130
|
+
* @defaultValue 3000
|
|
131
|
+
*/
|
|
132
|
+
duration: z.number().default(3e3)
|
|
133
|
+
}).loose().optional(),
|
|
134
|
+
/** 错误提示配置 */
|
|
135
|
+
error: z.object({
|
|
136
|
+
/**
|
|
137
|
+
* 是否显示错误提示
|
|
138
|
+
* @defaultValue true
|
|
139
|
+
*/
|
|
140
|
+
show: z.boolean().default(true),
|
|
141
|
+
/**
|
|
142
|
+
* 默认颜色
|
|
143
|
+
* @defaultValue 'error'
|
|
144
|
+
*/
|
|
145
|
+
color: z.string().default("error"),
|
|
146
|
+
/**
|
|
147
|
+
* 默认图标
|
|
148
|
+
*/
|
|
149
|
+
icon: z.string().optional(),
|
|
150
|
+
/**
|
|
151
|
+
* 持续时间 (ms)
|
|
152
|
+
* @defaultValue 3000
|
|
153
|
+
*/
|
|
154
|
+
duration: z.number().default(3e3)
|
|
155
|
+
}).loose().optional()
|
|
156
|
+
});
|
|
157
|
+
export const apiEndpointConfigSchema = z.object({
|
|
158
|
+
/** 基础 URL */
|
|
159
|
+
baseURL: z.string(),
|
|
160
|
+
/** 端点别名 */
|
|
161
|
+
alias: z.string().optional(),
|
|
162
|
+
/** 该端点的认证配置 */
|
|
163
|
+
auth: apiAuthConfigSchema.partial().optional(),
|
|
164
|
+
/** 该端点的 Toast 配置 */
|
|
165
|
+
toast: apiToastConfigSchema.partial().optional(),
|
|
166
|
+
/** 该端点的成功判断配置 */
|
|
167
|
+
success: apiSuccessConfigSchema.partial().optional(),
|
|
168
|
+
/** 默认请求头 */
|
|
169
|
+
headers: z.record(z.string(), z.string()).optional()
|
|
170
|
+
});
|
|
171
|
+
export const movkApiModuleOptionsSchema = z.object({
|
|
172
|
+
/**
|
|
173
|
+
* 是否启用 API 功能
|
|
174
|
+
* @defaultValue true
|
|
175
|
+
*/
|
|
176
|
+
enabled: z.boolean().default(true),
|
|
177
|
+
/**
|
|
178
|
+
* 默认使用的端点名称
|
|
179
|
+
* @defaultValue 'default'
|
|
180
|
+
*/
|
|
181
|
+
defaultEndpoint: z.string().default("default"),
|
|
182
|
+
/**
|
|
183
|
+
* API 端点配置
|
|
184
|
+
* @defaultValue { default: { baseURL: '/api' } }
|
|
185
|
+
*/
|
|
186
|
+
endpoints: z.record(z.string(), apiEndpointConfigSchema).default({
|
|
187
|
+
default: { baseURL: "/api" }
|
|
188
|
+
}),
|
|
189
|
+
/**
|
|
190
|
+
* 全局认证配置
|
|
191
|
+
*/
|
|
192
|
+
auth: apiAuthConfigSchema.optional(),
|
|
193
|
+
/**
|
|
194
|
+
* 全局 Toast 配置
|
|
195
|
+
*/
|
|
196
|
+
toast: apiToastConfigSchema.optional(),
|
|
197
|
+
/**
|
|
198
|
+
* 全局成功判断配置
|
|
199
|
+
*/
|
|
200
|
+
success: apiSuccessConfigSchema.optional(),
|
|
201
|
+
/**
|
|
202
|
+
* 是否启用调试模式
|
|
203
|
+
* @defaultValue false
|
|
204
|
+
*/
|
|
205
|
+
debug: z.boolean().default(false)
|
|
206
|
+
}).transform((data) => ({
|
|
207
|
+
...data,
|
|
208
|
+
// 使用 schema 解析确保默认值生效
|
|
209
|
+
auth: apiAuthConfigSchema.parse(data.auth ?? {}),
|
|
210
|
+
toast: apiToastConfigSchema.parse(data.toast ?? {}),
|
|
211
|
+
success: apiSuccessConfigSchema.parse(data.success ?? {})
|
|
212
|
+
}));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineEventHandler, readBody, createError } from "h3";
|
|
2
|
+
export default defineEventHandler(async (event) => {
|
|
3
|
+
const body = await readBody(event);
|
|
4
|
+
if (!body || typeof body !== "object") {
|
|
5
|
+
throw createError({
|
|
6
|
+
statusCode: 400,
|
|
7
|
+
message: "Invalid request body"
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
if (!body.user || typeof body.user !== "object") {
|
|
11
|
+
throw createError({
|
|
12
|
+
statusCode: 400,
|
|
13
|
+
message: "User data is required"
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
await setUserSession(event, body);
|
|
17
|
+
return { success: true };
|
|
18
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { $Fetch, FetchOptions, FetchHooks, FetchError } from 'ofetch';
|
|
2
|
+
import type { UseFetchOptions as NuxtUseFetchOptions, AsyncData } from 'nuxt/app';
|
|
3
|
+
import type { ToastProps } from '@nuxt/ui';
|
|
4
|
+
import type { z } from 'zod/v4';
|
|
5
|
+
import type { apiSessionConfigSchema, apiEndpointConfigSchema, movkApiModuleOptionsSchema } from '../schemas/api.js';
|
|
6
|
+
import { apiSuccessConfigSchema, apiAuthConfigSchema, apiToastConfigSchema } from '../schemas/api.js';
|
|
7
|
+
import type { User, UserSession, UserSessionComposable } from '#auth-utils';
|
|
8
|
+
/**
|
|
9
|
+
* 扩展 ofetch FetchOptions,添加 context 字段
|
|
10
|
+
*
|
|
11
|
+
* ofetch 运行时支持 context 字段用于在 hooks 间传递自定义数据,
|
|
12
|
+
* 但其 TypeScript 类型定义未包含此字段,这里进行补充
|
|
13
|
+
*/
|
|
14
|
+
declare module 'ofetch' {
|
|
15
|
+
interface FetchOptions {
|
|
16
|
+
/** 请求上下文,用于在 hooks 间传递自定义数据 */
|
|
17
|
+
context?: ApiFetchContext;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* API 请求上下文
|
|
22
|
+
*
|
|
23
|
+
* 用于在 hooks 中传递请求级配置(如 Toast、业务检查等)
|
|
24
|
+
*/
|
|
25
|
+
export interface ApiFetchContext {
|
|
26
|
+
/** Toast 配置 */
|
|
27
|
+
toast?: RequestToastOptions | false;
|
|
28
|
+
/** 跳过业务状态码检查 */
|
|
29
|
+
skipBusinessCheck?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** 成功响应判断配置 */
|
|
32
|
+
export type ApiSuccessConfig = z.infer<typeof apiSuccessConfigSchema>;
|
|
33
|
+
/** Session 管理配置 */
|
|
34
|
+
export type ApiSessionConfig = z.infer<typeof apiSessionConfigSchema>;
|
|
35
|
+
/** 认证配置 */
|
|
36
|
+
export type ApiAuthConfig = z.infer<typeof apiAuthConfigSchema>;
|
|
37
|
+
/** Toast 配置 */
|
|
38
|
+
export type ApiToastConfig = z.infer<typeof apiToastConfigSchema>;
|
|
39
|
+
/** 单个端点配置 */
|
|
40
|
+
export type ApiEndpointConfig = z.infer<typeof apiEndpointConfigSchema>;
|
|
41
|
+
/** 模块配置 */
|
|
42
|
+
export type MovkApiModuleOptions = z.infer<typeof movkApiModuleOptionsSchema>;
|
|
43
|
+
/**
|
|
44
|
+
* API 标准响应结构
|
|
45
|
+
* 支持多种常见后端响应格式
|
|
46
|
+
*/
|
|
47
|
+
export interface ApiResponse<T = unknown> {
|
|
48
|
+
code?: number | string;
|
|
49
|
+
status?: number | string;
|
|
50
|
+
msg?: string;
|
|
51
|
+
message?: string;
|
|
52
|
+
data?: T;
|
|
53
|
+
result?: T;
|
|
54
|
+
token?: string;
|
|
55
|
+
accessToken?: string;
|
|
56
|
+
error?: string | null;
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* API 业务错误
|
|
61
|
+
*/
|
|
62
|
+
export interface ApiError extends Error {
|
|
63
|
+
statusCode: number;
|
|
64
|
+
response?: ApiResponse;
|
|
65
|
+
isBusinessError: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 合并后的端点配置(运行时使用)
|
|
69
|
+
*/
|
|
70
|
+
export interface ResolvedEndpointConfig extends ApiEndpointConfig {
|
|
71
|
+
auth: Partial<ApiAuthConfig>;
|
|
72
|
+
toast: Partial<ApiToastConfig>;
|
|
73
|
+
success: Partial<ApiSuccessConfig>;
|
|
74
|
+
builtinHooks?: FetchHooks;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 请求级 Toast 配置
|
|
78
|
+
*/
|
|
79
|
+
export interface RequestToastOptions {
|
|
80
|
+
success?: Partial<ToastProps> | false;
|
|
81
|
+
error?: Partial<ToastProps> | false;
|
|
82
|
+
successMessage?: string;
|
|
83
|
+
errorMessage?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* useApiFetch 扩展选项
|
|
87
|
+
*
|
|
88
|
+
* @typeParam ResT - API 响应 data 字段的原始类型
|
|
89
|
+
* @typeParam DataT - transform 转换后的最终类型(默认等于 ResT)
|
|
90
|
+
*/
|
|
91
|
+
export interface ApiFetchExtras<ResT, DataT = ResT> {
|
|
92
|
+
/** 端点名称 */
|
|
93
|
+
endpoint?: string;
|
|
94
|
+
/** Toast 配置 */
|
|
95
|
+
toast?: RequestToastOptions | false;
|
|
96
|
+
/**
|
|
97
|
+
* 跳过业务状态码检查
|
|
98
|
+
* @defaultValue false
|
|
99
|
+
*/
|
|
100
|
+
skipBusinessCheck?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* 自定义数据转换函数
|
|
103
|
+
* 接收解包后的数据(response.data),与 Nuxt 官方 useFetch 行为一致
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* // 从分页响应中提取列表
|
|
108
|
+
* const { data } = useApiFetch<{ content: User[] }, User[]>('/users', {
|
|
109
|
+
* transform: ({ content }) => content ?? []
|
|
110
|
+
* })
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
transform?: (data: ResT) => DataT;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* useApiFetch 选项类型
|
|
117
|
+
*
|
|
118
|
+
* 基于 Nuxt UseFetchOptions,使用 Omit 移除冲突的 transform
|
|
119
|
+
* 支持双泛型:ResT 为原始响应类型,DataT 为转换后类型
|
|
120
|
+
*
|
|
121
|
+
* @typeParam ResT - API 响应 data 字段的原始类型
|
|
122
|
+
* @typeParam DataT - transform 转换后的最终类型(默认等于 ResT)
|
|
123
|
+
*/
|
|
124
|
+
export type UseApiFetchOptions<ResT, DataT = ResT> = Omit<NuxtUseFetchOptions<ApiResponse<ResT>, DataT>, 'transform'> & ApiFetchExtras<ResT, DataT>;
|
|
125
|
+
/**
|
|
126
|
+
* useApiFetch 返回类型
|
|
127
|
+
*/
|
|
128
|
+
export type UseApiFetchReturn<DataT> = AsyncData<DataT | null, FetchError | ApiError | null>;
|
|
129
|
+
/**
|
|
130
|
+
* 下载选项
|
|
131
|
+
*/
|
|
132
|
+
export interface DownloadOptions extends Omit<FetchOptions<'json'>, 'responseType'> {
|
|
133
|
+
/**
|
|
134
|
+
* Toast 配置
|
|
135
|
+
* - false: 禁用所有提示
|
|
136
|
+
* - RequestToastOptions: 自定义成功/失败提示
|
|
137
|
+
*/
|
|
138
|
+
toast?: RequestToastOptions | false;
|
|
139
|
+
/**
|
|
140
|
+
* 下载进度回调
|
|
141
|
+
* @param progress - 下载进度百分比 (0-100)
|
|
142
|
+
*/
|
|
143
|
+
onProgress?: (progress: number) => void;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 上传选项
|
|
147
|
+
*/
|
|
148
|
+
export interface UploadOptions extends Omit<FetchOptions<'json'>, 'responseType' | 'body'> {
|
|
149
|
+
/**
|
|
150
|
+
* 文件字段名
|
|
151
|
+
* @defaultValue 'file'
|
|
152
|
+
*/
|
|
153
|
+
fieldName?: string;
|
|
154
|
+
/**
|
|
155
|
+
* Toast 配置
|
|
156
|
+
* - false: 禁用所有提示
|
|
157
|
+
* - RequestToastOptions: 自定义成功/失败提示
|
|
158
|
+
*/
|
|
159
|
+
toast?: RequestToastOptions | false;
|
|
160
|
+
/**
|
|
161
|
+
* 上传进度回调
|
|
162
|
+
* @param progress - 上传进度百分比 (0-100)
|
|
163
|
+
*/
|
|
164
|
+
onProgress?: (progress: number) => void;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* API 客户端实例
|
|
168
|
+
*/
|
|
169
|
+
export interface ApiClient {
|
|
170
|
+
$fetch: $Fetch;
|
|
171
|
+
/**
|
|
172
|
+
* 切换端点
|
|
173
|
+
* @example $api.use('v2').$fetch('/users')
|
|
174
|
+
*/
|
|
175
|
+
use: (endpoint: string) => ApiClient;
|
|
176
|
+
/**
|
|
177
|
+
* 下载文件
|
|
178
|
+
* @param url - 下载 URL
|
|
179
|
+
* @param filename - 可选的文件名,如果不提供会从 Content-Disposition 响应头中提取
|
|
180
|
+
* @param options - 下载选项
|
|
181
|
+
* @example await $api.download('/export', 'data.csv')
|
|
182
|
+
* @example await $api.download('/export') // 自动从响应头提取文件名
|
|
183
|
+
*/
|
|
184
|
+
download: (url: string, filename?: string, options?: DownloadOptions) => Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* 上传单个或多个文件
|
|
187
|
+
* @param url - 上传 URL
|
|
188
|
+
* @param file - 单个文件、文件数组或 FormData
|
|
189
|
+
* @param options - 上传选项
|
|
190
|
+
* @example await $api.upload('/upload', file, { fieldName: 'avatar' })
|
|
191
|
+
* @example await $api.upload('/upload', [file1, file2]) // 多文件上传
|
|
192
|
+
*/
|
|
193
|
+
upload: <T = unknown>(url: string, file: File | File[] | FormData, options?: UploadOptions) => Promise<ApiResponse<T>>;
|
|
194
|
+
/**
|
|
195
|
+
* 获取端点配置(内部使用)
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
getConfig: () => ResolvedEndpointConfig;
|
|
199
|
+
}
|
|
200
|
+
/** 登录选项 */
|
|
201
|
+
export interface LoginOptions<LoginRData = unknown> {
|
|
202
|
+
loginPath: string;
|
|
203
|
+
credentials: unknown;
|
|
204
|
+
userInfoPath?: string;
|
|
205
|
+
tokenExtractor?: (response: ApiResponse<LoginRData>) => string | null | undefined;
|
|
206
|
+
sessionBuilder?: (userInfo: User, token: string) => UserSession;
|
|
207
|
+
endpoint?: string;
|
|
208
|
+
}
|
|
209
|
+
export interface LoginResult {
|
|
210
|
+
user: User;
|
|
211
|
+
token: string;
|
|
212
|
+
}
|
|
213
|
+
export interface UseApiAuthReturn extends UserSessionComposable {
|
|
214
|
+
login: <LoginRData = unknown>(options: LoginOptions<LoginRData>) => Promise<LoginResult>;
|
|
215
|
+
}
|
|
216
|
+
export declare const DEFAULT_SUCCESS_CONFIG: ApiSuccessConfig;
|
|
217
|
+
export declare const DEFAULT_AUTH_CONFIG: ApiAuthConfig;
|
|
218
|
+
export declare const DEFAULT_TOAST_CONFIG: ApiToastConfig;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
apiSuccessConfigSchema,
|
|
3
|
+
apiAuthConfigSchema,
|
|
4
|
+
apiToastConfigSchema
|
|
5
|
+
} from "../schemas/api.js";
|
|
6
|
+
export const DEFAULT_SUCCESS_CONFIG = apiSuccessConfigSchema.parse({});
|
|
7
|
+
export const DEFAULT_AUTH_CONFIG = apiAuthConfigSchema.parse({});
|
|
8
|
+
export const DEFAULT_TOAST_CONFIG = apiToastConfigSchema.parse({});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
declare module '#auth-utils' {
|
|
2
|
+
interface User {
|
|
3
|
+
// Add your own fields
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface UserSession {
|
|
7
|
+
/**
|
|
8
|
+
* Session ID
|
|
9
|
+
*/
|
|
10
|
+
id?: string
|
|
11
|
+
/**
|
|
12
|
+
* User session data, available on client and server
|
|
13
|
+
*/
|
|
14
|
+
user?: User
|
|
15
|
+
/**
|
|
16
|
+
* Private session data, only available on server/ code
|
|
17
|
+
*/
|
|
18
|
+
secure?: SecureSessionData
|
|
19
|
+
/**
|
|
20
|
+
* 认证 Token
|
|
21
|
+
*/
|
|
22
|
+
token?: string
|
|
23
|
+
/**
|
|
24
|
+
* Extra session data, available on client and server
|
|
25
|
+
*/
|
|
26
|
+
[key: string]: unknown
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface SecureSessionData {
|
|
30
|
+
// Add your own fields
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { }
|
|
@@ -1,30 +1,22 @@
|
|
|
1
|
-
import type { z } from 'zod/v4'
|
|
2
|
-
import type { AnyObject } from '@movk/core'
|
|
3
|
-
import type { ButtonProps } from '@nuxt/ui'
|
|
4
|
-
import type { AutoFormField } from './auto-form'
|
|
5
|
-
import type { AutoFormProps } from '../components/AutoForm.vue'
|
|
6
|
-
|
|
7
|
-
// AutoFormRendererArray
|
|
1
|
+
import type { z } from 'zod/v4';
|
|
2
|
+
import type { AnyObject } from '@movk/core';
|
|
3
|
+
import type { ButtonProps } from '@nuxt/ui';
|
|
4
|
+
import type { AutoFormField } from './auto-form.js';
|
|
5
|
+
import type { AutoFormProps } from '../components/AutoForm.vue.js';
|
|
8
6
|
export interface AutoFormRendererArrayProps<S extends z.ZodObject> extends Pick<AutoFormProps<S>, 'schema'> {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
field: AutoFormField;
|
|
8
|
+
extraProps?: AnyObject;
|
|
9
|
+
addButtonProps?: Partial<ButtonProps>;
|
|
12
10
|
}
|
|
13
|
-
|
|
14
|
-
// AutoFormRendererField
|
|
15
11
|
export interface AutoFormFieldProps<S extends z.ZodObject> extends Pick<AutoFormProps<S>, 'schema'> {
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
field: AutoFormField;
|
|
13
|
+
extraProps?: AnyObject;
|
|
18
14
|
}
|
|
19
|
-
|
|
20
|
-
// AutoFormRendererLayout
|
|
21
15
|
export interface AutoFormRendererLayoutProps<S extends z.ZodObject> extends Pick<AutoFormProps<S>, 'schema'> {
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
field: AutoFormField;
|
|
17
|
+
extraProps?: AnyObject;
|
|
24
18
|
}
|
|
25
|
-
|
|
26
|
-
// AutoFormRendererNested
|
|
27
19
|
export interface AutoFormRendererNestedProps<S extends z.ZodObject> extends Pick<AutoFormProps<S>, 'schema'> {
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
field: AutoFormField;
|
|
21
|
+
extraProps?: AnyObject;
|
|
30
22
|
}
|
|
File without changes
|
|
@@ -1,55 +1,43 @@
|
|
|
1
|
-
import type { ButtonProps, PopoverProps, ColorPickerProps, PopoverEmits, CalendarProps, CalendarEmits, InputProps, InputEmits, InputSlots, TooltipProps, InputValue } from '@nuxt/ui'
|
|
2
|
-
import type { OmitByKey } from '@movk/core'
|
|
3
|
-
import type { ClassNameValue } from 'tailwind-merge'
|
|
4
|
-
import type { DateFormatterOptions, useDateFormatter } from '../composables/useDateFormatter'
|
|
5
|
-
|
|
6
|
-
// ColorChooser
|
|
1
|
+
import type { ButtonProps, PopoverProps, ColorPickerProps, PopoverEmits, CalendarProps, CalendarEmits, InputProps, InputEmits, InputSlots, TooltipProps, InputValue } from '@nuxt/ui';
|
|
2
|
+
import type { OmitByKey } from '@movk/core';
|
|
3
|
+
import type { ClassNameValue } from 'tailwind-merge';
|
|
4
|
+
import type { DateFormatterOptions, useDateFormatter } from '../composables/useDateFormatter.js';
|
|
7
5
|
export interface ColorChooserProps<P extends 'click' | 'hover' = 'click'> extends /** @vue-ignore */ ColorPickerProps {
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
popoverProps?: PopoverProps<P>;
|
|
7
|
+
buttonProps?: ButtonProps;
|
|
10
8
|
}
|
|
11
|
-
|
|
12
|
-
export type
|
|
13
|
-
|
|
14
|
-
// DatePicker
|
|
15
|
-
export type LabelFormat = 'iso' | 'formatted' | 'date' | 'timestamp' | 'unix'
|
|
16
|
-
export type ValueType<R extends boolean, M extends boolean> = CalendarProps<R, M>['modelValue']
|
|
17
|
-
|
|
9
|
+
export type ColorChooserEmits = PopoverEmits;
|
|
10
|
+
export type LabelFormat = 'iso' | 'formatted' | 'date' | 'timestamp' | 'unix';
|
|
11
|
+
export type ValueType<R extends boolean, M extends boolean> = CalendarProps<R, M>['modelValue'];
|
|
18
12
|
export interface DatePickerProps<R extends boolean, M extends boolean, P extends 'click' | 'hover' = 'click'> extends /** @vue-ignore */ OmitByKey<CalendarProps<R, M>, 'modelValue'>, DateFormatterOptions {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
buttonProps?: ButtonProps;
|
|
14
|
+
popoverProps?: PopoverProps<P>;
|
|
15
|
+
labelFormat?: LabelFormat | ((formatter: ReturnType<typeof useDateFormatter>, modelValue: ValueType<R, M>) => string);
|
|
22
16
|
}
|
|
23
|
-
|
|
24
|
-
export type DatePickerEmits<R extends boolean, M extends boolean> = PopoverEmits & CalendarEmits<R, M>
|
|
25
|
-
|
|
26
|
-
// Input components
|
|
17
|
+
export type DatePickerEmits<R extends boolean, M extends boolean> = PopoverEmits & CalendarEmits<R, M>;
|
|
27
18
|
export interface WithCharacterLimitProps<T extends InputValue = InputValue> extends /** @vue-ignore */ OmitByKey<InputProps<T>, 'modelValue'> {
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
counterClass?: ClassNameValue;
|
|
30
21
|
}
|
|
31
|
-
export type WithCharacterLimitEmits<T extends InputValue = InputValue> = InputEmits<T
|
|
32
|
-
export type WithCharacterLimitSlots = OmitByKey<InputSlots, 'trailing'
|
|
33
|
-
|
|
22
|
+
export type WithCharacterLimitEmits<T extends InputValue = InputValue> = InputEmits<T>;
|
|
23
|
+
export type WithCharacterLimitSlots = OmitByKey<InputSlots, 'trailing'>;
|
|
34
24
|
export interface WithClearProps<T extends InputValue = InputValue> extends /** @vue-ignore */ OmitByKey<InputProps<T>, 'modelValue'> {
|
|
35
|
-
|
|
25
|
+
buttonProps?: ButtonProps;
|
|
36
26
|
}
|
|
37
27
|
export type WithClearEmits<T extends InputValue = InputValue> = InputEmits<T> & {
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
export type WithClearSlots = OmitByKey<InputSlots, 'trailing'
|
|
41
|
-
|
|
28
|
+
clear: [];
|
|
29
|
+
};
|
|
30
|
+
export type WithClearSlots = OmitByKey<InputSlots, 'trailing'>;
|
|
42
31
|
export interface WithCopyProps<T extends InputValue = InputValue> extends /** @vue-ignore */ OmitByKey<InputProps<T>, 'modelValue'> {
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
buttonProps?: ButtonProps;
|
|
33
|
+
tooltipProps?: TooltipProps;
|
|
45
34
|
}
|
|
46
35
|
export type WithCopyEmits<T extends InputValue = InputValue> = InputEmits<T> & {
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
export type WithCopySlots = OmitByKey<InputSlots, 'trailing'
|
|
50
|
-
|
|
36
|
+
copy: [value: string];
|
|
37
|
+
};
|
|
38
|
+
export type WithCopySlots = OmitByKey<InputSlots, 'trailing'>;
|
|
51
39
|
export interface WithPasswordToggleProps<T extends InputValue = InputValue> extends /** @vue-ignore */ OmitByKey<InputProps<T>, 'type' | 'modelValue'> {
|
|
52
|
-
|
|
40
|
+
buttonProps?: ButtonProps;
|
|
53
41
|
}
|
|
54
|
-
export type WithPasswordToggleEmits<T extends InputValue = InputValue> = InputEmits<T
|
|
55
|
-
export type WithPasswordToggleSlots = OmitByKey<InputSlots, 'trailing'
|
|
42
|
+
export type WithPasswordToggleEmits<T extends InputValue = InputValue> = InputEmits<T>;
|
|
43
|
+
export type WithPasswordToggleSlots = OmitByKey<InputSlots, 'trailing'>;
|
|
File without changes
|