feat(accounts): 添加账号cookies存储及支付链接国家选择功能

- 在账号详情页添加cookies编辑与保存功能,用于支付请求
- 支付页面新增国家选择下拉框,支持多国货币计费
- 优化无痕打开浏览器功能,支持注入账号cookies
- 更新数据库模型、API路由及前端界面
This commit is contained in:
cnlimiter
2026-03-17 13:59:00 +08:00
parent d1e37aff56
commit 036a66d72b
10 changed files with 274 additions and 79 deletions

View File

@@ -613,6 +613,17 @@ async function viewAccount(id) {
${tokens.refresh_token ? `<button class="btn btn-ghost btn-sm" onclick="copyToClipboard('${escapeHtml(tokens.refresh_token)}')" style="margin-left: 8px;">📋</button>` : ''}
</div>
</div>
<div class="info-item" style="grid-column: span 2;">
<span class="label">Cookies支付用</span>
<div class="value">
<textarea id="cookies-input-${id}" rows="3"
style="width:100%;font-size:0.7rem;font-family:var(--font-mono);background:var(--surface-hover);border:1px solid var(--border);border-radius:4px;padding:6px;color:var(--text-primary);resize:vertical;"
placeholder="粘贴完整 cookie 字符串,留空则清除">${escapeHtml(account.cookies || '')}</textarea>
<button class="btn btn-secondary btn-sm" style="margin-top:4px" onclick="saveCookies(${id})">
保存 Cookies
</button>
</div>
</div>
</div>
<div style="margin-top: var(--spacing-lg); display: flex; gap: var(--spacing-sm);">
<button class="btn btn-primary" onclick="refreshToken(${id}); elements.detailModal.classList.remove('active');">
@@ -861,3 +872,16 @@ async function handleBatchUploadTm() {
updateBatchButtons();
}
}
// 保存账号 Cookies
async function saveCookies(id) {
const textarea = document.getElementById(`cookies-input-${id}`);
if (!textarea) return;
const cookiesValue = textarea.value.trim();
try {
await api.patch(`/accounts/${id}`, { cookies: cookiesValue });
toast.success('Cookies 已保存');
} catch (e) {
toast.error('保存 Cookies 失败: ' + e.message);
}
}

View File

@@ -2,6 +2,12 @@
* 支付页面 JavaScript
*/
const COUNTRY_CURRENCY_MAP = {
SG: 'SGD', US: 'USD', TR: 'TRY', JP: 'JPY',
HK: 'HKD', GB: 'GBP', EU: 'EUR', AU: 'AUD',
CA: 'CAD', IN: 'INR', BR: 'BRL', MX: 'MXN',
};
let selectedPlan = 'plus';
let generatedLink = '';
@@ -28,6 +34,13 @@ async function loadAccounts() {
}
}
// 国家切换
function onCountryChange() {
const country = document.getElementById('country-select').value;
const currency = COUNTRY_CURRENCY_MAP[country] || 'USD';
document.getElementById('currency-display').value = currency;
}
// 选择套餐
function selectPlan(plan) {
selectedPlan = plan;
@@ -47,9 +60,12 @@ async function generateLink() {
return;
}
const country = document.getElementById('country-select').value || 'SG';
const body = {
account_id: parseInt(accountId),
plan_type: selectedPlan,
country: country,
};
if (selectedPlan === 'team') {
@@ -58,6 +74,9 @@ async function generateLink() {
body.price_interval = document.getElementById('price-interval').value;
}
const btn = document.querySelector('.form-actions .btn-primary');
if (btn) { btn.disabled = true; btn.textContent = '生成中...'; }
try {
const resp = await fetch('/api/payment/generate-link', {
method: 'POST',
@@ -76,6 +95,8 @@ async function generateLink() {
}
} catch (e) {
ui.showToast('请求失败: ' + e.message, 'error');
} finally {
if (btn) { btn.disabled = false; btn.textContent = '生成支付链接'; }
}
}
@@ -85,7 +106,6 @@ function copyLink() {
navigator.clipboard.writeText(generatedLink).then(() => {
ui.showToast('已复制到剪贴板', 'success');
}).catch(() => {
// 降级方案
const ta = document.getElementById('link-text');
ta.select();
document.execCommand('copy');
@@ -93,19 +113,23 @@ function copyLink() {
});
}
// 无痕打开浏览器
// 无痕打开浏览器(携带账号 cookie
async function openIncognito() {
if (!generatedLink) {
ui.showToast('请先生成链接', 'warning');
return;
}
const accountId = document.getElementById('account-select').value;
const statusEl = document.getElementById('open-status');
statusEl.textContent = '正在打开...';
try {
const body = { url: generatedLink };
if (accountId) body.account_id = parseInt(accountId);
const resp = await fetch('/api/payment/open-incognito', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: generatedLink }),
body: JSON.stringify(body),
});
const data = await resp.json();
if (data.success) {