| | |
| | | <template> |
| | | <div class="container mx-auto px-4 py-8"> |
| | | <div class="bg-white rounded-lg shadow"> |
| | | <div class="bg-white rounded-lg shadow h-[780px]"> |
| | | <!-- 购物车头部 --> |
| | | <div class="p-4 border-b"> |
| | | <h1 class="text-xl font-medium">购物车</h1> |
| | | </div> |
| | | |
| | | <!-- 购物车主体 --> |
| | | <div class="p-4"> |
| | | <div class="p-4" style="height: calc(100% - 58px); box-sizing: border-box;"> |
| | | <!-- 表头 --> |
| | | <div class="flex items-center pb-4 border-b text-gray-500 text-sm"> |
| | | <div class="flex items-center pb-4 border-b text-gray-500 text-sm" style=" background-color: #f4f4f4; box-sizing: border-box; padding: 10px 15px;"> |
| | | <div class="w-12"> |
| | | <el-checkbox v-model="allSelected" @change="handleSelectAll" /> |
| | | <el-checkbox v-model="allSelected" /> |
| | | </div> |
| | | <div class="flex-1">商品</div> |
| | | <div class="w-24 text-center">单价</div> |
| | |
| | | <el-empty v-if="!cartItems.length" description="购物车还是空的" /> |
| | | |
| | | <!-- 商品列表 --> |
| | | <div v-else> |
| | | <div v-for="item in cartItems" :key="item.addID" class="flex items-center py-4 border-b"> |
| | | <div v-else style=" height: calc(100% - 116px);overflow: auto;"> |
| | | <div v-for="item in cartItems" :key="item.addID" class="flex items-center border-b" style="padding: 1rem;"> |
| | | <div class="w-12"> |
| | | <el-checkbox v-model="item.selected" @change="updateSelectedState" /> |
| | | <el-checkbox |
| | | v-model="item.selected" |
| | | @change="(val) => cartStore.updateItemSelected(item.addID, val)" |
| | | /> |
| | | </div> |
| | | <div class="flex-1 flex items-center gap-4"> |
| | | <el-image :src="item.image" class="w-20 h-20 object-cover rounded" /> |
| | |
| | | :min="1" |
| | | :max="99" |
| | | size="small" |
| | | @change="handleQuantityChange(item)" |
| | | @change="() => handleQuantityChange(item)" |
| | | /> |
| | | </div> |
| | | <div class="w-24 text-center text-red-500"> |
| | |
| | | </div> |
| | | |
| | | <!-- 购物车底部 --> |
| | | <div class="flex justify-between items-center mt-4 py-4"> |
| | | <div v-if="cartItems.length" class="flex justify-between items-center mt-4 py-4"> |
| | | <div class="flex items-center gap-4"> |
| | | <el-checkbox v-model="allSelected" @change="handleSelectAll">全选</el-checkbox> |
| | | <!-- <el-checkbox v-model="allSelected">全选</el-checkbox> --> |
| | | <el-button type="text" @click="removeSelected">删除选中</el-button> |
| | | </div> |
| | | <div class="flex items-center gap-4"> |
| | |
| | | 已选商品 <span class="text-red-500">{{ selectedCount }}</span> 件 |
| | | </div> |
| | | <div class="text-gray-500"> |
| | | 合计:<span class="text-red-500 text-xl font-medium">¥{{ totalPrice.toFixed(2) }}</span> |
| | | 合计:<span class=" text-lg font-bold" style="color: #16b5cb;font-size: large;">¥{{ totalPrice.toFixed(2) }}</span> |
| | | </div> |
| | | <el-button type="primary" :disabled="!selectedCount" @click="handleCheckout"> |
| | | <el-button |
| | | type="primary" |
| | | :disabled="!selectedCount" |
| | | @click="handleCheckout" |
| | | > |
| | | 结算 |
| | | </el-button> |
| | | </div> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { computed, ref, watch } from 'vue'; |
| | | import { computed } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | import { ElMessage, ElMessageBox } from 'element-plus'; |
| | | import { useCartStore } from '@/stores/useCartStore'; |
| | |
| | | const cartStore = useCartStore(); |
| | | |
| | | // 获取购物车数据 |
| | | const cartItems = computed(() => { |
| | | return cartStore.cartItems.map(item => ({ |
| | | ...item, |
| | | selected: false |
| | | })); |
| | | }); |
| | | const cartItems = computed(() => cartStore.items); |
| | | |
| | | // 全选状态 |
| | | const allSelected = ref(false); |
| | | const allSelected = computed({ |
| | | get: () => cartItems.value.length > 0 && cartItems.value.every(item => item.selected), |
| | | set: (val) => { |
| | | cartStore.updateAllSelected(val); |
| | | } |
| | | }); |
| | | |
| | | // 选中商品数量 |
| | | const selectedCount = computed(() => { |
| | |
| | | const totalPrice = computed(() => { |
| | | return cartItems.value |
| | | .filter(item => item.selected) |
| | | .reduce((total, item) => total + item.price * item.quantity, 0); |
| | | .reduce((total, item) => total + (item.price * item.quantity), 0); |
| | | }); |
| | | |
| | | // 全选/取消全选 |
| | | const handleSelectAll = (val: boolean) => { |
| | | cartItems.value.forEach(item => { |
| | | item.selected = val; |
| | | }); |
| | | }; |
| | | |
| | | // 更新选中状态 |
| | | const updateSelectedState = () => { |
| | | allSelected.value = cartItems.value.length > 0 && |
| | | cartItems.value.every(item => item.selected); |
| | | }; |
| | | |
| | | // 修改商品数量 |
| | | const handleQuantityChange = (item: any) => { |
| | | const updatedItems = cartItems.value.map(cartItem => |
| | | cartItem.addID === item.addID ? { ...cartItem, quantity: item.quantity } : cartItem |
| | | ); |
| | | cartStore.setCartItems(updatedItems); |
| | | cartStore.updateItemQuantity(item.addID, item.quantity); |
| | | }; |
| | | |
| | | // 删除单个商品 |
| | |
| | | await ElMessageBox.confirm('确定要删除该商品吗?', '提示', { |
| | | type: 'warning' |
| | | }); |
| | | const updatedItems = cartItems.value.filter(cartItem => cartItem.addID !== item.addID); |
| | | cartStore.setCartItems(updatedItems); |
| | | cartStore.removeFromCart(item.addID); |
| | | ElMessage.success('删除成功'); |
| | | } catch { |
| | | // 用户取消删除 |
| | |
| | | await ElMessageBox.confirm('确定要删除选中的商品吗?', '提示', { |
| | | type: 'warning' |
| | | }); |
| | | const updatedItems = cartItems.value.filter(item => !item.selected); |
| | | cartStore.setCartItems(updatedItems); |
| | | cartItems.value.forEach(item => { |
| | | if (item.selected) { |
| | | cartStore.removeFromCart(item.addID); |
| | | } |
| | | }); |
| | | ElMessage.success('删除成功'); |
| | | } catch { |
| | | // 用户取消删除 |
| | |
| | | } |
| | | router.push('/order-info'); |
| | | }; |
| | | |
| | | // 监听购物车数据变化 |
| | | watch(() => cartStore.cartItems, () => { |
| | | updateSelectedState(); |
| | | }, { deep: true }); |
| | | </script> |
| | | |
| | | <style scoped> |