fix(orders): 修复订单列表显示问题并优化详情页展示

修复订单列表不显示 source_type=3 订单的问题,支持对对碰等玩法订单
优化订单标题显示逻辑,移除内部标识并添加保底显示
优化订单详情页,当没有实物商品时显示活动信息
重构订单类型判断逻辑,支持更多玩法类型
This commit is contained in:
邹方成 2025-12-22 14:40:53 +08:00
parent 2d218018e8
commit be57eda392
5 changed files with 230 additions and 42 deletions

View File

@ -214,7 +214,7 @@
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import PaymentPopup from '../../../components/PaymentPopup.vue'
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, joinLottery, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame } from '../../../api/appUser'
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame } from '../../../api/appUser'
const detail = ref({})
const statusText = ref('')
@ -1037,7 +1037,7 @@ async function doDraw() {
const openid = uni.getStorageSync('openid')
if (!openid) { uni.showToast({ title: '缺少OpenID请重新登录', icon: 'none' }); return }
uni.showLoading({ title: '拉起支付...' })
uni.showLoading({ title: '创建订单...' })
try {
if (!selectedCardType.value) {
uni.hideLoading()
@ -1045,17 +1045,24 @@ async function doDraw() {
return
}
const joinRes = await joinLottery({
activity_id: Number(aid),
// 1. createMatchingPreorder
const preRes = await createMatchingPreorder({
issue_id: Number(iid),
channel: 'miniapp',
count: 1,
coupon_id: selectedCoupon.value?.id ? Number(selectedCoupon.value.id) : 0
position: String(selectedCardType.value.code || ''),
coupon_id: selectedCoupon.value?.id ? Number(selectedCoupon.value.id) : 0,
item_card_id: selectedCard.value?.id ? Number(selectedCard.value.id) : 0
})
if (!joinRes) throw new Error('下单失败')
const orderNo = joinRes.order_no || joinRes.data?.order_no || joinRes.result?.order_no || joinRes.orderNo
if (!orderNo) throw new Error('未获取到订单号')
if (!preRes) throw new Error('创建订单失败')
// 2.
const orderNo = preRes.order_no || preRes.data?.order_no || preRes.result?.order_no || preRes.orderNo
if (!orderNo) throw new Error('未获取到订单号')
const gameId = preRes.game_id || preRes.data?.game_id || preRes.result?.game_id || preRes.gameId
const allCards = normalizeAllCards(preRes.all_cards || preRes.data?.all_cards || preRes.result?.all_cards || [])
// 3.
uni.showLoading({ title: '拉起支付...' })
const payRes = await createWechatOrder({ openid, order_no: orderNo })
await new Promise((resolve, reject) => {
uni.requestPayment({
@ -1063,23 +1070,14 @@ async function doDraw() {
timeStamp: payRes.timeStamp || payRes.timestamp,
nonceStr: payRes.nonceStr || payRes.noncestr,
package: payRes.package,
signType: payRes.signType || 'MD5',
signType: payRes.signType || 'RSA',
paySign: payRes.paySign,
success: resolve,
fail: reject
})
})
uni.showLoading({ title: '创建游戏...' })
const preRes = await createMatchingPreorder({
issue_id: Number(iid),
position: String(selectedCardType.value.code || ''),
coupon_id: selectedCoupon.value?.id ? Number(selectedCoupon.value.id) : 0,
item_card_id: selectedCard.value?.id ? Number(selectedCard.value.id) : 0
})
if (!preRes) throw new Error('创建游戏失败')
const gameId = preRes.game_id || preRes.data?.game_id || preRes.result?.game_id || preRes.gameId
const allCards = normalizeAllCards(preRes.all_cards || preRes.data?.all_cards || preRes.result?.all_cards || [])
// 4.
if (gameId) {
writeMatchingGameCacheEntry(aid, iid, {
game_id: String(gameId),
@ -1090,11 +1088,14 @@ async function doDraw() {
}
uni.hideLoading()
uni.showModal({
title: '支付成功',
content: '已创建对对碰游戏,可点击“继续游戏”继续。',
showCancel: false
})
uni.showToast({ title: '支付成功', icon: 'success' })
// 5.
syncResumeGame(aid)
const latest = findLatestMatchingGameCacheEntry(aid)
if (latest && latest.entry && latest.entry.game_id) {
await openGame(latest)
}
} catch (e) {
uni.hideLoading()
if (e?.errMsg && String(e.errMsg).includes('cancel')) {

View File

@ -40,14 +40,15 @@
<view class="section-card product-section">
<view class="section-header">
<text class="section-title">商品清单</text>
<text class="item-count"> {{ order.items ? order.items.length : 0 }} </text>
<text class="item-count"> {{ order.items ? order.items.length : (order.activity_name ? 1 : 0) }} </text>
</view>
<view class="order-items">
<!-- 常规商品列表 -->
<view v-for="(item, index) in order.items" :key="index" class="item-card">
<view class="item-image-wrap">
<image class="item-image" :src="getProductImage(item)" mode="aspectFill" />
<!-- 购买标识 -->
<view class="winner-tag" v-if="order.is_winner && order.source_type === 2">
<view class="winner-tag" v-if="order.is_winner && (order.source_type === 2 || order.source_type === 3)">
<text class="tag-text">已开启</text>
</view>
<view class="level-tag" v-if="order.reward_level">
@ -69,6 +70,30 @@
</view>
</view>
</view>
<!-- 活动信息当没有实物商品时显示 -->
<view v-if="(!order.items || order.items.length === 0) && order.activity_name" class="item-card">
<view class="item-image-wrap">
<image class="item-image" :src="defaultImage" mode="aspectFill" />
<view class="winner-tag" v-if="order.is_winner">
<text class="tag-text">已开启</text>
</view>
</view>
<view class="item-info">
<text class="item-title">{{ order.activity_name }}</text>
<view class="item-tags">
<text class="tag">参与记录</text>
<text class="tag" v-if="order.issue_number">{{ order.issue_number }}</text>
</view>
<view class="item-meta">
<view class="price-wrap">
<text class="currency">¥</text>
<text class="price">{{ formatPrice(order.actual_amount) }}</text>
</view>
<text class="item-quantity">x1</text>
</view>
</view>
</view>
</view>
</view>
@ -339,8 +364,16 @@ function getStatusIcon(item) {
function getSourceTypeText(type) {
if (type === 1) return '商城订单'
if (type === 2) return '一番赏'
if (type === 3) return '发奖记录'
if (type === 2 || type === 3) {
// 使
if (order.value && order.value.category_name) return order.value.category_name
if (order.value && order.value.activity_name) return order.value.activity_name
const playType = order.value && order.value.play_type
if (playType === 'match') return '对对碰'
if (playType === 'ichiban') return '一番赏'
if (type === 2) return '抽奖订单'
return '发奖记录'
}
return '其他'
}

View File

@ -168,19 +168,28 @@ function formatAmount(a) {
}
function getOrderTitle(item) {
// 使 remark
if (item.remark && !item.remark.startsWith('lottery:')) {
return item.remark
// 1. 使 items
if (item.items && item.items.length > 0 && item.items[0].title) {
return item.items[0].title
}
// 使 items
if (item.items && item.items.length > 0) {
return item.items[0].title || '商品'
}
// 使
// 2. 使
if (item.activity_name) {
return item.activity_name
}
return item.title || item.subject || '订单'
// 3. remark
if (item.remark) {
// lottery:xxx, matching_game:xxx
if (!item.remark.startsWith('lottery:') &&
!item.remark.startsWith('matching_game:') &&
!item.remark.includes(':issue:')) {
return item.remark
}
}
// 4.
return item.title || item.subject || '盲盒订单'
}
function getProductImage(item) {
@ -205,14 +214,28 @@ function getProductImage(item) {
function getTypeIcon(item) {
const sourceType = item.source_type
if (sourceType === 2) return '🎰' //
if (sourceType === 2 || sourceType === 3) {
//
const playType = item.play_type
if (playType === 'match') return '🎮' //
if (playType === 'ichiban') return '🎰' //
if (sourceType === 2) return '🎲' //
}
if (sourceType === 1) return '🛒' //
return '📦'
}
function getTypeName(item) {
const sourceType = item.source_type
if (sourceType === 2) return '一番赏'
if (sourceType === 2 || sourceType === 3) {
// 使
if (item.category_name) return item.category_name
if (item.activity_name) return item.activity_name
const playType = item.play_type
if (playType === 'match') return '对对碰'
if (playType === 'ichiban') return '一番赏'
if (sourceType === 2) return '抽奖'
}
if (sourceType === 1) return '商城'
return '订单'
}
@ -254,7 +277,8 @@ function apiStatus() {
// source_type=3
function filterOrders(items) {
if (!Array.isArray(items)) return []
return items.filter(item => item.source_type !== 3)
// source_type=3 source_type=3
return items
}
async function fetchOrders(append) {

127
utils/payment.js Normal file
View File

@ -0,0 +1,127 @@
/**
* 通用支付流程工具函数
*
* 用于统一 一番赏无限赏对对碰 三种玩法的支付流程
*/
import { createWechatOrder } from '../api/appUser'
/**
* 从API响应中提取订单号
* @param {Object} res - API 响应
* @returns {string|null}
*/
export function extractOrderNo(res) {
if (!res) return null
return res.order_no || res.orderNo || res.data?.order_no || res.data?.orderNo || res.result?.order_no || res.result?.orderNo || null
}
/**
* 执行微信支付流程
*
* @param {Object} options
* @param {string} options.orderNo - 订单号必须
* @param {string} [options.openid] - 用户 openid可选默认从 storage 读取
* @returns {Promise<void>} - 支付完成成功 resolve取消或失败时 reject
*/
export async function doWechatPayment({ orderNo, openid }) {
if (!orderNo) {
throw new Error('订单号不能为空')
}
const finalOpenid = openid || uni.getStorageSync('openid')
if (!finalOpenid) {
throw new Error('缺少OpenID请重新登录')
}
// 1. 获取微信支付参数
const payRes = await createWechatOrder({ openid: finalOpenid, order_no: orderNo })
if (!payRes || !payRes.package) {
throw new Error('获取支付参数失败')
}
// 2. 调起微信支付
return new Promise((resolve, reject) => {
uni.requestPayment({
provider: 'wxpay',
timeStamp: payRes.timeStamp || payRes.timestamp,
nonceStr: payRes.nonceStr || payRes.noncestr,
package: payRes.package,
signType: payRes.signType || 'RSA',
paySign: payRes.paySign,
success: resolve,
fail: (err) => {
if (err?.errMsg && String(err.errMsg).includes('cancel')) {
const cancelErr = new Error('支付已取消')
cancelErr.cancelled = true
reject(cancelErr)
} else {
reject(err)
}
}
})
})
}
/**
* 完整支付流程创建订单 + 支付
*
* @param {Object} options
* @param {Function} options.createOrder - 创建订单的函数返回 Promise结果需包含 order_no
* @param {string} [options.openid] - 用户 openid
* @param {Function} [options.onOrderCreated] - 订单创建后的回调参数为 (orderNo, response)
* @returns {Promise<{orderNo: string, orderResponse: any}>}
*/
export async function executePaymentFlow({ createOrder, openid, onOrderCreated }) {
// 1. 创建订单
const orderResponse = await createOrder()
const orderNo = extractOrderNo(orderResponse)
if (!orderNo) {
throw new Error('未获取到订单号')
}
// 2. 回调通知(用于保存游戏数据等)
if (typeof onOrderCreated === 'function') {
await onOrderCreated(orderNo, orderResponse)
}
// 3. 执行支付
await doWechatPayment({ orderNo, openid })
return { orderNo, orderResponse }
}
/**
* 检查登录状态
* @returns {{ok: boolean, openid?: string, message?: string}}
*/
export function checkLoginStatus() {
const token = uni.getStorageSync('token')
const phoneBound = !!uni.getStorageSync('phone_bound')
const openid = uni.getStorageSync('openid')
if (!token || !phoneBound) {
return { ok: false, message: '请先登录并绑定手机号' }
}
if (!openid) {
return { ok: false, message: '缺少OpenID请重新登录' }
}
return { ok: true, openid }
}
/**
* 显示登录提示弹窗
*/
export function showLoginPrompt() {
uni.showModal({
title: '提示',
content: '请先登录并绑定手机号',
confirmText: '去登录',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/pages/login/index' })
}
}
})
}

View File

@ -33,5 +33,8 @@
* [x] 2025-12-17: 将 dev 分支代码强制推送至 main 分支 (Deployment/Sync)。
* [x] 2025-12-18: 实现订单详情 API 与取消订单 API (后端接口对接)。
* [x] 2025-12-18: 开发订单详情页 UI 及交互逻辑。
* [x] 2025-12-22: 修复订单列表不显示问题,移除 source_type=3 过滤,并支持对对碰等玩法订单的正确展示(列表与详情)。
* [x] 2025-12-22: 修复订单列表标题显示为 "matching_game:xxx" 内部标识的问题,优化无商品信息时的标题展示。
* [x] 2025-12-22: 优化订单详情页,当没有实物商品时(如参与记录)显示活动信息,避免显示空的商品清单。
* [ ] 2025-12-17: 进行中 - 优化 `pages/activity/yifanshang/index.vue` 及相关组件。
* [ ] 2025-12-17: 待开始 - 优化 `pages/login/index.vue` 视觉细节。