vite-uni-dev-tool 1.1.0 → 1.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 +8 -0
- package/dist/const.cjs +1 -1
- package/dist/const.d.ts +1 -0
- package/dist/const.d.ts.map +1 -1
- package/dist/const.js +1 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +2 -2
- package/dist/i18n/locales/en.cjs +1 -1
- package/dist/i18n/locales/en.d.ts +5 -0
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +1 -1
- package/dist/i18n/locales/zh-Hans.cjs +1 -1
- package/dist/i18n/locales/zh-Hans.d.ts +5 -0
- package/dist/i18n/locales/zh-Hans.d.ts.map +1 -1
- package/dist/i18n/locales/zh-Hans.js +1 -1
- package/dist/modules/devIntercept/index.cjs +9 -9
- package/dist/modules/devIntercept/index.d.ts +1 -1
- package/dist/modules/devIntercept/index.d.ts.map +1 -1
- package/dist/modules/devIntercept/index.js +9 -9
- package/dist/modules/devStore/index.cjs +1 -1
- package/dist/modules/devStore/index.d.ts.map +1 -1
- package/dist/modules/devStore/index.js +1 -1
- package/dist/plugins/uniDevTool/transform/transformMain.cjs +3 -3
- package/dist/plugins/uniDevTool/transform/transformMain.d.ts +2 -1
- package/dist/plugins/uniDevTool/transform/transformMain.d.ts.map +1 -1
- package/dist/plugins/uniDevTool/transform/transformMain.js +3 -3
- package/dist/plugins/uniDevTool/transform/transformVue.cjs +31 -25
- package/dist/plugins/uniDevTool/transform/transformVue.d.ts +2 -1
- package/dist/plugins/uniDevTool/transform/transformVue.d.ts.map +1 -1
- package/dist/plugins/uniDevTool/transform/transformVue.js +30 -24
- package/dist/plugins/uniDevTool/uniDevTool.cjs +3 -3
- package/dist/plugins/uniDevTool/uniDevTool.d.ts +3 -1
- package/dist/plugins/uniDevTool/uniDevTool.d.ts.map +1 -1
- package/dist/plugins/uniDevTool/uniDevTool.js +3 -3
- package/dist/type.d.ts +3 -0
- package/dist/type.d.ts.map +1 -1
- package/dist/v3/DevTool/components/BluetoothList/BluetoothItem.vue +199 -199
- package/dist/v3/DevTool/components/BluetoothList/BluetoothTool.vue +730 -730
- package/dist/v3/DevTool/components/BluetoothList/index.vue +167 -167
- package/dist/v3/DevTool/components/CaptureScreen/index.vue +109 -109
- package/dist/v3/DevTool/components/ConsoleList/ConsoleItem.vue +230 -225
- package/dist/v3/DevTool/components/ConsoleList/RunJSInput.vue +247 -247
- package/dist/v3/DevTool/components/ConsoleList/index.vue +171 -171
- package/dist/v3/DevTool/components/ConsoleList/staticTips.ts +1145 -1145
- package/dist/v3/DevTool/components/DevToolTitle/index.vue +24 -24
- package/dist/v3/DevTool/components/DevToolWindow/DevToolOverlay.vue +197 -197
- package/dist/v3/DevTool/components/DevToolWindow/hooks/dataUtils.ts +48 -48
- package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolData.ts +387 -387
- package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolHandlers.ts +629 -629
- package/dist/v3/DevTool/components/DevToolWindow/hooks/useDevToolOverlay.ts +201 -197
- package/dist/v3/DevTool/components/ElEvent/ElEventItem.vue +105 -105
- package/dist/v3/DevTool/components/ElEvent/index.vue +106 -106
- package/dist/v3/DevTool/components/Instance/components/InstanceTreeNode.vue +265 -265
- package/dist/v3/DevTool/components/Instance/flatten.ts +226 -226
- package/dist/v3/DevTool/components/Instance/index.vue +94 -94
- package/dist/v3/DevTool/components/Instance/registry.ts +49 -49
- package/dist/v3/DevTool/components/Instance/transformTree.ts +375 -375
- package/dist/v3/DevTool/components/Instance/transformTreeCtx.ts +268 -268
- package/dist/v3/DevTool/components/Instance/typing.d.ts +43 -43
- package/dist/v3/DevTool/components/InstanceDetail/index.vue +485 -485
- package/dist/v3/DevTool/components/JsonDetail/index.vue +70 -70
- package/dist/v3/DevTool/components/NFCList/NFCItem.vue +112 -112
- package/dist/v3/DevTool/components/NFCList/NFCTool.vue +454 -454
- package/dist/v3/DevTool/components/NFCList/const.ts +56 -56
- package/dist/v3/DevTool/components/NFCList/index.vue +94 -94
- package/dist/v3/DevTool/components/NetworkList/InterceptConfig.vue +624 -624
- package/dist/v3/DevTool/components/NetworkList/InterceptItem.vue +140 -140
- package/dist/v3/DevTool/components/NetworkList/NetworkDetail.vue +287 -287
- package/dist/v3/DevTool/components/NetworkList/NetworkIntercept.vue +88 -88
- package/dist/v3/DevTool/components/NetworkList/NetworkItem.vue +163 -163
- package/dist/v3/DevTool/components/NetworkList/NetworkSend.vue +589 -589
- package/dist/v3/DevTool/components/NetworkList/const.ts +4 -4
- package/dist/v3/DevTool/components/NetworkList/hooks/useNetworkForm.ts +86 -86
- package/dist/v3/DevTool/components/NetworkList/index.vue +160 -160
- package/dist/v3/DevTool/components/NetworkList/utils.ts +101 -101
- package/dist/v3/DevTool/components/Performance/index.vue +498 -498
- package/dist/v3/DevTool/components/Performance/modules/PerformanceMetrics.vue +153 -153
- package/dist/v3/DevTool/components/Performance/modules/usePerformanceChart.ts +460 -460
- package/dist/v3/DevTool/components/Performance/modules/usePerformanceData.ts +258 -258
- package/dist/v3/DevTool/components/PiniaList/index.vue +93 -93
- package/dist/v3/DevTool/components/RunJS/index.vue +148 -148
- package/dist/v3/DevTool/components/ScanCodeList/ScanCodeItem.vue +97 -97
- package/dist/v3/DevTool/components/ScanCodeList/index.vue +100 -100
- package/dist/v3/DevTool/components/SettingButton/index.vue +45 -45
- package/dist/v3/DevTool/components/SettingList/index.vue +218 -218
- package/dist/v3/DevTool/components/SettingList/modules/SettingBarrage.vue +304 -304
- package/dist/v3/DevTool/components/SettingList/modules/SettingDevTool.vue +212 -212
- package/dist/v3/DevTool/components/SettingList/modules/SettingInfo.vue +157 -157
- package/dist/v3/DevTool/components/SettingList/modules/SettingLanguage.vue +74 -74
- package/dist/v3/DevTool/components/SettingList/modules/SettingLog.vue +230 -230
- package/dist/v3/DevTool/components/SettingList/typing.d.ts +2 -2
- package/dist/v3/DevTool/components/SourceCode/Line.vue +127 -127
- package/dist/v3/DevTool/components/SourceCode/parseCode.ts +609 -609
- package/dist/v3/DevTool/components/StorageList/index.vue +174 -174
- package/dist/v3/DevTool/components/TransferList/TransferDetail.vue +268 -268
- package/dist/v3/DevTool/components/VuexList/index.vue +84 -84
- package/dist/v3/DevTool/index.vue +1 -0
- package/dist/v3/components/AppTransition/index.vue +176 -176
- package/dist/v3/components/AutoSizer/index.vue +192 -192
- package/dist/v3/components/AutoSizer/index1.vue +184 -184
- package/dist/v3/components/AutoSizer/utils.ts +49 -49
- package/dist/v3/components/Barrage/BarrageItem.vue +137 -137
- package/dist/v3/components/Barrage/index.vue +202 -202
- package/dist/v3/components/CircularButton/index.vue +84 -84
- package/dist/v3/components/CustomSwiper/CustomSwiperItem.vue +49 -49
- package/dist/v3/components/CustomSwiper/index.vue +104 -104
- package/dist/v3/components/DevErrorBoundary/index.vue +380 -0
- package/dist/v3/components/Empty/index.vue +29 -29
- package/dist/v3/components/FilterSelect/index.vue +179 -179
- package/dist/v3/components/JsonPretty/components/Brackets/index.vue +27 -27
- package/dist/v3/components/JsonPretty/components/Carets/index.vue +59 -59
- package/dist/v3/components/JsonPretty/components/CheckController/index.vue +136 -136
- package/dist/v3/components/JsonPretty/components/TreeNode/index.vue +387 -387
- package/dist/v3/components/JsonPretty/hooks/useClipboard.ts +21 -21
- package/dist/v3/components/JsonPretty/hooks/useError.ts +21 -21
- package/dist/v3/components/JsonPretty/type.ts +127 -127
- package/dist/v3/components/JsonPretty/utils/index.ts +169 -169
- package/dist/v3/components/MovableContainer/index.vue +8 -4
- package/dist/v3/components/Pick/index.vue +322 -322
- package/dist/v3/components/Tag/index.vue +113 -113
- package/dist/v3/components/VirtualList/AutoSize.vue +40 -40
- package/dist/v3/components/VirtualList/index.vue +416 -416
- package/dist/v3/hooks/useBluetooth/index.ts +561 -561
- package/dist/v3/hooks/useContainerStyle.ts +153 -153
- package/dist/v3/hooks/useNFC/index.ts +107 -107
- package/dist/v3/hooks/useNFC/typing.d.ts +396 -396
- package/dist/v3/hooks/useNFC/useNFCAndroid.ts +966 -966
- package/dist/v3/hooks/useNFC/useNFCMpWeiXin.ts +812 -812
- package/dist/v3/hooks/useNFC/utils.ts +754 -754
- package/dist/v3/hooks/useRequest/index.ts +586 -586
- package/dist/v3/hooks/useRequest/utils.ts +267 -267
- package/dist/v3/hooks/useScanCode/index.ts +206 -206
- package/dist/v3/hooks/useWebsocket/index.ts +253 -253
- package/dist/v3/styles/theme.ts +12 -12
- package/package.json +9 -1
|
@@ -1,586 +1,586 @@
|
|
|
1
|
-
import {
|
|
2
|
-
onMounted,
|
|
3
|
-
onUnmounted,
|
|
4
|
-
ref,
|
|
5
|
-
shallowReactive,
|
|
6
|
-
watch,
|
|
7
|
-
type Ref,
|
|
8
|
-
} from 'vue';
|
|
9
|
-
import { debounce, throttle } from './utils';
|
|
10
|
-
|
|
11
|
-
// 添加分页响应数据类型
|
|
12
|
-
// type PaginationResponse<T = any> = {
|
|
13
|
-
// list?: T[];
|
|
14
|
-
// data?: T[];
|
|
15
|
-
// total: number;
|
|
16
|
-
// current: number;
|
|
17
|
-
// pageSize: number;
|
|
18
|
-
// };
|
|
19
|
-
|
|
20
|
-
type Option<R, P> = {
|
|
21
|
-
/** 是否需要uni.showLoading */
|
|
22
|
-
useUniLoading?: boolean;
|
|
23
|
-
/**
|
|
24
|
-
* 每次 ready 从 false 变为 true 时, 都会自动发起请求
|
|
25
|
-
* 为 false 时,请求永远都不会发出
|
|
26
|
-
*/
|
|
27
|
-
ready?: boolean | Ref<boolean>;
|
|
28
|
-
/** 立即执行 */
|
|
29
|
-
manual?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
* 默认参数
|
|
32
|
-
* 分页参数在请放入 分页参数配置中
|
|
33
|
-
*/
|
|
34
|
-
defaultParams?: P;
|
|
35
|
-
/** 依赖刷新 */
|
|
36
|
-
refreshDeps?: any[];
|
|
37
|
-
/**
|
|
38
|
-
* 延迟结束loading
|
|
39
|
-
* 单位毫秒
|
|
40
|
-
* 防止请求过快造成渲染闪烁
|
|
41
|
-
*/
|
|
42
|
-
loadingDelay?: number;
|
|
43
|
-
/**
|
|
44
|
-
* 轮询间隔
|
|
45
|
-
* 通过 run/runAsync 来启动轮询
|
|
46
|
-
* 通过 cancel 取消轮询
|
|
47
|
-
*/
|
|
48
|
-
pollingInterval?: number;
|
|
49
|
-
/**
|
|
50
|
-
* 轮询错误重试次数
|
|
51
|
-
*/
|
|
52
|
-
pollingErrorRetryCount?: number;
|
|
53
|
-
/**
|
|
54
|
-
* 错误重试次数
|
|
55
|
-
*/
|
|
56
|
-
errorRetryCount?: number;
|
|
57
|
-
/**
|
|
58
|
-
* 错误重试间隔
|
|
59
|
-
*/
|
|
60
|
-
errorRetryDelay?: number;
|
|
61
|
-
/**
|
|
62
|
-
* 防抖等待时间
|
|
63
|
-
*/
|
|
64
|
-
debounceWait?: number;
|
|
65
|
-
/**
|
|
66
|
-
* 在防抖开始前执行调用
|
|
67
|
-
*/
|
|
68
|
-
debounceLeading?: boolean;
|
|
69
|
-
/**
|
|
70
|
-
* 在防抖结束后执行调用
|
|
71
|
-
*/
|
|
72
|
-
debounceTrailing?: boolean;
|
|
73
|
-
/**
|
|
74
|
-
* 节流等待时间
|
|
75
|
-
*/
|
|
76
|
-
throttleWait?: number;
|
|
77
|
-
/**
|
|
78
|
-
* 在节流开始前执行调用
|
|
79
|
-
*/
|
|
80
|
-
throttleLeading?: boolean;
|
|
81
|
-
/**
|
|
82
|
-
* 在节流结束后执行调用
|
|
83
|
-
*/
|
|
84
|
-
throttleTrailing?: boolean;
|
|
85
|
-
/**
|
|
86
|
-
* 使用分页
|
|
87
|
-
*/
|
|
88
|
-
usePagination?: boolean;
|
|
89
|
-
/**
|
|
90
|
-
* 分页配置
|
|
91
|
-
*
|
|
92
|
-
*/
|
|
93
|
-
paginationConfig?: {
|
|
94
|
-
/** 当前页码参数名,默认 'current' */
|
|
95
|
-
currentKey?: string;
|
|
96
|
-
/** 每页数量参数名,默认 'pageSize' */
|
|
97
|
-
pageSizeKey?: string;
|
|
98
|
-
/** 总数字段名,默认 'total' */
|
|
99
|
-
totalKey?: string;
|
|
100
|
-
/** 默认当前页,默认 1 */
|
|
101
|
-
defaultCurrent?: number;
|
|
102
|
-
/** 默认每页数量,默认 10 */
|
|
103
|
-
defaultPageSize?: number;
|
|
104
|
-
/** 列表字段名,默认 'list' */
|
|
105
|
-
listKey?: string;
|
|
106
|
-
};
|
|
107
|
-
/** 请求前 */
|
|
108
|
-
onBefore?: () => Promise<void> | void;
|
|
109
|
-
/** 请求成功 */
|
|
110
|
-
onSuccess?: (result: R | undefined, params?: P) => void;
|
|
111
|
-
/** 请求失败 */
|
|
112
|
-
onError?: (error: any) => void;
|
|
113
|
-
/** 请求完成 */
|
|
114
|
-
onFinally?: () => void;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
export type Service<R, P extends any[]> = (...args: P) => Promise<R>;
|
|
118
|
-
|
|
119
|
-
const useRequest = <R, P extends any[]>(
|
|
120
|
-
service: Service<R, P>,
|
|
121
|
-
option?: Option<R, P>,
|
|
122
|
-
) => {
|
|
123
|
-
const loading = ref(false);
|
|
124
|
-
|
|
125
|
-
const data = ref<R>();
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 启用 usePagination 之后每页数据合并之后的结果
|
|
129
|
-
* 不开启默认为 []
|
|
130
|
-
* 只适合从第一页开始获取数据
|
|
131
|
-
*/
|
|
132
|
-
const list = ref<any[]>([]);
|
|
133
|
-
|
|
134
|
-
const pagination = shallowReactive({
|
|
135
|
-
current: 1,
|
|
136
|
-
pageSize: 10,
|
|
137
|
-
total: 0,
|
|
138
|
-
totalPage: 0,
|
|
139
|
-
hasMore: true,
|
|
140
|
-
// 分页操作方法
|
|
141
|
-
setCurrent: (current: number) => {
|
|
142
|
-
pagination.current = Math.max(1, current);
|
|
143
|
-
refresh();
|
|
144
|
-
},
|
|
145
|
-
setPageSize: (pageSize: number) => {
|
|
146
|
-
pagination.pageSize = pageSize;
|
|
147
|
-
pagination.current = 1; // 重置到第一页
|
|
148
|
-
refresh();
|
|
149
|
-
},
|
|
150
|
-
reset: () => {
|
|
151
|
-
pagination.current = option?.paginationConfig?.defaultCurrent ?? 1;
|
|
152
|
-
pagination.pageSize = option?.paginationConfig?.defaultPageSize ?? 10;
|
|
153
|
-
refresh();
|
|
154
|
-
},
|
|
155
|
-
prev: () => {
|
|
156
|
-
if (pagination.current > 1) {
|
|
157
|
-
pagination.current--;
|
|
158
|
-
refresh();
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
next: () => {
|
|
162
|
-
if (pagination.current < pagination.totalPage) {
|
|
163
|
-
pagination.current++;
|
|
164
|
-
refresh();
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
change: (current: number, pageSize: number) => {
|
|
168
|
-
pagination.current = current;
|
|
169
|
-
pagination.pageSize = pageSize;
|
|
170
|
-
refresh();
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
/** 最后一次请求的参数 */
|
|
175
|
-
let lastParams: P | undefined;
|
|
176
|
-
|
|
177
|
-
let loadingTimer: any;
|
|
178
|
-
|
|
179
|
-
let intervalTimer: any;
|
|
180
|
-
|
|
181
|
-
let allowPolling = true;
|
|
182
|
-
|
|
183
|
-
let pollingErrorCount = 0;
|
|
184
|
-
|
|
185
|
-
let errorCount = 0;
|
|
186
|
-
|
|
187
|
-
let intervalErrorRetry: ReturnType<typeof setTimeout> | undefined = undefined;
|
|
188
|
-
|
|
189
|
-
/** 记录上次 ready 的状态 */
|
|
190
|
-
let previousReady = false;
|
|
191
|
-
|
|
192
|
-
/** 获取 ready 值 */
|
|
193
|
-
const getReadyValue = (): boolean => {
|
|
194
|
-
if (option?.ready === undefined) return true;
|
|
195
|
-
return typeof option.ready === 'boolean'
|
|
196
|
-
? option.ready
|
|
197
|
-
: option.ready.value;
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
*
|
|
202
|
-
* @param params 请求参数
|
|
203
|
-
*/
|
|
204
|
-
function run(...params: P) {
|
|
205
|
-
if (getReadyValue()) {
|
|
206
|
-
execServiceFunction(params)
|
|
207
|
-
?.catch((error) => {
|
|
208
|
-
errorRetry(params);
|
|
209
|
-
throw error;
|
|
210
|
-
})
|
|
211
|
-
?.finally(() => {
|
|
212
|
-
if (option?.pollingInterval && option.pollingInterval > 0) {
|
|
213
|
-
startPolling(params);
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async function runAsync(...params: P) {
|
|
220
|
-
// 只有在 ready 为 true 时才执行请求
|
|
221
|
-
if (getReadyValue()) {
|
|
222
|
-
try {
|
|
223
|
-
const result = await execServiceFunction?.(params);
|
|
224
|
-
|
|
225
|
-
return result;
|
|
226
|
-
} catch (error) {
|
|
227
|
-
errorRetry(params);
|
|
228
|
-
throw error;
|
|
229
|
-
} finally {
|
|
230
|
-
if (option?.pollingInterval && option.pollingInterval > 0) {
|
|
231
|
-
startPolling(params);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* 刷新
|
|
239
|
-
*
|
|
240
|
-
*/
|
|
241
|
-
function refresh() {
|
|
242
|
-
// 只有在 ready 为 true 时才执行请求
|
|
243
|
-
if (getReadyValue()) {
|
|
244
|
-
const fallbackParams: P = ((lastParams || option?.defaultParams) ??
|
|
245
|
-
[]) as P;
|
|
246
|
-
|
|
247
|
-
execServiceFunction(fallbackParams);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* 刷新异步
|
|
253
|
-
*
|
|
254
|
-
* @return {*}
|
|
255
|
-
*/
|
|
256
|
-
function refreshAsync() {
|
|
257
|
-
// 只有在 ready 为 true 时才执行请求
|
|
258
|
-
if (getReadyValue()) {
|
|
259
|
-
const fallbackParams: P = ((lastParams || option?.defaultParams) ??
|
|
260
|
-
[]) as P;
|
|
261
|
-
|
|
262
|
-
return execServiceFunction(fallbackParams);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async function execService(params: P) {
|
|
267
|
-
// 只有在 ready 为 true 时才执行请求
|
|
268
|
-
if (getReadyValue()) {
|
|
269
|
-
try {
|
|
270
|
-
loading.value = true;
|
|
271
|
-
|
|
272
|
-
if (option?.useUniLoading) {
|
|
273
|
-
uni.showLoading({
|
|
274
|
-
title: '加载中...',
|
|
275
|
-
mask: true,
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
lastParams = params;
|
|
280
|
-
|
|
281
|
-
await option?.onBefore?.();
|
|
282
|
-
|
|
283
|
-
const result = await service(...params);
|
|
284
|
-
|
|
285
|
-
data.value = result;
|
|
286
|
-
|
|
287
|
-
// 如果启用了分页功能,处理分页数据
|
|
288
|
-
if (option?.usePagination) {
|
|
289
|
-
const config = option.paginationConfig || {};
|
|
290
|
-
const currentKey = config.currentKey || 'current';
|
|
291
|
-
const pageSizeKey = config.pageSizeKey || 'pageSize';
|
|
292
|
-
const totalKey = config.totalKey || 'total';
|
|
293
|
-
const listKey = config.listKey || 'list';
|
|
294
|
-
|
|
295
|
-
// 检查结果是否为分页格式
|
|
296
|
-
if (result && typeof result === 'object') {
|
|
297
|
-
const paginationResult = result as any;
|
|
298
|
-
pagination.current =
|
|
299
|
-
paginationResult[currentKey] || pagination.current;
|
|
300
|
-
pagination.pageSize =
|
|
301
|
-
paginationResult[pageSizeKey] || pagination.pageSize;
|
|
302
|
-
pagination.total = paginationResult[totalKey] || 0;
|
|
303
|
-
pagination.totalPage = Math.ceil(
|
|
304
|
-
pagination.total / pagination.pageSize,
|
|
305
|
-
);
|
|
306
|
-
pagination.hasMore = pagination.current < pagination.totalPage;
|
|
307
|
-
|
|
308
|
-
// 获取列表数据
|
|
309
|
-
const resultData =
|
|
310
|
-
paginationResult[listKey] ||
|
|
311
|
-
paginationResult.data ||
|
|
312
|
-
paginationResult.list ||
|
|
313
|
-
[];
|
|
314
|
-
// 根据当前页码决定是否合并列表数据
|
|
315
|
-
if (pagination.current === 1) {
|
|
316
|
-
// 第一页时,替换列表
|
|
317
|
-
list.value = [...resultData];
|
|
318
|
-
} else {
|
|
319
|
-
// 非第一页时,合并列表
|
|
320
|
-
list.value = [...list.value, ...resultData];
|
|
321
|
-
}
|
|
322
|
-
} else {
|
|
323
|
-
pagination.hasMore = false;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
option?.onSuccess?.(data.value, params);
|
|
328
|
-
|
|
329
|
-
return data.value;
|
|
330
|
-
} catch (error) {
|
|
331
|
-
pollingErrorCount++;
|
|
332
|
-
errorCount++;
|
|
333
|
-
option?.onError?.(error);
|
|
334
|
-
throw error;
|
|
335
|
-
} finally {
|
|
336
|
-
option?.onFinally?.();
|
|
337
|
-
|
|
338
|
-
const finishLoading = () => {
|
|
339
|
-
loading.value = false;
|
|
340
|
-
if (option?.useUniLoading) {
|
|
341
|
-
uni.hideLoading();
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
// 结束loading
|
|
346
|
-
if (
|
|
347
|
-
option?.useUniLoading &&
|
|
348
|
-
option?.loadingDelay &&
|
|
349
|
-
option.loadingDelay > 0
|
|
350
|
-
) {
|
|
351
|
-
clearTimeout(loadingTimer);
|
|
352
|
-
loadingTimer = setTimeout(finishLoading, option.loadingDelay);
|
|
353
|
-
} else {
|
|
354
|
-
finishLoading();
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const execServiceDebounce = debounce(execService, option?.debounceWait ?? 0, {
|
|
361
|
-
leading: option?.debounceLeading,
|
|
362
|
-
trailing: option?.debounceTrailing,
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
const execServiceThrottle = throttle(execService, option?.throttleWait ?? 0, {
|
|
366
|
-
leading: option?.throttleLeading,
|
|
367
|
-
trailing: option?.throttleTrailing,
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
function execServiceFunction(params: P) {
|
|
371
|
-
if (option?.usePagination) {
|
|
372
|
-
const config = option.paginationConfig || {};
|
|
373
|
-
const currentKey = config.currentKey || 'current';
|
|
374
|
-
const pageSizeKey = config.pageSizeKey || 'pageSize';
|
|
375
|
-
|
|
376
|
-
// 将分页参数注入到请求参数中
|
|
377
|
-
const paramsWithPagination = [...params] as any[];
|
|
378
|
-
|
|
379
|
-
// 如果参数是对象,添加分页字段
|
|
380
|
-
if (
|
|
381
|
-
paramsWithPagination.length > 0 &&
|
|
382
|
-
typeof paramsWithPagination[0] === 'object' &&
|
|
383
|
-
paramsWithPagination[0] !== null
|
|
384
|
-
) {
|
|
385
|
-
paramsWithPagination[0] = {
|
|
386
|
-
...paramsWithPagination[0],
|
|
387
|
-
[currentKey]: pagination.current,
|
|
388
|
-
[pageSizeKey]: pagination.pageSize,
|
|
389
|
-
};
|
|
390
|
-
} else {
|
|
391
|
-
// 如果没有参数或参数不是对象,创建一个包含分页信息的对象
|
|
392
|
-
paramsWithPagination.unshift({
|
|
393
|
-
[currentKey]: pagination.current,
|
|
394
|
-
[pageSizeKey]: pagination.pageSize,
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (option?.debounceWait) {
|
|
399
|
-
return execServiceDebounce(paramsWithPagination as P);
|
|
400
|
-
} else if (option?.throttleWait) {
|
|
401
|
-
return execServiceThrottle(paramsWithPagination as P);
|
|
402
|
-
} else {
|
|
403
|
-
return execService(paramsWithPagination as P);
|
|
404
|
-
}
|
|
405
|
-
} else {
|
|
406
|
-
if (option?.debounceWait) {
|
|
407
|
-
return execServiceDebounce(params);
|
|
408
|
-
} else if (option?.throttleWait) {
|
|
409
|
-
return execServiceThrottle(params);
|
|
410
|
-
} else {
|
|
411
|
-
return execService(params);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* 开启轮询
|
|
418
|
-
*
|
|
419
|
-
* @param {P} params
|
|
420
|
-
*/
|
|
421
|
-
function startPolling(params: P) {
|
|
422
|
-
cancel();
|
|
423
|
-
|
|
424
|
-
allowPolling = true;
|
|
425
|
-
|
|
426
|
-
if (!getReadyValue()) return;
|
|
427
|
-
|
|
428
|
-
// 使用递归函数实现轮询,确保请求完成后才开始计时下一次请求
|
|
429
|
-
const poll = async () => {
|
|
430
|
-
if (!allowPolling) return;
|
|
431
|
-
// 检查 ready 状态
|
|
432
|
-
|
|
433
|
-
try {
|
|
434
|
-
await execService(params);
|
|
435
|
-
|
|
436
|
-
// 检查错误重试次数限制
|
|
437
|
-
if (
|
|
438
|
-
option?.pollingErrorRetryCount &&
|
|
439
|
-
pollingErrorCount >= option.pollingErrorRetryCount
|
|
440
|
-
) {
|
|
441
|
-
cancel();
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// 如果仍然允许轮询,则设置定时器等待下次轮询
|
|
446
|
-
if (allowPolling) {
|
|
447
|
-
intervalTimer = setTimeout(poll, option?.pollingInterval);
|
|
448
|
-
}
|
|
449
|
-
} catch (_error) {
|
|
450
|
-
// execService 内部已经处理了错误,这里只需要确保继续轮询或停止
|
|
451
|
-
if (
|
|
452
|
-
option?.pollingErrorRetryCount &&
|
|
453
|
-
pollingErrorCount >= option.pollingErrorRetryCount
|
|
454
|
-
) {
|
|
455
|
-
cancel();
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// 如果仍然允许轮询,则设置定时器等待下次轮询
|
|
460
|
-
if (allowPolling) {
|
|
461
|
-
intervalTimer = setTimeout(poll, option?.pollingInterval);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
// 先等待轮询间隔时间,然后再开始执行请求
|
|
467
|
-
intervalTimer = setTimeout(() => {
|
|
468
|
-
// 开始轮询
|
|
469
|
-
poll();
|
|
470
|
-
}, option?.pollingInterval);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/** 取消轮询/重试 */
|
|
474
|
-
function cancel() {
|
|
475
|
-
allowPolling = false;
|
|
476
|
-
pollingErrorCount = 0;
|
|
477
|
-
errorCount = 0;
|
|
478
|
-
clearTimeout(intervalTimer);
|
|
479
|
-
clearTimeout(intervalErrorRetry);
|
|
480
|
-
clearTimeout(loadingTimer);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* 错误重试
|
|
485
|
-
*
|
|
486
|
-
* @param {P} params
|
|
487
|
-
* @return {*}
|
|
488
|
-
*/
|
|
489
|
-
function errorRetry(params: P) {
|
|
490
|
-
// 存在则不进入错误重试
|
|
491
|
-
if (option?.pollingErrorRetryCount || option?.pollingInterval) return;
|
|
492
|
-
// 不存在 或者 次数小于 0 不进入错误重试
|
|
493
|
-
if (!option?.errorRetryCount || option.errorRetryCount <= 0) return;
|
|
494
|
-
|
|
495
|
-
if (!getReadyValue()) return;
|
|
496
|
-
|
|
497
|
-
const poll = async () => {
|
|
498
|
-
try {
|
|
499
|
-
await execService(params);
|
|
500
|
-
cancel();
|
|
501
|
-
} catch (_error) {
|
|
502
|
-
if (option.errorRetryCount && option.errorRetryCount < errorCount) {
|
|
503
|
-
cancel();
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
intervalErrorRetry = setTimeout(poll, option?.errorRetryDelay ?? 0);
|
|
507
|
-
}
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
intervalErrorRetry = setTimeout(poll, option?.errorRetryDelay ?? 0);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
function setData(value: R) {
|
|
514
|
-
data.value = value;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
function setLoading(value: boolean) {
|
|
518
|
-
loading.value = value;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
onMounted(() => {
|
|
522
|
-
// 记录初始 ready 状态
|
|
523
|
-
previousReady = getReadyValue();
|
|
524
|
-
|
|
525
|
-
// 初始化分页配置
|
|
526
|
-
if (option?.usePagination) {
|
|
527
|
-
const config = option.paginationConfig || {};
|
|
528
|
-
pagination.current = config.defaultCurrent ?? 1;
|
|
529
|
-
pagination.pageSize = config.defaultPageSize ?? 10;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
if (!option?.manual && option?.ready !== false) {
|
|
533
|
-
const defaultParams = (option?.defaultParams || []) as P;
|
|
534
|
-
run(...defaultParams);
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
onUnmounted(() => {
|
|
539
|
-
cancel();
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
// 监听 ready 参数变化
|
|
543
|
-
watch(
|
|
544
|
-
() => getReadyValue(),
|
|
545
|
-
(currentReady) => {
|
|
546
|
-
// 只有当 ready 从 false 变为 true 时才执行请求
|
|
547
|
-
if (currentReady && !previousReady) {
|
|
548
|
-
const params = (lastParams || option?.defaultParams || []) as P;
|
|
549
|
-
// run(...params);
|
|
550
|
-
execService(params).catch(() => {
|
|
551
|
-
errorRetry(params);
|
|
552
|
-
});
|
|
553
|
-
} else {
|
|
554
|
-
cancel();
|
|
555
|
-
}
|
|
556
|
-
// 更新 previousReady 状态
|
|
557
|
-
previousReady = !!currentReady;
|
|
558
|
-
},
|
|
559
|
-
);
|
|
560
|
-
|
|
561
|
-
watch(
|
|
562
|
-
() => option?.refreshDeps,
|
|
563
|
-
() => {
|
|
564
|
-
refresh();
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
deep: true,
|
|
568
|
-
},
|
|
569
|
-
);
|
|
570
|
-
|
|
571
|
-
return {
|
|
572
|
-
data,
|
|
573
|
-
setData,
|
|
574
|
-
loading,
|
|
575
|
-
setLoading,
|
|
576
|
-
run,
|
|
577
|
-
runAsync,
|
|
578
|
-
refresh,
|
|
579
|
-
refreshAsync,
|
|
580
|
-
cancel,
|
|
581
|
-
pagination: option?.usePagination ? pagination : undefined,
|
|
582
|
-
list,
|
|
583
|
-
};
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
export default useRequest;
|
|
1
|
+
import {
|
|
2
|
+
onMounted,
|
|
3
|
+
onUnmounted,
|
|
4
|
+
ref,
|
|
5
|
+
shallowReactive,
|
|
6
|
+
watch,
|
|
7
|
+
type Ref,
|
|
8
|
+
} from 'vue';
|
|
9
|
+
import { debounce, throttle } from './utils';
|
|
10
|
+
|
|
11
|
+
// 添加分页响应数据类型
|
|
12
|
+
// type PaginationResponse<T = any> = {
|
|
13
|
+
// list?: T[];
|
|
14
|
+
// data?: T[];
|
|
15
|
+
// total: number;
|
|
16
|
+
// current: number;
|
|
17
|
+
// pageSize: number;
|
|
18
|
+
// };
|
|
19
|
+
|
|
20
|
+
type Option<R, P> = {
|
|
21
|
+
/** 是否需要uni.showLoading */
|
|
22
|
+
useUniLoading?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* 每次 ready 从 false 变为 true 时, 都会自动发起请求
|
|
25
|
+
* 为 false 时,请求永远都不会发出
|
|
26
|
+
*/
|
|
27
|
+
ready?: boolean | Ref<boolean>;
|
|
28
|
+
/** 立即执行 */
|
|
29
|
+
manual?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 默认参数
|
|
32
|
+
* 分页参数在请放入 分页参数配置中
|
|
33
|
+
*/
|
|
34
|
+
defaultParams?: P;
|
|
35
|
+
/** 依赖刷新 */
|
|
36
|
+
refreshDeps?: any[];
|
|
37
|
+
/**
|
|
38
|
+
* 延迟结束loading
|
|
39
|
+
* 单位毫秒
|
|
40
|
+
* 防止请求过快造成渲染闪烁
|
|
41
|
+
*/
|
|
42
|
+
loadingDelay?: number;
|
|
43
|
+
/**
|
|
44
|
+
* 轮询间隔
|
|
45
|
+
* 通过 run/runAsync 来启动轮询
|
|
46
|
+
* 通过 cancel 取消轮询
|
|
47
|
+
*/
|
|
48
|
+
pollingInterval?: number;
|
|
49
|
+
/**
|
|
50
|
+
* 轮询错误重试次数
|
|
51
|
+
*/
|
|
52
|
+
pollingErrorRetryCount?: number;
|
|
53
|
+
/**
|
|
54
|
+
* 错误重试次数
|
|
55
|
+
*/
|
|
56
|
+
errorRetryCount?: number;
|
|
57
|
+
/**
|
|
58
|
+
* 错误重试间隔
|
|
59
|
+
*/
|
|
60
|
+
errorRetryDelay?: number;
|
|
61
|
+
/**
|
|
62
|
+
* 防抖等待时间
|
|
63
|
+
*/
|
|
64
|
+
debounceWait?: number;
|
|
65
|
+
/**
|
|
66
|
+
* 在防抖开始前执行调用
|
|
67
|
+
*/
|
|
68
|
+
debounceLeading?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* 在防抖结束后执行调用
|
|
71
|
+
*/
|
|
72
|
+
debounceTrailing?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* 节流等待时间
|
|
75
|
+
*/
|
|
76
|
+
throttleWait?: number;
|
|
77
|
+
/**
|
|
78
|
+
* 在节流开始前执行调用
|
|
79
|
+
*/
|
|
80
|
+
throttleLeading?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* 在节流结束后执行调用
|
|
83
|
+
*/
|
|
84
|
+
throttleTrailing?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* 使用分页
|
|
87
|
+
*/
|
|
88
|
+
usePagination?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* 分页配置
|
|
91
|
+
*
|
|
92
|
+
*/
|
|
93
|
+
paginationConfig?: {
|
|
94
|
+
/** 当前页码参数名,默认 'current' */
|
|
95
|
+
currentKey?: string;
|
|
96
|
+
/** 每页数量参数名,默认 'pageSize' */
|
|
97
|
+
pageSizeKey?: string;
|
|
98
|
+
/** 总数字段名,默认 'total' */
|
|
99
|
+
totalKey?: string;
|
|
100
|
+
/** 默认当前页,默认 1 */
|
|
101
|
+
defaultCurrent?: number;
|
|
102
|
+
/** 默认每页数量,默认 10 */
|
|
103
|
+
defaultPageSize?: number;
|
|
104
|
+
/** 列表字段名,默认 'list' */
|
|
105
|
+
listKey?: string;
|
|
106
|
+
};
|
|
107
|
+
/** 请求前 */
|
|
108
|
+
onBefore?: () => Promise<void> | void;
|
|
109
|
+
/** 请求成功 */
|
|
110
|
+
onSuccess?: (result: R | undefined, params?: P) => void;
|
|
111
|
+
/** 请求失败 */
|
|
112
|
+
onError?: (error: any) => void;
|
|
113
|
+
/** 请求完成 */
|
|
114
|
+
onFinally?: () => void;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export type Service<R, P extends any[]> = (...args: P) => Promise<R>;
|
|
118
|
+
|
|
119
|
+
const useRequest = <R, P extends any[]>(
|
|
120
|
+
service: Service<R, P>,
|
|
121
|
+
option?: Option<R, P>,
|
|
122
|
+
) => {
|
|
123
|
+
const loading = ref(false);
|
|
124
|
+
|
|
125
|
+
const data = ref<R>();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 启用 usePagination 之后每页数据合并之后的结果
|
|
129
|
+
* 不开启默认为 []
|
|
130
|
+
* 只适合从第一页开始获取数据
|
|
131
|
+
*/
|
|
132
|
+
const list = ref<any[]>([]);
|
|
133
|
+
|
|
134
|
+
const pagination = shallowReactive({
|
|
135
|
+
current: 1,
|
|
136
|
+
pageSize: 10,
|
|
137
|
+
total: 0,
|
|
138
|
+
totalPage: 0,
|
|
139
|
+
hasMore: true,
|
|
140
|
+
// 分页操作方法
|
|
141
|
+
setCurrent: (current: number) => {
|
|
142
|
+
pagination.current = Math.max(1, current);
|
|
143
|
+
refresh();
|
|
144
|
+
},
|
|
145
|
+
setPageSize: (pageSize: number) => {
|
|
146
|
+
pagination.pageSize = pageSize;
|
|
147
|
+
pagination.current = 1; // 重置到第一页
|
|
148
|
+
refresh();
|
|
149
|
+
},
|
|
150
|
+
reset: () => {
|
|
151
|
+
pagination.current = option?.paginationConfig?.defaultCurrent ?? 1;
|
|
152
|
+
pagination.pageSize = option?.paginationConfig?.defaultPageSize ?? 10;
|
|
153
|
+
refresh();
|
|
154
|
+
},
|
|
155
|
+
prev: () => {
|
|
156
|
+
if (pagination.current > 1) {
|
|
157
|
+
pagination.current--;
|
|
158
|
+
refresh();
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
next: () => {
|
|
162
|
+
if (pagination.current < pagination.totalPage) {
|
|
163
|
+
pagination.current++;
|
|
164
|
+
refresh();
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
change: (current: number, pageSize: number) => {
|
|
168
|
+
pagination.current = current;
|
|
169
|
+
pagination.pageSize = pageSize;
|
|
170
|
+
refresh();
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
/** 最后一次请求的参数 */
|
|
175
|
+
let lastParams: P | undefined;
|
|
176
|
+
|
|
177
|
+
let loadingTimer: any;
|
|
178
|
+
|
|
179
|
+
let intervalTimer: any;
|
|
180
|
+
|
|
181
|
+
let allowPolling = true;
|
|
182
|
+
|
|
183
|
+
let pollingErrorCount = 0;
|
|
184
|
+
|
|
185
|
+
let errorCount = 0;
|
|
186
|
+
|
|
187
|
+
let intervalErrorRetry: ReturnType<typeof setTimeout> | undefined = undefined;
|
|
188
|
+
|
|
189
|
+
/** 记录上次 ready 的状态 */
|
|
190
|
+
let previousReady = false;
|
|
191
|
+
|
|
192
|
+
/** 获取 ready 值 */
|
|
193
|
+
const getReadyValue = (): boolean => {
|
|
194
|
+
if (option?.ready === undefined) return true;
|
|
195
|
+
return typeof option.ready === 'boolean'
|
|
196
|
+
? option.ready
|
|
197
|
+
: option.ready.value;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
*
|
|
202
|
+
* @param params 请求参数
|
|
203
|
+
*/
|
|
204
|
+
function run(...params: P) {
|
|
205
|
+
if (getReadyValue()) {
|
|
206
|
+
execServiceFunction(params)
|
|
207
|
+
?.catch((error) => {
|
|
208
|
+
errorRetry(params);
|
|
209
|
+
throw error;
|
|
210
|
+
})
|
|
211
|
+
?.finally(() => {
|
|
212
|
+
if (option?.pollingInterval && option.pollingInterval > 0) {
|
|
213
|
+
startPolling(params);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function runAsync(...params: P) {
|
|
220
|
+
// 只有在 ready 为 true 时才执行请求
|
|
221
|
+
if (getReadyValue()) {
|
|
222
|
+
try {
|
|
223
|
+
const result = await execServiceFunction?.(params);
|
|
224
|
+
|
|
225
|
+
return result;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
errorRetry(params);
|
|
228
|
+
throw error;
|
|
229
|
+
} finally {
|
|
230
|
+
if (option?.pollingInterval && option.pollingInterval > 0) {
|
|
231
|
+
startPolling(params);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 刷新
|
|
239
|
+
*
|
|
240
|
+
*/
|
|
241
|
+
function refresh() {
|
|
242
|
+
// 只有在 ready 为 true 时才执行请求
|
|
243
|
+
if (getReadyValue()) {
|
|
244
|
+
const fallbackParams: P = ((lastParams || option?.defaultParams) ??
|
|
245
|
+
[]) as P;
|
|
246
|
+
|
|
247
|
+
execServiceFunction(fallbackParams);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* 刷新异步
|
|
253
|
+
*
|
|
254
|
+
* @return {*}
|
|
255
|
+
*/
|
|
256
|
+
function refreshAsync() {
|
|
257
|
+
// 只有在 ready 为 true 时才执行请求
|
|
258
|
+
if (getReadyValue()) {
|
|
259
|
+
const fallbackParams: P = ((lastParams || option?.defaultParams) ??
|
|
260
|
+
[]) as P;
|
|
261
|
+
|
|
262
|
+
return execServiceFunction(fallbackParams);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function execService(params: P) {
|
|
267
|
+
// 只有在 ready 为 true 时才执行请求
|
|
268
|
+
if (getReadyValue()) {
|
|
269
|
+
try {
|
|
270
|
+
loading.value = true;
|
|
271
|
+
|
|
272
|
+
if (option?.useUniLoading) {
|
|
273
|
+
uni.showLoading({
|
|
274
|
+
title: '加载中...',
|
|
275
|
+
mask: true,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
lastParams = params;
|
|
280
|
+
|
|
281
|
+
await option?.onBefore?.();
|
|
282
|
+
|
|
283
|
+
const result = await service(...params);
|
|
284
|
+
|
|
285
|
+
data.value = result;
|
|
286
|
+
|
|
287
|
+
// 如果启用了分页功能,处理分页数据
|
|
288
|
+
if (option?.usePagination) {
|
|
289
|
+
const config = option.paginationConfig || {};
|
|
290
|
+
const currentKey = config.currentKey || 'current';
|
|
291
|
+
const pageSizeKey = config.pageSizeKey || 'pageSize';
|
|
292
|
+
const totalKey = config.totalKey || 'total';
|
|
293
|
+
const listKey = config.listKey || 'list';
|
|
294
|
+
|
|
295
|
+
// 检查结果是否为分页格式
|
|
296
|
+
if (result && typeof result === 'object') {
|
|
297
|
+
const paginationResult = result as any;
|
|
298
|
+
pagination.current =
|
|
299
|
+
paginationResult[currentKey] || pagination.current;
|
|
300
|
+
pagination.pageSize =
|
|
301
|
+
paginationResult[pageSizeKey] || pagination.pageSize;
|
|
302
|
+
pagination.total = paginationResult[totalKey] || 0;
|
|
303
|
+
pagination.totalPage = Math.ceil(
|
|
304
|
+
pagination.total / pagination.pageSize,
|
|
305
|
+
);
|
|
306
|
+
pagination.hasMore = pagination.current < pagination.totalPage;
|
|
307
|
+
|
|
308
|
+
// 获取列表数据
|
|
309
|
+
const resultData =
|
|
310
|
+
paginationResult[listKey] ||
|
|
311
|
+
paginationResult.data ||
|
|
312
|
+
paginationResult.list ||
|
|
313
|
+
[];
|
|
314
|
+
// 根据当前页码决定是否合并列表数据
|
|
315
|
+
if (pagination.current === 1) {
|
|
316
|
+
// 第一页时,替换列表
|
|
317
|
+
list.value = [...resultData];
|
|
318
|
+
} else {
|
|
319
|
+
// 非第一页时,合并列表
|
|
320
|
+
list.value = [...list.value, ...resultData];
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
pagination.hasMore = false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
option?.onSuccess?.(data.value, params);
|
|
328
|
+
|
|
329
|
+
return data.value;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
pollingErrorCount++;
|
|
332
|
+
errorCount++;
|
|
333
|
+
option?.onError?.(error);
|
|
334
|
+
throw error;
|
|
335
|
+
} finally {
|
|
336
|
+
option?.onFinally?.();
|
|
337
|
+
|
|
338
|
+
const finishLoading = () => {
|
|
339
|
+
loading.value = false;
|
|
340
|
+
if (option?.useUniLoading) {
|
|
341
|
+
uni.hideLoading();
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// 结束loading
|
|
346
|
+
if (
|
|
347
|
+
option?.useUniLoading &&
|
|
348
|
+
option?.loadingDelay &&
|
|
349
|
+
option.loadingDelay > 0
|
|
350
|
+
) {
|
|
351
|
+
clearTimeout(loadingTimer);
|
|
352
|
+
loadingTimer = setTimeout(finishLoading, option.loadingDelay);
|
|
353
|
+
} else {
|
|
354
|
+
finishLoading();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const execServiceDebounce = debounce(execService, option?.debounceWait ?? 0, {
|
|
361
|
+
leading: option?.debounceLeading,
|
|
362
|
+
trailing: option?.debounceTrailing,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const execServiceThrottle = throttle(execService, option?.throttleWait ?? 0, {
|
|
366
|
+
leading: option?.throttleLeading,
|
|
367
|
+
trailing: option?.throttleTrailing,
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
function execServiceFunction(params: P) {
|
|
371
|
+
if (option?.usePagination) {
|
|
372
|
+
const config = option.paginationConfig || {};
|
|
373
|
+
const currentKey = config.currentKey || 'current';
|
|
374
|
+
const pageSizeKey = config.pageSizeKey || 'pageSize';
|
|
375
|
+
|
|
376
|
+
// 将分页参数注入到请求参数中
|
|
377
|
+
const paramsWithPagination = [...params] as any[];
|
|
378
|
+
|
|
379
|
+
// 如果参数是对象,添加分页字段
|
|
380
|
+
if (
|
|
381
|
+
paramsWithPagination.length > 0 &&
|
|
382
|
+
typeof paramsWithPagination[0] === 'object' &&
|
|
383
|
+
paramsWithPagination[0] !== null
|
|
384
|
+
) {
|
|
385
|
+
paramsWithPagination[0] = {
|
|
386
|
+
...paramsWithPagination[0],
|
|
387
|
+
[currentKey]: pagination.current,
|
|
388
|
+
[pageSizeKey]: pagination.pageSize,
|
|
389
|
+
};
|
|
390
|
+
} else {
|
|
391
|
+
// 如果没有参数或参数不是对象,创建一个包含分页信息的对象
|
|
392
|
+
paramsWithPagination.unshift({
|
|
393
|
+
[currentKey]: pagination.current,
|
|
394
|
+
[pageSizeKey]: pagination.pageSize,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (option?.debounceWait) {
|
|
399
|
+
return execServiceDebounce(paramsWithPagination as P);
|
|
400
|
+
} else if (option?.throttleWait) {
|
|
401
|
+
return execServiceThrottle(paramsWithPagination as P);
|
|
402
|
+
} else {
|
|
403
|
+
return execService(paramsWithPagination as P);
|
|
404
|
+
}
|
|
405
|
+
} else {
|
|
406
|
+
if (option?.debounceWait) {
|
|
407
|
+
return execServiceDebounce(params);
|
|
408
|
+
} else if (option?.throttleWait) {
|
|
409
|
+
return execServiceThrottle(params);
|
|
410
|
+
} else {
|
|
411
|
+
return execService(params);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* 开启轮询
|
|
418
|
+
*
|
|
419
|
+
* @param {P} params
|
|
420
|
+
*/
|
|
421
|
+
function startPolling(params: P) {
|
|
422
|
+
cancel();
|
|
423
|
+
|
|
424
|
+
allowPolling = true;
|
|
425
|
+
|
|
426
|
+
if (!getReadyValue()) return;
|
|
427
|
+
|
|
428
|
+
// 使用递归函数实现轮询,确保请求完成后才开始计时下一次请求
|
|
429
|
+
const poll = async () => {
|
|
430
|
+
if (!allowPolling) return;
|
|
431
|
+
// 检查 ready 状态
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
await execService(params);
|
|
435
|
+
|
|
436
|
+
// 检查错误重试次数限制
|
|
437
|
+
if (
|
|
438
|
+
option?.pollingErrorRetryCount &&
|
|
439
|
+
pollingErrorCount >= option.pollingErrorRetryCount
|
|
440
|
+
) {
|
|
441
|
+
cancel();
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// 如果仍然允许轮询,则设置定时器等待下次轮询
|
|
446
|
+
if (allowPolling) {
|
|
447
|
+
intervalTimer = setTimeout(poll, option?.pollingInterval);
|
|
448
|
+
}
|
|
449
|
+
} catch (_error) {
|
|
450
|
+
// execService 内部已经处理了错误,这里只需要确保继续轮询或停止
|
|
451
|
+
if (
|
|
452
|
+
option?.pollingErrorRetryCount &&
|
|
453
|
+
pollingErrorCount >= option.pollingErrorRetryCount
|
|
454
|
+
) {
|
|
455
|
+
cancel();
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// 如果仍然允许轮询,则设置定时器等待下次轮询
|
|
460
|
+
if (allowPolling) {
|
|
461
|
+
intervalTimer = setTimeout(poll, option?.pollingInterval);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
// 先等待轮询间隔时间,然后再开始执行请求
|
|
467
|
+
intervalTimer = setTimeout(() => {
|
|
468
|
+
// 开始轮询
|
|
469
|
+
poll();
|
|
470
|
+
}, option?.pollingInterval);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** 取消轮询/重试 */
|
|
474
|
+
function cancel() {
|
|
475
|
+
allowPolling = false;
|
|
476
|
+
pollingErrorCount = 0;
|
|
477
|
+
errorCount = 0;
|
|
478
|
+
clearTimeout(intervalTimer);
|
|
479
|
+
clearTimeout(intervalErrorRetry);
|
|
480
|
+
clearTimeout(loadingTimer);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* 错误重试
|
|
485
|
+
*
|
|
486
|
+
* @param {P} params
|
|
487
|
+
* @return {*}
|
|
488
|
+
*/
|
|
489
|
+
function errorRetry(params: P) {
|
|
490
|
+
// 存在则不进入错误重试
|
|
491
|
+
if (option?.pollingErrorRetryCount || option?.pollingInterval) return;
|
|
492
|
+
// 不存在 或者 次数小于 0 不进入错误重试
|
|
493
|
+
if (!option?.errorRetryCount || option.errorRetryCount <= 0) return;
|
|
494
|
+
|
|
495
|
+
if (!getReadyValue()) return;
|
|
496
|
+
|
|
497
|
+
const poll = async () => {
|
|
498
|
+
try {
|
|
499
|
+
await execService(params);
|
|
500
|
+
cancel();
|
|
501
|
+
} catch (_error) {
|
|
502
|
+
if (option.errorRetryCount && option.errorRetryCount < errorCount) {
|
|
503
|
+
cancel();
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
intervalErrorRetry = setTimeout(poll, option?.errorRetryDelay ?? 0);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
intervalErrorRetry = setTimeout(poll, option?.errorRetryDelay ?? 0);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function setData(value: R) {
|
|
514
|
+
data.value = value;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function setLoading(value: boolean) {
|
|
518
|
+
loading.value = value;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
onMounted(() => {
|
|
522
|
+
// 记录初始 ready 状态
|
|
523
|
+
previousReady = getReadyValue();
|
|
524
|
+
|
|
525
|
+
// 初始化分页配置
|
|
526
|
+
if (option?.usePagination) {
|
|
527
|
+
const config = option.paginationConfig || {};
|
|
528
|
+
pagination.current = config.defaultCurrent ?? 1;
|
|
529
|
+
pagination.pageSize = config.defaultPageSize ?? 10;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (!option?.manual && option?.ready !== false) {
|
|
533
|
+
const defaultParams = (option?.defaultParams || []) as P;
|
|
534
|
+
run(...defaultParams);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
onUnmounted(() => {
|
|
539
|
+
cancel();
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// 监听 ready 参数变化
|
|
543
|
+
watch(
|
|
544
|
+
() => getReadyValue(),
|
|
545
|
+
(currentReady) => {
|
|
546
|
+
// 只有当 ready 从 false 变为 true 时才执行请求
|
|
547
|
+
if (currentReady && !previousReady) {
|
|
548
|
+
const params = (lastParams || option?.defaultParams || []) as P;
|
|
549
|
+
// run(...params);
|
|
550
|
+
execService(params).catch(() => {
|
|
551
|
+
errorRetry(params);
|
|
552
|
+
});
|
|
553
|
+
} else {
|
|
554
|
+
cancel();
|
|
555
|
+
}
|
|
556
|
+
// 更新 previousReady 状态
|
|
557
|
+
previousReady = !!currentReady;
|
|
558
|
+
},
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
watch(
|
|
562
|
+
() => option?.refreshDeps,
|
|
563
|
+
() => {
|
|
564
|
+
refresh();
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
deep: true,
|
|
568
|
+
},
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
data,
|
|
573
|
+
setData,
|
|
574
|
+
loading,
|
|
575
|
+
setLoading,
|
|
576
|
+
run,
|
|
577
|
+
runAsync,
|
|
578
|
+
refresh,
|
|
579
|
+
refreshAsync,
|
|
580
|
+
cancel,
|
|
581
|
+
pagination: option?.usePagination ? pagination : undefined,
|
|
582
|
+
list,
|
|
583
|
+
};
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
export default useRequest;
|