261 lines
6.5 KiB
TypeScript
261 lines
6.5 KiB
TypeScript
/**
|
||
* 登录逻辑 Composable
|
||
*/
|
||
|
||
import { ref, reactive, computed, onMounted } from 'vue'
|
||
import { useRouter, useRoute } from 'vue-router'
|
||
import { useI18n } from 'vue-i18n'
|
||
import { ElMessage, ElNotification } from 'element-plus'
|
||
import type { FormInstance, FormRules } from 'element-plus'
|
||
import { useUserStore } from '@/store/modules/user'
|
||
import { HOME_PAGE } from '@/router/routesAlias'
|
||
import AppConfig from '@/config'
|
||
import { AuthService } from '@/api/modules'
|
||
import { ApiStatus } from '@/utils/http/status'
|
||
import { MOCK_ACCOUNTS, mockLogin, mockGetUserInfo, type MockAccount } from '@/mock/auth'
|
||
import { saveCredentials, getRememberedCredentials } from '@/utils/auth/rememberPassword'
|
||
import { usernameRules, passwordRules } from '@/utils/auth/loginValidation'
|
||
import { getRedirectPath } from '@/router/guards/permission'
|
||
|
||
// 开发模式:是否使用 Mock 数据
|
||
const USE_MOCK = false
|
||
|
||
/**
|
||
* 登录表单数据
|
||
*/
|
||
interface LoginForm {
|
||
account: string
|
||
username: string
|
||
password: string
|
||
rememberPassword: boolean
|
||
}
|
||
|
||
export function useLogin() {
|
||
const { t } = useI18n()
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
const userStore = useUserStore()
|
||
|
||
// 表单引用
|
||
const formRef = ref<FormInstance>()
|
||
|
||
// 加载状态
|
||
const loading = ref(false)
|
||
|
||
// 表单数据
|
||
const formData = reactive<LoginForm>({
|
||
account: '',
|
||
username: '',
|
||
password: '',
|
||
rememberPassword: true
|
||
})
|
||
|
||
// 验证规则
|
||
const rules = computed<FormRules>(() => ({
|
||
username: usernameRules(t),
|
||
password: passwordRules(t)
|
||
}))
|
||
|
||
// Mock 账号列表
|
||
const mockAccounts = computed<MockAccount[]>(() => MOCK_ACCOUNTS)
|
||
|
||
/**
|
||
* 初始化表单
|
||
*/
|
||
const initForm = () => {
|
||
// 尝试加载记住的密码
|
||
const remembered = getRememberedCredentials()
|
||
if (remembered) {
|
||
formData.username = remembered.username
|
||
formData.password = remembered.password
|
||
formData.rememberPassword = remembered.rememberPassword
|
||
|
||
// 如果使用 Mock,尝试匹配 Mock 账号
|
||
if (USE_MOCK) {
|
||
const matchedAccount = MOCK_ACCOUNTS.find((acc) => acc.username === remembered.username)
|
||
if (matchedAccount) {
|
||
formData.account = matchedAccount.key
|
||
}
|
||
}
|
||
} else {
|
||
// 开发环境:使用真实 API 默认账号
|
||
if (import.meta.env.MODE === 'development') {
|
||
formData.username = 'admin'
|
||
formData.password = 'Admin@123456'
|
||
formData.rememberPassword = false
|
||
}
|
||
// 生产环境:不填充默认值
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置 Mock 账号
|
||
*/
|
||
const setupAccount = (key: string) => {
|
||
const selectedAccount = MOCK_ACCOUNTS.find((account) => account.key === key)
|
||
if (selectedAccount) {
|
||
formData.account = key
|
||
formData.username = selectedAccount.username
|
||
formData.password = selectedAccount.password
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 执行登录
|
||
*/
|
||
const handleLogin = async () => {
|
||
if (!formRef.value) return
|
||
|
||
try {
|
||
// 验证表单
|
||
const valid = await formRef.value.validate()
|
||
if (!valid) return
|
||
|
||
loading.value = true
|
||
|
||
// 判断使用 Mock 还是真实 API
|
||
if (USE_MOCK) {
|
||
await handleMockLogin()
|
||
} else {
|
||
await handleRealLogin()
|
||
}
|
||
} catch (error: any) {
|
||
console.error('登录失败:', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Mock 登录(开发测试用)
|
||
*/
|
||
const handleMockLogin = async () => {
|
||
const loginResult = mockLogin(formData.username, formData.password)
|
||
|
||
if (!loginResult) {
|
||
console.log(t('login.error.invalidCredentials'))
|
||
return
|
||
}
|
||
|
||
// 保存 Token
|
||
userStore.setToken(loginResult.access_token, loginResult.refresh_token)
|
||
|
||
// 获取用户信息
|
||
const userInfo = mockGetUserInfo(loginResult.access_token)
|
||
|
||
if (!userInfo) {
|
||
console.log(t('login.error.getUserInfoFailed'))
|
||
return
|
||
}
|
||
|
||
// 保存用户信息
|
||
userStore.setUserInfo(userInfo)
|
||
userStore.setLoginStatus(true)
|
||
|
||
// 保存记住密码
|
||
saveCredentials(formData.username, formData.password, formData.rememberPassword)
|
||
|
||
// 显示登录成功提示
|
||
showLoginSuccessNotice()
|
||
|
||
// 跳转到重定向页面或首页
|
||
const redirectPath = getRedirectPath(route)
|
||
await router.push(redirectPath || HOME_PAGE)
|
||
}
|
||
|
||
/**
|
||
* 真实 API 登录
|
||
*/
|
||
const handleRealLogin = async () => {
|
||
// 调用登录接口
|
||
const response = await AuthService.login({
|
||
username: formData.username,
|
||
password: formData.password
|
||
})
|
||
|
||
// 检查响应状态
|
||
if (response.code !== ApiStatus.success) {
|
||
console.log(response.msg || t('login.error.invalidCredentials'))
|
||
return
|
||
}
|
||
|
||
// 检查返回数据
|
||
if (!response.data || !response.data.access_token) {
|
||
console.log(t('login.error.loginFailed'))
|
||
return
|
||
}
|
||
|
||
// 检查用户类型 - 禁止企业账号登录
|
||
if (response.data.user && response.data.user.user_type === 4) {
|
||
console.log('企业账号无法登录后台管理系统')
|
||
return
|
||
}
|
||
|
||
// 保存 Token
|
||
userStore.setToken(response.data.access_token, response.data.refresh_token)
|
||
|
||
// 保存用户信息
|
||
if (response.data.user) {
|
||
userStore.setUserInfo(response.data.user)
|
||
userStore.setLoginStatus(true)
|
||
} else {
|
||
console.log(t('login.error.getUserInfoFailed'))
|
||
return
|
||
}
|
||
|
||
// 保存权限、菜单和按钮
|
||
if (response.data.permissions) {
|
||
userStore.setPermissions(response.data.permissions)
|
||
}
|
||
if (response.data.menus) {
|
||
userStore.setMenus(response.data.menus)
|
||
}
|
||
if (response.data.buttons) {
|
||
userStore.setButtons(response.data.buttons)
|
||
}
|
||
|
||
// 保存记住密码
|
||
saveCredentials(formData.username, formData.password, formData.rememberPassword)
|
||
|
||
// 显示登录成功提示
|
||
showLoginSuccessNotice()
|
||
|
||
// 等待数据持久化完成
|
||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||
|
||
// 跳转到重定向页面或首页
|
||
const redirectPath = getRedirectPath(route)
|
||
await router.push(redirectPath || HOME_PAGE)
|
||
}
|
||
|
||
/**
|
||
* 显示登录成功通知
|
||
*/
|
||
const showLoginSuccessNotice = () => {
|
||
setTimeout(() => {
|
||
ElNotification({
|
||
title: t('login.success.title'),
|
||
type: 'success',
|
||
duration: 2500,
|
||
zIndex: 10000,
|
||
message: `${t('login.success.message')}, ${AppConfig.systemInfo.name}!`
|
||
})
|
||
}, 150)
|
||
}
|
||
|
||
// 组件挂载时初始化
|
||
onMounted(() => {
|
||
initForm()
|
||
})
|
||
|
||
return {
|
||
formRef,
|
||
formData,
|
||
rules,
|
||
loading,
|
||
mockAccounts,
|
||
setupAccount,
|
||
handleLogin
|
||
}
|
||
}
|