From 66b5514463d106e6f320ed484c4a513b84b27d01 Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Fri, 15 Aug 2025 02:06:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=92=8C=E7=99=BB=E5=BD=95=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(theme): 重构主题管理逻辑,增加自定义颜色选项和持久化存储 refactor(login): 优化登录流程,增加防重复提交和错误处理 fix(request): 修复请求循环跳转问题,移除publicRequest方法 style: 更新页面样式和布局,改进用户体验 docs: 移除无用注释,更新代码文档 --- fastapp/components.d.ts | 1 + fastapp/package.json | 47 +-- fastapp/src/App.vue | 1 - fastapp/src/api/auth.ts | 17 +- fastapp/src/api/file.ts | 2 +- fastapp/src/api/user.ts | 2 +- .../qiun-data-charts/qiun-data-charts.vue | 2 +- fastapp/src/composables/useTheme.ts | 269 ++++++++++--- fastapp/src/enums/api-header.enum.ts | 15 + fastapp/src/pages/login/index.vue | 126 +++--- fastapp/src/pages/mine/index.vue | 50 ++- .../src/pages/mine/settings/theme/index.vue | 374 +++++++++++++++--- fastapp/src/store/modules/theme.store.ts | 1 + fastapp/src/store/modules/user.store.ts | 63 +-- fastapp/src/types/auto-imports.d.ts | 3 +- fastapp/src/utils/auth.ts | 17 - fastapp/src/utils/request.ts | 28 +- fastapp/vite.config.ts | 6 +- frontend/docs/public/group.jpg | Bin 209656 -> 205819 bytes 19 files changed, 749 insertions(+), 275 deletions(-) create mode 100644 fastapp/src/enums/api-header.enum.ts diff --git a/fastapp/components.d.ts b/fastapp/components.d.ts index 8af01919..78672471 100644 --- a/fastapp/components.d.ts +++ b/fastapp/components.d.ts @@ -21,6 +21,7 @@ declare module 'vue' { WdCard: typeof import('wot-design-uni/components/wd-card/wd-card.vue')['default'] WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default'] WdCellGroup: typeof import('wot-design-uni/components/wd-cell-group/wd-cell-group.vue')['default'] + WdCheckbox: typeof import('wot-design-uni/components/wd-checkbox/wd-checkbox.vue')['default'] WdCollapse: typeof import('wot-design-uni/components/wd-collapse/wd-collapse.vue')['default'] WdCollapseItem: typeof import('wot-design-uni/components/wd-collapse-item/wd-collapse-item.vue')['default'] WdConfigProvider: typeof import('wot-design-uni/components/wd-config-provider/wd-config-provider.vue')['default'] diff --git a/fastapp/package.json b/fastapp/package.json index fedda20a..1ea3572f 100644 --- a/fastapp/package.json +++ b/fastapp/package.json @@ -98,37 +98,38 @@ "commit": "git-cz" }, "dependencies": { - "@dcloudio/uni-app": "3.0.0-4020420240722002", - "@dcloudio/uni-app-harmony": "3.0.0-4020420240722002", - "@dcloudio/uni-app-plus": "3.0.0-4020420240722002", - "@dcloudio/uni-components": "3.0.0-4020420240722002", - "@dcloudio/uni-h5": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-alipay": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-baidu": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-jd": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-kuaishou": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-lark": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-qq": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-toutiao": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-weixin": "3.0.0-4020420240722002", - "@dcloudio/uni-mp-xhs": "3.0.0-4020420240722002", - "@dcloudio/uni-quickapp-webview": "3.0.0-4020420240722002", + "@dcloudio/uni-app": "3.0.0-4070520250711001", + "@dcloudio/uni-app-harmony": "3.0.0-4070520250711001", + "@dcloudio/uni-app-plus": "3.0.0-4070520250711001", + "@dcloudio/uni-components": "3.0.0-4070520250711001", + "@dcloudio/uni-h5": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-alipay": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-baidu": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-harmony": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-jd": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-kuaishou": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-lark": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-qq": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-toutiao": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-weixin": "3.0.0-4070520250711001", + "@dcloudio/uni-mp-xhs": "3.0.0-4070520250711001", + "@dcloudio/uni-quickapp-webview": "3.0.0-4070520250711001", "@stomp/stompjs": "^7.1.1", "@uni-helper/uni-use": "^0.19.14", "@vueuse/core": "9.13.0", "pinia": "^2.2.2", - "vue": "^3.5.13", - "wot-design-uni": "^1.9.1", - "vue-i18n": "9.14.5" + "vue": "^3.4.21", + "vue-i18n": "^9.1.9", + "wot-design-uni": "^1.9.1" }, "devDependencies": { "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@dcloudio/types": "^3.4.8", - "@dcloudio/uni-automator": "3.0.0-4020420240722002", - "@dcloudio/uni-cli-shared": "3.0.0-4020420240722002", - "@dcloudio/uni-stacktracey": "3.0.0-4020420240722002", - "@dcloudio/vite-plugin-uni": "3.0.0-4020420240722002", + "@dcloudio/uni-automator": "3.0.0-4070520250711001", + "@dcloudio/uni-cli-shared": "3.0.0-4070520250711001", + "@dcloudio/uni-stacktracey": "3.0.0-4070520250711001", + "@dcloudio/vite-plugin-uni": "3.0.0-4070520250711001", "@eslint/js": "^9.10.0", "@uni-helper/uni-types": "1.0.0-alpha.6", "@uni-helper/vite-plugin-uni-components": "^0.2.0", @@ -163,7 +164,7 @@ "unocss": "^0.62.4", "unocss-preset-weapp": "^0.62.2", "unplugin-auto-import": "^0.18.3", - "vite": "6.3.2", + "vite": "5.2.8", "vue-eslint-parser": "^9.4.3", "vue-tsc": "^1.0.24" } diff --git a/fastapp/src/App.vue b/fastapp/src/App.vue index bcf15faf..553ca4dc 100644 --- a/fastapp/src/App.vue +++ b/fastapp/src/App.vue @@ -5,7 +5,6 @@ import { useTheme } from "@/composables/useTheme"; const { initTheme } = useTheme(); onLaunch(() => { - console.log("App Launch"); // 初始化主题 initTheme(); }); diff --git a/fastapp/src/api/auth.ts b/fastapp/src/api/auth.ts index 8082d242..0f61d819 100644 --- a/fastapp/src/api/auth.ts +++ b/fastapp/src/api/auth.ts @@ -1,6 +1,7 @@ -import request, { publicRequest } from "@/utils/request"; +import request from "@/utils/request"; +import { ApiHeader } from "@/enums/api-header.enum"; -const AUTH_BASE_URL = "/api/v1/system/auth"; +const AUTH_BASE_URL = "/system/auth"; const AuthAPI = { /** @@ -12,10 +13,9 @@ const AuthAPI = { return request({ url: `${AUTH_BASE_URL}/login`, method: "POST", - headers: { - "Content-Type": "multipart/form-data", - }, + headers: { [ApiHeader.KEY]: ApiHeader.FORM }, data: body, + skipAuth: true, }); }, @@ -40,6 +40,7 @@ const AuthAPI = { return request({ url: `${AUTH_BASE_URL}/captcha/get`, method: "GET", + skipAuth: true, }); }, @@ -62,10 +63,11 @@ const AuthAPI = { * @returns 登录结果 */ loginByWxMiniAppPhone(data: WxLoginData): Promise { - return publicRequest({ + return request({ url: `${AUTH_BASE_URL}/wx/miniapp/phone-login`, method: "POST", data, + skipAuth: true, }); }, @@ -75,10 +77,11 @@ const AuthAPI = { * @returns 登录结果 */ loginByWxMiniAppCode(code: string): Promise { - return publicRequest({ + return request({ url: `${AUTH_BASE_URL}/wx/miniapp/code-login`, method: "POST", data: { code }, + skipAuth: true, }); }, }; diff --git a/fastapp/src/api/file.ts b/fastapp/src/api/file.ts index 5c444f4c..ccdd1d85 100644 --- a/fastapp/src/api/file.ts +++ b/fastapp/src/api/file.ts @@ -11,7 +11,7 @@ const FileAPI = { /** * 文件上传地址 */ - uploadUrl: baseApi + "/api/v1/files/upload", + uploadUrl: baseApi + "/files/upload", /** * 上传文件 diff --git a/fastapp/src/api/user.ts b/fastapp/src/api/user.ts index e9e14689..a105da96 100644 --- a/fastapp/src/api/user.ts +++ b/fastapp/src/api/user.ts @@ -1,6 +1,6 @@ import request from "@/utils/request"; -const USER_BASE_URL = "/api/v1/system/user"; +const USER_BASE_URL = "/system/user"; const UserAPI = { /** diff --git a/fastapp/src/components/qiun-data-charts/qiun-data-charts.vue b/fastapp/src/components/qiun-data-charts/qiun-data-charts.vue index c1fbfb7a..5dfe9880 100644 --- a/fastapp/src/components/qiun-data-charts/qiun-data-charts.vue +++ b/fastapp/src/components/qiun-data-charts/qiun-data-charts.vue @@ -915,7 +915,7 @@ export default { .exec((res) => { if (res[0]) { const canvas = res[0].node; - const ctx = canvas.getContext("2d"); + const ctx = canvas.getContext("2d", { willReadFrequently: false }); cfu.option[cid].context = ctx; cfu.option[cid].rotateLock = cfu.option[cid].rotate; if ( diff --git a/fastapp/src/composables/useTheme.ts b/fastapp/src/composables/useTheme.ts index 2e74666d..296c3898 100644 --- a/fastapp/src/composables/useTheme.ts +++ b/fastapp/src/composables/useTheme.ts @@ -1,4 +1,5 @@ import type { ConfigProviderThemeVars } from "wot-design-uni"; +import { useThemeStore } from "@/store/modules/theme.store"; // 定义主题色选项 export interface ThemeColorOption { @@ -15,8 +16,32 @@ export const themeColorOptions: ThemeColorOption[] = [ { name: "樱花粉", value: "pink", primary: "#FF69B4" }, { name: "紫罗兰", value: "purple", primary: "#8A2BE2" }, { name: "朱砂红", value: "red", primary: "#FF4757" }, + { name: "天空蓝", value: "sky", primary: "#00BFFF" }, + { name: "柠檬黄", value: "yellow", primary: "#FFD700" }, ]; +// 扩展的颜色选项,用于自定义颜色选择器 +export const extendedColorOptions: ThemeColorOption[] = [ + ...themeColorOptions, + { name: "珊瑚红", value: "coral", primary: "#FF6B6B" }, + { name: "薄荷绿", value: "mint", primary: "#4ECDC4" }, + { name: "黄金黄", value: "gold", primary: "#FFD166" }, + { name: "经典红", value: "classic-red", primary: "#CD5C5C" }, + { name: "自然绿", value: "nature-green", primary: "#228B22" }, + { name: "天蓝色", value: "sky-blue", primary: "#1890FF" }, + { name: "青绿色", value: "teal", primary: "#0FC6C2" }, + { name: "深紫色", value: "deep-purple", primary: "#722ED1" }, +]; + +export interface ThemeState { + theme: "light" | "dark"; + isDark: boolean; + followSystem: boolean; + hasUserSet: boolean; + currentThemeColor: ThemeColorOption; + showThemeColorSheet: boolean; +} + export function useTheme() { // 状态定义 const theme = ref<"light" | "dark">("light"); @@ -25,24 +50,7 @@ export function useTheme() { const currentThemeColor = ref(themeColorOptions[0]); const showThemeColorSheet = ref(false); - const colorColumns = [ - { value: "#165DFF", label: "蓝色" }, - { value: "#0FC6C2", label: "青绿色" }, - { value: "#722ED1", label: "紫色" }, - { value: "#F5222D", label: "红色" }, - { value: "#FA8C16", label: "橙色" }, - { value: "#FADB14", label: "黄色" }, - { value: "#52C41A", label: "绿色" }, - { value: "#EB2F96", label: "粉色" }, - { value: "#13C2C2", label: "青色" }, - { value: "#1890FF", label: "天蓝色" }, - { value: "#CD5C5C", label: "经典红" }, - { value: "#228B22", label: "自然绿" }, - { value: "#FF6B6B", label: "珊瑚红" }, - { value: "#4ECDC4", label: "薄荷绿" }, - { value: "#FFD166", label: "黄金黄色" }, - ]; - + // 主题变量 const themeVars = reactive({ darkBackground: "#0f0f0f", darkBackground2: "#1a1a1a", @@ -60,11 +68,36 @@ export function useTheme() { // 计算属性 const isDark = computed(() => theme.value === "dark"); + // 主题状态 + const themeState = computed(() => ({ + theme: theme.value, + isDark: isDark.value, + followSystem: followSystem.value, + hasUserSet: hasUserSet.value, + currentThemeColor: currentThemeColor.value, + showThemeColorSheet: showThemeColorSheet.value, + })); + /* 手动切换主题 */ function toggleTheme(mode?: "light" | "dark") { theme.value = mode || (theme.value === "light" ? "dark" : "light"); - hasUserSet.value = true; // 标记用户已手动设置 - followSystem.value = false; // 不再跟随系统 + hasUserSet.value = true; + followSystem.value = false; + setNavigationBarColor(); + saveThemeSettings(); + + // 实时应用主题变化 + applyThemeModeToApp(); + } + + /* 实时应用主题模式到应用 */ + function applyThemeModeToApp() { + // 更新CSS变量 + if (typeof document !== "undefined") { + document.documentElement.setAttribute("data-theme", theme.value); + } + + // 更新导航栏颜色 setNavigationBarColor(); } @@ -73,124 +106,240 @@ export function useTheme() { followSystem.value = follow; if (follow) { hasUserSet.value = false; - initTheme(); // 重新获取系统主题 + initTheme(); } - } + saveThemeSettings(); - /* 设置导航栏颜色 */ - function setNavigationBarColor() { - uni.setNavigationBarColor({ - frontColor: theme.value === "light" ? "#000000" : "#ffffff", - backgroundColor: theme.value === "light" ? "#ffffff" : "#000000", - }); + // 实时应用主题变化 + applyThemeModeToApp(); } /* 设置主题色 */ function setCurrentThemeColor(color: ThemeColorOption) { currentThemeColor.value = color; themeVars.colorTheme = color.primary; + hasUserSet.value = true; + saveThemeSettings(); + + // 实时应用主题色到全局 + applyThemeColorToApp(color.primary); + } + + /* 设置自定义主题色 */ + function setCustomThemeColor(color: string) { + const customTheme: ThemeColorOption = { + name: "自定义", + value: "custom", + primary: color, + }; + setCurrentThemeColor(customTheme); + } + + /* 重置主题 */ + function resetTheme() { + setCurrentThemeColor(themeColorOptions[0]); + theme.value = "light"; + followSystem.value = true; + hasUserSet.value = false; + initTheme(); + } + + /* 保存主题设置到本地存储 */ + function saveThemeSettings() { + try { + uni.setStorageSync("theme_settings", { + theme: theme.value, + currentThemeColor: currentThemeColor.value, + followSystem: followSystem.value, + hasUserSet: hasUserSet.value, + }); + } catch (error) { + console.warn("保存主题设置失败:", error); + } + } + + /* 从本地存储加载主题设置 */ + function loadThemeSettings() { + try { + const settings = uni.getStorageSync("theme_settings"); + if (settings) { + if (settings.theme && (settings.theme === "light" || settings.theme === "dark")) { + theme.value = settings.theme; + } + if (settings.currentThemeColor) { + const savedColor = + themeColorOptions.find((c) => c.value === settings.currentThemeColor.value) || + extendedColorOptions.find((c) => c.value === settings.currentThemeColor.value) || + settings.currentThemeColor; + if (savedColor) { + currentThemeColor.value = savedColor; + themeVars.colorTheme = savedColor.primary; + } + } + followSystem.value = settings.followSystem !== false; + hasUserSet.value = settings.hasUserSet === true; + } + } catch (error) { + console.warn("加载主题设置失败:", error); + } } /* 获取系统主题 */ function getSystemTheme(): "light" | "dark" { try { // #ifdef MP-WEIXIN - // 微信小程序使用 getAppBaseInfo const appBaseInfo = uni.getAppBaseInfo(); - if (appBaseInfo && appBaseInfo.theme) { + if (appBaseInfo?.theme) { return appBaseInfo.theme as "light" | "dark"; } // #endif // #ifndef MP-WEIXIN - // 其他平台使用 getSystemInfoSync const systemInfo = uni.getSystemInfoSync(); - if (systemInfo && systemInfo.theme) { + if (systemInfo?.theme) { return systemInfo.theme as "light" | "dark"; } // #endif } catch (error) { console.warn("获取系统主题失败:", error); } - return "light"; // 默认返回 light + return "light"; + } + + /* 设置导航栏颜色 */ + function setNavigationBarColor() { + // #ifndef H5 + uni.setNavigationBarColor({ + frontColor: theme.value === "light" ? "#000000" : "#ffffff", + backgroundColor: theme.value === "light" ? "#ffffff" : "#000000", + }); + // #endif + } + + /* 实时应用主题色到应用 */ + function applyThemeColorToApp(color: string) { + // 更新Wot Design组件库主题色 + themeVars.colorTheme = color; + + // 更新CSS变量 + if (typeof document !== "undefined") { + document.documentElement.style.setProperty("--wot-color-theme", color); + document.documentElement.style.setProperty("--primary-color", color); + document.documentElement.style.setProperty("--primary-color-light", color + "20"); + document.documentElement.style.setProperty("--primary-color-dark", color); + } + + // 同步到主题存储 + const themeStore = useThemeStore(); + themeStore.setPrimaryColor(color); + + // 通过事件总线通知全局主题变化 + uni.$emit("theme-color-changed", color); + + // 强制刷新页面样式 + setTimeout(() => { + uni.$emit("force-theme-refresh"); + }, 50); } /* 初始化主题 */ function initTheme() { - // 如果用户已手动设置且不跟随系统,保持当前主题 if (hasUserSet.value && !followSystem.value) { - console.log("使用用户设置的主题:", theme.value); setNavigationBarColor(); + applyThemeColorToApp(currentThemeColor.value.primary); return; } - // 获取系统主题 const systemTheme = getSystemTheme(); - - // 如果是首次启动或跟随系统,使用系统主题 if (!hasUserSet.value || followSystem.value) { theme.value = systemTheme; - if (!hasUserSet.value) { - followSystem.value = true; - } else { - console.log("跟随系统主题:", theme.value); - } } setNavigationBarColor(); + applyThemeColorToApp(currentThemeColor.value.primary); } - /* 打开主题色选择 */ + /* 主题色选择器相关 */ function openThemeColorPicker() { showThemeColorSheet.value = true; } - /* 关闭主题色选择 */ function closeThemeColorPicker() { showThemeColorSheet.value = false; } - /* 选择主题色 */ function selectThemeColor(option: ThemeColorOption) { setCurrentThemeColor(option); closeThemeColorPicker(); } - // 检查函数是否存在的工具函数 - const isFunction = (fn: any): boolean => typeof fn === "function"; - + // 生命周期 onBeforeMount(() => { + loadThemeSettings(); initTheme(); - if (isFunction(uni.onThemeChange)) { - uni.onThemeChange((res) => { - toggleTheme(res.theme); + + // #ifdef MP-WEIXIN + if (uni.onThemeChange) { + uni.onThemeChange((res: any) => { + if (followSystem.value) { + theme.value = res.theme; + setNavigationBarColor(); + } }); } + // #endif + + // #ifndef MP-WEIXIN + if (uni.onThemeChange) { + uni.onThemeChange((res: any) => { + if (followSystem.value) { + theme.value = res.theme; + setNavigationBarColor(); + } + }); + } + // #endif }); onUnmounted(() => { - if (isFunction(uni.offThemeChange)) { - uni.offThemeChange((res) => { - toggleTheme(res.theme); - }); + // 清理监听器 + if (uni.offThemeChange) { + uni.offThemeChange(() => {}); } }); return { + // 状态 theme: computed(() => theme.value), isDark, - colorColumns, followSystem: computed(() => followSystem.value), hasUserSet: computed(() => hasUserSet.value), currentThemeColor: computed(() => currentThemeColor.value), - showThemeColorSheet, - themeVars, + showThemeColorSheet: computed(() => showThemeColorSheet.value), + themeState, + + // 主题选项 themeColorOptions, - initTheme, + extendedColorOptions, + + // 主题变量 + themeVars, + + // 方法 toggleTheme, setFollowSystem, + setCurrentThemeColor, + setCustomThemeColor, + resetTheme, + initTheme, + + // 主题色选择器 openThemeColorPicker, closeThemeColorPicker, selectThemeColor, + + // 工具方法 + saveThemeSettings, + loadThemeSettings, }; } diff --git a/fastapp/src/enums/api-header.enum.ts b/fastapp/src/enums/api-header.enum.ts new file mode 100644 index 00000000..4f5ebd4b --- /dev/null +++ b/fastapp/src/enums/api-header.enum.ts @@ -0,0 +1,15 @@ +/** + * API 请求头相关枚举封装 + */ +export const enum ApiHeader { + KEY = "Content-Type", + + /* 表单数据 */ + FORM = "application/x-www-form-urlencoded", + + /* JSON 数据 */ + JSON = "application/json", + + /* 多部分数据 */ + MULTIPART = "multipart/form-data", +} diff --git a/fastapp/src/pages/login/index.vue b/fastapp/src/pages/login/index.vue index bd788fe9..aa333d33 100644 --- a/fastapp/src/pages/login/index.vue +++ b/fastapp/src/pages/login/index.vue @@ -92,8 +92,8 @@