From 634726b75ef6c2934683c741cefd4c44d4a9e7c5 Mon Sep 17 00:00:00 2001 From: HeZhaohui Date: Thu, 18 Dec 2025 10:36:08 +0800 Subject: [PATCH] =?UTF-8?q?Tcode=E5=B9=B3=E5=8F=B0=E6=94=B9=E9=80=A0?= =?UTF-8?q?=E5=8D=87=E7=BA=A7-CVE=20=E6=9C=88=E5=BA=A6=E8=B6=8B=E5=8A=BF?= =?UTF-8?q?=E5=9B=BE=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Jyh/security/index.vue | 235 ++++++++++++++++++++++++++++++- 1 file changed, 230 insertions(+), 5 deletions(-) diff --git a/src/views/Jyh/security/index.vue b/src/views/Jyh/security/index.vue index 0edc961..494b93c 100644 --- a/src/views/Jyh/security/index.vue +++ b/src/views/Jyh/security/index.vue @@ -160,13 +160,40 @@ - + + +
+
+

CVE 月度趋势

+ +
+ + +
+
+
+
+
+
@@ -374,9 +401,25 @@ const chartData = ref({ industryColorPalette: [ '#00f0ff', '#00b03e', '#ffeb00', '#ffab00', '#ff5100', '#c0232a', '#a262f2', '#379bff' - ] + ], + // CVE图表数据 + cveData: { + // 全球CVE:2024年实际数据(基于Excel数据) + global: { + cveCount: [98402, 418, 7025, 3711, 5115, 3228, 3044, 2957, 2592, 1455, 4826, 4661], + acceleration: [0, -99.58, 1580.62, -47.17, 37.83, -36.89, -5.70, -2.86, -12.34, -43.87, 231.68, -3.42] // 环比增长率(%) + }, + // 中国区域CVE:2024年数据(约占全球30%) + regional: { + cveCount: [29521, 125, 2108, 1113, 1535, 968, 913, 887, 778, 437, 1448, 1398], + acceleration: [0, -99.58, 1580.62, -47.17, 37.83, -36.89, -5.70, -2.86, -12.34, -43.87, 231.68, -3.42] // 环比增长率(%) + } + } }); +// 视图切换状态变量(global: 全球, regional: 区域) +const cveView = ref('global'); + // 空数据标识 const isEmpty = ref({ university: false, @@ -385,7 +428,8 @@ const isEmpty = ref({ developer: false, languageTech: false, highRiskComponent: false, - industryDistribution: false + industryDistribution: false, + cveData: false }); // 图表实例存储(用于resize时销毁重绘) @@ -396,7 +440,8 @@ const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({ developerChart: null, languageTechChart: null, highRiskComponentRank: null, - cweAttackSurfaceRadar: null + cweAttackSurfaceRadar: null, + CVEChart: null }); // ===================== 新增:列表自动滚动逻辑 ===================== @@ -475,6 +520,145 @@ onUnmounted(() => { } }); }); + +// CVE视图切换方法 +const switchCveView = (view: 'global' | 'regional') => { + cveView.value = view; + nextTick(() => { + initCVEChart(); + }); +}; + +// 初始化CVE图表(柱状图+折线图) +const initCVEChart = () => { + const el = document.getElementById('CVEChart'); + if (!el) return; + + if (chartInstances.value.CVEChart) { + chartInstances.value.CVEChart.dispose(); + } + + const myChart = echarts.init(el); + chartInstances.value.CVEChart = myChart; + + // 获取当前视图的数据 + const currentData = chartData.value.cveData[cveView.value === 'global' ? 'global' : 'regional']; + const cveCount = currentData.cveCount; + const acceleration = currentData.acceleration; + + // 月份数据 + const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']; + + myChart.setOption({ + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + crossStyle: { + color: '#999' + } + }, + formatter: (params: any) => { + let res = `
${params[0].name}
`; + params.forEach((p: any) => { + if (p.seriesName === 'CVE数量') { + res += `
${p.marker}${p.seriesName}: ${p.value}
`; + } else { + res += `
${p.marker}${p.seriesName}: ${p.value}%
`; + } + }); + return res; + } + }, + legend: { + top: 0, + textStyle: { fontSize: 12 }, + data: ['CVE数量', '环比增长率'] + }, + grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true }, + xAxis: [ + { + type: 'category', + data: months, + axisPointer: { + type: 'shadow' + }, + axisLabel: { fontSize: 12 } + } + ], + yAxis: [ + { + type: 'value', + name: 'CVE数量', + nameTextStyle: { fontSize: 12, color: '#333' }, + axisLabel: { fontSize: 12, color: '#666' }, + position: 'left', + splitLine: { + show: true, + lineStyle: { + type: 'dashed' + } + } + }, + { + type: 'value', + name: '环比增长率(%)', + nameTextStyle: { fontSize: 12, color: '#333' }, + axisLabel: { fontSize: 12, color: '#666' }, + position: 'right', + splitLine: { + show: false + } + } + ], + series: [ + { + name: 'CVE数量', + type: 'bar', + data: cveCount, + itemStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: '#3b82f6' }, + { offset: 1, color: '#60a5fa' } + ]) + }, + barWidth: 20, + label: { + show: true, + position: 'top', + formatter: '{c}', + fontSize: 10, + color: '#333' + } + }, + { + name: '环比增长率', + type: 'line', + yAxisIndex: 1, + data: acceleration, + itemStyle: { + color: '#ef4444', + borderWidth: 2 + }, + lineStyle: { + width: 2, + color: '#ef4444' + }, + symbol: 'circle', + symbolSize: 6, + smooth: true, + label: { + show: true, + position: 'top', + formatter: (params: any) => `${params.value}%`, + fontSize: 10, + color: '#333' + } + } + ] + }); +}; + // ===================== 新增结束 ===================== // 初始化高校参与分布饼图 @@ -870,7 +1054,9 @@ const initCharts = () => { developer: chartData.value.developer.data.every(item => item === 0), languageTech: chartData.value.languageTech.series.every(s => s.data.every(d => d === 0)), highRiskComponent: chartData.value.highRiskComponent.length === 0, - industryDistribution: chartData.value.industryDistribution.length === 0 + industryDistribution: chartData.value.industryDistribution.length === 0, + cveData: chartData.value.cveData.global.cveCount.every(item => item === 0) && + chartData.value.cveData.regional.cveCount.every(item => item === 0) }; // 初始化各图表 @@ -881,6 +1067,7 @@ const initCharts = () => { if (!isEmpty.value.languageTech) nextTick(() => initLanguageTechChart()); if (!isEmpty.value.highRiskComponent) nextTick(() => initHighRiskComponentChart()); if (!isEmpty.value.industryDistribution) nextTick(() => initIndustryDistributionChart()); + if (!isEmpty.value.cveData) nextTick(() => initCVEChart()); }; // 页面resize时重绘图表 @@ -1489,4 +1676,42 @@ onMounted(() => { max-height: 300px; } } + +/* 图表卡片头部样式 */ +.chartCard__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} + +/* 图表操作按钮样式 */ +.chartCard__actions { + display: flex; + gap: 8px; +} + +.chartCard__actionBtn { + padding: 4px 12px; + border: 1px solid #d1d5db; + background-color: #f9fafb; + border-radius: 4px; + font-size: 12px; + cursor: pointer; + transition: all 0.2s; +} + +.chartCard__actionBtn:hover { + background-color: #f3f4f6; +} + +.chartCard__actionBtn--active { + background-color: #3b82f6; + color: white; + border-color: #3b82f6; +} + +.chartCard__actionBtn--active:hover { + background-color: #2563eb; +} \ No newline at end of file