Răsfoiți Sursa

音频播放

jwyu 3 ani în urmă
părinte
comite
72b615f96f

+ 2 - 1
package.json

@@ -15,7 +15,8 @@
     "normalize.css": "^8.0.1",
     "vant": "^3.3.4",
     "vue": "^3.2.16",
-    "vue-router": "^4.0.12"
+    "vue-router": "^4.0.12",
+    "vuex": "^4.0.2"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^1.9.3",

BIN
src/assets/hzyb/report/audio-pause-small.png


BIN
src/assets/hzyb/report/audio-play-small.png


+ 2 - 1
src/main.js

@@ -1,8 +1,9 @@
 import { createApp } from 'vue'
 import App from './App.vue'
 import router from './router'
+import store from './store'
 import 'normalize.css'
 import './style/common.scss'
 import '@vant/touch-emulator';//vant 
 
-createApp(App).use(router).mount('#app')
+createApp(App).use(router).use(store).mount('#app')

+ 1 - 1
src/router/hzyb/index.js

@@ -39,7 +39,7 @@ export const hzybRoutes=[
     {
         path:'/hzyb/report',
         name:'hzybReport',
-        component: () => import("@/App.vue"),
+        component: () => import("@/views/hzyb/Index.vue"),
         children:[
             {
                 path:"detail",

+ 24 - 0
src/store/index.js

@@ -0,0 +1,24 @@
+import { createStore, Store } from "vuex";
+import hzyb from "./modules/hzyb";
+
+const store = createStore({
+  state: {
+    userData: {
+      CompanyCode: "",
+      CompanyName: "",
+      Email: "",
+      Sign: null,
+    },
+  },
+  mutations: {
+    getUserData(state, payload) {
+      state.userData = payload;
+    },
+  },
+  actions: {},
+  modules: {
+    hzyb
+  }
+});
+
+export default store;

+ 33 - 0
src/store/modules/hzyb.js

@@ -0,0 +1,33 @@
+const hzybStore={
+    namespaced: true,
+    state:{
+        audioData:{
+            INS:null,
+            url:'',//音频链接
+            videoTime:0,//音频时长
+            videoName:'',//音频名称
+            paused:true,//音频是否在暂停
+        }
+    },
+    mutations: {
+        // 点击播放音频
+        addAudio(state,e){
+            state.audioData.url=e.video_url
+            state.audioData.videoTime=e.video_play_seconds
+            state.audioData.videoName=e.video_name
+        },
+        // 更新音频播放状态
+        upateAudioStatus(state,e){
+            if(e==='end'){
+                state.audioData.url=''
+                
+            }else if(e==='play'){
+                state.audioData.paused=false
+            }else if(e==='paused'){
+                state.audioData.paused=true
+            }
+        }
+    }
+}
+
+export default hzybStore

+ 122 - 0
src/views/hzyb/Index.vue

@@ -0,0 +1,122 @@
+<script setup>
+import {ref} from 'vue'
+import { Slider } from 'vant';
+import { useStore } from "vuex";
+
+const store=useStore()
+
+const audioIconPlaysmall=new URL('../../assets/hzyb/report/audio-play-small.png', import.meta.url).href
+const audioIconPausesmall=new URL('../../assets/hzyb/report/audio-pause-small.png', import.meta.url).href
+
+const formatVoiceTime = (e) => {
+  let minus = parseInt(e / 60);
+  let sec = parseInt(e % 60);
+  return `${minus > 9 ? minus : "0" + minus}分${sec > 9 ? sec : "0" + sec}秒`;
+};
+
+let currentTime=ref(0)
+const audioIns=ref('')
+store.state.hzyb.audioData.INS=audioIns
+
+// 以下为音频事件
+const audioError=()=>{
+    console.log('音频加载出错');
+}
+
+const audioPlay=()=>{
+    console.log('音频开始播放');
+    store.commit('hzyb/upateAudioStatus','play')
+}
+
+const audioPause=()=>{
+    console.log('音频暂停');
+    store.commit('hzyb/upateAudioStatus','paused')
+}
+
+const audioTimeupdate=(e)=>{
+    // console.log(e.target.currentTime);
+    currentTime.value=e.target.currentTime
+}
+
+const audioEnded=()=>{
+    console.log('音频播放结束');
+    store.commit('hzyb/upateAudioStatus','end')
+}
+
+// 音频进度条变化
+const SliderChange=(e)=>{
+    store.state.hzyb.audioData.INS.currentTime=e
+}
+
+const handleClickAudio=()=>{
+    if(store.state.hzyb.audioData.paused){
+        store.state.hzyb.audioData.INS.play()
+    }else{
+        store.state.hzyb.audioData.INS.pause()
+    }
+}
+
+
+</script>
+
+
+<template>
+    <router-view />
+    <audio
+        ref="audioIns"
+        autoplay
+        :src="$store.state.hzyb.audioData.url"
+        @error="audioError"
+        @play="audioPlay"
+        @pause="audioPause"
+        @timeupdate="audioTimeupdate"
+        @ended="audioEnded"
+        style="display:none"
+    />
+    <!-- 悬浮音频模块 -->
+    <div class="popup-audio-box" v-if="$store.state.hzyb.audioData.url">
+        <div>{{$store.state.hzyb.audioData.videoName}}</div>
+        <div class="flex">
+            <span>{{formatVoiceTime(currentTime)}}</span>
+            <Slider button-size="10px" v-model="currentTime" :max="$store.state.hzyb.audioData.videoTime" active-color="#D5AD79" @change="SliderChange" class="slider"></Slider>
+            <span>{{ formatVoiceTime($store.state.hzyb.audioData.videoTime) }}</span>
+            <img :src="$store.state.hzyb.audioData.paused?audioIconPausesmall:audioIconPlaysmall"  alt="" @click="handleClickAudio">
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.popup-audio-box{
+    position: fixed;
+    z-index: 99;
+    bottom: 30px;
+    width: 90vw;
+    left: 5vw;
+    min-height: 100px;
+    background: #FFFFFF;
+    border: 2px solid rgba(240, 234, 226, 0.32);
+    border-radius: 10px;
+    padding: 10px 20px;
+    font-size: 24px;
+    text-align: center;
+    padding-bottom: 20px;
+    .flex{
+        display: flex;
+        align-items: center;
+        .slider{
+            margin-left: 15px;
+            margin-right: 10px;
+        }
+        span{
+            flex-shrink: 0;
+            display: inline-block;
+        }
+        img{
+            width: 36px;
+            height: 36px;
+            flex-shrink: 0;
+            margin-left: 10px;
+        }
+    }
+}
+</style>

+ 16 - 28
src/views/hzyb/report/ChapterDetail.vue

@@ -7,14 +7,8 @@
         <span>FICC团队</span>
         <span>{{formatTime(info.report_chapter_item.publish_time)}}</span>
       </div>
-      <div class="flex audio-wrap">
-        <img style="opacity:0.3" src="@/assets/hzyb/report/audio-pause.png" mode="aspectFill" v-if="!info.auth_ok"/>
-        <img src="@/assets/hzyb/report/audio-pause.png" mode="aspectFill" v-else/>
-        <div>
-          <div>{{info.report_chapter_item.video_name}}</div>
-          <div style="color: #999999">{{formatVoiceTime(info.report_chapter_item.video_play_seconds)}}</div>
-        </div>
-      </div>
+      <!-- 音频模块 -->
+      <AudioBox :audioData="audioData"></AudioBox>
       <div class="tips">
         <span>注:请务必阅读</span>
         <span style="color: #e3b377; margin-left: 20px" @click="showDisclaimers = true">免责声明</span>
@@ -55,7 +49,7 @@
       <van-row gutter="10">
         <van-col span="6" v-for="item in info.report_chapter_menu_list" :key="item.report_chapter_id">
           <div :class="['item',item.report_chapter_id==chapterId&&'active']" @click="handleChapterChange(item)">
-            <image :src="item.report_chapter_type_thumb" mode="aspectFill"/>
+            <img :src="item.report_chapter_type_thumb" mode="aspectFill"/>
             <text>{{item.report_chapter_type_name}}</text>
           </div>
         </van-col>
@@ -103,6 +97,7 @@ moment.locale('zh-cn')
 
 import {apiChapterDetail,apiChapterTickerValue} from '@/api/hzyb/report'
 import {Popup,Image as VanImage,PullRefresh,Col, Row} from 'vant'
+import AudioBox from './components/AudioBox.vue'
 export default {
   components:{
     [Popup.name]:Popup,
@@ -110,7 +105,8 @@ export default {
     [PullRefresh.name]:PullRefresh,
     [Col.name]:Col,
     [Row.name]:Row,
-    [PullRefresh.name]:PullRefresh
+    [PullRefresh.name]:PullRefresh,
+    AudioBox
   },
   data() {
     return {
@@ -118,6 +114,7 @@ export default {
       chapterId:0,
       fromPage:'',//如果是从首页(home)来的则隐藏底部切换
       info:null,
+      audioData:{},//音频数据
 
       tickerInfo:null,
       tickerHead:[],
@@ -160,6 +157,12 @@ export default {
       const res=await apiChapterDetail({report_chapter_id:Number(this.chapterId)})
       if(res.code===200){
         this.info=res.data
+        this.audioData={
+          auth_ok:res.data.auth_ok,
+          video_name:res.data.report_chapter_item.video_name,
+          video_play_seconds:res.data.report_chapter_item.video_play_seconds,
+          video_url:res.data.report_chapter_item.video_url
+        }
         document.title=res.data.report_chapter_item.classify_name_first
         if(res.data.report_chapter_item.classify_name_first==='晨报'){
           this.getTickerValue()
@@ -171,7 +174,7 @@ export default {
     async getTickerValue(){
       const res=await apiChapterTickerValue({report_chapter_id:Number(this.chapterId)})
       if(res.code===200){
-        if(!res.data.list) return
+        if(!res.data||!res.data.list) return
         this.tickerInfo=res.data
         if(res.data.ticker_title.report_chapter_type_id===17){
           this.tickerHead=[
@@ -337,21 +340,6 @@ export default {
         justify-content: space-between;
         font-size: 28px;
     }
-    .audio-wrap{
-        height: 160px;
-        background: #FAF7EE;
-        border-radius: 16px;
-        margin-top: 20px;
-        padding: 10px 31px;
-        margin-bottom: 31px;
-        align-items: center;
-        img{
-            width: 110px;
-            height: 110px;
-            display: block;
-            margin-right: 16px;
-        }
-    }
 
     .tips{
         font-size: 34px;
@@ -410,7 +398,7 @@ export default {
         width: 76px;
         height: 76px;
         right: 34px;
-        bottom: 100px;
+        bottom: 150px;
     }
     .chapter-list-wrap {
     background-color: #f5f6fa;
@@ -449,7 +437,7 @@ export default {
       padding-top: 20px;
       border-radius: 8px;
       overflow: hidden;
-      image{
+      img{
         width: 88px;
         height: 88px;
         display: block;

+ 12 - 9
src/views/hzyb/report/Detail.vue

@@ -52,14 +52,8 @@
                 <span>{{info.report_info.author}}</span>
                 <span>{{formatTime(info.report_info.publish_time)}}</span>
             </div>
-            <div class="flex audio-wrap">
-                <img style="opacity:0.3" src="@/assets/hzyb/report/audio-pause.png" mode="aspectFill" v-if="!info.auth_ok"/>
-                <img src="@/assets/hzyb/report/audio-pause.png" mode="aspectFill" v-else/>
-                <div>
-                    <div>{{info.report_info.video_name}}</div>
-                    <div style="color:#999999">{{formatVoiceTime(info.report_info.video_play_seconds)}}</div>
-                </div>
-            </div>
+            <!-- 音频模块 -->
+            <AudioBox :audioData="audioData"></AudioBox>
             <div class="tips">
                 <span>注:请务必阅读</span>
                 <span style="color:#E3B377;margin-left:20px" @click="showDisclaimers=true">免责声明</span>
@@ -122,17 +116,20 @@ moment.locale('zh-cn')
 import {apiReportDetail} from '@/api/hzyb/report'
 import {apiApplyPermission} from '@/api/hzyb/user'
 import {Popup,Image as VanImage,PullRefresh} from 'vant'
+import AudioBox from './components/AudioBox.vue'
 export default {
     components:{
         [Popup.name]:Popup,
         [VanImage.name]:VanImage,
-        [PullRefresh.name]:PullRefresh
+        [PullRefresh.name]:PullRefresh,
+        AudioBox
     },
     data () {
         return {
             showDisclaimers:false,//显示免责声明
             reportId:0,
             info:null,
+            audioData:{},//音频数据
             chapterList:[],
             pupData:{
 				show:false,
@@ -170,6 +167,12 @@ export default {
             const res=await apiReportDetail({report_id:Number(this.reportId)})
             if(res.code===200){
                 this.info=res.data
+                this.audioData={
+                    auth_ok:res.data.auth_ok,
+                    video_name:res.data.report_chapter_item.video_name,
+                    video_play_seconds:res.data.report_chapter_item.video_play_seconds,
+                    video_url:res.data.report_chapter_item.video_url
+                }
                 this.chapterList=res.data.report_chapter_list
                 document.title = res.data.report_info.classify_name_first
                 if(!res.data.auth_ok){

+ 67 - 0
src/views/hzyb/report/components/AudioBox.vue

@@ -0,0 +1,67 @@
+<script setup>
+import {reactive, ref} from 'vue'
+import { Slider } from 'vant';
+import { useStore } from "vuex";
+
+const store=useStore()
+
+const props = defineProps({
+  audioData: Object,
+});
+
+const formatVoiceTime = (e) => {
+  let minus = parseInt(e / 60);
+  let sec = parseInt(e % 60);
+  return `${minus > 9 ? minus : "0" + minus}分${sec > 9 ? sec : "0" + sec}秒`;
+};
+
+const audioIconPlay=new URL('../../../../assets/hzyb/report/audio-play.png', import.meta.url).href
+const audioIconPause=new URL('../../../../assets/hzyb/report/audio-pause.png', import.meta.url).href
+
+
+const handleClickAudio=()=>{
+    if(store.state.hzyb.audioData.url==props.audioData.video_url){
+      if(store.state.hzyb.audioData.paused){
+        store.state.hzyb.audioData.INS.play()
+      }else{
+        store.state.hzyb.audioData.INS.pause()
+      }
+    }else{
+        store.commit('hzyb/addAudio',props.audioData)
+    }
+}
+
+
+</script>
+
+<template>
+  <div class="flex audio-wrap">
+    <img style="opacity: 0.3" src="@/assets/hzyb/report/audio-pause.png" mode="aspectFill" v-if="!audioData.auth_ok" />
+    <img :src="$store.state.hzyb.audioData.url==audioData.video_url&&!$store.state.hzyb.audioData.paused?audioIconPlay:audioIconPause" @click="handleClickAudio" v-else />
+    <div>
+      <div>{{ audioData.video_name }}</div>
+      <div style="color: #999999">{{ formatVoiceTime(audioData.video_play_seconds) }}</div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.flex{
+    display: flex;
+}
+.audio-wrap {
+  height: 160px;
+  background: #faf7ee;
+  border-radius: 16px;
+  margin-top: 20px;
+  padding: 10px 31px;
+  margin-bottom: 31px;
+  align-items: center;
+  img {
+    width: 110px;
+    height: 110px;
+    display: block;
+    margin-right: 16px;
+  }
+}
+</style>