feat:修改信息界面
This commit is contained in:
286
src/views/userInfo/profile/index.vue
Normal file
286
src/views/userInfo/profile/index.vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
import { message } from 'ant-design-vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import type { RuleObject } from 'ant-design-vue/es/form';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
interface UserProfile {
|
||||
username: string;
|
||||
fullname: string;
|
||||
birthdate: string;
|
||||
email: string;
|
||||
phonenumber: string;
|
||||
sex: string;
|
||||
}
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const formRef = ref<FormInstance>();
|
||||
const loading = ref(false);
|
||||
|
||||
const formState = ref<UserProfile>({
|
||||
username: '',
|
||||
fullname: '',
|
||||
birthdate: '',
|
||||
email: '',
|
||||
phonenumber: '',
|
||||
sex: '0'
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const rules = computed<{ [k: string]: RuleObject | RuleObject[] }>(() => {
|
||||
const validateUsername = async (_rule: RuleObject, value: string) => {
|
||||
if (value && (value.length < 4 || value.length > 20)) {
|
||||
return Promise.reject(t('page.login.register.usernameLengthLimit'));
|
||||
}
|
||||
const currentUsername = authStore.userInfo?.user?.userName;
|
||||
if (value && value !== currentUsername) {
|
||||
const { exists } = await authStore.checkUserRepeat({ username: value, authType: 'u' });
|
||||
if (exists) {
|
||||
return Promise.reject(t('page.login.register.usernameExists'));
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const validatePhone = async (_rule: RuleObject, value: string) => {
|
||||
if (!value) return Promise.resolve();
|
||||
|
||||
const phonePattern = /^1[3-9]\d{9}$/;
|
||||
if (!phonePattern.test(value)) {
|
||||
return Promise.reject(t('page.login.register.phoneInvalid'));
|
||||
}
|
||||
|
||||
const currentPhone = authStore.userInfo?.user?.phonenumber;
|
||||
if (value !== currentPhone) {
|
||||
const { exists } = await authStore.checkUserRepeat({ phonenumber: value, authType: 'u' });
|
||||
if (exists) {
|
||||
return Promise.reject(t('page.login.register.phoneExists'));
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const validateEmail = async (_rule: RuleObject, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.reject(t('page.login.register.emailRequired'));
|
||||
}
|
||||
|
||||
const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
|
||||
if (!emailPattern.test(value)) {
|
||||
return Promise.reject(t('page.login.register.emailInvalid'));
|
||||
}
|
||||
|
||||
const currentEmail = authStore.userInfo?.user?.email;
|
||||
if (value !== currentEmail) {
|
||||
const { exists } = await authStore.checkUserRepeat({ email: value, authType: 'u' });
|
||||
if (exists) {
|
||||
return Promise.reject(t('page.login.register.emailExists'));
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
return {
|
||||
username: [
|
||||
{ required: true, message: t('page.login.register.usernameRequired'), trigger: 'change' },
|
||||
{ validator: validateUsername, trigger: 'blur' }
|
||||
],
|
||||
fullname: [
|
||||
{ required: true, message: t('page.login.register.fullNameRequired'), trigger: 'blur' }
|
||||
],
|
||||
email: [
|
||||
{ required: true, message: t('page.login.register.emailRequired'), trigger: 'change' },
|
||||
{ validator: validateEmail, trigger: 'blur' }
|
||||
],
|
||||
phonenumber: [{ validator: validatePhone, trigger: 'blur' }],
|
||||
birthdate: [
|
||||
{ required: true, message: t('page.login.register.birthDateRequired') },
|
||||
{
|
||||
validator: (_rule: RuleObject, value: string) => {
|
||||
if (value && dayjs(value).isAfter(dayjs())) {
|
||||
return Promise.reject(t('page.login.register.birthDateInvalid'));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const userInfo = authStore.userInfo?.user;
|
||||
if (!userInfo) {
|
||||
message.error(t('page.profile.getUserInfoFailed'));
|
||||
return;
|
||||
}
|
||||
|
||||
formState.value = {
|
||||
username: userInfo.userName ?? '',
|
||||
fullname: userInfo.nickName ?? '',
|
||||
birthdate: '',
|
||||
email: userInfo.email ?? '',
|
||||
phonenumber: userInfo.phonenumber ?? '',
|
||||
sex: userInfo.sex ?? '0'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to get user info:', error);
|
||||
message.error(t('page.profile.updateFailed'));
|
||||
}
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value?.validate();
|
||||
loading.value = true;
|
||||
|
||||
// 构建更新数据,只包含后端接受的字段
|
||||
// const updateData = {
|
||||
// userName: formState.value.username,
|
||||
// nickName: formState.value.fullname,
|
||||
// email: formState.value.email,
|
||||
// phonenumber: formState.value.phonenumber,
|
||||
// sex: formState.value.sex,
|
||||
// age: formState.value.birthdate ? dayjs().diff(dayjs(formState.value.birthdate), 'year') : undefined
|
||||
// };
|
||||
|
||||
// 这里需要调用后端 API 更新用户信息
|
||||
// await authStore.updateUserProfile(updateData);
|
||||
|
||||
message.success(t('page.profile.updateSuccess'));
|
||||
} catch (error) {
|
||||
console.error('表单验证失败:', error);
|
||||
message.error(t('page.profile.updateFailed'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const handleBack = () => {
|
||||
router.push('/userInfo/usercard');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="profile-container">
|
||||
<a-card :title="t('view.userInfo')" :bordered="false">
|
||||
<template #extra>
|
||||
<a-button @click="handleBack">
|
||||
{{ t('page.login.common.back') }}
|
||||
</a-button>
|
||||
</template>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:rules="rules"
|
||||
:label-col="{ span: 4 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
>
|
||||
<a-form-item :label="t('page.login.register.username')" name="username">
|
||||
<a-input
|
||||
v-model:value="formState.username"
|
||||
:placeholder="t('page.login.register.usernameLengthLimit')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('page.login.register.fullName')" name="fullname">
|
||||
<a-input
|
||||
v-model:value="formState.fullname"
|
||||
:placeholder="t('page.login.register.fullName')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('page.login.register.birthDate')" name="birthdate">
|
||||
<a-date-picker
|
||||
v-model:value="formState.birthdate"
|
||||
style="width: 100%"
|
||||
:format="'YYYY-MM-DD'"
|
||||
:placeholder="t('page.login.register.birthDatePlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('page.login.register.email')" name="email">
|
||||
<a-input
|
||||
v-model:value="formState.email"
|
||||
:placeholder="t('page.login.register.emailPlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('page.login.register.phone')" name="phonenumber">
|
||||
<a-input
|
||||
v-model:value="formState.phonenumber"
|
||||
:placeholder="t('page.login.register.phonePlaceholder')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('page.login.register.gender')" name="sex">
|
||||
<a-radio-group v-model:value="formState.sex" class="w-full">
|
||||
<a-radio value="0">{{ t('page.login.register.male') }}</a-radio>
|
||||
<a-radio value="1">{{ t('page.login.register.female') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :wrapper-col="{
|
||||
xs: { span: 24, offset: 0 }, // 移动端下占满宽度,不偏移
|
||||
sm: { span: 16, offset: 4 } // PC端保持原样
|
||||
}" class="text-center">
|
||||
<a-button type="primary" :loading="loading" @click="handleSubmit">
|
||||
{{ t('common.confirm') }}
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.profile-container {
|
||||
padding: 24px;
|
||||
background-color: #f5f5f7;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
:deep(.ant-card) {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
:deep(.ant-card-head-title) {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:deep(.ant-radio-group) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item:last-child .ant-form-item-control-input-content) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 576px) {
|
||||
:deep(.ant-form-item-control) {
|
||||
max-width: 100% !important; /* 覆盖 ant-design-vue 的默认样式 */
|
||||
}
|
||||
|
||||
:deep(.ant-form-item:last-child) {
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user