|
|
|
|
@ -7,11 +7,16 @@
|
|
|
|
|
</list-header>
|
|
|
|
|
<div v-loading="loading" style="min-height: 90px;">
|
|
|
|
|
<block-box
|
|
|
|
|
v-for="{ poolId, poolName, nodeNum, cpuCores, gpuNum, availableMemory, totalMemory, diskSize, nodeList, linkUrl }, index in paginatedList"
|
|
|
|
|
v-for="{ poolId, poolName, poolType, nodeNum, cpuCores, gpuNum, availableMemory, totalMemory, diskSize, nodeList, linkUrl }, index in paginatedList"
|
|
|
|
|
:key="poolId" style="margin: 15px 0 0 0;">
|
|
|
|
|
<el-row style="align-items: center;">
|
|
|
|
|
<div class="left">
|
|
|
|
|
<b class="title">{{ poolName }}</b>
|
|
|
|
|
<div style="display: flex;align-items: center;">
|
|
|
|
|
<b class="title" style="margin-right: 10px;">{{ poolName }}</b>
|
|
|
|
|
<el-tag disable-transitions :type="typeColorMap[poolType]">
|
|
|
|
|
{{ ['单机单卡', '单机多卡', '多机多卡'][poolType] || '未知类型' }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="tags">
|
|
|
|
|
<span>节点数量 {{ nodeNum }}</span>
|
|
|
|
|
<span>CPU数 {{ cpuCores }}核</span>
|
|
|
|
|
@ -27,9 +32,7 @@
|
|
|
|
|
<el-button @click="sendRouteChange(linkUrl, 'open')" type="text">配置</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<el-button
|
|
|
|
|
@click="dialogVisible = true; editId = poolId; nodeSelect = nodeList.map(e => e.nodeIp); input = poolName"
|
|
|
|
|
type="text">编辑</el-button>
|
|
|
|
|
<el-button @click="handleEdit(poolId, poolName, nodeList, poolType)" type="text">编辑</el-button>
|
|
|
|
|
<el-button @click="() => handleDelete(poolId)" type="text">删除</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
@ -38,27 +41,38 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 分页组件 -->
|
|
|
|
|
<el-pagination style="margin-top: 15px;" background v-model:current-page="currentPage" v-model:page-size="pageSize"
|
|
|
|
|
<el-pagination style="margin-top: 15px;" background :current-page="currentPage" :page-size="pageSize"
|
|
|
|
|
layout="total, ->, sizes, jumper, prev, next" :page-sizes="[10, 20, 50, 100]" :total="list.length"
|
|
|
|
|
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
|
|
|
|
|
|
|
|
|
<el-dialog @close="editId = null; input = ''; nodeSelect = []" v-model="dialogVisible"
|
|
|
|
|
<el-dialog @close="editId = null; input = ''; poolType = null; nodeSelect = []" v-model="dialogVisible"
|
|
|
|
|
:title="editId ? '编辑资源池' : '创建资源池'" width="1180" :before-close="handleClose">
|
|
|
|
|
<el-row :wrap="false" style="align-items: center;">
|
|
|
|
|
<span style="flex-shrink: 0; margin-right: 14px;">资源池名称</span>
|
|
|
|
|
<el-input style="flex: 1;" v-model="input" size="large" maxlength="20" />
|
|
|
|
|
</el-row>
|
|
|
|
|
<div style="margin-top: 20px; margin-bottom: 10px;">
|
|
|
|
|
<el-row :wrap="false" style="align-items: center; margin-top: 20px;">
|
|
|
|
|
<span style="flex-shrink: 0; margin-right: 14px;">资源池类型</span>
|
|
|
|
|
<el-select v-model="poolType" size="large" style="flex: 1;" placeholder="请选择资源池类型" @change="handleTypeChange">
|
|
|
|
|
<el-option label="单机单卡" value="0" />
|
|
|
|
|
<el-option label="单机多卡" value="1" />
|
|
|
|
|
<el-option label="多机多卡" value="2" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-row>
|
|
|
|
|
<div v-if="poolType" style="margin-top: 20px; margin-bottom: 10px;">
|
|
|
|
|
<span>选择节点</span>
|
|
|
|
|
<span style="float: right;">已选<span style="color: #3061D0; margin: 0 5px;">{{ nodeSelect.length
|
|
|
|
|
}}</span>个节点</span>
|
|
|
|
|
}}</span>个节点</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wrap">
|
|
|
|
|
<div v-if="poolType" class="wrap">
|
|
|
|
|
<div class="wrap-left">
|
|
|
|
|
<div style="margin-top: 12px;"
|
|
|
|
|
v-for="{ nodeIp, cpuCores, gpuNum, gpuMemory, totalMemory, diskSize }, index in nodeList" :key="nodeIp">
|
|
|
|
|
v-for="{ nodeIp, cpuCores, gpuNum, gpuMemory, totalMemory, diskSize } in filteredNodeList"
|
|
|
|
|
:key="nodeIp">
|
|
|
|
|
<div style="display: flex; align-items: center;">
|
|
|
|
|
<el-checkbox :model-value="nodeSelect.includes(nodeIp)" @change="handleCheckboxChange(nodeIp)" />
|
|
|
|
|
<el-checkbox v-if="poolType === '2'" :model-value="nodeSelect.includes(nodeIp)"
|
|
|
|
|
@change="handleCheckboxChange(nodeIp)" />
|
|
|
|
|
<el-radio v-else :model-value="nodeSelect[0]" :label="nodeIp" @change="handleRadioChange(nodeIp)" />
|
|
|
|
|
<span style="color: #0B1524; margin-left: 8px;">{{ nodeIp }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wrap-row">
|
|
|
|
|
@ -75,7 +89,7 @@
|
|
|
|
|
</div>
|
|
|
|
|
<div class="wrap-right">
|
|
|
|
|
<div style="margin-top: 12px;"
|
|
|
|
|
v-for="{ nodeIp, cpuCores, gpuNum, gpuMemory, totalMemory, diskSize }, index in nodeList.filter(e => nodeSelect.includes(e.nodeIp))"
|
|
|
|
|
v-for="{ nodeIp, cpuCores, gpuNum, gpuMemory, totalMemory, diskSize } in nodeList.filter(e => nodeSelect.includes(e.nodeIp))"
|
|
|
|
|
:key="nodeIp">
|
|
|
|
|
<div style="display: flex; align-items: center;">
|
|
|
|
|
<el-icon :size="16" color="red" style="cursor: pointer;" @click="handleCheckboxChange(nodeIp)">
|
|
|
|
|
@ -111,6 +125,12 @@ import useParentAction from '~/vgpu/hooks/useParentAction';
|
|
|
|
|
|
|
|
|
|
const { sendRouteChange } = useParentAction();
|
|
|
|
|
|
|
|
|
|
const typeColorMap = {
|
|
|
|
|
0: 'success', // 绿色 - 单机单卡
|
|
|
|
|
1: 'warning', // 橙色 - 单机多卡
|
|
|
|
|
2: '' // 蓝色 - 多机多卡
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 数据列表相关
|
|
|
|
|
const list = ref([])
|
|
|
|
|
const loading = ref(true)
|
|
|
|
|
@ -124,6 +144,7 @@ const total = ref(0)
|
|
|
|
|
const dialogVisible = ref(false)
|
|
|
|
|
const editId = ref(null)
|
|
|
|
|
const input = ref('')
|
|
|
|
|
const poolType = ref(null) // 默认不选择
|
|
|
|
|
const btnLoading = ref(false)
|
|
|
|
|
|
|
|
|
|
// 节点选择相关
|
|
|
|
|
@ -137,6 +158,19 @@ const paginatedList = computed(() => {
|
|
|
|
|
return list.value.slice(start, end)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 根据类型过滤节点列表
|
|
|
|
|
const filteredNodeList = computed(() => {
|
|
|
|
|
if (!poolType.value) return []
|
|
|
|
|
|
|
|
|
|
if (poolType.value === '1') {
|
|
|
|
|
// 单机多卡:过滤掉只有1张显卡的节点
|
|
|
|
|
return nodeList.value.filter(node => node.gpuNum > 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 单机单卡(0)和多机多卡(2):显示所有节点
|
|
|
|
|
return nodeList.value
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 分页大小变化
|
|
|
|
|
const handleSizeChange = (val) => {
|
|
|
|
|
pageSize.value = val
|
|
|
|
|
@ -157,6 +191,13 @@ const handleOk = async () => {
|
|
|
|
|
})
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!poolType.value) {
|
|
|
|
|
ElMessage({
|
|
|
|
|
message: '请选择资源池类型',
|
|
|
|
|
type: 'warning',
|
|
|
|
|
})
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!nodeSelect.value.length) {
|
|
|
|
|
ElMessage({
|
|
|
|
|
message: '请选择节点',
|
|
|
|
|
@ -172,11 +213,13 @@ const handleOk = async () => {
|
|
|
|
|
res = await pollApi.update({
|
|
|
|
|
pool_id: editId.value,
|
|
|
|
|
pool_name: input.value,
|
|
|
|
|
pool_type: poolType.value,
|
|
|
|
|
nodes
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
res = await pollApi.create({
|
|
|
|
|
pool_name: input.value,
|
|
|
|
|
pool_type: poolType.value,
|
|
|
|
|
nodes
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
@ -191,6 +234,27 @@ const handleOk = async () => {
|
|
|
|
|
btnLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 编辑处理
|
|
|
|
|
const handleEdit = (poolId, poolName, nodes, type) => {
|
|
|
|
|
dialogVisible.value = true;
|
|
|
|
|
editId.value = poolId;
|
|
|
|
|
input.value = poolName;
|
|
|
|
|
nodeSelect.value = nodes.map(e => e.nodeIp);
|
|
|
|
|
// 设置资源池类型
|
|
|
|
|
poolType.value = type.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 类型变化处理
|
|
|
|
|
const handleTypeChange = () => {
|
|
|
|
|
// 类型改变时清空已选择的节点
|
|
|
|
|
nodeSelect.value = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 单选变化
|
|
|
|
|
const handleRadioChange = (ip) => {
|
|
|
|
|
nodeSelect.value = [ip];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 复选框变化
|
|
|
|
|
const handleCheckboxChange = (ip) => {
|
|
|
|
|
const index = nodeSelect.value.indexOf(ip);
|
|
|
|
|
|