态势感知平台-开源生态与贡献价值全景基础设施覆盖率组件抽离
This commit is contained in:
@@ -131,13 +131,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 2. 基础设施覆盖率(多层环形图)-->
|
||||
<div class="index-module__NV_5cW__chartCard">
|
||||
<h3 class="chartCard__title">基础设施覆盖率</h3>
|
||||
<div class="chartCard__container">
|
||||
<div class="num-empty" v-if="isEmpty.infrastructure"></div>
|
||||
<div v-else id="infrastructureChart" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<InfrastructureCoverageChart />
|
||||
|
||||
<!-- 3. 高校开源社团/俱乐部数量-->
|
||||
<div class="index-module__NV_5cW__chartCard">
|
||||
@@ -291,6 +285,7 @@
|
||||
import { onMounted, ref, nextTick, watch, onUnmounted, computed } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import TalentMapChart from './TalentMapChart.vue'
|
||||
import InfrastructureCoverageChart from './components/InfrastructureCoverageChart.vue'
|
||||
import { usePageResize } from '@/utils/hooks/usePageResize';
|
||||
|
||||
// 页面尺寸响应式
|
||||
@@ -578,7 +573,6 @@ const communityData = computed(() => processUniversityClubsData());
|
||||
|
||||
// 空数据标识
|
||||
const isEmpty = ref({
|
||||
infrastructure: false,
|
||||
community: false,
|
||||
communityHealth: false,
|
||||
languageTech: false
|
||||
@@ -648,7 +642,6 @@ const getTalentLevel = (density: number) => {
|
||||
|
||||
// 图表实例存储(用于resize时销毁重绘)
|
||||
const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({
|
||||
infrastructureChart: null,
|
||||
communityChart: null,
|
||||
communityHealthChart: null,
|
||||
languageTechChart: null,
|
||||
@@ -801,154 +794,7 @@ const formatDevs = (num: number) => {
|
||||
};
|
||||
// ===================== 新增结束 =====================
|
||||
|
||||
// 初始化基础设施覆盖率图表(多层环形图)
|
||||
const initInfrastructureChart = () => {
|
||||
const el = document.getElementById('infrastructureChart');
|
||||
if (!el) return;
|
||||
|
||||
if (chartInstances.value.infrastructureChart) {
|
||||
chartInstances.value.infrastructureChart.dispose();
|
||||
}
|
||||
|
||||
const myChart = echarts.init(el);
|
||||
chartInstances.value.infrastructureChart = myChart;
|
||||
|
||||
const seriesData: any[] = [];
|
||||
const center = ['50%', '50%'];
|
||||
|
||||
chartData.value.infrastructure.forEach(item => {
|
||||
const completeValue = item.value;
|
||||
const remainingValue = 100 - item.value;
|
||||
|
||||
seriesData.push({
|
||||
name: item.name,
|
||||
type: 'pie',
|
||||
radius: item.radius,
|
||||
center: center,
|
||||
avoidLabelOverlap: false,
|
||||
startAngle: 90,
|
||||
hoverAnimation: true,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
scale: true,
|
||||
scaleSize: 5
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: completeValue,
|
||||
name: item.name,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0, 0, 0, 1,
|
||||
[
|
||||
{ offset: 0, color: item.color },
|
||||
{ offset: 1, color: item.color + '80' }
|
||||
]
|
||||
),
|
||||
shadowBlur: 10,
|
||||
shadowColor: item.color + '40'
|
||||
}
|
||||
},
|
||||
{
|
||||
value: remainingValue,
|
||||
name: '未覆盖',
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 255, 255, 0.08)'
|
||||
},
|
||||
tooltip: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
disabled: true
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
// 计算平均覆盖率
|
||||
const avgCoverage = Math.round(
|
||||
chartData.value.infrastructure.reduce((sum, d) => sum + d.value, 0) /
|
||||
chartData.value.infrastructure.length
|
||||
);
|
||||
|
||||
myChart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||
borderColor: '#3b82f6',
|
||||
borderWidth: 1,
|
||||
borderRadius: 6,
|
||||
padding: 12,
|
||||
textStyle: { color: '#333', fontSize: 13 },
|
||||
formatter: (params: any) => {
|
||||
if (params.name === '未覆盖') return '';
|
||||
const remaining = 100 - params.value;
|
||||
return `
|
||||
<div style="line-height: 2;">
|
||||
<div style="font-weight: bold; color: #3b82f6; margin-bottom: 6px; border-bottom: 2px solid #3b82f6; padding-bottom: 4px;">${params.name}</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<span style="display: inline-block; width: 8px; height: 8px; background: #10b981; border-radius: 50%;"></span>
|
||||
已覆盖:<span style="color: #10b981; font-weight: 700; font-size: 15px;">${params.value}%</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<span style="display: inline-block; width: 8px; height: 8px; background: #ef4444; border-radius: 50%;"></span>
|
||||
未覆盖:<span style="color: #ef4444; font-weight: 700; font-size: 15px;">${remaining}%</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: chartData.value.infrastructure.map(d => d.name),
|
||||
orient: 'vertical',
|
||||
right: '5%',
|
||||
top: 'center',
|
||||
itemWidth: 12, // 图例方块宽度
|
||||
itemHeight: 12, // 图例方块高度
|
||||
itemGap: 15, // 图例项之间的间距
|
||||
icon: 'circle', // 图例形状:'circle'圆形, 'rect'方形, 'roundRect'圆角方形
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontSize: 12,
|
||||
padding: [0, 0, 0, 8] // 文字与图标的间距
|
||||
}
|
||||
},
|
||||
graphic: [
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '45%',
|
||||
style: {
|
||||
text: '总覆盖度',
|
||||
fill: '#666',
|
||||
fontSize: 13,
|
||||
fontWeight: '500'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '52%',
|
||||
style: {
|
||||
text: `${avgCoverage}%`,
|
||||
fill: '#3b82f6',
|
||||
fontSize: 28,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: seriesData,
|
||||
animationDuration: 1000,
|
||||
animationEasing: 'cubicOut',
|
||||
animationDelay: (idx: number) => idx * 100
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化高校开源社团/俱乐部数量图表(纵向柱状图)
|
||||
const initCommunityChart = () => {
|
||||
@@ -2082,14 +1928,12 @@ const initHeatmapChart = () => {
|
||||
const initCharts = () => {
|
||||
// 空数据判断
|
||||
isEmpty.value = {
|
||||
infrastructure: chartData.value.infrastructure.length === 0,
|
||||
community: chartData.value.universityClubs.length === 0,
|
||||
communityHealth: chartData.value.communityHealth.github.every(item => item === 0),
|
||||
languageTech: chartData.value.languageTech.mainstream.length === 0 && chartData.value.languageTech.emerging.length === 0
|
||||
};
|
||||
|
||||
// 初始化各图表
|
||||
if (!isEmpty.value.infrastructure) nextTick(() => initInfrastructureChart());
|
||||
if (!isEmpty.value.community) nextTick(() => initCommunityChart());
|
||||
if (!isEmpty.value.communityHealth) nextTick(() => initCommunityHealthChart());
|
||||
if (!isEmpty.value.languageTech) nextTick(() => initLanguageTechChart());
|
||||
|
||||
Reference in New Issue
Block a user