7.6 KiB
7.6 KiB
个人中心页面设计文档
1. 页面概述
个人中心页面允许当前登录的用户查看和修改自己的个人信息,以及更改密码。此页面旨在提供一个简单、安全的界面来管理个人账户资料。
2. 页面布局
2.1 布局结构
页面通常采用多标签页(Tabs)布局,将不同功能的表单分开,以保持界面整洁。
- 左侧: 用户头像和基本信息展示。
- 右侧: 包含两个标签页:
- 基本资料: 用于修改用户名、姓名、邮箱等信息。
- 修改密码: 用于更改当前用户的登录密码。
3. 组件结构
<template>
<div class="profile-page">
<el-page-header title="个人中心" content="管理您的账户信息" />
<el-row :gutter="20" class="page-container">
<el-col :span="8" :xs="24">
<el-card class="user-card">
<div class="user-avatar">
<el-avatar :size="100" :src="user.avatarUrl">{{ user.name?.[0] }}</el-avatar>
</div>
<div class="user-info">
<h2>{{ user.name }}</h2>
<p>{{ user.role }}</p>
</div>
</el-card>
</el-col>
<el-col :span="16" :xs="24">
<el-card>
<el-tabs v-model="activeTab">
<!-- 基本资料标签页 -->
<el-tab-pane label="基本资料" name="profile">
<el-form :model="profileForm" :rules="profileRules" ref="profileFormRef" label-width="80px">
<el-form-item label="用户名">
<el-input v-model="profileForm.username" disabled />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="profileForm.name" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="profileForm.email" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="updateProfile" :loading="profileLoading">
保存更改
</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 修改密码标签页 -->
<el-tab-pane label="修改密码" name="password">
<el-form :model="passwordForm" :rules="passwordRules" ref="passwordFormRef" label-width="80px">
<el-form-item label="旧密码" prop="oldPassword">
<el-input type="password" v-model="passwordForm.oldPassword" show-password />
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input type="password" v-model="passwordForm.newPassword" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input type="password" v-model="passwordForm.confirmPassword" show-password />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="changePassword" :loading="passwordLoading">
确认修改
</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
</div>
</template>
4. 数据结构
// 用户信息 (来自 Store)
interface UserProfile {
id: number;
username: string;
name: string;
email: string;
role: string;
avatarUrl?: string;
}
// 修改密码表单
interface PasswordForm {
oldPassword: string;
newPassword: string;
confirmPassword: string;
}
5. 状态管理
// 组件内状态
const activeTab = ref('profile');
const profileForm = ref<Partial<UserProfile>>({});
const passwordForm = ref<PasswordForm>({ oldPassword: '', newPassword: '', confirmPassword: '' });
const profileLoading = ref(false);
const passwordLoading = ref(false);
const profileFormRef = ref<FormInstance>();
const passwordFormRef = ref<FormInstance>();
// 全局状态 (Pinia Store)
const authStore = useAuthStore();
const { user } = storeToRefs(authStore);
// 当 user 数据从 store 加载后,填充表单
watch(user, (currentUser) => {
if (currentUser) {
profileForm.value = { ...currentUser };
}
}, { immediate: true });
6. 交互逻辑
6.1 更新个人资料
const updateProfile = async () => {
await profileFormRef.value?.validate(async (valid) => {
if (!valid) return;
profileLoading.value = true;
try {
await authStore.updateProfile({
name: profileForm.value.name,
email: profileForm.value.email,
});
ElMessage.success('个人资料更新成功');
} finally {
profileLoading.value = false;
}
});
};
6.2 修改密码
const changePassword = async () => {
await passwordFormRef.value?.validate(async (valid) => {
if (!valid) return;
passwordLoading.value = true;
try {
await authStore.changePassword(passwordForm.value);
ElMessage.success('密码修改成功,请重新登录');
// 密码修改成功后通常需要用户重新登录
authStore.logout();
router.push('/auth/login');
} catch (error) {
ElMessage.error(error.response?.data?.message || '密码修改失败');
} finally {
passwordLoading.value = false;
}
});
};
// 自定义密码验证规则
const validateConfirmPassword = (rule: any, value: any, callback: any) => {
if (value !== passwordForm.value.newPassword) {
callback(new Error('两次输入的新密码不一致'));
} else {
callback();
}
};
const passwordRules = {
// ... oldPassword 和 newPassword 的必填和长度规则
confirmPassword: [
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
{ validator: validateConfirmPassword, trigger: 'blur' }
]
};
7. API 调用
// api/me.ts
export const meApi = {
getProfile: () => apiClient.get<UserProfile>('/me'),
updateProfile: (data: Partial<UserProfile>) => apiClient.put('/me', data),
changePassword: (data: PasswordForm) => apiClient.post('/me/change-password', data),
};
// stores/auth.ts
export const useAuthStore = defineStore('auth', {
// ... state, getters
actions: {
async fetchUserProfile() {
// (在App启动或登录后调用)
const { data } = await meApi.getProfile();
this.user = data;
},
async updateProfile(data) {
const { data: updatedUser } = await meApi.updateProfile(data);
this.user = { ...this.user, ...updatedUser };
},
async changePassword(data) {
await meApi.changePassword(data);
}
}
});
8. 样式设计
.profile-page {
padding: 24px;
.page-container {
margin-top: 24px;
}
.user-card {
text-align: center;
.user-avatar {
margin-bottom: 20px;
}
.user-info h2 {
font-size: 20px;
margin-bottom: 8px;
}
.user-info p {
color: #909399;
}
}
}
9. 测试用例
- 集成测试:
- 测试页面是否能正确显示当前用户的个人信息。
- 测试更新个人资料功能是否成功,并验证 store 中的用户信息是否同步更新。
- 测试修改密码的成功流程,验证是否提示成功并跳转到登录页。
- 测试修改密码的失败流程(如旧密码错误),验证是否显示正确的错误提示。
- 测试所有表单验证规则是否生效(如邮箱格式、密码长度、两次密码一致性等)。
