Files
Cyrene_v2/frontend/web/src/pages/admin/MemoryPage.tsx
T
Aska 8bfda096f7 feat(admin): rebuild admin panel with full management features
- AdminLayout: sidebar navigation (5 pages)
- Dashboard: real-time service/plugin/uptime stats
- Services: status display for all 4 services
- IoT Management: 8 devices with toggle controls
- Plugins: 11 plugins grid with type badges
- Memory: full CRUD with search, add, delete
- 60 modules, TS zero errors, 256KB JS
2026-08-12 16:45:21 +08:00

102 lines
4.3 KiB
TypeScript

import { useState, useCallback } from 'react'
import { api, getToken } from '../../api/client'
interface MemoryEntry {
id: string; user_id: string; content: string; summary: string;
keywords: string[]; category: string; priority: number;
}
export default function MemoryPage() {
const [memories, setMemories] = useState<MemoryEntry[]>([])
const [search, setSearch] = useState('')
const [loading, setLoading] = useState(false)
const [newContent, setNewContent] = useState('')
const fetchMemories = useCallback(async (q?: string) => {
setLoading(true)
try {
const token = getToken()
const path = q ? `/api/v1/memory/search?q=${encodeURIComponent(q)}` : '/api/v1/memory'
const res = await fetch(path, { headers: { Authorization: `Bearer ${token}` } })
const data = await res.json()
setMemories(Array.isArray(data?.data) ? data.data : [])
} catch { } finally { setLoading(false) }
}, [])
const handleAdd = async () => {
if (!newContent.trim()) return
const token = getToken()
await fetch('/api/v1/memory', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ content: newContent, category: 'fact', priority: 0.5 }),
})
setNewContent('')
fetchMemories(search || undefined)
}
const handleDelete = async (id: string) => {
const token = getToken()
await fetch(`/api/v1/memory/${id}`, { method: 'DELETE', headers: { Authorization: `Bearer ${token}` } })
fetchMemories(search || undefined)
}
return (
<div className="p-6 space-y-4">
<h2 className="text-lg font-semibold">记忆管理</h2>
{/* Search */}
<div className="flex gap-2">
<input
type="text" value={search}
onChange={e => setSearch(e.target.value)}
onKeyDown={e => { if (e.key === 'Enter') fetchMemories(search) }}
placeholder="搜索记忆..."
className="flex-1 bg-[#000] border border-[#404040] focus:border-[#277AF7] rounded-[14px] px-4 py-2.5 text-sm text-[#F2F2F2] placeholder:text-white/30 outline-none"
/>
<button onClick={() => fetchMemories(search)} className="px-4 py-2.5 bg-[#277AF7] hover:bg-[#3482FF] text-white text-sm font-medium rounded-full">搜索</button>
<button onClick={() => { setSearch(''); fetchMemories() }} className="px-4 py-2.5 bg-[#000] border border-[#404040] text-white/50 text-sm rounded-full">全部</button>
</div>
{/* Add */}
<div className="flex gap-2">
<input
type="text" value={newContent}
onChange={e => setNewContent(e.target.value)}
placeholder="添加新记忆..."
className="flex-1 bg-[#000] border border-[#404040] focus:border-[#277AF7] rounded-[14px] px-4 py-2.5 text-sm text-[#F2F2F2] placeholder:text-white/30 outline-none"
/>
<button onClick={handleAdd} disabled={!newContent.trim()}
className="px-4 py-2.5 bg-[#277AF7] hover:bg-[#3482FF] disabled:bg-[#253E64] text-white text-sm font-medium rounded-full"
>添加</button>
</div>
{/* List */}
{loading ? (
<p className="text-center text-white/30 py-8 text-sm">加载中...</p>
) : memories.length === 0 ? (
<p className="text-center text-white/30 py-8 text-sm">暂无记忆</p>
) : (
<div className="space-y-2">
{memories.map(m => (
<div key={m.id} className="bg-[#000] border border-[#404040] rounded-[18px] px-4 py-3 flex items-start justify-between">
<div className="flex-1 min-w-0">
<p className="text-sm text-[#F2F2F2] truncate">{m.content}</p>
<div className="flex items-center gap-2 mt-1">
<span className="text-xs px-1.5 py-0.5 rounded bg-[#2B3B54]/50 text-[#4788FF]">{m.category}</span>
{m.keywords?.slice(0, 3).map((kw, i) => (
<span key={i} className="text-xs text-white/30">{kw}</span>
))}
</div>
</div>
<button onClick={() => handleDelete(m.id)}
className="text-xs text-[#F12522]/70 hover:text-[#F12522] shrink-0 ml-3 mt-0.5"
>删除</button>
</div>
))}
</div>
)}
</div>
)
}