态势感知平台-全球开源风险态势感知监控中心高危漏洞图组件抽离

This commit is contained in:
d266377
2026-01-27 14:40:47 +08:00
parent 75085051ff
commit d0be493658

View File

@@ -0,0 +1,196 @@
<template>
<div class="index-module__NV_5cW__chartCard mb-[1.5rem]">
<h3 class="chartCard__title">漏洞等级分布</h3>
<div class="chartCard__container">
<div class="num-empty" v-if="isEmpty.vulnerabilitySeverity"></div>
<div v-else id="vulnerabilitySeverityChart" class="chart"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, nextTick, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
// 空数据标识
const isEmpty = ref({
vulnerabilitySeverity: false
});
// 图表实例存储用于resize时销毁重绘
const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({
vulnerabilitySeverityChart: null
});
// 漏洞等级分布数据
const vulnerabilitySeverityData = {
categories: ['2024Q1', '2024Q2', '2024Q3', '2024Q4', '2025Q1', '2025Q2', '2025Q3', '2025Q4'],
series: [
{
name: '高危漏洞',
type: 'bar',
stack: 'total',
itemStyle: { color: '#FF4D4F' },
barWidth: 16,
data: [35, 42, 58, 45, 67, 78, 62, 85, 92]
},
{
name: '中危漏洞',
type: 'bar',
stack: 'total',
itemStyle: { color: '#FAAD14' },
barWidth: 16,
data: [65, 72, 88, 95, 112, 105, 120, 135, 128]
},
{
name: '低危漏洞',
type: 'bar',
stack: 'total',
itemStyle: { color: '#36CFC9' },
barWidth: 16,
data: [120, 135, 150, 142, 165, 180, 175, 190, 210]
}
]
};
// 初始化漏洞等级分布图表
const initVulnerabilitySeverityChart = () => {
const el = document.getElementById('vulnerabilitySeverityChart');
if (!el) return;
// 延迟执行以确保DOM已渲染
setTimeout(() => {
const chartEl = document.getElementById('vulnerabilitySeverityChart');
if (!chartEl) return;
if (chartInstances.value.vulnerabilitySeverityChart) {
chartInstances.value.vulnerabilitySeverityChart.dispose();
}
const myChart = echarts.init(chartEl);
chartInstances.value.vulnerabilitySeverityChart = myChart;
myChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params: any) => {
let res = `<div>${params[0].name}</div>`;
let total = 0;
params.forEach((p: any) => {
total += p.value;
});
params.forEach((p: any) => {
const percentage = ((p.value / total) * 100).toFixed(1);
res += `<div style="display:flex;align-items:center;"><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${p.color};"></span>${p.seriesName}: ${p.value} (${percentage}%)</div>`;
});
return res;
}
},
legend: {
top: 0,
textStyle: { fontSize: 12 },
data: vulnerabilitySeverityData.series.map(item => item.name)
},
grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true },
xAxis: [
{
type: 'category',
data: vulnerabilitySeverityData.categories,
axisLabel: { fontSize: 12 }
}
],
yAxis: [
{
type: 'value',
name: '漏洞数量',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
}
],
series: vulnerabilitySeverityData.series
});
// 监听窗口大小变化,重新调整图表大小
window.addEventListener('resize', () => {
if (chartInstances.value.vulnerabilitySeverityChart) {
chartInstances.value.vulnerabilitySeverityChart.resize();
}
});
}, 0);
};
// 在组件挂载后初始化图表
onMounted(() => {
nextTick(() => {
initVulnerabilitySeverityChart();
});
});
// 在组件卸载前清理资源
onUnmounted(() => {
if (chartInstances.value.vulnerabilitySeverityChart) {
chartInstances.value.vulnerabilitySeverityChart.dispose();
}
});
defineExpose({
initVulnerabilitySeverityChart
});
</script>
<style scoped>
.index-module__NV_5cW__chartCard {
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background: #ffffffe6;
border: 1px solid #ffffff4d;
border-radius: 1rem;
padding: 1.5rem;
box-shadow: 0 4px 16px #00000014;
transition: all 0.3s;
}
.index-module__NV_5cW__chartCard:hover {
border-color: #3b82f64d;
transform: translateY(-4px);
box-shadow: 0 8px 24px #0000001f;
}
.chartCard__title {
color: #333;
font-size: 1.1rem;
font-weight: 600;
margin: 0 0 1rem 0;
text-align: left;
}
.chartCard__container {
width: 100%;
height: 300px; /* 图表高度 */
position: relative;
}
.mb-\[1\.5rem\] {
margin-bottom: 1.5rem;
}
/* 与示例一致的空数据样式 */
.num-empty {
height: 10px;
border-top: 2px solid #2951E0;
width: 100%;
background: linear-gradient(180deg, rgba(41, 81, 224, 0.12) 0%, rgba(41, 81, 224, 0.04) 100%);
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
}
.chart {
width: 100%;
height: 100%;
}
</style>