Antd Dragger 从剪贴板粘贴图像

问题描述 投票:0回答:1

我使用Antd+Mobx,我想修改Dragger,以便它从剪贴板接收文件。主面板有图像粘贴处理程序,可将新文件添加到存储中的文件列表中。 最后,拖动的文件和对话框中的文件成功加载,但剪贴板中的文件仅添加到fileList中,未触发加载(未调用onChange)。我如何触发它以加载到服务器?

此图像从剪贴板处理,但未发送到服务器

主面板:

    const MathNewTaskPanel: React.FC<NewTaskProps> = inject('newTaskProps')(observer((props: NewTaskProps) => {

    ...

    const handlePaste = (event: ClipboardEvent<HTMLDivElement>) => {   

        if (event.clipboardData.files.length) {
            const ff = event.clipboardData.files[0];

            const file: UploadFile = {
                name: ff.name,
                size: ff.size,
                type: ff.type,
                uid: uuid(),
                originFileObj: ff
            };
            
            //main panel has two draggers. Only one is active at once
            photoUploaderStore.uploader.setFile(file);
            solutionUploaderStore.uploader.setFile(file);
        }
    };

    return (
        <div onPaste={handlePaste}>
           ...
        </div>
    );
}));

修改拖动器:

const ImageUploader: React.FC<IProps> = inject('uploader')(observer((props: IProps) => {

    const antUploaderProps = {
        name: 'file',
        multiple: true,
        listType: 'picture' as UploadListType,
        action: props.uploader.url,
        progress: 'line' as ProgressProps,
        fileList: props.uploader.files, 
        disabled: props.uploader.inactive,
        onChange(info: UploadChangeParam<UploadFile<any>>) {
            props.uploader.handleOnChange(info);
        }
    };

    return (
        <div> 
            <Dragger className={props.uploader.inactive ? "inactive-element" : ""} {...antUploaderProps}>
                <p className="ant-upload-drag-icon"><InboxOutlined /></p>
            </Dragger>
        </div>
    );
}));

拖拉机商店:

export default class ImageUploaderStore {

    @observable
    inactive: boolean;

    @observable
    taskId: number|null;

    @observable
    url: string;

    @observable
    files: UploadFile[] | [];

    isMainPhoto: boolean;

    subject: Subjects;

    mathTaskService: MathTaskService;

    constructor(subject: Subjects, isMainPhoto: boolean) {
        this.inactive = true;
        this.subject = subject;
        this.taskId = null;
        this.url = "";
        this.mathTaskService = new MathTaskService();
        this.isMainPhoto = isMainPhoto;
        this.files = [];
    }

    @action
    changeActivated(active: boolean) {
        this.inactive = !active;
    }

    @action
    setTaskId(taskId: number) {
        this.taskId = taskId;
        this.url = this.getUrl(taskId);
    }

    @action
    setFile(newFile: UploadFile) {
        if (!this.inactive) {
            this.files = [...this.files, newFile];
        }
    }

    @action
    setFiles(newFiles: UploadFile[]) {
        if (!this.inactive) {
            this.files = newFiles;
        }
    }

    @action
    handleOnChange(info: UploadChangeParam<UploadFile<any>>) {
        runInAction(() => {
            const { status } = info.file;

            if (status === 'done') {
                message.success(`${info.file.name} is successful loaded`);
            } else if (status === 'error') {
                message.error(`${info.file.name} loading failed`);
            }
            this.files = info.fileList;
        });
    }

    getUrl(taskId: number): string {

        if (this.subject === Subjects.MATH) {
            return this.isMainPhoto 
                ? this.mathTaskService.getPhotoUploadUrl(taskId) 
                : this.mathTaskService.getSolutionPhotoUploadUrl(taskId);
        }

        return "";
    }
}
antd mobx dragger
1个回答
0
投票

我在使用 antd upload 时遇到过类似的情况。在搜索解决方案后,我找到了这个主题,但是他们提供的解决方案并不完美。我花了一些时间让它变得更好。

这是我对这个问题的解决方案: 在我的解决方案中,我在 antd Upload 的输入中调度 drop 事件

首先我们要访问antd的隐藏。他们使用 rc-upload,其中 uploader 是私有方法,并且他们没有任何方法来获取 uploader。我们可以使用 typescript 的“后门”来覆盖它的接口:

...
import { UploadRef } from 'antd/es/upload/Upload'
import RcUpload from 'rc-upload'
const { Dragger } = Upload

interface ExtendedUploadRef<T = any> extends Omit<UploadRef<T>, 'upload'> {
  upload: Omit<RcUpload, 'uploader'> & {
    uploader: any
  }
}

const uploadRef = useRef<ExtendedUploadRef<any> | null>(null)

这个引用应该传递给 antd 组件,但 typescript 不会喜欢它,这就是为什么我们将它传递为

React.RefObject<UploadRef<any>>
:

<Dragger
  className="chat_file_upload_dragger"
  {...draggerProps}
  ref={uploadRef as React.RefObject<UploadRef<any>>}
>
  <div className="chat_file_upload_middle">
    <p className="ant-upload-drag-icon">
      <FaFileUpload />
    </p>
    <p className="ant-upload-text">
      {t('Click or drag file to this area to upload')}
    </p>
  </div>
</Dragger>

现在我们可以进入最有趣的部分,首先我们必须处理过去的事件,为此我使用了在项目中某处声明的可重用自定义挂钩:

import { useEffect, useRef } from "react"

export default function useEventListener(
  eventType: any,
  callback: any,
  element = window
) {
  const callbackRef = useRef(callback)

  useEffect(() => {
    callbackRef.current = callback
  }, [callback])

  useEffect(() => {
    if (element == null) return
    const handler = (e: any) => callbackRef.current(e)
    element.addEventListener(eventType, handler)

    return () => element.removeEventListener(eventType, handler)
  }, [eventType, element])
}

然后我们用 antd Upload 告诉我们的组件监听这个事件:

useEventListener('paste', handlePasteFiles)

handlePasteFiles 函数中发生的所有魔法如下:

function handlePasteFiles(e: ClipboardEvent) {

  const items = e.clipboardData?.items

  if (!items) return 
  const arrItems = Array.from(items)
  if (arrItems.every((item) => item.kind !== 'file')) return
  e.preventDefault() //to not paste file path if focused in some input text
  const fileList = new DataTransfer()
  arrItems.forEach((item) => {
    const file = item.getAsFile()
    if (!file) return
    fileList.items.add(file)
  })

  if (fileList.items.length > 0) {
    const dropEvent = new DragEvent('drop', {
      dataTransfer: fileList,
      bubbles: true,
      cancelable: true,
    })
    uploadRef.current?.upload?.uploader.fileInput.dispatchEvent(dropEvent)
  }
}

因此,当文件被拖放到其区域时,antd 会处理从剪贴板粘贴的文件

© www.soinside.com 2019 - 2024. All rights reserved.