From a9baf6bc0966792e508cb7c0f57daf779e58b9c3 Mon Sep 17 00:00:00 2001 From: shiran Date: Tue, 4 Aug 2026 08:57:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=AD=9B=E9=80=89=E9=80=89=E6=8B=A9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/UserGoodsSelectorPopup.vue | 64 +++++++++++++++++++ .../virtualization/VmMigrationRecord.vue | 32 ++++++++-- 2 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 src/components/admin/UserGoodsSelectorPopup.vue diff --git a/src/components/admin/UserGoodsSelectorPopup.vue b/src/components/admin/UserGoodsSelectorPopup.vue new file mode 100644 index 0000000..1d79c1a --- /dev/null +++ b/src/components/admin/UserGoodsSelectorPopup.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/views/virtualization/VmMigrationRecord.vue b/src/views/virtualization/VmMigrationRecord.vue index 82ae8d7..2341424 100644 --- a/src/views/virtualization/VmMigrationRecord.vue +++ b/src/views/virtualization/VmMigrationRecord.vue @@ -10,6 +10,11 @@ + + + + + @@ -26,8 +31,12 @@ - - + +
选择
+
+ +
选择
+
@@ -87,6 +96,8 @@ + + @@ -94,9 +105,11 @@ import { computed, inject, onMounted, reactive, ref } from 'vue' import { useRoute, useRouter } from 'vue-router' import { ElMessage } from 'element-plus' -import { Refresh, Search } from '@element-plus/icons-vue' +import { Close, Refresh, Search } from '@element-plus/icons-vue' import { getVmMigrationRecordList, getVmMigrationRecordDetail, getVmMigrationRecordChain } from '@/api/admin/kvmService' import { extractApiError } from '@/utils/kvmErrorUtil' +import UserListSelector from '@/components/admin/UserListSelector.vue' +import UserGoodsSelectorPopup from '@/components/admin/UserGoodsSelectorPopup.vue' const router = useRouter() const route = useRoute() @@ -105,7 +118,12 @@ const injectedServiceId = inject('serviceId', null) const serviceId = computed(() => injectedServiceId?.value || Number(route.query.service_id) || 0) const serviceDirection = ref('source') const loading = ref(false), records = ref([]), total = ref(0), page = ref(1), count = ref(20) -const filters = reactive({ migration_type: '', status: '', chain_id: '', task_id: '', user_id: null, user_goods_id: null, started_range: [] }) +const filters = reactive({ keyword: '', migration_type: '', status: '', chain_id: '', task_id: '', user_id: null, user_goods_id: null, _userName: '', _userGoodsName: '', started_range: [] }) +const showUserSelector = ref(false), showUserGoodsSelector = ref(false) +const clearUserGoods = () => { filters.user_goods_id = null; filters._userGoodsName = '' } +const clearUser = () => { filters.user_id = null; filters._userName = ''; clearUserGoods() } +const handleUserSelected = user => { const id = user.user_id || user.id; if (Number(filters.user_id) !== Number(id)) clearUserGoods(); filters.user_id = id; filters._userName = user.user_name || user.name || '' } +const handleUserGoodsSelected = goods => { filters.user_goods_id = goods.id; filters._userGoodsName = goods.good?.name || goods.good_name || goods.name || '' ; if (!filters.user_id && (goods.user_id || goods.userId)) filters.user_id = goods.user_id || goods.userId } const statusOptions = ['pending','exporting','importing','running','success','failed','aborted','warning'].map(value => ({ value, label: ({ pending:'等待中',exporting:'导出中',importing:'导入中',running:'执行中',success:'成功',failed:'失败',aborted:'已中断',warning:'警告' })[value] })) const statusLabel = s => statusOptions.find(x => x.value === s)?.label || s || '-' const statusType = s => ({ success:'success', failed:'danger', aborted:'info', warning:'warning', exporting:'warning', importing:'primary', running:'primary', pending:'info' })[s] || 'info' @@ -113,10 +131,10 @@ const typeLabel = t => ({ host_migrate:'宿主机迁移', data_migrate:'跨服 const formatTime = value => { if (!value) return '-'; const d = new Date(value); return Number.isNaN(d.getTime()) ? String(value) : d.toLocaleString('zh-CN', { hour12:false }) } const shortId = value => !value ? '-' : (value.length > 18 ? `${value.slice(0,8)}…${value.slice(-6)}` : value) const durationText = row => { if (!row.started_at) return '-'; const end = row.completed_at ? new Date(row.completed_at) : new Date(); const sec = Math.max(0, Math.floor((end-new Date(row.started_at))/1000)); return sec >= 3600 ? `${Math.floor(sec/3600)}时${Math.floor(sec%3600/60)}分` : sec >= 60 ? `${Math.floor(sec/60)}分${sec%60}秒` : `${sec}秒` } -const buildParams = () => { const p = { page:page.value, count:count.value }; ['migration_type','status','chain_id','task_id','user_id','user_goods_id'].forEach(k => { if (filters[k] !== '' && filters[k] != null) p[k]=filters[k] }); if (embedded && serviceId.value) p[`${serviceDirection.value}_service_id`] = serviceId.value; if (filters.started_range?.length===2) [p.started_at_start,p.started_at_end]=filters.started_range; return p } +const buildParams = () => { const p = { page:page.value, count:count.value }; ['keyword','migration_type','status','chain_id','task_id','user_id','user_goods_id'].forEach(k => { if (filters[k] !== '' && filters[k] != null) p[k]=filters[k] }); if (embedded && serviceId.value) p[`${serviceDirection.value}_service_id`] = serviceId.value; if (filters.started_range?.length===2) [p.started_at_start,p.started_at_end]=filters.started_range; return p } const loadList = async () => { loading.value=true; try { const res=await getVmMigrationRecordList(buildParams()); if(res?.data?.code!==200) throw new Error(extractApiError(res?.data,'加载迁移记录失败')); const d=res.data.data||{}; records.value=d.data||[]; total.value=d.all_count||0 } catch(e){ ElMessage.error(e.message||extractApiError(e?.response?.data,'加载迁移记录失败')) } finally { loading.value=false } } const search = () => { page.value=1; loadList() }, sizeChange = () => { page.value=1; loadList() } -const resetFilters = () => { Object.assign(filters,{ migration_type:'',status:'',chain_id:'',task_id:'',user_id:null,user_goods_id:null,started_range:[] }); search() } +const resetFilters = () => { Object.assign(filters,{ keyword:'',migration_type:'',status:'',chain_id:'',task_id:'',user_id:null,user_goods_id:null,_userName:'',_userGoodsName:'',started_range:[] }); search() } 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) } } @@ -136,5 +154,5 @@ onMounted(async () => {