Files
Environment-Monitoring-System/Design/frontend_Design/ProfilePage_Design.md
ChuXun 4ce487588a 1
2025-10-19 20:31:01 +08:00

258 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 个人中心页面设计文档
## 1. 页面概述
个人中心页面允许当前登录的用户查看和修改自己的个人信息,以及更改密码。此页面旨在提供一个简单、安全的界面来管理个人账户资料。
## 2. 页面布局
![个人中心页面布局示意图](https://placeholder-for-profile-page-mockup.png)
### 2.1 布局结构
页面通常采用多标签页Tabs布局将不同功能的表单分开以保持界面整洁。
- **左侧**: 用户头像和基本信息展示。
- **右侧**: 包含两个标签页:
- **基本资料**: 用于修改用户名、姓名、邮箱等信息。
- **修改密码**: 用于更改当前用户的登录密码。
## 3. 组件结构
```vue
<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. 数据结构
```typescript
// 用户信息 (来自 Store)
interface UserProfile {
id: number;
username: string;
name: string;
email: string;
role: string;
avatarUrl?: string;
}
// 修改密码表单
interface PasswordForm {
oldPassword: string;
newPassword: string;
confirmPassword: string;
}
```
## 5. 状态管理
```typescript
// 组件内状态
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 更新个人资料
```typescript
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 修改密码
```typescript
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 调用
```typescript
// 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. 样式设计
```scss
.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. 测试用例
1. **集成测试**:
- 测试页面是否能正确显示当前用户的个人信息。
- 测试更新个人资料功能是否成功,并验证 store 中的用户信息是否同步更新。
- 测试修改密码的成功流程,验证是否提示成功并跳转到登录页。
- 测试修改密码的失败流程(如旧密码错误),验证是否显示正确的错误提示。
- 测试所有表单验证规则是否生效(如邮箱格式、密码长度、两次密码一致性等)。