diff --git a/apps/frontend/src/components/files/ImageUpload.tsx b/apps/frontend/src/components/files/ImageUpload.tsx index eea6cf56..4beb6c8d 100644 --- a/apps/frontend/src/components/files/ImageUpload.tsx +++ b/apps/frontend/src/components/files/ImageUpload.tsx @@ -1,6 +1,7 @@ import { PlusOutlined } from '@ant-design/icons' -import { App, Upload } from 'antd' +import { App, Typography, Upload } from 'antd' import type { UploadFile, UploadProps } from 'antd' +import type { ClipboardEvent, DragEvent } from 'react' import { uploadFile } from '@/services/files' import type { UploadedFile } from '@/types/worker-platform' @@ -11,6 +12,7 @@ type ImageUploadProps = { scene: string scope: 'admin' | 'worker' | 'collect' maxCount?: number + showPasteHint?: boolean } export default function ImageUpload({ @@ -19,6 +21,7 @@ export default function ImageUpload({ scene, scope, maxCount = 6, + showPasteHint = false, }: ImageUploadProps) { const { message } = App.useApp() const fileList: UploadFile[] = value.map((file) => ({ @@ -29,6 +32,39 @@ export default function ImageUpload({ thumbUrl: file.thumbnailUrl || file.url, })) + async function uploadFiles(files: File[]) { + const images = files.filter((file) => file.type.startsWith('image/')) + if (images.length === 0) return + const remaining = maxCount - value.length + if (remaining <= 0) { + message.warning(`最多上传 ${maxCount} 张图片`) + return + } + const next = [...value] + for (const file of images.slice(0, remaining)) { + try { + const uploaded = await uploadFile(file, scene, scope) + next.push(uploaded) + } catch (error) { + message.error(error instanceof Error ? error.message : '上传失败') + } + } + if (next.length > value.length) onChange?.(next) + } + + function handlePaste(event: ClipboardEvent) { + const files = Array.from(event.clipboardData?.files || []) + if (files.length === 0) return + event.preventDefault() + void uploadFiles(files) + } + + function handleDrop(event: DragEvent) { + event.preventDefault() + const files = Array.from(event.dataTransfer?.files || []) + if (files.length > 0) void uploadFiles(files) + } + const customRequest: UploadProps['customRequest'] = async (options) => { try { const uploaded = await uploadFile(options.file as File, scene, scope) @@ -41,26 +77,38 @@ export default function ImageUpload({ } return ( - { - onChange?.( - value.filter((item) => (item.objectKey || item.url) !== file.uid), - ) - return true - }} - showUploadList={{ showPreviewIcon: false }} +
event.preventDefault()} > - {value.length >= maxCount ? null : ( - - )} - + { + onChange?.( + value.filter((item) => (item.objectKey || item.url) !== file.uid), + ) + return true + }} + showUploadList={{ showPreviewIcon: false }} + > + {value.length >= maxCount ? null : ( + + )} + + {showPasteHint ? ( + + 支持点击选择、粘贴剪贴板截图或拖拽图片上传 + + ) : null} +
) } diff --git a/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx b/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx index ec584e10..27c87f0d 100644 --- a/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx +++ b/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx @@ -571,6 +571,7 @@ export default function WorkerOrdersPage() { value={acceptanceFiles} onChange={setAcceptanceFiles} maxCount={6} + showPasteHint /> diff --git a/apps/frontend/src/styles/main.css b/apps/frontend/src/styles/main.css index 13acb14e..baf67194 100644 --- a/apps/frontend/src/styles/main.css +++ b/apps/frontend/src/styles/main.css @@ -1888,6 +1888,17 @@ select { font-size: 12px; } +.image-upload-droppable { + display: grid; + gap: 8px; + justify-items: start; +} + +.image-upload-hint { + font-size: 12px; + line-height: 1.5; +} + .image-preview-list { display: flex; flex-wrap: wrap;