feat(agent-sidebar): 独立问答智能体列表栏顶部增加智能体名称模糊搜索

下拉来源切换下面加搜索框:防抖 300ms 走服务端 search(按原始名 + 内置缩写
脱敏映射匹配,与智能体管理页一致),在当前来源(我的/某广场)内搜全量;
带清除按钮、无结果显示「未找到匹配的智能体」、搜索中复用 isFetching 半透明指示。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.8 committed 2026-06-19 11:16:39 +08:00
1 parent 0bd4524334
commit 13ef1942f2
1 file changed
+36 -4
@@ -1,10 +1,11 @@
"use client";
import { Bot, Loader2 } from "lucide-react";
import { Bot, Loader2, Search, X } from "lucide-react";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import { agentColor } from "@/components/workspace/agent-icon-color";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
@@ -52,6 +53,15 @@ export function AgentChatSidebar({ activeAgentId }: { activeAgentId?: string })
// 作用域:MINE_SCOPE 或某个广场 id。默认「我的」。
const [scope, setScope] = useState<string>(MINE_SCOPE);
// 模糊搜索智能体名称:防抖 300ms 后走服务端 search(后端按原始名 / 脱敏名匹配),
// 在当前来源(我的 / 某广场)内过滤,能搜全量而非仅当前 50 条。
const [searchInput, setSearchInput] = useState("");
const [search, setSearch] = useState("");
useEffect(() => {
const timer = setTimeout(() => setSearch(searchInput.trim()), 300);
return () => clearTimeout(timer);
}, [searchInput]);
// 若所选广场被管理员删除,回退到「我的」,避免指向不存在的广场。
useEffect(() => {
if (
@@ -70,13 +80,14 @@ export function AgentChatSidebar({ activeAgentId }: { activeAgentId?: string })
page: 1,
pageSize: LIST_PAGE_SIZE,
square: isMine ? undefined : scope,
search: search || undefined,
});
return (
// 配色与 AgentModuleSidebar 一致:浅色白底,深蓝主题半透明深蓝玻璃面板。
<aside className="flex h-full w-(--sidebar-width) shrink-0 flex-col overflow-hidden border-r border-border/70 bg-white text-foreground backdrop-blur-md dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80">
{/* 顶部:作用域下拉(我的 / 各广场) */}
<div className="shrink-0 border-b border-border/60 px-3 py-2.5">
{/* 顶部:作用域下拉(我的 / 各广场)+ 智能体名称模糊搜索 */}
<div className="shrink-0 space-y-2 border-b border-border/60 px-3 py-2.5">
<Select value={scope} onValueChange={setScope}>
<SelectTrigger className="h-9 w-full text-sm" title="切换智能体来源">
<SelectValue />
@@ -90,6 +101,27 @@ export function AgentChatSidebar({ activeAgentId }: { activeAgentId?: string })
))}
</SelectContent>
</Select>
<div className="relative">
<Search className="text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2" />
<Input
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
placeholder="搜索智能体名称"
className="h-9 pl-8 pr-8 text-sm"
aria-label="搜索智能体名称"
/>
{searchInput && (
<button
type="button"
onClick={() => setSearchInput("")}
className="text-muted-foreground hover:text-foreground absolute top-1/2 right-2 -translate-y-1/2"
title="清除"
aria-label="清除搜索"
>
<X className="size-3.5" />
</button>
)}
</div>
</div>
{/* 智能体列表 */}
@@ -101,7 +133,7 @@ export function AgentChatSidebar({ activeAgentId }: { activeAgentId?: string })
</div>
) : agents.length === 0 ? (
<p className="px-3 py-1.5 text-xs text-muted-foreground/70">
{isMine ? "暂无智能体" : "该广场暂无智能体"}
{search ? "未找到匹配的智能体" : isMine ? "暂无智能体" : "该广场暂无智能体"}
</p>
) : (
<div