fix: 虚拟机网络管理改为解绑逻辑,不再删除底层网络
将 user-vm/UserVmDetail 和 virtualization/VmDetail 中的网络删除操作改为通过 updateVm 接口解绑,与绑定网络逻辑对称,避免误删底层物理网络。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -344,7 +344,7 @@
|
|||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button v-if="!row.is_primary" link type="warning" size="small" @click="handleSetVmNetworkPrimary(row)">设为主IP</el-button>
|
<el-button v-if="!row.is_primary" link type="warning" size="small" @click="handleSetVmNetworkPrimary(row)">设为主IP</el-button>
|
||||||
<el-button link type="danger" size="small" :loading="deletingNetworkId === row.id" @click="handleDeleteVmNetwork(row)">
|
<el-button link type="danger" size="small" :loading="deletingNetworkId === row.id" @click="handleDeleteVmNetwork(row)">
|
||||||
<el-icon :size="14"><Delete /></el-icon>删除
|
<el-icon :size="14"><Delete /></el-icon>解绑
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -1807,21 +1807,27 @@ const resolveNetworkServiceHost = async (row) => {
|
|||||||
}
|
}
|
||||||
const handleDeleteVmNetwork = (row) => {
|
const handleDeleteVmNetwork = (row) => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
`将删除底层网络「${row.name}」(ID:${row.id}),该操作会影响所有绑定该网络的虚拟机,是否继续?`,
|
`确定从该虚拟机解绑网络「${row.name}」(ID:${row.id}) 吗?`,
|
||||||
'删除网络',
|
'解绑网络',
|
||||||
{ confirmButtonText: '确定删除', cancelButtonText: '取消', type: 'warning' }
|
{ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
||||||
).then(async () => {
|
).then(async () => {
|
||||||
deletingNetworkId.value = row.id
|
deletingNetworkId.value = row.id
|
||||||
try {
|
try {
|
||||||
const { serviceId, hostId } = await resolveNetworkServiceHost(row)
|
const payload = { user_goods_id: userGoodsId.value }
|
||||||
if (!serviceId) { ElMessage.error('无法获取该网络所属服务ID,删除失败'); return }
|
const remainingNetworks = vmNetworks.value.filter(n => n.id !== row.id)
|
||||||
const params = { service_id: serviceId, network_id: row.id }
|
const bridgeIds = remainingNetworks.filter(n => n.type === 'bridge').map(n => n.id)
|
||||||
if (hostId) params.host_id = hostId
|
const natNet = remainingNetworks.find(n => n.type === 'nat')
|
||||||
const res = await deletePointNetwork(params)
|
payload.network_ids = bridgeIds
|
||||||
if (res?.data?.code === 200) { ElMessage.success('删除成功'); loadDetail() }
|
if (natNet) payload.internet_network_id = natNet.id
|
||||||
else ElMessage.error(extractApiError(res?.data, '删除失败'))
|
if (vm.value?.rx_bandwidth) payload.rx_bandwidth = vm.value.rx_bandwidth
|
||||||
|
if (vm.value?.tx_bandwidth) payload.tx_bandwidth = vm.value.tx_bandwidth
|
||||||
|
if (vm.value?.ssh_port) payload.ssh_port = vm.value.ssh_port
|
||||||
|
if (inPortGroup.value?.id) payload.port_group_id = inPortGroup.value.id
|
||||||
|
const res = await updateUserVm(payload)
|
||||||
|
if (res?.data?.code === 200) { ElMessage.success('解绑网络成功'); loadDetail() }
|
||||||
|
else ElMessage.error(extractApiError(res?.data, '解绑网络失败'))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ElMessage.error(extractApiError(e?.response?.data, '删除失败'))
|
ElMessage.error(extractApiError(e?.response?.data, '解绑网络失败'))
|
||||||
} finally {
|
} finally {
|
||||||
deletingNetworkId.value = 0
|
deletingNetworkId.value = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,7 +257,11 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="已购商品" name="5">
|
<el-tab-pane label="已购商品" name="5">
|
||||||
<el-table :data="userGoodsList" v-loading="goodsListLoading" stripe style="width: 100%" table-layout="auto">
|
<el-table :data="userGoodsList" v-loading="goodsListLoading" stripe style="width: 100%" table-layout="auto">
|
||||||
<el-table-column prop="id" label="ID" width="70" />
|
<el-table-column prop="id" label="ID" width="70">
|
||||||
|
<template #default="{row}">
|
||||||
|
<el-link type="primary" :underline="false" @click="router.push(`/user-goods/detail/${row.id}`)">{{ row.id }}</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="商品名称" min-width="120" show-overflow-tooltip>
|
<el-table-column label="商品名称" min-width="120" show-overflow-tooltip>
|
||||||
<template #default="{row}">{{ row.good?.name || '-' }}</template>
|
<template #default="{row}">{{ row.good?.name || '-' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -280,6 +284,11 @@
|
|||||||
<el-table-column label="购买时间" min-width="140">
|
<el-table-column label="购买时间" min-width="140">
|
||||||
<template #default="{row}">{{ formatDate(row.CreatedAt) }}</template>
|
<template #default="{row}">{{ formatDate(row.CreatedAt) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="80" fixed="right">
|
||||||
|
<template #default="{row}">
|
||||||
|
<el-button type="primary" link size="small" @click="router.push(`/user-goods/detail/${row.id}`)">详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="pagination-wrapper" v-if="goodsListTotal > 0">
|
<div class="pagination-wrapper" v-if="goodsListTotal > 0">
|
||||||
<el-pagination
|
<el-pagination
|
||||||
|
|||||||
@@ -302,7 +302,7 @@
|
|||||||
<el-button link type="primary" size="small" @click="handleNetDetail(row)">详情</el-button>
|
<el-button link type="primary" size="small" @click="handleNetDetail(row)">详情</el-button>
|
||||||
<el-button link type="primary" size="small" @click="handleNetEdit(row)">编辑</el-button>
|
<el-button link type="primary" size="small" @click="handleNetEdit(row)">编辑</el-button>
|
||||||
<el-button v-if="!row.is_primary" link type="warning" size="small" @click="handleSetPrimary(row)">设为主IP</el-button>
|
<el-button v-if="!row.is_primary" link type="warning" size="small" @click="handleSetPrimary(row)">设为主IP</el-button>
|
||||||
<el-button link type="danger" size="small" @click="handleNetDelete(row)">删除</el-button>
|
<el-button link type="danger" size="small" @click="handleNetDelete(row)">解绑</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -2962,13 +2962,27 @@ const submitNetForm = () => {
|
|||||||
}
|
}
|
||||||
const handleNetDetail = (row) => { netDetailData.value = row; netDetailVisible.value = true }
|
const handleNetDetail = (row) => { netDetailData.value = row; netDetailVisible.value = true }
|
||||||
const handleNetDelete = (row) => {
|
const handleNetDelete = (row) => {
|
||||||
ElMessageBox.confirm(`确定要删除网络「${row.name}」(ID: ${row.id}) 吗?`, '删除网络', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
|
ElMessageBox.confirm(`确定从该虚拟机解绑网络「${row.name}」(ID: ${row.id}) 吗?`, '解绑网络', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
|
actionLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await deleteNetwork({ service_id: serviceId.value, network_id: row.id, host_id: row.host_id })
|
const fd = new FormData()
|
||||||
if (res?.data?.code === 200) { ElMessage.success('删除成功'); loadDetail() }
|
fd.append('service_id', serviceId.value)
|
||||||
else ElMessage.error(extractApiError(res?.data, '删除失败'))
|
fd.append('vm_id', vmId.value)
|
||||||
} catch (e) { ElMessage.error(extractApiError(e?.response?.data, '删除失败')) }
|
const remainingNetworks = vmNetworks.value.filter(n => n.id !== row.id)
|
||||||
|
const bridgeIds = remainingNetworks.filter(n => n.type === 'bridge').map(n => n.id)
|
||||||
|
const natNet = remainingNetworks.find(n => n.type === 'nat')
|
||||||
|
bridgeIds.forEach(id => fd.append('network_ids', id))
|
||||||
|
if (natNet) fd.append('internet_network_id', natNet.id)
|
||||||
|
if (detail.value?.rx_bandwidth) fd.append('rx_bandwidth', detail.value.rx_bandwidth)
|
||||||
|
if (detail.value?.tx_bandwidth) fd.append('tx_bandwidth', detail.value.tx_bandwidth)
|
||||||
|
if (detail.value?.ssh_port) fd.append('ssh_port', detail.value.ssh_port)
|
||||||
|
if (vmPortGroup.value?.id) fd.append('port_group_id', vmPortGroup.value.id)
|
||||||
|
const res = await updateVm(fd)
|
||||||
|
if (res?.data?.code === 200) { ElMessage.success('解绑网络成功'); loadDetail() }
|
||||||
|
else ElMessage.error(extractApiError(res?.data, '解绑网络失败'))
|
||||||
|
} catch (e) { ElMessage.error(extractApiError(e?.response?.data, '解绑网络失败')) }
|
||||||
|
finally { actionLoading.value = false }
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user