mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-05-06 20:02:51 +08:00
refactor(html): 优化前端页面
This commit is contained in:
@@ -439,6 +439,30 @@ body {
|
|||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-copy-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1;
|
||||||
|
background: var(--surface-hover);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-muted);
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-copy-icon:hover {
|
||||||
|
background: var(--primary-light);
|
||||||
|
color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
.btn-ghost:hover:not(:disabled) {
|
.btn-ghost:hover:not(:disabled) {
|
||||||
background: var(--surface-hover);
|
background: var(--surface-hover);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ function initEventListeners() {
|
|||||||
document.addEventListener('click', () => {
|
document.addEventListener('click', () => {
|
||||||
elements.exportMenu.classList.remove('active');
|
elements.exportMenu.classList.remove('active');
|
||||||
uploadMenu.classList.remove('active');
|
uploadMenu.classList.remove('active');
|
||||||
|
document.querySelectorAll('#accounts-table .dropdown-menu.active').forEach(m => m.classList.remove('active'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,21 +290,21 @@ function renderAccounts(accounts) {
|
|||||||
</td>
|
</td>
|
||||||
<td>${account.id}</td>
|
<td>${account.id}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="email-cell" title="${escapeHtml(account.email)}">
|
<span style="display:inline-flex;align-items:center;gap:4px;">
|
||||||
${escapeHtml(account.email)}
|
<span class="email-cell" title="${escapeHtml(account.email)}">${escapeHtml(account.email)}</span>
|
||||||
|
<button class="btn-copy-icon copy-email-btn" data-email="${escapeHtml(account.email)}" title="复制邮箱">📋</button>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="password-cell">
|
<td class="password-cell">
|
||||||
${account.password
|
${account.password
|
||||||
? `<span class="password-hidden" onclick="togglePassword(this, '${escapeHtml(account.password)}')" title="点击查看">${escapeHtml(account.password.substring(0, 4) + '****')}</span>`
|
? `<span style="display:inline-flex;align-items:center;gap:4px;">
|
||||||
|
<span class="password-hidden" data-pwd="${escapeHtml(account.password)}" onclick="togglePassword(this, this.dataset.pwd)" title="点击查看">${escapeHtml(account.password.substring(0, 4) + '****')}</span>
|
||||||
|
<button class="btn-copy-icon copy-pwd-btn" data-pwd="${escapeHtml(account.password)}" title="复制密码">📋</button>
|
||||||
|
</span>`
|
||||||
: '-'}
|
: '-'}
|
||||||
</td>
|
</td>
|
||||||
<td>${getServiceTypeText(account.email_service)}</td>
|
<td>${getServiceTypeText(account.email_service)}</td>
|
||||||
<td>
|
<td>${getStatusIcon(account.status)}</td>
|
||||||
<span class="status-badge ${getStatusClass('account', account.status)}">
|
|
||||||
${getStatusText('account', account.status)}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<div class="cpa-status">
|
<div class="cpa-status">
|
||||||
${account.cpa_uploaded
|
${account.cpa_uploaded
|
||||||
@@ -320,26 +321,17 @@ function renderAccounts(accounts) {
|
|||||||
</td>
|
</td>
|
||||||
<td>${format.date(account.last_refresh) || '-'}</td>
|
<td>${format.date(account.last_refresh) || '-'}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-buttons">
|
<div style="display:flex;gap:4px;align-items:center;white-space:nowrap;">
|
||||||
<button class="btn btn-ghost btn-sm" onclick="refreshToken(${account.id})" title="刷新Token">
|
<button class="btn btn-secondary btn-sm" onclick="viewAccount(${account.id})">详情</button>
|
||||||
🔄
|
<div class="dropdown" style="position:relative;">
|
||||||
</button>
|
<button class="btn btn-secondary btn-sm" onclick="event.stopPropagation();toggleMoreMenu(this)">更多</button>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="uploadAccount(${account.id})" title="上传账号">
|
<div class="dropdown-menu" style="min-width:100px;">
|
||||||
☁️
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);refreshToken(${account.id})">刷新</a>
|
||||||
</button>
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);uploadAccount(${account.id})">上传</a>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="markSubscription(${account.id})" title="标记订阅">
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeMoreMenu(this);markSubscription(${account.id})">标记</a>
|
||||||
🏷️
|
</div>
|
||||||
</button>
|
</div>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="viewAccount(${account.id})" title="查看详情">
|
<button class="btn btn-danger btn-sm" onclick="deleteAccount(${account.id}, '${escapeHtml(account.email)}')">删除</button>
|
||||||
👁️
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-ghost btn-sm" onclick="copyEmail('${escapeHtml(account.email)}')" title="复制邮箱">
|
|
||||||
📋
|
|
||||||
</button>
|
|
||||||
${account.password ? `<button class="btn btn-ghost btn-sm" onclick="copyToClipboard('${escapeHtml(account.password)}')" title="复制密码">🔑</button>` : ''}
|
|
||||||
<button class="btn btn-ghost btn-sm" onclick="deleteAccount(${account.id}, '${escapeHtml(account.email)}')" title="删除">
|
|
||||||
🗑️
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -365,6 +357,22 @@ function renderAccounts(accounts) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 绑定复制邮箱按钮
|
||||||
|
elements.table.querySelectorAll('.copy-email-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
copyToClipboard(btn.dataset.email);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 绑定复制密码按钮
|
||||||
|
elements.table.querySelectorAll('.copy-pwd-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
copyToClipboard(btn.dataset.pwd);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 渲染后同步全选框状态
|
// 渲染后同步全选框状态
|
||||||
const allCbs = elements.table.querySelectorAll('input[type="checkbox"][data-id]');
|
const allCbs = elements.table.querySelectorAll('input[type="checkbox"][data-id]');
|
||||||
const checkedCbs = elements.table.querySelectorAll('input[type="checkbox"][data-id]:checked');
|
const checkedCbs = elements.table.querySelectorAll('input[type="checkbox"][data-id]:checked');
|
||||||
@@ -1106,6 +1114,20 @@ async function handleBatchUploadTm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更多菜单切换
|
||||||
|
function toggleMoreMenu(btn) {
|
||||||
|
const menu = btn.nextElementSibling;
|
||||||
|
const isActive = menu.classList.contains('active');
|
||||||
|
// 关闭所有其他更多菜单
|
||||||
|
document.querySelectorAll('.dropdown-menu.active').forEach(m => m.classList.remove('active'));
|
||||||
|
if (!isActive) menu.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeMoreMenu(el) {
|
||||||
|
const menu = el.closest('.dropdown-menu');
|
||||||
|
if (menu) menu.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
// 保存账号 Cookies
|
// 保存账号 Cookies
|
||||||
async function saveCookies(id) {
|
async function saveCookies(id) {
|
||||||
const textarea = document.getElementById(`cookies-input-${id}`);
|
const textarea = document.getElementById(`cookies-input-${id}`);
|
||||||
|
|||||||
@@ -898,27 +898,33 @@ async function loadRecentAccounts() {
|
|||||||
<tr data-id="${account.id}">
|
<tr data-id="${account.id}">
|
||||||
<td>${account.id}</td>
|
<td>${account.id}</td>
|
||||||
<td>
|
<td>
|
||||||
<span title="${escapeHtml(account.email)}">${escapeHtml(account.email)}</span>
|
<span style="display:inline-flex;align-items:center;gap:4px;">
|
||||||
</td>
|
<span title="${escapeHtml(account.email)}">${escapeHtml(account.email)}</span>
|
||||||
<td class="password-cell">
|
<button class="btn-copy-icon copy-email-btn" data-email="${escapeHtml(account.email)}" title="复制邮箱">📋</button>
|
||||||
${account.password ? `<span class="password-hidden" title="点击查看">${escapeHtml(account.password.substring(0, 8))}...</span>` : '-'}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="status-badge ${getStatusClass('account', account.status)}" style="font-size: 0.7rem;">
|
|
||||||
${getStatusText('account', account.status)}
|
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="password-cell">
|
||||||
|
${account.password
|
||||||
|
? `<span style="display:inline-flex;align-items:center;gap:4px;">
|
||||||
|
<span class="password-hidden" title="点击查看">${escapeHtml(account.password.substring(0, 8))}...</span>
|
||||||
|
<button class="btn-copy-icon copy-pwd-btn" data-pwd="${escapeHtml(account.password)}" title="复制密码">📋</button>
|
||||||
|
</span>`
|
||||||
|
: '-'}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-buttons">
|
${getStatusIcon(account.status)}
|
||||||
<button class="btn btn-ghost btn-sm" onclick="copyToClipboard('${escapeHtml(account.email)}')" title="复制邮箱">
|
|
||||||
📋
|
|
||||||
</button>
|
|
||||||
${account.password ? `<button class="btn btn-ghost btn-sm" onclick="copyToClipboard('${escapeHtml(account.password)}')" title="复制密码">🔑</button>` : ''}
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
|
// 绑定复制按钮事件
|
||||||
|
elements.recentAccountsTable.querySelectorAll('.copy-email-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => { e.stopPropagation(); copyToClipboard(btn.dataset.email); });
|
||||||
|
});
|
||||||
|
elements.recentAccountsTable.querySelectorAll('.copy-pwd-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => { e.stopPropagation(); copyToClipboard(btn.dataset.pwd); });
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载账号列表失败:', error);
|
console.error('加载账号列表失败:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,23 @@ function initEventListeners() {
|
|||||||
// 临时邮箱配置
|
// 临时邮箱配置
|
||||||
elements.tempmailForm.addEventListener('submit', handleSaveTempmail);
|
elements.tempmailForm.addEventListener('submit', handleSaveTempmail);
|
||||||
elements.testTempmailBtn.addEventListener('click', handleTestTempmail);
|
elements.testTempmailBtn.addEventListener('click', handleTestTempmail);
|
||||||
|
|
||||||
|
// 点击其他地方关闭更多菜单
|
||||||
|
document.addEventListener('click', () => {
|
||||||
|
document.querySelectorAll('.dropdown-menu.active').forEach(m => m.classList.remove('active'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleEmailMoreMenu(btn) {
|
||||||
|
const menu = btn.nextElementSibling;
|
||||||
|
const isActive = menu.classList.contains('active');
|
||||||
|
document.querySelectorAll('.dropdown-menu.active').forEach(m => m.classList.remove('active'));
|
||||||
|
if (!isActive) menu.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeEmailMoreMenu(el) {
|
||||||
|
const menu = el.closest('.dropdown-menu');
|
||||||
|
if (menu) menu.classList.remove('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 切换添加表单子类型
|
// 切换添加表单子类型
|
||||||
@@ -211,19 +228,20 @@ async function loadOutlookServices() {
|
|||||||
${service.config?.has_oauth ? 'OAuth' : '密码'}
|
${service.config?.has_oauth ? 'OAuth' : '密码'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td title="${service.enabled ? '已启用' : '已禁用'}">${service.enabled ? '✅' : '⭕'}</td>
|
||||||
<span class="status-badge ${service.enabled ? 'active' : 'disabled'}">
|
|
||||||
${service.enabled ? '启用' : '禁用'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>${service.priority}</td>
|
<td>${service.priority}</td>
|
||||||
<td>${format.date(service.last_used)}</td>
|
<td>${format.date(service.last_used)}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-buttons">
|
<div style="display:flex;gap:4px;align-items:center;white-space:nowrap;">
|
||||||
<button class="btn btn-ghost btn-sm" onclick="editOutlookService(${service.id})" title="编辑">✏️</button>
|
<button class="btn btn-secondary btn-sm" onclick="editOutlookService(${service.id})">编辑</button>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="toggleService(${service.id}, ${!service.enabled})" title="${service.enabled ? '禁用' : '启用'}">${service.enabled ? '🔇' : '🔊'}</button>
|
<div class="dropdown" style="position:relative;">
|
||||||
<button class="btn btn-ghost btn-sm" onclick="testService(${service.id})" title="测试">🔌</button>
|
<button class="btn btn-secondary btn-sm" onclick="event.stopPropagation();toggleEmailMoreMenu(this)">更多</button>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="deleteService(${service.id}, '${escapeHtml(service.name)}')" title="删除">🗑️</button>
|
<div class="dropdown-menu" style="min-width:80px;">
|
||||||
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeEmailMoreMenu(this);toggleService(${service.id}, ${!service.enabled})">${service.enabled ? '禁用' : '启用'}</a>
|
||||||
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeEmailMoreMenu(this);testService(${service.id})">测试</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-danger btn-sm" onclick="deleteService(${service.id}, '${escapeHtml(service.name)}')">删除</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -281,15 +299,20 @@ async function loadCustomServices() {
|
|||||||
<td>${escapeHtml(service.name)}</td>
|
<td>${escapeHtml(service.name)}</td>
|
||||||
<td>${typeLabel}</td>
|
<td>${typeLabel}</td>
|
||||||
<td style="font-size: 0.75rem;">${escapeHtml(addr)}</td>
|
<td style="font-size: 0.75rem;">${escapeHtml(addr)}</td>
|
||||||
<td><span class="status-badge ${service.enabled ? 'active' : 'disabled'}">${service.enabled ? '启用' : '禁用'}</span></td>
|
<td title="${service.enabled ? '已启用' : '已禁用'}">${service.enabled ? '✅' : '⭕'}</td>
|
||||||
<td>${service.priority}</td>
|
<td>${service.priority}</td>
|
||||||
<td>${format.date(service.last_used)}</td>
|
<td>${format.date(service.last_used)}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-buttons">
|
<div style="display:flex;gap:4px;align-items:center;white-space:nowrap;">
|
||||||
<button class="btn btn-ghost btn-sm" onclick="editCustomService(${service.id}, '${service._subType}')" title="编辑">✏️</button>
|
<button class="btn btn-secondary btn-sm" onclick="editCustomService(${service.id}, '${service._subType}')">编辑</button>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="toggleService(${service.id}, ${!service.enabled})" title="${service.enabled ? '禁用' : '启用'}">${service.enabled ? '🔇' : '🔊'}</button>
|
<div class="dropdown" style="position:relative;">
|
||||||
<button class="btn btn-ghost btn-sm" onclick="testService(${service.id})" title="测试">🔌</button>
|
<button class="btn btn-secondary btn-sm" onclick="event.stopPropagation();toggleEmailMoreMenu(this)">更多</button>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="deleteService(${service.id}, '${escapeHtml(service.name)}')" title="删除">🗑️</button>
|
<div class="dropdown-menu" style="min-width:80px;">
|
||||||
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeEmailMoreMenu(this);toggleService(${service.id}, ${!service.enabled})">${service.enabled ? '禁用' : '启用'}</a>
|
||||||
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeEmailMoreMenu(this);testService(${service.id})">测试</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-danger btn-sm" onclick="deleteService(${service.id}, '${escapeHtml(service.name)}')">删除</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
initEventListeners();
|
initEventListeners();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', () => {
|
||||||
|
document.querySelectorAll('.dropdown-menu.active').forEach(m => m.classList.remove('active'));
|
||||||
|
});
|
||||||
|
|
||||||
// 初始化标签页
|
// 初始化标签页
|
||||||
function initTabs() {
|
function initTabs() {
|
||||||
elements.tabs.forEach(btn => {
|
elements.tabs.forEach(btn => {
|
||||||
@@ -424,11 +428,7 @@ function renderEmailServices(services) {
|
|||||||
</td>
|
</td>
|
||||||
<td>${escapeHtml(service.name)}</td>
|
<td>${escapeHtml(service.name)}</td>
|
||||||
<td>${getServiceTypeText(service.service_type)}</td>
|
<td>${getServiceTypeText(service.service_type)}</td>
|
||||||
<td>
|
<td title="${service.enabled ? '已启用' : '已禁用'}">${service.enabled ? '✅' : '⭕'}</td>
|
||||||
<span class="status-badge ${service.enabled ? 'active' : 'disabled'}">
|
|
||||||
${service.enabled ? '已启用' : '已禁用'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>${service.priority}</td>
|
<td>${service.priority}</td>
|
||||||
<td>${format.date(service.last_used)}</td>
|
<td>${format.date(service.last_used)}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -811,32 +811,38 @@ function renderProxies(proxies) {
|
|||||||
: `<button class="btn btn-ghost btn-sm" onclick="handleSetProxyDefault(${proxy.id})" title="设为默认">设默认</button>`
|
: `<button class="btn btn-ghost btn-sm" onclick="handleSetProxyDefault(${proxy.id})" title="设为默认">设默认</button>`
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td title="${proxy.enabled ? '已启用' : '已禁用'}">${proxy.enabled ? '✅' : '⭕'}</td>
|
||||||
<span class="status-badge ${proxy.enabled ? 'active' : 'disabled'}">
|
|
||||||
${proxy.enabled ? '已启用' : '已禁用'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>${format.date(proxy.last_used)}</td>
|
<td>${format.date(proxy.last_used)}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="action-buttons">
|
<div style="display:flex;gap:4px;align-items:center;white-space:nowrap;">
|
||||||
<button class="btn btn-ghost btn-sm" onclick="testProxyItem(${proxy.id})" title="测试">
|
<button class="btn btn-secondary btn-sm" onclick="editProxyItem(${proxy.id})">编辑</button>
|
||||||
🔌
|
<div class="dropdown" style="position:relative;">
|
||||||
</button>
|
<button class="btn btn-secondary btn-sm" onclick="event.stopPropagation();toggleSettingsMoreMenu(this)">更多</button>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="editProxyItem(${proxy.id})" title="编辑">
|
<div class="dropdown-menu" style="min-width:80px;">
|
||||||
✏️
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeSettingsMoreMenu(this);testProxyItem(${proxy.id})">测试</a>
|
||||||
</button>
|
<a href="#" class="dropdown-item" onclick="event.preventDefault();closeSettingsMoreMenu(this);toggleProxyItem(${proxy.id}, ${!proxy.enabled})">${proxy.enabled ? '禁用' : '启用'}</a>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="toggleProxyItem(${proxy.id}, ${!proxy.enabled})" title="${proxy.enabled ? '禁用' : '启用'}">
|
${!proxy.is_default ? `<a href="#" class="dropdown-item" onclick="event.preventDefault();closeSettingsMoreMenu(this);handleSetProxyDefault(${proxy.id})">设为默认</a>` : ''}
|
||||||
${proxy.enabled ? '🔒' : '🔓'}
|
</div>
|
||||||
</button>
|
</div>
|
||||||
<button class="btn btn-ghost btn-sm" onclick="deleteProxyItem(${proxy.id})" title="删除">
|
<button class="btn btn-danger btn-sm" onclick="deleteProxyItem(${proxy.id})">删除</button>
|
||||||
🗑️
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleSettingsMoreMenu(btn) {
|
||||||
|
const menu = btn.nextElementSibling;
|
||||||
|
const isActive = menu.classList.contains('active');
|
||||||
|
document.querySelectorAll('.dropdown-menu.active').forEach(m => m.classList.remove('active'));
|
||||||
|
if (!isActive) menu.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSettingsMoreMenu(el) {
|
||||||
|
const menu = el.closest('.dropdown-menu');
|
||||||
|
if (menu) menu.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
// 设为默认代理
|
// 设为默认代理
|
||||||
async function handleSetProxyDefault(id) {
|
async function handleSetProxyDefault(id) {
|
||||||
try {
|
try {
|
||||||
@@ -1070,11 +1076,7 @@ function renderTmServicesTable(services) {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>${escapeHtml(s.name)}</td>
|
<td>${escapeHtml(s.name)}</td>
|
||||||
<td style="font-size:0.85rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</td>
|
<td style="font-size:0.85rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</td>
|
||||||
<td>
|
<td style="text-align:center;" title="${s.enabled ? '已启用' : '已禁用'}">${s.enabled ? '✅' : '⭕'}</td>
|
||||||
<span class="badge" style="background:${s.enabled ? 'var(--success-color)' : 'var(--border)'};color:${s.enabled ? '#fff' : 'var(--text-muted)'};font-size:0.75rem;padding:2px 8px;border-radius:10px;">
|
|
||||||
${s.enabled ? '启用' : '禁用'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td style="text-align:center;">${s.priority}</td>
|
<td style="text-align:center;">${s.priority}</td>
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
<button class="btn btn-secondary btn-sm" onclick="editTmService(${s.id})">编辑</button>
|
<button class="btn btn-secondary btn-sm" onclick="editTmService(${s.id})">编辑</button>
|
||||||
@@ -1235,11 +1237,7 @@ function renderCpaServicesTable(services) {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>${escapeHtml(s.name)}</td>
|
<td>${escapeHtml(s.name)}</td>
|
||||||
<td style="font-size:0.85rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</td>
|
<td style="font-size:0.85rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</td>
|
||||||
<td>
|
<td style="text-align:center;" title="${s.enabled ? '已启用' : '已禁用'}">${s.enabled ? '✅' : '⭕'}</td>
|
||||||
<span class="badge" style="background:${s.enabled ? 'var(--success-color)' : 'var(--border)'};color:${s.enabled ? '#fff' : 'var(--text-muted)'};font-size:0.75rem;padding:2px 8px;border-radius:10px;">
|
|
||||||
${s.enabled ? '启用' : '禁用'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td style="text-align:center;">${s.priority}</td>
|
<td style="text-align:center;">${s.priority}</td>
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
<button class="btn btn-secondary btn-sm" onclick="editCpaService(${s.id})">编辑</button>
|
<button class="btn btn-secondary btn-sm" onclick="editCpaService(${s.id})">编辑</button>
|
||||||
@@ -1402,11 +1400,7 @@ function renderSub2ApiServices(services) {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>${escapeHtml(s.name)}</td>
|
<td>${escapeHtml(s.name)}</td>
|
||||||
<td style="font-size:0.85rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</td>
|
<td style="font-size:0.85rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</td>
|
||||||
<td>
|
<td style="text-align:center;" title="${s.enabled ? '已启用' : '已禁用'}">${s.enabled ? '✅' : '⭕'}</td>
|
||||||
<span class="badge" style="background:${s.enabled ? 'var(--success-color)' : 'var(--border)'};color:${s.enabled ? '#fff' : 'var(--text-muted)'};font-size:0.75rem;padding:2px 8px;border-radius:10px;">
|
|
||||||
${s.enabled ? '启用' : '禁用'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td style="text-align:center;">${s.priority}</td>
|
<td style="text-align:center;">${s.priority}</td>
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
<button class="btn btn-secondary btn-sm" onclick="editSub2ApiService(${s.id})">编辑</button>
|
<button class="btn btn-secondary btn-sm" onclick="editSub2ApiService(${s.id})">编辑</button>
|
||||||
|
|||||||
@@ -368,6 +368,19 @@ function getServiceTypeText(type) {
|
|||||||
return statusMap.service[type] || type;
|
return statusMap.service[type] || type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accountStatusIconMap = {
|
||||||
|
active: { icon: '🟢', title: '活跃' },
|
||||||
|
expired: { icon: '🟡', title: '过期' },
|
||||||
|
banned: { icon: '🔴', title: '封禁' },
|
||||||
|
failed: { icon: '❌', title: '失败' },
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStatusIcon(status) {
|
||||||
|
const s = accountStatusIconMap[status];
|
||||||
|
if (!s) return `<span title="${status}">⚪</span>`;
|
||||||
|
return `<span title="${s.title}">${s.icon}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 确认对话框
|
// 确认对话框
|
||||||
// ============================================
|
// ============================================
|
||||||
@@ -420,10 +433,29 @@ function confirm(message, title = '确认操作') {
|
|||||||
// ============================================
|
// ============================================
|
||||||
|
|
||||||
async function copyToClipboard(text) {
|
async function copyToClipboard(text) {
|
||||||
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
toast.success('已复制到剪贴板');
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
// 降级到 execCommand
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(text);
|
const ta = document.createElement('textarea');
|
||||||
toast.success('已复制到剪贴板');
|
ta.value = text;
|
||||||
return true;
|
ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0;pointer-events:none;';
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.focus();
|
||||||
|
ta.select();
|
||||||
|
const ok = document.execCommand('copy');
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
if (ok) {
|
||||||
|
toast.success('已复制到剪贴板');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw new Error('execCommand failed');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error('复制失败');
|
toast.error('复制失败');
|
||||||
return false;
|
return false;
|
||||||
@@ -498,3 +530,4 @@ window.throttle = throttle;
|
|||||||
window.getStatusText = getStatusText;
|
window.getStatusText = getStatusText;
|
||||||
window.getStatusClass = getStatusClass;
|
window.getStatusClass = getStatusClass;
|
||||||
window.getServiceTypeText = getServiceTypeText;
|
window.getServiceTypeText = getServiceTypeText;
|
||||||
|
window.getStatusIcon = getStatusIcon;
|
||||||
|
|||||||
@@ -172,7 +172,7 @@
|
|||||||
<th style="width: 80px;">CPA</th>
|
<th style="width: 80px;">CPA</th>
|
||||||
<th style="width: 80px;">订阅</th>
|
<th style="width: 80px;">订阅</th>
|
||||||
<th style="width: 140px;">最后刷新</th>
|
<th style="width: 140px;">最后刷新</th>
|
||||||
<th style="width: 170px;">操作</th>
|
<th style="width: 160px;">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="accounts-table">
|
<tbody id="accounts-table">
|
||||||
|
|||||||
@@ -386,7 +386,6 @@
|
|||||||
<th>邮箱</th>
|
<th>邮箱</th>
|
||||||
<th style="width: 120px;">密码</th>
|
<th style="width: 120px;">密码</th>
|
||||||
<th style="width: 80px;">状态</th>
|
<th style="width: 80px;">状态</th>
|
||||||
<th style="width: 80px;">操作</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="recent-accounts-table">
|
<tbody id="recent-accounts-table">
|
||||||
|
|||||||
Reference in New Issue
Block a user