<template>
<view class="order-page">
<!-- 顶部导航栏 -->
<view class="top-nav">
<view class="back-btn" @click="navigateBack">
<i class="iconfont icon-return"></i>
</view>
<view class="nav-title">
<text class="shop-name">{{ shopInfo.name }}</text>
<text class="shop-tag">外卖</text>
</view>
<view class="nav-actions">
<button class="action-btn" @click="showTel">
<i class="iconfont icon-tel"></i>
</button>
<button class="action-btn" @click="toggleCollect">
<i class="iconfont" :class="isCollected ? 'icon-collected' : 'icon-collect'"></i>
</button>
<button class="action-btn" @click="showShare">
<i class="iconfont icon-share"></i>
</button>
</view>
</view>
<!-- 商家信息栏 -->
<view class="shop-info-bar">
<view class="shop-rating">
<text class="rating-star">★</text>
<text class="rating-value">{{ shopInfo.rating }}</text>
<text class="rating-count">({{ shopInfo.ratingCount }}条)</text>
</view>
<view class="shop-sales">月售{{ shopInfo.monthSales }}单</view>
<view class="shop-distance">{{ shopInfo.distance }}km</view>
<view class="shop-delivery-time">{{ shopInfo.deliveryTime }}</view>
</view>
<!-- 主要内容区 -->
<view class="main-container">
<!-- 左侧分类栏 -->
<view class="category-list">
<view
v-for="(category, index) in categories"
:key="index"
class="category-item"
:class="{ active: activeCategory === index }"
@click="switchCategory(index)"
>
<text>{{ category.name }}</text>
<span class="category-badge" v-if="category.count > 0">{{ category.count }}</span>
</view>
</view>
<!-- 右侧商品列表 -->
<view class="goods-list">
<view v-for="(category, catIndex) in categories" :key="catIndex">
<view v-if="activeCategory === catIndex">
<view class="category-title">{{ category.name }}</view>
<view class="goods-items">
<view
class="goods-item"
v-for="(goods, goodsIndex) in category.goods"
:key="goodsIndex"
>
<image
class="goods-image"
:src="goods.image"
mode="widthFix"
:alt="goods.name"
></image>
<view class="goods-info">
<view class="goods-name">{{ goods.name }}</view>
<view class="goods-desc">{{ goods.description }}</view>
<view class="goods-price">
<text class="current-price">¥{{ goods.price.toFixed(2) }}</text>
<text class="original-price" v-if="goods.originalPrice">¥{{ goods.originalPrice.toFixed(2) }}</text>
</view>
<view class="goods-actions">
<view class="quantity-control" v-if="goods.quantity > 0">
<!-- 减少数量-->
<button class="btn minus" @click="decreaseQuantity(catIndex, goodsIndex)">
-
</button>
<input
type="number"
class="quantity-input"
:value="goods.quantity"
@input="handleInput(catIndex, goodsIndex, $event)"
@blur="updateQuantity(catIndex, goodsIndex, $event.target.value)"
>
</input>
<!-- 增加数量-->
<button class="btn plus" @click="increaseQuantity(catIndex, goodsIndex)">
+
</button>
</view>
<button
class="add-btn"
v-else
@click="increaseQuantity(catIndex, goodsIndex)"
>
+
</button>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 底部购物车栏 -->
<view class="cart-bar">
<view class="cart-icon" @click="toggleCart">
<i class="iconfont icon-shopping-cart"></i>
<span class="cart-count" v-if="totalCount > 0">{{ totalCount }}</span>
</view>
<view class="cart-info">
<view class="price-info">
<span class="total-price">¥{{ totalPrice.toFixed(2) }}</span>
<span class="min-order" v-if="totalPrice < shopInfo.minOrderAmount">
另需{{ (shopInfo.minOrderAmount - totalPrice).toFixed(2) }}元起送
</span>
</view>
<button
class="checkout-btn"
:disabled="totalCount === 0 || totalPrice < shopInfo.minOrderAmount"
@click="gotoCheckout"
>
去结算
</button>
</view>
</view>
<!-- 购物车弹窗 -->
<view class="cart-popup" v-if="showCart">
<view class="cart-overlay" @click="toggleCart"></view>
<view class="cart-content">
<view class="cart-header">
<text class="cart-title">已选商品</text>
<button class="clear-cart" @click="clearCart" v-if="totalCount > 0">
清空
</button>
</view>
<!-- 过滤出有商品的分类 -->
<view class="cart-goods-list" v-if="totalCount > 0">
<view
class="cart-goods-item"
v-for="(category, catIndex) in categories.filter(cat => cat.goods.some(good => good.quantity > 0))"
:key="catIndex"
>
<view
class="cart-goods-inner"
v-for="(goods, goodsIndex) in category.goods.filter(good => good.quantity > 0)"
:key="goodsIndex"
>
<view class="cart-goods-container">
<image class="cart-goods-image" :src="goods.image" mode="aspectFill"></image>
<view class="cart-goods-info">
<text class="cart-goods-name">{{ goods.name }}</text>
<text class="cart-goods-desc" v-if="goods.description">{{ goods.description }}</text>
<text class="cart-goods-price">¥{{ goods.price.toFixed(2) }}</text>
</view>
<view class="cart-quantity-control">
<button class="btn minus" @click="decreaseQuantity(getOriginalCatIndex(category), getOriginalGoodsIndex(category, goods))">
-
</button>
<input
type="number"
class="quantity-input"
:value="goods.quantity"
@input="handleInput(getOriginalCatIndex(category), getOriginalGoodsIndex(category, goods), $event)"
@blur="updateQuantity(getOriginalCatIndex(category), getOriginalGoodsIndex(category, goods), $event.target.value)"
>
<button class="btn plus" @click="increaseQuantity(getOriginalCatIndex(category), getOriginalGoodsIndex(category, goods))">
+
</button>
</view>
</view>
</view>
</view>
</view>
<view class="empty-cart" v-else>
<i class="iconfont icon-empty"></i>
<text>购物车还是空的~</text>
</view>
<view class="cart-footer" v-if="totalCount > 0">
<view class="cart-total">
<text>合计:</text>
<text class="cart-total-price">¥{{ totalPrice.toFixed(2) }}</text>
</view>
<button
class="cart-checkout-btn"
:disabled="totalPrice < shopInfo.minOrderAmount"
@click="gotoCheckout"
>
去结算
</button>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 商家信息
shopInfo: {
name: "麦当劳(科技园店)",
rating: 4.8,
ratingCount: 3241,
monthSales: 2345,
distance: 0.8,
deliveryTime: "25-35分钟",
minOrderAmount: 20,
deliveryFee: 5,
discounts: [
"满50减10",
"满80减20",
"首单立减5元"
]
},
// 商品分类
categories: [
{
name: "招牌套餐",
count: 0,
goods: [
{
name: "巨无霸套餐",
description: "巨无霸汉堡+薯条(中)+可乐(中)",
price: 39.5,
originalPrice: 49,
image: "https://picsum.photos/seed/burger1/100/100",
quantity: 0
},
{
name: "麦辣鸡腿堡套餐",
description: "麦辣鸡腿堡+薯条(中)+可乐(中)",
price: 35,
originalPrice: 45,
image: "https://picsum.photos/seed/burger2/100/100",
quantity: 0
}
]
},
{
name: "汉堡",
count: 0,
goods: [
{
name: "巨无霸",
description: "100%纯牛肉,搭配独特酱料",
price: 22,
image: "https://picsum.photos/seed/burger3/100/100",
quantity: 0
},
{
name: "麦辣鸡腿堡",
description: "香辣可口,回味无穷",
price: 19,
image: "https://picsum.photos/seed/burger4/100/100",
quantity: 0
}
]
},
{
name: "小食",
count: 0,
goods: [
{
name: "薯条(大)",
description: "金黄酥脆,口感绝佳",
price: 12,
image: "https://picsum.photos/seed/fries1/100/100",
quantity: 0
},
{
name: "麦乐鸡(10块)",
description: "外酥里嫩,配四种蘸酱",
price: 15,
image: "https://picsum.photos/seed/nuggets1/100/100",
quantity: 0
}
]
},
{
name: "饮料",
count: 0,
goods: [
{
name: "可乐(中)",
description: "冰爽可口,畅饮一夏",
price: 8,
image: "https://picsum.photos/seed/drink1/100/100",
quantity: 0
},
{
name: "雪碧(中)",
description: "清新爽口,透心凉",
price: 8,
image: "https://picsum.photos/seed/drink2/100/100",
quantity: 0
}
]
}
],
activeCategory: 0, // 当前激活的分类
showCart: false, // 是否显示购物车
isCollected: false // 是否收藏
};
},
computed: {
// 计算购物车商品总数
totalCount() {
let count = 0;
this.categories.forEach(category => {
category.goods.forEach(goods => {
count += goods.quantity;
});
});
return count;
},
// 计算购物车总价
totalPrice() {
let price = 0;
this.categories.forEach(category => {
category.goods.forEach(goods => {
price += goods.price * goods.quantity;
});
});
return price;
}
},
watch: {
// 监听购物车变化,更新分类计数
totalCount() {
this.updateCategoryCounts();
}
},
methods: {
// 返回上一页
navigateBack() {
uni.navigateBack();
},
// 切换分类
switchCategory(index) {
this.activeCategory = index;
},
// 增加商品数量
increaseQuantity(catIndex, goodsIndex) {
this.$set(
this.categories[catIndex].goods[goodsIndex],
'quantity',
this.categories[catIndex].goods[goodsIndex].quantity + 1
);
},
// 减少商品数量
decreaseQuantity(catIndex, goodsIndex) {
if (this.categories[catIndex].goods[goodsIndex].quantity > 0) {
this.$set(
this.categories[catIndex].goods[goodsIndex],
'quantity',
this.categories[catIndex].goods[goodsIndex].quantity - 1
);
}
},
// 实时处理输入框变化(解决输入无反应问题)
handleInput(catIndex, goodsIndex, e) {
const value = e.target.value;
// 实时限制只能输入正整数
if (/^\d*$/.test(value)) {
const quantity = parseInt(value) || 0;
this.$set(
this.categories[catIndex].goods[goodsIndex],
'quantity',
quantity
);
}
},
// 输入框失焦后确认更新
updateQuantity(catIndex, goodsIndex, value) {
// 确保数量为正整数
let quantity = parseInt(value) || 0;
quantity = Math.max(0, quantity);
this.$set(
this.categories[catIndex].goods[goodsIndex],
'quantity',
quantity
);
},
// 更新分类计数
updateCategoryCounts() {
this.categories.forEach((category, catIndex) => {
let count = 0;
category.goods.forEach(goods => {
count += goods.quantity;
});
this.$set(this.categories[catIndex], 'count', count);
});
},
// 切换购物车显示
toggleCart() {
this.showCart = !this.showCart;
},
// 清空购物车
clearCart() {
uni.showModal({
title: '提示',
content: '确定要清空购物车吗?',
success: (res) => {
if (res.confirm) {
this.categories.forEach((category, catIndex) => {
category.goods.forEach((goods, goodsIndex) => {
this.$set(
this.categories[catIndex].goods[goodsIndex],
'quantity',
0
);
});
});
}
}
});
},
// 去结算
gotoCheckout() {
if (this.totalCount === 0) {
uni.showToast({
title: '请选择商品',
icon: 'none'
});
return;
}
if (this.totalPrice < this.shopInfo.minOrderAmount) {
uni.showToast({
title: `还差¥${(this.shopInfo.minOrderAmount - this.totalPrice).toFixed(2)}起送`,
icon: 'none'
});
return;
}
// 跳转到结算页面
this.showCart = false;
console.log('前往结算页面');
// uni.navigateTo({
// url: '/pages/checkout/checkout'
// });
},
// 切换收藏状态
toggleCollect() {
this.isCollected = !this.isCollected;
uni.showToast({
title: this.isCollected ? '收藏成功' : '取消收藏',
icon: 'none'
});
},
// 显示电话
showTel() {
uni.showModal({
title: '联系商家',
content: '13800138000',
showCancel: true
});
},
// 显示分享
showShare() {
uni.showActionSheet({
itemList: ['分享给好友', '分享到朋友圈'],
success: function(res) {
console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
}
});
},
// 获取原始分类索引
getOriginalCatIndex(filteredCategory) {
return this.categories.findIndex(cat => cat.name === filteredCategory.name);
},
// 获取原始商品索引
getOriginalGoodsIndex(category, goods) {
return category.goods.findIndex(g => g.name === goods.name);
}
}
};
</script>
<style scoped>
/* 基础样式 */
.order-page {
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
background-color: #f5f5f5;
min-height: 100vh;
}
/* 顶部导航栏 */
.top-nav {
display: flex;
align-items: center;
height: 48px;
background-color: #fff;
padding: 0 16px;
border-bottom: 1px solid #eee;
}
.back-btn {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
flex: 1;
text-align: center;
}
.shop-name {
font-size: 17px;
font-weight: 500;
margin-right: 8px;
}
.shop-tag {
font-size: 12px;
color: #fff;
background-color: #ff6700;
padding: 1px 5px;
border-radius: 3px;
vertical-align: middle;
}
.nav-actions {
display: flex;
width: 120px;
justify-content: flex-end;
}
.action-btn {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
padding: 0;
}
/* 商家信息栏 */
.shop-info-bar {
display: flex;
padding: 12px 16px;
background-color: #fff;
font-size: 12px;
color: #666;
border-bottom: 1px solid #eee;
overflow-x: auto;
white-space: nowrap;
}
.shop-rating {
margin-right: 16px;
display: flex;
align-items: center;
}
.rating-star {
color: #ffce31;
margin-right: 4px;
}
.rating-value {
color: #ff6700;
font-weight: 500;
margin-right: 4px;
}
.shop-sales, .shop-distance, .shop-delivery-time {
margin-right: 16px;
}
/* 主要内容区 */
.main-container {
display: flex;
height: calc(100vh - 48px - 52px - 56px);
overflow: hidden;
}
/* 左侧分类栏 - 宽度优化 */
.category-list {
width: 80px;
background-color: #f5f5f5;
overflow-y: auto;
}
.category-item {
padding: 14px 8px;
text-align: center;
font-size: 13px;
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.category-item.active {
background-color: #fff;
color: #ff6700;
font-weight: 500;
}
.category-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 3px;
height: 100%;
background-color: #ff6700;
}
.category-badge {
position: absolute;
right: 8px;
top: 10px;
background-color: #ff6700;
color: #fff;
font-size: 10px;
width: 16px;
height: 16px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
/* 右侧商品列表 */
.goods-list {
flex: 1;
overflow-y: auto;
background-color: #fff;
}
.category-title {
padding: 8px 16px;
font-size: 12px;
color: #999;
background-color: #f5f5f5;
}
.goods-items {
width: 100%;
padding: 0 16px;
}
.goods-item {
display: flex;
padding: 12px 0;
border-bottom: 1px solid #eee;
align-items: flex-start;
}
.goods-image {
width: 70px;
height: 70px;
border-radius: 6px;
object-fit: cover;
}
.goods-info {
flex: 1;
margin-left: 12px;
min-height: 70px;
display: flex;
flex-direction: column;
}
.goods-name {
font-size: 14px;
color: #333;
margin-bottom: 4px;
line-height: 1.4;
}
.goods-desc {
font-size: 12px;
color: #999;
margin-bottom: 8px;
flex: 1;
line-height: 1.3;
}
.goods-price {
display: flex;
align-items: center;
}
.current-price {
font-size: 14px;
color: #ff4d4f;
font-weight: 500;
}
.original-price {
font-size: 12px;
color: #999;
text-decoration: line-through;
margin-left: 6px;
}
.goods-actions {
display: flex;
align-items: flex-end;
justify-content: flex-end;
margin-top: -30rpx;
/* margin-top: 99rpx; */
}
.add-btn {
width: 26px;
height: 26px;
border-radius: 50%;
background-color: #ff6700;
color: #fff;
border: none;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
.quantity-control {
display: flex;
align-items: center;
}
.quantity-control .btn {
width: 26px;
height: 26px;
border-radius: 50%;
background-color: #f2f2f2;
color: #333;
border: none;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
.quantity-input {
width: 32px;
height: 26px;
text-align: center;
font-size: 14px;
border: none;
background-color: transparent;
padding: 0;
margin: 0 2px;
}
/* 底部购物车栏 */
.cart-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 56px;
background-color: #fff;
display: flex;
align-items: center;
padding: 0 16px;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
z-index: 10;
}
.cart-icon {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #ff6700;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin-top: -25px;
position: relative;
}
.cart-count {
position: absolute;
top: -5px;
right: -5px;
width: 20px;
height: 20px;
background-color: #ff4d4f;
color: #fff;
border-radius: 50%;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
overflow: auto;
}
.cart-info {
flex: 1;
margin-left: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.price-info {
display: flex;
align-items: center;
}
.total-price {
font-size: 16px;
color: #ff4d4f;
font-weight: 500;
}
.min-order {
font-size: 12px;
color: #ff4d4f;
margin-left: 8px;
}
.checkout-btn {
width: 110px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background-color: #ff6700;
color: #fff;
border: none;
border-radius: 20px;
font-size: 15px;
}
.checkout-btn:disabled {
background-color: #ccc;
color: #999;
}
/* 购物车弹窗 */
.cart-popup {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 100;
}
.cart-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 100;
}
.cart-content {
background-color: #fff;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
max-height: 70vh;
z-index: 101;
position: relative;
animation: slideUp 0.3s ease-out forwards;
}
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
.cart-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 16px;
border-bottom: 1px solid #eee;
}
.cart-title {
font-size: 16px;
font-weight: 500;
}
.clear-cart {
color: #666;
background-color: transparent;
border: none;
font-size: 14px;
padding: 0;
}
.cart-goods-list {
max-height: calc(70vh - 130px);
overflow-y: auto;
padding: 0 16px;
}
.cart-goods-item {
padding: 10px 0;
}
.cart-goods-inner {
padding: 5px 0;
}
.cart-goods-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
}
.cart-goods-image {
width: 60px;
height: 60px;
border-radius: 6px;
margin-right: 10px;
}
.cart-goods-info {
flex: 1;
display: flex;
flex-direction: column;
}
.cart-goods-name {
font-size: 14px;
color: #333;
margin-bottom: 4px;
font-weight: 500;
}
.cart-goods-desc {
font-size: 12px;
color: #999;
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cart-goods-price {
font-size: 14px;
color: #ff4d4f;
}
.cart-quantity-control {
display: flex;
align-items: center;
justify-content: flex-end;
margin-left: 10px;
}
.cart-quantity-control .btn {
width: 26px;
height: 26px;
border-radius: 50%;
background-color: #f2f2f2;
color: #333;
border: none;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
.cart-quantity-control .quantity-input {
width: 32px;
height: 26px;
text-align: center;
font-size: 14px;
border: none;
background-color: transparent;
padding: 0;
margin: 0 2px;
}
.empty-cart {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 0;
color: #999;
}
.empty-cart .icon-empty {
font-size: 60px;
margin-bottom: 16px;
}
.cart-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
border-top: 1px solid #eee;
background-color: #fff;
position: sticky;
bottom: 0;
}
.cart-total {
font-size: 14px;
}
.cart-total-price {
font-size: 18px;
color: #ff4d4f;
font-weight: 500;
margin-left: 6px;
}
.cart-checkout-btn {
width: 130px;
height: 44px;
background-color: #ff6700;
color: #fff;
border: none;
border-radius: 22px;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
}
.cart-checkout-btn:disabled {
background-color: #ccc;
color: #999;
}
/* 图标样式 */
.iconfont {
font-family: "iconfont" !important;
font-size: 20px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-return:before { content: "\e601"; }
.icon-tel:before { content: "\e602"; }
.icon-collect:before { content: "\e603"; }
.icon-collected:before { content: "\e604"; }
.icon-share:before { content: "\e605"; }
.icon-discount:before { content: "\e606"; }
.icon-arrow-right:before { content: "\e607"; }
.icon-shopping-cart:before { content: "\e608"; }
.icon-plus:before { content: "\e609"; }
.icon-minus:before { content: "\e610"; }
.icon-empty:before { content: "\e611"; }
</style>
微信小程序购餐页面+购物车模板
最新推荐文章于 2025-08-17 13:02:36 发布
612

被折叠的 条评论
为什么被折叠?



