feat(platform): export history query results

This commit is contained in:
lingniu
2026-07-04 10:59:47 +08:00
parent 312382c95d
commit cd9d160f3e
6 changed files with 261 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
import { expect, test } from 'vitest';
import { buildCsv } from './csvExport';
test('builds csv with escaping and json object values', () => {
const csv = buildCsv(
[
{ title: 'VIN', value: (row: { vin: string; fields?: unknown }) => row.vin },
{ title: '字段', value: (row) => row.fields }
],
[{ vin: 'VIN"001', fields: { speed: 42 } }]
);
expect(csv).toBe('"VIN","字段"\n"VIN""001","{""speed"":42}"');
});

View File

@@ -0,0 +1,29 @@
export type CsvColumn<T> = {
title: string;
value: (row: T) => unknown;
};
function csvCell(value: unknown) {
if (value == null) return '';
const text = typeof value === 'object' ? JSON.stringify(value) : String(value);
return `"${text.replace(/"/g, '""')}"`;
}
export function buildCsv<T>(columns: CsvColumn<T>[], rows: T[]) {
return [
columns.map((item) => csvCell(item.title)).join(','),
...rows.map((row) => columns.map((column) => csvCell(column.value(row))).join(','))
].join('\n');
}
export function downloadCsv(filename: string, csv: string) {
const blob = new Blob(['\uFEFF', csv], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
}