Vue版购物车开发思路教程

来源:程序思维浏览:3682次
Vue是现在最流行的前端框架,电商是最热门的行业,购物车是电商的核心功能,那么如何开发一个扩展性强、易维护的购物车组件呢?下面就给大家介绍一下用Vue开发购物车的思路吧。
 
Vue版购物车开发思路教程

1、进入商品详情页面选择商品规格

进入商品详情页面选择商品规格

选择颜色或尺码,输入购买数量,点击确定进入购物车。
放入购物车的字段如下:
商品的id:gid
商品名称:title
商品图片:img
商品数量:amount
商品运费:freight
商品价格:price
是否选中:checked(值true,false)
商品属性(规格):
[{"attrid":"1034","title":"颜色","param":[{"paramid":"1189","title":"蓝色"}]},{"attrid":"1037","title":"尺码","param":[{"paramid":"1192","title":"37"}]}]

2、vuex和localStorage的作用

Vuex的作用:将购物车的数据存储到vuex中实现组件传值和双向绑定。
localStorage的作用:解决vuex刷新页面数据丢失的问题,将vuex中的数据缓存起来。

vuex和localStorage的作用1

vuex和localStorage的作用2

如上面两张图所示购物车里还没有商品。

3、购物车组件页面源码

cart/index.vue

<!--模板-->
<template>
<div id="cart-index">
<div v-wechat-title="$route.meta.title"></div>
<div id="luck-page" class="luck-page">
<subHeader :navTitle=navTitle></subHeader>
<div id="cart-main" class="cart-main">
<div v-for="(item,index) in cartData" class="item-list">
<div class="select-area">
<label :class="{'check-mark':true,'active':item.checked}" @click="selectCart(''+item.gid+'')"></label>
</div>
<div class="items-wrap">
<div class="goods-image"><img :src="item.img" /></div>
<div class="del" @click="delCart(''+index+'')">删除</div>
<div class="goods-title">{{item.title}}</div>
<div class="goods-param"><span class="attr" v-for="attr in item.attrs">{{attr.title}}:<span class="param" v-for="param in attr.param">{{param.title}}</span></span></div>
<div class="price">¥{{item.price}}</div>
<div class="itemsCount">
<div class="num-wrap">
<div class="minus active" @click="minus(''+item.gid+'',''+item.amount+'',''+item.checked+'')">-</div>
<div class="input-num"><input type="tel" class="amount" name="amount" v-model="item.amount" @keyup="checkAmount()" @blur="changeAmount(''+item.gid+'')" ></div>
<div class="plus" @click="plus(''+item.gid+'',''+item.amount+'',''+item.checked+'')">+</div>
</div>
</div>
</div>
</div>
</div>
<div class="sett-nav">
<div class="sett-left">
<div class="choose" id="all-check">
<label @click="allSelectCart()" >
<div :class="{'check-mark':true,'active':bAllChecked}"></div>
<div class="check-text">全选</div>
</label>
</div>
<div class="total">合计: <span>¥<span id="total">{{priceTotal}}</span></span></div>
</div>
<button type="button" @click="goOrder()" :class="{'sett-right':true,'disable':!bAllChecked}">去结算</button>
</div>
</div>
</div>
</template>
 
<!--行为-->
<script>
import subHeader from '../../common/page/sub-header'
export default {
name: 'cart-index',
data:function(){
return {
cartData:this.$store.state.cartData.length>0?this.$store.state.cartData:localStorage.cart!=undefined?JSON.parse(localStorage.cart):[],
priceTotal:this.$store.state.priceTotal>0?this.$store.state.priceTotal:localStorage.priceTotal!=undefined?localStorage.priceTotal:0,
bChecked:true,
bAllChecked:true,
navTitle:"购物车"
}
},
components:{
subHeader
},
methods:{
back(){
this.$router.go(-1);
},
//点击选择
selectCart(pGid){
if(this.bChecked){
this.bChecked=false;
}else{
this.bChecked=true;
}
this.$store.commit("checkedCartData", {gid: pGid, checked: this.bChecked});
this.priceTotal=this.$store.state.priceTotal;
this.setAllChecked();
},
//点击全择
allSelectCart(){
if(this.cartData.length>0) {
if (this.bAllChecked == true) {
this.bAllChecked = false;
} else {
this.bAllChecked = true;
}
this.$store.commit("allCheckedCartData", this.bAllChecked);
this.priceTotal = this.$store.state.priceTotal;
}
},
//点击减号
minus(pGid,pAmount,pChecked){
if(pChecked=='true') {
let iAmount = parseInt(pAmount);
if (iAmount <= 1) {
iAmount = 1;
} else {
iAmount -= 1;
}
this.$store.commit("changeAmount", {gid: pGid, amount: iAmount});
this.priceTotal = this.$store.state.priceTotal;
}
},
//点击加号
plus(pGid,pAmount,pChecked){
if(pChecked=='true') {
let iAmount = parseInt(pAmount);
iAmount += 1;
this.$store.commit("changeAmount", {gid: pGid, amount: iAmount});
this.priceTotal = this.$store.state.priceTotal;
}
},
//检测输入的数量是否规范
checkAmount(){
for(var key in this.cartData){
if(isNaN(this.cartData[key].amount)){
this.cartData[key].amount=this.cartData[key].amount.replace(/[a-zA-Z]|[\u4e00-\u9fa5]/g,'')
this.bSubmitAmount=false;
break;
}else if(this.cartData[key].amount<=0){
this.cartData[key].amount=1;
break;
} else{
this.bSubmitAmount=true;
}
}
},
//修改数量
changeAmount(pGid){
if(this.bSubmitAmount) {
for(let key in this.cartData){
if(this.cartData[key].gid==pGid){
if(this.cartData[key].checked==true){
this.$store.commit("changeAmount");
this.priceTotal = this.$store.state.priceTotal;
}
break;
}
}
}
},
//删除宝贝
delCart(pIndex){
this.$store.commit("delCart", {index: pIndex});
this.priceTotal = this.$store.state.priceTotal;
this.setAllChecked();
},
setAllChecked(){
if(this.priceTotal>0) {
this.bAllChecked = true;
}else{
this.bAllChecked = false;
}
},
goOrder(){
if(this.priceTotal>0) {
this.$router.push({path:this.config.path+ 'order/index'});
}
}
},
mounted:function(){
if(this.priceTotal>0){
this.bAllChecked=true;
}else{
this.bAllChecked=false;
}
},
created:function(){
this.bSubmitAmount=true;
this.cartData=this.$store.state.cartData.length>0?this.$store.state.cartData:localStorage.cart!=undefined?JSON.parse(localStorage.cart):[]
}
}
</script>
 
<!--样式-->
<style scoped>
.cart-main{width:100%;margin-top:2.5rem;padding-bottom:2.5rem;background:#FFFFFF;}
.cart-main .item-list{width:100%;height:6rem;background:#FFFFFF;border-bottom:#EFEFEF solid 2px;overflow:hidden;}
.cart-main .select-area{width:10%;height:100%;float:left;position:relative;}
.cart-main .select-area .check-mark{width:0.8rem;height:0.8rem;border:#EFEFEF solid 1px;border-radius:100%;position:absolute;z-index:1;top:43%;left:15%;}
.cart-main .select-area .check-mark input{opacity:0;width:100%;height:100%;}
.cart-main .select-area .check-mark.active{background-image:url("../../../assets/images/home/cart/checkmark.png");background-size:100%;background-repeat:no-repeat;background-position:center;border:none 0px;}
.cart-main .items-wrap{width:90%;height:100%;float:left;position:relative;}
.cart-main .items-wrap .goods-image{width:3rem;height:3rem;position:absolute;z-index:1;left:0px;top:25%;}
.cart-main .items-wrap .goods-image img{width:100%;}
.cart-main .items-wrap .goods-title{width: 70%;height: 1.5rem;font-size: 0.6rem; position: absolute;z-index: 1;left:25%;top: 10%;overflow: hidden;line-height: 0.8rem;text-align:left;}
.cart-main .items-wrap .goods-param{width: 70%;height: auto;font-size: 0.6rem; position: absolute;z-index: 1;left:25%;top: 38%;overflow: hidden;text-align:left;white-space:nowrap;text-overflow:ellipsis;color:#B5B5B5;}
.cart-main .items-wrap .goods-param .attr{margin-right:10px;}
.cart-main .items-wrap .goods-param .param{margin-right:5px;}
.cart-main .items-wrap .price{width:30%;height:auto;position:absolute;z-index:1;left:25%;top:65%;color:#CC0004;font-size:0.7rem;}
.cart-main .items-wrap .itemsCount{width:30%;height:auto;position:absolute;z-index:1;left:65%;top:65%;}
.cart-main .items-wrap .itemsCount .num-wrap{width:5rem;height:1.2rem;border:solid 1px #000000;border-radius:3px;position:absolute;z-index:1;right:5%;top:0%;overflow:hidden;}
.cart-main .items-wrap .itemsCount .num-wrap .minus{width:1.5rem;height:0.8rem;border-right:#B5B5B5 solid 1px;margin-top:4%;color:#B5B5B5;text-align:center;line-height:0.8rem;float:left;}
.cart-main .items-wrap .itemsCount .num-wrap .minus.active{color:#000000;border-right:#000000 solid 1px;}
.cart-main .items-wrap .itemsCount .num-wrap .plus{width:1.5rem;height:0.8rem;border-left:#000000 solid 1px;margin-top:4%;color:#000000;text-align:center;line-height:0.8rem;float:left;}
.cart-main .items-wrap .itemsCount .input-num{width:1.9rem;height:auto;float:left;margin-top:-0.2rem;}
.cart-main .items-wrap .itemsCount .input-num .amount{width:100%;font-size:0.7rem;border:none 0px;outline:none;-webkit-appearance:none;text-align:center;}
.cart-main .items-wrap .del{width:auto;height:auto;position:absolute;z-index:1;left:1rem;top:83%;font-size: 0.6rem;color:#B5B5B5;}
 
.sett-nav{width:100%;height:2.5rem;position:fixed;z-index:5;bottom:0px;border-top:solid #EFEFEF 1px;background:#FFFFFF;}
.sett-left{width:70%;height:100%;position:relative;float:left;}
.sett-nav .sett-left .choose{width:5rem;height:auto;position:absolute;z-index:5;left:5%;top:30%;}
.sett-nav .sett-left .choose .check-mark{width:0.8rem;height:0.8rem;border:#EFEFEF solid 1px;border-radius:100%;}
.sett-nav .sett-left .choose .check-mark.active{background-image:url("../../../assets/images/home/cart/checkmark.png");background-size:100%;background-repeat:no-repeat;background-position:center;border:none 0px;}
.sett-nav .sett-left .choose .check-text{width:2rem;height:auto;position:absolute;z-index:5;left:25%;top:15%;font-size:0.6rem;}
.sett-nav .sett-left .total{width:auto;height:auto;position:absolute;right:4%;top:33%;z-index:5;font-size:0.6rem;}
.sett-nav .sett-left .total span{color:#CC0004}
.sett-nav .sett-right{width:30%;height:100%;background:#CC0004;text-align:center;line-height:2.5rem;float:left;color:#FFFFFF;font-size:0.8rem;border-radius: 0px;}
.sett-nav .sett-right.disable{background:#BFBFBF;}
</style>

4、添加购物车

当我点击加入购物车后将数据存储到vuex和localStorage中, 
代码如下: 
main.js里面的代码 
import state from './store/state.js'
import mutations from './store/mutations.js' 
let store=new Vuex.Store({
  state,
  mutations
});
 
state.js里面的代码 
export default {
  //这里设置初始值的作用是:当浏览器关闭,再次打开时获取缓存数据。 
//购物车的json数据,如果localStorage.cart里面有数据就读取缓存中的数据,否则设置初始值为:[]
 
cartData:localStorage.cart!=undefined?JSON.parse(localStorage.cart):[],
 
//缓存中有商品总价就读取,否则设置初始值为:0
 priceTotal:localStorage.priceTotal!=undefined?localStorage.priceTotal:0,
 
//缓存中有运费就读取,否则设置初始值为:0
 freightTotal:localStorage.freightTotal!=undefined?localStorage.freightTotal:0
 } 
 
添加购物车的方法写在mutations.js里面 
 
export default {
   addCartData(state,data) {//添加购物车
   state.cartData=localStorage.cart!=undefined?JSON.parse(localStorage.cart):[];
   state.priceTotal=parseFloat(localStorage.priceTotal).toFixed(2);
   state.freightTotal=parseFloat(localStorage.freightTotal).toFixed(2);
   if(data.gid!='' && data.gid>0) {
     let existGid=false,priceTotal=0,freightTotal=0,aFreight=[];
     for(let i=0;i<state.cartData.length;i++) {
       //如果购物车商品有重复修改数量和价格
       if (data.gid == state.cartData[i].gid) {
         state.cartData[i].amount++;
         state.cartData[i].price+=state.cartData[i].price;
         state.cartData[i].attrs=data.attrs;
         existGid=true;
         break;
       }
     }
     //增加购物车商品
     if(existGid==false){
       state.cartData.push(data);
     }
     //计算总价
     for(let i=0;i<state.cartData.length;i++) {
       priceTotal += (state.cartData[i].price * state.cartData[i].amount);
       aFreight.push(state.cartData[i].freight);
     }
     freightTotal=Math.max.apply(null,aFreight);
     //console.log(JSON.stringify(state.cartData))
     state.priceTotal=priceTotal.toFixed(2);
     state.freightTotal=freightTotal.toFixed(2)
     localStorage.cart=JSON.stringify(state.cartData);
     localStorage.priceTotal=priceTotal.toFixed(2);
     localStorage.freightTotal=freightTotal.toFixed(2);
   }
 }
 
}
 
这样购物车里就增加了一个商品,如果是相同的商品数量加1,更改价格,如果是不同的商品购物车里会出现新的商品。如图: 

购物车页面

5、更改数量

进入购物车页面修改商品的数量。 
细节注意:如果输入的数量是字符或是汉字需自动消除掉,如果输入的数量是0,自动变为1。 
更改数量的代码写在mutations.js里面 
代码如下: 
 
       changeAmount(state,data){//更改数量
   let priceTotal=0;
   if(data) {
     for (let i = 0; i < state.cartData.length; i++) {
       if (state.cartData[i].gid == data.gid) {
         state.cartData[i].amount = data.amount;
         break;
       }
     }
   }
   for(let i=0;i<state.cartData.length;i++) {
       priceTotal += (state.cartData[i].price * state.cartData[i].amount);
   }
   state.priceTotal=priceTotal.toFixed(2);
    localStorage.priceTotal=priceTotal.toFixed(2);
   localStorage.cart=JSON.stringify(state.cartData);
 }

6、删除购物车里的商品

删除购物车的代码写在mutations.js里面

代码如下:

delCart(state,data){//删除购物车里的宝贝
  let priceTotal=0,freightTotal=0,aFreight=[];
  state.cartData.splice(data.index,1);
  for(let i=0;i<state.cartData.length;i++) {
    priceTotal += (state.cartData[i].price * state.cartData[i].amount);
    aFreight.push(state.cartData[i].freight);
  }
  freightTotal=Math.max.apply(null,aFreight);
  state.priceTotal=priceTotal.toFixed(2);
  state.freightTotal=freightTotal.toFixed(2);
  localStorage.priceTotal=priceTotal.toFixed(2);
  localStorage.cart=JSON.stringify(state.cartData);
  localStorage.freightTotal=freightTotal.toFixed(2);
}

7、勾选购物车里面的商品

如图:

勾选购物车里面的商品

细节注意:未勾选时,合计总价要减去未选中是商品价格,当全部未选中时,全选要自动设置成未勾选状态,去结算按钮不能点击,并且要置灰,未选中的商品不能点击+-号,且输入数量不能计算价格。

如图:

购物车未选中商品页面

勾选的代码写在mutations.js里面

代码如下:

勾选购物车里面的商品

checkedCartData(state,data){
  let priceTotal=0;
  for(let i=0;i<state.cartData.length;i++){
    if(data.gid==state.cartData[i].gid){
      state.cartData[i].checked=data.checked;
      break;
    }
  }
  for(let i=0;i<state.cartData.length;i++){
    if(state.cartData[i].checked==true) {
      priceTotal += (state.cartData[i].price * state.cartData[i].amount);
    }
  }
  state.priceTotal=priceTotal.toFixed(2);
  localStorage.cart=JSON.stringify(state.cartData);
  localStorage.priceTotal=priceTotal.toFixed(2);
}

8、全选与反选购物车里面的商品

全选如图:

全选商品

反选如图:

反选商品

细节注意:当全选按钮未选中时,合计总价为0,去结算按钮不能点击,并且要置灰,未选中的商品不能点击+-号,且输入数量不能计算价格。

最后存储到vuex中的 cartData json数据为:

[{"gid":"747503321","title":"波司登加绒加厚牛仔裤女秋冬2016新款个性百搭外穿宽松复古小脚裤","amount":10,"price":158,"img":"http://vueshop.glbuys.com/uploadfiles/1484288933.jpg","checked":true,"freight":0,"attrs":[{"attrid":"1011","title":"颜色","param":[{"paramid":"917","title":"红色"}]},{"attrid":"1012","title":"腰围","param":[{"paramid":"920","title":"12cm"}]}]},{"gid":"981541541","title":"欧美尖头蝴蝶结拖鞋女夏外穿2018新款绸缎面细跟凉拖半拖鞋穆勒鞋","amount":3,"price":1020,"img":"http://vueshop.glbuys.com/uploadfiles/1524556315.jpg","checked":true,"freight":20,"attrs":[{"attrid":"1034","title":"颜色","param":[{"paramid":"1189","title":"蓝色"}]},{"attrid":"1037","title":"尺码","param":[{"paramid":"1192","title":"37"}]}]},{"gid":"767430600","title":"雪兰黛2018春季新款高跟鞋尖头细跟性感鞋子女韩版透气纱网女单鞋 ","amount":1,"price":280,"img":"http://vueshop.glbuys.com/uploadfiles/1524556026.jpg","checked":true,"freight":10,"attrs":[{"attrid":"1034","title":"颜色","param":[{"paramid":"1170","title":"黑白"}]},{"attrid":"1037","title":"尺码","param":[{"paramid":"1174","title":"36"}]}]}]
精品好课
HTML5视频播放器video开发教程
适用人群1、有html基础2、有css基础3、有javascript基础课程概述手把手教你如何开发属于自己的HTML5视频播放器,利用mp4转成m3u8格式的视频,并在移动端和PC端进行播放支持m3u8直播格式,兼容...
VUE2+VUE3视频教程从入门到精通(全网最全的Vue课程)
VUE是目前最火的前端框架之一,就业薪资很高,本课程教您如何快速学会VUE+ES6并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习VUE高薪就...
jQuery视频教程从入门到精通
jquery视频教程从入门到精通,课程主要包含:jquery选择器、jquery事件、jquery文档操作、动画、Ajax、jquery插件的制作、jquery下拉无限加载插件的制作等等......
Vue2+Vue3+ES6+TS+Uni-app开发微信小程序从入门到实战视频教程
2021年最新Vue2+Vue3+ES6+TypeScript和uni-app开发微信小程序从入门到实战视频教程,本课程教你如何快速学会VUE和uni-app并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己...
最新完整React视频教程从入门到精通纯干货纯实战
React是目前最火的前端框架,就业薪资很高,本课程教您如何快速学会React并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习React高薪就...
React实战视频教程仿京东移动端电商
React是前端最火的框架之一,就业薪资很高,本课程教您如何快速学会React并应用到实战,对正在工作当中或打算学习React高薪就业的你来说,那么这门课程便是你手中的葵花宝典。
最新完整React+VUE视频教程从入门到精,企业级实战项目
React和VUE是目前最火的前端框架,就业薪资很高,本课程教您如何快速学会React和VUE并应用到实战,教你如何解决内存泄漏,常用库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习Re...
HTML5基础入门视频教程易学必会
HTML5基础入门视频教程,教学思路清晰,简单易学必会。适合人群:创业者,只要会打字,对互联网编程感兴趣都可以学。课程概述:该课程主要讲解HTML(学习HTML5的必备基础语言)、CSS3、Javascript(学习...
收藏
扫一扫关注我们