diff --git a/frontend-web/src/pages/SkillAddressesPage.tsx b/frontend-web/src/pages/SkillAddressesPage.tsx index 4fefa80e..3fa96662 100644 --- a/frontend-web/src/pages/SkillAddressesPage.tsx +++ b/frontend-web/src/pages/SkillAddressesPage.tsx @@ -17,6 +17,7 @@ import { Search, Undo2, Wand2, + X, } from "lucide-react"; import { toast } from "sonner"; @@ -231,11 +232,19 @@ export function SkillAddressesPanel() { } }; - const q = query.trim().toLowerCase(); - const visible = useMemo( - () => (!q ? groups : groups.filter((g) => g.address.toLowerCase().includes(q) || g.occurrences.some((o) => o.skill.toLowerCase().includes(q)))), - [groups, q], + // 多关键词检索:空格分隔,每个关键词都要命中(地址或技能名任一包含)才显示。 + // 这样「uat 4.cn」能同时收窄到既含 uat 又含 4.cn 的地址,应对地址过多的场景。 + const tokens = useMemo( + () => query.trim().toLowerCase().split(/\s+/).filter(Boolean), + [query], ); + const visible = useMemo(() => { + if (tokens.length === 0) return groups; + return groups.filter((g) => { + const haystack = `${g.address} ${g.occurrences.map((o) => o.skill).join(" ")}`.toLowerCase(); + return tokens.every((t) => haystack.includes(t)); + }); + }, [groups, tokens]); const totalOccurrences = useMemo(() => groups.reduce((s, g) => s + g.count, 0), [groups]); const canRollback = !!(lastBackupId || backups.length > 0); @@ -332,15 +341,25 @@ export function SkillAddressesPanel() { ))} -
+
setQuery(e.target.value)} - className="w-full rounded-xl border border-slate-200 bg-slate-50/50 py-2 pl-10 pr-4 text-sm transition-all hover:border-slate-300 focus:border-blue-500 focus:outline-none focus:ring-4 focus:ring-blue-500/5" + className="w-full rounded-xl border border-slate-200 bg-slate-50/50 py-2 pl-10 pr-9 text-sm transition-all hover:border-slate-300 focus:border-blue-500 focus:outline-none focus:ring-4 focus:ring-blue-500/5" /> + {query && ( + + )}
@@ -422,7 +441,7 @@ export function SkillAddressesPanel() { {/* 地址列表 */}
- 地址({visible.length}) + 地址({visible.length}{tokens.length > 0 ? ` / ${groups.length}` : ""}) {pendingCount > 0 && (