好了,不说废话了,以下将开始总结手机应用瑞幸咖啡项目的实现(只实现了部分页面及功能),路过的大佬可以滑走了!
本次项目使用vue框架实现,采用前后端配合的方式(即前端实现页面及数据渲染),后端提供接口(数据处理)。
瑞幸咖啡APP是一款咖啡类的购物APP,商品以咖啡类为主,业务流程从APP的登录到加入购物车,到支付成功。
由于水平有限,仅实现了部分页面和功能,由于是多人协作,实现的效果各有差异。
以下是部分静态页面效果
首页
菜单页
商品详情页
我的页面
订单页
此外还有购物车页面,地址页面,新增地址页面,个人资料页面,确认订单页面,取消订单页面等等,页面太多,就不一一列举了。
作为一个完整的项目,必须得有前后端交互功能,如果还有新入坑的不懂前后端交互,我可以简单介绍下。
前端:静态页面,通过接口向后端发出请求,拿到动态数据渲染在页面,一句话总结,前端做的就是用户看得到的部分;
后端:接收前端发来的请求,从数据库中查询数据,并返回给前端,也就是说,后端做的是用户看不到的部分。
在本次项目中,共实现了以下动态效果(我负责的部分)
首页通过商品类型获取对应类型的商品
首页点击图片获取该商品的详细信息
详情页获取该商品的所有评论信息
详情页将该商品加入购物车
购物车页面点击结算创建订单
订单页点击商品添加评论信息
首先,通过搭建脚手架
创建项目开发环境;
在项目开发中,使用了mock数据
,在没有后端接口的情况下模拟实现渲染;
使用了axios
向后端发送请求,还是用了拦截器
,这是在发送请求时携带token
信息的;’
在进行页面跳转时,由于我们做的是SPA(单页面应用),需要用路由
跳转,动态路由传参
等‘;
以及路由守卫
,以此来判断用户是否登录(能否显示页面),懒加载
技术可以提高页面加载性能;
在APP里经常会有一些表单验证或者遮罩层的结构,我们在项目中引入了第三方组件(Vant等)
;
在进行组件通信时,还用到了vueX
来保存全局数据,使用起来非常方便;
在发起请求时,由于前后端分离,势必造成跨域的问题,我们使用反向代理
来解决跨域;
到最后,到项目整体的打包
。
以上相关技术基本都属于vue系列技术,(可以参考我之前的博客, vue入门系列)我们在项目中或多或少都使用到。
第一次做vue项目,有很多瑕疵,以后有机会会完善一下,进来的大佬也可以在评论区指导指导,本人绝对虚心受教,刚入坑的小伙伴也可以交流交流!
登录这一部分不是我写的,但是基本实现还是知道的。由于原APP是没有注册页面,(好像大部分APP都是直接登录),所以只实现了登录的功能。
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.tel
if (!/^1[3456789]\d{9}$/.test(phone)) {
Toast.fail('手机号有误!');
return false;
}else{
Toast.success('手机号可用!');
}
},
},
};
验证手机号格式有误:
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信息) 无法访问。
首页这部分是我写的,所以我会说的更详细一点。
首先我们要知道,首页是可以直接访问,也就是不受token的影响,并且在首次登录成功后,也会跳转到首页。
首页的静态页面前面已经看过了,动态效果只有一个:点击商品类型获取对应的商品(轮播图就不说了吧,swiper组件实现的,有兴趣可以研究研究,下面我放上源码仅供参考)
<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>
我们可以看到,首页总共有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中的state
change(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', { //将请求结果传给mutations
data: res.data.data
})
})
},
change2(context, payload) {
// console.log(payload);
axios({
url: "/api/goods/findGoodsByType",
params: {
typeId: payload,
}
})
.then(res => {
context.commit('change', { //将请求结果传给mutations
data: res.data.data
})
})
},
}
})
//导航组件中的代码
export default {
data(){
return {
shoplist:this.$store.state.shopinfo,
}
},
//发送请求,获取数据
created(){
//组件里派发action
this.$store.dispatch('change1')
console.log(this.shoplist);
},
methods:{
tiao(id){
this.$router.push('/info/'+id)
},
toshopcart(){
this.$router.push('/Shop')
}
}
}
点击效果图(不会做gif图…)…
(vueX的安装及使用可以参考我的博客)
这个也是我写的,哈哈!
前面我们说了,点击首页中的商品获取商品的详细信息,通过动态路由传参,将商品的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的商品信息
获取评论也是在详情页,所以共用一个商品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
})
}
}
获取到的部分评论信息:
同样是在详情页,这里我用了一个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("加入失败!");
}
})
如图为加入购物车成功效果图:
菜单页相关功能不是我负责的,所以我会说的没有那么详细,不过还是会附上源码的。
菜单页,也就是各种咖啡或者奶茶下单,查看详情等一系列操作。
菜单页的商品是在页面加载时渲染的,默认是第一种,点击其他类型时显示对应商品,实现思路和首页的一致。
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);
},
},
点击其他分类时效果图:
这一部分是一个瀑布流结构,实现思路比较简单,获取商品数据,按瀑布流的结构渲染。
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;
});
}
}
效果图:(以下图片的排列方式就是瀑布流,交叉的感觉)
和主页点击图片跳转到详情页一样,思路我就不写了,都是把商品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;
});
},
};
不同的是,两个详情页结构有点差别。
地址页是从菜单页跳转过去的,菜单页有个配送方式(自提/外送)细心的同学可能看到了。点击外送时,会跳到地址页,可以进行新增地址等操作。
实现思路:
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),
});
});
});
},
获取地址信息效果图:
既然可以查,当然也可以加,不得不说参与到项目的每位同学都很给力啊。点个赞。
实现思路:
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);
});
添加地址:
订单页就是在订单创建出来后,查询出来的结果。订单的创建是在购物车点击结算创建的(前面提到过)。
实现思路:
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;
})
},
添加评论是在商品完成支付后才能够评论,相信大家都不陌生,没有哪款电商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('发布失败!');
}
})
}
}
添加成功的评论就会在详情页显示啦!
============================================
今天的内容就先到这儿吧,写了快一天了,后面的内容我会补上的!点个关注,期待下后面的精彩内容吧!
后续篇戳我阅读!