fix(Task1): seat展示截断 + 短码改名核销码 + barcode渲染函数化

- WalletService: 移除 getUserTickets() 中重复的 seat_number 字段
- ticket_card.html (PHP模板):
  - renderTickets(): seat_info → seat_number
  - 短码标签: 短码 → 核销码
  - showTicketBasic/Detail: seat_info → seat_number, 标签改名, canvas条码
  - 新增 renderBarcode() 独立函数
  - 清理 showTicketDetail/loadQrPayload/refreshQr 中的 inline try-catch JsBarcode
- ticket_card.js (静态JS): 同上修复, 同步跟上 PHP 模板的改动
  - renderBarcode() 在 refreshQr 中调用
feat/phase-b-verification
Council 2026-04-25 00:12:28 +08:00
parent 29f4c61110
commit 4c04b094e2
3 changed files with 63 additions and 16 deletions

View File

@ -95,8 +95,7 @@ class WalletService extends BaseService
'phone' => self::maskPhone($ticket['phone'] ?? ''), 'phone' => self::maskPhone($ticket['phone'] ?? ''),
'verify_status' => $ticket['verify_status'], 'verify_status' => $ticket['verify_status'],
'issued_at' => $ticket['issued_at'], 'issued_at' => $ticket['issued_at'],
'short_code' => $shortCode, 'short_code' => $shortCode,
'seat_number' => self::parseSeatNumber($ticket['seat_info'] ?? ''),
]; ];
} }

View File

@ -244,7 +244,7 @@
</div> </div>
</div> </div>
<div class="vr-ticket-footer"> <div class="vr-ticket-footer">
<div class="vr-ticket-short-code">码: {{short_code}}</div> <div class="vr-ticket-short-code">核销码: {{short_code}}</div>
<a href="javascript:;" class="vr-ticket-view-btn" onclick="VrTicketWallet.viewTicket({{id}})">查看票码 →</a> <a href="javascript:;" class="vr-ticket-view-btn" onclick="VrTicketWallet.viewTicket({{id}})">查看票码 →</a>
</div> </div>
</div> </div>
@ -366,11 +366,11 @@ var VrTicketWallet = (function() {
'<div class="vr-ticket-info">' + '<div class="vr-ticket-info">' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📅</span><span>' + escapeHtml(ticket.session_time) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📅</span><span>' + escapeHtml(ticket.session_time) + '</span></div>' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📍</span><span>' + escapeHtml(ticket.venue_name) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📍</span><span>' + escapeHtml(ticket.venue_name) + '</span></div>' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">💺</span><span>' + escapeHtml(ticket.seat_info) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">💺</span><span>' + escapeHtml(ticket.seat_number) + '</span></div>' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">👤</span><span>' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">👤</span><span>' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</span></div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-footer">' + '<div class="vr-ticket-footer">' +
'<div class="vr-ticket-short-code">码: ' + escapeHtml(ticket.short_code) + '</div>' + '<div class="vr-ticket-short-code">核销码: ' + escapeHtml(ticket.short_code) + '</div>' +
'<a href="javascript:;" class="vr-ticket-view-btn" onclick="VrTicketWallet.viewTicket(' + ticket.id + ')">查看票码 →</a>' + '<a href="javascript:;" class="vr-ticket-view-btn" onclick="VrTicketWallet.viewTicket(' + ticket.id + ')">查看票码 →</a>' +
'</div>' + '</div>' +
'</div>'; '</div>';
@ -434,7 +434,7 @@ var VrTicketWallet = (function() {
'<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' + '<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-short-code-display">' + '<div class="vr-ticket-short-code-display">' +
'<div class="vr-ticket-short-code-label">短码(人工核销)</div>' + '<div class="vr-ticket-short-code-label">核销码</div>' +
'<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' + '<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' +
'<canvas id="vrBarcodeCanvas" style="margin-top:8px;max-width:100%;display:block;"></canvas>' + '<canvas id="vrBarcodeCanvas" style="margin-top:8px;max-width:100%;display:block;"></canvas>' +
'</div>' + '</div>' +
@ -459,6 +459,7 @@ var VrTicketWallet = (function() {
'<div class="vr-ticket-detail-value">' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</div>' + '<div class="vr-ticket-detail-value">' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</div>' +
'</div>' + '</div>' +
'<button class="vr-ticket-refresh-btn" id="vrRefreshBtn" onclick="VrTicketWallet.refreshQr(' + ticket.id + ')">刷新二维码</button>'; '<button class="vr-ticket-refresh-btn" id="vrRefreshBtn" onclick="VrTicketWallet.refreshQr(' + ticket.id + ')">刷新二维码</button>';
renderBarcode(ticket.short_code);
} }
/** /**
@ -484,7 +485,7 @@ var VrTicketWallet = (function() {
'<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' + '<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-short-code-display">' + '<div class="vr-ticket-short-code-display">' +
'<div class="vr-ticket-short-code-label">短码(人工核销)</div>' + '<div class="vr-ticket-short-code-label">核销码</div>' +
'<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' + '<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' +
'<canvas id="vrBarcodeCanvas" style="margin-top:8px;max-width:100%;display:block;"></canvas>' + '<canvas id="vrBarcodeCanvas" style="margin-top:8px;max-width:100%;display:block;"></canvas>' +
'</div>' + '</div>' +
@ -510,7 +511,7 @@ var VrTicketWallet = (function() {
// 渲染 QR 码 // 渲染 QR 码
if (ticket.qr_payload) { if (ticket.qr_payload) {
renderQrCode(ticket.qr_payload, ticket.qr_expires_in); renderQrCode(ticket.qr_payload, ticket.qr_expires_in);
try { var bc = document.getElementById('vrBarcodeCanvas'); if (bc && ticket.short_code) { JsBarcode(bc, ticket.short_code, {format:'CODE128', width:2, height:60, displayValue:true, fontSize:14, margin:5}); } } catch(e){} renderBarcode(ticket.short_code);
} else { } else {
document.getElementById('vrQrcodeBox').innerHTML = '<div style="color:#999;">QR加载中...</div>'; document.getElementById('vrQrcodeBox').innerHTML = '<div style="color:#999;">QR加载中...</div>';
} }
@ -558,7 +559,7 @@ var VrTicketWallet = (function() {
})); }));
renderQrCode(ticket.qr_payload, expiresIn); renderQrCode(ticket.qr_payload, expiresIn);
try { var bc = document.getElementById('vrBarcodeCanvas'); if (bc && ticket.short_code) { JsBarcode(bc, ticket.short_code, {format:'CODE128', width:2, height:60, displayValue:true, fontSize:14, margin:5}); } } catch(e){} // barcode 由 showTicketBasic/showTicketDetail 单独调用
} }
}, },
error: function() { error: function() {
@ -597,6 +598,27 @@ var VrTicketWallet = (function() {
} }
} }
/**
* 渲染条形码CODE128
* @param {string} shortCode - 核销码
*/
function renderBarcode(shortCode) {
try {
var canvas = document.getElementById('vrBarcodeCanvas');
if (!canvas || !shortCode) return;
JsBarcode(canvas, shortCode, {
format: 'CODE128',
width: 2,
height: 60,
displayValue: true,
fontSize: 14,
margin: 5
});
} catch (e) {
console.warn('Barcode render failed:', e);
}
}
/** /**
* 刷新 QR * 刷新 QR
*/ */
@ -626,7 +648,7 @@ var VrTicketWallet = (function() {
// 重新渲染 QR // 重新渲染 QR
renderQrCode(ticket.qr_payload, ticket.qr_expires_in); renderQrCode(ticket.qr_payload, ticket.qr_expires_in);
try { var bc = document.getElementById('vrBarcodeCanvas'); if (bc && ticket.short_code) { JsBarcode(bc, ticket.short_code, {format:'CODE128', width:2, height:60, displayValue:true, fontSize:14, margin:5}); } } catch(e){} renderBarcode(ticket.short_code);
if (btn) { if (btn) {
btn.textContent = '已刷新'; btn.textContent = '已刷新';

View File

@ -98,11 +98,11 @@ var VrTicketWallet = (function() {
'<div class="vr-ticket-info">' + '<div class="vr-ticket-info">' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📅</span><span>' + escapeHtml(ticket.session_time) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📅</span><span>' + escapeHtml(ticket.session_time) + '</span></div>' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📍</span><span>' + escapeHtml(ticket.venue_name) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">📍</span><span>' + escapeHtml(ticket.venue_name) + '</span></div>' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">💺</span><span>' + escapeHtml(ticket.seat_info) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">💺</span><span>' + escapeHtml(ticket.seat_number) + '</span></div>' +
'<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">👤</span><span>' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</span></div>' + '<div class="vr-ticket-info-row"><span class="vr-ticket-info-icon">👤</span><span>' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</span></div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-footer">' + '<div class="vr-ticket-footer">' +
'<div class="vr-ticket-short-code">码: ' + escapeHtml(ticket.short_code) + '</div>' + '<div class="vr-ticket-short-code">核销码: ' + escapeHtml(ticket.short_code) + '</div>' +
'<a href="javascript:;" class="vr-ticket-view-btn" onclick="VrTicketWallet.viewTicket(' + ticket.id + ')">查看票码 →</a>' + '<a href="javascript:;" class="vr-ticket-view-btn" onclick="VrTicketWallet.viewTicket(' + ticket.id + ')">查看票码 →</a>' +
'</div>' + '</div>' +
'</div>'; '</div>';
@ -170,8 +170,9 @@ var VrTicketWallet = (function() {
'<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' + '<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-short-code-display">' + '<div class="vr-ticket-short-code-display">' +
'<div class="vr-ticket-short-code-label">短码(人工核销)</div>' + '<div class="vr-ticket-short-code-label">核销码</div>' +
'<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' + '<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' +
'<canvas id="vrBarcodeCanvas" style="margin-top:8px;max-width:100%;display:block;"></canvas>' +
'</div>' + '</div>' +
'<div class="vr-ticket-detail-row">' + '<div class="vr-ticket-detail-row">' +
'<div class="vr-ticket-detail-label">状态</div>' + '<div class="vr-ticket-detail-label">状态</div>' +
@ -187,13 +188,14 @@ var VrTicketWallet = (function() {
'</div>' + '</div>' +
'<div class="vr-ticket-detail-row">' + '<div class="vr-ticket-detail-row">' +
'<div class="vr-ticket-detail-label">座位</div>' + '<div class="vr-ticket-detail-label">座位</div>' +
'<div class="vr-ticket-detail-value">' + escapeHtml(ticket.seat_info) + '</div>' + '<div class="vr-ticket-detail-value">' + escapeHtml(ticket.seat_number) + '</div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-detail-row">' + '<div class="vr-ticket-detail-row">' +
'<div class="vr-ticket-detail-label">观演人</div>' + '<div class="vr-ticket-detail-label">观演人</div>' +
'<div class="vr-ticket-detail-value">' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</div>' + '<div class="vr-ticket-detail-value">' + escapeHtml(ticket.real_name) + ' ' + escapeHtml(ticket.phone) + '</div>' +
'</div>' + '</div>' +
(ticket.verify_status === 0 ? '<button class="vr-ticket-refresh-btn" id="vrRefreshBtn" onclick="VrTicketWallet.refreshQr(' + ticket.id + ')">刷新二维码</button>' : ''); (ticket.verify_status === 0 ? '<button class="vr-ticket-refresh-btn" id="vrRefreshBtn" onclick="VrTicketWallet.refreshQr(' + ticket.id + ')">刷新二维码</button>' : '');
renderBarcode(ticket.short_code);
} }
/** /**
@ -219,8 +221,9 @@ var VrTicketWallet = (function() {
'<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' + '<div class="vr-ticket-qr-expire" id="vrQrExpire"></div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-short-code-display">' + '<div class="vr-ticket-short-code-display">' +
'<div class="vr-ticket-short-code-label">短码(人工核销)</div>' + '<div class="vr-ticket-short-code-label">核销码</div>' +
'<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' + '<div class="vr-ticket-short-code-value">' + escapeHtml(ticket.short_code) + '</div>' +
'<canvas id="vrBarcodeCanvas" style="margin-top:8px;max-width:100%;display:block;"></canvas>' +
'</div>' + '</div>' +
verifiedBadge + verifiedBadge +
'<div class="vr-ticket-detail-row">' + '<div class="vr-ticket-detail-row">' +
@ -233,7 +236,7 @@ var VrTicketWallet = (function() {
'</div>' + '</div>' +
'<div class="vr-ticket-detail-row">' + '<div class="vr-ticket-detail-row">' +
'<div class="vr-ticket-detail-label">座位</div>' + '<div class="vr-ticket-detail-label">座位</div>' +
'<div class="vr-ticket-detail-value">' + escapeHtml(ticket.seat_info) + '</div>' + '<div class="vr-ticket-detail-value">' + escapeHtml(ticket.seat_number) + '</div>' +
'</div>' + '</div>' +
'<div class="vr-ticket-detail-row">' + '<div class="vr-ticket-detail-row">' +
'<div class="vr-ticket-detail-label">观演人</div>' + '<div class="vr-ticket-detail-label">观演人</div>' +
@ -243,6 +246,7 @@ var VrTicketWallet = (function() {
if (ticket.qr_payload) { if (ticket.qr_payload) {
renderQrCode(ticket.qr_payload, ticket.qr_expires_in); renderQrCode(ticket.qr_payload, ticket.qr_expires_in);
renderBarcode(ticket.short_code);
} else { } else {
document.getElementById('vrQrcodeBox').innerHTML = '<div style="color:#999;">QR加载中...</div>'; document.getElementById('vrQrcodeBox').innerHTML = '<div style="color:#999;">QR加载中...</div>';
} }
@ -323,6 +327,27 @@ var VrTicketWallet = (function() {
} }
} }
/**
* 渲染条形码CODE128
* @param {string} shortCode - 核销码
*/
function renderBarcode(shortCode) {
try {
var canvas = document.getElementById('vrBarcodeCanvas');
if (!canvas || !shortCode) return;
JsBarcode(canvas, shortCode, {
format: 'CODE128',
width: 2,
height: 60,
displayValue: true,
fontSize: 14,
margin: 5
});
} catch (e) {
console.warn('Barcode render failed:', e);
}
}
/** /**
* 刷新 QR * 刷新 QR
*/ */
@ -350,6 +375,7 @@ var VrTicketWallet = (function() {
})); }));
renderQrCode(ticket.qr_payload, ticket.qr_expires_in); renderQrCode(ticket.qr_payload, ticket.qr_expires_in);
renderBarcode(ticket.short_code);
if (btn) { if (btn) {
btn.textContent = '已刷新'; btn.textContent = '已刷新';