generator-mico-cli 0.2.30 → 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 +146 -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/micro-react/README.md +34 -0
- package/generators/micro-react/index.js +2 -0
- package/generators/micro-react/templates/apps/layout/config/config.dev.ts +21 -1
- package/generators/micro-react/templates/apps/layout/config/config.ts +0 -15
- package/generators/micro-react/templates/apps/layout/docs/arch-/346/227/245/345/277/227/344/270/216/345/270/270/351/207/217.md +16 -8
- package/generators/micro-react/templates/apps/layout/docs/feat-/346/236/204/345/273/272define/344/270/216/345/205/215/350/256/244/350/257/201/345/210/235/345/247/213/346/200/201.md +49 -3
- package/generators/micro-react/templates/apps/layout/docs/feature-/350/217/234/345/215/225/346/235/203/351/231/220/346/216/247/345/210/266.md +3 -1
- package/generators/micro-react/templates/apps/layout/docs/feature-/350/267/257/347/224/261/346/235/203/351/231/220/346/227/245/345/277/227.md +4 -4
- package/generators/micro-react/templates/apps/layout/src/app.tsx +10 -4
- package/generators/micro-react/templates/apps/layout/src/common/auth/auth-check-path.ts +14 -0
- package/generators/micro-react/templates/apps/layout/src/common/auth/index.ts +1 -0
- package/generators/micro-react/templates/apps/layout/src/common/logger.ts +51 -18
- package/generators/micro-react/templates/apps/layout/src/common/request/sso.ts +8 -5
- package/generators/micro-react/templates/package.json +1 -1
- 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/generators/subapp-react/README.md +43 -0
- package/generators/subapp-react/index.js +2 -0
- package/generators/subapp-react/templates/homepage/README.md +5 -1
- package/generators/subapp-react/templates/homepage/config/config.dev.ts +20 -0
- package/generators/subapp-react/templates/homepage/config/config.ts +0 -15
- package/generators/subapp-react/templates/homepage/src/common/logger.ts +50 -18
- package/generators/subapp-umd/README.md +37 -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
|
+
};
|