584 lines
19 KiB
Vue
584 lines
19 KiB
Vue
<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>
|