SpringBoot+vue旅游项目总结

Springboot+vue旅游项目小总结

此项目为一个springboot+vue入门级小项目,视频地址为:https://www.bilibili.com/video/BV1Nt4y127Jh

业务简单,对提升业务能力没什么大的帮助,更多的是可以熟悉开发流程和编码。

1.表结构

仅仅三张表,分别为用户表,省份表和景点表,其中省份表和景点表为一对多的关系。

用户表(t_user):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-03qLjhPk-1592974475668)(E:\MarkDown\项目分析篇\2.springboot+vue旅游项目\images\img1.png)]

省份表(t_province):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SYhQhFbS-1592974475676)(E:\MarkDown\项目分析篇\2.springboot+vue旅游项目\images\img2.png)]

景点表(t_place):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZjVolgcK-1592974475678)(E:\MarkDown\项目分析篇\2.springboot+vue旅游项目\images\img4.png)]

2.相关配置

server.port=8989
spring.application.name=travels

spring.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=root

mybatis.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();
        //验证码存入session
        String 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">&emsp;号:</div>
                    <input type="text" v-model="user.username" name="username">
                </label>
                <label>
                    <div class="label-text">&emsp;码:</div>
                    <input type="password" v-model="user.password" name="password">
                </label>
                <label>
                    <div class="label-text">&emsp;箱:</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>&emsp;
                <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;
                }
                //发送axios
                axios.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.省份模块

最终效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GdXCalJU-1592974475681)(E:\MarkDown\项目分析篇\2.springboot+vue旅游项目\images\img7.png)]

分页展示模块:

分页查询后端代码:

 @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集合返回给前端,其中需要的数据有:

  1. page,当前页是第几页
  2. rows:每页显示多少条记录。
  3. totals:总共多少条记录
  4. totalPage:总共多少页
  5. province为返回的省份list集合。

分页查询Service层代码:

@Override
    public List<Province> findByPage(Integer page, Integer rows) {
        //比如查询第1页,那么就查询的是limit 0,rows
        int 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,placecounts
        from t_province
        order by placecounts
        limit #{start},#{rows}
  </select>

前端实现:

html:

<div id="pages">
<!-- 当page -->
<a href="javascript:;" @click="findAll(page-1)" v-if="page>1" class="page">&lt;上一页</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">下一页&gt;</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>

注:此文仅仅为个人的一些编码总结。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SpringBoot+Vue2项目是一种前后端分离的开发模式,其中SpringBoot用于开发后端接口,Vue2用于开发前端页面。在这个项目中,可以使用SpringBootApplication注解来标识启动类,并通过@RestController注解来标识控制器类。\[1\] 在配置数据库时,可以在application.properties文件中添加相关配置,包括数据库驱动、URL、用户名和密码等信息。\[2\] 如果需要解决前后端跨域问题,可以在后端设置跨域配置,并将前端请求的baseURL属性值改为后台地址。这样就可以实现前后端的数据交互。\[3\] 总的来说,SpringBoot+Vue2项目是一种灵活、高效的开发模式,可以实现前后端的分离开发,并通过跨域配置实现数据的交互。 #### 引用[.reference_title] - *1* *2* [SDU项目实训——后台搭建——SpringBoot+Vue学习(二)](https://blog.csdn.net/m0_55633961/article/details/123504324)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [SpringBoot+Vue2项目解决前后端跨域方案](https://blog.csdn.net/zl5186888/article/details/126865950)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值