您的位置:时时app平台注册网站 > web前端 > 7-加入购物车、购物车删除(vuex)彩世界网址

7-加入购物车、购物车删除(vuex)彩世界网址

2019-11-28 05:16

  今天终于把商品页和购物车功能弄出来了,在这个开发过程中遇到一些小坑,比如购物车和商品页是分开两个组件的,没有利用到vue的双向数据绑定的特性,导致在操作加减商品数量时两个组件的数据没有同步,后来我就重写了一遍,好好的利用了vuex的状态保持,这个东西真的很好用。先秀一段我写的vuex代码吧!

三、购物车商品数量限制

思路 : 这类显示隐藏的案例,都是设置变量属性的ture/false
首先是有一个弹窗组件,当商品数量大于最大值得时候,这个组件需要弹出。
数据中已经有了最大值 : limit_num。
在state中定义一个变量:maxOff :false ,默认不显示,当购物车中商品增加的时候,比对当前商品的数量是否已经大于了limit_num,如果是,就让这个弹窗出来,也就是在mutation中设置该属性为true。

  • 商品数量超过后显示弹窗
 // 加入购物车
    addCarPanelData (state,data) {
      state.carPanelData.forEach((goods) => {
        if (goods.sku_id === data.sku_id) {
          goods.count  
          bOff = false
          //比较当前商品的数量和数据中的商品最大购买数量
          if (goods.count > goods.limit_num) {
            goods.count--
            state.maxOff = true
          }
        }
      })
 }
//组件中
<div id="prompt" v-if="maxOff">

 computed: {
        maxOff(){
            return this.$store.state.maxOff
        }
  }
  • 关闭弹窗
//mutations
   closePrompt (state) {
     state.maxOff = false
   }
//组件中
  methods : {
       closePrompt(){
           this.$store.commit('closePrompt')
       }
   }
 1 import Vue from 'vue';
 2 import App from './App';
 3 import router from './router';
 4 import VueResource from 'vue-resource';
 5 import Vuex from 'vuex';
 6 import vuex_store from './store';
 7 
 8 Vue.use(VueResource);
 9 Vue.use(Vuex);
10 
11 new Vue({
12     el: '#app',
13     router,
14     template: '<App/>',
15     components: { App },
16     store: vuex_store(Vuex)
17 });

四、购物车显示隐藏

思路: 一样的,这类显示隐藏,需要设置一个开关,去切换开关即可。

 state: {
    carPanelData: [],
    maxOff : false, // 弹窗开关
    carShow : false, // 购物车开关
    carTimer : null // 购物车定时器
  },
 // 购物车显示
    showCar (state) {
      clearTimeout(state.carTimer)
      state.carShow = true
    },
    // 购物车隐藏
    hideCar (state) {
      state.carTimer = setTimeout(() => {
        state.carShow = false
      },1000)
    }
===================
//组件中
 methods: {
        // 显示购物车
        showCar(){
           this.$store.commit('showCar')
        },
        // 隐藏购物车
        hideCar(){
          this.$store.commit('hideCar')
        }
 }
 1 //状态管理
 2 export default (Vuex) => {
 3     return new Vuex.Store({
 4         state: {
 5             totalMoney: 0, //已选购商品总价格
 6             productArray: [] //已选购商品数组
 7         },
 8         mutations: {
 9             setTotalMoney(state, num) { //设置商品总价格
10                 state.totalMoney = num;
11             },
12             mathTotalMoney(state) { //计算已选购商品总价格
13                 let total = 0;
14                 for (let i = 0; i < state.productArray.length; i  ) {
15                     let item = state.productArray[i];
16                     total  = (item.count * item.price);
17                 }
18                 state.totalMoney = total;
19             },
20             setProductArray(state, obj) { //商品放入或拿出购物车
21                 let index = -1;
22                 for (let i = 0; i < state.productArray.length; i  ) {
23                     var item = state.productArray[i];
24                     if (obj.id == item.id) {
25                         index = i;
26                         break;
27                     }
28                 }
29                 if (index >= 0) {
30                     if (obj.count <= 0) {
31                         state.productArray.splice(index, 1);
32                     } else {
33                         state.productArray[index] = obj;
34                     }
35                 } else {
36                     state.productArray.push(obj);
37                 }
38             },
39             clearProduct(state) { //清空购物车
40                 state.productArray = [];
41             }
42         },
43         getters: {
44             getTotalMoney(state) { //获取商品总价格
45                 return state.totalMoney;
46             },
47             getProductArray(state) { //获取已选购商品
48                 return state.productArray;
49             },
50             getProductById: (state, getters) => (id) => { //根据ID获取已选商品
51                 for (let i = 0; i < state.productArray.length; i  ) {
52                     var item = state.productArray[i];
53                     if (item.id == id) {
54                         return item;
55                     }
56                 }
57                 return false;
58             }
59         }
60     });
61 }

屏幕快照 2018-02-28 下午10.27.35.png

  源码地址:

四、购物车小球效果

思路:用的vue的transtion钩子函数,原理就是先把小球写死到购物车,点击的时候瞬间移入到需要的位置,然后做一个过渡动画即可,加入贝塞尔曲线。

state: {
    ball: { // 购物车小球
      show: false,
      el: null, // 点击的是哪个购物车按钮
      img: ''
}
mutations: {
    // 加入购物车
    addCarPanelData (state,data) {
      // 加上这个条件,确保小球飞完再添加
      if (!state.ball.show) {
        // 显示购物车
        state.carShow = true
        let bOff = true
        state.carPanelData.forEach((goods) => {
          // 比对ID,相同就说明购物车已存在此商品,数量增加
          if (goods.sku_id === data.sku_id) {
            goods.count  
            bOff = false
            if (goods.count > goods.limit_num) {
              goods.count--
              state.maxOff = true
              return
            }
            // 加入购物车,小球显示
            state.ball.show = true
            state.ball.img = data.ali_image
            // 通过event对象获取到当前点击的按钮
            state.ball.el = event.path[0]
          }
        })
        // 商品不存在,就往数组里新增商品数据
        if (bOff) {
          let goodsData = data
          Vue.set(goodsData,'count',1)
          state.carPanelData.push(goodsData)
          // 加入购物车,小球显示
          state.ball.show = true
          state.ball.img = data.ali_image
          // 通过event对象获取到当前点击的按钮
          state.ball.el = event.path[0]
        }
        console.log(event)
      }
    }
============
//组件内
 // 小球进入前,初始化
      beforeEnter(el){
        // 获取按钮的位置
        let rect = this.ball.el.getBoundingClientRect()
        // 获取购物车
        let rectEl = document.getElementsByClassName('ball-rect')[0].getBoundingClientRect()
        // 获取小球
        let ball = document.getElementsByClassName('mask-item')[0]
        //计算按钮和购物车的差值 : 购物车的中心点到左侧的距离 - 按钮中心点到左侧的距离
        let x = (rectEl.left   16) - (rect.left   rect.width/2)
        let y = rect.top   rect.height/2 - rectEl.top   5 - 16
        //计算小球和包着小球的父级的位置
        ball.style.transform = 'translate3d(-' x 'px,0,0)'
        el.style.transform = 'translate3d(0,' y 'px,0)'

        ball.src = this.ball.img
        console.log(this.ball.img)
      },
      //开始运动
      enter (el){
        let a = el.offsetHeight
        // 获取小球
        let ball = document.getElementsByClassName('mask-item')[0]
        el.a = a //避免变量没有使用,eslint报错
        el.style.transform = "translate3d(0,0,0)"
        ball.style.transform = "translate3d(0,0,0)"
      },
      // 结束,让小球隐藏
      afterEnter (el){
        this.ball.show = false
      }
    }
===========
    <transition 
      name="ball" 
      v-on:before-enter="beforeEnter" 
      v-on:enter="enter" 
      v-on:after-enter="afterEnter" 
      v-bind:css="true"
    >
      <div class="addcart-mask" v-show="ball.show">
        <img class="mask-item" src="" alt="彩世界网址 1">
      </div>
    </transition>
 ==========
 .ball-enter-active{
    transition: 1s cubic-bezier(.18,1,.94,1)
  }
  .ball-enter-active .mask-item{
    transition: 1s cubic-bezier(0,0,0,0)
  }

  上面代码就是入口文件,我将来 vuex 对象再传入我自己写的那个store模块中。接着继续说我的商品页和购物车吧,贴个动图给大看看效果如何。

彩世界网址 2

  不过我总觉得,我这样的用法有点不太对的。贴个代码,希望有高手指点下,我这样使用vuex可取不。

实现步骤解析

彩世界网址 3

一、加入购物车

  1. 将购物车数据统一放在vuex中:
    state里放置一个数组:carPanelData,里面放置购物车数据。
// 购物车商品数据
state: {
    carPanelData: []
}
  1. 往购物车里push数据:在mutation里面更改state数据。
    思路:首先需要将点击加入购物车的每一条数据加一个属性:count,计数,然后将这条点击的商品数据push到state中,当然,首先是需要先用商品ID和state里的商品ID比对,如果没有就push,如果有了,就计数。
    以下是代码思路
mutations:
    addCarPanelData(state,加入购物车的数据data){
        //循环carPanelData购物车数据
        //如果商品ID存在(购物车的id和传进来的ID比对),就设置count  
        //设置开关false
===================================================================
        **注意** : 如果上面的条件成立,以下是不执行的,所以可以设置一个开关bOff
        //如果开关值为true
        //否则就是商品ID不存在,设置一个新的变量goodsData = 传进来的data;
        //Vue.set(goodsData,'count',1):为这个变量设置count属性,值为1;
        //将这个goodsData,push到carPanelData中;              
}
--------------------------------------------------------------
 mutations: {
    addCarPanelData (state,data) {
      let bOff = true
      state.carPanelData.forEach((goods) => {
        if (goods.sku_id === data.sku_id) {
          goods.count  
          bOff = false
        }
      })
      if (bOff) {
        let goodsData = data
        Vue.set(goodsData,'count',1)
        state.carPanelData.push(goodsData)
      }
      console.log(state.carPanelData)
    }
  }
  1. 在商品页选择了商品,点击加入购物车按钮:这时,将这条数据传给vuex,记录到state中(mutation里面已经对该逻辑进行了处理)
    要点 : 记住vuex的思想,要想改变state,必须提交mutation。
 methods:{
       addCarPanelHandel(data){
        //改变state,必须提交mutation,并将此条数据传给vuex
          this.$store.commit('addCarPanelData',data)
        }
}
  1. 这时已经基本完成了购物车的逻辑,下面,我们把购物车单独出来做成组件:car-panel。
    这个时候就可以把相关数据绑定在购物车了。
    要点 :如何获取在购物车组件内获取vuex数据?
    用computed即可。
//相应的绑定代码=示例
<a href="#/item/100027401">{{item.title}})</a>
//获取vuex数据
computed : {
        carPanelData(){
            return this.$store.state.carPanelData
        },
        count(){
            return this.$store.getters.totleCount
        },
        totle(){
             return this.$store.getters.totlePrice
        }
}
  1. 最后,对购物车中的商品数量和商品总价计算。
    在vue中,我们需要对变量进行进一步处理,可以放在computed里,不建议放在模板中,同样,vuex中,state中的状态如果需要进一步处理,我们可以放入getters.
getters:{
    // 购物车商品数量计算
    totleCount (state) {
      let count = 0
      state.carPanelData.forEach((goods) => {
        count  = goods.count
      })
      return count
    },
    // 总价格
    totlePrice (state) {
      let price = 0
      state.carPanelData.forEach((goods) => {
        price  = goods.price * goods.count
      })
      return price
    }
  }

============================================================

  商品也和购物车功能,暂且就这些了。重点还是在布局上,js上的逻辑都不难。可以上我的github获取源码看看咯。

过程分析

1.首先购物车弹窗是一个组件,因为会出现在不同的页面中。
2.因为很多组件会用到购物车数据,所以统一放到vuex中。

二、购物车删除

一开始我的思路是
1、删除数据肯定是要改变state,改变state肯定是需要提交mutation,所以删除的相关逻辑方法应该写在mutation;
2、当时我的问题是如何知晓删除的是哪一条数据?
通过学习,弄清楚了,以后此类需求,都和加入购物和思路是一致的,都是通过对比删除的当前的ID和数据里的所有ID进行比对,就知道是删除具体哪条数据了。
3、那么我需要记住,当前选择的是哪条数据,都是通过在删除的点击方法对应的事件里,参数中传递当前数据(商品ID)即可。这是一个思路,需要牢记
具体实现步骤总结
点击购物车页面的删除商品按钮,绑定一个删除方法,参数传入当前被点击的商品ID,在这个方法里调用mutation里面的删除商品方法:

  1. 首先需要循环state的购物车数据;
  2. 比对每一项的商品ID是否和当前传入的ID相同,如果是相同的那么就return,不再继续循环了;
  3. 在state的购物车数据里删除这项ID相同的数据。
//mutation 
delCarPanelData (state,id) {
      state.carPanelData.forEach((goods,index) => {
        if (goods.sku_id === id) {
          state.carPanelData.splice(index,1)
          return
        }
      })
}
//购物车组件中
methods: {
        delCarPanelHandel(id){
            this.$store.commit('delCarPanelData',id)
        }
}
<div class="del-btn"  @click="delCarPanelHandel(item.sku_id)"> 删除 </div>

至此,已经完成了加购物车,并且计算数量和金额。

本文由时时app平台注册网站发布于web前端,转载请注明出处:7-加入购物车、购物车删除(vuex)彩世界网址

关键词: