feat: 用户商品状态筛选与统计对接
- 新增 getUserGoodsCount 接口对接,列表页/虚拟机列表页增加状态筛选与统计卡片 - 已删除/已到期商品适配及相关页面更新 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -28,8 +28,24 @@
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<!-- 批量操作栏 -->
|
||||
<div class="batch-bar" v-if="selectedVms.length">
|
||||
<span class="batch-info">已选择 <strong>{{ selectedVms.length }}</strong> 台虚拟机</span>
|
||||
<el-button type="success" size="small" @click="handleBatchPower('start')" :loading="batchLoading">
|
||||
<el-icon><VideoPlay /></el-icon>批量开机
|
||||
</el-button>
|
||||
<el-button type="warning" size="small" @click="handleBatchPower('stop')" :loading="batchLoading">
|
||||
<el-icon><SwitchButton /></el-icon>批量关机
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" @click="handleBatchDelete" :loading="batchLoading">
|
||||
<el-icon><Delete /></el-icon>批量删除
|
||||
</el-button>
|
||||
<el-button size="small" @click="clearSelection">取消选择</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 虚拟机列表 -->
|
||||
<el-table :data="vmList" v-loading="loading" stripe>
|
||||
<el-table ref="vmTableRef" :data="vmList" v-loading="loading" stripe @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="45" />
|
||||
<el-table-column prop="id" label="ID" width="70" />
|
||||
<el-table-column prop="name" label="名称" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="配置" min-width="200">
|
||||
@@ -433,7 +449,7 @@
|
||||
import { ref, reactive, computed, inject, onMounted, onBeforeUnmount, nextTick } 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 { Plus, Refresh, Search, ArrowLeft, ArrowDown, WarningFilled, VideoPlay, SwitchButton, Delete } from '@element-plus/icons-vue'
|
||||
import {
|
||||
getRemoteHostList, getVmList, getVmDetail, getVmStatus,
|
||||
createVm, rebuildVm, startVm, stopVm, rebootVm, suspendVm,
|
||||
@@ -466,6 +482,7 @@ const serviceName = computed(() => injectedServiceName?.value || route.query.ser
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const batchLoading = ref(false)
|
||||
const vmList = ref([])
|
||||
const total = ref(0)
|
||||
const keyword = ref('')
|
||||
@@ -473,6 +490,12 @@ const filterStatus = ref('')
|
||||
const hostOptions = ref([])
|
||||
const queryParams = reactive({ page: 1, page_size: 10 })
|
||||
|
||||
// 批量操作
|
||||
const vmTableRef = ref(null)
|
||||
const selectedVms = ref([])
|
||||
const handleSelectionChange = (selection) => { selectedVms.value = selection }
|
||||
const clearSelection = () => { vmTableRef.value?.clearSelection() }
|
||||
|
||||
// 选择器
|
||||
const showCreateImageSelector = ref(false)
|
||||
const showRebuildImageSelector = ref(false)
|
||||
@@ -989,6 +1012,72 @@ const handleDelete = (row) => {
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 批量电源操作(开机/关机)
|
||||
const handleBatchPower = (action) => {
|
||||
const label = action === 'start' ? '开机' : '关机'
|
||||
const targets = selectedVms.value
|
||||
const skipped = targets.filter(v =>
|
||||
action === 'start' ? v.status === 'running' : (v.status === 'stopped' || v.status === 'stop')
|
||||
)
|
||||
const toOperate = targets.filter(v => !skipped.includes(v))
|
||||
|
||||
if (!toOperate.length) {
|
||||
ElMessage.warning(`所选虚拟机均已处于目标状态,无需${label}`)
|
||||
return
|
||||
}
|
||||
|
||||
const msg = skipped.length
|
||||
? `将对 ${toOperate.length} 台虚拟机执行${label}(${skipped.length} 台已跳过),是否继续?`
|
||||
: `确定要对 ${toOperate.length} 台虚拟机执行批量${label}吗?`
|
||||
|
||||
ElMessageBox.confirm(msg, `批量${label}`, {
|
||||
confirmButtonText: '确定', cancelButtonText: '取消',
|
||||
type: action === 'stop' ? 'warning' : 'info'
|
||||
}).then(async () => {
|
||||
batchLoading.value = true
|
||||
const api = action === 'start' ? startVm : stopVm
|
||||
let success = 0, fail = 0
|
||||
await Promise.allSettled(toOperate.map(async (vm) => {
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('service_id', serviceId.value)
|
||||
fd.append('vm_id', vm.id)
|
||||
const res = await api(fd)
|
||||
if (res?.data?.code === 200) success++
|
||||
else fail++
|
||||
} catch { fail++ }
|
||||
}))
|
||||
batchLoading.value = false
|
||||
clearSelection()
|
||||
ElMessage[fail ? 'warning' : 'success'](`批量${label}完成:成功 ${success},失败 ${fail}`)
|
||||
loadList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
const count = selectedVms.value.length
|
||||
ElMessageBox.confirm(
|
||||
`确定要删除选中的 ${count} 台虚拟机吗?此操作不可恢复。`,
|
||||
'批量删除',
|
||||
{ confirmButtonText: '确定删除', cancelButtonText: '取消', type: 'warning' }
|
||||
).then(async () => {
|
||||
batchLoading.value = true
|
||||
let success = 0, fail = 0
|
||||
await Promise.allSettled(selectedVms.value.map(async (vm) => {
|
||||
try {
|
||||
const res = await deleteVm({ service_id: serviceId.value, vm_id: vm.id })
|
||||
if (res?.data?.code === 200) success++
|
||||
else fail++
|
||||
} catch { fail++ }
|
||||
}))
|
||||
batchLoading.value = false
|
||||
clearSelection()
|
||||
ElMessage[fail ? 'warning' : 'success'](`批量删除完成:成功 ${success},失败 ${fail}`)
|
||||
loadList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
const goBack = () => { router.push('/virtualization/kvm-service') }
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -1007,4 +1096,19 @@ defineExpose({ loadList })
|
||||
.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; }
|
||||
.batch-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
margin-bottom: 12px;
|
||||
background: #ecf5ff;
|
||||
border: 1px solid #d9ecff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.batch-info {
|
||||
font-size: 13px;
|
||||
color: #409eff;
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user