fix(admin/host): 硬盘IO限制带宽字段改为动态单位选择(B/s~GB/s), 带宽与IOPS分组渲染修复标签溢出 -- 缘由: 带宽字段直接展示bytes原始值且标签过长导致换行错乱 -- 预期: 带宽字段参照内存/磁盘方式通过下拉选择单位(默认MB/s), IOPS独立分组, 标签简化为读取/写入/突发读取/突发写入
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -137,10 +137,16 @@
|
|||||||
<el-icon class="section-arrow" :class="{ expanded: showDetailDiskIo }"><ArrowRight /></el-icon>
|
<el-icon class="section-arrow" :class="{ expanded: showDetailDiskIo }"><ArrowRight /></el-icon>
|
||||||
</h3>
|
</h3>
|
||||||
<div v-show="showDetailDiskIo" class="config-grid">
|
<div v-show="showDetailDiskIo" class="config-grid">
|
||||||
<div class="config-row" v-for="i in Math.ceil(diskIoFields.length / 3)" :key="i">
|
<div class="config-row">
|
||||||
<div class="config-cell" v-for="f in diskIoFields.slice((i - 1) * 3, i * 3)" :key="f.key">
|
<div class="config-cell" v-for="f in diskIoBwFields" :key="f.key">
|
||||||
<span class="config-label">{{ f.label }}</span>
|
<span class="config-label">{{ f.label }}带宽</span>
|
||||||
<span class="config-value mono-text">{{ formatDiskIoVal(detail[f.key], f) }}</span>
|
<span class="config-value mono-text">{{ formatDiskIoVal(detail[f.key], true) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="config-row">
|
||||||
|
<div class="config-cell" v-for="f in diskIoIopsFields" :key="f.key">
|
||||||
|
<span class="config-label">{{ f.label }} IOPS</span>
|
||||||
|
<span class="config-value mono-text">{{ formatDiskIoVal(detail[f.key], false) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -428,12 +434,25 @@
|
|||||||
<el-icon class="section-arrow" :class="{ expanded: showDiskIoSection }"><ArrowRight /></el-icon>
|
<el-icon class="section-arrow" :class="{ expanded: showDiskIoSection }"><ArrowRight /></el-icon>
|
||||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showDiskIoSection" class="tk-resource-grid">
|
<div v-show="showDiskIoSection">
|
||||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label">
|
<div class="io-sub-title">
|
||||||
<el-input-number v-model="formData[f.key]" :min="0" controls-position="right" />
|
带宽限制
|
||||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
<el-select v-model="ioBwUnit" class="tk-unit-select" style="width: 90px; margin-left: 8px">
|
||||||
|
<el-option v-for="u in ioBwUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoBwFields" :key="f.key" :label="f.label">
|
||||||
|
<el-input-number :model-value="+(formData[f.key] / getIoBwFactor()).toFixed(2)" @update:model-value="v => formData[f.key] = Math.round((v || 0) * getIoBwFactor())" :min="0" controls-position="right" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="io-sub-title">IOPS 限制</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoIopsFields" :key="f.key" :label="f.label">
|
||||||
|
<el-input-number v-model="formData[f.key]" :min="0" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tk-section">
|
<div class="tk-section">
|
||||||
<div class="tk-section-title">其他配置</div>
|
<div class="tk-section-title">其他配置</div>
|
||||||
@@ -508,12 +527,25 @@
|
|||||||
<el-icon class="section-arrow" :class="{ expanded: showTokenDiskIo }"><ArrowRight /></el-icon>
|
<el-icon class="section-arrow" :class="{ expanded: showTokenDiskIo }"><ArrowRight /></el-icon>
|
||||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showTokenDiskIo" class="tk-resource-grid">
|
<div v-show="showTokenDiskIo">
|
||||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label" class="tk-res-item">
|
<div class="io-sub-title">
|
||||||
<el-input-number v-model="tokenForm[f.key]" :min="0" controls-position="right" />
|
带宽限制
|
||||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
<el-select v-model="tokenIoBwUnit" class="tk-unit-select" style="width: 90px; margin-left: 8px">
|
||||||
|
<el-option v-for="u in ioBwUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoBwFields" :key="f.key" :label="f.label" class="tk-res-item">
|
||||||
|
<el-input-number :model-value="+(tokenForm[f.key] / getTokenIoBwFactor()).toFixed(2)" @update:model-value="v => tokenForm[f.key] = Math.round((v || 0) * getTokenIoBwFactor())" :min="0" controls-position="right" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="io-sub-title">IOPS 限制</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoIopsFields" :key="f.key" :label="f.label" class="tk-res-item">
|
||||||
|
<el-input-number v-model="tokenForm[f.key]" :min="0" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tk-section">
|
<div class="tk-section">
|
||||||
<div class="tk-section-title">令牌有效期</div>
|
<div class="tk-section-title">令牌有效期</div>
|
||||||
@@ -676,25 +708,42 @@ const diskIoDefaults = {
|
|||||||
read_bytes_sec_max: 314572800, write_bytes_sec_max: 314572800,
|
read_bytes_sec_max: 314572800, write_bytes_sec_max: 314572800,
|
||||||
read_iops_sec_max: 1000, write_iops_sec_max: 1000
|
read_iops_sec_max: 1000, write_iops_sec_max: 1000
|
||||||
}
|
}
|
||||||
const diskIoFields = [
|
const diskIoBwFields = [
|
||||||
{ key: 'read_bytes_sec', label: '读取带宽', unit: 'B/s', isBandwidth: true },
|
{ key: 'read_bytes_sec', label: '读取' },
|
||||||
{ key: 'write_bytes_sec', label: '写入带宽', unit: 'B/s', isBandwidth: true },
|
{ key: 'write_bytes_sec', label: '写入' },
|
||||||
{ key: 'read_iops_sec', label: '读取 IOPS', unit: 'IOPS', isBandwidth: false },
|
{ key: 'read_bytes_sec_max', label: '突发读取' },
|
||||||
{ key: 'write_iops_sec', label: '写入 IOPS', unit: 'IOPS', isBandwidth: false },
|
{ key: 'write_bytes_sec_max', label: '突发写入' }
|
||||||
{ key: 'read_bytes_sec_max', label: '突发读取带宽', unit: 'B/s', isBandwidth: true },
|
|
||||||
{ key: 'write_bytes_sec_max', label: '突发写入带宽', unit: 'B/s', isBandwidth: true },
|
|
||||||
{ key: 'read_iops_sec_max', label: '突发读取 IOPS', unit: 'IOPS', isBandwidth: false },
|
|
||||||
{ key: 'write_iops_sec_max', label: '突发写入 IOPS', unit: 'IOPS', isBandwidth: false }
|
|
||||||
]
|
]
|
||||||
const formatDiskIoVal = (val, field) => {
|
const diskIoIopsFields = [
|
||||||
|
{ key: 'read_iops_sec', label: '读取' },
|
||||||
|
{ key: 'write_iops_sec', label: '写入' },
|
||||||
|
{ key: 'read_iops_sec_max', label: '突发读取' },
|
||||||
|
{ key: 'write_iops_sec_max', label: '突发写入' }
|
||||||
|
]
|
||||||
|
const diskIoFields = [
|
||||||
|
...diskIoBwFields.map(f => ({ ...f, isBandwidth: true })),
|
||||||
|
...diskIoIopsFields.map(f => ({ ...f, isBandwidth: false }))
|
||||||
|
]
|
||||||
|
const formatDiskIoVal = (val, isBandwidth) => {
|
||||||
if (!val && val !== 0) return '-'
|
if (!val && val !== 0) return '-'
|
||||||
val = Number(val)
|
val = Number(val)
|
||||||
if (!field.isBandwidth) return val.toLocaleString() + ' ' + field.unit
|
if (!isBandwidth) return val.toLocaleString() + ' IOPS'
|
||||||
if (val >= 1073741824) return (val / 1073741824).toFixed(1) + ' GB/s'
|
if (val >= 1073741824) return (val / 1073741824).toFixed(1) + ' GB/s'
|
||||||
if (val >= 1048576) return (val / 1048576).toFixed(0) + ' MB/s'
|
if (val >= 1048576) return (val / 1048576).toFixed(0) + ' MB/s'
|
||||||
if (val >= 1024) return (val / 1024).toFixed(0) + ' KB/s'
|
if (val >= 1024) return (val / 1024).toFixed(0) + ' KB/s'
|
||||||
return val + ' B/s'
|
return val + ' B/s'
|
||||||
}
|
}
|
||||||
|
const ioBwUnitOptions = [
|
||||||
|
{ label: 'B/s', factor: 1 },
|
||||||
|
{ label: 'KB/s', factor: 1024 },
|
||||||
|
{ label: 'MB/s', factor: 1048576 },
|
||||||
|
{ label: 'GB/s', factor: 1073741824 }
|
||||||
|
]
|
||||||
|
const ioBwUnit = ref('MB/s')
|
||||||
|
const tokenIoBwUnit = ref('MB/s')
|
||||||
|
const getIoBwFactor = () => ioBwUnitOptions.find(u => u.label === ioBwUnit.value)?.factor || 1048576
|
||||||
|
const getTokenIoBwFactor = () => ioBwUnitOptions.find(u => u.label === tokenIoBwUnit.value)?.factor || 1048576
|
||||||
|
|
||||||
const showDiskIoSection = ref(false)
|
const showDiskIoSection = ref(false)
|
||||||
const showTokenDiskIo = ref(false)
|
const showTokenDiskIo = ref(false)
|
||||||
const showDetailDiskIo = ref(false)
|
const showDetailDiskIo = ref(false)
|
||||||
@@ -1058,6 +1107,7 @@ const openTokenDialog = () => {
|
|||||||
tokenMemUnit.value = 'GB'
|
tokenMemUnit.value = 'GB'
|
||||||
tokenDiskUnit.value = 'GB'
|
tokenDiskUnit.value = 'GB'
|
||||||
showTokenDiskIo.value = false
|
showTokenDiskIo.value = false
|
||||||
|
tokenIoBwUnit.value = 'MB/s'
|
||||||
tokenDialogVisible.value = true
|
tokenDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1377,5 +1427,6 @@ onBeforeUnmount(() => { isPageActive = false; disposeCharts() })
|
|||||||
.section-hint { font-size: 12px; color: #909399; font-weight: 400; }
|
.section-hint { font-size: 12px; color: #909399; font-weight: 400; }
|
||||||
.tk-section-title.clickable { cursor: pointer; user-select: none; display: flex; align-items: center; gap: 6px; }
|
.tk-section-title.clickable { cursor: pointer; user-select: none; display: flex; align-items: center; gap: 6px; }
|
||||||
.tk-section-title.clickable:hover { color: #409eff; }
|
.tk-section-title.clickable:hover { color: #409eff; }
|
||||||
|
.io-sub-title { font-size: 13px; font-weight: 500; color: #606266; margin: 12px 0 8px; display: flex; align-items: center; }
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -165,12 +165,25 @@
|
|||||||
<el-icon class="section-arrow" :class="{ expanded: showDiskIoSection }"><ArrowRight /></el-icon>
|
<el-icon class="section-arrow" :class="{ expanded: showDiskIoSection }"><ArrowRight /></el-icon>
|
||||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showDiskIoSection" class="tk-resource-grid">
|
<div v-show="showDiskIoSection">
|
||||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label">
|
<div class="io-sub-title">
|
||||||
<el-input-number v-model="formData[f.key]" :min="0" controls-position="right" />
|
带宽限制
|
||||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
<el-select v-model="ioBwUnit" class="tk-unit-select" style="width: 90px; margin-left: 8px">
|
||||||
|
<el-option v-for="u in ioBwUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoBwFields" :key="f.key" :label="f.label">
|
||||||
|
<el-input-number :model-value="+(formData[f.key] / getIoBwFactor()).toFixed(2)" @update:model-value="v => formData[f.key] = Math.round((v || 0) * getIoBwFactor())" :min="0" controls-position="right" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="io-sub-title">IOPS 限制</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoIopsFields" :key="f.key" :label="f.label">
|
||||||
|
<el-input-number v-model="formData[f.key]" :min="0" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tk-section">
|
<div class="tk-section">
|
||||||
<div class="tk-section-title">其他配置</div>
|
<div class="tk-section-title">其他配置</div>
|
||||||
@@ -290,12 +303,25 @@
|
|||||||
<el-icon class="section-arrow" :class="{ expanded: showTokenDiskIo }"><ArrowRight /></el-icon>
|
<el-icon class="section-arrow" :class="{ expanded: showTokenDiskIo }"><ArrowRight /></el-icon>
|
||||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showTokenDiskIo" class="tk-resource-grid">
|
<div v-show="showTokenDiskIo">
|
||||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label" class="tk-res-item">
|
<div class="io-sub-title">
|
||||||
<el-input-number v-model="tokenForm[f.key]" :min="0" controls-position="right" />
|
带宽限制
|
||||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
<el-select v-model="tokenIoBwUnit" class="tk-unit-select" style="width: 90px; margin-left: 8px">
|
||||||
|
<el-option v-for="u in ioBwUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoBwFields" :key="f.key" :label="f.label" class="tk-res-item">
|
||||||
|
<el-input-number :model-value="+(tokenForm[f.key] / getTokenIoBwFactor()).toFixed(2)" @update:model-value="v => tokenForm[f.key] = Math.round((v || 0) * getTokenIoBwFactor())" :min="0" controls-position="right" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="io-sub-title">IOPS 限制</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoIopsFields" :key="f.key" :label="f.label" class="tk-res-item">
|
||||||
|
<el-input-number v-model="tokenForm[f.key]" :min="0" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tk-section">
|
<div class="tk-section">
|
||||||
<div class="tk-section-title">令牌有效期</div>
|
<div class="tk-section-title">令牌有效期</div>
|
||||||
@@ -450,25 +476,32 @@ const diskIoDefaults = {
|
|||||||
read_bytes_sec_max: 314572800, write_bytes_sec_max: 314572800,
|
read_bytes_sec_max: 314572800, write_bytes_sec_max: 314572800,
|
||||||
read_iops_sec_max: 1000, write_iops_sec_max: 1000
|
read_iops_sec_max: 1000, write_iops_sec_max: 1000
|
||||||
}
|
}
|
||||||
const diskIoFields = [
|
const diskIoBwFields = [
|
||||||
{ key: 'read_bytes_sec', label: '读取带宽', unit: 'B/s', isBandwidth: true },
|
{ key: 'read_bytes_sec', label: '读取' },
|
||||||
{ key: 'write_bytes_sec', label: '写入带宽', unit: 'B/s', isBandwidth: true },
|
{ key: 'write_bytes_sec', label: '写入' },
|
||||||
{ key: 'read_iops_sec', label: '读取 IOPS', unit: 'IOPS', isBandwidth: false },
|
{ key: 'read_bytes_sec_max', label: '突发读取' },
|
||||||
{ key: 'write_iops_sec', label: '写入 IOPS', unit: 'IOPS', isBandwidth: false },
|
{ key: 'write_bytes_sec_max', label: '突发写入' }
|
||||||
{ key: 'read_bytes_sec_max', label: '突发读取带宽', unit: 'B/s', isBandwidth: true },
|
|
||||||
{ key: 'write_bytes_sec_max', label: '突发写入带宽', unit: 'B/s', isBandwidth: true },
|
|
||||||
{ key: 'read_iops_sec_max', label: '突发读取 IOPS', unit: 'IOPS', isBandwidth: false },
|
|
||||||
{ key: 'write_iops_sec_max', label: '突发写入 IOPS', unit: 'IOPS', isBandwidth: false }
|
|
||||||
]
|
]
|
||||||
const formatDiskIoVal = (val, field) => {
|
const diskIoIopsFields = [
|
||||||
if (!val && val !== 0) return '-'
|
{ key: 'read_iops_sec', label: '读取' },
|
||||||
val = Number(val)
|
{ key: 'write_iops_sec', label: '写入' },
|
||||||
if (!field.isBandwidth) return val.toLocaleString() + ' ' + field.unit
|
{ key: 'read_iops_sec_max', label: '突发读取' },
|
||||||
if (val >= 1073741824) return (val / 1073741824).toFixed(1) + ' GB/s'
|
{ key: 'write_iops_sec_max', label: '突发写入' }
|
||||||
if (val >= 1048576) return (val / 1048576).toFixed(0) + ' MB/s'
|
]
|
||||||
if (val >= 1024) return (val / 1024).toFixed(0) + ' KB/s'
|
const diskIoFields = [
|
||||||
return val + ' B/s'
|
...diskIoBwFields.map(f => ({ ...f, isBandwidth: true })),
|
||||||
}
|
...diskIoIopsFields.map(f => ({ ...f, isBandwidth: false }))
|
||||||
|
]
|
||||||
|
const ioBwUnitOptions = [
|
||||||
|
{ label: 'B/s', factor: 1 },
|
||||||
|
{ label: 'KB/s', factor: 1024 },
|
||||||
|
{ label: 'MB/s', factor: 1048576 },
|
||||||
|
{ label: 'GB/s', factor: 1073741824 }
|
||||||
|
]
|
||||||
|
const ioBwUnit = ref('MB/s')
|
||||||
|
const tokenIoBwUnit = ref('MB/s')
|
||||||
|
const getIoBwFactor = () => ioBwUnitOptions.find(u => u.label === ioBwUnit.value)?.factor || 1048576
|
||||||
|
const getTokenIoBwFactor = () => ioBwUnitOptions.find(u => u.label === tokenIoBwUnit.value)?.factor || 1048576
|
||||||
|
|
||||||
const showDiskIoSection = ref(false)
|
const showDiskIoSection = ref(false)
|
||||||
const showTokenDiskIo = ref(false)
|
const showTokenDiskIo = ref(false)
|
||||||
@@ -581,6 +614,7 @@ const resetForm = () => {
|
|||||||
_groupName: '', ...diskIoDefaults
|
_groupName: '', ...diskIoDefaults
|
||||||
})
|
})
|
||||||
showDiskIoSection.value = false
|
showDiskIoSection.value = false
|
||||||
|
ioBwUnit.value = 'MB/s'
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleHostGroupSelected = (group) => {
|
const handleHostGroupSelected = (group) => {
|
||||||
@@ -816,6 +850,7 @@ const openTokenDialog = () => {
|
|||||||
tokenMemUnit.value = 'GB'
|
tokenMemUnit.value = 'GB'
|
||||||
tokenDiskUnit.value = 'GB'
|
tokenDiskUnit.value = 'GB'
|
||||||
showTokenDiskIo.value = false
|
showTokenDiskIo.value = false
|
||||||
|
tokenIoBwUnit.value = 'MB/s'
|
||||||
tokenDialogVisible.value = true
|
tokenDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -900,4 +935,5 @@ onMounted(() => {
|
|||||||
.section-arrow { transition: transform 0.2s; font-size: 14px; }
|
.section-arrow { transition: transform 0.2s; font-size: 14px; }
|
||||||
.section-arrow.expanded { transform: rotate(90deg); }
|
.section-arrow.expanded { transform: rotate(90deg); }
|
||||||
.section-hint { font-size: 12px; color: #909399; font-weight: 400; }
|
.section-hint { font-size: 12px; color: #909399; font-weight: 400; }
|
||||||
|
.io-sub-title { font-size: 13px; font-weight: 500; color: #606266; margin: 12px 0 8px; display: flex; align-items: center; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -137,12 +137,25 @@
|
|||||||
<el-icon class="section-arrow" :class="{ expanded: showTokenDiskIo }"><ArrowRight /></el-icon>
|
<el-icon class="section-arrow" :class="{ expanded: showTokenDiskIo }"><ArrowRight /></el-icon>
|
||||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showTokenDiskIo" class="tk-resource-grid">
|
<div v-show="showTokenDiskIo">
|
||||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label" class="tk-res-item">
|
<div class="io-sub-title">
|
||||||
<el-input-number v-model="tokenForm[f.key]" :min="0" controls-position="right" />
|
带宽限制
|
||||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
<el-select v-model="tokenIoBwUnit" class="tk-unit-select" style="width: 90px; margin-left: 8px">
|
||||||
|
<el-option v-for="u in ioBwUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoBwFields" :key="f.key" :label="f.label" class="tk-res-item">
|
||||||
|
<el-input-number :model-value="+(tokenForm[f.key] / getTokenIoBwFactor()).toFixed(2)" @update:model-value="v => tokenForm[f.key] = Math.round((v || 0) * getTokenIoBwFactor())" :min="0" controls-position="right" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="io-sub-title">IOPS 限制</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoIopsFields" :key="f.key" :label="f.label" class="tk-res-item">
|
||||||
|
<el-input-number v-model="tokenForm[f.key]" :min="0" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tk-section">
|
<div class="tk-section">
|
||||||
<div class="tk-section-title">令牌有效期</div>
|
<div class="tk-section-title">令牌有效期</div>
|
||||||
@@ -345,12 +358,25 @@
|
|||||||
<el-icon class="section-arrow" :class="{ expanded: showHostDiskIo }"><ArrowRight /></el-icon>
|
<el-icon class="section-arrow" :class="{ expanded: showHostDiskIo }"><ArrowRight /></el-icon>
|
||||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showHostDiskIo" class="tk-resource-grid">
|
<div v-show="showHostDiskIo">
|
||||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label">
|
<div class="io-sub-title">
|
||||||
<el-input-number v-model="hostForm[f.key]" :min="0" controls-position="right" />
|
带宽限制
|
||||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
<el-select v-model="hostIoBwUnit" class="tk-unit-select" style="width: 90px; margin-left: 8px">
|
||||||
|
<el-option v-for="u in ioBwUnitOptions" :key="u.label" :label="u.label" :value="u.label" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoBwFields" :key="f.key" :label="f.label">
|
||||||
|
<el-input-number :model-value="+(hostForm[f.key] / getHostIoBwFactor()).toFixed(2)" @update:model-value="v => hostForm[f.key] = Math.round((v || 0) * getHostIoBwFactor())" :min="0" controls-position="right" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="io-sub-title">IOPS 限制</div>
|
||||||
|
<div class="tk-resource-grid">
|
||||||
|
<el-form-item v-for="f in diskIoIopsFields" :key="f.key" :label="f.label">
|
||||||
|
<el-input-number v-model="hostForm[f.key]" :min="0" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tk-section">
|
<div class="tk-section">
|
||||||
<div class="tk-section-title">其他配置</div>
|
<div class="tk-section-title">其他配置</div>
|
||||||
@@ -657,16 +683,33 @@ const diskIoDefaults = {
|
|||||||
read_bytes_sec_max: 314572800, write_bytes_sec_max: 314572800,
|
read_bytes_sec_max: 314572800, write_bytes_sec_max: 314572800,
|
||||||
read_iops_sec_max: 1000, write_iops_sec_max: 1000
|
read_iops_sec_max: 1000, write_iops_sec_max: 1000
|
||||||
}
|
}
|
||||||
const diskIoFields = [
|
const diskIoBwFields = [
|
||||||
{ key: 'read_bytes_sec', label: '读取带宽', unit: 'B/s', isBandwidth: true },
|
{ key: 'read_bytes_sec', label: '读取' },
|
||||||
{ key: 'write_bytes_sec', label: '写入带宽', unit: 'B/s', isBandwidth: true },
|
{ key: 'write_bytes_sec', label: '写入' },
|
||||||
{ key: 'read_iops_sec', label: '读取 IOPS', unit: 'IOPS', isBandwidth: false },
|
{ key: 'read_bytes_sec_max', label: '突发读取' },
|
||||||
{ key: 'write_iops_sec', label: '写入 IOPS', unit: 'IOPS', isBandwidth: false },
|
{ key: 'write_bytes_sec_max', label: '突发写入' }
|
||||||
{ key: 'read_bytes_sec_max', label: '突发读取带宽', unit: 'B/s', isBandwidth: true },
|
|
||||||
{ key: 'write_bytes_sec_max', label: '突发写入带宽', unit: 'B/s', isBandwidth: true },
|
|
||||||
{ key: 'read_iops_sec_max', label: '突发读取 IOPS', unit: 'IOPS', isBandwidth: false },
|
|
||||||
{ key: 'write_iops_sec_max', label: '突发写入 IOPS', unit: 'IOPS', isBandwidth: false }
|
|
||||||
]
|
]
|
||||||
|
const diskIoIopsFields = [
|
||||||
|
{ key: 'read_iops_sec', label: '读取' },
|
||||||
|
{ key: 'write_iops_sec', label: '写入' },
|
||||||
|
{ key: 'read_iops_sec_max', label: '突发读取' },
|
||||||
|
{ key: 'write_iops_sec_max', label: '突发写入' }
|
||||||
|
]
|
||||||
|
const diskIoFields = [
|
||||||
|
...diskIoBwFields.map(f => ({ ...f, isBandwidth: true })),
|
||||||
|
...diskIoIopsFields.map(f => ({ ...f, isBandwidth: false }))
|
||||||
|
]
|
||||||
|
const ioBwUnitOptions = [
|
||||||
|
{ label: 'B/s', factor: 1 },
|
||||||
|
{ label: 'KB/s', factor: 1024 },
|
||||||
|
{ label: 'MB/s', factor: 1048576 },
|
||||||
|
{ label: 'GB/s', factor: 1073741824 }
|
||||||
|
]
|
||||||
|
const hostIoBwUnit = ref('MB/s')
|
||||||
|
const tokenIoBwUnit = ref('MB/s')
|
||||||
|
const getHostIoBwFactor = () => ioBwUnitOptions.find(u => u.label === hostIoBwUnit.value)?.factor || 1048576
|
||||||
|
const getTokenIoBwFactor = () => ioBwUnitOptions.find(u => u.label === tokenIoBwUnit.value)?.factor || 1048576
|
||||||
|
|
||||||
const showHostDiskIo = ref(false)
|
const showHostDiskIo = ref(false)
|
||||||
const showTokenDiskIo = ref(false)
|
const showTokenDiskIo = ref(false)
|
||||||
|
|
||||||
@@ -690,6 +733,7 @@ const handleAddHost = () => {
|
|||||||
hostDialogType.value = 'add'
|
hostDialogType.value = 'add'
|
||||||
Object.assign(hostForm, { id: undefined, name: '', base_url: '', ip: '', token: '', port: 22, user: '', password: '', private_key: '', max_cpu: 0, max_memory: 0, max_disk: 0, rx_bandwidth: 0, tx_bandwidth: 0, host_group_id: 0, description: '', ...diskIoDefaults })
|
Object.assign(hostForm, { id: undefined, name: '', base_url: '', ip: '', token: '', port: 22, user: '', password: '', private_key: '', max_cpu: 0, max_memory: 0, max_disk: 0, rx_bandwidth: 0, tx_bandwidth: 0, host_group_id: 0, description: '', ...diskIoDefaults })
|
||||||
showHostDiskIo.value = false
|
showHostDiskIo.value = false
|
||||||
|
hostIoBwUnit.value = 'MB/s'
|
||||||
hostDialogVisible.value = true
|
hostDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,6 +741,7 @@ const handleAddHostToGroup = (group) => {
|
|||||||
hostDialogType.value = 'add'
|
hostDialogType.value = 'add'
|
||||||
Object.assign(hostForm, { id: undefined, name: '', base_url: '', ip: '', token: '', port: 22, user: '', password: '', private_key: '', max_cpu: 0, max_memory: 0, max_disk: 0, rx_bandwidth: 0, tx_bandwidth: 0, host_group_id: group.id, description: '', ...diskIoDefaults })
|
Object.assign(hostForm, { id: undefined, name: '', base_url: '', ip: '', token: '', port: 22, user: '', password: '', private_key: '', max_cpu: 0, max_memory: 0, max_disk: 0, rx_bandwidth: 0, tx_bandwidth: 0, host_group_id: group.id, description: '', ...diskIoDefaults })
|
||||||
showHostDiskIo.value = false
|
showHostDiskIo.value = false
|
||||||
|
hostIoBwUnit.value = 'MB/s'
|
||||||
hostDialogVisible.value = true
|
hostDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,6 +854,7 @@ const openTokenDialog = () => {
|
|||||||
tokenMemUnit.value = 'GB'
|
tokenMemUnit.value = 'GB'
|
||||||
tokenDiskUnit.value = 'GB'
|
tokenDiskUnit.value = 'GB'
|
||||||
showTokenDiskIo.value = false
|
showTokenDiskIo.value = false
|
||||||
|
tokenIoBwUnit.value = 'MB/s'
|
||||||
tokenDialogVisible.value = true
|
tokenDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,4 +945,5 @@ onMounted(() => { if (serviceId.value) loadTreeData() })
|
|||||||
.section-arrow { transition: transform 0.2s; font-size: 14px; }
|
.section-arrow { transition: transform 0.2s; font-size: 14px; }
|
||||||
.section-arrow.expanded { transform: rotate(90deg); }
|
.section-arrow.expanded { transform: rotate(90deg); }
|
||||||
.section-hint { font-size: 12px; color: #909399; font-weight: 400; }
|
.section-hint { font-size: 12px; color: #909399; font-weight: 400; }
|
||||||
|
.io-sub-title { font-size: 13px; font-weight: 500; color: #606266; margin: 12px 0 8px; display: flex; align-items: center; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user