mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-06-26 01:31:47 +08:00
fix(front): 优化前端显示
This commit is contained in:
@@ -85,13 +85,13 @@ const elements = {
|
||||
// 注册后自动操作
|
||||
autoUploadCpa: document.getElementById('auto-upload-cpa'),
|
||||
cpaServiceSelectGroup: document.getElementById('cpa-service-select-group'),
|
||||
cpaServiceCheckboxes: document.getElementById('cpa-service-checkboxes'),
|
||||
cpaServiceSelect: document.getElementById('cpa-service-select'),
|
||||
autoUploadSub2api: document.getElementById('auto-upload-sub2api'),
|
||||
sub2apiServiceSelectGroup: document.getElementById('sub2api-service-select-group'),
|
||||
sub2apiServiceCheckboxes: document.getElementById('sub2api-service-checkboxes'),
|
||||
sub2apiServiceSelect: document.getElementById('sub2api-service-select'),
|
||||
autoUploadTm: document.getElementById('auto-upload-tm'),
|
||||
tmServiceSelectGroup: document.getElementById('tm-service-select-group'),
|
||||
tmServiceCheckboxes: document.getElementById('tm-service-checkboxes'),
|
||||
tmServiceSelect: document.getElementById('tm-service-select'),
|
||||
};
|
||||
|
||||
// 初始化
|
||||
@@ -108,14 +108,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// 初始化注册后自动操作选项(CPA / Sub2API / TM)
|
||||
async function initAutoUploadOptions() {
|
||||
await Promise.all([
|
||||
loadServiceCheckboxes('cpa', '/cpa-services?enabled=true', elements.cpaServiceCheckboxes, elements.autoUploadCpa, elements.cpaServiceSelectGroup),
|
||||
loadServiceCheckboxes('sub2api', '/sub2api-services?enabled=true', elements.sub2apiServiceCheckboxes, elements.autoUploadSub2api, elements.sub2apiServiceSelectGroup),
|
||||
loadServiceCheckboxes('tm', '/tm-services?enabled=true', elements.tmServiceCheckboxes, elements.autoUploadTm, elements.tmServiceSelectGroup),
|
||||
loadServiceSelect('/cpa-services?enabled=true', elements.cpaServiceSelect, elements.autoUploadCpa, elements.cpaServiceSelectGroup),
|
||||
loadServiceSelect('/sub2api-services?enabled=true', elements.sub2apiServiceSelect, elements.autoUploadSub2api, elements.sub2apiServiceSelectGroup),
|
||||
loadServiceSelect('/tm-services?enabled=true', elements.tmServiceSelect, elements.autoUploadTm, elements.tmServiceSelectGroup),
|
||||
]);
|
||||
}
|
||||
|
||||
// 通用:加载服务 checkbox 列表,并处理联动
|
||||
async function loadServiceCheckboxes(type, apiPath, container, checkbox, selectGroup) {
|
||||
// 通用:构建自定义多选下拉组件并处理联动
|
||||
async function loadServiceSelect(apiPath, container, checkbox, selectGroup) {
|
||||
if (!checkbox || !container) return;
|
||||
let services = [];
|
||||
try {
|
||||
@@ -127,14 +127,31 @@ async function loadServiceCheckboxes(type, apiPath, container, checkbox, selectG
|
||||
checkbox.title = '请先在设置中添加对应服务';
|
||||
const label = checkbox.closest('label');
|
||||
if (label) label.style.opacity = '0.5';
|
||||
if (container) container.innerHTML = '<div style="color:var(--text-muted);font-size:0.8rem;">暂无可用服务</div>';
|
||||
container.innerHTML = '<div class="msd-empty">暂无可用服务</div>';
|
||||
} else {
|
||||
container.innerHTML = services.map(s => `
|
||||
<label style="display:flex;align-items:center;gap:6px;cursor:pointer;">
|
||||
<input type="checkbox" class="upload-svc-cb upload-svc-${type}" value="${s.id}" checked>
|
||||
<span style="font-size:0.85rem;">${escapeHtml(s.name)}</span>
|
||||
</label>
|
||||
`).join('');
|
||||
const items = services.map(s =>
|
||||
`<label class="msd-item">
|
||||
<input type="checkbox" value="${s.id}" checked>
|
||||
<span>${escapeHtml(s.name)}</span>
|
||||
</label>`
|
||||
).join('');
|
||||
container.innerHTML = `
|
||||
<div class="msd-dropdown" id="${container.id}-dd">
|
||||
<div class="msd-trigger" onclick="toggleMsd('${container.id}-dd')">
|
||||
<span class="msd-label">全部 (${services.length})</span>
|
||||
<span class="msd-arrow">▼</span>
|
||||
</div>
|
||||
<div class="msd-list">${items}</div>
|
||||
</div>`;
|
||||
// 监听 checkbox 变化,更新触发器文字
|
||||
container.querySelectorAll('.msd-item input').forEach(cb => {
|
||||
cb.addEventListener('change', () => updateMsdLabel(container.id + '-dd'));
|
||||
});
|
||||
// 点击外部关闭
|
||||
document.addEventListener('click', (e) => {
|
||||
const dd = document.getElementById(container.id + '-dd');
|
||||
if (dd && !dd.contains(e.target)) dd.classList.remove('open');
|
||||
}, true);
|
||||
}
|
||||
|
||||
// 联动显示/隐藏服务选择区
|
||||
@@ -143,13 +160,27 @@ async function loadServiceCheckboxes(type, apiPath, container, checkbox, selectG
|
||||
});
|
||||
}
|
||||
|
||||
// 获取选中的服务 ID 列表
|
||||
function getCheckedServiceIds(type) {
|
||||
const ids = [];
|
||||
document.querySelectorAll(`.upload-svc-${type}:checked`).forEach(cb => {
|
||||
ids.push(parseInt(cb.value));
|
||||
});
|
||||
return ids;
|
||||
function toggleMsd(ddId) {
|
||||
const dd = document.getElementById(ddId);
|
||||
if (dd) dd.classList.toggle('open');
|
||||
}
|
||||
|
||||
function updateMsdLabel(ddId) {
|
||||
const dd = document.getElementById(ddId);
|
||||
if (!dd) return;
|
||||
const all = dd.querySelectorAll('.msd-item input');
|
||||
const checked = dd.querySelectorAll('.msd-item input:checked');
|
||||
const label = dd.querySelector('.msd-label');
|
||||
if (!label) return;
|
||||
if (checked.length === 0) label.textContent = '未选择';
|
||||
else if (checked.length === all.length) label.textContent = `全部 (${all.length})`;
|
||||
else label.textContent = Array.from(checked).map(c => c.nextElementSibling.textContent).join(', ');
|
||||
}
|
||||
|
||||
// 获取自定义多选下拉中选中的服务 ID 列表
|
||||
function getSelectedServiceIds(container) {
|
||||
if (!container) return [];
|
||||
return Array.from(container.querySelectorAll('.msd-item input:checked')).map(cb => parseInt(cb.value));
|
||||
}
|
||||
|
||||
// 事件监听
|
||||
@@ -395,11 +426,11 @@ async function handleStartRegistration(e) {
|
||||
const requestData = {
|
||||
email_service_type: emailServiceType,
|
||||
auto_upload_cpa: elements.autoUploadCpa ? elements.autoUploadCpa.checked : false,
|
||||
cpa_service_ids: elements.autoUploadCpa && elements.autoUploadCpa.checked ? getCheckedServiceIds('cpa') : [],
|
||||
cpa_service_ids: elements.autoUploadCpa && elements.autoUploadCpa.checked ? getSelectedServiceIds(elements.cpaServiceSelect) : [],
|
||||
auto_upload_sub2api: elements.autoUploadSub2api ? elements.autoUploadSub2api.checked : false,
|
||||
sub2api_service_ids: elements.autoUploadSub2api && elements.autoUploadSub2api.checked ? getCheckedServiceIds('sub2api') : [],
|
||||
sub2api_service_ids: elements.autoUploadSub2api && elements.autoUploadSub2api.checked ? getSelectedServiceIds(elements.sub2apiServiceSelect) : [],
|
||||
auto_upload_tm: elements.autoUploadTm ? elements.autoUploadTm.checked : false,
|
||||
tm_service_ids: elements.autoUploadTm && elements.autoUploadTm.checked ? getCheckedServiceIds('tm') : [],
|
||||
tm_service_ids: elements.autoUploadTm && elements.autoUploadTm.checked ? getSelectedServiceIds(elements.tmServiceSelect) : [],
|
||||
};
|
||||
|
||||
// 如果选择了数据库中的服务,传递 service_id
|
||||
@@ -1099,11 +1130,11 @@ async function handleOutlookBatchRegistration() {
|
||||
concurrency: Math.min(50, Math.max(1, concurrency)),
|
||||
mode: mode,
|
||||
auto_upload_cpa: elements.autoUploadCpa ? elements.autoUploadCpa.checked : false,
|
||||
cpa_service_ids: elements.autoUploadCpa && elements.autoUploadCpa.checked ? getCheckedServiceIds('cpa') : [],
|
||||
cpa_service_ids: elements.autoUploadCpa && elements.autoUploadCpa.checked ? getSelectedServiceIds(elements.cpaServiceSelect) : [],
|
||||
auto_upload_sub2api: elements.autoUploadSub2api ? elements.autoUploadSub2api.checked : false,
|
||||
sub2api_service_ids: elements.autoUploadSub2api && elements.autoUploadSub2api.checked ? getCheckedServiceIds('sub2api') : [],
|
||||
sub2api_service_ids: elements.autoUploadSub2api && elements.autoUploadSub2api.checked ? getSelectedServiceIds(elements.sub2apiServiceSelect) : [],
|
||||
auto_upload_tm: elements.autoUploadTm ? elements.autoUploadTm.checked : false,
|
||||
tm_service_ids: elements.autoUploadTm && elements.autoUploadTm.checked ? getCheckedServiceIds('tm') : [],
|
||||
tm_service_ids: elements.autoUploadTm && elements.autoUploadTm.checked ? getSelectedServiceIds(elements.tmServiceSelect) : [],
|
||||
};
|
||||
|
||||
addLog('info', `[系统] 正在启动 Outlook 批量注册 (${selectedIds.length} 个账户)...`);
|
||||
|
||||
@@ -1076,7 +1076,7 @@ function renderTmServicesTable(services) {
|
||||
</span>
|
||||
</td>
|
||||
<td style="text-align:center;">${s.priority}</td>
|
||||
<td>
|
||||
<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="testTmServiceById(${s.id})">测试</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="deleteTmService(${s.id}, '${escapeHtml(s.name)}')">删除</button>
|
||||
@@ -1241,7 +1241,7 @@ function renderCpaServicesTable(services) {
|
||||
</span>
|
||||
</td>
|
||||
<td style="text-align:center;">${s.priority}</td>
|
||||
<td>
|
||||
<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="testCpaServiceById(${s.id})">测试</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="deleteCpaService(${s.id}, '${escapeHtml(s.name)}')">删除</button>
|
||||
@@ -1395,20 +1395,23 @@ async function loadSub2ApiServices() {
|
||||
function renderSub2ApiServices(services) {
|
||||
if (!elements.sub2ApiServicesTable) return;
|
||||
if (!services || services.length === 0) {
|
||||
elements.sub2ApiServicesTable.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:20px;">暂无服务,点击"添加服务"按钮添加</td></tr>';
|
||||
elements.sub2ApiServicesTable.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--text-muted);padding:20px;">暂无 Sub2API 服务,点击「添加服务」新增</td></tr>';
|
||||
return;
|
||||
}
|
||||
elements.sub2ApiServicesTable.innerHTML = services.map(s => `
|
||||
<tr>
|
||||
<td>${escapeHtml(s.name)}</td>
|
||||
<td><code>${escapeHtml(s.api_url)}</code></td>
|
||||
<td><span class="status-badge ${s.enabled ? 'active' : 'disabled'}">${s.enabled ? '已启用' : '已禁用'}</span></td>
|
||||
<td>${s.priority}</td>
|
||||
<td style="font-size:0.85rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-ghost btn-sm" onclick="editSub2ApiService(${s.id})" title="编辑">✏️</button>
|
||||
<button class="btn btn-ghost btn-sm" onclick="deleteSub2ApiService(${s.id}, '${escapeHtml(s.name)}')" title="删除">🗑️</button>
|
||||
</div>
|
||||
<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="white-space:nowrap;">
|
||||
<button class="btn btn-secondary btn-sm" onclick="editSub2ApiService(${s.id})">编辑</button>
|
||||
<button class="btn btn-secondary btn-sm" onclick="testSub2ApiServiceById(${s.id})">测试</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="deleteSub2ApiService(${s.id}, '${escapeHtml(s.name)}')">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
@@ -1486,6 +1489,19 @@ async function handleSaveSub2ApiService(e) {
|
||||
}
|
||||
}
|
||||
|
||||
async function testSub2ApiServiceById(id) {
|
||||
try {
|
||||
const result = await api.post(`/sub2api-services/${id}/test`);
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
} else {
|
||||
toast.error(result.message);
|
||||
}
|
||||
} catch (e) {
|
||||
toast.error('测试失败: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTestSub2ApiService() {
|
||||
const apiUrl = document.getElementById('sub2api-service-url').value.trim();
|
||||
const apiKey = document.getElementById('sub2api-service-key').value.trim();
|
||||
|
||||
@@ -83,6 +83,53 @@
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
/* 自定义多选下拉 */
|
||||
.multi-select-dropdown {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.msd-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
background: var(--surface);
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.msd-trigger:hover { border-color: var(--primary-color); }
|
||||
.msd-arrow { font-size: 0.7rem; transition: transform 0.15s; }
|
||||
.msd-dropdown.open .msd-arrow { transform: rotate(180deg); }
|
||||
.msd-list {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
left: 0; right: 0;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
|
||||
z-index: 100;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.msd-dropdown.open .msd-list { display: block; }
|
||||
.msd-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.msd-item:hover { background: var(--surface-hover); }
|
||||
.msd-item input[type=checkbox] { margin: 0; cursor: pointer; }
|
||||
.msd-empty { padding: 8px 12px; color: var(--text-muted); font-size: 0.8rem; }
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 1024px) {
|
||||
.two-column-layout {
|
||||
@@ -231,8 +278,7 @@
|
||||
<span>上传到 CPA</span>
|
||||
</label>
|
||||
<div id="cpa-service-select-group" style="display:none; margin-top: 6px; margin-bottom: 8px; padding-left: 4px;">
|
||||
<label style="font-size:0.85rem; color:var(--text-muted); margin-bottom:4px; display:block;">选择 CPA 服务(可多选)</label>
|
||||
<div id="cpa-service-checkboxes" style="display:flex; flex-direction:column; gap:4px; max-height:120px; overflow-y:auto; border:1px solid var(--border); border-radius:6px; padding:6px;"></div>
|
||||
<div class="multi-select-dropdown" id="cpa-service-select"></div>
|
||||
</div>
|
||||
|
||||
<!-- Sub2API -->
|
||||
@@ -241,8 +287,7 @@
|
||||
<span>上传到 Sub2API</span>
|
||||
</label>
|
||||
<div id="sub2api-service-select-group" style="display:none; margin-top: 6px; margin-bottom: 8px; padding-left: 4px;">
|
||||
<label style="font-size:0.85rem; color:var(--text-muted); margin-bottom:4px; display:block;">选择 Sub2API 服务(可多选)</label>
|
||||
<div id="sub2api-service-checkboxes" style="display:flex; flex-direction:column; gap:4px; max-height:120px; overflow-y:auto; border:1px solid var(--border); border-radius:6px; padding:6px;"></div>
|
||||
<div class="multi-select-dropdown" id="sub2api-service-select"></div>
|
||||
</div>
|
||||
|
||||
<!-- Team Manager -->
|
||||
@@ -251,8 +296,7 @@
|
||||
<span>上传到 Team Manager</span>
|
||||
</label>
|
||||
<div id="tm-service-select-group" style="display:none; margin-top: 6px; padding-left: 4px;">
|
||||
<label style="font-size:0.85rem; color:var(--text-muted); margin-bottom:4px; display:block;">选择 TM 服务(可多选)</label>
|
||||
<div id="tm-service-checkboxes" style="display:flex; flex-direction:column; gap:4px; max-height:120px; overflow-y:auto; border:1px solid var(--border); border-radius:6px; padding:6px;"></div>
|
||||
<div class="multi-select-dropdown" id="tm-service-select"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -214,11 +214,11 @@
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th style="width:150px;">名称</th>
|
||||
<th>API URL</th>
|
||||
<th style="width:80px;">状态</th>
|
||||
<th style="width:60px;">优先级</th>
|
||||
<th style="width:160px;">操作</th>
|
||||
<th style="width:60px;text-align:center;">优先级</th>
|
||||
<th style="width:220px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="cpa-services-table">
|
||||
@@ -240,11 +240,11 @@
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th style="width:150px;">名称</th>
|
||||
<th>API URL</th>
|
||||
<th style="width:80px;">状态</th>
|
||||
<th style="width:60px;">优先级</th>
|
||||
<th style="width:160px;">操作</th>
|
||||
<th style="width:60px;text-align:center;">优先级</th>
|
||||
<th style="width:220px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sub2api-services-table">
|
||||
@@ -266,11 +266,11 @@
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th style="width:150px;">名称</th>
|
||||
<th>API URL</th>
|
||||
<th style="width:80px;">状态</th>
|
||||
<th style="width:60px;">优先级</th>
|
||||
<th style="width:160px;">操作</th>
|
||||
<th style="width:60px;text-align:center;">优先级</th>
|
||||
<th style="width:220px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tm-services-table">
|
||||
|
||||
Reference in New Issue
Block a user