AI 摘要

本文记录 Spring Boot + Vue.js 实现 EAM 的完整后端搭建与前后端联调过程:先建库建表并初始化数据,IDEA 新建 Spring Boot 2.6.13 项目,集成 Lombok、MyBatis Plus 等依赖,配置数据源与分页插件;用 MyBatisCodeHelperPro 一键生成实体、Mapper、Service,封装统一响应类。接着完成资产增删改查接口,解决 Axios 提交 JSON 与后端表单接收类型不匹配问题,借助 qs 转换并统一 baseURL,实现模糊分页查询、新增、删除、回显与修改功能,并通过 Apifox 自测通过,前后端运行效果正常。

开发笔记

打开数据库连接软件,连接到本地 MySQL 数据库,并新建数据库。

在数据库中,根据项目要求,创建资产数据表,并为资产数据表初始化几条测试数据。

打开 IDEA,新建 Spring Boot 项目。

选择 Spring Boot 2.6.13,并选择项目依赖 Lombok、Spring Web、MySQL Driver、MyBatis Plus、HuTool。

修改 Spring Boot 项目配置文件(classpath:/application.properties),编写 MySQL 数据库配置、MyBatis Plus 配置。

# 服务器端口号
server.port=8082
# SQL映射文件路径,在resources文件夹下的mappers文件夹里
mybatis-plus.mapper-locations=classpath:mappers/*xml

# MySQL 数据库连接信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/zichan?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456..

在项目中创建基本的包结构和文件夹结构。

在 IDEA 中连接到本地 MySQL 数据库。

借助 MyBatisCodeHelperPro 插件,快捷为资产数据表生成实体类、数据访问层、业务逻辑层的代码片段。

代码片段生成完成,注意检查实体类中相关的数据类型,以及代码完整性。

新建统一响应实体类(package.utils.ResponseResult),封装响应体。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseResult implements Serializable {

    // 响应码
    private Integer code;

    // 响应信息
    private String message;

    // 响应数据
    private Object data;

}

新建 MyBatis Plus 配置类(package.config.MyBatisPlusConfig),编写分页插件配置。

@Configuration
public class MyBatisPlusConfig {
  
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
        return interceptor;
    }
  
}

前置工作完成,正式开始编写业务代码。

新建资产数据控制器(package.controller.ZichanController),添加基本注解,注入 Service 层。

@CrossOrigin(origins = "*") // 允许跨域
@RequestMapping("/api/zichan")    // 指定统一请求前缀为/api/zichan
@RestController // 指定该控制器下的方法全部返回字符串而不是视图,即@Controller + @ResponseBody的结合体
public class ZichanController {

    @Resource
    ZichanService zichanService;

}

修改资产数据控制器(package.controller.ZichanController),编写模糊查询资产列表的接口方法。

@GetMapping("/getList")
public String getList(@RequestParam(name = "name", required = false, defaultValue = "") String name,
                      @RequestParam(name = "pageNo", required = false, defaultValue = "1") Integer pageNo) {

    // 根据资产名称模糊查询资产列表
    LambdaQueryWrapper<Zichan> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    // 如果资产名称参数不为空,则根据资产名称模糊查询
    lambdaQueryWrapper.like(!name.isEmpty(), Zichan::getName, name);

    // 实例化分页对象,传入页码和页大小
    Page<Zichan> zichanPage = new Page<>(pageNo, 3);
  
    // 调用Service根据条件查询分页数据
    zichanService.page(zichanPage, lambdaQueryWrapper);

  
    // 将结果封装到统一响应实体中,并转换成JSON字符串返回给前端
    return JSONUtil.toJsonStr(new ResponseResult(200, "查询成功", zichanPage));
}

在 Apifox 中新建接口,对模糊查询资产列表的接口方法进行测试。

运行项目,查看效果。

修改资产列表视图页面组件(src/views/List.vue),编写获取资产列表的方法。

methods: {
    // 定义获取资产列表的方法
    getList() {
        axios.get('http://localhost:8082/api/zichan/getList?name=' + this.searchName + '&pageNo=' + this.pager.pageNo)
            .then(ret => {
                // 根据后端接口文档传递参数,并接收响应结果
                this.zichanList = ret.data.data.records
                this.pager.pageSize = ret.data.data.size
                this.pager.total = ret.data.data.total
                // 后端没有返回页码总数,所以需要手动计算
                // 页码总数 = 总数据数 / 页大小,并向上取整
                this.pager.pages = Math.ceil(this.pager.total / this.pager.pageSize)
            })
            .catch(error => {
                this.$message.error('请求失败')
            })
    },
    // ... 
}

运行项目,查看效果。

修改资产数据控制器(package.controller.ZichanController),编写新增资产数据的接口方法。

@PostMapping("/add")
public String add(@RequestParam(name = "type") Integer type,
                  @RequestParam(name = "status") Integer status,
                  @RequestParam(name = "name") String name,
                  @RequestParam(name = "usage") String usage,
                  @RequestParam(name = "place") String place) {

    // 将数据保存到实体类中
    Zichan zichan = new Zichan();
    zichan.setType(type);
    zichan.setStatus(status);
    zichan.setName(name);
    zichan.setUsage(usage);
    zichan.setPlace(place);

    // 生成资产编号
    zichan.setCode(IdUtil.randomUUID());
    // 生成入库时间
    zichan.setDate(DateUtil.formatDateTime(new Date()));

    // 调用Service新增数据
    if (zichanService.save(zichan)) {
        return JSONUtil.toJsonStr(new ResponseResult(200, "新增成功", null));
    } else {
        return JSONUtil.toJsonStr(new ResponseResult(-1, "新增失败", null));
    }
}

在 Apifox 中新建接口,对新增资产数据的接口方法进行测试。

运行项目,查看效果。

修改新增资产视图页面组件(src/views/Add.vue),编写发送表单请求的方法。

axios.post('http://localhost:8082/api/zichan/add', this.zichan)
// ...

运行项目,查看效果。

此时项目报 400 错误,请求参数与控制器接收的参数不匹配。

原因在于,Axios 发送 POST/PUT 请求时,传递的参数是以 JSON 类型(application/json)进行传递的,而控制器方法中是以表单类型(application/x-www-form-urlencoded)进行接收的。

在脚手架项目中,可以借助 qs 将请求参数转换为表单类型再提交。

在项目中进入终端,安装 qs。

npm install qs

修改项目入口配置文件(src/main.js),导入 Qs。

import qs from "qs"
Vue.prototype.$qs = qs

修改新增资产视图页面组件(src/views/Add.vue),编写发送表单请求的方法。

axios.post('http://localhost:8082/api/zichan/add', this.$qs.stringify(this.zichan))
// ...

运行项目,查看效果。

修改资产数据控制器(package.controller.ZichanController),编写删除资产数据的接口方法。

@DeleteMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
    // 调用Service删除数据
    if(zichanService.removeById(id)) {
        return JSONUtil.toJsonStr(new ResponseResult(200, "删除成功", null));
    } else {
        return JSONUtil.toJsonStr(new ResponseResult(-1, "删除失败", null));
    }
}

在 Apifox 中新建接口,对删除资产数据的接口方法进行测试。

运行项目,查看效果。

修改资产列表视图页面组件(src/views/List.vue),编写删除数据请求的方法。

axios.delete('http://localhost:8082/api/zichan/delete/' + id)
// ...

运行项目,查看效果。

修改资产数据控制器(package.controller.ZichanController),添加根据 id 获取资产数据的接口方法。

@GetMapping("/getById/{id}")
public String getById(@PathVariable Integer id) {
    return JSONUtil.toJsonStr(new ResponseResult(200, "查询成功", zichanService.getById(id)));
}

在 Apifox 中新建接口,对根据 id 获取资产数据的接口方法进行测试。

运行项目,查看效果。

修改修改资产视图页面组件(src/views/Modify.vue),完善表单数据回显。

axios.get('http://localhost:8082/api/zichan/getById/' + this.$route.params.id)
// ...

运行项目,查看效果。

修改资产数据控制器(package.controller.ZichanController),添加修改资产数据的接口方法。

@PutMapping("/modify/{id}")
public String modify(@PathVariable Integer id,
                     @RequestParam(name = "type") Integer type,
                     @RequestParam(name = "status") Integer status,
                     @RequestParam(name = "name") String name,
                     @RequestParam(name = "usage") String usage,
                     @RequestParam(name = "place") String place) {

    // 将数据保存到实体类中
    Zichan zichan = new Zichan();
    zichan.setId(id);
    zichan.setType(type);
    zichan.setStatus(status);
    zichan.setName(name);
    zichan.setUsage(usage);
    zichan.setPlace(place);

    // 调用Service修改数据
    if (zichanService.updateById(zichan)) {
        return JSONUtil.toJsonStr(new ResponseResult(200, "修改成功", null));
    } else {
        return JSONUtil.toJsonStr(new ResponseResult(-1, "修改失败", null));
    }
}

在 Apifox 中新建接口,对修改资产数据的接口方法进行测试。

运行项目,查看效果。

修改修改资产视图页面组件(src/views/Modify.vue),完善发送表单请求的方法。

axios.put('http://localhost:8082/api/zichan/modify/' + this.zichan.id, this.$qs.stringify(this.zichan))
// ...

运行项目,查看效果。

为了方便维护,可以修改 Axios 配置文件(src/plugins/axios.js),配置统一请求前缀。

let config = {
    baseURL: "http://localhost:8082"
    // timeout: 60 * 1000, // Timeout
    // withCredentials: true, // Check cross-site Access-Control
};

在发送 Axios 请求时,就不需要重复添加请求前缀了。

axios.get('/api/zichan/getList?name=' + this.searchName + '&pageNo=' + this.pager.pageNo)
// ...