<!--
* 上传视频和预览组件
*使用方式:
<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>