diff --git a/manual_trading_hub/hub_ai/context.py b/manual_trading_hub/hub_ai/context.py index 1ddd343..67f4763 100644 --- a/manual_trading_hub/hub_ai/context.py +++ b/manual_trading_hub/hub_ai/context.py @@ -652,41 +652,32 @@ def format_account_remark(ac: dict) -> str: return ";".join(parts) -def format_dashboard_account_lines(ac: dict) -> list[dict[str, Any]]: - """数据看板分户卡片:监控与持仓逐行展示(含浮盈亏数值供前端着色)。""" - lines: list[dict[str, Any]] = [] +def format_dashboard_account_detail(ac: dict) -> dict[str, Any]: + """数据看板分户卡片:监控仅数量,持仓逐行(含浮盈亏)。""" mon = ac.get("monitor_lines") or {} - for row in (mon.get("keys") or [])[:3]: - if row: - lines.append({"kind": "monitor", "text": str(row)}) - for row in (mon.get("orders") or [])[:3]: - if row: - lines.append({"kind": "monitor", "text": str(row)}) - for row in (mon.get("trends") or [])[:2]: - if row: - lines.append({"kind": "monitor", "text": str(row)}) - for row in (mon.get("rolls") or [])[:2]: - if row: - lines.append({"kind": "monitor", "text": str(row)}) + position_lines: list[dict[str, Any]] = [] for p in _filter_open_positions(ac.get("positions") or []): sym = p.get("symbol") or "?" side = p.get("side") or "?" upnl = _position_float_pnl(p) - lines.append( + position_lines.append( { "kind": "position", "text": f"{sym} {side}", "pnl": round(upnl, 4), } ) - if not lines: - issues = ac.get("issues") or [] - if issues: - for iss in issues[:3]: - lines.append({"kind": "issue", "text": str(iss)}) - else: - lines.append({"kind": "empty", "text": "无"}) - return lines + issues = [str(x) for x in (ac.get("issues") or [])[:3]] + return { + "monitor_counts": { + "keys": len(mon.get("keys") or []), + "orders": len(mon.get("orders") or []), + "trends": len(mon.get("trends") or []), + "rolls": len(mon.get("rolls") or []), + }, + "position_lines": position_lines, + "issues": issues, + } def collect_closed_trades_snapshot( diff --git a/manual_trading_hub/hub_dashboard.py b/manual_trading_hub/hub_dashboard.py index 638dba7..b6fe97f 100644 --- a/manual_trading_hub/hub_dashboard.py +++ b/manual_trading_hub/hub_dashboard.py @@ -8,7 +8,7 @@ from hub_ai.context import ( build_daily_context, collect_closed_trades_snapshot, format_account_remark, - format_dashboard_account_lines, + format_dashboard_account_detail, ) from hub_ai.config import trading_day_reset_hour from hub_trades_lib import current_trading_day @@ -64,7 +64,7 @@ def _enrich_account_row(ac: dict) -> dict: "float_pnl_u": ac.get("float_pnl_u"), "open_position_count": ac.get("open_position_count"), "remark": format_account_remark(ac), - "remark_lines": format_dashboard_account_lines(ac), + **format_dashboard_account_detail(ac), "issues": ac.get("issues") or [], "daily_loss_pct": loss_pct, "loss_alert": loss_alert, diff --git a/manual_trading_hub/static/app.css b/manual_trading_hub/static/app.css index e892744..923844a 100644 --- a/manual_trading_hub/static/app.css +++ b/manual_trading_hub/static/app.css @@ -3821,6 +3821,8 @@ html[data-theme="light"] button.danger { /* --- Hub AI 教练(整页一屏,内容区内滚动)--- */ body.hub-page-ai { overflow: hidden; + height: 100dvh; + max-height: 100dvh; } body.hub-page-ai .app-shell { padding-bottom: 12px; @@ -3831,6 +3833,13 @@ body.hub-page-ai .app-shell { flex-direction: column; box-sizing: border-box; } +body.hub-page-ai .app-shell > #page-ai { + flex: 1 1 auto; + min-height: 0; + display: flex; + flex-direction: column; + overflow: hidden; +} body.hub-page-ai .app-header { flex-shrink: 0; margin-bottom: 4px; @@ -3876,6 +3885,11 @@ body.hub-page-ai #page-ai { /* 手机 AI:须在 .ai-layout 双列定义之后,避免被覆盖成半屏 */ @media (max-width: 720px) { + html:has(body.hub-page-ai) { + height: 100%; + overflow: hidden; + } + body.hub-page-ai .app-shell { padding-bottom: max(8px, env(safe-area-inset-bottom)); height: var(--hub-vvh, 100dvh); @@ -3888,8 +3902,24 @@ body.hub-page-ai #page-ai { } body.hub-page-ai { + position: fixed; + inset: 0; + width: 100%; overflow: hidden; background: var(--bg); + overscroll-behavior: none; + } + + body.hub-page-ai .app-header { + padding: 6px 0; + margin-bottom: 2px; + gap: 8px; + } + + body.hub-page-ai .top-nav a { + min-height: 34px; + padding: 6px 10px; + font-size: 11px; } body.hub-page-ai.hub-ai-keyboard-open .app-header, @@ -4062,8 +4092,19 @@ body.hub-page-ai #page-ai { body.hub-page-ai .ai-chat-messages { flex: 1 1 auto; min-height: 0; + max-height: none; padding: 4px 2px 8px; + overflow-x: hidden; overflow-y: auto; + overscroll-behavior: contain; + -webkit-overflow-scrolling: touch; + } + + body.hub-page-ai .ai-layout[data-ai-mobile-tab="history"] .ai-chat-history-list { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; -webkit-overflow-scrolling: touch; } diff --git a/manual_trading_hub/static/app.js b/manual_trading_hub/static/app.js index 0ef0471..6af4cc9 100644 --- a/manual_trading_hub/static/app.js +++ b/manual_trading_hub/static/app.js @@ -3618,6 +3618,22 @@ window.addEventListener("popstate", setActiveNav); } + window.hubOpenMonitorExpand = function hubOpenMonitorExpand(exId) { + const id = String(exId || "").trim(); + if (!id) return; + expandedExchangeId = id; + sessionStorage.setItem("hub_expanded_ex", id); + if (currentPage() !== "monitor") { + history.pushState({}, "", "/monitor"); + setActiveNav(); + } + if (lastMonitorRows.length) { + openExchangeFullscreen(id); + } else { + void fetchMonitorBoardSnapshot({ showLoading: true }); + } + }; + initAuth().then((ok) => { if (!ok) return; initShellNav(); diff --git a/manual_trading_hub/static/dashboard.css b/manual_trading_hub/static/dashboard.css index dd1736a..4ce991e 100644 --- a/manual_trading_hub/static/dashboard.css +++ b/manual_trading_hub/static/dashboard.css @@ -317,6 +317,75 @@ body.hub-page-dashboard .page#page-dashboard { color: var(--dash-muted); line-height: 1.4; word-break: break-word; + display: flex; + flex-direction: column; + gap: 6px; +} + +.dash-ac-monitor-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; +} + +.dash-monitor-chip { + display: inline-flex; + align-items: center; + padding: 3px 8px; + border-radius: 6px; + font-size: 11px; + line-height: 1.3; + border: 1px solid transparent; + font-weight: 600; +} + +.dash-monitor-chip.dash-monitor-key { + color: #b8a0ff; + background: rgba(123, 97, 255, 0.18); + border-color: rgba(123, 97, 255, 0.42); +} + +.dash-monitor-chip.dash-monitor-order { + color: var(--dash-accent); + background: rgba(0, 212, 255, 0.14); + border-color: rgba(0, 212, 255, 0.38); +} + +.dash-monitor-chip.dash-monitor-trend { + color: var(--dash-ok); + background: rgba(0, 255, 157, 0.1); + border-color: rgba(0, 255, 157, 0.38); +} + +.dash-monitor-chip.dash-monitor-roll { + color: #ffb020; + background: rgba(255, 176, 32, 0.14); + border-color: rgba(255, 176, 32, 0.42); +} + +.dash-ac-expand-btn { + margin-left: auto; + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + padding: 0; + border-radius: 6px; + border: 1px solid var(--dash-card-border); + background: color-mix(in srgb, var(--dash-accent) 8%, var(--dash-card-bg)); + color: var(--dash-accent); + cursor: pointer; + flex-shrink: 0; +} + +.dash-ac-expand-btn:hover { + border-color: var(--dash-accent); + background: color-mix(in srgb, var(--dash-accent) 14%, var(--dash-card-bg)); +} + +.dash-ac-positions { display: flex; flex-direction: column; gap: 3px; @@ -346,10 +415,33 @@ body.hub-page-dashboard .page#page-dashboard { font-weight: 600; } +.dash-ac-remark-pos .pos { + color: var(--dash-ok); +} + +.dash-ac-remark-pos .neg { + color: var(--dash-warn); +} + +.dash-ac-remark-empty { + color: var(--dash-muted); +} + .dash-ac-remark-issue { color: var(--dash-warn); } +html[data-theme="light"] .dash-monitor-chip.dash-monitor-key { + color: #5b4fc7; + background: rgba(91, 79, 199, 0.1); + border-color: rgba(91, 79, 199, 0.28); +} + +html[data-theme="light"] .dash-monitor-chip.dash-monitor-trend { + background: rgba(10, 143, 92, 0.1); + border-color: rgba(10, 143, 92, 0.28); +} + .dash-table-wrap { overflow: auto; max-height: min(52vh, 480px); diff --git a/manual_trading_hub/static/dashboard.js b/manual_trading_hub/static/dashboard.js index f8001e6..de27944 100644 --- a/manual_trading_hub/static/dashboard.js +++ b/manual_trading_hub/static/dashboard.js @@ -90,33 +90,72 @@ `; } - function renderRemarkLines(ac) { - const lines = Array.isArray(ac && ac.remark_lines) ? ac.remark_lines : []; - if (!lines.length) { - const fallback = esc((ac && ac.remark) || "—"); - return `