功能实现02
3.功能03-添加家居信息
3.1需求分析
浏览器页面点击添加按钮,弹出提示框,填写家居信息,点击确定,可以把数据添加到数据库中。
3.2思路分析
- 完成后台代码 dao->service->controller,并对每层代码进行测试,到controller层使用postman发送http post请求完成测试
- 完成前端代码,使用axios 发送ajax请求(json数据)给后台,实现添加家居信息
3.3代码实现
![image-20230306205850299](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230306205850299.png)
后端项目使用的是 ssm 框架,src 下分为 bean,dao,service,controller,utils层,其中 controller 层由springmvc 接管,service 层由 spring 接管,dao 层由 mybatis 接管。
相比于传统的 Javaweb 项目,它的三层结构并没有发生根本变化,仅仅是框架化了,各组件由框架接管。原本的 jsp 则页面变成了 vue 前端项目。
3.3.1后端代码
由于使用了逆向工程,bean 层、dao 层以及dao层接口对应的mapper映射文件已经生成,因此暂时不必在这两层编写代码了。
(1)Service层,创建对应的接口和实现类
FurnService.java(接口)
package com.li.furn.service;
import com.li.furn.bean.Furn;
/**
* @author 李
* @version 1.0
*/
public interface FurnService {
//添加
public void save(Furn furn);
}
FurnServiceImpl.java(实现类)
package com.li.furn.service.impl;
import com.li.furn.bean.Furn;
import com.li.furn.dao.FurnMapper;
import com.li.furn.service.FurnService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @author 李
* @version 1.0
*/
@Service //注入spring容器
public class FurnServiceImpl implements FurnService {
//自动装配 FurnMapper接口对象(代理对象)
@Resource
private FurnMapper furnMapper;
//已经在spring配置文件中开启事务
@Override
public void save(Furn furn) {
furnMapper.insertSelective(furn);
}
}
测试代码:
package com.li.furn.test.service;
import com.li.furn.bean.Furn;
import com.li.furn.service.FurnService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.math.BigDecimal;
/**
* @author 李
* @version 1.0
*/
public class FurnServiceTest {
//spring容器
private ApplicationContext ioc;
//从spring容器中获取的是FurnService接口的代理对象
private FurnService furnService;
@Before
public void init() {
ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过FurnService.class类型获取FurnService接口对象的代理对象
furnService = ioc.getBean(FurnService.class);
}
@Test
public void FurnServiceTest() {
//添加数据
Furn furn = new Furn(null, "复古沙发", "森林之家",
new BigDecimal(1088), 12, 28,
"/assets/images/product-image/7.jpg");
furnService.save(furn);
System.out.println("添加成功");
//关闭sqlSession的动作底层会自动释放
}
}
测试成功:
![image-20230306212956088](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230306212956088.png)
![image-20230306213121934](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230306213121934.png)
(2)关于家居图片路径
给新增的家居增加一个默认图片的路径值,修改Furn.java(部分代码):
//默认图片路径
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 && !imgPath.equals("")
// =>使用StringUtils.hasText()代替,该方法要求传入的字符串不是null,并且不是"",并且不能全为空格
if (StringUtils.hasText(imgPath)) {
this.imgPath = imgPath;
}
}
(3)bean层创建Msg.java,用来返回给前端 Json 数据的通用类,本质就是一个”协议“
![image-20230306220803505](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230306220803505.png)
package com.li.furn.bean;
import java.util.HashMap;
import java.util.Map;
/**
* @author 李
* @version 1.0
* 用来返回给前端Json数据的通用类
*/
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;//返回的是当前Msg对象
}
//省略setter,getter方法
}
(4)创建FurnController.java,处理添加家居请求
package com.li.furn.controller;
import com.li.furn.bean.Furn;
import com.li.furn.bean.Msg;
import com.li.furn.service.FurnService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
/**
* @author 李
* @version 1.0
*/
@Controller//由springmvc来处理
public class FurnController {
@Resource
private FurnService furnService;
/**
* 1.响应客户端添加家居的请求
* 2.@RequestBody 注解将客户端提交的json数据封装成 Javabean 对象。
* 3.@ResponseBody 服务器返回的数据是按照json来返回的(底层是按照 http协议进行协商)
*
* @param furn
* @return
*/
@ResponseBody
@PostMapping("/save")
public Msg save(@RequestBody Furn furn) {
furnService.save(furn);
//如果没有出现异常,就返回成功
Msg success = Msg.success();
return success;
}
}
(5)Postman对Controller层进行测试
使用postman测试时,因为我们前台发送的是json数据,被服务器接收到后,转成Javabean数据,因此pom.xml需要引入jackson,处理json数据,否则后台会报错。
<!--引入jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.4</version> </dependency>
![image-20230307204911642](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230307204911642.png)
请求结果:
3.3.2前端代码
(1)修改HomeView.vue页面,代码略。
<template>
<div>
<!--增加按钮和搜索框-->
<div style="margin:10px 5px">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="margin:10px 5px">
<el-input v-model="search" style="width: 30%" placeholder="请输入关键字"/>
<el-button style="margin-left: 10px" type="primary">查找</el-button>
</div>
<!--表格-->
...
<!--添加家居的弹窗
1.el-dialog v-model="dialogVisible" 表示对话框,
和 dialogVisible 变量双向绑定,控制是否显示对话框
2.el-form:mode="form" 表示表单数据和form数据变量双向绑定
3.el-input v-mode="form.name" 表示表单的input控件,
名字为name,必须需要和后端Javabean属性一致
4.在前端中,对象的属性是可以动态生成的-->
<el-dialog title="提示" v-model="dialogVisible" width="40%">
<el-form :model="form" label-width="120px">
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
...
...
</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>
<script>
import request from '@/utils/request.js'
//导出组件
export default {
name: 'HomeView',
components: {},
data() {
return {
search: '',
dialogVisible: false,
form: {},//定义一个空表单
tableData: [...]
}
},
methods: {
add() {//显示添加对话框
//显示对话框
this.dialogVisible = true;
//每次点击都要清空上一次的表单数据
this.form = {};
},
save() {//将填写的表单数据发送给后端
//第一个参数为url,第二个参数是请求携带的数据
request.post("/api/save", this.form).then(res => {
console.log("res-", res)
this.dialogVisible = false;//隐藏表单
})
}
}
}
</script>
效果如下:
![image-20230308193159713](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230308193159713.png)
(2)安装axios,用于发送ajax请求给后台
![image-20230308193549442](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230308193549442.png)
(3)在前端项目的src目录下创建utlis目录,utils目录下创建工具文件request.js,用于创建axios request对象,发送ajax请求。
//引入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)
})
//导出request对象,在其他文件引入即可使用
export default request;
如果启动前端项目,提示找不到axios,需要把光标放在import axios from ‘axios’ 的提示上,会有一个修复提示,点击导入axios即可。
(4)修改HomeView.vue,在methods编写save方法,并测试。
...
save() {//将填写的表单数据发送给后端
//第一个参数为url,第二个参数是请求携带的数据
request.post('http://localhost:8080/save', this.form).then(res => {
console.log("res-", res)
this.dialogVisible = false;//隐藏表单
})
}
...
测试向后端项目发送请求,因为前后端的服务ip不一致,会出现跨域问题,浏览器会阻止访问:
![image-20230308200155412](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230308200155412.png)
(5)在项目目录下的vue.config.js,解决跨域问题
...
module.exports = {
devServer: {
port: 10001,//启动端口
proxy: {//设置代理,必须填
'/api': {//设置拦截器 拦截器格式
target: 'http://localhost:8080/',//代理的目标地址,就是/api代替的地址
changeOrigin: true,//是否设置同源,如果为true,就允许跨域
pathRewrite: {//路径重写
'/api': ''//选择忽略拦截器里面的单词
}
}
}
}
}
因为修改了配置文件,npm serve 需要重启,否则不能识别。
再次测试,成功发送数据:
![image-20230308203141229](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230308203141229.png)
3.4注意事项和细节
- 一定要确定request.post("/api/save")被代理后的url是项目后台服务对应提供的api接口url,否则报404
- postman测试时,要指定content-type,否则会出错(415错误)
- 如果后端需要将json提交的数据封装到对应的Javabean中,需要配置@RequestBody,否则会报错500
- 如果后端需要返回json数据,需要在方法上配置@ResponseBody,否则会报错404
4.功能04-显示家居信息
4.1需求分析
在页面中展示数据库的家居信息。
4.2思路分析
- 完成后台代码从dao-service-controller,并对每层代码进行测试
- 完成前台代码,使用axios发送http请求,返回所有家居数据,将数据绑定展示
4.3代码实现
4.3.1后端代码
分层完成,由于使用了逆向工程,bean 层、dao 层以及dao层接口对应的mapper映射文件已经生成,因此暂时不必在这两层编写代码了。
(1)service层,修改FurnService.java和FurnServiceImpl.java,增加findAll()方法。(展示不考虑分页问题)
FurnService.java:
//查询所有的家居信息
public List<Furn> findAll();
FurnServiceImpl.java:
@Override
public List<Furn> findAll() {
//如果传入为null表示返回所有的家居信息
return furnMapper.selectByExample(null);
}
(2)controller层,FurnController.java 处理现实家居的请求
@RequestMapping("/furns")
@ResponseBody
public Msg listFurns() {
List<Furn> furnList = furnService.findAll();
//将数据封装到 Meg对象中返回
Msg msg = Msg.success();
msg.add("furnList", furnList);
return msg;
}
(3)postman测试成功
![image-20230308210723639](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230308210723639.png)
![image-20230308210703467](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230308210703467.png)
4.3.2前端代码
(1)修改src/utils/request.js,增加response拦截器,统一处理响应后的结果
//response拦截器,可以在调用接口响应后,统一的处理返回结果
request.interceptors.response.use(
response => {
let res = response.data;
//如果返回的是文件
if (response.config.responseType === 'blob') {
return res;
}
//如果返回的是string,就转成json对象
if (typeof res === 'string') {
//如果不为空,就转成json对象
res = res ? JSON.parse(res) : res;
}
return res;
}, error => {
//如果失败
console.log("err=", error);
return Promise.reject(error);
})
(2)修改HomeView.vue,当现实页面之前就发出请求,然后将接收的数据显示到页面上
<template>
<div>
...
<!--表格显示家居新-->
<el-table :data="tableData" stripe style="width: 90%">
<el-table-column prop="id" label="ID" sortable></el-table-column>
<el-table-column prop="name" label="家居名"></el-table-column>
...
...
<el-table-column fixed="right" label="操作" width="100">
<template #default="scope">
<el-button link type="primary" size="small"
@click="handleEdit(scope.row)">编辑
</el-button>
<el-button link type="primary" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
...
</div>
</template>
<script>
import request from '@/utils/request.js'
//导出组件
export default {
name: 'HomeView',
components: {},
data() {
return {
search: '',
dialogVisible: false,
form: {},//定义一个空表单
tableData: []
}
},
created() {//生命周期函数
this.list();
},
methods: {
add() {//显示添加对话框
...
},
save() {//将填写的表单数据发送给后端
request.post("/api/save", this.form).then(res => {
console.log("res-", res)
this.dialogVisible = false;
//调用list方法,刷新页面显示的数据
this.list();
})
},
list() {//list方法,请求返回家居信息,当我们打开页面的时候,该方法就应该自动触发
request.get("/api/furns").then(res => {//res已经经过拦截器的处理>res=res.data
//根据res的结构来获取数据
this.tableData = res.extend.furnList;
})
}
}
}
</script>
![image-20230308214201082](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20230308214201082.png)