Add Vue frontend application
This commit is contained in:
34
frontend/src/api/agent.ts
Normal file
34
frontend/src/api/agent.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import api from './index'
|
||||
|
||||
export interface AgentStats {
|
||||
agent_id: string
|
||||
call_count: number
|
||||
current_task: string | null
|
||||
status: 'active' | 'idle' | 'disabled'
|
||||
}
|
||||
|
||||
export interface AgentConfig {
|
||||
id: string
|
||||
name: string
|
||||
role: string
|
||||
description: string
|
||||
system_prompt: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export const agentApi = {
|
||||
async getStats(): Promise<AgentStats[]> {
|
||||
const res = await api.get('/api/agents/stats')
|
||||
return res.data
|
||||
},
|
||||
|
||||
async getConfig(id: string): Promise<AgentConfig> {
|
||||
const res = await api.get(`/api/agents/config/${id}`)
|
||||
return res.data
|
||||
},
|
||||
|
||||
async updateConfig(id: string, data: Partial<AgentConfig>): Promise<AgentConfig> {
|
||||
const res = await api.put(`/api/agents/config/${id}`, data)
|
||||
return res.data
|
||||
},
|
||||
}
|
||||
44
frontend/src/api/conversation.ts
Normal file
44
frontend/src/api/conversation.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import api from './index'
|
||||
|
||||
export interface Message {
|
||||
id: string
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
model?: string
|
||||
tokens_used?: number
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface Conversation {
|
||||
id: string
|
||||
title?: string
|
||||
message_count: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export const conversationApi = {
|
||||
list() {
|
||||
return api.get<Conversation[]>('/api/conversations')
|
||||
},
|
||||
|
||||
create(title?: string) {
|
||||
return api.post<Conversation>('/api/conversations', { title })
|
||||
},
|
||||
|
||||
getMessages(conversationId: string) {
|
||||
return api.get<Message[]>(`/api/conversations/${conversationId}`)
|
||||
},
|
||||
|
||||
delete(conversationId: string) {
|
||||
return api.delete(`/api/conversations/${conversationId}`)
|
||||
},
|
||||
|
||||
chat(message: string, conversationId?: string, fileIds: string[] = []) {
|
||||
return api.post('/api/conversations/chat', {
|
||||
message,
|
||||
conversation_id: conversationId,
|
||||
file_ids: fileIds,
|
||||
})
|
||||
},
|
||||
}
|
||||
73
frontend/src/api/document.ts
Normal file
73
frontend/src/api/document.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import api from './index'
|
||||
|
||||
export interface Document {
|
||||
id: string
|
||||
title: string
|
||||
filename: string
|
||||
file_type: string
|
||||
file_size: number
|
||||
summary?: string
|
||||
chunk_count: number
|
||||
is_indexed: boolean
|
||||
folder_id?: string | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
chunk_id: string
|
||||
document_id: string
|
||||
document_title: string
|
||||
content: string
|
||||
score: number
|
||||
metadata_?: string
|
||||
prev_chunk?: string
|
||||
next_chunk?: string
|
||||
}
|
||||
|
||||
export interface UploadResponse {
|
||||
id: string
|
||||
title: string
|
||||
chunk_count: number
|
||||
status: string
|
||||
}
|
||||
|
||||
export const documentApi = {
|
||||
list(folderId?: string | null) {
|
||||
return api.get<Document[]>('/api/documents', {
|
||||
params: folderId ? { folder_id: folderId } : undefined,
|
||||
})
|
||||
},
|
||||
|
||||
upload(file: File, folderId?: string | null) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
if (folderId) {
|
||||
formData.append('folder_id', folderId)
|
||||
}
|
||||
return api.post<UploadResponse>('/api/documents/upload', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
},
|
||||
|
||||
get(id: string) {
|
||||
return api.get<Document>(`/api/documents/${id}`)
|
||||
},
|
||||
|
||||
getChunks(id: string) {
|
||||
return api.get<any[]>(`/api/documents/${id}/chunks`)
|
||||
},
|
||||
|
||||
delete(id: string) {
|
||||
return api.delete(`/api/documents/${id}`)
|
||||
},
|
||||
|
||||
search(query: string, top_k = 5, mode = 'hybrid') {
|
||||
return api.get<SearchResult[]>('/api/documents/search', {
|
||||
params: { query, top_k, mode },
|
||||
})
|
||||
},
|
||||
|
||||
getContent(id: string) {
|
||||
return api.get<string>(`/api/documents/${id}/content`)
|
||||
},
|
||||
}
|
||||
47
frontend/src/api/folder.ts
Normal file
47
frontend/src/api/folder.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import api from './index'
|
||||
|
||||
export interface FolderCreate {
|
||||
name: string
|
||||
parent_id?: string | null
|
||||
}
|
||||
|
||||
export interface FolderUpdate {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface FolderItem {
|
||||
id: string
|
||||
name: string
|
||||
parent_id: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface FolderTree {
|
||||
id: string
|
||||
name: string
|
||||
parent_id: string | null
|
||||
children: FolderTree[]
|
||||
}
|
||||
|
||||
export const folderApi = {
|
||||
// 获取文件夹树
|
||||
getTree() {
|
||||
return api.get<FolderTree[]>('/api/folders')
|
||||
},
|
||||
|
||||
// 创建文件夹
|
||||
create(data: FolderCreate) {
|
||||
return api.post('/api/folders', data)
|
||||
},
|
||||
|
||||
// 重命名文件夹
|
||||
rename(id: string, data: FolderUpdate) {
|
||||
return api.put(`/api/folders/${id}`, data)
|
||||
},
|
||||
|
||||
// 删除文件夹
|
||||
delete(id: string) {
|
||||
return api.delete(`/api/folders/${id}`)
|
||||
},
|
||||
}
|
||||
49
frontend/src/api/forum.ts
Normal file
49
frontend/src/api/forum.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import api from './index'
|
||||
|
||||
export interface ForumPost {
|
||||
id: string
|
||||
user_id: string
|
||||
title: string
|
||||
content: string
|
||||
category?: string
|
||||
is_executed: boolean
|
||||
execution_result?: string
|
||||
reply_count: number
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface ForumReply {
|
||||
id: string
|
||||
post_id: string
|
||||
user_id?: string
|
||||
agent_id?: string
|
||||
content: string
|
||||
is_ai_reply: boolean
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export const forumApi = {
|
||||
listPosts() {
|
||||
return api.get<ForumPost[]>('/api/forum/posts')
|
||||
},
|
||||
|
||||
createPost(data: { title: string; content: string; category?: string }) {
|
||||
return api.post<ForumPost>('/api/forum/posts', data)
|
||||
},
|
||||
|
||||
getPost(id: string) {
|
||||
return api.get<ForumPost>(`/api/forum/posts/${id}`)
|
||||
},
|
||||
|
||||
deletePost(id: string) {
|
||||
return api.delete(`/api/forum/posts/${id}`)
|
||||
},
|
||||
|
||||
listReplies(postId: string) {
|
||||
return api.get<ForumReply[]>(`/api/forum/posts/${postId}/replies`)
|
||||
},
|
||||
|
||||
createReply(postId: string, content: string) {
|
||||
return api.post<ForumReply>(`/api/forum/posts/${postId}/replies`, { content })
|
||||
},
|
||||
}
|
||||
56
frontend/src/api/graph.ts
Normal file
56
frontend/src/api/graph.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import api from './index'
|
||||
|
||||
export interface KGNode {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
description?: string
|
||||
importance: number
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
export interface KGEdge {
|
||||
id: string
|
||||
source: string
|
||||
target: string
|
||||
relation: string
|
||||
weight?: number
|
||||
}
|
||||
|
||||
export interface GraphData {
|
||||
nodes: KGNode[]
|
||||
edges: KGEdge[]
|
||||
stats: {
|
||||
node_count: number
|
||||
edge_count: number
|
||||
}
|
||||
}
|
||||
|
||||
export const graphApi = {
|
||||
get() {
|
||||
return api.get<GraphData>('/api/graph')
|
||||
},
|
||||
|
||||
build() {
|
||||
return api.post('/api/graph/build')
|
||||
},
|
||||
|
||||
getEntityContext(entity: string) {
|
||||
return api.get<{ context: string }>(`/api/graph/entity/${encodeURIComponent(entity)}`)
|
||||
},
|
||||
|
||||
getSummary() {
|
||||
return api.get<{ summary: string }>('/api/graph/summary')
|
||||
},
|
||||
|
||||
getNeighbors(nodeId: string, depth = 1) {
|
||||
return api.get<{ nodes: KGNode[]; edges: KGEdge[] }>(
|
||||
`/api/graph/neighbors/${nodeId}`,
|
||||
{ params: { depth } }
|
||||
)
|
||||
},
|
||||
|
||||
deleteNode(nodeId: string) {
|
||||
return api.delete(`/api/graph/nodes/${nodeId}`)
|
||||
},
|
||||
}
|
||||
29
frontend/src/api/index.ts
Normal file
29
frontend/src/api/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000',
|
||||
timeout: 30000,
|
||||
})
|
||||
|
||||
// 请求拦截器:添加 Token
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('access_token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
// 响应拦截器:处理错误
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('access_token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default api
|
||||
71
frontend/src/api/settings.ts
Normal file
71
frontend/src/api/settings.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import api from './index'
|
||||
|
||||
export type LLMProvider = 'openai' | 'claude' | 'ollama' | 'deepseek' | 'custom'
|
||||
export type LLMType = 'chat' | 'vlm' | 'embedding' | 'rerank'
|
||||
|
||||
export interface LLMModelConfig {
|
||||
name: string // 模型名称/别名
|
||||
provider: LLMProvider
|
||||
model: string
|
||||
base_url: string
|
||||
api_key: string
|
||||
enabled: boolean // 是否启用
|
||||
}
|
||||
|
||||
export interface LLMConfig {
|
||||
chat?: LLMModelConfig[]
|
||||
vlm?: LLMModelConfig[]
|
||||
embedding?: LLMModelConfig[]
|
||||
rerank?: LLMModelConfig[]
|
||||
}
|
||||
|
||||
export interface SchedulerConfig {
|
||||
daily_plan_time?: string
|
||||
forum_scan_interval_minutes?: number
|
||||
todo_ai_generate_time?: string
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
export interface ProfileUpdate {
|
||||
full_name?: string
|
||||
password?: string
|
||||
current_password?: string
|
||||
}
|
||||
|
||||
export interface SettingsResponse {
|
||||
profile: {
|
||||
id: string
|
||||
email: string
|
||||
full_name: string
|
||||
created_at: string
|
||||
}
|
||||
llm_config: LLMConfig
|
||||
scheduler_config: SchedulerConfig
|
||||
}
|
||||
|
||||
export const settingsApi = {
|
||||
// 获取设置
|
||||
get() {
|
||||
return api.get<SettingsResponse>('/api/settings')
|
||||
},
|
||||
|
||||
// 更新资料
|
||||
updateProfile(data: ProfileUpdate) {
|
||||
return api.put('/api/settings/profile', data)
|
||||
},
|
||||
|
||||
// 更新 LLM 配置
|
||||
updateLLM(config: Partial<LLMConfig>) {
|
||||
return api.put('/api/settings/llm', config)
|
||||
},
|
||||
|
||||
// 测试 LLM 连接
|
||||
testLLM(data: { type: LLMType } & Omit<LLMModelConfig, 'name' | 'enabled'>) {
|
||||
return api.post('/api/settings/llm/test', data)
|
||||
},
|
||||
|
||||
// 更新定时任务配置
|
||||
updateScheduler(config: Partial<SchedulerConfig>) {
|
||||
return api.put('/api/settings/scheduler', config)
|
||||
},
|
||||
}
|
||||
17
frontend/src/api/stats.ts
Normal file
17
frontend/src/api/stats.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import axios from '@/api'
|
||||
|
||||
export const getSystemHealth = () => axios.get('/stats/system')
|
||||
|
||||
export const getConversationStats = (days = 30) =>
|
||||
axios.get('/stats/conversations', { params: { days } })
|
||||
|
||||
export const getKnowledgeStats = (days = 30) =>
|
||||
axios.get('/stats/knowledge', { params: { days } })
|
||||
|
||||
export const getKanbanStats = (days = 30) =>
|
||||
axios.get('/stats/kanban', { params: { days } })
|
||||
|
||||
export const getCommunityStats = (days = 30) =>
|
||||
axios.get('/stats/community', { params: { days } })
|
||||
|
||||
export const getPersonalInsights = () => axios.get('/stats/insights')
|
||||
35
frontend/src/api/task.ts
Normal file
35
frontend/src/api/task.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import api from './index'
|
||||
|
||||
export type TaskStatus = 'todo' | 'in_progress' | 'done' | 'cancelled'
|
||||
export type TaskPriority = 'low' | 'medium' | 'high' | 'urgent'
|
||||
|
||||
export interface Task {
|
||||
id: string
|
||||
title: string
|
||||
description?: string
|
||||
status: TaskStatus
|
||||
priority: TaskPriority
|
||||
due_date?: string
|
||||
completed_at?: string
|
||||
tags?: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export const taskApi = {
|
||||
list(status?: TaskStatus) {
|
||||
return api.get<Task[]>('/api/tasks', { params: status ? { status } : {} })
|
||||
},
|
||||
|
||||
create(data: { title: string; description?: string; priority?: TaskPriority; due_date?: string }) {
|
||||
return api.post<Task>('/api/tasks', data)
|
||||
},
|
||||
|
||||
update(id: string, data: Partial<Task>) {
|
||||
return api.patch<Task>(`/api/tasks/${id}`, data)
|
||||
},
|
||||
|
||||
delete(id: string) {
|
||||
return api.delete(`/api/tasks/${id}`)
|
||||
},
|
||||
}
|
||||
59
frontend/src/api/todo.ts
Normal file
59
frontend/src/api/todo.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import api from './index'
|
||||
|
||||
export type TodoSource = 'ai_kanban' | 'ai_chat' | 'manual'
|
||||
|
||||
export interface Todo {
|
||||
id: string
|
||||
title: string
|
||||
is_completed: boolean
|
||||
source: TodoSource
|
||||
source_detail: string | null
|
||||
todo_date: string
|
||||
completed_at: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface TodoListResponse {
|
||||
items: Todo[]
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
}
|
||||
|
||||
export interface TodoSummary {
|
||||
date: string
|
||||
total: number
|
||||
completed: number
|
||||
pending: number
|
||||
}
|
||||
|
||||
export const todoApi = {
|
||||
list(date?: string, page = 1, pageSize = 50) {
|
||||
return api.get<TodoListResponse>('/api/todos', {
|
||||
params: date ? { date_str: date, page, page_size: pageSize } : { page, page_size: pageSize },
|
||||
})
|
||||
},
|
||||
|
||||
create(title: string) {
|
||||
return api.post<Todo>('/api/todos', { title })
|
||||
},
|
||||
|
||||
update(id: string, data: { title?: string; is_completed?: boolean }) {
|
||||
return api.patch<Todo>(`/api/todos/${id}`, data)
|
||||
},
|
||||
|
||||
delete(id: string) {
|
||||
return api.delete(`/api/todos/${id}`)
|
||||
},
|
||||
|
||||
aiGenerate() {
|
||||
return api.post<TodoListResponse>('/api/todos/ai-generate')
|
||||
},
|
||||
|
||||
summary(date?: string) {
|
||||
return api.get<TodoSummary>('/api/todos/summary', {
|
||||
params: date ? { date_str: date } : {},
|
||||
})
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user