import { defineStore } from "pinia"; import cache from "@/common/utils/cache"; import globalConfig from "@/common/config/global"; // 新增:导入 refreshToken 接口 import { login, smsLogin, getUserInfo, logout, moduleList, getSimpleDictDataList, refreshToken, } from "@/api/user"; // 新增:定义刷新Token的定时器标识(避免重复创建定时器) let refreshTokenTimer = null; export const useUserStore = defineStore("user", { // 修复1:删除重复的 state 定义,只保留根层级的 state state: () => ({ token: cache.get(globalConfig.cache.tokenKey) || "", // 初始值从缓存取(兼容持久化) refreshToken: cache.get(globalConfig.cache.refreshTokenKey) || "", // 新增:Refresh Token 状态 userInfo: cache.get(globalConfig.cache.userInfoKey) || {}, userId: cache.get(globalConfig.cache.userIdKey) || "", moduleListInfo: cache.get(globalConfig.cache.moduleListKey) || "", expireTime: cache.get(globalConfig.cache.expireTimeKey) || 0, dictData: cache.get(globalConfig.cache.dictDataKey) || {}, dictList: cache.get(globalConfig.cache.dictListKey) || [], // 用来存放【过滤后的】字典数组 logoutLoading: false, }), getters: { isLogin: (state) => { if (!state.token) return false; if (state.expireTime && state.expireTime < Date.now()) { return false; } return true; }, permissions: (state) => state.userInfo.permissions || [], // 新增:判断是否需要刷新Token(过期前30秒触发,避免已过期才刷新) needRefreshToken: (state) => { if (!state.token || !state.refreshToken) return false; const remainingTime = state.expireTime - Date.now(); // 剩余时间大于0秒 且 小于30秒(即将过期) return remainingTime > 0 && remainingTime < 30 * 1000; }, }, actions: { async login(params) { try { // smsLogin let res; if (params.type == "sms") { res = await smsLogin(params); } else { res = await login(params); } // 新增:从登录接口返回值中获取 refreshToken(若接口返回字段名不一致,可调整,如 res.refresh_token) const { accessToken, expiresTime, userId, refreshToken } = res; if (!accessToken) { throw new Error("登录失败,未获取到令牌"); } // 更新 Pinia state this.token = accessToken; this.refreshToken = refreshToken || ""; // 新增:存储Refresh Token this.expireTime = expiresTime; this.userId = userId; this.userInfo = {}; // 等待 Pinia 持久化同步 await new Promise((resolve) => setTimeout(resolve, 50)); // 获取用户信息 const userInfo = await this.getUserInfo(); this.userInfo = userInfo; // 获取模块列表 let moduleListInfo = null; try { moduleListInfo = await this.getModuleList(); this.moduleListInfo = moduleListInfo; } catch (moduleErr) { console.warn("获取模块列表失败(不影响登录):", moduleErr); uni.showToast({ title: "获取模块列表失败,可正常登录", icon: "none", }); } // 修复2:调用字典接口时,不直接抛错(避免阻断登录) try { const dictRes = await this.getAndSaveDictData(); // 仅接口返回成功时才更新字典 this.dictData = dictRes || {}; console.log("字典数据获取成功:", this.dictData); // 获取原始完整字典 const originDictList = uni.$dict.getDictSimpleList("business_line"); console.log("原始完整字典:", originDictList); // ========== 核心:根据 userInfo.busiLine 过滤,结果存入 dictList ========== // this.userInfo?.busiLine console.log("用户信息", this.userInfo.user); if (this.userInfo.user.busiLine && originDictList?.length) { const busiValueArr = this.userInfo.user.busiLine .split(",") .map((item) => item.trim()) .filter(Boolean); this.dictList = originDictList.filter((item) => { return busiValueArr.includes(item.value); }); } else { this.dictList = []; } console.log("过滤完成,dictList = ", this.dictList); } catch (dictErr) { console.warn("获取字典失败(不影响登录):", dictErr); uni.showToast({ title: "获取字典失败,可正常使用", icon: "none" }); this.dictList = []; // 异常情况置空 } // 新增:登录成功后,启动 Token 自动刷新定时器 this.startRefreshTokenTimer(); return { ...res, userInfo, moduleListInfo }; } catch (err) { console.error("登录流程失败:", err); throw err; } }, // 新增:调用刷新Token接口,返回刷新是否成功 async refreshTokenApi() { // 前置校验:无Refresh Token直接返回失败 if (!this.refreshToken) { console.warn("无刷新令牌,无法刷新Token"); this.logout(); // 无刷新令牌,直接退出登录 return false; } try { // 调用修正后的 refreshToken 接口,传入当前的 refreshToken const res = await refreshToken(this.refreshToken); // 校验接口返回结果(根据你的实际接口返回格式调整) if (!res || !res.accessToken) { throw new Error("刷新Token失败,未获取到新令牌"); } // 更新 Token 相关状态 this.token = res.accessToken; // 新的访问令牌 this.refreshToken = res.refreshToken || this.refreshToken; // 若接口返回新的Refresh Token则更新 this.expireTime = res.expiresTime; // 新的过期时间 console.log("Token 刷新成功"); return true; } catch (err) { console.error("刷新Token失败:", err); // 刷新失败,直接退出登录 this.logout(); return false; } }, // 新增:启动 Token 自动刷新定时器 startRefreshTokenTimer() { // 先清除已有定时器,避免重复创建 if (refreshTokenTimer) { clearTimeout(refreshTokenTimer); refreshTokenTimer = null; } // 计算剩余时间(过期前30秒执行刷新) const remainingTime = this.expireTime - Date.now() - 30 * 1000; if (remainingTime <= 0) { // 剩余时间不足,立即执行刷新 this.refreshTokenApi(); return; } // 设置定时器,到期后执行刷新 refreshTokenTimer = setTimeout(async () => { const refreshSuccess = await this.refreshTokenApi(); // 刷新成功后,重新启动定时器(循环自动刷新) if (refreshSuccess) { this.startRefreshTokenTimer(); } }, remainingTime); console.log( `Token 自动刷新定时器已启动,将在 ${Math.ceil(remainingTime / 1000)} 秒后执行刷新`, ); }, // 新增:停止 Token 自动刷新定时器(避免内存泄漏) stopRefreshTokenTimer() { if (refreshTokenTimer) { clearTimeout(refreshTokenTimer); refreshTokenTimer = null; } }, // 修复3:重构字典获取方法(加 Token 校验 + 强制携带 Token + 宽松错误处理) async getAndSaveDictData() { // 前置校验:无登录态直接返回,不请求接口 if (!this.isLogin) { console.warn("未登录,跳过字典获取"); return { code: -1, msg: "未登录" }; } try { // 强制携带 Token(和 getModuleList 保持一致,避免拦截器同步延迟) const res = await getSimpleDictDataList( {}, // 接口入参(按需传,比如 dictType: ['level']) { header: { Authorization: `Bearer ${this.token}` } }, ); // 校验接口返回码(核心:避免非 0 码数据存入) if (res.code !== 0) { console.warn("字典接口返回失败:", res.msg); return res; // 返回错误信息,但不抛错 } return res; } catch (err) { // 修复4:宽松错误处理,只打印日志,不抛错(避免阻断登录) console.error("字典接口请求异常:", err); return { code: -2, msg: "接口请求异常:" + (err.message || "网络错误"), }; } }, async getModuleList() { try { if (!this.token) { throw new Error("未获取到登录令牌,无法获取模块列表"); } const res = await moduleList( {}, { header: { Authorization: `Bearer ${this.token}` }, }, ); return res; } catch (err) { console.error("获取用户菜单失败:", err); if (err?.data?.code === 401 || err?.message.includes("401")) { throw new Error("登录态已过期,请重新登录"); } else { throw new Error("获取用户菜单失败,请重新登录"); } } }, async getUserInfo() { try { const res = await getUserInfo(); return res; } catch (err) { console.error("获取用户信息失败:", err); throw new Error("获取用户信息失败,请重新登录"); } }, logout() { // 新增:退出登录时,停止刷新定时器 this.stopRefreshTokenTimer(); const pages = getCurrentPages(); if (pages.length === 0) return; const currentPageRoute = pages[pages.length - 1].route; const loginPath = globalConfig.router.loginPath .replace(/^\//, "") .split("?")[0]; if (currentPageRoute === loginPath) { this.token = ""; this.refreshToken = ""; // 新增:清空Refresh Token this.userInfo = {}; this.userId = ""; this.moduleListInfo = ""; this.expireTime = 0; this.dictData = {}; this.dictList = []; return; } const logoutWithLock = async () => { if (this.logoutLoading) return; this.logoutLoading = true; try { await logout(); } catch (err) { console.error("退出登录接口调用失败:", err); } finally { this.token = ""; this.refreshToken = ""; // 新增:清空Refresh Token this.userInfo = {}; this.userId = ""; this.moduleListInfo = ""; this.dictData = {}; this.dictList = []; this.expireTime = 0; this.logoutLoading = false; uni.redirectTo({ url: globalConfig.router.loginPath, }); } }; logoutWithLock(); }, checkLogin() { if (!this.isLogin) { const pages = getCurrentPages(); if (pages.length === 0) return false; const currentPageRoute = pages[pages.length - 1].route; const loginPath = globalConfig.router.loginPath .replace(/^\//, "") .split("?")[0]; if (currentPageRoute !== loginPath) { uni.redirectTo({ url: globalConfig.router.loginPath, }); } return false; } // 新增:已登录时,启动刷新定时器(防止页面刷新后定时器丢失) this.startRefreshTokenTimer(); return true; }, }, persist: { enabled: true, key: "user_store", storage: { getItem: (key) => uni.getStorageSync(key), setItem: (key, value) => uni.setStorageSync(key, value), removeItem: (key) => uni.removeStorageSync(key), }, serializer: { serialize: (state) => { uni.setStorageSync(globalConfig.cache.tokenKey, state.token); uni.setStorageSync( globalConfig.cache.refreshTokenKey, state.refreshToken, ); uni.setStorageSync(globalConfig.cache.userIdKey, state.userId); uni.setStorageSync(globalConfig.cache.expireTimeKey, state.expireTime); uni.setStorageSync(globalConfig.cache.userInfoKey, state.userInfo); uni.setStorageSync( globalConfig.cache.moduleListKey, state.moduleListInfo, ); uni.setStorageSync(globalConfig.cache.dictDataKey, state.dictData); uni.setStorageSync(globalConfig.cache.dictListKey, state.dictList); return state; }, deserialize: (value) => { return { token: uni.getStorageSync(globalConfig.cache.tokenKey) || "", refreshToken: uni.getStorageSync(globalConfig.cache.refreshTokenKey) || "", userId: uni.getStorageSync(globalConfig.cache.userIdKey) || "", expireTime: uni.getStorageSync(globalConfig.cache.expireTimeKey) || 0, userInfo: uni.getStorageSync(globalConfig.cache.userInfoKey) || {}, moduleListInfo: uni.getStorageSync(globalConfig.cache.moduleListKey) || "", dictData: uni.getStorageSync(globalConfig.cache.dictDataKey) || {}, dictList: uni.getStorageSync(globalConfig.cache.dictListKey) || [], logoutLoading: false, }; }, }, }, });