فهرست منبع

Merge branch 'master' into xqc_fixscroll

jwyu 2 سال پیش
والد
کامیت
cb517b71b8

+ 27 - 1
src/api/voice.js

@@ -48,6 +48,32 @@ export const apiVoicePlayRecord=params=>{
  * 推送客群、模板消息
  * @param broadcast_id
  */
- export const apiVoiceSendMsg=params=>{
+export const apiVoiceSendMsg=params=>{
     return post('/voice/broadcast/msg_send',params)
+}
+
+/**
+ * 我的语音数量统计
+ * @param author_id
+ */
+export const apiMyVoiceCount=params=>{
+    return get('/voice/broadcast/list_count',params)
+}
+
+/**
+ * 语音详情
+ * @param broadcast_id
+ */
+export const apiVoiceDetail=params=>{
+    return get('/voice/broadcast/detail',params)
+}
+
+/**
+ * 发布语音
+ * @param broadcast_id 语音播报ID
+ * @param publish_type 发布类型:1-发布 2-定时发布
+ * @param pre_publish_time 预发布时间(类型为定时发布时必填)
+ */
+export const apiVoicePublish=params=>{
+    return post('/voice/broadcast/publish',params)
 }

BIN
src/assets/voice/clock-icon.png


BIN
src/assets/voice/publish2.png


+ 71 - 10
src/layout/component/Aside.vue

@@ -1,8 +1,10 @@
 <script setup>
 import { reactive, ref, watch } from "vue";
 import { useRoute,useRouter } from "vue-router";
+import { useStore } from "vuex";
 const route = useRoute();
 const router= useRouter()
+const store=useStore()
 
 let activePath = ref("/report/index");
 watch(
@@ -14,14 +16,28 @@ watch(
     immediate: true,
   }
 );
+
+//点击有二级菜单的默认打开第一个
 const submenuEvent = (index)=>{
-  console.log(index)
-  if(index==6){
-    //展开第一项
-    router.push('/question/list')
+  let tem=null
+  menuList.forEach(_item=>{
+    if(_item.path==index){
+      tem=_item.children
+    }
+  })
+  if(tem){
+    let flag=false
+    tem.forEach(item=>{
+      if(item.path==route.path){
+        flag=true
+      }
+    })
+    if(flag) return
+    router.push(tem[0].path)
   }
 }
-const menuList = reactive([
+
+let menuList = reactive([
   {
     MenuId: 1,
     name: "研报",
@@ -56,7 +72,8 @@ const menuList = reactive([
     path: "/video/list",
     icon_path: new URL('../../assets/leftNav/video-s.png', import.meta.url).href,
     children: null,
-  },{
+  },
+  {
     MenuId: 6,
     name: "问答",
     path: "/question/list",
@@ -81,23 +98,67 @@ const menuList = reactive([
     children: null,
   }
 ]);
+
+// 设置语音播报侧边栏
+watch(
+  ()=>store.state.userInfo,
+  ()=>{
+    menuList.forEach(item=>{
+      if(item.MenuId==7){
+        if(store.state.userInfo?.is_voice_admin==1){
+          item.children=[
+            {
+              MenuId:7.1,
+              name:'语音播报',
+              path:'/voice/list'
+            },
+            {
+              MenuId:7.2,
+              name:'我的语音',
+              path:'/voice/mine'
+            },
+          ]
+        }else{
+          item.children=null
+        }
+      }
+    })
+  },
+  {
+    deep:true,
+    immediate:true
+  }
+)
+
 </script>
 
 <template>
   <div class="aside-wrap">
-    <el-menu router :default-active="activePath" unique-opened @open="submenuEvent"
-      text-color="#333" active-text-color="#F3A52F" class="el-menu-wrap">
+    <el-menu 
+      router 
+      :default-active="activePath" 
+      @open="submenuEvent"
+      unique-opened 
+      text-color="#333" 
+      active-text-color="#F3A52F" 
+      class="el-menu-wrap"
+    >
       <template v-for="menu in menuList">
         <el-menu-item :index="menu.path" :key="menu.MenuId" v-if="!menu.children">
           <img class="menu-icon" :src="menu.icon_path" alt="" />
           <span class="menu-text">{{ menu.name }}</span>
         </el-menu-item>
-        <el-sub-menu :index="menu.MenuId + ''" :key="menu.MenuId+1" v-else>
+        <el-sub-menu :index="menu.path" :key="menu.MenuId"  v-else>
           <template #title>
             <img class="menu-icon" :src="menu.icon_path" alt="" />
             <span class="menu-text">{{ menu.name }}</span>
           </template>
-          <el-menu-item v-for="child in menu.children" :key="child.MenuId" :index="child.path" style="text-align: center">
+          <el-menu-item 
+            v-for="child in menu.children" 
+            :key="child.MenuId" 
+            :index="child.path" 
+            style="text-align: center"
+          >
             {{ child.name }}
           </el-menu-item>
         </el-sub-menu>

+ 21 - 0
src/router/index.js

@@ -362,6 +362,27 @@ const routes=[
           keepAlive:true,
           isRoot:true
         }
+      },
+      {
+        path:"mine",
+        name:"VoiceMine",
+        component:()=>import('@/views/voice/Mine.vue'),
+        meta: {
+          title: "我的语音",
+          keepAlive:true,
+          isRoot:true
+        }
+      },
+      {
+        path:"detail",
+        name:"VoiceDetail",
+        component:()=>import('@/views/voice/Detail.vue'),
+        meta: {
+          title: "语音详情",
+          keepAlive:false,
+          isRoot:false,
+          hasBack:true
+        }
       }
     ]
   },

+ 1 - 1
src/store/index.js

@@ -142,7 +142,7 @@ export default createStore({
     // 设置面包屑数据
     // 在全局路由钩子中和自定义路由函数中调用
     setBreadCrumb(state,data){
-      console.log('store中设置面包屑',data);
+      // console.log('store中设置面包屑',data);
       // 如果是侧边栏 清除路由栈
       if (data.meta.isRoot) {
         let obj = {

+ 6 - 2
src/style/global.scss

@@ -14,6 +14,10 @@ body,
   // min-width: 1024px;
 }
 
+:root{
+  --el-color-primary:#F3A52F;
+}
+
 // 禁止页面打印
 @media print{
   body{
@@ -110,9 +114,9 @@ img {
 }
 
 .self-elmessage-confirm-btn {
-  background-color: #F3A52F;
+  background-color: #F3A52F !important;
   color: #fff;
-  border: none;
+  border: none !important;
   width: 90px;
 
   &:hover {

+ 3 - 3
src/utils/interceptRouterMethod.js

@@ -21,8 +21,8 @@ const routerPush=router.push //保存原来的push函数
 // 重写push函数
 router.push = function push(location) {
     // location 可能是string 也可能是obj 完全取决于 你是怎么调push的
-    console.log('push');
-    console.log(location);
+    // console.log('push');
+    // console.log(location);
 
     let path=''
     let query=null
@@ -45,7 +45,7 @@ router.push = function push(location) {
         const routesItem=getToRoute(path)
         store.commit('setBreadCrumb', routesItem)
     }else{
-        console.log('拦截路由push改为返回');
+        // console.log('拦截路由push改为返回');
         const _index=index-(store.state.breadCrumbList.length-1)
         if(_index!==0){
             router.go(_index)

+ 2 - 3
src/utils/reportErr.js

@@ -3,7 +3,6 @@ import {apiReportingErrInfo} from '@/api/common'
 
 const reportErr=(app)=>{
     let errObj={}
-
     app.config.errorHandler=(err, instance, info)=>{
         console.error(err)
         errObj={
@@ -18,8 +17,8 @@ const reportErr=(app)=>{
     window.addEventListener('error',(e)=>{
         console.error(e)
         errObj={
-            msg:e.error.message,
-            stack:e.error.stack
+            msg:e.error?.message,
+            stack:e.error?.stack
         }
         if(import.meta.env.MODE==='production'){
             apiReportingErrInfo({errInfo:errObj})

+ 2 - 6
src/views/question/List.vue

@@ -9,7 +9,6 @@ import {apiGetTagTree} from "@/api/common"
 import { apiApplyPermission } from '@/api/user'
 import{ref,reactive} from "vue"
 import {onBeforeRouteLeave,useRouter} from "vue-router"
-import { useElementSize } from '@vueuse/core'
 import {ElMessage,ElMessageBox} from "element-plus"
 import SelfList from '@/components/SelfList.vue'
 import {useStore} from "vuex"
@@ -21,9 +20,6 @@ const store = useStore()
 const router = useRouter()
 
 moment.locale('zh-cn');
-//监听列表页面版心宽度
-const listPageEl=ref('')
-const {width}=useElementSize(listPageEl)
 
 const askForm = ref(null)
 
@@ -318,8 +314,8 @@ const setCancelNickHandle = async() => {
   </div>
   <!-- 有权限 -->
   <template v-if="question.isAuthor == 1 && question.isFinish"> 
-    <div class="question-list-container" ref="listPageEl" >
-        <div class="top-nav-box" :style="{width:width+'px'}">
+    <div class="question-list-container">
+        <div class="top-nav-box" >
             <div class="first-nav-box">
                 <span 
                     v-for="item in question.optionList" 

+ 2 - 6
src/views/question/MyList.vue

@@ -13,16 +13,12 @@ import {useStore} from 'vuex'
 import {ElMessageBox} from "element-plus"
 import SelfList from '@/components/SelfList.vue'
 import {onBeforeRouteLeave,useRouter} from "vue-router"
-import { useElementSize } from '@vueuse/core'
 import moment from 'moment';
 
 const store = useStore()
 const router = useRouter()
 
 moment.locale('zh-cn');
-//监听列表页面版心宽度
-const listPageEl=ref('')
-const {width}=useElementSize(listPageEl)
 
 const askForm = ref(null)
 
@@ -327,8 +323,8 @@ getBarList()
     </div>
   </div>
   <template v-if="question.isAuthor == 1 && question.isFinish"> 
-    <div class="my-questionList-contain" ref="listPageEl">
-      <div class="top-tabbar-box" :style="{width:width+'px'}">
+    <div class="my-questionList-contain">
+      <div class="top-tabbar-box">
         <div class="top-tabbar-row" >
           <div v-for="item in question.barList" :key="item.key" @click="changeTopTar(item.key)"
           :class="['top-tabbar-item',item.key == question.selectKey?'tabbar-active':'']">

+ 1 - 0
src/views/video/List.vue

@@ -453,6 +453,7 @@ onActivated(()=>{
                 margin: 19px 0;
                 position: relative;
                 background-size: cover;
+                background-position: center;
                 cursor: pointer;
                 &::after{
                     content:'';

+ 369 - 0
src/views/voice/Detail.vue

@@ -0,0 +1,369 @@
+<script setup>
+import {ref,onMounted} from 'vue'
+import {apiVoiceDetail,apiVoiceDel,apiVoicePlayRecord,apiVoiceSendMsg} from '@/api/voice'
+import {apiGetWechatQRCode} from '@/api/common'
+import {apiApplyPermission} from '@/api/user'
+import { useRoute, useRouter } from 'vue-router'
+import { useStore } from 'vuex'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import moment from 'moment'
+
+const route=useRoute()
+const router=useRouter()
+const store=useStore()
+
+let voiceId=route.query.voiceId
+
+// 获取详情
+let noAuth=ref(null)
+let info=ref(null)
+const getDetail=async ()=>{
+    const res=await apiVoiceDetail({broadcast_id:Number(voiceId)})
+    if(res.code===200){
+        info.value=res.data
+        noAuth.value=null
+        document.title=res.data.SectionName||'播报详情'
+        store.commit('modifyBreadCrumb',res.data.SectionName||'播报详情')
+        handelGetQRCodeImg()
+        //向小程序发送消息
+        let postData = {
+            path: "/pages-voice/voiceDetail",
+            params:{
+                voiceId:info.BroadcastId
+            },
+            title: `${info.value.SectionName}:${info.value.BroadcastName}`,
+            shareImg:info.value.ImgUrl
+        };
+        wx.miniProgram.postMessage({ data: postData });
+        
+    }else if(res.code===403){
+        noAuth.value=res.data
+        handleAutoApply()
+        //向小程序发送消息
+        let postData = {
+            path: "/pages-voice/voiceDetail",
+            params:{
+                voiceId:voiceId
+            },
+            title: `播报详情`,
+            shareImg:''
+        };
+        wx.miniProgram.postMessage({ data: postData });
+    }
+}
+getDetail()
+
+//获取音频对应的小程序二维码
+const handelGetQRCodeImg=async ()=>{
+    const res=await apiGetWechatQRCode({
+        CodeScene:JSON.stringify({voiceId:info.value.BroadcastId}),
+        CodePage:'pages-voice/voiceDetail'
+    })
+    if(res.code===200){
+        info.value.QRCodeImg=res.data
+    }
+}
+
+//无权限时申请
+const handleAutoApply=()=>{
+    if(noAuth.value.type=='contact'&&!noAuth.value.customer_info.has_apply){
+        if(noAuth.value.customer_info.status=='冻结'||(noAuth.value.customer_info.status=='试用'&&noAuth.value.customer_info.is_suspend==1)){
+            apiApplyPermission({
+                company_name:noAuth.value.customer_info.company_name,
+                real_name:noAuth.value.customer_info.name,
+                source:8,
+                from_page:'语音播报'
+            }).then(res=>{
+                if(res.code===200){
+                    console.log('主动申请成功');
+                }
+            }) 
+        }
+    }
+}
+const handleApply=()=>{
+    if(noAuth.value.customer_info.has_apply){
+        const htmlStr=`<p>您已提交过申请,请耐心等待</p>`
+        ElMessageBox({
+            title:`语音播报`,
+            message:htmlStr,
+            center: true,
+            dangerouslyUseHTMLString: true,
+            confirmButtonText:'知道了',
+            confirmButtonClass:'self-elmessage-confirm-btn'
+        })
+    }else{
+        if (!noAuth.value.customer_info.status || noAuth.value.customer_info.status != '流失') {
+            console.log('跳转申请页');
+            router.push({
+                path:'/apply/permission',
+                query:{
+                    source:8,
+                    fromPage:'语音播报'
+                }
+            })
+        }else{
+            apiApplyPermission({
+                company_name:noAuth.value.customer_info.company_name,
+                real_name:noAuth.value.customer_info.name,
+                source:8,
+                from_page:'语音播报'
+            }).then(res=>{
+                onsole.log('主动申请成功');
+                const htmlStr=`<p>申请已提交</p><p>请等待销售人员与您联系</p>`
+                ElMessageBox({
+                    title:`语音播报`,
+                    message:htmlStr,
+                    center: true,
+                    dangerouslyUseHTMLString: true,
+                    confirmButtonText:'知道了',
+                    confirmButtonClass:'self-elmessage-confirm-btn'
+                })
+            })
+        }
+    }
+}
+
+//删除音频
+const handleDel=(item)=>{
+    ElMessageBox({
+        title:`温馨提醒`,
+        message:'确定要删除该语音播报吗?',
+        center: true,
+        dangerouslyUseHTMLString: true,
+        confirmButtonText:'确定',
+        confirmButtonClass:'self-elmessage-confirm-btn',
+        showCancelButton:true,
+        cancelButtonText:'取消',
+        cancelButtonClass:'self-elmessage-cancel-btn'
+    }).then(()=>{
+        if(store.state.audioData.voiceId==item.BroadcastId){
+            //删除的正在播放
+            store.commit('closeAudio')
+        }
+        apiVoiceDel({broadcast_id:Number(item.BroadcastId)}).then(res=>{
+            if(res.code===200){
+                ElMessage.success('操作成功')
+                if(window.history.state.back){
+                    console.log('返回上一页');
+                    router.go(-1)
+                }else{
+                    console.log('返回列表');
+                    router.push({path:'/voice/list'})
+                }
+            }
+        })
+    }).catch(()=>{
+            
+    })
+}
+
+//发送模板消息
+const handleSendMsg=async (item)=>{
+    ElMessageBox({
+        title:`温馨提醒`,
+        message:'该操作将推送模板消息和客群,确认推送吗?',
+        center: true,
+        dangerouslyUseHTMLString: true,
+        confirmButtonText:'确定',
+        confirmButtonClass:'self-elmessage-confirm-btn',
+        showCancelButton:true,
+        cancelButtonText:'取消',
+        cancelButtonClass:'self-elmessage-cancel-btn'
+    }).then(()=>{
+        apiVoiceSendMsg({broadcast_id:Number(item.BroadcastId)}).then(res=>{
+            if(res.code===200){
+                ElMessage.success('操作成功')
+                info.value.CouldSendMsg=false
+            }
+        })
+    }).catch(()=>{
+            
+    })
+}
+
+//处理音频时长显示
+const formarVoiceTime=(e)=>{
+    let m=parseInt(e/60)
+    let s=parseInt(e%60)
+    return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
+}
+
+//播放音频
+const handlePlay=(item)=>{
+    if(store.state.audioData.voiceId==item.BroadcastId){
+        if(store.state.audioData.paused){
+            store.state.audioData.INS.play()
+        }else{
+            store.state.audioData.INS.pause()
+        }
+        return
+    }
+    store.commit('addAudio',{
+        list:[{name:item.BroadcastName,url:item.VoiceUrl,time:item.VoicePlaySeconds}],
+        voiceId:item.BroadcastId,
+        index:0
+    })
+    handleVoiceRecord(item)
+}
+
+//上报音频播放记录
+const handleVoiceRecord=async (item)=>{
+    const res=await apiVoicePlayRecord({
+        broadcast_id:item.BroadcastId
+    })
+    if(res.code===200){
+        console.log('上报音频播放记录');
+    }
+}
+
+</script>
+
+<template>
+    <div class="voice-no-auth" v-if="noAuth">
+        <img :src="$store.state.globalImgUrls.activityNoAuth" alt="">
+        <p style="font-size:16px;margin-bottom: 0;">您暂无权限查看语音播报</p>
+        <template v-if="noAuth.type=='contact'">
+            <p style="font-size:16px;margin-top: 5px;margin-bottom: 62px;">若想查看,可以联系对口销售--{{noAuth.name}}:<span style="color:#F3A52F">{{noAuth.mobile}}</span></p>
+        </template>
+        <template v-else>
+            <p style="font-size:16px;margin-top: 5px;margin-bottom: 62px;">若想参加可以申请开通</p>
+            <div class="global-main-btn btn" @click="handleApply" style="margin-bottom: 20px;">立即申请</div>
+        </template>
+    </div>
+    <div class="voice-detail-page" v-if="!noAuth&&info">
+        <h2 class="title">{{info.BroadcastName}}</h2>
+        <p class="publish-time">发布时间:{{moment(info.CreateTime).format('YYYY-MM-DD HH:mm:ss')}}</p>
+        <div class="flex bot-box">
+            <div class="flex audio-box" @click.stop="handlePlay(info)">
+                <div :class="['icon',($store.state.audioData.voiceId==info.BroadcastId)&&!$store.state.audioData.paused?'active':'']"></div>
+                <div>{{formarVoiceTime(info.VoicePlaySeconds)}}</div>
+            </div>
+            <div class="flex btns-box">
+                <img 
+                    class="btn publish-btn" 
+                    v-if="info.CouldSendMsg"
+                    src="@/assets/voice/publish.png" 
+                    alt=""
+                    @click.stop="handleSendMsg(info)"
+                />
+                <img 
+                    class="btn del-btn" 
+                    v-if="info.IsAuthor" 
+                    src="@/assets/voice/del.png" 
+                    alt=""
+                    @click.stop="handleDel(info)"
+                />
+                <el-popover
+                    :width="200"
+                    trigger="hover"
+                >
+                    <template #reference>
+                        <div class="btn icon-wechat"></div>
+                    </template>
+                    <template #default>
+                        <img style="width:100%" :src="info.QRCodeImg" alt="">
+                    </template>
+                </el-popover>
+            </div>
+        </div>
+        <div class="img-box">
+            <el-image
+                style="width: 100%; height: auto"
+                :src="item"
+                :preview-src-list="info.Imgs"
+                :initial-index="index"
+                fit="contain"
+                v-for="item,index in info.Imgs" 
+                :key="item"
+            />
+        </div>
+
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.icon-wechat{
+    cursor: pointer;
+    width: 24px;
+    height: 24px;
+    background-image: url('@/assets/icon-wechat.png');
+    background-size: cover;
+    margin-left: 30px;
+    &:hover{
+        background-image: url('@/assets/icon-wechat2.png');
+    }
+}
+.voice-no-auth{
+    text-align: center;
+    img{
+        width: 400px;
+    }
+    .btn{
+        width: 218px;
+        margin-left: auto;
+        margin-right: auto;
+    }
+}
+.voice-detail-page{
+    border-left: 1px solid #F2F2F2;
+    border-right: 1px solid #F2F2F2;
+    padding: 30px;
+    .title{
+        font-size: 16px;
+        margin: 0 0 10px 0;
+    }
+    .publish-time{
+        margin: 0;
+        font-size: 14px;
+        color: #666;
+    }
+    .audio-box{
+        width: 193px;
+        height: 38px;
+        background: #F4E1C9;
+        border-radius: 28px;
+        color: #E3B377;
+        font-size: 14px;
+        align-items: center;
+        padding-left: 20px;
+        cursor: pointer;
+        .icon{
+            width: 16px;
+            height: 19px;
+            background-image: url('@/assets/voice/pause.png');
+            background-size: cover;
+            margin-right: 10px;
+        }
+        .active{
+            background-image: url('@/assets/voice/playing.png');
+        }
+    }
+    .bot-box{
+        justify-content: space-between;
+        align-items: baseline;
+        margin-top: 20px;
+        .btn{
+            margin-left: 30px;
+        }
+        .del-btn{
+            width: 23px;
+            height: 23px;
+            cursor: pointer;
+        }
+        .publish-btn{
+            width: 23px;
+            height: 23px;
+            cursor: pointer;
+        }
+    }
+    .img-box{
+        margin-top: 30px;
+        img{
+            width: 100%;
+            object-fit: contain;
+            margin-bottom: 30px;
+        }
+    }
+}
+</style>

+ 16 - 5
src/views/voice/List.vue

@@ -266,6 +266,16 @@ const handleVoiceRecord=async (item)=>{
     }
 }
 
+//跳转详情
+const goDetail=(item)=>{
+    router.push({
+        path:'/voice/detail',
+        query:{
+            voiceId:item.BroadcastId,     
+        }
+    })
+}
+
 
 onMounted(() => {
   //向小程序发送消息
@@ -324,9 +334,9 @@ onActivated(()=>{
         >
             <div class="list-wrap">
                 <div class="item" v-for="item in listState.list" :key="item.BroadcastId">
-                    <h2 class="title">{{item.BroadcastName}}</h2>
-                    <div class="time">发布时间:{{moment(item.CreateTime).format('YYYY-MM-DD HH:mm:ss')}}</div>
-                    <div class="flex audio-box" @click="handlePlay(item)">
+                    <h2 class="title" @click="goDetail(item)">{{item.BroadcastName}}</h2>
+                    <div class="time">发布时间:{{moment(item.PublishTime).format('YYYY-MM-DD HH:mm:ss')}}</div>
+                    <div class="flex audio-box" @click.stop="handlePlay(item)">
                         <div :class="['icon',($store.state.audioData.voiceId==item.BroadcastId)&&!$store.state.audioData.paused?'active':'']"></div>
                         <div>{{formarVoiceTime(item.VoicePlaySeconds)}}</div>
                     </div>
@@ -335,14 +345,14 @@ onActivated(()=>{
                         v-if="item.CouldSendMsg"
                         src="@/assets/voice/publish.png" 
                         alt=""
-                        @click="handleSendMsg(item)"
+                        @click.stop="handleSendMsg(item)"
                     />
                     <img 
                         class="del-btn" 
                         v-if="item.IsAuthor" 
                         src="@/assets/voice/del.png" 
                         alt=""
-                        @click="handleDel(item)"
+                        @click.stop="handleDel(item)"
                     />
                     <el-popover
                         :width="200"
@@ -391,6 +401,7 @@ onActivated(()=>{
             .title{
                 font-size: 16px;
                 margin: 0 0 10px 0;
+                cursor: pointer;
             }
             .time{
                 color: #666;

+ 493 - 0
src/views/voice/Mine.vue

@@ -0,0 +1,493 @@
+<script setup>
+import {ref,reactive,onMounted,onActivated} from 'vue'
+import {apiGetWechatQRCode} from '@/api/common'
+import {apiVoiceList,apiVoiceDel,apiVoicePlayRecord,apiVoiceSendMsg,apiMyVoiceCount,apiVoicePublish} from '@/api/voice'
+import {apiApplyPermission,apiUserInfo} from '@/api/user'
+import SelfList from '@/components/SelfList.vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { useElementSize } from '@vueuse/core'
+import moment from 'moment'
+import { useRoute, useRouter } from 'vue-router'
+import { useStore } from 'vuex'
+
+const router=useRouter()
+const route=useRoute()
+const store=useStore()
+
+//监听列表页面版心宽度
+const listPageEl=ref('')
+const {width}=useElementSize(listPageEl)
+
+let user_id=store.state.userInfo?.user_id||''
+const init=async ()=>{
+    if(!user_id){
+        const res=await apiUserInfo()
+        if(res.code===200){
+            user_id=res.data.user_id
+        }
+    }
+    getStatusCount()
+    getVoiceList()
+}
+
+
+//状态选项
+let status=reactive({
+    list:[
+        {
+            lable:'未发布',
+            count:0,
+            value:0,
+            key:'Unpublished'
+        },
+        {
+            lable:'已发布',
+            count:0,
+            value:1,
+            key:'Published'
+        },
+        {
+            lable:'全部',
+            count:0,
+            value:2,
+            key:'All'
+        }
+    ],
+    select:0
+})
+const getStatusCount=async ()=>{
+    const res=await apiMyVoiceCount({author_id:user_id})
+    if(res.code===200){
+        status.list.forEach(item=>{
+            item.count=res.data[item.key]
+        })
+    }
+}
+
+
+//状态切换
+const handleChangeStatus=(item)=>{
+    status.select=item.value
+    listState.finished=false
+    listState.page=1
+    listState.list=[]
+    getStatusCount()
+    getVoiceList()
+}
+
+
+//获取音频列表数据
+let listState = reactive({
+    loading:false,
+    finished:false,
+    page:1,
+    pageSize:20,
+    list:[],
+})
+const getVoiceList=async ()=>{
+    listState.loading=true
+    const res=await apiVoiceList({
+        page_index:Number(listState.page),
+        page_size:listState.pageSize,
+        author_id:Number(user_id),
+        mine_status:status.select
+    })
+    listState.loading=false
+    if(res.code===200){
+        let arr=res.data.List||[]
+        listState.list=[...listState.list,...arr]
+        if(arr.length===0||arr.length<listState.pageSize){
+            listState.finished=true
+        }
+    }
+}
+
+// 加载下一页
+const onLoad=()=>{
+    listState.page++
+    getVoiceList()
+}
+
+//处理音频时长显示
+const formarVoiceTime=(e)=>{
+    let m=parseInt(e/60)
+    let s=parseInt(e%60)
+    return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
+}
+
+//获取音频单个对应的小程序二维码
+const handelGetQRCodeImg=async (item)=>{
+    if(item.QRCodeImg) return
+    const res=await apiGetWechatQRCode({
+        CodeScene:JSON.stringify({voiceId:item.BroadcastId}),
+        CodePage:'pages-voice/voiceDetail'
+    })
+    if(res.code===200){
+        item.QRCodeImg=res.data
+    }
+}
+
+//删除音频
+const handleDel=(item)=>{
+    ElMessageBox({
+        title:`温馨提醒`,
+        message:'确定要删除该语音播报吗?',
+        center: true,
+        dangerouslyUseHTMLString: true,
+        confirmButtonText:'确定',
+        confirmButtonClass:'self-elmessage-confirm-btn',
+        showCancelButton:true,
+        cancelButtonText:'取消',
+        cancelButtonClass:'self-elmessage-cancel-btn'
+    }).then(()=>{
+        if(store.state.audioData.voiceId==item.BroadcastId){
+            //删除的正在播放
+            store.commit('closeAudio')
+        }
+        apiVoiceDel({broadcast_id:Number(item.BroadcastId)}).then(res=>{
+            if(res.code===200){
+                ElMessage.success('操作成功')
+                listState.page=1
+                listState.finished=false
+                listState.list=[]
+                getStatusCount()
+                getVoiceList()
+            }
+        })
+    }).catch(()=>{
+            
+    })
+}
+
+//发送模板消息
+const handleSendMsg=async (item)=>{
+    ElMessageBox({
+        title:`温馨提醒`,
+        message:'该操作将推送模板消息和客群,确认推送吗?',
+        center: true,
+        dangerouslyUseHTMLString: true,
+        confirmButtonText:'确定',
+        confirmButtonClass:'self-elmessage-confirm-btn',
+        showCancelButton:true,
+        cancelButtonText:'取消',
+        cancelButtonClass:'self-elmessage-cancel-btn'
+    }).then(()=>{
+        apiVoiceSendMsg({broadcast_id:Number(item.BroadcastId)}).then(res=>{
+            if(res.code===200){
+                ElMessage.success('操作成功')
+                item.CouldSendMsg=false
+            }
+        })
+    }).catch(()=>{
+            
+    })
+}
+
+//发布语音&推送
+const handlePublish=(item)=>{
+    ElMessageBox({
+        title:`温馨提醒`,
+        message:'该操作将发布并且推送模板消息和客群',
+        center: true,
+        dangerouslyUseHTMLString: true,
+        confirmButtonText:'确定',
+        confirmButtonClass:'self-elmessage-confirm-btn',
+        showCancelButton:true,
+        cancelButtonText:'取消',
+        cancelButtonClass:'self-elmessage-cancel-btn'
+    }).then(async ()=>{
+        const pubRes=await apiVoicePublish({
+            broadcast_id:Number(item.BroadcastId),
+            publish_type:1
+        })
+        if(pubRes.code===200){
+            const sendRes=await apiVoiceSendMsg({broadcast_id:item.BroadcastId})
+            if(sendRes.code===200){
+                ElMessage.success('发布且推送成功')
+                listState.page=1
+                listState.list=[]
+                listState.finished=false
+                getStatusCount()
+                getVoiceList()
+            }else{
+                ElMessage.warning(sendRes.msg)
+            }
+        }else{
+            ElMessage.warning(pubRes.msg)
+        }
+    }).catch(()=>{
+            
+    })
+}
+
+//播放音频
+const handlePlay=(item)=>{
+    if(store.state.audioData.voiceId==item.BroadcastId){
+        if(store.state.audioData.paused){
+            store.state.audioData.INS.play()
+        }else{
+            store.state.audioData.INS.pause()
+        }
+        return
+    }
+    store.commit('addAudio',{
+        list:[{name:item.BroadcastName,url:item.VoiceUrl,time:item.VoicePlaySeconds}],
+        voiceId:item.BroadcastId,
+        index:0
+    })
+    if(item.PublishState==0) return //待发布的不记录
+    handleVoiceRecord(item)
+}
+
+//上报音频播放记录
+const handleVoiceRecord=async (item)=>{
+    const res=await apiVoicePlayRecord({
+        broadcast_id:item.BroadcastId
+    })
+    if(res.code===200){
+        console.log('上报音频播放记录');
+    }
+}
+
+//跳转详情
+const goDetail=async (item)=>{
+    if(item.PublishState==0){
+        const res=await apiGetWechatQRCode({
+            CodeScene:JSON.stringify({voiceId:item.BroadcastId}),
+            CodePage:'pages-voice/addVoice'
+        })
+        let htmlStr=`<div>PC端暂不支持编辑语音播报,请扫码进入小程序编辑</div>`
+        if(res.code===200){
+            htmlStr=`<div style="margin-bottom:10px">PC端暂不支持编辑语音播报,请扫码进入小程序编辑</div><div><img style="width:150px;height:150px" src="${res.data}" ></div>`
+        }
+
+        ElMessageBox({
+            title:`温馨提醒`,
+            message:htmlStr,
+            center: true,
+            dangerouslyUseHTMLString: true,
+            confirmButtonText:'知道了',
+            confirmButtonClass:'self-elmessage-confirm-btn',
+            showCancelButton:false,
+            cancelButtonText:'取消',
+            cancelButtonClass:'self-elmessage-cancel-btn'
+        }).then(()=>{}).catch(()=>{})
+    }else{
+        router.push({
+            path:'/voice/detail',
+            query:{
+                voiceId:item.BroadcastId,     
+            }
+        })
+    }
+    
+}
+
+
+onMounted(() => {
+    init()
+    //向小程序发送消息
+    let postData = {
+        path: "/pages/voice/voice",
+        params:{},
+        title: "语音播报",
+        shareImg:''
+    };
+    wx.miniProgram.postMessage({ data: postData });
+});
+onActivated(()=>{
+    //向小程序发送消息
+    let postData = {
+        path: "/pages/voice/voice",
+        params:{},
+        title: "语音播报",
+        shareImg:''
+    };
+    wx.miniProgram.postMessage({ data: postData });
+})
+
+
+</script>
+
+<template>
+    <div class="my-voice-list-page" ref="listPageEl">
+        <div class="top-nav-list-box" :style="{width:width+'px'}">
+            <span 
+                v-for="item in status.list" :key="item.value" 
+                :class="item.value==status.select?'active':''"
+                @click="handleChangeStatus(item)"
+            >{{item.lable}}({{item.count}})</span>
+        </div>
+
+        <SelfList 
+            :finished="listState.finished" 
+            :isEmpty="listState.list.length===0&&listState.finished"
+            :loading="listState.loading"
+            :count="listState.list.length"
+            @listOnload="onLoad"
+        >
+            <div class="list-wrap">
+                <div class="item" v-for="item in listState.list" :key="item.BroadcastId">
+                    <h2 class="title" @click="goDetail(item)">{{item.BroadcastName}}</h2>
+                    <div class="time">{{item.PublishState==0?'保存':'发布'}}时间:{{moment(item.PublishState==0?item.ModifyTime:item.PublishTime).format('YYYY-MM-DD HH:mm:ss')}}</div>
+                    <div class="flex audio-box" @click="handlePlay(item)">
+                        <div :class="['icon',($store.state.audioData.voiceId==item.BroadcastId)&&!$store.state.audioData.paused?'active':'']"></div>
+                        <div>{{formarVoiceTime(item.VoicePlaySeconds)}}</div>
+                    </div>
+                    <div class="btns-box">
+                        <el-popover
+                            :width="200"
+                            trigger="hover"
+                            @show="handelGetQRCodeImg(item)"
+                        >
+                            <template #reference>
+                                <div class="icon-wechat" v-if="item.PublishState!=0"></div>
+                            </template>
+                            <template #default>
+                                <img style="width:100%" :src="item.QRCodeImg" alt="">
+                            </template>
+                        </el-popover>
+                        <img 
+                            class="btn del-btn" 
+                            v-if="item.IsAuthor" 
+                            src="@/assets/voice/del.png" 
+                            alt=""
+                            @click.stop="handleDel(item)"
+                        />
+                        <img 
+                            class="btn  publish-btn" 
+                            v-if="item.CouldSendMsg&&item.PublishState!=0"
+                            src="@/assets/voice/publish.png" 
+                            alt=""
+                            @click.stop="handleSendMsg(item)"
+                        />
+                        <img 
+                            class="btn  clock-btn" 
+                            v-if="item.PublishState==0"
+                            src="@/assets/voice/publish2.png" 
+                            alt=""
+                            @click.stop="handlePublish(item)"
+                        />
+                    </div>
+                </div>
+            </div>
+        </SelfList>
+    </div>
+
+
+
+</template>
+
+<style lang="scss" scoped>
+.icon-wechat{
+    cursor: pointer;
+    width: 24px;
+    height: 24px;
+    background-image: url('@/assets/icon-wechat.png');
+    background-size: cover;
+    float: right;
+    margin-left: 60px;
+    &:hover{
+        background-image: url('@/assets/icon-wechat2.png');
+    }
+}
+.my-voice-list-page{
+    padding-top: 64px;
+    .top-nav-list-box{
+        position: fixed;
+        top: 60px;
+        padding: 20px;
+        width: 100%;
+        max-width: 1240px;
+        z-index: 10;
+        background: #FFFFFF;
+        box-shadow: 0px 4px 8px 0px rgba(0,0,0,0.04);
+        border: 1px solid #F2F2F2;
+        span{
+            width: 140px;
+            height: 40px;
+            background: #F6F6F6;
+            border-radius: 20px;
+            text-align: center;
+            display: inline-block;
+            margin-right: 30px;
+            line-height: 40px;
+            cursor: pointer;
+            font-size: 16px;
+        }
+
+        .active{
+            background: #FFFBF5;
+            box-shadow: 0px 6px 7px 0px #FFF7EB;
+            border-radius: 20px;
+            border: 1px solid #F3A52F;
+            color: #F3A52F;
+        }
+    }
+
+    .list-wrap{
+        .item{
+            padding: 20px;
+            border-left: 1px solid #F2F2F2;
+            border-top: 1px solid #F2F2F2;
+            border-right: 1px solid #F2F2F2;
+            position: relative;
+            &:last-child{
+                border-bottom: 1px solid #F2F2F2;
+            }
+            .title{
+                font-size: 16px;
+                margin: 0 0 10px 0;
+                cursor: pointer;
+            }
+            .time{
+                color: #666;
+                font-size: 14px;
+            }
+            .audio-box{
+                width: 193px;
+                height: 38px;
+                background: #F4E1C9;
+                border-radius: 28px;
+                color: #E3B377;
+                font-size: 14px;
+                align-items: center;
+                margin-top: 20px;
+                padding-left: 20px;
+                cursor: pointer;
+                .icon{
+                    width: 16px;
+                    height: 19px;
+                    background-image: url('@/assets/voice/pause.png');
+                    background-size: cover;
+                    margin-right: 10px;
+                }
+                .active{
+                    background-image: url('@/assets/voice/playing.png');
+                }
+            }
+            .btns-box{
+                position: absolute;
+                right: 20px;
+                bottom: 30px;
+                .btn{
+                    float: right;
+                    cursor: pointer;
+                    margin-left: 60px;
+                }
+                .publish-btn{
+                    width: 23px;
+                    height: 23px;
+                }
+                .del-btn{
+                    width: 23px;
+                    height: 23px;
+                }
+                .clock-btn{
+                    width: 23px;
+                }
+            }
+        }
+    }
+}
+</style>