@@ -1,6 +1,7 @@
|
||||
import {Image, Palette, RotateCcw, Save, Upload, X} from 'lucide-react'
|
||||
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
|
||||
@@ -11,6 +12,8 @@ interface SettingsPanelProps {
|
||||
interface ConversationSettings {
|
||||
themeColor: string
|
||||
backgroundImage?: string
|
||||
worldbookId?: number
|
||||
worldbookEnabled: boolean
|
||||
}
|
||||
|
||||
const THEME_COLORS = [
|
||||
@@ -25,13 +28,16 @@ const THEME_COLORS = [
|
||||
export default function SettingsPanel({ conversation, onClose, onUpdate }: SettingsPanelProps) {
|
||||
const [settings, setSettings] = useState<ConversationSettings>({
|
||||
themeColor: '#7C3AED',
|
||||
worldbookEnabled: false,
|
||||
})
|
||||
const [hasChanges, setHasChanges] = useState(false)
|
||||
const [backgroundPreview, setBackgroundPreview] = useState<string>()
|
||||
const [worldbooks, setWorldbooks] = useState<Worldbook[]>([])
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
loadSettings()
|
||||
loadWorldbooks()
|
||||
}, [])
|
||||
|
||||
const loadSettings = () => {
|
||||
@@ -44,6 +50,8 @@ export default function SettingsPanel({ conversation, onClose, onUpdate }: Setti
|
||||
// 合并默认设置和保存的设置
|
||||
const mergedSettings = {
|
||||
themeColor: '#7C3AED',
|
||||
worldbookEnabled: conversation.worldbookEnabled || false,
|
||||
worldbookId: conversation.worldbookId,
|
||||
...parsed
|
||||
}
|
||||
|
||||
@@ -60,6 +68,22 @@ export default function SettingsPanel({ conversation, onClose, onUpdate }: Setti
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +123,11 @@ export default function SettingsPanel({ conversation, onClose, onUpdate }: Setti
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
// 保存到后端
|
||||
await conversationApi.updateConversationSettings(conversation.id, settings)
|
||||
await conversationApi.updateConversationSettings(conversation.id, {
|
||||
settings,
|
||||
worldbookId: settings.worldbookId,
|
||||
worldbookEnabled: settings.worldbookEnabled,
|
||||
})
|
||||
|
||||
// 应用主题色到根元素
|
||||
if (settings.themeColor) {
|
||||
@@ -119,6 +147,7 @@ export default function SettingsPanel({ conversation, onClose, onUpdate }: Setti
|
||||
const handleReset = () => {
|
||||
setSettings({
|
||||
themeColor: '#7C3AED',
|
||||
worldbookEnabled: false,
|
||||
})
|
||||
setBackgroundPreview(undefined)
|
||||
setHasChanges(false)
|
||||
@@ -214,10 +243,64 @@ export default function SettingsPanel({ conversation, onClose, onUpdate }: Setti
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="glass-hover rounded-2xl p-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-2 bg-primary/20 rounded-lg">
|
||||
<BookOpen className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<h3 className="font-semibold">世界书设置</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="text-sm font-medium">启用世界书</label>
|
||||
<p className="text-xs text-white/60 mt-1">根据关键词自动注入背景信息</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleChange('worldbookEnabled', !settings.worldbookEnabled)}
|
||||
className={`relative w-12 h-6 rounded-full transition-colors cursor-pointer ${
|
||||
settings.worldbookEnabled ? 'bg-primary' : 'bg-white/20'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`absolute top-1 left-1 w-4 h-4 bg-white rounded-full transition-transform ${
|
||||
settings.worldbookEnabled ? 'translate-x-6' : ''
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{settings.worldbookEnabled && (
|
||||
<div>
|
||||
<label className="text-sm text-white/80 mb-3 block">选择世界书</label>
|
||||
<select
|
||||
value={settings.worldbookId || ''}
|
||||
onChange={(e) => handleChange('worldbookId', e.target.value ? Number(e.target.value) : undefined)}
|
||||
className="w-full px-4 py-3 glass rounded-xl text-sm bg-white/5 border border-white/10 focus:border-primary/50 focus:outline-none cursor-pointer"
|
||||
>
|
||||
<option value="">未选择</option>
|
||||
{worldbooks.map((wb) => (
|
||||
<option key={wb.id} value={wb.id}>
|
||||
{wb.name} ({wb.entryCount} 条目)
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{worldbooks.length === 0 && (
|
||||
<p className="text-xs text-white/40 mt-2">
|
||||
还没有世界书,请先在世界书管理页面创建
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="glass-hover rounded-2xl p-4">
|
||||
<div className="text-xs text-white/60 space-y-2">
|
||||
<p>💡 主题颜色会应用到整个界面</p>
|
||||
<p>💡 背景图片仅在对话页面显示</p>
|
||||
<p>💡 世界书会根据对话内容自动触发相关条目</p>
|
||||
<p>💡 AI 模型可在对话界面顶部切换</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user