fix(orders): 修复订单列表显示问题并优化详情页展示
修复订单列表不显示 source_type=3 订单的问题,支持对对碰等玩法订单 优化订单标题显示逻辑,移除内部标识并添加保底显示 优化订单详情页,当没有实物商品时显示活动信息 重构订单类型判断逻辑,支持更多玩法类型
This commit is contained in:
parent
2d218018e8
commit
be57eda392
@ -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')) {
|
||||
|
||||
@ -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 '其他'
|
||||
}
|
||||
|
||||
|
||||
@ -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
127
utils/payment.js
Normal 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' })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
3
说明文档.md
3
说明文档.md
@ -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` 视觉细节。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user