<template>
<div class="upload_container">
<div class="igmBox">
<div class="img_box" v-for="(item, index) in data" :key="item.url">
<img :src="getFileUrl(item)" @click="handleclicksc(index)">
<div class="delete_btn"> <van-icon name="cross" class="" @click="afterDelete(index)" /> </div>
</div>
</div>
<van-uploader v-bind="uploadParameter" :after-read="afterRead" :before-read="beforeRead" @oversize="onOversize"
upload-icon="plus" :preview-full-image="false" v-show="isUploadShow" ref="uploadRef">
<template #default>
<div></div>
</template>
</van-uploader>
<div class="uploadBox">
<div @click="$refs.uploadRef.chooseFile()" class="chooseFile"><i
class="iconfont icon-zengjia"></i><span>图片/视频</span></div>
<div v-longpress="handleAudio" class="chooseFile" @touchend="touchendBtn"><i
class="iconfont icon-zengjia"></i><span>语音输入</span></div>
<!-- 语音音阶动画 -->
<div class="prompt-layer prompt-layer-1" v-if="isLongPress">
<div class="prompt-loader">
<div class="em" v-for="(item, index) in 15" :key="index"></div>
</div>
<!-- <div class="p">{{ '剩余:' + count + 's' }}</div> -->
<div class="span">松手结束录音</div>
</div>
</div>
<PreviewImgOrVideo :previewShow="previewShow" @close="$event => previewShow = $event" :data="preViewData"
v-if="previewShow">
</PreviewImgOrVideo>
</div>
</template>
<script>
import { mapGetters } from "vuex";
import videoIcon from '../images/video_icon.png';
import audioIcon from '../images/audio_icon.png';
import { getNewFileUrl } from "@/utils/download";
import { filesExperts } from '@/api/appyjzj/newIndex';
import PreviewImgOrVideo from './previewImgOrVideo.vue';
export default {
directives: {
longpress: {
bind: function (el, binding) {
// 定义长按时间阈值
let pressTimer = null
let longPressActive = false
// 创建计时器(这会在长按后启动)
let start = (e) => {
if (e.type === 'click' && e.button !== 0) {
return
}
if (pressTimer === null) {
pressTimer = setTimeout(() => {
handler()
}, 500)
}
}
// 取消计时器(长按结束时会运行)
let cancel = (e) => {
if (pressTimer !== null) {
clearTimeout(pressTimer)
pressTimer = null
}
}
// 长按要运行的函数
const handler = (e) => {
binding.value(e)
}
// 添加事件监听器
el.addEventListener("mousedown", start)
el.addEventListener("touchstart", start)
// 取消计时器
el.addEventListener("click", cancel)
el.addEventListener("mouseout", cancel)
el.addEventListener("touchend", cancel)
el.addEventListener("touchcancel", cancel)
}
}
},
components: {
PreviewImgOrVideo
},
props: {
data: {
type: Array,
default: () => []
},
uploadParameter: {
type: Object,
default: () => { }
}
},
model: {
prop: 'data',
event: 'change'
},
data() {
return {
previewShow: false,
preViewData: {},
loading: false,
mediaRecorder: null,
isLongPress: false,
recordedBlob: false
};
},
computed: {
...mapGetters(["userInfo"]),
isUploadShow() {
return this.uploadParameter['max-count'] && this.uploadParameter['max-count'] > this.data.length
}
},
methods: {
getNewFileUrl,
isVideo(filename) {
const videoExtensions = ['mp4', 'mov', 'avi', 'mkv', 'MOV'];
const extension = filename.split('.').pop();
return videoExtensions.includes(extension);
},
isAudio(filename) {
const audioExtensions = ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'];
const extension = filename.split('.').pop();
return audioExtensions.includes(extension);
},
getFileUrl(file) {
if (this.isVideo(file.name)) {
return videoIcon;
} else if (this.isAudio(file.name)) {
return audioIcon;
} else {
return this.getNewFileUrl(file.url);
}
},
// 上传附件
async afterRead(file) {
this.loading = true
file.status = "uploading";
file.message = "上传中...";
const formData = new FormData();
formData.append("singleFile", file.file);
try {
const { data, code } = await filesExperts(formData);
if (code === 2000) {
this.data.push({
name: data.fileName,
uid: data.id,
url: data.location
});
file.status = "done";
file.message = "上传成功";
console.log('this.from', this.formData)
}
} catch (err) {
console.log('err', err)
file.status = "failed";
file.message = "上传失败";
} finally {
this.loading = false
}
},
beforeRead(file) {
if (this.loading) return false
if (!file.type.startsWith('image/') && !file.type.startsWith('video/')) {
this.$toast('请上传图片或视频文件');
return false;
}
return true;
},
//手动点击删除,修改包含所有信息的文件列表,通过watch根据该列表动态修改图片文件列表
afterDelete(index) {
this.data.splice(index, 1)
},
onOversize(file) {
this.$toast('超过大小');
},
//取消掉组件自带的点击预览功能,自己添加(系统自带预览点击视频时会先视频的播放图片)
handleclicksc(index) {
let file = this.data[index]
const name = file.name;
const fileExtension = name.split(".").pop().toLowerCase();
const isImage = ["jpg", "jpeg", "png", "gif", "bmp"].includes(fileExtension);
const isVideo = ["mp4", "mov", "avi", "mkv", 'MOV'].includes(fileExtension);
if (isVideo) {
this.preViewData = {
url: getNewFileUrl(file.url),
urlType: 'video'
}
}
if (isImage) {
this.preViewData = {
url: getNewFileUrl(file.url),
urlType: 'image'
}
}
this.previewShow = true
},
async handleAudio() {
this.isLongPress = true // 开始长按
// 开始录音
if (this.isLongPress) {
console.log('navigator', navigator)
console.log('navigator.mediaDevices', navigator.mediaDevices)
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
this.mediaRecorder = new MediaRecorder(stream);
this.mediaRecorder.addEventListener('dataavailable', event => {
console.log('event', event.data)
this.recordedBlob = event.data;
});
this.mediaRecorder.start();
})
.catch(error => {
// 用户拒绝访问麦克风
});
}
},
async touchendBtn() {
this.isLongPress = false // 结束长按
this.mediaRecorder.stop();
this.mediaRecorder.addEventListener('stop', async () => {
// dataavailable 会在这里被触发
try {
const formData = new FormData();
formData.append("singleFile", this.recordedBlob, `${this.userInfo.profile.name}的录音.mp3`);
const { data, code } = await filesExperts(formData);
if (code === 2000) {
this.data.push({
name: data.fileName,
uid: data.id,
url: data.location
});
}
} catch (err) {
console.log('err', err)
} finally {
this.loading = false
this.mediaRecorder = null;
}
})
}
}
}
</script>
<style scoped lang="scss">
.upload_container {
margin-top: 7px;
.igmBox {
display: grid;
grid-template-columns: repeat(auto-fill, 80px);
grid-gap: 10px;
}
width: 100%;
}
.img_box {
width: 80px;
height: 80px;
position: relative;
img {
width: 100%;
height: 100%;
}
.delete_btn {
position: absolute;
top: 0;
right: 0;
width: 14px;
height: 14px;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 0 0 0 12px;
.van-icon-cross {
position: absolute;
top: -2px;
right: -2px;
color: #fff;
font-size: 16px;
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
}
}
.uploadBox {
position: relative;
all: unset;
display: flex;
justify-content: flex-end;
.chooseFile {
margin-left: 16px;
i {
margin-right: 4px;
font-size: 16px;
color: #1890FF;
}
span {
font-size: 13px;
font-family: PingFangSC-Regular, PingFang SC;
color: #1890FF;
}
}
/* 提示小弹窗 */
.prompt-layer {
border-radius: 8px;
background: #f6f6f6;
padding: 8px 16px;
box-sizing: border-box;
position: absolute;
left: 50%;
top: 50%;
z-index: 999;
transform: translate(-50%, -50%);
// box-shadow: 5px rgba(0, 0, 0, .5);
}
// .prompt-layer::after {
// content: '';
// display: block;
// border: 6px solid rgba(0, 0, 0, 0);
// border-top-color: rgba(255, 211, 0, 1);
// position: absolute;
// bottom: -10px;
// left: 50%;
// transform: translateX(-50%);
// }
.prompt-layer-1 {
font-size: 12px;
width: 128px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.prompt-layer-1 .p {
color: #000000;
}
.prompt-layer-1 .span {
color: rgba(0, 0, 0, .6);
}
}
/* 语音音阶------------- */
.prompt-loader {
width: 96px;
height: 20px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.prompt-loader .em {
display: block;
background: #333333;
width: 1px;
height: 10%;
margin-right: 2.5px;
float: left;
}
.prompt-loader .em:last-child {
margin-right: 0px;
}
.prompt-loader .em:nth-child(1) {
animation: load 2.5s 1.4s infinite linear;
}
.prompt-loader .em:nth-child(2) {
animation: load 2.5s 1.2s infinite linear;
}
.prompt-loader .em:nth-child(3) {
animation: load 2.5s 1s infinite linear;
}
.prompt-loader .em:nth-child(4) {
animation: load 2.5s 0.8s infinite linear;
}
.prompt-loader .em:nth-child(5) {
animation: load 2.5s 0.6s infinite linear;
}
.prompt-loader .em:nth-child(6) {
animation: load 2.5s 0.4s infinite linear;
}
.prompt-loader .em:nth-child(7) {
animation: load 2.5s 0.2s infinite linear;
}
.prompt-loader .em:nth-child(8) {
animation: load 2.5s 0s infinite linear;
}
.prompt-loader .em:nth-child(9) {
animation: load 2.5s 0.2s infinite linear;
}
.prompt-loader .em:nth-child(10) {
animation: load 2.5s 0.4s infinite linear;
}
.prompt-loader .em:nth-child(11) {
animation: load 2.5s 0.6s infinite linear;
}
.prompt-loader .em:nth-child(12) {
animation: load 2.5s 0.8s infinite linear;
}
.prompt-loader .em:nth-child(13) {
animation: load 2.5s 1s infinite linear;
}
.prompt-loader .em:nth-child(14) {
animation: load 2.5s 1.2s infinite linear;
}
.prompt-loader .em:nth-child(15) {
animation: load 2.5s 1.4s infinite linear;
}
@keyframes load {
0% {
height: 10%;
}
50% {
height: 100%;
}
100% {
height: 10%;
}
}
/* 语音音阶-------------------- */
.prompt-layer-2 {
top: -40px;
}
.prompt-layer-2 .text {
color: rgba(0, 0, 0, 1);
font-size: 12px;
}
/deep/.van-uploader {
width: 100%;
.van-uploader__wrapper,
.van-uploader__input-wrapper {
width: 100%;
}
}
</style>
vue2(H5录音)
最新推荐文章于 2024-08-29 11:58:20 发布