Files
hzdyjs/mobile/src/utils/user.js
T
zlwu afe3e234e9 feat(mobile): 实现全局路由守卫功能,自动处理用户未登录跳转
✨ 新增功能:
- 全局路由守卫模块 (route-guard.js)
- 多层次权限验证 (登录状态+Token存在+Token过期检查)
- 自动拦截机制 (页面级+跳转级+HTTP级+应用级)
- 401响应自动处理和跳转
- 路由守卫测试页面

🔧 技术实现:
- checkPageAccess() 全局页面权限检查
- guardedNavigate/guardedSwitchTab 带权限检查的跳转
- handleUnauthorized() HTTP拦截器401处理
- 公开页面配置管理
- 优雅的错误提示和跳转逻辑

📱 页面集成:
- App.vue 初始化路由守卫
- profile/workbench 页面添加权限检查
- index 页面优化跳转逻辑
- 新增测试页面验证功能

📚 文档完善:
- 移动端路由守卫使用指南
- 详细的变更记录和技术说明
- 最佳实践和注意事项

🎯 核心效果:
用户登录失效或未登录时,访问任何需要权限的页面都自动跳转到登录页,提升安全性和用户体验。
2025-07-04 22:28:49 +08:00

316 lines
8.0 KiB
JavaScript

/**
* 用户管理工具
* 处理用户信息、权限、状态等相关功能
*/
import { userStorage } from './request';
/**
* 用户信息管理
*/
export const userManager = {
/**
* 获取当前用户信息
*/
getCurrentUser() {
const userInfo = userStorage.getUserInfo();
if (!userInfo || !userInfo.user) {
return null;
}
const user = userInfo.user;
const permissions = userInfo.permissions || [];
const roles = userInfo.roles || [];
return {
// 基本信息
userId: user.userId,
userName: user.userName,
nickName: user.nickName || user.userName,
email: user.email,
phoneNumber: user.phoneNumber,
sex: user.sex,
avatar: user.avatar,
// 部门信息
deptId: user.deptId,
deptName: user.dept?.deptName || '',
// 状态信息
status: user.status,
createTime: user.createTime,
loginDate: user.loginDate,
// 权限信息
permissions,
roles,
// 扩展信息
remark: user.remark,
};
},
/**
* 检查用户是否拥有指定权限
* @param {string} permission 权限标识
*/
hasPermission(permission) {
if (!permission) return true;
const userInfo = this.getCurrentUser();
if (!userInfo || !userInfo.permissions) {
return false;
}
return userInfo.permissions.includes(permission);
},
/**
* 检查用户是否拥有指定角色
* @param {string} role 角色标识
*/
hasRole(role) {
if (!role) return true;
const userInfo = this.getCurrentUser();
if (!userInfo || !userInfo.roles) {
return false;
}
return userInfo.roles.some((r) => r.roleKey === role);
},
/**
* 检查用户是否为管理员
*/
isAdmin() {
return this.hasRole('admin') || this.hasRole('super_admin');
},
/**
* 获取用户显示名称
*/
getDisplayName() {
const user = this.getCurrentUser();
if (!user) return '未登录';
return user.nickName || user.userName || '用户';
},
/**
* 获取用户头像URL
* @param {string} defaultAvatar 默认头像URL
*/
getAvatarUrl(defaultAvatar = '/static/default-avatar.png') {
const user = this.getCurrentUser();
if (!user || !user.avatar) {
return defaultAvatar;
}
// 如果是完整URL,直接返回
if (user.avatar.startsWith('http')) {
return user.avatar;
}
// 拼接基础URL
const baseURL =
process.env.NODE_ENV === 'development'
? 'http://localhost:8080'
: 'https://your-api-domain.com';
return baseURL + user.avatar;
},
/**
* 更新用户本地信息
* @param {object} userData 用户数据
*/
updateUserInfo(userData) {
try {
const currentInfo = userStorage.getUserInfo();
if (currentInfo) {
const updatedInfo = {
...currentInfo,
user: {
...currentInfo.user,
...userData,
},
};
uni.setStorageSync('userInfo', JSON.stringify(updatedInfo));
console.log('用户信息已更新');
return true;
}
} catch (e) {
console.error('更新用户信息失败:', e);
}
return false;
},
/**
* 格式化用户信息用于显示
*/
formatUserForDisplay() {
const user = this.getCurrentUser();
if (!user) return null;
return {
name: this.getDisplayName(),
avatar: this.getAvatarUrl(),
department: user.deptName || '未分配部门',
phone: user.phoneNumber || '未设置',
email: user.email || '未设置',
status: user.status === '0' ? '正常' : '停用',
isAdmin: this.isAdmin(),
lastLoginTime: user.loginDate ? new Date(user.loginDate).toLocaleString() : '首次登录',
};
},
};
/**
* 权限检查指令工具
*/
export const permissionUtils = {
/**
* 检查页面访问权限
* @param {string} pagePath 页面路径
*/
checkPagePermission(pagePath) {
// 公开页面,无需权限检查
const publicPages = ['/pages/login/index', '/pages/index/index', '/pages/profile/index'];
if (publicPages.includes(pagePath)) {
return true;
}
// 检查是否已登录
if (!userStorage.isLoggedIn()) {
return false;
}
// 这里可以根据具体业务需求添加页面权限检查逻辑
return true;
},
/**
* 权限控制的页面跳转
* @param {string} url 目标页面URL
* @param {string} permission 所需权限
*/
navigateWithPermission(url, permission = null) {
// 检查登录状态
if (!userStorage.isLoggedIn()) {
uni.showToast({
title: '请先登录',
icon: 'none',
});
uni.reLaunch({
url: '/pages/login/index',
});
return false;
}
// 检查权限
if (permission && !userManager.hasPermission(permission)) {
uni.showToast({
title: '权限不足',
icon: 'none',
});
return false;
}
// 执行跳转
uni.navigateTo({
url,
fail: () => {
uni.showToast({
title: '页面跳转失败',
icon: 'none',
});
},
});
return true;
},
};
/**
* 用户状态管理
*/
export const userState = {
/**
* 检查Token是否即将过期(提前5分钟提醒)
*/
checkTokenExpiration() {
try {
const token = userStorage.getToken();
if (!token) return false;
// 这里可以解析JWT token获取过期时间
// 简化处理:检查登录时间是否超过2小时
const userInfo = userStorage.getUserInfo();
if (userInfo && userInfo.loginTime) {
const loginTime = new Date(userInfo.loginTime).getTime();
const now = Date.now();
const twoHours = 2 * 60 * 60 * 1000; // 2小时
return now - loginTime > twoHours;
}
} catch (e) {
console.error('检查Token过期时间失败:', e);
}
return false;
},
/**
* 刷新Token
*/
async refreshToken() {
try {
// 这里调用刷新Token的API
// const result = await api.refreshToken();
// if (result.code === 200) {
// uni.setStorageSync('access_token', result.data.access_token);
// return true;
// }
console.log('Token刷新功能待实现');
} catch (e) {
console.error('刷新Token失败:', e);
}
return false;
},
/**
* 自动登录检查
*/
autoLoginCheck() {
if (!userStorage.isLoggedIn()) {
return false;
}
// 检查Token是否过期
if (this.checkTokenExpiration()) {
uni.showModal({
title: '登录过期',
content: '您的登录已过期,请重新登录',
showCancel: false,
success: () => {
userStorage.clearLoginInfo();
uni.reLaunch({
url: '/pages/login/index',
});
},
});
return false;
}
return true;
},
};
export default {
userManager,
permissionUtils,
userState,
};