Преглед на файлове

Merge branch 'master' into xqc_957

bding преди 6 месеца
родител
ревизия
d6f6282790

+ 0 - 1
index.html

@@ -10,6 +10,5 @@
   <body>
     <div id="app"></div>
     <script type="module" src="/src/main.js"></script>
-    <script type="text/javascript" src="https://gitee.com/dcloud/uni-app/raw/master/dist/uni.webview.1.5.2.js"></script>
   </body>
 </html>

+ 1 - 1
src/views/hzsl/report/newReport/ChapterDetail.vue

@@ -5,7 +5,7 @@
       <!-- <div class="title">【第{{info.report_chapter_item.stage}}期 | {{info.report_chapter_item.classify_name_first}}  | {{info.report_chapter_item.type_name}}】{{info.report_chapter_item.title}}</div> -->
       <div class="title">{{title}}</div>
       <div class="flex time">
-        <span>FICC团队</span>
+        <span>{{info.report_chapter_item.author}}</span>
         <span>{{formatTime(info.report_chapter_item.publish_time)}}</span>
       </div>
       <!-- 音频模块 -->

+ 1 - 1
src/views/hzsl/report/newReport/Detail.vue

@@ -50,7 +50,7 @@
             <!-- <div class="title">【第{{info.report_info.stage}}期|{{info.report_info.classify_name_second}}】{{info.report_info.title}}</div> -->
             <div class="title">{{title}}</div>
             <div class="flex time">
-                <span>FICC团队</span>
+                <span>{{info.report_info.author}}</span>
                 <span>{{formatTime(info.report_info.publish_time)}}</span>
             </div>
             <!-- 音频模块 -->

+ 352 - 5
src/views/hzyb/chart/Detail.vue

@@ -71,6 +71,28 @@ const basicXAxis={
     },
     xDateFormat:'%Y-%m-%d'
 }
+//季节性图设置
+const seasonOptions = {
+    //默认颜色配置
+    colors:['#4B0082','#7FFFAA','#FF4500','#808000','#EEE8AA','#849EC1','#8A4294','#578B5A','#FDA8C7','#53B3FF','#999999','#000000','#FFDF0C','#FF0000','#0033FF'],
+    yAxis: {
+        lineWidth: 1,
+        lineColor: '#bfbfbf',
+        tickColor: '#bfbfbf',
+        offset: 0,
+        opposite: false,
+        reversed: false,
+        visible: true,
+        gridLineWidth: 0,
+        tickWidth: 1,
+        tickLength:5,
+        tickPosition: 'inside',
+        endOnTick: false,
+        startOnTick: false,
+        showLastLabel: true, //显示最后刻度值
+        tickPixelInterval: 50
+    }
+}
 
 
 // 获取用户信息
@@ -347,7 +369,7 @@ const getChartInfo=async (type)=>{
         // document.title=res.data.ChartInfo.ChartName
         
         // 设置highchart配置 ChartType: 1曲线图 2季节图:季节图中公历和农历数据结构不同
-        if(![2,7,10,11].includes(res.data.ChartInfo.ChartType)){
+        if(![2,7,10,11,14].includes(res.data.ChartInfo.ChartType)){
             if(type=='init'){
                 dateType.value=res.data.ChartInfo.DateType
                 startYear.value=res.data.ChartInfo.StartYear
@@ -387,6 +409,9 @@ const getChartInfo=async (type)=>{
         //雷达图
         res.data.ChartInfo.ChartType === 11 && initRadarData(res.data);
 
+        // 截面组合图
+        res.data.ChartInfo.ChartType === 14 && initSectionalCombinationChart(res.data);
+
         
 
         // 向小程序发送分享数据
@@ -455,6 +480,197 @@ const getCommonChartDetail=async ()=>{
 }
 getChartInfo('init')
 
+// 截面组合图初始化
+function initSectionalCombinationChart(data){
+    const {XDataList,UnitList,SeriesList,IsHeap}=data.DataResp
+    /* 主题样式*/
+    const chartTheme =  resData.value.ChartInfo.ChartThemeStyle ? JSON.parse(resData.value.ChartInfo.ChartThemeStyle) : null;
+      
+    let leftIndex = SeriesList.findIndex((item) => item.IsAxis===1);
+    let rightIndex = SeriesList.findIndex((item) => !item.IsAxis);
+    let rightTwoIndex = SeriesList.findIndex((item) => item.IsAxis ===2);
+    axisLimitData.leftMin=data.ChartInfo.LeftMin
+    axisLimitData.leftMax=data.ChartInfo.LeftMax
+    axisLimitData.rightTwoMin=data.ChartInfo.Right2Min
+    axisLimitData.rightTwoMax=data.ChartInfo.Right2Max
+    axisLimitData.rightMin=data.ChartInfo.RightMin
+    axisLimitData.rightMax=data.ChartInfo.RightMax
+    if(leftIndex!=-1) {
+        hasLeftAxis.value=true
+    }
+    if(rightIndex!=-1){
+        hasRightAxis.value=true
+    }
+    if(rightTwoIndex!=-1){
+        hasRightTwoAxis.value=true
+    }
+
+    //x轴
+    const xAxis={
+        categories:XDataList.map(_=>_.Name),
+        tickWidth: 1,
+        labels: {
+          style:{
+            ...chartTheme&&chartTheme.xAxisOptions.style
+          },
+        },
+        plotBands: setAxisPlotAreas(3),
+        plotLines: setAxisPlotLines(3)
+    }
+
+    let yAxis=[]
+    let seriesData=[]
+
+    //有右二轴时排个序 按照左 右 右2的顺序
+    const chartDataList = SeriesList.some(_ =>_.IsAxis===2) ? changeEdbOrder(SeriesList) : _.cloneDeep(SeriesList);
+    chartDataList.forEach((item,index)=>{
+        //轴位置值相同的下标
+        const sameSideIndex = chartDataList.findIndex(
+          (i) => i.IsAxis === item.IsAxis
+        );
+
+        const yTitleMap={
+          1:['LeftName','LeftNameEn'],
+          0:['RightName','RightNameEn'],
+          2:['RightTwoName','RightTwoNameEn'],
+        }
+
+        let minLimit = 0,maxLimit = 0
+        const limitMap = {
+            0:['rightMin','rightMax'],
+            1:['leftMin','leftMax'],
+            2:['rightTwoMin','rightTwoMax']
+        }
+        if(limitMap[item.IsAxis]){
+            minLimit = axisLimitData[`${limitMap[item.IsAxis][0]}`]||0
+            maxLimit = axisLimitData[`${limitMap[item.IsAxis][1]}`]||0
+        }
+
+        let yItem = {
+          ...basicYAxis,
+          title: {
+            text: UnitList[yTitleMap[item.IsAxis][0]],
+            style:{
+              ...chartTheme&&chartTheme.yAxisOptions.style
+            },
+            align: 'high',
+            rotation: 0,
+            y: -12,
+            // x: (item.IsAxis===0 && this.rightTwoIndex>-1) ? -chartData[this.rightTwoIndex].Unit.length*12 : 0,
+            textAlign: item.IsAxis===1 ? 'left' : 'right',
+            reserveSpace: false
+          },
+          labels: {
+            formatter: function (ctx) {
+              return ctx.value;
+            },
+            align: 'center',
+            x: [0,2].includes(item.IsAxis) ? 5 : -5,
+            style: {
+              ...chartTheme&&chartTheme.yAxisOptions.style,
+            }
+          },
+          opposite: [0,2].includes(item.IsAxis),
+          min: Number(minLimit),
+          max: Number(maxLimit),
+          tickWidth: 1,
+          visible: sameSideIndex === index,
+          plotBands: setAxisPlotAreas(item.IsAxis),
+          plotLines: setAxisPlotLines(item.IsAxis)
+        };
+
+        //堆叠图的yAxis必须一致 数据列所对应的y轴
+        let serie_yIndex = index;
+        if(IsHeap==1) {
+          // 类型为堆叠图时公用第一个指标y轴 
+          serie_yIndex =  0;
+        }
+
+        //数据列
+        let dataArr=item.DataList||[]
+        // 根据NoDataEdbIndex 将对应位置的值置为null
+        dataArr.forEach((i,index)=>{
+          if(item.NoDataEdbIndex.includes(index)){
+            dataArr[index]=null
+          }
+        })
+        //图表可配置的线条数就10条,第11条用第1条的配置,索引取下模
+		const lineIndex = chartTheme ? index%chartTheme.lineOptionList.length : index
+        let obj={
+          data:dataArr,
+          type: item.ChartStyle||'',
+          dashStyle: (chartTheme&&chartTheme.lineOptionList[lineIndex].dashStyle)||'Solid',
+          chartType:'linear',
+          yAxis: serie_yIndex,
+          name:item.SeriesName,
+          nameCh:item.SeriesName,
+          nameEn:item.SeriesNameEn,
+          color: item.ChartColor,
+          lineWidth: Number(item.ChartWidth)||(chartTheme&&chartTheme.lineOptionList[lineIndex].lineWidth),
+          borderWidth: 1,
+          borderColor: item.ChartColor,
+          marker: {//展示数据点
+            enabled:item.ChartStyle!='column'&&item.IsPoint?true:false,
+            radius: (chartTheme&&chartTheme.lineOptionList[lineIndex].radius)||5,
+          },
+          dataLabels: {//展示数值
+            enabled: item.IsNumber?true:false,
+            align:item.ChartStyle=='column'?'center':undefined,
+            y:item.ChartStyle=='column'?-20:0,
+            inside: item.ChartStyle=='column'?false:undefined,
+            crop: item.ChartStyle=='column'?false:true,
+          },
+          stacking:IsHeap==1?'normal':undefined,
+          zIndex: ['line','spline'].includes(item.ChartStyle) ? 1 : 0, //防止组合图曲线被遮住
+        }
+
+        yAxis.push(yItem)
+        seriesData.push(obj)
+    })
+    
+    console.log(seriesData);
+
+    const tooltip={
+        formatter:function () { 
+          let str=`<b>${this.points[0].x}</b>`
+          this.points.forEach(item=>{
+            const sObj=seriesData.find(_=>_.name===item.series.name)
+            str=str+`<br><span style="color:${item.color}">\u25CF</span>${sObj.name}:${item.y} ${sObj.unitNameCh}`
+          })
+          return str
+        },
+        formatterCh:function () { 
+          let str=`<b>${this.points[0].x}</b>`
+          this.points.forEach(item=>{
+            const sObj=seriesData.find(_=>_.name===item.series.name)
+            str=str+`<br><span style="color:${item.color}">\u25CF</span>${sObj.nameCh}:${item.y} ${sObj.unitNameCh}`
+          })
+          return str
+        },
+        formatterEn:function () { 
+          let str=`<b>${this.points[0].x}</b>`
+          this.points.forEach(item=>{
+            const sObj=seriesData.find(_=>_.name===item.series.name)
+            str=str+`<br><span style="color:${item.color}">\u25CF</span>${sObj.nameEn}:${item.y} ${sObj.unitNameEn}`
+          })
+          return str
+        },
+        shared:true
+    }
+    
+
+    chartData.value = {
+        title: {
+            text:''
+        },
+        tooltip,
+        series: seriesData,
+        yAxis: yAxis,
+        xAxis,
+    };
+
+      
+}
 
 //相关性图表设置
 const relevanceChartData=ref(null)
@@ -710,7 +926,7 @@ const setCommodityChart = () => {
     //x轴
     let xAxis = {
         ...scatterXAxis,
-        categories: commodityXData.value.map(_ => _.Name),
+        categories: commodityXData.value.filter(_=>_.IsHide===0).map(_ => _.Name),
         tickWidth: 1,
         labels: {
             style: {
@@ -787,8 +1003,9 @@ const setCommodityChart = () => {
                 
                 if(haveContract) {
                     // 利润曲线指标名
+                    const isEdb=commodityEdbList.value.some(_=>_.EdbInfoId===haveContract)
                     let edb_name = resData.value.ChartInfo.Source === 5 
-                    ? (index === 0 ? obj_item.NameList[index] : `${resData.value.DataResp.ProfitName}(${obj_item.NameList[index]})`)
+                    ? (isEdb? obj_item.NameList[index] : `${resData.value.DataResp.ProfitName}(${obj_item.NameList[index]})`)
                     : commodityEdbList.value.find(_ => _.EdbInfoId === obj_item.XEdbInfoIdList[index]).EdbName;
                     str+=`<b>${ edb_name }</b>`
 
@@ -831,8 +1048,15 @@ const filterInvalidData = (item)=> {
         return item
     }
     })
+    // 根据设置的x轴显示隐藏去除值
+    let temArr=[]
+    commodityXData.value.forEach((i,index)=>{
+        if(i.IsHide!==1){
+            temArr.push(arr[index])
+        }
+    })
 
-    return arr;
+    return temArr;
 }
 
 
@@ -1665,7 +1889,8 @@ const setStackOrCombinChart = data => {
             LatestValue:item.LatestValue,
             borderWidth: 1,
             borderColor: item.ChartColor,
-            ...predict_params
+            ...predict_params,
+            stacking:resData.value.ChartInfo.ChartType==6&&!resData.value.DataResp.IsHeap?undefined:'normal',
         }
         item.DataList = item.DataList || [];
         for (let i of item.DataList) {
@@ -1833,6 +2058,11 @@ const changeEdbOrder = (data) => {
     return [left_edbs,right_edbs,right_two_edbs].flat(Infinity);
 }
 
+//获取RGBA的透明度
+function parseRgbaColor(color='rgba(51, 51, 51, 1)'){
+    const arr = color.match(/(\d(\.\d+)?)+/g) || ['','','',1];
+    return parseFloat(arr[3]||1)
+}
 //设置季节图配置
 const setSeasonOpt=(data)=>{
     hasLeftAxis.value=true
@@ -1855,6 +2085,7 @@ const setSeasonOpt=(data)=>{
 
     // 跟颜色对应
     chartTheme && (chartTheme.lineOptionList=chartTheme.lineOptionList.reverse().slice(-chartDataHandle.length))
+    //常规左轴
     chartDataHandle.forEach((item,index)=>{
             //预测指标配置
         let predict_params =  data.EdbInfoCategoryType === 1 ? getSeasonPredictParams(item.CuttingDataTimestamp) : {};
@@ -1887,6 +2118,86 @@ const setSeasonOpt=(data)=>{
         series.push(seriesItem)
 
     })
+    //同期上下限/均线/标准差
+    const {MaxMinLimits={},SamePeriodAverage={},SamePeriodStandardDeviation={}} = resData.value.DataResp||{}
+    if(MaxMinLimits.IsShow&&MaxMinLimits.List&&MaxMinLimits.List.length){
+        let serieItem = {
+            type:'arearange',//上下限是一个范围
+            data:[],
+            name:MaxMinLimits.Legend||'同期上下限',
+            color:MaxMinLimits.Color||'#075EEE' ,
+            fillOpacity:parseRgbaColor(MaxMinLimits.Color||'')>0.75?0.75:parseRgbaColor(MaxMinLimits.Color||''), //透明度最高0.75 
+            visible:true,
+        }
+        MaxMinLimits.List.forEach(item=>{
+            serieItem.data.push([item.DataTimestamp,item.MinValue,item.MaxValue])
+        })
+        series.push(serieItem)
+    }
+    if(SamePeriodAverage.IsShow&&SamePeriodAverage.List){
+        let serieItem = {
+            type:'line',
+            data:[],
+            lineWidth:SamePeriodAverage.LineWidth,
+            dashStyle:SamePeriodAverage.LineType,
+            name:SamePeriodAverage.Legend||'同期均值',
+            color:SamePeriodAverage.Color||'#075EEE',
+            visible:true,
+        }
+        SamePeriodAverage.List.forEach(item=>{
+            serieItem.data.push([item.DataTimestamp,item.Value])
+        })
+        series.push(serieItem)
+    }
+    if(SamePeriodStandardDeviation.IsShow&&SamePeriodStandardDeviation.List){
+        let serieItem = {
+            type:'arearange',//标准差也是一个范围
+            data:[],
+            name:SamePeriodStandardDeviation.Legend||'同期标准差',
+            color:SamePeriodStandardDeviation.Color||'#075EEE',
+            fillOpacity:parseRgbaColor(SamePeriodStandardDeviation.Color||'')>0.75?0.75:parseRgbaColor(SamePeriodStandardDeviation.Color||''),
+            visible:true,
+        }
+        SamePeriodStandardDeviation.List.forEach(item=>{
+            serieItem.data.push([item.DataTimestamp,item.MinValue,item.MaxValue])
+        })
+        series.push(serieItem)
+    }
+    //右轴
+    const {RightAxis:SeasonRightConfig={}} = resData.value.DataResp||{}
+    if(SeasonRightConfig.IsShow){
+        //右轴的设置
+        let serieConfig = SeasonRightConfig.Style==='column'?{
+            //柱形
+            type:'column',
+            color:SeasonRightConfig.ChartColor,
+            visible:true,
+        }:{
+            //标记点
+            type:'spline',
+            lineWidth:SeasonRightConfig.LineWidth,
+            dashStyle:SeasonRightConfig.LineStyle,
+            color:SeasonRightConfig.IsConnected?SeasonRightConfig.LineColor:'rgba(255, 255, 255, 0)',//没有连线颜色设置为透明
+            marker:{
+                enabled:true,
+                symbol:SeasonRightConfig.Shape,
+                fillColor:SeasonRightConfig.ChartColor,
+                radius:SeasonRightConfig.Size
+            },
+            visible:true,
+        }
+        let serieItem = {
+            ...serieConfig,
+            name:SeasonRightConfig.Legend||'',
+            data:[],
+            yAxis:1,
+        }
+        const DataList = (SeasonRightConfig.IndicatorType===1?SeasonRightConfig.EdbInfoList[0].DataList:e.EdbInfoList[1]?.DataList)||[]
+        DataList.forEach(item=>{
+            serieItem.data.push([item.DataTimestamp,item.Value])
+        })
+        series.push(serieItem)
+    }
 
     yAxis=[{
         IsAxis:data.IsAxis,
@@ -1934,6 +2245,42 @@ const setSeasonOpt=(data)=>{
         // chartEdbInfo:item//指标数据
     }]
 
+    //如果有右轴,yAxis加上右轴
+    if(SeasonRightConfig.IsShow){
+        const rightEdb = (SeasonRightConfig.IndicatorType===1?SeasonRightConfig.EdbInfoList[0]:e.EdbInfoList[1])||{Unit:''}
+        //左轴同比:text为空或% 右轴指标:取指标单位
+        if(SeasonRightConfig.IndicatorType===1){
+            rightEdb.Unit = SeasonRightConfig.NumFormat===1?'%':''
+        }else{
+            rightEdb.Unit = e.EdbInfoList[1]&&(e.EdbInfoList[1].ConvertUnit||e.EdbInfoList[1].Unit)||''
+        }
+        yAxis.push({
+            ...seasonOptions.yAxis,
+            opposite: true,//右轴
+            labels: {
+                align: 'center',
+                y:5,
+                style: {
+                  ...chartTheme&&chartTheme.yAxisOptions.style
+                }
+              },
+              title: {
+                text: rightEdb.Unit||'',
+                style:{
+                  ...chartTheme&&chartTheme.yAxisOptions.style
+                },
+                align: 'high',
+                rotation: 0,
+                y: -5,
+                x: 0,
+                textAlign: 'right',
+                reserveSpace: false,
+              },
+              max: Number(rightEdb.MaxData||''),
+              min: Number(rightEdb.MinData||''),
+        })
+    }
+
     chartData.value.series=series
     chartData.value.yAxis=yAxis
     // chartData.value.rangeSelector=rangeSelector

+ 117 - 42
src/views/hzyb/report/ChapterDetail.vue

@@ -1,60 +1,93 @@
 <template>
 <van-pull-refresh v-model="loading" disabled style="min-height:100vh">
-  <div class="content-swipe" v-if="bannerDataList.length > 0">
+  <!-- <div class="content-swipe" v-if="bannerDataList.length > 0">
         <van-swipe class="my-swipe" :autoplay="4000" :show-indicators="false">
             <van-swipe-item v-for="item in bannerDataList" :key="item.id" @click="bannerSwiperHandler(item)">
                 <img :src="item.image_url_mobile" />
             </van-swipe-item>
         </van-swipe>
-  </div>
+  </div> -->
   <div class="chapter-detail-page" v-if="info" :style="{paddingBottom:$store.state.hzyb.audioData.url&&'80px'}">
     <div :class="['main-box',!info.auth_ok&&'main-box-noauth']">
       <!-- <div class="title">【第{{info.report_chapter_item.stage}}期 | {{info.report_chapter_item.classify_name_first}}  | {{info.report_chapter_item.type_name}}】{{info.report_chapter_item.title}}</div> -->
-      <div class="title">{{title}}</div>
-      <div class="flex time">
-        <span>FICC团队</span>
-        <span>{{formatTime(info.report_chapter_item.publish_time)}}</span>
-      </div>
-      <!-- 音频模块 -->
-      <AudioBox :audioData="audioData" v-if="info.report_chapter_item.video_url&&info.report_chapter_item.video_play_seconds>0"></AudioBox>
-      <div class="flex tips">
-        <div style="flex:1">
-          <div class="abstract" v-if="info.report_chapter_item.abstract">摘要:{{info.report_chapter_item.abstract}}</div>
-          <div>
-            <span>注:请务必阅读</span>
-            <span style="color: #e3b377; margin-left: 20px" @click="showDisclaimers = true">免责声明</span>
-            <span 
-              v-if="info.report_chapter_item.video_url&&info.report_chapter_item.video_play_seconds>0" 
-              style="float:right;background:#E3B377;color:#fff;border-radius:30px;padding:0 10px;font-size:0.9em" @click="handlePlayAudioBG"
-            >背景播放</span>
+      <div :style="{backgroundColor:info.report_chapter_item.canvas_color||''}">
+        <!-- 无版头板尾显示标题 -->
+        <template v-if="(!info.report_chapter_item.head_img) && (!info.report_chapter_item.end_img)">
+          <div class="title">{{title}}</div>
+          <div class="flex time">
+            <span>{{info.report_chapter_item.author}}</span>
+            <span>{{formatTime(info.report_chapter_item.publish_time)}}</span>
           </div>
+        </template>
+
+        <!-- 拼接版头 -->
+        <div class="html-head-img-box" v-if="info.auth_ok&&info.report_chapter_item.head_img">
+            <img :src="info.report_chapter_item.head_img" 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>
-      <div id="report-rich-content" class="rich-content" ref="richConBox">
-        <div v-if="info.auth_ok">
-          <ul>
-              <li v-for="item in realContent" :key="item" v-html="item"></li>
-          </ul>
-        </div>
-        <div v-html="info.report_chapter_item.content_sub" v-else ></div>
-        <!-- 隐藏的水印 -->
-        <div class="hide-watermark-box" v-if="userInfo">
-          <div v-for="item in 20" :key="item">{{userInfo.mobile}}</div>
+
+        <template v-if="!info.report_chapter_item.report_video_url">
+          <!-- 音频模块 -->
+          <AudioBox :audioData="audioData" v-if="info.report_chapter_item.video_url&&info.report_chapter_item.video_play_seconds>0"></AudioBox>
+        </template>
+        
+        <div class="flex tips">
+          <div style="flex:1">
+            <div class="abstract" v-if="info.report_chapter_item.abstract">摘要:{{info.report_chapter_item.abstract}}</div>
+            <div>
+              <span>注:请务必阅读</span>
+              <span style="color: #e3b377; margin-left: 20px" @click="showDisclaimers = true">免责声明</span>
+              <span 
+                v-if="!info.report_chapter_item.report_video_url&&info.report_chapter_item.video_url&&info.report_chapter_item.video_play_seconds>0" 
+                style="float:right;background:#E3B377;color:#fff;border-radius:30px;padding:0 10px;font-size:0.9em" @click="handlePlayAudioBG"
+              >背景播放</span>
+            </div>
+          </div>
         </div>
-      </div>
-      <!-- 指标数据模块 -->
-      <div class="ticker-wrap" v-if="tickerInfo">
-        <div class="top-title">{{tickerInfo.ticker_title.report_chapter_type_name}}数据表</div>
-        <div class="table-box">
-          <div class="table-row table-head">
-            <div class="table-item" v-for="item in tickerHead" :key="item.key">{{item.label}}</div>
+        <div id="report-rich-content" class="rich-content" ref="richConBox">
+          <div v-if="info.auth_ok">
+            <ul>
+                <li v-for="item in realContent" :key="item" v-html="item"></li>
+            </ul>
           </div>
-          <div class="table-row table-body" v-for="(tr,index) in tickerInfo.list" :key="tr.base_column_name">
-            <div :class="['table-item',index%2==0?'grey':'',tr[td.key]<0?'minus':'']" v-for="td in tickerHead" :key="td.key">{{tr[td.key]}}</div>
+          <div v-html="info.report_chapter_item.content_sub" v-else ></div>
+          <!-- 隐藏的水印 -->
+          <div class="hide-watermark-box" v-if="userInfo">
+            <div v-for="item in 20" :key="item">{{userInfo.mobile}}</div>
+          </div>
+        </div>
+        <!-- 指标数据模块 -->
+        <div class="ticker-wrap" v-if="tickerInfo">
+          <div class="top-title">{{tickerInfo.ticker_title.report_chapter_type_name}}数据表</div>
+          <div class="table-box">
+            <div class="table-row table-head">
+              <div class="table-item" v-for="item in tickerHead" :key="item.key">{{item.label}}</div>
+            </div>
+            <div class="table-row table-body" v-for="(tr,index) in tickerInfo.list" :key="tr.base_column_name">
+              <div :class="['table-item',index%2==0?'grey':'',tr[td.key]<0?'minus':'']" v-for="td in tickerHead" :key="td.key">{{tr[td.key]}}</div>
+            </div>
           </div>
+          <div v-if="tickerInfo.ticker_title.report_chapter_type_id ===26" style="text-align:center;font-weight:bold">注:与新加坡TSR20相关数据均取展示日期前一交易日数据</div>
+        </div>
+
+        <!-- 拼接版尾 -->
+        <div class="html-end-img-box" v-if="info.auth_ok&&info.report_chapter_item.end_img">
+            <img :src="info.report_chapter_item.end_img" 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 v-if="tickerInfo.ticker_title.report_chapter_type_id ===26" style="text-align:center;font-weight:bold">注:与新加坡TSR20相关数据均取展示日期前一交易日数据</div>
       </div>
+
+
       <!-- 无权限 -->
       <div class="no-auth-wrap" v-if="!info.auth_ok">
         <div class="apply-box" v-if="info.permission_check.type=='apply'">
@@ -231,7 +264,15 @@ export default {
       collectIcons,
 
       isReportPublishCancel:false,//报告取消发布
-      bannerDataList:[]
+      bannerDataList:[],
+
+      headImgStyle:null,//版头style
+      endImgStyle:null,//版尾style
+      layoutBaseInfo:{
+          研报标题:'',
+          研报作者:'',
+          创建时间:''
+      }
     };
   },
   beforeCreate(){
@@ -244,7 +285,7 @@ export default {
     this.chapterId=this.$route.query.chapterId
     this.fromPage=this.$route.query.fromPage||''
     this.getDetail()
-    this.getBannerList()
+    // this.getBannerList()
   },
   mounted(){
         $(document).on('click', '.rich-content img',function(event) {
@@ -337,6 +378,14 @@ export default {
       const res=await apiChapterDetail({report_chapter_id:Number(this.chapterId)})
       if(res.code===200){
         this.info=res.data
+
+        this.headImgStyle=res.data.report_chapter_item.head_style?JSON.parse(res.data.report_chapter_item.head_style):[]
+        this.endImgStyle=res.data.report_chapter_item.end_style?JSON.parse(res.data.report_chapter_item.end_style):[]
+        this.layoutBaseInfo['研报标题']=res.data.report_chapter_item.title
+        this.layoutBaseInfo['研报作者']=res.data.report_chapter_item.author
+        // 已发布已通过的报告才显示发布时间
+        this.layoutBaseInfo['创建时间']=moment(res.data.report_chapter_item.publish_time).format('YYYY.MM.DD HH:mm')
+
         this.audioData={
           auth_ok:res.data.auth_ok,
           video_name:res.data.report_chapter_item.video_name,
@@ -431,6 +480,19 @@ export default {
       target.style.background = "url(" + data + ") repeat";
     },
 
+    // 智能布局内容排版全部变成1个1行的顺排
+    formatSmartStyle() {
+      this.$nextTick(() =>{
+          $('.report-drag-item-wrap_child-wrap').css({
+              'flex-wrap': 'wrap',
+          });
+          $('.report-drag-item-wrap_child-wrap').children().css({
+              'flex': 'none',
+              'width': '100%'
+          });
+        })
+    },
+
     /*内容分割*/
     splitContentHandle(content) {
         content=addTokenToIframe(content,this.info.report_chapter_item.report_id,this.info.report_chapter_item.report_chapter_id)
@@ -439,11 +501,14 @@ export default {
         this.realContent = this.totalContent.slice(0,this.pageSize)
         this.total_page =  parseInt(this.totalContent.length / this.pageSize) + 1;
         console.log( this.totalContent,this.realContent,this.total_page)
+        this.formatSmartStyle()
     },
 
     /* 加载下一页内容 */
     loadContent() {
         this.realContent = this.realContent.concat(this.totalContent.slice(this.page_no*this.pageSize, (this.page_no + 1)*this.pageSize))
+
+        this.formatSmartStyle()
     },
 
     loadMoreHandle: _.throttle(function() {
@@ -977,4 +1042,14 @@ export default {
   }
 }
 
+.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
+    }
+}
 </style>

+ 145 - 40
src/views/hzyb/report/Detail.vue

@@ -1,15 +1,15 @@
 <template>
 <van-pull-refresh v-model="loading" disabled style="min-height:100vh">
-    <div class="content-swipe" v-if="bannerDataList.length > 0">
+    <!-- <div class="content-swipe" v-if="bannerDataList.length > 0">
         <van-swipe class="my-swipe" :autoplay="4000" :show-indicators="false">
             <van-swipe-item v-for="item in bannerDataList" :key="item.id" @click="bannerSwiperHandler(item)">
                 <img :src="item.image_url_mobile" />
             </van-swipe-item>
         </van-swipe>
-    </div>
+    </div> -->
     <div class="report-detail-page" @click="closeAttention" v-if="info" :style="{paddingBottom:$store.state.hzyb.audioData.url&&'80px'}">
         <!-- 晨报、周报章节 -->
-        <div class="chapter-list-wrap" v-if="['晨报','周报'].includes(info.report_info.classify_name_first)">
+        <div class="chapter-list-wrap" v-if="info.report_info.has_chapter&&info.report_detail_show_type===2">
             <div class="top-box" :style="'background-image:url(' + info.report_info.banner_url + ')'">
                 <div class="title">{{info.report_info.classify_name_first}}</div>
                 <div class="sub-title">{{info.report_info.title}}</div>
@@ -67,37 +67,84 @@
         <!-- 报告详情 -->
         <div :class="['main-box',!info.auth_ok&&'main-box-noauth']" v-else>
             <!-- <div class="title">【第{{info.report_info.stage}}期|{{info.report_info.classify_name_second}}】{{info.report_info.title}}</div> -->
-            <div class="title">{{title}}</div>
-            <div class="flex time">
-                <span>FICC团队</span>
-                <span>{{formatTime(info.report_info.publish_time)}}</span>
-            </div>
-            <!-- 音频模块 -->
-            <AudioBox :audioData="audioData" v-if="info.report_info.video_url&&info.report_info.video_play_seconds>0"></AudioBox>
-            <div class="flex tips">
-                <div style="flex:1">
-                    <div v-if="info.road_video_id">点击<span style="color: #e3b377;" @click="goVideoPage">查看视频</span></div>
-                    <div class="abstract" v-if="info.report_info.abstract">摘要:{{info.report_info.abstract}}</div>
-                    <div>
-                        <span>注:请务必阅读</span>
-                        <span style="color:#E3B377;margin-left:20px" @click="showDisclaimers=true">免责声明</span>
-                        <span 
-                            v-if="info.report_info.video_url&&info.report_info.video_play_seconds>0" 
-                            style="float:right;background:#E3B377;color:#fff;border-radius:30px;padding:0 10px;font-size:0.9em" @click="handlePlayAudioBG"
-                        >背景播放</span>
+            <div :style="{backgroundColor:info.report_info.canvas_color||''}">
+                <!-- 无版头板尾显示标题 -->
+                <template v-if="(!info.report_info.head_img) && (!info.report_info.end_img)">
+                    <div class="title">{{title}}</div>
+                    <div class="flex time">
+                        <span>{{info.report_info.author}}</span>
+                        <span>{{formatTime(info.report_info.publish_time)}}</span>
+                    </div>
+                </template>
+
+                <!-- 拼接版头 -->
+                <div class="html-head-img-box" v-if="info.auth_ok&&info.report_info.head_img">
+                    <img :src="info.report_info.head_img" 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>
-            <div id="report-rich-content" class="rich-content" style="position: relative;" ref="richConBox">
-                <div v-if="info.auth_ok">
-                    <ul>
-                        <li v-for="item in realContent" :key="item" v-html="item"></li>
-                    </ul>
+
+                
+                <!-- 音频模块 -->
+                <AudioBox :audioData="audioData" v-if="info.report_info.video_url&&info.report_info.video_play_seconds>0"></AudioBox>
+                <div class="flex tips">
+                    <div style="flex:1">
+                        <div v-if="info.road_video_id">点击<span style="color: #e3b377;" @click="goVideoPage">查看视频</span></div>
+                        <div class="abstract" v-if="info.report_info.abstract">摘要:{{info.report_info.abstract}}</div>
+                        <div>
+                            <span>注:请务必阅读</span>
+                            <span style="color:#E3B377;margin-left:20px" @click="showDisclaimers=true">免责声明</span>
+                            <span 
+                                v-if="info.report_info.video_url&&info.report_info.video_play_seconds>0" 
+                                style="float:right;background:#E3B377;color:#fff;border-radius:30px;padding:0 10px;font-size:0.9em" @click="handlePlayAudioBG"
+                            >背景播放</span>
+                        </div>
+                    </div>
                 </div>
-                <div v-html="info.report_info.content_sub" v-else></div>
-                <!-- 隐藏的水印 -->
-                <div class="hide-watermark-box" v-if="userInfo">
-                    <div v-for="item in 20" :key="item">{{userInfo.mobile}}</div>
+                <div id="report-rich-content" class="rich-content" style="position: relative;" ref="richConBox">
+                    <!-- 单人报告 -->
+                    <template v-if="info.auth_ok">
+                        <div v-if="!info.report_info.has_chapter">
+                            <ul>
+                                <li v-for="item in realContent" :key="item" v-html="item"></li>
+                            </ul>
+                        </div>
+                        
+                        <!-- 展示拼接的章节报告 -->
+                        <template v-if="info.report_info.has_chapter && info.report_detail_show_type===1">
+                            <div 
+                                class="chapter-concat-item" 
+                                v-for="chapter in info.report_chapter_list"
+                                :key="chapter.report_chapter_id"
+                            >
+                                <div class="chapter-title">
+                                    <h3 class="chapter-title-text">{{chapter.title}}</h3>
+                                </div>
+                                <div class="html-cont" v-html="chapter.content"></div>
+                            </div>
+                        </template>
+                    </template>
+
+                    <div v-html="info.report_info.content_sub" v-else></div>
+                    <!-- 隐藏的水印 -->
+                    <div class="hide-watermark-box" v-if="userInfo">
+                        <div v-for="item in 20" :key="item">{{userInfo.mobile}}</div>
+                    </div>
+                </div>
+
+                <!-- 拼接版尾 -->
+                <div class="html-end-img-box" v-if="info.auth_ok&&info.report_info.end_img">
+                    <img :src="info.report_info.end_img" 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>
 
@@ -141,7 +188,7 @@
         </div>
         
         <!-- 留言点赞模块 -->
-        <div id="messgaeBoardCont" v-if="info.auth_ok && !['晨报','周报'].includes(info.report_info.classify_name_first)">
+        <div id="messgaeBoardCont" v-if="info.auth_ok && (!info.report_info.has_chapter||info.report_detail_show_type===1)">
             <LeaveMessage
                 :info="info"
                 @like_change="giveOrCancelLike"
@@ -253,7 +300,15 @@ export default {
 
             showAttention:false,//是否显示配置播放清单提示
             isReportPublishCancel:false,//报告取消发布
-            bannerDataList:[]
+            bannerDataList:[],
+
+            headImgStyle:null,//版头style
+            endImgStyle:null,//版尾style
+            layoutBaseInfo:{
+                研报标题:'',
+                研报作者:'',
+                创建时间:''
+            }
         }
     },
     beforeCreate(){
@@ -267,8 +322,7 @@ export default {
         this.fromPage = this.$route.query.fromPage || ''
         this.getDetail()
         this.getUserInfo()
-        this.getBannerList()
-        console.log(11111111);
+        // this.getBannerList()
     },
     mounted(){
         $(document).on('click', '.rich-content img',function(event) {
@@ -402,9 +456,16 @@ export default {
             const res=await apiReportDetail({report_id:Number(this.reportId)})
             if(res.code===200){
                 this.info=res.data
+
+                this.headImgStyle=res.data.report_info.head_style?JSON.parse(res.data.report_info.head_style):[]
+                this.endImgStyle=res.data.report_info.end_style?JSON.parse(res.data.report_info.end_style):[]
+                this.layoutBaseInfo['研报标题']=res.data.report_info.title
+                this.layoutBaseInfo['研报作者']=res.data.report_info.author
+                // 已发布已通过的报告才显示发布时间
+                this.layoutBaseInfo['创建时间']=moment(res.data.report_info.publish_time).format('YYYY.MM.DD HH:mm')
                 this.audioData={
                     auth_ok:res.data.auth_ok,
-                    video_name:res.data.report_info.video_name,
+                    video_name:res.data.report_info.video_name||`${res.data.report_info.title}(${moment(res.data.report_info.publish_time).format('MMDD')})`,
                     video_play_seconds:res.data.report_info.video_play_seconds,
                     video_url:res.data.report_info.video_url,
                     video_img:res.data.report_info.video_img
@@ -440,15 +501,14 @@ export default {
                 }
 
                 // 处理报告标题数据
-                if(!['晨报','周报'].includes(res.data.report_info.classify_name_first)){
-                    //【第{{info.report_info.stage}}期|{{info.report_info.classify_name_second}}】{{info.report_info.title}}
+                // if(!this.info.report_info.has_chapter){
                     const  time=moment(res.data.report_info.publish_time).format('MMDD')
                     if(res.data.report_info.classify_name_second==res.data.report_info.title){
                         this.title=`【第${res.data.report_info.stage}期】${res.data.report_info.title}(${time})`
                     }else{
                         this.title=`【第${res.data.report_info.stage}期|${res.data.report_info.classify_name_second}】${res.data.report_info.title}(${time})`
                     }
-                }
+                // }
 
                 //向小程序发送分享数据
                 //处理分享标题
@@ -463,7 +523,7 @@ export default {
                 }else{
                     shareTitle=res.data.report_info.title
                     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>`
-                    if(['晨报','周报'].includes(res.data.report_info.classify_name_first)){
+                    if(this.info.report_info.has_chapter){
                         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>`
                     }
                 }
@@ -524,6 +584,19 @@ export default {
             target.style.background = "url(" + data + ") repeat";
         },
 
+        // 智能布局内容排版全部变成1个1行的顺排
+        formatSmartStyle() {
+            this.$nextTick(() =>{
+                $('.report-drag-item-wrap_child-wrap').css({
+                    'flex-wrap': 'wrap',
+                });
+                $('.report-drag-item-wrap_child-wrap').children().css({
+                    'flex': 'none',
+                    'width': '100%'
+                });
+            })
+        },
+
         /*内容分割*/
         splitContentHandle(content) {
             content=addTokenToIframe(content,this.reportId,0)
@@ -531,11 +604,13 @@ export default {
             this.totalContent = arr.map(_ => _+'</p>');
             this.realContent = this.totalContent.slice(0,this.pageSize)
             this.total_page =  parseInt(this.totalContent.length / this.pageSize) + 1;
+            this.formatSmartStyle()
         },
 
         /* 加载下一页内容 */
         loadContent() {
             this.realContent = this.realContent.concat(this.totalContent.slice(this.page_no*this.pageSize, (this.page_no + 1)*this.pageSize))
+            this.formatSmartStyle()
         },
 
         loadMoreHandle: _.throttle(function() {
@@ -1114,4 +1189,34 @@ export default {
   }
 }
 
+.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
+    }
+}
+.chapter-concat-item {
+    padding: 20px 0;
+    .chapter-title {
+        display: flex;
+        align-items: center;
+        font-size: 30px;
+        .type {
+            height: fit-content;
+            display: inline-block;
+            color: #fff;
+            padding: 10px 20px;
+            background-color: #E6A23C;
+            border-radius: 8px; 
+            margin-right: 20px;
+        }
+        .chapter-title-text {
+            font-size: 30px;
+        }
+    }
+}
 </style>