fix: 数据迁移模块
This commit is contained in:
@@ -1611,7 +1611,9 @@ const handleDetailMigrateState = () => {
|
||||
loadDataMigrateProgress()
|
||||
startMigratePolling()
|
||||
}
|
||||
startDetailAutoRefresh()
|
||||
} else if (!vm.migrating) {
|
||||
stopDetailAutoRefresh()
|
||||
if (migratePollingTimer) {
|
||||
stopMigratePolling()
|
||||
dataMigrateProgressData.value = null
|
||||
@@ -2287,12 +2289,21 @@ const migrateProgressBarStatus = (stage) => {
|
||||
let migratePollingTimer = null
|
||||
const startMigratePolling = () => {
|
||||
stopMigratePolling()
|
||||
migratePollingTimer = setInterval(loadDataMigrateProgress, 5000)
|
||||
migratePollingTimer = setInterval(loadDataMigrateProgress, 3000)
|
||||
}
|
||||
const stopMigratePolling = () => {
|
||||
if (migratePollingTimer) { clearInterval(migratePollingTimer); migratePollingTimer = null }
|
||||
}
|
||||
|
||||
let detailAutoRefreshTimer = null
|
||||
const startDetailAutoRefresh = () => {
|
||||
if (detailAutoRefreshTimer) return
|
||||
detailAutoRefreshTimer = setInterval(() => { loadDetail() }, 3000)
|
||||
}
|
||||
const stopDetailAutoRefresh = () => {
|
||||
if (detailAutoRefreshTimer) { clearInterval(detailAutoRefreshTimer); detailAutoRefreshTimer = null }
|
||||
}
|
||||
|
||||
const abortLoading = ref(false)
|
||||
const handleAbortMigrate = () => {
|
||||
ElMessageBox.confirm('确定要中断当前数据迁移吗?此操作不可恢复!', '中断迁移', {
|
||||
@@ -3361,7 +3372,7 @@ onActivated(() => {
|
||||
if (loadedVmId !== vmId.value) initPage()
|
||||
})
|
||||
onDeactivated(() => { isPageActive = false; stopMigratePolling() })
|
||||
onBeforeUnmount(() => { isPageActive = false; disposeCharts(); stopMigratePolling() })
|
||||
onBeforeUnmount(() => { isPageActive = false; disposeCharts(); stopMigratePolling(); stopDetailAutoRefresh() })
|
||||
onMounted(() => { isPageActive = true; initPage() })
|
||||
</script>
|
||||
|
||||
|
||||
@@ -49,10 +49,21 @@
|
||||
<span v-else class="text-muted">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="140">
|
||||
<el-table-column label="状态" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="vmStatusType(row.status)" size="small">{{ vmStatusLabel(row.status) }}</el-tag>
|
||||
<el-tag v-if="row.data_migrate_status && !['completed','failed','aborted','cancelled'].includes(row.data_migrate_status)" type="warning" size="small" effect="dark" style="margin-left:4px">迁移中</el-tag>
|
||||
<template v-if="row.migrating">
|
||||
<div class="migrate-inline-status">
|
||||
<span class="migrate-inline-label">迁移中</span>
|
||||
<el-progress
|
||||
v-if="migrateProgressMap[row.id] != null"
|
||||
:percentage="Math.min(migrateProgressMap[row.id], 100)"
|
||||
:stroke-width="6" :show-text="false" status="warning"
|
||||
style="flex:1;min-width:50px"
|
||||
/>
|
||||
<span class="migrate-inline-pct" v-if="migrateProgressMap[row.id] != null">{{ migrateProgressMap[row.id] }}%</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-tag v-else :type="vmStatusType(row.status)" size="small">{{ vmStatusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="宿主机" width="140">
|
||||
@@ -357,14 +368,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, inject, onMounted } from 'vue'
|
||||
import { ref, reactive, computed, inject, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus, Refresh, Search, ArrowLeft, ArrowDown, WarningFilled } from '@element-plus/icons-vue'
|
||||
import {
|
||||
getRemoteHostList, getVmList, getVmDetail, getVmStatus,
|
||||
createVm, rebuildVm, startVm, stopVm, rebootVm, suspendVm,
|
||||
resumeVm, rescueVm, exitRescueVm, deleteVm, getNetworkList, getMetricsHistory
|
||||
resumeVm, rescueVm, exitRescueVm, deleteVm, getNetworkList, getMetricsHistory,
|
||||
getDataMigrateProgress
|
||||
} from '@/api/admin/kvmService'
|
||||
import { extractApiError } from '@/utils/kvmErrorUtil'
|
||||
import ImageSelectorPopup from '@/components/admin/ImageSelectorPopup.vue'
|
||||
@@ -581,9 +593,46 @@ const loadList = async () => {
|
||||
vmList.value = inner.data || inner.vms || (Array.isArray(inner) ? inner : [])
|
||||
total.value = inner.meta?.count ?? inner.all_count ?? inner.total ?? vmList.value.length
|
||||
} else { vmList.value = []; total.value = 0 }
|
||||
handleMigrateAutoRefresh()
|
||||
} catch (e) { ElMessage.error(extractApiError(e?.response?.data, '获取虚拟机列表失败')) } finally { loading.value = false }
|
||||
}
|
||||
|
||||
const migrateProgressMap = reactive({})
|
||||
let migrateAutoRefreshTimer = null
|
||||
|
||||
const fetchMigrateProgress = async () => {
|
||||
const migratingVms = vmList.value.filter(v => v.migrating && v.migrate_task_id)
|
||||
for (const vm of migratingVms) {
|
||||
try {
|
||||
const res = await getDataMigrateProgress({
|
||||
service_id: serviceId.value,
|
||||
migration_id: vm.migrate_task_id
|
||||
})
|
||||
if (res?.data?.code === 200 && res.data.data) {
|
||||
migrateProgressMap[vm.id] = res.data.data.progress ?? null
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
const handleMigrateAutoRefresh = () => {
|
||||
const hasMigrating = vmList.value.some(v => v.migrating)
|
||||
if (hasMigrating) {
|
||||
fetchMigrateProgress()
|
||||
if (!migrateAutoRefreshTimer) {
|
||||
migrateAutoRefreshTimer = setInterval(() => { loadList() }, 3000)
|
||||
}
|
||||
} else {
|
||||
stopMigrateAutoRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
const stopMigrateAutoRefresh = () => {
|
||||
if (migrateAutoRefreshTimer) { clearInterval(migrateAutoRefreshTimer); migrateAutoRefreshTimer = null }
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => stopMigrateAutoRefresh())
|
||||
|
||||
const handleSearch = () => { queryParams.page = 1; loadList() }
|
||||
|
||||
const handleAdd = () => {
|
||||
@@ -820,4 +869,7 @@ defineExpose({ loadList })
|
||||
<style scoped>
|
||||
.vm-manage-container { padding: 20px; }
|
||||
.vm-config { display: flex; gap: 4px; flex-wrap: wrap; }
|
||||
.migrate-inline-status { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
|
||||
.migrate-inline-label { color: #e6a23c; font-size: 13px; font-weight: 600; white-space: nowrap; }
|
||||
.migrate-inline-pct { color: #e6a23c; font-size: 12px; white-space: nowrap; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user