uniapp播放多个音频文件以及录音上传

  1. 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>
  1. 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: #FFFFFF;
		border-radius: 10rpx;
	}

	.sound-lay {
		width: 100%;
		display: flex;
		justify-content: flex-end;
	}

	.sound-item {
		display: flex;
		align-items: center;
		background-color: #0081ff;
		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: #fa3534;
		border-radius: 50px;
		width: 20px;
		height: 20px;
		display: flex;
		align-items: center;
		justify-content: center;
		min-width: 20px;
		margin-left: 10px;
	}

	#cover {
		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 #ccc;
		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, #53d6c1, #b4f8d9);
	}

	.viice-stop-cor {
		width: 30px;
		height: 30px;
		background-image: linear-gradient(to top right, #ec243b, #f86741);
	}

	.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 #dedede;
		height: 36px;
		line-height: 36px;
		width: 45%;
		text-align: center;
	}

	.submit-btn {
		background-color: #0081ff;
		height: 36px;
		line-height: 36px;
		width: 45%;
		text-align: center;
		color: white;
	}
</style>
  1. 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 #ccc;
		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>

  1. 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
}

你可能感兴趣的:(uniapp,vue.js,前端,uniapp)