Skip to content

Upload 上传 ​

用于把本地文件选择或拖拽到页面并上传到服务端,支持文件列表管理、进度、失败重试和图片预览。

何时使用 ​

  • 需要在表单、详情页或配置面板中让用户提交附件、图片、报表等文件。
  • 需要展示上传进度、限制文件数量与类型,或对失败文件提供重试入口。
  • 需要拖拽、粘贴、文件夹选择等更轻量的收集方式。

接入已有上传方法 ​

customRequest 返回 Promise 时,组件自动接管状态:业务只需调用项目里已经封装好的上传方法,不用手动回调 onSuccess / onError。resolve 即成功,结果落在 file.response;reject 即失败,列表出现重试入口。axios、flyio、fetch 或任何返回 Promise 的方法都可以直接接入。

选择文件
选择文件后观察状态变化
vue
<template>
  <x-upload
    v-model:file-list="fileList"
    :custom-request="({ file }) => uploadApi({ file, orderId: 'SO-20261003' })"
    response-url-key="data.url"
  />
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  import type { UploadFile } from 'x-next';

  const fileList = ref<UploadFile[]>([]);

  // 项目里已封装好的上传方法:返回 Promise 即可,组件自动显示进度、成功与失败
  const uploadApi = ({ file, orderId }: { file: File; orderId: string }) =>
    new Promise<{ code: number; data: { url: string } }>((resolve) => {
      // 实际项目里替换为 axios / flyio / fetch 调用
      window.setTimeout(() => {
        resolve({ code: 0, data: { url: `/files/${orderId}/${encodeURIComponent(file.name)}` } });
      }, 600);
    });
</script>

接入 fetch 时直接返回 Response 即可,组件会读取响应体并判断 HTTP 状态:

ts
const uploadApi = ({ file }: { file: File }) =>
  fetch('/api/upload', { method: 'POST', body: formDataOf(file) });

接入封装好的 axios 实例时返回它的 Promise;axios 成功的响应体(response.data)会成为 file.response:

ts
const uploadApi = ({ file }: { file: File }) =>
  http.post('/api/upload', formDataOf(file)); // -> file.response 即接口响应体

也可以直接用内置请求:<x-upload action="/api/upload" name="file" />。name 是 FormData 的字段名。

响应提取与失败处理 ​

字符串 responseUrlKey 支持 data.url 这样的嵌套路径,也可以传函数自行从 response 取值。方法 reject 后文件进入错误态,列表内点击重试即可再次调用同一个方法;error.message 会作为失败图标的提示,图片卡则会直接显示该文案。

选择文件
vue
<template>
  <x-upload
    response-url-key="data.url"
    :custom-request="uploadApi"
    @error="(file) => pushLog(`${file.name} 上传失败,已提供重试入口`)"
    @success="(file) => pushLog(`${file.name} 上传成功`)"
  />
  <ul>
    <li v-for="(message, index) in logs" :key="index">{{ message }}</li>
  </ul>
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  import type { UploadFile } from 'x-next';

  const logs = ref<string[]>([]);
  const pushLog = (message: string) => {
    logs.value = [message, ...logs.value].slice(0, 4);
  };

  // 服务端失败时直接 reject;组件的 error.message 会显示该文案
  const uploadApi = ({ file }: { file: File }) =>
    new Promise<{ data: { url: string } }>((_resolve, reject) => {
      window.setTimeout(() => reject(new Error('服务端拒绝了该文件')), 600);
    });
</script>

进度与取消 ​

业务方法需要展示进度时,把 signal 与 onProgress 透传给底层请求:signal 在取消、移除或组件卸载时触发,可用于中止 fetch / axios;onProgress 传 0 ~ 1 的百分比,不调用则进度只有 0 与 1 两个状态。

选择文件
上传中点击列表里的取消按钮可中止请求
vue
<template>
  <x-upload
    v-model:file-list="fileList"
    :custom-request="uploadApi"
    response-url-key="data.url"
  />
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const fileList = ref<UploadFile[]>([]);

  const uploadApi = (options: UploadRequestOption) =>
    new Promise<{ data: { url: string } }>((resolve, reject) => {
      let percent = 0;
      const timer = window.setInterval(() => {
        percent += 10;
        if (percent < 100) {
          // onProgress 传 0 ~ 1
          options.onProgress(percent / 100);
          return;
        }
        window.clearInterval(timer);
        resolve({ data: { url: `/files/${encodeURIComponent(options.file.name)}` } });
      }, 200);
      // signal 在取消 / 移除 / 卸载时触发;用 fetch 时可直接传 signal
      options.signal?.addEventListener('abort', () => {
        window.clearInterval(timer);
        reject(new DOMException('Aborted', 'AbortError'));
      });
    });
</script>

多选与数量限制 ​

multiple 开启多选;limit 限制列表数量,超出的文件不会进入列表,而是触发 exceed。

选择文件最多 2 个文件
vue
<template>
  <x-upload
    multiple
    :limit="2"
    tip="最多 2 个文件"
    response-url-key="url"
    :custom-request="mockRequest"
    @exceed="(info) => pushLog(`超出限制:${info.files.map((file) => file.name).join('、')}`)"
  />
  <ul
    ><li v-for="(message, index) in logs" :key="index">{{ message }}</li></ul
  >
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` });
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };

  const logs = ref<string[]>([]);
  const pushLog = (message: string) => {
    logs.value = [message, ...logs.value].slice(0, 5);
  };
</script>

受控文件列表 ​

传入 fileList 后由外部驱动列表;配合 v-model:file-list 可以拿到每一次变更后的完整列表。

选择文件
当前 1 个文件
vue
<template>
  <x-upload v-model:file-list="fileList" response-url-key="url" :custom-request="mockRequest" />
  <x-button size="small" @click="fileList = []">清空列表</x-button>
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  const fileList = ref<UploadFile[]>([
    {
      uid: 'seed-1',
      name: '季度报表.xlsx',
      status: 'done',
      percent: 1,
      url: 'https://example.com/files/report.xlsx',
    },
  ]);

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` });
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };
</script>

拖拽上传 ​

打开 draggable 后整块区域可点击、可拖入;批量收集需同时开启 multiple,tip 用于说明限制。默认插槽会替换整块区域内容。

拖拽文件到此处或点击选择,支持任意类型
支持批量选择,单个文件不超过 20MB
vue
<template>
  <x-upload
    draggable
    multiple
    response-url-key="url"
    :custom-request="mockRequest"
    tip="支持批量选择,单个文件不超过 20MB"
  >
    <div class="upload-demo-drag">
      <strong>拖拽文件到此处</strong>
      <span>或点击选择,支持任意类型</span>
    </div>
  </x-upload>
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` });
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };
</script>

directory 开启后,选择文件夹与拖入文件夹都会递归收集其中的文件,并保留相对路径。

图片列表 ​

listType="picture" 在行内展示缩略图;图片文件在加入列表时自动生成缩略图,也可以直接传 url / thumbUrl。

vue
<template>
  <x-upload
    response-url-key="url"
    :custom-request="mockRequest"
    v-model:file-list="fileList"
    list-type="picture"
    accept="image/*"
    image-loading="lazy"
    multiple
  >
    <template #image="{ file }">
      <img :src="file.thumbUrl || file.url" :alt="file.name" loading="lazy" />
    </template>
  </x-upload>
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  const fileList = ref<UploadFile[]>([
    {
      uid: 'pic-1',
      name: '封面图.png',
      status: 'done',
      percent: 1,
      type: 'image/png',
      url: 'https://example.com/cover.png',
    },
  ]);

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` });
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };
</script>

图片卡与预览 ​

listType="picture-card" 以瓦片展示,适合头像、封面、商品图等场景。打开 imagePreview 后点击卡片的预览按钮会用 ImagePreview 打开大图,并支持左右切换。

主视觉.png
详情.png
选择文件
最多 4 张
vue
<template>
  <x-upload
    v-model:file-list="fileList"
    list-type="picture-card"
    accept="image/*"
    image-preview
    :custom-request="mockRequest"
    response-url-key="url"
    :limit="4"
    tip="最多 4 张"
  />
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const fileList = ref<UploadFile[]>([
    {
      uid: 'card-1',
      name: '主视觉.png',
      status: 'done',
      percent: 1,
      type: 'image/png',
      url: 'https://example.com/cover.png',
    },
  ]);
  const mockRequest = (options: UploadRequestOption) => {
    const timer = window.setTimeout(
      () => options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` }),
      600,
    );
    return { abort: () => window.clearTimeout(timer) };
  };
</script>

手动上传 ​

auto-upload 关闭后,文件先以 init 状态入列,由业务调用 submit() 决定何时上传;abort() 取消单个或全部上传,retry() 重新上传失败文件。

选择文件
vue
<template>
  <x-upload
    response-url-key="url"
    :custom-request="mockRequest"
    ref="uploadRef"
    v-model:file-list="fileList"
    :auto-upload="false"
    multiple
  />
  <x-button type="primary" @click="uploadRef?.submit()">开始上传</x-button>
  <x-button @click="uploadRef?.abort()">全部取消</x-button>
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  const uploadRef = ref();
  const fileList = ref<UploadFile[]>([]);

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` });
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };
</script>

上传校验与失败重试 ​

beforeUpload 在文件入列前执行:返回 false 拦截,返回新的 File 可替换内容,返回 Promise 支持异步校验。上传失败的文件会进入错误态,列表内提供重试入口。

选择文件
vue
<template>
  <x-upload
    v-model:file-list="fileList"
    multiple
    accept=".png,.jpg,.jpeg,.pdf"
    :before-upload="beforeUpload"
    response-url-key="url"
    :custom-request="mockRequest"
  />
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  const fileList = ref<UploadFile[]>([]);

  const beforeUpload = (file: File) => {
    if (file.size > 1024 * 200) {
      // 返回 false 表示拦截,文件不会进入列表
      return false;
    }
    return true;
  };

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onError(new Error('示例:服务端拒绝了该文件'));
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };
</script>

自定义入口与列表项 ​

upload-button 替换入口内容,组件保留文件 input、点击、键盘和拖拽处理,file-name 定制文件名,extra-button 在操作区追加自定义操作,upload-item 可以整体替换某一行的渲染。

点击补充附件
附件合同扫描件.pdf(自定义文件名)详情
vue
<template>
  <x-upload response-url-key="url" :custom-request="mockRequest" v-model:file-list="fileList">
    <template #upload-button>
      <x-tag color="blue">点击补充附件</x-tag>
    </template>
    <template #file-name="{ file }">
      <span>{{ file.name }}(自定义文件名)</span>
    </template>
    <template #file-icon>
      <x-tag size="small">附件</x-tag>
    </template>
    <template #extra-button="{ file }">
      <x-link @click="detail = `查看 ${file.name} 的业务详情`">详情</x-link>
    </template>
  </x-upload>
  <span>{{ detail }}</span>
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  const fileList = ref<UploadFile[]>([
    { uid: 'slot-1', name: '合同扫描件.pdf', status: 'done', percent: 1 },
  ]);
  const detail = ref('');

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` });
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };
</script>

禁用与粘贴 ​

disabled 会禁用选择、开始、重试、取消与移除;历史附件仍可预览和下载;paste 打开后,聚焦组件即可粘贴剪贴板中的文件(例如截图)。

选择文件
聚焦下方区域后按 Ctrl / Cmd + V 粘贴文件
选择文件禁用状态无法选择或拖入
vue
<template>
  <x-upload
    response-url-key="url"
    :custom-request="mockRequest"
    v-model:file-list="fileList"
    paste
    multiple
    @change="(info) => (log = info.file.name)"
  />
  <x-upload response-url-key="url" :custom-request="mockRequest" disabled />
</template>

<script setup lang="ts">
  import { ref } from 'vue';

  const fileList = ref<UploadFile[]>([]);
  const log = ref('');

  import type { UploadFile, UploadRequestOption } from 'x-next';

  const mockRequest = (options: UploadRequestOption) => {
    let percent = 0;
    const timer = window.setInterval(() => {
      percent = Math.min(1, percent + 0.2);
      options.onProgress(percent);
      if (percent === 1) {
        window.clearInterval(timer);
        options.onSuccess({ url: `/files/${encodeURIComponent(options.file.name)}` });
      }
    }, 240);
    return { abort: () => window.clearInterval(timer) };
  };
</script>

文件夹选择 ​

directory 会收集整个目录,自动接收多个文件,不需要额外打开 multiple。选择后可以从原始文件的 webkitRelativePath 读取路径;拖入目录按批次递归读取子目录。

点击或拖拽文件到此处上传选择或拖入一个文件夹
vue
<template>
  <x-upload
    v-model:file-list="files"
    directory
    draggable
    :auto-upload="false"
    @change="recordPath"
  />
  <ul
    ><li v-for="(path, index) in paths" :key="index">{{ path }}</li></ul
  >
</template>

<script setup lang="ts">
  import { ref } from 'vue';
  import type { UploadFile, UploadChangeInfo } from 'x-next';
  const files = ref<UploadFile[]>([]);
  const paths = ref<string[]>([]);
  const recordPath = ({ file }: UploadChangeInfo) => {
    paths.value = [file.file?.webkitRelativePath || file.name, ...paths.value].slice(0, 6);
  };
</script>

替换文件与自定义列表 ​

updateFile(uid, file) 保留 uid,取消旧请求,清理旧响应和地址,并回到 init;点击逐项“开始上传”或调用 submit() 发送新内容。自定义 upload-item 时操作需由业务调用组件方法。

选择文件
vue
<template>
  <x-upload
    ref="uploadRef"
    v-model:file-list="files"
    :auto-upload="false"
    :custom-request="request"
    response-url-key="url"
  >
    <template #upload-item="{ file, index }">
      <span>{{ index + 1 }}. {{ file.name }}({{ file.status }})</span>
      <x-button :disabled="file.status === 'uploading'" @click="uploadRef?.submit(file)"
        >开始上传</x-button
      >
      <x-button :disabled="file.status !== 'uploading'" @click="uploadRef?.abort(file)"
        >取消</x-button
      >
    </template>
  </x-upload>
  <x-button :disabled="!files.length" @click="replace">替换第一个文件</x-button>
</template>

<script setup lang="ts">
  import { ref } from 'vue';
  import type { UploadFile, UploadRequestOption } from 'x-next';
  const uploadRef = ref();
  const files = ref<UploadFile[]>([]);
  const replace = () => {
    if (files.value[0])
      uploadRef.value?.updateFile(
        files.value[0].uid,
        new File(['修订'], '修订稿.txt', { type: 'text/plain' }),
      );
  };
  const request = (option: UploadRequestOption) => {
    const timer = window.setTimeout(() => option.onSuccess({ url: '/files/revised.txt' }), 600);
    return { abort: () => window.clearTimeout(timer) };
  };
</script>

Form 联动 ​

上传列表绑定 Form 模型字段后,加入、移除和上传状态变化会触发 change 校验。入口关联标签、必填状态和帮助/错误说明;Form 禁用态同时禁用上传操作。

选择文件
请选择附件后校验
vue
<template>
  <x-form ref="formRef" :model="model">
    <x-form-item
      field="attachments"
      label="附件"
      required
      help="至少选择一个文件"
      :rules="[{ required: true, type: 'array', minLength: 1, message: '请上传附件' }]"
    >
      <x-upload v-model:file-list="model.attachments" :auto-upload="false" action="/api/upload" />
    </x-form-item>
  </x-form>
  <x-button @click="validate">校验附件</x-button>
  <span>{{ result }}</span>
</template>

<script setup lang="ts">
  import { ref } from 'vue';
  import type { UploadFile } from 'x-next';
  const formRef = ref();
  const model = ref({ attachments: [] as UploadFile[] });
  const result = ref('');
  const validate = async () => {
    result.value = (await formRef.value?.validate()) ? '请补充附件' : '校验通过';
  };
</script>

按需导入 ​

ts
import { Upload } from 'x-next';

样式按需引入(base.css 为共享基础层,多个组件只需引入一次):

ts
import 'x-next/style/base.css';
import 'x-next/style/upload.css';

Props ​

参数说明类型默认值
fileList文件列表,支持 v-model:file-listUploadFile[]undefined
defaultFileList非受控模式下的初始文件列表UploadFile[][]
action上传地址,传函数时按文件动态解析并支持异步string | ((file: File) => string | Promise<string>)-
method请求方法'post' | 'put' | 'patch''post'
headers请求头Record<string, string>-
data附加表单字段,可传函数按文件解析Record<string, unknown> | ((file) => ...)-
nameFormData 文件字段名string | ((file) => string)'file'
withCredentials是否携带 Cookiebooleanfalse
timeout请求超时毫秒数,0 表示不超时number0
customRequest自定义上传请求;返回 Promise 时自动接管状态与结果,返回 { abort } 时手动回调(option: UploadRequestOption) => UploadRequest | Promise<unknown> | void-
accept接受的文件类型,同原生 acceptstring-
multiple是否允许选择多个文件booleanfalse
directory是否支持选择文件夹(含拖拽递归)booleanfalse
capture移动端捕获来源,透传原生 inputboolean | 'user' | 'environment'-
paste是否支持聚焦后粘贴文件booleanfalse
draggable是否渲染为拖拽上传区域booleanfalse
disabled是否禁用booleanundefined
autoUpload是否选择后立即上传booleantrue
limit最大文件数量,0 表示不限number0
beforeUpload上传前钩子,可拦截或替换文件(file: File, fileList: File[]) => void | boolean | File | Blob | string | Promise<...>-
beforeRemove移除前钩子,返回 false 阻止移除(file: UploadFile) => boolean | void | Promise<boolean | void>-
listType列表展示形态'text' | 'picture' | 'picture-card''text'
tip提示文案,拖拽区与图片卡入口显示string-
showFileList是否展示文件列表booleantrue
showUploadButton是否展示上传入口,对象形式可配置达 limit 后仍显示boolean | { showOnExceedLimit?: boolean }true
showRemoveButton是否展示移除按钮booleantrue
showRetryButton是否展示重试 / 开始上传按钮booleantrue
showCancelButton是否展示取消上传按钮booleantrue
showPreviewButton是否展示预览按钮booleantrue
showDownloadButton是否展示下载按钮booleanfalse
download链接是否带 download 属性,点击直接下载booleanfalse
showLink有地址时是否渲染为链接booleantrue
imagePreview是否用 ImagePreview 打开图片booleanfalse
imageLoading缩略图原生 loading 策略'eager' | 'lazy'-
responseUrlKey从响应中提取 url 的字段名(支持 data.url 嵌套路径)或解析函数string | ((file: UploadFile, response?: unknown) => string | undefined)-
ariaLabel上传入口的无障碍名称string-

Events ​

事件名说明回调参数
update:fileList文件列表变化,配合 v-model:file-list 使用fileList: UploadFile[]
change文件加入、进度、成功、失败、移除时触发{ file, fileList }
progress上传进度变化时触发,percent 为 0 ~ 1{ file, fileList, percent, event? }
success单个文件上传成功file: UploadFile
error单个文件上传失败file: UploadFile
remove文件被移除file: UploadFile
exceed文件数量超出 limit,超出部分不进入列表{ files: File[], fileList: UploadFile[] }
reject文件被拒绝,reason 为 accept / limit / beforeUpload / read{ files: File[], reason }
preview点击文件名或预览按钮file: UploadFile
download点击下载按钮file: UploadFile

Slots ​

插槽名说明插槽参数
default上传入口内容-
upload-button替换入口内容,保留组件交互-
upload-item替换整行列表项渲染{ file, index }
file-icon自定义文本列表的文件图标{ file }
file-name自定义文件名内容{ file }
extra-button在操作区追加自定义操作{ file }
image自定义图片 / 图片卡的缩略图{ file }

Methods ​

方法名说明参数
submit上传指定文件;不传参数时上传全部 init 状态文件(file?: UploadFile) => void
abort取消指定文件;不传参数时取消全部上传中的文件(file?: UploadFile) => void
retry重新上传失败文件(file: UploadFile) => void
upload直接上传一组 File,同样走 limit 与 beforeUpload(files: File[]) => void
updateFile替换指定 uid 的原始文件并回到待上传态(uid: string, file: File) => void
fileList当前文件列表(只读)UploadFile[]

UploadFile ​

字段说明类型
uid文件唯一标识,外部未提供时自动生成string
name文件名string
size文件大小(字节)number
typeMIME 类型string
status状态:待上传 / 上传中 / 完成 / 失败'init' | 'uploading' | 'done' | 'error'
percent上传进度,0 ~ 1number
url文件地址string
thumbUrl缩略图地址,图片文件自动生成本地预览string
response上传接口响应体any
error失败信息unknown
file原始 File 对象File

UploadRequestOption ​

customRequest 收到的参数,三种收尾方式二选一,不要混用:

  • 返回 Promise(推荐):resolve 即成功,reject 即失败,无需手动回调;option 里的 signal / onProgress 按需透传给底层请求。
  • 返回 { abort } 或 undefined:完全手动控制,自行调用 onProgress / onSuccess / onError;abort 会在取消、移除、卸载时被调用。
字段说明类型
action解析后的上传地址;未配置 action 时为空字符串string
method请求方法'post' | 'put' | 'patch'
nameFormData 文件字段名string
file原始 File 对象File
fileItem列表项,可读取 uid 与业务字段UploadFile
data解析后的附加表单字段Record<string, unknown> | undefined
headers请求头Record<string, string> | undefined
withCredentials是否携带 Cookieboolean
timeout超时毫秒数number
signal每次尝试的中止信号;取消 / 移除 / 卸载时触发,可直接透传给 fetch、axiosAbortSignal | undefined
onProgress进度回调,0 ~ 1(percent: number, event?: ProgressEvent) => void
onSuccess手动成功回调(response?: unknown) => void
onError手动失败回调(error?: unknown) => void

已知限制 ​

  • 内置 XHR 只处理单次请求,分片上传、断点续传需要自行实现 customRequest。
  • Promise 模式下组件的取消、移除与卸载会触发 option.signal;业务未使用 signal 时取消仅失效回调,网络请求不会被真正中止,但列表会回到待上传态。
  • 返回 fetch Response 时,非 2xx 会按失败处理,error.status 为 HTTP 状态码;JSON / text 响应体会解析后写入 file.response。
  • 在异步 customRequest 中既手动调用回调、又返回 Promise 时,以手动回调结果为准。
  • accept 是前端筛选,服务端仍需二次校验类型与大小。
  • reject 的 reason 为 limit 时,文件在 accept 之后被数量上限拦截;类型不符则为 accept。
  • 目录读取失败会触发 reject,reason="read";此时 files 是浏览器在拖拽事件中提供的原始文件,可能为空。
  • 图片缩略图通过 URL.createObjectURL 生成,组件移除或卸载时会回收;外部传入的 blob: 地址由业务自行释放。
  • 组件挂载时若同时传入 fileList 与 defaultFileList,以 fileList 为准。
  • 自定义入口保留 Enter / Space 与点击选择;image 在图片列表和图片卡生效。imageLoading="lazy" 直接使用浏览器原生懒加载。
  • capture="user" / "environment" 仅在支持相机捕获的移动端有意义,需要配合 accept="image/*" 在真实设备上验证。
  • 原生选择窗口由浏览器管理;取消选择不会改变列表。abort() 取消后组件会忽略本次尝试的后续回调;要真正停止网络请求,返回 { abort() {} } 或使用 option.signal。