用uniapp3.0的写法组合式api,setup形式封装一个图片上传公用组件,要求
1、使用uni-file-picker选择文件
2、uni.uploadFile上传图片
3、要能支持上传接口动态化
4、支持删除如片列表中已上传项
5、可以预览已上传列表图片
6、支持动态化限制图片格式,图片大小,
7、上传成功后走公用组件返回图片列表list,
具体的封装代码,和在页面使用该组件的例子
组件ImageUploader.vue
<template>
<view class="image-uploader">
<view class="header">
<text>图片上传</text>
<text>{{ uploadedFiles.length }}/{{ maxCount }}</text>
</view>
<uni-file-picker multiple :limit="maxCount" :disable="uploadedFiles.length >= maxCount" @select="onFileChange"
:ext-class="filePickerClass" :file-extname="allowedFormats" @delete="removeImage">
<view class="upload-btn">
<uni-icons type="camera-filled" color="#007aff" size="40"></uni-icons>
</view>
</uni-file-picker>
</view>
</template>
<script setup>
let base = import.meta.env.VITE_APP_BASE_API
let token = uni.getStorageSync(ACCESS_TOKEN)
const props = defineProps({
maxCount: {
type: Number,
default: 1
},
uploadUrl: {
type: String,
default: '/ap/files/upload555'//自己的上传地址
},
allowedFormats: {
type: Array,
default: () => ['jpg', 'jpeg', 'png', 'gif']
},
maxSize: {
type: Number,
default: 5 * 1024 * 1024 // 5MB
},
value: {
type: Array,
default: () => {
return [];
},
},
})
const emit = defineEmits(['update:list'])
const uploadedFiles = computed({
get: () => {
return props.value;
},
set: (val) => {
emit('update:value', val);
},
});
const onFileChange = async (files) => {
console.log('files', files)
// const files = event.detail.tempFiles
const filelist = files.tempFiles; // 获取选中的文件
for (const file of filelist) {
if (!props.allowedFormats.includes(file.name.split('.').pop().toLowerCase())) {
uni.showToast({
title: '不支持的文件格式',
icon: 'none'
})
continue
}
if (file.size > props.maxSize) {
uni.showToast({
title: '文件大小超出限制',
icon: 'none'
})
continue
}
console.log('hsjhj')
uni.uploadFile({
url: base + props.uploadUrl,
filePath: file.path,
name: 'file', // 这里根据API的要求来定义
header: {
// 'Content-Type': 'multipart/form-data', // 设置上传文件的 Content-Type
'Authorization': `Bearer ${token}`, // 自定义头部信息,比如 Authorization
'Tenant-Id': 1,
'CLIENT-TOC': 'Y'
},
success: (uploadFileRes) => {
console.log('upload success:', uploadFileRes);
let data = JSON.parse(uploadFileRes.data)
console.log('upload success: data', data);
if (data && data.code == 0) {
uploadedFiles.value.push({
url: data.data.url
})
console.log('uploadedFiles.value', uploadedFiles.value.length);
emit('update:list', uploadedFiles.value)
} else {
tip.error('上传失败')
}
},
fail: (error) => {
console.error('upload fail:', error);
tip.error('上传失败')
// 处理上传失败的逻辑
}
});
}
}
const previewImage = (url) => {
uni.previewImage({
urls: uploadedFiles.value.map(file => file.url),
current: url
})
}
const removeImage = (file) => {
console.log('hhj', file)
uploadedFiles.value.splice(file.index, 1)
emit('update:list', uploadedFiles.value)
}
const filePickerClass = {
'file-picker': true,
'disabled': uploadedFiles.value.length >= props.maxCount
}
</script>
<style scoped>
.image-uploader {
padding: 16px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
.header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.upload-btn {
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
border: 2px dashed #ddd;
border-radius: 8px;
color: #bbb;
}
</style>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
页面使用,预留了上传成功后的回调方法,可不传,也可以直接v-model绑定