diff --git a/src/views/virtualization/VmDetail.vue b/src/views/virtualization/VmDetail.vue
index 908d009..5764ac7 100644
--- a/src/views/virtualization/VmDetail.vue
+++ b/src/views/virtualization/VmDetail.vue
@@ -2048,15 +2048,17 @@ const loadDetail = async () => {
const handleDetailMigrateState = () => {
const vm = detail.value
if (!vm) return
- if (vm.migrating && vm.migrate_task_id) {
- dataMigrateTaskId.value = vm.migrate_task_id
- if (!dataMigrationId.value) dataMigrationId.value = vm.migrate_task_id
- if (!migratePollingTimer) {
- loadDataMigrateProgress()
- startMigratePolling()
+ if (vm.migrating === true) {
+ if (vm.migrate_task_id) {
+ dataMigrateTaskId.value = vm.migrate_task_id
+ if (!dataMigrationId.value) dataMigrationId.value = vm.migrate_task_id
+ if (!migratePollingTimer) {
+ loadDataMigrateProgress()
+ startMigratePolling()
+ }
}
startDetailAutoRefresh()
- } else if (!vm.migrating) {
+ } else {
stopDetailAutoRefresh()
if (migratePollingTimer) {
stopMigratePolling()
@@ -2905,11 +2907,7 @@ const loadDataMigrateProgress = async () => {
const currentMigrateStage = computed(() => dataMigrateProgressData.value?.status || dataMigrateProgressData.value?.stage || '')
-const isMigrating = computed(() => {
- if (detail.value?.migrating) return true
- const stage = currentMigrateStage.value
- return stage && !MIGRATE_DONE_STAGES.includes(stage)
-})
+const isMigrating = computed(() => detail.value?.migrating === true)
const migrateStageLabel = (stage) => ({
exporting: '导出中', importing: '导入中', transferring: '传输中',
diff --git a/src/views/virtualization/VmMigrationRecord.vue b/src/views/virtualization/VmMigrationRecord.vue
index 2341424..742bf53 100644
--- a/src/views/virtualization/VmMigrationRecord.vue
+++ b/src/views/virtualization/VmMigrationRecord.vue
@@ -91,7 +91,27 @@
链路 ID:{{ chainData.chain_id || '-' }}
- #{{ item.id }} · 第 {{ item.chain_seq || 1 }} 步 · {{ typeLabel(item.migration_type) }}{{ statusLabel(item.status) }}
服务 {{ item.source_service_id || '-' }} / VM {{ item.source_vm_id || '-' }} → 服务 {{ item.target_service_id || '-' }} / VM {{ item.target_vm_id || '-' }}
查看详情
+
+ #{{ item.id }} · 第 {{ item.chain_seq || 1 }} 步 · {{ typeLabel(item.migration_type) }}{{ statusLabel(item.status) }}
+
+
+ 源虚拟机
+ {{ chainVmName(item, 'source') || `VM #${item.source_vm_id}` }}
+ {{ chainVmName(item, 'source') || '-' }}
+ 服务 #{{ item.source_service_id || '-' }} · VM #{{ item.source_vm_id || '-' }}
+ IP:{{ chainVmIps(item, 'source') || '-' }}
+
+
→
+
+ 目标虚拟机
+ {{ chainVmName(item, 'target') || `VM #${item.target_vm_id}` }}
+ {{ chainVmName(item, 'target') || '-' }}
+ 服务 #{{ item.target_service_id || '-' }} · VM #{{ item.target_vm_id || '-' }}
+ IP:{{ chainVmIps(item, 'target') || '-' }}
+
+
+ 查看详情
+
@@ -138,6 +158,20 @@ const resetFilters = () => { Object.assign(filters,{ keyword:'',migration_type:'
const detailVisible=ref(false), detailLoading=ref(false), currentRecord=ref(null)
const snapshotFields=[{key:'request_snapshot',label:'请求参数'},{key:'before_snapshot',label:'迁移前快照'},{key:'after_snapshot',label:'迁移后快照'},{key:'manifest_snapshot',label:'迁移清单'},{key:'warning_snapshot',label:'警告'}]
const prettySnapshot = value => { if(!value) return '暂无数据'; try { return JSON.stringify(typeof value==='string'?JSON.parse(value):value,null,2) } catch { return String(value) } }
+const parseSnapshot = value => { if (!value) return {}; if (typeof value === 'object') return value; try { return JSON.parse(value) } catch { return {} } }
+const chainVmSnapshot = (record, side) => parseSnapshot(side === 'source' ? record.before_snapshot : record.after_snapshot)
+const chainVmName = (record, side) => {
+ const snapshot = chainVmSnapshot(record, side)
+ return record[`${side}_vm_name`] || snapshot.vm_detail?.data?.name || snapshot.vm_summary?.vm_name || ''
+}
+const chainVmIps = (record, side) => {
+ const snapshot = chainVmSnapshot(record, side)
+ const directIps = snapshot.vm_detail?.data?.ips
+ const values = directIps
+ ? String(directIps).split(',')
+ : [...(snapshot.ip_addresses?.ipv4 || []), ...(snapshot.ip_addresses?.ipv6 || [])]
+ return [...new Set(values.map(ip => String(ip).trim()).filter(Boolean))].join(', ')
+}
const openDetail = async row => { detailVisible.value=true; currentRecord.value=row; detailLoading.value=true; try { const res=await getVmMigrationRecordDetail({id:row.id}); if(res?.data?.code!==200) throw new Error(extractApiError(res?.data,'加载详情失败')); currentRecord.value=res.data.data?.record||res.data.data||row } catch(e){ ElMessage.error(e.message||extractApiError(e?.response?.data,'加载详情失败')) } finally { detailLoading.value=false } }
const chainVisible=ref(false), chainLoading=ref(false), chainData=reactive({chain_id:'',records:[]})
const openChain = async row => { chainVisible.value=true; chainLoading.value=true; Object.assign(chainData,{chain_id:row.chain_id||'',records:[]}); try { const params=row.chain_id?{chain_id:row.chain_id}:{id:row.id}; const res=await getVmMigrationRecordChain(params); if(res?.data?.code!==200) throw new Error(extractApiError(res?.data,'加载迁移链失败')); Object.assign(chainData,res.data.data||{}) } catch(e){ ElMessage.error(e.message||extractApiError(e?.response?.data,'加载迁移链失败')) } finally { chainLoading.value=false } }
@@ -154,5 +188,5 @@ onMounted(async () => {