diff --git a/src/components/grid/DataGrid.vue b/src/components/grid/DataGrid.vue
index f0c2cbae82..9e5a6e644f 100644
--- a/src/components/grid/DataGrid.vue
+++ b/src/components/grid/DataGrid.vue
@@ -35,6 +35,7 @@ import {
FileDown,
SquareDashed,
Check,
+ CopyPlus,
} from "lucide-vue-next";
import { Button } from "@/components/ui/button";
import {
@@ -882,6 +883,7 @@ const {
applyCellValue,
onEditKeydown,
addRow,
+ cloneRow,
showDeleteRowConfirm,
requestDeleteRow,
confirmDeleteRow,
@@ -2513,6 +2515,9 @@ defineExpose({
+
+ {{ t("grid.cloneRow") }}
+
{{ t("grid.restoreRow") }}
diff --git a/src/composables/useDataGridEditor.ts b/src/composables/useDataGridEditor.ts
index 3d9b2b983f..f7126a5c52 100644
--- a/src/composables/useDataGridEditor.ts
+++ b/src/composables/useDataGridEditor.ts
@@ -365,6 +365,25 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
});
}
+ function cloneRow(rowId: number) {
+ const item = getRowItem(rowId);
+ if (!item) return;
+ const primaryKeys = tableMeta.value?.primaryKeys ?? [];
+ const columns = result.value.columns;
+ const clonedData = item.data.map((val, i) => (primaryKeys.includes(columns[i]) ? null : val));
+ rowStatusFilter.value = rowStatusFilterAfterAddingRow(rowStatusFilter.value);
+ newRows.value.push(clonedData);
+ if (useTransaction.value && !transactionActive.value) {
+ enterTransaction();
+ }
+ const newRowId = -newRows.value.length;
+ nextTick(() => {
+ const el = getScrollerElement();
+ if (el) el.scrollTop = el.scrollHeight;
+ startEdit(newRowId, initialEditColumn?.value ?? 0);
+ });
+ }
+
function applyDeleteRow(rowId: number) {
const item = getRowItem(rowId);
if (!item) return;
@@ -622,6 +641,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
cancelEdit,
onEditKeydown,
addRow,
+ cloneRow,
applyDeleteRow,
showDeleteRowConfirm,
pendingDeleteRowId,
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index bde8438de0..6952d6143f 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -275,6 +275,7 @@ export default {
discard: "Discard",
dismiss: "Dismiss",
addRow: "Add Row",
+ cloneRow: "Clone Row",
deleteRow: "Delete Row",
restoreRow: "Restore Row",
statusClean: "Clean",
diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts
index f69528235c..5f84833bd4 100644
--- a/src/i18n/locales/es.ts
+++ b/src/i18n/locales/es.ts
@@ -276,6 +276,7 @@ export default {
discard: "Descartar",
dismiss: "Cerrar",
addRow: "Agregar fila",
+ cloneRow: "Clonar fila",
deleteRow: "Eliminar fila",
restoreRow: "Restaurar fila",
statusClean: "Sin cambios",
diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts
index 20780bfa60..49ed836b63 100644
--- a/src/i18n/locales/zh-CN.ts
+++ b/src/i18n/locales/zh-CN.ts
@@ -274,6 +274,7 @@ export default {
discard: "放弃",
dismiss: "关闭",
addRow: "新增行",
+ cloneRow: "克隆行",
deleteRow: "删除行",
restoreRow: "恢复行",
statusClean: "未改",