riku
2025-09-20 0796eebe3520fafb0ac5d36ee584af81506d7e9c
src/views/fysp/data-product/components/BaseDataProduct.vue
@@ -23,18 +23,18 @@
    </template>
    <template #buttons>
      <CompReportDownloadDialog
        name="问题与整改汇总分析与动态跟踪清单"
        :name="fileName"
        :locations="formSearch.locations"
        :scenetype="formSearch.scenetype"
        :time="formSearch.time"
        @submit="handleSearch"
        @submit="(e) => handleSearch(e, true)"
      ></CompReportDownloadDialog>
    </template>
  </FYSearchBar>
  <el-form ref="expandRef" :inline="true">
    <CompQuickSet @quick-set="setOptions"></CompQuickSet>
  </el-form>
  <el-space>
  <el-space ref="segmentedRef">
    <el-segmented
      :model-value="activeSheet"
      :options="sheetNames"
@@ -43,34 +43,75 @@
  </el-space>
  <el-table
    ref="tableRef"
    :data="excelData"
    :data="activeExcelData.data"
    v-loading="loading"
    table-layout="fixed"
    table-layout="auto"
    size="small"
    :height="tableHeight"
    fit
    flexible
    stripe
    border
  >
    <template v-if="excelData">
    <template v-if="activeExcelData.head">
      <el-table-column
        v-for="(value, key, index) in excelData[0]"
        v-for="(item, index) in activeExcelData.head"
        :key="index"
        :prop="key"
        :label="key"
        :prop="item.prop"
        :label="item.name"
      >
        <template v-if="item.children" #default="{ row }">
          <span v-html="row[item.prop]"></span>
          <el-table-column
            v-for="(item1, index1) in item.children"
            :key="index1"
            :prop="item1.prop"
            :label="item1.name"
          >
            <template v-if="item1.children" #default="scope1">
              <span v-html="scope1.row[item1.prop]"></span>
              <el-table-column
                v-for="(item2, index2) in item1.children"
                :key="index2"
                :prop="item2.prop"
                :label="item2.name"
              >
                <template v-if="item2.children" #default="scope2">
                  <span v-html="scope2.row[item2.prop]"></span>
                </template>
              </el-table-column>
            </template>
          </el-table-column>
        </template>
      </el-table-column>
    </template>
  </el-table>
  <div v-if="excelHtml" v-html="excelHtml"></div>
  <!-- <vue-office-excel
    :src="excel"
    :style="'height: ' + tableHeight"
    @rendered="renderedHandler"
    @error="errorHandler"
  /> -->
</template>
<script setup>
/**
 * 问题动态跟踪
 */
import { ref, onMounted } from 'vue';
//引入VueOfficeExcel组件
import VueOfficeExcel from '@vue-office/excel';
//引入相关样式
import '@vue-office/excel/lib/index.css';
import { ref, onMounted, computed } from 'vue';
import dayjs from 'dayjs';
import * as XLSX from 'xlsx';
import * as ExcelJS from 'exceljs';
import dataproductApi from '@/api/fysp/dataproductApi';
import CompReportDownloadDialog from './CompReportDownloadDialog.vue';
import { Base64 } from 'js-base64';
import { useBgtaskStore } from '@/stores/bgtaskStore';
const bgtaskStore = useBgtaskStore();
const emit = defineEmits(['search']);
@@ -91,6 +132,19 @@
  }
});
const fileName = computed(() => {
  switch (props.productType) {
    case 1:
      return '问题与整改汇总分析与动态跟踪清单';
    case 2:
      return '规范性评估与分析清单';
    case 3:
      return '问题与整改分布分析清单';
    default:
      return '报告清单';
  }
});
const formSearch = ref({
  locations: {},
  scenetype: {},
@@ -100,12 +154,14 @@
const tableHeight = ref('500');
let workbook;
const sheetNames = ref();
const excelDatas = ref(new Map());
const activeSheet = ref();
const excelData = ref();
const excelHtml = ref();
const activeExcelData = ref({});
const excel = ref('');
const searchRef = ref(null);
const expandRef = ref(null);
const segmentedRef = ref(null);
function setOptions(param) {
  formSearch.value.locations = param.locations;
@@ -119,7 +175,7 @@
  getTable(activeSheet.value);
}
function handleSearch(forceUpdate) {
function handleSearch(forceUpdate, isDownload) {
  const locations = formSearch.value.locations;
  const time = formSearch.value.time;
  const scenetype = formSearch.value.scenetype;
@@ -134,28 +190,87 @@
    scensetypeid: scenetype.value
  };
  excelDatas.value.clear();
  loading.value = true;
  dataproductApi
    .downloadProduct(area, props.productType, forceUpdate ? forceUpdate : false)
    .then((res) => {
      const data = new Uint8Array(res);
      workbook = XLSX.read(data, { type: 'array' });
      sheetNames.value = workbook.SheetNames;
      activeSheet.value = sheetNames.value[0];
      getTable(activeSheet.value);
    .then(async (res) => {
      if (res == false) {
        alert('报告生成中,可在后台任务查看生成进度');
        bgtaskStore.dialogShow = true;
        bgtaskStore.fetchTask();
      } else {
        if (isDownload) {
          const name = Base64.decode(res.headers.get('filename'));
          const url = window.URL.createObjectURL(res.data);
          const link = document.createElement('a');
          link.href = url;
          link.setAttribute('download', name);
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link);
          window.URL.revokeObjectURL(url);
        } else {
          res.data.arrayBuffer().then((data) => {
            workbook = XLSX.read(data, { type: 'array' });
            sheetNames.value = workbook.SheetNames;
            activeSheet.value = sheetNames.value[0];
            getTable(activeSheet.value);
          });
        }
        // const workbook = new ExcelJS.Workbook();
        // await workbook.xlsx.load(res);
        // workbook.eachSheet(function (worksheet, sheetId) {
        //   console.log(worksheet.name);
        // });
        // const sheet1 = workbook.worksheets[0]
        // const row = sheet1.getRow(1)
        // console.log(row);
        // console.log(res);
        // console.log(new ArrayBuffer(res));
        // res.arrayBuffer().then(r=>{
        //   excel.value = r
        // })
        // excel.value = new ArrayBuffer(res)
      }
    })
    .finally(() => (loading.value = false));
}
function getTable(sheetName) {
  const worksheet = workbook.Sheets[sheetName];
  const tableData = XLSX.utils.sheet_to_json(worksheet, { header: 3 });
  // const tableData = XLSX.utils.sheet_to_csv(worksheet);
  console.log(tableData);
  console.log(tableData[0]);
  combineTableHead(tableData);
  if (!excelDatas.value.has(sheetName)) {
    const worksheet = workbook.Sheets[sheetName];
    // const tableData = XLSX.utils.sheet_to_json(worksheet, { header: 3 });
    let tableData = XLSX.utils.sheet_to_txt(worksheet);
    // console.log(tableData);
  excelData.value = props.beforeDataShow(tableData);
    const regx = new RegExp(/"([^"]|\n)+"/, 'g');
    let matchTxt = tableData.match(regx);
    if (matchTxt) {
      matchTxt.forEach((txt) => {
        // let newTxt = txt.replace(new RegExp(/\n/, 'g'), '</span><br/><span>');
        // newTxt = newTxt.replace(new RegExp(/"/, 'g'), '');
        // tableData = tableData.replace(txt, `<span>${newTxt}</span>`);
        let newTxt = txt.replace(new RegExp(/\n/, 'g'), '');
        newTxt = newTxt.replace(new RegExp(/"/, 'g'), '');
        tableData = tableData.replace(txt, newTxt);
      });
    }
    // console.log(tableData);
    const t = strToTableObj(tableData);
    // console.log(t);
    // console.log(head);
    // console.log(data);
    excelDatas.value.set(sheetName, t);
  }
  activeExcelData.value = excelDatas.value.get(sheetName);
  // activeExcelData.value = props.beforeDataShow(tableData);
}
// 根据表头的行数,合并表头
@@ -173,11 +288,105 @@
function calcTableHeight() {
  const h1 = searchRef.value.$el.offsetHeight;
  const h2 = expandRef.value.$el.offsetHeight;
  const h3 = segmentedRef.value.$el.offsetHeight;
  const h = h1 + h2;
  const h = h1 + h2 + h3;
  return `calc(100vh - ${h}px - 60px - var(--el-main-padding) * 2)`;
}
function renderedHandler() {
  console.log('渲染完成');
}
function errorHandler(e) {
  console.log('渲染失败', e);
}
function strToTableObj(data, headNum) {
  const rows = data.split('\n').map((r) => r.split('\t'));
  if (rows.length == 0) return;
  let header = 1;
  for (let i = 1; i < rows.length; i++) {
    const row = rows[i];
    if (row.length == 0) {
      break;
    }
    if (row[0] == '') {
      header++;
    } else {
      break;
    }
  }
  if (rows.length < header) {
    throw new Error(`文件行数小于${header}`);
  }
  // 表头
  let lastHead = [];
  // 属性名
  const dataKeys = [];
  let offset = 1;
  for (let i = header - 1; i >= 0; i--) {
    const row = rows[i];
    if (lastHead.length == 0) {
      row.forEach((r, y) => {
        const prop = r == '' ? r : `id-${r}-${y}`;
        lastHead.push({ name: r, prop });
        dataKeys.push(prop);
      });
    } else {
      const newHead = [];
      row.forEach((r, y) => {
        const prop = `id-${r}-${y}`;
        if (dataKeys[y] == '') {
          // lastHead[y] = { name: r, prop };
          dataKeys[y] = prop;
        }
        const last = lastHead[y];
        if (!last) {
          console.log(last);
        }
        if (last.name == '') {
          newHead.push({ name: r, prop });
          offset++;
        } else if (r != '') {
          newHead.push({
            name: r,
            children: [last]
          });
          offset = 1;
        } else {
          newHead.push({ name: '', prop });
          let _index = newHead.length - 1 - offset;
          _index = _index >= 0 ? _index : 0;
          newHead[_index].children.push(last);
          offset++;
        }
      });
      lastHead = newHead;
    }
  }
  const heads = [];
  lastHead.forEach((h) => {
    if (h && h.name != '') {
      heads.push(h);
    }
  });
  const tableData = [];
  for (let i = header; i < rows.length; i++) {
    const row = rows[i];
    const data = {};
    row.forEach((r, y) => {
      data[dataKeys[y]] = r;
    });
    tableData.push(data);
  }
  return { head: heads, data: tableData };
}
onMounted(() => {
  tableHeight.value = calcTableHeight();
  // handleSearch()