import { computed, ref } from 'vue' import { defineStore } from 'pinia' import { getBindStaffInfo, loginByMobile, type OrganizationMemberInfo } from '@/api/auth' import type { TokenResponse } from '@/types/api' import { AUTH_EXPIRED_EVENT, AUTH_STORAGE_KEYS, AUTH_TOKEN_UPDATED_EVENT, clearAuthStorage, persistAuthToken, readStoredAccessToken, readStoredRefreshToken } from '@/utils/authStorage' import { env } from '@/utils/env' export interface StaffUser { id?: string username?: string phone?: string tenantId?: string | number } const readUser = (): StaffUser | null => { const raw = localStorage.getItem(AUTH_STORAGE_KEYS.user) if (!raw) return null try { return JSON.parse(raw) as StaffUser } catch { return null } } const readMember = (): OrganizationMemberInfo | null => { const raw = localStorage.getItem(AUTH_STORAGE_KEYS.member) if (!raw) return null try { return JSON.parse(raw) as OrganizationMemberInfo } catch { return null } } export const useAuthStore = defineStore('auth', () => { const token = ref(localStorage.getItem(AUTH_STORAGE_KEYS.token) || '') const refreshToken = ref(localStorage.getItem(AUTH_STORAGE_KEYS.refreshToken) || '') const user = ref(readUser()) const memberInfo = ref(readMember()) const isLoggedIn = computed(() => Boolean(token.value)) const hasOrganization = computed(() => Boolean(memberInfo.value?.organizationId)) const persistToken = (payload: TokenResponse, phone: string) => { const storedToken = persistAuthToken(payload) token.value = storedToken.accessToken refreshToken.value = storedToken.refreshToken user.value = { id: payload.user_id, username: payload.username || phone, phone, tenantId: payload.tenant_id } localStorage.setItem(AUTH_STORAGE_KEYS.user, JSON.stringify(user.value)) } const login = async (phone: string, code: string) => { const payload = await loginByMobile(phone, code) persistToken(payload, phone) try { const member = await getBindStaffInfo() if (!member?.organizationId || !member?.memberId) { throw new Error('未绑定组织,请联系管理员') } memberInfo.value = member localStorage.setItem(AUTH_STORAGE_KEYS.member, JSON.stringify(member)) } catch (error) { logout() throw new Error('未绑定组织,请联系管理员') } } const resetState = () => { token.value = '' refreshToken.value = '' user.value = null memberInfo.value = null } const syncStoredToken = () => { token.value = readStoredAccessToken() refreshToken.value = readStoredRefreshToken() } const logout = () => { resetState() clearAuthStorage() } if (typeof window !== 'undefined') { window.addEventListener(AUTH_EXPIRED_EVENT, resetState) window.addEventListener(AUTH_TOKEN_UPDATED_EVENT, syncStoredToken) } return { token, refreshToken, user, memberInfo, hasOrganization, isLoggedIn, isMockMode: env.useMock, clientId: env.clientId, clientConfigId: env.clientConfigId, login, logout } })