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:
@@ -92,11 +92,9 @@
|
||||
<th class="col-num">#</th>
|
||||
<th>邮箱</th>
|
||||
<th>密码</th>
|
||||
<th class="col-hide-mobile">客户ID</th>
|
||||
<th>令牌</th>
|
||||
<th>凭证</th>
|
||||
<th>支付状态</th>
|
||||
<th>退款状态</th>
|
||||
<th>退款到账</th>
|
||||
<th>支付时间</th>
|
||||
<th>退款时间</th>
|
||||
<th>封号时间</th>
|
||||
@@ -107,7 +105,7 @@
|
||||
</thead>
|
||||
<tbody id="accountTableBody">
|
||||
<tr>
|
||||
<td colspan="14">
|
||||
<td colspan="12">
|
||||
<div class="no-data">
|
||||
<i class="bi bi-inbox"></i>
|
||||
<div>暂无邮箱数据,点击"粘贴导入"添加</div>
|
||||
@@ -367,6 +365,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 凭证查看弹窗 -->
|
||||
<div class="paste-modal" id="credentialModal">
|
||||
<div class="paste-modal-content" style="max-width:500px;">
|
||||
<div class="paste-modal-header">
|
||||
<div class="paste-modal-icon"><i class="bi bi-key"></i></div>
|
||||
<div>
|
||||
<h3 class="paste-modal-title">账户凭证</h3>
|
||||
<p class="paste-modal-subtitle" id="credModalEmail"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note-fields">
|
||||
<div class="note-field-group">
|
||||
<label class="input-label">Client ID</label>
|
||||
<div class="cred-value-box" id="credClientId"></div>
|
||||
</div>
|
||||
<div class="note-field-group">
|
||||
<label class="input-label">Refresh Token</label>
|
||||
<div class="cred-value-box" id="credToken" style="word-break:break-all;max-height:120px;overflow-y:auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paste-modal-buttons">
|
||||
<button class="btn btn-primary" id="copyCredClientIdBtn"><i class="bi bi-clipboard"></i> 复制ID</button>
|
||||
<button class="btn btn-primary" id="copyCredTokenBtn"><i class="bi bi-clipboard"></i> 复制Token</button>
|
||||
<button class="btn btn-cancel" id="closeCredentialBtn">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast 提示 -->
|
||||
<div class="toast-container" id="toastContainer"></div>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -437,19 +437,17 @@ body {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.data-table th:nth-child(2) { width: 14%; min-width: 150px; } /* 邮箱 */
|
||||
.data-table th:nth-child(2) { width: 16%; min-width: 160px; } /* 邮箱 */
|
||||
.data-table th:nth-child(3) { width: 6%; min-width: 70px; } /* 密码 */
|
||||
.data-table th:nth-child(4) { width: 8%; min-width: 90px; } /* 客户ID */
|
||||
.data-table th:nth-child(5) { width: 10%; min-width: 110px; } /* 令牌 */
|
||||
.data-table th:nth-child(6) { width: 65px; } /* 支付状态 */
|
||||
.data-table th:nth-child(7) { width: 65px; } /* 退款状态 */
|
||||
.data-table th:nth-child(8) { width: 65px; } /* 退款到账 */
|
||||
.data-table th:nth-child(9) { width: 85px; } /* 支付时间 */
|
||||
.data-table th:nth-child(10) { width: 85px; } /* 退款时间 */
|
||||
.data-table th:nth-child(11) { width: 85px; } /* 封号时间 */
|
||||
.data-table th:nth-child(12) { width: 120px; } /* 备注/卡号 */
|
||||
.data-table th:nth-child(13) { width: 150px; } /* 代理 */
|
||||
.data-table th:nth-child(14) { width: 190px; } /* 操作 */
|
||||
.data-table th:nth-child(4) { width: 55px; } /* 凭证 */
|
||||
.data-table th:nth-child(5) { width: 65px; } /* 支付状态 */
|
||||
.data-table th:nth-child(6) { width: 65px; } /* 退款状态 */
|
||||
.data-table th:nth-child(7) { width: 85px; } /* 支付时间 */
|
||||
.data-table th:nth-child(8) { width: 85px; } /* 退款时间 */
|
||||
.data-table th:nth-child(9) { width: 85px; } /* 封号时间 */
|
||||
.data-table th:nth-child(10) { width: 120px; } /* 备注/卡号 */
|
||||
.data-table th:nth-child(11) { width: 150px; } /* 代理 */
|
||||
.data-table th:nth-child(12) { width: 220px; } /* 操作 */
|
||||
|
||||
/* 单元格内容 */
|
||||
.value-container {
|
||||
@@ -1318,6 +1316,23 @@ body {
|
||||
}
|
||||
.note-card i { font-size: 10px; }
|
||||
|
||||
.cred-value-box {
|
||||
padding: 10px 14px;
|
||||
background: rgba(248, 250, 252, 0.8);
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
color: #334155;
|
||||
word-break: break-all;
|
||||
line-height: 1.5;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.credential-btn {
|
||||
font-size: 11px !important;
|
||||
}
|
||||
|
||||
.note-fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user