diff --git a/frontend-web/src/App.tsx b/frontend-web/src/App.tsx index 01168597..5b907bf4 100644 --- a/frontend-web/src/App.tsx +++ b/frontend-web/src/App.tsx @@ -134,11 +134,11 @@ export function App() { } /> } /> diff --git a/frontend-web/src/components/workspace/agents/agent-card.tsx b/frontend-web/src/components/workspace/agents/agent-card.tsx index 25358b1f..27dda218 100644 --- a/frontend-web/src/components/workspace/agents/agent-card.tsx +++ b/frontend-web/src/components/workspace/agents/agent-card.tsx @@ -1,8 +1,15 @@ "use client"; -import { BotIcon, MessageSquareIcon, SquarePenIcon, Trash2Icon } from "lucide-react"; +import { + BotIcon, + EyeIcon, + EyeOffIcon, + MessageSquareIcon, + SquarePenIcon, + Trash2Icon, +} from "lucide-react"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; @@ -24,9 +31,11 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { useDeleteAgent, useUpdateAgent } from "@/core/agents"; import type { Agent } from "@/core/agents"; +import { getStoredAuth } from "@/core/auth"; import { useI18n } from "@/core/i18n/hooks"; import { useSkills } from "@/core/skills/hooks"; @@ -42,28 +51,45 @@ export function AgentCard({ agent }: AgentCardProps) { const { skills, isLoading: isSkillsLoading } = useSkills(); const [deleteOpen, setDeleteOpen] = useState(false); const [editOpen, setEditOpen] = useState(false); + const [editName, setEditName] = useState(agent.name); const [editDescription, setEditDescription] = useState(agent.description ?? ""); const [editSoul, setEditSoul] = useState(agent.soul ?? ""); + const [editPublished, setEditPublished] = useState(agent.published); const [selectedSkills, setSelectedSkills] = useState( agent.skills ?? [], ); + const currentUserId = useMemo(() => getStoredAuth()?.user_id ?? null, []); + const isBuiltin = agent.builtin; + const isOwned = + !isBuiltin && agent.user_id !== null && agent.user_id === currentUserId; + const isFromOthers = !isBuiltin && !isOwned; + useEffect(() => { if (!editOpen) { return; } + setEditName(agent.name); setEditDescription(agent.description ?? ""); setEditSoul(agent.soul ?? ""); + setEditPublished(agent.published); setSelectedSkills(agent.skills ?? []); - }, [agent.description, agent.skills, agent.soul, editOpen]); + }, [ + agent.description, + agent.name, + agent.published, + agent.skills, + agent.soul, + editOpen, + ]); function handleChat() { - router.push(`/workspace/agents/${agent.name}/chats/new`); + router.push(`/workspace/agents/${agent.id}/chats/new`); } async function handleDelete() { try { - await deleteAgent.mutateAsync(agent.name); + await deleteAgent.mutateAsync(agent.id); toast.success(t.agents.deleteSuccess); setDeleteOpen(false); } catch (err) { @@ -72,12 +98,19 @@ export function AgentCard({ agent }: AgentCardProps) { } async function handleSaveEdit() { + const trimmedName = editName.trim(); + if (!trimmedName) { + toast.error(t.agents.nameStepInvalidError ?? "名称不能为空"); + return; + } try { await updateAgent.mutateAsync({ - name: agent.name, + id: agent.id, request: { + name: trimmedName, description: editDescription, soul: editSoul, + published: editPublished, skills: selectedSkills, }, }); @@ -88,6 +121,18 @@ export function AgentCard({ agent }: AgentCardProps) { } } + async function handleTogglePublished() { + try { + await updateAgent.mutateAsync({ + id: agent.id, + request: { published: !agent.published }, + }); + toast.success(agent.published ? "已取消发布" : "已发布"); + } catch (err) { + toast.error(err instanceof Error ? err.message : String(err)); + } + } + function toggleSkill(skillName: string) { setSelectedSkills((prev) => { if (prev.includes(skillName)) { @@ -119,6 +164,39 @@ export function AgentCard({ agent }: AgentCardProps) { )} +
+ {isBuiltin && ( + + 内置 + + )} + {isOwned && ( + + 我的 + + )} + {isFromOthers && ( + + 来自他人 + + )} + {isOwned && agent.published && ( + + 已发布 + + )} +
{agent.description && ( @@ -157,41 +235,61 @@ export function AgentCard({ agent }: AgentCardProps) { {t.agents.chat} -
- - -
+ {isOwned && ( +
+ + + +
+ )} - 编辑智能体:{agent.name} + 编辑智能体:{agent.name} - 可以修改描述、技能白名单和 SOUL 内容。 + 可以修改名称、描述、技能白名单、SOUL 内容与发布状态。
- + setEditName(e.target.value)} + placeholder="输入智能体名称(允许中文)" + />
@@ -238,7 +336,7 @@ export function AgentCard({ agent }: AgentCardProps) { )}

- 不选任何技能会保存为无技能(skills = [])。 + 不选任何技能会保存为无技能(skills = [])。

@@ -250,6 +348,18 @@ export function AgentCard({ agent }: AgentCardProps) { className="min-h-40 font-mono text-xs" />
+
+
+

公开发布

+

+ 打开后,该智能体对所有用户可见。 +

+
+ +