Phone hub folds: emergency ops, market toolbar, calc tabs, OKX options.
Collapse bulky monitor/market controls by default on phone, show OKX options on tiles, add calculator tab switch, and request landscape on market fullscreen. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1493,15 +1493,18 @@
|
||||
else pill.classList.remove("syncing");
|
||||
}
|
||||
const upd = document.getElementById("monitor-updated");
|
||||
if (upd) {
|
||||
const updSum = document.getElementById("monitor-updated-summary");
|
||||
if (upd || updSum) {
|
||||
const ts = tsRaw.replace("T", " ");
|
||||
upd.textContent = options.stale
|
||||
const txt = options.stale
|
||||
? ts
|
||||
? `缓存 ${ts} · 后台聚合中…`
|
||||
: "后台聚合中…"
|
||||
: ts
|
||||
? `UPD ${ts}`
|
||||
: "";
|
||||
if (upd) upd.textContent = txt;
|
||||
if (updSum) updSum.textContent = txt;
|
||||
}
|
||||
updateMonitorAlertSummary(rows || []);
|
||||
void refreshMacroRiskBanner(rows || []);
|
||||
@@ -1923,6 +1926,46 @@
|
||||
syncHubAiMobileViewport();
|
||||
}
|
||||
|
||||
function syncHubPhoneFolds() {
|
||||
const phone = isMobileLayout();
|
||||
["monitor-ops-fold", "market-toolbar-fold"].forEach((id) => {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
if (!phone) {
|
||||
el.open = true;
|
||||
return;
|
||||
}
|
||||
// 手机默认折叠;用户手动点开后本会话不再强关
|
||||
if (el.dataset.userToggled === "1") return;
|
||||
el.open = false;
|
||||
});
|
||||
}
|
||||
|
||||
function bindHubPhoneFolds() {
|
||||
["monitor-ops-fold", "market-toolbar-fold"].forEach((id) => {
|
||||
const el = document.getElementById(id);
|
||||
if (!el || el.dataset.boundFold === "1") return;
|
||||
el.dataset.boundFold = "1";
|
||||
el.addEventListener("toggle", () => {
|
||||
if (!isMobileLayout()) return;
|
||||
el.dataset.userToggled = "1";
|
||||
});
|
||||
});
|
||||
const sym = document.getElementById("market-symbol");
|
||||
const tf = document.getElementById("market-timeframe");
|
||||
const syncMarketFoldMeta = () => {
|
||||
const meta = document.getElementById("market-fold-summary");
|
||||
if (!meta) return;
|
||||
const s = sym ? String(sym.value || "").trim() : "";
|
||||
const t = tf ? String(tf.value || "").trim() : "";
|
||||
meta.textContent = [s || "—", t || "—"].join(" · ");
|
||||
};
|
||||
if (sym) sym.addEventListener("change", syncMarketFoldMeta);
|
||||
if (sym) sym.addEventListener("input", syncMarketFoldMeta);
|
||||
if (tf) tf.addEventListener("change", syncMarketFoldMeta);
|
||||
syncMarketFoldMeta();
|
||||
}
|
||||
|
||||
function initHubMobileChrome() {
|
||||
const moreBtn = document.getElementById("m-tab-more");
|
||||
const backdrop = document.getElementById("hub-mobile-more-backdrop");
|
||||
@@ -1940,7 +1983,9 @@
|
||||
closeHubMobileMore();
|
||||
}
|
||||
});
|
||||
bindHubPhoneFolds();
|
||||
syncHubPhoneShellClass();
|
||||
syncHubPhoneFolds();
|
||||
}
|
||||
|
||||
function bindHubSpaNavLinks(selector) {
|
||||
@@ -1972,6 +2017,7 @@
|
||||
resizeTimer = setTimeout(() => {
|
||||
const nowMobile = isMobileLayout();
|
||||
syncHubPhoneShellClass();
|
||||
syncHubPhoneFolds();
|
||||
if (!nowMobile) closeHubMobileMore();
|
||||
if (lastMonitorRows.length && nowMobile !== wasMobile) {
|
||||
wasMobile = nowMobile;
|
||||
@@ -3541,21 +3587,37 @@
|
||||
}
|
||||
|
||||
function renderCardStrategyStats(row, hm, flaskOk) {
|
||||
if (!flaskOk || !hm || typeof hm !== "object") return "";
|
||||
const caps = row.capabilities || [];
|
||||
const chips = [];
|
||||
if (caps.includes("key")) {
|
||||
const kc = countKeyMonitorsByBucket(hm.keys || []);
|
||||
if (kc.breakout > 0) chips.push({ kind: "key-breakout", label: `突破 ${kc.breakout}` });
|
||||
if (kc.fib > 0) chips.push({ kind: "key-breakout", label: `斐波 ${kc.fib}` });
|
||||
if (kc.watch > 0) chips.push({ kind: "key-watch", label: `监控 ${kc.watch}` });
|
||||
if (flaskOk && hm && typeof hm === "object") {
|
||||
if (caps.includes("key")) {
|
||||
const kc = countKeyMonitorsByBucket(hm.keys || []);
|
||||
if (kc.breakout > 0) chips.push({ kind: "key-breakout", label: `突破 ${kc.breakout}` });
|
||||
if (kc.fib > 0) chips.push({ kind: "key-breakout", label: `斐波 ${kc.fib}` });
|
||||
if (kc.watch > 0) chips.push({ kind: "key-watch", label: `监控 ${kc.watch}` });
|
||||
}
|
||||
if (caps.includes("trend")) {
|
||||
const trendN = Array.isArray(hm.trends) ? hm.trends.length : 0;
|
||||
if (trendN > 0) chips.push({ kind: "trend", label: `趋势回调 ${trendN}` });
|
||||
}
|
||||
const rollN = Array.isArray(hm.rolls) ? hm.rolls.length : 0;
|
||||
if (rollN > 0) chips.push({ kind: "roll", label: `顺势加仓 ${rollN}` });
|
||||
}
|
||||
if (caps.includes("trend")) {
|
||||
const trendN = Array.isArray(hm.trends) ? hm.trends.length : 0;
|
||||
if (trendN > 0) chips.push({ kind: "trend", label: `趋势回调 ${trendN}` });
|
||||
// OKX/已勾选期权能力:磁贴始终展示期权入口态(含 0 仓)
|
||||
if (caps.includes("options") || rowHasOptionsLayout(row)) {
|
||||
const opt = row.options || {};
|
||||
if (opt.enabled === false) {
|
||||
chips.push({ kind: "options", label: "期权 · 未启用" });
|
||||
} else if (opt.ok === false) {
|
||||
chips.push({ kind: "options", label: "期权 · 异常" });
|
||||
} else {
|
||||
const n = Number(
|
||||
opt.position_count != null ? opt.position_count : (opt.positions || []).length
|
||||
);
|
||||
const nSafe = Number.isFinite(n) ? n : 0;
|
||||
chips.push({ kind: "options", label: nSafe > 0 ? `期权 ${nSafe}仓` : "期权" });
|
||||
}
|
||||
}
|
||||
const rollN = Array.isArray(hm.rolls) ? hm.rolls.length : 0;
|
||||
if (rollN > 0) chips.push({ kind: "roll", label: `顺势加仓 ${rollN}` });
|
||||
if (!chips.length) return "";
|
||||
return `<div class="card-strategy-stats">${chips
|
||||
.map(
|
||||
@@ -4110,12 +4172,33 @@
|
||||
const posLine =
|
||||
openCount > 0 ? `${openCount}仓 · ${alert.summary}` : alert.summary;
|
||||
const opt = row.options || {};
|
||||
const optCount =
|
||||
opt.enabled !== false && opt.ok !== false
|
||||
? Number(opt.position_count != null ? opt.position_count : (opt.positions || []).length)
|
||||
: 0;
|
||||
const posLineWithOpt =
|
||||
optCount > 0 ? `${posLine} · 期权${optCount}仓` : posLine;
|
||||
const hasOptCap = (row.capabilities || []).includes("options") || rowHasOptionsLayout(row);
|
||||
let optLine = "";
|
||||
let pnlShow = upnl;
|
||||
let pnlSuffix = "";
|
||||
if (hasOptCap) {
|
||||
if (opt.enabled === false) {
|
||||
optLine = "期权未启用";
|
||||
} else if (opt.ok === false) {
|
||||
optLine = "期权异常";
|
||||
} else {
|
||||
const optCount = Number(
|
||||
opt.position_count != null ? opt.position_count : (opt.positions || []).length
|
||||
);
|
||||
const n = Number.isFinite(optCount) ? optCount : 0;
|
||||
const bal = typeof optionsBalanceFields === "function" ? optionsBalanceFields(opt) : {};
|
||||
const optUpl = bal && bal.upl != null ? bal.upl : null;
|
||||
optLine = n > 0 ? `期权 ${n}仓` : "期权 空仓";
|
||||
if (optUpl != null && Number.isFinite(Number(optUpl))) {
|
||||
optLine += ` · 浮盈 ${fmt(optUpl, 2)}U`;
|
||||
// 永续空仓时主数字优先展示期权浮盈,避免一直显示 0U
|
||||
if (openCount === 0) {
|
||||
pnlShow = optUpl;
|
||||
pnlSuffix = "期权";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const hm = row.hub_monitor || {};
|
||||
const flaskOk = row.flask_ok !== false && hm.ok !== false;
|
||||
const strategyStats = renderCardStrategyStats(row, hm, flaskOk);
|
||||
@@ -4128,10 +4211,13 @@
|
||||
</div>
|
||||
${
|
||||
showAccountPnlPref()
|
||||
? `<div class="hub-tile-pnl ${pnlCls(upnl)}">${fmt(upnl, 2)} <small>U</small></div>`
|
||||
? `<div class="hub-tile-pnl ${pnlCls(pnlShow)}">${fmt(pnlShow, 2)} <small>U${
|
||||
pnlSuffix ? " · " + pnlSuffix : ""
|
||||
}</small></div>`
|
||||
: ""
|
||||
}
|
||||
<div class="hub-tile-meta">${esc(posLineWithOpt)}</div>
|
||||
<div class="hub-tile-meta">${esc(posLine)}</div>
|
||||
${optLine ? `<div class="hub-tile-meta hub-tile-opt">${esc(optLine)}</div>` : ""}
|
||||
${strategyStats}
|
||||
<div class="hub-tile-foot">UPD ${esc(tsShort)}</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user