import { Button, Modal, Slider, Space } from 'antd' import { useCallback, useState } from 'react' import Cropper, { type Area } from 'react-easy-crop' import { blobToFile, cropImageToBlob } from '../utils/cropImage' interface Props { open: boolean imageSrc: string | null filename: string onCancel: () => void onConfirm: (file: File) => void onSkip?: (file: File) => void originalFile?: File | null } export default function ImageCropModal({ open, imageSrc, filename, onCancel, onConfirm, onSkip, originalFile, }: Props) { const [crop, setCrop] = useState({ x: 0, y: 0 }) const [zoom, setZoom] = useState(1) const [croppedArea, setCroppedArea] = useState(null) const [submitting, setSubmitting] = useState(false) const onCropComplete = useCallback((_area: Area, pixels: Area) => { setCroppedArea(pixels) }, []) const handleConfirm = async () => { if (!imageSrc || !croppedArea) return setSubmitting(true) try { const blob = await cropImageToBlob(imageSrc, croppedArea) onConfirm(blobToFile(blob, filename.replace(/\.\w+$/, '') + '.jpg')) } finally { setSubmitting(false) } } const handleSkip = () => { if (originalFile && onSkip) { onSkip(originalFile) } } return ( {onSkip && originalFile && ( )} } >

拖动框选仅保留错题区域,识别会更准确

{imageSrc && ( )}
缩放
) }