Vue项目-手机app瑞幸咖啡详解(全网最细) 从脚手架搭建到前后端数据交互(一)
SPA项目-瑞幸咖啡
- 一、项目介绍
- 1.1、基本情况
- 1.2、实现的功能
- 1.2.1、静态页面实现
- 1.2.2、动态效果实现(接口)
- 1.3、项目使用到的相关技术
- 二、项目的业务流程(实现)
- 2.1、登录
- 2.1.1、登录的流程
- 2.2、首页
- 2.2.1、首页轮播图源码
- 2.2.2、点击商品分类获取商品
- 2.3、详情页(商品详情)
- 2.4、详情页(获取评论)
- 2.5、详情页(加入购物车)
- 2.6、菜单页
- 2.6.1、商品的显示(经典菜单)
- 2.6.2、商品的显示(瑞纳冰季)
- 2.6.3、显示商品详情
- 2.7、地址页
- 2.7.1、显示地址
- 2.7.2、添加地址
- 2.8、订单页
- 2.9、添加评论页
说实话,在写这篇博客之前,我想了很久要不要写,写的话无疑是一项工作量巨大的任务,但是扪心自问,这也是一次总结提升的机会,特别对于像我这样新入门的菜,,不新手来说。
好了,不说废话了,以下将开始总结手机应用瑞幸咖啡项目的实现(只实现了部分页面及功能),路过的大佬可以滑走了!
一、项目介绍
1.1、基本情况
本次项目使用vue框架实现,采用前后端配合的方式(即前端实现页面及数据渲染),后端提供接口(数据处理)。
瑞幸咖啡APP是一款咖啡类的购物APP,商品以咖啡类为主,业务流程从APP的登录到加入购物车,到支付成功。
由于水平有限,仅实现了部分页面和功能,由于是多人协作,实现的效果各有差异。
1.2、实现的功能
1.2.1、静态页面实现
以下是部分静态页面效果
首页

菜单页

商品详情页

我的页面

订单页

此外还有购物车页面,地址页面,新增地址页面,个人资料页面,确认订单页面,取消订单页面等等,页面太多,就不一一列举了。
1.2.2、动态效果实现(接口)
作为一个完整的项目,必须得有前后端交互功能,如果还有新入坑的不懂前后端交互,我可以简单介绍下。
前端:静态页面,通过接口向后端发出请求,拿到动态数据渲染在页面,一句话总结,前端做的就是用户看得到的部分;
后端:接收前端发来的请求,从数据库中查询数据,并返回给前端,也就是说,后端做的是用户看不到的部分。
在本次项目中,共实现了以下动态效果(我负责的部分)
首页通过商品类型获取对应类型的商品
首页点击图片获取该商品的详细信息
详情页获取该商品的所有评论信息
详情页将该商品加入购物车
购物车页面点击结算创建订单
订单页点击商品添加评论信息
1.3、项目使用到的相关技术
首先,通过搭建脚手架创建项目开发环境;
在项目开发中,使用了mock数据,在没有后端接口的情况下模拟实现渲染;
使用了axios向后端发送请求,还是用了拦截器,这是在发送请求时携带token信息的;’
在进行页面跳转时,由于我们做的是SPA(单页面应用),需要用路由跳转,动态路由传参等‘;
以及路由守卫,以此来判断用户是否登录(能否显示页面),懒加载技术可以提高页面加载性能;
在APP里经常会有一些表单验证或者遮罩层的结构,我们在项目中引入了第三方组件(Vant等);
在进行组件通信时,还用到了vueX来保存全局数据,使用起来非常方便;
在发起请求时,由于前后端分离,势必造成跨域的问题,我们使用反向代理来解决跨域;
到最后,到项目整体的打包。
以上相关技术基本都属于vue系列技术,(可以参考我之前的博客, vue入门系列)我们在项目中或多或少都使用到。
二、项目的业务流程(实现)
第一次做vue项目,有很多瑕疵,以后有机会会完善一下,进来的大佬也可以在评论区指导指导,本人绝对虚心受教,刚入坑的小伙伴也可以交流交流!
2.1、登录
登录这一部分不是我写的,但是基本实现还是知道的。由于原APP是没有注册页面,(好像大部分APP都是直接登录),所以只实现了登录的功能。
2.1.1、登录的流程
1,用户输入手机号,前端进行手机号格式验证;
2,若格式合法,用户点击获取验证码;
3,前端发请求,后端返回验证码;
4,用户输入验证码,并点击登录;
5,若验证码正确,后端返回登录成功信息,同时返回token信息;
6,前端接收token信息,并保存在本地(我们保存在cookie里)
- 获取验证码,并验证手机格式
export default {name: "Centent1",data(){return {tel:'',code:''}},methods: {fn() {//获取验证码axios({url: "/api/user/sendtel",params: {tel: this.tel,},// method:'get', 默认是get请求}).then((res) => {if(res.data.code=="1"){// console.log(res.data);console.log('发送成功');this.$store.state.code = res.data.data}else{console.log('发送失败');}})},testphone() {var phone = this.tel;this.$store.state.tel = this.telif (!/^1[3456789]\d{9}$/.test(phone)) {Toast.fail('手机号有误!');return false;}else{Toast.success('手机号可用!');}},},
};
验证手机号格式有误:

- 登录成功,并保存token信息
export default {name: "Centent2",methods: {fn() {console.log(this.$store.state.tel);axios({url: "/api/user/login",params: {tel: this.$store.state.tel,code: this.$store.state.code,},// method:'get', 默认是get请求}).then((res) => {if(res.data.code=='1'){// console.log(res.data.data);let date = new Date();date.setDate(date.getDate()+7)document.cookie = `token=${res.data.data};expires=${date}`;console.log(document.cookie);this.$router.push('/index')}else{console.log('登录失败');}});},},
};
获取到验证码,输入验证码

注意:由于项目中使用了路由守卫,部分页面如商品详情页,订单页在未登录状态时(即本地没有token信息) 无法访问。
- 未登录时浏览器的cookie

- 登录成功后浏览器的cookie

2.2、首页
首页这部分是我写的,所以我会说的更详细一点。
首先我们要知道,首页是可以直接访问,也就是不受token的影响,并且在首次登录成功后,也会跳转到首页。
首页的静态页面前面已经看过了,动态效果只有一个:点击商品类型获取对应的商品(轮播图就不说了吧,swiper组件实现的,有兴趣可以研究研究,下面我放上源码仅供参考)
2.2.1、首页轮播图源码
<template><div class="swiper-container"><div class="swiper-wrapper"><div class="swiper-slide"><img src="../assets/images/11.webp" alt=""></div><div class="swiper-slide"><img src="../assets/images/22.webp" alt=""></div><div class="swiper-slide"><img src="../assets/images/33.jpg" alt=""></div><div class="swiper-slide"><img src="../assets/images/44.webp" alt=""></div><div class="swiper-slide"><img src="../assets/images/55.webp" alt=""></div><div class="swiper-slide"><img src="../assets/images/66.webp" alt=""></div></div><!-- 如果需要分页器 --><div class="swiper-pagination"></div></div>
</template><script>
import Swiper from "swiper";
import "swiper/css/swiper.css";export default {name:"banner",mounted(){ new Swiper ('.swiper-container', { speed:800,autoplay:true,// 如果需要分页器pagination: {el: '.swiper-pagination',},}) }
}
</script><style scoped>.swiper-container{width: 100%;height: 2.42rem;position: relative;}.swiper-container img{width: 100%;height: 100%;}.swiper-pagination{position: absolute;bottom: 0.7rem;}
</style>
2.2.2、点击商品分类获取商品

我们可以看到,首页总共有4个分类,后端同学帮我们在数据库存好了,从左往右商品类型对应typeId为 4,5,6,7
这一块儿的实现方式有很多,如动态组件,组件通信等等,我推荐使用vueX,真的谁用谁说好!
实现思路:
1,首页默认加载第一种分类,那就在加载页面时发送typeId为4
的请求,获取数据并渲染;
2,点击其他分类时,再发送一次请求,获取对应类型的商品;
3,模板渲染时的值都来自vueX中的state,所以不用担心
默认第一种的数据会和点击其它分类时数据重复。(我
就遇到过这个问题,当时用动态组件做的)。
- 具体实现步骤(直接上源码)
//store/index.js(vueX)中的代码
import vueX from 'vuex'
import Vue from 'vue'
import axios from '../utils/request.js'
Vue.use(vueX)export default new vueX.Store({state: {shopinfo: [],tel: '',code: '',isshow: false,orderInfo: ["全部", "立等可取", "预约订单"],outerInfo: ["门店自提", "送货上门", "瑞即购"],isLoading: false,},mutations: {//state:store中的statechange(state, payload) {state.shopinfo = payload.data// console.log(state.shopinfo);},changeload(state, payload) {// console.log(payload);state.isshow = payload},changeIsLoading(state, payload) {state.isLoading = payload.isLoading;}},actions: {//context:store对象change1(context) {axios({url: "/api/goods/findGoodsByType",params: {typeId: 4,}}).then(res => {console.log(res.data.data);context.commit('change', { //将请求结果传给mutationsdata: res.data.data})})},change2(context, payload) {// console.log(payload);axios({url: "/api/goods/findGoodsByType",params: {typeId: payload,}}).then(res => {context.commit('change', { //将请求结果传给mutationsdata: res.data.data})})},}
})
//导航组件中的代码
export default {data(){return {shoplist:this.$store.state.shopinfo,}},//发送请求,获取数据created(){//组件里派发actionthis.$store.dispatch('change1')console.log(this.shoplist);},methods:{tiao(id){this.$router.push('/info/'+id)},toshopcart(){this.$router.push('/Shop')}}
}
点击效果图(不会做gif图…)…

(vueX的安装及使用可以参考我的博客)
2.3、详情页(商品详情)
这个也是我写的,哈哈!
前面我们说了,点击首页中的商品获取商品的详细信息,通过动态路由传参,将商品的id(不是类型id)传到详情页,再发送请求。
实现思路:
1,首页点击商品,将商品id通过路由传参传递到详情页;
2,详情页接收id值,发起请求,获取数据,渲染。
//点击商品,获取商品详情
import axios from '../utils/request.js'export default {data(){return {shopinfo:[]}},created(){ axios({url:'/api/goods/findGoodsByGid',params:{id:this.$route.params.id}}).then(res=>{if(res.data.code=='1'){// console.log('查询成功');this.shopinfo = res.data.data// console.log(this.shopinfo);}else{console.log('查询失败');}})}
}
获取id为15的商品信息

2.4、详情页(获取评论)
获取评论也是在详情页,所以共用一个商品id,获取评论也需要传商品id。(这个应该都能理解吧!)
实现思路:
1,接收传来的商品id值;
2,发请求,获取评论信息。
- 获取商品的评论信息
import axios from 'axios';export default {data(){return {comminfo:[]}},created(){axios({url:"/api/comment/findCommentsByGid",params:{gid:this.$route.params.id}}).then(res=>{// console.log(res.data);this.comminfo = res.data.data})}
}
获取到的部分评论信息:

是不好奇评论信息哪来的,往下看就知道了!
2.5、详情页(加入购物车)
同样是在详情页,这里我用了一个vant组件,结合自己手敲的代码。
先看效果图:(点击页面原有的加入购物车,弹出遮罩层)

遮罩层里的信息和获取商品详情的代码类似,我就不贴出源码了。
加入购物车实现思路:
1,拿到传来的id值;
2,用户修改商品数量,获取相关参数,发请求。
加入购物车源码:
jia() {axios({url:'/api/cart/addCart',method:'post',data:{"gid": 12,"number": 3,"price": 2,"total": 45,"uid": 4}}).then(res=>{if(res.data.code=='1'){console.log(res.data);Toast.success("加入成功!");}else{Toast.fail("加入失败!");}})
如图为加入购物车成功效果图:

此时,数据库中购物车对应的表就有相关数据了。
2.6、菜单页
菜单页相关功能不是我负责的,所以我会说的没有那么详细,不过还是会附上源码的。
菜单页,也就是各种咖啡或者奶茶下单,查看详情等一系列操作。
2.6.1、商品的显示(经典菜单)
菜单页的商品是在页面加载时渲染的,默认是第一种,点击其他类型时显示对应商品,实现思路和首页的一致。
created() {Axios({url: "/api/goods/findGoodsByHot",}).then((res) => {this.goods = res.data.data;});Axios({url: "/api/goods/findGoodsByType",params: {typeId: 2,},}).then((res) => {this.goods1 = res.data.data;});Axios({url: "/api/goods/findGoodsByType",params: {typeId: 3,},}).then((res) => {this.goods2 = res.data.data;});},methods: {ding(index) {this.$router.push("/Detail/" + index);},},
点击其他分类时效果图:

2.6.2、商品的显示(瑞纳冰季)
这一部分是一个瀑布流结构,实现思路比较简单,获取商品数据,按瀑布流的结构渲染。
import Axios from "../untils/request";
export default {
name:"Pu",
data(){return {goods:[],typeId:0,}
},created(){Axios({url: "/api/goods/findGoodsByType",params:{typeId:8}}).then((res) => {this.goods=res.data.data;});
}
}
效果图:(以下图片的排列方式就是瀑布流,交叉的感觉)

2.6.3、显示商品详情
和主页点击图片跳转到详情页一样,思路我就不写了,都是把商品id传过去,然后发请求,获取数据再渲染。
附上源代码:
export default {
name:"Dinfo",data() {return {shopinfo: [],};},created() {Axios({url: "/api/goods/findGoodsByGid",params: {id: this.$route.params.id,},}).then((res) => {this.shopinfo = res.data.data; });},
};
不同的是,两个详情页结构有点差别。

2.7、地址页
地址页是从菜单页跳转过去的,菜单页有个配送方式(自提/外送)细心的同学可能看到了。点击外送时,会跳到地址页,可以进行新增地址等操作。
2.7.1、显示地址
实现思路:
1,拿到用户对应的id值;
2,发请求,获取地址信息;
3,渲染到页面。
created() {Axios({url: "/api/addr/selectAddrByUserId/1",}).then((res) => {res.data.data.forEach((item) => {this.list.push({id: item.id,name: item.userName,tel: item.tel,address: item.detailAddr + item.houseNumber,isDefault:item.defaultAddr == 1? (this.isDefault = true): (this.isDefault = false),});});});},
获取地址信息效果图:

2.7.2、添加地址
既然可以查,当然也可以加,不得不说参与到项目的每位同学都很给力啊。点个赞。
实现思路:
1,添加地址为post请求,参数较多;
2,发请求。
methods: {onSave(con) {Toast.success("保存成功");this.$router.push("./Outering");Axios({url: "/api/addr/addAddr",method: "POST",data: {defaultAddr:1,detailAddr: con.province+con.city+con.county,houseNumber: con.addressDetail,id:con.id,label: "家",sex: 1,tel: con.tel,userId: 1,userName: con.name,},}).then((res) => {console.log("res.data",res.data);});
添加地址:

2.8、订单页
订单页就是在订单创建出来后,查询出来的结果。订单的创建是在购物车点击结算创建的(前面提到过)。
实现思路:
1,拿到用户id;
2,根据用户id查询所有订单信息,再渲染。
- 查询订单
created(){Axios({url:"/api/order/selectOrdersByUid/4"}).then(res=>{this.order=res.data.data;this.desc=res.data.data[0].orderDetailResps;})},

注意:订单只有创建成功后才能够查询。
2.9、添加评论页
添加评论是在商品完成支付后才能够评论,相信大家都不陌生,没有哪款电商APP会让你购买商品之前评论吧。
由于原页面没有添加评论页面(或者说我们没找到),所以我自己简简单单加了个页面(简陋版)

写的不好看(作为前端开发人员…),主要实用就行。
添加评论实现思路:
1,从订单页传来商品id以及用户id,再获取文本框里输入的内容;
2,采用post请求,添加评论。(现在知道为什么会有那么多评论信息了吧!)
methods:{back(){this.$router.back();},addcomm(){// console.log(this.txt);axios({url:'/api/comment/addComment',method:'post',data:{gid:this.$route.params.id,uid:9,comment:this.txt}}).then(res=>{if(res.data.code=='1'){Toast.success('发布成功!');//清空文本框console.log(this.txt);this.txt = ''}else{Toast.fail('发布失败!');}})}}
添加成功的评论就会在详情页显示啦!
============================================
今天的内容就先到这儿吧,写了快一天了,后面的内容我会补上的!点个关注,期待下后面的精彩内容吧!
后续篇戳我阅读!
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
