态势感知平台-增加武汉市开源建设生态页面展示

This commit is contained in:
fmk1023
2026-01-04 15:16:50 +08:00
parent e90006368a
commit 31da7bdea9
3 changed files with 300 additions and 94 deletions

View File

@@ -0,0 +1,119 @@
<template>
<div class="stats-bar-container">
<div class="stats-bar-item">
<span class="stats-number">150+</span>
<span class="stats-label">开源企业</span>
</div>
<div class="stats-divider"></div>
<div class="stats-bar-item">
<span class="stats-number">30+</span>
<span class="stats-label">高校参与</span>
</div>
<div class="stats-divider"></div>
<div class="stats-bar-item">
<span class="stats-number">120+</span>
<span class="stats-label">社区活动</span>
</div>
<div class="stats-divider"></div>
<div class="stats-bar-item">
<span class="stats-number">200+</span>
<span class="stats-label">开源项目</span>
</div>
</div>
</template>
<script>
export default {
name: "StatsBar"
}
</script>
<style lang="scss" scoped>
.stats-bar-container {
/* 基础布局 */
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
width: 100%;
padding: 1.5rem 2rem;
margin-top: 1rem;
gap: 1rem;
/* 玻璃拟态增强 */
background: rgba(255, 255, 255, 0.1); // 降低透明度更显高级
-webkit-backdrop-filter: blur(12px) saturate(180%);
backdrop-filter: blur(12px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 1.25rem;
/* 增加一个非常细微的内阴影,增加厚度感 */
box-shadow: inset 0 0 12px rgba(255, 255, 255, 0.05),
0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
&:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.3);
}
}
.stats-bar-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 100px;
transition: transform 0.3s ease;
&:hover {
transform: translateY(-2px);
}
}
.stats-number {
/* 使用渐变色让数字更有科技感 */
background: #C8161D;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 1.75rem; // 略微调大
font-weight: 800;
letter-spacing: -0.02em;
line-height: 1.2;
}
.stats-label {
/* 颜色改为深灰带点蓝调,或者纯白(如果在深色背景下) */
color: #4b5563;
font-size: 0.875rem;
font-weight: 600;
margin-top: 0.25rem;
opacity: 0.9;
letter-spacing: 0.05em;
}
.stats-divider {
background: linear-gradient(to bottom, transparent, rgba(255,255,255,0.3), transparent);
width: 1px;
height: 2.5rem;
flex-shrink: 0;
/* 在移动端隐藏分割线 */
@media (max-width: 640px) {
display: none;
}
}
/* 响应式微调 */
@media (max-width: 768px) {
.stats-bar-container {
padding: 1rem;
gap: 1.5rem;
}
.stats-number {
font-size: 1.5rem;
}
}
</style>

View File

@@ -8,43 +8,24 @@
<div class="index-module__NV_5cW__floatingElement"></div>
</div>
<div class="index-module__NV_5cW__titleSection">
<div class="index-module__NV_5cW__titleContainer">
<h1 class="index-module__NV_5cW__title">武汉市开源建设生态</h1>
<div class="index-module__NV_5cW__titleBadge">WOS</div>
</div>
<div class="index-module__NV_5cW__subtitle">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-users" aria-hidden="true">
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
<path d="M16 3.128a4 4 0 0 1 0 7.744"></path>
<path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
<circle cx="9" cy="7" r="4"></circle>
</svg>
<span>汇聚武汉开源力量共建城市开源生态</span>
</div>
<!-- 武汉4项核心开源数据统计 -->
<div class="index-module__NV_5cW__statsBar">
<div class="index-module__NV_5cW__stat">
<span class="index-module__NV_5cW__statNumber">120+</span>
<span class="index-module__NV_5cW__statLabel">开源企业</span>
</div>
<div class="index-module__NV_5cW__statDivider"></div>
<div class="index-module__NV_5cW__stat">
<span class="index-module__NV_5cW__statNumber">32+</span>
<span class="index-module__NV_5cW__statLabel">高校参与</span>
</div>
<div class="index-module__NV_5cW__statDivider"></div>
<div class="index-module__NV_5cW__stat">
<span class="index-module__NV_5cW__statNumber">300+</span>
<span class="index-module__NV_5cW__statLabel">社区活动</span>
</div>
<div class="index-module__NV_5cW__statDivider"></div>
<div class="index-module__NV_5cW__stat">
<span class="index-module__NV_5cW__statNumber">200+</span>
<span class="index-module__NV_5cW__statLabel">开源项目</span>
<div class="text-center max-w-4xl mx-auto">
<div class="flex items-center justify-center mb-5">
<div class="title-arrow-left"></div>
<h2 class="text-[32px] md:text-[40px] font-bold text-[#1a1a1a] px-5 tracking-[0.05em] leading-tight">
开源 · 武汉
</h2>
<div class="title-arrow-right"></div>
</div>
<p class="text-[#888888] text-base md:text-lg leading-relaxed max-w-3xl mx-auto">
汇聚武汉开源力量共建城市开源生态
</p>
</div>
<StatsBar />
</div>
</div>
@@ -144,9 +125,9 @@
</div>
</div>
<!-- 7. 武汉市语言与技术竞争趋势折线图近五年-->
<!-- 7. 武汉市语言与技术竞争趋势折线图2020-2024-->
<div class="index-module__NV_5cW__chartCard chartCard__wide">
<h3 class="chartCard__title">武汉市语言与技术竞争趋势2021-2025</h3>
<h3 class="chartCard__title">武汉市编程语言占比趋势2020-2024</h3>
<div class="chartCard__container">
<div class="num-empty" v-if="isEmpty.languageTech"></div>
<div v-else id="languageTechChart" class="chart"></div>
@@ -239,6 +220,7 @@
import { onMounted, ref, nextTick, watch, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import { usePageResize } from '@/utils/hooks/usePageResize';
import StatsBar from './StatsBar.vue'
// 页面尺寸响应式
const { widthType } = usePageResize();
@@ -268,24 +250,26 @@ const excellentProjects2025 = ref([
{ name: 'fountain', desc: '仓颉编程语言实现的JAVA Spring框架的国产替代', org: '江汉大学' }
]);
// 3. 图表数据定义
// 3. 图表数据定义(已更新为新数据)
const chartData = ref({
// 高校开源参与分布(包含华中科大、武大、湖北大学、江汉大学等
// 高校开源参与分布(新的占比数据
university: [
{ value: 30, name: '华中科技大学' },
{ value: 28, name: '华中科技大学' },
{ value: 25, name: '武汉大学' },
{ value: 15, name: '湖北大学' },
{ value: 10, name: '江汉大学' },
{ value: 8, name: '武汉理工大学' },
{ value: 7, name: '华中师范大学' },
{ value: 5, name: '其他高校' }
{ value: 13, name: '江汉大学' },
{ value: 9, name: '湖北大学' },
{ value: 7, name: '武汉理工大学' },
{ value: 6, name: '华中师范大学' },
{ value: 6, name: '武汉科技大学' },
{ value: 5, name: '湖北工业大学' },
{ value: 1, name: '其他' }
],
// 开源社区区域分布
// 开源社区区域分布(行政区+社区数量)
community: {
xAxis: ['武昌区', '洪山区', '东湖高新区', '江区', '汉阳区', '青山区'],
data: [28, 35, 42, 18, 15, 12]
xAxis: ['洪山区', '武昌区', '东湖高新区', '江区', '东西湖区', '青山区'],
data: [22, 11, 9, 5, 2, 1]
},
// OSS Compass开源项目排行提取OpenRank总分左边数值
// OSS Compass开源项目排行保留原有数据
ossCompass: {
yAxis: [
'openharmony/docs',
@@ -314,20 +298,29 @@ const chartData = ref({
{ openRank: '30.37 | 8.57', quality: '37.1 | 4.295', wxb: '35.097 | 6.8', score: '34.19 | 6.55' }
]
},
// 武汉开源开发者排行榜(模拟数据前10名按贡献值排序
// 武汉开源开发者排行榜(新的15位开发者总分数据
developer: {
yAxis: ['张XX', '李XX', '王XX', '刘XX', '陈XX', '杨XX', '黄XX', '赵XX', '吴XX', '周XX'],
data: [980, 850, 720, 650, 580, 520, 480, 420, 380, 350]
yAxis: [
'白纯骢', '陆红旭', '蔡冬亮', '张继德', '蒋周奇',
'张天雄', '阮文俊', '李奇', '李成刚', '陈鹏轩',
'陈天宇', '彭友顺', '杨帆', '江彬', '韩凡宇'
],
data: [92, 88, 85, 83, 81, 79, 77, 75, 74, 73, 72, 70, 68, 65, 63]
},
// 武汉市语言与技术竞争趋势2021-2025多系列
// 武汉市编程语言占比趋势2020-2024
languageTech: {
xAxis: ['2021', '2022', '2023', '2024', '2025'],
xAxis: ['2020', '2021', '2022', '2023', '2024'],
series: [
{ name: 'Java', data: [85, 82, 78, 75, 72] },
{ name: 'Python', data: [78, 80, 85, 88, 90] },
{ name: 'Rust', data: [20, 35, 45, 60, 75] },
{ name: '仓颉', data: [0, 5, 15, 30, 45] },
{ name: 'Go', data: [60, 65, 70, 72, 75] }
{ name: 'JavaScript', data: [15.50, 17.00, 18.50, 19.80, 20.50] },
{ name: 'Python', data: [18.00, 18.50, 17.80, 16.50, 15.20] },
{ name: 'Java', data: [10.00, 11.50, 12.80, 13.50, 14.00] },
{ name: 'TypeScript', data: [16.50, 14.80, 12.50, 10.20, 8.50] },
{ name: 'C#', data: [6.50, 6.80, 7.20, 7.50, 7.80] },
{ name: 'PHP', data: [4.20, 5.00, 5.80, 6.50, 7.00] },
{ name: 'C++', data: [5.50, 5.80, 6.00, 6.30, 6.50] },
{ name: 'Go', data: [6.00, 5.60, 5.30, 4.80, 4.20] },
{ name: 'Rust', data: [5.00, 4.50, 3.80, 3.20, 2.80] },
{ name: 'Kotlin', data: [3.50, 3.20, 3.00, 2.80, 2.50] }
]
}
});
@@ -446,12 +439,11 @@ const initUniversityChart = () => {
formatter: (params: any) => `${params.name}: ${params.value}%`
},
legend: {
// orient: 'vertical',
// left: 'left',
bottom: '0',
textStyle: { fontSize: 12 },
textStyle: { fontSize: 10 }, // 缩小图例字体,适配更多高校
itemWidth: 10,
itemHeight: 10
itemHeight: 10,
type: 'scroll' // 开启图例滚动,避免溢出
},
series: [
{
@@ -462,9 +454,12 @@ const initUniversityChart = () => {
label: {
show: true,
formatter: '{b}: {c}%',
fontSize: 10
fontSize: 9 // 缩小标签字体,适配更多高校
},
color: ['#3b82f6', '#8b5cf6', '#ec4899', '#10b981', '#f59e0b', '#6366f1', '#14b8a6']
color: [
'#3b82f6', '#8b5cf6', '#ec4899', '#10b981', '#f59e0b',
'#6366f1', '#14b8a6', '#f43f5e', '#94a3b8' // 新增颜色匹配9个分类
]
}
],
grid: { left: 0, bottom: 0, top: 0, right: 0 }
@@ -499,7 +494,8 @@ const initCommunityChart = () => {
type: 'value',
name: '社区数量',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
axisLabel: { fontSize: 12 },
min: 0 // 强制y轴从0开始更直观
},
series: [
{
@@ -603,12 +599,13 @@ const initDeveloperChart = () => {
type: 'value',
name: '贡献值',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
axisLabel: { fontSize: 12 },
min: 0 // 强制y轴从0开始
},
yAxis: {
type: 'category',
data: chartData.value.developer.yAxis,
axisLabel: { fontSize: 12 }
axisLabel: { fontSize: 11 } // 缩小字体适配15个开发者
},
series: [
{
@@ -620,7 +617,7 @@ const initDeveloperChart = () => {
show: true,
position: 'right',
formatter: '{c}',
fontSize: 12
fontSize: 11 // 缩小标签字体
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
@@ -633,7 +630,7 @@ const initDeveloperChart = () => {
});
};
// 初始化武汉市语言与技术竞争趋势(折线图)
// 初始化武汉市编程语言占比趋势(折线图)
const initLanguageTechChart = () => {
const el = document.getElementById('languageTechChart');
if (!el) return;
@@ -645,8 +642,11 @@ const initLanguageTechChart = () => {
const myChart = echarts.init(el);
chartInstances.value.languageTechChart = myChart;
// 定义系列颜色
const colorList = ['#3b82f6', '#10b981', '#f59e0b', '#ec4899', '#8b5cf6'];
// 定义系列颜色10个颜色匹配10种语言
const colorList = [
'#3b82f6', '#10b981', '#f59e0b', '#ec4899', '#8b5cf6',
'#6366f1', '#14b8a6', '#f43f5e', '#94a3b8', '#475569'
];
const series = chartData.value.languageTech.series.map((item, index) => ({
name: item.name,
type: 'line',
@@ -660,7 +660,9 @@ const initLanguageTechChart = () => {
{ offset: 0, color: `${colorList[index]}33` },
{ offset: 1, color: `${colorList[index]}08` }
])
}
},
// 隐藏部分系列的图例,避免拥挤(可选,根据需求调整)
// legendHoverLink: index < 5 // 只显示前5个语言的图例交互
}));
myChart.setOption({
@@ -669,16 +671,17 @@ const initLanguageTechChart = () => {
formatter: (params: any) => {
let res = `<div>${params[0].axisValue}</div>`;
params.forEach((p: any) => {
res += `<div>${p.seriesName}: ${p.value}</div>`;
res += `<div>${p.seriesName}: ${p.value}%</div>`;
});
return res;
}
},
legend: {
top: 0,
textStyle: { fontSize: 12 }
textStyle: { fontSize: 10 },
type: 'scroll' // 开启图例滚动适配10种语言
},
grid: { left: '3%', right: '4%', bottom: '3%', top: '10%', containLabel: true },
grid: { left: '3%', right: '4%', bottom: '3%', top: '15%', containLabel: true },
xAxis: {
type: 'category',
data: chartData.value.languageTech.xAxis,
@@ -686,9 +689,14 @@ const initLanguageTechChart = () => {
},
yAxis: {
type: 'value',
name: '使用占比/热度',
name: '占比(%',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
axisLabel: {
fontSize: 12,
formatter: '{value}%' // 显示百分比符号
},
min: 0,
max: 25 // 限制y轴最大值更直观
},
series
});
@@ -734,7 +742,7 @@ onMounted(() => {
<style lang="scss" scoped>
.index-module__NV_5cW__container {
background: linear-gradient(to bottom right, #faf5ff, #60a5fa);
//background: linear-gradient(to bottom right, #faf5ff, #60a5fa);
min-height: 100vh;
padding: 2rem 1rem;
position: relative;
@@ -801,6 +809,7 @@ onMounted(() => {
}
}
// 加宽标题区域移除max-width限制调整宽度为100%,增加内边距
.index-module__NV_5cW__titleSection {
color: #333;
z-index: 2;
@@ -809,6 +818,10 @@ onMounted(() => {
gap: 1rem;
display: flex;
position: relative;
width: 100%;
max-width: 1200px; // 增加最大宽度,适配大屏
margin: 0 auto; // 居中显示
padding: 0 2rem; // 增加左右内边距
}
.index-module__NV_5cW__titleContainer {
@@ -817,6 +830,7 @@ onMounted(() => {
align-items: center;
gap: 1rem;
display: flex;
width: 100%; // 宽度100%
}
.index-module__NV_5cW__title {
@@ -848,12 +862,15 @@ onMounted(() => {
text-align: center;
align-items: center;
gap: 0.5rem;
max-width: 500px;
max-width: 800px; // 加宽副标题最大宽度
font-size: 1.1rem;
font-weight: 400;
display: flex;
width: 100%; // 宽度100%
justify-content: center; // 居中
}
// 加宽数据统计栏移除max-width限制调整为100%宽度,适配大屏
.index-module__NV_5cW__statsBar {
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
@@ -861,12 +878,14 @@ onMounted(() => {
border: 1px solid #fff3;
border-radius: 1.5rem;
align-items: center;
gap: 1.5rem;
width: fit-content;
max-width: 90%;
gap: 2rem; // 增加间距,更舒展
width: 100%; // 宽度100%
max-width: 100%; // 移除最大宽度限制
margin-top: 0.75rem;
padding: 0.75rem 1.5rem;
padding: 1rem 2rem; // 增加内边距
display: flex;
justify-content: center; // 居中分布
flex-wrap: wrap; // 移动端自动换行
}
.index-module__NV_5cW__stat {
@@ -874,6 +893,8 @@ onMounted(() => {
align-items: center;
gap: 0.25rem;
display: flex;
flex: 1; // 平均分配宽度
min-width: 80px; // 最小宽度,防止挤压
}
.index-module__NV_5cW__statNumber {
@@ -1204,18 +1225,15 @@ onMounted(() => {
padding: 1.5rem 1rem;
}
// 移动端数据统计栏调整
.index-module__NV_5cW__statsBar {
flex-wrap: nowrap;
justify-content: space-between;
gap: 0.8rem;
width: 100%;
max-width: 100%;
padding: 0.8rem 1.2rem;
gap: 1rem;
padding: 0.8rem 1rem;
flex-wrap: wrap;
}
.index-module__NV_5cW__stat {
text-align: center;
flex: 1;
flex: 1 1 60px;
min-width: 60px;
}
@@ -1277,6 +1295,12 @@ onMounted(() => {
max-width: 100%;
margin-top: 0.2rem;
}
// 移动端标题区域调整
.index-module__NV_5cW__titleSection {
padding: 0 1rem;
max-width: 100%;
}
}
@media (max-width: 480px) {
@@ -1319,4 +1343,66 @@ onMounted(() => {
max-height: 300px;
}
}
/* 绘制标题左侧红色箭头(尖端指向文字) */
.title-arrow-left {
width: 0;
height: 0;
border-top: 9px solid transparent;
border-bottom: 9px solid transparent;
border-left: 12px solid #C8161D; /* 使用标准的实验室红色值 */
}
/* 绘制标题右侧红色箭头(尖端指向文字) */
.title-arrow-right {
width: 0;
height: 0;
border-top: 9px solid transparent;
border-bottom: 9px solid transparent;
border-right: 12px solid #C8161D;
}
/* 字体美化:确保标题厚重稳重 */
h2 {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
/* 针对标签内文字的微调 */
span {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.more-btn {
display: inline-block;
background-color: #C8161D; /* 品牌红 */
color: #ffffff;
font-size: 14px;
font-weight: 600;
padding: 10px 38px;
border-radius: 6px;
letter-spacing: 0.5px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 12px rgba(200, 22, 29, 0.15);
&:hover {
background-color: #a31218;
transform: translateY(-2px);
box-shadow: 0 6px 18px rgba(200, 22, 29, 0.25);
}
}
/* 文本截断 */
.line-clamp-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
/* 字体优化 */
h3, .more-btn {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Microsoft YaHei", sans-serif;
}
</style>

View File

@@ -43,7 +43,8 @@ const activeTab = ref('risk');
const tabs = [
{ id: 'risk', label: '全球开源风险态势感知监控中心', icon: 'fas fa-shield-virus' },
{ id: 'eco', label: '开源生态与贡献价值全景', icon: 'fas fa-chart-network' }
{ id: 'eco', label: '开源生态与贡献价值全景', icon: 'fas fa-chart-network' },
{ id: 'whky', label: '开源武汉', icon: 'fas fa-chart-network' },
];
const isFixed = ref(false);