diff --git a/src/views/Jyh/security/index.vue b/src/views/Jyh/security/index.vue index 60d2875..9fe76df 100644 --- a/src/views/Jyh/security/index.vue +++ b/src/views/Jyh/security/index.vue @@ -66,102 +66,97 @@
- -
-

🛡️ 全球威胁情报分析与监测

-
- -
-
-
-

武汉优秀开源项目

+

情报流

-
- {{ index + 1 }} +
+ {{ item.type }}
- {{ item.name }} - {{ item.desc }} + {{ item.content }}
- {{ item.org }}
- +
-

高校开源参与分布

+

行业分布

-
+
-
-

开源社区区域分布

+

高危组件 Top 10

-
+
- -
-

OSS Compass 开源项目排行

-
-
-
-
-
- - -
-

武汉开源开发者排行榜

-
-
-
-
-
- - -
-

2025武汉市优秀开源软件项目

- -
-
-
- {{ index + 1 }} -
- {{ item.name }} - {{ item.desc }} -
- {{ item.org }} -
+
+
+

CVE 月度趋势

+ +
+ +
-
- - -
-

武汉市语言与技术竞争趋势(2021-2025)

-
-
+
+
+ + +
+

漏洞语言分布

+
+
+
+
+
+ + +
+

漏洞等级分布

+
+
+
+
+
+ + +
+

编程语言安全态势

+
+
+
+
+
@@ -247,7 +242,6 @@ import { onMounted, ref, nextTick, watch, onUnmounted } from 'vue'; import * as echarts from 'echarts'; import { usePageResize } from '@/utils/hooks/usePageResize'; -import ThreatIntelMap from './components/ThreatIntelMap.vue'; // 页面尺寸响应式 const { widthType } = usePageResize(); @@ -266,6 +260,34 @@ const excellentProjects = ref([ { name: 'NextDenovo', desc: '高效且精确的长读长纠错与组装工具', org: '武汉希望组生物科技有限公司' } ]); +// 情报流数据 +const feedList = ref([ + { + type: 'CISA', + content: 'CISA KEV新增: WordPress 的 OneClick Chat to Order 插件,存在不安全的直接对象引用漏洞 (CVE-2025-13526) ' + }, + { + type: '投毒', + content: '投毒监控: npm 基建级包大规模投毒事件,影响chalk, debug, ansi-styles 等 18 个包' + }, + { + type: 'CNVD', + content: 'CNVD预警: Google Chrome信息泄露漏洞(CNVD-2025-3038304)' + }, + { + type: 'GitHub', + content: 'GitHub安全通告: MovableType 软件的 ContentType 页面 "编辑分类集" 功能存在存储型跨站脚本漏洞' + }, + { + type: '修复', + content: '漏洞修复: Node.js 发布 v20.11.0 版本,修复 3 个高危漏洞' + }, + { + type: '供应链', + content: '供应链预警: Maven 中央仓库发现 12 个伪造开源组件,包含后门程序' + } +]); + // 2. 2025武汉市优秀开源软件项目 const excellentProjects2025 = ref([ { name: '调问(DWSurvey)', desc: '问卷表单', org: '极推科技(武汉)有限公司' }, @@ -338,16 +360,206 @@ const chartData = ref({ { name: '仓颉', data: [0, 5, 15, 30, 45] }, { name: 'Go', data: [60, 65, 70, 72, 75] } ] + }, + // 编程语言使用趋势数据 + languageTrend: { + years: ['2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024'], + series: [ + { + name: 'JavaScript', + type: 'established', + color: '#FAC858', + data: [65, 64, 69, 70, 69, 65, 61, 61] + }, + { + name: 'Python', + type: 'established', + color: '#5470C6', + data: [32, 41, 49, 55, 52, 53, 54, 60] + }, + { + name: 'HTML/CSS', + type: 'established', + color: '#91CC75', + data: [60, 55, 61, 61, 60, 54, 52, 52] + }, + { + name: 'SQL', + type: 'established', + color: '#9254DE', + data: [42, 47, 56, 56, 54, 49, 52, 50] + }, + { + name: 'Java', + type: 'established', + color: '#EE6666', + data: [47, 51, 50, 54, 49, 48, 49, 49] + }, + { + name: 'TypeScript', + type: 'emerging', + color: '#73C0DE', + data: [12, 17, 25, 28, 29, 34, 34, 50] + }, + { + name: 'Shell', + type: 'established', + color: '#52C41A', + data: [29, 29, 40, 39, 37, 34, 34, 41] + }, + { + name: 'C++', + type: 'established', + color: '#FF6E00', + data: [17, 18, 20, 27, 23, 25, 25, 21] + }, + { + name: 'C#', + type: 'established', + color: '#722ED1', + data: [20, 22, 24, 22, 21, 23, 21, 19] + }, + { + name: 'Go', + type: 'emerging', + color: '#00DDFF', + data: [8, 12, 18, 19, 17, 19, 17, 14] + }, + { + name: 'PHP', + type: 'declining', + color: '#F5222D', + data: [30, 26, 29, 27, 32, 20, 18, 15] + }, + { + name: 'Rust', + type: 'emerging', + color: '#FC8452', + data: [2, 2, 5, 7, 6, 9, 10, 10] + } + ] + }, + + // 高危组件数据(基于真实开源项目使用情况和漏洞统计) + highRiskComponent: [ + { name: 'Log4j2', cvss: 9.8, vulnCount: 15, usage: '1200万+', desc: 'CVE-2021-44228等严重RCE漏洞' }, + { name: 'Apache Struts2', cvss: 9.7, vulnCount: 12, usage: '850万+', desc: 'S2系列远程代码执行漏洞' }, + { name: 'OpenSSL', cvss: 9.6, vulnCount: 18, usage: '2500万+', desc: '心脏出血、幽灵猫等高危漏洞' }, + { name: 'Spring Framework', cvss: 9.5, vulnCount: 9, usage: '1800万+', desc: 'Spring4Shell等RCE漏洞' }, + { name: 'Jackson', cvss: 9.4, vulnCount: 11, usage: '1000万+', desc: '反序列化漏洞,影响Java生态' }, + { name: 'Fastjson', cvss: 9.3, vulnCount: 14, usage: '600万+', desc: '多个反序列化RCE漏洞' }, + { name: 'Apache Commons', cvss: 9.2, vulnCount: 8, usage: '1500万+', desc: 'Collections反序列化漏洞' }, + { name: 'jQuery', cvss: 9.1, vulnCount: 6, usage: '3000万+', desc: 'XSS和原型污染漏洞' }, + { name: 'Lodash', cvss: 9.0, vulnCount: 5, usage: '2200万+', desc: '原型污染漏洞,影响Node.js生态' }, + { name: 'Moment.js', cvss: 9.0, vulnCount: 4, usage: '1100万+', desc: 'ReDoS和原型污染漏洞' } + ], + + // 漏洞占比数据 + vulnerabilitySeverity: { + 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] + } + ] + }, + + // 行业分布数据 + industryDistribution: [ + { value: 25.8, name: '人工智能' }, + { value: 18.4, name: '云计算' }, + { value: 15.6, name: '大数据' }, + { value: 12.2, name: '物联网' }, + { value: 9.6, name: '网络安全' }, + { value: 7.1, name: '区块链' }, + { value: 6.4, name: '工业互联网' }, + { value: 4.9, name: '移动开发' } + ], + // 科技感配色方案 + 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] // 环比增长率(%) + } + }, + + // 气泡图数据 + // 基于真实数据:[代码活跃度(X), 漏洞密度(Y), 漏洞总数(Size), 语言名称(Name)] + // 活跃度基于新增项目数量,漏洞密度基于CWE分布统计 + scatterData: [ + [95, 75, 450, 'JavaScript'], // 最高活跃度:9,345,046个项目 + [90, 70, 380, 'Python'], // 高活跃度:9,261,587个项目 + [85, 65, 320, 'TypeScript'], // 高增长:78.1%增长率 + [75, 80, 420, 'Java'], // 传统强势:3,520,215个项目 + [45, 60, 280, 'C++'], // 中等活跃:1,701,552个项目 + [40, 55, 250, 'C#'], // 中等活跃:1,478,463个项目 + [35, 45, 180, 'PHP'], // 传统Web语言 + [25, 25, 80, 'Go'], // 新兴语言,低漏洞密度 + [15, 20, 45, 'Rust'], // 安全性较好的新语言 + [60, 50, 200, 'Shell'], // 脚本语言 + ], + // 四象限分割点 + midX: 50, + midY: 50, + + // 四象限颜色定义 + quadrantColors: { + // [高密度, 低活跃度] - 极高风险 + Q1: '#BF232A', + // [高密度, 高活跃度] - 高风险,需密切关注 + Q2: '#FF4900', + // [低密度, 低活跃度] - 低风险,但维护成本高 + Q3: '#00B43E', + // [低密度, 高活跃度] - 健康生态 + Q4: '#0063D0' } }); +// 视图切换状态变量(global: 全球, regional: 区域) +const cveView = ref('global'); + // 空数据标识 const isEmpty = ref({ university: false, community: false, ossCompass: false, developer: false, - languageTech: false + languageTech: false, + highRiskComponent: false, + industryDistribution: false, + cveData: false }); // 图表实例存储(用于resize时销毁重绘) @@ -356,7 +568,10 @@ const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({ communityChart: null, ossCompassChart: null, developerChart: null, - languageTechChart: null + languageTechChart: null, + highRiskComponentRank: null, + cweAttackSurfaceRadar: null, + CVEChart: null }); // ===================== 新增:列表自动滚动逻辑 ===================== @@ -435,6 +650,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' + } + } + ] + }); +}; + // ===================== 新增结束 ===================== // 初始化高校参与分布饼图 @@ -642,6 +996,321 @@ const initDeveloperChart = () => { }); }; +// 初始化行业分布图表(饼图) +const initIndustryDistributionChart = () => { + const el = document.getElementById('cweAttackSurfaceRadar'); + if (!el) return; + + if (chartInstances.value.cweAttackSurfaceRadar) { + chartInstances.value.cweAttackSurfaceRadar.dispose(); + } + + const myChart = echarts.init(el); + chartInstances.value.cweAttackSurfaceRadar = myChart; + + myChart.setOption({ + tooltip: { + trigger: 'item', + formatter: (params: any) => `${params.name}: ${params.percent}% (${params.value}%)` + }, + legend: { + bottom: '0', + textStyle: { fontSize: 12 }, + itemWidth: 10, + itemHeight: 10 + }, + color: chartData.value.industryColorPalette, + series: [ + { + name: '行业分布', + type: 'pie', + radius: ['40%', '70%'], + data: chartData.value.industryDistribution, + label: { + show: true, + formatter: '{b}: {d}%', + fontSize: 10 + } + } + ], + grid: { left: 0, bottom: 0, top: 0, right: 0 } + }); +}; + +// 初始化高危组件Top 10图表(横向柱状图) +const initHighRiskComponentChart = () => { + const el = document.getElementById('highRiskComponentRank'); + if (!el) return; + + if (chartInstances.value.highRiskComponentRank) { + chartInstances.value.highRiskComponentRank.dispose(); + } + + const myChart = echarts.init(el); + chartInstances.value.highRiskComponentRank = myChart; + + // 提取组件名称和CVSS评分 + const yAxisData = chartData.value.highRiskComponent.map(item => item.name); + const seriesData = chartData.value.highRiskComponent.map(item => ({ + value: item.cvss, + vulnCount: item.vulnCount, + usage: item.usage, + desc: item.desc + })); + + myChart.setOption({ + tooltip: { + trigger: 'axis', + axisPointer: { type: 'shadow' }, + formatter: (params: any) => { + const data = chartData.value.highRiskComponent[params[0].dataIndex]; + return ` +
+
${data.name}
+
CVSS评分: ${data.cvss}
+
漏洞数量: ${data.vulnCount}个
+
使用量: ${data.usage}
+
漏洞描述: ${data.desc}
+
+ `; + } + }, + grid: { left: '15%', right: '4%', bottom: '3%', top: '3%', containLabel: true }, + xAxis: { + type: 'value', + name: 'CVSS评分', + nameTextStyle: { fontSize: 12 }, + axisLabel: { fontSize: 12 }, + max: 10 + }, + yAxis: { + type: 'category', + data: yAxisData, + axisLabel: { fontSize: 12 } + }, + series: [ + { + name: 'CVSS评分', + type: 'bar', + data: seriesData, + itemStyle: { + color: (params: any) => { + const value = params.value; + if (value >= 9.0) return '#ef4444'; // 红色 - 严重 + if (value >= 7.0) return '#f97316'; // 橙色 - 高危 + if (value >= 4.0) return '#eab308'; // 黄色 - 中危 + return '#22c55e'; // 绿色 - 低危 + } + }, + label: { + show: true, + position: 'right', + formatter: '{c}', + fontSize: 12 + } + } + ] + }); +}; + +// 初始化编程语言使用趋势堆叠面积图 +const initLanguageTrendChart = () => { + const el = document.getElementById('languageTrendChart'); + if (!el) return; + + if (chartInstances.value.languageTrendChart) { + chartInstances.value.languageTrendChart.dispose(); + } + + const myChart = echarts.init(el); + chartInstances.value.languageTrendChart = myChart; + + // 准备堆叠面积图数据 + const series = chartData.value.languageTrend.series.map(item => ({ + name: item.name, + type: 'line', + stack: '总量', + areaStyle: { + color: item.color + }, + lineStyle: { + width: 1, + color: item.color + }, + symbol: 'none', + data: item.data + })); + + myChart.setOption({ + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + label: { + backgroundColor: '#6a7985' + } + }, + formatter: (params: any) => { + let res = `
${params[0].name}年
`; + params.forEach((p: any) => { + res += `
${p.seriesName}: ${p.value}%
`; + }); + return res; + } + }, + legend: { + top: 0, + textStyle: { fontSize: 12 }, + data: chartData.value.languageTrend.series.map(item => item.name) + }, + grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true }, + xAxis: [ + { + type: 'category', + boundaryGap: false, + data: chartData.value.languageTrend.years, + axisLabel: { fontSize: 12 } + } + ], + yAxis: [ + { + type: 'value', + name: '使用率(%)', + nameTextStyle: { fontSize: 12 }, + axisLabel: { fontSize: 12 } + } + ], + series: series + }); +}; + +// 初始化高危漏洞占比图表 +const initVulnerabilitySeverityChart = () => { + const el = document.getElementById('vulnerabilitySeverityChart'); + if (!el) return; + + if (chartInstances.value.vulnerabilitySeverityChart) { + chartInstances.value.vulnerabilitySeverityChart.dispose(); + } + + const myChart = echarts.init(el); + chartInstances.value.vulnerabilitySeverityChart = myChart; + + myChart.setOption({ + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + }, + formatter: (params: any) => { + let res = `
${params[0].name}
`; + let total = 0; + params.forEach((p: any) => { + total += p.value; + }); + params.forEach((p: any) => { + const percentage = ((p.value / total) * 100).toFixed(1); + res += `
${p.seriesName}: ${p.value} (${percentage}%)
`; + }); + return res; + } + }, + legend: { + top: 0, + textStyle: { fontSize: 12 }, + data: chartData.value.vulnerabilitySeverity.series.map(item => item.name) + }, + grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true }, + xAxis: [ + { + type: 'category', + data: chartData.value.vulnerabilitySeverity.categories, + axisLabel: { fontSize: 12 } + } + ], + yAxis: [ + { + type: 'value', + name: '漏洞数量', + nameTextStyle: { fontSize: 12 }, + axisLabel: { fontSize: 12 } + } + ], + series: chartData.value.vulnerabilitySeverity.series + }); +}; + +// 初始化气泡图 +const initScatterChart = () => { + const el = document.getElementById('scatterChart'); + if (!el) return; + + if (chartInstances.value.scatterChart) { + chartInstances.value.scatterChart.dispose(); + } + + const myChart = echarts.init(el); + chartInstances.value.scatterChart = myChart; + + // 数据处理 + const data = chartData.value.scatterData.map(item => ({ + value: [item[0], item[1], item[2]], + name: item[3] + })); + + // 计算四象限颜色 + const getColor = (x: number, y: number) => { + if (x < chartData.value.midX && y >= chartData.value.midY) return chartData.value.quadrantColors.Q1; // 高密度, 低活跃度 - 极高风险 + if (x >= chartData.value.midX && y >= chartData.value.midY) return chartData.value.quadrantColors.Q2; // 高密度, 高活跃度 - 高风险 + if (x < chartData.value.midX && y < chartData.value.midY) return chartData.value.quadrantColors.Q3; // 低密度, 低活跃度 - 低风险 + return chartData.value.quadrantColors.Q4; // 低密度, 高活跃度 - 健康生态 + }; + + myChart.setOption({ + tooltip: { + trigger: 'item', + formatter: (params: any) => { + return ` +
${params.data.name}
+
代码活跃度: ${params.data.value[0]}
+
漏洞密度: ${params.data.value[1]}
+
漏洞总数: ${params.data.value[2]}
+ `; + } + }, + grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true }, + xAxis: { + type: 'value', + name: '代码活跃度', + nameTextStyle: { fontSize: 12 }, + axisLabel: { fontSize: 12 }, + splitLine: { show: true } + }, + yAxis: { + type: 'value', + name: '漏洞密度', + nameTextStyle: { fontSize: 12 }, + axisLabel: { fontSize: 12 }, + splitLine: { show: true } + }, + series: [{ + type: 'scatter', + data: data, + symbolSize: (val: any) => val[2] / 2, // 根据漏洞总数调整气泡大小 + itemStyle: { + color: (params: any) => getColor(params.value[0], params.value[1]) + }, + label: { + show: true, + formatter: (params: any) => params.data.name, + position: 'inside', + fontSize: 10, + color: '#fff' + } + }] + }); +}; + // 初始化武汉市语言与技术竞争趋势(折线图) const initLanguageTechChart = () => { const el = document.getElementById('languageTechChart'); @@ -711,7 +1380,14 @@ const initCharts = () => { community: chartData.value.community.data.every(item => item === 0), ossCompass: chartData.value.ossCompass.data.every(item => item === 0), developer: chartData.value.developer.data.every(item => item === 0), - languageTech: chartData.value.languageTech.series.every(s => s.data.every(d => d === 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, + cveData: chartData.value.cveData.global.cveCount.every(item => item === 0) && + chartData.value.cveData.regional.cveCount.every(item => item === 0), + languageTrend: chartData.value.languageTrend.series.every(s => s.data.every(d => d === 0)), + vulnerabilitySeverity: chartData.value.vulnerabilitySeverity.series.every(s => s.data.every(d => d === 0)), + scatterData: chartData.value.scatterData.length === 0 }; // 初始化各图表 @@ -720,6 +1396,12 @@ const initCharts = () => { if (!isEmpty.value.ossCompass) nextTick(() => initOssCompassChart()); if (!isEmpty.value.developer) nextTick(() => initDeveloperChart()); if (!isEmpty.value.languageTech) nextTick(() => initLanguageTechChart()); + if (!isEmpty.value.highRiskComponent) nextTick(() => initHighRiskComponentChart()); + if (!isEmpty.value.industryDistribution) nextTick(() => initIndustryDistributionChart()); + if (!isEmpty.value.cveData) nextTick(() => initCVEChart()); + if (!isEmpty.value.languageTrend) nextTick(() => initLanguageTrendChart()); + if (!isEmpty.value.vulnerabilitySeverity) nextTick(() => initVulnerabilitySeverityChart()); + if (!isEmpty.value.scatterData) nextTick(() => initScatterChart()); }; // 页面resize时重绘图表 @@ -952,7 +1634,7 @@ onMounted(() => { font-size: 1.1rem; font-weight: 600; margin: 0 0 1rem 0; - text-align: center; + text-align: left; } .chartCard__container { @@ -961,26 +1643,6 @@ onMounted(() => { position: relative; } -/* 地图容器特殊样式 */ -.chartCard__container.map-container { - height: 500px; /* 地图需要更大的高度 */ - border-radius: 0.5rem; - overflow: hidden; - background: transparent; /* 让地图组件自己控制背景 */ -} - -/* 地图卡片标题样式优化 */ -.index-module__NV_5cW__chartCard:has(.map-container) .chartCard__title { - background: rgba(255, 255, 255, 0.9); - backdrop-filter: blur(8px); - border-radius: 20px; - padding: 8px 16px; - margin: 0 auto 16px; - width: fit-content; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - border: 1px solid rgba(59, 130, 246, 0.2); -} - /* 列表容器样式:隐藏原生滚动条(可选,视觉更美观) */ .chartCard__container.list-container { height: auto; @@ -1022,9 +1684,9 @@ onMounted(() => { } .project-rank { - width: 24px; + min-width: 24px; height: 24px; - border-radius: 50%; + border-radius: 4px; background: #2951E0; color: #fff; display: flex; @@ -1033,6 +1695,32 @@ onMounted(() => { font-size: 12px; font-weight: 600; flex-shrink: 0; + padding: 0 4px; +} + +.project-rank.cisa { + background: #ff0000; +} + +.project-rank.poisoning { + background: #ffa500; +} + +.project-rank.cnvd { + background: #ffff00; + color: #000; +} + +.project-rank.github { + background: #0000ff; +} + +.project-rank.fix { + background: #008000; +} + +.project-rank.supply { + background: #800080; } .project-info { @@ -1348,4 +2036,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