当前位置: 首页 > news >正文

用户管理系统后台管理界面

用户管理系统后台管理界面主要功能:

  • 查看用户列表:展示所有用户的基本信息
  • 搜索用户:根据条件筛选用户
  • 添加新用户:创建新的用户账户
  • 编辑用户信息:修改现有用户的数据
  • 删除用户:移除不需要的用户
  • 批量操作:导入/导出用户数据

1.2 技术栈介绍

Vue 3
// Vue 3 的特点
- 组合式API (Composition API)
- 更好的性能
- 更小的包体积
- 更好的 TypeScript 支持
Element Plus
// UI 组件库,提供:
- 表格 (el-table)
- 表单 (el-form)
- 按钮 (el-button)
- 对话框 (el-dialog)
- 分页 (el-pagination)
// 等常用组件

第二部分:项目结构分析

2.1 文件组织

src/views/UserManagement.vue
├── <template>     # 页面结构
├── <script setup> # 逻辑代码
└── <style>        # 样式定义

2.2 组件架构

用户管理页面
├── 搜索和操作栏
│   ├── 搜索框
│   ├── 搜索/重置按钮
│   └── 新增/导出/导入按钮
├── 数据表格
│   ├── 用户信息列
│   ├── 状态开关
│   └── 操作按钮
├── 分页组件
└── 新增/编辑对话框├── 表单字段├── 验证规则└── 提交/取消按钮

Vue 3 基础语法详解

3.1 Composition API 基础

响应式数据定义
import { ref, reactive } from "vue";// ref:用于基本类型数据
const loading = ref(false);
const dialogVisible = ref(false);// reactive:用于对象类型数据
const searchForm = reactive({username: "",
});const userForm = reactive({id: null,username: "",email: "",// ...其他字段
});

为什么要区分 ref 和 reactive?

  • ref:包装基本类型(string, number, boolean),通过 .value 访问
  • reactive:直接包装对象,可以直接访问属性
模板引用
const userFormRef = ref();// 在模板中使用
<el-form ref="userFormRef"><!-- 表单内容 -->
</el-form>// 在代码中调用表单方法
const valid = await userFormRef.value.validate();

3.2 生命周期钩子

import { onMounted } from "vue";onMounted(() => {getList(); // 组件挂载后获取数据
});

页面结构详细分析

4.1 搜索和操作栏

<el-card class="search-card"><el-row :gutter="20"><!-- 搜索区域 --><el-col :span="6"><el-inputv-model="searchForm.username"placeholder="请输入用户名"clearable@clear="handleSearch"@keyup.enter="handleSearch"><template #prefix><el-icon><Search /></el-icon></template></el-input></el-col><!-- 搜索按钮 --><el-col :span="6"><el-button type="primary" @click="handleSearch">搜索</el-button><el-button @click="resetSearch">重置</el-button></el-col><!-- 操作按钮 --><el-col :span="12" style="text-align: right"><el-button type="primary" @click="showAddDialog"><el-icon><Plus /></el-icon>新增用户</el-button><!-- 导出/导入按钮 --></el-col></el-row>
</el-card>

关键语法解释:

  • v-model="searchForm.username":双向数据绑定
  • @click="handleSearch":事件监听
  • @keyup.enter:回车键事件
  • clearable:显示清空按钮
  • :span="6":栅格布局,占6列(总共24列)

4.2 数据表格

<el-table:data="tableData"v-loading="loading"stripestyle="width: 100%"
><!-- 基础列 --><el-table-column prop="id" label="ID" width="80" /><el-table-column prop="username" label="用户名" /><!-- 自定义列:分数标签 --><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-table-column prop="status" label="状态" width="100"><template #default="{ row }"><el-switchv-model="row.status":active-value="1":inactive-value="0"@change="handleStatusChange(row)"/></template></el-table-column><!-- 操作列 --><el-table-column label="操作" width="200" fixed="right"><template #default="{ row }"><el-button type="primary" size="small" @click="showEditDialog(row)">编辑</el-button><el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button></template></el-table-column>
</el-table>

关键概念:

  • :data="tableData":表格数据绑定
  • v-loading="loading":加载状态
  • template #default="{ row }":插槽,自定义列内容
  • { row }:解构赋值,获取当前行数据

核心功能实现详解

5.1 数据获取功能

基础版本
const getList = async () => {const res = await getUserList();tableData.value = res.data.list;
};
完整版本(带搜索、分页、错误处理)
const getList = async () => {// 1. 设置加载状态loading.value = true;try {// 2. 准备请求参数const params = {pageNum: pagination.pageNum,    // 当前页码pageSize: pagination.pageSize,  // 每页条数};// 3. 添加搜索条件(如果有)if (searchForm.username) {params.username = searchForm.username;}// 4. 发送请求const res = await getUserList(params);// 5. 更新数据tableData.value = res.data.list;pagination.total = res.data.total;} catch (error) {// 6. 错误处理ElMessage.error("获取用户列表失败");} finally {// 7. 清除加载状态loading.value = false;}
};
  • async/await:异步处理
  • try-catch-finally:错误处理模式
  • 参数的动态构建
  • 加载状态的管理

5.2 搜索功能实现

// 搜索处理
const handleSearch = () => {pagination.pageNum = 1;  // 重置到第一页getList();              // 重新获取数据
};// 重置搜索
const resetSearch = () => {searchForm.username = "";  // 清空搜索条件handleSearch();           // 执行搜索
};

搜索时可能数据总数发生变化,当前页码可能超出范围,所以要重置到第一页。

5.3 分页功能实现

// 分页数据
const pagination = reactive({pageNum: 1,      // 当前页码pageSize: 10,    // 每页条数total: 0,        // 总条数
});// 每页条数改变
const handleSizeChange = () => {pagination.pageNum = 1;  // 重置页码getList();              // 重新获取数据
};// 页码改变
const handleCurrentChange = () => {getList();  // 获取新页面数据
};

5.4 表单验证系统

验证规则定义
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 submitForm = async () => {// 1. 表单验证const valid = await userFormRef.value.validate();if (!valid) return;  // 验证失败,直接返回try {// 2. 根据是否编辑选择APIif (isEdit.value) {await updateUser(userForm.id, userForm);ElMessage.success("更新成功");} else {await createUser(userForm);ElMessage.success("创建成功");}// 3. 关闭对话框,刷新列表dialogVisible.value = false;getList();} catch (error) {ElMessage.error(error.message || "操作失败");}
};

6.1 状态切换功能

const handleStatusChange = async (row) => {const originalStatus = row.status;  // 保存原始状态try {// 发送更新请求await updateUser(row.id, { status: row.status });ElMessage.success("状态更新成功");} catch (error) {// 失败时回滚状态row.status = originalStatus === 1 ? 0 : 1;ElMessage.error("状态更新失败");}
};

乐观更新模式:

  1. 先更新界面(用户立即看到变化)
  2. 发送服务器请求
  3. 如果失败,回滚界面状态

6.2 删除确认功能

const handleDelete = async (row) => {try {// 1. 确认对话框await ElMessageBox.confirm(`确定要删除用户 ${row.username} 吗?`,"删除确认",{confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",});// 2. 执行删除await deleteUser(row.id);ElMessage.success("删除成功");// 3. 刷新列表getList();} catch (error) {// 4. 错误处理(区分取消和真正的错误)if (error !== "cancel") {ElMessage.error("删除失败");}}
};

6.3 CSV导出功能

const handleExport = async () => {try {// 1. 获取导出数据const res = await exportUsersCsv();// 2. 创建Blob对象const blob = new Blob([res], { type: "text/csv;charset=utf-8;" });// 3. 创建下载链接const url = window.URL.createObjectURL(blob);const link = document.createElement("a");link.href = url;link.download = `用户数据_${new Date().getTime()}.csv`;// 4. 触发下载document.body.appendChild(link);link.click();// 5. 清理资源setTimeout(() => {document.body.removeChild(link);window.URL.revokeObjectURL(url);}, 100);ElMessage.success("导出成功");} catch (error) {ElMessage.error("导出失败");}
};

文件下载原理:

  1. 服务器返回文件数据
  2. 创建Blob对象(二进制数据)
  3. 生成临时URL
  4. 创建a标签触发下载
  5. 清理临时资源

6.4 CSV导入功能

const handleImport = async (file) => {// 1. 创建表单数据const formData = new FormData();formData.append("file", file);try {// 2. 上传文件const res = await importUsersCsv(formData);const { successCount, errorCount, errors } = res.data;// 3. 根据结果显示不同消息if (errorCount > 0) {ElMessage.warning(`导入完成:成功 ${successCount} 条,失败 ${errorCount}`);console.error("导入错误:", errors);} else {ElMessage.success(`导入成功:共 ${successCount} 条数据`);}// 4. 刷新列表getList();} catch (error) {ElMessage.error("导入失败");}// 5. 阻止默认上传行为return false;
};

工具函数

7.1 工具函数

// 根据分数返回标签类型
const getScoreType = (score) => {if (score >= 90) return "success";    // 绿色if (score >= 60) return "warning";    // 橙色return "danger";                      // 红色
};// 格式化日期显示
const formatDate = (dateStr) => {if (!dateStr) return "";const date = new Date(dateStr);return date.toLocaleString("zh-CN");
};

7.2 表单重置

const resetForm = () => {// 1. 重置表单验证状态userFormRef.value?.resetFields();// 2. 重置表单数据Object.assign(userForm, {id: null,username: "",password: "",email: "",phone: "",score: 0,status: 1,});
};

7.3 对话框管理

// 新增用户
const showAddDialog = () => {isEdit.value = false;dialogTitle.value = "新增用户";dialogVisible.value = true;
};// 编辑用户
const showEditDialog = (row) => {isEdit.value = true;dialogTitle.value = "编辑用户";Object.assign(userForm, row);  // 复制数据到表单dialogVisible.value = true;
};

样式

8.1 CSS样式

.user-management {.search-card {margin-bottom: 20px;  // 搜索卡片间距}.table-card {.el-table {margin-bottom: 20px;  // 表格底部间距}}
}
  1. 加载状态v-loading 让用户知道数据正在加载

  2. 即时反馈:操作后立即显示成功/失败消息

  3. 确认操作:删除等危险操作需要确认

  4. 表单验证:实时验证用户输入

  5. 状态回滚:操作失败时恢复原始状态

  6. 组合式API:更好的代码组织和复用

  7. 响应式数据:自动更新界面

  8. 组件化开发:模块化、可维护

  9. 完善的错误处理:提升用户体验

  10. 统一的数据流:清晰的数据管理

<!-- src/views/UserManagement.vue -->
<template><div class="user-management"><!-- 搜索和操作栏 --><el-card class="search-card"><el-row :gutter="20"><el-col :span="6"><el-inputv-model="searchForm.username"placeholder="请输入用户名"clearable@clear="handleSearch"@keyup.enter="handleSearch"><template #prefix><el-icon><Search /></el-icon></template></el-input></el-col><el-col :span="6"><el-button type="primary" @click="handleSearch">搜索</el-button><el-button @click="resetSearch">重置</el-button></el-col><el-col :span="12" style="text-align: right"><el-button type="primary" @click="showAddDialog"><el-icon><Plus /></el-icon>新增用户</el-button><el-button type="success" @click="handleExport"><el-icon><Download /></el-icon>导出CSV</el-button><el-upload:show-file-list="false":before-upload="handleImport"accept=".csv"style="display: inline-block; margin-left: 10px"><el-button type="warning"><el-icon><Upload /></el-icon>导入CSV</el-button></el-upload></el-col></el-row></el-card><!-- 数据表格 --><el-card class="table-card"><el-table:data="tableData"v-loading="loading"stripestyle="width: 100%"><el-table-column prop="id" label="ID" width="80" /><el-table-column prop="username" label="用户名" /><el-table-column prop="email" label="邮箱" /><el-table-column prop="phone" label="手机号" /><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-table-column prop="status" label="状态" width="100"><template #default="{ row }"><el-switchv-model="row.status":active-value="1":inactive-value="0"@change="handleStatusChange(row)"/></template></el-table-column><el-table-column prop="createTime" label="创建时间" width="180"><template #default="{ row }">{{ formatDate(row.createTime) }}</template></el-table-column><el-table-column label="操作" width="200" fixed="right"><template #default="{ row }"><el-button type="primary" size="small" @click="showEditDialog(row)">编辑</el-button><el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><el-paginationv-model:current-page="pagination.pageNum"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"style="margin-top: 20px"/></el-card><!-- 新增/编辑对话框 --><el-dialogv-model="dialogVisible":title="dialogTitle"width="500px"@close="resetForm"><el-formref="userFormRef":model="userForm":rules="rules"label-width="80px"><el-form-item label="用户名" prop="username"><el-inputv-model="userForm.username"placeholder="请输入用户名":disabled="isEdit"/></el-form-item><el-form-item label="密码" prop="password" v-if="!isEdit"><el-inputv-model="userForm.password"type="password"placeholder="请输入密码"show-password/></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="userForm.email" placeholder="请输入邮箱" /></el-form-item><el-form-item label="手机号" prop="phone"><el-input v-model="userForm.phone" placeholder="请输入手机号" /></el-form-item><el-form-item label="分数" prop="score"><el-input-numberv-model="userForm.score":min="0":max="100"placeholder="请输入分数"/></el-form-item><el-form-item label="状态" prop="status"><el-radio-group v-model="userForm.status"><el-radio :value="1">启用</el-radio><el-radio :value="0">禁用</el-radio></el-radio-group></el-form-item></el-form><template #footer><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="submitForm">确定</el-button></template></el-dialog></div>
</template><script setup>
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {getUserList,createUser,updateUser,deleteUser,exportUsersCsv,importUsersCsv,
} from "@/api/user";// 数据定义
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 userForm = reactive({id: null,username: "",password: "",email: "",phone: "",score: 0,status: 1,
});// 表单验证规则
const rules = {username: [{ required: true, message: "请输入用户名", trigger: "blur" },{ min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" },],password: [{ required: true, message: "请输入密码", trigger: "blur" },{ min: 6, max: 20, message: "长度在 6 到 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 dialogTitle = ref("新增用户");// 方法
const getScoreType = (score) => {if (score >= 90) return "success";if (score >= 60) return "warning";return "danger";
};const formatDate = (dateStr) => {if (!dateStr) return "";const date = new Date(dateStr);return date.toLocaleString("zh-CN");
};// 获取用户列表
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);tableData.value = res.data.list;pagination.total = res.data.total;} catch (error) {ElMessage.error("获取用户列表失败");} finally {loading.value = false;}
};// 搜索
const handleSearch = () => {pagination.pageNum = 1;getList();
};// 重置搜索
const resetSearch = () => {searchForm.username = "";handleSearch();
};// 分页
const handleSizeChange = () => {pagination.pageNum = 1;getList();
};const handleCurrentChange = () => {getList();
};// 新增用户
const showAddDialog = () => {isEdit.value = false;dialogTitle.value = "新增用户";dialogVisible.value = true;
};// 编辑用户
const showEditDialog = (row) => {isEdit.value = true;dialogTitle.value = "编辑用户";Object.assign(userForm, row);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 resetForm = () => {userFormRef.value?.resetFields();Object.assign(userForm, {id: null,username: "",password: "",email: "",phone: "",score: 0,status: 1,});
};// 状态切换
const handleStatusChange = async (row) => {try {await updateUser(row.id, { status: row.status });ElMessage.success("状态更新成功");} catch (error) {row.status = row.status === 1 ? 0 : 1;ElMessage.error("状态更新失败");}
};// 删除用户
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();// 创建下载链接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();// 清理setTimeout(() => {document.body.removeChild(link);window.URL.revokeObjectURL(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; // 阻止默认上传行为
};// 生命周期
onMounted(() => {getList();
});
</script><style scoped lang="scss">
.user-management {.search-card {margin-bottom: 20px;}.table-card {.el-table {margin-bottom: 20px;}}
}
</style>
http://www.lryc.cn/news/617342.html

相关文章:

  • Python面试题及详细答案150道(41-55) -- 面向对象编程篇
  • VBA即用型代码手册:计算选择的单词数Count Words in Selection
  • 5种无需USB线将照片从手机传输到笔记本电脑的方法
  • vue+flask基于规则的求职推荐系统
  • Spring Boot启动事件详解:类型、监听与实战应用
  • 腾讯云Edgeone限时免费
  • 有序矩阵中第K小的元素+二分查找
  • 使用 Rust 创建 32 位 DLL 的完整指南
  • 数据大集网:精准获客新引擎,助力中小企业突破推广困局
  • CSPOJ:1561: 【提高】买木头
  • 请求报文和响应报文(详细讲解)
  • nomachine的安装和使用
  • 零基础学习jQuery第三天
  • 用 Python 绘制企业年度财务可视化报告 —— 从 Excel 到 9 种图表全覆盖
  • DDIA第五章:分布式数据复制中的一致性与冲突处理
  • 第5节 大模型分布式推理通信优化与硬件协同
  • 在Debian上安装MySQL
  • Excel 实战:基因表达矩阵前处理中测序符号的快速剥离方法
  • golang 基础案例_02
  • 设计模式笔记_结构型_享元模式
  • 深入解析Prompt缓存机制:原理、优化与最佳实践
  • Agent在供应链管理中的应用:库存优化与需求预测
  • Python FastAPI + React + Nginx 阿里云WINDOWS ECS部署实战:从标准流程到踩坑解决全记录
  • typecho博客设置浏览器标签页图标icon
  • 【工控】线扫相机小结 第六篇
  • uncalled4
  • 麒麟系统使用-PATH设置
  • 【接口自动化】-7- 热加载和日志封装
  • 实战:用 PyTorch 复现一个 3 层全连接网络,训练 MNIST,达到 95%+ 准确率
  • 软件测试关于搜索方面的测试用例