通常,我们做移动端商城的时候,通常会有购物车模块,那购物车模块有两种实现方式,一是储存在后台通过接口获取到购物车信息,二是存在用户手机本地,第一种方法只要调用接口获取比较简单,这里介绍的是第二种方法,利用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实现本地购物车功能的更多相关文章

  1. 利用Vue实现一个简单的购物车功能

    开始学习Vue的小白,dalao多多指正 想要实现下面这个界面,包含总价计算.数量增加和移除功能 话不多说代码如下 <!DOCTYPE html> <html> <hea ...

  2. 基于vue2.0打造移动商城页面实践 vue实现商城购物车功能 基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果

    基于vue2.0打造移动商城页面实践 地址:https://www.jianshu.com/p/2129bc4d40e9 vue实现商城购物车功能 地址:http://www.jb51.net/art ...

  3. vue实战记录(五)- vue实现购物车功能之商品总金额计算和单选全选删除功能

    vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(五) GitHub:sue ...

  4. vue实战记录(六)- vue实现购物车功能之地址列表选配

    vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(六) GitHub:sue ...

  5. vue实战记录(四)- vue实现购物车功能之过滤器的使用

    vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(四) GitHub:sue ...

  6. vue实战记录(三)- vue实现购物车功能之渲染商品列表

    vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(三) GitHub:sue ...

  7. vue实战记录(二)- vue实现购物车功能之创建vue实例

    vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(二) GitHub:sue ...

  8. vue实战记录(一)- vue实现购物车功能之前提准备

    vue实战,一步步实现vue购物车功能的过程记录,课程与素材来自慕课网,自己搭建了express本地服务器来请求数据 作者:狐狸家的鱼 本文链接:vue实战-实现购物车功能(一) GitHub:sue ...

  9. 用Vue来实现购物车功能(二)

    这个小demo具有添加商品进购物车 .增加购物车内商品的数量.减少购物车内商品的数量.计算一类商品的总价.以及计算所有商品的总价 首先看目录结构 因为我们的Tab.vue  Car.vue 以及Car ...

随机推荐

  1. Spring读取配置文件,地址问题,绝对路径,相对路径

    Spring在读取配置文件时,是相对于bin,或者WEB-INF的: “applicationContext.xml”就是找bin或WEB-INF及子文件夹下的文件: “/res/applicatio ...

  2. 原生js实现最简单的瀑布流布局

    文章地址 https://www.cnblogs.com/sandraryan/ 瀑布流:瀑布流,又称瀑布流式布局.是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动, ...

  3. Java反射机制(一):认识Class类

    一. 认识Class类 1.1 正常我们再使用一个类时,大多情况是先获取类的对象,然后通过对象去操作类中的属性或方法. 那,大家有没有想过,如果我们已经有了一个类的对象,我能否通过该对象去获取到类的信 ...

  4. .net Framework 源代码 · ScrollViewer

    本文是分析 .net Framework 源代码的系列,主要告诉大家微软做 ScrollViewer 的思路,分析很简单. 看完本文,可以学会如何写一个 ScrollViewer ,如何定义一个 IS ...

  5. H3C 配置PAP验证

  6. C# 配置文件存储 各种序列化算法性能比较

    本文比较多个方式进行配置文件的存储,对比各个不同算法的读写性能. 在应用软件启动的时候,需要读取配置文件,但是启动的性能很重要,所以需要有一个很快的读取配置文件的方法. 如果你不想看过程,那么请看拖动 ...

  7. codeforce 380(div.2)

    A B 略 C:二分,贪心 设d(i, v)为 剩余油量为v时,车开距离i 所需要的最小时间,使用线性规划不难算出: if v < i return INF; //无法到达 if v > ...

  8. LA 3942 ——Trie (前缀树)、DP

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  9. zoj 4124 "Median" (思维?假的图论?)

    传送门 来源:2019 年“浪潮杯”第十届山东省 ACM 省赛 题意: 对于一个包含n个数的(n为奇数)序列val[ ],排序后的 val[ (n+1) / 2 ] 定义为 median: 有 n 个 ...

  10. flex布局简单兼容性写法

    /* 定义 */ .flex-def { display: -webkit-box; /* 老版本语法: Safari, iOS, Android browser, older WebKit brow ...