|
@@ -0,0 +1,307 @@
|
|
|
|
+<template>
|
|
|
|
+ <div class="min-h-screen bg-gray-50">
|
|
|
|
+ <!-- 状态栏 -->
|
|
|
|
+ <div class="flex justify-between items-center px-4 py-2 bg-gradient-to-r from-blue-50 to-blue-100">
|
|
|
|
+ <div class="text-lg">14:38</div>
|
|
|
|
+ <div class="flex items-center gap-2">
|
|
|
|
+ <div class="text-sm">|||</div>
|
|
|
|
+ <WifiIcon class="w-4 h-4" />
|
|
|
|
+ <BatteryIcon class="w-4 h-4" />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 位置和商店信息 -->
|
|
|
|
+ <div class="bg-white px-4 py-2">
|
|
|
|
+ <div class="flex justify-between items-center">
|
|
|
|
+ <div class="flex items-center gap-1">
|
|
|
|
+ <MapPinIcon class="w-5 h-5" />
|
|
|
|
+ <span class="text-sm">解放路 2-1</span>
|
|
|
|
+ <ChevronDownIcon class="w-4 h-4" />
|
|
|
|
+ </div>
|
|
|
|
+ <div class="flex items-center gap-2">
|
|
|
|
+ <button class="text-sm">删除</button>
|
|
|
|
+ <MoreHorizontalIcon class="w-6 h-6" />
|
|
|
|
+ <CircleIcon class="w-6 h-6" />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 商店信息 -->
|
|
|
|
+ <div class="mt-3">
|
|
|
|
+ <div class="flex items-center gap-2">
|
|
|
|
+ <img src="/placeholder.svg?height=24&width=24" alt="store" class="w-6 h-6 rounded-full" />
|
|
|
|
+ <span class="font-medium">德龙大连西岗商场(721)</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="flex justify-between items-center mt-2 text-sm">
|
|
|
|
+ <div class="text-blue-500">18:00~20:30送达</div>
|
|
|
|
+ <div class="text-gray-500">已减9.0元运费</div>
|
|
|
|
+ <div class="text-blue-500">再逛逛 ></div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 整单换购 -->
|
|
|
|
+ <div class="bg-white mt-2 px-4 py-3">
|
|
|
|
+ <div class="flex justify-between items-center">
|
|
|
|
+ <div class="flex items-center">
|
|
|
|
+ <span class="bg-red-500 text-white text-xs px-2 py-0.5 rounded">整单换购</span>
|
|
|
|
+ <span class="text-sm ml-2">已购满29元,请参加换购</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="text-blue-500 text-sm">去换购 ></div>
|
|
|
|
+ </div>
|
|
|
|
+ <!-- 换购商品列表 -->
|
|
|
|
+ <div class="flex gap-4 overflow-x-auto no-scrollbar mt-4">
|
|
|
|
+ <div v-for="item in exchangeItems" :key="item.id" class="flex-shrink-0 w-24">
|
|
|
|
+ <img :src="item.image" :alt="item.name" class="w-24 h-24 object-cover rounded-lg" />
|
|
|
|
+ <div class="mt-2">
|
|
|
|
+ <div class="text-red-500 text-sm">¥{{ item.price }}</div>
|
|
|
|
+ <div class="text-gray-400 text-xs line-through">¥{{ item.originalPrice }}</div>
|
|
|
|
+ </div>
|
|
|
|
+ <button class="w-6 h-6 bg-blue-600 rounded-full flex items-center justify-center absolute right-0 bottom-0">
|
|
|
|
+ <PlusIcon class="w-4 h-4 text-white" />
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 购物车商品 -->
|
|
|
|
+ <div class="mt-2">
|
|
|
|
+ <div v-for="item in cartItems" :key="item.id" class="bg-white p-4 mb-2 flex">
|
|
|
|
+ <input
|
|
|
|
+ type="checkbox"
|
|
|
|
+ :checked="item.selected"
|
|
|
|
+ @change="toggleSelect(item.id)"
|
|
|
|
+ class="mr-3 w-5 h-5 rounded-full"
|
|
|
|
+ />
|
|
|
|
+ <div class="flex-1">
|
|
|
|
+ <div class="flex gap-4">
|
|
|
|
+ <img :src="item.image" :alt="item.name" class="w-20 h-20 object-cover rounded-lg" />
|
|
|
|
+ <div class="flex-1">
|
|
|
|
+ <h3 class="font-medium">{{ item.name }}</h3>
|
|
|
|
+ <div v-if="item.limit" class="inline-block bg-red-100 text-red-500 text-xs px-2 py-0.5 rounded mt-1">
|
|
|
|
+ 限购{{ item.limit }}件
|
|
|
|
+ </div>
|
|
|
|
+ <div class="flex justify-between items-center mt-2">
|
|
|
|
+ <div class="text-red-500 text-lg font-bold">¥{{ item.price }}</div>
|
|
|
|
+ <div class="flex items-center border rounded">
|
|
|
|
+ <button
|
|
|
|
+ class="px-3 py-1 border-r"
|
|
|
|
+ @click="updateQuantity(item.id, -1)"
|
|
|
|
+ >-</button>
|
|
|
|
+ <input
|
|
|
|
+ type="number"
|
|
|
|
+ v-model="item.quantity"
|
|
|
|
+ class="w-12 text-center py-1"
|
|
|
|
+ />
|
|
|
|
+ <button
|
|
|
|
+ class="px-3 py-1 border-l"
|
|
|
|
+ @click="updateQuantity(item.id, 1)"
|
|
|
|
+ >+</button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 热门推荐 -->
|
|
|
|
+ <div class="mt-4 px-4">
|
|
|
|
+ <div class="text-center mb-4">
|
|
|
|
+ <span class="text-xl font-bold">热 门 推 荐</span>
|
|
|
|
+ <span class="text-gray-400 text-sm block">Top Picks</span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 72小时热销榜 -->
|
|
|
|
+ <div class="bg-white rounded-lg p-4">
|
|
|
|
+ <div class="flex justify-between items-center mb-4">
|
|
|
|
+ <div class="flex items-center gap-2">
|
|
|
|
+ <img src="/placeholder.svg?height=24&width=24&text=🔥" alt="hot" class="w-6 h-6" />
|
|
|
|
+ <span class="font-bold">72小时热销榜</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="text-gray-500">邻里都爱买什么? ></div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 热销商品列表 -->
|
|
|
|
+ <div class="grid grid-cols-2 gap-4">
|
|
|
|
+ <div v-for="item in hotItems" :key="item.id" class="bg-white rounded-lg">
|
|
|
|
+ <img :src="item.image" :alt="item.name" class="w-full aspect-square object-cover rounded-lg" />
|
|
|
|
+ <div class="mt-2">
|
|
|
|
+ <h4 class="font-medium">{{ item.name }}</h4>
|
|
|
|
+ <div class="flex justify-between items-center mt-1">
|
|
|
|
+ <div class="text-red-500">¥{{ item.price }}</div>
|
|
|
|
+ <button class="w-6 h-6 bg-blue-600 rounded-full flex items-center justify-center">
|
|
|
|
+ <PlusIcon class="w-4 h-4 text-white" />
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 底部结算栏 -->
|
|
|
|
+ <div class="fixed bottom-16 left-0 right-0 bg-white border-t px-4 py-2 flex items-center justify-between">
|
|
|
|
+ <div class="flex items-center">
|
|
|
|
+ <input
|
|
|
|
+ type="checkbox"
|
|
|
|
+ :checked="allSelected"
|
|
|
|
+ @change="toggleSelectAll"
|
|
|
|
+ class="w-5 h-5 rounded-full mr-2"
|
|
|
|
+ />
|
|
|
|
+ <span>全选</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="flex items-center gap-4">
|
|
|
|
+ <div>
|
|
|
|
+ <div class="flex items-baseline">
|
|
|
|
+ <span>合计:</span>
|
|
|
|
+ <span class="text-red-500 text-xl font-bold">¥{{ totalAmount }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="text-xs text-gray-400">含包装费 ¥1</div>
|
|
|
|
+ </div>
|
|
|
|
+ <button class="bg-blue-600 text-white px-6 py-2 rounded-full">
|
|
|
|
+ 结算({{ selectedCount }})
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 底部导航 -->
|
|
|
|
+ <div class="fixed bottom-0 left-0 right-0 bg-white border-t flex justify-around py-2">
|
|
|
|
+ <div class="flex flex-col items-center text-gray-500">
|
|
|
|
+ <HomeIcon class="w-6 h-6" />
|
|
|
|
+ <span class="text-xs">首页</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="flex flex-col items-center text-gray-500">
|
|
|
|
+ <LayoutGridIcon class="w-6 h-6" />
|
|
|
|
+ <span class="text-xs">分类</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="flex flex-col items-center text-blue-600">
|
|
|
|
+ <div class="relative">
|
|
|
|
+ <ShoppingCartIcon class="w-6 h-6" />
|
|
|
|
+ <div class="absolute -top-1 -right-1 bg-red-500 text-white text-xs w-4 h-4 rounded-full flex items-center justify-center">
|
|
|
|
+ 2
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <span class="text-xs">购物车</span>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="flex flex-col items-center text-gray-500">
|
|
|
|
+ <UserIcon class="w-6 h-6" />
|
|
|
|
+ <span class="text-xs">我的</span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+
|
|
|
|
+ <script setup>
|
|
|
|
+ import { ref, computed } from 'vue';
|
|
|
|
+ import {
|
|
|
|
+ MapPin as MapPinIcon,
|
|
|
|
+ ChevronDown as ChevronDownIcon,
|
|
|
|
+ MoreHorizontal as MoreHorizontalIcon,
|
|
|
|
+ Circle as CircleIcon,
|
|
|
|
+ Plus as PlusIcon,
|
|
|
|
+ Wifi as WifiIcon,
|
|
|
|
+ Battery as BatteryIcon,
|
|
|
|
+ Home as HomeIcon,
|
|
|
|
+ LayoutGrid as LayoutGridIcon,
|
|
|
|
+ ShoppingCart as ShoppingCartIcon,
|
|
|
|
+ User as UserIcon
|
|
|
|
+ } from 'lucide-vue-next';
|
|
|
|
+
|
|
|
|
+ // 换购商品数据
|
|
|
|
+ const exchangeItems = ref([
|
|
|
|
+ { id: 1, name: '黄金玉米', price: '9.90', originalPrice: '15.80', image: '/placeholder.svg?height=96&width=96' },
|
|
|
|
+ { id: 2, name: '薯片三连包', price: '26.90', originalPrice: '29.90', image: '/placeholder.svg?height=96&width=96' },
|
|
|
|
+ { id: 3, name: '水磨糯米', price: '39.90', originalPrice: '44.50', image: '/placeholder.svg?height=96&width=96' },
|
|
|
|
+ { id: 4, name: '红薯', price: '16.80', originalPrice: '19.80', image: '/placeholder.svg?height=96&width=96' }
|
|
|
|
+ ]);
|
|
|
|
+
|
|
|
|
+ // 购物车商品数据
|
|
|
|
+ const cartItems = ref([
|
|
|
|
+ {
|
|
|
|
+ id: 1,
|
|
|
|
+ name: '臻选原味混合坚果 907g',
|
|
|
|
+ price: '113.00',
|
|
|
|
+ image: '/placeholder.svg?height=80&width=80',
|
|
|
|
+ quantity: 1,
|
|
|
|
+ selected: true
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: 2,
|
|
|
|
+ name: '臻选五常大米 5kg 季节限定',
|
|
|
|
+ price: '89.90',
|
|
|
|
+ image: '/placeholder.svg?height=80&width=80',
|
|
|
|
+ quantity: 1,
|
|
|
|
+ selected: true,
|
|
|
|
+ limit: 4
|
|
|
|
+ }
|
|
|
|
+ ]);
|
|
|
|
+
|
|
|
|
+ // 热门商品数据
|
|
|
|
+ const hotItems = ref([
|
|
|
|
+ {
|
|
|
|
+ id: 1,
|
|
|
|
+ name: '臻选100%苹果汁 1L',
|
|
|
|
+ price: '11.90',
|
|
|
|
+ plusPrice: '10.90',
|
|
|
|
+ image: '/placeholder.svg?height=160&width=160',
|
|
|
|
+ tags: ['满399减40', '满149减15']
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: 2,
|
|
|
|
+ name: '宜客鸡蛋面 1kg',
|
|
|
|
+ price: '14.80',
|
|
|
|
+ image: '/placeholder.svg?height=160&width=160',
|
|
|
|
+ rank: 1
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ id: 3,
|
|
|
|
+ name: '臻选精选鲜鸡蛋30枚 1.5kg',
|
|
|
|
+ price: '23.90',
|
|
|
|
+ image: '/placeholder.svg?height=160&width=160',
|
|
|
|
+ rank: 2
|
|
|
|
+ }
|
|
|
|
+ ]);
|
|
|
|
+
|
|
|
|
+ // 计算属性
|
|
|
|
+ const allSelected = computed(() => cartItems.value.every(item => item.selected));
|
|
|
|
+ const selectedCount = computed(() => cartItems.value.filter(item => item.selected).length);
|
|
|
|
+ const totalAmount = computed(() => {
|
|
|
|
+ const total = cartItems.value
|
|
|
|
+ .filter(item => item.selected)
|
|
|
|
+ .reduce((sum, item) => sum + parseFloat(item.price) * item.quantity, 0);
|
|
|
|
+ return total.toFixed(1);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 方法
|
|
|
|
+ const toggleSelect = (id) => {
|
|
|
|
+ const item = cartItems.value.find(item => item.id === id);
|
|
|
|
+ if (item) {
|
|
|
|
+ item.selected = !item.selected;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const toggleSelectAll = () => {
|
|
|
|
+ const newValue = !allSelected.value;
|
|
|
|
+ cartItems.value.forEach(item => item.selected = newValue);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const updateQuantity = (id, delta) => {
|
|
|
|
+ const item = cartItems.value.find(item => item.id === id);
|
|
|
|
+ if (item) {
|
|
|
|
+ const newQuantity = item.quantity + delta;
|
|
|
|
+ if (newQuantity > 0 && (!item.limit || newQuantity <= item.limit)) {
|
|
|
|
+ item.quantity = newQuantity;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ </script>
|
|
|
|
+
|
|
|
|
+ <style scoped>
|
|
|
|
+ .no-scrollbar {
|
|
|
|
+ -ms-overflow-style: none;
|
|
|
|
+ scrollbar-width: none;
|
|
|
|
+ }
|
|
|
|
+ .no-scrollbar::-webkit-scrollbar {
|
|
|
|
+ display: none;
|
|
|
|
+ }
|
|
|
|
+ </style>
|