@jetlinks-web/core 2.2.7 → 2.2.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetlinks-web/core",
3
- "version": "2.2.7",
3
+ "version": "2.2.8",
4
4
  "main": "index.ts",
5
5
  "module": "index.ts",
6
6
  "keywords": [
@@ -21,8 +21,8 @@
21
21
  "dependencies": {
22
22
  "axios": "^1.7.4",
23
23
  "rxjs": "^7.8.1",
24
- "@jetlinks-web/constants": "^1.0.9",
25
24
  "@jetlinks-web/types": "^1.0.2",
25
+ "@jetlinks-web/constants": "^1.0.9",
26
26
  "@jetlinks-web/utils": "^1.2.8"
27
27
  },
28
28
  "publishConfig": {
package/src/axios.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { TOKEN_KEY, BASE_API } from '@jetlinks-web/constants'
1
+ import { TOKEN_KEY, BASE_API, LOCAL_BASE_API } from '@jetlinks-web/constants'
2
2
  import {getToken, randomString} from '@jetlinks-web/utils'
3
3
  import axios from 'axios'
4
4
  import type {
@@ -87,7 +87,7 @@ const handleRequest = (config: InternalAxiosRequestConfig) => {
87
87
  requestRecords(config)
88
88
  const token = getToken();
89
89
  const lang = localStorage.getItem(_options.langKey)
90
- const localBaseApi = localStorage.getItem('')
90
+ const localBaseApi = localStorage.getItem(LOCAL_BASE_API)
91
91
 
92
92
  if (lang) {
93
93
  config.headers[_options.langKey] = lang
package/src/axios.ts~ ADDED
@@ -0,0 +1,419 @@
1
+ import { TOKEN_KEY, BASE_API } from '@jetlinks-web/constants'
2
+ import {getToken, randomString} from '@jetlinks-web/utils'
3
+ import axios from 'axios'
4
+ import type {
5
+ AxiosInstance,
6
+ AxiosResponse,
7
+ AxiosError,
8
+ InternalAxiosRequestConfig,
9
+ } from 'axios'
10
+ import type { AxiosResponseRewrite } from '@jetlinks-web/types'
11
+ import {isFunction, isObject} from 'lodash-es'
12
+
13
+ interface Options {
14
+
15
+ tokenExpiration: (err: AxiosError<any>, response: AxiosResponse) => void
16
+ handleReconnect: () => Promise<any>
17
+ filter_url?: Array<string>
18
+ code?: number
19
+ codeKey?: string
20
+ timeout?: number
21
+ handleRequest?: () => void
22
+ /**
23
+ * 用以获取localstorage中的lang
24
+ */
25
+ langKey?: string
26
+ /**
27
+ * response处理函数
28
+ * @param response AxiosResponse实例
29
+ */
30
+ handleResponse?: (response: AxiosResponse) => void
31
+ /**
32
+ * 错误处理函数
33
+ * @param msg 错误消息
34
+ * @param status 错误code
35
+ * @param error 错误实例
36
+ */
37
+ handleError?: (msg: string, status: string | number, error: AxiosError<any>) => void
38
+ requestOptions?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Record<string, any>
39
+ isCreateTokenRefresh?: boolean
40
+ }
41
+
42
+ interface RequestOptions {
43
+ url?: string
44
+ method?: string
45
+ params?: any
46
+ data?: any
47
+ [key: string]: any
48
+ }
49
+
50
+ let instance: AxiosInstance
51
+ let _options: Options = {
52
+ filter_url: [],
53
+ code: 200,
54
+ codeKey: 'status',
55
+ timeout: 1000 * 15,
56
+ handleRequest: undefined,
57
+ handleResponse: undefined,
58
+ handleError: undefined,
59
+ langKey: 'lang',
60
+ requestOptions: (config) => ({}),
61
+ tokenExpiration: () => {},
62
+ handleReconnect: () => Promise.resolve(),
63
+ isCreateTokenRefresh: false
64
+ }
65
+
66
+ let failedQueue = [];
67
+ let isRefreshing = false;
68
+
69
+ const isApp = (window as any).__MICRO_APP_ENVIRONMENT__
70
+
71
+ const pendingRequests = new Map<string, AbortController>();
72
+ const requestRecords = (config: InternalAxiosRequestConfig) => {
73
+ const key = randomString(32)
74
+
75
+ // 取消重复请求
76
+ if (pendingRequests.has(key)) {
77
+ pendingRequests.get(key)?.abort()
78
+ }
79
+
80
+ const controller = new AbortController()
81
+ config.signal = controller.signal;
82
+ config.__requestKey = key;
83
+
84
+ pendingRequests.set(key, controller)
85
+ }
86
+ const handleRequest = (config: InternalAxiosRequestConfig) => {
87
+ requestRecords(config)
88
+ const token = getToken();
89
+ const lang = localStorage.getItem(_options.langKey)
90
+ const localBaseApi = localStorage.getItem('')
91
+
92
+ if (lang) {
93
+ config.headers[_options.langKey] = lang
94
+ }
95
+
96
+ if (localBaseApi && !config.baseURL) {
97
+ const _url = config.url.startsWith('/') ? config.url : `/${config.url}`
98
+ config.url = localBaseApi + _url
99
+ }
100
+
101
+ // 没有token,并且该接口需要token校验
102
+ if (!token && !_options.filter_url?.some((url) => config.url?.includes(url))) {
103
+ // 跳转登录页
104
+ _options.tokenExpiration?.()
105
+ return config
106
+ }
107
+
108
+ if (!config.headers[TOKEN_KEY]) {
109
+ config.headers[TOKEN_KEY] = token
110
+ }
111
+
112
+ if (_options.requestOptions && isFunction(_options.requestOptions)) {
113
+ const extraOptions = _options.requestOptions(config)
114
+ if (extraOptions && isObject(extraOptions)) {
115
+ for (const key in extraOptions) {
116
+ config[key] = extraOptions[key]
117
+ }
118
+ }
119
+ }
120
+
121
+ return config
122
+ }
123
+
124
+ const handleResponse = (response: AxiosResponse) => {
125
+ if (_options.handleResponse && isFunction(_options.handleResponse)) {
126
+ return _options.handleResponse(response)
127
+ }
128
+
129
+ const __key = response.config?.__requestKey
130
+ if(__key){
131
+ pendingRequests.delete(__key)
132
+ }
133
+
134
+ if (response.data instanceof ArrayBuffer) {
135
+ return response
136
+ }
137
+
138
+ const status = response.data[_options.codeKey || 'status']
139
+
140
+ // 增加业务接口处理成功判断方式,只需要判断返回参数包含:success为true
141
+ if (
142
+ typeof response.data === 'object' &&
143
+ typeof response.data.success === 'undefined'
144
+ ) {
145
+ response.data.success = status === _options.code
146
+ }
147
+
148
+ return response.data
149
+ }
150
+
151
+ const createTokenRefreshHandler = async (err) => {
152
+ const originalRequest = err.config;
153
+ if (isRefreshing) { // 记录之后失败的请求
154
+ return new Promise((resolve, reject) => {
155
+ failedQueue.push({ resolve, reject });
156
+ }).then((_token) => {
157
+ originalRequest.headers[TOKEN_KEY] = _token;
158
+ return instance(originalRequest)
159
+ }).catch(err => Promise.reject(err))
160
+ }
161
+ originalRequest._retry = true;
162
+ isRefreshing = true;
163
+ try {
164
+ const loginResult = await _options.handleReconnect?.()
165
+ if(loginResult){
166
+ const token = getToken() // 更新请求头, 修改全部的token
167
+ originalRequest.headers[TOKEN_KEY] = token;
168
+ failedQueue.forEach(a => a.resolve(token));
169
+ return instance(originalRequest);
170
+ }
171
+ } catch (err) {
172
+ failedQueue.forEach(cb => cb.reject(err));
173
+ throw err;
174
+ } finally {
175
+ failedQueue = [];
176
+ isRefreshing = false;
177
+ }
178
+ }
179
+ const errorHandler = async (err: AxiosError<any>) => {
180
+ let description = err.response?.message || 'Error'
181
+ let _status: string | number = 0
182
+ if (err.response) {
183
+ const {data, status} = err.response
184
+ _status = status
185
+ switch (status) {
186
+ case 400:
187
+ case 403:
188
+ case 500:
189
+ description = (`${data?.message}`).substring(0, 90)
190
+ break;
191
+ case 401:
192
+ description = err.response.data.result?.text || '用户未登录';
193
+ _options.tokenExpiration?.(err)
194
+ if(_options.isCreateTokenRefresh){
195
+ return createTokenRefreshHandler(err)
196
+ }
197
+ break;
198
+ case 404:
199
+ description = err?.response?.data?.message || `${data?.error} ${data?.path}`
200
+ break;
201
+ default:
202
+ break;
203
+ }
204
+ } else if (err.response === undefined) {
205
+ description = err.message.includes('timeout') ? '接口响应超时' : err.message
206
+ _status = 'timeout'
207
+ }
208
+
209
+ if (_options.handleError && isFunction(_options.handleError)) {
210
+ _options.handleError(description, _status, err)
211
+ }
212
+
213
+ return Promise.reject(err)
214
+ }
215
+
216
+ export const abortAllRequests = () => {
217
+ pendingRequests.forEach(controller => controller.abort())
218
+ pendingRequests.clear()
219
+ }
220
+
221
+ export const crateAxios = (options: Options) => {
222
+ if (options) {
223
+ _options = Object.assign(_options, options)
224
+ }
225
+
226
+ instance = axios.create({
227
+ withCredentials: false,
228
+ timeout: _options.timeout,
229
+ baseURL: BASE_API
230
+ })
231
+
232
+ instance.interceptors.request.use(
233
+ handleRequest,
234
+ errorHandler
235
+ )
236
+
237
+ instance.interceptors.response.use(
238
+ handleResponse,
239
+ errorHandler
240
+ )
241
+ }
242
+
243
+ export const post = <T = any>(url: string, data: any = {}, ext?: any) => {
244
+ return (instance<any, AxiosResponseRewrite<T>>({
245
+ method: 'POST',
246
+ url,
247
+ data,
248
+ ...ext,
249
+ }))
250
+ }
251
+
252
+ export const get = <T = any>(url: string, params: any = undefined, ext?: any) => {
253
+ return instance<any, AxiosResponseRewrite<T>>({
254
+ method: 'GET',
255
+ url,
256
+ params,
257
+ ...ext,
258
+ })
259
+ }
260
+
261
+ export const put = <T = any>(url: string, data: any = {}, ext?: any) => {
262
+ return instance<any, AxiosResponseRewrite<T>>({
263
+ method: 'PUT',
264
+ url,
265
+ data,
266
+ ...ext,
267
+ })
268
+ }
269
+
270
+ export const patch = <T = any>(url: string, data: any = {}, ext?: any) => {
271
+ return instance<any, AxiosResponseRewrite<T>>({
272
+ method: 'patch',
273
+ url,
274
+ data,
275
+ ...ext,
276
+ })
277
+ }
278
+
279
+ export const remove = <T = any>(url: string, params: any = undefined, ext?: any) => {
280
+ return instance<any, AxiosResponseRewrite<T>>({
281
+ method: 'DELETE',
282
+ url,
283
+ params,
284
+ ...ext,
285
+ })
286
+ }
287
+
288
+ export const getStream = (url: string, params?: any, ext?: any) => {
289
+ return get(url, params, { responseType: 'arraybuffer', ...ext })
290
+ }
291
+
292
+ export const postStream = (url: string, data: any, ext?: any) => {
293
+ return post(url, data, { responseType: 'arraybuffer', ...ext })
294
+ }
295
+
296
+ export const request = {
297
+ post, get, put, patch, remove, getStream, postStream
298
+ }
299
+
300
+ export class Request {
301
+ modulePath: string
302
+
303
+ constructor(modulePath: string) {
304
+ this.modulePath = modulePath
305
+ }
306
+
307
+ /**
308
+ * 分页查询
309
+ * @param {object} data 查询参数
310
+ * @param {object} options 请求配置
311
+ * @returns {Promise<AxiosResponse<any>>} 分页查询结果
312
+ */
313
+ page(data: any={}, options: RequestOptions= {
314
+ url: undefined,
315
+ method: undefined,
316
+ }) {
317
+ const { url='/_query', method = 'post', ...rest } = options
318
+ return request[method](`${this.modulePath}${url}`, data, rest)
319
+ }
320
+
321
+ /**
322
+ * 不分页查询
323
+ * @param {object} data 查询参数
324
+ * @param {object} options 请求配置
325
+ * @returns {Promise<AxiosResponse<any>>} 不分页查询结果
326
+ */
327
+ noPage(data: any={}, options: RequestOptions = {
328
+ url: undefined,
329
+ method: undefined,
330
+ }) {
331
+ const { url='/_query/no-page', method = 'post', ...rest } = options
332
+ return request[method](`${this.modulePath}${url}`, { paging: false, ...data}, rest)
333
+ }
334
+
335
+ /**
336
+ * 详情查询
337
+ * @param {string} id 详情ID
338
+ * @param {object} params 查询参数
339
+ * @param {object} options 请求配置
340
+ * @returns {Promise<AxiosResponse<any>>} 详情查询结果
341
+ */
342
+ detail(id: string, params?: any, options: RequestOptions= {
343
+ url: undefined,
344
+ method: undefined,
345
+ }) {
346
+ const { url=`/${id}/detail`, method = 'get', ...rest } = options
347
+ return request[method](`${this.modulePath}${url}`, params, rest)
348
+ }
349
+
350
+ /**
351
+ * 保存
352
+ * @param {object} data 保存参数
353
+ * @param {object} options 请求配置
354
+ * @returns {Promise<AxiosResponse<any>>} 保存结果
355
+ */
356
+ save(data: any={}, options: RequestOptions = {
357
+ url: undefined,
358
+ method: undefined,
359
+ }) {
360
+ const { url=`/_create`, method = 'post', ...rest } = options
361
+ return request[method](`${this.modulePath}${url}`, data, rest)
362
+ }
363
+
364
+ /**
365
+ * 更新
366
+ * @param {object} data 更新参数
367
+ * @param {object} options 请求配置
368
+ * @returns {Promise<AxiosResponse<any>>} 更新结果
369
+ */
370
+ update(data: any={}, options: RequestOptions = {
371
+ url: undefined,
372
+ method: undefined,
373
+ }) {
374
+ const { url=`/_update`, method = 'patch', ...rest } = options
375
+ return patch(`${this.modulePath}${url}`, data, rest)
376
+ }
377
+
378
+ /**
379
+ * 删除
380
+ * @param {string} id 删除ID
381
+ * @param {object} options 请求配置
382
+ * @returns {Promise<AxiosResponse<any>>} 删除结果
383
+ */
384
+ delete(id: string, params?: any, options: RequestOptions = {
385
+ url: undefined,
386
+ method: undefined,
387
+ }) {
388
+ const { url=`/${id}`, method = 'post', ...rest } = options
389
+ return remove(`${this.modulePath}${url}`, params, rest)
390
+ }
391
+
392
+ post(...args) {
393
+ const [url, data, options] = args
394
+ return post(`${this.modulePath}${url}`, data, options)
395
+ }
396
+
397
+ get(...args) {
398
+ const [url, params, options] = args
399
+ return get(`${this.modulePath}${url}`, params, options)
400
+ }
401
+
402
+ put(...args) {
403
+ const [url, data, options] = args
404
+ return put(`${this.modulePath}${url}`, data, options)
405
+ }
406
+
407
+ patch(...args) {
408
+ const [url, data, options] = args
409
+ return patch(`${this.modulePath}${url}`, data, options)
410
+ }
411
+
412
+ remove(...args) {
413
+ const [url, params, options] = args
414
+ return remove(`${this.modulePath}${url}`, params, options)
415
+ }
416
+ }
417
+
418
+
419
+