Package the upload component based on Vue + Axios and support dragging and dropping files

Time:2021-9-15

1、 Input file

use   type=’file’  of Element can select files. Based on this, we can encapsulate custom upload components

  Four additional attributes can be received:

accept:String, which allows you to select file types, such as:  “.jpg,.jpeg,.png,.gif” 

multiple:Boolean value, whether multiple selections are allowed

capture: string, which can call the default camera or recorder of the device (valid at the mobile terminal)

files:Selected file object list, via  HTMLInputElement.files  Get and assign

 

but  It contains two parts: button and file list. If you add width, height and background color to it

.upload-inner {
      width: 200px;
      height: 80px;
      background-color: #e3e3e3;
  }

The style of this label is almost hopeless

Therefore, in order to use more beautiful upload controls, you usually choose to hide the real file upload controls

Then use other tags instead of the upload button to trigger the upload event in the click event

 

2、 Control design

This is a common upload control style. Its HTML can be designed as follows:

Click the upload button or drag the file into the box to upload
      Please select a file no larger than 10m

The top is outerComponent. The specific logic of the upload control can be encapsulated separately into  In component

Select file

stayIn the component, you need toControl is hidden and

<span style="background-color: rgba(245, 245, 245, 1); color: rgba(128, 0, 0, 1)">
.file-selector </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">{</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
  margin-bottom</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 16px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">

  .selector-btn {
    background-color</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> #2976e6</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    color</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> #fff</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    padding</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 6px 18px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    border-radius</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> 4px</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    cursor</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> pointer</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">

    &</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">hover {
      background-color: rgba($color: #2976e6, $alpha: 0.8)</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
      transition</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> background 180ms</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span>
    <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">}</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(128, 0, 0, 1)">
  }

  &-input </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">{</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(255, 0, 0, 1)">
    display</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">:</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)"> none</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">;</span>
  <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">}</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(128, 0, 0, 1)">
}
</span>

Then pass  

And monitorTo process the selected file (above)handleFiles

//Call upload function
handleUpClick() {
  This.uploadfinished // maintain an upload status. Disable the upload button during uploading
    ? this.$refs.input.click()
    : console.warn ('Please wait until all the current files are uploaded ');
},
handleFiles(e) {
  const files = e?.target?.files;
  this.readFiles(files);
},

 

3、 Processing files

The above hanldefiles calls the readfiles method, which is the main logic for processing files

//Process files as objects before uploading
readFiles(files) {
  if (!files || files.length <= 0) {
    return;
  }
  for (const file of files) {
    const url = window.URL.createObjectURL(file);
    const obj = {
      Title: file.name.replace (/ (\. [^ \.] * $) | [\] / GI, '') // remove the file suffix
      url,
      file,
      fileType: file.fileType,
      Status: 0, // status - > 0 waiting, 1 completed, 2 uploading, 3 uploading failed
      Percent: 0, // upload progress
    };
    //Define a list in data in advance to save the files to be uploaded
    this.list.push(obj);
  }
  //The initial value of StartIndex defined in data is 0, which is updated after uploading and used to append uploaded files
  this.startUpload(this.startIndex);
},

Then upload the file

//Files need to be verified before uploading
checkFile(index) {
  const file = this.list[index];

  //If the file does not exist, all files are uploaded
  if (!file) {
    this.uploadFinished = true; // after uploading, throw a success event to the parent component
    this.$emit('success', this.list);
    //Clear the value in the upload control to ensure that the change event can be triggered normally
    this.$refs.input.value = null;    this.startIndex = index > 1 ? index - 1 : 0;
    return false;
  }

  //Verify whether it has been uploaded
  if (`${file.status}` === "1") {
    this.startUpload(++index);
    return false;
  }

  //Verify file size
  if (this.maxSize && file.file && file.file.size >= this.maxSize) {
    this.startUpload(++index);
    return false;
  }

  return true;
}, // upload a single file
startUpload(index) {
  if (!this.checkFile(index)) { return; }

  //Start uploading and maintain the upload status
  this.uploadFinished = false;
  const file = this.list[index].file;
  const fileObj = this.list[index];

  //Create formdata for submission
  const data = new FormData();
  data.append('userfile', file);
  axios({
    URL: this.url, // upload interface, passed in by props
    method: 'post',
    data,
    withCredentials: true,
    Canceltoken: this. Source. Token, // used to cancel the interface request
    //Progress bar
    onUploadProgress: e => {
      If (fileobj. Status = = = 1) {return;} // Uploaded
      //Limit the maximum value to 99%
      const p = parseInt((e.loaded / e.total) * 99);
      if (e.total) {
        fileObj.status = 2; //  Uploading
        fileObj.percent = p; //  Update upload progress
      } else {
        fileObj.status = 3; //  Upload failed
      }
    },
  })
    .then(response => {
      if (`${response.code}` === "200") {
        fileObj.status = 1;
        fileObj.percent = 100;
      } else {
        fileObj.status = 3;
      }
    })
    .catch(e => {
      fileObj.status = 3;
    })
    .finally(e => {
      this.startUpload(++index);
    });
  //Upload complete
},

Used when uploadingaxiosof  onUploadProgressTo maintain the upload progress and the upload status of the current file, which is used to develop the styles in the upload

 

Also used   Canceltoken to cancel the upload, but to cancel the upload, you need to define a source variable in data in advance

Source: Axios. Canceltoken. Source(), // Axios cancels the request

So eventuallyThe data in the component is:

data: () => ({
    List: [], // selected file object
    Uploadfinished: true, // upload status
    StartIndex: 0, // the subscript to start uploading, which is used to append files
    Source: Axios. Canceltoken. Source(), // Axios cancels the request
}),

 

Finally, add a reset method to the component

Reset() {// reset
    this.list = [];
    this.source.cancel();
    this.startIndex = 0;
    this.uploadFinished = true;
    this.$refs.input && (this.$refs.input.value = null);
 },

The core logic of the upload component is completed

 

4、 Drag upload

In addition to the basic click button upload, you also need to support drag upload. This part of logic can be found inComplete in component

Let’s first review the HTML structure of the component

ref="uploadBtn"
        :accept="uploadConfig.accept"
      >
      Click the upload button or drag the file into the box to upload
      Please select a file no larger than 10m

One is added above  draggingVariable, which is used to control the style when dragging files. It needs to be defined in data

When the component is initialized, you need to initialize Ref=“pickerAreaBind drag event

In the process of dragging, through maintenancedraggingState to update hot zone style. When drag and drop ends, call.Component upload function

bindEvents() {
  const dropbox = this.$refs.pickerArea;
  //To prevent repeated binding events, you need to initialize binddrop in data to false
  if (!dropbox || this.bindDrop) { return }
  //Bind drag event, unbind when component is destroyed
  dropbox.addEventListener("drop", this.handleDrop, false);
  dropbox.addEventListener("dragleave", this.handleDragLeave);
  dropbox.addEventListener("dragover", this.handleDragOver);
  this.bindDrop = true;
},
//Drag to upload area
handleDragOver(e) {
  e.stopPropagation();
  e.preventDefault();
  this.dragging = true;
},
//Leave the upload area
handleDragLeave(e) {
  e.stopPropagation();
  e.preventDefault();
  this.dragging = false;
},
//Drag end
handleDrop(e) {
  e.stopPropagation();
  e.preventDefault();
  this.dragging = false;
  const files = e.dataTransfer.files; // call the upload function of the component
  this.$refs.uploadBtn && this.$refs.uploadBtn.readFiles(files);
},

The event listener is added through the addeventlister above, so you need to log out in the beforedestroy life cycle

beforeDestroy() {
  //Unbound drag event before component destruction
  try {
    const dropbox = this.$refs.pickerArea;
    dropbox.removeEventListener("drop", this.handleDrop);
    dropbox.removeEventListener("dragleave", this.handleDragLeave);
    dropbox.removeEventListener("dragover", this.handleDragOver);
    this.bindDrop = false;
  } catch (e) {}
},

Here, an upload component is completed, but only the upload function, and there are still some unfinished points in style and interaction

For example, the style of the hot zone when dragging, and the disabled style of the drag button during uploading. However, these maintain corresponding states, which can be used to add styles

What’s really not involved isSelected file style, including uploaded progress bar, file operation, etc

But up thereThrown in the componentthis.list, each element also maintains the upload status and upload progress, and the corresponding interaction can be developed based on this

 

Finish work~