| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- <script setup lang="ts">
- import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
- import { API_CONFIG } from './utils/config';
- import { handleUnauthorized, clearAuth } from './utils/auth';
- import { logger } from './utils/logger';
- /**
- * 应用启动
- * 登录状态检查采用非阻塞模式,避免网络延迟拖慢启动速度
- */
- onLaunch((options: any) => {
- logger.log("App Launch, options:", JSON.stringify(options));
- checkLoginStatus(options);
- });
- /**
- * 应用显示
- */
- onShow(() => {
- logger.log("App Show");
- });
- /**
- * 应用隐藏
- */
- onHide(() => {
- logger.log("App Hide");
- // 当应用隐藏时,清理购物页面的轮询状态
- const pollingActive = uni.getStorageSync('shoppingPollingActive');
- if (pollingActive === 'true') {
- logger.log('应用隐藏,清理购物页面轮询状态');
- uni.removeStorageSync('shoppingPollingActive');
- }
- });
- /**
- * 免登录白名单页面
- * 这些页面允许未登录用户直接访问
- */
- const LOGIN_WHITE_LIST = [
- 'pages/login/login',
- 'pages/products/products',
- ];
- /**
- * 验证token是否有效
- * 调用后端用户信息接口校验token,最多等待3秒,
- * 超时则假定token有效(由后续API请求的401拦截兜底)
- */
- const verifyToken = (): Promise<boolean> => {
- return new Promise((resolve) => {
- const token = uni.getStorageSync('accessToken');
- if (!token) {
- resolve(false);
- return;
- }
- let settled = false;
- const done = (result: boolean) => {
- if (!settled) {
- settled = true;
- resolve(result);
- }
- };
- const url = `${API_CONFIG.baseUrl}/login/user-info`;
- uni.request({
- url,
- method: 'POST',
- header: {
- 'Content-Type': 'application/json',
- 'accessToken': token
- },
- timeout: 5000,
- success: (res: any) => {
- if (res.statusCode === 200) {
- const data = res.data;
- if (data && (data.code === 200 || data.code === 0)) {
- done(true);
- } else if (data && data.code === 401) {
- logger.log('[App] token无效:', data.message);
- done(false);
- } else {
- logger.warn('[App] 验证token返回异常状态:', data?.code, data?.message);
- done(true);
- }
- } else {
- logger.warn('[App] 验证token网络异常, statusCode:', res.statusCode);
- done(true);
- }
- },
- fail: (err: any) => {
- logger.warn('[App] 验证token请求失败:', err.errMsg);
- done(true);
- }
- });
- // 竞速兜底:最多等3秒,超时假定token有效
- setTimeout(() => done(true), 3000);
- });
- };
- /**
- * 检查登录状态
- * 如果未登录或token无效且当前不在白名单页面,则跳转到登录页
- */
- const checkLoginStatus = async (launchOptions?: any) => {
- const token = uni.getStorageSync('accessToken');
- // 优先从启动参数获取目标页面路径(onLaunch时getCurrentPages为空)
- let currentPath = launchOptions?.path || '';
- if (!currentPath) {
- const pages = getCurrentPages();
- const currentPage = pages[pages.length - 1];
- currentPath = currentPage ? currentPage.route : '';
- }
- logger.log('[App] 检查登录状态 - token:', token ? '已存在' : '不存在', ', 目标页面:', currentPath);
- const isWhiteListed = LOGIN_WHITE_LIST.some(path => currentPath.indexOf(path) !== -1);
- // 没有token
- if (!token) {
- if (!isWhiteListed) {
- logger.log('[App] 未登录,跳转到登录页');
- uni.reLaunch({ url: '/pages/login/login' });
- }
- return;
- }
- // 有token,向后端验证是否有效
- logger.log('[App] token存在,验证token有效性...');
- const isValid = await verifyToken();
- if (!isValid && !isWhiteListed) {
- // token无效且不在白名单 -> 触发全局未授权处理
- logger.log('[App] token无效,触发全局未授权处理');
- handleUnauthorized();
- } else if (!isValid && isWhiteListed) {
- // 白名单页面只清除数据不跳转(不触发全局未授权处理的重定向)
- logger.log('[App] token无效,清除登录数据(当前在白名单页面)');
- clearAuth();
- } else {
- logger.log('[App] token有效');
- }
- };
- </script>
- <style>
- /* 全局动画样式 */
- @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
- @keyframes slideUp { from { opacity: 0; transform: translateY(40rpx); } to { opacity: 1; transform: translateY(0); } }
- @keyframes slideDown { from { opacity: 0; transform: translateY(-40rpx); } to { opacity: 1; transform: translateY(0); } }
- @keyframes scaleIn { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }
- @keyframes pulse { 0%, 100% { transform: scale(1); opacity: 0.3; } 50% { transform: scale(1.05); opacity: 0.5; } }
- @keyframes bounce { 0% { transform: scale(0); opacity: 0; } 50% { transform: scale(1.1); } 100% { transform: scale(1); opacity: 1; } }
- @keyframes skeleton { 0%, 100% { background-color: #F5F5F5; } 50% { background-color: #EEEEEE; } }
- </style>
|