当前位置: 首页 > article >正文

vue前端接包(axios)+ 前端导出excel(xlsx-js-style)

// 先在请求处加上:    
responseType: 'arraybuffer', // 指定响应类型为ArrayBuffer
 const data = new Uint8Array(response.data); // 将ArrayBuffer转换为Uint8Array

  const val = { columns: [], data: [] }

  let offset = 0; // 用于跟踪当前解析到的位置
  while (offset < data.length) {
    // 确保当前偏移量可读取8个字节
    if (offset + 8 > data.length) {
      console.warn('字节数不足,无法读取偏移处的长度:', offset);
      break; // 不够数据,退出循环
    }

    // 获取前8个字节,计算包长度
    const lengthHex = String.fromCharCode.apply(null, data.slice(offset, offset + 8));
    const packLength = parseInt(lengthHex, 16); // 转换为十进制

    // 确保当前偏移量可读取完整的数据包
    if (offset + 8 + packLength > data.length) {
      console.warn('字节数不足,无法在偏移处读取数据包:', offset);
      break; // 不够数据,退出循环
    }

    // 使用TextDecoder来处理JSON内容
    const decoder = new TextDecoder('utf-8');
    const jsonBody = decoder.decode(data.slice(offset + 8, offset + 8 + packLength)); // 获取JSON内容

    // 解析并存储数据包
    try {
      const packetData = JSON.parse(jsonBody); // 解析JSON
      if (offset == 0) {
        val.columns = packetData.columns;
      }
      val.data.push(...packetData.data);
    } catch (error) {
      console.error('Error parsing JSON:', error, jsonBody);
    }



    // 更新偏移量
    offset += 8 + packLength; // 移动到下一个包的起始位置
  }

  // 现在 allPackets 包含所有解析后的数据包
  console.log('All parsed packets:', val);

  const worker = new Worker(new URL('./export-worker.ts', import.meta.url), { type: 'module' });

  worker.postMessage({ data: val });

  worker.onmessage = (e) => {
    if (e && e.data && e.data.t == "export") {
      e.stopPropagation();
      e.preventDefault();
      // data will be the Uint8Array from the worker
      const res = e.data.v;
      downloadExcelFile(new Blob([res], { type: "application/octet-stream" }), row.TaskName + '-核查结果.xlsx');
      window.$message?.success('导出成功!');
    }

    operateLoading.value = false;
    worker.terminate(); // 终止 Worker
  };

  worker.onerror = (error) => {
    console.error('Worker error:', error);
    operateLoading.value = false;
    worker.terminate(); // 终止 Worker
  };

export-worker.ts

import * as XLSX_STYLE from 'xlsx-js-style';

interface Column {
  name: string;
  headerText: string;
}

interface Data {
  columns: Column[];
  data: any[][];
}

interface ExportMessage {
  t: string;
  v: any;
}

self.onmessage = async (event: MessageEvent) => {
  const { data }: { data: Data } = event.data;

  // 这里放置处理导出逻辑的代码
  const highlightFields: string[][] = [];
  const problemFieldsIndex: number = data.columns.findIndex(col => col.name === 'ProblemFields');

  // 移除 ProblemFields 字段
  if (problemFieldsIndex !== -1) {
    data.columns.splice(problemFieldsIndex, 1);
  }
  const formattedData = data.data.map((row: any[]) => {
    const problemFields = row[problemFieldsIndex];
    highlightFields.push(problemFields.split(','));
    row.splice(problemFieldsIndex, 1);
    // const rowData: { [key: string]: any } = {};
    // data.columns.forEach((col: Column, index: number) => {
    //   rowData[col.headerText] = row[index];
    // });
    return row;
  });

  const BATCH_SIZE = 30000; // 定义每批处理的数据大小

  // 添加标题行
  const header = data.columns.map(col => col.headerText);
  // 设置样式
  const headerCellStyle = {
    font: { name: 'Arial', sz: 10, bold: true, color: { rgb: "FFFFFF" } },  // 白色字体
    fill: { fgColor: { rgb: "808080" } }, // 灰色背景
    alignment: { vertical: 'center', horizontal: 'center' }
  };

  const cellStyle = {
    font: { name: 'Arial', sz: 10 },
    alignment: { vertical: 'center', horizontal: 'center', wrapText: true }
  };

  const highlightStyle = {
    fill: { fgColor: { rgb: "FFFF00" } }, // 黄色背景
    font: { name: '宋体', sz: 11 },
    alignment: { vertical: 'center', horizontal: 'center', wrapText: true },
    border: {
      top: { style: 'thin', color: { rgb: "C3CBDD" } },
      bottom: { style: 'thin', color: { rgb: "C3CBDD" } },
      left: { style: 'thin', color: { rgb: "C3CBDD" } },
      right: { style: 'thin', color: { rgb: "C3CBDD" } }
    }
  };
  const ws = [];
  for (let i = 0; i < formattedData.length; i += BATCH_SIZE) {
    const worksheet = XLSX_STYLE.utils.aoa_to_sheet([header], { origin: 'A1' }); // 创建一个空工作表
    const batchData = formattedData.slice(i, i + BATCH_SIZE);
    // 添加批量数据到工作表末尾
    XLSX_STYLE.utils.sheet_add_aoa(worksheet, batchData, { skipHeader: true, origin: -1 });

    // 设置列宽、行高和样式
    // 设置每列的宽度(单位:字符)
    const wsCols: { wch: number }[] = new Array(data.columns.length).fill({ wch: 30 });
    worksheet['!cols'] = wsCols;
    worksheet['!cols'][0] = { wch: 50 };

    // 设置行高(单位:像素,通常建议设置为 20 或更高)
    worksheet['!rows'] = new Array(batchData + 1).fill({ hpt: 50 });
    worksheet['!rows'][0] = { hpt: 25 };

    // 应用样式到表头和普通单元格
    for (const col in worksheet) {
      if (col[0] === '!') continue; // 跳过元数据
      const cell = worksheet[col];

      // 设置表头样式
      if (data.columns.some(header => header.headerText === cell.v)) {
        cell.s = headerCellStyle;
      } else {
        cell.s = cellStyle; // 设置普通单元格样式
      }
    }

    // 根据 ProblemFields 高亮单元格
    batchData.forEach((row, rowIndex) => {
      const globalRowIndex = i + rowIndex; // 计算全局行索引
      highlightFields[globalRowIndex].forEach(problemField => {
        const colIndex = data.columns.findIndex(col => col.name === problemField); // 找到对应的列索引
        if (colIndex !== -1) {
          const cellRef = XLSX_STYLE.utils.encode_cell({ c: colIndex, r: rowIndex + 1 }); // 行号加1因为第一行为表头
          if (worksheet[cellRef]) {
            worksheet[cellRef].s = highlightStyle; // 应用高亮样式
          }
        }
      });
    });

    ws.push(worksheet);
  }

  // const worksheet = XLSX_STYLE.utils.json_to_sheet(formattedData, { header: data.columns.map(col => col.headerText) });
  const workbook = XLSX_STYLE.utils.book_new();


  const min = formattedData.length > BATCH_SIZE ? BATCH_SIZE : formattedData.length;
  ws.forEach((worksheet: any) => {
    XLSX_STYLE.utils.book_append_sheet(workbook, worksheet);
  });

  console.log("准备写入")
  const val = XLSX_STYLE.write(workbook, { type: "array", bookType: "xlsx", bookSST: true, compression: true });
  console.log("写入完成")

  self.postMessage({ t: "export", v: val } as ExportMessage);
};




http://www.kler.cn/news/363680.html

相关文章:

  • go基础(一)
  • Webserver(1)Linux开发环境搭建
  • streamlit 实现 flink SQL运行界面
  • css模糊遮罩效果
  • 10. 异常处理器
  • Nova-Admin:基于Vue3、Vite、TypeScript和NaiveUI的开源简洁灵活管理模板
  • 植物端粒到端粒(T2T)基因组研究进展与展望
  • Android 图片相识度比较(pHash)
  • linux-牛刀小试
  • NAND FLASH 与 SPI FLASH
  • Python基于OpenCV的实时疲劳检测
  • AI网关对企业的意义及如何构建 AI 网关
  • [Windows] 很火的开源桌面美化工具 Seelen UI v2.0.2
  • Github 2024-10-18Java开源项目日报Top9
  • 使用 SSH 连接 GitLab 的常见问题及解决方案
  • 摄像机实时接入分析平台LiteAlServer视频智能分析软件抽烟检测算法的应用场景
  • a标签点击页面跳转是-403,回车后正常了
  • MySQL-28.事务-介绍与操作
  • 【每日一题】LeetCode - 反转整数问题
  • 多线程初阶(七):单例模式指令重排序
  • 【Docker技术详解】(一)Docker镜像文件系统的关系和交互
  • Spring Cache Caffeine 高性能缓存库
  • 学习AJAX请求(初步)24.10.21-10.23
  • JAVA应用测试,线上故障排查分析全套路!
  • 数据结构 —— 链式二叉树(C语言讲解)
  • main.ts中引入App.vue报错,提示“Cannot find module ‘./App.vue’ or its corresponding type