feat(editor): add cut and paste to query editor context menu
This commit is contained in:
9 files changed
+91
-1
No files matched your search
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, onActivated, onDeactivated, watch, shallowRef, computed, nextTick } from "vue";
|
||||
import { CaseLower, CaseUpper, Code2, Download, FileCode, Pencil, PencilRuler, Play, Copy, List, Search, Sparkles, Table2, TextSelect, Trash2 } from "@lucide/vue";
|
||||
import { CaseLower, CaseUpper, ClipboardPaste, Code2, Download, FileCode, Pencil, PencilRuler, Play, Copy, List, Scissors, Search, Sparkles, Table2, TextSelect, Trash2 } from "@lucide/vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import type { CompletionContext } from "@codemirror/autocomplete";
|
||||
import { Transaction, StateEffect } from "@codemirror/state";
|
||||
@@ -1324,6 +1324,50 @@ async function copySelectedSqlFromContextMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
async function cutSelectedSqlFromContextMenu() {
|
||||
if (!canCopySelectedSql.value) return;
|
||||
const currentView = view.value;
|
||||
if (!currentView) return;
|
||||
try {
|
||||
await copyToClipboard(selectedSql.value);
|
||||
// 剪切:复制后删除选中内容
|
||||
const selection = currentView.state.selection.main;
|
||||
if (!selection.empty) {
|
||||
currentView.dispatch({
|
||||
changes: { from: selection.from, to: selection.to },
|
||||
selection: { anchor: selection.from, head: selection.from },
|
||||
scrollIntoView: true,
|
||||
userEvent: "input.cut",
|
||||
});
|
||||
}
|
||||
toast(t("grid.cut"));
|
||||
focusEditor();
|
||||
} catch (e: any) {
|
||||
toast(t("grid.copyFailed", { message: e?.message || String(e) }), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
async function pasteClipboardSqlFromContextMenu() {
|
||||
if (props.readOnly) return;
|
||||
const currentView = view.value;
|
||||
if (!currentView) return;
|
||||
try {
|
||||
const text = await readTextFromClipboard();
|
||||
if (!text) return;
|
||||
const selection = currentView.state.selection.main;
|
||||
// 粘贴:替换选中内容或在光标处插入
|
||||
currentView.dispatch({
|
||||
changes: { from: selection.from, to: selection.to, insert: text },
|
||||
selection: { anchor: selection.from + text.length, head: selection.from + text.length },
|
||||
scrollIntoView: true,
|
||||
userEvent: "input.paste",
|
||||
});
|
||||
focusEditor();
|
||||
} catch (e: any) {
|
||||
toast(t("editor.contextMenu.pasteClipboardReadFailed", { message: e?.message || String(e) }), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
function selectAllSqlFromContextMenu() {
|
||||
const currentView = view.value;
|
||||
if (!currentView) return;
|
||||
@@ -1617,6 +1661,20 @@ const contextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
icon: Copy,
|
||||
shortcut: "Mod+C",
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.cutSelection"),
|
||||
action: cutSelectedSqlFromContextMenu,
|
||||
disabled: !canCopySelectedSql.value || props.readOnly,
|
||||
icon: Scissors,
|
||||
shortcut: "Mod+X",
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.pasteFromClipboard"),
|
||||
action: pasteClipboardSqlFromContextMenu,
|
||||
disabled: props.readOnly,
|
||||
icon: ClipboardPaste,
|
||||
shortcut: "Mod+V",
|
||||
},
|
||||
{
|
||||
label: t("editor.contextMenu.sendToAi"),
|
||||
action: () => {
|
||||
|
||||
@@ -1186,6 +1186,9 @@ export default {
|
||||
export: "Export",
|
||||
exportQueryResultTo: "Export current query result to {format}",
|
||||
copySelection: "Copy selection",
|
||||
cutSelection: "Cut selection",
|
||||
pasteFromClipboard: "Paste",
|
||||
pasteClipboardReadFailed: "Failed to read clipboard: {message}",
|
||||
sendToAi: "Send to AI",
|
||||
uppercaseSelection: "Convert to uppercase",
|
||||
lowercaseSelection: "Convert to lowercase",
|
||||
@@ -1848,6 +1851,7 @@ export default {
|
||||
sortUnsupported: "This SQL does not support full-result sorting. Try again with a single SELECT query.",
|
||||
truncatedHint: "Results were truncated after loading {count} rows. Use the footer pagination to browse loaded data; exporting the full result reruns the database query.",
|
||||
victoriaMetricsTruncatedHint: "Results exceeded the loading limit, so only the first {count} rows were loaded. Rows per page only paginates loaded data; shorten the time range or add filters to retrieve a complete result.",
|
||||
cut: "Cut",
|
||||
},
|
||||
exportProgress: {
|
||||
streamingUnsupported: "Streaming export is unsupported for this query. Simplify it or use a supported driver.",
|
||||
|
||||
@@ -1172,6 +1172,9 @@ export default withEnglishFallback({
|
||||
expandSelectStarUnavailable: "No se pudieron cargar las columnas de esta tabla",
|
||||
findReplace: "Buscar/Reemplazar",
|
||||
deleteEmptyLines: "Eliminar líneas vacías",
|
||||
cutSelection: "Cortar selección",
|
||||
pasteFromClipboard: "Pegar",
|
||||
pasteClipboardReadFailed: "Error al leer el portapapeles: {message}",
|
||||
},
|
||||
search: {
|
||||
find: "Buscar",
|
||||
@@ -1789,6 +1792,7 @@ export default withEnglishFallback({
|
||||
binaryImportApplied: "Se importaron {count} bytes, que se escribirán en la base de datos al guardar los cambios.",
|
||||
binaryImportFailed: "Error al importar el archivo: {message}",
|
||||
binaryImportTooLarge: "El archivo es demasiado grande ({size}, límite {limit}). No se admite importar archivos grandes en una sola celda.",
|
||||
cut: "Cortado",
|
||||
},
|
||||
exportProgress: {
|
||||
streamingUnsupported: "La exportación en streaming no es compatible con esta consulta. Simplifíquela o use un controlador compatible.",
|
||||
|
||||
@@ -1170,6 +1170,9 @@ export default withEnglishFallback({
|
||||
expandSelectStarUnavailable: "Impossibile caricare le colonne di questa tabella",
|
||||
findReplace: "Trova/Sostituisci",
|
||||
deleteEmptyLines: "Elimina righe vuote",
|
||||
cutSelection: "Taglia selezione",
|
||||
pasteFromClipboard: "Incolla",
|
||||
pasteClipboardReadFailed: "Impossibile leggere gli appunti: {message}",
|
||||
},
|
||||
search: {
|
||||
find: "Trova",
|
||||
@@ -1787,6 +1790,7 @@ export default withEnglishFallback({
|
||||
binaryImportApplied: "Importati {count} byte, scritti nel database dopo il salvataggio delle modifiche.",
|
||||
binaryImportFailed: "Importazione del file non riuscita: {message}",
|
||||
binaryImportTooLarge: "Il file è troppo grande ({size}, limite {limit}). L'importazione di file di grandi dimensioni in una singola cella non è supportata.",
|
||||
cut: "Tagliato",
|
||||
},
|
||||
exportProgress: {
|
||||
streamingUnsupported: "L'esportazione in streaming non è supportata per questa query. Semplificala o usa un driver supportato.",
|
||||
|
||||
@@ -1190,6 +1190,9 @@ export default withEnglishFallback({
|
||||
expandSelectStarUnavailable: "このテーブルの列を読み込めません",
|
||||
findReplace: "検索/置換",
|
||||
deleteEmptyLines: "空行を削除",
|
||||
cutSelection: "選択内容を切り取る",
|
||||
pasteFromClipboard: "貼り付け",
|
||||
pasteClipboardReadFailed: "クリップボードの読み取りに失敗しました:{message}",
|
||||
},
|
||||
search: {
|
||||
find: "検索",
|
||||
@@ -1814,6 +1817,7 @@ export default withEnglishFallback({
|
||||
binaryImportApplied: "{count} バイトをインポートしました。変更を保存するとデータベースに書き込まれます。",
|
||||
binaryImportFailed: "ファイルのインポートに失敗しました:{message}",
|
||||
binaryImportTooLarge: "ファイルが大きすぎます({size}、上限 {limit})。大きなファイルの単一セルへのインポートはサポートされていません。",
|
||||
cut: "切り取りました",
|
||||
},
|
||||
exportProgress: {
|
||||
streamingUnsupported: "このクエリはストリーミングエクスポートに対応していません。クエリを簡略化するか、対応しているドライバーを使用してください。",
|
||||
|
||||
@@ -1080,6 +1080,9 @@ export default withEnglishFallback({
|
||||
export: "내보내기",
|
||||
exportQueryResultTo: "현재 쿼리 결과를 {format}(으)로 내보내기",
|
||||
copySelection: "선택 영역 복사",
|
||||
cutSelection: "선택 영역 잘라내기",
|
||||
pasteFromClipboard: "붙여넣기",
|
||||
pasteClipboardReadFailed: "클립보드 읽기 실패: {message}",
|
||||
sendToAi: "AI로 보내기",
|
||||
uppercaseSelection: "대문자로 변환",
|
||||
lowercaseSelection: "소문자로 변환",
|
||||
@@ -1373,6 +1376,7 @@ export default withEnglishFallback({
|
||||
exported: "내보냈습니다",
|
||||
exportFailed: "내보내기 실패: {message}",
|
||||
copied: "복사했습니다",
|
||||
cut: "잘라냈습니다",
|
||||
copyFailed: "복사 실패: {message}",
|
||||
previewSqlEmpty: "미리볼 보류 중인 SQL 변경이 없습니다",
|
||||
renderMode: "렌더링 모드",
|
||||
|
||||
@@ -1171,6 +1171,9 @@ export default withEnglishFallback({
|
||||
expandSelectStarUnavailable: "Não foi possível carregar as colunas desta tabela",
|
||||
findReplace: "Localizar/substituir",
|
||||
deleteEmptyLines: "Excluir linhas vazias",
|
||||
cutSelection: "Recortar seleção",
|
||||
pasteFromClipboard: "Colar",
|
||||
pasteClipboardReadFailed: "Falha ao ler a área de transferência: {message}",
|
||||
},
|
||||
search: {
|
||||
find: "Localizar",
|
||||
@@ -1789,6 +1792,7 @@ export default withEnglishFallback({
|
||||
binaryImportApplied: "{count} bytes importados, gravados no banco de dados após salvar as alterações.",
|
||||
binaryImportFailed: "Falha ao importar o arquivo: {message}",
|
||||
binaryImportTooLarge: "O arquivo é muito grande ({size}, limite {limit}). Não há suporte para importar arquivos grandes em uma única célula.",
|
||||
cut: "Recortado",
|
||||
},
|
||||
exportProgress: {
|
||||
streamingUnsupported: "A exportação em streaming não é compatível com esta consulta. Simplifique-a ou use um driver compatível.",
|
||||
|
||||
@@ -1111,6 +1111,9 @@ export default withEnglishFallback({
|
||||
export: "导出",
|
||||
exportQueryResultTo: "导出当前查询结果集到 {format}",
|
||||
copySelection: "复制选中内容",
|
||||
cutSelection: "剪切选中内容",
|
||||
pasteFromClipboard: "粘贴",
|
||||
pasteClipboardReadFailed: "读取剪贴板失败:{message}",
|
||||
sendToAi: "发送到 AI",
|
||||
uppercaseSelection: "转为大写",
|
||||
lowercaseSelection: "转为小写",
|
||||
@@ -1414,6 +1417,7 @@ export default withEnglishFallback({
|
||||
xlsxHeaderOriginal: "表头使用字段名称",
|
||||
xlsxHeaderComment: "表头使用注释",
|
||||
copied: "已复制",
|
||||
cut: "已剪切",
|
||||
copyFailed: "复制失败:{message}",
|
||||
previewSqlEmpty: "暂无待预览的 SQL 更改",
|
||||
renderMode: "渲染模式",
|
||||
|
||||
@@ -1170,6 +1170,9 @@ export default withEnglishFallback({
|
||||
expandSelectStarUnavailable: "無法讀取此資料表的欄位資訊",
|
||||
findReplace: "查找/取代",
|
||||
deleteEmptyLines: "刪除空行",
|
||||
cutSelection: "剪下選取內容",
|
||||
pasteFromClipboard: "貼上",
|
||||
pasteClipboardReadFailed: "讀取剪貼簿失敗:{message}",
|
||||
},
|
||||
search: {
|
||||
find: "尋找",
|
||||
@@ -1788,6 +1791,7 @@ export default withEnglishFallback({
|
||||
xlsxHeaderPrompt: "請選擇匯出 Excel 時使用的表頭格式:",
|
||||
xlsxHeaderOriginal: "表頭使用欄位名稱",
|
||||
xlsxHeaderComment: "表頭使用註解",
|
||||
cut: "已剪下",
|
||||
},
|
||||
exportProgress: {
|
||||
streamingUnsupported: "目前查詢暫不支援串流匯出,請簡化查詢或使用支援的驅動程式。",
|
||||
|
||||
Reference in new issue
Block a user