优化拖拽

This commit is contained in:
yml2213
2026-08-16 14:38:10 +08:00
parent 4dd94cde27
commit c4e7e67115
2 changed files with 134 additions and 10 deletions
@@ -1,7 +1,7 @@
import { PlusOutlined } from '@ant-design/icons'
import { CloudUploadOutlined, PlusOutlined } from '@ant-design/icons'
import { App, Typography, Upload } from 'antd'
import type { UploadFile, UploadProps } from 'antd'
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'
import type { ClipboardEvent, DragEvent, FocusEvent, MouseEvent } from 'react'
import { uploadFile } from '@/services/files'
@@ -23,6 +23,8 @@ type GlobalPasteTarget = {
uploadImages: (files: File[]) => void
}
type UploadSource = 'default' | 'drag'
let nextTargetId = 1
const globalPasteTargets: GlobalPasteTarget[] = []
let globalPasteListenerAttached = false
@@ -62,6 +64,8 @@ export default function ImageUpload({
captureGlobalPaste = false,
}: ImageUploadProps) {
const { message } = App.useApp()
const [isDragging, setIsDragging] = useState(false)
const dragCounterRef = useRef(0)
const targetIdRef = useRef(0)
const valueRef = useRef(value)
valueRef.current = value
@@ -74,7 +78,10 @@ export default function ImageUpload({
thumbUrl: file.thumbnailUrl || file.url,
}))
async function uploadImages(images: File[]) {
async function uploadImages(
images: File[],
source: UploadSource = 'default',
) {
const remaining = maxCount - valueRef.current.length
if (remaining <= 0) {
message.warning(`最多上传 ${maxCount} 张图片`)
@@ -86,7 +93,7 @@ export default function ImageUpload({
const uploaded = await uploadFile(file, scene, scope)
next.push(uploaded)
} catch (error) {
message.error(error instanceof Error ? error.message : '上传失败')
message.error(resolveUploadErrorMessage(error, source))
}
}
if (next.length > valueRef.current.length) {
@@ -95,10 +102,13 @@ export default function ImageUpload({
}
}
async function uploadFiles(files: File[]) {
async function uploadFiles(
files: File[],
source: UploadSource = 'default',
) {
const images = files.filter((file) => file.type.startsWith('image/'))
if (images.length === 0) return
await uploadImages(images)
await uploadImages(images, source)
}
function handlePaste(event: ClipboardEvent<HTMLDivElement>) {
@@ -108,10 +118,42 @@ export default function ImageUpload({
void uploadFiles(files)
}
function handleDragEnter(event: DragEvent<HTMLDivElement>) {
event.preventDefault()
event.stopPropagation()
dragCounterRef.current += 1
if (event.dataTransfer?.types?.includes('Files')) {
setIsDragging(true)
}
}
function handleDragOver(event: DragEvent<HTMLDivElement>) {
event.preventDefault()
event.stopPropagation()
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'copy'
}
}
function handleDragLeave(event: DragEvent<HTMLDivElement>) {
event.preventDefault()
event.stopPropagation()
dragCounterRef.current -= 1
if (dragCounterRef.current <= 0) {
dragCounterRef.current = 0
setIsDragging(false)
}
}
function handleDrop(event: DragEvent<HTMLDivElement>) {
event.preventDefault()
event.stopPropagation()
dragCounterRef.current = 0
setIsDragging(false)
const files = Array.from(event.dataTransfer?.files || [])
if (files.length > 0) void uploadFiles(files)
if (files.length > 0) {
void uploadFiles(files, 'drag')
}
}
function activateGlobalPasteTarget() {
@@ -157,13 +199,22 @@ export default function ImageUpload({
return (
<div
className="image-upload-droppable"
className={`image-upload-droppable ${isDragging ? 'is-dragging' : ''}`}
onPaste={handlePaste}
onDrop={handleDrop}
onDragOver={(event) => event.preventDefault()}
onDropCapture={handleDrop}
onDragEnterCapture={handleDragEnter}
onDragOverCapture={handleDragOver}
onDragLeaveCapture={handleDragLeave}
onFocusCapture={handleWrapperFocus}
onClickCapture={handleWrapperClick}
>
{isDragging && (
<div className="image-upload-drag-overlay">
<CloudUploadOutlined className="image-upload-drag-icon" />
<span className="image-upload-drag-text"></span>
<span className="image-upload-drag-subtext"></span>
</div>
)}
<Upload
accept="image/jpeg,image/png,image/webp"
multiple
@@ -196,3 +247,11 @@ export default function ImageUpload({
</div>
)
}
function resolveUploadErrorMessage(error: unknown, source: UploadSource) {
const errorMessage = error instanceof Error ? error.message : '上传失败'
if (source === 'drag' && /网络连接错误|Network Error/i.test(errorMessage)) {
return '拖拽图片读取失败,请复制图片后粘贴,或保存到本地后点击上传'
}
return errorMessage
}