美文网首页
购物车的实现

购物车的实现

作者: sweetBoy_9126 | 来源:发表于2018-11-05 18:29 被阅读9次
    {
      "status": 200,
      "message": "",
      "cartList": [
        {
          "shopTitle": "寻找田野",
          "shopId": 75960,
          "goodsList": [
            {
              "id": 73835,
              "img": "http://dummyimage.com/90x90/f27988",
              "number": 2,
              "price": 75,
              "sku": "全网通,玫瑰金,3+32G",
              "title": "VIVO-Y66 全网通/移动版 3+32GB"
            },
            {
              "id": 75572,
              "img": "http://dummyimage.com/90x90/79abf2",
              "number": 2,
              "price": 31,
              "sku": "全网通,香槟金,3+32G",
              "title": "VIVO-Y66 全网通/移动版 3+32GB"
            }
          ]
        },
        {
          "shopTitle": "猫咪森林",
          "shopId": 51336,
          "goodsList": [
            {
              "id": 57979,
              "img": "http://dummyimage.com/90x90/cef279",
              "number": 9,
              "price": 44,
              "sku": "全网通,玫瑰金,3+32G",
              "title": "VIVO-Y66 全网通/移动版 3+32GB"
            }
          ]
        },
        {
          "shopTitle": "老爹果园",
          "shopId": 94835,
          "goodsList": [
            {
              "id": 44271,
              "img": "http://dummyimage.com/90x90/f279f2",
              "number": 5,
              "price": 105,
              "sku": "全网通,香槟金,3+32G",
              "title": "VIVO-Y66 全网通/移动版 3+32GB"
            },
            {
              "id": 81594,
              "img": "http://dummyimage.com/90x90/79f2ce",
              "number": 8,
              "price": 39,
              "sku": "全网通,玫瑰金,3+32G",
              "title": "VIVO-Y66 全网通/移动版 3+32GB"
            }
          ]
        }
      ]
    }
    

    通过axios拿到数据通过vue渲染到dom上

    <div class="js-shop-list shop-list" v-for="(shop,index) in list">
      <div class="header">
            <div class="select-group js-select-group" @click="checkedShop(shop)">
              <span class="check" :class="{checked:shop.checked}"></span>
            </div> 
            <a class="shop-title">
              <i class="custom-store-img">店铺:</i>
              {{shop.shopTitle}}
            </a>
            <a href="javascript:;" data-type="cart" 
            class="j-edit-list pull-right c-blue font-size-12 edit-list"
            @click="edit(shop,index)"
            >
              <!---->
              {{shop.editingMsg}}
         </a>
      </div>
      <ul class="js-list block block-list block-list-cart border-0">
        <li class="block-item block-item-cart" v-for="(good,goodIndex) in shop.goodsList">
           <div class="check-container" @click="checkedGood(shop,good)">
             <span class="check" :class="{checked:good.checked}"></span>
           </div>
        </li>
      </ul>
      <div class="select-all" @click="checkedAll">
        <span class="check" :class="{checked: allChecked}"></span> 
         全选
      </div>
    </div>
    let app = new Vue({
      el: '#app',
      data: {
        list: null
      },
      methods: {
        getLists(){
            axios.get(url.cartList)
            .then((res)=>{
                //将原始数据res.data.cartList赋值给一个变量
                let list = res.data.cartList
                //然后对这个变量直接进行动态属性添加
                list.forEach(shop => {
                    shop.checked = true
                    shop.goodsList.forEach(good=>{
                        good.checked = true
                    })
                });
                //最后再赋值给实例里的list
                this.list = list
            })
        },
      }
    })
    

    显示如下


    1. 实现选中购物车同一店铺里的所有商品都选中后,该店铺选中,在methods里写一个checkedGood方法

     checkedGood(shop,good){
            good.checked = !good.checked
            //通过商品是否全选中来选中店铺
            //数组的every方法,只要有一个元素返回false,
            //最终结果就会返回false,否则就是true
            //也就是只要shop.goodsList中的元素有一个没选中返回false,那么shop.checked就会是false未选中
            shop.checked = shop.goodsList.every(good=>{
                return good.checked
            })
        },
    

    2. 实现选中该店铺,店铺里的所有商品选中

    checkedShop(shop){
      shop.checked = !shop.checked
      shop.goodsList.forEach(good=>{
        good.checked = shop.checked
      })
    }
    

    3. 通过计算属性实现店铺全部选中时,全选按钮选中

    computed: {
      allChecked: {
        get(){
          if(this.list && this.list.length){
            return this.list.every(shop=>{
              return shop.checked
            })
          }
          return false
        }
      }
    }
    

    然后点击全选按钮时切换选中状态通过一个checkedAll方法

    checkedAll(){
      this.allChecked = !this.allChecked
    }
    

    但是因为checkedAll方法更改的是allChecked的值,而allChecked是计算属性里的,所以直接这么写值并不会改变,我们需要通过它的set函数来改变它

    computed: {
      allChecked: {
        get(){
          if(this.list && this.list.length){
            return this.list.every(shop=>{
              return shop.checked
            })
          }
          return false
        }
    +  set(newVal){
    +     return this.list.forEach(shop=>{
    +        shop.checked = newVal
    +        shop.goodsList.forEach(good=>{
    +          good.checked = newVal
    +        })
    +      })
    +    }
      }
    }
    

    4. 获取到商品的总价格

    需要初始化的时候给一个total,因为这个是随着你选中不同商品价格实时改变的,所以也需要用计算属性,给一个checkedLists

    <div class="total-price" v-show="!editingShop">
         合计:¥<span class="js-total-price" 
    style="color: rgb(255, 102, 0);">{{total | priceNum}}</span>
       <p class="c-gray-dark">不含运费</p>
    </div>
    <script>
    data: {
    + total: 0
    }
    computed: {
    + checkedLists(){
        if(this.list && this.list.length){
          let arr = [];
          let total = 0;
          this.list.forEach(shop=>{
            shop.goodsList.forEach(good=>{
              if(good.checked){
                total += good.price * good.number
                arr.push(good)
              }
            })
          })
          this.total = total
          return arr
        }
        return []
       }
    }
    </script>
    

    5. 点击右上角编辑按钮进入编辑状态的实现,因为是点击每个店铺都会针对该店铺显示的内容进行改变,所以需要给shop一个editing属性用来作为是否显示编辑状态,还有一个editingMsg属性用来显示右上角显示的内容是编辑还是完成,而这些属性都需要加到原始数据那才可以响应

    <li class="block-item block-item-cart" 
          v-for="(good,goodIndex) in shop.goodsList"
          :class="{editing: shop.editing}"
     >
    methods: {
      getLists(){
        axios.get(url.cartList)
        .then((res)=>{
            let list = res.data.cartList
            list.forEach(shop => {
                shop.checked = true
      +        shop.editing = false //一开始不显示编辑状态下的内容
      +        shop.editingMsg = '编辑'
                shop.goodsList.forEach(good=>{
                    good.checked = true
                })
            });
            //最后再赋值给实例里的list
            this.list = list
        })
    },
    + edit(shop,index){
        shop.editing = !shop.editing
        shop.editingMsg = shop.editing ? '完成' : '编辑'
        //之所以要对list重新遍历是为了拿到每一个店铺的index与传入的index进行判断
        this.list.forEach((item,i)=>{
          //如果不是当前点击的这个店铺
          if(index !== i){
            item.editingMsg = shop.editing ? '' : '编辑'
            item.editing = false
          }
        })
      }
    }
    

    最后通过一个全局变量editingShop来判断是否有店铺正在编辑,然后渲染不同的内容,默认为false。全局变量editingShopIndex用来接收正在编辑的店铺的索引值默认为-1

    <button href="javascript:;" class="js-go-pay btn 
    btn-orange-dark font-size-14"
      :disabled="!checkedLists.length"
      v-show="!editingShop"
    >
      结算 (3)
    </button>
    <!-- 编辑状态 -->
    <button href="javascript:;" :disabled="!removeLists.length" 
    class="j-delete-goods btn font-size-14 btn-red" 
    v-show="editingShop">删除
    </button>
    
    data: {
    + editingShop: false
    + editingShpIndex: -1 
    }
    methods: {
      edit(shop,index){
      + this.editingShop = shop.editing ? shop : null
      + this.editingShpIndex = shop.editing ? index : -1
      }
    }
    

    6. 编辑状态下的选中

    将编辑状态下选中的属性定义为removeChecked,同样因为需要响应,所以要在原始数据里处理

    methods: {
      getLists(){
        axios.get(url.cartList)
        .then(res=>{
            let list = res.data.cartList
            list.forEach(shop => {
              shop.checked = true
    +         shop.removeChecked = false
              shop.editing = false //一开始不显示编辑状态下的内容
              shop.editingMsg = '编辑'
              shop.goodsList.forEach(good=>{
                good.checked = true
    +           good.removeChecked = false
            })
        });
      //最后再赋值给实例里的list
      this.list = list
        })
      }
    ]
    

    然后只需要对之前写的方法里对editingShop是否存在,然后让他们的选中状态是checked还是removeChecked进行切换就行了,这里的editingShop就相当于遍历的shop

    //将通过商品是否全选来选中店铺的方法checkedGood修改
    checkedGood(shop,good){
      //如果是店铺正在编辑状态attr就使用removeChecked这个属性,否则就是checked
      let attr = this.editingShop ? 'removeChecked' : 'checked'
      good[attr] = !good[attr]
      shop[attr] = shop.goodsList.every(good=>{
        return good[attr]
      })
    }
    
    //将通过选中店铺来选中店铺中的所有商品的方法checkedShop修改为
    checkedShop(shop,good){
      let attr = this.editingShop ? 'removeChecked' : 'checked'
      shop[attr] = !shop[attr]
      shop.goodsList.forEach(good=>{
        good[attr] = shop[attr]
       })
    }
    
    //全选按钮同样是用一个计算属性removeChecked
    computed: {
    + removeChecked: {
        get(){
          //如果是编辑状态下,返回editingShop(shop)里的removeChecked的状态
          if(this.editingShop){
            return this.editingShop.removeChecked
          }
          return false
        }
        set(newVal){
          if(this.editing){
            //可编辑状态下的店铺的选中值是newVal
            this.editing.removeChecked = newVal;
            //可编辑状态下的店铺下面的全部商品的选中值都是newVal
            this.editing.goodsList.forEach(good=>{
              good.removeChecked = newVal
            })
          }
        }
      }
    }
    methods: {
      checkedAll(){
        let attr = this.editingShop ? 'removeChecked' : 'allChecked'
        this[attr] = !this[attr]
      }
    }
    
    //将之前的html修改为
    <div class="select-group js-select-group" @click="checkedShop(shop)">
       <span class="check" :class="{checked:editingShop ? shop.removeChecked : shop.checked}"></span>
    </div> 
    
    <div class="check-container" @click="checkedGood(shop,good)">
        <span class="check" :class="{checked:editingShop ? good.removeChecked : good.checked}"></span>
    </div>
    
    <div class="select-all" @click="checkedAll">
       <span class="check" :class="{checked: editingShop ? removeChecked : allChecked}"></span> 
       全选
     </div>
    

    通过removeLists方法来记录你编辑状态下选中的商品

    removeLists(){
        if(this.editingShop){
            let arr = []
            //this.editingShop就是shop
            this.editingShop.goodsList.forEach(good=>{
                if(good.removeChecked){
                    arr.push(good)
                }
            })
            return arr
        }
        return []
    }
    
    <button href="javascript:;" :disabled="!removeLists.length" 
      class="j-delete-goods btn font-size-14 btn-red" 
      v-show="editingShop">删除
    </button>
    

    现在就已经实现了不同状态下的选中

    7. 点击加减实现商品数量的改变

    这里不管是增加还是减少都是通过一个后台数据接口来存储的,只要响应成功了才能进行页面的加减

    <button type="button" class="minus" :class="{disabled:good.number === 1}"></button>
    <input type="text" pattern="[0-9]*" class="txt" v-model="good.number">
    <button type="button" class="plus "></button>
    <div class="response-area response-area-minus" @click="reduce(good)"></div>
    <div class="response-area response-area-plus" @click="add(good)"></div>
    
    reduce(good){
      if(good.number === 1) return
      axios.post(url.reduce,{
        id:good.id,
        number: good.number
      }).then(res=>{
        good.number -= 1
      })
    }
    add(good){
      axios.post(url.add,{
        id: good.id,
        number: good.number
      }).then(res=>{
        good.number += 1
      })
    }
    

    8. 编辑状态下未选中当前商品点击当前上商品的删除按钮删除当前商品


    当我们点击删除(remove)按钮会弹出一个弹窗需要我们进行确认,所以这里我们需要一个全局属性(removePopup)来作为显示这个弹窗的值,然后点击确定(removeConfirm)后才会删除当前商品,删除商品也需要通过接口传递数据,并且如果店铺里的商品数量为0,那么当前店铺也会被删除,然后重新将编辑(removeShop)状态改为显示状态(this.editing=false,this.editingShop=false,this.editingIndex=-1),并且每一个店铺的editingMSg变为'编辑'

    <div class="delete-btn" @click="remove(shop,index,good,goodIndex)"><span>删除</span></div>
    
    <div id="hEQD1ZYb2p" v-show="removePopup">
        确定要删除该商品吗?
        <div class="btn-2-1" @click="removePopup=false">
            <p>取消</p>
       </div>
        <div class="btn-2-1" @click="removeConfirm">
            <p>确定</p>
       </div>
    </div>
    <script>
    data: {
    + removePopup : false,
    + removeData: null//存储你删除的数据
    }
    methods: {
    + remove(shop,index,good,goodIndex){
        this.removePopup = true
        //通过remove拿到他们里面传进来的参数赋值给removeData
        this.removeData = {shop, index, good, goodIndex}
       }
    + removeConfirm(){
          //因为removeConfirm方法需要对你remove传进来的参数进行使用,而它本身并不能传入这些参数,所以我们要通过一个属性removeData来存储你删除的这些数据里的参数
          //直接通过解构拿到remove里面传进来的参数
          let {shop, index, good, goodIndex} = this.removeData
          axios.post(url.remove,{
            id: good.id
          }).then(res=>{
            //删除当前good,shop.goodsList就是good数组的集合就相当于[good1, good2, good3]
            shop.goodsList.splice(goodIndex,1)
            //如果店铺里的商品长度为0的时候
            if(!shop.goodsList){
              //this.list就是每一个shop的数组,也就是删除当前这个shop
              this.list.splice(index,1)
              //删除这个店铺后直接重置为非编辑状态
              this.removeShop()
            }
            this.removePopup = false
          })
      },
    + removeShop(){
        this.editing = false;
        this.editingShop = null,
        this.list.forEach(item=>{
            item.editingMsg = '编辑'
        })
      }
    }
    </script>
    
    1. 选中状态下点击总的删除按钮删除选中的一个或多个商品



      通过给一个全局属性removeMsg里的信息来做判断,如果未选中或者是通过当前的删除按钮来删除removeMsg里的信息就是'确定要删除该商品吗?
      '否则如果选中状态下通过下方删除按钮就是‘确定要删除所选${this.removeLists.length}个商品删除?’

    <div id="hEQD1ZYb2p" v-show="removePopup">
        {{removeMsg}}
        <div class="btn-2-1" @click="removePopup=false">
            <p>取消</p>
       </div>
        <div class="btn-2-1" @click="removeConfirm">
            <p>确定</p>
       </div>
    </div>
    
    <!--右下角删除按钮-->
    <button href="javascript:;" :disabled="!removeLists.length" 
    class="j-delete-goods btn font-size-14 btn-red" 
    v-show="editingShop"
    @click="removeList()"
    >删除
    </button>
    data: {
    +  removeMsg: ''
    }
    methods: {
      remove(shop,index,good,goodIndex){
         //删除单个商品
    +   this.removeMsg = '确定要删除该商品吗?'
      },
    +  removeList(){
           this.removePopup = true
           //删除选中状态下的一个或多个商品时的弹窗消息
           this.removeMsg = `确定要删除所选${this.removeLists.length}个商品删除?`
       },
      removeConfirm(){
        //如果是删除单个商品,通过当前商品的删除按钮触发
    +   if(this.removeMsg === '确定要删除该商品吗?'){
            let {shop,index,good,goodIndex} = this.removeData
            axios.post(url.remove,{
                id: good.id 
            }).then(res=>{
                //删除当前good,shop.goodsList就是每一个遍历的good
                shop.goodsList.splice(goodIndex,1)
                //如果店铺里的商品长度为0的时候
                if(!shop.goodsList.length){
                    //this.list就是每一个shop的数组,也就是删除当前这个shop
                    this.list.splice(index,1)
                    //删除这个店铺后直接重置为非编辑状态
                    this.removeShop()
                }
                this.removePopup = false
            })
    +   }else{
            //删除选中状态下的一个或多个商品时,通过下方的删除按钮触发的
            let ids = []
            this.removeLists.forEach(good=>{
                ids.push(good.id)
            })
            axios.post(url.mrremove,{
                ids
            }).then(res=>{
                let arr = []
                //遍历在编辑状态下被选中的shop里的good
                this.editingShop.goodsList.forEach(good=>{
                    //找到在编辑状态下当前店铺下的与选中的商品列表中id一致的那个商品的个索引
                    //也就是说你遍历的正在编辑状态下的店铺下的商品是不是在你的删除列表里面
                    //findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。
                    let index = this.removeLists.findIndex(item=>{
                        return item.id === good.id
                    })
                    //如果不在你的删除列表里面,也就是当前店铺中你没选中的商品(不需要删除的),就用一个新数组接收它
                    if(index === -1){
                        arr.push(good)
                    }
                })
                //如果当前编辑状态下的店铺中有没删除的商品,点击删除按钮触发时,
                //当前店铺里的商品就重新渲染成了你之前没选中(没删除)的那些,也就相当于删除了你选中的那些商品了
                if(arr.length){
                    this.editingShop.goodsList = arr
                }else{
                    //如果都在你的删除列表中,那么arr数组就是空,店铺里的商品就全删除了,需要把当前店铺删除
                    this.list.splice(this.editingShopIndex,1)
                    this.removeShop()
                }
                this.removePopup = false
            })
        }
      },
    }
    

    相关文章

      网友评论

          本文标题:购物车的实现

          本文链接:https://www.haomeiwen.com/subject/sfosxqtx.html