export2Excel.ts 4.8 KB
/* eslint-disable */
import { UPLOAD_FILE_MAX_LINE } from "@/app/constant";
import * as XLSX from "xlsx";
import { removeDeepThink } from "../deepThink";

export function toExcel(content: string) {
  content = removeDeepThink(content);
  if (hasTable(content)) {
    htmlToExcel(content);
  } else {
    markdownToExcel(content);
  }
}

export function hasTable(htmlContent: string): boolean {
  const parser = new DOMParser();
  const doc = parser.parseFromString(htmlContent, "text/html");
  return doc.querySelector("table") !== null;
}

export function markdownToExcel(content: string) {
  let sheetName = "result"; // 默认表名
  let tableContent = "";

  // 查找标题内容(以 ** 或 ## 或 ### 开头)
  const titleMatch = content.match(/^(#{2,3}|\*\*)\s*(.*)/m);
  if (titleMatch) {
    sheetName = titleMatch[2].trim().replace(/\s+/g, "_"); // 使用标题作为表名
    // 提取表格内容(跳过标题部分)
    tableContent = content.substring(titleMatch[0].length).trim();
  } else {
    tableContent = content;
  }

  // 查找表格的起始位置(第一行以 | 开头且以 | 结尾)
  let tableStartIndex = -1;
  const lines = tableContent.split("\n");
  for (let i = 0; i < lines.length; i++) {
    if (lines[i].trim().startsWith("|") && lines[i].trim().endsWith("|")) {
      tableStartIndex = i;
      break;
    }
  }

  if (tableStartIndex === -1) {
    throw new Error("表格内容未找到");
    return;
  }

  // 查找表格的结束位置(遇到不以 | 开头或者不以 | 结尾的行)
  let tableEndIndex = -1;
  for (let i = tableStartIndex; i < lines.length; i++) {
    if (!lines[i].trim().startsWith("|") || !lines[i].trim().endsWith("|")) {
      tableEndIndex = i;
      break;
    }
  }

  if (tableEndIndex === -1) {
    tableEndIndex = lines.length;
  }

  // 提取表格内容
  const tableData = lines
    .slice(tableStartIndex, tableEndIndex)
    .join("\n")
    .trim();

  // 解析表格内容
  const rows = tableData.split("\n");
  const data: any[] = [];
  let headers: string[] = [];

  rows.forEach((row, rowIndex) => {
    // 去掉行首尾的 | 符号,并按 | 分割
    const cells = row
      .replace(/^\s*\|/g, "")
      .replace(/\|\s*$/g, "")
      .split(/\s*\|\s*/);

    // 跳过分隔线(假设分隔线的每个单元格都是由短横线组成)
    if (
      rowIndex > 0 &&
      cells.every(
        (cell) => cell.trim().replace(/-/g, "").length === 0, // 检查单元格内容是否只包含短横线
      )
    ) {
      return;
    }

    if (rowIndex === 0) {
      // 第一行是表头
      headers = cells.map((cell) => cell.trim());
    } else {
      // 数据行
      const rowData: any = {};
      cells.forEach((cell, cellIndex) => {
        if (cellIndex < headers.length) {
          rowData[headers[cellIndex]] = cell.trim();
        }
      });
      data.push(rowData);
    }
  });

  // 创建工作表
  const ws = XLSX.utils.json_to_sheet(data);
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, sheetName);

  // 生成文件并下载
  XLSX.writeFile(wb, `${sheetName}.xlsx`);
}

export function getExcelData(file: File): Promise<any[][]> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = (e) => {
      try {
        const data = e.target?.result;
        if (!data) {
          reject(new Error("Failed to read file data"));
          return;
        }
        const workbook = XLSX.read(data, { type: "array" });
        const firstSheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[firstSheetName];

        // 使用类型断言将 unknown[] 转换为 any[][]
        const jsonData = XLSX.utils.sheet_to_json(worksheet, {
          header: 1,
        }) as any[][];
        resolve(jsonData.slice(0, UPLOAD_FILE_MAX_LINE));
      } catch (error) {
        reject(error);
      }
    };
    reader.onerror = (error) => {
      reject(error);
    };

    reader.readAsArrayBuffer(file);
  });
}

export function htmlToExcel(htmlContent: string) {
  const parser = new DOMParser();
  const doc = parser.parseFromString(htmlContent, "text/html");
  const table = doc.querySelector("table");
  if (!table) {
    return [];
  }
  const rows = table.querySelectorAll("tr");
  const tableData: string[][] = [];
  // 提取表格数据(包含表头)
  rows.forEach((row) => {
    const cells = row.querySelectorAll("td, th");
    const rowData: string[] = [];
    cells.forEach((cell) => {
      rowData.push(cell.textContent?.trim() || "");
    });
    tableData.push(rowData);
  });
  // 使用二维数组直接创建工作表
  const ws = XLSX.utils.aoa_to_sheet(tableData);
  // 创建工作簿并保存
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
  XLSX.writeFile(wb, "export.xlsx");
}