态势感知平台-开源生态与贡献价值全景高校开源社区/俱乐部数量组件抽离
This commit is contained in:
@@ -134,13 +134,7 @@
|
||||
<InfrastructureCoverageChart />
|
||||
|
||||
<!-- 3. 高校开源社团/俱乐部数量-->
|
||||
<div class="index-module__NV_5cW__chartCard">
|
||||
<h3 class="chartCard__title">高校开源社团/俱乐部数量</h3>
|
||||
<div class="chartCard__container">
|
||||
<div class="num-empty" v-if="isEmpty.community"></div>
|
||||
<div v-else id="communityChart" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<UniversityClubStatsChart />
|
||||
|
||||
<!-- 4. 社区治理健康度对比-->
|
||||
<div class="index-module__NV_5cW__chartCard">
|
||||
@@ -286,6 +280,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 UniversityClubStatsChart from './components/UniversityClubStatsChart.vue'
|
||||
import { usePageResize } from '@/utils/hooks/usePageResize';
|
||||
|
||||
// 页面尺寸响应式
|
||||
@@ -538,42 +533,12 @@ const chartData = ref({
|
||||
}
|
||||
});
|
||||
|
||||
// 处理高校社团数据
|
||||
const processUniversityClubsData = () => {
|
||||
const counts: { [key: string]: { count: number; clubs: string[] } } = {};
|
||||
|
||||
chartData.value.universityClubs.forEach(item => {
|
||||
if (!counts[item.university]) {
|
||||
counts[item.university] = { count: 0, clubs: [] };
|
||||
}
|
||||
counts[item.university].count++;
|
||||
counts[item.university].clubs.push(item.club);
|
||||
});
|
||||
|
||||
// 转换为数组并排序
|
||||
const sorted = Object.entries(counts)
|
||||
.map(([name, data]) => ({
|
||||
name,
|
||||
value: data.count,
|
||||
clubs: data.clubs
|
||||
}))
|
||||
.sort((a, b) => b.value - a.value);
|
||||
|
||||
return {
|
||||
universities: sorted.slice(0, 10).map(item => item.name), // 取前10名,避免X轴过挤
|
||||
values: sorted.slice(0, 10).map(item => item.value),
|
||||
details: sorted.slice(0, 10).reduce((acc, item) => {
|
||||
acc[item.name] = item.clubs;
|
||||
return acc;
|
||||
}, {} as { [key: string]: string[] })
|
||||
};
|
||||
};
|
||||
|
||||
const communityData = computed(() => processUniversityClubsData());
|
||||
|
||||
// 空数据标识
|
||||
const isEmpty = ref({
|
||||
community: false,
|
||||
communityHealth: false,
|
||||
languageTech: false
|
||||
});
|
||||
@@ -642,7 +607,6 @@ const getTalentLevel = (density: number) => {
|
||||
|
||||
// 图表实例存储(用于resize时销毁重绘)
|
||||
const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({
|
||||
communityChart: null,
|
||||
communityHealthChart: null,
|
||||
languageTechChart: null,
|
||||
vitalityChart: null,
|
||||
@@ -796,131 +760,7 @@ const formatDevs = (num: number) => {
|
||||
|
||||
|
||||
|
||||
// 初始化高校开源社团/俱乐部数量图表(纵向柱状图)
|
||||
const initCommunityChart = () => {
|
||||
const el = document.getElementById('communityChart');
|
||||
if (!el) return;
|
||||
|
||||
if (chartInstances.value.communityChart) {
|
||||
chartInstances.value.communityChart.dispose();
|
||||
}
|
||||
|
||||
const myChart = echarts.init(el);
|
||||
chartInstances.value.communityChart = myChart;
|
||||
|
||||
const data = communityData.value;
|
||||
|
||||
myChart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'shadow' },
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
||||
borderColor: '#3b82f6',
|
||||
borderWidth: 1,
|
||||
borderRadius: 6,
|
||||
padding: 12,
|
||||
textStyle: { color: '#333', fontSize: 13 },
|
||||
formatter: (params: any) => {
|
||||
const name = params[0].name;
|
||||
const value = params[0].value;
|
||||
const clubs = data.details[name] || [];
|
||||
|
||||
const clubsHtml = clubs.map(c => `• ${c}`).join('<br/>');
|
||||
|
||||
return `
|
||||
<div style="line-height: 2;">
|
||||
<div style="font-weight: bold; color: #3b82f6; margin-bottom: 6px; border-bottom: 2px solid #3b82f6; padding-bottom: 4px;">${name}</div>
|
||||
<div>社团数量: <span style="color: #10b981; font-weight: 700; font-size: 16px;">${value}</span> 个</div>
|
||||
<div style="margin-top: 8px; border-top: 1px solid rgba(0,0,0,0.1); padding-top: 6px; font-size: 11px; color: #666; max-height: 150px; overflow-y: auto;">
|
||||
${clubsHtml}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '10%',
|
||||
right: '10%',
|
||||
bottom: '5%',
|
||||
top: '20%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.universities,
|
||||
axisLabel: {
|
||||
fontSize: 10,
|
||||
rotate: 45,
|
||||
interval: 0,
|
||||
color: '#666',
|
||||
margin: 15
|
||||
},
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
length: 5
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: { color: '#e5e7eb', width: 2 }
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '社团数量',
|
||||
nameTextStyle: { fontSize: 12, color: '#666' },
|
||||
axisLabel: { fontSize: 12, color: '#666' },
|
||||
minInterval: 1,
|
||||
splitLine: {
|
||||
lineStyle: { color: 'rgba(0, 0, 0, 0.06)', type: 'dashed' }
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '社团数量',
|
||||
type: 'bar',
|
||||
data: data.values,
|
||||
barWidth: '60%',
|
||||
barCategoryGap: '30%',
|
||||
itemStyle: {
|
||||
color: (params: any) => {
|
||||
// 根据排名使用不同颜色
|
||||
const colors = [
|
||||
['#3b82f6', '#60a5fa'], // 蓝色
|
||||
['#8b5cf6', '#a78bfa'], // 紫色
|
||||
['#ec4899', '#f472b6'], // 粉色
|
||||
['#10b981', '#34d399'], // 绿色
|
||||
['#f59e0b', '#fbbf24'] // 橙色
|
||||
];
|
||||
const colorPair = colors[params.dataIndex % colors.length];
|
||||
return new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{ offset: 0, color: colorPair[1] },
|
||||
{ offset: 1, color: colorPair[0] }
|
||||
]);
|
||||
},
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
shadowBlur: 8,
|
||||
shadowColor: 'rgba(59, 130, 246, 0.3)'
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: '{c}',
|
||||
fontSize: 11,
|
||||
color: '#3b82f6',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 15,
|
||||
shadowColor: 'rgba(59, 130, 246, 0.6)'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
animationDuration: 1000,
|
||||
animationEasing: 'cubicOut',
|
||||
animationDelay: (idx: number) => idx * 50
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化社区治理健康度对比(纵向柱状图)
|
||||
const initCommunityHealthChart = () => {
|
||||
@@ -1928,13 +1768,11 @@ const initHeatmapChart = () => {
|
||||
const initCharts = () => {
|
||||
// 空数据判断
|
||||
isEmpty.value = {
|
||||
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.community) nextTick(() => initCommunityChart());
|
||||
if (!isEmpty.value.communityHealth) nextTick(() => initCommunityHealthChart());
|
||||
if (!isEmpty.value.languageTech) nextTick(() => initLanguageTechChart());
|
||||
nextTick(() => initVitalityChart());
|
||||
|
||||
Reference in New Issue
Block a user