Enhance AssistantPage with search functionality and UI improvements
Added a search feature to filter assistants by name, type, or ID, improving user experience in managing assistants. Updated the layout and styling for better visual appeal, including new components for badges and dropdown menus. Removed deprecated status fields from mock data to streamline the assistant list display.
This commit is contained in:
@@ -3,23 +3,27 @@
|
||||
import {
|
||||
Bot,
|
||||
Brain,
|
||||
Database,
|
||||
Mic,
|
||||
MoreHorizontal,
|
||||
Pencil,
|
||||
Plus,
|
||||
Rocket,
|
||||
Save,
|
||||
Search,
|
||||
Sparkles,
|
||||
Trash2,
|
||||
Volume2,
|
||||
} from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -53,7 +57,6 @@ type AssistantListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: AssistantType;
|
||||
status: "运行中" | "草稿" | "已停用";
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
@@ -62,28 +65,24 @@ const mockAssistants: AssistantListItem[] = [
|
||||
id: "asst_001",
|
||||
name: "政务视频咨询助手",
|
||||
type: "提示词",
|
||||
status: "运行中",
|
||||
updatedAt: "2026-06-05 18:20",
|
||||
},
|
||||
{
|
||||
id: "asst_002",
|
||||
name: "热线工单辅助助手",
|
||||
type: "工作流",
|
||||
status: "草稿",
|
||||
updatedAt: "2026-06-04 15:12",
|
||||
},
|
||||
{
|
||||
id: "asst_003",
|
||||
name: "Dify 知识库问答助手",
|
||||
type: "Dify",
|
||||
status: "运行中",
|
||||
updatedAt: "2026-06-02 09:48",
|
||||
},
|
||||
{
|
||||
id: "asst_004",
|
||||
name: "FastGPT 售后咨询助手",
|
||||
type: "FastGPT",
|
||||
status: "已停用",
|
||||
updatedAt: "2026-05-29 11:06",
|
||||
},
|
||||
];
|
||||
@@ -102,7 +101,20 @@ export function AssistantPage() {
|
||||
enableInterrupt: true,
|
||||
});
|
||||
const [view, setView] = useState<"list" | "create">("list");
|
||||
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const filteredAssistants = mockAssistants.filter((assistant) => {
|
||||
const keyword = searchQuery.trim().toLowerCase();
|
||||
|
||||
if (!keyword) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return [assistant.name, assistant.id, assistant.type, assistant.updatedAt]
|
||||
.join(" ")
|
||||
.toLowerCase()
|
||||
.includes(keyword);
|
||||
});
|
||||
|
||||
function updateForm<K extends keyof AssistantForm>(
|
||||
key: K,
|
||||
@@ -115,117 +127,145 @@ export function AssistantPage() {
|
||||
}
|
||||
if (view === "list") {
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-[1180px] flex-col gap-6">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="mx-auto flex w-full max-w-[1180px] flex-col gap-8">
|
||||
<div className="flex flex-col items-start justify-between gap-5 sm:flex-row sm:gap-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-3 w-3 rounded-full bg-blue-400 shadow-[0_0_0_4px_rgba(46,161,255,.16),0_0_14px_rgba(46,161,255,.35)]" />
|
||||
<h1 className="text-3xl font-bold">创建助手</h1>
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-muted-soft">
|
||||
<div className="caption-label text-muted-soft">助手管理</div>
|
||||
<h1 className="font-display display-lg mt-3 text-ink">助手列表</h1>
|
||||
<p className="mt-3 max-w-2xl text-[15px] leading-7 text-muted-foreground">
|
||||
管理已有的视频助手,支持提示词、工作流、Dify 和 FastGPT 类型。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
<Button
|
||||
size="lg"
|
||||
className="w-full shrink-0 gap-2 sm:w-auto"
|
||||
onClick={() => setView("create")}
|
||||
className="flex h-10 items-center gap-2 rounded-xl bg-blue-500 px-4 text-sm font-semibold text-white shadow-[0_8px_24px_rgba(29,123,255,.35)]"
|
||||
>
|
||||
<Plus size={16} />
|
||||
创建助手
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<section className="rounded-2xl border border-sidebar-border bg-sidebar p-5">
|
||||
<div className="mb-5 flex items-center justify-between gap-4">
|
||||
|
||||
<section className="rounded-2xl border border-hairline bg-card p-6 shadow-sm">
|
||||
<div className="mb-6 flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
<div>
|
||||
<div className="text-lg font-semibold">助手列表</div>
|
||||
<div className="mt-1 text-sm text-muted-soft">
|
||||
<div className="text-[18px] font-medium text-foreground">
|
||||
助手列表
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-muted-foreground">
|
||||
共 {mockAssistants.length} 个助手
|
||||
{searchQuery.trim() && `,已筛选 ${filteredAssistants.length} 个`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-10 w-[280px] items-center gap-3 rounded-xl border border-hairline-strong bg-surface-strong/60 px-3 text-sm text-muted-soft">
|
||||
<Search size={16} />
|
||||
<span>搜索助手名称...</span>
|
||||
|
||||
<div className="relative w-full md:w-[320px]">
|
||||
<Search
|
||||
size={15}
|
||||
className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-soft"
|
||||
/>
|
||||
<Input
|
||||
value={searchQuery}
|
||||
onChange={(event) => setSearchQuery(event.target.value)}
|
||||
className="h-10 border-hairline-strong bg-background pl-9 text-sm text-foreground placeholder:text-muted-soft"
|
||||
placeholder="搜索助手名称、类型或 ID..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-2xl border border-hairline-strong">
|
||||
<div className="grid grid-cols-[1fr_120px_120px_160px_120px] bg-surface-strong/70 px-5 py-3 text-xs font-medium text-muted-soft">
|
||||
<div>助手名称</div>
|
||||
<div>助手类型</div>
|
||||
<div>状态</div>
|
||||
<div>更新时间</div>
|
||||
<div className="text-right">操作</div>
|
||||
|
||||
<div className="overflow-hidden rounded-xl border border-hairline">
|
||||
<div className="hidden items-center gap-4 bg-surface-strong/60 px-5 py-3 md:flex">
|
||||
<div className="caption-label flex-1 text-muted-soft">
|
||||
助手名称
|
||||
</div>
|
||||
<div className="caption-label w-[110px] text-muted-soft">
|
||||
助手类型
|
||||
</div>
|
||||
<div className="caption-label w-[150px] text-muted-soft">
|
||||
更新时间
|
||||
</div>
|
||||
<div className="caption-label w-[116px] text-right text-muted-soft">
|
||||
操作
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="divide-y divide-hairline-strong">
|
||||
{mockAssistants.map((assistant) => (
|
||||
|
||||
<div className="divide-y divide-hairline">
|
||||
{filteredAssistants.map((assistant) => (
|
||||
<div
|
||||
key={assistant.id}
|
||||
className="grid grid-cols-[1fr_120px_120px_160px_120px] items-center px-5 py-4 text-sm transition-colors hover:bg-sidebar-accent/30"
|
||||
className="flex flex-col gap-3 px-5 py-4 text-sm transition-colors hover:bg-surface-strong/40 md:flex-row md:items-center md:gap-4"
|
||||
>
|
||||
<div>
|
||||
<div className="font-medium text-foreground">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate font-medium text-foreground">
|
||||
{assistant.name}
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-muted-soft">
|
||||
{assistant.id}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span className="rounded-full border border-hairline-strong bg-surface-strong px-2.5 py-1 text-xs text-muted-foreground">
|
||||
{assistant.type}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span
|
||||
className={[
|
||||
"rounded-full px-2.5 py-1 text-xs",
|
||||
assistant.status === "运行中"
|
||||
? "bg-green-500/10 text-green-400"
|
||||
: assistant.status === "草稿"
|
||||
? "bg-yellow-500/10 text-yellow-400"
|
||||
: "bg-muted/30 text-muted-foreground",
|
||||
].join(" ")}
|
||||
|
||||
<div className="md:w-[110px]">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="h-6 bg-surface-strong px-3 text-muted-foreground"
|
||||
>
|
||||
{assistant.status}
|
||||
</span>
|
||||
{assistant.type}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="text-muted-soft">{assistant.updatedAt}</div>
|
||||
|
||||
<div className="relative flex justify-end gap-2">
|
||||
<button className="flex h-8 items-center gap-1 rounded-lg border border-hairline-strong px-3 text-xs text-muted-foreground transition-colors hover:bg-sidebar-accent hover:text-foreground">
|
||||
|
||||
<div className="text-muted-foreground md:w-[150px]">
|
||||
{assistant.updatedAt}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 md:w-[116px]">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="gap-1.5 border-hairline-strong text-xs text-muted-foreground hover:text-foreground"
|
||||
onClick={() => setView("create")}
|
||||
>
|
||||
<Pencil size={14} />
|
||||
编辑
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() =>
|
||||
setOpenMenuId(
|
||||
openMenuId === assistant.id ? null : assistant.id,
|
||||
)
|
||||
}
|
||||
className="flex h-8 w-8 items-center justify-center rounded-lg border border-hairline-strong text-muted-foreground transition-colors hover:bg-sidebar-accent hover:text-foreground"
|
||||
>
|
||||
<MoreHorizontal size={15} />
|
||||
</button>
|
||||
|
||||
{openMenuId === assistant.id && (
|
||||
<div className="absolute right-0 top-9 z-10 w-28 rounded-xl border border-hairline-strong bg-sidebar p-1 shadow-xl">
|
||||
<button className="flex h-9 w-full items-center gap-2 rounded-lg px-3 text-sm text-red-400 transition-colors hover:bg-red-500/10">
|
||||
</Button>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
className="border-hairline-strong text-muted-foreground hover:text-foreground"
|
||||
aria-label={`${assistant.name} 更多操作`}
|
||||
>
|
||||
<MoreHorizontal size={15} />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-32 min-w-32 rounded-xl border border-hairline bg-popover p-1"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
variant="destructive"
|
||||
className="rounded-lg"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{filteredAssistants.length === 0 && (
|
||||
<div className="px-5 py-12 text-center">
|
||||
<div className="font-medium text-foreground">
|
||||
未找到匹配的助手
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-muted-foreground">
|
||||
请调整关键词后再试。
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -254,14 +294,6 @@ export function AssistantPage() {
|
||||
>
|
||||
返回列表
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="gap-2 border-hairline-strong text-foreground hover:bg-surface-strong"
|
||||
>
|
||||
<Save size={16} />
|
||||
保存草稿
|
||||
</Button>
|
||||
<Button size="lg" className="gap-2">
|
||||
<Rocket size={16} />
|
||||
创建助手
|
||||
@@ -495,4 +527,4 @@ function ToggleRow({
|
||||
<Switch checked={checked} onCheckedChange={onChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user