增加手机端自适应
This commit is contained in:
@@ -43,7 +43,7 @@ HUB_BRIDGE_TOKEN = (os.getenv("HUB_BRIDGE_TOKEN") or os.getenv("CONTROL_TOKEN")
|
||||
_trust_raw = (os.getenv("HUB_TRUST_LAN", "true") or "").strip().lower()
|
||||
HUB_TRUST_LAN = _trust_raw not in ("0", "false", "no", "off")
|
||||
DIR = Path(__file__).resolve().parent
|
||||
HUB_BUILD = "20260525-fs-pos-row"
|
||||
HUB_BUILD = "20260525-mobile"
|
||||
|
||||
|
||||
def _is_local(host: str | None) -> bool:
|
||||
|
||||
@@ -604,16 +604,73 @@ body.hub-fullscreen-open {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
/* 全屏放大:持仓卡片横向排列 */
|
||||
/* 全屏放大:持仓卡片横向排列,列数随仓位数量自适应 */
|
||||
.exchange-fullscreen .hub-pos-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 14px;
|
||||
align-items: start;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-list.count-1 {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-list.count-1 .hub-pos-card.pos-card {
|
||||
max-width: min(960px, 100%);
|
||||
margin-inline: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-list.count-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-list.count-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-list.count-4 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-list.count-5,
|
||||
.exchange-fullscreen .hub-pos-list.count-6 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-list.count-many {
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
}
|
||||
|
||||
.exchange-fullscreen .hub-pos-card.pos-card {
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.exchange-fullscreen .hub-pos-list.count-3,
|
||||
.exchange-fullscreen .hub-pos-list.count-5,
|
||||
.exchange-fullscreen .hub-pos-list.count-6 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.exchange-fullscreen .hub-pos-list.count-many {
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.exchange-fullscreen .hub-pos-list.count-2,
|
||||
.exchange-fullscreen .hub-pos-list.count-3,
|
||||
.exchange-fullscreen .hub-pos-list.count-4,
|
||||
.exchange-fullscreen .hub-pos-list.count-5,
|
||||
.exchange-fullscreen .hub-pos-list.count-6,
|
||||
.exchange-fullscreen .hub-pos-list.count-many {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
.exchange-fullscreen .hub-pos-list.count-1 .hub-pos-card.pos-card {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 对齐实盘「实时持仓」pos-card */
|
||||
@@ -1132,8 +1189,15 @@ button.btn-sm {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-scroll {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
min-width: 300px;
|
||||
border-collapse: collapse;
|
||||
font-size: 11px;
|
||||
}
|
||||
@@ -1436,16 +1500,267 @@ body.login-page {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* —— 手机 / 窄屏自适应 —— */
|
||||
@media (max-width: 720px) {
|
||||
.app-shell {
|
||||
padding: 0 12px 32px;
|
||||
padding: 0 max(12px, env(safe-area-inset-right)) max(28px, env(safe-area-inset-bottom))
|
||||
max(12px, env(safe-area-inset-left));
|
||||
}
|
||||
.grid-monitor,
|
||||
.settings-grid-wrap {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
.app-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
padding: 14px 0;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.sys-pill {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
width: 100%;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.top-nav a {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 10px 12px;
|
||||
min-height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
button.ghost#btn-logout {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.page-head {
|
||||
margin: 16px 0 12px;
|
||||
}
|
||||
|
||||
.page-head h1 {
|
||||
font-size: 17px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-spacer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toolbar-meta {
|
||||
text-align: center;
|
||||
order: 10;
|
||||
}
|
||||
|
||||
.toolbar button,
|
||||
.toolbar .chk-label {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.grid-monitor,
|
||||
.settings-grid-wrap {
|
||||
grid-template-columns: minmax(0, 1fr) !important;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card-head {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.card-actions .btn-link,
|
||||
.card-actions button {
|
||||
flex: 1 1 calc(50% - 4px);
|
||||
min-height: 40px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 12px;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.exchange-fullscreen {
|
||||
padding: max(10px, env(safe-area-inset-top)) max(10px, env(safe-area-inset-right))
|
||||
max(16px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-left));
|
||||
}
|
||||
|
||||
.exchange-fullscreen-panel {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.fs-head {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.fs-head-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fs-head-actions .btn-expand-back {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.fs-head-actions .btn-link,
|
||||
.fs-head-actions button {
|
||||
min-height: 44px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hub-pos-card .pos-card-head {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.hub-pos-card .pos-head-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.hub-pos-card .pos-entrust-btn,
|
||||
.hub-pos-card .pos-close-btn {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hub-pos-card .pos-ex-order-row {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.settings-card-foot {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.settings-card-foot .field {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.modal {
|
||||
padding: max(12px, env(safe-area-inset-top)) 12px max(12px, env(safe-area-inset-bottom));
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.modal-panel {
|
||||
max-width: none;
|
||||
width: 100%;
|
||||
border-radius: 12px 12px 0 0;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.modal-actions button {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
#toast {
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
bottom: max(12px, env(safe-area-inset-bottom));
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
body.login-page {
|
||||
padding: max(16px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
padding: 22px 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.card-actions .btn-link,
|
||||
.card-actions button {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.fs-head-actions {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.pos-action-group {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pos-action-group .btn-sm {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.data-table .td-actions {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,14 @@
|
||||
return n > 0 ? "pnl-pos" : "pnl-neg";
|
||||
}
|
||||
|
||||
/** 全屏持仓区:按仓位数量附加布局 class(1~6 固定列数,7+ 自动填充) */
|
||||
function hubPosListCountClass(n) {
|
||||
const c = Math.max(0, parseInt(n, 10) || 0);
|
||||
if (c <= 0) return "count-0";
|
||||
if (c <= 6) return `count-${c}`;
|
||||
return "count-many";
|
||||
}
|
||||
|
||||
function currentPage() {
|
||||
const p = window.location.pathname.replace(/\/$/, "") || "/monitor";
|
||||
if (p.includes("settings")) return "settings";
|
||||
@@ -103,9 +111,17 @@
|
||||
return (settingsCache?.exchanges || []).filter((x) => x.enabled);
|
||||
}
|
||||
|
||||
/** 监控卡片列数:3 个一行;4 个 2×2;5/6 个两行(每行最多 3) */
|
||||
function isMobileLayout() {
|
||||
return window.matchMedia("(max-width: 720px)").matches;
|
||||
}
|
||||
|
||||
/** 监控卡片列数:桌面 3/2 列;手机端固定单列 */
|
||||
function syncMonitorGridColumns(gridEl, count) {
|
||||
if (!gridEl) return;
|
||||
if (isMobileLayout()) {
|
||||
gridEl.style.gridTemplateColumns = "minmax(0, 1fr)";
|
||||
return;
|
||||
}
|
||||
let cols = 3;
|
||||
if (count <= 1) cols = 1;
|
||||
else if (count === 2) cols = 2;
|
||||
@@ -115,6 +131,19 @@
|
||||
gridEl.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`;
|
||||
}
|
||||
|
||||
function initMobileLayout() {
|
||||
let resizeTimer = null;
|
||||
window.addEventListener("resize", () => {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(() => {
|
||||
const box = document.getElementById("monitor-grid");
|
||||
if (box && lastMonitorRows.length) {
|
||||
syncMonitorGridColumns(box, lastMonitorRows.length);
|
||||
}
|
||||
}, 120);
|
||||
});
|
||||
}
|
||||
|
||||
function normSym(s) {
|
||||
return String(s || "")
|
||||
.toUpperCase()
|
||||
@@ -538,6 +567,7 @@
|
||||
const slAttr = esc(String(guess.sl)).replace(/"/g, """);
|
||||
const tpAttr = esc(String(guess.tp)).replace(/"/g, """);
|
||||
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></tr></thead><tbody>
|
||||
<tr>
|
||||
<td>${esc(x.symbol)}</td>
|
||||
@@ -552,6 +582,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
${renderOrdersCollapse(exchangeId, x.symbol, cond, reg)}
|
||||
</div>`;
|
||||
}
|
||||
@@ -644,8 +675,11 @@
|
||||
<div class="stat-box"><div class="stat-label">余额</div><div class="stat-value">${fmt(ag.balance_usdt, 2)} U</div></div>
|
||||
<div class="stat-box"><div class="stat-label">浮盈合计</div><div class="stat-value ${pnlCls(ag.total_unrealized_pnl)}">${fmt(ag.total_unrealized_pnl, 4)}</div></div>
|
||||
</div>`;
|
||||
html += '<div class="section-title">持仓(每币种一卡)</div><div class="hub-pos-list">';
|
||||
if (pos.length) {
|
||||
const posCount = pos.length;
|
||||
const posListCls = hubPosListCountClass(posCount);
|
||||
html += `<div class="section-title">持仓(${posCount} 仓 · 每币种一卡)</div>`;
|
||||
html += `<div class="hub-pos-list ${posListCls}" data-pos-count="${posCount}">`;
|
||||
if (posCount) {
|
||||
pos.forEach((p) => {
|
||||
html += renderLivePositionCard(row.id, p, findMonitorOrder(orders, p.symbol, p.side));
|
||||
});
|
||||
@@ -1087,6 +1121,7 @@
|
||||
|
||||
initTpslModal();
|
||||
initFullscreen();
|
||||
initMobileLayout();
|
||||
|
||||
initAuth().then((ok) => {
|
||||
if (!ok) return;
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>复盘系统中控</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<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" />
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260525-fs-pos-row" />
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260525-mobile" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-bg" aria-hidden="true"></div>
|
||||
@@ -108,6 +108,6 @@
|
||||
</div>
|
||||
|
||||
<div id="toast"></div>
|
||||
<script src="/assets/app.js?v=20260525-fs-fix"></script>
|
||||
<script src="/assets/app.js?v=20260525-mobile"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>登录 · 复盘系统中控</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<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" />
|
||||
<link rel="stylesheet" href="/assets/app.css" />
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260525-mobile" />
|
||||
</head>
|
||||
<body class="login-page">
|
||||
<div class="login-bg" aria-hidden="true"></div>
|
||||
|
||||
Reference in New Issue
Block a user