Files

119 lines
5.2 KiB
JavaScript
Raw Permalink Normal View History

2026-06-21 10:00:13 +08:00
(() => {
const loading = document.getElementById('loading');
const error = document.getElementById('error');
const errorMsg = document.getElementById('errorMsg');
const content = document.getElementById('content');
const copyCardIdBtn = document.getElementById('copyCardIdBtn');
const formatTime = (value) => {
if (value == null || value === '') return '---';
const ts = Number(value);
const date = Number.isFinite(ts) ? new Date(ts) : new Date(value);
return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString('zh-CN', { hour12: false });
};
const escapeHtml = (value) => String(value == null ? '' : value)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
const applyStatus = (el, text, cls) => {
if (!el) return;
el.textContent = text;
el.className = cls;
};
const setText = (id, value) => {
const el = document.getElementById(id);
if (el) el.textContent = value ?? '';
};
const normalizeStatus = (status) => {
const s = String(status || '').toLowerCase();
if (s === 'active') return { text: '正常', cls: 'jr-status-pill jr-status-valid' };
if (s === 'pending_pickup') return { text: '待领卡', cls: 'jr-status-pill jr-status-used' };
if (s === 'disabled' || s === 'lost' || s === 'refunded') return { text: '不可用', cls: 'jr-status-pill jr-status-expired' };
return { text: status || '未知', cls: 'jr-status-pill jr-status-expired' };
};
const normalizeMode = (card) => String(card?.status || '').trim().toLowerCase() === 'pending_pickup' ? 'voucher' : 'card';
const getLookupKey = (card, fallbackCardId) => {
const voucher = String(card?.voucher_code || card?.code || card?.order_code || '').trim();
const rawCardId = String(card?.card_id || fallbackCardId || '').trim();
return normalizeMode(card) === 'voucher' ? (voucher || rawCardId) : (rawCardId || voucher);
};
const getUsageHint = (card) => {
const status = String(card?.status || '').trim().toLowerCase();
if (status === 'pending_pickup') {
return '当前仍为待领卡状态,请持凭证码前往站内售票机完成领卡。';
}
return '卡片已启用,可在检票机直接刷卡进出站。';
};
const pathParts = location.pathname.split('/').filter(Boolean);
const cardId = decodeURIComponent(pathParts[pathParts.length - 1] || '');
if (!cardId) {
loading.style.display = 'none';
error.style.display = 'block';
errorMsg.textContent = '无效的卡号';
return;
}
fetch(`/api/public/ic-cards/query?q=${encodeURIComponent(cardId)}`)
.then((res) => res.json())
.then((res) => {
if (!res.ok || !res.card) {
throw new Error(res.error || '未找到 IC 卡信息');
}
const card = res.card;
const voucher = card.voucher_code || card.code || card.order_code || '---';
const status = normalizeStatus(card.status_label || card.status);
const shownCardId = card.display_card_id || card.card_id || cardId;
const lookupKey = getLookupKey(card, cardId);
const copyMode = normalizeMode(card);
loading.style.display = 'none';
error.style.display = 'none';
content.style.display = 'block';
setText('cardIdTop', shownCardId);
setText('holderName', card.holder_name || '未登记');
setText('cardBalance', card.balance ?? 0);
setText('cardVoucher', voucher);
setText('cardCreatedTs', formatTime(card.created_ts));
setText('cardUsageHint', getUsageHint(card));
applyStatus(document.getElementById('cardStatusTop'), status.text, status.cls);
applyStatus(document.getElementById('cardStatusTag'), status.text, status.cls);
const searchLink = document.getElementById('searchLink');
if (searchLink) {
const href = location.hostname.includes('fse-media.group')
? `https://ticket.fse-media.group/ic-card/search?q=${encodeURIComponent(lookupKey)}`
: `/ic-card-search.html?q=${encodeURIComponent(lookupKey)}`;
searchLink.href = href;
}
if (copyCardIdBtn) {
copyCardIdBtn.innerHTML = copyMode === 'voucher'
? '<i class="fas fa-copy"></i> 复制凭证码'
: '<i class="fas fa-copy"></i> 复制卡号';
copyCardIdBtn.onclick = () => {
const copyValue = copyMode === 'voucher' ? voucher : (card.card_id || cardId);
navigator.clipboard.writeText(copyValue).then(() => {
alert(copyMode === 'voucher' ? '已复制凭证码' : '已复制卡号');
});
};
}
})
.catch((err) => {
loading.style.display = 'none';
error.style.display = 'block';
errorMsg.textContent = err.message || String(err);
});
})();