campus-errand/miniapp/pages/help/help.vue
18631081161 3f23dee33f
All checks were successful
continuous-integration/drone/push Build is passing
佣金
2026-04-05 14:10:51 +08:00

426 lines
9.5 KiB
Vue

<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">
<!-- 1.要做的事情 -->
<view class="form-group">
<text class="form-label">1.万能帮事项</text>
<view class="input-box textarea-box">
<textarea v-model="form.itemName" placeholder="请输入需要做什么事,需详细描述" placeholder-class="placeholder"
:maxlength="500" />
</view>
</view>
<!-- 2.如何联系您 -->
<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" />
</view>
</view>
<!-- 3.商品总金额 -->
<view class="form-group">
<text class="form-label">3.商品总金额</text>
<view class="input-box">
<input v-model="form.goodsAmount" type="digit" placeholder="请输入代购商品总金额,包含可能存在的包装费等"
placeholder-class="placeholder" />
</view>
</view>
<!-- 4.跑腿佣金 -->
<view class="form-group">
<text class="form-label">4.跑腿佣金</text>
<view class="input-box">
<input v-model="form.commission" type="digit" :placeholder="`请输入跑腿佣金,最低${minCommission}元`"
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,
getMinCommission,
cancelOrder,
confirmPayment
} from '../../utils/api'
export default {
data() {
return {
form: {
itemName: '',
phone: '',
goodsAmount: '',
commission: ''
},
submitting: false,
statusBarHeight: 0,
bannerUrl: '',
minCommission: 1.0
}
},
computed: {
/** 支付金额 = 商品总金额 + 跑腿佣金 */
payAmount() {
const goods = parseFloat(this.form.goodsAmount) || 0
const commission = parseFloat(this.form.commission) || 0
return (goods + commission).toFixed(2)
}
},
onLoad() {
const sysInfo = uni.getSystemInfoSync()
this.statusBarHeight = sysInfo.statusBarHeight || 0
this.loadBanner()
this.loadMinCommission()
this.restoreFormData()
},
methods: {
/** 恢复登录前保存的表单数据 */
restoreFormData() {
const saved = uni.getStorageSync('loginFormData')
if (saved) {
try {
const data = JSON.parse(saved)
Object.assign(this.form, data)
} catch (e) {}
uni.removeStorageSync('loginFormData')
}
},
goBack() {
uni.navigateBack()
},
async loadBanner() {
try {
const res = await getPageBanner('help')
if (res?.value) this.bannerUrl = res.value
} catch (e) {}
},
async loadMinCommission() {
try {
const res = await getMinCommission()
if (res?.value) this.minCommission = parseFloat(res.value) || 1.0
} 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 < this.minCommission) {
uni.showToast({
title: `跑腿佣金不可低于${this.minCommission}`,
icon: 'none'
})
return false
}
if (val.includes('.') && val.split('.')[1].length > 2) {
uni.showToast({
title: '佣金最多支持小数点后2位',
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 (!/^1\d{10}$/.test(this.form.phone.trim())) {
uni.showToast({
title: '请输入正确的11位手机号',
icon: 'none'
})
return false
}
if (!this.form.goodsAmount && this.form.goodsAmount !== '0') {
this.form.goodsAmount = '0'
}
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.setStorageSync('loginFormData', JSON.stringify(this.form))
uni.setStorageSync('loginRedirect', '/pages/help/help')
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
})
if (result.paymentParams) {
try {
await this.wxPay(result.paymentParams)
} catch (e) {
// 支付失败/取消,撤销订单
try { await cancelOrder(result.id) } catch (ex) {}
return
}
// 支付成功,确认订单上架(带重试)
try { await confirmPayment(result.id) } catch (ex) {
console.warn('confirmPayment 失败,等待微信回调兜底', ex)
}
}
uni.showToast({
title: '下单成功',
icon: 'success'
})
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
}, 1500)
} catch (e) {
console.error('下单失败', e)
} finally {
this.submitting = false
}
},
/** 调用微信支付 */
wxPay(params) {
return new Promise((resolve, reject) => {
uni.requestPayment({
provider: 'wxpay',
timeStamp: params.timeStamp,
nonceStr: params.nonceStr,
package: params.package_,
signType: params.signType,
paySign: params.paySign,
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::after {
border: none;
}
.submit-btn[disabled] {
opacity: 0.6;
}
</style>