feat(log-center): 日志中心 v4 全量组件 — 统一查询面板/时间选择器/统计图表/聚类/数据源管理
This commit is contained in:
1 parent
880cf050fe
commit
bdf3c2daf4
30 files changed
+4276
-136
No files matched your search
@@ -0,0 +1,174 @@
|
||||
// LogQueryToolbar.tsx — 查询工具栏(统计条 + 分页 + 操作)// Logan.Li
|
||||
import { ArrowUpDown, Bell, Download, Copy, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
interface Props {
|
||||
total: number;
|
||||
queryTime?: number;
|
||||
status?: 'precise' | 'partial' | 'failed';
|
||||
perPage: number;
|
||||
onPerPageChange: (n: number) => void;
|
||||
page: number;
|
||||
totalPages: number;
|
||||
onPageChange: (p: number) => void;
|
||||
sortDirection?: 'asc' | 'desc';
|
||||
onSortToggle?: () => void;
|
||||
onExport?: () => void;
|
||||
onCopy?: () => void;
|
||||
onBellClick?: () => void;
|
||||
hasAlertRule?: boolean;
|
||||
}
|
||||
|
||||
const STATUS_LABEL: Record<string, { text: string; color: string }> = {
|
||||
precise: { text: '结果精确', color: '#22c55e' },
|
||||
partial: { text: '部分命中', color: '#f59e0b' },
|
||||
failed: { text: '查询失败', color: '#ef4444' },
|
||||
};
|
||||
|
||||
export function LogQueryToolbar({
|
||||
total, queryTime, status = 'precise', perPage, onPerPageChange,
|
||||
page, totalPages, onPageChange, sortDirection, onSortToggle,
|
||||
onExport, onCopy, onBellClick, hasAlertRule,
|
||||
}: Props) {
|
||||
const [jumpPage, setJumpPage] = useState('');
|
||||
const st = STATUS_LABEL[status] || STATUS_LABEL.precise;
|
||||
|
||||
const handleJump = () => {
|
||||
const p = parseInt(jumpPage, 10);
|
||||
if (p >= 1 && p <= totalPages) onPageChange(p);
|
||||
setJumpPage('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-3 py-2 px-3 border rounded-lg" style={{ background: 'var(--dg-bg-secondary)', borderColor: 'var(--dg-border)' }}>
|
||||
{/* 左侧统计 */}
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-base font-semibold" style={{ color: 'var(--dg-text-primary)' }}>
|
||||
日志条数: {total.toLocaleString()}
|
||||
</span>
|
||||
<span
|
||||
className="text-[10px] px-1.5 py-0.5 rounded font-medium"
|
||||
style={{ backgroundColor: `${st.color}15`, color: st.color }}
|
||||
>
|
||||
{st.text}
|
||||
</span>
|
||||
{queryTime !== undefined && (
|
||||
<span className="text-xs" style={{ color: 'var(--dg-text-muted)' }}>耗时: {queryTime}ms</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 分页 */}
|
||||
<div className="flex items-center gap-1.5 ml-auto">
|
||||
<select
|
||||
value={perPage}
|
||||
onChange={e => onPerPageChange(Number(e.target.value))}
|
||||
className="h-7 px-1.5 text-[11px] rounded dg-input"
|
||||
style={{ background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)' }}
|
||||
>
|
||||
{[10, 20, 50, 100, 200].map(n => <option key={n} value={n}>每页 {n}</option>)}
|
||||
</select>
|
||||
|
||||
<span className="text-[11px] mx-1" style={{ color: 'var(--dg-text-muted)' }}>
|
||||
总数: {total.toLocaleString()} 条
|
||||
</span>
|
||||
|
||||
<button
|
||||
onClick={() => onPageChange(Math.max(1, page - 1))}
|
||||
disabled={page <= 1}
|
||||
className="h-7 w-7 flex items-center justify-center rounded border disabled:opacity-30 transition-colors"
|
||||
style={{ borderColor: 'var(--dg-border)', color: 'var(--dg-text-secondary)', background: 'var(--dg-bg-tertiary)' }}
|
||||
>
|
||||
<ChevronLeft className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
||||
{/* 页码 */}
|
||||
{Array.from({ length: Math.min(5, totalPages) }, (_, i) => {
|
||||
let p: number;
|
||||
if (totalPages <= 5) p = i + 1;
|
||||
else if (page <= 3) p = i + 1;
|
||||
else if (page >= totalPages - 2) p = totalPages - 4 + i;
|
||||
else p = page - 2 + i;
|
||||
return (
|
||||
<button
|
||||
key={p}
|
||||
onClick={() => onPageChange(p)}
|
||||
className="h-7 min-w-[28px] px-1.5 text-[11px] rounded border transition-colors"
|
||||
style={{
|
||||
borderColor: p === page ? 'var(--dg-blue)' : 'var(--dg-border)',
|
||||
background: p === page ? 'var(--dg-blue)' : 'var(--dg-bg-tertiary)',
|
||||
color: p === page ? '#fff' : 'var(--dg-text-secondary)',
|
||||
}}
|
||||
>
|
||||
{p}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
{totalPages > 5 && page < totalPages - 2 && (
|
||||
<span className="text-[11px] px-1" style={{ color: 'var(--dg-text-muted)' }}>...</span>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => onPageChange(Math.min(totalPages, page + 1))}
|
||||
disabled={page >= totalPages}
|
||||
className="h-7 w-7 flex items-center justify-center rounded border disabled:opacity-30 transition-colors"
|
||||
style={{ borderColor: 'var(--dg-border)', color: 'var(--dg-text-secondary)', background: 'var(--dg-bg-tertiary)' }}
|
||||
>
|
||||
<ChevronRight className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-1 ml-1">
|
||||
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}>到第</span>
|
||||
<input
|
||||
type="number"
|
||||
value={jumpPage}
|
||||
onChange={e => setJumpPage(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && handleJump()}
|
||||
className="h-7 w-10 text-center text-[11px] rounded dg-input"
|
||||
style={{ background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)' }}
|
||||
/>
|
||||
<span className="text-[10px]" style={{ color: 'var(--dg-text-muted)' }}>页</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧操作 */}
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={onSortToggle}
|
||||
className="h-7 w-7 flex items-center justify-center rounded border transition-colors"
|
||||
style={{ borderColor: 'var(--dg-border)', color: 'var(--dg-text-muted)', background: 'var(--dg-bg-tertiary)' }}
|
||||
title={sortDirection === 'desc' ? '降序' : '升序'}
|
||||
>
|
||||
<ArrowUpDown className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={onExport}
|
||||
className="h-7 w-7 flex items-center justify-center rounded border transition-colors"
|
||||
style={{ borderColor: 'var(--dg-border)', color: 'var(--dg-text-muted)', background: 'var(--dg-bg-tertiary)' }}
|
||||
title="导出"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={onCopy}
|
||||
className="h-7 w-7 flex items-center justify-center rounded border transition-colors"
|
||||
style={{ borderColor: 'var(--dg-border)', color: 'var(--dg-text-muted)', background: 'var(--dg-bg-tertiary)' }}
|
||||
title="复制"
|
||||
>
|
||||
<Copy className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
<button
|
||||
onClick={onBellClick}
|
||||
className="relative h-7 w-7 flex items-center justify-center rounded border transition-colors"
|
||||
style={{ borderColor: 'var(--dg-border)', color: 'var(--dg-text-muted)', background: 'var(--dg-bg-tertiary)' }}
|
||||
title="关联告警"
|
||||
>
|
||||
<Bell className="h-3.5 w-3.5" />
|
||||
{hasAlertRule && (
|
||||
<span className="absolute -top-1 -right-1 h-2.5 w-2.5 rounded-full bg-red-500 ring-1" style={{ outline: '2px solid var(--dg-bg-secondary)' }} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user