vant封装视频上传组件

<!-- 
  * 上传视频和预览组件
  *使用方式:
<yh-uploadvideo code="jxhd" :limit="2" placeholder="请上传视频</br>(最多2条)" :grid="12" 
										v-model="formData.Video"></yh-uploadvideo>
	参数说明:
	grid://栅格占位 格数为0-24 用的是element自行联想 用来限制上传控件占位大小
	code://文件服务器必带的参数,其他地方用可以不加
	uploadpath://视频在文件服务器上存放目录
	filetypes://文件上传的类型如.png,.jpg,.jpeg,.webp,.gif
	limit://视频上传限制数量
	placeholder://上传控件上的文案
	disabled://是否禁止上传或者是用于浏览已上传的时候用true
	single://是否单个上传,形同limit=1
	fileRename://文件重命名定制项可不传
	category: //上传文件分类(0=系统普通文件,1=特定的目录,2=超越code的上传)
  *
 -->
<template>
	<div class="yh-uploadvideo">
		<div class="videoContent"v-if="fileList.length>0">
			<div class="video"v-for="(item, index) in fileList">
				<video controls="controls" class="pre-item-image" :src="FormatDFSUrl(item.url)" mode="aspectFill"></video>
				<div class="del u-f-ac u-f-ajc" @click="removeFun(index)" v-if="!disabled">
					<van-icon name="cross" color="#FFFFFF" />
				</div>
			</div>
		</div>
		<el-row :gutter="6">
			<el-col :span="gridNumber" v-if="fileList&&fileList.length<limitNumber&&!disabled">
				<van-uploader :multiple="limitNumber>1?true:false" :max-count="limitNumber" :after-read="afterRead" :accept="limitType">
					<template #default>
						<div class="slot-btn u-f-ac u-f-ajc" hover-class="slot-btn__hover" hover-stay-time="150">
							<div>
								<i class="el-icon-camera-solid"></i>
								<div class="placeholder" v-html="placeholderText"> </div>
							</div>
						</div>
					</template>
				</van-uploader>
			</el-col>
		</el-row>
	</div>
</template>

<script>
	import {
		ImagePreview
	} from 'vant';
	import {
		media_file_upload_url,
		FormatDFSUrl
	} from '@/api/learn.js'
	export default {
		name: "yh-uploadimage",
		data() {
			return {
				limitNumber: 2,
				showUploadList: false,
				placeholderText: "请上传视频",
				action: "", //上传地址
				limitType: ".mp4",
				upload_size: "10",
				currentCode: "", //文件服务器Code
				currentPath: "/upload", //文件存放目录
				fileList: [],
				gridNumber: 4
			}
		},
		props: {
			value: {
				type: String
			}, //传入的视频集合,多个之间用逗号分隔
			grid: {
				type: Number
			}, //栅格占位 格数为0-12
			code: {
				type: String
			}, //文件服务器Code
			uploadpath: {
				type: String,
				default: '/upload'
			}, //文件存放目录
			filetypes: {
				type: Array
			}, //文件类型,多个逗号分隔
			limit: {
				type: Number,
				default: 2,
			}, //上传文件数量限制
			placeholder: {
				type: String,
			}, //单个文件大小限制,单位M
			size: {
				type: String,
				default: "10"
			}, //单个文件大小限制,单位M
			disabled: {
				type: Boolean,
				default: false
			}, //是否禁用
			single: {
				type: Boolean,
				default: false
			}, //是否单个视频,相当于limit=1,但是优先级大于limit
			uploadcallback: {
				type: Function
			}, //上传完成的回调
			fileRename: {
				type: String,
			}, //文件重命名
			category: {
				type: Number
			}, //上传文件分类(0=系统普通文件,1=特定的目录,2=超越code的上传)
		},
		created() {
			this.initFun();
		},
		onPullDownRefresh() {},
		watch: {
			value() {
				this.uploadInit()
			},
			fileList() {
				let res = ""
				if (this.fileList && this.fileList.length) {
					this.fileList.map(el => {
						res += el.url + ","
					})
					res = res.substring(0, res.length - 1)
				}
				this.$emit("input", res);
			},
		},
		methods: {
			afterRead(file) {
				console.log(file)
				let files = file;
				if (files instanceof Array) {
					files.map(el => {
						this.imgUpload(el.file)
					})
				} else {
					this.imgUpload(files.file)
				}
			},
			imgUpload(file) {
				const formData = new FormData()
				formData.append('file', file)

				media_file_upload_url(this.action, formData).then(res => {
					if (res.success) {
						if (this.fileList.length < this.limitNumber) {
							this.fileList.push({
								url: res.response.path
							})
						} else {
							this.$toast('上传数量已达上限')
						}
					} else {
						this.notify({
							type: 'warning',
							message: res.response.msg
						});
					}

				})
			},
			compress(img, Orientation) {
				const canvas = document.createElement('canvas')
				const ctx = canvas.getContext('2d')
				// 瓦片canvas
				const tCanvas = document.createElement('canvas')
				const tctx = tCanvas.getContext('2d')
				const initSize = img.src.length
				let width = img.width
				let height = img.height
				// 如果视频大于四百万像素,计算压缩比并将大小压至400万以下
				let ratio
				if ((ratio = width * height / 4000000) > 1) {
					console.log('大于400万像素')
					ratio = Math.sqrt(ratio)
					width /= ratio
					height /= ratio
				} else {
					ratio = 1
				}
				canvas.width = width
				canvas.height = height
				// 		铺底色
				ctx.fillStyle = '#fff'
				ctx.fillRect(0, 0, canvas.width, canvas.height)
				// 如果视频像素大于100万则使用瓦片绘制
				let count
				if ((count = width * height / 1000000) > 1) {
					console.log('超过100W像素')
					count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
					//            计算每块瓦片的宽和高
					const nw = ~~(width / count)
					const nh = ~~(height / count)
					tCanvas.width = nw
					tCanvas.height = nh
					for (let i = 0; i < count; i++) {
						for (let j = 0; j < count; j++) {
							tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh)
							ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
						}
					}
				} else {
					ctx.drawImage(img, 0, 0, width, height)
				}
				// 修复ios上传视频的时候 被旋转的问题
				if (Orientation != '' && Orientation != 1) {
					switch (Orientation) {
						case 6: // 需要顺时针(向左)90度旋转
							this.rotateImg(img, 'left', canvas)
							break
						case 8: // 需要逆时针(向右)90度旋转
							this.rotateImg(img, 'right', canvas)
							break
						case 3: // 需要180度旋转
							this.rotateImg(img, 'right', canvas) // 转两次
							this.rotateImg(img, 'right', canvas)
							break
					}
				}
				// 进行最小压缩
				const ndata = canvas.toDataURL('image/jpeg', 0.1)
				/* console.log('压缩前:' + initSize)
				console.log('压缩后:' + ndata.length)
				console.log('ndata:' + ndata)
				console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + '%') */
				tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
				return ndata
			},
			uploadSectionFile(f) { //	附件上传
				const self = this
				let Orientation
				let ndata
				if (f.file.size <= 1 * 1024 * 1024) {
					// 判断视频是否大于1M,是就直接上传
					ndata = f.file
					self.imgUpload(ndata)
				} else {
					// 反之压缩视频
					const reader = new FileReader()
					// 将视频2将转成 base64 格式
					reader.readAsDataURL(f.file)
					console.log(reader)
					// 读取成功后的回调
					reader.onloadend = function() {
						const result = this.result
						const img = new Image()
						img.src = result
						img.onload = function() {
							ndata = self.compress(img, Orientation)
							// BASE64转视频
							var arr = ndata.split(',');
							var mime = arr[0].match(/:(.*?);/)[1]
							var bstr = atob(arr[1]);
							var n = bstr.length;
							var u8arr = new Uint8Array(n)
							while (n--) {
								u8arr[n] = bstr.charCodeAt(n)
							}
							ndata = new File([u8arr], f.file.name, {
								type: mime
							})
							// self.ndata = ndata
							self.imgUpload(ndata)
						}
					}
				}
			},
			FormatDFSUrl(path) {
				return FormatDFSUrl(path)
			},
			uploadInit() {
				if (this.value) {
					let curFilePathList = this.value.split(",");
					if (curFilePathList) {
						let fileListArr = [];
						for (let i = 0; i < curFilePathList.length; i++) {
							let item = curFilePathList[i];
							fileListArr.push({
								url: item,
							});
						}
						this.fileList = fileListArr;
					}
				} else {
					this.fileList = [];
				}

			},
			initFun() {
				this.uploadInit();
				if (this.grid) {
					this.gridNumber = this.grid;
				}
				if (this.limit) {
					this.limitNumber = this.limit;
				}
				if (this.single) {
					this.limitNumber = 1
				}
				if (this.size) {
					this.upload_size = this.size * 1024 * 1024;
				}
				if (this.placeholder) {
					this.placeholderText = this.placeholder;
				}
				if (this.filetypes) {
					this.limitType = this.filetypes;
				}
				if (this.code) {
					this.currentCode = this.code;
				} else {
					this.notify({
						type: 'warning',
						message: "[视频上传]未传入系统Code"
					});
					return;
				}
				if (this.uploadpath) {
					this.currentPath = this.uploadpath;
				}
				let $fileReName = '';
				if (this.fileRename)
					$fileReName = `&fileRename=${this.fileRename}`;
				this.action =
					`?code=${this.currentCode}&path=${this.currentPath}&category=${this.category}${$fileReName}`;
			},
			removeFun(index) {
				this.$dialog.confirm({
						title: '提示',
						message: '是否要删除该视频'
					})
					.then(() => {
						this.fileList.splice(index, 1);
					})
					.catch(() => {});
			},//删除视频
			previewImage(index) {
				let fileList = [];
				this.fileList.map(el => {
					fileList.push(this.FormatDFSUrl(el.url))
				})
				ImagePreview({
					images: fileList,
					startPosition: index,
				});

			},//预览视频
		}
	}
</script>

<style lang="scss" scoped>
	.col {
		margin-top: 10px;
	}

	/deep/ .u-upload {
		height: 180px;
		overflow: hidden;
		width: 100%;
		margin-top: -12px;
	}

	/deep/ .u-upload>uni-view {
		width: 100%;
		height: 100%;
		text-align: center;
	}

	/deep/ .van-uploader {
		width: 100%;

		.van-uploader__wrapper {
			width: 100%;

			.van-uploader__input-wrapper {
				width: 100%;
			}
		}
	}

	.slot-btn {
		width: 100%;
		height: 180px;
		background: rgb(244, 245, 246);
		text-align: center;
		padding: 0 20px;

		.el-icon-camera-solid {
			font-size: 60px;
			font-family: PingFang-SC-Medium, PingFang-SC;
			font-weight: 500;
			color: #CCCCCC;
		}

		.placeholder {
			font-size: 24px;
			font-family: PingFang-SC-Medium, PingFang-SC;
			font-weight: 500;
			color: #CCCCCC;
		}
	}

	.slot-btn__hover {
		background-color: rgb(235, 236, 238);
	}

	.pre-box {
		display: flex;
		align-items: center;
		justify-content: space-between;
		flex-wrap: wrap;
	}

	.pre-item {
		height: 180px;
		position: relative;
	}

	.pre-item-image {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}

	.del {
		position: absolute;
		top: -8px;
		right: -10px;
		z-index: 11;
		background: red;
		width: 32px;
		height: 32px;
		border-radius: 50%;
		text-align: center;
		font-size: 24px;
	}
	.videoContent {
		
	
		.video {position: relative;
		background: black;
		margin-bottom: 10px;
		height: 400px;
			/deep/ uni-video {
				width: 100%;    height: 200px;
			}
		}
	
		.pre-item-image {}
	
		.del {
			position: absolute;
			top: -8rpx;
			left: z;
			right: -10rpx;
			z-index: 11;
			background: red;
			width: 32rpx;
			height: 32rpx;
			border-radius: 50%;
			text-align: center;
		}
	}
	
	.slot-btn {
		height: 180rpx;
		background: rgb(244, 245, 246);
		text-align: center;
	
		.placeholder {
			font-size: 24rpx;
			font-family: PingFang-SC-Medium, PingFang-SC;
			font-weight: 500;
			color: #CCCCCC;
		}
	}
</style>

猜你喜欢

转载自blog.csdn.net/wgb0409/article/details/122497103
今日推荐