Align PSD template pool layout

This commit is contained in:
Your Name 2026-05-07 17:53:21 +08:00
parent d58b706799
commit c843661da3

View File

@ -41,7 +41,6 @@ import {
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Slider } from "@/components/ui/slider";
import { Textarea } from "@/components/ui/textarea";
@ -3039,6 +3038,94 @@ export function AgentPsdTemplateEditorDialog({
setSelectedTemplateId(nextTemplates[0].id);
}
function renderTemplatePoolSection({
mode,
templates: poolTemplates,
}: {
mode: AgentPsdTemplateMode;
templates: AgentPsdTemplate[];
}) {
const isSkuMode = mode === "sku";
const title = isSkuMode ? "SKU图模式模板" : "主图模式模板";
const description = selectedCategorySummaryText
? `当前按品类“${selectedCategorySummaryText}”筛选,同时保留“适合所有”的模板。`
: normalizedBrandName
? `这里集中展示当前品牌下的${isSkuMode ? " SKU 图" : "主图"}模式模板。`
: `当前未选择品牌,这里展示全部品牌下的${isSkuMode ? " SKU 图" : "主图"}模式模板。`;
const emptyMessage = selectedCategorySummaryText
? `当前品类“${selectedCategorySummaryText}”下暂无${isSkuMode ? " SKU 图" : "主图"}模式模板`
: normalizedBrandName
? `当前品牌“${normalizedBrandName}”下暂无${isSkuMode ? " SKU 图" : "主图"}模式模板`
: `当前暂无可用的${isSkuMode ? " SKU 图" : "主图"}模式模板,请先去后台配置。`;
const loadingMessage = `正在加载${isSkuMode ? " SKU 图" : "主图"}模式模板...`;
const isCurrentResultMode = normalizedTemplateMode === mode;
return (
<section className="flex min-h-[560px] min-w-0 flex-col overflow-hidden rounded-2xl border border-white/[0.08] bg-[#141416] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.04),0_18px_45px_rgba(0,0,0,0.22)]">
<div className="flex items-start justify-between gap-4 border-white/[0.07] border-b pb-4">
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2">
<h3 className="font-semibold text-base text-white">{title}</h3>
{isCurrentResultMode ? (
<span className="rounded-full border border-emerald-400/20 bg-emerald-400/10 px-2 py-0.5 font-medium text-[11px] text-emerald-300">
当前模式
</span>
) : null}
</div>
<p className="mt-2 text-white/50 text-xs leading-relaxed">
{description}
</p>
</div>
<div className="shrink-0 rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 font-medium text-white/70 text-xs">
{poolTemplates.length} 个模板
</div>
</div>
<div className="mt-4 min-h-0 flex-1 overflow-y-auto pr-1">
{isLoading ? (
<div className="grid gap-3">
{Array.from({ length: 3 }).map((_, index) => (
<div
className="rounded-xl border border-white/10 border-dashed bg-white/[0.02] px-4 py-5 text-center text-sm text-white/50 shadow-inner"
key={`${mode}-template-loading-${index}`}
>
{loadingMessage}
</div>
))}
</div>
) : poolTemplates.length > 0 ? (
<div className="grid gap-3 [grid-template-columns:repeat(auto-fit,minmax(260px,1fr))]">
{poolTemplates.map((template, index) => (
<button
className={`rounded-xl border p-3 text-left transition-all duration-200 ${
selectedTemplateId === template.id
? "border-emerald-500/50 bg-emerald-500/10 shadow-[0_0_15px_rgba(16,185,129,0.1)]"
: "border-white/[0.08] bg-white/[0.02] hover:border-white/[0.15] hover:bg-white/[0.04]"
}`}
key={template.id}
onClick={() => setSelectedTemplateId(template.id)}
type="button"
>
{renderTemplatePickerCardContent(template, {
showSecondaryCategory:
shouldGroupTemplatesBySecondaryCategory,
showBrand: shouldShowTemplateBrand,
emphasize: true,
eager: index < 4,
})}
</button>
))}
</div>
) : (
<div className="flex min-h-[260px] flex-1 flex-col items-center justify-center rounded-xl border border-white/10 border-dashed bg-white/[0.02] px-6 py-10 text-center text-sm text-white/50 shadow-inner">
{emptyMessage}
</div>
)}
</div>
</section>
);
}
return (
<>
<style
@ -3135,11 +3222,17 @@ export function AgentPsdTemplateEditorDialog({
</div>
<div className="grid min-h-0 flex-1 grid-cols-1 overflow-hidden xl:grid-cols-[320px_minmax(0,1fr)_380px]">
<div className="min-h-0 border-white/12 border-b bg-[#0a0a0a] p-5 xl:border-r xl:border-b-0">
<ScrollArea className="h-full pr-4">
<div className="space-y-4">
<div className="space-y-2">
{selectedTemplate ? (
<div
className={`min-h-0 border-white/12 border-b bg-[#0a0a0a] xl:border-r xl:border-b-0 ${
selectedTemplate ? "p-5" : "p-5 xl:col-span-2 xl:p-6"
}`}
>
<ScrollArea
className={selectedTemplate ? "h-full pr-4" : "h-full pr-3"}
>
{selectedTemplate ? (
<div className="space-y-4">
<div className="space-y-2">
<div className="mb-4 flex items-center justify-between gap-3">
<div>
<div className="font-semibold text-base text-white">
@ -3147,62 +3240,48 @@ export function AgentPsdTemplateEditorDialog({
</div>
</div>
</div>
) : (
<div className="mb-4 flex items-center justify-between gap-3">
<div>
<div className="font-semibold text-base text-white">
主图模式模板
{selectedCategorySummaryText ? (
<div className="rounded-lg border border-white/10 bg-white/[0.02] px-3 py-2 text-white/50 text-xs shadow-inner">
{`当前按品类“${selectedCategorySummaryText}”筛选模板,同时保留“适合所有”的模板。`}
</div>
) : null}
{hasTemplateOptions ? (
<div className="space-y-2.5">
<div className="inline-flex rounded-[14px] bg-white/5 p-1 shadow-inner ring-1 ring-white/10">
<button
className={`rounded-xl px-4 py-1.5 font-medium text-xs transition-all duration-200 ${
selectedTemplateMode === "main"
? "bg-emerald-500 text-white shadow-[0_2px_10px_rgba(16,185,129,0.3)] ring-1 ring-emerald-500/50"
: "text-white/60 hover:bg-white/5 hover:text-white"
}`}
onClick={() =>
handleSwitchSelectedTemplateMode("main")
}
type="button"
>
主图模式
</button>
<button
className={`rounded-xl px-4 py-1.5 font-medium text-xs transition-all duration-200 ${
selectedTemplateMode === "sku"
? "bg-emerald-500 text-white shadow-[0_2px_10px_rgba(16,185,129,0.3)] ring-1 ring-emerald-500/50"
: "text-white/60 hover:bg-white/5 hover:text-white"
}`}
onClick={() =>
handleSwitchSelectedTemplateMode("sku")
}
type="button"
>
SKU图模式
</button>
</div>
</div>
<div className="shrink-0 rounded-full border border-white/10 bg-white/[0.04] px-3 py-1 font-medium text-white/70 text-xs">
{mainModeTemplates.length} 个模板
</div>
</div>
)}
{selectedCategorySummaryText ? (
<div className="rounded-lg border border-white/10 bg-white/[0.02] px-3 py-2 text-white/50 text-xs shadow-inner">
当前按品类“{selectedCategorySummaryText}
”筛选模板,同时保留“适合所有”的模板。
</div>
) : null}
{selectedTemplate && hasTemplateOptions ? (
<div className="space-y-2.5">
<div className="inline-flex rounded-[14px] bg-white/5 p-1 shadow-inner ring-1 ring-white/10">
<button
className={`rounded-xl px-4 py-1.5 font-medium text-xs transition-all duration-200 ${
selectedTemplateMode === "main"
? "bg-emerald-500 text-white shadow-[0_2px_10px_rgba(16,185,129,0.3)] ring-1 ring-emerald-500/50"
: "text-white/60 hover:bg-white/5 hover:text-white"
}`}
onClick={() =>
handleSwitchSelectedTemplateMode("main")
}
type="button"
<Select
onValueChange={setSelectedTemplateId}
value={selectedTemplateSelectValue}
>
主图模式
</button>
<button
className={`rounded-xl px-4 py-1.5 font-medium text-xs transition-all duration-200 ${
selectedTemplateMode === "sku"
? "bg-emerald-500 text-white shadow-[0_2px_10px_rgba(16,185,129,0.3)] ring-1 ring-emerald-500/50"
: "text-white/60 hover:bg-white/5 hover:text-white"
}`}
onClick={() =>
handleSwitchSelectedTemplateMode("sku")
}
type="button"
>
SKU图模式
</button>
</div>
<Select
onValueChange={setSelectedTemplateId}
value={selectedTemplateSelectValue}
>
<SelectTrigger className="h-auto min-h-[58px] items-center rounded-xl border border-white/10 bg-white/[0.02] px-3 py-2 text-white/90 shadow-inner transition-colors hover:border-white/20 hover:bg-white/[0.04] focus:ring-1 focus:ring-emerald-500/50 data-[state=open]:border-emerald-500/50 [&>span]:contents">
{selectedTemplate ? (
<SelectTrigger className="h-auto min-h-[58px] items-center rounded-xl border border-white/10 bg-white/[0.02] px-3 py-2 text-white/90 shadow-inner transition-colors hover:border-white/20 hover:bg-white/[0.04] focus:ring-1 focus:ring-emerald-500/50 data-[state=open]:border-emerald-500/50 [&>span]:contents">
<div className="min-w-0 flex-1 pr-3">
{renderTemplateSelectContent(selectedTemplate, {
showSecondaryCategory:
@ -3210,152 +3289,105 @@ export function AgentPsdTemplateEditorDialog({
showBrand: shouldShowTemplateBrand,
})}
</div>
) : (
<SelectValue
placeholder={
isLoading ? "加载模板中..." : "请选择模板"
}
/>
)}
</SelectTrigger>
<SelectContent
className="border-white/10 bg-[#141416] text-white shadow-2xl"
style={{
backgroundColor: "#141416",
color: "#ffffff",
}}
>
{shouldGroupTemplatesBySecondaryCategory
? groupedSelectedModeTemplates.map((group) => (
<SelectGroup
key={`psd-template-group-${group.label}`}
>
<SelectLabel className="text-white/50">
{group.label}
</SelectLabel>
{group.templates.map((template) => (
<SelectItem
className="py-2.5 pr-3 text-white focus:bg-white/10 focus:text-white data-[state=checked]:bg-white/10 data-[state=checked]:text-white"
key={template.id}
value={template.id}
>
{renderTemplateSelectContent(template, {
showSecondaryCategory:
shouldGroupTemplatesBySecondaryCategory,
showBrand: shouldShowTemplateBrand,
compact: true,
})}
</SelectItem>
))}
</SelectGroup>
))
: selectedModeTemplates.map((template) => (
<SelectItem
className="py-2.5 pr-3 text-white focus:bg-white/10 focus:text-white data-[state=checked]:bg-white/10 data-[state=checked]:text-white"
key={template.id}
value={template.id}
>
{renderTemplateSelectContent(template, {
showBrand: shouldShowTemplateBrand,
compact: true,
})}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
) : (
<div className="space-y-2">
{isLoading ? (
<>
{Array.from({ length: 2 }).map((_, index) => (
<div
className="rounded-2xl border border-white/10 border-dashed bg-white/[0.02] px-4 py-5 text-center text-sm text-white/50 shadow-inner"
key={`main-template-loading-${index}`}
>
正在加载主图模式模板...
</div>
))}
</>
) : mainModeTemplates.length > 0 ? (
mainModeTemplates.map((template, index) => (
<button
className={`w-full rounded-2xl border p-3 text-left transition-all duration-200 ${
selectedTemplateId === template.id
? "border-emerald-500/50 bg-emerald-500/10 shadow-[0_0_15px_rgba(16,185,129,0.1)]"
: "border-white/[0.08] bg-white/[0.02] hover:border-white/[0.15] hover:bg-white/[0.04]"
}`}
key={template.id}
onClick={() => setSelectedTemplateId(template.id)}
type="button"
</SelectTrigger>
<SelectContent
className="border-white/10 bg-[#141416] text-white shadow-2xl"
style={{
backgroundColor: "#141416",
color: "#ffffff",
}}
>
{renderTemplatePickerCardContent(template, {
showSecondaryCategory:
shouldGroupTemplatesBySecondaryCategory,
showBrand: shouldShowTemplateBrand,
emphasize: true,
eager: index < 2,
})}
</button>
))
) : (
<div className="flex flex-col items-center justify-center rounded-2xl border border-white/10 border-dashed bg-white/[0.02] p-8 text-center text-sm text-white/50 shadow-inner">
{selectedCategorySummaryText
? `当前品类“${selectedCategorySummaryText}”下暂无主图模式模板`
: normalizedBrandName
? `当前品牌“${normalizedBrandName}”下暂无主图模式模板`
: "当前暂无可用的主图模式模板,请先去后台配置。"}
</div>
)}
{shouldGroupTemplatesBySecondaryCategory
? groupedSelectedModeTemplates.map((group) => (
<SelectGroup
key={`psd-template-group-${group.label}`}
>
<SelectLabel className="text-white/50">
{group.label}
</SelectLabel>
{group.templates.map((template) => (
<SelectItem
className="py-2.5 pr-3 text-white focus:bg-white/10 focus:text-white data-[state=checked]:bg-white/10 data-[state=checked]:text-white"
key={template.id}
value={template.id}
>
{renderTemplateSelectContent(
template,
{
showSecondaryCategory:
shouldGroupTemplatesBySecondaryCategory,
showBrand:
shouldShowTemplateBrand,
compact: true,
}
)}
</SelectItem>
))}
</SelectGroup>
))
: selectedModeTemplates.map((template) => (
<SelectItem
className="py-2.5 pr-3 text-white focus:bg-white/10 focus:text-white data-[state=checked]:bg-white/10 data-[state=checked]:text-white"
key={template.id}
value={template.id}
>
{renderTemplateSelectContent(template, {
showBrand: shouldShowTemplateBrand,
compact: true,
})}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
) : null}
</div>
<div className="mx-auto w-full max-w-[220px] overflow-hidden rounded-2xl border border-white/10 bg-white/5 shadow-inner">
<div
style={{
aspectRatio: `${Math.max(1, selectedTemplate.width)} / ${Math.max(1, selectedTemplate.height)}`,
}}
>
<TemplatePreviewImage
className="h-full w-full object-contain"
loading="eager"
quality={78}
template={selectedTemplate}
width={440}
/>
</div>
</div>
{sourceImageUrl ? null : (
<div className="rounded-xl border border-white/10 border-dashed bg-white/[0.02] px-4 py-3 text-white/50 text-xs leading-relaxed shadow-inner">
当前未从生图结果带入图片。请先在下方图片图层中点击“上传替换图片”,再开始套用
PSD 模板。
</div>
)}
</div>
{selectedTemplate ? (
<>
<div className="mx-auto w-full max-w-[220px] overflow-hidden rounded-2xl border border-white/10 bg-white/5 shadow-inner">
<div
style={{
aspectRatio: `${Math.max(1, selectedTemplate.width)} / ${Math.max(1, selectedTemplate.height)}`,
}}
>
<TemplatePreviewImage
className="h-full w-full object-contain"
loading="eager"
quality={78}
template={selectedTemplate}
width={440}
/>
</div>
</div>
{sourceImageUrl ? null : (
<div className="rounded-xl border border-white/10 border-dashed bg-white/[0.02] px-4 py-3 text-white/50 text-xs leading-relaxed shadow-inner">
当前未从生图结果带入图片。请先在下方图片图层中点击“上传替换图片”,再开始套用
PSD 模板。
</div>
)}
</>
) : (
<div className="flex h-full w-full flex-col items-center justify-center rounded-2xl border border-white/10 border-dashed bg-white/[0.02] p-8 text-center text-sm text-white/50 shadow-inner">
{isLoading
? "正在加载可用模板..."
: hasTemplateOptions
? "请从左侧主图模式模板或右侧 SKU 图模式模板中选择一个模板开始编辑。"
: selectedCategorySummaryText && normalizedBrandName
? `当前品牌“${normalizedBrandName}”下没有命中品类“${selectedCategorySummaryText}”的 PSD 模板,请先去后台配置。`
: selectedCategorySummaryText
? `当前品类“${selectedCategorySummaryText}”下还没有可用 PSD 模板,请先去后台配置。`
: normalizedBrandName
? `当前品牌“${normalizedBrandName}”下还没有可用 PSD 模板,请先去后台配置。`
: "当前还没有启用的 PSD 模板,请先去后台配置。"}
</div>
)}
</div>
) : (
<div className="grid min-h-[640px] gap-5 xl:grid-cols-2">
{renderTemplatePoolSection({
mode: "main",
templates: mainModeTemplates,
})}
{renderTemplatePoolSection({
mode: "sku",
templates: skuModeTemplates,
})}
</div>
)}
</ScrollArea>
</div>
<div className="flex min-h-0 flex-col border-white/12 border-b bg-[#0a0a0a] p-6 xl:border-r xl:border-b-0">
<div
className={
selectedTemplate
? "flex min-h-0 flex-col border-white/12 border-b bg-[#0a0a0a] p-6 xl:border-r xl:border-b-0"
: "hidden"
}
>
<div
className="psd-preview-viewport relative flex min-h-0 flex-1 items-center justify-center overflow-auto rounded-[28px] border border-white/[0.08] bg-[#141416] p-4 shadow-[inset_0_2px_20px_rgba(0,0,0,0.4)]"
ref={previewViewportRef}