Split OKX monitor into perp and options cards.
Align four exchange cards in a 2x2 grid so 1080p layout stays balanced. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,6 +4,32 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-16 · OKX 拆成永续/期权双卡(2×2 对齐)
|
||||
|
||||
### 修改原因
|
||||
|
||||
OKX 单卡内嵌永续+期权过高,右侧币安/Gate 两卡对不齐,1080p 观感不协调。按产品建议改为四卡对齐。
|
||||
|
||||
### 修改的地方
|
||||
|
||||
| 文件 | 改动摘要 |
|
||||
|------|----------|
|
||||
| `manual_trading_hub/static/app.js` | 期权分栏时 OKX 渲染为「·永续」「·期权」两张独立卡;标题与操作按钮按卡片分流 |
|
||||
| `manual_trading_hub/static/app.css` | 左右列均 `1fr/1fr`,四卡 2×2 等高对齐;卡体内滚 |
|
||||
| `manual_trading_hub/static/index.html` | 静态资源缓存 `20260716-hub-okx-2x2` |
|
||||
|
||||
### 达成的目标
|
||||
|
||||
监控区呈现:左上永续 / 左下期权 / 右上币安 / 右下 Gate,四卡对齐。
|
||||
|
||||
### 交付之后的验收
|
||||
|
||||
1. 桌面监控区可见 `OKX_趋势 · 永续` 与 `OKX_趋势 · 期权` 两张独立卡。
|
||||
2. 四卡与右侧币安、Gate 同行等高,不再一大两小。
|
||||
3. 点击任一张 OKX 卡标题仍可全屏;期权卡「全平」不重复出现。
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-16 · 1080p 监控区无持仓空洞收紧
|
||||
|
||||
### 修改原因
|
||||
|
||||
@@ -1275,31 +1275,25 @@ html[data-theme="light"] .host-metric-bar {
|
||||
.monitor-split-right {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.monitor-split-left > .card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monitor-split-right {
|
||||
display: grid;
|
||||
/* 无持仓时勿 1fr 均分拉高,按内容收紧;有仓时自然增高 */
|
||||
grid-template-rows: auto auto;
|
||||
align-content: start;
|
||||
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 16px;
|
||||
overflow: auto;
|
||||
align-content: stretch;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monitor-split-left > .card,
|
||||
.monitor-split-right > .card {
|
||||
height: auto;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-monitor-okx-split,
|
||||
.card-monitor-split-side {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: visible;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-monitor-okx-split .card-body,
|
||||
@@ -1307,7 +1301,7 @@ html[data-theme="light"] .host-metric-bar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: visible;
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
@@ -1374,6 +1368,7 @@ html[data-theme="light"] .host-metric-bar {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.monitor-split-left,
|
||||
.monitor-split-right {
|
||||
grid-template-rows: auto auto;
|
||||
}
|
||||
@@ -9898,15 +9893,18 @@ html[data-theme="light"] .hub-logs-card-hint {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body.hub-page-monitor:not(.hub-phone) .monitor-split-left,
|
||||
body.hub-page-monitor:not(.hub-phone) .monitor-split-right {
|
||||
gap: 8px;
|
||||
grid-template-rows: auto auto;
|
||||
align-content: start;
|
||||
overflow: auto;
|
||||
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
||||
align-content: stretch;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body.hub-page-monitor:not(.hub-phone) .monitor-split-left > .card,
|
||||
body.hub-page-monitor:not(.hub-phone) .monitor-split-right > .card {
|
||||
height: auto;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
body.hub-page-monitor:not(.hub-phone) .card-monitor-okx-split,
|
||||
@@ -9922,7 +9920,7 @@ html[data-theme="light"] .hub-logs-card-hint {
|
||||
body.hub-page-monitor:not(.hub-phone) .card-monitor-okx-split .card-body,
|
||||
body.hub-page-monitor:not(.hub-phone) .card-monitor-split-side .card-body {
|
||||
padding: 8px 10px;
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
body.hub-page-monitor:not(.hub-phone) .hub-inner-cards {
|
||||
@@ -10208,38 +10206,34 @@ body.hub-tablet.hub-page-monitor:not(.hub-phone) .grid-monitor-options-split .mo
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-left > .card {
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-left > .card,
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-right > .card {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-right > .card {
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-left,
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-right {
|
||||
grid-template-rows: auto auto;
|
||||
align-content: start;
|
||||
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
||||
align-content: stretch;
|
||||
gap: 6px;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 竖屏(<960)叠成 OKX 上 + Gate/币安并排 */
|
||||
/* 竖屏(<960)叠成 OKX 两卡在上 + Gate/币安并排 */
|
||||
@media (max-width: 960px) {
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-body {
|
||||
grid-template-rows: minmax(0, 1.35fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-right {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
grid-template-rows: auto;
|
||||
align-content: stretch;
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-left {
|
||||
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-right > .card {
|
||||
height: 100%;
|
||||
body.hub-tablet.hub-page-monitor:not(.hub-phone) .monitor-split-right {
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2434,7 +2434,11 @@
|
||||
if (optionsSplit) {
|
||||
const okxRow = displayRows.find((r) => rowHasOptionsLayout(r));
|
||||
const otherRows = displayRows.filter((r) => !rowHasOptionsLayout(r));
|
||||
const leftHtml = okxRow ? renderMonitorCard(okxRow, { okxSplit: true }) : "";
|
||||
/* OKX 拆成永续+期权两张独立卡,与右侧两所组成 2×2 对齐 */
|
||||
const leftHtml = okxRow
|
||||
? renderMonitorCard(okxRow, { okxPart: "perp", splitSide: true }) +
|
||||
renderMonitorCard(okxRow, { okxPart: "options", splitSide: true })
|
||||
: "";
|
||||
const rightHtml = otherRows.map((r) => renderMonitorCard(r, { splitSide: true })).join("");
|
||||
cardsHtml =
|
||||
`<div class="monitor-split-body">` +
|
||||
@@ -3852,12 +3856,26 @@
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderGridBody(row, ag, pos, hm, flaskOk, keys, orders, trends, rolls, kmap) {
|
||||
function renderGridBody(row, ag, pos, hm, flaskOk, keys, orders, trends, rolls, kmap, layoutOpts) {
|
||||
const layout = layoutOpts || {};
|
||||
const tickMap = buildPriceTickMap(row);
|
||||
const intraday = isIntradayDisciplineRow(row);
|
||||
let inner = "";
|
||||
if (layout.okxPart === "perp") {
|
||||
inner += renderAccountStatRow(row, ag);
|
||||
inner += renderPerpetualPositionsSection(row, ag, pos, orders, trends, tickMap, intraday);
|
||||
inner += renderCardStrategyStats(row, hm, flaskOk);
|
||||
inner += `<div class="card-expand-hint">点击标题栏进入全屏</div>`;
|
||||
return inner;
|
||||
}
|
||||
if (layout.okxPart === "options") {
|
||||
inner += renderOptionsSectionBody(row);
|
||||
inner += `<div class="card-expand-hint">点击标题栏进入全屏 · 期权专页可下单</div>`;
|
||||
return inner;
|
||||
}
|
||||
if (rowHasOptionsLayout(row)) {
|
||||
if (monitorGridOptionsSplit) {
|
||||
/* 兼容旧单卡内嵌;正常分栏已走 okxPart 双卡 */
|
||||
inner += '<div class="hub-inner-cards">';
|
||||
inner += renderPerpetualInnerCard(row, ag, pos, orders, trends, tickMap, intraday);
|
||||
inner += renderOptionsInnerCard(row);
|
||||
@@ -4266,41 +4284,49 @@
|
||||
inner = `<div class="err">${esc(agErr || "子代理返回失败")}</div>`;
|
||||
inner += `<div class="empty-hint">请检查 PM2 子代理与 <code>${esc(row.agent_url || "")}/status</code></div>`;
|
||||
} else {
|
||||
inner = renderGridBody(row, ag, pos, hm, flaskOk, keys, orders, trends, rolls, kmap);
|
||||
inner = renderGridBody(row, ag, pos, hm, flaskOk, keys, orders, trends, rolls, kmap, opts);
|
||||
}
|
||||
const online = row.http_ok && agOk;
|
||||
const cardCls = online ? "card-online" : "card-offline";
|
||||
const dotCls = online ? "ok" : "bad";
|
||||
const flaskOpen = row.flask_url_browser || row.flask_url;
|
||||
const okxPart = opts.okxPart || "";
|
||||
const isOkxOptionsCard = okxPart === "options";
|
||||
const isOkxPerpCard = okxPart === "perp";
|
||||
const titleSuffix = isOkxPerpCard ? " · 永续" : isOkxOptionsCard ? " · 期权" : "";
|
||||
const openFlask = flaskOpen
|
||||
? `<a class="btn-link btn-open-instance btn-open-trade" href="#" data-ex-id="${esc(row.id)}" data-next="/trade" data-new-tab="1">打开实例</a>`
|
||||
: "";
|
||||
const openTrade = flaskOpen
|
||||
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/trade">下单</a>`
|
||||
: "";
|
||||
const openKey = flaskOpen
|
||||
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/key_monitor">监控位</a>`
|
||||
: "";
|
||||
const openTrade =
|
||||
flaskOpen && !isOkxOptionsCard
|
||||
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/trade">下单</a>`
|
||||
: "";
|
||||
const openKey =
|
||||
flaskOpen && !isOkxOptionsCard
|
||||
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/key_monitor">监控位</a>`
|
||||
: "";
|
||||
const openReview = flaskOpen
|
||||
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/records">复盘</a>`
|
||||
: "";
|
||||
const openOptions =
|
||||
flaskOpen && (row.capabilities || []).includes("options")
|
||||
flaskOpen && ((row.capabilities || []).includes("options") || isOkxOptionsCard)
|
||||
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/options">期权</a>`
|
||||
: "";
|
||||
const intraday = isIntradayDisciplineRow(row);
|
||||
const fcHeadBadge = intraday ? forceCloseHeadBadgeHtml(row.force_close) : "";
|
||||
const fcHeadBadge = intraday && !isOkxOptionsCard ? forceCloseHeadBadgeHtml(row.force_close) : "";
|
||||
const showCloseAll = !intraday && !isOkxOptionsCard;
|
||||
const layoutCls = opts.okxSplit
|
||||
? " card-monitor-okx-split"
|
||||
: opts.splitSide
|
||||
: opts.splitSide || okxPart
|
||||
? " card-monitor-split-side"
|
||||
: "";
|
||||
return `<div class="card ${cardCls}${layoutCls}" data-ex-id="${esc(row.id)}">
|
||||
const partAttr = okxPart ? ` data-okx-part="${esc(okxPart)}"` : "";
|
||||
return `<div class="card ${cardCls}${layoutCls}" data-ex-id="${esc(row.id)}"${partAttr}>
|
||||
<div class="card-head card-expand-zone" title="点击放大全屏">
|
||||
<div>
|
||||
<div class="card-title-row">
|
||||
<span class="status-dot ${dotCls}" title="${online ? "在线" : "离线"}"></span>
|
||||
<div class="card-title"><span>${esc(row.name)}</span>${fcHeadBadge}${formatRiskStatusBadge(hm.risk_status)}</div>
|
||||
<div class="card-title"><span>${esc(row.name)}${esc(titleSuffix)}</span>${fcHeadBadge}${formatRiskStatusBadge(hm.risk_status)}</div>
|
||||
</div>
|
||||
<div class="card-sub">${esc(flaskOpen || "")}</div>
|
||||
</div>
|
||||
@@ -4310,7 +4336,7 @@
|
||||
${openKey}
|
||||
${openOptions}
|
||||
${openReview}
|
||||
${intraday ? "" : `<button type="button" class="danger btn-close-ex" data-id="${esc(row.id)}">全平</button>`}
|
||||
${showCloseAll ? `<button type="button" class="danger btn-close-ex" data-id="${esc(row.id)}">全平</button>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">${inner}</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
|
||||
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260716-hub-1080-tight" />
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260716-hub-okx-2x2" />
|
||||
<link rel="stylesheet" href="/assets/trade_stats_calendar.css?v=4" />
|
||||
<link rel="stylesheet" href="/assets/account_risk_badge.css?v=4" />
|
||||
<script src="/assets/account_risk_badge.js?v=4"></script>
|
||||
@@ -1327,6 +1327,6 @@
|
||||
<script src="/assets/options_expiry_countdown.js?v=1"></script>
|
||||
<script src="/assets/options_position_cards.js?v=1"></script>
|
||||
<script src="/assets/backup.js?v=1"></script>
|
||||
<script src="/assets/app.js?v=20260716-hub-desktop-clear"></script>
|
||||
<script src="/assets/app.js?v=20260716-hub-okx-2x2"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user