插件地址
可能遇到问题 uni-app 打包成安卓应用后 长按录音@touchend 不执行
插件二次修改,因为涉及到权限问题,所以在源插件的基础上增加了权限的判断。使用到的permission.js在这里查看
插件二次修改
<template>
<div>
<div @click="open">
<slot></slot>
</div>
<u-popup :show="show" @close="close" @open="open">
<view class="recorder">
<view class="re-top" v-if="showTop">
<view class="re-cancel" @click="cancel">取消</view>
<view class="re-confirm" :style="{color: theme}" @click="confirm">{{ confirmText }}</view>
</view>
<text class="title">{{ finish ? '点击播放' : '长按录制语音' }}</text>
<view class="recorder-box" v-if="!finish" @click="handle" @longpress="onStartRecoder"
@touchend="onEndRecoder">
<u-circle-progress2 :active-color="theme" :duration="0" :percent="calcProgress">
<view class="u-progress-content">
<image src="/static/sound-recording/voice.png" mode="aspectFit" :style="{
width: width,
height: height
}"></image>
</view>
</u-circle-progress2>
</view>
<view class="recorder-box" v-else @click="playVoice">
<u-circle-progress2 :active-color="theme" :duration="0" :percent="playProgress">
<view class="u-progress-content">
<image src="/static/sound-recording/play.png" mode="aspectFit" :style="{
width: width,
height: height
}" v-if="!playStatus"></image>
<image src="/static/sound-recording/pause.png" mode="aspectFit" :style="{
width: width,
height: height
}" v-else></image>
</view>
</u-circle-progress2>
</view>
<text class="now-date">{{ reDate }}</text>
<view @click="reset">重新录制</view>
</view>
</u-popup>
</div>
</template>
<script>
const recorderManager = uni.getRecorderManager();
const innerAudioContext = uni.createInnerAudioContext();
import permision from "@/utils/permission.js"
export default {
components: {},
props: {
width: {
type: String,
default: '60rpx'
},
height: {
type: String,
default: '60rpx'
},
showTop: {
type: Boolean,
default: true
},
maximum: {
type: [Number, String],
default: 15
},
duration: {
type: Number,
default: 20
},
theme: {
type: String,
default: '#32b99d'
},
confirmText: {
type: String,
default: '完成'
}
},
data() {
return {
reDate: '00:00',
sec: 0,
min: 0,
finish: false,
voicePath: '',
playProgress: 100,
playTimer: null,
timer: null,
playStatus: false,
show: false
};
},
created() {
// 监听
this.onMonitorEvents()
},
computed: {
// 录制时间计算
calcProgress() {
return (this.sec + (this.min * 60)) / this.maximum * 100
}
},
methods: {
open() {
let env = uni.getSystemInfoSync().platform
if (env === 'android') {
permision.requestAndroidPermission('android.permission.RECORD_AUDIO').then((e) => {
if (e === -1) {
uni.showToast({
title: '您已经永久拒绝录音权限,请在应用设置中手动打开',
icon: 'none',
})
} else if (e === 0) {
uni.showToast({
title: '您拒绝了录音授权',
icon: 'none',
})
} else if (e === 1) {
this.show = true
} else {
uni.showToast({
title: '授权返回值错误',
icon: 'none',
})
}
}).catch((err) => {
uni.showToast({
title: '拉起录音授权失败',
icon: 'none',
})
})
} else if (env === 'ios') {
if (permision.judgeIosPermission("record"))
this.show = true
else
uni.showToast({
title: '您拒绝了录音授权,请在应用设置中手动打开',
icon: 'none',
})
}
},
close() {
this.show = false
},
// 完成事件
confirm() {
if (!innerAudioContext.paused) {
innerAudioContext.stop()
}
this.$emit('confirm', this.voicePath)
this.close()
},
// 取消事件
cancel() {
if (!innerAudioContext.paused) {
innerAudioContext.stop()
}
this.$emit('cancel')
this.close()
},
// 点击事件
handle() {
this.$emit('click')
},
// 重新录制
reset() {
this.voicePath = ''
this.min = 0
this.sec = 0
this.reDate = '00:00'
this.playProgress = 100
this.finish = false
this.$emit('reset')
},
// 播放暂停录音
playVoice() {
innerAudioContext.src = this.voicePath;
if (innerAudioContext.paused) {
innerAudioContext.play()
this.playStatus = true
} else {
innerAudioContext.stop();
}
this.$emit('playVoice', innerAudioContext.paused)
},
// 录制结束
onEndRecoder() {
recorderManager.stop()
},
// 开始录制
onStartRecoder() {
recorderManager.start({
duration: this.maximum * 1000
})
},
// 监听
onMonitorEvents() {
// 录制开始
recorderManager.onStart(() => {
// uni.showLoading({
// title: '录制中...',
// mask: false
// })
this.startDate()
this.$emit('start')
})
// 录制结束
recorderManager.onStop(({
tempFilePath
}) => {
this.voicePath = tempFilePath
clearInterval(this.timer)
uni.hideLoading()
this.finish = true
this.$emit('end')
})
// 播放进度
innerAudioContext.onTimeUpdate(() => {
let totalDate = innerAudioContext.duration
let nowTime = innerAudioContext.currentTime
let surplus = totalDate - nowTime
this.playProgress = surplus / totalDate * 100
let _min = Math.floor(surplus / 60)
if (_min < 10) _min = '0' + _min;
let _sec = Math.floor(surplus % 60)
if (_sec < 10) _sec = '0' + _sec;
this.reDate = _min + ':' + _sec
})
// 播放暂停
innerAudioContext.onPause(() => {
this.resetDate()
this.playProgress = 100
this.playStatus = false
console.log('播放暂停')
this.$emit('stop')
})
// 播放停止
innerAudioContext.onStop(() => {
this.resetDate()
this.playProgress = 100
this.playStatus = false
console.log('播放停止')
this.$emit('stop')
})
},
// 录音计时
startDate() {
clearInterval(this.timer)
this.sec = 0
this.min = 0
this.timer = setInterval(() => {
this.sec += this.duration / 1000
if (this.sec >= 60) {
this.min++
this.sec = 0
}
this.resetDate()
}, this.duration)
},
// 播放时间
resetDate() {
let _s = this.sec < 10 ? '0' + parseInt(this.sec) : parseInt(this.sec)
let _m = this.min < 10 ? '0' + this.min : this.min
this.reDate = _m + ':' + _s
}
}
}
</script>
<style lang="scss">
.recorder {
position: relative;
display: flex;
align-items: center;
flex-direction: column;
background-color: #fff;
font-size: 24rpx;
width: 100%;
padding-bottom: 30rpx;
.re-top {
display: flex;
justify-content: space-between;
padding: 10rpx 20rpx;
width: 100%;
font-size: 28rpx;
box-sizing: border-box;
}
.title {
font-size: 36rpx;
color: #333;
padding: 20rpx 0 30rpx;
}
.recorder-box {
position: relative;
}
.now-date {
font-size: 28rpx;
color: #666;
padding: 20rpx 0;
}
}
</style>
插件使用
<sound-recording @confirm="getRecording">
<u-cell title="录音" :isLink="true" value="开始录音">
<view slot="value">
{{audio?'重新录制':'开始录音'}}
</view>
</u-cell>
</sound-recording>
getRecording(e) {
console.log(e)
this.audio = e
},