返回 DeepSeek-Reasonix
WorkspaceCsvPreview.tsx
根目录 / desktop / frontend / src / components / WorkspaceCsvPreview.tsx
1 import { useMemo } from "react";
2
3 const MAX_ROWS = 2_000;
4 const MAX_COLUMNS = 200;
5
6 export function parseDelimitedText(input: string, delimiter: "," | "\t"): string[][] {
7 const rows: string[][] = [];
8 let row: string[] = [];
9 let cell = "";
10 let quoted = false;
11 for (let index = 0; index < input.length; index += 1) {
12 const char = input[index];
13 if (quoted) {
14 if (char === '"') {
15 if (input[index + 1] === '"') {
16 cell += '"';
17 index += 1;
18 } else {
19 quoted = false;
20 }
21 } else {
22 cell += char;
23 }
24 continue;
25 }
26 if (char === '"' && cell === "") {
27 quoted = true;
28 } else if (char === delimiter) {
29 row.push(cell);
30 cell = "";
31 } else if (char === "\n") {
32 row.push(cell.replace(/\r$/, ""));
33 rows.push(row.slice(0, MAX_COLUMNS));
34 row = [];
35 cell = "";
36 if (rows.length >= MAX_ROWS) break;
37 } else {
38 cell += char;
39 }
40 }
41 if (rows.length < MAX_ROWS && (cell !== "" || row.length > 0)) {
42 row.push(cell.replace(/\r$/, ""));
43 rows.push(row.slice(0, MAX_COLUMNS));
44 }
45 return rows;
46 }
47
48 export function WorkspaceCsvPreview({ body, delimiter }: { body: string; delimiter: "," | "\t" }) {
49 const rows = useMemo(() => parseDelimitedText(body, delimiter), [body, delimiter]);
50 if (rows.length === 0) return <div className="workspace-empty">—</div>;
51 const [header, ...data] = rows;
52 return (
53 <div className="workspace-csv-preview">
54 <table>
55 <thead><tr>{header.map((value, index) => <th key={index}>{value}</th>)}</tr></thead>
56 <tbody>
57 {data.map((values, rowIndex) => (
58 <tr key={rowIndex}>{header.map((_, columnIndex) => <td key={columnIndex}>{values[columnIndex] ?? ""}</td>)}</tr>
59 ))}
60 </tbody>
61 </table>
62 </div>
63 );
64 }
65
65 lines Plain Text