diff --git "a/components/ModelConfigManager.tsx" "b/components/ModelConfigManager.tsx" --- "a/components/ModelConfigManager.tsx" +++ "b/components/ModelConfigManager.tsx" @@ -1,1215 +1,1215 @@ -import React, { useState, useEffect } from 'react'; -import { AiModel, AiRole, ApiChannel, ModelConfigManager } from '../constants'; -import { - Settings, - Plus, - Edit3, - Trash2, - Save, - X, - Download, - Upload, - RefreshCw, - Bot, - Brain, - Image, - Zap, - AlertCircle, - Check, - HardDrive, - Globe, - Key, - Eye, - EyeOff, - Star, - StarOff, - Shield, - Lock -} from 'lucide-react'; - -interface ModelConfigManagerProps { - isOpen: boolean; - onClose: () => void; - onConfigChange: () => void; -} - -interface EditingChannel extends Partial { - isNew?: boolean; -} - -interface EditingModel extends Partial { - isNew?: boolean; -} - -interface EditingRole extends Partial { - isNew?: boolean; -} - -const ModelConfigManagerComponent: React.FC = ({ - isOpen, - onClose, - onConfigChange -}) => { - const [activeTab, setActiveTab] = useState<'channels' | 'models' | 'roles' | 'storage'>('channels'); - const [channels, setChannels] = useState([]); - const [models, setModels] = useState([]); - const [roles, setRoles] = useState([]); - const [editingChannel, setEditingChannel] = useState(null); - const [editingModel, setEditingModel] = useState(null); - const [editingRole, setEditingRole] = useState(null); - const [validationErrors, setValidationErrors] = useState([]); - const [importText, setImportText] = useState(''); - const [showImport, setShowImport] = useState(false); - const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); - const [storageInfo, setStorageInfo] = useState<{ used: number; available: number; channels: number; models: number; roles: number }>({ used: 0, available: 0, channels: 0, models: 0, roles: 0 }); - const [showApiKeys, setShowApiKeys] = useState>({}); - - useEffect(() => { - if (isOpen) { - loadConfig(); - updateStorageInfo(); - } - }, [isOpen]); - - const loadConfig = () => { - try { - setChannels(ModelConfigManager.getChannels()); - setModels(ModelConfigManager.getModels()); - setRoles(ModelConfigManager.getRoles()); - } catch (error) { - console.error('加载配置失败:', error); - showMessage('error', '加载配置失败,请检查浏览器存储设置'); - } - }; - - const updateStorageInfo = () => { - try { - setStorageInfo(ModelConfigManager.getStorageInfo()); - } catch (error) { - console.error('获取存储信息失败:', error); - } - }; - - const showMessage = (type: 'success' | 'error', text: string) => { - setMessage({ type, text }); - setTimeout(() => setMessage(null), 3000); - }; - - const toggleApiKeyVisibility = (channelId: string) => { - setShowApiKeys(prev => ({ - ...prev, - [channelId]: !prev[channelId] - })); - }; - - // 获取渠道显示的API密钥内容 - const getDisplayApiKey = (channel: ApiChannel): string => { - if (channel.isProtected) { - return '●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● (受保护)'; - } - return channel.apiKey || '未设置'; - }; - - // 检查用户是否可以修改此渠道 - const canEditChannel = (channel: ApiChannel): boolean => { - return channel.isCustom || !channel.isProtected; - }; - - // ============ 渠道管理 ============ - - const handleSaveChannel = () => { - if (!editingChannel) return; - - const errors = ModelConfigManager.validateChannel(editingChannel); - if (errors.length > 0) { - setValidationErrors(errors); - return; - } - - try { - if (editingChannel.isNew) { - const { id, createdAt, isCustom, isNew, ...channelData } = editingChannel; - ModelConfigManager.addChannel(channelData as Omit); - showMessage('success', '渠道添加成功'); - } else { - ModelConfigManager.updateChannel(editingChannel.id!, editingChannel); - showMessage('success', '渠道更新成功'); - } - - loadConfig(); - updateStorageInfo(); - setEditingChannel(null); - setValidationErrors([]); - onConfigChange(); - } catch (error) { - showMessage('error', '保存渠道失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - }; - - const handleDeleteChannel = (id: string) => { - // 检查是否有模型使用此渠道 - const modelsUsingChannel = models.filter(model => model.channelId === id); - if (modelsUsingChannel.length > 0) { - showMessage('error', `无法删除渠道:有 ${modelsUsingChannel.length} 个模型正在使用此渠道`); - return; - } - - if (window.confirm('确定要删除这个渠道吗?此操作不可撤销。')) { - try { - ModelConfigManager.deleteChannel(id); - loadConfig(); - updateStorageInfo(); - showMessage('success', '渠道删除成功'); - onConfigChange(); - } catch (error) { - showMessage('error', '删除渠道失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - } - }; - - const handleSetDefaultChannel = (id: string) => { - try { - ModelConfigManager.updateChannel(id, { isDefault: true }); - loadConfig(); - showMessage('success', '默认渠道设置成功'); - } catch (error) { - showMessage('error', '设置默认渠道失败'); - } - }; - - const handleEditChannel = (channel: ApiChannel) => { - if (channel.isProtected && !channel.isCustom) { - // 对于受保护的预置渠道,创建一个编辑副本,但不显示真实的API密钥 - setEditingChannel({ - ...channel, - apiKey: '' // 不显示受保护的密钥 - }); - } else { - setEditingChannel(channel); - } - }; - - // ============ 模型管理 ============ - - const handleSaveModel = () => { - if (!editingModel) return; - - const errors = ModelConfigManager.validateModel(editingModel); - if (errors.length > 0) { - setValidationErrors(errors); - return; - } - - try { - if (editingModel.isNew) { - const { id, createdAt, isCustom, isNew, ...modelData } = editingModel; - ModelConfigManager.addModel(modelData as Omit); - showMessage('success', '模型添加成功'); - } else { - ModelConfigManager.updateModel(editingModel.id!, editingModel); - showMessage('success', '模型更新成功'); - } - - loadConfig(); - updateStorageInfo(); - setEditingModel(null); - setValidationErrors([]); - onConfigChange(); - } catch (error) { - showMessage('error', '保存模型失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - }; - - const handleDeleteModel = (id: string) => { - // 检查是否有角色使用此模型 - const rolesUsingModel = roles.filter(role => role.modelId === id); - if (rolesUsingModel.length > 0) { - showMessage('error', `无法删除模型:有 ${rolesUsingModel.length} 个角色正在使用此模型`); - return; - } - - if (window.confirm('确定要删除这个模型吗?此操作不可撤销。')) { - try { - ModelConfigManager.deleteModel(id); - loadConfig(); - updateStorageInfo(); - showMessage('success', '模型删除成功'); - onConfigChange(); - } catch (error) { - showMessage('error', '删除模型失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - } - }; - - // ============ 角色管理 ============ - - const handleSaveRole = () => { - if (!editingRole) return; - - if (!editingRole.name?.trim() || !editingRole.systemPrompt?.trim() || !editingRole.modelId) { - setValidationErrors(['角色名称、系统提示词和关联模型都不能为空']); - return; - } - - try { - if (editingRole.isNew) { - const { id, isNew, ...roleData } = editingRole; - ModelConfigManager.addRole(roleData as Omit); - showMessage('success', '角色添加成功'); - } else { - ModelConfigManager.updateRole(editingRole.id!, editingRole); - showMessage('success', '角色更新成功'); - } - - loadConfig(); - updateStorageInfo(); - setEditingRole(null); - setValidationErrors([]); - onConfigChange(); - } catch (error) { - showMessage('error', '保存角色失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - }; - - const handleDeleteRole = (id: string) => { - if (window.confirm('确定要删除这个角色吗?此操作不可撤销。')) { - try { - ModelConfigManager.deleteRole(id); - loadConfig(); - updateStorageInfo(); - showMessage('success', '角色删除成功'); - onConfigChange(); - } catch (error) { - showMessage('error', '删除角色失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - } - }; - - // ============ 通用操作 ============ - - const handleExport = () => { - try { - const config = ModelConfigManager.exportConfig(); - const blob = new Blob([config], { type: 'application/json' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = `multi-mind-chat-config-${new Date().toISOString().split('T')[0]}.json`; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - showMessage('success', '配置导出成功'); - } catch (error) { - showMessage('error', '导出配置失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - }; - - const handleImport = () => { - if (!importText.trim()) { - showMessage('error', '请输入配置内容'); - return; - } - - try { - const result = ModelConfigManager.importConfig(importText); - if (result.success) { - loadConfig(); - updateStorageInfo(); - setImportText(''); - setShowImport(false); - showMessage('success', result.message); - onConfigChange(); - } else { - showMessage('error', result.message); - } - } catch (error) { - showMessage('error', '导入配置时发生错误: ' + (error instanceof Error ? error.message : '未知错误')); - } - }; - - const handleReset = () => { - if (window.confirm('确定要重置为默认配置吗?这将删除所有自定义配置。')) { - try { - ModelConfigManager.resetToDefaults(); - loadConfig(); - updateStorageInfo(); - showMessage('success', '已重置为默认配置'); - onConfigChange(); - } catch (error) { - showMessage('error', '重置配置失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - } - }; - - const handleClearAllData = () => { - if (window.confirm('警告:这将清空所有配置数据,包括渠道、模型和角色!此操作不可撤销,确定继续吗?')) { - try { - ModelConfigManager.clearAllData(); - loadConfig(); - updateStorageInfo(); - showMessage('success', '所有数据已清空'); - onConfigChange(); - } catch (error) { - showMessage('error', '清空数据失败: ' + (error instanceof Error ? error.message : '未知错误')); - } - } - }; - - if (!isOpen) return null; - - return ( -
-
- {/* Header */} -
-
- -

Multi-Mind Chat 配置管理

-
-
- - - - -
-
- - {/* Message */} - {message && ( -
- {message.type === 'success' ? : } - {message.text} -
- )} - - {/* Import Section */} - {showImport && ( -
-

导入配置

-