feiyu02
2025-09-17 b330e57051e54789eb83d10dc58c4d9d10c608e1
src/views/fysp/data-product/ProdDailyReport.vue
@@ -1,9 +1,571 @@
<template>
  <div>DailyReport</div>
  <!-- 主内容 -->
  <FYSearchBar ref="searchRef" @search="search" :loading="loading">
    <template #options>
      <!-- 区县 -->
      <FYOptionLocation
        :allOption="false"
        :level="3"
        :checkStrictly="false"
        v-model:value="formSearch.locations"
        style="width: 300px"
      ></FYOptionLocation>
      <!-- 场景类型 -->
      <!-- <FYOptionScene
        :allOption="true"
        :type="2"
        v-model:value="formSearch.scenetype"
      ></FYOptionScene> -->
      <!-- 时间 -->
      <FYOptionTime
        :initValue="false"
        type="datetimerange"
        v-model:value="formSearch.timeArray"
        style="width: 250px"
      ></FYOptionTime>
    </template>
    <template #buttons>
      <el-button
        icon="Download"
        type="primary"
        class=""
        :loading="docLoading"
        :disabled="!docParam"
        @click="genWord()"
      >
        生成报告
      </el-button>
      <el-button
        icon="Download"
        type="success"
        class=""
        :disabled="!docParam"
        @click="exportToExcel()"
      >
        下载表格
      </el-button>
    </template>
  </FYSearchBar>
  <div class="m-task container">
    <!--头部信息-->
    <div class="h-top col-md-12">
      <div class="m-top">
        <span class="title-input"> {{ reportName }} </span>
        <!-- <input
          type="text"
          class="title-input"
          value="秋冬污染季期间徐汇区扬尘污染辅助监管工作日报"
        /> -->
      </div>
    </div>
    <div class="m-msg col-md-12">
      <span>{{ descMsg }}</span>
      <div class="report-table row">
        <table id="table_subtask" class="" border="1"></table>
      </div>
    </div>
  </div>
</template>
<script>
import taskApi from '@/api/fysp/taskApi';
import dayjs from 'dayjs';
import * as XLSX from 'xlsx';
import FileSaver from 'file-saver';
import analysisApi from '@/api/fysp/analysisApi.js';
import { exportDocx } from '@/utils/doc';
export default {
  name: 'DailyReport',
  computed: {
    reportName() {
      return `${this.formSearch.locations.dName}扬尘污染辅助监管工作日报`;
    }
  },
  data() {
    return {
      CompTable: {
        title: [],
        content: [],
        tableStyleClass: 'comp-table',
        rowStyleClss: 'oddrowcolor',
        init: function (title, content) {
          this.title = title;
          this.content = content;
        },
        /**
         * 展示表格
         * @param {*} elementId table的id
         */
        show: function (elementId) {
          const table = document.getElementById(elementId);
          if (table) {
            table.innerHTML = '';
            table.classList.add(this.tableStyleClass);
            this.createHead(elementId);
            this.createContents(elementId);
          }
        },
        /**
         * 生成表头
         * @param {*} elementId
         */
        createHead: function (elementId) {
          const element = document.getElementById(elementId);
          if (element) {
            const row = document.createElement('tr');
            for (let index = 0; index < this.title.length; index++) {
              const th = document.createElement('th');
              th.setAttribute('align', 'center');
              th.textContent =
                this.title[index] === undefined ? '' : this.title[index];
              row.appendChild(th);
            }
            element.appendChild(row);
          }
        },
        /**
         * 生成表内容
         * @param {*} elementId
         */
        createContents: function (elementId) {
          const element = document.getElementById(elementId);
          if (element) {
            for (let index = 0; index < this.content.length; index++) {
              if (index % 2 === 0) {
                this.rowStyleClss = 'evenrowcolor';
              } else {
                this.rowStyleClss = 'oddrowcolor';
              }
              const rowArray = this.content[index];
              this.createRow(rowArray, elementId);
            }
          }
        },
        /**
         * 生成一行(包括合并单元格)
         * @param {*} rowArray
         */
        createRow: function (rowArray, elementId) {
          const maxRows = this.getMaxRow(rowArray);
          const leftArray = [];
          const tr = document.createElement('tr');
          for (let i = 0; i < rowArray.length; i++) {
            let cell = rowArray[i];
            if (Array.isArray(cell)) {
              const rowspan = maxRows / cell.length;
              const td = document.createElement('td');
              td.setAttribute('rowspan', rowspan);
              td.classList.add(this.rowStyleClss);
              td.textContent = cell[0] === undefined ? '' : cell[0];
              tr.appendChild(td);
              leftArray.push(cell.slice(1)); // Save the rest of the cell contents
            } else {
              const td = document.createElement('td');
              td.setAttribute('rowspan', maxRows);
              td.classList.add(this.rowStyleClss);
              td.textContent = cell === undefined ? '' : cell;
              tr.appendChild(td);
            }
          }
          document.getElementById(elementId).appendChild(tr);
          for (let i = 1; i < maxRows; i++) {
            const tr = document.createElement('tr');
            leftArray.forEach((cell, y) => {
              if (i < cell.length) {
                const td = document.createElement('td');
                td.setAttribute('rowspan', maxRows / (cell.length - i));
                td.classList.add(this.rowStyleClss);
                td.textContent = cell[i] === undefined ? '' : cell[i];
                tr.appendChild(td);
              }
            });
            document.getElementById(elementId).appendChild(tr);
          }
        },
        /**
         * 获取这一行中需要合并的行数
         * @param {*} rowArray
         */
        getMaxRow: function (rowArray) {
          let maxRows = 1;
          rowArray.forEach((element) => {
            if (Array.isArray(element) && element.length > maxRows) {
              maxRows = element.length;
            }
          });
          return maxRows;
        }
      },
      canClickDay: [],
      excelConfig: {
        title: '',
        data: [],
        fields: []
      },
      table: {
        headers: [],
        data: []
      },
      loading: false,
      formSearch: {
        locations: {},
        scenetype: {},
        time: [],
        timeArray: [new Date(), new Date()],
        districtCode: '310116'
      },
      descMsg: '',
      // 导出报告参数
      docParam: undefined,
      docLoading: false
    };
  },
  watch: {
    formSearch: {
      handler(nV, oV) {
        // this.getCanClickDay();
      },
      deep: true
      // immediate: true
    }
  },
  mounted() {
    this.formSearch.time = new Date();
  },
  methods: {
    disabledDate(time) {
      this.getCanClickDay();
      let disabled =
        this.canClickDay.filter((item) => {
          let date = dayjs(time);
          let itemDay = new Date(item);
          console.log(
            'curr preview time canClickDay',
            itemDay.getFullYear(),
            itemDay.getMonth(),
            itemDay.getDate()
          );
          console.log(
            'curr preview time date',
            date.year(),
            date.month(),
            date.date()
          );
          return (
            date.year() == itemDay.getFullYear() &&
            date.month() == itemDay.getMonth() &&
            date.date() == itemDay.getDate()
          );
        }).length == 0;
      return !disabled;
    },
    getSelectedCityname() {
      let city = '';
      switch (this.formSearch.districtCode) {
        case '310116':
          city = '金山区';
          break;
        case '310106':
          city = '静安区';
          break;
        case '310104':
          city = '徐汇区';
          break;
        case '310105':
          city = '长宁区';
          break;
      }
      return city;
    },
    getCanClickDay() {
      taskApi.getTopTask().then((res) => {
        this.canClickDay = [];
        let city = this.getSelectedCityname();
        res
          .filter((r) => {
            return (
              r.districtname == city &&
              dayjs(this.formSearch.time).year() == dayjs(r.starttime).year() &&
              dayjs(this.formSearch.time).month() == dayjs(r.starttime).month()
            );
          })
          .map((topTask) => {
            taskApi.fetchDayTasks(topTask.tguid).then((res) => {
              res.forEach((r) => {
                let formSearchDate = dayjs(this.formSearch.time);
                let date = new Date();
                dayjs(date)
                  .year(formSearchDate.year())
                  .month(formSearchDate.month())
                  .date(Number(r.date.slice(8, 10)));
                this.canClickDay.push(date);
              });
              console.log('this.canClickDay', this.canClickDay);
            });
          });
      });
    },
    exportToExcel() {
      // 创建工作簿
      const wb = XLSX.utils.book_new();
      var wsData = [];
      for (let index = 0; index < this.table.data.length; index++) {
        const dataItem = this.table.data[index];
        var row = {};
        wsData.push(row);
        for (let index = 0; index < dataItem.length; index++) {
          row[this.table.headers[index]] = dataItem[index];
        }
      }
      console.log('data', wsData);
      // 创建工作表
      const ws = XLSX.utils.json_to_sheet(wsData);
      // 将工作表添加到工作簿
      XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
      // 生成Excel文件
      const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' });
      // 字符串转ArrayBuffer
      function s2ab(s) {
        const buf = new ArrayBuffer(s.length);
        const view = new Uint8Array(buf);
        for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
        return buf;
      }
      // 保存文件
      FileSaver.saveAs(
        new Blob([s2ab(wbout)], { type: 'application/octet-stream' }),
        `${this.reportName}.xlsx`
      );
    },
    search() {
      let sTime = dayjs(this.formSearch.timeArray[0])
        .hour(0)
        .minute(0)
        .second(0)
        .millisecond(0);
      let eTime = dayjs(this.formSearch.timeArray[1])
        .hour(23)
        .minute(59)
        .second(59)
        .millisecond(0);
      // eTime = eTime.toISOString();
      // sTime = sTime.toISOString();
      let config = {
        startTime: sTime.toDate(),
        endTime: eTime.toDate(),
        districtCode: this.formSearch.locations.dCode
      };
      this.loading = true;
      analysisApi
        .dailyreport(config)
        .then((res) => {
          this.table.headers = res.tableTitle[0];
          this.table.data = res.tableContent;
          this.CompTable.init(res.tableTitle[0], res.tableContent);
          this.CompTable.show('table_subtask');
          const countMap = new Map(); //各场景数量
          let proCount = 0; //总计问题数量
          let changeCount = 0; //总计整改数量
          const tableRows = [];
          res.tableContent.forEach((cList) => {
            const sceneType = cList[2];
            if (!(sceneType in countMap)) {
              countMap[sceneType] = 0;
            }
            countMap[sceneType] += 1;
            proCount += Number(cList[10]);
            changeCount += Number(cList[13]);
            tableRows.push({
              sIndex: cList[0],
              sType: sceneType,
              sName: cList[3],
              sLocation: cList[4],
              sProblemList: cList[8].split('\n'),
              time: cList[6],
              sTown: cList[5],
              sChangeList: cList[11].split('\n')
            });
          });
          let countStr = '';
          for (const key in countMap) {
            const e = countMap[key];
            if (countStr != '') {
              countStr += '、';
            }
            countStr += `${key}${e}个`;
          }
          this.descMsg = `共巡查${res.tableContent.length}个扬尘场景(${countStr}),共发现问题${proCount}项,督促整改了${changeCount}项。`;
          this.docParam = {
            month: sTime.month() + 1,
            day: sTime.date(),
            index: 1,
            hasMoreDays: !sTime.isSame(eTime, 'day'),
            endMonth: eTime.month() + 1,
            endDay: eTime.date(),
            totalScene: res.tableContent.length,
            sceneNumTxt: countStr,
            proNum: proCount,
            changeNum: changeCount,
            unChangeNum: proCount - changeCount,
            table1: tableRows
          };
        })
        .finally(() => (this.loading = false));
    },
    // 生成word报告
    genWord() {
      if (this.docParam) {
        const param = this.docParam;
        exportDocx(
          '/秋冬季空气质量攻坚工作提示模板.docx',
          param,
          `秋冬季空气质量攻坚工作提示(${param.month}月${param.day}日).docx`
        );
      }
    }
  }
};
</script>
<style>
.options {
  display: flex;
}
.c-time {
  width: 12rem;
  height: 3rem;
  border-radius: 3px;
  font-size: 1rem;
}
.download-div {
}
.download-btn {
  background-color: #5599cc;
  width: 10rem;
  height: 2.6rem;
  color: #ffffff;
  border-radius: 3px;
  margin-right: 2rem;
}
.h-top .m-top {
  /* width: 100%; */
  padding-right: 8rem;
  /* padding: 0 7% 0 0%; */
  /* min-height: 8rem; */
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
}
.title-input {
  margin-top: -20px;
  border-radius: 6px;
  color: #000;
  width: 80%;
  font-size: 1.5rem;
  line-height: 5rem;
  text-align: center;
  border: none;
}
.report-desc {
  width: 80%;
  margin-top: 5rem;
}
.desc-textarea {
  width: 100%;
  text-indent: 6rem;
  font-size: 1rem;
  margin-left: 5px;
}
.report-table {
  width: 100%;
  margin-top: 2rem;
  table-layout: fixed;
}
.r-table {
  width: 100%;
  font-size: 2rem;
}
.r-table tr {
  height: auto;
}
.r-table th {
  height: 100%;
  text-align: center;
}
.r-table td {
  border-width: 1px;
  min-width: 6rem;
}
.render-html {
  cursor: pointer;
}
.h-top .m-top .task-div {
  width: 100%;
  display: flex;
  justify-content: flex-end;
  height: 2.6rem;
  margin-bottom: 1rem;
}
/******************************表格样式*******************************/
table.comp-table {
  font-family: verdana, arial, sans-serif;
  font-size: 11px;
  color: #333333;
  border-width: 1px;
  border-color: #666666;
  border-collapse: collapse;
  margin: 4px;
  width: 87vw;
}
table.comp-table th {
  border-width: 1px;
  padding: 8px;
  border-style: solid;
  border-color: #666666;
  background-color: #dedede;
  text-align: center; /** 设置水平方向居中 */
  vertical-align: middle; /** 设置垂直方向居中 */
}
table.comp-table td {
  border-width: 1px;
  padding: 8px;
  border-style: solid;
  border-color: #666666;
  /* background-color: #ffffff; */
}
.oddrowcolor {
  background-color: #d9f2f5;
}
.evenrowcolor {
  background-color: #eff6f8;
}
/**********************************************************************/
</style>