feat: show time-close countdown after symbol in monitor area
Move timed-close badge next to contract name on hub board and instance position cards; refresh countdown on board render. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -413,6 +413,13 @@
|
||||
<div class="pos-card-head">
|
||||
<div class="pos-card-symbol">
|
||||
<strong>{{ o.exchange_symbol or o.symbol }}</strong>
|
||||
{% if o.time_close_enabled %}
|
||||
<span class="pos-symbol-time-close pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="pos-side-badge {{ 'pos-side-long' if o.direction == 'long' else 'pos-side-short' }}">{{ '做多' if o.direction == 'long' else '做空' }}</span>
|
||||
</div>
|
||||
<div class="pos-head-actions">
|
||||
@@ -427,12 +434,6 @@
|
||||
<span class="pos-meta-item {% if o.breakeven_enabled %}pos-meta-on{% else %}pos-meta-off{% endif %}">
|
||||
{% if o.breakeven_enabled %}移动保本:开 {{ o.breakeven_rr_trigger or '-' }}R→{{ price_fmt(o.symbol, o.breakeven_price) }}{% else %}移动保本:关{% endif %}
|
||||
</span>
|
||||
<span class="pos-meta-item pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
{% if not o.time_close_enabled %}style="display:none"{% endif %}
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· 倒计时 <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
<span class="pos-meta-item" id="order-be-wrap-{{ o.id }}" style="display:none"><span class="pos-breakeven-badge">已保本</span></span>
|
||||
</div>
|
||||
<div class="pos-grid">
|
||||
|
||||
@@ -393,6 +393,13 @@
|
||||
<div class="pos-card-head">
|
||||
<div class="pos-card-symbol">
|
||||
<strong>{{ o.exchange_symbol or o.symbol }}</strong>
|
||||
{% if o.time_close_enabled %}
|
||||
<span class="pos-symbol-time-close pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="pos-side-badge {{ 'pos-side-long' if o.direction == 'long' else 'pos-side-short' }}">{{ '做多' if o.direction == 'long' else '做空' }}</span>
|
||||
</div>
|
||||
<div class="pos-head-actions">
|
||||
@@ -407,12 +414,6 @@
|
||||
<span class="pos-meta-item {% if o.breakeven_enabled %}pos-meta-on{% else %}pos-meta-off{% endif %}">
|
||||
{% if o.breakeven_enabled %}移动保本:开 {{ o.breakeven_rr_trigger or '-' }}R→{{ price_fmt(o.symbol, o.breakeven_price) }}{% else %}移动保本:关{% endif %}
|
||||
</span>
|
||||
<span class="pos-meta-item pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
{% if not o.time_close_enabled %}style="display:none"{% endif %}
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· 倒计时 <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
<span class="pos-meta-item" id="order-be-wrap-{{ o.id }}" style="display:none"><span class="pos-breakeven-badge">已保本</span></span>
|
||||
</div>
|
||||
<div class="pos-grid">
|
||||
|
||||
@@ -393,6 +393,13 @@
|
||||
<div class="pos-card-head">
|
||||
<div class="pos-card-symbol">
|
||||
<strong>{{ o.exchange_symbol or o.symbol }}</strong>
|
||||
{% if o.time_close_enabled %}
|
||||
<span class="pos-symbol-time-close pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="pos-side-badge {{ 'pos-side-long' if o.direction == 'long' else 'pos-side-short' }}">{{ '做多' if o.direction == 'long' else '做空' }}</span>
|
||||
</div>
|
||||
<div class="pos-head-actions">
|
||||
@@ -407,12 +414,6 @@
|
||||
<span class="pos-meta-item {% if o.breakeven_enabled %}pos-meta-on{% else %}pos-meta-off{% endif %}">
|
||||
{% if o.breakeven_enabled %}移动保本:开 {{ o.breakeven_rr_trigger or '-' }}R→{{ price_fmt(o.symbol, o.breakeven_price) }}{% else %}移动保本:关{% endif %}
|
||||
</span>
|
||||
<span class="pos-meta-item pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
{% if not o.time_close_enabled %}style="display:none"{% endif %}
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· 倒计时 <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
<span class="pos-meta-item" id="order-be-wrap-{{ o.id }}" style="display:none"><span class="pos-breakeven-badge">已保本</span></span>
|
||||
</div>
|
||||
<div class="pos-grid">
|
||||
|
||||
@@ -422,6 +422,13 @@
|
||||
<div class="pos-card-head">
|
||||
<div class="pos-card-symbol">
|
||||
<strong>{{ o.exchange_symbol or o.symbol }}</strong>
|
||||
{% if o.time_close_enabled %}
|
||||
<span class="pos-symbol-time-close pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="pos-side-badge {{ 'pos-side-long' if o.direction == 'long' else 'pos-side-short' }}">{{ '做多' if o.direction == 'long' else '做空' }}</span>
|
||||
</div>
|
||||
<div class="pos-head-actions">
|
||||
@@ -436,12 +443,6 @@
|
||||
<span class="pos-meta-item {% if o.breakeven_enabled %}pos-meta-on{% else %}pos-meta-off{% endif %}">
|
||||
{% if o.breakeven_enabled %}移动保本:开 {{ o.breakeven_rr_trigger or '-' }}R→{{ price_fmt(o.symbol, o.breakeven_price) }}{% else %}移动保本:关{% endif %}
|
||||
</span>
|
||||
<span class="pos-meta-item pos-meta-on pos-time-close-meta" id="order-time-close-wrap-{{ o.id }}"
|
||||
{% if not o.time_close_enabled %}style="display:none"{% endif %}
|
||||
data-close-at-ms="{{ o.time_close_at_ms or '' }}">
|
||||
<span class="pos-time-close-label">时间平仓 {{ o.time_close_hours or '' }}h</span>
|
||||
· 倒计时 <span class="pos-time-close-cd" id="order-time-close-cd-{{ o.id }}">--:--:--</span>
|
||||
</span>
|
||||
<span class="pos-meta-item" id="order-be-wrap-{{ o.id }}" style="display:none"><span class="pos-breakeven-badge">已保本</span></span>
|
||||
</div>
|
||||
<div class="pos-grid">
|
||||
|
||||
@@ -1023,6 +1023,25 @@ body.market-chart-fs-open {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.hub-pos-card .pos-symbol-time-close,
|
||||
.hub-mini-title .pos-symbol-time-close {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 500;
|
||||
color: #8fc8ff;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
background: rgba(143, 200, 255, 0.1);
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.hub-pos-card .pos-symbol-time-close .pos-time-close-cd,
|
||||
.hub-mini-title .pos-symbol-time-close .pos-time-close-cd {
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
.hub-pos-card .pos-card-symbol strong {
|
||||
font-size: 14px;
|
||||
color: var(--text);
|
||||
|
||||
@@ -1495,6 +1495,9 @@
|
||||
}
|
||||
syncMonitorGridColumns(box, displayRows.length);
|
||||
bindMonitorInteractions(box);
|
||||
if (window.TimeCloseUI && TimeCloseUI.tickLocalCountdowns) {
|
||||
TimeCloseUI.tickLocalCountdowns();
|
||||
}
|
||||
|
||||
if (expandedExchangeId && fs && fsInner) {
|
||||
const row = rows.find((r) => String(r.id) === String(expandedExchangeId));
|
||||
@@ -1505,6 +1508,9 @@
|
||||
fs.setAttribute("aria-hidden", "false");
|
||||
document.body.classList.add("hub-fullscreen-open");
|
||||
bindMonitorInteractions(fsInner);
|
||||
if (window.TimeCloseUI && TimeCloseUI.tickLocalCountdowns) {
|
||||
TimeCloseUI.tickLocalCountdowns();
|
||||
}
|
||||
fsInner.querySelectorAll(".btn-expand-back").forEach((btn) => {
|
||||
btn.onclick = (ev) => {
|
||||
ev.stopPropagation();
|
||||
@@ -2073,6 +2079,17 @@
|
||||
return html;
|
||||
}
|
||||
|
||||
function timeCloseSymbolBadgeHtml(item) {
|
||||
if (!item || !item.time_close_enabled) return "";
|
||||
const tcLabel = item.time_close_label || `时间平仓 ${item.time_close_hours || ""}h`;
|
||||
const tcCd = item.time_close_countdown || "--:--:--";
|
||||
const tcAt = item.time_close_at_ms != null ? String(item.time_close_at_ms) : "";
|
||||
return (
|
||||
`<span class="pos-symbol-time-close pos-time-close-meta pos-meta-on" data-close-at-ms="${esc(tcAt)}">` +
|
||||
`${esc(tcLabel)} · <span class="pos-time-close-cd">${esc(tcCd)}</span></span>`
|
||||
);
|
||||
}
|
||||
|
||||
function renderTrendDcaTable(t, tickMap) {
|
||||
const levels = resolveTrendDcaLevels(t);
|
||||
if (!levels.length) return "";
|
||||
@@ -2304,26 +2321,18 @@
|
||||
meta.push(
|
||||
`<span class="${beOn ? "pos-meta-on" : "pos-meta-off"}">移动保本:${beOn ? "开" : "关"}</span>`
|
||||
);
|
||||
if (mo.time_close_enabled) {
|
||||
const tcLabel = mo.time_close_label || `时间平仓 ${mo.time_close_hours || ""}h`;
|
||||
const tcCd = mo.time_close_countdown || "--:--:--";
|
||||
const tcAt = mo.time_close_at_ms != null ? String(mo.time_close_at_ms) : "";
|
||||
meta.push(
|
||||
`<span class="pos-meta-item pos-meta-on pos-time-close-meta" data-close-at-ms="${esc(tcAt)}">` +
|
||||
`${esc(tcLabel)} · 倒计时 <span class="pos-time-close-cd">${esc(tcCd)}</span></span>`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
meta.push("来源: 交易所持仓");
|
||||
meta.push("风格: —");
|
||||
meta.push(`<span class="pos-meta-off">移动保本:关</span>`);
|
||||
}
|
||||
const symBeBadge = beSecured ? ` ${breakevenBadgeHtml()}` : "";
|
||||
const tcSymBadge = !isTrend && mo.time_close_enabled ? timeCloseSymbolBadgeHtml(mo) : "";
|
||||
const mktAttrs = marketOpenBtnAttrs(exchangeId, exchangeKey, symbol, pos, monitorOrder, trendPlan);
|
||||
return `<div class="pos-card hub-pos-card">
|
||||
<div class="pos-card-head">
|
||||
<div class="pos-card-symbol">
|
||||
<button type="button" class="btn-open-market sym-link pos-symbol-link" ${mktAttrs} title="打开行情区(含入场/止盈止损)"><strong>${esc(symbol)}</strong></button>${symBeBadge}
|
||||
<button type="button" class="btn-open-market sym-link pos-symbol-link" ${mktAttrs} title="打开行情区(含入场/止盈止损)"><strong>${esc(symbol)}</strong></button>${tcSymBadge}${symBeBadge}
|
||||
<span class="pos-side-badge ${sideCls}">${sideCn}</span>
|
||||
</div>
|
||||
<div class="pos-head-actions">
|
||||
@@ -2382,8 +2391,14 @@
|
||||
const amtLine = amtTxt
|
||||
? `<div class="hub-mini-line">挂单数量 ${esc(amtTxt)}</div>`
|
||||
: "";
|
||||
const keyTc =
|
||||
k.time_close_enabled && k.time_close_at_ms
|
||||
? timeCloseSymbolBadgeHtml(k)
|
||||
: k.time_close_enabled && k.time_close_hours
|
||||
? `<span class="pos-symbol-time-close pos-meta-on">时间平仓 ${esc(k.time_close_hours)}h</span>`
|
||||
: "";
|
||||
return `<div class="${cardCls}">
|
||||
<div class="hub-mini-title">${esc(k.symbol)} · ${esc(mt)}${dir} ${pendingTag}</div>
|
||||
<div class="hub-mini-title">${esc(k.symbol)} ${keyTc} · ${esc(mt)}${dir} ${pendingTag}</div>
|
||||
<div class="hub-mini-line">上沿 ${esc(k.upper)} / 下沿 ${esc(k.lower)}</div>
|
||||
${amtLine}
|
||||
<div class="hub-mini-line hub-key-status-line">${esc(kp.gate_summary || kp.price_display || kp.price || "—")}${kp.gate_metrics ? ` · ${esc(kp.gate_metrics)}` : ""}</div>
|
||||
@@ -2398,8 +2413,9 @@
|
||||
return orders
|
||||
.map((o) => {
|
||||
const sym = o.exchange_symbol || o.symbol || "";
|
||||
const tcBadge = o.time_close_enabled ? timeCloseSymbolBadgeHtml(o) : "";
|
||||
return `<div class="hub-mini-card">
|
||||
<div class="hub-mini-title">#${esc(o.id)} · ${esc(o.symbol || o.exchange_symbol)} · ${renderDirectionHtml(o.direction)}</div>
|
||||
<div class="hub-mini-title">#${esc(o.id)} · ${esc(o.symbol || o.exchange_symbol)} ${tcBadge} · ${renderDirectionHtml(o.direction)}</div>
|
||||
<div class="hub-mini-line">触发 ${fmtSymbolPrice(o.trigger_price, sym, tickMap)} · SL ${fmtSymbolPrice(o.stop_loss, sym, tickMap)} · TP ${fmtSymbolPrice(o.take_profit, sym, tickMap)} · ${esc(o.trade_style || o.monitor_type || "下单监控")}</div>
|
||||
</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=20260612-trade-row-click" />
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260612-time-close-symbol" />
|
||||
<link rel="stylesheet" href="/assets/dashboard.css?v=20260612-dash-monitor-count" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -589,6 +589,6 @@
|
||||
<script src="/assets/dashboard.js?v=20260612-dash-monitor-count"></script>
|
||||
<script src="/assets/ai_review_render.js?v=3"></script>
|
||||
<script src="/assets/time_close_ui.js?v=2"></script>
|
||||
<script src="/assets/app.js?v=20260612-time-close"></script>
|
||||
<script src="/assets/app.js?v=20260612-time-close-symbol"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -348,6 +348,22 @@ html[data-theme="light"] .pos-meta-item::after {
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.pos-symbol-time-close {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 500;
|
||||
color: #8fc8ff;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
background: rgba(143, 200, 255, 0.1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pos-symbol-time-close .pos-time-close-cd {
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
.key-time-close-wrap.is-disabled > label,
|
||||
.order-time-close-wrap.is-disabled > label {
|
||||
opacity: 0.72;
|
||||
|
||||
@@ -182,6 +182,9 @@
|
||||
<span class="key-row-summary-main">
|
||||
<span class="key-row-summary-title">
|
||||
<strong>{{ k.symbol }}</strong>
|
||||
{% if k.time_close_enabled and k.time_close_hours %}
|
||||
<span class="pos-symbol-time-close pos-meta-on">时间平仓 {{ k.time_close_hours }}h</span>
|
||||
{% endif %}
|
||||
{% if k.direction == 'watch' %}
|
||||
<span class="pos-side-badge" style="background:#2a3152;color:#9ab">双向</span>
|
||||
{% else %}
|
||||
@@ -207,9 +210,6 @@
|
||||
<span class="pos-meta-item">方案: {{ key_sl_tp_mode_label(k) }}</span>
|
||||
{% endif %}
|
||||
<span class="pos-meta-item">保本: {{ '开' if k.breakeven_enabled else '关' }}</span>
|
||||
{% if k.time_close_enabled and k.time_close_hours %}
|
||||
<span class="pos-meta-item pos-meta-on">时间平仓: {{ k.time_close_hours }}h</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="pos-grid">
|
||||
<div class="pos-cell"><span class="pos-label">现价</span><span class="pos-value" id="key-price-{{ k.id }}">-</span></div>
|
||||
|
||||
Reference in New Issue
Block a user