第一步编写前端页面,第二步搭建后端环境,现在开始第三步,继续完善前端功能
完善“添加”按钮功能
Ajax异步请求安装
- 在前端项目安装ajax。在Terminal输入:npm i axios -S
点击“添加”按钮,弹出Dialog对话框
设置对话框里面的内容——表单
- 对话框选用Dialog
- 嵌入表单menu,二者一起构成了新增对话框
- 注意v-model,:model,变量,方法的设置及格式
<!--添加对话框-->
<el-dialog text v-model="dialogVisible" width="30%" title="提示">
<!--添加表单-->
<el-form :model="furn" label-width="120px" style="width:80%">
<el-form-item label="商品名">
<el-input v-model="furn.name" />
</el-form-item>
<el-form-item label="制造商">
<el-input v-model="furn.maker" />
</el-form-item>
<el-form-item label="价格">
<el-input v-model="furn.price" />
</el-form-item>
<el-form-item label="销量">
<el-input v-model="furn.sales" />
</el-form-item>
<el-form-item label="库存">
<el-input v-model="furn.stock" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">
确定
</el-button>
</span>
</template>
</el-dialog>
创建axios request
- 命名成了myRequest,便于区别request
// 引入axios
import axios from 'axios';
// 通过axios创建对象-request,用于发送请求到后端
const myRequest=axios.create({
timeout: 5000
})
// request拦截器处理
myRequest.interceptors.request.use(config=>{
config.headers['Content-Type']='application/json;charset=utf-8'
return config
},error => {
return Promise.reject(error)
})
export default myRequest
跨域异常:AxiosError/跨源
- 修改vue.config.js
module.exports = {
devServer: {
port: 10000,
proxy:{ //设置代理,必填
"/api":{ //设置拦截器,格式:/+名字
target:"http://localhost:8080" ,//目标地址,就是'/api'要替换的地址
changeOrigin:true, //设置是否同源,设为是。实现跨域
pathRewrite:{ //路径重写
'/api':'' //选择忽略拦截器里面的单词
}
}
}
}
}
- HomeView.vue修改ajax的访问url:"localhost:8080/save"改为。前后端即可通信
myRequest.post("/api/save",this.furn).then(
显示所有家居信息
思路分析
完成后台代码从mapper->service->controller,并使用Postman对代码进行测试
- 因为使用搭建的环境,mybatis-plus已经完成了mapper\service层,直接完成controller即可
@RequestMapping("/list")
public Result listFurns(){
List<Furn> furns = furnService.list();
return Result.success(furns);
}
完成前台代码,使用axios发送http请求,返回所有家居数据,将数据绑定显示
methods:{
list(){
myRequest.get("/api/list").then(res=>{
console.log("res=",res);
this.tableData=res.data.data;
})
},
调用显示数据的方法。VUE生命周期
created(){
this.list()
},
增加 处理响应后的结果
- 主要是练习这种处理方式。这儿的用处不大
- 实际产生的作用就是把response.data的数据层级拿出来了一层,达成下面这个效果
- this.tableData=res.data.data=>this.tableData=res.data;前端HomeView.vue
// response拦截器,返回结果处理
myRequest.interceptors.response.use(response=>{
let res=response.data
if(response.config.responseType==='blob'){
return res;
}
if(typeof res==='string'){
res=res? JSON.parse(res):res
}
return res;
})
在save()方法增加list(),完成刷新
- 之前在list()方法里面加入了list(),导致循环,致使view页面不能显示
- list()方法加在res=>{}括号里面,在页面能立马看到更新效果。原因是Ajax的异步机制
- Ajax有通讯,运行慢,=>{}而括号外的代码在本机执行快,就导致list更新在新数据到来之前就执行了,导致看不到更新效果。
save(){
...
this.list() //新增
},
回显家具信息,并完成修改
思路分析
完成后台代码从mapper->service->controller,并对代码进行测试
- 破案了。updateById()这个方法来自于接口IService,它是谁实现的呢?它自己!接口的默认实现
@PutMapping("/update")
public Result updateFurn(@RequestBody Furn furn){
log.info("待修改的furn="+furn);
furnService.updateById(furn);
return Result.success();
}
完成前台代码,回显家居信息[方式1:直接将点击的表格当前行的数据进行回显,方式2(先思考,尝试完成):根据当前行的id(家居id),到db查询对应的数据,进行回显],再使用axios发送http请求,更新数据,将数据绑定显示
- 获取当前行数据的方法,就是在该行@Click=handleEdit(scope.row),在方法区,使用该方法,带入参数(row)即可获取。插槽机制
- row对象转换成JSON格式的过程
- JSON.parse(JS0N.stringify(row))就是对行数据进行深拷贝
- 这样点击的表格当前行的数据和弹出框的数据是独立的
handleEdit(row){
// 输出原始row
console.log("row1=",row)
// 字符串化row
console.log("row2=",JSON.stringify(row))
// JSON格式row
console.log("row3=",JSON.parse(JSON.stringify(row)))
},
- 回显当前行数据。方式1:
methods:{
handleEdit(row){
this.form= JSON.parse(JSON.stringify(row)) //获取当前行数据。并放到表单
this.dialogVisible=true //显示表单
},
- 从后端拿到数据再回显。方式2:
- 修改当前行数据,以及成功状态返回。同样也存在Ajax异步,更新list()要放在then()里面
save(){
if(this.form.id){//此时执行更新
myRequest.put("/api/update",this.form).then(
res=>{
if(res.code==="200"){//更新成功
this.$message({
type: "success",
message: res.msg
})
}else{//更新失败
this.$message({
type:"error",
message:"更新数据失败"
})
}
this.dialogVisible = false
this.list()
})
}else{//此时执行添加
myRequest.post("/api/save",this.form).then(
res=>{
// res就是后端输出给前端的结果
console.log("res",res)
this.dialogVisible = false
this.list()
})
}
},
删除家居项
思路分析
完成后台代码从mapper->service->controller,并对代码进行测试
@DeleteMapping("/del/{id}")
public Result deleteFurn(@PathVariable Integer id){
furnService.removeById(id);
return Result.success();
}
完成前台代码,使用axios发送http请求,删除数据,将数据绑定显示
- 注意删除的方法是用@confirm在<el-popconfirm>绑定的,而不是在<el-button>用@click。
<template #default="scope">
<el-button link type="primary" size="small" @click="handleEdit(scope.row)"
>编辑</el-button>
<el-popconfirm title="确定删除这条记录?" @confirm="handleDel(scope.row)">
<template #reference>
<el-button link type="primary" size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
- handleDel()方法的具体实现
- 完成后用list()进行刷新。同样注意位置,避免ajax异步导致刷新异常。
handleDel(row){
myRequest.delete("/api/del/"+row.id).then(res=>{
if(res.code==="200"){
this.$message({
type:"success",
message:res.msg
})
}else{
this.$message({
type:"error",
message:res.msg
})
}
this.list()
})
}
分页功能
需求分析
1.显示共多少记录
2.可以设置每项显示几条
3.点击第几页,显示对应数据
思路分析
1.后台使用MyBatis-plus分页插件完成查询
2.修改FurnController,增加处理分页显示代码
3.完成前台代码,加入分页导航,并将分页请求和后台接口结合
创建config/MybatisplusConfig.java配置类。配置分页插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//需要注入的对象
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件
//设置数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
增加分页插件的调用处理
- application.yml配置输出日志。便于调试
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 完成后端分页功能
@RequestMapping("/page")
public Result page(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize){
Page<Furn> furnPage = furnService.page(new Page<>(pageNum, pageSize));
return Result.success(furnPage);
}
增加前端分页控件,并在数据池完善参数。
<div>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[5, 10]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalCount"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
完成控件功能
- 完善分页长度,显示指定页码内容的方法。注意方法参数名是自定义的,由控件传入
methods:{
handleSizeChange(pageSize){
this.pageSize=pageSize
this.list()
},
handleCurrentChange(currentPage){
this.currentPage=currentPage
this.list()
},
- 采用分页方法显示,重写显示列表
- 注意url添加参数的格式。而且参数名需要和后端接收参数名一致。
- 注意返回接收数据时,表数据和总条数分别来自于records和total。记不得可以利用console的输出,自己手动查询变量名称后完善。
list(){
// 采用分页方式更新数据列表
myRequest.get("/api/page",{
params:{
pageNum:this.currentPage,
pageSize:this.pageSize
}
}).then(res=>{
console.log("res=",res)
this.tableData=res.data.records
this.totalCount=res.data.total
})
},
切换数据源为DruidDataSource
- 检查在pom.xml是否已经配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
- 创建配置类,完成注入
@Configuration
@Slf4j
public class DruidDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource dataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
log.info("数据源={}",druidDataSource.getClass());
return druidDataSource;
}
}
- 注意@ConfigurationProperties("spring.datasource")是已经配置在application.yml的
spring:
datasource:
url: jdbc:mysql://localhost:3306/furn_ssm?useSSL=true&useUnicode=true&charactorEncoding=utf-8
password: root
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
带条件检索的分页显示
思路分析
完成后台代码从mapper->service->controller,并对代码进行测试
- 构造用的是Wrappers的静态方法,而不是new ...
- 带条件的分页查找。page(分页,条件queryWrapper)
@RequestMapping("/search")
public Result pageBySearch(
@RequestParam(defaultValue = "1")Integer pageNum,
@RequestParam(defaultValue = "5")Integer pageSize,
@RequestParam(defaultValue = "")String search
){
QueryWrapper<Furn> queryWrapper = Wrappers.query();
if(StringUtils.hasText(search)){
queryWrapper.like("name",search);
}
Page<Furn> furnPage =
furnService.page(new Page<>(pageNum, pageSize), queryWrapper);
return Result.success(furnPage);
}
完成前台代码,使用axios发送http请求,完成带条件查询分页显示
- 前端按键添加响应方法
<el-button @click="list">查询</el-button>
- 注意参数的增加
list(){
//改用带条件的分页查询
myRequest.get("/api/search",{
params:{
pageNum:this.currentPage,
pageSize:this.pageSize,
search:this.search
}
}).then(res=>{...