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 {
|
import {
|
||||||
Bot,
|
Bot,
|
||||||
Brain,
|
Brain,
|
||||||
Database,
|
|
||||||
Mic,
|
Mic,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
Pencil,
|
Pencil,
|
||||||
Plus,
|
Plus,
|
||||||
Rocket,
|
Rocket,
|
||||||
Save,
|
|
||||||
Search,
|
Search,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Trash2,
|
Trash2,
|
||||||
Volume2,
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -53,7 +57,6 @@ type AssistantListItem = {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: AssistantType;
|
type: AssistantType;
|
||||||
status: "运行中" | "草稿" | "已停用";
|
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,28 +65,24 @@ const mockAssistants: AssistantListItem[] = [
|
|||||||
id: "asst_001",
|
id: "asst_001",
|
||||||
name: "政务视频咨询助手",
|
name: "政务视频咨询助手",
|
||||||
type: "提示词",
|
type: "提示词",
|
||||||
status: "运行中",
|
|
||||||
updatedAt: "2026-06-05 18:20",
|
updatedAt: "2026-06-05 18:20",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "asst_002",
|
id: "asst_002",
|
||||||
name: "热线工单辅助助手",
|
name: "热线工单辅助助手",
|
||||||
type: "工作流",
|
type: "工作流",
|
||||||
status: "草稿",
|
|
||||||
updatedAt: "2026-06-04 15:12",
|
updatedAt: "2026-06-04 15:12",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "asst_003",
|
id: "asst_003",
|
||||||
name: "Dify 知识库问答助手",
|
name: "Dify 知识库问答助手",
|
||||||
type: "Dify",
|
type: "Dify",
|
||||||
status: "运行中",
|
|
||||||
updatedAt: "2026-06-02 09:48",
|
updatedAt: "2026-06-02 09:48",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "asst_004",
|
id: "asst_004",
|
||||||
name: "FastGPT 售后咨询助手",
|
name: "FastGPT 售后咨询助手",
|
||||||
type: "FastGPT",
|
type: "FastGPT",
|
||||||
status: "已停用",
|
|
||||||
updatedAt: "2026-05-29 11:06",
|
updatedAt: "2026-05-29 11:06",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -102,7 +101,20 @@ export function AssistantPage() {
|
|||||||
enableInterrupt: true,
|
enableInterrupt: true,
|
||||||
});
|
});
|
||||||
const [view, setView] = useState<"list" | "create">("list");
|
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>(
|
function updateForm<K extends keyof AssistantForm>(
|
||||||
key: K,
|
key: K,
|
||||||
@@ -115,117 +127,145 @@ export function AssistantPage() {
|
|||||||
}
|
}
|
||||||
if (view === "list") {
|
if (view === "list") {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex w-full max-w-[1180px] flex-col gap-6">
|
<div className="mx-auto flex w-full max-w-[1180px] flex-col gap-8">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex flex-col items-start justify-between gap-5 sm:flex-row sm:gap-6">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="caption-label text-muted-soft">助手管理</div>
|
||||||
<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="font-display display-lg mt-3 text-ink">助手列表</h1>
|
||||||
<h1 className="text-3xl font-bold">创建助手</h1>
|
<p className="mt-3 max-w-2xl text-[15px] leading-7 text-muted-foreground">
|
||||||
</div>
|
|
||||||
<p className="mt-2 text-sm text-muted-soft">
|
|
||||||
管理已有的视频助手,支持提示词、工作流、Dify 和 FastGPT 类型。
|
管理已有的视频助手,支持提示词、工作流、Dify 和 FastGPT 类型。
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<Button
|
||||||
|
size="lg"
|
||||||
|
className="w-full shrink-0 gap-2 sm:w-auto"
|
||||||
onClick={() => setView("create")}
|
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} />
|
<Plus size={16} />
|
||||||
创建助手
|
创建助手
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section className="rounded-2xl border border-sidebar-border bg-sidebar p-5">
|
<section className="rounded-2xl border border-hairline bg-card p-6 shadow-sm">
|
||||||
<div className="mb-5 flex items-center justify-between gap-4">
|
<div className="mb-6 flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-lg font-semibold">助手列表</div>
|
<div className="text-[18px] font-medium text-foreground">
|
||||||
<div className="mt-1 text-sm text-muted-soft">
|
助手列表
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 text-sm text-muted-foreground">
|
||||||
共 {mockAssistants.length} 个助手
|
共 {mockAssistants.length} 个助手
|
||||||
|
{searchQuery.trim() && `,已筛选 ${filteredAssistants.length} 个`}
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div className="relative w-full md:w-[320px]">
|
||||||
<Search size={16} />
|
<Search
|
||||||
<span>搜索助手名称...</span>
|
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>
|
</div>
|
||||||
|
|
||||||
<div className="overflow-hidden rounded-2xl border border-hairline-strong">
|
<div className="overflow-hidden rounded-xl border border-hairline">
|
||||||
<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 className="hidden items-center gap-4 bg-surface-strong/60 px-5 py-3 md:flex">
|
||||||
<div>助手名称</div>
|
<div className="caption-label flex-1 text-muted-soft">
|
||||||
<div>助手类型</div>
|
助手名称
|
||||||
<div>状态</div>
|
</div>
|
||||||
<div>更新时间</div>
|
<div className="caption-label w-[110px] text-muted-soft">
|
||||||
<div className="text-right">操作</div>
|
助手类型
|
||||||
|
</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>
|
||||||
|
|
||||||
<div className="divide-y divide-hairline-strong">
|
<div className="divide-y divide-hairline">
|
||||||
{mockAssistants.map((assistant) => (
|
{filteredAssistants.map((assistant) => (
|
||||||
<div
|
<div
|
||||||
key={assistant.id}
|
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="min-w-0 flex-1">
|
||||||
<div className="font-medium text-foreground">
|
<div className="truncate font-medium text-foreground">
|
||||||
{assistant.name}
|
{assistant.name}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 text-xs text-muted-soft">
|
<div className="mt-1 text-xs text-muted-soft">
|
||||||
{assistant.id}
|
{assistant.id}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className="md:w-[110px]">
|
||||||
<span className="rounded-full border border-hairline-strong bg-surface-strong px-2.5 py-1 text-xs text-muted-foreground">
|
<Badge
|
||||||
{assistant.type}
|
variant="secondary"
|
||||||
</span>
|
className="h-6 bg-surface-strong px-3 text-muted-foreground"
|
||||||
</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(" ")}
|
|
||||||
>
|
>
|
||||||
{assistant.status}
|
{assistant.type}
|
||||||
</span>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-muted-soft">{assistant.updatedAt}</div>
|
<div className="text-muted-foreground md:w-[150px]">
|
||||||
|
{assistant.updatedAt}
|
||||||
<div className="relative flex justify-end gap-2">
|
</div>
|
||||||
<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="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} />
|
<Pencil size={14} />
|
||||||
编辑
|
编辑
|
||||||
</button>
|
</Button>
|
||||||
|
|
||||||
<button
|
<DropdownMenu>
|
||||||
onClick={() =>
|
<DropdownMenuTrigger asChild>
|
||||||
setOpenMenuId(
|
<Button
|
||||||
openMenuId === assistant.id ? null : assistant.id,
|
variant="outline"
|
||||||
)
|
size="icon-sm"
|
||||||
}
|
className="border-hairline-strong text-muted-foreground hover:text-foreground"
|
||||||
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"
|
aria-label={`${assistant.name} 更多操作`}
|
||||||
>
|
>
|
||||||
<MoreHorizontal size={15} />
|
<MoreHorizontal size={15} />
|
||||||
</button>
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
{openMenuId === assistant.id && (
|
<DropdownMenuContent
|
||||||
<div className="absolute right-0 top-9 z-10 w-28 rounded-xl border border-hairline-strong bg-sidebar p-1 shadow-xl">
|
align="end"
|
||||||
<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">
|
className="w-32 min-w-32 rounded-xl border border-hairline bg-popover p-1"
|
||||||
|
>
|
||||||
|
<DropdownMenuItem
|
||||||
|
variant="destructive"
|
||||||
|
className="rounded-lg"
|
||||||
|
>
|
||||||
<Trash2 size={14} />
|
<Trash2 size={14} />
|
||||||
删除
|
删除
|
||||||
</button>
|
</DropdownMenuItem>
|
||||||
</div>
|
</DropdownMenuContent>
|
||||||
)}
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -254,14 +294,6 @@ export function AssistantPage() {
|
|||||||
>
|
>
|
||||||
返回列表
|
返回列表
|
||||||
</Button>
|
</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">
|
<Button size="lg" className="gap-2">
|
||||||
<Rocket size={16} />
|
<Rocket size={16} />
|
||||||
创建助手
|
创建助手
|
||||||
@@ -495,4 +527,4 @@ function ToggleRow({
|
|||||||
<Switch checked={checked} onCheckedChange={onChange} />
|
<Switch checked={checked} onCheckedChange={onChange} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user