diff --git a/static/hub.css b/static/hub.css index e317c74..5fe72b6 100644 --- a/static/hub.css +++ b/static/hub.css @@ -1873,45 +1873,77 @@ html.hub-phone-html #overview-status { .hub-phone-cards { display: flex; flex-direction: column; - gap: 10px; + gap: 14px; } .hub-phone-card { background: rgba(16, 24, 40, 0.92); border: 1px solid rgba(56, 189, 248, 0.2); - border-radius: 12px; - padding: 12px; + border-radius: 14px; + padding: 16px 16px 14px; } .hub-phone-card-head { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 6px; + margin-bottom: 12px; +} +.hub-phone-card-head strong { + font-size: 1.02rem; + line-height: 1.3; +} +.hub-phone-card-tags { display: flex; flex-wrap: wrap; align-items: center; - gap: 6px 8px; - margin-bottom: 8px; + gap: 6px; +} +.hub-phone-card-pnl { + margin-bottom: 10px; +} +.hub-phone-card-pnl .muted { + display: block; + font-size: 0.72rem; + margin-bottom: 2px; +} +.hub-phone-card-pnl span:not(.muted) { + font-size: 1.35rem; + font-weight: 700; + font-variant-numeric: tabular-nums; + letter-spacing: -0.02em; } .hub-phone-card-kpis { display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; - margin-bottom: 8px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px 12px; + margin-bottom: 12px; } .hub-phone-card-kpis .muted { display: block; - font-size: 0.68rem; + font-size: 0.72rem; margin-bottom: 2px; } -.hub-phone-card-pos .pos-brief { - font-size: 0.78rem; +.hub-phone-card-pos { + font-size: 0.92rem; + line-height: 1.45; } -.hub-phone-card-exit, -.hub-phone-card-liq { - font-size: 0.76rem; +.hub-phone-pos-line + .hub-phone-pos-line { margin-top: 4px; } +.hub-phone-card-foot { + margin-top: 8px; + font-size: 0.8rem; + line-height: 1.4; +} +.hub-phone-card-err { + margin: 0 0 8px; + font-size: 0.78rem; +} .hub-phone-card-actions { display: flex; flex-wrap: wrap; gap: 8px; - margin-top: 10px; + margin-top: 14px; } html.hub-phone-html .desk { display: none !important; diff --git a/static/hub.js b/static/hub.js index fda84b5..3627156 100644 --- a/static/hub.js +++ b/static/hub.js @@ -337,6 +337,82 @@ }); } + function shortOptName(id) { + const s = String(id || "").trim(); + if (!s) return "—"; + const parts = s.split("-"); + if (parts.length >= 5) { + const base = parts[0]; + const strike = parts[3]; + const ot = String(parts[4] || "").toUpperCase(); + const side = ot === "P" ? "Put" : ot === "C" ? "Call" : ot; + return base + " " + side + " " + strike; + } + return s; + } + + function formatExitBriefPhone(p) { + if (!p) return ""; + if (p.target_monitor_text && String(p.target_monitor_text).trim() && p.target_monitor_text !== "—") { + return String(p.target_monitor_text).trim(); + } + const tgt = Number(p.target_index); + if (Number.isFinite(tgt) && tgt > 0) { + const ot = String(p.opt_type || "").toUpperCase(); + const side = ot === "P" ? "Put≤" : ot === "C" ? "Call≥" : "目标"; + return side + tgt; + } + if (p.profit_exit_enabled) { + const m = Number(p.profit_exit_mult); + if (Number.isFinite(m) && m > 0) { + return (Math.abs(m - Math.round(m)) < 1e-9 ? Math.round(m) : m) + "倍出场"; + } + return "利润出场"; + } + return ""; + } + + function phonePosLine(p) { + const sheets = p.sheets != null ? String(p.sheets) : "—"; + const lev = Number(p.contract_leverage != null ? p.contract_leverage : p.leverage); + const levTxt = Number.isFinite(lev) && lev > 0 ? " · " + lev.toFixed(1) + "×" : ""; + return escapeHtml(shortOptName(p.inst_id) + " · " + sheets + "张" + levTxt); + } + + function phonePositionBlock(brief, cond) { + const list = brief || []; + if (!list.length) { + if (cond) { + return ( + '
' + + escapeHtml(condWaitLine(cond)) + + "
" + ); + } + return '
暂无持仓
'; + } + const shown = list.slice(0, 3).map(function (p) { + return '
' + phonePosLine(p) + "
"; + }); + if (list.length > 3) { + shown.push('
还有 ' + (list.length - 3) + " 笔
"); + } + return '
' + shown.join("") + "
"; + } + + function phoneFootLine(brief) { + const list = brief || []; + if (!list.length) return ""; + const bits = []; + const exitText = formatExitBriefPhone(list[0]); + if (exitText) bits.push(exitText); + const liq = bidLiquidityLine(list[0]); + if (liq && liq.text) bits.push((liq.weak ? "买一弱 " : "") + liq.text); + if (!bits.length) return ""; + const cls = liq && liq.weak ? "hub-phone-card-foot liq-warn" : "hub-phone-card-foot muted"; + return '
' + escapeHtml(bits.join(" · ")) + "
"; + } + function renderPhoneCard(row) { const snap = row.snapshot || {}; const inst = snap.instance || {}; @@ -351,7 +427,9 @@ const modeHtml = m === "sim" || m === "live" ? '' + escapeHtml(m) + "" - : '' + escapeHtml(inst.mode || "—") + ""; + : inst.mode + ? '' + escapeHtml(inst.mode) + "" + : ""; const err = row.error ? '
错误: ' + escapeHtml(row.error) + "
" : ""; @@ -361,31 +439,26 @@ "" + escapeHtml(row.name || "") + "" + + '
' + (online ? '在线' : '离线') + modeHtml + condTagHtml(cond) + - "
" + + "" + err + - '
' + - '
浮盈' + + '
' + + '浮盈' + fmtPnlHtml(pnl) + "
" + - '
权利金' + + '
' + + "
权利金" + premiumCell(snap) + "
" + - '
占比' + + "
占比" + premiumRatioCell(snap) + "
" + "
" + - '
' + - positionSummary(brief, cond) + - "
" + - '
出场 ' + - (brief.length ? escapeHtml(formatExitBrief(brief[0])) : "—") + - "
" + - '
' + - bidLiquiditySummary(brief) + - "
" + + phonePositionBlock(brief, cond) + + phoneFootLine(brief) + '
' + '