目录
SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--后端实现
技术栈
实现功能04-添加家居信息
需求分析/图解
思路分析
代码实现
创建\service\FurnService.java 和\service\FurnServiceImpl.java, 增加添加方法
修改Furn.java , 当创建Furn 对象imgPath 为null 时, imgPath 给默认值
创建FurnServiceTest.java ,测试方法
创建Msg.java用来返回json 的数据的通用类
创建FurnController.java , 处理添加请求
使用Postman 来完成Controller 层的测试, 通过Postman 添加Furn 数据
点击添加按钮, 可以出现添加家居的对话框, 修改
完成测试: 看看点击新增按钮,能否正常的弹窗添加家居的对话框(含有表单)
项目前端安装axios, 用于发送Ajax 请求给后台, 一定要注意
创建工具文件D:\idea_java_projects\ssm_vue\src\utils\request.js , 用于创建axios
修改HomeView.vue , 在methods 编写save 方法, 并测试会出现跨域问题
创建vue.config.js 解决跨域问题, 因为修改了配置文件
修改Home.vue, 使用跨域请求, 并完成测试, 查看数据库,是否有新数据添加成功
注意事项和细节
实现功能05-显示家居信息
需求分析/图解
思路分析
代码实现
FurnService.java
FurnServiceImpl
修改FurnServiceTest.java ,测试findAll.
修改FurnController.java , 处理显示请求, 并使用Postman 完成测试
修改HomeView.vue , 编写list 方法
完成测试,看看是否可以显示家居列表信息.
修改src\utils\request.js 增加response 拦截器, 统一处理响应后结果
修改Home.vue , 简化返回处理
完成测试.
实现功能06-修改家居信息
需求分析/图解
思路分析
代码实现
修改FurnService.java接口
修改FurnServiceImpl.java
修改FurnServiceTest.java ,测试update
修改FurnController.java , 处理修改请求, 并使用Postman 完成测试
修改HomeView.vue , 编写handleEdit 方法, 回显数据并测试
可以测试一下, 点击编辑, 回显数据
修改HomeView.vue , 修改save 方法,
完成测试
实现功能07-删除家居信息
需求分析/图解
思路分析
代码实现
修改FurnService.java接口
修改FurnServiceImpl.java
修改FurnServiceTest.java ,测试del
修改FurnController.java , 处理删除请求, 并使用Postman 完成测试
修改HomeView.vue , 编写handleDel 方法, 完成删除
完成测试测试
将修改家居信息功能, 回显家居表单数据, 改成从后端-DB 获取
修改FurnService.java接口
修改FurnServiceImpl.java
修改FurnServiceTest.java ,处理根据id返回对应的furn对象
修改FurnController.java , 处理根据id返回对应的furn对象, 并使用Postman 完成测试
修改HomeView.vue , 编写handleEdit 第二种方法, 回显数据并测试
实现功能08-分页显示列表
需求分析/图解
思路分析
代码实现
1. 修改pom.xml 加入分页插件
2. 修改mybatis-config.xml, 配置分页拦截器
3. 修改FurnController.java 增加分页查询处理
修改FurnServiceTest.java ,测试分页查询是否OK
使用Postman 进行测试,看看分页查询是否OK
修改HomeView.vue , 完成分页导航显示、分页请求
增加element-plus 分页控件
完成测试
分页显示效果
实现功能09-带条件查询分页显示列表
需求分析/图解编辑
思路分析
代码实现
修改FurnService.java接口
修改FurnServiceImpl.java
修改FurnController.java , 处理带条件分页查询
修改FurnServiceTest.java ,测试条件分页查询是否OK
使用Postman 测试,是否通过
修改HomeView.vue , 完成带条件分页查询
测试分页条件查询
实现功能10-添加家居表单前端校验
说明: 参考element-plus 表单验证
思路分析
代码实现
修改HomeView.vue , 增加表单验证处理代码
测试
修改Homeview.vue 当表单验证不通过时,不提交表单
完成测试
实现功能11-添加家居表单后端校验
需求分析/图解
2. 后端校验-需求分析, 当后端校验没有通过,会出现灰色框提示, 后台不真正入库数据
思路分析
代码实现
1. 修改pom.xml 引入hibernate-validator jar 文件
2. 修改Furn.java , 使用hibernate-validator
3. 修改FurnController.java , 对save 方法进行完善
4. 修改HomeView.vue , 显示服务器校验返回的提示信息
完成测试: 添加家居表单后端校验
测试页面效果
测试完毕后, 记得恢复valid 的正确写法
SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--后端实现
技术栈
SSM 项目【前后端分离】
- 说明: 前后端分离开发, 前端框架Vue + 后端框架SSM
1. 前端框架Vue
2. 后台框架-SSM(SpringMVC+Spring+MyBatis)
3. 数据库-MySQL
4. 项目的依赖管理-Maven
5. 分页-pagehelper。
6. 逆向工程-MyBatis Generator。
7. 其它...
-前端框架Vue
1. 使用了ElementPlus 来展示数据。
2. 使用Axios 对象来请求数据/走接口。
-后端框架SSM
1. 使用经典的三层结构。
2. 使用MyBatis Generator 和MyBatis pageHelper。
实现功能04-添加家居信息
需求分析/图解
思路分析
1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试, 到controller 这一层,使用Postman 发送http post 请求完成测试
2. 完成前端代码, 使用axios 发送ajax(json 数据)给后台, 实现添加家居信息
代码实现
创建\service\FurnService.java 和\service\FurnServiceImpl.java, 增加添加方法
FurnService接口
public interface FurnService {
//添加
public void save(Furn furn);
}
FurnServiceImpl
@Service
public class FurnServiceImpl implements FurnService {
//注入/装配FurnMapper接口对象(代理对象)
@Resource
private FurnMapper furnMapper;
@Override
public void save(Furn furn) {
//解读
//1. 使用insertSelective
//2. 因为我们的furn表的id是自增的,就使用insertSelective
furnMapper.insertSelective(furn);
}
修改Furn.java , 当创建Furn 对象imgPath 为null 时, imgPath 给默认值
private String imgPath = "assets/images/product-image/1.jpg";
public Furn(Integer id, String name, String maker, BigDecimal price,Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
this.sales = sales;
this.stock = stock;
if(!(imgPath == null || imgPath.equals("")) ){
this.imgPath = imgPath;
}
}
创建FurnServiceTest.java 测试方法
public class FurnServiceTest {
//属性
private ApplicationContext ioc;
//从spring容器中,获取的是FurnService接口对象/代理对象
private FurnService furnService;
@Before
public void init() {
ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//说明
//1. 通过FurnService.class 类型获取 FurnService接口对象/代理对象
furnService = ioc.getBean(FurnService.class);
//com.sun.proxy.$Proxy21
System.out.println("furnService-" + furnService.getClass());
}
@Test
public void save() {
Furn furn =
new Furn(null, "小风扇", "wyx家居", new BigDecimal(180), 10,
70, "assets/images/product-image/1.jpg");
furnService.save(furn);
System.out.println("添加成功~");
}
}
创建Msg.java用来返回json 的数据的通用类
package com.wyxdu.furn.bean;
import java.util.HashMap;
import java.util.Map;
/**
* Msg: 后端程序返回给前端的json数据的Msg对象=》本质就是数据规则
*/
public class Msg {
//状态码 200-成功 400-失败
private int code;
//信息-说明
private String msg;
//返回给客户端/浏览器的数据-Map集合
private Map<String, Object> extend =
new HashMap<>();
//编写几个常用的方法-封装好msg
//返回success对应的msg
public static Msg success() {
Msg msg = new Msg();
msg.setCode(200);
msg.setMsg("success");
return msg;
}
//返回fail对应的msg
public static Msg fail() {
Msg msg = new Msg();
msg.setCode(400);
msg.setMsg("fail");
return msg;
}
//给返回的msg设置数据-不难应该可以看懂
public Msg add(String key, Object value) {
extend.put(key, value);
return this;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, Object> getExtend() {
return extend;
}
public void setExtend(Map<String, Object> extend) {
this.extend = extend;
}
}
创建FurnController.java , 处理添加请求
@Controller
public class FurnController {
//注入配置FurnService
@Resource
private FurnService furnService;
/**
* 解读
* 1、响应客户端的添加请求
* 2、@RequestBody: 使用 SpringMVC 的 @RequestBody 将客户端提交的 json 数据,封装成 JavaBean 对象
* 3、@ResponseBody: 服务器返回的数据格式是按照 json 来返回的(底层是按照http协议进行协商)
*/
@PostMapping("/save")
@ResponseBody
public Msg save(@RequestBody Furn furn) {
furnService.save(furn);
return Msg.success();
}
使用Postman 来完成Controller 层的测试, 通过Postman 添加Furn 数据
使用Postman 测试时,因为我们前台是发送的json 数据,被服务器接收到后,转成javabean 数据,因此pom.xml 需要引入jackson,处理json 数据,
否则后台会报错.Content type 'application/json;charset=UTF-8' not supported
<!-- 引入jackson,处理json 数据-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
点击添加按钮, 可以出现添加家居的对话框, 修改
SSM-Vue 整合项目\HomeView.vue ,
说明el-dialog 从Dialog 对话框获取, 表单代码从Form 表单获取,组合一下并调整一下即可
<!-- 添加家居的弹窗
说明:
1. el-dialog :v-model="dialogVisible" 表示对话框, 和dialogVisible 变量双向
绑定,控制是否显示对话框
2. el-form :model="form" 表示表单,数据和form 数据变量双向绑定
3. el-input v-model="form.name" 表示表单的input 空间,名字为name 需要和
后台Javabean 属性一致
-->
<el-dialog title="提示" v-model="dialogVisible" width="30%">
<el-form :model="form" label-width="120px">
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<el-form-item label="厂商">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<el-form-item label="价格">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<el-form-item label="销量">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<el-form-item label="库存">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
//增加数据, 一定要, 否则你会发现,在后面弹出的表单不能输入数据
data() {
return {
form: {},
//增加方法
methods: {
add() {
this.dialogVisible = true
this.form = {}
}
}
//增加点击新增的按钮事件
<div style="margin: 10px 0">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其它</el-button>
</div>
完成测试: 看看点击新增按钮,能否正常的弹窗添加家居的对话框(含有表单)
项目前端安装axios, 用于发送Ajax 请求给后台, 一定要注意
创建工具文件D:\idea_java_projects\ssm_vue\src\utils\request.js , 用于创建axios
// 引入axios 包
// 重要提示:如果在启动前端项目,提示找不到axios , 把光标放在import axios from 'axios' 的'axios', 会有一个修复提示, 导入axios, 小伙伴点击, 导入即可正常使用
import axios from 'axios'
// 通过axios 创建对象
const request = axios.create({
timeout: 5000
})
// request 拦截器
// 1. 可以对请求做一些处理
// 2. 比如统一加token,Content-Type 等
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
return config
}, error => {
return Promise.reject(error)
});
//导出
export default request
修改HomeView.vue , 在methods 编写save 方法, 并测试会出现跨域问题
<script>
// 引入request 组件
import request from "@/utils/request";
//methods 增加方法.
save() {
// =======说明====...
request.post("http://localhost:10001/save", this.form).then(res => {
console.log(res)
this.dialogVisible = false
})
}
</script>
创建vue.config.js 解决跨域问题, 因为修改了配置文件
npm serve 需要重启, 否则不能识别.
const {defineConfig} = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
module.exports = {
devServer: {
port: 10000, // 启动端口
//解读如果我们请的地址 /api/save => 代理到 http://localhost:8080/ssm/save
proxy: { //设置代理,必须填
'/api': { //设置拦截器 拦截器格式 斜杠+拦截器名字,名字可以自己定
target: 'http://localhost:8080/ssm', //代理的目标地址, 就是/api 代替 http://localhost:10001/
changeOrigin: true, //是否设置同源,输入是的, 浏览器就允许跨域
pathRewrite: { //路径重写
'/api': '' //选择忽略拦截器里面的单词
}
}
}
}
}
修改Home.vue, 使用跨域请求, 并完成测试, 查看数据库,是否有新数据添加成功
1. 将form 表单提交给/api/save 的接口
2. /api/save 等价http∶//locaLhost∶10001/save
3. 如果成功,就进入then 方法
4.res 就是返回的信息
5.查看mysql 看看数据是否保存
request.post("/api/save", this.form).then(res => {
console.log(res)
this.dialogVisible = false
})
}
提醒, 这里容易出现的问题
1) 一定要确定request.post("/api/save") 被代理后的url , 是项目后台服务对应提供的API接口url , 否则报404
2) 当跨域执行时请求,浏览器还是提示http://localhost:5927/api/xxx , 所以不要认为是api没有替换你的配置.
注意事项和细节
1. Postman 测试时, 要指定content-type ,否则会报错415
2. 如果需要将提交的json 数据, 封装到对应的Javabean, 需要配置@RequestBody , 否则会报错500
3. 如果需要返回json 数据, 需要在方法上, 配置@ResponseBody , 否则会报错404
实现功能05-显示家居信息
需求分析/图解
思路分析
1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码, 使用axios 发送http 请求,返回所有家居数据, 将数据绑定显示
代码实现
修改FurnService.java 和FurnServiceImpl.java, 增加findAll 方法
FurnService.java
//查询所有的家居信息
public List<Furn> findAll();
FurnServiceImpl
@Override
public List<Furn> findAll() {
//查看分析FurnMapper.xml 文件
//传入是null, 表示返回所有的家居信息
return furnMapper.selectByExample(null);
}
修改FurnServiceTest.java ,测试findAll.
@Test
public void findAll() {
List<Furn> furns = furnService.findAll();
for (Furn furn : furns) {
System.out.println("furn-" + furn);
}
}
修改FurnController.java , 处理显示请求, 并使用Postman 完成测试
@RequestMapping("/furns")
@ResponseBody
public Msg listFurns() {
List<Furn> furnList = furnService.findAll();
//Msg msg = Msg.success();
把家居信息,封装到msg对象
//msg.add("furnList", furnList);
return Msg.success().add("furnList", furnList);
}
修改HomeView.vue , 编写list 方法
//修改一下el-table
<el-table : data="tableData" stripe style="width: 100%">
<el-table-column
prop="id"
label="ID"
sortable
>
</el-table-column>
<el-table-column
prop="name"
label="家居名"
>
</el-table-column>
<el-table-column
prop="maker"
label="厂家">
</el-table-column>
<el-table-column
prop="price"
label="价格">
</el-table-column>
<el-table-column
prop="sales"
label="销量">
</el-table-column>
<el-table-column
prop="stock"
label="库存">
</el-table-column>
<el-table-column fixed="right" label="操作" width="100">
<template #default="scope">
<el-button @click="handleEdit(scope.row)" type="text">编辑</el-button>
<el-button type="text">删除</el-button>
</template>
</el-table-column>
</el - table >
//修改一下tableData: []
data() {
return {
form: {},
dialogVisible: false,
search: '',
tableData: []
}
}
//在created() 调用list() 完成页面数据获取
created() {
this.list()
}
//编写list() method
list() { //请求显示家居列表-带检索
request.get("/api/furns").then(res => {
//绑定tableData, 显示在表格
this.tableData = res.data.extend.furnsList
})
}
//在save() 调用后,调用list() 刷新页面
save() {
// =======说明====...
request.post("/api/save", this.form).then(res => {
console.log(res)
this.dialogVisible = false
this.list()
})
}
完成测试,看看是否可以显示家居列表信息.
修改src\utils\request.js 增加response 拦截器, 统一处理响应后结果
//引入axios
import axios from "axios";
//通过axios创建对象-request对象,用于发送请求到后端
const request = axios.create({
timeout: 5000
})
//request拦截器的处理
//1. 可以对请求做统一的处理
//2. 比如统一的加入token, Content-Type等
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8'
return config
}, error => {
return Promise.reject(error)
})
//response拦截器
//可以在调用接口响应后,统一的处理返回结果
request.interceptors.response.use(
response => {
let res = response.data
//如果返回的是文件,就返回
if (response.config.responseType === 'blob') {
return res
}
//如果是string, 就转成json对象
if (typeof res === 'string') {
//如果res 不为null, 就进行转换成json对象
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log("err", error)
return Promise.reject(error);
}
)
//导出request对象, 在其它文件就可以使用
export default request
修改Home.vue , 简化返回处理
完成测试.
实现功能06-修改家居信息
需求分析/图解
思路分析
1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码, 回显家居信息,再使用axios 发送http / ajax 请求,更新数据, 将数据绑定显示
代码实现
修改FurnService.java接口 和FurnServiceImpl.java, 增加update 方法
修改FurnService.java接口
//修改家居
public void update(Furn furn);
修改FurnServiceImpl.java
@Override
public void update(Furn furn) {
furnMapper.updateByPrimaryKeySelective(furn);
}
修改FurnServiceTest.java ,测试update
@Test
public void update() {
Furn furn = new Furn();
furn.setId(1);
furn.setName("北欧风格小桌子~~");
furn.setMaker("小猪家居");
//因为imgPath属性有一个默认值,
//所以如果我们不希望生成update 语句有对imgPath 字段修改,就显式的设置null
furn.setImgPath(null);
furnService.update(furn);
System.out.println("修改OK");
}
修改FurnController.java , 处理修改请求, 并使用Postman 完成测试
@PutMapping("/update")
@ResponseBody
public Msg update(@RequestBody Furn furn) {
furnService.update(furn);
return Msg.success();
}
修改HomeView.vue , 编写handleEdit 方法, 回显数据并测试
handleEdit(row) {
//说明
//1. JSON.stringify(row) 将row 转成json 字符串
//2. JSON.parse(xx) 将字符串转成json 对象
//3. 为什么这样做? 其实JSON.parse(JSON.stringify(row)) 就是对row 进行了深拷贝
//4. 这样表格中的行数据和弹出框的数据就是独立的了
this.form = JSON.parse(JSON.stringify(row))
this.dialogVisible = true
}
//触发handleEdit 方法
<el-button size="mini" @click="handleEdit(scope.row)" type="primary">编辑</el-button>
可以测试一下, 点击编辑, 回显数据
修改HomeView.vue , 修改save 方法,
处理修改请求, 说明更新成功的消息框, 不需要做额外处理, 直接使用this.$message 即可.
save() {
//增加处理修改逻辑
if (this.form.id) {
request.put("/api/update", this.form).then(res => {
if (res.code === 200) {//如果code 为200
this.$message({ //弹出更新成功的消息框
type: "success",
message: "更新成功"
})
} else {
this.$message({//弹出更新失败信息
type: "error",
message: res.msg
})
}
this.list() //刷新列表
this.dialogVisible = false
})
} else {//添加
//=======说明======
//1. 将form 表单提交给/api/save 的接口
//2. /api/save 等价http://localhost:10001/save
//3. 如果成功,就进入then 方法
//4. res 就是返回的信息
//5. 查看Mysql 看看数据是否保存
request.post("/api/save", this.form).then(res => {
this.dialogVisible = false
this.list()
})
}
}
完成测试
浏览器http://localhost:10000
实现功能07-删除家居信息
需求分析/图解
思路分析
1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码,使用axios 发送http Ajax 请求,删除数据, 将数据绑定显示
代码实现
修改FurnService.java 和FurnServiceImpl.java, 增加del 方法
修改FurnService.java接口
//删除家居
public void del(Integer id);
修改FurnServiceImpl.java
@Override
public void del(Integer id) {
furnMapper.deleteByPrimaryKey(id);
}
修改FurnServiceTest.java ,测试del
@Test
public void del() {
furnService.del(17);
System.out.println("删除OK");
}
修改FurnController.java , 处理删除请求, 并使用Postman 完成测试
@DeleteMapping("/del/{id}")
@ResponseBody
public Msg del(@PathVariable Integer id) {
furnService.del(id);
return Msg.success();
}
修改HomeView.vue , 编写handleDel 方法, 完成删除
//处理删除方法
handleDel(id) {
request.delete("/api/del/" + id).then(res => {
if (res.code === 200) {
this.$message({
type: "success",
message: "删除成功"
})
} else {
this.$message({
type: "error",
message: res.msg
})
}
this.list() // 刷新列表
})
}
//响应删除点击
<template #default="scope">
<el-button size="mini" @click="handleEdit(scope.row)" type="primary">编辑</el-button>
<!--增加popcomfirm 控件,确认删除-- >
<el-popconfirm
title="确定删除吗?" @confirm="handleDel(scope.row.id)" >
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el - popconfirm >
</template >
完成测试测试
将修改家居信息功能, 回显家居表单数据, 改成从后端-DB 获取
修改FurnService.java接口 和FurnServiceImpl.java, 增加根据id返回数据
修改FurnService.java接口
//根据id返回Furn
public Furn findById(Integer id);
修改FurnServiceImpl.java
@Override
public Furn findById(Integer id) {
return furnMapper.selectByPrimaryKey(id);
}
修改FurnServiceTest.java ,处理根据id返回对应的furn对象
@Test
public void findById(){
Furn byId = furnService.findById(2);
System.out.println(byId);
}
修改FurnController.java , 处理根据id返回对应的furn对象, 并使用Postman 完成测试
//提供接口,根据id返回对应的furn对象-封装Msg
@GetMapping("/find/{id}")
@ResponseBody
public Msg findById(@PathVariable Integer id) {
Furn furn = furnService.findById(id);
return Msg.success().add("furn", furn);
}
修改HomeView.vue , 编写handleEdit 第二种方法, 回显数据并测试
handleEdit(row) {
//console.log("row--", row)
//将当前的家居信息绑定到弹出对话框的form
//1. 方式1: 可以通过row.id 到后端-DB去获取对应的家居信息, 返回后将其绑定 this.form[这个是小伙伴可以解决-思考]
//2. 方式2: 把获取的row的数据通过处理,绑定到this.form 进行显示
//3. JSON.stringify(row): 将row 转成json字符串
//4. JSON.parse(JSON.stringify(row)) 将json字符串转成json对象
//this.form = JSON.parse(JSON.stringify(row))
//可以通过row.id 到后端-DB去获取对应的家居信息
request.get("/api/find/" + row.id).then(res => {
// console.log("家居信息-", res.extend.furn)
this.form = res.extend.furn
})
this.dialogVisible = true
}
实现功能08-分页显示列表
需求分析/图解
思路分析
1. 后台使用MyBatis PageHelper 插件完成分页查询, 前端我们使用分页组件
2. 修改FurnController , 增加处理分页显示代码API/接口
3. 完成前台代码,加入分页导航,并将分页请求和后台接口结合
4. 说明:有了现在的MyBatis PageHelper和前端的分页组件,完成分页就非常的方便,但是底层的分页模型和前面我们的java web原生项目一样
代码实现
1. 修改pom.xml 加入分页插件
<!-- 引入mybatis pageHelper 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
2. 修改mybatis-config.xml, 配置分页拦截器
<!-- plugins 标签要放在typeAliases 标签后面-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 分页合理化,如果pageNum > pages,就让他查询最后一页。
如果pageNum < 0,就查询第一页-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
3. 修改FurnController.java 增加分页查询处理
/**
* 分页请求接口
*
* @param pageNum: 要显示第几页 : 默认为1
* @param pageSize: 每页要显示几条记录:默认为5
* @return
*/
@ResponseBody
@RequestMapping("/furnsByPage")
public Msg listFurnsByPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize) {
//设置分页参数
//解读
//1.调用findAll是完成查询,底层会进行物理分页,而不是逻辑分页
//2.会根据分页参数来计算 limit ?, ?, 在发出SQL语句时,会带limit
PageHelper.startPage(pageNum, pageSize);
List<Furn> furnList = furnService.findAll();
//将分页查询的结果,封装到PageInfo
//PageInfo 对象包含了分页的各个信息,比如当前页面pageNum , 共有多少记录
//...
PageInfo pageInfo = new PageInfo(furnList, pageSize);
//将pageInfo封装到Msg对象,返回
return Msg.success().add("pageInfo", pageInfo);
}
修改FurnServiceTest.java ,测试分页查询是否OK
@Test
public void findById(){
Furn byId = furnService.findById(2);
System.out.println(byId);
}
使用Postman 进行测试,看看分页查询是否OK
修改HomeView.vue , 完成分页导航显示、分页请求
增加element-plus 分页控件
//增加element-plus 分页控件
<div style="margin: 10px 0">
<el-pagination
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5,10]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div >
</div >
</template >
//增加分页初始化数据
data() {
return {
currentPage: 1,
pageSize: 5,
total: 10,
//修改list(), 换成分页请求数据
list() { //请求显示家居列表-不带检索
request.get("/api/furnsByPage", {
params: {
pageNum: this.currentPage,
pageSize: this.pageSize
}
}).then(res => {
//绑定tableData, 显示在表格
this.tableData = res.extend.pageInfo.list
this.total = res.extend.pageInfo.total
})
}
//增加方法, 处理记录的变化, 这两个方法是和分页控件绑定的.
//处理每页显示多少条记录变化
handlePageSizeChange(pageSize) {
this.pageSize = pageSize
this.list()
}
,
//处理当前页变化, 比如点击分页连接,或者go to 第几页
handleCurrentChange(pageNum) {
this.currentPage = pageNum
this.list()
}
完成测试
启动项目后台服务furns_ssm
启动项目前台ssm_vue
浏览器: http://localhost:10000/
分页显示效果
● 测试分页显示效果, 浏览器: http://localhost:10000/
实现功能09-带条件查询分页显示列表
需求分析/图解
思路分析
1. 完成后台代码从dao -> serivce -> controller , 并对每层代码进行测试
2. 完成前台代码,使用axios 发送http 请求,完成带条件查询分页显示
代码实现
修改FurnService.java 和FurnServiceImpl.java , 增加条件查询
修改FurnService.java接口
//根据家居名称进行查询
public List<Furn> findByCondition(String name);
修改FurnServiceImpl.java
@Override
public List<Furn> findByCondition(String name) {
FurnExample furnExample = new FurnExample();
//通过Criteria 对象可以设置查询条件
FurnExample.Criteria criteria = furnExample.createCriteria();
//判断name是有具体的内容
if (StringUtils.hasText(name)) {
criteria.andNameLike("%" + name + "%");
}
//说明:如果name没有传值null ,"", " ", 依然是查询所有的记录
return furnMapper.selectByExample(furnExample);
}
修改FurnController.java , 处理带条件分页查询
/**
* 根据家居名进行分页查询-条件
*
* @param pageNum
* @param pageSize
* @return
*/
@ResponseBody
@RequestMapping("/furnsByConditionPage")
public Msg listFurnsByConditionPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize,
@RequestParam(defaultValue = "") String search) {
PageHelper.startPage(pageNum, pageSize);
List<Furn> furnList = furnService.findByCondition(search);
PageInfo pageInfo = new PageInfo(furnList, pageSize);
//将pageInfo封装到Msg对象,返回
return Msg.success().add("pageInfo", pageInfo);
}
@Test
public void findByCondition() {
List<Furn> furns = furnService.findByCondition("风格");
for (Furn furn : furns) {
System.out.println("furn--" + furn);
}
}
修改FurnServiceTest.java ,测试条件分页查询是否OK
@Test
public void findByCondition() {
List<Furn> furns = furnService.findByCondition("风格");
for (Furn furn : furns) {
System.out.println("furn--" + furn);
}
}
使用Postman 测试,是否通过
修改HomeView.vue , 完成带条件分页查询
========增加搜索区==========
< !--功能区域-->
<div style="margin: 10px 0">
<i class="el-icon-add-location"></i>
<el-button type="primary" @click="add">新增</el-button>
</div>
< !--搜索区域-->
<div style="margin: 10px 0">
<el-input v-model="search" placeholder=" 请输入关键字" style="width: 20%"
clearable></el-input>
<el-button type="primary" style="margin-left: 5px" @click="list">检索</el-button>
</div>
===========在数据池,增加search 变量=====
==========修改list 方法,请求带条件分页的API 接口===
测试分页条件查询
实现功能10-添加家居表单前端校验
说明: 参考element-plus 表单验证
思路分析
1. 完成前台代码,使用ElementPlus 的表单rules 验证即可
2. 参考ElementPlus 的表单验证文档
代码实现
修改HomeView.vue , 增加表单验证处理代码
==========增加对表单各个字段的校验规则=========
tableData: [],
rules: {
name: [
{ required: true, message: '请输入称家居名', trigger: 'blur' }
],
maker: [
{ required: true, message: '请输入称制造商', trigger: 'blur' }
],
price: [
{ required: true, message: '请输入价格', trigger: 'blur' },
{ pattern: /^(([1-9]\d*)|(0))(\.\d+)?$/, message: '请输入数字', trigger: 'blur' }
],
sales: [
{ required: true, message: '请输入销量', trigger: 'blur' },
{ pattern: /^(([1-9]\d*)|(0))$/, message: '请输入数字', trigger: 'blur' }
],
stock: [
{ required: true, message: '请输入库存', trigger: 'blur' },
{ pattern: /^(([1-9]\d*)|(0))$/, message: '请输入数字', trigger: 'blur' }
]
}
==========指定将创建的规则应用到form 表单, 注意名称要对应=========
添加家居的弹窗
说明:
1. el-dialog :v-model="dialogVisible" 表示对话框, 和dialogVisible 变量双向绑定,控制是否显示对话框
2. el-form :model="form" 表示表单,数据和form 数据变量双向绑定
3. el-input v-model="form.name" 表示表单的input 空间, 名字为name 需要和后台Javabean 属性一致
测试
就可以看到验证规则生效了【是光标离开输出框时,出现校验效果,因为是trigger:'blur' 事件】, 但是用户提交还是能成.
修改Homeview.vue 当表单验证不通过时,不提交表单
========修改save()===========
save() {
//增加处理修改逻辑
if (this.form.id) {
request.put("/api/update", this.form).then(res => {
if (res.code === 200) {//如果code 为200
this.$message({ //弹出更新成功的消息框
type: "success",
message: "更新成功"
})
} else {
this.$message({//弹出更新失败信息
type: "error",
message: res.msg
})
}
this.list() //刷新列表
this.dialogVisible = false
})
} else {//添加
//表单数据校验是否
this.$refs['form'].validate((valid) => {
if (valid) {
//=======说明======
//1. 将form 表单提交给/api/save 的接口
//2. /api/save 等价http://localhost:10001/save
//3. 如果成功,就进入then 方法
//4. res 就是返回的信息
//5. 查看Mysql 看看数据是否保存
request.post("/api/save", this.form).then(res => {
this.dialogVisible = false
this.list()
})
} else {
this.$message({//弹出更新失败信息
type: "error",
message: "验证失败,不提交"
})
return false
}
})
}
}
===========修改add()============
add() {
this.dialogVisible = true
this.form = {}
this.$refs['form'].resetFields()//将添加验证提示消息,清空
}
完成测试
实现功能11-添加家居表单后端校验
需求分析/图解
1. 为什么前端校验了,后端还需要校验?-看老韩使用Postman 添加数据, 破前端校验机制.
2. 后端校验-需求分析, 当后端校验没有通过,会出现灰色框提示, 后台不真正入库数据
思路分析
1. 后台使用JSR303 数据校验,引入hibernate-validator.jar ,在SpringMVC的博客有详细的 链接
2. 前台使用ElementPlus 进行数据绑定,并显示错误信息
代码实现
1. 修改pom.xml 引入hibernate-validator jar 文件
<!-- JSR303 数据校验支持 引入hibernate-validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
2. 修改Furn.java , 使用hibernate-validator
package com.wyxdu.furn.bean;
import org.hibernate.validator.constraints.Range;
import org.springframework.util.StringUtils;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
public class Furn {
private Integer id;
@NotEmpty(message = "请输入家居名")
private String name;
@NotEmpty(message = "请输入制造厂商")
private String maker;
@NotNull(message = "请输入数字")
@Range(min = 0, message = "价格不能小于0")
private BigDecimal price;
@NotNull(message = "请输入数字")
@Range(min = 0, message = "销量不能小于0")
private Integer sales;
@NotNull(message = "请输入数字")
@Range(min = 0, message = "库存不能小于0")
private Integer stock;
//说明: 修改Furn.java ,
// 当创建Furn对象 imgPath 为null时, imgPath 给默认值(默认图片路径)
private String imgPath = "assets/images/product-image/1.jpg";
//增加一个全参构造器
public Furn(Integer id, String name, String maker, BigDecimal price, Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
this.sales = sales;
this.stock = stock;
//解读
//如果imgPath 不为null ,而且是有数据,就设置给 this.imgPath,否则就使用默认值
//imgPath != null && !imgPath.equals("") =>使用一个工具类方法完成
/**
* public static boolean hasText(@Nullable String str) {
* return str != null && !str.isEmpty() && containsText(str);
* }
*/
//StringUtils.hasText(imgPath) 就是要求imgPath 不是 null ,而且不是"", 而且不是" "
//该方法后面小伙伴会常常使用
if (StringUtils.hasText(imgPath)) {
this.imgPath = imgPath;
}
}
//声明无参构造器
public Furn() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public String getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker == null ? null : maker.trim();
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath == null ? null : imgPath.trim();
}
@Override
public String toString() {
return "Furn{" +
"id=" + id +
", name='" + name + '\'' +
", maker='" + maker + '\'' +
", price=" + price +
", sales=" + sales +
", stock=" + stock +
", imgPath='" + imgPath + '\'' +
'}';
}
}
3. 修改FurnController.java , 对save 方法进行完善
@PostMapping("/save")
@ResponseBody
public Msg save(@Validated @RequestBody Furn furn, Errors errors) {
Map<String, Object> map = new HashMap<>();
List<FieldError> fieldErrors = errors.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
if (map.isEmpty()) {//说明后端校验通过,因为没有发现校验错误
furnService.save(furn);
//返回成功msg
return Msg.success();
} else {
//校验失败,把校验错误信息封装到Msg对象,并返回
return Msg.fail().add("errorMsg", map);
}
}
4. 修改HomeView.vue , 显示服务器校验返回的提示信息
===========在数据池,增加显示错误信息变量========
data() {
return {
//存放错误信息
serverValidErrors: {},
================修改save()方法,显示错误提示============
save() {
//增加处理修改逻辑
if (this.form.id) {
request.put("/api/update", this.form).then(res => {
if (res.code === 200) {//如果code 为200
this.$message({ //弹出更新成功的消息框
type: "success",
message: "更新成功"
})
} else {
this.$message({//弹出更新失败信息
type: "error",
message: res.msg
})
}
this.list() //刷新列表
this.dialogVisible = false
})
} else {//添加
//表单数据校验是否
this.$refs['form'].validate((valid) => {
if (valid) {
//=======说明======
//1. 将form 表单提交给/api/save 的接口
//2. /api/save 等价http://localhost:10001/save
//3. 如果成功,就进入then 方法
//4. res 就是返回的信息
//5. 查看Mysql 看看数据是否保存
request.post("/api/save", this.form).then(res => {
if (res.code === 200) {
this.dialogVisible = false
this.list()
} else if (res.code === 400) {
this.serverValidErrors.name = res.extend.errorMsg.name;
this.serverValidErrors.sales = res.extend.errorMsg.sales;
this.serverValidErrors.price = res.extend.errorMsg.price;
this.serverValidErrors.maker = res.extend.errorMsg.maker;
this.serverValidErrors.stock = res.extend.errorMsg.stock;
}
})
} else {
this.$message({//弹出更新失败信息
type: "error",
message: "验证失败,不提交"
})
return false
}
})
}
}
==========修改add()方法,清空错误信息=========
add() {
this.dialogVisible = true
this.form = {}
this.$refs['form'].resetFields()//将上传验证消息,清空
this.serverValidErrors = {}
},
========修改对话框,显示后台返回的校验错误信息========
<!--添加家居的弹窗
说明:
1. el-dialog :v-model="dialogVisible" 表示对话框, 和dialogVisible 变量双向绑定,控制是否
显示对话框
2. el-form :model="form" 表示表单,数据和form 数据变量双向绑定
3. el-input v-model="form.name" 表示表单的input 空间,名字为name 需要和后台Javabean
属性一致
-->
<el-dialog title="提示" v-model="dialogVisible" width="30%">
<el-form :model="form" :rules="rules" ref="form" label-width="120px">
<el-form-item label="家居名" prop="name">
<el-input v-model="form.name" style="width: 60%"></el-input>
{{ serverValidErrors.name }}
</el-form-item>
<el-form-item label="厂商" prop="maker">
<el-input v-model="form.maker" style="width: 60%"></el-input>
{{ serverValidErrors.maker }}
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input v-model="form.price" style="width: 60%"></el-input>
{{ serverValidErrors.price }}
</el-form-item>
<el-form-item label="销量" prop="sales">
<el-input v-model="form.sales" style="width: 60%"></el-input>
{{ serverValidErrors.sales }}
</el-form-item>
<el-form-item label="库存" prop="stock">
<el-input v-model="form.stock" style="width: 60%"></el-input>
{{ serverValidErrors.stock }}
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
完成测试: 添加家居表单后端校验
2、这时, 后台返回添加失败的提示信息
启动项目前台ssm_vue
浏览器: http://localhost:10000/
测试页面效果
测试完毕后, 记得恢复valid 的正确写法
本项目就已经全部完成