Upload 上传

文件上传组件。

何时使用

当需要将文件上传到后端服务器时。

基本用法

选择文件
<template>
  <d-upload v-model="uploadedFiles" :upload-options="uploadOptions" />
</template>
<script>
import { ref } from 'vue';

export default {
  setup() {
    const uploadedFiles = ref([]);
    const uploadOptions = ref({
      uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0',
    });

    return {
      uploadedFiles,
      uploadOptions,
    };
  },
};
</script>

多文件上传

使用multiple属性可以支持多文件上传,同时使用limit属性设置文件数的上限,当上传数超过限制时会触发on-exceed钩子函数。

选择文件
<template>
  <d-upload
    v-model="uploadedFiles"
    :upload-options="uploadOptions"
    multiple
    :limit="2"
    :on-exceed="handleExceed"
    :on-change="handleChange"
    :on-preview="onPreview"
  />
</template>
<script>
import { ref } from 'vue';

export default {
  setup() {
    const uploadedFiles = ref([]);
    const uploadOptions = ref({
      uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0',
    });
    const handleExceed = (files, uploadFiles) => {
      console.log('onExceed:', files);
      console.log('onExceed:', uploadFiles);
      throw new Error('文件数上限为 2');
    };
    const handleChange = (files, uploadFiles) => {
      console.log('onChange:', files);
      console.log('onChange:', uploadFiles);
    };

    const onPreview = (file) => {
      console.log('previewFile: ', file);
    };
    return {
      uploadedFiles,
      uploadOptions,
      handleExceed,
      handleChange,
      onPreview,
    };
  },
};
</script>

拖动文件上传

选择文件
<template>
  <d-upload v-model="uploadedFiles" :upload-options="uploadOptions" droppable />
</template>
<script>
import { ref } from 'vue';

export default {
  setup() {
    const uploadedFiles = ref([]);
    const uploadOptions = ref({
      uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0',
    });

    return {
      uploadedFiles,
      uploadOptions,
    };
  },
};
</script>

手动上传和清空

通过设置auto-upload为 false 可阻止选择文件后立刻上传的行为,然后通过submit方法进行手动上传。 使用clearFiles方法可以清空已上传和已选择的文件。

选择文件
<template>
  <div>
    <d-upload ref="demoUpload" multiple v-model="uploadedFiles" :upload-options="uploadOptions" :auto-upload="false" />
    <d-button @click="submit" style="margin-top: 20px">手动上传</d-button>
    <d-button @click="clear" style="margin: 20px 0px 0px 10px;">清空文件</d-button>
  </div>
</template>
<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const uploadedFiles = ref([]);
    const uploadOptions = ref({
      uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0',
    });
    const demoUpload = ref(null);
    const submit = () => {
      demoUpload.value.submit();
    };
    const clear = () => {
      demoUpload.value.clearFiles();
    };

    return {
      uploadedFiles,
      uploadOptions,
      demoUpload,
      submit,
      clear,
    };
  },
});
</script>

禁止上传

选择文件
<template>
  <d-upload v-model="uploadedFiles" :upload-options="uploadOptions" disabled />
</template>
<script>
import { ref } from 'vue';

export default {
  setup() {
    const uploadedFiles = ref([]);
    const uploadOptions = ref({
      uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0',
    });

    return {
      uploadedFiles,
      uploadOptions,
    };
  },
};
</script>

任意区域上传

将文件拖到此处,或 点击上传
food.jpegUploaded
food2.jpegUploaded
用户可通过默认 slot 支持文件任意区域上传。
<template>
  <d-upload
    class="upload-demo"
    accept=".png"
    v-model="uploadedFiles"
    :upload-options="uploadOptions"
    droppable
    :on-success="onSuccess"
    :on-error="onError"
    :on-progress="onProgress"
    :before-upload="beforeUpload"
    @file-over="fileOver"
    @file-drop="fileDrop"
    @file-select="fileSelect"
    @delete-uploaded-file="deleteUploadedFile"
  >
    <div class="upload-trigger">
      <div><d-icon name="upload" size="24px"></d-icon></div>
      <div style="margin-top: 20px;">
        将文件拖到此处,或
        <span class="link">点击上传</span>
      </div>
    </div>
    <template v-slot:uploaded-files="slotProps">
      <table class="table uploaded-files" v-if="slotProps.uploadedFiles.length > 0">
        <tbody>
          <tr v-for="(uploadedFile, index) in slotProps.uploadedFiles" :key="index" class="row">
            <td width="50%">
              <span>{{ uploadedFile.name }}</span>
            </td>
            <td width="25%">
              <span>Uploaded</span>
            </td>
            <td>
              <d-button size="xs" @click="(event) => slotProps.deleteFile(uploadedFile)">Delete</d-button>
            </td>
          </tr>
        </tbody>
      </table>
    </template>
  </d-upload>
</template>
<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const uploadedFiles = ref([
      {
        name: 'food.jpeg',
        url: 'https://devui.design/home',
      },
      {
        name: 'food2.jpeg',
        url: 'https://devui.design/home',
      },
    ]);

    const uploadOptions = ref({
      uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0',
    });

    const beforeUpload = (file) => {
      console.log(file);
      return true;
    };

    const onSuccess = (result) => {
      console.log('success', result);
    };

    const onError = () => {
      console.log(error);
    };

    const onProgress = (selectFile, uploadedFiles) => {
      console.log('selectFile: ', selectFile);
      console.log('uploadedFiles: ', uploadedFiles);
    };

    const fileOver = (fileInfo) => {
      console.log('fileInfo:', fileInfo);
    };

    const fileDrop = (fileInfo) => {
      console.log('fileInfo:', fileInfo);
    };

    const fileSelect = (fileInfo) => {
      console.log('fileInfo:', fileInfo);
    };

    const deleteUploadedFile = (fileInfo) => {
      console.log('fileInfo:', fileInfo);
    };

    watch(uploadedFiles, (newValue, oldValue) => {
      console.log('uploadedFiles', {
        newValue,
        oldValue,
      });
    });

    return {
      uploadedFiles,
      uploadOptions,
      beforeUpload,
      onSuccess,
      onError,
      onProgress,
      fileOver,
      fileDrop,
      fileSelect,
      deleteUploadedFile,
    };
  },
};
</script>

<style>
.upload-demo .upload-trigger {
  background-color: #fff;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  box-sizing: border-box;
  width: 360px;
  height: 180px;
  text-align: center;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.upload-demo .upload-trigger .link {
  color: #5e7ce0;
}
</style>

图片上传示例

<template>
  <div class="img-container" v-if="!showUpload">
    <img class="img-preview" />
    <div class="delete-img" @click="handleDel">
      <d-icon name="delete" size="14px"></d-icon>
    </div>
  </div>
  <d-upload class="upload-demo-new" accept=".png" :upload-options="uploadOptions" :before-upload="beforeUpload" v-if="showUpload">
    <div class="upload-trigger">
      <d-icon name="add" size="24px"></d-icon>
    </div>
  </d-upload>
</template>
<script>
import { ref, watch, nextTick } from 'vue';

export default {
  setup() {
    const showUpload = ref(true);
    const uploadOptions = ref({
      uri: 'https://run.mocky.io/v3/132b3ea3-23ea-436b-aed4-c43ef9d116f0',
    });

    const beforeUpload = (file) => {
      const reader = new FileReader();
      reader.onload = async () => {
        showUpload.value = false;
        await nextTick();
        document.getElementsByClassName('img-preview')[0].src = reader.result;
      };
      reader.readAsDataURL(file[0].file);
      return false;
    };

    const handleDel = () => {
      showUpload.value = true;
    };

    return {
      uploadOptions,
      beforeUpload,
      showUpload,
      handleDel,
    };
  },
};
</script>

<style>
.upload-demo-new .upload-trigger {
  background-color: #fff;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  box-sizing: border-box;
  width: 180px;
  height: 180px;
  text-align: center;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.upload-demo .upload-trigger .link {
  color: #5e7ce0;
}
.img-container {
  position: relative;
}
.img-preview {
  width: 180px;
  height: 180px;
  border-radius: 6px;
}
.delete-img {
  width: 24px;
  height: 24px;
  background-color: #f7f7f9;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: -12px;
  left: 168px;
}
</style>

Upload 参数

参数名类型默认说明跳转 Demo
acceptstring-可选,规定能够通过文件上传进行提交的文件类型,
例如 accept: '.xls,.xlsx,.png'
任意区域上传
before-uploadboolean Promise<boolean> -可选,上传前的回调,通过返回true or false
控制文件是否上传,参数为文件信息及上传配置
任意区域上传
disabledbooleanfalse可选,是否禁用上传组件禁止上传
droppablebooleanfalse可选,是否支持拖拽拖动文件上传
multiplebooleanfalse可选,是否支持多选文件多文件上传
upload-optionsIUploadOptions-可选,上传配置基本用法
uploaded-filesArray<Object>-可选,获取已上传的文件列表基本用法
on-successfunction([{file, response}])-可选,文件上传成功时的钩子任意区域上传
on-errorfunction({file, response})-可选,文件上传失败时的钩子任意区域上传
on-exceedfunction(files, uploadFiles)-可选,文件上传数超出限制时的钩子多文件上传
on-changefunction(files, uploadFiles)-可选,文件状态改变时的钩子,添加文件、上传文件成功和上传文件失败时都会被调用多文件上传
on-progressfunction(files, uploadFiles)-可选,文件上传时的钩子任意区域上传
on-previewfunction(file)-可选,点击文件列表中的文件时的钩子多文件上传
limitnumber-可选,允许上传文件的最大数量多文件上传
auto-uploadbooleantrue可选,是否在选取文件后立即上传手动上传和清空
http-requestfunction(files)-可选,覆盖默认的上传行为,files 为选择的文件对象列表-

Upload 事件

事件名类型说明跳转 Demo
delete-uploaded-file(v: string) => void删除上传文件的回调函数,返回删除文件的路径信息任意区域上传
file-drop(v: any) => void支持拖拽上传时,当前拖拽的文件列表回调,
单文件上传默认返回第一个文件
任意区域上传
file-over(v: boolean) => void支持拖拽上传时,文件移动到可拖放区域触发事件,
可拖动的元素移出放置目标时返回false
元素正在拖动到放置目标时返回true
任意区域上传
file-select(v: File) => void文件选择后的回调函数,返回已选择文件信息任意区域上传

Upload 方法

方法名说明跳转 Demo
submit用于手动上传选择的文件手动上传和清空
clearFiles用于清空已上传和已选择的文件列表(不支持在 before-upload 中调用)手动上传和清空

Upload 插槽

插槽名默认说明跳转 Demo
default--自定义默认内容任意区域上传
uploaded-files--可选,用于创建自定义已上传文件列表模板,参数为 {uploadedFiles, deleteFile}任意区域上传

Uplaod 类型定义

IUploadOptions

export class IUploadOptions {
  // 上传接口地址
  uri: string;
  // http 请求方法
  method?: string;
  // 上传文件大小限制
  maximumSize?: number;
  // 自定义请求headers
  headers?: { [key: string]: any };
  // 认证token
  authToken?: string;
  // 认证token header标示
  authTokenHeader?: string;
  // 上传额外自定义参数
  additionalParameter?: { [key: string]: any };
  // 上传文件字段名称,默认file
  fileFieldName?: string;
  // 多文件上传,是否检查文件重名,设置为true,重名文件不会覆盖,否则会覆盖上传
  checkSameName?: boolean;
  // 指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求
  withCredentials?: boolean;
  //  手动设置返回数据类型
  responseType?: 'arraybuffer' | 'blob' | 'json' | 'text';
}
Contributors