Jelajahi Sumber

Merge branch 'yb9.1'

jwyu 2 tahun lalu
induk
melakukan
33850ddfcb

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.0.0-beta.0",
+    "@vueuse/core": "^9.0.0",
     "axios": "^0.26.0",
     "element-plus": "^2.0.2",
     "lodash": "^4.17.21",

+ 8 - 0
src/api/voice.js

@@ -42,4 +42,12 @@ export const apiVoicePlayRecord=params=>{
 		source=4
 	}
     return post('/voice/broadcast/statistics/add',{...params,source:source})
+}
+
+/**
+ * 推送客群、模板消息
+ * @param broadcast_id
+ */
+ export const apiVoiceSendMsg=params=>{
+    return post('/voice/broadcast/msg_send',params)
 }

TEMPAT SAMPAH
src/assets/voice/publish.png


+ 2 - 2
src/components/LoginPop.vue

@@ -257,7 +257,7 @@ onMounted(()=>{
                 <div class="bind-accout-box" v-show="form.type=='email'">
                     <h2 class="title">邮箱登录</h2>
                     <div class="input-item">
-                        <input type="text" v-model="form.email" placeholder="请输入手机号">
+                        <input type="text" v-model="form.email" placeholder="请输入邮箱">
                     </div>
                     <div class="input-item">
                         <input v-model="form.code" placeholder="请输入验证码" />
@@ -322,7 +322,7 @@ onMounted(()=>{
                 <template v-if="form.type=='email'">
                 <h2 class="title">邮箱绑定</h2>
                 <div class="input-item">
-                    <input type="text" v-model="form.email" placeholder="请输入手机号">
+                    <input type="text" v-model="form.email" placeholder="请输入邮箱">
                 </div>
                 <div class="input-item">
                     <input v-model="form.code" placeholder="请输入验证码" />

+ 1 - 0
src/layout/Index.vue

@@ -79,6 +79,7 @@ import LoginPop from '@/components/LoginPop.vue'
     // min-width: 820px;
     max-width: 1240px;
     margin: 0 auto;
+    min-height: 600px;
   }
 }
 .el-footer{

+ 6 - 0
src/layout/component/Aside.vue

@@ -140,6 +140,12 @@ const menuList = reactive([
   font-size: 16px;
   font-weight: bold;
 }
+.el-sub-menu .el-menu-item{
+  padding: 0 20px !important;
+  box-sizing: border-box;
+  width: 100%;
+  min-width: 100%;
+}
 ul,
 li {
   box-sizing: border-box;

+ 4 - 0
src/store/index.js

@@ -32,6 +32,7 @@ export default createStore({
       activityId:0,//活动的id
       reportId:0,//报告的id
       voiceId:0,//语音播报列表id
+      questionId:0,//问答社区的id
       paused:true,//当前音频播放状态
       show:false,//是否显示音频弹窗
     },
@@ -71,6 +72,7 @@ export default createStore({
       state.audioData.activityId=e.activityId||0
       state.audioData.reportId=e.reportId||0
       state.audioData.voiceId=e.voiceId||0
+      state.audioData.questionId=e.questionId||0
       state.audioData.show=true
       state.audioData.INS.play()
     },
@@ -96,6 +98,7 @@ export default createStore({
           state.audioData.activityId=0
           state.audioData.reportId=0
           state.audioData.voiceId=0
+          state.audioData.questionId=0
           state.audioData.INS=null
           state.audioData.show=false
           state.audioData.paused=false
@@ -111,6 +114,7 @@ export default createStore({
       state.audioData.activityId=0
       state.audioData.reportId=0
       state.audioData.voiceId=0
+      state.audioData.questionId=0
       state.audioData.INS=null
       state.audioData.show=false
       state.audioData.paused=false

+ 50 - 121
src/views/question/List.vue

@@ -11,6 +11,7 @@ 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"
 import moment from 'moment';
 
@@ -82,6 +83,7 @@ const getOptionList =async ()=>{
     getQuestionList()
   }
 }
+getOptionList()
   // 联系销售
 const toContact = ()=>{
   // 请求申请接口
@@ -197,7 +199,7 @@ const getQuestionList=()=>{
   })
 }
 // 加载更多
-const loadingMore = ()=>{
+const onLoad = ()=>{
   question.page_index++
   getQuestionList()
 }
@@ -251,86 +253,31 @@ const submit = (formEl)=>{
   })
 }
 
-// 点击播放/暂停按钮
-const audioPlay = (item,index)=>{
-  if(item.audio_status.isPlay){
-    pause(item,index)
-  }else{
-    if(question.currentClickId != item.audio_list[0].community_question_audio_id){
-      question.currentClickId = item.audio_list[0].community_question_audio_id
-      // 增加点击数
-      apiCountAudioClick({
-          community_question_audio_id:question.currentClickId
-      }).then((res)=>{
-          if(res.code===200){
-            console.log('音频id为'+question.currentClickId+'点击次数+1')  
-          }  
-      })
+//播放音频
+const handlePlay=(item)=>{
+    if(store.state.audioData.questionId==item.community_question_id){
+        if(store.state.audioData.paused){
+            store.state.audioData.INS.play()
+        }else{
+            store.state.audioData.INS.pause()
+        }
+        return
     }
-    play(item,index)
-  }
+    const audioItem=item.audio_list[0]
+    store.commit('addAudio',{
+        list:[{name:item.question_content,url:audioItem.audio_url,time:audioItem.audio_play_seconds}],
+        questionId:item.community_question_id,
+        index:0
+    })
+    apiCountAudioClick({
+        community_question_audio_id:audioItem.community_question_audio_id
+    }).then((res)=>{
+        if(res.code===200){
+          console.log('音频id为'+audioItem.community_question_audio_id+'点击次数+1')  
+        }  
+    })
 }
 
-// type: pause 暂停 end结束
-const pause=(item,index,type='pause')=>{
-    audio.value[index].pause()
-    clearInterval(timer)
-    if(type == 'end'){
-      audio.value[index].currentTime = 0
-      item.audio_status.playedTime=0
-    }
-    item.audio_status.isPlay = false
-    question.audioPlayingIndex=-1
-    question.audioPlayingItem = null
-    item.audio_status.loading = false
-}
-// 播放
-const play=(item,index)=>{
-  if(audio.value[index].readyState!=4){
-    item.audio_status.loading=true
-    return 
-  }
-  if( question.audioPlayingIndex!= -1 ){
-    pause(question.audioPlayingItem,question.audioPlayingIndex,'end')
-  }
-  audio.value[index].play()
-  question.audioPlayingIndex = index
-  question.audioPlayingItem = item
-  timer = setInterval(()=>{
-    // 播放结束
-    if(audio.value[index].ended){
-      item.audio_status.playedTime = 0
-      item.audio_status.isPlay = false
-      question.audioPlayingIndex=-1
-      question.audioPlayingItem = null
-      question.currentClickId=0
-      clearInterval(timer)
-      return 
-    }
-    item.audio_status.playedTime += 1000
-  },1000)
-  item.audio_status.isPlay = true
-  item.audio_status.loading = false
-}
-// 等待 网络差时等待资源加载
-const audioWaiting = (item)=>{
-  item.audio_status.loading = true
-}
-// 可以播放 
-const audioCanPlay = (item,index)=>{
-  console.log(item,index)
-  item.audio_status.loading = false
-  if(!audio.value[index].played){
-    play(item,index)
-  }
-}
-
-getOptionList()
-onBeforeRouteLeave(()=>{
-  if( question.audioPlayingIndex!= -1 ){
-    pause(question.audioPlayingItem,question.audioPlayingIndex,'end')
-  }
-})
 
 
 /* 设置昵称弹窗 */
@@ -412,13 +359,14 @@ const setCancelNickHandle = async() => {
                 </el-popover>
             </div>
         </div>
-        <!-- 无数据 -->
-        <div class="question-noData" v-show="question.list.length==0 && !question.isLoading">
-          <img :src="$store.state.globalImgUrls.activityNoAuth" alt="没有数据">
-          <span>暂无数据</span>
-        </div>
-        <div class="question-list" v-show = "question.list.length>0 && question.isFinish">
-          <div class="question-item" v-for="(item,index) in question.list" :key="item.community_question_id">
+        <SelfList 
+            :finished="question.isTotalData" 
+            :isEmpty="question.list.length===0&&question.isTotalData"
+            :loading="question.isLoading"
+            @listOnload="onLoad"
+        >
+        <div class="question-list">
+          <div class="question-item" v-for="item in question.list" :key="item.community_question_id">
             <div class="question-info">
               <div class="label">{{item.research_group_second_name}}</div>
               {{item.question_content}}
@@ -426,37 +374,26 @@ const setCancelNickHandle = async() => {
             <div class="question-time">
               提问时间:{{moment(item.create_time).format('YYYY.MM.DD')}}
             </div>
-            <div class="question-audio">
-              <audio :autoplay="false" :loop="false" preload ref="audio"
-              @waiting="audioWaiting(item,index)" @canplay="audioCanPlay(item,index)">
-                <source :src="item.audio_list[0].audio_url" type="audio/mp3"  />
-                <p>你的浏览器不支持 HTML5 音频,请直接下载<a :href="item.audio_list[0].audio_url">音频文件</a>。</p>
-              </audio>
-              <div class="audio-icon" @click="audioPlay(item,index)" v-if="!item.audio_status.loading">
-                <img src="@/assets/recordplay.png" alt="播放" v-show="!item.audio_status.isPlay" />
-                <img src="@/assets/recordpause.png" alt="暂停" v-show="item.audio_status.isPlay" />
-              </div>
-              <div class="audio-icon loading-icon" @click="pause(item,index)" v-else>
-                <img class="load-img" src="@/assets/loading.png" alt="加载中">
-              </div>
+            <div class="question-audio" @click="handlePlay(item)">
+              <div :class="['audio-icon',($store.state.audioData.questionId==item.community_question_id)&&!$store.state.audioData.paused?'active':'']"></div>
               <div class="audio-pic">
                 <img src="@/assets/audio-waveform1.png">
                 <img src="@/assets/audio-waveform2.png">
               </div>
-              <div class="audio-time">{{moment((item.audio_status.audioTime - item.audio_status.playedTime)).format('mm:ss')}}</div>
+              <div class="audio-time">{{moment(item.audio_status.audioTime).format('mm:ss')}}</div>
             </div>
-
             <!-- 点赞/评论 -->
             <QuestionComment :item="item" @open_nick_dia="openNickDialog"/>
           </div>
-          <div class="bottom-tip-contain">
-            <div class="bottom-tip-loadMore" v-show="!question.isTotalData && !question.isLoading" @click="loadingMore">加载更多</div>
-            <div class="loading-text" v-show="question.isLoading">加载中······</div>
-            <div class="bottom-tip-noMore" v-show="question.isTotalData && !question.isLoading">没有更多了~</div>
-          </div>
         </div>
-        <el-dialog title="提问详情" v-model="question.showAskDia" :close-on-click-modal="false" draggable 
-        :width="600" >
+        </SelfList>
+        <el-dialog 
+          title="提问详情" 
+          v-model="question.showAskDia" 
+          :close-on-click-modal="false" 
+          draggable 
+          :width="600" 
+        >
           <el-form :model="question.askForm" ref="askForm" class="ask-form">
             <div class="ask-label">
               问题描述
@@ -692,20 +629,12 @@ const setCancelNickHandle = async() => {
         padding: 0 54px 0 27px;
         .audio-icon{
           cursor: pointer;
-          img{
-            width: 16px;
-          }
-          .load-img{
-            width: 20px;
-            animation: circle 0.5s linear infinite;
-          }
-          @keyframes circle {
-            0%{
-                transform: rotateZ(0);
-            }
-            100%{
-                transform: rotateZ(360deg);
-            }
+          width: 16px;
+          height: 19.5px;
+          background-size: cover;
+          background-image: url('@/assets/recordplay.png');
+          &.active{
+            background-image: url('@/assets/recordpause.png');
           }
         }
         .audio-pic{

+ 46 - 116
src/views/question/MyList.vue

@@ -11,6 +11,7 @@ import { apiApplyPermission } from '@/api/user'
 import{ref,reactive} from "vue"
 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';
@@ -295,86 +296,31 @@ const toDetail = (item)=>{
 }
 
 
-// 点击播放/暂停按钮
-const audioPlay = (item)=>{
-  let index = item.audio_status.audioIndex
-  if(item.audio_status.isPlay){
-    pause(item,index)
-  }else{
-    if(question.currentClickId != item.audio_list[0].community_question_audio_id){
-      question.currentClickId = item.audio_list[0].community_question_audio_id
-      // 增加点击数
-      apiCountAudioClick({
-          community_question_audio_id:question.currentClickId
-      }).then((res)=>{
-          if(res.code===200){
-            console.log('音频id为'+question.currentClickId+'点击次数+1')  
-          }  
-      })
-    }
-    play(item,index)
-  }
-}
-
-// type: pause 暂停 end结束
-const pause=(item,index,type='pause')=>{
-    audio.value[index].pause()
-    clearInterval(timer)
-    if(type == 'end'){
-      audio.value[index].currentTime = 0
-      item.audio_status.playedTime=0
+//播放音频
+const handlePlay=(item)=>{
+    if(store.state.audioData.questionId==item.community_question_id){
+        if(store.state.audioData.paused){
+            store.state.audioData.INS.play()
+        }else{
+            store.state.audioData.INS.pause()
+        }
+        return
     }
-    item.audio_status.isPlay = false
-    question.audioPlayingIndex=-1
-    question.audioPlayingItem = null
-    item.audio_status.loading = false
+    const audioItem=item.audio_list[0]
+    store.commit('addAudio',{
+        list:[{name:item.question_content,url:audioItem.audio_url,time:audioItem.audio_play_seconds}],
+        questionId:item.community_question_id,
+        index:0
+    })
+    apiCountAudioClick({
+        community_question_audio_id:audioItem.community_question_audio_id
+    }).then((res)=>{
+        if(res.code===200){
+          console.log('音频id为'+audioItem.community_question_audio_id+'点击次数+1')  
+        }  
+    })
 }
 
-const play=(item,index)=>{
-  if(audio.value[index].readyState!=4){
-    item.audio_status.loading=true
-    return 
-  }
-  if( question.audioPlayingIndex!= -1 ){
-    pause(question.audioPlayingItem,question.audioPlayingIndex,'end')
-  }
-  audio.value[index].play()
-  question.audioPlayingIndex = index
-  question.audioPlayingItem = item
-  timer = setInterval(()=>{
-    // 播放结束
-    if(audio.value[index].ended){
-      item.audio_status.playedTime = 0
-      item.audio_status.isPlay = false
-      question.audioPlayingIndex=-1
-      question.audioPlayingItem = null
-      question.currentClickId=0
-      clearInterval(timer)
-      return 
-    }
-    item.audio_status.playedTime += 1000
-  },1000)
-  item.audio_status.isPlay = true
-  item.audio_status.loading = false
-}
-// 等待 网络差时等待资源加载
-const audioWaiting = (item)=>{
-  item.audio_status.loading = true
-}
-// 可以播放 
-const audioCanPlay = (item)=>{
-  item.audio_status.loading = false
-  let index = item.audio_status.audioIndex
-  if(!audio.value[index].played){
-    play(item,index)
-  }
-}
-onBeforeRouteLeave(()=>{
-  if( question.audioPlayingIndex!= -1 ){
-    pause(question.audioPlayingItem,question.audioPlayingIndex,'end')
-  }
-})
-
 getBarList()
 </script>
 
@@ -412,11 +358,13 @@ getBarList()
           </div>
         </div>
       </div>
-        <div class="question-noData" v-show="question.list.length==0 && !question.isLoading">
-          <img :src="$store.state.globalImgUrls.activityNoAuth" alt="没有数据">
-          <span>暂无数据</span>
-        </div>
-        <div class="question-list" v-show = "question.list.length>0 && question.isFinish">
+      <SelfList 
+        :finished="question.isTotalData" 
+        :isEmpty="question.list.length===0&&question.isTotalData"
+        :loading="question.isLoading"
+        @listOnload="onLoad"
+      >
+        <div class="question-list">
           <div class="question-item" v-for="item in question.list" :key="item.community_question_id">
             <div class="question-info" @click="toDetail(item)">
               <div class="label" 
@@ -428,19 +376,8 @@ getBarList()
             <div class="question-time">
               提问时间:{{moment(item.create_time).format('YYYY.MM.DD')}}
             </div>
-            <div class="question-audio" v-if="item.reply_status === 3">
-              <audio :autoplay="false" :loop="false" preload ref="audio"
-              @waiting="audioWaiting(item)" @canplay="audioCanPlay(item)">
-                <source :src="item.audio_list[0].audio_url" type="audio/mp3"  />
-                <p>你的浏览器不支持 HTML5 音频,请直接下载<a :href="item.audio_list[0].audio_url">音频文件</a>。</p>
-              </audio>
-              <div class="audio-icon" @click="audioPlay(item)" v-if="!item.audio_status.loading">
-                <img src="@/assets/recordplay.png" alt="播放" v-show="!item.audio_status.isPlay" />
-                <img src="@/assets/recordpause.png" alt="暂停" v-show="item.audio_status.isPlay" />
-              </div>
-              <div class="audio-icon loading-icon" @click="pause(item,item.audio_status.audioIndex)" v-else>
-                <img class="load-img" src="@/assets/loading.png" alt="加载中">
-              </div>
+            <div class="question-audio" v-if="item.reply_status === 3" @click="handlePlay(item)">
+              <div :class="['audio-icon',($store.state.audioData.questionId==item.community_question_id)&&!$store.state.audioData.paused?'active':'']"></div>
               <div class="audio-pic">
                 <img src="@/assets/audio-waveform1.png">
                 <img src="@/assets/audio-waveform2.png">
@@ -448,17 +385,18 @@ getBarList()
               <div class="audio-time">{{moment((item.audio_status.audioTime - item.audio_status.playedTime)).format('mm:ss')}}</div>
             </div>
           </div>
-          <div class="bottom-tip-contain">
-            <div class="bottom-tip-loadMore" v-show="!question.isTotalData && !question.isLoading" @click="loadingMore">加载更多</div>
-            <div class="loading-text" v-show="question.isLoading">加载中······</div>
-            <div class="bottom-tip-noMore" v-show="question.isTotalData && !question.isLoading">没有更多了~</div>
-          </div>
         </div>
+      </SelfList>
     </div>
   </template>
   <!-- 回答弹窗 -->
-  <el-dialog title="回答提示" v-model="question.showReplayDia" :close-on-click-modal="false" draggable 
-  :width="407" >
+  <el-dialog 
+    title="回答提示" 
+    v-model="question.showReplayDia" 
+    :close-on-click-modal="false" 
+    draggable 
+    :width="407"
+  >
     <div class="replay-dialog-item">
       <p>PC端暂不支持回答问题,请扫码进入小程序回答</p>
       <img :src="question.replayCodeUrl" />
@@ -579,20 +517,12 @@ getBarList()
           padding: 0 54px 0 27px;
           .audio-icon{
             cursor: pointer;
-            img{
-              width: 16px;
-            }
-            .load-img{
-              width: 20px;
-              animation: circle 0.5s linear infinite;
-            }
-            @keyframes circle {
-              0%{
-                  transform: rotateZ(0);
-              }
-              100%{
-                  transform: rotateZ(360deg);
-              }
+            width: 16px;
+            height: 19.5px;
+            background-size: cover;
+            background-image: url('@/assets/recordplay.png');
+            &.active{
+              background-image: url('@/assets/recordpause.png');
             }
           }
           .audio-pic{

+ 0 - 80
src/views/question/questionMixins.js

@@ -1,80 +0,0 @@
-import{
-    apiQuestionList
-  }from '@/api/question'
-
-export const questionMixin = {
-    data() {
-        return {
-            // 问题列表
-            list:[],
-            page_index:1,
-            page_size:20,
-            //分组列表
-            optionList:[],
-            // 二级分组列表数据
-            secondGroupList:[],
-            // 一级分组Id
-            selectedGroupId_first :0,
-            // 二级分组Id
-            selectedGroupId_second:0,
-            // 正在播放的音频问题索引,没有为-1
-            audioPlayingIndex:-1,
-            // 正在播放的音频问题,没有为-1
-            audioPlayingItem:null,
-            reply_status:3,
-            // 是否加载完成
-            isFinish:false,
-            // 数据是否全部加载完成
-            isTotalData:false,
-            // 是否在加载中
-            isLoading:false,
-        }
-    },
-    methods: {
-        getQuestionList(){
-            console.log(this);
-            this.isLoading = true
-            console.log(this);
-            let params = {
-              group_id:this.selectedGroupId_second,
-              reply_status:this.reply_status,
-              page_index:this.page_index,
-              page_size:this.page_size,
-            }
-            console.log(params);
-            apiQuestionList(params).then(res=>{
-              this.isFinish = true
-              if(res.code == 200){
-                let arr =res.data || []
-                if(params.page_index ==1){
-                  if(arr.length==0) return 
-                  this.list = arr
-                }else{
-                  if(arr.length==0){
-                    this.isTotalData = true
-                    return 
-                  }
-                  this.list = [...this.list,...arr]
-                }
-                for (const item of this.list) {
-                  if(!item.audio_status){
-                    item.audio_status={
-                      isPlay:false,
-                      // 已播放时间
-                      playedTime:0,
-                      // 总共的时长
-                      audioTime:item.audio_list[0].audio_play_seconds*1000,
-                      // 是否在加载中
-                      loading:false
-                    }
-                  } 
-                }
-              }
-              console.log(this.list);
-            })
-            .finally(()=>{
-              this.isLoading = false
-            })
-        }
-    },
-}

+ 43 - 4
src/views/voice/List.vue

@@ -1,7 +1,7 @@
 <script setup>
 import {ref,reactive,onMounted,onActivated} from 'vue'
 import {apiGetWechatQRCode} from '@/api/common'
-import {apiVoiceList,apiVoiceDel,apiVoicePlayRecord,apiVoiceSectionList} from '@/api/voice'
+import {apiVoiceList,apiVoiceDel,apiVoicePlayRecord,apiVoiceSectionList,apiVoiceSendMsg} from '@/api/voice'
 import {apiApplyPermission} from '@/api/user'
 import SelfList from '@/components/SelfList.vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
@@ -171,7 +171,7 @@ const handelGetQRCodeImg=async (item)=>{
     if(item.QRCodeImg) return
     const res=await apiGetWechatQRCode({
         CodeScene:JSON.stringify({voiceId:item.BroadcastId}),
-        CodePage:'pages/voice/voice'
+        CodePage:'pages-voice/voiceDetail'
     })
     if(res.code===200){
         item.QRCodeImg=res.data
@@ -179,7 +179,7 @@ const handelGetQRCodeImg=async (item)=>{
 }
 
 //删除音频
-const handleDel=async (item)=>{
+const handleDel=(item)=>{
     ElMessageBox({
         title:`温馨提醒`,
         message:'确定要删除该语音播报吗?',
@@ -209,6 +209,30 @@ const handleDel=async (item)=>{
     })
 }
 
+//发送模板消息
+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 handlePlay=(item)=>{
     if(store.state.audioData.voiceId==item.BroadcastId){
@@ -300,13 +324,20 @@ onActivated(()=>{
                         <div :class="['icon',($store.state.audioData.voiceId==item.BroadcastId)&&!$store.state.audioData.paused?'active':'']"></div>
                         <div>{{formarVoiceTime(item.VoicePlaySeconds)}}</div>
                     </div>
+                    <img 
+                        class="publish-btn" 
+                        v-if="item.CouldSendMsg"
+                        src="@/assets/voice/publish.png" 
+                        alt=""
+                        @click="handleSendMsg(item)"
+                    />
                     <img 
                         class="del-btn" 
                         v-if="item.IsAuthor" 
                         src="@/assets/voice/del.png" 
                         alt=""
                         @click="handleDel(item)"
-                    >
+                    />
                     <el-popover
                         :width="200"
                         trigger="hover"
@@ -389,6 +420,14 @@ onActivated(()=>{
                 right: 100px;
                 cursor: pointer;
             }
+            .publish-btn{
+                width: 23px;
+                height: 23px;
+                position: absolute;
+                bottom: 30px;
+                right: 170px;
+                cursor: pointer;
+            }
         }
     }