fix: 平台配置页面修复 - 隐藏LDAP重复分组、敏感字段列表textarea多行展示、分组排序优化

- 过滤ldap分组(有独立LDAP页面不重复展示)
- 敏感字段列表从单行input改为textarea每行一个字段名(key修正data_masking_sensitive_fields)
- 添加GROUP_ORDER固定展示顺序:查询控制→安全策略→数据脱敏→日志清理→执行器→Ghost/OSC
- 所有分组输入框均有默认值,cleanup组retention天数输入框正常显示
This commit is contained in:
logan committed 2026-06-01 21:07:44 +08:00
1 parent 9c457eb599
commit 132b2db2d2
1 file changed
+40 -13
+40 -13
View File
@@ -16,17 +16,23 @@ interface SettingItem {
type SettingsMap = Record<string, SettingItem[]>; type SettingsMap = Record<string, SettingItem[]>;
/* ── 清理操作子组件 ── */ /* ── 清理操作子组件 ── */
interface CleanupPreviewItem { count: number; days: number }
function CleanupActions() { function CleanupActions() {
const [preview, setPreview] = useState<Record<string, number> | null>(null); const [preview, setPreview] = useState<Record<string, CleanupPreviewItem> | null>(null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [cleaning, setCleaning] = useState(false); const [cleaning, setCleaning] = useState(false);
const [result, setResult] = useState<string>(''); const [result, setResult] = useState<string>('');
const LABEL_MAP: Record<string, string> = {
tickets: '工单', audit_logs: '审计日志',
query_history: '查询历史', slow_logs: '慢日志',
};
const handlePreview = async () => { const handlePreview = async () => {
setLoading(true); setLoading(true);
setResult(''); setResult('');
try { try {
const res = await api.get('/settings/cleanup/preview') as Record<string, number>; const res = await api.get('/settings/cleanup/preview') as Record<string, CleanupPreviewItem>;
setPreview(res); setPreview(res);
} catch { } catch {
setResult('预览失败'); setResult('预览失败');
@@ -67,7 +73,7 @@ function CleanupActions() {
{preview && ( {preview && (
<div className="text-xs text-[var(--dg-text-muted)] space-y-1"> <div className="text-xs text-[var(--dg-text-muted)] space-y-1">
{Object.entries(preview).map(([k, v]) => ( {Object.entries(preview).map(([k, v]) => (
<div key={k}>{k}: <span className="text-[var(--dg-text-primary)] font-medium">{v}</span> 条</div> <div key={k}>{LABEL_MAP[k] || k}(保留 {v.days} 天): <span className="text-[var(--dg-text-primary)] font-medium">{v.count}</span> 条待清理</div>
))} ))}
</div> </div>
)} )}
@@ -78,7 +84,11 @@ function CleanupActions() {
); );
} }
/* ── 分组元信息 ── */ // 隐藏的分组(有独立页面的不在平台配置中展示)
const HIDDEN_GROUPS = new Set(['ldap', 'appearance']);
/* ── 分组元信息 + 展示顺序 ── */
const GROUP_ORDER = ['query', 'security', 'data_masking', 'cleanup', 'executor', 'ghost'];
const GROUP_META: Record<string, { label: string; icon: string; desc: string }> = { const GROUP_META: Record<string, { label: string; icon: string; desc: string }> = {
query: { label: '查询控制', icon: '🔍', desc: 'LIMIT 上限、超时、自动 LIMIT、危险检测' }, query: { label: '查询控制', icon: '🔍', desc: 'LIMIT 上限、超时、自动 LIMIT、危险检测' },
security: { label: '安全策略', icon: '🔒', desc: '登录锁定、密码策略、会话超时' }, security: { label: '安全策略', icon: '🔒', desc: '登录锁定、密码策略、会话超时' },
@@ -139,7 +149,8 @@ export default function PlatformConfigPage() {
return <div className="text-center py-20 text-[var(--dg-text-muted)]">加载中...</div>; return <div className="text-center py-20 text-[var(--dg-text-muted)]">加载中...</div>;
} }
const groupKeys = Object.keys(settings); // 按 GROUP_ORDER 排序,过滤隐藏分组
const visibleGroups = GROUP_ORDER.filter(g => settings[g]);
const hasChanges = Object.keys(changed).length > 0; const hasChanges = Object.keys(changed).length > 0;
return ( return (
@@ -156,9 +167,9 @@ export default function PlatformConfigPage() {
)} )}
{/* 各分组 */} {/* 各分组 */}
{groupKeys.map(group => { {visibleGroups.map(group => {
const meta = GROUP_META[group] || { label: group, icon: '⚙️', desc: '' }; const meta = GROUP_META[group] || { label: group, icon: '⚙️', desc: '' };
const items = settings[group] || []; const items = (settings[group] || []).filter((it: SettingItem) => !HIDDEN_GROUPS.has(it.group));
return ( return (
<div key={group} className="bg-[var(--dg-bg-secondary)] rounded-xl overflow-hidden"> <div key={group} className="bg-[var(--dg-bg-secondary)] rounded-xl overflow-hidden">
@@ -173,19 +184,22 @@ export default function PlatformConfigPage() {
{/* 设置项列表 */} {/* 设置项列表 */}
<div className="px-5 pb-4 space-y-3"> <div className="px-5 pb-4 space-y-3">
{items.map(item => { {items.map((item: SettingItem) => {
const val = displayValue(item); const val = displayValue(item);
const isChanged = changed[item.key] !== undefined; const isChanged = changed[item.key] !== undefined;
// 敏感字段列表 → textarea 多行展示
const isSensitiveList = item.key === 'data_masking_sensitive_fields';
return ( return (
<div key={item.key} className="flex items-center justify-between gap-4 py-2"> <div key={item.key} className={isSensitiveList ? 'py-2' : 'flex items-center justify-between gap-4 py-2'}>
<div className="flex-1 min-w-0"> <div className={isSensitiveList ? 'mb-2' : 'flex-1 min-w-0'}>
<div className="text-sm text-[var(--dg-text-primary)]">{item.label}</div> <div className="text-sm text-[var(--dg-text-primary)]">{item.label}</div>
{item.description && ( {item.description && (
<div className="text-xs text-[var(--dg-text-muted)] mt-0.5">{item.description}</div> <div className="text-xs text-[var(--dg-text-muted)] mt-0.5">{item.description}</div>
)} )}
</div> </div>
{!isSensitiveList && (
<div className="flex items-center gap-2 shrink-0"> <div className="flex items-center gap-2 shrink-0">
{/* 重置按钮 */} {/* 重置按钮 */}
{isChanged && item.default_value && ( {isChanged && item.default_value && (
@@ -212,8 +226,8 @@ export default function PlatformConfigPage() {
</button> </button>
)} )}
{/* 整数 → 数字输入 */} {/* 整数/数字 → 数字输入 */}
{item.value_type === 'integer' && ( {(item.value_type === 'integer' || item.value_type === 'number') && (
<input <input
type="number" type="number"
value={parseInt(val) || 0} value={parseInt(val) || 0}
@@ -224,7 +238,7 @@ export default function PlatformConfigPage() {
/> />
)} )}
{/* 字符串 → 文本 */} {/* 普通字符串 → 文本 */}
{item.value_type === 'string' && ( {item.value_type === 'string' && (
<input <input
type="text" type="text"
@@ -241,6 +255,19 @@ export default function PlatformConfigPage() {
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500" /> <span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
)} )}
</div> </div>
)}
{/* 敏感字段列表 → textarea 每行一个字段 */}
{isSensitiveList && (
<textarea
rows={Math.min(8, Math.max(3, val.split(',').length))}
value={val.split(',').map((s: string) => s.trim()).join('\n')}
onChange={e => handleChange(item.key, e.target.value.split('\n').map((s: string) => s.trim()).filter(Boolean).join(','))}
className={`w-full px-3 py-2 rounded-lg text-xs font-mono bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-primary)] outline-none resize-y ${
isChanged ? 'ring-1 ring-emerald-500/50' : ''
}`}
/>
)}
</div> </div>
); );
})} })}