From 424bbdfc52d56cb69a5e32b29a130c988de00eb0 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sat, 9 May 2026 01:06:19 +0800 Subject: [PATCH] feat: add value editor and Set NULL in cell detail panel Double-click value area or click Edit button to open textarea editor for long text/JSON. Add Set NULL button for quick null assignment. Fix: null values via ?? operator were ignored in dirty row display. --- src/components/grid/DataGrid.vue | 116 ++++++++++++++++++++++++++++++- src/i18n/locales/en.ts | 2 + src/i18n/locales/zh-CN.ts | 2 + 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/components/grid/DataGrid.vue b/src/components/grid/DataGrid.vue index 00b9ced145..ef7c6834b8 100644 --- a/src/components/grid/DataGrid.vue +++ b/src/components/grid/DataGrid.vue @@ -31,6 +31,7 @@ import { TriangleAlert, RefreshCcw, RotateCcw, + Pencil, } from "lucide-vue-next"; import { Button } from "@/components/ui/button"; import { @@ -556,7 +557,7 @@ const sortedRows = computed(() => { function rowDataWithChanges(row: CellValue[], sourceIndex: number): CellValue[] { const dirty = dirtyRows.value.get(sourceIndex); - return row.map((v, colIdx) => dirty?.get(colIdx) ?? v); + return row.map((v, colIdx) => (dirty?.has(colIdx) ? dirty.get(colIdx)! : v)); } interface RowItem { @@ -637,6 +638,7 @@ const activeCellDetail = computed(() => { } return { rowNumber: cell.rowIndex + 1, + rowId: item.id, colIndex: cell.col, column, type: columnTypeMap.value.get(column) || "", @@ -645,9 +647,83 @@ const activeCellDetail = computed(() => { rawValue, length: value === null ? 0 : String(value).length, formattedJson, + isEditable: props.editable && !item.isDeleted, }; }); +const detailEditValue = ref(""); +const isEditingDetail = ref(false); + +function startDetailEdit() { + const detail = activeCellDetail.value; + if (!detail || !detail.isEditable) return; + detailEditValue.value = + detail.value === null ? "" : typeof detail.value === "object" ? JSON.stringify(detail.value) : String(detail.value); + isEditingDetail.value = true; +} + +function commitDetailEdit() { + const detail = activeCellDetail.value; + if (!detail || !isEditingDetail.value) return; + isEditingDetail.value = false; + + const item = getRowItem(detail.rowId); + if (!item || item.isDeleted) return; + + if (item.isNew && item.newIndex !== undefined) { + const oldVal = newRows.value[item.newIndex]?.[detail.colIndex]; + newRows.value[item.newIndex][detail.colIndex] = coerceCellValue(detailEditValue.value, oldVal); + return; + } + + if (item.sourceIndex === undefined) return; + + const oldVal = props.result.rows[item.sourceIndex]?.[detail.colIndex]; + const newVal = coerceCellValue(detailEditValue.value, oldVal); + if (newVal !== oldVal) { + if (!dirtyRows.value.has(item.sourceIndex)) dirtyRows.value.set(item.sourceIndex, new Map()); + dirtyRows.value.get(item.sourceIndex)!.set(detail.colIndex, newVal); + if (useTransaction.value && !transactionActive.value) { + enterTransaction(); + } + } else { + const rowChanges = dirtyRows.value.get(item.sourceIndex); + rowChanges?.delete(detail.colIndex); + if (rowChanges?.size === 0) dirtyRows.value.delete(item.sourceIndex); + } + dirtyRows.value = new Map(dirtyRows.value); +} + +function cancelDetailEdit() { + isEditingDetail.value = false; +} + +function setDetailNull() { + const detail = activeCellDetail.value; + if (!detail || !detail.isEditable) return; + + const item = getRowItem(detail.rowId); + if (!item || item.isDeleted) return; + + if (item.isNew && item.newIndex !== undefined) { + newRows.value[item.newIndex][detail.colIndex] = null; + newRows.value = [...newRows.value]; + isEditingDetail.value = false; + detailCell.value = { ...detailCell.value! }; + return; + } + + if (item.sourceIndex === undefined) return; + if (!dirtyRows.value.has(item.sourceIndex)) dirtyRows.value.set(item.sourceIndex, new Map()); + dirtyRows.value.get(item.sourceIndex)!.set(detail.colIndex, null); + dirtyRows.value = new Map(dirtyRows.value); + if (useTransaction.value && !transactionActive.value) { + enterTransaction(); + } + isEditingDetail.value = false; + detailCell.value = { ...detailCell.value! }; +} + function toggleSort(colName: string, colIdx: number) { if (isResizing) return; if (sortCol.value === colName && sortColIndex.value === colIdx) { @@ -1802,8 +1878,26 @@ defineExpose({
{{ activeCellDetail.rawValue }}