import type { LoginAndRegisterParams } from '@vben/common-ui'; import type { UserInfo } from '@vben/types'; import type { UserInfoResp } from '#/api/core/user'; import { ref } from 'vue'; import { useRouter } from 'vue-router'; import { LOGIN_PATH } from '@vben/constants'; import { preferences } from '@vben/preferences'; import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; import { notification } from 'ant-design-vue'; import { defineStore } from 'pinia'; import { doLogout, getUserInfoApi, loginApi, seeConnectionClose } from '#/api'; import { $t } from '#/locales'; import { useDictStore } from './dict'; import { useThTenantStore } from './th-tenant'; /** 泰额登录后 account 不可用时,用登录邮箱/租户名构造最小 UserInfo */ function buildThFallbackUserInfo( params: LoginAndRegisterParams & { email?: string }, ctx: { tenantName?: string }, ): UserInfo { const email = params.email ?? ('username' in params ? params.username : '') ?? ''; const realName = ctx.tenantName?.trim() || email.split('@')[0] || 'User'; return { avatar: '', permissions: [], realName, roles: [], userId: '0', username: email, email, }; } function mapUserInfoResp(backUserInfo: UserInfoResp): UserInfo { const { permissionCodes = [], roleCodes = [], user } = backUserInfo; return { avatar: user.avatar ?? '', permissions: permissionCodes, realName: user.nick, roles: roleCodes, userId: user.userId, username: user.userName, email: user.email ?? '', }; } export const useAuthStore = defineStore('auth', () => { const accessStore = useAccessStore(); const userStore = useUserStore(); const router = useRouter(); const loginLoading = ref(false); /** * 异步处理登录操作 * Asynchronously handle the login process * @param params 登录表单数据 */ async function authLogin( params: LoginAndRegisterParams, onSuccess?: () => Promise | void, ) { let userInfo: null | UserInfo = null; let loginOk = false; try { loginLoading.value = true; const { token, refreshToken, tenantId, tenantName, locations } = await loginApi(params); if (!token?.trim()) { throw new Error('登录失败:未返回 token'); } accessStore.setAccessToken(token); accessStore.setRefreshToken(refreshToken); const thTenantStore = useThTenantStore(); thTenantStore.setTenantContext({ tenantId: tenantId ?? params.tenantId, tenantName: tenantName ?? '', locations: locations ?? [], }); userInfo = buildThFallbackUserInfo(params, { tenantName: tenantName ?? '', }); userStore.setUserInfo(userInfo); accessStore.setAccessCodes(userInfo.permissions); loginOk = true; } finally { // 必须在 router.push 之前结束 loading:守卫里拉菜单可能很慢/挂起,否则会一直转圈 loginLoading.value = false; } if (!loginOk || !userInfo) { return { userInfo }; } try { if (accessStore.loginExpired) { accessStore.setLoginExpired(false); } else if (onSuccess) { await onSuccess?.(); } else { await router.push(preferences.app.defaultHomePath); } notification.success({ description: `${$t('authentication.loginSuccessDesc')}:${userInfo.realName}`, duration: 3, message: $t('authentication.loginSuccess'), }); } catch (error) { console.error('[th-auth] 登录后跳转失败', error); } return { userInfo }; } async function logout(redirect: boolean = true) { try { await seeConnectionClose(); await doLogout(); } catch (error) { console.error(error); } finally { resetAllStores(); accessStore.setLoginExpired(false); // 回登陆页带上当前路由地址 await router.replace({ path: LOGIN_PATH, query: redirect ? { redirect: encodeURIComponent(router.currentRoute.value.fullPath), } : {}, }); } } async function fetchUserInfo() { const backUserInfo = await getUserInfoApi(); /** * 登录超时的情况 */ if (!backUserInfo) { throw new Error('获取用户信息失败.'); } const userInfo = mapUserInfoResp(backUserInfo); userStore.setUserInfo(userInfo); /** * 需要重新加载字典 * 比如退出登录切换到其他租户 */ const dictStore = useDictStore(); dictStore.resetCache(); return userInfo; } function $reset() { loginLoading.value = false; } return { $reset, authLogin, fetchUserInfo, loginLoading, logout, }; });