Merge client_id+token into credential popup, add refund button to actions

- Merge client_id and token columns into single "credential" button
- Click to view in modal with copy buttons for each
- Add refund toggle button in actions row with confirm dialog
- Remove separate refund_received column, cleaner layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-06 01:52:41 +08:00
parent d8d651240a
commit 1638d2c057
3 changed files with 119 additions and 35 deletions

View File

@@ -131,6 +131,29 @@ class MailManager {
}
});
// 凭证弹窗事件
document.getElementById('closeCredentialBtn').addEventListener('click', () => {
document.getElementById('credentialModal').classList.remove('show');
});
document.getElementById('credentialModal').addEventListener('click', (e) => {
if (e.target === e.currentTarget) e.currentTarget.classList.remove('show');
});
document.getElementById('copyCredClientIdBtn').addEventListener('click', () => {
this.copyToClipboard(document.getElementById('credClientId').textContent);
});
document.getElementById('copyCredTokenBtn').addEventListener('click', () => {
this.copyToClipboard(document.getElementById('credToken').textContent);
});
// 凭证查看按钮 — 事件委托
document.getElementById('accountTableBody').addEventListener('click', (e) => {
const btn = e.target.closest('.credential-btn');
if (!btn) return;
e.preventDefault();
e.stopPropagation();
this.showCredentialModal(btn.dataset.credEmail);
});
// 退款到账按钮 — 事件委托
document.getElementById('accountTableBody').addEventListener('click', (e) => {
const btn = e.target.closest('.refund-received-btn');
@@ -257,7 +280,7 @@ class MailManager {
async loadAccounts() {
const tbody = document.getElementById('accountTableBody');
tbody.innerHTML = `<tr><td colspan="14"><div class="table-loading"><div class="spinner-border spinner-border-sm"></div> 加载中...</div></td></tr>`;
tbody.innerHTML = `<tr><td colspan="12"><div class="table-loading"><div class="spinner-border spinner-border-sm"></div> 加载中...</div></td></tr>`;
try {
// 有筛选时加载全部数据,否则分页加载
@@ -277,11 +300,11 @@ class MailManager {
await this.loadClaudePaymentStatuses();
this.applyFilterAndRender();
} else {
tbody.innerHTML = `<tr><td colspan="14"><div class="no-data"><i class="bi bi-exclamation-circle"></i><div>${this.escapeHtml(result.message || '加载失败')}</div></div></td></tr>`;
tbody.innerHTML = `<tr><td colspan="12"><div class="no-data"><i class="bi bi-exclamation-circle"></i><div>${this.escapeHtml(result.message || '加载失败')}</div></div></td></tr>`;
}
} catch (err) {
console.error('加载账号失败:', err);
tbody.innerHTML = `<tr><td colspan="14"><div class="no-data"><i class="bi bi-wifi-off"></i><div>网络错误</div></div></td></tr>`;
tbody.innerHTML = `<tr><td colspan="12"><div class="no-data"><i class="bi bi-wifi-off"></i><div>网络错误</div></div></td></tr>`;
}
}
@@ -316,7 +339,7 @@ class MailManager {
renderAccounts() {
const tbody = document.getElementById('accountTableBody');
if (!this.accounts.length) {
tbody.innerHTML = `<tr><td colspan="14"><div class="no-data"><i class="bi bi-inbox"></i><div>暂无邮箱数据</div></div></td></tr>`;
tbody.innerHTML = `<tr><td colspan="12"><div class="no-data"><i class="bi bi-inbox"></i><div>暂无邮箱数据</div></div></td></tr>`;
return;
}
@@ -347,17 +370,8 @@ class MailManager {
${pwd ? `<button class="copy-icon-btn" data-copy-field="pwd" data-copy-key="${this.escapeHtml(email)}" title="复制"><i class="bi bi-clipboard"></i></button>` : ''}
</div>
</td>
<td class="col-hide-mobile">
<div class="value-container">
<span class="value-text value-secret" title="${this.escapeHtml(cid)}">${this.truncate(cid, 16)}</span>
${cid ? `<button class="copy-icon-btn" data-copy-field="cid" data-copy-key="${this.escapeHtml(email)}" title="复制"><i class="bi bi-clipboard"></i></button>` : ''}
</div>
</td>
<td>
<div class="value-container">
<span class="value-text value-secret" title="点击复制">${token ? this.truncate(token, 20) : '-'}</span>
${token ? `<button class="copy-icon-btn" data-copy-field="token" data-copy-key="${this.escapeHtml(email)}" title="复制"><i class="bi bi-clipboard"></i></button>` : ''}
</div>
<button class="note-cell-btn credential-btn" data-cred-email="${this.escapeHtml(email)}"><i class="bi bi-key"></i> 查看</button>
</td>
${this.renderClaudeColumns(email)}
<td>
@@ -365,6 +379,7 @@ class MailManager {
<button class="view" onclick="app.openMailbox('${this.escapeHtml(email)}', 'INBOX')">收件箱</button>
<button class="view" onclick="app.openMailbox('${this.escapeHtml(email)}', 'Junk')">垃圾箱</button>
<button class="view claude-check-btn" onclick="app.checkSingleClaudePayment('${this.escapeHtml(email)}', this)">Claude</button>
${this.renderRefundBtn(email)}
<button class="delete" onclick="app.deleteAccount('${this.escapeHtml(email)}')">删除</button>
</div>
</td>
@@ -705,6 +720,39 @@ class MailManager {
// Claude 支付检测
// ====================================================================
renderRefundBtn(email) {
const info = this.claudePaymentStatuses[email];
const isReceived = info && info.refund_received === '1';
if (isReceived) {
return `<button class="view" style="background:rgba(16,185,129,0.1);color:#059669;border-color:rgba(16,185,129,0.15);" onclick="app.confirmRefundToggle('${this.escapeHtml(email)}')">已到账</button>`;
}
return `<button class="view" onclick="app.confirmRefundToggle('${this.escapeHtml(email)}')">退款</button>`;
}
confirmRefundToggle(email) {
const info = this.claudePaymentStatuses[email];
const isReceived = info && info.refund_received === '1';
const msg = isReceived
? `确定将 ${email} 的退款状态改为【未到账】吗?`
: `确定将 ${email} 标记为【退款已到账】吗?`;
if (confirm(msg)) {
this.toggleRefundReceived(email);
}
}
showCredentialModal(email) {
const data = this._accountDataMap[email];
if (!data) return;
const cid = data.cid || '-';
const token = data.token || '-';
// 复用 paste-modal 样式
const modal = document.getElementById('credentialModal');
document.getElementById('credModalEmail').textContent = email;
document.getElementById('credClientId').textContent = cid;
document.getElementById('credToken').textContent = token;
modal.classList.add('show');
}
async toggleRefundReceived(email) {
try {
const resp = await fetch(`/api/tools/refund-received/${encodeURIComponent(email)}`, { method: 'POST' });
@@ -739,7 +787,6 @@ class MailManager {
if (!info) {
return `<td><span class="claude-badge claude-unknown">未检测</span></td>
<td><span class="claude-badge claude-unknown">未检测</span></td>
<td>-</td>
<td class="claude-time">-</td>
<td class="claude-time">-</td>
<td class="claude-time">-</td>
@@ -777,12 +824,8 @@ class MailManager {
? `<span class="claude-badge claude-suspended">${fmtTime(info.suspended_time)}</span>`
: '-';
const isRefundReceived = info.refund_received === '1';
const refundReceivedBtn = `<button class="claude-badge ${isRefundReceived ? 'claude-paid' : 'claude-unknown'} refund-received-btn" data-email="${this.escapeHtml(email)}" style="cursor:pointer;border:none;" title="点击切换">${isRefundReceived ? '已到账' : '未到账'}</button>`;
return `<td>${paidBadge}</td>
<td>${refundBadge}</td>
<td>${refundReceivedBtn}</td>
<td class="claude-time">${fmtTime(info.payment_time)}</td>
<td class="claude-time">${fmtTime(info.refund_time)}</td>
<td>${suspendedHtml}</td>