- test.vue 调用页面
<template>
<view>
<add-sound-recording :voiceList="voiceList"></add-sound-recording>
</view>
</template>
<script>
import addSoundRecording from '@/components/add-sound-recording.vue'
export default {
components: {
addSoundRecording
},
data() {
return {
voiceList: []
};
}
};
</script>
<style lang="scss" scoped>
</style>
- add-sound-recording.vue 录音组件 (u-form-item 是uview里的组件,可换成view)
<template>
<view>
<view class="content-box">
<u-form-item label="语音留言">
<view class="sound-lay">
<view class="sound-item" @click="isShow=!isShow">
<text>添加</text>
<image class="sound-recording" src="../static/sound_recording.png" mode=""></image>
</view>
</view>
</u-form-item>
<view class="custom-audio-list">
<template v-for="(item,index) in voiceList">
<view class="custom-audio-item">
<custom-audio :music="item.url" :musicID="item.id"></custom-audio>
<view class="u-delete-icons" @click="deleteVoice(index)">
<u-icon name="close" size="20" color="#ffffff"></u-icon>
</view>
</view>
</template>
</view>
</view>
<view v-if="isShow" id="cover" @click="closeVoice"></view>
<view v-if="isShow" class="voice-lay">
<view class="voice-title">
<text>{{title}}</text>
</view>
<view class="voice-time">{{$utils.getMinuteTime(voiceTime)}}</view>
<view class="viice-btn">
<view v-if="status == 0" class="viice-start-cor" @click="soundRecording(0)"></view>
<view v-if="status == 1" class="viice-stop-cor" @click="soundRecording(1)"></view>
<view v-if="status == 2" class="" @click="soundRecording(2)">
<image v-if="!music_play" class="play-btn-img" src="../static/play.png" mode=""></image>
<image v-if="music_play" class="play-btn-img" src="../static/stop.png" mode=""></image>
</view>
</view>
<view v-if="status == 2" class="submit-btns-lay">
<view class="cancel-btn" @click="closeVoice">取消</view>
<view class="submit-btn" @click="submitVoice">上传</view>
</view>
</view>
</view>
</template>
<script>
import customAudio from '@/components/custom-audio.vue'
export default {
name: 'add-sound-recording',
components: {
customAudio
},
props: {
voiceList: {
type: Array,
default: []
}
},
data() {
return {
isShow: 0,
status: 0, // 0 : 未开始 1:录音中 2 : 录制完成
title: '点击开始录音',
voiceTime: 0,
voicePath: '', //录音的文件路径
music_play: false,
uploadUrl: 'https://www.hbwh.com/api/uploadfile',//上传音频文件的地址
}
},
mounted() {
this.recorderManager = uni.getRecorderManager();
},
methods: {
deleteVoice(index) {
this.voiceList.splice(index, 1);
},
closeVoice() {
this.isShow = 0;
this.voiceTime = 0;
this.status = 0;
this.title = '点击开始录音';
this.music_play = false;
clearInterval(this.timeupdata);
},
soundRecording(status) {
if (status == 0) {
//开始录音
const options = { //参数
duration: 600000,
sampleRate: 44100,
numberOfChannels: 1,
encodeBitRate: 192000,
format: 'mp3',
frameSize: 50
}
this.recorderManager.start(options);
this.timeupdata = setInterval(() => {
this.voiceTime++
}, 1000);
this.status = 1;
this.title = '点击结束录音';
} else if (status == 1) {
//结束录音
this.status = 2;
this.title = '录制完成';
clearInterval(this.timeupdata);
this.recorderManager.stop();
this.recorderManager.onStop(res => {
this.voicePath = res.tempFilePath; //记录文件路径
});
} else if (status == 2) {
//录制完成, 点击播放录音
this.innerAudioContext = uni.createInnerAudioContext();
this.innerAudioContext.src = this.voicePath;
if (this.music_play) {
this.music_play = false;
this.innerAudioContext.pause();
} else {
this.music_play = true;
this.innerAudioContext.play();
}
this.innerAudioContext.onEnded(() => {
this.music_play = false;
})
}
},
submitVoice() {
// console.log(this.voicePath);
uni.showLoading({
title: '上传中...'
})
uni.uploadFile({
url: this.uploadUrl,
filePath: this.voicePath,
name: 'file',
header: {
Authorization: this.strToken
},
success: (res) => {
this.voiceList.push({
id: res.data.uid,
url: res.data.url
})
},
fail: (uploadFileErr) => {
console.log(uploadFileErr);
},
complete: () => {
uni.hideLoading();
this.closeVoice();
}
});
}
}
}
</script>
<style lang="scss" scoped>
.content-box {
margin-bottom: 20rpx;
background-color:
border-radius: 10rpx;
}
.sound-lay {
width: 100%;
display: flex;
justify-content: flex-end;
}
.sound-item {
display: flex;
align-items: center;
background-color:
color: white;
padding: 3px 10px;
border-radius: 3px;
}
.sound-recording {
width: 20px;
height: 18px;
position: relative;
left: 3px;
}
.custom-audio-list {
padding: 8px 0;
}
.custom-audio-item {
margin-bottom: 8px;
display: flex;
align-items: center;
}
.u-delete-icons {
background-color:
border-radius: 50px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
min-width: 20px;
margin-left: 10px;
}
position: fixed;
left: 0px;
bottom: 0px;
background: rgba(0, 0, 0, .3);
width: 100%;
height: 100%;
filter: alpha(opacity=30);
z-Index: 9;
}
.voice-lay {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
z-index: 10;
background-color: white;
padding: 20px 16px 30px 16px;
border-radius: 15px 15px 0 0;
}
.voice-title {
text-align: center;
padding-bottom: 6px;
color: rgba(0, 0, 0, .6);
}
.voice-time {
text-align: center;
padding-bottom: 14px;
}
.viice-btn {
width: 60px;
height: 60px;
box-shadow: 2px 2px 4px 2px
border-radius: 50%;
margin: auto;
display: flex;
align-items: center;
justify-content: center;
}
.viice-start-cor {
width: 30px;
height: 30px;
background-image: linear-gradient(to top right,
}
.viice-stop-cor {
width: 30px;
height: 30px;
background-image: linear-gradient(to top right,
}
.play-btn-img {
width: 40px;
height: 40px;
display: block;
}
.submit-btns-lay {
display: flex;
justify-content: space-around;
padding-top: 20px;
}
.cancel-btn {
border: 1px solid
height: 36px;
line-height: 36px;
width: 45%;
text-align: center;
}
.submit-btn {
background-color:
height: 36px;
line-height: 36px;
width: 45%;
text-align: center;
color: white;
}
</style>
- custom-audio.vue组件(vuex_audioMusicID 可用一个全局变量代替)
<template>
<view class="audio-page">
<view class="box-left">
<view class="page-btn" @click="clickAudio">
<!-- <image src="../static/voice.png" mode="widthFix"></image> -->
<image v-if="!music_play" src="../static/play.png" mode="widthFix"></image>
<image v-if="music_play" src="../static/stop.png" mode="widthFix"></image>
</view>
</view>
<view class="box-content">
<view class="progress">
<text>{{$utils.getMinuteTime(start_time)}}</text>
<slider :value="(start_time / total_time) * 100" block-size="10" block-color="#5c5858"
activeColor="#5c5858" @change="sliderChange"></slider>
<text>{{$utils.getMinuteTime(total_time)}}</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'custom-audio',
props: {
musicID: {
type: String,
default: 0
},
music: {
type: String,
default: ''
}
},
data() {
return {
start_time: 0,
total_time: '',
music_play: false,
interval: null,
timeupdata: null
}
},
mounted() {
this.playAudio();
uni.$on('update', (data) => {
if (data.musicID != this.musicID) {
this.music_play = false;
this.start_time = 0;
}
});
},
methods: {
// 播放音乐
playAudio() {
this.innerAudioContext = uni.createInnerAudioContext();
this.innerAudioContext.src = this.music;
this.interval = setInterval(() => {
if (this.innerAudioContext.duration != 0 && !isNaN(this.innerAudioContext.duration)) {
this.total_time = Math.round(this.innerAudioContext.duration)
// console.log('音频时长', this.total_time)
clearInterval(this.interval)
}
}, 100)
this.timeupdata = setInterval(() => {
// console.log((this.start_time / this.total_time) * 100)
if (this.music_play && this.vuex_audioMusicID == this.musicID) {
this.start_time++
// console.log('当前时间', this.utils.getMinuteTime(this.start_time))
if (this.start_time >= this.total_time) {
this.music_play = false;
setTimeout(() => {
this.start_time = 0;
}, 1000)
}
}
}, 1000)
},
clickAudio() {
if (this.music_play) {
this.music_play = false;
this.innerAudioContext.pause();
} else {
if (this.vuex_audioMusicID != this.musicID) {
this.$u.vuex('vuex_audioMusicID', this.musicID);
this.innerAudioContext = uni.createInnerAudioContext();
this.innerAudioContext.src = this.music;
uni.$emit('update', {
musicID: this.musicID
});
}
this.music_play = true;
this.innerAudioContext.play();
}
},
// 拖动进度条
sliderChange(e) {
// console.log(e)
const second = (e.detail.value / 100) * this.total_time;
this.innerAudioContext.seek(second);
this.start_time = second;
},
},
destroyed() {
this.music_play = false;
this.innerAudioContext.pause();
clearInterval(this.timeupdata);
}
}
</script>
<style lang="scss">
.audio-page {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 1px 1px 2px 1px
padding: 0 10px 0 8px;
.box-left {
width: 10%;
position: relative;
display: flex;
.box-img {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 2;
}
.page-btn {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 0;
top: 0;
z-index: 3;
image {
width: 25px;
height: 25px;
border-radius: 50%;
}
}
}
.box-content {
width: 90%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
box-sizing: border-box;
font-size: 12px;
.content-name {
width: 100%;
display: -webkit-box;
/* 弹性盒模型 */
-webkit-box-orient: vertical;
/* 元素垂直居中*/
-webkit-line-clamp: 1;
/* 文字显示的行数*/
overflow: hidden;
/* 超出隐藏 */
}
.progress {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
slider {
width: 80%;
}
}
}
}
</style>
- utils 文件
// 秒转换分秒
const getMinuteTime = data => {
let minute = parseInt(data / 60)
let second = parseInt(data % 60)
if (minute.toString().length == 1) minute = `0${minute}`
if (second.toString().length == 1) second = `0${second}`
return `${minute}:${second}`
}
export default {
getMinuteTime
}