jwyu 2 gadi atpakaļ
vecāks
revīzija
252cd45fd4

+ 10 - 0
api/common.js

@@ -67,4 +67,14 @@ export const apiGetTagTree = params=>{
  */
 export const apiUserBindPermission=()=>{
     return httpGet('/company/permission/bind',{})
+}
+
+/**
+ * 更新媒体播放记录时长
+ * @param id 日志ID
+ * @param stop_seconds 访问时长
+ * @param source 来源:1-问答社区; 2-语音播报; 3-视频社区; 4-路演视频
+ */
+export const apiViewLogUpdate=params=>{
+    return httpPost('/public/view_log/update',params)
 }

+ 19 - 0
api/report.js

@@ -107,4 +107,23 @@ export const apiChapterTickerValue=params=>{
  */
 export const apiReportPPtImgs=params=>{
     return httpGet('/report/ppt_img',params)
+}
+
+/**
+ * 品种类型报告中品种筛选项
+ * @param classify_id 分类id
+ */
+export const apiGoodsPermissionList=params=>{
+    return httpGet('/company/permission/commodities',params)
+}
+
+/**
+ * 按品种查询报告列表
+ * @param chart_permission_id 品种id
+ * @param classify_id 分类id
+ * @param current_index
+ * @param page_size
+ */
+export const apiReportListForVariety=params=>{
+    return httpGet('/report/variety/list',params)
 }

+ 30 - 0
api/user.js

@@ -69,4 +69,34 @@ export const apiLastApplyRecord=(params)=>{
  */
 export const apiSetMyinfo = params => {
 	return httpPost('/user/set',params)
+}
+
+/**
+ * 收藏
+ * @param collection_type 收藏类型:1-研报; 2-视频社区; 3-微路演视频
+ * @param primary_id //收藏类型主ID(如报告id,视频id)
+ * @param extend_id //扩展ID-如研报章节ID
+ * @param source_agent //操作来源:1-小程序 2-小程序 PC 3-弘则研究公众号 4-Web PC
+ */
+export const apiSetCollect=params=>{
+	return httpPost('/collection/collect',{source_agent:1,...params})
+}
+
+/**
+ * 取消收藏
+ * @param collection_id 收藏ID
+ */
+export const apiCancelCollect=params=>{
+	return httpPost('/collection/cancel',params)
+}
+
+/**
+ * 我的收藏列表
+ * @param curr_page
+ * @param page_size
+ * @param keywords
+ * @param from_type 来源类型:0-全部; 1-研报; 2-线上路演; 3-视频社区
+ */
+export const apiMyCollectList=params=>{
+	return httpGet('/collection/list',params)
 }

+ 16 - 1
components/audioBox/audioBox.vue

@@ -54,6 +54,7 @@
 </template>
 
 <script>
+import {apiViewLogUpdate} from '@/api/common'
 export default {
     filters:{
         formatVoiceTime(e){
@@ -120,6 +121,7 @@ export default {
             const curAudio=this.$store.state.audio.list[this.$store.state.audio.index]
             setTimeout(() => {
                 if(this.globalBgMusic.src!=curAudio.url){
+                    this.handleUpdateAudioPlayTime()
                     this.globalBgMusic.src=curAudio.url 
                     this.globalBgMusic.title=curAudio.title
                 }
@@ -141,16 +143,18 @@ export default {
             })
             this.globalBgMusic.onPause(()=>{
                 console.log('音频暂停');
+                this.handleUpdateAudioPlayTime()
                 this.play=false
                 this.$store.commit('audio/updateAudioPause',true)
             })
             this.globalBgMusic.onStop(()=>{
                 console.log('音频停止');
+                this.handleUpdateAudioPlayTime()
                 this.$store.commit('audio/removeAudio')
             })
             this.globalBgMusic.onEnded(()=>{
                 console.log('音频onEnded');
-
+                this.handleUpdateAudioPlayTime()
                 const index=this.$store.state.audio.index 
                 if(index==this.$store.state.audio.list.length-1){
                     this.$store.commit('audio/removeAudio')
@@ -212,6 +216,17 @@ export default {
             }
         },
 
+        // 记录音频播放 时长
+        handleUpdateAudioPlayTime(){
+            if(!this.$store.state.audio.recordId||this.$store.state.audio.curTime==0) return
+            apiViewLogUpdate({
+                id:this.$store.state.audio.recordId,
+                stop_seconds:parseInt(this.$store.state.audio.curTime),
+                source:this.$store.state.audio.lastType
+            }).then(res=>{
+                console.log('音频播放时间记录成功');
+            })
+        }
     },
 }
 </script>

+ 72 - 0
components/collectBox/collectBox.vue

@@ -0,0 +1,72 @@
+<template>
+    <view class="collect-box" @click.stop.prevent="handleClick">
+        <image class="icon" :src="cid>0?require('@/static/collect-s.png'):require('@/static/collect.png')" mode="aspectFill" />
+    </view>
+</template>
+
+<script>
+import {apiSetCollect,apiCancelCollect} from '@/api/user'
+export default {
+    props:{
+        type:{//收藏类型:1-研报; 2-视频社区; 3-微路演视频
+            type:Number,
+            default:0
+        },
+        primaryId:{// 藏类型主ID(如报告id,视频id)
+            default:0
+        },
+        extendId:{//扩展ID-如研报章节ID
+            default:0
+        },
+        collectId:{//收藏的id 大于0说明收藏了
+            default:0,
+        }
+    },
+    data() {
+        return {
+            cid:this.collectId
+        }
+    },
+    methods: {
+        handleClick(){
+            if(this.cid>0){
+                apiCancelCollect({
+                    collection_id:Number(this.cid)
+                }).then(res=>{
+                    if(res.code===200){
+                        uni.showToast({
+                            title:'取消收藏!',
+                            icon:'none'
+                        })
+                        this.cid=0
+                    }
+                })
+            }else{
+                apiSetCollect({
+                    collection_type:this.type,
+                    primary_id:Number(this.primaryId),
+                    extend_id:Number(this.extendId)
+                }).then(res=>{
+                    if(res.code===200){
+                        uni.showToast({
+                            title:'收藏成功!',
+                            icon:'none'
+                        })
+                        this.cid=res.data
+                    }
+                })
+            }
+        }
+    },
+
+}
+</script>
+
+<style lang="scss" scoped>
+.collect-box{
+    .icon{
+        width: 34rpx;
+        height: 34rpx;
+    }
+}
+</style>

+ 9 - 3
components/sharePoster/sharePoster.vue

@@ -1,8 +1,11 @@
 <template>
     <view class="share-poster-wrap" :catchtouchmove="false">
-        <image @click="handleCreatePoster" :style="style" class="share-icon" src="@/static/share-poster-icon.png" mode="aspectFill"/>
+        <div v-if="showSlot" @click.stop="handleCreatePoster">
+            <slot></slot>
+        </div>
+        <image @click.stop="handleCreatePoster" :style="style" class="share-icon" src="@/static/share-poster-icon.png" mode="aspectFill" v-else/>
 
-        <view class="poster-mask" v-if="show||showPoster" @click="showPoster=false"></view>
+        <view class="poster-mask" v-if="show||showPoster" @click.stop="showPoster=false"></view>
         <view class="loading-box" v-if="show">
             <image class="load-img" src="../../static/loading.png" mode="aspectFill" />
             <view>海报生成中...</view>
@@ -16,10 +19,13 @@ import {apiGetPoster} from '@/api/common'
 export default {
     // shareData.type 分享页面: 
     //activity_detail(活动详情) activity_list(活动列表) special_column_list(专栏列表) special_column_detail(专栏详情) 
-    //report_list(报告列表) report_detail(报告详情) chart_list(图库列表) chart_detail(图库详情)
+    //report_list(报告列表) report_detail(报告详情) chart_list(图库列表) chart_detail(图库详情) voice_detail(语音详情)
     props:{
         style:null,
         shareData:null,
+        showSlot:{//是否用插槽显示
+            default:false
+        }
     },
     data () {
         return {

+ 1 - 1
mixin/index.js

@@ -52,7 +52,7 @@ module.exports = {
      * 报告时间格式化
      */
     formatReportTime(e){
-      return moment(e).format('YYYY.MM.DD')
+      return moment(e).format('YYYY-MM-DD')
     }
 
   },

+ 1 - 0
mixin/questionMixin.js

@@ -405,6 +405,7 @@ export default {
                 }).then((res)=>{
                     if(res.code===200){
                       console.log('音频id为'+audioItem.community_question_audio_id+'点击次数+1')  
+                      this.$store.commit('audio/addAudioRecordId',{recordId:res.data,source:1})
                     }
                 })
             }

+ 11 - 3
pages-report/classify.vue

@@ -2,7 +2,7 @@
   <view class="classify-page">
     <swiper class="swiper" autoplay interval>
       <swiper-item>
-        <image @click="goFiccService" :src="globalImgUrls.ficcServiceImg" mode="aspectFill"/>
+        <image @click="goFiccService" :src="globalImgUrls.ficcServiceImg+'?t='+new Date().getDay()" mode="aspectFill"/>
       </swiper-item>
     </swiper>
     <view class="list">
@@ -45,7 +45,7 @@ export default {
     },
 
     goDetail(item){
-      //redirect_type : 跳转页面类型:1,专栏列表,2报告列表,3专栏详情
+      //redirect_type : 跳转页面类型:1,专栏列表,2报告列表,3专栏详情,4品种类型列表
       if(item.redirect_type==1){
         uni.navigateTo({ url: `/pages-report/specialColumn/list?classifyId=${item.classify_id_first}&classifyName=${item.classify_name_first}` })
       }
@@ -55,6 +55,9 @@ export default {
       if(item.redirect_type==3){
         uni.navigateTo({url:'/pages-report/specialColumn/detail?columnId='+item.classify_id_second})
       }
+      if(item.redirect_type==4){
+        uni.navigateTo({url:`/pages-report/reportForVariety/list?classifyId=${item.classify_id_first}&classifyName=${item.classify_name_first}`})
+      }
     },
 
     goFiccService(){
@@ -63,7 +66,12 @@ export default {
   }
 };
 </script>
-
+<style>
+page{
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+}
+</style>
 <style lang="scss" scoped>
 .classify-page {
   padding: 34rpx;

+ 366 - 0
pages-report/reportForVariety/list.vue

@@ -0,0 +1,366 @@
+<template>
+    <view class="varietyauth-report-list">
+        <view class="top-variety-box">
+            <view class="first-nav">
+                <text 
+                    :class="['first-nav-item',item.id===firstVarietyId?'active':'']" 
+                    v-for="item in varietyList" 
+                    :key="item.id"
+                    @click="handleSelectFirstVariety(item)"
+                >{{item.classify_name}}</text>
+            </view>
+            <view class="sub-nav">
+                <text 
+                    :class="['sub-nav-item',item.chart_permission_id===secVarietyId?'active':'']" 
+                    v-for="item in secVarietyList" 
+                    :key="item.chart_permission_id"
+                    @click="handleSelectSecVariety(item)"
+                >{{item.chart_permission_name}}</text>
+            </view>
+        </view>
+        <view class="report-empty-box" v-if="finished&&list.length==0">
+            <image :src="globalImgUrls.chartEmpty" mode="widthFix" />
+            <view>暂无报告</view>
+        </view>
+        <view class="report-list-wrap" v-else>
+            <view class="flex item" v-for="item in list" :key="item.report_id" @click="goReportDetail(item)">
+                <image class="img" :src="item.report_img_url" mode="aspectFill" lazy-load />
+                <view class="con">
+                    <view class="van-multi-ellipsis--l2 title">{{item.title}}</view>
+                    <view class="info">{{item.classify_name_second}} · {{item.stage}}期 | {{item.publish_time|formatReportTime}}</view>
+                </view>
+                <view class="audio-box" @click.stop="handleClickAudio(item)">
+                    <image src="../static/a-pause.png" mode="aspectFill"/>
+                </view>
+            </view>
+        </view>
+
+        <!-- 音频弹窗 -->
+        <audioBox v-if="showAudioPop"></audioBox>
+
+        <!-- 分享海报 -->
+        <sharePoster 
+        :style="{bottom:'190rpx'}" 
+        :shareData="{
+            type:'report_list',
+            code_page:'pages-report/reportForVariety/list',
+            code_scene:code_scene,
+            data:shareParams
+        }"></sharePoster>
+    </view>
+</template>
+
+<script>
+import {apiGetSceneToParams} from '@/api/common'
+import {apiGoodsPermissionList,apiReportListForVariety} from '@/api/report'
+import audioBox from '../components/audioBox.vue'
+const moment=require('@/utils/moment-with-locales.min')
+export default {
+    computed:{
+        showAudioPop(){//是否显示音频弹窗
+            return this.$store.state.report.audioData.show 
+        },
+        curAudioReportId(){//当前播放的音频所属报告
+            return this.$store.state.report.audioData.reportId
+        },
+        curAudioPaused(){//当前音频是否暂停状态
+            return this.$store.state.report.audioData.paused
+        },
+        shareParams(){
+            let obj={
+                list_title:this.classifyName,
+                img_1:'',
+                title_1:'',
+                abstract_1:'',
+                abstract_1_style:'',
+                time_1:'',
+                img_2:'',
+                title_2:'',
+                abstract_2:'',
+                abstract_2_style:'',
+                time_2:'',
+            }
+            if(this.list[0]){
+                obj.img_1=this.list[0].report_img_url
+                obj.title_1=this.list[0].title 
+                obj.abstract_1=this.list[0].classify_name_second
+                obj.time_1=`${this.list[0].stage}期 | ${moment(this.list[0].publish_time).format('YYYY.MM.DD')}`
+                obj.abstract_1_style=this.list[0].classify_name_second?'':'display:none'
+            }
+            if(this.list[1]){
+                obj.img_2=this.list[1].report_img_url
+                obj.title_2=this.list[1].title 
+                obj.abstract_2=this.list[1].classify_name_second
+                obj.time_2=`${this.list[1].stage}期 | ${moment(this.list[1].publish_time).format('YYYY.MM.DD')}`
+                obj.abstract_2_style=this.list[1].classify_name_second?'':'display:none'
+            }
+            return obj
+        },
+        code_scene(){
+            return JSON.stringify({classifyId:this.classifyId,classifyName:this.classifyName})
+        }
+    },
+    components: {
+        audioBox
+    },
+    data() {
+        return {
+            classifyId:0,
+            classifyName:'',
+            varietyList:[],
+            firstVarietyId:0,//选择的一级品种id
+            secVarietyList:[],//二级品种
+            secVarietyId:0,//选择的二级品种id
+
+            list:[],
+            finished:false,
+            page:1,
+            pageSize:20
+        }
+    },
+    onLoad(opt){
+        this.init(opt)
+    },
+    onPullDownRefresh() {
+        this.page=1
+        this.list=[]
+        this.finished=false
+        this.getPermissionList()
+        setTimeout(() => {
+            uni.stopPullDownRefresh()
+        }, 1500);
+    },
+    onReachBottom() {
+        if(this.finished) return
+        this.page++
+        this.getList()
+    },
+    onShareAppMessage() {
+        return {
+            title:`FICC【${this.classifyName}】`
+        }
+    },
+    methods: {
+        async init(opt){
+            let classifyId=opt.classifyId||0
+            let classifyName=opt.classifyName
+            if(opt.scene){
+                const res=await apiGetSceneToParams({scene_key:opt.scene})
+                if(res.code===200){
+                    const obj=JSON.parse(res.data)
+                    classifyId=obj.classifyId
+                    classifyName=obj.classifyName
+                }
+            }
+            this.classifyId=classifyId
+            this.classifyName=decodeURIComponent(classifyName)
+            uni.setNavigationBarTitle({ title: this.classifyName })
+            this.getPermissionList()
+        },
+
+        //获取用户已绑定品种
+        async getPermissionList(){
+            const res=await apiGoodsPermissionList({
+                classify_id:Number(this.classifyId),
+            })
+            if(res.code===200){
+                let arr=res.data.permission_list||[]
+                //如果一个权限都没有则返回到分类列表
+                if(arr.length===0){
+                    uni.showToast({
+                        title: '暂无权限',
+                        icon: 'none'
+                    })
+                    setTimeout(() => {
+                        uni.reLaunch({
+                            url: '/pages-report/classify',
+                        });
+                    }, 1500);
+                    
+                    return
+                }
+                this.varietyList=arr
+                this.handleSelectFirstVariety(this.varietyList[0])
+            }
+        },
+
+        //选择一级品种
+        handleSelectFirstVariety(item){
+            this.firstVarietyId=item.id
+            this.secVarietyList=item.list
+            this.handleSelectSecVariety(item.list[0])
+        },
+        //选择二级品种
+        handleSelectSecVariety(item){
+            this.secVarietyId=item.chart_permission_id
+            this.page=1
+            this.finished=false
+            this.list=[]
+            this.getList()
+        },
+
+        async getList(){
+            const res=await apiReportListForVariety({
+                chart_permission_id:Number(this.secVarietyId),
+                classify_id:Number(this.classifyId),
+                current_index:this.page,
+                page_size:this.pageSize
+            })
+            if(res.code===200){
+                const arr=res.data.list||[]
+                this.list=[...this.list,...arr]
+                this.finished=res.data.paging.is_end
+            }
+        },
+
+        // 跳转报告详情
+        goReportDetail(item){
+            if(['晨报','周报'].includes(item.classify_name_first)){
+                uni.navigateTo({url: `/pages-report/chapterDetail?chapterId=${item.report_chapter_id}`})
+            }else{
+                uni.navigateTo({url:'/pages-report/reportDetail?reportId='+item.report_id})
+            }
+        },
+
+        handleClickAudio(item){
+            if(!item.auth_ok) return
+            if(!item.video_list||item.video_list.length==0){
+                uni.showToast({
+                    title: '暂无音频',
+                    icon: 'none'
+                })
+                return
+            }
+            // 判断是否为同一个音频
+            if(this.$store.state.report.audioData.reportId==item.report_id){
+                if(this.globalBgMusic.paused){
+                    this.globalBgMusic.play()
+                    this.$store.commit('showPopAudio')
+                }else{
+                    this.globalBgMusic.pause()
+                }
+            }else{
+                this.$store.commit('addAudio', {list:item.video_list,reportId:item.report_id})
+            }
+        },
+    },
+}
+</script>
+
+<style>
+page{
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+}
+</style>
+
+<style lang="scss" scoped>
+.top-variety-box{
+    padding: 40rpx 34rpx 0 34rpx;
+    box-shadow: 0px 4rpx 4rpx rgba(198, 198, 198, 0.25);
+    background-color: #fff;
+    position: sticky;
+    left: 0;
+    right: 0;
+    top: 0;
+    z-index: 50;
+    .first-nav-item{
+        display: inline-block;
+        min-width: 140rpx;
+        line-height: 70rpx;
+        margin-right: 30rpx;
+        font-size: 32rpx;
+        color: #666;
+        text-align: center;
+        background: #F5F5F5;
+        border-radius: 4px;
+        box-sizing: border-box;
+        padding: 0 20rpx;
+        margin-bottom: 16rpx;
+        &.active{
+            color: #E3B377;
+            background: #FDF8F2;
+        }
+    }
+    .sub-nav{
+        overflow-x: auto;
+        white-space: nowrap;
+        padding: 16rpx 0;
+        &::-webkit-scrollbar{
+            width: 0;
+            height: 0;
+            display: none;
+        }
+        .sub-nav-item{
+            display: inline-block;
+            position: relative;
+            margin-right: 50rpx;
+            font-size: 28rpx;
+            color: #666;
+            position: relative;
+            &.active{
+                color: #E3B377;
+                &::after{
+                    content: '';
+                    display: block;
+                    width: 54rpx;
+                    height: 4rpx;
+                    background-color: #E3B377;
+                    position: absolute;
+                    bottom: -16rpx;
+                    left: 50%;
+                    transform: translateX(-50%);
+                }
+            }
+        }
+    }
+}
+.report-list-wrap{
+    padding: 34rpx;
+    .item{
+        margin-bottom: 30rpx;
+        position: relative;
+        .img{
+            width: 90rpx;
+            height: 120rpx;
+            background-color: #f5f5f5;
+            border-radius: 8rpx;
+            overflow: hidden;
+            margin-right: 19rpx;
+            flex-shrink: 0;
+        }
+        .con{
+            padding-right: 100rpx;
+            flex: 1;
+        }
+        .title{
+            font-size: 28rpx;
+            font-weight: bold;
+            margin-bottom: 10rpx;
+            min-height: 70rpx;
+        }
+        .info{
+            font-size: 24rpx;
+            color: #9C9791;
+        }
+        .audio-box{
+            position: absolute;
+            top: 50%;
+            transform: translateY(-50%);
+            width: 22px;
+            height: 20px;
+            background: linear-gradient(180deg, #F3A52F 0%, #E3B377 100%);
+            border-radius: 6px;
+            right: 20rpx;
+            text-align: center;
+            image{
+                width: 12px;
+                height: 12px;
+                position: relative;
+                top: 1px;
+            }
+        }
+
+    }
+}
+</style>

+ 51 - 52
pages-report/reportList.vue

@@ -24,11 +24,13 @@
         <image class="img" :src="item.report_img_url" mode="aspectFill" lazy-load />
         <view class="con">
           <view class="title" v-html="item.title"></view>
-          <view class="info" v-html="item.classify_name_second"></view>
-          <view class="time">{{item.stage}}期 | {{item.publish_time|formatReportTime}}</view>
-          <view :class="['audio-box',!item.auth_ok&&'grey-audio-box']" @click.stop="handleClickAudio(item)" v-if="item.auth_ok">
-            <image :src="curAudioReportId==item.report_id&&!curAudioPaused?'./static/audio-s.png':'./static/audio.png'" mode="aspectFill"/>
-            <text>{{curAudioReportId==item.report_id&&!curAudioPaused?'暂停':'播放'}}</text>
+          <view class="info">
+            <view v-html="item.classify_name_second" style="display:inline-block"></view>
+            <text v-if="item.classify_name_second" style="display:inline-block;margin:0 10rpx">&nbsp;·&nbsp;</text>
+            <text>{{item.stage}}期 | {{item.publish_time|formatReportTime}}</text>
+          </view>
+          <view class="audio-box" v-if="item.auth_ok" @click.stop="handleClickAudio(item)">
+            <image :src="curAudioReportId==item.report_id&&!curAudioPaused?'./static/a-play.png':'./static/a-pause.png'" mode="aspectFill"/>
           </view>
         </view>
       </view>
@@ -36,7 +38,7 @@
 
     <!-- 筛选 -->
     <van-popup :show="showFilter" position="bottom" :safe-area-inset-bottom="false" round @close="showFilter=false">
-        <view class="filter-wrap">
+        <view class="filter-wrap" @touchmove.stop>
             <view class="flex top">
                 <text style="color:#000">筛选</text>
                 <text style="color:#E3B377" @click="showFilter=false">取消</text>
@@ -93,10 +95,12 @@ export default {
         img_1:'',
         title_1:'',
         abstract_1:'',
+        abstract_1_style:'',
         time_1:'',
         img_2:'',
         title_2:'',
         abstract_2:'',
+        abstract_2_style:'',
         time_2:'',
       }
       if(this.list[0]){
@@ -104,12 +108,14 @@ export default {
         obj.title_1=this.list[0].title 
         obj.abstract_1=this.list[0].classify_name_second
         obj.time_1=`${this.list[0].stage}期 | ${moment(this.list[0].publish_time).format('YYYY.MM.DD')}`
+        obj.abstract_1_style=this.list[0].classify_name_second?'':'display:none'
       }
       if(this.list[1]){
         obj.img_2=this.list[1].report_img_url
         obj.title_2=this.list[1].title 
         obj.abstract_2=this.list[1].classify_name_second
         obj.time_2=`${this.list[1].stage}期 | ${moment(this.list[1].publish_time).format('YYYY.MM.DD')}`
+        obj.abstract_2_style=this.list[1].classify_name_second?'':'display:none'
       }
       return obj
     },
@@ -274,7 +280,8 @@ export default {
 
 <style>
 page{
-  padding-bottom: 0;
+  padding-bottom: constant(safe-area-inset-bottom);
+  padding-bottom: env(safe-area-inset-bottom);
 }
 </style>
 <style lang="scss" scoped>
@@ -298,58 +305,50 @@ page{
 }
 .report-list-wrap {
   padding: 0 34rpx;
-  .item {
-    padding-bottom: 30rpx;
+  .item{
     margin-bottom: 30rpx;
-    border-bottom: 1px solid #EDEDED;
-    .img {
-      width: 120rpx;
-      height: 180rpx;
+    position: relative;
+    .img{
+      width: 90rpx;
+      height: 120rpx;
       background-color: #f5f5f5;
+      border-radius: 8rpx;
+      overflow: hidden;
+      margin-right: 19rpx;
       flex-shrink: 0;
-      margin-right: 20rpx;
-      border-radius: 16rpx;
     }
-    .con {
+    .con{
+      padding-right: 100rpx;
       flex: 1;
-      position: relative;
-      overflow: hidden;
-      .title {
-        font-size: 32rpx;
-        font-weight: bold;
-        margin-bottom: 8rpx;
-      }
-      
-      .time {
-        position: absolute;
-        color: #666666;
-        bottom: 0;
-        left: 0;
-        font-size: 28rpx;
-      }
-      .audio-box {
-        position: absolute;
-        bottom: 0;
-        right: 0;
-        width: 99rpx;
-        height: 39rpx;
-        background: #E3B377;
-        border-radius: 20rpx;
-        color: #fff;
-        font-size: 24rpx;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        image{
-          width: 30rpx;
-          height: 30rpx;
-          margin-right: 4rpx;
-        }
-      }
-      .grey-audio-box {
-        background: linear-gradient(114deg, #b0b0b0 0%, #e5e2e2 100%);
+    }
+    .title{
+      font-size: 28rpx;
+      font-weight: bold;
+      margin-bottom: 10rpx;
+      min-height: 70rpx;
+    }
+    .info{
+      font-size: 24rpx;
+      color: #9C9791;
+    }
+    .audio-box{
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 22px;
+      height: 20px;
+      background: linear-gradient(180deg, #F3A52F 0%, #E3B377 100%);
+      border-radius: 6px;
+      right: 20rpx;
+      text-align: center;
+      image{
+        width: 12px;
+        height: 12px;
+        position: relative;
+        top: 1px;
       }
     }
+
   }
 }
 .filter-wrap{

BIN
pages-report/static/a-pause.png


BIN
pages-report/static/a-play.png


+ 14 - 2
pages-roadShow/video/list.vue

@@ -36,6 +36,12 @@
                     <text class="tag">{{item.chart_permission_name}}</text>
                     <text class="title">{{item.title}}</text>
                 </view>
+                <collectBox
+                    :type="3"
+                    :primaryId="item.road_video_id"
+                    :collectId="item.collection_id"
+                    class="share-btn collect-btn"
+                />
                 <button 
                     class="share-btn" 
                     open-type="share" 
@@ -50,6 +56,7 @@
                     autoplay
                     object-fit="contain"
                     show-mute-btn
+                	show-background-playback-button
                     :poster="item.cover_img_url"
                     :src="item.video_url"
                     enable-play-gesture
@@ -120,11 +127,13 @@ import {apiGetSceneToParams,apiUserBindPermission} from '@/api/common'
 import noAuth from './components/noAuth.vue'
 import dragButton from '@/components/dragButton/dragButton.vue'
 import comment from '@/components/videoComment/comment.vue'
+import collectBox from '@/components/collectBox/collectBox.vue'
 export default {
     components:{
         noAuth,
         dragButton,
-        comment
+        comment,
+        collectBox
     },
     data() {
         return {
@@ -347,7 +356,7 @@ export default {
             padding: 30rpx 34rpx;
             position: relative;
             .title-box{
-                padding-right: 40rpx;
+                margin-right: 110rpx;
             }
             .share-btn{
                 position: absolute;
@@ -366,6 +375,9 @@ export default {
                 width: 32.5rpx;
                 height: 32rpx;
             }
+            .collect-btn{
+                right: 96rpx;
+            }
             .tag{
                 color: #E4B478;
                 background-color: #333;

+ 43 - 2
pages-roadShow/video/search.vue

@@ -23,6 +23,12 @@
                     <text class="tag">{{item.chart_permission_name}}</text>
                     <text class="title">{{item.title}}</text>
                 </view>
+                <collectBox
+                    :type="3"
+                    :primaryId="item.road_video_id"
+                    :collectId="item.collection_id"
+                    class="share-btn collect-btn"
+                />
                 <button 
                     class="share-btn" 
                     open-type="share" 
@@ -37,11 +43,14 @@
                     autoplay
                     object-fit="contain"
                     show-mute-btn
+                    show-background-playback-button
                     :poster="item.cover_img_url"
                     :src="item.video_url"
                     enable-play-gesture
                     :id="item.road_video_id"
                     @ended="handleVideoEnd"
+                    @pause="handleVideoPause"
+                    @timeupdate="handleTimeUpdate"
                     v-if="item.road_video_id==curVideoId"
                 ></video>
                 <image @click="handelClickPlay(item)" v-else class="poster" :src="item.cover_img_url" mode="aspectFill" lazy-load/>
@@ -55,9 +64,12 @@
 import searchBox from '@/components/searchBox/searchBox.vue'
 import {apiRoadShowVideoList,apiRoadShowVideoPlayLog} from '@/api/roadShow'
 import comment from '@/components/videoComment/comment.vue'
+import {apiViewLogUpdate} from '@/api/common'
+import collectBox from '@/components/collectBox/collectBox.vue'
 export default {
     components: {
         searchBox,
+        collectBox,
         comment
     },
     data() {
@@ -70,7 +82,9 @@ export default {
             pageSize:10,
 
             curVideoId:0,
-            curVideoIns:null
+            curVideoIns:null,
+            curVideoTime:0,
+            videoRecordId:0,
         }
     },
     onReachBottom() {
@@ -143,6 +157,7 @@ export default {
             apiRoadShowVideoPlayLog({video_id:Number(item.road_video_id)}).then(res=>{
                 if(res.code===200){
                     console.log('视频埋点成功');
+                    this.videoRecordId=res.data
                 }
             })
         },
@@ -155,6 +170,29 @@ export default {
                 this.curVideoId=0
                 this.curVideoIns=null
             }, 200);
+        },
+
+        //时长变化
+        handleTimeUpdate(e){
+            // console.log(this.curVideoId,e.detail.currentTime);
+            this.curVideoTime=e.detail.currentTime
+        },
+
+        handleVideoPause(){
+            // console.log(`视频 pause---${this.videoRecordId}----${this.curVideoTime}`);
+            this.handleUpdateVideoPlayTime()
+        },
+
+        // 更新播放时长
+        handleUpdateVideoPlayTime(){
+            if(!this.videoRecordId) return
+            apiViewLogUpdate({
+                id:this.videoRecordId,
+                stop_seconds:parseInt(this.curVideoTime),
+                source:4
+            }).then(res=>{
+                console.log('更新播放时长成功');
+            })
         }
     },
 }
@@ -190,7 +228,7 @@ page{
             padding: 30rpx 34rpx;
             position: relative;
             .title-box{
-                padding-right: 40rpx;
+                margin-right: 110rpx;
             }
             .share-btn{
                 position: absolute;
@@ -209,6 +247,9 @@ page{
                 width: 32.5rpx;
                 height: 32rpx;
             }
+            .collect-btn{
+                right: 96rpx;
+            }
             .tag{
                 color: #E4B478;
                 background-color: #333;

+ 3 - 11
pages-sandTable/sandTable.vue

@@ -244,16 +244,7 @@
 		},
 		onReachBottom() {
 			if(this.isRequseting) return 
-			if(this.list.length>=this.total && this.total!=0){
-				// if(this.haveShowToast) return
-				// // 只显示一次
-				// this.haveShowToast = true
-				// uni.showToast({
-				// 	title:"没有了~",
-				// 	icon:'none'
-				// })
-				return
-			}
+			if(this.list.length>=this.total && this.total!=0) return
 			this.sandTableQuery.curr_page++
 			this.getSandBoxList()
 		},
@@ -409,6 +400,7 @@
 							uni.navigateTo({url:'/pages-applyPermission/applyResult'})
 						})
 					}else{
+						this.haveGoToResult=true
 						uni.navigateTo({ url: '/pages-applyPermission/applyPermission?source=7&from_page=沙盘推演' })
 					}
 				}
@@ -479,7 +471,7 @@
 							current:index,
 							showmenu:false
 						})
-					},10)
+					},20)
 				})
 			},
 			// 生成海报

+ 315 - 0
pages-user/myCollect.vue

@@ -0,0 +1,315 @@
+<template>
+    <view class="my-collect-page">
+        <view :class="['top-wrap',!(!isSearchRes&&!keywords)?'top-wrap-ptb':'']">
+            <searchBox placeholder="搜索" @change="onChange" @search="onSearch" />
+            <view class="tab-box" v-show="!isSearchRes&&!keywords">
+                <text 
+                    :class="['tab-item',active==item.val?'tab-active':'']" 
+                    v-for="item in opts" 
+                    :key="item.val"
+                    @click="handleChangeOpt(item.val)"
+                >{{item.name}}</text>
+            </view>
+        </view>
+        <view class="list-wrap">
+            <view class="item" v-for="(item,index) in list" :key="item.CollectionId">
+                <view class="c-time">{{item.CreateTime|formatCTime}}</view>
+                <van-swipe-cell :right-width="90">
+                    <!-- 视频类别样式 -->
+                    <view class="flex video-box" v-if="[2,3].includes(item.CollectionType)" @click="goDetail(item)">
+                        <image class="img" :src="item.ImgUrl" mode="aspectFill" lazy-load="true"/>
+                        <view class="con">
+                            <view class="title" v-html="item.Title"></view>
+                            <view class="author">{{item.Author}}</view>
+                            <view class="time">发布时间:{{item.PublishTime}}</view>
+                        </view>
+                    </view>
+                    <!-- 报告类型样式 -->
+                    <view class="report-box" v-if="item.CollectionType==1" @click="goDetail(item)">
+                        <view class="title" v-html="item.Title"></view>
+                        <view class="con">
+                            <text v-if="item.ClassifyName">#{{item.ClassifyName}}</text>
+                            <text v-if="item.ClassifySecondName" style="margin-left:20rpx">#{{item.ClassifySecondName}}</text>
+                            <view class="time">发布时间:{{item.PublishTime}}</view>
+                        </view>
+                    </view>
+                    <view slot="right" class="cancel-btn" @click="handleCancel(item,index)">取消收藏</view>
+                </van-swipe-cell>
+            </view>
+              
+        </view>
+        <view class="report-empty-box" v-if="finished&&list.length==0">
+            <image :src="globalImgUrls.chartEmpty" mode="widthFix" />
+            <view>暂无相关收藏内容</view>
+        </view>
+        
+        <van-dialog id="van-dialog" />
+    </view>
+</template>
+
+<script>
+import searchBox from "@/components/searchBox/searchBox.vue";
+import {apiMyCollectList,apiCancelCollect} from '@/api/user'
+const moment=require('@/utils/moment-with-locales.min')
+export default {
+    components: {
+        searchBox
+    },
+    filters: {
+        formatCTime(e){
+            if(moment().isSame(e,'year')){//当年
+                return moment(e).format('MM-DD')
+            }else{
+                return moment(e).format('YYYY-MM-DD')
+            }
+        }
+    },
+    data() {
+        return {
+            opts:[
+                {
+                    name:'全部',
+                    val:0,
+                },
+                {
+                    name:'研报',
+                    val:1,
+                },
+                {
+                    name:'线上路演',
+                    val:2,
+                },
+                {
+                    name:'视频社区',
+                    val:3,
+                },
+            ],
+            active:0,
+
+            page:1,
+            pageSize:20,
+            finished:false,
+            list:[],
+            keywords:'',
+            isSearchRes:false
+        }
+    },
+    onLoad(){
+        this.getList()
+    },
+    onPullDownRefresh() {
+        this.keywords=''
+        this.page=1
+        this.list=[]
+        this.finished=false
+        this.isSearchRes=false
+        this.getList()
+        setTimeout(() => {
+            uni.stopPullDownRefresh()
+        }, 1500);
+    },
+    onReachBottom() {
+        if(this.finished) return
+        this.page++
+        this.getList()
+    },
+    methods: {
+        onChange(e){
+            this.keywords=e
+            this.finished=false
+            this.list=[]
+        },
+
+        onSearch(){
+            this.active=0
+            this.page=1
+            this.finished=false
+            this.list=[]
+            if(this.keywords){
+                this.isSearchRes=true
+            }else{
+                this.isSearchRes=false
+            }
+            
+            this.getList()
+        },
+
+        handleChangeOpt(e){
+            this.active=e
+            this.page=1
+            this.finished=false
+            this.list=[]
+            this.keywords=''
+            this.isSearchRes=false
+            this.getList()
+        },
+
+        async getList(){
+            const res=await apiMyCollectList({
+                curr_page:this.page,
+                page_size:this.pageSize,
+                keywords:this.keywords,
+                from_type:this.active
+            })
+            if(res.code===200){
+                const arr=res.data.list
+                this.list=[...this.list,...arr]
+                this.finished=res.data.paging.is_end
+            }
+        },
+
+        handleCancel(item,index){
+            this.$dialog.confirm({
+                title:'',
+                message: '确认取消收藏?',
+                confirmButtonText:'确定'
+            }).then(()=>{
+                apiCancelCollect({collection_id:Number(item.CollectionId)}).then(res=>{
+                    if(res.code===200){
+                        uni.showToast({
+                            title:'操作成功',
+                            icon:'none'
+                        })
+                        this.list.splice(index,1)
+                    }
+                })
+            }).catch(()=>{})
+        },
+
+        goDetail(item){
+            if(item.CollectionType==1){// 报告
+                if(item.ExtendId>0){
+                    uni.navigateTo({url: `/pages-report/chapterDetail?chapterId=${item.ExtendId}`})
+                }else{
+                    uni.navigateTo({url:'/pages-report/reportDetail?reportId='+item.PrimaryId})
+                }
+            }else if(item.CollectionType==2){//视频社区
+                //跳转tabbar不允许带参数 所以用relaunch
+                uni.reLaunch({url:'/pages/video/videoList?videoId='+item.PrimaryId})
+            }else if(item.CollectionType==3){//路演视频
+                uni.reLaunch({url:'/pages/roadShow/video/list?videoId='+item.PrimaryId})
+            }
+        }
+
+    },
+}
+</script>
+<style lang="scss">
+page{
+    padding-bottom: constant(safe-area-inset-bottom);
+    padding-bottom: env(safe-area-inset-bottom);
+}
+</style>
+
+<style lang="scss" scoped>
+.top-wrap{
+    position: sticky;
+    top: 0;
+    left: 0;
+    right: 0;
+    z-index: 50;
+    background-color: #fff;
+    padding: 40rpx 34rpx 0 34rpx;
+    box-shadow: 0px 4rpx 4rpx 0px rgba(198,198,198,0.25);
+    &.top-wrap-ptb{
+        box-shadow: none;
+        padding-bottom: 20rpx;
+    }
+    .tab-box{
+        padding-top: 40rpx;
+        padding-bottom: 10rpx;
+        .tab-item{
+            display: inline-block;
+            margin-right: 60rpx;
+            color: #666;
+            font-size: 32rpx;
+        }
+        .tab-active{
+            color: #333;
+            position: relative;
+            &::after{
+                position: absolute;
+                content: '';
+                display: block;
+                width: 36rpx;
+                height: 6rpx;
+                background: #E3B377;
+                border-radius: 3rpx;
+                bottom: -10rpx;
+                left: 50%;
+                transform: translateX(-50%);
+            }
+        }
+    }
+}
+.list-wrap{
+    padding: 34rpx;
+    .item{
+        padding: 30rpx 0;
+        border-bottom: 1px solid #E5E5E5;
+        .c-time{
+            display: inline-block;
+            font-size: 28rpx;
+            padding: 5rpx 10rpx;
+            color: #666;
+            background-color: #f6f6f6;
+            border-radius: 3rpx;
+            margin-bottom: 12rpx;
+        }
+        .video-box{
+            .img{
+                width: 221rpx;
+                height: 192rpx;
+                flex-shrink: 0;
+                border-radius: 4rpx;
+                margin-right: 20rpx;
+            }
+            .con{
+                flex: 1;
+            }
+            .title{
+                font-size: 32rpx;
+                color: #000;
+                min-height: 80rpx;
+            }
+            .author{
+                color: #666;
+                min-height: 40rpx;
+                margin: 10rpx 0 20rpx 0;
+            }
+            .time{
+                color: #666;
+            }
+        }
+
+        .report-box{
+            .title{
+                line-height: 1.7;
+                margin: 12rpx 0;
+            }
+            .con{
+                text{
+                    display: inline-block;
+                    color: #666;
+                }
+                .time{
+                    float: right;
+                }
+            }
+        }
+
+        .cancel-btn{
+            position: absolute;
+            top: 50%;
+            transform: translateY(-50%);
+            background: #F3A52F;
+            border-radius: 25px;
+            color: #fff;
+            font-size: 16px;
+            width: 84px;
+            text-align: center;
+            line-height: 30px;
+        }
+    }
+}
+</style>

+ 1 - 0
pages-voice/myVoice.vue

@@ -338,6 +338,7 @@ export default {
             })
             if(res.code===200){
                 console.log('上报音频播放记录');
+                this.$store.commit('audio/addAudioRecordId',{recordId:res.data,source:2})
             }
         }
     },

+ 19 - 1
pages-voice/voiceDetail.vue

@@ -22,6 +22,20 @@
         </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"/>
+        <sharePoster 
+            :showSlot="true"
+            :shareData="{
+                type:'voice_detail',
+                code_page:'pages-voice/voiceDetail',
+                code_scene:JSON.stringify({voiceId:info.BroadcastId}),
+                data:{
+                    title:info.BroadcastName,
+                    img:info.ImgUrl
+                }
+            }"
+        >
+            <image style="width:34rpx;height:34rpx;float: right;" src="@/static/voice/creat-poster-icon.png" mode="aspectFill" />
+        </sharePoster>
 
         <!-- 图片部分 -->
         <view class="imgs-box">
@@ -61,12 +75,14 @@ import {apiGetSceneToParams} from '@/api/common'
 import noAuth from '@/pages/voice/components/noAuth.vue'
 import audioBox from '@/components/audioBox/audioBox.vue'
 import dragButton from '@/components/dragButton/dragButton.vue'
+import sharePoster from '@/components/sharePoster/sharePoster.vue'
 const moment=require('@/utils/moment-with-locales.min')
 export default {
     components:{
         noAuth,
         audioBox,
-        audioBox
+        dragButton,
+        sharePoster
     },
     computed:{
         showAudioPop(){//是否显示音频弹窗
@@ -201,6 +217,7 @@ export default {
             })
             if(res.code===200){
                 console.log('上报音频播放记录');
+                this.$store.commit('audio/addAudioRecordId',{recordId:res.data,source:2})
             }
         },
 
@@ -305,6 +322,7 @@ export default {
             float: right;
             width: 36rpx;
             height: 36rpx;
+            margin-left: 40rpx;
         }
         .imgs-box{
             margin-top: 120rpx;

+ 23 - 8
pages.json

@@ -154,13 +154,20 @@
 						"enablePullDownRefresh": true
 					}
 				},
-			   {
-			   	"path": "mysetting",
-			   	"style":{
-			   		"navigationBarTitleText": "我的设置"
-			   	}
-			   }
-      ]
+			   	{
+					"path": "mysetting",
+					"style":{
+						"navigationBarTitleText": "我的设置"
+					}
+				},
+				{
+					"path": "myCollect",
+					"style":{
+						"navigationBarTitleText": "我的收藏",
+						"enablePullDownRefresh": true
+					}
+				}
+      		]
 		},
 		// 图库模块
 		{
@@ -252,6 +259,13 @@
 					"style":{
 						"navigationStyle": "custom"
 					}
+				},
+				//按用户有权限品种分的报告列表页
+				{
+					"path": "reportForVariety/list",
+					"style":{
+						"enablePullDownRefresh": true
+					}
 				}
 			]
 		},
@@ -409,7 +423,8 @@
 			"van-dialog": "/wxcomponents/vant/dialog/index",
 			"van-cell": "/wxcomponents/vant/cell/index",
 			"van-tree-select": "/wxcomponents/vant/tree-select/index",
-			"van-datetime-picker": "/wxcomponents/vant/datetime-picker/index"
+			"van-datetime-picker": "/wxcomponents/vant/datetime-picker/index",
+			"van-swipe-cell": "/wxcomponents/vant/swipe-cell/index"
 		}
 	}
 }

+ 14 - 2
pages/pc.vue

@@ -22,7 +22,8 @@ const mapObj=new Map([
     ['pages/voice/voice','/voice/list'],
     ['pages-voice/voiceDetail','/voice/detail'],
     ['pages-roadShow/video/list','/roadshow/video/list'],
-    ['pages/roadShow/video/list','/roadshow/video/list']
+    ['pages/roadShow/video/list','/roadshow/video/list'],
+    ['pages-report/reportForVariety/list','/report/varietyreportlist']
 ])//map映射小程序页面路径对应h5页面路径
 import {apiUserInfo} from '@/api/user'
 import {apiGetSceneToParams} from '@/api/common'
@@ -110,7 +111,18 @@ export default {
                 }
             }
             console.log('拼接字符串:',paramsObjStr);
-            this.url=`${pcBaseUrl}${mapObj.get(decodeURIComponent(options.xcxPath))||'/'}?${paramsObjStr}#wechat_redirect`
+			uni.getSystemInfo({
+					success: (data) => {
+						// 企业微信会额外返回一个 environment 字段 值为 wxwork 在企业微信PC版中,一旦后缀带上#wechat_redirect ,就打不开,不知道为何,社区也没有找到什么结果
+						this.url=`${pcBaseUrl}${mapObj.get(decodeURIComponent(options.xcxPath))||'/'}?${paramsObjStr}${data.environment=='wxwork'?'':'#wechat_redirect'}`
+                        // console.log(`${pcBaseUrl}${mapObj.get(decodeURIComponent(options.xcxPath))||'/'}?${paramsObjStr}${data.environment=='wxwork'?'':'#wechat_redirect'}`);
+					},
+					fail: (err) => {
+						this.url=`${pcBaseUrl}${mapObj.get(decodeURIComponent(options.xcxPath))||'/'}?${paramsObjStr}#wechat_redirect`
+						console.log(err);
+					}
+			})
+            
         }
     },
 

+ 43 - 3
pages/roadShow/video/list.vue

@@ -36,6 +36,12 @@
                     <text class="tag">{{item.chart_permission_name}}</text>
                     <text class="title">{{item.title}}</text>
                 </view>
+                <collectBox
+                    :type="3"
+                    :primaryId="item.road_video_id"
+                    :collectId="item.collection_id"
+                    class="share-btn collect-btn"
+                />
                 <button 
                     class="share-btn" 
                     open-type="share" 
@@ -50,11 +56,14 @@
                     autoplay
                     object-fit="contain"
                     show-mute-btn
+                    show-background-playback-button
                     :poster="item.cover_img_url"
                     :src="item.video_url"
                     enable-play-gesture
                     :id="item.road_video_id"
                     @ended="handleVideoEnd"
+                    @pause="handleVideoPause"
+                    @timeupdate="handleTimeUpdate"
                     v-if="item.road_video_id==curVideoId"
                 ></video>
                 <image @click="handelClickPlay(item)" v-else class="poster" :src="item.cover_img_url" mode="aspectFill" lazy-load/>
@@ -116,15 +125,17 @@
 </template>
 <script>
 import {apiRoadShowVideoList,apiRoadShowVideoPlayLog} from '@/api/roadShow'
-import {apiGetSceneToParams,apiUserBindPermission} from '@/api/common'
+import {apiGetSceneToParams,apiUserBindPermission,apiViewLogUpdate} from '@/api/common'
 import noAuth from './components/noAuth.vue'
 import dragButton from '@/components/dragButton/dragButton.vue'
 import comment from '@/components/videoComment/comment.vue'
+import collectBox from '@/components/collectBox/collectBox.vue'
 export default {
     components:{
         noAuth,
         dragButton,
-        comment
+        comment,
+        collectBox
     },
     data() {
         return {
@@ -141,6 +152,8 @@ export default {
 
             curVideoId:0,
             curVideoIns:null,
+            curVideoTime:0,
+            videoRecordId:0,
 
             isAuth:true,
             noAuthData:null,
@@ -295,6 +308,7 @@ export default {
             apiRoadShowVideoPlayLog({video_id:Number(item.road_video_id)}).then(res=>{
                 if(res.code===200){
                     console.log('视频埋点成功');
+                    this.videoRecordId=res.data
                 }
             })
         },
@@ -307,6 +321,29 @@ export default {
                 this.curVideoId=0
                 this.curVideoIns=null
             }, 200);
+        },
+
+        //时长变化
+        handleTimeUpdate(e){
+            // console.log(this.curVideoId,e.detail.currentTime);
+            this.curVideoTime=e.detail.currentTime
+        },
+
+        handleVideoPause(){
+            // console.log(`视频 pause---${this.videoRecordId}----${this.curVideoTime}`);
+            this.handleUpdateVideoPlayTime()
+        },
+
+        // 更新播放时长
+        handleUpdateVideoPlayTime(){
+            if(!this.videoRecordId) return
+            apiViewLogUpdate({
+                id:this.videoRecordId,
+                stop_seconds:parseInt(this.curVideoTime),
+                source:4
+            }).then(res=>{
+                console.log('更新播放时长成功');
+            })
         }
     },
 }
@@ -355,7 +392,7 @@ export default {
             padding: 30rpx 34rpx;
             position: relative;
             .title-box{
-                padding-right: 40rpx;
+                margin-right: 110rpx;
             }
             .share-btn{
                 position: absolute;
@@ -374,6 +411,9 @@ export default {
                 width: 32.5rpx;
                 height: 32rpx;
             }
+            .collect-btn{
+                right: 96rpx;
+            }
             .tag{
                 color: #E4B478;
                 background-color: #333;

+ 13 - 6
pages/user/user.vue

@@ -47,17 +47,15 @@
 				</block>
 				
 				<view v-else class="right-text look" @click="handleToUserPermission">
-					<text>查看</text>
+					<!-- <text>查看</text> -->
 					<van-icon name="arrow"></van-icon>
 				</view>
 			</view>
 			<view class="flex item-card" >
 				<image src="@/static/message_ico.png" mode="widthFix" />
-				<view class="label flex" style="align-items: center;">消息通知 
-					<text class="unread-ico" v-if="userInfo.un_read">{{userInfo.un_read}}</text> 
-				</view>
+				<view class="label flex" style="align-items: center;">消息通知</view>
 				<view class="right-text look" @click="toMessageHadle">
-					<text>查看</text>
+					<text class="unread-ico" v-if="userInfo.un_read">{{userInfo.un_read}}</text> 
 					<van-icon name="arrow"></van-icon>
 				</view>
 			</view>
@@ -77,7 +75,16 @@
 					<image src="../../static/voice/mine-voice-icon.png" mode="widthFix" />
 					<text class="label">我的语音</text>
 					<view class="right-text look">
-						<text>查看</text>
+						<van-icon name="arrow"></van-icon>
+					</view>
+				</view>
+				<view></view>
+			</navigator>
+			<navigator url="/pages-user/myCollect">
+				<view class="flex item-card">
+					<image src="../../static/collect-user-icon.png" mode="widthFix" />
+					<text class="label">我的收藏</text>
+					<view class="right-text look">
 						<van-icon name="arrow"></van-icon>
 					</view>
 				</view>

+ 43 - 3
pages/video/videoList.vue

@@ -32,6 +32,12 @@
                     <text class="tag">{{item.variety_tag_name}}</text>
                     <text class="title">{{item.title}}</text>
                 </view>
+                <collectBox
+                    :type="2"
+                    :primaryId="item.community_video_id"
+                    :collectId="item.collection_id"
+                    class="share-btn collect-btn"
+                />
                 <button 
                     class="share-btn" 
                     open-type="share" 
@@ -43,11 +49,14 @@
                     autoplay
                     object-fit="contain"
                     show-mute-btn
+                    show-background-playback-button
                     :poster="item.cover_img_url"
                     :src="item.video_url"
                     enable-play-gesture
                     :id="item.community_video_id"
                     @ended="handleVideoEnd"
+                    @pause="handleVideoPause"
+                    @timeupdate="handleTimeUpdate"
                     v-if="item.community_video_id==curVideoId"
                 ></video>
                 <image @click="handelClickPlay(item)" v-else class="poster" :src="item.cover_img_url" mode="aspectFill" lazy-load/>
@@ -110,15 +119,17 @@
 <script>
 import {apiVideoList,apiVideoPlayLog} from '@/api/video'
 import {apiOptionList} from '@/api/question'
-import {apiGetSceneToParams,apiGetTagTree} from '@/api/common'
+import {apiGetSceneToParams,apiGetTagTree,apiViewLogUpdate} from '@/api/common'
 import noAuth from './components/noAuth.vue'
 import dragButton from '@/components/dragButton/dragButton.vue'
 import comment from '@/components/videoComment/comment.vue'
+import collectBox from '@/components/collectBox/collectBox.vue'
 export default {
     components:{
         noAuth,
         comment,
-        dragButton
+        dragButton,
+        collectBox
     },
     data() {
         return {
@@ -135,6 +146,8 @@ export default {
 
             curVideoId:0,
             curVideoIns:null,
+            curVideoTime:0,
+            videoRecordId:0,
 
             isAuth:true,
             noAuthData:null,
@@ -285,6 +298,7 @@ export default {
             apiVideoPlayLog({video_id:Number(item.community_video_id)}).then(res=>{
                 if(res.code===200){
                     console.log('视频埋点成功');
+                    this.videoRecordId=res.data
                 }
             })
         },
@@ -297,6 +311,29 @@ export default {
                 this.curVideoId=0
                 this.curVideoIns=null
             }, 200);
+        },
+
+         //时长变化
+        handleTimeUpdate(e){
+            // console.log(this.curVideoId,e.detail.currentTime);
+            this.curVideoTime=e.detail.currentTime
+        },
+
+        handleVideoPause(){
+            // console.log(`视频 pause---${this.videoRecordId}----${this.curVideoTime}`);
+            this.handleUpdateVideoPlayTime()
+        },
+
+        // 更新播放时长
+        handleUpdateVideoPlayTime(){
+            if(!this.videoRecordId) return
+            apiViewLogUpdate({
+                id:this.videoRecordId,
+                stop_seconds:parseInt(this.curVideoTime),
+                source:3
+            }).then(res=>{
+                console.log('更新播放时长成功');
+            })
         }
     },
 }
@@ -344,7 +381,7 @@ export default {
             padding: 30rpx 34rpx;
             position: relative;
             .title-box{
-                padding-right: 40rpx;
+                margin-right: 110rpx;
             }
             .share-btn{
                 position: absolute;
@@ -363,6 +400,9 @@ export default {
                 width: 32.5rpx;
                 height: 32rpx;
             }
+            .collect-btn{
+                right: 96rpx;
+            }
             .tag{
                 color: #E4B478;
                 background-color: #333;

+ 44 - 3
pages/video/videoSearch.vue

@@ -23,6 +23,12 @@
                     <text class="tag">{{item.chart_permission_name}}</text>
                     <text class="title">{{item.title}}</text>
                 </view>
+                <collectBox
+                    :type="2"
+                    :primaryId="item.community_video_id"
+                    :collectId="item.collection_id"
+                    class="share-btn collect-btn"
+                />
                 <button 
                     class="share-btn" 
                     open-type="share" 
@@ -34,11 +40,14 @@
                     autoplay
                     object-fit="contain"
                     show-mute-btn
+                    show-background-playback-button
                     :poster="item.cover_img_url"
                     :src="item.video_url"
                     enable-play-gesture
                     :id="item.community_video_id"
                     @ended="handleVideoEnd"
+                    @pause="handleVideoPause"
+                    @timeupdate="handleTimeUpdate"
                     v-if="item.community_video_id==curVideoId"
                 ></video>
                 <image @click="handelClickPlay(item)" v-else class="poster" :src="item.cover_img_url" mode="aspectFill" lazy-load/>
@@ -51,11 +60,14 @@
 <script>
 import searchBox from '@/components/searchBox/searchBox.vue'
 import {apiVideoList,apiVideoPlayLog} from '@/api/video'
+import {apiViewLogUpdate} from '@/api/common'
 import comment from '@/components/videoComment/comment.vue'
+import collectBox from '@/components/collectBox/collectBox.vue'
 export default {
     components: {
         searchBox,
-        comment
+        comment,
+        collectBox
     },
     data() {
         return {
@@ -67,7 +79,9 @@ export default {
             pageSize:10,
 
             curVideoId:0,
-            curVideoIns:null
+            curVideoIns:null,
+            curVideoTime:0,
+            videoRecordId:0,
         }
     },
     onReachBottom() {
@@ -140,6 +154,7 @@ export default {
             apiVideoPlayLog({video_id:Number(item.community_video_id)}).then(res=>{
                 if(res.code===200){
                     console.log('视频埋点成功');
+                    this.videoRecordId=res.data
                 }
             })
         },
@@ -152,6 +167,29 @@ export default {
                 this.curVideoId=0
                 this.curVideoIns=null
             }, 200);
+        },
+
+        //时长变化
+        handleTimeUpdate(e){
+            // console.log(this.curVideoId,e.detail.currentTime);
+            this.curVideoTime=e.detail.currentTime
+        },
+
+        handleVideoPause(){
+            // console.log(`视频 pause---${this.videoRecordId}----${this.curVideoTime}`);
+            this.handleUpdateVideoPlayTime()
+        },
+
+        // 更新播放时长
+        handleUpdateVideoPlayTime(){
+            if(!this.videoRecordId) return
+            apiViewLogUpdate({
+                id:this.videoRecordId,
+                stop_seconds:parseInt(this.curVideoTime),
+                source:3
+            }).then(res=>{
+                console.log('更新播放时长成功');
+            })
         }
     },
 }
@@ -186,7 +224,7 @@ page{
             padding: 30rpx 34rpx;
             position: relative;
             .title-box{
-                padding-right: 40rpx;
+                margin-right: 110rpx;
             }
             .share-btn{
                 position: absolute;
@@ -205,6 +243,9 @@ page{
                 width: 32rpx;
                 height: 32rpx;
             }
+            .collect-btn{
+                right: 96rpx;
+            }
             .tag{
                 color: #E4B478;
                 background-color: #333;

+ 59 - 64
pages/voice/voice.vue

@@ -23,7 +23,6 @@
             </van-tabs>
         </view>
           
-          
         <view class="empty-box" v-if="list.length==0&&finished">
             <image
                 :src="globalImgUrls.activityNoAuth"
@@ -42,20 +41,24 @@
                     />
                     <text>{{item.VoicePlaySeconds|formatVoiceTime}}</text>
                 </view>
-                <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)" v-if="item.IsAuthor">
-                    <image class="del-img" src="@/static/voice/del.png" mode="widthFix"/>
-                </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>
+                <view class="opt-box">
+                    <image style="width:34rpx;height:34rpx" src="@/static/voice/publish.png" mode="widthFix" @click.stop="handleSendMsgItem(item)" v-if="item.CouldSendMsg"/>
+                    <image style="width:34rpx;height:34rpx" src="@/static/voice/del.png" mode="widthFix" v-if="item.IsAuthor" @click.stop="handleDelItem(item)" />
+                    <sharePoster 
+                        :showSlot="true"
+                        :shareData="getItemShareData(item)"
+                    >
+                        <image style="width:32rpx;height:32rpx" src="@/static/voice/creat-poster-icon.png" mode="aspectFill" />
+                    </sharePoster>
+                    <button 
+                        class="share-btn" 
+                        open-type="share" 
+                        :data-item="item"
+                        @click.stop=""
+                    >
+                        <image class="share-img" src="@/static/share-icon.png" mode="aspectFill"/>
+                    </button>
+                </view>
             </view>
         </view>
 
@@ -115,12 +118,14 @@ import {apiGetSceneToParams} from '@/api/common'
 import noAuth from './components/noAuth.vue'
 import audioBox from '@/components/audioBox/audioBox.vue'
 import dragButton from '@/components/dragButton/dragButton.vue'
+import sharePoster from '@/components/sharePoster/sharePoster.vue'
 const moment=require('@/utils/moment-with-locales.min')
 export default {
     components:{
         noAuth,
         audioBox,
-        dragButton
+        dragButton,
+        sharePoster
     },
     filters:{
         formatTime(e){
@@ -423,6 +428,20 @@ export default {
             })
             if(res.code===200){
                 console.log('上报音频播放记录');
+                this.$store.commit('audio/addAudioRecordId',{recordId:res.data,source:2})
+            }
+        },
+
+        //语音详情生成海报参数
+        getItemShareData(item){
+            return {
+                type:'voice_detail',
+                code_page:'pages-voice/voiceDetail',
+                code_scene:JSON.stringify({voiceId:item.BroadcastId}),
+                data:{
+                    title:item.BroadcastName,
+                    img:item.ImgUrl
+                }
             }
         }
     },
@@ -503,58 +522,34 @@ 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;
+        .opt-box{
+            position: relative;
+            float: right;
+            bottom: 40rpx;
+            display: flex;
+            image{
+                margin-left: 40rpx;
             }
-        }
-        .publish-img{
-            width: 34rpx;
-            height: 34rpx;
-        }
-        .del-btn{
-            position: absolute;
-            right: 96rpx;
-            bottom: 30rpx;
-            width: 36rpx;
-            height: 36rpx;
-            background-color: transparent;
-            line-height: 1;
-            padding: 0;
-            &::after{
-                border: none;
+            .share-btn{
+                background-color: transparent;
+                width: 36rpx;
+                height: 36rpx;
+                line-height: 1;
+                padding: 0;
+                margin-left: 40rpx;
+                &::after{
+                    border: none;
+                }
+                .share-img{
+                    width: 32.5rpx;
+                    height: 32rpx;
+                    margin-left: 0;
+                }
             }
-        }
-        .del-img{
-            width: 34rpx;
-            height: 34rpx;
+            
         }
 
-        .share-btn{
-            position: absolute;
-            bottom: 30rpx;
-            right: 0rpx;
-            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;
         }

BIN
static/collect-s.png


BIN
static/collect-user-icon.png


BIN
static/collect.png


BIN
static/voice/creat-poster-icon.png


+ 7 - 0
store/modules/audio.js

@@ -11,6 +11,8 @@ const audioModules={
         questionId:0,//当前是哪个问答的音频
         paused:true,//当前是否音频正在播放 true暂停状态
         curTime:0,//当前正在播放的音频播放的时间
+        recordId:0,//播放记录id
+        lastType:0,//上次播放的是那种的音频,用于更新媒体播放记录时长中的source
     },
     mutations: {
         addAudio(state,e){
@@ -51,6 +53,11 @@ const audioModules={
         // 关闭弹窗
         closePopAudio(state){
             state.show=false
+        },
+        // 设置播放记录id
+        addAudioRecordId(state,e){
+            state.recordId=e.recordId
+            state.lastType=e.source
         }
     }
 }