import { computed, reactive, ref } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import { toUserMessage } from '@/utils/api-response' import { getCurrentUser, updateProfile } from '@/api/auth' import { useLoginStore } from '@/stores/login' import type { CurrentUserResponse } from '@/types/auth' export function useAccountPanel() { const loginStore = useLoginStore() const router = useRouter() const panel = ref<'profile' | 'password' | null>(null) const profile = ref(null) const loading = ref(false) const saving = ref(false) const error = ref('') const contacts = reactive({ phone: '', email: '' }) const password = reactive({ current: '', next: '', confirm: '' }) const strength = computed(() => { const value = password.next if (!value) return '—' if ( value.length < 12 || !/[A-Z]/.test(value) || !/[a-z]/.test(value) || !/\d/.test(value) || !/[^A-Za-z0-9]/.test(value) ) return '弱(未满足要求)' return value.length >= 16 ? '很强' : '强' }) let loadId = 0 async function open(kind: 'profile' | 'password') { const currentLoad = ++loadId loading.value = false error.value = '' profile.value = null Object.assign(contacts, { phone: '', email: '' }) Object.assign(password, { current: '', next: '', confirm: '' }) panel.value = kind if (kind !== 'profile') return loading.value = true try { const result = await getCurrentUser() if (currentLoad !== loadId) return profile.value = result contacts.phone = profile.value.phone ?? '' contacts.email = profile.value.email ?? '' } catch (cause) { if (currentLoad === loadId) error.value = toUserMessage(cause, '个人信息加载失败,请重试') } finally { if (currentLoad === loadId) loading.value = false } } function close() { if (saving.value) return loadId++ panel.value = null Object.assign(password, { current: '', next: '', confirm: '' }) } async function save() { if (saving.value || loading.value) return error.value = '' if (panel.value === 'password') { if (!password.current) error.value = '请输入当前密码' else if (strength.value.startsWith('弱') || !password.next || password.next.length > 128) error.value = '新密码须为 12–128 位,同时包含大小写字母、数字和特殊字符' else if (password.next !== password.confirm) error.value = '两次输入的新密码不一致' else if (password.current === password.next) error.value = '新密码不能与当前密码相同' } else { if (!profile.value) return const phone = contacts.phone.trim() const email = contacts.email.trim() if (phone !== (profile.value.phone ?? '') && phone && !/^[0-9+() -]{5,32}$/.test(phone)) error.value = '请输入有效的完整手机号' else if ( email !== (profile.value.email ?? '') && email && (email.includes('*') || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) ) error.value = '请输入有效的完整邮箱' } if (error.value) return saving.value = true try { if (panel.value === 'password') { await loginStore.changePassword({ currentPassword: password.current, newPassword: password.next, }) Object.assign(password, { current: '', next: '', confirm: '' }) panel.value = null await router.replace({ name: 'login', query: { passwordChanged: '1' } }) } else if (profile.value) { await updateProfile({ phone: contacts.phone.trim() === (profile.value.phone ?? '') ? undefined : contacts.phone.trim(), email: contacts.email.trim() === (profile.value.email ?? '') ? undefined : contacts.email.trim(), expectedVersion: profile.value.version, }) panel.value = null ElMessage.success('联系方式已保存') } } catch (cause) { error.value = toUserMessage(cause, '保存失败,请稍后重试') } finally { saving.value = false } } return { panel, profile, loading, saving, error, contacts, password, strength, open, close, save } }