From 3817269c0a45437a18eb7f8052add173d9a76800 Mon Sep 17 00:00:00 2001 From: Andy Lee Date: Fri, 31 Jul 2026 14:25:18 +0800 Subject: [PATCH] fix: bugs in VM selection table Signed-off-by: Andy Lee --- .../vm-migration/ConfigureMappingsStep.vue | 54 ++++++++------- .../components/vm-migration/MappingColumn.vue | 2 +- .../components/vm-migration/SelectVmsStep.vue | 67 +++++++++++++------ .../vm-migration/StorageDefaultsModal.vue | 1 + 4 files changed, 79 insertions(+), 45 deletions(-) diff --git a/pkg/harvester/components/vm-migration/ConfigureMappingsStep.vue b/pkg/harvester/components/vm-migration/ConfigureMappingsStep.vue index 75d7ab5c..fed7b2fa 100644 --- a/pkg/harvester/components/vm-migration/ConfigureMappingsStep.vue +++ b/pkg/harvester/components/vm-migration/ConfigureMappingsStep.vue @@ -126,6 +126,10 @@ const applyStorageMapTargets = (mapSpec, { markOverridden = false, captureInheri if (match?.destination?.storageClass) { entry.target = match.destination.storageClass; + if (captureInherited) { + entry.inheritedFromProvider = true; + } + if (match.destination.volumeMode) { entry.volumeMode = match.destination.volumeMode; @@ -252,18 +256,19 @@ const buildStorageEntries = () => { if (ds && ds.id) { if (!datastoreMap[ds.id]) { datastoreMap[ds.id] = { - name: ds.name || t('harvester.addons.vmMigration.generic.unknown'), - id: ds.id, - type: ds.type || '', - capacity: 0, - target: '', - volumeMode: DEFAULT_VOLUME_MODE, - accessModes: [...DEFAULT_ACCESS_MODES], - inheritedVolumeMode: DEFAULT_VOLUME_MODE, - inheritedAccessModes: [...DEFAULT_ACCESS_MODES], - overridden: false, - usedBy: [], - _key: `stor-${ ds.id }`, + name: ds.name || t('harvester.addons.vmMigration.generic.unknown'), + id: ds.id, + type: ds.type || '', + capacity: 0, + target: '', + volumeMode: DEFAULT_VOLUME_MODE, + accessModes: [...DEFAULT_ACCESS_MODES], + inheritedVolumeMode: DEFAULT_VOLUME_MODE, + inheritedAccessModes: [...DEFAULT_ACCESS_MODES], + inheritedFromProvider: false, + overridden: false, + usedBy: [], + _key: `stor-${ ds.id }`, }; } @@ -293,18 +298,19 @@ const buildNetworkEntriesFromProvider = (networksData) => { const buildStorageEntriesFromProvider = (datastoresData) => { storageEntries.value = (Array.isArray(datastoresData) ? datastoresData : []).map((ds) => ({ - name: ds.name || ds.id, - id: ds.id || '', - type: ds.type || '', - capacity: ds.capacity || 0, - target: '', - volumeMode: DEFAULT_VOLUME_MODE, - accessModes: [...DEFAULT_ACCESS_MODES], - inheritedVolumeMode: DEFAULT_VOLUME_MODE, - inheritedAccessModes: [...DEFAULT_ACCESS_MODES], - overridden: false, - usedBy: [], - _key: `stor-${ ds.id || ds.name }`, + name: ds.name || ds.id, + id: ds.id || '', + type: ds.type || '', + capacity: ds.capacity || 0, + target: '', + volumeMode: DEFAULT_VOLUME_MODE, + accessModes: [...DEFAULT_ACCESS_MODES], + inheritedVolumeMode: DEFAULT_VOLUME_MODE, + inheritedAccessModes: [...DEFAULT_ACCESS_MODES], + inheritedFromProvider: false, + overridden: false, + usedBy: [], + _key: `stor-${ ds.id || ds.name }`, })); }; diff --git a/pkg/harvester/components/vm-migration/MappingColumn.vue b/pkg/harvester/components/vm-migration/MappingColumn.vue index f234121d..e7d83749 100644 --- a/pkg/harvester/components/vm-migration/MappingColumn.vue +++ b/pkg/harvester/components/vm-migration/MappingColumn.vue @@ -108,7 +108,7 @@ const formatModes = (entry) => {
{{ formatModes(entry) }} {{ t('harvester.addons.vmMigration.storageDefaults.inherited', { provider: inheritedProviderName }) }} diff --git a/pkg/harvester/components/vm-migration/SelectVmsStep.vue b/pkg/harvester/components/vm-migration/SelectVmsStep.vue index cd55be06..33be7e1d 100644 --- a/pkg/harvester/components/vm-migration/SelectVmsStep.vue +++ b/pkg/harvester/components/vm-migration/SelectVmsStep.vue @@ -25,8 +25,9 @@ const { discoveredVMs, selectedVMIds, tableRows } = toRefs(props.stepData); const selectedVMs = ref([]); const loading = ref(true); -const networkMap = ref({}); -const datastoreMap = ref({}); +// Restored from stepData so friendly names survive step navigation / remount. +const networkMap = ref(props.stepData.networkMap || {}); +const datastoreMap = ref(props.stepData.datastoreMap || {}); const sortableTableRef = ref(null); const allVMsSelected = ref(false); const errors = ref([]); @@ -240,12 +241,22 @@ const clearSelection = () => { if (table) { table.clearSelection(); + + if (typeof table.setPage === 'function') { + table.setPage(1); + } } }); }; -// Re-sorts the table so currently-selected VMs appear on the first pages. -const showSelectedFirst = () => { +const selectAllVMs = () => { + allVMsSelected.value = true; + selectedVMIds.value = new Set(discoveredVMs.value.map((vm) => vm.id)); + selectedVMs.value = discoveredVMs.value.slice(); +}; + +// Moves currently-selected VMs to the first page(s) and re-checks them. +const sortSelectedToFront = () => { if (selectedVMIds.value.size === 0) { return; } @@ -258,19 +269,33 @@ const showSelectedFirst = () => { h.name === 'vmName' ? { ...h, sort: ['selectedSort', 'vmName'] } : h )); + skipNextSelectionEvent = true; + nextTick(() => { const table = sortableTableRef.value; - if (table) { - table.page = 1; - } - }); -}; + if (!table) { + skipNextSelectionEvent = false; -const selectAllVMs = () => { - allVMsSelected.value = true; - selectedVMIds.value = new Set(discoveredVMs.value.map((vm) => vm.id)); - selectedVMs.value = discoveredVMs.value.slice(); + return; + } + + if (typeof table.changeSort === 'function') { + table.changeSort('vmName', false); + } else if (typeof table.setPage === 'function') { + table.setPage(1); + } + + nextTick(() => { + const rowsToReselect = (table.pagedRows || []).filter((row) => selectedVMIds.value.has(row._original?.id)); + + if (rowsToReselect.length > 0) { + table.update(rowsToReselect, []); + } + + skipNextSelectionEvent = false; + }); + }); }; watch( @@ -342,6 +367,9 @@ const fetchVMs = async() => { return map; }, {}); + props.stepData.networkMap = networkMap.value; + props.stepData.datastoreMap = datastoreMap.value; + lastFetchedAt.value = Date.now(); tableRows.value = buildTableRows(); }; @@ -398,6 +426,10 @@ const init = async() => { tableRows.value = buildTableRows(); loading.value = false; + // Returning to the step with an existing selection: bring selected VMs forward and re-check them. + await nextTick(); + sortSelectedToFront(); + return; } @@ -465,7 +497,6 @@ init(); :row-actions="false" :groupable="false" :paging="true" - :rows-per-page="20" key-field="_key" @selection="onSelect" > @@ -475,13 +506,9 @@ init(); {{ t('harvester.addons.vmMigration.selectVms.availableVms') }} - + {{ selectedCount }} {{ t('harvester.addons.vmMigration.selectVms.selected') }} - +