|
@@ -0,0 +1,509 @@
|
|
|
+<script setup>
|
|
|
+import { ref } from 'vue'
|
|
|
+import moment from 'moment'
|
|
|
+import { useRoute, useRouter } from 'vue-router'
|
|
|
+import { apiEtaReport,apiEtaReportCollect } from '@/api/etaReport'
|
|
|
+import { useUserInfo } from '@/hooks/useUserInfo'
|
|
|
+import {apiSystemCommon} from '@/api/system'
|
|
|
+import { ChevronLeftIcon } from 'tdesign-icons-vue-next'
|
|
|
+import { MessagePlugin } from 'tdesign-vue-next'
|
|
|
+import AudioBox from './components/AudioBox.vue'
|
|
|
+import ReportContent from './components/ReportContent.vue'
|
|
|
+import ChapterWrap from './components/ChapterWrap.vue'
|
|
|
+import Disclaimer from './components/Disclaimer.vue'
|
|
|
+import CollectReport from '../user/favorite/components/CollectReport.vue'
|
|
|
+import { async } from '@antv/x6/lib/registry/marker/async'
|
|
|
+
|
|
|
+
|
|
|
+const router = useRouter()
|
|
|
+const route = useRoute()
|
|
|
+
|
|
|
+let audioData=ref({})
|
|
|
+const info = ref({})
|
|
|
+const chapterList = ref([])
|
|
|
+const headImgStyle = ref([])//版头style
|
|
|
+const endImgStyle = ref([])//版尾style
|
|
|
+const layoutBaseInfo = ref({
|
|
|
+ 研报标题:'',
|
|
|
+ 研报作者:'',
|
|
|
+ 创建时间:''
|
|
|
+})
|
|
|
+async function getReportDetail (){
|
|
|
+ const res = await apiEtaReport.reportDetail({
|
|
|
+ ReportId:Number(route.query.id)
|
|
|
+ })
|
|
|
+ if(res.Ret!==200) return
|
|
|
+ // if(!res.Data.HasAuth) return MessagePlugin.warning('您暂未开通该报告阅读权限,请联系销售!')
|
|
|
+
|
|
|
+ info.value=res.Data;
|
|
|
+ chapterList.value = res.Data.ChapterList || [];
|
|
|
+ disclaimer.value = res.Data.Disclaimer;
|
|
|
+
|
|
|
+ headImgStyle.value=info.value.HeadStyle?JSON.parse(info.value.HeadStyle):[]
|
|
|
+ endImgStyle.value=info.value.HeadStyle?JSON.parse(info.value.HeadStyle):[]
|
|
|
+ layoutBaseInfo.value['研报标题']=info.value.Title
|
|
|
+ layoutBaseInfo.value['研报作者']=info.value.Author
|
|
|
+ // 已发布已通过的报告才显示发布时间
|
|
|
+ layoutBaseInfo.value['创建时间']=moment(info.value.PublishTime).format('YYYY.MM.DD HH:mm')
|
|
|
+
|
|
|
+ audioData.value={
|
|
|
+ auth_ok:info.value.HasAuth,
|
|
|
+ video_name:info.value.VideoName||`${info.value.Title}(${moment(info.value.PublishTime).format('MMDD')})`,
|
|
|
+ video_size:info.value.VideoSize||'',
|
|
|
+ video_play_seconds:info.value.VideoPlaySeconds,
|
|
|
+ video_url:info.value.VideoUrl,
|
|
|
+ reportId:info.value.Id
|
|
|
+ }
|
|
|
+ document.title = info.value.ClassifyNameFirst
|
|
|
+
|
|
|
+ if(chapterList.value.length) {
|
|
|
+ handleChangeChapter(chapterList.value[0])
|
|
|
+ }
|
|
|
+
|
|
|
+ setWaterMark()
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+const chapterInfo = ref({})
|
|
|
+async function getChapterDetail(id=null) {
|
|
|
+ const res = await apiEtaReport.chapterDetail({
|
|
|
+ ChapterId: id||Number(route.query.chapterId)
|
|
|
+ })
|
|
|
+ if(res.Ret!==200) return
|
|
|
+
|
|
|
+ chapterInfo.value=res.Data;
|
|
|
+ disclaimer.value = res.Data.Disclaimer;
|
|
|
+ audioData.value={
|
|
|
+ auth_ok:chapterInfo.value.HasAuth,
|
|
|
+ video_name:chapterInfo.value.VideoName||`${chapterInfo.value.Title}(${moment(chapterInfo.value.PublishTime).format('MMDD')})`,
|
|
|
+ video_size:chapterInfo.value.VideoSize||'',
|
|
|
+ video_play_seconds:chapterInfo.value.VideoPlaySeconds,
|
|
|
+ video_url:chapterInfo.value.VideoUrl,
|
|
|
+ reportId:chapterInfo.value.Id
|
|
|
+ }
|
|
|
+
|
|
|
+ setWaterMark()
|
|
|
+
|
|
|
+}
|
|
|
+function initGet() {
|
|
|
+ route.query.chapterId ? getChapterDetail() : getReportDetail()
|
|
|
+}
|
|
|
+initGet()
|
|
|
+
|
|
|
+async function setWaterMark() {
|
|
|
+ const res=await apiSystemCommon.userInfo()
|
|
|
+
|
|
|
+ const options = {
|
|
|
+ text: res.Data?.Mobile||'',
|
|
|
+ fontSize: '14px',
|
|
|
+ color: '#999',
|
|
|
+ rotate: -40,
|
|
|
+ opacity: 0.2,
|
|
|
+ gapX: 180,
|
|
|
+ gapY: 120
|
|
|
+ };
|
|
|
+
|
|
|
+ // 创建 canvas 绘制水印图案
|
|
|
+ const canvas = document.createElement('canvas');
|
|
|
+ canvas.width = options.gapX;
|
|
|
+ canvas.height = options.gapY;
|
|
|
+ const ctx = canvas.getContext('2d');
|
|
|
+
|
|
|
+ ctx.save();
|
|
|
+ ctx.globalAlpha = options.opacity;
|
|
|
+ ctx.font = `${options.fontSize} Arial`;
|
|
|
+ ctx.fillStyle = options.color;
|
|
|
+ ctx.translate(canvas.width / 2, canvas.height / 2);
|
|
|
+ ctx.rotate((options.rotate * Math.PI) / 180);
|
|
|
+ ctx.textAlign = 'center';
|
|
|
+ ctx.textBaseline = 'middle';
|
|
|
+ ctx.fillText(options.text, 0, 0);
|
|
|
+ ctx.restore();
|
|
|
+
|
|
|
+ // 转换为 base64 图片
|
|
|
+ const base64Url = canvas.toDataURL('image/png');
|
|
|
+
|
|
|
+ // 设置背景样式
|
|
|
+ const el = document.getElementsByClassName('report-wrapper')[0];
|
|
|
+ el.style.backgroundImage = `url(${base64Url})`;
|
|
|
+ el.style.backgroundRepeat = 'repeat';
|
|
|
+ el.style.backgroundSize = `${options.gapX}px ${options.gapY}px`;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+const selectChapterId = ref(0)
|
|
|
+function handleChangeChapter(item) {
|
|
|
+ if(!item.HasAuth) return MessagePlugin.warning('您暂未开通该报告阅读权限,请联系销售!')
|
|
|
+
|
|
|
+ selectChapterId.value = item.ReportChapterId;
|
|
|
+ getChapterDetail(selectChapterId.value)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+const disclaimer = ref('带我去带我去多我去大青蛙单位群')
|
|
|
+const showDisclaimers=ref(false)//显示免责声明
|
|
|
+
|
|
|
+
|
|
|
+// 报告标题
|
|
|
+function formatTitle(e) {
|
|
|
+ let t=moment(e.PublishTime).format('MMDD')
|
|
|
+ let title=''
|
|
|
+ if(e.ClassifyNameSecond==e.Title){
|
|
|
+ title=`【第${e.Stage}期】${e.Title}(${t})`
|
|
|
+ }else{
|
|
|
+ title=`【第${e.Stage}期 | ${e.ClassifyNameFirst}】${e.Title}(${t})`
|
|
|
+ }
|
|
|
+ return title
|
|
|
+}
|
|
|
+
|
|
|
+function formatChapterTitle(e) {
|
|
|
+ let t=moment(e.PublishTime).format('MMDD')
|
|
|
+ let title=''
|
|
|
+
|
|
|
+ title=`【第${e.Stage}期 | ${e.ClassifyNameFirst} | ${e.TypeName}】${e.Title}(${t})`
|
|
|
+
|
|
|
+ return title
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+const showCollectReport = ref(false)
|
|
|
+//收藏
|
|
|
+async function handleCancelCollectReport() {
|
|
|
+
|
|
|
+ const res = await apiEtaReportCollect.reportCollectCancel({
|
|
|
+ ReportId: Number(route.query.id),
|
|
|
+ ReportChapterId: Number(route.query.chapterId)
|
|
|
+ })
|
|
|
+
|
|
|
+ if(res.Ret !== 200) return
|
|
|
+ MessagePlugin.success('取消成功')
|
|
|
+ if(route.query.chapterId) {
|
|
|
+ chapterInfo.value.IsCollected = false
|
|
|
+ }else {
|
|
|
+ info.value.IsCollected=false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 实现头部的适配
|
|
|
+const contentRef=ref('')
|
|
|
+const headerRef=ref('')
|
|
|
+let contentWidth=ref('')
|
|
|
+/* 重绘固定头宽度 */
|
|
|
+const resetHeaderWidthHandle = () => {
|
|
|
+ contentWidth.value = contentRef.value.offsetWidth
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ nextTick(() => resetHeaderWidthHandle())
|
|
|
+
|
|
|
+ window.addEventListener('resize',resetHeaderWidthHandle)
|
|
|
+ window.addEventListener('resize',resetHeaderWidthHandle)
|
|
|
+});
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ window.removeEventListener('resize',resetHeaderWidthHandle)
|
|
|
+})
|
|
|
+
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <div class="report-detail-page safe-content">
|
|
|
+ <div class="top-nav-wrap" ref="headerRef" :style="{'width':`${contentWidth}px`}">
|
|
|
+ <div class="flex top">
|
|
|
+ <div class="flex back" @click="router.go(-1)">
|
|
|
+ <ChevronLeftIcon />
|
|
|
+ 返回
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex" v-if="!info.HasChapter||route.query.chapterId">
|
|
|
+ <div class="collect-icon">
|
|
|
+ <svg-icon v-if="info?.IsCollected||chapterInfo?.IsCollected" name="star_fill" style="font-size:20px;cursor: pointer;" @click="handleCancelCollectReport"></svg-icon>
|
|
|
+ <svg-icon v-else name="star" style="font-size:20px;cursor: pointer;" @click="showCollectReport=true"></svg-icon>
|
|
|
+ </div>
|
|
|
+ {{(info.IsCollected||chapterInfo.IsCollected) ? '已收藏' : '收藏研报'}}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 报告详情 -->
|
|
|
+ <div class="report-box" ref="contentRef">
|
|
|
+ <!-- 章节区域 -->
|
|
|
+ <ChapterWrap
|
|
|
+ :list="chapterList"
|
|
|
+ :chapterId="selectChapterId"
|
|
|
+ v-if="info.HasChapter"
|
|
|
+ @change="handleChangeChapter"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 章节详情内容区 -->
|
|
|
+ <div class="report-wrapper "
|
|
|
+ :class="info.HasChapter?'chapter-detail':''"
|
|
|
+ v-if="info.HasChapter||route.query.chapterId"
|
|
|
+ >
|
|
|
+ <!-- 无版头板尾显示标题 -->
|
|
|
+
|
|
|
+ <div class="title" v-if="chapterInfo?.Title">{{formatChapterTitle(chapterInfo)}}</div>
|
|
|
+ <div class="time flex">
|
|
|
+ <span>{{chapterInfo.Author}}</span>
|
|
|
+ <span>{{moment(chapterInfo.PublishTime).format('YYYY.MM.DD HH:mm')}}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="tips">
|
|
|
+ <div class="abstract" v-if="chapterInfo.Abstract">摘要:{{chapterInfo.Abstract}}</div>
|
|
|
+ <div v-if="disclaimer">
|
|
|
+ <span>*注:请务必阅读</span>
|
|
|
+ <span style="color:#3D5EFF;margin-left:15px;cursor: pointer;" @click="showDisclaimers=true">免责声明</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 音频模块 -->
|
|
|
+ <AudioBox
|
|
|
+ :data="audioData"
|
|
|
+ v-if="chapterInfo.VideoUrl&&chapterInfo.VideoPlaySeconds>0">
|
|
|
+ </AudioBox>
|
|
|
+
|
|
|
+ <div id="report-rich-content" class="no-select-text rich-content" ref="waterMarkEl">
|
|
|
+
|
|
|
+ <ReportContent
|
|
|
+ v-if="chapterInfo.Content"
|
|
|
+ :html="chapterInfo.Content"
|
|
|
+ ></ReportContent>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 报告内容区域 -->
|
|
|
+ <div class="report-wrapper" v-else>
|
|
|
+ <!-- 无版头板尾显示标题 -->
|
|
|
+ <template v-if="(!info.HeadImg) && (!info.EndImg)">
|
|
|
+ <div class="title" v-if="info?.Title">{{formatTitle(info)}}</div>
|
|
|
+ <div class="time flex">
|
|
|
+ <span>{{info.Author}}</span>
|
|
|
+ <span>{{moment(info.PublishTime).format('YYYY.MM.DD HH:mm')}}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 拼接版头 -->
|
|
|
+ <div class="html-head-img-box" v-if="info.HeadImg">
|
|
|
+ <img :src="info.HeadImg" alt="" style="display:block;width:100%">
|
|
|
+ <div
|
|
|
+ class="head-layout-item"
|
|
|
+ v-for="item in headImgStyle"
|
|
|
+ :key="item.value"
|
|
|
+ :style="{
|
|
|
+ fontFamily:item.family,
|
|
|
+ fontSize:(item.size*2)+'px',
|
|
|
+ fontWeight:item.weight,
|
|
|
+ textAlign:item.align,
|
|
|
+ color:item.color,
|
|
|
+ width:item.width,
|
|
|
+ height:item.height,
|
|
|
+ left:item.left,
|
|
|
+ top:item.top
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ {{ layoutBaseInfo[item.value] }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="tips">
|
|
|
+ <div class="abstract" v-if="info.Abstract">摘要:{{info.Abstract}}</div>
|
|
|
+ <div v-if="disclaimer">
|
|
|
+ <span>*注:请务必阅读</span>
|
|
|
+ <span style="color:#3D5EFF;margin-left:15px;cursor: pointer;" @click="showDisclaimers=true">免责声明</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 音频模块 -->
|
|
|
+ <AudioBox
|
|
|
+ :data="audioData"
|
|
|
+ v-if="info.VideoUrl&&info.VideoPlaySeconds>0">
|
|
|
+ </AudioBox>
|
|
|
+
|
|
|
+ <div id="report-rich-content" class="no-select-text rich-content" ref="waterMarkEl">
|
|
|
+
|
|
|
+ <ReportContent
|
|
|
+ v-if="info.Content"
|
|
|
+ :html="info.Content"
|
|
|
+ ></ReportContent>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 拼接版尾 -->
|
|
|
+ <div class="html-end-img-box" v-if="info.EndImg">
|
|
|
+ <img :src="info.EndImg" alt="" style="display:block;width:100%">
|
|
|
+ <div class="head-layout-item" v-for="item in endImgStyle" :key="item.value"
|
|
|
+ :style="{fontFamily:item.family,fontSize:(item.size*2)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
|
|
|
+ width:item.width,height:item.height,left:item.left,top:item.top
|
|
|
+ }">
|
|
|
+ {{ layoutBaseInfo[item.value] }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+ <!-- <empty-wrap v-else msg="暂无阅读权限"/> -->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 免责声明 -->
|
|
|
+ <Disclaimer v-model:show="showDisclaimers" :content="disclaimer"/>
|
|
|
+
|
|
|
+ <!-- 收藏报告 -->
|
|
|
+ <CollectReport
|
|
|
+ v-model:show="showCollectReport"
|
|
|
+ :data="{
|
|
|
+ reportId: Number(route.query.id),
|
|
|
+ chapterId: Number(route.query.chapterId)
|
|
|
+ }"
|
|
|
+ @success="route.query.chapterId?(info.IsCollected=true):(chapterInfo.IsCollected=true)"
|
|
|
+ />
|
|
|
+
|
|
|
+ <t-back-top
|
|
|
+ :visible-height="200"
|
|
|
+ shape="circle"
|
|
|
+ theme="primary"
|
|
|
+ style="position: fixed; right: 30px; bottom: 40px"
|
|
|
+ ></t-back-top>
|
|
|
+</template>
|
|
|
+<style scoped lang="scss">
|
|
|
+@import './css/index.scss';
|
|
|
+.top-nav-wrap {
|
|
|
+ position: fixed;
|
|
|
+ top: 70px;
|
|
|
+ z-index: 99;
|
|
|
+ background-color: #fff;
|
|
|
+ padding-top: 30px;
|
|
|
+ padding-bottom: 20px;
|
|
|
+ width: 1240px;
|
|
|
+ border-bottom: 1px solid #DCDFE6;
|
|
|
+ .top {
|
|
|
+ justify-content: space-between;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+ h2 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 24px;
|
|
|
+ font-style: italic;
|
|
|
+ margin-right: 15px;
|
|
|
+ cursor: pointer;
|
|
|
+ flex-shrink:0;
|
|
|
+ }
|
|
|
+ .back {
|
|
|
+ cursor: pointer;
|
|
|
+ &:hover {
|
|
|
+ color: #3D5EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .collect-icon {
|
|
|
+ padding: 2px;
|
|
|
+ border: 1px solid #C8CDD9;
|
|
|
+ border-radius: 4px;
|
|
|
+ margin-right:5px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.report-box {
|
|
|
+ display: flex;
|
|
|
+ margin-top: 90px;
|
|
|
+ .report-wrapper {
|
|
|
+ flex: 1;
|
|
|
+ max-width: 970px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding-bottom: 20px;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ &.chapter-detail {
|
|
|
+ padding-left: 230px;
|
|
|
+ max-width: none;
|
|
|
+ }
|
|
|
+ .title{
|
|
|
+ display: inline;
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-left: -14px;
|
|
|
+ }
|
|
|
+ .time{
|
|
|
+ color: #666;
|
|
|
+ margin-top: 20px;
|
|
|
+ margin-bottom: 30px;
|
|
|
+ font-size: 16px;
|
|
|
+ justify-content: space-between;
|
|
|
+ }
|
|
|
+ .tips{
|
|
|
+ font-size: 18px;
|
|
|
+ margin-bottom: 30px;
|
|
|
+ position: relative;
|
|
|
+ padding-left: 20px;
|
|
|
+ &::before{
|
|
|
+ content: '';
|
|
|
+ width: 6px;
|
|
|
+ height: 100%;
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ display: inline-block;
|
|
|
+ background-color: #3D5EFF;
|
|
|
+ margin-right: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .abstract{
|
|
|
+ font-size: 18px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+ .rich-content{
|
|
|
+ margin-top: 30px;
|
|
|
+ line-height: 1.8;
|
|
|
+ font-size: 18px;
|
|
|
+ position: relative;
|
|
|
+ :deep(img){
|
|
|
+ width: 100% !important;
|
|
|
+ }
|
|
|
+ :deep(span){
|
|
|
+ font-size: 18px !important;
|
|
|
+ line-height: 1.8 !important;
|
|
|
+ background-color: rgba(255, 255, 255, 0) !important;
|
|
|
+ }
|
|
|
+ :deep(ul,ol,li,p){
|
|
|
+ font-size: 18px !important;
|
|
|
+ line-height: 1.8 !important;
|
|
|
+ background-color: rgba(255, 255, 255, 0) !important;
|
|
|
+ }
|
|
|
+ :deep(iframe){
|
|
|
+ width: 100% !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .html-head-img-box,.html-end-img-box{
|
|
|
+ margin-bottom: 10px;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ .head-layout-item{
|
|
|
+ position: absolute;
|
|
|
+ overflow: hidden;
|
|
|
+ box-sizing: border-box
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#report-rich-content{
|
|
|
+ position: relative;
|
|
|
+ .hide-watermark-box{
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ position: absolute;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: 40px;
|
|
|
+ z-index: 9;
|
|
|
+ transform: rotate(-50deg);
|
|
|
+ background: transparent;
|
|
|
+ left: 0;
|
|
|
+ top: 0;
|
|
|
+ pointer-events: none;
|
|
|
+ color: #DAE0FE; // 水印蒙层字体颜色设置为白色透明
|
|
|
+ opacity: 0.4;
|
|
|
+ }
|
|
|
+}
|
|
|
+.empty-wrap {
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+</style>
|