feat(export): support XLSX comment headers

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-08-25 22:00:26 +08:00
1 parent 86f36e51ad
commit 46111850db
4 files changed
+311 -28

No files matched your search

@@ -2012,13 +2012,11 @@ async function exportDataXlsx(row: ObjectBrowserRow) {
try {
columnInfos = await api.getColumns(props.connection.id, props.database, schema || props.database, row.name, props.catalog);
const hasComments = hasXlsxHeaderComments(columnInfos.map((column) => column.comment));
const result = await showObjectBrowserXlsxHeaderDialog(hasComments);
const result = await showObjectBrowserXlsxHeaderDialog(hasXlsxHeaderComments(columnInfos.map((column) => column.comment)));
if (result === null) return;
headerMode = result;
} catch {
// Column fetch failed, fallback to export without comments
columnInfos = undefined;
// Export still works with field-name headers when column metadata is unavailable.
}
await exportTableData(row, "xlsx", columnInfos, headerMode);
@@ -0,0 +1,35 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const source = readFileSync(new URL("../ObjectBrowser.vue", import.meta.url), "utf8");
function functionBody(name: string): string {
const signature = new RegExp(`(?:async\\s+)?function\\s+${name}\\s*\\([^)]*\\)\\s*(?::\\s*[^\\{]+)?\\{`, "m").exec(source);
if (!signature) throw new Error(`Missing function ${name}`);
const bodyStart = signature.index + signature[0].length;
let depth = 1;
for (let index = bodyStart; index < source.length; index += 1) {
if (source[index] === "{") depth += 1;
else if (source[index] === "}") depth -= 1;
if (depth === 0) return source.slice(bodyStart, index);
}
throw new Error(`Unclosed function ${name}`);
}
describe("ObjectBrowser XLSX export", () => {
it("asks for a header mode before opening the save dialog when comments exist", () => {
const dialog = functionBody("showObjectBrowserXlsxHeaderDialog");
const exportDataXlsx = functionBody("exportDataXlsx");
expect(dialog).toContain('if (!hasComments) return Promise.resolve("name")');
expect(exportDataXlsx).toContain("hasXlsxHeaderComments(columnInfos.map((column) => column.comment))");
expect(exportDataXlsx.indexOf("await showObjectBrowserXlsxHeaderDialog(")).toBeLessThan(exportDataXlsx.indexOf('await exportTableData(row, "xlsx", columnInfos, headerMode)'));
});
it("falls back to field-name headers when column metadata is unavailable", () => {
const exportDataXlsx = functionBody("exportDataXlsx");
expect(exportDataXlsx).toContain("catch {\n // Export still works with field-name headers when column metadata is unavailable.\n }");
expect(exportDataXlsx).toContain('await exportTableData(row, "xlsx", columnInfos, headerMode)');
});
});