238 lines
8.5 KiB
TypeScript
238 lines
8.5 KiB
TypeScript
|
|
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>
|
|||
|
|
);
|
|||
|
|
}
|