From c8ef1f44f339b6908394955a9d77c6381efdd316 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 9 Sep 2026 13:12:44 +0800 Subject: [PATCH] fix(oracle): copy temporal cells in display format Closes #8569 --- apps/desktop/src/components/grid/DataGrid.vue | 1 + .../__tests__/useDataGridExport.spec.ts | 36 ++++++++++++++++++- .../src/composables/useDataGridExport.ts | 9 ++++- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index e99b28dd19..124b5b8aeb 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -7012,6 +7012,7 @@ const { sourceColumns: visibleSourceColumns, columnComments: visibleColumnComments, allColumnComments, + displayValue: formatCellCached, mongoDocuments: computed(() => props.result.mongo_copy_documents ?? props.result.mongo_documents), spatialColumns: computed(() => props.result.spatial_columns), spatialValues: computed(() => props.result.spatial_values), diff --git a/apps/desktop/src/composables/__tests__/useDataGridExport.spec.ts b/apps/desktop/src/composables/__tests__/useDataGridExport.spec.ts index 48c46d5cf8..8fd9d873a6 100644 --- a/apps/desktop/src/composables/__tests__/useDataGridExport.spec.ts +++ b/apps/desktop/src/composables/__tests__/useDataGridExport.spec.ts @@ -1,10 +1,12 @@ import { computed, ref } from "vue"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { useDataGridExport, type UseDataGridExportOptions } from "@/composables/useDataGridExport"; +import type { DatabaseType } from "@/types/database"; import { buildDataGridCopyUpdateStatements } from "@/lib/dataGrid/dataGridSql"; import { copyToClipboard } from "@/lib/common/clipboard"; import type { DataGridTableMeta } from "@/lib/dataGrid/dataGridSql"; import type { CellSelectionMatrix, SelectionData } from "@/lib/dataGrid/gridSelection"; +import type { CellValue } from "@/lib/dataGrid/cellValue"; import { extractDataGridSelection } from "@/lib/backend/api"; import { DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS } from "@/lib/dataGrid/dataGridCopyExtractor"; import { clearDataGridClipboardCopy, parseDataGridClipboard } from "@/lib/dataGrid/dataGridClipboard"; @@ -115,6 +117,8 @@ function createExportState( isSyntheticContext = false, contextRowId?: number | null, contextColumn?: number, + databaseType: DatabaseType = "mysql", + displayValue?: (value: CellValue, columnIndex: number) => string, ) { const rows = (rowDataList ?? [rowData ?? columns.map((column, index) => (column === "id" ? 1 : `value-${index}`))]).map((data, index) => ({ ...row(data), id: index + 1 })); const resolvedContextRowId = contextRowId === undefined ? (rows[0]?.id ?? null) : contextRowId; @@ -124,7 +128,8 @@ function createExportState( displayItems: computed(() => rows), sql: computed(() => undefined), tableMeta: computed(() => tableMeta), - databaseType: computed(() => "mysql"), + databaseType: computed(() => databaseType), + displayValue, connectionId: computed(() => "connection-1"), database: computed(() => "dbx"), context: computed(() => "table-data"), @@ -738,6 +743,35 @@ describe("useDataGridExport prepared row statements", () => { expect(copyToClipboard).toHaveBeenCalledWith(""); }); + it("copies Oracle temporal cells using the displayed value", async () => { + const table: DataGridTableMeta = { + tableName: "events", + columns: [{ name: "created_at", data_type: "timestamp", is_nullable: true }], + primaryKeys: [], + }; + const state = createExportState( + table, + ["created_at"], + undefined, + ["2020-12-02T15:18:29"], + undefined, + undefined, + [], + DEFAULT_DATA_GRID_EXTRACTOR_OPTIONS, + false, + undefined, + false, + 1, + 0, + "oracle", + () => "2020-12-02 15:18:29", + ); + + await state.copyCell(); + + expect(copyToClipboard).toHaveBeenCalledWith("2020-12-02 15:18:29"); + }); + it("copies all rows with empty fields for NULL cells", async () => { const text = "id\tname\n1\t\n2\tAda"; const state = createExportState(editableTable, ["id", "name"], undefined, undefined, undefined, [ diff --git a/apps/desktop/src/composables/useDataGridExport.ts b/apps/desktop/src/composables/useDataGridExport.ts index 06998c5da0..e2c4c81064 100644 --- a/apps/desktop/src/composables/useDataGridExport.ts +++ b/apps/desktop/src/composables/useDataGridExport.ts @@ -23,6 +23,7 @@ import { useSettingsStore } from "@/stores/settingsStore"; import { expandNestedJsonStringsForCopy } from "@/lib/common/jsonCopyValue"; import { buildMongoCopyDocumentFromOriginal, buildMongoCopyInsertDocument, buildMongoCopyUpdateDocument, formatMongoShellLiteral, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues"; import { formatMongoShellText } from "@/lib/mongo/mongoFormatter"; +import { isTemporalColumnType } from "@/lib/dataGrid/columnFormatter"; import type { DatabaseType, QueryResult } from "@/types/database"; import type { QueryResultExportRequest } from "@/lib/backend/api"; import { usesSyntheticRowIdKey } from "@/lib/table/tableEditing"; @@ -82,6 +83,7 @@ export interface UseDataGridExportOptions { copyInsertTargetLabel?: ComputedRef; mongoUpdateTarget?: ComputedRef; databaseType: ComputedRef; + displayValue?: (value: CellValue, columnIndex: number) => string; identifierQuote?: ComputedRef; connectionId: ComputedRef; database: ComputedRef; @@ -553,7 +555,12 @@ export function useDataGridExport(options: UseDataGridExportOptions) { const [resolvedItem] = await resolveVisibleRowValues([item], [sourceIndex]); const val = resolvedItem?.data[contextCell.value.col] ?? null; // 外部剪贴板呈现文本型 MySQL VARBINARY(NULL 也按空串输出);内部网格副本仍保留原 hex,保证回粘无损。 - await copyText(clipboardCellValue(binaryClipboardCellValue(val, contextCell.value.col)), { rows: [[val]] }); + const rawValue = clipboardCellValue(binaryClipboardCellValue(val, contextCell.value.col)); + const copyValue = + options.databaseType.value === "oracle" && isTemporalColumnType(options.columnTypes.value?.[contextCell.value.col]) + ? options.displayValue?.(val, sourceIndex) ?? rawValue + : rawValue; + await copyText(copyValue, { rows: [[val]] }); } async function copyRow() {