Detail.vue 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240
  1. <script setup>
  2. import {onMounted, onUpdated, ref,computed,nextTick} from 'vue'
  3. import { ElMessageBox } from 'element-plus'
  4. import moment from 'moment';
  5. import 'moment/dist/locale/zh-cn'
  6. import AudioBox from './components/AudioBox.vue'
  7. import SharePoster from '@/components/SharePoster.vue'
  8. import Comment from '@/components/Comment.vue'
  9. import {apiReportDetail,apiReportMoreRecmd,apiReportDetailBanner,apiRddpShareImg,apiReportPPtImgs,apiReportChapterAudioSet,apiPublicBannerMark,apiPublicBannerList} from '@/api/report'
  10. import {apiGetWechatQRCode} from '@/api/common'
  11. import {apiApplyPermission} from '@/api/user'
  12. import preLoadImg from '@/utils/preLoadImg.js'
  13. import { useRoute , onBeforeRouteUpdate,useRouter} from 'vue-router';
  14. import { useStore } from 'vuex';
  15. import {useWaterMark} from '@/hooks/waterMark.js'
  16. import CollectBox from '@/components/CollectBox.vue'
  17. import collectIcon from '@/assets/collect2.png'
  18. import collectSIcon from '@/assets/collect2-s.png'
  19. import {addTokenToIframe} from '@/utils/common.js'
  20. import { onClickOutside } from '@vueuse/core'
  21. import reportCancel from './components/reportCancel.vue'
  22. import ReportContent from './components/ReportContent.vue'
  23. import Disclaimer from '@/components/Disclaimer.vue'
  24. import { apiBaseConfig } from '@/api/common'
  25. moment.locale('zh-cn')
  26. const route=useRoute()
  27. const router=useRouter()
  28. const store=useStore()
  29. const waterMarkEl=ref('')//水印盒子
  30. let reportId=ref(route.query.reportId||'')
  31. //跳转线上路演
  32. const goVideoPage = (item)=>{
  33. router.push({
  34. path:'/roadshow/video/list',
  35. query:{
  36. videoId:item.road_video_id,
  37. fromPage:'reportDetail'
  38. }
  39. })
  40. }
  41. //获取报告对应的ppt图片
  42. let pptImgs=ref([])
  43. let showPreViewPPT=ref(false)
  44. let preViewPPTIndex=ref(0)
  45. const getReportPPTImgs=async ()=>{
  46. const res=await apiReportPPtImgs({
  47. report_id:Number(reportId.value),
  48. report_chapter_id:0
  49. })
  50. if(res.code===200){
  51. pptImgs.value=res.data||[]
  52. if(res.data&&res.data.length>0){
  53. preLoadImg(res.data)
  54. }
  55. }
  56. }
  57. const disclaimer = ref('')
  58. function getConfig() {
  59. apiBaseConfig().then(res => {
  60. if (res.code == 200) {
  61. disclaimer.value = res.data.disclaimer
  62. }
  63. })
  64. }
  65. getConfig()
  66. // 获取报告详情
  67. let info=ref(null)
  68. let audioData=ref(null)
  69. let isReportPublishCancel=ref(false)//报告取消发布了
  70. const headImgStyle = ref([])//版头style
  71. const endImgStyle = ref([])//版尾style
  72. const layoutBaseInfo = ref({
  73. 研报标题:'',
  74. 研报作者:'',
  75. 创建时间:''
  76. })
  77. const getReportDetail=async ()=>{
  78. const res=await apiReportDetail({
  79. report_id:Number(reportId.value)
  80. })
  81. if(res.code===200){
  82. info.value=res.data
  83. headImgStyle.value=res.data.report_info.head_style?JSON.parse(res.data.report_info.head_style):[]
  84. endImgStyle.value=res.data.report_info.end_style?JSON.parse(res.data.report_info.end_style):[]
  85. layoutBaseInfo.value['研报标题']=res.data.report_info.title
  86. layoutBaseInfo.value['研报作者']=res.data.report_info.author
  87. // 已发布已通过的报告才显示发布时间
  88. layoutBaseInfo.value['创建时间']=moment(res.data.report_info.publish_time).format('YYYY.MM.DD HH:mm')
  89. if(!res.data.report_info.has_chapter) {
  90. info.value.report_info.content=addTokenToIframe(res.data.report_info.content,res.data.report_info.report_id,0)
  91. }
  92. //章节拼接报告拼接iframe token
  93. if(res.data.report_info.has_chapter&&res.data.report_detail_show_type===1) {
  94. res.data.report_chapter_list.forEach(chapter => {
  95. chapter.content = addTokenToIframe(chapter.content,reportId.value,chapter.report_chapter_id)
  96. })
  97. }
  98. audioData.value={
  99. auth_ok:res.data.auth_ok,
  100. video_name:res.data.report_info.video_name||`${res.data.report_info.title}(${moment(res.data.report_info.publish_time).format('MMDD')})`,
  101. video_size:res.data.report_info.video_size||'',
  102. video_play_seconds:res.data.report_info.video_play_seconds,
  103. video_url:res.data.report_info.video_url,
  104. reportId:res.data.report_info.report_id
  105. }
  106. document.title = res.data.report_info.classify_name_first
  107. // if(['晨报','周报'].includes(res.data.report_info.classify_name_first)){
  108. store.commit('modifyBreadCrumb',res.data.report_info.classify_name_first)
  109. // }
  110. // 获取详情如果为联系销售根据判断条件是否主动申请一次
  111. if(!res.data.auth_ok){
  112. if(info.value.permission_check.type=='contact'&&!info.value.permission_check.customer_info.has_apply){
  113. if(info.value.permission_check.customer_info.status=='冻结'||(info.value.permission_check.customer_info.status=='试用'&&info.value.permission_check.customer_info.is_suspend==1)){
  114. apiApplyPermission({
  115. company_name:info.value.permission_check.customer_info.company_name,
  116. real_name:info.value.permission_check.customer_info.name,
  117. source:4,
  118. from_page:'报告详情'
  119. }).then(res=>{
  120. if(res.code===200){
  121. console.log('主动申请成功');
  122. }
  123. })
  124. }
  125. }
  126. }
  127. // 获取侧边更多推荐
  128. if(res.data.auth_ok){
  129. getAsideMoreRecmd(res.data.report_info)
  130. getAsideBanner(res.data.report_info)
  131. getReportPPTImgs()
  132. // 设置水印
  133. nextTick(()=>{
  134. useWaterMark(store.state.userInfo?.mobile,waterMarkEl)
  135. })
  136. }
  137. //向小程序发送分享数据
  138. //处理分享标题
  139. let shareTitle='',shareImg='',imgText='';
  140. const shareTime=moment(res.data.report_info.publish_time).format('MMDD')
  141. if(res.data.report_info.abstract){
  142. shareTitle=res.data.report_info.abstract
  143. imgText=`<div style="font-size:78px">第${res.data.report_info.stage}期 | ${res.data.report_info.title}${shareTime})</div>`
  144. }else{
  145. shareTitle=res.data.report_info.title
  146. imgText=`<div style="font-size:78px">${moment(res.data.report_info.publish_time).format('YYYY/MM/DD')}</div><div style="font-size:78px">第${res.data.report_info.stage}期 | ${res.data.report_info.classify_name_second}</div>`
  147. if(['晨报','周报'].includes(res.data.report_info.classify_name_first)){
  148. imgText=`<div style="font-size:78px">${moment(res.data.report_info.publish_time).format('YYYY/MM/DD')}</div><div style="font-size:78px">第${res.data.report_info.stage}期 | ${res.data.report_info.classify_name_first} </div>`
  149. }
  150. }
  151. const rddpImgRes=await apiRddpShareImg({
  152. pars:JSON.stringify({
  153. title:imgText,
  154. time_format:moment(res.data.report_info.publish_time).format('YYYY/MM/DD'),
  155. background_img:res.data.report_info.share_bg_img,
  156. ReportId:Number(reportId.value),
  157. })
  158. })
  159. if(rddpImgRes.code===200){
  160. shareImg=rddpImgRes.data
  161. }
  162. const postData={
  163. path:['晨报','周报'].includes(res.data.report_info.classify_name_first)?'/pages-report/chapterList':'/pages-report/reportDetail',
  164. params:{
  165. reportId:reportId.value
  166. },
  167. title:shareTitle,
  168. shareImg:shareImg
  169. }
  170. wx.miniProgram.postMessage({ data: postData })
  171. }else if(res.code===4002){
  172. isReportPublishCancel.value=true
  173. }
  174. }
  175. getReportDetail()
  176. // 侧边栏更多推荐
  177. let moreRecmdList=ref([])
  178. const getAsideMoreRecmd=async (data)=>{
  179. console.log('获取侧边栏更多推荐');
  180. const res=await apiReportMoreRecmd({
  181. reportId:Number(data.report_id),
  182. classify_name_first:data.classify_name_first
  183. })
  184. if(res.code===200){
  185. moreRecmdList.value=res.data
  186. }
  187. }
  188. //点击侧边栏更多推荐
  189. const handleClickAsideRecmd=(item)=>{
  190. router.replace({
  191. query:{
  192. reportId:item.ReportId
  193. }
  194. })
  195. // 更新页面数据
  196. // reportId.value=item.ReportId
  197. // getReportDetail()
  198. // getQRCodeImg()
  199. }
  200. //侧边栏报告合集
  201. let banner=ref(null)
  202. const getAsideBanner=async (data)=>{
  203. console.log('获取侧边栏报告合集');
  204. const res=await apiReportDetailBanner({
  205. reportId:Number(data.report_id),
  206. classify_name_first:data.classify_name_first
  207. })
  208. if(res.code===200){
  209. banner.value=res.data
  210. }
  211. }
  212. //点击侧边栏报告合集
  213. const handleAsideBanner=(data)=>{
  214. if(data.Type=='报告合集'&&data.ShowType===1){
  215. router.push({
  216. path:'/report/list',
  217. query:{
  218. classifyId:data.ClassifyIdFirst,
  219. classifyName:data.ClassifyNameFirst
  220. }
  221. })
  222. }else if(data.Type=='报告合集'&&data.ShowType===3){
  223. router.push({
  224. path:'/report/varietyreportlist',
  225. query:{
  226. classifyId:data.ClassifyIdFirst,
  227. }
  228. })
  229. }
  230. else{
  231. router.push({
  232. path:'/report/specialcolumndetail',
  233. query:{
  234. columnId:data.ClassifyIdSecond
  235. }
  236. })
  237. }
  238. }
  239. let preViewImgs=ref([])
  240. let preViewImgIndex=ref(0)
  241. let showPreViewImg=ref(false)
  242. onMounted(()=>{
  243. $(document).on('click', '.rich-content img',function(event) {
  244. let imgArray = [];
  245. let curImageSrc = $(this).attr('src');
  246. let oParent = $(this).parent();
  247. if (curImageSrc && !oParent.attr('href')) {
  248. if(preViewImgs.value.length===0){
  249. $('.rich-content img').each(function(index, el) {
  250. let itemSrc = $(this).attr('src');
  251. imgArray.push(itemSrc);
  252. });
  253. preViewImgs.value=imgArray
  254. }
  255. preViewImgIndex.value=preViewImgs.value.indexOf(curImageSrc)||0
  256. showPreViewImg.value=true
  257. }
  258. })
  259. // getBannerlist()
  260. })
  261. onBeforeRouteUpdate((to,from)=>{
  262. console.log(to.query);
  263. // 更新页面数据
  264. reportId.value=to.query.reportId
  265. getReportDetail()
  266. getQRCodeImg()
  267. })
  268. // 设置章节列表tag颜色
  269. const getTagColor=(str)=>{
  270. if( str.includes('多')||str.includes('强')||str.includes('反弹') ){
  271. return "#DF6051";
  272. }else if( str.includes('空')||str.includes('调整') ){
  273. return "#6FC5B4";
  274. }else{
  275. return "#009fe6";
  276. }
  277. }
  278. // 跳转章节详情
  279. const goChapterDetail=(item)=>{
  280. router.push({
  281. path:'/report/chapterdetail',
  282. query:{
  283. chapterId:item.report_chapter_id,
  284. frompage:'reportdetail'
  285. }
  286. })
  287. }
  288. //登录
  289. const handleGoLogin=()=>{
  290. wx.miniProgram.getEnv(res=>{
  291. if(res.miniprogram){
  292. localStorage.setItem('goBeforeUrl',window.location.href)
  293. wx.miniProgram.reLaunch({url:'/pages/login'})
  294. }
  295. })
  296. }
  297. // 点击申请
  298. const handleGoApply=async ()=>{
  299. if(store.state.userInfo.is_bind===0){
  300. ElMessageBox({
  301. title:`温馨提示`,
  302. message:'为了优化您的用户体验,<br>请登录后查看更多信息!',
  303. dangerouslyUseHTMLString: true,
  304. center: true,
  305. confirmButtonText:'去登录',
  306. confirmButtonClass:'self-elmessage-confirm-btn',
  307. showCancelButton:true,
  308. cancelButtonText:'取消',
  309. cancelButtonClass:'self-elmessage-cancel-btn'
  310. }).then(res=>{
  311. wx.miniProgram.reLaunch({url:'/pages/login'})
  312. }).catch(()=>{})
  313. return
  314. }
  315. if(info.value.permission_check.type=='apply'){
  316. if(info.value.permission_check.customer_info.has_apply){// 已经申请过
  317. const htmlStr=`<p>您已提交过申请,请耐心等待</p>`
  318. ElMessageBox({
  319. title:'温馨提醒',
  320. message:htmlStr,
  321. center: true,
  322. dangerouslyUseHTMLString: true,
  323. confirmButtonText:'知道了',
  324. confirmButtonClass:'self-elmessage-confirm-btn'
  325. })
  326. }else{
  327. if(!info.value.permission_check.customer_info.status||info.value.permission_check.customer_info.status!='流失'||info.value.permission_check.customer_info.status!='关闭'){
  328. router.push({
  329. path:'/apply/permission',
  330. query:{
  331. source:4,
  332. fromPage:'报告详情'
  333. }
  334. })
  335. }else{//主动调一次申请权限接口
  336. const res=await apiApplyPermission({
  337. company_name:info.value.permission_check.customer_info.company_name,
  338. real_name:info.value.permission_check.customer_info.name,
  339. source:4,
  340. from_page:'报告详情'
  341. })
  342. if(res.code===200){
  343. getReportDetail()
  344. const htmlStr=`<p>申请已提交</p><p>请等待销售人员与您联系</p>`
  345. ElMessageBox({
  346. title:'温馨提醒',
  347. message:htmlStr,
  348. center: true,
  349. dangerouslyUseHTMLString: true,
  350. confirmButtonText:'知道了',
  351. confirmButtonClass:'self-elmessage-confirm-btn'
  352. })
  353. }
  354. }
  355. }
  356. }
  357. }
  358. // 获取小程序码
  359. let QRCodeImg=ref('')
  360. const getQRCodeImg=async ()=>{
  361. const res=await apiGetWechatQRCode({
  362. CodeScene:JSON.stringify({reportId:reportId.value}),
  363. CodePage:'pages-report/reportDetail'
  364. })
  365. if(res.code===200){
  366. QRCodeImg.value=res.data
  367. }
  368. }
  369. getQRCodeImg()
  370. let showDisclaimers=ref(false)//显示免责声明
  371. const code_scene=computed(()=>{
  372. return JSON.stringify({reportId:reportId.value})
  373. })
  374. const posterParams=computed(()=>{
  375. return {
  376. report_type:info.value.report_info.classify_name_first,
  377. // report_title:`【第${info.value.report_info.stage}期 | ${info.value.report_info.classify_name_second}】${info.value.report_info.title}`,
  378. report_title:formatTitle(info.value.report_info),
  379. report_abstract:info.value.report_info.content
  380. }
  381. })
  382. // 报告标题
  383. const formatTitle=(e)=>{
  384. let t=moment(e.publish_time).format('MMDD')
  385. let title=''
  386. if(e.classify_name_second==e.title){
  387. title=`【第${e.stage}期】${e.title}${t})`
  388. }else{
  389. title=`【第${e.stage}期 | ${e.classify_name_second}${e.title}${t})`
  390. }
  391. return title
  392. }
  393. // 点击播放周报章节音频
  394. const handlePlayWeekAudio=(e)=>{
  395. if(store.state.audioData.reportId==info.value.report_info.report_id){
  396. if(store.state.audioData.paused){
  397. store.state.audioData.INS.play()
  398. }else{
  399. store.state.audioData.INS.pause()
  400. }
  401. return
  402. }
  403. const arr=[]
  404. info.value.report_chapter_list.forEach(item=>{
  405. if(item.is_close==0){
  406. arr.push({
  407. url:item.video_url,
  408. time:item.video_play_seconds,
  409. name:item.video_name
  410. })
  411. }
  412. })
  413. console.log(arr);
  414. let index=0
  415. if(e){
  416. arr.forEach((_item,idx)=>{
  417. if(_item.url==e.video_url) index=idx
  418. })
  419. }
  420. store.commit('addAudio',{
  421. list:arr,
  422. index:index,
  423. reportId:info.value.report_info.report_id,
  424. })
  425. }
  426. let showAudioPlayListSet=ref(false)//显示播放清单配置
  427. const handleAudioSet=(item)=>{
  428. apiReportChapterAudioSet({
  429. type_id:item.type_id,
  430. is_close:item.is_close==0?1:0
  431. }).then(res=>{
  432. if(res.code===200){
  433. item.is_close=item.is_close==0?1:0
  434. }
  435. })
  436. }
  437. // banner data
  438. const bannerDataList =ref([])
  439. /* 点击了banner */
  440. const bannerClickHandler = async (item) => {
  441. if(store.state.userInfo.is_bind===0){
  442. ElMessageBox({
  443. title:`温馨提示`,
  444. message:'为了优化您的用户体验,<br>请登录后查看更多信息!',
  445. dangerouslyUseHTMLString: true,
  446. center: true,
  447. confirmButtonText:'去登录',
  448. confirmButtonClass:'self-elmessage-confirm-btn',
  449. showCancelButton:true,
  450. cancelButtonText:'取消',
  451. cancelButtonClass:'self-elmessage-cancel-btn'
  452. }).then(res=>{
  453. wx.miniProgram.reLaunch({url:'/pages/login'})
  454. }).catch(()=>{})
  455. return
  456. }
  457. let source = sessionStorage.getItem('platformSource')
  458. const res = await apiPublicBannerMark({
  459. first_source: source == 'xcx' ? 2 : 3, //一级来源 1小程序移动 2小程序pc 3研报官网
  460. second_source: 2 ,//二级来源 1首页 2研报详情页
  461. id:item.id
  462. })
  463. if(res.code ==200){
  464. router.push({
  465. path:'/report/disseminatePage',
  466. query: {
  467. imgBg: item.jump_url_pc,
  468. enable: item.enable,
  469. id: item.id,
  470. title: item.remark
  471. }
  472. })
  473. }
  474. }
  475. // banner 获取列表
  476. const getBannerlist = async () => {
  477. const res = await apiPublicBannerList()
  478. if(res.code ==200){
  479. bannerDataList.value = res.data
  480. }
  481. }
  482. // 显示引导
  483. let showAttention=ref(localStorage.getItem('showAttention')?false:true)
  484. const audioPlayListPop=ref(null)
  485. onClickOutside(audioPlayListPop, (event) => {
  486. if(info.value.report_info.classify_name_first=='周报'&&info.value.auth_ok){
  487. closeShowAttention()
  488. }
  489. })
  490. const closeShowAttention=()=>{
  491. showAttention.value=false
  492. localStorage.setItem('showAttention','true')
  493. }
  494. let showAttentionPop=ref(localStorage.getItem('showAttentionPop')?false:true)
  495. const attentionPop=ref(null)
  496. onClickOutside(attentionPop, (event) => {
  497. if(showAudioPlayListSet.value){
  498. closeShowAttentionPop()
  499. }
  500. })
  501. const closeShowAttentionPop=()=>{
  502. showAttentionPop.value=false
  503. localStorage.setItem('showAttentionPop','true')
  504. }
  505. </script>
  506. <template>
  507. <div class="report-detail-page" v-if="info">
  508. <div class="hasrightaside-box">
  509. <div class="content-box">
  510. <!-- 晨报、周报章节 列表展示形式 -->
  511. <div class="chapter-list-wrap" v-if="info.report_info.has_chapter&&info.report_detail_show_type===2">
  512. <div
  513. :class="['top-box',info.report_info.classify_name_first=='周报'?'top-box-week':'']"
  514. :style="'background-image:url(' + info.report_info.banner_url + ')'"
  515. >
  516. <div class="time-box">
  517. <div class="day">{{moment(info.report_info.publish_time).format('DD')}}</div>
  518. <div style="font-size:14px">
  519. <div>{{moment(info.report_info.publish_time).format('ddd')}}</div>
  520. <div>{{moment(info.report_info.publish_time).format('YYYY-MM')}}</div>
  521. </div>
  522. </div>
  523. <div class="title">{{info.report_info.classify_name_first}}</div>
  524. <div class="sub-title">{{info.report_info.title}}</div>
  525. <!-- 列表有音频 章节就不显示 -->
  526. <template v-if="!info.report_info.video_url">
  527. <!-- 音频设置 -->
  528. <el-popover
  529. :visible="showAttention&&info.auth_ok"
  530. placement="bottom-end"
  531. :width="340"
  532. >
  533. <template #reference>
  534. <img
  535. src="@/assets/audio-set.png"
  536. class="audio-set-box"
  537. v-if="info.report_info.classify_name_first=='周报'&&info.auth_ok" @click="showAudioPlayListSet=true"
  538. />
  539. </template>
  540. <template #default>
  541. <div ref="audioPlayListPop" style="position: relative;">
  542. <h2 style="color:#F3A52F;font-size:16px">配置播放列表</h2>
  543. <p>点击<span style="color:#F3A52F">设置</span>,打开<span style="color:#F3A52F">播放列表</span>,进行<span style="color:#F3A52F">关注品种配置</span></p>
  544. <img src="@/assets/icon-close-2.png" alt="" style="width:9px;height:9px;position: absolute;top:-12px;right:10px" @click="closeShowAttention">
  545. </div>
  546. </template>
  547. </el-popover>
  548. <!-- 播放 按钮 -->
  549. <div
  550. class="audio-play-box"
  551. v-if="info.report_info.classify_name_first=='周报'&&info.auth_ok"
  552. @click="handlePlayWeekAudio(null)"
  553. >
  554. <div :class="['icon',$store.state.audioData.reportId==info.report_info.report_id&&!$store.state.audioData.paused?'icon-active':'']"></div>
  555. <span>{{$store.state.audioData.reportId==info.report_info.report_id&&!$store.state.audioData.paused?'暂停':'播放'}}</span>
  556. </div>
  557. </template>
  558. </div>
  559. <div class="list-box">
  560. <div class="flex item" v-for="item in info.report_chapter_list" :key="item.report_chapter_id" @click="goChapterDetail(item)">
  561. <div class="img-box">
  562. <el-image class="img" :src="item.report_chapter_type_thumb" fit="cover" />
  563. </div>
  564. <div class="con">
  565. <div class="title">
  566. {{item.report_chapter_type_name}}
  567. <span class="tag" :style="{backgroundColor:getTagColor(tag)}" v-for="tag in item.trend.split(',')" :key="tag">{{tag}}</span>
  568. </div>
  569. <div class="van-multi-ellipsis--l2 sub-title">{{item.title}}</div>
  570. <div class="update-time">更新至:{{moment(item.publish_time).format('YYYY-MM-DD')}}</div>
  571. <div
  572. :class="['audio-icon-box',$store.state.audioData.list[$store.state.audioData.index]?.url==item.video_url&&!$store.state.audioData.paused?'audio-icon-box--active':'']"
  573. v-if="info.report_info.classify_name_first=='周报'&&item.is_close==0&&!info.report_info.video_url"
  574. @click.stop="handlePlayWeekAudio(item)"
  575. ></div>
  576. </div>
  577. </div>
  578. </div>
  579. <!-- 无权限 -->
  580. <div class="no-auth-wrap" v-if="store.state.userInfo?.is_bind==0">
  581. <div class="apply-box">
  582. <div>您尚未登录,请登录后查看更多信息</div>
  583. <div class="global-main-btn btn" @click="handleGoLogin">立即登录</div>
  584. </div>
  585. </div>
  586. <template v-else>
  587. <div class="no-auth-wrap" v-if="!info.auth_ok">
  588. <img class="img" width="400" src="https://hzstatic.hzinsights.com/static/icon/hzyb/activity_no_auth.png" mode="widthFix" />
  589. <div class="apply-box" v-if="info.permission_check.type=='apply'">
  590. <div>您暂无权限查看报告,若想查看请申请开通</div>
  591. <div class="global-main-btn btn" @click="handleGoApply">立即申请</div>
  592. </div>
  593. <div class="apply-box" v-else>
  594. <div>您暂无权限查看报告 </div>
  595. <div>若想查看请联系对口销售:{{info.permission_check.name}}--{{info.permission_check.mobile}}</div>
  596. </div>
  597. </div>
  598. </template>
  599. </div>
  600. <!-- 报告详情 章节报告拼接形式 -->
  601. <div class="report-box" v-else>
  602. <!-- <template v-if="bannerDataList.length > 0">
  603. <el-carousel height="90px" style="margin-bottom:20px;" :interval="4000" :indicator-position="bannerDataList.length==0?'none':''" :arrow="bannerDataList.length==0?'never':''">
  604. <el-carousel-item @click="bannerClickHandler(item)" v-for="item in bannerDataList" :key="item.id">
  605. <img style="width:100%;height:100%;cursor:pointer;" :src="item.image_url_pc" alt="">
  606. </el-carousel-item>
  607. </el-carousel>
  608. </template> -->
  609. <div :style="{backgroundColor:info.report_info.canvas_color||''}">
  610. <!-- 无版头板尾显示标题 -->
  611. <template v-if="(!info.report_info.head_img) && (!info.report_info.end_img)">
  612. <div class="title">{{formatTitle(info.report_info)}}</div>
  613. <div class="time">
  614. <span>{{info.report_info.author}}</span>
  615. <span>{{moment(info.report_info.publish_time).format('YYYY.MM.DD HH:mm')}}</span>
  616. </div>
  617. </template>
  618. <!-- 拼接版头 -->
  619. <div class="html-head-img-box" v-if="info.auth_ok&&info.report_info.head_img">
  620. <img :src="info.report_info.head_img" alt="" style="display:block;width:100%">
  621. <div class="head-layout-item" v-for="item in headImgStyle" :key="item.value"
  622. :style="{fontFamily:item.family,fontSize:(item.size*2)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
  623. width:item.width,height:item.height,left:item.left,top:item.top
  624. }">
  625. {{ layoutBaseInfo[item.value] }}
  626. </div>
  627. </div>
  628. <div class="flex tips" style="position:relative">
  629. <div>
  630. <div v-if="info.road_video_id">点击<span style="color: #F3A52F;cursor: pointer;" @click="goVideoPage(info)">查看视频</span></div>
  631. <div class="abstract" v-if="info.report_info.abstract">摘要:{{info.report_info.abstract}}</div>
  632. <div v-if="disclaimer">
  633. <span>*注:请务必阅读</span>
  634. <span style="color:#F3A52F;margin-left:20px;cursor: pointer;" @click="showDisclaimers=true">免责声明</span>
  635. </div>
  636. </div>
  637. </div>
  638. <!-- 音频模块 -->
  639. <AudioBox :data="audioData" v-if="info.report_info.video_url&&info.report_info.video_play_seconds>0"></AudioBox>
  640. <div id="report-rich-content" class="no-select-text rich-content" ref="waterMarkEl">
  641. <template v-if="info.auth_ok">
  642. <!-- 展示拼接的章节报告 -->
  643. <template v-if="info.report_info.has_chapter && info.report_detail_show_type===1">
  644. <div
  645. class="chapter-concat-item"
  646. v-for="chapter in info.report_chapter_list"
  647. :key="chapter.report_chapter_id"
  648. >
  649. <div class="chapter-title">
  650. <h3 class="chapter-title-text">{{chapter.title}}</h3>
  651. </div>
  652. <div class="html-cont" v-html="chapter.content"></div>
  653. </div>
  654. </template>
  655. <!-- 单人报告 -->
  656. <ReportContent :html="info.report_info.content" v-if="!info.report_info.has_chapter"></ReportContent>
  657. </template>
  658. <div v-html="info.report_info.content_sub" v-else></div>
  659. <!-- 隐藏的水印 -->
  660. <div class="hide-watermark-box">
  661. <div v-for="item in 20" :key="item">{{$store.state.userInfo.mobile}}</div>
  662. </div>
  663. </div>
  664. <!-- 拼接版尾 -->
  665. <div class="html-end-img-box" v-if="info.auth_ok&&info.report_info.end_img">
  666. <img :src="info.report_info.end_img" alt="" style="display:block;width:100%">
  667. <div class="head-layout-item" v-for="item in endImgStyle" :key="item.value"
  668. :style="{fontFamily:item.family,fontSize:(item.size*2)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
  669. width:item.width,height:item.height,left:item.left,top:item.top
  670. }">
  671. {{ layoutBaseInfo[item.value] }}
  672. </div>
  673. </div>
  674. </div>
  675. <!-- 评论点赞模块 -->
  676. <Comment
  677. :data="{
  678. report_id:info.report_info.report_id,
  679. report_chapter_id:0,
  680. old_report_id:0,
  681. old_report_chapter_id:0,
  682. like_enabled:info.like_enabled,
  683. like_num:info.like_num
  684. }"
  685. v-if="info.auth_ok"
  686. />
  687. <!-- 无权限 -->
  688. <div class="no-auth-wrap" v-if="store.state.userInfo?.is_bind==0">
  689. <div class="apply-box">
  690. <div>您尚未登录,请登录后查看更多信息</div>
  691. <div class="global-main-btn btn" @click="handleGoLogin">立即登录</div>
  692. </div>
  693. </div>
  694. <template v-else>
  695. <div class="no-auth-wrap" v-if="!info.auth_ok">
  696. <div class="apply-box" v-if="info.permission_check.type=='apply'">
  697. <div>您暂无权限查看报告,若想查看请申请开通</div>
  698. <div class="global-main-btn btn" @click="handleGoApply">立即申请</div>
  699. </div>
  700. <div class="apply-box" v-else>
  701. <div>您暂无权限查看报告 </div>
  702. <div>若想查看请联系对口销售:{{info.permission_check.name}}--{{info.permission_check.mobile}}</div>
  703. </div>
  704. </div>
  705. </template>
  706. <!-- 右侧悬浮操作栏 -->
  707. <div class="right-fix-opt-box">
  708. <!-- 收藏 -->
  709. <CollectBox
  710. :type="1"
  711. :primaryId="info.report_info.report_id"
  712. :collectId="info.collection_id"
  713. @change="e=>info.collection_id=e"
  714. v-if="info&&info.auth_ok"
  715. >
  716. <img class="collect-icon" :src="info.collection_id>0?collectSIcon:collectIcon" alt="">
  717. </CollectBox>
  718. <!-- ppt -->
  719. <img class="ppt-icon" src="@/assets/ppt-icon.png" v-if="pptImgs.length>0" @click="showPreViewPPT=true" alt="">
  720. <!-- 生成海报 -->
  721. <SharePoster
  722. :shareData="{
  723. type:'report_detail',
  724. code_page:'pages-report/reportDetail',
  725. code_scene:code_scene,
  726. data:posterParams
  727. }"
  728. :style="{position:'static',display:'inline-block'}"
  729. v-if="info&&info.auth_ok"
  730. >
  731. </SharePoster>
  732. </div>
  733. </div>
  734. </div>
  735. <div class="right-aside-box" v-if="info.auth_ok&&!info.report_info.has_chapter">
  736. <div class="fix-top">
  737. <div class="share-box">
  738. <div class="label">分享</div>
  739. <el-popover
  740. :width="200"
  741. popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; padding: 20px;"
  742. >
  743. <template #reference><div class="icon"></div></template>
  744. <template #default>
  745. <img :src="QRCodeImg" class="share-xcx-img" alt="" style="width: 150px;display: block;margin: 0 auto;">
  746. </template>
  747. </el-popover>
  748. </div>
  749. <div class="hot-box" style="margin-top: 60px;" v-if="banner">
  750. <div class="label">{{banner.Type}}</div>
  751. <div class="img-con" :style="'background-image:url('+banner.ImgUrl+')'" @click="handleAsideBanner(banner)">
  752. <!-- 专栏 -->
  753. <div v-if="banner.Type=='专栏详情'" class="cloumn-box">
  754. <div class="title">{{banner.ClassifyNameSecond.substring(0, 12)}}</div>
  755. <div class="multi-ellipsis user">{{banner.ReportAuthor}} {{banner.VipTitle}}</div>
  756. <div class="stage">更新至{{banner.Stage}}</div>
  757. </div>
  758. <!-- 报告列表 -->
  759. <div v-else class="rep-box cloumn-box">
  760. <div class="title">{{banner.ClassifyNameFirst.substring(0, 12)}}</div>
  761. <!-- <span class="stage">第{{banner.Stage}}期</span> -->
  762. </div>
  763. </div>
  764. </div>
  765. <div class="recmd-box" style="margin-top: 40px;">
  766. <div class="label">更多推荐</div>
  767. <div
  768. class="recmd-item"
  769. v-for="item in moreRecmdList"
  770. :key="item.Id"
  771. @click="handleClickAsideRecmd(item)"
  772. >
  773. <div class="title">{{item.ClassifySecondFirst}}</div>
  774. <div>{{item.Stage}} | {{item.Title}}</div>
  775. </div>
  776. </div>
  777. </div>
  778. </div>
  779. </div>
  780. </div>
  781. <report-cancel v-if="isReportPublishCancel"/>
  782. <!-- 图片预览 -->
  783. <el-image-viewer
  784. v-if="showPreViewImg"
  785. :initial-index="preViewImgIndex"
  786. @close="showPreViewImg=false"
  787. :url-list="preViewImgs"
  788. />
  789. <!-- 预览ppt -->
  790. <div class="ppt-preview-box" v-if="showPreViewPPT">
  791. <el-image-viewer
  792. v-if="showPreViewPPT"
  793. :initial-index="preViewPPTIndex"
  794. @close="showPreViewPPT=false"
  795. :url-list="pptImgs"
  796. />
  797. </div>
  798. <!-- 免责申明 -->
  799. <Disclaimer v-model:show="showDisclaimers"/>
  800. <!-- 播放清单设置 -->
  801. <el-dialog v-model="showAudioPlayListSet" :close-on-click-modal="false" @closed="closeShowAttentionPop" custom-class="audioplaylist-popup" draggable width="40%">
  802. <template #header>
  803. <span style="font-weight:bold">播放列表</span>
  804. <span style="margin-left:10px;cursor: pointer;" @click="showAttentionPop=true">
  805. <img src="@/assets/icon-atten.png" alt="" style="width:11px;height:11px;margin-right:4px">
  806. <span style="color:#999;font-size:12px">操作提示</span>
  807. </span>
  808. </template>
  809. <div class="audioplaylist-wrap">
  810. <div class="item" v-for="item in info.report_chapter_list" :key="item.report_chapter_id">
  811. <span>{{item.report_chapter_type_name}}</span>
  812. <el-switch
  813. :model-value="item.is_close==0?true:false"
  814. @change="handleAudioSet(item)"
  815. />
  816. </div>
  817. <div class="attention-box" ref="attentionPop" v-if="showAttentionPop">
  818. <h2 style="color:#F3A52F;font-size:16px">配置播放列表</h2>
  819. <p>
  820. 品种<span style="color:#F3A52F">开启</span>,表示添加到待播放列表;<br>
  821. 品种<span style="color:#F3A52F">关闭</span>,表示从待播放列表中清除;<br>
  822. 您可以根据自己关注的品种进行选择~
  823. </p>
  824. <img src="@/assets/icon-close-2.png" alt="" style="width:9px;height:9px;position: absolute;top:15px;right:15px" @click="closeShowAttentionPop">
  825. </div>
  826. </div>
  827. </el-dialog>
  828. </template>
  829. <style lang="scss">
  830. .audioplaylist-popup{
  831. .el-dialog__header{
  832. border-bottom: 1px solid #F2F2F2;
  833. }
  834. }
  835. </style>
  836. <style lang="scss" scoped>
  837. .report-detail-page{
  838. .report-box{
  839. .title{
  840. display: inline;
  841. font-size: 24px;
  842. font-weight: bold;
  843. margin-left: -14px;
  844. }
  845. .time{
  846. color: #666;
  847. margin-top: 20px;
  848. margin-bottom: 30px;
  849. font-size: 16px;
  850. span:last-child{
  851. float: right;
  852. }
  853. }
  854. .tips{
  855. font-size: 18px;
  856. margin-bottom: 30px;
  857. &::before{
  858. content: '';
  859. width: 6px;
  860. display: inline-block;
  861. background-color: #F3A52F;
  862. margin-right: 10px;
  863. }
  864. }
  865. .abstract{
  866. font-size: 18px;
  867. margin-bottom: 20px;
  868. }
  869. .rich-content{
  870. margin-top: 30px;
  871. line-height: 1.8;
  872. font-size: 18px;
  873. position: relative;
  874. :deep(img){
  875. width: 100% !important;
  876. }
  877. :deep(span){
  878. font-size: 18px !important;
  879. line-height: 1.8 !important;
  880. background-color: rgba(255, 255, 255, 0) !important;
  881. }
  882. :deep(ul,ol,li,p){
  883. font-size: 18px !important;
  884. line-height: 1.8 !important;
  885. background-color: rgba(255, 255, 255, 0) !important;
  886. }
  887. :deep(iframe){
  888. width: 100% !important;
  889. }
  890. }
  891. .right-fix-opt-box{
  892. position: fixed;
  893. right: 33px;
  894. bottom: 150px;
  895. z-index: 1000;
  896. .ppt-icon{
  897. width: 54px;
  898. height: 54px;
  899. object-fit: cover;
  900. display: block;
  901. margin: 0 auto;
  902. }
  903. .collect-icon{
  904. width: 57px;
  905. height: 57px;
  906. object-fit: cover;
  907. display: block;
  908. margin: 0 auto;
  909. }
  910. }
  911. }
  912. .no-auth-wrap{
  913. text-align: center;
  914. color: #F3A52F;
  915. margin-top: -140px;
  916. padding-top: 140px;
  917. min-height: 200px;
  918. position: relative;
  919. z-index: 5;
  920. background: linear-gradient(360deg, #FFFFFF 60%, rgba(255, 255, 255, 0) 88%);
  921. .btn{
  922. width: 218px;
  923. margin-left: auto;
  924. margin-right: auto;
  925. margin-top: 20px;
  926. }
  927. }
  928. .chapter-list-wrap{
  929. .top-box{
  930. height: 140px;
  931. background-color: rgba($color: #000000, $alpha: 0.7);
  932. background-size: cover;
  933. position: relative;
  934. margin-bottom: 20px;
  935. padding: 39px 34px 20px 34px;
  936. color: #fff;
  937. .title{
  938. font-size: 25px;
  939. font-weight: 600;
  940. }
  941. .sub-title{
  942. margin-top: 8px;
  943. }
  944. .time-box{
  945. position: absolute;
  946. right: 34px;
  947. bottom: 30px;
  948. display: flex;
  949. align-items: center;
  950. .day{
  951. padding-right: 5px;
  952. font-size: 23px;
  953. font-weight: 600;
  954. border-right: 1px solid #fff;
  955. margin-right: 5px;
  956. }
  957. }
  958. .audio-set-box{
  959. width: 46px;
  960. height: 28px;
  961. position: absolute;
  962. top: 20px;
  963. right: 34px;
  964. cursor: pointer;
  965. }
  966. .audio-play-box{
  967. width: 88px;
  968. height: 24px;
  969. background-color: #F3A52F;
  970. border-radius: 24px;
  971. position: absolute;
  972. bottom: 20px;
  973. right: 34px;
  974. display: flex;
  975. align-items: center;
  976. justify-content: center;
  977. color: #FFFFFF;
  978. cursor: pointer;
  979. .icon{
  980. width: 14px;
  981. height: 14px;
  982. background-image: url('@/assets/audio-pause-white.png');
  983. background-size: cover;
  984. margin-right: 5px;
  985. }
  986. .icon-active{
  987. background-image: url('@/assets/audio-doing-white.png');
  988. }
  989. }
  990. }
  991. .top-box-week{
  992. padding: 20px 34px;
  993. .title{
  994. margin-top: 10px;
  995. }
  996. .time-box{
  997. position: relative;
  998. bottom: 0;
  999. right: 0;
  1000. }
  1001. }
  1002. .list-box{
  1003. min-height: 100px;
  1004. background-color: #fff;
  1005. position: relative;
  1006. .item{
  1007. align-items: center;
  1008. padding: 20px 0;
  1009. border-bottom: 1px solid #E5E5E5;
  1010. cursor: pointer;
  1011. .img-box{
  1012. width: 55px;
  1013. height: 55px;
  1014. box-sizing: border-box;
  1015. border-radius: 8px;
  1016. border: solid 1px #E5E5E5;
  1017. display: flex;
  1018. align-items: center;
  1019. justify-content: center;
  1020. margin-right: 20px;
  1021. .img{
  1022. width: 35px;
  1023. height: 35px;
  1024. }
  1025. }
  1026. .con{
  1027. flex: 1;
  1028. position: relative;
  1029. .title{
  1030. margin-bottom: 8px;
  1031. font-weight: bold;
  1032. .tag{
  1033. font-size: 12px;
  1034. color: #fff;
  1035. font-weight: normal;
  1036. display: inline-block;
  1037. background-color: #1E88E5;
  1038. line-height: 20px;
  1039. padding: 0 6px;
  1040. border-radius: 4px;
  1041. margin-left: 10px;
  1042. }
  1043. }
  1044. &:hover{
  1045. .title{
  1046. color: #F3A52F;
  1047. }
  1048. }
  1049. .sub-title{
  1050. color: #333;
  1051. }
  1052. .update-time{
  1053. position: absolute;
  1054. top: 0;
  1055. right: 0;
  1056. color: #999999;
  1057. font-size: 14px;
  1058. }
  1059. .audio-icon-box{
  1060. position: absolute;
  1061. bottom: 0;
  1062. right: 0;
  1063. width: 21px;
  1064. height: 21px;
  1065. background-image: url('@/assets/audio-pause.png');
  1066. background-size: cover;
  1067. }
  1068. .audio-icon-box--active{
  1069. background-image: url('@/assets/audio-doing.png');
  1070. }
  1071. }
  1072. }
  1073. }
  1074. }
  1075. .right-aside-box{
  1076. .hot-box{
  1077. .img-con{
  1078. position: relative;
  1079. .cloumn-box{
  1080. padding: 17px 15px;
  1081. .title{
  1082. font-size: 16px;
  1083. font-weight: bold;
  1084. color: #fff;
  1085. width: 105px;
  1086. }
  1087. .user{
  1088. font-size: 12px;
  1089. color: #fff;
  1090. margin-top: 4px;
  1091. width: 105px;
  1092. }
  1093. .stage{
  1094. margin-top: 10px;
  1095. color: #F3A52F;
  1096. font-size: 12px;
  1097. }
  1098. }
  1099. .rep-box{
  1100. .stage{
  1101. position: absolute;
  1102. bottom: 27px;
  1103. left: 16px;
  1104. color: #F3A52F;
  1105. font-size: 12px;
  1106. }
  1107. }
  1108. }
  1109. }
  1110. }
  1111. }
  1112. .audioplaylist-wrap{
  1113. max-height: 70vh;
  1114. overflow-y: auto;
  1115. position: relative;
  1116. .item{
  1117. display: flex;
  1118. justify-content: space-between;
  1119. align-items: center;
  1120. border-bottom: 1px solid #F2F2F2;
  1121. font-size: 16px;
  1122. padding: 15px 20px;
  1123. }
  1124. .attention-box{
  1125. position: absolute;
  1126. top: 50px;
  1127. right: 20px;
  1128. border: 1px solid #F2F2F2;
  1129. border-radius: 8px;
  1130. background-color: #fff;
  1131. width: 300px;
  1132. box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, 0.06);
  1133. padding: 16px;
  1134. }
  1135. }
  1136. .disclaimers-box{
  1137. max-height: 60vh;
  1138. overflow: auto;
  1139. }
  1140. .html-head-img-box,.html-end-img-box{
  1141. margin-bottom: 10px;
  1142. position: relative;
  1143. overflow: hidden;
  1144. .head-layout-item{
  1145. position: absolute;
  1146. overflow: hidden;
  1147. box-sizing: border-box
  1148. }
  1149. }
  1150. .chapter-concat-item {
  1151. padding: 10px 0;
  1152. .chapter-title {
  1153. display: flex;
  1154. align-items: center;
  1155. font-size: 15px;
  1156. .type {
  1157. height: fit-content;
  1158. display: inline-block;
  1159. color: #fff;
  1160. padding: 5px 10px;
  1161. background-color: #E6A23C;
  1162. border-radius: 4px;
  1163. margin-right: 10px;
  1164. }
  1165. .chapter-title-text {
  1166. font-size: 15px;
  1167. }
  1168. }
  1169. }
  1170. </style>