import type { MarkedExtension, Tokens } from "marked"; /** * 将表格数据转换为 CSV 格式字符串 * @param headers 表头数组 * @param rows 表格数据行 * @returns CSV 格式字符串 */ function tableToCSV(headers: string[], rows: string[][]): string { const escapeCell = (cell: string) => { // 如果单元格包含逗号、换行或引号,需要转义 if (cell.includes(',') || cell.includes('\n') || cell.includes('"') || cell.includes("#")) { return `"${cell.replace(/"/g, '""')}"`; } return cell; }; const headerLine = headers.map(escapeCell).join(','); const dataLines = rows.map(row => row.map(escapeCell).join(',')); return [headerLine, ...dataLines].join('\n'); } export default function markedTable(): MarkedExtension { return { renderer: { table(token: Tokens.Table) { // 检查表头是否包含 md-table-label const header = token.header; let roll = ''; const labelIndex = header.findIndex(cell => { if(cell.text === 'md-roll-label' || cell.text.match(/(\d+)?d\d+/)){ roll = ' roll=true'; return true; }else if(cell.text === 'md-remix-label'){ roll = ' roll=true remix=true'; return true; } return cell.text === 'md-table-label' || cell.text === 'label'; }); // 默认表格渲染 - 使用 marked 默认行为 if(labelIndex === -1) return false; const headers = token.header.map(cell => cell.text); headers[labelIndex] = 'label'; const rows = token.rows.map(row => row.map(cell => cell.text)); if(header.findIndex(header => header.text === 'body') < 0){ // 收集所有非 label 列的表头 const bodyColumns = headers .filter(cell => cell !== 'label'); // 构建 body 列的模板:**列名**:{{列名}}\n\n const bodyTemplate = bodyColumns .map(col => `**${col}**:{{${col}}}`) .join('\n\n'); headers.push('body'); rows.forEach(row => { row.push(bodyTemplate); }); } // 生成 CSV 数据 const csvData = tableToCSV(headers, rows); // 渲染为 md-table 组件,内联 CSV 数据 return `${csvData}\n`; } } }; }