Browse Source

Merge branch 'ch/ETA_2.4.1' of eta_front/eta_mobile_front into debug

leichen 1 month ago
parent
commit
61b8152424

+ 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">

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

@@ -0,0 +1,603 @@
+<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 tableRef = 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 minRow = computed(() => {
+    return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
+  });
+  const maxRow = computed(() => {
+    return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
+  });
+  const maxCol = computed(() => {
+    return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndCol)
+  });
+  const minCol = computed(() => {
+    return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndCol)
+  });
+
+  // watch(
+  //   () => props.TableInfo.TableData.Data,  // 假设 config.data 是 props.TableInfo 下的一个属性
+  //   (newVal) => {
+  //     nextTick(() => {
+  //       tableHeight.value = tableRef.value ? tableRef.value.offsetHeight : 35;
+  //     });
+  //   },
+  //   { deep: true }
+  // );
+
+  //手机端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 = tableRef.value ? tableRef.value.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 ? cellRef.value.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 
+              :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' : ''">
+            <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 {
+  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;
+    }
+  }
+}
+</style>
+<style scoped lang="scss">
+.table 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;
+  word-break: break-all;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  position: relative;
+
+  &.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)
+  }
+  // &.td-col-select::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 1px solid rgb(24, 173, 24);
+  //   border-bottom: none;
+  //   border-top: none;
+  // }
+  // &.td-row-select::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 1px solid rgb(24, 173, 24);
+  //   border-left: none;
+  //   border-right: none;
+  // }
+  &.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;
+    /* border: 2px solid #409eff; */
+  }
+  &.sm {
+    width: 36px;
+    min-width: 36px;
+    max-width: 36px;
+  }
+}
+//整行选中
+tr {
+  // position: relative;
+  // &.choose-all::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 2px solid #5897fb;
+  //   box-shadow: 0 0 5px rgba(73, 177, 249, .5)
+  // }
+  &.fix {
+    position:sticky;
+    top: 0;
+    z-index: 98; // 表格右键操作弹窗为99
+  }
+} 
+
+.el-icon-sort {
+  color: #409eff;
+  cursor: pointer;
+  font-size: 16px;
+}
+
+</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;
+}
+table {
+  width: 100%;
+  font-size: 14px;
+  color: #333;
+  td,
+  th {
+    // min-width: 120px;
+    word-break: break-all;
+    word-wrap: break-word;
+    line-height: 1.2em;
+    border: 1px solid #dcdfe6;
+    // height: 40px;
+    text-align: center;
+    border-left: none;
+    border-top: none;
+    &:first-child {
+			border-left: 1px solid #dcdfe6;
+		}
+  }
+
+  .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; }
+  }
+}
+
+.no-water{
+  td,
+  th {
+    background-color: #fff;
+  }
+  .head-column {
+    background-color: #505B78;
+    color: #fff;
+  }
+}
+
+.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%;
+}
+</style>

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

@@ -0,0 +1,604 @@
+<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 tableRef = 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 minRow = computed(() => {
+    return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
+  });
+  const maxRow = computed(() => {
+    return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
+  });
+  const maxCol = computed(() => {
+    return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndCol)
+  });
+  const minCol = computed(() => {
+    return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndCol)
+  });
+
+  // watch(
+  //   () => props.TableInfo.TableData.Data,  // 假设 config.data 是 props.TableInfo 下的一个属性
+  //   (newVal) => {
+  //     nextTick(() => {
+  //       tableHeight.value = tableRef.value ? tableRef.value.offsetHeight : 35;
+  //     });
+  //   },
+  //   { deep: true }
+  // );
+
+  //手机端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 = tableRef.value ? tableRef.value.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 ? cellRef.value.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" ref="cellRef"></th> -->
+
+            <!-- 列头 -->
+            <th 
+              :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' : ''">
+            <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 {
+  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;
+    }
+  }
+}
+</style>
+<style scoped lang="scss">
+.table 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;
+  word-break: break-all;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  position: relative;
+
+  &.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)
+  }
+  // &.td-col-select::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 1px solid rgb(24, 173, 24);
+  //   border-bottom: none;
+  //   border-top: none;
+  // }
+  // &.td-row-select::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 1px solid rgb(24, 173, 24);
+  //   border-left: none;
+  //   border-right: none;
+  // }
+  &.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;
+    /* border: 2px solid #409eff; */
+  }
+  &.sm {
+    width: 36px;
+    min-width: 36px;
+    max-width: 36px;
+  }
+}
+//整行选中
+tr {
+  // position: relative;
+  // &.choose-all::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 2px solid #5897fb;
+  //   box-shadow: 0 0 5px rgba(73, 177, 249, .5)
+  // }
+  &.fix {
+    position:sticky;
+    top: 0;
+    z-index: 98; // 表格右键操作弹窗为99
+  }
+} 
+
+.el-icon-sort {
+  color: #409eff;
+  cursor: pointer;
+  font-size: 16px;
+}
+
+</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;
+}
+table {
+  width: 100%;
+  font-size: 14px;
+  color: #333;
+  td,
+  th {
+    // min-width: 120px;
+    word-break: break-all;
+    word-wrap: break-word;
+    line-height: 1.2em;
+    border: 1px solid #dcdfe6;
+    // height: 40px;
+    text-align: center;
+    border-left: none;
+    border-top: none;
+    &:first-child {
+			border-left: 1px solid #dcdfe6;
+		}
+  }
+
+  .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; }
+  }
+}
+
+.no-water{
+  td,
+  th {
+    background-color: #fff;
+  }
+  .head-column {
+    background-color: #505B78;
+    color: #fff;
+  }
+}
+
+.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%;
+}
+</style>

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

@@ -0,0 +1,564 @@
+<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[0] ? props.TableInfo.TableData.Data[0].length : 0
+    );
+  });
+  const rowHeader = computed(() => {
+    let total_length = dateArr.value.length +  props.TableInfo.TableData.TextRowData.length;
+    return getRowHeaderCode(total_length);
+  });
+  const minRow = computed(() => {
+    return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
+  });
+  const maxRow = computed(() => {
+    return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
+  });
+  const maxCol = computed(() => {
+    return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndCol)
+  });
+  const minCol = computed(() => {
+    return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.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);
+    
+    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.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 
+              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">
+
+            <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' : ''">
+
+            <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' : ''">
+
+            <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;">
+        <tbody>
+          <tr>
+            <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' : ''">
+
+            <!-- 名称 单位 -->
+            <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 {
+  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;
+    }
+  }
+}
+</style>
+<style scoped lang="scss">
+.table 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;
+  word-break: break-all;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  position: relative;
+
+  &.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)
+  }
+  // &.td-col-select::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 1px solid rgb(24, 173, 24);
+  //   border-bottom: none;
+  //   border-top: none;
+  // }
+  // &.td-row-select::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 1px solid rgb(24, 173, 24);
+  //   border-left: none;
+  //   border-right: none;
+  // }
+  &.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;
+    /* border: 2px solid #409eff; */
+  }
+  &.sm {
+    width: 36px;
+    min-width: 36px;
+    max-width: 36px;
+  }
+}
+//整行选中
+tr {
+  // position: relative;
+  // &.choose-all::after {
+  //   position: absolute;
+  //   top: 0;
+  //   left: 0;
+  //   right: 0;
+  //   bottom: 0;
+  //   content: "";
+  //   display: block;
+  //   outline: 0;
+  //   border: 2px solid #5897fb;
+  //   box-shadow: 0 0 5px rgba(73, 177, 249, .5)
+  // }
+  &.fix {
+    position:sticky;
+    top: 0;
+    z-index: 98; // 表格右键操作弹窗为99
+  }
+} 
+
+.el-icon-sort {
+  color: #409eff;
+  cursor: pointer;
+  font-size: 16px;
+}
+
+</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;
+}
+table {
+  width: 100%;
+  font-size: 14px;
+  color: #333;
+  td,
+  th {
+    // min-width: 120px;
+    word-break: break-all;
+    word-wrap: break-word;
+    line-height: 1.2em;
+    border: 1px solid #dcdfe6;
+    // height: 40px;
+    text-align: center;
+    border-left: none;
+    border-top: none;
+    &:first-child {
+			border-left: 1px solid #dcdfe6;
+		}
+  }
+
+  .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; }
+  }
+}
+
+.no-water{
+  td,
+  th {
+    background-color: #fff;
+  }
+  .head-column {
+    background-color: #505B78;
+    color: #fff;
+  }
+}
+
+.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%;
+}
+</style>

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

@@ -7,6 +7,8 @@ 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';
 const {checkAuthBtn} = useAuthBtn()
 const publicSettingStore = usePublicSettingStore()
 // import { useDownLoadFile } from '@/hooks/useDownLoadFile'
@@ -24,6 +26,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 +237,9 @@ 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>
+            <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 +303,7 @@ function Base64() {
     }
     .sheet-box{
         width: 100%;
-        height: calc(100vh - 400px);
+        // height: calc(100vh - 400px);
     }
     .sheet-more-action-wrap{
         .head-box{