jwyu 2 年之前
父節點
當前提交
7ec0a3cd52

+ 1 - 1
src/api/http.js

@@ -41,7 +41,7 @@ _axios.interceptors.response.use(
     }
     if(data.code===401){//token失效
       ElMessage.error('请重新登录')
-      wx.miniProgram.switchTab({url: '/pages/activity/activity'})
+      wx.miniProgram.switchTab({url: '/pages/report/report'})
     }
     if(data.code!==200&&data.code!==403&&data.code!==4001&&data.code!==401){
       ElMessage.error(data.msg)

+ 1 - 2
src/api/report.js

@@ -47,7 +47,7 @@ export const apiReportSearch=params=>{
  * 分类数据
  */
 export const apiReportClassify=()=>{
-    return get('/classify/ficc',{})
+    return get('/pc/classify',{})
 }
 
 /**
@@ -62,7 +62,6 @@ export const apiReportList=params=>{
 /**
  * 二级分类列表
  * @param classify_id_first
- * @param classify_id_second
  */
 export const apiSubClassifyList=params=>{
     return get('/classify/simple/list',params)

二進制
src/assets/tag.png


+ 6 - 0
src/layout/component/Header.vue

@@ -204,6 +204,8 @@ const goBack=()=>{
       <div id="chart-in-head"></div>
       <!-- 研报首页搜索 -->
       <div id="reportIndex-in-head"></div>
+      <!-- 报告列表搜索与筛选 -->
+      <div id="reportlist-in-head"></div>
     </div>
   </div>
 </template>
@@ -274,6 +276,10 @@ const goBack=()=>{
       float: right;
       height: 100%;
     }
+    #reportlist-in-head{
+      float: right;
+      height: 100%;
+    }
     
   }
 }

+ 35 - 2
src/router/index.js

@@ -68,7 +68,7 @@ const routes=[
   {
     path:'/report',
     name:'Report',
-    redirect: '/report/list',
+    redirect: '/report/index',
     component: () => import("@/layout/Index.vue"),
     meta: {
       title:"研报"
@@ -76,7 +76,7 @@ const routes=[
     children:[
       {
         path: "index",
-        name: "ReportList",
+        name: "ReportIndex",
         component: () => import("@/views/report/Index.vue"),
         meta: {
           title: "研报首页",
@@ -128,6 +128,39 @@ const routes=[
           hasBack:true
         },
       },
+      {
+        path: "list",
+        name: "ReportList",
+        component: () => import("@/views/report/List.vue"),
+        meta: {
+          title: "报告列表",
+          keepAlive:false,
+          isRoot:false,
+          hasBack:true
+        },
+      },
+      {
+        path: "specialcolumnlist",
+        name: "ReportSpecialColumnList",
+        component: () => import("@/views/report/specialColumn/List.vue"),
+        meta: {
+          title: "专栏列表",
+          keepAlive:false,
+          isRoot:false,
+          hasBack:true
+        },
+      },
+      {
+        path: "specialcolumndetail",
+        name: "ReportSpecialColumnDetail",
+        component: () => import("@/views/report/specialColumn/Detail.vue"),
+        meta: {
+          title: "专栏详情",
+          keepAlive:false,
+          isRoot:false,
+          hasBack:true
+        },
+      },
     ]
   },
   {

+ 2 - 1
src/store/index.js

@@ -13,7 +13,8 @@ export default createStore({
       loginTop:'https://hzstatic.hzinsights.com/static/icon/hzyb/login_top_img.png',
       idCardExp:'https://hzstatic.hzinsights.com/static/icon/hzyb/idcard_exp.jpg',
       defaultAvatar:'https://hzstatic.hzinsights.com/static/icon/hzyb/default_avatar.png',
-      ficcServiceImg:'https://hzstatic.hzinsights.com/static/icon/hzyb/ficc_service_pc.png'
+      ficcServiceImg:'https://hzstatic.hzinsights.com/static/icon/hzyb/ficc_service_pc.png',
+      specialColumnBanner:'https://hzstatic.hzinsights.com/static/icon/hzyb/special_column_banner_pc.png'
     },
     token:token,
     userInfo:null,

+ 8 - 4
src/style/global.scss

@@ -98,20 +98,20 @@ img {
 }
 
 .self-elmessage-confirm-btn {
-  background-color: #DAB37C;
+  background-color: #F3A52F;
   color: #fff;
   border: none;
   width: 90px;
 
   &:hover {
-    background-color: #DAB37C;
+    background-color: #F3A52F;
   }
 }
 
 .self-elmessage-cancel-btn {
   background-color: #fff;
-  border-color: #DAB37C;
-  color: #DAB37C;
+  border-color: #F3A52F;
+  color: #F3A52F;
   width: 90px;
 
   &:hover {
@@ -227,6 +227,10 @@ img {
         padding-top: 16px;
         padding-bottom: 12px;
         border-bottom: 1px solid #F2F2F2;
+        cursor: pointer;
+        &:hover{
+          color: #F3A52F;
+        }
 
         .title {
           font-weight: bold;

+ 0 - 1
src/views/chart/List.vue

@@ -132,7 +132,6 @@ onMounted(()=>{
                         </div>
                     </template>
                 </el-popover>
-                
             </div>
         </teleport>
     </template>

+ 31 - 1
src/views/report/ChapterDetail.vue

@@ -1,5 +1,5 @@
 <script setup>
-import {ref} from 'vue'
+import {ref,onMounted} from 'vue'
 import {apiChapterDetail,apiChapterTickerValue} from '@/api/report'
 import moment from 'moment';
 import AudioBox from './components/AudioBox.vue'
@@ -89,6 +89,28 @@ const getChapterReportDetail=async ()=>{
 getChapterReportDetail()
 
 
+let preViewImgs=ref([])
+let preViewImgIndex=ref(0)
+let showPreViewImg=ref(false)
+
+onMounted(()=>{
+    $(document).on('click', '.rich-content img',function(event) {
+	    let imgArray = [];
+	    let curImageSrc = $(this).attr('src');
+		let oParent = $(this).parent();
+		if (curImageSrc && !oParent.attr('href')) {
+            if(preViewImgs.value.length===0){
+                $('.rich-content img').each(function(index, el) {
+                    let itemSrc = $(this).attr('src');
+                    imgArray.push(itemSrc);
+                });
+                preViewImgs.value=imgArray
+            }
+            preViewImgIndex.value=preViewImgs.value.indexOf(curImageSrc)||0
+            showPreViewImg.value=true
+	    }
+    })
+})
 
 </script>
 
@@ -186,6 +208,14 @@ getChapterReportDetail()
             </div>
         </div>
     </div>
+
+    <!-- 图片预览 -->
+    <el-image-viewer
+        v-if="showPreViewImg"
+        :initial-index="preViewImgIndex"
+        @close="showPreViewImg=false"
+        :url-list="preViewImgs"
+    />
 </template>
 
 <style lang="scss" scoped>

+ 84 - 1
src/views/report/Classify.vue

@@ -1,10 +1,70 @@
 <script setup>
+import {ref} from 'vue'
+import {apiReportClassify} from '@/api/report'
+import { useRouter } from 'vue-router';
+
+const router=useRouter()
+
+
+// 获取列表数据
+let list=ref([])
+const getClassifyList=async ()=>{
+    const res=await apiReportClassify()
+    if(res.code===200){
+        list.value=res.data
+    }
+}
+getClassifyList()
+
+// 跳转下一级
+const goNext=(item)=>{
+    // redirect_type : 跳转页面类型:1专栏列表,2报告列表,3专栏详情
+    if(item.redirect_type===1){
+        router.push({
+            path:'/report/specialcolumnlist',
+            query:{
+                classifyId:item.classify_id_first,
+                classifyName:item.classify_name_first
+            }
+        })
+    }
+    if(item.redirect_type===2){
+        router.push({
+            path:'/report/list',
+            query:{
+                classifyId:item.classify_id_first,
+                classifyName:item.classify_name_first
+            }
+        })
+    }
+    if(item.redirect_type===3){
+        router.push({
+            path:'/report/specialcolumndetail',
+            query:{
+                columnId:item.classify_id_second
+            }
+        })
+    }
+
+
+}
 
 </script>
 <template>
     <div class="report-classify-page">
         <img :src="$store.state.globalImgUrls.ficcServiceImg" alt=""  class="top-banner">
-        
+        <div class="list-wrap">
+            <el-row :gutter="20">
+                <el-col :span="6" v-for="item in list" :key="item.classify_name_first">
+                    <div class="item" @click="goNext(item)" :style="'background-image:url('+item.back_img_url+')'">
+                        <div class="con">
+                            <div style="font-size: 18px;font-weight: bold;margin-bottom: 5px;">{{item.classify_name_first}}</div>
+                            <span style="margin-left: -7px;font-size: 14px;color: #666;">【FICC | {{item.latest}} 期】</span>
+                        </div>
+                    </div>
+                </el-col>
+            </el-row>
+        </div>
     </div>
 </template>
 
@@ -16,5 +76,28 @@
         width: 100%;
         display: block;
     }
+    .list-wrap{
+        .item{
+            padding-top: 42%;
+            border: 1px solid #ECECEC;
+            border-radius: 4px;
+            margin-bottom: 20px;
+            background-size: cover;
+            background-repeat: no-repeat;
+            position: relative;
+            cursor: pointer;
+            .con{
+                position: absolute;
+                top: 0;
+                left: 0;
+                right: 0;
+                bottom: 0;
+                padding: 0 20px;
+                display: flex;
+                flex-direction: column;
+                justify-content: center;
+            }
+        }
+    }
 }
 </style>

+ 88 - 74
src/views/report/Detail.vue

@@ -1,5 +1,5 @@
 <script setup>
-import {ref} from 'vue'
+import {onMounted, ref} from 'vue'
 import moment from 'moment';
 import AudioBox from './components/AudioBox.vue'
 import {apiReportDetail} from '@/api/report'
@@ -31,6 +31,51 @@ const getReportDetail=async ()=>{
 }
 getReportDetail()
 
+
+let preViewImgs=ref([])
+let preViewImgIndex=ref(0)
+let showPreViewImg=ref(false)
+
+onMounted(()=>{
+    $(document).on('click', '.rich-content img',function(event) {
+	    let imgArray = [];
+	    let curImageSrc = $(this).attr('src');
+		let oParent = $(this).parent();
+		if (curImageSrc && !oParent.attr('href')) {
+            if(preViewImgs.value.length===0){
+                $('.rich-content img').each(function(index, el) {
+                    let itemSrc = $(this).attr('src');
+                    imgArray.push(itemSrc);
+                });
+                preViewImgs.value=imgArray
+            }
+            preViewImgIndex.value=preViewImgs.value.indexOf(curImageSrc)||0
+            showPreViewImg.value=true
+	    }
+    })
+})
+
+// 设置章节列表tag颜色
+const getTagColor=(str)=>{
+    if( str.includes('多')||str.includes('强')||str.includes('反弹') ){
+        return "#DF6051";
+    }else if( str.includes('空')||str.includes('调整') ){
+        return "#6FC5B4";
+    }else{
+        return "#009fe6";
+    }
+}
+
+// 跳转章节详情
+const goChapterDetail=(item)=>{
+    router.push({
+        path:'/report/chapterdetail',
+        query:{
+            chapterId:item.report_chapter_id
+        }
+    })
+}
+
 </script>
 
 <template>
@@ -39,35 +84,22 @@ getReportDetail()
             <div class="content-box">
                 <!-- 晨报、周报章节 -->
                 <div class="chapter-list-wrap" v-if="['晨报','周报'].includes(info.report_info.classify_name_first)">
-                    <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>
-                        <div class="flex top-bot">
-                            <div class="flex time-box">
-                                <div class="day">{{formatChapterTime(info.report_info.publish_time,'day')}}</div>
-                                <div>
-                                    <div>{{formatChapterTime(info.report_info.publish_time,'week')}}</div>
-                                    <div>{{formatChapterTime(info.report_info.publish_time,'year-month')}}</div>
-                                </div>
-                            </div>
-                            <div class="num">第{{info.report_info.stage}}期</div>
-                        </div>
-                    </div>
+                    <div class="top-box" :style="'background-image:url(' + info.report_info.banner_url + ')'"></div>
                     <div class="list-box">
-                        <div class="flex item" v-for="item in chapterList" :key="item.report_chapter_id" @click="goChapterDetail(item)">
-                            <van-image class="img" :src="item.report_chapter_type_thumb" mode="aspectFill" />
+                        <div class="flex item" v-for="item in info.report_chapter_list" :key="item.report_chapter_id" @click="goChapterDetail(item)">
+                            <el-image class="img" :src="item.report_chapter_type_thumb" fit="cover" />
                             <div class="con">
                                 <div class="title">
                                     {{item.report_chapter_type_name}} 
-                                    <text class="tag" :style="{backgroundColor:getTagColor(tag)}" v-for="tag in item.trend.split(',')" :key="tag">{{tag}}</text>
+                                    <span class="tag" :style="{backgroundColor:getTagColor(tag)}" v-for="tag in item.trend.split(',')" :key="tag">{{tag}}</span>
                                 </div>
                                 <div class="van-multi-ellipsis--l2 sub-title">{{item.title}}</div>
-                                <div class="update-time">更新至:{{formatChapterTime(item.publish_time,'year-month-day')}}</div>
+                                <div class="update-time">更新至:{{moment(item.publish_time).format('YYYY-MM-DD')}}</div>
                             </div>
                         </div>
                     </div>
                     <!-- 无权限 -->
-                    <div class="no-auth-box" v-if="!info.auth_ok">
+                    <div class="no-auth-wrap" v-if="!info.auth_ok">
                         <img class="img" src="https://hzstatic.hzinsights.com/static/icon/hzyb/activity_no_auth.png" mode="widthFix" />
                         <div class="apply-box" v-if="info.permission_check.type=='apply'">
                             <div>您暂无权限查看报告,若想查看请申请开通</div>
@@ -76,7 +108,6 @@ getReportDetail()
                         <div class="apply-box" v-else>
                             <div>您暂无权限查看报告 </div>
                             <div>若想查看请联系对口销售:{{info.permission_check.name}}</div>
-                            <a class="btn" :href="'tel:'+info.permission_check.mobile" tag="div">立即联系</a>
                         </div>
                     </div>
                 </div>
@@ -108,13 +139,12 @@ getReportDetail()
                         </div>
                         <div class="apply-box" v-else>
                             <div>您暂无权限查看报告 </div>
-                            <div>若想查看请联系对口销售:{{info.permission_check.name}}</div>
-                            <a class="btn" :href="'tel:'+info.permission_check.mobile" tag="div">立即联系</a>
+                            <div>若想查看请联系对口销售:{{info.permission_check.name}}--{{info.permission_check.mobile}}</div>
                         </div>
                     </div>
                 </div>
             </div>
-            <div class="right-aside-box">
+            <div class="right-aside-box" v-if="info.auth_ok">
                 <div class="fix-top">
                 <div class="share-box">
                     <div class="label">分享</div>
@@ -151,6 +181,14 @@ getReportDetail()
             </div>
         </div>
     </div>
+
+    <!-- 图片预览 -->
+    <el-image-viewer
+        v-if="showPreViewImg"
+        :initial-index="preViewImgIndex"
+        @close="showPreViewImg=false"
+        :url-list="preViewImgs"
+    />
 </template>
 
 <style lang="scss" scoped>
@@ -217,62 +255,40 @@ getReportDetail()
             }
         }
     }
+    .no-auth-wrap{
+        text-align: center;
+        color: #F3A52F;
+        margin-top: -140px;
+        padding-top: 140px;
+        min-height: 200px;
+        background: linear-gradient(360deg, #FFFFFF 60%, rgba(255, 255, 255, 0) 88%);
+        .btn{
+            width: 218px;
+            margin-left: auto;
+            margin-right: auto;
+            margin-top: 20px;
+        }
+    }
     
     .chapter-list-wrap{
         .top-box{
-            height: 418px;
+            height: 140px;
             background-color: rgba($color: #000000, $alpha: 0.7);
             background-size: cover;
-            color: #fff;
             position: relative;
-            .title{
-                text-align: center;
-                font-size: 34px;
-                font-weight: 600;
-                padding-top: 78px;
-            }
-            .sub-title{
-                font-size: 32px;
-                text-align: center;
-                width: 70%;
-                margin-left: auto;
-                margin-right: auto;
-            }
-            .top-bot{
-                position: absolute;
-                bottom: 70px;
-                left: 34px;
-                right: 34px;
-                justify-content: space-between;
-                align-items: flex-end;
-                .time-box{
-                    align-items: center;
-                    font-size: 24px;
-                    .day{
-                        font-size: 32px;
-                        border-right: 1px solid #fff;
-                        padding-right: 15px;
-                        margin-right: 15px;
-                    }
-                }
-                
-            }
+            margin-bottom: 20px;
         }
         .list-box{
-            margin-top: -50px;
-            border-top-left-radius: 40px;
-            border-top-right-radius: 40px;
             min-height: 100px;
             background-color: #fff;
             position: relative;
-            z-index: 2;
             .item{
-                padding: 30px 34px;
+                align-items: center;
+                padding: 20px 0;
                 border-bottom: 1px solid #E5E5E5;
                 .img{
-                    width: 104px;
-                    height: 104px;
-                    // background-color: #f5f5f5;
+                    width: 55px;
+                    height: 55px;
                     flex-shrink: 0;
                     margin-right: 20px;
                 }
@@ -280,31 +296,29 @@ getReportDetail()
                     flex: 1;
                     position: relative;
                     .title{
-                        font-size: 28px;
-                        color: #57768D;
+                        margin-bottom: 8px;
                         font-weight: bold;
-                        margin-bottom: 5px;
                         .tag{
-                            font-size: 20px;
+                            font-size: 12px;
                             color: #fff;
                             font-weight: normal;
                             display: inline-block;
                             background-color: #1E88E5;
-                            line-height: 37px;
+                            line-height: 20px;
                             padding: 0 6px;
                             border-radius: 4px;
                             margin-left: 10px;
                         }
                     }
                     .sub-title{
-                        color: #999;
+                        color: #333;
                     }
                     .update-time{
                         position: absolute;
                         top: 0;
                         right: 0;
-                        color: #D4D4D4;
-                        font-size: 20px;
+                        color: #999999;
+                        font-size: 14px;
                     }
                 }
             }

+ 30 - 6
src/views/report/Index.vue

@@ -1,18 +1,22 @@
 <script setup>
 import { apiReportIndexPageAuthList, apiReportIndexPageList,apiReportIndexNewbanner } from '@/api/report'
 import { onMounted, reactive, ref } from "vue"
+import Search from "@/components/Search.vue"
+import SelfList from '@/components/SelfList.vue'
 import { useRouter } from "vue-router"
+import { ElMessageBox } from 'element-plus'
 import moment from 'moment'
 import 'moment/dist/locale/zh-cn'
 moment.locale('zh-cn')
 
-import Search from "@/components/Search.vue"
-import SelfList from '@/components/SelfList.vue'
-
 const router = useRouter()
 
 
 // 获取顶部权限分类数据
+let authData=reactive({
+  isBuy:false,
+  contactInfo:null
+})
 let firstTypeList = ref([])//一级分类数据
 let subTypeList = ref([])//二级分类数据
 let selectFirstType = ref('')//选择的一级分类
@@ -22,6 +26,9 @@ const getTopPermissionList = async () => {
   if (res.code === 200) {
     firstTypeList.value = res.data.permission_list.filter(item => item.sort != 100000)
     clickFirstType(firstTypeList.value[0])
+  
+    authData.isBuy=res.data.check_flag
+    authData.contactInfo=res.data.contact_info
   }
 }
 getTopPermissionList()
@@ -91,6 +98,24 @@ const clickSubType = (item) => {
   reportState.page = 1
   reportState.finished = false
   getReportList()
+  handleShowAuthData(item)
+}
+
+// 判断是否要为已购用户且点击的品种没有权限
+const handleShowAuthData=(e)=>{
+  if(authData.isBuy){
+    if(!e.auth_ok){
+      const  htmlStr=`您暂无该品种权限,若想查看请联系对口销售--${authData.contactInfo.name}:${authData.contactInfo.mobile}`
+      ElMessageBox({
+        title:'温馨提醒',
+        message:htmlStr,
+        center: true,
+        dangerouslyUseHTMLString: true,
+        confirmButtonText:'知道了',
+        confirmButtonClass:'self-elmessage-confirm-btn'
+      })
+    }
+  }
 }
 
 // 列表加载更多
@@ -159,7 +184,7 @@ const formatDate=(e)=>{
       ></Search>
     </teleport>
   </template>
-  <div class="hasrightaside-box report-list-page">
+  <div class="hasrightaside-box report-index-page">
     <div class="content-box report-main">
       <div class="top-nav-wrap">
         <div class="flex first-nav">
@@ -247,7 +272,7 @@ const formatDate=(e)=>{
 $bg-color: #f6f6f6;
 $active-color: #f3a52f;
 
-.report-list-page {
+.report-index-page {
   position: relative;
   .report-main {
     .top-nav-wrap {
@@ -260,7 +285,6 @@ $active-color: #f3a52f;
       // min-width: 880px;
       // max-width: 1240px;
       width: 100%;
-      border-bottom: 1px solid #f2f2f2;
       .first-nav {
         .item {
           width: 140px;

+ 269 - 0
src/views/report/List.vue

@@ -0,0 +1,269 @@
+<script setup>
+import {ref,onMounted, reactive} from 'vue'
+import moment from 'moment';
+import { apiSubClassifyList , apiReportList } from '@/api/report'
+import Search from '@/components/Search.vue'
+import SelfList from '@/components/SelfList.vue'
+import { useRoute, useRouter } from 'vue-router';
+
+const route=useRoute()
+const router=useRouter()
+
+let classifyId=ref(route.query.classifyId||0) //一级分类id
+
+document.title=decodeURIComponent(route.query.classifyName)
+
+// 获取筛选项(二级分类)
+let filterList=ref([])
+let selectSubClassifyId=ref(0)
+const getFilterList=async ()=>{
+    const res=await apiSubClassifyList({classify_id_first:Number(classifyId.value)})
+    if(res.code===200){
+        filterList.value=res.data||[]
+    }
+}
+getFilterList()
+
+// 获取报告列表
+let reportState=reactive({
+    list:[],
+    page:1,
+    pageSize:20,
+    loading:false,
+    finished:false,
+    searchVal:''
+})
+const getReportList=async ()=>{
+    const res=await apiReportList({
+        classify_id_first:Number(classifyId.value),
+        classify_id_second:Number(selectSubClassifyId.value),
+        key_word:reportState.searchVal,
+        current_index:reportState.page,
+        page_size:reportState.pageSize
+    })
+    if(res.code===200){
+        let arr=res.data.list||[]
+        reportState.list=[...reportState.list,...arr]
+        if(res.data.paging.is_end){
+          reportState.finished=true
+        }
+    }
+}
+getReportList()
+
+// 加载更多列表数据
+const onLoad=()=>{
+    reportState.page++
+    getReportList()
+}
+
+// 点击筛选项
+const handleClickFilterItem=(item)=>{
+    reportState.list=[]
+    reportState.page=1
+    reportState.finished=false
+    if(selectSubClassifyId.value==item.classify_id_second){
+        selectSubClassifyId.value=0
+    }else{
+        selectSubClassifyId.value=item.classify_id_second
+    }
+    getReportList()
+}
+
+// 搜索
+const handleSearch=(e)=>{
+    reportState.list=[]
+    reportState.page=1
+    reportState.finished=false
+    reportState.searchVal=e||''
+    getReportList()
+}
+
+
+// 跳转报告详情
+const goReportDetail=(item)=>{
+    router.push({
+        path:'/report/detail',
+        query:{
+            reportId:item.report_id
+        }
+    })
+}
+
+
+let isMounted=ref(false)
+onMounted(()=>{
+    isMounted.value=true
+})
+</script>
+
+<template>
+    <!-- 顶部搜索和筛选 -->
+    <template v-if="isMounted&&$route.path=='/report/list'">
+        <teleport to="#reportlist-in-head">
+            <div class="flex-col-center reportlist-search-filter-wrap">
+                <Search
+                    placeholder="请输入标题/关键词"
+                    @search="handleSearch"
+                    @clean="handleSearch"
+                ></Search>
+                <template v-if="filterList.length>0">
+                <el-popover 
+                    :width="580"
+                    trigger="click"
+                    placement="bottom-end"
+                    popper-style="box-shadow: rgb(14 18 22 / 35%) 0px 10px 38px -10px, rgb(14 18 22 / 20%) 0px 10px 20px -15px; padding: 20px;"
+                >
+                    <template #reference>
+                        <div class="filter-btn">筛选</div>
+                    </template>
+                    <template #default>
+                        <div class="flex filter-box">
+                            <div 
+                                :class="['item',item.classify_id_second==selectSubClassifyId&&'active']" 
+                                v-for="item in filterList" 
+                                :key="item.classify_id_second"
+                                @click="handleClickFilterItem(item)"
+                            >{{item.classify_second_simple}}</div>
+                        </div>
+                    </template>
+                </el-popover>
+                </template>
+            </div>
+        </teleport>
+    </template>
+    <div class="report-list-page">
+        <SelfList 
+            :finished="reportState.finished" 
+            :isEmpty="reportState.list.length === 0 && reportState.finished" 
+            :loading="reportState.loading" 
+            @listOnload="onLoad"
+        >
+        <div class="flex list-wrap">
+            <div class="flex item" v-for="item in reportState.list" :key="item.report_id" @click="goReportDetail(item)">
+                <el-image class="report-img" :src="item.report_img_url" fit="cover" lazy />
+                <div class="con">
+                    <div class="top-name" v-html="item.title_info"></div>
+                    <div class="title" v-html="item.title"></div>
+                    <div class="des" v-html="item.abstract"></div>
+                    <div class="clear-float bot">
+                        <span class="time">{{moment(item.publish_time).format('YYYY.MM.DD HH:mm')}}</span>
+                        <div class="btn" v-if="item.auth_ok">立即播放</div>
+                    </div>  
+                </div>
+            </div>
+        </div>
+        </SelfList>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.reportlist-search-filter-wrap{
+    height: 100%;
+    .filter-btn{
+        cursor: pointer;
+        margin-left: 25px;
+        color: #F3A52F;
+        font-size: 16px;
+        &::before{
+            content: '';
+            display: inline-block;
+            width: 17px;
+            height: 13px;
+            background-image: url('@/assets/icon-filter.png');
+            background-size: cover;
+            vertical-align: middle;
+            margin-right: 3px;
+        }
+    }
+}
+.filter-box{
+    flex-wrap: wrap;
+    .item{
+        margin: 5px 10px;
+        width: 113px;
+        height: 40px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        background-color: #f6f6f6;
+        border-radius: 4px;
+        font-size: 16px;
+        cursor: pointer;
+        :hover{
+            color: #fff;
+            background-color: #F3A52F;
+        }
+    }
+    .active{
+        color: #fff;
+        background-color: #F3A52F;
+    }
+}
+.report-list-page{
+    .list-wrap{
+        flex-wrap: wrap;
+        .item{
+            flex-shrink: 0;
+            width: 380px;
+            background: #FFFFFF;
+            box-shadow: 3px 3px 12px 1px rgba(184, 184, 184, 0.16);
+            border-radius: 8px;
+            border: 1px solid #F2F2F2;
+            padding: 20px;
+            margin-bottom: 20px;
+            margin-left: 10px;
+            margin-right: 10px;
+            overflow: hidden;
+            .report-img{
+                flex-shrink: 0;
+                width: 110px;
+                height: 160px;
+                border-radius: 8px;
+                margin-right: 15px;
+            }
+            .con{
+                flex: 1;
+                display: flex;
+                flex-direction: column;
+                justify-content: space-between;
+                overflow: hidden;
+            }
+            .top-name{
+                background-color: #FFFBF5;
+                text-align: center;
+                font-size: 14px;
+                color: #F3A52F;
+                padding: 4px 0;
+            }
+            .des{
+                color: #999;
+            }
+            .bot{
+                display: flex;
+                align-items: flex-end;
+                justify-content: space-between;
+                .time{
+                    flex-shrink: 0;
+                    font-size: 14px;
+                    color: #999;
+                }
+                .btn{
+                    flex-shrink: 0;
+                    width: 88px;
+                    height: 30px;
+                    background: #F3A52F;
+                    border-radius: 15px;
+                    color: #fff;
+                    text-align: center;
+                    line-height: 30px;
+                    font-size: 14px;
+                    cursor: pointer;
+                }
+            }
+        }
+    }
+
+}
+</style>
+

+ 2 - 1
src/views/report/Search.vue

@@ -35,7 +35,7 @@ const handleSearch=(e)=>{
     listState.page=1
     listState.list=[]
     listState.finished=false
-    listState.keyword=e
+    listState.keyword=e||''
     getSearchList()
 }
 
@@ -54,6 +54,7 @@ onMounted(() => {
                 placeholder="请输入标题/关键词"
                 :autoFocus="true"
                 @search="handleSearch"
+                @clean="handleSearch"
             ></Search>
         </teleport>
     </template>

+ 275 - 0
src/views/report/specialColumn/Detail.vue

@@ -0,0 +1,275 @@
+<script setup>
+import {reactive, ref} from 'vue'
+import moment from 'moment';
+import SelfList from '@/components/SelfList.vue'
+import {apiSpecialColumnDetail,apiSpecialColumnReportList} from '@/api/report'
+import { useRoute, useRouter } from 'vue-router';
+
+const route=useRoute()
+const router=useRouter()
+
+let nav=ref('专栏目录')
+
+let info=ref(null)//专栏详情
+const getSpecialColumnDetail=async ()=>{
+    const res=await apiSpecialColumnDetail({classify_id_second:Number(route.query.columnId)})
+    if(res.code===200){
+        info.value=res.data
+        document.title=res.data.classify_name_second
+    }
+}
+getSpecialColumnDetail()
+
+
+// 获取报告列表
+let listState=reactive({
+    list:[],
+    page:1,
+    pageSize:20,
+    finished:false,
+    loading:false
+})
+const getReportList=async ()=>{
+    listState.loading=true
+    const res=await apiSpecialColumnReportList({
+        classify_id_second:Number(route.query.columnId||0),
+        current_index:listState.page,
+        page_size:listState.pageSize
+    })
+    listState.loading=false
+    if(res.code===200){
+        let arr=res.data.list||[]
+        listState.list=[...listState.list,...arr]
+        if(res.data.paging.is_end){
+            listState.finished=true
+        }
+    }
+}
+getReportList()
+
+// 加载更多
+const onLoad=()=>{
+    listState.page++
+    getReportList()
+}
+
+//跳转报告详情
+const goReportDetail=(item)=>{
+    router.push({
+        path:'/report/detail',
+        query:{
+            reportId:item.report_id
+        }
+    })
+}
+
+
+
+</script>
+
+<template>
+    <div class="specialColumn-detail-page" v-if="info">
+        <div class="top-abstract-box" >
+            <div class="title">{{info.classify_name_second}}</div>
+            <p class="abstract">{{info.abstract}}</p>
+        </div>
+        <div class="main-box">
+            <div class="nav-box">
+                <span :class="nav==='分析师介绍'&&'active'" @click="nav='分析师介绍'">分析师介绍</span>
+                <span :class="nav==='专栏目录'&&'active'"  @click="nav='专栏目录'">专栏目录</span>
+                <span :class="nav==='客户评价'&&'active'"  @click="nav='客户评价'">客户评价</span>
+            </div>
+            <!-- 分析师介绍模块 -->
+            <div class="user-info-wrap" v-if="nav==='分析师介绍'">
+                <div class="flex user-box">
+                    <img class="avatar" :src="info.avatar_img_url" alt="">
+                    <div>
+                        <div>
+                            <span class="name">{{info.report_author}}</span>
+                            <span class="vip" v-if="info.vip_title">{{info.vip_title}}</span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!-- 专栏报告模块 -->
+            <template v-if="nav==='专栏目录'">
+                <SelfList 
+                    :finished="listState.finished" 
+                    :isEmpty="listState.list.length === 0 && listState.finished" 
+                    :loading="listState.loading"
+                    @listOnload="onLoad"
+                >
+                    <div class="report-list">
+                        <div 
+                            class="flex item" 
+                            v-for="item in listState.list" 
+                            :key="item.report_id"
+                            @click="goReportDetail(item)"
+                        >
+                            <div class="user-box">
+                                <img class="avatar" :src="item.report_img_url" alt="">
+                                <div>
+                                    <span>{{item.author}}</span>
+                                    <img src="@/assets/tag.png" alt="">
+                                </div>
+                            </div>
+                            <div class="con">
+                                <span class="title">【第{{item.stage}}期|FICC】{{item.classify_name_second}}</span>
+                                <div class="des" v-html="item.abstract"></div>
+                                <div class="time">{{moment(item.publish_time).format('YYYY.MM.DD HH:mm')}}</div>
+                            </div>
+                            <div class="global-plain-btn audio-btn">立即播放</div>
+                        </div>
+                    </div>
+                </SelfList>
+            </template>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.specialColumn-detail-page{
+    .top-abstract-box{
+        .title{
+            font-size: 28px;
+            margin-bottom: 16px;
+        }
+        .abstract{
+            line-height: 2;
+        }
+    }
+
+    .main-box{
+        .nav-box{
+            padding-top: 60px;
+            padding-bottom: 10px;
+            background-color: #fff;
+            span{
+                font-size: 18px;
+                position: relative;
+                margin-right: 60px;
+                &:hover{
+                    color: #F3A52F;
+                }
+            }
+            .active{
+                color: #F3A52F;
+                &::after{
+                    content: '';
+                    display: block;
+                    width: 40px;
+                    height: 4px;
+                    background: #F3A52F;
+                    border-radius: 2px;
+                    position: absolute;
+                    top: 105%;
+                    left: 50%;
+                    transform: translateX(-50%);
+                }
+            }
+        }
+
+        .user-info-wrap{
+            margin-top: 40px;
+            .user-box{
+                align-items: center;
+                .avatar{
+                    width: 90px;
+                    height: 90px;
+                    object-fit: cover;
+                    display: block;
+                    margin-right: 18px;
+                }
+                .name{
+                    font-size: 20px;
+                    font-weight: bold;
+                    margin-right: 10px;
+                }
+                .vip{
+                    background-color: #FAF7EE;
+                    font-size: 14px;
+                    height: 24px;
+                    line-height: 24px;
+                    padding: 0 10px;
+                    border-radius: 23px;
+                    &::before{
+                        content: '';
+                        display: inline-block;
+                        width: 15px;
+                        height: 15px;
+                        background-image: url('@/assets/tag.png');
+                        background-size: cover;
+                        position: relative;
+                        top: 2px;
+                        margin-right: 3px;
+                    }
+                }
+            }
+        }
+
+        .report-list{
+            .item{
+                padding-top: 20px;
+                padding-bottom: 30px;
+                padding-left: 16px;
+                padding-right: 16px;
+                border-bottom: 1px solid #F2F2F2;
+                position: relative;
+                .user-box{
+                    flex-shrink: 0;
+                    .avatar{
+                        width: 64px;
+                        height: 64px;
+                        border-radius: 50%;
+                        object-fit: cover;
+                        display: block;
+                        margin: 0 auto;
+                    }
+                    div{
+                        margin-top: 7px;
+                        margin-left: auto;
+                        margin-right: auto;
+                        width: 96px;
+                        height: 28px;
+                        line-height: 28px;
+                        background: #FFFBF5;
+                        border-radius: 4px;
+                        text-align: center;
+                        font-size: 14px;
+                        color: #F3A52F;
+                        img{
+                            width: 16px;
+                            height: 16px;
+                            position: relative;
+                            top: 2px;
+                        }
+                    }
+                }
+                .con{
+                    display: flex;
+                    flex-direction: column;
+                    justify-content: space-between;
+                    margin-left: 40px;
+                    flex: 1;
+                    color: #000;
+                    .title{
+                        font-size: 20px;
+                        margin-left: -8px;
+                    }
+                }
+                .audio-btn{
+                    position: absolute;
+                    bottom: 30px;
+                    right: 16px;
+                    width: 100px;
+                    font-size: 14px;
+                    height: 30px;
+                    line-height: 30px;
+                    padding-top: 0;
+                    padding-bottom: 0;
+                }
+            }
+        }
+    }
+}
+</style>

+ 196 - 0
src/views/report/specialColumn/List.vue

@@ -0,0 +1,196 @@
+<script setup>
+import {reactive, ref} from 'vue'
+import SelfList from '@/components/SelfList.vue'
+import {apiReportClassify,apiSpecialColumnList} from '@/api/report'
+import { useRoute, useRouter } from 'vue-router';
+
+const route=useRoute()
+const router=useRouter()
+
+document.title=decodeURIComponent(route.query.classifyName)
+
+// 获取分类列表数据 过滤出其中的专栏
+let tabList=ref([])
+let classifyId=ref(Number(route.query.classifyId)||0)
+const getClassifyList=async ()=>{
+    const res=await apiReportClassify()
+    if(res.code===200){
+        let arr=res.data||[]
+        tabList.value=arr.filter(item=>item.redirect_type===1)
+    }
+}
+getClassifyList()
+
+// 获取专栏列表
+let listState=reactive({
+    list:[],
+    finished:false,
+    loading:false
+})
+const getSpecialColumnList=async ()=>{
+    listState.loading=true
+    const res=await apiSpecialColumnList({
+        classify_id_first:Number(classifyId.value)
+    })
+    listState.loading=false
+    if(res.code===200){
+        listState.list=res.data||[]
+        listState.finished=true
+    }
+}
+getSpecialColumnList()
+
+
+// 点击切换专栏
+const handleClickTab=(item)=>{
+    document.title=item.props.label
+    classifyId.value=item.props.name 
+    getSpecialColumnList()
+}
+
+// 跳转专栏详情
+const goSpecialColumnDetail=(item)=>{
+    router.push({
+        path:'/report/specialcolumndetail',
+        query:{
+            columnId:item.classify_id_second
+        }
+    })
+}
+</script>
+
+<template>
+    <div class="report-specialColumn-list-page">
+        <img :src="$store.state.globalImgUrls.specialColumnBanner" alt=""  class="top-banner">
+        <el-tabs v-model="classifyId" class="tabs-wrap" @tab-click="handleClickTab">
+            <el-tab-pane 
+                :label="item.classify_name_first" 
+                :name="item.classify_id_first"
+                v-for="item in tabList" 
+                :key="item.classify_name_first"
+            ></el-tab-pane>
+        </el-tabs>
+        <SelfList 
+            :finished="listState.finished" 
+            :isEmpty="listState.list.length === 0 && listState.finished" 
+            :loading="listState.loading"
+        >
+            <div class="flex list-wrap">
+                <div class="item" v-for="item in listState.list" :key="item.classify_id_second" >
+                    <div class="global-plain-btn btn" @click="goSpecialColumnDetail(item)">立即查看</div>
+                    <div class="title">{{item.classify_name_second}}</div>
+                    <div class="flex user-box">
+                        <img :src="item.home_img_url" alt="" class="avatar">
+                        <div class="name">{{item.report_author}}</div>
+                        <div class="stage">最新:{{item.stage}}期</div>
+                    </div>
+                    <div class="multi-ellipsis-l2 des">专栏介绍</div>
+                </div>
+            </div>
+        </SelfList>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.report-specialColumn-list-page{
+    .top-banner{
+        display: block;
+        width: 100%;
+        margin-bottom: 40px;
+    }
+    .tabs-wrap{
+        :deep(.el-tabs__active-bar){
+            height: 4px;
+            background: #F3A52F;
+            border-radius: 2px 2px 2px 2px;
+        }
+        :deep(.el-tabs__nav-wrap::after){
+            display: none;
+        }
+        :deep(.el-tabs__item.is-active){
+            color: #F3A52F;
+        }
+        :deep(.el-tabs__item){
+            font-size: 20px;
+            &:hover{
+                color: #F3A52F;
+            }
+        }
+    }
+
+    .list-wrap{
+        flex-wrap: wrap;
+        .item{
+            padding-top: 30px;
+            padding-bottom: 30px;
+            width: 50%;
+            border-top: 1px solid #F2F2F2;
+            border-bottom: 1px solid #F2F2F2;
+            position: relative;
+            .btn{
+                position: absolute;
+                top: 30px;
+                right: 0;
+                width: 100px;
+                height: 28px;
+                line-height: 28px;
+                font-size: 14px;
+                padding-top: 0;
+                padding-bottom: 0;
+            }
+            .title{
+                font-size: 20px;
+            }
+            .user-box{
+                margin-top: 6px;
+                margin-bottom: 20px;
+                align-items: center;
+                .avatar{
+                    width: 40px;
+                    height: 40px;
+                    display: block;
+                    object-fit: cover;
+                    border-radius: 50%;
+                    margin-right: 10px;
+                    position: relative;
+                    top: 3px;
+                }
+                .name{
+                    margin-right: 20px;
+                    &::after{
+                        content: '';
+                        width: 20px;
+                        height: 20px;
+                        display: inline-block;
+                        background-image: url('@/assets/tag.png');
+                        background-size: cover;
+                        position: relative;
+                        top:5px;
+                        margin-left: 5px;
+                    }
+                }
+                .stage{
+                    font-size: 14px;
+                    color: #F3A52F;
+                    position: relative;
+                    top: 2px;
+                }
+            }
+            .des{
+                font-size: 14px;
+                color: #666;
+            }
+        }
+        .item:nth-child(odd){
+            padding-right: 25px;
+            border-right: 1px solid #F2F2F2;
+            .btn{
+                right: 25px;
+            }
+        }
+        .item:nth-child(even){
+            padding-left: 25px;
+        }
+    }
+}
+</style>