)
效果如下组件如下template view view classupload-box view classupload-item v-for(item, index) in fileList :keyindex clickpreviewImage(item) image :srcitem.url modeaspectFill/image text classdelete click.stopdeleteImage(index)×/text /view view classupload-btn v-iffileList.length maxCount clickuploadShow true text classplus/text text上传图片/text /view /view text classtip最多上传{{ maxCount }}张图片/text u-modal :showuploadShow canceluploadShow false :showConfirmButtonfalse :showCancelButtonfalse closeuploadShow false :closeOnClickOverlaytrue view classmodal_view view classmodal_photo styledisplay:flex view classda clickphotoUpload(1)拍照上传/view view classda clickphotoUpload(2)从相册导入/view /view /view /u-modal /view /template script import { getFilePrivate } from /api/需引入自己的上传接口 export default { name: UploadImage, props: { // 接收已存在的图片ID字符串用逗号分隔 value: { type: String, default: }, // 最大上传数量 maxCount: { type: Number, default: 3 } }, data () { return { fileList: [], // 用于页面展示图片 { url: base64 } imgUrls: [], // 存储图片ID数组 uploadShow: false, // 控制弹窗显示 baseUrl: uni.getStorageSync(baseUrl) } }, watch: { value: { handler (newVal) { if (newVal) { // 防止重复请求如果当前 imgUrls 已经和传入的一致则不重新请求 const newIds newVal.split(,) if (JSON.stringify(this.imgUrls) JSON.stringify(newIds)) return this.imgUrls newIds this.initFileList() } else { this.fileList [] this.imgUrls [] } }, immediate: true } }, methods: { // 初始化图片列表将ID转为base64展示 async initFileList () { this.fileList await Promise.all( this.imgUrls.map(async (id) { const url await this.getImgUrl(id) return { url } }) ) }, // 获取图片Base64 async getImgUrl (id) { try { let res await getFilePrivate(id) return data:image/jpeg;base64, uni.arrayBufferToBase64(res) } catch (error) { console.error(获取图片失败:, error) return } }, // 选择图片 photoUpload (type) { uni.chooseImage({ count: this.maxCount - this.fileList.length, sourceType: type 1 ? [camera] : [album], success: async (res) { this.uploadShow false for (let filePath of res.tempFilePaths) { await this.uploadImage(filePath) } } }) }, // 上传单张图片到服务器 uploadImage (filePath) { return new Promise((resolve, reject) { uni.uploadFile({ url: this.baseUrl /filePrivate, filePath: filePath, header: { token: uni.getStorageSync(token) }, name: file, success: async (res) { let obj JSON.parse(res.data) if (obj.data obj.data.bcode 0) { const imgId obj.data.bdata this.imgUrls.push(imgId) const base64Url await this.getImgUrl(imgId) this.fileList.push({ url: base64Url }) // 通知父组件数据更新 this.$emit(input, this.imgUrls.join(,)) } resolve() }, fail: (err) { reject(err) } }) }) }, // 预览图片 previewImage (item) { uni.previewImage({ urls: [item.url] }) }, // 删除图片 deleteImage (index) { this.fileList.splice(index, 1) this.imgUrls.splice(index, 1) this.$emit(input, this.imgUrls.join(,)) } } } /script style langscss scoped .upload-box { display: flex; flex-wrap: wrap; margin-top: 20rpx; .upload-item { width: 200rpx; height: 200rpx; margin-right: 20rpx; margin-bottom: 20rpx; position: relative; image { width: 100%; height: 100%; border-radius: 8rpx; } .delete { position: absolute; right: -10rpx; top: -10rpx; width: 40rpx; height: 40rpx; line-height: 40rpx; text-align: center; background-color: #f56c6c; color: #fff; border-radius: 50%; } } .upload-btn { width: 200rpx; height: 200rpx; border: 2rpx dashed #dcdfe6; border-radius: 8rpx; display: flex; flex-direction: column; align-items: center; justify-content: center; .plus { font-size: 60rpx; color: #909399; margin-bottom: 10rpx; } text { font-size: 24rpx; color: #909399; } } } .tip { font-size: 24rpx; color: #909399; margin-top: 10rpx; } .modal_view { padding: 20rpx; width: 560rpx; .modal_photo { display: flex; justify-content: space-between; height: 90rpx; width: 520rpx; margin: 40rpx auto; view { height: 88rpx; width: 246rpx; justify-content: center; border-radius: 45rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; } view:nth-child(1) { background-color: #225AE7; color: #fff; } view:nth-child(2) { border: 1rpx solid #666; color: #666; } } } /style使用示例如下template view stylepadding: 20px; !-- 1. 使用组件v-model绑定数据maxCount限制最多上传2张 -- UploadImage v-modelimgIds :maxCount2 / !-- 2. 看看绑定的值变成了啥 -- view stylemargin-top: 20px; color: #999; 当前绑定的图片ID字符串{{ imgIds || 暂无 }} /view /view /template script import UploadImage from /components/UploadImage/UploadImage.vue export default { components: { UploadImage }, data() { return { // 用来存放图片ID的字符串多个ID用逗号隔开。新增时为空编辑时赋值如 101,102 imgIds: } } } /script上传文件的接口必须要特殊处理export function getFilePrivate(id) { return new Promise((resolve, reject) { uni.request({ url: xxxxxxx/${id}, responseType: arraybuffer, header: { Content-Type: application/x-www-form-urlencoded, token: ${user.state().token} }, success: res { resolve(res.data) }, fail: err { console.log(request失败fail, err); reject(err) } }) }) }