generator-mico-cli 0.2.31 → 0.2.32
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.
- package/README.md +145 -18
- package/bin/mico.js +76 -0
- package/generators/h5-react/ignore-list.json +1 -0
- package/generators/h5-react/index.js +349 -0
- package/generators/h5-react/meta.json +11 -0
- package/generators/h5-react/templates/.commitlintrc.js +7 -0
- package/generators/h5-react/templates/.cursor/rules/cicd-deploy.mdc +104 -0
- package/generators/h5-react/templates/.cursor/rules/common-intl.mdc +42 -0
- package/generators/h5-react/templates/.cursor/rules/git-hooks.mdc +40 -0
- package/generators/h5-react/templates/.cursor/rules/internal-packages.mdc +46 -0
- package/generators/h5-react/templates/.cursor/rules/monorepo.mdc +64 -0
- package/generators/h5-react/templates/.cursor/rules/package-json.mdc +52 -0
- package/generators/h5-react/templates/.cursor/rules/tailwind-umi.mdc +60 -0
- package/generators/h5-react/templates/.cursor/rules/umi-app.mdc +74 -0
- package/generators/h5-react/templates/.cursor/rules/umi-config.mdc +86 -0
- package/generators/h5-react/templates/.cursor/rules/umi-mock.mdc +80 -0
- package/generators/h5-react/templates/.cursor/rules/workspace-request.mdc +52 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/SKILL.md +213 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/evals/evals.json +23 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/cursor-rule-template.md +60 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-1-scanning.md +102 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-2-context-analysis.md +102 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-3-pattern-extraction.md +105 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-4-module-mapping.md +65 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/phase-5-glossary.md +63 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/DEV_PATTERNS.tpl.md +77 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/GLOSSARY.tpl.md +17 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/MODULE_MAP.tpl.md +45 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/templates/PROJECT_CONTEXT.tpl.md +155 -0
- package/generators/h5-react/templates/.cursor/skills/biz-app-analyzer/references/update-mode.md +116 -0
- package/generators/h5-react/templates/.env.development +5 -0
- package/generators/h5-react/templates/.env.production +5 -0
- package/generators/h5-react/templates/.env.testing +5 -0
- package/generators/h5-react/templates/.husky/commit-msg +2 -0
- package/generators/h5-react/templates/.husky/pre-commit +2 -0
- package/generators/h5-react/templates/.lintstagedrc.js +8 -0
- package/generators/h5-react/templates/.prettierrc.json +7 -0
- package/generators/h5-react/templates/CICD/before_build.sh +76 -0
- package/generators/h5-react/templates/CICD/start_dev.sh +54 -0
- package/generators/h5-react/templates/CICD/start_local.sh +30 -0
- package/generators/h5-react/templates/CICD/start_prod.sh +53 -0
- package/generators/h5-react/templates/CICD/start_test.sh +55 -0
- package/generators/h5-react/templates/CICD/wangsu_fresh_dev.sh +19 -0
- package/generators/h5-react/templates/CICD/wangsu_fresh_prod.sh +19 -0
- package/generators/h5-react/templates/CICD/wangsu_fresh_test.sh +19 -0
- package/generators/h5-react/templates/README.md +301 -0
- package/generators/h5-react/templates/_gitignore +30 -0
- package/generators/h5-react/templates/_npmrc +6 -0
- package/generators/h5-react/templates/apps/.gitkeep +0 -0
- package/generators/h5-react/templates/dev.preset.json +10 -0
- package/generators/h5-react/templates/package.json +56 -0
- package/generators/h5-react/templates/packages/common-intl/README.md +180 -0
- package/generators/h5-react/templates/packages/common-intl/eslint.config.ts +12 -0
- package/generators/h5-react/templates/packages/common-intl/package.json +31 -0
- package/generators/h5-react/templates/packages/common-intl/src/index.ts +3 -0
- package/generators/h5-react/templates/packages/common-intl/src/intl.ts +100 -0
- package/generators/h5-react/templates/packages/common-intl/tsconfig.json +3 -0
- package/generators/h5-react/templates/packages/components/eslint.config.ts +12 -0
- package/generators/h5-react/templates/packages/components/package.json +32 -0
- package/generators/h5-react/templates/packages/components/src/Layout/ImmersiveHeader.tsx +126 -0
- package/generators/h5-react/templates/packages/components/src/Layout/LayoutFooter.tsx +72 -0
- package/generators/h5-react/templates/packages/components/src/Layout/index.tsx +121 -0
- package/generators/h5-react/templates/packages/components/src/assets/image/back.png +0 -0
- package/generators/h5-react/templates/packages/components/src/index.ts +0 -0
- package/generators/h5-react/templates/packages/components/tsconfig.json +13 -0
- package/generators/h5-react/templates/packages/components/typings.d.ts +1 -0
- package/generators/h5-react/templates/packages/constant/eslint.config.ts +12 -0
- package/generators/h5-react/templates/packages/constant/package.json +19 -0
- package/generators/h5-react/templates/packages/constant/src/index.ts +0 -0
- package/generators/h5-react/templates/packages/constant/src/member.ts +8 -0
- package/generators/h5-react/templates/packages/constant/tsconfig.json +3 -0
- package/generators/h5-react/templates/packages/deeplink/eslint.config.ts +12 -0
- package/generators/h5-react/templates/packages/deeplink/package.json +18 -0
- package/generators/h5-react/templates/packages/deeplink/src/index.ts +7 -0
- package/generators/h5-react/templates/packages/deeplink/tsconfig.json +3 -0
- package/generators/h5-react/templates/packages/domain/eslint.config.ts +12 -0
- package/generators/h5-react/templates/packages/domain/package.json +18 -0
- package/generators/h5-react/templates/packages/domain/src/index.ts +29 -0
- package/generators/h5-react/templates/packages/domain/tsconfig.json +3 -0
- package/generators/h5-react/templates/packages/domain/types.d.ts +11 -0
- package/generators/h5-react/templates/packages/eslint/eslint.config.base.ts +36 -0
- package/generators/h5-react/templates/packages/eslint/eslint.config.react.ts +33 -0
- package/generators/h5-react/templates/packages/eslint/package.json +22 -0
- package/generators/h5-react/templates/packages/js-bridge/eslint.config.ts +17 -0
- package/generators/h5-react/templates/packages/js-bridge/package.json +23 -0
- package/generators/h5-react/templates/packages/js-bridge/src/call.ts +126 -0
- package/generators/h5-react/templates/packages/js-bridge/src/closeH5Page.ts +9 -0
- package/generators/h5-react/templates/packages/js-bridge/src/getUserInfo.ts +96 -0
- package/generators/h5-react/templates/packages/js-bridge/src/index.ts +15 -0
- package/generators/h5-react/templates/packages/js-bridge/tsconfig.json +3 -0
- package/generators/h5-react/templates/packages/js-bridge/type.d.ts +24 -0
- package/generators/h5-react/templates/packages/request/axios.d.ts +42 -0
- package/generators/h5-react/templates/packages/request/eslint.config.ts +17 -0
- package/generators/h5-react/templates/packages/request/package.json +22 -0
- package/generators/h5-react/templates/packages/request/src/index.ts +165 -0
- package/generators/h5-react/templates/packages/request/src/interceptors.ts +126 -0
- package/generators/h5-react/templates/packages/request/src/types.ts +101 -0
- package/generators/h5-react/templates/packages/request/src/url-resolver.ts +66 -0
- package/generators/h5-react/templates/packages/request/src/utils.ts +12 -0
- package/generators/h5-react/templates/packages/request/tsconfig.json +3 -0
- package/generators/h5-react/templates/packages/request/umi.d.ts +94 -0
- package/generators/h5-react/templates/packages/typescript/package.json +11 -0
- package/generators/h5-react/templates/packages/typescript/tsconfig.base.json +23 -0
- package/generators/h5-react/templates/packages/typescript/tsconfig.react.json +7 -0
- package/generators/h5-react/templates/packages/umi-config/eslint.config.ts +12 -0
- package/generators/h5-react/templates/packages/umi-config/package.json +31 -0
- package/generators/h5-react/templates/packages/umi-config/src/config.dev.ts +34 -0
- package/generators/h5-react/templates/packages/umi-config/src/config.prod.development.ts +17 -0
- package/generators/h5-react/templates/packages/umi-config/src/config.prod.production.ts +42 -0
- package/generators/h5-react/templates/packages/umi-config/src/config.prod.testing.ts +17 -0
- package/generators/h5-react/templates/packages/umi-config/src/config.prod.ts +56 -0
- package/generators/h5-react/templates/packages/umi-config/src/config.ts +86 -0
- package/generators/h5-react/templates/packages/umi-config/src/index.ts +25 -0
- package/generators/h5-react/templates/packages/umi-config/src/plugins/apply-sentry-plugin.ts +57 -0
- package/generators/h5-react/templates/packages/umi-config/src/type.d.ts +3 -0
- package/generators/h5-react/templates/packages/umi-config/tsconfig.json +3 -0
- package/generators/h5-react/templates/packages/utils/eslint.config.ts +12 -0
- package/generators/h5-react/templates/packages/utils/package.json +27 -0
- package/generators/h5-react/templates/packages/utils/src/date.ts +21 -0
- package/generators/h5-react/templates/packages/utils/src/env.ts +40 -0
- package/generators/h5-react/templates/packages/utils/src/index.ts +3 -0
- package/generators/h5-react/templates/packages/utils/src/md5.ts +17 -0
- package/generators/h5-react/templates/packages/utils/src/mock.ts +83 -0
- package/generators/h5-react/templates/packages/utils/src/number.ts +23 -0
- package/generators/h5-react/templates/packages/utils/src/tailwind.ts +12 -0
- package/generators/h5-react/templates/packages/utils/src/url.ts +19 -0
- package/generators/h5-react/templates/packages/utils/tsconfig.json +9 -0
- package/generators/h5-react/templates/page.config.ts +1 -0
- package/generators/h5-react/templates/pnpm-workspace.yaml +17 -0
- package/generators/h5-react/templates/scripts/collect-dist.js +78 -0
- package/generators/h5-react/templates/scripts/dev-preset.js +265 -0
- package/generators/h5-react/templates/scripts/dev-preset.schema.json +39 -0
- package/generators/h5-react/templates/scripts/dev.js +133 -0
- package/generators/h5-react/templates/scripts/gateway.ts +241 -0
- package/generators/h5-react/templates/turbo.json +86 -0
- package/generators/subapp-h5/ignore-list.json +1 -0
- package/generators/subapp-h5/index.js +424 -0
- package/generators/subapp-h5/meta.json +10 -0
- package/generators/subapp-h5/templates/.env +1 -0
- package/generators/subapp-h5/templates/.stylelintrc.js +22 -0
- package/generators/subapp-h5/templates/config/config.dev.ts +7 -0
- package/generators/subapp-h5/templates/config/config.prod.development.ts +7 -0
- package/generators/subapp-h5/templates/config/config.prod.production.ts +10 -0
- package/generators/subapp-h5/templates/config/config.prod.testing.ts +7 -0
- package/generators/subapp-h5/templates/config/config.prod.ts +7 -0
- package/generators/subapp-h5/templates/config/config.ts +6 -0
- package/generators/subapp-h5/templates/config/routes.ts +13 -0
- package/generators/subapp-h5/templates/eslint.config.ts +12 -0
- package/generators/subapp-h5/templates/mock/user.ts +34 -0
- package/generators/subapp-h5/templates/package.json +42 -0
- package/generators/subapp-h5/templates/src/app.tsx +14 -0
- package/generators/subapp-h5/templates/src/assets/yay.jpg +0 -0
- package/generators/subapp-h5/templates/src/intl.ts +37 -0
- package/generators/subapp-h5/templates/src/layouts/index.tsx +10 -0
- package/generators/subapp-h5/templates/src/pages/index.tsx +22 -0
- package/generators/subapp-h5/templates/src/services/user.ts +38 -0
- package/generators/subapp-h5/templates/tailwind.config.js +16 -0
- package/generators/subapp-h5/templates/tailwind.css +7 -0
- package/generators/subapp-h5/templates/tsconfig.json +3 -0
- package/generators/subapp-h5/templates/typings.d.ts +1 -0
- package/lib/setup-multica-desktop.js +154 -0
- package/package.json +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { apiBaseUrl } from '<%= packageScope %>/domain';
|
|
2
|
+
import { isDebug } from '<%= packageScope %>/utils/env';
|
|
3
|
+
import { getURLParams } from '<%= packageScope %>/utils/url';
|
|
4
|
+
import { callJSBridgeMethods } from './call';
|
|
5
|
+
|
|
6
|
+
export interface GetUserInfoResponse {
|
|
7
|
+
accessToken: string;
|
|
8
|
+
uid: number;
|
|
9
|
+
nickname: string;
|
|
10
|
+
avatar: string;
|
|
11
|
+
lang: Lang;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type Lang = 'en' | 'zh-hant';
|
|
15
|
+
|
|
16
|
+
interface CallingCache {
|
|
17
|
+
resolve: (userInfo: GetUserInfoResponse) => void;
|
|
18
|
+
reject: (error: Error) => void;
|
|
19
|
+
callback?: (userInfo: GetUserInfoResponse) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function fetchUserInfo(email: string): Promise<GetUserInfoResponse> {
|
|
23
|
+
const response = await fetch(`${apiBaseUrl}/v2/api/auth/email/code_login`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
body: JSON.stringify({
|
|
26
|
+
email,
|
|
27
|
+
appId: 'omigo',
|
|
28
|
+
code: '9999',
|
|
29
|
+
}),
|
|
30
|
+
headers: {
|
|
31
|
+
source: 'web',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
const { token, userInfo } = data.content;
|
|
36
|
+
return {
|
|
37
|
+
accessToken: token.accessToken,
|
|
38
|
+
uid: userInfo.uid,
|
|
39
|
+
nickname: userInfo.nickname,
|
|
40
|
+
avatar: userInfo.avatar,
|
|
41
|
+
lang: (getURLParams().get('lang') as Lang) || 'zh-hant',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 用户信息
|
|
47
|
+
*/
|
|
48
|
+
let _userInfo: GetUserInfoResponse | null = null;
|
|
49
|
+
const callingCache = new Set<CallingCache>();
|
|
50
|
+
export default async function getUserInfo(
|
|
51
|
+
callback?: (userInfo: GetUserInfoResponse) => void,
|
|
52
|
+
) {
|
|
53
|
+
if (_userInfo) {
|
|
54
|
+
callback?.(_userInfo);
|
|
55
|
+
return _userInfo;
|
|
56
|
+
}
|
|
57
|
+
if (!callingCache.size) {
|
|
58
|
+
callJSBridgeMethods<GetUserInfoResponse>('getUserInfo', null, {
|
|
59
|
+
showToast: false,
|
|
60
|
+
})
|
|
61
|
+
.then((response) => {
|
|
62
|
+
return response.data;
|
|
63
|
+
})
|
|
64
|
+
.catch((error) => {
|
|
65
|
+
if (isDebug) {
|
|
66
|
+
// 本地和测试环境使用,线上不要开放!!!
|
|
67
|
+
return fetchUserInfo(
|
|
68
|
+
getURLParams().get('email') ?? 'tianyu@micous.com',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
throw error;
|
|
72
|
+
})
|
|
73
|
+
.then((userInfo) => {
|
|
74
|
+
userInfo.lang = userInfo.lang.toLowerCase() as Lang;
|
|
75
|
+
if (userInfo.lang.startsWith('zh')) {
|
|
76
|
+
// 临时把中文全部转换为繁体
|
|
77
|
+
userInfo.lang = 'zh-hant';
|
|
78
|
+
}
|
|
79
|
+
_userInfo = userInfo;
|
|
80
|
+
callingCache.forEach(({ resolve, callback }) => {
|
|
81
|
+
resolve(userInfo);
|
|
82
|
+
callback?.(userInfo);
|
|
83
|
+
});
|
|
84
|
+
callingCache.clear();
|
|
85
|
+
})
|
|
86
|
+
.catch((error) => {
|
|
87
|
+
callingCache.forEach(({ reject }) => {
|
|
88
|
+
reject(error);
|
|
89
|
+
});
|
|
90
|
+
callingCache.clear();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return new Promise<GetUserInfoResponse>((resolve, reject) => {
|
|
94
|
+
callingCache.add({ resolve, reject, callback });
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isAndroid, isIOS } from '<%= packageScope %>/utils/env';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 是否可以使用JSBridge
|
|
5
|
+
*/
|
|
6
|
+
export function canUseJSBridge() {
|
|
7
|
+
if (isAndroid) {
|
|
8
|
+
return !!window.Omigo && typeof window.Omigo.post === 'function';
|
|
9
|
+
} else if (isIOS) {
|
|
10
|
+
return (
|
|
11
|
+
!!window.MCAIJsBridge && typeof window.MCAIJsBridge.post === 'function'
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
MCAIJsBridge?: {
|
|
4
|
+
post?: <D = unknown, T = null>(
|
|
5
|
+
method: string,
|
|
6
|
+
payload: T,
|
|
7
|
+
callback: (response: JSBridgeResponse<D>) => void,
|
|
8
|
+
) => void;
|
|
9
|
+
};
|
|
10
|
+
Omigo?: {
|
|
11
|
+
post?: (method: string, payload: T, postID: string) => void;
|
|
12
|
+
};
|
|
13
|
+
AndroidJSBridgeCallback?: (
|
|
14
|
+
postID: string,
|
|
15
|
+
response: JSBridgeResponse<unknown>,
|
|
16
|
+
) => void;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface JSBridgeResponse<D = unknown> {
|
|
21
|
+
code: number;
|
|
22
|
+
message: string;
|
|
23
|
+
data: D;
|
|
24
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type AxiosRequestConfig<D = any> = {
|
|
2
|
+
url?: string;
|
|
3
|
+
method?: string;
|
|
4
|
+
baseURL?: string;
|
|
5
|
+
headers?: Record<string, any>;
|
|
6
|
+
params?: any;
|
|
7
|
+
data?: D;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
responseType?: string;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type AxiosResponse<T = any, D = any> = {
|
|
14
|
+
data: T;
|
|
15
|
+
status: number;
|
|
16
|
+
statusText: string;
|
|
17
|
+
headers: Record<string, any>;
|
|
18
|
+
config: AxiosRequestConfig<D>;
|
|
19
|
+
request?: any;
|
|
20
|
+
[key: string]: any;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type AxiosError<T = any, D = any> = Error & {
|
|
24
|
+
config?: AxiosRequestConfig<D>;
|
|
25
|
+
request?: any;
|
|
26
|
+
response?: AxiosResponse<T, D>;
|
|
27
|
+
code?: string;
|
|
28
|
+
status?: number;
|
|
29
|
+
message?: string;
|
|
30
|
+
isAxiosError?: boolean;
|
|
31
|
+
toJSON?: () => Record<string, any>;
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type AxiosInstance = {
|
|
36
|
+
request<T = any, R = AxiosResponse<T>, D = any>(
|
|
37
|
+
config: AxiosRequestConfig<D>,
|
|
38
|
+
): Promise<R>;
|
|
39
|
+
[key: string]: any;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type RequestError = AxiosError | Error;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import baseConfig from '<%= packageScope %>/eslint';
|
|
2
|
+
import { defineConfig } from 'eslint/config';
|
|
3
|
+
export default defineConfig([
|
|
4
|
+
...baseConfig,
|
|
5
|
+
{
|
|
6
|
+
languageOptions: {
|
|
7
|
+
parserOptions: {
|
|
8
|
+
tsconfigRootDir: import.meta.dirname,
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
rules: {
|
|
14
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
]);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= packageScope %>/request",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"<%= packageScope %>/domain": "workspace:^",
|
|
12
|
+
"<%= packageScope %>/js-bridge": "workspace:^"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"<%= packageScope %>/eslint": "workspace:^",
|
|
16
|
+
"<%= packageScope %>/typescript": "workspace:^"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"lint": "eslint",
|
|
20
|
+
"lint:fix": "eslint --fix"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 统一请求模块
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - 提供统一的 request 函数
|
|
6
|
+
* - 导出配置和拦截器相关方法
|
|
7
|
+
*
|
|
8
|
+
* 模块拆分:
|
|
9
|
+
* - types.ts: 类型定义
|
|
10
|
+
* - interceptors.ts: 拦截器管理
|
|
11
|
+
* - url-resolver.ts: URL 解析
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { request as rawRequest } from 'umi';
|
|
15
|
+
import { isAxiosError, isNormalResponseError } from './utils';
|
|
16
|
+
|
|
17
|
+
// 拦截器相关
|
|
18
|
+
import {
|
|
19
|
+
addRequestInterceptor,
|
|
20
|
+
addResponseInterceptor,
|
|
21
|
+
initDefaultInterceptors,
|
|
22
|
+
runRequestInterceptors,
|
|
23
|
+
runResponseInterceptors,
|
|
24
|
+
} from './interceptors';
|
|
25
|
+
|
|
26
|
+
// 类型导出
|
|
27
|
+
import type {
|
|
28
|
+
NormalResponseData,
|
|
29
|
+
RequestContext,
|
|
30
|
+
RequestInterceptor,
|
|
31
|
+
RequestParams,
|
|
32
|
+
ResponseData,
|
|
33
|
+
ResponseError,
|
|
34
|
+
ResponseInterceptor,
|
|
35
|
+
UnifiedRequestOptions,
|
|
36
|
+
} from './types';
|
|
37
|
+
|
|
38
|
+
// URL 解析
|
|
39
|
+
import { resolveRequestUrl } from './url-resolver';
|
|
40
|
+
|
|
41
|
+
initDefaultInterceptors();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 请求池
|
|
45
|
+
*/
|
|
46
|
+
const requestPool = new Set<UnifiedRequestOptions>();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 统一请求函数
|
|
50
|
+
*/
|
|
51
|
+
export const request = async <
|
|
52
|
+
T extends ResponseData = NormalResponseData,
|
|
53
|
+
P extends RequestParams = RequestParams,
|
|
54
|
+
>(
|
|
55
|
+
url: string,
|
|
56
|
+
options?: UnifiedRequestOptions<P>,
|
|
57
|
+
): Promise<T> => {
|
|
58
|
+
const resolvedUrl = resolveRequestUrl(url, options);
|
|
59
|
+
const {
|
|
60
|
+
skipRequestInterceptors,
|
|
61
|
+
skipResponseInterceptors,
|
|
62
|
+
isShowLoading = true,
|
|
63
|
+
...restOptions
|
|
64
|
+
} = options ?? {};
|
|
65
|
+
|
|
66
|
+
if (isShowLoading) {
|
|
67
|
+
if (requestPool.size === 0) {
|
|
68
|
+
// 显示loading
|
|
69
|
+
console.log('显示loading');
|
|
70
|
+
}
|
|
71
|
+
requestPool.add(options!);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const ctx: RequestContext = {
|
|
76
|
+
url: resolvedUrl,
|
|
77
|
+
options: restOptions ?? {},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
let next = ctx;
|
|
81
|
+
if (skipRequestInterceptors !== true) {
|
|
82
|
+
next = await runRequestInterceptors(ctx);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const mergedHeaders: Record<string, string> = {
|
|
86
|
+
...(next.options.headers || {}),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const normalizedOptions = {
|
|
90
|
+
...next.options,
|
|
91
|
+
headers: mergedHeaders,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const response = await rawRequest<T>(next.url, normalizedOptions);
|
|
95
|
+
if (skipResponseInterceptors === true) {
|
|
96
|
+
return response;
|
|
97
|
+
}
|
|
98
|
+
return await runResponseInterceptors(response, {
|
|
99
|
+
url: next.url,
|
|
100
|
+
options: normalizedOptions,
|
|
101
|
+
});
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const error = err as ResponseError;
|
|
104
|
+
let message = '';
|
|
105
|
+
if (isAxiosError(error)) {
|
|
106
|
+
const axiosResponse = error.response;
|
|
107
|
+
message =
|
|
108
|
+
axiosResponse?.data?.message || error.message || axiosResponse?.data;
|
|
109
|
+
} else if (isNormalResponseError(error)) {
|
|
110
|
+
message = error.message;
|
|
111
|
+
} else {
|
|
112
|
+
message = error.message;
|
|
113
|
+
}
|
|
114
|
+
alert(message);
|
|
115
|
+
throw error;
|
|
116
|
+
} finally {
|
|
117
|
+
if (isShowLoading) {
|
|
118
|
+
requestPool.delete(options!);
|
|
119
|
+
if (requestPool.size === 0) {
|
|
120
|
+
// 隐藏loading
|
|
121
|
+
console.log('隐藏loading');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 使用临时拦截器执行请求
|
|
129
|
+
*/
|
|
130
|
+
export const withTemporaryInterceptors = async <T>(
|
|
131
|
+
runner: () => Promise<T>,
|
|
132
|
+
interceptors: {
|
|
133
|
+
request?: RequestInterceptor;
|
|
134
|
+
response?: ResponseInterceptor;
|
|
135
|
+
},
|
|
136
|
+
): Promise<T> => {
|
|
137
|
+
const ejectors: Array<() => void> = [];
|
|
138
|
+
|
|
139
|
+
if (interceptors.request) {
|
|
140
|
+
ejectors.push(addRequestInterceptor(interceptors.request));
|
|
141
|
+
}
|
|
142
|
+
if (interceptors.response) {
|
|
143
|
+
ejectors.push(addResponseInterceptor(interceptors.response));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
return await runner();
|
|
148
|
+
} finally {
|
|
149
|
+
for (const eject of ejectors) {
|
|
150
|
+
eject();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// 导出拦截器注册方法
|
|
156
|
+
export const registerRequestInterceptor = addRequestInterceptor;
|
|
157
|
+
export const registerResponseInterceptor = addResponseInterceptor;
|
|
158
|
+
|
|
159
|
+
// 导出类型
|
|
160
|
+
export type {
|
|
161
|
+
RequestContext,
|
|
162
|
+
RequestInterceptor,
|
|
163
|
+
ResponseInterceptor,
|
|
164
|
+
UnifiedRequestOptions,
|
|
165
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 请求/响应拦截器管理
|
|
3
|
+
*/
|
|
4
|
+
// import getUserInfo from '<%= packageScope %>/js-bridge/getUserInfo';
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
NormalResponseError,
|
|
8
|
+
RequestContext,
|
|
9
|
+
RequestInterceptor,
|
|
10
|
+
ResponseData,
|
|
11
|
+
ResponseInterceptor,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
const requestInterceptors: Array<{ id: number; handler: RequestInterceptor }> =
|
|
15
|
+
[];
|
|
16
|
+
const responseInterceptors: Array<{
|
|
17
|
+
id: number;
|
|
18
|
+
handler: ResponseInterceptor;
|
|
19
|
+
}> = [];
|
|
20
|
+
|
|
21
|
+
let interceptorSeed = 0;
|
|
22
|
+
|
|
23
|
+
export const addRequestInterceptor = (
|
|
24
|
+
handler: RequestInterceptor,
|
|
25
|
+
): (() => void) => {
|
|
26
|
+
const id = ++interceptorSeed;
|
|
27
|
+
requestInterceptors.push({ id, handler });
|
|
28
|
+
return () => {
|
|
29
|
+
const index = requestInterceptors.findIndex((item) => item.id === id);
|
|
30
|
+
if (index >= 0) {
|
|
31
|
+
requestInterceptors.splice(index, 1);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const addResponseInterceptor = (
|
|
37
|
+
handler: ResponseInterceptor,
|
|
38
|
+
): (() => void) => {
|
|
39
|
+
const id = ++interceptorSeed;
|
|
40
|
+
responseInterceptors.push({ id, handler });
|
|
41
|
+
return () => {
|
|
42
|
+
const index = responseInterceptors.findIndex((item) => item.id === id);
|
|
43
|
+
if (index >= 0) {
|
|
44
|
+
responseInterceptors.splice(index, 1);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const runRequestInterceptors = async (
|
|
50
|
+
ctx: RequestContext,
|
|
51
|
+
): Promise<RequestContext> => {
|
|
52
|
+
let current = ctx;
|
|
53
|
+
for (const { handler } of requestInterceptors) {
|
|
54
|
+
current = await handler(current);
|
|
55
|
+
}
|
|
56
|
+
return current;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const runResponseInterceptors = async <T extends ResponseData>(
|
|
60
|
+
response: T,
|
|
61
|
+
ctx: RequestContext,
|
|
62
|
+
): Promise<T> => {
|
|
63
|
+
let current = response;
|
|
64
|
+
for (const { handler } of responseInterceptors) {
|
|
65
|
+
current = (await handler(current, ctx)) as T;
|
|
66
|
+
}
|
|
67
|
+
return current;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 构建默认请求头
|
|
72
|
+
*/
|
|
73
|
+
export const buildDefaultHeaders = async () => {
|
|
74
|
+
const headers: Record<string, string> = {};
|
|
75
|
+
// 获取用户信息,添加token,具体实现请根据客户端的JSBridge规则修改
|
|
76
|
+
// const userInfo = await getUserInfo();
|
|
77
|
+
// headers['Authorization'] = userInfo.accessToken;
|
|
78
|
+
return headers;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 初始化默认拦截器
|
|
83
|
+
*/
|
|
84
|
+
export const initDefaultInterceptors = (): void => {
|
|
85
|
+
// 添加默认请求头
|
|
86
|
+
addRequestInterceptor(async (ctx) => {
|
|
87
|
+
const defaultHeaders = await buildDefaultHeaders();
|
|
88
|
+
return {
|
|
89
|
+
url: ctx.url,
|
|
90
|
+
options: {
|
|
91
|
+
...ctx.options,
|
|
92
|
+
headers: {
|
|
93
|
+
...defaultHeaders,
|
|
94
|
+
...(ctx.options.headers || {}),
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// 处理业务错误码({ code, message } 格式)
|
|
101
|
+
addResponseInterceptor(async (data) => {
|
|
102
|
+
if (data && typeof data === 'object' && 'code' in data) {
|
|
103
|
+
const code = Number(data.code);
|
|
104
|
+
if (!isNaN(code) && code !== 200 && code !== 0) {
|
|
105
|
+
let message = '';
|
|
106
|
+
if ('message' in data) {
|
|
107
|
+
message = data.message;
|
|
108
|
+
} else {
|
|
109
|
+
message = data.msg;
|
|
110
|
+
}
|
|
111
|
+
const error = new Error(
|
|
112
|
+
message || `请求失败 (${code})`,
|
|
113
|
+
) as NormalResponseError;
|
|
114
|
+
error.code = code;
|
|
115
|
+
error.response = data;
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
const error = new Error(data || '请求失败') as NormalResponseError;
|
|
120
|
+
error.code = 1;
|
|
121
|
+
error.response = data;
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
return data;
|
|
125
|
+
});
|
|
126
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 请求模块类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { RequestError } from '../axios.d';
|
|
6
|
+
|
|
7
|
+
export type Method =
|
|
8
|
+
| 'get'
|
|
9
|
+
| 'GET'
|
|
10
|
+
| 'delete'
|
|
11
|
+
| 'DELETE'
|
|
12
|
+
| 'head'
|
|
13
|
+
| 'HEAD'
|
|
14
|
+
| 'options'
|
|
15
|
+
| 'OPTIONS'
|
|
16
|
+
| 'post'
|
|
17
|
+
| 'POST'
|
|
18
|
+
| 'put'
|
|
19
|
+
| 'PUT'
|
|
20
|
+
| 'patch'
|
|
21
|
+
| 'PATCH'
|
|
22
|
+
| 'purge'
|
|
23
|
+
| 'PURGE'
|
|
24
|
+
| 'link'
|
|
25
|
+
| 'LINK'
|
|
26
|
+
| 'unlink'
|
|
27
|
+
| 'UNLINK';
|
|
28
|
+
|
|
29
|
+
export type ResponseType =
|
|
30
|
+
| 'arraybuffer'
|
|
31
|
+
| 'blob'
|
|
32
|
+
| 'document'
|
|
33
|
+
| 'json'
|
|
34
|
+
| 'text'
|
|
35
|
+
| 'stream';
|
|
36
|
+
|
|
37
|
+
export type UnifiedRequestOptions<P extends RequestParams = any> = {
|
|
38
|
+
method?: Method;
|
|
39
|
+
headers?: Record<string, string>;
|
|
40
|
+
data?: P;
|
|
41
|
+
params?: P;
|
|
42
|
+
useCache?: boolean;
|
|
43
|
+
validateCache?: (url: string, options: UnifiedRequestOptions<P>) => boolean;
|
|
44
|
+
onUploadProgress?: (progressEvent: any) => void;
|
|
45
|
+
onDownloadProgress?: (progressEvent: any) => void;
|
|
46
|
+
/** 自定义 baseURL,覆盖全局配置 */
|
|
47
|
+
baseURL?: string;
|
|
48
|
+
/** 使用原始 URL,跳过所有 URL 处理 */
|
|
49
|
+
rawUrl?: boolean;
|
|
50
|
+
isShowLoading?: boolean;
|
|
51
|
+
responseType?: ResponseType;
|
|
52
|
+
skipResponseInterceptors?: boolean;
|
|
53
|
+
skipRequestInterceptors?: boolean;
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type RequestParams = Record<string, any>;
|
|
58
|
+
|
|
59
|
+
export type ResponseData = NormalResponseData | MsgResponseData | string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 普通接口响应数据类型,根据自身业务接口,自定义调整
|
|
63
|
+
*/
|
|
64
|
+
export type NormalResponseData = {
|
|
65
|
+
code: number;
|
|
66
|
+
message: string;
|
|
67
|
+
[key: string]: any;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type MsgResponseData = {
|
|
71
|
+
msg: string;
|
|
72
|
+
code: number;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type RequestContext = {
|
|
76
|
+
url: string;
|
|
77
|
+
options: UnifiedRequestOptions;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type RequestInterceptor = (
|
|
81
|
+
ctx: RequestContext,
|
|
82
|
+
) => Promise<RequestContext> | RequestContext;
|
|
83
|
+
|
|
84
|
+
export type ResponseInterceptor<T extends ResponseData = ResponseData> = (
|
|
85
|
+
response: T,
|
|
86
|
+
ctx: RequestContext,
|
|
87
|
+
) => Promise<T> | T;
|
|
88
|
+
|
|
89
|
+
export interface PendingRequest {
|
|
90
|
+
ctx: RequestContext;
|
|
91
|
+
resolve: (ctx: RequestContext) => void;
|
|
92
|
+
reject: (error: Error) => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type NormalResponseError = Error & {
|
|
96
|
+
code: number;
|
|
97
|
+
message: string;
|
|
98
|
+
response: ResponseData;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export type ResponseError = NormalResponseError | RequestError;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL 解析与拼接工具
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { apiBaseUrl } from '<%= packageScope %>/domain';
|
|
6
|
+
import type { UnifiedRequestOptions } from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 判断是否为绝对 URL
|
|
10
|
+
*/
|
|
11
|
+
const isAbsoluteUrl = (url: string): boolean => {
|
|
12
|
+
return (
|
|
13
|
+
url.startsWith('http://') ||
|
|
14
|
+
url.startsWith('https://') ||
|
|
15
|
+
url.startsWith('//')
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 解析最终请求 URL
|
|
21
|
+
*
|
|
22
|
+
* 配置优先级:
|
|
23
|
+
* 1. rawUrl: true → 直接返回原始 url
|
|
24
|
+
* 2. 绝对 URL → 直接返回
|
|
25
|
+
* 3. 拼接 apiBaseUrl + url
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
export const resolveRequestUrl = (
|
|
29
|
+
url: string,
|
|
30
|
+
options?: UnifiedRequestOptions,
|
|
31
|
+
): string => {
|
|
32
|
+
// rawUrl: 完全透传,不做任何处理
|
|
33
|
+
if (options?.rawUrl) {
|
|
34
|
+
return url;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 绝对 URL 直接返回
|
|
38
|
+
if (isAbsoluteUrl(url)) {
|
|
39
|
+
return url;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const baseURL = options?.baseURL ?? apiBaseUrl;
|
|
43
|
+
|
|
44
|
+
if (baseURL) {
|
|
45
|
+
delete options?.baseURL;
|
|
46
|
+
// 有 baseURL 时,拼接完整的绝对路径
|
|
47
|
+
const normalizedBase = baseURL.endsWith('/')
|
|
48
|
+
? baseURL.slice(0, -1)
|
|
49
|
+
: baseURL;
|
|
50
|
+
const normalizedPath = url.startsWith('/') ? url : `/${url}`;
|
|
51
|
+
return `${normalizedBase}${normalizedPath}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return url;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 从 URL 中移除指定参数
|
|
59
|
+
*/
|
|
60
|
+
export const removeParamFromUrl = (param: string): void => {
|
|
61
|
+
if (typeof window === 'undefined') return;
|
|
62
|
+
const current = new URL(window.location.href);
|
|
63
|
+
current.searchParams.delete(param);
|
|
64
|
+
const nextUrl = current.toString();
|
|
65
|
+
window.history.replaceState(null, '', nextUrl);
|
|
66
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AxiosError } from '../axios.d';
|
|
2
|
+
import type { NormalResponseError, ResponseError } from './types';
|
|
3
|
+
|
|
4
|
+
export const isAxiosError = (error: ResponseError): error is AxiosError => {
|
|
5
|
+
return 'config' in error && 'response' in error;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const isNormalResponseError = (
|
|
9
|
+
error: ResponseError,
|
|
10
|
+
): error is NormalResponseError => {
|
|
11
|
+
return 'code' in error && 'message' in error && 'response' in error;
|
|
12
|
+
};
|