效果:增加数量不做限制,减少数量到0的时候从购物车中删除商品。
购物车数据结构:
html
<!-- 单向联动菜单 -->
<view class="scroll-box" :style="{ 'height': scrollHeight +'px' }">
<!-- 左边栏 -->
<view class="left-tab">
<scroll-view class="tab" scroll-y="true" scroll-with-animation >
<block v-for="(item,index) in tabData" :key="index" >
<!-- 标签动态样式 -->
<view class="tab-item" :class=" [currentTab==index ? 'active' : 'none'] " @click="clickTab"
:data-current="index" :data-title="[item.title]" >
{{item.title}}
</view>
</block>
</scroll-view>
</view>
<!-- 右边页面 -->
<scroll-view class="right-box" scroll-y="true" :scroll-top="scrollTop" @scroll="scroll" >
<view class="page-view">
<view class="class-item">
<view class="item-title">
<text>{{goodsTitle}}</text>
</view>
<view class="item-container">
<view class="thumb-box" v-for="(item, index) in menuData" :key="index">
<view class="left-image">
<image class="item-menu-image" :src="item.imgUrl" mode=""></image>
</view>
<view class="right-text">
<view class="item-menu-name">{{item.name}}</view>
<view class="item-menu-explain">{{item.explain}}</view>
<view class="item-menu-ps">
<view class="item-menu-price">¥{{item.price}}</view>
<!-- Animate动画库使用 https://blog.csdn.net/qq_40976321/article/details/107379659 -->
<view class="item-menu-select" hover-class="animated rotateOut" @click="addCar" :data-item="[item]">加购</view>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 底部购物车 -->
<view class="car" v-if="isShowCar">
<view class="car-left">
<view class="car-pice">¥{{totalPrice}}</view>
</view>
<view class="car-right">
<view class="car-text">选好了</view>
</view>
</view>
<!-- 凸起图标 -->
<view class="car-img-back" v-if="isShowCar" @click="clickCar">
<image class="car-img" src="../../../static/icon/png/bag.png" mode=""></image>
<!-- 角标 -->
<view class="car-num">
<text class="car-num-text">{{totalNum}}</text>
</view>
</view>
<!-- 购物车列表 -->
<view class="car-list" v-if="isShowList">
<scroll-view scroll-y="true" style="height: 500rpx;">
<view class="car-box" v-for="(item, index) in carData" :key="index">
<view class="car-left-image" style="width: 23%;">
<image class="car-menu-image" :src="item.imgUrl" mode=""></image>
</view>
<view class="car-right-text" style="width: 73%;">
<view class="car-menu-name">{{item.name}}</view>
<view class="car-menu-explain">{{item.explain}}</view>
<view class="car-menu-ps">
<view class="car-menu-price" >¥{{item.price}}</view>
<view class="car-num-select" >
<view @click="clickMinus(index)">
<image style="width: 48rpx;height: 48rpx;" src="../../../static/icon/png/jian.png" ></image>
</view>
<view style="margin: 0rpx 15rpx;font-size: 28rpx;"> {{item.num}} </view>
<view @click="clickAdd(index)">
<image style="width: 48rpx;height: 48rpx;" src="../../../static/icon/png/jia.png" ></image>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 遮罩层 -->
<view class="car-mark" v-if="isShowCarMark" @click="clickCarMark"></view>
js
//点击左边类别栏事件
clickTab(e) {
let clickIndex = e.target.dataset.current //当前标签的索引
this.currentTab = clickIndex
let tapTitle = e.target.dataset.title //获得点击的标题
this.goodsTitle = tapTitle[0]
//对数组进行筛选,让右边的列表只显示对应类别的数据
this.menuData = this.tempData.filter(item => item.title == this.goodsTitle)
//滚动条返回顶部
this.scrollTop = 0
},
//记录滚动条位置
scroll : function(e) {
this.scrollTop = e.detail.scrollTop
},
//显示、隐藏购物车清单
clickCar(){
this.isShowList = !this.isShowList
this.isShowCarMark = !this.isShowCarMark
},
clickCarMark(){
this.isShowList = !this.isShowList
this.isShowCarMark = !this.isShowCarMark
},
//**********商品加入购物车 ***************
addCar(e){
//获得点击加购的商品数据
let item = e.target.dataset.item[0]
//如果购物车里的商品name = 点击数据商品name 则返回此元素下标
let index = this.carData.findIndex(ev => ev.name === item.name)
//如果购物车不存在相同的商品
if(index === -1) {
item.num = 1 //添加数量属性num ,默认为 1【原数据没有数量字段】
this.carData.push(item) //把商品追加进购物车
} else {
this.carData[index].num++ //存在相同的商品则数量叠加
}
//更新总数与总价
this.allNum()
this.allPrice()
//显示购物车
this.isShowCar = true
},
//增加数量
clickAdd(index){
this.carData[index].num = this.carData[index].num + 1
this.allNum()
this.allPrice()
},
//减少数量,小于1时删除元素
clickMinus(index){
if(this.carData[index].num > 1){
this.carData[index].num = this.carData[index].num - 1
} else {
this.carData.splice(index,1) //从数组删除元素
}
this.allNum()
this.allPrice()
},
// 计算商品总数量
allNum() {
let count = 0;
this.carData.forEach(item=>{
count+=item.num
})
this.totalNum = count
//购物车有商品的时候,滚动栏高度减少,让出位置给购物车
if(this.totalNum === 1 && this.isAddHeight){
this.scrollHeight = this.scrollHeight - 50
this.isAddHeight = false
}
//购物车没有商品的时候,隐藏组件,还原滚动栏高度
if (this.totalNum === 0){
this.scrollHeight = this.scrollHeight + 50
this.isAddHeight = true
this.isShowCar = false
this.clickCar()
}
},
// 计算商品总价格
allPrice() {
let Price = 0;
this.carData.forEach(item=>{
//总价格=数量 X 单价 ,由于数组的单价是字符串类型,所以要先转换成浮点数字类型
Price+=item.num * parseFloat(item.price)
})
//Number类型的数据调用toFixed方法,指定保留几位小数,返回的数据是一个string类型
this.totalPrice = Price.toFixed(2)
},
css
.scroll-box{
display: flex;
flex-direction: row;
/* 需要定义高度,否则不能分栏滚动 */
/* height: calc(78.2vh); */
}
.left-tab{
flex: 1; /*均匀分配元素*/
display: flex;
overflow: hidden;
background: #f6f6f6;
}
.tab{
width: 150rpx;
height: 100%;
}
.tab-item{
height: 100rpx;
background: #f6f6f6;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
font-size: 22rpx;
line-height: 1;
}
.active{ /* 选项卡活动时的样式 */
color:#A7D500;
font-weight: bolder;
background: #ffffff;
}
/* 右边页面样式 */
.right-box {
left: 15%;
width: 80%;
background: #f6f6f6;
}
.page-view {
padding: 10rpx;
}
.class-item {
margin-bottom: 30rpx;
background-color: #fff;
padding: 16rpx;
border-radius: 8rpx;
}
.item-title {
font-size: 26rpx;
color: $u-main-color;
font-weight: bold;
}
.item-container {
display: flex;
flex-direction: column;
}
.thumb-box {
width: 98%;
height: 150rpx;
display: flex;
flex-direction: row;
align-items: center;
/* justify-content: center; */
margin-top: 50rpx;
}
.item-menu-image {
width: 150rpx;
height: 150rpx;
}
.item-menu-name {
font-weight: normal;
font-size: 28rpx;
margin-bottom: 10rpx;
}
.item-menu-explain{
font-size: 20rpx;
margin-bottom: 10rpx;
/* 下方四项配合使用,超出范围的显示... */
width: 400rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.item-menu-ps{
display: flex;
flex-direction: row;
justify-content: space-between; /*两边对齐*/
}
.item-menu-price{
font-size: 26rpx;
font-weight: 600;
}
.item-menu-select{
width: 100rpx;
height: 36rpx;
/* 文字垂直居中 (line-height)和(height)设置一样就可以 */
line-height: 36rpx;
/* 文字水平居中 */
text-align:center;
font-size: 22rpx;
background-color: #A7D500;
border-radius: 50rpx;
}
/* 购物车 */
.car{
display: flex;
flex-direction: row;
align-items: center;
height: 100rpx;
width: 100%;
position: fixed;
bottom: 0rpx;
background-color:#ffffff ;
z-index: 79;
}
.car-img-back{
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 100rpx;
width: 100rpx;
position: fixed;
bottom: 35rpx;
left: 30rpx;
background-color: #A7D500;
border-radius: 50%;
z-index: 79;
}
.car-img{
height: 60rpx;
width: 60rpx;
}
.car-left{
/* 平均分布元素,父元素display: flex; 子元素flex:1; */
flex: 1;
}
.car-pice{
padding-left: 160rpx;
font-size: 34rpx;
font-weight: 700;
}
.car-num{
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 30rpx;
width: 30rpx;
position: fixed;
bottom: 100rpx;
left: 110rpx;
background-color: #ffea49;
border-radius: 50%;
z-index: 79;
}
.car-num-text{
font-size: 20rpx;
}
.car-right{
/* 平均分布元素,父元素display: flex; 子元素flex:1; */
flex: 1;
}
.car-text{
height: 100rpx;
width: 200rpx;
line-height: 100rpx;
margin-left: 200rpx;
text-align: center;
background-color: #A7D500;
}
.car-list{
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 500rpx;
width: 100%;
position: fixed;
bottom: 100rpx;
background-color: #ffffff;
border-radius: 10px 10px 0px 0px;
z-index: 78;
}
.car-container {
display: flex;
flex-direction: column;
width: 100%;
}
.car-box {
width: 100%;
height: 120rpx;
display: flex;
flex-direction: row;
align-items: center;
padding: 20rpx 10rpx;
border-bottom: solid #f4f4f4 1px;
}
.car-menu-image {
width: 100rpx;
height: 100rpx;
margin-left: 35rpx;
}
.car-menu-name {
font-weight: normal;
font-size: 28rpx;
margin-bottom: 10rpx;
}
.car-menu-explain{
font-size: 20rpx;
margin-bottom: 10rpx;
/* 下方四项配合使用,超出范围的显示... */
width: 400rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.car-menu-ps{
display: flex;
flex-direction: row;
justify-content: space-between; /*两边对齐*/
}
.car-menu-price{
font-size: 26rpx;
font-weight: 600;
}
.car-num-select{
display: flex;
flex-direction: row;
width: 160rpx;
height: 36rpx;
}
/* 遮罩层 */
.car-mark{
position: fixed;
left:0;
top:0;
width:100%;
height:100%;
z-index:77;
background-color: rgba(0, 0, 0, 0.4);
transition: all 0.3s ease-in 0s;
}