从零开始,搭建一个简单的购物平台(十七)前端商城部分
从零开始,搭建一个简单的购物平台(十六)前端商城部分:
https://blog.csdn.net/time_____/article/details/108769229
项目源码(持续更新):https://gitee.com/DieHunter/myCode/tree/master/shopping
上篇文章讲到了商品详情页面实现,最后一个功能是加入购物车,也就是通过列表对比商品是否存在,若存在,则对现有商品数据进行修改,若不存在,则初始化商品,并添加至购物车列表中,这篇文章将介绍对购物车数据进行操作功能
购物车功能包括
修改商品数量 删除单个商品 全选/反选 批量删除
这个页面,将其分解成四个组件,分别是顶部的Title,菜单栏Tabbar,单件商品shopCarItem和下面的批量操作shopCarOrder
修改商品数量在上篇文章中已介绍,通过countShopItem方法进行修改
删除单个商品
delShopItem(_id) {
let _shopCar = this.state//获取现有购物车列表
_shopCar.splice(_id, 1)//数组删除第_id项
this.state = _shopCar//刷新购物车列表
this.$events.emitEvent(EventName.CountShop);//刷新界面
}
全选/反选,这里有两个注意的地方,全选/反选的实现有两种状态,一是每个商品单选被全选或反选后全选按钮也会跟随状态,二是全选按钮全选或反选后所有单选按钮也会跟随状态
我们首先在store的action加一个方法,用于总计商品的全选状态,商品总数,及总价格。每当商品被选中,就触发这个方法,刷新数据
filterSelect() {//修改商品全选,单个商品驱动全选按钮,刷新数据
let shopConfig = {//所有商品总计初始值
_count: 1,//是否全选
_selCount: 0,//商品总数
_sum: 0//商品总价
}
this.state.forEach(item => {
shopConfig._selCount += item.isSelect ? 1 : 0;
shopConfig._count *= item.isSelect;//判断是否全选,初始值0,若全为false,相乘等于0,若全为true,相乘为1,即等于1是全选,等于0是未全选
shopConfig._sum += item.isSelect ? item.sum : 0
});
this.$events.emitEvent(EventName.SelectParent, shopConfig);
}
此外,我们需要再写个方法,用于全选按钮修改各个商品的选中状态
selAllChild(_sel) {//修改商品全选,全选按钮驱动单个商品,刷新数据
this.state = this.state.map(item => {
item.isSelect = _sel;//当全选按钮选中,修改所有商品状态
return item;
});
this.$events.emitEvent(EventName.SelectAllChild);
}
于是在单件商品shopCarItem组件中,我们进行调用action中的方法,修改全局state值
<template>
<div>
<ul v-if="shopList.length" class="shopCar">
<li v-for="(item,index) in shopList" :key="index">
<span class="mint-checkbox" @click="selectHandler(index)">
<input class="mint-checkbox-input" type="checkbox" :checked="item.isSelect" />
<span class="mint-checkbox-core"></span>
</span>
<img :src="imgPath+item.shopPic" alt />
<div class="shopInfo">
<div>
<span>{{item.shopName}} {{item.shopScale}}克</span>
<span>¥{{item.shopPrice}}</span>
</div>
<div>
<mt-button class="minus" type="default" @click="minusShopHandler(item)">-</mt-button>
<span>{{item.shopCount}}</span>
<mt-button class="add" type="default" @click="addShopHandler(item)">+</mt-button>
<mt-button class="del" type="default" @click="delShopHandler(index)">×</mt-button>
</div>
</div>
</li>
</ul>
<div v-else class="noShop">
<div class="icon-jiarugouwuche iconfont"></div>
<span>购物车为空</span>
</div>
</div>
</template>
<script>
import Config from "../../config/config";
import { Toast } from "mint-ui";
const { EventName } = Config;
export default {
name: "shopCarItem",
data() {
return {
shopCar: null,//初始化购物车
shopList: [],//购物车列表state
imgPath: Config.RequestPath,//静态文件根目录
selectAll: false,//全选
};
},
created() {
this.shopCar = new this.$store.ShopCar();
this.shopList = this.shopCar.state;
this.$events.onEvent(EventName.CountShop, this.countHandler);//商品数量监听
this.$events.onEvent(EventName.SelectAllChild, this.selAllHandler);//商品全选监听
},
mounted() {
this.shopCar.filterSelect();//初始化全选,商品数量,商品总价
},
destroyed() {
this.$events.offEvent(EventName.CountShop, this.countHandler);
this.$events.offEvent(EventName.SelectAllChild, this.selAllHandler);
},
methods: {
countHandler(res) {//修改商品数量,刷新数据
this.shopList = this.shopCar.state;
this.shopCar.filterSelect();
},
selectHandler(_id) {//修改商品全选,单个商品驱动全选按钮,刷新数据
this.shopList[_id].isSelect = !this.shopList[_id].isSelect;
this.shopCar.state = this.shopList;
this.shopCar.filterSelect();
},
selAllHandler() {//修改商品全选,全选按钮驱动单个商品,刷新数据
this.shopList = this.shopCar.state;
this.shopCar.filterSelect();
},
addShopHandler(_data) {//添加商品,刷新数据
_data.shopCount = 1;
this.shopCar.countShopItem({
..._data,
});
},
minusShopHandler(_data) {//减少商品,刷新数据
_data.shopCount = -1;
this.shopCar.countShopItem({
..._data,
});
},
delShopHandler(_id) {//删除单个商品,刷新数据
this.shopCar.delShopItem(_id);
},
},
};
</script>
<style lang="less" scoped>
@import "../../style/init.less";
.noShop {
width: 100%;
height: 100%;
text-align: center;
span {
.f_s(36);
}
div {
.w(200);
.h(200);
border-radius: 100%;
background: @mainColor;
.titleFont();
box-shadow: 5px 5px 8px #777;
.l_h(200);
text-align: center;
margin: 100px auto 20px;
.f_s(100);
}
}
.shopCar {
.padbottom(130);
width: 100%;
li {
border-bottom: 1px solid #d3d3d3;
padding-left: unit(35 / @pxtorem, rem);
.h(320);
.mint-checkbox {
.h(320);
.l_h(320);
display: inline-block;
vertical-align: middle;
}
.mint-checkbox-input:checked + .mint-checkbox-core {
background: #ea3e3c;
border-color: #ea3e3c;
}
img {
.h(265);
.w(265);
margin-left: unit(20 / @pxtorem, rem);
display: inline-block;
vertical-align: middle;
background: #f5f6f5;
}
.shopInfo {
.h(235);
width: 50%;
.padtop(30);
padding-left: unit(10 / @pxtorem, rem);
display: inline-block;
vertical-align: middle;
div:nth-child(1) {
overflow: hidden;
span {
float: left;
}
span:nth-child(2) {
float: right;
}
}
div:nth-child(2) {
margin-top: unit(85 / @pxtorem, rem);
span {
display: inline-block;
vertical-align: middle;
padding: 0 unit(50 / @pxtorem, rem);
}
.add,
.minus,
.del {
display: inline-block;
vertical-align: middle;
background: white;
box-shadow: none;
.f_s(50);
}
.del {
float: right;
}
}
}
}
}
</style>
在批量操作shopCarOrder组件中同理
<template>
<div class="shopOrder">
<span class="mint-checkbox" @click="selectHandler">
<input class="mint-checkbox-input" type="checkbox" :checked="isSelAll" />
<span class="mint-checkbox-core"></span>
</span>
<span>全选({{selCount}})</span>
<span @click="delSelShop">删除({{selCount}})</span>
<span>
<span>¥{{sum}}</span>
<span class="icon-qianjin iconfont" @click="sendOrder"></span>
</span>
</div>
</template>
<script>
import Config from "../../config/config";
import ShopCarOrderBussiness from "./bussiness";
import { Toast } from "mint-ui";
const { EventName } = Config;
export default {
name: "shopCarOrder",
data() {
return {
shopCar: null,
isSelAll: false,//全选
selCount: 0,//商品数量
sum: 0,//商品总价
orderList: null,//提交订单请求参数
shopCarOrderBussiness: null,
};
},
created() {
this.shopCar = new this.$store.ShopCar();
this.shopCarOrderBussiness = new ShopCarOrderBussiness(this);
this.$events.onEvent(EventName.SelectParent, this.selAllHandler);//全选按钮监听,通过监听所有商品都选中或未全选,修改状态
},
destroyed() {
this.$events.offEvent(EventName.SelectParent, this.selAllHandler);
},
methods: {
selectHandler() {//驱动修改所有商品选中状态
this.isSelAll = !this.isSelAll;
this.shopCar.selAllChild(this.isSelAll);
},
selAllHandler({ _count, _selCount, _sum }) {
this.isSelAll = _count;
this.selCount = _selCount;
this.sum = _sum;
},
delSelShop() {//删除选中商品
this.shopCar.delSelShop();
},
sendOrder() {//提交订单
this.shopCarOrderBussiness.sendOrderList();
},
},
};
</script>
<style lang="less" scoped>
@import "../../style/init.less";
.shopOrder {
.f_s(34);
color: #fff;
position: fixed;
.mcolor();
bottom: unit(130 / @pxtorem, rem);
width: 100%;
.h(130);
.l_h(130);
> span:nth-child(1),
> span:nth-child(2) {
padding-left: unit(35 / @pxtorem, rem);
}
> span:nth-child(3) {
padding-left: unit(200 / @pxtorem, rem);
}
> span:nth-child(4) {
float: right;
margin-right: unit(50 / @pxtorem, rem);
> span:nth-child(1) {
padding-left: unit(20 / @pxtorem, rem);
border-left: 1px dashed #fff;
}
> span:nth-child(2) {
padding-left: unit(50 / @pxtorem, rem);
}
}
> span {
display: inline-block;
}
.mint-checkbox-input + .mint-checkbox-core {
background: transparent;
border-color: #fff;
}
.mint-checkbox-input:checked + .mint-checkbox-core {
.mcolor();
}
}
</style>
在action中,我们还需要写一个函数,用于删除选中商品
delSelShop() {//直接通过遍历商品选中状态值进行删除,并刷新数据
let _list = []
this.state.map(item => {
if (!item.isSelect) {
_list.push(item)
}
});
this.state = _list
this.$events.emitEvent(EventName.CountShop);
}
最后,在shopCar.vue界面中引入这四个组件,页面实现完成
<template>
<div>
<Top title="购物车"></Top>
<div class="content">
<ShopCarItem></ShopCarItem>
<ShopCarOrder></ShopCarOrder>
</div>
<TabBar></TabBar>
</div>
</template>
<script>
import Top from "../../components/top/top";
import ShopCarItem from "../../components/shopCarItem/shopCarItem";
import ShopCarOrder from "../../components/shopCarOrder/shopCarOrder";
import TabBar from "../../components/tabBar/tabBar";
export default {
name: "shopCar",
data() {
return {};
},
components: {
Top,
ShopCarItem,
ShopCarOrder,
TabBar
},
created() {}
};
</script>
<style lang="less" scoped>
@import "../../style/init.less";
</style>
本篇文章主要对商品购物车操作进行了介绍,其中运用了数据刷新视图的基本操作,将数据,逻辑,视图三者分开,结合vue的数据绑定渲染页面。下篇文章将实现用户的注册,登录功能(简单的用户名密码注册,登录,和邮箱验证模块)