create-bubbles 0.1.11 → 0.1.13

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 (161) hide show
  1. package/dist/index.js +2 -1
  2. package/package.json +1 -1
  3. package/template-vp-monorepo-react-nestjs/.oxfmtrc.jsonc +8 -0
  4. package/template-vp-monorepo-react-nestjs/.oxlintrc.jsonc +3 -0
  5. package/template-vp-monorepo-react-nestjs/.specs//351/234/200/346/261/202.md +5 -0
  6. package/template-vp-monorepo-react-nestjs/.vite-hooks/commit-msg +1 -0
  7. package/template-vp-monorepo-react-nestjs/.vite-hooks/pre-commit +1 -0
  8. package/template-vp-monorepo-react-nestjs/.vscode/extensions.json +3 -0
  9. package/template-vp-monorepo-react-nestjs/README.md +29 -0
  10. package/template-vp-monorepo-react-nestjs/apps/service/.oxlintrc.jsonc +3 -0
  11. package/template-vp-monorepo-react-nestjs/apps/web/.env +1 -0
  12. package/template-vp-monorepo-react-nestjs/apps/web/.oxlintrc.jsonc +4 -0
  13. package/{template-vue-vite-biome → template-vp-monorepo-react-nestjs/apps/web}/index.html +4 -4
  14. package/template-vp-monorepo-react-nestjs/apps/web/package.json +39 -0
  15. package/template-vp-monorepo-react-nestjs/apps/web/public/favicon.svg +1 -0
  16. package/template-vp-monorepo-react-nestjs/apps/web/public/icons.svg +24 -0
  17. package/template-vp-monorepo-react-nestjs/apps/web/src/App.tsx +24 -0
  18. package/template-vp-monorepo-react-nestjs/apps/web/src/assets/hero.png +0 -0
  19. package/template-vp-monorepo-react-nestjs/apps/web/src/assets/react.svg +1 -0
  20. package/template-vp-monorepo-react-nestjs/apps/web/src/assets/vite.svg +1 -0
  21. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Loading/PageLoading.tsx +11 -0
  22. package/template-vp-monorepo-react-nestjs/apps/web/src/main.tsx +5 -0
  23. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/home/index.tsx +17 -0
  24. package/template-vp-monorepo-react-nestjs/apps/web/src/router/index.tsx +25 -0
  25. package/template-vp-monorepo-react-nestjs/apps/web/src/styles/index.css +7 -0
  26. package/template-vp-monorepo-react-nestjs/apps/web/src/types/auto-imports.d.ts +58 -0
  27. package/{template-vue-vite-biome → template-vp-monorepo-react-nestjs/apps/web}/tsconfig.json +5 -9
  28. package/template-vp-monorepo-react-nestjs/apps/web/vite.config.ts +32 -0
  29. package/template-vp-monorepo-react-nestjs/commitlint.config.js +0 -0
  30. package/template-vp-monorepo-react-nestjs/package.json +20 -0
  31. package/template-vp-monorepo-react-nestjs/packages/utils/README.md +23 -0
  32. package/template-vp-monorepo-react-nestjs/packages/utils/package.json +39 -0
  33. package/template-vp-monorepo-react-nestjs/packages/utils/src/index.ts +3 -0
  34. package/template-vp-monorepo-react-nestjs/packages/utils/tests/index.test.ts +6 -0
  35. package/template-vp-monorepo-react-nestjs/packages/utils/tsconfig.json +20 -0
  36. package/template-vp-monorepo-react-nestjs/packages/utils/tsdown.config.ts +9 -0
  37. package/template-vp-monorepo-react-nestjs/packages/utils/vite.config.ts +7 -0
  38. package/template-vp-monorepo-react-nestjs/pnpm-workspace.yaml +23 -0
  39. package/template-vp-monorepo-react-nestjs/tsconfig.json +9 -0
  40. package/template-vp-monorepo-react-nestjs/vite.config.ts +8 -0
  41. package/template-vp-react/.vscode/settings.json +11 -0
  42. package/template-vp-react/package.json +10 -10
  43. package/template-vp-react-shadcn/.env +1 -0
  44. package/template-vp-react-shadcn/.oxfmtrc.jsonc +8 -0
  45. package/template-vp-react-shadcn/.oxlintrc.jsonc +3 -0
  46. package/template-vp-react-shadcn/.vite-hooks/commit-msg +1 -0
  47. package/template-vp-react-shadcn/.vite-hooks/pre-commit +1 -0
  48. package/template-vp-react-shadcn/.vscode/extensions.json +3 -0
  49. package/template-vp-react-shadcn/.vscode/settings.json +12 -0
  50. package/template-vp-react-shadcn/agents.md +44 -0
  51. package/template-vp-react-shadcn/commitlint.config.js +1 -0
  52. package/template-vp-react-shadcn/components.json +25 -0
  53. package/template-vp-react-shadcn/index.html +13 -0
  54. package/template-vp-react-shadcn/package.json +56 -0
  55. package/template-vp-react-shadcn/public/favicon.svg +1 -0
  56. package/template-vp-react-shadcn/public/icons.svg +24 -0
  57. package/template-vp-react-shadcn/src/App.tsx +24 -0
  58. package/template-vp-react-shadcn/src/assets/hero.png +0 -0
  59. package/template-vp-react-shadcn/src/assets/react.svg +1 -0
  60. package/template-vp-react-shadcn/src/assets/vite.svg +1 -0
  61. package/template-vp-react-shadcn/src/components/Loading/PageLoading.tsx +11 -0
  62. package/template-vp-react-shadcn/src/components/Player/VideoJS/index.tsx +163 -0
  63. package/template-vp-react-shadcn/src/components/Player/index.ts +7 -0
  64. package/template-vp-react-shadcn/src/components/ui/button.tsx +67 -0
  65. package/template-vp-react-shadcn/src/components/ui/sonner.tsx +43 -0
  66. package/template-vp-react-shadcn/src/components/ui/spinner.tsx +17 -0
  67. package/template-vp-react-shadcn/src/hooks/.gitkeep +0 -0
  68. package/template-vp-react-shadcn/src/lib/utils.ts +6 -0
  69. package/template-vp-react-shadcn/src/main.tsx +5 -0
  70. package/template-vp-react-shadcn/src/pages/home/index.tsx +96 -0
  71. package/template-vp-react-shadcn/src/router/index.tsx +25 -0
  72. package/template-vp-react-shadcn/src/router/modules/.gitkeep +0 -0
  73. package/template-vp-react-shadcn/src/store/.gitkeep +0 -0
  74. package/template-vp-react-shadcn/src/styles/index.css +16 -0
  75. package/template-vp-react-shadcn/src/styles/shadcn.css +127 -0
  76. package/template-vp-react-shadcn/src/types/auto-imports.d.ts +58 -0
  77. package/template-vp-react-shadcn/src/utils/env/index.tsx +6 -0
  78. package/template-vp-react-shadcn/src/utils/request/alova-core/index.ts +276 -0
  79. package/template-vp-react-shadcn/src/utils/request/alova-core/utils.ts +24 -0
  80. package/template-vp-react-shadcn/src/utils/request/index.ts +48 -0
  81. package/template-vp-react-shadcn/src/utils/request/readme.md +176 -0
  82. package/template-vp-react-shadcn/tsconfig.json +32 -0
  83. package/template-vp-react-shadcn/vite.config.ts +35 -0
  84. package/template-vue-vite-eslint/package.json +29 -33
  85. package/template-vue-vite-eslint/src/main.ts +0 -1
  86. package/template-vue-vite-eslint/src/styles/index.scss +0 -3
  87. package/template-vue-vite-eslint/src/types/components.d.ts +0 -4
  88. package/template-vue-vite-eslint/src/utils/request/index.ts +4 -4
  89. package/template-vue-vite-eslint/vite.config.ts +2 -4
  90. package/template-vue-rsbuild-biome/.env +0 -5
  91. package/template-vue-rsbuild-biome/.env.development +0 -1
  92. package/template-vue-rsbuild-biome/.env.production +0 -1
  93. package/template-vue-rsbuild-biome/.prettierignore +0 -4
  94. package/template-vue-rsbuild-biome/.prettierrc +0 -18
  95. package/template-vue-rsbuild-biome/.vscode/settings.json +0 -13
  96. package/template-vue-rsbuild-biome/README.md +0 -29
  97. package/template-vue-rsbuild-biome/biome.json +0 -109
  98. package/template-vue-rsbuild-biome/index.html +0 -7
  99. package/template-vue-rsbuild-biome/lefthook.yml +0 -11
  100. package/template-vue-rsbuild-biome/package.json +0 -36
  101. package/template-vue-rsbuild-biome/rsbuild.config.ts +0 -41
  102. package/template-vue-rsbuild-biome/src/App.vue +0 -3
  103. package/template-vue-rsbuild-biome/src/index.ts +0 -13
  104. package/template-vue-rsbuild-biome/src/router/guard/index.tsx +0 -6
  105. package/template-vue-rsbuild-biome/src/router/guard/permissionGuard.ts +0 -8
  106. package/template-vue-rsbuild-biome/src/router/index.tsx +0 -14
  107. package/template-vue-rsbuild-biome/src/router/modules/index.tsx +0 -10
  108. package/template-vue-rsbuild-biome/src/store/index.ts +0 -7
  109. package/template-vue-rsbuild-biome/src/store/modules/user.ts +0 -17
  110. package/template-vue-rsbuild-biome/src/styles/index.css +0 -16
  111. package/template-vue-rsbuild-biome/src/types/auto-import.d.ts +0 -78
  112. package/template-vue-rsbuild-biome/src/types/components.d.ts +0 -15
  113. package/template-vue-rsbuild-biome/src/types/env.d.ts +0 -17
  114. package/template-vue-rsbuild-biome/src/utils/env.ts +0 -10
  115. package/template-vue-rsbuild-biome/src/utils/request/core/index.ts +0 -166
  116. package/template-vue-rsbuild-biome/src/utils/request/core/utils.ts +0 -38
  117. package/template-vue-rsbuild-biome/src/utils/request/index.ts +0 -39
  118. package/template-vue-rsbuild-biome/src/views/home/index.vue +0 -14
  119. package/template-vue-rsbuild-biome/tsconfig.json +0 -29
  120. package/template-vue-rsbuild-biome/uno.config.ts +0 -9
  121. package/template-vue-vite-biome/.env +0 -4
  122. package/template-vue-vite-biome/.env.development +0 -1
  123. package/template-vue-vite-biome/.env.production +0 -1
  124. package/template-vue-vite-biome/.gitlab-ci.yml +0 -84
  125. package/template-vue-vite-biome/.vscode/settings.json +0 -10
  126. package/template-vue-vite-biome/README.md +0 -3
  127. package/template-vue-vite-biome/biome.json +0 -109
  128. package/template-vue-vite-biome/commitlint.config.js +0 -1
  129. package/template-vue-vite-biome/lefthook.yml +0 -11
  130. package/template-vue-vite-biome/package.json +0 -50
  131. package/template-vue-vite-biome/public/vite.svg +0 -1
  132. package/template-vue-vite-biome/src/App.vue +0 -19
  133. package/template-vue-vite-biome/src/assets/icon/vue.svg +0 -1
  134. package/template-vue-vite-biome/src/components/Icon/svg-icon.vue +0 -29
  135. package/template-vue-vite-biome/src/layout/default/index.vue +0 -3
  136. package/template-vue-vite-biome/src/main.ts +0 -15
  137. package/template-vue-vite-biome/src/router/guard/index.ts +0 -7
  138. package/template-vue-vite-biome/src/router/guard/permissionGuard.ts +0 -13
  139. package/template-vue-vite-biome/src/router/index.ts +0 -17
  140. package/template-vue-vite-biome/src/router/modules/index.ts +0 -31
  141. package/template-vue-vite-biome/src/store/index.ts +0 -10
  142. package/template-vue-vite-biome/src/store/modules/user.ts +0 -17
  143. package/template-vue-vite-biome/src/styles/element-plus-variables.css +0 -4
  144. package/template-vue-vite-biome/src/styles/index.scss +0 -31
  145. package/template-vue-vite-biome/src/styles/variables.scss +0 -3
  146. package/template-vue-vite-biome/src/types/vite-env.d.ts +0 -13
  147. package/template-vue-vite-biome/src/utils/env.ts +0 -10
  148. package/template-vue-vite-biome/src/utils/request/core/index.ts +0 -166
  149. package/template-vue-vite-biome/src/utils/request/core/utils.ts +0 -38
  150. package/template-vue-vite-biome/src/utils/request/index.ts +0 -39
  151. package/template-vue-vite-biome/src/views/home/index.vue +0 -16
  152. package/template-vue-vite-biome/src/views/login/index.vue +0 -11
  153. package/template-vue-vite-biome/src/views/model/index.vue +0 -7
  154. package/template-vue-vite-biome/uno.config.ts +0 -10
  155. package/template-vue-vite-biome/vite.config.ts +0 -51
  156. package/template-vue-vite-eslint/.vscode/settings.json +0 -62
  157. package/template-vue-vite-eslint/src/styles/element-plus-variables.css +0 -3
  158. package/template-vue-vite-eslint/src/styles/plus-pro-components-variables.css +0 -3
  159. package/template-vue-vite-eslint/src/types/index.d.ts +0 -1
  160. /package/{template-vue-rsbuild-biome/public → template-vp-monorepo-react-nestjs/apps/web/src/router/modules}/.gitkeep +0 -0
  161. /package/{template-vue-vite-biome/src/styles/font.scss → template-vp-react-shadcn/src/api/.gitkeep} +0 -0
@@ -0,0 +1,276 @@
1
+ import type { Alova, AlovaDefaultCacheAdapter, AlovaOptions } from "alova";
2
+ import { createAlova } from "alova";
3
+ import adapterFetch, { type FetchRequestAdapter, type FetchRequestInit } from "alova/fetch";
4
+ import type { ReactHookExportType } from "alova/react";
5
+
6
+ import { deepMergeObject, isPlainObject } from "./utils";
7
+
8
+ type StatusMatcher = number | number[];
9
+ type HeaderValue = string | (() => string);
10
+ type RequestAlovaGenerics = {
11
+ Responded: unknown;
12
+ Transformed: unknown;
13
+ RequestConfig: FetchRequestInit;
14
+ Response: Response;
15
+ ResponseHeader: Headers;
16
+ L1Cache: AlovaDefaultCacheAdapter;
17
+ L2Cache: AlovaDefaultCacheAdapter;
18
+ StatesExport: ReactHookExportType<unknown>;
19
+ };
20
+
21
+ interface StatusMap {
22
+ success?: StatusMatcher;
23
+ unAuthorized?: StatusMatcher;
24
+ }
25
+
26
+ interface CodeMap {
27
+ success?: number[];
28
+ unAuthorized?: number[];
29
+ }
30
+
31
+ export interface BaseRequestOption {
32
+ baseUrl?: string;
33
+ timeout?: number;
34
+ commonHeaders?: Record<string, HeaderValue>;
35
+ statusMap?: StatusMap;
36
+ codeMap?: CodeMap;
37
+ responseDataKey?: string;
38
+ responseMessageKey?: string;
39
+ isTransformResponse?: boolean;
40
+ isShowSuccessMessage?: boolean;
41
+ successDefaultMessage?: string;
42
+ isShowErrorMessage?: boolean;
43
+ errorDefaultMessage?: string;
44
+ statesHook?: AlovaOptions<RequestAlovaGenerics>["statesHook"];
45
+ successMessageFunc?: (message: string) => void;
46
+ errorMessageFunc?: (message: string) => void;
47
+ unAuthorizedResponseFunc?: () => void;
48
+ requestAdapter?: FetchRequestAdapter;
49
+ }
50
+
51
+ export interface CustomConfig {
52
+ isTransformResponse?: boolean;
53
+ isShowSuccessMessage?: boolean;
54
+ isShowErrorMessage?: boolean;
55
+ }
56
+
57
+ declare module "alova" {
58
+ interface AlovaCustomTypes {
59
+ meta: CustomConfig;
60
+ }
61
+ }
62
+
63
+ interface RequestErrorOptions {
64
+ status?: number;
65
+ code?: unknown;
66
+ data?: unknown;
67
+ response?: Response;
68
+ }
69
+
70
+ export class RequestError extends Error {
71
+ status?: number;
72
+ code?: unknown;
73
+ data?: unknown;
74
+ response?: Response;
75
+
76
+ constructor(message: string, options: RequestErrorOptions = {}) {
77
+ super(message);
78
+ this.name = "RequestError";
79
+ this.status = options.status;
80
+ this.code = options.code;
81
+ this.data = options.data;
82
+ this.response = options.response;
83
+ }
84
+ }
85
+
86
+ export type RequestInstance = Alova<RequestAlovaGenerics>;
87
+ export type DualCallInstance = RequestInstance & ((option?: CustomConfig) => RequestInstance);
88
+
89
+ type RequestOption = BaseRequestOption & CustomConfig;
90
+
91
+ const DEFAULT_REQUEST_ADAPTER = adapterFetch();
92
+ const DEFAULT_RESPONSE_DATA_KEY = "data";
93
+ const DEFAULT_RESPONSE_MESSAGE_KEY = "message";
94
+ const DEFAULT_SUCCESS_MESSAGE = "操作成功";
95
+ const DEFAULT_ERROR_MESSAGE = "服务异常";
96
+ const DEFAULT_REQUEST_OPTION: RequestOption = {
97
+ baseUrl: "/",
98
+ timeout: 0,
99
+ statusMap: {
100
+ success: [200, 201, 204],
101
+ unAuthorized: 401,
102
+ },
103
+ codeMap: {
104
+ success: [200],
105
+ unAuthorized: [401],
106
+ },
107
+ responseDataKey: DEFAULT_RESPONSE_DATA_KEY,
108
+ responseMessageKey: DEFAULT_RESPONSE_MESSAGE_KEY,
109
+ isTransformResponse: true,
110
+ isShowSuccessMessage: false,
111
+ successDefaultMessage: DEFAULT_SUCCESS_MESSAGE,
112
+ isShowErrorMessage: true,
113
+ errorDefaultMessage: DEFAULT_ERROR_MESSAGE,
114
+ requestAdapter: DEFAULT_REQUEST_ADAPTER,
115
+ };
116
+
117
+ function isMatchedStatus(status: number, matcher?: StatusMatcher) {
118
+ if (matcher === undefined) return status >= 200 && status < 300;
119
+ return Array.isArray(matcher) ? matcher.includes(status) : matcher === status;
120
+ }
121
+
122
+ function isMatchedCode(code: unknown, codes?: number[]) {
123
+ if (!codes?.length) return true;
124
+
125
+ const normalizedCode = Number(code);
126
+ return Number.isFinite(normalizedCode) && codes.includes(normalizedCode);
127
+ }
128
+
129
+ function getResponseMessage(data: unknown, messageKey: string, defaultMessage: string) {
130
+ if (!isPlainObject(data)) return defaultMessage;
131
+
132
+ const message = data[messageKey] ?? data.message ?? data.msg;
133
+ if (typeof message === "string") return message || defaultMessage;
134
+ if (typeof message === "number") return String(message);
135
+
136
+ return defaultMessage;
137
+ }
138
+
139
+ function getRuntimeConfig(option: RequestOption, customConfig?: CustomConfig) {
140
+ return {
141
+ isTransformResponse: customConfig?.isTransformResponse ?? option.isTransformResponse ?? true,
142
+ isShowSuccessMessage:
143
+ customConfig?.isShowSuccessMessage ?? option.isShowSuccessMessage ?? false,
144
+ isShowErrorMessage: customConfig?.isShowErrorMessage ?? option.isShowErrorMessage ?? true,
145
+ };
146
+ }
147
+
148
+ async function parseResponse(response: Response) {
149
+ if (!response.body || response.status === 204) return undefined;
150
+
151
+ const contentType = response.headers.get("content-type") ?? "";
152
+ if (contentType.includes("application/json")) return response.json();
153
+ if (contentType.startsWith("text/")) return response.text();
154
+
155
+ const clonedResponse = response.clone();
156
+ try {
157
+ return await response.json();
158
+ } catch {
159
+ try {
160
+ return await clonedResponse.text();
161
+ } catch {
162
+ return undefined;
163
+ }
164
+ }
165
+ }
166
+
167
+ export function createInstance(option: RequestOption): RequestInstance {
168
+ const mergeOption = deepMergeObject(DEFAULT_REQUEST_OPTION, option);
169
+
170
+ return createAlova({
171
+ baseURL: mergeOption.baseUrl,
172
+ timeout: mergeOption.timeout,
173
+ statesHook: mergeOption.statesHook,
174
+ requestAdapter: mergeOption.requestAdapter ?? DEFAULT_REQUEST_ADAPTER,
175
+ beforeRequest: (method) => {
176
+ for (const [key, value] of Object.entries(mergeOption.commonHeaders ?? {})) {
177
+ method.config.headers[key] = typeof value === "function" ? value() : value;
178
+ }
179
+ },
180
+ cacheFor: null,
181
+ responded: {
182
+ onSuccess: async (response, method) => {
183
+ const runtimeConfig = getRuntimeConfig(mergeOption, method.config.meta);
184
+ if (!runtimeConfig.isTransformResponse) return response;
185
+
186
+ const data = await parseResponse(response);
187
+ const responseMessageKey = mergeOption.responseMessageKey ?? DEFAULT_RESPONSE_MESSAGE_KEY;
188
+ const errorDefaultMessage = mergeOption.errorDefaultMessage ?? DEFAULT_ERROR_MESSAGE;
189
+
190
+ if (!isMatchedStatus(response.status, mergeOption.statusMap?.success)) {
191
+ if (isMatchedStatus(response.status, mergeOption.statusMap?.unAuthorized)) {
192
+ mergeOption.unAuthorizedResponseFunc?.();
193
+ }
194
+
195
+ const errorMessage = getResponseMessage(data, responseMessageKey, errorDefaultMessage);
196
+ if (runtimeConfig.isShowErrorMessage) {
197
+ mergeOption.errorMessageFunc?.(errorMessage);
198
+ }
199
+ throw new RequestError(errorMessage, {
200
+ status: response.status,
201
+ data,
202
+ response,
203
+ });
204
+ }
205
+
206
+ if (!isPlainObject(data)) return data;
207
+
208
+ const responseCode = data.code;
209
+ const responseMessage = getResponseMessage(
210
+ data,
211
+ responseMessageKey,
212
+ mergeOption.successDefaultMessage ?? DEFAULT_SUCCESS_MESSAGE,
213
+ );
214
+
215
+ if (
216
+ responseCode !== undefined &&
217
+ !isMatchedCode(responseCode, mergeOption.codeMap?.success)
218
+ ) {
219
+ if (isMatchedCode(responseCode, mergeOption.codeMap?.unAuthorized)) {
220
+ mergeOption.unAuthorizedResponseFunc?.();
221
+ }
222
+
223
+ const errorMessage = getResponseMessage(data, responseMessageKey, errorDefaultMessage);
224
+ if (runtimeConfig.isShowErrorMessage) {
225
+ mergeOption.errorMessageFunc?.(errorMessage);
226
+ }
227
+ throw new RequestError(errorMessage, {
228
+ status: response.status,
229
+ code: responseCode,
230
+ data,
231
+ response,
232
+ });
233
+ }
234
+
235
+ if (runtimeConfig.isShowSuccessMessage) {
236
+ mergeOption.successMessageFunc?.(responseMessage);
237
+ }
238
+
239
+ const responseDataKey = mergeOption.responseDataKey ?? DEFAULT_RESPONSE_DATA_KEY;
240
+ return responseDataKey in data ? data[responseDataKey] : data;
241
+ },
242
+ onError: (error) => {
243
+ const errorMessage =
244
+ error instanceof Error
245
+ ? error.message
246
+ : (mergeOption.errorDefaultMessage ?? DEFAULT_ERROR_MESSAGE);
247
+
248
+ if (mergeOption.isShowErrorMessage) {
249
+ mergeOption.errorMessageFunc?.(errorMessage);
250
+ }
251
+
252
+ throw error;
253
+ },
254
+ },
255
+ });
256
+ }
257
+
258
+ export function createDualCallInstance(baseConfig: BaseRequestOption): DualCallInstance {
259
+ const defaultInstance = createInstance(baseConfig);
260
+ const dualInstance = ((option?: CustomConfig) => {
261
+ if (!option) return defaultInstance;
262
+ return createInstance(deepMergeObject(baseConfig, option));
263
+ }) as DualCallInstance;
264
+
265
+ Object.assign(dualInstance, defaultInstance);
266
+ dualInstance.Get = defaultInstance.Get.bind(defaultInstance);
267
+ dualInstance.Post = defaultInstance.Post.bind(defaultInstance);
268
+ dualInstance.Put = defaultInstance.Put.bind(defaultInstance);
269
+ dualInstance.Delete = defaultInstance.Delete.bind(defaultInstance);
270
+ dualInstance.Patch = defaultInstance.Patch.bind(defaultInstance);
271
+ dualInstance.Head = defaultInstance.Head.bind(defaultInstance);
272
+ dualInstance.Options = defaultInstance.Options.bind(defaultInstance);
273
+ dualInstance.Request = defaultInstance.Request.bind(defaultInstance);
274
+
275
+ return dualInstance;
276
+ }
@@ -0,0 +1,24 @@
1
+ type AnyRecord = Record<string, unknown>;
2
+
3
+ export function isPlainObject(value: unknown): value is AnyRecord {
4
+ return (
5
+ value !== null &&
6
+ typeof value === "object" &&
7
+ !Array.isArray(value) &&
8
+ Object.getPrototypeOf(value) === Object.prototype
9
+ );
10
+ }
11
+
12
+ export function deepMergeObject<T extends object>(source: T, target: Partial<T>) {
13
+ const result = { ...source } as AnyRecord;
14
+
15
+ for (const [key, value] of Object.entries(target)) {
16
+ const sourceValue = result[key];
17
+ result[key] =
18
+ isPlainObject(sourceValue) && isPlainObject(value)
19
+ ? deepMergeObject(sourceValue, value)
20
+ : value;
21
+ }
22
+
23
+ return result as T;
24
+ }
@@ -0,0 +1,48 @@
1
+ import reactHook from "alova/react";
2
+ import { toast } from "sonner";
3
+
4
+ import { navigator } from "@/router";
5
+ import { envVariables } from "@/utils/env";
6
+
7
+ import { createDualCallInstance } from "./alova-core";
8
+
9
+ function normalizeBaseUrl(apiAffix?: string) {
10
+ if (!apiAffix) return "/";
11
+ if (/^https?:\/\//.test(apiAffix) || apiAffix.startsWith("/")) {
12
+ return apiAffix;
13
+ }
14
+
15
+ return `/${apiAffix}`;
16
+ }
17
+
18
+ function getBaseConfig(): Parameters<typeof createDualCallInstance>[0] {
19
+ return {
20
+ baseUrl: normalizeBaseUrl(envVariables.API_AFFIX),
21
+ statusMap: {
22
+ success: [200, 201, 204],
23
+ unAuthorized: 401,
24
+ },
25
+ codeMap: {
26
+ success: [200],
27
+ unAuthorized: [401],
28
+ },
29
+ responseDataKey: "data",
30
+ responseMessageKey: "msg",
31
+ commonHeaders: {},
32
+ successMessageFunc: (msg) => {
33
+ toast.success(msg);
34
+ },
35
+ errorMessageFunc: (msg) => {
36
+ toast.error(msg);
37
+ },
38
+ unAuthorizedResponseFunc: () => {
39
+ navigator("/login");
40
+ toast.error("登录过期或未登录");
41
+ },
42
+ statesHook: reactHook,
43
+ };
44
+ }
45
+
46
+ const alovaRequest = createDualCallInstance(getBaseConfig());
47
+
48
+ export default alovaRequest;
@@ -0,0 +1,176 @@
1
+ # request 使用说明
2
+
3
+ `src/utils/request/index.ts` 导出的是项目统一的 alova 请求实例,业务接口建议统一放在 `src/api` 里封装,页面和组件不要直接散写请求配置。
4
+
5
+ ## 基础用法
6
+
7
+ 请求方法会创建 alova `Method` 实例,不会在创建时立刻发送请求。可以在组件中交给 `useRequest`,也可以在普通函数里直接 `await`。
8
+
9
+ ```ts
10
+ import alovaRequest from "@/utils/request";
11
+
12
+ interface DramaItem {
13
+ id: number;
14
+ title: string;
15
+ }
16
+
17
+ interface DramaListParams {
18
+ page: number;
19
+ pageSize: number;
20
+ }
21
+
22
+ export const getDramaList = (params: DramaListParams) => {
23
+ return alovaRequest.Get<DramaItem[]>("/drama/list", { params });
24
+ };
25
+ ```
26
+
27
+ 组件中使用:
28
+
29
+ ```tsx
30
+ import { useRequest } from "alova/client";
31
+
32
+ import { getDramaList } from "@/api";
33
+
34
+ export function DramaList() {
35
+ const { data, loading, error } = useRequest(getDramaList({ page: 1, pageSize: 20 }), {
36
+ initialData: [],
37
+ });
38
+
39
+ // ...
40
+ }
41
+ ```
42
+
43
+ 普通异步函数中使用:
44
+
45
+ ```ts
46
+ const list = await getDramaList({ page: 1, pageSize: 20 });
47
+ ```
48
+
49
+ ## 请求参数
50
+
51
+ GET 查询参数放在第二个参数的 `params` 里:
52
+
53
+ ```ts
54
+ alovaRequest.Get<UserInfo>("/user/detail", {
55
+ params: {
56
+ id: 1,
57
+ },
58
+ });
59
+ ```
60
+
61
+ POST、PUT、PATCH、DELETE 的 body 放在第二个参数,配置放在第三个参数:
62
+
63
+ ```ts
64
+ alovaRequest.Post<CreateUserResult>("/user/create", {
65
+ name: "Tom",
66
+ });
67
+
68
+ alovaRequest.Post<CreateUserResult>(
69
+ "/user/create",
70
+ { name: "Tom" },
71
+ {
72
+ meta: {
73
+ isShowSuccessMessage: true,
74
+ },
75
+ },
76
+ );
77
+ ```
78
+
79
+ ## 返回结构
80
+
81
+ 默认按下面这种后端格式处理:
82
+
83
+ ```ts
84
+ interface ApiResponse<T> {
85
+ code: number;
86
+ data: T;
87
+ msg: string;
88
+ }
89
+ ```
90
+
91
+ 默认规则:
92
+
93
+ - HTTP 状态码 `200`、`201`、`204` 视为成功。
94
+ - 业务 `code` 在 `codeMap.success` 中视为成功,当前默认是 `[200]`。
95
+ - 成功后返回 `data` 字段,所以 `alovaRequest.Get<User[]>` 的最终数据类型就是 `User[]`。
96
+ - 如果响应不是普通对象,会直接返回解析后的原始数据,比如字符串。
97
+
98
+ ## 提示开关
99
+
100
+ 单次请求可以通过 `meta` 控制是否转换响应、是否显示成功提示、是否显示错误提示。
101
+
102
+ ```ts
103
+ alovaRequest.Post(
104
+ "/user/update",
105
+ { id: 1, name: "Tom" },
106
+ {
107
+ meta: {
108
+ isShowSuccessMessage: true,
109
+ isShowErrorMessage: false,
110
+ },
111
+ },
112
+ );
113
+ ```
114
+
115
+ 也可以创建一个带临时配置的请求实例:
116
+
117
+ ```ts
118
+ const silentRequest = alovaRequest({
119
+ isShowErrorMessage: false,
120
+ });
121
+
122
+ silentRequest.Get<UserInfo>("/user/detail", {
123
+ params: { id: 1 },
124
+ });
125
+ ```
126
+
127
+ ## 原始响应
128
+
129
+ 下载文件、读取响应头、或不想走业务 `code/data/msg` 转换时,关闭 `isTransformResponse`。
130
+
131
+ ```ts
132
+ const downloadFile = () => {
133
+ return alovaRequest.Get<Response>("/file/download", {
134
+ meta: {
135
+ isTransformResponse: false,
136
+ },
137
+ });
138
+ };
139
+
140
+ const response = await downloadFile();
141
+ const blob = await response.blob();
142
+ ```
143
+
144
+ ## 错误处理
145
+
146
+ HTTP 状态码错误、业务 `code` 错误都会抛出 `RequestError`,alova hook 会进入 `error` 分支。
147
+
148
+ ```ts
149
+ import { RequestError } from "@/utils/request/alova-core";
150
+
151
+ try {
152
+ await getDramaList({ page: 1, pageSize: 20 });
153
+ } catch (error) {
154
+ if (error instanceof RequestError) {
155
+ console.log(error.status, error.code, error.message);
156
+ }
157
+ }
158
+ ```
159
+
160
+ `401` 会自动执行 `unAuthorizedResponseFunc`,当前行为是跳转到 `/login` 并提示登录过期。
161
+
162
+ ## 环境变量
163
+
164
+ 请求基础路径来自 `VITE_API_AFFIX`:
165
+
166
+ ```env
167
+ VITE_API_AFFIX=/api
168
+ ```
169
+
170
+ 没有配置时会使用 `/`,避免出现 `/undefined`。
171
+
172
+ ## 注意事项
173
+
174
+ - 业务接口优先写成函数并标注返回泛型,例如 `alovaRequest.Get<UserInfo>()`,组件里的 `useRequest` 会自动推断数据类型。
175
+ - 不要把 GET 查询参数直接作为第二个裸对象传入,第二个参数是配置对象,查询参数必须放到 `{ params }`。
176
+ - 需要局部关闭错误提示时优先用 `meta.isShowErrorMessage`,不要在组件里重复 toast。
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
7
+ "types": ["vite/client"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+ "jsx": "react-jsx",
17
+
18
+ /* Linting */
19
+ "strict": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "erasableSyntaxOnly": true,
23
+ "noFallthroughCasesInSwitch": true,
24
+ "noUncheckedSideEffectImports": true,
25
+
26
+ "baseUrl": ".",
27
+ "paths": {
28
+ "@/*": ["src/*"]
29
+ }
30
+ },
31
+ "include": ["src"]
32
+ }
@@ -0,0 +1,35 @@
1
+ import path from "path";
2
+
3
+ import tailwindcss from "@tailwindcss/vite";
4
+ import react from "@vitejs/plugin-react";
5
+ import AutoImport from "unplugin-auto-import/vite";
6
+ import { defineConfig, loadEnv } from "vite-plus";
7
+
8
+ export default defineConfig(({ mode }) => {
9
+ const root = process.cwd();
10
+ const env = loadEnv(mode, root);
11
+ const { VITE_PORT } = env;
12
+
13
+ return {
14
+ resolve: {
15
+ alias: {
16
+ "@": path.resolve(__dirname, "src"),
17
+ },
18
+ },
19
+ server: {
20
+ port: Number(VITE_PORT),
21
+ host: "0.0.0.0",
22
+ },
23
+ plugins: [
24
+ react(),
25
+ AutoImport({
26
+ imports: ["react", "react-router", "react-dom"],
27
+ dts: "./src/types/auto-imports.d.ts",
28
+ }),
29
+ tailwindcss(),
30
+ ],
31
+ staged: {
32
+ "*": "vp check --fix",
33
+ },
34
+ };
35
+ });
@@ -11,50 +11,46 @@
11
11
  "lint:fix": "eslint --fix"
12
12
  },
13
13
  "dependencies": {
14
- "@alova/adapter-axios": "^2.0.17",
15
- "@element-plus/icons-vue": "^2.3.2",
16
- "@unocss/preset-wind3": "^66.5.10",
17
- "@vue-flow/core": "^1.48.0",
18
- "alova": "^3.4.1",
19
- "axios": "^1.13.2",
20
- "dayjs": "^1.11.19",
14
+ "@alova/adapter-axios": "^2.0.18",
15
+ "@antdv-next/icons": "^1.0.6",
16
+ "@unocss/preset-wind3": "^66.6.8",
17
+ "alova": "^3.5.1",
18
+ "antdv-next": "^1.2.2",
19
+ "axios": "^1.16.0",
20
+ "dayjs": "^1.11.20",
21
21
  "echarts": "^6.0.0",
22
- "element-plus": "^2.12.0",
23
- "nanoid": "^5.1.6",
24
22
  "nprogress": "^0.2.0",
25
23
  "pinia": "^3.0.4",
26
24
  "pinia-plugin-persistedstate": "^4.7.1",
27
25
  "plus-pro-components": "^0.1.30",
28
- "radashi": "^12.7.1",
29
- "vue": "^3.5.25",
30
- "vue-draggable-plus": "^0.6.0",
31
- "vue-router": "^4.6.4",
32
- "vue3-blocks-tree": "^0.6.2"
26
+ "radashi": "^12.9.0",
27
+ "vue": "^3.5.34",
28
+ "vue-router": "^5.0.6"
33
29
  },
34
30
  "devDependencies": {
35
- "@antfu/eslint-config": "^6.7.1",
36
- "@biomejs/biome": "^2.3.9",
37
- "@commitlint/cli": "^20.2.0",
38
- "@commitlint/config-conventional": "^20.2.0",
31
+ "@antdv-next/auto-import-resolver": "^1.0.7",
32
+ "@antfu/eslint-config": "^8.2.0",
33
+ "@commitlint/cli": "^20.5.3",
34
+ "@commitlint/config-conventional": "^20.5.3",
39
35
  "@plus-pro-components/resolver": "^0.0.3",
40
- "@types/node": "^24.10.4",
36
+ "@types/node": "^24.12.3",
41
37
  "@types/nprogress": "^0.2.3",
42
- "@unocss/eslint-plugin": "^66.5.10",
43
- "@unocss/preset-attributify": "^66.5.10",
44
- "@vitejs/plugin-vue": "^6.0.3",
45
- "@vue/tsconfig": "^0.8.1",
46
- "eslint": "9.39.2",
47
- "eslint-plugin-format": "^1.1.0",
38
+ "@unocss/eslint-plugin": "^66.6.8",
39
+ "@unocss/preset-attributify": "^66.6.8",
40
+ "@vitejs/plugin-vue": "^6.0.6",
41
+ "@vue/tsconfig": "^0.9.1",
42
+ "eslint": "10.3.0",
43
+ "eslint-plugin-format": "^2.0.1",
48
44
  "fast-glob": "^3.3.3",
49
- "lefthook": "^2.0.12",
50
- "sass": "^1.97.0",
51
- "typescript": "~5.9.3",
52
- "unocss": "66.5.10",
53
- "unplugin-auto-import": "^20.3.0",
54
- "unplugin-vue-components": "^30.0.0",
55
- "vite": "8.0.0-beta.2",
45
+ "lefthook": "^2.1.6",
46
+ "sass": "^1.99.0",
47
+ "typescript": "~6.0.3",
48
+ "unocss": "66.6.8",
49
+ "unplugin-auto-import": "^21.0.0",
50
+ "unplugin-vue-components": "^32.0.0",
51
+ "vite": "8.0.11",
56
52
  "vite-plugin-inspect": "^11.3.3",
57
53
  "vite-plugin-svg-icons": "^2.0.1",
58
- "vue-tsc": "^3.1.1"
54
+ "vue-tsc": "^3.2.8"
59
55
  }
60
56
  }
@@ -6,7 +6,6 @@ import { setupStore } from './store'
6
6
 
7
7
  import 'virtual:svg-icons-register'
8
8
 
9
- import '@/styles/element-plus-variables.css'
10
9
  import '@/styles/index.scss'
11
10
  import 'virtual:uno.css'
12
11
 
@@ -1,8 +1,5 @@
1
1
  @use './font.scss';
2
2
  @use './variables.scss';
3
- @use './element-plus-variables.css';
4
- @use './plus-pro-components-variables.css';
5
- @use 'element-plus/theme-chalk/dark/css-vars.css';
6
3
 
7
4
  html,
8
5
  body,