通常,我们做移动端商城的时候,通常会有购物车模块,那购物车模块有两种实现方式,一是储存在后台通过接口获取到购物车信息,二是存在用户手机本地,第一种方法只要调用接口获取比较简单,这里介绍的是第二种方法,利用vuex将购物车数据存在本地的方法。
vue项目创建方法和vuex的写法之前博文都有介绍,这里就不再重复了;
vant安装:
# 通过 npm 安装
npm i vant -S
# 通过 yarn 安装
yarn add vant
具体使用方法请去它的官网了解
地址:https://youzan.github.io/vant/#/zh-CN/
购物车页面编写和本地的价格计算:
1.我们把vant按需引入,我们用到了Icon, Checkbox, Stepper, SubmitBar, Toast这些组件;
2.为了方便复制即用,我在本地写了一些购物车死数据goods用于渲染;
3.虽说是把商品信息购物车存在本地,实际上我们只需要存商品id和商品数量num;
4.我们需要通过单选的change事件,全选事件,步进器增加减少进行价格换算;
为了方便复制粘贴直接看效果,直接上demo
demo.html
<template> <div class="shopCart"> <div class="cartList"> <ul v-if="goods.length > 0"> <li v-for="item in goods" :key="item.id"> <van-checkbox :value="item.id" v-model="item.isChecked" checked-color="#15C481" @click="chooseChange(item.id, item)" ></van-checkbox> <div class="shopdetail"> <div class="detailimg"> <img :src="item.thumb" /> </div> <div class="detailtext"> <div class="shoptitle van-multi-ellipsis--l2"> {{ item.title }} </div> <div class="shoppricenum"> <p class="shopprice"> ¥{{ item.price }}{{ item.lvd > 0 ? "+" + item.lvd + "LVD" : "" }} </p> <div class="shopnum"> <van-stepper v-model="item.num" @change="onChange(item)" /> </div> </div> </div> </div> </li> </ul> <div class="nohaveshop" v-else> <van-icon name="shopping-cart-o" /> <p class="p1">你的购物车空空如也~~</p> <p class="p2">快去采购吧!</p> </div> </div> <div class="cartfotter" v-if="goods.length > 0"> <van-submit-bar button-text="去结算" @submit="onSubmit"> <van-checkbox v-model="allchecked" checked-color="#15C481" @click="checkAll" >全选</van-checkbox > <div class="buyprice"> <p class="p1">合计</p> <p class="p2"> ¥{{ totalprice }}{{ totallvd > 0 ? "+" + totallvd + "LVD" : "" }} </p> </div> </van-submit-bar> </div> </div> </template> <script> import { Icon, Checkbox, Stepper, SubmitBar, Toast } from "vant"; export default { components: { [Icon.name]: Icon, [Checkbox.name]: Checkbox, [Stepper.name]: Stepper, [SubmitBar.name]: SubmitBar, [Toast.name]: Toast }, data() { return { goods: [ { id: 1, title: "Zoneid 2019 新款羊羔绒蓝白拼接立领夹克男 9.9成新", price: 980, lvd: 12, num: 1, thumb: "https://img.yzcdn.cn/public_files/2017/10/24/2f9a36046449dafb8608e99990b3c205.jpeg" }, { id: 2, title: "青春女装复古时尚保暖拼接翻领夹克", price: 1200, lvd: 0, num: 2, thumb: "https://img.yzcdn.cn/public_files/2017/10/24/f6aabd6ac5521195e01e8e89ee9fc63f.jpeg" }, { id: 3, title: "成熟男装新款西装立体拼接立领夹克和正装", price: 1000, lvd: 8, num: 1, thumb: "https://img.yzcdn.cn/public_files/2017/10/24/320454216bbe9e25c7651e1fa51b31fd.jpeg" } ], allchecked: false, selectedData: [], // 总价 totalprice: 0, totallvd: 0 }; }, created: function() { this.count(); }, computed: {}, methods: { // 单选的change事件 chooseChange(i, item) { Toast(i); if (this.selectedData.indexOf(i) > -1) { console.log(i); var arrs = this.selectedData.filter(function(item) { return item != i; }); this.selectedData = arrs; item.isChecked = false; // this.remove(this.selectedData, i); this.count(); console.log(this.selectedData); } else { this.selectedData.push(i); item.isChecked = true; this.count(); } if (this.selectedData.length < this.goods.length) { this.allchecked = false; } else { this.allchecked = true; } this.count(); console.log(this.selectedData); }, // 商品数量 onChange(item) { Toast(item.num); this.count(); console.log(this.goods); }, // 计算价格 count: function() { var totalPrice = 0; //临时总价 var totalLvd = 0; //临时lvd this.goods.forEach(function(val) { if (val.isChecked) { totalPrice += val.num * val.price; //累计总价 totalLvd += val.num * val.lvd; //累计lvd } }); this.totalprice = totalPrice; this.totallvd = totalLvd; }, // 全选 checkAll() { let list = this.goods; if (this.allchecked === true) { list.forEach(element => { element.isChecked = false; }); this.selectedData = []; this.count(); console.log("111" + this.selectedData); } else { list.forEach(element => { element.isChecked = true; if (this.selectedData.indexOf(element.id) < 0) { this.selectedData.push(element.id); } }); this.count(); console.log("222" + this.selectedData); } }, // 去结算 onSubmit() { // 选择购买的商品 var cartgoods = []; this.goods.forEach(function(item) { if (item.isChecked) { cartgoods.push({ id: item.id, num: item.num }); } }); if (cartgoods.length === 0) { Toast("请选择商品购买"); } else { this.$router.push("shopBuy"); } console.log(cartgoods); } } }; </script> <style lang="scss" scoped> .shopCart { width: 100%; min-height: 100vh; display: flex; flex-direction: column; background-color: #f6f6f6; .cartList { width: 100%; display: flex; flex-direction: column; align-items: center; margin-top: 16px; ul { width: 100%; display: flex; flex-direction: column; align-items: center; margin-bottom: 100px; li { width: 100%; height: 96px; background-color: #fff; display: flex; flex-direction: row; align-items: center; margin-bottom: 12px; .van-checkbox { margin-left: 17px; ::v-deep .van-checkbox__icon { height: 14px; line-height: 14px; .van-icon { width: 14px; height: 14px; font-size: 12px; border: 1px solid #a5a5a5; } } } .shopdetail { display: flex; flex-direction: row; align-items: center; margin-left: 13px; .detailimg { width: 64px; height: 64px; background: rgba(165, 165, 165, 1); border-radius: 4px; img { width: 100%; height: 100%; border-radius: 4px; } } .detailtext { width: 230px; height: 60px; display: flex; flex-direction: column; margin-left: 8px; position: relative; .shoptitle { width: 180px; text-align: justify; font-size: 12px; color: #212121; line-height: 17px; } .shoppricenum { width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: space-between; position: absolute; bottom: 0px; .shopprice { font-size: 12px; color: #15c481; font-weight: 600; } .shopnum { display: flex; ::v-deep .van-stepper { button { 14px; height: 14px; border: 1px solid #333333; border-radius: 50px; background-color: #fff; } .van-stepper__minus::before { width: 8px; } .van-stepper__plus::before { width: 8px; } .van-stepper__plus::after { height: 8px; } .van-stepper__input { font-size: 12px; color: #333333; background-color: #fff; padding: 0px 12px; } } } } } } } } .nohaveshop { display: flex; flex-direction: column; align-items: center; margin-top: 100px; .van-icon { font-size: 60px; color: #666; } p { font-size: 14px; color: #999; } .p1 { margin-top: 20px; } } } .cartfotter { width: 100%; height: 60px; position: fixed; bottom: 0; left: 0; .van-submit-bar__bar { height: 60px; font-size: 16px; .van-checkbox { margin-left: 17px; ::v-deep .van-checkbox__icon { height: 14px; line-height: 14px; .van-icon { width: 14px; height: 14px; font-size: 12px; border: 1px solid #a5a5a5; } } ::v-deep .van-checkbox__label { font-size: 16px; color: #212121; margin-left: 9px; } } .buyprice { flex: 1; padding-right: 8px; text-align: right; display: flex; flex-direction: column; .p1 { font-size: 10px; color: #001410; } .p2 { font-size: 12px; color: #15c481; margin-top: 4px; } } .van-button--danger { width: 130px; height: 60px; background: rgba(21, 196, 129, 1); border: none; font-size: 16px; color: #ffffff; } } } } </style>
上面代码没用到vuex,目的是方便复制在进去就能直接运行,上面代码同样适用于接口获取购物车的情况,进行本地价格计算,vuex我在下面分开介绍,按需添加进去;
store下的index.js
import Vue from "vue"; import Vuex from "vuex"; import app from "@/store/module/cart"; Vue.use(Vuex); export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules: { cart } });
上方代码我们创建了一个cart,专门用于购物车模块;
cart.js
export default { state: { // 购物车 cartGoods: [] }, getters: { cartGoodIds(state) { return state.cartGoods.map(item => item.productId); } }, mutations: { INIT_CART(state) { if (localStorage && localStorage.getItem("cartGoods")) { const goods = JSON.parse(localStorage.getItem("cartGoods")); state.cartGoods = goods; } }, ADD_TO_CART(state, addGood) { const findGood = state.cartGoods.find( item => item.productId === addGood.productId ); if (findGood) { findGood.num = findGood.num + addGood.num; } else { state.cartGoods.push(addGood); } if (localStorage) { localStorage.setItem("cartGoods", JSON.stringify(state.cartGoods)); } }, INPUT_TO_CART(state, inpGood) { const findGood = state.cartGoods.find( item => item.productId === inpGood.productId ); const largeGood = state.cartGoods.find(item => item.num > inpGood.num); const equalGood = state.cartGoods.find(item => item.num == inpGood.num); const smallGood = state.cartGoods.find(item => item.num < inpGood.num); if (findGood && largeGood) { findGood.num = findGood.num - (findGood.num - inpGood.num); } if (findGood && equalGood) { findGood.num = inpGood.num; } if (findGood && smallGood) { findGood.num = inpGood.num; } if (localStorage) { localStorage.setItem("cartGoods", JSON.stringify(state.cartGoods)); } } } };
上方我们定义了一些计算方法和事件方法,来在其他页面调用时改变购物车储存的信息;
其他页面:
// 导入 import { mapGetters, mapMutations } from "vuex"; // 计算 computed: { ...mapGetters(["cartGoodIds"]) }, // 方法 methods: { ...mapMutations(["ADD_TO_CART", "INPUT_TO_CART", "INIT_CART"]), } // 使用 // 增加 // 增加 plusadd(item) { this.$store.commit("ADD_TO_CART", { productId: item.id, num: 1 }); }, // 减少 minuscut(item) { this.$store.commit("ADD_TO_CART", { productId: item.id, num: -1 }); }, // 输入 blurinput(item) { this.$store.commit("INPUT_TO_CART", { productId: item.id, num: parseInt(item.num) }); },
就先粗略的介绍到这里,有什么问题提出来我及时改正,谢谢!