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

7.6 KiB
Raw Blame History

个人中心页面设计文档

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. 测试用例

  1. 集成测试:
    • 测试页面是否能正确显示当前用户的个人信息。
    • 测试更新个人资料功能是否成功,并验证 store 中的用户信息是否同步更新。
    • 测试修改密码的成功流程,验证是否提示成功并跳转到登录页。
    • 测试修改密码的失败流程(如旧密码错误),验证是否显示正确的错误提示。
    • 测试所有表单验证规则是否生效(如邮箱格式、密码长度、两次密码一致性等)。