diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 52962535..7e4ec260 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,9 +1,10 @@ import { useEffect } from 'react' import { NavLink, Navigate, Route, Routes } from 'react-router-dom' -import { BookOpen, FlaskConical, History, Settings, Wrench } from 'lucide-react' +import { BookOpen, FlaskConical, History, MessageSquare, Settings, Wrench } from 'lucide-react' import clsx from 'clsx' import { api } from './api/client' import { useCatalog } from './store/catalog' +import { ChatPage } from './pages/ChatPage' import { StudioPage } from './pages/StudioPage' import { RunsPage } from './pages/RunsPage' import { ToolsPage } from './pages/ToolsPage' @@ -12,6 +13,7 @@ import { SettingsPage, applyTheme } from './pages/SettingsPage' import { Spinner } from './components/ui' const NAV = [ + { to: '/chat', label: '问数据', icon: MessageSquare }, { to: '/studio', label: '编排', icon: FlaskConical }, { to: '/runs', label: '运行', icon: History }, { to: '/tools', label: '工具', icon: Wrench }, @@ -73,7 +75,9 @@ export default function App() {
- } /> + {/* 默认落到对话页:多数人要的是答案,画布留给要自己编排的人 */} + } /> + } /> } /> } /> } /> diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index f64a5117..e5b74929 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -77,6 +77,19 @@ export const api = { }, // ---- 运行 ---- + datasources: { + list: () => get('/datasources'), + kinds: () => get('/datasources/kinds'), + create: (body: any) => post('/datasources', body), + update: (id: string, body: any) => patch(`/datasources/${id}`, body), + remove: (id: string) => del(`/datasources/${id}`), + test: (id: string) => post(`/datasources/${id}/test`, {}), + introspect: (id: string, schema?: string) => + post(`/datasources/${id}/introspect${schema ? `?schema=${encodeURIComponent(schema)}` : ''}`, {}), + schema: (id: string, table?: string) => + get(`/datasources/${id}/schema${table ? `?table=${encodeURIComponent(table)}` : ''}`), + }, + runs: { list: (params?: { workflow_id?: string; status?: string; limit?: number }) => { const q = new URLSearchParams( diff --git a/frontend/src/pages/ChatPage.tsx b/frontend/src/pages/ChatPage.tsx new file mode 100644 index 00000000..342cfc14 --- /dev/null +++ b/frontend/src/pages/ChatPage.tsx @@ -0,0 +1,335 @@ +import { useEffect, useMemo, useRef, useState } from 'react' +import { + ChevronRight, Database, MessageSquare, Send, Square, Table2, Trash2, Wrench, XCircle, +} from 'lucide-react' +import clsx from 'clsx' +import { api } from '../api/client' +import { ApprovalCard } from '../run/RunPanel' +import { useCatalog } from '../store/catalog' +import { useChat, type ChatTurn } from '../store/chat' +import { useStudio, toFlow } from '../store/studio' +import { Empty, Spinner, useToast } from '../components/ui' + +/** + * 对话式入口。 + * + * 画布是给编排的人用的:15 种节点、每种十几个配置项,那是把控制权交到手里的 + * 代价。但大多数时候人要的只是一个答案——"上季度华东销量怎么样",不关心它 + * 是 input→agent→output 还是别的什么。 + * + * 所以这一页把图藏起来:你问,它自己接数据源、建流程、跑完、给结论。图仍然 + * 真实存在(可以展开看,也能拿到画布里继续改),只是不再是必经之路。 + */ +export function ChatPage() { + const { turns, busy, ask, stop, clear } = useChat() + const [draft, setDraft] = useState('') + const bottomRef = useRef(null) + const sources = useDataSources() + + useEffect(() => { + bottomRef.current?.scrollIntoView({ behavior: 'smooth' }) + }, [turns.length, turns[turns.length - 1]?.phase]) + + const send = () => { + const q = draft.trim() + if (!q || busy) return + ask(q) + setDraft('') + } + + return ( +
+
+ + 问数据 + + 说需求,它自己接数据源、建流程、跑完给结论 + + + {!!turns.length && ( + + )} +
+ +
+ {!turns.length ? ( + { setDraft(q); }} /> + ) : ( +
+ {turns.map((turn) => )} +
+ )} +
+
+ +
+
+