feat(hub): add archive quote AI coach review from inner light page

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-11 21:10:39 +08:00
parent bb8bb3ae34
commit 180aff5310
7 changed files with 276 additions and 4 deletions
+5
View File
@@ -5615,8 +5615,13 @@ body.funds-fullscreen-open {
}
.archive-quote-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.archive-quote-ai-btn {
color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 35%, var(--border-soft));
}
.archive-main-panel {
display: flex;
flex-direction: column;
+71
View File
@@ -3497,9 +3497,70 @@
}
}
const ARCHIVE_QUOTE_AI_KEY = "hub_archive_quote_ai";
async function consumeArchiveQuoteAiPending() {
let raw = "";
try {
raw = sessionStorage.getItem(ARCHIVE_QUOTE_AI_KEY) || "";
} catch (_) {
return;
}
if (!raw) return;
sessionStorage.removeItem(ARCHIVE_QUOTE_AI_KEY);
let payload;
try {
payload = JSON.parse(raw);
} catch (_) {
return;
}
const content = String((payload && payload.content) || "").trim();
const quoteDate = String((payload && payload.quote_date) || "").trim();
if (!content) return;
const input = document.getElementById("ai-chat-input");
if (input) input.value = content;
updateAiBotTabs("trading");
if (isMobileAiLayout()) {
localStorage.setItem(AI_MOBILE_TAB_KEY, "trading");
applyAiMobileTab("trading");
}
setAiChatBusy(true);
renderAiChatMessages(aiChatSessionCache, {
pendingUser: content,
thinking: true,
});
try {
const r = await apiFetch("/api/ai/chat/archive-quote", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ quote_date: quoteDate, content }),
});
const j = await r.json();
if (!r.ok) throw new Error(j.detail || j.msg || "发送失败");
aiChatSessionCache = j.session || null;
aiChatSessionsCache = j.sessions || aiChatSessionsCache;
renderAiChatMessages(aiChatSessionCache);
renderAiChatHistory(aiChatSessionsCache);
if (input) input.value = "";
showToast("复盘语录已发送给交易教练");
} catch (e) {
showToast(String(e), true);
try {
await loadAiChatSession();
} catch (_) {
renderAiChatMessages(aiChatSessionCache);
}
} finally {
setAiChatBusy(false);
}
}
async function loadAiPage() {
applyAiMobileTab();
await loadAiChatSession();
await consumeArchiveQuoteAiPending();
const mobTab = normalizeAiMobileTab(localStorage.getItem(AI_MOBILE_TAB_KEY) || "trading");
if (isMobileAiLayout() && AI_MOBILE_CHAT_TABS.has(mobTab)) {
const input = document.getElementById("ai-chat-input");
@@ -3665,6 +3726,16 @@
window.addEventListener("popstate", setActiveNav);
}
window.hubNavigateTo = function hubNavigateTo(path) {
const href = String(path || "/").split("?")[0] || "/";
if (href === window.location.pathname) {
setActiveNav();
return;
}
history.pushState({}, "", href);
setActiveNav();
};
window.hubOpenMonitorExpand = function hubOpenMonitorExpand(exId) {
const id = String(exId || "").trim();
if (!id) return;
+37
View File
@@ -566,6 +566,9 @@
'<button type="button" class="archive-del-btn archive-quote-del-btn" data-id="' +
q.id +
'">删除</button>' +
'<button type="button" class="ghost archive-quote-ai-btn" data-id="' +
q.id +
'">AI对话</button>' +
"</div></div>"
: "") +
"</div>"
@@ -591,6 +594,12 @@
void deleteQuote(btn.getAttribute("data-id"));
});
});
elQuotesList.querySelectorAll(".archive-quote-ai-btn").forEach(function (btn) {
btn.addEventListener("click", function (ev) {
ev.stopPropagation();
startQuoteAiChat(btn.getAttribute("data-id"));
});
});
}
async function loadQuotes() {
@@ -645,6 +654,34 @@
setStatus("语录已保存");
}
const ARCHIVE_QUOTE_AI_KEY = "hub_archive_quote_ai";
function startQuoteAiChat(quoteId) {
const q = findQuote(quoteId);
const content = q && String(q.content || "").trim();
if (!q || !content) {
setStatus("语录内容为空,无法发起 AI 对话");
return;
}
try {
sessionStorage.setItem(
ARCHIVE_QUOTE_AI_KEY,
JSON.stringify({
quote_date: q.quote_date || "",
content: content,
})
);
} catch (_) {
setStatus("无法保存跳转数据");
return;
}
if (typeof window.hubNavigateTo === "function") {
window.hubNavigateTo("/ai");
return;
}
location.href = "/ai";
}
async function deleteQuote(id) {
if (!id || !window.confirm("确定删除这条复盘语录?")) return;
const r = await apiFetch("/api/archive/quotes/" + id, { method: "DELETE" });
+3 -3
View File
@@ -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-archive-mobile" />
<link rel="stylesheet" href="/assets/app.css?v=20260612-archive-ai-chat" />
<link rel="stylesheet" href="/assets/dashboard.css?v=20260612-dash-monitor-count" />
</head>
<body>
@@ -589,11 +589,11 @@
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
<script src="/assets/chart.js?v=20260609-market-day-split"></script>
<script src="/assets/archive.js?v=20260612-archive-quotes-v3"></script>
<script src="/assets/archive.js?v=20260612-archive-ai-chat"></script>
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></script>
<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-table"></script>
<script src="/assets/app.js?v=20260612-archive-ai-chat"></script>
</body>
</html>