mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
feat(mongodb): support fine-grained export for rows, columns, and cell regions
This commit is contained in:
@@ -6276,6 +6276,10 @@ function affectedRowIds(): number[] {
|
||||
if (hasRowSelection.value && selectedRowCount.value > 0) {
|
||||
return [...selectedRowIds.value].filter((rowId) => !getRowItem(rowId)?.isDraft);
|
||||
}
|
||||
if (contextCell.value?.col === -1 && contextCell.value.rowId !== undefined) {
|
||||
const item = getRowItem(contextCell.value.rowId);
|
||||
return item && !item.isDraft ? [item.id] : [];
|
||||
}
|
||||
const range = selectedRange.value;
|
||||
if (range && range.startRow !== range.endRow) {
|
||||
return displayRowRefs.value
|
||||
@@ -6286,6 +6290,41 @@ function affectedRowIds(): number[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
function targetColumnIndexes(): number[] {
|
||||
if (hasColumnSelection.value && selectedColumnIndexes.value.size > 0) {
|
||||
return [...selectedColumnIndexes.value].sort((a, b) => a - b);
|
||||
}
|
||||
if (contextHeaderVisibleColIdx.value !== null && contextHeaderVisibleColIdx.value !== undefined) {
|
||||
return [contextHeaderVisibleColIdx.value];
|
||||
}
|
||||
if (contextHeaderColumnIndex.value !== null) {
|
||||
const idx = visibleColumnIndexes.value.indexOf(contextHeaderColumnIndex.value);
|
||||
if (idx >= 0) return [idx];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
const hasSelectedRows = computed(() => affectedRowIds().length > 0);
|
||||
const hasSelectedColumns = computed(() => targetColumnIndexes().length > 0);
|
||||
|
||||
function activeCellSelectionTarget(): { rowIds: number[]; columnIndexes: number[] } | null {
|
||||
if (selectedCellMatrix.value && selectedCellMatrix.value.rowIndexes.length > 0 && selectedCellMatrix.value.columnIndexes.length > 0) {
|
||||
const rowIds = selectedCellMatrix.value.rowIndexes
|
||||
.map((rowIndex) => displayItemAt(rowIndex))
|
||||
.filter((item): item is RowItem => !!item && !item.isDraft)
|
||||
.map((item) => item.id);
|
||||
if (rowIds.length > 0) {
|
||||
return {
|
||||
rowIds,
|
||||
columnIndexes: selectedCellMatrix.value.columnIndexes.slice(),
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasSelectedCells = computed(() => !hasRowSelection.value && !hasColumnSelection.value && hasCellSelection.value && !!activeCellSelectionTarget());
|
||||
|
||||
function deletableRowIds(rowIds: number[]): number[] {
|
||||
const eligible = rowIds.filter((rowId) => canDeleteRowItem(getRowItem(rowId)));
|
||||
// A custom row-safety predicate is an all-or-nothing contract. Never silently
|
||||
@@ -6297,55 +6336,170 @@ function deletableRowIds(rowIds: number[]): number[] {
|
||||
function exportSelectedRowsCsv() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportCsv(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportCsv({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedRowsXlsx() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportXlsx(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportXlsx({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function openSelectedRowsXlsx() {
|
||||
const cellTarget = activeCellSelectionTarget();
|
||||
if (cellTarget) return openXlsx(cellTarget);
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return openXlsx();
|
||||
return openXlsx(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
if (rowIds.length === 0 && columnIndexes === undefined) return openXlsx();
|
||||
return openXlsx({ rowIds: rowIds.length > 0 ? rowIds : undefined, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedRowsXlsxWithSql() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportXlsxWithSql(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportXlsxWithSql({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedRowsJson() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportJson(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportJson({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedRowsMarkdown() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportMarkdown(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportMarkdown({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedRowsHtml() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportHtml(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportHtml({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedRowsSql() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportSql(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportSql({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedRowsTxt() {
|
||||
const rowIds = affectedRowIds();
|
||||
if (rowIds.length === 0) return;
|
||||
return exportTxt(rowIds);
|
||||
const columnIndexes = hasSelectedColumns.value ? targetColumnIndexes() : undefined;
|
||||
return exportTxt({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsCsv() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportCsv({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsXlsx() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportXlsx({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsXlsxWithSql() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportXlsxWithSql({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsJson() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportJson({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsMarkdown() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportMarkdown({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsHtml() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportHtml({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsSql() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportSql({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedColumnsTxt() {
|
||||
const columnIndexes = targetColumnIndexes();
|
||||
if (columnIndexes.length === 0) return;
|
||||
const rowIds = hasSelectedRows.value ? affectedRowIds() : undefined;
|
||||
return exportTxt({ rowIds, columnIndexes });
|
||||
}
|
||||
|
||||
function exportSelectedCellsCsv() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportCsv(target);
|
||||
}
|
||||
|
||||
function exportSelectedCellsXlsx() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportXlsx(target);
|
||||
}
|
||||
|
||||
function exportSelectedCellsXlsxWithSql() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportXlsxWithSql(target);
|
||||
}
|
||||
|
||||
function exportSelectedCellsJson() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportJson(target);
|
||||
}
|
||||
|
||||
function exportSelectedCellsMarkdown() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportMarkdown(target);
|
||||
}
|
||||
|
||||
function exportSelectedCellsHtml() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportHtml(target);
|
||||
}
|
||||
|
||||
function exportSelectedCellsSql() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportSql(target);
|
||||
}
|
||||
|
||||
function exportSelectedCellsTxt() {
|
||||
const target = activeCellSelectionTarget();
|
||||
if (!target) return;
|
||||
return exportTxt(target);
|
||||
}
|
||||
|
||||
function executePreviewAction(action: PreviewAction) {
|
||||
@@ -8552,7 +8706,7 @@ const exportMenuItems = computed(() => {
|
||||
: []),
|
||||
]
|
||||
: [];
|
||||
const selectedItems = isMultiRow.value
|
||||
const selectedRowsItems = hasSelectedRows.value
|
||||
? [
|
||||
{
|
||||
value: "selected-csv",
|
||||
@@ -8581,6 +8735,64 @@ const exportMenuItems = computed(() => {
|
||||
{ value: "selected-txt", label: t("grid.exportSelectedRowsTxt") },
|
||||
]
|
||||
: [];
|
||||
const selectedColumnsItems = hasSelectedColumns.value
|
||||
? [
|
||||
{
|
||||
value: "selected-columns-csv",
|
||||
label: t("grid.exportSelectedColumnsCsv"),
|
||||
separatorBefore: true,
|
||||
},
|
||||
{ value: "selected-columns-xlsx", label: t("grid.exportSelectedColumnsXlsx") },
|
||||
...(canIncludeSql
|
||||
? [
|
||||
{
|
||||
value: "selected-columns-xlsx-with-sql",
|
||||
label: t("grid.exportSelectedColumnsXlsxWithSql"),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ value: "selected-columns-json", label: t("grid.exportSelectedColumnsJson") },
|
||||
{
|
||||
value: "selected-columns-markdown",
|
||||
label: t("grid.exportSelectedColumnsMarkdown"),
|
||||
},
|
||||
{
|
||||
value: "selected-columns-html",
|
||||
label: t("grid.exportSelectedColumnsHtml"),
|
||||
},
|
||||
{ value: "selected-columns-sql", label: t("grid.exportSelectedColumnsSql") },
|
||||
{ value: "selected-columns-txt", label: t("grid.exportSelectedColumnsTxt") },
|
||||
]
|
||||
: [];
|
||||
const selectedCellsItems = hasSelectedCells.value
|
||||
? [
|
||||
{
|
||||
value: "selected-cells-csv",
|
||||
label: t("grid.exportSelectedCellsCsv"),
|
||||
separatorBefore: true,
|
||||
},
|
||||
{ value: "selected-cells-xlsx", label: t("grid.exportSelectedCellsXlsx") },
|
||||
...(canIncludeSql
|
||||
? [
|
||||
{
|
||||
value: "selected-cells-xlsx-with-sql",
|
||||
label: t("grid.exportSelectedCellsXlsxWithSql"),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{ value: "selected-cells-json", label: t("grid.exportSelectedCellsJson") },
|
||||
{
|
||||
value: "selected-cells-markdown",
|
||||
label: t("grid.exportSelectedCellsMarkdown"),
|
||||
},
|
||||
{
|
||||
value: "selected-cells-html",
|
||||
label: t("grid.exportSelectedCellsHtml"),
|
||||
},
|
||||
{ value: "selected-cells-sql", label: t("grid.exportSelectedCellsSql") },
|
||||
{ value: "selected-cells-txt", label: t("grid.exportSelectedCellsTxt") },
|
||||
]
|
||||
: [];
|
||||
|
||||
if (!hasFullResultExport) {
|
||||
return [
|
||||
@@ -8593,7 +8805,9 @@ const exportMenuItems = computed(() => {
|
||||
{ value: "sql", label: t("grid.exportSql") },
|
||||
{ value: "txt", label: t("grid.exportTxt") },
|
||||
...allResultItems,
|
||||
...selectedItems,
|
||||
...selectedRowsItems,
|
||||
...selectedColumnsItems,
|
||||
...selectedCellsItems,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -8633,7 +8847,9 @@ const exportMenuItems = computed(() => {
|
||||
{ value: "sql", label: t("grid.exportCurrentResultSql") },
|
||||
{ value: "txt", label: t("grid.exportCurrentResultTxt") },
|
||||
...allResultItems,
|
||||
...selectedItems,
|
||||
...selectedRowsItems,
|
||||
...selectedColumnsItems,
|
||||
...selectedCellsItems,
|
||||
];
|
||||
});
|
||||
|
||||
@@ -8669,6 +8885,22 @@ function selectExportMenuItem(value: string) {
|
||||
"selected-html": exportSelectedRowsHtml,
|
||||
"selected-sql": exportSelectedRowsSql,
|
||||
"selected-txt": exportSelectedRowsTxt,
|
||||
"selected-columns-csv": exportSelectedColumnsCsv,
|
||||
"selected-columns-xlsx": exportSelectedColumnsXlsx,
|
||||
"selected-columns-xlsx-with-sql": exportSelectedColumnsXlsxWithSql,
|
||||
"selected-columns-json": exportSelectedColumnsJson,
|
||||
"selected-columns-markdown": exportSelectedColumnsMarkdown,
|
||||
"selected-columns-html": exportSelectedColumnsHtml,
|
||||
"selected-columns-sql": exportSelectedColumnsSql,
|
||||
"selected-columns-txt": exportSelectedColumnsTxt,
|
||||
"selected-cells-csv": exportSelectedCellsCsv,
|
||||
"selected-cells-xlsx": exportSelectedCellsXlsx,
|
||||
"selected-cells-xlsx-with-sql": exportSelectedCellsXlsxWithSql,
|
||||
"selected-cells-json": exportSelectedCellsJson,
|
||||
"selected-cells-markdown": exportSelectedCellsMarkdown,
|
||||
"selected-cells-html": exportSelectedCellsHtml,
|
||||
"selected-cells-sql": exportSelectedCellsSql,
|
||||
"selected-cells-txt": exportSelectedCellsTxt,
|
||||
};
|
||||
actions[value]?.();
|
||||
}
|
||||
@@ -12736,7 +12968,7 @@ function exportSubmenu(): ContextMenuItem {
|
||||
action: exportXlsxWithSql,
|
||||
});
|
||||
}
|
||||
if (isMultiRow.value) {
|
||||
if (hasSelectedRows.value) {
|
||||
items.push(
|
||||
{ label: "", separator: true },
|
||||
{ label: t("grid.exportSelectedRowsCsv"), action: exportSelectedRowsCsv },
|
||||
@@ -12768,6 +13000,70 @@ function exportSubmenu(): ContextMenuItem {
|
||||
{ label: t("grid.exportSelectedRowsTxt"), action: exportSelectedRowsTxt },
|
||||
);
|
||||
}
|
||||
if (hasSelectedColumns.value) {
|
||||
items.push(
|
||||
{ label: "", separator: true },
|
||||
{ label: t("grid.exportSelectedColumnsCsv"), action: exportSelectedColumnsCsv },
|
||||
{
|
||||
label: t("grid.exportSelectedColumnsXlsx"),
|
||||
action: exportSelectedColumnsXlsx,
|
||||
},
|
||||
...(props.context === "results" && !!(props.exportSql || props.sql)?.trim()
|
||||
? [
|
||||
{
|
||||
label: t("grid.exportSelectedColumnsXlsxWithSql"),
|
||||
action: exportSelectedColumnsXlsxWithSql,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: t("grid.exportSelectedColumnsJson"),
|
||||
action: exportSelectedColumnsJson,
|
||||
},
|
||||
{
|
||||
label: t("grid.exportSelectedColumnsMarkdown"),
|
||||
action: exportSelectedColumnsMarkdown,
|
||||
},
|
||||
{
|
||||
label: t("grid.exportSelectedColumnsHtml"),
|
||||
action: exportSelectedColumnsHtml,
|
||||
},
|
||||
{ label: t("grid.exportSelectedColumnsSql"), action: exportSelectedColumnsSql },
|
||||
{ label: t("grid.exportSelectedColumnsTxt"), action: exportSelectedColumnsTxt },
|
||||
);
|
||||
}
|
||||
if (hasSelectedCells.value) {
|
||||
items.push(
|
||||
{ label: "", separator: true },
|
||||
{ label: t("grid.exportSelectedCellsCsv"), action: exportSelectedCellsCsv },
|
||||
{
|
||||
label: t("grid.exportSelectedCellsXlsx"),
|
||||
action: exportSelectedCellsXlsx,
|
||||
},
|
||||
...(props.context === "results" && !!(props.exportSql || props.sql)?.trim()
|
||||
? [
|
||||
{
|
||||
label: t("grid.exportSelectedCellsXlsxWithSql"),
|
||||
action: exportSelectedCellsXlsxWithSql,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: t("grid.exportSelectedCellsJson"),
|
||||
action: exportSelectedCellsJson,
|
||||
},
|
||||
{
|
||||
label: t("grid.exportSelectedCellsMarkdown"),
|
||||
action: exportSelectedCellsMarkdown,
|
||||
},
|
||||
{
|
||||
label: t("grid.exportSelectedCellsHtml"),
|
||||
action: exportSelectedCellsHtml,
|
||||
},
|
||||
{ label: t("grid.exportSelectedCellsSql"), action: exportSelectedCellsSql },
|
||||
{ label: t("grid.exportSelectedCellsTxt"), action: exportSelectedCellsTxt },
|
||||
);
|
||||
}
|
||||
const extractorItems = buildExtractorContextItems("export");
|
||||
if (extractorItems.length > 0) {
|
||||
items.push({ label: "", separator: true }, ...extractorItems);
|
||||
|
||||
@@ -88,6 +88,7 @@ function createMongoExportState(options: {
|
||||
hasCompleteLocalResult?: UseDataGridExportOptions["hasCompleteLocalResult"];
|
||||
completeLocalResult?: UseDataGridExportOptions["completeLocalResult"];
|
||||
externalCellValue?: UseDataGridExportOptions["externalCellValue"];
|
||||
visibleColumnIndexes?: number[];
|
||||
}) {
|
||||
const items = options.items ?? [options.item];
|
||||
const selectedRowIds = options.selectedRowIds ?? new Set<number>();
|
||||
@@ -121,6 +122,7 @@ function createMongoExportState(options: {
|
||||
hasCompleteLocalResult: options.hasCompleteLocalResult,
|
||||
completeLocalResult: options.completeLocalResult,
|
||||
externalCellValue: options.externalCellValue,
|
||||
visibleColumnIndexes: options.visibleColumnIndexes ? computed(() => options.visibleColumnIndexes) : undefined,
|
||||
};
|
||||
return useDataGridExport(state);
|
||||
}
|
||||
@@ -1805,6 +1807,74 @@ describe("useDataGridExport prepared row statements", () => {
|
||||
expect(exportQueryResultCsv).toHaveBeenLastCalledWith(expect.any(String), ["name"], [["Visible"], ["Other"]], expect.anything(), expect.anything());
|
||||
});
|
||||
|
||||
it("exports duplicate-named columns by source position from the full result", async () => {
|
||||
setActivePinia(createPinia());
|
||||
const state = createMongoExportState({
|
||||
columns: ["id", "id"],
|
||||
item: { ...row(["1", "one"]), sourceIndex: 0 },
|
||||
mongoDocuments: [{ id: 1 }],
|
||||
visibleColumnIndexes: [0, 1],
|
||||
fullExportResult: async () => ({
|
||||
columns: ["id", "id"],
|
||||
column_types: ["int", "varchar"],
|
||||
rows: [
|
||||
["1", "one"],
|
||||
["2", "two"],
|
||||
],
|
||||
affected_rows: 2,
|
||||
execution_time_ms: 1,
|
||||
}),
|
||||
});
|
||||
|
||||
await state.exportCsv();
|
||||
|
||||
expect(exportQueryResultCsv).toHaveBeenLastCalledWith(
|
||||
expect.any(String),
|
||||
["id", "id"],
|
||||
[
|
||||
["1", "one"],
|
||||
["2", "two"],
|
||||
],
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("exports duplicate-named columns by source position when the complete result is local", async () => {
|
||||
setActivePinia(createPinia());
|
||||
const completeLocalResult = {
|
||||
columns: ["id", "id"],
|
||||
column_types: ["int", "varchar"],
|
||||
rows: [
|
||||
["1", "one"],
|
||||
["2", "two"],
|
||||
],
|
||||
affected_rows: 2,
|
||||
execution_time_ms: 1,
|
||||
};
|
||||
const state = createMongoExportState({
|
||||
columns: ["id", "id"],
|
||||
item: { ...row(["1", "one"]), sourceIndex: 0 },
|
||||
mongoDocuments: [{ id: 1 }],
|
||||
visibleColumnIndexes: [0, 1],
|
||||
hasCompleteLocalResult: computed(() => true),
|
||||
completeLocalResult: computed(() => completeLocalResult),
|
||||
});
|
||||
|
||||
await state.exportCsv();
|
||||
|
||||
expect(exportQueryResultCsv).toHaveBeenLastCalledWith(
|
||||
expect.any(String),
|
||||
["id", "id"],
|
||||
[
|
||||
["1", "one"],
|
||||
["2", "two"],
|
||||
],
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it("passes the CSV NULL setting to the export call", async () => {
|
||||
setActivePinia(createPinia());
|
||||
const state = createMongoExportState({
|
||||
@@ -1869,6 +1939,166 @@ describe("useDataGridExport prepared row statements", () => {
|
||||
|
||||
expect(exportQueryResultJson).toHaveBeenCalledWith(expect.any(String), columns, [[mongoCopyDocument._id, mongoCopyDocument.valueMap, mongoCopyDocument.createdTime]]);
|
||||
});
|
||||
|
||||
it("preserves Mongo Extended JSON when exporting a single selected row", async () => {
|
||||
const columns = ["_id", "valueMap", "createdTime"];
|
||||
const doc1 = {
|
||||
_id: { $oid: "6a9fb51db2f0c46b94002f26" },
|
||||
valueMap: { field1: "A", count: { $numberLong: "1234567890123" } },
|
||||
createdTime: { $date: "2026-09-08T07:11:25.458Z" },
|
||||
};
|
||||
const doc2 = {
|
||||
_id: { $oid: "6a9fb51db2f0c46b94002f27" },
|
||||
valueMap: { field1: "B", count: { $numberLong: "9876543210987" } },
|
||||
createdTime: { $date: "2026-09-09T08:00:00.000Z" },
|
||||
};
|
||||
const item1 = { ...row(["6a9fb51db2f0c46b94002f26", JSON.stringify(doc1.valueMap), 'ISODate("2026-09-08T07:11:25.458Z")']), id: 101, sourceIndex: 0 };
|
||||
const item2 = { ...row(["6a9fb51db2f0c46b94002f27", JSON.stringify(doc2.valueMap), 'ISODate("2026-09-09T08:00:00.000Z")']), id: 102, sourceIndex: 1 };
|
||||
const state = createMongoExportState({
|
||||
columns,
|
||||
item: item1,
|
||||
items: [item1, item2],
|
||||
mongoDocuments: [doc1, doc2],
|
||||
selectedRowIds: new Set([102]),
|
||||
});
|
||||
|
||||
await state.exportJson([102]);
|
||||
|
||||
expect(exportQueryResultJson).toHaveBeenCalledWith(expect.any(String), columns, [[doc2._id, doc2.valueMap, doc2.createdTime]]);
|
||||
});
|
||||
|
||||
it("preserves Mongo Extended JSON when exporting selected columns", async () => {
|
||||
const columns = ["_id", "valueMap", "createdTime"];
|
||||
const doc1 = {
|
||||
_id: { $oid: "6a9fb51db2f0c46b94002f26" },
|
||||
valueMap: { field1: "A" },
|
||||
createdTime: { $date: "2026-09-08T07:11:25.458Z" },
|
||||
};
|
||||
const item1 = { ...row(["6a9fb51db2f0c46b94002f26", JSON.stringify(doc1.valueMap), 'ISODate("2026-09-08T07:11:25.458Z")']), id: 101, sourceIndex: 0 };
|
||||
const state = createMongoExportState({
|
||||
columns,
|
||||
item: item1,
|
||||
items: [item1],
|
||||
mongoDocuments: [doc1],
|
||||
});
|
||||
|
||||
await state.exportJson({ columnIndexes: [1, 2] });
|
||||
|
||||
expect(exportQueryResultJson).toHaveBeenCalledWith(expect.any(String), ["valueMap", "createdTime"], [[doc1.valueMap, doc1.createdTime]]);
|
||||
});
|
||||
|
||||
it("preserves Mongo Extended JSON when exporting selected row and column intersection", async () => {
|
||||
const columns = ["_id", "valueMap", "createdTime"];
|
||||
const doc1 = {
|
||||
_id: { $oid: "6a9fb51db2f0c46b94002f26" },
|
||||
valueMap: { field1: "A" },
|
||||
createdTime: { $date: "2026-09-08T07:11:25.458Z" },
|
||||
};
|
||||
const doc2 = {
|
||||
_id: { $oid: "6a9fb51db2f0c46b94002f27" },
|
||||
valueMap: { field1: "B" },
|
||||
createdTime: { $date: "2026-09-09T08:00:00.000Z" },
|
||||
};
|
||||
const item1 = { ...row(["6a9fb51db2f0c46b94002f26", JSON.stringify(doc1.valueMap), 'ISODate("2026-09-08T07:11:25.458Z")']), id: 101, sourceIndex: 0 };
|
||||
const item2 = { ...row(["6a9fb51db2f0c46b94002f27", JSON.stringify(doc2.valueMap), 'ISODate("2026-09-09T08:00:00.000Z")']), id: 102, sourceIndex: 1 };
|
||||
const state = createMongoExportState({
|
||||
columns,
|
||||
item: item1,
|
||||
items: [item1, item2],
|
||||
mongoDocuments: [doc1, doc2],
|
||||
});
|
||||
|
||||
await state.exportJson({ rowIds: [102], columnIndexes: [0, 2] });
|
||||
|
||||
expect(exportQueryResultJson).toHaveBeenCalledWith(expect.any(String), ["_id", "createdTime"], [[doc2._id, doc2.createdTime]]);
|
||||
});
|
||||
|
||||
it("exports non-Mongo CSV with selected columns and rows", async () => {
|
||||
setActivePinia(createPinia());
|
||||
const table: DataGridTableMeta = {
|
||||
tableName: "users",
|
||||
primaryKeys: ["id"],
|
||||
columns: [
|
||||
{ name: "id", data_type: "int" },
|
||||
{ name: "name", data_type: "varchar" },
|
||||
{ name: "email", data_type: "varchar" },
|
||||
],
|
||||
};
|
||||
const rowDataList = [
|
||||
[1, "Alice", "alice@example.com"],
|
||||
[2, "Bob", "bob@example.com"],
|
||||
[3, "Charlie", "charlie@example.com"],
|
||||
];
|
||||
const state = createExportState(table, ["id", "name", "email"], undefined, undefined, undefined, rowDataList, [2]);
|
||||
|
||||
await state.exportCsv({ rowIds: [2], columnIndexes: [1, 2] });
|
||||
|
||||
expect(exportQueryResultCsv).toHaveBeenLastCalledWith(expect.any(String), ["name", "email"], [["Bob", "bob@example.com"]], expect.anything(), expect.anything());
|
||||
});
|
||||
|
||||
it("exports non-Mongo JSON with selected columns and rows", async () => {
|
||||
setActivePinia(createPinia());
|
||||
const table: DataGridTableMeta = {
|
||||
tableName: "users",
|
||||
primaryKeys: ["id"],
|
||||
columns: [
|
||||
{ name: "id", data_type: "int" },
|
||||
{ name: "name", data_type: "varchar" },
|
||||
{ name: "email", data_type: "varchar" },
|
||||
],
|
||||
};
|
||||
const rowDataList = [
|
||||
[1, "Alice", "alice@example.com"],
|
||||
[2, "Bob", "bob@example.com"],
|
||||
[3, "Charlie", "charlie@example.com"],
|
||||
];
|
||||
const state = createExportState(table, ["id", "name", "email"], undefined, undefined, undefined, rowDataList, [1, 3]);
|
||||
|
||||
await state.exportJson({ rowIds: [1, 3], columnIndexes: [1] });
|
||||
|
||||
expect(exportQueryResultJson).toHaveBeenLastCalledWith(expect.any(String), ["name"], [["Alice"], ["Charlie"]]);
|
||||
});
|
||||
|
||||
it("exports rectangular cell selection matrix with correct rows and columns", async () => {
|
||||
setActivePinia(createPinia());
|
||||
const table: DataGridTableMeta = {
|
||||
tableName: "users",
|
||||
primaryKeys: ["id"],
|
||||
columns: [
|
||||
{ name: "id", data_type: "int" },
|
||||
{ name: "name", data_type: "varchar" },
|
||||
{ name: "email", data_type: "varchar" },
|
||||
],
|
||||
};
|
||||
const rowDataList = [
|
||||
[1, "Alice", "alice@example.com"],
|
||||
[2, "Bob", "bob@example.com"],
|
||||
[3, "Charlie", "charlie@example.com"],
|
||||
];
|
||||
const matrix: CellSelectionMatrix = {
|
||||
rowIndexes: [0, 1],
|
||||
columnIndexes: [1, 2],
|
||||
columns: ["name", "email"],
|
||||
rows: [
|
||||
["Alice", "alice@example.com"],
|
||||
["Bob", "bob@example.com"],
|
||||
],
|
||||
};
|
||||
const state = createExportState(table, ["id", "name", "email"], matrix, undefined, undefined, rowDataList);
|
||||
|
||||
await state.exportCsv({ rowIds: [1, 2], columnIndexes: [1, 2] });
|
||||
|
||||
expect(exportQueryResultCsv).toHaveBeenLastCalledWith(
|
||||
expect.any(String),
|
||||
["name", "email"],
|
||||
[
|
||||
["Alice", "alice@example.com"],
|
||||
["Bob", "bob@example.com"],
|
||||
],
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// issue #7471:文本型 MySQL VARBINARY 复制单元格/多选/整行时,外部剪贴板应呈现原始字符串;
|
||||
|
||||
@@ -172,6 +172,21 @@ describe("SQL export column selection across entrypoints", () => {
|
||||
expect(mocks.toast).toHaveBeenCalledWith("grid.exportFailed", 5000);
|
||||
});
|
||||
|
||||
it("resolves subset type fallbacks by name instead of subset position", async () => {
|
||||
const choices = sqlExportColumnChoices(["name"]);
|
||||
vi.mocked(showSqlInsertModeDialog).mockResolvedValue({ insertMode: "single", selectedColumns: [choices[0]!] });
|
||||
const state = useDataGridExport(
|
||||
createOptions({
|
||||
columns: computed(() => ["name"]),
|
||||
allColumns: computed(() => ["id", "name"]),
|
||||
allColumnTypes: computed(() => ["tsvector", "text"]),
|
||||
tableMeta: computed(() => ({ tableName: "users", primaryKeys: [], columns: [] })),
|
||||
}),
|
||||
);
|
||||
await state.exportSql({ columnIndexes: [0] });
|
||||
expect(showSqlInsertModeDialog).toHaveBeenCalledWith({ allowSplit: false, columns: choices });
|
||||
});
|
||||
|
||||
it("keeps legacy omitted selection behavior", async () => {
|
||||
vi.mocked(showSqlInsertModeDialog).mockResolvedValue({ insertMode: "batch" });
|
||||
await useDataGridExport(createOptions()).exportCurrentPageSql();
|
||||
|
||||
@@ -58,6 +58,23 @@ const FORMAT_META: Record<string, { ext: string; label: string }> = {
|
||||
txt: { ext: "txt", label: "Text" },
|
||||
};
|
||||
|
||||
export interface DataGridExportTarget {
|
||||
rowIds?: number[];
|
||||
columnIndexes?: number[];
|
||||
}
|
||||
|
||||
export type ExportTargetParam = number[] | DataGridExportTarget;
|
||||
|
||||
export function normalizeExportTarget(target?: ExportTargetParam, columnIndexesParam?: number[]): { rowIds?: number[]; columnIndexes?: number[] } {
|
||||
if (Array.isArray(target)) {
|
||||
return { rowIds: target, columnIndexes: columnIndexesParam };
|
||||
}
|
||||
return {
|
||||
rowIds: target?.rowIds,
|
||||
columnIndexes: columnIndexesParam ?? target?.columnIndexes,
|
||||
};
|
||||
}
|
||||
|
||||
interface RowItem {
|
||||
id: number;
|
||||
sourceIndex?: number;
|
||||
@@ -346,10 +363,15 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeCompleteLocalResult(result: QueryResult): { columns: string[]; columnTypes: string[]; columnComments: Array<string | undefined>; rows: CellValue[][]; mongoCopyDocuments?: unknown[]; spatialColumns?: QueryResult["spatial_columns"]; spatialValues?: QueryResult["spatial_values"] } {
|
||||
function normalizeCompleteLocalResult(
|
||||
result: QueryResult,
|
||||
targetCols?: readonly string[],
|
||||
sourceIndexesParam?: readonly number[],
|
||||
): { columns: string[]; columnTypes: string[]; columnComments: Array<string | undefined>; rows: CellValue[][]; mongoCopyDocuments?: unknown[]; spatialColumns?: QueryResult["spatial_columns"]; spatialValues?: QueryResult["spatial_values"] } {
|
||||
const editorSettings = useSettingsStore().editorSettings;
|
||||
if (databaseType.value === "mongodb") {
|
||||
const projected = projectResultColumns(result, columns.value);
|
||||
const isSubset = targetCols !== undefined && (targetCols.length !== result.columns.length || !targetCols.every((col, i) => col === result.columns[i]));
|
||||
if (databaseType.value === "mongodb" || isSubset) {
|
||||
const projected = projectResultColumns(result, targetCols ?? columns.value, sourceIndexesParam);
|
||||
const rows = editorSettings.exportRowLimitEnabled ? projected.rows.slice(0, editorSettings.exportRowLimit) : projected.rows;
|
||||
return {
|
||||
columns: projected.columns,
|
||||
@@ -397,6 +419,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
function projectResultColumns(
|
||||
result: QueryResult,
|
||||
targetColumns: readonly string[],
|
||||
sourceIndexesParam?: readonly number[],
|
||||
): {
|
||||
columns: string[];
|
||||
columnTypes: string[];
|
||||
@@ -404,7 +427,9 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
spatialColumns?: QueryResult["spatial_columns"];
|
||||
spatialValues?: QueryResult["spatial_values"];
|
||||
} {
|
||||
const sourceIndexes = targetColumns.map((column) => result.columns.indexOf(column));
|
||||
// Name lookup resolves duplicate column names (e.g. SELECT a.id, b.id) to
|
||||
// the first occurrence; prefer the caller's explicit source indexes.
|
||||
const sourceIndexes = sourceIndexesParam?.length === targetColumns.length ? sourceIndexesParam.map((index) => (index >= 0 && index < result.columns.length ? index : -1)) : targetColumns.map((column) => result.columns.indexOf(column));
|
||||
const targetIndexBySource = new Map<number, number>();
|
||||
sourceIndexes.forEach((sourceIndex, targetIndex) => {
|
||||
if (sourceIndex >= 0) targetIndexBySource.set(sourceIndex, targetIndex);
|
||||
@@ -421,25 +446,29 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
};
|
||||
}
|
||||
|
||||
function mongoLocalRowsForJson(items: RowItem[]): CellValue[][] {
|
||||
function mongoLocalRowsForJson(items: RowItem[], targetCols: readonly string[] = columns.value): CellValue[][] {
|
||||
return items.map((item) => {
|
||||
const document = rowToJsonObject(item);
|
||||
return columns.value.map((column) => (document[column] as CellValue) ?? null);
|
||||
return targetCols.map((column) => (document[column] as CellValue) ?? null);
|
||||
});
|
||||
}
|
||||
|
||||
function externalizeRows(rows: CellValue[][]): CellValue[][] {
|
||||
function externalizeRows(rows: CellValue[][], columnIndexes?: number[]): CellValue[][] {
|
||||
if (columnIndexes) {
|
||||
return rows.map((row) => columnIndexes.map((colIdx) => (options.externalCellValue ? options.externalCellValue(row[colIdx], colIdx) : row[colIdx])));
|
||||
}
|
||||
if (!options.externalCellValue) return rows;
|
||||
return rows.map((row) => row.map((value, columnIndex) => options.externalCellValue!(value, columnIndex)));
|
||||
}
|
||||
|
||||
async function resultToExport(
|
||||
rowIds?: number[],
|
||||
target?: ExportTargetParam,
|
||||
onProgress?: (info: { rowsExported: number; totalRows: number | null }) => void,
|
||||
useFullExport = true,
|
||||
formatDateTime = true,
|
||||
headerMode: XlsxHeaderMode = "name",
|
||||
preserveMongoExtendedJson = false,
|
||||
columnIndexesParam?: number[],
|
||||
): Promise<{
|
||||
columns: string[];
|
||||
columnTypes: string[];
|
||||
@@ -448,10 +477,20 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
spatialValues?: QueryResult["spatial_values"];
|
||||
rows: CellValue[][];
|
||||
}> {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
const hasColumnSubset = columnIndexes !== undefined;
|
||||
const validColumnIndexes = hasColumnSubset ? columnIndexes.filter((index) => index >= 0 && index < columns.value.length) : columns.value.map((_, index) => index);
|
||||
const targetColumns = validColumnIndexes.map((index) => columns.value[index]!);
|
||||
const targetColumnTypes = (columnTypes.value ?? []).length === columns.value.length ? validColumnIndexes.map((index) => columnTypes.value?.[index] ?? "") : targetColumns.map(() => "");
|
||||
const visibleSourceIndexes = visibleColumnIndexesOption?.value;
|
||||
// Explicit source indexes are only meaningful when the grid maps visible
|
||||
// columns to result positions; otherwise keep name-based resolution.
|
||||
const projectionSourceIndexes = visibleSourceIndexes ? validColumnIndexes.map((targetIdx) => visibleSourceIndexes[targetIdx] ?? targetIdx) : undefined;
|
||||
|
||||
if (useFullExport && rowIds === undefined && fullExportResult && !hasCompleteLocalResult?.value) {
|
||||
const result = await fullExportResult(onProgress);
|
||||
if (result) {
|
||||
const projected = databaseType.value === "mongodb" ? projectResultColumns(result, columns.value) : undefined;
|
||||
const projected = databaseType.value === "mongodb" || hasColumnSubset ? projectResultColumns(result, targetColumns, projectionSourceIndexes) : undefined;
|
||||
const exportedColumns = projected?.columns ?? result.columns;
|
||||
const exportedColumnTypes = projected?.columnTypes ?? result.column_types ?? [];
|
||||
const exportedRows = projected?.rows ?? result.rows;
|
||||
@@ -480,7 +519,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
// client-side filters/search and unsaved edits, which would silently
|
||||
// change what the export contains.
|
||||
if (useFullExport && rowIds === undefined && hasCompleteLocalResult?.value && completeLocalResult?.value) {
|
||||
const normalized = normalizeCompleteLocalResult(completeLocalResult.value);
|
||||
const normalized = hasColumnSubset || databaseType.value === "mongodb" ? normalizeCompleteLocalResult(completeLocalResult.value, targetColumns, projectionSourceIndexes) : normalizeCompleteLocalResult(completeLocalResult.value);
|
||||
const columnComments = buildXlsxHeaderOverrides(normalized.columns, normalized.columnComments, headerMode);
|
||||
return {
|
||||
...applyGlobalDateTimeExportFormat(
|
||||
@@ -492,23 +531,40 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
spatialValues: normalized.spatialValues,
|
||||
};
|
||||
}
|
||||
const commentHeader = buildXlsxHeaderOverrides(columns.value, visibleXlsxColumnComments.value, headerMode);
|
||||
const targetComments = commentsForExportColumns(targetColumns, false);
|
||||
const commentHeader = buildXlsxHeaderOverrides(targetColumns, targetComments, headerMode);
|
||||
const exportItems = await resolveVisibleRowValues(rowsToExport(rowIds));
|
||||
const visibleIndexes = visibleColumnIndexesOption?.value ?? columns.value.map((_, index) => index);
|
||||
const spatialColumns = spatialColumnsOption?.value?.map((column) => ({ column_index: visibleIndexes.indexOf(column.column_index), srid: column.srid })).filter((column) => column.column_index >= 0);
|
||||
const targetSourceIndexes = validColumnIndexes.map((targetIdx) => visibleIndexes[targetIdx] ?? targetIdx);
|
||||
const targetIndexBySource = new Map<number, number>();
|
||||
targetSourceIndexes.forEach((sourceIdx, targetIdx) => {
|
||||
targetIndexBySource.set(sourceIdx, targetIdx);
|
||||
});
|
||||
const spatialColumns = spatialColumnsOption?.value?.flatMap((column) => {
|
||||
const targetIndex = targetIndexBySource.get(column.column_index);
|
||||
return targetIndex === undefined ? [] : [{ column_index: targetIndex, srid: column.srid }];
|
||||
});
|
||||
const spatialValues = spatialValuesOption?.value;
|
||||
const mappedSpatialValues = spatialValues?.length ? exportItems.map((item) => targetSourceIndexes.map((sourceIdx) => spatialValues[item.sourceIndex ?? -1]?.[sourceIdx] ?? null)) : undefined;
|
||||
|
||||
return {
|
||||
...applyGlobalDateTimeExportFormat(
|
||||
{
|
||||
columns: columns.value,
|
||||
columnTypes: (columnTypes.value ?? []).map((type) => type ?? ""),
|
||||
rows: preserveMongoExtendedJson && databaseType.value === "mongodb" ? mongoLocalRowsForJson(exportItems) : externalizeRows(exportItems.map((item) => item.data)),
|
||||
columns: targetColumns,
|
||||
columnTypes: targetColumnTypes,
|
||||
rows:
|
||||
preserveMongoExtendedJson && databaseType.value === "mongodb"
|
||||
? mongoLocalRowsForJson(exportItems, targetColumns)
|
||||
: externalizeRows(
|
||||
exportItems.map((item) => item.data),
|
||||
validColumnIndexes,
|
||||
),
|
||||
},
|
||||
formatDateTime && !preserveMongoExtendedJson,
|
||||
),
|
||||
columnComments: commentHeader,
|
||||
...(spatialColumns?.length ? { spatialColumns } : {}),
|
||||
...(spatialValues?.length ? { spatialValues: exportItems.map((item) => visibleIndexes.map((index) => spatialValues[item.sourceIndex ?? -1]?.[index] ?? null)) } : {}),
|
||||
...(mappedSpatialValues?.length ? { spatialValues: mappedSpatialValues } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -869,11 +925,12 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
return exported;
|
||||
}
|
||||
|
||||
async function exportCsv(rowIds?: number[]) {
|
||||
async function exportCsv(target?: ExportTargetParam, columnIndexesParam?: number[]) {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
if (await exportQueryResultViaBackend("csv", rowIds)) return;
|
||||
if (await exportFullTableDataViaBackend("csv", rowIds)) return;
|
||||
if (await exportQueryResultViaBackend("csv", rowIds, false, "name", true, undefined, false, columnIndexes)) return;
|
||||
if (await exportFullTableDataViaBackend("csv", rowIds, "name", true, undefined, false, columnIndexes)) return;
|
||||
|
||||
const needsFullExport = rowIds === undefined && !!fullExportResult && !hasCompleteLocalResult?.value;
|
||||
if (needsFullExport && exportProgressDialog && exportProgressState) {
|
||||
@@ -891,7 +948,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
};
|
||||
exportProgressDialog.value = true;
|
||||
}
|
||||
const result = await resultToExport(rowIds, (info) => {
|
||||
const result = await resultToExport({ rowIds, columnIndexes }, (info) => {
|
||||
if (needsFullExport && exportProgressState && exportProgressState.value.status === "Running") {
|
||||
// Guard against the COUNT estimate being too low: if the real
|
||||
// fetched count exceeds it, bump totalRows so the progress bar
|
||||
@@ -951,7 +1008,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportCurrentPageCsv() {
|
||||
async function exportCurrentPageCsv(columnIndexes?: number[]) {
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
let outputPath = exportFileName("export-page", "csv", { page: true });
|
||||
@@ -964,7 +1021,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
if (!path) return;
|
||||
outputPath = path;
|
||||
}
|
||||
const result = await resultToExport(undefined, undefined, false);
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false);
|
||||
await api.exportQueryResultCsv(outputPath, result.columns, result.rows, useSettingsStore().editorSettings.csvQuoteMode, csvNullLiteralForMode(useSettingsStore().editorSettings.csvNullMode));
|
||||
notifyExportSuccess(outputPath);
|
||||
} catch (e: any) {
|
||||
@@ -973,11 +1030,12 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportJson(rowIds?: number[]) {
|
||||
async function exportJson(target?: ExportTargetParam, columnIndexesParam?: number[]) {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
if (await exportFullTableDataViaBackend("json", rowIds)) return;
|
||||
if (await exportQueryResultViaBackend("json", rowIds)) return;
|
||||
if (await exportFullTableDataViaBackend("json", rowIds, "name", true, undefined, false, columnIndexes)) return;
|
||||
if (await exportQueryResultViaBackend("json", rowIds, false, "name", true, undefined, false, columnIndexes)) return;
|
||||
|
||||
let outputPath = exportFileName("export", "json");
|
||||
if (isTauriRuntime()) {
|
||||
@@ -989,7 +1047,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
if (!path) return;
|
||||
outputPath = path;
|
||||
}
|
||||
const result = await resultToExport(rowIds, undefined, true, true, "name", true);
|
||||
const result = await resultToExport({ rowIds, columnIndexes }, undefined, true, true, "name", true);
|
||||
await api.exportQueryResultJson(outputPath, result.columns, result.rows);
|
||||
notifyExportSuccess(outputPath);
|
||||
} catch (e: any) {
|
||||
@@ -998,7 +1056,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportCurrentPageJson() {
|
||||
async function exportCurrentPageJson(columnIndexes?: number[]) {
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
let outputPath = exportFileName("export-page", "json", { page: true });
|
||||
@@ -1011,7 +1069,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
if (!path) return;
|
||||
outputPath = path;
|
||||
}
|
||||
const result = await resultToExport(undefined, undefined, false, true, "name", true);
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false, true, "name", true);
|
||||
await api.exportQueryResultJson(outputPath, result.columns, result.rows);
|
||||
notifyExportSuccess(outputPath);
|
||||
} catch (e: any) {
|
||||
@@ -1020,10 +1078,11 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportMarkdown(rowIds?: number[]) {
|
||||
async function exportMarkdown(target?: ExportTargetParam, columnIndexesParam?: number[]) {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
if (await exportFullTableDataViaBackend("markdown", rowIds)) return;
|
||||
if (await exportFullTableDataViaBackend("markdown", rowIds, "name", true, undefined, false, columnIndexes)) return;
|
||||
|
||||
let outputPath = exportFileName("export", "md");
|
||||
if (isTauriRuntime()) {
|
||||
@@ -1035,7 +1094,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
if (!path) return;
|
||||
outputPath = path;
|
||||
}
|
||||
const result = await resultToExport(rowIds);
|
||||
const result = await resultToExport({ rowIds, columnIndexes });
|
||||
await api.exportQueryResultMarkdown(outputPath, result.columns, result.rows);
|
||||
notifyExportSuccess(outputPath);
|
||||
} catch (e: any) {
|
||||
@@ -1044,7 +1103,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportCurrentPageMarkdown() {
|
||||
async function exportCurrentPageMarkdown(columnIndexes?: number[]) {
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
let outputPath = exportFileName("export-page", "md", { page: true });
|
||||
@@ -1057,7 +1116,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
if (!path) return;
|
||||
outputPath = path;
|
||||
}
|
||||
const result = await resultToExport(undefined, undefined, false);
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false);
|
||||
await api.exportQueryResultMarkdown(outputPath, result.columns, result.rows);
|
||||
notifyExportSuccess(outputPath);
|
||||
} catch (e: any) {
|
||||
@@ -1066,12 +1125,13 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportTxt(rowIds?: number[]) {
|
||||
async function exportTxt(target?: ExportTargetParam, columnIndexesParam?: number[]) {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
if (await exportQueryResultViaBackend("txt", rowIds)) return;
|
||||
if (await exportFullTableDataViaBackend("txt", rowIds)) return;
|
||||
const result = await resultToExport(rowIds);
|
||||
if (await exportQueryResultViaBackend("txt", rowIds, false, "name", true, undefined, false, columnIndexes)) return;
|
||||
if (await exportFullTableDataViaBackend("txt", rowIds, "name", true, undefined, false, columnIndexes)) return;
|
||||
const result = await resultToExport({ rowIds, columnIndexes });
|
||||
const content = formatTsv(result.columns, result.rows);
|
||||
const saved = await saveTextFile(content, exportFileName(tableMeta.value?.tableName || "export", "txt", { preferFallback: true }), "Text", "txt");
|
||||
if (!saved) return;
|
||||
@@ -1082,7 +1142,8 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportHtml(rowIds?: number[], openAfterExport = false) {
|
||||
async function exportHtml(target?: ExportTargetParam, openAfterExport = false, columnIndexesParam?: number[]) {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
let outputPath = exportFileName("export", "html");
|
||||
@@ -1098,7 +1159,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
outputPath = path;
|
||||
}
|
||||
}
|
||||
const result = await resultToExport(rowIds);
|
||||
const result = await resultToExport({ rowIds, columnIndexes });
|
||||
await api.exportQueryResultHtml(outputPath, currentExportTitle(), result.columns, result.rows);
|
||||
notifyExportSuccess(openAfterExport ? undefined : outputPath);
|
||||
if (openAfterExport && isTauriRuntime()) await api.openQueryResultTempFile(outputPath);
|
||||
@@ -1108,7 +1169,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportCurrentPageHtml() {
|
||||
async function exportCurrentPageHtml(columnIndexes?: number[]) {
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
let outputPath = exportFileName("export-page", "html", { page: true });
|
||||
@@ -1121,7 +1182,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
if (!path) return;
|
||||
outputPath = path;
|
||||
}
|
||||
const result = await resultToExport(undefined, undefined, false);
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false);
|
||||
await api.exportQueryResultHtml(outputPath, currentExportTitle(), result.columns, result.rows);
|
||||
notifyExportSuccess(outputPath);
|
||||
} catch (e: any) {
|
||||
@@ -1130,10 +1191,10 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportCurrentPageTxt() {
|
||||
async function exportCurrentPageTxt(columnIndexes?: number[]) {
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
const result = await resultToExport(undefined, undefined, false);
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false);
|
||||
const content = formatTsv(result.columns, result.rows);
|
||||
const saved = await saveTextFile(content, exportFileName("export-page", "txt", { page: true }), "Text", "txt");
|
||||
if (!saved) return;
|
||||
@@ -1144,14 +1205,15 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportXlsxResult(rowIds: number[] | undefined, includeSqlSheet: boolean, openAfterExport = false) {
|
||||
async function exportXlsxResult(target?: ExportTargetParam, includeSqlSheet = false, openAfterExport = false, columnIndexesParam?: number[]) {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
const exportOptions = await showXlsxHeaderDialog();
|
||||
if (exportOptions === null) return;
|
||||
|
||||
await runExclusiveExport(async () => {
|
||||
try {
|
||||
if (await exportQueryResultViaBackend("xlsx", rowIds, includeSqlSheet, exportOptions.headerMode, exportOptions.autoFilter, undefined, openAfterExport)) return;
|
||||
if (await exportFullTableDataViaBackend("xlsx", rowIds, exportOptions.headerMode, exportOptions.autoFilter, undefined, openAfterExport)) return;
|
||||
if (await exportQueryResultViaBackend("xlsx", rowIds, includeSqlSheet, exportOptions.headerMode, exportOptions.autoFilter, undefined, openAfterExport, columnIndexes)) return;
|
||||
if (await exportFullTableDataViaBackend("xlsx", rowIds, exportOptions.headerMode, exportOptions.autoFilter, undefined, openAfterExport, columnIndexes)) return;
|
||||
|
||||
let outputPath = exportFileName("export", "xlsx");
|
||||
if (isTauriRuntime()) {
|
||||
@@ -1183,7 +1245,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
exportProgressDialog.value = true;
|
||||
}
|
||||
const result = await resultToExport(
|
||||
rowIds,
|
||||
{ rowIds, columnIndexes },
|
||||
(info) => {
|
||||
if (needsFullExport && exportProgressState && exportProgressState.value.status === "Running") {
|
||||
const adjustedTotal = info.totalRows !== null && info.rowsExported > info.totalRows ? info.rowsExported : info.totalRows;
|
||||
@@ -1233,23 +1295,23 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportXlsx(rowIds?: number[]) {
|
||||
await exportXlsxResult(rowIds, false);
|
||||
async function exportXlsx(target?: ExportTargetParam, columnIndexes?: number[]) {
|
||||
await exportXlsxResult(target, false, false, columnIndexes);
|
||||
}
|
||||
|
||||
async function openXlsx(rowIds?: number[]) {
|
||||
await exportXlsxResult(rowIds, false, true);
|
||||
async function openXlsx(target?: ExportTargetParam, columnIndexes?: number[]) {
|
||||
await exportXlsxResult(target, false, true, columnIndexes);
|
||||
}
|
||||
|
||||
async function openBrowser(rowIds?: number[]) {
|
||||
await exportHtml(rowIds, true);
|
||||
async function openBrowser(target?: ExportTargetParam, columnIndexes?: number[]) {
|
||||
await exportHtml(target, true, columnIndexes);
|
||||
}
|
||||
|
||||
async function exportXlsxWithSql(rowIds?: number[]) {
|
||||
await exportXlsxResult(rowIds, true);
|
||||
async function exportXlsxWithSql(target?: ExportTargetParam, columnIndexes?: number[]) {
|
||||
await exportXlsxResult(target, true, false, columnIndexes);
|
||||
}
|
||||
|
||||
async function exportCurrentPageXlsxResult(includeSqlSheet: boolean) {
|
||||
async function exportCurrentPageXlsxResult(includeSqlSheet: boolean, columnIndexes?: number[]) {
|
||||
const exportOptions = await showXlsxHeaderDialog();
|
||||
if (exportOptions === null) return;
|
||||
|
||||
@@ -1265,7 +1327,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
if (!path) return;
|
||||
outputPath = path;
|
||||
}
|
||||
const result = await resultToExport(undefined, undefined, false, true, exportOptions.headerMode);
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false, true, exportOptions.headerMode);
|
||||
await writeXlsxResult(outputPath, result, includeSqlSheet, exportOptions.autoFilter, currentPageExportSql());
|
||||
notifyExportSuccess(outputPath);
|
||||
} catch (e: any) {
|
||||
@@ -1274,12 +1336,12 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportCurrentPageXlsx() {
|
||||
await exportCurrentPageXlsxResult(false);
|
||||
async function exportCurrentPageXlsx(columnIndexes?: number[]) {
|
||||
await exportCurrentPageXlsxResult(false, columnIndexes);
|
||||
}
|
||||
|
||||
async function exportCurrentPageXlsxWithSql() {
|
||||
await exportCurrentPageXlsxResult(true);
|
||||
async function exportCurrentPageXlsxWithSql(columnIndexes?: number[]) {
|
||||
await exportCurrentPageXlsxResult(true, columnIndexes);
|
||||
}
|
||||
|
||||
async function exportResultSheetsXlsxResult(sheetsToExport: Array<{ sheetName: string; result: QueryResult; sql?: string }> | undefined, includeSqlSheet: boolean) {
|
||||
@@ -1360,11 +1422,11 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
return excludeColumns ? { excludePrimaryKeys: true, primaryKeys: excludeColumns } : {};
|
||||
}
|
||||
|
||||
async function exportFullTableDataViaBackend(format: "csv" | "xlsx" | "json" | "markdown" | "sql" | "txt", rowIds?: number[], headerMode: XlsxHeaderMode = "name", autoFilter = true, sqlExportOptions?: SqlExportOptions, openAfterExport = false): Promise<boolean> {
|
||||
async function exportFullTableDataViaBackend(format: "csv" | "xlsx" | "json" | "markdown" | "sql" | "txt", rowIds?: number[], headerMode: XlsxHeaderMode = "name", autoFilter = true, sqlExportOptions?: SqlExportOptions, openAfterExport = false, columnIndexes?: number[]): Promise<boolean> {
|
||||
const meta = tableMeta.value;
|
||||
// The backend table exporter currently builds two-part table names. External
|
||||
// Doris/StarRocks catalogs need the data-tab paginator's three-part SQL.
|
||||
if (rowIds !== undefined || context.value !== "table-data" || !meta || meta.catalog || !connectionId.value || !database.value) {
|
||||
if (rowIds !== undefined || columnIndexes !== undefined || context.value !== "table-data" || !meta || meta.catalog || !connectionId.value || !database.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1474,8 +1536,17 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function exportQueryResultViaBackend(format: "csv" | "xlsx" | "json" | "txt" | "sql", rowIds?: number[], includeSqlSheet = false, headerMode: XlsxHeaderMode = "name", autoFilter = true, sqlExportOptions?: SqlExportOptions, openAfterExport = false): Promise<boolean> {
|
||||
if (rowIds !== undefined || context.value !== "results" || !queryResultExportRequest) {
|
||||
async function exportQueryResultViaBackend(
|
||||
format: "csv" | "xlsx" | "json" | "txt" | "sql",
|
||||
rowIds?: number[],
|
||||
includeSqlSheet = false,
|
||||
headerMode: XlsxHeaderMode = "name",
|
||||
autoFilter = true,
|
||||
sqlExportOptions?: SqlExportOptions,
|
||||
openAfterExport = false,
|
||||
columnIndexes?: number[],
|
||||
): Promise<boolean> {
|
||||
if (rowIds !== undefined || columnIndexes !== undefined || context.value !== "results" || !queryResultExportRequest) {
|
||||
return false;
|
||||
}
|
||||
if (databaseType.value === "mongodb") return false;
|
||||
@@ -1598,26 +1669,31 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function exportQueryResultSqlViaBackend(rowIds: number[] | undefined, sqlExportOptions: SqlExportOptions): Promise<boolean> {
|
||||
if (!isTauriRuntime()) return false;
|
||||
return exportQueryResultViaBackend("sql", rowIds, false, "name", true, sqlExportOptions);
|
||||
async function exportQueryResultSqlViaBackend(rowIds: number[] | undefined, sqlExportOptions: SqlExportOptions, columnIndexes?: number[]): Promise<boolean> {
|
||||
if (!isTauriRuntime() || columnIndexes !== undefined) return false;
|
||||
return exportQueryResultViaBackend("sql", rowIds, false, "name", true, sqlExportOptions, false, columnIndexes);
|
||||
}
|
||||
|
||||
function sqlExportChoices(fullResult: boolean): SqlExportColumnSelection[] {
|
||||
const names = context.value === "table-data" ? effectiveColumns(sourceColumns.value, columns.value) : fullResult ? allColumns.value : columns.value;
|
||||
function sqlExportChoices(fullResult: boolean, columnSubset?: readonly string[]): SqlExportColumnSelection[] {
|
||||
const names = columnSubset ?? (context.value === "table-data" ? effectiveColumns(sourceColumns.value, columns.value) : fullResult ? allColumns.value : columns.value);
|
||||
const excluded = new Set((sqlExportExcludedColumns() ?? []).map(normalizeColumnName));
|
||||
const types = fullResult && context.value === "results" ? allColumnTypes.value : columnTypes.value;
|
||||
// A subset's sourceIndex is its position within the subset; resolve the
|
||||
// metadata-less type fallback by name against the typed column list.
|
||||
const typeNames = fullResult && context.value === "results" ? allColumns.value : context.value === "table-data" ? effectiveColumns(sourceColumns.value, columns.value) : columns.value;
|
||||
const typeByName = columnSubset ? new Map(typeNames.map((name, index) => [name, types?.[index]])) : undefined;
|
||||
const metadataByName = new Map((tableMeta.value?.columns ?? []).map((column) => [normalizeColumnName(column.name), column]));
|
||||
return sqlExportColumnChoices(names).filter((column) => {
|
||||
if (excluded.has(normalizeColumnName(column.name)) || usesSyntheticRowIdKey(databaseType.value, [column.name])) return false;
|
||||
const metadata = metadataByName.get(normalizeColumnName(column.name));
|
||||
if (databaseType.value === "mysql" && /\b(?:virtual|stored|persistent)\s+generated\b|\bgenerated\s+always\s+as\s*\(/i.test(metadata?.extra ?? "")) return false;
|
||||
const columnType = (metadata?.data_type || types?.[column.sourceIndex])?.trim().replace(/^"|"$/g, "").toLowerCase();
|
||||
const columnType = (metadata?.data_type || (columnSubset ? typeByName?.get(column.name) : types?.[column.sourceIndex]))?.trim().replace(/^"|"$/g, "").toLowerCase();
|
||||
return databaseType.value !== "postgres" || (columnType !== "tsvector" && !columnType?.endsWith(".tsvector"));
|
||||
});
|
||||
}
|
||||
|
||||
async function exportSql(rowIds?: number[]) {
|
||||
async function exportSql(target?: ExportTargetParam, columnIndexesParam?: number[]) {
|
||||
const { rowIds, columnIndexes } = normalizeExportTarget(target, columnIndexesParam);
|
||||
await runExclusiveExport(async () => {
|
||||
const exportId = uuid();
|
||||
const exportStartedAt = performance.now();
|
||||
@@ -1643,7 +1719,11 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
},
|
||||
true,
|
||||
);
|
||||
const selectedSqlExportOptions = (await showSqlInsertModeDialog({ allowSplit: rowIds === undefined && context.value === "table-data", columns: sqlExportChoices(rowIds === undefined) })) as SqlExportOptions | SqlInsertMode | null;
|
||||
const chosenColumns = columnIndexes !== undefined ? columnIndexes.map((i) => columns.value[i]).filter((c): c is string => !!c) : undefined;
|
||||
const selectedSqlExportOptions = (await showSqlInsertModeDialog({
|
||||
allowSplit: rowIds === undefined && columnIndexes === undefined && context.value === "table-data",
|
||||
columns: sqlExportChoices(rowIds === undefined, chosenColumns),
|
||||
})) as SqlExportOptions | SqlInsertMode | null;
|
||||
if (selectedSqlExportOptions === null) {
|
||||
logExportStage("cancelled", { stage: "insert-mode-dialog" });
|
||||
return;
|
||||
@@ -1654,7 +1734,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
try {
|
||||
// Step 1: table-data context — existing backend table export
|
||||
logExportStage("backend-export-start");
|
||||
const handledByBackend = await exportFullTableDataViaBackend("sql", rowIds, "name", true, sqlExportOptions);
|
||||
const handledByBackend = await exportFullTableDataViaBackend("sql", rowIds, "name", true, sqlExportOptions, false, columnIndexes);
|
||||
logExportStage("backend-export-finished", { handledByBackend });
|
||||
if (handledByBackend) {
|
||||
logExportStage("done", { path: "backend" });
|
||||
@@ -1663,7 +1743,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
|
||||
// Step 2: query-result context — NEW backend streaming with background task
|
||||
logExportStage("query-backend-export-start");
|
||||
const handledQueryByBackend = await exportQueryResultSqlViaBackend(rowIds, sqlExportOptions);
|
||||
const handledQueryByBackend = await exportQueryResultSqlViaBackend(rowIds, sqlExportOptions, columnIndexes);
|
||||
logExportStage("query-backend-export-finished", { handledByBackend: handledQueryByBackend });
|
||||
if (handledQueryByBackend) {
|
||||
logExportStage("done", { path: "query-backend" });
|
||||
@@ -1671,7 +1751,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
}
|
||||
|
||||
// Step 3: fallback — local export (Web and edge-case scenarios)
|
||||
const result = await resultToExport(rowIds, undefined, true, false);
|
||||
const result = await resultToExport({ rowIds, columnIndexes }, undefined, true, false);
|
||||
logExportStage(
|
||||
"result-ready",
|
||||
{
|
||||
@@ -1727,14 +1807,15 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
async function exportCurrentPageSql() {
|
||||
async function exportCurrentPageSql(columnIndexes?: number[]) {
|
||||
await runExclusiveExport(async () => {
|
||||
const selectedSqlExportOptions = (await showSqlInsertModeDialog({ columns: sqlExportChoices(false) })) as SqlExportOptions | SqlInsertMode | null;
|
||||
const chosenColumns = columnIndexes !== undefined ? columnIndexes.map((i) => columns.value[i]).filter((c): c is string => !!c) : undefined;
|
||||
const selectedSqlExportOptions = (await showSqlInsertModeDialog({ columns: sqlExportChoices(false, chosenColumns) })) as SqlExportOptions | SqlInsertMode | null;
|
||||
if (selectedSqlExportOptions === null) return;
|
||||
const sqlExportOptions: SqlExportOptions = typeof selectedSqlExportOptions === "string" ? { insertMode: selectedSqlExportOptions } : selectedSqlExportOptions;
|
||||
const insertMode = sqlExportOptions.insertMode;
|
||||
try {
|
||||
const result = await resultToExport(undefined, undefined, false, false);
|
||||
const result = await resultToExport({ columnIndexes }, undefined, false, false);
|
||||
const exportData = sqlInsertExportData(result, sqlExportOptions.selectedColumns);
|
||||
const content = await formatSqlInsert({
|
||||
databaseType: databaseType.value,
|
||||
|
||||
@@ -1910,6 +1910,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "Seçilmiş sətirləri HTML kimi ixrac et",
|
||||
exportSelectedRowsSql: "Seçilmiş sətirləri SQL INSERT kimi ixrac et",
|
||||
exportSelectedRowsTxt: "Seçilmiş sətirləri TXT kimi ixrac et",
|
||||
exportSelectedColumnsCsv: "Seçilmiş Sütunları CSV kimi ixrac et",
|
||||
exportSelectedColumnsXlsx: "Seçilmiş Sütunları XLSX kimi ixrac et",
|
||||
exportSelectedColumnsXlsxWithSql: "Seçilmiş Sütunları SQL ilə XLSX kimi ixrac et",
|
||||
exportSelectedColumnsJson: "Seçilmiş Sütunları JSON kimi ixrac et",
|
||||
exportSelectedColumnsMarkdown: "Seçilmiş Sütunları Markdown kimi ixrac et",
|
||||
exportSelectedColumnsHtml: "Seçilmiş Sütunları HTML kimi ixrac et",
|
||||
exportSelectedColumnsSql: "Seçilmiş Sütunları SQL INSERT kimi ixrac et",
|
||||
exportSelectedColumnsTxt: "Seçilmiş Sütunları TXT kimi ixrac et",
|
||||
exportSelectedCellsCsv: "Seçilmiş Hüceyrələri CSV kimi ixrac et",
|
||||
exportSelectedCellsXlsx: "Seçilmiş Hüceyrələri XLSX kimi ixrac et",
|
||||
exportSelectedCellsXlsxWithSql: "Seçilmiş Hüceyrələri SQL ilə XLSX kimi ixrac et",
|
||||
exportSelectedCellsJson: "Seçilmiş Hüceyrələri JSON kimi ixrac et",
|
||||
exportSelectedCellsMarkdown: "Seçilmiş Hüceyrələri Markdown kimi ixrac et",
|
||||
exportSelectedCellsHtml: "Seçilmiş Hüceyrələri HTML kimi ixrac et",
|
||||
exportSelectedCellsSql: "Seçilmiş Hüceyrələri SQL INSERT kimi ixrac et",
|
||||
exportSelectedCellsTxt: "Seçilmiş Hüceyrələri TXT kimi ixrac et",
|
||||
exported: "İxrac edildi",
|
||||
exportFailed: "İxrac etmək mümkün olmadı: {message}",
|
||||
xlsxHeaderTitle: "Başlıq formatı",
|
||||
|
||||
@@ -2323,6 +2323,22 @@ export default {
|
||||
exportSelectedRowsHtml: "Export Selected Rows as HTML",
|
||||
exportSelectedRowsSql: "Export Selected Rows as SQL INSERT",
|
||||
exportSelectedRowsTxt: "Export Selected Rows as TXT",
|
||||
exportSelectedColumnsCsv: "Export Selected Columns as CSV",
|
||||
exportSelectedColumnsXlsx: "Export Selected Columns as XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "Export Selected Columns as XLSX with SQL",
|
||||
exportSelectedColumnsJson: "Export Selected Columns as JSON",
|
||||
exportSelectedColumnsMarkdown: "Export Selected Columns as Markdown",
|
||||
exportSelectedColumnsHtml: "Export Selected Columns as HTML",
|
||||
exportSelectedColumnsSql: "Export Selected Columns as SQL INSERT",
|
||||
exportSelectedColumnsTxt: "Export Selected Columns as TXT",
|
||||
exportSelectedCellsCsv: "Export Selected Cells as CSV",
|
||||
exportSelectedCellsXlsx: "Export Selected Cells as XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "Export Selected Cells as XLSX with SQL",
|
||||
exportSelectedCellsJson: "Export Selected Cells as JSON",
|
||||
exportSelectedCellsMarkdown: "Export Selected Cells as Markdown",
|
||||
exportSelectedCellsHtml: "Export Selected Cells as HTML",
|
||||
exportSelectedCellsSql: "Export Selected Cells as SQL INSERT",
|
||||
exportSelectedCellsTxt: "Export Selected Cells as TXT",
|
||||
exported: "Exported",
|
||||
exportFailed: "Export failed: {message}",
|
||||
xlsxHeaderTitle: "Header Format",
|
||||
|
||||
@@ -2184,6 +2184,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "Exportar filas seleccionadas como HTML",
|
||||
exportSelectedRowsSql: "Exportar filas seleccionadas como SQL INSERT",
|
||||
exportSelectedRowsTxt: "Exportar filas seleccionadas como TXT",
|
||||
exportSelectedColumnsCsv: "Exportar columnas seleccionadas como CSV",
|
||||
exportSelectedColumnsXlsx: "Exportar columnas seleccionadas como XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "Exportar columnas seleccionadas como XLSX con SQL",
|
||||
exportSelectedColumnsJson: "Exportar columnas seleccionadas como JSON",
|
||||
exportSelectedColumnsMarkdown: "Exportar columnas seleccionadas como Markdown",
|
||||
exportSelectedColumnsHtml: "Exportar columnas seleccionadas como HTML",
|
||||
exportSelectedColumnsSql: "Exportar columnas seleccionadas como SQL INSERT",
|
||||
exportSelectedColumnsTxt: "Exportar columnas seleccionadas como TXT",
|
||||
exportSelectedCellsCsv: "Exportar celdas seleccionadas como CSV",
|
||||
exportSelectedCellsXlsx: "Exportar celdas seleccionadas como XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "Exportar celdas seleccionadas como XLSX con SQL",
|
||||
exportSelectedCellsJson: "Exportar celdas seleccionadas como JSON",
|
||||
exportSelectedCellsMarkdown: "Exportar celdas seleccionadas como Markdown",
|
||||
exportSelectedCellsHtml: "Exportar celdas seleccionadas como HTML",
|
||||
exportSelectedCellsSql: "Exportar celdas seleccionadas como SQL INSERT",
|
||||
exportSelectedCellsTxt: "Exportar celdas seleccionadas como TXT",
|
||||
exported: "Exportado",
|
||||
exportFailed: "Error al exportar: {message}",
|
||||
copied: "Copiado",
|
||||
|
||||
@@ -2182,6 +2182,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "Ekspor Baris Terpilih sebagai HTML",
|
||||
exportSelectedRowsSql: "Ekspor Baris Terpilih sebagai SQL INSERT",
|
||||
exportSelectedRowsTxt: "Ekspor Baris Terpilih sebagai TXT",
|
||||
exportSelectedColumnsCsv: "Ekspor Kolom Terpilih sebagai CSV",
|
||||
exportSelectedColumnsXlsx: "Ekspor Kolom Terpilih sebagai XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "Ekspor Kolom Terpilih sebagai XLSX dengan SQL",
|
||||
exportSelectedColumnsJson: "Ekspor Kolom Terpilih sebagai JSON",
|
||||
exportSelectedColumnsMarkdown: "Ekspor Kolom Terpilih sebagai Markdown",
|
||||
exportSelectedColumnsHtml: "Ekspor Kolom Terpilih sebagai HTML",
|
||||
exportSelectedColumnsSql: "Ekspor Kolom Terpilih sebagai SQL INSERT",
|
||||
exportSelectedColumnsTxt: "Ekspor Kolom Terpilih sebagai TXT",
|
||||
exportSelectedCellsCsv: "Ekspor Sel Terpilih sebagai CSV",
|
||||
exportSelectedCellsXlsx: "Ekspor Sel Terpilih sebagai XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "Ekspor Sel Terpilih sebagai XLSX dengan SQL",
|
||||
exportSelectedCellsJson: "Ekspor Sel Terpilih sebagai JSON",
|
||||
exportSelectedCellsMarkdown: "Ekspor Sel Terpilih sebagai Markdown",
|
||||
exportSelectedCellsHtml: "Ekspor Sel Terpilih sebagai HTML",
|
||||
exportSelectedCellsSql: "Ekspor Sel Terpilih sebagai SQL INSERT",
|
||||
exportSelectedCellsTxt: "Ekspor Sel Terpilih sebagai TXT",
|
||||
exported: "Diekspor",
|
||||
exportFailed: "Ekspor gagal: {message}",
|
||||
xlsxHeaderTitle: "Format Header",
|
||||
|
||||
@@ -2082,6 +2082,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "Esporta Righe Selezionate come HTML",
|
||||
exportSelectedRowsSql: "Esporta Righe Selezionate come SQL INSERT",
|
||||
exportSelectedRowsTxt: "Esporta Righe Selezionate come TXT",
|
||||
exportSelectedColumnsCsv: "Esporta Colonne Selezionate come CSV",
|
||||
exportSelectedColumnsXlsx: "Esporta Colonne Selezionate come XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "Esporta Colonne Selezionate come XLSX con SQL",
|
||||
exportSelectedColumnsJson: "Esporta Colonne Selezionate come JSON",
|
||||
exportSelectedColumnsMarkdown: "Esporta Colonne Selezionate come Markdown",
|
||||
exportSelectedColumnsHtml: "Esporta Colonne Selezionate come HTML",
|
||||
exportSelectedColumnsSql: "Esporta Colonne Selezionate come SQL INSERT",
|
||||
exportSelectedColumnsTxt: "Esporta Colonne Selezionate come TXT",
|
||||
exportSelectedCellsCsv: "Esporta Celle Selezionate come CSV",
|
||||
exportSelectedCellsXlsx: "Esporta Celle Selezionate come XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "Esporta Celle Selezionate come XLSX con SQL",
|
||||
exportSelectedCellsJson: "Esporta Celle Selezionate come JSON",
|
||||
exportSelectedCellsMarkdown: "Esporta Celle Selezionate come Markdown",
|
||||
exportSelectedCellsHtml: "Esporta Celle Selezionate come HTML",
|
||||
exportSelectedCellsSql: "Esporta Celle Selezionate come SQL INSERT",
|
||||
exportSelectedCellsTxt: "Esporta Celle Selezionate come TXT",
|
||||
exported: "Esportato",
|
||||
exportFailed: "Esportazione non riuscita: {message}",
|
||||
copied: "Copiato",
|
||||
|
||||
@@ -2091,6 +2091,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "選択行をHTMLでエクスポート",
|
||||
exportSelectedRowsSql: "選択行をSQL INSERTでエクスポート",
|
||||
exportSelectedRowsTxt: "選択行をTXTでエクスポート",
|
||||
exportSelectedColumnsCsv: "選択した列を CSV としてエクスポート",
|
||||
exportSelectedColumnsXlsx: "選択した列を XLSX としてエクスポート",
|
||||
exportSelectedColumnsXlsxWithSql: "選択した列を SQL 付き XLSX としてエクスポート",
|
||||
exportSelectedColumnsJson: "選択した列を JSON としてエクスポート",
|
||||
exportSelectedColumnsMarkdown: "選択した列を Markdown としてエクスポート",
|
||||
exportSelectedColumnsHtml: "選択した列を HTML としてエクスポート",
|
||||
exportSelectedColumnsSql: "選択した列を SQL INSERT としてエクスポート",
|
||||
exportSelectedColumnsTxt: "選択した列を TXT としてエクスポート",
|
||||
exportSelectedCellsCsv: "選択したセルを CSV としてエクスポート",
|
||||
exportSelectedCellsXlsx: "選択したセルを XLSX としてエクスポート",
|
||||
exportSelectedCellsXlsxWithSql: "選択したセルを SQL 付き XLSX としてエクスポート",
|
||||
exportSelectedCellsJson: "選択したセルを JSON としてエクスポート",
|
||||
exportSelectedCellsMarkdown: "選択したセルを Markdown としてエクスポート",
|
||||
exportSelectedCellsHtml: "選択したセルを HTML としてエクスポート",
|
||||
exportSelectedCellsSql: "選択したセルを SQL INSERT としてエクスポート",
|
||||
exportSelectedCellsTxt: "選択したセルを TXT としてエクスポート",
|
||||
exported: "エクスポートしました",
|
||||
exportFailed: "エクスポートに失敗しました: {message}",
|
||||
copied: "コピーしました",
|
||||
|
||||
@@ -2131,6 +2131,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "선택한 행을 HTML로 내보내기",
|
||||
exportSelectedRowsSql: "선택한 행을 SQL INSERT로 내보내기",
|
||||
exportSelectedRowsTxt: "선택한 행을 TXT로 내보내기",
|
||||
exportSelectedColumnsCsv: "선택한 열을 CSV로 내보내기",
|
||||
exportSelectedColumnsXlsx: "선택한 열을 XLSX로 내보내기",
|
||||
exportSelectedColumnsXlsxWithSql: "선택한 열을 SQL 포함 XLSX로 내보내기",
|
||||
exportSelectedColumnsJson: "선택한 열을 JSON으로 내보내기",
|
||||
exportSelectedColumnsMarkdown: "선택한 열을 Markdown으로 내보내기",
|
||||
exportSelectedColumnsHtml: "선택한 열을 HTML로 내보내기",
|
||||
exportSelectedColumnsSql: "선택한 열을 SQL INSERT로 내보내기",
|
||||
exportSelectedColumnsTxt: "선택한 열을 TXT로 내보내기",
|
||||
exportSelectedCellsCsv: "선택한 셀을 CSV로 내보내기",
|
||||
exportSelectedCellsXlsx: "선택한 셀을 XLSX로 내보내기",
|
||||
exportSelectedCellsXlsxWithSql: "선택한 셀을 SQL 포함 XLSX로 내보내기",
|
||||
exportSelectedCellsJson: "선택한 셀을 JSON으로 내보내기",
|
||||
exportSelectedCellsMarkdown: "선택한 셀을 Markdown으로 내보내기",
|
||||
exportSelectedCellsHtml: "선택한 셀을 HTML로 내보내기",
|
||||
exportSelectedCellsSql: "선택한 셀을 SQL INSERT로 내보내기",
|
||||
exportSelectedCellsTxt: "선택한 셀을 TXT로 내보내기",
|
||||
exported: "내보냈습니다",
|
||||
exportFailed: "내보내기 실패: {message}",
|
||||
copied: "복사했습니다",
|
||||
|
||||
@@ -2084,6 +2084,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "Exportar Linhas Selecionadas como HTML",
|
||||
exportSelectedRowsSql: "Exportar Linhas Selecionadas como SQL INSERT",
|
||||
exportSelectedRowsTxt: "Exportar Linhas Selecionadas como TXT",
|
||||
exportSelectedColumnsCsv: "Exportar Colunas Selecionadas como CSV",
|
||||
exportSelectedColumnsXlsx: "Exportar Colunas Selecionadas como XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "Exportar Colunas Selecionadas como XLSX com SQL",
|
||||
exportSelectedColumnsJson: "Exportar Colunas Selecionadas como JSON",
|
||||
exportSelectedColumnsMarkdown: "Exportar Colunas Selecionadas como Markdown",
|
||||
exportSelectedColumnsHtml: "Exportar Colunas Selecionadas como HTML",
|
||||
exportSelectedColumnsSql: "Exportar Colunas Selecionadas como SQL INSERT",
|
||||
exportSelectedColumnsTxt: "Exportar Colunas Selecionadas como TXT",
|
||||
exportSelectedCellsCsv: "Exportar Células Selecionadas como CSV",
|
||||
exportSelectedCellsXlsx: "Exportar Células Selecionadas como XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "Exportar Células Selecionadas como XLSX com SQL",
|
||||
exportSelectedCellsJson: "Exportar Células Selecionadas como JSON",
|
||||
exportSelectedCellsMarkdown: "Exportar Células Selecionadas como Markdown",
|
||||
exportSelectedCellsHtml: "Exportar Células Selecionadas como HTML",
|
||||
exportSelectedCellsSql: "Exportar Células Selecionadas como SQL INSERT",
|
||||
exportSelectedCellsTxt: "Exportar Células Selecionadas como TXT",
|
||||
exported: "Exportado",
|
||||
exportFailed: "Falha na exportação: {message}",
|
||||
copied: "Copiado",
|
||||
|
||||
@@ -2221,6 +2221,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "Экспорт выбранных строк в HTML",
|
||||
exportSelectedRowsSql: "Экспорт выбранных строк в SQL INSERT",
|
||||
exportSelectedRowsTxt: "Экспорт выбранных строк в TXT",
|
||||
exportSelectedColumnsCsv: "Экспорт выбранных столбцов в CSV",
|
||||
exportSelectedColumnsXlsx: "Экспорт выбранных столбцов в XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "Экспорт выбранных столбцов в XLSX с SQL",
|
||||
exportSelectedColumnsJson: "Экспорт выбранных столбцов в JSON",
|
||||
exportSelectedColumnsMarkdown: "Экспорт выбранных столбцов в Markdown",
|
||||
exportSelectedColumnsHtml: "Экспорт выбранных столбцов в HTML",
|
||||
exportSelectedColumnsSql: "Экспорт выбранных столбцов в SQL INSERT",
|
||||
exportSelectedColumnsTxt: "Экспорт выбранных столбцов в TXT",
|
||||
exportSelectedCellsCsv: "Экспорт выбранных ячеек в CSV",
|
||||
exportSelectedCellsXlsx: "Экспорт выбранных ячеек в XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "Экспорт выбранных ячеек в XLSX с SQL",
|
||||
exportSelectedCellsJson: "Экспорт выбранных ячеек в JSON",
|
||||
exportSelectedCellsMarkdown: "Экспорт выбранных ячеек в Markdown",
|
||||
exportSelectedCellsHtml: "Экспорт выбранных ячеек в HTML",
|
||||
exportSelectedCellsSql: "Экспорт выбранных ячеек в SQL INSERT",
|
||||
exportSelectedCellsTxt: "Экспорт выбранных ячеек в TXT",
|
||||
exported: "Экспортировано",
|
||||
exportFailed: "Не удалось экспортировать: {message}",
|
||||
xlsxHeaderTitle: "Формат заголовков",
|
||||
|
||||
@@ -1903,6 +1903,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "Seçili Satırları HTML Olarak Dışa Aktar",
|
||||
exportSelectedRowsSql: "Seçili Satırları SQL INSERT Olarak Dışa Aktar",
|
||||
exportSelectedRowsTxt: "Seçili Satırları TXT Olarak Dışa Aktar",
|
||||
exportSelectedColumnsCsv: "Seçili Sütunları CSV Olarak Dışa Aktar",
|
||||
exportSelectedColumnsXlsx: "Seçili Sütunları XLSX Olarak Dışa Aktar",
|
||||
exportSelectedColumnsXlsxWithSql: "Seçili Sütunları SQL ile XLSX Olarak Dışa Aktar",
|
||||
exportSelectedColumnsJson: "Seçili Sütunları JSON Olarak Dışa Aktar",
|
||||
exportSelectedColumnsMarkdown: "Seçili Sütunları Markdown Olarak Dışa Aktar",
|
||||
exportSelectedColumnsHtml: "Seçili Sütunları HTML Olarak Dışa Aktar",
|
||||
exportSelectedColumnsSql: "Seçili Sütunları SQL INSERT Olarak Dışa Aktar",
|
||||
exportSelectedColumnsTxt: "Seçili Sütunları TXT Olarak Dışa Aktar",
|
||||
exportSelectedCellsCsv: "Seçili Hücreleri CSV Olarak Dışa Aktar",
|
||||
exportSelectedCellsXlsx: "Seçili Hücreleri XLSX Olarak Dışa Aktar",
|
||||
exportSelectedCellsXlsxWithSql: "Seçili Hücreleri SQL ile XLSX Olarak Dışa Aktar",
|
||||
exportSelectedCellsJson: "Seçili Hücreleri JSON Olarak Dışa Aktar",
|
||||
exportSelectedCellsMarkdown: "Seçili Hücreleri Markdown Olarak Dışa Aktar",
|
||||
exportSelectedCellsHtml: "Seçili Hücreleri HTML Olarak Dışa Aktar",
|
||||
exportSelectedCellsSql: "Seçili Hücreleri SQL INSERT Olarak Dışa Aktar",
|
||||
exportSelectedCellsTxt: "Seçili Hücreleri TXT Olarak Dışa Aktar",
|
||||
exported: "Dışa aktarıldı",
|
||||
exportFailed: "Dışa aktarma başarısız: {message}",
|
||||
xlsxHeaderTitle: "Başlık Biçimi",
|
||||
|
||||
@@ -2257,6 +2257,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "导出选中行为 HTML",
|
||||
exportSelectedRowsSql: "导出选中行为 SQL INSERT",
|
||||
exportSelectedRowsTxt: "导出选中行为 TXT",
|
||||
exportSelectedColumnsCsv: "导出选中列为 CSV",
|
||||
exportSelectedColumnsXlsx: "导出选中列为 XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "导出选中列为 XLSX(含 SQL)",
|
||||
exportSelectedColumnsJson: "导出选中列为 JSON",
|
||||
exportSelectedColumnsMarkdown: "导出选中列为 Markdown",
|
||||
exportSelectedColumnsHtml: "导出选中列为 HTML",
|
||||
exportSelectedColumnsSql: "导出选中列为 SQL INSERT",
|
||||
exportSelectedColumnsTxt: "导出选中列为 TXT",
|
||||
exportSelectedCellsCsv: "导出选区为 CSV",
|
||||
exportSelectedCellsXlsx: "导出选区为 XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "导出选区为 XLSX(含 SQL)",
|
||||
exportSelectedCellsJson: "导出选区为 JSON",
|
||||
exportSelectedCellsMarkdown: "导出选区为 Markdown",
|
||||
exportSelectedCellsHtml: "导出选区为 HTML",
|
||||
exportSelectedCellsSql: "导出选区为 SQL INSERT",
|
||||
exportSelectedCellsTxt: "导出选区为 TXT",
|
||||
exported: "导出完成",
|
||||
exportFailed: "导出失败:{message}",
|
||||
xlsxHeaderTitle: "表头格式",
|
||||
|
||||
@@ -2084,6 +2084,22 @@ export default withEnglishFallback({
|
||||
exportSelectedRowsHtml: "匯出選取資料列為 HTML",
|
||||
exportSelectedRowsSql: "匯出選取資料列為 SQL INSERT",
|
||||
exportSelectedRowsTxt: "匯出選取資料列為 TXT",
|
||||
exportSelectedColumnsCsv: "匯出選取欄為 CSV",
|
||||
exportSelectedColumnsXlsx: "匯出選取欄為 XLSX",
|
||||
exportSelectedColumnsXlsxWithSql: "匯出選取欄為 XLSX(含 SQL)",
|
||||
exportSelectedColumnsJson: "匯出選取欄為 JSON",
|
||||
exportSelectedColumnsMarkdown: "匯出選取欄為 Markdown",
|
||||
exportSelectedColumnsHtml: "匯出選取欄為 HTML",
|
||||
exportSelectedColumnsSql: "匯出選取欄為 SQL INSERT",
|
||||
exportSelectedColumnsTxt: "匯出選取欄為 TXT",
|
||||
exportSelectedCellsCsv: "匯出選取範圍為 CSV",
|
||||
exportSelectedCellsXlsx: "匯出選取範圍為 XLSX",
|
||||
exportSelectedCellsXlsxWithSql: "匯出選取範圍為 XLSX(含 SQL)",
|
||||
exportSelectedCellsJson: "匯出選取範圍為 JSON",
|
||||
exportSelectedCellsMarkdown: "匯出選取範圍為 Markdown",
|
||||
exportSelectedCellsHtml: "匯出選取範圍為 HTML",
|
||||
exportSelectedCellsSql: "匯出選取範圍為 SQL INSERT",
|
||||
exportSelectedCellsTxt: "匯出選取範圍為 TXT",
|
||||
exported: "匯出完成",
|
||||
exportFailed: "匯出失敗:{message}",
|
||||
copied: "已複製",
|
||||
|
||||
Reference in New Issue
Block a user