feat(account): 按原型完善左下角账户菜单与个人信息

This commit is contained in:
xy
2026-10-03 11:23:50 +08:00
parent eb4693cf65
commit 05c5723419
7 changed files with 442 additions and 18 deletions
@@ -0,0 +1,121 @@
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<CurrentUserResponse | null>(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 }
}