Files
Aida_Purchaser_Front/src/views/setting/useSettingsForm.ts
2026-05-11 11:28:59 +08:00

289 lines
7.4 KiB
TypeScript

import { computed, ref, shallowRef, watch, type Ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
languageValues,
regionValues,
roleValues,
type LanguageValue,
type RegionValue,
type RoleValue,
type SecurityDraft,
type SettingsData
} from './types'
type Translate = (key: string, ...args: unknown[]) => string
interface UseSettingsFormOptions {
t: Translate
locale: Ref<string>
}
const languageLocaleMap: Record<LanguageValue, 'ENGLISH' | 'CHINESE_SIMPLIFIED'> = {
english: 'ENGLISH',
chinese: 'CHINESE_SIMPLIFIED'
}
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const createDefaultData = (): SettingsData => ({
firstName: 'Alexandra',
lastName: 'Chen',
email: 'alex.chen@gmail.com',
username: '@alexandra_chen',
role: ['student', 'graphicDesigner'],
language: 'english',
region: 'hongKongSar'
})
const cloneSettingsData = (data: SettingsData): SettingsData => ({
firstName: data.firstName,
lastName: data.lastName,
email: data.email,
username: data.username,
role: [...data.role],
language: data.language,
region: data.region
})
const createEmptySecurityDraft = (): SecurityDraft => ({
newEmail: '',
newPassword: '',
currentPassword: ''
})
export function useSettingsForm({ t, locale }: UseSettingsFormOptions) {
const sourceData = ref<SettingsData>(createDefaultData())
const draftData = ref<SettingsData>(cloneSettingsData(sourceData.value))
const securityDraft = ref<SecurityDraft>(createEmptySecurityDraft())
const isEditing = shallowRef(false)
const saving = shallowRef(false)
const isVerificationDialogVisible = shallowRef(false)
const verificationTargetEmail = shallowRef('')
const verifiedEmail = shallowRef('')
const roleList = computed(() =>
roleValues.map((value) => ({
name: t(`Settings.roles.${value}`),
value
}))
)
const languageList = computed(() =>
languageValues.map((value) => ({
label: t(`Settings.languages.${value}`),
value
}))
)
const regionList = computed(() =>
regionValues.map((value) => ({
label: t(`Settings.regions.${value}`),
value
}))
)
const displayData = computed(() => (isEditing.value ? draftData.value : sourceData.value))
const normalizedNewEmail = computed(() => securityDraft.value.newEmail.trim())
const hasNewEmailChange = computed(
() => normalizedNewEmail.value.length > 0 && normalizedNewEmail.value !== sourceData.value.email
)
const isEmailVerified = computed(
() => hasNewEmailChange.value && verifiedEmail.value === normalizedNewEmail.value
)
const displayLanguageLabel = computed(() => t(`Settings.languages.${displayData.value.language}`))
const displayRegionLabel = computed(() => t(`Settings.regions.${displayData.value.region}`))
const fullName = computed(() => {
const data = displayData.value
return `${data.firstName} ${data.lastName}`.trim()
})
const roleModel = computed<RoleValue[]>({
get: () => displayData.value.role,
set: (value) => {
if (isEditing.value) {
draftData.value.role = value
return
}
sourceData.value.role = value
}
})
const resetEmailVerificationState = () => {
isVerificationDialogVisible.value = false
verificationTargetEmail.value = ''
verifiedEmail.value = ''
}
const syncAppLanguage = (language: LanguageValue) => {
const nextLocale = languageLocaleMap[language]
locale.value = nextLocale
localStorage.setItem('language', nextLocale)
}
const resetDraftState = () => {
draftData.value = cloneSettingsData(sourceData.value)
securityDraft.value = createEmptySecurityDraft()
resetEmailVerificationState()
}
const handleEdit = () => {
resetDraftState()
isEditing.value = true
}
const resetSecurityEmail = () => {
securityDraft.value.newEmail = ''
resetEmailVerificationState()
}
const resetSecurityPassword = () => {
securityDraft.value.newPassword = ''
securityDraft.value.currentPassword = ''
}
const handleDiscard = () => {
resetDraftState()
isEditing.value = false
}
const closeVerificationDialog = () => {
isVerificationDialogVisible.value = false
verificationTargetEmail.value = ''
}
const handleVerifyEmail = () => {
const nextEmail = normalizedNewEmail.value
if (!nextEmail) {
ElMessage.warning(t('Settings.messages.enterNewEmailFirst'))
return
}
if (!emailPattern.test(nextEmail)) {
ElMessage.warning(t('Settings.messages.invalidEmail'))
return
}
if (nextEmail === sourceData.value.email) {
ElMessage.warning(t('Settings.messages.sameEmail'))
return
}
if (verifiedEmail.value === nextEmail) {
ElMessage.success(t('Settings.messages.alreadyVerified'))
return
}
verificationTargetEmail.value = nextEmail
handleSendVerifyCode()
isVerificationDialogVisible.value = true
}
const handleSendVerifyCode = () => {
ElMessage.success(t('Settings.messages.verificationCodeSent'))
}
const handleVerificationSubmit = (code: string) => {
if (code.length !== 6) {
ElMessage.warning(t('Settings.messages.enterVerificationCode'))
return
}
verifiedEmail.value = verificationTargetEmail.value
closeVerificationDialog()
ElMessage.success(t('Settings.messages.verificationCompleted'))
}
const buildNextData = (): SettingsData => {
const nextEmail = securityDraft.value.newEmail.trim() || draftData.value.email
return {
firstName: draftData.value.firstName.trim(),
lastName: draftData.value.lastName.trim(),
username: draftData.value.username.trim(),
email: nextEmail,
role: [...draftData.value.role],
language: draftData.value.language,
region: draftData.value.region
}
}
const handleSave = async () => {
if (hasNewEmailChange.value && !isEmailVerified.value) {
ElMessage.warning(t('Settings.messages.verifyEmailBeforeSave'))
return
}
const nextData = buildNextData()
const previousLanguage = sourceData.value.language
saving.value = true
try {
sourceData.value = cloneSettingsData(nextData)
if (nextData.language !== previousLanguage) {
syncAppLanguage(nextData.language)
}
draftData.value = cloneSettingsData(sourceData.value)
securityDraft.value = createEmptySecurityDraft()
resetEmailVerificationState()
isEditing.value = false
ElMessage.success(t('Settings.messages.settingsUpdated'))
} catch (error) {
console.warn(error)
} finally {
saving.value = false
}
}
watch(
() => securityDraft.value.newEmail,
(value) => {
const trimmedValue = value.trim()
if (verifiedEmail.value && trimmedValue !== verifiedEmail.value) {
verifiedEmail.value = ''
}
if (
isVerificationDialogVisible.value &&
verificationTargetEmail.value &&
trimmedValue !== verificationTargetEmail.value
) {
closeVerificationDialog()
}
}
)
return {
sourceData,
draftData,
securityDraft,
isEditing,
saving,
isVerificationDialogVisible,
verificationTargetEmail,
roleList,
languageList,
regionList,
displayData,
isEmailVerified,
displayLanguageLabel,
displayRegionLabel,
fullName,
roleModel,
handleEdit,
handleDiscard,
handleSave,
resetSecurityEmail,
resetSecurityPassword,
handleVerifyEmail,
handleSendVerifyCode,
handleVerificationSubmit,
closeVerificationDialog
}
}