From b94e159e748e5cfafa9fc23b7c8b91b362570bed Mon Sep 17 00:00:00 2001 From: Shenlijun Date: Sat, 4 Jul 2026 10:32:45 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=BA=95=E5=BA=A7=E3=80=91=E6=96=B0?= =?UTF-8?q?=E5=A2=9Eloading=E5=92=8Crequest=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- snowy-admin-web/src/hooks/useLoading.js | 82 +++++++++++++++++++++++++ snowy-admin-web/src/hooks/useRequest.js | 79 ++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 snowy-admin-web/src/hooks/useLoading.js create mode 100644 snowy-admin-web/src/hooks/useRequest.js diff --git a/snowy-admin-web/src/hooks/useLoading.js b/snowy-admin-web/src/hooks/useLoading.js new file mode 100644 index 00000000..2a6642d8 --- /dev/null +++ b/snowy-admin-web/src/hooks/useLoading.js @@ -0,0 +1,82 @@ +// useLoading.js +import { ref } from 'vue'; + +export function useLoading(name, initialValue = false) { + // 参数校验:name 必须是非空字符串 + if (typeof name !== 'string' || !name.trim()) { + throw new Error('useLoading: first argument "name" must be a non-empty string'); + } + + const loading = ref(initialValue); + const capName = name.charAt(0).toUpperCase() + name.slice(1); // 首字母大写 + + const start = () => { + loading.value = true; + }; + + const stop = () => { + loading.value = false; + }; + + return { + [`${name}Loading`]: loading, // 动态命名:userLoading + [`start${capName}Loading`]: start, // 动态命名:startUserLoading + [`stop${capName}Loading`]: stop // 动态命名:stopUserLoading + }; +} + + + +/** + 使用示例: + + + + + */ \ No newline at end of file diff --git a/snowy-admin-web/src/hooks/useRequest.js b/snowy-admin-web/src/hooks/useRequest.js new file mode 100644 index 00000000..7569774b --- /dev/null +++ b/snowy-admin-web/src/hooks/useRequest.js @@ -0,0 +1,79 @@ +import { ref } from 'vue'; + +export function useRequest(name, service) { + // 参数校验(保持不变) + if (typeof name !== 'string' || !name.trim()) { + throw new Error('useRequest: first argument "name" must be a non-empty string'); + } + if (typeof service !== 'function') { + throw new Error('useRequest: second argument "service" must be a function'); + } + + const data = ref(null); + const error = ref(null); + const loading = ref(false); + const capName = name.charAt(0).toUpperCase() + name.slice(1); + + // ✅ 新增:纯 Promise 方法(不管理任何状态) + const runAsync = (...args) => { + return service(...args); // 直接返回原始 Promise + }; + + // ✅ 优化:基于 runAsync 实现 run(避免逻辑重复) + const run = async (...args) => { + loading.value = true; + error.value = null; + try { + const result = await runAsync(...args); // ← 复用 runAsync + data.value = result; + return result; + } catch (err) { + error.value = err; + throw err; + } finally { + loading.value = false; + } + }; + + return { + [`${name}Data`]: data, + [`${name}Error`]: error, + [`${name}Loading`]: loading, + [`fetch${capName}`]: run, // 自动管理状态 + [`fetch${capName}Async`]: runAsync // 纯 Promise 模式 + }; +} + +/* + +✅ 测试用例 1:基础成功请求(run 模式) +const mockService = () => Promise.resolve({ id: 1, name: "Test User" }); + +// 初始化 hook +const { fetchUser, userData, userLoading, userError } = + useRequest('user', mockService); + +// 触发请求 +fetchUser(); + +// 验证结果 +console.log(userLoading.value); // true → false (自动切换) +console.log(userData.value); // { id: 1, name: "Test User" } +console.log(userError.value); // null + + +✅ 测试用例 2:基础成功请求(runAsync 模式) +const { fetchUserAsync, userData, userLoading } = + useRequest('user', mockService); + +// 仅执行请求,不修改内部状态 +fetchUserAsync().then(result => { + console.log("Raw result:", result); // { id: 1, name: "Test User" } + + // 关键验证:内部状态未被修改 + console.log(userLoading.value); // false (始终为 false) + console.log(userData.value); // null (始终为 null) +}); + + + */ \ No newline at end of file