@qlover/create-app 1.0.2 → 2.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.
Files changed (556) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/index.cjs +9 -9
  3. package/dist/index.js +9 -9
  4. package/package.json +4 -3
  5. package/dist/configs/_common/.editorconfig +0 -23
  6. package/dist/configs/_common/.env.template +0 -13
  7. package/dist/configs/_common/.gitattributes +0 -2
  8. package/dist/configs/_common/.github/workflows/general-check.yml +0 -41
  9. package/dist/configs/_common/.github/workflows/release.yml +0 -81
  10. package/dist/configs/_common/.gitignore.template +0 -64
  11. package/dist/configs/_common/.husky/commit-msg +0 -3
  12. package/dist/configs/_common/.husky/pre-commit +0 -3
  13. package/dist/configs/_common/.prettierignore +0 -17
  14. package/dist/configs/_common/.prettierrc.js +0 -7
  15. package/dist/configs/_common/.vscode/extensions.json +0 -9
  16. package/dist/configs/_common/.vscode/react.code-snippets +0 -19
  17. package/dist/configs/_common/.vscode/settings.json +0 -16
  18. package/dist/configs/_common/commitlint.config.js +0 -10
  19. package/dist/configs/_common/package.json.template +0 -75
  20. package/dist/configs/node-lib/eslint.config.js +0 -50
  21. package/dist/templates/next-app/.env.template +0 -25
  22. package/dist/templates/next-app/.prettierignore +0 -58
  23. package/dist/templates/next-app/README.en.md +0 -130
  24. package/dist/templates/next-app/README.md +0 -130
  25. package/dist/templates/next-app/config/IOCIdentifier.ts +0 -74
  26. package/dist/templates/next-app/config/Identifier/api.ts +0 -41
  27. package/dist/templates/next-app/config/Identifier/common/admint.table.ts +0 -69
  28. package/dist/templates/next-app/config/Identifier/common/common.ts +0 -90
  29. package/dist/templates/next-app/config/Identifier/common/index.ts +0 -3
  30. package/dist/templates/next-app/config/Identifier/common/validators.ts +0 -34
  31. package/dist/templates/next-app/config/Identifier/index.ts +0 -3
  32. package/dist/templates/next-app/config/Identifier/pages/index.ts +0 -7
  33. package/dist/templates/next-app/config/Identifier/pages/page.about.ts +0 -20
  34. package/dist/templates/next-app/config/Identifier/pages/page.admin.home.ts +0 -27
  35. package/dist/templates/next-app/config/Identifier/pages/page.admin.locales.ts +0 -266
  36. package/dist/templates/next-app/config/Identifier/pages/page.admin.user.ts +0 -293
  37. package/dist/templates/next-app/config/Identifier/pages/page.home.ts +0 -56
  38. package/dist/templates/next-app/config/Identifier/pages/page.login.ts +0 -159
  39. package/dist/templates/next-app/config/Identifier/pages/page.register.ts +0 -177
  40. package/dist/templates/next-app/config/adminNavs.ts +0 -19
  41. package/dist/templates/next-app/config/common.ts +0 -43
  42. package/dist/templates/next-app/config/cookies.ts +0 -23
  43. package/dist/templates/next-app/config/i18n/AboutI18n.ts +0 -14
  44. package/dist/templates/next-app/config/i18n/HomeI18n.ts +0 -24
  45. package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +0 -51
  46. package/dist/templates/next-app/config/i18n/admin18n.ts +0 -75
  47. package/dist/templates/next-app/config/i18n/i18nConfig.ts +0 -16
  48. package/dist/templates/next-app/config/i18n/i18nKeyScheam.ts +0 -36
  49. package/dist/templates/next-app/config/i18n/index.ts +0 -7
  50. package/dist/templates/next-app/config/i18n/loginI18n.ts +0 -50
  51. package/dist/templates/next-app/config/i18n/register18n.ts +0 -44
  52. package/dist/templates/next-app/config/route.ts +0 -9
  53. package/dist/templates/next-app/config/theme.ts +0 -28
  54. package/dist/templates/next-app/docs/en/api.md +0 -387
  55. package/dist/templates/next-app/docs/en/component.md +0 -544
  56. package/dist/templates/next-app/docs/en/database.md +0 -496
  57. package/dist/templates/next-app/docs/en/development-guide.md +0 -727
  58. package/dist/templates/next-app/docs/en/env.md +0 -563
  59. package/dist/templates/next-app/docs/en/i18n.md +0 -287
  60. package/dist/templates/next-app/docs/en/index.md +0 -165
  61. package/dist/templates/next-app/docs/en/page.md +0 -457
  62. package/dist/templates/next-app/docs/en/project-structure.md +0 -176
  63. package/dist/templates/next-app/docs/en/router.md +0 -427
  64. package/dist/templates/next-app/docs/en/theme.md +0 -532
  65. package/dist/templates/next-app/docs/en/validator.md +0 -478
  66. package/dist/templates/next-app/docs/zh/api.md +0 -387
  67. package/dist/templates/next-app/docs/zh/component.md +0 -544
  68. package/dist/templates/next-app/docs/zh/database.md +0 -496
  69. package/dist/templates/next-app/docs/zh/development-guide.md +0 -727
  70. package/dist/templates/next-app/docs/zh/env.md +0 -563
  71. package/dist/templates/next-app/docs/zh/i18n.md +0 -287
  72. package/dist/templates/next-app/docs/zh/index.md +0 -165
  73. package/dist/templates/next-app/docs/zh/page.md +0 -457
  74. package/dist/templates/next-app/docs/zh/project-structure.md +0 -176
  75. package/dist/templates/next-app/docs/zh/router.md +0 -427
  76. package/dist/templates/next-app/docs/zh/theme.md +0 -532
  77. package/dist/templates/next-app/docs/zh/validator.md +0 -476
  78. package/dist/templates/next-app/eslint.config.mjs +0 -285
  79. package/dist/templates/next-app/make/generateLocales.ts +0 -32
  80. package/dist/templates/next-app/migrations/schema/LocalesSchema.ts +0 -15
  81. package/dist/templates/next-app/migrations/schema/UserSchema.ts +0 -38
  82. package/dist/templates/next-app/migrations/sql/1694244000000.sql +0 -21
  83. package/dist/templates/next-app/next.config.ts +0 -25
  84. package/dist/templates/next-app/package.json +0 -87
  85. package/dist/templates/next-app/postcss.config.mjs +0 -5
  86. package/dist/templates/next-app/public/favicon.ico +0 -0
  87. package/dist/templates/next-app/public/file.svg +0 -1
  88. package/dist/templates/next-app/public/globe.svg +0 -1
  89. package/dist/templates/next-app/public/locales/en.json +0 -182
  90. package/dist/templates/next-app/public/locales/zh.json +0 -182
  91. package/dist/templates/next-app/public/next.svg +0 -1
  92. package/dist/templates/next-app/public/vercel.svg +0 -1
  93. package/dist/templates/next-app/public/window.svg +0 -1
  94. package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +0 -37
  95. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +0 -42
  96. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +0 -153
  97. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +0 -20
  98. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +0 -67
  99. package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +0 -18
  100. package/dist/templates/next-app/src/app/[locale]/auth/login/LoginForm.tsx +0 -126
  101. package/dist/templates/next-app/src/app/[locale]/auth/login/page.tsx +0 -90
  102. package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +0 -8
  103. package/dist/templates/next-app/src/app/[locale]/auth/register/RegisterForm.tsx +0 -197
  104. package/dist/templates/next-app/src/app/[locale]/auth/register/page.tsx +0 -90
  105. package/dist/templates/next-app/src/app/[locale]/layout.tsx +0 -63
  106. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +0 -24
  107. package/dist/templates/next-app/src/app/[locale]/page.tsx +0 -98
  108. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +0 -13
  109. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +0 -13
  110. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +0 -20
  111. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +0 -13
  112. package/dist/templates/next-app/src/app/api/admin/users/route.ts +0 -20
  113. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +0 -32
  114. package/dist/templates/next-app/src/app/api/auth/callback/route.ts +0 -11
  115. package/dist/templates/next-app/src/app/api/callback/route.ts +0 -49
  116. package/dist/templates/next-app/src/app/api/locales/json/route.ts +0 -33
  117. package/dist/templates/next-app/src/app/api/user/login/route.ts +0 -10
  118. package/dist/templates/next-app/src/app/api/user/logout/route.ts +0 -8
  119. package/dist/templates/next-app/src/app/api/user/register/route.ts +0 -11
  120. package/dist/templates/next-app/src/app/manifest.ts +0 -16
  121. package/dist/templates/next-app/src/app/robots.txt +0 -2
  122. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +0 -28
  123. package/dist/templates/next-app/src/base/cases/AppConfig.ts +0 -40
  124. package/dist/templates/next-app/src/base/cases/Datetime.ts +0 -18
  125. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +0 -57
  126. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -116
  127. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +0 -45
  128. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +0 -32
  129. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +0 -77
  130. package/dist/templates/next-app/src/base/cases/ResourceState.ts +0 -17
  131. package/dist/templates/next-app/src/base/cases/RouterService.ts +0 -52
  132. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -73
  133. package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +0 -53
  134. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -212
  135. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -26
  136. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +0 -36
  137. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +0 -27
  138. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -25
  139. package/dist/templates/next-app/src/base/port/IOCInterface.ts +0 -33
  140. package/dist/templates/next-app/src/base/port/RouterInterface.ts +0 -11
  141. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +0 -25
  142. package/dist/templates/next-app/src/base/port/ZodBuilderInterface.ts +0 -8
  143. package/dist/templates/next-app/src/base/services/AdminPageEvent.ts +0 -26
  144. package/dist/templates/next-app/src/base/services/AdminPageScheduler.ts +0 -42
  145. package/dist/templates/next-app/src/base/services/AppApiRequester.ts +0 -67
  146. package/dist/templates/next-app/src/base/services/AppUserGateway.ts +0 -110
  147. package/dist/templates/next-app/src/base/services/I18nService.ts +0 -87
  148. package/dist/templates/next-app/src/base/services/ResourceService.ts +0 -139
  149. package/dist/templates/next-app/src/base/services/UserService.ts +0 -68
  150. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +0 -106
  151. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +0 -87
  152. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +0 -110
  153. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +0 -52
  154. package/dist/templates/next-app/src/base/types/AppPageRouter.ts +0 -12
  155. package/dist/templates/next-app/src/base/types/PagesRouter.ts +0 -9
  156. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +0 -76
  157. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +0 -125
  158. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -50
  159. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  160. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +0 -18
  161. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +0 -68
  162. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -100
  163. package/dist/templates/next-app/src/core/globals.ts +0 -28
  164. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +0 -80
  165. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +0 -66
  166. package/dist/templates/next-app/src/i18n/loadMessages.ts +0 -103
  167. package/dist/templates/next-app/src/i18n/request.ts +0 -31
  168. package/dist/templates/next-app/src/i18n/routing.ts +0 -35
  169. package/dist/templates/next-app/src/lib/supabase/client.ts +0 -8
  170. package/dist/templates/next-app/src/lib/supabase/conts.ts +0 -2
  171. package/dist/templates/next-app/src/lib/supabase/proxy.ts +0 -84
  172. package/dist/templates/next-app/src/lib/supabase/server.ts +0 -38
  173. package/dist/templates/next-app/src/pages/[locale]/about.tsx +0 -61
  174. package/dist/templates/next-app/src/pages/_app.tsx +0 -50
  175. package/dist/templates/next-app/src/pages/_document.tsx +0 -13
  176. package/dist/templates/next-app/src/proxy.ts +0 -33
  177. package/dist/templates/next-app/src/server/AppErrorApi.ts +0 -10
  178. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +0 -110
  179. package/dist/templates/next-app/src/server/AppSuccessApi.ts +0 -7
  180. package/dist/templates/next-app/src/server/NextApiServer.ts +0 -61
  181. package/dist/templates/next-app/src/server/PagesRouteParams.ts +0 -145
  182. package/dist/templates/next-app/src/server/PasswordEncrypt.ts +0 -18
  183. package/dist/templates/next-app/src/server/ServerAuth.ts +0 -81
  184. package/dist/templates/next-app/src/server/SupabaseBridge.ts +0 -262
  185. package/dist/templates/next-app/src/server/UserCredentialToken.ts +0 -53
  186. package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +0 -86
  187. package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +0 -42
  188. package/dist/templates/next-app/src/server/controllers/LocalesController.ts +0 -36
  189. package/dist/templates/next-app/src/server/controllers/UserController.ts +0 -91
  190. package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +0 -8
  191. package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +0 -21
  192. package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +0 -11
  193. package/dist/templates/next-app/src/server/port/CrentialTokenInterface.ts +0 -5
  194. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +0 -36
  195. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +0 -12
  196. package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +0 -10
  197. package/dist/templates/next-app/src/server/port/LocalesRepositoryInterface.ts +0 -43
  198. package/dist/templates/next-app/src/server/port/PaginationInterface.ts +0 -6
  199. package/dist/templates/next-app/src/server/port/RouteParamsnHandlerInterface.ts +0 -18
  200. package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +0 -15
  201. package/dist/templates/next-app/src/server/port/ServerInterface.ts +0 -23
  202. package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +0 -15
  203. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +0 -14
  204. package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +0 -23
  205. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -216
  206. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -102
  207. package/dist/templates/next-app/src/server/services/AIService.ts +0 -45
  208. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +0 -21
  209. package/dist/templates/next-app/src/server/services/AdminLocalesService.ts +0 -20
  210. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +0 -137
  211. package/dist/templates/next-app/src/server/services/ApiUserService.ts +0 -29
  212. package/dist/templates/next-app/src/server/services/UserService.ts +0 -134
  213. package/dist/templates/next-app/src/server/validators/ExtendedExecutorError.ts +0 -6
  214. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -145
  215. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -82
  216. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +0 -70
  217. package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +0 -68
  218. package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  219. package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  220. package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +0 -3
  221. package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  222. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +0 -4
  223. package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  224. package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  225. package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +0 -3
  226. package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  227. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +0 -34
  228. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  229. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  230. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  231. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  232. package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +0 -44
  233. package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +0 -43
  234. package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +0 -3
  235. package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +0 -43
  236. package/dist/templates/next-app/src/styles/css/index.css +0 -6
  237. package/dist/templates/next-app/src/styles/css/page.css +0 -20
  238. package/dist/templates/next-app/src/styles/css/scrollbar.css +0 -34
  239. package/dist/templates/next-app/src/styles/css/tailwind.css +0 -5
  240. package/dist/templates/next-app/src/styles/css/themes/_default.css +0 -30
  241. package/dist/templates/next-app/src/styles/css/themes/dark.css +0 -30
  242. package/dist/templates/next-app/src/styles/css/themes/index.css +0 -3
  243. package/dist/templates/next-app/src/styles/css/themes/pink.css +0 -30
  244. package/dist/templates/next-app/src/styles/css/zIndex.css +0 -9
  245. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +0 -150
  246. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +0 -39
  247. package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +0 -64
  248. package/dist/templates/next-app/src/uikit/components/ClientSeo.tsx +0 -36
  249. package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +0 -42
  250. package/dist/templates/next-app/src/uikit/components/EditableCell.tsx +0 -118
  251. package/dist/templates/next-app/src/uikit/components/FeatureItem.tsx +0 -13
  252. package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +0 -34
  253. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +0 -50
  254. package/dist/templates/next-app/src/uikit/components/With.tsx +0 -17
  255. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportButton.tsx +0 -62
  256. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -28
  257. package/dist/templates/next-app/src/uikit/components/localesImportButton/import.module.css +0 -6
  258. package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +0 -29
  259. package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +0 -17
  260. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +0 -105
  261. package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +0 -20
  262. package/dist/templates/next-app/src/uikit/components-app/LanguageSwitcher.tsx +0 -75
  263. package/dist/templates/next-app/src/uikit/components-app/LogoutButton.tsx +0 -45
  264. package/dist/templates/next-app/src/uikit/components-app/ThemeSwitcher.tsx +0 -144
  265. package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +0 -98
  266. package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +0 -93
  267. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +0 -25
  268. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +0 -22
  269. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +0 -37
  270. package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +0 -32
  271. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +0 -11
  272. package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +0 -8
  273. package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +0 -42
  274. package/dist/templates/next-app/tailwind.config.ts +0 -8
  275. package/dist/templates/next-app/tsconfig.json +0 -39
  276. package/dist/templates/node-lib/__tests__/readJson.test.ts +0 -26
  277. package/dist/templates/node-lib/bin/test.js +0 -30
  278. package/dist/templates/node-lib/package.json +0 -66
  279. package/dist/templates/node-lib/rollup.config.js +0 -79
  280. package/dist/templates/node-lib/src/index.ts +0 -7
  281. package/dist/templates/node-lib/src/readJson.ts +0 -12
  282. package/dist/templates/node-lib/tsconfig.json +0 -23
  283. package/dist/templates/pack-app/README.md +0 -108
  284. package/dist/templates/pack-app/eslint.config.js +0 -97
  285. package/dist/templates/pack-app/fe-config.json +0 -35
  286. package/dist/templates/pack-app/package.json +0 -86
  287. package/dist/templates/pack-app/pnpm-workspace.yaml +0 -2
  288. package/dist/templates/pack-app/tsconfig.json +0 -9
  289. package/dist/templates/pack-app/tsconfig.test.json +0 -10
  290. package/dist/templates/pack-app/vite.config.ts +0 -14
  291. package/dist/templates/react-app/.env.template +0 -22
  292. package/dist/templates/react-app/.prettierignore +0 -17
  293. package/dist/templates/react-app/README.en.md +0 -274
  294. package/dist/templates/react-app/README.md +0 -273
  295. package/dist/templates/react-app/__tests__/__mocks__/BootstrapTest.ts +0 -16
  296. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfig.ts +0 -48
  297. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +0 -17
  298. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +0 -14
  299. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +0 -38
  300. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +0 -53
  301. package/dist/templates/react-app/__tests__/__mocks__/components/TestRouter.tsx +0 -46
  302. package/dist/templates/react-app/__tests__/__mocks__/components/index.ts +0 -12
  303. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +0 -96
  304. package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +0 -110
  305. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOC.ts +0 -55
  306. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +0 -74
  307. package/dist/templates/react-app/__tests__/setup/index.ts +0 -1
  308. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +0 -64
  309. package/dist/templates/react-app/__tests__/src/App.structure.test.tsx +0 -115
  310. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +0 -288
  311. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +0 -226
  312. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +0 -178
  313. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +0 -181
  314. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +0 -61
  315. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +0 -177
  316. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +0 -191
  317. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +0 -245
  318. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +0 -240
  319. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +0 -242
  320. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +0 -135
  321. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +0 -74
  322. package/dist/templates/react-app/__tests__/src/main.test.tsx +0 -46
  323. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +0 -97
  324. package/dist/templates/react-app/__tests__/src/uikit/components/chatMessage/ChatRoot.test.tsx +0 -274
  325. package/dist/templates/react-app/config/IOCIdentifier.ts +0 -91
  326. package/dist/templates/react-app/config/Identifier/common/common.error.ts +0 -34
  327. package/dist/templates/react-app/config/Identifier/common/common.ts +0 -62
  328. package/dist/templates/react-app/config/Identifier/common/index.ts +0 -2
  329. package/dist/templates/react-app/config/Identifier/components/component.chatMessage.ts +0 -56
  330. package/dist/templates/react-app/config/Identifier/components/component.messageBaseList.ts +0 -103
  331. package/dist/templates/react-app/config/Identifier/index.ts +0 -2
  332. package/dist/templates/react-app/config/Identifier/pages/index.ts +0 -9
  333. package/dist/templates/react-app/config/Identifier/pages/page.about.ts +0 -189
  334. package/dist/templates/react-app/config/Identifier/pages/page.executor.ts +0 -275
  335. package/dist/templates/react-app/config/Identifier/pages/page.home.ts +0 -71
  336. package/dist/templates/react-app/config/Identifier/pages/page.identifiter.ts +0 -102
  337. package/dist/templates/react-app/config/Identifier/pages/page.jsonStorage.ts +0 -77
  338. package/dist/templates/react-app/config/Identifier/pages/page.login.ts +0 -162
  339. package/dist/templates/react-app/config/Identifier/pages/page.message.ts +0 -20
  340. package/dist/templates/react-app/config/Identifier/pages/page.register.ts +0 -159
  341. package/dist/templates/react-app/config/Identifier/pages/page.request.ts +0 -169
  342. package/dist/templates/react-app/config/app.router.ts +0 -338
  343. package/dist/templates/react-app/config/common.ts +0 -85
  344. package/dist/templates/react-app/config/feapi.mock.json +0 -34
  345. package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +0 -51
  346. package/dist/templates/react-app/config/i18n/aboutI18n.ts +0 -42
  347. package/dist/templates/react-app/config/i18n/chatMessageI18n.ts +0 -17
  348. package/dist/templates/react-app/config/i18n/executorI18n.ts +0 -51
  349. package/dist/templates/react-app/config/i18n/homeI18n.ts +0 -24
  350. package/dist/templates/react-app/config/i18n/i18nConfig.ts +0 -30
  351. package/dist/templates/react-app/config/i18n/identifiter18n.ts +0 -30
  352. package/dist/templates/react-app/config/i18n/jsonStorage18n.ts +0 -27
  353. package/dist/templates/react-app/config/i18n/login18n.ts +0 -42
  354. package/dist/templates/react-app/config/i18n/messageBaseListI18n.ts +0 -22
  355. package/dist/templates/react-app/config/i18n/messageI18n.ts +0 -14
  356. package/dist/templates/react-app/config/i18n/notFoundI18n.ts +0 -34
  357. package/dist/templates/react-app/config/i18n/register18n.ts +0 -40
  358. package/dist/templates/react-app/config/i18n/request18n.ts +0 -41
  359. package/dist/templates/react-app/config/theme.ts +0 -21
  360. package/dist/templates/react-app/docs/en/bootstrap.md +0 -1891
  361. package/dist/templates/react-app/docs/en/components/chat-message-component.md +0 -320
  362. package/dist/templates/react-app/docs/en/components/chat-message-refactor.md +0 -283
  363. package/dist/templates/react-app/docs/en/components/message-base-list-component.md +0 -171
  364. package/dist/templates/react-app/docs/en/development-guide.md +0 -1199
  365. package/dist/templates/react-app/docs/en/env.md +0 -1336
  366. package/dist/templates/react-app/docs/en/global.md +0 -509
  367. package/dist/templates/react-app/docs/en/i18n.md +0 -979
  368. package/dist/templates/react-app/docs/en/index.md +0 -802
  369. package/dist/templates/react-app/docs/en/ioc.md +0 -1365
  370. package/dist/templates/react-app/docs/en/playwright/e2e-tests.md +0 -321
  371. package/dist/templates/react-app/docs/en/playwright/index.md +0 -19
  372. package/dist/templates/react-app/docs/en/playwright/installation-summary.md +0 -332
  373. package/dist/templates/react-app/docs/en/playwright/overview.md +0 -222
  374. package/dist/templates/react-app/docs/en/playwright/quickstart.md +0 -325
  375. package/dist/templates/react-app/docs/en/playwright/reorganization-notes.md +0 -340
  376. package/dist/templates/react-app/docs/en/playwright/setup-complete.md +0 -290
  377. package/dist/templates/react-app/docs/en/playwright/testing-guide.md +0 -565
  378. package/dist/templates/react-app/docs/en/request.md +0 -423
  379. package/dist/templates/react-app/docs/en/router.md +0 -404
  380. package/dist/templates/react-app/docs/en/store.md +0 -1331
  381. package/dist/templates/react-app/docs/en/test-guide.md +0 -976
  382. package/dist/templates/react-app/docs/en/theme.md +0 -424
  383. package/dist/templates/react-app/docs/en/typescript-guide.md +0 -473
  384. package/dist/templates/react-app/docs/en/why-no-globals.md +0 -797
  385. package/dist/templates/react-app/docs/zh/bootstrap.md +0 -1891
  386. package/dist/templates/react-app/docs/zh/components/chat-message-component.md +0 -320
  387. package/dist/templates/react-app/docs/zh/components/chat-message-refactor.md +0 -283
  388. package/dist/templates/react-app/docs/zh/components/message-base-list-component.md +0 -171
  389. package/dist/templates/react-app/docs/zh/development-guide.md +0 -1199
  390. package/dist/templates/react-app/docs/zh/env.md +0 -1336
  391. package/dist/templates/react-app/docs/zh/global.md +0 -511
  392. package/dist/templates/react-app/docs/zh/i18n.md +0 -979
  393. package/dist/templates/react-app/docs/zh/index.md +0 -786
  394. package/dist/templates/react-app/docs/zh/ioc.md +0 -1364
  395. package/dist/templates/react-app/docs/zh/playwright/e2e-tests.md +0 -321
  396. package/dist/templates/react-app/docs/zh/playwright/index.md +0 -19
  397. package/dist/templates/react-app/docs/zh/playwright/installation-summary.md +0 -332
  398. package/dist/templates/react-app/docs/zh/playwright/overview.md +0 -222
  399. package/dist/templates/react-app/docs/zh/playwright/quickstart.md +0 -325
  400. package/dist/templates/react-app/docs/zh/playwright/reorganization-notes.md +0 -340
  401. package/dist/templates/react-app/docs/zh/playwright/setup-complete.md +0 -290
  402. package/dist/templates/react-app/docs/zh/playwright/testing-guide.md +0 -565
  403. package/dist/templates/react-app/docs/zh/request.md +0 -427
  404. package/dist/templates/react-app/docs/zh/router.md +0 -408
  405. package/dist/templates/react-app/docs/zh/store.md +0 -1329
  406. package/dist/templates/react-app/docs/zh/test-guide.md +0 -976
  407. package/dist/templates/react-app/docs/zh/theme.md +0 -424
  408. package/dist/templates/react-app/docs/zh/typescript-guide.md +0 -473
  409. package/dist/templates/react-app/docs/zh/why-no-globals.md +0 -797
  410. package/dist/templates/react-app/e2e/App.spec.ts +0 -319
  411. package/dist/templates/react-app/e2e/fixtures/base.fixture.ts +0 -40
  412. package/dist/templates/react-app/e2e/main.spec.ts +0 -20
  413. package/dist/templates/react-app/e2e/utils/test-helpers.ts +0 -19
  414. package/dist/templates/react-app/eslint.config.mjs +0 -325
  415. package/dist/templates/react-app/index.html +0 -13
  416. package/dist/templates/react-app/makes/generateTs2LocalesOptions.ts +0 -26
  417. package/dist/templates/react-app/package.json +0 -125
  418. package/dist/templates/react-app/playwright.config.ts +0 -79
  419. package/dist/templates/react-app/postcss.config.js +0 -5
  420. package/dist/templates/react-app/public/locales/en/common.json +0 -235
  421. package/dist/templates/react-app/public/locales/zh/common.json +0 -235
  422. package/dist/templates/react-app/public/logo.svg +0 -1
  423. package/dist/templates/react-app/public/router-root/logo.svg +0 -1
  424. package/dist/templates/react-app/src/App.tsx +0 -35
  425. package/dist/templates/react-app/src/assets/react.svg +0 -1
  426. package/dist/templates/react-app/src/base/apis/AiApi.ts +0 -68
  427. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +0 -29
  428. package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +0 -14
  429. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +0 -86
  430. package/dist/templates/react-app/src/base/apis/feApi/FeApiType.ts +0 -21
  431. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +0 -142
  432. package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +0 -14
  433. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +0 -86
  434. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +0 -70
  435. package/dist/templates/react-app/src/base/cases/AppConfig.ts +0 -123
  436. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +0 -115
  437. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +0 -64
  438. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +0 -45
  439. package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +0 -23
  440. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +0 -55
  441. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +0 -80
  442. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +0 -40
  443. package/dist/templates/react-app/src/base/cases/ResourceState.ts +0 -23
  444. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +0 -173
  445. package/dist/templates/react-app/src/base/cases/TranslateI18nInterface.ts +0 -26
  446. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +0 -23
  447. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +0 -10
  448. package/dist/templates/react-app/src/base/port/IOCInterface.ts +0 -38
  449. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +0 -21
  450. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +0 -23
  451. package/dist/templates/react-app/src/base/port/RequestStatusInterface.ts +0 -5
  452. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +0 -29
  453. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +0 -20
  454. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +0 -61
  455. package/dist/templates/react-app/src/base/services/I18nService.ts +0 -146
  456. package/dist/templates/react-app/src/base/services/IdentifierService.ts +0 -162
  457. package/dist/templates/react-app/src/base/services/RouteService.ts +0 -115
  458. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +0 -45
  459. package/dist/templates/react-app/src/base/services/UserService.ts +0 -88
  460. package/dist/templates/react-app/src/base/types/Page.ts +0 -47
  461. package/dist/templates/react-app/src/base/types/deprecated-antd.d.ts +0 -60
  462. package/dist/templates/react-app/src/base/types/global.d.ts +0 -8
  463. package/dist/templates/react-app/src/core/IOC.ts +0 -28
  464. package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +0 -108
  465. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -54
  466. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  467. package/dist/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +0 -14
  468. package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +0 -28
  469. package/dist/templates/react-app/src/core/clientIoc/ClientIOC.ts +0 -47
  470. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +0 -142
  471. package/dist/templates/react-app/src/core/globals.ts +0 -47
  472. package/dist/templates/react-app/src/main.tsx +0 -19
  473. package/dist/templates/react-app/src/pages/404.tsx +0 -19
  474. package/dist/templates/react-app/src/pages/500.tsx +0 -18
  475. package/dist/templates/react-app/src/pages/NoRouteFound.tsx +0 -5
  476. package/dist/templates/react-app/src/pages/auth/Layout.tsx +0 -27
  477. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +0 -166
  478. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +0 -226
  479. package/dist/templates/react-app/src/pages/base/AboutPage.tsx +0 -123
  480. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +0 -467
  481. package/dist/templates/react-app/src/pages/base/HomePage.tsx +0 -81
  482. package/dist/templates/react-app/src/pages/base/IdentifierPage.tsx +0 -117
  483. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +0 -132
  484. package/dist/templates/react-app/src/pages/base/Layout.tsx +0 -20
  485. package/dist/templates/react-app/src/pages/base/MessagePage.tsx +0 -71
  486. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +0 -18
  487. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +0 -193
  488. package/dist/templates/react-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  489. package/dist/templates/react-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  490. package/dist/templates/react-app/src/styles/css/antd-themes/_common/index.css +0 -3
  491. package/dist/templates/react-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  492. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +0 -4
  493. package/dist/templates/react-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  494. package/dist/templates/react-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  495. package/dist/templates/react-app/src/styles/css/antd-themes/menu/index.css +0 -3
  496. package/dist/templates/react-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  497. package/dist/templates/react-app/src/styles/css/antd-themes/no-context.css +0 -34
  498. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  499. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  500. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  501. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  502. package/dist/templates/react-app/src/styles/css/antd-themes/table/_default.css +0 -44
  503. package/dist/templates/react-app/src/styles/css/antd-themes/table/dark.css +0 -43
  504. package/dist/templates/react-app/src/styles/css/antd-themes/table/index.css +0 -3
  505. package/dist/templates/react-app/src/styles/css/antd-themes/table/pink.css +0 -43
  506. package/dist/templates/react-app/src/styles/css/index.css +0 -6
  507. package/dist/templates/react-app/src/styles/css/page.css +0 -20
  508. package/dist/templates/react-app/src/styles/css/scrollbar.css +0 -34
  509. package/dist/templates/react-app/src/styles/css/tailwind.css +0 -5
  510. package/dist/templates/react-app/src/styles/css/themes/_default.css +0 -30
  511. package/dist/templates/react-app/src/styles/css/themes/dark.css +0 -30
  512. package/dist/templates/react-app/src/styles/css/themes/index.css +0 -3
  513. package/dist/templates/react-app/src/styles/css/themes/pink.css +0 -30
  514. package/dist/templates/react-app/src/styles/css/zIndex.css +0 -9
  515. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +0 -72
  516. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +0 -41
  517. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +0 -22
  518. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +0 -136
  519. package/dist/templates/react-app/src/uikit/components/AppRouterProvider.tsx +0 -35
  520. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +0 -51
  521. package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +0 -44
  522. package/dist/templates/react-app/src/uikit/components/BaseRouteProvider.tsx +0 -21
  523. package/dist/templates/react-app/src/uikit/components/BaseRouteSeo.tsx +0 -18
  524. package/dist/templates/react-app/src/uikit/components/BootstrapsProvider.tsx +0 -11
  525. package/dist/templates/react-app/src/uikit/components/ClientSeo.tsx +0 -62
  526. package/dist/templates/react-app/src/uikit/components/ComboProvider.tsx +0 -38
  527. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +0 -78
  528. package/dist/templates/react-app/src/uikit/components/Loading.tsx +0 -49
  529. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +0 -43
  530. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +0 -58
  531. package/dist/templates/react-app/src/uikit/components/MessageBaseList.tsx +0 -258
  532. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +0 -19
  533. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +0 -137
  534. package/dist/templates/react-app/src/uikit/components/With.tsx +0 -17
  535. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatMessageBridge.ts +0 -205
  536. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatRoot.tsx +0 -21
  537. package/dist/templates/react-app/src/uikit/components/chatMessage/FocusBar.tsx +0 -108
  538. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageApi.ts +0 -282
  539. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageItem.tsx +0 -102
  540. package/dist/templates/react-app/src/uikit/components/chatMessage/MessagesList.tsx +0 -86
  541. package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +0 -42
  542. package/dist/templates/react-app/src/uikit/contexts/IOCContext.ts +0 -13
  543. package/dist/templates/react-app/src/uikit/hooks/useAppTranslation.ts +0 -26
  544. package/dist/templates/react-app/src/uikit/hooks/useI18nInterface.ts +0 -25
  545. package/dist/templates/react-app/src/uikit/hooks/useIOC.ts +0 -35
  546. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +0 -21
  547. package/dist/templates/react-app/src/uikit/hooks/useRouterI18nGuard.ts +0 -25
  548. package/dist/templates/react-app/src/uikit/hooks/useStrictEffect.ts +0 -31
  549. package/dist/templates/react-app/src/vite-env.d.ts +0 -1
  550. package/dist/templates/react-app/tailwind.config.js +0 -4
  551. package/dist/templates/react-app/tsconfig.app.json +0 -36
  552. package/dist/templates/react-app/tsconfig.e2e.json +0 -24
  553. package/dist/templates/react-app/tsconfig.json +0 -22
  554. package/dist/templates/react-app/tsconfig.node.json +0 -27
  555. package/dist/templates/react-app/tsconfig.test.json +0 -18
  556. package/dist/templates/react-app/vite.config.ts +0 -144
@@ -1,1329 +0,0 @@
1
- # Store 状态管理
2
-
3
- ## 📋 目录
4
-
5
- - [核心理念](#-核心理念) - 应用层通知 UI 层
6
- - [什么是 Store](#-什么是-store) - 状态容器
7
- - [为什么需要 Store](#-为什么需要-store) - 解决通信问题
8
- - [核心问题](#-核心问题) - 应用层如何通知 UI 层
9
- - [项目中的实现](#-项目中的实现) - 实战指南
10
- - [使用方式](#-使用方式) - Service + Store + useStore
11
- - [测试](#-测试) - 独立测试和组合测试
12
- - [最佳实践](#-最佳实践) - 7 条核心实践
13
- - [常见问题](#-常见问题) - FAQ
14
-
15
- ---
16
-
17
- ## 🎯 核心理念
18
-
19
- > **🚨 核心问题:应用层(Service)如何通知 UI 层更新,同时保持分离?**
20
-
21
- > **⭐ 解决方案:Service 包含 Store,通过 `emit` 发布状态,UI 通过 `useStore` 订阅状态!**
22
-
23
- ### 核心概念
24
-
25
- ```
26
- ┌──────────────────────────────────────────────┐
27
- │ 问题:UI 和逻辑已经分离了,但如何通信? │
28
- │ │
29
- │ Service (应用层) │
30
- │ ├── 业务逻辑 │
31
- │ └── 数据处理 │
32
- │ ↓ 如何通知? │
33
- │ Component (UI 层) │
34
- │ └── UI 渲染 │
35
- │ │
36
- │ ❌ 问题:Service 改变了数据,UI 如何知道? │
37
- └──────────────────────────────────────────────┘
38
-
39
- ┌──────────────────────────────────────────────┐
40
- │ 解决方案:Store 作为桥梁 │
41
- │ │
42
- │ Service (应用层) │
43
- │ ├── 业务逻辑 │
44
- │ ├── Store (状态容器) │
45
- │ │ ├── state (状态) │
46
- │ │ └── emit() (发布状态) │
47
- │ │ │
48
- │ │ ↓ 发布订阅模式 │
49
- │ │ │
50
- │ └── useStore (订阅) │
51
- │ ↓ │
52
- │ Component (UI 层) │
53
- │ └── 自动更新 UI │
54
- │ │
55
- │ ✅ Service 通过 emit 发布状态 │
56
- │ ✅ UI 通过 useStore 订阅状态 │
57
- │ ✅ 保持分离,解耦合 │
58
- └──────────────────────────────────────────────┘
59
- ```
60
-
61
- ---
62
-
63
- ## 🗂️ 什么是 Store
64
-
65
- Store 是一个**响应式状态容器**,基于**发布订阅模式**实现。
66
-
67
- ### 简单理解
68
-
69
- ```
70
- Store = 状态 + 发布订阅
71
-
72
- Service 拥有 Store
73
- Service 通过 Store.emit() 发布状态
74
- UI 通过 useStore() 订阅状态
75
- ```
76
-
77
- ### 类比理解
78
-
79
- ```
80
- Store 就像一个广播电台:
81
-
82
- 📻 电台(Store)
83
- - 有节目内容(state)
84
- - 可以广播节目(emit)
85
- - 听众可以收听(subscribe)
86
-
87
- 🎤 主持人(Service)
88
- - 制作节目内容(业务逻辑)
89
- - 通过电台广播(emit)
90
-
91
- 📱 听众(UI Component)
92
- - 收听电台(useStore)
93
- - 听到新内容自动反应(自动更新 UI)
94
- ```
95
-
96
- ---
97
-
98
- ## 🤔 为什么需要 Store
99
-
100
- ### 核心问题:UI 和逻辑分离后,如何通信?
101
-
102
- 我们已经通过 IOC 实现了 UI 和逻辑分离,但问题来了:
103
-
104
- #### ❌ 问题示例:没有 Store
105
-
106
- ```typescript
107
- // Service(逻辑层)
108
- @injectable()
109
- export class UserService {
110
- private user: UserInfo | null = null;
111
-
112
- async login(username: string, password: string) {
113
- const response = await this.api.login({ username, password });
114
- this.user = response.user; // ✅ 登录成功,user 已更新
115
-
116
- // ❌ 问题:UI 如何知道 user 已经更新?
117
- // ❌ Service 无法通知 UI
118
- }
119
- }
120
-
121
- // UI 组件
122
- function UserProfile() {
123
- const userService = useIOC('UserServiceInterface');
124
-
125
- // ❌ 问题:如何获取 userService.user?
126
- // ❌ userService.user 更新后,如何触发 UI 重新渲染?
127
-
128
- return <div>{/* 无法显示 user */}</div>;
129
- }
130
-
131
- // 😰😰😰 问题总结:
132
- // 1. UI 无法获取 Service 的内部状态
133
- // 2. Service 状态更新后,UI 不知道
134
- // 3. 需要手动调用某个方法来获取状态?(打破分离原则)
135
- // 4. 需要轮询检查状态?(性能差)
136
- ```
137
-
138
- #### ✅ 解决方案:使用 Store
139
-
140
- ```typescript
141
- // Service(逻辑层)
142
- @injectable()
143
- export class UserService extends StoreInterface<UserState> {
144
- constructor() {
145
- super(() => ({
146
- user: null,
147
- loading: false
148
- }));
149
- }
150
-
151
- async login(username: string, password: string) {
152
- // 设置加载状态
153
- this.emit({ ...this.state, loading: true });
154
-
155
- const response = await this.api.login({ username, password });
156
-
157
- // ✅ 通过 emit 发布新状态,自动通知所有订阅者
158
- this.emit({
159
- user: response.user,
160
- loading: false
161
- });
162
- }
163
- }
164
-
165
- // UI 组件
166
- function UserProfile() {
167
- const userService = useIOC('UserServiceInterface');
168
-
169
- // ✅ 通过 useStore 订阅状态
170
- const { user, loading } = useStore(userService);
171
-
172
- // ✅ userService.emit() 时,组件会自动重新渲染
173
-
174
- if (loading) return <div>Loading...</div>;
175
- return <div>{user?.name}</div>;
176
- }
177
-
178
- // ✅✅✅ 优势总结:
179
- // 1. UI 可以订阅 Service 的状态
180
- // 2. Service 状态更新后,UI 自动更新
181
- // 3. 保持分离(Service 不知道有哪些 UI 在监听)
182
- // 4. 高性能(只有订阅的组件才会更新)
183
- ```
184
-
185
- ### 对比总结
186
-
187
- | 特性 | 没有 Store | 有 Store |
188
- | ---------------- | ---------------------- | --------------------- |
189
- | **状态获取** | ❌ 无法获取内部状态 | ✅ 通过 useStore 订阅 |
190
- | **状态更新通知** | ❌ UI 不知道状态变化 | ✅ emit 自动通知 |
191
- | **UI 更新** | ❌ 需要手动触发 | ✅ 自动重新渲染 |
192
- | **解耦** | ❌ Service 需要知道 UI | ✅ 完全解耦 |
193
- | **性能** | ❌ 轮询或全局更新 | ✅ 精确更新订阅者 |
194
- | **可测试性** | ❌ 难以测试状态变化 | ✅ 易于测试状态 |
195
-
196
- ---
197
-
198
- ## ❓ 核心问题
199
-
200
- ### 应用层如何通知 UI 层,同时保持分离?
201
-
202
- #### 问题拆解
203
-
204
- 1. **应用层(Service)有状态** - 如用户信息、加载状态
205
- 2. **UI 层需要显示这些状态** - 显示用户名、显示加载动画
206
- 3. **应用层状态会变化** - 登录成功后,用户信息更新
207
- 4. **UI 层需要自动更新** - 用户信息变化后,UI 自动显示新名字
208
- 5. **保持分离** - Service 不应该直接操作 UI,UI 不应该直接访问 Service 内部
209
-
210
- #### 解决方案:发布订阅模式
211
-
212
- ```typescript
213
- // 1. Service 定义状态
214
- interface UserState {
215
- user: UserInfo | null;
216
- loading: boolean;
217
- }
218
-
219
- // 2. Service 继承 StoreInterface
220
- @injectable()
221
- export class UserService extends StoreInterface<UserState> {
222
- constructor() {
223
- super(() => ({
224
- user: null,
225
- loading: false
226
- }));
227
- }
228
-
229
- // 3. Service 通过 emit 发布状态
230
- async login(username: string, password: string) {
231
- this.emit({ ...this.state, loading: true }); // 发布:开始加载
232
-
233
- const response = await this.api.login({ username, password });
234
-
235
- this.emit({
236
- user: response.user,
237
- loading: false
238
- }); // 发布:加载完成,用户已登录
239
- }
240
-
241
- // 4. Service 不需要知道谁在监听
242
- // ✅ 完全解耦
243
- }
244
-
245
- // 5. UI 通过 useStore 订阅状态
246
- function LoginPage() {
247
- const userService = useIOC('UserServiceInterface');
248
- const { loading } = useStore(userService);
249
-
250
- const handleLogin = () => {
251
- userService.login('user', 'pass');
252
- };
253
-
254
- // 6. 当 Service emit 新状态时,UI 自动更新
255
- return (
256
- <button onClick={handleLogin} disabled={loading}>
257
- {loading ? 'Logging in...' : 'Login'}
258
- </button>
259
- );
260
- }
261
- ```
262
-
263
- #### 工作流程
264
-
265
- ```
266
- ┌─────────────────────────────────────────────┐
267
- │ 完整的状态更新流程 │
268
- │ │
269
- │ 1. 用户点击按钮 │
270
- │ ↓ │
271
- │ 2. UI 调用 Service 方法 │
272
- │ userService.login() │
273
- │ ↓ │
274
- │ 3. Service 执行业务逻辑 │
275
- │ - 调用 API │
276
- │ - 处理数据 │
277
- │ ↓ │
278
- │ 4. Service 通过 emit 发布新状态 │
279
- │ this.emit({ user: ..., loading: false })│
280
- │ ↓ │
281
- │ 5. Store 通知所有订阅者 │
282
- │ listeners.forEach(listener => ...) │
283
- │ ↓ │
284
- │ 6. useStore 收到通知 │
285
- │ 触发组件重新渲染 │
286
- │ ↓ │
287
- │ 7. UI 显示最新状态 │
288
- │ 显示用户名 / 隐藏加载动画 │
289
- └─────────────────────────────────────────────┘
290
- ```
291
-
292
- ---
293
-
294
- ## 🛠️ 项目中的实现
295
-
296
- ### 1. 文件结构
297
-
298
- ```
299
- src/
300
- ├── base/
301
- │ ├── services/
302
- │ │ ├── UserService.ts # Service 继承 StoreInterface
303
- │ │ ├── RouteService.ts # Service 继承 StoreInterface
304
- │ │ └── I18nService.ts # Service 继承 StoreInterface
305
- │ └── port/
306
- │ └── UserServiceInterface.ts # Service 接口
307
- └── uikit/
308
- └── hooks/
309
- └── useStore.ts (from @brain-toolkit/react-kit)
310
- ```
311
-
312
- ### 2. Store 基类
313
-
314
- Store 系统基于 `@brain-toolkit/react-kit` 的 `SliceStore`:
315
-
316
- ```typescript
317
- // 来自 @brain-toolkit/react-kit
318
- export class SliceStore<T> {
319
- protected state: T;
320
- private listeners = new Set<(state: T) => void>();
321
-
322
- constructor(stateFactory: () => T) {
323
- this.state = stateFactory();
324
- }
325
-
326
- // 发布状态
327
- protected emit(newState: T) {
328
- this.state = newState;
329
- // 通知所有订阅者
330
- this.listeners.forEach((listener) => listener(this.state));
331
- }
332
-
333
- // 订阅状态
334
- subscribe(listener: (state: T) => void) {
335
- this.listeners.add(listener);
336
- // 返回取消订阅函数
337
- return () => this.listeners.delete(listener);
338
- }
339
-
340
- // 获取当前状态
341
- getState(): T {
342
- return this.state;
343
- }
344
- }
345
- ```
346
-
347
- ### 3. StoreInterface 基类
348
-
349
- 项目中的 Store 基类,提供额外的工具方法:
350
-
351
- ```typescript
352
- // 来自 @qlover/corekit-bridge
353
- export abstract class StoreInterface<
354
- T extends StoreStateInterface
355
- > extends SliceStore<T> {
356
- constructor(protected stateFactory: () => T) {
357
- super(stateFactory);
358
- }
359
-
360
- // 重置状态
361
- resetState(): void {
362
- this.emit(this.stateFactory());
363
- }
364
-
365
- // 克隆状态(用于更新)
366
- cloneState(source?: Partial<T>): T {
367
- const cloned = clone(this.state);
368
- if (typeof cloned === 'object' && cloned !== null) {
369
- Object.assign(cloned, source);
370
- }
371
- return cloned;
372
- }
373
- }
374
- ```
375
-
376
- ### 4. 状态接口
377
-
378
- ```typescript
379
- // 所有状态必须实现此接口
380
- export interface StoreStateInterface {
381
- // 可以在这里定义通用属性
382
- // loading?: boolean;
383
- // error?: Error | null;
384
- }
385
- ```
386
-
387
- ---
388
-
389
- ## 📝 使用方式
390
-
391
- ### 1. 定义状态接口
392
-
393
- ```typescript
394
- // src/base/services/UserService.ts
395
- export interface UserState extends StoreStateInterface {
396
- user: UserInfo | null;
397
- loading: boolean;
398
- error: Error | null;
399
- }
400
- ```
401
-
402
- ### 2. Service 继承 StoreInterface
403
-
404
- ```typescript
405
- // src/base/services/UserService.ts
406
- import { StoreInterface } from '@qlover/corekit-bridge';
407
- import { injectable, inject } from 'inversify';
408
-
409
- @injectable()
410
- export class UserService extends StoreInterface<UserState> {
411
- constructor(
412
- @inject(UserApi) private api: UserApi,
413
- @inject(IOCIdentifier.AppConfig) private config: AppConfig
414
- ) {
415
- // 初始化状态
416
- super(() => ({
417
- user: null,
418
- loading: false,
419
- error: null
420
- }));
421
- }
422
-
423
- // 业务方法:通过 emit 发布状态
424
- async login(username: string, password: string) {
425
- // 1. 开始加载
426
- this.emit({
427
- ...this.state,
428
- loading: true,
429
- error: null
430
- });
431
-
432
- try {
433
- // 2. 调用 API
434
- const response = await this.api.login({ username, password });
435
-
436
- // 3. 成功:发布新状态
437
- this.emit({
438
- user: response.user,
439
- loading: false,
440
- error: null
441
- });
442
- } catch (error) {
443
- // 4. 失败:发布错误状态
444
- this.emit({
445
- ...this.state,
446
- loading: false,
447
- error: error as Error
448
- });
449
- }
450
- }
451
-
452
- async logout() {
453
- this.emit({
454
- user: null,
455
- loading: false,
456
- error: null
457
- });
458
- }
459
-
460
- // 使用 cloneState 简化更新
461
- setUser(user: UserInfo) {
462
- this.emit(this.cloneState({ user }));
463
- }
464
- }
465
- ```
466
-
467
- ### 3. UI 订阅状态
468
-
469
- ```typescript
470
- // src/pages/LoginPage.tsx
471
- import { useStore } from '@brain-toolkit/react-kit/hooks/useStore';
472
- import { useIOC } from '@/uikit/hooks/useIOC';
473
-
474
- function LoginPage() {
475
- const userService = useIOC('UserServiceInterface');
476
-
477
- // ✅ 方式 1:订阅完整状态
478
- const { user, loading, error } = useStore(userService);
479
-
480
- const handleLogin = async () => {
481
- await userService.login('username', 'password');
482
- };
483
-
484
- if (loading) {
485
- return <div>Loading...</div>;
486
- }
487
-
488
- return (
489
- <div>
490
- {error && <div>Error: {error.message}</div>}
491
- <button onClick={handleLogin}>Login</button>
492
- </div>
493
- );
494
- }
495
- ```
496
-
497
- ### 4. 使用选择器(性能优化)
498
-
499
- ```typescript
500
- // src/pages/UserProfile.tsx
501
- function UserProfile() {
502
- const userService = useIOC('UserServiceInterface');
503
-
504
- // ✅ 方式 2:只订阅需要的状态(性能更好)
505
- const user = useStore(userService, (state) => state.user);
506
-
507
- // ✅ 只有 user 变化时才重新渲染,loading 变化不会触发
508
-
509
- return <div>{user?.name}</div>;
510
- }
511
- ```
512
-
513
- ### 5. 定义选择器(推荐)
514
-
515
- ```typescript
516
- // src/base/services/UserService.ts
517
- @injectable()
518
- export class UserService extends StoreInterface<UserState> {
519
- // ... 其他代码
520
-
521
- // ✅ 定义选择器
522
- selector = {
523
- user: (state: UserState) => state.user,
524
- loading: (state: UserState) => state.loading,
525
- error: (state: UserState) => state.error,
526
- isLoggedIn: (state: UserState) => state.user !== null
527
- };
528
- }
529
-
530
- // 使用
531
- function UserProfile() {
532
- const userService = useIOC('UserServiceInterface');
533
-
534
- // ✅ 使用预定义的选择器
535
- const user = useStore(userService, userService.selector.user);
536
- const isLoggedIn = useStore(userService, userService.selector.isLoggedIn);
537
-
538
- return <div>{isLoggedIn ? user?.name : 'Please login'}</div>;
539
- }
540
- ```
541
-
542
- ### 6. 实际项目示例
543
-
544
- #### 示例 1:UserService
545
-
546
- ```typescript
547
- // src/base/services/UserService.ts
548
- @injectable()
549
- export class UserService extends UserAuthServiceInterface {
550
- constructor(
551
- @inject(UserApi) userApi: UserApi,
552
- @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
553
- @inject(IOCIdentifier.LocalStorageEncrypt) storage: Storage
554
- ) {
555
- super(userApi, {
556
- userStorage: {
557
- key: appConfig.userInfoStorageKey,
558
- storage: storage
559
- },
560
- credentialStorage: {
561
- key: appConfig.userTokenStorageKey,
562
- storage: storage
563
- }
564
- });
565
- }
566
-
567
- // ✅ UserService 继承的基类包含 store
568
- override get store(): UserAuthStore<UserApiState> {
569
- return super.store as UserAuthStore<UserApiState>;
570
- }
571
-
572
- override async logout(): Promise<void> {
573
- await super.logout();
574
- // ✅ store 会自动通知 UI
575
- this.routerService.gotoLogin();
576
- }
577
- }
578
-
579
- // 使用
580
- function Layout() {
581
- const userService = useIOC(IOCIdentifier.UserServiceInterface);
582
-
583
- // ✅ 订阅 userService.store
584
- useStore(userService.store);
585
-
586
- if (userService.isAuthenticated()) {
587
- return <Navigate to="/" replace />;
588
- }
589
-
590
- return <Outlet />;
591
- }
592
- ```
593
-
594
- #### 示例 2:RouteService
595
-
596
- ```typescript
597
- // src/base/services/RouteService.ts
598
- export class RouteService extends StoreInterface<RouterServiceState> {
599
- constructor(
600
- protected uiBridge: UIBridgeInterface<NavigateFunction>,
601
- protected i18nService: I18nServiceInterface,
602
- protected options: RouterServiceOptions
603
- ) {
604
- super(
605
- () => new RouterServiceState(options.routes, !!options.hasLocalRoutes)
606
- );
607
- }
608
-
609
- // ✅ 通过 emit 发布路由变化
610
- override changeRoutes(routes: RouteConfigValue[]): void {
611
- this.emit(this.cloneState({ routes }));
612
- }
613
-
614
- override goto(path: string, options?: NavigateOptions): void {
615
- const composedPath = this.composePath(path);
616
- this.uiBridge.getUIBridge()(composedPath, options);
617
- }
618
- }
619
-
620
- // 使用
621
- function AppRouterProvider() {
622
- const routerService = useIOC(IOCIdentifier.RouteServiceInterface);
623
-
624
- // ✅ 订阅 routes 变化
625
- const routes = useStore(routerService, (state) => state.routes);
626
-
627
- const router = createBrowserRouter(routes);
628
-
629
- return <RouterProvider router={router} />;
630
- }
631
- ```
632
-
633
- #### 示例 3:I18nService
634
-
635
- ```typescript
636
- // src/base/services/I18nService.ts
637
- export class I18nService extends StoreInterface<I18nServiceState> {
638
- constructor(protected pathname: string) {
639
- super(() => new I18nServiceState(i18n.language as I18nServiceLocale));
640
- }
641
-
642
- selector = {
643
- loading: (state: I18nServiceState) => state.loading,
644
- language: (state: I18nServiceState) => state.language
645
- };
646
-
647
- override async changeLanguage(lng: string): Promise<void> {
648
- // ✅ 发布加载状态
649
- this.emit(this.cloneState({ loading: true }));
650
-
651
- await i18n.changeLanguage(lng);
652
-
653
- // ✅ 发布完成状态
654
- this.emit({
655
- language: lng as I18nServiceLocale,
656
- loading: false
657
- });
658
- }
659
- }
660
-
661
- // 使用
662
- function LanguageSwitcher() {
663
- const i18nService = useIOC(IOCIdentifier.I18nServiceInterface);
664
-
665
- // ✅ 只订阅 loading 状态
666
- const loading = useStore(i18nService, i18nService.selector.loading);
667
-
668
- return (
669
- <Select
670
- value={i18n.language}
671
- loading={loading}
672
- onChange={(lng) => i18nService.changeLanguage(lng)}
673
- />
674
- );
675
- }
676
- ```
677
-
678
- ---
679
-
680
- ## 🧪 测试
681
-
682
- ### 核心优势:Store 可以独立测试,UI 可以 mock Store
683
-
684
- #### 1. 测试 Service 和 Store(逻辑测试)
685
-
686
- ```typescript
687
- // __tests__/src/base/services/UserService.test.ts
688
- import { describe, it, expect, vi, beforeEach } from 'vitest';
689
- import { UserService } from '@/base/services/UserService';
690
-
691
- describe('UserService (逻辑测试)', () => {
692
- let userService: UserService;
693
- let mockApi: any;
694
-
695
- beforeEach(() => {
696
- mockApi = {
697
- login: vi.fn(),
698
- getUserInfo: vi.fn()
699
- };
700
-
701
- userService = new UserService(mockApi, mockConfig, mockStorage);
702
- });
703
-
704
- it('should update store state when login success', async () => {
705
- // ✅ 测试状态变化
706
- mockApi.login.mockResolvedValue({
707
- user: { name: 'John', id: 1 },
708
- token: 'test-token'
709
- });
710
-
711
- // 订阅状态变化
712
- const states: any[] = [];
713
- userService.subscribe((state) => {
714
- states.push({ ...state });
715
- });
716
-
717
- // 调用登录
718
- await userService.login('user', 'pass');
719
-
720
- // ✅ 验证状态变化序列
721
- expect(states).toHaveLength(2);
722
-
723
- // 第一次 emit:loading = true
724
- expect(states[0]).toEqual({
725
- user: null,
726
- loading: true,
727
- error: null
728
- });
729
-
730
- // 第二次 emit:loading = false, user = John
731
- expect(states[1]).toEqual({
732
- user: { name: 'John', id: 1 },
733
- loading: false,
734
- error: null
735
- });
736
- });
737
-
738
- it('should update store state when login fails', async () => {
739
- mockApi.login.mockRejectedValue(new Error('Invalid credentials'));
740
-
741
- const states: any[] = [];
742
- userService.subscribe((state) => states.push({ ...state }));
743
-
744
- await expect(userService.login('user', 'wrong')).rejects.toThrow();
745
-
746
- // ✅ 验证错误状态
747
- expect(states[1]).toEqual({
748
- user: null,
749
- loading: false,
750
- error: expect.any(Error)
751
- });
752
- });
753
-
754
- it('should emit logout state', () => {
755
- // 先设置用户登录
756
- userService.emit({
757
- user: { name: 'John', id: 1 },
758
- loading: false,
759
- error: null
760
- });
761
-
762
- // 登出
763
- userService.logout();
764
-
765
- // ✅ 验证状态被重置
766
- expect(userService.getState()).toEqual({
767
- user: null,
768
- loading: false,
769
- error: null
770
- });
771
- });
772
- });
773
-
774
- // ✅✅✅ 优势:
775
- // 1. 不需要渲染 UI
776
- // 2. 可以测试所有状态变化
777
- // 3. 可以验证 emit 的调用序列
778
- // 4. 测试运行快速
779
- ```
780
-
781
- #### 2. 测试 UI 组件(UI 测试)
782
-
783
- ```typescript
784
- // __tests__/src/pages/LoginPage.test.tsx
785
- import { describe, it, expect, vi } from 'vitest';
786
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
787
- import { LoginPage } from '@/pages/LoginPage';
788
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
789
-
790
- describe('LoginPage (UI 测试)', () => {
791
- it('should show loading when login', async () => {
792
- // ✅ Mock Service 和 Store
793
- const mockStore = {
794
- user: null,
795
- loading: false,
796
- error: null
797
- };
798
-
799
- const mockUserService = {
800
- login: vi.fn().mockImplementation(() => {
801
- // 模拟状态变化
802
- mockStore.loading = true;
803
- return Promise.resolve();
804
- }),
805
- subscribe: vi.fn(),
806
- getState: () => mockStore
807
- };
808
-
809
- const mockIOC = (identifier: string) => {
810
- if (identifier === 'UserServiceInterface') return mockUserService;
811
- };
812
-
813
- // ✅ 渲染组件
814
- const { rerender } = render(
815
- <IOCProvider value={mockIOC}>
816
- <LoginPage />
817
- </IOCProvider>
818
- );
819
-
820
- // 点击登录按钮
821
- const loginButton = screen.getByText('Login');
822
- fireEvent.click(loginButton);
823
-
824
- // ✅ 验证 Service 被调用
825
- expect(mockUserService.login).toHaveBeenCalled();
826
-
827
- // 模拟状态更新
828
- mockStore.loading = true;
829
- rerender(
830
- <IOCProvider value={mockIOC}>
831
- <LoginPage />
832
- </IOCProvider>
833
- );
834
-
835
- // ✅ 验证 UI 显示加载状态
836
- expect(screen.getByText('Loading...')).toBeInTheDocument();
837
- });
838
-
839
- it('should show error message when login fails', () => {
840
- const mockStore = {
841
- user: null,
842
- loading: false,
843
- error: new Error('Invalid credentials')
844
- };
845
-
846
- const mockUserService = {
847
- login: vi.fn(),
848
- subscribe: vi.fn(),
849
- getState: () => mockStore
850
- };
851
-
852
- const mockIOC = (identifier: string) => {
853
- if (identifier === 'UserServiceInterface') return mockUserService;
854
- };
855
-
856
- render(
857
- <IOCProvider value={mockIOC}>
858
- <LoginPage />
859
- </IOCProvider>
860
- );
861
-
862
- // ✅ 验证错误消息显示
863
- expect(screen.getByText('Error: Invalid credentials')).toBeInTheDocument();
864
- });
865
- });
866
-
867
- // ✅✅✅ 优势:
868
- // 1. 不需要真实的 Service 实现
869
- // 2. 可以轻松模拟各种状态
870
- // 3. UI 测试专注于 UI 逻辑
871
- ```
872
-
873
- #### 3. 组合测试(集成测试)
874
-
875
- ```typescript
876
- // __tests__/src/integration/UserLogin.test.tsx
877
- import { describe, it, expect } from 'vitest';
878
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
879
- import { LoginPage } from '@/pages/LoginPage';
880
- import { UserService } from '@/base/services/UserService';
881
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
882
-
883
- describe('User Login Flow (组合测试)', () => {
884
- it('should complete login flow', async () => {
885
- // ✅ 使用真实的 Service 和 Store
886
- const mockApi = {
887
- login: vi.fn().mockResolvedValue({
888
- user: { name: 'John', id: 1 },
889
- token: 'test-token'
890
- })
891
- };
892
-
893
- const userService = new UserService(mockApi, mockConfig, mockStorage);
894
-
895
- const mockIOC = (identifier: string) => {
896
- if (identifier === 'UserServiceInterface') return userService;
897
- };
898
-
899
- // ✅ 渲染真实 UI
900
- render(
901
- <IOCProvider value={mockIOC}>
902
- <LoginPage />
903
- </IOCProvider>
904
- );
905
-
906
- // ✅ 模拟用户操作
907
- const loginButton = screen.getByText('Login');
908
- fireEvent.click(loginButton);
909
-
910
- // ✅ 验证加载状态
911
- await waitFor(() => {
912
- expect(screen.getByText('Loading...')).toBeInTheDocument();
913
- });
914
-
915
- // ✅ 验证登录成功
916
- await waitFor(() => {
917
- expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
918
- expect(userService.getState().user).toEqual({
919
- name: 'John',
920
- id: 1
921
- });
922
- });
923
-
924
- // ✅ 验证 API 被调用
925
- expect(mockApi.login).toHaveBeenCalled();
926
- });
927
- });
928
-
929
- // ✅✅✅ 优势:
930
- // 1. 测试真实的用户流程
931
- // 2. 验证 Service 和 UI 的集成
932
- // 3. 发现集成问题
933
- ```
934
-
935
- ### 测试策略总结
936
-
937
- ```
938
- ┌────────────────────────────────────────┐
939
- │ 测试金字塔 │
940
- │ │
941
- │ △ UI 测试 (10%) │
942
- │ ╱ ╲ │
943
- │ ╱ ╲ 组合测试 (20%) │
944
- │ ╱ ╲ │
945
- │ ╱───────╲ │
946
- │ ╱ ╲ Store + Service 测试 (70%) │
947
- │╱═══════════╲ │
948
- │ │
949
- │ Store 测试:测试状态变化逻辑 │
950
- │ 组合测试:测试 Service + UI 集成 │
951
- │ UI 测试:测试 UI 交互 │
952
- └────────────────────────────────────────┘
953
- ```
954
-
955
- ---
956
-
957
- ## 💎 最佳实践
958
-
959
- ### 1. ✅ Service 继承 StoreInterface
960
-
961
- ```typescript
962
- // ✅ 好的做法:Service 继承 StoreInterface
963
- @injectable()
964
- export class UserService extends StoreInterface<UserState> {
965
- constructor() {
966
- super(() => ({
967
- user: null,
968
- loading: false
969
- }));
970
- }
971
-
972
- async login(username: string, password: string) {
973
- this.emit({ ...this.state, loading: true });
974
- // ...
975
- }
976
- }
977
-
978
- // ❌ 不好的做法:Service 不继承 StoreInterface
979
- @injectable()
980
- export class UserService {
981
- private user: UserInfo | null = null;
982
-
983
- // 问题:UI 无法订阅状态
984
- }
985
- ```
986
-
987
- ### 2. ✅ 使用 emit 发布状态
988
-
989
- ```typescript
990
- // ✅ 好的做法:通过 emit 发布状态
991
- async login(username: string, password: string) {
992
- this.emit({ ...this.state, loading: true });
993
-
994
- const response = await this.api.login({ username, password });
995
-
996
- this.emit({
997
- user: response.user,
998
- loading: false
999
- });
1000
- }
1001
-
1002
- // ❌ 不好的做法:直接修改 state
1003
- async login(username: string, password: string) {
1004
- this.state.loading = true; // ❌ 不会通知订阅者
1005
-
1006
- const response = await this.api.login({ username, password });
1007
-
1008
- this.state.user = response.user; // ❌ 不会通知订阅者
1009
- }
1010
- ```
1011
-
1012
- ### 3. ✅ 使用 cloneState 简化更新
1013
-
1014
- ```typescript
1015
- // ✅ 好的做法:使用 cloneState
1016
- setUser(user: UserInfo) {
1017
- this.emit(this.cloneState({ user }));
1018
- }
1019
-
1020
- setLoading(loading: boolean) {
1021
- this.emit(this.cloneState({ loading }));
1022
- }
1023
-
1024
- // ⚠️ 也可以:手动展开
1025
- setUser(user: UserInfo) {
1026
- this.emit({ ...this.state, user });
1027
- }
1028
- ```
1029
-
1030
- ### 4. ✅ 定义选择器
1031
-
1032
- ```typescript
1033
- // ✅ 好的做法:定义选择器
1034
- @injectable()
1035
- export class UserService extends StoreInterface<UserState> {
1036
- selector = {
1037
- user: (state: UserState) => state.user,
1038
- loading: (state: UserState) => state.loading,
1039
- isLoggedIn: (state: UserState) => state.user !== null
1040
- };
1041
- }
1042
-
1043
- // 使用
1044
- const isLoggedIn = useStore(userService, userService.selector.isLoggedIn);
1045
-
1046
- // ❌ 不好的做法:内联选择器
1047
- const isLoggedIn = useStore(userService, (state) => state.user !== null);
1048
- // 问题:每次渲染都创建新函数
1049
- ```
1050
-
1051
- ### 5. ✅ 使用选择器优化性能
1052
-
1053
- ```typescript
1054
- // ✅ 好的做法:只订阅需要的状态
1055
- function UserName() {
1056
- const userService = useIOC('UserServiceInterface');
1057
-
1058
- // 只订阅 user,loading 变化不会触发重新渲染
1059
- const user = useStore(userService, (state) => state.user);
1060
-
1061
- return <span>{user?.name}</span>;
1062
- }
1063
-
1064
- // ❌ 不好的做法:订阅完整状态
1065
- function UserName() {
1066
- const userService = useIOC('UserServiceInterface');
1067
-
1068
- // loading 变化也会触发重新渲染
1069
- const { user, loading } = useStore(userService);
1070
-
1071
- return <span>{user?.name}</span>;
1072
- }
1073
- ```
1074
-
1075
- ### 6. ✅ 状态保持不可变
1076
-
1077
- ```typescript
1078
- // ✅ 好的做法:创建新对象
1079
- updateUser(changes: Partial<UserInfo>) {
1080
- this.emit({
1081
- ...this.state,
1082
- user: {
1083
- ...this.state.user,
1084
- ...changes
1085
- }
1086
- });
1087
- }
1088
-
1089
- // ❌ 不好的做法:直接修改对象
1090
- updateUser(changes: Partial<UserInfo>) {
1091
- this.state.user.name = changes.name; // ❌ 直接修改
1092
- this.emit(this.state); // ❌ 引用相同,可能不触发更新
1093
- }
1094
- ```
1095
-
1096
- ### 7. ✅ 合理划分状态
1097
-
1098
- ```typescript
1099
- // ✅ 好的做法:每个 Service 管理自己的状态
1100
- class UserService extends StoreInterface<UserState> {
1101
- // 只管理用户相关状态
1102
- }
1103
-
1104
- class ThemeService extends StoreInterface<ThemeState> {
1105
- // 只管理主题相关状态
1106
- }
1107
-
1108
- class I18nService extends StoreInterface<I18nState> {
1109
- // 只管理国际化相关状态
1110
- }
1111
-
1112
- // ❌ 不好的做法:全局大 Store
1113
- class GlobalStore extends StoreInterface<GlobalState> {
1114
- // 包含所有状态:用户、主题、国际化等
1115
- // 问题:任何状态变化都会影响所有订阅者
1116
- }
1117
- ```
1118
-
1119
- ---
1120
-
1121
- ## ❓ 常见问题
1122
-
1123
- ### Q1: 为什么不用 Redux?
1124
-
1125
- **A:**
1126
-
1127
- | 特性 | Redux | Store (SliceStore) |
1128
- | ------------------- | ------------------------------------ | ------------------------- |
1129
- | **复杂度** | ❌ 高(Action, Reducer, Middleware) | ✅ 低(emit + subscribe) |
1130
- | **学习曲线** | ❌ 陡峭 | ✅ 平缓 |
1131
- | **TypeScript 支持** | ⚠️ 需要额外配置 | ✅ 原生支持 |
1132
- | **IOC 集成** | ⚠️ 需要额外工作 | ✅ 天然集成 |
1133
- | **性能** | ✅ 好 | ✅ 好 |
1134
- | **适用场景** | 大型应用 | 中小型应用 |
1135
-
1136
- **我们的选择:**
1137
-
1138
- - 项目已经使用 IOC,不需要 Redux 的全局状态管理
1139
- - 每个 Service 管理自己的状态,更清晰
1140
- - SliceStore 足够简单和强大
1141
-
1142
- ### Q2: Store 和 React Context 有什么区别?
1143
-
1144
- **A:**
1145
-
1146
- | 特性 | React Context | Store |
1147
- | ------------------- | ------------------------- | ------------------------- |
1148
- | **作用域** | 组件树 | 全局(通过 IOC) |
1149
- | **性能** | ⚠️ 任何值变化都会重新渲染 | ✅ 只有订阅的值变化才渲染 |
1150
- | **选择器** | ❌ 无 | ✅ 有 |
1151
- | **与 Service 集成** | ⚠️ 需要手动 | ✅ 天然集成 |
1152
-
1153
- **建议:**
1154
-
1155
- - 使用 Store 管理应用状态(Service 状态)
1156
- - 使用 Context 管理 UI 状态(如模态框、临时表单数据)
1157
-
1158
- ### Q3: 如何避免重复渲染?
1159
-
1160
- **A:** 使用选择器
1161
-
1162
- ```typescript
1163
- // ❌ 问题:订阅完整状态
1164
- const { user, loading, error } = useStore(userService);
1165
- // loading 变化会导致组件重新渲染
1166
-
1167
- // ✅ 解决:只订阅需要的状态
1168
- const user = useStore(userService, (state) => state.user);
1169
- // 只有 user 变化才会重新渲染
1170
- ```
1171
-
1172
- ### Q4: 可以在 Service 外部调用 emit 吗?
1173
-
1174
- **A:** 不建议
1175
-
1176
- ```typescript
1177
- // ❌ 不好的做法
1178
- function SomeComponent() {
1179
- const userService = useIOC('UserServiceInterface');
1180
-
1181
- // ❌ 直接调用 emit
1182
- userService.emit({ user: newUser, loading: false });
1183
- }
1184
-
1185
- // ✅ 好的做法:通过 Service 方法
1186
- function SomeComponent() {
1187
- const userService = useIOC('UserServiceInterface');
1188
-
1189
- // ✅ 调用 Service 方法
1190
- userService.setUser(newUser);
1191
- }
1192
-
1193
- // Service 中
1194
- @injectable()
1195
- export class UserService extends StoreInterface<UserState> {
1196
- setUser(user: UserInfo) {
1197
- this.emit(this.cloneState({ user }));
1198
- }
1199
- }
1200
- ```
1201
-
1202
- **原因:**
1203
-
1204
- - 保持封装性
1205
- - 方便测试
1206
- - 业务逻辑集中在 Service
1207
-
1208
- ### Q5: Store 状态更新不生效?
1209
-
1210
- **A:** 检查以下几点:
1211
-
1212
- ```typescript
1213
- // ❌ 常见错误 1:直接修改 state
1214
- this.state.loading = true; // 不会触发更新
1215
-
1216
- // ✅ 正确:使用 emit
1217
- this.emit({ ...this.state, loading: true });
1218
-
1219
- // ❌ 常见错误 2:没有创建新对象
1220
- const state = this.state;
1221
- state.loading = true;
1222
- this.emit(state); // 引用相同,可能不触发更新
1223
-
1224
- // ✅ 正确:创建新对象
1225
- this.emit({ ...this.state, loading: true });
1226
-
1227
- // ❌ 常见错误 3:忘记订阅
1228
- function MyComponent() {
1229
- const userService = useIOC('UserServiceInterface');
1230
- // 没有调用 useStore,无法接收更新
1231
-
1232
- return <div>{userService.getState().user?.name}</div>;
1233
- }
1234
-
1235
- // ✅ 正确:使用 useStore 订阅
1236
- function MyComponent() {
1237
- const userService = useIOC('UserServiceInterface');
1238
- const user = useStore(userService, (state) => state.user);
1239
-
1240
- return <div>{user?.name}</div>;
1241
- }
1242
- ```
1243
-
1244
- ### Q6: 如何在 Service 之间共享状态?
1245
-
1246
- **A:** 通过 IOC 注入
1247
-
1248
- ```typescript
1249
- // Service A
1250
- @injectable()
1251
- export class UserService extends StoreInterface<UserState> {
1252
- // ...
1253
- }
1254
-
1255
- // Service B 依赖 Service A
1256
- @injectable()
1257
- export class ProfileService {
1258
- constructor(
1259
- @inject('UserServiceInterface')
1260
- private userService: UserService
1261
- ) {}
1262
-
1263
- async updateProfile(data: ProfileData) {
1264
- // ✅ 访问 UserService 的状态
1265
- const user = this.userService.getState().user;
1266
-
1267
- // ✅ 也可以订阅 UserService 的状态
1268
- this.userService.subscribe((state) => {
1269
- console.log('User state changed:', state);
1270
- });
1271
- }
1272
- }
1273
- ```
1274
-
1275
- ---
1276
-
1277
- ## 📚 相关文档
1278
-
1279
- - [项目架构设计](./index.md) - 了解整体架构
1280
- - [IOC 容器](./ioc.md) - 依赖注入和 UI 分离
1281
- - [Bootstrap 启动器](./bootstrap.md) - 应用启动和初始化
1282
- - [测试指南](./test-guide.md) - 详细的测试策略
1283
-
1284
- ---
1285
-
1286
- ## 🎉 总结
1287
-
1288
- Store 状态管理的核心价值:
1289
-
1290
- 1. **解决通信问题** 📡 - 应用层通知 UI 层,同时保持分离
1291
- 2. **发布订阅模式** 🔔 - Service emit,UI useStore
1292
- 3. **自动更新 UI** ⚡ - 状态变化时,UI 自动重新渲染
1293
- 4. **保持解耦** 🔗 - Service 不知道有哪些 UI 在监听
1294
- 5. **易于测试** 🧪 - Store 可以独立测试
1295
- 6. **性能优化** 🚀 - 选择器只订阅需要的状态
1296
- 7. **类型安全** 🔒 - TypeScript 完整支持
1297
-
1298
- **记住核心模式:**
1299
-
1300
- ```typescript
1301
- // 1. Service 继承 StoreInterface
1302
- class MyService extends StoreInterface<MyState> {
1303
- // 2. 通过 emit 发布状态
1304
- doSomething() {
1305
- this.emit({ ...this.state, data: newData });
1306
- }
1307
- }
1308
-
1309
- // 3. UI 通过 useStore 订阅状态
1310
- function MyComponent() {
1311
- const myService = useIOC('MyServiceInterface');
1312
- const data = useStore(myService, (state) => state.data);
1313
-
1314
- return <div>{data}</div>;
1315
- }
1316
- ```
1317
-
1318
- **核心原则:**
1319
-
1320
- - ✅ Service 通过 emit 发布状态
1321
- - ✅ UI 通过 useStore 订阅状态
1322
- - ✅ 使用选择器优化性能
1323
- - ✅ 状态保持不可变
1324
- - ✅ 每个 Service 管理自己的状态
1325
-
1326
- ---
1327
-
1328
- **问题反馈:**
1329
- 如果你对 Store 状态管理有任何疑问或建议,请在团队频道中讨论或提交 Issue。