import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Card, Form, Input, Button, Select, Slider, InputNumber, message, Space, Typography, Spin, Modal, Tooltip, Alert } from 'antd'; import { SettingOutlined, SaveOutlined, DeleteOutlined, ReloadOutlined, ArrowLeftOutlined, InfoCircleOutlined} from '@ant-design/icons'; import { settingsApi } from '../services/api'; import type { SettingsUpdate } from '../types'; const { Title, Paragraph } = Typography; const { Option } = Select; export default function SettingsPage() { const navigate = useNavigate(); const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [initialLoading, setInitialLoading] = useState(true); const [hasSettings, setHasSettings] = useState(false); const [isDefaultSettings, setIsDefaultSettings] = useState(false); const [modelOptions, setModelOptions] = useState>([]); const [fetchingModels, setFetchingModels] = useState(false); const [modelsFetched, setModelsFetched] = useState(false); useEffect(() => { loadSettings(); }, []); const loadSettings = async () => { setInitialLoading(true); try { const settings = await settingsApi.getSettings(); form.setFieldsValue(settings); // 判断是否为默认设置(id='0'表示来自.env的默认配置) if (settings.id === '0' || !settings.id) { setIsDefaultSettings(true); setHasSettings(false); } else { setIsDefaultSettings(false); setHasSettings(true); } } catch (error: any) { // 如果404表示还没有设置,使用默认值 if (error?.response?.status === 404) { setHasSettings(false); setIsDefaultSettings(true); form.setFieldsValue({ api_provider: 'openai', api_base_url: 'https://api.openai.com/v1', model_name: 'gpt-4', temperature: 0.7, max_tokens: 2000, }); } else { message.error('加载设置失败'); } } finally { setInitialLoading(false); } }; const handleSave = async (values: SettingsUpdate) => { setLoading(true); try { await settingsApi.saveSettings(values); message.success('设置已保存'); setHasSettings(true); setIsDefaultSettings(false); } catch (error) { message.error('保存设置失败'); } finally { setLoading(false); } }; const handleReset = () => { Modal.confirm({ title: '重置设置', content: '确定要重置为默认值吗?', okText: '确定', cancelText: '取消', onOk: () => { form.setFieldsValue({ api_provider: 'openai', api_key: '', api_base_url: 'https://api.openai.com/v1', model_name: 'gpt-4', temperature: 0.7, max_tokens: 2000, }); message.info('已重置为默认值,请点击保存'); }, }); }; const handleDelete = () => { Modal.confirm({ title: '删除设置', content: '确定要删除所有设置吗?此操作不可恢复。', okText: '确定', cancelText: '取消', okType: 'danger', onOk: async () => { setLoading(true); try { await settingsApi.deleteSettings(); message.success('设置已删除'); setHasSettings(false); form.resetFields(); } catch (error) { message.error('删除设置失败'); } finally { setLoading(false); } }, }); }; const apiProviders = [ { value: 'openai', label: 'OpenAI', defaultUrl: 'https://api.openai.com/v1' }, { value: 'azure', label: 'Azure OpenAI', defaultUrl: 'https://YOUR-RESOURCE.openai.azure.com' }, { value: 'anthropic', label: 'Anthropic', defaultUrl: 'https://api.anthropic.com' }, { value: 'custom', label: '自定义', defaultUrl: '' }, ]; const handleProviderChange = (value: string) => { const provider = apiProviders.find(p => p.value === value); if (provider && provider.defaultUrl) { form.setFieldValue('api_base_url', provider.defaultUrl); } // 清空模型列表,需要重新获取 setModelOptions([]); setModelsFetched(false); }; const handleFetchModels = async (silent: boolean = false) => { const apiKey = form.getFieldValue('api_key'); const apiBaseUrl = form.getFieldValue('api_base_url'); const provider = form.getFieldValue('api_provider'); if (!apiKey || !apiBaseUrl) { if (!silent) { message.warning('请先填写 API 密钥和 API 地址'); } return; } setFetchingModels(true); try { const response = await settingsApi.getAvailableModels({ api_key: apiKey, api_base_url: apiBaseUrl, provider: provider || 'openai' }); setModelOptions(response.models); setModelsFetched(true); if (!silent) { message.success(`成功获取 ${response.count || response.models.length} 个可用模型`); } } catch (error: any) { const errorMsg = error?.response?.data?.detail || '获取模型列表失败'; if (!silent) { message.error(errorMsg); } setModelOptions([]); setModelsFetched(true); // 即使失败也标记为已尝试,避免重复请求 } finally { setFetchingModels(false); } }; const handleModelSelectFocus = () => { // 如果还没有获取过模型列表,自动获取 if (!modelsFetched && !fetchingModels) { handleFetchModels(true); // silent模式,不显示成功消息 } }; return (
{/* 标题栏 */}
配置你的AI API接口参数,这些设置将用于小说生成、角色创建等AI功能。 {/* 默认配置提示 */} {isDefaultSettings && (

当前显示的是从服务器 .env 文件读取的默认配置。

点击"保存设置"后,配置将保存到数据库并同步更新到 .env 文件。

} type="info" showIcon style={{ marginBottom: 16 }} /> )} {/* 已保存配置提示 */} {hasSettings && !isDefaultSettings && ( )} {/* 表单 */}
API 提供商 } name="api_provider" rules={[{ required: true, message: '请选择API提供商' }]} > API 密钥 } name="api_key" rules={[{ required: true, message: '请输入API密钥' }]} > API 地址 } name="api_base_url" rules={[ { required: true, message: '请输入API地址' }, { type: 'url', message: '请输入有效的URL' } ]} > 模型名称 } name="model_name" rules={[{ required: true, message: '请输入或选择模型名称' }]} >