@base-web-kits/base-tools-web 1.1.18-alpha.1 → 1.2.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/dist/async/index.d.ts +4 -0
- package/dist/async/index.d.ts.map +1 -1
- package/dist/base-tools-web.umd.global.js +64 -52
- package/dist/base-tools-web.umd.global.js.map +1 -1
- package/dist/index.cjs +53 -976
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +237 -991
- package/dist/index.js.map +1 -1
- package/dist/network/request.d.ts +33 -28
- package/dist/network/request.d.ts.map +1 -1
- package/dist/storage/index.d.ts +1 -3
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/web/async/index.ts +30 -4
- package/src/web/network/request.ts +70 -61
- package/src/web/storage/index.ts +1 -3
|
@@ -39,22 +39,24 @@ export type RequestConfigBase<D extends RequestData = RequestData> = {
|
|
|
39
39
|
successCode: (number | string)[];
|
|
40
40
|
/** 登录过期状态码 */
|
|
41
41
|
reloginCode: (number | string)[];
|
|
42
|
-
/** 是否显示进度条: 支持字符串,自定义文本 (默认true) */
|
|
43
|
-
showLoading?: boolean | string;
|
|
44
|
-
/** 是否提示接口异常 (默认true) */
|
|
45
|
-
toastError?: boolean;
|
|
46
|
-
/** 是否输出日志 (默认true) */
|
|
47
|
-
isLog?: boolean;
|
|
48
|
-
/** 额外输出的日志数据 */
|
|
49
|
-
extraLog?: Record<string, unknown>;
|
|
50
|
-
/** 响应数据的缓存时间, 单位毫秒。仅在成功时缓存;仅缓存在内存,应用退出,缓存消失。(默认0,不开启缓存) */
|
|
51
|
-
cacheTime?: number;
|
|
52
42
|
/** 是否开启流式传输 (如 SSE) */
|
|
53
43
|
enableChunked?: boolean;
|
|
54
44
|
/** 响应类型 (默认 json, enableChunked为true时忽略) */
|
|
55
45
|
responseType?: 'text' | 'arraybuffer' | 'json';
|
|
46
|
+
/** 响应数据的缓存时间, 单位毫秒。仅在成功时缓存;仅缓存在内存,应用退出,缓存消失。(默认0,不开启缓存) */
|
|
47
|
+
cacheTime?: number;
|
|
48
|
+
/** 是否提示接口异常 (默认true) */
|
|
49
|
+
toastError?: boolean;
|
|
50
|
+
/** 是否显示进度条: 支持字符串,自定义文本 (默认true) */
|
|
51
|
+
showLoading?: boolean | string;
|
|
52
|
+
/** 是否输出日志 (默认true) */
|
|
53
|
+
showLog?: boolean;
|
|
54
|
+
/** 成功和失败时,额外输出的日志数据 (可覆盖内部log参数,如'name') */
|
|
55
|
+
logExtra?: Record<string, unknown>;
|
|
56
56
|
/** 响应拦截 */
|
|
57
|
-
|
|
57
|
+
resMap?: (data: ResponseData) => ResponseData;
|
|
58
|
+
/** 获取task对象, 用于取消请求或监听流式数据 */
|
|
59
|
+
onTaskReady?: (task: RequestTask) => void;
|
|
58
60
|
};
|
|
59
61
|
/**
|
|
60
62
|
* 请求任务对象 (用于取消请求或监听流式数据)
|
|
@@ -77,7 +79,6 @@ export type ChunkCallback = (response: {
|
|
|
77
79
|
* 基础请求 (返回 Promise 和 Task 对象)
|
|
78
80
|
* 基于 fetch API 封装,支持流式请求
|
|
79
81
|
* @param config 请求配置
|
|
80
|
-
* @returns Promise<T> & { task?: RequestTask }
|
|
81
82
|
* @example
|
|
82
83
|
* // 在入口文件完成配置 (确保请求失败有toast提示,登录过期能够触发重新登录,log有日志输出)
|
|
83
84
|
* setBaseToolsConfig({
|
|
@@ -100,7 +101,7 @@ export type ChunkCallback = (response: {
|
|
|
100
101
|
* export function requestApi<T>(config: RequestConfig) {
|
|
101
102
|
* return request<T>({
|
|
102
103
|
* header: { token: 'xx', version: 'xx', tid: 'xx' }, // 会自动过滤空值
|
|
103
|
-
* //
|
|
104
|
+
* // resMap: (res) => res, // 响应拦截,可预处理响应数据,如解密 (可选)
|
|
104
105
|
* resKey: 'data',
|
|
105
106
|
* msgKey: 'message',
|
|
106
107
|
* codeKey: 'status',
|
|
@@ -122,32 +123,36 @@ export type ChunkCallback = (response: {
|
|
|
122
123
|
* return requestApi<GoodItem[]>({ url: '/goods/list', resKey: 'data.list', ...config });
|
|
123
124
|
* }
|
|
124
125
|
*
|
|
125
|
-
* const goodList = await apiGoodList({ data: { page:1, size:10 }
|
|
126
|
+
* const goodList = await apiGoodList({ data: { page:1, size:10 } });
|
|
126
127
|
*
|
|
127
128
|
* // 3. 基于上面 requestApi 的流式接口
|
|
128
|
-
* export function apiChatStream(
|
|
129
|
-
* return requestApi
|
|
129
|
+
* export function apiChatStream(config: RequestConfig) {
|
|
130
|
+
* return requestApi({
|
|
131
|
+
* ...config,
|
|
130
132
|
* url: '/sse/chatStream',
|
|
131
|
-
* data,
|
|
132
133
|
* resKey: false,
|
|
133
134
|
* showLoading: false,
|
|
134
|
-
* responseType: 'arraybuffer',
|
|
135
|
-
* enableChunked: true,
|
|
135
|
+
* responseType: 'arraybuffer', // 流式响应类型
|
|
136
|
+
* enableChunked: true, // 开启分块传输
|
|
136
137
|
* });
|
|
137
138
|
* }
|
|
138
139
|
*
|
|
139
|
-
*
|
|
140
|
+
* // 流式监听
|
|
141
|
+
* const onTaskReady = (task: RequestTask) => {
|
|
142
|
+
* task.onChunkReceived((res) => {
|
|
143
|
+
* console.log('ArrayBuffer', res.data);
|
|
144
|
+
* });
|
|
145
|
+
* }
|
|
140
146
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* });
|
|
147
|
+
* // 流式发起
|
|
148
|
+
* const data: ChatData = { content: '你好', conversationId: 123 };
|
|
149
|
+
* await apiChatStream({ data, onTaskReady });
|
|
144
150
|
*
|
|
145
|
-
*
|
|
146
|
-
* task
|
|
151
|
+
* // 流式取消 (在组件销毁或页面关闭时调用)
|
|
152
|
+
* task?.offChunkReceived(); // 取消监听,中断流式接收
|
|
153
|
+
* task?.abort(); // 取消请求 (若流式已生成,此时abort无效,因为请求已成功)
|
|
147
154
|
*/
|
|
148
|
-
export declare function request<T, D extends RequestData = RequestData>(config: RequestConfigBase<D>): Promise<T
|
|
149
|
-
task?: RequestTask;
|
|
150
|
-
};
|
|
155
|
+
export declare function request<T, D extends RequestData = RequestData>(config: RequestConfigBase<D>): Promise<T>;
|
|
151
156
|
/**
|
|
152
157
|
* 参数过滤undefined, 避免接口处理异常 (不可过滤 null 、 "" 、 false 、 0 这些有效值)
|
|
153
158
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/web/network/request.ts"],"names":[],"mappings":"AAUA,aAAa;AACb,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,MAAM,GACN,KAAK,GACL,QAAQ,GACR,SAAS,GACT,MAAM,GACN,SAAS,GACT,OAAO,GACP,OAAO,CAAC;AAEZ;;;GAGG;AACH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,WAAW,GACX,eAAe,GACf,IAAI,GACJ,QAAQ,GACR,eAAe,GACf,cAAc,CAAC,UAAU,CAAC,GAC1B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,OAAO,EAAE,GACT,IAAI,CAAC;AAET;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;AAEpG;;GAEG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/F;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IACnE,WAAW;IACX,GAAG,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/web/network/request.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 写入 localStorage(自动 JSON 序列化)
|
|
3
|
-
* 当 `value` 为 `null` 或 `undefined` 时,会移除该键。
|
|
4
|
-
* 支持保存:对象、数组、字符串、数字、布尔值。
|
|
5
3
|
* @param key 键名
|
|
6
|
-
* @param value
|
|
4
|
+
* @param value 任意可序列化的值:对象、数组、字符串、数字、布尔值。(`null` 或 `undefined` 会自动移除该键)
|
|
7
5
|
* @param days 过期天数(从当前时间起算)
|
|
8
6
|
* @example
|
|
9
7
|
* setLocalStorage('user', { id: 1, name: 'Alice' }); // 对象
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web/storage/index.ts"],"names":[],"mappings":"AAMA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web/storage/index.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,QAiBzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAiBlE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,QAE7C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@base-web-kits/base-tools-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"description": "Independent Web utilities package built from src/web.",
|
|
6
6
|
"keywords": [
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@base-web-kits/base-tools-ts": "^1.
|
|
28
|
+
"@base-web-kits/base-tools-ts": "^1.2.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"axios": ">=0.18.0"
|
package/src/web/async/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { cloneDeep } from '@base-web-kits/base-tools-ts';
|
|
1
2
|
import { getBaseToolsConfig } from '../index';
|
|
3
|
+
import type { AppLogInfo } from '../index';
|
|
2
4
|
|
|
3
5
|
type WebApi<Option = any, Res = any, Config = any> = (
|
|
4
6
|
option: Option,
|
|
@@ -20,6 +22,12 @@ export type WebApiConfig<Res = any, Err = any> = {
|
|
|
20
22
|
|
|
21
23
|
/** 是否显示日志, 默认 true */
|
|
22
24
|
showLog?: boolean;
|
|
25
|
+
|
|
26
|
+
/** 成功响应数据的处理, 如解密操作 (返回值在成功日志中输出'resMap'字段) */
|
|
27
|
+
resMap?: (res: any) => Res;
|
|
28
|
+
|
|
29
|
+
/** 成功和失败时,额外输出的日志数据 (可覆盖内部log参数,如'name') */
|
|
30
|
+
logExtra?: Record<string, unknown>;
|
|
23
31
|
};
|
|
24
32
|
|
|
25
33
|
/**
|
|
@@ -42,7 +50,10 @@ export function enhanceWebApi<Option = any, Res = any, Err = any, Config = any>(
|
|
|
42
50
|
toastSuccess = false,
|
|
43
51
|
toastError = true,
|
|
44
52
|
showLog = true,
|
|
53
|
+
resMap,
|
|
54
|
+
logExtra,
|
|
45
55
|
} = finalConfig;
|
|
56
|
+
|
|
46
57
|
const {
|
|
47
58
|
log,
|
|
48
59
|
toast,
|
|
@@ -60,15 +71,30 @@ export function enhanceWebApi<Option = any, Res = any, Err = any, Config = any>(
|
|
|
60
71
|
webApi(option, finalConfig)
|
|
61
72
|
.then((res) => {
|
|
62
73
|
if (showLoading) hideLoadingFn?.();
|
|
63
|
-
if (showLog) log?.('info', { name: fname, status: 'success', option, res });
|
|
64
|
-
resolve(res);
|
|
65
74
|
|
|
66
|
-
const
|
|
75
|
+
const finalRes = resMap ? resMap(res) : res;
|
|
76
|
+
|
|
77
|
+
if (showLog) {
|
|
78
|
+
const logData: AppLogInfo = { name: fname, status: 'success', option, ...logExtra };
|
|
79
|
+
|
|
80
|
+
if (resMap) {
|
|
81
|
+
logData.res = res; // 输出原始数据
|
|
82
|
+
logData.resMap = cloneDeep(finalRes); // 深拷贝处理后数据,避免外部修改对象,造成输出不一致
|
|
83
|
+
} else {
|
|
84
|
+
logData.res = cloneDeep(res); // 深拷贝原始数据,避免外部修改对象,造成输出不一致
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
log?.('info', logData);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
resolve(finalRes);
|
|
91
|
+
|
|
92
|
+
const msg = typeof toastSuccess === 'function' ? toastSuccess(finalRes) : toastSuccess;
|
|
67
93
|
if (msg) toast?.({ msg, status: 'success' });
|
|
68
94
|
})
|
|
69
95
|
.catch((e) => {
|
|
70
96
|
if (showLoading) hideLoadingFn?.();
|
|
71
|
-
if (showLog) log?.('error', { name: fname, status: 'fail', option, e });
|
|
97
|
+
if (showLog) log?.('error', { name: fname, status: 'fail', option, e, ...logExtra });
|
|
72
98
|
|
|
73
99
|
const msg = typeof toastError === 'function' ? toastError(e) : toastError;
|
|
74
100
|
if (msg) {
|
|
@@ -52,12 +52,16 @@ export type RequestConfig<D extends RequestData = RequestData> = Partial<Request
|
|
|
52
52
|
export type RequestConfigBase<D extends RequestData = RequestData> = {
|
|
53
53
|
/** 接口地址 */
|
|
54
54
|
url: string;
|
|
55
|
+
|
|
55
56
|
/** 请求方法 */
|
|
56
57
|
method?: RequestMethod;
|
|
58
|
+
|
|
57
59
|
/** 请求头(会自动过滤undefined, null, "";不过滤0和false; 数字和布尔值会自动转换为字符串) */
|
|
58
60
|
header?: Record<string, string | number | boolean | null | undefined>;
|
|
61
|
+
|
|
59
62
|
/** 请求参数 */
|
|
60
63
|
data?: D;
|
|
64
|
+
|
|
61
65
|
/** 超时时间 (毫秒), 默认 60000 */
|
|
62
66
|
timeout?: number;
|
|
63
67
|
|
|
@@ -79,29 +83,32 @@ export type RequestConfigBase<D extends RequestData = RequestData> = {
|
|
|
79
83
|
/** 登录过期状态码 */
|
|
80
84
|
reloginCode: (number | string)[];
|
|
81
85
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
/** 是否提示接口异常 (默认true) */
|
|
86
|
-
toastError?: boolean;
|
|
87
|
-
|
|
88
|
-
/** 是否输出日志 (默认true) */
|
|
89
|
-
isLog?: boolean;
|
|
86
|
+
/** 是否开启流式传输 (如 SSE) */
|
|
87
|
+
enableChunked?: boolean;
|
|
90
88
|
|
|
91
|
-
/**
|
|
92
|
-
|
|
89
|
+
/** 响应类型 (默认 json, enableChunked为true时忽略) */
|
|
90
|
+
responseType?: 'text' | 'arraybuffer' | 'json';
|
|
93
91
|
|
|
94
92
|
/** 响应数据的缓存时间, 单位毫秒。仅在成功时缓存;仅缓存在内存,应用退出,缓存消失。(默认0,不开启缓存) */
|
|
95
93
|
cacheTime?: number;
|
|
96
94
|
|
|
97
|
-
/**
|
|
98
|
-
|
|
95
|
+
/** 是否提示接口异常 (默认true) */
|
|
96
|
+
toastError?: boolean;
|
|
99
97
|
|
|
100
|
-
/**
|
|
101
|
-
|
|
98
|
+
/** 是否显示进度条: 支持字符串,自定义文本 (默认true) */
|
|
99
|
+
showLoading?: boolean | string;
|
|
100
|
+
|
|
101
|
+
/** 是否输出日志 (默认true) */
|
|
102
|
+
showLog?: boolean;
|
|
103
|
+
|
|
104
|
+
/** 成功和失败时,额外输出的日志数据 (可覆盖内部log参数,如'name') */
|
|
105
|
+
logExtra?: Record<string, unknown>;
|
|
102
106
|
|
|
103
107
|
/** 响应拦截 */
|
|
104
|
-
|
|
108
|
+
resMap?: (data: ResponseData) => ResponseData;
|
|
109
|
+
|
|
110
|
+
/** 获取task对象, 用于取消请求或监听流式数据 */
|
|
111
|
+
onTaskReady?: (task: RequestTask) => void;
|
|
105
112
|
};
|
|
106
113
|
|
|
107
114
|
/**
|
|
@@ -130,7 +137,6 @@ const requestCache = new Map<string, { res: unknown; expire: number }>();
|
|
|
130
137
|
* 基础请求 (返回 Promise 和 Task 对象)
|
|
131
138
|
* 基于 fetch API 封装,支持流式请求
|
|
132
139
|
* @param config 请求配置
|
|
133
|
-
* @returns Promise<T> & { task?: RequestTask }
|
|
134
140
|
* @example
|
|
135
141
|
* // 在入口文件完成配置 (确保请求失败有toast提示,登录过期能够触发重新登录,log有日志输出)
|
|
136
142
|
* setBaseToolsConfig({
|
|
@@ -153,7 +159,7 @@ const requestCache = new Map<string, { res: unknown; expire: number }>();
|
|
|
153
159
|
* export function requestApi<T>(config: RequestConfig) {
|
|
154
160
|
* return request<T>({
|
|
155
161
|
* header: { token: 'xx', version: 'xx', tid: 'xx' }, // 会自动过滤空值
|
|
156
|
-
* //
|
|
162
|
+
* // resMap: (res) => res, // 响应拦截,可预处理响应数据,如解密 (可选)
|
|
157
163
|
* resKey: 'data',
|
|
158
164
|
* msgKey: 'message',
|
|
159
165
|
* codeKey: 'status',
|
|
@@ -175,30 +181,57 @@ const requestCache = new Map<string, { res: unknown; expire: number }>();
|
|
|
175
181
|
* return requestApi<GoodItem[]>({ url: '/goods/list', resKey: 'data.list', ...config });
|
|
176
182
|
* }
|
|
177
183
|
*
|
|
178
|
-
* const goodList = await apiGoodList({ data: { page:1, size:10 }
|
|
184
|
+
* const goodList = await apiGoodList({ data: { page:1, size:10 } });
|
|
179
185
|
*
|
|
180
186
|
* // 3. 基于上面 requestApi 的流式接口
|
|
181
|
-
* export function apiChatStream(
|
|
182
|
-
* return requestApi
|
|
187
|
+
* export function apiChatStream(config: RequestConfig) {
|
|
188
|
+
* return requestApi({
|
|
189
|
+
* ...config,
|
|
183
190
|
* url: '/sse/chatStream',
|
|
184
|
-
* data,
|
|
185
191
|
* resKey: false,
|
|
186
192
|
* showLoading: false,
|
|
187
|
-
* responseType: 'arraybuffer',
|
|
188
|
-
* enableChunked: true,
|
|
193
|
+
* responseType: 'arraybuffer', // 流式响应类型
|
|
194
|
+
* enableChunked: true, // 开启分块传输
|
|
189
195
|
* });
|
|
190
196
|
* }
|
|
191
197
|
*
|
|
192
|
-
*
|
|
198
|
+
* // 流式监听
|
|
199
|
+
* const onTaskReady = (task: RequestTask) => {
|
|
200
|
+
* task.onChunkReceived((res) => {
|
|
201
|
+
* console.log('ArrayBuffer', res.data);
|
|
202
|
+
* });
|
|
203
|
+
* }
|
|
193
204
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* });
|
|
205
|
+
* // 流式发起
|
|
206
|
+
* const data: ChatData = { content: '你好', conversationId: 123 };
|
|
207
|
+
* await apiChatStream({ data, onTaskReady });
|
|
197
208
|
*
|
|
198
|
-
*
|
|
199
|
-
* task
|
|
209
|
+
* // 流式取消 (在组件销毁或页面关闭时调用)
|
|
210
|
+
* task?.offChunkReceived(); // 取消监听,中断流式接收
|
|
211
|
+
* task?.abort(); // 取消请求 (若流式已生成,此时abort无效,因为请求已成功)
|
|
200
212
|
*/
|
|
201
213
|
export function request<T, D extends RequestData = RequestData>(config: RequestConfigBase<D>) {
|
|
214
|
+
const {
|
|
215
|
+
url,
|
|
216
|
+
data,
|
|
217
|
+
header,
|
|
218
|
+
method = 'GET',
|
|
219
|
+
resKey,
|
|
220
|
+
msgKey,
|
|
221
|
+
codeKey,
|
|
222
|
+
successKey,
|
|
223
|
+
successCode,
|
|
224
|
+
reloginCode,
|
|
225
|
+
showLoading = true,
|
|
226
|
+
toastError = true,
|
|
227
|
+
enableChunked = false,
|
|
228
|
+
cacheTime,
|
|
229
|
+
resMap,
|
|
230
|
+
responseType = 'json',
|
|
231
|
+
timeout = 60000,
|
|
232
|
+
onTaskReady,
|
|
233
|
+
} = config;
|
|
234
|
+
|
|
202
235
|
// 1. 初始化控制对象
|
|
203
236
|
const controller = new AbortController();
|
|
204
237
|
const signal = controller.signal;
|
|
@@ -214,30 +247,11 @@ export function request<T, D extends RequestData = RequestData>(config: RequestC
|
|
|
214
247
|
chunkCallback = null;
|
|
215
248
|
},
|
|
216
249
|
};
|
|
250
|
+
onTaskReady?.(task);
|
|
217
251
|
|
|
218
252
|
// 2. 创建 Promise
|
|
219
|
-
|
|
253
|
+
return new Promise<T>((resolve, reject) => {
|
|
220
254
|
const execute = async () => {
|
|
221
|
-
const {
|
|
222
|
-
url,
|
|
223
|
-
data,
|
|
224
|
-
header,
|
|
225
|
-
method = 'GET',
|
|
226
|
-
resKey,
|
|
227
|
-
msgKey,
|
|
228
|
-
codeKey,
|
|
229
|
-
successKey,
|
|
230
|
-
successCode,
|
|
231
|
-
reloginCode,
|
|
232
|
-
showLoading = true,
|
|
233
|
-
toastError = true,
|
|
234
|
-
enableChunked = false,
|
|
235
|
-
cacheTime,
|
|
236
|
-
responseInterceptor,
|
|
237
|
-
responseType = 'json',
|
|
238
|
-
timeout = 60000,
|
|
239
|
-
} = config;
|
|
240
|
-
|
|
241
255
|
const isGet = method === 'GET';
|
|
242
256
|
const isObjectData = isPlainObject(data);
|
|
243
257
|
const isArrayData = !isObjectData && Array.isArray(data);
|
|
@@ -349,7 +363,7 @@ export function request<T, D extends RequestData = RequestData>(config: RequestC
|
|
|
349
363
|
if (showLoading) appConfig.hideLoading?.();
|
|
350
364
|
|
|
351
365
|
// 响应拦截
|
|
352
|
-
const res =
|
|
366
|
+
const res = resMap ? resMap(resData) : resData;
|
|
353
367
|
|
|
354
368
|
// 2.10 业务状态码解析
|
|
355
369
|
const code = getObjectValue(res, codeKey);
|
|
@@ -395,12 +409,7 @@ export function request<T, D extends RequestData = RequestData>(config: RequestC
|
|
|
395
409
|
};
|
|
396
410
|
|
|
397
411
|
execute();
|
|
398
|
-
})
|
|
399
|
-
|
|
400
|
-
// 3. 挂载 Task
|
|
401
|
-
promise.task = task;
|
|
402
|
-
|
|
403
|
-
return promise;
|
|
412
|
+
});
|
|
404
413
|
}
|
|
405
414
|
|
|
406
415
|
/**
|
|
@@ -439,12 +448,12 @@ function logRequestInfo(options: {
|
|
|
439
448
|
e?: unknown;
|
|
440
449
|
}) {
|
|
441
450
|
const { log } = getBaseToolsConfig();
|
|
442
|
-
const {
|
|
451
|
+
const { showLog = true } = options.config;
|
|
443
452
|
|
|
444
|
-
if (!log || !
|
|
453
|
+
if (!log || !showLog) return;
|
|
445
454
|
|
|
446
455
|
const { config, res, fromCache = false, startTime, status, e } = options;
|
|
447
|
-
const { url, data, header, method,
|
|
456
|
+
const { url, data, header, method, logExtra } = config;
|
|
448
457
|
const endTime = Date.now();
|
|
449
458
|
const fmt = 'YYYY-MM-DD HH:mm:ss.SSS';
|
|
450
459
|
|
|
@@ -459,7 +468,7 @@ function logRequestInfo(options: {
|
|
|
459
468
|
startTime: toDayjs(startTime).format(fmt),
|
|
460
469
|
endTime: toDayjs(endTime).format(fmt),
|
|
461
470
|
duration: endTime - startTime,
|
|
462
|
-
...
|
|
471
|
+
...logExtra,
|
|
463
472
|
};
|
|
464
473
|
|
|
465
474
|
if (status === 'success') {
|
package/src/web/storage/index.ts
CHANGED
|
@@ -6,10 +6,8 @@ const WK = {
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 写入 localStorage(自动 JSON 序列化)
|
|
9
|
-
* 当 `value` 为 `null` 或 `undefined` 时,会移除该键。
|
|
10
|
-
* 支持保存:对象、数组、字符串、数字、布尔值。
|
|
11
9
|
* @param key 键名
|
|
12
|
-
* @param value
|
|
10
|
+
* @param value 任意可序列化的值:对象、数组、字符串、数字、布尔值。(`null` 或 `undefined` 会自动移除该键)
|
|
13
11
|
* @param days 过期天数(从当前时间起算)
|
|
14
12
|
* @example
|
|
15
13
|
* setLocalStorage('user', { id: 1, name: 'Alice' }); // 对象
|