@robot-admin/request-core 0.1.3 → 0.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/README.md CHANGED
@@ -1,412 +1,455 @@
1
- # @robot-admin/request-core
2
-
3
- > 为 Vue 3 打造的企业级请求解决方案:Axios 增强 + 7 大插件 + CRUD Composable
4
-
5
- [![npm version](https://img.shields.io/npm/v/@robot-admin/request-core.svg)](https://www.npmjs.com/package/@robot-admin/request-core)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
-
8
- ---
9
-
10
- ## ✨ 核心特性
11
-
12
- - 🚀 **开箱即用**:3 步接入,5 分钟实现完整 CRUD
13
- - 🔌 **7 大插件**:缓存、重试、去重、取消、reLogin 等开箱即用
14
- - 📊 **useTableCrud**:配置式表格 CRUD,自动处理分页/搜索/增删改查
15
- - 🎯 **智能适配**:自动兼容不同后端响应格式(字段名、成功码)
16
- - 💪 **类型安全**:完整 TypeScript 支持
17
- - 🎨 **Naive UI 集成**:深度集成 Naive UI 组件
18
-
19
- ---
20
-
21
- ## 📦 安装
22
-
23
- ```bash
24
- npm install @robot-admin/request-core
25
- # 或
26
- bun add @robot-admin/request-core
27
- ```
28
-
29
- **Peer Dependencies**: `vue@^3.4.0`, `naive-ui@^2.38.0`
30
-
31
- ---
32
-
33
- ## 🚀 30 秒快速上手
34
-
35
- ### 1️⃣ 初始化(main.ts)
36
-
37
- ```ts
38
- import { createApp } from 'vue'
39
- import { createRequestCore } from '@robot-admin/request-core'
40
-
41
- const app = createApp(App)
42
-
43
- app.use(createRequestCore({
44
- request: { baseURL: '/api', timeout: 10000 },
45
- interceptors: {
46
- request: (config) => {
47
- config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`
48
- return config
49
- }
50
- }
51
- }))
52
- ```
53
-
54
- ### 2️⃣ 使用 CRUD(任意组件)
55
-
56
- ```vue
57
- <script setup lang="ts">
58
- import { useTableCrud } from '@robot-admin/request-core'
59
-
60
- const table = useTableCrud({
61
- api: { list: '/users', get: '/users/:id', create: '/users', update: '/users/:id', remove: '/users/:id' },
62
- columns: [
63
- { key: 'id', title: 'ID' },
64
- { key: 'name', title: '姓名' },
65
- { key: 'email', title: '邮箱' }
66
- ]
67
- })
68
- </script>
69
-
70
- <template>
71
- <n-data-table :data="table.data.value" :columns="table.columns.value" :loading="table.loading.value" />
72
- </template>
73
- ```
74
-
75
- ✅ **完成!** 一个完整的带分页、搜索、编辑、删除的数据表格!
76
-
77
- ---
78
-
79
- ## 📚 核心 API
80
-
81
- ### 请求方法
82
-
83
- | 方法 | 说明 | 示例 |
84
- |------|------|------|
85
- | `getData(url, config?)` | GET 请求 | `getData('/users')` |
86
- | `postData(url, data, config?)` | POST 请求 | `postData('/users', { name: '张三' })` |
87
- | `putData(url, data, config?)` | PUT 请求 | `putData('/users/1', { name: '李四' })` |
88
- | `deleteData(url, config?)` | DELETE 请求 | `deleteData('/users/1')` |
89
-
90
- ### useTableCrud 配置
91
-
92
- | 属性 | 类型 | 必填 | 说明 |
93
- |------|------|------|------|
94
- | `api.list` | `string` | | 列表接口 |
95
- | `api.get` | `string` | - | 详情接口(支持 `:id`) |
96
- | `api.create` | `string` | - | 创建接口 |
97
- | `api.update` | `string` | - | 更新接口(支持 `:id`) |
98
- | `api.remove` | `string` | - | 删除接口(支持 `:id`) |
99
- | `columns` | `TableColumn[]` | | 表格列配置 |
100
- | `customActions` | `CustomAction[]` | - | 自定义操作按钮 |
101
- | `idKey` | `string` | - | ID 字段名(默认 `'id'`) |
102
- | `defaultPageSize` | `number` | - | 每页条数(默认 `10`) |
103
- | `autoLoad` | `boolean` | - | 自动加载(默认 `true`) |
104
-
105
- ### useTableCrud 返回值
106
-
107
- | 属性/方法 | 类型 | 说明 |
108
- |-----------|------|------|
109
- | `data` | `Ref<T[]>` | 表格数据 |
110
- | `loading` | `Ref<boolean>` | 加载状态 |
111
- | `total` | `Ref<number>` | 总条数 |
112
- | `pagination` | `object` | 分页配置(Naive UI 格式) |
113
- | `columns` | `Ref<DataTableColumn[]>` | 表格列(含操作列) |
114
- | `search()` | `() => Promise<void>` | 搜索 |
115
- | `resetSearch()` | `() => void` | 重置搜索 |
116
- | `refresh()` | `() => Promise<void>` | 刷新数据 |
117
- | `viewDetail(row)` | `(row: T) => void` | 查看详情 |
118
- | `handleEdit(row)` | `(row: T) => void` | 编辑 |
119
- | `handleDelete(row)` | `(row: T) => void` | 删除 |
120
-
121
- ---
122
-
123
- ## 🔌 插件系统
124
-
125
- 所有请求方法都支持插件配置:
126
-
127
- ### 缓存插件(仅 GET)
128
-
129
- ```ts
130
- getData('/users', {
131
- cache: {
132
- enabled: true, // 启用缓存
133
- ttl: 300000, // 5 分钟过期
134
- forceUpdate: false // 不强制更新
135
- }
136
- })
137
- ```
138
-
139
- ### 重试插件
140
-
141
- ```ts
142
- postData('/submit', data, {
143
- retry: {
144
- enabled: true, // 启用重试
145
- count: 3, // 最多重试 3 次
146
- delay: 1000, // 重试延迟 1 秒
147
- exponentialBackoff: true // 指数退避(1s, 2s, 4s)
148
- }
149
- })
150
- ```
151
-
152
- ### 去重插件
153
-
154
- ```ts
155
- getData('/users', {
156
- dedupe: {
157
- enabled: true, // 启用去重(默认启用)
158
- keyGenerator: (config) => `${config.method}-${config.url}` // 自定义去重 key
159
- }
160
- })
161
- ```
162
-
163
- ### 取消插件
164
-
165
- ```ts
166
- getData('/users', {
167
- cancel: {
168
- enabled: true, // 启用自动取消(路由切换时)
169
- whitelist: [] // 白名单接口(不取消)
170
- }
171
- })
172
- ```
173
-
174
- ### reLogin 插件
175
-
176
- 自动管理重新登录场景,多个请求等待登录完成后自动重试:
177
-
178
- ```ts
179
- import { onReLoginSuccess } from '@robot-admin/request-core'
180
-
181
- // 在登录成功后调用
182
- onReLoginSuccess() // 通知所有等待的请求继续
183
- ```
184
-
185
- ---
186
-
187
- ## 🎯 完整示例
188
-
189
- ### 场景 1:完整的数据表格(分页 + 搜索 + CRUD)
190
-
191
- ```vue
192
- <script setup lang="ts">
193
- import { useTableCrud } from '@robot-admin/request-core'
194
-
195
- interface User {
196
- id: number
197
- name: string
198
- email: string
199
- role: string
200
- }
201
-
202
- const table = useTableCrud<User>({
203
- api: {
204
- list: '/api/users/list',
205
- get: '/api/users/:id',
206
- create: '/api/users',
207
- update: '/api/users/:id',
208
- remove: '/api/users/:id'
209
- },
210
- columns: [
211
- { key: 'id', title: 'ID', width: 80 },
212
- { key: 'name', title: '姓名', width: 120 },
213
- { key: 'email', title: '邮箱', width: 200 },
214
- { key: 'role', title: '角色', width: 100 }
215
- ],
216
- customActions: [
217
- {
218
- key: 'resetPassword',
219
- label: '重置密码',
220
- icon: 'mdi:lock-reset',
221
- handler: async (row, ctx) => {
222
- await postData(`/api/users/${row.id}/reset-password`, {})
223
- ctx.message.success('密码已重置')
224
- }
225
- }
226
- ]
227
- })
228
- </script>
229
-
230
- <template>
231
- <n-space vertical>
232
- <!-- 搜索栏 -->
233
- <n-space>
234
- <n-input v-model:value="table.searchKeyword.value" placeholder="搜索用户..." />
235
- <n-button type="primary" @click="table.search()">搜索</n-button>
236
- <n-button @click="table.resetSearch()">重置</n-button>
237
- <n-button type="success" @click="table.handleCreate()">新增用户</n-button>
238
- </n-space>
239
-
240
- <!-- 表格 -->
241
- <n-data-table
242
- :data="table.data.value"
243
- :columns="table.columns.value"
244
- :loading="table.loading.value"
245
- :pagination="table.pagination"
246
- />
247
- </n-space>
248
- </template>
249
- ```
250
-
251
- ### 场景 2:自定义请求(带插件)
252
-
253
- ```ts
254
- import { getData, postData } from '@robot-admin/request-core'
255
-
256
- // 带缓存的 GET 请求
257
- const users = await getData('/api/users', {
258
- cache: { enabled: true, ttl: 300000 } // 缓存 5 分钟
259
- })
260
-
261
- // 带重试的 POST 请求
262
- const result = await postData('/api/submit', { data: 'test' }, {
263
- retry: { enabled: true, count: 3 }
264
- })
265
- ```
266
-
267
- ---
268
-
269
- ## ⚙️ 高级配置
270
-
271
- ### 适配不同后端响应格式
272
-
273
- #### 1. 自定义成功状态码
274
-
275
- 如果后端返回的成功码不是 `0` 或 `200`:
276
-
277
- ```ts
278
- createRequestCore({
279
- request: { baseURL: '/api' },
280
- successCodes: [1, '1', 'success'], // 自定义成功码
281
- interceptors: {
282
- response: (response) => {
283
- const { code } = response.data
284
- if (![1, '1', 'success'].includes(code)) {
285
- throw new Error(response.data.message)
286
- }
287
- return response
288
- }
289
- }
290
- })
291
- ```
292
-
293
- #### 2. 自定义字段名映射
294
-
295
- 如果后端返回的字段名不标准(如 `items` 而非 `list`):
296
-
297
- ```ts
298
- createRequestCore({
299
- request: { baseURL: '/api' },
300
- fieldAliases: {
301
- data: ['result', 'data'], // 数据层字段
302
- list: ['items', 'records', 'list'], // 列表字段
303
- total: ['totalCount', 'total'] // 总数字段
304
- }
305
- })
306
- ```
307
-
308
- #### 3. 单个接口特殊处理
309
-
310
- ```ts
311
- useTableCrud({
312
- api: { list: '/special/api' },
313
- extractListData: (response) => ({
314
- items: response.result?.data || [],
315
- total: response.result?.count || 0
316
- })
317
- })
318
- ```
319
-
320
- ---
321
-
322
- ## 💡 最佳实践
323
-
324
- ### ✅ 推荐做法
325
-
326
- 1. **统一初始化配置**
327
- ```ts
328
- // src/plugins/request-core.ts
329
- export function setupRequestCore(app: App) {
330
- app.use(createRequestCore({ /* 统一配置 */ }))
331
- }
332
- ```
333
-
334
- 2. **使用 composable 封装业务逻辑**
335
- ```ts
336
- // composables/useUsers.ts
337
- export function useUsers() {
338
- return useTableCrud<User>({
339
- api: { /* ... */ },
340
- columns: [ /* ... */ ]
341
- })
342
- }
343
- ```
344
-
345
- 3. **开启缓存减少重复请求**
346
- ```ts
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
+ > 为 Vue 3 打造的企业级请求解决方案:Axios 增强 + 6 个请求插件 + CRUD Composable
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@robot-admin/request-core.svg)](https://www.npmjs.com/package/@robot-admin/request-core)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ 当前版本:`0.2.0`。
9
+
10
+ ---
11
+
12
+ ## 核心特性
13
+
14
+ - 🚀 **开箱即用**:3 步接入,5 分钟实现完整 CRUD
15
+ - 🔌 **请求插件**:缓存、重试、去重、取消与可共享的 reLogin 协调
16
+ - 📊 **useTableCrud**:配置式表格 CRUD,自动处理分页/搜索/增删改查
17
+ - 🎯 **智能适配**:自动兼容不同后端响应格式(字段名、成功码)
18
+ - 💪 **类型安全**:完整 TypeScript 支持
19
+ - 🎨 **Naive UI 集成**:深度集成 Naive UI 组件
20
+
21
+ ---
22
+
23
+ ## 📦 安装
24
+
25
+ ```bash
26
+ npm install @robot-admin/request-core
27
+ # 或
28
+ bun add @robot-admin/request-core
29
+ ```
30
+
31
+ **Peer Dependencies**: `axios@^1.7.0`;使用根入口或 `./crud` 时还需
32
+ `vue@^3.4.0` 与 `naive-ui@^2.38.0`。
33
+
34
+ 纯请求场景可从 `@robot-admin/request-core/axios` 导入 `createAxiosInstance`
35
+ API;CRUD 可使用 `@robot-admin/request-core/crud`。根入口继续保留
36
+ `createRequestCore` 和全部 API,兼容已有项目。
37
+
38
+ ---
39
+
40
+ ## 🚀 30 秒快速上手
41
+
42
+ ### 1️⃣ 初始化(main.ts)
43
+
44
+ ```ts
45
+ import { createApp } from 'vue'
46
+ import { createRequestCore } from '@robot-admin/request-core'
47
+
48
+ const app = createApp(App)
49
+
50
+ app.use(createRequestCore({
51
+ request: { baseURL: '/api', timeout: 10000 },
52
+ interceptors: {
53
+ request: (config) => {
54
+ config.headers.Authorization = `Bearer ${localStorage.getItem('token')}`
55
+ return config
56
+ }
57
+ }
58
+ }))
59
+ ```
60
+
61
+ ### 2️⃣ 使用 CRUD(任意组件)
62
+
63
+ ```vue
64
+ <script setup lang="ts">
65
+ import { useTableCrud } from '@robot-admin/request-core'
66
+
67
+ const table = useTableCrud({
68
+ api: { list: '/users', get: '/users/:id', create: '/users', update: '/users/:id', remove: '/users/:id' },
69
+ columns: [
70
+ { key: 'id', title: 'ID' },
71
+ { key: 'name', title: '姓名' },
72
+ { key: 'email', title: '邮箱' }
73
+ ]
74
+ })
75
+ </script>
76
+
77
+ <template>
78
+ <n-data-table :data="table.data.value" :columns="table.columns.value" :loading="table.loading.value" />
79
+ </template>
80
+ ```
81
+
82
+ ✅ **完成!** 一个完整的带分页、搜索、编辑、删除的数据表格!
83
+
84
+ ---
85
+
86
+ ## 📚 核心 API
87
+
88
+ ### 请求方法
89
+
90
+ | 方法 | 说明 | 示例 |
91
+ |------|------|------|
92
+ | `getData(url, config?)` | GET 请求 | `getData('/users')` |
93
+ | `postData(url, data, config?)` | POST 请求 | `postData('/users', { name: '张三' })` |
94
+ | `putData(url, data, config?)` | PUT 请求 | `putData('/users/1', { name: '李四' })` |
95
+ | `deleteData(url, config?)` | DELETE 请求 | `deleteData('/users/1')` |
96
+
97
+ ### useTableCrud 配置
98
+
99
+ | 属性 | 类型 | 必填 | 说明 |
100
+ |------|------|------|------|
101
+ | `api.list` | `string` | | 列表接口 |
102
+ | `api.get` | `string` | - | 详情接口(支持 `:id`) |
103
+ | `api.create` | `string` | - | 创建接口 |
104
+ | `api.update` | `string` | - | 更新接口(支持 `:id`) |
105
+ | `api.remove` | `string` | - | 删除接口(支持 `:id`) |
106
+ | `columns` | `TableColumn[]` | ✅ | 表格列配置 |
107
+ | `customActions` | `CustomAction[]` | - | 自定义操作按钮 |
108
+ | `idKey` | `string` | - | ID 字段名(默认 `'id'`) |
109
+ | `defaultPageSize` | `number` | - | 每页条数(默认 `10`) |
110
+ | `autoLoad` | `boolean` | - | 自动加载(默认 `true`) |
111
+
112
+ ### useTableCrud 返回值
113
+
114
+ | 属性/方法 | 类型 | 说明 |
115
+ |-----------|------|------|
116
+ | `data` | `Ref<T[]>` | 表格数据 |
117
+ | `loading` | `Ref<boolean>` | 加载状态 |
118
+ | `total` | `Ref<number>` | 总条数 |
119
+ | `pagination` | `object` | 分页配置(Naive UI 格式) |
120
+ | `columns` | `Ref<DataTableColumn[]>` | 表格列(含操作列) |
121
+ | `search()` | `() => Promise<void>` | 搜索 |
122
+ | `resetSearch()` | `() => void` | 重置搜索 |
123
+ | `refresh()` | `() => Promise<void>` | 刷新数据 |
124
+ | `viewDetail(row)` | `(row: T) => void` | 查看详情 |
125
+ | `handleEdit(row)` | `(row: T) => void` | 编辑 |
126
+ | `handleDelete(row)` | `(row: T) => void` | 删除 |
127
+
128
+ ---
129
+
130
+ ## 🔌 插件系统
131
+
132
+ 所有请求方法都支持插件配置:
133
+
134
+ ### 缓存插件(仅 GET)
135
+
136
+ ```ts
137
+ getData('/users', {
138
+ cache: {
139
+ enabled: true, // 启用缓存
140
+ ttl: 300000, // 5 分钟过期
141
+ forceUpdate: false // 不强制更新
142
+ }
143
+ })
144
+ ```
145
+
146
+ ### 重试插件
147
+
148
+ ```ts
149
+ getData('/report', {
150
+ retry: {
151
+ enabled: true, // 启用重试
152
+ count: 3, // 最多重试 3 次
153
+ delay: 1000, // 基础延迟 1 秒
154
+ exponentialBackoff: true,// 指数退避
155
+ jitter: true, // ±25% 随机抖动,降低并发惊群
156
+ }
157
+ })
158
+ ```
159
+
160
+ 默认只重试 `GET/HEAD/OPTIONS/PUT/DELETE`。只有服务端实现幂等键等保护、确认重复
161
+ 执行安全时,才应通过 `retryableMethods: ['POST']` 显式允许 POST。
162
+
163
+ ### 去重插件
164
+
165
+ ```ts
166
+ getData('/users', {
167
+ dedupe: {
168
+ enabled: true, // 启用去重(默认启用)
169
+ keyGenerator: (config) => `${config.method}-${config.url}` // 自定义去重 key
170
+ }
171
+ })
172
+ ```
173
+
174
+ 默认 key 会包含 `Authorization`、`X-Tenant-Id` 和 `X-User-Id`,防止多租户或
175
+ 多用户场景下缓存/去重串用;如自定义 `keyGenerator`,也应保留等价的身份维度。
176
+
177
+ ### 取消插件
178
+
179
+ ```ts
180
+ getData('/users', {
181
+ cancel: {
182
+ enabled: true, // 启用自动取消(路由切换时)
183
+ whitelist: [] // 白名单接口(不取消)
184
+ }
185
+ })
186
+ ```
187
+
188
+ ### reLogin 插件
189
+
190
+ 为多个 401 请求提供同一个等待 Promise。业务拦截器仍负责展示登录界面、
191
+ 刷新 token 和重发请求,库不会擅自决定业务流程:
192
+
193
+ ```ts
194
+ import {
195
+ waitForReLogin,
196
+ onReLoginSuccess,
197
+ onReLoginCancel,
198
+ } from '@robot-admin/request-core/axios'
199
+
200
+ // 每个收到 401 的请求都等待同一个 Promise
201
+ await waitForReLogin()
202
+
203
+ // 登录弹窗在成功或取消时调用其一
204
+ onReLoginSuccess()
205
+ onReLoginCancel()
206
+ ```
207
+
208
+ ---
209
+
210
+ ## 🎯 完整示例
211
+
212
+ ### 场景 1:完整的数据表格(分页 + 搜索 + CRUD)
213
+
214
+ ```vue
215
+ <script setup lang="ts">
216
+ import { useTableCrud } from '@robot-admin/request-core'
217
+
218
+ interface User {
219
+ id: number
220
+ name: string
221
+ email: string
222
+ role: string
223
+ }
224
+
225
+ const table = useTableCrud<User>({
226
+ api: {
227
+ list: '/api/users/list',
228
+ get: '/api/users/:id',
229
+ create: '/api/users',
230
+ update: '/api/users/:id',
231
+ remove: '/api/users/:id'
232
+ },
233
+ columns: [
234
+ { key: 'id', title: 'ID', width: 80 },
235
+ { key: 'name', title: '姓名', width: 120 },
236
+ { key: 'email', title: '邮箱', width: 200 },
237
+ { key: 'role', title: '角色', width: 100 }
238
+ ],
239
+ customActions: [
240
+ {
241
+ key: 'resetPassword',
242
+ label: '重置密码',
243
+ icon: 'mdi:lock-reset',
244
+ handler: async (row, ctx) => {
245
+ await postData(`/api/users/${row.id}/reset-password`, {})
246
+ ctx.message.success('密码已重置')
247
+ }
248
+ }
249
+ ]
250
+ })
251
+ </script>
252
+
253
+ <template>
254
+ <n-space vertical>
255
+ <!-- 搜索栏 -->
256
+ <n-space>
257
+ <n-input v-model:value="table.searchKeyword.value" placeholder="搜索用户..." />
258
+ <n-button type="primary" @click="table.search()">搜索</n-button>
259
+ <n-button @click="table.resetSearch()">重置</n-button>
260
+ <n-button type="success" @click="table.handleCreate()">新增用户</n-button>
261
+ </n-space>
262
+
263
+ <!-- 表格 -->
264
+ <n-data-table
265
+ :data="table.data.value"
266
+ :columns="table.columns.value"
267
+ :loading="table.loading.value"
268
+ :pagination="table.pagination"
269
+ />
270
+ </n-space>
271
+ </template>
272
+ ```
273
+
274
+ ### 场景 2:自定义请求(带插件)
275
+
276
+ ```ts
277
+ import { getData, postData } from '@robot-admin/request-core'
278
+
279
+ // 带缓存的 GET 请求
280
+ const users = await getData('/api/users', {
281
+ cache: { enabled: true, ttl: 300000 } // 缓存 5 分钟
282
+ })
283
+
284
+ // 默认仅重试幂等方法;POST 必须由业务明确确认可安全重复后才能加入白名单
285
+ const result = await postData('/api/idempotent-submit', { data: 'test' }, {
286
+ retry: {
287
+ enabled: true,
288
+ count: 3,
289
+ retryableMethods: ['POST'],
290
+ jitter: true,
291
+ }
292
+ })
293
+ ```
294
+
295
+ 调用方传入的 `AbortSignal` 会与 dedupe、路由批量取消共享同一取消链;自动重试
296
+ 的退避等待也可立即取消。默认重试方法为 `GET/HEAD/OPTIONS/PUT/DELETE`,不会
297
+ 自动重试普通 POST 请求。
298
+
299
+ ---
300
+
301
+ ## ⚙️ 高级配置
302
+
303
+ ### 适配不同后端响应格式
304
+
305
+ #### 1. 自定义成功状态码
306
+
307
+ 如果后端返回的成功码不是 `0` 或 `200`:
308
+
309
+ ```ts
310
+ createRequestCore({
311
+ request: { baseURL: '/api' },
312
+ successCodes: [1, '1', 'success'], // 自定义成功码
313
+ interceptors: {
314
+ response: (response) => {
315
+ const { code } = response.data
316
+ if (![1, '1', 'success'].includes(code)) {
317
+ throw new Error(response.data.message)
318
+ }
319
+ return response
320
+ }
321
+ }
322
+ })
323
+ ```
324
+
325
+ #### 2. 自定义字段名映射
326
+
327
+ 如果后端返回的字段名不标准(如 `items` 而非 `list`):
328
+
329
+ ```ts
330
+ createRequestCore({
331
+ request: { baseURL: '/api' },
332
+ fieldAliases: {
333
+ data: ['result', 'data'], // 数据层字段
334
+ list: ['items', 'records', 'list'], // 列表字段
335
+ total: ['totalCount', 'total'] // 总数字段
336
+ }
337
+ })
338
+ ```
339
+
340
+ #### 3. 单个接口特殊处理
341
+
342
+ ```ts
343
+ useTableCrud({
344
+ api: { list: '/special/api' },
345
+ extractListData: (response) => ({
346
+ items: response.result?.data || [],
347
+ total: response.result?.count || 0
348
+ })
349
+ })
350
+ ```
351
+
352
+ ---
353
+
354
+ ## 💡 最佳实践
355
+
356
+ ### ✅ 推荐做法
357
+
358
+ 1. **统一初始化配置**
359
+ ```ts
360
+ // src/plugins/request-core.ts
361
+ export function setupRequestCore(app: App) {
362
+ app.use(createRequestCore({ /* 统一配置 */ }))
363
+ }
364
+ ```
365
+
366
+ 2. **使用 composable 封装业务逻辑**
367
+ ```ts
368
+ // composables/useUsers.ts
369
+ export function useUsers() {
370
+ return useTableCrud<User>({
371
+ api: { /* ... */ },
372
+ columns: [ /* ... */ ]
373
+ })
374
+ }
375
+ ```
376
+
377
+ 3. **开启缓存减少重复请求**
378
+ ```ts
379
+ getData('/api/config', { cache: { enabled: true, ttl: 600000 } })
380
+ ```
381
+
382
+ 4. **仅为幂等接口启用重试**
383
+ ```ts
384
+ getData('/api/report', { retry: { enabled: true, count: 3, jitter: true } })
385
+ ```
386
+
387
+ ### 避免的做法
388
+
389
+ 1. 不要在每个组件中重复配置 axios
390
+ 2. 不要禁用去重插件(除非有特殊需求)
391
+ 3. ❌ 不要在 useTableCrud 外部调用其内部方法
392
+ 4. ❌ 不要为付款、创建订单等非幂等 POST 盲目开启重试
393
+
394
+ ---
395
+
396
+ ## 📖 完整类型定义
397
+
398
+ ```ts
399
+ // 核心配置
400
+ export type RequestCoreConfig = {
401
+ request: AxiosRequestConfig // Axios 基础配置
402
+ successCodes?: (string | number)[] // 成功状态码
403
+ fieldAliases?: FieldAliases // 字段映射
404
+ interceptors?: InterceptorConfig // 拦截器
405
+ }
406
+
407
+ // CRUD 配置
408
+ export type UseTableCrudConfig<T> = {
409
+ api: ApiEndpoints // API 端点
410
+ columns: TableColumn[] // 表格列
411
+ customActions?: CustomAction[] // 自定义操作
412
+ idKey?: string // ID 字段名
413
+ defaultPageSize?: number // 默认分页大小
414
+ autoLoad?: boolean // 是否自动加载
415
+ extractListData?: (res: any) => { items: T[]; total: number }
416
+ }
417
+
418
+ // 插件配置
419
+ export type EnhancedAxiosRequestConfig = AxiosRequestConfig & {
420
+ cache?: CacheConfig // 缓存配置
421
+ retry?: RetryConfig // 重试配置
422
+ dedupe?: DedupeConfig // 去重配置
423
+ cancel?: CancelConfig // 取消配置
424
+ }
425
+ ```
426
+
427
+ 查看完整类型定义:[src/index.ts](./src/index.ts)
428
+
429
+ ## 从 v0.1.x 升级到 v0.2.0
430
+
431
+ - `axios` 现在是必须的 peerDependency,应用应显式安装 `axios ^1.7`;这可以避免
432
+ 多 Axios 实例导致取消错误识别失效。
433
+ - 纯请求场景建议改从 `@robot-admin/request-core/axios` 导入,避免引入 Vue 与
434
+ Naive UI;CRUD 可从 `@robot-admin/request-core/crud` 导入,根入口保持兼容。
435
+ - POST 不再默认重试。确有幂等保障时,通过 `retryableMethods` 显式开启。
436
+ - 去重和路由取消现在共享取消控制器,调用方 `AbortSignal` 也会参与同一取消链。
437
+ - 401 协调使用共享 Promise;登录成功或取消后必须分别调用 `onReLoginSuccess()`
438
+ 或 `onReLoginCancel()` 释放所有等待请求。
439
+
440
+ ---
441
+
442
+ ## 🛠️ 开发
443
+
444
+ ```bash
445
+ bun install # 安装依赖
446
+ bun run dev # 开发模式(watch)
447
+ bun run build # 构建
448
+ bun run type-check # 类型检查
449
+ ```
450
+
451
+ ---
452
+
453
+ ## 📄 License
454
+
455
+ MIT © [ChenYu](https://github.com/ChenyCHENYU)