diff --git a/static/css/style.css b/static/css/style.css
index b6e7818..4c40acc 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -439,6 +439,30 @@ body {
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) {
background: var(--surface-hover);
color: var(--text-primary);
diff --git a/static/js/accounts.js b/static/js/accounts.js
index db18034..923d3b9 100644
--- a/static/js/accounts.js
+++ b/static/js/accounts.js
@@ -172,6 +172,7 @@ function initEventListeners() {
document.addEventListener('click', () => {
elements.exportMenu.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) {
${account.id} |
-
- ${escapeHtml(account.email)}
+
+ ${escapeHtml(account.email)}
+
|
${account.password
- ? `${escapeHtml(account.password.substring(0, 4) + '****')}`
+ ? `
+ ${escapeHtml(account.password.substring(0, 4) + '****')}
+
+ `
: '-'}
|
${getServiceTypeText(account.email_service)} |
-
-
- ${getStatusText('account', account.status)}
-
- |
+ ${getStatusIcon(account.status)} |
${account.cpa_uploaded
@@ -320,26 +321,17 @@ function renderAccounts(accounts) {
|
${format.date(account.last_refresh) || '-'} |
- |
@@ -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 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
async function saveCookies(id) {
const textarea = document.getElementById(`cookies-input-${id}`);
diff --git a/static/js/app.js b/static/js/app.js
index 73e8878..c240222 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -898,27 +898,33 @@ async function loadRecentAccounts() {
| ${account.id} |
- ${escapeHtml(account.email)}
- |
-
- ${account.password ? `${escapeHtml(account.password.substring(0, 8))}...` : '-'}
- |
-
-
- ${getStatusText('account', account.status)}
+
+ ${escapeHtml(account.email)}
+
|
+
+ ${account.password
+ ? `
+ ${escapeHtml(account.password.substring(0, 8))}...
+
+ `
+ : '-'}
+ |
-
-
- ${account.password ? `` : ''}
-
+ ${getStatusIcon(account.status)}
|
`).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) {
console.error('加载账号列表失败:', error);
}
diff --git a/static/js/email_services.js b/static/js/email_services.js
index e142480..aca83cc 100644
--- a/static/js/email_services.js
+++ b/static/js/email_services.js
@@ -145,6 +145,23 @@ function initEventListeners() {
// 临时邮箱配置
elements.tempmailForm.addEventListener('submit', handleSaveTempmail);
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.enabled ? '启用' : '禁用'}
-
- |
+ ${service.enabled ? '✅' : '⭕'} |
${service.priority} |
${format.date(service.last_used)} |
- |
@@ -281,15 +299,20 @@ async function loadCustomServices() {
${escapeHtml(service.name)} |
${typeLabel} |
${escapeHtml(addr)} |
- ${service.enabled ? '启用' : '禁用'} |
+ ${service.enabled ? '✅' : '⭕'} |
${service.priority} |
${format.date(service.last_used)} |
- |
`;
diff --git a/static/js/settings.js b/static/js/settings.js
index 4dc3a9e..595f038 100644
--- a/static/js/settings.js
+++ b/static/js/settings.js
@@ -90,6 +90,10 @@ document.addEventListener('DOMContentLoaded', () => {
initEventListeners();
});
+document.addEventListener('click', () => {
+ document.querySelectorAll('.dropdown-menu.active').forEach(m => m.classList.remove('active'));
+});
+
// 初始化标签页
function initTabs() {
elements.tabs.forEach(btn => {
@@ -424,11 +428,7 @@ function renderEmailServices(services) {
${escapeHtml(service.name)} |
${getServiceTypeText(service.service_type)} |
-
-
- ${service.enabled ? '已启用' : '已禁用'}
-
- |
+ ${service.enabled ? '✅' : '⭕'} |
${service.priority} |
${format.date(service.last_used)} |
@@ -811,32 +811,38 @@ function renderProxies(proxies) {
: ``
}
|
-
-
- ${proxy.enabled ? '已启用' : '已禁用'}
-
- |
+ ${proxy.enabled ? '✅' : '⭕'} |
${format.date(proxy.last_used)} |
- |
`).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) {
try {
@@ -1070,11 +1076,7 @@ function renderTmServicesTable(services) {
| ${escapeHtml(s.name)} |
${escapeHtml(s.api_url)} |
-
-
- ${s.enabled ? '启用' : '禁用'}
-
- |
+ ${s.enabled ? '✅' : '⭕'} |
${s.priority} |
@@ -1235,11 +1237,7 @@ function renderCpaServicesTable(services) {
|
| ${escapeHtml(s.name)} |
${escapeHtml(s.api_url)} |
-
-
- ${s.enabled ? '启用' : '禁用'}
-
- |
+ ${s.enabled ? '✅' : '⭕'} |
${s.priority} |
@@ -1402,11 +1400,7 @@ function renderSub2ApiServices(services) {
|
| ${escapeHtml(s.name)} |
${escapeHtml(s.api_url)} |
-
-
- ${s.enabled ? '启用' : '禁用'}
-
- |
+ ${s.enabled ? '✅' : '⭕'} |
${s.priority} |
diff --git a/static/js/utils.js b/static/js/utils.js
index 3c17184..e5adf85 100644
--- a/static/js/utils.js
+++ b/static/js/utils.js
@@ -368,6 +368,19 @@ function getServiceTypeText(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 `⚪`;
+ return `${s.icon}`;
+}
+
// ============================================
// 确认对话框
// ============================================
@@ -420,10 +433,29 @@ function confirm(message, title = '确认操作') {
// ============================================
async function copyToClipboard(text) {
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ try {
+ await navigator.clipboard.writeText(text);
+ toast.success('已复制到剪贴板');
+ return true;
+ } catch (err) {
+ // 降级到 execCommand
+ }
+ }
try {
- await navigator.clipboard.writeText(text);
- toast.success('已复制到剪贴板');
- return true;
+ const ta = document.createElement('textarea');
+ ta.value = text;
+ 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) {
toast.error('复制失败');
return false;
@@ -498,3 +530,4 @@ window.throttle = throttle;
window.getStatusText = getStatusText;
window.getStatusClass = getStatusClass;
window.getServiceTypeText = getServiceTypeText;
+window.getStatusIcon = getStatusIcon;
diff --git a/templates/accounts.html b/templates/accounts.html
index fec857d..efd1204 100644
--- a/templates/accounts.html
+++ b/templates/accounts.html
@@ -172,7 +172,7 @@
| CPA |
订阅 |
最后刷新 |
- 操作 |
+ 操作 |
diff --git a/templates/index.html b/templates/index.html
index 12b9602..6a52391 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -386,7 +386,6 @@
邮箱 |
密码 |
状态 |
- 操作 |