diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index a488cfb297..dd2e7c2513 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -105,6 +105,7 @@ import { nextKeyboardTransposeState, nextTransposeState, nextTransposeStateForRecordCount, + transposeRecordIndexesForMode, transposeFieldWidth, transposeScrollLeftForRecord, visibleTransposeRecordWindow, @@ -460,6 +461,7 @@ const previewDialogOpen = ref(false); const previewDialogConfig = shallowRef<{ component: any; props: Record } | null>(null); const transposeRowIndex = ref(null); const showTranspose = ref(false); +const multiRowTranspose = ref(false); const preserveTransposeOnNextResult = ref(false); const transposeScrollRef = ref(); const transposeScrollLeft = ref(0); @@ -4836,22 +4838,37 @@ const visibleTransposeRecordIndexes = computed(() => { const window = transposeRecordWindow.value; return Array.from({ length: window.end - window.start }, (_, offset) => window.start + offset); }); +const activeTransposeRecordIndexes = computed(() => + transposeRecordIndexesForMode({ + multiRow: multiRowTranspose.value, + activeRecordIndex: transposeRowIndex.value, + totalRecords: displayRowCount.value, + visibleRecordIndexes: visibleTransposeRecordIndexes.value, + }), +); +const transposeBeforeSpacerWidth = computed(() => + multiRowTranspose.value ? transposeRecordWindow.value.beforeWidth : 0, +); +const transposeAfterSpacerWidth = computed(() => + multiRowTranspose.value ? transposeRecordWindow.value.afterWidth : 0, +); const transposeRows = computed(() => { return buildVisibleTransposeRows({ columns: visibleColumns.value, records: displayRowRefs.value.map((_, index) => displayItemAt(index)?.data ?? []), - recordIndexes: visibleTransposeRecordIndexes.value, + recordIndexes: activeTransposeRecordIndexes.value, valueIndexes: visibleColumnIndexes.value, typeByColumn: columnTypeMap.value, displayValue: (value, _column, index) => formatCellCached(value, visibleColumnIndexes.value[index]), }); }); const isTransposeMode = computed(() => showTranspose.value && transposeRows.value.length > 0); -const transposeTotalWidth = computed( - () => - transposePinnedWidth.value + - Array.from({ length: displayRowCount.value }, (_, i) => getTransposeRecordWidth(i)).reduce((sum, w) => sum + w, 0), -); +const transposeTotalWidth = computed(() => { + const recordIndexes = multiRowTranspose.value + ? Array.from({ length: displayRowCount.value }, (_, i) => i) + : activeTransposeRecordIndexes.value; + return transposePinnedWidth.value + recordIndexes.reduce((sum, i) => sum + getTransposeRecordWidth(i), 0); +}); function transposeScrollElement(): HTMLElement | undefined { const raw = transposeScrollRef.value; @@ -4888,6 +4905,26 @@ function scrollTransposeRecordIntoView(rowIndex: number) { }); } +function setMultiRowTranspose(value: boolean) { + multiRowTranspose.value = value; + if (!showTranspose.value) return; + nextTick(updateTransposeViewport); + if (value && transposeRowIndex.value !== null) { + scrollTransposeRecordIntoView(transposeRowIndex.value); + } else { + nextTick(() => { + const el = transposeScrollElement(); + if (!el) return; + el.scrollLeft = 0; + updateTransposeViewport(); + }); + } +} + +function toggleMultiRowTranspose() { + setMultiRowTranspose(!multiRowTranspose.value); +} + function applyTransposeState(next: { showTranspose: boolean; transposeRowIndex: number | null }) { showTranspose.value = next.showTranspose; transposeRowIndex.value = next.transposeRowIndex; @@ -5624,6 +5661,9 @@ defineExpose({ toggleDdl: toggleTableInfo, showTableInfo, toggleTableInfo, + multiRowTranspose, + setMultiRowTranspose, + toggleMultiRowTranspose, focusSearch, visibleColumnCount, displayableColumnCount, @@ -6436,6 +6476,11 @@ const gridContextMenuItems = computed(() => { {{ t("grid.rowNumber") }} {{ (transposeRowIndex ?? 0) + 1 }} + + {{ multiRowTranspose ? t("grid.transposeMultiRow") : t("grid.transposeSingleRow") }} +