Add mobile compact trade and journal lists with tap-to-expand detail.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-23 16:53:56 +08:00
parent 8e810154ca
commit c302c3e4ea
8 changed files with 418 additions and 68 deletions
+9 -17
View File
@@ -237,7 +237,7 @@
.stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px} .stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px}
.stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4} .stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4}
</style> </style>
<link rel="stylesheet" href="/static/instance_theme.css?v=15"> <link rel="stylesheet" href="/static/instance_theme.css?v=16">
</head> </head>
<body data-page="{{ page }}"> <body data-page="{{ page }}">
@@ -822,7 +822,8 @@
</div> </div>
</div> </div>
<script src="/static/instance_ui.js?v=1"></script> <script src="/static/instance_ui.js?v=2"></script>
<script src="/static/instance_records_mobile.js?v=1"></script>
<script src="/static/time_close_ui.js?v=2"></script> <script src="/static/time_close_ui.js?v=2"></script>
<script src="/static/ai_review_render.js?v=2"></script> <script src="/static/ai_review_render.js?v=2"></script>
<script src="/static/form_submit_guard.js?v=2"></script> <script src="/static/form_submit_guard.js?v=2"></script>
@@ -907,6 +908,7 @@ function setDetailBodyPlain(text){
body.innerText = text || ""; body.innerText = text || "";
} }
function setDetailBodyMarkdown(text){ function setDetailBodyMarkdown(text){
if(window.InstanceUI && InstanceUI.clearDetailActions) InstanceUI.clearDetailActions();
const body = document.getElementById("detailBody"); const body = document.getElementById("detailBody");
if(!body) return; if(!body) return;
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){ if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
@@ -1110,22 +1112,12 @@ function loadJournals(){
const qs = listWindowQueryString(); const qs = listWindowQueryString();
fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{ fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{
Object.keys(journalCache).forEach(k=>delete journalCache[k]); Object.keys(journalCache).forEach(k=>delete journalCache[k]);
let html=""; data.forEach(o=>{ journalCache[o.id] = o; });
data.forEach(o=>{
journalCache[o.id] = o;
const moodTags = (o.mood_issues || []).join(",") || "无";
html += `<div class="entry">
<div><strong>${o.coin||"-"} ${o.tf||"-"}</strong> | 盈亏:${o.pnl||"-"}U</div>
<div>开:${o.open_datetime||"-"} 平:${o.close_datetime||"-"} 持仓:${o.hold_duration||"-"}</div>
<div>心态标签:${moodTags}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px">
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openJournalDetail('${o.id}')">查看详情</button>
<button type="button" class="btn-del" onclick="deleteJournal('${o.id}')">删除</button>
</div>
</div>`;
});
const box = document.getElementById("journal-list"); const box = document.getElementById("journal-list");
if(box){ box.innerHTML = html || "<div class='entry'>暂无数据</div>"; } if(box){
const html = InstanceUI.renderJournalListHtml(data);
box.innerHTML = html || "<div class='journal-empty-msg'>暂无数据</div>";
}
}); });
} }
+9 -17
View File
@@ -237,7 +237,7 @@
.stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px} .stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px}
.stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4} .stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4}
</style> </style>
<link rel="stylesheet" href="/static/instance_theme.css?v=15"> <link rel="stylesheet" href="/static/instance_theme.css?v=16">
</head> </head>
<body data-page="{{ page }}"> <body data-page="{{ page }}">
@@ -789,7 +789,8 @@
</div> </div>
</div> </div>
<script src="/static/instance_ui.js?v=1"></script> <script src="/static/instance_ui.js?v=2"></script>
<script src="/static/instance_records_mobile.js?v=1"></script>
<script src="/static/time_close_ui.js?v=2"></script> <script src="/static/time_close_ui.js?v=2"></script>
<script src="/static/ai_review_render.js?v=2"></script> <script src="/static/ai_review_render.js?v=2"></script>
<script src="/static/form_submit_guard.js?v=2"></script> <script src="/static/form_submit_guard.js?v=2"></script>
@@ -874,6 +875,7 @@ function setDetailBodyPlain(text){
body.innerText = text || ""; body.innerText = text || "";
} }
function setDetailBodyMarkdown(text){ function setDetailBodyMarkdown(text){
if(window.InstanceUI && InstanceUI.clearDetailActions) InstanceUI.clearDetailActions();
const body = document.getElementById("detailBody"); const body = document.getElementById("detailBody");
if(!body) return; if(!body) return;
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){ if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
@@ -1077,22 +1079,12 @@ function loadJournals(){
const qs = listWindowQueryString(); const qs = listWindowQueryString();
fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{ fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{
Object.keys(journalCache).forEach(k=>delete journalCache[k]); Object.keys(journalCache).forEach(k=>delete journalCache[k]);
let html=""; data.forEach(o=>{ journalCache[o.id] = o; });
data.forEach(o=>{
journalCache[o.id] = o;
const moodTags = (o.mood_issues || []).join(",") || "无";
html += `<div class="entry">
<div><strong>${o.coin||"-"} ${o.tf||"-"}</strong> | 盈亏:${o.pnl||"-"}U</div>
<div>开:${o.open_datetime||"-"} 平:${o.close_datetime||"-"} 持仓:${o.hold_duration||"-"}</div>
<div>心态标签:${moodTags}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px">
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openJournalDetail('${o.id}')">查看详情</button>
<button type="button" class="btn-del" onclick="deleteJournal('${o.id}')">删除</button>
</div>
</div>`;
});
const box = document.getElementById("journal-list"); const box = document.getElementById("journal-list");
if(box){ box.innerHTML = html || "<div class='entry'>暂无数据</div>"; } if(box){
const html = InstanceUI.renderJournalListHtml(data);
box.innerHTML = html || "<div class='journal-empty-msg'>暂无数据</div>";
}
}); });
} }
+9 -17
View File
@@ -237,7 +237,7 @@
.stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px} .stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px}
.stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4} .stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4}
</style> </style>
<link rel="stylesheet" href="/static/instance_theme.css?v=15"> <link rel="stylesheet" href="/static/instance_theme.css?v=16">
</head> </head>
<body data-page="{{ page }}"> <body data-page="{{ page }}">
@@ -789,7 +789,8 @@
</div> </div>
</div> </div>
<script src="/static/instance_ui.js?v=1"></script> <script src="/static/instance_ui.js?v=2"></script>
<script src="/static/instance_records_mobile.js?v=1"></script>
<script src="/static/time_close_ui.js?v=2"></script> <script src="/static/time_close_ui.js?v=2"></script>
<script src="/static/ai_review_render.js?v=2"></script> <script src="/static/ai_review_render.js?v=2"></script>
<script src="/static/form_submit_guard.js?v=2"></script> <script src="/static/form_submit_guard.js?v=2"></script>
@@ -874,6 +875,7 @@ function setDetailBodyPlain(text){
body.innerText = text || ""; body.innerText = text || "";
} }
function setDetailBodyMarkdown(text){ function setDetailBodyMarkdown(text){
if(window.InstanceUI && InstanceUI.clearDetailActions) InstanceUI.clearDetailActions();
const body = document.getElementById("detailBody"); const body = document.getElementById("detailBody");
if(!body) return; if(!body) return;
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){ if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
@@ -1077,22 +1079,12 @@ function loadJournals(){
const qs = listWindowQueryString(); const qs = listWindowQueryString();
fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{ fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{
Object.keys(journalCache).forEach(k=>delete journalCache[k]); Object.keys(journalCache).forEach(k=>delete journalCache[k]);
let html=""; data.forEach(o=>{ journalCache[o.id] = o; });
data.forEach(o=>{
journalCache[o.id] = o;
const moodTags = (o.mood_issues || []).join(",") || "无";
html += `<div class="entry">
<div><strong>${o.coin||"-"} ${o.tf||"-"}</strong> | 盈亏:${o.pnl||"-"}U</div>
<div>开:${o.open_datetime||"-"} 平:${o.close_datetime||"-"} 持仓:${o.hold_duration||"-"}</div>
<div>心态标签:${moodTags}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px">
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openJournalDetail('${o.id}')">查看详情</button>
<button type="button" class="btn-del" onclick="deleteJournal('${o.id}')">删除</button>
</div>
</div>`;
});
const box = document.getElementById("journal-list"); const box = document.getElementById("journal-list");
if(box){ box.innerHTML = html || "<div class='entry'>暂无数据</div>"; } if(box){
const html = InstanceUI.renderJournalListHtml(data);
box.innerHTML = html || "<div class='journal-empty-msg'>暂无数据</div>";
}
}); });
} }
+9 -17
View File
@@ -237,7 +237,7 @@
.stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px} .stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px}
.stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4} .stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4}
</style> </style>
<link rel="stylesheet" href="/static/instance_theme.css?v=15"> <link rel="stylesheet" href="/static/instance_theme.css?v=16">
</head> </head>
<body data-page="{{ page }}"> <body data-page="{{ page }}">
@@ -818,7 +818,8 @@
</div> </div>
</div> </div>
<script src="/static/instance_ui.js?v=1"></script> <script src="/static/instance_ui.js?v=2"></script>
<script src="/static/instance_records_mobile.js?v=1"></script>
<script src="/static/time_close_ui.js?v=2"></script> <script src="/static/time_close_ui.js?v=2"></script>
<script src="/static/ai_review_render.js?v=2"></script> <script src="/static/ai_review_render.js?v=2"></script>
<script src="/static/form_submit_guard.js?v=2"></script> <script src="/static/form_submit_guard.js?v=2"></script>
@@ -903,6 +904,7 @@ function setDetailBodyPlain(text){
body.innerText = text || ""; body.innerText = text || "";
} }
function setDetailBodyMarkdown(text){ function setDetailBodyMarkdown(text){
if(window.InstanceUI && InstanceUI.clearDetailActions) InstanceUI.clearDetailActions();
const body = document.getElementById("detailBody"); const body = document.getElementById("detailBody");
if(!body) return; if(!body) return;
if(window.AiReviewRender && AiReviewRender.setElementMarkdown){ if(window.AiReviewRender && AiReviewRender.setElementMarkdown){
@@ -1106,22 +1108,12 @@ function loadJournals(){
const qs = listWindowQueryString(); const qs = listWindowQueryString();
fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{ fetch("/api/journals" + (qs ? "?" + qs : "")).then(r=>r.json()).then(data=>{
Object.keys(journalCache).forEach(k=>delete journalCache[k]); Object.keys(journalCache).forEach(k=>delete journalCache[k]);
let html=""; data.forEach(o=>{ journalCache[o.id] = o; });
data.forEach(o=>{
journalCache[o.id] = o;
const moodTags = (o.mood_issues || []).join(",") || "无";
html += `<div class="entry">
<div><strong>${o.coin||"-"} ${o.tf||"-"}</strong> | 盈亏:${o.pnl||"-"}U</div>
<div>开:${o.open_datetime||"-"} 平:${o.close_datetime||"-"} 持仓:${o.hold_duration||"-"}</div>
<div>心态标签:${moodTags}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px">
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openJournalDetail('${o.id}')">查看详情</button>
<button type="button" class="btn-del" onclick="deleteJournal('${o.id}')">删除</button>
</div>
</div>`;
});
const box = document.getElementById("journal-list"); const box = document.getElementById("journal-list");
if(box){ box.innerHTML = html || "<div class='entry'>暂无数据</div>"; } if(box){
const html = InstanceUI.renderJournalListHtml(data);
box.innerHTML = html || "<div class='journal-empty-msg'>暂无数据</div>";
}
}); });
} }
+1
View File
@@ -54,6 +54,7 @@ def install_instance_theme_static(app) -> None:
"instance_theme.css": "text/css; charset=utf-8", "instance_theme.css": "text/css; charset=utf-8",
"account_risk_badge.css": "text/css; charset=utf-8", "account_risk_badge.css": "text/css; charset=utf-8",
"instance_ui.js": "application/javascript; charset=utf-8", "instance_ui.js": "application/javascript; charset=utf-8",
"instance_records_mobile.js": "application/javascript; charset=utf-8",
"ai_review_render.js": "application/javascript; charset=utf-8", "ai_review_render.js": "application/javascript; charset=utf-8",
"form_submit_guard.js": "application/javascript; charset=utf-8", "form_submit_guard.js": "application/javascript; charset=utf-8",
"key_monitor_form.js": "application/javascript; charset=utf-8", "key_monitor_form.js": "application/javascript; charset=utf-8",
+72
View File
@@ -0,0 +1,72 @@
/**
* 手机端交易记录 / 复盘记录紧凑列表币种 · 方向 · 盈亏点击展开详情
*/
(function (global) {
"use strict";
var resizeTimer = null;
function refreshTradeRecords() {
var UI = global.InstanceUI;
if (!UI) return;
var card = document.querySelector(".records-card");
if (!card) return;
var tableWrap = card.querySelector(".table-wrap");
var table = tableWrap && tableWrap.querySelector("table");
if (!table) return;
var listEl = card.querySelector(".mobile-record-list");
var mobile = UI.isMobileCompactRecords();
if (!mobile) {
if (listEl) listEl.remove();
return;
}
if (!listEl) {
listEl = document.createElement("div");
listEl.className = "mobile-record-list";
tableWrap.parentNode.insertBefore(listEl, tableWrap);
}
var rows = table.querySelectorAll('tr[id^="trade-row-"]');
listEl.innerHTML = Array.prototype.map
.call(rows, function (tr) {
return UI.renderMobileTradeRow(tr);
})
.join("");
listEl.querySelectorAll(".mobile-record-row").forEach(function (btn) {
btn.addEventListener("click", function () {
var rowId = btn.getAttribute("data-row-id");
var tr = rowId && document.getElementById(rowId);
if (tr) UI.openTradeRecordDetailModal(tr);
});
});
}
function onResize() {
if (resizeTimer) clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
refreshTradeRecords();
if (typeof global.loadJournals === "function" && document.getElementById("journal-list")) {
global.loadJournals();
}
}, 180);
}
function init() {
refreshTradeRecords();
global.addEventListener("resize", onResize);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
global.InstanceRecordsMobile = {
refresh: refreshTradeRecords,
};
})(typeof window !== "undefined" ? window : globalThis);
+138
View File
@@ -98,6 +98,120 @@
.grid { .grid {
grid-template-columns: minmax(0, 1fr) !important; grid-template-columns: minmax(0, 1fr) !important;
} }
.records-card .table-wrap {
display: none !important;
}
.mobile-record-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.mobile-record-row-wrap {
display: flex;
align-items: stretch;
gap: 6px;
}
.mobile-record-row {
flex: 1;
display: grid;
grid-template-columns: minmax(0, 1.2fr) auto minmax(0, 0.9fr);
align-items: center;
gap: 8px;
width: 100%;
margin: 0;
padding: 10px 12px;
border: 1px solid rgba(120, 140, 200, 0.28);
border-radius: 8px;
background: rgba(18, 24, 42, 0.65);
color: #e8ecff;
font-size: 0.82rem;
text-align: left;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.mobile-record-row:active {
background: rgba(30, 42, 72, 0.85);
}
.mrr-symbol {
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mrr-dir {
justify-self: center;
}
.mrr-dir .badge {
font-size: 0.72rem;
padding: 2px 8px;
}
.mrr-pnl {
justify-self: end;
font-weight: 600;
white-space: nowrap;
}
.mrr-muted {
color: #8892b0;
font-size: 0.78rem;
}
.mobile-record-del {
flex: 0 0 36px;
width: 36px;
border: 1px solid rgba(200, 80, 80, 0.35);
border-radius: 8px;
background: rgba(80, 24, 24, 0.35);
color: #ff9a9a;
font-size: 1.1rem;
line-height: 1;
cursor: pointer;
}
#journal-list .entry {
display: none;
}
#journal-list .journal-empty-msg {
color: #8892b0;
font-size: 0.82rem;
padding: 8px 4px;
}
#detailActions.detail-actions,
.detail-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 10px 14px 14px;
border-top: 1px solid rgba(120, 140, 200, 0.2);
}
.detail-actions-inner {
display: flex;
flex-wrap: wrap;
gap: 8px;
width: 100%;
}
.detail-actions .table-del,
.detail-actions button {
font-size: 0.78rem !important;
padding: 6px 10px !important;
}
}
.mobile-record-list {
display: none;
} }
/* 手机竖屏(含大屏手机) */ /* 手机竖屏(含大屏手机) */
@@ -1090,3 +1204,27 @@ html[data-theme="light"] .key-row-collapse.key-history-failed .key-history-outco
border-color: rgba(192, 48, 48, 0.22) !important; border-color: rgba(192, 48, 48, 0.22) !important;
} }
html[data-theme="light"] .mobile-record-row {
background: #fff !important;
border-color: #b8c8d8 !important;
color: #142232 !important;
}
html[data-theme="light"] .mobile-record-row:active {
background: #eef3f8 !important;
}
html[data-theme="light"] .mrr-muted {
color: #6a7588 !important;
}
html[data-theme="light"] .mobile-record-del {
background: rgba(192, 48, 48, 0.08) !important;
border-color: rgba(192, 48, 48, 0.28) !important;
color: #b04040 !important;
}
html[data-theme="light"] .detail-actions {
border-top-color: #d0dae4 !important;
}
+171
View File
@@ -66,6 +66,7 @@
titleEl.innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`; titleEl.innerText = `交易复盘详情|${o.coin || "-"} ${o.tf || "-"}`;
} }
setJournalDetailBody(o, formatExitLine); setJournalDetailBody(o, formatExitLine);
clearDetailActions();
const imgEl = document.getElementById("detailImage"); const imgEl = document.getElementById("detailImage");
if (imgEl) { if (imgEl) {
if (o.image) { if (o.image) {
@@ -83,6 +84,168 @@
if (modal) modal.style.display = "flex"; if (modal) modal.style.display = "flex";
} }
function isMobileCompactRecords() {
if (typeof window === "undefined" || !window.matchMedia) return false;
return window.matchMedia("(max-width: 720px)").matches;
}
function inferJournalDirection(o) {
const text = String((o && o.entry_reason) || "");
if (/做空|空头|short/i.test(text)) {
return { text: "做空", cls: "direction-short" };
}
if (/做多|多头|long/i.test(text)) {
return { text: "做多", cls: "direction-long" };
}
return null;
}
function renderJournalListHtml(data) {
if (!data || !data.length) return "";
const mobile = isMobileCompactRecords();
return data
.map(function (o) {
if (mobile) {
const dir = inferJournalDirection(o);
const pnlCls = pnlClassFromValue(o.pnl);
const dirHtml = dir
? `<span class="badge ${dir.cls}">${escapeHtml(dir.text)}</span>`
: `<span class="mrr-muted">-</span>`;
const id = escapeHtml(o.id);
return `<div class="mobile-record-row-wrap">
<button type="button" class="mobile-record-row" onclick="openJournalDetail('${id}')">
<span class="mrr-symbol">${escapeHtml(o.coin || "-")} ${escapeHtml(o.tf || "")}</span>
<span class="mrr-dir">${dirHtml}</span>
<span class="mrr-pnl ${pnlCls}">${escapeHtml(o.pnl == null || o.pnl === "" ? "-" : o.pnl)}U</span>
</button>
<button type="button" class="mobile-record-del" title="删除" onclick="deleteJournal('${id}')">×</button>
</div>`;
}
const moodTags = (o.mood_issues || []).join(",") || "无";
const id = escapeHtml(o.id);
return `<div class="entry">
<div><strong>${escapeHtml(o.coin || "-")} ${escapeHtml(o.tf || "-")}</strong> | :${escapeHtml(o.pnl == null || o.pnl === "" ? "-" : o.pnl)}U</div>
<div>:${escapeHtml(o.open_datetime || "-")} :${escapeHtml(o.close_datetime || "-")} 持仓:${escapeHtml(o.hold_duration || "-")}</div>
<div>心态标签:${escapeHtml(moodTags)}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px">
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openJournalDetail('${id}')">查看详情</button>
<button type="button" class="btn-del" onclick="deleteJournal('${id}')">删除</button>
</div>
</div>`;
})
.join("");
}
function parseTradeRecordRow(tr) {
const cells = tr.querySelectorAll("td");
if (cells.length < 14) return null;
const dirBadge = cells[2].querySelector(".badge");
return {
rowId: tr.id,
symbol: cells[0].textContent.trim(),
type: cells[1].textContent.trim(),
directionHtml: dirBadge ? dirBadge.outerHTML : cells[2].innerHTML,
directionText: cells[2].textContent.trim(),
trigger: cells[3].textContent.trim(),
stopLoss: cells[4].textContent.trim(),
takeProfit: cells[5].textContent.trim(),
margin: cells[6].textContent.trim(),
leverage: cells[7].textContent.trim(),
holdMinutes: cells[8].textContent.trim(),
openedAt: cells[9].textContent.trim(),
closedAt: cells[10].textContent.trim(),
pnlHtml: cells[11].innerHTML,
pnlText: cells[11].textContent.trim(),
resultHtml: cells[12].innerHTML,
resultText: cells[12].textContent.trim(),
actionsHtml: cells[13].innerHTML,
};
}
function renderMobileTradeRow(tr) {
const row = parseTradeRecordRow(tr);
if (!row) return "";
const pnlCls = pnlClassFromValue(row.pnlText);
return `<button type="button" class="mobile-record-row" data-row-id="${escapeHtml(row.rowId)}">
<span class="mrr-symbol">${escapeHtml(row.symbol)}</span>
<span class="mrr-dir">${row.directionHtml}</span>
<span class="mrr-pnl ${pnlCls}">${escapeHtml(row.pnlText || "-")}</span>
</button>`;
}
function buildTradeRecordDetailHtml(row) {
const lines = [
`品种:${escapeHtml(row.symbol)}`,
`类型:${escapeHtml(row.type)}`,
`方向:${row.directionHtml}`,
`成交价:${escapeHtml(row.trigger)}`,
`止损(开仓)${escapeHtml(row.stopLoss)}`,
`止盈:${escapeHtml(row.takeProfit)}`,
`基数:${escapeHtml(row.margin)}`,
`杠杆:${escapeHtml(row.leverage)}`,
`持仓分钟:${escapeHtml(row.holdMinutes)}`,
`开仓时间:${escapeHtml(row.openedAt)}`,
`平仓时间:${escapeHtml(row.closedAt)}`,
`盈亏U${row.pnlHtml}`,
`结果:${row.resultHtml}`,
];
return lines.join("<br>");
}
function clearDetailActions() {
const el = document.getElementById("detailActions");
if (el) {
el.innerHTML = "";
el.style.display = "none";
}
}
function setDetailActionsHtml(html) {
let el = document.getElementById("detailActions");
if (!el) {
const panel = document.querySelector("#detailModal .panel");
if (!panel) return;
el = document.createElement("div");
el.id = "detailActions";
el.className = "detail-actions";
const body = document.getElementById("detailBody");
if (body && body.parentNode === panel) {
panel.insertBefore(el, body.nextSibling);
} else {
panel.appendChild(el);
}
}
el.innerHTML = html || "";
el.style.display = html ? "flex" : "none";
}
function openTradeRecordDetailModal(tr) {
const row = parseTradeRecordRow(tr);
if (!row) return;
const titleEl = document.getElementById("detailTitle");
if (titleEl) {
titleEl.innerText = `交易记录|${row.symbol}`;
}
const body = document.getElementById("detailBody");
if (body) {
body.classList.remove("md-review", "journal-detail-meta");
body.innerHTML = buildTradeRecordDetailHtml(row);
}
setDetailActionsHtml(
`<div class="detail-actions-inner">${row.actionsHtml}</div>`
);
const imgEl = document.getElementById("detailImage");
if (imgEl) {
imgEl.src = "";
imgEl.style.display = "none";
}
if (typeof setDetailModalFullscreen === "function") {
setDetailModalFullscreen(false);
}
const modal = document.getElementById("detailModal");
if (modal) modal.style.display = "flex";
}
global.InstanceUI = { global.InstanceUI = {
escapeHtml: escapeHtml, escapeHtml: escapeHtml,
pnlClassFromValue: pnlClassFromValue, pnlClassFromValue: pnlClassFromValue,
@@ -90,5 +253,13 @@
buildJournalDetailHtml: buildJournalDetailHtml, buildJournalDetailHtml: buildJournalDetailHtml,
setJournalDetailBody: setJournalDetailBody, setJournalDetailBody: setJournalDetailBody,
openJournalDetailModal: openJournalDetailModal, openJournalDetailModal: openJournalDetailModal,
isMobileCompactRecords: isMobileCompactRecords,
inferJournalDirection: inferJournalDirection,
renderJournalListHtml: renderJournalListHtml,
parseTradeRecordRow: parseTradeRecordRow,
renderMobileTradeRow: renderMobileTradeRow,
buildTradeRecordDetailHtml: buildTradeRecordDetailHtml,
openTradeRecordDetailModal: openTradeRecordDetailModal,
clearDetailActions: clearDetailActions,
}; };
})(typeof window !== "undefined" ? window : globalThis); })(typeof window !== "undefined" ? window : globalThis);