shanbinzhang 1 долоо хоног өмнө
parent
commit
9d454d6d9e

+ 48 - 0
src/api/etaReport/collect.js

@@ -0,0 +1,48 @@
+import {get,post} from '@/api/index'
+
+// 研报收藏模块
+export default{
+  // 新增分类
+  addClassify:(params)=>{
+    return post('/report_collect/classify/add',params)
+  },
+  // 编辑分类
+  editClassify:(params)=>{
+    return post('/report_collect/classify/edit',params)
+  },
+  // 移动分类
+  moveClassify:(params)=>{
+    return post('/report_collect/classify/move',params)
+  },
+  // 删除分类
+  deleteClassify:(params)=>{
+    return post('/report_collect/classify/delete',params)
+  },
+  // 分类列表
+  classifyList:(params)=>{
+    return get('/report_collect/classify/list',params)
+  },
+
+  // 收藏报告
+  reportCollect:(params)=>{
+    return post('/report_collect/add',params)
+  },
+  // 取消收藏报告
+  reportCollectCancel:(params)=>{
+    return post('/report_collect/delete',params)
+  },
+  // 收藏报告列表数据
+  reportCollectList:(params)=>{
+    return get('/report_collect/list',params)
+  },
+  // 批量转移报告
+  reportCollectBatchMove:(params)=>{
+    return post('/report_collect/modify_collect_classify',params)
+  },
+
+  //报告的所属分类树
+  etaReportClassify: params => {
+    return get('/report/classify_tree',params)
+  }
+  
+}

+ 7 - 0
src/api/etaReport/index.js

@@ -0,0 +1,7 @@
+import apiEtaReport from './report'
+import apiEtaReportCollect from './collect'
+
+export {
+  apiEtaReport,
+  apiEtaReportCollect
+}

+ 59 - 0
src/api/etaReport/report.js

@@ -0,0 +1,59 @@
+import {get,post} from '@/api/index'
+
+// 研报模块
+export default {
+  /**
+   * 报告列表
+   * @param {*} params PageSize=&CurrentIndex=&ShowAll=&IsToday=&PermissionId=1
+   * @returns 
+   */
+  reportList: params => {
+    return get('/report/list',params)
+  },
+
+  /**
+   * 品种分类
+   * @param {*} params 
+   * @returns 
+   */
+  permissionList: params => {
+    return get('/report/permission_list',params)
+  },
+
+  /**
+   * 
+   * @param {*} params PageSize=5&CurrentIndex=1&ShowAll=&Keyword=
+   * @returns 
+   */
+  reportSearch: params => {
+    return get('/report/search_by_es',params)
+  },
+
+  /**
+   * 
+   * @param {*} params ReportId
+   * @returns 
+   */
+  reportDetail: params => {
+    return get('/report/detail',params)
+  },
+
+  /**
+   * 
+   * @param {*} params ReportId ChapterId
+   * @returns 
+   */
+  chapterDetail: params => {
+    return get('/report/chapter_detail',params)
+  },
+
+  //热门推荐
+  recommandList: params => {
+    return get('/report/hot_recommend',params)
+  },
+
+  //海报为
+  reportPoster: params => {
+    return get('/report/poster',params)
+  }
+}

+ 11 - 0
src/assets/svg/chart.svg

@@ -0,0 +1,11 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="mask0_796_8413" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
+<rect width="20" height="20" fill="#D9D9D9"/>
+</mask>
+<g mask="url(#mask0_796_8413)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 3.75V16.25H16.25V3.75H3.75ZM3.5 2.5C2.94772 2.5 2.5 2.94772 2.5 3.5V16.5C2.5 17.0523 2.94772 17.5 3.5 17.5H16.5C17.0523 17.5 17.5 17.0523 17.5 16.5V3.5C17.5 2.94772 17.0523 2.5 16.5 2.5H3.5Z" fill="#333333"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.50002 10.5L11.9881 16.1101C12.2037 16.3796 12.16 16.7729 11.8905 16.9885C11.6209 17.2042 11.2276 17.1605 11.012 16.8909L7.50002 12.501L3.98807 16.8909C3.77244 17.1605 3.37913 17.2042 3.10959 16.9885C2.84005 16.7729 2.79635 16.3796 3.01198 16.1101L7.50002 10.5Z" fill="#333333"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5 8.5L16.9881 11.6101C17.2037 11.8796 17.16 12.2729 16.8905 12.4885C16.6209 12.7042 16.2276 12.6605 16.012 12.3909L14.5 10.501L10.9881 14.8909C10.7724 15.1605 10.3791 15.2042 10.1096 14.9885C9.84005 14.7729 9.79635 14.3796 10.012 14.1101L14.5 8.5Z" fill="#333333"/>
+<path d="M8 7.5C8 8.32843 7.32843 9 6.5 9C5.67157 9 5 8.32843 5 7.5C5 6.67157 5.67157 6 6.5 6C7.32843 6 8 6.67157 8 7.5Z" fill="#333333"/>
+</g>
+</svg>

+ 10 - 0
src/assets/svg/report.svg

@@ -0,0 +1,10 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<mask id="mask0_796_7932" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="20" height="20">
+<rect width="20" height="20" fill="#D9D9D9"/>
+</mask>
+<g mask="url(#mask0_796_7932)">
+</g>
+<path d="M6.33341 2.66651C6.50641 2.66574 6.67293 2.73224 6.7978 2.85197C6.92267 2.9717 6.9961 3.13528 7.0026 3.30816C7.00909 3.48103 6.94812 3.64966 6.83258 3.77842C6.71705 3.90717 6.55598 3.98598 6.38341 3.99818L6.33341 3.99984H4.00008V17.3332H16.0001V3.99984H13.8334C13.6604 4.00062 13.4939 3.93412 13.369 3.81438C13.2442 3.69465 13.1707 3.53107 13.1642 3.3582C13.1577 3.18532 13.2187 3.0167 13.3342 2.88794C13.4498 2.75918 13.6108 2.68038 13.7834 2.66818L13.8334 2.66651H16.0001C16.3422 2.66651 16.6712 2.79799 16.919 3.03375C17.1669 3.26951 17.3146 3.59152 17.3317 3.93318L17.3334 3.99984V17.3332C17.3334 17.6753 17.2019 18.0043 16.9662 18.2521C16.7304 18.5 16.4084 18.6477 16.0667 18.6648L16.0001 18.6665H4.00008C3.658 18.6665 3.32901 18.535 3.08114 18.2993C2.83328 18.0635 2.68552 17.7415 2.66841 17.3998L2.66675 17.3332V3.99984C2.66675 3.65776 2.79823 3.32877 3.03399 3.08091C3.26975 2.83305 3.59176 2.68528 3.93341 2.66818L4.00008 2.66651H6.33341Z" fill="#333333"/>
+<path d="M12.3334 1.3335H7.66675C6.56508 1.3335 5.66675 2.23183 5.66675 3.3335C5.66675 4.43516 6.56508 5.3335 7.66675 5.3335H12.3334C13.4351 5.3335 14.3334 4.43516 14.3334 3.3335C14.3334 2.23183 13.4351 1.3335 12.3334 1.3335ZM7.66675 2.66683H12.3334C12.6984 2.66683 13.0001 2.9685 13.0001 3.3335C13.0001 3.6985 12.6984 4.00016 12.3334 4.00016H7.66675C7.30175 4.00016 7.00008 3.6985 7.00008 3.3335C7.00008 2.9685 7.30175 2.66683 7.66675 2.66683Z" fill="#333333"/>
+<path d="M13.1953 8.19516L14.138 9.13782L9.80465 13.4712C9.67963 13.5961 9.51009 13.6663 9.33331 13.6663C9.15654 13.6663 8.987 13.5961 8.86198 13.4712L6.19531 10.8045L7.13798 9.86182L9.33331 12.0565L13.1953 8.19482V8.19516Z" fill="#333333"/>
+</svg>

+ 2 - 2
src/components/LoadList.vue

@@ -53,8 +53,8 @@ const handleClickLoadMore=()=>{
             height: 30px;
             background: #FFFFFF;
             border-radius: 20px;
-            border: 1px solid #F3A52F;
-            color: #F3A52F;
+            border: 1px solid var(--td-brand-color);
+            color: var(--td-brand-color);
             font-size: 14px;
             text-align: center;
             line-height: 30px;

+ 1 - 1
src/router/modules/etaChart.js

@@ -28,7 +28,7 @@ export default[
       },
       {
         path:'favorite',
-        name:'ETAFavorite',
+        name:'ETAFavoriteChart',
         component:()=>import('@/views/user/favorite/Index.vue'),
         meta:{
           title:'我的收藏'

+ 24 - 0
src/router/modules/report.js

@@ -34,6 +34,30 @@ export default[
           title:'研报详情',
         },
       },
+      {
+        path:'search',
+        name:'ETAReportSearch',
+        component:()=>import('@/views/report/Search.vue'),
+        meta:{
+          title:'研报搜索',
+        },
+      },
+      {
+        path:'favorite',
+        name:'ETAFavoriteReport',
+        component:()=>import('@/views/user/favorite/Index.vue'),
+        meta:{
+          title:'我的收藏'
+        },
+      },
+      {
+        path:'posterDetail',
+        name:'ETAPosterDetail',
+        component:()=>import('@/views/report/PosterDetail.vue'),
+        meta:{
+          title:'海报'
+        },
+      },
     ]
   }
 ]

+ 15 - 1
src/store/modules/report/index.js

@@ -5,6 +5,16 @@ import { MessagePlugin } from 'tdesign-vue-next';
 
 export const useReportStore = defineStore('reportState',() => {
 
+  const showAllReport = ref(false); //显示全部筛选项 系统共享 神奇的设计
+  function changeShowAll(bool) {
+    showAllReport.value = bool;
+  }
+
+  const posterInfo = ref({});//首页海报位信息
+  function setPosterInfo(info) {
+    posterInfo.value = info
+  }
+
   const audioData = ref({
     INS:null,//音频实例
     list:[],//音频数据{time:音频时长,url:音频地址,name:音频名称}
@@ -103,7 +113,11 @@ export const useReportStore = defineStore('reportState',() => {
     closeAudio,
     audioStatusChange,
     closeAudioPop,
-    updateAudioTime
+    updateAudioTime,
+    showAllReport,
+    changeShowAll,
+    posterInfo,
+    setPosterInfo
   }
 
 })

+ 108 - 243
src/views/report/Classify.vue

@@ -1,155 +1,63 @@
 <script setup>
 import { nextTick, reactive, ref } from 'vue'
 import { SearchIcon,InfoCircleFilledIcon } from 'tdesign-icons-vue-next';
-import { useRouter } from 'vue-router'
+import { useRoute, useRouter } from 'vue-router'
+import { apiEtaReport } from '@/api/etaReport'
+import { useReportStore } from '@/store/modules/report'
+import { useReport } from './hooks/useReport'
 import moment from 'moment'
 import 'moment/locale/zh-cn';
 moment.locale('zh-cn')
 import LoadList from '@/components/LoadList.vue'
 
 const router = useRouter()
+const route = useRoute()
 
-const searchText = ref('')
+const  { handleToDetail } = useReport();
+const reportStore = useReportStore();
 
 const listState = reactive({
-  list: [
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        }
-  ],
-  showAll: false,
+  list: [],
+  showAll: reportStore.showAllReport||false,
   page: 1,
   pageSize: 20,
   finished: true,
   loading: false,
-  dateArr: [],//日期数据
+  searchText: ''
 })
 
+async function getList() {
+  listState.loading=true
+  const res = await apiEtaReport.reportList({
+    PageSize: listState.pageSize,
+    CurrentIndex: listState.page,
+    ShowAll: listState.showAll,
+    IsToday: false,
+    PermissionId: Number(selectSubType.value)
+  })
+
+  listState.loading=false
+  if(listState.page===1){
+    document.body.scrollTop=document.documentElement.scrollTop=0
+  }
+
+  if(res.Ret !== 200) return
+
+  if(res.Data.Paging.IsEnd){
+    listState.finished=true
+  }
+  let arr=res.Data.List||[];
+  listState.list=[...listState.list,...arr];
+}
+function onLoad() {
+  listState.page++
+  getList()
+}
+function initList() {
+  listState.page = 1;
+  getList()
+}
+
 // 格式化列表日期
 const formatDate=(e)=>{
   const isSameYear=moment(e).isSame(new Date(), 'year');
@@ -160,113 +68,55 @@ const formatDate=(e)=>{
   }
 }
 
-const latestNewsList = ref([
-  { img: '',title:'带我去带我去多带我去带我去多' },
-  { img: '',title:'带我去带我去多带我去带我去多' },
-  { img: '',title:'带我去带我去多带我去带我去多' },
-  { img: '',title:'带我去带我去多带我去带我去多',abstract: '带我去多无群单位娶老婆德莱文【的利物浦【 单 ' },
-  { img: '',title:'带我去带我去多带我去带我去多' },
-  { img: '',title:'带我去带我去多带我去带我去多' },
-  { img: '',title:'带我去带我去多带我去带我去多' },
-])
-
 
 
 const selectFirstType = ref('')
 const selectSubType = ref('')
-const firstClassifyList = ref([
-  { classify_name: '宏观经济',icon_url:'',id: 0 },
-  { classify_name: '宏观经济2',icon_url:'',id: 1 },
-  { classify_name: '宏观经济3',icon_url:'',id: 2 },
-  { classify_name: '宏观经济4',icon_url:'',id: 3 },
-  { classify_name: '宏观经济6',icon_url:'',id: 4 },
-])
-const subClassifyList = ref([
-  {
-    "chart_permission_name": "宏观经济",
-    "chart_permission_id": 1,
-    "sort": -999,
-    "auth_ok": true,
-    "pirce_driven_state": 0
-  },
-  {
-    "chart_permission_name": "利率债",
-    "chart_permission_id": 16,
-    "sort": -997,
-    "auth_ok": true,
-    "pirce_driven_state": 0
-  },
-  {
-    "chart_permission_name": "资产配置",
-    "chart_permission_id": 45,
-    "sort": -997,
-    "auth_ok": true,
-    "pirce_driven_state": 1
-  },
-  {
-    "chart_permission_name": "汇率",
-    "chart_permission_id": 46,
-    "sort": -994,
-    "auth_ok": true,
-    "pirce_driven_state": 1
-  },
-  {
-    "chart_permission_name": "贵金属",
-    "chart_permission_id": 47,
-    "sort": -994,
-    "auth_ok": true,
-    "pirce_driven_state": 1
-  },
-  {
-    "chart_permission_name": "名字超长名字超长名字超长名字超长名字超长名字超长名字超长名字超长名字超长",
-    "chart_permission_id": 108,
-    "sort": 2,
-    "auth_ok": false,
-    "pirce_driven_state": 1
-  },
-  {
-    "chart_permission_name": "股票交易权限",
-    "chart_permission_id": 76,
-    "sort": 23,
-    "auth_ok": false,
-    "pirce_driven_state": 1
-  },
-  {
-    "chart_permission_name": "jinyon",
-    "chart_permission_id": 107,
-    "sort": 63,
-    "auth_ok": false,
-    "pirce_driven_state": 1
-  }
-])
+const firstClassifyList = ref([])
+const subClassifyList = ref([])
+async function getPermissionList() {
+  const res = await apiEtaReport.permissionList()
+  if(res.Ret !== 200) return
+  firstClassifyList.value = res.Data || [];
+  
+  let item = route.query.permissionId 
+    ? firstClassifyList.value.find(_ => _.PermissionId===Number(route.query.permissionId)) 
+    : firstClassifyList.value[0];
+  handleClickFirstType(item)
+}
+getPermissionList()
+
 function handleClickFirstType(item) {
-  selectFirstType.value = item.classify_name
-  // subTypeList.value = item.list;
+  selectFirstType.value = item.PermissionName;
+  subClassifyList.value = item.Child;
 
-  // handleClickSubType(item.list[0])
-  // nextTick(() => {
-  //   resetHeaderWidthHandle();
-  // });
+  handleClickSubType(subClassifyList.value[0])
+  nextTick(() => {
+    resetHeaderWidthHandle();
+  });
 }
 function handleClickSubType(item) {
-  selectSubType.value = item.chart_permission_id
+  selectSubType.value = item.PermissionId
   listState.list = []
   listState.page = 1
   listState.finished = false
-  getReportList()
-  handleShowAuthData(item)
-  getLatestNews()
+  getList()
 }
 
 
+const latestNewsList = ref([])
+async function getReCommandList() {
+  const res = await apiEtaReport.recommandList();
+  if(res.Ret !== 200) return
+  latestNewsList.value = res.Data||[];
+}
+getReCommandList()
+
 
 function handleToIndex() {
   router.replace('/etaReport/index')
 }
 
-function handleToDetail() {
-  router.push(`/etaReport/detail?id=`)
-}
 
 // 实现头部的适配
 const contentRef=ref('')
@@ -297,7 +147,7 @@ onUnmounted(() => {
         <div class="flex">
           <h2 @click="handleToIndex">今日研报</h2>
           <div class="flex" style="flex-shrink:0">
-            <t-radio allow-uncheck v-model="listState.showAll">显示全部 </t-radio>
+            <t-radio allow-uncheck v-model="listState.showAll" @change="reportStore.changeShowAll(listState.showAll);initList()">显示全部 </t-radio>
             <t-tooltip 
               content="未勾选时展示所有客户有阅读权限的报告,勾选时展示弘则ETA所有已发布的报告"
             >
@@ -307,19 +157,22 @@ onUnmounted(() => {
 
           <div class="first-tab flex">
             <div 
-              :class="['item', item.classify_name == selectFirstType && 'item-active']" 
+              :class="['item', item.PermissionName == selectFirstType && 'item-active']" 
               v-for="item in firstClassifyList" 
-              :key="item.classify_name" 
+              :key="item.PermissionName" 
               @click="handleClickFirstType(item)"
-            >{{ item.classify_name }}</div>
+            >{{ item.PermissionName }}</div>
           </div>
         </div>
 
         <t-input 
-          v-model="searchText" 
+          v-model="listState.searchText" 
           placeholder="请输入搜索内容" 
           clearable
+          size="large"
           style="width:240px"
+          readonly
+          @click="router.push({path:'/etaReport/search'})"
         >
           <template #prefixIcon>
             <SearchIcon />
@@ -330,11 +183,11 @@ onUnmounted(() => {
 
       <div class="sub-tab">
         <span 
-          :class="['sub-item', item.chart_permission_id === selectSubType && 'sub-active']" 
+          :class="['sub-item', item.PermissionId === selectSubType && 'sub-active']" 
           v-for="item in subClassifyList" 
-          :key="item.chart_permission_id" 
+          :key="item.PermissionId" 
           @click="handleClickSubType(item)"
-        >{{ item.chart_permission_name }}</span>
+        >{{ item.PermissionName }}</span>
       </div>
     </div>
 
@@ -349,22 +202,28 @@ onUnmounted(() => {
           @listOnload="onLoad"
         >
           <div class="report-list-wrap"  v-if="listState.list.length" :style="{'margin-top':`${headerHight}px`}">
-            <div class="content-item" v-for="item in listState.list" :key="item.date" @click="handleGoReportDetail">
+            <div 
+              class="content-item" 
+              :class="!item.HasAuth?'noAuth':''"
+              v-for="item in listState.list" 
+              :key="`${item.ReportId}_${item.ReportChapterId}`" 
+              @click="handleToDetail(item)"
+            >
               <div class="report-item-info">
-                <img src="" class="report-img">
+                <img :src="item.ClassifyCoverImg" class="report-img">
                 <div class="right-info">
                   <div class="c-stage flex">
                     <div>
-                      <t-tag theme="primary">能源化工</t-tag>
-                      {{ '第五期' }}
-                      | {{ item.classify_name_first }}
+                      <t-tag theme="primary" v-if="item.PermissionNames">{{item.PermissionNames[0]}}</t-tag>
+                      {{ `第${item.Stage}期` }}
+                      | {{ item.ClassifyName }}
 
                     </div>
                     
-                    <div class="c-time">{{ moment(item.publish_time).format('YYYY-MM-DD')  }}</div>
+                    <div class="c-time">{{ item.PublishTime }}</div>
                   </div>
-                  <div class="c-title">{{ item.title }}</div>
-                  <div class="desc" v-html="item.content_sub"></div>
+                  <div class="c-title text-ellipsis--l1">{{ item.Title }}</div>
+                  <div class="desc text-ellipsis--l1" v-html="item.Abstract"></div>
                 </div>
               </div>
             </div>
@@ -380,14 +239,14 @@ onUnmounted(() => {
             <div 
               class="recmd-item" 
               v-for="(item,index) in latestNewsList" 
-              :key="item.report_id"
+              :key="item.ReportId"
               @click="handleToDetail(item)"
             >
               <div class="title text-ellipsis--l1">
                 <span class="sort-num" :class="index>2?'grey':'notice'">{{index+1}}</span>
-                {{item.title}}
+                {{item.Title}}
               </div>
-              <div class="abstract text-ellipsis--l1" v-html="item.abstract" v-if="item.abstract"></div>
+              <div class="abstract text-ellipsis--l1" v-html="item.abstract" v-if="item.Abstract"></div>
             </div>
           </div>
 
@@ -482,7 +341,7 @@ onUnmounted(() => {
     }
   }
   .report-main {
-
+    .content-box { overflow: hidden; }
     .report-list-wrap {
       margin-top: 130px;
 
@@ -490,9 +349,15 @@ onUnmounted(() => {
         padding: 20px 0;
         border-bottom: 1px solid #DCDFE6;
         position: relative;
-
+        &.noAuth {
+          color: #C0C4CC;
+          .c-time,.desc {
+            color: #C0C4CC;
+          }
+        }
         .right-info{
           flex: 1;
+          overflow: hidden;
         }
 
         .report-item-info {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 5 - 13
src/views/report/Detail.vue


+ 69 - 226
src/views/report/Index.vue

@@ -1,176 +1,65 @@
 <script setup>
 import { reactive, ref } from 'vue'
 import { SearchIcon,InfoCircleFilledIcon } from 'tdesign-icons-vue-next';
+import { useReportStore } from '@/store/modules/report'
 import { useRouter } from 'vue-router';
 import moment from 'moment'
+import _ from 'lodash'
 import 'moment/locale/zh-cn';
 moment.locale('zh-cn')
+import { apiEtaReport } from '@/api/etaReport'
 import LoadList from '@/components/LoadList.vue'
+import RightSideWrap from './components/RightSlideWrap.vue'
+import { useReport } from './hooks/useReport'
+
+const reportStore = useReportStore();
+const  { handleToDetail } = useReport()
 
 const router = useRouter()
 
 const searchText = ref('')
 
 const listState = reactive({
-  list: [
-    {
-      "date": "2025-05-19",
-      "sub_list": [
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        }
-      ]
-    },
-    {
-      "date": "2025-05-19",
-      "sub_list": [
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        }
-      ]
-    },
-    {
-      "date": "2025-05-19",
-      "sub_list": [
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        }
-      ]
-    },
-    {
-      "date": "2025-05-19",
-      "sub_list": [
-        {
-          "report_id": 9716,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T16:21:51+08:00",
-          "title": "智能布局pdf生成",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">给他人工我跟热范围废物费恶无非王菲菲</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        },
-        {
-          "report_id": 9714,
-          "report_chapter_id": 0,
-          "classify_id_first": 749,
-          "classify_name_first": "自由报告",
-          "classify_id_second": 0,
-          "classify_name_second": "",
-          "report_chapter_type_id": 0,
-          "publish_time": "2025-05-19T14:29:44+08:00",
-          "title": "测试报告常规布局",
-          "content_sub": "<div style=\"-webkit-line-clamp: 3;-webkit-box-orient: vertical;display: -webkit-box;overflow: hidden;text-overflow: ellipsis;\">带我去多无群大青蛙单位群单位</div>",
-          "classify_id": 0,
-          "classify_name": "",
-          "report_detail_show_type": 0,
-          "rai_report_id": 0
-        }
-      ]
-    },
-  ],
+  list: [],
   page: 1,
   pageSize: 20,
   finished: true,
   loading: false,
-  dateArr: [],//日期数据
+  showAll: reportStore.showAllReport||false
 })
 
-function getList() {
-  
+async function getList() {
+  listState.loading=true
+  const res = await apiEtaReport.reportList({
+    PageSize: listState.pageSize,
+    CurrentIndex: listState.page,
+    ShowAll: listState.showAll,
+    IsToday: true
+  })
+
+  listState.loading=false
+  if(listState.page===1){
+    document.body.scrollTop=document.documentElement.scrollTop=0
+  }
+
+  if(res.Ret !== 200) return
+
+  if(res.Data.Paging.IsEnd){
+    listState.finished=true
+  }
+  let arr=res.Data.List||[];
+  listState.list=[...listState.list,...arr];
+}
+getList()
+
+function onLoad() {
+  listState.page++
+  getList()
+}
+
+function initList() {
+  listState.page = 1;
+  getList()
 }
 
 // 格式化列表日期
@@ -183,22 +72,6 @@ const formatDate=(e)=>{
   }
 }
 
-const latestNewsList = ref([
-  { img: '' },
-  { img: '' },
-  { img: '' },
-])
-
-
-function handleClickClassify() {
-  router.push('/etaReport/classify')
-}
-
-function handleToDetail() {
-  router.push(`/etaReport/detail?id=`)
-}
-
-
 // 实现头部的适配
 const contentRef=ref('')
 const headerRef=ref('')
@@ -227,7 +100,7 @@ onUnmounted(() => {
         <div class="flex">
           <h2>今日研报</h2>
           <div class="flex">
-            <t-radio allow-uncheck v-model="listState.showAll">显示全部 </t-radio>
+            <t-radio allow-uncheck v-model="listState.showAll" @change="reportStore.changeShowAll(listState.showAll);initList()">显示全部 </t-radio>
             <t-tooltip 
               content="未勾选时展示所有客户有阅读权限的报告,勾选时展示弘则ETA所有已发布的报告"
             >
@@ -242,6 +115,9 @@ onUnmounted(() => {
           placeholder="请输入搜索内容" 
           clearable
           style="width:400px"
+          size="large"
+          readonly
+          @click="router.push({path:'/etaReport/search'})"
         >
           <template #prefixIcon>
             <SearchIcon />
@@ -258,21 +134,27 @@ onUnmounted(() => {
         @listOnload="onLoad"
       >
         <div class="report-list-wrap"  v-if="listState.list.length">
-          <div class="item" v-for="item in listState.list" :key="item.date">
-            <div class="item-time">{{ formatDate(item.date) }}</div>
+          <div class="item">
+            <div class="item-time">{{ formatDate(listState.list[0].PublishTime) }}</div>
             <div class="content-list">
-              <div class="content-item" v-for="citem in item.sub_list" :key="citem.report_id" @click="handleToDetail">
-                <div class="c-time">{{ moment(citem.publish_time).format('HH:mm:ss')  }}</div>
+              <div 
+                class="content-item"
+                :class="!citem.HasAuth?'noAuth':''"
+                v-for="citem in listState.list" 
+                :key="`${citem.ReportId}_${citem.ReportChapterId}`" 
+                @click="handleToDetail(citem)"
+              >
+                <div class="c-time">{{ moment(citem.PublishTime).format('HH:mm:ss')  }}</div>
                 <div class="report-item-info">
-                  <img src="" class="report-img">
-                  <div>
+                  <img :src="citem.ClassifyCoverImg" class="report-img">
+                  <div style="flex:1">
                     <div class="c-stage">
-                      <t-tag theme="primary">能源化工</t-tag>
-                      {{ '第五期' }}
-                      | {{ citem.classify_name_first }}
+                      <t-tag theme="primary" v-if="citem.PermissionNames">{{citem.PermissionNames[0]}}</t-tag>
+                      {{ `第${citem.Stage}期` }}
+                      | {{ citem.ClassifyName }}
                     </div>
-                    <div class="c-title">{{ citem.title }}</div>
-                    <div class="desc" v-html="citem.content_sub"></div>
+                    <div class="c-title text-ellipsis--l1">{{ citem.Title }}</div>
+                    <div class="desc text-ellipsis--l1" v-html="citem.Abstract"></div>
                   </div>
                 </div>
               </div>
@@ -282,27 +164,9 @@ onUnmounted(() => {
       </LoadList>
 
     </div>
-    <div class="right-aside-box">
-      <div class="fix-top" style="z-index: 100;">
-        <div class="recommand-banner" :style="`background-image: url(${''})`"></div>
-        <div class="recmd-box">
-          <div class="label">行业板块</div>
-          <div class="flex recmd-wrap">
-
-            <div 
-              class="recmd-item" 
-              v-for="item in latestNewsList" 
-              :key="item.report_id"
-              @click="handleClickClassify(item)"
-              :style="`background-imgage: url('')`"
-            >
-              <!-- <div class="title">{{item.classify_name_second}}</div> -->
-            </div>
-          </div>
-        </div>
 
-      </div>
-    </div>
+    <!-- 右侧 -->
+    <RightSideWrap/>
 
 
   </div>
@@ -330,6 +194,7 @@ onUnmounted(() => {
         font-size: 24px;
         font-style: italic;
         margin-right: 15px;
+        flex-shrink: 0;
       }
     }
 
@@ -371,7 +236,9 @@ onUnmounted(() => {
           padding: 20px 0;
           border-bottom: 1px solid #DCDFE6;
           position: relative;
-
+          &.noAuth {
+            color: #C0C4CC;
+          }
           .report-item-info {
             display: flex;
           }
@@ -422,29 +289,5 @@ onUnmounted(() => {
     }
 
   }
-  .right-aside-box{
-    .recommand-banner {
-      width: 420px;
-      height: 240px;
-      margin-bottom: 15px;
-    }
-
-    .recmd-box {
-      .label {
-        font-size: 24px;
-      }
-
-      .recmd-wrap {
-        flex-wrap: wrap;
-        gap: 10px;
-        justify-content: space-around;
-        .recmd-item {
-          width: 120px;
-          height: 120px;
-          background: #3D5EFF;
-        }
-      }
-    }
-  }
 }
 </style>

+ 109 - 0
src/views/report/PosterDetail.vue

@@ -0,0 +1,109 @@
+<script setup>
+import { ref } from 'vue'
+import moment from 'moment'
+import { useRoute, useRouter } from 'vue-router'
+import { useReportStore } from '@/store/modules/report'
+import { useUserInfo } from '@/hooks/useUserInfo'
+import { ChevronLeftIcon } from 'tdesign-icons-vue-next'
+import { MessagePlugin } from 'tdesign-vue-next'
+
+
+const router = useRouter()
+const route = useRoute()
+
+const reportStore = useReportStore()
+
+
+// 实现头部的适配
+const contentRef=ref('')
+const headerRef=ref('')
+let contentWidth=ref('')
+/* 重绘固定头宽度 */
+const resetHeaderWidthHandle = () => {
+  contentWidth.value = contentRef.value.offsetWidth
+}
+onMounted(() => {
+  nextTick(() => resetHeaderWidthHandle())
+  
+  window.addEventListener('resize',resetHeaderWidthHandle)
+  window.addEventListener('resize',resetHeaderWidthHandle)
+});
+
+onUnmounted(() => {
+  window.removeEventListener('resize',resetHeaderWidthHandle)
+})
+
+</script>
+<template>
+  <div class="report-detail-page safe-content">
+    <div class="top-nav-wrap" ref="headerRef" :style="{'width':`${contentWidth}px`}">
+      <div class="flex top">
+        <div class="flex back" @click="router.go(-1)">
+          <ChevronLeftIcon />
+          返回
+        </div>
+
+        <div class="flex">
+          
+        </div>
+      </div>  
+    </div>
+
+    <div class="report-box" ref="contentRef">
+      <img :src="reportStore.posterInfo?.LongImg" alt="">
+    </div>
+  </div>  
+
+  <t-back-top
+    :visible-height="200"
+    shape="circle"
+    theme="primary"
+    style="position: fixed; right: 30px; bottom: 40px"
+  ></t-back-top>
+</template>
+<style scoped lang="scss">
+@import './css/index.scss';
+.top-nav-wrap {
+    position: fixed;
+    top: 70px;
+    z-index: 99;
+    background-color: #fff;
+    padding-top: 30px;
+    padding-bottom: 20px;
+    width: 1240px;
+    border-bottom: 1px solid #DCDFE6;
+    .top {
+      justify-content: space-between;
+      font-size: 16px;
+    }
+    h2 {
+      margin: 0;
+      font-size: 24px;
+      font-style: italic;
+      margin-right: 15px;
+      cursor: pointer;
+      flex-shrink:0;
+    }
+    .back {
+      cursor: pointer;
+      &:hover {
+        color: #3D5EFF;
+      }
+    }
+    .collect-icon {
+      padding: 2px;
+      border: 1px solid #C8CDD9;
+      border-radius: 4px;
+      margin-right:5px;
+    }
+}
+
+.report-box {
+  display: flex;
+  margin-top: 90px;
+  img {
+    max-width: 100%;
+    margin: 0 auto;
+  }
+}
+</style>

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

@@ -1,10 +1,269 @@
 <script setup>
-import { ref } from 'vue'
+import { reactive, ref } from 'vue'
+import { SearchIcon,InfoCircleFilledIcon,ChevronLeftIcon } from 'tdesign-icons-vue-next';
+import { useReportStore } from '@/store/modules/report'
+import { apiEtaReport } from '@/api/etaReport'
+import { useRouter } from 'vue-router';
+import { useReport } from './hooks/useReport'
+import moment from 'moment'
+import 'moment/locale/zh-cn';
+moment.locale('zh-cn')
+import LoadList from '@/components/LoadList.vue'
+import RightSideWrap from './components/RightSlideWrap.vue'
+
+const reportStore = useReportStore();
+const  { handleToDetail } = useReport()
+
+const router = useRouter()
+
+const searchText = ref('')
+
+const listState = reactive({
+  list: [],
+  page: 1,
+  pageSize: 20,
+  finished: true,
+  loading: false,
+  searchText:'',
+  showAll: reportStore.showAllReport||false
+})
+
+async function getList() {
+  listState.loading=true
+  const res = await apiEtaReport.reportSearch({
+    PageSize: listState.pageSize,
+    CurrentIndex: listState.page,
+    ShowAll: listState.showAll,
+    Keyword: listState.searchText
+  })
+  
+  listState.loading=false
+  if(res.Ret !== 200) return
+  if(listState.page===1){
+    document.body.scrollTop=document.documentElement.scrollTop=0
+  }
+
+  if(res.Ret !== 200) return
+
+  if(res.Data.Paging.IsEnd){
+    listState.finished=true
+  }
+
+  let arr=res.Data.List||[];
+  listState.list=[...listState.list,...arr];
+
+}
+function onLoad() {
+  listState.page++;
+  getList()
+}
+
+function initList() {
+  listState.page = 1;
+  getList()
+}
+
+
+function handleClickClassify() {
+  router.push('/etaReport/classify')
+}
+
+
+// 实现头部的适配
+const contentRef=ref('')
+const headerRef=ref('')
+let contentWidth=ref('')
+let headerHight=ref('')
+/* 重绘固定头宽度 */
+const resetHeaderWidthHandle = () => {
+  contentWidth.value = contentRef.value.offsetWidth+'px'
+  headerHight.value = headerRef.value.offsetHeight-10+'px'
+}
+onMounted(() => {
+  resetHeaderWidthHandle()
+  window.addEventListener('resize',resetHeaderWidthHandle)
+});
+
+onUnmounted(() => {
+  window.removeEventListener('resize',resetHeaderWidthHandle)
+})
 
 </script>
 <template>
-  <div></div>
+  <div class="report-index-page safe-content hasrightaside-box">
+
+    <div class="content-box report-main" ref="contentRef">
+      <div class="top-nav-wrap" ref="headerRef" :style="{'width':contentWidth}">
+        <div class="flex">
+          <div class="flex back" @click="router.go(-1)">
+            <ChevronLeftIcon />
+            返回
+          </div>
+          <div class="flex">
+            <t-radio allow-uncheck v-model="listState.showAll" @change="reportStore.changeShowAll(listState.showAll);initList()">显示全部 </t-radio>
+            <t-tooltip 
+              content="未勾选时展示所有客户有阅读权限的报告,勾选时展示弘则ETA所有已发布的报告"
+            >
+              <InfoCircleFilledIcon style="margin-left:5px;"/>
+            </t-tooltip>
+
+          </div>
+        </div>
+
+        <t-input 
+          v-model="listState.searchText" 
+          placeholder="请输入搜索内容" 
+          clearable
+          size="large"
+          style="width:400px"
+          autofocus
+          @change="initList"
+        >
+          <template #prefixIcon>
+            <SearchIcon />
+          </template>
+        </t-input>
+      </div>
+
+      <!-- 报告列表 -->
+      <LoadList 
+        emptyMsg="暂无搜索结果"
+        :finished="listState.finished" 
+        :isEmpty="listState.list.length === 0 && listState.finished" 
+        :loading="listState.loading" 
+        :count="listState.list.length"
+        @listOnload="onLoad"
+      >
+        <div class="report-list-wrap"  v-if="listState.list.length">
+          <div class="content-item" 
+            :class="!item.HasAuth?'noAuth':''"
+            v-for="item in listState.list" 
+            :key="`${item.ReportId}_${item.ReportChapterId}`" 
+            @click="handleToDetail(item)"
+          >
+              <div class="report-item-info">
+                <img :src="item.ClassifyCoverImg" class="report-img">
+                <div class="right-info">
+                  <div class="c-stage flex">
+                    <div>
+                      <t-tag theme="primary" v-if="item.PermissionNames">{{item.PermissionNames[0]}}</t-tag>
+                      {{ `第${item.Stage}期` }}
+                      | {{ item.ClassifyName }}
+
+                    </div>
+                    
+                    <div class="c-time">{{ item.PublishTime }}</div>
+                  </div>
+                  <div class="c-title text-ellipsis--l1">{{ item.Title }}</div>
+                  <div class="desc text-ellipsis--l1" v-html="item.Contentsub"></div>
+                </div>
+              </div>
+            </div>
+        </div>
+      </LoadList>
+
+    </div>
+
+    <!-- 右侧 -->
+    <RightSideWrap/>
+
+
+  </div>
 </template>
 <style scoped lang="scss">
+@import './css/index.scss';
+.report-index-page {
+  position: relative;
+
+  .report-main {
+    .top-nav-wrap {
+      position: fixed;
+      top: 70px;
+      z-index: 99;
+      background-color: #fff;
+      padding-top: 30px;
+      padding-bottom: 20px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: 740px;
+      border-bottom: 1px solid #DCDFE6;
+      h2 {
+        margin: 0;
+        font-size: 24px;
+        font-style: italic;
+        margin-right: 15px;
+      }
+      .back {
+        margin-right: 30px;
+        cursor: pointer;
+        font-size: 16px;
+      }
+    }
+
+    .report-list-wrap {
+      margin-top: 100px;
+      .content-item {
+        padding: 20px 0;
+        border-bottom: 1px solid #DCDFE6;
+        position: relative;
+        &.noAuth {
+          color: #C0C4CC;
+          .c-time,.desc {
+            color: #C0C4CC;
+          }
+        }
+        .right-info{
+          flex: 1;
+        }
+
+        .report-item-info {
+          display: flex;
+        }
+
+        .t-tag--primary {
+          background-color: #49517E;
+          margin-right: 10px;
+        }
+
+        .c-time {
+          color: #666;
+          font-size: 14px;
+        }
+        .report-img {
+          width: 72px;
+          height: 96px;
+          margin-right: 10px;
+        }
+
+        .c-stage {
+          font-size: 16px;
+          justify-content: space-between;
+        }
+        .c-title {
+          font-size: 16px;
+          font-weight: bold;
+          word-wrap: break-word;
+          white-space: normal;
+          word-break: break-all;
+          margin-top: 10px;
+        }
+
+        .desc {
+          line-height: 1.5;
+          margin-top: 10px;
+          color: #666666;
+          font-size: 14px;
+          word-wrap: break-word;
+          word-break: break-all;
+          :deep(div){
+            word-wrap: break-word;
+            word-break: break-all;
+          }
+        }
+      }
+    }
 
+  }
+}
 </style>

+ 12 - 3
src/views/report/components/ChapterWrap.vue

@@ -5,13 +5,15 @@ const props = defineProps({
   chapterId: Number
 })
 
+const emit = defineEmits(['change'])
+
 
 </script>
 <template>
   <div class="chapter-list-wrap">
-    <div :class="['item',{act: item.chapterId===chapterId}]" v-for="item in list" :key="item">
-      <div class="chapter-title text-ellipsis--l1" v-text="item.title"></div>
-      <p class="text-ellipsis--l1" v-text="item.report_chapter_type_name"></p>
+    <div :class="['item',{act: item.ReportChapterId===chapterId},{disabled: !item.HasAuth}]" v-for="item in list" :key="item" @click="emit('change',item)">
+      <div class="chapter-title text-ellipsis--l1" v-text="item.TypeName" v-if="item.TypeName"></div>
+      <p class="text-ellipsis--l1" v-text="item.Title"></p>
     </div>
   </div>
 </template>
@@ -42,5 +44,12 @@ const props = defineProps({
       color: #7C54FF;
     }
   }
+  &.disabled {
+      color: #C0C4CC;
+      background: #F8F8F8;
+    .chapter-title {
+      color: #C0C4CC;
+    }
+  }
 }
 </style>

+ 98 - 0
src/views/report/components/RightSlideWrap.vue

@@ -0,0 +1,98 @@
+<script setup>
+import { ref } from 'vue'
+import { useRouter } from 'vue-router'
+import { apiEtaReport } from '@/api/etaReport'
+import { useReportStore } from '@/store/modules/report'
+
+const router = useRouter()
+const reportStore = useReportStore();
+
+
+const firstClassifyList = ref([])
+async function getPermissionList() {
+  const res = await apiEtaReport.permissionList()
+  if(res.Ret !== 200) return
+  firstClassifyList.value = res.Data.map(_ => ({
+    ..._,
+    Child: null
+  }));
+}
+getPermissionList()
+
+
+async function getPosterInfo() {
+  const res = await apiEtaReport.reportPoster();
+  if(res.Ret !== 200) return
+  reportStore.setPosterInfo(res.Data || '')
+}
+getPosterInfo()
+
+
+function handleClickClassify(item) {
+  router.push(`/etaReport/classify?permissionId=${item.PermissionId}`)
+}
+
+function handleToBanner() {
+  router.push('/etaReport/posterDetail')
+}
+
+</script>
+<template>
+  <div class="right-aside-box">
+    <div class="fix-top" style="z-index: 100;">
+      <div class="recommand-banner" :style="`background-image: url(${reportStore.posterInfo?.ShortImg})`" @click="handleToBanner"></div>
+      <div class="recmd-box">
+        <div class="label">行业板块</div>
+        <div class="flex recmd-wrap">
+
+          <div 
+            class="recmd-item" 
+            v-for="item in firstClassifyList" 
+            :key="item.PermissionName"
+            @click="handleClickClassify(item)"
+            :style="`background-image: url('${item.ForumIndustryImgUrl}')`"
+          >
+            <div class="title" :style="`width:${item.PermissionName.length>3?'65px':'auto'}`">{{item.PermissionName}}</div>
+          </div>
+        </div>
+      </div>
+
+    </div>
+  </div>
+</template>
+<style scoped lang="scss">
+@import '../css/index.scss';
+.right-aside-box{
+    .recommand-banner {
+      width: 420px;
+      height: 240px;
+      margin-bottom: 15px;
+      background-size: cover;
+      cursor: pointer;
+    }
+
+    .recmd-box {
+      .label {
+        font-size: 24px;
+      }
+
+      .recmd-wrap {
+        flex-wrap: wrap;
+        gap: 10px;
+        justify-content: space-around;
+        .recmd-item {
+          width: 120px;
+          height: 120px;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          .title {
+            color: #fff;
+            font-size: 28px;
+            text-align: center;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 15 - 0
src/views/report/hooks/useReport.js

@@ -0,0 +1,15 @@
+import { useRouter } from 'vue-router'
+import { MessagePlugin } from 'tdesign-vue-next'
+export function useReport() {
+
+  const router = useRouter()
+  function handleToDetail(item) {
+    if(!item.HasAuth) return MessagePlugin.warning('您暂未开通该报告阅读权限,请联系销售!')
+
+    router.push(`/etaReport/detail?id=${item.ReportId}&chapterId=${item.ReportChapterId||''}`)
+  }
+
+  return {
+    handleToDetail
+  }
+}

+ 10 - 6
src/views/user/favorite/Index.vue

@@ -1,17 +1,21 @@
 <script setup>
 import { ref } from 'vue'
-import Aslide from './components/Aslide.vue'
+import { useRoute } from 'vue-router'
+import Aslide from './components/CollectAslide.vue'
 import etaReport from './etaReport.vue'
 import etaChart from './etaChart.vue'
 
+const route = useRoute()
+
+function getComponents() {
+  return route.path==='/etaChart/favorite' ? etaChart : etaReport;
+}
 </script>
 <template>
-  <div>
-    <Aslide/>
-
-    <etaChart/>
+  <div class="flex">
+    <Aslide />
 
-    <!-- <etaReport/> -->
+    <component :is="getComponents()"></component>
   </div>
 </template>
 <style scoped lang="scss">

+ 0 - 10
src/views/user/favorite/components/Aslide.vue

@@ -1,10 +0,0 @@
-<script setup>
-import { ref } from 'vue'
-
-</script>
-<template>
-  <div></div>
-</template>
-<style scoped lang="scss">
-
-</style>

+ 53 - 0
src/views/user/favorite/components/CollectAslide.vue

@@ -0,0 +1,53 @@
+<script setup>
+import { ref } from 'vue'
+import { useRouter,useRoute } from 'vue-router'
+
+const route = useRoute()
+const router = useRouter()
+
+const emit = defineEmits(['change'])
+
+const menu = [
+  {  
+    title: 'ETA图库',
+    path: '/etaChart/favorite',
+    icon:'chart'
+  },
+  {  
+    title: 'ETA研报',
+    path: '/etaReport/favorite',
+    icon:'report'
+  },
+]
+const activeMenu = ref(route.path)
+
+function handleClickItem(value) {
+  router.push(value)
+}
+</script>
+<template>
+<div class="left-aslide">
+  <t-menu v-model="activeMenu" theme="light" @change="handleClickItem" width="200px">
+    <t-menu-item :value="item.path" v-for="item in menu" :key="item.path">
+      <svg-icon 
+        :name="item.icon"
+        style="font-size:20px;margin-right:10px;"
+      ></svg-icon>
+      {{item.title}}
+    </t-menu-item>
+  </t-menu>
+</div>
+</template>
+<style scoped lang="scss">
+.left-aslide {
+  margin-right: 20px;
+  background: #fff;
+  border: 1px solid #D8D8D8;
+  box-shadow: 0px 4px 12px 0px #23003514;
+  border-radius: 4px;
+}
+:deep(.t-menu__content) {
+  display: flex;
+  align-items: center;
+}
+</style>

+ 15 - 14
src/views/user/favorite/components/CollectReport.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { apiETAChartUser } from '@/api/etaChart'
+import { apiEtaReportCollect } from '@/api/etaReport'
 import { MessagePlugin } from 'tdesign-vue-next'
 import EditClassify from './EditClassify.vue'
 
@@ -18,7 +18,7 @@ const props = defineProps({
 const selectClassify = ref('')
 const classifyOpts = ref([])
 async function getClassifyOpts() {
-  const res = await apiETAChartUser.classifyList()
+  const res = await apiEtaReportCollect.classifyList()
   if (res.Ret !== 200) return
   const arr = res.Data?.List || []
   classifyOpts.value = arr
@@ -41,22 +41,22 @@ async function handleSave(){
     MessagePlugin.warning('请选择分类')
     return
   }
-  const res=await apiETAChartUser.chartCollect({
-    ChartInfoId:props.data.ChartInfoId,
+  const res=await apiEtaReportCollect.reportCollect({
+    ReportId:props.data.reportId,
+    ReportChapterId: props.data.chapterId,
     CollectClassifyId:selectClassify.value
   })
   if(res.Ret!==200) return
   show.value=false
-  if(res.Msg==='已收藏,可选择在ETA投研资源库-我的收藏/ETA-我的图库中-ETA投研资源库查看'){
-    await $confirmDialog({
-      header:'提示',
-      body: res.Msg,
-      confirmBtn:'知道了',
-      cancelBtn:null,
-    });
-  }else{
-    MessagePlugin.success('收藏成功')
-  }
+  // if(res.Msg==='已收藏,可选择在ETA投研资源库-我的收藏/ETA-我的图库中-ETA投研资源库查看'){
+  //   await $confirmDialog({
+  //     header:'提示',
+  //     body: res.Msg,
+  //     confirmBtn:'知道了',
+  //     cancelBtn:null,
+  //   });
+  // }else{
+  MessagePlugin.success('收藏成功')
   emits('success')
 }
 
@@ -99,6 +99,7 @@ async function handleSave(){
   <EditClassify 
     v-model:show="showEditClassify" 
     :data="null" 
+    source="report"
     @change="getClassifyOpts"
   />
 </template>

+ 27 - 0
src/views/user/favorite/components/EditClassify.vue

@@ -1,7 +1,9 @@
 <script setup>
 import { useTemplateRef } from "vue"
 import {apiETAChartUser} from '@/api/etaChart'
+import { apiEtaReportCollect } from '@/api/etaReport'
 import {MessagePlugin} from 'tdesign-vue-next'
+import { async } from "@antv/x6/lib/registry/marker/async"
 
 
 
@@ -12,6 +14,10 @@ const props = defineProps({
   data: {
     type: [null, Object],
     default: null
+  },
+  source: { // 研报复用  report 默认chart
+    type: String,
+    default: 'chart'
   }
 })
 
@@ -25,6 +31,27 @@ const formIns=useTemplateRef('formIns')
 async function handleSave(){
   const validRes=await formIns.value.validate()
   if(validRes!==true) return
+
+  props.source === 'report' ? handleReoprtApi() : handleChartApi()
+
+ 
+}
+
+async function handleReoprtApi() {
+  const res=props.data
+    ? await apiEtaReportCollect.editClassify({
+      ClassifyName:formData.classifyName,
+      CollectClassifyId:props.data.CollectClassifyId
+    })
+    : await apiEtaReportCollect.addClassify({
+      ClassifyName:formData.classifyName
+    })
+  if(res.Ret!==200) return
+  MessagePlugin.success(props.data?'编辑成功':'新增成功')
+  show.value=false
+  emits('change')
+}
+async function handleChartApi() {
   const res=props.data?await apiETAChartUser.editClassify({
     ClassifyName:formData.classifyName,
     CollectClassifyId:props.data.CollectClassifyId

+ 21 - 18
src/views/user/favorite/components/MoveReport.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { apiETAChartUser } from '@/api/etaChart'
+import { apiEtaReportCollect,apiEtaReport } from '@/api/etaReport'
 import { SearchIcon } from 'tdesign-icons-vue-next'
 import { MessagePlugin } from 'tdesign-vue-next'
 
@@ -27,12 +27,12 @@ const columns=[
     width: 50,
   },
   {
-    colKey: 'ChartName',
-    title: '图表名称',
+    colKey: 'Title',
+    title: '研报名称',
     align: 'center'
   },
   {
-    colKey: 'SysUserRealName',
+    colKey: 'AdminRealName',
     title: '创建人',
     width: '120',
     align: 'center'
@@ -47,8 +47,8 @@ const pagination = ref({
   showPageSize:false
 });
 async function getChartList(){
-  const res=await apiETAChartUser.chartCollectList({
-    CollectClassifyIds:selectClassify.value?selectClassify.value.join(','):'',
+  const res=await apiEtaReportCollect.reportCollectList({
+    CollectClassifyId:selectClassify.value?selectClassify.value.join(','):'',
     Keyword:keyword.value,
     PageSize:pagination.value.pageSize,
     CurrentIndex:pagination.value.current
@@ -56,11 +56,14 @@ async function getChartList(){
   if(res.Ret!==200) return
   const arr=res.Data.List||[]
   pagination.value.total=res.Data.Paging.Totals
-  tableData.value=arr
+  tableData.value=arr.map(_ => ({
+    ..._,
+    singleId: _.ReportChapterId || _.ReportId
+  }))
   if(checkAll.value){
     tableData.value.forEach(item=>{
-      if(!selectedRowKeys.value.includes(item.ChartInfoId)&&!unSelectedCharts.includes(item.ChartInfoId)){
-        selectedRowKeys.value.push(item.ChartInfoId)
+      if(!selectedRowKeys.value.includes(item.singleId)&&!unSelectedCharts.includes(item.singleId)){
+        selectedRowKeys.value.push(item.singleId)
       }
     })
   }
@@ -106,7 +109,7 @@ function handleTableSelectChange(value, { type, currentRowKey }) {
   updateSelectionState();
 }
 function handleFullSelection(isCheck) {
-  const currentPageIds = new Set(tableData.value.map(item => item.ChartInfoId));
+  const currentPageIds = new Set(tableData.value.map(item => item.singleId));
   
   if (isCheck) {
     // 全选时从排除列表移除当前页所有ID
@@ -144,8 +147,8 @@ function handleClickCheckAll(check) {
     indeterminate.value=false
     unSelectedCharts=[]
     tableData.value.forEach(item=>{
-      if(!selectedRowKeys.value.includes(item.ChartInfoId)){
-        selectedRowKeys.value.push(item.ChartInfoId)
+      if(!selectedRowKeys.value.includes(item.singleId)){
+        selectedRowKeys.value.push(item.singleId)
       }
     })
   }else{
@@ -167,12 +170,12 @@ async function handleSave() {
   }
   const params={
     SelectAll:checkAll.value,
-    CollectClassifyIds:selectClassify.value?selectClassify.value.join(','):'',
+    CollectClassifyIds:selectClassify.value?selectClassify.value:[],
     Keyword:keyword.value,
-    ChartInfoIds:checkAll.value?unSelectedCharts.join(',') : selectedRowKeys.value.join(','),
+    Collects:checkAll.value?unSelectedCharts : selectedRowKeys.value,
     CollectClassifyId:newClassify.value,
   }
-  const res=await apiETAChartUser.chartCollectBatchMove(params)
+  const res=await apiEtaReportCollect.reportCollectBatchMove(params)
   if(res.Ret!==200) return
   MessagePlugin.success('转移成功')
   show.value=false
@@ -196,7 +199,7 @@ async function handleSave() {
     <div class="flex top-filter-wrap">
       <t-select
         v-model="selectClassify"
-        placeholder="图表分类"
+        placeholder="研报分类"
         multiple
         :min-collapsed-num="1"
         clearable
@@ -210,7 +213,7 @@ async function handleSave() {
           :value="item.CollectClassifyId"
         ></t-option>
       </t-select>
-      <t-input placeholder="请输入图表名称" v-model="keyword" @change="handleRefreshList" style="max-width: 600px">
+      <t-input placeholder="请输入研报名称" v-model="keyword" @change="handleRefreshList" style="max-width: 600px">
         <template #prefixIcon>
           <SearchIcon />
         </template>
@@ -223,7 +226,7 @@ async function handleSave() {
       >全选</t-checkbox>
     </div>
     <t-table
-      row-key="ChartInfoId"
+      row-key="singleId"
       :data="tableData"
       :columns="columns"
       bordered

+ 11 - 9
src/views/user/favorite/components/ReportClassifyWrap.vue

@@ -1,8 +1,9 @@
 <script setup>
 import {apiETAChartUser} from '@/api/etaChart'
+import { apiEtaReportCollect } from '@/api/etaReport'
 import { useTemplateRef, watch } from "vue";
 import EditClassify from './EditClassify.vue'
-import MoveChart from './MoveChart.vue'
+import MoveReport from './MoveReport.vue'
 import { MessagePlugin } from 'tdesign-vue-next';
 
 const emits=defineEmits(['change'])
@@ -38,7 +39,7 @@ const classifyActions=[
 const classifyList=ref([])
 // 获取分类
 async function getClassifyList(){
-  const res=await apiETAChartUser.classifyList()
+  const res=await apiEtaReportCollect.classifyList()
   if(res.Ret!==200)return
   const arr=res.Data?.List||[]
   classifyList.value=arr
@@ -62,7 +63,7 @@ async function handleSortEnd({node}){
     NextCollectClassifyId:moveTargetIndex===resArr.length-1?0:resArr[moveTargetIndex+1].CollectClassifyId,
     CollectClassifyId:node.value
   }
-  const res=await apiETAChartUser.moveClassify(params)
+  const res=await apiEtaReportCollect.moveClassify(params)
   if(res.Ret!==200){
     getClassifyList()
     return
@@ -75,9 +76,9 @@ async function handleSortEnd({node}){
 async function handleDeleteClassify(node){
   await $confirmDialog({
     header:'提示',
-    body: '若删除该分类,则分类下关联的所有图表将被全部删除,是否继续?',
+    body: '若删除该分类,则分类下关联的所有本报告将被全部删除,是否继续?',
   });
-  const res=await apiETAChartUser.deleteClassify({
+  const res=await apiEtaReportCollect.deleteClassify({
     CollectClassifyId:node.value
   })
   if(res.Ret!==200) return
@@ -93,13 +94,13 @@ function clickHandler(option,node){
 }
 
 // 转移分类
-const showMoveChart=ref(false)
+const showMoveReport=ref(false)
 
 </script>
 
 <template>
   <div class="bg-white flex_col classify-wrap">
-    <h3 class="label-text">ETA图库</h3>
+    <h3 class="label-text">ETA研报</h3>
     <div class="classify-tree"> 
       <t-tree
         ref="treeIns"
@@ -134,7 +135,7 @@ const showMoveChart=ref(false)
         <t-icon name="add-rectangle" size="20px"></t-icon>
         <span>添加分类</span>
       </div>
-      <div class="opt-item" @click="showMoveChart=true">
+      <div class="opt-item" @click="showMoveReport=true">
         <t-icon name="swap" size="20px"></t-icon>
         <span>转移分类</span>
       </div>
@@ -143,11 +144,12 @@ const showMoveChart=ref(false)
   <!-- 新增\编辑分类 -->
   <EditClassify 
     v-model:show="showEditClassify" 
+    source="report"
     :data="activeClassifyData" 
     @change="getClassifyList"
   />
   <!-- 转移分类 -->
-  <MoveChart v-model:show="showMoveChart" :classifyOpts="classifyList" @change="emits('change',selecClassify)"/>
+  <MoveReport v-model:show="showMoveReport" :classifyOpts="classifyList" @change="emits('change',selecClassify)"/>
 </template>
 
 <style lang="scss" scoped>

+ 1 - 0
src/views/user/favorite/etaChart.vue

@@ -127,6 +127,7 @@ async function handleCancelCollect(item,index){
 
 <style lang="scss" scoped>
 .my-favorite-chart-page{
+  flex: 1;
   gap: 0 20px;
   height: calc(100vh - 120px);
   .left-wrap{

+ 176 - 86
src/views/user/favorite/etaReport.vue

@@ -1,22 +1,50 @@
 <script setup>
 import {SearchIcon} from 'tdesign-icons-vue-next'
 import ClassifyWrap from './components/ReportClassifyWrap.vue'
-import {apiETAChartUser} from '@/api/etaChart'
+import { apiEtaReportCollect,apiEtaReport } from '@/api/etaReport'
+import { useReport } from '../../report/hooks/useReport'
 import { MessagePlugin } from 'tdesign-vue-next'
+import moment from 'moment'
+import { ref,reactive } from 'vue'
+
+
+const  { handleToDetail } = useReport()
+
+const filterState = reactive({
+  industry: [],
+  reportClassifyIds: [],
+  keyword: ''
+})
+const industryOpts = ref([])
+async function getPermissionList() {
+  const res = await apiEtaReport.permissionList()
+  if(res.Ret !== 200) return
+  industryOpts.value = res.Data || [];
+}
+getPermissionList()
+
+const reportClassifyList = ref([])
+async function getReportClassify() {
+  const res = await apiEtaReportCollect.etaReportClassify()
+  if(res.Ret !== 200) return
+  reportClassifyList.value = res.Data
+}
+getReportClassify()
+
 
 const classifyId=ref(0)
 // const SysUserIds=ref([])
-const keyword=ref('')
-const chartList=ref([])
+const list=ref([])
 const page=ref(1)
 const pageSize=ref(30)
 const total=ref(0)
 const finished=ref(false)
 async function getReportList(){
-  const res=await apiETAChartUser.chartCollectList({
-    CollectClassifyIds:classifyId.value||'',
-    // SysUserIds:SysUserIds.value.join(','),
-    Keyword:keyword.value,
+  const res=await apiEtaReportCollect.reportCollectList({
+    CollectClassifyId:classifyId.value||'',
+    PermissionIds:filterState.industry.join(','),
+    ClassifyIds: filterState.reportClassifyIds.join(','),
+    Keyword:filterState.keyword,
     PageSize:pageSize.value,
     CurrentIndex:page.value
   })
@@ -24,17 +52,17 @@ async function getReportList(){
   const arr=res.Data.List||[]
   total.value=res.Data.Paging.Totals
   finished.value=res.Data.Paging.IsEnd
-  chartList.value=arr
+  list.value=arr
 }
 
-function onPageChange(e){
-  page.value=e
-  getChartList()
+function onLoad(){
+  page.value++
+  getReportList()
 }
 function refreshList(){
   page.value=1
-  chartList.value=[]
-  getChartList()
+  list.value=[]
+  getReportList()
 }
 function handleClassifyChange(e){
   refreshList()
@@ -47,21 +75,15 @@ function handleSearch(){
 }
 
 
-const showChartDetailPop=ref(false)
-const activeChartInfo=ref(null)
-function handleShowChartDetail(e){
-  activeChartInfo.value=e
-  showChartDetailPop.value=true
-}
-
 // 取消收藏
 async function handleCancelCollect(item,index){
-  const res=await apiETAChartUser.chartCollectCancel({
-    ChartInfoId:item.ChartInfoId
+  const res=await apiEtaReportCollect.reportCollectCancel({
+    ReportId: item.ReportId,
+    ReportChapterId: item.ReportChapterId
   })
   if(res.Ret!==200) return
   MessagePlugin.success('取消成功')
-  chartList.value.splice(index,1)
+  list.value.splice(index,1)
 }
 
 </script>
@@ -73,10 +95,47 @@ async function handleCancelCollect(item,index){
     </div>
     <div class="flex right-wrap">
       <div class="flex top-filter">
-        <!-- <select-chart-creator v-model="SysUserIds" @change="refreshList" style="width:300px" size="large" /> -->
+
+        <t-cascader 
+          v-model="filterState.industry" 
+          :options="industryOpts" 
+          :minCollapsedNum="1"
+          size="large"
+          :keys="{
+              label: 'PermissionName',
+              value: 'PermissionId',
+              children: 'Child'
+          }"
+          filterable
+          multiple 
+          clearable
+          placeholder="行业板块"
+          style="width:250px"
+          @change="refreshList"
+        />
+
+        <t-cascader 
+          v-model="filterState.reportClassifyIds" 
+          :options="reportClassifyList" 
+          :minCollapsedNum="1"
+          size="large"
+          :keys="{
+              label: 'ClassifyName',
+              value: 'Id',
+              children: 'Children',
+          }"
+          :checkStrictly="true"
+          filterable
+          multiple 
+          clearable
+          placeholder="报告分类"
+          style="width:250px"
+          @change="refreshList"
+        />
+
         <t-input 
-          v-model="keyword" 
-          placeholder="请输入图表名称" 
+          v-model="filterState.keyword" 
+          placeholder="请输入研报名称" 
           size="large" 
           style="max-width:600px"
           clearable
@@ -87,41 +146,50 @@ async function handleCancelCollect(item,index){
           </template>
         </t-input>
       </div>
-      <empty-wrap v-if="finished&&chartList.length===0" />
-      <div class="chart-list-wrap" v-else>
-        <ul class="flex chart-list">
-          <li class="bg-white chart-item" v-for="item,index in chartList" :key="item.ChartCollectId">
-            <div class="flex chart-name">
-              <div class="text-ellipsis--l1 name">{{item.ChartName}}</div>
-              <svg-icon name="star_fill" style="font-size:20px;cursor: pointer;" @click="handleCancelCollect(item,index)"></svg-icon>
-              <svg-icon name="full_screen" style="font-size:20px;margin-left:20px;cursor: pointer;" @click="handleShowChartDetail(item)"></svg-icon>
-            </div>
-            <img class="chart-img" :src="item.ChartImage" alt="">
-            <div class="time">
-              <span>收藏时间:</span>
-              <span>{{formatTime(item.CollectTime,'YYYY-MM-DD HH:mm:ss')}}</span>
+      <!-- 报告列表 -->
+      <LoadList 
+        emptyMsg="暂无搜索结果"
+        :finished="finished" 
+        :isEmpty="list.length === 0 && finished" 
+        :loading="loading" 
+        :count="list.length"
+        @listOnload="onLoad"
+      >
+        <div class="report-list-wrap"  v-if="list.length">
+          <div 
+            class="content-item" 
+            v-for="item in list"
+            :key="`${item.ReportId}_${item.ReportChapterId}`" 
+            @click="handleToDetail(item)"
+          >
+              <div class="report-item-info">
+                <img :src="item.ClassifyCoverImg" class="report-img">
+                <div class="right-info">
+                  <div class="c-stage flex">
+                    <div>
+                      <t-tag theme="primary" v-if="item.PermissionNames">{{item.PermissionNames[0]}}</t-tag>
+                      {{ `第${item.Stage}期` }}
+                      | {{ item.ClassifyName }}
+
+                    </div>
+                    
+                    <div class="c-time">{{ item.PublishTime }}</div>
+                  </div>
+                  <div class="c-title text-ellipsis--l1">{{ item.Title }}</div>
+                  <div class="desc text-ellipsis--l1" v-html="item.Abstract"></div>
+                </div>
+              </div>
             </div>
-          </li>
-        </ul>
-        <t-pagination
-          v-model="page"
-          :pageSize="pageSize"
-          :total="total"
-          :totalContent="true"
-          :showPageSize="false"
-          @current-change="onPageChange"
-          style="margin-top:20px"
-        />
-      </div>
+        </div>
+      </LoadList>
     </div>
   </div>
 
-  <!-- 图表详情弹窗 -->
-  <ChartDetailPop v-model:show="showChartDetailPop" :chartInfoId="activeChartInfo?.ChartInfoId" @collectChange="refreshList()"/>
 </template>
 
 <style lang="scss" scoped>
 .my-favorite-chart-page{
+  flex: 1;
   gap: 0 20px;
   height: calc(100vh - 120px);
   .left-wrap{
@@ -149,42 +217,64 @@ async function handleCancelCollect(item,index){
       margin-bottom: 20px;
       gap: 0 20px;
     }
-    .chart-list-wrap{
-      flex: 1;
-      overflow-y: auto;
-    }
   }
-  .chart-list{
-    flex-wrap: wrap;
-    gap: 20px;
-    margin-block-start:0;
-    margin-block-end:0;
-    padding-inline-start:0;
-    .chart-item{
-      list-style-type: none;
-      width: calc(calc(100% - 60px)/4);
-      border: 1px solid var(--border-color);
-      box-shadow: 0px 4px 12px 0px #2300351F;
-      border-radius: 4px;
-      overflow: hidden;
-      .chart-name{
-        border-bottom: 1px solid var(--border-color);
-        padding: 14px;
-        .name{
-          font-size: 16px;
-          font-weight: 600;
-          flex: 1;
-        }
+  .report-list-wrap {
+    flex: 1;
+    overflow-y: auto;
+    .content-item {
+      padding: 20px;
+      margin-bottom: 10px;
+      border: 1px solid #DCDFE6;
+      position: relative;
+      background-color: #fff;
+
+      .right-info{
+        flex: 1;
+      }
+
+      .report-item-info {
+        display: flex;
+      }
+
+      .t-tag--primary {
+        background-color: #49517E;
+        margin-right: 10px;
       }
-      .chart-img{
-        display: block;
-        width: 100%;
-        height: 230px;
+
+      .c-time {
+        color: #666;
+        font-size: 14px;
       }
-      .time{
-        border-top: 1px solid var(--border-color);
-        padding: 10px 14px;
-        color: #999999;
+      .report-img {
+        width: 72px;
+        height: 96px;
+        margin-right: 10px;
+      }
+
+      .c-stage {
+        font-size: 16px;
+        justify-content: space-between;
+      }
+      .c-title {
+        font-size: 16px;
+        font-weight: bold;
+        word-wrap: break-word;
+        white-space: normal;
+        word-break: break-all;
+        margin-top: 10px;
+      }
+
+      .desc {
+        line-height: 1.5;
+        margin-top: 10px;
+        color: #666666;
+        font-size: 14px;
+        word-wrap: break-word;
+        word-break: break-all;
+        :deep(div){
+          word-wrap: break-word;
+          word-break: break-all;
+        }
       }
     }
   }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно