@zuopngfei 26563409c9 wewe
2025-11-27 18:18:37 +08:00

571 lines
12 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="cart-page">
<!-- 购物车列表 -->
<view class="cart-list" v-if="cartList.length > 0">
<view class="cart-item" v-for="(item, index) in cartList" :key="item.id">
<view class="item-checkbox" @click="toggleSelect(index)">
<view class="checkbox" :class="{ checked: item.selected === 1 }">
<text class="iconfont icon-gou" v-if="item.selected === 1"></text>
</view>
</view>
<view class="item-image">
<image :src="item.product_image_url" mode="aspectFill"></image>
</view>
<view class="item-info">
<view class="item-title">{{ item.product_name || item.name }}</view>
<view class="item-spec" v-if="item.sku_name">
{{ item.sku_name }}
</view>
<view class="item-price-row">
<view class="item-price">
<text class="price-symbol"></text>
<text class="price-value">{{ formatPrice(item.price) || formatPrice(item.sku_price)
}}</text>
</view>
<view class="item-count">
<view class="count-btn" @click="decreaseCount(index)">-</view>
<view class="count-num">{{ item.quantity || item.count }}</view>
<view class="count-btn" @click="increaseCount(index)">+</view>
</view>
</view>
</view>
<view class="item-delete" @click="deleteItem(index)">
<text class="iconfont icon-cangpeitubiao_shanchu"></text>
</view>
</view>
</view>
<!-- 空购物车 -->
<view class="empty-cart" v-else>
<view class="empty-icon">
<text class="iconfont icon-gouwuche"></text>
</view>
<view class="empty-text">购物车是空的</view>
<view class="empty-tip">快去挑选心仪的商品吧~</view>
<view class="empty-btn" @click="goShopping">去逛逛</view>
</view>
<!-- 底部结算栏 -->
<view class="cart-footer" v-if="cartList.length > 0">
<view class="footer-left">
<view class="footer-checkbox" @click="toggleSelectAll">
<view class="checkbox" :class="{ checked: isAllSelected }">
<text class="iconfont icon-gou" v-if="isAllSelected"></text>
</view>
<text class="footer-text">全选</text>
</view>
</view>
<view class="footer-right">
<view class="footer-total">
<text class="total-label">合计</text>
<text class="total-price">{{ formatPrice(totalPrice) }}</text>
</view>
<view class="footer-btn" :class="{ disabled: selectedCount === 0 }" @click="checkout">
结算({{ selectedCount }})
</view>
</view>
</view>
</view>
</template>
<script>
import request from '@/api/request.js';
export default {
data() {
return {
cartList: [],
loading: false
};
},
computed: {
// 是否全选
isAllSelected() {
if (this.cartList.length === 0) return false;
return this.cartList.every(item => item.selected === 1);
},
// 已选商品数量
selectedCount() {
return this.cartList.filter(item => item.selected === 1).length;
},
// 总价
totalPrice() {
let total = 0;
this.cartList.forEach(item => {
if (item.selected === 1) {
const price = parseFloat(item.price || item.sku_price || 0);
const quantity = parseInt(item.quantity || item.count || 0);
total += price * quantity;
}
});
return total.toFixed(2);
}
},
onLoad() {
this.getUserIsLogin();
},
onShow() {
this.getUserIsLogin();
},
methods: {
async getUserIsLogin() {
const token = await uni.getStorageSync('access_token')
if (token) {
// 页面显示时刷新购物车
this.loadCartList();
} else {
uni.navigateTo({
url: '/pages/login/index'
})
}
},
formatPrice(value) {
if (!value) {
return 0.00;
}
// 分转换为元
value = value / 100;
// 保留两位小数
return value.toFixed(2);
},
// 加载购物车列表
async loadCartList() {
try {
this.loading = true;
const res = await request('xcx/carts', 'GET', { page: 1, page_size: 99 });
// 根据实际返回数据结构调整
const list = res.list || res.data || res || [];
// 为每个商品添加selected属性1表示选中2表示不选中
this.cartList = list.map(item => ({
...item,
selected: item.selected !== undefined ? item.selected : 1
}));
} catch (error) {
console.error('加载购物车失败:', error);
// 如果接口不存在,使用模拟数据
if (error.message && error.message.includes('404')) {
this.cartList = [];
} else {
uni.showToast({
title: '加载购物车失败',
icon: 'none'
});
}
} finally {
this.loading = false;
}
},
// 切换商品选择状态
toggleSelect(index) {
// 1表示选中2表示不选中
this.cartList[index].selected = this.cartList[index].selected === 1 ? 2 : 1;
this.updateCartItem(index);
},
// 全选/取消全选
toggleSelectAll() {
// 1表示选中2表示不选中
const selectAll = this.isAllSelected ? 2 : 1;
this.cartList.forEach((item, index) => {
item.selected = selectAll;
this.updateCartItem(index);
});
},
// 减少数量
async decreaseCount(index) {
const item = this.cartList[index];
const currentCount = parseInt(item.quantity || item.count || 1);
if (currentCount <= 1) {
uni.showToast({
title: '商品数量不能少于1',
icon: 'none'
});
return;
}
item.quantity = currentCount - 1;
item.count = currentCount - 1;
await this.updateCartItem(index);
},
// 增加数量
async increaseCount(index) {
const item = this.cartList[index];
const currentCount = parseInt(item.quantity || item.count || 1);
item.quantity = currentCount + 1;
item.count = currentCount + 1;
await this.updateCartItem(index);
},
// 更新购物车商品
async updateCartItem(index) {
const item = this.cartList[index];
try {
// 根据实际接口调整selected: 1表示选中2表示不选中
await request('xcx/cart/' + item.id, 'PUT', {
quantity: item.quantity || item.count,
selected: item.selected
});
} catch (error) {
console.error('更新购物车失败:', error);
// 如果接口不存在,只更新本地数据
}
},
// 删除商品
deleteItem(index) {
const item = this.cartList[index];
uni.showModal({
title: '提示',
content: '确定要删除这个商品吗?',
success: async (res) => {
if (res.confirm) {
try {
// 使用 DELETE 方法删除,与更新接口风格保持一致
await request('xcx/cart/' + item.id, 'DELETE');
// 删除成功后从列表中移除
this.cartList.splice(index, 1);
uni.showToast({
title: '删除成功',
icon: 'success',
duration: 1500
});
} catch (error) {
console.error('删除失败:', error);
// 如果接口调用失败,仍然删除本地数据(容错处理)
this.cartList.splice(index, 1);
uni.showToast({
title: '删除成功',
icon: 'success',
duration: 1500
});
}
}
}
});
},
// 结算
checkout() {
if (this.selectedCount === 0) {
uni.showToast({
title: '请选择要结算的商品',
icon: 'none'
});
return;
}
const selectedItems = this.cartList.filter(item => item.selected === 1);
// 将选中的商品信息传递给订单页面
const orderData = selectedItems.map(item => ({
cart_id: item.id,
product_id: item.product_id,
sku_id: item.sku_id,
quantity: item.quantity || item.count,
price: item.price || item.sku_price,
product_image_url: item.product_image_url
}));
// 跳转到订单页面,根据实际路由调整
uni.navigateTo({
url: `/pages/order/create?items=${encodeURIComponent(JSON.stringify(orderData))}`
});
},
// 去逛逛
goShopping() {
uni.switchTab({
url: '/pages/index/index'
});
}
}
};
</script>
<style scoped>
.cart-page {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
/* 购物车列表 */
.cart-list {
padding: 20rpx 0;
}
.cart-item {
display: flex;
align-items: center;
background-color: #fff;
margin-bottom: 20rpx;
padding: 30rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
position: relative;
}
.item-checkbox {
margin-right: 24rpx;
}
.checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ddd;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.checkbox.checked {
background: linear-gradient(135deg, #9810fa 0%, #7a0bc7 100%);
border-color: #9810fa;
}
.checkbox.checked .iconfont {
color: #fff;
font-size: 24rpx;
}
.item-image {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
overflow: hidden;
margin-right: 24rpx;
background-color: #f5f5f5;
}
.item-image image {
width: 100%;
height: 100%;
}
.item-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 160rpx;
}
.item-title {
font-size: 28rpx;
color: #333;
font-weight: 500;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
.item-spec {
margin-top: 12rpx;
font-size: 24rpx;
color: #999;
}
.item-price-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
}
.item-price {
color: #e7000b;
font-weight: bold;
}
.price-symbol {
font-size: 24rpx;
}
.price-value {
font-size: 32rpx;
}
.item-count {
display: flex;
align-items: center;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
overflow: hidden;
}
.count-btn {
width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #666;
background-color: #f8f8f8;
}
.count-btn:active {
background-color: #e8e8e8;
}
.count-num {
min-width: 60rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #333;
background-color: #fff;
border-left: 2rpx solid #e5e5e5;
border-right: 2rpx solid #e5e5e5;
}
.item-delete {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
margin-left: 20rpx;
padding: 10rpx;
box-sizing: border-box;
position: absolute;
top: 20rpx;
right: 20rpx;
}
.item-delete:active {
opacity: 0.6;
}
.item-delete .iconfont {
font-size: 36rpx;
color: #e7000b;
}
/* 空购物车 */
.empty-cart {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 200rpx;
}
.empty-icon {
width: 200rpx;
height: 200rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
border-radius: 50%;
margin-bottom: 40rpx;
}
.empty-icon .iconfont {
font-size: 120rpx;
color: #ccc;
}
.empty-text {
font-size: 32rpx;
color: #666;
margin-bottom: 16rpx;
}
.empty-tip {
font-size: 24rpx;
color: #999;
margin-bottom: 60rpx;
}
.empty-btn {
width: 240rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: linear-gradient(135deg, #9810fa 0%, #7a0bc7 100%);
color: #fff;
border-radius: 40rpx;
font-size: 28rpx;
}
/* 底部结算栏 */
.cart-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
z-index: 100;
}
.footer-left {
display: flex;
align-items: center;
}
.footer-checkbox {
display: flex;
align-items: center;
}
.footer-text {
margin-left: 16rpx;
font-size: 28rpx;
color: #333;
}
.footer-right {
display: flex;
align-items: center;
}
.footer-total {
display: flex;
align-items: baseline;
margin-right: 24rpx;
}
.total-label {
font-size: 28rpx;
color: #333;
}
.total-price {
font-size: 36rpx;
color: #e7000b;
font-weight: bold;
}
.footer-btn {
min-width: 180rpx;
height: 72rpx;
line-height: 72rpx;
text-align: center;
background: linear-gradient(135deg, #9810fa 0%, #7a0bc7 100%);
color: #fff;
border-radius: 36rpx;
font-size: 28rpx;
padding: 0 32rpx;
}
.footer-btn.disabled {
background: #ccc;
color: #999;
}
</style>