refactor: unify ai wash upload preview
This commit is contained in:
parent
afc41c5a42
commit
b1dc367bcf
@ -988,69 +988,6 @@ export function AiWashWorkspace({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className={cn(
|
||||
"h-12 w-full rounded-[20px] px-4 text-[15px] font-semibold",
|
||||
PRIMARY_ACTION_BUTTON_CLASS
|
||||
)}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
!compareSourceUrl ||
|
||||
enabledPresets.length === 0 ||
|
||||
(shouldShowUserModelSelector && !selectedUserModel)
|
||||
}
|
||||
onClick={handleSubmit}
|
||||
type="button"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Sparkles className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
{isSubmitting
|
||||
? "正在洗图"
|
||||
: totalEstimatedCost > 0
|
||||
? `开始 AI洗图 · ${totalEstimatedCost} 积分`
|
||||
: "开始 AI洗图"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className={cn(SUBTLE_PANEL_CLASS, "space-y-3 p-3.5")}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-[15px] font-semibold text-[#111111]">
|
||||
上传底图
|
||||
</div>
|
||||
<Button
|
||||
className={cn(
|
||||
"h-9 rounded-full px-3 text-xs font-medium",
|
||||
SECONDARY_ACTION_BUTTON_CLASS
|
||||
)}
|
||||
onClick={() => onRequestAddImage?.()}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="outline"
|
||||
>
|
||||
<Upload className="mr-1.5 h-3.5 w-3.5" />
|
||||
{compareSourceUrl ? "更换图片" : "选择图片"}
|
||||
</Button>
|
||||
</div>
|
||||
{compareSourceUrl ? (
|
||||
<div className="overflow-hidden rounded-[18px] border border-[#ececec] bg-white">
|
||||
<img
|
||||
alt="AI洗图底图"
|
||||
className="aspect-[16/10] w-full object-cover"
|
||||
src={getDisplayImageUrl(compareSourceUrl, 720)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className="flex aspect-[16/10] w-full items-center justify-center rounded-[18px] border border-dashed border-[#d9d9d9] bg-white text-center text-sm text-[#8a8a8a] transition hover:border-[#111111] hover:text-[#111111]"
|
||||
onClick={() => onRequestAddImage?.()}
|
||||
type="button"
|
||||
>
|
||||
先上传一张底图
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={cn(SUBTLE_PANEL_CLASS, "space-y-2 p-3.5")}>
|
||||
@ -1247,49 +1184,27 @@ export function AiWashWorkspace({
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="flex min-w-0 flex-1 flex-col overflow-hidden px-6 pb-6 pt-6">
|
||||
<main className="flex min-w-0 flex-1 flex-col gap-3 overflow-hidden px-6 pb-6 pt-6">
|
||||
<section
|
||||
className={cn(
|
||||
PANEL_CLASS,
|
||||
"flex min-h-0 flex-1 flex-col overflow-hidden p-4"
|
||||
"flex min-h-0 flex-1 flex-col overflow-hidden p-3"
|
||||
)}
|
||||
>
|
||||
<div className="mb-4 flex flex-wrap items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="text-[24px] font-semibold leading-tight text-[#111111]">
|
||||
{selectedPreviewResult?.title || "AI洗图结果预览"}
|
||||
</div>
|
||||
<span className="rounded-full border border-[#ebebeb] bg-[#fafafa] px-3 py-1 text-xs text-[#666666]">
|
||||
{getAiWashStatusLabel(selectedPreviewResult?.status)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 max-w-3xl text-sm leading-6 text-[#666666]">
|
||||
{selectedPreviewResult?.description ||
|
||||
"左侧选择渲染目标后,所有洗图结果都会共用这里的大尺寸预览区。"}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2 text-xs text-[#666666]">
|
||||
{selectedPreviewResult?.modelId ? (
|
||||
<span className="rounded-full border border-[#ebebeb] bg-[#fafafa] px-3 py-1.5">
|
||||
{selectedPreviewResult.modelId}
|
||||
</span>
|
||||
) : null}
|
||||
{selectedPreviewResult?.createdAt ? (
|
||||
<span className="rounded-full border border-[#ebebeb] bg-[#fafafa] px-3 py-1.5">
|
||||
{new Date(selectedPreviewResult.createdAt).toLocaleTimeString(
|
||||
"zh-CN",
|
||||
{
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
}
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative min-h-[560px] flex-1 overflow-hidden rounded-[28px] border border-[#e9e9e9] bg-[radial-gradient(circle_at_50%_0%,#ffffff_0%,#f8f8f8_52%,#f2f2f2_100%)]">
|
||||
<div className="relative min-h-0 flex-1 overflow-hidden rounded-[28px] border border-[#e9e9e9] bg-[radial-gradient(circle_at_50%_0%,#ffffff_0%,#f8f8f8_52%,#f2f2f2_100%)]">
|
||||
{compareSourceUrl ? (
|
||||
<button
|
||||
className={cn(
|
||||
"absolute right-4 top-4 z-20 inline-flex h-10 items-center rounded-full px-4 text-sm font-medium",
|
||||
SECONDARY_ACTION_BUTTON_CLASS
|
||||
)}
|
||||
onClick={() => onRequestAddImage?.()}
|
||||
type="button"
|
||||
>
|
||||
<Upload className="mr-2 h-4 w-4" />
|
||||
更换图片
|
||||
</button>
|
||||
) : null}
|
||||
{selectedPreviewResult ? (
|
||||
previewHasImage ? (
|
||||
<>
|
||||
@ -1359,54 +1274,96 @@ export function AiWashWorkspace({
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : compareSourceUrl ? (
|
||||
<>
|
||||
<img
|
||||
alt="AI洗图底图"
|
||||
className="absolute inset-0 h-full w-full object-contain"
|
||||
src={getDisplayImageUrl(compareSourceUrl, 1800)}
|
||||
/>
|
||||
<div className="pointer-events-none absolute left-4 top-4 rounded-full bg-black/65 px-3 py-1.5 text-[11px] text-white">
|
||||
原图
|
||||
</div>
|
||||
{(selectedPreviewResult.status === "running" ||
|
||||
selectedPreviewResult.status === "submitting" ||
|
||||
selectedPreviewResult.status === "failed") ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/58 px-6 text-center backdrop-blur-[1px]">
|
||||
<div className="max-w-sm rounded-[24px] border border-[#ececec] bg-white/92 px-6 py-5 shadow-[0_18px_40px_rgba(0,0,0,0.08)]">
|
||||
{selectedPreviewResult.status === "running" ||
|
||||
selectedPreviewResult.status === "submitting" ? (
|
||||
<>
|
||||
<Loader2 className="mx-auto mb-3 h-8 w-8 animate-spin text-[#666666]" />
|
||||
<div className="text-base font-medium text-[#111111]">
|
||||
{selectedPreviewResult.status === "submitting"
|
||||
? "正在提交模型任务"
|
||||
: "模型正在生成结果"}
|
||||
</div>
|
||||
<div className="mt-2 text-sm leading-6 text-[#777777]">
|
||||
结果完成后会在同一区域切换为可拖动对比图。
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-base font-medium text-[#9b2c2c]">
|
||||
该目标生成失败
|
||||
</div>
|
||||
<div className="mt-2 text-sm leading-6 text-[#9b2c2c]">
|
||||
{selectedPreviewResult.error || "请调整配置后重新生成。"}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pointer-events-none absolute bottom-5 left-1/2 -translate-x-1/2 rounded-full border border-[#e5e5e5] bg-white/92 px-4 py-2 text-xs text-[#666666] shadow-[0_12px_24px_rgba(0,0,0,0.06)]">
|
||||
原图已就绪,点击下方开始 AI洗图
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="absolute inset-0 flex items-center justify-center px-6 text-center">
|
||||
<div className="max-w-sm">
|
||||
{selectedPreviewResult.status === "running" ||
|
||||
selectedPreviewResult.status === "submitting" ? (
|
||||
<>
|
||||
<Loader2 className="mx-auto mb-4 h-9 w-9 animate-spin text-[#666666]" />
|
||||
<div className="text-base font-medium text-[#111111]">
|
||||
{selectedPreviewResult.status === "submitting"
|
||||
? "正在提交模型任务"
|
||||
: "模型正在生成结果"}
|
||||
</div>
|
||||
<div className="mt-2 text-sm leading-6 text-[#777777]">
|
||||
生成完成后,这里会自动切换为大图对比预览。
|
||||
</div>
|
||||
</>
|
||||
) : selectedPreviewResult.status === "failed" ? (
|
||||
<>
|
||||
<div className="text-base font-medium text-[#9b2c2c]">
|
||||
该目标生成失败
|
||||
</div>
|
||||
<div className="mt-2 text-sm leading-6 text-[#9b2c2c]">
|
||||
{selectedPreviewResult.error || "请调整配置后重新生成。"}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ImageIcon className="mx-auto mb-4 h-10 w-10 text-[#b2b2b2]" />
|
||||
<div className="text-base font-medium text-[#111111]">
|
||||
固定单图预览区
|
||||
</div>
|
||||
<div className="mt-2 text-sm leading-6 text-[#777777]">
|
||||
先上传底图并点击开始 AI洗图,结果会在这里完整展示。
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
<button
|
||||
className="inline-flex h-16 items-center rounded-[26px] bg-[#111111] px-10 text-[18px] font-semibold text-white shadow-[0_18px_40px_rgba(0,0,0,0.16)] transition hover:bg-black"
|
||||
onClick={() => onRequestAddImage?.()}
|
||||
type="button"
|
||||
>
|
||||
<ImageIcon className="mr-3 h-5 w-5" />
|
||||
添加图片
|
||||
</button>
|
||||
<div className="mt-4 text-sm text-[#777777]">
|
||||
选择本地图后即可开始 AI洗图
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
) : compareSourceUrl ? (
|
||||
<>
|
||||
<img
|
||||
alt="AI洗图底图"
|
||||
className="absolute inset-0 h-full w-full object-contain"
|
||||
src={getDisplayImageUrl(compareSourceUrl, 1800)}
|
||||
/>
|
||||
<div className="pointer-events-none absolute left-4 top-4 rounded-full bg-black/65 px-3 py-1.5 text-[11px] text-white">
|
||||
原图
|
||||
</div>
|
||||
<div className="pointer-events-none absolute bottom-5 left-1/2 -translate-x-1/2 rounded-full border border-[#e5e5e5] bg-white/92 px-4 py-2 text-xs text-[#666666] shadow-[0_12px_24px_rgba(0,0,0,0.06)]">
|
||||
原图已就绪,点击下方开始 AI洗图
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="absolute inset-0 flex items-center justify-center px-6 text-center">
|
||||
<div>
|
||||
<Sparkles className="mx-auto mb-4 h-10 w-10 text-[#9a9a9a]" />
|
||||
<div className="text-base font-medium text-[#111111]">
|
||||
AI洗图准备就绪
|
||||
</div>
|
||||
<div className="mt-2 text-sm text-[#777777]">
|
||||
管理员配置预设后,这里会展示共享结果预览。
|
||||
<button
|
||||
className="inline-flex h-16 items-center rounded-[26px] bg-[#111111] px-10 text-[18px] font-semibold text-white shadow-[0_18px_40px_rgba(0,0,0,0.16)] transition hover:bg-black"
|
||||
onClick={() => onRequestAddImage?.()}
|
||||
type="button"
|
||||
>
|
||||
<ImageIcon className="mr-3 h-5 w-5" />
|
||||
添加图片
|
||||
</button>
|
||||
<div className="mt-4 text-sm text-[#777777]">
|
||||
选择本地图后即可开始 AI洗图
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1414,30 +1371,61 @@ export function AiWashWorkspace({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="mt-5 flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="rounded-full border border-[#e5e5e5] bg-white px-4 py-2 text-xs text-[#666666] shadow-[0_0_0_1px_rgba(0,0,0,0.02),0_10px_20px_rgba(0,0,0,0.03)]">
|
||||
{selectedResult
|
||||
? `已选中 ${selectedResult.title},可直接发送回画布`
|
||||
: selectedPreviewResult
|
||||
? `当前预览 ${selectedPreviewResult.title},生成完成后可发送回画布`
|
||||
: "请选择或生成一张洗图结果"}
|
||||
? compareSourceUrl
|
||||
? `当前目标 ${selectedPreviewResult.title},生成完成后可发送回画布`
|
||||
: "请先添加一张底图"
|
||||
: compareSourceUrl
|
||||
? "原图已上传,点击开始 AI洗图生成结果"
|
||||
: "请先添加一张底图"}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button
|
||||
className={cn(
|
||||
"h-11 rounded-[18px] px-5 text-sm font-semibold",
|
||||
PRIMARY_ACTION_BUTTON_CLASS
|
||||
)}
|
||||
disabled={
|
||||
isSubmitting ||
|
||||
!compareSourceUrl ||
|
||||
enabledPresets.length === 0 ||
|
||||
(shouldShowUserModelSelector && !selectedUserModel)
|
||||
}
|
||||
onClick={handleSubmit}
|
||||
type="button"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Sparkles className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
{isSubmitting
|
||||
? "正在洗图"
|
||||
: totalEstimatedCost > 0
|
||||
? `开始 AI洗图 · ${totalEstimatedCost} 积分`
|
||||
: "开始 AI洗图"}
|
||||
</Button>
|
||||
<Button
|
||||
className={cn(
|
||||
"h-11 rounded-[18px] px-5 text-sm font-medium",
|
||||
SECONDARY_ACTION_BUTTON_CLASS
|
||||
)}
|
||||
disabled={!selectedResult?.imageUrl}
|
||||
onClick={handleApply}
|
||||
type="button"
|
||||
variant="outline"
|
||||
>
|
||||
<Send className="mr-2 h-4 w-4" />
|
||||
发送到画布
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
className={cn(
|
||||
"h-11 rounded-[18px] px-5 text-sm font-medium",
|
||||
SECONDARY_ACTION_BUTTON_CLASS
|
||||
)}
|
||||
disabled={!selectedResult?.imageUrl}
|
||||
onClick={handleApply}
|
||||
type="button"
|
||||
variant="outline"
|
||||
>
|
||||
<Send className="mr-2 h-4 w-4" />
|
||||
发送到画布
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className={cn(PANEL_CLASS, "mt-4 overflow-hidden p-3")}>
|
||||
<div className={cn(PANEL_CLASS, "overflow-hidden p-3")}>
|
||||
<button
|
||||
className="flex w-full items-center justify-between gap-3 rounded-[22px] px-1 py-1 text-left transition hover:bg-[#fafafa]"
|
||||
onClick={() => setIsHistoryExpanded((current) => !current)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user