feat(order-ticket): 订单详情页核销码与票务卡片优化
1. 核销码弹窗 (ticket-qr-popup) - canvas 加 pointer-events:none 修复 swiper 滑动被拦截的问题 - 新增 verify_status 遮罩,已核销/已退款的票显示"已核销/已退款 此码不可用" - 遮罩加 backdrop-filter 模糊,文字放大并上移居中 2. 订单详情页票务卡片 (user-order-detail) - 注释隐藏底部"查看票码 ›"链接(功能与顶部按钮重叠) - 票卡右上角加核销/退款状态印章动画 - ticket_detail_list 传递 verify_status,API 图片路径加 resolveImageUrl 拼接 - 新增 resolveImageUrl 方法,与票夹保持一致 - 订单信息面板精简:删除重复条目,仅保留订单编号/总价/支付金额/支付方式/创建时间/支付时间/发货时间master
parent
da1e95e741
commit
efe6227f06
|
|
@ -50,6 +50,17 @@
|
|||
<view class="barcode-wrapper">
|
||||
<w-barcode :options="getBarcodeOptions(ticket)" />
|
||||
</view>
|
||||
|
||||
<!-- 已核销遮罩(覆盖整个亮白底卡:QR + 条形码) -->
|
||||
<view
|
||||
v-if="ticket.verify_status !== undefined && ticket.verify_status !== 0"
|
||||
class="verified-overlay"
|
||||
>
|
||||
<text class="verified-overlay-text">
|
||||
{{ ticket.verify_status === 1 ? '已核销' : '已退款' }}
|
||||
</text>
|
||||
<text class="verified-overlay-sub">此码不可用</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 核销码明文 -->
|
||||
|
|
@ -153,13 +164,15 @@ export default {
|
|||
},
|
||||
|
||||
// 构造 w-barcode 配置
|
||||
// 注意:w-barcode 的 SetGradient 期望 color 为数组(如 ["#000000"]),
|
||||
// 传字符串会导致逐字符拆分,产生 "unsupported color:#" / "unsupported color:0" 警告
|
||||
getBarcodeOptions: function(ticket) {
|
||||
return {
|
||||
code: ticket.short_code || '',
|
||||
width: 480,
|
||||
height: 100,
|
||||
bgColor: '#ffffff',
|
||||
color: '#000000',
|
||||
color: ['#000000'],
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
@ -287,6 +300,10 @@ export default {
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* canvas 元素穿透触摸事件,避免 swiper 滑动被拦截 */
|
||||
.qrcode-wrapper canvas {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 过期遮罩 */
|
||||
.expired-overlay {
|
||||
|
|
@ -332,6 +349,10 @@ export default {
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
/* canvas 元素穿透触摸事件,避免 swiper 滑动被拦截 */
|
||||
.barcode-wrapper canvas {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ========== 核销码明文 ========== */
|
||||
.short-code-text {
|
||||
|
|
@ -365,6 +386,41 @@ export default {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
/* ========== 已核销遮罩 ========== */
|
||||
.verified-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(17, 17, 17, 0.82);
|
||||
/* #ifdef H5 || APP-PLUS */
|
||||
backdrop-filter: blur(12rpx);
|
||||
/* #endif */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
border-radius: 32rpx;
|
||||
z-index: 30;
|
||||
transform: translateY(-30rpx);
|
||||
}
|
||||
|
||||
.verified-overlay-text {
|
||||
font-size: 48rpx;
|
||||
font-weight: 900;
|
||||
color: #e1251b;
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
.verified-overlay-sub {
|
||||
font-size: 26rpx;
|
||||
color: #aaaaaa;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
/* ========== 多票指示器 ========== */
|
||||
.indicator-dots {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -270,4 +270,49 @@
|
|||
color: #e1251b;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ========== 核销水印盖章(复用票夹样式) ========== */
|
||||
.ticket-order-panel .stamp-container {
|
||||
position: absolute;
|
||||
right: 50rpx;
|
||||
top: 40rpx;
|
||||
z-index: 20;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ticket-order-panel .verified-stamp {
|
||||
border: 4rpx solid #888888;
|
||||
color: #888888;
|
||||
font-size: 18rpx;
|
||||
font-weight: 900;
|
||||
padding: 6rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
transform: rotate(-18deg);
|
||||
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||
animation: stamp-slam 0.45s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ticket-order-panel .verified-stamp.refunded {
|
||||
border-color: #ff9900;
|
||||
color: #ff9900;
|
||||
}
|
||||
|
||||
@keyframes stamp-slam {
|
||||
0% {
|
||||
transform: scale(3) rotate(-35deg);
|
||||
opacity: 0;
|
||||
}
|
||||
70% {
|
||||
transform: scale(0.95) rotate(-15deg);
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) rotate(-18deg);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
<button v-if="detail.operate_data.is_cancel == 1" class="round bg-white cr-yellow br-yellow margin-bottom-main" type="default" size="mini" @tap="cancel_event" hover-class="none">{{$t('common.cancel')}}</button>
|
||||
<button v-if="detail.operate_data.is_pay == 1" class="round bg-white cr-green br-green margin-bottom-main" type="default" size="mini" @tap="pay_event" hover-class="none">{{$t('order.order.1i873j')}}</button>
|
||||
<button v-if="detail.operate_data.is_collect == 1 && !is_ticket_order" class="round bg-white cr-green br-green margin-bottom-main" type="default" size="mini" @tap="collect_event" hover-class="none">{{$t('orderallot-list.orderallot-list.w2w2w4')}}</button>
|
||||
<button v-if="is_ticket_order" class="round bg-red cr-white br-red margin-bottom-main" type="default" size="mini" @tap="url_event" :data-value="'/pages/plugins/vr-ticket-wallet/ticket-wallet'" hover-class="none">查看核销码</button>
|
||||
<button v-if="can_show_ticket" class="round bg-red cr-white br-red margin-bottom-main" type="default" size="mini" @tap="openQrPopup" hover-class="none">查看核销码</button>
|
||||
<button v-if="(detail.plugins_express_data || 0) == 1 && (detail.express_data || null) != null" class="round bg-white cr-main br-main margin-bottom-main" type="default" size="mini" @tap="url_event" :data-value="'/pages/plugins/express/detail/detail?oid=' + detail.id" hover-class="none">{{$t('orderallot-list.orderallot-list.w2t242')}}</button>
|
||||
<button v-if="(detail.plugins_delivery_data || 0) > 0" class="round bg-white cr-main br-main margin-bottom-main" type="default" size="mini" @tap="url_event" :data-value="'/pages/plugins/delivery/logistics/logistics?id=' + detail.plugins_delivery_data" hover-class="none">{{$t('orderallot-list.orderallot-list.w2t242')}}</button>
|
||||
<button v-if="detail.operate_data.is_comments == 1" class="round bg-white cr-green br-green margin-bottom-main" type="default" size="mini" @tap="url_event" :data-value="'/pages/user-order-comments/user-order-comments?id='+detail.id" hover-class="none">{{$t('common.comment')}}</button>
|
||||
|
|
@ -134,7 +134,7 @@
|
|||
</view>
|
||||
|
||||
<!-- 票务详情面板 - 复刻 ticket-wallet.vue 样式 -->
|
||||
<view v-if="is_ticket_order && ticket_detail_list.length > 0" class="ticket-order-panel padding-horizontal-main padding-top-main">
|
||||
<view v-if="can_show_ticket && ticket_detail_list.length > 0" class="ticket-order-panel padding-horizontal-main padding-top-main">
|
||||
<!-- 标题行 -->
|
||||
<view class="panel-item-title-row">
|
||||
<text class="panel-main-title">票务信息</text>
|
||||
|
|
@ -153,6 +153,12 @@
|
|||
|
||||
<!-- 卡券主要内容区 -->
|
||||
<view class="card-inner-box">
|
||||
<!-- 核销水印盖章(复用票夹 verified-stamp 样式) -->
|
||||
<view v-if="ticket.verify_status !== undefined && ticket.verify_status !== 0" class="stamp-container">
|
||||
<view class="verified-stamp" :class="{ 'refunded': ticket.verify_status !== 1 }">
|
||||
{{ ticket.verify_status === 1 ? '已核销 PASSED' : '已退款 REFUND' }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- 头部:场馆行 -->
|
||||
<view class="card-header-row">
|
||||
<view class="cinema-title-wrap">
|
||||
|
|
@ -194,8 +200,10 @@
|
|||
|
||||
<!-- 底部提示栏 -->
|
||||
<view class="ticket-card-footer">
|
||||
<text class="footer-tip-text">使用时请点击展示二维码/条形码</text>
|
||||
<text class="view-code-text" @tap="url_event" data-value="/pages/plugins/vr-ticket-wallet/ticket-wallet">查看票码 ›</text>
|
||||
<text class="footer-tip-text">使用时请点击顶部"查看核销码"按钮</text>
|
||||
<!-- 功能与顶部按钮重叠,暂时屏蔽以免混淆
|
||||
<text class="view-code-text" @tap="openQrPopup" :data-index="tIdx">查看票码 ›</text>
|
||||
-->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -515,6 +523,24 @@
|
|||
|
||||
<!-- 公共 -->
|
||||
<component-common ref="common"></component-common>
|
||||
|
||||
<!-- 核销码弹窗(复用 ticket-wallet 的 ticket-qr-popup 组件) -->
|
||||
<u-popup
|
||||
ref="qrPopupRef"
|
||||
propType="center"
|
||||
:propIsMaskClick="true"
|
||||
:propRound="24"
|
||||
propBackgroundColor="#111111"
|
||||
@change="onPopupChange"
|
||||
>
|
||||
<ticket-qr-popup
|
||||
:tickets="currentQrTickets"
|
||||
:current-index="currentQrIndex"
|
||||
:show-info="currentShowInfo"
|
||||
@change="onQrSlideChange"
|
||||
@refresh="onQrRefresh"
|
||||
/>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
|
|
@ -526,6 +552,8 @@
|
|||
import componentPopup from '@/components/popup/popup';
|
||||
import componentPayment from '@/components/payment/payment';
|
||||
import componentHospitalOrderDetail from '@/pages/plugins/hospital/components/order-detail/order-detail';
|
||||
import uPopup from '@/components/u-popup/u-popup.vue';
|
||||
import TicketQrPopup from '@/pages/plugins/vr-ticket-wallet/components/ticket-qr-popup/index.vue';
|
||||
var common_static_url = app.globalData.get_static_url('common');
|
||||
export default {
|
||||
data() {
|
||||
|
|
@ -567,6 +595,17 @@
|
|||
original_payment_list: [],
|
||||
// 票务相关数据
|
||||
default_poster: '/static/images/common/empty.png',
|
||||
// 票务 API 数据(合并自原第二个 data(),修复双 data() 导致小程序下 setData 失效)
|
||||
ticket_api_data: [], // 从 API 获取的票务数据缓存
|
||||
ticket_loading: false, // 票务数据加载状态
|
||||
// 核销码弹窗相关(复用 ticket-wallet 逻辑)
|
||||
qrPopupBg: '#111111',
|
||||
currentQrTickets: [],
|
||||
currentQrIndex: 0,
|
||||
currentShowInfo: {},
|
||||
qrRefreshTimer: null,
|
||||
screenBrightnessValue: 0.5,
|
||||
currentExpiresIn: 0,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -577,14 +616,8 @@
|
|||
componentPopup,
|
||||
componentPayment,
|
||||
componentHospitalOrderDetail,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// ... existing data properties
|
||||
ticket_api_data: [], // 从 API 获取的票务数据缓存
|
||||
ticket_loading: false, // 票务数据加载状态
|
||||
};
|
||||
uPopup,
|
||||
TicketQrPopup,
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
|
@ -597,6 +630,13 @@
|
|||
return item.spec_text && item.spec_text.indexOf('$vr-') !== -1;
|
||||
});
|
||||
},
|
||||
// 是否允许展示票务面板与核销码入口(仅已付款及之后状态:2=已支付/待核销 3=待收货 4=已完成)
|
||||
can_show_ticket: function() {
|
||||
if (!this.is_ticket_order) return false;
|
||||
if (!this.detail || this.detail.status == null) return false;
|
||||
var s = parseInt(this.detail.status);
|
||||
return s === 2 || s === 3 || s === 4;
|
||||
},
|
||||
// 票务详情列表(优先使用 API 数据,降级到 spec_text 解析)
|
||||
ticket_detail_list: function() {
|
||||
// 优先使用 API 返回的数据
|
||||
|
|
@ -605,13 +645,14 @@
|
|||
return {
|
||||
id: ticket.id,
|
||||
title: ticket.goods_title || '',
|
||||
images: ticket.goods_image || '',
|
||||
images: this.resolveImageUrl(ticket.goods_image) || this.default_poster,
|
||||
goods_id: ticket.goods_id,
|
||||
zone: this.extractSeatField(ticket.seat_info, 3) || '',
|
||||
session_time: ticket.session_time || this.extractSeatField(ticket.seat_info, 0) || '',
|
||||
venue_name: ticket.venue_name || this.extractSeatField(ticket.seat_info, 1) || '',
|
||||
room_name: this.extractSeatField(ticket.seat_info, 2) || '',
|
||||
seat_number: ticket.seat_number || this.extractSeatField(ticket.seat_info, 4) || '',
|
||||
verify_status: ticket.verify_status !== undefined ? ticket.verify_status : 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
@ -631,6 +672,7 @@
|
|||
venue_name: spec.venue_name || '',
|
||||
room_name: spec.room_name || '',
|
||||
seat_number: spec.seat_number || '',
|
||||
verify_status: 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -652,26 +694,32 @@
|
|||
|
||||
// 数据加载
|
||||
this.init();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 调用公共事件方法
|
||||
app.globalData.page_event_onshow_handle();
|
||||
|
||||
// 公共onshow事件
|
||||
if ((this.$refs.common || null) != null) {
|
||||
this.$refs.common.on_show();
|
||||
}
|
||||
|
||||
// 分享菜单处理
|
||||
app.globalData.page_share_handle();
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 调用公共事件方法
|
||||
app.globalData.page_event_onshow_handle();
|
||||
|
||||
// 公共onshow事件
|
||||
if ((this.$refs.common || null) != null) {
|
||||
this.$refs.common.on_show();
|
||||
}
|
||||
|
||||
// 分享菜单处理
|
||||
app.globalData.page_share_handle();
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
// 页面卸载时清理弹窗定时器与屏幕亮度
|
||||
onUnload() {
|
||||
this.clearQrRefreshTimer();
|
||||
this.restoreScreenBrightness();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 初始化配置
|
||||
init_config(status) {
|
||||
|
|
@ -683,51 +731,44 @@
|
|||
app.globalData.is_config(this, 'init_config');
|
||||
}
|
||||
},
|
||||
|
||||
// 初始化
|
||||
init() {
|
||||
this.setData({
|
||||
data_list_loding_status: 1,
|
||||
});
|
||||
uni.request({
|
||||
url: app.globalData.get_request_url('detail', 'order'),
|
||||
method: 'POST',
|
||||
data: this.params,
|
||||
dataType: 'json',
|
||||
success: (res) => {
|
||||
uni.stopPullDownRefresh();
|
||||
if (res.data.code == 0) {
|
||||
|
||||
// 初始化
|
||||
init() {
|
||||
this.setData({
|
||||
data_list_loding_status: 1,
|
||||
});
|
||||
uni.request({
|
||||
url: app.globalData.get_request_url('detail', 'order'),
|
||||
method: 'POST',
|
||||
data: this.params,
|
||||
dataType: 'json',
|
||||
success: (res) => {
|
||||
uni.stopPullDownRefresh();
|
||||
if (res.data.code == 0) {
|
||||
var data = res.data.data;
|
||||
var detail = data.data || null;
|
||||
this.setData({
|
||||
detail: data.data,
|
||||
var detail = data.data || null;
|
||||
this.setData({
|
||||
detail: data.data,
|
||||
detail_list: [
|
||||
{ name: this.$t('user-order-detail.user-order-detail.n18sd2'), value: data.data.order_no || '', is_copy: 1 },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.346376'), value: data.data.warehouse_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.io6p5k'), value: data.data.order_model_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.yxwu8n'), value: data.data.status_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.23qj7m'), value: data.data.pay_status_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.vg4jb1'), value: data.data.price || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.2y7l13'), value: data.data.total_price || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.n18sd2'), value: data.data.order_no || '', is_copy: 1 },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.346376'), value: data.data.warehouse_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.io6p5k'), value: data.data.order_model_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.yxwu8n'), value: data.data.status_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.23qj7m'), value: data.data.pay_status_name || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.vg4jb1'), value: data.data.price || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.346376'), value: data.data.warehouse_name || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.io6p5k'), value: data.data.order_model_name || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.yxwu8n'), value: data.data.status_name || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.23qj7m'), value: data.data.pay_status_name || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.vg4jb1'), value: data.data.price || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.2y7l13'), value: data.data.total_price || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.6ub2j0'), value: data.data.preferential_price || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.8b18q8'), value: data.data.increase_price || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.6ub2j0'), value: data.data.preferential_price || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.8b18q8'), value: data.data.increase_price || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.516tlr'), value: data.data.pay_price || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.0e1sfs'), value: (data.data.payment_name || '') + ((data.data.is_under_line_text || null) == null || (data.data.payment_name || null) == null ? '' : '(' + data.data.is_under_line_text + ')') },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.2b5fc8'), value: data.data.user_note || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.2b5fc8'), value: data.data.user_note || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.h2c78h'), value: data.data.add_time || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.9vivhl'), value: data.data.confirm_time || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.9vivhl'), value: data.data.confirm_time || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.wn83rn'), value: data.data.pay_time || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.1674dc'), value: data.data.delivery_time || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.0i938r'), value: data.data.collect_time || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.1jpv4n'), value: data.data.cancel_time || '' },
|
||||
{ name: this.$t('user-order-detail.user-order-detail.8o2of3'), value: data.data.close_time || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.0i938r'), value: data.data.collect_time || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.1jpv4n'), value: data.data.cancel_time || '' },
|
||||
// { name: this.$t('user-order-detail.user-order-detail.8o2of3'), value: data.data.close_time || '' },
|
||||
],
|
||||
extension_data: data.data.extension_data || [],
|
||||
site_fictitious: data.site_fictitious || null,
|
||||
|
|
@ -1127,11 +1168,20 @@
|
|||
return parts[index] || '';
|
||||
},
|
||||
|
||||
// 将相对路径拼接为完整静态资源 URL(兼容非 H5 环境)
|
||||
resolveImageUrl(path) {
|
||||
if (!path) return '';
|
||||
if (path.indexOf('http') === 0) return path;
|
||||
var base = (app.globalData.data.static_url || '').replace(/\/+$/, '');
|
||||
return base + '/' + path.replace(/^\/+/, '');
|
||||
},
|
||||
|
||||
// 获取订单票务数据(使用后端 order_id 参数优化)
|
||||
fetch_ticket_data: function() {
|
||||
var self = this;
|
||||
if (!self.detail || !self.detail.id) return;
|
||||
if (!self.is_ticket_order) return;
|
||||
if (!self.can_show_ticket) return;
|
||||
|
||||
self.setData({
|
||||
ticket_loading: true
|
||||
|
|
@ -1186,7 +1236,208 @@
|
|||
else if (key === '$vr-演播室') result.room_name = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 核销码弹窗相关方法(复用 ticket-wallet 逻辑) ==========
|
||||
|
||||
// 打开 QR 弹窗(点击"查看核销码"按钮或"查看票码 ›"链接)
|
||||
openQrPopup(e) {
|
||||
var self = this;
|
||||
// 状态守卫:仅已付款及之后允许
|
||||
if (!this.can_show_ticket) {
|
||||
app.globalData.showToast('当前订单状态不可查看核销码');
|
||||
return;
|
||||
}
|
||||
// 数据守卫
|
||||
if (!this.ticket_detail_list || this.ticket_detail_list.length === 0) {
|
||||
app.globalData.showToast('暂无票据信息');
|
||||
return;
|
||||
}
|
||||
|
||||
// 起始票索引(来自"查看票码 ›"的 data-index,缺省 0)
|
||||
var tIdx = 0;
|
||||
if (e && e.currentTarget && e.currentTarget.dataset && e.currentTarget.dataset.index != null) {
|
||||
tIdx = parseInt(e.currentTarget.dataset.index) || 0;
|
||||
}
|
||||
var startTicket = this.ticket_detail_list[tIdx] || this.ticket_detail_list[0];
|
||||
if (!startTicket || !startTicket.id) {
|
||||
app.globalData.showToast('票据信息缺失');
|
||||
return;
|
||||
}
|
||||
|
||||
// 按演出 + 场次 + 场馆分组(与 ticket-wallet groupByShow 对齐)
|
||||
var groupKey = (startTicket.goods_id || '') + '|' + (startTicket.session_time || '') + '|' + (startTicket.venue_name || '');
|
||||
var groupTickets = this.ticket_detail_list.filter(function(t) {
|
||||
return (t.goods_id || '') + '|' + (t.session_time || '') + '|' + (t.venue_name || '') === groupKey;
|
||||
});
|
||||
var ticketIds = groupTickets.map(function(t) { return t.id; });
|
||||
|
||||
uni.showLoading({ title: '加载中...' });
|
||||
|
||||
// 并行请求每张票的 detail(含 qr_payload + short_code + qr_expires_in)
|
||||
var promises = ticketIds.map(function(id) {
|
||||
return self.fetchTicketDetail(id);
|
||||
});
|
||||
|
||||
Promise.all(promises).then(function(results) {
|
||||
uni.hideLoading();
|
||||
var validTickets = results.filter(function(r) { return r != null; });
|
||||
if (validTickets.length === 0) {
|
||||
app.globalData.showToast('加载票据失败,请重试');
|
||||
return;
|
||||
}
|
||||
self.currentQrTickets = validTickets;
|
||||
self.currentQrIndex = 0;
|
||||
self.currentShowInfo = {
|
||||
title: startTicket.title,
|
||||
session_time: startTicket.session_time,
|
||||
venue_name: startTicket.venue_name,
|
||||
};
|
||||
self.qrPopupBg = '#111111';
|
||||
self.boostScreenBrightness();
|
||||
self.startQrCountdown();
|
||||
|
||||
self.$nextTick(function() {
|
||||
if (self.$refs.qrPopupRef) {
|
||||
self.$refs.qrPopupRef.open();
|
||||
}
|
||||
});
|
||||
}).catch(function() {
|
||||
uni.hideLoading();
|
||||
app.globalData.showToast('加载票据失败,请重试');
|
||||
});
|
||||
},
|
||||
|
||||
// 获取单张票详情(含 qr_payload)
|
||||
fetchTicketDetail(ticketId) {
|
||||
return new Promise(function(resolve) {
|
||||
uni.request({
|
||||
url: app.globalData.get_request_url('detail', 'ticket', 'vr_ticket', 'id=' + ticketId),
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if (res.data.code == 0 && res.data.data && res.data.data.ticket) {
|
||||
resolve(res.data.data.ticket);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
},
|
||||
fail: function() {
|
||||
resolve(null);
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Swiper 滑动切换
|
||||
onQrSlideChange(index) {
|
||||
this.currentQrIndex = index;
|
||||
this.startQrCountdown();
|
||||
},
|
||||
|
||||
// QR 弹窗关闭回调
|
||||
onPopupChange(e) {
|
||||
if (e && e.show === false) {
|
||||
this.clearQrRefreshTimer();
|
||||
this.restoreScreenBrightness();
|
||||
}
|
||||
},
|
||||
|
||||
// 启动内部倒计时(无 UI)
|
||||
startQrCountdown() {
|
||||
var self = this;
|
||||
this.clearQrRefreshTimer();
|
||||
|
||||
var currentTicket = this.currentQrTickets[this.currentQrIndex];
|
||||
if (!currentTicket) return;
|
||||
|
||||
this.currentExpiresIn = currentTicket.qr_expires_in || 0;
|
||||
|
||||
this.qrRefreshTimer = setInterval(function() {
|
||||
self.currentExpiresIn--;
|
||||
// 剩余 ≤ 30 秒时静默刷新
|
||||
if (self.currentExpiresIn <= 30 && self.currentExpiresIn > 0) {
|
||||
self.silentRefreshQr(self.currentQrIndex);
|
||||
}
|
||||
if (self.currentExpiresIn <= 0) {
|
||||
self.clearQrRefreshTimer();
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
// 静默刷新某张票的 QR
|
||||
silentRefreshQr(ticketIndex) {
|
||||
var self = this;
|
||||
var ticket = this.currentQrTickets[ticketIndex];
|
||||
if (!ticket) return;
|
||||
|
||||
uni.request({
|
||||
url: app.globalData.get_request_url('refreshQr', 'ticket', 'vr_ticket', 'id=' + ticket.id),
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
if (res.data.code == 0 && res.data.data && res.data.data.ticket) {
|
||||
self.$set(self.currentQrTickets, ticketIndex, res.data.data.ticket);
|
||||
self.currentExpiresIn = res.data.data.ticket.qr_expires_in || 0;
|
||||
}
|
||||
},
|
||||
fail: function() {
|
||||
// 静默失败,不提示
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 用户点击过期遮罩触发刷新
|
||||
onQrRefresh() {
|
||||
var self = this;
|
||||
var ticketIndex = this.currentQrIndex;
|
||||
var ticket = this.currentQrTickets[ticketIndex];
|
||||
if (!ticket) return;
|
||||
|
||||
uni.showLoading({ title: '刷新中...' });
|
||||
uni.request({
|
||||
url: app.globalData.get_request_url('refreshQr', 'ticket', 'vr_ticket', 'id=' + ticket.id),
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
uni.hideLoading();
|
||||
if (res.data.code == 0 && res.data.data && res.data.data.ticket) {
|
||||
self.$set(self.currentQrTickets, ticketIndex, res.data.data.ticket);
|
||||
self.currentExpiresIn = res.data.data.ticket.qr_expires_in || 0;
|
||||
} else {
|
||||
app.globalData.showToast(res.data.msg || '刷新失败');
|
||||
}
|
||||
},
|
||||
fail: function() {
|
||||
uni.hideLoading();
|
||||
app.globalData.showToast('网络错误,刷新失败');
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 清理倒计时定时器
|
||||
clearQrRefreshTimer() {
|
||||
if (this.qrRefreshTimer) {
|
||||
clearInterval(this.qrRefreshTimer);
|
||||
this.qrRefreshTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
// 提亮屏幕(检票场景)
|
||||
boostScreenBrightness() {
|
||||
// #ifndef H5
|
||||
uni.setScreenBrightness({ value: 1.0 });
|
||||
// #endif
|
||||
},
|
||||
|
||||
// 恢复屏幕原始亮度
|
||||
restoreScreenBrightness() {
|
||||
// #ifndef H5
|
||||
if (this.screenBrightnessValue > 0) {
|
||||
uni.setScreenBrightness({ value: this.screenBrightnessValue });
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue