From 0e474cecd49978627bdd306adc4df6f1daeacc5c Mon Sep 17 00:00:00 2001 From: gggaiitx <62124152+gggaiitx@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:23:36 +0800 Subject: [PATCH] feat(grid): add copy selected rows as TSV --- apps/desktop/src/components/grid/DataGrid.vue | 6 ++++++ apps/desktop/src/composables/useDataGridExport.ts | 8 ++++++++ apps/desktop/src/i18n/locales/en.ts | 4 ++++ apps/desktop/src/i18n/locales/es.ts | 4 ++++ apps/desktop/src/i18n/locales/it.ts | 4 ++++ apps/desktop/src/i18n/locales/ja.ts | 4 ++++ apps/desktop/src/i18n/locales/pt-BR.ts | 4 ++++ apps/desktop/src/i18n/locales/zh-CN.ts | 4 ++++ apps/desktop/src/i18n/locales/zh-TW.ts | 4 ++++ 9 files changed, 42 insertions(+) diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 62d915c6f..83f2de130 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -6116,6 +6116,7 @@ const { copySelectionJson, copySelectionSqlInList, copySelectedRowsTsv, + copySelectedRowsTsvWithHeaders, copyColumnNames, exportCsv, exportCurrentPageCsv, @@ -8245,6 +8246,11 @@ function copySubmenu(): ContextMenuItem { items.push({ label: t("grid.copyCell"), action: copyCell }); } items.push({ label: labels.row, action: copyRow }); + if (hasRowSelection.value && selectedRowCount.value > 0) { + const singleRowSelected = selectedRowCount.value === 1; + items.push({ label: singleRowSelected ? t("grid.copySelectedRowTsv") : t("grid.copySelectedRowsTsv", { count: selectedRowCount.value }), action: copySelectedRowsTsv }); + items.push({ label: singleRowSelected ? t("grid.copySelectedRowTsvWithHeaders") : t("grid.copySelectedRowsTsvWithHeaders", { count: selectedRowCount.value }), action: copySelectedRowsTsvWithHeaders }); + } if (isMultiRow.value) { items.push({ label: labels.insertMerged, action: () => copyRowAsInsert("merged"), disabled: !canCopyPreparedInsert(false, "merged") }); items.push({ label: labels.insertRowByRow, action: () => copyRowAsInsert("row-by-row"), disabled: !canCopyPreparedInsert(false, "row-by-row") }); diff --git a/apps/desktop/src/composables/useDataGridExport.ts b/apps/desktop/src/composables/useDataGridExport.ts index 95b03e305..3fc1aa01a 100644 --- a/apps/desktop/src/composables/useDataGridExport.ts +++ b/apps/desktop/src/composables/useDataGridExport.ts @@ -453,6 +453,13 @@ export function useDataGridExport(options: UseDataGridExportOptions) { await copyText(formatSelectionAsTsv({ columns: columns.value, rows })); } + async function copySelectedRowsTsvWithHeaders() { + if (!hasRowSelection.value || selectedRowIds.value.size === 0) return; + const rows = displayItems.value.filter((item) => selectedRowIds.value.has(item.id) && !item.isDraft).map((item) => item.data); + if (rows.length === 0) return; + await copyText(formatSelectionAsTsv({ columns: columns.value, rows }, true)); + } + async function copyColumnNames() { if (columns.value.length === 0) return; await copyText(columns.value.join("\t")); @@ -1112,6 +1119,7 @@ export function useDataGridExport(options: UseDataGridExportOptions) { copySelectionJson, copySelectionSqlInList, copySelectedRowsTsv, + copySelectedRowsTsvWithHeaders, copyColumnNames, exportCsv, exportCurrentPageCsv, diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index fd5b9008c..8417cab6f 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -852,6 +852,10 @@ export default { cloneRows: "Clone {count} as New Rows", restoreRows: "Restore {count} Rows", copyRows: "Copy {count} Rows (JSON)", + copySelectedRowsTsv: "Copy {count} Rows (TSV)", + copySelectedRowsTsvWithHeaders: "Copy {count} Rows with Headers (TSV)", + copySelectedRowTsv: "Copy 1 Row (TSV)", + copySelectedRowTsvWithHeaders: "Copy 1 Row with Headers (TSV)", copyColumnsJson: "Copy {count} Columns (JSON)", copyRowsInsert: "Copy {count} Rows as INSERT", copyRowsInsertMerged: "Copy {count} Rows as INSERT (Merged)", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index 412f1739c..f8d43dee1 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -796,6 +796,10 @@ export default withEnglishFallback({ cloneRows: "Clonar {count} como filas nuevas", restoreRows: "Restaurar {count} filas", copyRows: "Copiar {count} filas (JSON)", + copySelectedRowsTsv: "Copiar {count} filas (TSV)", + copySelectedRowsTsvWithHeaders: "Copiar {count} filas con encabezados (TSV)", + copySelectedRowTsv: "Copiar 1 fila (TSV)", + copySelectedRowTsvWithHeaders: "Copiar 1 fila con encabezados (TSV)", copyColumnsJson: "Copiar {count} columnas (JSON)", copyRowsInsert: "Copiar {count} filas como INSERT", copyRowsInsertMerged: "Copiar {count} filas como INSERT (combinado)", diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts index 72ea71e38..92697c3fe 100644 --- a/apps/desktop/src/i18n/locales/it.ts +++ b/apps/desktop/src/i18n/locales/it.ts @@ -794,6 +794,10 @@ export default withEnglishFallback({ cloneRows: "Clona {count} come Nuove Righe", restoreRows: "Ripristina {count} Righe", copyRows: "Copia {count} Righe (JSON)", + copySelectedRowsTsv: "Copia {count} Righe (TSV)", + copySelectedRowsTsvWithHeaders: "Copia {count} Righe con Intestazioni (TSV)", + copySelectedRowTsv: "Copia 1 Riga (TSV)", + copySelectedRowTsvWithHeaders: "Copia 1 Riga con Intestazioni (TSV)", copyColumnsJson: "Copia {count} Colonne (JSON)", copyRowsInsert: "Copia {count} Righe come INSERT", copyRowsInsertMerged: "Copia {count} Righe come INSERT (unito)", diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts index 5650349bc..188484b1a 100644 --- a/apps/desktop/src/i18n/locales/ja.ts +++ b/apps/desktop/src/i18n/locales/ja.ts @@ -790,6 +790,10 @@ export default withEnglishFallback({ cloneRows: "{count}行を新しい行として複製", restoreRows: "{count}行を復元", copyRows: "{count}行をコピー (JSON)", + copySelectedRowsTsv: "{count}行をコピー (TSV)", + copySelectedRowsTsvWithHeaders: "{count}行をヘッダー付きでコピー (TSV)", + copySelectedRowTsv: "1行をコピー (TSV)", + copySelectedRowTsvWithHeaders: "1行をヘッダー付きでコピー (TSV)", copyColumnsJson: "{count}列をコピー (JSON)", copyRowsInsert: "{count}行をINSERTとしてコピー", copyRowsInsertMerged: "{count}行をINSERTとしてコピー(まとめて)", diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts index c7fa7c362..931040497 100644 --- a/apps/desktop/src/i18n/locales/pt-BR.ts +++ b/apps/desktop/src/i18n/locales/pt-BR.ts @@ -795,6 +795,10 @@ export default withEnglishFallback({ cloneRows: "Clonar {count} como Novas Linhas", restoreRows: "Restaurar {count} Linhas", copyRows: "Copiar {count} Linhas (JSON)", + copySelectedRowsTsv: "Copiar {count} Linhas (TSV)", + copySelectedRowsTsvWithHeaders: "Copiar {count} Linhas com Cabeçalhos (TSV)", + copySelectedRowTsv: "Copiar 1 Linha (TSV)", + copySelectedRowTsvWithHeaders: "Copiar 1 Linha com Cabeçalhos (TSV)", copyColumnsJson: "Copiar {count} Colunas (JSON)", copyRowsInsert: "Copiar {count} Linhas como INSERT", copyRowsInsertMerged: "Copiar {count} Linhas como INSERT (mesclado)", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 6a3a5958d..5a5af7e89 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -854,6 +854,10 @@ export default withEnglishFallback({ cloneRows: "克隆 {count} 行为新行", restoreRows: "恢复 {count} 行", copyRows: "复制 {count} 行 (JSON)", + copySelectedRowsTsv: "复制 {count} 行 (TSV)", + copySelectedRowsTsvWithHeaders: "复制 {count} 行含表头 (TSV)", + copySelectedRowTsv: "复制 1 行 (TSV)", + copySelectedRowTsvWithHeaders: "复制 1 行含表头 (TSV)", copyColumnsJson: "复制 {count} 列 (JSON)", copyRowsInsert: "复制 {count} 行为 INSERT", copyRowsInsertMerged: "复制 {count} 行为 INSERT(合并)", diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts index f56644cb0..0a55e773c 100644 --- a/apps/desktop/src/i18n/locales/zh-TW.ts +++ b/apps/desktop/src/i18n/locales/zh-TW.ts @@ -795,6 +795,10 @@ export default withEnglishFallback({ cloneRows: "複製 {count} 筆為新資料", restoreRows: "復原 {count} 筆", copyRows: "複製 {count} 筆 (JSON)", + copySelectedRowsTsv: "複製 {count} 筆 (TSV)", + copySelectedRowsTsvWithHeaders: "複製 {count} 筆含表頭 (TSV)", + copySelectedRowTsv: "複製 1 筆 (TSV)", + copySelectedRowTsvWithHeaders: "複製 1 筆含表頭 (TSV)", copyColumnsJson: "複製 {count} 欄 (JSON)", copyRowsInsert: "複製 {count} 筆為 INSERT", copyRowsInsertMerged: "複製 {count} 筆為 INSERT(合併)",