From 8fbbfc8093888299b78ad314a085ce23d3aedf0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E9=91=AB=E5=8D=87?= Date: Wed, 10 Jun 2026 09:37:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E5=B9=BF=E5=9C=BA=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E4=BD=93=E9=AA=8C=E4=BC=98=E5=8C=96=E4=B8=8E=E5=88=87?= =?UTF-8?q?=E6=8D=A2tab=E9=87=8D=E7=BD=AE=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 智能体广场:切换 我的/广场/私密广场 时清空重置搜索/标签/排序/发布者筛选 - 通用搜索栏(TagSearchBar):新增「搜索」按钮与回车立即触发(跳过防抖)、 搜索中转圈提示,新增 hideTags / searchPlaceholder 选项 - 定时任务广场:新增客户端搜索(名称/摘要/创建者,兼容脱敏文本) - agents 分页 hook 暴露 isFetching 以驱动搜索中状态 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../workspace/agents/agent-gallery.tsx | 19 ++++- .../workspace/tags/tag-search-bar.tsx | 75 +++++++++++++++---- frontend-web/src/core/agents/hooks.ts | 8 +- frontend-web/src/pages/ScheduledTasksPage.tsx | 50 +++++++++++-- 4 files changed, 127 insertions(+), 25 deletions(-) diff --git a/frontend-web/src/components/workspace/agents/agent-gallery.tsx b/frontend-web/src/components/workspace/agents/agent-gallery.tsx index 7e2b57c0..6d099a18 100644 --- a/frontend-web/src/components/workspace/agents/agent-gallery.tsx +++ b/frontend-web/src/components/workspace/agents/agent-gallery.tsx @@ -74,6 +74,7 @@ export function AgentGallery() { agents: mainAgents, total: mainTotal, isLoading: mainLoading, + isFetching: mainFetching, } = useAgentsPaginated({ scope: mainScope, search, @@ -89,6 +90,7 @@ export function AgentGallery() { agents: privateAgents, total: privateTotal, isLoading: isPrivateLoading, + isFetching: isPrivateFetching, error: privateError, } = usePrivateAgentsPaginated(privatePassword, { search, @@ -315,9 +317,16 @@ export function AgentGallery() { {/* Tabs */} - setActiveTab(v as "all" | "mine" | "private") - } + onValueChange={(v) => { + setActiveTab(v as "all" | "mine" | "private"); + // Each tab starts from a clean slate: switching tabs clears the + // keyword / tag / sort / publisher filters instead of carrying them + // over. The TagSearchBar's own input box is reset via its key below. + setSearch(""); + setTagIds([]); + setSort("time_desc"); + setPublisher(null); + }} className="flex flex-1 flex-col overflow-hidden" >
@@ -351,7 +360,11 @@ export function AgentGallery() {
void; - tagIds: string[]; - onTagIdsChange: (value: string[]) => void; + tagIds?: string[]; + onTagIdsChange?: (value: string[]) => void; className?: string; actions?: ReactNode; + /** Show an inline spinner while the caller's query is fetching. */ + searching?: boolean; + /** Hide the tag chip row for resources that have no tags. */ + hideTags?: boolean; + /** Override the search input placeholder. */ + searchPlaceholder?: string; }) { const { t } = useI18n(); const { data: tags } = useTags(targetType); @@ -47,11 +63,21 @@ export function TagSearchBar({ const [expanded, setExpanded] = useState(false); const cbRef = useRef(onSearchChange); cbRef.current = onSearchChange; + // Keep the pending debounce timer so an explicit search (Enter / 搜索 button) + // can cancel it and fire immediately instead of racing the 300ms delay. + const timerRef = useRef | null>(null); useEffect(() => { - const id = setTimeout(() => cbRef.current(input.trim()), 300); - return () => clearTimeout(id); + timerRef.current = setTimeout(() => cbRef.current(input.trim()), 300); + return () => { + if (timerRef.current) clearTimeout(timerRef.current); + }; }, [input]); + const flushSearch = () => { + if (timerRef.current) clearTimeout(timerRef.current); + cbRef.current(input.trim()); + }; + const allTags = tags ?? []; const canCollapse = allTags.length > COLLAPSE_THRESHOLD; // When collapsed, always keep the selected tags visible so the active @@ -62,7 +88,7 @@ export function TagSearchBar({ : allTags; const toggleTag = (id: string) => { - onTagIdsChange( + onTagIdsChange?.( tagIds.includes(id) ? tagIds.filter((x) => x !== id) : [...tagIds, id], ); }; @@ -75,8 +101,14 @@ export function TagSearchBar({ setInput(e.target.value)} - placeholder={t.adminTags.searchPlaceholder} - className="pl-8" + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + flushSearch(); + } + }} + placeholder={searchPlaceholder ?? t.adminTags.searchPlaceholder} + className={cn("pl-8", searching && "pr-9")} name="tag-search" // A `type="password"` field elsewhere on the page (e.g. the private // square access gate) makes browsers / password managers pick the @@ -88,7 +120,24 @@ export function TagSearchBar({ data-lpignore="true" data-form-type="other" /> + {searching && ( + + )} + {actions && (
{actions} @@ -96,11 +145,11 @@ export function TagSearchBar({ )}
- {allTags.length > 0 && ( + {!hideTags && allTags.length > 0 && (