feat(HEL-566): 去掉主站行情管理入口并修好模型列表收起

主站行情管理并入数据中枢
- 数据中枢数据源配置页新增「主站行情任务」卡:交易时段后台刷新开关、
  手动后台刷新、历史区间回补(任务提交 + 轮询主站 job 记录亮灯),
  数据与调度仍归主站,控制台只经桥接代操作
- 新增桥接端点 /api/hub-admin/market/{refresh,backfill};回补改为
  market.backfill 后台任务(jobs.config.json 注册,锁独立,超时 30 分钟),
  桥接调用秒回,不再阻塞
- 主站桌面端删除顶栏「行情管理」按钮与整个行情管理对话框,清理随之
  失效的 CSS;移动端删除 system/admin 页与入口,管理员专区只留数据中枢
- Tushare Token / iFinD 凭证编辑沿用数据源页既有凭证区,无功能缺失

模型池收起修复
- 拉出模型清单后按钮切换为「收起列表」,收起只留一行摘要;再次点击
  重新拉取并展开;勾选添加完成后清单自动收起(原逻辑保留)
- 交互全部沿用 HEL-558 已确认样式的既有按钮与提示组件,未新增视觉

自测
- verify_baseline 通过;pytest 492 项通过;数据中枢 240 项通过
- verify_datahub_console 新增 [11b] 行情任务桥接端到端段;UI 自测新增
  行情任务卡开关往返、模型清单展开/收起/再展开/添加自动收起,日夜主题
  与 1030 窄屏复验通过
- Playwright e2e 102/103:唯一失败项在基线提交上同样失败(本机字体度量
  导致的头部溢出,与本卡无关)

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
总工
2026-09-16 17:24:05 +08:00
co-authored by multica-agent
parent 44d6de3e2b
commit fa82b8621e
28 changed files with 563 additions and 551 deletions
-4
View File
@@ -13,10 +13,6 @@ async function initialize() {
document.querySelector("#journalDate").max = todayString();
document.querySelector("#tradeLogDate").value = elements.tradeDate.value;
document.querySelector("#tradeLogDate").max = todayString();
document.querySelector("#backfillStart").value = todayString();
document.querySelector("#backfillEnd").value = todayString();
document.querySelector("#backfillStart").max = todayString();
document.querySelector("#backfillEnd").max = todayString();
document.querySelector("#qiObservationDate").value = elements.tradeDate.value;
document.querySelector("#qiObservationDate").max = todayString();
document.querySelector("#accountBirthDate").max = todayString();
-30
View File
@@ -157,7 +157,6 @@
</div>
<button id="refreshButton" class="button command-button" type="button" title="刷新"><i data-lucide="refresh-cw"></i><span>刷新</span></button>
<button id="syncButton" class="button primary command-button" type="button" title="后台刷新" hidden><i data-lucide="cloud-download"></i><span>后台刷新</span></button>
<button id="marketAdminButton" class="button command-button" type="button" title="行情管理" hidden><i data-lucide="settings-2"></i><span>行情管理</span></button>
<button id="settingsButton" class="button command-button" type="button" title="数据中枢" hidden><i data-lucide="database"></i><span>数据中枢</span></button>
<div class="account-menu-shell">
<div id="accountRoleBadges" class="account-role-badges" aria-label="账号身份">
@@ -592,35 +591,6 @@
</section>
</dialog>
<dialog id="adminDialog" class="settings-dialog admin-dialog" aria-labelledby="adminDialogTitle">
<div class="dialog-header">
<div><span class="dialog-eyebrow">管理员</span><h2 id="adminDialogTitle">行情管理</h2></div>
<button id="closeAdminDialog" class="icon-button" type="button" aria-label="关闭" title="关闭"><i data-lucide="x"></i></button>
</div>
<div id="adminConnectionStatus" class="connection-status" aria-live="polite">正在读取系统状态</div>
<p class="form-hint">模型池、会员与邀请码已统一在数据中枢管理,点击顶栏「数据中枢」进入。</p>
<div class="admin-panel" data-admin-panel="market">
<form id="systemMarketForm" class="settings-section">
<div class="settings-section-heading"><h3>公共行情</h3><span id="systemDataStatus">待检查</span></div>
<label class="form-field"><span>行情来源凭据</span><input id="systemTokenInput" type="text" value="请在数据中枢后台统一管理" disabled></label>
<label class="form-field"><span>实时来源凭据</span><input id="systemIfindTokenInput" type="text" value="请在数据中枢后台统一管理" disabled></label>
<label class="switch-control"><input id="systemBackgroundRefresh" type="checkbox"><span>启用交易时段后台刷新</span></label>
<p class="form-hint">所有用户读取同一份后台快照,页面不会随后台任务自动重绘。</p>
<div id="datahubRouteStatus" class="admin-refresh-status" data-tone="idle" role="status" aria-live="polite"><i data-lucide="database"></i><span>数据中枢线路待检查</span></div>
<div id="adminRefreshStatus" class="admin-refresh-status" data-tone="idle" role="status" aria-live="polite"><i data-lucide="circle-dot"></i><span>尚未手动刷新</span></div>
<div class="dialog-actions admin-inline-actions"><button id="adminRefreshButton" class="button" type="button"><i data-lucide="refresh-cw"></i>立即后台刷新</button><button class="button primary" type="submit">保存刷新设置</button></div>
</form>
<section class="settings-section">
<div class="settings-section-heading"><h3>历史数据回补</h3><span>管理员任务</span></div>
<div class="backfill-controls">
<label class="form-field"><span>开始日期</span><input id="backfillStart" type="date"></label>
<label class="form-field"><span>结束日期</span><input id="backfillEnd" type="date"></label>
</div>
<div class="dialog-actions"><button id="backfillButton" class="button" type="button">开始回补</button></div>
</section>
</div>
</dialog>
<div id="loadingOverlay" class="loading-overlay" hidden>
<div class="loading-box">
<span class="spinner" aria-hidden="true"></span>
+1 -2
View File
@@ -51,8 +51,7 @@
items: [
{ key: "system/profile", label: "账号资料", icon: "user" },
{ key: "system/password", label: "修改密码", icon: "lock" },
{ key: "system/membership", label: "会员状态", icon: "gem" },
{ key: "system/admin", label: "行情管理", icon: "sliders-horizontal", adminOnly: true }
{ key: "system/membership", label: "会员状态", icon: "gem" }
]
}
},
-38
View File
@@ -3407,44 +3407,6 @@
line-height: 1.45;
}
.m-sys-status-list {
display: grid;
gap: 8px;
}
.m-sys-status-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: var(--font-size-body);
color: var(--text-primary);
}
.m-sys-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--text-tertiary);
}
.m-sys-dot--ok {
background: var(--market-down);
}
.m-sys-switch-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.m-sys-switch-row strong {
display: block;
font-size: var(--font-size-body);
margin-bottom: 0;
}
.m-btn-outline,
.m-btn-outline-danger {
width: 100%;
+6 -142
View File
@@ -3132,7 +3132,6 @@
"system/profile": setupSystemPage,
"system/password": setupSystemPage,
"system/membership": setupSystemPage,
"system/admin": setupSystemPage,
};
const COMPLEX_LOADERS = {
@@ -3154,7 +3153,6 @@
"system/profile": loadSystem,
"system/password": loadSystem,
"system/membership": loadSystem,
"system/admin": loadSystem,
};
function isComplexPage(key) {
@@ -4928,14 +4926,6 @@
return String(key || state.key || "").indexOf("system/") === 0;
}
function systemForbiddenHtml() {
return '<div class="m-state m-motion-rise-in" data-system-page="forbidden">' +
'<span class="m-state-icon">' + icon("lock", 26) + "</span>" +
"<p>仅管理员可访问</p>" +
"<small>行情管理与数据中枢需要管理员权限。</small>" +
"</div>";
}
function systemFill(html) {
const scroll = document.getElementById("m-scroll");
if (scroll) scroll.innerHTML = html;
@@ -4975,26 +4965,17 @@
function loadSystem() {
const key = state.key;
if (key === "system/admin" && !global.MobileSession.isAdmin()) {
systemFill(systemForbiddenHtml());
return;
}
if (key === "system/password") {
renderSystemPassword();
return;
}
const seq = nextSeq();
const url = key === "system/admin" ? "/api/admin/settings" : "/api/account/status";
const url = "/api/account/status";
return global.MobileAPI.request(url).then(function (payload) {
if (seq !== state.seq || state.key !== key) return;
if (key === "system/admin") {
state.system.admin = payload || {};
renderSystemAdmin();
} else {
state.system.account = payload || {};
if (key === "system/profile") renderSystemProfile();
else renderSystemMembership();
}
state.system.account = payload || {};
if (key === "system/profile") renderSystemProfile();
else renderSystemMembership();
}).catch(function (error) {
if (seq !== state.seq || state.key !== key) return;
systemFill(errorHtml(error && error.message ? error.message : "加载失败"));
@@ -5078,9 +5059,6 @@
{ key: "system/password", icon: "lock", label: "修改密码", hint: "建议定期更换" },
{ key: "system/membership", icon: "gem", label: "会员状态", hint: "有效期与智能分析额度" }
];
const adminRows = [
{ key: "system/admin", icon: "sliders-horizontal", label: "行情管理", hint: "数据源状态 · 后台刷新 · 历史回补" }
];
const html =
'<div class="m-sys-home" data-system-page="home">' +
'<div class="m-card m-sys-profile-card"><span class="m-sys-avatar">' + escapeHtml(avatar) + "</span><div>" +
@@ -5093,7 +5071,7 @@
'<h3 class="m-sys-group-title">偏好</h3>' +
'<div class="m-card m-sys-list">' + systemThemeRowHtml() + "</div>" +
(global.MobileSession.isAdmin()
? '<h3 class="m-sys-group-title">管理员专区</h3><div class="m-card m-sys-list">' + adminRows.map(systemRowHtml).join("") + dataHubRowHtml() + "</div>"
? '<h3 class="m-sys-group-title">管理员专区</h3><div class="m-card m-sys-list">' + dataHubRowHtml() + "</div>"
: "") +
'<h3 class="m-sys-group-title">其他</h3>' +
'<div class="m-card m-sys-list">' +
@@ -5106,7 +5084,7 @@
'<span class="m-sys-row-body"><strong>退出登录</strong><small>退出后需要重新登录</small></span>' +
'<span class="m-sys-row-chevron">' + icon("chevron-right", 16) + "</span></button>" +
"</div>" +
'<p class="m-sys-foot">' + (global.MobileSession.isAdmin() ? "小白复盘 · 内网个人版" : "行情管理与数据中枢仅管理员可见") + "</p>" +
'<p class="m-sys-foot">' + (global.MobileSession.isAdmin() ? "小白复盘 · 内网个人版" : "数据中枢仅管理员可见") + "</p>" +
"</div>";
document.getElementById("m-view").innerHTML = html;
}
@@ -5196,55 +5174,6 @@
systemFill(html);
}
function statusDot(ok) {
return '<span class="m-sys-dot' + (ok ? " m-sys-dot--ok" : "") + '"></span>';
}
function datahubStatusText(hub) {
const enabled = number(hub.enabled_reads);
const total = number(hub.total_reads) || enabled;
const fallbacks = hub.fallback_labels || [];
if (fallbacks.length) return " 备用 " + fallbacks.join("、");
if (hub.configured) return " 主线路 " + enabled + "/" + total;
return " 未配置";
}
function renderSystemAdmin() {
const payload = state.system.admin || {};
const data = payload.data || {};
const ifind = data.ifind || {};
const marketHtml =
'<div class="m-sys-body" data-system-admin-panel="market">' +
'<div class="m-card m-sys-section"><strong>数据源状态</strong>' +
'<div class="m-sys-status-list">' +
'<div class="m-sys-status-item"><span>数据中枢</span><span>' + statusDot(data.configured) + (data.configured ? " 已连接" : " 未连接") + "</span></div>" +
'<div class="m-sys-status-item"><span>iFinD</span><span>' + statusDot(ifind.configured) + (ifind.configured ? " 已配置" : " 未配置") + "</span></div>" +
'<div class="m-sys-status-item"><span>行情快照</span><strong>' + number(data.snapshot_dates) + " 个交易日</strong></div>" +
'<div class="m-sys-status-item"><span>后台刷新</span><span>' + statusDot(data.background_refresh_enabled) + (data.background_refresh_enabled ? " 已启用" : " 已暂停") + "</span></div>" +
'<div class="m-sys-status-item"><span>数据中枢</span><span>' + statusDot(Boolean((data.datahub || {}).configured) && !((data.datahub || {}).fallback_count)) + datahubStatusText(data.datahub || {}) + "</span></div>" +
"</div></div>" +
'<div class="m-card m-sys-section"><strong>数据源密钥</strong>' +
formFieldHtml("行情来源凭据", '<input id="m-sys-token" type="text" value="请在数据中枢后台统一管理" disabled>', false) +
formFieldHtml("实时来源凭据", '<input id="m-sys-ifind" type="text" value="请在数据中枢后台统一管理" disabled>', false) +
'<button class="m-btn-primary" type="button" data-system-save-market>刷新状态</button></div>' +
'<div class="m-card m-sys-section"><strong>后台刷新</strong>' +
'<div class="m-sys-switch-row"><div><strong>交易时段自动刷新</strong><p class="m-sys-hint">开启后后台定时更新快照</p></div>' +
'<button class="m-theme-switch" type="button" data-system-toggle-refresh role="switch" aria-checked="' + (data.background_refresh_enabled ? "true" : "false") + '" aria-label="交易时段自动刷新"><span class="m-theme-switch-thumb"></span></button></div>' +
'<input id="m-sys-bg-refresh" type="checkbox"' + (data.background_refresh_enabled ? " checked" : "") + ' hidden>' +
'<button class="m-btn-outline" type="button" data-system-refresh>立即刷新一次</button>' +
'<p class="m-sys-hint">所有用户读取同一份快照,刷新不影响当前页面内容。</p></div>' +
'<div class="m-card m-sys-section"><strong>历史数据回补</strong>' +
formFieldHtml("开始日期", dateInputHtml("m-sys-backfill-start", ""), false) +
formFieldHtml("结束日期", dateInputHtml("m-sys-backfill-end", ""), false) +
'<button class="m-btn-outline" type="button" data-system-backfill>开始回补</button>' +
'<p class="m-sys-hint">回补用于补齐缺失的历史行情,开始前会再次确认;回补期间页面可正常使用。</p></div>' +
'<div class="m-card m-sys-section"><strong>模型池 · 会员 · 邀请码</strong>' +
'<p class="m-sys-hint">这些配置已统一在数据中枢管理。</p>' +
'<button class="m-btn-outline" type="button" data-system-datahub>打开数据中枢</button></div>' +
"</div>";
systemFill(marketHtml);
}
function dataHubConsoleUrl() {
if (global.XIAOBAI_DATAHUB_URL) return String(global.XIAOBAI_DATAHUB_URL);
// HEL-564: 固定内网地址,见 frontend/shared/admin.js 同名函数。
@@ -5373,67 +5302,6 @@
});
}
function saveSystemMarket() {
const button = document.querySelector("[data-system-save-market]");
if (button) button.disabled = true;
loadSystem().then(function () {
showToast("数据中枢状态已刷新");
}).catch(function (error) {
showToast(error && error.message ? error.message : "数据中枢状态读取失败");
}).then(function () {
if (button) button.disabled = false;
});
}
function toggleSystemRefresh() {
const enabled = !Boolean((state.system.admin && state.system.admin.data && state.system.admin.data.background_refresh_enabled));
global.MobileAPI.request("/api/admin/settings", "POST", {
background_refresh_enabled: enabled,
}).then(function () {
showToast(enabled ? "后台刷新已启用" : "后台刷新已暂停");
loadSystem();
}).catch(function (error) {
showToast(error && error.message ? error.message : "后台刷新设置失败");
});
}
function startSystemRefresh() {
const button = document.querySelector("[data-system-refresh]");
if (button) button.disabled = true;
global.MobileAPI.request("/api/admin/refresh", "POST", { trade_date: todayString() }).then(function (payload) {
showToast((payload && payload.message) || "后台刷新已提交");
}).catch(function (error) {
showToast(error && error.message ? error.message : "后台刷新启动失败");
}).then(function () {
if (button) button.disabled = false;
});
}
function startSystemBackfill() {
const startDate = (document.getElementById("m-sys-backfill-start") || {}).value;
const endDate = (document.getElementById("m-sys-backfill-end") || {}).value;
openConfirmSheet("开始历史回补?", "将按选定日期补齐缺失行情,回补期间页面仍可使用。", {
confirmLabel: "开始回补",
centered: true,
onConfirm: function () {
const button = document.querySelector("[data-system-backfill]");
if (button) button.disabled = true;
global.MobileAPI.request("/api/backfill", "POST", {
start_date: startDate,
end_date: endDate,
}).then(function (payload) {
const count = payload && payload.results ? payload.results.length : 0;
showToast("历史回补完成,共处理 " + count + " 个工作日");
loadSystem();
}).catch(function (error) {
showToast(error && error.message ? error.message : "回补失败");
}).then(function () {
if (button) button.disabled = false;
});
}
});
}
function bindEvents() {
window.addEventListener("hashchange", closeSheet);
@@ -5566,10 +5434,6 @@
if (event.target.closest("[data-system-save-password]")) { saveSystemPassword(); return; }
if (event.target.closest("[data-system-switch]")) { switchSystemAccount(); return; }
if (event.target.closest("[data-system-logout]")) { logoutSystemAccount(); return; }
if (event.target.closest("[data-system-save-market]")) { saveSystemMarket(); return; }
if (event.target.closest("[data-system-toggle-refresh]")) { toggleSystemRefresh(); return; }
if (event.target.closest("[data-system-refresh]")) { startSystemRefresh(); return; }
if (event.target.closest("[data-system-backfill]")) { startSystemBackfill(); return; }
if (event.target.closest("[data-system-datahub]")) { openDataHubConsole(); return; }
// 我的复盘:新增/编辑/删除/筛选/提交等操作
+3 -94
View File
@@ -1,54 +1,3 @@
async function backfillData() {
const button = document.querySelector("#backfillButton");
button.disabled = true;
setLoading(true, "正在回补历史交易日");
try {
const payload = await apiRequest("/api/backfill", "POST", {
start_date: document.querySelector("#backfillStart").value,
end_date: document.querySelector("#backfillEnd").value,
});
const failed = (payload.failed_count || 0);
const skipped = (payload.skipped_non_trading_days || []).length;
const suffix = failed
? `,失败 ${failed}`
: skipped
? `,跳过 ${skipped} 个非交易日`
: "";
showToast(`历史回补完成,共处理 ${payload.results.length} 个交易日${suffix}`);
state.sentimentHistory = null;
state.sentimentHistoryKey = "";
if (state.activeView === "sentimentCycleView") {
await loadSentimentHistory(true);
}
await openAdminSettings(true);
} catch (error) {
showToast(error.message);
} finally {
setLoading(false);
button.disabled = false;
}
}
async function openAdminSettings(refreshOnly = false) {
if (state.user?.role !== "admin") return;
if (!refreshOnly) openModalDialog(elements.adminDialog);
const status = document.querySelector("#adminConnectionStatus");
status.textContent = "正在读取系统状态";
try {
const payload = await apiRequest("/api/admin/settings");
const data = payload.data || {};
const ifind = data.ifind || {};
status.textContent = `数据中枢 ${data.configured ? "已连接" : "未连接"} · iFinD ${ifind.configured ? "已配置" : "未配置"} · ${number(data.snapshot_dates)} 个交易日`;
status.classList.toggle("connected", Boolean(data.configured));
setText("systemDataStatus", data.background_refresh_enabled ? "后台刷新已启用" : "后台刷新已暂停");
renderDatahubRouteStatus(data.datahub || {});
document.querySelector("#systemBackgroundRefresh").checked = Boolean(data.background_refresh_enabled);
} catch (error) {
status.textContent = error.message || "系统配置读取失败";
}
}
function dataHubConsoleUrl() {
if (window.XIAOBAI_DATAHUB_URL) return String(window.XIAOBAI_DATAHUB_URL);
// HEL-564: 固定内网地址。域名只反代 8765,按主机名推导会拼出打不开的
@@ -61,49 +10,9 @@ function openDataHubConsole() {
window.open(dataHubConsoleUrl(), "_blank", "noopener");
}
function renderDatahubRouteStatus(hub) {
const box = document.querySelector("#datahubRouteStatus");
if (!box) return;
const label = box.querySelector("span");
const enabled = number(hub.enabled_reads);
const total = number(hub.total_reads) || enabled;
const fallbacks = hub.fallback_labels || [];
if (fallbacks.length) {
box.dataset.tone = "warning";
if (label) label.textContent = `数据中枢主线路 ${enabled}/${total} · 备用 ${fallbacks.length} 类:${fallbacks.join("、")}`;
return;
}
box.dataset.tone = hub.configured ? "success" : "idle";
if (label) {
label.textContent = hub.configured
? `数据中枢主线路 ${enabled}/${total},当前无备用`
: "数据中枢未配置,网站只保留已有真实快照";
}
}
async function saveMarketSettings(event) {
event.preventDefault();
const button = event.currentTarget.querySelector("button[type='submit']");
button.disabled = true;
try {
await apiRequest("/api/admin/settings", "POST", {
background_refresh_enabled: document.querySelector("#systemBackgroundRefresh").checked,
});
showToast("行情刷新设置已保存");
await openAdminSettings(true);
} catch (error) {
showToast(error.message || "系统配置保存失败");
} finally {
button.disabled = false;
}
}
/* HEL-566: 主站「行情管理」已整体并入数据中枢(数据源配置页的
主站行情任务卡:后台刷新开关 / 手动刷新 / 历史回补),主站只保留
顶栏「数据中枢」入口。 */
function bindAdminEvents() {
document.querySelector("#settingsButton").addEventListener("click", openDataHubConsole);
document.querySelector("#marketAdminButton").addEventListener("click", () => openAdminSettings());
document.querySelector("#closeAdminDialog").addEventListener("click", () => elements.adminDialog.close());
document.querySelector("#backfillButton").addEventListener("click", backfillData);
document.querySelector("#systemMarketForm").addEventListener("submit", saveMarketSettings);
document.querySelector("#adminRefreshButton").addEventListener("click", startAdminRefresh);
}
+2 -109
View File
@@ -121,23 +121,6 @@
line-height: 1.5;
}
.admin-dialog {
width: min(1060px, -24px + 100vw);
}
.admin-panel[hidden] {
display: none;
}
.admin-inline-actions {
display: flex;
justify-content: space-between;
gap: 10px;
}
.admin-dialog > form,
.settings-section > form {
padding: 0px;
}
@@ -160,22 +143,6 @@
font-size: 12px;
}
.switch-control {
display: flex;
align-items: center;
gap: 9px;
margin-top: 13px;
color: var(--text-secondary);
font-size: 13px;
font-weight: 650;
}
.connection-status {
margin: 18px 18px 0px;
@@ -200,8 +167,7 @@
padding: 18px;
}
.settings-dialog .settings-section > form,
.settings-dialog.admin-dialog > form {
.settings-dialog .settings-section > form {
padding: 0px;
}
@@ -245,22 +211,6 @@
grid-column: 1 / -1;
}
.backfill-controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
@media (max-width: 600px) {
.admin-inline-actions {
align-items: stretch;
flex-direction: column;
}
}
.form-hint {
margin: 10px 0px 0px;
@@ -271,32 +221,6 @@
line-height: 1.6;
}
.admin-refresh-status {
display: flex;
align-items: flex-start;
gap: 8px;
margin-top: 12px;
padding: 10px 12px;
border: 1px solid var(--line);
border-radius: 10px;
background: var(--surface-muted);
color: var(--text-muted);
font-size: 12px;
line-height: 1.55;
}
.admin-refresh-status svg {
flex: 0 0 auto;
width: 15px;
height: 15px;
margin-top: 2px;
}
.admin-refresh-status[data-tone="running"] { color: var(--primary); }
.admin-refresh-status[data-tone="success"] { color: var(--down); }
.admin-refresh-status[data-tone="warning"] { color: var(--warning); }
.admin-refresh-status[data-tone="failure"] { color: var(--up); }
.account-button > span {
flex: 0 0 auto;
@@ -359,8 +283,7 @@
padding-left: 12px;
}
.settings-dialog .settings-section > form,
.settings-dialog.admin-dialog > form {
.settings-dialog .settings-section > form {
padding: 0px;
}
@@ -368,10 +291,6 @@
.settings-dialog .form-hint {
overflow-wrap: anywhere;
}
.backfill-controls {
grid-template-columns: 1fr;
}
}
.account-menu-chevron {
@@ -1297,32 +1216,6 @@ button.account-role-badge:focus-visible {
max-width: 460px;
}
.settings-dialog.admin-dialog {
width: min(900px, -28px + 100vw);
}
.admin-dialog .connection-status {
margin: 12px 20px 0px;
padding: 8px 10px;
border-radius: 7px;
color: var(--text-secondary);
font-size: 11px;
}
.admin-dialog .admin-panel {
max-width: 860px;
margin-inline: auto;
}
.admin-dialog .settings-section + .settings-section {
border-top: 1px solid var(--dialog-line);
}
@media (max-width: 760px) {
.account-settings-dialog .membership-status-grid {
grid-template-columns: repeat(2, minmax(0px, 1fr));
-1
View File
@@ -212,7 +212,6 @@ const elements = {
entityDetailDialog: document.querySelector("#entityDetailDialog"),
entityDetailChart: document.querySelector("#entityDetailChart"),
settingsDialog: document.querySelector("#settingsDialog"),
adminDialog: document.querySelector("#adminDialog"),
priceChart: document.querySelector("#priceChart"),
stockPreview: document.querySelector("#stockPreview"),
stockPreviewBackdrop: document.querySelector("#stockPreviewBackdrop"),
+2 -19
View File
@@ -38,22 +38,18 @@ async function loadDashboard(force = false, background = false, showOverlay = tr
}
async function startAdminRefresh() {
const buttons = [document.querySelector("#syncButton"), document.querySelector("#adminRefreshButton")].filter(Boolean);
const buttons = [document.querySelector("#syncButton")].filter(Boolean);
buttons.forEach((button) => { button.disabled = true; });
const requestedDate = elements.tradeDate.value;
setAdminRefreshStatus("running", `正在刷新 ${requestedDate} 的行情,请稍候…`, "loader-circle");
try {
const payload = await apiRequest("/api/admin/refresh", "POST", { trade_date: requestedDate });
if (!payload.started || !payload.job_key) {
setAdminRefreshStatus("warning", "已有刷新任务正在运行,请稍后再试。", "clock-3");
showToast(payload.message || "已有后台刷新任务正在运行");
return;
}
setStatus(`正在刷新 ${requestedDate} 的行情`);
const job = await waitForAdminRefresh(payload.job_key);
if (job.status === "failed") {
const reason = job.message || job.error_code || "数据源未返回结果";
setAdminRefreshStatus("failure", `刷新失败:${reason}`, "circle-x");
setStatus("后台刷新失败");
showToast("后台刷新失败");
return;
@@ -68,25 +64,20 @@ async function startAdminRefresh() {
const updated = formatTimestamp(meta.updated_at);
const freshness = dashboardFreshnessMessage(meta);
if (meta.realtime && actualCompact === requestedCompact && !meta.carried_forward) {
setAdminRefreshStatus("success", `刷新成功:已获取 ${actualDate} 的盘中行情,更新时间 ${updated}`, "circle-check");
showToast(`刷新成功:已获取 ${actualDate} 的盘中行情`);
return;
}
if (freshness || actualCompact !== requestedCompact || meta.carried_forward || meta.limit_data_source === "derived") {
setAdminRefreshStatus("warning", freshness || `部分正式数据尚未到齐,当前展示 ${actualDate || "最近可用数据"}`, "triangle-alert");
setStatus(freshness || "部分正式数据尚未到齐,当前展示最近可用数据");
return;
}
if (meta.notice) {
setAdminRefreshStatus("warning", `已刷新到 ${actualDate}${updated}),但数据源提示:${meta.notice}`, "triangle-alert");
showToast(`已刷新到 ${actualDate},请留意数据源提示`);
} else {
setAdminRefreshStatus("success", `刷新成功:已获取 ${actualDate} 的最新行情,更新时间 ${updated}`, "circle-check");
showToast(`刷新成功:已获取 ${actualDate} 的最新行情`);
showToast(`刷新成功:已获取 ${actualDate} 的最新行情,更新时间 ${updated}`);
}
} catch (error) {
const message = error.message || "后台刷新失败";
setAdminRefreshStatus("failure", `刷新失败:${message}`, "circle-x");
setStatus("后台刷新失败");
showToast(message);
} finally {
@@ -94,14 +85,6 @@ async function startAdminRefresh() {
}
}
function setAdminRefreshStatus(tone, message, icon = "circle-dot") {
const status = document.querySelector("#adminRefreshStatus");
if (!status) return;
status.dataset.tone = tone;
status.innerHTML = `<i data-lucide="${icon}"></i><span>${escapeHtml(message)}</span>`;
refreshIcons();
}
async function waitForAdminRefresh(jobKey) {
for (let attempt = 0; attempt < 120; attempt += 1) {
const payload = await apiRequest("/api/admin/settings");
-1
View File
@@ -48,7 +48,6 @@ async function applyAuthenticatedSession(session) {
const isAdmin = session.user?.role === "admin";
updateAccountIdentityBadges(session.user?.membership || {});
document.querySelector("#settingsButton").hidden = !isAdmin;
document.querySelector("#marketAdminButton").hidden = !isAdmin;
document.querySelector("#syncButton").hidden = !isAdmin;
document.querySelector("#reasonForm").hidden = !isAdmin;
document.querySelector("#sectorPhaseManager").hidden = !isAdmin;