SpringBoot+vue旅游项目总结
Springboot+vue旅游项目小总结
此项目为一个springboot+vue入门级小项目,视频地址为:https://www.bilibili.com/video/BV1Nt4y127Jh
业务简单,对提升业务能力没什么大的帮助,更多的是可以熟悉开发流程和编码。
1.表结构
仅仅三张表,分别为用户表,省份表和景点表,其中省份表和景点表为一对多的关系。
用户表(t_user):
省份表(t_province):
景点表(t_place):
2.相关配置
server.port=8989
spring.application.name=travelsspring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/travels?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=rootmybatis.mapper-locations=classpath:org/hz/travels/mapper/*.xml
mybatis.type-aliases-package=org.hz.travels.entity#图片存储到本地位置
spring.resources.static-locations=file:${upload.dir}
upload.dir=E://images
3.用户模块
注册模块–验证码
原始验证码的实现较为简单,通过验证码工具类生成一个验证码,通过 session.setAttribute(“code”, randomCode.toString());将code设置到session域中,当用户注册的时候,输入的验证码与session中的验证码进行比较即可。
**但是此项目由于使用的是前后端分离项目,前端采用vue+axios方式,axios底层封装了Ajax请求,但是Ajax请求是XMLHTTPRequest
对象发起的而不是浏览器,导致每次请求都会生成一个seesion会话,这样当点击注册按钮之后,对生成一个新的会话,而这个新的会话中不存在“code”,所以在比对用户提交的验证码的时候就会出现异常。**原生Ajax的解决方法:提供了xhrFields: {withCredentials: true} 属性用于携带认证信息,即携带上一次请求生成的Cookie去发出请求,保证了Session的唯一性。
但是在axios中暂时知道怎么使用,所以此处先临时将验证码设置到了ServletContext对象中。
后端代码如下:
/*获取验证码,通过map将验证码的base64编码响应给客户端
*/
@GetMapping("getImage")public Map<String, String> getImage(HttpServletRequest request) throws IOException {Map<String, String> result = new HashMap<>();CreateImageCode createImageCode = new CreateImageCode();//获取验证码String securityCode = createImageCode.getCode();//验证码存入sessionString key = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());request.getServletContext().setAttribute(key, securityCode);//生成图片BufferedImage image = createImageCode.getBuffImg();//进行base64编码ByteArrayOutputStream bos = new ByteArrayOutputStream();ImageIO.write(image, "png", bos);//进行Base64的一个编码String string = Base64Utils.encodeToString(bos.toByteArray());result.put("key", key);result.put("image", string);return result;}
前端渲染:
动态绑定src
<img :src="src" id="img-vcode" @click="getImage" :key="key">
const app = new Vue({el: "#app",data:{user:{},code:"",src:"",key:"",},methods:{getImage(){ //获取验证码_this = this;axios.get("http://localhost:8989/user/getImage").then((res)=>{console.log(res.data.key);//绑定图片的src属性。_this.src = "data:image/png;base64,"+res.data.image;_this.key = res.data.key;});}},//生命周期函数,实例生成以后执行created(){this.getImage();//获取验证码}});
_this.src = “data:image/png;base64,”+res.data.image; //解码的时候必须指定前缀
_this.key = res.data.key; //这个key是后台设置的ServletContext
created(){
this.getImage(); //created()中方法在vue实例创建之前调用,即访问界面就加载验证码
}
注册模块–保存用户
<form action="province/provincelist.html" method="post"><label><div class="label-text">账 号:</div><input type="text" v-model="user.username" name="username"></label><label><div class="label-text">密 码:</div><input type="password" v-model="user.password" name="password"></label><label><div class="label-text">邮 箱:</div><input type="text" v-model="user.email" name="email"></label><img :src="src" id="img-vcode" @click="getImage" :key="key"><label><div class="label-text">验证码:</div><input type="text" v-model="code" name="vcode" style="width: 100px"></label><button type="button" @click="saveUserInfo">提 交</button> <a href="login.html">去登录</a></form>
使用v-model和this.user属性进行绑定,图片的绑定key属性
javacript代码:
const app = new Vue({el: "#app",data:{user:{}, //关联的用户对象code:"", //用户输入的验证码src:"", //绑定的src属性key:"", //后台返回的时间戳key,前台重新提交,根据key判断ServletContext是否存在},methods:{saveUserInfo(){ //注册console.log(this.user.username + this.user.password + this.user.email);console.log(this.code);if(!this.user.username){alert('用户名不能为空!!!!');return;}if(!this.user.password){alert('密码不能为空!!!!');return;}//发送axiosaxios.post("http://localhost:8989/user/register?code="+this.code+"&key="+this.key,this.user).then((res)=>{console.log(res);if(res.data.state){alert(res.data.msg+",点击确定跳转到登录页面!!!");location.href='./login.html';}else{alert(res.data.msg);}});}},//生命周期函数,实例生成以后执行created(){this.getImage();//获取验证码}});
后端注册代码:
@PostMapping("register")public Result register(String code, String key, @RequestBody User user, HttpServletRequest request) {Result result = new Result();//获取ServletContex中存储的验证码String keyCode = (String) request.getServletContext().getAttribute(key);//验证try {if (code.equalsIgnoreCase(keyCode)) {//注册用户userService.register(user);result.setState(true);result.setMsg("注册成功!!!");} else {throw new RuntimeException("验证码错误!!!");}} catch (Exception e) {e.printStackTrace();//捕获异常信息,并将其设置到响应信息中result.setMsg(e.getMessage()).setState(false);}return result;}
4.省份模块
最终效果:
分页展示模块:
分页查询后端代码:
@RequestMapping("findByPage")public Map<String ,Object> findByPage(Integer page,Integer rows){page = page == null ? 1 : page;rows = rows == null ? 4 : rows;HashMap<String, Object> map = new HashMap<>();//分页处理List<Province> provinces = provinceService.findByPage(page, rows);//计算总页数Integer totals = provinceService.findTotals();Integer totalPage = totals % rows == 0 ? totals / rows : totals / rows + 1;map.put("provinces", provinces);map.put("totals", totals);map.put("totalPage", totalPage);map.put("page", page);return map;}
使用map集合返回给前端,其中需要的数据有:
- page,当前页是第几页
- rows:每页显示多少条记录。
- totals:总共多少条记录
- totalPage:总共多少页
- province为返回的省份list集合。
分页查询Service层代码:
@Overridepublic List<Province> findByPage(Integer page, Integer rows) {//比如查询第1页,那么就查询的是limit 0,rowsint start = (page-1)*rows;return provinceMapper.findByPage(start,rows);}
UserMapping.xml代码:
<resultMap id="BaseResultMap" type="org.hz.travels.entity.Province" ><id column="id" property="id" jdbcType="INTEGER" /><result column="name" property="name" jdbcType="VARCHAR" /><result column="tags" property="tags" jdbcType="VARCHAR" /><result column="placecounts" property="placecounts" jdbcType="INTEGER" /></resultMap><select id="findByPage" resultType="Province">select id,name,tags,placecountsfrom t_provinceorder by placecountslimit #{start},#{rows}</select>
前端实现:
html:
<div id="pages">
<!-- 当page -->
<a href="javascript:;" @click="findAll(page-1)" v-if="page>1" class="page"><上一页</a>
<a class="page" href="javascript:;" v-for="indexpage in totalPage" @click="findAll(indexpage)" v-text="indexpage"></a>
<a href="javascript:;" v-if="page<totalPage" @click="findAll(page+1)" class="page">下一页></a>
</div>
script代码:
const app = new Vue({el: "#app",data: {provinces: [],page: 1, //第一次加载列表默认加载第一页数据rows: 2, //指定每页加载两条数据totalPage: 0, //总共多少页totals: 0, //共多少条数据},methods: {//用户点击第几页,就传这个页码的值去做请求。findAll(indexpage) { //查询所有if (indexpage) {this.page = indexpage;}_this = this;axios.get("http://localhost:8989/province/findByPage?page=" + this.page + "&rows=" + this.rows).then((res) => {_this.provinces = res.data.provinces;_this.page = res.data.page;_this.totalPage = res.data.totalPage;_this.totals = res.data.totals;});}},//vue实例化前加载列表created() {this.findAll();}})
修改省份信息:
业务代码比较简单,需要注意的只是前端的一点:
<a :href="'./updateprovince.html?id='+province.id">修改省份</a>
在省份列表界面点击修改,跳转到修改界面,传过去一个省份id。
在修改界面拿到这个id,进行修改操作,修改之前先要把该省份信息加载到修改界面:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CDfKsn0J-1592974475684)(E:\MarkDown\项目分析篇\2.springboot+vue旅游项目\images\img8.png)]
new Vue({el: "#app",data: {id: "",province: {}},methods: {findOneProvince(id) {_this = this;axios.get("http://localhost:8989/province/findOne?id=" + id).then((res) => {console.log(res.data);_this.province = res.data;});},updateProvince() {axios.post("http://localhost:8989/province/updateProvince", this.province).then((res) => {if (res.data.state) {alert(res.data.msg + ",点击确定回到主页!!!");location.href = './provincelist.html';} else{alert(res.data.msg);}});}},created() {this.id = location.href.substring(location.href.indexOf("=") + 1);this.findOneProvince(this.id);}})
5.景点模块
添加景点:
添加模块主要牵扯到文件上传的问题,另外需要更新省份的placeCount字段
后端代码如下:
//将图片保存到本地位置
@Value("${upload.dir}")private String realPath;
@PostMapping("save")public Result save(MultipartFile pic,Place place) throws IOException {Result result=new Result();try {//对图片文件进行Base64的转化String picpath = Base64Utils.encodeToString(pic.getBytes());place.setPicpath(picpath);//处理文件上传String extension = FilenameUtils.getExtension(pic.getOriginalFilename());String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + extension;//将这个文件写入到本地磁盘的具体位置pic.transferTo(new File(realPath,newFileName));Integer id = place.getProvince().getId();Province oneProvince = provinceService.findOneProvince(id);oneProvince.setPlacecounts(oneProvince.getPlacecounts()+1);//更新省份信息provinceService.updateProvince(oneProvince);placeService.save(place);result.setMsg("保存景点信息成功");}catch (Exception e){result.setMsg(e.getMessage());}return result;}
前端代码:
使用vue中的ref注册组件信息:
<input type="file" ref="myFile" id="imgfile" style="display: none" onchange="imgfileChange()">
由于使用了文件,所以提交的时候需要使用FormData的方式提交:script如下:
savePlaceInfo() { //保存景点的方法console.log(this.place);//拿到注册的组件let myFile = this.$refs.myFile;let files = myFile.files;let file = files[0];let formData = new FormData();formData.append("pic", file);formData.append("name", this.place.name);formData.append("hottime", this.place.hottime);formData.append("hotticket", this.place.hotticket);formData.append("dimticket", this.place.dimticket);formData.append("placedes", this.place.placedes);formData.append("province.id", 1);axios({method: 'post',url: 'http://localhost:8989/place/save',data: formData,headers: {'Content-Type': 'multipart/form-data'}}).then((res) => {console.log(res.data);if (res.data.state) {alert(res.data.msg + ",点击确定回到景点列表");location.href = "./viewspotlist.html?id=" + this.place.provinceid;} else {alert(res.data.msg + ",点击确定回到景点列表");}});
}
formData.append(“province.id”, 1); 外键关联属性绑定的时候使用这个
在添加的时候需要绑定省份id,绑定方法如下:
<select v-model="place.province.id"><option v-for="(pro,index) in provinces" :value="pro.id" v-text="pro.name"></option></select>
展示列表模块:
分页和省份列表一样,不同的是需要渲染一个base64的图片信息。
渲染的时候必须在属性前加上如下代码:
<td><img :src="'data:image/png;base64,'+place.picpath" class="viewspotimg"></td>
注:此文仅仅为个人的一些编码总结。
SpringBoot+vue旅游项目总结相关推荐
- Springboot vue旅游资讯网站管理系统
旅游信息管理系统是对旅游信息资源进行管理的系统.随着旅游信息在种类和数量上的增多,以及涉及的方面相对较广,旅游信息的管理难度也在增大.伴随计算机的普及,人们也愿意并习惯通过计算机来获取信息资源,人们可 ...
- SpringBoot + Vue + nginx项目一起部署
SpringBoot + Vue + nginx项目一起部署 SpringBoot + Vue 一起部署到 nginx 1.后端项目部署: (1) Java项目打包上传到 服务器,开启服务 java ...
- springboot+vue计算机旅游管理系统 springboot+vue旅游网
springboot+vue计算机旅游管理系统 springboot+vue旅游网 包含前台用户系统和后台管理系统 源码: vx:daihq713
- 基于springboot+vue物流项目
基于springboot+vue物流项目 ✌全网粉丝20W+,csdn特邀作者.博客专家.CSDN新星计划导师.java领域优质创作者,博客之星.掘金/华为云/阿里云/InfoQ等平台优质作者.专注于 ...
- 踩坑之旅:springboot+vue+webpack项目实战(一)
2019独角兽企业重金招聘Python工程师标准>>> 网上关于springboot的小项目很多,node.js+vue的项目也很多,但是好像没有两者合一的项目,最近在想实践下将两者 ...
- 开发一个springboot+vue的项目【增加铃声制作的功能】
大家好呀,我是小孟! 参考的地址:https://github.com/Yin-Hongwei 前面小伙伴说要学习spring+vue的项目,我们录制了非常详细的教程,视频播放了突破了30w+,看来是 ...
- springboot + vue + elementUI项目实战——简洁清新的员工管理系统(一)
springboot + vue + elementUI + mybatis + redis 清新的员工管理系统 前言 从这期,项目从需求分析开始,一步步实现一个老经典的清新的员工管理系统,适合有 ...
- B站云E办Springboot+vue——前端项目完整版(含源码)
一.项目简介 项目背景:受疫情的影响,许多企业由线上办公转为线下办公.随着线上办公的人数的增多,线上办公的优点逐步凸显:通过实现工作流程的自动化.节省企业办公费用.实现绿色办公,同时提升办公效率. 项 ...
- Java Springboot+vue毕业项目实战-疫情防疫信息管理系统
主要技术实现:文末获取源码联系 Java.springmvc.VUE.node.js.mybatis.mysql.tomcat.jquery.layui.bootstarp.JavaScript.ht ...
最新文章
- 为什么Scrum模式适合软件开发?
- mysql8.0 服务移除_Linux下彻底删除Mysql 8.0服务的方法
- 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线...
- Day10-Python3基础-协程、异步IO、redis缓存、rabbitMQ队列
- GMIS 2017大会戴文渊演讲:构建AI商业大脑
- Sun过去的世界中的JDK 11和代理
- Java 最常见 200+ 面试题全解析:面试必备
- cemtos7重置mysql root,Centos7重置MySQL8.0 root密码
- 如何在Mac上设置QLab工作区
- 【劲峰论道时空分析技术-学习笔记】4 如何度量时空变化
- IIS_PHP5.3.x zend guard loader 的配置方法
- 最新Java开发毕业论文参考文献干货满满
- 故障模块名称kernelbase.dll_OBDII(OBD2)故障码库P0700P0799
- mysql手册06_触发器
- Bus error 问题原因
- 什么是web前端?前端可以做什么?html5有什么用?
- 【C语言小游戏】计算器
- 基于51单片机的智能红外遥控防雨晾衣架 雨滴光强检测系统proteus仿真原理图PCB
- 又一款开源客服系统开放了
- 最常用的酒店IPTV系统实施方案
热门文章
- mlf机器人无限制格斗_2017FMB无限制机器人格斗比赛视频-2017极战 FMB 无限制机器人格斗大赛视频高清版 - 极光站...
- 基于STM32F103单片机智能风扇 手机蓝牙无线控制系统
- 超人前传 主题曲 Save Me (转载)
- 解析STEAM教育理念下美术作业设计生活化
- Kafka到底有多高可靠?
- FBA发货限制大改革,准备好的货还敢发吗?
- Uni-app 实现md5加密
- 【开发工具----Matlab】Matlab7.0(R14)注册码
- ARM寄存器(R13/R14/R15)
- JS 设置定时器和清除定时器