@moluoxixi/ajax-package 0.0.32 → 0.0.33
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 +1016 -0
- package/es/BaseHttpClient.d.ts +111 -0
- package/es/SystemErrorDialog.d.ts +4 -0
- package/es/_types/api.d.ts +75 -0
- package/es/_types/emits.d.ts +7 -0
- package/es/_types/props.d.ts +18 -2
- package/es/_utils/messageWrapper.d.ts +4 -0
- package/es/_utils/notificationWrapper.d.ts +4 -0
- package/es/_utils/systemErrorInfo.d.ts +21 -0
- package/es/class.d.ts +180 -1
- package/es/index.mjs +5116 -47
- package/es/netseriver.d.ts +14 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
# AjaxPackage
|
|
2
|
+
|
|
3
|
+
统一的 HTTP 服务封装与约定。提供 `getHttpService` 工厂函数、`BaseApi` 类以及 Vue 插件等多种使用方式,内置超时、token、响应字段映射、错误处理等能力。
|
|
4
|
+
|
|
5
|
+
## 导出
|
|
6
|
+
|
|
7
|
+
- 函数:`getHttpService(options: HttpServiceOptions): HttpService`
|
|
8
|
+
- 创建 HTTP 服务实例,提供快捷方法
|
|
9
|
+
- 函数:`createHttpService(options: HttpServiceOptions): HttpService`
|
|
10
|
+
- 与 `getHttpService` 功能相同
|
|
11
|
+
- 类:`BaseApi`
|
|
12
|
+
- 基于 axios 封装的类式 API 请求工具
|
|
13
|
+
- 插件:`VueAxiosPlugin`
|
|
14
|
+
- Vue Axios 插件,提供全局的 `$http` 方法
|
|
15
|
+
|
|
16
|
+
## 快速开始
|
|
17
|
+
|
|
18
|
+
### 使用 getHttpService
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
22
|
+
|
|
23
|
+
const httpApi = getHttpService({
|
|
24
|
+
baseURL: 'https://api.example.com',
|
|
25
|
+
timeout: 5000,
|
|
26
|
+
getToken: () => localStorage.getItem('token'),
|
|
27
|
+
responseFields: {
|
|
28
|
+
code: 'Code',
|
|
29
|
+
message: 'Message',
|
|
30
|
+
data: 'data',
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
export function getUserList(params: any) {
|
|
35
|
+
return httpApi.get('/users', params)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createUser(data: any) {
|
|
39
|
+
return httpApi.post('/users', data)
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 使用 BaseApi 类
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
47
|
+
|
|
48
|
+
const api = new BaseApi({
|
|
49
|
+
baseURL: 'https://api.example.com',
|
|
50
|
+
timeout: 5000,
|
|
51
|
+
responseFields: {
|
|
52
|
+
code: 'code',
|
|
53
|
+
message: 'message',
|
|
54
|
+
data: 'data',
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
export async function getUserList(params: any) {
|
|
59
|
+
return api.get('/users', params)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function createUser(data: any) {
|
|
63
|
+
return api.post('/users', data)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 使用 Vue 插件
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createApp } from 'vue'
|
|
71
|
+
import VueAxiosPlugin from '@moluoxixi/ajaxpackage'
|
|
72
|
+
|
|
73
|
+
const app = createApp(App)
|
|
74
|
+
|
|
75
|
+
app.use(VueAxiosPlugin, {
|
|
76
|
+
default: {
|
|
77
|
+
baseURL: 'https://api.example.com',
|
|
78
|
+
timeout: 5000,
|
|
79
|
+
getToken: () => localStorage.getItem('token'),
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// 在组件中使用
|
|
84
|
+
export default {
|
|
85
|
+
async mounted() {
|
|
86
|
+
const data = await this.$http.get('/users')
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## getHttpService 配置项
|
|
92
|
+
|
|
93
|
+
### HttpServiceOptions
|
|
94
|
+
|
|
95
|
+
| 选项 | 说明 | 类型 | 必填 | 默认值 |
|
|
96
|
+
| --- | --- | --- | --- | --- |
|
|
97
|
+
| `baseURL` | 服务地址 | `string` | 是 | `''` |
|
|
98
|
+
| `timeout` | 超时时间(毫秒) | `number` | 否 | `5000` |
|
|
99
|
+
| `getToken` | 获取 token 的函数 | ^[Function]`() => string \| null` | 否 | `() => null` |
|
|
100
|
+
| `onLoginRequired` | 登录失效回调函数 | ^[Function]`() => void` | 否 | `() => { window.location.href = '/login?redirect=' + encodeURIComponent(window.location.href) }` |
|
|
101
|
+
| `responseFields` | 响应字段映射 | ^[Object]`{ code?: string; message?: string; data?: string; errors?: string; tips?: string }` | 否 | `{ code: 'code', message: 'msg', data: 'data' }` |
|
|
102
|
+
|
|
103
|
+
### responseFields 字段说明
|
|
104
|
+
|
|
105
|
+
| 字段 | 说明 | 类型 | 默认值 |
|
|
106
|
+
| --- | --- | --- | --- |
|
|
107
|
+
| `code` | 状态码字段名,支持路径解析(如 `'result.code'`) | `string` | `'code'` |
|
|
108
|
+
| `message` | 消息字段名,支持路径解析 | `string` | `'msg'` |
|
|
109
|
+
| `data` | 数据字段名,支持路径解析 | `string` | `'data'` |
|
|
110
|
+
| `errors` | 错误数组字段名 | `string` | `undefined` |
|
|
111
|
+
| `tips` | 提示信息字段名 | `string` | `undefined` |
|
|
112
|
+
|
|
113
|
+
### 使用示例:基础配置
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
117
|
+
|
|
118
|
+
const httpApi = getHttpService({
|
|
119
|
+
baseURL: 'https://api.example.com',
|
|
120
|
+
timeout: 5000,
|
|
121
|
+
getToken: () => {
|
|
122
|
+
return localStorage.getItem('token') || ''
|
|
123
|
+
},
|
|
124
|
+
responseFields: {
|
|
125
|
+
code: 'Code',
|
|
126
|
+
message: 'Message',
|
|
127
|
+
data: 'data',
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 使用示例:自定义响应字段映射
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
136
|
+
|
|
137
|
+
const httpApi = getHttpService({
|
|
138
|
+
baseURL: 'https://api.example.com',
|
|
139
|
+
timeout: 5000,
|
|
140
|
+
responseFields: {
|
|
141
|
+
code: 'status', // 自定义状态码字段
|
|
142
|
+
message: 'msg', // 自定义消息字段
|
|
143
|
+
data: 'result', // 自定义数据字段
|
|
144
|
+
errors: 'errorList', // 自定义错误数组字段
|
|
145
|
+
tips: 'tipList', // 自定义提示信息字段
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 使用示例:路径解析
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
154
|
+
|
|
155
|
+
const httpApi = getHttpService({
|
|
156
|
+
baseURL: 'https://api.example.com',
|
|
157
|
+
responseFields: {
|
|
158
|
+
code: 'result.code', // 支持嵌套路径解析
|
|
159
|
+
message: 'result.message',
|
|
160
|
+
data: 'result.data',
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 使用示例:登录失效处理
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
169
|
+
|
|
170
|
+
const httpApi = getHttpService({
|
|
171
|
+
baseURL: 'https://api.example.com',
|
|
172
|
+
getToken: () => localStorage.getItem('token'),
|
|
173
|
+
onLoginRequired: () => {
|
|
174
|
+
// 清除本地 token
|
|
175
|
+
localStorage.removeItem('token')
|
|
176
|
+
// 跳转到登录页
|
|
177
|
+
window.location.href = '/login?redirect=' + encodeURIComponent(window.location.href)
|
|
178
|
+
},
|
|
179
|
+
})
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## HttpService 实例方法
|
|
183
|
+
|
|
184
|
+
### get(url, params, config)
|
|
185
|
+
|
|
186
|
+
发送 GET 请求。
|
|
187
|
+
|
|
188
|
+
**参数:**
|
|
189
|
+
- `url: string` - 请求 URL
|
|
190
|
+
- `params?: object` - 查询参数
|
|
191
|
+
- `config?: AxiosRequestConfig` - axios 配置
|
|
192
|
+
|
|
193
|
+
**返回:** `Promise<any>`
|
|
194
|
+
|
|
195
|
+
**示例:**
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
199
|
+
|
|
200
|
+
const httpApi = getHttpService({
|
|
201
|
+
baseURL: 'https://api.example.com',
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
async function getUserList() {
|
|
205
|
+
const data = await httpApi.get('/users', { page: 1, size: 10 })
|
|
206
|
+
return data
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function getUserById(id: string) {
|
|
210
|
+
const data = await httpApi.get(`/users/${id}`)
|
|
211
|
+
return data
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function searchUsers(keyword: string) {
|
|
215
|
+
const data = await httpApi.get('/users/search', { keyword }, {
|
|
216
|
+
headers: {
|
|
217
|
+
'X-Custom-Header': 'custom-value',
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
return data
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### post(url, data, config, addSign)
|
|
225
|
+
|
|
226
|
+
发送 POST 请求。
|
|
227
|
+
|
|
228
|
+
**参数:**
|
|
229
|
+
- `url: string` - 请求 URL
|
|
230
|
+
- `data?: any` - 请求体数据
|
|
231
|
+
- `config?: AxiosRequestConfig` - axios 配置
|
|
232
|
+
- `addSign?: (config: AxiosRequestConfig) => void` - 签名函数,用于在请求前修改配置
|
|
233
|
+
|
|
234
|
+
**返回:** `Promise<any>`
|
|
235
|
+
|
|
236
|
+
**示例:**
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
240
|
+
import type { AxiosRequestConfig } from 'axios'
|
|
241
|
+
|
|
242
|
+
const httpApi = getHttpService({
|
|
243
|
+
baseURL: 'https://api.example.com',
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
async function createUser(userData: any) {
|
|
247
|
+
const data = await httpApi.post('/users', userData)
|
|
248
|
+
return data
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function updateUser(id: string, userData: any) {
|
|
252
|
+
const data = await httpApi.post(`/users/${id}`, userData, {
|
|
253
|
+
headers: {
|
|
254
|
+
'Content-Type': 'application/json',
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
return data
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function createUserWithSign(userData: any) {
|
|
261
|
+
function addSign(config: AxiosRequestConfig) {
|
|
262
|
+
const timestamp = Date.now()
|
|
263
|
+
const sign = generateSign(userData, timestamp)
|
|
264
|
+
config.headers = config.headers || {}
|
|
265
|
+
config.headers['X-Timestamp'] = timestamp.toString()
|
|
266
|
+
config.headers['X-Sign'] = sign
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const data = await httpApi.post('/users', userData, {}, addSign)
|
|
270
|
+
return data
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function generateSign(data: any, timestamp: number): string {
|
|
274
|
+
// 签名生成逻辑
|
|
275
|
+
return 'signature'
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### put(url, data, config)
|
|
280
|
+
|
|
281
|
+
发送 PUT 请求。
|
|
282
|
+
|
|
283
|
+
**参数:**
|
|
284
|
+
- `url: string` - 请求 URL
|
|
285
|
+
- `data?: any` - 请求体数据
|
|
286
|
+
- `config?: AxiosRequestConfig` - axios 配置
|
|
287
|
+
|
|
288
|
+
**返回:** `Promise<any>`
|
|
289
|
+
|
|
290
|
+
**示例:**
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
294
|
+
|
|
295
|
+
const httpApi = getHttpService({
|
|
296
|
+
baseURL: 'https://api.example.com',
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
async function updateUser(id: string, userData: any) {
|
|
300
|
+
const data = await httpApi.put(`/users/${id}`, userData)
|
|
301
|
+
return data
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function updateUserPartial(id: string, partialData: any) {
|
|
305
|
+
const data = await httpApi.put(`/users/${id}`, partialData, {
|
|
306
|
+
headers: {
|
|
307
|
+
'Content-Type': 'application/json-patch+json',
|
|
308
|
+
},
|
|
309
|
+
})
|
|
310
|
+
return data
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### delete(url, params, config)
|
|
315
|
+
|
|
316
|
+
发送 DELETE 请求。
|
|
317
|
+
|
|
318
|
+
**参数:**
|
|
319
|
+
- `url: string` - 请求 URL
|
|
320
|
+
- `params?: object` - 查询参数
|
|
321
|
+
- `config?: AxiosRequestConfig` - axios 配置
|
|
322
|
+
|
|
323
|
+
**返回:** `Promise<any>`
|
|
324
|
+
|
|
325
|
+
**示例:**
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
329
|
+
|
|
330
|
+
const httpApi = getHttpService({
|
|
331
|
+
baseURL: 'https://api.example.com',
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
async function deleteUser(id: string) {
|
|
335
|
+
const data = await httpApi.delete(`/users/${id}`)
|
|
336
|
+
return data
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function deleteUsers(ids: string[]) {
|
|
340
|
+
const data = await httpApi.delete('/users', { ids }, {
|
|
341
|
+
headers: {
|
|
342
|
+
'X-Batch-Delete': 'true',
|
|
343
|
+
},
|
|
344
|
+
})
|
|
345
|
+
return data
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### uploadFile(url, file, config)
|
|
350
|
+
|
|
351
|
+
上传文件。
|
|
352
|
+
|
|
353
|
+
**参数:**
|
|
354
|
+
- `url: string` - 上传 URL
|
|
355
|
+
- `file: File` - 要上传的文件
|
|
356
|
+
- `config?: AxiosRequestConfig` - axios 配置
|
|
357
|
+
|
|
358
|
+
**返回:** `Promise<any>`
|
|
359
|
+
|
|
360
|
+
**示例:**
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
364
|
+
|
|
365
|
+
const httpApi = getHttpService({
|
|
366
|
+
baseURL: 'https://api.example.com',
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
async function uploadAvatar(file: File) {
|
|
370
|
+
const data = await httpApi.uploadFile('/upload/avatar', file)
|
|
371
|
+
return data
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function uploadFileWithProgress(file: File, onProgress: (progress: number) => void) {
|
|
375
|
+
const data = await httpApi.uploadFile('/upload/file', file, {
|
|
376
|
+
onUploadProgress: (progressEvent) => {
|
|
377
|
+
const progress = progressEvent.total
|
|
378
|
+
? Math.round((progressEvent.loaded * 100) / progressEvent.total)
|
|
379
|
+
: 0
|
|
380
|
+
onProgress(progress)
|
|
381
|
+
},
|
|
382
|
+
})
|
|
383
|
+
return data
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### all(requests)
|
|
388
|
+
|
|
389
|
+
批量请求。
|
|
390
|
+
|
|
391
|
+
**参数:**
|
|
392
|
+
- `requests: Promise<any>[]` - 请求 Promise 数组
|
|
393
|
+
|
|
394
|
+
**返回:** `Promise<any[]>`
|
|
395
|
+
|
|
396
|
+
**示例:**
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
import { getHttpService } from '@moluoxixi/ajaxpackage'
|
|
400
|
+
|
|
401
|
+
const httpApi = getHttpService({
|
|
402
|
+
baseURL: 'https://api.example.com',
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
async function loadDashboardData() {
|
|
406
|
+
const [users, posts, comments] = await httpApi.all([
|
|
407
|
+
httpApi.get('/users'),
|
|
408
|
+
httpApi.get('/posts'),
|
|
409
|
+
httpApi.get('/comments'),
|
|
410
|
+
])
|
|
411
|
+
return { users, posts, comments }
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function loadUserData(userId: string) {
|
|
415
|
+
const [user, posts, followers] = await httpApi.all([
|
|
416
|
+
httpApi.get(`/users/${userId}`),
|
|
417
|
+
httpApi.get(`/users/${userId}/posts`),
|
|
418
|
+
httpApi.get(`/users/${userId}/followers`),
|
|
419
|
+
])
|
|
420
|
+
return { user, posts, followers }
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## BaseApi 类
|
|
425
|
+
|
|
426
|
+
### 构造函数配置
|
|
427
|
+
|
|
428
|
+
| 选项 | 说明 | 类型 | 必填 | 默认值 |
|
|
429
|
+
| --- | --- | --- | --- | --- |
|
|
430
|
+
| `baseURL` | 服务地址 | `string` | 是 | - |
|
|
431
|
+
| `timeout` | 超时时间(毫秒) | `number` | 否 | `5000` |
|
|
432
|
+
| `responseFields` | 响应字段映射 | ^[Object]`{ code?: string; message?: string; data?: string; errors?: string; tips?: string }` | 否 | `{ code: 'code', message: 'message', data: 'data', errors: 'errors', tips: 'tips' }` |
|
|
433
|
+
| `onTimeout` | 超时回调函数 | ^[Function]`() => void` | 否 | `() => {}` |
|
|
434
|
+
|
|
435
|
+
### BaseApi 实例方法
|
|
436
|
+
|
|
437
|
+
#### get(url, params, data, config)
|
|
438
|
+
|
|
439
|
+
发送 GET 请求。
|
|
440
|
+
|
|
441
|
+
**示例:**
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
445
|
+
|
|
446
|
+
const api = new BaseApi({
|
|
447
|
+
baseURL: 'https://api.example.com',
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
async function getUserList() {
|
|
451
|
+
const data = await api.get('/users', { page: 1, size: 10 })
|
|
452
|
+
return data
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
#### post(url, data, params, config)
|
|
457
|
+
|
|
458
|
+
发送 POST 请求。
|
|
459
|
+
|
|
460
|
+
**示例:**
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
464
|
+
|
|
465
|
+
const api = new BaseApi({
|
|
466
|
+
baseURL: 'https://api.example.com',
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
async function createUser(userData: any) {
|
|
470
|
+
const data = await api.post('/users', userData)
|
|
471
|
+
return data
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### put(url, data, params, config)
|
|
476
|
+
|
|
477
|
+
发送 PUT 请求。
|
|
478
|
+
|
|
479
|
+
**示例:**
|
|
480
|
+
|
|
481
|
+
```ts
|
|
482
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
483
|
+
|
|
484
|
+
const api = new BaseApi({
|
|
485
|
+
baseURL: 'https://api.example.com',
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
async function updateUser(id: string, userData: any) {
|
|
489
|
+
const data = await api.put(`/users/${id}`, userData)
|
|
490
|
+
return data
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
#### delete(url, params, data, config)
|
|
495
|
+
|
|
496
|
+
发送 DELETE 请求。
|
|
497
|
+
|
|
498
|
+
**示例:**
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
502
|
+
|
|
503
|
+
const api = new BaseApi({
|
|
504
|
+
baseURL: 'https://api.example.com',
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
async function deleteUser(id: string) {
|
|
508
|
+
const data = await api.delete(`/users/${id}`)
|
|
509
|
+
return data
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
#### upload(url, formData, config)
|
|
514
|
+
|
|
515
|
+
上传文件,自动携带 `multipart/form-data` 头信息。
|
|
516
|
+
|
|
517
|
+
**示例:**
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
521
|
+
|
|
522
|
+
const api = new BaseApi({
|
|
523
|
+
baseURL: 'https://api.example.com',
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
async function uploadFile(file: File) {
|
|
527
|
+
const formData = new FormData()
|
|
528
|
+
formData.append('file', file)
|
|
529
|
+
const data = await api.upload('/upload', formData, {
|
|
530
|
+
onUploadProgress: (event) => {
|
|
531
|
+
const percent = event.total ? Math.round(event.loaded / event.total * 100) : 0
|
|
532
|
+
console.log(`上传进度:${percent}%`)
|
|
533
|
+
},
|
|
534
|
+
})
|
|
535
|
+
return data
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
#### all(requests)
|
|
540
|
+
|
|
541
|
+
批量请求。`requests` 既可以是 `AxiosRequestConfig[]`,也可以是已经发起的请求 `Promise[]`。
|
|
542
|
+
|
|
543
|
+
**示例:**
|
|
544
|
+
|
|
545
|
+
```ts
|
|
546
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
547
|
+
import type { AxiosRequestConfig } from 'axios'
|
|
548
|
+
|
|
549
|
+
const api = new BaseApi({
|
|
550
|
+
baseURL: 'https://api.example.com',
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
async function loadDashboardData() {
|
|
554
|
+
const requests: AxiosRequestConfig[] = [
|
|
555
|
+
{ url: '/users', method: 'get' },
|
|
556
|
+
{ url: '/posts', method: 'get' },
|
|
557
|
+
{ url: '/comments', method: 'get' },
|
|
558
|
+
]
|
|
559
|
+
const [users, posts, comments] = await api.all(requests)
|
|
560
|
+
return { users, posts, comments }
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// 也可以传入已经发起的请求
|
|
564
|
+
async function loadDashboardDataByPromises() {
|
|
565
|
+
const api = new BaseApi({ baseURL: 'https://api.example.com' })
|
|
566
|
+
const [users, posts, comments] = await api.all([
|
|
567
|
+
api.get('/users'),
|
|
568
|
+
api.get('/posts'),
|
|
569
|
+
api.get('/comments'),
|
|
570
|
+
])
|
|
571
|
+
return { users, posts, comments }
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### 继承扩展 BaseApi
|
|
576
|
+
|
|
577
|
+
可以通过继承 `BaseApi` 创建自定义的 API 类,重写 `processRequestConfig` 和 `processResponseError` 方法。
|
|
578
|
+
|
|
579
|
+
**示例:**
|
|
580
|
+
|
|
581
|
+
```ts
|
|
582
|
+
import { BaseApi } from '@moluoxixi/ajaxpackage'
|
|
583
|
+
import type { InternalAxiosRequestConfig, AxiosError } from 'axios'
|
|
584
|
+
|
|
585
|
+
class UserApi extends BaseApi {
|
|
586
|
+
constructor() {
|
|
587
|
+
super({
|
|
588
|
+
baseURL: 'https://api.example.com/users',
|
|
589
|
+
timeout: 5000,
|
|
590
|
+
})
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
processRequestConfig(config: InternalAxiosRequestConfig) {
|
|
594
|
+
// 自定义请求拦截器处理
|
|
595
|
+
config.headers = config.headers || {}
|
|
596
|
+
config.headers['X-Custom-Header'] = 'custom-value'
|
|
597
|
+
return config
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async processResponseError(error: AxiosError): Promise<AxiosError> {
|
|
601
|
+
// 自定义错误处理
|
|
602
|
+
if (error.response?.status === 404) {
|
|
603
|
+
console.error('资源未找到')
|
|
604
|
+
}
|
|
605
|
+
return error
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async getUserById(id: string) {
|
|
609
|
+
return this.get(`/${id}`)
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
async createUser(userData: any) {
|
|
613
|
+
return this.post('/', userData)
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const userApi = new UserApi()
|
|
618
|
+
export default userApi
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
## VueAxiosPlugin 插件
|
|
622
|
+
|
|
623
|
+
### 安装插件
|
|
624
|
+
|
|
625
|
+
```ts
|
|
626
|
+
import { createApp } from 'vue'
|
|
627
|
+
import VueAxiosPlugin from '@moluoxixi/ajaxpackage'
|
|
628
|
+
|
|
629
|
+
const app = createApp(App)
|
|
630
|
+
|
|
631
|
+
app.use(VueAxiosPlugin, {
|
|
632
|
+
default: {
|
|
633
|
+
baseURL: 'https://api.example.com',
|
|
634
|
+
timeout: 5000,
|
|
635
|
+
getToken: () => localStorage.getItem('token'),
|
|
636
|
+
},
|
|
637
|
+
})
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### 插件配置选项
|
|
641
|
+
|
|
642
|
+
| 选项 | 说明 | 类型 | 必填 |
|
|
643
|
+
| --- | --- | --- | --- |
|
|
644
|
+
| `default` | 默认实例配置 | `HttpServiceOptions` | 否 |
|
|
645
|
+
| `instances` | 其他实例配置 | ^[Object]`Record<string, HttpServiceOptions>` | 否 |
|
|
646
|
+
| `globalMixin` | 是否启用全局 mixin | `boolean` | 否,默认 `true` |
|
|
647
|
+
|
|
648
|
+
### 使用示例:单实例
|
|
649
|
+
|
|
650
|
+
```ts
|
|
651
|
+
import { createApp } from 'vue'
|
|
652
|
+
import VueAxiosPlugin from '@moluoxixi/ajaxpackage'
|
|
653
|
+
|
|
654
|
+
const app = createApp(App)
|
|
655
|
+
|
|
656
|
+
app.use(VueAxiosPlugin, {
|
|
657
|
+
default: {
|
|
658
|
+
baseURL: 'https://api.example.com',
|
|
659
|
+
timeout: 5000,
|
|
660
|
+
getToken: () => localStorage.getItem('token'),
|
|
661
|
+
},
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
// 在组件中使用
|
|
665
|
+
export default {
|
|
666
|
+
async mounted() {
|
|
667
|
+
const users = await this.$http.get('/users')
|
|
668
|
+
console.log(users)
|
|
669
|
+
},
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### 使用示例:多实例
|
|
674
|
+
|
|
675
|
+
```ts
|
|
676
|
+
import { createApp } from 'vue'
|
|
677
|
+
import VueAxiosPlugin from '@moluoxixi/ajaxpackage'
|
|
678
|
+
|
|
679
|
+
const app = createApp(App)
|
|
680
|
+
|
|
681
|
+
app.use(VueAxiosPlugin, {
|
|
682
|
+
default: {
|
|
683
|
+
baseURL: 'https://api.example.com',
|
|
684
|
+
},
|
|
685
|
+
instances: {
|
|
686
|
+
admin: {
|
|
687
|
+
baseURL: 'https://admin-api.example.com',
|
|
688
|
+
getToken: () => localStorage.getItem('adminToken'),
|
|
689
|
+
},
|
|
690
|
+
public: {
|
|
691
|
+
baseURL: 'https://public-api.example.com',
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
// 在组件中使用
|
|
697
|
+
export default {
|
|
698
|
+
async mounted() {
|
|
699
|
+
// 使用默认实例
|
|
700
|
+
const users = await this.$http.get('/users')
|
|
701
|
+
|
|
702
|
+
// 使用 admin 实例
|
|
703
|
+
const adminData = await this.$httpAdmin.get('/admin/users')
|
|
704
|
+
|
|
705
|
+
// 使用 public 实例
|
|
706
|
+
const publicData = await this.$httpPublic.get('/public/news')
|
|
707
|
+
},
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### 使用示例:Composition API
|
|
712
|
+
|
|
713
|
+
```ts
|
|
714
|
+
import { inject } from 'vue'
|
|
715
|
+
|
|
716
|
+
export default {
|
|
717
|
+
setup() {
|
|
718
|
+
const $http = inject('$http')
|
|
719
|
+
|
|
720
|
+
async function loadData() {
|
|
721
|
+
const data = await $http.get('/users')
|
|
722
|
+
return data
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return {
|
|
726
|
+
loadData,
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
}
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
## 错误处理
|
|
733
|
+
|
|
734
|
+
### 自动错误处理
|
|
735
|
+
|
|
736
|
+
AjaxPackage 会自动处理以下错误:
|
|
737
|
+
|
|
738
|
+
1. **401 错误(登录失效)**
|
|
739
|
+
- 自动调用 `onLoginRequired` 回调
|
|
740
|
+
- 显示错误提示
|
|
741
|
+
|
|
742
|
+
2. **超时错误**
|
|
743
|
+
- 显示超时提示信息
|
|
744
|
+
|
|
745
|
+
3. **网络错误**
|
|
746
|
+
- 显示网络错误提示
|
|
747
|
+
|
|
748
|
+
4. **响应错误码**
|
|
749
|
+
- 根据 `responseFields.code` 判断错误
|
|
750
|
+
- 显示错误消息
|
|
751
|
+
|
|
752
|
+
5. **错误数组(errors)**
|
|
753
|
+
- 如果响应中包含错误数组,会以通知形式显示所有错误
|
|
754
|
+
|
|
755
|
+
6. **提示信息(tips)**
|
|
756
|
+
- 如果响应中包含提示信息,会以警告通知形式显示
|
|
757
|
+
|
|
758
|
+
### 自定义错误处理
|
|
759
|
+
|
|
760
|
+
如果需要对响应结构进行更细粒度的处理,可以继承 `BaseApi` 并重写 `processResponseConfig` 或 `processResponseError`。在这些方法中根据业务规则解析数据、抛出错误或执行额外的副作用。
|
|
761
|
+
|
|
762
|
+
## SSR 支持
|
|
763
|
+
|
|
764
|
+
AjaxPackage 支持 SSR(服务端渲染)环境。当 `document` 不存在时,会自动使用 `console` 输出消息和通知,而不是使用 Element Plus 的组件。
|
|
765
|
+
|
|
766
|
+
## 注意事项
|
|
767
|
+
|
|
768
|
+
1. **响应字段映射**
|
|
769
|
+
- 支持路径解析,如 `'result.code'` 可以访问嵌套字段
|
|
770
|
+
- 如果路径解析失败,会尝试使用默认字段名
|
|
771
|
+
|
|
772
|
+
2. **Token 处理**
|
|
773
|
+
- Token 会自动添加到请求头的 `Token` 字段
|
|
774
|
+
- 如果 `getToken` 返回 `null` 或空字符串,不会添加 Token
|
|
775
|
+
|
|
776
|
+
3. **错误处理**
|
|
777
|
+
- 401 错误会自动触发登录失效回调
|
|
778
|
+
- 其他错误会显示错误提示,但不会自动处理
|
|
779
|
+
|
|
780
|
+
4. **批量请求**
|
|
781
|
+
- `all` 方法使用 `Promise.all`,如果任何一个请求失败,整个批量请求会失败
|
|
782
|
+
- 建议在业务代码中处理错误情况
|
|
783
|
+
|
|
784
|
+
5. **请求取消**
|
|
785
|
+
- 当前版本未提供请求取消封装,若业务需要可直接使用 axios 自带的取消能力自行实现
|
|
786
|
+
|
|
787
|
+
## SystemErrorDialog 组件
|
|
788
|
+
|
|
789
|
+
### 概述
|
|
790
|
+
|
|
791
|
+
`SystemErrorDialog` 是一个用于显示系统异常信息的对话框组件,专门用于展示请求错误时的详细信息,包括用户信息、科室信息、网络信息和错误详情。
|
|
792
|
+
|
|
793
|
+
### 组件特性
|
|
794
|
+
|
|
795
|
+
- 🎯 **轻量级设计**:只接收必要的字符串参数,避免大对象响应式开销
|
|
796
|
+
- 📱 **响应式布局**:支持移动端和桌面端的良好显示效果
|
|
797
|
+
- 🔧 **高度可配置**:支持自定义标题、宽度和各种信息字段
|
|
798
|
+
- 🎨 **美观界面**:采用现代化的UI设计,信息展示清晰易读
|
|
799
|
+
|
|
800
|
+
### Props 参数
|
|
801
|
+
|
|
802
|
+
| 参数 | 说明 | 类型 | 默认值 | 必填 |
|
|
803
|
+
|------|------|------|--------|------|
|
|
804
|
+
| `title` | 对话框标题 | `string` | `'系统异常信息'` | 否 |
|
|
805
|
+
| `width` | 对话框宽度 | `number \| string` | `520` | 否 |
|
|
806
|
+
| `userName` | 用户名 | `string` | - | 否 |
|
|
807
|
+
| `userId` | 用户ID | `string` | - | 否 |
|
|
808
|
+
| `deptName` | 科室名称 | `string` | - | 否 |
|
|
809
|
+
| `deptId` | 科室ID | `string` | - | 否 |
|
|
810
|
+
| `clientIp` | 客户端IP地址 | `string` | - | 否 |
|
|
811
|
+
| `requestUrl` | 请求URL路径 | `string` | - | 否 |
|
|
812
|
+
| `traceId` | 链路追踪ID | `string` | - | 否 |
|
|
813
|
+
| `errorMessage` | 错误消息 | `string` | - | 否 |
|
|
814
|
+
| `errorCode` | 错误代码 | `string` | - | 否 |
|
|
815
|
+
|
|
816
|
+
### Events 事件
|
|
817
|
+
|
|
818
|
+
| 事件名 | 说明 | 参数 |
|
|
819
|
+
|--------|------|------|
|
|
820
|
+
| `close` | 关闭对话框时触发 | - |
|
|
821
|
+
| `confirm` | 点击确认按钮时触发 | - |
|
|
822
|
+
|
|
823
|
+
### 使用示例
|
|
824
|
+
|
|
825
|
+
#### 基础用法
|
|
826
|
+
|
|
827
|
+
```vue
|
|
828
|
+
<template>
|
|
829
|
+
<div>
|
|
830
|
+
<ElButton @click="showErrorDialog">显示异常信息</ElButton>
|
|
831
|
+
|
|
832
|
+
<SystemErrorDialog
|
|
833
|
+
v-model="dialogVisible"
|
|
834
|
+
:user-name="errorInfo.userName"
|
|
835
|
+
:user-id="errorInfo.userId"
|
|
836
|
+
:dept-name="errorInfo.deptName"
|
|
837
|
+
:dept-id="errorInfo.deptId"
|
|
838
|
+
:client-ip="errorInfo.clientIp"
|
|
839
|
+
:request-url="errorInfo.requestUrl"
|
|
840
|
+
:trace-id="errorInfo.traceId"
|
|
841
|
+
@close="dialogVisible = false"
|
|
842
|
+
@confirm="handleConfirm"
|
|
843
|
+
/>
|
|
844
|
+
</div>
|
|
845
|
+
</template>
|
|
846
|
+
|
|
847
|
+
<script setup>
|
|
848
|
+
import { ref } from 'vue'
|
|
849
|
+
import { ElButton } from 'element-plus'
|
|
850
|
+
import SystemErrorDialog from '@moluoxixi/ajaxpackage/SystemErrorDialog.vue'
|
|
851
|
+
|
|
852
|
+
const dialogVisible = ref(false)
|
|
853
|
+
|
|
854
|
+
const errorInfo = {
|
|
855
|
+
userName: '张三',
|
|
856
|
+
userId: '12345',
|
|
857
|
+
deptName: '信息科',
|
|
858
|
+
deptId: 'IT001',
|
|
859
|
+
clientIp: '192.168.1.100',
|
|
860
|
+
requestUrl: '/api/user/login',
|
|
861
|
+
traceId: 'trace-123456789',
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function showErrorDialog() {
|
|
865
|
+
dialogVisible.value = true
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function handleConfirm() {
|
|
869
|
+
console.log('用户确认了异常信息')
|
|
870
|
+
dialogVisible.value = false
|
|
871
|
+
}
|
|
872
|
+
</script>
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
#### 完整配置
|
|
876
|
+
|
|
877
|
+
```vue
|
|
878
|
+
<template>
|
|
879
|
+
<SystemErrorDialog
|
|
880
|
+
v-model="dialogVisible"
|
|
881
|
+
title="详细系统异常信息"
|
|
882
|
+
:width="600"
|
|
883
|
+
:user-name="fullErrorInfo.userName"
|
|
884
|
+
:user-id="fullErrorInfo.userId"
|
|
885
|
+
:dept-name="fullErrorInfo.deptName"
|
|
886
|
+
:dept-id="fullErrorInfo.deptId"
|
|
887
|
+
:client-ip="fullErrorInfo.clientIp"
|
|
888
|
+
:request-url="fullErrorInfo.requestUrl"
|
|
889
|
+
:trace-id="fullErrorInfo.traceId"
|
|
890
|
+
:error-code="fullErrorInfo.errorCode"
|
|
891
|
+
:error-message="fullErrorInfo.errorMessage"
|
|
892
|
+
@close="dialogVisible = false"
|
|
893
|
+
@confirm="handleConfirm"
|
|
894
|
+
/>
|
|
895
|
+
</template>
|
|
896
|
+
|
|
897
|
+
<script setup>
|
|
898
|
+
import { ref } from 'vue'
|
|
899
|
+
import SystemErrorDialog from '@moluoxixi/ajaxpackage/SystemErrorDialog.vue'
|
|
900
|
+
|
|
901
|
+
const dialogVisible = ref(false)
|
|
902
|
+
|
|
903
|
+
const fullErrorInfo = {
|
|
904
|
+
userName: '李四',
|
|
905
|
+
userId: '67890',
|
|
906
|
+
deptName: '财务科',
|
|
907
|
+
deptId: 'FIN001',
|
|
908
|
+
clientIp: '192.168.1.200',
|
|
909
|
+
requestUrl: '/api/finance/report',
|
|
910
|
+
traceId: 'trace-987654321',
|
|
911
|
+
errorCode: 'ERR_500',
|
|
912
|
+
errorMessage: '服务器内部错误,请联系系统管理员',
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function handleConfirm() {
|
|
916
|
+
// 处理确认逻辑
|
|
917
|
+
dialogVisible.value = false
|
|
918
|
+
}
|
|
919
|
+
</script>
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
#### 从响应对象提取信息
|
|
923
|
+
|
|
924
|
+
```typescript
|
|
925
|
+
import type { AxiosResponse } from 'axios'
|
|
926
|
+
import SystemErrorDialog from '@moluoxixi/ajaxpackage/SystemErrorDialog.vue'
|
|
927
|
+
|
|
928
|
+
// 工具函数:从响应对象提取异常信息
|
|
929
|
+
function extractErrorInfo(response: AxiosResponse) {
|
|
930
|
+
const requestData = {
|
|
931
|
+
...response.config?.params,
|
|
932
|
+
...normalizePayload(response.config?.data),
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
return {
|
|
936
|
+
userName: requestData.userName || requestData.username,
|
|
937
|
+
userId: requestData.userId || requestData.userid,
|
|
938
|
+
deptName: requestData.deptName || requestData.departmentName,
|
|
939
|
+
deptId: requestData.deptId || requestData.departmentId,
|
|
940
|
+
clientIp: requestData.clientIp || requestData.ip,
|
|
941
|
+
requestUrl: response.config?.url,
|
|
942
|
+
traceId: resolveTraceId(response.headers),
|
|
943
|
+
errorCode: response.data?.code || response.status?.toString(),
|
|
944
|
+
errorMessage: response.data?.message || response.statusText,
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// 在错误处理中使用
|
|
949
|
+
function handleApiError(error: any) {
|
|
950
|
+
if (error.response) {
|
|
951
|
+
const errorInfo = extractErrorInfo(error.response)
|
|
952
|
+
// 显示异常对话框
|
|
953
|
+
showSystemErrorDialog(errorInfo)
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
### 组件方法
|
|
959
|
+
|
|
960
|
+
通过 `ref` 可以访问组件的方法:
|
|
961
|
+
|
|
962
|
+
```vue
|
|
963
|
+
<template>
|
|
964
|
+
<SystemErrorDialog
|
|
965
|
+
ref="errorDialogRef"
|
|
966
|
+
v-model="dialogVisible"
|
|
967
|
+
:user-name="errorInfo.userName"
|
|
968
|
+
@close="dialogVisible = false"
|
|
969
|
+
/>
|
|
970
|
+
</template>
|
|
971
|
+
|
|
972
|
+
<script setup>
|
|
973
|
+
import { ref } from 'vue'
|
|
974
|
+
import SystemErrorDialog from '@moluoxixi/ajaxpackage/SystemErrorDialog.vue'
|
|
975
|
+
|
|
976
|
+
const errorDialogRef = ref()
|
|
977
|
+
|
|
978
|
+
// 通过方法关闭对话框
|
|
979
|
+
function closeDialog() {
|
|
980
|
+
errorDialogRef.value?.close()
|
|
981
|
+
}
|
|
982
|
+
</script>
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
### 样式定制
|
|
986
|
+
|
|
987
|
+
组件使用 scoped 样式,如需自定义样式,可以通过深度选择器:
|
|
988
|
+
|
|
989
|
+
```vue
|
|
990
|
+
<style>
|
|
991
|
+
.custom-error-dialog :deep(.error-info-container) {
|
|
992
|
+
background-color: #f5f5f5;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.custom-error-dialog :deep(.error-code) {
|
|
996
|
+
color: #ff4757;
|
|
997
|
+
font-weight: bold;
|
|
998
|
+
}
|
|
999
|
+
</style>
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
### 设计理念
|
|
1003
|
+
|
|
1004
|
+
#### 为什么不直接传递 response 对象?
|
|
1005
|
+
|
|
1006
|
+
1. **性能考虑**:response 对象通常很大,包含大量不必要的信息,将其变为响应式会造成性能开销
|
|
1007
|
+
2. **数据安全**:避免在组件中暴露完整的请求/响应信息
|
|
1008
|
+
3. **接口清晰**:明确的 props 定义让组件的使用更加清晰和可控
|
|
1009
|
+
4. **灵活性**:调用方可以自由决定传递哪些信息,支持多种数据来源
|
|
1010
|
+
|
|
1011
|
+
#### 信息展示逻辑
|
|
1012
|
+
|
|
1013
|
+
- **菜单名称**:自动获取当前页面的 URL(`location.href`)
|
|
1014
|
+
- **未知值处理**:对于未传递的信息显示"未知"
|
|
1015
|
+
- **错误信息**:错误代码和错误消息会以不同颜色突出显示
|
|
1016
|
+
- **响应式设计**:在移动端会自动调整布局为垂直排列
|