213 lines
6.4 KiB
Vue
213 lines
6.4 KiB
Vue
<template>
|
||
<el-drawer
|
||
v-model="drawerVisible"
|
||
title="购物车"
|
||
direction="rtl"
|
||
size="400px"
|
||
>
|
||
<div class="cart-content h-full flex flex-col">
|
||
<!-- 购物车列表 -->
|
||
<div class="cart-items flex-1 overflow-y-auto p-4">
|
||
<div v-if="items.length === 0" class="empty-cart text-center py-8">
|
||
<div class="text-6xl mb-4">🛒</div>
|
||
<p class="text-gray-500">购物车是空的</p>
|
||
<el-button type="primary" @click="drawerVisible = false" class="mt-4">
|
||
去购物
|
||
</el-button>
|
||
</div>
|
||
|
||
<div v-else class="space-y-4">
|
||
<div
|
||
v-for="(item, index) in items"
|
||
:key="`${item.productId}-${item.color}-${item.size}`"
|
||
class="cart-item p-4 border border-gray-200 rounded-lg shadow-sm transition duration-300 ease-in-out hover:shadow-md"
|
||
>
|
||
<div class="flex space-x-3">
|
||
<!-- 商品图片 -->
|
||
<img
|
||
:src="item.cover"
|
||
:alt="item.title"
|
||
class="w-16 h-16 object-cover rounded"
|
||
>
|
||
|
||
<!-- 商品信息 -->
|
||
<div class="flex-1 min-w-0">
|
||
<h4 class="font-medium text-sm line-clamp-2 mb-1">{{ item.title }}</h4>
|
||
<div class="text-xs text-gray-500 mb-2">
|
||
{{ item.color }} / {{ item.size }}
|
||
</div>
|
||
<div class="flex items-center justify-between">
|
||
<span class="text-red-600 font-bold">¥{{ item.price }}</span>
|
||
|
||
<!-- 数量控制 -->
|
||
<div class="flex items-center space-x-2">
|
||
<el-button
|
||
size="small"
|
||
@click="updateQuantity(index, item.quantity - 1)"
|
||
:disabled="item.quantity <= 1"
|
||
>
|
||
-
|
||
</el-button>
|
||
<span class="w-8 text-center">{{ item.quantity }}</span>
|
||
<el-button
|
||
size="small"
|
||
@click="updateQuantity(index, item.quantity + 1)"
|
||
>
|
||
+
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 删除按钮 -->
|
||
<el-button
|
||
text
|
||
type="danger"
|
||
@click="removeItem(index)"
|
||
class="self-start"
|
||
>
|
||
<el-icon><Delete /></el-icon>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部结算 -->
|
||
<div v-if="items.length > 0" class="cart-footer border-t pt-4 mt-4 p-4">
|
||
<!-- 优惠信息 -->
|
||
<div class="discount-info mb-4 p-3 bg-yellow-50 rounded-lg shadow-sm">
|
||
<div class="flex items-center justify-between text-sm">
|
||
<span>满减优惠</span>
|
||
<span class="text-orange-600">-¥{{ discountAmount }}</span>
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">
|
||
满{{ discountThreshold }}元减{{ discountAmount }}元
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 总计 -->
|
||
<div class="total-section mb-4">
|
||
<div class="flex justify-between items-center mb-2">
|
||
<span>商品总计:</span>
|
||
<span class="text-red-600">¥{{ subtotal }}</span>
|
||
</div>
|
||
<div class="flex justify-between items-center mb-2">
|
||
<span>运费:</span>
|
||
<span class="text-green-600">{{ shippingFee === 0 ? '免运费' : `¥${shippingFee}` }}</span>
|
||
</div>
|
||
<div class="flex justify-between items-center mb-2">
|
||
<span>优惠:</span>
|
||
<span class="text-orange-600">-¥{{ discountAmount }}</span>
|
||
</div>
|
||
<div class="flex justify-between items-center text-lg font-bold border-t pt-2">
|
||
<span>总计:</span>
|
||
<span class="text-red-600">¥{{ finalTotal }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 结算按钮 -->
|
||
<el-button
|
||
type="primary"
|
||
size="large"
|
||
class="w-full py-3 rounded-lg shadow-md hover:shadow-lg transition duration-300 ease-in-out"
|
||
@click="handleCheckout"
|
||
>
|
||
去结算 ({{ totalQuantity }})
|
||
</el-button>
|
||
|
||
<!-- 继续购物 -->
|
||
<el-button
|
||
size="large"
|
||
class="w-full py-3 mt-2 rounded-lg border border-gray-300 hover:border-red-300 hover:text-red-600 transition duration-300 ease-in-out"
|
||
@click="drawerVisible = false"
|
||
>
|
||
继续购物
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-drawer>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed } from 'vue'
|
||
import { Delete } from '@element-plus/icons-vue'
|
||
|
||
const props = defineProps({
|
||
visible: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
items: {
|
||
type: Array,
|
||
default: () => []
|
||
}
|
||
})
|
||
|
||
const emit = defineEmits(['update:visible', 'checkout', 'update-quantity', 'remove-item'])
|
||
|
||
const drawerVisible = computed({
|
||
get: () => props.visible,
|
||
set: (value) => emit('update:visible', value)
|
||
})
|
||
|
||
// 计算总数量
|
||
const totalQuantity = computed(() => {
|
||
return props.items.reduce((total, item) => total + item.quantity, 0)
|
||
})
|
||
|
||
// 计算小计
|
||
const subtotal = computed(() => {
|
||
return props.items.reduce((total, item) => total + item.price * item.quantity, 0).toFixed(2)
|
||
})
|
||
|
||
// 运费计算
|
||
const shippingFee = computed(() => {
|
||
return parseFloat(subtotal.value) >= 99 ? 0 : 10
|
||
})
|
||
|
||
// 优惠计算
|
||
const discountThreshold = 200
|
||
const discountAmount = computed(() => {
|
||
return parseFloat(subtotal.value) >= discountThreshold ? 20 : 0
|
||
})
|
||
|
||
// 最终总计
|
||
const finalTotal = computed(() => {
|
||
const total = parseFloat(subtotal.value) + shippingFee.value - discountAmount.value
|
||
return Math.max(0, total).toFixed(2)
|
||
})
|
||
|
||
const updateQuantity = (index, quantity) => {
|
||
emit('update-quantity', index, quantity)
|
||
}
|
||
|
||
const removeItem = (index) => {
|
||
emit('remove-item', index)
|
||
}
|
||
|
||
const handleCheckout = () => {
|
||
emit('checkout')
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.cart-item:hover {
|
||
@apply border-red-300;
|
||
}
|
||
|
||
.empty-cart {
|
||
@apply text-gray-400;
|
||
}
|
||
|
||
.el-button--primary {
|
||
background-color: #dc3545;
|
||
border-color: #dc3545;
|
||
color: white;
|
||
}
|
||
|
||
.el-button--primary:hover {
|
||
background-color: #c82333;
|
||
border-color: #bd2130;
|
||
}
|
||
</style> |