You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1020 lines
24 KiB

<template>
<view class="bg">
<view class="header-box" v-if="list && list.length > 0">
<view class="all-select-box" @click="changeAllSelect">
<view class="selectBox flex-around" v-if="allSelect">
<img src="https://static.ticket.sz-trip.com/cgc/images/order/dui.png" />
</view>
<view class="noSelect" v-else></view>
<text class="all-select-text">全选</text>
</view>
<view class="guanli" @click="guanli">{{ operate }}</view>
</view>
<view>
<!-- 按供应商分组显示商品 -->
<view
class="goodBox"
v-for="(supplierGroup, supplierIndex) in list"
:key="supplierIndex"
v-if="supplierGroup.goods && supplierGroup.goods.length > 0"
>
<view class="supplier-header">
<view class="supplier-title-box">
<view class="supplier-select-box" @click="changeSupplierSelect(supplierGroup)">
<view class="selectBox flex-around" v-if="isSupplierAllSelected(supplierGroup)">
<img src="https://static.ticket.sz-trip.com/cgc/images/order/dui.png" />
</view>
<view class="noSelect" v-else></view>
</view>
<view class="title">{{ supplierGroup.supplierName }}</view>
</view>
</view>
<view
class="goodItem"
v-for="(goodItem, goodIndex) in supplierGroup.goods"
:key="goodIndex"
v-if="goodItem.flag != 0"
>
<view
class="noSelect"
v-if="!goodItem.is_select"
@click="changeSelect(goodItem)"
></view>
<view
class="selectBox flex-around"
v-else
@click="changeSelect(goodItem)"
>
<img
src="https://static.ticket.sz-trip.com/cgc/images/order/dui.png"
/>
</view>
<image
:src="showImg(goodItem.specUrl)"
mode="aspectFill"
@click="goDetails(goodItem.goodsId)"
>
</image>
<view class="contentBox">
<view class="title text-overflow">{{ goodItem.goodsName }}</view>
<view class="subTitle text-overflow"
>{{ goodItem.specValueOne }}-{{ goodItem.specValueTwo }}</view
>
<view class="priceBox flex-between">
<view class="price">{{ goodItem.salePrice }}</view>
<view class="quantity-control">
<view
:class="[
'quantity-btn',
'minus-btn',
goodItem.quantity == 1 ? 'disabled' : '',
]"
@click="delNum(goodItem)"
>
-
</view>
<view class="quantity-input">{{ goodItem.quantity }}</view>
<view class="quantity-btn plus-btn" @click="addNum(goodItem)">
+
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="loseGood" v-if="loseList && loseList.length > 0">
<view class="title flex-between"
>失效商品<span @click="delLoseGood('', 1)">清空失效商品</span></view
>
<view
class="goodItem"
v-for="(goodItem, goodIndex) in loseList"
:key="goodIndex"
>
<view class="noSelect"></view>
<image :src="showImg(goodItem.specUrl)" mode="aspectFill"></image>
<view class="contentBox">
<view class="title text-overflow">{{ goodItem.goodsName }}</view>
<view class="subTitle text-overflow"
>{{ goodItem.specValueOne }}-{{ goodItem.specValueTwo }}</view
>
<view class="priceBox flex-between">
<view class="price">{{ goodItem.salePrice }}</view>
<view class="btn" @click="delLoseGood(goodItem.id, 0)">删除</view>
</view>
</view>
</view>
</view>
<!-- 预定按钮 -->
<view class="orderBox flex-between">
<view class="priceBox">
<!-- <view class="selectBox flex-around" v-if="allSelect" @click="changeAllSelect">
<img src="https://static.ticket.sz-trip.com/cgc/images/order/dui.png" alt="">
</view>
<view class="noSelect" v-else @click="changeAllSelect"></view>全选 -->
<span v-if="operate == '管理'">在线付:</span>
<p v-if="operate == '管理'">¥{{ getAllPrice() }}</p>
</view>
<view class="btn" @click="order" v-if="operate == '管理'">提交订单</view>
<view class="btn" @click="delGood" v-else>删除</view>
</view>
<!-- 空状态 -->
<view
class="empty-state"
v-if="getTotalGoodsCount() == 0 && loseList.length == 0"
>
<image
class="empty-icon"
:src="showImg('/uploads/20250826/empty-cart.png')"
mode="aspectFit"
></image>
<text class="empty-title">购物车空空如也</text>
<text class="empty-desc">快去挑选心仪的商品吧</text>
<view class="empty-btn" @click="goShopping">去逛逛</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
operate: "管理",
list: [], // 改为按供应商分组的数组
loseList: [],
allSelect: false,
};
},
onShow() {
this.getShoppingList();
this.allSelect = false;
},
methods: {
getShoppingList() {
this.Post({}, "/framework/cart/list", "DES")
.then((res) => {
const list = res.data || [];
this.loseList = [];
// 按供应商分组有效商品
const supplierGroups = {};
list.forEach((item) => {
if (item.effectStatus == 1) {
this.loseList.push(item);
} else {
const supplierId = item.supplierId || "default";
if (!supplierGroups[supplierId]) {
supplierGroups[supplierId] = {
supplierId: supplierId,
supplierName: item.supplierName || "默认供应商",
goods: [],
};
}
supplierGroups[supplierId].goods.push(item);
}
});
// 转换为数组格式
this.list = Object.values(supplierGroups);
console.log("购物车数据更新:", this.list);
console.log("失效商品:", this.loseList);
})
.catch((err) => {
console.error("获取购物车数据失败:", err);
this.list = [];
this.loseList = [];
});
},
// 管理
guanli() {
if (this.operate == "管理") {
this.operate = "退出管理";
} else {
this.operate = "管理";
}
console.log(this.operate);
},
delNum(item) {
if (item.quantity <= 1) {
item.quantity = 1;
uni.showToast({
title: "已经最少了,不能再减了",
icon: "none",
});
} else {
item.quantity -= 1;
this.updateCart(item);
}
},
addNum(item) {
item.quantity += 1;
this.updateCart(item);
},
updateCart(item) {
let that = this;
that
.Post(
{
cartId: item.id,
quantity: item.quantity,
},
"/framework/cart/updateQuantity",
"DES"
)
.then(function (res) {
if (res) {
// that.$toast.clear()
}
});
},
// 更改选中状态
changeSelect(item) {
item.is_select = !item.is_select;
this.updateAllSelectStatus();
this.$forceUpdate();
},
// 去商品详情
goDetails(id) {
uni.navigateTo({
url: "/subPackages/techan/detail?id=" + id,
});
},
// 去购物
goShopping() {
uni.switchTab({
url: "/pages/index/index",
});
},
// 改变所有选中
changeAllSelect() {
this.allSelect = !this.allSelect;
this.list.forEach((supplierGroup) => {
supplierGroup.goods.forEach((item) => {
if (this.allSelect) item.is_select = true;
else item.is_select = false;
});
});
this.$forceUpdate();
},
// 改变供应商全选
changeSupplierSelect(supplierGroup) {
const isAllSelected = this.isSupplierAllSelected(supplierGroup);
supplierGroup.goods.forEach((item) => {
item.is_select = !isAllSelected;
});
this.updateAllSelectStatus();
this.$forceUpdate();
},
// 检查供应商是否全选
isSupplierAllSelected(supplierGroup) {
if (!supplierGroup.goods || supplierGroup.goods.length === 0) {
return false;
}
return supplierGroup.goods.every(item => item.is_select);
},
// 更新全选状态
updateAllSelectStatus() {
let allSelected = true;
for (let supplierGroup of this.list) {
for (let goods of supplierGroup.goods) {
if (!goods.is_select) {
allSelected = false;
break;
}
}
if (!allSelected) break;
}
this.allSelect = allSelected;
},
// 删除失效商品
delLoseGood(id, index) {
if (index == 0) {
// 删除单个失效商品
uni.showModal({
title: "确认删除",
content: "确定要删除这个失效商品吗?",
success: (res) => {
if (res.confirm) {
this.Post(
{
s_id: id,
method: "delete",
},
`/framework/cart/remove/${id}`,
"DES"
)
.then((res) => {
uni.showToast({
title: "删除成功",
icon: "success",
});
// 强制刷新数据
setTimeout(() => {
this.getShoppingList();
}, 100);
})
.catch((err) => {
console.error("删除失效商品失败:", err);
uni.showToast({
title: "删除失败",
icon: "none",
});
});
}
},
});
} else {
// 清空所有失效商品
uni.showModal({
title: "确认清空",
content: `确定要清空所有失效商品吗?`,
success: (res) => {
if (res.confirm) {
let ids = [];
this.loseList.forEach((item) => {
ids.push(item.id);
});
this.Post(
{
s_id: ids.toString(),
method: "delete",
},
`/framework/cart/remove/${ids.toString()}`,
"DES"
)
.then((res) => {
uni.showToast({
title: "清空成功",
icon: "success",
});
// 强制刷新数据
setTimeout(() => {
this.getShoppingList();
}, 100);
})
.catch((err) => {
console.error("清空失效商品失败:", err);
uni.showToast({
title: "清空失败",
icon: "none",
});
});
}
},
});
}
},
// 删除商品
delGood() {
let ids = [];
this.list.forEach((supplierGroup) => {
supplierGroup.goods.forEach((item) => {
if (item.is_select == true) {
ids.push(item.id);
}
});
});
if (ids.length === 0) {
uni.showToast({
title: "请选择要删除的商品",
icon: "none",
});
return;
}
uni.showModal({
title: "确认删除",
content: `确定要删除选中的${ids.length}件商品吗?`,
success: (res) => {
if (res.confirm) {
this.Post(
{
s_id: ids.toString(),
method: "delete",
},
`/framework/cart/remove/${ids.toString()}`,
"DES"
)
.then((res) => {
uni.showToast({
title: "删除成功",
icon: "success",
});
// 强制刷新数据
setTimeout(() => {
this.getShoppingList();
}, 100);
this.operate = "管理"; // 删除后退出管理模式
})
.catch((err) => {
console.error("删除失败:", err);
uni.showToast({
title: "删除失败",
icon: "none",
});
});
}
},
});
},
// 获取总价
getAllPrice() {
let allPrice = 0;
this.list.forEach((supplierGroup) => {
supplierGroup.goods.forEach((item) => {
if (item.is_select == true) {
allPrice += item.salePrice * item.quantity;
}
});
});
return allPrice.toFixed(2);
},
// 结算
order() {
let list = [];
let ids = [];
this.list.forEach((supplierGroup) => {
supplierGroup.goods.forEach((item) => {
if (item.is_select == true) {
ids.push(item.merchant_id);
list.push(item);
}
});
});
// if (Array.from(new Set(ids)).length > 1) {
// uni.showToast({
// title: '请选择同一商户下的商品',
// icon: 'none'
// })
// return;
// }
if (list.length == 0) {
uni.showToast({
title: "请选择商品",
icon: "none",
});
return;
}
this.Post(
{
bos: list,
method: "post",
},
`/framework/cart/check-cart-items`,
"DES"
)
.then((res) => {
if (res.data.canOrder) {
this.$store.commit("changeOrderSCart", list);
this.gotoPath("/subPackages/techan/order");
} else {
uni.showToast({
title: res.message,
icon: "none",
});
this.getShoppingList();
}
})
.catch((res) => {
uni.showToast({
title: "部分商品无法下单,请重新加载购物车",
icon: "none",
});
setTimeout(() => {
this.getShoppingList();
}, 800);
});
// this.gotoPath('/subPackages/goods/goodsOrder')
},
// 获取商品总数量
getTotalGoodsCount() {
let count = 0;
this.list.forEach((supplierGroup) => {
count += supplierGroup.goods.length;
});
return count;
},
},
};
</script>
<style scoped lang="scss">
.bg {
width: 100%;
min-height: 100vh;
padding: 0 27rpx 200rpx;
box-sizing: border-box;
background-color: #f8f8f8;
}
.header-box {
display: flex;
justify-content: space-between;
align-items: center;
height: 76rpx;
padding: 0 20rpx;
margin-bottom: 20rpx;
}
.all-select-box {
display: flex;
align-items: center;
.noSelect {
width: 37rpx;
height: 37rpx;
border-radius: 50%;
border: 2rpx solid #666666;
box-sizing: border-box;
margin-right: 15rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
}
}
.selectBox {
width: 37rpx;
height: 37rpx;
margin-right: 15rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
}
img {
width: 100%;
height: 100%;
}
}
.all-select-text {
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #6ca5aa;
}
}
.guanli {
height: 76rpx;
text-align: right;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #6ca5aa;
line-height: 76rpx;
padding: 0 20rpx;
border-radius: 20rpx;
transition: all 0.3s ease;
&:active {
background: rgba(108, 165, 170, 0.1);
transform: scale(0.95);
}
}
.goodBox {
width: 697rpx;
height: auto;
background: #ffffff;
border-radius: 20rpx;
margin: 0 auto 27rpx;
padding: 25rpx 20rpx;
box-sizing: border-box;
.supplier-header {
margin-bottom: 20rpx;
.supplier-title-box {
display: flex;
align-items: center;
.supplier-select-box {
margin-right: 15rpx;
.noSelect {
width: 37rpx;
height: 37rpx;
border-radius: 50%;
border: 2rpx solid #666666;
box-sizing: border-box;
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
}
}
.selectBox {
width: 37rpx;
height: 37rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
}
img {
width: 100%;
height: 100%;
}
}
}
.title {
font-size: 30rpx;
font-weight: 600;
color: #333333;
}
}
}
.goodItem {
width: 100%;
height: 173rpx;
margin-top: 46rpx;
display: flex;
.noSelect {
width: 37rpx;
height: 37rpx;
border-radius: 50%;
border: 2rpx solid #666666;
box-sizing: border-box;
margin-right: 25rpx;
margin-top: 68rpx;
}
.selectBox {
width: 37rpx;
height: 37rpx;
// background: linear-gradient(90deg, #FA2B66, #FF9834);
// border-radius: 50%;
margin-right: 25rpx;
margin-top: 68rpx;
img {
width: 100%;
height: 100%;
}
}
image {
width: 173rpx;
height: 173rpx;
border-radius: 13rpx;
}
.contentBox {
width: 400rpx;
height: 173rpx;
margin-left: 23rpx;
.title {
font-size: 31rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
}
.subTitle {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #7c7c7c;
margin-top: 13rpx;
}
.priceBox {
margin-top: 35rpx;
.price {
font-size: 40rpx;
font-family: PingFang SC;
font-weight: 500;
color: #ff2d3b;
}
.price:before {
content: "";
font-size: 27rpx;
}
.quantity-control {
display: flex;
align-items: center;
border: 2rpx solid #e5e5e5;
border-radius: 25rpx;
overflow: hidden;
.quantity-btn {
width: 60rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f8f9fa;
transition: all 0.3s ease;
&.minus-btn {
border-right: 1rpx solid #e5e5e5;
}
&.plus-btn {
border-left: 1rpx solid #e5e5e5;
}
&.disabled {
background: #f1f1f1;
color: #ccc;
}
&:not(.disabled):active {
background: #e9ecef;
}
font-size: 50rpx;
font-weight: 500;
color: #333;
.btn-text {
}
}
.quantity-input {
width: 80rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
font-size: 28rpx;
color: #333;
font-weight: 500;
}
}
}
}
}
}
.loseGood {
width: 697rpx;
height: auto;
background: #ffffff;
border-radius: 20rpx;
margin: 0 auto 27rpx;
padding: 25rpx 20rpx;
box-sizing: border-box;
.title {
font-size: 35rpx;
font-family: PingFang SC;
font-weight: 500;
color: #666666;
span {
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 400;
color: #2791f7;
}
}
.goodItem {
width: 100%;
height: 173rpx;
margin-top: 46rpx;
display: flex;
color: #999999;
.noSelect {
width: 37rpx;
height: 37rpx;
border-radius: 50%;
border: 2rpx solid #cccccc;
background: #f1f1f1;
box-sizing: border-box;
margin-right: 25rpx;
margin-top: 68rpx;
}
image {
width: 173rpx;
height: 173rpx;
border-radius: 13rpx;
margin-right: 23rpx;
}
.contentBox {
width: 421rpx;
height: 173rpx;
.title {
font-size: 31rpx;
font-family: PingFang SC;
font-weight: 500;
}
.subTitle {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
}
.priceBox {
margin-top: 35rpx;
.price {
font-size: 40rpx;
font-family: PingFang SC;
font-weight: 500;
}
.price:before {
content: "";
font-size: 27rpx;
}
.btn {
width: 125rpx;
height: 53rpx;
background: #999999;
border-radius: 13rpx;
text-align: center;
line-height: 53rpx;
font-size: 29rpx;
font-family: PingFang SC;
font-weight: 500;
color: #ffffff;
}
}
}
}
}
/* 空状态样式 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 0;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 40rpx;
opacity: 0.6;
}
.empty-title {
font-size: 36rpx;
color: #333;
font-weight: 500;
margin-bottom: 20rpx;
}
.empty-desc {
font-size: 28rpx;
color: #999;
margin-bottom: 60rpx;
}
.empty-btn {
width: 240rpx;
height: 80rpx;
background: #6ca5aa;
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 30rpx;
font-weight: 500;
box-shadow: 0 8rpx 20rpx rgba(108, 165, 170, 0.3);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
box-shadow: 0 4rpx 10rpx rgba(108, 165, 170, 0.2);
}
}
}
.orderBox {
width: 750rpx;
height: 151rpx;
background: #ffffff;
box-shadow: 0px 0px 16rpx 0px rgba(6, 0, 1, 0.1);
padding: 0 27rpx;
box-sizing: border-box;
position: fixed;
left: 0;
bottom: 0;
.priceBox {
display: flex;
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #666666;
span {
font-size: 27rpx;
font-family: PingFang SC;
font-weight: 400;
color: #333333;
margin-left: 20rpx;
}
p {
font-size: 36rpx;
font-family: PingFang SC;
font-weight: 500;
color: #ff2d3b;
margin-top: -5rpx;
}
.noSelect {
width: 41rpx;
height: 41rpx;
border-radius: 50%;
border: 2rpx solid #666666;
box-sizing: border-box;
margin-right: 10rpx;
}
.selectBox {
width: 41rpx;
height: 41rpx;
// background: linear-gradient(90deg, #FA2B66, #FF9834);
border-radius: 50%;
margin-right: 10rpx;
img {
width: 100%;
height: 100%;
}
}
}
.btn {
width: 219rpx;
height: 77rpx;
background: #6ca5aa;
border-radius: 39rpx;
text-align: center;
line-height: 77rpx;
font-size: 33rpx;
font-family: PingFang SC;
font-weight: 500;
color: #ffffff;
}
}
/* 移除旧的样式 */
.zhihui {
background: #e8e8e8 !important;
color: #999999 !important;
}
/* 优化商品卡片样式 */
.goodBox {
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
&:hover {
transform: translateY(-2rpx);
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.12);
}
}
/* 优化选择框样式 */
.noSelect {
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
}
}
.selectBox {
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
}
}
</style>