Ver Fonte

高级查询导出、统计图表导出excel

zhongxiaoyu há 2 anos atrás
pai
commit
e4b717b79c

+ 14 - 1
src/views/advancedQuery/views/advancedHome.vue

@@ -50,6 +50,10 @@
             class="btnAn"
             @click="dialogShow"
           >高级查询</button>
+          <button
+            class="btnAn"
+            @click="exportHandler('table', '高级查询结果')"
+          >导出</button>
           <button
             class="btnAn"
             @click="toNewAdvance"
@@ -343,7 +347,7 @@ import { parseTime } from '@/utils/index'
 import { Query, myQuery } from '@/api/dataIntegration'
 import { mapGetters } from 'vuex'
 import TableHeaderCell from '@/components/TableHeaderCell'
-import { setTableFilters } from '@/utils/table'
+import { setTableFilters, throttledExportToExcel } from '@/utils/table'
 
 export default {
   name: 'AdvancedHome',
@@ -1205,6 +1209,15 @@ export default {
       return function () {
         return ['合计', `共${num}件`]
       }
+    },
+    exportHandler(refName, tableName) {
+      if (!this.tableData.length) {
+        this.$message.info('无数据')
+        return
+      }
+      const table = this.$refs[refName].$el.cloneNode(true)
+      const fileName = `${tableName}.xlsx`
+      throttledExportToExcel(table, tableName, fileName)
     }
   }
 }

+ 43 - 23
src/views/baggageManagement/components/container/index.vue

@@ -14,7 +14,21 @@
         default-expand-all
         :expand-on-click-node="false"
         @current-change="currentChangeHandler"
-      />
+      >
+        <span
+          slot-scope="{ node, data }"
+          class="el-tree-node__label"
+        >
+          <template v-if="data.index < 0">
+            <el-tooltip content="容器历史默认显示最近10条历史,如果需要可联系管理员进行配置">
+              <span>{{ node.label }}</span>
+            </el-tooltip>
+          </template>
+          <template v-else>
+            <span>{{ node.label }}</span>
+          </template>
+        </span>
+      </el-tree>
     </div>
     <div
       v-loading="tableLoading"
@@ -172,7 +186,7 @@ export default {
       return [
         {
           index: -1,
-          label: '容器历史',
+          label: `容器历史-${this.queryData.containerID}`,
           children: this.containerHistory.map(({ flightNO, flightDate, departureAirport, arriveAirport }, index) => ({
             index,
             label: [flightNO, flightDate.replaceAll('-', '/'), departureAirport, arriveAirport].join('-')
@@ -402,33 +416,39 @@ export default {
     flex: 0 1 352px;
     margin-right: 16px;
     background-color: #ffffff;
-    ::v-deep .el-tree > .el-tree-node {
-      > .el-tree-node__children {
-        display: block;
-      }
-      .el-tree-node__content {
-        height: 38px;
-        .el-tree-node__label {
-          margin-left: 0;
-          letter-spacing: 1px;
-          line-height: 38px;
-          font-size: 14px;
-          font-family: Helvetica, 'Microsoft YaHei';
-          font-weight: bold;
-          color: #101116;
+    ::v-deep .el-tree {
+      height: calc(100vh - 80px - 17px - 20px);
+      overflow-x: hidden;
+      overflow-y: auto;
+      > .el-tree-node {
+        > .el-tree-node__children {
+          display: block;
         }
-      }
-      > .el-tree-node__content {
-        > .el-tree-node__expand-icon.el-icon-caret-right:not(.is-leaf):before {
-          font-size: 16px;
-          color: #101116;
+        .el-tree-node__content {
+          height: 38px;
+          .el-tree-node__label {
+            margin-left: 0;
+            letter-spacing: 1px;
+            line-height: 38px;
+            font-size: 14px;
+            font-family: Helvetica, 'Microsoft YaHei';
+            font-weight: bold;
+            color: #101116;
+          }
         }
-        > .el-tree-node__label {
-          font-size: 16px;
+        > .el-tree-node__content {
+          > .el-tree-node__expand-icon.el-icon-caret-right:not(.is-leaf):before {
+            font-size: 16px;
+            color: #101116;
+          }
+          > .el-tree-node__label {
+            font-size: 16px;
+          }
         }
       }
     }
   }
+
   .container-right {
     flex: 1;
     ::v-deep .el-table {

+ 132 - 11
src/views/statisticsCharts/components/commonBarStatisticsCharts.vue

@@ -25,6 +25,9 @@
 import StatisticsHeader from './statisticsHeader.vue'
 import { Query } from '@/api/dataIntegration'
 import { mapGetters } from 'vuex'
+import * as XLSX from 'xlsx'
+import XLSX_STYLE from 'xlsx-style'
+import FileSaver from 'file-saver'
 
 export default {
   name: 'CommonBarStatisticsCharts',
@@ -51,6 +54,8 @@ export default {
       hasChartData: false,
       seriesKey: 'seriesData',
       filters: [],
+      tableData: [],
+      params: [],
       options: {
         backgroundColor: '#fff',
         tooltip: {
@@ -275,7 +280,7 @@ export default {
           id = this.querySettings.byArea
         }
         params = [formData.interval, formData.area, formData.inOrOut, formData.dateTime[0], formData.dateTime[1]]
-      } else if (formData.range !== '基地分公司' && formData.range !== '') {
+      } else if (formData.range !== '') {
         if (formData.flightType === '有行李') {
           id = this.querySettings.withBaggageByOther
         } else if (formData.baggageType === '不包含DEL') {
@@ -300,11 +305,14 @@ export default {
         }
       }
       if (formData.passengerType.length) {
-        this.filters = {
-          key: formData.passengerType[0],
-          value: formData.passengerType[1]
-        }
+        this.filters = [
+          {
+            key: formData.passengerType[0],
+            value: formData.passengerType[1]
+          }
+        ]
       }
+      this.params = [...params, ...this.filters.map(({ value }) => value)]
       this.getChartsData(id, params)
     },
     async getChartsData(id, params) {
@@ -356,6 +364,7 @@ export default {
           this.options.series[2].data = yAxisData
           this.options.yAxis[1].min = (Math.min(...yAxisData) - 0.1).toFixed(2)
           this.options.yAxis[1].max = (Math.max(...yAxisData) + 0.1).toFixed(2)
+          this.tableData = [xAxisData, seriesDatas, yAxisData]
           this.hasChartData = true
         } else {
           this.$message.error(message || '失败')
@@ -387,12 +396,124 @@ export default {
         this.$message.warning('请查询后再进行导出')
         return
       }
-      const myCanvas = this.myChart._dom.querySelectorAll('canvas')[0]
-      const image = myCanvas.toDataURL('image/png')
-      const $a = document.createElement('a')
-      $a.setAttribute('href', image)
-      $a.setAttribute('download', `${this.chartsTitle}统计.png`)
-      $a.click()
+      // const myCanvas = this.myChart._dom.querySelectorAll('canvas')[0]
+      // const image = myCanvas.toDataURL('image/png')
+      // const $a = document.createElement('a')
+      // $a.setAttribute('href', image)
+      // $a.setAttribute('download', `${this.chartsTitle}统计.png`)
+      // $a.click()
+
+      // 生成表格数据
+      const xlsxDatas = [['时间', this.chartsTitle.replace('量', '数量'), `${this.chartsTitle}环比`]]
+      const transposition = this.tableData[0].map((col, colIndex) => {
+        return this.tableData.map((row, rowIndex) => {
+          return rowIndex === 2 ? (row[colIndex] * 100).toFixed(2) + '%' : row[colIndex]
+        })
+      })
+      xlsxDatas.push(...transposition)
+      // 添加合计行
+      if (xlsxDatas.length > 2) {
+        const summaryRow = ['合计']
+        const colNum = xlsxDatas[0].length
+        for (let colIndex = 1; colIndex < colNum; colIndex++) {
+          summaryRow[colIndex] = xlsxDatas.reduce((pre, currentRow, rowIndex) => {
+            if (colIndex === 1) {
+              if (rowIndex === 0) {
+                return 0
+              } else {
+                return pre + currentRow[colIndex]
+              }
+            } else {
+              return pre
+            }
+          }, '')
+        }
+        xlsxDatas.push(summaryRow)
+      }
+      // 计算列宽
+      const columnWidths = []
+      xlsxDatas.forEach((row, rowIndex) => {
+        // 计算每一列宽度,考虑换行
+        row.forEach((cell, columnIndex) => {
+          const cellWidth = Math.max(
+            ...cell
+              .toString()
+              .split('\n')
+              .map(cellRow =>
+                cellRow.split('').reduce((pre, curr) => {
+                  const letterSize = curr.charCodeAt(0) > 255 ? 2 : 1
+                  return pre + letterSize
+                }, 0)
+              )
+          )
+          if ((!columnWidths[columnIndex] && cellWidth > 0) || cellWidth > columnWidths[columnIndex]) {
+            columnWidths[columnIndex] = cellWidth
+          }
+        })
+      })
+      // 生成表格
+      const sheet = XLSX.utils.aoa_to_sheet(xlsxDatas)
+      // 添加列宽度
+      sheet['!cols'] = columnWidths.map(width => ({
+        wch: width + 2
+      }))
+      // 样式
+      const borderStyle = {
+        style: 'medium',
+        color: {
+          rgb: 'FFFFFF'
+        }
+      }
+      const reg = /^[A-Z]+([\d]+$)/
+      for (const key in sheet) {
+        const match = reg.test(key)
+        if (match) {
+          const rowIndex = reg.exec(key)[1]
+          let cellStyle = {
+            alignment: {
+              horizontal: 'center',
+              vertical: 'center',
+              wrapText: true
+            }
+          }
+          if (Number(rowIndex) === 1) {
+            cellStyle = {
+              ...cellStyle,
+              border: {
+                top: borderStyle,
+                right: borderStyle,
+                bottom: borderStyle,
+                left: borderStyle
+              },
+              font: {
+                color: {
+                  rgb: 'FFFFFF'
+                }
+              },
+              fill: {
+                fgColor: {
+                  rgb: '3366FF'
+                }
+              }
+            }
+          } else {
+            cellStyle.alignment.horizontal = 'left'
+          }
+          sheet[key].s = cellStyle
+        }
+      }
+      // 表格数据转换
+      const workBook = XLSX.utils.book_new()
+      XLSX.utils.book_append_sheet(workBook, sheet, this.chartsTitle)
+      const tableWrite = XLSX_STYLE.write(workBook, {
+        bookType: 'xlsx',
+        bookSST: true,
+        type: 'buffer',
+        cellStyles: true
+      })
+      // 下载表格
+      const fileName = `${this.chartsTitle}统计-${this.params.join('-')}.xlsx`
+      FileSaver.saveAs(new Blob([tableWrite], { type: 'application/octet-stream' }), fileName)
     }
   }
 }

+ 109 - 6
src/views/statisticsCharts/components/commonPieStatisticsCharts.vue

@@ -25,6 +25,9 @@
 import StatisticsHeader from './statisticsHeader.vue'
 import { Query } from '@/api/dataIntegration'
 import { mapGetters } from 'vuex'
+import * as XLSX from 'xlsx'
+import XLSX_STYLE from 'xlsx-style'
+import FileSaver from 'file-saver'
 
 export default {
   name: 'CommonBarStatisticsCharts',
@@ -57,6 +60,8 @@ export default {
       debounceTime: 300,
       chartHeight: '70vh',
       hasChartData: false,
+      tableData: [],
+      params: [],
       options: {
         backgroundColor: '#ffffff',
         tooltip: {
@@ -337,6 +342,7 @@ export default {
           params.splice(2, 0, formData.terminal)
         }
       }
+      this.params = params
       this.getChartsData(id, params)
     },
     async getChartsData(id, params) {
@@ -365,6 +371,7 @@ export default {
           })
           this.options.title.subtext = totalCount.toString()
           this.totalCount.value = totalCount
+          this.tableData = listValues
           this.hasChartData = true
         } else {
           this.$message.error(message || '失败')
@@ -396,12 +403,108 @@ export default {
         this.$message.warning('请查询后再进行导出')
         return
       }
-      const myCanvas = this.myChart._dom.querySelectorAll('canvas')[0]
-      const image = myCanvas.toDataURL('image/png')
-      const $a = document.createElement('a')
-      $a.setAttribute('href', image)
-      $a.setAttribute('download', `${this.chartsTitle}统计.png`)
-      $a.click()
+      // const myCanvas = this.myChart._dom.querySelectorAll('canvas')[0]
+      // const image = myCanvas.toDataURL('image/png')
+      // const $a = document.createElement('a')
+      // $a.setAttribute('href', image)
+      // $a.setAttribute('download', `${this.chartsTitle}统计.png`)
+      // $a.click()
+
+      const xlsxDatas = [['时间', '位置', '分类', '数量', '占比']]
+      xlsxDatas.push(
+        ...this.tableData.map(element => [
+          element['A'],
+          element['location'],
+          element[this.categoryKey],
+          element[this.seriesKey],
+          ((element[this.seriesKey] / this.totalCount.value) * 100).toFixed(2) + '%'
+        ])
+      )
+      xlsxDatas.push(['合计', '', '', this.totalCount.value, ''])
+      // 计算列宽
+      const columnWidths = []
+      xlsxDatas.forEach((row, rowIndex) => {
+        // 计算每一列宽度,考虑换行
+        row.forEach((cell, columnIndex) => {
+          const cellWidth = Math.max(
+            ...cell
+              .toString()
+              .split('\n')
+              .map(cellRow =>
+                cellRow.split('').reduce((pre, curr) => {
+                  const letterSize = curr.charCodeAt(0) > 255 ? 2 : 1
+                  return pre + letterSize
+                }, 0)
+              )
+          )
+          if ((!columnWidths[columnIndex] && cellWidth > 0) || cellWidth > columnWidths[columnIndex]) {
+            columnWidths[columnIndex] = cellWidth
+          }
+        })
+      })
+      // 生成表格
+      const sheet = XLSX.utils.aoa_to_sheet(xlsxDatas)
+      // 添加列宽度
+      sheet['!cols'] = columnWidths.map(width => ({
+        wch: width + 2
+      }))
+      // 样式
+      const borderStyle = {
+        style: 'medium',
+        color: {
+          rgb: 'FFFFFF'
+        }
+      }
+      const reg = /^[A-Z]+([\d]+$)/
+      for (const key in sheet) {
+        const match = reg.test(key)
+        if (match) {
+          const rowIndex = reg.exec(key)[1]
+          let cellStyle = {
+            alignment: {
+              horizontal: 'center',
+              vertical: 'center',
+              wrapText: true
+            }
+          }
+          if (Number(rowIndex) === 1) {
+            cellStyle = {
+              ...cellStyle,
+              border: {
+                top: borderStyle,
+                right: borderStyle,
+                bottom: borderStyle,
+                left: borderStyle
+              },
+              font: {
+                color: {
+                  rgb: 'FFFFFF'
+                }
+              },
+              fill: {
+                fgColor: {
+                  rgb: '3366FF'
+                }
+              }
+            }
+          } else {
+            cellStyle.alignment.horizontal = 'left'
+          }
+          sheet[key].s = cellStyle
+        }
+      }
+      // 表格数据转换
+      const workBook = XLSX.utils.book_new()
+      XLSX.utils.book_append_sheet(workBook, sheet, this.chartsTitle)
+      const tableWrite = XLSX_STYLE.write(workBook, {
+        bookType: 'xlsx',
+        bookSST: true,
+        type: 'buffer',
+        cellStyles: true
+      })
+      // 下载表格
+      const fileName = `${this.chartsTitle}统计-${this.params.join('-')}.xlsx`
+      FileSaver.saveAs(new Blob([tableWrite], { type: 'application/octet-stream' }), fileName)
     }
   }
 }