更新记录
1.0.0(2026-07-16) 下载此版本
1.1.5(2026-07-16)
- 上传:
name/header/formData合并为uploadOptions,默认{ name:'file', header:{}, formData:{} },只填uploadUrl即可上传 - 文档:
readme.md补全安装、快速上手、Props / Events / 方法 / 插槽 / 上传约定与完整示例
1.1.4(2026-07-16)
- 上传:
formData完全由用户配置,不再自动注入isCover/index;文档补充uploadUrl/header/formData/name
1.1.3(2026-07-16)
- 显示开关:
showCover(角标+设封面)、showRemove、showSize;文案coverText/setCoverText
1.1.2(2026-07-16)
- 恢复真正的长按拖拽排序:浮层跟手、占位高亮、拖动锁滚(
@dragging+ page-meta)
1.1.1(2026-07-16)
- (已回退)曾改为点选放置
1.1.0(2026-07-16)
- 增强:排序、封面、本地压缩与体积、上传进度条
1.0.0(2026-07-16)
- 首发:九宫格选图、预览、删除、maxCount / maxSize、可选 autoUpload
1.1.5(2026-07-16)
- 上传:
name/header/formData合并为uploadOptions,默认{ name:'file', header:{}, formData:{} },只填uploadUrl即可上传 - 文档:
readme.md补全安装、快速上手、Props / Events / 方法 / 插槽 / 上传约定与完整示例
1.1.4(2026-07-16)
- 上传:
formData完全由用户配置,不再自动注入isCover/index;文档补充uploadUrl/header/formData/name
1.1.3(2026-07-16)
- 显示开关:
showCover(角标+设封面)、showRemove、showSize;文案coverText/setCoverText
1.1.2(2026-07-16)
- 恢复真正的长按拖拽排序:浮层跟手、占位高亮、拖动锁滚(
@dragging+ page-meta)
1.1.1(2026-07-16)
- (已回退)曾改为点选放置
1.1.0(2026-07-16)
- 增强:排序、封面、本地压缩与体积、上传进度条
1.0.0(2026-07-16)
- 首发:九宫格选图、预览、删除、maxCount / maxSize、可选 autoUpload
平台兼容性
uni-app(3.8.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
kex-uploader
多图上传组件(uni-app Vue3):九宫格选图、长按拖拽排序、设封面、本地压缩、上传进度。
兼容 H5 / App / 各主流小程序。
功能一览
| 能力 | 说明 |
|---|---|
| 九宫格选图 | uni.chooseImage,v-model 为 string[](url 数组) |
| 预览 / 删除 | 点击预览,右上角删除 |
| 数量 / 大小限制 | maxCount、maxSize(MB) |
| 长按拖拽排序 | 浮层跟手;配合 page-meta 锁页面滚动 |
| 封面 | 首张为封面,非首张可点「设封面」 |
| 本地压缩 | H5 canvas / 其它端 uni.compressImage,可调质量 |
| 自动上传 | uni.uploadFile,最少只填 upload-url |
| 上传进度 | 遮罩百分比 + 进度条,失败可点重试 |
安装
将本插件目录拷贝到业务项目:
项目根目录/uni_modules/kex-uploader/
uni-app easycom 会自动注册,无需手动 import。
要求:uni-app Vue3。
快速上手
1. 仅选图(不上传)
<template>
<kex-uploader v-model="images" :max-count="9" />
</template>
<script setup>
import { ref } from 'vue'
const images = ref([]) // string[],本地临时路径或远程 url
</script>
2. 自动上传(推荐)
最少只填接口地址,name / header / formData 有默认值:
<template>
<kex-uploader
v-model="images"
:auto-upload="true"
upload-url="https://example.com/api/upload"
@uploadSuccess="onOk"
@uploadFail="onFail"
/>
</template>
<script setup>
import { ref } from 'vue'
const images = ref([])
const onOk = (e) => {
// e.url 优先取接口返回的远程地址;e.data 为解析后的 JSON
console.log('上传成功', e.url, e.data)
}
const onFail = (e) => {
console.log('上传失败', e.err)
}
</script>
3. 带 Token / 业务字段上传
<template>
<kex-uploader
v-model="images"
:auto-upload="true"
upload-url="https://example.com/api/upload"
:upload-options="uploadOptions"
@uploadSuccess="onOk"
/>
</template>
<script setup>
import { ref, computed } from 'vue'
const images = ref([])
const token = ref('Bearer xxx')
const uploadOptions = computed(() => ({
// name 默认 'file',可不写
header: {
Authorization: token.value
},
// formData 的值必须是字符串
formData: {
folderId: '123',
module: 'attachment'
}
}))
</script>
4. 手动上传
<template>
<kex-uploader
ref="uploaderRef"
v-model="images"
:auto-upload="false"
upload-url="https://example.com/api/upload"
:upload-options="uploadOptions"
/>
<button @click="doUpload">开始上传</button>
</template>
<script setup>
import { ref } from 'vue'
const images = ref([])
const uploaderRef = ref(null)
const uploadOptions = { header: { Authorization: 'Bearer xxx' } }
const doUpload = () => {
uploaderRef.value?.uploadAll()
}
</script>
5. 排序 + 封面(含锁滚)
小程序 / App 拖动时建议配合 page-meta,避免页面跟着滚:
<template>
<page-meta :disable-scroll="dragging" />
<kex-uploader
v-model="images"
:sortable="true"
:show-cover="true"
@dragging="(v) => (dragging = v)"
@sort="onSort"
@coverChange="onCover"
/>
</template>
<script setup>
import { ref } from 'vue'
const images = ref([])
const dragging = ref(false)
const onSort = (e) => console.log('排序', e.from, '→', e.to)
const onCover = (e) => console.log('封面', e.url)
</script>
6. 本地压缩
<kex-uploader
v-model="images"
:compress="true"
:compress-quality="0.7"
:compress-max-width="1920"
:show-size="true"
@compress="onCompress"
/>
- H5:canvas 压成 JPEG
- App / 小程序:
uni.compressImage - 开启后底部出现质量滑条(下次选图生效)
- 角标显示:
压缩后←原体积
7. 回显已有图片
<script setup>
import { ref } from 'vue'
const images = ref([
'https://example.com/a.jpg',
'https://example.com/b.jpg'
])
</script>
<template>
<kex-uploader v-model="images" />
</template>
第一张即为封面(showCover 为 true 时显示角标)。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| modelValue / v-model | string[] |
[] |
图片 url 列表;第 0 张为封面 |
| maxCount | Number |
9 |
最多可选张数 |
| maxSize | Number |
10 |
单张上限,单位 MB;0 表示不限制 |
| sourceType | string[] |
['album','camera'] |
选图来源 |
| sizeType | string[] |
['original'] |
传给 chooseImage;开启 compress 时内部会强制 original |
| disabled | Boolean |
false |
禁用(不可选、不可删、不可拖) |
| preview | Boolean |
true |
点击格子预览大图 |
| showCount | Boolean |
true |
加号格显示「当前/上限」 |
| size | Number |
160 |
格子边长,单位 rpx |
| toast | Boolean |
true |
超限、压缩等场景是否 toast |
| sortable | Boolean |
true |
长按拖拽排序 |
| showCover | Boolean |
true |
封面角标 + 非首张「设封面」按钮 |
| coverText | String |
'封面' |
首张角标文案 |
| setCoverText | String |
'封面' |
设封面按钮文案 |
| showRemove | Boolean |
true |
是否显示删除按钮 |
| deletable | Boolean |
true |
兼容旧属性;与 showRemove 任一为 false 则不显示删除 |
| showSize | Boolean |
true |
是否显示体积角标 |
| compress | Boolean |
false |
选图后本地压缩 |
| compressQuality | Number |
0.7 |
压缩质量 0.2 ~ 1 |
| compressMaxWidth | Number |
1920 |
H5 压缩最大边长(px) |
| autoUpload | Boolean |
false |
选图(及压缩)完成后是否自动上传 |
| uploadUrl | String |
'' |
上传接口;有值且 autoUpload 为 true 时自动上传 |
| uploadOptions | Object |
见下表 | 上传参数,对应 uni.uploadFile |
uploadOptions
| 字段 | 类型 | 默认 | 对应 uni.uploadFile | 说明 |
|---|---|---|---|---|
| name | String |
'file' |
name |
文件字段名 |
| header | Object |
{} |
header |
请求头,可用 computed 动态带 Token |
| formData | Object |
{} |
formData |
额外表单字段;值必须是字符串 |
未传的字段会自动补默认值,因此只配 upload-url 即可发起上传。
// 完整示例
{
name: 'file',
header: { Authorization: 'Bearer xxx' },
formData: { bizId: '1', type: 'image' }
}
Events
| 事件 | 触发时机 | 回调参数 |
|---|---|---|
| update:modelValue | 列表变化 | string[] urls |
| change | 列表任意变化 | { urls, fileList, cover, ... } |
| select | 每次选图完成(含压缩后) | 见下方 |
| uploadSuccess | 单张上传成功 | 见下方 |
| uploadFail | 单张上传失败 | { index, url, err, statusCode?, data?, res? } |
| sort | 拖拽排序结束 | { from, to, urls } |
| coverChange | 封面变更 | { url } |
| compress | 单张压缩完成 | { from, to, originSize, size, quality } |
| oversize | 单张超体积 | { path, size, maxSize } |
| exceed | 超过 maxCount | { maxCount } |
| remove | 删除一张 | { index, url } |
| dragging | 拖动开始/结束 | true / false |
@select
{
tempFilePaths, // string[] 本次新增路径(压缩后)
files, // 本次新增详情:{ url, localPath, byteSize, originSize, status, index }[]
count, // 本次新增张数
urls, // 当前全部 urls(同 v-model)
fileList, // 当前全部:{ url, status, byteSize, originSize, isCover }[]
cover // 封面 url
}
@uploadSuccess
{
index, // 在列表中的下标
url, // 优先为接口返回的远程 url,解析不到则仍为本地路径
localPath, // 上传时的本地路径
isCover, // 是否封面(index === 0)
statusCode, // HTTP 状态码
data, // 解析后的服务端 JSON(解析失败则为原始字符串)
res, // uni.uploadFile 原始 success 参数
file, // { url, localPath, byteSize, originSize, status }
urls, // 当前全部 urls
fileList // 当前全部文件详情
}
@change
{
urls, // string[]
fileList, // { url, status, progress, message, byteSize, originSize, isCover }[]
cover // 封面 url
}
文件 status 可能值:pending · compressing · uploading · success · fail。
方法(ref)
<kex-uploader ref="uploaderRef" v-model="images" upload-url="..." />
| 方法 | 说明 |
|---|---|
choose() |
打开选图 |
upload(index) |
上传指定下标 |
uploadAll() |
上传所有 pending / fail 项 |
setCover(index) |
将第 index 张设为封面(移到首位) |
moveItem(from, to) |
移动位置 |
getFileList() |
返回当前文件详情数组 |
getCover() |
返回封面 url |
uploaderRef.value.choose()
uploaderRef.value.uploadAll()
console.log(uploaderRef.value.getFileList())
console.log(uploaderRef.value.getCover())
插槽
| 插槽 | 说明 |
|---|---|
add |
自定义「添加」格子内容 |
<kex-uploader v-model="images">
<template #add>
<text>添加图片</text>
</template>
</kex-uploader>
上传接口约定
组件通过 uni.uploadFile 上传。HTTP 2xx 视为成功。
成功后会尝试从响应里解析远程地址,并写回 v-model。识别字段顺序:
- 字符串本身
url/pathdata.url/data.pathresult.url/result.path
示例响应:
{ "code": 0, "data": { "url": "https://cdn.example.com/a.jpg" } }
若字段名不同,可在 @uploadSuccess 里用 e.data 自行处理,再改 v-model。
注意事项
- Vue3 only,依赖 easycom /
uni_modules。 formData值必须是字符串(uni 规范),例如folderId: '123',不要传数字或对象。- 拖拽锁滚:H5 组件内会锁 body;小程序 / App 请监听
@dragging,配合页面
<page-meta :disable-scroll="dragging" />。 - 自动上传条件:
autoUpload === true且uploadUrl非空。 - 封面:始终是列表第 0 张;拖到首位或点「设封面」都会触发
@coverChange。 - 权限:选图会触发系统相册 / 相机授权,请在各端 manifest 中按需配置。
完整示例
<template>
<page-meta :disable-scroll="dragging" />
<view class="page">
<kex-uploader
ref="uploaderRef"
v-model="images"
:max-count="9"
:max-size="10"
:sortable="true"
:show-cover="true"
:show-remove="true"
:show-size="true"
:compress="true"
:compress-quality="0.7"
:auto-upload="true"
upload-url="https://example.com/api/upload"
:upload-options="uploadOptions"
@select=""
@change="onChange"
@uploadSuccess="onUploadOk"
@uploadFail="onUploadFail"
@sort="onSort"
@coverChange="onCover"
@dragging="(v) => (dragging = v)"
/>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
const images = ref([])
const dragging = ref(false)
const uploaderRef = ref(null)
const token = ref('') // 从登录态获取
const uploadOptions = computed(() => ({
name: 'file',
header: token.value ? { Authorization: token.value } : {},
formData: { module: 'product' }
}))
const = (e) => console.log('选图', e.count, e.urls)
const onChange = (e) => console.log('列表', e.urls, '封面', e.cover)
const onUploadOk = (e) => console.log('成功', e.url, e.data)
const onUploadFail = (e) => console.log('失败', e.err)
const onSort = (e) => console.log('排序', e.from, e.to)
const onCover = (e) => console.log('封面变更', e.url)
</script>
版本
详见同目录 changelog.md。

收藏人数:
下载插件并导入HBuilderX
赞赏(0)
下载 8
赞赏 0
下载 12431468
赞赏 1934
赞赏
京公网安备:11010802035340号