Files
aiops/frontend/src/components/monitor/log-center/SavedQueriesPanel.tsx
T
Logan 7e1c8f583c feat: 工具配置增强、站点拨测与日志中心多项改进
- 工具配置:新增卸载 API/UI、Go 工具链、流水线构建自动安装依赖
- 修复 ListTools VerifyTool 递归栈溢出导致获取工具配置失败
- Node.js Alpine 安装改用 unofficial-builds musl 预编译包与 index.json 解析 LTS
- 站点拨测:详情页路由、告警自动恢复、并发拨测与默认可观测频率
- 日志中心:RBAC、查询构建器、ES/Loki 增强与审计 request/response 展示
- 发布配置:构建节点、流水线远程执行、Deploy RBAC 导航拆分
2026-07-02 07:47:33 +08:00

88 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from 'react';
import { X, Trash2, Play, Bookmark, Clock } from 'lucide-react';
import type { SavedQuery } from './types-v3';
import { savedQueryStorage } from './types-v3';
import { ENGINE_META } from './types-v3';
interface Props {
visible: boolean;
onClose: () => void;
onSelect: (item: SavedQuery) => void;
}
// 已保存查询面板(localStorage)// Logan.Li
export function SavedQueriesPanel({ visible, onClose, onSelect }: Props) {
const [items, setItems] = useState<SavedQuery[]>([]);
useEffect(() => {
if (visible) setItems(savedQueryStorage.getAll());
}, [visible]);
if (!visible) return null;
return (
<>
<div className="fixed inset-0 z-40" style={{ background: 'rgba(0,0,0,0.3)' }} onClick={onClose} />
<div
className="fixed right-0 top-0 h-full z-50 border-l flex flex-col"
style={{ width: '380px', background: 'var(--dg-bg-secondary)', borderColor: 'var(--dg-border)' }}
>
<div className="flex items-center justify-between px-4 py-3 border-b" style={{ borderColor: 'var(--dg-border)' }}>
<div className="flex items-center gap-2">
<Bookmark className="h-4 w-4" style={{ color: 'var(--dg-blue)' }} />
<span className="text-sm font-medium" style={{ color: 'var(--dg-text-primary)' }}>已保存查询</span>
<span className="text-[10px] px-1.5 py-0.5 rounded" style={{ background: 'var(--dg-bg-tertiary)', color: 'var(--dg-text-muted)' }}>
{items.length}
</span>
</div>
<button onClick={onClose} className="h-7 w-7 flex items-center justify-center" style={{ color: 'var(--dg-text-muted)' }}>
<X className="h-4 w-4" />
</button>
</div>
<div className="flex-1 overflow-y-auto p-3 space-y-2">
{items.length === 0 ? (
<p className="text-xs text-center py-8" style={{ color: 'var(--dg-text-muted)' }}>暂无保存的查询</p>
) : items.map((item) => (
<div
key={item.id}
className="p-3 rounded-lg border group"
style={{ background: 'var(--dg-bg-tertiary)', borderColor: 'var(--dg-border)' }}
>
<div className="flex items-start justify-between gap-2">
<div className="min-w-0 flex-1">
<p className="text-sm font-medium truncate" style={{ color: 'var(--dg-text-primary)' }}>{item.name}</p>
<p className="text-[10px] mt-0.5" style={{ color: ENGINE_META[item.engine]?.color || 'var(--dg-text-muted)' }}>
{ENGINE_META[item.engine]?.name} · {item.timeRangeLabel}
</p>
<p className="text-[11px] font-mono mt-1 truncate" style={{ color: 'var(--dg-text-secondary)' }}>{item.query}</p>
<p className="text-[10px] mt-1 flex items-center gap-1" style={{ color: 'var(--dg-text-muted)' }}>
<Clock className="h-3 w-3" />
{new Date(item.createdAt).toLocaleString()}
</p>
</div>
<div className="flex flex-col gap-1 opacity-80 group-hover:opacity-100">
<button
onClick={() => { onSelect(item); onClose(); }}
className="h-7 w-7 flex items-center justify-center rounded border"
style={{ borderColor: 'var(--dg-border)', color: '#22c55e' }}
title="应用"
>
<Play className="h-3.5 w-3.5" />
</button>
<button
onClick={() => { savedQueryStorage.remove(item.id); setItems(savedQueryStorage.getAll()); }}
className="h-7 w-7 flex items-center justify-center rounded border"
style={{ borderColor: 'var(--dg-border)', color: 'var(--dg-text-muted)' }}
title="删除"
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
</div>
</div>
))}
</div>
</div>
</>
);
}