feat: 添加 vue-sonner,优化反馈

This commit is contained in:
2026-05-23 14:37:53 +08:00
parent 7d68db723b
commit e317e117d9
9 changed files with 141 additions and 12 deletions
+8
View File
@@ -1,5 +1,6 @@
// app/stores/answer.ts - 答题页和 Dashboard 的统一状态管理
import { defineStore } from "pinia";
import { toast } from "vue-sonner";
import type {
IHealthResponse,
@@ -133,8 +134,10 @@ export const useAnswerStore = defineStore("answer", () => {
}
searchErrorMessage.value = res.data.msg || "答题失败";
toast.error(searchErrorMessage.value);
} catch (error) {
searchErrorMessage.value = getClientErrorMessage(error, "服务器内部错误");
toast.error(searchErrorMessage.value);
} finally {
searchLoading.value = false;
}
@@ -150,6 +153,7 @@ export const useAnswerStore = defineStore("answer", () => {
error,
"健康检查失败"
);
toast.error(dashboardErrorMessage.value);
}
};
@@ -164,6 +168,7 @@ export const useAnswerStore = defineStore("answer", () => {
error,
"统计信息读取失败"
);
toast.error(dashboardErrorMessage.value);
} finally {
statsLoading.value = false;
}
@@ -186,6 +191,7 @@ export const useAnswerStore = defineStore("answer", () => {
error,
"问答记录读取失败"
);
toast.error(dashboardErrorMessage.value);
} finally {
recordsLoading.value = false;
}
@@ -204,12 +210,14 @@ export const useAnswerStore = defineStore("answer", () => {
try {
await AnswerService.clearCache();
toast.success("缓存已清空");
await Promise.allSettled([getStats(), getRecords(1)]);
} catch (error) {
dashboardErrorMessage.value = getClientErrorMessage(
error,
"缓存清理失败"
);
toast.error(dashboardErrorMessage.value);
} finally {
cacheClearing.value = false;
}
+11
View File
@@ -1,5 +1,6 @@
// app/stores/auth.ts - 用户登录状态管理
import { defineStore } from "pinia";
import { toast } from "vue-sonner";
import { authClient } from "@/utils/auth";
@@ -79,6 +80,7 @@ export const useAuthStore = defineStore("auth", () => {
const result = await authClient.signIn.email({ email, password });
if (result.error) {
error.value = "邮箱或密码错误";
toast.error(error.value);
return false;
}
// 直接使用登录响应中的用户数据,无需重复请求 get-session
@@ -87,9 +89,11 @@ export const useAuthStore = defineStore("auth", () => {
result.data.user as Parameters<typeof _setUserFromResult>[0]
);
}
toast.success("登录成功");
return true;
} catch {
error.value = "登录失败,请重试";
toast.error(error.value);
return false;
} finally {
loading.value = false;
@@ -104,6 +108,7 @@ export const useAuthStore = defineStore("auth", () => {
const result = await authClient.signUp.email({ name, email, password });
if (result.error) {
error.value = "注册失败,邮箱可能已被使用";
toast.error(error.value);
return false;
}
if (result.data?.user) {
@@ -111,9 +116,11 @@ export const useAuthStore = defineStore("auth", () => {
result.data.user as Parameters<typeof _setUserFromResult>[0]
);
}
toast.success("注册成功");
return true;
} catch {
error.value = "注册失败,请重试";
toast.error(error.value);
return false;
} finally {
loading.value = false;
@@ -125,6 +132,7 @@ export const useAuthStore = defineStore("auth", () => {
await authClient.signOut();
user.value = null;
initialized.value = false;
toast.success("已退出登录");
};
/** 刷新当前用户的 apiToken;成功后同步更新 store */
@@ -139,12 +147,15 @@ export const useAuthStore = defineStore("auth", () => {
}>("/api/user/refresh-token", { method: "POST" });
if (res.code === 0 && res.data?.apiToken && user.value) {
user.value = { ...user.value, apiToken: res.data.apiToken };
toast.success("Token 已刷新");
return true;
}
error.value = res.msg || "刷新失败";
toast.error(error.value);
return false;
} catch {
error.value = "刷新失败,请重试";
toast.error(error.value);
return false;
} finally {
loading.value = false;
+20
View File
@@ -0,0 +1,20 @@
// app/stores/global.ts - 全局通用状态(主题等),持久化由 @nuxtjs/color-mode 统一管理(localStorage/cookie
export const useGlobalStore = defineStore("theme", () => {
// useColorMode 会自动读写持久化存储,class 也由其挂到 <html>
const colorMode = useColorMode();
/** 当前是否深色模式 */
const isDark = computed(() => colorMode.value === "dark");
/** 切换深色 / 浅色 */
const toggle = () => {
colorMode.value = isDark.value ? "light" : "dark";
};
/** vue-sonner Toaster 的 theme prop 值,跟随当前模式 */
const toasterTheme = computed<"light" | "dark">(() =>
isDark.value ? "dark" : "light"
);
return { isDark, toggle, toasterTheme };
});