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[]>;
|
||||
|
||||
/* ── 清理操作子组件 ── */
|
||||
interface CleanupPreviewItem { count: number; days: number }
|
||||
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 [cleaning, setCleaning] = useState(false);
|
||||
const [result, setResult] = useState<string>('');
|
||||
|
||||
const LABEL_MAP: Record<string, string> = {
|
||||
tickets: '工单', audit_logs: '审计日志',
|
||||
query_history: '查询历史', slow_logs: '慢日志',
|
||||
};
|
||||
|
||||
const handlePreview = async () => {
|
||||
setLoading(true);
|
||||
setResult('');
|
||||
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);
|
||||
} catch {
|
||||
setResult('预览失败');
|
||||
@@ -67,7 +73,7 @@ function CleanupActions() {
|
||||
{preview && (
|
||||
<div className="text-xs text-[var(--dg-text-muted)] space-y-1">
|
||||
{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>
|
||||
)}
|
||||
@@ -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 }> = {
|
||||
query: { label: '查询控制', icon: '🔍', desc: 'LIMIT 上限、超时、自动 LIMIT、危险检测' },
|
||||
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>;
|
||||
}
|
||||
|
||||
const groupKeys = Object.keys(settings);
|
||||
// 按 GROUP_ORDER 排序,过滤隐藏分组
|
||||
const visibleGroups = GROUP_ORDER.filter(g => settings[g]);
|
||||
const hasChanges = Object.keys(changed).length > 0;
|
||||
|
||||
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 items = settings[group] || [];
|
||||
const items = (settings[group] || []).filter((it: SettingItem) => !HIDDEN_GROUPS.has(it.group));
|
||||
|
||||
return (
|
||||
<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">
|
||||
{items.map(item => {
|
||||
{items.map((item: SettingItem) => {
|
||||
const val = displayValue(item);
|
||||
const isChanged = changed[item.key] !== undefined;
|
||||
// 敏感字段列表 → textarea 多行展示
|
||||
const isSensitiveList = item.key === 'data_masking_sensitive_fields';
|
||||
|
||||
return (
|
||||
<div key={item.key} className="flex items-center justify-between gap-4 py-2">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div key={item.key} className={isSensitiveList ? 'py-2' : 'flex items-center justify-between gap-4 py-2'}>
|
||||
<div className={isSensitiveList ? 'mb-2' : 'flex-1 min-w-0'}>
|
||||
<div className="text-sm text-[var(--dg-text-primary)]">{item.label}</div>
|
||||
{item.description && (
|
||||
<div className="text-xs text-[var(--dg-text-muted)] mt-0.5">{item.description}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isSensitiveList && (
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
{/* 重置按钮 */}
|
||||
{isChanged && item.default_value && (
|
||||
@@ -212,8 +226,8 @@ export default function PlatformConfigPage() {
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* 整数 → 数字输入 */}
|
||||
{item.value_type === 'integer' && (
|
||||
{/* 整数/数字 → 数字输入 */}
|
||||
{(item.value_type === 'integer' || item.value_type === 'number') && (
|
||||
<input
|
||||
type="number"
|
||||
value={parseInt(val) || 0}
|
||||
@@ -224,7 +238,7 @@ export default function PlatformConfigPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 字符串 → 文本 */}
|
||||
{/* 普通字符串 → 文本 */}
|
||||
{item.value_type === 'string' && (
|
||||
<input
|
||||
type="text"
|
||||
@@ -241,6 +255,19 @@ export default function PlatformConfigPage() {
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
|
||||
)}
|
||||
</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>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in new issue
Block a user