|
@@ -767,6 +767,29 @@
|
|
|
|
|
|
|
|
// ---------- 智能问数 · 机器人(仅经营分析页面) ----------
|
|
// ---------- 智能问数 · 机器人(仅经营分析页面) ----------
|
|
|
|
|
|
|
|
|
|
+ // 对话历史按主体持久化到 localStorage,刷新/重开后可继续追问
|
|
|
|
|
+ const CHAT_LS_KEY = 'shanReports.chat.v1';
|
|
|
|
|
+ const chatStoreKey = () => state.entity || '_';
|
|
|
|
|
+
|
|
|
|
|
+ function loadChatForEntity() {
|
|
|
|
|
+ const all = LS.get(CHAT_LS_KEY, {});
|
|
|
|
|
+ state.chat.messages = Array.isArray(all[chatStoreKey()]) ? all[chatStoreKey()] : [];
|
|
|
|
|
+ if (state.chat.open) renderChatMessages();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function saveChatForEntity() {
|
|
|
|
|
+ const key = chatStoreKey();
|
|
|
|
|
+ const all = LS.get(CHAT_LS_KEY, {});
|
|
|
|
|
+ const trimmed = state.chat.messages.filter((m) => !m.pending).slice(-20);
|
|
|
|
|
+ const attempt = (payload) => {
|
|
|
|
|
+ try { localStorage.setItem(CHAT_LS_KEY, JSON.stringify(payload)); return true; } catch { return false; }
|
|
|
|
|
+ };
|
|
|
|
|
+ if (attempt({ ...all, [key]: trimmed })) return;
|
|
|
|
|
+ // 图表行数据可能撑爆配额 → 退化为只存文字与图表配置
|
|
|
|
|
+ const slim = { ...all, [key]: trimmed.map((m) => ({ ...m, rows: undefined })) };
|
|
|
|
|
+ if (!(attempt(slim) || attempt({ [key]: slim[key].slice(-10) }))) { /* 配额不足则放弃持久化 */ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function chatVisibleFor(view) { return view === 'reports' || view === 'analysis'; }
|
|
function chatVisibleFor(view) { return view === 'reports' || view === 'analysis'; }
|
|
|
|
|
|
|
|
function setChatOpen(open) {
|
|
function setChatOpen(open) {
|
|
@@ -774,6 +797,7 @@
|
|
|
$('chatbotPanel').hidden = !open;
|
|
$('chatbotPanel').hidden = !open;
|
|
|
$('chatbotFab').classList.toggle('active', open);
|
|
$('chatbotFab').classList.toggle('active', open);
|
|
|
if (open) {
|
|
if (open) {
|
|
|
|
|
+ loadChatForEntity();
|
|
|
$('chatbotFab').hidden = false;
|
|
$('chatbotFab').hidden = false;
|
|
|
setTimeout(() => $('chatbotInput').focus(), 60);
|
|
setTimeout(() => $('chatbotInput').focus(), 60);
|
|
|
scrollChatBottom();
|
|
scrollChatBottom();
|
|
@@ -907,6 +931,7 @@
|
|
|
};
|
|
};
|
|
|
if (pendingIdx >= 0) state.chat.messages.splice(pendingIdx, 1, msg);
|
|
if (pendingIdx >= 0) state.chat.messages.splice(pendingIdx, 1, msg);
|
|
|
else state.chat.messages.push(msg);
|
|
else state.chat.messages.push(msg);
|
|
|
|
|
+ saveChatForEntity();
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
const pendingIdx = state.chat.messages.findIndex((m) => m.pending);
|
|
const pendingIdx = state.chat.messages.findIndex((m) => m.pending);
|
|
|
const errMsg = { role: 'assistant', text: '出错了:' + e.message, error: true };
|
|
const errMsg = { role: 'assistant', text: '出错了:' + e.message, error: true };
|
|
@@ -1049,6 +1074,9 @@
|
|
|
$('btnChatClose').addEventListener('click', () => setChatOpen(false));
|
|
$('btnChatClose').addEventListener('click', () => setChatOpen(false));
|
|
|
$('btnChatClear').addEventListener('click', () => {
|
|
$('btnChatClear').addEventListener('click', () => {
|
|
|
state.chat.messages = [];
|
|
state.chat.messages = [];
|
|
|
|
|
+ const all = LS.get(CHAT_LS_KEY, {});
|
|
|
|
|
+ delete all[state.entity || '_'];
|
|
|
|
|
+ LS.set(CHAT_LS_KEY, all);
|
|
|
renderChatMessages();
|
|
renderChatMessages();
|
|
|
});
|
|
});
|
|
|
$('btnChatSend').addEventListener('click', () => sendChat($('chatbotInput').value));
|
|
$('btnChatSend').addEventListener('click', () => sendChat($('chatbotInput').value));
|