previewTable.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <script setup lang="ts">
  2. import { ref, provide, onMounted } from 'vue'
  3. import { post, get } from "@/utils/request";
  4. import { GET_RES_BY_FORM_JSON } from '../api';
  5. import type { TableColumnCtx, SummaryMethod } from 'element-plus'
  6. import rangeFilter from "./rangeFilter.vue";
  7. import { rangeGetSql } from '../function';
  8. import { formatDate } from '@/utils/times';
  9. const rangeFilterRef = ref<InstanceType<typeof rangeFilter> | null>(null);
  10. const SQLstr = ref<string>('')
  11. const driginalDataTableList = ref<any[]>([])
  12. const addEditData = JSON.parse(sessionStorage.getItem('reportJson') || '{}')
  13. const tableHeader = ref<any[]>([])
  14. const tableData = ref<any[]>([])
  15. const tableLoading = ref(false)
  16. const dataRangeData = ref<any[]>([])
  17. const dataRangeDataVisable = ref(false)
  18. const listThatNeedsToBeGrouped = ref<number[]>([]) // 分组索引
  19. const listStatisticsList = ref<number[]>([]) // 统计索引
  20. const tableKey = ref<number>(1)
  21. // 统计
  22. const getSummaries: SummaryMethod<any> = (params) => {
  23. const { columns, data } = params
  24. const sums: (string)[] = []
  25. columns.forEach((column, index) => {
  26. if (index === 0) {
  27. sums[index] = '合计'
  28. return
  29. }
  30. const values = data.map(item => Number(item[column.property as string]))
  31. if (!values.every(value => isNaN(value)) && listStatisticsList.value.includes(index)) {
  32. const total = values.reduce((prev, curr) => {
  33. const value = Number(curr)
  34. if (!isNaN(value)) {
  35. return prev + curr
  36. } else {
  37. return prev
  38. }
  39. }, 0)
  40. sums[index] = total.toFixed(2) // 保证返回 string,解决类型报错
  41. } else {
  42. sums[index] = ''
  43. }
  44. })
  45. return sums
  46. }
  47. // 合并分组
  48. function mergeColumns({ row, rowIndex, columnIndex }: { row: any, rowIndex: number, columnIndex: number }) {
  49. // 只处理需要合并的列
  50. if (!listThatNeedsToBeGrouped.value.includes(columnIndex)) {
  51. return { rowspan: 1, colspan: 1 };
  52. }
  53. const field = tableHeader.value[columnIndex]?.label;
  54. if (!field) return { rowspan: 1, colspan: 1 };
  55. // 当前行之前的内容已经合并过,直接跳过
  56. if (rowIndex > 0 && tableData.value[rowIndex - 1][field] === row[field]) {
  57. return { rowspan: 0, colspan: 0 }; // 隐藏
  58. }
  59. // 计算当前单元格应该合并多少行
  60. let rowspan = 1;
  61. for (let i = rowIndex + 1; i < tableData.value.length; i++) {
  62. if (tableData.value[i][field] === row[field]) {
  63. rowspan++;
  64. } else {
  65. break;
  66. }
  67. }
  68. return { rowspan, colspan: 1 };
  69. }
  70. function determinationOfDataRange() {
  71. const rangeFilterData = rangeFilterRef.value?.getRangeData()
  72. const rangeSQL = rangeGetSql(rangeFilterData, addEditData)
  73. const sqlList = SQLstr.value.split('where')[0]
  74. SQLstr.value = `${sqlList}${rangeSQL}`
  75. dataRangeDataVisable.value = false
  76. getData()
  77. }
  78. function setDataRange() {
  79. dataRangeDataVisable.value = true
  80. setTimeout(() => {
  81. rangeFilterRef.value?.setRangeData(dataRangeData.value)
  82. }, 100)
  83. }
  84. function getData() {
  85. tableLoading.value = true
  86. post(GET_RES_BY_FORM_JSON, { formJson: SQLstr.value }).then(res => {
  87. tableData.value = res.data
  88. setTimeout(() => {
  89. tableKey.value++
  90. }, 100)
  91. }).finally(() => {
  92. tableLoading.value = false
  93. })
  94. }
  95. async function initializedData(SQL: string, rangeFilterList: any[], tableData: any[]) {
  96. await filterGroupStatistics(tableData)
  97. const heading = tableData.map(item => ({
  98. filed: `${item.tableName}_${item.columnName}`,
  99. label: item.columnComment,
  100. type: item.dataType,
  101. }))
  102. driginalDataTableList.value = tableData
  103. tableHeader.value = heading
  104. dataRangeData.value = rangeFilterList
  105. SQLstr.value = SQL
  106. console.log(tableHeader.value, '<==== tableHeader.value')
  107. getData()
  108. }
  109. // 过滤出需要分组和统计的数组
  110. function filterGroupStatistics(tableData: any[]) {
  111. return new Promise<void>((resolve) => {
  112. const numberList = tableData.map((item: any, index: number) => item?.singleColumnOperation?.grouping ? index : 9999)
  113. const statisticsList = tableData.map((item: any, index: number) => item?.singleColumnOperation?.statistics ? index : 9999)
  114. listThatNeedsToBeGrouped.value = numberList.filter((item: any) => item != 9999)
  115. listStatisticsList.value = statisticsList.filter((item: any) => item != 9999)
  116. resolve()
  117. })
  118. }
  119. defineExpose({
  120. initializedData
  121. });
  122. onMounted(() => { })
  123. </script>
  124. <template>
  125. <div class="h-full flex flex-col">
  126. <div class="flex items-center mb-4 h-[5%]">
  127. <el-button type="primary" @click="setDataRange()">设置数据范围</el-button>
  128. </div>
  129. <div class="flex-1 h-[92%]">
  130. <el-table :data="tableData" border style="width: 100%;height: 100%" v-loading="tableLoading" :span-method="mergeColumns" :summary-method="getSummaries" :show-summary="listStatisticsList.length > 0 ? true : false" :key="tableKey">
  131. <el-table-column :prop="item.label" :label="item.label" min-width="240" v-for="(item, index) in tableHeader">
  132. <template #default="scope">
  133. <template v-if="item.type == 'timestamp' || item.type == 'datetime' || item.type == 'timestamp' || item.type == 'date'">
  134. {{ scope.row[item.label] ? formatDate(new Date(scope.row[item.label])) : '' }}
  135. </template>
  136. <template v-else>
  137. {{ scope.row[item.label] }}
  138. </template>
  139. </template>
  140. </el-table-column>
  141. </el-table>
  142. </div>
  143. <!-- 数据范围弹框 -->
  144. <el-dialog v-model="dataRangeDataVisable" width="680" :show-close="false" top="10vh">
  145. <template #header="{ close, titleId, titleClass }">
  146. <div class="flex justify-between items-center border-b pb-3 dialog-header">
  147. <h4 :id="titleId">数据范围</h4>
  148. <div class="flex">
  149. <el-button @click="dataRangeDataVisable = false">取消</el-button>
  150. <el-button type="primary" @click="determinationOfDataRange()">确定</el-button>
  151. </div>
  152. </div>
  153. </template>
  154. <div class="p-8">
  155. <rangeFilter ref="rangeFilterRef" />
  156. </div>
  157. </el-dialog>
  158. </div>
  159. </template>
  160. <style lang="scss"></style>