通常,我們做移動端商城的時候,通常會有購物車模塊,那購物車模塊有兩種實現方式,一是儲存在后台通過接口獲取到購物車信息,二是存在用戶手機本地,第一種方法只要調用接口獲取比較簡單,這里介紹的是第二種方法,利用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 { width: 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) }); },
就先粗略的介紹到這里,有什么問題提出來我及時改正,謝謝!