feat(hub): simplify desktop monitor cards to positions only

Move orders, key levels, entrust, trend plans, and roll groups to fullscreen detail view.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-03 23:15:55 +08:00
parent ed3ff747f4
commit 833c51e34a
2 changed files with 18 additions and 60 deletions
+17 -59
View File
@@ -1537,7 +1537,9 @@
.join("");
}
function renderPositionBlock(exchangeId, exchangeKey, x, monitorOrder, trendPlan, tickMap) {
function renderPositionBlock(exchangeId, exchangeKey, x, monitorOrder, trendPlan, tickMap, opts) {
const options = opts || {};
const compact = !!options.compact;
const symAttr = esc(x.symbol || "").replace(/"/g, "&quot;");
const exKeyAttr = esc(exchangeKey || exchangeId || "").replace(/"/g, "&quot;");
const sideAttr = esc((x.side || "").toLowerCase()).replace(/"/g, "&quot;");
@@ -1551,6 +1553,15 @@
const tpAttr = esc(String(tpsl.tp)).replace(/"/g, "&quot;");
const mktAttrs = marketOpenBtnAttrs(exchangeId, exchangeKey, x.symbol, x, monitorOrder, trendPlan);
const symBeBadge = beSecured ? ` ${breakevenBadgeHtml()}` : "";
const actionCell = compact
? `<button type="button" class="btn-close-pos btn-sm danger" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}">平仓</button>`
: `<div class="pos-action-group">
<button type="button" class="btn-place-tpsl btn-sm ghost" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}" data-contracts="${contractsAttr}" data-sl="${slAttr}" data-tp="${tpAttr}">委托</button>
<button type="button" class="btn-close-pos btn-sm danger" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}">平仓</button>
</div>`;
const ordersBlock = compact
? ""
: renderOrdersCollapse(exchangeId, x.symbol, cond, reg, tickMap);
return `<div class="pos-block">
<div class="table-scroll">
<table class="data-table"><thead><tr><th>合约</th><th>方向</th><th>开仓价</th><th>标记价</th><th>张数</th><th>浮盈</th><th>操作</th></tr></thead><tbody>
@@ -1561,16 +1572,11 @@
<td>${fmtMarkPrice(x, tickMap)}</td>
<td>${fmt(x.contracts, 4)}</td>
<td class="${pnlCls(x.unrealized_pnl)}">${fmt(x.unrealized_pnl, 2)}</td>
<td class="td-actions">
<div class="pos-action-group">
<button type="button" class="btn-place-tpsl btn-sm ghost" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}" data-contracts="${contractsAttr}" data-sl="${slAttr}" data-tp="${tpAttr}">委托</button>
<button type="button" class="btn-close-pos btn-sm danger" data-ex-id="${esc(exchangeId)}" data-symbol="${symAttr}" data-side="${sideAttr}">平仓</button>
</div>
</td>
<td class="td-actions">${actionCell}</td>
</tr>
</tbody></table>
</div>
${renderOrdersCollapse(exchangeId, x.symbol, cond, reg, tickMap)}
${ordersBlock}
</div>`;
}
@@ -1590,63 +1596,15 @@
p,
findMonitorOrder(orders, p.symbol, p.side),
findTrendPlan(trends, p.symbol, p.side),
tickMap
tickMap,
{ compact: true }
)
)
.join("");
} else {
inner += '<div class="empty-hint">无持仓</div>';
}
if (orders.length) {
inner += `<div class="section-title">下单监控 · ${orders.length}</div>`;
orders.forEach((o) => {
const sym = o.exchange_symbol || o.symbol || "";
inner += `<div class="list-line">${esc(o.symbol || o.exchange_symbol)} · ${renderDirectionHtml(o.direction)} · 触发 ${fmtSymbolPrice(o.trigger_price, sym, tickMap)}</div>`;
});
}
if ((row.capabilities || []).includes("key")) {
inner += `<div class="section-title">关键位 · ${keys.length}</div>`;
if (!flaskOk) {
const fe = row.flask_error || hm.msg || hm.error || "策略 Flask 未连通";
inner += `<div class="err">${esc(fe)}</div>`;
} else if (!keys.length) {
inner += '<div class="empty-hint">当前无记录</div>';
} else {
keys.forEach((k) => {
const kp = kmap[k.id] || kmap[String(k.id)] || {};
const mt = k.monitor_type || k.type || "";
const pending = keyHasPendingOrder(k, kp);
const lineCls = pending ? "list-line hub-key-pending" : "list-line";
let line = `${esc(k.symbol)} · ${esc(mt)}`;
if (k.direction) line += ` · ${renderDirectionHtml(k.direction)}`;
if (pending) line += ` · <span class="hub-key-pending-tag">挂单</span>`;
const amtTxt = fmtKeyOrderAmount(k);
if (amtTxt) line += ` · 数量 ${esc(amtTxt)}`;
line += ` · ${esc(k.upper)} / ${esc(k.lower)}`;
if (kp.price_display != null || kp.price != null) {
line += ` · ${esc(kp.price_display != null ? kp.price_display : kp.price)}`;
}
line += ` · ${esc(kp.gate_summary || "-")}`;
inner += `<div class="${lineCls}">${line}</div>`;
});
}
}
if ((row.capabilities || []).includes("trend") && trends.length) {
inner += `<div class="section-title">趋势回调 · ${trends.length}</div>`;
trends.forEach((t) => {
const sym = t.exchange_symbol || t.symbol || "";
const sl = t.stop_loss_display || fmtSymbolPrice(t.stop_loss, sym, tickMap);
const tp = t.take_profit_display || fmtSymbolPrice(t.take_profit, sym, tickMap);
inner += `<div class="list-line">#${t.id} ${esc(t.symbol)} ${renderDirectionHtml(t.direction)} · 均价 ${esc(t.avg_entry_price_display || fmtSymbolPrice(t.avg_entry_price, sym, tickMap))} · SL ${esc(sl)} · TP ${esc(tp)}${trendAddSummaryHtml(t, tickMap)}</div>`;
});
}
if (rolls.length) {
inner += `<div class="section-title">顺势加仓 · ${rolls.length}</div>`;
rolls.forEach((g) => {
inner += `<div class="list-line">组 #${g.id} · 监控 #${g.order_monitor_id || "—"} · ${g.leg_count != null ? g.leg_count : "—"} 腿</div>`;
});
}
inner += `<div class="card-expand-hint">点击标题栏放大全屏 · 查看持仓卡片 / 关键位 / 策略详情</div>`;
inner += `<div class="card-expand-hint">点击标题栏进入全屏 · 委托 / 关键位 / 下单监控 / 趋势回调 / 顺势加仓</div>`;
return inner;
}