Constrain funds bar width and fold strategy rules.

Match page max-width on ultrawide and move the plan subtitle into a collapsible rules section under the funds strip.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-29 17:37:34 +08:00
parent 5c3bd4b654
commit f49365b4d4
5 changed files with 145 additions and 59 deletions
+3
View File
@@ -163,6 +163,9 @@ async def funds_summary(_user: Annotated[str, Depends(require_user)]) -> dict[st
"options_trading_label": _fmt_opt(opt_t_usdc, opt_t_usdt),
"realtime_pnl": realtime,
"usdc_usdt_rate": rate,
"perp_inst_id": str(
db.get_setting("perp_inst_id") or s.perp_inst_id or "ETH-USDT-SWAP"
),
"updated_at_ms": int(datetime.now(timezone.utc).timestamp() * 1000),
}
+9
View File
@@ -5,6 +5,15 @@
---
## 2026-07-29 — 资金条与内容区同宽;规则说明折叠
### 变更
1. 资金横条内容区与下方页面同宽(带鱼屏不再拉满全屏)。
2. 原计划页说明文案收入资金条下方「规则说明」折叠。
---
## 2026-07-29 — 资金条 + USDT/USDC 兑换划转(SIM/LIVE
### 变更
+77 -48
View File
@@ -15,6 +15,7 @@ export type FundsSummary = {
options_trading_label: string;
realtime_pnl: number | null;
usdc_usdt_rate?: number;
perp_inst_id?: string;
detail?: string;
};
@@ -30,6 +31,7 @@ function pnlClass(n: number | null | undefined) {
export default function FundsBar() {
const [s, setS] = useState<FundsSummary | null>(null);
const [rulesOpen, setRulesOpen] = useState(false);
const load = useCallback(() => {
apiFetch<FundsSummary>("/api/funds/summary")
@@ -47,56 +49,83 @@ export default function FundsBar() {
if (!s?.ok) return null;
const rulesText = `行情 ${s.exchange || "OKX"} · ${
s.perp_inst_id || "ETH-USDT-SWAP"
} · 目标平仓(双腿/远虚只平永续) · 未达标则到期结算`;
return (
<div className="funds-bar" aria-label="资金与统计">
<div className="funds-item funds-item--primary">
<div className="funds-label"></div>
<div className="funds-value">{s.exchange || "—"}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{s.trading_day}</div>
</div>
<div className="funds-item funds-item--primary">
<div className="funds-label"></div>
<div className="funds-value mono">{s.total_trades}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">
{(s.win_rate * 100).toFixed(0)}%
<div className="funds-bar-shell">
<div className="funds-bar-inner">
<div className="funds-bar" aria-label="资金与统计">
<div className="funds-item funds-item--primary">
<div className="funds-label"></div>
<div className="funds-value">{s.exchange || "—"}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{s.trading_day}</div>
</div>
<div className="funds-item funds-item--primary">
<div className="funds-label"></div>
<div className="funds-value mono">{s.total_trades}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">
{(s.win_rate * 100).toFixed(0)}%
</div>
</div>
<div className="funds-item" title="平均盈利 ÷ 平均亏损">
<div className="funds-label"></div>
<div className="funds-value mono">
{s.profit_loss_ratio != null
? s.profit_loss_ratio.toFixed(2)
: "—"}
</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{fmtU(s.total_funds)}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{fmtU(s.funding_usdt)}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{fmtU(s.trading_usdt)}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{s.options_funding_label}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{s.options_trading_label}</div>
</div>
<div className="funds-item funds-item--pnl">
<div className="funds-label"></div>
<div className={`funds-value mono ${pnlClass(s.realtime_pnl)}`}>
{s.realtime_pnl == null ? "—" : fmtU(s.realtime_pnl)}
</div>
</div>
</div>
</div>
<div className="funds-item" title="平均盈利 ÷ 平均亏损">
<div className="funds-label"></div>
<div className="funds-value mono">
{s.profit_loss_ratio != null ? s.profit_loss_ratio.toFixed(2) : "—"}
</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{fmtU(s.total_funds)}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{fmtU(s.funding_usdt)}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{fmtU(s.trading_usdt)}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{s.options_funding_label}</div>
</div>
<div className="funds-item">
<div className="funds-label"></div>
<div className="funds-value mono">{s.options_trading_label}</div>
</div>
<div className="funds-item funds-item--pnl">
<div className="funds-label"></div>
<div className={`funds-value mono ${pnlClass(s.realtime_pnl)}`}>
{s.realtime_pnl == null ? "—" : fmtU(s.realtime_pnl)}
<div className="funds-rules">
<button
type="button"
className="funds-rules-toggle"
aria-expanded={rulesOpen}
onClick={() => setRulesOpen((v) => !v)}
>
<span></span>
<span className="funds-rules-chevron" aria-hidden>
{rulesOpen ? "▾" : "▸"}
</span>
</button>
{rulesOpen ? (
<p className="funds-rules-body meta">{rulesText}</p>
) : null}
</div>
</div>
</div>
-8
View File
@@ -167,14 +167,6 @@ export default function PlanPage() {
<div className="plan-page">
<div className="plan-desktop-head">
<h2 style={{ marginTop: 0 }}></h2>
<p style={{ color: "var(--muted)", marginTop: -8 }}>
{(snap?.exchange || "okx").toUpperCase()}
{snap?.perp_inst_id ? ` · ${snap.perp_inst_id}` : ""} ·
(/) ·
{plan?.mode === "LIVE" && plan.live_ready === false
? ` · 未就绪: ${plan.live_ready_reason || "请配置 API"}`
: ""}
</p>
</div>
{/* 手机端一屏监控卡:状态 / 净利 / 倒计时 */}
+56 -3
View File
@@ -83,13 +83,32 @@ input {
grid-template-rows: auto auto 1fr;
}
.funds-bar-shell {
border-bottom: 1px solid var(--line);
background: rgba(18, 22, 28, 0.96);
}
.funds-bar-inner {
width: 100%;
max-width: min(1680px, 100%);
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box;
}
@media (min-width: 1800px) {
.funds-bar-inner {
max-width: 1760px;
padding-left: 28px;
padding-right: 28px;
}
}
.funds-bar {
display: flex;
flex-wrap: wrap;
gap: 2px 0;
padding: 8px 12px;
border-bottom: 1px solid var(--line);
background: rgba(18, 22, 28, 0.96);
padding: 8px 0 4px;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@@ -128,6 +147,40 @@ input {
font-variant-numeric: tabular-nums;
}
.funds-rules {
padding: 2px 10px 8px;
border-top: 1px solid rgba(30, 38, 48, 0.85);
}
.funds-rules-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 0;
padding: 0;
border: none;
background: transparent;
color: var(--muted);
font-size: 12px;
cursor: pointer;
}
.funds-rules-toggle:hover {
color: var(--text);
}
.funds-rules-chevron {
font-size: 11px;
opacity: 0.85;
}
.funds-rules-body {
margin: 6px 0 0;
font-size: 12px;
line-height: 1.45;
color: var(--muted);
}
.topnav {
display: grid;
grid-template-columns: 1fr auto 1fr;