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 InputArea from './components/InputArea'
@@ -9,26 +9,40 @@ export type Message = {
}
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() {
const [messages, setMessages] = useState<Message[]>([])
const [messages, setMessages] = useState<Message[]>(loadMessages)
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 userMsg: Message = { id: nextId++, role: 'user', text }
setMessages((prev) => [...prev, userMsg])
const updated = [...messages, userMsg]
setMessages(updated)
setLoading(true)
try {
const apiMessages = [
...messages.map((m) => ({
const apiMessages = updated.map((m) => ({
role: m.role === 'user' ? 'user' : 'assistant',
content: m.text,
})),
{ role: 'user', content: userMsg.text },
]
}))
const res = await fetch(API, {
method: 'POST',
@@ -46,10 +60,24 @@ export default function App() {
}
}
const handleClear = () => {
setMessages([])
localStorage.removeItem(STORAGE_KEY)
}
return (
<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>
<ChatArea messages={messages} />
<InputArea onSend={handleSend} disabled={loading} />