【基础篇】六、基于SpringBoot来整合SSM的案例(下)

文章目录

  • 1、前后端调用:axios发送异步请求
  • 2、添加功能
  • 3、删除功能
  • 4、修改功能
  • 5、异常消息处理
  • 6、分页功能
  • 7、分页Bug处理
  • 8、条件查询

接下来加入前端页面,使用axios发送异步请求调用上篇的接口。调前端代码时,发现还挺有趣,刷新、隐藏、调用、以及一些交互逻辑的代码翻译,等框架学完看看前端的东西。
在这里插入图片描述

1、前后端调用:axios发送异步请求

关于前端资源文件的位置:

  • 前后端分离结构设计中页面归属前端服务器
  • 单体工程中页面放置在resources目录下的static目录中(建议执行clean)

调用下上篇的getAll接口,并将结果打印到console控制台来先调试下。前端发送异步请求,调用后端接口:

//列表
getAll() {axios.get("/books").then((res)=>{console.log(res.data);});
},

created钩子函数用于初始化页面时发起调用,如页面加载完后要调接口查全部图书:

//钩子函数,VUE对象初始化完成后自动执行
created() {//查全部this.getAll();
}

将查询数据返回到页面,利用前端数据双向绑定进行数据展示:

//列表
getAll() {axios.get("/books").then((res)=>{this.dataList = res.data.data;});
},

重启服务,刷新页面,列表功能实现。

在这里插入图片描述

2、添加功能

弹出添加窗口:

//弹出添加窗口
handleCreate() {this.dialogFormVisible = true;  //true即弹出
},

这样写,弹窗的窗口带有上次添加时的数据,需要在每次弹出前清除旧数据,即重置表单:

//重置表单
resetForm() {this.formData = {};
},

因此,弹出窗口应该是:

//弹出添加窗口
handleCreate() {this.dialogFormVisible = true;this.resetForm();
},

弹出添加窗口后,写数据,然后提交给后端:

//添加
handleAdd () {//发送异步请求axios.post("/books",this.formData).then((res)=>{//如果操作成功,关闭弹层,显示数据if(res.data.flag){this.dialogFormVisible = false;this.$message.success("添加成功");}else {this.$message.error("添加失败");}}).finally(()=>{this.getAll();});
},

以上即添加成功,则关闭弹层并给用户一个message添加成功。添加失败时不关闭弹层,方便用户修改后继续添加。不管成功与否,最后finally都刷新下列表。最后,当点弹窗的取消时,给用户一个取消成功的提示:

//取消
cancel(){this.dialogFormVisible = false;  //关闭弹窗this.$message.info("当前操作取消");
},

在这里插入图片描述

3、删除功能

基本逻辑和添加类似:删除接口返回成功时弹窗给用户提示删除成功,接口失败则返回删除失败。

// 删除
handleDelete(row) {axios.delete("/books/"+row.id).then((res)=>{if(res.data.flag){this.$message.success("删除成功");}else{this.$message.error("删除失败");}}).finally(()=>{this.getAll();});
}

这样一点删除按钮,就会调用删除接口,为了防止用户误操作,加$confirm,catch后面就是取消删除的逻辑:

// 删除
handleDelete(row) {//1.弹出提示框this.$confirm("确认要删除这条数据吗?","Tip",{type:'info'}).then(()=>{//2.做删除业务axios.delete("/books/"+row.id).then((res)=>{……}).finally(()=>{this.getAll();});}).catch(()=>{//3.取消删除this.$message.info("取消删除操作");});
}

删除这块,注意三点:

  • row对象,删除操作需要传递当前行数据对应的id值到后台,row.id(row的数据可以log.console看下)
  • 删除操作结束后动态刷新页面加载数据
  • 删除操作前弹出提示框避免误操作

在这里插入图片描述

4、修改功能

首先是弹出修改窗口,里面包含当前这条数据的信息:

//弹出编辑窗口
handleUpdate(row) {axios.get("/books/"+row.id).then((res)=>{if(res.data.flag){//展示弹层,加载数据this.formData = res.data.data;this.dialogFormVisible4Edit = true;}else{this.$message.error("数据不存在,已自动刷新页面");}});
},

(以下这个场景复现:复制页面,在一个页面后删除后,另一个页面未刷新,在这个页面点编辑,即编辑一条不存在的数据。PS:前面的删除应该也有这个场景,需要加个else)
在这里插入图片描述
以上:

  • 加载要修改数据通过传递当前行数据对应的id值到后台查询数据
  • 利用前端数据双向绑定将查询到的数据进行回显

写修改的逻辑:

//修改
handleEdit() {axios.put("/books",this.formData).then((res)=>{//如果操作成功,关闭弹层并刷新页面if(res.data.flag){this.dialogFormVisible4Edit = false;this.$message.success("修改成功");}else {this.$message.error("修改失败,请重试");}}).finally(()=>{this.getAll();});
},

取消编辑:

//和之前的取消复用一个函数
cancel(){this.dialogFormVisible = false;    //关闭新增弹窗窗口this.dialogFormVisible4Edit = false;  //关闭编辑弹窗窗口this.$message.info("操作取消");
},

到此,基本的增删改查的接口与页面完成。

5、异常消息处理

当接口请求出现异常时,返回体如下,和统一结果类R不一样,影响前端取值:

{
"timestamp": "2021-09-15T03:27:31.038+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/books"
}

鉴于此,加统一异常处理器,将异常处理成统一结果类返回给前端。修改R类,加msg字段:

@Data
public class R{private Boolean flag;private Object data;private String msg;public R(Boolean flag,Object data){this.flag = flag;this.data = data;}public R(Boolean flag,String msg){this.flag = flag;this.msg = msg;}//也可以定义个静态方法,里面封装构造方法public static R success(Object data){return new R(ture,data)}public static R error (String msg){return new R(false,msg);}}

定义全局异常处理器

@RestControllerAdvice
public class ProjectExceptionAdvice {@ExceptionHandler(Exception.class)public R doException(Exception ex){//记录日志//发送消息给运维//发送邮件给开发人员,ex对象发送给开发人员ex.printStackTrace();return R.error("服务异常,请稍后重试!");}
}

此时,前端的msg就别写死了,从响应里拿res.data.msg

//添加
handleAdd () {//发送异步请求axios.post("/books",this.formData).then((res)=>{//如果操作成功,关闭弹层,显示数据if(res.data.flag){this.dialogFormVisible = false;this.$message.success("添加成功");}else {this.$message.error(res.data.msg);}}).finally(()=>{this.getAll();});
},

当然,响应成功的提示也可以改为res.data.msg,后端返回结果加msg:

@PostMapping
public R save(@RequestBody Book book) throws IOException {Boolean flag = bookService.insert(book);return new R(flag , flag ? "添加成功^_^" : "添加失败-_-!");
}

在这里插入图片描述
最后,小总结:

  • 使用注解@RestControllerAdvice定义SpringMVC异常处理器用来处理异常的
  • 异常处理器必须被扫描加载,否则无法生效(当然@RestControllerAdvice注解里面包含了@Component,你注意扫描范围就好)
  • 表现层返回结果的模型类中添加消息属性msg用来传递消息到页面

6、分页功能

页面使用el分页组件添加分页功能:

<!--分页组件-->
<div class="pagination-container"><el-paginationclass="pagiantion"@current-change="handleCurrentChange":current-page="pagination.currentPage":page-size="pagination.pageSize"layout="total, prev, pager, next, jumper":total="pagination.total"></el-pagination>
</div>

定义分页组件需要使用的数据并将数据绑定到分页组件:

data:{pagination: { //分页相关模型数据currentPage: 1, //当前页码pageSize:10, //每页显示的记录数total:0, //总记录数}
},

将之前的查全部接口改为调用分页功能的接口,传入上面定义的currentPage、pageSize

getAll() {axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res) => {});
},

之前的分页接口:

@GetMapping("/{currentPage}/{pageSize}")
public R getAll(@PathVariable Integer currentPage,@PathVariable Integer pageSize){IPage<Book> pageBook = bookService.getPage(currentPage, pageSize);return new R(null != pageBook ,pageBook);}

加载分页数据,并给组件的页码和pageSize赋值,否则页码显示不对:

getAll() {axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res) => {this.pagination.total = res.data.data.total;this.pagination.currentPage = res.data.data.current;this.pagination.pagesize = res.data.data.size;this.dataList = res.data.data.records;});
},

从第一页切换到第二页,即切换页码的实现,就改下currentPage,并调用getAll:

//切换页码
handleCurrentChange(currentPage) {this.pagination.currentPage = currentPage;this.getAll();
},

效果:

在这里插入图片描述
分页功能的实现流程:

  • 使用el分页组件
  • 定义分页组件绑定的数据模型
  • 异步调用获取分页数据
  • 分页数据页面回显

7、分页Bug处理

场景:

一共两页数据,第二页仅有一条数据,删除这条数据后,实际只有一页数据,但前端页面仍停留在第二页。以下是查询接口的返回结果:

在这里插入图片描述

F12看到此时前端传参currentPage为2,后端接口修改下:对查询结果进行校验,当当前页码值大于总页码值时,就重新查询,使用最大页码值当作currentPage来查。IPage对象中就有数据总共能有几页的数据pages

@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable int currentPage,@PathVariable int pageSize){//先按传的查,拿到IPage对象IPage<Book> page = bookService.getPage(currentPage, pageSize);//如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值if( currentPage > page.getPages()){page = bookService.getPage((int)page.getPages(), pageSize);}return new R(true, page);
}

8、条件查询

查询条件数据封装,可单独封装,也可和之前的分页一起封装:

pagination: { //分页相关模型数据currentPage: 1, //当前页码pageSize:10, //每页显示的记录数total:0, //总记录数name: "",type: "",description: ""
}

页面数据模型绑定:

<div class="filter-container"><el-input placeholder="图书类别" v-model="pagination.type" class="filter-item"/><el-input placeholder="图书名称" v-model="pagination.name" class="filter-item"/><el-input placeholder="图书描述" v-model="pagination.description" class="filter-item"/><el-button @click="getAll()" class="dalfBut">查询</el-button><el-button type="primary" class="butT" @click="handleCreate()">新建</el-button>
</div>

组织数据成为get请求发送的数据,拼出一个请求路径,log.console看下param拼接是否正常:

getAll() {//1.获取查询条件,拼接查询条件param = "?name="+this.pagination.name;param += "&type="+this.pagination.type;param += "&description="+this.pagination.description;console.log("-----------------"+ param);axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize+param).then((res) => {this.pagination.total = res.data.data.total;this.pagination.currentPage = res.data.data.current;this.pagination.pagesize = res.data.data.size;this.dataList = res.data.data.records;});
},

修改Controller,接收条件查询的参数,GET下接收对象,不加@RequestBody:

@GetMapping("{currentPage}/{pageSize}")
public R getAll(@PathVariable int currentPage,@PathVariable int pageSize,Book book) {IPage<Book> pageBook = bookService.getPage(currentPage,pageSize);return new R(null != pageBook ,pageBook);}

修改Service层,使用LamdbaQueryWrapper对象拼接查询条件:

public interface IBookService extends IService<Book> {IPage<Book> getPage(Integer currentPage,Integer pageSize,Book queryBook);
}
@Service
public class BookServiceImpl2 extends ServiceImpl<BookDao,Book> implements IBookService {public IPage<Book> getPage(Integer currentPage,Integer pageSize,Book queryBook){IPage page = new Page(currentPage,pageSize);LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();lqw.like(Strings.isNotEmpty(queryBook.getName()),Book::getName,queryBook.getName());lqw.like(Strings.isNotEmpty(queryBook.getType()),Book::getType,queryBook.getType());lqw.like(Strings.isNotEmpty(queryBook.getDescription()),Book::getDescription,queryBook.getDescription());return bookDao.selectPage(page,lqw);}
}

最终效果:

在这里插入图片描述

到此,基础篇结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/82149.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的电动车租赁网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

最新AI创作系统+ChatGPT商业运营源码+支持GPT4.0+支持国内AI模型/支持AI绘画

一、AI创作系统 SparkAi系统是基于很火的GPT提问进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT系统&#xff1f;小编这里写一个详细图文教程吧&#x…

ADB底层原理

介绍 adb的全称为Android Debug Bridge&#xff0c;就是起到调试桥的作用。通过adb我们可以在Eclipse/Android Studio中方便通过DDMS来调试Android程序&#xff0c;说白了就是debug工具。adb是android sdk里的一个工具, 用这个工具可以直接操作管理android模拟器或者真实的and…

Kotlin simple convert ArrayList CopyOnWriteArrayList MutableList

Kotlin simple convert ArrayList CopyOnWriteArrayList MutableList Kotlin读写分离CopyOnWriteArrayList_zhangphil的博客-CSDN博客Java并发多线程环境中&#xff0c;造成死锁的最简单的场景是&#xff1a;多线程中的一个线程T_A持有锁L1并且申请试图获得锁L2&#xff0c;而多…

【数据库系统概论】数据模型

数据模型是什么两类数据模型两步抽象概念模型数据模型 常用的数据模型感谢 &#x1f496; 数据模型是什么 模型是对现实世界中某个对象特征的模拟和抽象。比如飞机模型就体现了飞机的特性&#xff0c;它模拟飞机的起飞、飞行和降落&#xff0c;它抽象了飞机的基本特征——机头…

C++笔记之文档术语——将可调用对象作为函数参数

C笔记之文档术语——将可调用对象作为函数参数 相关博文&#xff1a;C笔记之函数对象functors与可调用对象 文章目录 C笔记之文档术语——将可调用对象作为函数参数1.在函数参数中传递可调用对象2.‘在参数中传入可调用对象’和‘将可调用对象作为函数参数’哪个描述更加专业…

数据库----数据查询

1.6 查询语句 语法&#xff1a;select [选项] 列名 [from 表名] [where 条件] [group by 分组] [order by 排序][having 条件] [limit 限制]1.6.1 字段表达式 mysql> select 锄禾日当午; ------------ | 锄禾日当午 | ------------ | 锄禾日当午 | ---…

SQL死锁进程内容查询语句

1.方式1 SELECT object_name(A.resource_associated_entity_id) as TABLENAME, A.request_session_id AS SPID,DB_NAME(B.dbid) AS DBName,B.blocked,B.dbid,B.program_name,B.waitresource,B.lastwaittype,B.loginame,B.hostname,B.login_time,B.last_batch--,B.* FROM sy…

redis 集群(cluster)

1. 前言 我们知道&#xff0c;在Web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999% 等等&#xff09;。但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xf…

【面试经典150 | 双指针】判断子序列

文章目录 写在前面Tag题目来源题目解题解题思路方法一&#xff1a;双指针方法二&#xff1a;动态规划 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对…

Eclipse开源代码下载

当前插件开发&#xff0c;需要修改eclipse源码&#xff0c;如需要修改remote相关的代码&#xff0c;所以需要下载相关源码。网上大多资料都说的不清不楚的&#xff0c;也可能我太小白&#xff0c;不明白&#xff0c;反正就是折腾了一两天才感觉有点思路&#xff0c;改如何找源码…

virtualbox共享文件夹设置

宿主机是mac os&#xff0c;虚拟机是centos7.9。 一、virtualbox设置共享文件夹 选中虚拟机->设置->共享文件夹->点击号图标进行添加&#xff1a; 二、给虚拟机安装增强功能 打开virtualbox的虚拟机窗口界面&#xff0c;点击窗口顶部菜单中的设备->安装增强功能…

正则表达式学习和高级用法

以下所有的验证都在 在线验证 1. 起始符 / 正则表达式的起始符2. 限定符 匹配前面的子表达式**1次或多次**。例如&#xff0c;zo 能匹配 "zo" 以及"zoo"&#xff0c;但不能匹配 "z"。等价于 {1,}。 ? 匹配前面的子表达式**0次或1次**。例如…

Java基础入门·对存储文件File的相关操作

前言 File类获取的方法 getName() | getPath() File getAbsoluteFile() | File getParentFile() long length() File类遍历方法 IO流对象的分类 1.按照操作的文件类型分类 2.按照数据的流向分类 IO流对象的分类归纳 OutputStream 字节输出流写入文件的步骤 追加写入 F…

电路中VCC VDD VSS VEE GND的含义

在电路中&#xff0c;芯片引脚经常会出现VCC&#xff0c;VDD&#xff0c;VSS&#xff0c;VEE和GND这些标示。 其中VCC一般表示通用芯片的电源引脚&#xff0c;比如一些模拟运放的正电源引脚&#xff0c;74系列数字芯片的电源引脚&#xff0c;VCC一般接相应的正电源电压。 VDD一…

Mock数据:单元测试中的心灵鸡汤

在当今的软件开发领域&#xff0c;质量控制已经成为了一个不可或缺的环节。为了确保软件的稳定性和可靠性&#xff0c;开发者们投入了大量的时间和精力进行各种测试。其中&#xff0c;单元测试作为最基础的测试方法&#xff0c;其重要性不言而喻。然而&#xff0c;单元测试中的…

用于视觉检测的线扫远心镜头VS-LTC系列

VS-LTC 系列线扫远心镜头&#xff0c;近期已经新增 3.5倍及5倍型号 &#xff0c;支持大靶面芯片相机&#xff1b;适用于半导体&#xff0c;基板以及 Mini LED等行业的视觉检测。 在机器视觉产品资料查询平台&#xff0c;了解更多VS-LTC工业镜头的信息。 大靶面线扫远心镜头 …

swift 页面跳转

segue 故事板的屏幕之间导航 设置全局变量 退回操作 Optionals ??

vue项目打包_以生产环境prod模式打包_vue-cli-service 不是内部或外部命令,也不是可运行的程序---vue工作笔记0025

打开命令行: 首先执行npm install 不执行会报错: npm run build:prod --scripts-prepend-node-pathauto 然后再这样执行就是以生产环境模式打包了.

lv5 嵌入式开发-2 exec函数族

目录 1 进程 – exec函数族 1.1 exec函数族特点 1.2 进程 – execl / execlp使用方法 1.3 进程 – execv / execvp 2 进程 – system 3 exec族要点演示 掌握&#xff1a;exec函数族、system 1 进程 – exec函数族 执行程序&#xff0c;通孔ps -elf发现&#xff0c;父进…