import { useState, useEffect, useCallback } from 'react'; import api from '../api/request'; /* ── 类型 ── */ interface SettingItem { key: string; value: string; value_type: string; group: string; label: string; description: string | null; default_value: string | null; sort_order: number; } type SettingsMap = Record; /* ── 清理操作子组件 ── */ function CleanupActions() { const [preview, setPreview] = useState | null>(null); const [loading, setLoading] = useState(false); const [cleaning, setCleaning] = useState(false); const [result, setResult] = useState(''); const handlePreview = async () => { setLoading(true); setResult(''); try { const res = await api.get('/settings/cleanup/preview') as Record; setPreview(res); } catch { setResult('预览失败'); } finally { setLoading(false); } }; const handleCleanup = async () => { setCleaning(true); setResult(''); try { const res = await api.post('/settings/cleanup/run') as { message: string; stats: Record }; const total = Object.values(res.stats).reduce((a, b) => a + b, 0); setResult(`清理完成,共删除 ${total} 条记录`); setPreview(null); } catch { setResult('清理失败'); } finally { setCleaning(false); } }; return (
{preview && ( )}
{preview && (
{Object.entries(preview).map(([k, v]) => (
{k}: {v} 条
))}
)} {result && (
{result}
)}
); } /* ── 分组元信息 ── */ const GROUP_META: Record = { query: { label: '查询控制', icon: '🔍', desc: 'LIMIT 上限、超时、自动 LIMIT' }, security: { label: '安全策略', icon: '🔒', desc: '登录安全、密码策略' }, data_masking: { label: '数据脱敏', icon: '🛡️', desc: '控制查询结果中的敏感字段脱敏' }, cleanup: { label: '日志清理', icon: '🧹', desc: '工单、审计日志、查询历史保留天数' }, executor: { label: '执行器', icon: '⚙️', desc: 'SQL 执行超时' }, }; export default function PlatformConfigPage() { const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [changed, setChanged] = useState>({}); const [toast, setToast] = useState(''); const fetchSettings = useCallback(async () => { try { const res = await api.get('/settings'); setSettings(res as SettingsMap); } catch { setToast('加载设置失败'); } finally { setLoading(false); } }, []); useEffect(() => { fetchSettings(); }, [fetchSettings]); const handleChange = (key: string, value: string) => { setChanged(prev => ({ ...prev, [key]: value })); }; const handleSave = async () => { if (Object.keys(changed).length === 0) return; setSaving(true); try { const items = Object.entries(changed).map(([key, value]) => ({ key, value })); await api.put('/settings', items); setChanged({}); setToast('保存成功'); await fetchSettings(); } catch (e: any) { setToast(e?.response?.data?.detail || '保存失败'); } finally { setSaving(false); setTimeout(() => setToast(''), 3000); } }; const handleReset = (key: string, defaultValue: string | null) => { if (defaultValue) handleChange(key, defaultValue); }; const displayValue = (item: SettingItem) => changed[item.key] ?? item.value; if (loading) { return
加载中...
; } const groupKeys = Object.keys(settings); const hasChanges = Object.keys(changed).length > 0; return (
{/* Toast */} {toast && (
{toast}
)} {/* 各分组 */} {groupKeys.map(group => { const meta = GROUP_META[group] || { label: group, icon: '⚙️', desc: '' }; const items = settings[group] || []; return (
{/* 分组头 */}
{meta.icon}

{meta.label}

{meta.desc}

{/* 设置项列表 */}
{items.map(item => { const val = displayValue(item); const isChanged = changed[item.key] !== undefined; return (
{item.label}
{item.description && (
{item.description}
)}
{/* 重置按钮 */} {isChanged && item.default_value && ( )} {/* 布尔 → 开关 */} {item.value_type === 'boolean' && ( )} {/* 整数 → 数字输入 */} {item.value_type === 'integer' && ( handleChange(item.key, e.target.value)} className={`w-24 px-2 py-1 rounded-lg text-sm text-right bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-primary)] outline-none ${ isChanged ? 'ring-1 ring-emerald-500/50' : '' }`} /> )} {/* 字符串 → 文本 */} {item.value_type === 'string' && ( handleChange(item.key, e.target.value)} className={`w-48 px-2 py-1 rounded-lg text-sm bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-primary)] outline-none ${ isChanged ? 'ring-1 ring-emerald-500/50' : '' }`} /> )} {/* 变更指示 */} {isChanged && ( )}
); })}
{/* 日志清理分组专属操作区 */} {group === 'cleanup' && }
); })} {/* 保存按钮 */} {hasChanges && (
已修改 {Object.keys(changed).length} 项
)}
); }