效果图如下:
说明:点击商家复选框,可选中当前商家下的所有商品。点击全选,选中全部商家的商品
以下是代码:
<template>
<view class="cart-index">
<view class="lement">
<view>
<checkbox-group @change="checkboxChangeAll">
<checkbox class="round" style="transform:scale(0.7)" :checked="isAllChecked" />
</checkbox-group>
</view>
<view>全选</view>
<view style="display: flex;">
<view>合计:</view>
<view style="color: #FF1E1E;font-size: 28rpx;">¥{{totalPrice}}</view>
</view>
<view>免费配送</view>
<view class="now" @click="submitOrder">立即结算</view>
</view>
<!-- 这里开始遍历商家和商品 -->
<!-- 这一层是商家,val是商家的数据,k是商家的下标,可以在数组中用于定位商家 -->
<view class="item" v-for="(val,k) in cartData" :key="k">
<view class="title">
<checkbox-group @change="checkboxChangeShop($event, k)">
<checkbox class="round" style="transform:scale(0.7)" :checked="val.checked" />
</checkbox-group>
<image style="width:35rpx;height: 35rpx;margin-top: 7rpx;" :src="val.storeDto.storePic"></image>
<text>{{val.storeDto.storeName}}</text>
</view>
<!-- 这一层是商品的遍历,item是商品信息,index是商品下标 -->
<view v-for="(item,index) in val.shoppingTrolleyDtoList">
<slideLeft @delItem="delItem(index,k)">
<view class="cart-container">
<view>
<!-- 这一层是商品的勾选,我们可以传入商家和商品的下标,用于帮我们快速定位商品 -->
<checkbox-group @change="checkboxChangeGood($event,item.id, k, index)">
<checkbox class="round" style="transform:scale(0.7)" :checked="item.checked" />
</checkbox-group>
</view>
<view style="background: #F2F2F2;">
<image style="width:168rpx;height: 168rpx;" :src="item.goodsDto.goodsPicPathList[0]">
</image>
</view>
<view class="message">
<view style="font-size: 28rpx;">{{item.goodsDto.goodsName}}</view>
<view class="guige">
<view>{{message(index,k,item.goodsSpec)}}</view>
<image style="width: 26rpx;height: 26rpx;" src="../../static/index/arrow.png"></image>
</view>
<view class="price">
<view>¥{{item.goodsDto.price}}</view>
<view>¥{{item.goodsPrice}}</view>
<view class="amount">
<view @click="sub(item,index,k)">-</view>
<view>{{item.goodsCount}}</view>
<view @click="add(item)">+</view>
</view>
</view>
</view>
</view>
</slideLeft>
</view>
</view>
</view>
</template>
<script>
import slideLeft from '@/components/slide-left/slide-left.vue'
import {
cartList,
deleteCart
} from "../../src/api.js"
export default {
components: {
slideLeft
},
data() {
return {
isAllChecked: false, //是否全选
totalPrice: 0, //总价
cartData: [], //数据
// specifications: '',
// color: ''
// 首先,我们要实现的是全选,那么肯定会将所有的数据汇总到一个数组,那么建立totalArr用于存储
// 此外,我们还要考虑存储的格式,每个商家代表一个对象,我们要的是商家里面的商品,所以:[[], []], [] 这样的格式
// 之后,我们计算商品价格,那么就只需将数组扁平化,计算和即可。
// 先考虑单个商品勾选时,怎么放入
// 不过你现在这种方式也可以,直接在返回的数组上修改
// 思路还是从单选商品开始
totalArr: []
}
},
onLoad() {
this.list();
},
//监听页面滚动
onPageScroll(res) {
this.scrollTop = res.scrollTop
},
methods: {
message(index, k, goodsSpec) {
console.log(goodsSpec[1]);
// console.log(goodsSpec[3]);
let newGood1 = this.cartData[k].shoppingTrolleyDtoList[index].goodsSpecList[0].child;
let newGood2 = this.cartData[k].shoppingTrolleyDtoList[index].goodsSpecList[1].child;
console.log(newGood1.length);
for (let i = 0; i < newGood1.length; i++) {
if (goodsSpec[1] == i) {
// break
// console.log(newGood1[i].name);
var specifications = newGood1[i].name;
}
}
for (let j = 0; j < newGood2.length; j++) {
if (goodsSpec[3] == j) {
// break
// console.log(newGood2[i].name);
var color = newGood2[j].name;
}
}
return [specifications, color];
},
async list() {
let data = {
userUuid: uni.getStorageSync('uuid'),
currentPage: 1,
pageSize: 10
}
let res = await cartList({
header: {
token: uni.getStorageSync('token'),
clientType: 1
},
data: data
})
if (res.code == 200) {
this.cartData = res.data;
console.log(this.cartData)
}
},
async delItem(index, k) {
console.log(this.cartData[k].shoppingTrolleyDtoList[index].goodsUuid, "goodsUuid");
console.log(this.cartData[k].shoppingTrolleyDtoList[index].storeUuid, "storeUuid");
let data = {
isSelect: 0,
shoppingTrolleyVoList: [{
storeUuid: this.cartData[k].shoppingTrolleyDtoList[index].storeUuid,
goodsUuid: this.cartData[k].shoppingTrolleyDtoList[index].goodsUuid
}]
}
let res = await deleteCart({
method: "POST",
header: {
token: uni.getStorageSync('token'),
clientType: 1
},
data: data
})
console.log(res.data)
if (res.code == 200) {
this.list();
this.cartData = res.data;
}
},
add(item) {
item.goodsCount++;
},
sub(item, index, k) {
console.log(this.cartList, "this.cartList");
if (item.goodsCount <= 1) {
uni.showModal({
title: '提示',
content: '确定删除吗',
success: (res) => {
if (res.confirm) {
console.log('用户点击确定');
console.log(index);
this.delItem(index, k);
} else if (res.cancel) {
console.log('用户点击取消');
item.count == 0;
}
}
});
} else {
item.goodsCount--;
}
},
// delItem(index) {
// console.log(this.cartList, "this.cartList");
// this.cartList.splice(index, 1)
// console.log("删除了", index);
// },
submitOrder() { // 提交购物车订单
uni.navigateTo({
url: '/pages/address/address'
})
},
// this.cartData.forEach(item => {
// item.checked = this.checkedAll;
// if (item.shoppingTrolleyDtoList) {
// item.shoppingTrolleyDtoList.forEach(citem => {
// citem.checked = this.checkedAll;
// })
// }
// })
// 合计
setCart() {
let totalPrice = 0;
this.cartList.forEach(shop => {
if (shop.checked) {
shop['shoppingTrolleyDtoList'].forEach(good => {
totalPrice += good.count * good.price
})
}
})
this.totalPrice = totalPrice
},
// 全选
// 需要联动商家、商品
checkboxChangeAll(e) {
console.log(e,"全选的e");
const isChecked = e.detail.value.length > 0;
// 联动商家、商品
this.cartData.forEach(shop => {
console.log(shop['shoppingTrolleyDtoList'],"shop");
this.$set(shop, 'checked', isChecked);
shop['shoppingTrolleyDtoList'].forEach(good => {
this.$set(good, 'checked', isChecked)
})
})
console.log('购物车数据', this.cartData);
console.log('全选数据', this.isAllChecked);
// this.isAllChecked = !this.isAllChecked
// this.cartList.forEach(v => v.isChecked = this.isAllChecked)
},
// 勾选商家
// 需要联动更新商品、全选
checkboxChangeShop(e, shopIndex) {
console.log(e, shopIndex);
const shop = this.cartData[shopIndex];
// e.detail.length > 0 则是勾选中
const isChecked = e.detail.value.length > 0;
this.$set(shop, 'checked', isChecked);
// 联动商品
const goods = this.cartData[shopIndex]['shoppingTrolleyDtoList']
goods.forEach(g => {
this.$set(g, 'checked', isChecked)
});
// 联动全选
const shops = this.cartData
if (isChecked) {
let shopCheckSum = 0;
// 判断是否所有商家都勾选
shops.forEach(s => {
if (s.checked) {
shopCheckSum ++
}
})
this.isAllChecked = shopCheckSum === shops.length;
} else {
this.isAllChecked = false
}
console.log('购物车数据', this.cartData);
console.log('全选数据', this.isAllChecked);
},
// 勾选商品
// 需要联动更新商家、全选
checkboxChangeGood(e, id, shopIndex, goodIndex) {
console.log(e, id, shopIndex, goodIndex);
// 找到商品,并修改相关状态
// e.detail.length > 0 则是勾选中
const good = this.cartData[shopIndex]['shoppingTrolleyDtoList'][goodIndex]; // 单个商品
this.$set(good, 'checked', e.detail.value.length > 0);
// 商品状态变更,那么再考虑商家状态
// 判断当前商家下面有多少个勾选的商品,如果勾选总数和商家拥有的商品总数一致,那么商家勾选中
const goods = this.cartData[shopIndex]['shoppingTrolleyDtoList']; // 多个商品
const shop = this.cartData[shopIndex]; // 单个商店
let goodCheckSum = 0;
goods.forEach(g => {
if (g.checked) {
goodCheckSum ++;
}
})
this.$set(shop, 'checked', goodCheckSum === goods.length);
// 之后再判断全选
// 当所有商家都勾选,那么全选就勾选上
const shops = this.cartData; // 所有商店
let shopCheckSum = 0;
shops.forEach(s => {
if (s.checked) {
shopCheckSum ++
}
});
this.isAllChecked = shopCheckSum === this.cartData.length;
console.log('购物车数据', this.cartData);
console.log('全选数据', this.isAllChecked);
// console.log(e, id, this.cartData)
// console.table({
// '商家下标': shopIndex,
// '商品下标': goodIndex
// })
// // var temp = []
// // 找到被修改的商品对象,this.cartData是最外层的,代表着商家,里面一层才是商品
// let index = this.cartData.findIndex(v => v.id === id)
// // 选中状态取反
// this.cartData[index].isChecked = !this.cartData[index].isChecked
// temp = this.cartData.every(v => v.isChecked)
// if (temp) {
// this.isAllChecked = true
// } else {a
// this.isAllChecked = false
// }
// this.setCart()
},
},
}
</script>
<style lang="stylus" scoped>
.cart-index{
background-color: #F7F7F7;
.cart-container{
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30rpx;
margin-left: -20rpx;
}
.lement{
position fixed;
bottom 0;
display flex;
justify-content space-between;
align-items center;
padding 30rpx;
font-size: 24rpx;
background-color white;
width: 93%;
z-index:1000
.now{
background: #FF1E1E;
border-radius 38rpx;
font-size: 30rpx;
color:white;
padding 20rpx 65rpx
}
}
.item{
padding 0 30rpx 130rpx;
background-color white;
margin-top 15rpx
.title{
display flex;
text{
font-size 28rpx;
margin-left 5px;
margin-top 5rpx
}
}
.message{
width 61%;
display flex;
flex-direction column;
justify-content space-between
view:nth-child(1){
// font-size: 28rpx;
}
.guige{
display flex;
justify-content space-around;
align-items center;
width 35%
background: #F3F3F3;
border-radius 20rpx;
color: #999999;
font-size 24rpx;
padding 7rpx;
margin-top 10rpx
}
.price{
display flex;
justify-content space-between;
margin-top 10rpx
view:nth-child(1){
font-size 30rpx;
color: #FF1E1E;
}
view:nth-child(2){
font-size: 24rpx;
color: #999999;
text-decoration: line-through;
margin-top 5rpx;
margin-left -60rpx
}
}
.amount{
display flex;
view:nth-child(2){
font-size: 28rpx;
color: #333333;
text-decoration: none;
margin-left 20rpx;
margin-right 20rpx
}
view:nth-child(1),view:nth-child(3){
width:46rpx;
height 46rpx;
background-color #F4F4F4;
text-align center;
font-size 30rpx;
color:black
}
}
}
}
}
</style>
<template>
<view>
<view class="box-slideLeft" >
<view class="touch-item touch-slideLeft " @touchstart="touchS" @touchmove="touchM" @touchend="touchE" :style="item_show.txtStyle">
<slot />
</view>
<view class="touch-item del-box-touch-slideLeft cf-shuCenter" @click="delItem(item_show)">
<view class="delete">删除</view>
</view>
</view>
</view>
</template>
<script>
export default {
components: {
},
props: {
data_transit: {
type: Object,
default () {
return {}
}
},
//可不传参
item: {
type: Object,
default () {
return {}
}
},
},
computed: {
},
data() {
return {
item_show : {},
delBtnWidth: 60, //删除按钮宽度单位(rpx)
startX: '',
};
},
created:function(){
//专门处理检查对象中,某字段是否存在的,如果存在返回 true 不存在返回 false
let that = this ;
let item = that.item ;
if(!item.hasOwnProperty("txtStyle")){
this.$set(this.item,'txtStyle','');//不需要初始化了
}
this.item_show = this.item ;
},
watch: {
item(e){
this.item_show = e ;
},
},
methods: {
//点击删除按钮事件
delItem: function(e) {
let that = this;
let data ={
item : e ,
data : that.data_transit ,
};
console.log(data,"子组件的值");
this.$emit('delItem', data);
},
touchS: function(e) {
let that = this;
if (e.touches.length == 1) {
//设置触摸起始点水平方向位置
this.startX = e.touches[0].clientX
}
},
touchM: function(e) {
let that = this;
if (e.touches.length == 1) {
//手指移动时水平方向位置
var moveX = e.touches[0].clientX;
//手指起始点位置与移动期间的差值
var disX = this.startX - moveX;
var delBtnWidth = this.delBtnWidth;
var txtStyle = "";
if (disX == 0 || disX < 0) { //如果移动距离小于等于0,说明向右滑动,文本层位置不变
txtStyle = "left:0px";
} else if (disX > 0) { //移动距离大于0,文本层left值等于手指移动距离
txtStyle = "left:-" + disX + "px";
if (disX >= delBtnWidth) {
//控制手指移动距离最大值为删除按钮的宽度
txtStyle = "left:-" + delBtnWidth + "px";
}
}
//获取手指触摸的是哪一项
that.item_show.txtStyle = txtStyle;
}
},
touchE: function(e) {
let that = this;
if (e.changedTouches.length == 1) {
//手指移动结束后水平位置
var endX = e.changedTouches[0].clientX;
//触摸开始与结束,手指移动的距离
var disX = this.startX - endX;
var delBtnWidth = this.delBtnWidth;
//如果距离小于删除按钮的1/2,不显示删除按钮
var txtStyle = disX > delBtnWidth / 2 ? "left:-" + delBtnWidth + "px" : "left:0px";
//获取手指触摸的是哪一项
that.item_show.txtStyle = txtStyle;
}
},
}
}
</script>
<style lang="scss">
// @import './iconfont.css';//便于有删除图标
.box-slideLeft {
view {
box-sizing: border-box;
}
position: relative;
overflow: hidden;
.touch-item {
position: absolute;
top: 0;
padding: 10px 10px 10px;
background-color: #FFFFFF;
// border-radius: 10px;
overflow: hidden;
}
.touch-slideLeft {
position: relative;
width: 100%;
z-index: 5;
transition: left 0.2s ease-in-out;
// white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.del-box-touch-slideLeft {
right: 0;
float: left;
width: 70px;
height: 100%;
line-height: 101px;
background-color: #FF1E1E;;
border-radius: 0 10px 10px 0;
color: #fff;
font-size: 18px;
font-weight: lighter;
text-align: center;
}
.delete{
font-size: 28rpx;
}
.icon-shanchu{
font-size: 44upx;
}
.cf-shuCenter{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
</style>