anl 26.806.0 → 26.928.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.d.ts +11 -0
- package/lib/index.cjs +1 -1
- package/lib/package.cjs +1 -1
- package/lib/request-templates/_shared/api-type.d.ts +52 -0
- package/lib/request-templates/_shared/error-message.ts +55 -0
- package/lib/request-templates/_shared/fetch.ts +208 -0
- package/lib/request-templates/fetch/dio.ts +233 -0
- package/lib/request-templates/taro/dio.ts +183 -0
- package/lib/request-templates/uniapp/dio.ts +201 -0
- package/lib/request-templates/wx/dio.ts +201 -0
- package/lib/standard/install-commitlint.cjs +1 -1
- package/lib/swagger-codegen/components/schema-resolver.cjs +3 -3
- package/lib/swagger-codegen/config-template.cjs +6 -0
- package/lib/swagger-codegen/index.cjs +7 -44
- package/lib/swagger-codegen/path/index.cjs +1 -1
- package/lib/swagger-codegen/shared/constants.cjs +1 -1
- package/lib/swagger-codegen/shared/naming.cjs +1 -1
- package/lib/swagger-codegen/shared/prettier.cjs +4 -0
- package/lib/swagger-codegen/shared/request-template.cjs +1 -0
- package/lib/swagger-codegen/shared/writer.cjs +3 -3
- package/lib/utils/index.cjs +1 -1
- package/package.json +4 -1
- /package/lib/{ajax-config → request-templates/axios}/api-type.d.ts +0 -0
- /package/lib/{ajax-config → request-templates/axios}/dio.ts +0 -0
- /package/lib/{ajax-config → request-templates/axios}/error-message.ts +0 -0
- /package/lib/{ajax-config → request-templates/axios}/fetch.ts +0 -0
package/config.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export type TDatalevel = 'data' | 'serve' | 'axios';
|
|
|
3
3
|
/** 终端日志输出级别 */
|
|
4
4
|
export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'verbose';
|
|
5
5
|
|
|
6
|
+
/** 请求模板类型 —— 决定生成到项目 `<saveApiListFolderPath>/config/` 下的底层请求实现 */
|
|
7
|
+
export type RequestTemplate = 'axios' | 'fetch' | 'wx' | 'uniapp' | 'taro';
|
|
8
|
+
|
|
6
9
|
/** 响应模型转换配置 */
|
|
7
10
|
export interface IResponseModelTransform {
|
|
8
11
|
/** 转换类型:unwrap-剔除响应模型,wrap-添加响应模型,replace-替换响应模型 */
|
|
@@ -79,6 +82,14 @@ export interface ConfigType {
|
|
|
79
82
|
apiListFileName?: string;
|
|
80
83
|
/** 请求方法导入路径 */
|
|
81
84
|
requestMethodsImportPath: string;
|
|
85
|
+
/** 请求模板类型(决定生成到 `<saveApiListFolderPath>/config/` 下的底层 `dio.ts` 实现),默认 `'axios'`。
|
|
86
|
+
* - `axios` 基于 axios(浏览器/Node,需 `npm i axios`)
|
|
87
|
+
* - `fetch` 基于原生 fetch(浏览器/现代 Node,无额外依赖)
|
|
88
|
+
* - `wx` 基于 `wx.request`(微信小程序原生)
|
|
89
|
+
* - `uniapp` 基于 `uni.request`(uni-app 跨端)
|
|
90
|
+
* - `taro` 基于 `Taro.request`(Taro 3+ 跨端)
|
|
91
|
+
* 注意:切换模板时需手动删除 `<saveApiListFolderPath>/config/` 目录,CLI 才会重新写入对应模板文件。 */
|
|
92
|
+
requestTemplate?: RequestTemplate;
|
|
82
93
|
/** 追加请求头 */
|
|
83
94
|
headers?: Record<string, string>;
|
|
84
95
|
/** 公共前缀(仅为兼容旧配置,最终以服务级配置为准) */
|
package/lib/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=require(`./_virtual/rolldown_runtime.cjs`),t=require(`./package.cjs`),n=require(`./git-local-config/index.cjs`),r=require(`./skill-init/index.cjs`),i=require(`./standard/lint-init.cjs`),a=require(`./swagger-codegen/index.cjs`);let o=require(`commander`),s=require(`inquirer`);s=e.__toESM(s),o.program.version(`${t.version}`,`-v --version`).usage(`<command> [options]`),o.program.command(`type`).description(`auto interface`).option(`-s, --show <what>`,`show interface list after generation: miss | gen`).option(`-f, --format [config]`,`enable prettier formatting after generation; optionally specify a prettier config file path (e.g. --format .prettierrc.mjs)`).option(`-l, --log-level <level>`,`set log output level: silent | error | warn | info | verbose`).option(`-S, --service <names>`,"only regenerate the specified swagger service(s); comma-separated. Match by `name` (preferred) or `apiListFileName` without extension. Other services are kept untouched.").action(e=>{let t=(e.show??``).toLowerCase().trim(),n=t===`miss`||t===`missing`||t===`m`||t===`exclude`||t===`x`?`miss`:t===`gen`||t===`generated`||t===`g`||t===`include`||t===`i`?`gen`:void 0,r=e.service?e.service.split(`,`).map(e=>e.trim()).filter(Boolean):void 0;new a.Main().initialize(n,e.format,e.logLevel,r).catch(e=>{console.error(e)})}),o.program.command(`lint`).description(`install linting tools (eslint, stylelint, prettier, commitlint, vscode)`).action(()=>i.lintHandle()),o.program.command(`git`).description(`config git Local custom command`).action(async()=>{let{features:e}=await s.default.prompt([{type:`checkbox`,name:`features`,message:`Select the required Git features (multi-select):`,choices:[{name:`gitflow standard branch creation`,value:`gitflow`},{name:`automatically set commit subject`,value:`commitSubject`},{name:`custom git command`,value:`customGitCommand`}],pageSize:10}]);n.gitHandle(e).catch(e=>{console.error(e)})}),o.program.command(`skill`).description(`initialize an agent skill into the current project`).action(()=>{r.skillHandle().catch(e=>{console.error(e)})}),o.program.parse(process.argv);
|
|
1
|
+
const e=require(`./_virtual/rolldown_runtime.cjs`),t=require(`./package.cjs`),n=require(`./git-local-config/index.cjs`),r=require(`./skill-init/index.cjs`),i=require(`./standard/lint-init.cjs`),a=require(`./swagger-codegen/index.cjs`);let o=require(`commander`),s=require(`inquirer`);s=e.__toESM(s),o.program.version(`${t.version}`,`-v --version`).usage(`<command> [options]`),o.program.command(`type`).description(`auto interface`).option(`-s, --show <what>`,`show interface list after generation: miss | gen`).option(`-f, --format [config]`,`enable prettier formatting after generation; optionally specify a prettier config file path (e.g. --format .prettierrc.mjs)`).option(`-l, --log-level <level>`,`set log output level: silent | error | warn | info | verbose`).option(`-S, --service <names>`,"only regenerate the specified swagger service(s); comma-separated. Match by `name` (preferred) or `apiListFileName` without extension. Other services are kept untouched.").option(`-t, --template <name>`,`request template written to <saveApiListFolderPath>/config: axios | fetch | wx | uniapp | taro. Overrides an.config.ts. Only takes effect when the config folder does not exist yet.`).action(e=>{let t=(e.show??``).toLowerCase().trim(),n=t===`miss`||t===`missing`||t===`m`||t===`exclude`||t===`x`?`miss`:t===`gen`||t===`generated`||t===`g`||t===`include`||t===`i`?`gen`:void 0,r=e.service?e.service.split(`,`).map(e=>e.trim()).filter(Boolean):void 0;new a.Main().initialize(n,e.format,e.logLevel,r,e.template).catch(e=>{console.error(e)})}),o.program.command(`lint`).description(`install linting tools (eslint, stylelint, prettier, commitlint, vscode)`).action(()=>i.lintHandle()),o.program.command(`git`).description(`config git Local custom command`).action(async()=>{let{features:e}=await s.default.prompt([{type:`checkbox`,name:`features`,message:`Select the required Git features (multi-select):`,choices:[{name:`gitflow standard branch creation`,value:`gitflow`},{name:`automatically set commit subject`,value:`commitSubject`},{name:`custom git command`,value:`customGitCommand`}],pageSize:10}]);n.gitHandle(e).catch(e=>{console.error(e)})}),o.program.command(`skill`).description(`initialize an agent skill into the current project`).action(()=>{r.skillHandle().catch(e=>{console.error(e)})}),o.program.parse(process.argv);
|
package/lib/package.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=`26.
|
|
1
|
+
var e=`26.928.0`;Object.defineProperty(exports,`version`,{enumerable:!0,get:function(){return e}});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* datalevel 说明:
|
|
3
|
+
* - 'data' 返回业务数据本体(ResponseModel.data)
|
|
4
|
+
* - 'serve' 返回完整业务响应体 ResponseModel<T>
|
|
5
|
+
* - 'axios' 返回底层适配器的原始响应对象 DioResponse<ResponseModel<T>>
|
|
6
|
+
* (命名沿用历史值以保持代码生成器一致,语义为"最底层原始响应")
|
|
7
|
+
*/
|
|
8
|
+
type TDatalevel = 'data' | 'serve' | 'axios';
|
|
9
|
+
|
|
10
|
+
type RServe<T> = Promise<ResponseModel<T>>;
|
|
11
|
+
type RAxios<T> = Promise<DioResponse<ResponseModel<T>>>;
|
|
12
|
+
|
|
13
|
+
interface ResponseModel<T> {
|
|
14
|
+
code: number;
|
|
15
|
+
message: string;
|
|
16
|
+
data: T;
|
|
17
|
+
success: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 底层适配器统一响应结构(axios/fetch/wx/uni/taro 的共同抽象) */
|
|
21
|
+
interface DioResponse<T = unknown> {
|
|
22
|
+
data: T;
|
|
23
|
+
status: number;
|
|
24
|
+
statusText?: string;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 底层适配器统一请求配置(axios/fetch/wx/uni/taro 的共同抽象) */
|
|
29
|
+
interface DioRequestConfig {
|
|
30
|
+
url?: string;
|
|
31
|
+
method?: 'GET' | 'DELETE' | 'PUT' | 'POST' | 'PATCH' | 'OPTIONS' | 'HEAD' | 'SEARCH' | Lowercase<'GET' | 'DELETE' | 'PUT' | 'POST' | 'PATCH' | 'OPTIONS' | 'HEAD' | 'SEARCH'>;
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
timeout?: number;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
responseType?: 'json' | 'text' | 'blob' | 'arrayBuffer';
|
|
36
|
+
[key: string]: unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type IRequestFnParams = DioRequestConfig & {
|
|
40
|
+
query?: unknown;
|
|
41
|
+
body?: unknown;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
interface IRequestFnRestParams {
|
|
45
|
+
config?: DioRequestConfig;
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ErrorPayload {
|
|
50
|
+
errors?: { message?: string }[];
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 提示组件请按项目实际替换:
|
|
3
|
+
* - Web:可换成 antd / element-plus / naive-ui 的 message
|
|
4
|
+
* - 小程序/uni-app/Taro:可换成 wx.showToast / uni.showToast / Taro.showToast
|
|
5
|
+
* 这里保留一个最小实现(console.warn),避免默认强依赖任何提示库。
|
|
6
|
+
*/
|
|
7
|
+
const message = {
|
|
8
|
+
info: (msg: string) => console.info('[tip]', msg),
|
|
9
|
+
warning: (msg: string) => console.warn('[warn]', msg),
|
|
10
|
+
error: (msg: string) => console.error('[error]', msg),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** 跳过提示的 code 码 */
|
|
14
|
+
const skipTipCode: number[] = [];
|
|
15
|
+
|
|
16
|
+
/** 需要重写提示的 code 码 */
|
|
17
|
+
const rewriteCodeMessage = ({ code = 10000, message = '网络错误,稍后重试!' }: ResponseModel<unknown>) => {
|
|
18
|
+
switch (code) {
|
|
19
|
+
case 10000:
|
|
20
|
+
return { code, message: '系统错误,稍后重试!' };
|
|
21
|
+
default:
|
|
22
|
+
return { code, message };
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const messageTip = ({ data }: DioResponse<ResponseModel<unknown>>) => {
|
|
27
|
+
if (Object.prototype.toString.call(data) !== '[object Object]') return;
|
|
28
|
+
if (!('message' in data && 'code' in data)) return;
|
|
29
|
+
|
|
30
|
+
const { code, message: msg } = rewriteCodeMessage(data);
|
|
31
|
+
|
|
32
|
+
if (skipTipCode.includes(code)) return;
|
|
33
|
+
|
|
34
|
+
// 以下的代码是根据 code 码范围来显示不同的提示信息,请根据实际需求修改
|
|
35
|
+
|
|
36
|
+
/** 信息提示范围 */
|
|
37
|
+
if (code >= 2000 && code <= 3999) {
|
|
38
|
+
message.info(msg);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 警告提示范围 */
|
|
42
|
+
if (code > 3999 && code <= 4999) {
|
|
43
|
+
message.warning(msg);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 错误提示范围 */
|
|
47
|
+
if (code > 4999 && code <= 5999) {
|
|
48
|
+
message.error(msg);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 系统提示范围 */
|
|
52
|
+
if (code >= 9000) {
|
|
53
|
+
message.info(msg);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 只需要导出 GET DELETE PUT POST PATCH OPTIONS HEAD SEARCH 这些请求方法即可,
|
|
3
|
+
* 至于方法内使用 axios / fetch / wx / uni / taro 由 dio.ts 中的 dio 实例决定。
|
|
4
|
+
* 请注意方法的参数和返回值类型,请参考 dio.ts 中 dio 的类型定义;实现函数重载。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { dio } from './dio';
|
|
8
|
+
import { messageTip } from './error-message';
|
|
9
|
+
|
|
10
|
+
function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
11
|
+
function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
12
|
+
function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
13
|
+
function GET<R = unknown>(url: string, params: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
14
|
+
return dio
|
|
15
|
+
.request<ResponseModel<R>>({
|
|
16
|
+
...params,
|
|
17
|
+
url,
|
|
18
|
+
query: params.query,
|
|
19
|
+
method: 'GET',
|
|
20
|
+
})
|
|
21
|
+
.then((res) => {
|
|
22
|
+
messageTip(res);
|
|
23
|
+
switch (datalevel) {
|
|
24
|
+
case 'data':
|
|
25
|
+
return res.data.data;
|
|
26
|
+
case 'serve':
|
|
27
|
+
return res.data;
|
|
28
|
+
case 'axios':
|
|
29
|
+
return res;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
35
|
+
function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
36
|
+
function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
37
|
+
function DELETE<R = unknown>(url: string, params: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
38
|
+
return dio
|
|
39
|
+
.request<ResponseModel<R>>({
|
|
40
|
+
...params,
|
|
41
|
+
url,
|
|
42
|
+
query: params.query,
|
|
43
|
+
method: 'DELETE',
|
|
44
|
+
})
|
|
45
|
+
.then((res) => {
|
|
46
|
+
messageTip(res);
|
|
47
|
+
switch (datalevel) {
|
|
48
|
+
case 'data':
|
|
49
|
+
return res.data.data;
|
|
50
|
+
case 'serve':
|
|
51
|
+
return res.data;
|
|
52
|
+
case 'axios':
|
|
53
|
+
return res;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function PUT<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
59
|
+
function PUT<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
60
|
+
function PUT<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
61
|
+
function PUT<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
62
|
+
return dio
|
|
63
|
+
.request<ResponseModel<R>>({
|
|
64
|
+
...rest,
|
|
65
|
+
url,
|
|
66
|
+
query,
|
|
67
|
+
body,
|
|
68
|
+
method: 'PUT',
|
|
69
|
+
})
|
|
70
|
+
.then((res) => {
|
|
71
|
+
messageTip(res);
|
|
72
|
+
switch (datalevel) {
|
|
73
|
+
case 'data':
|
|
74
|
+
return res.data.data;
|
|
75
|
+
case 'serve':
|
|
76
|
+
return res.data;
|
|
77
|
+
case 'axios':
|
|
78
|
+
return res;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function POST<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
84
|
+
function POST<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
85
|
+
function POST<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
86
|
+
function POST<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
87
|
+
return dio
|
|
88
|
+
.request<ResponseModel<R>>({
|
|
89
|
+
...rest,
|
|
90
|
+
url,
|
|
91
|
+
query,
|
|
92
|
+
body,
|
|
93
|
+
method: 'POST',
|
|
94
|
+
})
|
|
95
|
+
.then((res) => {
|
|
96
|
+
messageTip(res);
|
|
97
|
+
switch (datalevel) {
|
|
98
|
+
case 'data':
|
|
99
|
+
return res.data.data;
|
|
100
|
+
case 'serve':
|
|
101
|
+
return res.data;
|
|
102
|
+
case 'axios':
|
|
103
|
+
return res;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function PATCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
109
|
+
function PATCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
110
|
+
function PATCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
111
|
+
function PATCH<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
112
|
+
return dio
|
|
113
|
+
.request<ResponseModel<R>>({
|
|
114
|
+
...rest,
|
|
115
|
+
url,
|
|
116
|
+
query,
|
|
117
|
+
body,
|
|
118
|
+
method: 'PATCH',
|
|
119
|
+
})
|
|
120
|
+
.then((res) => {
|
|
121
|
+
messageTip(res);
|
|
122
|
+
switch (datalevel) {
|
|
123
|
+
case 'data':
|
|
124
|
+
return res.data.data;
|
|
125
|
+
case 'serve':
|
|
126
|
+
return res.data;
|
|
127
|
+
case 'axios':
|
|
128
|
+
return res;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function OPTIONS<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
134
|
+
function OPTIONS<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
135
|
+
function OPTIONS<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
136
|
+
function OPTIONS<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
137
|
+
return dio
|
|
138
|
+
.request<ResponseModel<R>>({
|
|
139
|
+
...rest,
|
|
140
|
+
url,
|
|
141
|
+
query,
|
|
142
|
+
body,
|
|
143
|
+
method: 'OPTIONS',
|
|
144
|
+
})
|
|
145
|
+
.then((res) => {
|
|
146
|
+
messageTip(res);
|
|
147
|
+
switch (datalevel) {
|
|
148
|
+
case 'data':
|
|
149
|
+
return res.data.data;
|
|
150
|
+
case 'serve':
|
|
151
|
+
return res.data;
|
|
152
|
+
case 'axios':
|
|
153
|
+
return res;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function HEAD<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
159
|
+
function HEAD<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
160
|
+
function HEAD<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
161
|
+
function HEAD<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
162
|
+
return dio
|
|
163
|
+
.request<ResponseModel<R>>({
|
|
164
|
+
...rest,
|
|
165
|
+
url,
|
|
166
|
+
query,
|
|
167
|
+
body,
|
|
168
|
+
method: 'HEAD',
|
|
169
|
+
})
|
|
170
|
+
.then((res) => {
|
|
171
|
+
messageTip(res);
|
|
172
|
+
switch (datalevel) {
|
|
173
|
+
case 'data':
|
|
174
|
+
return res.data.data;
|
|
175
|
+
case 'serve':
|
|
176
|
+
return res.data;
|
|
177
|
+
case 'axios':
|
|
178
|
+
return res;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function SEARCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'serve'): RServe<R>;
|
|
184
|
+
function SEARCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'data'): Promise<R>;
|
|
185
|
+
function SEARCH<R = unknown>(url: string, params: IRequestFnParams, datalevel?: 'axios'): RAxios<R>;
|
|
186
|
+
function SEARCH<R = unknown>(url: string, { query, body, ...rest }: IRequestFnParams, datalevel: TDatalevel = 'serve') {
|
|
187
|
+
return dio
|
|
188
|
+
.request<ResponseModel<R>>({
|
|
189
|
+
...rest,
|
|
190
|
+
url,
|
|
191
|
+
query,
|
|
192
|
+
body,
|
|
193
|
+
method: 'SEARCH',
|
|
194
|
+
})
|
|
195
|
+
.then((res) => {
|
|
196
|
+
messageTip(res);
|
|
197
|
+
switch (datalevel) {
|
|
198
|
+
case 'data':
|
|
199
|
+
return res.data.data;
|
|
200
|
+
case 'serve':
|
|
201
|
+
return res.data;
|
|
202
|
+
case 'axios':
|
|
203
|
+
return res;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export { DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, SEARCH };
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fetch 版 dio 实例:基于 Web 原生 fetch,无外部依赖。
|
|
3
|
+
* 与 axios 版保持相同的对外形态:`dio.request(config)` 与 `dio.interceptors.request|response`。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 环境变量参考(Vite):
|
|
7
|
+
// const { VITE_API_URL, VITE_API_PROXY, PROD } = import.meta.env;
|
|
8
|
+
// export const BASE_URL = PROD ? VITE_API_URL : VITE_API_PROXY;
|
|
9
|
+
export const BASE_URL = '';
|
|
10
|
+
|
|
11
|
+
type Interceptor<V> = {
|
|
12
|
+
onFulfilled?: (value: V) => V | Promise<V>;
|
|
13
|
+
onRejected?: (error: unknown) => unknown;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class InterceptorManager<V> {
|
|
17
|
+
private handlers: Interceptor<V>[] = [];
|
|
18
|
+
use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: unknown) => unknown): number {
|
|
19
|
+
this.handlers.push({ onFulfilled, onRejected });
|
|
20
|
+
return this.handlers.length - 1;
|
|
21
|
+
}
|
|
22
|
+
eject(id: number): void {
|
|
23
|
+
if (this.handlers[id]) this.handlers[id] = {};
|
|
24
|
+
}
|
|
25
|
+
forEach(cb: (h: Interceptor<V>) => void): void {
|
|
26
|
+
this.handlers.forEach((h) => h && cb(h));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const DEFAULT_TIMEOUT = 600000; // 10 分钟
|
|
31
|
+
|
|
32
|
+
function buildURL(base: string, url: string, query?: unknown): string {
|
|
33
|
+
const full = /^https?:\/\//i.test(url) ? url : `${base}${url}`;
|
|
34
|
+
if (!query || typeof query !== 'object') return full;
|
|
35
|
+
const usp = new URLSearchParams();
|
|
36
|
+
Object.entries(query as Record<string, unknown>).forEach(([k, v]) => {
|
|
37
|
+
if (v === undefined || v === null) return;
|
|
38
|
+
if (Array.isArray(v)) v.forEach((item) => usp.append(k, String(item)));
|
|
39
|
+
else usp.append(k, String(v));
|
|
40
|
+
});
|
|
41
|
+
const qs = usp.toString();
|
|
42
|
+
if (!qs) return full;
|
|
43
|
+
return full.includes('?') ? `${full}&${qs}` : `${full}?${qs}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeHeaders(h?: Record<string, string>): Record<string, string> {
|
|
47
|
+
return { 'Content-Type': 'application/json', ...(h ?? {}) };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function coreRequest<T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
|
|
51
|
+
const { url = '', method = 'GET', query, body, headers, timeout = DEFAULT_TIMEOUT, signal, responseType = 'json' } = config;
|
|
52
|
+
const finalHeaders = normalizeHeaders(headers);
|
|
53
|
+
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
const timerId = setTimeout(() => controller.abort(), timeout);
|
|
56
|
+
// 外部传入的 signal 优先,被 abort 时也要中断 fetch
|
|
57
|
+
if (signal) signal.addEventListener('abort', () => controller.abort(), { once: true });
|
|
58
|
+
|
|
59
|
+
const init: RequestInit = {
|
|
60
|
+
method: String(method).toUpperCase(),
|
|
61
|
+
headers: finalHeaders,
|
|
62
|
+
signal: controller.signal,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const upperMethod = init.method as string;
|
|
66
|
+
if (body !== undefined && upperMethod !== 'GET' && upperMethod !== 'HEAD') {
|
|
67
|
+
init.body = typeof body === 'string' || body instanceof FormData || body instanceof Blob ? (body as BodyInit) : JSON.stringify(body);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let response: Response;
|
|
71
|
+
try {
|
|
72
|
+
response = await fetch(buildURL(BASE_URL, url, query), init);
|
|
73
|
+
} finally {
|
|
74
|
+
clearTimeout(timerId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let data: unknown;
|
|
78
|
+
if (responseType === 'text') data = await response.text();
|
|
79
|
+
else if (responseType === 'blob') data = await response.blob();
|
|
80
|
+
else if (responseType === 'arrayBuffer') data = await response.arrayBuffer();
|
|
81
|
+
else {
|
|
82
|
+
const text = await response.text();
|
|
83
|
+
try {
|
|
84
|
+
data = text ? JSON.parse(text) : null;
|
|
85
|
+
} catch {
|
|
86
|
+
data = text;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const respHeaders: Record<string, string> = {};
|
|
91
|
+
response.headers.forEach((v, k) => (respHeaders[k] = v));
|
|
92
|
+
|
|
93
|
+
const dioResponse: DioResponse<T> = {
|
|
94
|
+
data: data as T,
|
|
95
|
+
status: response.status,
|
|
96
|
+
statusText: response.statusText,
|
|
97
|
+
headers: respHeaders,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
const err: Error & { response?: DioResponse<T>; config?: DioRequestConfig } = new Error(`Request failed with status ${response.status}`);
|
|
102
|
+
err.response = dioResponse;
|
|
103
|
+
err.config = config;
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
return dioResponse;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const requestInterceptor = new InterceptorManager<DioRequestConfig & { query?: unknown; body?: unknown }>();
|
|
110
|
+
const responseInterceptor = new InterceptorManager<DioResponse<unknown>>();
|
|
111
|
+
|
|
112
|
+
async function runRequest<T>(config: DioRequestConfig & { query?: unknown; body?: unknown }): Promise<DioResponse<T>> {
|
|
113
|
+
let current: DioRequestConfig & { query?: unknown; body?: unknown } = config;
|
|
114
|
+
const reqHandlers: Interceptor<typeof current>[] = [];
|
|
115
|
+
requestInterceptor.forEach((h) => reqHandlers.push(h));
|
|
116
|
+
for (const h of reqHandlers) {
|
|
117
|
+
try {
|
|
118
|
+
if (h.onFulfilled) current = await h.onFulfilled(current);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let value: DioResponse<T>;
|
|
126
|
+
try {
|
|
127
|
+
value = await coreRequest<T>(current);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
let error: unknown = e;
|
|
130
|
+
const handlers: Interceptor<DioResponse<unknown>>[] = [];
|
|
131
|
+
responseInterceptor.forEach((h) => handlers.push(h));
|
|
132
|
+
for (const h of handlers) {
|
|
133
|
+
if (h.onRejected) {
|
|
134
|
+
try {
|
|
135
|
+
const recovered = await h.onRejected(error);
|
|
136
|
+
return recovered as DioResponse<T>;
|
|
137
|
+
} catch (next) {
|
|
138
|
+
error = next;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let currentRes: DioResponse<unknown> = value;
|
|
146
|
+
const resHandlers: Interceptor<DioResponse<unknown>>[] = [];
|
|
147
|
+
responseInterceptor.forEach((h) => resHandlers.push(h));
|
|
148
|
+
for (const h of resHandlers) {
|
|
149
|
+
try {
|
|
150
|
+
if (h.onFulfilled) currentRes = await h.onFulfilled(currentRes);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
if (h.onRejected) return Promise.reject(h.onRejected(e)) as never;
|
|
153
|
+
throw e;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return currentRes as DioResponse<T>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const dio = {
|
|
160
|
+
interceptors: {
|
|
161
|
+
request: requestInterceptor,
|
|
162
|
+
response: responseInterceptor,
|
|
163
|
+
},
|
|
164
|
+
request: <T = unknown>(config: DioRequestConfig & { query?: unknown; body?: unknown }) => runRequest<T>(config),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 请求拦截
|
|
169
|
+
*/
|
|
170
|
+
dio.interceptors.request.use(
|
|
171
|
+
(config) => {
|
|
172
|
+
config.headers = { ...(config.headers ?? {}), Authorization: `Bearer 【auth -> replace】` };
|
|
173
|
+
return config;
|
|
174
|
+
},
|
|
175
|
+
(error) => {
|
|
176
|
+
console.error('request interceptors', error);
|
|
177
|
+
return error;
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 响应拦截 —— 成功(2xx)
|
|
183
|
+
*/
|
|
184
|
+
dio.interceptors.response.use(
|
|
185
|
+
(response) => response,
|
|
186
|
+
(rawError) => {
|
|
187
|
+
const error = rawError as Error & { response?: DioResponse<{ message?: string; data?: unknown }> };
|
|
188
|
+
if (!error.response) {
|
|
189
|
+
console.error('网络异常,请检查网络连接');
|
|
190
|
+
return Promise.reject(error);
|
|
191
|
+
}
|
|
192
|
+
const { status, data } = error.response;
|
|
193
|
+
const respData = (data ?? {}) as { message?: string; data?: unknown };
|
|
194
|
+
let errorMsg = respData.message ?? '请求失败,请稍后重试';
|
|
195
|
+
const payload = respData.data;
|
|
196
|
+
if (typeof payload === 'string') errorMsg = payload;
|
|
197
|
+
else if (payload && typeof payload === 'object' && 'errors' in payload) {
|
|
198
|
+
const errors = (payload as ErrorPayload).errors;
|
|
199
|
+
if (Array.isArray(errors)) {
|
|
200
|
+
const collected = errors.map((it) => it?.message).filter((m): m is string => Boolean(m));
|
|
201
|
+
if (collected.length) errorMsg = collected.join(';');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
switch (status) {
|
|
206
|
+
case 400:
|
|
207
|
+
console.error(errorMsg);
|
|
208
|
+
break;
|
|
209
|
+
case 401:
|
|
210
|
+
console.error('登录过期,请重新登录');
|
|
211
|
+
if (typeof window !== 'undefined' && window.location.pathname !== '/login') window.location.href = '/login';
|
|
212
|
+
break;
|
|
213
|
+
case 403:
|
|
214
|
+
console.error('服务器拒绝请求');
|
|
215
|
+
break;
|
|
216
|
+
case 404:
|
|
217
|
+
console.error('资源未找到');
|
|
218
|
+
break;
|
|
219
|
+
case 500:
|
|
220
|
+
console.error('服务器内部错误');
|
|
221
|
+
break;
|
|
222
|
+
case 502:
|
|
223
|
+
console.error('网关或代理服务器收到无效响应');
|
|
224
|
+
break;
|
|
225
|
+
case 503:
|
|
226
|
+
console.error('服务器暂时无法处理请求');
|
|
227
|
+
break;
|
|
228
|
+
default:
|
|
229
|
+
console.error(errorMsg);
|
|
230
|
+
}
|
|
231
|
+
return Promise.reject(error);
|
|
232
|
+
},
|
|
233
|
+
);
|