import { Form, Input, Button, message, Tabs, Space, Card, Select, Modal, Radio, InputNumber } from 'antd'; import { useEffect, useState } from 'react'; import PageCard from '../../components/PageCard'; import { getAllConfig, setConfig } from '../../api/config'; import { vectorDBApi } from '../../api/vectorDB'; import { AppstoreOutlined, RobotOutlined, DatabaseOutlined, SkinOutlined } from '@ant-design/icons'; import { useTheme } from '../../contexts/ThemeContext'; import '../../styles/settings-tabs.css'; const APP_CONFIG_KEYS: {key: string, label: string, default?: string}[] = [ { key: 'APP_NAME', label: '应用名称' }, { key: 'APP_LOGO', label: 'LOGO地址' }, { key: 'APP_DOMAIN', label: '应用域名' }, { key: 'FILE_DOMAIN', label: '文件域名' }, ]; const VISION_CONFIG_KEYS = [ { key: 'AI_VISION_API_URL', label: '视觉模型 API 地址' }, { key: 'AI_VISION_MODEL', label: '视觉模型', default: 'Qwen/Qwen2.5-VL-32B-Instruct' }, { key: 'AI_VISION_API_KEY', label: '视觉模型 API Key' }, ]; const EMBED_CONFIG_KEYS = [ { key: 'AI_EMBED_API_URL', label: '嵌入模型 API 地址' }, { key: 'AI_EMBED_MODEL', label: '嵌入模型', default: 'Qwen/Qwen3-Embedding-8B' }, { key: 'AI_EMBED_API_KEY', label: '嵌入模型 API Key' }, ]; const ALL_AI_KEYS = [...VISION_CONFIG_KEYS, ...EMBED_CONFIG_KEYS]; // Theme related config keys const THEME_KEYS = { MODE: 'THEME_MODE', PRIMARY: 'THEME_PRIMARY_COLOR', RADIUS: 'THEME_BORDER_RADIUS', TOKENS: 'THEME_CUSTOM_TOKENS', CSS: 'THEME_CUSTOM_CSS', }; export default function SystemSettingsPage() { const [loading, setLoading] = useState(false); const [config, setConfigState] = useState | null>(null); const [activeTab, setActiveTab] = useState('appearance'); const { refreshTheme, previewTheme } = useTheme(); useEffect(() => { getAllConfig().then((data) => setConfigState(data as Record)); }, []); const handleSave = async (values: any) => { setLoading(true); try { for (const [key, value] of Object.entries(values)) { await setConfig(key, String(value ?? '')); } message.success('保存成功'); setConfigState({ ...config, ...values }); // trigger theme refresh if related keys changed if (Object.keys(values).some(k => Object.values(THEME_KEYS).includes(k))) { await refreshTheme(); } } catch (e: any) { message.error(e.message || '保存失败'); } setLoading(false); }; // 离开“外观设置”时,恢复后端持久化配置(取消未保存的预览) useEffect(() => { if (activeTab !== 'appearance') { refreshTheme(); } }, [activeTab]); if (!config) { return
加载中...
; } return ( 外观设置 ), children: (
{ try { const tokens = all[THEME_KEYS.TOKENS] ? JSON.parse(all[THEME_KEYS.TOKENS]) : undefined; previewTheme({ mode: all[THEME_KEYS.MODE], primaryColor: all[THEME_KEYS.PRIMARY], borderRadius: typeof all[THEME_KEYS.RADIUS] === 'number' ? all[THEME_KEYS.RADIUS] : undefined, customTokens: tokens, customCSS: all[THEME_KEYS.CSS], }); } catch { // JSON 不合法时忽略 tokens 预览,其他项仍然生效 previewTheme({ mode: all[THEME_KEYS.MODE], primaryColor: all[THEME_KEYS.PRIMARY], borderRadius: typeof all[THEME_KEYS.RADIUS] === 'number' ? all[THEME_KEYS.RADIUS] : undefined, customCSS: all[THEME_KEYS.CSS], }); } }} onFinish={async (vals) => { // Validate JSON if provided if (vals[THEME_KEYS.TOKENS]) { try { JSON.parse(vals[THEME_KEYS.TOKENS]); } catch { return message.error('高级 Token 需为合法 JSON'); } } await handleSave(vals); }} style={{ marginTop: 24 }} key={'appearance-' + JSON.stringify(config)} > 亮色 暗色 跟随系统
) }, { key: 'app', label: ( 应用设置 ), children: (
[key, config[key] ?? def ?? ''])), }} onFinish={handleSave} style={{ marginTop: 24 }} key={JSON.stringify(config)} > {APP_CONFIG_KEYS.map(({ key, label }) => ( ))}
), }, { key: 'ai', label: ( AI设置 ), children: (
[key, config[key] ?? def ?? ''])), }} onFinish={handleSave} style={{ marginTop: 24 }} key={JSON.stringify(config)} > {VISION_CONFIG_KEYS.map(({ key, label }) => ( ))} {EMBED_CONFIG_KEYS.map(({ key, label }) => ( ))}
), }, { key: 'vector-db', label: ( 向量数据库 ), children: (