一、整体框架分析
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 || "密码修改失败");
}
};
四、关键技术点总结
- Vue 3 Composition API:使用
ref
、reactive
、onMounted
等组合式API - Element Plus组件:充分利用表格、分页、对话框、表单等组件
- 表单验证:内置验证规则和自定义验证器
- 文件处理:CSV导入导出,使用FormData和Blob
- 状态管理:Vuex进行全局状态管理
- 异步处理:async/await处理API调用
- 响应式设计:数据驱动视图更新
这两个组件展示了一个完整的用户管理系统前端实现,代码结构清晰,功能完善,是典型的企业级应用开发模式。