@yh-ui/request 0.1.21
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/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/adapters/fetch.cjs +157 -0
- package/dist/adapters/fetch.d.ts +25 -0
- package/dist/adapters/fetch.mjs +148 -0
- package/dist/adapters/index.cjs +27 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.mjs +2 -0
- package/dist/adapters/platform.cjs +394 -0
- package/dist/adapters/platform.d.ts +72 -0
- package/dist/adapters/platform.mjs +369 -0
- package/dist/cache/index.cjs +56 -0
- package/dist/cache/index.d.ts +21 -0
- package/dist/cache/index.mjs +14 -0
- package/dist/cache/indexedDB.cjs +188 -0
- package/dist/cache/indexedDB.d.ts +58 -0
- package/dist/cache/indexedDB.mjs +176 -0
- package/dist/cache/localStorage.cjs +158 -0
- package/dist/cache/localStorage.d.ts +58 -0
- package/dist/cache/localStorage.mjs +153 -0
- package/dist/cache/memory.cjs +112 -0
- package/dist/cache/memory.d.ts +71 -0
- package/dist/cache/memory.mjs +103 -0
- package/dist/graphql.cjs +255 -0
- package/dist/graphql.d.ts +192 -0
- package/dist/graphql.mjs +235 -0
- package/dist/http-cache.cjs +248 -0
- package/dist/http-cache.d.ts +156 -0
- package/dist/http-cache.mjs +233 -0
- package/dist/index.cjs +181 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.mjs +16 -0
- package/dist/interceptors/debug.cjs +139 -0
- package/dist/interceptors/debug.d.ts +92 -0
- package/dist/interceptors/debug.mjs +130 -0
- package/dist/interceptors/index.cjs +38 -0
- package/dist/interceptors/index.d.ts +6 -0
- package/dist/interceptors/index.mjs +3 -0
- package/dist/interceptors/progress.cjs +185 -0
- package/dist/interceptors/progress.d.ts +97 -0
- package/dist/interceptors/progress.mjs +177 -0
- package/dist/interceptors/security.cjs +154 -0
- package/dist/interceptors/security.d.ts +83 -0
- package/dist/interceptors/security.mjs +134 -0
- package/dist/plugin.cjs +166 -0
- package/dist/plugin.d.ts +106 -0
- package/dist/plugin.mjs +163 -0
- package/dist/request.cjs +396 -0
- package/dist/request.d.ts +111 -0
- package/dist/request.mjs +339 -0
- package/dist/types.cjs +13 -0
- package/dist/types.d.ts +157 -0
- package/dist/types.mjs +7 -0
- package/dist/useAIStream.cjs +125 -0
- package/dist/useAIStream.d.ts +89 -0
- package/dist/useAIStream.mjs +108 -0
- package/dist/useLoadMore.cjs +136 -0
- package/dist/useLoadMore.d.ts +84 -0
- package/dist/useLoadMore.mjs +134 -0
- package/dist/usePagination.cjs +141 -0
- package/dist/usePagination.d.ts +89 -0
- package/dist/usePagination.mjs +132 -0
- package/dist/useQueue.cjs +243 -0
- package/dist/useQueue.d.ts +118 -0
- package/dist/useQueue.mjs +239 -0
- package/dist/useRequest.cjs +325 -0
- package/dist/useRequest.d.ts +126 -0
- package/dist/useRequest.mjs +329 -0
- package/dist/useRequestQueue.cjs +36 -0
- package/dist/useRequestQueue.d.ts +52 -0
- package/dist/useRequestQueue.mjs +27 -0
- package/dist/useSSE.cjs +241 -0
- package/dist/useSSE.d.ts +74 -0
- package/dist/useSSE.mjs +226 -0
- package/dist/websocket.cjs +325 -0
- package/dist/websocket.d.ts +163 -0
- package/dist/websocket.mjs +316 -0
- package/package.json +61 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type Ref, type ShallowRef } from 'vue';
|
|
2
|
+
import { type RequestResponse } from './types';
|
|
3
|
+
/** 分页参数 */
|
|
4
|
+
export interface PaginationOptions {
|
|
5
|
+
/** 当前页码 */
|
|
6
|
+
current?: number;
|
|
7
|
+
/** 每页条数 */
|
|
8
|
+
pageSize?: number;
|
|
9
|
+
/** 总数 */
|
|
10
|
+
total?: number;
|
|
11
|
+
/** 总页数 */
|
|
12
|
+
totalPages?: number;
|
|
13
|
+
}
|
|
14
|
+
/** 分页 Hook 配置 */
|
|
15
|
+
export interface UsePaginationOptions<TData, TParams extends unknown[] = unknown[]> {
|
|
16
|
+
/** 请求函数(当作为第一个参数传入时可省略) */
|
|
17
|
+
service?: (...args: TParams) => Promise<RequestResponse<TData>>;
|
|
18
|
+
/** 默认分页配置 */
|
|
19
|
+
defaultPagination?: PaginationOptions;
|
|
20
|
+
/** 请求实例 */
|
|
21
|
+
request?: Request;
|
|
22
|
+
/** 成功回调 */
|
|
23
|
+
onSuccess?: (data: TData, params: TParams) => void;
|
|
24
|
+
/** 错误回调 */
|
|
25
|
+
onError?: (error: unknown, params: TParams) => void;
|
|
26
|
+
/** 完成后回调 */
|
|
27
|
+
onFinally?: (params: TParams) => void;
|
|
28
|
+
/** 是否手动触发 */
|
|
29
|
+
manual?: boolean;
|
|
30
|
+
/** 默认参数 */
|
|
31
|
+
defaultParams?: TParams;
|
|
32
|
+
}
|
|
33
|
+
/** 分页 Hook 返回值 */
|
|
34
|
+
export interface UsePaginationReturn<TData, TParams extends unknown[] = unknown[]> {
|
|
35
|
+
/** 当前页码 */
|
|
36
|
+
current: Ref<number>;
|
|
37
|
+
/** 每页条数 */
|
|
38
|
+
pageSize: Ref<number>;
|
|
39
|
+
/** 总数 */
|
|
40
|
+
total: Ref<number>;
|
|
41
|
+
/** 总页数 */
|
|
42
|
+
totalPages: Ref<number>;
|
|
43
|
+
/** 数据 */
|
|
44
|
+
data: ShallowRef<TData | undefined>;
|
|
45
|
+
/** 加载状态 */
|
|
46
|
+
loading: Ref<boolean>;
|
|
47
|
+
/** 刷新状态 */
|
|
48
|
+
refreshing: Ref<boolean>;
|
|
49
|
+
/** 请求参数 */
|
|
50
|
+
params: Ref<TParams>;
|
|
51
|
+
/** 错误 */
|
|
52
|
+
error: ShallowRef<unknown>;
|
|
53
|
+
/** 是否还有更多 */
|
|
54
|
+
noMore: Ref<boolean>;
|
|
55
|
+
/** 加载中 */
|
|
56
|
+
loadingMore: Ref<boolean>;
|
|
57
|
+
/** 分页方法 */
|
|
58
|
+
pagination: {
|
|
59
|
+
/** 加载指定页 */
|
|
60
|
+
loadPage: (page: number) => Promise<void>;
|
|
61
|
+
/** 下一页 */
|
|
62
|
+
nextPage: () => Promise<void>;
|
|
63
|
+
/** 上一页 */
|
|
64
|
+
prevPage: () => Promise<void>;
|
|
65
|
+
/** 首页 */
|
|
66
|
+
firstPage: () => Promise<void>;
|
|
67
|
+
/** 末页 */
|
|
68
|
+
lastPage: () => Promise<void>;
|
|
69
|
+
/** 刷新 */
|
|
70
|
+
refresh: () => Promise<void>;
|
|
71
|
+
/** 设置每页条数 */
|
|
72
|
+
setPageSize: (size: number) => void;
|
|
73
|
+
/** 设置总数 */
|
|
74
|
+
setTotal: (total: number) => void;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* usePagination - 分页请求 Hook
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* const {
|
|
82
|
+
* current, pageSize, total, data, loading,
|
|
83
|
+
* pagination: { loadPage, nextPage, prevPage, refresh }
|
|
84
|
+
* } = usePagination(
|
|
85
|
+
* (page, size) => request.get('/api/list', { params: { page, size } }),
|
|
86
|
+
* { defaultPagination: { current: 1, pageSize: 10 } }
|
|
87
|
+
* )
|
|
88
|
+
*/
|
|
89
|
+
export declare function usePagination<TData = unknown, TParams extends unknown[] = [number, number]>(service: (current: number, pageSize: number, ...args: unknown[]) => Promise<RequestResponse<TData>>, options?: UsePaginationOptions<TData, TParams>): UsePaginationReturn<TData, TParams>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { ref, shallowRef, computed, watch, onMounted } from "vue";
|
|
2
|
+
export function usePagination(service, options = {}) {
|
|
3
|
+
const {
|
|
4
|
+
defaultPagination = { current: 1, pageSize: 10 },
|
|
5
|
+
onSuccess,
|
|
6
|
+
onError,
|
|
7
|
+
onFinally,
|
|
8
|
+
manual = false,
|
|
9
|
+
defaultParams = []
|
|
10
|
+
} = options;
|
|
11
|
+
const current = ref(defaultPagination.current || 1);
|
|
12
|
+
const pageSize = ref(defaultPagination.pageSize || 10);
|
|
13
|
+
const total = ref(defaultPagination.total || 0);
|
|
14
|
+
const totalPages = computed(() => {
|
|
15
|
+
if (total.value <= 0) return 0;
|
|
16
|
+
return Math.ceil(total.value / pageSize.value);
|
|
17
|
+
});
|
|
18
|
+
const noMore = computed(() => {
|
|
19
|
+
return current.value >= totalPages.value && total.value > 0;
|
|
20
|
+
});
|
|
21
|
+
const data = shallowRef(void 0);
|
|
22
|
+
const loading = ref(false);
|
|
23
|
+
const refreshing = ref(false);
|
|
24
|
+
const loadingMore = ref(false);
|
|
25
|
+
const error = shallowRef(void 0);
|
|
26
|
+
const params = ref(defaultParams);
|
|
27
|
+
const loadData = async () => {
|
|
28
|
+
if (loading.value) return;
|
|
29
|
+
loading.value = true;
|
|
30
|
+
error.value = void 0;
|
|
31
|
+
try {
|
|
32
|
+
const page = current.value;
|
|
33
|
+
const size = pageSize.value;
|
|
34
|
+
const extraParams = params.value.slice(2);
|
|
35
|
+
const response = await service(page, size, ...extraParams);
|
|
36
|
+
const pageData = response.data;
|
|
37
|
+
if (pageData && typeof pageData === "object") {
|
|
38
|
+
const paginatedData = pageData;
|
|
39
|
+
if (typeof paginatedData.total !== "undefined") {
|
|
40
|
+
total.value = paginatedData.total;
|
|
41
|
+
}
|
|
42
|
+
if (typeof paginatedData.totalCount !== "undefined") {
|
|
43
|
+
total.value = paginatedData.totalCount;
|
|
44
|
+
}
|
|
45
|
+
if (typeof paginatedData.totalElements !== "undefined") {
|
|
46
|
+
total.value = paginatedData.totalElements;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
data.value = pageData;
|
|
50
|
+
onSuccess?.(pageData, params.value);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
error.value = err;
|
|
53
|
+
onError?.(err, params.value);
|
|
54
|
+
} finally {
|
|
55
|
+
loading.value = false;
|
|
56
|
+
refreshing.value = false;
|
|
57
|
+
loadingMore.value = false;
|
|
58
|
+
onFinally?.(params.value);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const loadPage = async (page) => {
|
|
62
|
+
if (page < 1 || totalPages.value > 0 && page > totalPages.value) return;
|
|
63
|
+
current.value = page;
|
|
64
|
+
await loadData();
|
|
65
|
+
};
|
|
66
|
+
const nextPage = async () => {
|
|
67
|
+
if (noMore.value) return;
|
|
68
|
+
await loadPage(current.value + 1);
|
|
69
|
+
};
|
|
70
|
+
const prevPage = async () => {
|
|
71
|
+
await loadPage(current.value - 1);
|
|
72
|
+
};
|
|
73
|
+
const firstPage = async () => {
|
|
74
|
+
await loadPage(1);
|
|
75
|
+
};
|
|
76
|
+
const lastPage = async () => {
|
|
77
|
+
await loadPage(totalPages.value);
|
|
78
|
+
};
|
|
79
|
+
const refresh = async () => {
|
|
80
|
+
current.value = 1;
|
|
81
|
+
refreshing.value = true;
|
|
82
|
+
await loadData();
|
|
83
|
+
};
|
|
84
|
+
const setPageSize = (size) => {
|
|
85
|
+
pageSize.value = size;
|
|
86
|
+
if (!manual) {
|
|
87
|
+
refresh();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const setTotal = (newTotal) => {
|
|
91
|
+
total.value = newTotal;
|
|
92
|
+
};
|
|
93
|
+
watch(pageSize, () => {
|
|
94
|
+
if (!manual) {
|
|
95
|
+
current.value = 1;
|
|
96
|
+
loadData();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
watch(current, () => {
|
|
100
|
+
if (!manual) {
|
|
101
|
+
loadData();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
if (!manual) {
|
|
105
|
+
onMounted(() => {
|
|
106
|
+
loadData();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
current,
|
|
111
|
+
pageSize,
|
|
112
|
+
total,
|
|
113
|
+
totalPages,
|
|
114
|
+
data,
|
|
115
|
+
loading,
|
|
116
|
+
refreshing,
|
|
117
|
+
loadingMore,
|
|
118
|
+
error,
|
|
119
|
+
params,
|
|
120
|
+
noMore,
|
|
121
|
+
pagination: {
|
|
122
|
+
loadPage,
|
|
123
|
+
nextPage,
|
|
124
|
+
prevPage,
|
|
125
|
+
firstPage,
|
|
126
|
+
lastPage,
|
|
127
|
+
refresh,
|
|
128
|
+
setPageSize,
|
|
129
|
+
setTotal
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useQueue = useQueue;
|
|
7
|
+
var _vue = require("vue");
|
|
8
|
+
let taskIdCounter = 0;
|
|
9
|
+
function generateTaskId() {
|
|
10
|
+
return `task_${Date.now()}_${++taskIdCounter}`;
|
|
11
|
+
}
|
|
12
|
+
function useQueue(options = {}) {
|
|
13
|
+
const {
|
|
14
|
+
concurrency = 1,
|
|
15
|
+
autoStart = false,
|
|
16
|
+
continueOnError = false,
|
|
17
|
+
onTaskComplete,
|
|
18
|
+
onTaskError,
|
|
19
|
+
onAllComplete
|
|
20
|
+
} = options;
|
|
21
|
+
const taskList = (0, _vue.reactive)([]);
|
|
22
|
+
const taskMap = /* @__PURE__ */new Map();
|
|
23
|
+
const isRunning = (0, _vue.ref)(false);
|
|
24
|
+
const isPaused = (0, _vue.ref)(false);
|
|
25
|
+
const currentTaskIds = /* @__PURE__ */new Set();
|
|
26
|
+
const version = (0, _vue.ref)(0);
|
|
27
|
+
const triggerUpdate = () => {
|
|
28
|
+
version.value++;
|
|
29
|
+
};
|
|
30
|
+
const tasks = (0, _vue.computed)(() => {
|
|
31
|
+
void version.value;
|
|
32
|
+
return [...taskList];
|
|
33
|
+
});
|
|
34
|
+
const pendingTasks = (0, _vue.computed)(() => tasks.value.filter(t => t.status === "pending").sort((a, b) => (b.priority || 0) - (a.priority || 0)));
|
|
35
|
+
const runningTasks = (0, _vue.computed)(() => tasks.value.filter(t => t.status === "running"));
|
|
36
|
+
const completedTasks = (0, _vue.computed)(() => tasks.value.filter(t => t.status === "fulfilled"));
|
|
37
|
+
const failedTasks = (0, _vue.computed)(() => tasks.value.filter(t => t.status === "rejected"));
|
|
38
|
+
const isEmpty = (0, _vue.computed)(() => tasks.value.length === 0);
|
|
39
|
+
const isAllComplete = (0, _vue.computed)(() => {
|
|
40
|
+
if (tasks.value.length === 0) return false;
|
|
41
|
+
return tasks.value.every(t => t.status === "fulfilled" || t.status === "rejected");
|
|
42
|
+
});
|
|
43
|
+
const completedCount = (0, _vue.computed)(() => completedTasks.value.length);
|
|
44
|
+
const totalCount = (0, _vue.computed)(() => tasks.value.length);
|
|
45
|
+
const executeTask = async task => {
|
|
46
|
+
if (task.status === "canceled" || task.status === "running") return;
|
|
47
|
+
task.status = "running";
|
|
48
|
+
task.startTime = Date.now();
|
|
49
|
+
currentTaskIds.add(task.id);
|
|
50
|
+
triggerUpdate();
|
|
51
|
+
try {
|
|
52
|
+
if (task.delay && task.delay > 0) {
|
|
53
|
+
await new Promise(resolve => setTimeout(resolve, task.delay));
|
|
54
|
+
}
|
|
55
|
+
if (task.status === "canceled") return;
|
|
56
|
+
const result = await task.task();
|
|
57
|
+
if (task.status === "canceled") return;
|
|
58
|
+
task.status = "fulfilled";
|
|
59
|
+
task.result = result;
|
|
60
|
+
task.endTime = Date.now();
|
|
61
|
+
triggerUpdate();
|
|
62
|
+
onTaskComplete?.(task);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
task.status = "rejected";
|
|
65
|
+
task.error = error instanceof Error ? error : new Error(String(error));
|
|
66
|
+
task.endTime = Date.now();
|
|
67
|
+
triggerUpdate();
|
|
68
|
+
onTaskError?.(task, task.error);
|
|
69
|
+
if (!continueOnError) {
|
|
70
|
+
pause();
|
|
71
|
+
}
|
|
72
|
+
} finally {
|
|
73
|
+
currentTaskIds.delete(task.id);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const processQueue = () => {
|
|
77
|
+
if (isPaused.value || !isRunning.value) return;
|
|
78
|
+
let launched = 0;
|
|
79
|
+
const available = concurrency - currentTaskIds.size;
|
|
80
|
+
if (available <= 0) return;
|
|
81
|
+
const pending = taskList.filter(t => t.status === "pending").sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
82
|
+
for (let i = 0; i < pending.length && launched < available; i++) {
|
|
83
|
+
const nextTask = pending[i];
|
|
84
|
+
if (nextTask) {
|
|
85
|
+
launched++;
|
|
86
|
+
executeTask(nextTask).finally(() => {
|
|
87
|
+
if (isRunning.value && !isPaused.value) {
|
|
88
|
+
setTimeout(processQueue, 0);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (currentTaskIds.size === 0 && pending.length === 0 && isRunning.value) {
|
|
94
|
+
isRunning.value = false;
|
|
95
|
+
onAllComplete?.(tasks.value);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const add = (task, addOptions = {}) => {
|
|
99
|
+
const id = generateTaskId();
|
|
100
|
+
const newTask = {
|
|
101
|
+
id,
|
|
102
|
+
key: addOptions.key,
|
|
103
|
+
task,
|
|
104
|
+
priority: addOptions.priority,
|
|
105
|
+
delay: addOptions.delay,
|
|
106
|
+
metadata: addOptions.metadata,
|
|
107
|
+
status: "pending",
|
|
108
|
+
createdAt: Date.now()
|
|
109
|
+
};
|
|
110
|
+
taskMap.set(id, newTask);
|
|
111
|
+
taskList.push(newTask);
|
|
112
|
+
triggerUpdate();
|
|
113
|
+
if (autoStart && !isRunning.value) {
|
|
114
|
+
start();
|
|
115
|
+
} else if (isRunning.value && !isPaused.value) {
|
|
116
|
+
setTimeout(processQueue, 0);
|
|
117
|
+
}
|
|
118
|
+
return id;
|
|
119
|
+
};
|
|
120
|
+
const remove = taskId => {
|
|
121
|
+
const task = taskMap.get(taskId);
|
|
122
|
+
if (task && task.status === "pending") {
|
|
123
|
+
task.status = "canceled";
|
|
124
|
+
taskMap.delete(taskId);
|
|
125
|
+
const index = taskList.findIndex(t => t.id === taskId);
|
|
126
|
+
if (index > -1) taskList.splice(index, 1);
|
|
127
|
+
triggerUpdate();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const clear = () => {
|
|
131
|
+
taskMap.forEach(task => {
|
|
132
|
+
if (task.status === "pending" || task.status === "running") {
|
|
133
|
+
task.status = "canceled";
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
taskMap.clear();
|
|
137
|
+
taskList.length = 0;
|
|
138
|
+
currentTaskIds.clear();
|
|
139
|
+
isRunning.value = false;
|
|
140
|
+
triggerUpdate();
|
|
141
|
+
};
|
|
142
|
+
const start = () => {
|
|
143
|
+
if (isRunning.value) return;
|
|
144
|
+
isRunning.value = true;
|
|
145
|
+
isPaused.value = false;
|
|
146
|
+
processQueue();
|
|
147
|
+
};
|
|
148
|
+
const pause = () => {
|
|
149
|
+
isPaused.value = true;
|
|
150
|
+
};
|
|
151
|
+
const resume = () => {
|
|
152
|
+
isPaused.value = false;
|
|
153
|
+
if (!isRunning.value) {
|
|
154
|
+
start();
|
|
155
|
+
} else {
|
|
156
|
+
processQueue();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const cancel = taskId => {
|
|
160
|
+
const task = taskMap.get(taskId);
|
|
161
|
+
if (task) {
|
|
162
|
+
if (task.status === "pending") {
|
|
163
|
+
task.status = "canceled";
|
|
164
|
+
taskMap.delete(taskId);
|
|
165
|
+
const index = taskList.findIndex(t => t.id === taskId);
|
|
166
|
+
if (index > -1) taskList.splice(index, 1);
|
|
167
|
+
triggerUpdate();
|
|
168
|
+
} else if (task.status === "running") {
|
|
169
|
+
task.status = "canceled";
|
|
170
|
+
triggerUpdate();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const cancelAll = () => {
|
|
175
|
+
taskMap.forEach(task => {
|
|
176
|
+
if (task.status === "pending" || task.status === "running") {
|
|
177
|
+
task.status = "canceled";
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
currentTaskIds.clear();
|
|
181
|
+
isRunning.value = false;
|
|
182
|
+
triggerUpdate();
|
|
183
|
+
};
|
|
184
|
+
const retry = taskId => {
|
|
185
|
+
const task = taskMap.get(taskId);
|
|
186
|
+
if (task && (task.status === "rejected" || task.status === "canceled")) {
|
|
187
|
+
task.status = "pending";
|
|
188
|
+
task.error = void 0;
|
|
189
|
+
task.result = void 0;
|
|
190
|
+
task.startTime = void 0;
|
|
191
|
+
task.endTime = void 0;
|
|
192
|
+
version.value++;
|
|
193
|
+
if (isRunning.value && !isPaused.value) {
|
|
194
|
+
setTimeout(processQueue, 0);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const retryAll = () => {
|
|
199
|
+
taskMap.forEach(task => {
|
|
200
|
+
if (task.status === "rejected" || task.status === "canceled") {
|
|
201
|
+
task.status = "pending";
|
|
202
|
+
task.error = void 0;
|
|
203
|
+
task.result = void 0;
|
|
204
|
+
task.startTime = void 0;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
triggerUpdate();
|
|
208
|
+
if (!isRunning.value) {
|
|
209
|
+
start();
|
|
210
|
+
} else if (isPaused.value) {
|
|
211
|
+
resume();
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const getTask = taskId => {
|
|
215
|
+
return taskMap.get(taskId);
|
|
216
|
+
};
|
|
217
|
+
(0, _vue.onUnmounted)(() => {
|
|
218
|
+
cancelAll();
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
tasks,
|
|
222
|
+
pendingTasks,
|
|
223
|
+
runningTasks,
|
|
224
|
+
completedTasks,
|
|
225
|
+
failedTasks,
|
|
226
|
+
isRunning,
|
|
227
|
+
isEmpty,
|
|
228
|
+
isAllComplete,
|
|
229
|
+
completedCount,
|
|
230
|
+
totalCount,
|
|
231
|
+
add,
|
|
232
|
+
remove,
|
|
233
|
+
clear,
|
|
234
|
+
start,
|
|
235
|
+
pause,
|
|
236
|
+
resume,
|
|
237
|
+
cancel,
|
|
238
|
+
cancelAll,
|
|
239
|
+
retry,
|
|
240
|
+
retryAll,
|
|
241
|
+
getTask
|
|
242
|
+
};
|
|
243
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type Ref, type ComputedRef } from 'vue';
|
|
2
|
+
/** 队列任务状态 */
|
|
3
|
+
export type QueueTaskStatus = 'pending' | 'running' | 'fulfilled' | 'rejected' | 'canceled';
|
|
4
|
+
/** 队列任务 */
|
|
5
|
+
export interface QueueTask<T = unknown> {
|
|
6
|
+
/** 任务 ID */
|
|
7
|
+
id: string;
|
|
8
|
+
/** 任务 key(用于去重) */
|
|
9
|
+
key?: string;
|
|
10
|
+
/** 任务函数 */
|
|
11
|
+
task: () => Promise<T>;
|
|
12
|
+
/** 任务优先级(数字越大优先级越高) */
|
|
13
|
+
priority?: number;
|
|
14
|
+
/** 任务状态 */
|
|
15
|
+
status: QueueTaskStatus;
|
|
16
|
+
/** 任务结果 */
|
|
17
|
+
result?: T;
|
|
18
|
+
/** 任务错误 */
|
|
19
|
+
error?: Error;
|
|
20
|
+
/** 任务元数据 */
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
22
|
+
/** 开始时间 */
|
|
23
|
+
startTime?: number;
|
|
24
|
+
/** 结束时间 */
|
|
25
|
+
endTime?: number;
|
|
26
|
+
/** 延迟时间 (ms) */
|
|
27
|
+
delay?: number;
|
|
28
|
+
/** 创建时间 */
|
|
29
|
+
createdAt: number;
|
|
30
|
+
}
|
|
31
|
+
/** 队列配置 */
|
|
32
|
+
export interface UseQueueOptions {
|
|
33
|
+
/** 最大并发数 */
|
|
34
|
+
concurrency?: number;
|
|
35
|
+
/** 是否自动开始 */
|
|
36
|
+
autoStart?: boolean;
|
|
37
|
+
/** 任务失败时是否继续 */
|
|
38
|
+
continueOnError?: boolean;
|
|
39
|
+
/** 任务完成回调 */
|
|
40
|
+
onTaskComplete?: <T>(task: QueueTask<T>) => void;
|
|
41
|
+
/** 任务错误回调 */
|
|
42
|
+
onTaskError?: <T>(task: QueueTask<T>, error: Error) => void;
|
|
43
|
+
/** 所有任务完成回调 */
|
|
44
|
+
onAllComplete?: <T>(tasks: QueueTask<T>[]) => void;
|
|
45
|
+
}
|
|
46
|
+
/** 添加任务选项 */
|
|
47
|
+
export interface AddTaskOptions {
|
|
48
|
+
key?: string;
|
|
49
|
+
priority?: number;
|
|
50
|
+
delay?: number;
|
|
51
|
+
metadata?: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
/** 队列 Hook 返回值 */
|
|
54
|
+
export interface UseQueueReturn<T = unknown> {
|
|
55
|
+
/** 队列任务列表 */
|
|
56
|
+
tasks: ComputedRef<QueueTask<T>[]>;
|
|
57
|
+
/** 待执行的任务 */
|
|
58
|
+
pendingTasks: ComputedRef<QueueTask<T>[]>;
|
|
59
|
+
/** 正在执行的任务 */
|
|
60
|
+
runningTasks: ComputedRef<QueueTask<T>[]>;
|
|
61
|
+
/** 已完成的任务 */
|
|
62
|
+
completedTasks: ComputedRef<QueueTask<T>[]>;
|
|
63
|
+
/** 失败的任务 */
|
|
64
|
+
failedTasks: ComputedRef<QueueTask<T>[]>;
|
|
65
|
+
/** 队列是否正在运行 */
|
|
66
|
+
isRunning: Ref<boolean>;
|
|
67
|
+
/** 队列是否为空 */
|
|
68
|
+
isEmpty: ComputedRef<boolean>;
|
|
69
|
+
/** 队列是否全部完成 */
|
|
70
|
+
isAllComplete: ComputedRef<boolean>;
|
|
71
|
+
/** 已完成数量 */
|
|
72
|
+
completedCount: ComputedRef<number>;
|
|
73
|
+
/** 总数量 */
|
|
74
|
+
totalCount: ComputedRef<number>;
|
|
75
|
+
/** 添加任务 */
|
|
76
|
+
add: <R = unknown>(task: () => Promise<R>, options?: AddTaskOptions) => string;
|
|
77
|
+
/** 移除任务 */
|
|
78
|
+
remove: (taskId: string) => void;
|
|
79
|
+
/** 清空队列 */
|
|
80
|
+
clear: () => void;
|
|
81
|
+
/** 开始执行队列 */
|
|
82
|
+
start: () => void;
|
|
83
|
+
/** 暂停队列 */
|
|
84
|
+
pause: () => void;
|
|
85
|
+
/** 继续队列 */
|
|
86
|
+
resume: () => void;
|
|
87
|
+
/** 取消任务 */
|
|
88
|
+
cancel: (taskId: string) => void;
|
|
89
|
+
/** 取消所有任务 */
|
|
90
|
+
cancelAll: () => void;
|
|
91
|
+
/** 重试任务 */
|
|
92
|
+
retry: (taskId: string) => void;
|
|
93
|
+
/** 重试所有失败任务 */
|
|
94
|
+
retryAll: () => void;
|
|
95
|
+
/** 获取任务 */
|
|
96
|
+
getTask: (taskId: string) => QueueTask<T> | undefined;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* useQueue - 请求队列管理 Hook
|
|
100
|
+
*
|
|
101
|
+
* 支持并发控制、优先级、延迟执行、失败重试等功能
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* const {
|
|
105
|
+
* tasks, pendingTasks, runningTasks, isRunning,
|
|
106
|
+
* add, remove, start, pause, clear
|
|
107
|
+
* } = useQueue({
|
|
108
|
+
* concurrency: 3,
|
|
109
|
+
* autoStart: true,
|
|
110
|
+
* })
|
|
111
|
+
*
|
|
112
|
+
* // 添加任务
|
|
113
|
+
* add(async () => {
|
|
114
|
+
* const res = await request.get('/api/item/1')
|
|
115
|
+
* return res.data
|
|
116
|
+
* }, { priority: 1 })
|
|
117
|
+
*/
|
|
118
|
+
export declare function useQueue<T = unknown>(options?: UseQueueOptions): UseQueueReturn<T>;
|