refac
This commit is contained in:
@@ -12,25 +12,25 @@ import type { WorkSheet } from 'xlsx';
|
||||
|
||||
/** Convert column index (0-based) to Excel-style letter (A, B, …, Z, AA, AB, …) */
|
||||
const colLetter = (i: number): string => {
|
||||
let s = '';
|
||||
let n = i;
|
||||
while (n >= 0) {
|
||||
s = String.fromCharCode(65 + (n % 26)) + s;
|
||||
n = Math.floor(n / 26) - 1;
|
||||
}
|
||||
return s;
|
||||
let s = '';
|
||||
let n = i;
|
||||
while (n >= 0) {
|
||||
s = String.fromCharCode(65 + (n % 26)) + s;
|
||||
n = Math.floor(n / 26) - 1;
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
/** Escape HTML entities */
|
||||
const esc = (v: unknown): string => {
|
||||
if (v === null || v === undefined || v === '') return ' ';
|
||||
return String(v).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
if (v === null || v === undefined || v === '') return ' ';
|
||||
return String(v).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
};
|
||||
|
||||
export interface ExcelTableResult {
|
||||
html: string;
|
||||
rowCount: number;
|
||||
colCount: number;
|
||||
html: string;
|
||||
rowCount: number;
|
||||
colCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,49 +38,51 @@ export interface ExcelTableResult {
|
||||
* Uses sheet_to_json with header:1 for a raw 2D array.
|
||||
*/
|
||||
export async function excelToTable(worksheet: WorkSheet): Promise<ExcelTableResult> {
|
||||
const XLSX = await import('xlsx');
|
||||
const rows: unknown[][] = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: '' });
|
||||
const XLSX = await import('xlsx');
|
||||
const rows: unknown[][] = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: '' });
|
||||
|
||||
if (rows.length === 0) {
|
||||
return { html: '<table><tbody><tr><td> </td></tr></tbody></table>', rowCount: 0, colCount: 0 };
|
||||
}
|
||||
if (rows.length === 0) {
|
||||
return {
|
||||
html: '<table><tbody><tr><td> </td></tr></tbody></table>',
|
||||
rowCount: 0,
|
||||
colCount: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Determine column count from the widest row
|
||||
const colCount = rows.reduce((max, row) => Math.max(max, row.length), 0);
|
||||
const rowCount = rows.length;
|
||||
// Determine column count from the widest row
|
||||
const colCount = rows.reduce((max, row) => Math.max(max, row.length), 0);
|
||||
const rowCount = rows.length;
|
||||
|
||||
const parts: string[] = [];
|
||||
parts.push('<table>');
|
||||
const parts: string[] = [];
|
||||
parts.push('<table>');
|
||||
|
||||
// Column letter header row
|
||||
parts.push('<thead><tr>');
|
||||
parts.push('<th class="excel-row-num"></th>'); // corner cell
|
||||
for (let c = 0; c < colCount; c++) {
|
||||
parts.push(`<th class="excel-col-hdr">${colLetter(c)}</th>`);
|
||||
}
|
||||
parts.push('</tr></thead>');
|
||||
// Column letter header row
|
||||
parts.push('<thead><tr>');
|
||||
parts.push('<th class="excel-row-num"></th>'); // corner cell
|
||||
for (let c = 0; c < colCount; c++) {
|
||||
parts.push(`<th class="excel-col-hdr">${colLetter(c)}</th>`);
|
||||
}
|
||||
parts.push('</tr></thead>');
|
||||
|
||||
// Data rows
|
||||
parts.push('<tbody>');
|
||||
for (let r = 0; r < rowCount; r++) {
|
||||
const row = rows[r];
|
||||
parts.push('<tr>');
|
||||
parts.push(`<td class="excel-row-num">${r + 1}</td>`);
|
||||
for (let c = 0; c < colCount; c++) {
|
||||
const val = c < row.length ? row[c] : '';
|
||||
const isNum = typeof val === 'number';
|
||||
parts.push(
|
||||
`<td${isNum ? ' class="excel-num"' : ''}>${esc(val)}</td>`
|
||||
);
|
||||
}
|
||||
parts.push('</tr>');
|
||||
}
|
||||
parts.push('</tbody></table>');
|
||||
// Data rows
|
||||
parts.push('<tbody>');
|
||||
for (let r = 0; r < rowCount; r++) {
|
||||
const row = rows[r];
|
||||
parts.push('<tr>');
|
||||
parts.push(`<td class="excel-row-num">${r + 1}</td>`);
|
||||
for (let c = 0; c < colCount; c++) {
|
||||
const val = c < row.length ? row[c] : '';
|
||||
const isNum = typeof val === 'number';
|
||||
parts.push(`<td${isNum ? ' class="excel-num"' : ''}>${esc(val)}</td>`);
|
||||
}
|
||||
parts.push('</tr>');
|
||||
}
|
||||
parts.push('</tbody></table>');
|
||||
|
||||
const DOMPurify = (await import('dompurify')).default;
|
||||
return {
|
||||
html: DOMPurify.sanitize(parts.join('')),
|
||||
rowCount,
|
||||
colCount
|
||||
};
|
||||
const DOMPurify = (await import('dompurify')).default;
|
||||
return {
|
||||
html: DOMPurify.sanitize(parts.join('')),
|
||||
rowCount,
|
||||
colCount
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user