Vue项目-手机app瑞幸咖啡详解(全网最细) 从脚手架搭建到前后端数据交互(一)


说实话,在写这篇博客之前,我想了很久要不要写,写的话无疑是一项工作量巨大的任务,但是扪心自问,这也是一次总结提升的机会,特别对于像我这样新入门的菜,,不新手来说。

好了,不说废话了,以下将开始总结手机应用瑞幸咖啡项目的实现(只实现了部分页面及功能),路过的大佬可以滑走了!

一、项目介绍

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.tel
	     if (!/^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中的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的安装及使用可以参考我的博客

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('发布失败!');
          }
      })
    }
  }

添加成功的评论就会在详情页显示啦!

============================================
今天的内容就先到这儿吧,写了快一天了,后面的内容我会补上的!点个关注,期待下后面的精彩内容吧!

后续篇戳我阅读!

  • 43
    点赞
  • 254
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值