@robot-admin/request-core 0.1.3 → 0.4.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/CHANGELOG.md +102 -0
- package/LICENSE +21 -0
- package/README.md +346 -412
- package/SECURITY.md +14 -0
- package/dist/axios.cjs +141 -0
- package/dist/axios.cjs.map +1 -0
- package/dist/axios.d.cts +67 -0
- package/dist/axios.d.ts +67 -0
- package/dist/axios.js +4 -0
- package/dist/axios.js.map +1 -0
- package/dist/chunk-2JXH7SIL.js +711 -0
- package/dist/chunk-2JXH7SIL.js.map +1 -0
- package/dist/chunk-56QOEWUS.js +8 -0
- package/dist/chunk-56QOEWUS.js.map +1 -0
- package/dist/chunk-DUP3Y4DN.js +320 -0
- package/dist/chunk-DUP3Y4DN.js.map +1 -0
- package/dist/chunk-JGFRUAZT.cjs +1007 -0
- package/dist/chunk-JGFRUAZT.cjs.map +1 -0
- package/dist/chunk-KHI6XQRN.cjs +717 -0
- package/dist/chunk-KHI6XQRN.cjs.map +1 -0
- package/dist/chunk-OAZ5BUBU.js +969 -0
- package/dist/chunk-OAZ5BUBU.js.map +1 -0
- package/dist/chunk-QSDNDTOZ.js +28 -0
- package/dist/chunk-QSDNDTOZ.js.map +1 -0
- package/dist/chunk-UHXBAFY6.cjs +323 -0
- package/dist/chunk-UHXBAFY6.cjs.map +1 -0
- package/dist/chunk-WUZ43MLM.cjs +30 -0
- package/dist/chunk-WUZ43MLM.cjs.map +1 -0
- package/dist/chunk-WXHQXPS4.cjs +10 -0
- package/dist/chunk-WXHQXPS4.cjs.map +1 -0
- package/dist/crud.cjs +19 -0
- package/dist/crud.cjs.map +1 -0
- package/dist/crud.d.cts +10 -0
- package/dist/crud.d.ts +10 -0
- package/dist/crud.js +6 -0
- package/dist/crud.js.map +1 -0
- package/dist/index.cjs +136 -1122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -644
- package/dist/index.d.ts +7 -644
- package/dist/index.js +5 -1105
- package/dist/index.js.map +1 -1
- package/dist/naive.cjs +14 -0
- package/dist/naive.cjs.map +1 -0
- package/dist/naive.d.cts +9 -0
- package/dist/naive.d.ts +9 -0
- package/dist/naive.js +5 -0
- package/dist/naive.js.map +1 -0
- package/dist/types-BRDRqYFs.d.cts +197 -0
- package/dist/types-BVJhlSuh.d.cts +310 -0
- package/dist/types-BVJhlSuh.d.ts +310 -0
- package/dist/types-CUyibJZX.d.ts +197 -0
- package/dist/vue.cjs +93 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +38 -0
- package/dist/vue.d.ts +38 -0
- package/dist/vue.js +71 -0
- package/dist/vue.js.map +1 -0
- package/package.json +77 -14
package/README.md
CHANGED
|
@@ -1,412 +1,346 @@
|
|
|
1
|
-
# @robot-admin/request-core
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
getData('/api/config', { cache: { enabled: true, ttl: 600000 } })
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
4. **重要接口启用重试**
|
|
351
|
-
```ts
|
|
352
|
-
postData('/api/payment', data, { retry: { enabled: true, count: 3 } })
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
### ❌ 避免的做法
|
|
356
|
-
|
|
357
|
-
1. ❌ 不要在每个组件中重复配置 axios
|
|
358
|
-
2. ❌ 不要禁用去重插件(除非有特殊需求)
|
|
359
|
-
3. ❌ 不要在 useTableCrud 外部调用其内部方法
|
|
360
|
-
|
|
361
|
-
---
|
|
362
|
-
|
|
363
|
-
## 📖 完整类型定义
|
|
364
|
-
|
|
365
|
-
```ts
|
|
366
|
-
// 核心配置
|
|
367
|
-
export type RequestCoreConfig = {
|
|
368
|
-
request: AxiosRequestConfig // Axios 基础配置
|
|
369
|
-
successCodes?: (string | number)[] // 成功状态码
|
|
370
|
-
fieldAliases?: FieldAliases // 字段映射
|
|
371
|
-
interceptors?: InterceptorConfig // 拦截器
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// CRUD 配置
|
|
375
|
-
export type UseTableCrudConfig<T> = {
|
|
376
|
-
api: ApiEndpoints // API 端点
|
|
377
|
-
columns: TableColumn[] // 表格列
|
|
378
|
-
customActions?: CustomAction[] // 自定义操作
|
|
379
|
-
idKey?: string // ID 字段名
|
|
380
|
-
defaultPageSize?: number // 默认分页大小
|
|
381
|
-
autoLoad?: boolean // 是否自动加载
|
|
382
|
-
extractListData?: (res: any) => { items: T[]; total: number }
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// 插件配置
|
|
386
|
-
export type EnhancedAxiosRequestConfig = AxiosRequestConfig & {
|
|
387
|
-
cache?: CacheConfig // 缓存配置
|
|
388
|
-
retry?: RetryConfig // 重试配置
|
|
389
|
-
dedupe?: DedupeConfig // 去重配置
|
|
390
|
-
cancel?: CancelConfig // 取消配置
|
|
391
|
-
}
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
查看完整类型定义:[src/index.ts](./src/index.ts)
|
|
395
|
-
|
|
396
|
-
---
|
|
397
|
-
|
|
398
|
-
## 🛠️ 开发
|
|
399
|
-
|
|
400
|
-
```bash
|
|
401
|
-
bun install # 安装依赖
|
|
402
|
-
bun run dev # 开发模式(watch)
|
|
403
|
-
bun run build # 构建
|
|
404
|
-
bun run type-check # 类型检查
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
---
|
|
408
|
-
|
|
409
|
-
## 📄 License
|
|
410
|
-
|
|
411
|
-
MIT © [ChenYu](https://github.com/ChenyCHENYU)
|
|
412
|
-
|
|
1
|
+
# @robot-admin/request-core
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@robot-admin/request-core)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
面向生产环境的实例化请求编排与 Vue 3 Headless CRUD 工具。当前版本:
|
|
7
|
+
`0.4.0`。
|
|
8
|
+
|
|
9
|
+
它保留 Axios 的完整能力,只收拢应用中最容易重复出错的部分:并发请求、缓存、
|
|
10
|
+
取消、重试、Token 刷新、错误标准化以及列表 CRUD 生命周期。
|
|
11
|
+
|
|
12
|
+
## 特性
|
|
13
|
+
|
|
14
|
+
- 每个 Client 独立持有缓存、待处理请求、取消作用域和认证状态,无跨应用污染。
|
|
15
|
+
- 简单调用保持一行,复杂调用通过平铺的请求配置按需启用。
|
|
16
|
+
- 相同请求支持 `join`、`takeLatest`、`takeFirst`、`allow` 四种并发语义。
|
|
17
|
+
- 内存 LRU 缓存支持 TTL、标签/前缀失效、自定义同步 CacheStore 和引用保护。
|
|
18
|
+
- 重试支持幂等方法白名单、指数退避、抖动、`Retry-After` 和总时间预算。
|
|
19
|
+
- Token 主动刷新、并发 401 恢复和重新登录均使用 single-flight。
|
|
20
|
+
- `RequestError` 统一业务、HTTP、网络、超时、取消和配置错误。
|
|
21
|
+
- Vue 层不依赖具体 UI;Naive UI 仅作为可选兼容适配层。
|
|
22
|
+
- ESM、CJS 和 TypeScript 类型入口均经过发布前验证。
|
|
23
|
+
|
|
24
|
+
## 安装与入口
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bun add @robot-admin/request-core axios
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| 入口 | 依赖边界 | 用途 |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| `@robot-admin/request-core/axios` | Axios | 推荐的请求 Client、策略及兼容 API |
|
|
33
|
+
| `@robot-admin/request-core/vue` | Axios + Vue | `useRequest`、Headless `useTableCrud`、Client 注入 |
|
|
34
|
+
| `@robot-admin/request-core/naive` | Axios + Vue + Naive UI | `useNaiveTableCrud` 兼容适配 |
|
|
35
|
+
| `@robot-admin/request-core` | 完整兼容入口 | 旧项目平滑迁移,当前仍会引用 Naive UI |
|
|
36
|
+
| `@robot-admin/request-core/crud` | 兼容入口 | 已废弃,迁移到 `/vue` 或 `/naive` |
|
|
37
|
+
|
|
38
|
+
纯请求项目应使用 `/axios`,这样不会引入 Vue 或任何 UI 框架。
|
|
39
|
+
|
|
40
|
+
## 推荐接入
|
|
41
|
+
|
|
42
|
+
### 创建唯一的应用 Client
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// src/services/request.ts
|
|
46
|
+
import {
|
|
47
|
+
createRequestClient,
|
|
48
|
+
type ResponseAdapter,
|
|
49
|
+
} from '@robot-admin/request-core/axios'
|
|
50
|
+
|
|
51
|
+
const responseAdapter: ResponseAdapter = {
|
|
52
|
+
isSuccess: data => [0, 200].includes((data as { code: number }).code),
|
|
53
|
+
getData: data => (data as { data: unknown }).data,
|
|
54
|
+
getError: data => ({
|
|
55
|
+
code: (data as { code?: number }).code,
|
|
56
|
+
message: (data as { message?: string }).message ?? '请求失败',
|
|
57
|
+
data,
|
|
58
|
+
}),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const request = createRequestClient({
|
|
62
|
+
request: {
|
|
63
|
+
baseURL: import.meta.env.VITE_API_BASE,
|
|
64
|
+
timeout: 10_000,
|
|
65
|
+
},
|
|
66
|
+
response: responseAdapter,
|
|
67
|
+
defaults: {
|
|
68
|
+
concurrency: 'join',
|
|
69
|
+
retry: { enabled: false },
|
|
70
|
+
cache: { enabled: false },
|
|
71
|
+
},
|
|
72
|
+
hooks: {
|
|
73
|
+
onError: error => {
|
|
74
|
+
if (error.kind !== 'canceled') window.$message?.error(error.message)
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
所有能力均可省略。默认缓存和重试关闭;新 Client 的相同安全读取请求默认共享结果,
|
|
81
|
+
副作用方法默认允许并发。
|
|
82
|
+
|
|
83
|
+
### 类型化调用
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
interface User {
|
|
87
|
+
id: number
|
|
88
|
+
name: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface CreateUser {
|
|
92
|
+
name: string
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const users = await request.get<User[]>('/users', {
|
|
96
|
+
params: { keyword: 'robot' },
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const user = await request.post<User, CreateUser>('/users', {
|
|
100
|
+
name: 'Robot',
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const response = await request.raw<User>({
|
|
104
|
+
method: 'GET',
|
|
105
|
+
url: '/users/1',
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
支持 `request/get/post/put/patch/delete/head/options/raw`。`raw()` 返回完整
|
|
110
|
+
`AxiosResponse`,其他方法返回响应适配器处理后的数据。
|
|
111
|
+
|
|
112
|
+
## 全局配置与按需能力
|
|
113
|
+
|
|
114
|
+
策略可以在 Client 层配置默认值,也可以被单次请求覆盖:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
await request.get('/dashboard', {
|
|
118
|
+
cache: { enabled: true, ttl: 60_000, tags: ['dashboard'] },
|
|
119
|
+
retry: { enabled: true, count: 2, maxElapsedMs: 8_000 },
|
|
120
|
+
concurrency: 'takeLatest',
|
|
121
|
+
scope: 'dashboard-page',
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
request.cache.invalidateTag('dashboard')
|
|
125
|
+
request.requests.cancelScope('dashboard-page')
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
并发策略:
|
|
129
|
+
|
|
130
|
+
- `join`:相同请求共享一次网络调用,适合字典和初始化数据。
|
|
131
|
+
- `takeLatest`:取消旧请求,只保留最新请求,适合搜索和分页。
|
|
132
|
+
- `takeFirst`:已有相同请求时拒绝新请求,适合提交按钮。
|
|
133
|
+
- `allow`:允许全部并发,适合调用方自行管理的任务。
|
|
134
|
+
|
|
135
|
+
旧 `dedupe` 配置继续可用。隐式去重只作用于 GET、HEAD、OPTIONS;POST 等
|
|
136
|
+
副作用请求不会被默认取消。
|
|
137
|
+
|
|
138
|
+
### 缓存
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const client = createRequestClient({
|
|
142
|
+
cache: { maxSize: 500, clone: true },
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
await client.get('/users', {
|
|
146
|
+
cache: {
|
|
147
|
+
enabled: true,
|
|
148
|
+
ttl: 5 * 60_000,
|
|
149
|
+
tags: ['users'],
|
|
150
|
+
varyHeaders: ['accept-language'],
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
client.cache.clear()
|
|
155
|
+
client.cache.invalidateTag('users')
|
|
156
|
+
client.cache.invalidatePrefix('GET|')
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
缓存按 Client 隔离,键包含 `baseURL`、URL、方法、参数、请求体、响应类型和身份
|
|
160
|
+
相关请求头摘要。切换用户或租户时仍建议显式 `client.cache.clear()`。
|
|
161
|
+
|
|
162
|
+
### 重试
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
await request.get('/reports', {
|
|
166
|
+
retry: {
|
|
167
|
+
enabled: true,
|
|
168
|
+
count: 3,
|
|
169
|
+
delay: 500,
|
|
170
|
+
maxDelay: 10_000,
|
|
171
|
+
maxElapsedMs: 20_000,
|
|
172
|
+
respectRetryAfter: true,
|
|
173
|
+
onRetry: ({ attempt, delay }) => reportRetry(attempt, delay),
|
|
174
|
+
},
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
默认只允许 GET、HEAD、OPTIONS、PUT、DELETE 重试。POST 不会自动重试;流式或
|
|
179
|
+
不可重放的 body 也会被保护性跳过。
|
|
180
|
+
|
|
181
|
+
## 认证恢复
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const request = createRequestClient({
|
|
185
|
+
request: { baseURL: '/api' },
|
|
186
|
+
auth: {
|
|
187
|
+
getToken: () => userStore.token,
|
|
188
|
+
shouldRefresh: () => userStore.isTokenExpiringSoon(),
|
|
189
|
+
refresh: async ({ raw, signal }) => {
|
|
190
|
+
const response = await raw<{ data: { token: string } }>({
|
|
191
|
+
method: 'POST',
|
|
192
|
+
url: '/auth/refresh-token',
|
|
193
|
+
data: { refreshToken: userStore.refreshToken },
|
|
194
|
+
signal,
|
|
195
|
+
})
|
|
196
|
+
const token = response.data.data.token
|
|
197
|
+
userStore.setToken(token)
|
|
198
|
+
return token
|
|
199
|
+
},
|
|
200
|
+
reauthenticate: async () => {
|
|
201
|
+
await reLoginDialog.open()
|
|
202
|
+
return userStore.token
|
|
203
|
+
},
|
|
204
|
+
isAuthRequest: config => config.url?.startsWith('/auth/') === true,
|
|
205
|
+
},
|
|
206
|
+
})
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
并发请求共享同一次刷新或重新登录。401 请求最多自动重放一次;`raw` 会自动
|
|
210
|
+
设置 `skipAuth` 并关闭重试、缓存去重和取消跟踪,防止刷新接口递归。
|
|
211
|
+
|
|
212
|
+
单次请求也可显式使用 `skipAuth: true`。
|
|
213
|
+
|
|
214
|
+
## Vue 接入
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
// main.ts
|
|
218
|
+
import { createRequestPlugin } from '@robot-admin/request-core/vue'
|
|
219
|
+
import { request } from '@/services/request'
|
|
220
|
+
|
|
221
|
+
app.use(createRequestPlugin(request))
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
这只通过 Vue InjectionKey 提供 Client,不写入 `window`,也不修改 Vue 全局类型。
|
|
225
|
+
组件仍可通过配置显式传入 Client,适合多后端或多租户应用。
|
|
226
|
+
|
|
227
|
+
### useRequest
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { useRequest } from '@robot-admin/request-core/vue'
|
|
231
|
+
import { request } from '@/services/request'
|
|
232
|
+
|
|
233
|
+
const user = useRequest(
|
|
234
|
+
({ signal }, id: number) => request.get<User>(`/users/${id}`, { signal }),
|
|
235
|
+
{ concurrency: 'takeLatest', keepPreviousData: true },
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
await user.run(1)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
提供 `data/error/loading/run/cancel/reset`,并在 Vue 作用域销毁时自动取消。
|
|
242
|
+
取消或重置后,即使业务执行器没有响应 `AbortSignal`,过期结果也不会回写;
|
|
243
|
+
`onSuccess/onError` 仅用于观察生命周期,其自身异常不会篡改真实请求结果。
|
|
244
|
+
|
|
245
|
+
## Headless useTableCrud
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
import { useTableCrud } from '@robot-admin/request-core/vue'
|
|
249
|
+
import { request } from '@/services/request'
|
|
250
|
+
|
|
251
|
+
const table = useTableCrud<User, UserFilters, UserSort>({
|
|
252
|
+
client: request,
|
|
253
|
+
autoLoad: 'mounted',
|
|
254
|
+
initialFilters: { keyword: '' },
|
|
255
|
+
query: ({ page, pageSize, filters, sort, signal }) =>
|
|
256
|
+
request.get('/users', {
|
|
257
|
+
params: { page, pageSize, ...filters, sort },
|
|
258
|
+
signal,
|
|
259
|
+
concurrency: 'takeLatest',
|
|
260
|
+
}),
|
|
261
|
+
mutations: {
|
|
262
|
+
create: (row, { signal }) => request.post('/users', row, { signal }),
|
|
263
|
+
update: (row, { signal }) =>
|
|
264
|
+
request.put(`/users/${row.id}`, row, { signal }),
|
|
265
|
+
remove: (row, { signal }) =>
|
|
266
|
+
request.delete(`/users/${row.id}`, { signal }),
|
|
267
|
+
},
|
|
268
|
+
createNewRow: () => ({ id: 0, name: '' }),
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
await table.search({ keyword: 'robot' })
|
|
272
|
+
await table.resetSearch()
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
主要返回值:
|
|
276
|
+
|
|
277
|
+
| 状态/方法 | 说明 |
|
|
278
|
+
| --- | --- |
|
|
279
|
+
| `rows`, `total`, `error`, `lastUpdated` | 数据与错误状态 |
|
|
280
|
+
| `loading`, `isInitialLoading`, `isRefreshing` | 查询状态 |
|
|
281
|
+
| `creating`, `updating`, `removing` | 独立变更状态 |
|
|
282
|
+
| `filters`, `sort`, `page`, `pagination` | 查询条件与分页 |
|
|
283
|
+
| `refresh/reload/search/resetSearch/setSort` | 查询操作 |
|
|
284
|
+
| `create/save/remove/batchRemove/getDetail` | CRUD 操作 |
|
|
285
|
+
| `createDraft/cancel/dispose` | 草稿和生命周期 |
|
|
286
|
+
|
|
287
|
+
刷新采用 latest-wins,旧响应不会覆盖新数据;批量删除默认最多并发 4 个请求;
|
|
288
|
+
关闭删除后刷新时会同步维护本地行和总数;组件作用域销毁会终止未完成任务。
|
|
289
|
+
Message 适配器属于呈现观察层,其异常不会改变 CRUD 请求结果。
|
|
290
|
+
|
|
291
|
+
### Naive UI 兼容层
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
import { useNaiveTableCrud } from '@robot-admin/request-core/naive'
|
|
295
|
+
|
|
296
|
+
const table = useNaiveTableCrud({
|
|
297
|
+
client: request,
|
|
298
|
+
query: context => userApi.list(context),
|
|
299
|
+
columns,
|
|
300
|
+
})
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
`useNaiveTableCrud` 只注入 Naive UI 的 Message/Dialog,数据能力与 `/vue` 完全
|
|
304
|
+
共用。Element Plus 项目直接使用 `/vue`,在真实业务需要前无需额外 UI 适配包。
|
|
305
|
+
|
|
306
|
+
## 兼容 API
|
|
307
|
+
|
|
308
|
+
`createRequestCore()`、`getData()`、`postData()`、`putData()`、`patchData()`、
|
|
309
|
+
`deleteData()` 继续可用。需要让这些全局兼容函数指向新 Client 时:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
const request = createRequestClient({ setAsDefault: true })
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
或者调用 `setDefaultRequestClient(request)`。全局兼容入口只保存“默认 Client”引用;
|
|
316
|
+
请求运行状态仍属于具体实例。
|
|
317
|
+
|
|
318
|
+
## 从 0.2.x 升级
|
|
319
|
+
|
|
320
|
+
1. 请求代码改从 `/axios` 导入,新代码优先使用 `createRequestClient()`。
|
|
321
|
+
2. Vue 应用通过 `/vue` 的 `createRequestPlugin()` 注入 Client。
|
|
322
|
+
3. Headless CRUD 从 `/vue` 导入;需要现有 Naive 消息行为时使用
|
|
323
|
+
`/naive` 的 `useNaiveTableCrud()`。
|
|
324
|
+
4. 根入口和 `/crud` 暂时兼容,`/crud` 已标记废弃。
|
|
325
|
+
5. `dedupe: true` 仍表示 take-latest;副作用方法不再隐式启用去重。
|
|
326
|
+
6. 缓存、取消和 reLogin 现在按 Axios 实例隔离;多 Client 场景应通过各自控制器
|
|
327
|
+
清理状态。
|
|
328
|
+
|
|
329
|
+
没有删除 0.2.x 的公开请求方法、配置字段或 Naive CRUD 交互能力。
|
|
330
|
+
|
|
331
|
+
## 开发与发布验证
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
bun run type-check
|
|
335
|
+
bun run test
|
|
336
|
+
bun run build
|
|
337
|
+
bun run check:package
|
|
338
|
+
bun run verify
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
`prepublishOnly` 会执行完整 `verify`,包含类型、测试、构建、publint、ESM/CJS
|
|
342
|
+
入口、跨入口默认实例和依赖边界检查。
|
|
343
|
+
|
|
344
|
+
## License
|
|
345
|
+
|
|
346
|
+
[MIT](./LICENSE)
|