@qlover/create-app 0.0.1

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 (144) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/bin/create-app.js +28 -0
  3. package/dist/cjs/index.d.ts +67 -0
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/es/index.d.ts +67 -0
  6. package/dist/es/index.js +1 -0
  7. package/package.json +60 -0
  8. package/templates/fe-react/.env +3 -0
  9. package/templates/fe-react/README.md +50 -0
  10. package/templates/fe-react/config/app.common.ts +52 -0
  11. package/templates/fe-react/config/app.router.json +150 -0
  12. package/templates/fe-react/config/feapi.mock.json +14 -0
  13. package/templates/fe-react/config/i18n.ts +21 -0
  14. package/templates/fe-react/config/theme.json +90 -0
  15. package/templates/fe-react/eslint.config.js +31 -0
  16. package/templates/fe-react/index.html +13 -0
  17. package/templates/fe-react/lib/fe-react-controller/FeController.ts +15 -0
  18. package/templates/fe-react/lib/fe-react-controller/index.ts +2 -0
  19. package/templates/fe-react/lib/fe-react-controller/useController.ts +71 -0
  20. package/templates/fe-react/lib/fe-react-theme/ThemeController.ts +40 -0
  21. package/templates/fe-react/lib/fe-react-theme/ThemeStateGetter.ts +53 -0
  22. package/templates/fe-react/lib/fe-react-theme/index.ts +3 -0
  23. package/templates/fe-react/lib/fe-react-theme/tw-generator.js +239 -0
  24. package/templates/fe-react/lib/fe-react-theme/type.ts +21 -0
  25. package/templates/fe-react/lib/openAiApi/OpenAIAuthPlugin.ts +29 -0
  26. package/templates/fe-react/lib/openAiApi/OpenAIClient.ts +51 -0
  27. package/templates/fe-react/lib/openAiApi/StreamProcessor.ts +81 -0
  28. package/templates/fe-react/lib/openAiApi/index.ts +3 -0
  29. package/templates/fe-react/lib/request-common-plugin/index.ts +169 -0
  30. package/templates/fe-react/lib/tw-root10px/index.css +4 -0
  31. package/templates/fe-react/lib/tw-root10px/index.js +178 -0
  32. package/templates/fe-react/package.json +49 -0
  33. package/templates/fe-react/postcss.config.js +6 -0
  34. package/templates/fe-react/public/locales/en/about.json +3 -0
  35. package/templates/fe-react/public/locales/en/common.json +6 -0
  36. package/templates/fe-react/public/locales/en/executor.json +6 -0
  37. package/templates/fe-react/public/locales/en/home.json +10 -0
  38. package/templates/fe-react/public/locales/en/jsonStorage.json +11 -0
  39. package/templates/fe-react/public/locales/en/login.json +7 -0
  40. package/templates/fe-react/public/locales/en/request.json +15 -0
  41. package/templates/fe-react/public/locales/zh/about.json +3 -0
  42. package/templates/fe-react/public/locales/zh/common.json +7 -0
  43. package/templates/fe-react/public/locales/zh/executor.json +7 -0
  44. package/templates/fe-react/public/locales/zh/home.json +10 -0
  45. package/templates/fe-react/public/locales/zh/jsonStorage.json +11 -0
  46. package/templates/fe-react/public/locales/zh/login.json +8 -0
  47. package/templates/fe-react/public/locales/zh/request.json +15 -0
  48. package/templates/fe-react/public/logo.svg +1 -0
  49. package/templates/fe-react/src/App.tsx +20 -0
  50. package/templates/fe-react/src/assets/react.svg +1 -0
  51. package/templates/fe-react/src/components/Loading.tsx +41 -0
  52. package/templates/fe-react/src/components/LocaleLink.tsx +42 -0
  53. package/templates/fe-react/src/components/ProcessProvider.tsx +41 -0
  54. package/templates/fe-react/src/components/ThemeSwitcher.tsx +29 -0
  55. package/templates/fe-react/src/containers/context/BaseRouteContext.ts +27 -0
  56. package/templates/fe-react/src/containers/context/BaseRouteProvider.tsx +11 -0
  57. package/templates/fe-react/src/containers/globals.ts +31 -0
  58. package/templates/fe-react/src/containers/index.ts +71 -0
  59. package/templates/fe-react/src/hooks/useLanguageGuard.ts +25 -0
  60. package/templates/fe-react/src/hooks/useStrictEffect.ts +29 -0
  61. package/templates/fe-react/src/main.tsx +15 -0
  62. package/templates/fe-react/src/pages/404.tsx +14 -0
  63. package/templates/fe-react/src/pages/500.tsx +14 -0
  64. package/templates/fe-react/src/pages/auth/Layout.tsx +14 -0
  65. package/templates/fe-react/src/pages/auth/Login.tsx +62 -0
  66. package/templates/fe-react/src/pages/auth/Register.tsx +3 -0
  67. package/templates/fe-react/src/pages/base/About.tsx +12 -0
  68. package/templates/fe-react/src/pages/base/Executor.tsx +38 -0
  69. package/templates/fe-react/src/pages/base/Home.tsx +78 -0
  70. package/templates/fe-react/src/pages/base/JSONStorage.tsx +124 -0
  71. package/templates/fe-react/src/pages/base/Layout.tsx +17 -0
  72. package/templates/fe-react/src/pages/base/RedirectPathname.tsx +15 -0
  73. package/templates/fe-react/src/pages/base/Request.tsx +91 -0
  74. package/templates/fe-react/src/pages/base/components/BaseHeader.tsx +19 -0
  75. package/templates/fe-react/src/pages/index.tsx +108 -0
  76. package/templates/fe-react/src/services/controllers/ExecutorController.ts +56 -0
  77. package/templates/fe-react/src/services/controllers/JSONStorageController.ts +42 -0
  78. package/templates/fe-react/src/services/controllers/RequestController.ts +105 -0
  79. package/templates/fe-react/src/services/controllers/RouterController.ts +90 -0
  80. package/templates/fe-react/src/services/controllers/UserController.ts +146 -0
  81. package/templates/fe-react/src/services/feApi/FeApi.ts +51 -0
  82. package/templates/fe-react/src/services/feApi/FeApiMockPlugin.ts +42 -0
  83. package/templates/fe-react/src/services/feApi/FeApiType.ts +55 -0
  84. package/templates/fe-react/src/services/feApi/index.ts +2 -0
  85. package/templates/fe-react/src/services/i18n/index.ts +50 -0
  86. package/templates/fe-react/src/services/pageProcesser/PageProcesser.ts +29 -0
  87. package/templates/fe-react/src/services/pageProcesser/index.ts +1 -0
  88. package/templates/fe-react/src/styles/css/index.css +2 -0
  89. package/templates/fe-react/src/styles/css/page.css +3 -0
  90. package/templates/fe-react/src/styles/css/tailwind.css +3 -0
  91. package/templates/fe-react/src/types/Page.ts +49 -0
  92. package/templates/fe-react/src/types/UIDependenciesInterface.ts +31 -0
  93. package/templates/fe-react/src/types/global.d.ts +7 -0
  94. package/templates/fe-react/src/utils/RequestLogger.ts +34 -0
  95. package/templates/fe-react/src/utils/datetime.ts +25 -0
  96. package/templates/fe-react/src/utils/thread.ts +3 -0
  97. package/templates/fe-react/src/vite-env.d.ts +1 -0
  98. package/templates/fe-react/tailwind.config.js +18 -0
  99. package/templates/fe-react/tsconfig.app.json +29 -0
  100. package/templates/fe-react/tsconfig.json +7 -0
  101. package/templates/fe-react/tsconfig.node.json +22 -0
  102. package/templates/fe-react/vite.config.ts +32 -0
  103. package/templates/pack-app/.editorconfig +23 -0
  104. package/templates/pack-app/.env +5 -0
  105. package/templates/pack-app/.env.template +6 -0
  106. package/templates/pack-app/.gitattributes +2 -0
  107. package/templates/pack-app/.github/workflows/general-check.yml +50 -0
  108. package/templates/pack-app/.github/workflows/release.yml.template +110 -0
  109. package/templates/pack-app/.prettierignore +5 -0
  110. package/templates/pack-app/.prettierrc.js +3 -0
  111. package/templates/pack-app/CHANGELOG.md +0 -0
  112. package/templates/pack-app/README.md +1 -0
  113. package/templates/pack-app/eslint.config.js +77 -0
  114. package/templates/pack-app/fe-config.json +10 -0
  115. package/templates/pack-app/jest.config.js +31 -0
  116. package/templates/pack-app/package.json +66 -0
  117. package/templates/pack-app/packages/browser/__tests__/calc.test.ts +9 -0
  118. package/templates/pack-app/packages/browser/package.json +11 -0
  119. package/templates/pack-app/packages/browser/rollup.config.js +70 -0
  120. package/templates/pack-app/packages/browser/src/calc.ts +3 -0
  121. package/templates/pack-app/packages/browser/src/index.ts +1 -0
  122. package/templates/pack-app/packages/browser/tsconfig.json +15 -0
  123. package/templates/pack-app/packages/node/__tests__/readJson.test.ts +25 -0
  124. package/templates/pack-app/packages/node/package.json +11 -0
  125. package/templates/pack-app/packages/node/rollup.config.js +89 -0
  126. package/templates/pack-app/packages/node/src/index.ts +7 -0
  127. package/templates/pack-app/packages/node/src/readJson.ts +6 -0
  128. package/templates/pack-app/packages/node/tsconfig.json +17 -0
  129. package/templates/pack-app/packages/react-vite-lib/README.md +50 -0
  130. package/templates/pack-app/packages/react-vite-lib/__tests__/Sum.test.ts +9 -0
  131. package/templates/pack-app/packages/react-vite-lib/__tests__/Text.test.tsx +11 -0
  132. package/templates/pack-app/packages/react-vite-lib/eslint.config.js +28 -0
  133. package/templates/pack-app/packages/react-vite-lib/index.html +13 -0
  134. package/templates/pack-app/packages/react-vite-lib/package.json +30 -0
  135. package/templates/pack-app/packages/react-vite-lib/public/vite.svg +1 -0
  136. package/templates/pack-app/packages/react-vite-lib/src/calc.ts +3 -0
  137. package/templates/pack-app/packages/react-vite-lib/src/commponents/Text.tsx +7 -0
  138. package/templates/pack-app/packages/react-vite-lib/src/index.ts +2 -0
  139. package/templates/pack-app/packages/react-vite-lib/src/vite-env.d.ts +1 -0
  140. package/templates/pack-app/packages/react-vite-lib/tsconfig.json +25 -0
  141. package/templates/pack-app/packages/react-vite-lib/vite.config.ts +24 -0
  142. package/templates/pack-app/pnpm-workspace.yaml +2 -0
  143. package/templates/pack-app/tsconfig.json +9 -0
  144. package/templates/pack-app/tsconfig.test.json +10 -0
@@ -0,0 +1,169 @@
1
+ import { get, isUndefined, set } from 'lodash';
2
+ import {
3
+ ExecutorPlugin,
4
+ RequestAdapterConfig,
5
+ ExecutorContext,
6
+ RequestAdapterResponse
7
+ } from '@qlover/fe-utils';
8
+
9
+ export type RequestCommonPluginConfig = {
10
+ token?: string | (() => string | null);
11
+
12
+ /**
13
+ * token prefix.
14
+ *
15
+ * eg. `Bearer xxx`, `Token xxx`
16
+ */
17
+ tokenPrefix?: string;
18
+
19
+ /**
20
+ * @default `Authorization`
21
+ */
22
+ authKey?: string;
23
+
24
+ /**
25
+ * default request headers
26
+ */
27
+ defaultHeaders?: Record<string, string>;
28
+
29
+ /**
30
+ * Whether the token is required.
31
+ *
32
+ * if not token provided, throw error.
33
+ *
34
+ * @default `false`
35
+ */
36
+ requiredToken?: boolean;
37
+
38
+ /**
39
+ * defualt request data
40
+ */
41
+ defaultRequestData?: Record<string, unknown>;
42
+
43
+ /**
44
+ * transform request data before sending.
45
+ *
46
+ * @param data - request data
47
+ * @param context - executor context
48
+ * @returns - request data
49
+ */
50
+ requestDataSerializer?: (
51
+ data: unknown,
52
+ context: ExecutorContext<RequestAdapterConfig>
53
+ ) => unknown;
54
+ };
55
+
56
+ /**
57
+ * Represents a plugin for handling common request configurations and behaviors.
58
+ *
59
+ * The RequestCommonPlugin is designed to manage request headers, token handling,
60
+ * and data serialization before sending requests. It ensures that the necessary
61
+ * configurations are applied to each request, enhancing the flexibility and
62
+ * reusability of request handling in the application.
63
+ *
64
+ * Main functions include:
65
+ * - Appending default headers and token to requests.
66
+ * - Merging default request data with provided parameters.
67
+ * - Serializing request data before sending.
68
+ *
69
+ * @example
70
+ * const plugin = new RequestCommonPlugin({
71
+ * token: 'your-token',
72
+ * tokenPrefix: 'Bearer',
73
+ * defaultHeaders: { 'Custom-Header': 'value' },
74
+ * requiredToken: true,
75
+ * });
76
+ */
77
+ export class RequestCommonPlugin
78
+ implements ExecutorPlugin<RequestAdapterConfig>
79
+ {
80
+ readonly pluginName = 'RequestCommonPlugin';
81
+
82
+ constructor(readonly config: RequestCommonPluginConfig = {}) {
83
+ if (config.requiredToken && !config.token) {
84
+ throw new Error('Token is required!');
85
+ }
86
+ }
87
+
88
+ onBefore(context: ExecutorContext<RequestAdapterConfig>): void {
89
+ const {
90
+ tokenPrefix,
91
+ defaultHeaders,
92
+ authKey = 'Authorization',
93
+ defaultRequestData,
94
+ requestDataSerializer
95
+ } = this.config;
96
+ const { parameters } = context;
97
+
98
+ // append default headers
99
+ parameters.headers = {
100
+ ...defaultHeaders,
101
+ ...parameters.headers
102
+ };
103
+
104
+ // append content type header
105
+ if (
106
+ !parameters.headers['Content-Type'] &&
107
+ parameters.responseType === 'json'
108
+ ) {
109
+ parameters.headers['Content-Type'] = 'application/json';
110
+ }
111
+
112
+ // append token
113
+ if (authKey && !parameters.headers[authKey]) {
114
+ const authToken = this.getAuthToken();
115
+ const authValue = tokenPrefix ? `${tokenPrefix} ${authToken}` : authToken;
116
+ if (authValue) {
117
+ parameters.headers[authKey] = authValue;
118
+ }
119
+ }
120
+
121
+ // merge defaults request data
122
+ if (defaultRequestData) {
123
+ Object.entries(defaultRequestData).forEach(([key, value]) => {
124
+ const prevValue = get(parameters.data, key);
125
+ if (isUndefined(prevValue)) {
126
+ set(parameters.data as Record<string, unknown>, key, value);
127
+ }
128
+ });
129
+ }
130
+
131
+ // serialize request data
132
+ if (requestDataSerializer && parameters.data) {
133
+ parameters.data = requestDataSerializer(parameters.data, context);
134
+ }
135
+ }
136
+
137
+ async onSuccess(
138
+ context: ExecutorContext<RequestAdapterConfig>
139
+ ): Promise<void> {
140
+ const { parameters, returnValue } = context;
141
+ const response = (returnValue as RequestAdapterResponse<unknown, Response>)
142
+ .data;
143
+
144
+ if (response instanceof Response) {
145
+ switch (parameters.responseType) {
146
+ case 'json':
147
+ context.returnValue = await response.json();
148
+ break;
149
+ case 'text':
150
+ context.returnValue = await response.text();
151
+ break;
152
+ case 'blob':
153
+ context.returnValue = await response.blob();
154
+ break;
155
+ // FIXME: adapter support `arraybuffer`
156
+ // @ts-expect-error
157
+ case 'arrayBuffer':
158
+ case 'arraybuffer':
159
+ context.returnValue = await response.arrayBuffer();
160
+ break;
161
+ }
162
+ }
163
+ }
164
+
165
+ getAuthToken(): string {
166
+ const { token } = this.config;
167
+ return typeof token === 'function' ? (token() ?? '') : (token ?? '');
168
+ }
169
+ }
@@ -0,0 +1,4 @@
1
+ :root,
2
+ html {
3
+ font-size: 10px;
4
+ }
@@ -0,0 +1,178 @@
1
+ import plugin from 'tailwindcss/plugin';
2
+
3
+ /**
4
+ * Tailwind CSS 配置 - 10px 根字体大小
5
+ *
6
+ * 注意事项:
7
+ * 1. 所有的 rem 值都基于 10px 计算
8
+ * 2. 16px = 1.6rem
9
+ * 3. spacing 中的值需要乘以 1.6 来保持与原来的单位一致
10
+ *
11
+ * 由于移动端开发需求,方便计算,重置像素 10px, 将默认 16px 变成 10px
12
+ *
13
+ * 但是 tailwind 中的单位间距都是有个 rem 做单位, 16px 变成 10px 基数改变, 需要在原来不变像素上用 10px 基数重写一遍
14
+ *
15
+ * @see https://redpixelthemes.com/blog/change-tailwindcss-base-font-size/
16
+ * @type {import('tailwindcss').Config['theme']}
17
+ *
18
+ * @example
19
+ * // 使用示例
20
+ * text-base -> 1.6rem -> 16px
21
+ * p-4 -> 1.6rem -> 16px
22
+ * w-40 -> 16rem -> 160px
23
+ */
24
+ const root10pxThemes = {
25
+ lineHeight: {
26
+ 3: '1.2rem',
27
+ 4: '1.6rem',
28
+ 5: '2.0rem',
29
+ 6: '2.4rem',
30
+ 7: '2.8rem',
31
+ 8: '3.2rem',
32
+ 9: '3.6rem',
33
+ 10: '4.0rem',
34
+ none: 1,
35
+ tight: 1.25,
36
+ snug: 1.375,
37
+ normal: 1.5,
38
+ relaxed: 1.625,
39
+ loose: 2
40
+ },
41
+ maxWidth: {
42
+ xs: '32.0rem',
43
+ sm: '38.4rem',
44
+ md: '44.8rem',
45
+ lg: '51.2rem',
46
+ xl: '57.6rem',
47
+ '2xl': '67.2rem',
48
+ '3xl': '76.8rem',
49
+ '4xl': '89.6rem',
50
+ '5xl': '102.4rem',
51
+ '6xl': '115.2rem',
52
+ '7xl': '128.0rem'
53
+ },
54
+
55
+ //
56
+ // 为了兼容之前的写法,在10的倍数上以 16px 作为基准单位计算
57
+ // 默认是以 4的倍数作为基数计算间距,但是由于 html font-size 变为了 10px
58
+ // 现在版本从之前的16px -> 10px 整体缩小,所以,这里需要手动将间距放大,去适应以前的环境
59
+ // TODO: 后期将所有间距调整为基准值, 不使用放大值
60
+
61
+ /**
62
+ * 因为之前版本 root size 为默认 16px, tailwind 默认就以 16px 为基准设置 spacing
63
+ *
64
+ * 而现在版本 root size 为 10px, 为了兼容以前代码, 将所有 spacing 重写
65
+ *
66
+ * @see https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale
67
+ *
68
+ * 例如:
69
+ * 以前版本 1 -> 4px -> 0.25rem, 而现在 1 -> 4px -> 0.1rem
70
+ * 以前版本 4 -> 16px -> 1rem 而现在 4 -> 16px -> 1.6rem
71
+ *
72
+ * 注意只是 spaceing 使用这规则,也就是将默认的 px 间距用 10 重新换算一遍,间距并未改变
73
+ */
74
+ spacing: {
75
+ 0.5: '0.2rem',
76
+ 1: '0.4rem',
77
+ 1.5: '0.6rem',
78
+ 2: '0.8rem',
79
+ 2.5: '1rem',
80
+ 3: '1.2rem',
81
+ 3.5: '1.4rem',
82
+ 4: '1.6rem',
83
+ 5: '2rem',
84
+ 6: '2.4rem',
85
+ 7: '2.8rem',
86
+ 8: '3.2rem',
87
+ 9: '3.6rem',
88
+ 10: '4rem',
89
+ 11: '4.4rem',
90
+ 12: '4.8rem',
91
+ 14: '5.6rem',
92
+ 16: '6.4rem',
93
+ 20: '8rem',
94
+ 24: '9.6rem',
95
+ 28: '11.2rem',
96
+ 32: '12.8rem',
97
+ 36: '14.4rem',
98
+ 40: '16rem',
99
+ 44: '17.6rem',
100
+ 48: '19.2rem',
101
+ 52: '20.8rem',
102
+ 56: '22.4rem',
103
+ 60: '24rem',
104
+ 64: '25.6rem',
105
+ 72: '28.8rem',
106
+ 80: '32rem',
107
+ 96: '38.4rem'
108
+ },
109
+
110
+ /**
111
+ * 字体对应 font-size 和 line-height
112
+ *
113
+ * @see https://tailwindcss.com/docs/font-size
114
+ */
115
+ fontSize: {
116
+ xxs: ['2rem', '1.2rem'],
117
+ xs: ['1.2rem', '1.6rem'],
118
+ sm: ['1.4rem', '2rem'],
119
+ base: ['1.6rem', '2.4rem'],
120
+ lg: ['1.8rem', '2.8rem'],
121
+ xl: ['2rem', '2.8rem'],
122
+ '2xl': ['2.4rem', '3.2rem'],
123
+ '3xl': ['3rem', '3.6rem'],
124
+ '4xl': ['3.6rem', '4rem'],
125
+ '5xl': ['4.8rem', 1],
126
+ '6xl': ['6rem', 1],
127
+ '7xl': ['7.2rem', 1],
128
+ '8xl': ['9.6rem', 1],
129
+ '9xl': ['12.8rem', 1],
130
+ 12: ['1.2rem'],
131
+ 14: ['1.4rem'],
132
+ 16: ['1.6rem'],
133
+ 18: ['1.8rem'],
134
+ 20: ['2rem'],
135
+ 22: ['2.2rem'],
136
+ 24: ['2.4rem'],
137
+ 26: ['2.6rem'],
138
+ 32: ['3.2rem'],
139
+ 34: ['3.4rem'],
140
+ 36: ['3.6rem'],
141
+ 40: ['4rem'],
142
+ 46: ['4.6rem'],
143
+ 50: ['5rem'],
144
+ 54: ['5.4rem'],
145
+ 60: ['6rem'],
146
+ 70: ['7rem'],
147
+ 80: ['8rem'],
148
+ 90: ['9rem'],
149
+ 100: ['10rem']
150
+ },
151
+
152
+ screens: {
153
+ /** @media (min-width: 375px) { ... } */
154
+ xs: '375px',
155
+ /** @media (min-width: 640px) { ... } */
156
+ sm: '640px',
157
+ /** @media (min-width: 768px) { ... } */
158
+ md: '768px',
159
+ /** @media (min-width: 1024px) { ... } */
160
+ lg: '1024px',
161
+ /** @media (min-width: 1280px) { ... } */
162
+ xl: '1280px',
163
+ /** @media (min-width: 1536px) { ... } */
164
+ '2xl': '1536px',
165
+ /** @media (min-width: 1640px) { ... } */
166
+ '3xl': '1640px',
167
+ /** @media (min-width: 1920px) { ... } */
168
+ maxxl: '1920px'
169
+ }
170
+ };
171
+
172
+ const root10pxPlugin = plugin(({ addBase }) => {
173
+ addBase({
174
+ html: { fontSize: '10px' }
175
+ });
176
+ });
177
+
178
+ export default { themes: root10pxThemes, plugin: root10pxPlugin };
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "fe-react",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "lint": "eslint .",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@qlover/fe-utils": "^1.0.19",
14
+ "@qlover/slice-store-react": "^1.0.8",
15
+ "i18next": "^24.2.0",
16
+ "i18next-browser-languagedetector": "^8.0.2",
17
+ "i18next-http-backend": "^3.0.1",
18
+ "lodash": "^4.17.21",
19
+ "react": "^18.3.1",
20
+ "react-dom": "^18.3.1",
21
+ "react-i18next": "^15.2.0",
22
+ "react-router-dom": "6"
23
+ },
24
+ "devDependencies": {
25
+ "@eslint/js": "^9.11.1",
26
+ "@qlover/eslint-plugin-fe-dev": "^0.2.0",
27
+ "@qlover/fe-scripts": "latest",
28
+ "@rollup/plugin-alias": "^5.1.1",
29
+ "@types/lodash": "^4.17.13",
30
+ "@types/react": "^18.3.11",
31
+ "@types/react-dom": "^18.3.0",
32
+ "@types/react-syntax-highlighter": "^15.5.13",
33
+ "@types/typo-js": "^1.2.2",
34
+ "@vitejs/plugin-react": "^4.3.2",
35
+ "@vitejs/plugin-react-swc": "^3.5.0",
36
+ "autoprefixer": "^10.4.20",
37
+ "eslint": "^9.15.0",
38
+ "eslint-plugin-react-hooks": "^5.0.0",
39
+ "eslint-plugin-react-refresh": "^0.4.14",
40
+ "globals": "^15.12.0",
41
+ "postcss": "^8.4.49",
42
+ "sass-embedded": "^1.79.4",
43
+ "tailwindcss": "^3.4.16",
44
+ "typescript": "^5.6.3",
45
+ "typescript-eslint": "^8.15.0",
46
+ "vite": "^5.4.8",
47
+ "vite-plugin-cross-origin-isolation": "^0.1.6"
48
+ }
49
+ }
@@ -0,0 +1,6 @@
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "title": "About"
3
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "404.notComponent": "Component not found",
3
+ "404.notPage": "Page not found",
4
+ "500.title": "Server Error",
5
+ "header.theme.label": "Switch Theme"
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "executorDemo": "Executor Demo",
3
+ "requestTimeout": "Request Timeout",
4
+ "executorTestPlugin": "Executor Test Plugin",
5
+ "testPlugin": "Test Plugin"
6
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "welcome": "Welcome to the home page",
3
+ "description": "A modern frontend utility library collection providing various practical tools and components",
4
+ "about": "About Us",
5
+ "about_description": "Learn more about our project and team information",
6
+ "jsonstorage": "JSONStorage",
7
+ "jsonstorage_description": "Efficient JSON data storage solution",
8
+ "request": "Request",
9
+ "request_description": "A modern frontend utility library collection providing various practical tools and components"
10
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "title": "JSONStorage Demo",
3
+ "title2": "Permanent storage test",
4
+ "title3": "Expire time test",
5
+ "title4": "Request Timeout",
6
+ "format.title": "Test key: ${key}, Random value range: ${min}~${max}",
7
+ "setRandomValue": "Set random value",
8
+ "currentValue": "Current value",
9
+ "ms": "ms",
10
+ "setExpireTime": "Set random value(with expire time)"
11
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "title": "Login",
3
+ "email": "Email",
4
+ "username": "Username",
5
+ "password": "Password",
6
+ "login": "Login"
7
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "requestTimeout": "Request Timeout",
3
+ "helloResult": "Hello result is",
4
+ "helloError": "Hello error is",
5
+ "ipInfoResult": "IpInfo result is",
6
+ "ipInfo": "IpInfo",
7
+ "randomUser": "RandomUser",
8
+ "loading": "Loading...",
9
+ "randomUserResult": "RandomUser result is",
10
+ "randomUserError": "RandomUser error is",
11
+ "triggerAbortRequest": "Trigger Abort Request",
12
+ "stopAbortRequest": "Stop Abort Request",
13
+ "abortRequestResult": "Abort Request Result",
14
+ "abortRequestError": "Abort Request Error"
15
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "title": "关于我们"
3
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "404.notComponent": "组件不存在",
3
+ "404.notPage": "页面不存在",
4
+ "500.title": "服务器错误",
5
+ "header.theme.label": "切换主题"
6
+ }
7
+
@@ -0,0 +1,7 @@
1
+ {
2
+ "executorDemo": "执行器 Demo",
3
+ "requestTimeout": "请求超时",
4
+ "executorTestPlugin": "执行器 测试插件",
5
+ "testPlugin": "测试插件"
6
+ }
7
+
@@ -0,0 +1,10 @@
1
+ {
2
+ "welcome": "欢迎来到主页",
3
+ "description": "一个现代前端实用库集合,提供各种实用工具和组件",
4
+ "about": "关于我们",
5
+ "about_description": "了解更多关于我们的项目和团队信息",
6
+ "jsonstorage": "JSON存储",
7
+ "jsonstorage_description": "高效的JSON数据存储解决方案",
8
+ "request": "请求",
9
+ "request_description": "一个现代前端实用库集合,提供各种实用工具和组件"
10
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "title": "JSONStorage Demo",
3
+ "title2": "永久存储测试",
4
+ "title3": "过期时间测试",
5
+ "title4": "请求超时时间设置",
6
+ "format.title": "测试 key: ${key}, 随机值范围: ${min}~${max}",
7
+ "setRandomValue": "设置随机值",
8
+ "currentValue": "当前值",
9
+ "ms": "毫秒",
10
+ "setExpireTime": "设置随机值(带过期时间)"
11
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "title": "登录",
3
+ "email": "邮箱",
4
+ "username": "用户名",
5
+ "password": "密码",
6
+ "login": "登录"
7
+ }
8
+
@@ -0,0 +1,15 @@
1
+ {
2
+ "requestTimeout": "请求超时",
3
+ "helloResult": "Hello 结果是",
4
+ "helloError": "Hello 错误是",
5
+ "ipInfoResult": "IpInfo 结果是",
6
+ "ipInfo": "IpInfo",
7
+ "randomUser": "随机用户",
8
+ "loading": "加载中...",
9
+ "randomUserResult": "随机用户 结果是",
10
+ "randomUserError": "随机用户 错误是",
11
+ "triggerAbortRequest": "触发中止请求",
12
+ "stopAbortRequest": "停止中止请求",
13
+ "abortRequestResult": "中止请求 结果是",
14
+ "abortRequestError": "中止请求 错误是"
15
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path><text x="50" y="50" font-family="Arial" font-size="24" fill="black">fe</text></svg>
@@ -0,0 +1,20 @@
1
+ import './styles/css/index.css';
2
+ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
3
+ import { createFeReactRoutes } from './pages';
4
+ import { I18nService } from '@/services/i18n';
5
+ import { useMemo } from 'react';
6
+ import { routerController } from './containers';
7
+
8
+ I18nService.init();
9
+
10
+ function App() {
11
+ const routerBase = useMemo(() => {
12
+ const routes = createFeReactRoutes(routerController.getRoutes());
13
+ const router = createBrowserRouter(routes);
14
+ return router;
15
+ }, []);
16
+
17
+ return <RouterProvider router={routerBase} />;
18
+ }
19
+
20
+ export default App;
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Loading component
3
+ *
4
+ * @description This component displays a loading indicator. It can be used to indicate loading states either in fullscreen mode or within a specific section of the page.
5
+ *
6
+ * @param {boolean} fullscreen - Determines if the loading indicator should cover the entire screen. Defaults to false.
7
+ *
8
+ * @returns {JSX.Element} A div element displaying a loading icon.
9
+ *
10
+ * @example
11
+ * <Loading fullscreen={true} />
12
+ * <Loading fullscreen={false} />
13
+ */
14
+ export function Loading({ fullscreen = false }: { fullscreen?: boolean }) {
15
+ return (
16
+ <div
17
+ className={`flex justify-center items-center ${fullscreen ? 'fixed inset-0 bg-white bg-opacity-80 z-50' : 'relative'}`}
18
+ >
19
+ <svg
20
+ className="animate-spin h-8 w-8 text-gray-500"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ fill="none"
23
+ viewBox="0 0 24 24"
24
+ >
25
+ <circle
26
+ className="opacity-25"
27
+ cx="12"
28
+ cy="12"
29
+ r="10"
30
+ stroke="currentColor"
31
+ strokeWidth="4"
32
+ ></circle>
33
+ <path
34
+ className="opacity-75"
35
+ fill="currentColor"
36
+ d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
37
+ ></path>
38
+ </svg>
39
+ </div>
40
+ );
41
+ }
@@ -0,0 +1,42 @@
1
+ import { LinkProps, Link as RouterLink, To, useParams } from 'react-router-dom';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface LocaleLinkProps extends Omit<LinkProps, 'href' | 'to'> {
5
+ href: string | To;
6
+ locale?: string;
7
+ children: ReactNode;
8
+ defaultLocale?: string;
9
+ className?: string;
10
+ }
11
+
12
+ const LocaleLink: React.FC<LocaleLinkProps> = ({
13
+ href,
14
+ locale,
15
+ children,
16
+ defaultLocale,
17
+ ...props
18
+ }) => {
19
+ const { lng } = useParams<{ lng: string }>();
20
+
21
+ locale = locale || lng;
22
+
23
+ const isDefaultLocale = locale === defaultLocale;
24
+
25
+ let localizedHref: string | To;
26
+ if (typeof href === 'string') {
27
+ localizedHref = isDefaultLocale ? href : `/${locale}${href}`;
28
+ } else {
29
+ localizedHref = {
30
+ ...href,
31
+ pathname: isDefaultLocale ? href.pathname : `/${locale}${href.pathname}`
32
+ };
33
+ }
34
+
35
+ return (
36
+ <RouterLink {...props} to={localizedHref}>
37
+ {children}
38
+ </RouterLink>
39
+ );
40
+ };
41
+
42
+ export default LocaleLink;