diff --git a/app/web/static/index.html b/app/web/static/index.html
index c3e3921..82b8ebe 100644
--- a/app/web/static/index.html
+++ b/app/web/static/index.html
@@ -113,7 +113,7 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
PMS 持仓管理v{{ health.version || '-' }} · {{ health.now || '' }}
- {{ health.trade_day ? '交易日':'非交易日' }}
+ {{ marketState }}
买入 {{ ov.buy_halt ? '已暂停':'正常' }}
执行 {{ ov.exec_halt ? '已暂停':'正常' }}
自主 {{ tx('autonomy', ov.autonomy) }}?
@@ -127,6 +127,8 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
+ {{ autoRefresh ? '自动' : '手动' }} · {{ lastRefresh }}
@@ -139,9 +141,12 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
-
+
+
(positions.value || []).filter(p => (p.total_qty || 0) > 0));
// ---- 今日在办分档 + 候选处置 (2026-08-12 重构新增) ----
const todayYmd = computed(() => (String(health.value.now || '').slice(0, 10).replace(/-/g, '')) || '');
+ // 是否处于连续交易时段 (09:30-11:30 / 13:00-15:00 的交易日)。缺实时价在盘中才是问题,
+ // 休市/非交易日本就没有实时行情, 不该报警 —— 2026-08-12 用户提。
+ const marketOpen = computed(() => {
+ if (!health.value.trade_day) return false;
+ const t = (health.value.now || '').slice(11, 16);
+ if (!t) return false;
+ return (t >= '09:30' && t <= '11:30') || (t >= '13:00' && t <= '15:00');
+ });
+ const marketState = computed(() =>
+ !health.value.trade_day ? '非交易日' : (marketOpen.value ? '盘中' : '休市'));
const _LIVE_INS = ['PROPOSED', 'RULE_PASSED', 'JUDGE_PASSED', 'DISPATCHED'];
const _sameDayIns = r => { const y = todayYmd.value;
return !y || String(r.instruction_id || '').indexOf('INS_' + y + '_') === 0; };
@@ -1448,8 +1465,8 @@ createApp({
if (o.buy_halt) out.push('已暂停买入(卖出与止损不受影响)');
if (o.exec_halt) out.push('休假模式:所有自动动作暂停,仅保留对账与日报');
if (o.brake_until) out.push('组合刹车中(解除日 ' + o.brake_until + ')');
- if ((o.price_missing || []).length)
- out.push(o.price_missing.length + ' 只取不到实时价(停牌等),其盈亏与安全垫暂不可用');
+ if (marketOpen.value && (o.price_missing || []).length)
+ out.push(o.price_missing.length + ' 只取不到实时价(停牌或行情中断),其盈亏与安全垫暂不可用');
if (o.sector_ready === false) out.push('行业数据未就绪,行业类操作暂不可用');
const j = (dm.value || {}).judge;
if (j && j.available === false) out.push('决策系统暂不可用:补足/加仓/补仓一律降级为需你确认');
@@ -1703,6 +1720,7 @@ createApp({
loadPositions(), loadInstructions(), loadLedger(), loadProposals(),
loadDispatchMode(), loadWs(), loadStrategies(), loadOpLog(), loadOpenScan()]);
await loadNames();
+ lastRefresh.value = (health.value.now || '').slice(11, 19);
loading.value = false;
wsPollSync();
}
@@ -1896,7 +1914,24 @@ createApp({
reportDrawer.value = true;
}
- onMounted(() => { loadAll(); loadPlan(); }); // 计划状态并行拉, 不阻塞首屏
+ // —— 自动刷新: 盘中 30s、休市 5min; 只拉快变数据, 不动 params(免得冲掉设置里在改的输入) ——
+ let refreshTimer = null, refreshTick = 0;
+ async function refreshLive() {
+ await Promise.all([loadOverview(), loadPositions(), loadInstructions(), loadLedger(),
+ loadProposals(), loadStrategies(), loadOpLog(), loadOpenScan()]);
+ await loadNames();
+ lastRefresh.value = (health.value.now || '').slice(11, 19);
+ }
+ function startAutoRefresh() {
+ if (refreshTimer) return;
+ refreshTimer = setInterval(() => {
+ if (!autoRefresh.value) return;
+ refreshTick++;
+ if (marketOpen.value || refreshTick % 10 === 0) refreshLive();
+ }, 30000);
+ }
+ onUnmounted(() => { if (refreshTimer) clearInterval(refreshTimer); });
+ onMounted(() => { loadAll(); loadPlan(); startAutoRefresh(); }); // 首屏 + 启动自动刷新
return { tab, loading, err, health, ov, params, catalog, commands, plans, plansOf,
positions, lots, lotsOf, instructions, ledger, proposals, report, reportDrawer,
opsDrawer, opsResult, opsLoading, issuing, form, curSpec, dirtyCount, dm,
@@ -1918,7 +1953,7 @@ createApp({
openStrategy, validateStrategy, attachStrategy, setStrategyStatus, stratStateText, resumeBuy,
showLedger, onPosExpand,
openScan, insTab, todayYmd, insLive, insDone, insEnd, dispOf, loadOpenScan, denyToday,
- showOpLog, showSettings, watchCandidates };
+ showOpLog, showSettings, watchCandidates, marketOpen, marketState, lastRefresh, autoRefresh };
}
}).use(ElementPlus).mount('#app');