Эх сурвалжийг харах

完成语音播报模块音频播放修改

jwyu 2 жил өмнө
parent
commit
e2b43a64a8

+ 266 - 0
components/audioBox/audioBox.vue

@@ -0,0 +1,266 @@
+<template>
+    <view class="global-voice-box">
+        <view class="flex small-box" v-if="!showBig" @click.prevent="showBig=true">
+            <view style="flex:1;overflow: hidden;">
+                <view class="van-ellipsis">{{title}}</view>
+                <view class="time" style="font-size:24rpx;color:#666">时长 {{audioTime|formatVoiceTime}}</view>
+            </view>
+            <image 
+                class="pause-img"  
+                :src="play?require('@/static/audio-doing.png'):require('@/static/audio-pause-3.png')" 
+                mode="aspectFill"
+                @click.stop="handleChangePlayStatus"
+            />
+            <van-icon @click.stop="handleClosePopAudio" name="cross" size="18" color="#BBC3C9"/>
+            <van-progress 
+                color="#D4AC78" 
+                track-color="#fff"
+                :show-pivot="false" 
+                stroke-width="2px" 
+                custom-class="bot-progress" 
+                :percentage="percentage" 
+            />
+        </view>
+        <view class="big-box" v-else>
+            <view class="flex top" style="overflow: hidden;">
+                <van-icon name="arrow-down" size="18" color="#BBC3C9" @click="showBig=false" />
+                <view class="van-ellipsis" style="flex:1;margin:0 10rpx;text-align:center">{{title}}</view>
+                <van-icon @click.stop="handleClosePopAudio" name="cross" size="18" color="#BBC3C9"/>
+            </view>
+            <view class="flex center">
+                <image 
+                    class="pause-img"  
+                    :src="play?require('@/static/audio-doing.png'):require('@/static/audio-pause-3.png')" 
+                    mode="aspectFill"
+                    @click.stop="handleChangePlayStatus"
+                />
+                <text class="time">{{curTime|formatVoiceTime}}</text>
+                <slider
+                    activeColor="#e3b377"
+                    :max="audioTime" 
+                    :value="curTime" 
+                    @change="handleAudioSliderChange($event)"
+                    block-size="16"
+                    class="slider"
+                />
+                <text class="time">{{audioTime|formatVoiceTime}}</text>
+            </view>
+        </view>
+    </view>
+      
+</template>
+
+<script>
+export default {
+    filters:{
+        formatVoiceTime(e){
+            let m=parseInt(e/60)
+            let s=parseInt(e%60)
+            return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
+        }
+    },
+
+    computed: {
+        audioData(){
+            return this.$store.state.audio
+        },
+        //用户监听音频是否变化了 是否需要初始化
+        audioInit(){
+            return {
+                reportId:this.$store.state.audio.reportId,
+                voiceId:this.$store.state.audio.voiceId,
+            }
+        },
+
+        percentage(){
+            return parseInt((this.curTime/this.audioTime)*100)
+        },
+
+        reportAudioShow(){
+            return this.$store.state.report.audioData.show
+        }
+    },
+    watch:{
+        audioInit:{
+            handler(nval){
+                console.log(nval);
+                this.init()
+            },
+            immediate:true
+        },
+        showBig:{
+            handler(nval){
+                this.$store.commit('audio/showBig',nval)
+            },
+            immediate:true
+        }
+    },
+    data() {
+        return {
+            showBig:false,
+            curTime:0,
+            audioTime:0,//当前音频总时长
+            title:'',//当前音频标题
+            play:false
+        }
+    },
+    methods: {
+        init(){
+            console.log('audioBox init');
+            let delyTime=0
+            if(this.$store.state.report.audioData.show){
+                this.globalBgMusic.stop()
+                delyTime=300
+            }
+            console.log('audioBox init',delyTime);
+            const curAudio=this.$store.state.audio.list[this.$store.state.audio.index]
+            setTimeout(() => {
+                if(this.globalBgMusic.src!=curAudio.url){
+                    this.globalBgMusic.src=curAudio.url 
+                    this.globalBgMusic.title=curAudio.title
+                }
+                this.audioTime=curAudio.time
+                this.title=curAudio.title
+                this.curTime=parseInt(this.globalBgMusic.currentTime)
+                this.play=!this.globalBgMusic.paused
+                this.listenAudio()
+            }, delyTime);
+        },
+
+        //音频播放事件
+        listenAudio(){
+            console.log('设置监听事件');
+            this.globalBgMusic.onPlay(()=>{
+                console.log('开始播放');
+                this.play=true
+                this.$store.commit('audio/updateAudioPause',false)
+            })
+            this.globalBgMusic.onPause(()=>{
+                console.log('音频暂停');
+                this.play=false
+                this.$store.commit('audio/updateAudioPause',true)
+            })
+            this.globalBgMusic.onStop(()=>{
+                console.log('音频停止');
+                this.$store.commit('audio/removeAudio')
+            })
+            this.globalBgMusic.onEnded(()=>{
+                console.log('音频onEnded');
+
+                const index=this.$store.state.audio.index 
+                if(index==this.$store.state.audio.list.length-1){
+                    this.$store.commit('audio/removeAudio')
+                }else{
+                    this.handleAudioChange('next')
+                }
+                
+            })
+            this.globalBgMusic.onError((e)=>{
+                console.log('音频onError',e);
+                this.$store.commit('audio/removeAudio')
+                uni.showToast({
+                    title: '音频播放错误',
+                    icon: 'none'
+                })
+            })
+            this.globalBgMusic.onTimeUpdate(()=>{
+                // console.log('时间更新');
+                this.curTime=parseInt(this.globalBgMusic.currentTime)
+                this.$store.commit('audio/updateAudioTime',this.curTime)
+            })
+        },
+
+        //关闭弹窗停止播放的音频
+        handleClosePopAudio(){
+            this.globalBgMusic.stop()
+        },
+
+        //音频切换(暂时没有用到因为都是一个音频 如果后期有多个 直接在页面dom调用此方法即可)
+        handleAudioChange(type){
+            let temIndex=this.$store.state.report.audioData.index
+            if(type=='before'){
+                if(temIndex>0){
+                    let index=temIndex-1
+                    this.$store.commit('audio/updateAudioIndex', index)
+                    this.init()
+                }
+            }else{
+                if(temIndex<this.$store.state.report.audioData.list.length-1){
+                    let index=temIndex+1
+                    this.$store.commit('audio/updateAudioIndex', index)
+                    this.init()
+                }
+            }
+        },
+
+        //拖动进度条
+        handleAudioSliderChange(e){
+            const value=e.detail.value
+            this.globalBgMusic.seek(value)
+        },
+
+        //音频点击暂停播放
+        handleChangePlayStatus(){
+            if(!this.globalBgMusic.paused){
+                this.globalBgMusic.pause()
+            }else{
+                this.globalBgMusic.play()
+            }
+        },
+
+    },
+}
+</script>
+
+<style>
+.bot-progress{
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+</style>
+
+<style lang="scss" scoped>
+.global-voice-box{
+    position: fixed;
+    background: #FFFFFF;
+    border: 1px solid #E4E4E4;
+    box-shadow: 0px 0px 40rpx rgba(50, 35, 17, 0.25);
+    left: 20rpx;
+    right: 20rpx;
+    bottom: calc(130rpx + constant(safe-area-inset-bottom));
+    bottom: calc(130rpx + env(safe-area-inset-bottom));
+    z-index: 99;
+    .small-box{
+        padding: 18rpx 48rpx 18rpx 20rpx;
+        position: relative;
+        align-items: center;
+        .pause-img{
+            width: 60rpx;
+            height: 60rpx;
+            margin-right: 44rpx;
+            margin-left: 30rpx;
+            flex-shrink: 0;
+        }
+    }
+    .big-box{
+        padding: 33rpx 30rpx;
+        .center{
+            align-items: center;
+            margin-top: 20rpx;
+            .time{
+                font-size: 24rpx;
+            }
+            .pause-img{
+                width: 60rpx;
+                height: 60rpx;
+                margin-right: 34rpx;
+            }
+            .slider{
+                flex: 1;
+            }
+        }
+    }
+}
+</style>

+ 40 - 48
pages-voice/voiceDetail.vue

@@ -5,7 +5,7 @@
         <view class="time">发布时间:{{info.CreateTime|formatTime}}</view>
         <view class="flex audio-box">
             <image 
-                :src="paused?'../../../static/voice/pause.png':'../../../static/voice/playing.png'" 
+                :src="voiceId==curVoiceId&&!curAudioPaused?require('@/static/voice/playing.png'):require('@/static/voice/pause.png')" 
                 mode="aspectFill" 
                 @click="handlePlayAudio" 
             />
@@ -22,8 +22,11 @@
         </view>
         <image class="del-btn" src="@/static/voice/del.png" mode="widthFix" @click="handleDel" v-if="info.IsAuthor"/>
         <image class="publish-btn" src="@/static/voice/publish.png" mode="widthFix" @click="handleSendMsg" v-if="info.CouldSendMsg"/>
+        
+        <view v-show="false"><audioBox v-if="showAudioPop"/></view>
 
         <van-dialog id="van-dialog" />
+
     </view>
     <noAuth :info="noAuthData" v-else/>
 
@@ -33,11 +36,30 @@
 import {apiVoicePlayRecord,apiVoiceDel,apiVoiceDetail,apiVoiceSendMsg} from '@/api/voice'
 import {apiGetSceneToParams} from '@/api/common'
 import noAuth from '@/pages/voice/components/noAuth.vue'
+import audioBox from '@/components/audioBox/audioBox.vue'
 const moment=require('@/utils/moment-with-locales.min')
-let innerAudioContext = uni.createInnerAudioContext();//播放音频实例
 export default {
     components:{
-        noAuth
+        noAuth,
+        audioBox
+    },
+    computed:{
+        showAudioPop(){//是否显示音频弹窗
+            return this.$store.state.audio.show
+        },
+        curVoiceId(){//当前正在播放的音频id
+            return this.$store.state.audio.voiceId
+        },
+        curAudioPaused(){//当前音频是否暂停状态
+            return this.$store.state.audio.paused
+        },
+        curTime(){
+            let t=0
+            if(this.voiceId==this.$store.state.audio.voiceId){
+                t=this.$store.state.audio.curTime
+            }
+            return t
+        }
     },
     filters:{
         formatTime(e){
@@ -56,20 +78,14 @@ export default {
             noAuthData:null,
             info:{},
 
-            curTime:0,
+            // curTime:0,
             duration:0,
-            paused:true,
-
-            isStartPlay:true,//是否从头播放的
         }
     },
     onLoad(options){
         this.init(options)
     },
-    onShow(){
-        innerAudioContext=uni.createInnerAudioContext();
-        this.listenAudio()
-    },
+
     onShareAppMessage(){
         const title=`${this.info.SectionName}:${this.info.BroadcastName}`
         return {
@@ -77,13 +93,7 @@ export default {
             imageUrl:this.info.ImgUrl
         }
     },
-    onHide(){
-        innerAudioContext.pause()
-    },
-    onUnload(){
-        innerAudioContext.destroy()
-        this.paused=true
-	},
+
     methods: {
         async init(options){
             if(options.scene){
@@ -171,40 +181,22 @@ export default {
 
         //点击播放\暂停音频
         handlePlayAudio(){
-            if(this.isStartPlay){
-                this.handleVoicePlayRecord()
-            }
-            innerAudioContext.src=this.info.VoiceUrl
-            this.isStartPlay=false
-            innerAudioContext.obeyMuteSwitch=false
-            if(this.paused){
-                innerAudioContext.play()
+            if(this.$store.state.audio.voiceId==this.voiceId){
+                if(this.globalBgMusic.paused){
+                    this.globalBgMusic.play()
+                }else{
+                    this.globalBgMusic.pause()
+                }
             }else{
-                innerAudioContext.pause()
+                const list=[{url:this.info.VoiceUrl,time:this.info.VoicePlaySeconds,title:this.info.BroadcastName,}]
+                this.$store.commit('audio/addAudio',{
+                    list:list,
+                    voiceId:this.voiceId
+                })
+                this.handleVoicePlayRecord()
             }
         },
 
-        //音频播放事件
-        listenAudio(){
-            innerAudioContext.onPlay(()=>{
-                console.log('开始播放录音');
-                this.paused=false
-            })
-            innerAudioContext.onPause(()=>{
-                console.log('录音播放暂停');
-                this.paused=true
-            })
-            innerAudioContext.onEnded(()=>{
-                console.log('录音播放自然结束');
-                this.paused=true
-                this.curTime=0
-                this.isStartPlay=true
-            })
-            innerAudioContext.onTimeUpdate(()=>{
-                // console.log('时间更新');
-                this.curTime=parseInt(innerAudioContext.currentTime)
-            })
-        },
 
         //拖动音频播放进度条
         handleAudioSliderChange(e){

+ 81 - 79
pages/voice/voice.vue

@@ -12,17 +12,16 @@
             />
             <view>暂无数据</view>
         </view>
-        <view class="list-wrap" :style="{paddingBottom:IsVoiceAdmin&&'200rpx'}" v-else>
+        <view class="list-wrap" :style="{paddingBottom:pagePaddingBot}" v-else>
             <view class="item" v-for="item in list" :key="item.BroadcastId" @click="handleGoDetail(item)">
                 <view class="title">{{item.BroadcastName}}</view>
                 <view class="time">发布时间:{{item.CreateTime|formatTime}}</view>
                 <view class="flex audio-box" @click.stop="handlePlay(item)">
                     <image 
-                        :src="item.BroadcastId==temAudio.id&&!temAudio.paused?require('@/static/voice/playing.png'):require('@/static/voice/pause.png')" 
+                        :src="item.BroadcastId==curVoiceId&&!curAudioPaused?require('@/static/voice/playing.png'):require('@/static/voice/pause.png')" 
                         mode="widthFix" 
                     />
-                    <text v-if="item.BroadcastId==temAudio.id">{{temAudio.curTime|formatVoiceTime}}</text>
-                    <text v-else>{{item.VoicePlaySeconds|formatVoiceTime}}</text>
+                    <text>{{item.VoicePlaySeconds|formatVoiceTime}}</text>
                 </view>
                 <button class="publish-btn" @click.stop="handleSendMsgItem(item)" v-if="item.CouldSendMsg">
                     <image class="publish-img" src="@/static/voice/publish.png" mode="widthFix" />
@@ -42,7 +41,7 @@
         </view>
 
         <navigator url="/pages-voice/addVoice">
-            <view class="add-btn" v-if="IsVoiceAdmin">新建语音</view>
+            <view :class="['add-btn',showAudioPop?showAudioBigPop?'add-btn-bot3':'add-btn-bot2':'add-btn-bot1']" v-if="IsVoiceAdmin">新建语音</view>
         </navigator>
 
         <!-- 筛选弹窗 -->
@@ -70,6 +69,13 @@
             </view>
         </van-popup>
 
+        <!-- 音频悬浮 -->
+        <view v-if="showPage">
+            <audioBox v-if="showAudioPop"/>
+        </view>
+          
+        
+
         <van-dialog id="van-dialog" />
     </view>
     <noAuth :info="noAuthData" v-else/>
@@ -79,11 +85,12 @@
 import {apiVoiceList,apiVoiceSectionList,apiVoicePlayRecord,apiVoiceDel,apiVoiceSendMsg} from '@/api/voice'
 import {apiGetSceneToParams} from '@/api/common'
 import noAuth from './components/noAuth.vue'
+import audioBox from '@/components/audioBox/audioBox.vue'
 const moment=require('@/utils/moment-with-locales.min')
-let innerAudioContext = uni.createInnerAudioContext();//播放音频实例
 export default {
     components:{
-        noAuth
+        noAuth,
+        audioBox
     },
     filters:{
         formatTime(e){
@@ -95,9 +102,42 @@ export default {
             return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
         }
     },
+    computed:{
+        showAudioPop(){//是否显示音频弹窗
+            return this.$store.state.audio.show
+        },
+        showAudioBigPop(){
+            return this.$store.state.audio.showBig
+        },
+        curVoiceId(){//当前正在播放的音频id
+            return this.$store.state.audio.voiceId
+        },
+        curAudioPaused(){//当前音频是否暂停状态
+            return this.$store.state.audio.paused
+        },
+
+        pagePaddingBot(){
+            let num=34
+            if(this.IsVoiceAdmin){
+                num=num+160
+            }else{
+                num=34
+            }
+            if(this.$store.state.audio.show){
+                if(this.$store.state.audio.showBig){
+                    num=num+260
+                }else{
+                    num=num+180
+                }
+            }else{
+                num=num+0
+            }
+
+            return num+'rpx'
+        }
+    },
     data() {
         return {
-            // innerAudioContext:null,//播放音频实例
             list:[],
             page:1,
             pageSize:20,
@@ -114,13 +154,7 @@ export default {
             mainActiveIndex:0,
             activeId:0,//选择的板块id
 
-            temAudio:{
-                paused:true,
-                url:'',//临时音频地址
-                duration:'',//时长
-                id:0,
-                curTime:''
-            }
+            showPage:false,
         }
     },
     onLoad(){
@@ -133,21 +167,13 @@ export default {
         if(!this.isAuth){
             this.getVoiceList()
         }
-        innerAudioContext=uni.createInnerAudioContext();
-        this.listenAudio()
+        this.showPage=true
     },
     onHide(){
-        innerAudioContext.destroy()
-        this.temAudio.id=0
-        this.temAudio.paused=true
-        this.temAudio.curTime=''
+        this.showPage=false
     },
     onUnload(){
 		uni.$off('addVoiceSuccess')
-        innerAudioContext.destroy()
-        this.temAudio.id=0
-        this.temAudio.paused=true
-        this.temAudio.curTime=''
 	},
     onShareAppMessage({from,target}) {
         console.log(from,target);
@@ -306,6 +332,11 @@ export default {
                 message: '确定要删除该语音播报吗?',
                 confirmButtonText:'确定'
             }).then(()=>{
+                if(this.temAudio.item&&this.temAudio.item.id==item.BroadcastId){
+                    //删除的音频正好在播放则暂停
+                    this.globalBgMusic.stop()
+                }
+                
                 apiVoiceDel({broadcast_id:Number(item.BroadcastId)}).then(res=>{
                     if(res.code===200){
                         uni.showToast({
@@ -321,56 +352,24 @@ export default {
             }).catch(()=>{})
         },
 
+        //点击音频 播放或者暂停
         handlePlay(item){
-            if(this.temAudio.id==item.BroadcastId){
-                if(innerAudioContext.paused){
-                    innerAudioContext.play()
+            if(this.$store.state.audio.voiceId==item.BroadcastId){
+                if(this.globalBgMusic.paused){
+                    this.globalBgMusic.play()
                 }else{
-                    innerAudioContext.pause()
+                    this.globalBgMusic.pause()
                 }
             }else{
-                if(!innerAudioContext.paused){
-                    innerAudioContext.stop()
-                }
-                this.temAudio.id=item.BroadcastId
-                this.temAudio.duration=item.VoicePlaySeconds
-                this.temAudio.curTime=item.VoicePlaySeconds
-                innerAudioContext.src=item.VoiceUrl 
-                innerAudioContext.obeyMuteSwitch=false
-                innerAudioContext.play()
+                const list=[{url:item.VoiceUrl,time:item.VoicePlaySeconds,title:item.BroadcastName,}]
+                this.$store.commit('audio/addAudio',{
+                    list:list,
+                    voiceId:item.BroadcastId
+                })
                 this.handleVoicePlayRecord(item)
             }
         },
 
-        //音频播放事件
-        listenAudio(){
-            innerAudioContext.onPlay(()=>{
-                console.log('开始播放录音');
-                this.temAudio.paused=false
-            })
-            innerAudioContext.onPause(()=>{
-                console.log('录音播放暂停');
-                this.temAudio.paused=true
-            })
-            // innerAudioContext.onStop(()=>{
-            //     console.log('录音播放停止');
-            //     this.temAudio.paused=true
-            //     // setTimeout(() => {
-            //         this.temAudio.id=0
-            //     // }, 100);
-            // })
-            innerAudioContext.onEnded(()=>{
-                console.log('录音播放自然结束');
-                this.temAudio.paused=true
-                this.temAudio.id=0
-
-            })
-            innerAudioContext.onTimeUpdate(()=>{
-                // console.log('时间更新');
-                this.temAudio.curTime=parseInt(this.temAudio.duration)-parseInt(innerAudioContext.currentTime)
-            })
-        },
-
         //上报音频播放记录
         async handleVoicePlayRecord(item){
             const res=await apiVoicePlayRecord({
@@ -415,17 +414,7 @@ export default {
 
 <style lang="scss" scoped>
 .voice-play-page{
-    
     .top-filter-box{
-        // padding-left: 34rpx;
-        // padding-bottom: 10rpx;
-        // position: sticky;
-        // top: 0;
-        // left: 0;
-        // z-index: 99;
-        // background-color: #fff;
-        // display: flex;
-        // align-items: center;
         display: flex;
 		flex: auto;
 		align-items: center;
@@ -547,11 +536,24 @@ export default {
     color: #E3B377;
     font-size: 32rpx;
     left: 50%;
-    bottom: calc(80px + constant(safe-area-inset-bottom));
-    bottom: calc(80px + env(safe-area-inset-bottom));
+    bottom: calc(150rpx + constant(safe-area-inset-bottom));
+    bottom: calc(150rpx + env(safe-area-inset-bottom));
     transform: translateX(-50%);
     background: #333333;
     box-shadow: 0px 4rpx 20rpx rgba(160, 126, 84, 0.25);
     border-radius: 40rpx;
 }
+.add-btn-bot1{
+    bottom: calc(120rpx + constant(safe-area-inset-bottom));
+    bottom: calc(120rpx + env(safe-area-inset-bottom));
+}
+.add-btn-bot2{
+    bottom: calc(260rpx + constant(safe-area-inset-bottom));
+    bottom: calc(260rpx + env(safe-area-inset-bottom));
+}
+.add-btn-bot3{
+    bottom: calc(355rpx + constant(safe-area-inset-bottom));
+    bottom: calc(355rpx + env(safe-area-inset-bottom));
+}
+
 </style>

+ 3 - 1
store/index.js

@@ -3,6 +3,7 @@ import Vuex from 'vuex'
 import user from './modules/user.js'
 import activity from './modules/activity'
 import report from './modules/report'
+import audio from './modules/audio'
 Vue.use(Vuex);//vue的插件机制
 
 //Vuex.Store 构造器选项
@@ -13,7 +14,8 @@ const store = new Vuex.Store({
 	modules:{
 		user,
 		activity,
-		report
+		report,
+		audio
 	}
 })
 export default store

+ 55 - 0
store/modules/audio.js

@@ -0,0 +1,55 @@
+// 全局音频背景播放状态管理模块
+const audioModules={
+    namespaced: true,
+    state:{
+        show:false,//是否显示音频弹窗
+        showBig:false,//显示大弹窗
+        list:[],//[{url:音频地址,time:音频时长,title:音频标题,}]
+        index:0,//当前是播放第几个
+        reportId:0,//当前是哪个报告的音频
+        voiceId:0,//当前是哪个语音播报的音频
+        paused:true,//当前是否音频正在播放 true暂停状态
+        curTime:0,//当前正在播放的音频播放的时间
+    },
+    mutations: {
+        addAudio(state,e){
+            state.show=true
+            state.list=e.list
+            state.index=0
+            state.reportId=e.reportId||0
+            state.voiceId=e.voiceId||0
+        },
+        updateAudioIndex(state,e){
+            state.index=e
+        },
+        // 音频状态
+        updateAudioPause(state,e){
+            state.paused=e
+        },
+        // 更新音频播放进度
+        updateAudioTime(state,e){
+            state.curTime=e
+        },
+        removeAudio(state,e){
+            state.show=false
+            state.list=[]
+            state.index=0
+            state.reportId=0
+            state.voiceId=0
+            state.paused=true
+        },
+        //显示弹窗
+        showPopAudio(state){
+            state.show=true
+        },
+        showBig(state,e){
+            state.showBig=e
+        },
+        // 关闭弹窗
+        closePopAudio(state){
+            state.show=false
+        }
+    }
+}
+
+export default audioModules;