Browse Source

完成音频

jwyu 3 years ago
parent
commit
94bdbbac06

+ 1 - 0
index.html

@@ -4,6 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="icon" href="/favicon.ico" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
     <title>弘则研报</title>
   </head>
   <body>

+ 17 - 0
src/api/report.js

@@ -0,0 +1,17 @@
+/**
+ * 报告模块
+ */
+import {get,post} from './http'
+
+// 获取报告详情
+//research_report_id
+export const apiGetReportDetail=(params)=>{
+    return get('/report/research_report',params)
+}
+
+//获取周度报告详情
+//research_report_type_id
+export const apiGetChapterReportDetail=params=>{
+    return get('/report/research_report_chapter',params)
+}
+

BIN
src/assets/audio-before-grey.png


BIN
src/assets/audio-doing-white.png


BIN
src/assets/audio-doing.png


BIN
src/assets/audio-pause-3.png


+ 46 - 7
src/layout/Index.vue

@@ -57,6 +57,22 @@ const goBack=()=>{
   router.go(-1)
 }
 
+
+// 音频切换
+const handleAudioChange=(e)=>{
+  store.commit('changeAudio',e)
+}
+// 关闭音频
+const handleAudioClose=()=>{
+  store.commit('closeAudio')
+}
+//音频状态改变
+const handleAudioStatus=(e)=>{
+  store.commit('audioStatusChange',e)
+}
+const globalAudioIns=ref(null)
+store.state.audioData.INS=globalAudioIns
+
 </script>
 
 <template>
@@ -122,8 +138,24 @@ const goBack=()=>{
     </el-container>
 
     <!-- 全局音频模块 -->
-    <div class="global-audio-box">
-      <audio controls src="https://hongze.oss-accelerate.aliyuncs.com/static/audio/202201/20220113/lEKsJ16hRaasrQuNQX7TQC4YmKzC.mp3"></audio>
+    <div class="global-audio-box" v-if="$store.state.audioData.list.length>0">
+      <div style="height:10px;cursor: move;">
+        <svg @click="handleAudioClose" class="icon" width="20" height="20" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-042ca774=""><path fill="currentColor" d="M764.288 214.592L512 466.88 259.712 214.592a31.936 31.936 0 00-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1045.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0045.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 10-45.12-45.184z"></path></svg>
+      </div>
+      <h2 style="text-align:center;font-size:14px;padding:0 20px">{{$store.state.audioData.list[$store.state.audioData.index].voiceName}}</h2>
+      <audio 
+        controls 
+        autoplay 
+        @ended="handleAudioChange('auto')" 
+        @pause="handleAudioStatus('paused')"
+        @play="handleAudioStatus('paly')"
+        :src="$store.state.audioData.list[$store.state.audioData.index].voiceUrl"
+        ref="globalAudioIns"
+      ></audio>
+      <div style="text-align:center;margin:4px 0 10px 0">
+        <img src="@/assets/audio-before-grey.png" alt="" style="width:30px;margin-right:20px;cursor: pointer;" @click="handleAudioChange('before')">
+        <img src="@/assets/audio-before-grey.png" alt="" style="width:30px;transform: rotate(180deg);cursor: pointer;" @click="handleAudioChange('next')">
+      </div>
     </div>
     
   </div>
@@ -145,8 +177,8 @@ const goBack=()=>{
   margin-right: auto;
   position: relative;
   .back-icon{
-    position: absolute;
-    left: 14px;
+    position: fixed;
+    left: 180px;
     top: 23px;
     width: 30px;
     height: 30px;
@@ -159,6 +191,7 @@ const goBack=()=>{
   width: 186px;
   padding: 20px;
   text-align: center;
+  flex-shrink: 0;
   .icon-scan {
     width: 26px;
     height: 26px;
@@ -188,8 +221,8 @@ const goBack=()=>{
 
 .global-audio-box{
   position: fixed;
-  width: 624px;
-  height: 59px;
+  width: 485px;
+  min-height: 59px;
   background: #FFFFFF;
   box-shadow: 0px 3px 12px 0px rgba(81, 88, 101, 0.16);
   border-radius: 8px;
@@ -198,9 +231,15 @@ const goBack=()=>{
   bottom: 30px;
   transform: translateX(-50%);
   z-index: 999;
+  .icon{
+    float: right;
+    position: relative;
+    right: 10px;
+    cursor: pointer;
+  }
   audio{
     width: 90%;
-    height: 40px;
+    height: 30px;
     display: block;
     margin-left: auto;
     margin-right: auto;

+ 29 - 0
src/router/index.js

@@ -38,6 +38,35 @@ const routes=[
       }
     ]
   },
+  {
+    path:'/report',
+    name:'Report',
+    redirect: '/report/detail',
+    component: () => import("@/layout/Index.vue"),
+    meta: {
+      title:"报告"
+    },
+    children:[
+      {
+        path: "detail",
+        name: "ReportDetail",
+        component: () => import("@/views/report/Detail.vue"),
+        meta: {
+          title: "报告详情",
+          hasBack:true
+        }
+      },
+      {
+        path: "chapterdetail",
+        name: "ChapterReportDetail",
+        component: () => import("@/views/report/ChapterDetail.vue"),
+        meta: {
+          title: "报告详情",
+          hasBack:true
+        }
+      }
+    ]
+  }
 
 ]
 

+ 52 - 0
src/store/index.js

@@ -1,10 +1,18 @@
 import { createStore } from "vuex";
+import {ElMessage} from 'element-plus'
 import {apiUserInfo} from '@/api/user.js'
 const token=localStorage.getItem('token')||''
 export default createStore({
   state: {
     token:token,
     userInfo:null,
+    audioData:{
+      INS:null,
+      list:[],//音频数据
+      index:0,//当前播放第几个
+      activityId:0,//活动的id
+      paused:false,//当前音频播放状态
+    }
   },
   mutations: {
     // 获取token
@@ -16,6 +24,50 @@ export default createStore({
     // 获取个人信息
     setUserInfo(state,e){
       state.userInfo=e
+    },
+
+    // 设置音频播放
+    addAudio(state,e){
+      state.audioData.list=e.list
+      state.audioData.activityId=e.activityId
+      state.audioData.index=e.index
+    },
+    //切换音频
+    changeAudio(state,e){
+      if(e==='before'){//点击按钮切换上一首
+        if(state.audioData.index===0){
+          ElMessage('当前为第一首')
+        }else{
+          state.audioData.index--
+        }
+      }else if(e==='next'){//点击按钮切换下一首
+        if(state.audioData.index==state.audioData.list.length-1){
+          ElMessage('当前为最后一首')
+        }else{
+          state.audioData.index++
+        }
+      }else if(e==='auto'){//自动播放切换下一首
+        if(state.audioData.index==state.audioData.list.length-1){
+          // ElMessage('当前为最后一首')
+        }else{
+          state.audioData.index++
+        }
+      }
+    },
+    // 关闭音频
+    closeAudio(state,e){
+      state.audioData.list=[]
+      state.audioData.index=0
+      state.audioData.activityId=0
+      state.audioData.INS=null
+    },
+    //音频状态切换
+    audioStatusChange(state,e){
+      if(e=='paused'){
+        state.audioData.paused=true
+      }else{
+        state.audioData.paused=false
+      }
     }
   },
   actions: {

+ 46 - 7
src/views/activity/Detail.vue

@@ -1,10 +1,10 @@
 <script setup>
-window.scrollTo=0
-import { ref } from "vue";
-import { useRoute } from "vue-router";
+import { computed, ref } from "vue";
+import { useRoute, useRouter } from "vue-router";
 import { formatActivityTime } from "./utils";
 
 const route = useRoute();
+const router=useRouter()
 
 // 获取详情
 import { apiActivityDetail } from "@/api/activity.js";
@@ -82,6 +82,38 @@ const formatAudioTime=(e)=>{
     let sec=e%60
     return `${minus>9?minus:'0'+minus}分${sec>9?sec:'0'+sec}秒`
 }
+
+// 跳转报告详情
+const goDetail=()=>{
+    let params=info.value.reportLink.split('?')[1]
+    router.push(`/report/detail?${params}`)
+}
+
+
+import { useStore } from "vuex";
+const store=useStore()
+const curAudiourl=computed(()=>{
+    return store.state.audioData.list[store.state.audioData.index]&&store.state.audioData.list[store.state.audioData.index].voiceUrl
+})
+const aduioIconDefault=new URL('../../assets/audio-pause-2.png', import.meta.url).href
+const aduioIconPlay=new URL('../../assets/audio-doing.png', import.meta.url).href
+const aduioIconPause=new URL('../../assets/audio-pause-3.png', import.meta.url).href
+const handlePlayAudio=(index)=>{
+    if(store.state.audioData.list.length>0){
+        if(store.state.audioData.index==index){
+            if(store.state.audioData.paused){
+                store.state.audioData.INS.play()
+            }else{
+                store.state.audioData.INS.pause()
+            }
+        }else{
+            store.commit('addAudio',{list:audioList.value,activityId:route.query.activityId,index:index})
+        }
+    }else{
+        store.commit('addAudio',{list:audioList.value,activityId:route.query.activityId,index:index})
+    }
+    
+}
 </script>
 
 <template>
@@ -99,9 +131,13 @@ const formatAudioTime=(e)=>{
             <div class="con">
                 <!-- 音频模块 -->
                 <div class="audio-wrap" v-if="audioList.length>0">
-                    <div class="flex audio-item" v-for="item in audioList" :key="item.voiceUrl">
-                        <img src="@/assets/audio-pause-2.png" alt="">
-                        <div class="name">{{item.voiceName}}</div>
+                    <div 
+                        class="flex audio-item" 
+                        v-for="(item,index) in audioList" 
+                        :key="item.voiceUrl"
+                    >
+                        <img @click="handlePlayAudio(index)" :src="curAudiourl==item.voiceUrl?$store.state.audioData.paused?aduioIconPause:aduioIconPlay:aduioIconDefault" alt="">
+                        <div :class="['name',curAudiourl==item.voiceUrl&&'name-active']">{{item.voiceName}}</div>
                         <span class="time">{{formatAudioTime(item.voicePlaySeconds)}}</span>
                     </div>
                 </div>
@@ -124,7 +160,7 @@ const formatAudioTime=(e)=>{
                     </div>
                 </div>
                 <div class="btns">
-                    <div class="btn active" v-if="info.reportLink">查看相关报告</div>
+                    <div class="btn active" v-if="info.reportLink" @click="goDetail">查看相关报告</div>
                     <block v-if="info.activityState === 1">
                     <div class="btn" v-if="info.firstActivityTypeId === 3">
                         {{
@@ -241,6 +277,9 @@ const formatAudioTime=(e)=>{
                 flex: 1;
                 margin: 0 10px;
             }
+            .name-active{
+                color: #DAB37C;
+            }
             .time{
                 color: #B6B6B6;
             }

+ 45 - 3
src/views/activity/List.vue

@@ -143,6 +143,25 @@ const cancelRemind=async (item)=>{
 }
 
 
+// 获取音频
+import { useStore } from "vuex";
+const store = useStore();
+import {apiActivityAudios} from '@/api/activity'
+const handleGetAudio=async (item)=>{
+    if(store.state.audioData.list.length>0&&store.state.audioData.activityId==item.activityId){
+        if(store.state.audioData.paused){
+            store.state.audioData.INS.play()
+        }else{
+            store.state.audioData.INS.pause()
+        }
+        return
+    }
+    const res=await apiActivityAudios({activity_id:Number(item.activityId)})
+    if(res.code===200){
+        store.commit('addAudio',{list:res.data,activityId:item.activityId,index:0})
+    }
+}
+
 
 
 // 记录滚动条
@@ -156,6 +175,10 @@ let scrollTopNum=ref(0)
 const listWrapScroll=(e)=>{
     scrollTopNum.value=e.srcElement.scrollTop
 }
+
+const aduioIconPlay=new URL('../../assets/audio-doing-white.png', import.meta.url).href
+const aduioIconPaused=new URL('../../assets/audio-pause.png', import.meta.url).href
+const aduioIconDefault=new URL('../../assets/audio-pause-3.png', import.meta.url).href
 </script>
 
 
@@ -205,9 +228,18 @@ const listWrapScroll=(e)=>{
                                 >{{item.registerState?'取消线下报名':'报名线下参会'}}</span>
                             </block>
                             <!-- 音频播放 -->
-                            <div class="btn active" v-if="item.firstActivityTypeId===1&&item.activityState===3&&item.hasPlayBack">
-                                <img class="icon-audio" src="../../assets/audio-pause.png" />
-                                <span style="vertical-align: middle;line-height: 1">回放</span>
+                            <div 
+                                :class="['btn btn-audio',$store.state.audioData.activityId==item.activityId&&'btn-audio-active']" 
+                                v-if="item.firstActivityTypeId===1&&item.activityState===3&&item.hasPlayBack"
+                                @click.stop="handleGetAudio(item)"
+                            >
+                                <img 
+                                    class="icon-audio" 
+                                    :src="$store.state.audioData.activityId==item.activityId?$store.state.audioData.paused?aduioIconPaused:aduioIconPlay:aduioIconDefault" 
+                                />
+                                <span style="vertical-align: middle;line-height: 1">{{
+                                    $store.state.audioData.activityId==item.activityId?$store.state.audioData.paused?'已暂停':'播放中':'回放'
+                                }}</span>
                             </div>
                         </div>
                     </div>
@@ -332,6 +364,16 @@ const listWrapScroll=(e)=>{
                         background: #DAB37C;
                         color: #fff;
                     }
+                    .btn-audio{
+                        border: 1px solid #DAB37C;
+                        color: #DAB37C;
+                        background-color: #fff;
+                    }
+                    .btn-audio-active{
+                        background: #DAB37C;
+                        color: #fff;
+                        border: none;
+                    }
                     .icon-audio{
                         width: 17px;
                         height: 17px;

+ 207 - 0
src/views/report/ChapterDetail.vue

@@ -0,0 +1,207 @@
+<script setup>
+import moment from 'moment'
+import {ref,onMounted} from 'vue'
+import { useRoute } from 'vue-router'
+const route=useRoute()
+
+import {apiGetChapterReportDetail,apiGetReportDetail} from '@/api/report'
+let research_report_type_id=ref(route.query.research_report_type_id)
+let info=ref(null)
+const getInfo=async ()=>{
+    const res=await apiGetChapterReportDetail({research_report_type_id:Number(research_report_type_id.value)})
+    if(res.code===200){
+        info.value=res.data
+    }
+}
+getInfo()
+
+//点击切换章节
+const chapterTypeChange=(item)=>{
+    research_report_type_id.value=item.ResearchReportTypeId
+    info.value=null
+    getInfo()
+}
+
+// 获取底部列表
+let botList=ref([])
+const getBotList=async ()=>{
+    const res=await apiGetReportDetail({research_report_id:Number(route.query.research_report_id)})
+    if(res.code===200){
+        botList.value=res.data.research_report_type_list
+    }
+}
+getBotList()
+
+
+
+let preViewImgs=ref([])
+let preViewImgIndex=ref(0)
+let showPreViewImg=ref(false)
+
+onMounted(()=>{
+    $(document).on('click', '.content-wrap img',function(event) {
+	    let imgArray = [];
+	    let curImageSrc = $(this).attr('src');
+		let oParent = $(this).parent();
+		if (curImageSrc && !oParent.attr('href')) {
+            if(preViewImgs.value.length===0){
+                $('.content-wrap img').each(function(index, el) {
+                    let itemSrc = $(this).attr('src');
+                    imgArray.push(itemSrc);
+                });
+                preViewImgs.value=imgArray
+            }
+            preViewImgIndex.value=preViewImgs.value.indexOf(curImageSrc)||0
+            showPreViewImg.value=true
+	    }
+    })
+})
+
+// 免责声明
+let disclaimers=ref(false)
+</script>
+
+
+<template>
+    <div class="report-detail-page" v-if="info">
+        <!-- 章节详情 -->
+        <div class="other-type-wrap">
+            <div class="section top-box">
+                <h1 class="title">【{{info.research_report_type_info.research_report_type_title}}】</h1>
+                <div class="flag-box" style="margin:14px 0">
+                    <img style="width:18px;vertical-align: middle;display: inline-block;" :src="info.research_report_type_info.banner_url" alt="">
+                    <span style="vertical-align: middle;margin:0 13px 0 6px;display: inline-block;">{{info.research_report_type_info.report_chapter_type_name}}</span>
+                    <span style="vertical-align: middle;color:#999999;display: inline-block;">弘则研究</span>
+                </div>
+                <p><span>注:请务必阅读</span><span style="margin-left:5px;color:#DAB37C;cursor: pointer;" @click="disclaimers=true">免责声明</span></p>
+            </div>
+            <div class="section content-wrap">
+                <div class="item" v-for="item in info.research_report_type_content_list" :key="item.sort">
+                    <h2 class="content-title">{{item.content_type?item.content_type:'核心观点'}}</h2>
+                    <div v-html="item.content" class="content-text" @click="clickContent"></div>
+                </div>
+            </div>
+            <!-- 种类快捷切换 -->
+            <div class="section classify-list">
+                <div style="font-size:16px;color:#999">更多</div>
+                <ul class="flex list">
+                    <li 
+                        :class="['item',item.ResearchReportTypeId==research_report_type_id&&'active']" 
+                        v-for="item in botList" 
+                        :key="item.ResearchReportTypeId"
+                        @click="chapterTypeChange(item)"
+                    >
+                        <img :src="item.BannerUrl" alt="">
+                        <span>{{item.ReportChapterTypeName}}</span>
+                    </li>
+                </ul>
+            </div>
+        </div>
+        
+    </div>
+
+    <!-- 图片预览 -->
+    <el-image-viewer
+        v-if="showPreViewImg"
+        :initial-index="preViewImgIndex"
+        @close="showPreViewImg=false"
+        :url-list="preViewImgs"
+    />
+
+    <!-- 免责声明 -->
+    <el-dialog
+        v-model="disclaimers"
+        title="免责声明"
+        width="40%"
+        draggable
+        center
+    >
+        <div>
+            <p>
+                1、本报告仅供弘则弥道(上海)投资咨询有限公司正式签约的机构客户使用,不会仅因接收人/接受机构收到本报告而将其视为客户。
+            </p>
+            <p>
+                2、本报告根据国际和行业通行的准则,以合法渠道获得这些信息,尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性,也不保证本报告所包含的信息或建议在本报告发出后不会发生任何变更。本报告中所提供的信息仅供参考。
+            </p>
+            <p>
+                3、报告中的内容不对投资者做出的最终操作建议做任何的担保,也没有任何形式的分享投资收益或者分担投资损失的书面或口头承诺。不作为客户在投资、法律、会计或税务等方面的最终操作建议,也不作为道义的、责任的和法律的依据或者凭证,无论是否已经明示或者暗示。
+            </p>
+            <p>
+                4、在任何情况下,本公司不对客户/接受人/接受机构因使用报告中内容所引致的一切损失负责任,客户/接受人/接受机构需自行承担全部风险。
+            </p>
+        </div>
+    </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+.report-detail-page{
+    border: 1px solid #EBEBEB;
+    border-radius: 4px;
+    margin-top: 30px;
+    .section{
+        padding: 20px;
+    }
+}
+.other-type-wrap{
+    .top-box{
+        border-bottom: 1px dashed #EBEBEB;
+        .title{
+            font-size: 20px;
+            margin: 0;
+        }
+    }
+    .content-wrap{
+        border-bottom: 1px dashed #EBEBEB;
+        .item{
+            .content-title{
+                font-size: 16px;
+                color: #000;
+                &::before{
+                    content: '';
+                    display: inline-block;
+                    margin-right: 5px;
+                    position: relative;
+                    top: 4px;
+                    width: 6px;
+                    height: 20px;
+                    background: #DAB37C;
+                }
+            }
+            .content-text{
+                font-size: 14px;
+                :deep(img){
+                    max-width: 100%;
+                }
+            }
+        }
+
+    }
+}
+.classify-list{
+    .list{
+        margin-top: 20px;
+        flex-wrap: wrap;
+        .item{
+            padding: 7px 8px;
+            background-color: #F7F7F7;
+            border-radius: 4px;
+            margin-right: 12px;
+            margin-bottom: 12px;
+            cursor: pointer;
+            img{
+                width: 20px;
+                height: 20px;
+                vertical-align: middle;
+            }
+            span{
+                vertical-align: middle;
+                margin-left: 6px;
+                font-size: 16px;
+            }
+        }
+        .active{
+            border:1px solid #DAB37C;
+        }
+    }
+}
+</style>

+ 306 - 0
src/views/report/Detail.vue

@@ -0,0 +1,306 @@
+<script setup>
+import moment from 'moment'
+import {ref,onMounted} from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+const route=useRoute()
+const router=useRouter()
+
+import {apiGetReportDetail} from '@/api/report'
+let info=ref(null)
+const getInfo=async ()=>{
+    const res=await apiGetReportDetail({research_report_id:Number(route.query.research_report_id)})
+    if(res.code===200){
+        info.value=res.data
+    }
+}
+getInfo()
+
+// 设置tag颜色
+const getTagColor=(str)=>{
+    if( str.includes('多')||str.includes('强')||str.includes('反弹') ){
+		return "#DF6051";
+	}else if( str.includes('空')||str.includes('调整') ){
+		return "#6FC5B4";
+	}else{
+		return "#009fe6";
+	}
+}
+
+
+let preViewImgs=ref([])
+let preViewImgIndex=ref(0)
+let showPreViewImg=ref(false)
+
+onMounted(()=>{
+    $(document).on('click', '.content-wrap img',function(event) {
+	    let imgArray = [];
+	    let curImageSrc = $(this).attr('src');
+		let oParent = $(this).parent();
+		if (curImageSrc && !oParent.attr('href')) {
+            if(preViewImgs.value.length===0){
+                $('.content-wrap img').each(function(index, el) {
+                    let itemSrc = $(this).attr('src');
+                    imgArray.push(itemSrc);
+                });
+                preViewImgs.value=imgArray
+            }
+            preViewImgIndex.value=preViewImgs.value.indexOf(curImageSrc)||0
+            showPreViewImg.value=true
+	    }
+    })
+})
+
+
+// 跳转章节详情
+const goChapterDetail=(item)=>{
+    router.push({
+        path:'/report/chapterdetail',
+        query:{
+            research_report_type_id:item.ResearchReportTypeId,
+            research_report_id:route.query.research_report_id
+        }
+    })
+}
+
+</script>
+
+
+<template>
+    <div class="report-detail-page" v-if="info">
+        <!-- 晨报、周报类型 -->
+        <div class="list-type-wrap" v-if="['day','week'].includes(info.research_report_info.type)">
+            <div class="section top-box">
+                <h1 class="type-text">{{info.research_report_info.type=='day'?'晨报':'周报'}}</h1>
+                <div class="flex title-box">
+                    <div class="left">
+                        <span style="font-size:16px;margin-right:20px">{{info.research_report_info.researchReportName.substring(info.research_report_info.researchReportName.indexOf('】')+1)}}</span>
+                        <span>第{{info.research_report_info.periods}}期</span>
+                    </div>
+                    <div class="time">{{moment(info.research_report_info.researchReportDate).format('YYYY-MM-DD')}}</div>
+                </div>
+            </div>
+            <div class="section list-wrap">
+                <ul class="list">
+                    <li class="flex item" v-for="item in info.research_report_type_list" :key="item.ResearchReportTypeId" @click="goChapterDetail(item)">
+                        <div class="flex img-box">
+                            <el-image
+                                class="img"
+                                :src="item.BannerUrl"
+                                fit="cover"
+                            ></el-image>
+                        </div>
+                        <div class="content-box">
+                            <div class="flex title">
+                                <h2>{{item.ReportChapterTypeName}}</h2>
+                                <div v-if="item.Trend" style="margin-left:10px">
+                                    <span
+                                        class="tag"
+                                        v-for="tag in item.Trend.split('&')" 
+                                        :key="tag"
+                                        :style="{backgroundColor:getTagColor(tag)}"
+                                    >{{tag}}</span>
+                                </div>
+                            </div>
+                            <div class="sub-title">
+                                <p>{{item.ResearchReportTypeTitle}}</p>
+                            </div>
+                            <div class="update-time" v-if="info.research_report_info.type=='day'">更新至:{{moment(item.LastUpdatedTime).format('YYYY-MM-DD')}}</div>
+                        </div>
+                    </li>
+                </ul>
+            </div>
+        </div>
+        <!-- 其他类型 -->
+        <div class="other-type-wrap" v-else>
+            <div class="section top-box">
+                <h1 class="title">【第{{info.research_report_info.periods}}期 | {{info.research_report_info.reportVariety}}】</h1>
+                <div class="flex sub-title">
+                    <div class="left">{{info.research_report_info.researchReportName.substring(info.research_report_info.researchReportName.indexOf('】')+1)}}</div>
+                    <span class="time">{{moment(info.research_report_info.researchReportDate).format('YYYY-MM-DD')}}</span>
+                </div>
+            </div>
+            <div class="section content-wrap">
+                <div class="item" v-for="item in info.ResearchReportTypeContentList" :key="item.sort">
+                    <h2 class="content-title">{{item.content_type?item.content_type:'核心观点'}}</h2>
+                    <div v-html="item.content" class="content-text" @click="clickContent"></div>
+                </div>
+            </div>
+        </div>
+        <!-- 免责声明 -->
+        <div class="disclaimers-wrap">
+            <h2>免责声明</h2>
+            <p>
+                1、本报告仅供弘则弥道(上海)投资咨询有限公司正式签约的机构客户使用,不会仅因接收人/接受机构收到本报告而将其视为客户。
+            </p>
+            <p>
+                2、本报告根据国际和行业通行的准则,以合法渠道获得这些信息,尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性,也不保证本报告所包含的信息或建议在本报告发出后不会发生任何变更。本报告中所提供的信息仅供参考。
+            </p>
+            <p>
+                3、报告中的内容不对投资者做出的最终操作建议做任何的担保,也没有任何形式的分享投资收益或者分担投资损失的书面或口头承诺。不作为客户在投资、法律、会计或税务等方面的最终操作建议,也不作为道义的、责任的和法律的依据或者凭证,无论是否已经明示或者暗示。
+            </p>
+            <p>
+                4、在任何情况下,本公司不对客户/接受人/接受机构因使用报告中内容所引致的一切损失负责任,客户/接受人/接受机构需自行承担全部风险。
+            </p>
+        </div>
+    </div>
+
+    <el-image-viewer
+        v-if="showPreViewImg"
+        :initial-index="preViewImgIndex"
+        @close="showPreViewImg=false"
+        :url-list="preViewImgs"
+    />
+</template>
+
+<style lang="scss" scoped>
+.report-detail-page{
+    border: 1px solid #EBEBEB;
+    border-radius: 4px;
+    margin-top: 30px;
+    .section{
+        padding: 20px;
+    }
+}
+.list-type-wrap{
+    .top-box{
+        border-bottom: 1px dashed #EBEBEB;
+        .type-text{
+            font-size: 16px;
+            margin: 0;
+        }
+        .title-box{
+            justify-content: space-between;
+            margin-top: 12px;
+            .left{
+                &::before{
+                    content: '';
+                    display: inline-block;
+                    width: 6px;
+                    height: 20px;
+                    background: #DAB37C;
+                    margin-right: 6px;
+                    position: relative;
+                    top: 4px;
+                }
+            }
+            .time{
+                color: #B6B6B6;
+                margin-left: 20px;
+                flex-shrink: 0;
+                position: relative;
+                top: 7px;
+            }
+        }
+    }
+    .list-wrap{
+        .item{
+            margin-bottom: 20px;
+            .img-box{
+                width: 70px;
+                height: 70px;
+                border: 1px solid #EBEBEB;
+                align-items: center;
+                justify-content: center;
+                border-radius: 4px;
+                margin-right: 20px;
+                flex-shrink: 0;
+                .img{
+                    width: 32px;
+                    height: 32px;
+                }
+            }
+            .content-box{
+                flex: 1;
+                .title{
+                    h2{
+                        margin: 0;
+                        font-size: 16px;
+                        cursor: pointer;
+                    }
+                    .tag{
+                        display: inline-block;
+                        background-color: #009fe6;
+                        color: #fff;
+                        font-size: 12px;
+                        padding: 4px 8px;
+                        line-height: 1;
+                        border-radius: 4px;
+                        margin-right: 5px;
+                    }
+                }
+                .update-time{
+                    float: right;
+                    color: #B6B6B6;
+                }
+            }
+        }
+    }
+}
+.other-type-wrap{
+    .top-box{
+        border-bottom: 1px dashed #EBEBEB;
+        .title{
+            font-size: 20px;
+            margin: 0;
+        }
+        .sub-title{
+            justify-content: space-between;
+            margin-top: 20px;
+            .left{
+                font-size: 16px;
+                &::before{
+                    content: '';
+                    display: inline-block;
+                    width: 6px;
+                    height: 20px;
+                    background: #DAB37C;
+                    margin-right: 6px;
+                    position: relative;
+                    top: 4px;
+                }
+            }
+            .time{
+                color: #B6B6B6;
+                margin-left: 20px;
+                flex-shrink: 0;
+                position: relative;
+                top: 7px;
+            }
+        }
+    }
+    .content-wrap{
+        .item{
+            .content-title{
+                font-size: 16px;
+                color: #000;
+                &::before{
+                    content: '';
+                    display: inline-block;
+                    margin-right: 5px;
+                    position: relative;
+                    top: 4px;
+                    width: 6px;
+                    height: 20px;
+                    background: #DAB37C;
+                }
+            }
+            .content-text{
+                font-size: 14px;
+                :deep(img){
+                    max-width: 100%;
+                }
+            }
+        }
+
+    }
+}
+.disclaimers-wrap{
+    border-top: 1px dashed #EBEBEB;
+    padding: 20px;
+    h2{
+        margin: 0;
+        text-align: center;
+        font-size: 16px;
+    }
+}
+</style>