69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
// unified-query/QueryControlRow.tsx — 查询控制行(引擎徽标 + 输入框 + 查询按钮 + Live Tail)
|
||
import { Play, Loader2, Radio } from 'lucide-react';
|
||
import { QueryAutocompleteInput } from '../QueryAutocompleteInput';
|
||
import { useTranslation } from 'react-i18next';
|
||
|
||
interface QueryControlRowProps {
|
||
badge: { label: string; dotColor: string };
|
||
query: string;
|
||
onQueryChange: (q: string) => void;
|
||
effEngine: 'loki' | 'elasticsearch';
|
||
metaQueryPlaceholder: string;
|
||
labelNames: string[];
|
||
labelValues: string[];
|
||
mappingFields: string[];
|
||
onRequestLabelValues: (label: string) => void;
|
||
loading: boolean;
|
||
liveTail: boolean;
|
||
hasSource: boolean;
|
||
onRun: () => void;
|
||
onToggleLiveTail: () => void;
|
||
}
|
||
|
||
export function QueryControlRow({
|
||
badge, query, onQueryChange, effEngine, metaQueryPlaceholder,
|
||
labelNames, labelValues, mappingFields, onRequestLabelValues,
|
||
loading, liveTail, hasSource, onRun, onToggleLiveTail,
|
||
}: QueryControlRowProps) {
|
||
const { t } = useTranslation();
|
||
const canQuery = hasSource && query.trim().length > 0;
|
||
return (
|
||
<div className="flex items-center gap-2">
|
||
<div className="flex-1 flex items-center gap-2 min-w-0">
|
||
<div className="flex items-center gap-1.5 px-2.5 h-10 rounded flex-shrink-0" style={{ background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)' }}>
|
||
<span className="w-2 h-2 rounded-full" style={{ background: badge.dotColor }} />
|
||
<span className="text-[11px] font-semibold" style={{ color: badge.dotColor }}>{badge.label}</span>
|
||
</div>
|
||
<QueryAutocompleteInput
|
||
value={query}
|
||
onChange={onQueryChange}
|
||
engine={effEngine}
|
||
placeholder={metaQueryPlaceholder}
|
||
labelNames={labelNames}
|
||
labelValues={labelValues}
|
||
mappingFields={mappingFields}
|
||
onRequestLabelValues={onRequestLabelValues}
|
||
/>
|
||
<button onClick={onRun} disabled={loading || liveTail || !canQuery}
|
||
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 disabled:opacity-40 disabled:cursor-not-allowed"
|
||
style={{ background: '#22c55e' }}>
|
||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />} {t('monitor.logPanel.query')}
|
||
</button>
|
||
<button
|
||
onClick={onToggleLiveTail}
|
||
disabled={!canQuery}
|
||
className="h-10 px-3 rounded text-xs flex items-center gap-1 flex-shrink-0"
|
||
style={{
|
||
border: `1px solid ${liveTail ? '#ef4444' : 'var(--dg-border)'}`,
|
||
color: liveTail ? '#ef4444' : 'var(--dg-text-secondary)',
|
||
background: liveTail ? 'rgba(239,68,68,0.1)' : 'var(--dg-bg-tertiary)',
|
||
}}
|
||
title={t('monitor.logPanel.liveTailTitle')}
|
||
>
|
||
<Radio className={`h-3.5 w-3.5 ${liveTail ? 'animate-pulse' : ''}`} /> Tail
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|