refactor(log-center): 清理重复/死代码组件 — 移除 DatasourceCard/DatasourceDialog/DeleteConfirmDialog/EsQueryPanel/LokiQueryPanel/LogResultsTable/LogLibraryPage/StatusDot/useLogQuery/logLibraryTypes 10 个冗余组件 (1752 行删除)
This commit is contained in:
1 parent
0c1964aa85
commit
0c2c0adaaf
13 files changed
+105
-1752
No files matched your search
@@ -13,7 +13,7 @@ import { QueryHistoryPanel } from './QueryHistoryPanel';
|
||||
import { LogAlertFromQueryDialog } from './LogAlertFromQueryDialog';
|
||||
import { logCenterApi, type LogDataSource } from '@/api/monitor/log-center';
|
||||
import type { LogEngine, TimeRange, ResultTab, ViewMode, LogEntry, StatTable, LogCluster, QueryHistoryItem } from './types-v3';
|
||||
import { ENGINE_META, generateMockEntries, generateMockStatTable, generateMockClusters, extractFields, generateHistogramData, historyStorage } from './types-v3';
|
||||
import { ENGINE_META, buildStatTableFromEntries, buildClustersFromEntries, extractFields, generateHistogramData, historyStorage } from './types-v3';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
// 默认最近5分钟 // Logan.Li
|
||||
@@ -24,12 +24,6 @@ const DEFAULT_TIME_RANGE: TimeRange = {
|
||||
type: 'relative',
|
||||
};
|
||||
|
||||
// Demo 数据源(后端无数据源时的兜底)// Logan.Li
|
||||
const DEMO_SOURCES: Record<'loki' | 'elasticsearch', LogDataSource[]> = {
|
||||
loki: [{ id: -1, name: 'demo-loki', type: 'loki', url: '' }],
|
||||
elasticsearch: [{ id: -2, name: 'demo-es', type: 'elasticsearch', url: '' }],
|
||||
};
|
||||
|
||||
// 引擎默认查询语句 // Logan.Li
|
||||
const DEFAULT_QUERIES: Record<'loki' | 'elasticsearch', string> = {
|
||||
loki: '{app="cex-admin-api"} |= ""',
|
||||
@@ -81,22 +75,34 @@ const ALL_TEMPLATES: Record<'loki' | 'elasticsearch', { label: string; query: st
|
||||
// LogQL / Lucene 语法高亮 → HTML // Logan.Li
|
||||
function highlightSyntax(query: string, engine: 'loki' | 'elasticsearch'): string {
|
||||
if (!query) return '<span style="color:transparent">.</span>';
|
||||
// 先转义 HTML 特殊字符,避免查询语句中的 < > & 破坏标签结构 // Logan.Li
|
||||
const esc = query.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
const span = (color: string, text: string) => `<span style="color:${color}">${text}</span>`;
|
||||
if (engine === 'loki') {
|
||||
return query
|
||||
.replace(/([a-zA-Z_][\w-]*)(?==)/g, '<s c="#f59e0b">$1</s>')
|
||||
.replace(/("(?:[^"\\]|\\.)*")/g, '<s c="#22c55e">$1</s>')
|
||||
.replace(/(\|=|\!=|=~|!~|=)/g, '<s c="#f43f5e">$1</s>')
|
||||
.replace(/(\|json|\|regexp|\|line_format|\|unwrap|\|pattern|\|label_format)/g, '<s c="#8b5cf6">$1</s>')
|
||||
.replace(/\b(count_over_time|sum|rate|avg|max|min|count|by|over)\b/g, '<s c="#06b6d4">$1</s>')
|
||||
.replace(/(\{|\})/g, '<s c="#f59e0b">$1</s>')
|
||||
.replace(/<s c=/g, '<span style="color=') + '<span style="color:transparent">.</span>';
|
||||
// 单正则 + 回调:一次匹配,避免链式 replace 对已生成颜色值的二次匹配 // Logan.Li
|
||||
return esc.replace(
|
||||
/("(?:[^"\\]|\\.)*")|([a-zA-Z_][\w-]*)(?==)|(\|json|\|regexp|\|line_format|\|unwrap|\|pattern|\|label_format)|\b(count_over_time|sum|rate|avg|max|min|count|by|over)\b|([{}])|(\|=|\!=|=~|!~|=)/g,
|
||||
(m, str, ident, pipe, fn, brace, op) => {
|
||||
if (str) return span('#22c55e', str);
|
||||
if (ident) return span('#f59e0b', ident);
|
||||
if (pipe) return span('#8b5cf6', pipe);
|
||||
if (fn) return span('#06b6d4', fn);
|
||||
if (brace) return span('#f59e0b', brace);
|
||||
if (op) return span('#f43f5e', op);
|
||||
return m;
|
||||
},
|
||||
) + '<span style="color:transparent">.</span>';
|
||||
}
|
||||
return query
|
||||
.replace(/\b(AND|OR|NOT)\b/g, '<s c="#f43f5e">$1</s>')
|
||||
.replace(/([a-zA-Z_@][\w.-]*):/g, '<s c="#f59e0b">$1</s>:')
|
||||
.replace(/("(?:[^"\\]|\\.)*")/g, '<s c="#22c55e">$1</s>')
|
||||
.replace(/(\*|\?|\~|\^|\[|\]|\(|\))/g, '<s c="#8b5cf6">$1</s>')
|
||||
.replace(/<s c=/g, '<span style="color=') + '<span style="color:transparent">.</span>';
|
||||
return esc.replace(
|
||||
/("(?:[^"\\]|\\.)*")|\b(AND|OR|NOT)\b|([a-zA-Z_@][\w.-]*)(?=:)|([*?~^[\]()])/g,
|
||||
(m, str, kw, field, punct) => {
|
||||
if (str) return span('#22c55e', str);
|
||||
if (kw) return span('#f43f5e', kw);
|
||||
if (field) return span('#f59e0b', field);
|
||||
if (punct) return span('#8b5cf6', punct);
|
||||
return m;
|
||||
},
|
||||
) + '<span style="color:transparent">.</span>';
|
||||
}
|
||||
|
||||
// 语法高亮输入框:底层高亮 + 上层透明输入 // Logan.Li
|
||||
@@ -132,15 +138,13 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
const meta = ENGINE_META[effEngine];
|
||||
const badge = ENGINE_BADGE[effEngine];
|
||||
|
||||
// 从后端加载真实数据源,同时保留 demo 兜底 // Logan.Li
|
||||
const [sources, setSources] = useState<LogDataSource[]>(DEMO_SOURCES[effEngine]);
|
||||
// 从后端加载真实数据源(无 demo 兜底,无源时引导用户前往日志库配置)// Logan.Li
|
||||
const [sources, setSources] = useState<LogDataSource[]>([]);
|
||||
useEffect(() => {
|
||||
logCenterApi.listDatasources(effEngine).then((res: any) => {
|
||||
const list: LogDataSource[] = Array.isArray(res) ? res : (res?.list || []);
|
||||
// 合并真实数据源 + demo 兜底(真实源在前) // Logan.Li
|
||||
if (list.length > 0) setSources([...list, ...DEMO_SOURCES[effEngine]]);
|
||||
else setSources(DEMO_SOURCES[effEngine]);
|
||||
}).catch(() => setSources(DEMO_SOURCES[effEngine]));
|
||||
setSources(list);
|
||||
}).catch(() => setSources([]));
|
||||
}, [effEngine]);
|
||||
|
||||
const [query, setQuery] = useState(DEFAULT_QUERIES[effEngine]);
|
||||
@@ -168,13 +172,15 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
const [saveMenuOpen, setSaveMenuOpen] = useState(false);
|
||||
const [hasQueried, setHasQueried] = useState(false);
|
||||
|
||||
// 数据源切换后更新 dsId // Logan.Li
|
||||
// 数据源加载后选中默认或首个(无源时保持空)// Logan.Li
|
||||
useEffect(() => {
|
||||
if (sources.length > 0) setDsId(String(sources[0].id));
|
||||
if (sources.length > 0) {
|
||||
const def = sources.find(s => s.is_default) || sources[0];
|
||||
setDsId(String(def.id));
|
||||
}
|
||||
}, [sources]);
|
||||
|
||||
const currentSource = sources.find(s => String(s.id) === dsId);
|
||||
const isDemo = currentSource && (currentSource.id < 0 || !currentSource.url);
|
||||
|
||||
const fields = useMemo(() => extractFields(entries), [entries]);
|
||||
const commonLabels = useMemo(() => extractCommonLabels(entries), [entries]);
|
||||
@@ -182,26 +188,13 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
const totalPages = Math.max(1, Math.ceil(sortedEntries.length / perPage));
|
||||
const pagedEntries = useMemo(() => { const s = (page - 1) * perPage; return sortedEntries.slice(s, s + perPage); }, [sortedEntries, page, perPage]);
|
||||
|
||||
// 执行查询 — 接入真实 API(demo 源则使用 mock)// Logan.Li
|
||||
// 执行查询 — 接入真实 API(无 mock)// Logan.Li
|
||||
const handleRun = useCallback(async () => {
|
||||
if (!currentSource) return;
|
||||
const dsIdNum = currentSource.id;
|
||||
setLoading(true);
|
||||
const t0 = performance.now();
|
||||
|
||||
// Demo 源:模拟数据
|
||||
if (isDemo) {
|
||||
setTimeout(() => {
|
||||
const mock = generateMockEntries(effEngine as LogEngine, 120);
|
||||
setEntries(mock); setHistogramData(generateHistogramData(mock)); setTotalCount(mock.length * 14);
|
||||
setQueryTime(Math.round(performance.now() - t0)); setStatTable(generateMockStatTable(effEngine as LogEngine));
|
||||
setClusters(generateMockClusters()); setPage(1); setLoading(false); setHasQueried(true);
|
||||
historyStorage.add({ engine: effEngine as LogEngine, datasourceName: currentSource.name, query, timeRange: timeRange.label, resultCount: mock.length });
|
||||
}, 300);
|
||||
return;
|
||||
}
|
||||
|
||||
// 真实 API 查询
|
||||
try {
|
||||
const startISO = (timeRange.start + (timeRange.start.includes('T') ? '' : '.000Z')).replace(' ', 'T');
|
||||
const endISO = (timeRange.end + (timeRange.end.includes('T') ? '' : '.000Z')).replace(' ', 'T');
|
||||
@@ -241,8 +234,9 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
setHistogramData(generateHistogramData(mapped));
|
||||
setTotalCount(data?.total || mapped.length);
|
||||
setQueryTime(Math.round(performance.now() - t0));
|
||||
setStatTable(generateMockStatTable(effEngine as LogEngine));
|
||||
setClusters(generateMockClusters());
|
||||
// 统计图表 / 聚类基于真实返回条目聚合(无 mock)// Logan.Li
|
||||
setStatTable(buildStatTableFromEntries(mapped));
|
||||
setClusters(buildClustersFromEntries(mapped));
|
||||
setPage(1);
|
||||
setHasQueried(true);
|
||||
historyStorage.add({ engine: effEngine as LogEngine, datasourceName: currentSource.name, query, timeRange: timeRange.label, resultCount: mapped.length });
|
||||
@@ -252,7 +246,7 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [effEngine, query, currentSource, timeRange, isDemo, toast]);
|
||||
}, [effEngine, query, currentSource, timeRange, toast]);
|
||||
|
||||
const toggleField = useCallback((n: string) => setSelectedFields(p => p.includes(n) ? p.filter(f => f !== n) : [...p, n]), []);
|
||||
const handleSaveMenu = (a: 'query' | 'alert') => { setSaveMenuOpen(false); a === 'query' ? setSaveQueryOpen(true) : setSaveAlertOpen(true); };
|
||||
@@ -280,10 +274,11 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
<TimeRangePicker value={timeRange} onChange={setTimeRange} />
|
||||
|
||||
<select value={dsId} onChange={e => setDsId(e.target.value)} className="h-10 px-2 text-xs rounded flex-shrink-0" style={{ background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)' }}>
|
||||
{sources.map(s => <option key={s.id} value={s.id}>{s.name}{(s.id < 0 || !s.url) ? ' (模拟)' : ''}</option>)}
|
||||
{sources.length === 0 && <option value="">无可用{meta.name}数据源</option>}
|
||||
{sources.map(s => <option key={s.id} value={s.id}>{s.name}{s.is_default ? ' (默认)' : ''}</option>)}
|
||||
</select>
|
||||
|
||||
<button onClick={handleRun} disabled={loading} className="h-10 px-4 rounded text-xs font-medium text-white flex items-center gap-1.5 flex-shrink-0 transition-opacity hover:opacity-90" style={{ background: '#22c55e' }}>
|
||||
<button onClick={handleRun} disabled={loading || !currentSource} className="h-10 px-4 rounded text-xs font-medium text-white flex items-center gap-1.5 flex-shrink-0 transition-opacity hover:opacity-90" style={{ background: '#22c55e' }}>
|
||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />} 查询
|
||||
</button>
|
||||
|
||||
@@ -305,6 +300,13 @@ export function UnifiedQueryPanel({ engine, onRuleCreated }: Props) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 无数据源时引导用户前往日志库配置 // Logan.Li */}
|
||||
{sources.length === 0 && (
|
||||
<div className="p-3 rounded border text-xs" style={{ background: 'rgba(245,158,11,0.1)', borderColor: 'rgba(245,158,11,0.3)', color: '#f59e0b' }}>
|
||||
暂无{meta.name}数据源,请先前往「日志库」添加数据源。
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 快捷查询助手 */}
|
||||
<QuickQueryBar engine={effEngine} templates={ALL_TEMPLATES[effEngine]} onSelect={setQuery} />
|
||||
|
||||
|
||||
Reference in new issue
Block a user