jwyu vor 2 Jahren
Ursprung
Commit
ac194a73aa
8 geänderte Dateien mit 416 neuen und 45 gelöschten Zeilen
  1. 1 0
      README.md
  2. 16 0
      api/voice.js
  3. 50 20
      pages-voice/addVoice.vue
  4. 284 0
      pages-voice/voiceDetail.vue
  5. 6 0
      pages.json
  6. 2 1
      pages/pc.vue
  7. 57 24
      pages/voice/voice.vue
  8. BIN
      static/voice/publish.png

+ 1 - 0
README.md

@@ -7,5 +7,6 @@
   5. 所有接口请用 api 开头,如:'apiUserInfo'
   6. 不可将会动态显示的tabbar项放在pages.json配置为启动页,如需改变tabbar 请调用this.$store.dispatch('getTabBar')
   7. tabbar的默认数据在config中
+  8. 如果是从海报扫码进入的(获取到小程序带来的参数为scene),均要调用apiGetSceneToParams接口获取实际参数
 
 	

+ 16 - 0
api/voice.js

@@ -35,4 +35,20 @@ export const apiVoiceDel=params=>{
  */
 export const apiVoicePlayRecord=params=>{
     return httpPost('/voice/broadcast/statistics/add',{...params,source:1})
+}
+
+/**
+ * 语音详情
+ * @param broadcast_id
+ */
+export const apiVoiceDetail=params=>{
+    return httpGet('/voice/broadcast/detail',params)
+}
+
+/**
+ * 推送客群、模板消息
+ * @param broadcast_id
+ */
+export const apiVoiceSendMsg=params=>{
+    return httpPost('/voice/broadcast/msg_send',params)
 }

+ 50 - 20
pages-voice/addVoice.vue

@@ -109,11 +109,13 @@
                 />
             </view>
         </van-popup>
+
+        <van-dialog id="van-dialog" />
     </view>
 </template>
 
 <script>
-import {apiVoiceSectionList} from '@/api/voice'
+import {apiVoiceSectionList,apiVoiceSendMsg} from '@/api/voice'
 import {baseApiUrl} from '@/utils/config.js'
 import CryptoJS from '@/utils/crypto.js'
 import uniAsync from "@/utils/uni-async.js"; // uni api async 化
@@ -168,8 +170,6 @@ export default {
             options:[],
             mainActiveIndex:0,
             activeId:0,//选择的板块id
-
-            isPublished:false,//是否正在发布,方式快速点击发布多条
         }
     },
     onLoad(){
@@ -393,7 +393,6 @@ export default {
 
         // 发布
         handlePublish(){
-            if(this.isPublished) return
             if(!this.form.title||!this.form.variety_id){
                 uni.showToast({
 					title:'请将内容填写完整',
@@ -401,6 +400,22 @@ export default {
 				})
                 return
             }
+
+            this.$dialog.confirm({
+                title:'',
+                message: '发布后将推送模板消息和客群,确认发布吗?',
+                confirmButtonText:'发布且推送',
+                cancelButtonText:'仅发布'
+            }).then(()=>{
+                //发布且推送
+                this.handleConfirmPublish(false)
+            }).catch(()=>{
+                //仅仅是发布
+                this.handleConfirmPublish(true)
+            })
+        },
+        //确认发布 onlyPublished:true仅发布
+        handleConfirmPublish(onlyPublished){
             let formData={
                 broadcast_name:this.form.title,
                 section_id:Number(this.form.section_id),
@@ -411,10 +426,6 @@ export default {
                 author_id:Number(this.$store.state.user.userInfo.user_id),
                 author:this.$store.state.user.userInfo.real_name
             }
-            this.isPublished=true
-            uni.showLoading({
-				title:'发布中...'
-			})
             uni.uploadFile({
                 url: baseApiUrl + "/voice/broadcast/add",
                 filePath: this.temAudio.url,
@@ -428,14 +439,19 @@ export default {
                     const res =  envVersion === 'release' ? JSON.parse(CryptoJS.Des3Decrypt(result.data)) :  JSON.parse(result.data);
                     console.log(res);
                     if(res.code===200){
-                        uni.showToast({
-                            title:'发布成功',
-                            icon:'success'
-                        })
-                        setTimeout(() => {
-                            uni.$emit('addVoiceSuccess')
-                            uni.navigateBack()
-                        }, 1000);
+                        if(onlyPublished){
+                            uni.showToast({
+                                title:'发布成功',
+                                icon:'success'
+                            })
+                            setTimeout(() => {
+                                uni.$emit('addVoiceSuccess')
+                                uni.navigateBack()
+                            }, 1000);
+                        }else{
+                            //需要推送
+                            this.handleSendMsg(res.data)
+                        }
                     }else{
                         uni.showToast({
                             title:res.msg,
@@ -451,12 +467,25 @@ export default {
                     })
                 },
                 complete: () => {
-                    console.log('con');
-                    uni.hideLoading()
-                    this.isPublished=false
+
                 }
             });
-              
+        },
+
+        //推送消息
+        handleSendMsg(id){
+            apiVoiceSendMsg({broadcast_id:id}).then(res=>{
+                if(res.code===200){
+                    uni.showToast({
+                        title:"发布&推送成功",
+                        icon:'success'
+                    })
+                    setTimeout(() => {
+                        uni.$emit('addVoiceSuccess')
+                        uni.navigateBack()
+                    }, 1000);
+                }
+            })
         }
 
     },
@@ -572,6 +601,7 @@ page{
         margin-bottom: 180rpx;
         padding: 0 30rpx;
         position: relative;
+        border-radius: 16rpx;
         .left-time{
             position: absolute;
             bottom: 20rpx;

+ 284 - 0
pages-voice/voiceDetail.vue

@@ -0,0 +1,284 @@
+<template>
+    <view class="voice-detail" v-if="isAuth">
+        <view class="section-name">{{info.SectionName}}</view>
+        <view class="title">{{info.BroadcastName}}</view>
+        <view class="time">发布时间:{{info.CreateTime|formatTime}}</view>
+        <view class="flex audio-box">
+            <image 
+                :src="paused?'../../../static/voice/pause.png':'../../../static/voice/playing.png'" 
+                mode="aspectFill" 
+                @click="handlePlayAudio" 
+            />
+            <slider
+                activeColor="#E6B77D"
+                :max="duration" 
+                :value="curTime" 
+                @change="handleAudioSliderChange($event)"
+                block-size="12"
+                class="slider"
+            />
+            <text class="left-time">{{curTime|formatVoiceTime}}</text>
+            <text class="right-time">{{duration|formatVoiceTime}}</text>
+        </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"/>
+
+        <van-dialog id="van-dialog" />
+    </view>
+    <noAuth :info="noAuthData" v-else/>
+
+</template>
+
+<script>
+import {apiVoicePlayRecord,apiVoiceDel,apiVoiceDetail,apiVoiceSendMsg} from '@/api/voice'
+import {apiGetSceneToParams} from '@/api/common'
+import noAuth from '@/pages/voice/components/noAuth.vue'
+const moment=require('@/utils/moment-with-locales.min')
+let innerAudioContext = uni.createInnerAudioContext();//播放音频实例
+export default {
+    components:{
+        noAuth
+    },
+    filters:{
+        formatTime(e){
+            return moment(e).format('YYYY-MM-DD HH:mm:ss')
+        },
+        formatVoiceTime(e){
+            let m=parseInt(e/60)
+            let s=parseInt(e%60)
+            return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
+        }
+    },
+    data() {
+        return {
+            voiceId:'',
+            isAuth:true,
+            noAuthData:null,
+            info:{},
+
+            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 {
+            title:title,
+            imageUrl:this.info.ImgUrl
+        }
+    },
+    onHide(){
+        innerAudioContext.pause()
+    },
+    onUnload(){
+        innerAudioContext.destroy()
+        this.paused=true
+	},
+    methods: {
+        async init(options){
+            if(options.scene){
+                const res=await apiGetSceneToParams({scene_key:options.scene})
+                if(res.code===200){
+                    const obj=JSON.parse(res.data)
+                    this.voiceId=obj.voiceId
+                }
+            }else{
+                this.voiceId=options.voiceId||0
+            }
+            this.getDetail()
+        },
+
+        async getDetail(){
+            const res=await apiVoiceDetail({broadcast_id:Number(this.voiceId)})
+            if(res.code===200){
+                this.info=res.data
+                this.duration=Number(res.data.VoicePlaySeconds)||0
+                this.isAuth=true
+            }else if(res.code===403){
+                this.isAuth=false
+                this.noAuthData=res.data
+            }
+        },
+
+        //删除
+        handleDel(){
+            this.$dialog.confirm({
+                title:'',
+                message: '确定要删除该语音播报吗?',
+                confirmButtonText:'确定'
+            }).then(()=>{
+                apiVoiceDel({broadcast_id:Number(this.voiceId)}).then(res=>{
+                    if(res.code===200){
+                        uni.showToast({
+                            title:'操作成功',
+                            icon:'none'
+                        })
+                        setTimeout(() => {
+                            uni.$emit('addVoiceSuccess')
+                            uni.switchTab({
+                                url: '/pages/voice/voice'
+                            });
+                        }, 1000);
+                    }
+                })
+            }).catch(()=>{})
+        },
+
+        // 推送消息
+        handleSendMsg(){
+            this.$dialog.confirm({
+                title:'',
+                message: '该操作将推送模板消息和客群,确认推送吗?',
+                confirmButtonText:'确认'
+            }).then(()=>{
+                apiVoiceSendMsg({broadcast_id:Number(this.voiceId)}).then(res=>{
+                    if(res.code===200){
+                        uni.showToast({
+                            title:'操作成功',
+                            icon:'none'
+                        })
+                        setTimeout(() => {
+                            uni.$emit('addVoiceSuccess')
+                            uni.switchTab({
+                                url: '/pages/voice/voice'
+                            });
+                        }, 1000);
+                    }
+                })
+            }).catch(()=>{})
+        },
+
+        //上报音频播放记录
+        async handleVoicePlayRecord(){
+            const res=await apiVoicePlayRecord({
+                broadcast_id:Number(this.voiceId)
+            })
+            if(res.code===200){
+                console.log('上报音频播放记录');
+            }
+        },
+
+        //点击播放\暂停音频
+        handlePlayAudio(){
+            if(this.isStartPlay){
+                this.handleVoicePlayRecord()
+            }
+            innerAudioContext.src=this.info.VoiceUrl
+            this.isStartPlay=false
+            innerAudioContext.obeyMuteSwitch=false
+            if(this.paused){
+                innerAudioContext.play()
+            }else{
+                innerAudioContext.pause()
+            }
+        },
+
+        //音频播放事件
+        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){
+            const value=e.detail.value
+            innerAudioContext.seek(value)
+        },
+
+
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+    .voice-detail{
+        padding: 50rpx 34rpx;
+        .section-name{
+            background: #FDF8F2;
+            border-radius: 8rpx;
+            border: 1px solid #E3B377;
+            display: inline-block;
+            padding: 19rpx 27rpx;
+            margin-bottom: 40rpx;
+        }
+        .title{
+            font-size: 32rpx;
+            line-height: 38rpx;
+            margin-bottom: 20rpx;
+        }
+        .time{
+            color: #999;
+            font-size: 28rpx;
+            line-height: 33rpx;
+        }
+        .audio-box{
+            background-color: #FDF8F2;
+            height: 123rpx;
+            align-items: center;
+            margin-top: 50rpx;
+            margin-bottom: 40rpx;
+            padding: 0 30rpx;
+            position: relative;
+            .left-time{
+                position: absolute;
+                bottom: 20rpx;
+                left: 100rpx;
+                color: #999999;
+                font-size: 20rpx;
+            }
+            .right-time{
+                position: absolute;
+                bottom: 20rpx;
+                right: 40rpx;
+                color: #999999;
+                font-size: 20rpx;
+            }
+            image{
+                width: 40rpx;
+                height: 48rpx;
+                flex-shrink: 0;
+                margin-right: 30rpx;
+            }
+            .slider{
+                flex: 1;
+                margin: 0 10rpx;
+            }
+        }
+        .del-btn{
+            float: left;
+            width: 36rpx;
+            height: 36rpx;
+        }
+        .publish-btn{
+            float: right;
+            width: 36rpx;
+            height: 36rpx;
+        }
+    }
+</style>

+ 6 - 0
pages.json

@@ -288,6 +288,12 @@
 					"style":{
 						"navigationBarTitleText": "新建语音"
 					}
+				},
+				{
+					"path": "voiceDetail",
+					"style":{
+						"navigationBarTitleText": "播报详情"
+					}
 				}
 			]
 		}

+ 2 - 1
pages/pc.vue

@@ -19,7 +19,8 @@ const mapObj=new Map([
     ['pages-report/specialColumn/detail','/report/specialcolumndetail'],
     ['pages/video/videoList','/video/list'],
 	['pages-sandTable/sandTable','/sandBox/list'],
-    ['pages/voice/voice','/voice/list']
+    ['pages/voice/voice','/voice/list'],
+    ['pages-voice/voiceDetail','/voice/list'],
 ])//map映射小程序页面路径对应h5页面路径
 import {apiUserInfo} from '@/api/user'
 import {apiGetSceneToParams} from '@/api/common'

+ 57 - 24
pages/voice/voice.vue

@@ -13,10 +13,10 @@
             <view>暂无数据</view>
         </view>
         <view class="list-wrap" :style="{paddingBottom:IsVoiceAdmin&&'200rpx'}" v-else>
-            <view class="item" v-for="item in list" :key="item.BroadcastId">
+            <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="handlePlay(item)">
+                <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')" 
                         mode="widthFix" 
@@ -24,13 +24,17 @@
                     <text v-if="item.BroadcastId==temAudio.id">{{temAudio.curTime|formatVoiceTime}}</text>
                     <text v-else>{{item.VoicePlaySeconds|formatVoiceTime}}</text>
                 </view>
-                <button class="del-btn" @click="handleDelItem(item)">
-                <image class="del-img" src="@/static/voice/del.png" @click="handleDelItem(item)" mode="widthFix" v-if="item.IsAuthor" />
+                <button class="publish-btn" @click.stop="handleSendMsgItem(item)" v-if="item.CouldSendMsg">
+                    <image class="publish-img" src="@/static/voice/publish.png" mode="widthFix" />
+                </button>
+                <button class="del-btn" @click.stop="handleDelItem(item)">
+                    <image class="del-img" src="@/static/voice/del.png" mode="widthFix" v-if="item.IsAuthor" />
                 </button>
                 <button 
                     class="share-btn" 
                     open-type="share" 
                     :data-item="item"
+                    @click.stop=""
                 >
                     <image class="share-img" src="@/static/share-icon.png" mode="aspectFill"/>
                 </button>
@@ -72,7 +76,7 @@
 </template>
 
 <script>
-import {apiVoiceList,apiVoiceSectionList,apiVoicePlayRecord,apiVoiceDel} from '@/api/voice'
+import {apiVoiceList,apiVoiceSectionList,apiVoicePlayRecord,apiVoiceDel,apiVoiceSendMsg} from '@/api/voice'
 import {apiGetSceneToParams} from '@/api/common'
 import noAuth from './components/noAuth.vue'
 const moment=require('@/utils/moment-with-locales.min')
@@ -119,8 +123,8 @@ export default {
             }
         }
     },
-    onLoad(options){
-        this.init(options)
+    onLoad(){
+        this.getVoiceList()
         this.getOptionsList()
         this.addListenVoiceSuccess()
     },
@@ -147,12 +151,12 @@ export default {
 	},
     onShareAppMessage({from,target}) {
         console.log(from,target);
-        let path='/pages/voice/voice?voiceId=0'
+        let path='/pages/voice/voice'
         let title='语音播报'
         let imageUrl=''
         if(from=='button'){
             title=`${target.dataset.item.SectionName}:${target.dataset.item.BroadcastName}`
-            path=`/pages/voice/voice?voiceId=${target.dataset.item.BroadcastId}`
+            path=`/pages-voice/voiceDetail?voiceId=${target.dataset.item.BroadcastId}`
             imageUrl=target.dataset.item.ImgUrl
         }
         return {
@@ -179,6 +183,12 @@ export default {
     },
 
     methods: {
+        handleGoDetail(item){
+            uni.navigateTo({
+                url: '/pages-voice/voiceDetail?voiceId='+item.BroadcastId,
+            });
+        },
+
         // 监听添加音频成功刷新列表
         addListenVoiceSuccess(){
             uni.$on('addVoiceSuccess',()=>{
@@ -191,19 +201,6 @@ export default {
             })
         },
 
-        async init(options){
-            if(options.scene){
-                const res=await apiGetSceneToParams({scene_key:options.scene})
-                if(res.code===200){
-                    const obj=JSON.parse(res.data)
-                    this.voiceId=obj.voiceId
-                }
-            }else{
-                this.voiceId=options.voiceId||0
-            }
-            this.getVoiceList()
-        },
-
         // 获取音频列表
         async getVoiceList(){
             const res=await apiVoiceList({
@@ -282,6 +279,25 @@ export default {
             this.getVoiceList()
             this.showFilter=false
         },
+
+        //推送消息
+        handleSendMsgItem(item){
+            this.$dialog.confirm({
+                title:'',
+                message: '该操作将推送模板消息和客群,确认推送吗?',
+                confirmButtonText:'确认'
+            }).then(()=>{
+                apiVoiceSendMsg({broadcast_id:item.BroadcastId}).then(res=>{
+                    if(res.code===200){
+                        uni.showToast({
+                            title:"推送成功",
+                            icon:'success'
+                        })
+                        item.CouldSendMsg=false
+                    }
+                })
+            }).catch(()=>{})
+        },
         
         //删除音频
         handleDelItem(item){
@@ -444,9 +460,26 @@ export default {
         border-bottom: 1px solid #CDCDCD;
         padding: 30rpx 0;
         position: relative;
+        .publish-btn{
+            position: absolute;
+            right: 192rpx;
+            bottom: 30rpx;
+            width: 36rpx;
+            height: 36rpx;
+            background-color: transparent;
+            line-height: 1;
+            padding: 0;
+            &::after{
+                border: none;
+            }
+        }
+        .publish-img{
+            width: 34rpx;
+            height: 34rpx;
+        }
         .del-btn{
             position: absolute;
-            right: 130rpx;
+            right: 96rpx;
             bottom: 30rpx;
             width: 36rpx;
             height: 36rpx;
@@ -465,7 +498,7 @@ export default {
         .share-btn{
             position: absolute;
             bottom: 30rpx;
-            right: 34rpx;
+            right: 0rpx;
             background-color: transparent;
             width: 36rpx;
             height: 36rpx;

BIN
static/voice/publish.png