fix: 修改内存的基础单位为kb
Build and Deploy Vue3 / build (push) Successful in 2m38s
Build and Deploy Vue3 / deploy (push) Successful in 1m3s

This commit is contained in:
2026-03-21 15:25:38 +08:00
parent cf19956b88
commit 9edb59d16e
14 changed files with 819 additions and 155 deletions
+58 -31
View File
@@ -37,7 +37,7 @@
<div class="vm-config">
<el-tag size="small" type="info" v-if="row.vcpu">{{ row.vcpu }}</el-tag>
<el-tag size="small" type="info" v-if="row.memory">{{ formatMemory(row.memory) }}</el-tag>
<el-tag size="small" type="info" v-if="row.system_size">{{ row.system_size }}MB盘</el-tag>
<el-tag size="small" type="info" v-if="row.system_size">{{ row.system_size }}GB盘</el-tag>
</div>
</template>
</el-table-column>
@@ -58,10 +58,26 @@
<template #default="{ row }">{{ getHostLabel(row.host_id) }}</template>
</el-table-column>
<el-table-column prop="user_id" label="用户" width="80" />
<el-table-column label="操作" width="120" fixed="right">
<el-table-column label="操作" width="280" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="handleGoDetail(row)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
<el-button link type="primary" size="small" @click="handleGoDetail(row)">详情</el-button>
<el-button link type="success" size="small" @click="handlePower(row, 'start')" :disabled="row.status === 'running'">启动</el-button>
<el-button link type="warning" size="small" @click="handlePower(row, 'stop')" :disabled="row.status === 'stopped' || row.status === 'stop'">关机</el-button>
<el-dropdown trigger="click" @command="cmd => handleMoreAction(row, cmd)" style="margin-left: 4px">
<el-button link type="info" size="small">更多<el-icon class="el-icon--right"><ArrowDown /></el-icon></el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="reboot">重启</el-dropdown-item>
<el-dropdown-item command="suspend">暂停</el-dropdown-item>
<el-dropdown-item command="resume" v-if="row.status === 'paused'">恢复</el-dropdown-item>
<el-dropdown-item command="rebuild" divided>重建</el-dropdown-item>
<el-dropdown-item command="rescue">救援模式</el-dropdown-item>
<el-dropdown-item command="exit_rescue">退出救援</el-dropdown-item>
<el-dropdown-item command="detail" divided>查看详情</el-dropdown-item>
<el-dropdown-item command="delete" divided style="color: #f56c6c">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-table-column>
</el-table>
@@ -108,7 +124,7 @@
<div class="bind-selector-row">
<el-input :model-value="createForm.host_group_id ? `${createForm._groupName || ''} (ID: ${createForm.host_group_id})` : '未选择'" disabled style="flex: 1" />
<el-button type="primary" @click="showHostGroupSelector = true" style="margin-left: 8px">选择</el-button>
<el-button v-if="createForm.host_group_id" @click="createForm.host_group_id = 0; createForm._groupName = ''" style="margin-left: 4px">清除</el-button>
<el-button v-if="createForm.host_group_id" @click="createForm.host_group_id = null; createForm._groupName = ''" style="margin-left: 4px">清除</el-button>
</div>
</el-form-item>
@@ -119,20 +135,20 @@
<el-select v-model="memoryUnit" class="resource-unit-select">
<el-option v-for="u in memoryUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
</el-select>
<el-input-number v-model="memoryDisplay" :min="1" controls-position="right" class="resource-input" />
<el-input-number v-model="memoryDisplay" :min="0" controls-position="right" class="resource-input" />
</div>
<div class="resource-item">
<span class="resource-label">* 系统盘</span>
<el-select v-model="diskUnit" class="resource-unit-select">
<el-option v-for="u in diskUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
</el-select>
<el-input-number v-model="diskDisplay" :min="1" controls-position="right" class="resource-input" />
<el-input-number v-model="diskDisplay" :min="0" controls-position="right" class="resource-input" />
</div>
</div>
<div class="resource-row">
<div class="resource-item">
<span class="resource-label">* CPU(核)</span>
<el-input-number v-model="createForm.vcpu" :min="1" controls-position="right" class="resource-input" />
<el-input-number v-model="createForm.vcpu" :min="0" controls-position="right" class="resource-input" />
</div>
<div class="resource-item">
<span class="resource-label">下行带宽(Mbps)</span>
@@ -155,10 +171,10 @@
<el-input-number v-model="createForm.ip_num" :min="1" controls-position="right" style="width: 100%" />
</el-form-item>
<el-form-item label="网络IP列表" v-if="ipMode === 'ids'">
<el-select v-model="createForm.network_ids" multiple filterable placeholder="选择网络IP" style="width: 100%">
<el-option v-for="n in networkOptions" :key="n.id" :label="`${n.name || ''} - ${n.address || n.ip || ''} (ID: ${n.id})`" :value="n.id" />
<el-select v-model="createForm.network_ids" multiple filterable placeholder="选择可用网络IP" style="width: 100%">
<el-option v-for="n in networkOptions" :key="n.id" :label="`${n.name || ''} - ${n.address || n.ip || ''}`" :value="n.id" />
</el-select>
<div class="form-tip" v-if="!networkOptions.length">请先选择宿主机以加载可用网络</div>
<div class="form-tip" v-if="!networkOptions.length">请先选择宿主机以加载可用网络(仅显示未使用的网络)</div>
</el-form-item>
</el-form>
<template #footer>
@@ -193,7 +209,7 @@
<el-descriptions-item label="名称">{{ currentDetail.name }}</el-descriptions-item>
<el-descriptions-item label="CPU">{{ currentDetail.vcpu }} 核</el-descriptions-item>
<el-descriptions-item label="内存">{{ formatMemory(currentDetail.memory) }}</el-descriptions-item>
<el-descriptions-item label="系统盘">{{ currentDetail.system_size }} MB</el-descriptions-item>
<el-descriptions-item label="系统盘">{{ currentDetail.system_size }} GB</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="vmStatusType(currentDetail.status)" size="small">{{ vmStatusLabel(currentDetail.status) }}</el-tag>
</el-descriptions-item>
@@ -304,8 +320,9 @@ const router = useRouter()
const embedded = inject('embedded', false)
const injectedServiceId = inject('serviceId', null)
const injectedServiceName = inject('serviceName', null)
const injectedHostId = inject('hostId', null)
const serviceId = computed(() => injectedServiceId?.value || parseInt(route.query.service_id) || 0)
const hostId = computed(() => parseInt(route.query.host_id) || 0)
const hostId = computed(() => injectedHostId?.value || parseInt(route.query.host_id) || 0)
const serviceName = computed(() => injectedServiceName?.value || route.query.service_name || '')
const loading = ref(false)
@@ -329,35 +346,37 @@ const hostMode = ref('host')
const ipMode = ref('num')
const networkOptions = ref([])
// 内存单位: API传输单位为 KB
// 内存单位: API传输单位为 bytes
const memoryUnitOptions = [
{ label: 'KB', factor: 1 },
{ label: 'MB', factor: 1024 },
{ label: 'GB', factor: 1048576 }
]
const memoryUnit = ref('KB')
const memoryUnit = ref('GB')
const getMemFactor = () => memoryUnitOptions.find(u => u.label === memoryUnit.value)?.factor || 1
const memoryDisplay = computed({
get: () => Math.round(createForm.memory / getMemFactor()),
set: (v) => { createForm.memory = Math.round(v * getMemFactor()) }
})
// 系统盘单位: API传输单位为 MB
// 系统盘: API传输单位为 GB
const diskUnitOptions = [
{ label: 'MB', factor: 1 },
{ label: 'GB', factor: 1024 }
{ label: 'GB', factor: 1 },
{ label: 'TB', factor: 1024 }
]
const diskUnit = ref('GB')
const getDiskFactor = () => diskUnitOptions.find(u => u.label === diskUnit.value)?.factor || 1
const diskDisplay = computed({
get: () => Math.round(createForm.system_size / getDiskFactor()),
get: () => {
const f = getDiskFactor()
return f === 1 ? createForm.system_size : +(createForm.system_size / f).toFixed(2)
},
set: (v) => { createForm.system_size = Math.round(v * getDiskFactor()) }
})
const loadNetworkOptions = async (hostId) => {
if (!hostId) return
try {
const res = await getNetworkList({ service_id: serviceId.value, host_id: hostId, page: 1, page_size: 200 })
const res = await getNetworkList({ service_id: serviceId.value, host_id: hostId, used: false, page: 1, page_size: 200 })
const body = res?.data
if (body?.code === 200 && body?.data) {
const inner = body.data
@@ -377,7 +396,7 @@ const loadHostOptions = async () => {
const body = res?.data
if (body?.code === 200 && body?.data) {
const inner = body.data
hostOptions.value = inner.hosts || inner.data || (Array.isArray(inner) ? inner : [])
hostOptions.value = Array.isArray(inner) ? inner : (inner.hosts || inner.list || inner.data || [])
}
} catch (e) { console.error('加载宿主机列表失败:', e) }
}
@@ -402,16 +421,16 @@ const rebuildImageName = ref('')
const vmMetricsData = ref(null)
const createForm = reactive({
name: '', host_id: 0, image_id: 0, vcpu: 1, memory: 1048576,
system_size: 10240, rx_bandwidth: 0, tx_bandwidth: 0,
host_group_id: 0, user_id: 0, ip_num: 0, network_ids: [],
name: '', host_id: null, image_id: 0, vcpu: 0, memory: 0,
system_size: 0, rx_bandwidth: 0, tx_bandwidth: 0,
host_group_id: null, user_id: 0, ip_num: 0, network_ids: [],
_imageName: '', _groupName: '', _userName: ''
})
const createRules = {
image_id: [{ required: true, message: '请选择镜像', trigger: 'blur', type: 'number', min: 1 }],
vcpu: [{ required: true, message: '请输入CPU核数', trigger: 'blur' }],
memory: [{ required: true, message: '请输入内存(KB)', trigger: 'blur' }],
memory: [{ required: true, message: '请输入内存', trigger: 'blur' }],
system_size: [{ required: true, message: '请输入系统盘大小', trigger: 'blur' }],
user_id: [{ required: true, message: '请选择用户', trigger: 'change', type: 'number', min: 1 }]
}
@@ -430,6 +449,8 @@ const vmStatusLabel = (s) => ({
const formatMemory = (kb) => {
if (!kb) return '-'
kb = Number(kb)
if (kb >= 1073741824) return (kb / 1073741824).toFixed(1) + ' TB'
if (kb >= 1048576) return (kb / 1048576).toFixed(1) + ' GB'
if (kb >= 1024) return (kb / 1024).toFixed(0) + ' MB'
return kb + ' KB'
@@ -484,14 +505,16 @@ const handleSearch = () => { queryParams.page = 1; loadList() }
const handleAdd = () => {
Object.assign(createForm, {
name: '', host_id: hostId.value || 0, image_id: 0,
vcpu: 1, memory: 1048576, system_size: 10240,
rx_bandwidth: 0, tx_bandwidth: 0, host_group_id: 0, user_id: 0, ip_num: 0, network_ids: [],
name: '', host_id: null, image_id: 0,
vcpu: 0, memory: 0, system_size: 0,
rx_bandwidth: 0, tx_bandwidth: 0, host_group_id: null, user_id: 0, ip_num: 0, network_ids: [],
_imageName: '', _groupName: '', _userName: ''
})
memoryUnit.value = 'GB'
diskUnit.value = 'GB'
hostMode.value = 'host'
ipMode.value = 'num'
if (createForm.host_id) loadNetworkOptions(createForm.host_id)
networkOptions.value = []
createDialogVisible.value = true
}
@@ -552,9 +575,13 @@ const handlePower = (row, action) => {
}
const handleMoreAction = (row, command) => {
const powerActions = ['reboot', 'suspend', 'resume']
if (powerActions.includes(command)) { handlePower(row, command); return }
if (command === 'rebuild') handleRebuild(row)
else if (command === 'rescue') handleRescue(row)
else if (command === 'exit_rescue') handleExitRescue(row)
else if (command === 'detail') handleViewDetail(row)
else if (command === 'delete') handleDelete(row)
}
const handleRebuild = (row) => {
@@ -639,7 +666,7 @@ const fetchVmMetrics = async (vm) => {
}
const handleGoDetail = (row) => {
router.push({ path: '/virtualization/vm-detail', query: { service_id: serviceId.value, service_name: serviceName.value, id: row.id } })
router.push({ path: '/virtualization/vm-detail', query: { service_id: serviceId.value, service_name: serviceName.value, vm_id: row.id } })
}
const handleDelete = (row) => {