uniapp封装图片上传组件,使用v-model双向绑定

使用uniapp开发时大多会开发到图片上传这个功能,将其封装成组件,使其更方便使用,并且使用v-model 更好的双向绑定,不用单独监听事件。并且实现一次选择多张图片,循环上传。

以下是代码

js 复制代码
<template>
	<view class="grid-img">
		<!-- 遍历v-model绑定的图片数组 -->
		<view
			v-for="(item, index) in value"
			:key="index"
			class="img-view"
			:style="{ width: wihe[0], height: wihe[1] }"
		>
			<image
				class="imgs"
				:src="item"
				:style="{ width: wihe[0], height: wihe[1] }"
			></image>
			<view class="img-close" @click="closeItem(index)">
				<u-icon name="close" color="#fff" size="10"></u-icon>
			</view>
		</view>
		<!-- 上传按钮:未达最大数量时显示 -->
		<view v-if="value.length < maxCount" @click="startUpload">
			<view class="upload-view" :style="{ width: wihe[0], height: wihe[1] }">
				<u-icon name="plus" size="20" bold color="#BFBFBF"></u-icon>
			</view>
		</view>
	</view>
</template>

<script>
import { upload } from '@/api/common.js';
import storage from '@/utils/storage.js';
import config from '@/config/config';

export default {
	props: {
		// v-model绑定的图片数组(核心)
		value: {
			type: Array,
			default: () => []
		},
		maxCount: {
			type: Number,
			default: 1 // 最大上传总数(如2)
		},
		wihe: {
			type: Array,
			default: () => ['150rpx', '150rpx'] // 图片宽高
		},
		multiple: {
			type: Boolean,
			default: true // 是否允许选择多张(true=支持多图,false=仅单张)
		},
		maxLength: {
			type: Number,
			default: 100 // 单张图片大小限制(单位可自定义,如KB)
		}
	},
	methods: {
		// 点击上传:处理多图选择与批量上传
		startUpload() {
			// 剩余可上传数量(避免超过maxCount)
			const remainCount = this.maxCount - this.value.length;
			if (remainCount <= 0) return;

			uni.chooseImage({
				// 最多选择数量:multiple为true时取剩余数,false时最多1张
				count: this.multiple ? remainCount : 1,
				sizeType: ['original', 'compressed'],
				success: (res) => {
					const tempFilePaths = res.tempFilePaths; // 选中的所有图片路径
					const total = tempFilePaths.length; // 选中图片总数
					let completed = 0; // 已完成上传的数量(控制loading隐藏)

					// 显示loading:所有图片上传完再隐藏
					uni.showLoading({ title: `上传中(${completed}/${total})` });

					// 循环上传每张图片
					tempFilePaths.forEach((filePath) => {
						this.uploadSingleFile(filePath, () => {
							// 每张上传完成后更新计数与loading文本
							completed++;
							uni.showLoading({ title: `上传中(${completed}/${total})` });

							// 所有图片上传完,隐藏loading
							if (completed === total) {
								uni.hideLoading();
							}
						});
					});
				}
			});
		},

		// 单张图片上传:抽离为独立方法,便于循环调用
		uploadSingleFile(filePath, onComplete) {
			uni.uploadFile({
				url: upload, // 上传接口地址
				filePath: filePath,
				name: 'file', // 接口接收文件的参数名(需与后端一致)
				header: {
					accessToken: storage.getAccessToken() // 身份令牌
				},
				success: (res) => {
					const result = JSON.parse(res.data);
					// 单张上传成功:同步到父组件数组
					if (result.code === 200) {
						this.uploadSuccess(result.result);
					}
					// 单张上传失败:提示错误(不影响其他图片)
					else {
						this.$u.toast(`图片上传失败:${result.msg}`);
					}
				},
				fail: (err) => {
					console.error('单张上传失败:', err);
					this.$u.toast('图片上传失败,请重试');
				},
				complete: () => {
					// 无论成功/失败,都标记为"已完成"(更新计数)
					onComplete();
				}
			});
		},

		// 上传成功:通知父组件更新v-model数组
		uploadSuccess(imgUrl) {
			// 生成新数组(不直接修改props,遵循单向数据流)
			const newImgList = [...this.value, imgUrl];
			// 触发input事件,父组件v-model自动同步
			this.$emit('input', newImgList);
		},

		// 删除图片:同步更新父组件数组
		closeItem(index) {
			// 过滤掉要删除的图片,生成新数组
			const newImgList = this.value.filter((_, i) => i !== index);
			this.$emit('input', newImgList);
		}
	}
};
</script>

<style lang="scss" scoped>
.grid-img {
	display: flex;
	gap: 16rpx;
	flex-wrap: wrap;
	align-items: flex-start;
}

.img-view {
	position: relative;
	overflow: hidden;
	border-radius: 8rpx;
}

.imgs {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover; // 避免图片拉伸变形
}

.img-close {
	position: absolute;
	top: -8rpx;
	right: -8rpx;
	width: 32rpx;
	height: 32rpx;
	background-color: rgba(0, 0, 0, 0.6);
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 10; // 确保关闭按钮在最上层
}

.upload-view {
	background-color: #f6f6f6;
	border: 1px dashed #ddd;
	border-radius: 8rpx;
	display: flex;
	align-items: center;
	justify-content: center;
}
</style>

使用方式:

js 复制代码
<imgUpload
	v-model="feedBack.images"
	:maxCount="2"
	:wihe="['150rpx', '150rpx']"
></imgUpload>
相关推荐
Renounce3 小时前
【Android】从早期 MVC 到现代 MVVM 的架构变迁
前端
拉不动的猪3 小时前
回顾前端项目打包时--脚本引入时机与环境类型的判断问题
前端·vue.js·面试
比特记忆3 小时前
Android开发中用到的反射机制
前端
fox_3 小时前
一次搞懂柯里化:从最简单代码到支持任意函数,这篇让你不再踩参数传递的坑
前端·javascript
Keepreal4963 小时前
实现一个简单的hello world vs-code插件
前端·javascript·visual studio code
用户458203153173 小时前
CSS 层叠层 (@layer) 详解:控制样式优先级新方式
前端·css
月弦笙音3 小时前
【Vue组件】封装组件该考虑的核心点
前端·javascript·vue.js
清风细雨_林木木3 小时前
HttpOnly 是怎么防止 XSS 攻击的?
前端·xss
用户2519162427113 小时前
Node之单表基本查询
前端·javascript·node.js