@okcy/alova 1.0.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 +116 -0
- package/dist/index.d.mts +218 -0
- package/dist/index.mjs +308 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @okcy/alova
|
|
2
|
+
|
|
3
|
+
基于 [alova](https://alova.js.org) 的 Vue 3 增强 HTTP 请求层。
|
|
4
|
+
|
|
5
|
+
封装了请求/响应拦截、全局 loading、401/400/403 错误处理、分页、表单、串行请求等通用逻辑。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @okcy/alova
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 快速开始
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createAlova, alovaDefaultOptions } from "@okcy/alova";
|
|
17
|
+
import { createHookHandler, createMethodHandler } from "@okcy/alova";
|
|
18
|
+
|
|
19
|
+
// 1. 创建 alova 实例
|
|
20
|
+
const alova = createAlova({
|
|
21
|
+
baseURL: "https://api.example.com",
|
|
22
|
+
...alovaDefaultOptions,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// 2. 配置请求/响应/错误处理
|
|
26
|
+
const methodHandler = createMethodHandler({
|
|
27
|
+
onAuth: () => router.push("/login"),
|
|
28
|
+
onE400: (res) => /* 处理参数校验错误 */ res,
|
|
29
|
+
onE403: () => /* 无权限处理 */,
|
|
30
|
+
onError: (res) => /* 通用错误处理 */,
|
|
31
|
+
onRequest: (method) => /* 请求前处理(如加 token) */,
|
|
32
|
+
onResponse: (data) => data,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// 3. 绑定到 alova
|
|
36
|
+
alova.beforeRequest(methodHandler.beforeRequest);
|
|
37
|
+
alova.response.onSuccess(methodHandler.responseHandler);
|
|
38
|
+
alova.response.onError(methodHandler.errorHandler);
|
|
39
|
+
|
|
40
|
+
// 4. 创建 hook 处理器
|
|
41
|
+
const hooks = createHookHandler({
|
|
42
|
+
onLoading: (loading, instance, msg) => /* 全局 loading 控制 */,
|
|
43
|
+
onPagination: (ctx) => /* 分页状态同步 */,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export const useList = hooks.usePagination;
|
|
47
|
+
export const useDetail = hooks.useRequest;
|
|
48
|
+
export const useFormData = hooks.useForm;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## API
|
|
52
|
+
|
|
53
|
+
### createAlova(alovaOptions)
|
|
54
|
+
|
|
55
|
+
创建 alova 实例。`alovaDefaultOptions` 提供默认配置:
|
|
56
|
+
- `statesHook: VueHook`
|
|
57
|
+
- `timeout: 30000ms`
|
|
58
|
+
- `cacheFor: GET 500ms`
|
|
59
|
+
|
|
60
|
+
### createMethodHandler(options)
|
|
61
|
+
|
|
62
|
+
创建请求/响应/错误统一处理器:
|
|
63
|
+
|
|
64
|
+
| 选项 | 说明 |
|
|
65
|
+
|------|------|
|
|
66
|
+
| `onAuth()` | 401 时触发(如跳转登录页) |
|
|
67
|
+
| `onE400(res, method)` | 400 参数校验失败回调,返回值会合并到 reject 的 data |
|
|
68
|
+
| `onE403(res, method)` | 403 无权限回调 |
|
|
69
|
+
| `onError(res, method)` | 其他错误回调 |
|
|
70
|
+
| `onRequest(method)` | 请求前置处理(如添加 token) |
|
|
71
|
+
| `onResponse(data, method)` | 响应数据预处理 |
|
|
72
|
+
|
|
73
|
+
### createHookHandler(options)
|
|
74
|
+
|
|
75
|
+
创建增强版的 alova hooks,自动处理 loading 状态:
|
|
76
|
+
|
|
77
|
+
| 选项 | 说明 |
|
|
78
|
+
|------|------|
|
|
79
|
+
| `onLoading(loading, instance, msg?)` | loading 状态变化回调 |
|
|
80
|
+
| `onPagination?(ctx)` | 分页状态同步(如更新 UI 组件) |
|
|
81
|
+
| `paginationConfig?()` | 分页默认配置 |
|
|
82
|
+
| `paginationRouter?(params)` | 分页参数路由同步 |
|
|
83
|
+
|
|
84
|
+
返回 hooks:
|
|
85
|
+
- **usePagination** — 增强分页,自动管理 `params`、路由同步、重置
|
|
86
|
+
- **useRequest** — 增强请求,支持 `force()` 强制刷新
|
|
87
|
+
- **useForm** — 增强表单,支持 400 错误回显
|
|
88
|
+
- **useSerialRequest** — 串行请求
|
|
89
|
+
|
|
90
|
+
### 工具函数
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { excludeNull, invalidateCache } from "@okcy/alova";
|
|
94
|
+
|
|
95
|
+
excludeNull({ a: " ", b: "hello", c: NaN });
|
|
96
|
+
// => { b: "hello" }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 缓存辅助
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { cacheForSec, cacheForMin, cacheForHour, cacheForDay } from "@okcy/alova";
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 类型
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import type { ResponseData, ResponseList, ResponsePage, ResponseError } from "@okcy/alova";
|
|
109
|
+
import type { UsePaginationResult, UseRequestResult, UseFormResult } from "@okcy/alova";
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 依赖
|
|
113
|
+
|
|
114
|
+
- Vue 3、alova
|
|
115
|
+
- `@okcy/core`(emittery)
|
|
116
|
+
- query-string
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { AlovaMethodHandler, FormHookConfig, PaginationHookConfig, RequestHookConfig, UseHookExposure, UsePaginationExposure, createClientTokenAuthentication } from "alova/client";
|
|
2
|
+
import qs from "query-string";
|
|
3
|
+
import { AlovaGenerics, DetailCacheConfig, Method, Method as Method$1, createAlova, invalidateCache, queryCache, setCache } from "alova";
|
|
4
|
+
import { ComponentInternalInstance, Ref } from "vue";
|
|
5
|
+
//#region src/response.d.ts
|
|
6
|
+
type ResponseData<T = any> = {
|
|
7
|
+
code: number;
|
|
8
|
+
data?: T;
|
|
9
|
+
msg: string;
|
|
10
|
+
};
|
|
11
|
+
type ResponseError<T = any> = {
|
|
12
|
+
body?: ResponseData<T>;
|
|
13
|
+
fields: {
|
|
14
|
+
[name: string]: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
type ResponsePage = {
|
|
18
|
+
current?: number;
|
|
19
|
+
total: number;
|
|
20
|
+
pageSize: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
totalPage?: number;
|
|
23
|
+
};
|
|
24
|
+
type ResponseList<T> = {
|
|
25
|
+
page: ResponsePage;
|
|
26
|
+
list: T[];
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/alova.axios.d.ts
|
|
30
|
+
declare const alovaDefaultOptions: {
|
|
31
|
+
statesHook: import("alova/vue").VueHookType;
|
|
32
|
+
responded: {
|
|
33
|
+
onError: (err: any, method: any) => Promise<never>;
|
|
34
|
+
onComplete(method: any): void;
|
|
35
|
+
};
|
|
36
|
+
timeout: number;
|
|
37
|
+
cacheFor: {
|
|
38
|
+
GET: {
|
|
39
|
+
expire: number;
|
|
40
|
+
};
|
|
41
|
+
HEAD: number;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/pagination.d.ts
|
|
46
|
+
type Obj = Record<string, any>;
|
|
47
|
+
type AG$3<T> = AlovaGenerics<T, T, T, T, T>;
|
|
48
|
+
interface UsePaginationOptions<Q extends Obj = Obj> extends PaginationHookConfig<any, any> {
|
|
49
|
+
params?: Q | Record<string, any>;
|
|
50
|
+
transform?: (params: Q) => void;
|
|
51
|
+
}
|
|
52
|
+
type PaginationMethodHandler<T, Q extends Obj = Obj> = (params: Q) => Method$1<any>;
|
|
53
|
+
type OldUsePaginationExposure<T> = Omit<UsePaginationExposure<AG$3<T>, T[], any[]>, "onSuccess">;
|
|
54
|
+
interface UsePaginationResult<T, Q extends Obj = Obj> extends OldUsePaginationExposure<T>, Pick<UseBaseHookExposure<T, AG$3<T>, UsePaginationResult<T, Q>>, "onBefore"> {
|
|
55
|
+
loading: Ref<boolean>;
|
|
56
|
+
params: Q;
|
|
57
|
+
data: Ref<T[]>;
|
|
58
|
+
/**
|
|
59
|
+
* 分页配置
|
|
60
|
+
*/
|
|
61
|
+
pagination: Ref<any>;
|
|
62
|
+
/**
|
|
63
|
+
* 重置分页参数
|
|
64
|
+
* @param params 分页参数
|
|
65
|
+
* @returns 无
|
|
66
|
+
*/
|
|
67
|
+
reset: () => void;
|
|
68
|
+
/**
|
|
69
|
+
* 分页路由更新
|
|
70
|
+
* @param params 分页参数
|
|
71
|
+
* @returns 无
|
|
72
|
+
*/
|
|
73
|
+
onRouter: (handler?: (params: Q | any) => void) => UsePaginationResult<T, Q>;
|
|
74
|
+
/**
|
|
75
|
+
* 重置分页参数
|
|
76
|
+
* @param params 分页参数
|
|
77
|
+
* @returns 无
|
|
78
|
+
*/
|
|
79
|
+
onReset: (handler?: (params: Q) => void) => UsePaginationResult<T, Q>;
|
|
80
|
+
/**
|
|
81
|
+
* 请求成功回调
|
|
82
|
+
* @param response 响应数据
|
|
83
|
+
* @returns 无
|
|
84
|
+
*/
|
|
85
|
+
onSuccess: (handler?: (response: {
|
|
86
|
+
data: ResponseList<T>;
|
|
87
|
+
}) => void) => UsePaginationResult<T, Q>;
|
|
88
|
+
}
|
|
89
|
+
type UsePaginationHook = <T = any, Q extends Obj = Obj>(handler: PaginationMethodHandler<T, Q>, options?: UsePaginationOptions<Q>) => UsePaginationResult<T, Q>;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/request.d.ts
|
|
92
|
+
type AG$2<T> = AlovaGenerics<T, T, T, T, T>;
|
|
93
|
+
interface UseRequestResult<T> extends UseBaseHookExposure<T, AG$2<T>, UseRequestResult<T>> {
|
|
94
|
+
/**
|
|
95
|
+
* 强制请求
|
|
96
|
+
* @returns
|
|
97
|
+
*/
|
|
98
|
+
force: () => UseRequestResult<T>;
|
|
99
|
+
data: Ref<T>;
|
|
100
|
+
}
|
|
101
|
+
type UseRequestHook = <T = any>(handler: Method$1<any> | AlovaMethodHandler<any, any>, config?: RequestHookConfig<AG$2<T>, any>) => UseRequestResult<T>;
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/serial-request.d.ts
|
|
104
|
+
type AG$1<T> = AlovaGenerics<T, T, T, T, T>;
|
|
105
|
+
interface UseRequestResult$1<T> extends UseBaseHookExposure<T, AG$1<T>, UseRequestResult$1<T>> {
|
|
106
|
+
/**
|
|
107
|
+
* 强制请求
|
|
108
|
+
* @returns
|
|
109
|
+
*/
|
|
110
|
+
force: () => UseRequestResult$1<T>;
|
|
111
|
+
data: Ref<T>;
|
|
112
|
+
}
|
|
113
|
+
type handler = Method$1<any> | AlovaMethodHandler<any, any>;
|
|
114
|
+
type UseSerialRequestHook = <T = any>(handlers: handler[], config?: RequestHookConfig<AG$1<T>, any>) => UseRequestResult$1<T>;
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/handler.d.ts
|
|
117
|
+
export interface ErrorWithResponse extends Error {
|
|
118
|
+
response?: ResponseData;
|
|
119
|
+
}
|
|
120
|
+
interface CreateMethodHandler {
|
|
121
|
+
onAuth(): void;
|
|
122
|
+
onE400(response: ResponseData, method: Method$1): Record<string, any>;
|
|
123
|
+
onE403(response: ResponseData, method: Method$1): void;
|
|
124
|
+
onError(response: ResponseData, method: Method$1): void;
|
|
125
|
+
onRequest(method: Method$1<any>): void;
|
|
126
|
+
onResponse(response: ResponseData, method: Method$1): any;
|
|
127
|
+
}
|
|
128
|
+
export declare const createMethodHandler: (options: CreateMethodHandler) => {
|
|
129
|
+
beforeRequest: (method: Method$1) => void;
|
|
130
|
+
errorHandler: (error: ErrorWithResponse, method: Method$1) => Promise<never>;
|
|
131
|
+
responseHandler: (response: any, method: Method$1) => any;
|
|
132
|
+
};
|
|
133
|
+
export interface CreateHookHandler {
|
|
134
|
+
/**
|
|
135
|
+
* 加载状态处理
|
|
136
|
+
*/
|
|
137
|
+
onLoading(loading: boolean, instance: ComponentInternalInstance, message?: string): void;
|
|
138
|
+
/**
|
|
139
|
+
* 分页UI处理
|
|
140
|
+
*/
|
|
141
|
+
onPagination?(ctx: any): boolean | any;
|
|
142
|
+
/**
|
|
143
|
+
* 分页默认配置
|
|
144
|
+
*/
|
|
145
|
+
paginationConfig?(): PaginationHookConfig<any, any>;
|
|
146
|
+
/**
|
|
147
|
+
* 默认分页路由更新
|
|
148
|
+
* @param params 分页参数
|
|
149
|
+
* @returns 无
|
|
150
|
+
*/
|
|
151
|
+
paginationRouter?(params: any): void;
|
|
152
|
+
}
|
|
153
|
+
interface CreateHookHandlerReturn {
|
|
154
|
+
usePagination: UsePaginationHook;
|
|
155
|
+
useRequest: UseRequestHook;
|
|
156
|
+
useForm: UseFormHook;
|
|
157
|
+
useSerialRequest: UseSerialRequestHook;
|
|
158
|
+
}
|
|
159
|
+
export declare const defaultPaginationConfig: () => PaginationHookConfig<any, any>;
|
|
160
|
+
export declare const createHookHandler: (options: CreateHookHandler) => CreateHookHandlerReturn;
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/hook-base.d.ts
|
|
163
|
+
interface UseBaseHookExposure<T, AG extends AlovaGenerics, SelfType = unknown> extends Omit<UseHookExposure<AG, any, SelfType>, "loading"> {
|
|
164
|
+
loading: Ref<boolean>;
|
|
165
|
+
isFirst: Ref<boolean>;
|
|
166
|
+
/**
|
|
167
|
+
* 请求前调用
|
|
168
|
+
* @param handler
|
|
169
|
+
* @param params
|
|
170
|
+
*/
|
|
171
|
+
onBefore(callback: (handler: {
|
|
172
|
+
method: Method$1<AG>;
|
|
173
|
+
}, params: Record<string, any>) => void): SelfType;
|
|
174
|
+
/**
|
|
175
|
+
* 响应加载,默认只执行一次
|
|
176
|
+
* @param first 是否首次调用,默认true
|
|
177
|
+
* @param message 提示信息
|
|
178
|
+
*/
|
|
179
|
+
onLoading(first?: boolean, message?: string): SelfType;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/form.d.ts
|
|
183
|
+
type AG<T> = AlovaGenerics<any, T, T, T, T>;
|
|
184
|
+
interface UseFormResult<T> extends UseBaseHookExposure<T, AG<T>, UseFormResult<T>> {
|
|
185
|
+
e400: Ref<any>;
|
|
186
|
+
onE400: (handler: (e: any) => void) => UseFormResult<T>;
|
|
187
|
+
form: Ref<T>;
|
|
188
|
+
onRestore(handler: () => void): UseFormResult<T>;
|
|
189
|
+
updateForm(newForm: Partial<T> | ((oldForm: T) => T)): void;
|
|
190
|
+
initialForm?: Partial<T>;
|
|
191
|
+
reset(): void;
|
|
192
|
+
}
|
|
193
|
+
interface UseFormOptions<T> extends Omit<FormHookConfig<AG<T>, T, any>, "initialForm"> {
|
|
194
|
+
initialForm?: Partial<T>;
|
|
195
|
+
}
|
|
196
|
+
type UseFormHook = <T = any>(handler: (form: T) => Method$1<any>, config?: UseFormOptions<T>) => UseFormResult<T>;
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/utils.d.ts
|
|
199
|
+
export declare const excludeNull: (params: any) => any;
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/index.d.ts
|
|
202
|
+
/**
|
|
203
|
+
* 缓存配置(秒)
|
|
204
|
+
*/
|
|
205
|
+
export declare const cacheForSec: (sec?: number) => DetailCacheConfig<any>;
|
|
206
|
+
/**
|
|
207
|
+
* 缓存时间配置
|
|
208
|
+
* @param minutes 分钟
|
|
209
|
+
*/
|
|
210
|
+
export declare const cacheForMin: (minutes?: number) => DetailCacheConfig<any>;
|
|
211
|
+
/**
|
|
212
|
+
* 缓存时间配置
|
|
213
|
+
* @param day 天
|
|
214
|
+
*/
|
|
215
|
+
export declare const cacheForHour: (hour?: number) => DetailCacheConfig<any>;
|
|
216
|
+
export declare const cacheForDay: (day?: number) => DetailCacheConfig<any>;
|
|
217
|
+
//#endregion
|
|
218
|
+
export { type Method, type ResponseData, type ResponseError, type ResponseList, type ResponsePage, type UseFormHook, type UseFormResult, type UsePaginationHook, type UsePaginationResult, type UseRequestHook, type UseRequestResult, type UseSerialRequestHook, alovaDefaultOptions, createAlova, createClientTokenAuthentication, invalidateCache, qs, queryCache, setCache };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { createClientTokenAuthentication, useAutoRequest, useForm, usePagination, useRequest, useSerialRequest } from "alova/client";
|
|
2
|
+
import qs, { default as qs$1 } from "query-string";
|
|
3
|
+
import { createAlova, invalidateCache, queryCache, setCache } from "alova";
|
|
4
|
+
import VueHook from "alova/vue";
|
|
5
|
+
import { Emitter } from "@okcy/core/emittery";
|
|
6
|
+
import { getCurrentInstance, reactive, ref, watch } from "vue";
|
|
7
|
+
//#region src/alova.axios.ts
|
|
8
|
+
const alovaDefaultOptions = {
|
|
9
|
+
statesHook: VueHook,
|
|
10
|
+
responded: {
|
|
11
|
+
onError: (err, method) => {
|
|
12
|
+
const response = err.response || err;
|
|
13
|
+
return Promise.reject(response);
|
|
14
|
+
},
|
|
15
|
+
onComplete(method) {}
|
|
16
|
+
},
|
|
17
|
+
timeout: 3e4,
|
|
18
|
+
cacheFor: {
|
|
19
|
+
GET: { expire: 500 },
|
|
20
|
+
HEAD: 500
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/hook-base.ts
|
|
25
|
+
useAutoRequest.onPolling = (notify, config) => {
|
|
26
|
+
config.notify = notify;
|
|
27
|
+
config.resume();
|
|
28
|
+
return () => clearInterval(config.timer);
|
|
29
|
+
};
|
|
30
|
+
const useBase = function(requestHook, methodHandler, config) {
|
|
31
|
+
const self = this;
|
|
32
|
+
const instance = getCurrentInstance();
|
|
33
|
+
const events = new Emitter();
|
|
34
|
+
const isGlobalLoading = ref(false);
|
|
35
|
+
const _config = Object.assign(config || {}, { middleware(ctx, next) {
|
|
36
|
+
ctx.method.meta = {};
|
|
37
|
+
events.emit("before", ctx);
|
|
38
|
+
events.emit("loading", {
|
|
39
|
+
loading: true,
|
|
40
|
+
method: ctx.method,
|
|
41
|
+
instance
|
|
42
|
+
});
|
|
43
|
+
return next();
|
|
44
|
+
} });
|
|
45
|
+
const request = requestHook.bind(self)(methodHandler, _config);
|
|
46
|
+
request.isFirst = ref(true);
|
|
47
|
+
request.onBefore = (handler) => {
|
|
48
|
+
events.on("before", ({ data }) => {
|
|
49
|
+
handler(data, data.method.config.params);
|
|
50
|
+
});
|
|
51
|
+
return request;
|
|
52
|
+
};
|
|
53
|
+
const loadingFun = async (ctx, message) => {
|
|
54
|
+
self.onLoading(true, instance, message);
|
|
55
|
+
isGlobalLoading.value = true;
|
|
56
|
+
};
|
|
57
|
+
request.onLoading = (first = true, message) => {
|
|
58
|
+
if (first) events.once("loading", ({ data }) => {
|
|
59
|
+
loadingFun(data, message);
|
|
60
|
+
return true;
|
|
61
|
+
});
|
|
62
|
+
else events.on("loading", ({ data }) => loadingFun(data, message));
|
|
63
|
+
return request;
|
|
64
|
+
};
|
|
65
|
+
request.onComplete((ctx) => {
|
|
66
|
+
if (isGlobalLoading.value) {
|
|
67
|
+
self.onLoading(false, instance);
|
|
68
|
+
isGlobalLoading.value = false;
|
|
69
|
+
}
|
|
70
|
+
request.isFirst.value = false;
|
|
71
|
+
return request;
|
|
72
|
+
});
|
|
73
|
+
return request;
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/form.ts
|
|
77
|
+
function useForm$1(handler, config) {
|
|
78
|
+
const events = new Emitter();
|
|
79
|
+
const self = this;
|
|
80
|
+
const e400 = ref({});
|
|
81
|
+
const _config = Object.assign({ initialForm: {} }, config);
|
|
82
|
+
const req = useBase.bind(self)(useForm, handler, _config);
|
|
83
|
+
req.onError(({ error }) => {
|
|
84
|
+
switch (error.code) {
|
|
85
|
+
case 400:
|
|
86
|
+
e400.value = error.data;
|
|
87
|
+
events.emit("e400", e400.value);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
req.e400 = e400;
|
|
91
|
+
req.onE400 = (handler) => {
|
|
92
|
+
events.on("e400", () => handler(e400.value));
|
|
93
|
+
return req;
|
|
94
|
+
};
|
|
95
|
+
return req;
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/utils.ts
|
|
99
|
+
const excludeNull = (params) => {
|
|
100
|
+
const q = {};
|
|
101
|
+
for (const [key, value] of Object.entries(params)) {
|
|
102
|
+
if (value === "NaN") continue;
|
|
103
|
+
if (typeof value === "number" && isNaN(value)) continue;
|
|
104
|
+
if (typeof value === "string" && value.trim() === "") continue;
|
|
105
|
+
q[key] = typeof value === "string" ? value.trim() : value;
|
|
106
|
+
}
|
|
107
|
+
const query = qs$1.stringify(q, {
|
|
108
|
+
skipNull: true,
|
|
109
|
+
skipEmptyString: true
|
|
110
|
+
});
|
|
111
|
+
return qs$1.parse(query, {
|
|
112
|
+
parseNumbers: false,
|
|
113
|
+
parseBooleans: true
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/pagination.ts
|
|
118
|
+
const toNumber = (val) => {
|
|
119
|
+
const num = Number(val);
|
|
120
|
+
return isNaN(num) ? 1 : num;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* 增强版 usePagination —— 一步完成参数管理 + 请求 + 参数清理
|
|
124
|
+
*/
|
|
125
|
+
function usePagination$1(handler, options) {
|
|
126
|
+
const self = this;
|
|
127
|
+
const { params: initialParams = {}, transform, ...config } = options ?? {};
|
|
128
|
+
const paginationConfig = self.paginationConfig();
|
|
129
|
+
const events = new Emitter();
|
|
130
|
+
const params = reactive({ ...initialParams });
|
|
131
|
+
paginationConfig.initialPage = toNumber(params.current ?? 1);
|
|
132
|
+
paginationConfig.initialPageSize = toNumber(params.initialPageSize ?? 20);
|
|
133
|
+
const req = useBase.bind(this)(usePagination, (current, pageSize) => handler({
|
|
134
|
+
...params,
|
|
135
|
+
current,
|
|
136
|
+
pageSize
|
|
137
|
+
}), Object.assign(paginationConfig, config));
|
|
138
|
+
req.isLastPage.value = false;
|
|
139
|
+
const toRouter = (params) => {
|
|
140
|
+
if (events.listenerCount("router")) events.emit("router", excludeNull(params || {}));
|
|
141
|
+
else self.paginationRouter?.(excludeNull(params || {}));
|
|
142
|
+
};
|
|
143
|
+
watch(() => params.current, (newCurrent) => {
|
|
144
|
+
params.current = toNumber(newCurrent);
|
|
145
|
+
params.pageSize = toNumber(params.pageSize || config?.initialPageSize || 20);
|
|
146
|
+
toRouter(params);
|
|
147
|
+
});
|
|
148
|
+
const pagination = ref(self.onPagination?.(req));
|
|
149
|
+
req.onBefore((ctx, params) => {
|
|
150
|
+
toRouter(params);
|
|
151
|
+
});
|
|
152
|
+
req.onComplete((ctx) => {
|
|
153
|
+
pagination.value = self.onPagination?.(req);
|
|
154
|
+
});
|
|
155
|
+
const result = Object.assign(req, {
|
|
156
|
+
params,
|
|
157
|
+
pagination,
|
|
158
|
+
reset: () => {
|
|
159
|
+
for (const key in params) delete params[key];
|
|
160
|
+
events.emit("reset", params);
|
|
161
|
+
req.reload();
|
|
162
|
+
},
|
|
163
|
+
onReset: (handler) => {
|
|
164
|
+
events.on("reset", ({ data }) => handler(data));
|
|
165
|
+
return result;
|
|
166
|
+
},
|
|
167
|
+
onRouter: (handler) => {
|
|
168
|
+
events.on("router", ({ data }) => handler(data));
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/request.ts
|
|
176
|
+
function useRequest$1(handler, config) {
|
|
177
|
+
const force = ref(false);
|
|
178
|
+
const _config = Object.assign({ force: () => force.value }, config);
|
|
179
|
+
const req = useBase.bind(this)(useRequest, handler, _config);
|
|
180
|
+
req.force = () => {
|
|
181
|
+
force.value = true;
|
|
182
|
+
return req;
|
|
183
|
+
};
|
|
184
|
+
req.onComplete(() => {
|
|
185
|
+
force.value = false;
|
|
186
|
+
});
|
|
187
|
+
return req;
|
|
188
|
+
}
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/serial-request.ts
|
|
191
|
+
function useSerialRequest$1(handlers, config) {
|
|
192
|
+
const force = ref(false);
|
|
193
|
+
const _config = Object.assign({ force: () => force.value }, config);
|
|
194
|
+
const req = useBase.bind(this)(useSerialRequest, handlers, _config);
|
|
195
|
+
req.force = () => {
|
|
196
|
+
force.value = true;
|
|
197
|
+
return req;
|
|
198
|
+
};
|
|
199
|
+
req.onComplete(() => {
|
|
200
|
+
force.value = false;
|
|
201
|
+
});
|
|
202
|
+
return req;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/handler.ts
|
|
206
|
+
const createMethodHandler = (options) => {
|
|
207
|
+
const { onE400, onE403, onError, onAuth, onRequest, onResponse } = options;
|
|
208
|
+
const beforeRequest = (method) => {
|
|
209
|
+
onRequest?.(method);
|
|
210
|
+
};
|
|
211
|
+
const responseHandler = (response, method) => {
|
|
212
|
+
const { data, request, ...res } = response;
|
|
213
|
+
Object.assign(method.meta || {}, { response: res });
|
|
214
|
+
return onResponse ? onResponse?.(data, method) : data;
|
|
215
|
+
};
|
|
216
|
+
const errorHandler = (error, method) => {
|
|
217
|
+
const { response } = error;
|
|
218
|
+
if (response) {
|
|
219
|
+
const res = onResponse?.(response, method);
|
|
220
|
+
switch (res.code) {
|
|
221
|
+
case 401:
|
|
222
|
+
onAuth();
|
|
223
|
+
break;
|
|
224
|
+
case 400:
|
|
225
|
+
res.data = onE400(res, method);
|
|
226
|
+
break;
|
|
227
|
+
case 403:
|
|
228
|
+
onE403(response.data, method);
|
|
229
|
+
break;
|
|
230
|
+
default: onError(response.data, method);
|
|
231
|
+
}
|
|
232
|
+
return Promise.reject(res);
|
|
233
|
+
}
|
|
234
|
+
return Promise.reject({
|
|
235
|
+
code: "500",
|
|
236
|
+
data: void 0,
|
|
237
|
+
msg: error.message
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
return {
|
|
241
|
+
beforeRequest,
|
|
242
|
+
errorHandler,
|
|
243
|
+
responseHandler
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
const defaultPaginationConfig = () => {
|
|
247
|
+
return {
|
|
248
|
+
initialPage: 1,
|
|
249
|
+
initialPageSize: 20,
|
|
250
|
+
preloadPreviousPage: false,
|
|
251
|
+
preloadNextPage: false,
|
|
252
|
+
total: (res) => res?.page?.total ?? 0,
|
|
253
|
+
data: (res) => res?.list ?? []
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
const createHookHandler = (options) => {
|
|
257
|
+
const { paginationConfig = defaultPaginationConfig, paginationRouter, onPagination, onLoading } = options;
|
|
258
|
+
return {
|
|
259
|
+
usePagination: usePagination$1.bind({
|
|
260
|
+
onPagination,
|
|
261
|
+
paginationConfig,
|
|
262
|
+
paginationRouter,
|
|
263
|
+
onLoading
|
|
264
|
+
}),
|
|
265
|
+
useRequest: useRequest$1.bind({ onLoading }),
|
|
266
|
+
useForm: useForm$1.bind({ onLoading }),
|
|
267
|
+
useSerialRequest: useSerialRequest$1.bind({ onLoading })
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region src/index.ts
|
|
272
|
+
/**
|
|
273
|
+
* 缓存配置(秒)
|
|
274
|
+
*/
|
|
275
|
+
const cacheForSec = (sec = 1) => {
|
|
276
|
+
return {
|
|
277
|
+
mode: "restore",
|
|
278
|
+
expire: 1e3 * sec
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
/**
|
|
282
|
+
* 缓存时间配置
|
|
283
|
+
* @param minutes 分钟
|
|
284
|
+
*/
|
|
285
|
+
const cacheForMin = (minutes = 1) => {
|
|
286
|
+
return {
|
|
287
|
+
mode: "restore",
|
|
288
|
+
expire: 6e4 * minutes
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* 缓存时间配置
|
|
293
|
+
* @param day 天
|
|
294
|
+
*/
|
|
295
|
+
const cacheForHour = (hour = 1) => {
|
|
296
|
+
return {
|
|
297
|
+
mode: "restore",
|
|
298
|
+
expire: 36e5 * hour
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
const cacheForDay = (day = 1) => {
|
|
302
|
+
return {
|
|
303
|
+
mode: "restore",
|
|
304
|
+
expire: 864e5 * day
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
//#endregion
|
|
308
|
+
export { alovaDefaultOptions, cacheForDay, cacheForHour, cacheForMin, cacheForSec, createAlova, createClientTokenAuthentication, createHookHandler, createMethodHandler, defaultPaginationConfig, excludeNull, invalidateCache, qs, queryCache, setCache };
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@okcy/alova",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.mjs",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public",
|
|
15
|
+
"registry": "https://registry.npmjs.org/"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@alova/shared": "1.3.1",
|
|
19
|
+
"alova": "3.5.0",
|
|
20
|
+
"query-string": "9.2.2",
|
|
21
|
+
"vue": "3.5.24",
|
|
22
|
+
"@okcy/core": "1.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"vite-plus": "0.3.1"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"dev": "vp pack --watch",
|
|
29
|
+
"build": "vp pack"
|
|
30
|
+
}
|
|
31
|
+
}
|