优化了样式,抽象出了 WXSS/JS 库

This commit is contained in:
2026-06-13 18:19:46 +08:00
parent 6fbae9b32c
commit 3dc03f6fe5
51 changed files with 7154 additions and 9035 deletions

View File

@@ -1,547 +1,332 @@
// pages/submit/submit.js
const app = getApp()
import request from '../../utils/request.js'
// 引入弹窗服务(路径请根据项目实际调整)
import PopupService from '../../services/popupService.js'
Page({
data: {
// 全局变量
ossImageUrl: '',
apiBaseUrl: '',
// 从商品详情页传入的数据
shangpinData: null,
shangpinId: '',
// 表单数据
nicheng: '', // 游戏昵称/ID
beizhu: '', // 备注信息
zhiding: '', // 指定打手ID新增
jine: 0, // 总金额
// 页面状态
isSubmitting: false,
canSubmit: false,
isPaying: false,
// 支付相关
orderId: '',
payParams: null,
checkCount: 0,
maxCheckCount: 3
},
onLoad(options) {
// 获取全局变量
this.setData({
ossImageUrl: app.globalData.ossImageUrl || '',
apiBaseUrl: app.globalData.apiBaseUrl || ''
})
// 设置页面标题
wx.setNavigationBarTitle({
title: '提交订单'
})
// 解析传递的商品数据
try {
if (options.data) {
const shangpinData = JSON.parse(decodeURIComponent(options.data))
// 处理图片URL拼接完整路径
if (shangpinData.image && !shangpinData.image.startsWith('http')) {
shangpinData.image = this.data.ossImageUrl + shangpinData.image
}
// 处理价格显示
if (shangpinData.price) {
const price = parseFloat(shangpinData.price) || 0
const priceInteger = Math.floor(price)
let priceDecimal = '00'
const decimalPart = (price - priceInteger).toFixed(2)
if (decimalPart > 0) {
priceDecimal = decimalPart.toString().split('.')[1]
}
shangpinData.priceInteger = priceInteger
shangpinData.priceDecimal = priceDecimal
}
// 设置商品数据
this.setData({
shangpinData: shangpinData,
shangpinId: shangpinData.id || '',
jine: parseFloat(shangpinData.price) || 0,
})
} else {
this.handleDataError('商品数据不完整')
}
} catch (error) {
this.handleDataError('解析商品数据失败')
}
},
onShow() {
// 原有代码:注册通知组件
this.registerNotificationComponent();
// 🆕 进入页面时触发弹窗检查PopupService 会自动判断是否展示)
PopupService.checkAndShow(this, 'tijiao');
},
// pages/submit/submit.js 中添加 onHide 方法(如果已有则合并内容)
onHide() {
const popupComp = this.selectComponent('#popupNotice');
if (popupComp && popupComp.cleanup) {
popupComp.cleanup();
}
},
// 新增:注册通知组件
registerNotificationComponent() {
const app = getApp();
const notificationComp = this.selectComponent('#global-notification');
if (notificationComp && notificationComp.showNotification) {
//console.log('🏪 商城页面注册通知组件');
app.globalData.globalNotification = {
show: (data) => notificationComp.showNotification(data),
hide: () => notificationComp.hideNotification()
};
}
},
/**
* 处理数据错误
*/
handleDataError(errorMsg) {
wx.showToast({
title: errorMsg || '数据错误',
icon: 'none'
})
setTimeout(() => {
wx.navigateBack()
}, 1500)
},
/**
* 游戏昵称输入事件
*/
onNicknameInput(e) {
const value = e.detail.value.trim()
this.setData({
nicheng: value
}, () => {
this.checkFormValid()
})
},
/**
* 备注信息输入事件
*/
onRemarkInput(e) {
const value = e.detail.value.trim()
this.setData({
beizhu: value
})
},
/**
* 指定打手ID输入事件
*/
onZhidingInput(e) {
const value = e.detail.value.trim()
this.setData({
zhiding: value
})
},
/**
* 检查表单是否有效
*/
checkFormValid() {
const { nicheng } = this.data
const isValid = nicheng && nicheng.length > 0 && nicheng.length <= 15
this.setData({
canSubmit: isValid
})
},
/**
* 提交订单
*/
onSubmitOrder() {
const { nicheng, beizhu, zhiding, jine, shangpinId, canSubmit, isSubmitting } = this.data
// 验证表单
if (!canSubmit) {
wx.showToast({
title: '请填写游戏昵称',
icon: 'none'
})
return
}
// 防止重复提交
if (isSubmitting) {
return
}
this.setData({
isSubmitting: true,
isPaying: false
})
// 显示loading超时设为10秒
wx.showLoading({
title: '创建订单中...',
mask: true
})
const timeoutTimer = setTimeout(() => {
wx.hideLoading()
wx.showToast({
title: '网络超时,请检查您是否已登录',
icon: 'none'
})
this.setData({
isSubmitting: false,
isPaying: false
})
}, 10000)
// 构建请求数据添加zhiding字段
const orderData = {
shangpin_id: shangpinId, // 商品ID
nicheng: nicheng, // 游戏昵称
beizhu: beizhu || '', // 备注(可为空)
zhiding: zhiding || '', // 指定打手ID可为空
jine: jine // 金额
}
// 调用后端创建订单接口
request({
url: '/dingdan/xiadan',
method: 'POST',
data: orderData
})
.then(res => {
clearTimeout(timeoutTimer)
wx.hideLoading()
if (res.statusCode === 200 && res.data) {
const response = res.data
if (response.code === 0 && response.data) {
// 保存订单ID和支付参数
const orderId = response.data.dingdanid
const payParams = response.data.payParams || {}
this.setData({
orderId: orderId,
payParams: payParams,
isSubmitting: false,
isPaying: true,
checkCount: 0
})
// 调起微信支付
this.triggerWxPayment(payParams)
} else {
wx.showToast({
title: response.msg || '创建订单失败',
icon: 'none'
})
this.setData({
isSubmitting: false,
isPaying: false
})
}
} else {
wx.showToast({
title: '网络错误,请重试',
icon: 'none'
})
this.setData({
isSubmitting: false,
isPaying: false
})
}
})
.catch(error => {
clearTimeout(timeoutTimer)
wx.hideLoading()
wx.showToast({
title: '创建订单失败',
icon: 'none'
})
this.setData({
isSubmitting: false,
isPaying: false
})
})
},
/**
* 调起微信支付
*/
triggerWxPayment(payParams) {
// 验证支付参数是否完整
if (!payParams || !payParams.timeStamp || !payParams.nonceStr ||
!payParams.package || !payParams.signType || !payParams.paySign) {
wx.showToast({
title: '支付参数不完整',
icon: 'none'
})
this.setData({
isSubmitting: false,
isPaying: false
})
return
}
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
success: (res) => {
// 支付成功,开始双重确认
this.onPaymentSuccess()
},
fail: (err) => {
// 根据错误类型处理
if (err.errMsg === 'requestPayment:fail cancel') {
// 用户取消支付
this.handleUserCancel()
} else {
// 其他支付错误
this.handlePaymentFail(err)
}
}
})
},
/**
* 处理用户取消支付
*/
handleUserCancel() {
const { orderId } = this.data
wx.showToast({
title: '已取消支付',
icon: 'none',
duration: 1500
})
// 通知后端用户取消(静默请求,不阻塞用户)
if (orderId) {
request({
url: '/dingdan/shibai',
method: 'POST',
data: {
dingdanid: orderId,
yuanyin: 'user_cancel'
}
}).catch(() => {
// 静默失败
})
}
this.setData({
isSubmitting: false,
isPaying: false
})
},
/**
* 处理支付失败
*/
handlePaymentFail(err) {
const { orderId } = this.data
wx.showToast({
title: '支付失败',
icon: 'none',
duration: 1500
})
// 通知后端支付失败简化流程直接请求不显示loading
if (orderId) {
request({
url: '/dingdan/shibai',
method: 'POST',
data: {
dingdanid: orderId,
yuanyin: 'payment_fail',
cuowu: err.errMsg || ''
}
}).catch(() => {
// 静默失败
}).finally(() => {
this.setData({
isSubmitting: false,
isPaying: false
})
})
} else {
this.setData({
isSubmitting: false,
isPaying: false
})
}
},
/**
* 支付成功处理(双重确认)
*/
onPaymentSuccess() {
const { orderId } = this.data
if (!orderId) {
wx.showToast({
title: '订单ID不存在',
icon: 'none'
})
this.setData({
isSubmitting: false,
isPaying: false
})
return
}
// 显示支付成功提示(时间缩短)
wx.showToast({
title: '支付成功,确认中...',
icon: 'none',
duration: 1500
})
// 开始双重确认流程
this.checkOrderStatus()
},
/**
* 检查订单状态(双重确认)
*/
checkOrderStatus() {
const { orderId, checkCount, maxCheckCount } = this.data
// 显示加载(时间缩短)
wx.showLoading({
title: `确认支付状态${checkCount > 0 ? `(${checkCount + 1})` : ''}`,
mask: true
})
// 超时设为5秒
const timeoutTimer = setTimeout(() => {
wx.hideLoading()
this.handleConfirmError('网络超时')
}, 5000)
// 向后端发起支付复查请求
request({
url: '/dingdan/fucha',
method: 'POST',
data: {
dingdanid: orderId
}
})
.then(res => {
clearTimeout(timeoutTimer)
wx.hideLoading()
if (res.statusCode === 200 && res.data) {
const response = res.data
if (response.code === 0) {
// 确认成功,订单状态已更新
this.handleConfirmSuccess()
} else {
// 后端返回错误
this.handleConfirmError(response.msg || '订单状态异常')
}
} else {
// 网络错误
this.handleConfirmError('网络错误')
}
})
.catch(error => {
clearTimeout(timeoutTimer)
wx.hideLoading()
this.handleConfirmError('检查失败')
})
},
/**
* 处理确认成功
*/
handleConfirmSuccess() {
// 显示最终成功提示
wx.showToast({
title: '购买成功!',
icon: 'success',
duration: 1500
})
// 1.5秒后跳转到首页
setTimeout(() => {
wx.switchTab({
url: '/pages/index/index'
})
}, 1500)
this.setData({
isSubmitting: false,
isPaying: false
})
},
/**
* 处理确认错误
*/
handleConfirmError(errorMsg) {
const { checkCount, maxCheckCount } = this.data
const newCheckCount = checkCount + 1
if (newCheckCount < maxCheckCount) {
// 还可以重试
this.setData({
checkCount: newCheckCount
})
// 1秒后重试
setTimeout(() => {
this.checkOrderStatus()
}, 1000)
} else {
// 达到最大重试次数
wx.showModal({
title: '支付确认中',
content: '支付已成功,但订单状态确认异常。请稍后在订单列表中查看状态,如有问题请联系客服。',
showCancel: false,
confirmText: '我知道了',
success: (res) => {
if (res.confirm) {
wx.switchTab({
url: '/pages/index/index'
})
}
}
})
this.setData({
isSubmitting: false,
isPaying: false
})
}
}
})
// pages/submit/submit.js
const app = getApp()
import request from '../../utils/request.js'
import PopupService from '../../services/popupService.js'
Page({
data: {
ossImageUrl: '',
apiBaseUrl: '',
shangpinData: null,
shangpinId: '',
nicheng: '',
beizhu: '',
zhiding: '',
quantity: 1,
jine: 0,
totalPriceInteger: '0',
totalPriceDecimal: '00',
isSubmitting: false,
canSubmit: false,
isPaying: false,
orderId: '',
payParams: null,
checkCount: 0,
maxCheckCount: 3
},
onLoad(options) {
this.setData({
ossImageUrl: app.globalData.ossImageUrl || '',
apiBaseUrl: app.globalData.apiBaseUrl || ''
})
wx.setNavigationBarTitle({ title: '提交订单' })
try {
if (options.data) {
const shangpinData = JSON.parse(decodeURIComponent(options.data))
if (shangpinData.image && !shangpinData.image.startsWith('http')) {
shangpinData.image = this.data.ossImageUrl + shangpinData.image
}
if (shangpinData.price) {
const price = parseFloat(shangpinData.price) || 0
const priceInteger = Math.floor(price)
let priceDecimal = '00'
const decimalPart = (price - priceInteger).toFixed(2)
if (decimalPart > 0) {
priceDecimal = decimalPart.toString().split('.')[1]
}
shangpinData.priceInteger = priceInteger
shangpinData.priceDecimal = priceDecimal
}
this.setData({
shangpinData: shangpinData,
shangpinId: shangpinData.id || '',
jine: parseFloat(shangpinData.price) || 0,
}, () => this.updateTotalPrice())
} else {
this.handleDataError('商品数据不完整')
}
} catch (error) {
this.handleDataError('解析商品数据失败')
}
},
onShow() {
this.registerNotificationComponent()
PopupService.checkAndShow(this, 'tijiao')
},
onHide() {
const popupComp = this.selectComponent('#popupNotice')
if (popupComp && popupComp.cleanup) popupComp.cleanup()
},
registerNotificationComponent() {
const app = getApp()
const notificationComp = this.selectComponent('#global-notification')
if (notificationComp && notificationComp.showNotification) {
app.globalData.globalNotification = {
show: (data) => notificationComp.showNotification(data),
hide: () => notificationComp.hideNotification()
}
}
},
updateTotalPrice() {
const total = this.data.jine * this.data.quantity
const intPart = Math.floor(total)
const decPart = (total - intPart).toFixed(2).split('.')[1]
this.setData({
totalPriceInteger: intPart,
totalPriceDecimal: decPart
})
},
onQtyMinus() {
if (this.data.quantity <= 1) return
this.setData({ quantity: this.data.quantity - 1 }, () => this.updateTotalPrice())
},
onQtyPlus() {
if (this.data.quantity >= 99) return
this.setData({ quantity: this.data.quantity + 1 }, () => this.updateTotalPrice())
},
handleDataError(errorMsg) {
wx.showToast({ title: errorMsg || '数据错误', icon: 'none' })
setTimeout(() => wx.navigateBack(), 1500)
},
goBack() {
wx.navigateBack()
},
onNicknameInput(e) {
this.setData({ nicheng: e.detail.value.trim() }, () => this.checkFormValid())
},
onRemarkInput(e) {
this.setData({ beizhu: e.detail.value.trim() })
},
onZhidingInput(e) {
this.setData({ zhiding: e.detail.value.trim() })
},
checkFormValid() {
const { nicheng } = this.data
this.setData({ canSubmit: nicheng && nicheng.length > 0 && nicheng.length <= 15 })
},
onSubmitOrder() {
const { nicheng, beizhu, zhiding, jine, quantity, shangpinId, canSubmit, isSubmitting } = this.data
if (!canSubmit) {
wx.showToast({ title: '请填写游戏昵称', icon: 'none' })
return
}
if (isSubmitting) return
this.setData({ isSubmitting: true, isPaying: false })
wx.showLoading({ title: '创建订单中...', mask: true })
const timeoutTimer = setTimeout(() => {
wx.hideLoading()
wx.showToast({ title: '网络超时,请检查您是否已登录', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
}, 10000)
const orderData = {
shangpin_id: shangpinId,
nicheng: nicheng,
beizhu: beizhu || '',
zhiding: zhiding || '',
jine: jine * quantity,
shuliang: quantity
}
request({
url: '/dingdan/xiadan',
method: 'POST',
data: orderData
})
.then(res => {
clearTimeout(timeoutTimer)
wx.hideLoading()
if (res.statusCode === 200 && res.data) {
const response = res.data
if (response.code === 0 && response.data) {
const orderId = response.data.dingdanid
const payParams = response.data.payParams || {}
this.setData({
orderId: orderId,
payParams: payParams,
isSubmitting: false,
isPaying: true,
checkCount: 0
})
this.triggerWxPayment(payParams)
} else {
wx.showToast({ title: response.msg || '创建订单失败', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
}
} else {
wx.showToast({ title: '网络错误,请重试', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
}
})
.catch(() => {
clearTimeout(timeoutTimer)
wx.hideLoading()
wx.showToast({ title: '创建订单失败', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
})
},
triggerWxPayment(payParams) {
if (!payParams || !payParams.timeStamp || !payParams.nonceStr ||
!payParams.package || !payParams.signType || !payParams.paySign) {
wx.showToast({ title: '支付参数不完整', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
return
}
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
success: () => this.onPaymentSuccess(),
fail: (err) => {
if (err.errMsg === 'requestPayment:fail cancel') {
this.handleUserCancel()
} else {
this.handlePaymentFail(err)
}
}
})
},
handleUserCancel() {
const { orderId } = this.data
wx.showToast({ title: '已取消支付', icon: 'none', duration: 1500 })
if (orderId) {
request({
url: '/dingdan/shibai',
method: 'POST',
data: { dingdanid: orderId, yuanyin: 'user_cancel' }
}).catch(() => {})
}
this.setData({ isSubmitting: false, isPaying: false })
},
handlePaymentFail(err) {
const { orderId } = this.data
wx.showToast({ title: '支付失败', icon: 'none', duration: 1500 })
if (orderId) {
request({
url: '/dingdan/shibai',
method: 'POST',
data: { dingdanid: orderId, yuanyin: 'payment_fail', cuowu: err.errMsg || '' }
}).catch(() => {}).finally(() => {
this.setData({ isSubmitting: false, isPaying: false })
})
} else {
this.setData({ isSubmitting: false, isPaying: false })
}
},
onPaymentSuccess() {
const { orderId } = this.data
if (!orderId) {
wx.showToast({ title: '订单ID不存在', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
return
}
wx.showToast({ title: '支付成功,确认中...', icon: 'none', duration: 1500 })
this.checkOrderStatus()
},
checkOrderStatus() {
const { orderId, checkCount, maxCheckCount } = this.data
wx.showLoading({ title: `确认支付状态${checkCount > 0 ? `(${checkCount + 1})` : ''}`, mask: true })
const timeoutTimer = setTimeout(() => {
wx.hideLoading()
this.handleConfirmError('网络超时')
}, 5000)
request({
url: '/dingdan/fucha',
method: 'POST',
data: { dingdanid: orderId }
})
.then(res => {
clearTimeout(timeoutTimer)
wx.hideLoading()
if (res.statusCode === 200 && res.data) {
if (res.data.code === 0) {
this.handleConfirmSuccess()
} else {
this.handleConfirmError(res.data.msg || '订单状态异常')
}
} else {
this.handleConfirmError('网络错误')
}
})
.catch(() => {
clearTimeout(timeoutTimer)
wx.hideLoading()
this.handleConfirmError('检查失败')
})
},
handleConfirmSuccess() {
wx.showToast({ title: '购买成功!', icon: 'success', duration: 1500 })
setTimeout(() => wx.switchTab({ url: '/pages/index/index' }), 1500)
this.setData({ isSubmitting: false, isPaying: false })
},
handleConfirmError(errorMsg) {
const newCheckCount = this.data.checkCount + 1
if (newCheckCount < this.data.maxCheckCount) {
this.setData({ checkCount: newCheckCount })
setTimeout(() => this.checkOrderStatus(), 1000)
} else {
wx.showModal({
title: '支付确认中',
content: '支付已成功,但订单状态确认异常。请稍后在订单列表中查看状态,如有问题请联系客服。',
showCancel: false,
confirmText: '我知道了',
success: (res) => {
if (res.confirm) wx.switchTab({ url: '/pages/index/index' })
}
})
this.setData({ isSubmitting: false, isPaying: false })
}
}
})

View File

@@ -1,124 +1,132 @@
<!--pages/submit/submit.wxml-->
<view class="submit-page">
<!-- 商品信息区域 -->
<view class="product-section">
<view class="product-card">
<!-- 商品图片 -->
<view class="product-image-box">
<image
src="{{shangpinData && shangpinData.image ? shangpinData.image : ''}}"
mode="aspectFill"
class="product-image"
/>
</view>
<!-- 商品信息 -->
<view class="product-info">
<view class="product-title">
<text class="title-text">{{shangpinData && shangpinData.title ? shangpinData.title : '商品标题'}}</text>
</view>
<!-- 价格信息 -->
<view class="price-info">
<text class="price-label">价格:</text>
<view class="price-value">
<text class="price-icon">¥</text>
<text class="price-integer">{{shangpinData && shangpinData.priceInteger ? shangpinData.priceInteger : '0'}}</text>
<text class="price-decimal">.{{shangpinData && shangpinData.priceDecimal ? shangpinData.priceDecimal : '00'}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 表单区域(左右布局) -->
<view class="form-section">
<view class="form-card">
<!-- 游戏昵称 - 左右布局 -->
<view class="form-item-row">
<view class="item-label-row">
<text class="label-text-bold">昵称</text>
<text class="label-required">*</text>
</view>
<input
class="item-input-row"
type="text"
placeholder="请输入您的昵称"
placeholder-class="input-placeholder-gray"
maxlength="15"
value="{{nicheng}}"
bindinput="onNicknameInput"
/>
</view>
<!-- 备注信息 - 左右布局 -->
<view class="form-item-row">
<view class="item-label-row">
<text class="label-text-bold">备注</text>
</view>
<input
class="item-input-row"
type="text"
placeholder="请补充您要补充的其他联系信息"
placeholder-class="input-placeholder-gray"
maxlength="30"
value="{{beizhu}}"
bindinput="onRemarkInput"
/>
</view>
<!-- 指定打手ID - 左右布局 -->
<view class="form-item-row">
<view class="item-label-row">
<text class="label-text-bold">指定服务者ID(选填)</text>
</view>
<input
class="item-input-row"
type="text"
placeholder="填写您需要指定的服务者"
placeholder-class="input-placeholder-gray"
maxlength="20"
value="{{zhiding}}"
bindinput="onZhidingInput"
/>
</view>
<!-- 支付方式 -->
<view class="payment-item">
<view class="payment-method">
<image src="/images/wechat-pay.png" class="payment-icon" />
<text class="payment-text">微信支付</text>
<view class="payment-checkbox">
<text class="checkbox-text">✓</text>
</view>
</view>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="footer-section">
<view class="total-price">
<text class="total-label">总价:</text>
<view class="total-value">
<text class="price-icon">¥</text>
<text class="price-integer">{{shangpinData && shangpinData.priceInteger ? shangpinData.priceInteger : '0'}}</text>
<text class="price-decimal">.{{shangpinData && shangpinData.priceDecimal ? shangpinData.priceDecimal : '00'}}</text>
</view>
</view>
<view
class="submit-btn {{canSubmit && !isSubmitting ? 'submit-btn-active' : 'submit-btn-disabled'}}"
bindtap="onSubmitOrder"
>
<text class="btn-text">{{isSubmitting ? '提交中...' : '提交订单'}}</text>
<view class="btn-glow"></view>
</view>
</view>
<!-- 全局通知组件 -->
<global-notification id="global-notification" />
<!-- 弹窗公告组件(供 PopupService 调用) -->
<popup-notice id="popupNotice" />
</view>
<!--pages/submit/submit.wxml-->
<view class="page">
<!-- 商品摘要 - 拼多多SKU弹窗顶部风格 -->
<view class="sku-header">
<image class="sku-thumb" src="{{shangpinData.image || ''}}" mode="aspectFill" />
<view class="sku-header-info">
<view class="sku-price-row">
<text class="sku-price-sym">¥</text>
<text class="sku-price-int">{{totalPriceInteger}}</text>
<text class="sku-price-dec">.{{totalPriceDecimal}}</text>
</view>
<text class="sku-stock">库存{{shangpinData.kucun || 0}}件</text>
<text class="sku-selected">已选:{{quantity}}份</text>
</view>
<view class="sku-close" bindtap="goBack">×</view>
</view>
<!-- 购买数量 -->
<view class="sku-section">
<text class="sku-section-title">购买数量</text>
<view class="qty-row">
<view class="qty-btn {{quantity <= 1 ? 'qty-btn--disabled' : ''}}" bindtap="onQtyMinus">-</view>
<text class="qty-val">{{quantity}}</text>
<view class="qty-btn {{quantity >= 99 ? 'qty-btn--disabled' : ''}}" bindtap="onQtyPlus">+</view>
</view>
</view>
<!-- 服务信息 -->
<view class="sku-section">
<text class="sku-section-title">服务信息</text>
<view class="form-field">
<view class="field-top">
<text class="field-name">游戏昵称</text>
<text class="field-required">必填</text>
</view>
<input
class="field-input"
type="text"
placeholder="请输入您的游戏昵称"
placeholder-class="ph"
maxlength="15"
value="{{nicheng}}"
bindinput="onNicknameInput"
/>
</view>
<view class="form-field">
<view class="field-top">
<text class="field-name">备注</text>
<text class="field-optional">选填</text>
</view>
<input
class="field-input"
type="text"
placeholder="补充其他联系信息"
placeholder-class="ph"
maxlength="30"
value="{{beizhu}}"
bindinput="onRemarkInput"
/>
</view>
<view class="form-field">
<view class="field-top">
<text class="field-name">指定服务者</text>
<text class="field-optional">选填</text>
</view>
<input
class="field-input"
type="text"
placeholder="填写指定服务者ID"
placeholder-class="ph"
maxlength="20"
value="{{zhiding}}"
bindinput="onZhidingInput"
/>
</view>
</view>
<!-- 服务保障 -->
<view class="sku-section">
<text class="sku-section-title">服务保障</text>
<view class="guarantee-row">
<view class="guarantee-item">
<text class="g-check">✓</text>
<text class="g-text">极速接单</text>
</view>
<view class="guarantee-item">
<text class="g-check">✓</text>
<text class="g-text">资金担保</text>
</view>
<view class="guarantee-item">
<text class="g-check">✓</text>
<text class="g-text">售后保障</text>
</view>
<view class="guarantee-item">
<text class="g-check">✓</text>
<text class="g-text">隐私保护</text>
</view>
</view>
</view>
<!-- 支付方式 -->
<view class="sku-section">
<text class="sku-section-title">支付方式</text>
<view class="pay-row">
<image src="/images/wechat-pay.png" class="pay-icon" />
<text class="pay-label">微信支付</text>
<view class="pay-check">
<text class="pay-check-icon">✓</text>
</view>
</view>
</view>
<!-- 底部留白 -->
<view class="bottom-space"></view>
<!-- 底部确认按钮 -->
<view class="confirm-bar">
<view
class="confirm-btn {{canSubmit && !isSubmitting ? 'confirm-btn--active' : 'confirm-btn--disabled'}}"
bindtap="onSubmitOrder"
hover-class="confirm-btn--hover"
>
{{isSubmitting ? '提交中...' : '确定'}}
</view>
</view>
<global-notification id="global-notification" />
<popup-notice id="popupNotice" />
</view>

View File

@@ -1,280 +1,296 @@
/* pages/submit/submit.wxss */
.submit-page {
display: flex;
flex-direction: column;
min-height: 100vh;
background: linear-gradient(135deg, #f8f9ff 0%, #f0f3ff 100%);
box-sizing: border-box;
}
/* 商品信息区域 */
.product-section {
padding: 30rpx 30rpx 20rpx 30rpx;
flex-shrink: 0;
}
.product-card {
background: white;
border-radius: 20rpx;
padding: 30rpx;
display: flex;
align-items: center;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06);
}
.product-image-box {
width: 160rpx;
height: 160rpx;
border-radius: 16rpx;
overflow: hidden;
background: #f5f5f5;
flex-shrink: 0;
margin-right: 24rpx;
}
.product-image {
width: 100%;
height: 100%;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 160rpx;
}
.product-title {
margin-bottom: 20rpx;
}
.title-text {
font-size: 30rpx;
font-weight: 600;
color: #222;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.price-info {
display: flex;
align-items: baseline;
}
.price-label {
font-size: 26rpx;
color: #666;
font-weight: 500;
}
.price-value {
display: flex;
align-items: baseline;
}
.price-icon {
font-size: 28rpx;
color: #ff4d4f;
font-weight: 600;
}
.price-integer {
font-size: 38rpx;
color: #ff4d4f;
font-weight: 700;
margin-left: 4rpx;
}
.price-decimal {
font-size: 28rpx;
color: #ff4d4f;
font-weight: 600;
}
/* 表单区域 */
.form-section {
padding: 0 30rpx 20rpx 30rpx;
flex-shrink: 0;
}
.form-card {
background: white;
border-radius: 20rpx;
padding: 30rpx;
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.05);
}
.form-item-row {
display: flex;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.form-item-row:last-child {
border-bottom: none;
}
.item-label-row {
width: 220rpx;
flex-shrink: 0;
display: flex;
align-items: center;
}
.label-text-bold {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.label-required {
font-size: 30rpx;
color: #ff4d4f;
margin-left: 8rpx;
}
.item-input-row {
flex: 1;
font-size: 28rpx;
color: #222;
height: 60rpx;
line-height: 60rpx;
padding: 0 20rpx;
text-align: right;
background: #fafafa;
border-radius: 10rpx;
}
.input-placeholder-gray {
color: #999;
font-size: 28rpx;
}
/* 支付方式 */
.payment-item {
margin-top: 30rpx;
padding-top: 30rpx;
border-top: 1rpx solid #f0f0f0;
}
.payment-method {
display: flex;
align-items: center;
padding: 20rpx 0;
}
.payment-icon {
width: 48rpx;
height: 48rpx;
margin-right: 20rpx;
}
.payment-text {
font-size: 28rpx;
color: #333;
font-weight: 500;
flex: 1;
}
.payment-checkbox {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: #52c41a;
display: flex;
align-items: center;
justify-content: center;
}
.checkbox-text {
color: white;
font-size: 28rpx;
font-weight: 600;
}
/* 底部操作栏 */
.footer-section {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 30rpx;
background: white;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
margin-top: auto;
flex-shrink: 0;
}
.total-price {
display: flex;
align-items: baseline;
flex: 1;
}
.total-label {
font-size: 30rpx;
color: #333;
font-weight: 600;
}
.total-value {
display: flex;
align-items: baseline;
}
.submit-btn {
width: 240rpx;
height: 88rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: all 0.3s;
}
.submit-btn-active {
background: linear-gradient(135deg, #52c41a, #389e0d);
box-shadow: 0 8rpx 32rpx rgba(255, 77, 79, 0.4);
}
.submit-btn-disabled {
background: #ccc;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
}
.submit-btn-active:active {
transform: scale(0.98);
box-shadow: 0 4rpx 20rpx rgba(255, 77, 79, 0.5);
}
.btn-text {
font-size: 32rpx;
color: white;
font-weight: 600;
position: relative;
z-index: 2;
}
.btn-glow {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transform: translateX(-100%);
animation: btnGlow 3s infinite;
}
@keyframes btnGlow {
0% { transform: translateX(-100%); }
50% { transform: translateX(100%); }
100% { transform: translateX(100%); }
}
/* pages/submit/submit.wxss */
page {
background: #f2f2f2;
}
.page {
min-height: 100vh;
padding-bottom: 140rpx;
box-sizing: border-box;
}
/* SKU头部 - 拼多多弹窗风格 */
.sku-header {
display: flex;
background: #fff;
padding: 24rpx;
position: relative;
margin-bottom: 16rpx;
}
.sku-thumb {
width: 180rpx;
height: 180rpx;
border-radius: 12rpx;
flex-shrink: 0;
margin-right: 20rpx;
background: #f5f5f5;
}
.sku-header-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 4rpx 0;
}
.sku-price-row {
display: flex;
align-items: baseline;
}
.sku-price-sym {
font-size: 28rpx;
color: #e02e24;
font-weight: 700;
}
.sku-price-int {
font-size: 48rpx;
color: #e02e24;
font-weight: 800;
line-height: 1;
margin-left: 2rpx;
}
.sku-price-dec {
font-size: 28rpx;
color: #e02e24;
font-weight: 700;
}
.sku-stock {
font-size: 24rpx;
color: #999;
}
.sku-selected {
font-size: 24rpx;
color: #666;
}
.sku-close {
position: absolute;
top: 16rpx;
right: 16rpx;
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
color: #999;
border-radius: 50%;
background: #f5f5f5;
line-height: 1;
}
/* SKU区块 */
.sku-section {
background: #fff;
padding: 24rpx 28rpx;
margin-bottom: 16rpx;
}
.sku-section-title {
font-size: 28rpx;
font-weight: 700;
color: #222;
margin-bottom: 20rpx;
display: block;
}
/* 数量选择器 */
.qty-row {
display: flex;
align-items: center;
}
.qty-btn {
width: 64rpx;
height: 64rpx;
border-radius: 8rpx;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 700;
color: #333;
border: 1rpx solid #e8e8e8;
}
.qty-btn--disabled {
color: #ccc;
border-color: #f0f0f0;
background: #fafafa;
}
.qty-val {
min-width: 80rpx;
text-align: center;
font-size: 32rpx;
font-weight: 700;
color: #222;
margin: 0 20rpx;
}
/* 表单字段 */
.form-field {
padding: 16rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.form-field:last-child {
border-bottom: none;
padding-bottom: 0;
}
.field-top {
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
.field-name {
font-size: 26rpx;
font-weight: 600;
color: #333;
}
.field-required {
font-size: 20rpx;
color: #e02e24;
background: #fff1f0;
padding: 2rpx 10rpx;
border-radius: 4rpx;
margin-left: 10rpx;
}
.field-optional {
font-size: 20rpx;
color: #bbb;
margin-left: 10rpx;
}
.field-input {
width: 100%;
height: 72rpx;
background: #f7f7f7;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 26rpx;
color: #333;
border: 1rpx solid #eee;
box-sizing: border-box;
}
.ph {
color: #bbb;
font-size: 26rpx;
}
/* 服务保障 */
.guarantee-row {
display: flex;
flex-wrap: wrap;
gap: 12rpx 20rpx;
}
.guarantee-item {
display: flex;
align-items: center;
}
.g-check {
font-size: 20rpx;
color: #e02e24;
font-weight: 700;
margin-right: 4rpx;
}
.g-text {
font-size: 24rpx;
color: #666;
}
/* 支付方式 */
.pay-row {
display: flex;
align-items: center;
}
.pay-icon {
width: 44rpx;
height: 44rpx;
margin-right: 16rpx;
}
.pay-label {
flex: 1;
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.pay-check {
width: 36rpx;
height: 36rpx;
border-radius: 50%;
background: #52c41a;
display: flex;
align-items: center;
justify-content: center;
}
.pay-check-icon {
color: #fff;
font-size: 22rpx;
font-weight: 700;
}
/* 底部留白 */
.bottom-space {
height: 20rpx;
}
/* 底部确认按钮 */
.confirm-bar {
position: fixed;
bottom: 0;
right: 0;
width: 50%;
height: 100rpx;
background: #fff;
padding-bottom: env(safe-area-inset-bottom);
z-index: 9999;
display: flex;
}
.confirm-btn {
width: 100%;
height: 100%;
border-radius: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 700;
color: #fff;
letter-spacing: 4rpx;
}
.confirm-btn--active {
background: #e02e24;
}
.confirm-btn--disabled {
background: #ccc;
}
.confirm-btn--hover {
opacity: 0.9;
transform: scale(0.98);
}