Render saved journals as a table like trade records.

Desktop journal list now uses columns for coin, direction, PnL, times, and actions instead of stacked entry cards.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-17 11:55:18 +08:00
parent d9b8069906
commit f435e9dfaa
5 changed files with 63 additions and 21 deletions
+50 -15
View File
@@ -151,7 +151,14 @@
}
function inferJournalDirection(o) {
const text = String((o && o.entry_reason) || "");
const hint = String((o && (o.direction_hint || o.direction)) || "").toLowerCase();
if (hint === "long" || hint === "buy" || hint === "多") {
return { text: "做多", cls: "direction-long" };
}
if (hint === "short" || hint === "sell" || hint === "空") {
return { text: "做空", cls: "direction-short" };
}
const text = String((o && (o.entry_reason || o.note)) || "");
if (/做空|空头|short/i.test(text)) {
return { text: "做空", cls: "direction-short" };
}
@@ -164,9 +171,9 @@
function renderJournalListHtml(data) {
if (!data || !data.length) return "";
const mobile = isMobileCompactRecords();
return data
.map(function (o) {
if (mobile) {
if (mobile) {
return data
.map(function (o) {
const dir = inferJournalDirection(o);
const pnlCls = pnlClassFromValue(o.pnl);
const dirHtml = dir
@@ -181,20 +188,48 @@
</button>
<button type="button" class="mobile-record-del" title="删除" onclick="deleteJournal('${id}')">×</button>
</div>`;
}
const moodTags = (o.mood_issues || []).join(",") || "无";
})
.join("");
}
const rows = data
.map(function (o) {
const moodTags = Array.isArray(o.mood_issues)
? o.mood_issues.join(",")
: o.mood_issues || "";
const mood = moodTags || "无";
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>`;
const pnlCls = pnlClassFromValue(o.pnl);
const pnlTxt =
o.pnl == null || o.pnl === "" ? "-" : String(o.pnl);
const dir = inferJournalDirection(o);
const dirHtml = dir
? `<span class="badge ${dir.cls}">${escapeHtml(dir.text)}</span>`
: "-";
return `<tr id="journal-row-${id}">
<td>${escapeHtml(o.coin || "-")}</td>
<td>${escapeHtml(o.tf || "-")}</td>
<td>${dirHtml}</td>
<td>${escapeHtml(o.order_type || "-")}</td>
<td>${escapeHtml(o.entry_reason || "-")}</td>
<td><span class="${pnlCls}">${escapeHtml(pnlTxt)}</span></td>
<td>${escapeHtml((o.open_datetime || "-").toString().slice(0, 16))}</td>
<td>${escapeHtml((o.close_datetime || "-").toString().slice(0, 16))}</td>
<td>${escapeHtml(o.hold_duration || "-")}</td>
<td>${escapeHtml(mood)}</td>
<td>
<button type="button" class="table-del" style="background:#1f3a5a;color:#8fc8ff;margin-right:6px" onclick="openJournalDetail('${id}')">查看详情</button>
<button type="button" class="table-del" onclick="deleteJournal('${id}')">删除</button>
</td>
</tr>`;
})
.join("");
return `<div class="table-wrap"><table class="rr-journals-table">
<thead><tr>
<th>品种</th><th>周期</th><th>方向</th><th>下单类型</th><th>开仓类型</th>
<th>盈亏U</th><th>开仓时间</th><th>平仓时间</th><th>持仓</th><th>心态标签</th><th>操作</th>
</tr></thead>
<tbody>${rows}</tbody>
</table></div>`;
}
function parseTradeRecordRow(tr) {
+7
View File
@@ -324,6 +324,13 @@
journalsPages = Math.max(1, Math.ceil(total / PAGE_SIZE) || 1);
if (journalsPage >= journalsPages) journalsPage = Math.max(0, journalsPages - 1);
updatePager("journals");
var hint = $("rr-journals-hint");
if (hint) {
hint.textContent =
total > 0
? "已保存的复盘(共" + total + "条,每页5条)."
: "已保存的复盘(每页5条).";
}
var slice = journalsAll.slice(
journalsPage * PAGE_SIZE,
journalsPage * PAGE_SIZE + PAGE_SIZE
+2 -2
View File
@@ -90,7 +90,7 @@
</div>
</div>
<script src="/static/instance_ui.js?v=8"></script>
<script src="/static/instance_ui.js?v=9"></script>
<script src="/static/journal_upload_slots.js?v=3"></script>
<script src="/static/instance_records_mobile.js?v=2"></script>
<script src="/static/time_close_ui.js?v=3"></script>
@@ -108,7 +108,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
<script src="/static/key_monitor_form.js?v=2"></script>
<script src="/static/instance_stats.js?v=4"></script>
{% include 'embed_boot_scripts.html' %}
<script src="/static/records_review_page.js?v=1"></script>
<script src="/static/records_review_page.js?v=2"></script>
<script>
window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
</script>
+2 -2
View File
@@ -447,7 +447,7 @@
</div>
</div>
<script src="/static/instance_ui.js?v=8"></script>
<script src="/static/instance_ui.js?v=9"></script>
<script src="/static/journal_upload_slots.js?v=3"></script>
<script src="/static/instance_records_mobile.js?v=2"></script>
<script src="/static/time_close_ui.js?v=3"></script>
@@ -1986,7 +1986,7 @@ setInterval(tickOrderHoldDurations, 1000);
tickOrderHoldDurations();
setInterval(refreshPriceSnapshotConditional, {{ price_refresh_seconds * 1000 }});
</script>
<script src="/static/records_review_page.js?v=1"></script>
<script src="/static/records_review_page.js?v=2"></script>
<script>
window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
</script>
+2 -2
View File
@@ -126,8 +126,8 @@
<div class="card full" id="rr-journals-card">
<h3 style="margin-top:0">交易复盘记录</h3>
<p class="rr-hint">已保存的复盘(每页5条).</p>
<div id="journal-list-wrap" class="rr-list-wrap">
<p class="rr-hint" id="rr-journals-hint">已保存的复盘(每页5条).</p>
<div id="journal-list-wrap" class="rr-list-wrap rr-journals-wrap">
<div id="journal-list"></div>
</div>
<div class="rr-pager" id="rr-journals-pager">