diff --git a/PWA/src/App.tsx b/PWA/src/App.tsx index 9ce8e52..0217f4f 100644 --- a/PWA/src/App.tsx +++ b/PWA/src/App.tsx @@ -1,18 +1,58 @@ import { useState } from 'react' -import ModelSelector from './components/ModelSelector' import ChatArea from './components/ChatArea' import InputArea from './components/InputArea' -export type Model = 'Omni' | 'Qwen3.5' | 'Deepseek' +export type Message = { + id: number + role: 'user' | 'shenyan' + text: string +} + +const API = 'http://localhost:3001/api/chat' + +let nextId = 0 export default function App() { - const [activeModel, setActiveModel] = useState('Deepseek') + const [messages, setMessages] = useState([]) + const [loading, setLoading] = useState(false) + + const handleSend = async (text: string) => { + const userMsg: Message = { id: nextId++, role: 'user', text } + setMessages((prev) => [...prev, userMsg]) + setLoading(true) + + try { + const apiMessages = [ + ...messages.map((m) => ({ + role: m.role === 'user' ? 'user' : 'assistant', + content: m.text, + })), + { role: 'user', content: userMsg.text }, + ] + + const res = await fetch(API, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ model: 'deepseek-chat', messages: apiMessages }), + }) + + const data = await res.json() + const reply = data.choices?.[0]?.message?.content || '(no response)' + setMessages((prev) => [...prev, { id: nextId++, role: 'shenyan', text: reply }]) + } catch { + setMessages((prev) => [...prev, { id: nextId++, role: 'shenyan', text: '连接失败' }]) + } finally { + setLoading(false) + } + } return (
- - - +
+ 沈晏 +
+ +
) } diff --git a/PWA/src/components/ChatArea.tsx b/PWA/src/components/ChatArea.tsx index 6d6009a..cff4dcf 100644 --- a/PWA/src/components/ChatArea.tsx +++ b/PWA/src/components/ChatArea.tsx @@ -1,15 +1,33 @@ -export default function ChatArea() { +import { useRef, useEffect } from 'react' +import type { Message } from '../App' + +export default function ChatArea({ messages }: { messages: Message[] }) { + const bottom = useRef(null) + + useEffect(() => { + bottom.current?.scrollIntoView({ behavior: 'smooth' }) + }, [messages]) + return ( -
-
-
-

- 1. 示例文字: -

-

- 2. -

-
+
+
+ {messages.map((m) => ( +
+
+ {m.text} +
+
+ ))} +
) diff --git a/PWA/src/components/InputArea.tsx b/PWA/src/components/InputArea.tsx index 908502b..f458931 100644 --- a/PWA/src/components/InputArea.tsx +++ b/PWA/src/components/InputArea.tsx @@ -1,13 +1,39 @@ -export default function InputArea() { +import { useState } from 'react' + +export default function InputArea({ onSend, disabled }: { onSend: (text: string) => void; disabled?: boolean }) { + const [value, setValue] = useState('') + + const handleSubmit = () => { + const trimmed = value.trim() + if (!trimmed) return + onSend(trimmed) + setValue('') + } + return ( -
-
- +
+