feat: 券详情页接入实时核销码(/voucher/checkCode,60秒滚动刷新)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-09-24 11:43:45 +08:00
co-authored by Claude Fable 5
parent 571afb0673
commit 5d253c60a8
2 changed files with 41 additions and 12 deletions
+4 -1
View File
@@ -26,6 +26,7 @@ const AppointUrl = 'zhy-api/cardBag/appoint'
const VoucherCreateUrl = 'open-api/hhService/voucher/create' const VoucherCreateUrl = 'open-api/hhService/voucher/create'
const VoucherPrepayUrl = 'open-api/hhService/voucher/prepay' const VoucherPrepayUrl = 'open-api/hhService/voucher/prepay'
const VoucherLatestUrl = 'open-api/hhService/voucher/latest' const VoucherLatestUrl = 'open-api/hhService/voucher/latest'
const VoucherCheckCodeUrl = 'open-api/hhService/voucher/checkCode'
// 此处第二个参数vm,就是我们在页面使用的this // 此处第二个参数vm,就是我们在页面使用的this
const install = (Vue, vm) => { const install = (Vue, vm) => {
@@ -60,6 +61,7 @@ const install = (Vue, vm) => {
const voucherCreate = (params = {}) => vm.$u.post(VoucherCreateUrl, params) const voucherCreate = (params = {}) => vm.$u.post(VoucherCreateUrl, params)
const voucherPrepay = (params = {}) => vm.$u.post(VoucherPrepayUrl, params) const voucherPrepay = (params = {}) => vm.$u.post(VoucherPrepayUrl, params)
const voucherLatest = (params = {}) => vm.$u.post(VoucherLatestUrl, params) const voucherLatest = (params = {}) => vm.$u.post(VoucherLatestUrl, params)
const voucherCheckCode = (params = {}) => vm.$u.post(VoucherCheckCodeUrl, params)
vm.$u.api = { vm.$u.api = {
checkerLogin, checkerLogin,
@@ -79,7 +81,8 @@ const install = (Vue, vm) => {
getWinnerList, getWinnerList,
voucherCreate, voucherCreate,
voucherPrepay, voucherPrepay,
voucherLatest voucherLatest,
voucherCheckCode
} }
} }
+37 -11
View File
@@ -19,11 +19,11 @@
<block v-else-if="status === 2"> <block v-else-if="status === 2">
<view class="qrbox"> <view class="qrbox">
<view class="qr-badge">有效</view> <view class="qr-badge">有效</view>
<!-- 石油返回图片URL时直接展示 --> <!-- 核销码为图片URL时直接展示 -->
<image v-if="isQrImage" class="qr-img" :src="voucher.voucherCode" mode="widthFix" /> <image v-if="isQrImage" class="qr-img" :src="qrText" mode="widthFix" />
<!-- 返回码字符串时用 qrcodejs 渲染 --> <!-- 核销码为字符串时前端渲染二维码(每60秒向服务端换取新码) -->
<view v-else id="petrol-qr" class="qr-canvas"></view> <view v-else id="petrol-qr" class="qr-canvas"></view>
<view class="qr-tip">到店出示此二维码,收银员以“中石油汽油券”通道扫码核销</view> <view class="qr-tip">核销码每 60 秒自动刷新,请到店后出示给收银员扫码</view>
</view> </view>
<view class="card"> <view class="card">
@@ -93,8 +93,10 @@ export default {
phone: '', phone: '',
voucher: {}, voucher: {},
status: null, // null=加载中;含义对齐后端 statusCode status: null, // null=加载中;含义对齐后端 statusCode
qrText: '', // xuchao: 实时核销码(60 秒滚动,来自 /voucher/checkCode)
pollTimer: null, pollTimer: null,
pollLeft: 3, pollLeft: 3,
checkTimer: null,
voucherName: VOUCHER_NAME, voucherName: VOUCHER_NAME,
voucherRule: VOUCHER_RULE, voucherRule: VOUCHER_RULE,
useRules: VOUCHER_USE_RULES, useRules: VOUCHER_USE_RULES,
@@ -107,7 +109,7 @@ export default {
return f ? (f / 100).toFixed(2) : '' return f ? (f / 100).toFixed(2) : ''
}, },
isQrImage() { isQrImage() {
return typeof this.voucher.voucherCode === 'string' && /^https?:\/\//i.test(this.voucher.voucherCode) return typeof this.qrText === 'string' && /^https?:\/\//i.test(this.qrText)
} }
}, },
onLoad(options) { onLoad(options) {
@@ -121,6 +123,7 @@ export default {
}, },
onUnload() { onUnload() {
this.stopPoll() this.stopPoll()
this.stopCheckTimer()
}, },
methods: { methods: {
refresh(first) { refresh(first) {
@@ -143,7 +146,11 @@ export default {
} else { } else {
this.stopPoll() this.stopPoll()
} }
if (this.status === 2) this.$nextTick(() => this.renderQr()) if (this.status === 2) {
this.loadCheckCode()
} else {
this.stopCheckTimer()
}
}).catch(() => { }).catch(() => {
if (first) uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' }) if (first) uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' })
}) })
@@ -159,18 +166,37 @@ export default {
stopPoll() { stopPoll() {
if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null } if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer = null }
}, },
// xuchao: 码字符串→二维码渲染;qrcode.min.js 为本地静态资源(static/js/),动态注入一次 // xuchao: 取实时核销码并渲染;CNPC 侧 60 秒滚动,前端每 55 秒预取刷新
loadCheckCode() {
const orderNo = this.voucher.orderNo || this.orderNo
if (!orderNo) return
this.$u.api.voucherCheckCode({ orderNo, phone: this.phone }).then(res => {
if (!bizOk(res)) { uni.showToast({ title: bizMsg(res) || '获取核销码失败', icon: 'none' }); return }
const d = bizData(res)
this.qrText = d.checkCode || ''
this.$nextTick(() => this.renderQr())
this.startCheckTimer()
}).catch(() => {})
},
startCheckTimer() {
this.stopCheckTimer()
this.checkTimer = setInterval(() => this.loadCheckCode(), 55000)
},
stopCheckTimer() {
if (this.checkTimer) { clearInterval(this.checkTimer); this.checkTimer = null }
},
// xuchao: 核销码字符串→二维码渲染;qrcode.min.js 为本地静态资源(static/js/),动态注入一次
renderQr() { renderQr() {
if (this.isQrImage) return if (this.isQrImage) return
if (!this.qrText) return
const draw = () => { const draw = () => {
if (typeof document === 'undefined' || typeof window.QRCode === 'undefined') return if (typeof document === 'undefined' || typeof window.QRCode === 'undefined') return
const el = document.getElementById('petrol-qr') const el = document.getElementById('petrol-qr')
if (!el || el.hasChildNodes()) { if (!el) return
if (el && this.voucher.voucherCode) { el.innerHTML = ''; } else { return } el.innerHTML = ''
}
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new window.QRCode(el, { new window.QRCode(el, {
text: this.voucher.voucherCode, text: this.qrText,
width: 200, height: 200, colorDark: '#1a1818', colorLight: '#ffffff', width: 200, height: 200, colorDark: '#1a1818', colorLight: '#ffffff',
correctLevel: window.QRCode.CorrectLevel.H correctLevel: window.QRCode.CorrectLevel.H
}) })