前后端分离: vue3+SpringBoot+ElementPlus+Axios+MyBatisPuls

前后端分离: vue3+SpringBoot

项目介绍

🌟项目页面


🌟技术栈:

1.前端技术栈: Vue3+Axios+ElementPlus

2.后端技术栈: SpringBoot+MyBatisPlus

3.数据库: MySQL

4.项目依赖管理: Maven

5.分页: MyBatisPlus的分页插件

6.切换数据源DruidDataSource

7.在LambdaQueryWrapper 引出知识点 lambda方法引用的 类名::实例方法

8.前端使用了axios关于request和respones的拦截器, 并且解决了跨域问题

gitee网址: https://gitee.com/zhao-zhiwei521/vue-springboot

搭建Vue前端工程

一,SSM整合-前后端分离(项目环境搭建)
二,SSM项目-前后端分离(搭建Vue前端工程)

vue create springboot_vue

axios请求响应拦截

js 复制代码
//重要提示: 如果在启动前端项目时, 提示找不到axios, 把光标放在 import axios from "axios"的'axios', 会有一个修复提示, 导入axios, 导入即可正常使用.
import axios from 'axios'

const axios_ = axios.create({
    timeout: 5000
})

//添加请求拦截器
axios_.interceptors.request.use(config => {
    //在请求之前请求头转为json数据
    config.headers['Content-Type'] = 'application/json;charset=utf-8'
    return config
}, error => {
    return Promise.reject(error)
})

//添加响应拦截器
axios_.interceptors.response.use(response => {
    console.log('response======>', JSON.stringify(response))

    let res = response.data;
    //如果返回的是文件
    if (response.config.responseType === 'blob') {
        return res;
    }
    //如果是string类型, 就转为json
    if (typeof res === 'string') {
        res = res ? JSON.parse(res) : res;
    }
    return res;
}, error => {
    return Promise.reject(error);
})

export default axios_

跨域

java 复制代码
module.exports = {
  devServer: {
    port: 10000, // 前端开发服务器运行端口(通过 http://localhost:10000 访问)
    proxy: {      // 代理配置(用于解决开发环境跨域问题)
      '/api': {   // 拦截所有以 /api 开头的请求
        target: 'http://localhost:9090/', // 后端服务地址(对应您的 Spring Boot 应用)
        changeOrigin: true, // 修改请求头中的 Origin 为目标地址(模拟同源请求)
        pathRewrite: {
          '^/api': '' // 路径重写:移除请求路径中的 /api 前缀
        }
      }
    }
  }
}

搭建后端

SSM整合-前后端分离(实现增删改查)

@TableId,@TableName

java 复制代码
//如果SysUser实体类名和数据库表名一致,或者经过驼峰换转后一致, 则不需要加@TableName注解
@Data
//@TableName("sys_user")
public class SysUser {

    //这里我们使用@TableId, 表主键标识
    //当我们在private Integer id上标识了@TableId, 说明id对应的就是表的id字段, 而且是主键
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
}

分页显示

思路:

1.后台使用mybatis-plus分页插件

2.前端使用vue分页组件

实现

1.src/main/java/com/zzw/springboot/config/MybatisPlusConfig.java

java 复制代码
@Configuration
public class MybatisPlusConfig {

    /**
     * 1.注入MybatisPlusInterceptor 对象/bean
     * 2.在MybatisPlusInterceptor bean 加入分页插件 PaginationInnerInterceptor
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //这里的分页要指定数据库的类型,因为不同的数据库,分页的sql语句不一样
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }
}

2.SysUserController 分页查询

java 复制代码
//前后端分离, 前端发出请求, 后端返回json数据
@Slf4j
@RestController
@RequestMapping("/sysUser")
public class SysUserController {
    @Autowired
    private SysUserService sysUserService;
    
    /**
     *
     * @param pageNum 显示第几页, 默认为 1
     * @param pageSize 每页有多少条 默认为10
     * @return
     */
    @GetMapping("listByPage")
    public Result listByPage(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
                             @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
        //1.在javaweb中, 分页查询已经学过
        Page<SysUser> page = sysUserService.page(new Page<>(pageNum, pageSize));
        //2.返回数据, 查询分页查询的数据结构
        return Result.success(page);
    }
}

3.vue分页组件

html 复制代码
<el-pagination
    v-model:current-page="searchForm.currentPage"
    v-model:page-size="searchForm.pageSize"
    :total="total"
    :page-sizes="[10, 20, 50, 100]"
    layout="total, sizes, prev, pager, next, jumper"
    @size-change="handleSizeChange"
    @current-change="handleCurrentChange"
/>

配置Druid数据源

参考整合Druid到SpringBoot

带条件的分页查询

java 复制代码
@GetMapping("listByCondition")
public Result listByCondition(@RequestParam(defaultValue = "1") Integer pageNum,
                              @RequestParam(defaultValue = "10") Integer pageSize,
                              SysUser sysUser) {

    //1.先床啊进QueryWrapper, 可以将我们的检索条件封装进QueryWrapper
    QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
    // 根据用户名模糊查询
    if (StringUtils.hasText(sysUser.getUsername()) ) {
        wrapper.like("username", sysUser.getUsername());
    }

    Page<SysUser> page = sysUserService.page(new Page<>(pageNum, pageSize), wrapper);
    return Result.success(page);
}

后端校验

java 复制代码
/**
 * 验证表单
 */
private Map<String, Object> validateForm(Errors errors) {
    Map<String, Object> errorMap = new HashMap<>();
    List<FieldError> fieldErrors = errors.getFieldErrors();
    for (FieldError fieldError : fieldErrors) {
        errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
    }
    return errorMap;
}
}

@PostMapping("/save")
    public Result save(@RequestBody @Validated SysUser sysUser, Errors errors) {
        Map<String, Object> map = validateForm(errors);
        if (!map.isEmpty()) {
            return Result.error(map, "数据校验失败");
        }
        boolean save = sysUserService.save(sysUser);
        if (save) {
            return Result.success("添加成功");
        }
        return Result.error("添加失败");
    }

lambda表达式说明

https://baijiahao.baidu.com/s?id=1652786021461159890&wfr=spider&for=pc

https://blog.csdn.net/hjl21/article/details/102702934

解读

java 复制代码
//编写方法,使用lambdaQueryWrapper封装查询条件, 完成检索
@GetMapping("listByCondition2")
public Result listByCondition2(@RequestParam(defaultValue = "1") Integer pageNum,
                              @RequestParam(defaultValue = "10") Integer pageSize,
                              SysUser sysUser) {

    //说明: 关于lambda表达式, 我们这里使用的是 类名::实例方法
    //是lambda方法引用中不太好理解的

    //解读
    //1. SysUser::getUsername 通过lambda表达式 引用实例方法 getUsername
    //2. 这里就是把 SysUser::getUsername 赋给 SFunction<T,R> 函数式接口
    //3. 查看 SFunction<T,R> 源代码
    /**
     * @FunctionalInterface
     * public interface SFunction<T, R> extends Function<T, R>, Serializable {
     * }
     * 父接口
     *@FunctionalInterface
     * public interface Function<T, R> {
     *      R apply(T t);//抽象方法, 表示根据类型T的参数, 获取类型R的结果
     *
     *      //这里有默认实现方法
     * }
     */
    //4.传入 SysUser::getUsername 后, 就相当于实现了 SFunction<T,R> 的apply方法
    //5.底层会根据 传入的 SysUser:getUsername 去得到该方法的对应的属性映射的表的字段, 更加灵活
    //<resultMap id="BaseResultMap" type="com.zzw.springboot.bean.SysMenu">
    //        <id column="id" jdbcType="INTEGER" property="id"/>

    //创建lambdaQueryWrapper, 封装查询条件
    LambdaQueryWrapper<SysUser> lambdaQueryWrapper = Wrappers.<SysUser>lambdaQuery();

    //判断
    if (StringUtils.hasText(sysUser.getUsername())) {
        //lambdaQueryWrapper.like(SysUser::getUsername, sysUser.getUsername());
        //换一个写法, 依然正确
        SFunction<SysUser, Object> sf = SysUser::getUsername;;
        lambdaQueryWrapper.like(sf, sysUser.getUsername());
    }

    Page<SysUser> page = sysUserService.page(new Page<SysUser>(pageNum, pageSize), lambdaQueryWrapper);
    log.info("page={}", page);
    return Result.success(page);
}

模拟实现src/main/java/com/zzw/springboot/lambda/Test.java

java 复制代码
public class Test {
    public static void main(String[] args) {
        //传统的方法来实现ZzwFuntcion接口, 使用匿名内部类, 得到一个实现接口的对象
        /**
         * class Test$1 implements ZzwFunction<AClass, Object> {
         *     public Object apply (Aclass aclass) {
         *          return "Aclass";
         *     }
         * }
         * Test$1 test$1 = new Test$1();
         * test$1.apply(new Aclass());
         */
        //ZzwFunction<AClass, String> zf = new ZzwFunction<AClass, String>() {
        //    @Override
        //    public String apply(AClass aClass) {
        //        return "AClass";
        //    }
        //};
        //Object result = zf.apply(new AClass());
        //System.out.println("result=" + result);
        ZzwFunction<AClass, String> zf2 = AClass::getBrand;
        Object result2 = zf2.apply(new AClass());
        System.out.println("result2=" + result2);
        //zf2.say();
    }
}

//定义一个函数式接口: 有且只有一个抽象方法的接口
//可以使用@FunctionalInterface 来标识一个函数式接口
//ZzwFunction是一个函数式接口(这个是一个自定义泛型接口)
@FunctionalInterface
interface ZzwFunction<T,R> {
    R apply(T t);//抽象方法: 表示根据类型T的参数, 获取类型R的结果

    //函数式接口, 依然可以有多个默认是先方法
    default public void say() {
        System.out.println("hello");
    }
}

class AClass {//Bean
    private String name = "lambda AClass";

    private String brand = "奔驰品牌";

    public String getName() {
        return name;
    }

    public String getBrand() {
        return brand;
    }
}
相关推荐
Howard_Stark几秒前
Spring的BeanFactory和FactoryBean的区别
java·后端·spring
饮长安千年月1 分钟前
学生管理系统审计
java·网络安全·代码审计
-曾牛9 分钟前
Spring Boot中@RequestParam、@RequestBody、@PathVariable的区别与使用
java·spring boot·后端·intellij-idea·注解·spring boot 注解·混淆用法
软件20513 分钟前
【UserDetailsService】
spring boot
新时代苦力工21 分钟前
处理对象集合,输出Map<String, Map<String, List<MyObject>>>格式数据,无序组合键处理方法
java·数据结构·list
极客智谷23 分钟前
Spring AI应用系列——基于Alibaba DashScope的聊天记忆功能实现
人工智能·后端
极客智谷25 分钟前
Spring AI应用系列——基于Alibaba DashScope实现功能调用的聊天应用
人工智能·后端
RJiazhen25 分钟前
5分钟让你的服务接入AI——用 API Auto MCP Server 实现大模型与后端系统的无缝对话
后端·开源·mcp
前端付豪28 分钟前
2、ArkTS 是什么?鸿蒙最强开发语言语法全讲解(附实操案例)
前端·后端·harmonyos
前端付豪31 分钟前
8、鸿蒙动画开发实战:做一个会跳舞的按钮!(附动效示意图)
前端·后端·harmonyos