<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 == '查看详情'" />