Tcode运营平台改造升级-中国地图配置
This commit is contained in:
@@ -281,21 +281,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span>🌍 中国开源人才密度地图</span>
|
<span>🌍 中国开源人才密度地图</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="map-container">
|
<TalentMapChart />
|
||||||
<div id="talentMapChart" class="chart"></div>
|
|
||||||
|
|
||||||
<!-- 右侧排行榜 -->
|
|
||||||
<div class="map-rank-panel">
|
|
||||||
<div class="panel-title">🏆 开源人才数量排行 TOP 5</div>
|
|
||||||
<div class="rank-list">
|
|
||||||
<div class="rank-item" v-for="(item, index) in topRegions" :key="index">
|
|
||||||
<span :class="['rank-num', 'rank-' + (index + 1)]">{{ index + 1 }}</span>
|
|
||||||
<span class="rank-name">{{ item.name }}</span>
|
|
||||||
<span class="rank-score">{{ item.score }} 万人</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -304,6 +290,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, nextTick, watch, onUnmounted, computed } from 'vue';
|
import { onMounted, ref, nextTick, watch, onUnmounted, computed } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
|
import TalentMapChart from './TalentMapChart.vue'
|
||||||
import { usePageResize } from '@/utils/hooks/usePageResize';
|
import { usePageResize } from '@/utils/hooks/usePageResize';
|
||||||
|
|
||||||
// 页面尺寸响应式
|
// 页面尺寸响应式
|
||||||
@@ -651,57 +638,6 @@ const toggleLabels = () => {
|
|||||||
initHeatmapChart();
|
initHeatmapChart();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 中国开源人才密度地图数据
|
|
||||||
const talentMapData = ref([
|
|
||||||
{ name: '北京市', value: [116.4074, 39.9042, 141.15, 231.47] },
|
|
||||||
{ name: '上海市', value: [121.4737, 31.2304, 269.83, 169.99] },
|
|
||||||
{ name: '浙江省', value: [120.1551, 30.2794, 8.94, 90.98] },
|
|
||||||
{ name: '广东省', value: [113.2644, 23.1291, 7.5, 149.76] },
|
|
||||||
{ name: '台湾省', value: [121.5090, 25.0443, 32.75, 117.88] },
|
|
||||||
{ name: '四川省', value: [104.0657, 30.6595, 1.01, 48.7] },
|
|
||||||
{ name: '江苏省', value: [118.7780, 32.0472, 5.25, 56.28] },
|
|
||||||
{ name: '湖北省', value: [114.3053, 30.5928, 1.84, 34.09] },
|
|
||||||
{ name: '福建省', value: [119.3062, 26.0747, 1.39, 16.79] },
|
|
||||||
{ name: '陕西省', value: [108.9480, 34.2632, 1.04, 21.38] },
|
|
||||||
{ name: '重庆市', value: [106.5049, 29.5332, 1.61, 13.24] },
|
|
||||||
{ name: '山东省', value: [120.3331, 36.0670, 1.02, 16.02] },
|
|
||||||
{ name: '湖南省', value: [112.9822, 28.1944, 0.68, 14.25] },
|
|
||||||
{ name: '安徽省', value: [117.2830, 31.8611, 0.93, 12.92] },
|
|
||||||
{ name: '辽宁省', value: [123.4321, 41.8007, 0.77, 11.37] },
|
|
||||||
{ name: '云南省', value: [102.7123, 25.0406, 0.1, 3.68] },
|
|
||||||
{ name: '天津市', value: [117.1901, 39.1258, 7.79, 9.34] },
|
|
||||||
{ name: '河南省', value: [113.6654, 34.7579, 0.55, 9.07] },
|
|
||||||
{ name: '吉林省', value: [125.3245, 43.8171, 0.15, 2.7] },
|
|
||||||
{ name: '江西省', value: [115.8581, 28.6820, 0.25, 4.17] },
|
|
||||||
{ name: '广西壮族自治区', value: [108.3200, 22.8241, 0.14, 3.15] },
|
|
||||||
{ name: '黑龙江省', value: [126.5349, 45.8038, 0.11, 5.13] },
|
|
||||||
{ name: '澳门特别行政区', value: [113.5490, 22.1987, 737.5, 2.36] },
|
|
||||||
{ name: '河北省', value: [114.5024, 38.0454, 0.26, 4.74] },
|
|
||||||
{ name: '山西省', value: [112.5489, 37.8570, 0.25, 3.78] },
|
|
||||||
{ name: '贵州省', value: [106.7135, 26.5783, 0.11, 1.93] },
|
|
||||||
{ name: '内蒙古自治区', value: [111.6708, 40.8183, 0.01, 0.96] },
|
|
||||||
{ name: '香港特别行政区', value: [114.1095, 22.3964, 1797.7, 197.75] },
|
|
||||||
{ name: '新疆维吾尔自治区', value: [87.6168, 43.7928, 0.01, 0.77] },
|
|
||||||
{ name: '甘肃省', value: [103.8235, 36.0580, 0.04, 1.46] },
|
|
||||||
{ name: '海南省', value: [110.3312, 20.0319, 0.36, 1.25] },
|
|
||||||
{ name: '宁夏回族自治区', value: [106.2782, 38.4664, 0.05, 0.27] },
|
|
||||||
{ name: '西藏自治区', value: [91.1175, 29.6469, 0.01, 0.14] },
|
|
||||||
{ name: '青海省', value: [101.7782, 36.6172, 0.01, 0.18] }
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 计算 TOP 5 地区
|
|
||||||
const topRegions = computed(() => {
|
|
||||||
const sorted = [...talentMapData.value]
|
|
||||||
.map(item => ({
|
|
||||||
name: item.name,
|
|
||||||
score: item.value[3],
|
|
||||||
density: item.value[2]
|
|
||||||
}))
|
|
||||||
.sort((a, b) => b.score - a.score);
|
|
||||||
|
|
||||||
return sorted.slice(0, 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取人才等级
|
// 获取人才等级
|
||||||
const getTalentLevel = (density: number) => {
|
const getTalentLevel = (density: number) => {
|
||||||
if (density >= 500) return '🔥 核心枢纽';
|
if (density >= 500) return '🔥 核心枢纽';
|
||||||
@@ -717,8 +653,7 @@ const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({
|
|||||||
communityHealthChart: null,
|
communityHealthChart: null,
|
||||||
languageTechChart: null,
|
languageTechChart: null,
|
||||||
vitalityChart: null,
|
vitalityChart: null,
|
||||||
heatmapChart: null,
|
heatmapChart: null
|
||||||
talentMapChart: null
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===================== 新增:列表自动滚动逻辑 =====================
|
// ===================== 新增:列表自动滚动逻辑 =====================
|
||||||
@@ -2143,183 +2078,6 @@ const initHeatmapChart = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始化中国开源人才密度地图
|
|
||||||
const initTalentMapChart = () => {
|
|
||||||
const el = document.getElementById('talentMapChart');
|
|
||||||
if (!el) return;
|
|
||||||
|
|
||||||
if (chartInstances.value.talentMapChart) {
|
|
||||||
chartInstances.value.talentMapChart.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
const myChart = echarts.init(el);
|
|
||||||
chartInstances.value.talentMapChart = myChart;
|
|
||||||
|
|
||||||
// 注册中国地图(科技蓝风格)
|
|
||||||
// 尝试导入本地地图文件,如果失败则从 CDN 加载
|
|
||||||
import('@/views/Jyh/ecosystem/json/china.js')
|
|
||||||
.then(() => {
|
|
||||||
console.log('本地地图文件加载成功');
|
|
||||||
// 延迟一下确保地图注册完成
|
|
||||||
setTimeout(() => {
|
|
||||||
renderMap();
|
|
||||||
}, 100);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.warn('本地地图文件加载失败,尝试从 CDN 加载:', err);
|
|
||||||
// 从 CDN 加载地图数据
|
|
||||||
fetch('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(geoJson => {
|
|
||||||
echarts.registerMap('china', geoJson);
|
|
||||||
renderMap();
|
|
||||||
})
|
|
||||||
.catch(cdnErr => {
|
|
||||||
console.error('CDN 地图数据加载失败:', cdnErr);
|
|
||||||
el.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #666; font-size: 14px;">地图数据加载失败,请刷新页面重试</div>';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 渲染地图的函数
|
|
||||||
function renderMap() {
|
|
||||||
myChart.setOption({
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
backgroundColor: 'rgba(17, 54, 122, 0.95)',
|
|
||||||
borderColor: '#00A0FF',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderRadius: 8,
|
|
||||||
padding: 16,
|
|
||||||
textStyle: { color: '#6dd0e3', fontSize: 14 },
|
|
||||||
extraCssText: 'box-shadow: 0 4px 16px rgba(0, 160, 255, 0.4);',
|
|
||||||
formatter: (params: any) => {
|
|
||||||
if (params.componentSubType === 'scatter') {
|
|
||||||
const data = params.data.value;
|
|
||||||
const name = params.name;
|
|
||||||
const density = data[2];
|
|
||||||
const total = data[3];
|
|
||||||
const level = getTalentLevel(density);
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div style="line-height: 2;">
|
|
||||||
<div style="font-weight: bold; color: #00F2FF; font-size: 16px; margin-bottom: 10px;">${name}</div>
|
|
||||||
<div style="margin-bottom: 6px;">
|
|
||||||
<span style="color: #6dd0e3;">开源人才密度:</span>
|
|
||||||
<span style="color: #00C0FF; font-weight: 700; font-size: 15px;">${density}</span> 人/km²
|
|
||||||
</div>
|
|
||||||
<div style="margin-bottom: 6px;">
|
|
||||||
<span style="color: #6dd0e3;">开源人才数量:</span>
|
|
||||||
<span style="color: #00C0FF; font-weight: 700; font-size: 15px;">${total}</span> 万人
|
|
||||||
</div>
|
|
||||||
<div style="margin-top: 8px; padding: 4px 10px; background: rgba(0, 242, 255, 0.2); border-radius: 6px; color: #00F2FF; font-size: 12px; font-weight: 600;">
|
|
||||||
${level}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
visualMap: {
|
|
||||||
show: false,
|
|
||||||
min: 0,
|
|
||||||
max: 500,
|
|
||||||
inRange: {
|
|
||||||
color: ['#00A0FF', '#0063D0', '#004190'],
|
|
||||||
symbolSize: [10, 60]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
geo: {
|
|
||||||
map: 'china',
|
|
||||||
roam: true,
|
|
||||||
scaleLimit: {
|
|
||||||
min: 0.8,
|
|
||||||
max: 4
|
|
||||||
},
|
|
||||||
zoom: 2.1,
|
|
||||||
center: [105, 38],
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
fontSize: 10,
|
|
||||||
color: '#CEF4FD',
|
|
||||||
emphasis: {
|
|
||||||
color: '#00F2FF',
|
|
||||||
fontSize: 11,
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
areaColor: 'rgba(0, 30, 80, 0.6)',
|
|
||||||
borderColor: 'rgba(0, 160, 255, 0.5)',
|
|
||||||
borderWidth: 1.5,
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowColor: 'rgba(0, 160, 255, 0.3)'
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
areaColor: 'rgba(0, 50, 120, 0.8)',
|
|
||||||
borderColor: '#00C0FF',
|
|
||||||
borderWidth: 2,
|
|
||||||
shadowBlur: 15,
|
|
||||||
shadowColor: 'rgba(0, 192, 255, 0.6)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '开源人才密度',
|
|
||||||
type: 'scatter',
|
|
||||||
coordinateSystem: 'geo',
|
|
||||||
data: talentMapData.value,
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: (val: any) => {
|
|
||||||
const density = val[2];
|
|
||||||
return Math.sqrt(density) * 1.5 + 8;
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params: any) => {
|
|
||||||
const density = params.value[2];
|
|
||||||
if (density >= 500) return '#FF4900';
|
|
||||||
if (density >= 100) return '#FFAB00';
|
|
||||||
if (density >= 10) return '#00C0FF';
|
|
||||||
return '#00A0FF';
|
|
||||||
},
|
|
||||||
shadowBlur: 25,
|
|
||||||
shadowColor: 'rgba(0, 242, 255, 0.6)',
|
|
||||||
opacity: 0.85
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 35,
|
|
||||||
shadowColor: 'rgba(0, 242, 255, 1)',
|
|
||||||
scale: 1.3,
|
|
||||||
opacity: 1
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
formatter: '{b}',
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
backgroundColor: 'rgba(0, 192, 255, 0.95)',
|
|
||||||
color: '#fff',
|
|
||||||
borderColor: '#00F2FF',
|
|
||||||
borderWidth: 2,
|
|
||||||
padding: [5, 10],
|
|
||||||
borderRadius: 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
animationType: 'scale',
|
|
||||||
animationEasing: 'elasticOut',
|
|
||||||
animationDelay: (idx: number) => idx * 50
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 统一初始化图表
|
// 统一初始化图表
|
||||||
const initCharts = () => {
|
const initCharts = () => {
|
||||||
// 空数据判断
|
// 空数据判断
|
||||||
@@ -2337,7 +2095,6 @@ const initCharts = () => {
|
|||||||
if (!isEmpty.value.languageTech) nextTick(() => initLanguageTechChart());
|
if (!isEmpty.value.languageTech) nextTick(() => initLanguageTechChart());
|
||||||
nextTick(() => initVitalityChart());
|
nextTick(() => initVitalityChart());
|
||||||
nextTick(() => initHeatmapChart());
|
nextTick(() => initHeatmapChart());
|
||||||
nextTick(() => initTalentMapChart());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 页面resize时重绘图表
|
// 页面resize时重绘图表
|
||||||
@@ -3368,152 +3125,7 @@ onMounted(() => {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map-container {
|
|
||||||
display: flex;
|
|
||||||
gap: 2rem;
|
|
||||||
align-items: stretch;
|
|
||||||
position: relative;
|
|
||||||
min-height: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-container .chart {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 600px;
|
|
||||||
background: linear-gradient(135deg,
|
|
||||||
rgba(0, 20, 60, 0.4) 0%,
|
|
||||||
rgba(0, 40, 100, 0.3) 100%);
|
|
||||||
border-radius: 1rem;
|
|
||||||
border: 2px solid rgba(0, 192, 255, 0.3);
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 192, 255, 0.2);
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-rank-panel {
|
|
||||||
width: 300px;
|
|
||||||
border-radius: 1rem;
|
|
||||||
border: 2px solid rgba(0, 192, 255, 0.3);
|
|
||||||
padding: 1.5rem;
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 192, 255, 0.2);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-title {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: black;
|
|
||||||
text-align: center;
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
border-bottom: 2px solid rgba(0, 242, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.75rem;
|
|
||||||
max-height: 400px;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
padding: 0.85rem 1rem;
|
|
||||||
background: rgba(0, 192, 255, 0.08);
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
border: 1px solid rgba(0, 192, 255, 0.2);
|
|
||||||
border-left: 3px solid #00A0FF;
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-item::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: -100%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg, transparent, rgba(0, 242, 255, 0.15), transparent);
|
|
||||||
transition: left 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-item:hover::before {
|
|
||||||
left: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-item:hover {
|
|
||||||
background: rgba(0, 192, 255, 0.2);
|
|
||||||
border-color: #00F2FF;
|
|
||||||
border-left-color: #00F2FF;
|
|
||||||
box-shadow: 0 6px 16px rgba(0, 242, 255, 0.3);
|
|
||||||
transform: translateX(5px) scale(1.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-num {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 700;
|
|
||||||
flex-shrink: 0;
|
|
||||||
font-family: 'DIN Alternate', 'Arial', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-num.rank-1 {
|
|
||||||
background: linear-gradient(135deg, #FFD700, #FFA500);
|
|
||||||
color: #1a1a1a;
|
|
||||||
box-shadow: 0 4px 12px rgba(255, 215, 0, 0.6);
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-num.rank-2 {
|
|
||||||
background: linear-gradient(135deg, #E8E8E8, #C0C0C0);
|
|
||||||
color: #1a1a1a;
|
|
||||||
box-shadow: 0 4px 12px rgba(192, 192, 192, 0.6);
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-num.rank-3 {
|
|
||||||
background: linear-gradient(135deg, #CD7F32, #B8733C);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 4px 12px rgba(205, 127, 50, 0.6);
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-num.rank-4,
|
|
||||||
.rank-num.rank-5 {
|
|
||||||
background: rgba(0, 192, 255, 0.2);
|
|
||||||
color: #00C0FF;
|
|
||||||
font-weight: bold;
|
|
||||||
border: 2px solid rgba(0, 192, 255, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-name {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
//color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rank-score {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #00F2FF;
|
|
||||||
font-family: 'DIN Alternate', 'Arial', sans-serif;
|
|
||||||
text-shadow: 0 0 8px rgba(0, 242, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 热力图特殊样式 */
|
/* 热力图特殊样式 */
|
||||||
.heatmap-section {
|
.heatmap-section {
|
||||||
|
|||||||
1
src/views/Jyh/ecosystem/json/china.json
Normal file
1
src/views/Jyh/ecosystem/json/china.json
Normal file
File diff suppressed because one or more lines are too long
347
src/views/Jyh/ecosystem/talentMapChart.vue
Normal file
347
src/views/Jyh/ecosystem/talentMapChart.vue
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
<template>
|
||||||
|
<div class="map-container">
|
||||||
|
<div id="talentMapChart" class="chart"></div>
|
||||||
|
|
||||||
|
<!-- 右侧排行榜 -->
|
||||||
|
<div class="map-rank-panel">
|
||||||
|
<div class="panel-title">🏆 开源人才数量排行 TOP 5</div>
|
||||||
|
<div class="rank-list">
|
||||||
|
<div class="rank-item" v-for="(item, index) in topRegions" :key="index">
|
||||||
|
<span :class="['rank-num', 'rank-' + (index + 1)]">{{ index + 1 }}</span>
|
||||||
|
<span class="rank-name">{{ item.name }}</span>
|
||||||
|
<span class="rank-score">{{ item.score }} 万人</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {onMounted, ref, onBeforeUnmount, computed} from 'vue';
|
||||||
|
|
||||||
|
// 初始化中国开源人才密度地图
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
import geoJson from './json/china.json';
|
||||||
|
|
||||||
|
const talentMapChart = ref()
|
||||||
|
|
||||||
|
|
||||||
|
// 中国开源人才密度地图数据
|
||||||
|
const talentMapData = ref([
|
||||||
|
{ name: '北京市', value: [116.4074, 39.9042, 141.15, 231.47] },
|
||||||
|
{ name: '上海市', value: [121.4737, 31.2304, 269.83, 169.99] },
|
||||||
|
{ name: '浙江省', value: [120.1551, 30.2794, 8.94, 90.98] },
|
||||||
|
{ name: '广东省', value: [113.2644, 23.1291, 7.5, 149.76] },
|
||||||
|
{ name: '台湾省', value: [121.5090, 25.0443, 32.75, 117.88] },
|
||||||
|
{ name: '四川省', value: [104.0657, 30.6595, 1.01, 48.7] },
|
||||||
|
{ name: '江苏省', value: [118.7780, 32.0472, 5.25, 56.28] },
|
||||||
|
{ name: '湖北省', value: [114.3053, 30.5928, 1.84, 34.09] },
|
||||||
|
{ name: '福建省', value: [119.3062, 26.0747, 1.39, 16.79] },
|
||||||
|
{ name: '陕西省', value: [108.9480, 34.2632, 1.04, 21.38] },
|
||||||
|
{ name: '重庆市', value: [106.5049, 29.5332, 1.61, 13.24] },
|
||||||
|
{ name: '山东省', value: [120.3331, 36.0670, 1.02, 16.02] },
|
||||||
|
{ name: '湖南省', value: [112.9822, 28.1944, 0.68, 14.25] },
|
||||||
|
{ name: '安徽省', value: [117.2830, 31.8611, 0.93, 12.92] },
|
||||||
|
{ name: '辽宁省', value: [123.4321, 41.8007, 0.77, 11.37] },
|
||||||
|
{ name: '云南省', value: [102.7123, 25.0406, 0.1, 3.68] },
|
||||||
|
{ name: '天津市', value: [117.1901, 39.1258, 7.79, 9.34] },
|
||||||
|
{ name: '河南省', value: [113.6654, 34.7579, 0.55, 9.07] },
|
||||||
|
{ name: '吉林省', value: [125.3245, 43.8171, 0.15, 2.7] },
|
||||||
|
{ name: '江西省', value: [115.8581, 28.6820, 0.25, 4.17] },
|
||||||
|
{ name: '广西壮族自治区', value: [108.3200, 22.8241, 0.14, 3.15] },
|
||||||
|
{ name: '黑龙江省', value: [126.5349, 45.8038, 0.11, 5.13] },
|
||||||
|
{ name: '澳门特别行政区', value: [113.5490, 22.1987, 737.5, 2.36] },
|
||||||
|
{ name: '河北省', value: [114.5024, 38.0454, 0.26, 4.74] },
|
||||||
|
{ name: '山西省', value: [112.5489, 37.8570, 0.25, 3.78] },
|
||||||
|
{ name: '贵州省', value: [106.7135, 26.5783, 0.11, 1.93] },
|
||||||
|
{ name: '内蒙古自治区', value: [111.6708, 40.8183, 0.01, 0.96] },
|
||||||
|
{ name: '香港特别行政区', value: [114.1095, 22.3964, 1797.7, 197.75] },
|
||||||
|
{ name: '新疆维吾尔自治区', value: [87.6168, 43.7928, 0.01, 0.77] },
|
||||||
|
{ name: '甘肃省', value: [103.8235, 36.0580, 0.04, 1.46] },
|
||||||
|
{ name: '海南省', value: [110.3312, 20.0319, 0.36, 1.25] },
|
||||||
|
{ name: '宁夏回族自治区', value: [106.2782, 38.4664, 0.05, 0.27] },
|
||||||
|
{ name: '西藏自治区', value: [91.1175, 29.6469, 0.01, 0.14] },
|
||||||
|
{ name: '青海省', value: [101.7782, 36.6172, 0.01, 0.18] }
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 计算 TOP 5 地区
|
||||||
|
const topRegions = computed(() => {
|
||||||
|
const sorted = [...talentMapData.value]
|
||||||
|
.map(item => ({
|
||||||
|
name: item.name,
|
||||||
|
score: item.value[3],
|
||||||
|
density: item.value[2]
|
||||||
|
}))
|
||||||
|
.sort((a, b) => b.score - a.score);
|
||||||
|
|
||||||
|
return sorted.slice(0, 5);
|
||||||
|
});
|
||||||
|
// 挂载时初始化
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
initTalentMapChart()
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
const initTalentMapChart = () => {
|
||||||
|
const el = document.getElementById('talentMapChart');
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
if (talentMapChart.value) {
|
||||||
|
talentMapChart.value.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
const myChart = echarts.init(el);
|
||||||
|
talentMapChart.value = myChart;
|
||||||
|
|
||||||
|
// --- 修改后的加载逻辑:直接获取 JSON ---
|
||||||
|
// 这里可以换成你本地项目的路径,例如:'/maps/china.json'
|
||||||
|
const MAP_JSON_URL = 'https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json';
|
||||||
|
|
||||||
|
// 1. 注册地图数据
|
||||||
|
echarts.registerMap('china', geoJson);
|
||||||
|
|
||||||
|
// 2. 调用渲染函数
|
||||||
|
renderMap();
|
||||||
|
|
||||||
|
// 渲染地图的函数 (配置保持原有的科技感风格)
|
||||||
|
function renderMap() {
|
||||||
|
myChart.setOption({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
backgroundColor: 'rgba(17, 54, 122, 0.95)',
|
||||||
|
borderColor: '#00A0FF',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: 16,
|
||||||
|
textStyle: { color: '#6dd0e3', fontSize: 14 },
|
||||||
|
formatter: (params: any) => {
|
||||||
|
if (params.seriesType === 'scatter') {
|
||||||
|
const data = params.data.value;
|
||||||
|
const density = data[2];
|
||||||
|
const total = data[3];
|
||||||
|
// 内部辅助函数获取等级
|
||||||
|
const getLevel = (d: number) => {
|
||||||
|
if(d > 500) return '人才极高密度区';
|
||||||
|
if(d > 100) return '人才高密度区';
|
||||||
|
return '人才稳步增长区';
|
||||||
|
};
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div style="line-height: 2;">
|
||||||
|
<div style="font-weight: bold; color: #00F2FF; font-size: 16px; margin-bottom: 10px;">${params.name}</div>
|
||||||
|
<div><span style="color: #6dd0e3;">人才密度:</span><span style="color: #00C0FF; font-weight: 700;">${density}</span> 人/km²</div>
|
||||||
|
<div><span style="color: #6dd0e3;">人才总量:</span><span style="color: #00C0FF; font-weight: 700;">${total}</span> 万人</div>
|
||||||
|
<div style="margin-top: 8px; padding: 4px 10px; background: rgba(0, 242, 255, 0.2); border-radius: 6px; color: #00F2FF; font-size: 12px;">${getLevel(density)}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
show: false,
|
||||||
|
min: 0,
|
||||||
|
max: 500,
|
||||||
|
inRange: {
|
||||||
|
color: ['#00A0FF', '#0063D0', '#004190'],
|
||||||
|
symbolSize: [10, 60]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
geo: {
|
||||||
|
map: 'china',
|
||||||
|
roam: true,
|
||||||
|
scaleLimit: { min: 0.8, max: 4 },
|
||||||
|
zoom: 2.6, // 调整为 standard zoom
|
||||||
|
center: [104.5, 37.5],
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 10,
|
||||||
|
color: '#CEF4FD'
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: 'rgba(0, 30, 80, 0.6)',
|
||||||
|
borderColor: 'rgba(0, 160, 255, 0.5)',
|
||||||
|
borderWidth: 1.5,
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowColor: 'rgba(0, 160, 255, 0.3)'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: 'rgba(0, 50, 120, 0.8)',
|
||||||
|
borderColor: '#00C0FF'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '开源人才密度',
|
||||||
|
type: 'scatter',
|
||||||
|
coordinateSystem: 'geo',
|
||||||
|
data: talentMapData.value,
|
||||||
|
symbolSize: (val: any) => Math.sqrt(val[2]) * 2 + 8,
|
||||||
|
itemStyle: {
|
||||||
|
color: (params: any) => {
|
||||||
|
const density = params.value[2];
|
||||||
|
if (density >= 500) return '#FF4900';
|
||||||
|
if (density >= 100) return '#FFAB00';
|
||||||
|
return '#00C0FF';
|
||||||
|
},
|
||||||
|
shadowBlur: 20,
|
||||||
|
shadowColor: 'rgba(0, 242, 255, 0.6)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.map-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
align-items: stretch;
|
||||||
|
position: relative;
|
||||||
|
min-height: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-container .chart {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 600px;
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgba(0, 20, 60, 0.4) 0%,
|
||||||
|
rgba(0, 40, 100, 0.3) 100%);
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: 2px solid rgba(0, 192, 255, 0.3);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 192, 255, 0.2);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-rank-panel {
|
||||||
|
width: 300px;
|
||||||
|
border-radius: 1rem;
|
||||||
|
border: 2px solid rgba(0, 192, 255, 0.3);
|
||||||
|
padding: 1.5rem;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 192, 255, 0.2);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: black;
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
border-bottom: 2px solid rgba(0, 242, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.85rem 1rem;
|
||||||
|
background: rgba(0, 192, 255, 0.08);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
border: 1px solid rgba(0, 192, 255, 0.2);
|
||||||
|
border-left: 3px solid #00A0FF;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-item::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(0, 242, 255, 0.15), transparent);
|
||||||
|
transition: left 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-item:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-item:hover {
|
||||||
|
background: rgba(0, 192, 255, 0.2);
|
||||||
|
border-color: #00F2FF;
|
||||||
|
border-left-color: #00F2FF;
|
||||||
|
box-shadow: 0 6px 16px rgba(0, 242, 255, 0.3);
|
||||||
|
transform: translateX(5px) scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-num {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-family: 'DIN Alternate', 'Arial', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-num.rank-1 {
|
||||||
|
background: linear-gradient(135deg, #FFD700, #FFA500);
|
||||||
|
color: #1a1a1a;
|
||||||
|
box-shadow: 0 4px 12px rgba(255, 215, 0, 0.6);
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-num.rank-2 {
|
||||||
|
background: linear-gradient(135deg, #E8E8E8, #C0C0C0);
|
||||||
|
color: #1a1a1a;
|
||||||
|
box-shadow: 0 4px 12px rgba(192, 192, 192, 0.6);
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-num.rank-3 {
|
||||||
|
background: linear-gradient(135deg, #CD7F32, #B8733C);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 12px rgba(205, 127, 50, 0.6);
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-num.rank-4,
|
||||||
|
.rank-num.rank-5 {
|
||||||
|
background: rgba(0, 192, 255, 0.2);
|
||||||
|
color: #00C0FF;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 2px solid rgba(0, 192, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rank-score {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #00F2FF;
|
||||||
|
font-family: 'DIN Alternate', 'Arial', sans-serif;
|
||||||
|
text-shadow: 0 0 8px rgba(0, 242, 255, 0.5);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user