浏览代码

上线yb9.0,解决冲突

jwyu 2 年之前
父节点
当前提交
363fb7af05

+ 1 - 1
api/user.js

@@ -48,7 +48,7 @@ export const apiUserLogin=params=>{
  * @param company_name 公司名
  * @param company_name 公司名
  * @param permission 选择的权限
  * @param permission 选择的权限
  * @param real_name 姓名
  * @param real_name 姓名
- * @param source 来源:我的1、活动2、图库3、研报4、沙盘推演7
+ * @param source 来源:我的1、活动2、图库3、研报4、问答社区5、价格驱动6、沙盘推演7、语音播报8
  * @param source_agent 来源平台:1:小程序、2:小程序(pc)、3:公众号、4:官网web(pc)
  * @param source_agent 来源平台:1:小程序、2:小程序(pc)、3:公众号、4:官网web(pc)
  * @param from_page 来源页面: '活动列表'、'活动详情'等
  * @param from_page 来源页面: '活动列表'、'活动详情'等
  */
  */

+ 38 - 0
api/voice.js

@@ -0,0 +1,38 @@
+// 语音播报模块
+
+import { httpGet, httpPost } from "@/utils/request.js";
+
+/**
+ * 语音列表
+ * @param page_index 
+ * @param page_size
+ * @param broadcast_id 语音id
+ * @param section_id 板块id
+ */
+export const apiVoiceList=params=>{
+    return httpPost('/voice/broadcast/list',params)
+}
+
+/**
+ * 语音板块列表
+ */
+export const apiVoiceSectionList=()=>{
+    return httpGet('/voice/broadcast/section/list',{})
+}
+
+/**
+ * 删除语音
+ * @param broadcast_id
+ */
+export const apiVoiceDel=params=>{
+    return httpGet('/voice/broadcast/delete',params)
+}
+
+/**
+ * 播放记录
+ * @param broadcast_id
+ * @param source 来源平台:1:小程序、2:小程序(pc)、3:公众号、4:官网web(pc)
+ */
+export const apiVoicePlayRecord=params=>{
+    return httpPost('/voice/broadcast/statistics/add',{...params,source:1})
+}

+ 1 - 1
mixin/index.js

@@ -5,7 +5,7 @@ moment.locale('zh-cn');
 import {globalImgUrls} from "../utils/config"
 import {globalImgUrls} from "../utils/config"
 import store from '@/store'
 import store from '@/store'
 
 
-const tabbarPathList=['pages/activity/activity','pages/pricedriven/pricedriven','pages/video/videoList','pages/report/report','pages/question/question']
+const tabbarPathList=['pages/voice/voice','pages/pricedriven/pricedriven','pages/video/videoList','pages/report/report','pages/question/question']
 
 
 module.exports = {
 module.exports = {
   watch: {
   watch: {

+ 663 - 0
pages-voice/addVoice.vue

@@ -0,0 +1,663 @@
+<template>
+    <view class="add-voice-page">
+        <van-field
+            :value="form.title"
+            placeholder="请输入语音标题"
+            :border="false"
+            clearable
+            @change="inputChange"
+            label="语音标题"
+        />
+        <van-field
+            :value="form.variety_name"
+            placeholder="请选择品种"
+            :border="false"
+            readonly
+            is-link
+            label="品种"
+            @click-input="showFilter=true"
+            @click-icon="showFilter=true"
+        />
+        <van-field
+            :value="form.section_name"
+            placeholder="请选择板块名称"
+            :border="false"
+            readonly
+            is-link
+            label="板块名称"
+            @click-input="showFilter=true"
+            @click-icon="showFilter=true"
+        />
+
+        <view class="flex audio-box" v-if="recorderStatus==='stop'">
+            <image 
+                :src="temAudio.paused?'../../../static/voice/pause.png':'../../../static/voice/playing.png'" 
+                mode="aspectFill" 
+                @click="handlePlayAudio" 
+            />
+            <slider
+                activeColor="#E6B77D"
+                :max="temAudio.duration" 
+                :value="temAudio.curTime" 
+                @change="handleAudioSliderChange($event)"
+                block-size="12"
+                class="slider"
+            />
+            <text class="left-time">{{temAudio.curTime|formatTime}}</text>
+            <text class="right-time">{{temAudio.duration|formatTime}}</text>
+            <view class="del-btn" @click="handleDelRecord">
+                <image src="./static/del.png" mode="aspectFill" />
+                <text>删除</text>
+            </view>
+              
+        </view>
+          
+
+        <view class="empty-voice-box" v-if="recorderStatus==='start'">
+            <image src="./static/record.png" mode="aspectFill" />
+            <view>无录音(录音时长超过十分钟自动结束)</view>
+        </view>
+
+        <view class="animat-box" v-if="recorderStatus==='doing'||recorderStatus==='pause'">
+            <view class="con-box">
+                <image :class="['img move1',recorderStatus==='doing'?'animat-run':'animat-pause']" src="./static/record-img.png" mode="widthFix" />
+                <image :class="['img move2',recorderStatus==='doing'?'animat-run':'animat-pause']" src="./static/record-img.png" mode="widthFix" />
+                <image :class="['img move3',recorderStatus==='doing'?'animat-run':'animat-pause']" src="./static/record-img.png" mode="widthFix" />
+            </view>
+            <view class="bot-text">{{time|formatTime}}</view>
+        </view>
+
+        <view class="sound-record-wrap" v-if="recorderStatus!=='stop'">
+            <view class="top-text">点击开始录音</view>
+            <image class="btn" :src="btnImg" mode="aspectFill" @click="handleClickBtn" />
+            <view 
+                class="del-btn" 
+                :style="{color:recorderStatus==='doing'&&'#999'}" 
+                v-if="recorderStatus!=='start'"
+                @click="handleResetRecorder"
+            >删除</view>
+            <view 
+                class="done-btn" 
+                v-if="recorderStatus!=='start'"
+                @click="handleEndRecorder"
+            >完成</view>
+        </view>
+
+        <view class="publish-btn" v-if="recorderStatus==='stop'" @click="handlePublish">发布</view>
+        
+        <!-- 筛选弹窗 -->
+        <van-popup 
+            :show="showFilter" 
+            position="bottom"  
+            :close-on-click-overlay="true"
+            @close="showFilter = false"
+            round
+        >
+            <view class="fliter-wrap-list">
+                <view class="flex top">
+                    <text style="color:#000">全部选项</text>
+                    <text style="color:#E3B377" @click="showFilter=false">取消</text>
+                </view>
+                <van-tree-select
+                    :items="options"
+                    :main-active-index="mainActiveIndex"
+                    :active-id="activeId"
+                    @click-nav="onClickNav"
+                    @click-item="onClickItem"
+                    main-active-class="main-active-class"
+                    content-active-class="content-active-class"
+                />
+            </view>
+        </van-popup>
+    </view>
+</template>
+
+<script>
+import {apiVoiceSectionList} 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 化
+const recorderManager = wx.getRecorderManager();//录音实例
+let innerAudioContext = uni.createInnerAudioContext();//播放音频实例
+let TIMER=null//计时器
+export default {
+    filters:{
+        formatTime(e){
+            let m=parseInt(e/60)
+            let s=parseInt(e%60)
+            return `${m>9?m:'0'+m}:${s>9?s:'0'+s}`
+        }
+    },
+    computed:{
+        btnImg(){
+            if(this.recorderStatus==='start'){
+                return './static/voice-start.png'
+            }else if(this.recorderStatus==='doing'){
+                return './static/voice-doing.png'
+            }else if(this.recorderStatus==='stop'){
+                return './static/voice-pause.png'
+            }else if(this.recorderStatus==='pause'){
+                return './static/voice-pause.png'
+            }
+        }
+    },
+    data() {
+        return {
+            form:{
+                title:'',//语音标题
+                variety_name:'',
+                variety_id:'',
+                section_id:'',
+                section_name:'',
+                img_url:''
+            },
+
+            recorderStatus:'start',//当前录音状态 start开始 doing正在录音 stop停止录音 pause录音暂停
+            time:0,
+            isReset:false,//是否点击了重置
+
+            temAudio:{
+                url:'',//临时音频地址
+                duration:'',//时长
+                size:'',//大小
+                curTime:0,//播放时当前播放的时间
+                paused:true,
+            },//临时音频文件信息
+
+            showFilter:false,
+            options:[],
+            mainActiveIndex:0,
+            activeId:0,//选择的板块id
+
+        }
+    },
+    onLoad(){
+        // 调取用户授权使用麦克风
+        uni.authorize({
+            scope: 'scope.record',
+            success() {}
+        })
+        this.listenVoice()
+        
+        this.getOptionsList()
+    },
+    onShow(){
+        innerAudioContext = uni.createInnerAudioContext()
+        this.listenAudio()
+    },
+    onHide(){
+        innerAudioContext.destroy()
+        this.temAudio.paused=true
+    },
+    onUnload(){
+        innerAudioContext.destroy()
+	},
+    methods: {
+        //录音事件
+        listenVoice(){
+            recorderManager.onStart(()=>{
+                //录音开始监听事件
+                console.log('开始录音');
+                this.recorderStatus='doing'
+                this.isReset=false
+                if(!TIMER){
+                    TIMER=setInterval(() => {
+                        this.time++
+                    }, 1000);
+                }
+                
+            })
+            recorderManager.onPause(()=>{
+                //录音暂停监听事件
+                console.log('录音暂停');
+                this.recorderStatus='pause'
+                clearInterval(TIMER)
+                TIMER=null
+            })
+            recorderManager.onResume(()=>{
+                //录音继续监听事件
+                console.log('录音继续');
+                this.recorderStatus='doing'
+
+                if(!TIMER){
+                    TIMER=setInterval(() => {
+                        this.time++
+                    }, 1000);
+                }
+            })
+            recorderManager.onStop((e)=>{
+                //录音结束监听事件
+                console.log('录音结束',e);
+
+                // 如果是点击重置(删除按钮)的 则不做结束处理
+                if(!this.isReset){
+                    this.recorderStatus='stop'
+                    this.temAudio.url=e.tempFilePath
+                    this.temAudio.size=e.fileSize
+                    this.temAudio.duration=parseInt(e.duration/1000)
+                }
+                
+                clearInterval(TIMER)
+                TIMER=null
+            })
+            recorderManager.onError((e)=>{
+                //录音事件错误监听
+                console.log('录音错误哦',e);
+            })
+        },
+
+        //点击录音操作按钮
+        async handleClickBtn(){
+            const setRes=await uniAsync.getSetting()
+            console.log(setRes.authSetting['scope.record']);
+            if(!setRes.authSetting['scope.record']){
+                uni.showToast({
+                    title:'请打开麦克风交流',
+                    icon:'none'
+                })
+                uni.openSetting()
+                return
+            }
+             
+            if(this.recorderStatus==='start'){
+                recorderManager.start({
+                    duration:600000,
+                    format:'mp3'
+                })
+            }
+            if(this.recorderStatus==='doing'){
+                recorderManager.pause()//暂停录音
+            }
+            if(this.recorderStatus==='pause'){
+                recorderManager.resume()//继续录音
+            }
+        },
+
+        //点击重置录音状态
+        handleResetRecorder(){
+            if(this.recorderStatus==='doing') return
+            this.isReset=true
+            recorderManager.stop()
+            this.recorderStatus='start'
+            this.handleDelRecord()
+        },
+
+        //点击完成录音
+        handleEndRecorder(){
+            //点击完成时还不是已结束录音状态
+            this.isReset=false
+            recorderManager.stop()
+        },
+
+        //拖动音频播放进度条
+        handleAudioSliderChange(e){
+            const value=e.detail.value
+            innerAudioContext.seek(value)
+        },
+
+        //点击播放\暂停音频
+        handlePlayAudio(){
+            innerAudioContext.src=this.temAudio.url
+            if(this.temAudio.paused){
+                innerAudioContext.play()
+            }else{
+                innerAudioContext.pause()
+            }
+        },
+
+        //音频播放事件
+        listenAudio(){
+            innerAudioContext.onPlay(()=>{
+                console.log('开始播放录音');
+                this.temAudio.paused=false
+            })
+            innerAudioContext.onPause(()=>{
+                console.log('录音播放暂停');
+                this.temAudio.paused=true
+            })
+            // innerAudioContext.onStop(()=>{
+            //     console.log('录音播放停止');
+            //     this.temAudio.paused=true
+            //     innerAudioContext.src=''
+            // })
+            innerAudioContext.onEnded(()=>{
+                console.log('录音播放自然结束');
+                this.temAudio.curTime=this.temAudio.duration
+                setTimeout(() => {
+                    this.temAudio.paused=true
+                    innerAudioContext.src=''
+                    this.temAudio.curTime=0
+                }, 300);
+            })
+            innerAudioContext.onTimeUpdate(()=>{
+                this.temAudio.curTime=parseInt(innerAudioContext.currentTime)
+            })
+        },
+
+        //删除录音记录
+        handleDelRecord(){
+            this.recorderStatus='start'
+            this.isReset=true
+            this.temAudio.url=''
+            this.temAudio.duration=''
+            this.temAudio.curTime=0
+            this.temAudio.paused=true
+            this.time=0
+            innerAudioContext.stop()
+            TIMER=null
+        },
+
+        //获取选项数据
+        async getOptionsList(){
+            const res=await apiVoiceSectionList()
+            if(!res.code===200) return
+            const arr=res.data||[]
+        
+            this.options=arr.filter(item=>{
+                item.text=item.VarietyName
+                item.children=item.Children.filter(_item=>{
+                    if(_item.Status===1){
+                        _item.text=_item.SectionName
+                        _item.id=_item.SectionId
+                        return _item
+                    }
+                })
+                if(item.children.length>0){
+                    delete item.Children
+                    return item
+                }
+            })
+        },
+
+        onClickNav({detail}){
+            console.log(detail);
+            this.mainActiveIndex=detail.index
+        },
+
+        onClickItem({detail}){
+            console.log(detail);
+            this.activeId=detail.id
+            this.form.section_id=detail.SectionId
+            this.form.section_name=detail.SectionName
+            this.form.variety_name=this.options[this.mainActiveIndex].VarietyName
+            this.form.variety_id=this.options[this.mainActiveIndex].VarietyId
+            this.form.img_url=detail.ImgUrl
+            this.showFilter=false
+        },
+
+        inputChange(event) {
+            this.form.title=event.detail
+        },
+
+        // 发布
+        handlePublish(){
+            if(!this.form.title||!this.form.variety_id){
+                uni.showToast({
+					title:'请将内容填写完整',
+					icon:'none'
+				})
+                return
+            }
+            let formData={
+                broadcast_name:this.form.title,
+                section_id:Number(this.form.section_id),
+                section_name:this.form.section_name,
+                variety_id:Number(this.form.variety_id),
+                variety_name:this.form.variety_name,
+                img_url:this.form.img_url,
+                author_id:Number(this.$store.state.user.userInfo.user_id),
+                author:this.$store.state.user.userInfo.real_name
+            }
+            uni.uploadFile({
+                url: baseApiUrl + "/voice/broadcast/add",
+                filePath: this.temAudio.url,
+                name: 'file',
+                header: {
+                    Authorization: this.$store.state.user.token,
+                },
+                formData: formData,
+                success: (result) => {
+                    const { envVersion } = uni.getAccountInfoSync().miniProgram
+                    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);
+                    }else{
+                        uni.showToast({
+                            title:res.msg,
+                            icon:'none'
+                        })
+                    }
+                },
+                fail: () => {
+                    console.log('发布失败');
+                    uni.showToast({
+                        title:'发布失败,请稍后重试!',
+                        icon:'none'
+                    })
+                },
+                complete: () => {
+                    console.log('con');
+                }
+            });
+              
+        }
+
+    },
+}
+</script>
+
+<style lang="scss">
+.add-voice-page .van-cell{
+    border-bottom: 1px solid #e5e5e5;
+}
+.add-voice-page .van-field__label{
+    font-size: 32rpx;
+    color: #333;
+}
+/* .add-voice-page .van-cell__title{
+    max-width: 6.2em;
+    min-width: 6.2em;
+    margin-right: 12px;
+    font-size: 32rpx;
+    color: #333;
+} */
+.add-voice-page .van-cell__value{
+    text-align: left;
+    font-size: 32rpx;
+}
+
+.add-voice-page{
+    .fliter-wrap-list{
+        background-color: #fff;
+        padding-top: 53rpx;
+        .top{
+            font-size: 32rpx;
+            justify-content: space-between;
+            margin-bottom: 40rpx;
+            padding: 0 34rpx;
+        }
+        .van-sidebar{
+            flex-shrink: 0;
+        }
+        .van-tree-select__content{
+            overflow-x: hidden;
+        }
+        .main-active-class{
+            border-color: #E3B377;
+        }
+        .content-active-class{
+            color: #E3B377;
+        }
+    }
+}
+
+page{
+    padding-bottom:constant(safe-area-inset-bottom);
+    padding-bottom:env(safe-area-inset-bottom);
+}
+</style>
+
+<style lang="scss" scoped>
+.add-voice-page{
+    .empty-voice-box{
+        height: 50vh;
+        padding-top: 150rpx;
+        image{
+            width: 140rpx;
+            height: 140rpx;
+            margin-bottom: 20rpx;
+        }
+        text-align: center;
+        color: #999999;
+    }
+    .sound-record-wrap{
+        border-top: 1px solid #E6E6E6;
+        padding-top: 126rpx;
+        position: relative;
+        .top-text{
+            text-align: center;
+            color: #EE3636;
+            position: absolute;
+            top: 50rpx;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+        .btn{
+            width: 118rpx;
+            height: 118rpx;
+            display: block;
+            margin-left: auto;
+            margin-right: auto;
+        }
+        .del-btn{
+            position: absolute;
+            left: 122rpx;
+            bottom: 40rpx;
+            font-size: 32rpx;
+            color: #EE3636;
+        }
+        .done-btn{
+            position: absolute;
+            right: 122rpx;
+            bottom: 40rpx;
+            font-size: 32rpx;
+            color: #EE3636;
+        }
+    }
+
+    .audio-box{
+        background-color: #FDF8F2;
+        height: 123rpx;
+        align-items: center;
+        margin-left: 34rpx;
+        margin-right: 34rpx;
+        margin-top: 60rpx;
+        margin-bottom: 180rpx;
+        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{
+            display: flex;
+            align-items: center;
+            color: #999999;
+            font-size: 28rpx;
+            position: absolute;
+            bottom: -50rpx;
+            right: 0;
+            image{
+                width: 32rpx;
+                height: 35rpx;
+                margin-right: 10rpx;
+            }
+        }
+    }
+
+    .publish-btn{
+        width: 390rpx;
+        height: 80rpx;
+        text-align: center;
+        line-height: 80rpx;
+        color: #fff;
+        background: #E6B77D;
+        font-size: 32rpx;
+        border-radius: 40px;
+        margin-left: auto;
+        margin-right: auto;
+    }
+}
+
+.animat-box{
+    height: 50vh;
+    .con-box{
+        height: 80%;
+        background-color: #FAFAFA;
+        position: relative;
+    }
+    .bot-text{
+        font-size: 60rpx;
+        padding-top: 36rpx;
+        text-align: center;
+    }
+    .img{
+        position: absolute;
+        width: 100vw;
+        top: 27%;
+        transform: translateX(100vw);
+    }
+    .move1{
+        animation: move 30s linear infinite;
+    }
+    .move2{
+        animation: move 30s 10s linear infinite;
+    }
+    .move3{
+        animation: move 30s 20s linear infinite;
+    }
+    .animat-pause{
+        animation-play-state: paused;
+    }
+    .animat-run{
+        animation-play-state: running;
+    }
+
+    @keyframes move {
+        0%{
+            transform: translateX(100vw);
+        }
+        100%{
+            transform: translateX(-200vw);
+        }
+    }
+}
+</style>

二进制
pages-voice/static/del.png


二进制
pages-voice/static/record-img.png


二进制
pages-voice/static/record.png


二进制
pages-voice/static/voice-doing.png


二进制
pages-voice/static/voice-pause.png


二进制
pages-voice/static/voice-start.png


+ 28 - 7
pages.json

@@ -72,6 +72,13 @@
 			"style": {
 			"style": {
 				"navigationBarTitleText": "视频社区"
 				"navigationBarTitleText": "视频社区"
 			}
 			}
+		},
+		{
+			"path":"pages/voice/voice",
+			"style": {
+				"navigationBarTitleText": "语音播报",
+				"enablePullDownRefresh": true
+			}
 		}
 		}
 	],
 	],
 	"subPackages":[
 	"subPackages":[
@@ -271,6 +278,18 @@
 					}
 					}
 				}
 				}
 			]
 			]
+		},
+		//语音播放模块
+		{
+			"root":"pages-voice",
+			"pages": [
+				{
+					"path": "addVoice",
+					"style":{
+						"navigationBarTitleText": "新建语音"
+					}
+				}
+			]
 		}
 		}
 	],
 	],
 		
 		
@@ -297,17 +316,17 @@
 				"iconPath": "./static/tabbar/video.png",
 				"iconPath": "./static/tabbar/video.png",
 				"selectedIconPath": "./static/tabbar/video-s.png"
 				"selectedIconPath": "./static/tabbar/video-s.png"
 			},
 			},
-			{
-				"pagePath": "pages/activity/activity",
-				"text": "活动",
-				"iconPath": "./static/tabbar/activity.png",
-				"selectedIconPath": "./static/tabbar/activity-s.png"
-			},
 			{
 			{
 				"pagePath": "pages/question/question",
 				"pagePath": "pages/question/question",
 				"text": "问答",
 				"text": "问答",
 				"iconPath": "./static/tabbar/question.png",
 				"iconPath": "./static/tabbar/question.png",
 				"selectedIconPath": "./static/tabbar/question-s.png"
 				"selectedIconPath": "./static/tabbar/question-s.png"
+			},
+			{
+				"pagePath": "pages/voice/voice",
+				"text": "语音播报",
+				"iconPath": "./static/tabbar/voice.png",
+				"selectedIconPath": "./static/tabbar/voice-s.png"
 			}
 			}
 		]
 		]
 	},
 	},
@@ -340,7 +359,9 @@
 			"van-row": "/wxcomponents/vant/row/index",
 			"van-row": "/wxcomponents/vant/row/index",
   			"van-col": "/wxcomponents/vant/col/index",
   			"van-col": "/wxcomponents/vant/col/index",
 			"van-progress": "/wxcomponents/vant/progress/index",
 			"van-progress": "/wxcomponents/vant/progress/index",
-			"van-dialog": "/wxcomponents/vant/dialog/index"
+			"van-dialog": "/wxcomponents/vant/dialog/index",
+			"van-cell": "/wxcomponents/vant/cell/index",
+			"van-tree-select": "/wxcomponents/vant/tree-select/index"
 		}
 		}
 	}
 	}
 }
 }

+ 3 - 2
pages/pc.vue

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

+ 4 - 3
pages/report/report.vue

@@ -122,6 +122,7 @@ export default {
 			['chart','/pages/chart/chart'],
 			['chart','/pages/chart/chart'],
 			['buy','/pages/buy/buy'],
 			['buy','/pages/buy/buy'],
 			['sandbox','/pages-sandTable/sandTable'],
 			['sandbox','/pages-sandTable/sandTable'],
+      ['activity','/pages/activity/activity']
 		]),
 		]),
 		tabCards: []
 		tabCards: []
     }
     }
@@ -545,10 +546,10 @@ movable-area{
 	display: flex;
 	display: flex;
 	padding:  40rpx 34rpx 20rpx;
 	padding:  40rpx 34rpx 20rpx;
 	align-items: center;
 	align-items: center;
-	justify-content: space-around;
+	justify-content: space-between;
 	.card-item {
 	.card-item {
-		margin-right: 40rpx;
-		&:last-child { margin-right: 0; }
+		// margin-right: 40rpx;
+		// &:last-child { margin-right: 0; }
 		.card-ico {
 		.card-ico {
 			width: 100rpx;
 			width: 100rpx;
 			height: 100rpx;
 			height: 100rpx;

+ 98 - 0
pages/voice/components/noAuth.vue

@@ -0,0 +1,98 @@
+<template>
+  <view class="voice-no-auth">
+        <image class="img" :src="globalImgUrls.activityNoAuth" mode="widthFix"></image>
+		<view style="margin-bottom:15px">您暂无权限查看语音播报</view>
+		<view v-if="info.type==='contact'" style="margin-bottom:15px">若想查看可以联系对口销售</view>
+		<view v-else style="margin-bottom:15px">若想查看可以申请开通</view>
+		<view v-if="info.type==='contact'">
+			{{info.name||''}}:<text @click="handleCall" style="color:#E3B377">{{info.mobile||''}}</text>
+		</view>
+		<view class="global-btn-yellow-change btn" @click="handleApply" v-else style="margin-top:30px">立即申请</view>
+  </view>
+</template>
+
+<script>
+import {apiApplyPermission} from '@/api/user'
+export default {    
+    props: {
+        info:null
+    },
+    watch:{
+        info(){
+            this.handleAutoApply()
+        }
+    },
+    
+    methods: {
+        handleCall(){
+
+            uni.makePhoneCall({
+                phoneNumber: this.info.mobile,
+                success: (result) => {},
+                fail: (error) => {}
+            })
+        },
+
+        handleAutoApply(){
+            if(this.info.type=='contact'&&!this.info.customer_info.has_apply){
+                if(this.info.customer_info.status=='冻结'||(this.info.customer_info.status=='试用'&&this.info.customer_info.is_suspend==1)){
+                    apiApplyPermission({
+                        company_name:this.info.customer_info.company_name,
+                        real_name:this.info.customer_info.name,
+                        source:8,
+                        from_page:'语音播报'
+                    }).then(res=>{
+                        if(res.code===200){
+                            console.log('主动申请成功');
+                        }
+                    }) 
+                }
+            }
+        },
+
+        handleApply(){
+            const {customer_info}=this.info
+            if(customer_info.has_apply){
+                uni.showToast({
+                  title:'您已提交过申请,请耐心等待',
+                  icon:'none'
+                })
+            }else{
+                if (!customer_info.status || customer_info.status != '流失') {
+                    uni.navigateTo({
+                        url: "/pages-applyPermission/applyPermission?source=8&from_page=语音播报"
+                    })
+                }else{
+                    apiApplyPermission({
+                        company_name:customer_info.company_name,
+                        real_name:customer_info.name,
+                        source:8,
+                        from_page:'语音播报'
+                    }).then(res=>{
+                        uni.navigateTo({url:'/pages-applyPermission/applyResult'})
+                    })
+                }
+            }
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.voice-no-auth{
+    padding: 34rpx;
+    text-align: center;
+    font-size: $global-font-size-lg;
+    .img{
+        width: 100%;
+        margin-bottom: 50rpx;
+    }
+    .btn{
+        width: 380rpx;
+        line-height: 70rpx;
+        margin-left: auto;
+        margin-right: auto;
+        margin-top: 40rpx;
+    }
+}
+</style>

+ 510 - 0
pages/voice/voice.vue

@@ -0,0 +1,510 @@
+<template>
+    <view class="voice-play-page" v-if="isAuth">
+        <view class="top-filter-box">
+            <image src="@/static/question/select.png" mode="aspectFill" @click="showFilter = true" />
+            <text @click="showFilter = true">筛选</text>
+        </view>
+          
+        <view class="empty-box" v-if="list.length==0&&finished">
+            <image
+                :src="globalImgUrls.activityNoAuth"
+                mode="widthFix"
+            />
+            <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="title">{{item.BroadcastName}}</view>
+                <view class="time">发布时间:{{item.CreateTime|formatTime}}</view>
+                <view class="flex audio-box" @click="handlePlay(item)">
+                    <image 
+                        :src="item.BroadcastId==temAudio.id&&!temAudio.paused?require('@/static/voice/playing.png'):require('@/static/voice/pause.png')" 
+                        mode="widthFix" 
+                    />
+                    <text v-if="item.BroadcastId==temAudio.id">{{temAudio.curTime|formatVoiceTime}}</text>
+                    <text v-else>{{item.VoicePlaySeconds|formatVoiceTime}}</text>
+                </view>
+                <image class="del-btn" src="@/static/voice/del.png" @click="handleDelItem(item)" mode="widthFix" v-if="item.IsAuthor" />
+                <button 
+                    class="share-btn" 
+                    open-type="share" 
+                    :data-item="item"
+                >
+                    <image class="share-img" src="@/static/share-icon.png" mode="aspectFill"/>
+                </button>
+            </view>
+        </view>
+
+        <navigator url="/pages-voice/addVoice">
+            <view class="add-btn" v-if="IsVoiceAdmin">新建语音</view>
+        </navigator>
+
+        <!-- 筛选弹窗 -->
+        <van-popup 
+            :show="showFilter" 
+            position="bottom"  
+            :close-on-click-overlay="true"
+            @close="showFilter = false"
+            round
+        >
+            <view class="fliter-wrap-list">
+                <view class="flex top">
+                    <text style="color:#000">全部筛选</text>
+                    <text style="color:#E3B377" @click="showFilter=false">取消</text>
+                </view>
+                <van-tree-select
+                    :items="options"
+                    :main-active-index="mainActiveIndex"
+                    :active-id="activeId"
+                    @click-nav="onClickNav"
+                    @click-item="onClickItem"
+                    main-active-class="main-active-class"
+                    content-active-class="content-active-class"
+                />
+            </view>
+        </van-popup>
+
+        <van-dialog id="van-dialog" />
+    </view>
+    <noAuth :info="noAuthData" v-else/>
+</template>
+
+<script>
+import {apiVoiceList,apiVoiceSectionList,apiVoicePlayRecord,apiVoiceDel} from '@/api/voice'
+import {apiGetSceneToParams} from '@/api/common'
+import noAuth from './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 {
+            // innerAudioContext:null,//播放音频实例
+            list:[],
+            page:1,
+            pageSize:20,
+            finished:false,
+            voiceId:0,//分享时进入的音频id
+
+            IsVoiceAdmin:false,//是否是语音管理员
+
+            isAuth:true,
+            noAuthData:null,
+
+            showFilter:false,
+            options:[],
+            mainActiveIndex:0,
+            activeId:0,//选择的板块id
+
+            temAudio:{
+                paused:true,
+                url:'',//临时音频地址
+                duration:'',//时长
+                id:0,
+                curTime:''
+            }
+        }
+    },
+    onLoad(options){
+        this.init(options)
+        this.getOptionsList()
+        this.addListenVoiceSuccess()
+    },
+    onShow(){
+        //无权限时刷新列表
+        if(!this.isAuth){
+            this.getVoiceList()
+        }
+        innerAudioContext=uni.createInnerAudioContext();
+        this.listenAudio()
+    },
+    onHide(){
+        innerAudioContext.destroy()
+        this.temAudio.id=0
+        this.temAudio.paused=true
+        this.temAudio.curTime=''
+    },
+    onUnload(){
+		uni.$off('addVoiceSuccess')
+        innerAudioContext.destroy()
+        this.temAudio.id=0
+        this.temAudio.paused=true
+        this.temAudio.curTime=''
+	},
+    onShareAppMessage({from,target}) {
+        console.log(from,target);
+        let path='/pages/voice/voice?voiceId=0'
+        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}`
+            imageUrl=target.dataset.item.ImgUrl
+        }
+        return {
+            title:title,
+            path:path,
+            imageUrl:imageUrl
+        }
+    },
+    onPullDownRefresh(){
+        this.voiceId=0
+        this.page=1
+        this.list=[]
+        this.finished=false
+        this.getVoiceList()
+        this.getOptionsList()
+        setTimeout(() => {
+            uni.stopPullDownRefresh()
+        }, 1500)
+    },
+    onReachBottom() {
+        if(this.finished) return
+        this.page++
+        this.getVoiceList()
+    },
+
+    methods: {
+        // 监听添加音频成功刷新列表
+        addListenVoiceSuccess(){
+            uni.$on('addVoiceSuccess',()=>{
+                this.voiceId=0
+                this.page=1
+                this.list=[]
+                this.finished=false
+                this.getVoiceList()
+                this.getOptionsList()
+            })
+        },
+
+        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({
+                page_index:this.page,
+                page_size:this.pageSize,
+                broadcast_id:Number(this.voiceId),
+                section_id:Number(this.activeId)
+            })
+            if(res.code===200){
+                this.IsVoiceAdmin=res.data.IsVoiceAdmin
+                let arr=res.data.List||[]
+                this.list=[...this.list,...arr]
+                if(arr.length===0){
+                    this.finished=true
+                }
+
+                // 如果有voiceId 则说明是分享进入的 如果没有数据则提示
+                if(this.voiceId!=0&&arr.length===0){
+                    uni.showToast({
+                        title:'该语音播报不存在',
+                        icon:'none'
+                    })
+                    setTimeout(() => {
+                        this.voiceId=0
+                        this.page=1
+                        this.list=[]
+                        this.finished=false
+                        this.getVoiceList()
+                        this.getOptionsList()
+                    }, 1500);
+                }
+            }else if(res.code===403){
+                //无权限用户
+                this.isAuth=false
+                this.noAuthData=res.data
+            }
+        },
+
+        //获取筛选数据
+        async getOptionsList(){
+            const res=await apiVoiceSectionList()
+            if(res.code!==200) return
+            const arr=res.data||[]
+            this.options=arr.map(item=>{
+                let obj={
+                    text:'',
+                    children:[]
+                }
+                obj.text=item.VarietyName
+                obj.children=item.Children.map(_item=>{
+                    return {
+                        text:_item.SectionName,
+                        id:_item.SectionId
+                    }
+                })
+                return obj
+            })
+        },
+
+        onClickNav({detail}){
+            console.log(detail);
+            this.mainActiveIndex=detail.index
+        },
+
+        onClickItem({detail}){
+            console.log(detail);
+            if(this.activeId==detail.id){
+                this.activeId=0
+            }else{
+                this.activeId=detail.id
+            }
+            this.voiceId=0
+            this.page=1
+            this.list=[]
+            this.finished=false
+            this.getVoiceList()
+            this.showFilter=false
+        },
+        
+        //删除音频
+        handleDelItem(item){
+            this.$dialog.confirm({
+                title:'',
+                message: '确定要删除该语音播报吗?',
+                confirmButtonText:'确定'
+            }).then(()=>{
+                apiVoiceDel({broadcast_id:Number(item.BroadcastId)}).then(res=>{
+                    if(res.code===200){
+                        uni.showToast({
+                            message:'操作成功',
+                            icon:'none'
+                        })
+                        this.page=1
+                        this.list=[]
+                        this.finished=false
+                        this.getVoiceList()
+                    }
+                })
+            }).catch(()=>{})
+        },
+
+        handlePlay(item){
+            if(this.temAudio.id==item.BroadcastId){
+                if(innerAudioContext.paused){
+                    innerAudioContext.play()
+                }else{
+                    innerAudioContext.pause()
+                }
+            }else{
+                if(!innerAudioContext.paused){
+                    innerAudioContext.stop()
+                }
+                this.temAudio.id=item.BroadcastId
+                this.temAudio.duration=item.VoicePlaySeconds
+                this.temAudio.curTime=item.VoicePlaySeconds
+                innerAudioContext.src=item.VoiceUrl 
+                innerAudioContext.play()
+                this.handleVoicePlayRecord(item)
+            }
+        },
+
+        //音频播放事件
+        listenAudio(){
+            innerAudioContext.onPlay(()=>{
+                console.log('开始播放录音');
+                this.temAudio.paused=false
+            })
+            innerAudioContext.onPause(()=>{
+                console.log('录音播放暂停');
+                this.temAudio.paused=true
+            })
+            // innerAudioContext.onStop(()=>{
+            //     console.log('录音播放停止');
+            //     this.temAudio.paused=true
+            //     // setTimeout(() => {
+            //         this.temAudio.id=0
+            //     // }, 100);
+            // })
+            innerAudioContext.onEnded(()=>{
+                console.log('录音播放自然结束');
+                this.temAudio.paused=true
+                this.temAudio.id=0
+
+            })
+            innerAudioContext.onTimeUpdate(()=>{
+                // console.log('时间更新');
+                this.temAudio.curTime=parseInt(this.temAudio.duration)-parseInt(innerAudioContext.currentTime)
+            })
+        },
+
+        //上报音频播放记录
+        async handleVoicePlayRecord(item){
+            const res=await apiVoicePlayRecord({
+                broadcast_id:item.BroadcastId
+            })
+            if(res.code===200){
+                console.log('上报音频播放记录');
+            }
+        }
+    },
+}
+</script>
+
+<style lang="scss">
+.voice-play-page{
+    
+    .fliter-wrap-list{
+        background-color: #fff;
+        padding-top: 53rpx;
+        padding-bottom: 100rpx;
+        .top{
+            font-size: 32rpx;
+            justify-content: space-between;
+            margin-bottom: 40rpx;
+            padding: 0 34rpx;
+        }
+        .van-sidebar{
+            flex-shrink: 0;
+        }
+        .van-tree-select__content{
+            overflow-x: hidden;
+        }
+        .main-active-class{
+            border-color: #E3B377;
+        }
+        .content-active-class{
+            color: #E3B377;
+        }
+    }
+}
+</style>
+
+<style lang="scss" scoped>
+.voice-play-page{
+    
+    .top-filter-box{
+        // padding-left: 34rpx;
+        // padding-bottom: 10rpx;
+        // position: sticky;
+        // top: 0;
+        // left: 0;
+        // z-index: 99;
+        // background-color: #fff;
+        // display: flex;
+        // align-items: center;
+        display: flex;
+		flex: auto;
+		align-items: center;
+		height: 100rpx;
+		background-color: white;
+		position: sticky;
+		top: 0;
+		left: 0;
+		padding-left: 34rpx;
+		z-index: 99;
+        image{
+            width: 34rpx;
+            height: 34rpx;
+        }
+        color: #E3B377;
+        font-size: 28rpx;
+    }
+}
+.empty-box{
+    text-align: center;
+    font-size: 32rpx;
+    color: #999;
+    padding-top: 150rpx;
+    image{
+        width: 80vw;
+        margin-bottom: 57rpx;
+    }
+}
+.list-wrap{
+    padding: 0 34rpx 34rpx 34rpx;
+    .item{
+        border-bottom: 1px solid #CDCDCD;
+        padding: 30rpx 0;
+        position: relative;
+        .del-btn{
+            position: absolute;
+            right: 130rpx;
+            bottom: 30rpx;
+            width: 32rpx;
+            height: 32rpx;
+        }
+        .share-btn{
+            position: absolute;
+            bottom: 30rpx;
+            right: 34rpx;
+            background-color: transparent;
+            width: 36rpx;
+            height: 36rpx;
+            line-height: 1;
+            padding: 0;
+            &::after{
+                border: none;
+            }
+        }
+        .share-img{
+            width: 32.5rpx;
+            height: 32rpx;
+        }
+        .title{
+            font-size: 32rpx;
+        }
+        .time{
+            font-size: 28rpx;
+            color: #666;
+            margin-top: 20rpx;
+            margin-bottom: 30rpx;
+        }
+        .audio-box{
+            width: 185rpx;
+            height: 56rpx;
+            align-items: center;
+            justify-content: center;
+            border-radius: 28rpx;
+            background-color: #F4E1C9;
+            color: #E3B377;
+            image{
+                width: 23rpx;
+                height: 28rpx;
+                margin-right: 20rpx;
+            }
+        }
+    }
+
+}
+.add-btn{
+    position: fixed;
+    width: 514rpx;
+    height: 80rpx;
+    text-align: center;
+    line-height: 80rpx;
+    color: #E3B377;
+    font-size: 32rpx;
+    left: 50%;
+    bottom: calc(80px + constant(safe-area-inset-bottom));
+    bottom: calc(80px + env(safe-area-inset-bottom));
+    transform: translateX(-50%);
+    background: #333333;
+    box-shadow: 0px 4rpx 20rpx rgba(160, 126, 84, 0.25);
+    border-radius: 40rpx;
+}
+</style>

二进制
static/tabbar/voice-s.png


二进制
static/tabbar/voice.png


二进制
static/voice/del.png


二进制
static/voice/pause.png


二进制
static/voice/playing.png


+ 7 - 7
utils/config.js

@@ -67,13 +67,13 @@ const defaultTabBarListConfig=[
         iconPath: "../static/tabbar/question.png",
         iconPath: "../static/tabbar/question.png",
         selectedIconPath: "../static/tabbar/question-s.png",
         selectedIconPath: "../static/tabbar/question-s.png",
     },
     },
-	 {
-	     key: "activity",
-	     pagePath: "pages/activity/activity",
-	     text: "活动",
-	     iconPath: "../static/tabbar/activity.png",
-	     selectedIconPath: "../static/tabbar/activity-s.png",
-	 },
+	{
+	    key: "voice_broadcast",
+	    pagePath: "pages/voice/voice",
+	    text: "语音播报",
+	    iconPath: "../static/tabbar/voice.png",
+	    selectedIconPath: "../static/tabbar/voice-s.png",
+	},
 ]
 ]
 
 
 
 

+ 1 - 1
utils/request.js

@@ -95,7 +95,7 @@ const http=(url,params,method)=>{
 			method:method,
 			method:method,
 			header:{
 			header:{
 				Authorization:store.state.user.token,
 				Authorization:store.state.user.token,
-				version:'yb7.0'
+				version:'yb9.0'
 			},
 			},
 			success(e) {
 			success(e) {
 				// 接口404
 				// 接口404