Browse Source

Merge branch 'master' into eta2.4.5

shanbinzhang 1 month ago
parent
commit
4bd530767d
50 changed files with 4944 additions and 108 deletions
  1. 8 0
      index.html
  2. 2061 0
      public/luckyexcel.umd.js
  3. 5 2
      src/CustomElement/EtaChart.ce.vue
  4. 24 1
      src/CustomElement/EtaTable.ce.vue
  5. 8 0
      src/CustomElement/api/getData.js
  6. 21 0
      src/api/etaForum.js
  7. 9 0
      src/api/sheet.js
  8. 36 14
      src/hooks/chart/render.js
  9. 8 0
      src/hooks/common.js
  10. 1 1
      src/lang/cn.js
  11. 1 1
      src/lang/en.js
  12. 12 2
      src/views/chartETA/ChartDetail.vue
  13. 1 1
      src/views/chartETA/Search.vue
  14. 1 1
      src/views/dataEDB/SearchList.vue
  15. 1 1
      src/views/dataEDB/calculate/components/SelectEDB.vue
  16. 17 3
      src/views/externalReport/List.vue
  17. 20 8
      src/views/myETA/ChartDetail.vue
  18. 13 5
      src/views/myETA/ChooseChart.vue
  19. 61 10
      src/views/myETA/Index.vue
  20. 1 1
      src/views/myETA/SearchList.vue
  21. 15 2
      src/views/myETA/components/EDBInfo.vue
  22. 27 1
      src/views/myETA/hooks/useClassify.js
  23. 6 1
      src/views/ppt/components/ChartWrap.vue
  24. 3 2
      src/views/report/AddReport.vue
  25. 2 1
      src/views/report/EditReport.vue
  26. 2 2
      src/views/report/PreviewDetail.vue
  27. 2 1
      src/views/report/Search.vue
  28. 1 1
      src/views/report/components/reportInsert/ETAChart.vue
  29. 204 0
      src/views/report/components/reportInsert/ETAForumChart.vue
  30. 8 2
      src/views/report/components/reportInsert/Index.vue
  31. 60 11
      src/views/report/components/reportInsert/MyETAChart.vue
  32. 1 1
      src/views/report/components/reportInsert/PriceChart.vue
  33. 2 1
      src/views/report/components/reportInsert/SandTableImg.vue
  34. 2 1
      src/views/report/components/reportInsert/SemanticsImg.vue
  35. 2 2
      src/views/report/components/reportInsert/SheetTableChart.vue
  36. 1 1
      src/views/report/components/reportInsert/StatisticAnalysis.vue
  37. 15 8
      src/views/report/smartReport/EditReport.vue
  38. 2 2
      src/views/reportEn/AddReport.vue
  39. 2 1
      src/views/reportEn/Search.vue
  40. 4 2
      src/views/sheetList/balanceDetail.vue
  41. 154 0
      src/views/sheetList/common/option.js
  42. 583 0
      src/views/sheetList/components/BalanceSheet.vue
  43. 610 0
      src/views/sheetList/components/MixedSheet.vue
  44. 163 0
      src/views/sheetList/components/SharedSheet.vue
  45. 100 0
      src/views/sheetList/components/Sheet.vue
  46. 634 0
      src/views/sheetList/components/TimelineSheet.vue
  47. 9 2
      src/views/sheetList/sharedDetail.vue
  48. 2 2
      src/views/sheetList/sharedSearch.vue
  49. 3 3
      src/views/tabbar/Home.vue
  50. 16 7
      src/views/tabbar/User.vue

+ 8 - 0
index.html

@@ -6,6 +6,11 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
     <title>投研平台</title>
     <link href='/froala_editor.pkgd.min.css' rel='stylesheet' type='text/css' />
+    	<!-- luckysheet cdn -->
+    <link rel='stylesheet' href='/Luckysheet@2.1.13/dist/plugins/css/pluginsCss.css' />
+    <link rel='stylesheet' href='/Luckysheet@2.1.13/dist/plugins/plugins.css' />
+    <link rel='stylesheet' href='/Luckysheet@2.1.13/dist/css/luckysheet.css' />
+    <link rel='stylesheet' href='/Luckysheet@2.1.13/dist/assets/iconfont/iconfont.css' />
   </head>
   <body>
     <div id="app"></div>
@@ -14,5 +19,8 @@
     <script type='text/javascript' src='/froala_editor.pkgd.min.js'></script>
     <script type='text/javascript' src='/froala_editor_zh_cn.js'></script>
     <script type='text/javascript' src="/aws-sdk.min.js"></script>
+    <script src="/Luckysheet@2.1.13/dist/plugins/js/plugin.js"></script>
+    <script src="/Luckysheet@2.1.13/dist/luckysheet.umd.js"></script>
+    <script src="/luckyexcel.umd.js"></script>
   </body>
 </html>

File diff suppressed because it is too large
+ 2061 - 0
public/luckyexcel.umd.js


+ 5 - 2
src/CustomElement/EtaChart.ce.vue

@@ -1,7 +1,7 @@
 <script setup>
 import {nextTick,ref} from 'vue'
 import {useChartRender} from '@/hooks/chart/render'
-import {chartInfoByCode} from './api/getData.js'
+import {chartInfoByCode,forumChartInfoByCode} from './api/getData.js'
 import {parseQueryString} from "./utils/index"
 const {chartRender,setLimitData,isUseSelfLimit}=useChartRender()
 
@@ -30,7 +30,10 @@ async function getChartData(){
         return
     } 
     try {
-        const res=await chartInfoByCode({
+        const res=params.isETAForumChart&&params.isETAForumChart=='true'?await forumChartInfoByCode({
+            UniqueCode:params.code,
+            IsReplace:1
+        }): await chartInfoByCode({
             UniqueCode:params.code,
             IsReplace:1,
             AuthToken: params.authToken || ''

+ 24 - 1
src/CustomElement/EtaTable.ce.vue

@@ -28,8 +28,10 @@ const setDefaultSource=(sourceText)=>{
 
 async function getTableData(){
     const res = await infoByCode({  
-        UniqueCode: params.code, 
+        UniqueCode: params.code,
         FromScene: Number(params.fromScene||''),
+        ReferencedId: Number(params.sourceId||''),
+        Uuid: params.uid||"",
         AuthToken: params.authToken || ''
     });
     if(res.Ret !== 200) return
@@ -47,6 +49,24 @@ async function getTableData(){
 }
 
 getTableData()
+
+function getSizeStyle(index,type) {
+    const { HeightList,WidthList } = info.value.ReferencedExcelConfig;
+
+    if(WidthList&&HeightList) {
+        let columnsWArr = WidthList.split(',').map(_ =>Number(_)),
+            rowsHArr = HeightList.split(',').map(_ =>Number(_));
+        
+        if(type === 'height') {
+            return rowsHArr[index]?`height:${rowsHArr[index]}px`:'';
+        }else {
+            return columnsWArr[index]?`width:${columnsWArr[index]}px`:''
+        }
+
+    }else {
+        return ''
+    }
+}
 </script>
 
 <template>
@@ -67,6 +87,7 @@ getTableData()
                 <tr 
                     v-for="(item,index) in info.TableInfo?.TableDataList"
                     :key="index"
+                    :style="`${getSizeStyle(index,'height')}`"
                 >
                     <td 
                     :class="['data-cell',{
@@ -83,6 +104,8 @@ getTableData()
                         font-weight: ${cell.bl ? 'bold' : 'normal'};
                         font-style: ${cell.it ? 'italic' : 'normal'};
                         background: ${cell.bg};
+                        font-size: ${cell.fs||info.Config?.FontSize||12}px;
+                        ${getSizeStyle(cell_index,'width')}
                     `"
                     >
                     <div class="split-word" v-if="cell.ct.s">

+ 8 - 0
src/CustomElement/api/getData.js

@@ -11,6 +11,14 @@ export const chartInfoByCode=params=>{
   return post('/chart/detail',params)
 }
 
+/**
+ * 社区图表通过code获取图表详情
+ * @param UniqueCode 
+*/
+export const forumChartInfoByCode=params=>{
+  return post('/eta_forum/chart/detail',params)
+}
+
 /**
  * 通过code获取表格详情
  * @param UniqueCode 

+ 21 - 0
src/api/etaForum.js

@@ -0,0 +1,21 @@
+// eta社区模块
+import { get,post } from "./index";
+
+export default{
+  //用户收藏列表的分类数据
+  myClassifyList:params=>{
+    return get('/eta_forum/collect/chart_classify',{})
+  },
+  //获取用户收藏的图表
+  getChartListForCollect:params=>{
+    return get('/eta_forum/collect/chart',params)
+  },
+  // 获取社区图表详情
+  getChartDetail:params=>{
+    return get('/eta_forum/chart/from_unique_code',params)
+  },
+  // 获取用户在ETA社区中有权限的图表
+  getChartList:params=>{
+    return get('/eta_forum/chart_list',params)
+  }
+}

+ 9 - 0
src/api/sheet.js

@@ -27,6 +27,15 @@ export default {
     sheetList(params){
         return get('/datamanage/excel_info/list',params)
     },
+    /**
+     * 表格列表es
+     * @param Keyword
+     * @param CurrentIndex
+     * @param PageSize
+     */
+    sheetListEs(params){
+        return get('/datamanage/excel_info/search_by_es',params)
+    },
 
     /**
      * 表格刷新

+ 36 - 14
src/hooks/chart/render.js

@@ -1338,6 +1338,7 @@ function setStackOrCombinChart(e){
     let series=[]
     let yAxis=[]
     let xAxis = {}
+    let ExtraConfig={}
 
     let temYLeftArr=[]
     let temYRightArr=[]
@@ -1348,7 +1349,9 @@ function setStackOrCombinChart(e){
 
     //有右二轴时排个序 按照左 右 右2的顺序
     let newData = data.some(_ =>_.IsAxis===2) ? changeEdbOrder(data) : data;
-
+    if(e.ChartInfo.ChartType===3 && e.ChartInfo.ExtraConfig){
+        ExtraConfig=JSON.parse(e.ChartInfo.ExtraConfig)
+    }
 
     newData.forEach((item,index)=>{
 
@@ -1357,7 +1360,7 @@ function setStackOrCombinChart(e){
 
         //堆叠图的yAxis必须一致 数据列所对应的y轴
         let serie_yIndex = index;
-        if([3,4].includes(e.ChartInfo.ChartType)) {
+        if([4].includes(e.ChartInfo.ChartType) || (e.ChartInfo.ChartType ===3 && ExtraConfig.IsHeap==1)) {
             // 类型为堆叠图时公用第一个指标y轴 
             serie_yIndex =  0;
         } else if(e.ChartInfo.ChartType ===6 && ['areaspline','column'].includes(item.ChartStyle)) {
@@ -1368,13 +1371,13 @@ function setStackOrCombinChart(e){
         item.IsAxis = serie_yIndex === index ? item.IsAxis : newData[serie_yIndex].IsAxis;
         item.IsOrder = serie_yIndex === index ? item.IsOrder : newData[serie_yIndex].IsOrder;
 
-        temYLeftIndex = [3,4].includes(e.ChartInfo.ChartType) 
+        temYLeftIndex = [4].includes(e.ChartInfo.ChartType) 
             ? (newData[serie_yIndex].IsAxis ? serie_yIndex : -1)
             : data.findIndex((item) => item.IsAxis===1);
-        temYRightIndex = [3,4].includes(e.ChartInfo.ChartType) 
+        temYRightIndex = [4].includes(e.ChartInfo.ChartType) 
             ? (newData[serie_yIndex].IsAxis ? -1 : serie_yIndex)
             : data.findIndex((item) => !item.IsAxis);
-        temYRightTwoIndex = [3,4].includes(e.ChartInfo.ChartType) 
+        temYRightTwoIndex = [4].includes(e.ChartInfo.ChartType) 
             ? -1
             : data.findIndex((item) => item.IsAxis===2);
         
@@ -1399,7 +1402,7 @@ function setStackOrCombinChart(e){
             dataGrouping:{
                 enabled:false
             },
-            type: chartStyle || item.ChartStyle,
+            type: e.ChartInfo.ChartType !== 3?(chartStyle || item.ChartStyle):ExtraConfig.IsHeap==1?'area':((chartTheme && chartTheme.lineOptionList[lineIndex].lineType) || item.ChartStyle),
             yAxis:serie_yIndex,
             name:temName,
             nameZh:temName,
@@ -1411,7 +1414,8 @@ function setStackOrCombinChart(e){
             LatestDate:item.LatestDate,
             LatestValue:item.LatestValue,
             ...predict_params,
-            stacking:e.ChartInfo.ChartType==6&&!e.DataResp.IsHeap?undefined:'normal',
+            stacking:e.ChartInfo.ChartType==6?!e.DataResp.IsHeap?undefined:'normal':e.ChartInfo.ChartType==3&&ExtraConfig.IsHeap==1?ExtraConfig.HeapWay==1?'normal':'percent':undefined,
+            threshold: e.ChartInfo.ChartType==3?item.ChartScale?Number(item.ChartScale):0:null
         }
         item.DataList = item.DataList || [];
         for (let i of item.DataList) {
@@ -1450,8 +1454,8 @@ function setStackOrCombinChart(e){
             ...basicYAxis,
             IsAxis:item.IsAxis,
             labels: {
-                formatter: function (ctx) {
-                    return sameSideIndex !== index ? '' : ctx.value;
+                formatter:  (ctx) => {
+                    return sameSideIndex !== index ? '' : e.ChartInfo.ChartType===3&&ExtraConfig.HeapWay==2?ctx.value+'%':ctx.value;
                 },
                 align: 'center',
                 x: [0,2].includes(item.IsAxis) ? 5 : -5,
@@ -1477,8 +1481,8 @@ function setStackOrCombinChart(e){
             },
             opposite: [0,2].includes(item.IsAxis),
             reversed: item.IsOrder,
-            min: Number(minLimit),
-            max: Number(maxLimit),
+            min: e.ChartInfo.ChartType===3&&ExtraConfig.HeapWay==2?null:Number(minLimit),
+            max: e.ChartInfo.ChartType===3&&ExtraConfig.HeapWay==2?null:Number(maxLimit),
             tickWidth: sameSideIndex !== index ? 0 : 1,
             visible: serie_yIndex === index && sameSideIndex ===index,
             plotBands: setAxisPlotAreas(item.IsAxis),
@@ -1551,14 +1555,32 @@ function setStackOrCombinChart(e){
         }
     })
     
-   
-
-    return {
+    let options={
         series,
         xAxis:xAxis,
         yAxis,
         rangeSelector:{ enabled: false}
     }
+
+    //面积图百分比tooltip
+    if(e.ChartInfo.ChartType===3&&ExtraConfig.HeapWay == 2) {
+        options.tooltip = {
+            split: false,
+            shared: true,
+            dateTimeLabelFormats: {
+                // 时间格式化字符
+                day: '%Y/%m/%d',
+                week: "%Y/%m",
+                month: '%Y/%m',
+                year: '%Y/%m',
+            },
+            xDateFormat:'%Y/%m/%d',
+            className:'chart-tooltips-box',
+            pointFormat: '<span style=\"color:{point.color}\">●</span><span>{series.name}</span>' +':<b>{point.percentage:.1f}%</b> ({point.y:,.1f})<br/>'
+        }
+    }
+
+    return options
 }
 
 

+ 8 - 0
src/hooks/common.js

@@ -80,4 +80,12 @@ export async function setClipboardData(text,msg){
        document.body.removeChild(input);
        showToast({message:msg||'复制链接成功',type:'success'})
    }
+}
+
+// 设置高亮
+export function setHightLightText(text, keyword) {
+    let reg = new RegExp(keyword, "g");
+    let replaceString = `<span style='color: #0052D9;'>${keyword.trim()}</span>`;
+    text = text.replace(reg, replaceString);
+    return text;
 }

+ 1 - 1
src/lang/cn.js

@@ -7,7 +7,7 @@ export default {
   // 我的
   my: {
     company_name: '公司名称',
-    department: '所属部门',
+    department: '部门/分组',
     role: '角色',
     log_out: '退出登录',
   },

+ 1 - 1
src/lang/en.js

@@ -6,7 +6,7 @@ export default {
   // 我的
   my: {
     company_name: 'Company Name',
-    department: 'Department',
+    department: 'Department/Group',
     role: 'Role',
     log_out: 'Log out',
   },

+ 12 - 2
src/views/chartETA/ChartDetail.vue

@@ -112,6 +112,16 @@ function setDateAppear() {
     }
 }
 
+// 是否面积图堆叠百分比
+function isChartHeap(info){
+    let tag=true
+    if(info.ChartType==3&&info.ExtraConfig){
+        let ExtraConfig=JSON.parse(info.ExtraConfig)
+        tag=ExtraConfig.HeapWay==2?false:true
+    }
+    return tag
+}
+
 onMounted(() => {
     currentLang.value = localStorage.getItem('chartETALange')==='EN'?'en':'zh'
     initChartState(route.query)
@@ -824,7 +834,7 @@ function openDateSelect(){
         <!-- 指标模块 -->
         <div class="edb-list-box">
             <!-- pad 设置上下限按钮 -->
-            <div class="pad-limit-set-btn" @click="handleShowAxisLimitOpt" v-if="![3,4,6,7,8,9].includes(chartInfo.Source)&&checkAuthBtn(chartLibBtn.chartLib_editLimit)">设置上下限</div>
+            <div class="pad-limit-set-btn" @click="handleShowAxisLimitOpt" v-if="![3,4,6,7,8,9].includes(chartInfo.Source)&&checkAuthBtn(chartLibBtn.chartLib_editLimit)&&isChartHeap(chartInfo)">设置上下限</div>
             <!-- <div class="list-lable">指标信息</div> -->
             <div class="list-box">
                 <div class="list-item" v-for="item in edbList" :key="item.EdbInfoId" @click="handleShowEDBInfo(item)">
@@ -844,7 +854,7 @@ function openDateSelect(){
                 <img class="icon" style="transform: rotate(180deg);" src="@/assets/imgs/icon_arrow.png" alt="">
                 <div>下一张</div>
             </div>
-            <div class="item" @click="handleShowAxisLimitOpt" v-if="![3,4,6,7,8,9].includes(chartInfo.Source)&&checkAuthBtn(chartLibBtn.chartLib_editLimit)">
+            <div class="item" @click="handleShowAxisLimitOpt" v-if="![3,4,6,7,8,9].includes(chartInfo.Source)&&checkAuthBtn(chartLibBtn.chartLib_editLimit)&&isChartHeap(chartInfo)">
                 <img class="icon" src="@/assets/imgs/myETA/icon_limit.png" alt="">
                 <div>上下限</div>
             </div>

+ 1 - 1
src/views/chartETA/Search.vue

@@ -96,7 +96,7 @@ function goDetail(item){
         >
             <ul class="list-wrap">
                 <li class="item" v-for="item in listState.list" :key="item.ChartInfoId" @click="goDetail(item)">
-                    <div class="van-ellipsis name">{{currentLang==='EN'?(item.ChartNameEn||item.ChartName):item.ChartName}}</div>
+                    <div class="van-ellipsis name" v-html="item.SearchText"></div>
                     <img class="img" :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" alt="">
                     <div class="time">
                         <span>{{moment(item.CreateTime).format('YYYY-MM-DD')}}</span>

+ 1 - 1
src/views/dataEDB/SearchList.vue

@@ -84,7 +84,7 @@ function goDetail(item){
         >
             <ul class="list-wrap">
                 <li class="item" v-for="item in listState.list" :key="item.EdbInfoId" @click="goDetail(item)">
-                    <div class="van-multi-ellipsis--l2 name">{{item.EdbName}}</div>
+                    <div class="van-multi-ellipsis--l2 name" v-html="item.SearchText"></div>
                     <van-image
                         class="img"
                         :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage"

+ 1 - 1
src/views/dataEDB/calculate/components/SelectEDB.vue

@@ -147,7 +147,7 @@ function handleShowEDBInfo(item){
                                 <van-radio :name="item.EdbInfoId" :disabled="!item.HaveOperaAuth">
                                     <div class="con">
                                         <img :src="useNoAuth().noAuthIco" width="18" height="18" v-if="!item.HaveOperaAuth">
-                                        <div class="name">{{item.EdbName}}</div>
+                                        <div class="name" v-html="item.SearchText"></div>
                                         <svg-icon @click.stop="handleShowEDBInfo(item)" class="icon" name="error-circle-filled" size="16px" color="#999"/>
                                     </div>
                                 </van-radio>

+ 17 - 3
src/views/externalReport/List.vue

@@ -1,5 +1,6 @@
 <script setup name="WarResearchReport">
-import {computed, nextTick, onMounted, reactive,ref} from 'vue'
+import {setHightLightText} from '@/hooks/common'
+import {computed, nextTick, onMounted, reactive,ref,watch} from 'vue'
 import apiReport from '@/api/report'
 import {reportExternalInterface} from '@/api/reportExternal'
 import moment from 'moment'
@@ -27,6 +28,8 @@ const showCleanFilterBox=computed(()=>{
     ) return true
 })
 
+const isSearch=ref(false) // 是否为搜索状态
+
 
 const isClickClose=ref(false)//是否点击过关闭一键清空模块
 function handleCleanFilter(){
@@ -51,7 +54,7 @@ async function getClassify(type='') {
     filterEmpty(classifyOptions.value)
 
     if(type === 'init') {
-        listState.classifys = collectClassifyOpts.value.length ? collectClassifyOpts.value.map(_ => _.Id) : [];
+        // listState.classifys = collectClassifyOpts.value.length ? collectClassifyOpts.value.map(_ => _.Id) : [];
         getList()
         handleShowFilter()
     }
@@ -108,6 +111,16 @@ const listState = reactive({
     finished:false,
     loading:false
 })
+
+watch(
+  ()=>listState.keyWord,
+  (val)=>{
+    console.log(val)
+    if(!val){
+        isSearch.value=false
+    }
+  }
+)
 async function getList(){
     const res = isRise.value
         ? await reportExternalInterface.getRiseReportList({
@@ -147,6 +160,7 @@ function onLoad(){
 }
 function refreshList(){
     document.documentElement.scrollTop=0
+    isSearch.value=true
     listState.page=1
     listState.list=[]
     listState.finished=false
@@ -490,7 +504,7 @@ function goDetail(item){
                 >   
                     <div class="list-top">
                         <h2 class="van-ellipsis title">
-                            {{item.Title}}
+                            <span v-html="isSearch?setHightLightText(item.Title,listState.keyWord):item.Title"></span>
                             <template v-if="isRise">
                                 ({{ moment(item.MsgSendTime||item.PublishTime||item.CreateTime).format('MMDD')}})
                             </template>

+ 20 - 8
src/views/myETA/ChartDetail.vue

@@ -11,6 +11,7 @@ import apiMyETAChart from '@/api/myETA'
 import apiDataEDB from '@/api/dataEDB'
 import apiSheet from '@/api/sheet'
 import apiIntervalAnalysis from '@/api/intervalAnalysis'
+import apiETAForum from '@/api/etaForum'
 import { useRoute, useRouter } from 'vue-router'
 import {useChartRender} from '@/hooks/chart/render'
 import {yearSelectOpt,sameOptionType} from '@/hooks/chart/config'
@@ -53,6 +54,7 @@ const {options,axisLimitState,chartRender,setLimitData,isUseSelfLimit}=useChartR
 const route=useRoute()
 const router=useRouter()
 let chartCode=route.query.code
+const isETAForumChart=ref(route.query.isETAForumChart=='true'?true:false)
 let CHARTINS=null//图表实例
 
 // 获取当前图表所在分类下的所有图表数据 用于上一张下一张切换
@@ -95,12 +97,22 @@ function handleSwitchChart(type){
     getChartInfo()
 }
 
+// 是否面积图堆叠百分比
+function isChartHeap(info){
+    let tag=true
+    if(info.ChartType==3&&info.ExtraConfig){
+        let ExtraConfig=JSON.parse(info.ExtraConfig)
+        tag=ExtraConfig.HeapWay==2?false:true
+    }
+    return tag
+}
+
 // 获取图详情
 let chartInfoData=null
 let chartInfo=ref(null)
 let edbList=ref([])//指标数据
 async function getChartInfo(){
-    const res=await apiETAChart.chartInfoByCode({UniqueCode:chartCode})
+    const res=isETAForumChart.value?await apiETAForum.getChartDetail({UniqueCode:chartCode}) : await apiETAChart.chartInfoByCode({UniqueCode:chartCode})
     if(res.Ret!==200) return
     chartInfoData=res.Data
 
@@ -787,7 +799,7 @@ const showSaveToMaterial=ref(false)
             <!-- 一般曲线图选择时间区间或者季节图选择日期 -->
             <div 
                 class="select-time-box" 
-                v-if="(sameOptionType.includes(chartInfo.ChartType)&& chartInfo.Source===1)||chartInfo.ChartType===2" 
+                v-if="!isETAForumChart&&((sameOptionType.includes(chartInfo.ChartType)&& chartInfo.Source===1)||chartInfo.ChartType===2)" 
                 @click="openDateSelect"
             >
                 <img class="left-icon" src="@/assets/imgs/icon_calendar.png" alt="">
@@ -796,7 +808,7 @@ const showSaveToMaterial=ref(false)
             </div>
 
             <!-- pad端时间和操作按钮模块 -->
-            <div class="pad-time-action-wrap">
+            <div class="pad-time-action-wrap" v-if="!isETAForumChart">
                 <div class="left-time-box" >
                     <template v-if="sameOptionType.includes(chartInfo.ChartType)&& chartInfo.Source===1">
                         <span :class="['item',chartState.yearVal==''?'active':'']" @click="handleYearChange({value:''})">全部</span>
@@ -814,7 +826,7 @@ const showSaveToMaterial=ref(false)
                     >{{chartState.startTime?chartState.startTime+'~'+(chartState.endTime?chartState.endTime:'至今'):'请选择时间段'}}</span>
                 </div>
                 <div class="right-action-box">
-                    <div class="item" @click="handleShowAxisLimitOpt" v-if="[1,2,5,10,12].includes(chartInfo.Source)&&checkAuthBtn(myETABtn.myChart_editLimit)">
+                    <div class="item" @click="handleShowAxisLimitOpt" v-if="[1,2,5,10,12].includes(chartInfo.Source)&&checkAuthBtn(myETABtn.myChart_editLimit)&&isChartHeap(chartInfo)">
                         <img src="@/assets/imgs/myETA/icon_limit2.png" alt="">
                         <span>上下限设置</span>
                     </div>
@@ -860,7 +872,7 @@ const showSaveToMaterial=ref(false)
             </div>
 
             <!-- 一般曲线图选择时间区间 -->
-            <div class="select-year-box" v-if="sameOptionType.includes(chartInfo.ChartType)&& chartInfo.Source===1">
+            <div class="select-year-box" v-if="!isETAForumChart&&sameOptionType.includes(chartInfo.ChartType)&& chartInfo.Source===1">
                 <span :class="['item',chartState.yearVal==''?'active':'']" @click="handleYearChange({value:''})">全部</span>
                 <span 
                     :class="['item',chartState.yearVal==item.value?'active':'']"
@@ -870,7 +882,7 @@ const showSaveToMaterial=ref(false)
                 >{{item.name}}</span>
             </div>
             <!-- 季节图切换公/农历 -->
-            <div class="calendar-type-box" v-if="chartInfo.ChartType === 2">
+            <div class="calendar-type-box" v-if="!isETAForumChart&&chartInfo.ChartType === 2">
                 <span 
                     :class="chartState.calendarType=='公历'?'active':''"
                     @click="handleSeasonTypeChange('公历')"
@@ -902,11 +914,11 @@ const showSaveToMaterial=ref(false)
                     <img class="icon" style="transform: rotate(180deg);" src="@/assets/imgs/icon_arrow.png" alt="">
                     <div>下一张</div>
                 </div>
-                <div class="item" @click="handleShowAxisLimitOpt" v-if="[1,2,5,10].includes(chartInfo.Source)&&checkAuthBtn(myETABtn.myChart_editLimit)">
+                <div class="item" @click="handleShowAxisLimitOpt" v-if="[1,2,5,10].includes(chartInfo.Source)&&checkAuthBtn(myETABtn.myChart_editLimit)&&isChartHeap(chartInfo)">
                     <img class="icon" src="@/assets/imgs/myETA/icon_limit.png" alt="">
                     <div>上下限</div>
                 </div>
-                <div class="item" @click="showMoreAction=true" v-if="isMoreActionShow||$route.query.from==='edbRelationChart'">
+                <div class="item" @click="showMoreAction=true" v-if="!isETAForumChart&&(isMoreActionShow||$route.query.from==='edbRelationChart')">
                     <img class="icon" src="@/assets/imgs/myETA/icon_menu.png" alt="">
                     <div>更多</div>
                 </div>

+ 13 - 5
src/views/myETA/ChooseChart.vue

@@ -5,6 +5,7 @@ import apiFuture from '@/api/futureChart'
 import apiCorrelation from '@/api/correlationChart'
 import apiLineEquationChart from '@/api/lineEquationChart'
 import apiStatisticFeatureChart from '@/api/statisticFeatureChart'
+import apiCrossVarietyChart from '@/api/crossVarietyChart'
 import {apiMyClassifyList,apiMyChartAdd,apiAddClassify} from '@/api/myETA'
 import { showToast } from 'vant';
 import { useWindowSize } from '@vueuse/core'
@@ -33,6 +34,10 @@ const typeOpt=[
     {
         name:"统计特征",
         type:5,
+    },
+    {
+        name:"跨品种分析",
+        type:6,
     }
 
 ]
@@ -53,19 +58,22 @@ async function getChartList(){
         CurrentIndex: listState.page,
         ChartClassifyId: 0,
         KeyWord: keyword.value,
+        Keyword:keyword.value,
         IsShowMe:isShowMe.value
     }
     let res
     if(type.value==1){
-        res=await apiChart.pubChartList(params)
+        res=await apiChart.ETAChartListByES(params)
     }else if(type.value==2){
         res=await apiFuture.searchChartList(params)
     }else if(type.value==3){
-        res=await apiCorrelation.chartList(params)
+        res=await apiCorrelation.searchChartList(params)
     }else if(type.value==4){
-        res=await apiLineEquationChart.chartList(params)
+        res=await apiLineEquationChart.searchChartList(params)
     }else if(type.value==5){
-        res=await apiStatisticFeatureChart.chartList(params)
+        res=await apiStatisticFeatureChart.searchChart(params)
+    }else if(type.value==6){
+        res=await apiCrossVarietyChart.searchChart(params)
     }
     
     if(res.Ret==200){
@@ -222,7 +230,7 @@ async function handleConfirmEditClassify(){
         <img v-if="listState.list.length==0&&listState.finished" class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
         <ul class="list-wrap">
             <li class="item" v-for="item in listState.list" :key="item.ChartInfoId">
-                <div class="van-multi-ellipsis--l2 name">{{item.ChartName}}</div>
+                <div class="van-multi-ellipsis--l2 name" v-html="item.SearchText"></div>
                 <van-image
                     fit="contain"
                     lazy-load

+ 61 - 10
src/views/myETA/Index.vue

@@ -3,6 +3,7 @@ import {ref,reactive,computed} from 'vue'
 import { useRouter } from 'vue-router'
 import {useClassify} from './hooks/useClassify'
 import apiMyETA  from '@/api/myETA'
+import apiETAForum from '@/api/etaForum'
 import { showConfirmDialog, showToast } from 'vant';
 import { useWindowSize } from '@vueuse/core'
 import draggable from 'vuedraggable'
@@ -21,6 +22,7 @@ const {
     classifyState,
     getMyClassify,
     getPubClassify,
+    getETAForumClassify,
     classifyTypeChange,
     handleAddClassify,
     handleConfirmEditClassify,
@@ -33,7 +35,9 @@ const {
 }=useClassify()
 
 const curChartClassifyList=computed(()=>{
-    return classifyState.classifyTypeAct==1?classifyState.myClassifyList:classifyState.pubClassifyList
+    if(classifyState.classifyTypeAct==1) return classifyState.myClassifyList
+    if(classifyState.classifyTypeAct==2) return classifyState.pubClassifyList
+    if(classifyState.classifyTypeAct==3) return classifyState.forumClassifyList
 })
 //我的图库/公共图库opt:重命名、可见、复制、删除
 const isOptShow = computed(()=>{
@@ -66,6 +70,7 @@ function onClassifySortEnd(e){
 const showClassifyPop=ref(false)
 
 async function initPage(){
+    getETAForumClassify()
     getPubClassify()
     await getMyClassify()
     listState.cid=classifyState.myClassifyList[0]?.MyChartClassifyId
@@ -92,7 +97,11 @@ const listState = reactive({
     total:0,
 })
 async function getChartList(){
-    const res=await apiMyETA.myChartList({
+    const res=classifyState.classifyTypeAct==3?await apiETAForum.getChartListForCollect({
+        CollectClassifyIds:`${listState.cid}`,
+        PageSize:listState.pageSize,
+        CurrentIndex:listState.page,
+    }) : await apiMyETA.myChartList({
         CurrentIndex:listState.page,
         PageSize:listState.pageSize,
         MyChartClassifyId:listState.cid
@@ -107,7 +116,17 @@ async function getChartList(){
         
         listState.finished=res.Data.Paging.IsEnd
         const arr=res.Data.List||[]
-        listState.list=[...listState.list,...arr]
+        if(classifyState.classifyTypeAct==3){
+            arr.forEach(_item=>{
+                listState.list.push({
+                    ..._item,
+                    HaveOperaAuth:true
+                })
+            })
+        }else{
+            listState.list=[...listState.list,...arr]
+        }
+        
         listState.total=res.Data.Paging.Totals
     }
 }
@@ -118,7 +137,14 @@ function onLoad(){
 
 // 切换分类
 function handleSwitchClassify(item){
-    listState.ctype=classifyState.classifyTypeAct==1?'我的图库':'公共图库'
+    if(classifyState.classifyTypeAct==1){
+        listState.ctype='我的图库'
+    }else if(classifyState.classifyTypeAct==2){
+        listState.ctype='公共图库'
+    }else if(classifyState.classifyTypeAct==3){
+        listState.ctype='ETA社区图库'
+    }
+    
     listState.cid=item.MyChartClassifyId
     listState.cname=item.MyChartClassifyName
     listState.list=[]
@@ -180,7 +206,8 @@ function goDetail(item){
         query:{
             code:item.UniqueCode,
             cid:listState.cid,
-            iscommon:listState.ctype=='我的图库'?false:true
+            iscommon:listState.ctype=='我的图库'?false:true,
+            isETAForumChart:classifyState.classifyTypeAct==3?true:false
         }
     })
 }
@@ -226,7 +253,7 @@ async function goSearch(){
                     @click="goDetail(item)"
                 >
                     <div class="van-multi-ellipsis--l2 name">{{item.ChartName}}</div>
-                    <img class="img" :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" alt="">   
+                    <img class="img" :src="([1,11].includes(item.Source)&&!item.HaveOperaAuth)?useNoAuth().noAuthImg:item.ChartImage" alt="">   
                     <div class="time">
                         <span>{{item.CreateTime.slice(0,10)}}</span>
                         <img v-if="listState.ctype==='我的图库'&&checkAuthBtn(myETABtn.myChart_move)" class="remove-box" @click.stop="handleRemoveChart(item,index)" src="@/assets/imgs/myETA/icon_remove2.png" alt="">
@@ -244,12 +271,20 @@ async function goSearch(){
     >
         <div class="classify-wrap">
             <div class="type-box">
-                <span
+                <van-tabs class="type-tab" :active="classifyState.classifyTypeAct" @click-tab="classifyTypeChange">
+                    <van-tab 
+                        :title="item.name" 
+                        :name="item.type" 
+                        v-for="item in classifyTypeOpt"
+                        :key="item.type"
+                    ></van-tab>
+                </van-tabs>
+                <!-- <span
                     :class="['item',item.type===classifyState.classifyTypeAct?'active':'']"
                     v-for="item in classifyTypeOpt" 
                     :key="item.id"
                     @click="classifyTypeChange(item)"
-                >{{item.name}}</span>
+                >{{item.name}}</span> -->
             </div>
             <draggable 
                 v-if="classifyState.classifyTypeAct==1"
@@ -281,6 +316,21 @@ async function goSearch(){
                     </li>
                 </template>
             </draggable>
+            <ul 
+                class="list-box"
+                v-else-if="classifyState.classifyTypeAct==3"
+            >
+                <li 
+                    class="select-text-disabled item" 
+                    v-for="element in curChartClassifyList" 
+                    :key="element.CollectClassifyId"
+                    @click="handleSwitchClassify(element)"
+                >
+                    <div :class="['van-ellipsis name',element.CollectClassifyId===listState.cid&&'active-name']">{{element.ClassifyName}}</div>
+                </li>
+            </ul>
+            
+
             <div class="public-classify-wrap" v-else>
                 <van-collapse class="classify-level" v-model="activeMenu" :border="false">
                     <van-collapse-item
@@ -480,8 +530,9 @@ async function goSearch(){
     display: flex;
     flex-direction: column;
     .type-box{
-        padding: $page-padding;
-        box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.08);
+        // padding: $page-padding;
+        border-bottom: 1px solid #C6C6C6;
+        // box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.08);
         .item{
             margin-right: 40px;
             font-size: 32px;

+ 1 - 1
src/views/myETA/SearchList.vue

@@ -83,7 +83,7 @@ function goDetail(item){
         >
             <ul class="list-wrap">
                 <li class="item" v-for="item in listState.list" :key="item.ChartInfoId" @click="goDetail(item)">
-                    <div class="van-multi-ellipsis--l2 name">{{item.ChartName}}</div>
+                    <div class="van-multi-ellipsis--l2 name" v-html="item.SearchText"></div>
                     <img class="img" :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" alt="">   
                     <div class="time">
                         <span>{{item.CreateTime.slice(0,10)}}</span>

+ 15 - 2
src/views/myETA/components/EDBInfo.vue

@@ -1,5 +1,6 @@
 <script setup>
 import {ref,onMounted,watch,computed} from 'vue'
+import { useRouter } from 'vue-router'
 import { V3ColorPicker } from "v3-color-picker-teleport"
 import {sameOptionType} from '@/hooks/chart/config'
 import _ from 'lodash'
@@ -11,6 +12,7 @@ const {checkAuthBtn} = useAuthBtn()
 const {copyData} =useCopyEdbData()
 
 const leadUnitOpt=[{text:'年'}, {text:'季'}, {text:'月'}, {text:'周'}, {text:'天'}]//领先指标频度配置
+const router = useRouter()
 
 const props=defineProps({
     show:{
@@ -133,7 +135,18 @@ function handleLeadUnitChange(e){
 const showSourceDetail=ref(false)
 
 function handleCopyEDBData(){
-    copyData(props.data)
+    copyData(props.data.DataList||[])
+}
+
+// 查看数据
+function goDetail(){
+    router.push({
+        path:'/dataEDB/detail',
+        query:{
+            edbInfoId:props.data.EdbInfoId,
+            showType:'data'
+        }
+    })
 }
 
 </script>
@@ -241,7 +254,7 @@ function handleCopyEDBData(){
                     </template>-->
                     <div class="item-box" v-if="checkAuthBtn(myETABtn.myChart_checkData)||checkAuthBtn(myETABtn.myChart_copyData)">
                         <van-button color="#F2F3FF" size="small" style="color:#0052D9;margin-right:10px" @click="handleCopyEDBData" v-permission="myETABtn.myChart_copyData">复制数据</van-button>
-                        <van-button color="#0052D9" size="small" v-permission="myETABtn.myChart_checkData">查看数据</van-button>
+                        <van-button color="#0052D9" size="small" v-permission="myETABtn.myChart_checkData" @click="goDetail">查看数据</van-button>
                     </div>
                 </div>
             </div>

+ 27 - 1
src/views/myETA/hooks/useClassify.js

@@ -7,6 +7,7 @@ import {
     apiShareClassify,
     apiClassifyCopy
 } from '@/api/myETA.js'
+import apiETAForum from '@/api/etaForum'
 import {reactive} from 'vue'
 import { showToast,showDialog,Dialog } from 'vant';
 
@@ -17,6 +18,10 @@ export function useClassify(){
             name:"我的图库",
             type:1
         },
+        {
+            name:"ETA社区图库",
+            type:3
+        },
         {
             name:"公共图库",
             type:2
@@ -27,6 +32,7 @@ export function useClassify(){
         classifyTypeAct:1,//当前选中的类型
         myClassifyList:[],//我的图库分类
         pubClassifyList:[],//公共图库分类
+        forumClassifyList:[],//ETA社区图库分类
         classifyAct:0,//当前选中的分类id
 
         showEidtClassifyName:false,//显示新增/编辑分类弹窗
@@ -52,9 +58,28 @@ export function useClassify(){
         }
     }
 
+    // 获取ETA社区图库分类
+    const getETAForumClassify=async()=>{
+        const res=await apiETAForum.myClassifyList()
+        if(res.Ret===200){
+            const arr=res.Data.List||[]
+            classifyState.forumClassifyList=arr.map(item=>{
+                return {
+                    ...item,
+                    MyChartClassifyId:item.CollectClassifyId,
+                    MyChartClassifyName:item.ClassifyName
+                }
+            })
+            // 没有分类则去除eta社区分类模块
+            if(classifyState.forumClassifyList.length===0){
+                classifyTypeOpt.splice(1,1)
+            }
+        }
+    }
+
     // 切换图库分类类型
     const classifyTypeChange=(e)=>{
-        classifyState.classifyTypeAct=e.type
+        classifyState.classifyTypeAct=e.name
     }
 
     // 显示新增分类弹窗
@@ -153,6 +178,7 @@ export function useClassify(){
         classifyState,
         getMyClassify,
         getPubClassify,
+        getETAForumClassify,
         classifyTypeChange,
         handleAddClassify,
         handleConfirmEditClassify,

+ 6 - 1
src/views/ppt/components/ChartWrap.vue

@@ -1,6 +1,7 @@
 <script setup>
 import {computed,onMounted,ref} from 'vue'
 import apiChart from '@/api/chart.js'
+import apiETAForum from '@/api/etaForum'
 import {chartRender,useChartRender} from '@/hooks/chart/render'
 
 
@@ -24,7 +25,11 @@ const sourceFrom=ref(null)
 // 获取图表详情
 let chartIsDelete=ref(false)//图表是否被删除
 async function getChartInfo(){
-    const res=await apiChart.chartInfoByCode({UniqueCode:props.itemData.chartId,IsCache: true})
+    const isETAForumChart=props.itemData.chartId.startsWith('isETAForumChart_')
+
+    const res=isETAForumChart?await apiETAForum.getChartDetail({
+        UniqueCode:props.itemData.chartId.replace(/^isETAForumChart_/, '')
+    }) :await apiChart.chartInfoByCode({UniqueCode:props.itemData.chartId,IsCache: true})
     if(res.Ret===200){
         if(!res.Data.ChartInfo){
             chartIsDelete.value=true

+ 3 - 2
src/views/report/AddReport.vue

@@ -82,7 +82,7 @@ async function handleReportBaseInfoChange(e){
 // 报告插入数据弹窗
 const showReportInsertPop=ref(false)
 /**
- * list:[UniqueCode] 图表code
+ * list:[UniqueCode] 图表code 如果是以 isETAForumChart_ 开头则说明是社区图表
  * type:iframe/img 插入的为iframe或者图片
  * chartType: chart-图表,sheet-表格
  */
@@ -98,8 +98,9 @@ function handleInsert({list,type,chartType}){
             // link=import.meta.env.MODE==='production'?'https://chartlib.hzinsights.com/chartshow':'https://charttest.hzinsights.com/chartshow'
             link=link+'/chartshow'
             list.forEach(item => {
+                const isETAForumChart=item.startsWith('isETAForumChart_')?true:false
                 reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
-						<iframe src='${link}?code=${item}&fromPage=' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+						<iframe src='${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&fromPage=&isETAForumChart=${isETAForumChart}' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
 					</p>`)
             });
         }else if(chartType==='sheet'){

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

@@ -192,8 +192,9 @@ async function handleInsert({list,type,chartType}){
             list.forEach(async(item) => {
                 //临时权限token
                 let authToken = await getLinkAuthToken(item);
+                const isETAForumChart=item.startsWith('isETAForumChart_')?true:false
                 reportContentEditorIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
-						<iframe src='${link}?code=${item}&fromPage=&authToken=${authToken}' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+						<iframe src='${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&fromPage=&isETAForumChart=${isETAForumChart}&authToken=${authToken}' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
 					</p>`,false)
             });
         }else if(chartType==='sheet'){

+ 2 - 2
src/views/report/PreviewDetail.vue

@@ -100,7 +100,7 @@ function handleCopyLink() {
         <div class="html-head-img-box" v-if="reportInfo && reportInfo.HeadImg">
             <img :src="reportInfo.HeadImg" alt="" style="display:block;width:100%">
             <div class="head-layout-item" v-for="item in headImgStyle" :key="item.value"
-            :style="{fontFamily:item.family,fontSize:(item.size*2)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
+            :style="{fontFamily:item.family,fontSize:(item.sizeMobile || item.size)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
                 width:item.width,height:item.height,left:item.left,top:item.top
             }">
                 {{ layoutBaseInfo[item.value] }}
@@ -142,7 +142,7 @@ function handleCopyLink() {
         <div class="html-end-img-box" v-if="reportInfo && reportInfo.EndImg">
             <img :src="reportInfo.EndImg" alt="" style="display:block;width:100%">
             <div class="head-layout-item" v-for="item in endImgStyle" :key="item.value"
-            :style="{fontFamily:item.family,fontSize:(item.size*2)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
+            :style="{fontFamily:item.family,fontSize:(item.sizeMobile || item.size)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
                 width:item.width,height:item.height,left:item.left,top:item.top
             }">
                 {{ layoutBaseInfo[item.value] }}

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

@@ -1,4 +1,5 @@
 <script setup name="ReportSearch">
+import {setHightLightText} from '@/hooks/common'
 import {ref,reactive} from 'vue'
 import apiReport from '@/api/report'
 import { showToast } from 'vant'
@@ -114,7 +115,7 @@ function goDetail(item){
                     class="item" 
                     @click="goDetail(item)"
                 >
-                    <h2 :class="['van-ellipsis title',item.Title.startsWith('【')?'inline-title':'']">{{item.Title}}</h2>
+                    <h2 v-html="setHightLightText(item.Title,keyword)" :class="['van-ellipsis title',item.Title.startsWith('【')?'inline-title':'']"></h2>
                     <p class="van-multi-ellipsis--l2 des">{{item.Abstract}}</p>
                     <div class="bot-info">
                         <div>

+ 1 - 1
src/views/report/components/reportInsert/ETAChart.vue

@@ -102,7 +102,7 @@ watch(
                     :key="item.ChartInfoId"
                     @click="handleSelect(item)"
                 >
-                    <div class="van-multi-ellipsis--l2 title">{{item.ChartName}}</div>
+                    <div class="van-multi-ellipsis--l2 title" v-html="item.SearchText"></div>
                     <img :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" alt="">
 
                     <svg v-if="selectChartList.includes(item.UniqueCode)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">

+ 204 - 0
src/views/report/components/reportInsert/ETAForumChart.vue

@@ -0,0 +1,204 @@
+<script setup>
+import {reactive, ref, watch} from 'vue'
+import apiETAForum from '@/api/etaForum'
+import { showToast } from 'vant'
+import { vInfiniteScroll } from '@vueuse/components'
+import { useNoAuth } from '@/hooks/useNoAuth'
+
+const emits=defineEmits(['update'])
+
+const searchVal=ref('')
+
+const listState=reactive({
+    page:1,
+    pageSize:20,
+    list:[],
+    finished:false,
+    loading:false
+})
+
+async function getChartList(){
+    const params={
+        Keyword:searchVal.value,
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize,
+    }
+    listState.loading=true
+    
+    let res = await apiETAForum.getChartList(params)
+    
+    listState.loading=false
+    if(res.Ret===200){
+        const arr=res.Data.ChartInfoList||[]
+        arr.forEach(item => {
+          listState.list.push({
+            ...item,
+            UniqueCode:`isETAForumChart_${item.UniqueCode}`,//在获取图表详情时通过前缀区分
+            HaveOperaAuth:true
+          })
+        });
+        listState.finished=res.Data?.Paging.IsEnd
+    }
+}
+getChartList()
+// 触底加载更多
+function onLoadMore(){
+    if(listState.finished||listState.loading||!listState.Keyword) return
+    listState.page++
+    getChartList()
+}
+
+function handleRefreshList(){
+    listState.list=[]
+    listState.page=1
+    listState.finished=false
+    selectChartList.value=[]
+    getChartList()
+}
+
+const selectChartList=ref([])
+function handleSelect(item){
+    const index=selectChartList.value.indexOf(item.UniqueCode)
+    if(index!==-1){
+        selectChartList.value.splice(index,1)
+    }else{
+        selectChartList.value.push(item.UniqueCode)
+    }
+    
+}
+
+watch(
+    ()=>selectChartList.value,
+    ()=>{
+        emits('update',selectChartList.value)
+    },
+    {
+        immediate:true
+    }
+)
+
+</script>
+
+<template>
+    <div class="ETA-chart-wrap">
+        <div class="sticky-box">
+            <van-search v-model="searchVal" shape="round" placeholder="请输入图表名称" @search="handleRefreshList" @clear="handleRefreshList" />
+        </div>
+        <div class="content-box">
+            <div v-if="listState.list.length==0&&listState.finished">
+                <img class="list-empty-img" src="https://hzstatic.hzinsights.com/static/ETA_mobile/empty_img.png" alt="">
+                <p style="text-align:center;color:#999999;font-size:12px">暂无图表</p>
+            </div>
+            
+            <ul class="chart-list" v-infinite-scroll="[onLoadMore, { 'distance' : 10 }]">
+                <li 
+                    :class="['chart-item',selectChartList.includes(item.UniqueCode)&&'active']" 
+                    v-for="item in listState.list" 
+                    :key="item.ChartInfoId"
+                    @click="handleSelect(item)"
+                >
+                    <div class="van-multi-ellipsis--l2 title">{{item.ChartName}}</div>
+                    <img :src="item.ChartImage" alt="">
+
+                    <svg v-if="selectChartList.includes(item.UniqueCode)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+                        <path d="M14.5 28C22.232 28 28.5 21.732 28.5 14C28.5 6.26801 22.232 0 14.5 0C6.76801 0 0.5 6.26801 0.5 14C0.5 21.732 6.76801 28 14.5 28ZM7.5 14.413L8.913 13L12.5 16.586L20.085 9L21.5 10.415L12.5 19.414L7.5 14.413Z" fill="#0052D9"/>
+                    </svg>
+
+                </li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+                <li class="chart-item" style="height:0;border:none;margin-bottom:0;padding:0"></li>
+            </ul>
+        </div>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+.ETA-chart-wrap{
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    .content-box{
+        overflow: hidden;
+        flex: 1;
+        padding: var(--van-padding-sm);
+        min-height: 300PX;
+    }
+    .chart-list{
+        height: 100%;
+        overflow-y: auto;
+        display: flex;
+        flex-wrap: wrap;
+        justify-content: space-between;
+        .chart-item{
+            width: 48%;
+            background: #FFFFFF;
+            border: 3px solid $border-color;
+            box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.03);
+            border-radius: 4px;
+            margin-bottom: 30px;
+            overflow: hidden;
+            padding: 14px;
+            box-sizing: border-box;
+            position: relative;
+            max-height: 336px;
+            .title{
+                font-size: 28px;
+                min-height: 60px;
+            }
+            img{
+                width: 100%;
+            }
+        }
+        .active{
+            border-color: $theme-color;
+            svg{
+                width: 28px;
+                height: 28px;
+                position: absolute;
+                right: 22px;
+                bottom: 22px;
+            }
+        }
+    }
+}
+.sticky-box{
+    flex-shrink: 0;
+    // position: sticky;
+    // top: 0;
+    // z-index: 10;
+    background-color: #fff;
+    .van-tabs{
+        border-bottom: 1px solid $border-color;
+    }
+    .van-checkbox{
+        margin: var(--van-padding-sm);
+    }
+}
+@media screen and (min-width:$media-width){
+    .ETA-chart-wrap{
+        .chart-list{
+            
+            .chart-item{
+                width: 260px;
+                border-width: 1px;
+                border-radius: 2px;
+                margin-bottom: 15px;
+                padding: 7px;
+                max-height: 220px;
+                .title{
+                    font-size: 14px;
+                    min-height: 30px;
+                }
+            }
+            .active{
+                svg{
+                    width: 14px;
+                    height: 14px;
+                    right: 11px;
+                    bottom: 11px;
+                }
+            }
+        }
+    }
+}
+</style>

+ 8 - 2
src/views/report/components/reportInsert/Index.vue

@@ -10,6 +10,7 @@ import MaterialImg from './reportMaterial/Index.vue'
 import StatisticAnalysis from './StatisticAnalysis.vue'
 import PriceChart from './PriceChart.vue'
 import BalanceSheet from './BalanceSheet/Index.vue'
+import ETAForumChart from './ETAForumChart.vue'
 
 const emits=defineEmits(['insert'])
 
@@ -30,6 +31,11 @@ const typeOpt=ref([
         label:'我的图库',
         path:'mychart'
     },
+    {
+        value:'etaForum',
+        label:'ETA社区图库',
+        path:'mychart'
+    },
     {
         value:'表格插入',
         label:'表格',
@@ -92,7 +98,7 @@ function handleSelectChart(data){
 }
 
 function handleConfirmInsert(){
-    if(['图表插入','批量插入','统计分析','商品价格曲线'].includes(activeType.value)){
+    if(['图表插入','批量插入','统计分析','商品价格曲线','etaForum'].includes(activeType.value)){
         console.log(list.value)
         let filterList = list.value;
 
@@ -127,7 +133,7 @@ function handleConfirmInsert(){
             <StatisticAnalysis @update="handleSelectChart" v-if="activeType==='统计分析'"/>
             <PriceChart @update="handleSelectChart" v-if="activeType==='商品价格曲线'"/>
             <BalanceSheet @update="handleSelectChart" @insert="handleConfirmInsert" v-if="activeType==='平衡表'"/>
-            
+            <ETAForumChart @update="handleSelectChart" v-if="activeType==='etaForum'"/>
         </div>
         <div class="bot-btn" v-if="activeType!=='平衡表'">
             <van-button type="primary" block @click="handleConfirmInsert" :disabled="list.length===0">插入</van-button>

+ 60 - 11
src/views/report/components/reportInsert/MyETAChart.vue

@@ -1,11 +1,13 @@
 <script setup>
 import {reactive,ref,watch} from 'vue'
 import apiMyETAChart from '@/api/myETA'
+import apiETAForum from '@/api/etaForum'
 import { vInfiniteScroll } from '@vueuse/components'
 import { useNoAuth } from '@/hooks/useNoAuth'
 
 const emits=defineEmits(['update'])
 
+const classifyType=ref('myETA')
 // 获取我的图库中分类
 let showClassify=ref(false)
 let classifyList=[]
@@ -13,9 +15,20 @@ let activeClassify=ref('')
 let activeClassifyName=ref('')
 let curClassifyList=ref([])
 async function getClassifyList(){
-    const res=await apiMyETAChart.myClassifyList()
+    const res=classifyType.value==='myETA'? await apiMyETAChart.myClassifyList():await apiETAForum.myClassifyList()
     if(res.Ret===200){
-        classifyList=res.Data?.List??[]
+        if(classifyType.value==='myETA'){
+            classifyList=res.Data?.List||[]
+        }else{
+            const arr=res.Data.List||[]
+            classifyList=arr.map(item=>{
+                return {
+                    MyChartClassifyId:item.CollectClassifyId,
+                    MyChartClassifyName:item.ClassifyName
+                }
+            })
+        }
+        
         curClassifyList.value=classifyList
     }
 }
@@ -23,13 +36,25 @@ getClassifyList()
 
 let searchVal=ref('')
 function handleRefreshList(){
-    activeClassify=ref('')
+    activeClassify.value=''
     curClassifyList.value=classifyList.filter(item=>item.MyChartClassifyName.includes(searchVal.value))
 }
+// 切换分类类型
+function handleClassifyTypeChange(){
+    searchVal.value=''
+    activeClassify.value=''
+    activeClassifyName.value=''
+    curClassifyList.value=[]
+    classifyList=[]
+    getClassifyList()
+}
 
 function handleSave(){
     activeClassifyName.value=classifyList.filter(item=>item.MyChartClassifyId==activeClassify.value)[0]?.MyChartClassifyName
     showClassify.value=false
+    listState.page=1
+    listState.list=[]
+    listState.finished=false
     getChartList()
 }
 
@@ -41,18 +66,34 @@ const listState=reactive({
     loading:false
 })
 async function getChartList(){
-    const params={
+    listState.loading=true
+    let res=classifyType.value==='myETA'? await apiMyETAChart.myChartList({
         CurrentIndex:listState.page,
         PageSize:listState.pageSize,
         MyChartClassifyId:activeClassify.value
-    }
-    listState.loading=true
-    let res=await apiMyETAChart.myChartList(params)
+    }):await apiETAForum.getChartListForCollect({
+        CurrentIndex:listState.page,
+        PageSize:listState.pageSize,
+        CollectClassifyIds:`${activeClassify.value}`
+    })
     listState.loading=false
     if(res.Ret===200){
-        const arr=res.Data.List||[]
-        listState.list=[...listState.list,...arr]
-        listState.finished=res.Data?.Paging.IsEnd
+        if(classifyType.value==='myETA'){
+            const arr=res.Data?.List||[]
+            listState.list=[...listState.list,...arr]
+            listState.finished=res.Data?.Paging.IsEnd
+        }else{
+            const arr=res.Data?.List||[]
+            arr.forEach(_item => {
+                listState.list.push({
+                    ..._item,
+                    HaveOperaAuth:true,
+                    UniqueCode:`isETAForumChart_${_item.UniqueCode}`
+                })
+            });
+            listState.finished=res.Data?.Paging.IsEnd
+        }
+        
     }
 }
 // 触底加载更多
@@ -68,7 +109,8 @@ watch(
         emits('update',listState.list.map(item=>item.UniqueCode))
     },
     {
-        immediate:true
+        immediate:true,
+        deep:true
     }
 )
 
@@ -108,6 +150,10 @@ watch(
         :style="{ height: '100%' }"
     >
         <div class="select-classify-wrap">
+            <van-tabs class="type-tab" v-model:active="classifyType" @change="handleClassifyTypeChange">
+                <van-tab title="我的图库" name="myETA"></van-tab>
+                <van-tab title="ETA社区图库" name="ETAForum"></van-tab>
+            </van-tabs>
             <van-search v-model="searchVal" shape="round" placeholder="请输入分类名称" @search="handleRefreshList" @clear="handleRefreshList" />
             <ul class="list">
                 <van-radio-group v-model="activeClassify">
@@ -177,6 +223,9 @@ watch(
     display: flex;
     flex-direction: column;
     overflow: hidden;
+    .type-tab{
+        border-bottom: 1px solid #E7E7E7;
+    }
     .list{
         overflow-y: auto;
         flex: 1;

+ 1 - 1
src/views/report/components/reportInsert/PriceChart.vue

@@ -101,7 +101,7 @@ watch(
                     :key="item.ChartInfoId"
                     @click="handleSelect(item)"
                 >
-                    <div class="van-multi-ellipsis--l2 title">{{item.ChartName}}</div>
+                    <div class="van-multi-ellipsis--l2 title" v-html="item.SearchText"></div>
                     <img :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" alt="">
 
                     <svg v-if="selectChartList.includes(item.UniqueCode)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">

+ 2 - 1
src/views/report/components/reportInsert/SandTableImg.vue

@@ -1,4 +1,5 @@
 <script setup>
+import {setHightLightText} from '@/hooks/common'
 import {reactive, ref,watch} from 'vue'
 import apiSandBox from '@/api/sandBox'
 import { vInfiniteScroll } from '@vueuse/components'
@@ -83,7 +84,7 @@ watch(
                     :key="item.SandboxId"
                     @click="handleSelect(item)"
                 >
-                    <div class="van-multi-ellipsis--l2 title">{{item.Name}}</div>
+                    <div class="van-multi-ellipsis--l2 title" v-html="setHightLightText(item.Name,searchVal)"></div>
                     <img :src="item.PicUrl" alt="">
 
                     <svg v-if="selectChartList.includes(item.SandboxId)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">

+ 2 - 1
src/views/report/components/reportInsert/SemanticsImg.vue

@@ -1,4 +1,5 @@
 <script setup>
+import {setHightLightText} from '@/hooks/common'
 import {reactive, ref,watch} from 'vue'
 import apiSemanticAnalysis from '@/api/semanticAnalysis'
 import { vInfiniteScroll } from '@vueuse/components'
@@ -83,7 +84,7 @@ watch(
                     :key="item.SaCompareId"
                     @click="handleSelect(item)"
                 >
-                    <div class="van-multi-ellipsis--l2 title">{{item.Title}}</div>
+                    <div class="van-multi-ellipsis--l2 title" v-html="setHightLightText(item.Title,searchVal)"></div>
                     <img :src="item.ResultImg" alt="">
 
                     <svg v-if="selectChartList.includes(item.SaCompareId)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">

+ 2 - 2
src/views/report/components/reportInsert/SheetTableChart.vue

@@ -44,7 +44,7 @@ async function getSheetList(){
         IsShowMe:onlyMe.value
     }
     listState.loading=true
-    let res=await apiSheetChart.sheetList(params)
+    let res=await apiSheetChart.sheetListEs(params)
     listState.loading=false
     if(res.Ret===200){
         const arr=res.Data.List||[]
@@ -136,7 +136,7 @@ watch(
                     :key="item.UniqueCode"
                     @click="handleSelect(item)"
                 >
-                    <div class="van-multi-ellipsis--l2 title">{{item.ExcelName}}</div>
+                    <div class="van-multi-ellipsis--l2 title" v-html="item.SearchText"></div>
                     <img :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ExcelImage" alt="">
 
                     <svg v-if="selectChartList.includes(item.UniqueCode)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">

+ 1 - 1
src/views/report/components/reportInsert/StatisticAnalysis.vue

@@ -169,7 +169,7 @@ watch(
                     :key="item.ChartInfoId"
                     @click="handleSelect(item)"
                 >
-                    <div class="van-multi-ellipsis--l2 title">{{item.ChartName}}</div>
+                    <div class="van-multi-ellipsis--l2 title" v-html="item.SearchText"></div>
                     <img :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ChartImage" alt="">
 
                     <svg v-if="selectChartList.includes(item.UniqueCode)" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">

+ 15 - 8
src/views/report/smartReport/EditReport.vue

@@ -525,11 +525,12 @@ async function handleChartInsert({list,type,chartType}){
 					for(let item of list) {
 						//临时权限token
 						const authToken = await getLinkAuthToken(item);
+						const isETAForumChart=item.startsWith('isETAForumChart_')?true:false
 						tempCompDataArr.push({
 							compId:3,
 							compType:'chart',
 							id:getCompId(3),
-							content:`${link}?code=${item}&authToken=${authToken}`,
+							content:`${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&isETAForumChart=${isETAForumChart}&authToken=${authToken}`,
 							titleText: '',
 							style:'height:350px',
 							child:[]
@@ -864,12 +865,12 @@ const {
 
           <!-- 版头 -->
           <div class="html-head-img-box">
-              <div class="opt-btn-box"  style="display: none;">
+              <div class="opt-btn-box btn-top"  style="display: none;">
 									<van-icon name="delete-o" @click.stop="deleteLayoutPic(1)" color="#f00" size="22"/>
               </div>
               <img :src="headImg" alt="" style="display:block;width:100%">
               <div class="head-layout-item" v-for="item in headImgStyle" :key="item.value"
-              :style="{fontFamily:item.family,fontSize:(item.size*2)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
+              :style="{fontFamily:item.family,fontSize:(item.sizeMobile || item.size)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
                   width:item.width,height:item.height,left:item.left,top:item.top
               }">
                   {{ layoutBaseInfo[item.value] }}
@@ -1003,7 +1004,7 @@ const {
               </div>
               <img :src="endImg" alt="" style="display:block;width:100%">
               <div class="head-layout-item" v-for="item in endImgStyle" :key="item.value"
-              :style="{fontFamily:item.family,fontSize:(item.size*2)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
+              :style="{fontFamily:item.family,fontSize:(item.sizeMobile || item.size)+'px',fontWeight:item.weight,textAlign:item.align,color:item.color,
                   width:item.width,height:item.height,left:item.left,top:item.top
               }">
                   {{ layoutBaseInfo[item.value] }}
@@ -1308,6 +1309,9 @@ const {
 		padding: 20px;
 		.html-head-img-box,.html-end-img-box{
 				position: relative;
+				.btn-top {
+					margin-top: 60px;
+				}
 				&:hover{
 						.opt-btn-box{
 								display: block !important;
@@ -1424,10 +1428,13 @@ const {
     }
 		.report-content-box{
 			.html-head-img-box,.html-end-img-box{
-					.opt-btn-box{
-						top:-32px;
-						padding: 6px 10px 0;
-					}
+				.btn-top {
+					margin-top: 30px;
+				} 
+				.opt-btn-box{
+					top:-32px;
+					padding: 6px 10px 0;
+				}
 			}
 		} 
 

+ 2 - 2
src/views/reportEn/AddReport.vue

@@ -194,9 +194,9 @@ function handleInsert({list,type,chartType}){
             list.forEach(async(item) => {
                 //临时权限token
                 let authToken = await getLinkAuthToken(item);
-
+                const isETAForumChart=item.startsWith('isETAForumChart_')?true:false
                 reportContentIns.html.insert(`<p style='text-align:left; margin-top:10px;'>
-						<iframe src='${link}?code=${item}&fromPage=en&authToken=${authToken}' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
+						<iframe src='${link}?code=${isETAForumChart?item.replace(/^isETAForumChart_/, ''):item}&fromPage=en&isETAForumChart=${isETAForumChart}&authToken=${authToken}' width='100%' height='350' style='border-width:0px; min-height:350px;'></iframe>
 					</p>`)
             });
         }else if(chartType==='sheet'){

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

@@ -1,4 +1,5 @@
 <script setup name="ReportEnSearchList">
+import {setHightLightText} from '@/hooks/common'
 import {ref,reactive} from 'vue'
 import apiReportEn from '@/api/reportEn'
 import { showToast } from 'vant'
@@ -85,7 +86,7 @@ function goDetail(item){
                     class="item" 
                     @click="goDetail(item)"
                 >
-                    <h2 :class="['van-ellipsis title',item.Title.startsWith('【')?'inline-title':'']">{{item.Title}}</h2>
+                    <h2 v-html="setHightLightText(item.Title,keyword)" :class="['van-ellipsis title',item.Title.startsWith('【')?'inline-title':'']"></h2>
                     <p class="van-multi-ellipsis--l2 des">{{item.Abstract}}</p>
                     <div class="bot-info">
                         <div>

+ 4 - 2
src/views/sheetList/balanceDetail.vue

@@ -5,6 +5,7 @@ import apiSheet from '@/api/sheet'
 import { useWindowSize } from '@vueuse/core'
 import { showToast, showDialog  } from "vant";
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
+import BalanceSheet from './components/BalanceSheet.vue';
 import {getStaticImg} from '@/hooks/common.js'
 import {etaTablePermission,useAuthBtn} from '@/hooks/useAuthBtn'
 const {checkAuthBtn} = useAuthBtn()
@@ -22,7 +23,7 @@ let showVersionPicker = ref(false)
 const chartList = ref([])
 const sheetActions = computed(() => {
     const list = [
-        // { label: globalProperties.$t('shared_table.refresh'), types:'flushed', src: getStaticImg('table/flushed.png'), isAuth: etaTablePermission.etaTable_customize_balance_refresh},
+        { label: globalProperties.$t('shared_table.refresh'), types:'flushed', src: getStaticImg('table/flushed.png'), isAuth: etaTablePermission.etaTable_customize_balance_refresh},
         { label: globalProperties.$t('shared_table.download'), types:'download', src: getStaticImg('table/download.png'), isAuth: queryData.value.HaveOperaAuth && etaTablePermission.etaTable_customize_balance_download},
         { label: globalProperties.$t('shared_table.delete'), types:'delete', src: getStaticImg('table/delete.png'), isAuth: queryData.value.Button && queryData.value.Button.DeleteButton && etaTablePermission.etaTable_customize_balance_del},
     ]
@@ -299,7 +300,8 @@ function goChart () {
             <!-- <div class="top-item"><van-icon name="chart-trending-o" size="22" @click.stop="goChart"v-if="queryData.Source === 5" /> <van-icon name="more-o" size="22" @click.stop="showMoreAction=true"/></div> -->
         </div>
         <div class="sheet-box" v-if="queryData.UniqueCode">
-            <iframe :src="link + '/sheetshow?code=' + queryData.UniqueCode" frameborder="0" width="100%" height="100%"></iframe>
+            <!-- <iframe :src="link + '/sheetshow?code=' + queryData.UniqueCode" frameborder="0" width="100%" height="100%"></iframe> -->
+            <BalanceSheet :TableInfo="queryData"></BalanceSheet>
         </div>
         <div class="bottom">
             <template v-for="item in sheetActions" :key="item.types">

+ 154 - 0
src/views/sheetList/common/option.js

@@ -0,0 +1,154 @@
+
+/*  初始化  
+  options 其他配置 包括初始化数据 data:[{ celldata:[] }]
+  sheetInfo 表格id相关信息 用来内容hooks变化时保存草稿
+  limit 限制性数据
+  callbackItems 回调函数组成的对象
+*/
+import { showToast } from "vant";
+export function initSheet(container,options={},sheetInfo={},limit,callbackItems) {
+  console.log(limit);
+  const configOpt = {
+    container,
+    lang: localStorage.getItem('i18n') == 'en' ? 'en' : 'zh', // 设定表格语言
+    showinfobar: false,//顶部info
+    showsheetbar:false,//底部sheet页 暂禁止添加多个表格
+		showtoolbarConfig:{
+			image: false,//图片
+			print: false,//打印
+      chart: false, // '图表'
+      postil:  false, //'批注'
+		},
+    cellRightClickConfig: {
+      chart: false, // 图表生成
+      image: false, // 插入图片
+      link: false, // 插入链接
+    },
+    ...options,
+    // allowCopy:false,//没效果
+    hook: {
+      cellEditBefore:(range)=>{
+        if(limit.disabled){
+          showToast({message: '当前不可编辑',type:'warning'})
+          setTimeout(()=>{
+            luckysheet.exitEditMode();
+          },0)
+        }
+      },
+      cellUpdateBefore:()=>{
+        if(limit.disabled){
+          // 不可编辑
+          return false
+        }
+      },
+      // rangeCopyBefore:(range,data)=>{
+      //   // 不触发
+      //   console.log(range,data,'range,data','rangeCopyBefore');
+      // },
+      // rangeCopyAfter:(range,data)=>{
+      //   // 不触发
+      //   console.log(range,data,'range,data','rangeCopyAfter');
+      // },
+      updated: (a,b,c,d,e)=> {
+        /**
+         * lucksheet 本身没有完美的禁止用户编辑的功能,只能自己控制了,用户操作后撤销,用户撤销后重做
+         * stopUndo -- 阻止撤销 由于撤销会触发updated,又再进行撤销,直接撤销到最初状态了,我们只需要撤销一次
+         * withdrawUndo -- 撤销用户撤销的操作 即重做
+         */
+        if(limit.disabled){
+          if((!luckysheet.stopUndo)){
+            setTimeout(()=>{
+              showToast({message: '当前不可编辑',type:'warning'})
+              if(luckysheet.withdrawUndo) {
+                // 重做用户撤销的操作,我们自己的重做操作,不能再撤销
+                luckysheet.stopUndo=false
+                luckysheet.withdrawUndo=false
+                return
+              }
+              let result=luckysheet.undo()
+              luckysheet.stopUndo=false
+              if(Object.keys(result).length==0){
+                requestAnimationFrame(()=>{
+                  luckysheet.redo()
+                  luckysheet.withdrawUndo=true
+                })
+              }
+            },0)
+            luckysheet.stopUndo=true
+          }
+          return 
+        }
+        // console.log(callbackItems);
+        callbackItems.updated()
+        //草稿
+        // if(sheetInfo.Source&&sheetInfo.Source===1) {
+        //   let data = luckysheet.getAllSheets()[0];
+        //   data.luckysheet_select_save = [];
+        //   const { ExcelInfoId,ExcelName,ExcelClassifyId } = sheetInfo;
+        //   ExcelInfoId && sheetInterface.sheetDrafSave({
+        //     ExcelInfoId,
+        //     ExcelName,
+        //     ExcelClassifyId,
+        //     Content: JSON.stringify(data)
+        //   })
+        // }
+      },
+      ...options.hook,
+    },
+
+    showtoolbar: false, // 是否显示工具栏
+    showstatisticBar: false, // 是否显示底部计数栏
+    sheetBottomConfig: false, // sheet页下方的添加行按钮和回到顶部按钮配置
+    allowEdit: false, // 是否允许前台编辑
+    enableAddRow: false, // 允许增加行
+    enableAddCol: false, // 允许增加列
+    userInfo: false, // 右上角的用户信息展示样式
+    showRowBar: false, // 是否显示行号区域
+    showColumnBar: false, // 是否显示列号区域
+    sheetFormulaBar: false, // 是否显示公式栏
+    enableAddBackTop: false,//返回头部按钮
+    rowHeaderWidth: 0,//纵坐标
+    columnHeaderHeight: 0,//横坐标
+    showstatisticBarConfig: {
+      count:false,
+      view:false,
+      zoom:false,
+    },
+    showsheetbarConfig: {
+      add: false, //新增sheet
+      menu: false, //sheet管理菜单
+      sheet: false, //sheet页显示
+    },
+  }
+  
+  luckysheet.create(configOpt)
+  
+}
+
+/* 保存表格关联截图 手动选区截图再清空选区 */
+export const getSheetImage = (data) => {
+  const { celldata } = data;
+
+  //超过1000个就不遍历了
+  let r_start,r_end,c_start,c_end;
+  if(celldata.length > 1000) {
+    const splitData = celldata.slice(0,1000);
+    const r_arr = splitData.map(_ => _.c);
+    r_start = splitData[0].r;
+    r_end = splitData[splitData.length-1].r;
+    c_start = Math.min(...r_arr);
+    c_end = Math.max(...r_arr);
+
+  }else {
+    const r_arr = celldata.map(_ => _.c);
+    r_start = celldata[0].r;
+    r_end = celldata[celldata.length-1].r;
+    c_start = Math.min(...r_arr);
+    c_end = Math.max(...r_arr);
+  }
+  
+  luckysheet.setRangeShow({row:[r_start,r_end],column:[c_start,c_end]},{show: false})
+  let img = luckysheet.getScreenshot()
+  return img;
+
+}

+ 583 - 0
src/views/sheetList/components/BalanceSheet.vue

@@ -0,0 +1,583 @@
+<script setup>
+  // import Sheet from '@/components/Sheet.vue'
+  import { ref, reactive, computed, onMounted, watch } from 'vue'
+  const props = defineProps({
+    TableInfo:{
+      type:Object,
+      default:{}
+    }
+  })
+
+  const tableHeight = ref(0)
+  const disabled = ref(true)
+  const cellRef = ref([])
+  const rowTable = ref(null);
+  const tableData = reactive(props.TableInfo.TableData.Data);
+  const freezeData = reactive(props.TableInfo.ExtraConfig.TableFreeze);
+
+  const columnHeader = computed(() => {
+    return getColumnHeaderCode(
+      props.TableInfo.TableData.Data[0] ? props.TableInfo.TableData.Data[0].length : 0
+    );
+  });
+  const rowHeader = computed(() => {
+    let total_length = props.TableInfo.TableData.Data.length;
+    return getRowHeaderCode(total_length);
+  });
+  // 提取公共路径到局部变量
+  const tableFreeze = computed(() => props.TableInfo.ExtraConfig.TableFreeze);
+
+  // 使用对象解构获取所需属性
+  const minRow = computed(() => {
+    const { FreezeStartRow, FreezeEndRow } = tableFreeze.value;
+    return Math.min(FreezeStartRow, FreezeEndRow) === 0 ? 1 : Math.min(FreezeStartRow, FreezeEndRow)
+  });
+
+  const maxRow = computed(() => {
+    const { FreezeStartRow, FreezeEndRow } = tableFreeze.value;
+    return Math.max(FreezeStartRow, FreezeEndRow);
+  });
+
+  const minCol = computed(() => {
+    const { FreezeStartCol, FreezeEndCol } = tableFreeze.value;
+    return Math.min(FreezeStartCol, FreezeEndCol) === 0 ? 1 : Math.min(FreezeStartCol, FreezeEndCol);
+  });
+
+  const maxCol = computed(() => {
+    const { FreezeStartCol, FreezeEndCol } = tableFreeze.value;
+    return Math.max(FreezeStartCol, FreezeEndCol);
+  });
+
+  //手机端pc端不同样式
+  const dynamicSty = computed(()=>{
+    return isMobile() ? 'mobile-sty' : 'pc-sty';
+  })
+  //判断是否是手机设备
+ function isMobile() {
+		// 判断是否是移动设备的正则表达式
+		const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
+	
+		// 获取用户代理信息
+		const userAgent = navigator.userAgent;
+	
+		// 使用正则表达式检查用户代理信息
+		return mobileRegex.test(userAgent);
+  }
+  
+  // 字母列标
+  function getColumnHeaderCode(len) {
+    let tag_arr = [];
+    for(let i=0;i<len;i++) tag_arr.push(String.fromCharCode(65+i));
+    return tag_arr;
+  }
+
+  // 行标
+  function getRowHeaderCode(len) {
+    let tag_arr = [];
+    for(let i=0;i<len;i++) tag_arr.push(String(1+i));
+    return tag_arr;
+  }
+
+   // 判断展示小数位数值还是原来的值
+   function showCellValue(cell){
+      // console.log(cell)
+    let Value=''
+    if("Decimal" in cell&&cell.Decimal!=-1){
+      const multiplier = Math.pow(10, cell.Decimal);
+      const cellValue=+cell.Value
+      Value= cell.Decimal == 0 ? Math.round(cellValue) : Math.round(cellValue * multiplier) / multiplier;
+    }else{
+      Value=cell.ShowValue
+    }
+    return Value
+  }
+
+  // 初始化单元格横纵坐标
+  function initIndex(rindex, cindex, row, col) {
+    props.TableInfo.TableData.Data[rindex][cindex]["cIndex"] = cindex;
+    props.TableInfo.TableData.Data[rindex][cindex]["rIndex"] = rindex;
+    props.TableInfo.TableData.Data[rindex][cindex]["colIndex"] = col;
+    props.TableInfo.TableData.Data[rindex][cindex]["rowIndex"] = row;
+  }
+
+  onMounted(() => {
+    tableHeight.value = rowTable.value ? rowTable.value[0].offsetHeight : 35;
+  })
+
+  // 设置背景色及字体颜色
+  function getShowCss(style, type = '') {
+    const styleCss = JSON.parse(style);
+    let color = styleCss.glObj ? styleCss.glObj["color"] : styleCss["color"];
+    let BackgroundColor = styleCss.glObj
+        ? styleCss.glObj["background-color"]
+        : styleCss["background-color"];
+    let obj = {
+        color: color,
+        "background-color": BackgroundColor,
+        'text-align': styleCss.align ? styleCss.align : 'center',
+        'width': styleCss.width ? styleCss.width + 'px' : '140px',
+    }
+    if (type == 'header') {
+        obj = {
+            'width': styleCss.width ? styleCss.width + 'px' : '140px',
+        }
+    }
+    return obj;
+  }
+  // 是否展示小数
+  function isShowDecimal(cell) {
+    // // console.log(cell,111)
+    const styleCss = cell.ShowStyle ? JSON.parse(cell.ShowStyle) : {};
+    let tag = !isNaN(parseFloat(cell.ShowValue)) && isFinite(cell.ShowValue)
+    return tag ? !isNaN(parseFloat(styleCss.decimal)) && isFinite(styleCss.decimal) : false
+  }
+
+  // 由于在showStyle做了背景色及字体颜色处理,解决判断冲突
+  function isShowFormat(style, type = "css") {
+    const styleCss = style ? JSON.parse(style) : {};
+    let tag =
+        type === "css"
+            ? styleCss.pn > 0 || styleCss.pn < 0 || ["percent"].includes(styleCss.nt)
+            : style.EdbInfoId;
+    return tag;
+  }
+  // 展示小数
+  function showDecimalValue(cell) {
+    const styleCss = cell.ShowStyle ? JSON.parse(cell.ShowStyle) : {};
+    let Value = ''
+    if (styleCss.nt == 'percent' && cell.ShowFormatValue.indexOf('%') > -1) {
+      if (styleCss.last == 'nt') {
+        Value = commonDecimalValue(cell.ShowValue * 100, styleCss.decimal) + '%'
+      } else {
+        Value = (parseFloat(commonDecimalValue(cell.ShowValue, styleCss.decimal) * 100)) + '%'
+      }
+    } else {
+      Value = parseFloat(commonDecimalValue(cell.ShowValue, styleCss.decimal)).toFixed(styleCss.decimal)
+    }
+    if (getDecimalPlaces(cell.ShowValue) < styleCss.decimal || styleCss.nt == 'percent') {
+        Value = transDecimalPlace(cell.ShowValue + '', { ...styleCss, pn: styleCss.decimal - getDecimalPlaces(cell.ShowValue) + (styleCss.nt == 'percent'&&getDecimalPlaces(cell.ShowValue)!=0 ? 2 : 0) })
+    }
+    // console.log(cell)
+    tableData[cell.rIndex][cell.cIndex].ShowFormatValue = Value;
+    return Value
+  }
+  function commonDecimalValue(values, decimal) {
+    const multiplier = Math.pow(10, decimal);
+    return decimal == 0 ? Math.round(values) : Math.round(values * multiplier) / multiplier;
+  }
+  /* 计算小数点位数 */
+  function getDecimalPlaces(numStr) {
+    // 移除百分号,如果有的话
+    numStr = numStr.replace('%', '');
+
+    // 如果没有小数点,说明小数点位数为 0
+    if (!numStr.includes('.')) {
+      return 0;
+    }
+
+    // 获取当前小数点后的位数
+    const decimalPlaces = numStr.split('.')[1].length;
+
+    return decimalPlaces;
+  }
+  /* 增加减少小数点位数 */
+function transDecimalPlace(str,{pn,nt}) {
+
+  if(!isNumberVal(str)) return '';
+
+  let s = str.replace(/%/,''),
+      decimalPlaces = getDecimalPlaces(str),
+      decimalNum=pn;
+
+  //是否是原数字后设置百分比的
+  let transPercent = (nt==='percent'&&!str.endsWith('%')) ? true : false;
+
+  //原百分比设置为数字的
+  let transDecimal = (nt==='number'&&str.endsWith('%')) ? true : false
+
+  //后缀 百分号
+  let suffix = ((str.endsWith('%')&&nt!=='number')||transPercent) ? '%' : '';
+
+  let num = parseFloat(s);
+
+  if(decimalPlaces===0) { //整数
+    if(transPercent) {
+      return decimalNum > 0 
+        ? `${multiply(num,100)}.${'0'.repeat(decimalNum)}${suffix}`
+        : `${multiply(num,100)}${suffix}`;
+    }
+
+    if(transDecimal) {
+      return decimalNum > 0 
+        ? `${divide(num,100)}${'0'.repeat(decimalNum)}`
+        : `${divide(num,100)}`;
+    }
+
+    // 补零
+    return decimalNum > 0 
+      ? `${s}.${'0'.repeat(decimalNum)}${suffix}`
+      : `${s}${suffix}`;
+  }
+
+  if(decimalNum > 0) {
+    let addPointStr = `${s}${'0'.repeat(decimalNum)}`;
+    
+    return transPercent 
+      ? `${parseFloat(multiply(num,100)).toFixed(decimalPlaces+decimalNum-2)}${suffix}`
+      : transDecimal
+        ? `${parseFloat(divide(num,100)).toFixed(decimalPlaces+decimalNum+2)}`
+        : `${addPointStr}${suffix}`
+  }else {
+    let maxDecimal = Math.max(0,decimalNum+decimalPlaces);
+    let maxDecimalPercent = Math.max(0,maxDecimal-2);
+
+    return transPercent 
+      ? `${parseFloat(multiply(num,100)).toFixed(maxDecimalPercent)}${suffix}`
+      : transDecimal
+        ? `${parseFloat(divide(num,100)).toFixed(maxDecimal+2)}`
+        : `${parseFloat(num.toFixed(maxDecimal))}${suffix}`
+  }
+}
+/* 判断值是否是一个数字或数字字符串 */
+function isNumberVal(value) {
+  let reg = /^[-]?(?:\d*\.?\d+|\d+%|\d*\.\d+%)$/;
+  
+  return reg.test(value);
+}
+
+  // 是否固定列
+  function isWithinColRange (index) {
+    return rowHeader.value[index] >= minCol.value &&  rowHeader.value[index] <= maxCol.value
+  }
+   // 获取某一列的宽度
+  function getColumnHeaderWidth (index) {
+    return cellRef.value && cellRef.value[index] ? cellRef.value[index].offsetWidth : 104
+  }
+</script>
+
+<template>
+  <div class="sheet-show-wrapper">
+    <div :class="['table-wrapper',dynamicSty ]" >
+      <table
+        border="0"
+        class="table el-table__body"
+        id="myTable"
+        :style="disabled ? 'width:100%' : ''"
+        ref="tableRef"
+        style="position: relative;width: auto;"
+      >
+        <thead>
+          <tr ref="rowTable">
+             <!-- 行头 -->
+             <th class="th-tg sm"  style="width:36px"></th>
+
+            <!-- 列头 -->
+            <th 
+              ref="cellRef"
+              :style="TableInfo.TableData.Data[0][index].ShowStyle?getShowCss(TableInfo.TableData.Data[0][index].ShowStyle,'header'):{}"
+              v-for="(item, index) in columnHeader"
+              :key="index"
+              class="th-tg th-col"
+              :data-cindex="item"
+              :data-rindex="-1"
+            >
+              {{ item }}
+            </th>
+          </tr>
+        </thead>
+
+        <tbody>
+          <tr ref="rowTable" v-for="(row, index) in TableInfo.TableData.Data" :key="index" :style="freezeData ? `top: ${(index- minRow+1)*tableHeight}px;` : ''" :class="freezeData && rowHeader[index] >= minRow &&  rowHeader[index] <= maxRow ? 'fix' : ''">
+             <!-- 行头 -->
+             <th
+                class="th-tg th-row sm"
+                @contextmenu.prevent="rightClickHandle"
+                :data-rindex="rowHeader[index]"
+                :data-cindex="-1"
+              >
+                {{ rowHeader[index] }}
+              </th>
+            
+            <td
+              v-for="(cell, cell_index) in row"
+              :key="`${index}_${cell_index}`"
+              :data-rindex="rowHeader[index]"
+              :data-cindex="columnHeader[cell_index]"
+              :data-datarindex="index"
+              :data-datacindex="cell_index"
+              :style="[cell.ShowStyle?getShowCss(cell.ShowStyle):{}, freezeData && isWithinColRange(cell_index) ? {left: (cell_index- minCol+1)*getColumnHeaderWidth(cell_index) + 'px'} : '']"
+              :class="[freezeData && isWithinColRange(cell_index) ? 'fix-col' : '']"
+              :initIndex="initIndex(index,cell_index,rowHeader[index],columnHeader[cell_index])"
+              :data-key="cell.Uid"
+              v-show="!cell.merData || cell.merData.type!=='merged'"
+              :colspan="(cell.merData && cell.merData.type=='merge' && cell.merData.mer)?cell.merData.mer.colspan || 1:1"
+              :rowspan="(cell.merData && cell.merData.type=='merge' && cell.merData.mer)?cell.merData.mer.rowspan || 1:1"
+            >
+            <!-- 插入单元格禁止编辑 -->
+            <!-- [4,5,6,7,8].includes(cell.DataType)&&!cell.CanEdit -->
+            <template 
+              v-if="!cell.CanEdit
+              ||disabled
+              ||(cell.DataType===1&&[1,2].includes(cell.DataTimeType))"
+            >
+            <!-- 数字格式化显示 -->
+            <span 
+              v-if="cell.ShowStyle"
+              :data-rindex="rowHeader[index]"
+              :data-cindex="columnHeader[cell_index]"
+              :data-datarindex="index"
+              :data-datacindex="cell_index"
+              :data-key="cell.Uid"
+            >
+              {{isShowDecimal(cell)?showDecimalValue(cell):isShowFormat(cell.ShowStyle)?cell.ShowFormatValue:cell.DataTime?cell.ShowValue:[8,7,6,5,1].includes(cell.DataType)?cell.ShowValue?cell.ShowValue:'-':cell.Value}}
+            </span>
+
+            <span
+              :data-rindex="rowHeader[index]"
+              :data-cindex="columnHeader[cell_index]"
+              :data-datarindex="index"
+              :data-datacindex="cell_index"
+              :data-key="cell.Uid"
+              v-else
+            >{{ cell.ShowValue }}</span>
+          </template>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+
+    <div class="tool sheet-bottom">
+      <div class="sheet-source" 
+        v-if="TableInfo.SourcesFrom&&JSON.parse(TableInfo.SourcesFrom).isShow"
+        :style="`
+          color: ${ JSON.parse(TableInfo.SourcesFrom).color };
+          font-size: ${ JSON.parse(TableInfo.SourcesFrom).fontSize }px;
+        `"
+      >
+          source:<em>{{ JSON.parse(TableInfo.SourcesFrom).text}}</em>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang='scss' scoped>
+// sheet-show-wrapper 相关样式
+.sheet-show-wrapper {
+  max-width: 1200px;
+  overflow: hidden;
+  position: relative;
+  margin: 0 auto;
+  background: #fff;
+
+  .tool {
+    margin-top: 5px;
+    span {
+      cursor: pointer;
+    }
+  }
+
+  .sheet-bottom {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    white-space: nowrap;
+    padding: 0 10px;
+
+    .sheet-source {
+      width: 30%;
+      min-width: 150px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+  }
+}
+
+// 表格相关样式
+.table {
+  width: 100%;
+  font-size: 14px;
+  color: #333;
+
+  td, th {
+    width: 104px;
+    min-width: 104px;
+    height: 35px;
+    background: #fff;
+    text-align: center;
+    word-break: break-all;
+    border: none;
+    outline-color: #dcdfe6;
+    outline-style: solid;
+    outline-width: 1px;
+    word-wrap: break-word;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    position: relative;
+
+    &:first-child {
+      border-left: 1px solid #dcdfe6;
+    }
+    &.insert {
+      background: #FFEFDD;
+    }
+
+
+    &.fix-col {
+      position: sticky;
+      left: 0;
+      z-index: 98; // 表格右键操作弹窗为99
+    }
+  }
+
+  .th-tg {
+    background: #EBEEF5;
+
+    &:hover {
+      cursor: pointer;
+      background: #ddd;
+    }
+
+    &.sm {
+      width: 36px;
+      min-width: 36px;
+      max-width: 36px;
+    }
+  }
+
+}
+
+// 表格行(tr)相关样式
+tr {
+  &.fix {
+    position: sticky;
+    top: 0;
+    z-index: 98; // 表格右键操作弹窗为99
+  }
+
+  // 可根据需要添加更多tr的样式,例如整行选中效果等
+}
+
+// 滚动条样式
+::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track {
+  background: rgb(239, 239, 239);
+  border-radius: 2px;
+}
+
+::-webkit-scrollbar-thumb {
+  background: #ccc;
+  border-radius: 10px;
+  &:hover {
+    background: #888;
+  }
+}
+
+::-webkit-scrollbar-corner {
+  background: #666;
+}
+
+// table-wrapper 样式
+.table-wrapper {
+  max-width: calc(100vw - 20px);
+  max-height: calc(100vh - 400px);
+  margin: 0 auto;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch; /* ios滚动条 */
+}
+
+// PC端表格样式
+.pc-sty table {
+  table-layout: auto;
+
+  td, th {
+    width: auto;
+    height: auto;
+    padding: 0.4em 0;
+  }
+}
+
+// 移动端表格样式
+.mobile-sty table {
+  table-layout: auto;
+
+  td, th {
+    min-width: 120px;
+    height: 40px;
+  }
+}
+
+@media screen and (min-width: 650px) {
+  // 滚动条样式
+  ::-webkit-scrollbar {
+    width: 3px;
+    height: 3px;
+  }
+  ::-webkit-scrollbar-track {
+    border-radius: 1px;
+  }
+  ::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+  }
+  
+  // 表格相关样式
+  .sheet-show-wrapper {
+    .table-wrapper {
+      max-height: calc(100vh - 220px);
+    }
+
+    .tool {
+      margin-top: 3px;
+      span {
+        cursor: pointer;
+      }
+    }
+
+    .sheet-bottom {
+      padding: 0 5px;
+
+      .sheet-source {
+        width: 15%;
+        min-width: 75px;
+      }
+    }
+  }
+
+  // 表格相关样式
+  .table {
+    font-size: 7px;
+    td, th {
+      width: 52px;
+      min-width: 52px;
+      height: 17px;
+      outline-width: 1px;
+    }
+
+    .th-tg {
+      &.sm {
+        width: 18px;
+        min-width: 18px;
+        max-width: 18px;
+      }
+    }
+
+  }
+
+  // 移动端表格样式
+  .mobile-sty table {
+    table-layout: auto;
+
+    td, th {
+      min-width: 60px;
+      height: 20px;
+    }
+  }
+}
+</style>

+ 610 - 0
src/views/sheetList/components/MixedSheet.vue

@@ -0,0 +1,610 @@
+<script setup>
+  // import Sheet from '@/components/Sheet.vue'
+  import { ref, reactive, computed, onMounted, watch } from 'vue'
+  const props = defineProps({
+    TableInfo:{
+      type:Object,
+      default:{}
+    }
+  })
+
+  const tableHeight = ref(0)
+  const disabled = ref(true)
+  const cellRef = ref(null)
+  const rowTable = ref(null);
+  const tableData = reactive(props.TableInfo.TableData.Data);
+  const freezeData = reactive(props.TableInfo.ExtraConfig.TableFreeze);
+
+  const columnHeader = computed(() => {
+    return getColumnHeaderCode(
+      props.TableInfo.TableData.Data[0] ? props.TableInfo.TableData.Data[0].length : 0
+    );
+  });
+  const rowHeader = computed(() => {
+    let total_length = props.TableInfo.TableData.Data.length;
+    return getRowHeaderCode(total_length);
+  });
+  // 提取公共路径到局部变量
+  const tableFreeze = computed(() => props.TableInfo.ExtraConfig.TableFreeze);
+
+  // 使用对象解构获取所需属性
+  const minRow = computed(() => {
+    const { FreezeStartRow, FreezeEndRow } = tableFreeze.value;
+    return Math.min(FreezeStartRow, FreezeEndRow) === 0 ? 1 : Math.min(FreezeStartRow, FreezeEndRow)
+  });
+
+  const maxRow = computed(() => {
+    const { FreezeStartRow, FreezeEndRow } = tableFreeze.value;
+    return Math.max(FreezeStartRow, FreezeEndRow);
+  });
+
+  const minCol = computed(() => {
+    const { FreezeStartCol, FreezeEndCol } = tableFreeze.value;
+    return Math.min(FreezeStartCol, FreezeEndCol) === 0 ? 1 : Math.min(FreezeStartCol, FreezeEndCol);
+  });
+
+  const maxCol = computed(() => {
+    const { FreezeStartCol, FreezeEndCol } = tableFreeze.value;
+    return Math.max(FreezeStartCol, FreezeEndCol);
+  });
+
+  //手机端pc端不同样式
+  const dynamicSty = computed(()=>{
+    return isMobile() ? 'mobile-sty' : 'pc-sty';
+  })
+  //判断是否是手机设备
+ function isMobile() {
+		// 判断是否是移动设备的正则表达式
+		const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
+	
+		// 获取用户代理信息
+		const userAgent = navigator.userAgent;
+	
+		// 使用正则表达式检查用户代理信息
+		return mobileRegex.test(userAgent);
+  }
+  
+  // 字母列标
+  function getColumnHeaderCode(len) {
+    let tag_arr = [];
+    for(let i=0;i<len;i++) tag_arr.push(String.fromCharCode(65+i));
+    return tag_arr;
+  }
+
+  // 行标
+  function getRowHeaderCode(len) {
+    let tag_arr = [];
+    for(let i=0;i<len;i++) tag_arr.push(String(1+i));
+    return tag_arr;
+  }
+
+   // 判断展示小数位数值还是原来的值
+   function showCellValue(cell){
+      // console.log(cell)
+    let Value=''
+    if("Decimal" in cell&&cell.Decimal!=-1){
+      const multiplier = Math.pow(10, cell.Decimal);
+      const cellValue=+cell.Value
+      Value= cell.Decimal == 0 ? Math.round(cellValue) : Math.round(cellValue * multiplier) / multiplier;
+    }else{
+      Value=cell.ShowValue
+    }
+    return Value
+  }
+
+  // 初始化单元格横纵坐标
+  function initIndex(rindex, cindex, row, col) {
+    props.TableInfo.TableData.Data[rindex][cindex]["cIndex"] = cindex;
+    props.TableInfo.TableData.Data[rindex][cindex]["rIndex"] = rindex;
+    props.TableInfo.TableData.Data[rindex][cindex]["colIndex"] = col;
+    props.TableInfo.TableData.Data[rindex][cindex]["rowIndex"] = row;
+  }
+
+  onMounted(() => {
+    tableHeight.value = rowTable.value ? rowTable.value[0].offsetHeight : 35;
+  })
+
+  // 设置背景色及字体颜色
+  function getShowCss(style, type = '') {
+    const styleCss = JSON.parse(style);
+    let color = styleCss.glObj ? styleCss.glObj["color"] : styleCss["color"];
+    let BackgroundColor = styleCss.glObj
+        ? styleCss.glObj["background-color"]
+        : styleCss["background-color"];
+    let obj = {
+        color: color,
+        "background-color": BackgroundColor,
+        'text-align': styleCss.align ? styleCss.align : 'center',
+        'width': styleCss.width ? styleCss.width + 'px' : '140px',
+    }
+    if (type == 'header') {
+        obj = {
+            'width': styleCss.width ? styleCss.width + 'px' : '140px',
+        }
+    }
+    return obj;
+  }
+  // 是否展示小数
+  function isShowDecimal(cell) {
+    // // console.log(cell,111)
+    const styleCss = cell.ShowStyle ? JSON.parse(cell.ShowStyle) : {};
+    let tag = !isNaN(parseFloat(cell.ShowValue)) && isFinite(cell.ShowValue)
+    return tag ? !isNaN(parseFloat(styleCss.decimal)) && isFinite(styleCss.decimal) : false
+  }
+
+  // 由于在showStyle做了背景色及字体颜色处理,解决判断冲突
+  function isShowFormat(style, type = "css") {
+    const styleCss = style ? JSON.parse(style) : {};
+    let tag =
+        type === "css"
+            ? styleCss.pn > 0 || styleCss.pn < 0 || ["percent"].includes(styleCss.nt)
+            : style.EdbInfoId;
+    return tag;
+  }
+  // 展示小数
+  function showDecimalValue(cell) {
+    const styleCss = cell.ShowStyle ? JSON.parse(cell.ShowStyle) : {};
+    let Value = ''
+    if (styleCss.nt == 'percent' && cell.ShowFormatValue.indexOf('%') > -1) {
+      if (styleCss.last == 'nt') {
+        Value = commonDecimalValue(cell.ShowValue * 100, styleCss.decimal) + '%'
+      } else {
+        Value = (parseFloat(commonDecimalValue(cell.ShowValue, styleCss.decimal) * 100)) + '%'
+      }
+    } else {
+      Value = parseFloat(commonDecimalValue(cell.ShowValue, styleCss.decimal)).toFixed(styleCss.decimal)
+    }
+    if (getDecimalPlaces(cell.ShowValue) < styleCss.decimal || styleCss.nt == 'percent') {
+        Value = transDecimalPlace(cell.ShowValue + '', { ...styleCss, pn: styleCss.decimal - getDecimalPlaces(cell.ShowValue) + (styleCss.nt == 'percent'&&getDecimalPlaces(cell.ShowValue)!=0 ? 2 : 0) })
+    }
+    // console.log(cell)
+    tableData[cell.rIndex][cell.cIndex].ShowFormatValue = Value;
+    return Value
+  }
+  function commonDecimalValue(values, decimal) {
+    const multiplier = Math.pow(10, decimal);
+    return decimal == 0 ? Math.round(values) : Math.round(values * multiplier) / multiplier;
+  }
+  /* 计算小数点位数 */
+  function getDecimalPlaces(numStr) {
+    // 移除百分号,如果有的话
+    numStr = numStr.replace('%', '');
+
+    // 如果没有小数点,说明小数点位数为 0
+    if (!numStr.includes('.')) {
+      return 0;
+    }
+
+    // 获取当前小数点后的位数
+    const decimalPlaces = numStr.split('.')[1].length;
+
+    return decimalPlaces;
+  }
+  /* 增加减少小数点位数 */
+function transDecimalPlace(str,{pn,nt}) {
+
+  if(!isNumberVal(str)) return '';
+
+  let s = str.replace(/%/,''),
+      decimalPlaces = getDecimalPlaces(str),
+      decimalNum=pn;
+
+  //是否是原数字后设置百分比的
+  let transPercent = (nt==='percent'&&!str.endsWith('%')) ? true : false;
+
+  //原百分比设置为数字的
+  let transDecimal = (nt==='number'&&str.endsWith('%')) ? true : false
+
+  //后缀 百分号
+  let suffix = ((str.endsWith('%')&&nt!=='number')||transPercent) ? '%' : '';
+
+  let num = parseFloat(s);
+
+  if(decimalPlaces===0) { //整数
+    if(transPercent) {
+      return decimalNum > 0 
+        ? `${multiply(num,100)}.${'0'.repeat(decimalNum)}${suffix}`
+        : `${multiply(num,100)}${suffix}`;
+    }
+
+    if(transDecimal) {
+      return decimalNum > 0 
+        ? `${divide(num,100)}${'0'.repeat(decimalNum)}`
+        : `${divide(num,100)}`;
+    }
+
+    // 补零
+    return decimalNum > 0 
+      ? `${s}.${'0'.repeat(decimalNum)}${suffix}`
+      : `${s}${suffix}`;
+  }
+
+  if(decimalNum > 0) {
+    let addPointStr = `${s}${'0'.repeat(decimalNum)}`;
+    
+    return transPercent 
+      ? `${parseFloat(multiply(num,100)).toFixed(decimalPlaces+decimalNum-2)}${suffix}`
+      : transDecimal
+        ? `${parseFloat(divide(num,100)).toFixed(decimalPlaces+decimalNum+2)}`
+        : `${addPointStr}${suffix}`
+  }else {
+    let maxDecimal = Math.max(0,decimalNum+decimalPlaces);
+    let maxDecimalPercent = Math.max(0,maxDecimal-2);
+
+    return transPercent 
+      ? `${parseFloat(multiply(num,100)).toFixed(maxDecimalPercent)}${suffix}`
+      : transDecimal
+        ? `${parseFloat(divide(num,100)).toFixed(maxDecimal+2)}`
+        : `${parseFloat(num.toFixed(maxDecimal))}${suffix}`
+  }
+}
+/* 判断值是否是一个数字或数字字符串 */
+function isNumberVal(value) {
+  let reg = /^[-]?(?:\d*\.?\d+|\d+%|\d*\.\d+%)$/;
+  
+  return reg.test(value);
+}
+
+  // 是否固定列
+  function isWithinColRange (index) {
+    return rowHeader.value[index] >= minCol.value &&  rowHeader.value[index] <= maxCol.value
+  }
+   // 获取某一列的宽度
+  function getColumnHeaderWidth (index) {
+    if (!cellRef.value) return
+    // console.log(index);
+    // console.log(cellRef.value);
+    // console.log(cellRef.value[index].offsetWidth);
+    
+    return cellRef.value && cellRef.value[index] ? cellRef.value[index].offsetWidth : 104
+  }
+</script>
+
+<template>
+  <div class="sheet-show-wrapper">
+    <div :class="['table-wrapper',dynamicSty ]" >
+      <table
+        border="0"
+        class="table el-table__body"
+        id="myTable"
+        :style="disabled ? 'width:100%' : ''"
+        ref="tableRef"
+        style="position: relative;width: auto;"
+      >
+        <thead>
+          <tr ref="rowTable">
+            <!-- 行头 -->
+            <th class="th-tg sm"  style="width:36px"></th>
+
+            <!-- 列头 -->
+            <th 
+              ref="cellRef"
+              :style="TableInfo.TableData.Data[0][index].ShowStyle?getShowCss(TableInfo.TableData.Data[0][index].ShowStyle,'header'):{}"
+              v-for="(item, index) in columnHeader"
+              :key="index"
+              class="th-tg th-col"
+              :data-cindex="item"
+              :data-rindex="-1"
+            >
+              {{ item }}
+            </th>
+          </tr>
+        </thead>
+
+        <tbody>
+          <tr ref="rowTable" v-for="(row, index) in TableInfo.TableData.Data" :key="index" :style="freezeData ? `top: ${(index- minRow+1)*tableHeight}px;` : ''" :class="freezeData && rowHeader[index] >= minRow &&  rowHeader[index] <= maxRow ? 'fix' : ''">
+              <!-- 行头 -->
+              <th
+                class="th-tg th-row sm"
+                @contextmenu.prevent="rightClickHandle"
+                :data-rindex="rowHeader[index]"
+                :data-cindex="-1"
+              >
+                {{ rowHeader[index] }}
+              </th>
+        
+            <td
+              v-for="(cell, cell_index) in row"
+              :key="`${index}_${cell_index}`"
+              :data-rindex="rowHeader[index]"
+              :data-cindex="columnHeader[cell_index]"
+              :data-datarindex="index"
+              :data-datacindex="cell_index"
+              :style="[cell.ShowStyle?getShowCss(cell.ShowStyle):{}, freezeData && isWithinColRange(cell_index) ? {left: (cell_index- minCol+1)*getColumnHeaderWidth(cell_index) + 'px'} : '']"
+              :class="[freezeData && isWithinColRange(cell_index) ? 'fix-col' : '']"
+              :initIndex="initIndex(index,cell_index,rowHeader[index],columnHeader[cell_index])"
+              :data-key="cell.Uid"
+              v-show="!cell.merData || cell.merData.type!=='merged'"
+              :colspan="(cell.merData && cell.merData.type=='merge' && cell.merData.mer)?cell.merData.mer.colspan || 1:1"
+              :rowspan="(cell.merData && cell.merData.type=='merge' && cell.merData.mer)?cell.merData.mer.rowspan || 1:1"
+            >
+            <!-- 插入单元格禁止编辑 -->
+            <!-- [4,5,6,7,8].includes(cell.DataType)&&!cell.CanEdit -->
+            <template 
+              v-if="!cell.CanEdit
+              ||disabled
+              ||(cell.DataType===1&&[1,2].includes(cell.DataTimeType))"
+            >
+            <!-- 数字格式化显示 -->
+            <span 
+              v-if="cell.ShowStyle"
+              :data-rindex="rowHeader[index]"
+              :data-cindex="columnHeader[cell_index]"
+              :data-datarindex="index"
+              :data-datacindex="cell_index"
+              :data-key="cell.Uid"
+            >
+              {{isShowDecimal(cell)?showDecimalValue(cell):isShowFormat(cell.ShowStyle)?cell.ShowFormatValue:cell.DataTime?cell.ShowValue:[8,7,6,5,1].includes(cell.DataType)?cell.ShowValue?cell.ShowValue:'-':cell.Value}}
+            </span>
+
+            <span
+              :data-rindex="rowHeader[index]"
+              :data-cindex="columnHeader[cell_index]"
+              :data-datarindex="index"
+              :data-datacindex="cell_index"
+              :data-key="cell.Uid"
+              v-else
+            >{{ cell.ShowValue }}</span>
+          </template>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+
+    <div class="tool sheet-bottom">
+      <div class="sheet-source" 
+        v-if="TableInfo.SourcesFrom&&JSON.parse(TableInfo.SourcesFrom).isShow"
+        :style="`
+          color: ${ JSON.parse(TableInfo.SourcesFrom).color };
+          font-size: ${ JSON.parse(TableInfo.SourcesFrom).fontSize }px;
+        `"
+      >
+          source:<em>{{ JSON.parse(TableInfo.SourcesFrom).text}}</em>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang='scss' scoped>
+// sheet-show-wrapper 相关样式
+.sheet-show-wrapper {
+  max-width: 1200px;
+  overflow: hidden;
+  position: relative;
+  margin: 0 auto;
+  background: #fff;
+
+  .tool {
+    margin-top: 5px;
+    span {
+      cursor: pointer;
+    }
+  }
+
+  .sheet-bottom {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    white-space: nowrap;
+    padding: 0 10px;
+
+    .sheet-source {
+      width: 30%;
+      min-width: 150px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .right-btns {
+      display: flex;
+      align-items: center;
+      gap: 15px;
+      color: #666;
+    }
+  }
+}
+
+// 表格相关样式
+.table {
+  width: 100%;
+  font-size: 14px;
+  color: #333;
+
+  td, th {
+    width: 104px;
+    min-width: 104px;
+    height: 35px;
+    background: #fff;
+    text-align: center;
+    word-break: break-all;
+    border: none;
+    outline-color: #dcdfe6;
+    outline-style: solid;
+    outline-width: 1px;
+    word-wrap: break-word;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    position: relative;
+
+    &:first-child {
+      border-left: 1px solid #dcdfe6;
+    }
+    &.insert {
+      background: #FFEFDD;
+    }
+
+
+    &.fix-col {
+      position: sticky;
+      left: 0;
+      z-index: 98; // 表格右键操作弹窗为99
+    }
+  }
+
+  .th-tg {
+    background: #EBEEF5;
+
+    &:hover {
+      cursor: pointer;
+      background: #ddd;
+    }
+
+    &.sm {
+      width: 36px;
+      min-width: 36px;
+      max-width: 36px;
+    }
+  }
+
+}
+
+// 表格行(tr)相关样式
+tr {
+  &.fix {
+    position: sticky;
+    top: 0;
+    z-index: 98; // 表格右键操作弹窗为99
+  }
+
+  // 可根据需要添加更多tr的样式,例如整行选中效果等
+}
+
+// 滚动条样式
+::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track {
+  background: rgb(239, 239, 239);
+  border-radius: 2px;
+}
+
+::-webkit-scrollbar-thumb {
+  background: #ccc;
+  border-radius: 10px;
+
+  &:hover {
+    background: #888;
+  }
+}
+
+::-webkit-scrollbar-corner {
+  background: #666;
+}
+
+// table-wrapper 样式
+.table-wrapper {
+  max-width: calc(100vw - 20px);
+  max-height: calc(100vh - 400px);
+  margin: 0 auto;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch; /* ios滚动条 */
+}
+
+// PC端表格样式
+.pc-sty table {
+  table-layout: auto;
+
+  td, th {
+    width: auto;
+    height: auto;
+    padding: 0.4em 0;
+  }
+}
+
+// 移动端表格样式
+.mobile-sty table {
+  table-layout: auto;
+
+  td, th {
+    min-width: 120px;
+    height: 40px;
+  }
+}
+
+// 背景水印样式
+.background-watermark {
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: 100%;
+}
+@media screen and (min-width: 650px) {
+  // 滚动条样式
+  ::-webkit-scrollbar {
+    width: 3px;
+    height: 3px;
+  }
+  ::-webkit-scrollbar-track {
+    border-radius: 1px;
+  }
+  ::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+  }
+  
+  // 表格相关样式
+  .sheet-show-wrapper {
+    .table-wrapper {
+      max-height: calc(100vh - 220px);
+    }
+
+    .tool {
+      margin-top: 3px;
+      span {
+        cursor: pointer;
+      }
+    }
+
+    .sheet-bottom {
+      padding: 0 5px;
+
+      .sheet-source {
+        width: 15%;
+        min-width: 75px;
+      }
+    }
+  }
+
+  // 表格相关样式
+  .table {
+    font-size: 7px;
+    td, th {
+      width: 52px;
+      min-width: 52px;
+      height: 17px;
+      outline-width: 1px;
+    }
+
+    .th-tg {
+      &.sm {
+        width: 18px;
+        min-width: 18px;
+        max-width: 18px;
+      }
+    }
+
+  }
+  // PC端表格样式
+  .pc-sty table {
+    table-layout: auto;
+
+    td, th {
+      width: auto;
+      height: auto;
+      padding: 0.2em 0;
+    }
+  }
+  // 移动端表格样式
+  .mobile-sty table {
+    table-layout: auto;
+
+    td, th {
+      min-width: 60px;
+      height: 20px;
+    }
+  }
+}
+</style>

+ 163 - 0
src/views/sheetList/components/SharedSheet.vue

@@ -0,0 +1,163 @@
+<script setup>
+  // import Sheet from '@/components/Sheet.vue'
+  import { ref, reactive, computed, onMounted, watch } from 'vue'
+  import Sheet from './Sheet.vue';
+  // 定义 props
+  const props = defineProps({
+    TableInfo:{
+      type:Object,
+      default:{}
+    }
+  })
+
+  //手机端pc端不同样式
+  const dynamicSty = computed(()=>{
+    return isMobile() ? 'mobile-sty' : 'pc-sty';
+  })
+  //判断是否是手机设备
+ function isMobile() {
+		// 判断是否是移动设备的正则表达式
+		const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
+	
+		// 获取用户代理信息
+		const userAgent = navigator.userAgent;
+	
+		// 使用正则表达式检查用户代理信息
+		return mobileRegex.test(userAgent);
+  }
+  
+
+
+
+
+
+
+
+
+
+</script>
+
+<template>
+  <div class="sheet-show-wrapper">
+    <div :class="['table-wrapper',dynamicSty ]" >
+      
+      <Sheet
+        :limit="{
+            disabled:true
+        }"
+        :sheetInfo="{
+            ExcelInfoId: TableInfo.ExcelInfoId,
+            ExcelName: TableInfo.ExcelName,
+            ExcelClassifyId: TableInfo.ExcelClassifyId,
+            Source: TableInfo.Source
+        }"
+        :option="{
+            data: [{
+            ...JSON.parse(TableInfo.Content),
+            scrollTop: 0,
+            scrollLeft: 0
+            }]
+        }"
+        >
+      </Sheet>
+    </div>
+
+    <div class="tool sheet-bottom">
+      <div class="sheet-source" 
+        v-if="TableInfo.SourcesFrom&&JSON.parse(TableInfo.SourcesFrom).isShow"
+        :style="`
+          color: ${ JSON.parse(TableInfo.SourcesFrom).color };
+          font-size: ${ JSON.parse(TableInfo.SourcesFrom).fontSize }px;
+        `"
+      >
+          source:<em>{{ JSON.parse(TableInfo.SourcesFrom).text}}</em>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang='scss' scoped>
+.sheet-show-wrapper {
+  max-width: 1200px;
+  overflow: hidden;
+  position: relative;
+  margin: 0 auto;
+  background: #fff;
+  .tool{
+    // text-align: right;
+    margin-top: 5px;
+    span{
+        cursor: pointer;
+    }
+  }
+  .sheet-bottom{
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    white-space: nowrap;
+    padding: 0 10px;
+    .sheet-source{
+      width: 30%;
+      min-width: 150px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+    .right-btns {
+      display: flex;
+      align-items: center;
+      gap:15px;
+      color: #666;
+    }
+  }
+}
+@media screen and (min-width: 650px) {
+  .sheet-show-wrapper {
+    height: calc(100vh - 190px);
+  }
+}
+</style>
+<style lang='scss' scoped>
+::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+::-webkit-scrollbar-track {
+  background: rgb(239, 239, 239);
+  border-radius: 2px;
+}
+::-webkit-scrollbar-thumb {
+  background: #ccc;
+  border-radius: 10px;
+}
+::-webkit-scrollbar-thumb:hover {
+  background: #888;
+}
+::-webkit-scrollbar-corner {
+  background: #666;
+}
+.table-wrapper {
+  max-width: calc(100vw - 20px);
+  height: calc(100vh - 400px);
+  margin: 0 auto;
+  // margin-right: -5px;
+  overflow: auto;
+  -webkit-overflow-scrolling: touch; /* ios滚动条 */
+}
+
+.pc-sty table {
+  table-layout: auto;
+  td,th {
+    width: auto;
+    height: auto;
+    padding: 0.4em 0;
+  }
+}
+
+.mobile-sty table {
+  table-layout: auto;
+  td,th {
+    min-width: 120px;
+    height: 40px;
+  }
+}
+</style>

+ 100 - 0
src/views/sheetList/components/Sheet.vue

@@ -0,0 +1,100 @@
+<template>
+  <div id="sheet-container"></div>
+</template>
+
+<script setup>
+import { onMounted, onBeforeUnmount, ref, defineProps, defineEmits } from 'vue';
+import { initSheet } from '../common/option';
+
+// 定义 props
+const props = defineProps({
+  option: {
+    type: Object,
+    default: () => ({})
+  },
+  sheetInfo: {
+    type: Object,
+    default: () => ({})
+  },
+  limit: {
+    type: Object,
+    default: () => ({ disabled: false })
+  }
+});
+
+// 定义 emit 事件
+const emit = defineEmits(['updated']);
+
+// 响应式数据
+const sheetObj = ref({});
+
+// 方法
+const init = () => {
+  let optionData = props.option ? props.option : {};
+  let callbackItems = { updated: updateEmit };
+  initSheet('sheet-container', optionData, props.sheetInfo, props.limit, callbackItems);
+  setTimeout(() => {
+    const sheetDom = document.querySelector('#sheet-container');
+    const morebtn = document.querySelector('#luckysheet-icon-morebtn-div');
+    if (morebtn) {
+      morebtn.style.maxWidth = sheetDom.clientWidth - 70 + 'px';
+    }
+  }, 300);
+};
+
+const copyDisable = (e) => {
+  if (!props.limit.disabled) return;
+  if (e.target && e.target.getAttribute('data-type') && e.target.getAttribute('data-type').indexOf('lucksheet') !== -1) {
+    luckysheet.enterEditMode();
+    return false;
+  }
+};
+
+const getCellContent = (arr) => {
+  if (!arr || !arr.length) return '';
+  let cellStr = '';
+  arr.forEach((_) => {
+    cellStr += _.v.replace(/\r/, '').replace(/\n/, '');
+  });
+  return cellStr;
+};
+
+const updateEmit = () => {
+  emit('updated');
+};
+
+// 生命周期钩子
+onMounted(() => {
+  init();
+  document.addEventListener('copy', copyDisable);
+});
+
+onBeforeUnmount(() => {
+  luckysheet.destroy();
+  document.removeEventListener('copy', copyDisable);
+});
+</script>
+
+<style scoped lang="scss">
+#sheet-container {
+  margin: 0;
+  padding: 0;
+  position: absolute;
+  width: 100%;
+  left: 0px;
+  top: 0;
+  bottom: 0px;
+}
+</style>
+
+<style lang="scss">
+.luckysheet .toolbar {
+  background: none;
+  margin: 0;
+  padding: 0;
+}
+
+.luckysheet-input-box {
+  z-index: 99999;
+}
+</style>

+ 634 - 0
src/views/sheetList/components/TimelineSheet.vue

@@ -0,0 +1,634 @@
+<script setup>
+  // import Sheet from '@/components/Sheet.vue'
+  import { ref, computed, onMounted } from 'vue'
+  const props = defineProps({
+    TableInfo:{
+      type:Object,
+      default:{}
+    }
+  })
+
+  const tableHeight = ref(0)
+  const rowTable = ref(null)
+  const disabled = ref(true)
+  const EdbKeys = ref(["EdbName", "Unit"])
+  const cellRef = ref(null)
+
+  const dateArr = computed (() => {
+    return props.TableInfo.TableData.Data.length ? props.TableInfo.TableData.Data[0].Data.map(_ => _.DataTime) : []
+  })
+  const columnHeader = computed(() => {
+    return getColumnHeaderCode(
+      props.TableInfo.TableData.Data ? props.TableInfo.TableData.Data.length : 0
+    );
+  });
+  const rowHeader = computed(() => {
+    let total_length = dateArr.value.length +  props.TableInfo.TableData.TextRowData.length;
+    return getRowHeaderCode(total_length);
+  });
+  // 提取公共路径到局部变量
+  const tableFreeze = computed(() => props.TableInfo.ExtraConfig.TableFreeze);
+
+  const minRow = computed(() => {
+    const { FreezeStartRow, FreezeEndRow } = tableFreeze.value;
+    return Math.min(FreezeStartRow, FreezeEndRow) === 0 ? 1 : Math.min(FreezeStartRow, FreezeEndRow)
+  });
+
+  const maxRow = computed(() => {
+    const { FreezeStartRow, FreezeEndRow } = tableFreeze.value;
+    return Math.max(FreezeStartRow, FreezeEndRow);
+  });
+
+  const minCol = computed(() => {
+    const { FreezeStartCol, FreezeEndCol } = tableFreeze.value;
+    return Math.min(FreezeStartCol, FreezeEndCol) === 0 ? 1 : Math.min(FreezeStartCol, FreezeEndCol);
+  });
+
+  const maxCol = computed(() => {
+    const { FreezeStartCol, FreezeEndCol } = tableFreeze.value;
+    return Math.max(FreezeStartCol, FreezeEndCol);
+  });
+
+  //手机端pc端不同样式
+  const dynamicSty = computed(()=>{
+    return isMobile() ? 'mobile-sty' : 'pc-sty';
+  })
+  //判断是否是手机设备
+ function isMobile() {
+		// 判断是否是移动设备的正则表达式
+		const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
+	
+		// 获取用户代理信息
+		const userAgent = navigator.userAgent;
+	
+		// 使用正则表达式检查用户代理信息
+		return mobileRegex.test(userAgent);
+  }
+  
+  // 字母列标
+  function getColumnHeaderCode(len) {
+    let tag_arr = [];
+    for(let i=0;i<len;i++) tag_arr.push(String.fromCharCode(65+i));
+    return tag_arr;
+  }
+
+  // 行标
+  function getRowHeaderCode(len) {
+    let tag_arr = [];
+    for(let i=0;i<len;i++) tag_arr.push(String(1+i));
+    return tag_arr;
+  }
+
+   // 判断展示小数位数值还是原来的值
+   function showCellValue(cell){
+      // console.log(cell)
+    let Value=''
+    if("Decimal" in cell&&cell.Decimal!=-1){
+      const multiplier = Math.pow(10, cell.Decimal);
+      const cellValue=+cell.Value
+      Value= cell.Decimal == 0 ? Math.round(cellValue) : Math.round(cellValue * multiplier) / multiplier;
+    }else{
+      Value=cell.ShowValue
+    }
+    return Value
+  }
+
+  onMounted(() => {
+    console.log('rowTable', rowTable.value.offsetHeight);
+    
+    tableHeight.value = rowTable.value ? rowTable.value.offsetHeight : 35;
+  })
+
+  // 是否固定列
+  function isWithinColRange (index) {
+    return rowHeader.value[index] >= minCol.value &&  rowHeader.value[index] <= maxCol.value
+  }
+   // 获取某一列的宽度
+  function getColumnHeaderWidth (index) {
+    return cellRef.value && cellRef.value ? cellRef.value[index].offsetWidth : 104
+  }
+</script>
+
+<template>
+  <div class="sheet-show-wrapper">
+    <div :class="['table-wrapper',dynamicSty ]" v-if="props.TableInfo.ExcelType === 1">
+      <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''" style="position: relative;">
+        <thead>
+          <tr ref="rowTable">
+            <!-- 行头 -->
+            <th class="th-tg sm"></th>
+            <th class="th-tg"></th>
+
+            <!-- 列头 -->
+            <th 
+              ref="cellRef"
+              v-for="(item, index) in columnHeader" 
+              :key="index" 
+              class="th-tg th-col"
+              :data-cindex="item"
+              :data-rindex="-1">
+              {{item}}
+            </th>
+          </tr>
+        </thead>
+
+        <tbody>
+          <tr v-for="(item, index) in EdbKeys" :key="item">
+            <!-- 行头 -->
+            <th class="th-tg sm"></th>
+
+            <td rowspan="2" v-if="index === 0" class="head-column">
+              日期
+            </td>
+            <td 
+              v-for="(edb, sub_index) in TableInfo.TableData.Data" 
+              :key="sub_index"
+              :data-rindex="-1"
+              :data-cindex="columnHeader[sub_index]"
+              :class="['data-cell',{
+                'one-bg':(index+1)%2&&index>0,
+                'tow-bg': (index+1)%2!==0&&index>0,
+                'head-column': index === 0,
+              }]"
+            >
+              <template v-if="item === 'EdbName'">
+                <span :class="{'edbname-td':disabled}" @click="edbJumpToBase(edb)">{{ edb.EdbAliasName||edb[item] }}</span>
+              </template>
+              <template v-else>{{ edb[item] }} / {{edb.Frequency }}</template>
+            </td>
+          </tr>
+
+          <!-- 数据行  第一列日期-->
+          <tr v-for="(date, dateIndex) in dateArr" :key="date" :style="TableInfo.ExtraConfig.TableFreeze ? `top: ${(dateIndex- minRow+1)*tableHeight}px;` : ''" :class="TableInfo.ExtraConfig.TableFreeze && rowHeader[dateIndex] >= minRow &&  rowHeader[dateIndex] <= maxRow ? 'fix' : ''">
+            <!-- 行头 -->
+            <th 
+              class="th-tg th-row sm"
+              :data-cindex="-1"
+              :data-rindex="rowHeader[dateIndex]"
+              @contextmenu.prevent="rightClickHandle"
+            >{{rowHeader[dateIndex]}}</th>
+            <td 
+              :data-rindex="rowHeader[dateIndex]"
+              :data-cindex="-1"
+              :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(dateIndex) ? `left: 0px;` : ''"
+              :class="[TableInfo.ExtraConfig.TableFreeze && isWithinColRange(dateIndex) ? 'fix-col' : '']"
+            >{{date}}</td>
+            <td 
+              v-for="(edb, edb_index) in TableInfo.TableData.Data" 
+              :key="edb_index"
+              :data-rindex="rowHeader[dateIndex]"
+              :data-cindex="columnHeader[edb_index]"
+              :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(edb_index+dateArr.length) ? `left: ${(edb_index+dateArr.length- minCol+1)*getColumnHeaderWidth(edb_index+dateArr.length)}px;` : ''"
+              :class="[([2,3,5].includes(edb.Data.find(_ =>_.DataTime === date)?.DataType)&&edb.Data.find(_ =>_.DataTime === date)?.ShowValue)?'insert': '', TableInfo.ExtraConfig.TableFreeze && isWithinColRange(edb_index+dateArr.length) ? 'fix-col' : '']"
+            >
+              <!-- 实际值/插值 -->
+              <span 
+                :data-rindex="rowHeader[dateIndex]"
+                :data-cindex="columnHeader[edb_index]"
+                v-if="(edb.Data.find(_ =>_.DataTime === date)?.ShowValue&&!edb.Data.find(_ =>_.DataTime === date).CanEdit)||disabled"
+              >
+                {{showCellValue(edb.Data.find(_ =>_.DataTime === date)) || '-'}}
+              </span>
+            </td>
+          </tr>
+          <!-- 文本行 -->
+          <tr v-for="(row,index) in TableInfo.TableData.TextRowData" :key="index" :style="TableInfo.ExtraConfig.TableFreeze ? `top: ${(index+dateArr.length-minRow+1)*tableHeight}px;` : ''" :class="TableInfo.ExtraConfig.TableFreeze && rowHeader[index+dateArr.length] >= minRow &&  rowHeader[index+dateArr.length] <= maxRow ? 'fix' : ''">
+            <!-- 行头 -->
+            <th 
+              class="th-tg th-row sm" 
+              @contextmenu.prevent="rightClickHandle"
+              :data-rindex="rowHeader[index+dateArr.length]"
+              :data-cindex="-1"
+            >{{rowHeader[index+dateArr.length]}}</th>
+            <td 
+              v-for="(cell, cell_index) in row" 
+              :key="`${index}_${cell_index}`"
+              :data-rindex="rowHeader[index+dateArr.length]"
+              :data-cindex="cell_index===0?-1:columnHeader[cell_index-1]"
+              :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(cell_index+dateArr.length) ? `left: ${(cell_index+dateArr.length- minCol+1)*getColumnHeaderWidth(cell_index+dateArr.length)}px;` : ''"
+              :class="[([2,3,5].includes(cell.DataType)&&cell.ShowValue)?'insert': '',  TableInfo.ExtraConfig.TableFreeze && isWithinColRange(cell_index+dateArr.length) ? 'fix-col' : '']"
+            >
+              <span 
+                :data-rindex="rowHeader[index+dateArr.length]"
+                :data-cindex="cell_index===0?-1:columnHeader[cell_index-1]"
+                v-if="(cell.ShowValue&&!cell.CanEdit)||disabled"
+              > {{cell.ShowValue}}</span>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <div :class="['table-wrapper',dynamicSty ]" v-else>
+      <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''" style="position: relative;">
+        <thead>
+          <tr ref="rowTable">
+            <!-- 行头 -->
+            <th class="th-tg sm"></th>
+
+            <th class="th-tg"></th>
+            <th class="th-tg"></th>
+
+            <!-- 列头 -->
+            <th 
+              v-for="(item, index) in rowHeader" 
+              :key="index" 
+              ref="cellRef"
+              class="th-tg th-col"
+              :data-cindex="-1"
+              :data-rindex="rowHeader[index]">
+              {{item}}
+            </th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr>
+            <!-- 行头 -->
+            <th class="th-tg sm"></th>
+
+            <td colspan="2" class="head-column">
+              日期
+            </td>
+
+            <!-- 日期列 -->
+            <td 
+              v-for="(date, sub_index) in dateArr" 
+              :key="date"
+              :data-rindex="rowHeader[sub_index]"
+              :data-cindex="-1"
+ 
+              :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(sub_index) ? `left: ${(sub_index- minCol+1)*getColumnHeaderWidth(sub_index)}px;` : ''"
+              :class="['data-cell','head-column',
+              TableInfo.ExtraConfig.TableFreeze && isWithinColRange(sub_index) ? 'fix-col' : '']"
+            >{{ date }}</td>
+
+            <!-- 文本列 -->
+            <td
+              v-for="(column,index) in TableInfo.TableData.TextRowData"
+              :key="index"
+              :data-cindex="-1"
+              :data-rindex="rowHeader[index+dateArr.length]"
+              :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(index+dateArr.length) ? `left: ${(index+dateArr.length- minCol+1)*getColumnHeaderWidth(index+dateArr.length)}px;` : ''"
+              :class="'head-column', [([2,3,5].includes(column[0].DataType)&&column[0].ShowValue)?'insert': '', TableInfo.ExtraConfig.TableFreeze && isWithinColRange(index+dateArr.length) ? 'fix-col' : '']"
+              @click="clickCell($event,column[0])"
+              @dblclick="dblClickCell($event,column[0])"
+              @copy="copyCellHandle($event,column[0])"
+              @paste="pasteCellHandle($event,column[0])"
+            >
+              <span 
+                :data-cindex="-1"
+                :data-rindex="rowHeader[index+dateArr.length]"
+              > {{column[0].ShowValue}}</span>
+            </td>
+          </tr>
+
+
+          <!-- 指标行 -->
+          <tr v-for="(edb, edb_index) in TableInfo.TableData.Data" :key="edb.EdbInfoId" :style="TableInfo.ExtraConfig.TableFreeze ? `top: ${(edb_index- minRow+1)*tableHeight}px;` : ''" :class="TableInfo.ExtraConfig.TableFreeze && rowHeader[edb_index] >= minRow &&  rowHeader[edb_index] <= maxRow ? 'fix' : ''">
+            <!-- 行头 -->
+            <th 
+              class="th-tg th-row sm"
+              :data-cindex="columnHeader[edb_index]"
+              :data-rindex="-1"
+              @contextmenu.prevent="rightClickHandle"
+            >
+              {{columnHeader[edb_index]}}
+            </th>
+            
+            <!-- 名称 单位 -->
+            <td 
+              v-for="(item, index) in EdbKeys" 
+              :key="index"
+              :data-rindex="-1"
+              :data-cindex="columnHeader[edb_index]"
+              @click="() => { !disabled && item==='EdbName' && clickEdbName(edb)}"
+            >
+              <template v-if="item === 'EdbName'">
+                <span :class="{'edbname-td':disabled}" @click="edbJumpToBase(edb)">{{ edb.EdbAliasName||(edb[item]) }}</span>
+              </template>
+              <template v-else>{{ edb[item] }}/{{edb.Frequency }}</template>
+            </td>
+
+            <!-- 数据列 -->
+            <td
+              v-for="(data, data_index) in edb.Data"
+              :key="`${edb.EdbInfoId}_${rowHeader[data_index]}_${columnHeader[edb_index]}`"
+              :data-rindex="rowHeader[data_index]"
+              :data-cindex="columnHeader[edb_index]"
+              :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(data_index) ? `left: ${(data_index- minCol+1)*getColumnHeaderWidth(data_index)}px;` : ''"
+              :class="[([2,3,5].includes(data.DataType)&&data.ShowValue)?'insert': '', TableInfo.ExtraConfig.TableFreeze && isWithinColRange(data_index) ? 'fix-col' : '']"
+              @click="clickCell($event,data)"
+              @dblclick="dblClickCell($event,data)"
+              @copy="copyCellHandle($event,data)"
+              @paste="pasteCellHandle($event,data)"
+            >
+              <!-- 实际值/插值 -->
+              <span
+                :data-rindex="rowHeader[data_index]"
+                :data-cindex="columnHeader[edb_index]"
+              >
+                {{data.ShowValue || '-'}}
+              </span>
+            </td>
+
+            <!-- 文本列 -->
+            <td
+              v-for="(column,column_index) in TableInfo.TableData.TextRowData"
+              :key="`${rowHeader[column_index+dateArr.length]}_${columnHeader[edb_index]}`"
+              :data-rindex="rowHeader[column_index+dateArr.length]"
+              :data-cindex="columnHeader[edb_index]"
+              :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(column_index+dateArr.length) ? `left: ${(column_index+dateArr.length- minCol+1)*getColumnHeaderWidth(column_index+dateArr.length)}px;` : ''"
+              :class="[([2,3,5].includes(column[edb_index+1].DataType)&&column[edb_index+1].ShowValue)?'insert': '', TableInfo.ExtraConfig.TableFreeze && isWithinColRange(column_index+dateArr.length) ? 'fix-col' : '']"
+              @click="clickCell($event,column[edb_index+1])"
+              @dblclick="dblClickCell($event,column[edb_index+1])"
+              @copy="copyCellHandle($event,column[edb_index+1])"
+              @paste="pasteCellHandle($event,column[edb_index+1])"
+            >
+              <span 
+                :data-rindex="rowHeader[column_index+dateArr.length]"
+                :data-cindex="columnHeader[edb_index]"
+              > {{column[edb_index+1].ShowValue}}</span>
+              />
+            </td>
+      
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <div class="tool sheet-bottom">
+      <div class="sheet-source" 
+        v-if="TableInfo.SourcesFrom&&JSON.parse(TableInfo.SourcesFrom).isShow"
+        :style="`
+          color: ${ JSON.parse(TableInfo.SourcesFrom).color };
+          font-size: ${ JSON.parse(TableInfo.SourcesFrom).fontSize }px;
+        `"
+      >
+          source:<em>{{ JSON.parse(TableInfo.SourcesFrom).text}}</em>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang='scss' scoped>
+// sheet-show-wrapper 相关样式
+.sheet-show-wrapper {
+  max-width: 1200px;
+  overflow: hidden;
+  position: relative;
+  margin: 0 auto;
+  background: #fff;
+  // table-wrapper 样式
+  .table-wrapper {
+    max-width: calc(100vw - 20px);
+    max-height: calc(100vh - 400px);
+    margin: 0 auto;
+    overflow: auto;
+    -webkit-overflow-scrolling: touch; /* ios滚动条 */
+  }
+  .tool {
+    margin-top: 5px;
+    span {
+      cursor: pointer;
+    }
+  }
+
+  .sheet-bottom {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    white-space: nowrap;
+    padding: 0 10px;
+
+    .sheet-source {
+      width: 30%;
+      min-width: 150px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  }
+}
+
+// 表格相关样式
+.table {
+  width: 100%;
+  font-size: 14px;
+  color: #333;
+
+  td, th {
+    width: 104px;
+    min-width: 104px;
+    height: 35px;
+    background: #fff;
+    text-align: center;
+    word-break: break-all;
+    border: none;
+    outline-color: #dcdfe6;
+    outline-style: solid;
+    outline-width: 1px;
+    word-wrap: break-word;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    position: relative;
+
+    &:first-child {
+      border-left: 1px solid #dcdfe6;
+    }
+
+    &.td-chose::after {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      content: "";
+      display: block;
+      outline: 0;
+      border: 2px solid #0033FF;
+      box-shadow: 0 0 5px rgba(73, 177, 249, .5);
+    }
+
+    &.insert {
+      background: #FFEFDD;
+    }
+
+    .edbname-td {
+      &:hover {
+        text-decoration: underline;
+      }
+    }
+
+    &.fix-col {
+      position: sticky;
+      left: 0;
+      z-index: 98; // 表格右键操作弹窗为99
+    }
+  }
+
+  .th-tg {
+    background: #EBEEF5;
+
+    &:hover {
+      cursor: pointer;
+      background: #ddd;
+    }
+
+    &.sm {
+      width: 36px;
+      min-width: 36px;
+      max-width: 36px;
+    }
+  }
+
+  .data-cell {
+    color: #333;
+
+    &.one-bg {
+      background-color: #EFEEF1;
+    }
+
+    &.two-bg {
+      background-color: #fff;
+    }
+  }
+
+  .thead-sticky {
+    position: sticky;
+    top: 0;
+  }
+
+  .head-column {
+    background-color: #505B78;
+    color: #fff;
+  }
+
+  .split-word {
+    span {
+      display: inline;
+    }
+  }
+}
+
+tr {
+  &.fix {
+    position: sticky;
+    top: 0;
+    z-index: 98; // 表格右键操作弹窗为99
+  }
+}
+
+// 滚动条样式
+::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
+}
+
+::-webkit-scrollbar-track {
+  background: rgb(239, 239, 239);
+  border-radius: 2px;
+}
+
+::-webkit-scrollbar-thumb {
+  background: #ccc;
+  border-radius: 10px;
+
+  &:hover {
+    background: #888;
+  }
+}
+
+::-webkit-scrollbar-corner {
+  background: #666;
+}
+
+// PC端表格样式
+.pc-sty table {
+  table-layout: auto;
+
+  td, th {
+    width: auto;
+    height: auto;
+    padding: 0.4em 0;
+  }
+}
+
+// 移动端表格样式
+.mobile-sty table {
+  table-layout: auto;
+
+  td, th {
+    min-width: 120px;
+    height: 40px;
+  }
+}
+
+// 背景水印样式
+.background-watermark {
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: 100%;
+}
+
+@media screen and (min-width: 650px) {
+  .sheet-show-wrapper {
+    .table-wrapper {
+      max-height: calc(100vh - 220px);
+    }
+
+    .tool {
+      margin-top: 3px;
+    }
+
+    .sheet-bottom {
+      padding: 0 5px;
+      .sheet-source {
+        width: 15%;
+        min-width: 75px;
+      }
+    }
+  }
+
+  // 表格相关样式
+  .table {
+    width: 100%;
+    font-size: 7px;
+    color: #333;
+
+    td, th {
+      width: 52px;
+      min-width: 52px;
+      height: 17px;
+    }
+    .th-tg {
+      &.sm {
+        width: 18px;
+        min-width: 18px;
+        max-width: 18px;
+      }
+    }
+  }
+  // 滚动条样式
+  ::-webkit-scrollbar {
+    width: 3px;
+    height: 3px;
+  }
+
+  ::-webkit-scrollbar-track {
+    background: rgb(239, 239, 239);
+    border-radius: 1px;
+  }
+
+  ::-webkit-scrollbar-thumb {
+    background: #ccc;
+    border-radius: 5px;
+  }
+  // 移动端表格样式
+  .mobile-sty table {
+    td, th {
+      min-width: 60px;
+      height: 20px;
+    }
+  }
+}
+</style>

+ 9 - 2
src/views/sheetList/sharedDetail.vue

@@ -7,6 +7,9 @@ import { useWindowSize } from '@vueuse/core'
 import { showToast, showDialog  } from "vant";
 import {usePublicSettingStore} from '@/store/modules/publicSetting'
 import {etaTablePermission,useAuthBtn} from '@/hooks/useAuthBtn'
+import TimelineSheet from './components/TimelineSheet.vue';
+import MixedSheet from './components/MixedSheet.vue';
+import SharedSheet from './components/SharedSheet.vue';
 const {checkAuthBtn} = useAuthBtn()
 const publicSettingStore = usePublicSettingStore()
 // import { useDownLoadFile } from '@/hooks/useDownLoadFile'
@@ -24,6 +27,7 @@ const sheetActions = computed(() => {
         { label: globalProperties.$t('shared_table.download'), types:'download', src: getStaticImg('table/download.png'), isAuth: checkAuthBtn(authList.download) },
         { label: globalProperties.$t('shared_table.delete'), types:'delete', src: getStaticImg('table/delete.png'), isAuth: queryData.value.Button && queryData.value.Button.DeleteButton && checkAuthBtn(authList.delete)},
         // 若有需要,可以添加其他操作项
+        { label: globalProperties.$t('shared_table.refresh'), types:'flushed', src: getStaticImg('table/flushed.png'), isAuth: etaTablePermission.etaTable_customize_balance_refresh},
     ];
 });
 
@@ -234,7 +238,10 @@ function Base64() {
             </div>
         </div>
         <div class="sheet-box" v-if="queryData.UniqueCode">
-            <iframe v-if="link" :src="link + '/sheetshow?code=' + queryData.UniqueCode" frameborder="0" width="100%" height="100%"></iframe>
+            <!-- <iframe v-if="link && queryData.Source === 1" :src="link + '/sheetshow?code=' + queryData.UniqueCode" frameborder="0" width="100%" height="100%"></iframe> -->
+            <SharedSheet v-if="queryData.Source === 1" :TableInfo="queryData"></SharedSheet>
+            <TimelineSheet v-if="queryData.Source === 2" :TableInfo="queryData"></TimelineSheet>
+            <MixedSheet v-if="queryData.Source === 3" :TableInfo="queryData"></MixedSheet>
         </div>
         <div class="bottom">
             <template v-for="item in sheetActions" :key="item.types">
@@ -298,7 +305,7 @@ function Base64() {
     }
     .sheet-box{
         width: 100%;
-        height: calc(100vh - 400px);
+        // height: calc(100vh - 400px);
     }
     .sheet-more-action-wrap{
         .head-box{

+ 2 - 2
src/views/sheetList/sharedSearch.vue

@@ -17,7 +17,7 @@ const listState = reactive({
     loading:false
 })
 async function getList(){
-    const res=await apiSheet.sheetList({
+    const res=await apiSheet.sheetListEs({
         CurrentIndex: listState.page,
         PageSize: listState.pageSize,
         Keyword: keyword.value,
@@ -86,7 +86,7 @@ function goDetail(item){
         >
             <ul class="list-wrap">
                 <li class="item" v-for="item in listState.list" :key="item.ChartInfoId" @click="goDetail(item)">
-                    <div class="van-ellipsis name">{{item.ExcelName}}</div>
+                    <div class="van-ellipsis name" v-html="item.SearchText"></div>
                     <img class="img" :src="!item.HaveOperaAuth?useNoAuth().noAuthImg:item.ExcelImage" alt="">
                     <div class="time">
                         <span>{{moment(item.CreateTime).format('YYYY-MM-DD')}}</span>

+ 3 - 3
src/views/tabbar/Home.vue

@@ -195,9 +195,9 @@ const topImg=computed(()=>{
 
 const menuConfig=[
     {
-        name:'战研中心研报',
+        name:'国事研报',
         des:'全系统各类分析报告一体化展示平台',
-        key:'战研中心研报',
+        key:'国事研报',
         type:'zh',
         level:1,
         path:'/war_research_report/list',
@@ -437,7 +437,7 @@ getSetTitle()
                 :key="item.name"
                 :class="['item-box',
                     item.type==='en'?'item-box_en':'',
-                    item.key==='战研中心研报'&&'full-item'
+                    item.key==='国事研报'&&'full-item'
                 ]"
                 :style="{backgroundColor:item.backgroundColor}"
                 @click="goNext(item.path)"

+ 16 - 7
src/views/tabbar/User.vue

@@ -36,16 +36,16 @@ getSetTitle()
         </div>
         <div class="info-list">
             <div class="item">
-                <span>{{ $t('my.company_name') }}:</span>
-                <span>{{ CompanyName }}</span>
+                <div class="label">{{ $t('my.company_name') }}:</div>
+                <div class="content">{{ CompanyName }}</div>
             </div>
             <div class="item">
-                <span>{{ $t('my.department') }}:</span>
-                <span>{{userInfo.GroupName || userInfo.DepartmentName}}</span>
+                <div class="label">{{ $t('my.department') }}:</div>
+                <div class="content">{{ userInfo.DepartmentName + (userInfo.GroupName ? '/' : '') + userInfo.GroupName}}</div>
             </div>
             <div class="item">
-                <span>{{ $t('my.role') }}:</span>
-                <span>{{userInfo.RoleName}}</span>
+                <div class="label">{{ $t('my.role') }}:</div>
+                <div class="content">{{userInfo.RoleName}}</div>
             </div>
         </div>
         <van-button 
@@ -82,9 +82,18 @@ getSetTitle()
 .info-list{
     padding: 40px;
     .item{
-        padding: 30px 0;
+        line-height: 60px;
+        padding: 15px 0;
         border-bottom: 1px solid $border-color;
         color: $font-grey;
+        .label {
+            font-size: 24px;
+            color: $font-grey_999;
+        }
+        .content {
+            font-size: 28px;
+            color: #333333;
+        }
     }
 }
 .login_out_btn{

Some files were not shown because too many files changed in this diff