态势感知中心-全球开源风险态势感知监控中心漏洞语言分布对接接口
This commit is contained in:
@@ -9,8 +9,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, nextTick, onMounted, onUnmounted } from 'vue';
|
import { ref, nextTick, onMounted, onUnmounted, watch } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
|
import { languageTrendPage1 } from '@/api/jyh/situation';
|
||||||
|
|
||||||
// 空数据标识
|
// 空数据标识
|
||||||
const isEmpty = ref({
|
const isEmpty = ref({
|
||||||
@@ -22,83 +23,188 @@ const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({
|
|||||||
languageTrendChart: null
|
languageTrendChart: null
|
||||||
});
|
});
|
||||||
|
|
||||||
// 漏洞语言分布数据
|
// 漏洞语言分布数据 - 响应式数据
|
||||||
const languageTrendData = {
|
const languageTrendData = ref({
|
||||||
years: ['2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024'],
|
years: [] as string[],
|
||||||
series: [
|
series: [] as any[]
|
||||||
{
|
});
|
||||||
name: 'JavaScript',
|
|
||||||
type: 'established',
|
// 获取后端数据
|
||||||
color: '#FAC858',
|
const fetchLanguageTrendData = async () => {
|
||||||
data: [65, 64, 69, 70, 69, 65, 61, 61]
|
try {
|
||||||
},
|
console.log('开始请求漏洞语言分布数据...');
|
||||||
{
|
const response = await languageTrendPage1();
|
||||||
name: 'Python',
|
console.log('API响应:', response);
|
||||||
type: 'established',
|
|
||||||
color: '#5470C6',
|
if (response?.code === 0) {
|
||||||
data: [32, 41, 49, 55, 52, 53, 54, 60]
|
console.log('API返回的数据:', response.data);
|
||||||
},
|
|
||||||
{
|
// 处理后端返回的数据格式
|
||||||
name: 'HTML/CSS',
|
const processedData = processBackendData(response.data);
|
||||||
type: 'established',
|
languageTrendData.value = processedData;
|
||||||
color: '#91CC75',
|
|
||||||
data: [60, 55, 61, 61, 60, 54, 52, 52]
|
// 更新空数据标识
|
||||||
},
|
isEmpty.value.languageTrend = !processedData.years || processedData.years.length === 0;
|
||||||
{
|
} else {
|
||||||
name: 'SQL',
|
console.error('获取漏洞语言分布数据失败:', response?.msg || '未知错误');
|
||||||
type: 'established',
|
// 设置默认数据
|
||||||
color: '#9254DE',
|
setDefaultData();
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
]
|
} catch (error) {
|
||||||
|
console.error('获取漏洞语言分布数据异常:', error);
|
||||||
|
// 发生异常时设置默认数据
|
||||||
|
setDefaultData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理后端返回的数据格式
|
||||||
|
const processBackendData = (backendData: any[]) => {
|
||||||
|
// 按语言分组
|
||||||
|
const groupedData: { [key: string]: { year: string, value: number }[] } = {};
|
||||||
|
|
||||||
|
backendData.forEach(item => {
|
||||||
|
if (!groupedData[item.name]) {
|
||||||
|
groupedData[item.name] = [];
|
||||||
|
}
|
||||||
|
groupedData[item.name].push({
|
||||||
|
year: item.year,
|
||||||
|
value: item.data
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 提取所有年份并排序
|
||||||
|
const allYearsSet = new Set<string>();
|
||||||
|
Object.values(groupedData).forEach(langData => {
|
||||||
|
langData.forEach(d => allYearsSet.add(d.year));
|
||||||
|
});
|
||||||
|
const years = Array.from(allYearsSet).sort();
|
||||||
|
|
||||||
|
// 生成系列数据
|
||||||
|
const series = Object.entries(groupedData).map(([name, data]) => {
|
||||||
|
// 根据年份顺序填充数据,缺失的设为0
|
||||||
|
const values = years.map(year => {
|
||||||
|
const found = data.find(d => d.year === year);
|
||||||
|
return found ? found.value : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 这里可以使用预定义的颜色方案,或者动态生成
|
||||||
|
const predefinedColors: { [key: string]: string } = {
|
||||||
|
'JavaScript': '#FAC858',
|
||||||
|
'Python': '#5470C6',
|
||||||
|
'HTML/CSS': '#91CC75',
|
||||||
|
'SQL': '#9254DE',
|
||||||
|
'Java': '#EE6666',
|
||||||
|
'TypeScript': '#73C0DE',
|
||||||
|
'Shell': '#52C41A',
|
||||||
|
'C++': '#FF6E00',
|
||||||
|
'C#': '#722ED1',
|
||||||
|
'Go': '#00DDFF',
|
||||||
|
'PHP': '#F5222D',
|
||||||
|
'Rust': '#FC8452'
|
||||||
|
};
|
||||||
|
|
||||||
|
const color = predefinedColors[name] || getRandomColor();
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
type: 'established', // 可以根据实际情况调整
|
||||||
|
color: color,
|
||||||
|
data: values
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
years: years,
|
||||||
|
series: series
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置默认数据
|
||||||
|
const setDefaultData = () => {
|
||||||
|
languageTrendData.value = {
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
isEmpty.value.languageTrend = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生成随机颜色的辅助函数
|
||||||
|
const getRandomColor = () => {
|
||||||
|
const colors = ['#FAC858', '#5470C6', '#91CC75', '#9254DE', '#EE6666', '#73C0DE', '#52C41A', '#FF6E00', '#722ED1', '#00DDFF', '#F5222D', '#FC8452'];
|
||||||
|
return colors[Math.floor(Math.random() * colors.length)];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始化漏洞语言分布图表(堆叠面积图)
|
// 初始化漏洞语言分布图表(堆叠面积图)
|
||||||
@@ -119,7 +225,7 @@ const initLanguageTrendChart = () => {
|
|||||||
chartInstances.value.languageTrendChart = myChart;
|
chartInstances.value.languageTrendChart = myChart;
|
||||||
|
|
||||||
// 准备堆叠面积图数据
|
// 准备堆叠面积图数据
|
||||||
const series = languageTrendData.series.map(item => ({
|
const series = languageTrendData.value.series.map(item => ({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
stack: '总量',
|
stack: '总量',
|
||||||
@@ -154,14 +260,14 @@ const initLanguageTrendChart = () => {
|
|||||||
legend: {
|
legend: {
|
||||||
top: 0,
|
top: 0,
|
||||||
textStyle: { fontSize: 12 },
|
textStyle: { fontSize: 12 },
|
||||||
data: languageTrendData.series.map(item => item.name)
|
data: languageTrendData.value.series.map(item => item.name)
|
||||||
},
|
},
|
||||||
grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true },
|
grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true },
|
||||||
xAxis: [
|
xAxis: [
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: languageTrendData.years,
|
data: languageTrendData.value.years,
|
||||||
axisLabel: { fontSize: 12 }
|
axisLabel: { fontSize: 12 }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -185,13 +291,24 @@ const initLanguageTrendChart = () => {
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 在组件挂载后初始化图表
|
|
||||||
onMounted(() => {
|
// 在组件挂载后获取数据并初始化图表
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetchLanguageTrendData();
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
initLanguageTrendChart();
|
initLanguageTrendChart();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 监听数据变化,重新初始化图表
|
||||||
|
watch(languageTrendData, () => {
|
||||||
|
if (!isEmpty.value.languageTrend) {
|
||||||
|
nextTick(() => {
|
||||||
|
initLanguageTrendChart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, { deep: true });
|
||||||
|
|
||||||
// 在组件卸载前清理资源
|
// 在组件卸载前清理资源
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (chartInstances.value.languageTrendChart) {
|
if (chartInstances.value.languageTrendChart) {
|
||||||
|
|||||||
Reference in New Issue
Block a user