diff --git a/src/api/system/unitConverter/sysUnitConverter.js b/src/api/system/unitConverter/sysUnitConverter.js index de425f6..45b20ea 100644 --- a/src/api/system/unitConverter/sysUnitConverter.js +++ b/src/api/system/unitConverter/sysUnitConverter.js @@ -4,9 +4,7 @@ import request from '@/utils/request' export function listConvert(query) { return request({ url: '/system/convert/list', - headers: { - isToken: false - }, + method: 'get', params: query }) @@ -16,9 +14,7 @@ export function listConvert(query) { export function getConvert(id) { return request({ url: '/system/convert/' + id, - headers: { - isToken: false - }, + method: 'get' }) } @@ -27,9 +23,7 @@ export function getConvert(id) { export function addConvert(data) { return request({ url: '/system/convert', - headers: { - isToken: false - }, + method: 'post', data: data }) @@ -39,9 +33,7 @@ export function addConvert(data) { export function updateConvert(data) { return request({ url: '/system/convert', - headers: { - isToken: false - }, + method: 'put', data: data }) @@ -51,9 +43,7 @@ export function updateConvert(data) { export function delConvert(id) { return request({ url: '/system/convert/' + id, - headers: { - isToken: false - }, + method: 'delete' }) } diff --git a/src/main.js b/src/main.js index 021ac8f..50059f3 100644 --- a/src/main.js +++ b/src/main.js @@ -1,7 +1,6 @@ import App from './App.vue' import plugins from './plugins' -import store from './store' -import uviewPlus from 'uview-plus' +import store from './store' import { createSSRApp } from 'vue' @@ -14,8 +13,7 @@ import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, select export function createApp() { const app = createSSRApp(App) - app.use(store) - app.use(uviewPlus) + app.use(store) app.use(plugins) // #ifndef MP-WEIXIN diff --git a/src/manifest.json b/src/manifest.json index 00233f8..e844371 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -16,6 +16,9 @@ "autoclose" : true, "delay" : 0 }, + "compatible" : { + "ignoreVersion" : true // 忽略版本检查 + }, /* 模块配置 */ "modules" : { "OAuth" : {} diff --git a/src/pages/index.vue b/src/pages/index.vue index 98c8b30..6594f0c 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -33,16 +33,22 @@ ref, onMounted } from "vue"; - import modal from "@/plugins/modal" - import { - storage - } from '@/utils/storageUnit.ts'; + import modal from "@/plugins/modal" + import { extractModuleData } from '@/utils/moudlesData.ts'; - import useUnitStore from '@/store/modules/unitData.ts'; + + import useUnitStore from '@/store/modules/unitData.ts'; + import { + getToken, + setToken, + removeToken + } from "@/utils/auth"; + const unitStore = useUnitStore(); + const current = ref(0); const swiperDotIndex = ref(0); const data = ref([{ @@ -58,14 +64,60 @@ // 核心数据:计算分组和功能项定义 const moudlesGroups = ref([]); - - onMounted(async() => { + + // 登录状态校验函数 + const checkLoginStatus = () => { + // 1. 检查本地token是否存在 + const token = getToken(); + + console.log(token) + // 2. 无token/用户信息,直接跳转登录 + if (!token) { + uni.reLaunch({ + url: '/pages/login' + }); + return false; // 未登录 + } + return true; // 已登录 + }; + + onMounted(async () => { + // 第一步:优先校验登录状态(核心修复) + const isLogin = checkLoginStatus(); + if (!isLogin) return; // 未登录则终止后续逻辑 moudlesGroups.value = extractModuleData(['流量计算', '参数计算'], false) - // 优先从本地缓存恢复,再请求最新数据 - unitStore.restoreUnitDataFromLocal(); - await unitStore.getList(); - - }) + // 优先从本地缓存恢复,再请求最新数据 + unitStore.restoreUnitDataFromLocal(); + try { + await unitStore.getList(); + } catch (error) { + console.error('获取单位数据失败:', error); + + if (error.code === 401 || + (error.message && + (error.message.includes('会话过期') || + error.message.includes('无效的会话')))) { + + // 清除本地存储 + uni.removeStorageSync('token'); + uni.removeStorageSync('userInfo'); + + // 跳转到登录页 + uni.reLaunch({ + url: '/pages/login/' + }); + } else { + // 其他错误:显示具体错误信息 + const errorMsg = error.message || '数据加载失败,请稍后重试'; + modal.showToast({ + title: errorMsg, + mask: false, + icon: 'none', + duration: 2000 + }); + } + } + }); function navigateToMoudles(item) { console.log(item) diff --git a/src/pages/login.vue b/src/pages/login.vue index 34d7dc9..e639068 100644 --- a/src/pages/login.vue +++ b/src/pages/login.vue @@ -130,12 +130,17 @@ wxLogin, wxAppLogin } from '@/api/oauth' - import useUserStore from '@/store/modules/user' + import { setToken } from '@/utils/auth' import config from '@/config.js' import request from '@/utils/request' + + // 添加一个标志位,防止重复跳转 + let isChecking = false; + let hasChecked = false; + import useUserStore from '@/store/modules/user' const userStore = useUserStore() const appName = config.appInfo.name || '天然气工具箱' @@ -245,13 +250,18 @@ const res = await login(params.username, params.password, params.code, params.uuid) + if (res.token) { setToken(res.token) await userStore.getInfo() - uni.switchTab({ - url: '/pages/index' - }); + // 添加延迟,确保状态更新完成 + setTimeout(() => { + // 使用 reLaunch 关闭所有页面并跳转到首页 + uni.reLaunch({ + url: '/pages/index' + }) + }, 300) } } catch (error) { // 如果需要验证码,重新获取 @@ -348,7 +358,7 @@ if (platform === 'android' || platform === 'ios') { // APP 端 - code = await wechatLoginApp(); + code = await wechatLoginApp(); console.log(code); @@ -405,15 +415,55 @@ getCaptcha() }) - onLoad(() => { - // 检查是否有token,如果有则直接跳转 - const token = uni.getStorageSync('token') - if (token) { - uni.reLaunch({ - url: '/pages/index' - }) + onLoad(async (options) => { + // === 新增:防止重复检查 === + if (isChecking || hasChecked) return; + isChecking = true; + + try { + const token = uni.getStorageSync('token'); + + if (!token) { + hasChecked = true; + isChecking = false; + getCaptcha(); // 确保验证码加载 + return; + } + + // 仅当明确要求跳过自动登录时才中断 + if (options?.skipAutoLogin === 'true') { + hasChecked = true; + isChecking = false; + return; + } + + // 验证 token 有效性(关键:使用安全的验证方式) + try { + await userStore.getInfo(); // 这会触发用户信息请求 + + // 成功获取用户信息,跳转首页 + uni.reLaunch({ + url: '/pages/index' + }); + } catch (error) { + // === 重点修复:精确处理 token 失效 === + if (error.code === 401 || + (error.message && + (error.message.includes('会话过期') || + error.message.includes('无效的会话')))) { + + // 清除失效 token + uni.removeStorageSync('token'); + uni.removeStorageSync('userInfo'); + modal.alert('登录已过期,请重新登录'); + } + getCaptcha(); // 重新加载验证码 + } + } finally { + isChecking = false; + hasChecked = true; } - }) + }); \ No newline at end of file diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 7a2d227..dec6eeb 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -1,13 +1,78 @@ const TokenKey = 'App-Token' -export function getToken():string { - return uni.getStorageSync(TokenKey) +/** + * 获取Token(兼容多平台,带错误处理) + * @returns string 空字符串表示无Token + */ +export function getToken(): string { + try { + // 优先使用uniapp API + const token = uni.getStorageSync(TokenKey) + // 处理非字符串场景(如存入了null/undefined) + return token ? String(token) : '' + } catch (error) { + // 兼容H5隐私模式/localStorage不可用场景 + console.warn('获取Token失败:', error) + return '' + } } -export function setToken(token:string) { - return uni.setStorageSync(TokenKey, token) +/** + * 设置Token(兼容多平台,带错误处理) + * @param token 必须为字符串,非字符串会自动转换 + * @returns boolean 是否设置成功 + */ +export function setToken(token: string): boolean { + try { + if (typeof token !== 'string') { + console.warn('Token必须为字符串,已自动转换') + token = String(token) + } + uni.setStorageSync(TokenKey, token) + return true + } catch (error) { + console.error('设置Token失败:', error) + // H5兜底:尝试直接操作localStorage + if (process.env.VUE_APP_PLATFORM === 'h5' && typeof localStorage !== 'undefined') { + try { + localStorage.setItem(TokenKey, token) + return true + } catch (e) { + console.error('H5 localStorage设置失败:', e) + } + } + return false + } } -export function removeToken() { - return uni.removeStorageSync(TokenKey) +/** + * 移除Token(兼容多平台,带错误处理) + * @returns boolean 是否移除成功 + */ +export function removeToken(): boolean { + try { + uni.removeStorageSync(TokenKey) + return true + } catch (error) { + console.error('移除Token失败:', error) + // H5兜底 + if (process.env.VUE_APP_PLATFORM === 'h5' && typeof localStorage !== 'undefined') { + try { + localStorage.removeItem(TokenKey) + return true + } catch (e) { + console.error('H5 localStorage移除失败:', e) + } + } + return false + } } + +/** + * 检查Token是否有效(辅助函数) + * @returns boolean + */ +export function hasValidToken(): boolean { + const token = getToken() + return !!token && token.trim() !== '' +} \ No newline at end of file diff --git a/src/utils/request.ts b/src/utils/request.ts index 6c4660c..1d92123 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -8,103 +8,101 @@ import useUserStore from '@/store/modules/user' let timeout = 10000 const baseUrl = config.baseUrl -const request = (config: RequestConfig): Promise> => { - // 是否需要设置 token - const isToken = (config.headers || {}).isToken === false - config.header = config.header || {} - if (getToken() && !isToken) { - config.header['Authorization'] = 'Bearer ' + getToken() - } - // get请求映射params参数 - if (config.params) { - let url = config.url + '?' + tansParams(config.params) - url = url.slice(0, -1) - config.url = url - } - return new Promise((resolve, reject) => { - uni.request({ - method: config.method || 'GET', - timeout: config.timeout || timeout, - url: (config.baseUrl || baseUrl) + config.url, - data: config.data, - header: config.header, - dataType: 'json' - }).then(response => { - /* let [error, res] = response - if (error) { - toast('后端接口连接异常') - reject('后端接口连接异常') - return - } */ - const res = response - const data: ResponseData = res.data as ResponseData - const code = data.code || 200 - // @ts-ignore - const msg: string = errorCode[code] || data.msg || errorCode['default'] - if (code === 401) { - showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => { - if (res.confirm) { - useUserStore().logOut().then(res => { - uni.reLaunch({ url: '/pages/login' }) - }) - } - }) - reject('无效的会话,或者会话已过期,请重新登录。') - } else if (code === 500) { - toast(msg) - reject('500') - } else if (code !== 200) { - toast(msg) - reject(code) - } - resolve(data) - }) - .catch(error => { - // === 修复这里的问题 === - let message = '网络请求失败' - - // 安全地获取错误信息 - if (error) { - // 小程序环境错误对象可能有不同的结构 - if (error.errMsg) { - message = error.errMsg - } else if (error.message) { - message = error.message - } else if (typeof error === 'string') { - message = error - } - } - - // 安全地使用 includes 方法 - 添加类型检查 - if (message && typeof message === 'string') { - if (message.includes('Network Error')) { - message = '后端接口连接异常' - } else if (message.includes('timeout')) { - message = '系统接口请求超时' - } else if (message.includes('Request failed with status code')) { - message = '系统接口' + message.substr(message.length - 3) + '异常' - } - } else { - // 如果 message 不是字符串,转换为字符串 - message = String(message || '未知错误') - } - toast(message) - reject(error) - }) - }) +const request = (config : RequestConfig) : Promise> => { + // 是否需要设置 token + const isToken = (config.headers || {}).isToken === false + config.header = config.header || {} + if (getToken() && !isToken) { + config.header['Authorization'] = 'Bearer ' + getToken() + } + // get请求映射params参数 + if (config.params) { + let url = config.url + '?' + tansParams(config.params) + url = url.slice(0, -1) + config.url = url + } + return new Promise((resolve, reject) => { + uni.request({ + method: config.method || 'GET', + timeout: config.timeout || timeout, + url: (config.baseUrl || baseUrl) + config.url, + data: config.data, + header: config.header, + dataType: 'json' + }).then(response => { + /* let [error, res] = response + if (error) { + toast('后端接口连接异常') + reject('后端接口连接异常') + return + } */ + const res = response + const data : ResponseData = res.data as ResponseData + const code = data.code || 200 + // @ts-ignore + const msg : string = errorCode[code] || data.msg || errorCode['default'] + if (code === 401) { + showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => { + if (res.confirm) { + useUserStore().logOut().then(res => { + uni.reLaunch({ url: '/pages/login' }) + }) + } + }) + reject('无效的会话,或者会话已过期,请重新登录。') + } else if (code === 500) { + toast(msg) + reject('500') + } else if (code !== 200) { + toast(msg) + reject(code) + } + resolve(data) + }) + .catch(error => { + // === 修复这里的问题 === + let message = '网络请求失败' + + + // === 重点修复:安全提取错误信息 === + if (error && typeof error === 'object') { + // 优先使用后端返回的错误消息 + if (error.data?.msg) message = error.data.msg; + // 小程序特有的错误结构 + else if (error.errMsg) message = error.errMsg; + // 常规错误消息 + else if (error.message) message = error.message; + } else if (typeof error === 'string') { + message = error; + } + + // 特殊错误处理 + if (message.includes('Network Error')) { + message = '网络连接异常'; + } else if (message.includes('timeout')) { + message = '请求超时,请检查网络'; + } + + toast(message); + + // === 关键:返回带code的错误对象 === + const err = new Error(message); + err.code = error.statusCode || 500; // 保留HTTP状态码 + reject(err); + }) + }) +} +export function postAction(url : string, data ?: any, isToken : boolean = true) { + return request({ data, url, method: 'POST', headers: { isToken }, }) +} +export function getAction(url : string, params ?: any, isToken : boolean = true) { + return request({ params, url, method: 'GET', headers: { isToken }, }) +} +export function putAction(url : string, data ?: any, isToken : boolean = true) { + return request({ data, url, method: 'PUT', headers: { isToken }, }) +} +export function deleteAction(url : string, data ?: any, isToken : boolean = true) { + return request({ data, url, method: 'DELETE', headers: { isToken }, }) } -export function postAction(url: string, data?: any, isToken: boolean = true) { - return request({ data, url, method: 'POST', headers: { isToken }, }) -} -export function getAction(url: string, params?: any, isToken: boolean = true) { - return request({ params, url, method: 'GET', headers: { isToken }, }) -} -export function putAction(url: string, data?: any, isToken: boolean = true) { - return request({ data, url, method: 'PUT', headers: { isToken }, }) -} -export function deleteAction(url: string, data?: any, isToken: boolean = true) { - return request({ data, url, method: 'DELETE', headers: { isToken }, }) -} - -export default request +export default request \ No newline at end of file diff --git a/src/vite.config.js b/src/vite.config.js index 1d873bb..e84266a 100644 --- a/src/vite.config.js +++ b/src/vite.config.js @@ -25,4 +25,23 @@ export default defineConfig(() => { } } } -}) \ No newline at end of file +}) + +module.exports = { + configureWebpack: { + optimization: { + splitChunks: { + chunks: 'async', // 仅异步代码抽离 + minSize: 20000, // 大于20KB才抽离 + cacheGroups: { + commons: { + name: 'commons', + chunks: 'initial', + minChunks: 2, // 被引用2次以上抽离 + priority: -10 + } + } + } + } + } +} \ No newline at end of file