This commit is contained in:
dekun
2026-05-27 15:52:00 +08:00
parent 6093bf6b94
commit 7aed70db04
4 changed files with 224 additions and 32 deletions
+56 -8
View File
@@ -82,6 +82,14 @@
.detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer} .detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer}
.detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff} .detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff}
.detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150} .detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150}
.detail-modal .panel-actions{display:flex;gap:8px;align-items:center;flex-shrink:0}
.detail-modal .panel-fs{padding:6px 10px;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:8px;cursor:pointer;font-size:.82rem}
.detail-modal.fullscreen{padding:10px}
.detail-modal.fullscreen .panel{width:100%;height:100%;max-width:none;max-height:none;display:flex;flex-direction:column;overflow:hidden}
.detail-modal.fullscreen .panel-body{flex:1;overflow:auto;min-height:0;font-size:.9rem}
.ai-result-wrap{margin-top:8px}
.ai-result-toolbar{display:flex;gap:8px;margin-top:6px}
.ai-result-toolbar .btn-fs{padding:4px 10px;font-size:.78rem;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:6px;cursor:pointer}
.table-wrap{overflow-x:auto} .table-wrap{overflow-x:auto}
.dual-panel-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch} .dual-panel-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch}
.dual-panel-grid .card{height:100%;display:flex;flex-direction:column} .dual-panel-grid .card{height:100%;display:flex;flex-direction:column}
@@ -746,8 +754,18 @@
<button type="button" onclick="genWeekly()">生成周复盘</button> <button type="button" onclick="genWeekly()">生成周复盘</button>
<button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button> <button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button>
</div> </div>
<div id="daily_result" class="ai-result" style="display:none"></div> <div class="ai-result-wrap" id="daily_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result" style="display:none"></div> <div id="daily_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('日复盘结果', 'daily_result')">全屏查看</button>
</div>
</div>
<div class="ai-result-wrap" id="weekly_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('周复盘结果', 'weekly_result')">全屏查看</button>
</div>
</div>
<div class="panel-list" style="margin-top:10px"> <div class="panel-list" style="margin-top:10px">
<div class="panel-item"> <div class="panel-item">
<strong>交易复盘记录</strong> <strong>交易复盘记录</strong>
@@ -807,7 +825,10 @@
<div class="panel" onclick="event.stopPropagation()"> <div class="panel" onclick="event.stopPropagation()">
<div class="panel-head"> <div class="panel-head">
<div class="panel-title" id="detailTitle">详情</div> <div class="panel-title" id="detailTitle">详情</div>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button> <div class="panel-actions">
<button type="button" class="panel-fs" onclick="expandDetailToFullscreen()">全屏</button>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button>
</div>
</div> </div>
<div class="panel-body" id="detailBody"></div> <div class="panel-body" id="detailBody"></div>
<img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)"> <img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)">
@@ -854,8 +875,28 @@ function validateJournalEntryReason(){
} }
function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";} function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";}
function closeModal(){document.getElementById("imgModal").style.display="none";} function closeModal(){document.getElementById("imgModal").style.display="none";}
function forceCloseDetailModal(){document.getElementById("detailModal").style.display="none";} function setDetailModalFullscreen(on){
const modal = document.getElementById("detailModal");
if(modal){ modal.classList.toggle("fullscreen", !!on); }
}
function forceCloseDetailModal(){
const modal = document.getElementById("detailModal");
if(modal){ modal.style.display = "none"; modal.classList.remove("fullscreen"); }
}
function closeDetailModal(e){if(e.target && e.target.id==="detailModal"){forceCloseDetailModal();}} function closeDetailModal(e){if(e.target && e.target.id==="detailModal"){forceCloseDetailModal();}}
function expandDetailToFullscreen(){ setDetailModalFullscreen(true); }
function openAiInlineResultFullscreen(title, elementId){
const el = document.getElementById(elementId || "daily_result");
const text = String((el && el.innerText) || "").trim();
if(!text){ alert("暂无内容"); return; }
document.getElementById("detailTitle").innerText = title || "AI复盘";
document.getElementById("detailBody").innerText = text;
const imgEl = document.getElementById("detailImage");
imgEl.src = "";
imgEl.style.display = "none";
setDetailModalFullscreen(true);
document.getElementById("detailModal").style.display = "flex";
}
const journalCache = {}; const journalCache = {};
const reviewCache = {}; const reviewCache = {};
@@ -897,10 +938,11 @@ function openJournalDetail(id){
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
} }
setDetailModalFullscreen(false);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
function openReviewDetail(id){ function openReviewDetail(id, fullscreen){
const r = reviewCache[id]; const r = reviewCache[id];
if(!r){ return; } if(!r){ return; }
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`; document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`;
@@ -908,6 +950,7 @@ function openReviewDetail(id){
const imgEl = document.getElementById("detailImage"); const imgEl = document.getElementById("detailImage");
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
setDetailModalFullscreen(!!fullscreen);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
@@ -1096,7 +1139,8 @@ function loadReviews(){
<div style="font-size:12px;color:#9aa">${r.created_at || ""}</div> <div style="font-size:12px;color:#9aa">${r.created_at || ""}</div>
<div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div> <div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px"> <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="openReviewDetail('${r.id}')">查看全文</button> <button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', false)">查看</button>
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', true)">全屏</button>
<a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a> <a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a>
<button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button> <button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button>
</div> </div>
@@ -1113,8 +1157,10 @@ function genDaily(){
fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`}) fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("daily_result"); const el=document.getElementById("daily_result");
const wrap=document.getElementById("daily_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }
@@ -1126,8 +1172,10 @@ function genWeekly(){
fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`}) fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("weekly_result"); const el=document.getElementById("weekly_result");
const wrap=document.getElementById("weekly_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }
+56 -8
View File
@@ -82,6 +82,14 @@
.detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer} .detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer}
.detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff} .detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff}
.detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150} .detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150}
.detail-modal .panel-actions{display:flex;gap:8px;align-items:center;flex-shrink:0}
.detail-modal .panel-fs{padding:6px 10px;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:8px;cursor:pointer;font-size:.82rem}
.detail-modal.fullscreen{padding:10px}
.detail-modal.fullscreen .panel{width:100%;height:100%;max-width:none;max-height:none;display:flex;flex-direction:column;overflow:hidden}
.detail-modal.fullscreen .panel-body{flex:1;overflow:auto;min-height:0;font-size:.9rem}
.ai-result-wrap{margin-top:8px}
.ai-result-toolbar{display:flex;gap:8px;margin-top:6px}
.ai-result-toolbar .btn-fs{padding:4px 10px;font-size:.78rem;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:6px;cursor:pointer}
.table-wrap{overflow-x:auto} .table-wrap{overflow-x:auto}
.dual-panel-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch} .dual-panel-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch}
.dual-panel-grid .card{height:100%;display:flex;flex-direction:column} .dual-panel-grid .card{height:100%;display:flex;flex-direction:column}
@@ -746,8 +754,18 @@
<button type="button" onclick="genWeekly()">生成周复盘</button> <button type="button" onclick="genWeekly()">生成周复盘</button>
<button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button> <button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button>
</div> </div>
<div id="daily_result" class="ai-result" style="display:none"></div> <div class="ai-result-wrap" id="daily_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result" style="display:none"></div> <div id="daily_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('日复盘结果', 'daily_result')">全屏查看</button>
</div>
</div>
<div class="ai-result-wrap" id="weekly_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('周复盘结果', 'weekly_result')">全屏查看</button>
</div>
</div>
<div class="panel-list" style="margin-top:10px"> <div class="panel-list" style="margin-top:10px">
<div class="panel-item"> <div class="panel-item">
<strong>交易复盘记录</strong> <strong>交易复盘记录</strong>
@@ -807,7 +825,10 @@
<div class="panel" onclick="event.stopPropagation()"> <div class="panel" onclick="event.stopPropagation()">
<div class="panel-head"> <div class="panel-head">
<div class="panel-title" id="detailTitle">详情</div> <div class="panel-title" id="detailTitle">详情</div>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button> <div class="panel-actions">
<button type="button" class="panel-fs" onclick="expandDetailToFullscreen()">全屏</button>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button>
</div>
</div> </div>
<div class="panel-body" id="detailBody"></div> <div class="panel-body" id="detailBody"></div>
<img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)"> <img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)">
@@ -854,8 +875,28 @@ function validateJournalEntryReason(){
} }
function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";} function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";}
function closeModal(){document.getElementById("imgModal").style.display="none";} function closeModal(){document.getElementById("imgModal").style.display="none";}
function forceCloseDetailModal(){document.getElementById("detailModal").style.display="none";} function setDetailModalFullscreen(on){
const modal = document.getElementById("detailModal");
if(modal){ modal.classList.toggle("fullscreen", !!on); }
}
function forceCloseDetailModal(){
const modal = document.getElementById("detailModal");
if(modal){ modal.style.display = "none"; modal.classList.remove("fullscreen"); }
}
function closeDetailModal(e){if(e.target && e.target.id==="detailModal"){forceCloseDetailModal();}} function closeDetailModal(e){if(e.target && e.target.id==="detailModal"){forceCloseDetailModal();}}
function expandDetailToFullscreen(){ setDetailModalFullscreen(true); }
function openAiInlineResultFullscreen(title, elementId){
const el = document.getElementById(elementId || "daily_result");
const text = String((el && el.innerText) || "").trim();
if(!text){ alert("暂无内容"); return; }
document.getElementById("detailTitle").innerText = title || "AI复盘";
document.getElementById("detailBody").innerText = text;
const imgEl = document.getElementById("detailImage");
imgEl.src = "";
imgEl.style.display = "none";
setDetailModalFullscreen(true);
document.getElementById("detailModal").style.display = "flex";
}
const journalCache = {}; const journalCache = {};
const reviewCache = {}; const reviewCache = {};
@@ -897,10 +938,11 @@ function openJournalDetail(id){
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
} }
setDetailModalFullscreen(false);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
function openReviewDetail(id){ function openReviewDetail(id, fullscreen){
const r = reviewCache[id]; const r = reviewCache[id];
if(!r){ return; } if(!r){ return; }
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`; document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`;
@@ -908,6 +950,7 @@ function openReviewDetail(id){
const imgEl = document.getElementById("detailImage"); const imgEl = document.getElementById("detailImage");
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
setDetailModalFullscreen(!!fullscreen);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
@@ -1096,7 +1139,8 @@ function loadReviews(){
<div style="font-size:12px;color:#9aa">${r.created_at || ""}</div> <div style="font-size:12px;color:#9aa">${r.created_at || ""}</div>
<div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div> <div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px"> <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="openReviewDetail('${r.id}')">查看全文</button> <button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', false)">查看</button>
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', true)">全屏</button>
<a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a> <a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a>
<button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button> <button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button>
</div> </div>
@@ -1113,8 +1157,10 @@ function genDaily(){
fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`}) fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("daily_result"); const el=document.getElementById("daily_result");
const wrap=document.getElementById("daily_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }
@@ -1126,8 +1172,10 @@ function genWeekly(){
fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`}) fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("weekly_result"); const el=document.getElementById("weekly_result");
const wrap=document.getElementById("weekly_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }
+56 -8
View File
@@ -83,6 +83,14 @@
.detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer} .detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer}
.detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff} .detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff}
.detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150} .detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150}
.detail-modal .panel-actions{display:flex;gap:8px;align-items:center;flex-shrink:0}
.detail-modal .panel-fs{padding:6px 10px;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:8px;cursor:pointer;font-size:.82rem}
.detail-modal.fullscreen{padding:10px}
.detail-modal.fullscreen .panel{width:100%;height:100%;max-width:none;max-height:none;display:flex;flex-direction:column;overflow:hidden}
.detail-modal.fullscreen .panel-body{flex:1;overflow:auto;min-height:0;font-size:.9rem}
.ai-result-wrap{margin-top:8px}
.ai-result-toolbar{display:flex;gap:8px;margin-top:6px}
.ai-result-toolbar .btn-fs{padding:4px 10px;font-size:.78rem;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:6px;cursor:pointer}
.table-wrap{overflow-x:auto} .table-wrap{overflow-x:auto}
.trade-dashboard{grid-column:1/-1;display:flex;flex-direction:column;gap:14px} .trade-dashboard{grid-column:1/-1;display:flex;flex-direction:column;gap:14px}
.trade-panels-row,.dual-panel-grid,.strategy-trading-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch} .trade-panels-row,.dual-panel-grid,.strategy-trading-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch}
@@ -572,8 +580,18 @@
<button type="button" onclick="genWeekly()">生成周复盘</button> <button type="button" onclick="genWeekly()">生成周复盘</button>
<button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button> <button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button>
</div> </div>
<div id="daily_result" class="ai-result" style="display:none"></div> <div class="ai-result-wrap" id="daily_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result" style="display:none"></div> <div id="daily_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('日复盘结果', 'daily_result')">全屏查看</button>
</div>
</div>
<div class="ai-result-wrap" id="weekly_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('周复盘结果', 'weekly_result')">全屏查看</button>
</div>
</div>
<div class="panel-list" style="margin-top:10px"> <div class="panel-list" style="margin-top:10px">
<div class="panel-item"> <div class="panel-item">
<strong>交易复盘记录</strong> <strong>交易复盘记录</strong>
@@ -646,7 +664,10 @@
<div class="panel" onclick="event.stopPropagation()"> <div class="panel" onclick="event.stopPropagation()">
<div class="panel-head"> <div class="panel-head">
<div class="panel-title" id="detailTitle">详情</div> <div class="panel-title" id="detailTitle">详情</div>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button> <div class="panel-actions">
<button type="button" class="panel-fs" onclick="expandDetailToFullscreen()">全屏</button>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button>
</div>
</div> </div>
<div class="panel-body" id="detailBody"></div> <div class="panel-body" id="detailBody"></div>
<img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)"> <img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)">
@@ -693,7 +714,27 @@ function validateJournalEntryReason(){
} }
function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";} function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";}
function closeModal(){document.getElementById("imgModal").style.display="none";} function closeModal(){document.getElementById("imgModal").style.display="none";}
function forceCloseDetailModal(){document.getElementById("detailModal").style.display="none";} function setDetailModalFullscreen(on){
const modal = document.getElementById("detailModal");
if(modal){ modal.classList.toggle("fullscreen", !!on); }
}
function forceCloseDetailModal(){
const modal = document.getElementById("detailModal");
if(modal){ modal.style.display = "none"; modal.classList.remove("fullscreen"); }
}
function expandDetailToFullscreen(){ setDetailModalFullscreen(true); }
function openAiInlineResultFullscreen(title, elementId){
const el = document.getElementById(elementId || "daily_result");
const text = String((el && el.innerText) || "").trim();
if(!text){ alert("暂无内容"); return; }
document.getElementById("detailTitle").innerText = title || "AI复盘";
document.getElementById("detailBody").innerText = text;
const imgEl = document.getElementById("detailImage");
imgEl.src = "";
imgEl.style.display = "none";
setDetailModalFullscreen(true);
document.getElementById("detailModal").style.display = "flex";
}
function fmtU2(n){ function fmtU2(n){
if(n === null || n === undefined || n === "") return "-"; if(n === null || n === undefined || n === "") return "-";
const x = Number(n); const x = Number(n);
@@ -771,10 +812,11 @@ function openJournalDetail(id){
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
} }
setDetailModalFullscreen(false);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
function openReviewDetail(id){ function openReviewDetail(id, fullscreen){
const r = reviewCache[id]; const r = reviewCache[id];
if(!r){ return; } if(!r){ return; }
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`; document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`;
@@ -782,6 +824,7 @@ function openReviewDetail(id){
const imgEl = document.getElementById("detailImage"); const imgEl = document.getElementById("detailImage");
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
setDetailModalFullscreen(!!fullscreen);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
@@ -929,7 +972,8 @@ function loadReviews(){
<div style="font-size:12px;color:#9aa">${r.created_at || ""}</div> <div style="font-size:12px;color:#9aa">${r.created_at || ""}</div>
<div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div> <div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px"> <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="openReviewDetail('${r.id}')">查看全文</button> <button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', false)">查看</button>
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', true)">全屏</button>
<a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a> <a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a>
<button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button> <button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button>
</div> </div>
@@ -946,8 +990,10 @@ function genDaily(){
fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`}) fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("daily_result"); const el=document.getElementById("daily_result");
const wrap=document.getElementById("daily_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }
@@ -959,8 +1005,10 @@ function genWeekly(){
fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`}) fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("weekly_result"); const el=document.getElementById("weekly_result");
const wrap=document.getElementById("weekly_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }
+56 -8
View File
@@ -82,6 +82,14 @@
.detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer} .detail-modal .panel-close{padding:6px 10px;background:#2f2134;color:#ffb2b2;border:none;border-radius:8px;cursor:pointer}
.detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff} .detail-modal .panel-body{white-space:pre-wrap;line-height:1.5;font-size:.86rem;color:#e5e9ff}
.detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150} .detail-modal .panel-image{margin-top:10px;max-width:min(100%,680px);border-radius:8px;cursor:pointer;border:1px solid #2a3150}
.detail-modal .panel-actions{display:flex;gap:8px;align-items:center;flex-shrink:0}
.detail-modal .panel-fs{padding:6px 10px;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:8px;cursor:pointer;font-size:.82rem}
.detail-modal.fullscreen{padding:10px}
.detail-modal.fullscreen .panel{width:100%;height:100%;max-width:none;max-height:none;display:flex;flex-direction:column;overflow:hidden}
.detail-modal.fullscreen .panel-body{flex:1;overflow:auto;min-height:0;font-size:.9rem}
.ai-result-wrap{margin-top:8px}
.ai-result-toolbar{display:flex;gap:8px;margin-top:6px}
.ai-result-toolbar .btn-fs{padding:4px 10px;font-size:.78rem;background:#1f3a5a;color:#8fc8ff;border:none;border-radius:6px;cursor:pointer}
.table-wrap{overflow-x:auto} .table-wrap{overflow-x:auto}
.dual-panel-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch} .dual-panel-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch}
.dual-panel-grid .card{height:100%;display:flex;flex-direction:column} .dual-panel-grid .card{height:100%;display:flex;flex-direction:column}
@@ -755,8 +763,18 @@
<button type="button" onclick="genWeekly()">生成周复盘</button> <button type="button" onclick="genWeekly()">生成周复盘</button>
<button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button> <button type="button" onclick="exportWeeklyBundleMd()" style="background:#1f3a5a">导出当周复盘MD</button>
</div> </div>
<div id="daily_result" class="ai-result" style="display:none"></div> <div class="ai-result-wrap" id="daily_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result" style="display:none"></div> <div id="daily_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('日复盘结果', 'daily_result')">全屏查看</button>
</div>
</div>
<div class="ai-result-wrap" id="weekly_result_wrap" style="display:none">
<div id="weekly_result" class="ai-result"></div>
<div class="ai-result-toolbar">
<button type="button" class="btn-fs" onclick="openAiInlineResultFullscreen('周复盘结果', 'weekly_result')">全屏查看</button>
</div>
</div>
<div class="panel-list" style="margin-top:10px"> <div class="panel-list" style="margin-top:10px">
<div class="panel-item"> <div class="panel-item">
<strong>交易复盘记录</strong> <strong>交易复盘记录</strong>
@@ -816,7 +834,10 @@
<div class="panel" onclick="event.stopPropagation()"> <div class="panel" onclick="event.stopPropagation()">
<div class="panel-head"> <div class="panel-head">
<div class="panel-title" id="detailTitle">详情</div> <div class="panel-title" id="detailTitle">详情</div>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button> <div class="panel-actions">
<button type="button" class="panel-fs" onclick="expandDetailToFullscreen()">全屏</button>
<button type="button" class="panel-close" onclick="forceCloseDetailModal()">关闭</button>
</div>
</div> </div>
<div class="panel-body" id="detailBody"></div> <div class="panel-body" id="detailBody"></div>
<img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)"> <img id="detailImage" class="panel-image" src="" alt="detail-image" style="display:none" onclick="showImage(this.src)">
@@ -863,8 +884,28 @@ function validateJournalEntryReason(){
} }
function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";} function showImage(src){document.getElementById("bigImg").src=src;document.getElementById("imgModal").style.display="flex";}
function closeModal(){document.getElementById("imgModal").style.display="none";} function closeModal(){document.getElementById("imgModal").style.display="none";}
function forceCloseDetailModal(){document.getElementById("detailModal").style.display="none";} function setDetailModalFullscreen(on){
const modal = document.getElementById("detailModal");
if(modal){ modal.classList.toggle("fullscreen", !!on); }
}
function forceCloseDetailModal(){
const modal = document.getElementById("detailModal");
if(modal){ modal.style.display = "none"; modal.classList.remove("fullscreen"); }
}
function closeDetailModal(e){if(e.target && e.target.id==="detailModal"){forceCloseDetailModal();}} function closeDetailModal(e){if(e.target && e.target.id==="detailModal"){forceCloseDetailModal();}}
function expandDetailToFullscreen(){ setDetailModalFullscreen(true); }
function openAiInlineResultFullscreen(title, elementId){
const el = document.getElementById(elementId || "daily_result");
const text = String((el && el.innerText) || "").trim();
if(!text){ alert("暂无内容"); return; }
document.getElementById("detailTitle").innerText = title || "AI复盘";
document.getElementById("detailBody").innerText = text;
const imgEl = document.getElementById("detailImage");
imgEl.src = "";
imgEl.style.display = "none";
setDetailModalFullscreen(true);
document.getElementById("detailModal").style.display = "flex";
}
const journalCache = {}; const journalCache = {};
const reviewCache = {}; const reviewCache = {};
@@ -906,10 +947,11 @@ function openJournalDetail(id){
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
} }
setDetailModalFullscreen(false);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
function openReviewDetail(id){ function openReviewDetail(id, fullscreen){
const r = reviewCache[id]; const r = reviewCache[id];
if(!r){ return; } if(!r){ return; }
document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`; document.getElementById("detailTitle").innerText = `${r.review_type === "daily" ? "日复盘" : "周复盘"}${r.target_date || "-"}`;
@@ -917,6 +959,7 @@ function openReviewDetail(id){
const imgEl = document.getElementById("detailImage"); const imgEl = document.getElementById("detailImage");
imgEl.src = ""; imgEl.src = "";
imgEl.style.display = "none"; imgEl.style.display = "none";
setDetailModalFullscreen(!!fullscreen);
document.getElementById("detailModal").style.display = "flex"; document.getElementById("detailModal").style.display = "flex";
} }
@@ -1105,7 +1148,8 @@ function loadReviews(){
<div style="font-size:12px;color:#9aa">${r.created_at || ""}</div> <div style="font-size:12px;color:#9aa">${r.created_at || ""}</div>
<div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div> <div style="margin-top:4px;color:#c9d2ff">${shortText || "(空)"}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:6px"> <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="openReviewDetail('${r.id}')">查看全文</button> <button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', false)">查看</button>
<button type="button" class="btn-del" style="border:none;cursor:pointer;background:#1f3a5a;color:#8fc8ff" onclick="openReviewDetail('${r.id}', true)">全屏</button>
<a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a> <a class="btn-del" style="text-decoration:none;background:#1f3a5a;color:#8fc8ff" href="/export/review_md/${r.id}">导出MD</a>
<button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button> <button type="button" class="btn-del" onclick="deleteReview('${r.id}')">删除</button>
</div> </div>
@@ -1122,8 +1166,10 @@ function genDaily(){
fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`}) fetch("/ai_daily_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`date=${encodeURIComponent(d)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("daily_result"); const el=document.getElementById("daily_result");
const wrap=document.getElementById("daily_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }
@@ -1135,8 +1181,10 @@ function genWeekly(){
fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`}) fetch("/ai_weekly_review",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`start_date=${encodeURIComponent(s)}&end_date=${encodeURIComponent(e)}`})
.then(r=>r.json()).then(data=>{ .then(r=>r.json()).then(data=>{
const el=document.getElementById("weekly_result"); const el=document.getElementById("weekly_result");
const wrap=document.getElementById("weekly_result_wrap");
el.innerText=data.result; el.innerText=data.result;
el.style.display="block"; if(wrap){ wrap.style.display="block"; }
else { el.style.display="block"; }
loadReviews(); loadReviews();
}); });
} }