fix(grid): enable prepared row copy actions

This commit is contained in:
Freedom 2026-07-13 00:27:47 +08:00 committed by GitHub
parent a3f3cadb39
commit d27f8e18a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 14 deletions

View File

@ -8683,18 +8683,15 @@ function copySubmenu(): ContextMenuItem {
items.push({
label: labels.insertNoPkMerged,
action: () => copyRowAsInsertWithoutPrimaryKeys("merged"),
disabled: !canCopyRowAsInsertWithoutPrimaryKeys.value,
});
items.push({
label: labels.insertNoPkRowByRow,
action: () => copyRowAsInsertWithoutPrimaryKeys("row-by-row"),
disabled: !canCopyRowAsInsertWithoutPrimaryKeys.value,
});
} else {
items.push({
label: labels.insertNoPk,
action: () => copyRowAsInsertWithoutPrimaryKeys(),
disabled: !canCopyRowAsInsertWithoutPrimaryKeys.value,
});
}
}

View File

@ -390,9 +390,7 @@ test("copy row as INSERT refreshes prepared SQL after row data changes", async (
isDirtyCol: [false, false],
status: "",
};
apiMock.buildDataGridCopyInsertStatement
.mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'before');")
.mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'after');");
apiMock.buildDataGridCopyInsertStatement.mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'before');").mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'after');");
const composable = useDataGridExport({
columns: computed(() => ["id", "name"]),
displayItems: computed(() => [row]),
@ -426,14 +424,61 @@ test("copy row as INSERT refreshes prepared SQL after row data changes", async (
await composable.copyRowAsInsert();
assert.equal(apiMock.buildDataGridCopyInsertStatement.mock.calls.length, 2);
assert.deepEqual(apiMock.buildDataGridCopyInsertStatement.mock.calls.map((call) => call[0].rows), [
[[1, "before"]],
[[1, "after"]],
]);
assert.deepEqual(clipboardMock.copyToClipboard.mock.calls.map((call) => call[0]), [
"INSERT INTO users (id, name) VALUES (1, 'before');",
"INSERT INTO users (id, name) VALUES (1, 'after');",
]);
assert.deepEqual(
apiMock.buildDataGridCopyInsertStatement.mock.calls.map((call) => call[0].rows),
[[[1, "before"]], [[1, "after"]]],
);
assert.deepEqual(
clipboardMock.copyToClipboard.mock.calls.map((call) => call[0]),
["INSERT INTO users (id, name) VALUES (1, 'before');", "INSERT INTO users (id, name) VALUES (1, 'after');"],
);
});
test("copy row as INSERT works without prior prefetch (first context-menu click)", async () => {
const contextCell = ref({ rowId: 1, rowIndex: 0, col: 0 });
const row = {
id: 1,
data: [1, "alice"],
isNew: false,
isDeleted: false,
isDirtyCol: [false, false],
status: "",
};
apiMock.buildDataGridCopyInsertStatement.mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'alice');");
const composable = useDataGridExport({
columns: computed(() => ["id", "name"]),
displayItems: computed(() => [row]),
sql: computed(() => undefined),
tableMeta: computed(() => ({
tableName: "users",
primaryKeys: ["id"],
})),
databaseType: computed(() => "mysql"),
connectionId: computed(() => "conn-1"),
database: computed(() => "db"),
context: computed(() => "table-data"),
sourceColumns: computed(() => undefined),
columnTypes: computed(() => undefined),
whereInput: computed(() => undefined),
orderBy: computed(() => undefined),
exportBatchSize: computed(() => 1000),
hasCellSelection: computed(() => false),
selectedCells: computed(() => ({ columns: [], rows: [] })),
selectedRange: computed(() => null),
contextCell,
getRowItem: () => row,
selectedRowIds: ref(new Set<number>()),
hasRowSelection: computed(() => false),
});
assert.equal(composable.canCopyRowAsInsert.value, true);
assert.equal(composable.canCopyPreparedInsert(false), false);
await composable.copyRowAsInsert();
assert.equal(apiMock.buildDataGridCopyInsertStatement.mock.calls.length, 1);
assert.equal(clipboardMock.copyToClipboard.mock.calls[0][0], "INSERT INTO users (id, name) VALUES (1, 'alice');");
assert.equal(composable.canCopyPreparedInsert(false), true);
});
test("default data grid export file names use sanitized base names and compact local timestamps", () => {