迁移前端代码

main
陈博文 1 week ago
parent ddebaa3c41
commit 41810f214d

@ -0,0 +1,36 @@
import request from '@/utils/request';
const apiPrefix = '/api/vgpu';
class pollApi {
getPollList(params) {
return request({
url: apiPrefix + '/v1/resource/pool/list',
method: 'GET',
params,
});
}
// getNodes(data) {
// return request({
// url: apiPrefix + '/v1/nodes',
// method: 'POST',
// data,
// });
// }
// getNodeDetail(params) {
// return request({
// url: apiPrefix + '/v1/node',
// method: 'GET',
// params,
// });
// }
// getNodeListReq(data) {
// return request(this.getNodeList(data));
// }
}
export default new pollApi();

@ -0,0 +1,22 @@
import { useRouter } from 'vue-router';
export default function useParentAction() {
const router = useRouter();
const sendRouteChange = (url) => {
if (window.parent !== window) {
// 在 iframe 中,通知父窗口
const message = {
type: 'ChangeThePath',
data: url
};
window.parent.postMessage(JSON.stringify(message), '*');
} else {
// 不在 iframe 中,直接使用 router 跳转
router.push(url);
}
};
const hasParentWindow = window.parent !== window;
return { sendRouteChange, hasParentWindow };
}

@ -20,6 +20,12 @@ export default (Layout) => ({
name: 'overview',
meta: { title: '资源总览', icon: 'dashboard', noCache: true },
},
{
path: '/admin/vgpu/poll/admin',
component: () => import('~/vgpu/views/poll/admin/index.vue'),
name: 'poll-admin',
meta: { title: '资源池管理', icon: 'vgpu-pool-tab', noCache: true },
},
{
path: '/admin/vgpu/node/admin',
component: () => import('~/vgpu/views/node/admin/index.vue'),

@ -11,29 +11,29 @@ export const rangeConfigInit = [
normal: {
color: {
type: 'linear',
x: 0, // 渐变起始点 0%
y: 0, // 渐变起始点 0%
x2: 0, // 渐变结束点 100%
y2: 1, // 渐变结束点 100%
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(250, 200, 88, 0.16)', // 渐变起始颜色
color: 'rgba(250, 200, 88, 0.16)',
},
{
offset: 1,
color: 'rgba(250, 200, 88, 0.00)', // 渐变结束颜色
color: 'rgba(250, 200, 88, 0.00)',
},
],
global: false, // 缺省为 false
global: false,
},
},
},
itemStyle: {
color: 'rgb(250, 200, 88)', // 设置线条颜色为橙色
color: 'rgb(250, 200, 88)',
},
lineStyle: {
color: 'rgb(250, 200, 88)', // 设置线条颜色为橙色
color: 'rgb(250, 200, 88)',
},
},
{
@ -45,65 +45,134 @@ export const rangeConfigInit = [
normal: {
color: {
type: 'linear',
x: 0, // 渐变起始点 0%
y: 0, // 渐变起始点 0%
x2: 0, // 渐变结束点 100%
y2: 1, // 渐变结束点 100%
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(84, 112, 198, 0.16)', // 渐变起始颜色
color: 'rgba(84, 112, 198, 0.16)',
},
{
offset: 1,
color: 'rgba(84, 112, 198, 0.00)', // 渐变结束颜色
color: 'rgba(84, 112, 198, 0.00)',
},
],
global: false, // 缺省为 false
global: false,
},
},
},
itemStyle: {
color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色
color: 'rgb(84, 112, 198)',
},
lineStyle: {
color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色
color: 'rgb(84, 112, 198)',
},
},
{
name: '显存',
query: `sum(hami_container_vmemory_allocated) / sum(hami_memory_size) * 100`,
data: [],
type: 'line',
areaStyle: {
normal: {
color: {
type: 'linear',
x: 0, // 渐变起始点 0%
y: 0, // 渐变起始点 0%
x2: 0, // 渐变结束点 100%
y2: 1, // 渐变结束点 100%
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(34, 139, 34, 0.16)', // 渐变起始颜色
color: 'rgba(34, 139, 34, 0.16)',
},
{
offset: 1,
color: 'rgba(34, 139, 34, 0.00)', // 渐变结束颜色
color: 'rgba(34, 139, 34, 0.00)',
},
],
global: false, // 缺省为 false
global: false,
},
},
},
itemStyle: {
color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色
color: 'rgb(145, 204, 117)',
},
lineStyle: {
color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色
color: 'rgb(145, 204, 117)',
},
},
],
{
name: 'CPU',
query: `sum(hami_container_cpu_allocated) / sum(hami_cpu_count) * 100`,
data: [],
type: 'line',
areaStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(255, 99, 71, 0.16)',
},
{
offset: 1,
color: 'rgba(255, 99, 71, 0.00)',
},
],
global: false,
},
},
},
itemStyle: {
color: 'rgb(255, 99, 71)',
},
lineStyle: {
color: 'rgb(255, 99, 71)',
},
},
{
name: '内存',
query: `sum(hami_container_memory_allocated) / sum(hami_memory_capacity) * 100`,
data: [],
type: 'line',
areaStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(138, 43, 226, 0.16)',
},
{
offset: 1,
color: 'rgba(138, 43, 226, 0.00)',
},
],
global: false,
},
},
},
itemStyle: {
color: 'rgb(138, 43, 226)',
},
lineStyle: {
color: 'rgb(138, 43, 226)',
},
}
]
},
{
title: '资源使用趋势',
@ -174,6 +243,74 @@ export const rangeConfigInit = [
color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色
},
},
{
name: 'CPU',
query: `sum(hami_container_cpu_allocated) / sum(hami_cpu_count) * 100`,
data: [],
type: 'line',
areaStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(255, 99, 71, 0.16)',
},
{
offset: 1,
color: 'rgba(255, 99, 71, 0.00)',
},
],
global: false,
},
},
},
itemStyle: {
color: 'rgb(255, 99, 71)',
},
lineStyle: {
color: 'rgb(255, 99, 71)',
},
},
{
name: '内存',
query: `sum(hami_container_memory_allocated) / sum(hami_memory_capacity) * 100`,
data: [],
type: 'line',
areaStyle: {
normal: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(138, 43, 226, 0.16)',
},
{
offset: 1,
color: 'rgba(138, 43, 226, 0.00)',
},
],
global: false,
},
},
},
itemStyle: {
color: 'rgb(138, 43, 226)',
},
lineStyle: {
color: 'rgb(138, 43, 226)',
},
}
],
},
];

@ -3,28 +3,30 @@
<div class="home-left">
<Block title="显卡资源">
<template #extra>
<div class="all-btn" @click="router.push('/admin/vgpu/card/admin')">
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/card/admin')">
全部<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template>
<div class="card-overview">
<div v-for="item in cardGaugeConfig" :key="item.title">
<div v-for="item in cardGaugeConfig.slice(0, 5)" :key="item.title">
<Gauge v-bind="item" />
</div>
</div>
<div class="card-overview">
<div v-for="item in cardGaugeConfig.slice(-3)" :key="item.title">
<Gauge v-bind="item" />
</div>
</div>
</Block>
<Block title="资源总览">
<template #extra>
<div class="all-btn" @click="router.push('/admin/vgpu/card/admin')">
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/card/admin')">
全部<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template>
<ul class="resourceOverview">
<li
v-for="{ title, count, icon, to, unit } in resourceOverview"
:key="title"
@click="router.push(to)"
>
<li v-for="{ title, count, icon, to, unit } in resourceOverview" :key="title"
:style="{ cursor: to ? 'pointer' : 'default' }" @click="sendRouteChange(to)">
<div class="avatar">
<svg-icon :icon="icon" />
</div>
@ -44,28 +46,21 @@
<template #extra>
<time-picker v-model="times" type="datetimerange" size="small" />
</template>
<echarts-plus
:options="getRangeOptions(dataSource)"
style="height: 250px"
/>
<echarts-plus :options="getRangeOptions(dataSource)" style="height: 250px" />
</Block>
</div>
<div class="home-right">
<Block title="节点总览" style="margin-bottom: 16px">
<template #extra>
<div class="all-btn" @click="router.push('/admin/vgpu/node/admin')">
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/node/admin')">
全部<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template>
<ul class="node-all">
<li
v-for="{ title, status, count, color } in nodes"
:key="title"
@click="
router.push(`/admin/vgpu/node/admin?isSchedulable=${status}`)
"
>
<li v-for="{ title, status, count, color } in nodes" :key="title" @click="
sendRouteChange(`/admin/vgpu/node/admin?isSchedulable=${status}`)
">
<div class="title">{{ title }}</div>
<div class="count" :style="{ color }">
{{ count }}
@ -75,7 +70,7 @@
</Block>
<Block title="显卡类型分布" style="margin-bottom: 16px">
<template #extra>
<div class="all-btn" @click="router.push('/admin/vgpu/card/admin')">
<div class="all-btn" @click="sendRouteChange('/admin/vgpu/card/admin')">
全部<svg-icon icon="more" style="margin-left: 4px" />
</div>
</template>
@ -84,15 +79,9 @@
</div>
</Block>
<TabTop
v-bind="nodeTotalTop"
:onClick="(params) => handleChartClick(params, router)"
style="margin-bottom: 16px"
/>
<TabTop
v-bind="nodeUsedTop"
:onClick="(params) => handleChartClick(params, router)"
/>
<TabTop v-bind="nodeTotalTop" :onClick="(params) => handleChartClick(params, router)"
style="margin-bottom: 16px" />
<TabTop v-bind="nodeUsedTop" :onClick="(params) => handleChartClick(params, router)" />
</div>
</div>
</template>
@ -114,6 +103,7 @@ import taskApi from '~/vgpu/api/task';
import monitorApi from '~/vgpu/api/monitor';
import cardApi from '~/vgpu/api/card';
import useInstantVector from '~/vgpu/hooks/useInstantVector';
import useParentAction from '~/vgpu/hooks/useParentAction';
import useFetchList from '@/hooks/useFetchList';
import { getTopOptions } from '~/vgpu/components/config';
import EchartsPlus from '@/components/Echarts-plus.vue';
@ -128,10 +118,12 @@ const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000);
const { sendRouteChange } = useParentAction();
const times = ref([start, end]);
const handlePieClick = (params) => {
router.push(`/admin/vgpu/card/admin?type=${params.data.name}`);
sendRouteChange(`/admin/vgpu/card/admin?type=${params.data.name}`);
};
@ -139,6 +131,36 @@ const alarmData = ref([])
const chartWidth = ref(200);
const cardGaugeConfig = useInstantVector([
{
title: 'CPU 使用率',
percent: 0,
query: `avg(sum (hami_container_vgpu_allocated) by (instance))`,
totalQuery: `avg(sum (hami_vgpu_count) by (instance))`,
percentQuery: `avg(sum (hami_container_vgpu_allocated) by (instance))/avg(sum (hami_vgpu_count) by (instance)) *100`,
total: 0,
used: 0,
unit: '核',
},
{
title: '内存 使用率',
percent: 0,
query: `avg(sum (hami_container_vgpu_allocated) by (instance))`,
totalQuery: `avg(sum (hami_vgpu_count) by (instance))`,
percentQuery: `avg(sum (hami_container_vgpu_allocated) by (instance))/avg(sum (hami_vgpu_count) by (instance)) *100`,
total: 0,
used: 0,
unit: 'GiB',
},
{
title: '磁盘 使用率',
percent: 0,
query: `avg(sum (hami_container_vgpu_allocated) by (instance))`,
totalQuery: `avg(sum (hami_vgpu_count) by (instance))`,
percentQuery: `avg(sum (hami_container_vgpu_allocated) by (instance))/avg(sum (hami_vgpu_count) by (instance)) *100`,
total: 0,
used: 0,
unit: '块',
},
{
title: 'vGPU 分配率',
percent: 0,
@ -197,12 +219,39 @@ const resourceOverview = ref([
count: 0,
icon: 'vgpu-node',
unit: '个',
to: '/admin/vgpu/node/admin'
},
{
title: '资源池',
count: 0,
icon: 'vgpu-pool-tab',
unit: '个',
to: '/admin/vgpu/resource/admin'
},
{
title: 'CPU',
count: 0,
icon: 'vgpu-chip',
unit: '核',
},
{
title: '内存',
count: 0,
icon: 'vgpu-memory',
unit: 'GIB',
},
{
title: '磁盘',
count: 0,
icon: 'vgpu-disk',
unit: '个',
},
{
title: '显卡',
count: 0,
icon: 'vgpu-gpu-d',
unit: '张',
to: '/admin/vgpu/card/admin'
},
{
title: 'vGPU',
@ -327,6 +376,20 @@ const nodeUsedTop = {
title: '节点资源使用率 Top5',
key: 'used',
config: [
{
tab: 'GPU',
key: 'gpu',
nameKey: 'node',
data: [],
query: 'topk(5, avg(hami_core_util_avg) by (node))',
},
{
tab: '内存',
key: 'internal',
nameKey: 'node',
data: [],
query: 'topk(5, avg(hami_core_util_avg) by (node))',
},
{
tab: '算力',
key: 'core',
@ -349,6 +412,20 @@ const nodeTotalTop = {
title: '节点资源分配率 Top5',
key: 'used',
config: [
{
tab: 'GPU',
key: 'gpu',
nameKey: 'node',
data: [],
query: `topk(5, avg(hami_container_vgpu_allocated{}) by (node) / avg(hami_vgpu_count{}) by (node) * 100)`,
},
{
tab: '内存',
key: 'internal',
nameKey: 'node',
data: [],
query: `topk(5, avg(hami_container_vgpu_allocated{}) by (node) / avg(hami_vgpu_count{}) by (node) * 100)`,
},
{
tab: 'vGPU',
key: 'vgpu',
@ -402,22 +479,23 @@ const fetchRangeData = () => {
cardApi
.getRangeVector({
...params,
query: `sum({__name__=~"alert:.*:count"})`,
})
.then((res) => {
alarmData.value = res.data[0].values;
});
.getRangeVector({
...params,
query: `sum({__name__=~"alert:.*:count"})`,
})
.then((res) => {
alarmData.value = res.data[0].values;
});
};
watchEffect(() => {
resourceOverview.value[0].count = nodeData.value.length;
resourceOverview.value[1].count = cardData.value.length;
resourceOverview.value[2].count = cardGaugeConfig.value[0].total;
resourceOverview.value[3].count = cardGaugeConfig.value[1].total;
resourceOverview.value[4].count = cardGaugeConfig.value[2].total.toFixed(0);
resourceOverview.value[5].count = cardData.value.length;
resourceOverview.value[6].count = cardGaugeConfig.value[3].total;
resourceOverview.value[7].count = cardGaugeConfig.value[4].total;
resourceOverview.value[8].count = cardGaugeConfig.value[5].total.toFixed(0);
});
onMounted(async () => {
@ -479,6 +557,7 @@ watch(
height: 190px;
display: grid;
grid-template-columns: repeat(5, 1fr);
.gauge-info {
margin-top: 10px;
}

@ -0,0 +1,438 @@
<template>
<div>
<back-header>
节点管理 > {{ detail.name }}
<!-- <template #extra>-->
<!-- <el-form-item-->
<!-- label="节点调度"-->
<!-- style="margin-bottom: 0; margin-right: 20px"-->
<!-- >-->
<!-- <el-radio-group-->
<!-- :disabled="detail.isExternal"-->
<!-- v-model="tempSchedulable"-->
<!-- size="small"-->
<!-- @change="onChangeSchedulable"-->
<!-- >-->
<!-- <el-radio-button label="启用" :value="true" />-->
<!-- <el-radio-button label="禁用" :value="false" />-->
<!-- </el-radio-group>-->
<!-- </el-form-item>-->
<!-- </template>-->
</back-header>
<block-box class="node-block">
<div class="node-detail">
<div class="node-detail-left">
<div class="title">详细信息</div>
<ul class="node-detail-info">
<li v-for="{ label, value, render } in detailColumns" :key="label">
<span class="label">{{ label }}</span>
<component v-if="render" :is="render(detail)" />
<span v-else class="value">{{ detail[value] }}</span>
</li>
</ul>
</div>
</div>
</block-box>
<block-box>
<ul class="card-gauges">
<li v-for="(item, index) in gaugeConfig" :key="index">
<template v-if="!detail.isExternal || index >= 2">
<Gauge v-bind="item" />
</template>
<template v-else-if="detail.isExternal && index < 2">
<el-empty description="暂无资源分配数据" :image-size="90" />
</template>
</li>
</ul>
</block-box>
<div class="line-box">
<block-box title="资源分配趋势(%">
<template #extra>
<time-picker v-model="times" type="datetimerange" size="small" />
</template>
<div style="height: 200px">
<echarts-plus
:options="
getRangeOptions({
core: gaugeConfig[0].data,
memory: gaugeConfig[1].data,
})
"
/>
</div>
</block-box>
<block-box title="资源使用趋势(%">
<template #extra>
<time-picker v-model="times" type="datetimerange" size="small" />
</template>
<div style="height: 200px">
<echarts-plus
:options="
getRangeOptions({
core: gaugeConfig[2].data,
memory: gaugeConfig[3].data,
})
"
/>
</div>
</block-box>
</div>
<block-box title="显卡列表">
<CardList :hideTitle="true" :filters="{ nodeUid: detail.uid }" />
</block-box>
<block-box title="任务列表">
<template v-if="detail.isExternal">
<el-alert title="由于节点未纳管,无法获取到任务数据" show-icon type="warning" :closable="false" />
<el-empty description="暂无任务数据" :image-size="100" />
</template>
<template v-else>
<TaskList :hideTitle="true" :filters="{ nodeUid: detail.uid }" />
</template>
</block-box>
</div>
</template>
<script setup lang="jsx">
import BackHeader from '@/components/BackHeader.vue';
import { useRoute, useRouter } from 'vue-router';
import BlockBox from '@/components/BlockBox.vue';
import {computed, onMounted, ref, watch} from 'vue';
import { Tools } from '@element-plus/icons-vue';
import CardList from '~/vgpu/views/card/admin/index.vue';
import TaskList from '~/vgpu/views/task/admin/index.vue';
import Gauge from '~/vgpu/components/gauge.vue';
import useInstantVector from '~/vgpu/hooks/useInstantVector';
import EchartsPlus from '@/components/Echarts-plus.vue';
import TimeSelect from '~/vgpu/components/timeSelect.vue';
import nodeApi from '~/vgpu/api/node';
import { getLineOptions } from '~/vgpu/views/monitor/overview/getOptions';
import { ElMessage, ElMessageBox } from 'element-plus';
import api from '~/vgpu/api/task';
import { getRangeOptions } from './getOptions';
import {getDaysInRange} from "@/utils";
const route = useRoute();
const router = useRouter();
const detail = ref({});
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000);
const times = ref([start, end]);
const isSchedulable = ref(true);
const tempSchedulable = ref(isSchedulable.value);
const cp = useInstantVector(
[
{
label: 'vGPU 超配',
count: '0',
query: `avg(hami_vgpu_count{node=~"$node"})`,
},
{
label: '算力超配',
count: '0',
query: `avg(hami_vcore_scaling{node=~"$node"})`,
},
{
label: '显存超配',
count: '1.5',
query: `avg(hami_vmemory_scaling{node=~"$node"})`,
},
],
(query) => query.replaceAll('$node', detail.value.name),
);
const gaugeConfig = useInstantVector(
[
{
title: '算力分配率',
percent: 0,
query: `avg(sum(hami_container_vcore_allocated{node=~"$node"}) by (instance))`,
totalQuery: `avg(sum(hami_core_size{node=~"$node"}) by (instance))`,
percentQuery: `avg(sum(hami_container_vcore_allocated{node=~"$node"}) by (instance)) / avg(sum(hami_core_size{node=~"$node"}) by (instance)) *100`,
total: 0,
used: 0,
unit: ' ',
},
{
title: '显存分配率',
percent: 0,
query: `avg(sum(hami_container_vmemory_allocated{node=~"$node"}) by (instance)) / 1024`,
totalQuery: `avg(sum(hami_memory_size{node=~"$node"}) by (instance)) / 1024`,
percentQuery: `(avg(sum(hami_container_vmemory_allocated{node=~"$node"}) by (instance)) / 1024) /(avg(sum(hami_memory_size{node=~"$node"}) by (instance)) / 1024) *100`,
total: 0,
used: 0,
unit: 'GiB',
},
{
title: '算力使用率',
percent: 0,
query: `avg(sum(hami_core_util{node=~"$node"}) by (instance))`,
percentQuery: `avg(sum(hami_core_util_avg{node=~"$node"}) by (instance))`,
totalQuery: `avg(sum(hami_core_size{node=~"$node"}) by (instance))`,
total: 100,
used: 0,
unit: ' ',
},
{
title: '显存使用率',
percent: 0,
query: `avg(sum(hami_memory_used{node=~"$node"}) by (instance)) / 1024`,
totalQuery: `avg(sum(hami_memory_size{node=~"$node"}) by (instance))/1024`,
percentQuery: `(avg(sum(hami_memory_used{node=~"$node"}) by (instance)) / 1024)/(avg(sum(hami_memory_size{node=~"$node"}) by (instance))/1024)*100`,
total: 0,
used: 0,
unit: 'GiB',
},
],
(query) => query.replaceAll(`$node`, detail.value.name),
times,
);
const detailColumns = [
{
label: '节点状态',
value: 'status',
render: ({ isSchedulable, isExternal }) => {
if (detail.value && detail.value.isSchedulable !== undefined) {
return (
<el-tag disable-transitions type={isExternal ? 'warning' : (isSchedulable ? 'success' : 'danger')}>
{isExternal ? '未纳管' : (isSchedulable ? '可调度' : '禁止调度')}
</el-tag>
);
} else {
return <el-tag disable-transitions size="small" type="info">加载中...</el-tag>;
}
},
},
{
label: '节点 IP 地址',
value: 'ip',
render: ({ ip }) => <text-plus text={ip} copy />,
},
{
label: '节点 UUID',
value: 'uid',
render: ({ uid }) => <text-plus text={uid} copy />,
},
{
label: '操作系统类型',
value: 'operatingSystem',
render: ({ operatingSystem }) => (
<span>
{operatingSystem==='' ? '--' : operatingSystem}
</span>
),
},
{
label: '系统架构',
value: 'architecture',
render: ({ architecture }) => (
<span>
{architecture==='' ? '--' : architecture}
</span>
),
},
{
label: 'kubelet 版本',
value: 'kubeletVersion',
render: ({ kubeletVersion }) => (
<span>
{kubeletVersion==='' ? '--' : kubeletVersion}
</span>
),
},
{
label: '操作系统版本',
value: 'osImage',
render: ({ osImage }) => (
<span>
{osImage==='' ? '--' : osImage}
</span>
),
},
{
label: '内核版本',
value: 'kernelVersion',
render: ({ kernelVersion }) => (
<span>
{kernelVersion==='' ? '--' : kernelVersion}
</span>
),
},
{
label: 'kube-proxy 版本',
value: 'kubeProxyVersion',
render: ({ kubeProxyVersion }) => (
<span>
{kubeProxyVersion==='' ? '--' : kubeProxyVersion}
</span>
),
},
{
label: '容器运行时',
value: 'containerRuntimeVersion',
render: ({ containerRuntimeVersion }) => (
<span>
{containerRuntimeVersion==='' ? '--' : containerRuntimeVersion}
</span>
),
},
{
label: '显卡数量',
value: 'cardCnt',
render: ({ cardCnt }) => (
<span>
{cardCnt==='' ? '--' : cardCnt}
</span>
),
},
{
label: '创建时间',
value: 'creationTimestamp',
render: ({ creationTimestamp }) => (
<span>
{creationTimestamp==='' ? '--' : creationTimestamp}
</span>
),
},
];
const onChangeSchedulable = (val) => {
ElMessageBox.confirm(
`确认对该节点进行${val ? '启用' : '禁用'}操作?`,
'操作确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
},
)
.then(async () => {
try {
await nodeApi
.stop({
nodeName: detail.value.name,
switch: val ? 'off' : 'on',
})
.then(() => {
setTimeout(() => {
refresh();
ElMessage.success(`${val ? '启用' : '禁用'}成功`);
}, 500);
});
} catch (error) {
ElMessage.error(error.message);
tempSchedulable.value = isSchedulable.value;
}
})
.catch(() => {
tempSchedulable.value = isSchedulable.value;
});
};
const refresh = async () => {
detail.value = await nodeApi.getNodeDetail({ uid: route.params.uid });
isSchedulable.value = detail.value.isSchedulable;
};
onMounted(async () => {
await refresh();
});
</script>
<style lang="scss">
.node-detail {
display: flex;
height: 100%;
//gap: 50px;
ul {
margin: 0;
padding: 0;
list-style: none;
}
.title {
color: #1d2b3a;
font-family: 'PingFang SC';
font-size: 14px;
font-style: normal;
font-weight: 500;
//line-height: 20px;
margin-bottom: 20px;
}
//.node-detail-left {
// min-width: 800px;
//}
.node-detail-info {
gap: 15px;
font-size: 12px;
display: grid;
grid-template-columns: repeat(3, 1fr);
.label {
display: inline-block;
width: 100px;
height: 20px;
color: #939ea9;
}
.set {
:hover {
cursor: pointer;
color: #324558;
}
}
.cp {
display: flex;
gap: 25px;
}
}
.gauges {
flex: 1;
display: flex;
li {
flex: 1;
}
}
}
.card-gauges {
margin: 0;
padding: 0;
list-style: none;
display: flex;
height: 200px;
li {
flex: 1;
}
}
.line-box {
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 20px;
}
.node-block {
display: flex;
flex-direction: column;
.home-block-content {
flex: 1;
}
}
</style>

@ -0,0 +1,120 @@
import { timeParse } from '@/utils';
export const getRangeOptions = ({ core = [], memory = [] }) => {
return {
legend: {
// data: [],
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
},
formatter: function (params) {
var res = params[0].name + '<br/>';
for (var i = 0; i < params.length; i++) {
res +=
params[i].marker +
params[i].seriesName +
' : ' +
(+params[i].value).toFixed(0) +
`%<br/>`;
}
return res;
},
},
grid: {
top: 37, // 上边距
bottom: 20, // 下边距
left: '7%', // 左边距
right: 10, // 右边距
},
xAxis: {
type: 'category',
data: core.map((item) => timeParse(+item.timestamp)),
axisLabel: {
formatter: function (value) {
return timeParse(value, 'HH:mm');
},
},
},
yAxis: {
type: 'value',
// max: 100,
axisLabel: {
formatter: function (value) {
return `${value} %`;
},
},
},
series: [
{
name: '算力',
data: core,
type: 'line',
areaStyle: {
normal: {
color: {
type: 'linear',
x: 0, // 渐变起始点 0%
y: 0, // 渐变起始点 0%
x2: 0, // 渐变结束点 100%
y2: 1, // 渐变结束点 100%
colorStops: [
{
offset: 0,
color: 'rgba(84, 112, 198, 0.16)', // 渐变起始颜色
},
{
offset: 1,
color: 'rgba(84, 112, 198, 0.00)', // 渐变结束颜色
},
],
global: false, // 缺省为 false
},
},
},
itemStyle: {
color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色
},
lineStyle: {
color: 'rgb(84, 112, 198)', // 设置线条颜色为橙色
},
},
{
name: '显存',
data: memory,
type: 'line',
areaStyle: {
normal: {
color: {
type: 'linear',
x: 0, // 渐变起始点 0%
y: 0, // 渐变起始点 0%
x2: 0, // 渐变结束点 100%
y2: 1, // 渐变结束点 100%
colorStops: [
{
offset: 0,
color: 'rgba(34, 139, 34, 0.16)', // 渐变起始颜色
},
{
offset: 1,
color: 'rgba(34, 139, 34, 0.00)', // 渐变结束颜色
},
],
global: false, // 缺省为 false
},
},
},
itemStyle: {
color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色
},
lineStyle: {
color: 'rgb(145, 204, 117)', // 设置线条颜色为橙色
},
},
],
};
};

@ -0,0 +1,233 @@
<template>
<list-header description="资源池管理用于统一管理和调度计算资源,支持资源的动态分配、回收与负载均衡,提升资源利用率和系统灵活性。">
<template #actions>
<el-button @click="editData = null; dialogVisible = true" style="margin-right: 24px;" type="primary"
round>创建资源池</el-button>
</template>
</list-header>
<block-box
v-for="{ poolId, poolName, nodeNum, cpuCores, gpuNum, availableMemory, totalMemory, diskSize }, index in list"
:key="poolId">
<el-row style="align-items: center;">
<div class="left">
<b class="title">{{ poolName }}</b>
<div class="tags">
<span>节点数量&nbsp;&nbsp;{{ nodeNum }}</span>
<span>CPU数&nbsp;&nbsp;{{ cpuCores }}</span>
<span>显卡数量&nbsp;&nbsp;{{ gpuNum }}</span>
<span>可用/总内存&nbsp;&nbsp;{{ bytesToGB(availableMemory) }}GB / {{ bytesToGB(totalMemory) }}GB</span>
<span>磁盘大小&nbsp;&nbsp;{{ bytesToGB(diskSize) }}GB</span>
</div>
</div>
<div class="right">
<el-button type="text">查看详情</el-button>
<template v-if="index === 0">
<el-button type="text">配置</el-button>
</template>
<template v-else>
<el-button type="text">编辑</el-button>
<el-button type="text">删除</el-button>
</template>
</div>
</el-row>
</block-box>
<el-dialog v-model="dialogVisible" :title="editData ? '编辑资源池' : '创建资源池'" 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" />
</el-row>
<div style="margin-top: 20px; margin-bottom: 10px;">
<span>选择节点</span>
<span style="float: right;">已选<span style="color: #3061D0; margin: 0 5px;">{{ nodeSelect.length }}</span>个节点</span>
</div>
<div class="wrap">
<div class="wrap-left">
<div style="margin-top: 12px;"
v-for="{ nodeIp, cpuCores, gpuNum, gpuMemory, totalMemory, diskSize }, index in nodeList" :key="nodeIp">
<div style="display: flex; align-items: center;">
<el-checkbox :model-value="nodeSelect.includes(nodeIp)" @change="handleCheckboxChange(nodeIp)" />
<span style="color: #0B1524; margin-left: 8px;">{{ nodeIp }}</span>
</div>
<div class="wrap-row">
<div>显卡数量<span>{{ gpuNum }}</span></div>
<div>显卡大小<span>{{ bytesToGB(gpuMemory) }}GB</span></div>
<div>内存大小<span>{{ bytesToGB(totalMemory) }}GB</span></div>
<div>磁盘大小<span>{{ bytesToGB(diskSize) }}GB</span></div>
<div>CPU<span>{{ cpuCores }}</span></div>
</div>
</div>
</div>
<div class="wrap-center">
</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))"
:key="nodeIp">
<div style="display: flex; align-items: center;">
<el-icon :size="16" color="red" style="cursor: pointer;" @click="handleCheckboxChange(nodeIp)">
<Remove />
</el-icon>
<span style="color: #0B1524; margin-left: 6px;">{{ nodeIp }}</span>
</div>
<div class="wrap-row" style="margin-top: 6px;">
<div>显卡数量<span>{{ gpuNum }}</span></div>
<div>显卡大小<span>{{ bytesToGB(gpuMemory) }}GB</span></div>
<div>内存大小<span>{{ bytesToGB(totalMemory) }}GB</span></div>
<div>磁盘大小<span>{{ bytesToGB(diskSize) }}GB</span></div>
<div>CPU<span>{{ cpuCores }}</span></div>
</div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="jsx">
import pollApi from '~/vgpu/api/poll';
import { ref, onMounted } from 'vue';
import BlockBox from '@/components/BlockBox.vue';
import { Remove } from '@element-plus/icons-vue'
const list = ref([])
const dialogVisible = ref(false)
const editData = ref(null)
const nodeList = ref([])
const nodeSelect = ref([])
const input = ref('')
const bytesToGB = (bytes) => {
return Math.round(bytes / (1024 * 1024 * 1024));
}
const handleCheckboxChange = (ip) => {
const index = nodeSelect.value.indexOf(ip);
if (index > -1) {
nodeSelect.value.splice(index, 1);
} else {
nodeSelect.value.push(ip);
}
}
onMounted(async () => {
console.log(111)
// list.value = await pollApi.getPollList({});
list.value = [
{
"poolId": "4",
"poolName": "master资源池",
"cpuCores": "96",
"nodeNum": "1",
"gpuNum": "0",
"availableMemory": "112892563456",
"totalMemory": "134472257536",
"diskSize": "7676310884352",
},
{
"poolId": "5",
"poolName": "worker资源池",
"cpuCores": "80",
"nodeNum": "1",
"gpuNum": "1",
"availableMemory": "134327394304",
"totalMemory": "134432251904",
"diskSize": "7675682045952",
}
]
nodeList.value = [
{
"nodeName": "k8s1",
"cpuCores": "96",
"gpuNum": "0",
"gpuMemory": "0",
"totalMemory": "134472257536",
"diskSize": "7676310884352",
"nodeIp": "172.16.100.14"
},
{
"nodeName": "k8s2",
"cpuCores": "80",
"gpuNum": "1",
"gpuMemory": "25769803776",
"totalMemory": "134432251904",
"diskSize": "7675682045952",
"nodeIp": "172.16.100.15"
}
]
});
</script>
<style scoped lang="scss">
.left {
flex: 1;
}
.right {
width: 170px;
display: flex;
justify-content: end;
}
.title {
color: #3D4F62;
font-size: 18px;
}
.tags {
display: flex;
gap: 60px;
margin-top: 10px;
span {
font-size: 14px;
color: #3D4F62;
}
}
.wrap {
display: flex;
align-items: center;
}
.wrap-left,
.wrap-right {
flex: 1;
height: 320px;
background: #F6F7F9;
border-radius: 4px;
overflow: auto;
padding: 0 14px 14px 14px;
.wrap-row {
display: flex;
gap: 18px;
font-size: 12px;
margin-left: 23px;
div {
color: #5A6B7D;
span {
color: #0B1524;
margin-left: 4px;
}
}
}
}
.wrap-center {
width: 60px;
flex-shrink: 0;
}
</style>

@ -0,0 +1,40 @@
import api from '~/vgpu/api/card';
export default {
items: [
{
label: 'IP',
name: 'ip',
component: 'input',
},
{
label: '节点状态',
name: 'isSchedulable',
component: 'select',
props: {
mode: 'static',
options: [
{
label: '可调度',
value: 'true',
},
{
label: '禁止调度',
value: 'false',
},
],
},
},
{
label: '显卡型号',
name: 'type',
component: 'select',
props: {
mode: 'remote',
api: api.getCardType(),
labelKey: 'type',
valueKey: 'type',
},
},
],
};

@ -0,0 +1,3 @@
<template>
<router-view></router-view>
</template>

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>CPU</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="0801图标" transform="translate(-158.000000, -119.000000)">
<g id="CPU" transform="translate(158.000000, 119.000000)">
<g id="编组" transform="translate(1.000000, 1.000000)" fill-rule="nonzero">
<path d="M14,4.2 L14,4.2 C14,4.6 13.8,4.8 13.4,4.8 L11.4,4.8 C11,4.8 10.8,4.6 10.8,4.2 L10.8,4.2 C10.8,3.8 11,3.6 11.4,3.6 L13.4,3.6 C13.8,3.6 14,3.8 14,4.2 Z" id="路径" fill="#324558"></path>
<path d="M3.2,4.2 L3.2,4.2 C3.2,4.6 3,4.8 2.60000001,4.8 L0.599999996,4.8 C0.199999999,4.8 -2.48689958e-16,4.6 -2.48689958e-16,4.2 L-2.48689958e-16,4.2 C-2.48689958e-16,3.8 0.199999999,3.6 0.599999996,3.6 L2.60000001,3.6 C3,3.6 3.2,3.8 3.2,4.2 Z" id="路径" fill="#324558"></path>
<path d="M14,7 L14,7 C14,7.4 13.8,7.6 13.4,7.6 L11.4,7.6 C11,7.6 10.8,7.4 10.8,7 L10.8,7 C10.8,6.6 11,6.4 11.4,6.4 L13.4,6.4 C13.8,6.4 14,6.6 14,7 Z" id="路径" fill="#324558"></path>
<path d="M3.2,7 L3.2,7 C3.2,7.4 3,7.6 2.60000001,7.6 L0.599999996,7.6 C0.199999999,7.6 -2.48689958e-16,7.4 -2.48689958e-16,7 L-2.48689958e-16,7 C-2.48689958e-16,6.6 0.199999999,6.4 0.599999996,6.4 L2.60000001,6.4 C3,6.4 3.2,6.6 3.2,7 Z" id="路径" fill="#324558"></path>
<path d="M14,9.8 L14,9.8 C14,10.2 13.8,10.4 13.4,10.4 L11.4,10.4 C11,10.4 10.8,10.2 10.8,9.8 L10.8,9.8 C10.8,9.4 11,9.2 11.4,9.2 L13.4,9.2 C13.8,9.2 14,9.4 14,9.8 Z" id="路径" fill="#324558"></path>
<path d="M3.2,9.8 L3.2,9.8 C3.2,10.2 3,10.4 2.60000001,10.4 L0.599999996,10.4 C0.199999999,10.4 -2.48689958e-16,10.2 -2.48689958e-16,9.8 L-2.48689958e-16,9.8 C-2.48689958e-16,9.4 0.199999999,9.2 0.599999996,9.2 L2.60000001,9.2 C3,9.2 3.2,9.4 3.2,9.8 Z" id="路径" fill="#324558"></path>
<path d="M4.2,0 L4.2,0 C4.6,0 4.8,0.199999999 4.8,0.599999996 L4.8,2.60000001 C4.8,3 4.6,3.2 4.2,3.2 L4.2,3.2 C3.8,3.2 3.6,3 3.6,2.60000001 L3.6,0.599999996 C3.6,0.199999999 3.8,0 4.2,0 Z" id="路径" fill="#324558"></path>
<path d="M4.2,10.8 L4.2,10.8 C4.6,10.8 4.8,11 4.8,11.4 L4.8,13.4 C4.8,13.8 4.6,14 4.2,14 L4.2,14 C3.8,14 3.6,13.8 3.6,13.4 L3.6,11.4 C3.6,11 3.8,10.8 4.2,10.8 Z" id="路径" fill="#324558"></path>
<path d="M7,0 L7,0 C7.4,0 7.6,0.199999999 7.6,0.599999996 L7.6,2.60000001 C7.6,3 7.4,3.2 7,3.2 L7,3.2 C6.6,3.2 6.4,3 6.4,2.60000001 L6.4,0.599999996 C6.4,0.199999999 6.6,0 7,0 Z" id="路径" fill="#324558"></path>
<path d="M7,10.8 L7,10.8 C7.4,10.8 7.6,11 7.6,11.4 L7.6,13.4 C7.6,13.8 7.4,14 7,14 L7,14 C6.6,14 6.4,13.8 6.4,13.4 L6.4,11.4 C6.4,11 6.6,10.8 7,10.8 Z" id="路径" fill="#324558"></path>
<path d="M9.8,0 L9.8,0 C10.2,0 10.4,0.199999999 10.4,0.599999996 L10.4,2.60000001 C10.4,3 10.2,3.2 9.8,3.2 L9.8,3.2 C9.4,3.2 9.2,3 9.2,2.60000001 L9.2,0.599999996 C9.2,0.199999999 9.4,0 9.8,0 Z" id="路径" fill="#324558"></path>
<path d="M9.8,10.8 L9.8,10.8 C10.2,10.8 10.4,11 10.4,11.4 L10.4,13.4 C10.4,13.8 10.2,14 9.8,14 L9.8,14 C9.4,14 9.2,13.8 9.2,13.4 L9.2,11.4 C9.2,11 9.4,10.8 9.8,10.8 Z" id="路径" fill="#324558"></path>
<path d="M2.8,2 L11.2,2 C11.7333333,2 12,2.26666666 12,2.8 L12,11.2 C12,11.7333333 11.7333333,12 11.2,12 L2.8,12 C2.26666666,12 2,11.7333333 2,11.2 L2,2.8 C2,2.26666666 2.26666666,2 2.8,2 Z" id="路径" fill="#B6C2CD"></path>
<path d="M4.39999999,4.00000001 L9.60000001,4.00000001 C9.86666667,4.00000001 10,4.13333334 10,4.39999999 L10,9.60000001 C10,9.86666667 9.86666666,10 9.60000001,10 L4.39999999,10 C4.13333333,10 4.00000001,9.86666666 4.00000001,9.60000001 L4.00000001,4.39999999 C4.00000001,4.13333333 4.13333334,4.00000001 4.39999999,4.00000001 Z" id="路径" fill="#324558"></path>
</g>
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>磁盘</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="0801图标" transform="translate(-129.000000, -119.000000)">
<g id="磁盘" transform="translate(129.000000, 119.000000)">
<g id="编组" transform="translate(2.000000, 2.000000)" fill-rule="nonzero">
<path d="M7.8,3.6 C7.8,4.01538462 7.24615385,4.8 6,4.8 C4.75384615,4.8 4.2,4.01538462 4.2,3.6 C4.2,3.18461538 4.75384615,2.4 6,2.4 C7.24615385,2.4 7.8,3.18461538 7.8,3.6 Z M2.44615385,0 C1.89230769,0 1.38461538,0.415384615 1.24615385,0.969230769 L0,7.06153846 L0,10.8 C0,11.4461538 0.553846154,12 1.2,12 L10.8,12 C11.4461538,12 12,11.4461538 12,10.8 L12,7.2 L12,7.06153846 L10.7538462,0.969230769 C10.6153846,0.415384615 10.1076923,0 9.55384615,0 L2.44615385,0 Z M1.2,8.4 L10.8,8.4 L10.8,10.8 L1.2,10.8 L1.2,8.4 Z" id="形状" fill="#B6C2CD"></path>
<polygon id="路径" fill="#324558" points="7.2 9 9.6 9 9.6 10.2 7.2 10.2"></polygon>
<path d="M9,3.6 C9,4.93846154 7.66153846,6 6,6 C4.33846154,6 3,4.93846154 3,3.6 C3,2.26153846 4.33846154,1.2 6,1.2 C7.66153846,1.2 9,2.26153846 9,3.6 L9,3.6 Z" id="路径" fill="#324558"></path>
</g>
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>内存</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="0801图标" transform="translate(-126.000000, -89.000000)">
<g id="内存" transform="translate(126.000000, 89.000000)">
<g id="编组" transform="translate(0.000000, 4.000000)" fill-rule="nonzero">
<polygon id="路径" fill="#B6C2CD" points="15.4105263 1.74376645 15.9442105 1.74376645 15.9442105 0.351842105 0.00973684211 0.351842105 0.00973684211 1.74376645 0.574440789 1.74376645 0.574440789 3.60355263 0.00973684211 3.60355263 0.00973684211 7.38580592 1.44445724 7.38580592 1.44445724 6.48133224 1.74748355 6.10506579 2.17620066 6.10506579 2.47935855 6.48133224 2.47935855 7.38580592 3.16615132 7.38580592 3.16615132 6.48133224 3.46924342 6.10506579 3.89796053 6.10506579 4.20111842 6.48133224 4.20111842 7.38580592 4.88784539 7.38580592 4.88784539 6.48133224 5.19101974 6.10506579 5.61972039 6.10506579 5.92282895 6.48133224 5.92282895 7.38580592 6.60976974 7.38580592 6.60976974 6.48133224 6.91286184 6.10506579 7.3415625 6.10506579 7.64467105 6.48133224 7.64467105 7.38580592 8.33144737 7.38580592 8.33144737 6.48133224 8.63462171 6.10506579 9.06327303 6.10506579 9.36643092 6.48133224 9.36643092 7.38580592 10.0532895 7.38580592 10.0532895 6.48133224 10.356398 6.10506579 10.7850987 6.10506579 11.088273 6.48133224 11.088273 7.38580592 11.7750658 7.38580592 11.7750658 6.48133224 12.0782237 6.10506579 12.506875 6.10506579 12.8100329 6.48133224 12.8100329 7.38580592 13.4968257 7.38580592 13.4968257 6.48133224 13.8 6.10506579 14.2286349 6.10506579 14.5318092 6.48133224 14.5318092 7.38580592 15.9442105 7.38580592 15.9442105 3.60355263 15.4105263 3.60355263"></polygon>
<polygon id="路径" fill="#FFFFFF" points="12.1547039 1.91907895 14.1579934 1.91907895 14.1579934 4.92402961 12.1547039 4.92402961"></polygon>
<polygon id="路径" fill="#324558" points="12.3891447 2.15847039 13.9236349 2.15847039 13.9236349 4.68453947 12.3891447 4.68453947"></polygon>
<polygon id="路径" fill="#FFFFFF" points="9.56504934 1.91907895 11.5682566 1.91907895 11.5682566 4.92402961 9.56504934 4.92402961"></polygon>
<polygon id="路径" fill="#324558" points="9.79947368 2.15847039 11.333898 2.15847039 11.333898 4.68453947 9.79947368 4.68453947"></polygon>
<polygon id="路径" fill="#FFFFFF" points="6.97532895 1.91907895 8.97860197 1.91907895 8.97860197 4.92402961 6.97532895 4.92402961"></polygon>
<polygon id="路径" fill="#324558" points="7.20975329 2.15847039 8.74409539 2.15847039 8.74409539 4.68453947 7.20975329 4.68453947"></polygon>
<polygon id="路径" fill="#FFFFFF" points="4.38569079 1.91907895 6.38888158 1.91907895 6.38888158 4.92402961 4.38569079 4.92402961"></polygon>
<polygon id="路径" fill="#324558" points="4.62004934 2.15847039 6.15452303 2.15847039 6.15452303 4.68453947 4.62004934 4.68453947"></polygon>
<polygon id="路径" fill="#FFFFFF" points="1.79588816 1.91907895 3.79922697 1.91907895 3.79922697 4.92402961 1.79588816 4.92402961"></polygon>
<polygon id="路径" fill="#324558" points="2.03046053 2.15847039 3.56480263 2.15847039 3.56480263 4.68453947 2.03046053 4.68453947"></polygon>
<polygon id="路径" fill="#324558" points="0.00973684211 0.0122697368 15.9442105 0.0122697368 15.9442105 1.28539474 0.00973684211 1.28539474"></polygon>
</g>
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>资源池</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="0801图标" transform="translate(-158.000000, -92.000000)">
<g id="资源池" transform="translate(158.000000, 92.000000)">
<g transform="translate(1.000000, 1.000000)" fill-rule="nonzero">
<path d="M7.20239646,0.0609577812 L9.91061465,1.56738236 C10.0590515,1.64888669 10.1516425,1.80445034 10.152507,1.97378936 L10.152507,4.96781693 C10.1515387,5.13694715 10.0589413,5.29225462 9.91061465,5.37352684 L7.20239646,6.87995141 C7.05823735,6.95998771 6.8829867,6.95998771 6.73882758,6.87995141 L4.0313065,5.37352684 C3.88324905,5.29207209 3.79093938,5.13679977 3.79011126,4.96781693 L3.79011126,1.97378936 C3.79011126,1.80509212 3.88282504,1.64963969 4.0313065,1.56738236 L6.73952468,0.0602606817 C6.8836838,-0.0197756137 7.05893445,-0.0197756137 7.20309356,0.0602606817 L7.20239646,0.0609577812 Z M3.4129823,6.18982631 L6.12259467,7.69694799 C6.27005967,7.77872779 6.36158922,7.9340345 6.36169861,8.10265789 L6.36169861,11.0966855 C6.36169861,11.2639885 6.26968194,11.4194409 6.12259467,11.5010012 L3.4129823,13.0102141 C3.26882319,13.0902504 3.09357254,13.0902504 2.94941342,13.0102141 L0.240498137,11.5030925 C0.0921661659,11.4213246 0,11.2653648 0,11.0959884 L0,8.10126371 C0,7.93326355 0.0913195896,7.77850824 0.240498137,7.6962509 L2.94941342,6.18843213 C3.09357254,6.10839583 3.26882319,6.10839583 3.4129823,6.18843213 L3.4129823,6.18912922 L3.4129823,6.18982631 Z M10.9925077,6.18982631 L13.701423,7.69694799 C13.8492074,7.77850822 13.9419211,7.93396065 13.9419211,8.10126371 L13.9419211,11.0966855 C13.9419211,11.2639885 13.8492074,11.4194409 13.7021201,11.5010012 L10.9932048,13.0102141 C10.8490457,13.0902504 10.6737951,13.0902504 10.5296359,13.0102141 L7.82072067,11.5030925 C7.67238869,11.4213246 7.58022252,11.2653648 7.58022252,11.0959884 L7.58022252,8.10126371 C7.58022252,7.93326355 7.67154211,7.77850824 7.82072067,7.6962509 L10.5296359,6.18843213 C10.6736138,6.10865359 10.8485298,6.10865359 10.9925077,6.18843213 L10.9925077,6.18912922 L10.9925077,6.18982631 Z" id="形状" fill="#B6C2CD"></path>
<path d="M4.88803756,1.95148228 C4.83118916,1.98395572 4.79668641,2.04495065 4.79814093,2.11040406 C4.79959545,2.17585747 4.83677368,2.23525948 4.89500851,2.26517552 L6.88870323,3.27387351 C6.94089577,3.30010442 7.00241956,3.30010442 7.0546121,3.27387351 L9.0503981,2.2665697 C9.10970885,2.23701629 9.1477603,2.17703183 9.14922277,2.11078207 C9.15068523,2.04453231 9.11531789,1.98292712 9.05736907,1.9507852 L7.06367434,0.820095395 C7.00752096,0.788003954 6.93858275,0.788003954 6.88242937,0.820095395 L4.88873464,1.95287648 L4.88873464,1.95148228 L4.88803756,1.95148228 Z" id="路径" fill="#324558"></path>
<path d="M3.03585333,9.43411137 C3.08785561,9.46010933 3.14906282,9.46010933 3.2010651,9.43411137 L5.19475982,8.42680756 C5.25393207,8.39753134 5.29210732,8.33797924 5.2940053,8.27198794 C5.29590328,8.20599665 5.26121387,8.14434888 5.20382207,8.11172015 L3.20873316,6.98103035 C3.15254256,6.94907931 3.08367877,6.94907931 3.02748817,6.98103035 L1.03379346,8.11311433 C0.97670752,8.14572866 0.942177837,8.20708948 0.94392655,8.27281192 C0.945675263,8.33853437 0.9834191,8.39797217 1.04215861,8.42750466 L3.03585333,9.43480846 L3.03585333,9.43411137 Z" id="路径" fill="#324558"></path>
<path d="M10.4536525,9.4292317 C10.504414,9.45846441 10.5661168,9.46157547 10.6195613,9.43759684 L12.8795468,8.42192789 C12.9415278,8.39458359 12.9826814,8.33450214 12.9857844,8.26682844 C12.9888873,8.19915473 12.9534044,8.13555939 12.8941858,8.10265789 L10.9165243,6.97893906 C10.8605494,6.94711253 10.7919513,6.94711253 10.7359764,6.97893906 L8.72346006,8.12217659 C8.66823793,8.15384209 8.63406431,8.21252547 8.6337751,8.27618162 C8.63348954,8.33983777 8.6671319,8.39882731 8.72206586,8.43099014 L10.4536525,9.4292317 L10.4536525,9.4292317 Z" id="路径" fill="#324558"></path>
</g>
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

@ -1,5 +1,5 @@
<template>
<div id="content" :style="{ paddingLeft: isHome ? 0 : '20px' }">
<div id="content" :style="{ paddingLeft: hasParentWindow || isHome ? 0 : '20px' }">
<!-- <transition name="fade-transform" mode="out-in"> -->
<!-- <keep-alive> -->
<router-view />
@ -11,6 +11,10 @@
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import useParentAction from '~/vgpu/hooks/useParentAction';
const { hasParentWindow } = useParentAction();
const route = useRoute();

@ -1,7 +1,7 @@
<template>
<TopBar />
<div class="page">
<sidebar v-if="!isNoSidebar()" />
<TopBar v-if="!hasParentWindow" />
<div class="page" :style="{ padding: hasParentWindow ? '20px 30px' : '76px 20px 20px 20px' }">
<sidebar v-if="!hasParentWindow && !isNoSidebar()" />
<app-main />
</div>
</template>
@ -9,6 +9,9 @@
<script>
import '@tabler/core/dist/css/tabler.min.css';
import { AppMain, Sidebar, TopBar } from './components';
import { useRouter } from 'vue-router'
import useParentAction from '~/vgpu/hooks/useParentAction';
export default {
name: 'Layout',
@ -17,13 +20,48 @@ export default {
Sidebar,
TopBar,
},
setup() {
const router = useRouter();
const { hasParentWindow } = useParentAction();
return { router, hasParentWindow };
},
created() {
this.isNoSidebar();
this.setupMessageListener();
},
beforeDestroy() {
window.removeEventListener('message', this.handleMessage);
},
mounted() {
this.sendLoadedMessage();
},
methods: {
isNoSidebar() {
const noSidebarPaths = ['/admin/home', '/admin/message-center', "/admin/about-system", "/admin/settings/config-map"];
return noSidebarPaths.includes(this.$route.fullPath);
},
setupMessageListener() {
window.addEventListener('message', this.handleMessage);
},
handleMessage(event) {
try {
const messageData = JSON.parse(event.data);
if (messageData.type === "ChangeTheRoute") {
this.router.push({
path: messageData.data,
});
}
} catch (e) { }
},
sendLoadedMessage() {
if (window.parent !== window) {
const message = {
type: 'ComponentLoaded',
data: window.location.pathname
};
window.parent.postMessage(JSON.stringify(message), '*');
}
}
},
};
@ -36,8 +74,9 @@ export default {
.page {
display: flex;
flex-direction: row;
padding: 20px;
padding-top: 76px;
// padding: 20px 30px;
// padding: 20px;
// padding-top: 76px;
height: calc(100vh);
}

@ -63,5 +63,12 @@ module.exports = defineConfig({
logging: 'info',
webSocketURL: `ws://0.0.0.0:${port}/ws`,
},
proxy: {
'/api/vgpu': {
target: 'http://172.16.100.14:29999',
// target: 'http://192.168.2.99:8000',
changeOrigin: true,
},
}
},
});

Loading…
Cancel
Save