refactor: sync clonePersistence types, extract WatermarkToolPage
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
import {
|
||||
CloudUploadOutlined,
|
||||
FileImageOutlined,
|
||||
FolderOpenOutlined,
|
||||
FrownOutlined,
|
||||
LoadingOutlined,
|
||||
QuestionCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import type { ChangeEvent, DragEvent, KeyboardEvent, RefObject } from "react";
|
||||
import { toast } from "../../../components/toast/toastStore";
|
||||
|
||||
export interface WatermarkImageItem {
|
||||
src: string;
|
||||
name: string;
|
||||
format: string;
|
||||
}
|
||||
|
||||
export type WatermarkStatus = "idle" | "processing" | "done" | "failed";
|
||||
|
||||
interface WatermarkToolPageProps {
|
||||
inputRef: RefObject<HTMLInputElement>;
|
||||
urlInputRef: RefObject<HTMLInputElement>;
|
||||
image: WatermarkImageItem | null;
|
||||
isDragging: boolean;
|
||||
status: WatermarkStatus;
|
||||
progress: number;
|
||||
resultUrl: string | null;
|
||||
onUpload: (event: ChangeEvent<HTMLInputElement>) => void;
|
||||
onDrop: (event: DragEvent<HTMLDivElement>) => void;
|
||||
onDraggingChange: (dragging: boolean) => void;
|
||||
onRemoveImage: () => void;
|
||||
onUrlImport: () => void;
|
||||
onGenerate: () => void;
|
||||
onDownload: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// 去水印工具页面:上传含水印图片 → AI 清理 → 预览/下载结果。
|
||||
// 从 EcommercePage 的 watermarkPreview 抽出,状态与处理逻辑仍在父组件,本组件纯展示 + 回调。
|
||||
export default function WatermarkToolPage({
|
||||
inputRef,
|
||||
urlInputRef,
|
||||
image,
|
||||
isDragging,
|
||||
status,
|
||||
progress,
|
||||
resultUrl,
|
||||
onUpload,
|
||||
onDrop,
|
||||
onDraggingChange,
|
||||
onRemoveImage,
|
||||
onUrlImport,
|
||||
onGenerate,
|
||||
onDownload,
|
||||
onClose,
|
||||
}: WatermarkToolPageProps) {
|
||||
return (
|
||||
<main key="watermark" className="ecom-watermark-page ecom-tool-page-enter" aria-label="去水印">
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="ecom-command-hidden-file"
|
||||
onChange={onUpload}
|
||||
aria-label="上传去水印图片"
|
||||
/>
|
||||
<aside className="ecom-watermark-side">
|
||||
<header className="ecom-quick-set-panel-head ecom-watermark-panel-head">
|
||||
<strong className="ecom-quick-set-page-title">去水印</strong>
|
||||
<button type="button" className="ecom-quick-set-back" onClick={onClose}>首页</button>
|
||||
<button type="button" className="ecom-quick-set-back" onClick={onClose}>上一页</button>
|
||||
</header>
|
||||
<p className="ecom-watermark-intro">上传商品素材,快速清理画面中的水印、文字和瑕疵。</p>
|
||||
<section className="ecom-watermark-panel">
|
||||
<header>
|
||||
<strong>上传素材</strong>
|
||||
<span>{image ? "已上传" : "待上传"}</span>
|
||||
</header>
|
||||
<div
|
||||
className={`ecom-watermark-upload-card${isDragging ? " is-dragging" : ""}${image ? " has-image" : ""}`}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => inputRef.current?.click()}
|
||||
onKeyDown={(event: KeyboardEvent) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
inputRef.current?.click();
|
||||
}
|
||||
}}
|
||||
onDragEnter={(event) => {
|
||||
event.preventDefault();
|
||||
onDraggingChange(true);
|
||||
}}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDragLeave={() => onDraggingChange(false)}
|
||||
onDrop={onDrop}
|
||||
>
|
||||
{image ? (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="ecom-watermark-remove"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onRemoveImage();
|
||||
}}
|
||||
aria-label="删除素材"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
<figure>
|
||||
<img src={image.src} alt={image.name} />
|
||||
</figure>
|
||||
<div>
|
||||
<strong>{image.name}</strong>
|
||||
<span>{image.format || "PNG / JPG / WebP"}</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CloudUploadOutlined />
|
||||
<strong>上传含水印图片</strong>
|
||||
<span>支持 PNG / JPG / WebP,拖拽或点击上传</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="ecom-watermark-url-row">
|
||||
<input
|
||||
ref={urlInputRef}
|
||||
placeholder="粘贴图片 URL"
|
||||
aria-label="粘贴图片 URL"
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") void onUrlImport();
|
||||
}}
|
||||
/>
|
||||
<button type="button" onClick={() => void onUrlImport()}>导入</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="ecom-watermark-panel">
|
||||
<strong>处理说明</strong>
|
||||
<p>优先保留商品主体、材质和边缘细节,适合电商主图、详情图和社媒素材清理。</p>
|
||||
</section>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="ecom-watermark-primary"
|
||||
onClick={onGenerate}
|
||||
disabled={!image || status === "processing"}
|
||||
>
|
||||
{status === "processing" ? <LoadingOutlined /> : <FileImageOutlined />}
|
||||
{status === "processing" ? "处理中" : "开始去水印"}
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
<section className="ecom-watermark-workspace">
|
||||
{!image ? (
|
||||
<div
|
||||
className={`ecom-watermark-dropzone${isDragging ? " is-dragging" : ""}`}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => inputRef.current?.click()}
|
||||
onKeyDown={(event: KeyboardEvent) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
inputRef.current?.click();
|
||||
}
|
||||
}}
|
||||
onDragEnter={(event) => {
|
||||
event.preventDefault();
|
||||
onDraggingChange(true);
|
||||
}}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
onDragLeave={() => onDraggingChange(false)}
|
||||
onDrop={onDrop}
|
||||
>
|
||||
<CloudUploadOutlined />
|
||||
<strong>点击或拖拽上传图片</strong>
|
||||
<span>支持 PNG / JPG / WebP,上传含水印图片后点击开始去水印</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="ecom-watermark-grid">
|
||||
<article className="ecom-watermark-preview-card">
|
||||
<span>原图</span>
|
||||
<img src={image.src} alt="原图" />
|
||||
</article>
|
||||
|
||||
<article className="ecom-watermark-preview-card">
|
||||
<span>去水印结果</span>
|
||||
{status === "processing" ? (
|
||||
<div className="ecom-watermark-processing" role="status" aria-live="polite">
|
||||
<LoadingOutlined />
|
||||
<strong>正在去水印</strong>
|
||||
<em>AI 正在清理图片中的水印和文字</em>
|
||||
<div className="ecom-quick-set-progress">
|
||||
<div className="ecom-quick-set-progress-bar" style={{ width: `${Math.round(progress)}%` }} />
|
||||
</div>
|
||||
<em className="ecom-quick-set-progress-text">{Math.round(progress)}%</em>
|
||||
</div>
|
||||
) : status === "done" && resultUrl ? (
|
||||
<>
|
||||
<img src={resultUrl} alt="去水印结果" />
|
||||
<button type="button" className="ecom-watermark-zoom" aria-label="查看大图">
|
||||
<QuestionCircleOutlined />
|
||||
</button>
|
||||
</>
|
||||
) : status === "failed" ? (
|
||||
<div className="ecom-watermark-empty">
|
||||
<FrownOutlined />
|
||||
<strong>去水印失败</strong>
|
||||
<em>请检查网络或重试,如余额不足请先充值</em>
|
||||
</div>
|
||||
) : (
|
||||
<div className="ecom-watermark-empty">
|
||||
<FileImageOutlined />
|
||||
<strong>等待处理</strong>
|
||||
<em>点击开始去水印后显示结果</em>
|
||||
</div>
|
||||
)}
|
||||
<div className="ecom-watermark-actions">
|
||||
<button type="button" onClick={() => toast.success("已加入资产库")} disabled={status !== "done"}>
|
||||
<FolderOpenOutlined />
|
||||
加入资产库
|
||||
</button>
|
||||
<button type="button" onClick={onDownload} disabled={status !== "done"}>
|
||||
<CloudUploadOutlined />
|
||||
下载图片
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user