SwUploadImage
图片上传组件,无样式:自身不渲染任何 UI,通过 uploadFn prop 注入实际上传逻辑,通过默认 slot 暴露状态(isOver / pending / open),由消费方完全掌控视觉呈现。支持点击选择和拖拽两种方式,多文件并发上传,成功 / 失败按条 emit。
基础用法(按钮式)
最简用法:按钮触发选择图片,上传完成后展示预览。
基础用法(按钮 + 结果预览)
加载中...
自定义触发区(#default slot)
经典「+ 号上传卡」:默认 slot 完全接管触发区 UI,上传完成后 + 号变成预览图。
+ 号上传卡(slot 自定义)
加载中...
拖拽上传
slot 的 isOver 标记拖拽悬停态,可用来高亮放置区。
拖拽 + 点击双通道
加载中...
多图上传
开启 multiple 后可同时选择或拖入多张图片,内部并发上传并逐条 emit success。
多图上传
加载中...
错误处理
uploadFn reject 时触发 error 事件,按条反馈。
上传失败反馈(@error)
加载中...
受控列表(fileList + #file + maxCount + beforeUpload)
传 v-model:file-list 后组件接管已传列表:成功结果自动追加、#file 插槽逐条渲染预览与删除;maxCount 控制数量上限(超限整批拒绝并 emit exceed)、beforeUpload 做逐文件前置校验(体积 / 重名 / 类型,返回 false 剔除)。这是迁移老项目多文件上传(预览 + 删除 + limit + 校验)的完整形态。
<template>
<SwUploadImage
v-model:file-list="images"
:upload-fn="uploadImage"
:max-count="9"
:before-upload="checkSize"
multiple
@exceed="useToast().warning('最多只能选择 9 张图片')"
>
<template #default="{ open, pending }">
<SwButton :loading="pending" @click="open">选择图片</SwButton>
</template>
<template #file="{ file, remove }">
<span class="thumb">
<SwImage :src="file.value" alt="已传图片" ratio="1/1" />
<SwButton aria-label="删除" @click="remove">✕</SwButton>
</span>
</template>
</SwUploadImage>
</template>
<script setup lang="ts">
const images = ref<ImageUploadResult[]>([])
const checkSize = (file: File) =>
file.size <= 10 * 1024 * 1024 || (useToast().warning('图片不能超过 10MB'), false)
</script>
Props
| 名称 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
uploadFn | (file: File, thumbnail: boolean) => Promise<ImageUploadResult> | ✓ | — | 实际上传函数,组件只管流程,网络层由外部注入 |
accept | string | image/* | 文件选择框的 accept 属性 | |
thumbnail | boolean | false | 是否作为缩略图上传,作为第二参数传给 uploadFn | |
multiple | boolean | false | 是否允许多文件 | |
fileList | ImageUploadResult[] | undefined | 已传文件受控列表(v-model:file-list)。传入后成功结果自动追加,配合 #file 插槽渲染预览 + 删除;不传保持"单次上传薄壳"行为 | |
maxCount | number | undefined | 文件数量上限(含 fileList 现有计数)。选择超限时整批拒绝并 emit exceed | |
beforeUpload | (file: File, batch: File[]) => boolean | Promise<boolean> | undefined | 逐文件前置否决钩子:返回 false 剔除该文件(其余照传,全剔则本批终止)。体积 / 重名 / 自定义校验与用户提示都在钩子里做 |
Events
| 事件名 | 载荷类型 | 说明 |
|---|---|---|
success | ImageUploadResult | 单个文件上传成功后触发 |
error | Error | 上传异常时触发 |
update:fileList | ImageUploadResult[] | 受控列表变化(成功追加 / remove() 移除),携带新列表 |
remove | (file: ImageUploadResult, index: number) | #file 插槽的 remove() 被调用 |
exceed | File[] | 选择文件数超出 maxCount,携带本次选择的文件(本批已被整体拒绝) |
ImageUploadResult 结构:
type ImageUploadResult = { id: string; value: string; width: number; height: number }
Slots
| 插槽名 | 作用域参数 | 说明 |
|---|---|---|
default | { isOver, pending, open } | 拖拽悬停态、上传中状态、打开文件选择框 |
file | { file, index, remove } | 传了 fileList 时对列表逐条渲染(预览 / 删除按钮由消费方自绘);remove() 从列表移除该条并 emit remove |
注意事项
- 组件无样式:不内置任何 CSS,UI 完全交给 slot 的消费方实现。
uploadFn是外部注入的函数,通常封装自useUploadercomposable,与实际业务接口对接;Demo 中使用 mock 实现演示流程。- 上传中(
pending=true)组件静默拒绝重复触发,slot UI 必须用pending把触发元素做 disabled 处理。 - 视频分片上传见 SwUploadVideo,普通文件上传见 SwUploadFile。