小吃街

This commit is contained in:
18631081161 2026-03-17 15:42:18 +08:00
parent 6488aa837d
commit 2d979e821e
13 changed files with 1241 additions and 877 deletions

View File

@ -36,11 +36,12 @@
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="菜品照片" prop="photo">
<el-input v-model="form.photo" placeholder="照片地址" />
<el-upload action="/api/upload/image" :headers="uploadHeaders" :show-file-list="false"
:on-success="(res) => form.photo = res.url" accept="image/*" style="margin-top: 8px;">
<el-button size="small">上传照片</el-button>
:on-success="(res) => form.photo = res.url" accept="image/*">
<el-image v-if="form.photo" :src="form.photo" style="width: 120px; height: 120px; cursor: pointer; border-radius: 8px;" fit="cover" />
<el-button v-else size="small">上传照片</el-button>
</el-upload>
<el-button v-if="form.photo" size="small" text type="danger" style="margin-top: 4px;" @click="form.photo = ''">移除</el-button>
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input-number v-model="form.price" :min="0" :precision="2" :step="1" />

View File

@ -5,32 +5,33 @@
<el-button type="primary" @click="openDialog()">新增门店</el-button>
</div>
<el-table :data="list" v-loading="loading" border>
<el-table-column label="照片" width="100">
<el-table :data="list" v-loading="loading" border stripe>
<el-table-column label="照片" width="120" align="center">
<template #default="{ row }">
<el-image :src="row.photo" style="width: 60px; height: 60px;" fit="cover" />
<el-image :src="row.photo" style="width: 80px; height: 80px; border-radius: 8px;" fit="cover" :preview-src-list="[row.photo]" preview-teleported />
</template>
</el-table-column>
<el-table-column prop="name" label="门店名称" width="140" />
<el-table-column prop="location" label="位置" show-overflow-tooltip />
<el-table-column prop="packingFeeType" label="打包费类型" width="120">
<el-table-column prop="name" label="门店名称" min-width="120" />
<el-table-column prop="location" label="位置" min-width="140" show-overflow-tooltip />
<el-table-column label="打包费" width="140" align="center">
<template #default="{ row }">
{{ row.packingFeeType === 'Fixed' ? '总打包费' : '单份打包费' }}
<span>{{ row.packingFeeType === 'Fixed' ? '总打包费' : '单份' }} ¥{{ row.packingFeeAmount }}</span>
</template>
</el-table-column>
<el-table-column prop="packingFeeAmount" label="打包费金额" width="110" />
<el-table-column prop="dishCount" label="菜品数" width="80" />
<el-table-column label="状态" width="80">
<el-table-column prop="dishCount" label="菜品数" width="80" align="center" />
<el-table-column label="状态" width="80" align="center">
<template #default="{ row }">
<el-tag :type="row.isEnabled ? 'success' : 'info'" size="small">{{ row.isEnabled ? '启用' : '禁用' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="260" fixed="right">
<el-table-column label="操作" width="300" fixed="right" align="center">
<template #default="{ row }">
<el-button size="small" @click="openDialog(row)">编辑</el-button>
<el-button size="small" @click="manageBanners(row)">Banner</el-button>
<el-button size="small" type="primary" @click="$router.push(`/shops/${row.id}/dishes`)">菜品</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
<div style="display: flex; gap: 4px; justify-content: center; flex-wrap: nowrap;">
<el-button size="small" @click="openDialog(row)">编辑</el-button>
<el-button size="small" @click="manageBanners(row)">Banner</el-button>
<el-button size="small" type="primary" @click="$router.push(`/shops/${row.id}/dishes`)">菜品</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
@ -42,11 +43,12 @@
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="门店照片" prop="photo">
<el-input v-model="form.photo" placeholder="照片地址" />
<el-upload action="/api/upload/image" :headers="uploadHeaders" :show-file-list="false"
:on-success="(res) => form.photo = res.url" accept="image/*" style="margin-top: 8px;">
<el-button size="small">上传照片</el-button>
:on-success="(res) => form.photo = res.url" accept="image/*">
<el-image v-if="form.photo" :src="form.photo" style="width: 120px; height: 120px; cursor: pointer; border-radius: 8px;" fit="cover" />
<el-button v-else size="small">上传照片</el-button>
</el-upload>
<el-button v-if="form.photo" size="small" text type="danger" style="margin-top: 4px;" @click="form.photo = ''">移除</el-button>
</el-form-item>
<el-form-item label="门店位置" prop="location">
<el-input v-model="form.location" />

View File

@ -224,12 +224,11 @@ export default {
/* 顶部大图 */
.top-banner {
padding: 20rpx 24rpx 0;
width: 100%;
}
.banner-img {
width: 100%;
height: 330rpx;
border-radius: 20rpx;
}
/* 表单区域 */

View File

@ -367,7 +367,7 @@ export default {
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
display: flex;
align-items: center;
justify-content: space-between;
justify-content: flex-end;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
}
@ -375,17 +375,19 @@ export default {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
flex: 1;
}
.submit-btn {
width: 240rpx;
height: 80rpx;
line-height: 80rpx;
background-color: #007AFF;
background-color: #FAD146;
color: #ffffff;
font-size: 30rpx;
border-radius: 40rpx;
border: none;
margin-right: 10rpx;
}
.submit-btn[disabled] {

View File

@ -19,17 +19,18 @@
<!-- 门店列表 -->
<view class="shop-list">
<view
class="shop-card"
v-for="shop in shops"
:key="shop.id"
@click="goShopDetail(shop.id)"
>
<view class="shop-card" v-for="shop in shops" :key="shop.id" @click="goShopDetail(shop.id)">
<image class="shop-photo" :src="shop.photo" mode="aspectFill"></image>
<view class="shop-info">
<text class="shop-name">{{ shop.name }}</text>
<text class="shop-location">{{ shop.location }}</text>
<text class="shop-dish-count">{{ shop.dishCount || 0 }} 种美食</text>
<view class="info-row">
<image class="info-icon" src="/static/ic_address.png" mode="aspectFit"></image>
<text class="info-text">{{ shop.location || '暂无位置信息' }}</text>
</view>
<view class="info-row">
<image class="info-icon" src="/static/ic_delicacy.png" mode="aspectFit"></image>
<text class="info-text">{{ shop.dishCount || 0 }}种美食</text>
</view>
</view>
</view>
</view>
@ -67,20 +68,13 @@ export default {
if (res?.value) this.bannerUrl = res.value
} catch (e) {}
},
/** 加载门店列表 */
async loadShops() {
this.loading = true
try {
const res = await getShops()
this.shops = res || []
} catch (e) {
// request
} finally {
this.loading = false
}
} catch (e) {} finally { this.loading = false }
},
/** 跳转门店详情 */
goShopDetail(id) {
uni.navigateTo({ url: `/pages/food/shop-detail?id=${id}` })
}
@ -89,6 +83,11 @@ export default {
</script>
<style scoped>
.food-page {
background-color: #f5f5f5;
min-height: 100vh;
}
/* 自定义导航栏 */
.custom-navbar {
position: fixed;
@ -127,61 +126,83 @@ export default {
/* 顶部大图 */
.top-banner {
padding: 0 24rpx 0;
margin-bottom: 20rpx;
width: 100%;
}
.banner-img {
width: 100%;
height: 280rpx;
border-radius: 20rpx;
height: 360rpx;
}
.food-page {
padding: 24rpx;
background-color: #f5f5f5;
min-height: 100vh;
/* 门店列表 */
.shop-list {
padding: 0 24rpx;
}
.shop-card {
display: flex;
align-items: center;
background-color: #ffffff;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
padding: 24rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.shop-card:first-child {
border-radius: 20rpx 20rpx 0 0;
margin-top: 20rpx;
}
.shop-card:last-child {
border-radius: 0 0 20rpx 20rpx;
border-bottom: none;
}
.shop-card:first-child:last-child {
border-radius: 20rpx;
}
.shop-photo {
width: 200rpx;
height: 200rpx;
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
flex-shrink: 0;
}
.shop-info {
flex: 1;
padding: 20rpx 24rpx;
padding-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.shop-name {
font-size: 30rpx;
font-size: 32rpx;
font-weight: bold;
color: #333333;
margin-bottom: 12rpx;
margin-bottom: 16rpx;
}
.shop-location {
font-size: 24rpx;
color: #999999;
.info-row {
display: flex;
align-items: flex-start;
margin-bottom: 8rpx;
}
.shop-dish-count {
font-size: 24rpx;
color: #007AFF;
.info-icon {
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
margin-top: 4rpx;
flex-shrink: 0;
}
.info-text {
font-size: 26rpx;
color: #999999;
line-height: 1.4;
}
/* 空状态 */
.empty-state {
display: flex;
justify-content: center;

View File

@ -11,64 +11,63 @@
</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 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-label">注意事项</text>
<text class="notice-text">{{ shop.notice }}</text>
</view>
<!-- 菜品列表 -->
<view class="dish-section">
<text class="section-title">美食列表</text>
<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>
<text class="dish-price">¥{{ dish.price.toFixed(2) }}</text>
</view>
<!-- 加减按钮购物车功能在 18.3 中实现 -->
<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>
<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 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">
<view class="action-btn minus-btn" v-if="getQuantity(dish.id) > 0" @click="onMinus(dish)">
<text class="action-text">-</text>
</view>
<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>
</view>
</view>
</view>
</view>
</view>
<!-- 底部购物车栏购物车功能在 18.3 中完善 -->
<view class="cart-bar" v-if="cartTotalCount > 0">
<view class="cart-info" @click="showCartPopup = true">
<text class="cart-count">{{ cartTotalCount }} </text>
<text class="cart-price">¥{{ cartTotalPrice }}</text>
<!-- 底部购物车栏 -->
<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" @click="goCheckout">去结算</button>
<button class="cart-checkout-btn" :class="{ disabled: cartTotalCount === 0 }"
@click="goCheckout">去结算</button>
</view>
<!-- 购物车弹窗 -->
@ -82,9 +81,8 @@
<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-shop">{{ shop.name }}</text>
<text class="cart-item-name">{{ item.dish.name }}</text>
<text class="cart-item-price">¥{{ item.dish.price.toFixed(2) }}</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)">
@ -97,7 +95,6 @@
</view>
</view>
</scroll-view>
<!-- 额外费用和总价 -->
<view class="cart-popup-footer">
<view class="cart-fee-row" v-if="packingFee > 0">
<text class="cart-fee-label">打包费</text>
@ -114,421 +111,472 @@
</template>
<script>
import { getShopDetail } from '../../utils/api'
import { useCartStore } from '../../stores/cart'
import {
getShopDetail
} from '../../utils/api'
import {
useCartStore
} from '../../stores/cart'
export default {
data() {
return {
shop: {},
dishes: [],
showCartPopup: false,
statusBarHeight: 0
}
},
computed: {
/** 获取购物车 store */
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
export default {
data() {
return {
shop: {},
dishes: [],
showCartPopup: false,
statusBarHeight: 0
}
// PerItem ×
return this.cartTotalCount * (this.shop.packingFeeAmount || 0)
},
/** 含打包费的总价 */
cartTotalPriceWithFee() {
return (parseFloat(this.cartTotalPrice) + this.packingFee).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 || []
// store
this.cartStore.setShopInfo({
id: this.shop.id,
name: this.shop.name,
packingFeeType: this.shop.packingFeeType,
packingFeeAmount: this.shop.packingFeeAmount
computed: {
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) {
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,
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.showCartPopup = false
},
goCheckout() {
if (this.cartTotalCount === 0) return
uni.navigateTo({
url: '/pages/food/food-order'
})
} catch (e) {
// request
}
},
/** 获取菜品在购物车中的数量 */
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.showCartPopup = false
},
/** 去结算 */
goCheckout() {
if (this.cartTotalCount === 0) return
uni.navigateTo({ url: '/pages/food/food-order' })
}
}
}
</script>
<style scoped>
/* 自定义导航栏 */
.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;
}
.shop-detail-page {
padding-bottom: 140rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.shop-detail-page {
padding-bottom: 140rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.shop-banner {
width: 100%;
height: 360rpx;
}
/* 自定义导航栏 */
.custom-navbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 999;
background: #FFB700;
}
.banner-image {
width: 100%;
height: 360rpx;
}
.navbar-content {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
}
.notice-section {
margin: 20rpx 24rpx;
background-color: #fff8e6;
border-radius: 12rpx;
padding: 20rpx 24rpx;
}
.nav-back {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.notice-label {
font-size: 26rpx;
color: #e6a23c;
font-weight: bold;
margin-bottom: 8rpx;
display: block;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.notice-text {
font-size: 24rpx;
color: #666666;
}
.navbar-title {
font-size: 34rpx;
font-weight: bold;
color: #363636;
}
.dish-section {
margin: 0 24rpx;
}
.nav-placeholder {
width: 60rpx;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333333;
margin-bottom: 20rpx;
display: block;
}
/* Banner */
.shop-banner {
width: 100%;
height: 360rpx;
}
.dish-item {
display: flex;
align-items: center;
background-color: #ffffff;
border-radius: 12rpx;
margin-bottom: 16rpx;
padding: 16rpx;
}
.banner-image {
width: 100%;
height: 360rpx;
}
.dish-photo {
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
flex-shrink: 0;
}
/* 门店信息卡片 */
.shop-info-card {
display: flex;
align-items: center;
padding: 24rpx;
background: #fff;
}
.dish-info {
flex: 1;
padding: 0 20rpx;
display: flex;
flex-direction: column;
}
.shop-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 16rpx;
flex-shrink: 0;
}
.dish-name {
font-size: 28rpx;
color: #333333;
margin-bottom: 8rpx;
}
.shop-info-text {
flex: 1;
padding-left: 20rpx;
}
.dish-price {
font-size: 28rpx;
color: #e64340;
font-weight: bold;
}
.shop-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 12rpx;
display: block;
}
.dish-actions {
display: flex;
align-items: center;
flex-shrink: 0;
}
.shop-location-row {
display: flex;
align-items: flex-start;
}
.action-btn {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.location-icon {
width: 26rpx;
height: 26rpx;
margin-right: 6rpx;
margin-top: 4rpx;
flex-shrink: 0;
}
.minus-btn {
background-color: #f0f0f0;
}
.shop-location {
font-size: 26rpx;
color: #999;
line-height: 1.4;
}
.plus-btn {
background-color: #007AFF;
}
/* 注意事项 */
.notice-section {
padding: 16rpx 24rpx;
background: #fff;
border-top: 1rpx solid #f0f0f0;
}
.action-text {
font-size: 32rpx;
line-height: 48rpx;
text-align: center;
}
.notice-text {
font-size: 24rpx;
color: #999;
}
.minus-btn .action-text {
color: #666666;
}
/* 菜品列表 */
.dish-list {
margin-top: 20rpx;
padding: 0 24rpx;
}
.plus-btn .action-text {
color: #ffffff;
}
.dish-item {
display: flex;
align-items: center;
background: #fff;
padding: 24rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.dish-quantity {
font-size: 28rpx;
color: #333333;
min-width: 48rpx;
text-align: center;
}
.dish-item:first-child {
border-radius: 20rpx 20rpx 0 0;
}
/* 底部购物车栏 */
.cart-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background-color: #333333;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
padding-bottom: env(safe-area-inset-bottom);
}
.dish-item:last-child {
border-radius: 0 0 20rpx 20rpx;
border-bottom: none;
}
.cart-info {
display: flex;
align-items: center;
}
.dish-item:first-child:last-child {
border-radius: 20rpx;
}
.cart-count {
font-size: 28rpx;
color: #ffffff;
margin-right: 20rpx;
}
.dish-photo {
width: 140rpx;
height: 140rpx;
border-radius: 12rpx;
flex-shrink: 0;
}
.cart-price {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
}
.dish-info {
flex: 1;
padding-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 140rpx;
}
.cart-checkout-btn {
width: 200rpx;
height: 72rpx;
line-height: 72rpx;
background-color: #007AFF;
color: #ffffff;
font-size: 28rpx;
border-radius: 36rpx;
border: none;
text-align: center;
}
.dish-name {
font-size: 30rpx;
color: #333;
font-weight: 500;
}
/* 购物车弹窗 */
.cart-popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 100;
display: flex;
align-items: flex-end;
}
.dish-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.cart-popup {
width: 100%;
max-height: 70vh;
background-color: #ffffff;
border-radius: 24rpx 24rpx 0 0;
display: flex;
flex-direction: column;
}
.dish-price {
font-size: 32rpx;
color: #FF6B00;
font-weight: bold;
}
.cart-popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.dish-actions {
display: flex;
align-items: center;
flex-shrink: 0;
}
.cart-popup-title {
font-size: 30rpx;
font-weight: bold;
color: #333333;
}
.action-btn {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.cart-popup-clear {
font-size: 26rpx;
color: #999999;
}
.minus-btn {
background: #f0f0f0;
}
.cart-popup-list {
max-height: 50vh;
padding: 0 30rpx;
}
.plus-btn {
background: #FF8C00;
}
.cart-popup-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.action-text {
font-size: 36rpx;
line-height: 1;
display: block;
}
.cart-item-photo {
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
flex-shrink: 0;
}
.minus-btn .action-text {
color: #666;
}
.cart-item-info {
flex: 1;
padding: 0 16rpx;
display: flex;
flex-direction: column;
}
.plus-btn .action-text {
color: #fff;
}
.cart-item-shop {
font-size: 22rpx;
color: #999999;
}
.dish-quantity {
font-size: 28rpx;
color: #333;
min-width: 48rpx;
text-align: center;
}
.cart-item-name {
font-size: 26rpx;
color: #333333;
}
/* 底部购物车栏 */
.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-item-price {
font-size: 26rpx;
color: #e64340;
}
.cart-left {
display: flex;
align-items: center;
}
.cart-popup-footer {
padding: 16rpx 30rpx;
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid #f0f0f0;
}
.cart-icon {
width: 80rpx;
height: 80rpx;
margin-right: 16rpx;
}
.cart-fee-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8rpx 0;
}
.cart-price {
font-size: 36rpx;
color: #FF6B00;
font-weight: bold;
}
.cart-fee-label {
font-size: 26rpx;
color: #666666;
}
.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-fee-value {
font-size: 26rpx;
color: #333333;
}
.cart-checkout-btn.disabled {
background: #e0e0e0;
color: #999;
}
.cart-fee-total {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
}
</style>
/* 购物车弹窗 */
.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-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>

View File

@ -21,9 +21,10 @@
<view class="form-section">
<!-- 1.要做的事情 -->
<view class="form-group">
<text class="form-label">1.要做的事情</text>
<text class="form-label">1.万能帮事项</text>
<view class="input-box textarea-box">
<textarea v-model="form.itemName" placeholder="请描述您需要帮忙做的事情" placeholder-class="placeholder" :maxlength="500" />
<textarea v-model="form.itemName" placeholder="请输入需要做什么事,需详细描述" placeholder-class="placeholder"
:maxlength="500" />
</view>
</view>
@ -31,16 +32,17 @@
<view class="form-group">
<text class="form-label">2.如何联系您</text>
<view class="input-box">
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到" placeholder-class="placeholder" maxlength="11" />
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到"
placeholder-class="placeholder" maxlength="11" />
</view>
</view>
<!-- 3.商品总金额 -->
<view class="form-group">
<text class="form-label">3.商品总金额</text>
<text class="form-tip">需包含商品总额和包装费等额外费用</text>
<view class="input-box">
<input v-model="form.goodsAmount" type="digit" placeholder="请输入商品总金额" placeholder-class="placeholder" />
<input v-model="form.goodsAmount" type="digit" placeholder="请输入代购商品总金额,包含可能存在的包装费等"
placeholder-class="placeholder" />
</view>
</view>
@ -48,7 +50,8 @@
<view class="form-group">
<text class="form-label">4.跑腿佣金</text>
<view class="input-box">
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金最低1.0元" placeholder-class="placeholder" />
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金,若涉及购买,需包含商品金额"
placeholder-class="placeholder" />
</view>
<text class="form-tip">佣金先由平台保管接单方完成订单后才会收到佣金</text>
</view>
@ -65,263 +68,307 @@
</template>
<script>
import { createOrder, getPageBanner } from '../../utils/api'
import {
createOrder,
getPageBanner
} from '../../utils/api'
export default {
data() {
return {
form: {
itemName: '',
phone: '',
goodsAmount: '',
commission: ''
export default {
data() {
return {
form: {
itemName: '',
phone: '',
goodsAmount: '',
commission: ''
},
submitting: false,
statusBarHeight: 0,
bannerUrl: ''
}
},
computed: {
/** 支付金额 = 商品总金额 + 跑腿佣金 */
payAmount() {
const goods = parseFloat(this.form.goodsAmount) || 0
const commission = parseFloat(this.form.commission) || 0
return (goods + commission).toFixed(1)
}
},
onLoad() {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight || 0
this.loadBanner()
},
methods: {
goBack() {
uni.navigateBack()
},
submitting: false,
statusBarHeight: 0,
bannerUrl: ''
}
},
computed: {
/** 支付金额 = 商品总金额 + 跑腿佣金 */
payAmount() {
const goods = parseFloat(this.form.goodsAmount) || 0
const commission = parseFloat(this.form.commission) || 0
return (goods + commission).toFixed(1)
}
},
onLoad() {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight || 0
this.loadBanner()
},
methods: {
goBack() { uni.navigateBack() },
async loadBanner() {
try {
const res = await getPageBanner('help')
if (res?.value) this.bannerUrl = res.value
} catch (e) {}
},
/** 校验佣金 */
validateCommission() {
const val = this.form.commission
if (!val) {
uni.showToast({ title: '请输入跑腿佣金', icon: 'none' })
return false
}
const num = parseFloat(val)
if (isNaN(num) || num < 1.0) {
uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' })
return false
}
if (val.includes('.') && val.split('.')[1].length > 1) {
uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' })
return false
}
return true
},
/** 校验表单 */
validateForm() {
if (!this.form.itemName.trim()) {
uni.showToast({ title: '请描述需要帮忙做的事情', icon: 'none' })
return false
}
if (!this.form.phone.trim()) {
uni.showToast({ title: '请输入手机号', icon: 'none' })
return false
}
if (!this.form.goodsAmount) {
uni.showToast({ title: '请输入商品总金额', icon: 'none' })
return false
}
const goodsNum = parseFloat(this.form.goodsAmount)
if (isNaN(goodsNum) || goodsNum <= 0) {
uni.showToast({ title: '请输入正确的商品总金额', icon: 'none' })
return false
}
return this.validateCommission()
},
/** 提交订单 */
async onSubmit() {
if (!this.validateForm()) return
async loadBanner() {
try {
const res = await getPageBanner('help')
if (res?.value) this.bannerUrl = res.value
} catch (e) {}
},
/** 校验佣金 */
validateCommission() {
const val = this.form.commission
if (!val) {
uni.showToast({
title: '请输入跑腿佣金',
icon: 'none'
})
return false
}
const num = parseFloat(val)
if (isNaN(num) || num < 1.0) {
uni.showToast({
title: '跑腿佣金不可低于1.0元',
icon: 'none'
})
return false
}
if (val.includes('.') && val.split('.')[1].length > 1) {
uni.showToast({
title: '佣金最多支持小数点后1位',
icon: 'none'
})
return false
}
return true
},
/** 校验表单 */
validateForm() {
if (!this.form.itemName.trim()) {
uni.showToast({
title: '请描述需要帮忙做的事情',
icon: 'none'
})
return false
}
if (!this.form.phone.trim()) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return false
}
if (!this.form.goodsAmount) {
uni.showToast({
title: '请输入商品总金额',
icon: 'none'
})
return false
}
const goodsNum = parseFloat(this.form.goodsAmount)
if (isNaN(goodsNum) || goodsNum <= 0) {
uni.showToast({
title: '请输入正确的商品总金额',
icon: 'none'
})
return false
}
return this.validateCommission()
},
/** 提交订单 */
async onSubmit() {
if (!this.validateForm()) return
//
const token = uni.getStorageSync('token')
if (!token) {
uni.navigateTo({ url: '/pages/login/login' })
return
}
//
const token = uni.getStorageSync('token')
if (!token) {
uni.navigateTo({
url: '/pages/login/login'
})
return
}
this.submitting = true
try {
const commission = parseFloat(this.form.commission)
const goodsAmount = parseFloat(this.form.goodsAmount)
const result = await createOrder({
orderType: 'Help',
itemName: this.form.itemName.trim(),
phone: this.form.phone.trim(),
goodsAmount,
commission,
totalAmount: goodsAmount + commission
this.submitting = true
try {
const commission = parseFloat(this.form.commission)
const goodsAmount = parseFloat(this.form.goodsAmount)
const result = await createOrder({
orderType: 'Help',
itemName: this.form.itemName.trim(),
phone: this.form.phone.trim(),
goodsAmount,
commission,
totalAmount: goodsAmount + commission
})
if (result.paymentParams) await this.wxPay(result.paymentParams)
uni.showToast({
title: '下单成功',
icon: 'success'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (e) {} finally {
this.submitting = false
}
},
/** 调用微信支付 */
wxPay(params) {
return new Promise((resolve, reject) => {
uni.requestPayment({
...params,
success: resolve,
fail: (err) => {
if (err.errMsg !== 'requestPayment:fail cancel')
uni.showToast({
title: '支付失败',
icon: 'none'
})
reject(err)
}
})
})
if (result.paymentParams) await this.wxPay(result.paymentParams)
uni.showToast({ title: '下单成功', icon: 'success' })
setTimeout(() => { uni.navigateBack() }, 1500)
} catch (e) {} finally { this.submitting = false }
},
/** 调用微信支付 */
wxPay(params) {
return new Promise((resolve, reject) => {
uni.requestPayment({
...params, success: resolve,
fail: (err) => {
if (err.errMsg !== 'requestPayment:fail cancel')
uni.showToast({ title: '支付失败', icon: 'none' })
reject(err)
}
})
})
}
}
}
}
</script>
<style scoped>
.order-page {
padding-bottom: 40rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.order-page {
padding-bottom: 40rpx;
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;
}
/* 自定义导航栏 */
.custom-navbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 999;
background: #FFB700;
}
/* 顶部大图 */
.top-banner {
padding: 20rpx 24rpx 0;
}
.banner-img {
width: 100%;
height: 280rpx;
border-radius: 20rpx;
}
.navbar-content {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
}
/* 表单区域 */
.form-section {
padding: 10rpx 24rpx 0;
}
.nav-back {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.form-group {
margin-bottom: 24rpx;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.form-label {
font-size: 30rpx;
color: #333333;
font-weight: bold;
display: block;
margin-bottom: 16rpx;
}
.navbar-title {
font-size: 34rpx;
font-weight: bold;
color: #363636;
}
.input-box {
background-color: #ffffff;
border-radius: 16rpx;
padding: 0 24rpx;
height: 88rpx;
display: flex;
align-items: center;
}
.nav-placeholder {
width: 60rpx;
}
.input-box input {
width: 100%;
height: 100%;
font-size: 28rpx;
color: #333;
}
/* 顶部大图 */
.top-banner {
width: 100%;
}
.textarea-box {
height: 240rpx;
padding: 20rpx 24rpx;
align-items: flex-start;
}
.banner-img {
width: 100%;
height: 330rpx;
}
.textarea-box textarea {
width: 100%;
height: 100%;
font-size: 28rpx;
color: #333;
}
/* 表单区域 */
.form-section {
padding: 10rpx 24rpx 0;
}
.form-tip {
font-size: 24rpx;
color: #999999;
margin-top: 12rpx;
display: block;
text-align: center;
}
.form-group {
margin-bottom: 24rpx;
}
/* 支付金额 + 提交按钮 */
.submit-section {
padding: 20rpx 24rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.form-label {
font-size: 30rpx;
color: #333333;
font-weight: bold;
display: block;
margin-bottom: 16rpx;
}
.pay-amount {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
display: block;
text-align: center;
margin-bottom: 20rpx;
}
.input-box {
background-color: #ffffff;
border-radius: 16rpx;
padding: 0 24rpx;
height: 88rpx;
display: flex;
align-items: center;
}
.submit-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background: #FAD146;
color: #ffffff;
font-size: 32rpx;
font-weight: bold;
border-radius: 10rpx;
border: none;
text-align: center;
}
.input-box input {
width: 100%;
height: 100%;
font-size: 28rpx;
color: #333;
}
.submit-btn[disabled] {
opacity: 0.6;
}
</style>
.textarea-box {
height: 240rpx;
padding: 20rpx 24rpx;
align-items: flex-start;
}
.textarea-box textarea {
width: 100%;
height: 100%;
font-size: 28rpx;
color: #333;
}
.form-tip {
font-size: 24rpx;
color: #999999;
margin-top: 12rpx;
display: block;
text-align: center;
}
/* 支付金额 + 提交按钮 */
.submit-section {
padding: 20rpx 24rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.pay-amount {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
display: block;
text-align: center;
margin-bottom: 20rpx;
}
.submit-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background: #FAD146;
color: #ffffff;
font-size: 32rpx;
font-weight: bold;
border-radius: 10rpx;
border: none;
text-align: center;
}
.submit-btn[disabled] {
opacity: 0.6;
}
</style>

View File

@ -37,38 +37,63 @@
<view v-else-if="orders.length === 0" class="empty-tip">暂无订单</view>
<view v-for="order in orders" :key="order.id" class="order-card">
<!-- 标题行物品名 + 跑腿费 -->
<view class="card-title-row">
<text class="card-item-name">{{ getItemTitle(order) }}</text>
<view class="card-commission">
<text class="commission-label">跑腿费</text>
<text class="commission-value">{{ order.commission }}</text>
</view>
</view>
<!-- 信息行 -->
<view class="card-body">
<view class="info-row" v-if="order.pickupLocation">
<view class="dot green"></view>
<text class="info-text">取货地址{{ order.pickupLocation }}</text>
</view>
<view class="info-row" v-if="order.deliveryLocation">
<view class="dot orange"></view>
<text class="info-text">送达地址{{ order.deliveryLocation }}</text>
</view>
<!-- 万能帮特殊卡片布局 -->
<template v-if="order.orderType === 'Help'">
<view class="info-row remark-row">
<image class="remark-icon" src="/static/ic_modify.png" mode="aspectFit"></image>
<text class="info-text">备注信息{{ order.remark || '无' }}</text>
<text class="info-text">备注信息{{ order.itemName || '无' }}</text>
</view>
</view>
<view class="card-footer" style="margin-top: 20rpx;">
<view class="help-commission">
<text class="help-commission-label">跑腿费(含其他费用)</text>
<text class="commission-value">{{ getHelpTotalAmount(order) }}</text>
</view>
<view class="footer-spacer"></view>
<button class="accept-btn" size="mini" @click="onAcceptClick(order)">接单</button>
</view>
</template>
<!-- 接单按钮 -->
<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>
</view>
<!-- 其他类型默认卡片布局 -->
<template v-else>
<!-- 标题行物品名 + 跑腿费 -->
<view class="card-title-row">
<text class="card-item-name">{{ getItemTitle(order) }}</text>
<view class="card-commission">
<text class="commission-label">跑腿费</text>
<text class="commission-value">{{ order.commission }}</text>
</view>
</view>
<!-- 代购垫付商品金额 -->
<view class="goods-amount-row" v-if="order.orderType === 'Purchase' && order.goodsAmount">
<text class="goods-amount-label">垫付商品金额</text>
<text class="goods-amount-value">¥{{ order.goodsAmount }}</text>
</view>
<!-- 信息行 -->
<view class="card-body">
<view class="info-row" v-if="order.pickupLocation">
<view class="dot green"></view>
<text class="info-text">取货地址{{ order.pickupLocation }}</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="info-row remark-row">
<image class="remark-icon" src="/static/ic_modify.png" mode="aspectFit"></image>
<text class="info-text">备注信息{{ order.remark || '无' }}</text>
</view>
</view>
<!-- 接单按钮 -->
<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>
</view>
</template>
</view>
</scroll-view>
@ -235,6 +260,12 @@
this.loadOrders()
},
loadMore() {},
/** 获取万能帮总费用(佣金+商品金额) */
getHelpTotalAmount(order) {
const commission = parseFloat(order.commission) || 0
const goodsAmount = parseFloat(order.goodsAmount) || 0
return (commission + goodsAmount).toFixed(1)
},
/** 获取卡片标题 */
getItemTitle(order) {
const typeMap = {
@ -246,7 +277,7 @@
}
const prefix = typeMap[order.orderType] || ''
if (order.orderType === 'Food') return `美食街订单(${order.shopCount || 0}家)`
if (order.orderType === 'Help') return `万能帮:${order.remark || ''}`
if (order.orderType === 'Help') return `万能帮:${order.itemName || ''}`
return `${prefix}${order.itemName || ''}`
},
viewFoodDetail(order) {
@ -477,9 +508,28 @@
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16rpx;
}
/* 代购垫付金额 */
.goods-amount-row {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.goods-amount-label {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.goods-amount-value {
font-size: 30rpx;
color: #FF6B00;
font-weight: bold;
}
.card-item-name {
font-size: 30rpx;
color: #333;
@ -490,7 +540,7 @@
.card-commission {
display: flex;
align-items: baseline;
align-items: center;
flex-shrink: 0;
}
@ -505,6 +555,17 @@
font-weight: bold;
}
/* 万能帮跑腿费 */
.help-commission {
display: flex;
align-items: center;
}
.help-commission-label {
font-size: 26rpx;
color: #333;
}
/* 信息行 */
.card-body {
margin-bottom: 20rpx;
@ -544,8 +605,8 @@
}
.info-text {
font-size: 26rpx;
color: #666;
font-size: 28rpx;
color: #363636;
}
/* 卡片底部 */

View File

@ -231,12 +231,11 @@ export default {
/* 顶部大图 */
.top-banner {
padding: 20rpx 24rpx 0;
width: 100%;
}
.banner-img {
width: 100%;
height: 330rpx;
border-radius: 20rpx;
}
/* 表单区域 */

View File

@ -40,26 +40,30 @@
<view class="form-group">
<text class="form-label">4.备注信息</text>
<view class="input-box textarea-box">
<textarea v-model="form.remark" placeholder="请输入备注信息,如规格、数量、品牌等" placeholder-class="placeholder" :maxlength="200" />
<textarea v-model="form.remark" placeholder="请输入备注信息,如规格、数量、品牌等" placeholder-class="placeholder"
:maxlength="200" />
</view>
</view>
<view class="form-group">
<text class="form-label">5.如何联系您</text>
<view class="input-box">
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到" placeholder-class="placeholder" maxlength="11" />
<input v-model="form.phone" type="number" placeholder="请输入手机号,对方接单后才能看到"
placeholder-class="placeholder" maxlength="11" />
</view>
</view>
<view class="form-group">
<text class="form-label">6.商品总金额</text>
<view class="input-box">
<input v-model="form.goodsAmount" type="digit" placeholder="请输入商品总金额" placeholder-class="placeholder" />
<input v-model="form.goodsAmount" type="digit" placeholder="请输入商品总金额"
placeholder-class="placeholder" />
</view>
<text class="form-tip">需包含商品总额和包装费等额外费用</text>
</view>
<view class="form-group">
<text class="form-label">7.跑腿佣金</text>
<view class="input-box">
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金最低1.0元" placeholder-class="placeholder" />
<input v-model="form.commission" type="digit" placeholder="请输入跑腿佣金最低1.0元"
placeholder-class="placeholder" />
</view>
<text class="form-tip">佣金先由平台保管接单方完成订单后才会收到佣金</text>
</view>
@ -76,136 +80,316 @@
</template>
<script>
import { createOrder, getPageBanner } from '../../utils/api'
import {
createOrder,
getPageBanner
} from '../../utils/api'
export default {
data() {
return {
form: {
itemName: '',
pickupLocation: '',
deliveryLocation: '',
remark: '',
phone: '',
goodsAmount: '',
commission: ''
export default {
data() {
return {
form: {
itemName: '',
pickupLocation: '',
deliveryLocation: '',
remark: '',
phone: '',
goodsAmount: '',
commission: ''
},
submitting: false,
statusBarHeight: 0,
bannerUrl: ''
}
},
computed: {
/** 支付金额 = 商品总金额 + 跑腿佣金 */
payAmount() {
const goods = parseFloat(this.form.goodsAmount) || 0
const commission = parseFloat(this.form.commission) || 0
return (goods + commission).toFixed(1)
}
},
onLoad() {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight || 0
this.loadBanner()
},
methods: {
goBack() {
uni.navigateBack()
},
submitting: false,
statusBarHeight: 0,
bannerUrl: ''
}
},
computed: {
/** 支付金额 = 商品总金额 + 跑腿佣金 */
payAmount() {
const goods = parseFloat(this.form.goodsAmount) || 0
const commission = parseFloat(this.form.commission) || 0
return (goods + commission).toFixed(1)
}
},
onLoad() {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight || 0
this.loadBanner()
},
methods: {
goBack() { uni.navigateBack() },
async loadBanner() {
try {
const res = await getPageBanner('purchase')
if (res?.value) this.bannerUrl = res.value
} catch (e) {}
},
validateCommission() {
const val = this.form.commission
if (!val) { uni.showToast({ title: '请输入跑腿佣金', icon: 'none' }); return false }
const num = parseFloat(val)
if (isNaN(num) || num < 1.0) { uni.showToast({ title: '跑腿佣金不可低于1.0元', icon: 'none' }); return false }
if (val.includes('.') && val.split('.')[1].length > 1) { uni.showToast({ title: '佣金最多支持小数点后1位', icon: 'none' }); return false }
return true
},
validateForm() {
if (!this.form.itemName.trim()) { uni.showToast({ title: '请输入代购物品', icon: 'none' }); return false }
if (!this.form.pickupLocation.trim()) { uni.showToast({ title: '请输入买货地点', icon: 'none' }); return false }
if (!this.form.deliveryLocation.trim()) { uni.showToast({ title: '请输入送达地点', icon: 'none' }); return false }
if (!this.form.phone.trim()) { uni.showToast({ title: '请输入手机号', icon: 'none' }); return false }
if (!this.form.goodsAmount) { uni.showToast({ title: '请输入商品总金额', icon: 'none' }); return false }
const goodsNum = parseFloat(this.form.goodsAmount)
if (isNaN(goodsNum) || goodsNum <= 0) { uni.showToast({ title: '请输入正确的商品总金额', icon: 'none' }); return false }
return this.validateCommission()
},
async onSubmit() {
if (!this.validateForm()) return
const token = uni.getStorageSync('token')
if (!token) { uni.navigateTo({ url: '/pages/login/login' }); return }
this.submitting = true
try {
const commission = parseFloat(this.form.commission)
const goodsAmount = parseFloat(this.form.goodsAmount)
const result = await createOrder({
orderType: 'Purchase',
itemName: this.form.itemName.trim(),
pickupLocation: this.form.pickupLocation.trim(),
deliveryLocation: this.form.deliveryLocation.trim(),
remark: this.form.remark.trim(),
phone: this.form.phone.trim(),
goodsAmount, commission,
totalAmount: goodsAmount + commission
async loadBanner() {
try {
const res = await getPageBanner('purchase')
if (res?.value) this.bannerUrl = res.value
} catch (e) {}
},
validateCommission() {
const val = this.form.commission
if (!val) {
uni.showToast({
title: '请输入跑腿佣金',
icon: 'none'
});
return false
}
const num = parseFloat(val)
if (isNaN(num) || num < 1.0) {
uni.showToast({
title: '跑腿佣金不可低于1.0元',
icon: 'none'
});
return false
}
if (val.includes('.') && val.split('.')[1].length > 1) {
uni.showToast({
title: '佣金最多支持小数点后1位',
icon: 'none'
});
return false
}
return true
},
validateForm() {
if (!this.form.itemName.trim()) {
uni.showToast({
title: '请输入代购物品',
icon: 'none'
});
return false
}
if (!this.form.pickupLocation.trim()) {
uni.showToast({
title: '请输入买货地点',
icon: 'none'
});
return false
}
if (!this.form.deliveryLocation.trim()) {
uni.showToast({
title: '请输入送达地点',
icon: 'none'
});
return false
}
if (!this.form.phone.trim()) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
});
return false
}
if (!this.form.goodsAmount) {
uni.showToast({
title: '请输入商品总金额',
icon: 'none'
});
return false
}
const goodsNum = parseFloat(this.form.goodsAmount)
if (isNaN(goodsNum) || goodsNum <= 0) {
uni.showToast({
title: '请输入正确的商品总金额',
icon: 'none'
});
return false
}
return this.validateCommission()
},
async onSubmit() {
if (!this.validateForm()) return
const token = uni.getStorageSync('token')
if (!token) {
uni.navigateTo({
url: '/pages/login/login'
});
return
}
this.submitting = true
try {
const commission = parseFloat(this.form.commission)
const goodsAmount = parseFloat(this.form.goodsAmount)
const result = await createOrder({
orderType: 'Purchase',
itemName: this.form.itemName.trim(),
pickupLocation: this.form.pickupLocation.trim(),
deliveryLocation: this.form.deliveryLocation.trim(),
remark: this.form.remark.trim(),
phone: this.form.phone.trim(),
goodsAmount,
commission,
totalAmount: goodsAmount + commission
})
if (result.paymentParams) await this.wxPay(result.paymentParams)
uni.showToast({
title: '下单成功',
icon: 'success'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
} catch (e) {} finally {
this.submitting = false
}
},
wxPay(params) {
return new Promise((resolve, reject) => {
uni.requestPayment({
...params,
success: resolve,
fail: (err) => {
if (err.errMsg !== 'requestPayment:fail cancel')
uni.showToast({
title: '支付失败',
icon: 'none'
})
reject(err)
}
})
})
if (result.paymentParams) await this.wxPay(result.paymentParams)
uni.showToast({ title: '下单成功', icon: 'success' })
setTimeout(() => { uni.navigateBack() }, 1500)
} catch (e) {} finally { this.submitting = false }
},
wxPay(params) {
return new Promise((resolve, reject) => {
uni.requestPayment({
...params, success: resolve,
fail: (err) => {
if (err.errMsg !== 'requestPayment:fail cancel')
uni.showToast({ title: '支付失败', icon: 'none' })
reject(err)
}
})
})
}
}
}
}
</script>
<style scoped>
.order-page {
padding-bottom: 40rpx;
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; }
.top-banner { padding: 20rpx 24rpx 0; }
.banner-img { width: 100%; height: 280rpx; border-radius: 20rpx; }
.form-section { padding: 10rpx 24rpx 0; }
.form-group { margin-bottom: 24rpx; }
.form-label { font-size: 30rpx; color: #333333; font-weight: bold; display: block; margin-bottom: 16rpx; }
.input-box {
background-color: #ffffff; border-radius: 16rpx; padding: 0 24rpx; height: 88rpx; display: flex; align-items: center;
}
.input-box input { width: 100%; height: 100%; font-size: 28rpx; color: #333; }
.textarea-box { height: 240rpx; padding: 20rpx 24rpx; align-items: flex-start; }
.textarea-box textarea { width: 100%; height: 100%; font-size: 28rpx; color: #333; }
.form-tip { font-size: 24rpx; color: #999999; margin-top: 12rpx; display: block; text-align: center; }
.submit-section { padding: 20rpx 24rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); }
.pay-amount { font-size: 32rpx; color: #e64340; font-weight: bold; display: block; text-align: center; margin-bottom: 20rpx; }
.submit-btn {
width: 100%; height: 96rpx; line-height: 96rpx; background: #FAD146; color: #ffffff;
font-size: 32rpx; font-weight: bold; border-radius: 10rpx; border: none; text-align: center;
}
.submit-btn[disabled] { opacity: 0.6; }
</style>
.order-page {
padding-bottom: 40rpx;
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;
}
.top-banner {
width: 100%;
}
.banner-img {
width: 100%;
height: 330rpx;
}
.form-section {
padding: 10rpx 24rpx 0;
}
.form-group {
margin-bottom: 24rpx;
}
.form-label {
font-size: 30rpx;
color: #333333;
font-weight: bold;
display: block;
margin-bottom: 16rpx;
}
.input-box {
background-color: #ffffff;
border-radius: 16rpx;
padding: 0 24rpx;
height: 88rpx;
display: flex;
align-items: center;
}
.input-box input {
width: 100%;
height: 100%;
font-size: 28rpx;
color: #333;
}
.textarea-box {
height: 240rpx;
padding: 20rpx 24rpx;
align-items: flex-start;
}
.textarea-box textarea {
width: 100%;
height: 100%;
font-size: 28rpx;
color: #333;
}
.form-tip {
font-size: 24rpx;
color: #999999;
margin-top: 12rpx;
display: block;
text-align: center;
}
.submit-section {
padding: 20rpx 24rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.pay-amount {
font-size: 32rpx;
color: #e64340;
font-weight: bold;
display: block;
text-align: center;
margin-bottom: 20rpx;
}
.submit-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background: #FAD146;
color: #ffffff;
font-size: 32rpx;
font-weight: bold;
border-radius: 10rpx;
border: none;
text-align: center;
}
.submit-btn[disabled] {
opacity: 0.6;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B