122 lines
4.3 KiB
TypeScript
122 lines
4.3 KiB
TypeScript
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 }
|
|
}
|