import {Image, Palette, RotateCcw, Save, Upload, X, BookOpen} from 'lucide-react' import {useEffect, useRef, useState} from 'react' import {type Conversation, conversationApi} from '../api/conversation' import {type Worldbook, getWorldbookList} from '../api/worldbook' interface SettingsPanelProps { conversation: Conversation onClose: () => void onUpdate?: (settings: ConversationSettings) => void } interface ConversationSettings { themeColor: string backgroundImage?: string worldbookId?: number worldbookEnabled: boolean } const THEME_COLORS = [ { value: '#7C3AED', label: '紫色' }, { value: '#F97316', label: '橙色' }, { value: '#10B981', label: '绿色' }, { value: '#3B82F6', label: '蓝色' }, { value: '#EC4899', label: '粉色' }, { value: '#F59E0B', label: '黄色' }, ] export default function SettingsPanel({ conversation, onClose, onUpdate }: SettingsPanelProps) { const [settings, setSettings] = useState({ themeColor: '#7C3AED', worldbookEnabled: false, }) const [hasChanges, setHasChanges] = useState(false) const [backgroundPreview, setBackgroundPreview] = useState() const [worldbooks, setWorldbooks] = useState([]) const fileInputRef = useRef(null) useEffect(() => { loadSettings() loadWorldbooks() }, []) const loadSettings = () => { if (conversation.settings) { try { const parsed = typeof conversation.settings === 'string' ? JSON.parse(conversation.settings) : conversation.settings // 合并默认设置和保存的设置 const mergedSettings = { themeColor: '#7C3AED', worldbookEnabled: conversation.worldbookEnabled || false, worldbookId: conversation.worldbookId, ...parsed } setSettings(mergedSettings) if (parsed.backgroundImage) { setBackgroundPreview(parsed.backgroundImage) } // 应用主题色 if (parsed.themeColor) { document.documentElement.style.setProperty('--color-primary', parsed.themeColor) } } catch (e) { console.error('解析设置失败:', e) } } else { // 如果没有 settings,从 conversation 直接读取世界书配置 setSettings(prev => ({ ...prev, worldbookEnabled: conversation.worldbookEnabled || false, worldbookId: conversation.worldbookId, })) } } const loadWorldbooks = async () => { try { const response = await getWorldbookList({ page: 1, pageSize: 100 }) setWorldbooks(response.data.list || []) } catch (error) { console.error('加载世界书列表失败:', error) } } const handleChange = (key: keyof ConversationSettings, value: any) => { setSettings(prev => ({ ...prev, [key]: value })) setHasChanges(true) } const handleImageUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return if (!file.type.startsWith('image/')) { alert('请选择图片文件') return } if (file.size > 5 * 1024 * 1024) { alert('图片大小不能超过 5MB') return } const reader = new FileReader() reader.onload = (event) => { const dataUrl = event.target?.result as string setBackgroundPreview(dataUrl) handleChange('backgroundImage', dataUrl) } reader.readAsDataURL(file) } const handleRemoveBackground = () => { setBackgroundPreview(undefined) handleChange('backgroundImage', undefined) } const handleSave = async () => { try { // 保存到后端 await conversationApi.updateConversationSettings(conversation.id, { settings, worldbookId: settings.worldbookId, worldbookEnabled: settings.worldbookEnabled, }) // 应用主题色到根元素 if (settings.themeColor) { document.documentElement.style.setProperty('--color-primary', settings.themeColor) } // 通知父组件更新 onUpdate?.(settings) setHasChanges(false) alert('设置已保存') } catch (err) { console.error('保存设置失败:', err) alert('保存失败,请重试') } } const handleReset = () => { setSettings({ themeColor: '#7C3AED', worldbookEnabled: false, }) setBackgroundPreview(undefined) setHasChanges(false) } return (

对话设置

外观设置

{THEME_COLORS.map((color) => ( ))}
{backgroundPreview ? (
背景预览
) : ( )}

世界书设置

根据关键词自动注入背景信息

{settings.worldbookEnabled && (
{worldbooks.length === 0 && (

还没有世界书,请先在世界书管理页面创建

)}
)}

💡 主题颜色会应用到整个界面

💡 背景图片仅在对话页面显示

💡 世界书会根据对话内容自动触发相关条目

💡 AI 模型可在对话界面顶部切换

) }