feat(ai-assistant): 按场景隔离会话存储

- 将 sessionStore 从对象改为数组,每个场景独立存储
  - sessionCacheKey 改为 sessionCacheKeyPrefix,拼接场景 key 动态生成
  - initSession 改为异步方法,切换场景时按需加载对应数据
  - 使用防抖更新 displayWelcomePrompts,避免场景切换时闪屏
  - 修复输入框文字颜色样式
This commit is contained in:
kuaifan 2026-01-17 02:24:31 +00:00
parent acb9cd317c
commit 347465fc4d

View File

@ -99,7 +99,7 @@
</div> </div>
<div class="ai-assistant-welcome-prompts"> <div class="ai-assistant-welcome-prompts">
<div <div
v-for="(prompt, index) in welcomePrompts" v-for="(prompt, index) in displayWelcomePrompts"
:key="index" :key="index"
class="ai-assistant-prompt-card" class="ai-assistant-prompt-card"
@click="onPromptClick(prompt)"> @click="onPromptClick(prompt)">
@ -154,6 +154,7 @@
<script> <script>
import Vue from "vue"; import Vue from "vue";
import {debounce} from "lodash";
import emitter from "../../store/events"; import emitter from "../../store/events";
import {SSEClient} from "../../utils"; import {SSEClient} from "../../utils";
import {AIBotMap, AIModelNames} from "../../utils/ai"; import {AIBotMap, AIModelNames} from "../../utils/ai";
@ -214,18 +215,27 @@ export default {
// //
sessionEnabled: false, sessionEnabled: false,
sessionStore: {}, sessionStore: [],
currentSessionKey: 'default', currentSessionKey: 'default',
currentSessionId: null, currentSessionId: null,
currentSceneKey: null, currentSceneKey: null,
sessionCacheKey: 'aiAssistant.sessions', sessionCacheKeyPrefix: 'aiAssistant.sessions',
maxSessionsPerKey: 20, maxSessionsPerKey: 20,
sessionStoreLoaded: false,
//
displayWelcomePrompts: [],
} }
}, },
created() {
//
this.refreshWelcomePromptsDebounced = debounce(() => {
this.displayWelcomePrompts = getWelcomePrompts(this.$store, this.$route?.params || {});
}, 100);
},
mounted() { mounted() {
emitter.on('openAIAssistant', this.onOpenAIAssistant); emitter.on('openAIAssistant', this.onOpenAIAssistant);
this.loadCachedModel(); this.loadCachedModel();
this.loadSessionStore();
this.mountFloatButton(); this.mountFloatButton();
}, },
beforeDestroy() { beforeDestroy() {
@ -233,6 +243,7 @@ export default {
this.clearActiveSSEClients(); this.clearActiveSSEClients();
this.clearAutoSubmitTimer(); this.clearAutoSubmitTimer();
this.unmountFloatButton(); this.unmountFloatButton();
this.refreshWelcomePromptsDebounced?.cancel();
}, },
computed: { computed: {
selectedModelOption({modelMap, inputModel}) { selectedModelOption({modelMap, inputModel}) {
@ -242,19 +253,30 @@ export default {
return this.responses.length === 0; return this.responses.length === 0;
}, },
currentSessionList() { currentSessionList() {
return this.sessionStore[this.currentSessionKey] || []; return this.sessionStore || [];
}, },
hasSessionHistory() { hasSessionHistory() {
return this.currentSessionList.length > 0; return this.currentSessionList.length > 0;
}, },
welcomePrompts() { // watch displayWelcomePrompts
return getWelcomePrompts(this.$store, this.$route?.params || {}); welcomePromptsKey() {
//
const routeName = this.$store.state.routeName;
const dialogId = this.$store.state.dialogId;
const projectId = this.$store.getters.projectData?.id;
return `${routeName}|${dialogId}|${projectId}`;
}, },
}, },
watch: { watch: {
inputModel(value) { inputModel(value) {
this.saveModelCache(value); this.saveModelCache(value);
}, },
welcomePromptsKey: {
handler() {
this.refreshWelcomePromptsDebounced();
},
immediate: true,
},
}, },
methods: { methods: {
/** /**
@ -325,7 +347,7 @@ export default {
/** /**
* 实际执行打开助手的逻辑 * 实际执行打开助手的逻辑
*/ */
doOpenAssistant(params, displayMode) { async doOpenAssistant(params, displayMode) {
// //
this.displayMode = displayMode; this.displayMode = displayMode;
this.inputValue = params.value || ''; this.inputValue = params.value || '';
@ -344,7 +366,7 @@ export default {
this.pendingAutoSubmit = !!params.autoSubmit; this.pendingAutoSubmit = !!params.autoSubmit;
// //
this.initSession(params.sessionKey, params.sceneKey, params.resumeSession); await this.initSession(params.sessionKey, params.sceneKey, params.resumeSession);
this.showModal = true; this.showModal = true;
this.fetchModelOptions(); this.fetchModelOptions();
@ -1011,30 +1033,46 @@ export default {
// ==================== ==================== // ==================== ====================
/** /**
* 加载持久化的会话数据 * 获取指定场景的缓存 key
*/ */
async loadSessionStore() { getSessionCacheKey(sessionKey) {
try { return `${this.sessionCacheKeyPrefix}_${sessionKey || 'default'}`;
const stored = await $A.IDBString(this.sessionCacheKey);
if (stored) {
this.sessionStore = JSON.parse(stored);
}
} catch (e) {
this.sessionStore = {};
}
}, },
/** /**
* 持久化会话数据 * 加载指定场景的会话数据
*/
async loadSessionStore(sessionKey) {
const cacheKey = this.getSessionCacheKey(sessionKey);
try {
const stored = await $A.IDBString(cacheKey);
if (stored) {
this.sessionStore = JSON.parse(stored);
if (!Array.isArray(this.sessionStore)) {
this.sessionStore = [];
}
} else {
this.sessionStore = [];
}
} catch (e) {
this.sessionStore = [];
}
this.sessionStoreLoaded = true;
},
/**
* 持久化当前场景的会话数据
*/ */
saveSessionStore() { saveSessionStore() {
const cacheKey = this.getSessionCacheKey(this.currentSessionKey);
try { try {
$A.IDBSave(this.sessionCacheKey, JSON.stringify(this.sessionStore)); $A.IDBSave(cacheKey, JSON.stringify(this.sessionStore));
} catch (e) { } catch (e) {
console.warn('[AIAssistant] Failed to save session store:', e); console.warn('[AIAssistant] Failed to save session store:', e);
} }
}, },
/** /**
* 生成会话 ID * 生成会话 ID
*/ */
@ -1059,10 +1097,10 @@ export default {
}, },
/** /**
* 获取指定场景的会话列表 * 获取当前场景的会话列表
*/ */
getSessionList(sessionKey) { getSessionList() {
return this.sessionStore[sessionKey] || []; return this.sessionStore || [];
}, },
/** /**
@ -1071,7 +1109,7 @@ export default {
* @param {string} sceneKey - 场景标识用于判断是否恢复会话 * @param {string} sceneKey - 场景标识用于判断是否恢复会话
* @param {number} resumeTimeout - 恢复超时时间默认1天 * @param {number} resumeTimeout - 恢复超时时间默认1天
*/ */
initSession(sessionKey, sceneKey = null, resumeTimeout = 86400) { async initSession(sessionKey, sceneKey = null, resumeTimeout = 86400) {
// //
if (this.responses.length > 0) { if (this.responses.length > 0) {
this.saveCurrentSession(); this.saveCurrentSession();
@ -1081,11 +1119,15 @@ export default {
this.currentSceneKey = sceneKey; this.currentSceneKey = sceneKey;
if (this.sessionEnabled) { if (this.sessionEnabled) {
this.currentSessionKey = sessionKey; //
if (this.currentSessionKey !== sessionKey || !this.sessionStoreLoaded) {
this.currentSessionKey = sessionKey;
await this.loadSessionStore(sessionKey);
}
// sceneKey // sceneKey
if (sceneKey) { if (sceneKey) {
const sessions = this.getSessionList(sessionKey); const sessions = this.getSessionList();
// //
const matchedSession = sessions.find(s => s.sceneKey === sceneKey); const matchedSession = sessions.find(s => s.sceneKey === sceneKey);
if (matchedSession) { if (matchedSession) {
@ -1108,6 +1150,7 @@ export default {
this.currentSessionId = null; this.currentSessionId = null;
this.currentSceneKey = null; this.currentSceneKey = null;
this.responses = []; this.responses = [];
this.sessionStoreLoaded = false;
} }
}, },
@ -1132,31 +1175,30 @@ export default {
return; return;
} }
const sessionKey = this.currentSessionKey; // sessionStore
if (!this.sessionStore[sessionKey]) { if (!Array.isArray(this.sessionStore)) {
this.$set(this.sessionStore, sessionKey, []); this.sessionStore = [];
} }
const sessions = this.sessionStore[sessionKey]; const existingIndex = this.sessionStore.findIndex(s => s.id === this.currentSessionId);
const existingIndex = sessions.findIndex(s => s.id === this.currentSessionId);
const sessionData = { const sessionData = {
id: this.currentSessionId, id: this.currentSessionId,
title: this.generateSessionTitle(this.responses), title: this.generateSessionTitle(this.responses),
responses: JSON.parse(JSON.stringify(this.responses)), responses: JSON.parse(JSON.stringify(this.responses)),
sceneKey: this.currentSceneKey, sceneKey: this.currentSceneKey,
createdAt: existingIndex > -1 ? sessions[existingIndex].createdAt : Date.now(), createdAt: existingIndex > -1 ? this.sessionStore[existingIndex].createdAt : Date.now(),
updatedAt: Date.now(), updatedAt: Date.now(),
}; };
if (existingIndex > -1) { if (existingIndex > -1) {
sessions[existingIndex] = sessionData; this.sessionStore.splice(existingIndex, 1, sessionData);
} else { } else {
sessions.unshift(sessionData); this.sessionStore.unshift(sessionData);
} }
// //
if (sessions.length > this.maxSessionsPerKey) { if (this.sessionStore.length > this.maxSessionsPerKey) {
sessions.splice(this.maxSessionsPerKey); this.sessionStore.splice(this.maxSessionsPerKey);
} }
this.saveSessionStore(); this.saveSessionStore();
@ -1166,7 +1208,7 @@ export default {
* 加载指定会话 * 加载指定会话
*/ */
loadSession(sessionId) { loadSession(sessionId) {
const sessions = this.getSessionList(this.currentSessionKey); const sessions = this.getSessionList();
const session = sessions.find(s => s.id === sessionId); const session = sessions.find(s => s.id === sessionId);
if (session) { if (session) {
// //
@ -1200,10 +1242,9 @@ export default {
* 删除指定会话 * 删除指定会话
*/ */
deleteSession(sessionId) { deleteSession(sessionId) {
const sessions = this.getSessionList(this.currentSessionKey); const index = this.sessionStore.findIndex(s => s.id === sessionId);
const index = sessions.findIndex(s => s.id === sessionId);
if (index > -1) { if (index > -1) {
sessions.splice(index, 1); this.sessionStore.splice(index, 1);
this.saveSessionStore(); this.saveSessionStore();
// //
if (this.currentSessionId === sessionId) { if (this.currentSessionId === sessionId) {
@ -1220,7 +1261,7 @@ export default {
title: this.$L('清空历史会话'), title: this.$L('清空历史会话'),
content: this.$L('确定要清空当前场景的所有历史会话吗?'), content: this.$L('确定要清空当前场景的所有历史会话吗?'),
onOk: () => { onOk: () => {
this.$set(this.sessionStore, this.currentSessionKey, []); this.sessionStore = [];
this.saveSessionStore(); this.saveSessionStore();
this.createNewSession(false); this.createNewSession(false);
} }
@ -1442,6 +1483,7 @@ export default {
gap: 12px; gap: 12px;
.ivu-input { .ivu-input {
color: #333333;
background-color: transparent; background-color: transparent;
border: 0; border: 0;
border-radius: 0; border-radius: 0;