caiyunyi/pages/ownWater/waterShopCar.vue
ylj20011123 35e40eb558 update
2026-02-28 18:25:12 +08:00

584 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="main">
<!-- 顶部地址选择 -->
<view class="header" :style="{ paddingTop: menu.top + 'px' }">
<view class="headerTop" :style="{ height: menu.height + 'px' }">
<view class="pageName">自有水购物车</view>
</view>
<view class="addressBox" @click="handleShowAddress">
<view class="addrLeft">
<view class="addrTitle">
<image class="fixedIcon" src="/static/images/home/fixed.svg" />
<text class="addrText">{{ showAddressObj.DOORPLATE || "点击添加地址" }}</text>
</view>
<view class="addrUser" v-if="showAddressObj.MEMBERADDRESS_ID">
<text>{{ showAddressObj.USER_NAME }}</text>
<text>{{ showAddressObj.MOBILEPHONE }}</text>
</view>
</view>
<image class="arrow" src="https://eshangtech.com/caiyunyiImg/moreIcon.png" />
</view>
</view>
<view class="pageContent">
<scroll-view v-if="cartList.length > 0" class="cartList" scroll-y :show-scrollbar="false">
<view class="cartItemWrapper" v-for="(item, index) in cartList" :key="index">
<!-- 采用 shopCar 逻辑z-index 层级管理 -->
<view class="cartItem" @touchstart="touchStart($event, index)" @touchmove="touchMove($event, index)"
@touchend="touchEnd($event, index)"
:style="{ transform: 'translateX(' + (item.offsetX || 0) + 'px)' }">
<view class="itemCheck" @click="toggleSelect(item)">
<radio :checked="item.select" color="#4BCB7E" />
</view>
<image class="itemImg" :src="item.IMAGE_PATH || '/static/images/home/defultImg.png'"
mode="aspectFill" />
<view class="itemInfo">
<!-- 参考 shopCar title 样式 -->
<view class="itemTitle">{{ item.COMMODITY_NAME }}</view>
<view class="itemBottom">
<view class="price">¥{{ item.COMMODITY_MEMBERPRICE }}</view>
<view class="stepper">
<view class="stepBtn" @click="changeCount(item, -1)">-</view>
<input class="stepInput" type="number" v-model="item.count"
@blur="handleInputCount(item)" @confirm="handleInputCount(item)" />
<view class="stepBtn" @click="changeCount(item, 1)">+</view>
</view>
</view>
</view>
</view>
<!-- 删除按钮固定在底层右侧 -->
<view class="itemDelete" @click="deleteItem(index)">删除</view>
</view>
<view style="height: 280rpx;"></view>
</scroll-view>
<view class="emptyBox" v-else>
<image class="emptyImg" src="https://eshangtech.com/caiyunyiImg/noShopCar.png" />
<text class="emptyText">购物车空空如也~</text>
</view>
</view>
<!-- 底部结算栏 -->
<view class="settleBar" v-if="cartList.length > 0">
<view class="allSelect" @click="toggleAllSelect">
<radio :checked="isAllSelected" color="#4BCB7E" />
<text>全选</text>
</view>
<view class="totalInfo">
<view class="totalPrice">
合计: <text class="unit">¥</text><text class="money">{{ totalPrice }}</text>
</view>
<view class="payBtn" @click="handleGoPay">立即下单</view>
</view>
</view>
<uni-popup ref="addressPopup" type="bottom">
<view class="addressPopupBox">
<view class="popHeader">选择收货地址</view>
<scroll-view class="popList" scroll-y>
<view class="addrItem" v-for="(item, index) in addressList" :key="index"
@click="selectAddress(item)">
<radio :checked="item.MEMBERADDRESS_ID === showAddressObj.MEMBERADDRESS_ID" color="#4BCB7E" />
<view class="addrDetail">
<view class="detailText">{{ item.ADDRESS }}{{ item.DOORPLATE }}</view>
<view class="detailInfo">{{ item.USER_NAME }} {{ item.MOBILEPHONE }}</view>
</view>
</view>
</scroll-view>
<view class="addAddrBtn" @click="goAddAddress">新增收货地址</view>
</view>
</uni-popup>
<own-water-tabbar :page="'/pages/ownWater/waterShopCar'" :shopCarLength="totalCartCount" />
</view>
</template>
<script>
import ownWaterTabbar from "@/components/ownWaterTabbar.vue";
export default {
components: {
ownWaterTabbar
},
data() {
return {
menu: {},
cartList: [],
totalCartCount: 0,
totalPrice: "0.00",
isAllSelected: false,
addressList: [],
showAddressObj: {},
startX: 0,
delBtnWidth: 72 // 采用 shopCar 的 72px 宽度
};
},
onLoad() {
this.menu = uni.getMenuButtonBoundingClientRect();
this.handleGetAddressList();
},
onShow() {
this.syncCartData();
},
methods: {
syncCartData() {
let list = uni.getStorageSync("waterShopCar") || [];
this.cartList = list.map(item => ({ ...item, offsetX: 0 }));
this.calculateTotal();
},
touchStart(e, index) {
if (e.touches.length === 1) {
this.startX = e.touches[0].clientX;
}
},
touchMove(e, index) {
if (e.touches.length === 1) {
let moveX = e.touches[0].clientX;
let disX = this.startX - moveX;
let offsetX = 0;
if (disX > 0) {
offsetX = -disX;
if (disX >= this.delBtnWidth) offsetX = -this.delBtnWidth;
}
this.cartList[index].offsetX = offsetX;
}
},
touchEnd(e, index) {
if (this.cartList[index].offsetX < -this.delBtnWidth / 2) {
this.cartList[index].offsetX = -this.delBtnWidth;
} else {
this.cartList[index].offsetX = 0;
}
},
calculateTotal() {
let total = 0;
let count = 0;
let allSelected = this.cartList.length > 0;
this.cartList.forEach(item => {
count += (item.count || 0);
if (item.select) total += (item.COMMODITY_MEMBERPRICE * item.count);
else allSelected = false;
});
this.totalCartCount = count;
this.totalPrice = total.toFixed(2);
this.isAllSelected = allSelected;
},
toggleSelect(item) {
item.select = !item.select;
this.updateCache();
},
toggleAllSelect() {
const targetState = !this.isAllSelected;
this.cartList.forEach(item => item.select = targetState);
this.updateCache();
},
changeCount(item, delta) {
const newCount = (item.count || 0) + delta;
if (newCount < 1) return;
item.count = newCount;
this.updateCache();
},
handleInputCount(item) {
let count = parseInt(item.count);
if (isNaN(count) || count < 1) {
count = 1;
}
item.count = count;
this.updateCache();
},
deleteItem(index) {
uni.showModal({
title: "提示",
content: "确定要删除该商品吗?",
success: (res) => {
if (res.confirm) {
this.cartList.splice(index, 1);
this.updateCache();
}
}
});
},
updateCache() {
uni.setStorageSync("waterShopCar", this.cartList.map(i => {
let n = { ...i };
delete n.offsetX;
return n;
}));
this.calculateTotal();
},
async handleGetAddressList() {
try {
const data = await this.$api.getCoop({ action_type: "GetAddressList" });
this.addressList = data.Data.List || [];
const defaultAddr = this.addressList.find(i => i.ISDEFAULT === 1);
this.showAddressObj = defaultAddr || (this.addressList.length > 0 ? this.addressList[0] : {});
} catch (e) {
console.error(e);
}
},
handleShowAddress() { this.$refs.addressPopup.open(); },
selectAddress(item) {
this.showAddressObj = item;
this.$refs.addressPopup.close();
},
goAddAddress() { uni.navigateTo({ url: "/pages/myAddress/newAdd/index" }); },
handleGoPay() {
const selectedItems = this.cartList.filter(i => i.select);
if (selectedItems.length === 0) {
uni.showToast({ title: "请先勾选商品", icon: "none" });
return;
}
// 封装跳转参数
const params = {
shopList: selectedItems,
address: this.showAddressObj.ADDRESS + (this.showAddressObj.DOORPLATE || "")
};
uni.navigateTo({
url: `/pages/ownWater/waterSettlement?data=${encodeURIComponent(JSON.stringify(params))}`
});
}
}
};
</script>
<style lang="less" scoped>
.main {
background: #f5f5f5;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
/* 强力屏蔽可能出现的滚动条 */
::v-deep ::-webkit-scrollbar {
display: none !important;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
.header {
background: #fff;
padding-bottom: 20rpx;
z-index: 200;
.headerTop {
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: bold;
}
.addressBox {
margin: 20rpx 30rpx 0;
padding: 20rpx;
background: #f8f8f8;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: space-between;
.addrLeft {
flex: 1;
.addrTitle {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.fixedIcon {
width: 28rpx;
height: 28rpx;
margin-right: 12rpx;
}
.addrText {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
}
.addrUser {
font-size: 24rpx;
color: #666;
padding-left: 40rpx;
text {
margin-right: 16rpx;
}
}
}
.arrow {
width: 32rpx;
height: 32rpx;
opacity: 0.5;
}
}
}
.pageContent {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
.cartList {
height: 100%;
padding: 20rpx;
box-sizing: border-box;
.cartItemWrapper {
position: relative;
width: 100%;
margin-bottom: 20rpx;
border-radius: 16rpx;
overflow: hidden;
background: red; // 删除按钮的背景色
.cartItem {
position: relative;
background: #fff;
padding: 20rpx;
display: flex;
align-items: center;
z-index: 2;
transition: transform 0.2s ease-out;
.itemCheck {
margin-right: 10rpx;
transform: scale(0.8);
}
.itemImg {
width: 140rpx;
height: 140rpx;
border-radius: 8rpx;
margin-right: 20rpx;
flex-shrink: 0;
}
.itemInfo {
flex: 1;
overflow: hidden;
.itemTitle {
width: 100%;
font-size: 28rpx;
color: #333;
line-height: 36rpx;
margin-bottom: 20rpx;
// 精准还原 shopCar 的 line-clamp 逻辑
display: -webkit-box;
line-clamp: 2;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.itemBottom {
display: flex;
justify-content: space-between;
align-items: center;
.price {
color: #ff4d4f;
font-size: 30rpx;
font-weight: bold;
}
.stepper {
display: flex;
align-items: center;
border: 1px solid #ddd;
border-radius: 4rpx;
.stepBtn {
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f8f8f8;
}
.stepInput {
width: 60rpx;
height: 50rpx;
text-align: center;
font-size: 24rpx;
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
}
}
}
}
}
.itemDelete {
position: absolute;
top: 0;
right: 0;
width: 72px;
height: 100%;
background: red;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
z-index: 1;
font-weight: bold;
}
}
}
.emptyBox {
height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.emptyImg {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
.emptyText {
font-size: 28rpx;
color: #999;
}
}
}
.settleBar {
position: fixed;
bottom: calc(50px + env(safe-area-inset-bottom));
width: 100%;
height: 100rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
box-sizing: border-box;
border-top: 1rpx solid #eee;
z-index: 100;
.allSelect {
display: flex;
align-items: center;
font-size: 28rpx;
color: #666;
radio {
transform: scale(0.8);
}
}
.totalInfo {
display: flex;
align-items: center;
.totalPrice {
font-size: 28rpx;
color: #333;
margin-right: 24rpx;
.unit {
font-size: 22rpx;
color: #ff4d4f;
margin-left: 8rpx;
}
.money {
font-size: 36rpx;
font-weight: bold;
color: #ff4d4f;
}
}
.payBtn {
height: 72rpx;
line-height: 72rpx;
padding: 0 40rpx;
background: #4BCB7E;
color: #fff;
border-radius: 36rpx;
font-size: 28rpx;
font-weight: bold;
}
}
}
.addressPopupBox {
background: #fff;
border-top-left-radius: 32rpx;
border-top-right-radius: 32rpx;
// 预留出 Tabbar 的高度 (50px) 以及安全区域
padding-bottom: calc(50px + env(safe-area-inset-bottom));
.popHeader {
height: 100rpx;
line-height: 100rpx;
text-align: center;
font-size: 32rpx;
font-weight: bold;
border-bottom: 1rpx solid #eee;
}
.popList {
max-height: 60vh;
.addrItem {
padding: 30rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #f5f5f5;
.addrDetail {
margin-left: 20rpx;
.detailText {
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-bottom: 8rpx;
}
.detailInfo {
font-size: 24rpx;
color: #666;
}
}
}
}
.addAddrBtn {
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: #4BCB7E;
color: #fff;
margin: 20rpx 30rpx;
border-radius: 40rpx;
font-size: 28rpx;
}
}
}
</style>