🎨 优化角色卡编辑

Signed-off-by: Echo <1711788888@qq.com>
This commit is contained in:
2026-02-28 15:09:53 +08:00
parent 81b552b689
commit a6234e7bb0
8 changed files with 530 additions and 9 deletions

View File

@@ -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>