Add brain and chat workspace views

Expand the frontend with brain, graph, and chat workspace updates so the
new backend orchestration and memory features have matching screens.
These changes also wire the new APIs into routing and add focused view
and routing tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-22 13:48:16 +08:00
parent d2447ee635
commit 7d80a6e2ec
21 changed files with 3095 additions and 658 deletions

View File

@@ -2,9 +2,14 @@ import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import api from '@/api'
let unauthorizedListenerRegistered = false
export const useAuthStore = defineStore('auth', () => {
const token = ref<string | null>(localStorage.getItem('access_token'))
const user = ref<{ id: string; email: string; full_name?: string } | null>(null)
const isAuthReady = ref(false)
const isFetchingUser = ref(false)
let authReadyPromise: Promise<void> | null = null
const isAuthenticated = computed(() => !!token.value)
@@ -17,28 +22,63 @@ export const useAuthStore = defineStore('auth', () => {
})
token.value = response.data.access_token
localStorage.setItem('access_token', response.data.access_token)
user.value = null
isAuthReady.value = false
await fetchUser()
}
async function fetchUser() {
if (!token.value) return
if (!token.value) {
user.value = null
isAuthReady.value = true
return
}
isFetchingUser.value = true
try {
const response = await api.get('/api/auth/me')
user.value = response.data
} catch {
logout()
} catch (error: any) {
const status = error?.response?.status
if (status === 401 || status === 403) {
logout()
} else {
console.error('获取当前用户失败:', error)
}
} finally {
isFetchingUser.value = false
isAuthReady.value = true
}
}
function logout() {
token.value = null
user.value = null
isFetchingUser.value = false
isAuthReady.value = true
localStorage.removeItem('access_token')
}
if (token.value) {
fetchUser()
async function ensureAuthReady() {
if (isAuthReady.value) return
if (!authReadyPromise) {
authReadyPromise = fetchUser().finally(() => {
authReadyPromise = null
})
}
await authReadyPromise
}
return { token, user, isAuthenticated, login, logout, fetchUser }
if (typeof window !== 'undefined' && !unauthorizedListenerRegistered) {
window.addEventListener('jarvis:auth-unauthorized', logout)
unauthorizedListenerRegistered = true
}
if (token.value) {
void ensureAuthReady()
} else {
isAuthReady.value = true
}
return { token, user, isAuthenticated, isAuthReady, isFetchingUser, login, logout, fetchUser, ensureAuthReady }
})

View File

@@ -12,7 +12,7 @@ export const useConversationStore = defineStore('conversation', () => {
conversations.value = data
}
function setCurrentConversation(id: string) {
function setCurrentConversation(id: string | null) {
currentConversationId.value = id
}
@@ -20,6 +20,10 @@ export const useConversationStore = defineStore('conversation', () => {
messages.value.push(msg)
}
function removeMessage(id: string) {
messages.value = messages.value.filter((msg) => msg.id !== id)
}
function setMessages(data: Message[]) {
messages.value = data
}
@@ -40,6 +44,7 @@ export const useConversationStore = defineStore('conversation', () => {
setConversations,
setCurrentConversation,
addMessage,
removeMessage,
setMessages,
addConversation,
removeConversation,