UserManagement.vue和Profile.vue详细解释

发布于:2025-09-09 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、整体框架分析

UserManagement.vue - 用户管理页面

这是一个典型的后台管理系统的CRUD页面,使用了Vue 3的Composition API和Element Plus组件库,主要功能包括:

  • 用户列表展示(表格+分页)
  • 搜索和筛选
  • 新增/编辑/删除用户
  • CSV导入导出
  • 状态管理(启用/禁用)

Profile.vue - 个人中心页面

这是用户个人信息管理页面,主要功能包括:

  • 查看个人信息
  • 编辑个人信息
  • 修改密码

二、UserManagement.vue 详细讲解

1. 模板部分(Template)

搜索和操作栏
<el-card class="search-card">
  <el-row :gutter="20">  <!-- gutter设置列之间的间距为20px -->

搜索输入框部分:

<el-input
  v-model="searchForm.username"  <!-- 双向绑定搜索关键词 -->
  placeholder="请输入用户名"
  clearable                       <!-- 显示清空按钮 -->
  @clear="handleSearch"           <!-- 清空时触发搜索 -->
  @keyup.enter="handleSearch"     <!-- 回车键触发搜索 -->
>
  <template #prefix>              <!-- 输入框前缀插槽 -->
    <el-icon><Search /></el-icon>
  </template>
</el-input>

文件上传组件:

<el-upload
  :show-file-list="false"    <!-- 不显示已上传文件列表 -->
  :before-upload="handleImport"  <!-- 上传前的钩子,处理文件 -->
  accept=".csv"               <!-- 只接受CSV文件 -->
  style="display: inline-block; margin-left: 10px"
>
数据表格
<el-table
  :data="tableData"      <!-- 绑定表格数据源 -->
  v-loading="loading"    <!-- 加载状态,显示loading动画 -->
  stripe                 <!-- 斑马纹样式,隔行变色 -->
  style="width: 100%"
>

分数列的条件渲染:

<el-table-column prop="score" label="分数" width="80">
  <template #default="{ row }">  <!-- 解构获取当前行数据 -->
    <el-tag :type="getScoreType(row.score)">{{ row.score }}</el-tag>
    <!-- 根据分数显示不同颜色的标签 -->
  </template>
</el-table-column>

状态开关列:

<el-switch
  v-model="row.status"
  :active-value="1"      <!-- 开启时的值 -->
  :inactive-value="0"    <!-- 关闭时的值 -->
  @change="handleStatusChange(row)"  <!-- 状态改变时的处理 -->
/>
分页组件
<el-pagination
  v-model:current-page="pagination.pageNum"  <!-- Vue3新语法,双向绑定当前页 -->
  v-model:page-size="pagination.pageSize"    <!-- 双向绑定每页条数 -->
  :page-sizes="[10, 20, 50, 100]"           <!-- 每页条数选项 -->
  :total="pagination.total"                  <!-- 总条数 -->
  layout="total, sizes, prev, pager, next, jumper"  <!-- 分页器布局 -->
  @size-change="handleSizeChange"            <!-- 每页条数改变 -->
  @current-change="handleCurrentChange"      <!-- 当前页改变 -->
/>
新增/编辑对话框
<el-dialog
  v-model="dialogVisible"     <!-- 控制对话框显示/隐藏 -->
  :title="dialogTitle"        <!-- 动态标题 -->
  width="500px"
  @close="resetForm"          <!-- 关闭时重置表单 -->
>

2. 脚本部分(Script)

数据定义
// 响应式数据
const loading = ref(false);          // 加载状态
const tableData = ref([]);          // 表格数据
const dialogVisible = ref(false);   // 对话框显示状态
const isEdit = ref(false);          // 是否编辑模式
const userFormRef = ref();          // 表单引用

// 响应式对象
const searchForm = reactive({       // 搜索表单
  username: "",
});

const pagination = reactive({       // 分页参数
  pageNum: 1,      // 当前页
  pageSize: 10,    // 每页条数
  total: 0,        // 总条数
});
表单验证规则
const rules = {
  username: [
    { required: true, message: "请输入用户名", trigger: "blur" },
    { min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" },
  ],
  email: [
    { required: true, message: "请输入邮箱", trigger: "blur" },
    { type: "email", message: "请输入正确的邮箱地址", trigger: "blur" },
  ],
  phone: [
    {
      pattern: /^1[3-9]\d{9}$/,  // 手机号正则表达式
      message: "请输入正确的手机号",
      trigger: "blur",  // 失去焦点时触发验证
    },
  ],
};
核心方法详解

获取列表数据:

const getList = async () => {
  loading.value = true;  // 开启加载状态
  try {
    const params = {
      pageNum: pagination.pageNum,
      pageSize: pagination.pageSize,
    };
    
    // 条件性添加搜索参数
    if (searchForm.username) {
      params.username = searchForm.username;
    }
    
    const res = await getUserList(params);  // 调用API
    
    // 更新数据
    tableData.value = res.data.list;    // 列表数据
    pagination.total = res.data.total;  // 总条数
  } catch (error) {
    ElMessage.error("获取用户列表失败");
  } finally {
    loading.value = false;  // 关闭加载状态
  }
};

编辑用户:

const showEditDialog = (row) => {
  isEdit.value = true;
  dialogTitle.value = "编辑用户";
  Object.assign(userForm, row);  // 将row的属性复制到userForm,保持响应性
  dialogVisible.value = true;
};

提交表单:

const submitForm = async () => {
  const valid = await userFormRef.value.validate();  // 验证表单
  if (!valid) return;

  try {
    if (isEdit.value) {
      await updateUser(userForm.id, userForm);  // 更新
      ElMessage.success("更新成功");
    } else {
      await createUser(userForm);  // 创建
      ElMessage.success("创建成功");
    }
    dialogVisible.value = false;
    getList();  // 刷新列表
  } catch (error) {
    ElMessage.error(error.message || "操作失败");
  }
};

删除用户:

const handleDelete = async (row) => {
  try {
    await ElMessageBox.confirm(
      `确定要删除用户 ${row.username} 吗?`,  // 模板字符串嵌入变量
      "删除确认",
      {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }
    );

    await deleteUser(row.id);
    ElMessage.success("删除成功");
    getList();
  } catch (error) {
    if (error !== "cancel") {  // 排除取消操作
      ElMessage.error("删除失败");
    }
  }
};

导出CSV:

const handleExport = async () => {
  try {
    const res = await exportUsersCsv();
    
    // 创建Blob对象
    const url = window.URL.createObjectURL(
      new Blob([res], { type: "text/csv;charset=utf-8;" })
    );
    
    // 创建下载链接
    const link = document.createElement("a");
    link.href = url;
    link.download = `用户数据_${new Date().getTime()}.csv`;  // 文件名加时间戳
    document.body.appendChild(link);
    link.click();
    
    // 清理DOM和内存
    setTimeout(() => {
      document.body.removeChild(link);
      window.URL.revokeObjectURL(url);  // 释放URL对象
    }, 100);
    
    ElMessage.success("导出成功");
  } catch (error) {
    console.error("导出错误:", error);
    ElMessage.error("导出失败: " + (error.message || "未知错误"));
  }
};

导入CSV:

const handleImport = async (file) => {
  const formData = new FormData();  // 创建表单数据对象
  formData.append("file", file);

  try {
    const res = await importUsersCsv(formData);
    const { successCount, errorCount, errors } = res.data;  // 解构导入结果

    if (errorCount > 0) {
      ElMessage.warning(
        `导入完成:成功 ${successCount} 条,失败 ${errorCount}`
      );
      console.error("导入错误:", errors);
    } else {
      ElMessage.success(`导入成功:共 ${successCount} 条数据`);
    }

    getList();  // 刷新列表
  } catch (error) {
    ElMessage.error("导入失败");
  }

  return false; // 阻止默认上传行为
};

三、Profile.vue 详细讲解

1. 模板部分

个人信息卡片
<el-card>
  <template #header>  <!-- 卡片头部插槽 -->
    <div class="card-header">
      <span>个人信息</span>
      <el-button type="primary" size="small" @click="isEdit = !isEdit">
        {{ isEdit ? "取消编辑" : "编辑信息" }}  <!-- 动态按钮文本 -->
      </el-button>
    </div>
  </template>
表单禁用控制
<el-form
  ref="profileFormRef"
  :model="profileForm"
  :rules="rules"
  :disabled="!isEdit"  <!-- 根据编辑状态控制表单是否可编辑 -->
  label-width="100px"
  style="max-width: 600px"
>
修改密码表单
<el-form-item label="确认密码" prop="confirmPassword">
  <el-input
    v-model="passwordForm.confirmPassword"
    type="password"
    show-password  <!-- 显示密码可见性切换按钮 -->
  />
</el-form-item>

2. 脚本部分

初始化和依赖
import { useStore } from "vuex";      // Vuex状态管理
import { useRouter } from "vue-router"; // 路由
const store = useStore();
const router = useRouter();
const currentUser = store.getters.user;  // 从store获取当前用户信息
密码验证规则
const passwordRules = {
  confirmPassword: [
    { required: true, message: "请再次输入新密码", trigger: "blur" },
    {
      validator: (rule, value, callback) => {  // 自定义验证器
        if (value !== passwordForm.newPassword) {
          callback(new Error("两次输入密码不一致"));
        } else {
          callback();
        }
      },
      trigger: "blur",
    },
  ],
};
保存个人信息
const saveProfile = async () => {
  const valid = await profileFormRef.value.validate();
  if (!valid) return;

  try {
    await updateUser(profileForm.id, {
      email: profileForm.email,
      phone: profileForm.phone,
    });

    // 更新store中的用户信息
    store.commit("SET_USER", { ...currentUser, ...profileForm });

    ElMessage.success("保存成功");
    isEdit.value = false;  // 退出编辑模式
  } catch (error) {
    ElMessage.error("保存失败");
  }
};
修改密码
const handleChangePassword = async () => {
  const valid = await passwordFormRef.value.validate();
  if (!valid) return;

  try {
    await changePassword(currentUser.id, {
      oldPassword: passwordForm.oldPassword,
      newPassword: passwordForm.newPassword,
    });

    ElMessage.success("密码修改成功,请重新登录");
    
    resetPasswordForm();  // 清空表单
    
    // 延迟退出登录
    setTimeout(async () => {
      await store.dispatch("logout");  // 调用store的logout action
      router.push("/login");           // 跳转到登录页
    }, 1500);  // 1.5秒后执行
    
  } catch (error) {
    ElMessage.error(error.message || "密码修改失败");
  }
};

四、关键技术点总结

  1. Vue 3 Composition API:使用refreactiveonMounted等组合式API
  2. Element Plus组件:充分利用表格、分页、对话框、表单等组件
  3. 表单验证:内置验证规则和自定义验证器
  4. 文件处理:CSV导入导出,使用FormData和Blob
  5. 状态管理:Vuex进行全局状态管理
  6. 异步处理:async/await处理API调用
  7. 响应式设计:数据驱动视图更新

这两个组件展示了一个完整的用户管理系统前端实现,代码结构清晰,功能完善,是典型的企业级应用开发模式。


网站公告

今日签到

点亮在社区的每一天
去签到