Appearance
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 可以拿到每一次变更后的完整列表。
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 用于说明限制。默认插槽会替换整块区域内容。
拖拽文件到此处或点击选择,支持任意类型
支持批量选择,单个文件不超过 20MBvue
<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 打开大图,并支持左右切换。
选择文件
最多 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 可以整体替换某一行的渲染。
点击补充附件
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 打开后,聚焦组件即可粘贴剪贴板中的文件(例如截图)。
选择文件
选择文件禁用状态无法选择或拖入
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-list | UploadFile[] | undefined |
defaultFileList | 非受控模式下的初始文件列表 | UploadFile[] | [] |
action | 上传地址,传函数时按文件动态解析并支持异步 | string | ((file: File) => string | Promise<string>) | - |
method | 请求方法 | 'post' | 'put' | 'patch' | 'post' |
headers | 请求头 | Record<string, string> | - |
data | 附加表单字段,可传函数按文件解析 | Record<string, unknown> | ((file) => ...) | - |
name | FormData 文件字段名 | string | ((file) => string) | 'file' |
withCredentials | 是否携带 Cookie | boolean | false |
timeout | 请求超时毫秒数,0 表示不超时 | number | 0 |
customRequest | 自定义上传请求;返回 Promise 时自动接管状态与结果,返回 { abort } 时手动回调 | (option: UploadRequestOption) => UploadRequest | Promise<unknown> | void | - |
accept | 接受的文件类型,同原生 accept | string | - |
multiple | 是否允许选择多个文件 | boolean | false |
directory | 是否支持选择文件夹(含拖拽递归) | boolean | false |
capture | 移动端捕获来源,透传原生 input | boolean | 'user' | 'environment' | - |
paste | 是否支持聚焦后粘贴文件 | boolean | false |
draggable | 是否渲染为拖拽上传区域 | boolean | false |
disabled | 是否禁用 | boolean | undefined |
autoUpload | 是否选择后立即上传 | boolean | true |
limit | 最大文件数量,0 表示不限 | number | 0 |
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 | 是否展示文件列表 | boolean | true |
showUploadButton | 是否展示上传入口,对象形式可配置达 limit 后仍显示 | boolean | { showOnExceedLimit?: boolean } | true |
showRemoveButton | 是否展示移除按钮 | boolean | true |
showRetryButton | 是否展示重试 / 开始上传按钮 | boolean | true |
showCancelButton | 是否展示取消上传按钮 | boolean | true |
showPreviewButton | 是否展示预览按钮 | boolean | true |
showDownloadButton | 是否展示下载按钮 | boolean | false |
download | 链接是否带 download 属性,点击直接下载 | boolean | false |
showLink | 有地址时是否渲染为链接 | boolean | true |
imagePreview | 是否用 ImagePreview 打开图片 | boolean | false |
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 |
type | MIME 类型 | string |
status | 状态:待上传 / 上传中 / 完成 / 失败 | 'init' | 'uploading' | 'done' | 'error' |
percent | 上传进度,0 ~ 1 | number |
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' |
name | FormData 文件字段名 | string |
file | 原始 File 对象 | File |
fileItem | 列表项,可读取 uid 与业务字段 | UploadFile |
data | 解析后的附加表单字段 | Record<string, unknown> | undefined |
headers | 请求头 | Record<string, string> | undefined |
withCredentials | 是否携带 Cookie | boolean |
timeout | 超时毫秒数 | number |
signal | 每次尝试的中止信号;取消 / 移除 / 卸载时触发,可直接透传给 fetch、axios | AbortSignal | 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。