create-bubbles 0.1.12 → 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 (45) hide show
  1. package/dist/index.js +2 -1
  2. package/package.json +1 -1
  3. package/template-vp-react/.vscode/settings.json +11 -0
  4. package/template-vp-react-shadcn/.env +1 -0
  5. package/template-vp-react-shadcn/.oxfmtrc.jsonc +8 -0
  6. package/template-vp-react-shadcn/.oxlintrc.jsonc +3 -0
  7. package/template-vp-react-shadcn/.vite-hooks/commit-msg +1 -0
  8. package/template-vp-react-shadcn/.vite-hooks/pre-commit +1 -0
  9. package/template-vp-react-shadcn/.vscode/extensions.json +3 -0
  10. package/template-vp-react-shadcn/.vscode/settings.json +12 -0
  11. package/template-vp-react-shadcn/agents.md +44 -0
  12. package/template-vp-react-shadcn/commitlint.config.js +1 -0
  13. package/template-vp-react-shadcn/components.json +25 -0
  14. package/template-vp-react-shadcn/index.html +13 -0
  15. package/template-vp-react-shadcn/package.json +56 -0
  16. package/template-vp-react-shadcn/public/favicon.svg +1 -0
  17. package/template-vp-react-shadcn/public/icons.svg +24 -0
  18. package/template-vp-react-shadcn/src/App.tsx +24 -0
  19. package/template-vp-react-shadcn/src/api/.gitkeep +0 -0
  20. package/template-vp-react-shadcn/src/assets/hero.png +0 -0
  21. package/template-vp-react-shadcn/src/assets/react.svg +1 -0
  22. package/template-vp-react-shadcn/src/assets/vite.svg +1 -0
  23. package/template-vp-react-shadcn/src/components/Loading/PageLoading.tsx +11 -0
  24. package/template-vp-react-shadcn/src/components/Player/VideoJS/index.tsx +163 -0
  25. package/template-vp-react-shadcn/src/components/Player/index.ts +7 -0
  26. package/template-vp-react-shadcn/src/components/ui/button.tsx +67 -0
  27. package/template-vp-react-shadcn/src/components/ui/sonner.tsx +43 -0
  28. package/template-vp-react-shadcn/src/components/ui/spinner.tsx +17 -0
  29. package/template-vp-react-shadcn/src/hooks/.gitkeep +0 -0
  30. package/template-vp-react-shadcn/src/lib/utils.ts +6 -0
  31. package/template-vp-react-shadcn/src/main.tsx +5 -0
  32. package/template-vp-react-shadcn/src/pages/home/index.tsx +96 -0
  33. package/template-vp-react-shadcn/src/router/index.tsx +25 -0
  34. package/template-vp-react-shadcn/src/router/modules/.gitkeep +0 -0
  35. package/template-vp-react-shadcn/src/store/.gitkeep +0 -0
  36. package/template-vp-react-shadcn/src/styles/index.css +16 -0
  37. package/template-vp-react-shadcn/src/styles/shadcn.css +127 -0
  38. package/template-vp-react-shadcn/src/types/auto-imports.d.ts +58 -0
  39. package/template-vp-react-shadcn/src/utils/env/index.tsx +6 -0
  40. package/template-vp-react-shadcn/src/utils/request/alova-core/index.ts +276 -0
  41. package/template-vp-react-shadcn/src/utils/request/alova-core/utils.ts +24 -0
  42. package/template-vp-react-shadcn/src/utils/request/index.ts +48 -0
  43. package/template-vp-react-shadcn/src/utils/request/readme.md +176 -0
  44. package/template-vp-react-shadcn/tsconfig.json +32 -0
  45. package/template-vp-react-shadcn/vite.config.ts +35 -0
@@ -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
+ });