feat(admin/host): 宿主机表单与详情增加硬盘IO限制8字段(可折叠动态展示) -- 缘由: 后端新增 read/write_bytes_sec, read/write_iops_sec 及突发对应字段 -- 预期: HostManage/HostDetail/HostTreeManage 的新增/编辑/令牌表单含可折叠IO参数区, 详情页可展开查看IO限制值
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -131,6 +131,19 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tk-section">
|
||||
<div class="tk-section-title clickable" @click="showTokenDiskIo = !showTokenDiskIo">
|
||||
硬盘 IO 限制
|
||||
<el-icon class="section-arrow" :class="{ expanded: showTokenDiskIo }"><ArrowRight /></el-icon>
|
||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||
</div>
|
||||
<div v-show="showTokenDiskIo" class="tk-resource-grid">
|
||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label" class="tk-res-item">
|
||||
<el-input-number v-model="tokenForm[f.key]" :min="0" controls-position="right" />
|
||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tk-section">
|
||||
<div class="tk-section-title">令牌有效期</div>
|
||||
<el-form-item label="有效期" prop="expire_hours">
|
||||
@@ -326,6 +339,19 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tk-section">
|
||||
<div class="tk-section-title clickable" @click="showHostDiskIo = !showHostDiskIo">
|
||||
硬盘 IO 限制
|
||||
<el-icon class="section-arrow" :class="{ expanded: showHostDiskIo }"><ArrowRight /></el-icon>
|
||||
<span class="section-hint">可选,不展开则使用默认值</span>
|
||||
</div>
|
||||
<div v-show="showHostDiskIo" class="tk-resource-grid">
|
||||
<el-form-item v-for="f in diskIoFields" :key="f.key" :label="f.label">
|
||||
<el-input-number v-model="hostForm[f.key]" :min="0" controls-position="right" />
|
||||
<span class="tk-res-unit">{{ f.unit }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tk-section">
|
||||
<div class="tk-section-title">其他配置</div>
|
||||
<el-form-item label="宿主机组">
|
||||
@@ -625,6 +651,25 @@ const handleOptimalHost = async (row) => {
|
||||
}
|
||||
|
||||
// ---- 宿主机 CRUD ----
|
||||
const diskIoDefaults = {
|
||||
read_bytes_sec: 314572800, write_bytes_sec: 314572800,
|
||||
read_iops_sec: 1000, write_iops_sec: 1000,
|
||||
read_bytes_sec_max: 314572800, write_bytes_sec_max: 314572800,
|
||||
read_iops_sec_max: 1000, write_iops_sec_max: 1000
|
||||
}
|
||||
const diskIoFields = [
|
||||
{ key: 'read_bytes_sec', label: '读取带宽', unit: 'B/s', isBandwidth: true },
|
||||
{ key: 'write_bytes_sec', label: '写入带宽', unit: 'B/s', isBandwidth: true },
|
||||
{ key: 'read_iops_sec', label: '读取 IOPS', unit: 'IOPS', isBandwidth: false },
|
||||
{ key: 'write_iops_sec', label: '写入 IOPS', unit: 'IOPS', isBandwidth: false },
|
||||
{ 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 showHostDiskIo = ref(false)
|
||||
const showTokenDiskIo = ref(false)
|
||||
|
||||
const hostDialogVisible = ref(false)
|
||||
const hostDialogType = ref('add')
|
||||
const hostFormRef = ref(null)
|
||||
@@ -632,7 +677,8 @@ const hostForm = reactive({
|
||||
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: ''
|
||||
rx_bandwidth: 0, tx_bandwidth: 0, host_group_id: 0, description: '',
|
||||
...diskIoDefaults
|
||||
})
|
||||
const hostFormRules = {
|
||||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||||
@@ -642,13 +688,15 @@ const hostFormRules = {
|
||||
|
||||
const handleAddHost = () => {
|
||||
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: '' })
|
||||
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
|
||||
hostDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleAddHostToGroup = (group) => {
|
||||
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: '' })
|
||||
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
|
||||
hostDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -659,14 +707,17 @@ const handleEditHost = (row) => {
|
||||
port: row.port || 22, user: row.user || '', password: row.password || '', private_key: row.private_key || '',
|
||||
max_cpu: row.max_cpu || 0, max_memory: row.max_memory || 0, max_disk: row.max_disk || 0,
|
||||
rx_bandwidth: row.rx_bandwidth || 0, tx_bandwidth: row.tx_bandwidth || 0,
|
||||
host_group_id: row.host_group_id || 0, description: row.description || ''
|
||||
host_group_id: row.host_group_id || 0, description: row.description || '',
|
||||
...Object.fromEntries(diskIoFields.map(f => [f.key, row[f.key] ?? diskIoDefaults[f.key]]))
|
||||
})
|
||||
showHostDiskIo.value = diskIoFields.some(f => row[f.key] && row[f.key] !== diskIoDefaults[f.key])
|
||||
getRemoteHostDetail({ service_id: serviceId.value, id: row.id }).then(res => {
|
||||
if (res?.data?.code === 200 && res?.data?.data) {
|
||||
const d = res.data.data.host ?? res.data.data.data ?? res.data.data
|
||||
if (d.password) hostForm.password = d.password
|
||||
if (d.token) hostForm.token = d.token
|
||||
if (d.private_key) hostForm.private_key = d.private_key
|
||||
diskIoFields.forEach(f => { if (d[f.key] !== undefined) hostForm[f.key] = d[f.key] })
|
||||
}
|
||||
}).catch(() => {})
|
||||
hostDialogVisible.value = true
|
||||
@@ -723,7 +774,8 @@ const tokenForm = reactive({
|
||||
name: '', host_group_id: 0, max_cpu: 4,
|
||||
max_memory: 4194304, max_disk: 100,
|
||||
rx_bandwidth: 100, tx_bandwidth: 100,
|
||||
description: '', expire_hours: 24
|
||||
description: '', expire_hours: 24,
|
||||
...diskIoDefaults
|
||||
})
|
||||
const tokenResultInfo = reactive({ name: '', expire_hours: 24, token: '', service_id: 0 })
|
||||
const tokenRules = {
|
||||
@@ -751,10 +803,12 @@ const openTokenDialog = () => {
|
||||
name: '', host_group_id: 0, max_cpu: 4,
|
||||
max_memory: 4194304, max_disk: 100,
|
||||
rx_bandwidth: 100, tx_bandwidth: 100,
|
||||
description: '', expire_hours: 24
|
||||
description: '', expire_hours: 24,
|
||||
...diskIoDefaults
|
||||
})
|
||||
tokenMemUnit.value = 'GB'
|
||||
tokenDiskUnit.value = 'GB'
|
||||
showTokenDiskIo.value = false
|
||||
tokenDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -772,6 +826,7 @@ const handleTokenSubmit = () => {
|
||||
fd.append('max_disk', tokenForm.max_disk)
|
||||
fd.append('rx_bandwidth', tokenForm.rx_bandwidth)
|
||||
fd.append('tx_bandwidth', tokenForm.tx_bandwidth)
|
||||
diskIoFields.forEach(f => { if (tokenForm[f.key] !== undefined) fd.append(f.key, tokenForm[f.key]) })
|
||||
fd.append('description', tokenForm.description || '')
|
||||
fd.append('expire_hours', tokenForm.expire_hours)
|
||||
const res = await createHostToken(fd)
|
||||
@@ -839,4 +894,9 @@ onMounted(() => { if (serviceId.value) loadTreeData() })
|
||||
|
||||
.host-addr { color: #409eff; font-size: 13px; }
|
||||
.host-url { color: #909399; font-size: 12px; }
|
||||
.tk-section-title.clickable { cursor: pointer; user-select: none; display: flex; align-items: center; gap: 6px; }
|
||||
.tk-section-title.clickable:hover { color: #409eff; }
|
||||
.section-arrow { transition: transform 0.2s; font-size: 14px; }
|
||||
.section-arrow.expanded { transform: rotate(90deg); }
|
||||
.section-hint { font-size: 12px; color: #909399; font-weight: 400; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user