feat(ui): 全局弹窗替代原生 alert/confirm,添加防重复提交与搜索功能

- 新增 ConfirmModal / ResultModal 全局组件,替代原生 alert/confirm/prompt
- 资产/平台/账号保存添加防重复提交锁(assetSaving / providerSaving)
- 添加 Esc 键盘快捷键关闭模态框
- 平台/监控视图添加搜索过滤功能
- 修复监控中心 SSL TAB 下新增按钮的显示逻辑
- 调整 toast 位置避免遮挡底部导航
- 统一各弹窗 z-index 层级
This commit is contained in:
gouki
2026-10-17 21:17:18 +00:00
parent a63fcb786f
commit eeb05372c4
7 changed files with 167 additions and 48 deletions
+9 -9
View File
@@ -141,26 +141,26 @@ const AssetsView = {
return p ? p.name : slug;
}
async function refreshBalance(a) {
if (!confirm('刷新「' + a.name + '」的余额?')) return;
if (!await showConfirm('刷新余额', '刷新「' + a.name + '」的余额?')) return;
try {
const r = await Api.post('/assets/' + a.id + '/refresh-balance', {});
alert('余额已更新:' + r.balance + ' ' + r.currency);
showResult('刷新成功', '余额已更新:' + r.balance + ' ' + r.currency);
await loadAssets();
} catch (e) { alert('刷新失败:' + e.message); }
} catch (e) { showResult('刷新失败', e.message, true); }
}
async function syncAllAI() {
if (!confirm('同步所有 AI 账号的余额?')) return;
if (!await showConfirm('同步 AI 余额', '同步所有 AI 账号的余额?')) return;
try {
const r = await Api.post('/sync/ai-balances', {});
// skipped:平台本身没余额接口(如 OpenAI 已下线 billing),不属于失败
alert('同步完成:成功 ' + r.success + ',失败 ' + r.failed +
showResult('同步完成', '成功 ' + r.success + ',失败 ' + r.failed +
(r.skipped ? ',跳过 ' + r.skipped + '(平台无余额接口)' : '') +
(r.errors && r.errors.length ? '\n' + r.errors.join('\n') : ''));
await loadAssets();
} catch (e) { alert('同步失败:' + e.message); }
} catch (e) { showResult('同步失败', e.message, true); }
}
async function syncAllAssets() {
if (!confirm('从云端拉取所有已配 API 凭证账号下的 VPS 与域名?\n\n会按 (平台, 云端实例ID) 去重:已存在则更新状态/到期日,不存在则新建。')) return;
if (!await showConfirm('同步云端资产', '从云端拉取所有已配 API 凭证账号下的 VPS 与域名?\n\n会按 (平台, 云端实例ID) 去重:已存在则更新状态/到期日,不存在则新建。')) return;
try {
const r = await Api.post('/sync/assets', {});
const lines = [
@@ -172,9 +172,9 @@ const AssetsView = {
if (r.skipped_no_config) lines.push('跳过(账号未配 API 凭证):' + r.skipped_no_config);
if (r.skipped_no_adapter) lines.push('跳过(平台无可用适配器):' + r.skipped_no_adapter);
if (r.errors && r.errors.length) lines.push('\n' + r.errors.join('\n'));
alert(lines.join('\n'));
showResult('同步完成', lines.join('\n'));
await Promise.all([loadAssets(), loadOverview()]);
} catch (e) { alert('同步失败:' + e.message); }
} catch (e) { showResult('同步失败', e.message, true); }
}
return { store, Fmt, tabs, tabView, list, switchTab, providerName, reload: loadAssets, clearProvider: clearProviderFilter, edit: openAssetEdit, del: deleteAsset, viewServer, refreshBalance, syncAllAI, syncAllAssets, canControl: assetCanControl, openAct: openActions };
},