import { useCallback, useEffect, useState } from 'react'; import { listBlockedWords, createBlockedWord, updateBlockedWord, deleteBlockedWord, } from '../../api/client'; import type { BlockedWord } from '../../types'; const DEFAULT_WORDS = ['goose', 'aider', 'openhands']; export function BlockedWordsPage() { const [words, setWords] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [message, setMessage] = useState(null); const [newWord, setNewWord] = useState(''); const [newReplacement, setNewReplacement] = useState('***'); const [newNote, setNewNote] = useState(''); const [adding, setAdding] = useState(false); const [editId, setEditId] = useState(null); const [editWord, setEditWord] = useState(''); const [editReplacement, setEditReplacement] = useState(''); const [editNote, setEditNote] = useState(''); const [editBusy, setEditBusy] = useState(false); const load = useCallback(async () => { setLoading(true); setError(null); try { setWords(await listBlockedWords()); } catch (err) { setError(err instanceof Error ? err.message : '加载失败'); } finally { setLoading(false); } }, []); useEffect(() => { void load(); }, [load]); const handleAdd = async (e: React.FormEvent) => { e.preventDefault(); if (!newWord.trim()) return; setAdding(true); setError(null); setMessage(null); try { const created = await createBlockedWord({ word: newWord.trim(), replacement: newReplacement || '***', note: newNote.trim() || undefined, }); setWords((prev) => [created, ...prev]); setNewWord(''); setNewReplacement('***'); setNewNote(''); setMessage(`已添加:${created.word}`); } catch (err) { setError(err instanceof Error ? err.message : '添加失败'); } finally { setAdding(false); } }; const startEdit = (w: BlockedWord) => { setEditId(w.id); setEditWord(w.word); setEditReplacement(w.replacement); setEditNote(w.note ?? ''); }; const cancelEdit = () => setEditId(null); const handleSaveEdit = async (id: string) => { setEditBusy(true); setError(null); setMessage(null); try { const updated = await updateBlockedWord(id, { word: editWord.trim(), replacement: editReplacement || '***', note: editNote.trim() || '', }); setWords((prev) => prev.map((w) => (w.id === id ? updated : w))); setEditId(null); setMessage('已更新'); } catch (err) { setError(err instanceof Error ? err.message : '更新失败'); } finally { setEditBusy(false); } }; const handleToggleStatus = async (w: BlockedWord) => { setError(null); try { const updated = await updateBlockedWord(w.id, { status: w.status === 'active' ? 'disabled' : 'active', }); setWords((prev) => prev.map((item) => (item.id === w.id ? updated : item))); } catch (err) { setError(err instanceof Error ? err.message : '状态切换失败'); } }; const handleDelete = async (id: string, word: string) => { if (!window.confirm(`确认删除词语「${word}」?`)) return; setError(null); try { await deleteBlockedWord(id); setWords((prev) => prev.filter((w) => w.id !== id)); setMessage(`已删除:${word}`); } catch (err) { setError(err instanceof Error ? err.message : '删除失败'); } }; const handleQuickAdd = async (word: string) => { setError(null); setMessage(null); try { const created = await createBlockedWord({ word, replacement: '***' }); setWords((prev) => [created, ...prev]); setMessage(`已添加:${created.word}`); } catch (err) { setError(err instanceof Error ? err.message : '添加失败'); } }; const existingWords = new Set(words.map((w) => w.word.toLowerCase())); const quickAddable = DEFAULT_WORDS.filter((w) => !existingWords.has(w.toLowerCase())); return (

违禁词管理

H5 聊天中 AI 回复将自动替换以下词语,立即生效(前端刷新后加载最新规则)。

{error &&

{error}

} {message &&

{message}

} {quickAddable.length > 0 && (

快速添加预设违禁词

{quickAddable.map((w) => ( ))}
)}

添加词语

词语列表{loading ? '' : `(${words.length})`}

{loading &&

加载中…

} {!loading && words.length === 0 &&

暂无违禁词

} {!loading && words.length > 0 && ( {words.map((w) => ( {editId === w.id ? ( <> ) : ( <> )} ))}
词语 替换为 备注 状态 操作
setEditWord(e.target.value)} style={{ width: '120px' }} /> setEditReplacement(e.target.value)} style={{ width: '80px' }} /> setEditNote(e.target.value)} style={{ width: '120px' }} /> {w.word} {w.replacement} {w.note || '—'} {w.status === 'active' ? '启用' : '禁用'}
)}
); }