145 lines
5.1 KiB
TypeScript
145 lines
5.1 KiB
TypeScript
"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>
|
||
);
|
||
}
|