k8s管理系统项目前端-summary

<template>

  <div class="demo-collapse">
    <el-collapse v-model="activeNames" @change="handleChange">
      <el-collapse-item title="集群资源" name="1">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-card class="box-card" shadow="hover">
              <div ref="echartsRefNode" class="echarts-container"></div>
            </el-card>
          </el-col>
          <el-col :span="8">
            <el-card class="box-card" shadow="hover">
              <div ref="echartsRefPod" class="echarts-container"></div>
            </el-card>
          </el-col>
          <el-col :span="8">
            <el-card class="box-card" shadow="hover">
              <div ref="echartsRefDeployment" class="echarts-container"></div>
            </el-card>
          </el-col>
        </el-row>
      </el-collapse-item>
      <el-collapse-item title="节点资源" name="2">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-card class="box-card" shadow="hover">
              <div ref="echartsRefCpu" class="echarts-container"></div>
            </el-card>
          </el-col>
          <el-col :span="8">
            <el-card class="box-card" shadow="hover">
              <div ref="echartsRefMemory" class="echarts-container"></div>
            </el-card>
          </el-col>
        </el-row>
      </el-collapse-item>
      <el-collapse-item title="资源统计" name="3">
        <el-row :gutter="20">
          <el-col :span="24">
            <el-card class="box-cardnode" shadow="hover">
              <div ref="echartsRefNamespacePodCount" class="echarts-container"></div>
            </el-card>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-card class="box-cardnode" shadow="hover">
              <div ref="echartsRefNamespaceDeploymentCount" class="echarts-container"></div>
            </el-card>
          </el-col>
        </el-row>
      </el-collapse-item>
    </el-collapse>
  </div>
</template>

<script setup>
import { nextTick, onBeforeMount, reactive, ref, watch} from 'vue'
import * as echarts from 'echarts';
import {DEPLOYMENT_LIST, GET_NS_DEPLOYMENT_COUNT, GET_NS_PODS_COUNT, NODE_LIST, POD_LIST} from "../../../api/k8s.js";
const activeNames = ref(['1','2','3'])
const handleChange = (val) => {
  console.log(val)
}

const echartsRefNode = ref(null);
const echartsRefPod = ref(null);
const echartsRefDeployment = ref(null);
const echartsRefCpu = ref(null);
const echartsRefMemory = ref(null);
const echartsRefNamespacePodCount = ref(null);
const echartsRefNamespaceDeploymentCount = ref(null);

// const echartsRefDeployment = ref(null);
let myCharts = [null,null,null];

// 初始化ECharts图表
const initChart = (echartsRef,index,optionData) => {
  if (echartsRef.value) {
    let chartInstance = myCharts[index];
    if (!chartInstance) {
      chartInstance = echarts.init(echartsRef.value);
      myCharts[index] = chartInstance; // 将图表实例保存在数组中
    }
    chartInstance.setOption(optionData);
    myCharts[index] = chartInstance; // 将图表实例保存在数组中

  }
};

// 获取node信息
// 假设这些是从API或其他数据源中获取的值
let readyValue = ref(0);
let notReadyValue = ref(0);


const nodeParameters = reactive({
  page_size: 200,
  page_number: 1,
  isMaster: 3,
})
let nodeCpuAllocatable=  ref(0)
    //cpu总量
    let nodeCpuCapacity = ref(0)
    //内存可分配
    let nodeMemAllocatable = ref(0)
    //内存总量
    let nodeMemCapacity = ref(0)
  var nodeCount = ref(0)
const getNodeData = async ()=>{

  try {
    const resp = await NODE_LIST(nodeParameters)
    for (const respKey of resp.data.Items) {
          console.log(respKey)
      //计算node的cpu mem和pod的可分配及总容量数据
      nodeCpuAllocatable.value = parseInt(respKey.status.allocatable.cpu) + nodeCpuAllocatable.value
      nodeCpuCapacity.value = parseInt(respKey.status.capacity.cpu) + nodeCpuCapacity.value
      nodeMemAllocatable.value = parseInt(respKey.status.allocatable.memory) + nodeMemAllocatable.value
      nodeMemCapacity.value = parseInt(respKey.status.capacity.memory) + nodeMemCapacity.value
      for (const status of respKey.status.conditions) {
          console.log(status)
        if (status.type == "Ready" && status.status =="True"){
          readyValue.value ++
        }
      }

    }
    nodeCount = resp.data.total
    notReadyValue.value = nodeCount - readyValue.value
    const nodeOption = {
      title: {
        text: '集群节点数量:'+nodeCount,
        left: 'center'
      },
      tooltip: {
        trigger: 'item'
      },
      legend: {
        orient: 'vertical',
        left: 'left'
      },
      series: [
        {
          name: '状态',
          type: 'pie',
          radius: '50%',
          data: [
            {
              value: readyValue.value,
              name: 'Ready',
              itemStyle: { color: '#92CC76' }  // 设置为绿色
            },
            {
              value: notReadyValue.value,
              name: 'NotReady',
              itemStyle: { color: '#E64A3E' }  // 设置为红色
            },
            // 其他状态数据
          ],
          emphasis: {
            itemStyle: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
          }
        }
      ]
    };
    initChart(echartsRefNode,0,nodeOption)
    updateNodeResourceCharts(); // 更新资源图表的函数

  }catch (e){
    console.log(e)
  }
}
let namespacePodCountData = ref()


let namespaceDeploymentCountData = ref()


const updateNodeResourceCharts = () => {
  // CPU资源饼图配置
  const cpuOption = {
    title: {
      text: 'CPU 资源',
      left: 'center'
    },
    tooltip: {
      trigger: 'item'
    },
    legend: {
      orient: 'vertical',
      left: 'left'
    },
    series: [
      {
        name: 'CPU资源',
        type: 'pie',
        radius: '50%',
        data: [
          { value: nodeCpuAllocatable.value, name: 'Allocatable' },
          { value: nodeCpuCapacity.value - nodeCpuAllocatable.value, name: 'Used' }
        ],
        emphasis: {
          itemStyle: {
            shadowBlur: 10,
            shadowOffsetX: 0,
            shadowColor: 'rgba(0, 0, 0, 0.5)'
          }
        }
      }
    ]
  };
  initChart(echartsRefCpu, 3, cpuOption); // 初始化CPU资源图表

  // 内存资源饼图配置
  const memoryOption = {
    title: {
      text: '内存资源',
      subtext: '单位: GiB', // 添加单位说明
      left: 'center'
    },
    tooltip: {
      trigger: 'item'
     },
    legend: {
      orient: 'vertical',
      left: 'left'
    },
    series: [
      {
        name: '内存资源',
        type: 'pie',
        radius: '50%',
        data: [
          { value: bytesToGiB(nodeMemAllocatable.value), name: 'Allocatable' },
          { value: bytesToGiB(nodeMemCapacity.value - nodeMemAllocatable.value), name: 'Used' }
        ],
        emphasis: {
          itemStyle: {
            shadowBlur: 10,
            shadowOffsetX: 0,
            shadowColor: 'rgba(0, 0, 0, 0.5)'
          }
        }
      }
    ]
  };
  initChart(echartsRefMemory, 4, memoryOption); // 初始化内存资源图表
};
const bytesToGiB = (bytes) => {
  let a = bytes / 1024 / 1024
  //四舍五入保留小数点0位,也就是去除小数点
  return a.toFixed(0)
};
const PodListParameters = reactive({
  page_size: 2000000,
  page_number: 1,
  fileName:"",
  namespace:"",
})
var PodCount = ref(0)
let RunningValue = ref(0);
let PendingValue = ref(0);
let FailedValue = ref(0);
let SucceededValue = ref(0);
const getPodListData = async ()=>{
  try {
    const resp = await POD_LIST(PodListParameters)
    console.log(resp.data.items)
    for (const respKey of resp.data.items) {
      // console.log(respKey)
      if (respKey.status.phase == "Running"){
        RunningValue.value ++
      }else if( respKey.status.phase =="Pending") {
        PendingValue.value ++
      }else if (respKey.status.phase =="Failed") {
        FailedValue.value ++
      }else if (respKey.status.phase == "Successed"){
        SucceededValue.value ++
      }
    }
    PodCount = resp.data.total
    const podOption = {
      title: {
        text: 'Pod数量:'+PodCount,
        left: 'center'
      },
      tooltip: {
        trigger: 'item'
      },
      legend: {
        orient: 'vertical',
        left: 'left'
      },
      series: [
        {
          name: '状态',
          type: 'pie',
          radius: '50%',
          data: [
            { value: RunningValue.value, name: 'Running',    itemStyle: { color: '#92CC76' }},
            { value: PendingValue.value, name: 'Pending',itemStyle: { color: '#EFA634' } },
            { value: FailedValue.value, name: 'Failed',itemStyle: { color: '#EF2D21' } },
            { value: SucceededValue.value, name: 'Succeeded',itemStyle: { color: '#2653EF' } }
          ],
          emphasis: {
            itemStyle: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
          }
        }
      ]
    };
    // initChart(echartsRefNode,0,)
    initChart(echartsRefPod,1,podOption)
    // initChart(echartsRefNs,2)

  }catch (e){
    console.log(e)
  }
}
const DeploymentListParameters = reactive({
  page_size: 2000000,
  page_number: 1,
  fileName:"",
  namespace:"",
})
var DeploymentCount = ref(0)
// 假设的Deployment状态数据处理
let availableValue = ref(0);
let progressingValue = ref(0);
let failureValue = ref(0);
const NSPodsCountParameters = reactive({
  namespace:"",
})
const getNSPodsCount = async ()=>{
  try {
    const resp = await GET_NS_PODS_COUNT(NSPodsCountParameters)
    namespacePodCountData.value = resp
    const option = {
      title: {
        text: '命名空间的 Pod 计数', // 标题文本
        left: 'center', // 标题的水平位置
        textStyle: {
          color: '#333', // 标题的字体颜色
          fontWeight: 'normal', // 标题的字体粗细
          fontSize: 16 // 标题的字体大小
        }
      },
      tooltip: {
        trigger: 'axis', // 触发类型:坐标轴触发
        axisPointer: {
          type: 'cross', // 十字准星指示器,显示标线
          crossStyle: {
            color: '#999',
          },
        }
      },
      xAxis: {
        type: 'category',
        data: namespacePodCountData.value.data.map(ns => ns.namespace),
        axisTick: {
          alignWithLabel: true // 刻度线和标签对齐
        }
      },
      yAxis: {
        type: 'value',
        axisLine: {
          show: false // 不显示y轴线
        },
        axisTick: {
          show: false // 不显示刻度
        },
        splitLine: { // 分割线
          lineStyle: { // 使用深浅的间隔色
            type: 'dashed'
          }
        }
      },
      grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true // 包含标签
      },
      series: [{
        data: namespacePodCountData.value.data.map(ns => {
          return {
            value: ns.podCount,
            label: {
              show: true,
              position: 'top',
              formatter: '{c}',
              color: '#000'
            }
          };
        }),
        type: 'bar',
        barWidth: '60%', // 柱图宽度
        showBackground: true,
        backgroundStyle: {
          color: 'rgba(222, 222, 222, 0.2)'
        },
        itemStyle: { // 定制展示(如柱形图的颜色)
          color: '#3398DB'
        }
      }]
    };
    initChart(echartsRefNamespacePodCount,5,option)
  }catch (e){
    console.log(e)
  }
}
const getNSDeploymentCount = async ()=>{
  try {
    const resp = await GET_NS_DEPLOYMENT_COUNT(NSPodsCountParameters)
    namespaceDeploymentCountData.value = resp
    const option = {
      title: {
        text: '命名空间的 Deployment 计数', // 标题文本
        left: 'center', // 标题的水平位置
        textStyle: {
          color: '#333', // 标题的字体颜色
          fontWeight: 'normal', // 标题的字体粗细
          fontSize: 16 // 标题的字体大小
        }
      },
      tooltip: {
        trigger: 'axis', // 触发类型:坐标轴触发
        axisPointer: {
          type: 'cross', // 十字准星指示器,显示标线
          crossStyle: {
            color: '#999',
          },
        }
      },
      xAxis: {
        type: 'category',
        data: namespaceDeploymentCountData.value.data.map(ns => ns.namespace),
        axisTick: {
          alignWithLabel: true // 刻度线和标签对齐
        }
      },
      yAxis: {
        type: 'value',
        axisLine: {
          show: false // 不显示y轴线
        },
        axisTick: {
          show: false // 不显示刻度
        },
        splitLine: { // 分割线
          lineStyle: { // 使用深浅的间隔色
            type: 'dashed'
          }
        }
      },
      grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        containLabel: true // 包含标签
      },
      series: [{
        data: namespaceDeploymentCountData.value.data.map(ns => {
          return {
            value: ns.deploymentCount,
            label: {
              show: true,
              position: 'top',
              formatter: '{c}',
              color: '#000'
            }
          };
        }),
        type: 'bar',
        barWidth: '60%', // 柱图宽度
        showBackground: true,
        backgroundStyle: {
          color: 'rgba(222, 222, 222, 0.2)'
        },
        itemStyle: { // 定制展示(如柱形图的颜色)
          color: '#3398DB'
        }
      }]
    };
    initChart(echartsRefNamespaceDeploymentCount,6,option)

  }catch (e){
    console.log(e)
  }
}
const getDeploymentListData = async ()=>{
  try {
    const resp = await DEPLOYMENT_LIST(DeploymentListParameters)
    console.log(resp)
    console.log(resp.data.Items)
    // 假设你的API返回数据结构包含了Deployment的状态信息
    for (const deployment of resp.data.Items) {
      if (deployment.status.availableReplicas === deployment.spec.replicas) {
        availableValue.value++;
      }
      if (deployment.status.unavailableReplicas > 0) {
        failureValue.value++;
      }
      // 你可以添加更多的条件检查来更新progressingValue等

      // 注意:你需要根据你的实际API响应格式来调整以上代码
    }
    DeploymentCount = resp.data.total
    const deploymentOption = {
      title: {
        text: 'Deployment数量:'+DeploymentCount,
        left: 'center'
      },
      tooltip: {
        trigger: 'item'
      },
      legend: {
        orient: 'vertical',
        left: 'left'
      },
      series: [
        {
          name: '状态',
          type: 'pie',
          radius: '50%',
          data: [
            { value: availableValue.value, name: 'available',    itemStyle: { color: '#92CC76' }},
            { value: failureValue.value, name: 'unavailable',itemStyle: { color: '#EFA634' } },
          ],
          emphasis: {
            itemStyle: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
          }
        }
      ]
    };
    // initChart(echartsRefNode,0,)
    initChart(echartsRefDeployment,2,deploymentOption)
    // initChart(echartsRefNs,2)

  }catch (e){
    console.log(e)
  }
}
// 使用 watchEffect 或 watch 监听 activeNames 的变化,并在相应的折叠面板打开时初始化图表
watch(activeNames, async (newActiveNames) => {
  if (newActiveNames.includes('1')) {
    // 使用 nextTick 确保 DOM 更新完成后再初始化图表
    nextTick(() => {
      initChart(echartsRefNode, 0);
      initChart(echartsRefPod, 1);
      initChart(echartsRefDeployment, 2);

    });
  }
  if (newActiveNames.includes('2')) {
    nextTick(() => {
      if (myCharts[3]) myCharts[3].resize();
      if (myCharts[4]) myCharts[4].resize();
    });
  }
  if (newActiveNames.includes('3')) {
    nextTick(() => {
      initChart(echartsRefNamespacePodCount, 5);
      initChart(echartsRefNamespaceDeploymentCount, 6);

    });
  }

});
 onBeforeMount(async ()=> {
  await Promise.all([getNodeData(),getPodListData(),getDeploymentListData(),getNSPodsCount(),getNSDeploymentCount()])

});

</script>

<style scoped>
.echarts-container {
  width: 100%;
  height: 300px; /* 或者您希望的高度 */
}

.box-card {
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

</style>