feat(hub): add archive quote AI coach review from inner light page
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" });
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user