diff --git a/src/views/Jyh/ecosystem/components/UniversityClubStatsChart.vue b/src/views/Jyh/ecosystem/components/UniversityClubStatsChart.vue new file mode 100644 index 0000000..8a30ffa --- /dev/null +++ b/src/views/Jyh/ecosystem/components/UniversityClubStatsChart.vue @@ -0,0 +1,364 @@ + + + + + \ No newline at end of file diff --git a/src/views/Jyh/ecosystem/index.vue b/src/views/Jyh/ecosystem/index.vue index 76e546f..ff5232a 100644 --- a/src/views/Jyh/ecosystem/index.vue +++ b/src/views/Jyh/ecosystem/index.vue @@ -134,13 +134,7 @@ -
-

高校开源社团/俱乐部数量

-
-
-
-
-
+
@@ -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('
'); - - return ` -
-
${name}
-
社团数量: ${value}
-
- ${clubsHtml} -
-
- `; - } - }, - 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());