campus-errand/miniapp/pages/purchase/purchase.vue
2026-03-17 15:42:18 +08:00

395 lines
8.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="order-page">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content">
<view class="nav-back" @click="goBack">
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
</view>
<text class="navbar-title">代购</text>
<view class="nav-placeholder"></view>
</view>
</view>
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
<!-- 顶部大图 -->
<view class="top-banner" v-if="bannerUrl">
<image class="banner-img" :src="bannerUrl" mode="aspectFill"></image>
</view>
<!-- 表单区域 -->
<view class="form-section">
<view class="form-group">
<text class="form-label">1.代购物品</text>
<view class="input-box">
<input v-model="form.itemName" placeholder="请输入代购物品名" placeholder-class="placeholder" />
</view>
</view>
<view class="form-group">
<text class="form-label">2.买货地点</text>
<view class="input-box">
<input v-model="form.pickupLocation" placeholder="请输入买货地点" placeholder-class="placeholder" />
</view>
</view>
<view class="form-group">
<text class="form-label">3.送达地点</text>
<view class="input-box">
<input v-model="form.deliveryLocation" placeholder="请输入送达地点" placeholder-class="placeholder" />
</view>
</view>
<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" />
</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" />
</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" />
</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" />
</view>
<text class="form-tip">佣金先由平台保管,接单方完成订单后才会收到佣金</text>
</view>
</view>
<!-- 支付金额 + 提交按钮 -->
<view class="submit-section">
<text class="pay-amount">支付金额:¥{{ payAmount }}</text>
<button class="submit-btn" @click="onSubmit" :loading="submitting" :disabled="submitting">
支付佣金 确定下单
</button>
</view>
</view>
</template>
<script>
import {
createOrder,
getPageBanner
} from '../../utils/api'
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()
},
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)
}
})
})
}
}
}
</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 {
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>