H5上传图片/文件组件封装

2 篇文章 0 订阅
<template>
	<van-uploader v-if="shouldShowUploader" v-model="imagePathList" :multiple="multiple" :accept="accept"
		:max-count="imgCount" :after-read="handleRead" :disabled="disabled" :deletable="!disabled" @delete="handleDelete" />
	<video class="video" v-if="shouldShowVideo" controls :src="videoUrl"></video>
	<div class="deleteVideo" v-if="shouldShowDeleteVideo" @click="handleDelete">x</div>
</template>

<script setup lang="ts">
	import axios from 'axios';
	import { Toast } from 'vant';
	import { ossPath, baseUrl } from '../../utils/http/axiosKeyConfig';

	const props = defineProps({
		imagepath: {
			type: Array,
			default: () => []
		},
		multiple: {
			type: Boolean,
			default: false
		},
		imgCount: {
			type: Number,
			default: 1
		},
		allowedTypes: {
			type: Array,
			default: () => ['image/jpeg', 'image/jpg', 'image/png']
		},
		accept: {
			type: String,
			default: "video/*"
		},
		disabled: {
			type: Boolean,
			default: false
		}
	});

	const imagePathList = ref<any[]>([]);//上传的文件路径
	const imagePathValue = ref('');//当前上传的文件路径

	const handleRead = (files : any) => {
		files.status = 'uploading';
		files.message = '上传中...';
		if (props.accept === 'image/*') {
			uploadImage(files);
		} else {
			uploadVideo(files);
		}
	};

	/**
	 * 删除文件		重置路径变量
	 */
	const handleDelete = () => {
		imagePathValue.value = '';
		imagePathList.value = [];
	};

	/**
	 * 图片上传
	 */
	const uploadImage = async (files : any) => {
		const file = files.file;
		if (!props.allowedTypes.includes(file.type)) {
			Toast('只能上传JPEG、JPG或PNG格式的图片');
			handleDelete();
			return;
		}

		const formData = new FormData();
		formData.append('file', file);

		const data = {
			ossEnum: 'STORE_INFO_IMG_PATH',
			file: formData.get('file')
		};

		const token = getToken();
		if (!token) return;

		try {
			const response = await axios.post(
				`${baseUrl}/api/own/upload/image`,//换成自己的上传图片接口
				data,
				{
					headers: getHeaders(token)
				}
			);
			handleResponse(response, files);
		} catch {
			handleError();
		}
	};

	/**
	 * 视频上传
	 */
	const uploadVideo = async (files : any) => {
		const file = files.file;
		const isValid = await beforeRead(file);
		if (!isValid) return;

		const formData = new FormData();
		formData.append('file', file);

		const data = {
			ossEnum: 'STORE_INFO_VIDEO_PATH',
			file: formData.get('file')
		};

		const token = getToken();
		if (!token) return;

		try {
			const response = await axios.post(
				`${baseUrl}/api/own/upload/video`,//换成自己的上传视频接口
				data,
				{
					headers: getHeaders(token)
				}
			);
			handleResponse(response, files);
		} catch {
			handleError();
		}
	};

	/**
	 * 文件读取前的校验
	 */
	const beforeRead = async (file : any) => {
		if (props.accept === 'video/*') {
			const extension = file.name.split('.').pop().toLowerCase();
			const allowedExtensions = ['avi', 'mpeg', 'mp4', 'mov'];

			if (!allowedExtensions.includes(extension)) {
				Toast('只能上传AVI、MPEG、MP4或MOV格式的视频');
				handleDelete();
				return false;
			}

			if (file.size > 50 * 1024 * 1024) {
				Toast('文件大小不能超过50MB');
				handleDelete();
				return false;
			}

			return validateVideoDuration(file);
		}
		return true;
	};

	/**
	 * 验证上传的视频是否符合所需的时间要求
	 */
	const validateVideoDuration = (file : any) => {
		return new Promise((resolve) => {
			const video = document.createElement('video');
			video.preload = 'metadata';
			video.onloadedmetadata = () => {
				window.URL.revokeObjectURL(video.src);
				if (video.duration < 10) {
					Toast('视频时长必须超过10秒');
					handleDelete();
					resolve(false);
				} else {
					resolve(true);
				}
			};
			video.src = URL.createObjectURL(file);
		});
	};

	const getToken = () => {
		const token = JSON.parse(localStorage.getItem('token') as string)?.value;
		if (!token) {
			Toast('上传失败,请先登录');
			return null;
		}
		return token;
	};

	const getHeaders = (token : string) => {
		return {
			'Content-Type': 'multipart/form-data',
			token,
			os: 'h5',
			osType: 'h5',
			businessType: 'admin',
		};
	};

	/**
	 * 图片/视频上传成功后的响应
	 */
	const handleResponse = (response : any, files : any) => {
		if (response.data.status != 10000) {
			Toast(response.data.msg);
			handleDelete();
		} else {
			imagePathValue.value = response.data.data;
			imagePathList.value = [{ url: ossPath + response.data.data }];
			files.status = 'success';
		}
	};

	/**
	 * 上传失败的情况处理
	 */
	const handleError = () => {
		Toast('上传失败');
		handleDelete();
	};

	watch(
		() => props.imagepath,
		(newVal : any) => {
			imagePathList.value = newVal;
			imagePathValue.value = newVal[0]?.url;
		}
	);

	const isVideo = computed(() => {
		if (imagePathList.value.length > 0) {
			const url = imagePathList.value[0].url?.toLowerCase();
			return ['avi', 'mpeg', 'mp4', 'mov'].some(ext => url?.endsWith(ext));
		}
		return false;
	});

	/** 获取视频的地址拼接访问域名 */
	const videoUrl = computed(() => {
		return imagePathValue.value.includes(ossPath) ? imagePathValue.value : ossPath + imagePathValue.value;
	});

	/** 判断是否显示上传组件 */
	const shouldShowUploader = computed(() => {
		return imagePathList.value.length == 0 || imagePathList.value[0].file || !isVideo.value;
	});

	/** 控制上传视频的时候是否显示上传组件 */
	const shouldShowVideo = computed(() => {
		return imagePathList.value.length != 0 && !imagePathList.value[0].file && isVideo.value;
	});

	/** 控制删除视频功能是否显示 */
	const shouldShowDeleteVideo = computed(() => {
		return imagePathList.value.length != 0 && !imagePathList.value[0].file && isVideo.value && !props.disabled;
	});

	defineExpose({
		imagePath: imagePathValue
	});
</script>

<style scoped lang="less">
	.van-uploader {
		width: 65px;
		height: 65px;
		margin-right: 3px;

		::v-deep .van-image {
			width: 65px !important;
			height: 65px !important;
		}

		::v-deep .van-uploader__upload {
			height: 65px !important;
		}

		::v-deep .van-uploader__file {
			width: 65px !important;
			height: 65px !important;
		}
	}

	.video {
		width: 300px;
		height: 100px;
	}

	.deleteVideo {
		position: relative;
		top: -40px;
		right: 20px;
		padding: 5px;
	}
</style>

使用示例

<up-load-images ref="upImageRef" accept="image/*" :imagepath='formData.idCardPositivePath1'
					:disabled="name == '查看详情'" />

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值