TimelineSheet.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <script setup>
  2. // import Sheet from '@/components/Sheet.vue'
  3. import { ref, computed, onMounted } from 'vue'
  4. const props = defineProps({
  5. TableInfo:{
  6. type:Object,
  7. default:{}
  8. }
  9. })
  10. const tableHeight = ref(0)
  11. const rowTable = ref(null)
  12. const disabled = ref(true)
  13. const EdbKeys = ref(["EdbName", "Unit"])
  14. const cellRef = ref(null)
  15. const dateArr = computed (() => {
  16. return props.TableInfo.TableData.Data.length ? props.TableInfo.TableData.Data[0].Data.map(_ => _.DataTime) : []
  17. })
  18. const columnHeader = computed(() => {
  19. return getColumnHeaderCode(
  20. props.TableInfo.TableData.Data[0] ? props.TableInfo.TableData.Data[0].length : 0
  21. );
  22. });
  23. const rowHeader = computed(() => {
  24. let total_length = dateArr.value.length + props.TableInfo.TableData.TextRowData.length;
  25. return getRowHeaderCode(total_length);
  26. });
  27. const minRow = computed(() => {
  28. return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
  29. });
  30. const maxRow = computed(() => {
  31. return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartRow, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndRow)
  32. });
  33. const maxCol = computed(() => {
  34. return Math.max(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndCol)
  35. });
  36. const minCol = computed(() => {
  37. return Math.min(props.TableInfo.ExtraConfig.TableFreeze.FreezeStartCol, props.TableInfo.ExtraConfig.TableFreeze.FreezeEndCol)
  38. });
  39. //手机端pc端不同样式
  40. const dynamicSty = computed(()=>{
  41. return isMobile() ? 'mobile-sty' : 'pc-sty';
  42. })
  43. //判断是否是手机设备
  44. function isMobile() {
  45. // 判断是否是移动设备的正则表达式
  46. const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
  47. // 获取用户代理信息
  48. const userAgent = navigator.userAgent;
  49. // 使用正则表达式检查用户代理信息
  50. return mobileRegex.test(userAgent);
  51. }
  52. // 字母列标
  53. function getColumnHeaderCode(len) {
  54. let tag_arr = [];
  55. for(let i=0;i<len;i++) tag_arr.push(String.fromCharCode(65+i));
  56. return tag_arr;
  57. }
  58. // 行标
  59. function getRowHeaderCode(len) {
  60. let tag_arr = [];
  61. for(let i=0;i<len;i++) tag_arr.push(String(1+i));
  62. return tag_arr;
  63. }
  64. // 判断展示小数位数值还是原来的值
  65. function showCellValue(cell){
  66. // console.log(cell)
  67. let Value=''
  68. if("Decimal" in cell&&cell.Decimal!=-1){
  69. const multiplier = Math.pow(10, cell.Decimal);
  70. const cellValue=+cell.Value
  71. Value= cell.Decimal == 0 ? Math.round(cellValue) : Math.round(cellValue * multiplier) / multiplier;
  72. }else{
  73. Value=cell.ShowValue
  74. }
  75. return Value
  76. }
  77. onMounted(() => {
  78. console.log('rowTable', rowTable.value);
  79. tableHeight.value = rowTable.value ? rowTable.value.offsetHeight : 35;
  80. })
  81. // 是否固定列
  82. function isWithinColRange (index) {
  83. return rowHeader.value[index] >= minCol.value && rowHeader.value[index] <= maxCol.value
  84. }
  85. // 获取某一列的宽度
  86. function getColumnHeaderWidth (index) {
  87. return cellRef.value && cellRef.value ? cellRef.value.offsetWidth : 104
  88. }
  89. </script>
  90. <template>
  91. <div class="sheet-show-wrapper">
  92. <div :class="['table-wrapper',dynamicSty ]" v-if="props.TableInfo.ExcelType === 1">
  93. <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''" style="position: relative;">
  94. <thead>
  95. <tr ref="rowTable">
  96. <!-- 列头 -->
  97. <th
  98. ref="cellRef"
  99. v-for="(item, index) in columnHeader"
  100. :key="index"
  101. class="th-tg th-col"
  102. :data-cindex="item"
  103. :data-rindex="-1">
  104. {{item}}
  105. </th>
  106. </tr>
  107. </thead>
  108. <tbody>
  109. <tr v-for="(item, index) in EdbKeys" :key="item">
  110. <td rowspan="2" v-if="index === 0" class="head-column">
  111. 日期
  112. </td>
  113. <td
  114. v-for="(edb, sub_index) in TableInfo.TableData.Data"
  115. :key="sub_index"
  116. :data-rindex="-1"
  117. :data-cindex="columnHeader[sub_index]"
  118. :class="['data-cell',{
  119. 'one-bg':(index+1)%2&&index>0,
  120. 'tow-bg': (index+1)%2!==0&&index>0,
  121. 'head-column': index === 0,
  122. }]"
  123. >
  124. <template v-if="item === 'EdbName'">
  125. <span :class="{'edbname-td':disabled}" @click="edbJumpToBase(edb)">{{ edb.EdbAliasName||edb[item] }}</span>
  126. </template>
  127. <template v-else>{{ edb[item] }} / {{edb.Frequency }}</template>
  128. </td>
  129. </tr>
  130. <!-- 数据行 第一列日期-->
  131. <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' : ''">
  132. <td
  133. :data-rindex="rowHeader[dateIndex]"
  134. :data-cindex="-1"
  135. :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(dateIndex) ? `left: 0px;` : ''"
  136. :class="[TableInfo.ExtraConfig.TableFreeze && isWithinColRange(dateIndex) ? 'fix-col' : '']"
  137. >{{date}}</td>
  138. <td
  139. v-for="(edb, edb_index) in TableInfo.TableData.Data"
  140. :key="edb_index"
  141. :data-rindex="rowHeader[dateIndex]"
  142. :data-cindex="columnHeader[edb_index]"
  143. :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(edb_index+dateArr.length) ? `left: ${(edb_index+dateArr.length- minCol+1)*getColumnHeaderWidth(edb_index+dateArr.length)}px;` : ''"
  144. :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' : '']"
  145. >
  146. <!-- 实际值/插值 -->
  147. <span
  148. :data-rindex="rowHeader[dateIndex]"
  149. :data-cindex="columnHeader[edb_index]"
  150. v-if="(edb.Data.find(_ =>_.DataTime === date)?.ShowValue&&!edb.Data.find(_ =>_.DataTime === date).CanEdit)||disabled"
  151. >
  152. {{showCellValue(edb.Data.find(_ =>_.DataTime === date)) || '-'}}
  153. </span>
  154. </td>
  155. </tr>
  156. <!-- 文本行 -->
  157. <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' : ''">
  158. <td
  159. v-for="(cell, cell_index) in row"
  160. :key="`${index}_${cell_index}`"
  161. :data-rindex="rowHeader[index+dateArr.length]"
  162. :data-cindex="cell_index===0?-1:columnHeader[cell_index-1]"
  163. :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(cell_index+dateArr.length) ? `left: ${(cell_index+dateArr.length- minCol+1)*getColumnHeaderWidth(cell_index+dateArr.length)}px;` : ''"
  164. :class="[([2,3,5].includes(cell.DataType)&&cell.ShowValue)?'insert': '', TableInfo.ExtraConfig.TableFreeze && isWithinColRange(cell_index+dateArr.length) ? 'fix-col' : '']"
  165. >
  166. <span
  167. :data-rindex="rowHeader[index+dateArr.length]"
  168. :data-cindex="cell_index===0?-1:columnHeader[cell_index-1]"
  169. v-if="(cell.ShowValue&&!cell.CanEdit)||disabled"
  170. > {{cell.ShowValue}}</span>
  171. </td>
  172. </tr>
  173. </tbody>
  174. </table>
  175. </div>
  176. <div :class="['table-wrapper',dynamicSty ]" v-else>
  177. <table width="auto" border="0" class="table" :style="disabled ? 'width:100%':''" style="position: relative;">
  178. <tbody>
  179. <tr>
  180. <td colspan="2" class="head-column">
  181. 日期
  182. </td>
  183. <!-- 日期列 -->
  184. <td
  185. v-for="(date, sub_index) in dateArr"
  186. :key="date"
  187. :data-rindex="rowHeader[sub_index]"
  188. :data-cindex="-1"
  189. :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(sub_index) ? `left: ${(sub_index- minCol+1)*getColumnHeaderWidth(sub_index)}px;` : ''"
  190. :class="['data-cell','head-column',
  191. TableInfo.ExtraConfig.TableFreeze && isWithinColRange(sub_index) ? 'fix-col' : '']"
  192. >{{ date }}</td>
  193. <!-- 文本列 -->
  194. <td
  195. v-for="(column,index) in TableInfo.TableData.TextRowData"
  196. :key="index"
  197. :data-cindex="-1"
  198. :data-rindex="rowHeader[index+dateArr.length]"
  199. :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(index+dateArr.length) ? `left: ${(index+dateArr.length- minCol+1)*getColumnHeaderWidth(index+dateArr.length)}px;` : ''"
  200. :class="'head-column', [([2,3,5].includes(column[0].DataType)&&column[0].ShowValue)?'insert': '', TableInfo.ExtraConfig.TableFreeze && isWithinColRange(index+dateArr.length) ? 'fix-col' : '']"
  201. @click="clickCell($event,column[0])"
  202. @dblclick="dblClickCell($event,column[0])"
  203. @copy="copyCellHandle($event,column[0])"
  204. @paste="pasteCellHandle($event,column[0])"
  205. >
  206. <span
  207. :data-cindex="-1"
  208. :data-rindex="rowHeader[index+dateArr.length]"
  209. > {{column[0].ShowValue}}</span>
  210. </td>
  211. </tr>
  212. <!-- 指标行 -->
  213. <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' : ''">
  214. <!-- 名称 单位 -->
  215. <td
  216. v-for="(item, index) in EdbKeys"
  217. :key="index"
  218. :data-rindex="-1"
  219. :data-cindex="columnHeader[edb_index]"
  220. @click="() => { !disabled && item==='EdbName' && clickEdbName(edb)}"
  221. >
  222. <template v-if="item === 'EdbName'">
  223. <span :class="{'edbname-td':disabled}" @click="edbJumpToBase(edb)">{{ edb.EdbAliasName||(edb[item]) }}</span>
  224. </template>
  225. <template v-else>{{ edb[item] }}/{{edb.Frequency }}</template>
  226. </td>
  227. <!-- 数据列 -->
  228. <td
  229. v-for="(data, data_index) in edb.Data"
  230. :key="`${edb.EdbInfoId}_${rowHeader[data_index]}_${columnHeader[edb_index]}`"
  231. :data-rindex="rowHeader[data_index]"
  232. :data-cindex="columnHeader[edb_index]"
  233. :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(data_index) ? `left: ${(data_index- minCol+1)*getColumnHeaderWidth(data_index)}px;` : ''"
  234. :class="[([2,3,5].includes(data.DataType)&&data.ShowValue)?'insert': '', TableInfo.ExtraConfig.TableFreeze && isWithinColRange(data_index) ? 'fix-col' : '']"
  235. @click="clickCell($event,data)"
  236. @dblclick="dblClickCell($event,data)"
  237. @copy="copyCellHandle($event,data)"
  238. @paste="pasteCellHandle($event,data)"
  239. >
  240. <!-- 实际值/插值 -->
  241. <span
  242. :data-rindex="rowHeader[data_index]"
  243. :data-cindex="columnHeader[edb_index]"
  244. >
  245. {{data.ShowValue || '-'}}
  246. </span>
  247. </td>
  248. <!-- 文本列 -->
  249. <td
  250. v-for="(column,column_index) in TableInfo.TableData.TextRowData"
  251. :key="`${rowHeader[column_index+dateArr.length]}_${columnHeader[edb_index]}`"
  252. :data-rindex="rowHeader[column_index+dateArr.length]"
  253. :data-cindex="columnHeader[edb_index]"
  254. :style="TableInfo.ExtraConfig.TableFreeze && isWithinColRange(column_index+dateArr.length) ? `left: ${(column_index+dateArr.length- minCol+1)*getColumnHeaderWidth(column_index+dateArr.length)}px;` : ''"
  255. :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' : '']"
  256. @click="clickCell($event,column[edb_index+1])"
  257. @dblclick="dblClickCell($event,column[edb_index+1])"
  258. @copy="copyCellHandle($event,column[edb_index+1])"
  259. @paste="pasteCellHandle($event,column[edb_index+1])"
  260. >
  261. <span
  262. :data-rindex="rowHeader[column_index+dateArr.length]"
  263. :data-cindex="columnHeader[edb_index]"
  264. > {{column[edb_index+1].ShowValue}}</span>
  265. />
  266. </td>
  267. </tr>
  268. </tbody>
  269. </table>
  270. </div>
  271. <div class="tool sheet-bottom">
  272. <div class="sheet-source"
  273. v-if="TableInfo.SourcesFrom&&JSON.parse(TableInfo.SourcesFrom).isShow"
  274. :style="`
  275. color: ${ JSON.parse(TableInfo.SourcesFrom).color };
  276. font-size: ${ JSON.parse(TableInfo.SourcesFrom).fontSize }px;
  277. `"
  278. >
  279. source:<em>{{ JSON.parse(TableInfo.SourcesFrom).text}}</em>
  280. </div>
  281. </div>
  282. </div>
  283. </template>
  284. <style lang='scss' scoped>
  285. .sheet-show-wrapper {
  286. max-width: 1200px;
  287. overflow: hidden;
  288. position: relative;
  289. margin: 0 auto;
  290. background: #fff;
  291. .tool{
  292. // text-align: right;
  293. margin-top: 5px;
  294. span{
  295. cursor: pointer;
  296. }
  297. }
  298. .sheet-bottom{
  299. display: flex;
  300. align-items: center;
  301. justify-content: space-between;
  302. white-space: nowrap;
  303. padding: 0 10px;
  304. .sheet-source{
  305. width: 30%;
  306. min-width: 150px;
  307. overflow: hidden;
  308. text-overflow: ellipsis;
  309. }
  310. .right-btns {
  311. display: flex;
  312. align-items: center;
  313. gap:15px;
  314. color: #666;
  315. }
  316. }
  317. }
  318. </style>
  319. <style scoped lang="scss">
  320. .table td,th {
  321. width: 104px;
  322. min-width: 104px;
  323. height: 35px;
  324. background: #fff;
  325. text-align: center;
  326. word-break: break-all;
  327. border: none;
  328. outline-color: #dcdfe6;
  329. outline-style: solid;
  330. outline-width: 1px;
  331. word-wrap: break-word;
  332. word-break: break-all;
  333. white-space: nowrap;
  334. overflow: hidden;
  335. text-overflow: ellipsis;
  336. position: relative;
  337. &.td-chose::after {
  338. position: absolute;
  339. top: 0;
  340. left: 0;
  341. right: 0;
  342. bottom: 0;
  343. content: "";
  344. display: block;
  345. outline: 0;
  346. border: 2px solid #0033FF;
  347. box-shadow: 0 0 5px rgba(73, 177, 249, .5)
  348. }
  349. // &.td-col-select::after {
  350. // position: absolute;
  351. // top: 0;
  352. // left: 0;
  353. // right: 0;
  354. // bottom: 0;
  355. // content: "";
  356. // display: block;
  357. // outline: 0;
  358. // border: 1px solid rgb(24, 173, 24);
  359. // border-bottom: none;
  360. // border-top: none;
  361. // }
  362. // &.td-row-select::after {
  363. // position: absolute;
  364. // top: 0;
  365. // left: 0;
  366. // right: 0;
  367. // bottom: 0;
  368. // content: "";
  369. // display: block;
  370. // outline: 0;
  371. // border: 1px solid rgb(24, 173, 24);
  372. // border-left: none;
  373. // border-right: none;
  374. // }
  375. &.insert {
  376. background: #FFEFDD;
  377. }
  378. .edbname-td {
  379. &:hover {
  380. text-decoration: underline;
  381. }
  382. }
  383. &.fix-col {
  384. position:sticky;
  385. left: 0;
  386. z-index: 98; // 表格右键操作弹窗为99
  387. }
  388. }
  389. .th-tg {
  390. background: #EBEEF5;
  391. &:hover {
  392. cursor: pointer;
  393. background: #ddd;
  394. /* border: 2px solid #409eff; */
  395. }
  396. &.sm {
  397. width: 36px;
  398. min-width: 36px;
  399. max-width: 36px;
  400. }
  401. }
  402. //整行选中
  403. tr {
  404. // position: relative;
  405. // &.choose-all::after {
  406. // position: absolute;
  407. // top: 0;
  408. // left: 0;
  409. // right: 0;
  410. // bottom: 0;
  411. // content: "";
  412. // display: block;
  413. // outline: 0;
  414. // border: 2px solid #5897fb;
  415. // box-shadow: 0 0 5px rgba(73, 177, 249, .5)
  416. // }
  417. &.fix {
  418. position:sticky;
  419. top: 0;
  420. z-index: 98; // 表格右键操作弹窗为99
  421. }
  422. }
  423. .el-icon-sort {
  424. color: #409eff;
  425. cursor: pointer;
  426. font-size: 16px;
  427. }
  428. </style>
  429. <style lang='scss' scoped>
  430. ::-webkit-scrollbar {
  431. width: 6px;
  432. height: 6px;
  433. }
  434. ::-webkit-scrollbar-track {
  435. background: rgb(239, 239, 239);
  436. border-radius: 2px;
  437. }
  438. ::-webkit-scrollbar-thumb {
  439. background: #ccc;
  440. border-radius: 10px;
  441. }
  442. ::-webkit-scrollbar-thumb:hover {
  443. background: #888;
  444. }
  445. ::-webkit-scrollbar-corner {
  446. background: #666;
  447. }
  448. .table-wrapper {
  449. max-width: calc(100vw - 20px);
  450. height: calc(100vh - 400px);
  451. margin: 0 auto;
  452. // margin-right: -5px;
  453. overflow: auto;
  454. }
  455. table {
  456. width: 100%;
  457. font-size: 14px;
  458. color: #333;
  459. td,
  460. th {
  461. // min-width: 120px;
  462. word-break: break-all;
  463. word-wrap: break-word;
  464. line-height: 1.2em;
  465. border: 1px solid #dcdfe6;
  466. // height: 40px;
  467. text-align: center;
  468. border-left: none;
  469. border-top: none;
  470. &:first-child {
  471. border-left: 1px solid #dcdfe6;
  472. }
  473. }
  474. .data-cell{
  475. color: #333;
  476. &.one-bg {
  477. background-color: #EFEEF1;
  478. }
  479. &.two-bg {
  480. background-color: #fff;
  481. }
  482. }
  483. .thead-sticky {
  484. position: sticky;
  485. top: 0;
  486. }
  487. .head-column {
  488. background-color: #505B78;
  489. color: #fff;
  490. }
  491. .split-word {
  492. span { display: inline; }
  493. }
  494. }
  495. .no-water{
  496. td,
  497. th {
  498. background-color: #fff;
  499. }
  500. .head-column {
  501. background-color: #505B78;
  502. color: #fff;
  503. }
  504. }
  505. .pc-sty table {
  506. table-layout: auto;
  507. td,th {
  508. width: auto;
  509. height: auto;
  510. padding: 0.4em 0;
  511. }
  512. }
  513. .mobile-sty table {
  514. table-layout: auto;
  515. td,th {
  516. min-width: 120px;
  517. height: 40px;
  518. }
  519. }
  520. .background-watermark{
  521. background-repeat: no-repeat;
  522. background-position: center center;
  523. background-size: 100%;
  524. }
  525. </style>