diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 033e1ddb70..8f598d48fe 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -37,6 +37,7 @@ import { Maximize2, PanelBottom, PanelRight, + RefreshCw, TableProperties, UserRound, Database, @@ -11818,6 +11819,18 @@ const gridContextMenuItems = computed(() => { }), [exportSubmenu()], previewItems, + // 右键刷新:与工具栏刷新按钮/Mod+R 走同一个 onToolbarRefresh,方便 + // 习惯 Navicat 等工具在数据页右键刷新的用户(#7273)。 + [ + { label: "", separator: true }, + { + label: t("grid.refresh"), + action: () => void onToolbarRefresh(), + icon: RefreshCw, + shortcut: "Mod+R", + disabled: () => isSaving.value, + }, + ], ); }); diff --git a/apps/desktop/src/components/grid/__tests__/DataGridContextExtractor.spec.ts b/apps/desktop/src/components/grid/__tests__/DataGridContextExtractor.spec.ts index 58847304e8..1bdab14825 100644 --- a/apps/desktop/src/components/grid/__tests__/DataGridContextExtractor.spec.ts +++ b/apps/desktop/src/components/grid/__tests__/DataGridContextExtractor.spec.ts @@ -135,7 +135,7 @@ function pasteGridCell(host: HTMLElement, value: string) { gridRoot.dispatchEvent(paste); } -function mountGrid(initialResult = largeValueResult()) { +function mountGrid(initialResult = largeValueResult(), onReload?: () => void) { const result = shallowRef(markRaw(initialResult)); const onExecuteSql = vi.fn().mockResolvedValue(undefined); const pinia = createPinia(); @@ -169,6 +169,7 @@ function mountGrid(initialResult = largeValueResult()) { primaryKeys: ["id"], }, onExecuteSql, + ...(onReload ? { onReload } : {}), }), }, ); @@ -320,6 +321,23 @@ describe("DataGrid context menu target lifecycle", () => { expect(contextMenuLabels()).toContain("Filter"); }); + it("offers toolbar-equivalent refresh in the cell context menu (issue #7273)", async () => { + const onReload = vi.fn(); + const { host } = mountGrid(hydratedResult(1, "value"), onReload); + await settle(); + const cell = host.querySelector('[data-row-index="0"] [data-visible-col-index="1"]'); + if (!cell) throw new Error("Grid cell not found"); + openContextMenu(cell); + await settle(); + // The item renders its label plus the Mod+R shortcut keys, so match by prefix. + const refreshLabels = contextMenuLabels().filter((label) => label.startsWith("Refresh")); + expect(refreshLabels.length).toBe(1); + const refreshButton = [...document.querySelectorAll("[data-dbx-context-menu] button")].find((button) => button.textContent?.trim().startsWith("Refresh")); + refreshButton!.click(); + await settle(); + expect(onReload).toHaveBeenCalledTimes(1); + }); + it("keeps the native context menu for the expanded condition editor", async () => { const { host } = mountGrid(hydratedResult(1, "abcdefghijklmnopqrstuvwxyz")); await settle();