77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
|
|
import { useEffect } from "react";
|
||
|
|
import { createPortal } from "react-dom";
|
||
|
|
import { CloseOutlined, DeleteOutlined, DownloadOutlined } from "@ant-design/icons";
|
||
|
|
|
||
|
|
export interface ProductSetPreviewSelection {
|
||
|
|
src: string;
|
||
|
|
label: string;
|
||
|
|
nodeId?: string;
|
||
|
|
cardId?: string;
|
||
|
|
removable?: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface ProductSetPreviewModalProps {
|
||
|
|
preview: ProductSetPreviewSelection | null;
|
||
|
|
onClose: () => void;
|
||
|
|
onDownload: (preview: ProductSetPreviewSelection) => void;
|
||
|
|
onRemove: (preview: ProductSetPreviewSelection) => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 商品套图大图预览弹窗。通过 portal 挂到 body,支持下载/移除。
|
||
|
|
export default function ProductSetPreviewModal({ preview, onClose, onDownload, onRemove }: ProductSetPreviewModalProps) {
|
||
|
|
// Esc 关闭
|
||
|
|
useEffect(() => {
|
||
|
|
if (!preview) return;
|
||
|
|
const handleKey = (event: KeyboardEvent) => {
|
||
|
|
if (event.key === "Escape") onClose();
|
||
|
|
};
|
||
|
|
window.addEventListener("keydown", handleKey);
|
||
|
|
return () => window.removeEventListener("keydown", handleKey);
|
||
|
|
}, [preview, onClose]);
|
||
|
|
|
||
|
|
if (!preview || typeof document === "undefined") return null;
|
||
|
|
|
||
|
|
return createPortal(
|
||
|
|
<div className="product-set-preview-backdrop" role="presentation" onClick={onClose}>
|
||
|
|
<section
|
||
|
|
className="product-set-preview-modal"
|
||
|
|
role="dialog"
|
||
|
|
aria-modal="true"
|
||
|
|
aria-label={preview.label}
|
||
|
|
onClick={(event) => event.stopPropagation()}
|
||
|
|
>
|
||
|
|
<button type="button" className="product-set-preview-close" onClick={onClose} aria-label="关闭预览">
|
||
|
|
<CloseOutlined />
|
||
|
|
</button>
|
||
|
|
<img src={preview.src} alt={preview.label} />
|
||
|
|
<div className="product-set-preview-footer">
|
||
|
|
<strong>{preview.label}</strong>
|
||
|
|
<div className="product-set-preview-actions" aria-label="图片操作">
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
className="product-set-preview-action"
|
||
|
|
onClick={() => {
|
||
|
|
onDownload(preview);
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<DownloadOutlined />
|
||
|
|
<span>下载</span>
|
||
|
|
</button>
|
||
|
|
{preview.removable ? (
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
className="product-set-preview-action product-set-preview-action--danger"
|
||
|
|
onClick={() => onRemove(preview)}
|
||
|
|
>
|
||
|
|
<DeleteOutlined />
|
||
|
|
<span>移除</span>
|
||
|
|
</button>
|
||
|
|
) : null}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
</div>,
|
||
|
|
document.body,
|
||
|
|
);
|
||
|
|
}
|