vue框架下的組件化的購物車實現

最近在學習vue,而後瞭解到這個框架的一個突出特色就是組件化,因此用這種形式實現了一個購物車,由於在實際項目中,數量加減可能不僅在購物車裏用到,因此把這個小的效果也提取出來了,在實現過程當中造成了不少坑,這裏記錄一下,但願對你們能有所幫助。css

tip1: 這裏會用到使用的組件庫是vux,  須要先安裝(npm insatall vux --save   npm install vux-loader --save-dev),而後具體怎麼使用,若是不清楚請去看vux官網。html

我把列表和底部的全選計數分別寫成了組件。vue

tip2:這裏涉及到了父子組件之間的傳值,以及非父子組件之間的傳值。其中父子組件之間的傳值不在贅述,非父子組件之間傳值,須要定義個公共的公共實例文件bus.js,做爲中間倉庫來傳值,否則路由組件之間達不到傳值的效果。web

bus.js:npm

import Vue from "vue"
export default new Vue()

 

cart父組件(這裏是本身寫死的數據,寫在了父組件,這樣就能夠避免寫在列表頁要傳給父組件,以後再由父組件傳給footer組件的弊端):數組

html:app

<template>
  <div id="cart">
    <div class="contentWrapChild">
      <cart-list :cartList="cartList" ></cart-list>
      <cart-footer :cartList="cartList" ></cart-footer>
    </div>
  </div>
</template>

 

js:框架

  import cartList from "./component/cartList.vue";
  import cartFooter from "./component/cartFooter.vue"
  import Bus from "./../../assets/js/bus"

  export default{
    data(){
      return{
        cartList:[
          {
            id:1,
            img:"../../../assets/img/bindOwner.jpg",
            title:"中秋節茶月餅禮盒",
            spec:"規格:",
            priceNow:500,
            number:2,
            checked:false,
            stock:5,       //  庫存
            index:0,
          },
          {
            id:2,
            img:"../../../assets/img/shopIndex1.jpg",
            title:"fx參天眼藥水",
            spec:"規格:",
            priceNow:45,
            number:2,
            checked:false,
            stock:5,
            index:1,
          },
          {
            id:3,
            img:"../../../assets/img/shopIndex2.jpg",
            title:"牛奶沐浴乳",
            spec:"規格:",
            priceNow:20,
            number:2,
            checked:false,
            stock:5,
            index:2,
          }
        ],
        newCartData:[],
      }
    },
    components:{
      cartList,
      cartFooter
    }
  }

 

list組件(列表子組件):iphone

<template>
  <div class="cartList">
    <group ref="list">
      <cell v-for="(item,index) in this.cartList" :key="index">
        <div style="border-bottom:1px solid #e5e5e5">
          <div class="child child-inp">
            <input type="checkbox" class="choice" :checked="item.checked" @click="listSelect(item.id)">
          </div>
          <div class="child child-img goodsImg">
            <img :src="item.img"/>
          </div>
          <div class="child child-text">
            <p class="title">{{item.title}}</p>
            <p class="weui-media-box__desc spec">{{item.spec}}</p>
            <p class="point"><span>{{item.priceNow}}</span>積分</p>
          </div>
        </div>
        <div style="text-align: right;margin-right:0.2rem;">
          <num-choice :gsId="item.id" :count="item.number" :stock="item.stock" ></num-choice>
          <i class="icon iconfont icon-icon--" style="font-size:22px;vertical-align: text-bottom" @click="deleteGoods(item.id)"></i>
        </div>
      </cell>
    </group>
  </div>
</template>

 

對應的樣式:工具

.cartList >>> .weui-cells{
    margin-top:0
  }
  .cartList >>> .vux-cell-primary{
    flex:none;
  }
  .cartList >>> .weui-cell__ft{
    width:100%;
    height:100%;
    text-align: left;
  }
  .cartList >>> .weui-cell{
    height:2.14rem;
    padding:0;
  }
  .cartList{
    width:100%;
    .child{
      display:inline-block;
    }
    .child-inp,.child-img{
      height:100%;
      vertical-align: top;
    }
    .child-inp{
      width:0.5rem;
      padding-left:0.3rem;
      input{
        width: 0.38rem;
        height: 0.38rem;
        background: #fff;
        border: 1px solid #ddd;
        appearance: normal;
         -moz-appearance: button;
         -webkit-appearance: button;
        outline: none;
        border-radius: 2px;
        margin: 0.52rem 0 0 0;
        position: relative;
        vertical-align: middle;
        margin-right: 0.5rem;
      }
    }
    .goodsImg{
      margin-right: .8em;
      width: 1.2rem;
      height: 1.2rem;
      line-height: 1.42rem;
      text-align: center;
      img{
        width:100%;
        height:100%;
      }
    }
    .child-text{
      margin: 0.22rem 0.2rem 0.1rem 0;
      .title{
        font-size: 0.28rem;
        color: #999;
        line-height: 0.36rem;
        white-space: normal;
      }
      .spec{
        font-size: 0.24rem;
        line-height:0.4rem;
      }
      .point{
        font-size: 0.3rem;
        color: #ff0000;
      }
    }
  }

 

 js:  (注意本身項目的路徑)

  import NumChoice from "../../../components/numChoice.vue"
  import Bus from "./../../../assets/js/bus"    //    和footer組件引入公共的bug,來作爲中間傳達的工具
  export default{
    data(){
      return{
        newGs:[],
        liCheckedStatus:false   
      }
    },
    components:{
      NumChoice,
      Actionsheet,
      Group,
      XSwitch
    },
    props:[
        "cartList",
    ],
    methods:{
//  點擊單個列表項:由於總結算數和總的積分數都要變化,全部要傳值給CartFooter組件-----暫時用非父子組件之間的傳值方法
//  一、 實現單個列表的反選
//  2   把選中的商品push到新的數組中去,方便傳值給cartFooter組件
      listSelect(gsId){
        let gs = this.cartList;
        let newGs = this.newGs.splice(0,this.length);   //   每次給newGs push的時候都先把newGs清空
        for(let i in gs){
          let item = gs[i];
          if(item.id == gsId){
            item.checked = !item.checked;
          }
          if(gs[i].checked){
            newGs.push(item);
          }
        }
//     把新獲得的列表回傳給footer組件
        Bus.$emit("fromList",newGs)
      },
//    刪除商品
      deleteGoods(gsId){
        let that = this;
        this.$vux.confirm.show({
          title:"刪除該商品",
          content:"是否肯定刪除該商品?",
          onConfirm () {
            let goodsArr = [];
              for (let i in that.cartList){
                let item = that.cartList[i];
                if(item.id == gsId){
                  that.cartList.splice(i,1);
                }
              }

              for(let j in that.cartList){
                let item = that.cartList[j];
                if(item.checked){
                  goodsArr.push(item);
                }
              }

            Bus.$emit("fromList",goodsArr)
          },
          onCancel () {

          },
        })
      },
    },
    created(){
//    接收numberChoice組件回傳回來的新的商品數量
      Bus.$on("fromNumChoice",(data,id)=>{
        let newGs = this.newGs;   //   每次給newGs push的時候都先把newGs清空

        for(let i in this.cartList){
          if(id == this.cartList[i].id ){    //   判斷點擊的商品id和傳過來的id同樣,這件商品就改爲選中狀態,同時push到新的數組中,把新的數組傳給footer組件
            this.cartList[i].checked = true;
            this.cartList[i].number = data;
          }
        }
        let ckCertList = [];
        for(let i in this.cartList){
            if(this.cartList[i].checked){
              ckCertList.push(this.cartList[i]);
            }
        }
        Bus.$emit("fromList",ckCertList)   //   回傳新的列表
      })

    }
  }

 

 footer組件(底部)

html:

<template>
  <div class="cartFooter">
    <p class="prompt">積分餘額:200</p>
    <div class="footer">
      <label>
        <input type="radio" @click="allSelect" :checked="isAllChecked">全選
      </label>
      <p class="pointAll">合計:{{totalPoint}}<span>積分</span></p>
      <a href="#" @click="settle" >去結算({{totalNumber}})</a>
    </div>
  </div>
</template>

css:

.cartFooter{
    width:100%;
    height:1.56rem;
    position:fixed;
    bottom:0;
    .prompt{
      width: 100%;
      height: 0.58rem;
      font-size: 0.24rem;
      color: #ff0000;
      background: #999;
      line-height: 0.58rem;
      text-align: center;
      position: fixed;
      bottom: 0.98rem;
      /*z-index: 3;*/
    }
    .footer{
      width: 100%;
      height: 0.98rem;
      color: #3ccd58;
      background: #555;
      line-height: 0.98rem;
      display: flex;
      label{
        font-size: 0.26rem;
        color: #fff;
        margin: 0 0.2rem;
        display: table;
        input{
          width: 16px;
          height: 16px;
          background: #fff;
          border: 1px solid #ddd;
          /* margin-right: 5px; */
          appearance: normal;
          -moz-appearance: button;
          -webkit-appearance: button;
          outline: none;
          border-radius: 2px;
          margin: 0 5px 3px 0;
          position: relative;
          vertical-align: middle;
        }
      }
      p{
        font-size: 0.32rem;
        padding-right: 0.14rem;
      }
      a{
        font-size: 0.3rem;
        color: #fff;
        background: #3ccd58;
        padding: 0;
        line-height: 0.98rem;
        flex: 1;
      }
    }
  }

js:

import Bus from "./../../../assets/js/bus"    //    和cartList組件引入公共的bug,來作爲中間傳達的工具

  export default{
    data(){
      return{
        totalPoint:0,
        totalNumber:0,
        isAllChecked:false
      }
    },
    props:{
      cartList:Array,   // 父組件傳過來的數據
    },
    methods:{
//       點擊全選按鈕:
//      1.  實現全選按鈕的反選
//      2.  選中全選按鈕時,全部列表項是選中狀態,總結算數爲列表的length; 合計積分數爲每一個列表項的數量*單個列表項的積分數的總和。不然就是所有未選中狀態;
      allSelect(){
        this.isAllChecked =!this.isAllChecked;   //  取反
        let list = this.cartList;
        this.totalPoint = 0;   //   每次點擊全選按鈕的時候把總積分清零
        for(let i in list){
          let item = list[i];
          item.checked = this.isAllChecked;
          if(list.length > 0 && item.checked){
            this.totalNumber = list.length;
            this.totalPoint += item.number*item.priceNow;

          }else{
            this.totalNumber = 0;
            this.totalPoint = 0;
          }
        }
      },
      settle(){
        if(this.totalNumber == 0){
          this.$vux.toast.show({
            width:"250px",
            type:'text',
            text:'您尚未選擇須要結算的商品',
            position:"middle"
          });
        }
      }
    },
    created(){
//    接收列表頁傳過來的新的列表
      Bus.$on("fromList",(data)=>{
        let newGs = data;
        this.totalPoint = 0;   //  每次計算以前先把總積分數清零
        this.totalNumber = 0;
        for(let j in newGs){
          if(newGs[j].checked){
            this.totalPoint += newGs[j].number * newGs[j].priceNow;

          }
        }
        if(this.cartList.length != "" && this.cartList.length == newGs.length){   //   控制全選按鈕的選中狀態  用傳過來的新列表的長度和原始列表的長度進行比較

          this.isAllChecked = true;
        }else{
          this.isAllChecked = false;
        }
//       商品總件數爲新列表的總長度
        this.totalNumber = data.length;
      })
    },
  }

 

numChoice組件(商品加減組件):
html:
<template>
  <div class="numChoice">
    <button class="sub" @click="sub">-</button>
    <input type="text" v-model='value'/>
    <button class="add" @click="add">+</button>
  </div>
</template>

css:

.numChoice{
    display:inline-block;
    vertical-align: super;
  }
  /*清除input默認樣式*/
  input{
    outline:0;   /*去掉谷歌自帶的點就input框出現的邊框狀況*/
    /*-webkit-appearance:button;  是元素標籤看起來像個按鈕,意思是定義了按鈕樣式*/
    -webkit-appearance:none;  /*去掉按鈕樣式*/
    border-radius: 0;
  }
  .numChoice button,.numChoice input{
    background-color: #fff;
    border: 1px solid #999;
    font-size: 17px;
    text-align: center;
    vertical-align: middle;
    -webkit-appearance : none ;  /*解決iphone safari上的圓角問題*/
    border-radius: 0;
  }
  .numChoice button{
    width: 22px;
    height: 22px;
    line-height: 14px;
    color: #666;
  }
  .numChoice input{
    width: 20px;
    height: 20px;
    line-height: 16px;
    color: #000;
  }

js:

import Bus from "./../assets/js/bus"    //    和cartList組件引入公共的bug,來作爲中間傳達的工具

// 由於這個組件想寫成公共組件  因此不該該作過多操做  只要加減數量變化,回傳過去就好,其餘根據數量變化發生的變化在列表頁判斷就行
  export default{
    data(){
      return{
        value:this.count,
      }
    },
    props:["gsId","count","stock"],
    methods:{
      add(){
        this.value++;
        if(this.value >= this.stock){
          this.value = this.stock;
        }
        Bus.$emit("fromNumChoice",this.value,this.gsId);  //  數量改變後把發生改變的這一項和數量的變化告訴列表頁
      },
      sub(){
        this.value--;
        if(this.value <= 1){
          this.value = 1;
        }
        Bus.$emit("fromNumChoice",this.value,this.gsId);
      }
    }
  }

 以上就是使用vue組件化的購物車的實現,暫時不上動圖了,有興趣能夠本身搭建環境粘貼賦值實現一下,有問題歡迎提出交流。

相關文章
相關標籤/搜索