ai-chatbot/components/agents/AgentPresetCard.tsx
2026-05-28 19:05:25 +08:00

145 lines
5.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { Copy, Pencil, Sparkles, Trash2, Video } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import type { AgentPreset } from "@/types/agent";
import {
AGENT_PRESET_PREVIEW_IMAGE_SIZES,
getAgentPresetPreviewImageUrl,
} from "./agent-preset-preview";
interface AgentPresetCardProps {
preset: AgentPreset;
selected?: boolean;
onSelect: (presetId: string) => void;
onFork?: (preset: AgentPreset) => void;
onEdit?: (preset: AgentPreset) => void;
onDelete?: (preset: AgentPreset) => void;
}
export function AgentPresetCard({
preset,
selected = false,
onSelect,
onFork,
onEdit,
onDelete,
}: AgentPresetCardProps) {
const rawPreviewUrl =
preset.avatarUrl ||
preset.referenceAssets?.[0]?.thumbnailUrl ||
preset.referenceAssets?.[0]?.originalUrl ||
"";
const previewUrl = getAgentPresetPreviewImageUrl(rawPreviewUrl);
const ownerName =
String(preset.owner?.name || "").trim() ||
String(preset.owner?.email || "").trim() ||
"未知发布人";
const publishedAt = (() => {
const raw = preset.createdAt ? new Date(preset.createdAt) : null;
if (!raw || Number.isNaN(raw.getTime())) return "未知时间";
return raw.toLocaleDateString("zh-CN", {
timeZone: "Asia/Shanghai",
year: "numeric",
month: "2-digit",
day: "2-digit",
});
})();
return (
<div
className={cn(
"group mx-auto w-full max-w-[230px] overflow-hidden rounded-2xl border bg-background transition-all",
selected
? "border-primary bg-primary/5 shadow-[0_0_0_1px_rgba(59,130,246,0.12)]"
: "border-border hover:border-primary/40 hover:shadow-sm"
)}
>
<button
type="button"
className="block w-full text-left"
onClick={() => onSelect(preset.id)}
>
<div className="relative aspect-[4/3] w-full overflow-hidden bg-muted/40">
{previewUrl ? (
<img
src={previewUrl}
alt={preset.name}
loading="lazy"
decoding="async"
fetchPriority="low"
sizes={AGENT_PRESET_PREVIEW_IMAGE_SIZES}
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.03]"
/>
) : (
<div className="flex h-full w-full items-center justify-center">
{preset.type === "video" ? (
<Video className="h-8 w-8 text-muted-foreground/50" />
) : (
<Sparkles className="h-8 w-8 text-muted-foreground/50" />
)}
</div>
)}
<div className="absolute left-2 top-2 flex flex-wrap gap-1.5">
<Badge variant="secondary" className="bg-white/90 text-black shadow-sm">
{preset.type === "video" ? "视频" : preset.type === "text" ? "生文" : "生图"}
</Badge>
</div>
<div className="absolute right-2 top-2 flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
{preset.visibility === "private" && onEdit ? (
<button
type="button"
className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-black shadow-sm transition-colors hover:bg-white"
onClick={(event) => {
event.stopPropagation();
onEdit(preset);
}}
title="编辑"
>
<Pencil className="h-4 w-4" />
</button>
) : null}
{preset.visibility === "private" && onDelete ? (
<button
type="button"
className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-destructive shadow-sm transition-colors hover:bg-white"
onClick={(event) => {
event.stopPropagation();
onDelete(preset);
}}
title="删除"
>
<Trash2 className="h-4 w-4" />
</button>
) : null}
{preset.visibility === "public" && onFork ? (
<button
type="button"
className="inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/90 text-black shadow-sm transition-colors hover:bg-white"
onClick={(event) => {
event.stopPropagation();
onFork(preset);
}}
title="另存为我的风格化"
>
<Copy className="h-4 w-4" />
</button>
) : null}
</div>
</div>
<div className="space-y-1.5 p-3">
<div className="line-clamp-1 text-sm font-semibold">{preset.name}</div>
<div className="line-clamp-1 text-xs text-muted-foreground">
发布人:{ownerName}
</div>
<div className="text-xs text-muted-foreground">
发布时间:{publishedAt}
</div>
</div>
</button>
</div>
);
}