Vue2积分商城PC端项目(三)

发布于:2022-11-10 ⋅ 阅读:(1182) ⋅ 点赞:(0)

目录

一、手机号码登录逻辑分析

1.1、抽取前两个验证的代码

1.2、发起登录请求

1.3、购物车按钮的布局

1.4、购物车按钮展示(登录状态)分析

二、提示组件的封装

2.1、icon图标的使用

2.1.1、在全局中引入

2.1.2、具体图标名称

2.1.3、在组件中使用

2.2、Toast组件的初步封装与使用

2.3、Toast组件展示

2.4、Toast组件的进场离场效果

2.5、封装Toast的属性

2.6、Toast组件自动关闭的处理

2.7、 封装Toast组件总结

三、微信登录二维码的获取与展示

3.1、获取微信二维码

3.2、微信二维码样式调整

四、微信扫码登录——临时票据code换取token

五、手机验证码登录


一、手机号码登录逻辑分析

手机号码登录逻辑分析:

1、手机号码格式是否正确

2、拼图滑块验证通过

3、验证码是否为空(注意,这里我们前端并没有拿到发送的手机验证码,所以只能判空)

4、发起登录请求

1.1、抽取前两个验证的代码

前两个验证我们前面已经做了,可以直接封装成函数

Login.vue中:

	toVerify(){
            // 1、验证手机号是否正确
            if(!validateTelephoneNumber(this.phoneNum)){
                alert("请输入正确的手机号");
                this.$refs.phone.focus();
                return
            }

            // 2、进行滑块验证
            if (this.msg == "再试一次" || this.msg == "向右滑动") {
                alert("请先进行滑块验证");
                return 
            }
        },
        getCode(){
            this.toVerify();
        	//3、发起请求
            ...
        }

如果只是按照上面进行抽取,则会有验证不通过还发起请求的bug

所以,this.toVerify();应换成:

toVerify(){
            // 1、验证手机号是否正确
            if(!validateTelephoneNumber(this.phoneNum)){
                alert("请输入正确的手机号");
                this.$refs.phone.focus();
                return
            }

            // 2、进行滑块验证
            if (this.msg == "再试一次" || this.msg == "向右滑动") {
                alert("请先进行滑块验证");
                return 
            }
            // 【!!!!!!!!】
            return true
            // 【!!!!!!!!】
        },
        getCode(){
            if(!this.toVerify()){
                return
            };
        	//3、验证成功后,发起请求
            ...
        }

1.2、发起登录请求

api.js中:

// 手机号登录请求
export const PhoneLoginAPI = params => request.post("/phoneRegin",qs.stringify(params));

Login.vue中:

import { SendSMSAPI, PhoneLoginAPI } from "@/request/api";
...
		// 点击登录按钮
    async submitFn() {
      if (!this.verifyFn()) return;
      // 3.手机验证码的验证
      if (!this.SMScode.trim()) {
        //非空判断
        alert("请输入手机验证码!");
        return;
      }
      // 做登录功能
      // alert("验证通过,可以登录了");
      let res = await PhoneLoginAPI({
        verifyCode: this.SMScode,
        phone: this.phoneNum,
      });
      if(!res)return;
      // 登录成功之后的逻辑
      // (1)提示登陆成功
      alert("登陆成功!")
      // (2)关闭登录窗口
      this.changeIsShowLoginModal(false)
      // (3)保存token值
      localStorage.setItem("x-auth-token",res["x-auth-token"])
      // (4)登录状态的切换(成功之后要显示昵称\头像\购物车按钮...)
      console.log(res);
      this.changeIsLogined(true);
    },

1.3、购物车按钮的布局

登陆之后的购物车按钮布局:

TopBar.vue文件中:

  <li class="btn" @click="changeIsShowLoginModal(true)" v-show="!isLogined">
            登录</li>
<li class="btn cart-btn" v-show="isLogined">
    <img src="@/assets/img/cart.png" /><span>购物车</span><b>{{ cartTotal }}</b>
</li>
import { mapMutations, mapState } from "vuex";
export default {
  data() {
    return {
      // 购物车数量
      cartTotal: 0,
    };
  },
//   总结:vuex中state中的值用computed获取;mutations里的方法用methods获取
  computed: {
    ...mapState({
      isLogined: (state) => state.loginStatus.isLogined,
    }),
  },
  methods: {
    ...mapMutations({
      changeIsShowLoginModal: "showModal/changeIsShowLoginModal",
    }),
  },
};
</script>
...
   .btn {
        width: 124px;
        height: 40px;
        text-align: center;
        line-height: 40px;
        background: @base-color;
      }
      .cart-btn {
        b {
          display: inline-block;
          width: 22px;
          height: 22px;
          background: #f0604d;
          border-radius: 11px;
          line-height: 22px;
          text-align: center;
          margin-left: 5px;
        }
      }
    }
...

  总结:

vuex中state中的值用computed获取;mutations以及actions里的方法都用methods获取

1.4、购物车按钮展示(登录状态)分析

因为该购物车按钮需要依靠 有没有登录这个状态的值 来进行展示,并且,这个值会在Login.vue这个文件中进行修改,所以,我们把有没有登录这个状态值放在Vuex中:

store目录中新建文件夹loginStatus,中新建index.js:

左边是store文件夹里的index.js文件,用来引入模块

右边是store文件夹下的loginStatus文件夹下的index.js文件,存储了"isLogined"  [登陆状态]

最后在登录的回调函数中进行修改这个值。即在Login.vue中:

	...mapMutations({
            chanIsShowLoginModal:"showModal/chanIsShowLoginModal",
            chanIsLogined:"loginStatus/chanIsLogined"
        }),
	...
 (4)登录状态的切换(成功之后要显示昵称\头像\购物车按钮...)
      this.changeIsLogined(true);

二、提示组件的封装

2.1、icon图标的使用

2.1.1、在全局中引入

效果图中icon的链接:

https://at.alicdn.com/t/font_2730880_ylrio3ahhx.css

2.1.2、具体图标名称

图标名称 图标类名
YDUI-复选框(选中) icon-yduifuxuankuangxuanzhong
YDUI-复选框 icon-yduifuxuankuang
loading icon-loading
toast-失败_画板 1 icon-toast-shibai_huaban
toast-警告 icon-toast-jinggao
toast _成功 icon-toast_chenggong

2.1.3、在组件中使用

<i class="iconfont icon-loading"></i>

2.2、Toast组件的初步封装与使用

https://at.alicdn.com/t/font_2730880_ylrio3ahhx.css

iconfont的样式链接内容粘贴到src/assets/css/public.less中,

components目录下新建Toast.vue

<template>
    <div class="toast">
        <i class="iconfont icon-toast-shibai_huaban"></i>
        <span>提示内容</span>
    </div>
</template>
<style lang = "less" scoped>
.toast{
  position: fixed;
  padding: 10px 20px;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #fff;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  border-radius: 10px;

  .iconfont{
    margin-right: 10px;
  }
  .icon-toast-shibai_huaban{
    color: red;
  }
  .icon-toast_chenggong{
    color: green;
  }
  .icon-toast-jinggao{
    color: orange;
  }
}
</style>

App.vue中引入注册使用即可 。

2.3、Toast组件展示

Toast组件展示与否最终可以在各个组件中调用,所以放在Vuex中。

然后在App中:

<Toast v-show="isShowToast"></Toast>
<script>
...
import {mapState} from "vuex"
export default {
   ...
    computed:{
        ...mapState({
            isShowToast:state=>state.showToast.isShowToast
        })
    }
}
</script>

在TopBar组件中,先找头像尝试点击触发展示:

<img @click="showToastFn" src="../assets/img/userImg.f8bbec5e.png" width="26" alt="">

<script>
    methods:{
        ...mapMutations({
            chanIsShowLoginModal:"showModal/chanIsShowLoginModal",
            chanIsShowToast:"showToast/chanIsShowToast"
        }),
        showToastFn(){
            this.chanIsShowToast(true)
        }
    }
</script>

2.4、Toast组件的进场离场效果

Vue提供了transition组件,配合css3可以用来做进场离场效果:进入/离开 & 列表过渡 — Vue.js

App.vue中添加:

<template>
  	<div id="app">
        <transition name="slide">
            <Toast v-show="isShowToast"></Toast>
        </transition>
        ...
   </div>
</template>
<style lang="less">
/* 入场的起始状态 = 离场的结束状态 */
.slide-enter, .slide-leave-to{
  opacity: 0;
}
.slide-enter-active, .slide-leave-active{
  transition: opacity .3s linear;
}
.slide-enter-to, .slide-leave{
  opacity: 1;
}
</style>

在TobBar.vue组件中:

methods:{
    ...mapMutations({
        chanIsShowLoginModal:"showModal/chanIsShowLoginModal",
        chanIsShowToast:"showToast/chanIsShowToast"
    }),
    showToastFn(){
        this.chanIsShowToast(true);
        setTimeout(()=>{
            this.chanIsShowToast(false);
        },1500)
    }
}

2.5、封装Toast的属性

一个完整的Toast组件最好需要有展示、颜色、类型三种属性:

在vuex中,补充:

export default{
    namespaced:true,
    state: {
        // 表示是否展示提示
        isShowToast:false, 
        // toast的内容
        toastMsg: "默认内容",
        // toast的类型(success, danger, info)
        toastType: "success"
    },
    mutations: {
        chanIsShowToast(state,payload){
            console.log(payload);
            state.isShowToast = payload.isShow;
            if(payload.isShow){
                state.toastMsg = payload.msg;
                state.toastType = payload.type;
            }
            
        }
    },
    actions: {
        
    },
}

Toast.vue组件:

<template>
    <div class="toast">
        <!-- <i class="iconfont icon-toast-shibai_huaban"></i>
        <span>提示内容</span> -->
        <i 
      :class="toastType=='success' ? 'iconfont icon-toast_chenggong' : (toastType=='danger' ? 'iconfont icon-toast-shibai_huaban' : 'iconfont icon-toast-jinggao')"
    ></i>
    <span>{{toastMsg}}</span>
    </div>
</template>
<script>
import {mapState} from "vuex"
export default {
    data () {
        return {
        }
    },
    computed:{
        ...mapState({
            toastMsg:state=>state.showToast.toastMsg,
            toastType:state=>state.showToast.toastType,
        })
    }
}
</script>

最后在TopBar.vue组件中,调用的时候传入对象:

		showToastFn(){
            this.chanIsShowToast({
                isShow:true,
                msg:"请先登录",
                type:"danger"
            });
            setTimeout(()=>{
                this.chanIsShowToast({
                    isShow:false,
                });
            },1500)
        }

2.6、Toast组件自动关闭的处理

Toast组件应该具备自动关闭的功能,而不是每次调用都要写一段定时器代码来关闭。

即TopBar.vue组件中去掉setTimeout方法,在TopBar.vue中:

	import {mapMutations,mapState,mapActions} from "vuex"
	methods:{
        ...mapMutations({
            chanIsShowLoginModal:"showModal/chanIsShowLoginModal"
        }),
        ...mapActions({
            asyncIsShowToast:"showToast/asyncIsShowToast"
        }),
        showToastFn(){
            this.asyncIsShowToast({
                isShow:true,
                msg:"请先登录",
                type:"danger"
            });
            /* setTimeout(()=>{
                this.chanIsShowToast({
                    isShow:false,
                });
            },1500) */
        }
    }

在Vuex中书写actions:

actions: {
        asyncIsShowToast(context,payload){
            context.commit("chanIsShowToast",payload)
            setTimeout(()=>{
                context.commit("chanIsShowToast",{
                    isShow:false
                })
            },1000)

        }
    },

2.7、 封装Toast组件总结

1.结构样式

2.显示和隐藏的变量存在vuex中

3.过渡效果(进场和离场)

4.msg和type的处理(toast文字和图标的处理)

5.定时器,隐藏toast组件

6.定时器优化--抽取定时器到actions方法中,让调用更加简便。

以后在任意组件中想要使用提示框:

	import {mapActions} from "vuex"
	methods:{
        ...mapActions({
            asyncChanToastState:"showToast/asyncChanToastState"
        }),
        showToastFn(){
            this.asyncChanToastState({
                msg:"请先登录",
                type:"danger"
            });
        }
    }

三、微信登录二维码的获取与展示

3.1、获取微信二维码

在 public/ndex.html 的head标签中引入:

<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>

把Login.vue文件中展示二维码图片的盒子上添加 id=“weixin” ,将来获取的微信二维码会以iframe的方式嵌入到这个盒子中:

<div id="weixin" class="qrcode" v-show="!isShowForm">
     二维码
</div>

在api.js中:

// 微信登录(这个接口必须用qs对数据进行格式化)
export const WeixinLoginApi = (params) => request.post(`/wechatUsers/PCLogin`, qs.stringify(params));

在点击切换到微信登录的函数中:

    weixinClick() {
      // 点击切换微信扫码登录这一项,并向微信扫码登录
      this.isShowForm = false;
      // 申请微信登录二维码
      let _this = this;
      new WxLogin({
        id: "weixin",
        appid: "wx67cfaf9e3ad31a0d", // 这个appid要填死
        scope: "snsapi_login",
        // 扫码成功后重定向的接口
        redirect_uri: "https://sc.wolfcode.cn/cms/wechatUsers/shop/PC",
        // state填写编码后的url
        state: encodeURIComponent(
          window.btoa("http://127.0.0.1:8080" + _this.$route.path)
        ),
        // 调用样式文件
        href: "data:text/css;base64,Lyogd3hsb2dpbi5jc3MgKi8NCi5pbXBvd2VyQm94IC50aXRsZSwgLmltcG93ZXJCb3ggLmluZm97DQogIGRpc3BsYXk6IG5vbmU7DQp9DQoNCi5pbXBvd2VyQm94IC5xcmNvZGV7DQogIG1hcmdpbi10b3A6IDIwcHg7DQp9",
      });
    },

3.2、微信二维码样式调整

把今天其他文件夹中的wxLoginStyle文件夹放到utils文件夹中,然后在这个wxLoginStyle目录下用node执行js文件:

node data-url.js

得到:

data:text/css;base64,Lyogd3hsb2dpbi5jc3MgKi8NCi5pbXBvd2VyQm94IC50aXRsZSwgLmltcG93ZXJCb3ggLmluZm97DQogIGRpc3BsYXk6IG5vbmU7DQp9DQoNCi5pbXBvd2VyQm94IC5xcmNvZGV7DQogIG1hcmdpbi10b3A6IDIwcHg7DQp9

把它填到上面的href属性,隐藏头部尾部。

最后调整页面iframe外层盒子的样式,使二维码居中

#weixin{
    /* background-color: #fcf; */
    display: flex;
    justify-content: center;
    margin-top: -20px;
}

如果运行报错,在当前页面引入:

<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js" type="text/javascript"></script>

完成至此已经可以进行扫码登录了。

如果扫码跳转的时候被谷歌浏览器被拦截,需要修改谷歌浏览器设置。

打开谷歌浏览器设置,搜索 “弹出式窗口和重定向”,

 微信登陆流程:

 

四、微信扫码登录——临时票据code换取token

我们需要在顶部组件TopBar.vue,加载的时候,用临时票据code换取token

import {WeixinLoginApi} from "@/request/api"
...
created(){
    // 微信登录第二步:临时票据code换取token
    let mycode = this.$route.query.code;
    if (mycode) {  
        // 有code才去换取token
        WeixinLoginApi({
          code: mycode,
        }).then((res) => {
          console.log(res);
          if (res.code === 0) {
            // 登录成功
            // 1、提示用户登录成功
            this.asyncChanToastState({
                msg:"登录成功",
                type:"success"
            });
            // 2、保存token值
            localStorage.setItem("x-auth-token", res["x-auth-token"]);
            // 3、改变登录状态
            this.chanIsLogined(true);
          }
        })
    }
}

登陆成功,清除地址栏上显示的code,在成功的回调函数中:

// 4、清除浏览器地址栏上的code
this.$router.push(this.$route.path);
/*
setTimeout(()=>{
	this.$router.go(0)
},2000)
*/

当返回的res.code不为0,表示登录失败

if (res.code === 0) {
	...
}else{
    // 登陆失败给出提示信息
    this.asyncChanToastState({
        msg:res.message,
        type:"danger"
    });
}

拿不到code的时候,也更新一下用户登录状态:

	if(mycode){
    	....   
    }	
	else{
      // 没有mycode的时候也设置用户状态
      // 没有code说明,用户没扫码
      // 说明用户已经登录的不用扫码,或者用户没有登录没扫码,
      // 判断用户能否拿到token来做出登录状态的更新
      let mytoken = localStorage.getItem("x-auth-token")
      this.chanIsLogined(Boolean(mytoken));
    }

五、手机验证码登录

在Login.vue中,修改两个参数,并且补上res.code==0(但是每天一个手机号有限定发送短信验证码的次数):

	// 去做登录
      LoginAPI({
        phone: this.phoneNumber,
        password: this.SMScode,
      }).then((res) => {
        console.log(res);
        if(res.code==0){   
            alert("登录成功");
            localStorage.setItem("x-auth-token", res["x-auth-token"]);
            this.chanIsShowLoginMadal(false);
            this.chanIsLogined(true);
        }else{
          alert(res.message);
        }
      });

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到