Vue+Vant+Vuex实现本地购物车功能
通常,我们做移动端商城的时候,通常会有购物车模块,那购物车模块有两种实现方式,一是储存在后台通过接口获取到购物车信息,二是存在用户手机本地,第一种方法只要调用接口获取比较简单,这里介绍的是第二种方法,利用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:;
}
.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:;
left:;
.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:;
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)
});
},
就先粗略的介绍到这里,有什么问题提出来我及时改正,谢谢!
Vue+Vant+Vuex实现本地购物车功能的更多相关文章
- 利用Vue实现一个简单的购物车功能
开始学习Vue的小白,dalao多多指正 想要实现下面这个界面,包含总价计算.数量增加和移除功能 话不多说代码如下 <!DOCTYPE html> <html> <hea ...
- 基于vue2.0打造移动商城页面实践 vue实现商城购物车功能 基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果
基于vue2.0打造移动商城页面实践 地址:https://www.jianshu.com/p/2129bc4d40e9 vue实现商城购物车功能 地址:http://www.jb51.net/art ...
- vue实战记录(五)- vue实现购物车功能之商品总金额计算和单选全选删除功能
vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(五) GitHub:sue ...
- vue实战记录(六)- vue实现购物车功能之地址列表选配
vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(六) GitHub:sue ...
- vue实战记录(四)- vue实现购物车功能之过滤器的使用
vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(四) GitHub:sue ...
- vue实战记录(三)- vue实现购物车功能之渲染商品列表
vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(三) GitHub:sue ...
- vue实战记录(二)- vue实现购物车功能之创建vue实例
vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(二) GitHub:sue ...
- vue实战记录(一)- vue实现购物车功能之前提准备
vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(一) GitHub:sue ...
- 用Vue来实现购物车功能(二)
这个小demo具有添加商品进购物车 .增加购物车内商品的数量.减少购物车内商品的数量.计算一类商品的总价.以及计算所有商品的总价 首先看目录结构 因为我们的Tab.vue Car.vue 以及Car ...
随机推荐
- Android 开源库StickyListHeadersListView来实现ListView列表分组效果
项目中有一新的需求,要求能像一些Android机带"联系人列表"一样,数据可以自动分组,且在列表滑动过程中,列表头固定在顶部,效果图如下: 下面就带大家实现上面的效果, 首先,我们 ...
- H3C 使用命令视图
- oracle函数 round(x[,y])
[功能]返回四舍五入后的值 [参数]x,y,数字型表达式,如果y不为整数则截取y整数部分,如果y>0则四舍五入为y位小数,如果y小于0则四舍五入到小数点向左第y位. [返回]数字 [示例] se ...
- Logback新版本报no applicable action for [Encoding]问题
logback.xml配置文件如下: <?xml version="1.0" encoding="UTF-8"?> <configuratio ...
- H3C 无线覆盖原则-蜂窝式覆盖
- 数(aqnum)
数(aqnum) 3.1 题目描述 秋锅对数论很感兴趣,他特别喜欢一种数字.秋锅把这种数字命名为 农数 ,英文名为 AQ number . 这种数字定义如下: 定义 1 一个数 n 是农数,当且仅当对 ...
- Redis在Laravel项目中的应用实例详解
https://mp.weixin.qq.com/s/axIgNPZLJDh9VFGVk7oYYA 在初步了解Redis在Laravel中的应用 那么我们试想这样的一个应用场景 一个文章或者帖子的浏览 ...
- 有什么类方法或是函数可以查看某个项目的Laravel版本的?
查看composer.json文件: "require": { "php": ">=7.0.0", "fideloper/p ...
- H3C ping命令的输出
- C语言动态内存
动态分配内存的概述 在数组一章中,介绍过数组的长度是预先定义好的,在整个程序中固定不变,但是在实际的编程中,往往会发生这种情况,即所需内存空间取决于实际输入的数据,而无法预先确定.为了解决上述问题,c ...