PWA对话持久化:localStorage,刷新不丢

This commit is contained in:
fyah
2026-05-09 13:54:09 +08:00
parent 35501c5e6e
commit 7680ae53a3

View File

@@ -1,4 +1,4 @@
import { useState } from 'react' import { useState, useEffect, useRef } from 'react'
import ChatArea from './components/ChatArea' import ChatArea from './components/ChatArea'
import InputArea from './components/InputArea' import InputArea from './components/InputArea'
@@ -9,26 +9,40 @@ export type Message = {
} }
const API = 'http://localhost:3001/api/chat' const API = 'http://localhost:3001/api/chat'
const STORAGE_KEY = 'shenyan-messages'
let nextId = 0 function loadMessages(): Message[] {
try {
const raw = localStorage.getItem(STORAGE_KEY)
return raw ? JSON.parse(raw) : []
} catch {
return []
}
}
let nextId = (loadMessages().reduce((max, m) => Math.max(max, m.id), 0) || 0) + 1
export default function App() { export default function App() {
const [messages, setMessages] = useState<Message[]>([]) const [messages, setMessages] = useState<Message[]>(loadMessages)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const didLoad = useRef(false)
useEffect(() => {
if (!didLoad.current) { didLoad.current = true; return }
localStorage.setItem(STORAGE_KEY, JSON.stringify(messages))
}, [messages])
const handleSend = async (text: string) => { const handleSend = async (text: string) => {
const userMsg: Message = { id: nextId++, role: 'user', text } const userMsg: Message = { id: nextId++, role: 'user', text }
setMessages((prev) => [...prev, userMsg]) const updated = [...messages, userMsg]
setMessages(updated)
setLoading(true) setLoading(true)
try { try {
const apiMessages = [ const apiMessages = updated.map((m) => ({
...messages.map((m) => ({
role: m.role === 'user' ? 'user' : 'assistant', role: m.role === 'user' ? 'user' : 'assistant',
content: m.text, content: m.text,
})), }))
{ role: 'user', content: userMsg.text },
]
const res = await fetch(API, { const res = await fetch(API, {
method: 'POST', method: 'POST',
@@ -46,10 +60,24 @@ export default function App() {
} }
} }
const handleClear = () => {
setMessages([])
localStorage.removeItem(STORAGE_KEY)
}
return ( return (
<div className="flex flex-col h-full bg-black"> <div className="flex flex-col h-full bg-black">
<header className="text-center pt-3 pb-2 text-sm tracking-wide flex-shrink-0" style={{ color: '#999' }}> <header className="flex items-center justify-center pt-3 pb-2 flex-shrink-0 relative">
<span className="text-sm tracking-wide" style={{ color: '#999' }}></span>
{messages.length > 0 && (
<button
onClick={handleClear}
className="absolute right-4 text-xs"
style={{ color: 'rgba(255,255,255,0.25)' }}
>
</button>
)}
</header> </header>
<ChatArea messages={messages} /> <ChatArea messages={messages} />
<InputArea onSend={handleSend} disabled={loading} /> <InputArea onSend={handleSend} disabled={loading} />