sync: 同步 sql-platform-pro 最新代码 — 权限体系安全修复 v0.8.1
- P0: 实例访问控制全线接入权限组 (829测试) - P1: permissions字段激活 + 操作级权限检查 (843测试) - 前端: 删除数据权限页面, 权限组合并为唯一入口 - 新增: CodeMirror编辑器 + 查询模板 + 慢查询分析 - 新增: MongoDB管理页面 + Dashboard增强 - 修复: Pydantic V2迁移 + 前端TS错误修复
This commit is contained in:
1 parent
6fcd9465cf
commit
4c9427e546
105 files changed
+23680
-471
No files matched your search
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
ClipboardList, Clock, CheckCircle, AlertTriangle,
|
||||
Database, ChevronRight,
|
||||
X, AlertCircle, Info, Bell
|
||||
X, AlertCircle, Info, Bell, Activity, TrendingUp, Zap, Server,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { dbInstances, statusMap, statusColorMap, dbTypeColorMap, tickets, announcements } from '@/lib/mock';
|
||||
@@ -296,6 +296,127 @@ export default function Dashboard() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 数据源健康概览 + 查询统计 */}
|
||||
<div className="grid grid-cols-2 gap-5">
|
||||
{/* 数据源健康概览 */}
|
||||
<Card className="dg-card">
|
||||
<CardHeader className="pb-2 border-b border-[var(--dg-border)]">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-[15px] font-semibold flex items-center gap-2">
|
||||
<Server className="w-4 h-4 text-blue-400" />
|
||||
数据源健康概览
|
||||
</CardTitle>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">{onlineInstances}/{dbInstances.length} 在线</span>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
{/* 按类型统计 */}
|
||||
<div className="grid grid-cols-4 gap-2 p-4 border-b border-[var(--dg-border-light)]">
|
||||
{(['mysql', 'postgresql', 'redis', 'mongodb'] as const).map(type => {
|
||||
const typeInstances = dbInstances.filter(i => i.dbType === type);
|
||||
const typeOnline = typeInstances.filter(i => i.status === 'online').length;
|
||||
const iconMap: Record<string, string> = { mysql: '/db-logos/mysql.svg', postgresql: '/db-logos/postgres.svg', redis: '/db-logos/redis.svg', mongodb: '/db-logos/mongo.svg' };
|
||||
const labelMap: Record<string, string> = { mysql: 'MySQL', postgresql: 'PostgreSQL', redis: 'Redis', mongodb: 'MongoDB' };
|
||||
return (
|
||||
<div key={type} className="text-center p-2.5 rounded-lg bg-[var(--dg-bg-tertiary)]">
|
||||
<img src={iconMap[type]} alt="" className="w-6 h-6 object-contain mx-auto mb-1.5" />
|
||||
<div className="text-sm font-semibold">{typeInstances.length}</div>
|
||||
<div className="text-[10px] text-[var(--dg-text-muted)]">{labelMap[type]}</div>
|
||||
<div className="text-[10px] mt-1">
|
||||
<span className={typeOnline === typeInstances.length && typeInstances.length > 0 ? 'text-green-400' : typeOnline > 0 ? 'text-yellow-400' : 'text-[var(--dg-text-muted)]'}>
|
||||
{typeOnline}/{typeInstances.length}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* 最近告警实例 */}
|
||||
{dbInstances.filter(i => i.status === 'warning' || i.status === 'offline').length > 0 && (
|
||||
<div className="p-4">
|
||||
<div className="text-xs text-[var(--dg-text-muted)] mb-2">需要关注</div>
|
||||
{dbInstances.filter(i => i.status === 'warning' || i.status === 'offline').slice(0, 3).map(inst => (
|
||||
<div key={inst.id} className="flex items-center gap-3 py-2">
|
||||
<span className={`w-2 h-2 rounded-full ${inst.status === 'offline' ? 'bg-red-400' : 'bg-yellow-400'}`} />
|
||||
<span className="text-sm flex-1">{inst.name}</span>
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">{inst.host}:{inst.port}</span>
|
||||
<span className={`text-[10px] px-2 py-0.5 rounded-full ${inst.status === 'offline' ? 'bg-red-500/15 text-red-400' : 'bg-yellow-500/15 text-yellow-400'}`}>
|
||||
{inst.status === 'offline' ? '离线' : '告警'}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 查询统计 */}
|
||||
<Card className="dg-card">
|
||||
<CardHeader className="pb-2 border-b border-[var(--dg-border)]">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-[15px] font-semibold flex items-center gap-2">
|
||||
<Activity className="w-4 h-4 text-green-400" />
|
||||
查询统计
|
||||
</CardTitle>
|
||||
<div className="flex gap-1.5">
|
||||
{['今日', '本周', '本月'].map((p, i) => (
|
||||
<button key={p} className={`px-3 py-1 text-xs rounded-md transition-colors ${i === 0 ? 'bg-green-500/15 text-green-400' : 'text-[var(--dg-text-muted)] hover:text-[var(--dg-text-secondary)]'}`}>
|
||||
{p}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-4">
|
||||
{/* 统计指标 */}
|
||||
<div className="grid grid-cols-3 gap-4 mb-4">
|
||||
<div className="p-3 rounded-lg bg-[var(--dg-bg-tertiary)]">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Zap className="w-3.5 h-3.5 text-blue-400" />
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">总查询数</span>
|
||||
</div>
|
||||
<div className="text-xl font-bold">1,284</div>
|
||||
<div className="text-[10px] text-green-400 mt-1">↑ 12% vs 昨日</div>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg bg-[var(--dg-bg-tertiary)]">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<TrendingUp className="w-3.5 h-3.5 text-orange-400" />
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">平均耗时</span>
|
||||
</div>
|
||||
<div className="text-xl font-bold">23ms</div>
|
||||
<div className="text-[10px] text-green-400 mt-1">↓ 8% vs 昨日</div>
|
||||
</div>
|
||||
<div className="p-3 rounded-lg bg-[var(--dg-bg-tertiary)]">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<AlertTriangle className="w-3.5 h-3.5 text-red-400" />
|
||||
<span className="text-xs text-[var(--dg-text-muted)]">慢查询</span>
|
||||
</div>
|
||||
<div className="text-xl font-bold">7</div>
|
||||
<div className="text-[10px] text-red-400 mt-1">↑ 3 vs 昨日</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 查询类型分布 */}
|
||||
<div className="space-y-2.5">
|
||||
<div className="text-xs text-[var(--dg-text-muted)]">查询类型分布</div>
|
||||
{[
|
||||
{ label: 'MySQL', count: 682, total: 1284, color: 'bg-[#00758f]' },
|
||||
{ label: 'PostgreSQL', count: 312, total: 1284, color: 'bg-[#336791]' },
|
||||
{ label: 'Redis', count: 198, total: 1284, color: 'bg-[#dc382d]' },
|
||||
{ label: 'MongoDB', count: 92, total: 1284, color: 'bg-[#47a248]' },
|
||||
].map(item => (
|
||||
<div key={item.label} className="flex items-center gap-3">
|
||||
<span className="text-xs w-20 text-[var(--dg-text-secondary)]">{item.label}</span>
|
||||
<div className="flex-1 h-2 rounded-full bg-[var(--dg-bg-tertiary)] overflow-hidden">
|
||||
<div className={`h-full rounded-full ${item.color}`} style={{ width: `${(item.count / item.total) * 100}%` }} />
|
||||
</div>
|
||||
<span className="text-xs w-12 text-right text-[var(--dg-text-muted)]">{item.count}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 待处理工单 */}
|
||||
<Card className="dg-card">
|
||||
<CardHeader className="pb-2 border-b border-[var(--dg-border)]">
|
||||
|
||||
Reference in new issue
Block a user