尚品汇前端项目总结3(P60-P90)

目录

detail模块开发

路由的滚动行为

产品售卖属性值排他思想

放大镜

购买产品个数的操作(正则表达式)

 加入购物车

shopCart组件开发 

游客身份uuid

购物车动态展示数据

修改购物车中商品数量 

删除全部选中的商品以及全选

注册与登录界面

注册业务

 验证码

注册

登录业务

token

登录后header状态的改变

退出登录


 

detail模块开发

路由的滚动行为

使用前端路由,当页面切换新路由时,想要页面滚到顶部,或者保持原先的滚动位置,可以自定义路由切换时页面的滚动行为

let router = new VueRouter({
    routes
    scrollBehavior(to,before,savedPosition) {
        //return 期望滚动到哪个位置
        //滚动到顶部
        return { y: 0 }
    }
})

to和from表示路由对象savedPosition当且仅当popstate(通过浏览器的前进后退按钮触发)导航时才可用

return的信息格式为{x:number,y:number}

产品售卖属性值排他思想

点击的标签加上样式: 要获得被点击的属性值以及属性值列表,当发生点击事件的时候,先将属性列表中所有的属性都清除样式,然后再给被点击的属性加上样式

      changeClass(spuSaleAttrValueList,spuSaleAttrValue){
        for(let i=0;i<spuSaleAttrValueList.length;i++){
          spuSaleAttrValueList[i].isChecked=0;
        }
        spuSaleAttrValue.isChecked=1;
      }

放大镜

放大镜结构如下,下面为轮播图,与之前的轮播图不同的就是再swiper里面设置了一次展示几张【slidesPerView:3】

<div class="spec-preview">
    <!-- 背景图片 -->
    <img :src=" imgObj.imgUrl"  />
    <!-- 触发事件的地方 -->
    <div class="event" @mousemove="small" ref="box"></div>
    <!-- 放大的部分 -->
    <div class="big" ref="big">
      <img :src=" imgObj.imgUrl" ref="img" />
    </div>
    <!-- 覆盖层 -->
    <div class="mask" ref="mask"></div>
  </div>

event.offsetX,event.offsetY:鼠标相对于父盒子的位置

offsetWidth,offsetHeight

style.top,style.left:元素相对于父盒子的定位

逻辑部分:首先要判断mask的位置是否超出边界,如果超出就做相应处理,img的移动部分主要是利用公式

img移动距离/mask移动距离=-img可移动最大距离/mask可移动最大距离

因为img和mask移动方向相反,所以有一个负号 

    small(event){
      let mask=this.$refs.mask;
      let box=this.$refs.box;
      let big=this.$refs.big;
      let img=this.$refs.img;
      let left=event.offsetX-mask.offsetWidth/2;
      let top=event.offsetY-mask.offsetHeight/2;
      if(event.offsetX-mask.offsetWidth/2<0){
        left=0
      } 
      if(event.offsetX-mask.offsetWidth/2>box.offsetWidth-mask.offsetWidth){
        left=box.offsetWidth-mask.offsetWidth
      }
      if(event.offsetY-mask.offsetHeight/2<0){
        top=0
      }
       if(event.offsetY-mask.offsetHeight/2>=box.offsetHeight-mask.offsetHeight){
        top=box.offsetHeight-mask.offsetHeight
      }
      mask.style.top=top+'px';
      mask.style.left=left+'px';
      //最大偏移量的比
      img.style.left=-((img.offsetWidth-big.offsetWidth)/(box.offsetWidth-mask.offsetWidth))*left+'px'
      img.style.top=-((img.offsetHeight-big.offsetHeight)/(box.offsetHeight-mask.offsetHeight))*top+'px'
      
    }

购买产品个数的操作(正则表达式)

加减按钮要注意不能为负数,给输入框绑定change事件 

正则表达式: 

changeSkuNum(){
        //正则,可以输入0-9中任何数,并且每个数出现的次数可以是0次或无数次
        let rg=/^[0-9]*$/;
        this.skuNum=Math.ceil(this.skuNum);
        if(!rg.test(this.skuNum)){
          this.skuNum=1;
        }else{
        //隐式转换成数字
          this.skuNum=this.skuNum*1;
        }
      }

不用正则:

      changeSkuNum(){
         //隐式转换成number,如果是数字以外的类型转换成number就会是NaN
         let value=this.skuNum*1
         //排除负数的情况
         if(isNaN(this.skuNum)||value<1){
           this.skuNum=1
         }
         //不能是小数
         this.skuNum=Math.ceil(this.skuNum);
      }

 加入购物车

 点击加入购物车后,首先要先向购物车发送信息,如果成功返回则将相关数据存储在本地,最后进行路由跳转。

skuInfo不建议用路由传参,因为会显示在地址栏中,会很丑,所以这里用会话存储传参(因为是单页面的应用),简单的数据再用路由传参

浏览器存储功能(html5新增):一般存储的是字符串 可以用JSON.stringify()把对象转换成字符串存储,取的时候用JSON.parse()转回去

  1. 本地存储:不删除会一直存在
  2. 会话存储:会话结束就删除
 postAddToCart(){
       this.$store.dispatch('detail/postAddToCart',{skuId:this.$route.params.skuid,skuNum:this.skuNum}).then(
          response=>{
            sessionStorage.setItem('skuInfo',JSON.stringify(this.skuInfo))
            this.$router.push({name:'addcartsuccess',query:{skuNum:this.skuNum}})

          },error=>{
            alert('error')
          }
        )
      
      }

shopCart组件开发 

游客身份uuid

 uuid:能够生成一个随机的id

单独写一个js文件,放在新建的utils中,写一个生成uuid的函数,返回值为生成的uuid。

先看看本地存储里面是否已经有uuid,如果没有则生成uuid并存储后返回,有就直接返回

import { v4 as uuidv4 } from 'uuid'
export const getUUID = () => {
    let uuid_token = localStorage.getItem('UUIDTOKEN');
    if (!uuid_token) {
        uuid_token = uuidv4()
        localStorage.setItem('UUIDTOKEN', uuid_token);
    }
    return uuid_token
}

在store里面生成并存储(应该在detail里面生成,因为在detail中加入购物车的时候就需要把uuid传回去)

const state = {
    detail: {},
    uuid_token: getUUID()
};

 在请求拦截器中设置,在request中引入store就可以获取store中的数值。

在请求头的userTepId(这是和后台商量好的)中携带

if (store.state.detail.uuid_token) {
            config.headers.userTempId = store.state.detail.uuid_token
        };

购物车动态展示数据

Array.forEach(item=>{})遍历每个数组

Arry.every(item=>判断)如果都符合返回真否则为假 

    //计算总价格  
    totalPrice(){
        let sum=0
        this.cartInfoList.forEach(item=>{
          if(item.isChecked==1)
          {sum=sum+item.skuNum*item.skuPrice}
        })
        return sum
      },
     //判断是否全选
      isAllCheck(){
        return this.cartInfoList.every(item=>{return item.isChecked==1}) 
      }

修改购物车中商品数量 

 该接口需要传递商品数量的变化量正数为增加,负数为减少,因此点击+号num=1,点击减号,当当前的skuNum大于1则num=-1否则num=0;在输入框输入数字的时候,如果输入的是非法字符或者负数则不改变,因此num=0,如果是合法的数字则取整数然后减去原先的skuNum获得变化量。最后重新发送请求,如果发送成功则重新获取购物车数据。

为了避免频繁修改并发送请求,添加了节流函数

     changeNum:throttle(async function(type,num,cartInfo){
        switch(type){
          case 'add':
            num=1;
            break;
          case 'sec':
            if(cartInfo.skuNum>=2){
              num=-1
            }else{
              num=0
            }
            break;
          case 'change':
            if(isNaN(num)||num<1){
              num=0
            }else{
              // 这里的num没有双向数据绑定,所以num是改变后的值
              num=parseInt(num)-cartInfo.skuNum;
            }
        } 
        try{
          await this.$store.dispatch('detail/postAddToCart',{skuId:cartInfo.skuId,skuNum:num});
          this.getCartList()
        }catch(error){
          alert('error')
        }
        
      },500),

剩余的删除产品、改变产品状态的操作和前面差不多

删除全部选中的商品以及全选

在store里面处理逻辑,遍历购物车信息,选择的话就派发之前写的删除请求,并将返回的promise存在数组中,等遍历结束之后用promise.all来判断之前返回的promise中有没有失败的,只要有失败promise.all都会返回失败。

 deleteAll({ getters, dispatch }) {
        let promiseAll = []
        getters.cartInfoList.forEach(item => {
            let result = item.isChecked && dispatch('deleteCart', item.skuId)
            promiseAll.push(result)
        });
        // context.dispatch('deleteCart')
        //只要有一个失败,就会返回失败
        return Promise.all(promiseAll)
    },

Promise.all() 是一个内置的辅助函数,接受一组 promise(或者一个可迭代的对象),并返回一个promise

const allPromise = Promise.all([promise1, promise2, ...]);

 通过全选改变商品状态的方法和删除方法差不多

注册与登录界面

注册业务

 验证码

点击获取验证码的时候,先判断是否已经输入手机号,如果输入了手机号那就派发一个action。因为给手机发验证码要钱,这里就直接将返回的验证码存储在仓库里面,然后自动显示在验证码框上 

async getCode(){
        try{
         this.phone && await this.$store.dispatch('users/getCode',this.phone);
         this.code=this.$store.state.users.code;
        }catch(error){
          console.log('error')
        }
        
      }

注册

首先确认所有要填写的内容都有并且密码和确认密码相同 ,并向后台要数据,如果成功返回,那就跳转到登录页面

      async userRegister(){
        try{
          const {phone,password,code,passwordSure,agree}=this;
          if(phone&&password&&code&&password==passwordSure&&agree){
            await this.$store.dispatch('users/getRegister',{phone,password,code});
          this.$router.push('/login')
          }  
        }catch(error){
          console.log(error.message)
        }
        
      }

登录业务

token

 确认用户名和密码都有,就请求数据,成功则返回原来要去的界面(例如没有登录的情况下,如果去个人中心,会自动跳转到登录界面,登录成功后再跳转到个人中心界面)

      async login(){
        try{
         const {phone,password}=this
         if( phone&&password)
           {  
            await this.$store.dispatch('users/getLogin',{phone,password})
            let toPath=this.$route.query.toPath||'/home'
            this.$router.push(toPath)
            
        }
        }catch(error){
          console.log(error.message)
        }
        
      }

 注意:表单标签form有默认行为,要取消可以用prevant

 <button class="btn" @click.prevent="login">登&nbsp;&nbsp;录</button>

 登录成功后,后台返回的数据中有token,是用户登录的唯一标识,拿到token要先存在localstorage(因为vuex不是持久化的,如果不存在本地,刷新一下就没有了)并且存在state里面

    async getLogin(context, { phone, password }) {
        let result = await reqLogin({ phone, password });
        if (result.code == 200) {
            localStorage.setItem('TOKEN', result.data.token)
            context.commit('GETTOKEN', result.data.token);
            return 'ok'
        } else {
            alert(result.message)
            return Promise.reject(new Error(result.message));
        }
    },

使用请求拦截器,在请求头中加上token 

        if (store.state.users.token) {
            config.headers.token = store.state.users.token
        }

登录后header状态的改变

登录成功后,登录的地方要显示用户名,因此需要获得用户信息,

由于header不是路由组件,只挂载一次,因此不能在header的mounted里面获取;

如果在App.vue里面获取,由于App只加载一次,第一次加载的时候没有登录就没有token,因此无法获取用户信息,必须要手动刷新一次。

因此在路由守卫里面设置,发送路由变化的时候,如果已经有token那就获取用户信息

退出登录

向服务器请求删除数据,然后返回首页 

 async logout(){
      try{
       await this.$store.dispatch('users/getLogout');
       this.$router.push('/home')
      }catch(error){
        console.log(error.message)
      }
      
    }

 服务器返回成功后,要将仓库中的token和用户信息需要清除,本地存储的token也需要情况

   //退出登录后原先的token就没用了要清除
    async getLogout(context) {
        let result = await reqLogout();
        if (result.code == 200) {
            context.commit('CLEARINFO')
            return 'ok'
        } else {

            return Promise.reject(new Error(result.message));
        }
    },
    CLEARINFO(state) {
        state.token = '',
            state.userInfo = {},
            localStorage.removeItem('TOKEN')
    }

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值