import React, { useRef, useState } from 'react'; import loadingSvg from '../icons/loading.svg?raw'; import uploadSvg from '../icons/upload.svg?raw'; import type { EditorProps } from './EditorProps'; /** * 图片编辑器组件,用于上传和替换图片。 */ export const ImageEditor: React.FC = ({ element, onClose }) => { const imgElement = element as HTMLImageElement; const [src, setSrc] = useState(imgElement.src); const [isUploading, setIsUploading] = useState(false); const fileInputRef = useRef(null); const [step, setStep] = useState<'upload' | 'preview' | 'complete'>('upload'); const [previewSrc, setPreviewSrc] = useState(null); const [error, setError] = useState(null); const maxFileSizeMB = window.ENV.MAX_UPLOAD_SIZE_MB || 5; const maxFileSize = maxFileSizeMB * 1024 * 1024; const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { setIsUploading(true); setError(null); if (file.size > maxFileSize) { const maxSizeMB = Math.round(maxFileSize / (1024 * 1024)); setError(`文件大小超过限制,最大允许${maxSizeMB}MB`); setIsUploading(false); return; } const reader = new FileReader(); reader.onload = (event) => { if (event.target?.result) { setPreviewSrc(event.target.result as string); setStep('preview'); setIsUploading(false); } }; reader.onerror = () => { console.error('文件读取失败'); setError('文件读取失败'); setIsUploading(false); }; reader.readAsDataURL(file); } }; const triggerFileInput = () => { fileInputRef.current?.click(); }; const handleConfirm = async () => { if (previewSrc) { setIsUploading(true); try { // 从base64 src 中提取文件数据 const base64Data = previewSrc.split(',')[1]; const byteCharacters = atob(base64Data); const byteArrays = []; for (let i = 0; i < byteCharacters.length; i++) { byteArrays.push(byteCharacters.charCodeAt(i)); } const byteArray = new Uint8Array(byteArrays); const blob = new Blob([byteArray], { type: 'image/png' }); if (blob.size > maxFileSize) { const maxSizeMB = Math.round(maxFileSize / (1024 * 1024)); throw new Error(`文件大小超过限制,最大允许${maxSizeMB}MB`); } const fileName = `image_${Date.now()}.png`; const file = new File([blob], fileName, { type: 'image/png' }); const formData = new FormData(); formData.append('file', file); const response = await fetch('/api/upload', { method: 'POST', body: formData, }); const result = await response.json(); if (!response.ok) { throw new Error(result.message || '上传失败'); } imgElement.src = result.data.url; setSrc(result.data.url); setStep('complete'); onClose(); setTimeout(() => { setStep('upload'); setPreviewSrc(null); }, 1500); } catch (error) { console.error('文件上传失败', error); setError(error instanceof Error ? error.message : '文件上传失败'); setIsUploading(false); } } }; const handleCancel = () => { setStep('upload'); setPreviewSrc(null); setError(null); }; return (
{step === 'upload' && (
{isUploading ? (

正在上传图片...

) : ( <>

点击或拖拽图片到此处上传

{error &&

{error}

} )}
)} {step === 'preview' && previewSrc && (
预览图片

原图

原图

新图

新图
{error &&

{error}

}
)}
); };