feat(frontend): analysis/logs 筛选下拉改可搜索 Select

数据源换 ui/select;命名空间复用 SearchableValueSelect 工具条态(可搜、全部置顶、loading);与 ahead 功能 tip 分开。
This commit is contained in:
Codex committed 2026-10-28 22:02:45 +08:00
1 parent 11d0fff764
commit 016d3ecdfd
5 files changed
+197 -86

No files matched your search

+33 -17
View File
@@ -4,6 +4,8 @@ import { useTranslation } from 'react-i18next'
import '@/i18n/domains'
import { BarChart3, Boxes, FileWarning, Loader2, RefreshCw, ScrollText, TriangleAlert } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { SearchableValueSelect } from '@/components/SearchableValueSelect'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { PageContainer } from '@/components/PageContainer'
import { EmptyState } from '@/components/EmptyState'
@@ -35,6 +37,7 @@ const AnalysisPage = memo(function AnalysisPage() {
const [sources, setSources] = useState<LogSource[]>([])
const [sourceId, setSourceId] = useState(0)
const [namespaces, setNamespaces] = useState<string[]>([])
const [namespacesLoading, setNamespacesLoading] = useState(false)
const [namespace, setNamespace] = useState('')
const [start, setStart] = useState(() => toLocal(new Date(Date.now() - 3600000)))
const [end, setEnd] = useState(() => toLocal(new Date()))
@@ -53,11 +56,17 @@ const AnalysisPage = memo(function AnalysisPage() {
}, [])
// 命名空间候选随数据源变化(切换数据源时在 onChange 里同步清掉已选命名空间)
useEffect(() => {
if (!sourceId) return
if (!sourceId) {
setNamespaces([])
setNamespacesLoading(false)
return
}
let active = true
setNamespacesLoading(true)
logsApi.labelValues(sourceId, 'namespace')
.then(list => { if (active) setNamespaces(Array.isArray(list) ? list : []) })
.catch(() => { if (active) setNamespaces([]) })
.finally(() => { if (active) setNamespacesLoading(false) })
return () => { active = false }
}, [sourceId])
@@ -87,25 +96,32 @@ const AnalysisPage = memo(function AnalysisPage() {
>
{/* 筛选栏:数据源 / 命名空间 / 时间范围 */}
<div className="flex flex-wrap items-center gap-2">
<select
aria-label={t('filter.source')}
className="h-9 min-w-40 rounded-md border bg-background px-3 text-sm text-foreground"
value={sourceId}
onChange={event => { setSourceId(Number(event.target.value)); setNamespace('') }}
<Select
value={sourceId ? String(sourceId) : undefined}
onValueChange={value => { setSourceId(Number(value)); setNamespace('') }}
disabled={sources.length === 0}
>
{sources.length === 0 && <option value={0}>{t('filter.sourceEmpty')}</option>}
{sources.map(source => <option key={source.id} value={source.id}>{source.name}</option>)}
</select>
<select
aria-label={t('filter.namespace')}
className="h-9 min-w-36 rounded-md border bg-background px-3 text-sm text-foreground"
<SelectTrigger className="h-9 min-w-40" aria-label={t('filter.source')}>
<SelectValue placeholder={t('filter.sourceEmpty')} />
</SelectTrigger>
<SelectContent>
{sources.map(source => (
<SelectItem key={source.id} value={String(source.id)}>{source.name}</SelectItem>
))}
</SelectContent>
</Select>
<SearchableValueSelect
hideLabel
ariaLabel={t('filter.namespace')}
values={namespaces}
value={namespace}
onChange={event => setNamespace(event.target.value)}
onChange={setNamespace}
allowEmpty
emptyLabel={t('filter.allNamespaces')}
loading={namespacesLoading}
disabled={!sourceId}
>
<option value="">{t('filter.allNamespaces')}</option>
{namespaces.map(item => <option key={item} value={item}>{item}</option>)}
</select>
className="min-w-36"
/>
<TimeRangePicker start={start} end={end} onChange={(nextStart, nextEnd) => { setStart(nextStart); setEnd(nextEnd) }} />
</div>