diff --git a/dashboard/src/components/chat/ConfigSelector.vue b/dashboard/src/components/chat/ConfigSelector.vue index d77e91072d..d53e3ed779 100644 --- a/dashboard/src/components/chat/ConfigSelector.vue +++ b/dashboard/src/components/chat/ConfigSelector.vue @@ -77,6 +77,11 @@ import { computed, onMounted, ref, watch } from 'vue'; import axios from 'axios'; import { useToast } from '@/utils/toast'; import { useModuleI18n } from '@/i18n/composables'; +import { + getStoredDashboardUsername, + getStoredSelectedChatConfigId, + setStoredSelectedChatConfigId +} from '@/utils/chatConfigBinding'; interface ConfigInfo { id: string; @@ -88,8 +93,6 @@ interface ConfigChangedPayload { agentRunnerType: string; } -const STORAGE_KEY = 'chat.selectedConfigId'; - const props = withDefaults(defineProps<{ sessionId?: string | null; platformId?: string; @@ -128,7 +131,7 @@ const hasActiveSession = computed(() => !!normalizedSessionId.value); const messageType = computed(() => (props.isGroup ? 'GroupMessage' : 'FriendMessage')); -const username = computed(() => localStorage.getItem('user') || 'guest'); +const username = computed(() => getStoredDashboardUsername()); const sessionKey = computed(() => { if (!normalizedSessionId.value) { @@ -265,10 +268,10 @@ async function confirmSelection() { } const previousId = selectedConfigId.value; await setSelection(tempSelectedConfig.value); - localStorage.setItem(STORAGE_KEY, tempSelectedConfig.value); + setStoredSelectedChatConfigId(tempSelectedConfig.value); const applied = await applySelectionToBackend(tempSelectedConfig.value); if (!applied) { - localStorage.setItem(STORAGE_KEY, previousId); + setStoredSelectedChatConfigId(previousId); await setSelection(previousId); } dialog.value = false; @@ -287,7 +290,7 @@ async function syncSelectionForSession() { await fetchRoutingEntries(); const resolved = resolveConfigId(targetUmo.value); await setSelection(resolved); - localStorage.setItem(STORAGE_KEY, resolved); + setStoredSelectedChatConfigId(resolved); } watch( @@ -299,7 +302,7 @@ watch( onMounted(async () => { await fetchConfigList(); - const stored = props.initialConfigId || localStorage.getItem(STORAGE_KEY) || 'default'; + const stored = props.initialConfigId || getStoredSelectedChatConfigId(); selectedConfigId.value = stored; await setSelection(stored); await syncSelectionForSession(); diff --git a/dashboard/src/components/chat/StandaloneChat.vue b/dashboard/src/components/chat/StandaloneChat.vue index 25ca7faf93..db762dabf7 100644 --- a/dashboard/src/components/chat/StandaloneChat.vue +++ b/dashboard/src/components/chat/StandaloneChat.vue @@ -70,6 +70,7 @@ import { useMessages } from '@/composables/useMessages'; import { useMediaHandling } from '@/composables/useMediaHandling'; import { useRecording } from '@/composables/useRecording'; import { useToast } from '@/utils/toast'; +import { buildWebchatUmoDetails } from '@/utils/chatConfigBinding'; interface Props { configId?: string | null; @@ -82,6 +83,7 @@ const props = withDefaults(defineProps(), { const { t } = useI18n(); const { error: showError } = useToast(); + // UI 状态 const imagePreviewDialog = ref(false); const previewImageUrl = ref(''); @@ -90,11 +92,33 @@ const previewImageUrl = ref(''); const currSessionId = ref(''); const getCurrentSession = computed(() => null); // 独立测试模式不需要会话信息 +async function bindConfigToSession(sessionId: string) { + const confId = (props.configId || '').trim(); + if (!confId || confId === 'default') { + return; + } + + const umoDetails = buildWebchatUmoDetails(sessionId, false); + + await axios.post('/api/config/umo_abconf_route/update', { + umo: umoDetails.umo, + conf_id: confId + }); +} + async function newSession() { try { const response = await axios.get('/api/chat/new_session'); const sessionId = response.data.data.session_id; + + try { + await bindConfigToSession(sessionId); + } catch (err) { + console.error('Failed to bind config to session', err); + } + currSessionId.value = sessionId; + return sessionId; } catch (err) { console.error(err); diff --git a/dashboard/src/composables/useSessions.ts b/dashboard/src/composables/useSessions.ts index 1cbe6284dc..decde8d9e9 100644 --- a/dashboard/src/composables/useSessions.ts +++ b/dashboard/src/composables/useSessions.ts @@ -1,6 +1,7 @@ import { ref, computed } from 'vue'; import axios from 'axios'; import { useRouter } from 'vue-router'; +import { buildWebchatUmoDetails, getStoredSelectedChatConfigId } from '@/utils/chatConfigBinding'; export interface Session { session_id: string; @@ -62,10 +63,25 @@ export function useSessions(chatboxMode: boolean = false) { async function newSession() { try { + const selectedConfigId = getStoredSelectedChatConfigId(); const response = await axios.get('/api/chat/new_session'); const sessionId = response.data.data.session_id; + const platformId = response.data.data.platform_id; + currSessionId.value = sessionId; + if (selectedConfigId && selectedConfigId !== 'default' && platformId === 'webchat') { + try { + const umoDetails = buildWebchatUmoDetails(sessionId, false); + await axios.post('/api/config/umo_abconf_route/update', { + umo: umoDetails.umo, + conf_id: selectedConfigId + }); + } catch (err) { + console.error('Failed to bind config to session', err); + } + } + // 更新 URL const basePath = chatboxMode ? '/chatbox' : '/chat'; router.push(`${basePath}/${sessionId}`); diff --git a/dashboard/src/utils/chatConfigBinding.ts b/dashboard/src/utils/chatConfigBinding.ts new file mode 100644 index 0000000000..192f25d9cc --- /dev/null +++ b/dashboard/src/utils/chatConfigBinding.ts @@ -0,0 +1,60 @@ +export const CHAT_SELECTED_CONFIG_STORAGE_KEY = 'chat.selectedConfigId'; + +export type ChatMessageType = 'FriendMessage' | 'GroupMessage'; + +export interface WebchatUmoDetails { + platformId: string; + messageType: ChatMessageType; + username: string; + sessionKey: string; + umo: string; +} + +function getFromLocalStorage(key: string, fallback: string): string { + try { + if (typeof localStorage === 'undefined') { + return fallback; + } + const value = localStorage.getItem(key); + return value == null ? fallback : value; + } catch { + return fallback; + } +} + +function setToLocalStorage(key: string, value: string): void { + try { + if (typeof localStorage === 'undefined') { + return; + } + localStorage.setItem(key, value); + } catch { + // Ignore storage errors (e.g. private mode / restricted storage). + } +} + +export function getStoredDashboardUsername(): string { + return getFromLocalStorage('user', '').trim() || 'guest'; +} + +export function getStoredSelectedChatConfigId(): string { + return getFromLocalStorage(CHAT_SELECTED_CONFIG_STORAGE_KEY, '').trim() || 'default'; +} + +export function setStoredSelectedChatConfigId(configId: string): void { + setToLocalStorage(CHAT_SELECTED_CONFIG_STORAGE_KEY, configId); +} + +export function buildWebchatUmoDetails(sessionId: string, isGroup = false): WebchatUmoDetails { + const platformId = 'webchat'; + const username = getStoredDashboardUsername(); + const messageType: ChatMessageType = isGroup ? 'GroupMessage' : 'FriendMessage'; + const sessionKey = `${platformId}!${username}!${sessionId}`; + return { + platformId, + messageType, + username, + sessionKey, + umo: `${platformId}:${messageType}:${sessionKey}` + }; +}