fix: 平台配置页面修复 - 隐藏LDAP重复分组、敏感字段列表textarea多行展示、分组排序优化
- 过滤ldap分组(有独立LDAP页面不重复展示) - 敏感字段列表从单行input改为textarea每行一个字段名(key修正data_masking_sensitive_fields) - 添加GROUP_ORDER固定展示顺序:查询控制→安全策略→数据脱敏→日志清理→执行器→Ghost/OSC - 所有分组输入框均有默认值,cleanup组retention天数输入框正常显示
This commit is contained in:
1 parent
9c457eb599
commit
132b2db2d2
1 file changed
+40
-13
@@ -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>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in new issue
Block a user