VUE+SPRINGBOOT从0-1打造前后端-前后台系统-用户管理

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

在现代Web应用开发中,前后端分离架构已经成为主流模式。本文将通过一个完整的用户管理系统案例,详细介绍如何使用Vue.js + Element UI构建前端界面,结合Spring Boot实现后端服务,实现前后端分离开发。该系统包含用户信息的增删改查、批量操作、导入导出等常见功能,是一个典型的企业级管理系统模块。

一、技术栈介绍

1.1 前端技术栈

  • Vue.js:渐进式JavaScript框架,用于构建用户界面

  • Element UI:基于Vue.js的桌面端组件库,提供丰富的UI组件

  • Axios:基于Promise的HTTP客户端,用于前后端通信

  • Vue Router:Vue.js官方的路由管理器

1.2 后端技术栈

  • Spring Boot:简化Spring应用初始搭建和开发过程的框架

  • MyBatis-Plus:MyBatis的增强工具,简化CRUD操作

  • Hutool:Java工具类库,提供Excel导入导出等功能

  • JWT:用于身份验证的JSON Web Token

二、前端实现详解

2.1 项目结构与配置

前端项目采用Vue CLI搭建,主要目录结构如下:

src/
├── views/
│   └── User.vue       # 用户管理主页面
├── router/            # 路由配置
├── api/               # API请求封装
└── public/config.js   # 全局配置

public/config.js中配置后端服务地址:

window.WHITE_IP = "your-server-ip:port";

2.2 用户管理页面布局

用户管理页面主要分为以下几个部分:

  1. 搜索区域:提供用户名搜索和重置功能

  2. 操作按钮区域:新增、批量删除、导入导出等功能按钮

  3. 表格展示区域:用户数据列表展示

  4. 分页组件:数据分页控制

  5. 弹窗表单:新增/编辑用户信息

<div class="User-container">
  <!-- 搜索区域 -->
  <div style="margin: 10px 0">
    <el-input v-model="send.name" placeholder="请输入用户名"></el-input>
    <el-button @click="selectListPage">搜索</el-button>
    <el-button @click="reset">重置</el-button>
    
    <!-- 操作按钮 -->
    <el-button @click="insertWindow">新增</el-button>
    <el-popconfirm @confirm="confirmRemoveIdList">
      <el-button slot="reference">批量删除</el-button>
    </el-popconfirm>
    <el-button @click="list_export">导出</el-button>
  </div>
  
  <!-- 表格区域 -->
  <el-table :data="list" @selection-change="onSelectChange">
    <!-- 表格列定义 -->
  </el-table>
  
  <!-- 分页组件 -->
  <el-pagination
    @size-change="onSizeChange"
    @current-change="onCurrentChange"
    :current-page="send.currentPage"
    :page-size="send.pageSize"
    :total="send.total">
  </el-pagination>
  
  <!-- 弹窗表单 -->
  <el-dialog :visible.sync="sendFormFlag">
    <el-form>
      <!-- 表单字段 -->
    </el-form>
  </el-dialog>
</div>

2.3 核心功能实现

2.3.1 分页查询

分页查询是管理系统的核心功能,前端需要维护当前页码、每页条数等参数:

data() {
  return {
    send: {
      currentPage: 1,
      pageSize: 10,
      total: 0,
      name: ''
    },
    list: []
  }
},
methods: {
  // 分页查询
  selectListPage() {
    this.$http.post("/user/list_page", this.send).then(res => {
      if (res.data.code === "200") {
        this.send.total = res.data.object.total
        this.list = res.data.object.data
      }
    })
  },
  
  // 分页大小变化
  onSizeChange(pageSize) {
    this.send.pageSize = pageSize
    this.selectListPage()
  },
  
  // 当前页变化
  onCurrentChange(currentPage) {
    this.send.currentPage = currentPage
    this.selectListPage()
  }
}
2.3.2 新增与编辑用户

通过同一个弹窗表单实现新增和编辑功能:

methods: {
  // 打开新增窗口
  insertWindow() {
    this.sendFormFlag = true
    this.sendForm = {}
  },
  
  // 打开编辑窗口
  updateWindow(row) {
    this.sendFormFlag = true
    this.sendForm = JSON.parse(JSON.stringify(row))
  },
  
  // 确认保存
  confirmInsertOrUpdate() {
    this.$http.post("/user/insertOrUpdate", this.sendForm).then(res => {
      if (res.data.code === "200") {
        this.$message.success('保存成功')
        this.selectListPage()
      }
    })
  }
}
2.3.3 批量操作

批量操作需要处理表格的多选功能:

methods: {
  // 多选变化
  onSelectChange(val) {
    this.sendForm.removeIdList = val
  },
  
  // 确认批量删除
  confirmRemoveIdList() {
    this.sendForm.removeIdList = this.sendForm.removeIdList.map(v => v.id)
    this.$http.post("user/list_delete", this.sendForm).then(res => {
      this.selectListPage()
    })
  }
}
2.3.4 导入导出功能

导入导出是管理系统常见功能,使用Element UI的Upload组件实现:

methods: {
  // 导出
  list_export() {
    window.open(`https://${WHITE_IP}/user/list_export`)
  },
  
  // 导入成功回调
  onImportSuccess() {
    this.$message.success("文件导入成功")
    this.selectListPage()
  }
}

三、后端实现详解

3.1 项目结构

后端采用标准的Spring Boot项目结构:

src/main/java/
└── com.black
    ├── controller    # 控制器层
    ├── mapper        # 数据访问层
    ├── pojo          # 实体类
    └── util         # 工具类

3.2 核心控制器实现

用户控制器处理所有用户相关的请求:

@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    UserMapper userMapper;
    
    // 新增或更新用户
    @PostMapping("/insertOrUpdate")
    public Res insertOrUpdate(@RequestBody User user) {
        if (user.getId() != null) {
            userMapper.updateById(user);
        } else {
            user.setPassword(MyUtils.getSHA256StrJava("123456"));
            userMapper.insert(user);
        }
        return Res.success(null);
    }
    
    // 分页查询
    @PostMapping("/list_page")
    public Res list_page(@RequestBody User user) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(user.getName())) {
            queryWrapper.like("name", user.getName());
        }
        
        int total = userMapper.selectCount(queryWrapper);
        queryWrapper.last("limit " + user.getStart() + "," + user.getEnd());
        List<User> dataList = userMapper.selectList(queryWrapper);
        
        Map<String, Object> result = new HashMap<>();
        result.put("total", total);
        result.put("data", dataList);
        return Res.success(result);
    }
    
    // 导出Excel
    @GetMapping("/list_export")
    public void list_export(HttpServletResponse response) throws Exception {
        List<User> list = userMapper.selectList(null);
        ExcelWriter writer = ExcelUtil.getWriter(true);
        
        // 设置标题别名
        writer.addHeaderAlias("id", "ID");
        writer.addHeaderAlias("name", "用户名");
        // ...其他字段
        
        writer.write(list, true);
        
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        String fileName = URLEncoder.encode("用户信息", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
        
        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        out.close();
        writer.close();
    }
}

3.3 分页查询实现

后端分页查询使用MyBatis-Plus实现:

@PostMapping("/list_page")
public Res list_page(@RequestBody User user) {
    // 1. 构建查询条件
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    if (StringUtils.isNotBlank(user.getName())) {
        queryWrapper.like("name", user.getName());
    }
    
    // 2. 获取总数
    int total = userMapper.selectCount(queryWrapper);
    
    // 3. 计算分页参数
    int start = (user.getCurrentPage() - 1) * user.getPageSize();
    int end = user.getPageSize();
    
    // 4. 执行分页查询
    queryWrapper.last("limit " + start + "," + end);
    List<User> dataList = userMapper.selectList(queryWrapper);
    
    // 5. 返回结果
    Map<String, Object> result = new HashMap<>();
    result.put("total", total);
    result.put("data", dataList);
    return Res.success(result);
}

3.4 Excel导出实现

使用Hutool工具库实现Excel导出:

@GetMapping("/list_export")
public void list_export(HttpServletResponse response) throws Exception {
    // 1. 查询数据
    List<User> list = userMapper.selectList(null);
    
    // 2. 创建Excel写入器
    ExcelWriter writer = ExcelUtil.getWriter(true);
    
    // 3. 设置标题别名
    writer.addHeaderAlias("id", "ID");
    writer.addHeaderAlias("name", "用户名");
    writer.addHeaderAlias("nick", "昵称");
    // ...其他字段
    
    // 4. 写入数据
    writer.write(list, true);
    
    // 5. 设置响应头
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    String fileName = URLEncoder.encode("用户信息", "UTF-8");
    response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
    
    // 6. 输出流
    ServletOutputStream out = response.getOutputStream();
    writer.flush(out, true);
    out.close();
    writer.close();
}

四、前后端交互设计

4.1 API接口规范

系统采用RESTful风格设计API,主要接口如下:

请求方法 路径 描述
POST /user/list_page 分页查询用户列表
POST /user/insertOrUpdate 新增或更新用户
POST /user/delete 删除用户
POST /user/list_delete 批量删除用户
GET /user/list_export 导出用户数据到Excel

4.2 数据格式约定

前后端统一使用JSON格式交互,响应数据格式如下:

成功响应:

{
  "code": "200",
  "message": "成功",
  "object": {
    "total": 100,
    "data": [...]
  }
}

错误响应:

{
  "code": "500",
  "message": "服务器错误"
}

4.3 Axios封装

对Axios进行统一封装,处理请求和响应:

import axios from 'axios'
import router from '../router'

// 创建axios实例
const service = axios.create({
  baseURL: process.env.BASE_API,
  timeout: 5000
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 在请求头中添加token
    if (localStorage.token) {
      config.headers['Authorization'] = localStorage.token
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code !== '200') {
      // token过期处理
      if (res.code === '401') {
        router.push('/login')
      }
      return Promise.reject(res)
    } else {
      return res.object
    }
  },
  error => {
    return Promise.reject(error)
  }
)

export default service

五、系统安全考虑

5.1 密码安全

用户密码使用SHA256加密存储:

public class MyUtils {
    public static String getSHA256StrJava(String str) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(str.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}

5.2 接口权限控制

使用JWT进行接口权限验证:

public class JwtInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null) {
            throw new RuntimeException("无token,请重新登录");
        }
        
        // 验证token
        Claims claims = JwtUtil.parseToken(token);
        if (claims == null) {
            throw new RuntimeException("token不合法");
        }
        
        // 将用户信息存入request
        request.setAttribute("userInfo", claims.get("userInfo"));
        return true;
    }
}

5.3 XSS防护

对用户输入进行过滤,防止XSS攻击:

public class XssFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(httpRequest);
        chain.doFilter(xssRequest, response);
    }
}

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }
    
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        return cleanXSS(value);
    }
    
    private String cleanXSS(String value) {
        if (value == null) return null;
        return HtmlUtils.htmlEscape(value);
    }
}

六、性能优化实践

6.1 前端性能优化

  1. 组件懒加载:路由组件按需加载

  2. 表格虚拟滚动:大数据量表格使用虚拟滚动

  3. 请求防抖:搜索框输入使用防抖处理

// 防抖处理
debounceSelectListPage: _.debounce(function() {
  this.selectListPage()
}, 500)

6.2 后端性能优化

  1. MyBatis二级缓存:启用MyBatis二级缓存减少数据库查询

  2. 分页优化:使用更高效的分页查询方式

  3. 批量操作:使用批量插入代替循环单条插入

// 批量插入示例
public void batchInsert(List<User> userList) {
    SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (User user : userList) {
        mapper.insert(user);
    }
    session.commit();
    session.clearCache();
    session.close();
}

七、总结与扩展

本文详细介绍了基于Vue.js和Spring Boot的前后端分离用户管理系统的实现。系统实现了用户管理的常见功能,包括:

  1. 用户信息的增删改查

  2. 批量操作

  3. 数据导入导出

  4. 分页查询与条件过滤

  5. 系统安全防护

扩展方向

  1. 角色权限管理:实现更细粒度的权限控制

  2. 操作日志:记录用户操作日志

  3. 数据可视化:增加用户数据统计图表

  4. 消息通知:系统消息通知功能

  5. 移动端适配:开发响应式布局或单独移动端应用

通过这个项目,开发者可以掌握前后端分离开发的核心技术栈,了解企业级应用开发的常见模式和最佳实践。希望本文能为您的开发工作提供有价值的参考。


网站公告

今日签到

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