Some checks reported errors
continuous-integration/drone/push Build encountered an error
581 lines
12 KiB
Vue
581 lines
12 KiB
Vue
<template>
|
|
<view class="shop-detail-page">
|
|
<!-- 自定义导航栏 -->
|
|
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
|
<view class="navbar-content">
|
|
<view class="nav-back" @click="goBack">
|
|
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
|
</view>
|
|
<text class="navbar-title">门店详情</text>
|
|
<view class="nav-placeholder"></view>
|
|
</view>
|
|
</view>
|
|
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
|
|
|
<!-- 门店 Banner -->
|
|
<swiper v-if="shop.banners && shop.banners.length > 0" class="shop-banner" indicator-dots autoplay circular
|
|
indicator-color="rgba(255,255,255,0.5)" indicator-active-color="#ffffff">
|
|
<swiper-item v-for="(banner, index) in shop.banners" :key="index">
|
|
<image class="banner-image" :src="banner.imageUrl" mode="aspectFill"></image>
|
|
</swiper-item>
|
|
</swiper>
|
|
|
|
<!-- 门店信息卡片 -->
|
|
<view class="shop-info-card">
|
|
<image class="shop-avatar" :src="shop.photo || '/static/ic_default.png'" mode="aspectFill"></image>
|
|
<view class="shop-info-text">
|
|
<text class="shop-name">{{ shop.name }}</text>
|
|
<view class="shop-location-row">
|
|
<image class="location-icon" src="/static/ic_address.png" mode="aspectFit"></image>
|
|
<text class="shop-location">{{ shop.location || '暂无位置信息' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 注意事项 -->
|
|
<view v-if="shop.notice" class="notice-section">
|
|
<text class="notice-text">{{ shop.notice }}</text>
|
|
</view>
|
|
|
|
<!-- 菜品列表 -->
|
|
<view class="dish-list">
|
|
<view class="dish-item" v-for="dish in dishes" :key="dish.id">
|
|
<image class="dish-photo" :src="dish.photo" mode="aspectFill"></image>
|
|
<view class="dish-info">
|
|
<text class="dish-name">{{ dish.name }}</text>
|
|
<view class="dish-bottom">
|
|
<text class="dish-price">¥{{ dish.price.toFixed(1) }}</text>
|
|
<view class="dish-actions">
|
|
<image class="action-icon" src="/static/ic_reduce.png" mode="aspectFit" v-if="getQuantity(dish.id) > 0" @click="onMinus(dish)"></image>
|
|
<text class="dish-quantity"
|
|
v-if="getQuantity(dish.id) > 0">{{ getQuantity(dish.id) }}</text>
|
|
<image class="action-icon" src="/static/ic_add.png" mode="aspectFit" @click="onPlus(dish)"></image>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 底部购物车栏 -->
|
|
<view class="cart-bar">
|
|
<view class="cart-left" @click="cartTotalCount > 0 && (showCartPopup = true)">
|
|
<image class="cart-icon" src="/static/ic_courier.png" mode="aspectFit"></image>
|
|
<text class="cart-price">¥ {{ cartTotalCount > 0 ? cartTotalPrice : '0' }}</text>
|
|
</view>
|
|
<button class="cart-checkout-btn" :class="{ disabled: cartTotalCount === 0 }"
|
|
@click="goCheckout">去结算</button>
|
|
</view>
|
|
|
|
<!-- 购物车弹窗 -->
|
|
<view class="cart-popup-mask" v-if="showCartPopup" @click="showCartPopup = false">
|
|
<view class="cart-popup" @click.stop>
|
|
<view class="cart-popup-header">
|
|
<text class="cart-popup-title">购物车</text>
|
|
<text class="cart-popup-clear" @click="clearCart">清空</text>
|
|
</view>
|
|
<scroll-view scroll-y class="cart-popup-list">
|
|
<view v-for="shopGroup in cartStore.shopList" :key="shopGroup.shopInfo.id" class="cart-shop-group">
|
|
<text class="cart-shop-name">{{ shopGroup.shopInfo.name }}</text>
|
|
<view class="cart-popup-item" v-for="item in shopGroup.items" :key="item.dish.id">
|
|
<image class="cart-item-photo" :src="item.dish.photo" mode="aspectFill"></image>
|
|
<view class="cart-item-info">
|
|
<text class="cart-item-name">{{ item.dish.name }}</text>
|
|
<text class="cart-item-price">¥{{ item.dish.price.toFixed(1) }}</text>
|
|
</view>
|
|
<view class="dish-actions">
|
|
<image class="action-icon" src="/static/ic_reduce.png" mode="aspectFit" @click="onPopupMinus(shopGroup.shopInfo.id, item.dish)"></image>
|
|
<text class="dish-quantity">{{ item.quantity }}</text>
|
|
<image class="action-icon" src="/static/ic_add.png" mode="aspectFit" @click="onPopupPlus(shopGroup.shopInfo.id, item.dish)"></image>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
<view class="cart-popup-footer">
|
|
<view class="cart-fee-row" v-if="cartStore.packingFee > 0">
|
|
<text class="cart-fee-label">打包费</text>
|
|
<text class="cart-fee-value">¥{{ cartStore.packingFee.toFixed(2) }}</text>
|
|
</view>
|
|
<view class="cart-fee-row">
|
|
<text class="cart-fee-label">总价</text>
|
|
<text class="cart-fee-total">¥{{ cartStore.totalPriceWithFee.toFixed(2) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
getShopDetail
|
|
} from '../../utils/api'
|
|
import {
|
|
useCartStore
|
|
} from '../../stores/cart'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
shop: {},
|
|
dishes: [],
|
|
showCartPopup: false,
|
|
statusBarHeight: 0
|
|
}
|
|
},
|
|
computed: {
|
|
cartStore() {
|
|
return useCartStore()
|
|
},
|
|
cartTotalCount() {
|
|
return this.cartStore.totalCount
|
|
},
|
|
cartTotalPrice() {
|
|
return this.cartStore.totalPrice.toFixed(2)
|
|
}
|
|
},
|
|
onLoad(options) {
|
|
const sysInfo = uni.getSystemInfoSync()
|
|
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
|
if (options.id) this.loadShopDetail(options.id)
|
|
},
|
|
methods: {
|
|
goBack() {
|
|
uni.navigateBack()
|
|
},
|
|
async loadShopDetail(id) {
|
|
try {
|
|
const res = await getShopDetail(id)
|
|
this.shop = res || {}
|
|
this.dishes = res.dishes || []
|
|
this.cartStore.setShopInfo({
|
|
id: this.shop.id,
|
|
name: this.shop.name,
|
|
photo: this.shop.photo,
|
|
packingFeeType: this.shop.packingFeeType,
|
|
packingFeeAmount: this.shop.packingFeeAmount
|
|
})
|
|
} catch (e) {}
|
|
},
|
|
getQuantity(dishId) {
|
|
return this.cartStore.getQuantity(dishId)
|
|
},
|
|
onPlus(dish) {
|
|
this.cartStore.addItem(dish)
|
|
},
|
|
onMinus(dish) {
|
|
this.cartStore.removeItem(dish.id)
|
|
},
|
|
clearCart() {
|
|
this.cartStore.clearCart()
|
|
// 清空后重新设置当前门店信息,确保可以继续加购
|
|
this.cartStore.setShopInfo({
|
|
id: this.shop.id,
|
|
name: this.shop.name,
|
|
photo: this.shop.photo,
|
|
packingFeeType: this.shop.packingFeeType,
|
|
packingFeeAmount: this.shop.packingFeeAmount
|
|
})
|
|
this.showCartPopup = false
|
|
},
|
|
/** 弹窗中减少指定门店的菜品 */
|
|
onPopupMinus(shopId, dish) {
|
|
const prevShopId = this.cartStore.currentShopId
|
|
this.cartStore.currentShopId = shopId
|
|
this.cartStore.removeItem(dish.id)
|
|
this.cartStore.currentShopId = prevShopId
|
|
// 购物车清空后关闭弹窗
|
|
if (this.cartStore.totalCount === 0) this.showCartPopup = false
|
|
},
|
|
/** 弹窗中增加指定门店的菜品 */
|
|
onPopupPlus(shopId, dish) {
|
|
const prevShopId = this.cartStore.currentShopId
|
|
this.cartStore.currentShopId = shopId
|
|
this.cartStore.addItem(dish)
|
|
this.cartStore.currentShopId = prevShopId
|
|
},
|
|
goCheckout() {
|
|
if (this.cartTotalCount === 0) return
|
|
uni.navigateTo({
|
|
url: '/pages/food/food-order'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.shop-detail-page {
|
|
padding-bottom: 140rpx;
|
|
background-color: #f5f5f5;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* 自定义导航栏 */
|
|
.custom-navbar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
z-index: 999;
|
|
background: #FFB700;
|
|
}
|
|
|
|
.navbar-content {
|
|
height: 44px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 20rpx;
|
|
}
|
|
|
|
.nav-back {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.back-icon {
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
}
|
|
|
|
.navbar-title {
|
|
font-size: 34rpx;
|
|
font-weight: bold;
|
|
color: #363636;
|
|
}
|
|
|
|
.nav-placeholder {
|
|
width: 60rpx;
|
|
}
|
|
|
|
/* Banner */
|
|
.shop-banner {
|
|
width: 100%;
|
|
height: 360rpx;
|
|
}
|
|
|
|
.banner-image {
|
|
width: 100%;
|
|
height: 360rpx;
|
|
}
|
|
|
|
/* 门店信息卡片 */
|
|
.shop-info-card {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 24rpx;
|
|
background: #fff;
|
|
}
|
|
|
|
.shop-avatar {
|
|
width: 100rpx;
|
|
height: 100rpx;
|
|
border-radius: 16rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.shop-info-text {
|
|
flex: 1;
|
|
padding-left: 20rpx;
|
|
}
|
|
|
|
.shop-name {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 12rpx;
|
|
display: block;
|
|
}
|
|
|
|
.shop-location-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.location-icon {
|
|
width: 26rpx;
|
|
height: 26rpx;
|
|
margin-right: 6rpx;
|
|
margin-top: 4rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.shop-location {
|
|
font-size: 26rpx;
|
|
color: #999;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* 注意事项 */
|
|
.notice-section {
|
|
padding: 16rpx 24rpx;
|
|
background: #fff;
|
|
border-top: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.notice-text {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
/* 菜品列表 */
|
|
.dish-list {
|
|
margin-top: 20rpx;
|
|
padding: 0 24rpx;
|
|
}
|
|
|
|
.dish-item {
|
|
display: flex;
|
|
align-items: center;
|
|
background: #fff;
|
|
padding: 24rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.dish-item:first-child {
|
|
border-radius: 20rpx 20rpx 0 0;
|
|
}
|
|
|
|
.dish-item:last-child {
|
|
border-radius: 0 0 20rpx 20rpx;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.dish-item:first-child:last-child {
|
|
border-radius: 20rpx;
|
|
}
|
|
|
|
.dish-photo {
|
|
width: 140rpx;
|
|
height: 140rpx;
|
|
border-radius: 12rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.dish-info {
|
|
flex: 1;
|
|
padding-left: 20rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
min-height: 140rpx;
|
|
}
|
|
|
|
.dish-name {
|
|
font-size: 30rpx;
|
|
color: #333;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.dish-bottom {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.dish-price {
|
|
font-size: 32rpx;
|
|
color: #FF6B00;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.dish-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.action-icon {
|
|
width: 48rpx;
|
|
height: 48rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.dish-quantity {
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
min-width: 48rpx;
|
|
text-align: center;
|
|
}
|
|
|
|
/* 底部购物车栏 */
|
|
.cart-bar {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 110rpx;
|
|
background: #fff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 24rpx;
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.cart-left {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.cart-icon {
|
|
width: 80rpx;
|
|
height: 80rpx;
|
|
margin-right: 16rpx;
|
|
}
|
|
|
|
.cart-price {
|
|
font-size: 36rpx;
|
|
color: #FF6B00;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.cart-checkout-btn {
|
|
width: 220rpx;
|
|
height: 72rpx;
|
|
line-height: 72rpx;
|
|
background: linear-gradient(135deg, #FFB700, #FF9500);
|
|
color: #fff;
|
|
font-size: 28rpx;
|
|
border-radius: 36rpx;
|
|
border: none;
|
|
text-align: center;
|
|
margin-right: 10rpx;
|
|
}
|
|
|
|
.cart-checkout-btn::after {
|
|
border: none;
|
|
}
|
|
|
|
.cart-checkout-btn.disabled {
|
|
background: #e0e0e0;
|
|
color: #999;
|
|
}
|
|
|
|
/* 购物车弹窗 */
|
|
.cart-popup-mask {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
z-index: 100;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
}
|
|
|
|
.cart-popup {
|
|
width: 100%;
|
|
max-height: 60vh;
|
|
background: #fff;
|
|
border-radius: 24rpx 24rpx 0 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.cart-popup-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 24rpx 30rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.cart-popup-title {
|
|
font-size: 30rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.cart-popup-clear {
|
|
font-size: 26rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.cart-popup-list {
|
|
max-height: 50vh;
|
|
padding: 0 30rpx;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
}
|
|
|
|
.cart-shop-group {
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.cart-shop-name {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
padding: 16rpx 0 8rpx;
|
|
display: block;
|
|
}
|
|
|
|
.cart-popup-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 0;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.cart-item-photo {
|
|
width: 80rpx;
|
|
height: 80rpx;
|
|
border-radius: 8rpx;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.cart-item-info {
|
|
flex: 1;
|
|
padding: 0 16rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
.cart-item-name {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.cart-item-price {
|
|
font-size: 26rpx;
|
|
color: #FF6B00;
|
|
}
|
|
|
|
.cart-popup-footer {
|
|
padding: 16rpx 30rpx;
|
|
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
|
border-top: 1rpx solid #f0f0f0;
|
|
}
|
|
|
|
.cart-fee-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8rpx 0;
|
|
}
|
|
|
|
.cart-fee-label {
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.cart-fee-value {
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.cart-fee-total {
|
|
font-size: 32rpx;
|
|
color: #FF6B00;
|
|
font-weight: bold;
|
|
}
|
|
</style> |