create-bubbles 0.1.24 → 0.1.26

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 (204) hide show
  1. package/dist/index.js +15 -0
  2. package/package.json +1 -2
  3. package/template-nextjs-vinext-eslint/Dockerfile +4 -4
  4. package/template-nextjs-vinext-eslint/README.md +6 -2
  5. package/template-nextjs-vinext-eslint/docker-compose.yaml +4 -4
  6. package/template-nextjs-vinext-eslint/eslint.config.js +2 -0
  7. package/template-nextjs-vinext-eslint/package.json +28 -26
  8. package/template-nextjs-vinext-eslint/pnpm-workspace.yaml +13 -1
  9. package/template-nextjs-vinext-eslint/src/utils/request/core/index.ts +63 -45
  10. package/template-nextjs-vinext-eslint/src/utils/request/core/utils.ts +12 -6
  11. package/template-nextjs-vinext-eslint/vite.config.ts +1 -1
  12. package/template-react-rsbuild-biome/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  13. package/template-react-rsbuild-biome/AGENTS.md +11 -0
  14. package/template-react-rsbuild-biome/index.html +2 -2
  15. package/template-react-rsbuild-biome/package.json +25 -24
  16. package/template-react-rsbuild-biome/pnpm-workspace.yaml +5 -0
  17. package/template-react-rsbuild-biome/rsbuild.config.ts +1 -1
  18. package/template-react-rsbuild-biome/src/assets/icon/logo.svg +1 -1
  19. package/template-react-rsbuild-biome/src/assets/svg/draft.svg +4 -0
  20. package/template-react-rsbuild-biome/src/components/Icon/svg-icon/README.md +37 -0
  21. package/template-react-rsbuild-biome/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  22. package/template-react-rsbuild-biome/src/env.d.ts +7 -0
  23. package/template-react-rsbuild-biome/src/utils/request/index.ts +3 -4
  24. package/template-react-rsbuild-biome/tsconfig.json +1 -2
  25. package/template-taro-react-oxc/.oxlintrc.json +0 -1
  26. package/template-taro-react-oxc/package.json +53 -52
  27. package/template-taro-react-oxc/pnpm-workspace.yaml +46 -0
  28. package/template-taro-vue-eslint/package.json +56 -56
  29. package/template-vp-monorepo-react-hono/apps/web/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  30. package/template-vp-monorepo-react-hono/apps/web/AGENTS.md +11 -0
  31. package/template-vp-monorepo-react-hono/apps/web/package.json +1 -0
  32. package/template-vp-monorepo-react-hono/apps/web/src/assets/svg/draft.svg +4 -0
  33. package/template-vp-monorepo-react-hono/apps/web/src/components/Icon/svg-icon/README.md +37 -0
  34. package/template-vp-monorepo-react-hono/apps/web/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  35. package/template-vp-monorepo-react-hono/apps/web/tsconfig.json +1 -1
  36. package/template-vp-monorepo-react-hono/apps/web/vite.config.ts +2 -0
  37. package/template-vp-monorepo-react-hono/pnpm-workspace.yaml +1 -0
  38. package/template-vp-monorepo-react-nestjs/.agents/rules//344/273/243/347/240/201/350/256/276/350/256/241.md +4 -0
  39. package/template-vp-monorepo-react-nestjs/.agents/rules//345/205/261/344/272/253/344/273/243/347/240/201.md +4 -0
  40. package/template-vp-monorepo-react-nestjs/.spaces/01./344/274/201/344/270/232/347/272/247/351/241/271/347/233/256/347/272/247//351/234/200/346/261/202/346/226/207/346/241/243.md +177 -0
  41. package/template-vp-monorepo-react-nestjs/.spaces/server/04.upload.md +4504 -0
  42. package/template-vp-monorepo-react-nestjs/.spaces/server/05./344/270/212/344/274/240/346/224/271/351/200/240/346/226/271/346/241/210/README.md +858 -0
  43. package/template-vp-monorepo-react-nestjs/.spaces/server/06./345/205/254/345/205/261/351/230/237/345/210/227/346/216/245/345/205/245/346/226/271/346/241/210/README.md +1890 -0
  44. package/template-vp-monorepo-react-nestjs/.spaces/server/06./345/205/254/345/205/261/351/230/237/345/210/227/346/216/245/345/205/245/346/226/271/346/241/210//344/270/212/344/274/240/346/270/205/347/220/206.md +1465 -0
  45. package/template-vp-monorepo-react-nestjs/AGENTS.md +8 -12
  46. package/template-vp-monorepo-react-nestjs/apps/server/.agents/rules/error-handling.md +84 -0
  47. package/template-vp-monorepo-react-nestjs/apps/server/.env.development +60 -0
  48. package/template-vp-monorepo-react-nestjs/apps/server/.env.production +16 -0
  49. package/template-vp-monorepo-react-nestjs/apps/server/AGENTS.md +9 -0
  50. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/0000_military_colonel_america.sql +11 -0
  51. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/0001_loud_siren.sql +27 -0
  52. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/meta/0000_snapshot.json +97 -0
  53. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/meta/0001_snapshot.json +354 -0
  54. package/template-vp-monorepo-react-nestjs/apps/server/drizzle/meta/_journal.json +20 -0
  55. package/template-vp-monorepo-react-nestjs/apps/server/package.json +4 -0
  56. package/template-vp-monorepo-react-nestjs/apps/server/src/app.module.ts +14 -2
  57. package/template-vp-monorepo-react-nestjs/apps/server/src/common/adapters/fastify.adapter.ts +6 -0
  58. package/template-vp-monorepo-react-nestjs/apps/server/src/config/index.ts +2 -0
  59. package/template-vp-monorepo-react-nestjs/apps/server/src/config/queue.config.ts +105 -0
  60. package/template-vp-monorepo-react-nestjs/apps/server/src/config/storage.config.ts +33 -0
  61. package/template-vp-monorepo-react-nestjs/apps/server/src/database/schema.ts +57 -1
  62. package/template-vp-monorepo-react-nestjs/apps/server/src/main.ts +1 -0
  63. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/auth/auth.service.ts +1 -3
  64. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/auth/dto/auth-repsponse.dto.ts +2 -2
  65. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/queue-worker.bootstrap.ts +24 -0
  66. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.constants.ts +24 -0
  67. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.contracts.ts +35 -0
  68. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.module.ts +56 -0
  69. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.processor.ts +62 -0
  70. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/task-queue/task-queue.service.ts +58 -0
  71. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/test/error-catelog.spec.ts +34 -0
  72. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/dto/initiate-mutipart-upload.dto.ts +26 -0
  73. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/dto/upload-params.dto.ts +15 -0
  74. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/storage/exact-size.transform.ts +66 -0
  75. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/storage/minio-storage.adapter.ts +239 -0
  76. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/storage/storage.port.ts +61 -0
  77. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.constants.ts +23 -0
  78. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.controller.ts +109 -0
  79. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.errors.ts +80 -0
  80. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.module.ts +20 -0
  81. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.reponsitory.ts +166 -0
  82. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/upload.service.ts +696 -0
  83. package/template-vp-monorepo-react-nestjs/apps/server/src/modules/upload/utils/object-key.ts +10 -0
  84. package/template-vp-monorepo-react-nestjs/apps/server/test/task-queue.integration.spec.ts +120 -0
  85. package/template-vp-monorepo-react-nestjs/apps/server/test/task-queue.processor.spec.ts +63 -0
  86. package/template-vp-monorepo-react-nestjs/apps/server/test/task-queue.service.spec.ts +106 -0
  87. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  88. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//345/274/271/347/252/227/347/273/204/344/273/266.md +4 -0
  89. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//346/226/207/344/273/266/346/213/206/345/210/206.md +4 -0
  90. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//347/273/204/344/273/266/345/221/275/345/220/215.md +3 -0
  91. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//350/241/250/346/240/274/351/241/265/351/235/242.md +11 -0
  92. package/template-vp-monorepo-react-nestjs/apps/web/.agents/rules//351/241/265/351/235/242/346/216/245/345/217/243.md +3 -0
  93. package/template-vp-monorepo-react-nestjs/apps/web/.env.dev +1 -1
  94. package/template-vp-monorepo-react-nestjs/apps/web/AGENTS.md +16 -0
  95. package/template-vp-monorepo-react-nestjs/apps/web/README.MD +153 -0
  96. package/template-vp-monorepo-react-nestjs/apps/web/index.html +3 -2
  97. package/template-vp-monorepo-react-nestjs/apps/web/package.json +6 -1
  98. package/template-vp-monorepo-react-nestjs/apps/web/public/favicon.svg +9 -1
  99. package/template-vp-monorepo-react-nestjs/apps/web/src/App.module.css +5 -0
  100. package/template-vp-monorepo-react-nestjs/apps/web/src/App.tsx +16 -9
  101. package/template-vp-monorepo-react-nestjs/apps/web/src/api/auth.ts +9 -0
  102. package/template-vp-monorepo-react-nestjs/apps/web/src/assets/svg/draft.svg +4 -0
  103. package/template-vp-monorepo-react-nestjs/apps/web/src/components/DraftProTable/DraftProTable.module.css +78 -0
  104. package/template-vp-monorepo-react-nestjs/apps/web/src/components/DraftProTable/DraftProTable.tsx +99 -0
  105. package/template-vp-monorepo-react-nestjs/apps/web/src/components/DraftProTable/README.md +34 -0
  106. package/template-vp-monorepo-react-nestjs/apps/web/src/components/FullHeightProTable/FullHeightProTable.module.css +54 -0
  107. package/template-vp-monorepo-react-nestjs/apps/web/src/components/FullHeightProTable/FullHeightProTable.tsx +41 -0
  108. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Icon/svg-icon/README.md +37 -0
  109. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  110. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Loading/PageLoading.module.css +7 -0
  111. package/template-vp-monorepo-react-nestjs/apps/web/src/components/Loading/PageLoading.tsx +2 -1
  112. package/template-vp-monorepo-react-nestjs/apps/web/src/config/theme.ts +20 -0
  113. package/template-vp-monorepo-react-nestjs/apps/web/src/layouts/BasicLayout.module.css +17 -0
  114. package/template-vp-monorepo-react-nestjs/apps/web/src/layouts/BasicLayout.tsx +106 -0
  115. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/i18n/index.module.css +23 -0
  116. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/i18n/index.tsx +53 -0
  117. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/components/ProjectFormDialog.tsx +132 -0
  118. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/config/columns.tsx +121 -0
  119. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/config/index.ts +77 -0
  120. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/components/DraftProjectFormDialog.tsx +140 -0
  121. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/config/columns.tsx +127 -0
  122. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/config/index.ts +39 -0
  123. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/config/projects.ts +48 -0
  124. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/index.module.css +49 -0
  125. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/draft/index.tsx +163 -0
  126. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/index.module.css +53 -0
  127. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/examples/pro-table/index.tsx +135 -0
  128. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/home/index.module.css +23 -0
  129. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/home/index.tsx +26 -43
  130. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/login/api.ts +11 -0
  131. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/login/index.tsx +183 -0
  132. package/template-vp-monorepo-react-nestjs/apps/web/src/pages/login/login.css +164 -0
  133. package/template-vp-monorepo-react-nestjs/apps/web/src/router/index.tsx +36 -12
  134. package/template-vp-monorepo-react-nestjs/apps/web/src/styles/index.css +11 -4
  135. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/request/core/index.ts +55 -47
  136. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/request/index.ts +10 -5
  137. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/storage/cookie.ts +50 -0
  138. package/template-vp-monorepo-react-nestjs/apps/web/src/utils/storage/session.ts +33 -0
  139. package/template-vp-monorepo-react-nestjs/apps/web/test/draft-projects.spec.ts +89 -0
  140. package/template-vp-monorepo-react-nestjs/apps/web/test/request.spec.ts +145 -0
  141. package/template-vp-monorepo-react-nestjs/apps/web/test/storage.spec.ts +108 -0
  142. package/template-vp-monorepo-react-nestjs/apps/web/test/tsconfig.json +7 -0
  143. package/template-vp-monorepo-react-nestjs/apps/web/tsconfig.json +1 -1
  144. package/template-vp-monorepo-react-nestjs/apps/web/vite.config.ts +7 -0
  145. package/template-vp-monorepo-react-nestjs/apps/web-vue/tsconfig.tsbuildinfo +1 -0
  146. package/template-vp-monorepo-react-nestjs/docker-compose.yml +19 -0
  147. package/template-vp-monorepo-react-nestjs/mise.toml +1 -1
  148. package/template-vp-monorepo-react-nestjs/package.json +1 -6
  149. package/template-vp-monorepo-react-nestjs/pnpm-workspace.yaml +9 -1
  150. package/template-vp-react/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  151. package/template-vp-react/AGENTS.md +11 -0
  152. package/template-vp-react/commitlint.config.js +0 -0
  153. package/template-vp-react/package.json +21 -26
  154. package/template-vp-react/pnpm-workspace.yaml +6 -0
  155. package/template-vp-react/src/assets/svg/draft.svg +4 -0
  156. package/template-vp-react/src/components/Icon/svg-icon/README.md +37 -0
  157. package/template-vp-react/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  158. package/template-vp-react/src/utils/request/index.ts +18 -5
  159. package/template-vp-react/tsconfig.json +2 -3
  160. package/template-vp-react/vite.config.ts +2 -0
  161. package/template-vp-react-shadcn/.agents/rules/SVG/344/275/277/347/224/250.md +8 -0
  162. package/template-vp-react-shadcn/.vscode/settings.json +2 -2
  163. package/template-vp-react-shadcn/{agents.md → AGENTS.md} +12 -2
  164. package/template-vp-react-shadcn/README.md +41 -0
  165. package/template-vp-react-shadcn/commitlint.config.js +1 -1
  166. package/template-vp-react-shadcn/components.json +1 -1
  167. package/template-vp-react-shadcn/package.json +25 -32
  168. package/template-vp-react-shadcn/pnpm-workspace.yaml +10 -0
  169. package/template-vp-react-shadcn/src/App.tsx +10 -10
  170. package/template-vp-react-shadcn/src/assets/svg/draft.svg +4 -0
  171. package/template-vp-react-shadcn/src/components/Icon/svg-icon/README.md +37 -0
  172. package/template-vp-react-shadcn/src/components/Icon/svg-icon/SvgIcon.tsx +27 -0
  173. package/template-vp-react-shadcn/src/components/Loading/PageLoading.tsx +4 -4
  174. package/template-vp-react-shadcn/src/components/Player/VideoJS/index.tsx +94 -78
  175. package/template-vp-react-shadcn/src/components/Player/index.ts +1 -1
  176. package/template-vp-react-shadcn/src/components/ui/button.tsx +25 -32
  177. package/template-vp-react-shadcn/src/components/ui/spinner.tsx +8 -7
  178. package/template-vp-react-shadcn/src/components/ui/toast.tsx +229 -0
  179. package/template-vp-react-shadcn/src/lib/utils.ts +3 -3
  180. package/template-vp-react-shadcn/src/main.tsx +5 -4
  181. package/template-vp-react-shadcn/src/pages/home/index.tsx +43 -35
  182. package/template-vp-react-shadcn/src/router/index.tsx +15 -15
  183. package/template-vp-react-shadcn/src/styles/index.css +4 -4
  184. package/template-vp-react-shadcn/src/styles/shadcn.css +2 -2
  185. package/template-vp-react-shadcn/src/utils/env/index.tsx +2 -2
  186. package/template-vp-react-shadcn/src/utils/request/alova-core/index.ts +102 -30
  187. package/template-vp-react-shadcn/src/utils/request/alova-core/utils.ts +5 -2
  188. package/template-vp-react-shadcn/src/utils/request/index.ts +32 -19
  189. package/template-vp-react-shadcn/src/utils/request/readme.md +41 -38
  190. package/template-vp-react-shadcn/tsconfig.json +2 -3
  191. package/template-vp-react-shadcn/vite.config.ts +29 -15
  192. package/template-vp-vue-eslint-vapor/package.json +26 -33
  193. package/template-vp-vue-eslint-vapor/pnpm-workspace.yaml +29 -1
  194. package/template-vue-vp-eslint/README.md +3 -10
  195. package/template-vue-vp-eslint/package.json +27 -34
  196. package/template-vue-vp-eslint/pnpm-workspace.yaml +27 -2
  197. package/template-vue-vp-eslint/src/layout/default/index.vue +4 -3
  198. package/dist/index.mjs +0 -422
  199. package/template-vp-monorepo-react-nestjs/.agents/auth.config.ts +0 -0
  200. package/template-vp-monorepo-react-nestjs/.spaces/server/todo.md +0 -991
  201. package/template-vp-monorepo-react-nestjs/apps/server/README.md +0 -3
  202. package/template-vp-react-shadcn/.oxfmtrc.jsonc +0 -8
  203. package/template-vp-react-shadcn/.oxlintrc.jsonc +0 -3
  204. package/template-vp-react-shadcn/src/components/ui/sonner.tsx +0 -43
@@ -0,0 +1,183 @@
1
+ import { AppstoreOutlined, ArrowRightOutlined, LockOutlined, UserOutlined } from '@ant-design/icons'
2
+ import { LoginForm, ProFormText } from '@ant-design/pro-components'
3
+ import { useRequest } from 'alova/client'
4
+ import { Alert } from 'antd'
5
+ import { useState } from 'react'
6
+ import { useNavigate } from 'react-router'
7
+ import type { LoginRequest } from 'shared/types'
8
+ import { ACCOUNT_PATTERN, normalizeAccount } from 'shared/utils'
9
+ import { cookie } from '@/utils/storage/cookie'
10
+ import { login } from './api'
11
+ import './login.css'
12
+
13
+ function ModuleDrawing() {
14
+ return (
15
+ <svg className="login-drawing" viewBox="0 0 540 360" fill="none" aria-hidden="true">
16
+ <g stroke="currentColor" strokeWidth="1.5">
17
+ <path d="M156 88h38a24 24 0 0 1 24 24v30M322 180h52a24 24 0 0 0 24-24v-28M218 218v38a24 24 0 0 1-24 24h-54M322 218v50a24 24 0 0 0 24 24h54" />
18
+ <path
19
+ d="M100 130v58a24 24 0 0 0 24 24h42M444 240v-28a24 24 0 0 0-24-24h-46"
20
+ strokeDasharray="4 7"
21
+ opacity=".35"
22
+ />
23
+ <g fill="var(--login-panel-bg)">
24
+ <rect x="52" y="46" width="104" height="84" rx="12" />
25
+ <rect x="354" y="48" width="104" height="80" rx="12" />
26
+ <rect x="60" y="244" width="80" height="72" rx="12" />
27
+ <rect x="396" y="248" width="88" height="68" rx="12" />
28
+ </g>
29
+ <path
30
+ d="M76 72h24v24H76zM112 72h20M112 84h12M112 96h20M378 74h56M378 88h36M378 102h46M80 264h40M80 278h24M80 292h32"
31
+ opacity=".7"
32
+ />
33
+ <circle cx="440" cy="282" r="14" />
34
+ <path d="m434 282 4 4 8-9" stroke="var(--login-accent)" />
35
+ <rect
36
+ x="218"
37
+ y="128"
38
+ width="104"
39
+ height="104"
40
+ rx="20"
41
+ fill="var(--login-panel-bg)"
42
+ stroke="var(--login-accent)"
43
+ />
44
+ <g stroke="var(--login-accent)" strokeWidth="2">
45
+ <rect x="244" y="154" width="20" height="20" rx="3" />
46
+ <rect x="276" y="154" width="20" height="20" rx="3" />
47
+ <rect x="244" y="186" width="20" height="20" rx="3" />
48
+ <rect x="276" y="186" width="20" height="20" rx="3" />
49
+ </g>
50
+ <g fill="var(--login-accent)" stroke="none">
51
+ <circle cx="194" cy="88" r="3" />
52
+ <circle cx="374" cy="180" r="3" />
53
+ <circle cx="218" cy="256" r="3" />
54
+ </g>
55
+ </g>
56
+ </svg>
57
+ )
58
+ }
59
+
60
+ export default function LoginPage() {
61
+ const navigate = useNavigate()
62
+ const { send, loading } = useRequest(login, { immediate: false })
63
+ const [error, setError] = useState<string | null>(null)
64
+
65
+ async function handleLogin(values: LoginRequest) {
66
+ if (loading) return false
67
+ setError(null)
68
+ try {
69
+ const result = await send({
70
+ account: normalizeAccount(values.account),
71
+ password: values.password,
72
+ })
73
+ cookie.set('token', result.accessToken, {
74
+ expires: new Date(result.absoluteExpiresAt),
75
+ })
76
+ void navigate('/home', { replace: true })
77
+ return true
78
+ } catch (cause) {
79
+ setError(cause instanceof Error ? cause.message : '登录失败,请稍后重试')
80
+ return false
81
+ }
82
+ }
83
+
84
+ return (
85
+ <main className="login-page">
86
+ <title>登录 - 通用平台</title>
87
+ <section className="login-brand-panel" aria-label="通用平台">
88
+ <div className="login-brand">
89
+ <AppstoreOutlined />
90
+ <span>通用平台</span>
91
+ </div>
92
+ <div className="login-story">
93
+ <p className="login-eyebrow">UNIVERSAL PLATFORM</p>
94
+ <h1>
95
+ 连接业务,
96
+ <br />
97
+ 从这里开始。
98
+ </h1>
99
+ <ModuleDrawing />
100
+ </div>
101
+ <div className="login-brand-footer">
102
+ <span>一个平台,多种可能</span>
103
+ <span>UNIVERSAL PLATFORM</span>
104
+ </div>
105
+ </section>
106
+
107
+ <section className="login-form-panel" aria-labelledby="login-title">
108
+ <div className="login-form-content">
109
+ <p className="login-form-eyebrow">WORKSPACE</p>
110
+ <h2 id="login-title">登录工作台</h2>
111
+ <p className="login-description">欢迎回来,请使用平台账号登录。</p>
112
+ <LoginForm<LoginRequest>
113
+ autoFocusFirstInput={false}
114
+ requiredMark={false}
115
+ contentStyle={{ width: '100%', minWidth: 0 }}
116
+ containerStyle={{ padding: 0, minWidth: 0 }}
117
+ onFinish={handleLogin}
118
+ onValuesChange={() => {
119
+ if (error) setError(null)
120
+ }}
121
+ submitter={{
122
+ searchConfig: { submitText: '登录' },
123
+ submitButtonProps: {
124
+ size: 'large',
125
+ loading,
126
+ icon: <ArrowRightOutlined />,
127
+ iconPlacement: 'end',
128
+ },
129
+ }}
130
+ >
131
+ {error && (
132
+ <Alert className="login-error" type="error" showIcon title={error} role="alert" />
133
+ )}
134
+ <ProFormText
135
+ name="account"
136
+ label="账号"
137
+ placeholder="请输入账号"
138
+ fieldProps={{
139
+ prefix: <UserOutlined />,
140
+ autoComplete: 'username',
141
+ maxLength: 32,
142
+ size: 'large',
143
+ autoCapitalize: 'none',
144
+ spellCheck: false,
145
+ }}
146
+ rules={[
147
+ { required: true, whitespace: true, message: '请输入账号' },
148
+ {
149
+ min: 4,
150
+ max: 32,
151
+ transform: (value: string) => value?.trim(),
152
+ message: '账号长度为 4–32 位',
153
+ },
154
+ {
155
+ pattern: ACCOUNT_PATTERN,
156
+ transform: (value: string) => value?.trim(),
157
+ message: '账号仅支持字母、数字和下划线',
158
+ },
159
+ ]}
160
+ />
161
+ <ProFormText.Password
162
+ name="password"
163
+ label="密码"
164
+ placeholder="请输入密码"
165
+ fieldProps={{
166
+ prefix: <LockOutlined />,
167
+ autoComplete: 'current-password',
168
+ maxLength: 128,
169
+ size: 'large',
170
+ }}
171
+ rules={[
172
+ { required: true, message: '请输入密码' },
173
+ { max: 128, message: '密码不能超过 128 位' },
174
+ ]}
175
+ />
176
+ </LoginForm>
177
+ <p className="login-account-hint">请使用已开通的账号,账号区分于显示名称。</p>
178
+ </div>
179
+ <footer className="login-footer">通用平台 · 业务工作空间</footer>
180
+ </section>
181
+ </main>
182
+ )
183
+ }
@@ -0,0 +1,164 @@
1
+ @layer components {
2
+ .login-page {
3
+ --login-panel-bg: #102f40;
4
+ --login-accent: #7dd4ce;
5
+ display: grid;
6
+ grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
7
+ min-height: 100vh;
8
+ min-height: 100svh;
9
+ background: #fff;
10
+ }
11
+
12
+ .login-brand-panel {
13
+ display: flex;
14
+ flex-direction: column;
15
+ justify-content: space-between;
16
+ padding: 40px 56px 28px;
17
+ overflow: hidden;
18
+ color: #e3f1f4;
19
+ background-color: var(--login-panel-bg);
20
+ background-image:
21
+ linear-gradient(#ffffff03 1px, transparent 1px),
22
+ linear-gradient(90deg, #ffffff03 1px, transparent 1px);
23
+ background-size: 32px 32px;
24
+ }
25
+
26
+ .login-brand {
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 12px;
30
+ font-size: 20px;
31
+ font-weight: 600;
32
+ letter-spacing: 0.06em;
33
+ }
34
+
35
+ .login-brand > .anticon {
36
+ color: var(--login-accent);
37
+ font-size: 28px;
38
+ }
39
+
40
+ .login-story {
41
+ width: 100%;
42
+ max-width: 540px;
43
+ margin: 40px auto 24px;
44
+ }
45
+
46
+ .login-eyebrow,
47
+ .login-form-eyebrow {
48
+ font-family: Bahnschrift, 'Segoe UI', sans-serif;
49
+ font-size: 11px;
50
+ letter-spacing: 0.16em;
51
+ }
52
+
53
+ .login-eyebrow {
54
+ margin-bottom: 20px;
55
+ color: #a8c4cc;
56
+ }
57
+ .login-story h1 {
58
+ margin: 0;
59
+ font-size: clamp(32px, 3.2vw, 48px);
60
+ font-weight: 500;
61
+ line-height: 1.5;
62
+ letter-spacing: 0.06em;
63
+ }
64
+ .login-drawing {
65
+ display: block;
66
+ width: 100%;
67
+ max-height: 310px;
68
+ margin-top: 20px;
69
+ color: #6d9cae;
70
+ }
71
+
72
+ .login-brand-footer {
73
+ display: flex;
74
+ justify-content: space-between;
75
+ gap: 20px;
76
+ color: #a8c4cc;
77
+ font-size: 11px;
78
+ letter-spacing: 0.08em;
79
+ }
80
+
81
+ .login-form-panel {
82
+ position: relative;
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ padding: 88px 40px 112px;
87
+ }
88
+ .login-form-content {
89
+ width: 100%;
90
+ max-width: 368px;
91
+ }
92
+ .login-form-eyebrow {
93
+ margin: 0 0 14px;
94
+ color: var(--ant-color-primary);
95
+ }
96
+ .login-form-content h2 {
97
+ margin: 0 0 12px;
98
+ font-size: 30px;
99
+ font-weight: 600;
100
+ letter-spacing: 0.02em;
101
+ }
102
+ .login-description {
103
+ margin: 0 0 36px;
104
+ color: var(--ant-color-text-secondary);
105
+ }
106
+ .login-error {
107
+ margin-bottom: 24px;
108
+ }
109
+ .login-form-content .ant-form-item {
110
+ margin-bottom: 24px;
111
+ }
112
+ .login-form-content .ant-input-prefix {
113
+ margin-inline-end: 10px;
114
+ color: var(--ant-color-text-tertiary);
115
+ }
116
+ .login-form-content .ant-btn-primary {
117
+ height: 44px;
118
+ margin-top: 8px;
119
+ box-shadow: none;
120
+ }
121
+ .login-account-hint {
122
+ margin: 24px 0 0;
123
+ color: var(--ant-color-text-secondary);
124
+ font-size: 12px;
125
+ text-align: center;
126
+ }
127
+ .login-footer {
128
+ position: absolute;
129
+ bottom: 28px;
130
+ inset-inline: 24px;
131
+ color: var(--ant-color-text-tertiary);
132
+ font-size: 12px;
133
+ text-align: center;
134
+ }
135
+ }
136
+
137
+ @media (max-width: 1023px) {
138
+ .login-brand-panel {
139
+ padding-inline: 32px;
140
+ }
141
+ }
142
+
143
+ @media (max-width: 767px) {
144
+ .login-page {
145
+ grid-template-columns: 1fr;
146
+ grid-template-rows: auto 1fr;
147
+ }
148
+ .login-brand-panel {
149
+ padding: 24px;
150
+ }
151
+ .login-brand {
152
+ font-size: 18px;
153
+ }
154
+ .login-story,
155
+ .login-brand-footer {
156
+ display: none;
157
+ }
158
+ .login-form-panel {
159
+ padding: 52px 24px 92px;
160
+ }
161
+ .login-form-content h2 {
162
+ font-size: 28px;
163
+ }
164
+ }
@@ -1,20 +1,44 @@
1
- import { createBrowserRouter, Navigate, type RouteObject } from 'react-router'
1
+ import { Button, Result } from 'antd'
2
+ import { lazy } from 'react'
3
+ import { createBrowserRouter, Link, Navigate, type RouteObject } from 'react-router'
4
+ import BasicLayout from '@/layouts/BasicLayout'
2
5
 
3
- export const lazyLoad = (path: string, fileName = 'index') => {
4
- const Module = lazy(() => import(`@/pages/${path}/${fileName}.tsx`))
5
- return <Module />
6
- }
6
+ const Home = lazy(() => import('@/pages/home'))
7
+ const I18nExample = lazy(() => import('@/pages/examples/i18n'))
8
+ const ProTableExample = lazy(() => import('@/pages/examples/pro-table'))
9
+ const ProTableDraftExample = lazy(() => import('@/pages/examples/pro-table/draft'))
10
+ const Login = lazy(() => import('@/pages/login'))
7
11
 
8
12
  const routes: RouteObject[] = [
9
13
  {
10
- path: '/',
11
- id: 'index',
12
- element: <Navigate to={'/home'} />,
14
+ path: '/login',
15
+ element: <Login />,
13
16
  },
14
17
  {
15
- path: '/home',
16
- id: 'home',
17
- element: lazyLoad('home'),
18
+ path: '/',
19
+ element: <BasicLayout />,
20
+ children: [
21
+ { index: true, element: <Navigate to="/home" replace /> },
22
+ { path: 'home', element: <Home /> },
23
+ { path: 'examples/pro-table', element: <ProTableExample /> },
24
+ { path: 'examples/pro-table/draft', element: <ProTableDraftExample /> },
25
+ { path: 'examples/i18n', element: <I18nExample /> },
26
+ {
27
+ path: '*',
28
+ element: (
29
+ <Result
30
+ status="404"
31
+ title="404"
32
+ subTitle="页面不存在,请检查地址或返回工作台。"
33
+ extra={
34
+ <Link to="/home">
35
+ <Button type="primary">返回工作台</Button>
36
+ </Link>
37
+ }
38
+ />
39
+ ),
40
+ },
41
+ ],
18
42
  },
19
43
  ]
20
44
 
@@ -22,4 +46,4 @@ export const router = createBrowserRouter(routes, {
22
46
  basename: '/',
23
47
  })
24
48
 
25
- export const navigator = router.navigate
49
+ export const navigator = router.navigate.bind(router)
@@ -1,7 +1,14 @@
1
+ @layer theme, base, antd, antd-pro, components, utilities;
2
+
3
+ @import 'antd/dist/reset.css' layer(base);
1
4
  @import 'tailwindcss';
2
5
 
3
- html,
4
- body {
5
- margin: 0;
6
- padding: 0;
6
+ @layer base {
7
+ html,
8
+ body,
9
+ #root {
10
+ min-width: 320px;
11
+ min-height: 100%;
12
+ margin: 0;
13
+ }
7
14
  }
@@ -1,3 +1,4 @@
1
+ import { axiosRequestAdapter, type AlovaAxiosRequestConfig } from '@alova/adapter-axios'
1
2
  import type {
2
3
  AlovaGlobalCacheAdapter,
3
4
  AlovaOptions,
@@ -7,8 +8,7 @@ import type {
7
8
  StatesHook,
8
9
  } from 'alova'
9
10
  import { createAlova } from 'alova'
10
- import type { FetchRequestInit } from 'alova/fetch'
11
- import adapterFetch from 'alova/fetch'
11
+ import type { AxiosResponse, AxiosResponseHeaders } from 'axios'
12
12
  import { deepMergeObject, isPlainObject, isReadableStream, tryParseJsonString } from './utils.ts'
13
13
 
14
14
  type MaybePromise<T> = T | Promise<T>
@@ -23,7 +23,6 @@ type StatusMatcher<RE> = number | number[] | ((status: number, response: RE) =>
23
23
  type CodeMatcher = Array<number | string>
24
24
 
25
25
  export interface StatusMap<RE = unknown> {
26
- success?: StatusMatcher<RE>
27
26
  unAuthorized?: StatusMatcher<RE>
28
27
  }
29
28
 
@@ -40,9 +39,9 @@ export interface RequestMeta {
40
39
  }
41
40
 
42
41
  export interface BaseRequestOption<
43
- RC extends object = FetchRequestInit,
44
- RE = Response,
45
- RH = Headers,
42
+ RC extends object = AlovaAxiosRequestConfig,
43
+ RE = AxiosResponse,
44
+ RH = AxiosResponseHeaders,
46
45
  SE extends StatesExport<any> = StatesExport<any>,
47
46
  > extends RequestMeta {
48
47
  baseUrl?: string
@@ -60,7 +59,8 @@ export interface BaseRequestOption<
60
59
  errorDefaultMessage?: string
61
60
  successMessageFunc?: (message: string) => void
62
61
  errorMessageFunc?: (message: string) => void
63
- unAuthorizedResponseFunc?: () => void
62
+ unAuthorizedResponseFunc?: (response: RE) => void
63
+ /** 默认使用 Axios;自定义适配器也须拒绝 HTTP 错误,并通过 error.response 提供响应。 */
64
64
  requestAdapter?: AlovaRequestAdapter<RC, RE, RH>
65
65
  l1Cache?: AlovaGlobalCacheAdapter
66
66
  l2Cache?: AlovaGlobalCacheAdapter
@@ -68,16 +68,16 @@ export interface BaseRequestOption<
68
68
  }
69
69
 
70
70
  export type baseRequestOption<
71
- RC extends object = FetchRequestInit,
72
- RE = Response,
73
- RH = Headers,
71
+ RC extends object = AlovaAxiosRequestConfig,
72
+ RE = AxiosResponse,
73
+ RH = AxiosResponseHeaders,
74
74
  SE extends StatesExport<any> = StatesExport<any>,
75
75
  > = BaseRequestOption<RC, RE, RH, SE>
76
76
 
77
77
  export type RequestOption<
78
- RC extends object = FetchRequestInit,
79
- RE = Response,
80
- RH = Headers,
78
+ RC extends object = AlovaAxiosRequestConfig,
79
+ RE = AxiosResponse,
80
+ RH = AxiosResponseHeaders,
81
81
  SE extends StatesExport<any> = StatesExport<any>,
82
82
  > = BaseRequestOption<RC, RE, RH, SE>
83
83
 
@@ -119,7 +119,6 @@ const defaultRequestOption: BaseRequestOption<any, any, any, any> = {
119
119
  baseUrl: '/',
120
120
  timeout: undefined,
121
121
  statusMap: {
122
- success: 200,
123
122
  unAuthorized: 401,
124
123
  },
125
124
  codeMap: {
@@ -137,7 +136,7 @@ const defaultRequestOption: BaseRequestOption<any, any, any, any> = {
137
136
  errorDefaultMessage: DEFAULT_ERROR_MESSAGE,
138
137
  cacheFor: null,
139
138
  cacheLogger: true,
140
- requestAdapter: adapterFetch() as AlovaRequestAdapter<any, any, any>,
139
+ requestAdapter: axiosRequestAdapter(),
141
140
  }
142
141
 
143
142
  function getMethodMeta(method: unknown): RequestMeta {
@@ -155,7 +154,7 @@ function isMatchedStatus<RE>(
155
154
  matcher: StatusMatcher<RE> | undefined,
156
155
  response: RE,
157
156
  ): boolean {
158
- if (matcher === undefined) return status >= 200 && status < 300
157
+ if (matcher === undefined) return false
159
158
 
160
159
  if (typeof matcher === 'function') return matcher(status, response)
161
160
 
@@ -283,13 +282,21 @@ function resolveConfig<RC extends object, RE, RH, SE extends StatesExport<any>>(
283
282
  }
284
283
 
285
284
  export function createInstance<
286
- RC extends object = FetchRequestInit,
287
- RE = Response,
288
- RH = Headers,
285
+ RC extends object = AlovaAxiosRequestConfig,
286
+ RE = AxiosResponse,
287
+ RH = AxiosResponseHeaders,
289
288
  SE extends StatesExport<any> = StatesExport<any>,
290
289
  >(option: RequestOption<RC, RE, RH, SE> = {}) {
291
290
  const config = resolveConfig(option)
292
291
 
292
+ function responseError(data: unknown, status: number) {
293
+ const code = getResponseField(data, config.responseCodeKey)
294
+ return Object.assign(
295
+ new Error(getResponseMessage(data, config.responseMessageKey, config.errorDefaultMessage)),
296
+ { status, code: typeof code === 'string' || typeof code === 'number' ? code : undefined },
297
+ )
298
+ }
299
+
293
300
  const alovaOptions: AlovaOptions<RequestAlovaGenerics<RC, RE, RH, SE>> = {
294
301
  baseURL: config.baseUrl,
295
302
  timeout: config.timeout,
@@ -310,6 +317,7 @@ export function createInstance<
310
317
  }
311
318
  },
312
319
  responded: {
320
+ // HTTP 成败由适配器判断;这里只解析成功响应并检查业务 code。
313
321
  onSuccess: async (response, method) => {
314
322
  const meta = getMethodMeta(method)
315
323
  const shouldTransform = getMetaFlag(meta, 'isTransformResponse', config.isTransformResponse)
@@ -322,18 +330,6 @@ export function createInstance<
322
330
  const status = getResponseStatus(response)
323
331
  const data = await getResponseData(response)
324
332
 
325
- if (!isMatchedStatus(status, config.statusMap.success, response)) {
326
- if (isMatchedStatus(status, config.statusMap.unAuthorized, response))
327
- config.unAuthorizedResponseFunc?.()
328
-
329
- if (showError) {
330
- config.errorMessageFunc?.(
331
- getResponseMessage(data, config.responseMessageKey, config.errorDefaultMessage),
332
- )
333
- }
334
- return Promise.reject(response)
335
- }
336
-
337
333
  if (!isWrapped) {
338
334
  if (showSuccess) config.successMessageFunc?.(config.successDefaultMessage)
339
335
  return data
@@ -348,30 +344,42 @@ export function createInstance<
348
344
  )
349
345
 
350
346
  if (!isMatchedCode(code, config.codeMap.success)) {
351
- if (isMatchedCode(code, config.codeMap.unAuthorized)) config.unAuthorizedResponseFunc?.()
347
+ if (isMatchedCode(code, config.codeMap.unAuthorized))
348
+ config.unAuthorizedResponseFunc?.(response)
352
349
 
353
350
  if (showError) {
354
351
  config.errorMessageFunc?.(
355
352
  getResponseMessage(data, config.responseMessageKey, config.errorDefaultMessage),
356
353
  )
357
354
  }
358
- return Promise.reject(response)
355
+ throw responseError(data, status)
359
356
  }
360
357
 
361
358
  if (showSuccess) config.successMessageFunc?.(responseMessage)
362
359
 
363
360
  return responseData
364
361
  },
365
- onError: (error, method) => {
362
+ // 统一处理 HTTP 错误、网络故障和超时;onSuccess 抛出的业务错误直接传给调用方。
363
+ onError: async (error, method) => {
366
364
  const meta = getMethodMeta(method)
367
365
  const showError = getMetaFlag(meta, 'isShowErrorMessage', config.isShowErrorMessage)
366
+ const response = (error as { response?: RE } | undefined)?.response
367
+ let failure: Error
368
+
369
+ if (response) {
370
+ const status = getResponseStatus(response)
371
+ if (isMatchedStatus(status, config.statusMap.unAuthorized, response)) {
372
+ config.unAuthorizedResponseFunc?.(response)
373
+ }
374
+ failure = responseError(await getResponseData(response), status)
375
+ } else {
376
+ failure = new Error('无法连接服务,请检查网络后重试')
377
+ }
368
378
 
369
379
  if (showError) {
370
- config.errorMessageFunc?.(
371
- error instanceof Error ? error.message : config.errorDefaultMessage,
372
- )
380
+ config.errorMessageFunc?.(failure.message)
373
381
  }
374
- return Promise.reject(error)
382
+ throw failure
375
383
  },
376
384
  },
377
385
  }
@@ -380,24 +388,24 @@ export function createInstance<
380
388
  }
381
389
 
382
390
  export type RequestInstance<
383
- RC extends object = FetchRequestInit,
384
- RE = Response,
385
- RH = Headers,
391
+ RC extends object = AlovaAxiosRequestConfig,
392
+ RE = AxiosResponse,
393
+ RH = AxiosResponseHeaders,
386
394
  SE extends StatesExport<any> = StatesExport<any>,
387
395
  > = ReturnType<typeof createInstance<RC, RE, RH, SE>>
388
396
 
389
397
  export type DualCallInstance<
390
- RC extends object = FetchRequestInit,
391
- RE = Response,
392
- RH = Headers,
398
+ RC extends object = AlovaAxiosRequestConfig,
399
+ RE = AxiosResponse,
400
+ RH = AxiosResponseHeaders,
393
401
  SE extends StatesExport<any> = StatesExport<any>,
394
402
  > = RequestInstance<RC, RE, RH, SE> &
395
403
  ((option?: RequestOption<RC, RE, RH, SE>) => RequestInstance<RC, RE, RH, SE>)
396
404
 
397
405
  export function createDualCallInstance<
398
- RC extends object = FetchRequestInit,
399
- RE = Response,
400
- RH = Headers,
406
+ RC extends object = AlovaAxiosRequestConfig,
407
+ RE = AxiosResponse,
408
+ RH = AxiosResponseHeaders,
401
409
  SE extends StatesExport<any> = StatesExport<any>,
402
410
  >(baseConfig: BaseRequestOption<RC, RE, RH, SE>): DualCallInstance<RC, RE, RH, SE> {
403
411
  const defaultInstance = createInstance(baseConfig)