import {computed, ref} from 'vue' import { getTenantIdByName, login, logout, smsCode, smsLogin } from '@/api/login/login.js' import {ElLoading, ElMessage} from 'element-plus' import {refreshAllDictData} from './dictUtils.js'; // CryptoJS 库(用于密码加密) import CryptoJS from 'crypto-js'; // 消息工具 import {Message} from './message/Message.js'; import {CONFIG, refreshRoleRoute} from "@/utils/roleUtils.js"; import {homeRoutes, managementRoutes} from "@/router/index.js"; // 加密密钥 (从环境变量获取) const SECRET_KEY = import.meta.env.VITE_SECRET_KEY; // 是否开启租户模式 const tenantOpen = import.meta.env.VITE_APP_COMPULSORY_TENANT === 'true'; //需要删除的缓存key列表 const CACHE_KEYS_TO_DELETE = [ 'userId', 'token', 'isLoggedIn', CONFIG.USER_ROLE_ROUTE_KEY, CONFIG.USER_ROLE_ROUTE_KEY + CONFIG.EXPIRY_SUFFIX, CONFIG.USER_ROLE_ROUTE_MENU_KEY, CONFIG.USER_ROLE_ROUTE_MENU_KEY + CONFIG.EXPIRY_SUFFIX, ]; // 密码加密函数(使用AES加密) const encryptPassword = (password) => { // 将加密结果转换为字符串并返回 (encrypt加密) return CryptoJS.AES.encrypt(password, SECRET_KEY).toString(); }; // 密码解密函数 (接收加密后的密码字符串作为参数) const decryptPassword = (encryptedPassword) => { // (decrypt解密) const bytes = CryptoJS.AES.decrypt(encryptedPassword, SECRET_KEY); // 将解密结果转换为 UTF-8 编码的字符串并返回 return bytes.toString(CryptoJS.enc.Utf8); }; // 登录数据结构 const createLoginData = () => { return ref({ loginForm: { tenantName: tenantOpen ? (import.meta.env.VITE_APP_DEFAULT_LOGIN_TENANT || '') : null, username: import.meta.env.VITE_APP_DEFAULT_LOGIN_USERNAME || '', password: import.meta.env.VITE_APP_DEFAULT_LOGIN_PASSWORD || '', smsCode: '', // 短信验证码字段 rememberMe: false, // 记住 phoneNumber: '' // 手机号字段 } }) } // 验证码相关逻辑 const createVerificationCodeLogic = () => { const countingDown = ref(false) const countDown = ref(60) const sendingCode = ref(false) let countDownTimer = null // 开始倒计时 const startCountDown = () => { countingDown.value = true countDown.value = 60 if (countDownTimer) { clearInterval(countDownTimer) } countDownTimer = setInterval(() => { countDown.value-- if (countDown.value <= 0) { clearInterval(countDownTimer) countingDown.value = false } }, 1000) } // 清除计时器 const clearCountDownTimer = () => { if (countDownTimer) { clearInterval(countDownTimer) countDownTimer = null } } // 获取短信验证码 const getSmsCode = async (tenantId, tenantName, mobile) => { sendingCode.value = true try { // 构建请求头,只有当 tenantId 存在时才添加 Tenant-Id const headers = tenantId ? { 'Tenant-Id': tenantId } : {} const res = await smsCode( headers, { tenantName, mobile, scene: import.meta.env.VITE_APP_LOGIN_SMS_TEMPLATE_ID, } ) if (res.code === 0) { ElMessage.success('验证码发送成功') startCountDown() } else { ElMessage.error(res.message || '验证码发送失败') } } catch (error) { ElMessage.error('验证码发送失败,请重试') console.error('发送验证码错误:', error) } finally { sendingCode.value = false } } return { countingDown, countDown, sendingCode, getSmsCode, clearCountDownTimer } } // 获取租户ID const getTenantId = async (tenantName) => { try { const res = await getTenantIdByName(tenantName) if (res && res.data) { return res.data } else { ElMessage.error('租户填写错误!') return null } } catch (error) { ElMessage.error('租户填写错误!') console.error('获取租户 ID 错误:', error) return null } } // 登录逻辑 const loginLogic = async (loginForm, tenantId, isAuthorized, router) => { const loginLoading = ref(false) const loading = ref() const isLoggedIn = ref(false) loginLoading.value = true try { let res // 构建请求头,只有当 tenantId 存在时才添加 Tenant-Id const headers = tenantId ? { 'Tenant-Id': tenantId } : {} if (!isAuthorized.value) { // 未授权状态,使用短信验证码登录 res = await smsLogin( headers, { mobile: loginForm.phoneNumber, code: loginForm.smsCode, } ) } else { // 授权状态,使用账号密码登录 // 传输时使用明文密码,确保后端能正确处理 res = await login( headers, loginForm ) } if (!res) { return false } // 校验登录状态 if (res.code === 0) { // ElMessage.success('登录成功') isLoggedIn.value = true // 存储登录状态 localStorage.setItem('isLoggedIn', 'true') localStorage.setItem('token', res.data.accessToken) // 登录成功后存储用户ID if (res.data.userId) { localStorage.setItem('userId', res.data.userId) } // 总是存储用户名和租户名称 localStorage.setItem('tenantName', res.data.tenantName) // 存储记住我状态 localStorage.setItem('rememberMe', loginForm.rememberMe) if (loginForm.rememberMe) { localStorage.setItem('userName', loginForm.username) // 保存加密后的密码 localStorage.setItem('password', encryptPassword(loginForm.password)) } else { // 如果没有勾选记住我,清除密码 localStorage.removeItem('password') } loading.value = ElLoading.service({ lock: true, text: '正在加载系统中...', background: 'rgba(0, 0, 0, 0.7)' }) // 获取字典数据 await refreshDictData(); // 获取角色路由数据 await refreshRoleRouteData(); // 登录成功后,跳转到指定的页面 router.push(managementRoutes.home) return true } else if (res.code === 1002000009) { // 未授权状态,切换到短信验证码登录 ElMessage.warning(res.msg || '登录IP未被授权,请使用手机号短信验证码登录!') loginForm.phoneNumber = res.data?.mobile isAuthorized.value = false return false } else { ElMessage.error(res.msg || '登录失败,请检查账号密码!') return false } } catch (error) { ElMessage.error('登录出错,请重试!') console.error('登录错误:', error) return false } finally { loginLoading.value = false if (loading.value) { loading.value.close() } } } // 本地存储管理 const loadLoginData = (loginData) => { const storedTenantName = localStorage.getItem('tenantName') const storedUserName = localStorage.getItem('userName') const storedPassword = localStorage.getItem('password') // 恢复登录信息到输入框 if (tenantOpen && storedTenantName) { loginData.value.loginForm.tenantName = storedTenantName } if (storedUserName) { loginData.value.loginForm.username = storedUserName } if (storedPassword) { // 解密密码并显示明文 loginData.value.loginForm.password = decryptPassword(storedPassword) loginData.value.loginForm.rememberMe = true } } // 检查登录状态 const checkLoginStatus = (router) => { const storedStatus = localStorage.getItem('isLoggedIn') if (storedStatus === 'true') { // 获取字典数据 refreshDictData(); // 获取角色路由数据 refreshRoleRouteData(); router.push(managementRoutes.home) return true } return false } // 表单验证规则生成 const generateRules = (isAuthorized) => { return computed(() => { if (isAuthorized.value) { // 授权状态:需要账号和密码 const rules = { username: [{ required: true, message: '请输入账号', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }] } if (tenantOpen) { rules.tenantName = [{ required: true, message: '请输入学校名称', trigger: 'blur' }] } return rules } else { // 未授权状态:需要手机号和短信验证码 const rules = { phoneNumber: [ { required: true, message: '请输入手机号', trigger: 'blur' }, { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' } ], smsCode: [{ required: true, message: '请输入短信验证码', trigger: 'blur' }] } if (tenantOpen) { rules.tenantName = [{ required: true, message: '请输入学校名称', trigger: 'blur' }] } return rules } }) } // 自动登录逻辑(用于QuickLogin和PromotionLogin) const autoLogin = async (tenantName, username, password, router, redirectPath) => { let loading = null try { // 显示全局加载状态 loading = ElLoading.service({ lock: true, text: '登录中...', background: 'rgba(0, 0, 0, 0.7)' }) // 获取租户ID,只有当 tenantOpen 为 true 时才获取 let tenantId = await getTenantId(tenantName) if (!tenantId) { // 租户验证失败 return false } // 构建请求头,只有当 tenantId 存在时才添加 Tenant-Id const headers = tenantId ? { 'Tenant-Id': tenantId } : {} // 执行登录 const res = await login( headers, { tenantName, username, password } ) if (res && res.code === 0) { // 登录成功,保存登录状态 localStorage.setItem('isLoggedIn', 'true') localStorage.setItem('token', res.data.accessToken) localStorage.setItem('userName', username) localStorage.setItem('tenantName', tenantName) localStorage.setItem('password', encryptPassword(password)) localStorage.setItem('rememberMe', 'true') // 获取字典数据 await refreshDictData(); // 获取角色路由数据 await refreshRoleRouteData(); ElMessage.success('信息校验成功') // 跳转到课程界面 router.push(redirectPath) return true } else { ElMessage.error(res?.message || '信息校验失败') // 如果登录失败,跳转到正常登录页面 router.push(homeRoutes.login) return false } } catch (error) { ElMessage.error('信息校验过程中发生错误') console.error('信息校验错误:', error) // 错误时跳转到登录页面 router.push(homeRoutes.login) return false } finally { // 关闭加载状态 if (loading) { loading.close() } } } // 退出登录逻辑 const logoutLogic = async (router, redirectPath = homeRoutes.login) => { try { // 调用logout API 退出登录 const logoutRes = await logout() console.log('退出登录:', logoutRes); //清除本地存储 removeLocalStorage(); // 检查是否勾选了记住我 const rememberMe = localStorage.getItem('rememberMe') === 'true' if (!rememberMe) { // 未勾选记住我,清空所有信息 localStorage.removeItem('userName') localStorage.removeItem('tenantName') localStorage.removeItem('rememberMe') } // 跳转到登录页面 router.push({ path: redirectPath }) } catch (error) { console.error('退出登录失败:', error) // API调用失败,也清空本地存储 //清除本地存储 removeLocalStorage(); // 检查是否勾选了记住我 const rememberMe = localStorage.getItem('rememberMe') === 'true' if (!rememberMe) { // 未勾选记住我,清空所有信息 localStorage.removeItem('userName') localStorage.removeItem('tenantName') localStorage.removeItem('rememberMe') } Message().notifyError('退出登录失败,请重试', true) router.push({ path: homeRoutes.login }) } } //清除本地存储 const removeLocalStorage = () => { // 清空登录状态相关信息 CACHE_KEYS_TO_DELETE.forEach(key => localStorage.removeItem(key)); removeLocalStorageKey(localStorage.getItem('token')) } const removeLocalStorageKey = (remKey) => { if (remKey) { for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key && key.startsWith(remKey)) { localStorage.removeItem(key); } } } } // 添加获取字典数据的函数 const refreshDictData = async () => { try { // 使用典管理工具更新所有字典 await refreshAllDictData(); } catch (error) { console.error('获取字典数据失败:', error); } }; // 添加获取角色路由数据的函数 const refreshRoleRouteData = async () => { try { // 使用角色路由工具更新角色路由 await refreshRoleRoute(); } catch (error) { console.error('获取角色路由数据失败:', error); } }; export { createLoginData, createVerificationCodeLogic, getTenantId, loginLogic, loadLoginData, checkLoginStatus, generateRules, autoLogin, logoutLogic, removeLocalStorageKey }