.
This commit is contained in:
parent
2d979e821e
commit
d0a63a38af
|
|
@ -45,12 +45,14 @@
|
|||
<el-tab-pane label="客服二维码" name="qrcode">
|
||||
<el-form label-width="100px" style="max-width: 500px;">
|
||||
<el-form-item label="二维码图片">
|
||||
<el-input v-model="configs.qrcode" placeholder="二维码图片地址" />
|
||||
<el-upload action="/api/upload/image" :headers="uploadHeaders" :show-file-list="false"
|
||||
:on-success="(res) => configs.qrcode = res.url" accept="image/*" style="margin-top: 8px;">
|
||||
<el-button size="small">上传图片</el-button>
|
||||
:on-success="(res) => configs.qrcode = res.url" accept="image/*">
|
||||
<el-image v-if="configs.qrcode" :src="configs.qrcode" style="width: 150px; cursor: pointer; border-radius: 8px;" fit="contain" />
|
||||
<el-button v-else size="small">上传图片</el-button>
|
||||
</el-upload>
|
||||
<el-image v-if="configs.qrcode" :src="configs.qrcode" style="width: 150px; margin-top: 8px;" fit="contain" />
|
||||
<div v-if="configs.qrcode" style="margin-top: 4px;">
|
||||
<el-button size="small" text type="danger" @click="configs.qrcode = ''">移除</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="saveConfig('qrcode')">保存</el-button>
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@
|
|||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<view v-if="qrcodeUrl" class="qrcode-card">
|
||||
<text class="qrcode-title">客服二维码</text>
|
||||
<image class="qrcode-image" :src="qrcodeUrl" mode="aspectFit" @click="previewImage"></image>
|
||||
<text class="qrcode-tip">长按识别二维码联系客服</text>
|
||||
</view>
|
||||
<view v-else class="empty-tip">
|
||||
<text>暂无客服二维码</text>
|
||||
<view class="page-body">
|
||||
<view v-if="qrcodeUrl" class="qrcode-card">
|
||||
<text class="qrcode-title">客服二维码</text>
|
||||
<image class="qrcode-image" :src="qrcodeUrl" mode="aspectFit" @click="previewImage"></image>
|
||||
<text class="qrcode-tip">长按识别二维码联系客服</text>
|
||||
</view>
|
||||
<view v-else class="empty-tip">
|
||||
<text>暂无客服二维码</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
|
@ -94,9 +96,16 @@ export default {
|
|||
width: 60rpx;
|
||||
}
|
||||
.qrcode-page {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ export default {
|
|||
}
|
||||
|
||||
.summary {
|
||||
border-top: 2rpx solid #007AFF;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* 底部接单栏 */
|
||||
|
|
@ -371,9 +371,10 @@ export default {
|
|||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background-color: #007AFF;
|
||||
color: #ffffff;
|
||||
background-color: #FAD146;
|
||||
color: #333333;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 44rpx;
|
||||
border: none;
|
||||
}
|
||||
|
|
@ -419,6 +420,7 @@ export default {
|
|||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
|
|
@ -433,11 +435,16 @@ export default {
|
|||
.modal-btn.cancel {
|
||||
background-color: #f5f5f5;
|
||||
color: #666666;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-btn.cancel::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-btn.confirm {
|
||||
background-color: #007AFF;
|
||||
color: #ffffff;
|
||||
background-color: #FAD146;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.cert-form .form-item {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,13 @@
|
|||
<!-- 订单商品摘要 -->
|
||||
<view class="order-summary">
|
||||
<text class="section-title">订单商品</text>
|
||||
<view class="summary-item" v-for="item in cartItems" :key="item.dish.id">
|
||||
<text class="summary-name">{{ item.dish.name }} × {{ item.quantity }}</text>
|
||||
<text class="summary-price">¥{{ (item.dish.price * item.quantity).toFixed(2) }}</text>
|
||||
<view v-for="shopGroup in cartStore.shopList" :key="shopGroup.shopInfo.id" class="shop-group">
|
||||
<text class="shop-group-name">{{ shopGroup.shopInfo.name }}</text>
|
||||
<view class="summary-item" v-for="item in shopGroup.items" :key="item.dish.id">
|
||||
<image class="summary-photo" :src="item.dish.photo" mode="aspectFill"></image>
|
||||
<text class="summary-name">{{ item.dish.name }} × {{ item.quantity }}</text>
|
||||
<text class="summary-price">¥{{ (item.dish.price * item.quantity).toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="summary-item" v-if="packingFee > 0">
|
||||
<text class="summary-name">打包费</text>
|
||||
|
|
@ -84,9 +88,6 @@ export default {
|
|||
cartStore() {
|
||||
return useCartStore()
|
||||
},
|
||||
cartItems() {
|
||||
return this.cartStore.items
|
||||
},
|
||||
packingFee() {
|
||||
return this.cartStore.packingFee
|
||||
},
|
||||
|
|
@ -153,13 +154,8 @@ export default {
|
|||
const commission = parseFloat(this.form.commission)
|
||||
const goodsAmount = this.cartStore.totalPriceWithFee
|
||||
|
||||
// 构建美食街订单数据
|
||||
const foodItems = this.cartItems.map(item => ({
|
||||
shopId: this.cartStore.shopInfo?.id,
|
||||
dishId: item.dish.id,
|
||||
quantity: item.quantity,
|
||||
unitPrice: item.dish.price
|
||||
}))
|
||||
// 构建美食街订单数据(多门店)
|
||||
const foodItems = this.cartStore.getAllFoodItems()
|
||||
|
||||
const orderData = {
|
||||
orderType: 'Food',
|
||||
|
|
@ -268,6 +264,18 @@ export default {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.shop-group {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.shop-group-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding: 8rpx 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
@ -275,9 +283,18 @@ export default {
|
|||
padding: 10rpx 0;
|
||||
}
|
||||
|
||||
.summary-photo {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 8rpx;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.summary-name {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.summary-price {
|
||||
|
|
@ -367,6 +384,7 @@ export default {
|
|||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 999;
|
||||
justify-content: flex-end;
|
||||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,11 +39,58 @@
|
|||
<view v-if="!loading && shops.length === 0" class="empty-state">
|
||||
<text class="empty-text">暂无门店</text>
|
||||
</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 { getShops, getPageBanner } from '../../utils/api'
|
||||
import { useCartStore } from '../../stores/cart'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
@ -51,7 +98,19 @@ export default {
|
|||
shops: [],
|
||||
loading: true,
|
||||
statusBarHeight: 0,
|
||||
bannerUrl: ''
|
||||
bannerUrl: '',
|
||||
showCartPopup: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cartStore() {
|
||||
return useCartStore()
|
||||
},
|
||||
cartTotalCount() {
|
||||
return this.cartStore.totalCount
|
||||
},
|
||||
cartTotalPrice() {
|
||||
return this.cartStore.totalPrice.toFixed(2)
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
|
|
@ -77,6 +136,31 @@ export default {
|
|||
},
|
||||
goShopDetail(id) {
|
||||
uni.navigateTo({ url: `/pages/food/shop-detail?id=${id}` })
|
||||
},
|
||||
/** 去结算 */
|
||||
goCheckout() {
|
||||
if (this.cartTotalCount === 0) return
|
||||
uni.navigateTo({ url: '/pages/food/food-order' })
|
||||
},
|
||||
/** 清空购物车 */
|
||||
clearCart() {
|
||||
this.cartStore.clearCart()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -86,6 +170,7 @@ export default {
|
|||
.food-page {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 140rpx;
|
||||
}
|
||||
|
||||
/* 自定义导航栏 */
|
||||
|
|
@ -214,4 +299,197 @@ export default {
|
|||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* 底部购物车栏 */
|
||||
.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.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;
|
||||
}
|
||||
|
||||
.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-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>
|
||||
|
|
|
|||
|
|
@ -46,14 +46,10 @@
|
|||
<view class="dish-bottom">
|
||||
<text class="dish-price">¥{{ dish.price.toFixed(1) }}</text>
|
||||
<view class="dish-actions">
|
||||
<view class="action-btn minus-btn" v-if="getQuantity(dish.id) > 0" @click="onMinus(dish)">
|
||||
<text class="action-text">-</text>
|
||||
</view>
|
||||
<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>
|
||||
<view class="action-btn plus-btn" @click="onPlus(dish)">
|
||||
<text class="action-text">+</text>
|
||||
</view>
|
||||
<image class="action-icon" src="/static/ic_add.png" mode="aspectFit" @click="onPlus(dish)"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -78,31 +74,30 @@
|
|||
<text class="cart-popup-clear" @click="clearCart">清空</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="cart-popup-list">
|
||||
<view class="cart-popup-item" v-for="item in cartItems" :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">
|
||||
<view class="action-btn minus-btn" @click="onMinus(item.dish)">
|
||||
<text class="action-text">-</text>
|
||||
<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>
|
||||
<text class="dish-quantity">{{ item.quantity }}</text>
|
||||
<view class="action-btn plus-btn" @click="onPlus(item.dish)">
|
||||
<text class="action-text">+</text>
|
||||
<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="packingFee > 0">
|
||||
<view class="cart-fee-row" v-if="cartStore.packingFee > 0">
|
||||
<text class="cart-fee-label">打包费</text>
|
||||
<text class="cart-fee-value">¥{{ packingFee.toFixed(2) }}</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">¥{{ cartTotalPriceWithFee }}</text>
|
||||
<text class="cart-fee-total">¥{{ cartStore.totalPriceWithFee.toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -131,23 +126,11 @@
|
|||
cartStore() {
|
||||
return useCartStore()
|
||||
},
|
||||
cartItems() {
|
||||
return this.cartStore.items
|
||||
},
|
||||
cartTotalCount() {
|
||||
return this.cartStore.totalCount
|
||||
},
|
||||
cartTotalPrice() {
|
||||
return this.cartStore.totalPrice.toFixed(2)
|
||||
},
|
||||
packingFee() {
|
||||
if (!this.shop.packingFeeType) return 0
|
||||
if (this.shop.packingFeeType === 'Fixed')
|
||||
return this.cartTotalCount > 0 ? (this.shop.packingFeeAmount || 0) : 0
|
||||
return this.cartTotalCount * (this.shop.packingFeeAmount || 0)
|
||||
},
|
||||
cartTotalPriceWithFee() {
|
||||
return (parseFloat(this.cartTotalPrice) + this.packingFee).toFixed(2)
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
|
|
@ -185,6 +168,22 @@
|
|||
this.cartStore.clearCart();
|
||||
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({
|
||||
|
|
@ -380,35 +379,10 @@
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
.action-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.minus-btn {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.plus-btn {
|
||||
background: #FF8C00;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 36rpx;
|
||||
line-height: 1;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.minus-btn .action-text {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.plus-btn .action-text {
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dish-quantity {
|
||||
|
|
@ -519,6 +493,18 @@
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<image v-if="bannerUrl" class="top-banner" :src="bannerUrl" mode="aspectFill"></image>
|
||||
|
||||
<!-- 分类标签 + 排序 -->
|
||||
<view class="tab-sort-bar">
|
||||
<view class="tab-sort-bar" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<scroll-view class="tab-scroll" scroll-x>
|
||||
<view class="tab-item" v-for="tab in tabs" :key="tab.value"
|
||||
:class="{ active: currentTab === tab.value }" @click="switchTab(tab.value)">
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<scroll-view class="order-list" scroll-y @scrolltolower="loadMore">
|
||||
<view class="order-list">
|
||||
<view v-if="loading && orders.length === 0" class="loading-tip">加载中...</view>
|
||||
<view v-else-if="orders.length === 0" class="empty-tip">暂无订单</view>
|
||||
|
||||
|
|
@ -53,6 +53,38 @@
|
|||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 美食街:专属卡片布局 -->
|
||||
<template v-else-if="order.orderType === 'Food'">
|
||||
<!-- 标题行:门店数量 + 跑腿费 -->
|
||||
<view class="card-title-row">
|
||||
<text class="card-item-name">门店数量:{{ order.shopCount || 0 }}个</text>
|
||||
<view class="card-commission">
|
||||
<text class="commission-label">跑腿费:</text>
|
||||
<text class="commission-value">{{ order.commission }}元</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 美食数量 -->
|
||||
<view class="food-info-row">
|
||||
<text class="food-info-label">美食数量:</text>
|
||||
<text class="food-info-value">{{ order.dishItemCount || 0 }}份</text>
|
||||
</view>
|
||||
<!-- 垫付商品金额 -->
|
||||
<view class="food-info-row" v-if="order.goodsAmount">
|
||||
<text class="food-info-label">垫付商品金额:</text>
|
||||
<text class="food-info-price">¥{{ order.goodsAmount }}</text>
|
||||
</view>
|
||||
<!-- 送达地址 -->
|
||||
<view class="info-row" v-if="order.deliveryLocation">
|
||||
<view class="dot orange"></view>
|
||||
<text class="info-text">送达地址:{{ order.deliveryLocation }}</text>
|
||||
</view>
|
||||
<!-- 查看详情按钮 -->
|
||||
<view class="card-footer" style="margin-top: 16rpx;">
|
||||
<view class="footer-spacer"></view>
|
||||
<button class="accept-btn" size="mini" @click="viewFoodDetail(order)">查看详情</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 其他类型:默认卡片布局 -->
|
||||
<template v-else>
|
||||
<!-- 标题行:物品名 + 跑腿费 -->
|
||||
|
|
@ -89,13 +121,11 @@
|
|||
<!-- 接单按钮 -->
|
||||
<view class="card-footer">
|
||||
<view class="footer-spacer"></view>
|
||||
<button v-if="order.orderType === 'Food'" class="accept-btn" size="mini"
|
||||
@click="viewFoodDetail(order)">查看详情</button>
|
||||
<button v-else class="accept-btn" size="mini" @click="onAcceptClick(order)">接单</button>
|
||||
<button class="accept-btn" size="mini" @click="onAcceptClick(order)">接单</button>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 接单确认弹窗 -->
|
||||
<view class="modal-mask" v-if="showAcceptModal" @click="showAcceptModal = false">
|
||||
|
|
@ -177,10 +207,6 @@
|
|||
{
|
||||
label: '佣金优先',
|
||||
value: 'commission'
|
||||
},
|
||||
{
|
||||
label: '距离优先',
|
||||
value: 'distance'
|
||||
}
|
||||
],
|
||||
sortIndex: 0,
|
||||
|
|
@ -195,15 +221,13 @@
|
|||
phone: ''
|
||||
},
|
||||
certSubmitting: false,
|
||||
certStatus: null,
|
||||
userLocation: null
|
||||
certStatus: null
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
||||
this.loadBanner()
|
||||
this.requestLocation()
|
||||
this.loadOrders()
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -214,19 +238,6 @@
|
|||
if (res?.value) this.bannerUrl = res.value
|
||||
} catch (e) {}
|
||||
},
|
||||
/** 申请定位权限 */
|
||||
requestLocation() {
|
||||
uni.getLocation({
|
||||
type: 'gcj02',
|
||||
success: (res) => {
|
||||
this.userLocation = {
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude
|
||||
}
|
||||
},
|
||||
fail: () => {}
|
||||
})
|
||||
},
|
||||
/** 加载订单列表 */
|
||||
async loadOrders() {
|
||||
this.loading = true
|
||||
|
|
@ -236,10 +247,6 @@
|
|||
type: this.currentTab,
|
||||
sort: sortValue
|
||||
}
|
||||
if (sortValue === 'distance' && this.userLocation) {
|
||||
params.latitude = this.userLocation.latitude
|
||||
params.longitude = this.userLocation.longitude
|
||||
}
|
||||
const res = await getOrderHall(params)
|
||||
this.orders = res || []
|
||||
} catch (e) {
|
||||
|
|
@ -370,10 +377,9 @@
|
|||
|
||||
<style scoped>
|
||||
.hall-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* 自定义导航栏 */
|
||||
|
|
@ -403,7 +409,6 @@
|
|||
.top-banner {
|
||||
width: 100%;
|
||||
height: 374rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 分类标签 + 排序栏 */
|
||||
|
|
@ -411,7 +416,10 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20rpx;
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.tab-scroll {
|
||||
|
|
@ -482,7 +490,6 @@
|
|||
|
||||
/* 订单列表 */
|
||||
.order-list {
|
||||
flex: 1;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
|
|
@ -566,6 +573,30 @@
|
|||
color: #333;
|
||||
}
|
||||
|
||||
/* 美食街卡片信息行 */
|
||||
.food-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6rpx 0;
|
||||
}
|
||||
|
||||
.food-info-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.food-info-value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.food-info-price {
|
||||
font-size: 30rpx;
|
||||
color: #FF6B00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 信息行 */
|
||||
.card-body {
|
||||
margin-bottom: 20rpx;
|
||||
|
|
|
|||
|
|
@ -11,30 +11,37 @@
|
|||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 状态筛选标签 -->
|
||||
<scroll-view class="tab-bar" scroll-x>
|
||||
<view
|
||||
class="tab-item"
|
||||
v-for="tab in statusTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentStatus === tab.value }"
|
||||
@click="switchStatus(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
<!-- 筛选栏(吸顶) -->
|
||||
<view class="filter-sticky" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<!-- 状态筛选标签 -->
|
||||
<view class="tab-bar">
|
||||
<scroll-view class="tab-scroll" scroll-x>
|
||||
<view
|
||||
class="tab-item"
|
||||
v-for="tab in statusTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentStatus === tab.value }"
|
||||
@click="switchStatus(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 类型筛选标签 -->
|
||||
<scroll-view class="type-bar" scroll-x>
|
||||
<view
|
||||
class="type-item"
|
||||
v-for="tab in typeTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentType === tab.value }"
|
||||
@click="switchType(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
<!-- 类型筛选标签 -->
|
||||
<view class="type-bar">
|
||||
<scroll-view class="type-scroll" scroll-x>
|
||||
<view
|
||||
class="type-item"
|
||||
v-for="tab in typeTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentType === tab.value }"
|
||||
@click="switchType(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<scroll-view class="order-list" scroll-y>
|
||||
<view class="order-list">
|
||||
<view v-if="loading" class="loading-tip">加载中...</view>
|
||||
<view v-else-if="filteredOrders.length === 0" class="empty-tip">暂无订单</view>
|
||||
|
||||
|
|
@ -124,7 +131,7 @@
|
|||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 评价弹窗 -->
|
||||
<view class="modal-mask" v-if="showReviewModal" @click="showReviewModal = false">
|
||||
|
|
@ -365,18 +372,26 @@ export default {
|
|||
width: 60rpx;
|
||||
}
|
||||
.my-orders-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* 筛选栏吸顶容器 */
|
||||
.filter-sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* 状态筛选栏 */
|
||||
.tab-bar {
|
||||
white-space: nowrap;
|
||||
background-color: #ffffff;
|
||||
padding: 16rpx 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tab-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
|
|
@ -390,17 +405,18 @@ export default {
|
|||
}
|
||||
|
||||
.tab-item.active {
|
||||
background-color: #007AFF;
|
||||
background-color: #FFB700;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 类型筛选栏 */
|
||||
.type-bar {
|
||||
white-space: nowrap;
|
||||
background-color: #ffffff;
|
||||
padding: 12rpx 24rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.type-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.type-item {
|
||||
|
|
@ -414,13 +430,12 @@ export default {
|
|||
}
|
||||
|
||||
.type-item.active {
|
||||
color: #007AFF;
|
||||
border-color: #007AFF;
|
||||
color: #FFB700;
|
||||
border-color: #FFB700;
|
||||
}
|
||||
|
||||
/* 订单列表 */
|
||||
.order-list {
|
||||
flex: 1;
|
||||
padding: 16rpx 24rpx;
|
||||
}
|
||||
|
||||
|
|
@ -449,7 +464,7 @@ export default {
|
|||
.order-type-tag {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
background-color: #007AFF;
|
||||
background-color: #FFB700;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
|
@ -514,19 +529,19 @@ export default {
|
|||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #007AFF;
|
||||
background-color: #FAD146;
|
||||
}
|
||||
|
||||
.btn-primary text {
|
||||
color: #ffffff;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.btn-detail {
|
||||
border: 1rpx solid #007AFF;
|
||||
border: 1rpx solid #FFB700;
|
||||
}
|
||||
|
||||
.btn-detail text {
|
||||
color: #007AFF;
|
||||
color: #FFB700;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
|
|
@ -619,7 +634,7 @@ export default {
|
|||
}
|
||||
|
||||
.modal-btn.confirm {
|
||||
background-color: #007AFF;
|
||||
color: #ffffff;
|
||||
background-color: #FAD146;
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -11,30 +11,37 @@
|
|||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 状态筛选标签 -->
|
||||
<scroll-view class="tab-bar" scroll-x>
|
||||
<view
|
||||
class="tab-item"
|
||||
v-for="tab in statusTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentStatus === tab.value }"
|
||||
@click="switchStatus(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
<!-- 筛选栏(吸顶) -->
|
||||
<view class="filter-sticky" :style="{ top: (statusBarHeight + 44) + 'px' }">
|
||||
<!-- 状态筛选标签 -->
|
||||
<view class="tab-bar">
|
||||
<scroll-view class="tab-scroll" scroll-x>
|
||||
<view
|
||||
class="tab-item"
|
||||
v-for="tab in statusTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentStatus === tab.value }"
|
||||
@click="switchStatus(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 类型筛选标签 -->
|
||||
<scroll-view class="type-bar" scroll-x>
|
||||
<view
|
||||
class="type-item"
|
||||
v-for="tab in typeTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentType === tab.value }"
|
||||
@click="switchType(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
<!-- 类型筛选标签 -->
|
||||
<view class="type-bar">
|
||||
<scroll-view class="type-scroll" scroll-x>
|
||||
<view
|
||||
class="type-item"
|
||||
v-for="tab in typeTabs"
|
||||
:key="tab.value"
|
||||
:class="{ active: currentType === tab.value }"
|
||||
@click="switchType(tab.value)"
|
||||
>{{ tab.label }}</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<scroll-view class="order-list" scroll-y>
|
||||
<view class="order-list">
|
||||
<view v-if="loading" class="loading-tip">加载中...</view>
|
||||
<view v-else-if="filteredOrders.length === 0" class="empty-tip">暂无接单</view>
|
||||
|
||||
|
|
@ -104,7 +111,7 @@
|
|||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -244,17 +251,25 @@ export default {
|
|||
width: 60rpx;
|
||||
}
|
||||
.my-taken-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* 筛选栏吸顶容器 */
|
||||
.filter-sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.tab-bar {
|
||||
white-space: nowrap;
|
||||
background-color: #ffffff;
|
||||
padding: 16rpx 24rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tab-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
|
|
@ -268,16 +283,17 @@ export default {
|
|||
}
|
||||
|
||||
.tab-item.active {
|
||||
background-color: #007AFF;
|
||||
background-color: #FFB700;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.type-bar {
|
||||
white-space: nowrap;
|
||||
background-color: #ffffff;
|
||||
padding: 12rpx 24rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.type-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.type-item {
|
||||
|
|
@ -291,12 +307,11 @@ export default {
|
|||
}
|
||||
|
||||
.type-item.active {
|
||||
color: #007AFF;
|
||||
border-color: #007AFF;
|
||||
color: #FFB700;
|
||||
border-color: #FFB700;
|
||||
}
|
||||
|
||||
.order-list {
|
||||
flex: 1;
|
||||
padding: 16rpx 24rpx;
|
||||
}
|
||||
|
||||
|
|
@ -324,13 +339,13 @@ export default {
|
|||
.order-type-tag {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
background-color: #007AFF;
|
||||
background-color: #FFB700;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.order-status { font-size: 24rpx; font-weight: 500; }
|
||||
.status-progress { color: #007AFF; }
|
||||
.status-progress { color: #FFB700; }
|
||||
.status-done { color: #52c41a; }
|
||||
.status-cancel { color: #999999; }
|
||||
.status-confirm { color: #ff9900; }
|
||||
|
|
@ -378,11 +393,11 @@ export default {
|
|||
|
||||
.btn text { font-size: 26rpx; }
|
||||
|
||||
.btn-primary { background-color: #007AFF; }
|
||||
.btn-primary text { color: #ffffff; }
|
||||
.btn-primary { background-color: #FAD146; }
|
||||
.btn-primary text { color: #333333; }
|
||||
|
||||
.btn-detail { border: 1rpx solid #007AFF; }
|
||||
.btn-detail text { color: #007AFF; }
|
||||
.btn-detail { border: 1rpx solid #FFB700; }
|
||||
.btn-detail text { color: #FFB700; }
|
||||
|
||||
.btn-contact { border: 1rpx solid #52c41a; }
|
||||
.btn-contact text { color: #52c41a; }
|
||||
|
|
|
|||
|
|
@ -582,19 +582,19 @@ export default {
|
|||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #007AFF;
|
||||
background-color: #FAD146;
|
||||
}
|
||||
|
||||
.btn-primary text {
|
||||
color: #ffffff;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
border: 1rpx solid #007AFF;
|
||||
border: 1rpx solid #FAD146;
|
||||
}
|
||||
|
||||
.btn-secondary text {
|
||||
color: #007AFF;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
|
|
|
|||
|
|
@ -11,36 +11,38 @@
|
|||
</view>
|
||||
</view>
|
||||
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
||||
<!-- 已认证状态 -->
|
||||
<view v-if="certStatus === 'Approved'" class="status-card success">
|
||||
<text class="status-icon">✓</text>
|
||||
<text class="status-text">已通过跑腿认证</text>
|
||||
</view>
|
||||
<view class="page-body">
|
||||
<!-- 已认证状态 -->
|
||||
<view v-if="certStatus === 'Approved'" class="status-card success">
|
||||
<text class="status-icon">✓</text>
|
||||
<text class="status-text">已通过跑腿认证</text>
|
||||
</view>
|
||||
|
||||
<!-- 审核中状态 -->
|
||||
<view v-else-if="certStatus === 'Pending'" class="status-card pending">
|
||||
<text class="status-icon">⏳</text>
|
||||
<text class="status-text">平台审核中</text>
|
||||
<text class="status-hint">请耐心等待审核结果</text>
|
||||
</view>
|
||||
<!-- 审核中状态 -->
|
||||
<view v-else-if="certStatus === 'Pending'" class="status-card pending">
|
||||
<text class="status-icon">⏳</text>
|
||||
<text class="status-text">平台审核中</text>
|
||||
<text class="status-hint">请耐心等待审核结果</text>
|
||||
</view>
|
||||
|
||||
<!-- 未认证/被拒绝,显示认证表单 -->
|
||||
<view v-else class="cert-form-section">
|
||||
<view class="form-card">
|
||||
<text class="form-title">跑腿认证</text>
|
||||
<text class="form-desc">完成认证后即可接单赚取佣金</text>
|
||||
<!-- 未认证/被拒绝,显示认证表单 -->
|
||||
<view v-else class="cert-form-section">
|
||||
<view class="form-card">
|
||||
<text class="form-title">跑腿认证</text>
|
||||
<text class="form-desc">完成认证后即可接单赚取佣金</text>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">姓名</text>
|
||||
<input class="form-input" v-model="form.realName" placeholder="请输入真实姓名" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" v-model="form.phone" type="number" placeholder="请输入手机号" maxlength="11" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">姓名</text>
|
||||
<input class="form-input" v-model="form.realName" placeholder="请输入真实姓名" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" v-model="form.phone" type="number" placeholder="请输入手机号" maxlength="11" />
|
||||
</view>
|
||||
|
||||
<view class="submit-btn" :class="{ disabled: submitting }" @click="submitCert">
|
||||
<text>{{ submitting ? '提交中...' : '提交认证' }}</text>
|
||||
<view class="submit-btn" :class="{ disabled: submitting }" @click="submitCert">
|
||||
<text>{{ submitting ? '提交中...' : '提交认证' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -142,8 +144,15 @@ export default {
|
|||
width: 60rpx;
|
||||
}
|
||||
.cert-page {
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-body {
|
||||
flex: 1;
|
||||
padding: 40rpx 24rpx;
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
miniapp/static/ic_add.png
Normal file
BIN
miniapp/static/ic_add.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
miniapp/static/ic_reduce.png
Normal file
BIN
miniapp/static/ic_reduce.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -1,42 +1,69 @@
|
|||
/**
|
||||
* 购物车状态管理
|
||||
* 管理美食街购物车中的菜品、数量和价格计算
|
||||
* Requirements: 9.1-9.9
|
||||
* 购物车状态管理(多门店版)
|
||||
* 按门店分组存储菜品,支持跨门店下单
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export const useCartStore = defineStore('cart', () => {
|
||||
// 购物车商品列表 [{ dish: { id, name, photo, price, shopId }, quantity }]
|
||||
const items = ref([])
|
||||
// 按门店分组存储: { shopId: { shopInfo, items: [{ dish, quantity }] } }
|
||||
const shops = ref({})
|
||||
|
||||
// 当前门店信息 { id, name, packingFeeType, packingFeeAmount }
|
||||
const shopInfo = ref(null)
|
||||
// 当前门店 ID(门店详情页使用)
|
||||
const currentShopId = ref(null)
|
||||
|
||||
// ==================== 计算属性 ====================
|
||||
|
||||
/** 购物车总数量 = 所有菜品数量之和 */
|
||||
/** 所有门店列表 */
|
||||
const shopList = computed(() => {
|
||||
return Object.values(shops.value).filter(s => s.items.length > 0)
|
||||
})
|
||||
|
||||
/** 当前门店的商品列表 */
|
||||
const items = computed(() => {
|
||||
if (!currentShopId.value || !shops.value[currentShopId.value]) return []
|
||||
return shops.value[currentShopId.value].items
|
||||
})
|
||||
|
||||
/** 当前门店信息 */
|
||||
const shopInfo = computed(() => {
|
||||
if (!currentShopId.value || !shops.value[currentShopId.value]) return null
|
||||
return shops.value[currentShopId.value].shopInfo
|
||||
})
|
||||
|
||||
/** 所有门店总数量 */
|
||||
const totalCount = computed(() => {
|
||||
return items.value.reduce((sum, item) => sum + item.quantity, 0)
|
||||
})
|
||||
|
||||
/** 购物车商品总价 = 所有菜品的(数量 × 单价)之和 */
|
||||
const totalPrice = computed(() => {
|
||||
return items.value.reduce((sum, item) => {
|
||||
return sum + item.quantity * item.dish.price
|
||||
}, 0)
|
||||
})
|
||||
|
||||
/** 打包费计算 */
|
||||
const packingFee = computed(() => {
|
||||
if (!shopInfo.value || totalCount.value === 0) return 0
|
||||
const { packingFeeType, packingFeeAmount } = shopInfo.value
|
||||
if (!packingFeeAmount) return 0
|
||||
if (packingFeeType === 'Fixed') {
|
||||
return packingFeeAmount
|
||||
let count = 0
|
||||
for (const shop of Object.values(shops.value)) {
|
||||
count += shop.items.reduce((sum, item) => sum + item.quantity, 0)
|
||||
}
|
||||
// PerItem 模式:总份数 × 单份打包费
|
||||
return totalCount.value * packingFeeAmount
|
||||
return count
|
||||
})
|
||||
|
||||
/** 所有门店商品总价(不含打包费) */
|
||||
const totalPrice = computed(() => {
|
||||
let price = 0
|
||||
for (const shop of Object.values(shops.value)) {
|
||||
price += shop.items.reduce((sum, item) => sum + item.quantity * item.dish.price, 0)
|
||||
}
|
||||
return price
|
||||
})
|
||||
|
||||
/** 所有门店打包费总和 */
|
||||
const packingFee = computed(() => {
|
||||
let fee = 0
|
||||
for (const shop of Object.values(shops.value)) {
|
||||
if (shop.items.length === 0) continue
|
||||
const { packingFeeType, packingFeeAmount } = shop.shopInfo
|
||||
if (!packingFeeAmount) continue
|
||||
if (packingFeeType === 'Fixed') {
|
||||
fee += packingFeeAmount
|
||||
} else {
|
||||
const shopCount = shop.items.reduce((s, i) => s + i.quantity, 0)
|
||||
fee += shopCount * packingFeeAmount
|
||||
}
|
||||
}
|
||||
return fee
|
||||
})
|
||||
|
||||
/** 含打包费的总金额 */
|
||||
|
|
@ -44,75 +71,87 @@ export const useCartStore = defineStore('cart', () => {
|
|||
return totalPrice.value + packingFee.value
|
||||
})
|
||||
|
||||
/** 当前门店的商品数量 */
|
||||
const currentShopCount = computed(() => {
|
||||
if (!currentShopId.value || !shops.value[currentShopId.value]) return 0
|
||||
return shops.value[currentShopId.value].items.reduce((sum, item) => sum + item.quantity, 0)
|
||||
})
|
||||
|
||||
// ==================== 方法 ====================
|
||||
|
||||
/**
|
||||
* 设置门店信息
|
||||
* @param {Object} info - 门店信息
|
||||
*/
|
||||
/** 设置当前门店信息 */
|
||||
function setShopInfo(info) {
|
||||
// 如果切换了门店,清空购物车
|
||||
if (shopInfo.value && shopInfo.value.id !== info.id) {
|
||||
items.value = []
|
||||
currentShopId.value = info.id
|
||||
if (!shops.value[info.id]) {
|
||||
shops.value[info.id] = { shopInfo: info, items: [] }
|
||||
} else {
|
||||
// 更新门店信息
|
||||
shops.value[info.id].shopInfo = info
|
||||
}
|
||||
shopInfo.value = info
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜品在购物车中的数量
|
||||
* @param {number} dishId - 菜品 ID
|
||||
* @returns {number} 数量
|
||||
*/
|
||||
/** 获取菜品在当前门店购物车中的数量 */
|
||||
function getQuantity(dishId) {
|
||||
const item = items.value.find(i => i.dish.id === dishId)
|
||||
if (!currentShopId.value || !shops.value[currentShopId.value]) return 0
|
||||
const item = shops.value[currentShopId.value].items.find(i => i.dish.id === dishId)
|
||||
return item ? item.quantity : 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加菜品到购物车,数量 +1
|
||||
* @param {Object} dish - 菜品对象 { id, name, photo, price }
|
||||
*/
|
||||
/** 添加菜品到当前门店购物车 */
|
||||
function addItem(dish) {
|
||||
const existing = items.value.find(i => i.dish.id === dish.id)
|
||||
if (!currentShopId.value) return
|
||||
const shop = shops.value[currentShopId.value]
|
||||
if (!shop) return
|
||||
const existing = shop.items.find(i => i.dish.id === dish.id)
|
||||
if (existing) {
|
||||
existing.quantity += 1
|
||||
} else {
|
||||
items.value.push({
|
||||
dish: { ...dish },
|
||||
quantity: 1
|
||||
})
|
||||
shop.items.push({ dish: { ...dish }, quantity: 1 })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少菜品数量,数量减至 0 时移除
|
||||
* @param {number} dishId - 菜品 ID
|
||||
*/
|
||||
/** 减少当前门店菜品数量 */
|
||||
function removeItem(dishId) {
|
||||
const index = items.value.findIndex(i => i.dish.id === dishId)
|
||||
if (!currentShopId.value) return
|
||||
const shop = shops.value[currentShopId.value]
|
||||
if (!shop) return
|
||||
const index = shop.items.findIndex(i => i.dish.id === dishId)
|
||||
if (index === -1) return
|
||||
items.value[index].quantity -= 1
|
||||
if (items.value[index].quantity <= 0) {
|
||||
items.value.splice(index, 1)
|
||||
shop.items[index].quantity -= 1
|
||||
if (shop.items[index].quantity <= 0) {
|
||||
shop.items.splice(index, 1)
|
||||
}
|
||||
// 如果门店购物车为空,移除门店
|
||||
if (shop.items.length === 0) {
|
||||
delete shops.value[currentShopId.value]
|
||||
}
|
||||
}
|
||||
|
||||
/** 清空购物车 */
|
||||
/** 清空所有购物车 */
|
||||
function clearCart() {
|
||||
items.value = []
|
||||
shops.value = {}
|
||||
currentShopId.value = null
|
||||
}
|
||||
|
||||
/** 获取所有门店的 foodItems(下单用) */
|
||||
function getAllFoodItems() {
|
||||
const foodItems = []
|
||||
for (const shop of Object.values(shops.value)) {
|
||||
for (const item of shop.items) {
|
||||
foodItems.push({
|
||||
shopId: shop.shopInfo.id,
|
||||
dishId: item.dish.id,
|
||||
quantity: item.quantity,
|
||||
unitPrice: item.dish.price
|
||||
})
|
||||
}
|
||||
}
|
||||
return foodItems
|
||||
}
|
||||
|
||||
return {
|
||||
items,
|
||||
shopInfo,
|
||||
totalCount,
|
||||
totalPrice,
|
||||
packingFee,
|
||||
totalPriceWithFee,
|
||||
setShopInfo,
|
||||
getQuantity,
|
||||
addItem,
|
||||
removeItem,
|
||||
clearCart
|
||||
shops, currentShopId, shopList, items, shopInfo,
|
||||
totalCount, totalPrice, packingFee, totalPriceWithFee, currentShopCount,
|
||||
setShopInfo, getQuantity, addItem, removeItem, clearCart, getAllFoodItems
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -93,7 +93,10 @@ public class FoodOrderItemResponse
|
|||
{
|
||||
public int Id { get; set; }
|
||||
public int ShopId { get; set; }
|
||||
public string ShopName { get; set; } = string.Empty;
|
||||
public int DishId { get; set; }
|
||||
public string DishName { get; set; } = string.Empty;
|
||||
public string? DishPhoto { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public decimal UnitPrice { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -658,7 +658,8 @@ app.MapGet("/api/orders/{id}", async (int id, HttpContext httpContext, AppDbCont
|
|||
var currentUserId = int.Parse(userIdClaim.Value);
|
||||
|
||||
var order = await db.Orders
|
||||
.Include(o => o.FoodOrderItems)
|
||||
.Include(o => o.FoodOrderItems).ThenInclude(fi => fi.Shop)
|
||||
.Include(o => o.FoodOrderItems).ThenInclude(fi => fi.Dish)
|
||||
.Include(o => o.Runner)
|
||||
.FirstOrDefaultAsync(o => o.Id == id);
|
||||
|
||||
|
|
@ -738,7 +739,10 @@ app.MapGet("/api/orders/{id}", async (int id, HttpContext httpContext, AppDbCont
|
|||
{
|
||||
Id = fi.Id,
|
||||
ShopId = fi.ShopId,
|
||||
ShopName = fi.Shop?.Name ?? "未知门店",
|
||||
DishId = fi.DishId,
|
||||
DishName = fi.Dish?.Name ?? "未知菜品",
|
||||
DishPhoto = fi.Dish?.Photo,
|
||||
Quantity = fi.Quantity,
|
||||
UnitPrice = fi.UnitPrice
|
||||
}).ToList();
|
||||
|
|
@ -764,10 +768,13 @@ app.MapGet("/api/orders/hall", async (
|
|||
query = query.Where(o => o.OrderType == orderType);
|
||||
}
|
||||
|
||||
// 排序:佣金优先(默认)或距离优先(暂不支持距离,回退到佣金)
|
||||
query = sort?.ToLower() == "distance"
|
||||
? query.OrderByDescending(o => o.Commission) // 距离排序需地图 API,暂用佣金排序
|
||||
: query.OrderByDescending(o => o.Commission);
|
||||
// 排序
|
||||
query = sort?.ToLower() switch
|
||||
{
|
||||
"commission" => query.OrderByDescending(o => o.Commission),
|
||||
"distance" => query.OrderByDescending(o => o.CreatedAt), // 距离排序需地址坐标,暂按时间排序
|
||||
_ => query.OrderByDescending(o => o.CreatedAt) // 默认按时间排序
|
||||
};
|
||||
|
||||
var orders = await query.ToListAsync();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user