| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- 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} 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,
- ];
- // 密码加密函数(使用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, redirectPath) => {
- 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('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(redirectPath)
- 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, redirectPath) => {
- const storedStatus = localStorage.getItem('isLoggedIn')
- if (storedStatus === 'true') {
- // 获取字典数据
- refreshDictData();
- // 获取角色路由数据
- refreshRoleRouteData();
- router.push(redirectPath)
- 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')
- 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
- }
|