最近在學習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組件化的購物車的實現,暫時不上動圖了,有興趣能夠本身搭建環境粘貼賦值實現一下,有問題歡迎提出交流。