Files
Situation-Awareness-Platfor…/src/views/Jyh/security/index.vue

1140 lines
29 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="index-module__NV_5cW__container">
<!-- 头部区域标题副标题武汉核心数据统计 -->
<div class="index-module__NV_5cW__header">
<div class="index-module__NV_5cW__floatingElements">
<div class="index-module__NV_5cW__floatingElement"></div>
<div class="index-module__NV_5cW__floatingElement"></div>
<div class="index-module__NV_5cW__floatingElement"></div>
</div>
<StatsGlass />
</div>
<!-- 内容区域ECharts图表模块 + 项目列表 -->
<div class="index-module__NV_5cW__content">
<!-- ECharts图表区域 -->
<div class="index-module__NV_5cW__chartsSection">
<!-- <div class="index-module__NV_5cW__sectionHeader">-->
<!-- <h2 class="index-module__NV_5cW__sectionTitle">-->
<!-- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"-->
<!-- stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"-->
<!-- class="lucide lucide-chart-pie" aria-hidden="true">-->
<!-- <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>-->
<!-- <circle cx="12" cy="10" r="3"></circle>-->
<!-- </svg>-->
<!-- <span>全球开源风险态势感知监控中心数据可视化</span>-->
<!-- </h2>-->
<!-- </div>-->
<!-- 图表网格布局新增多个模块 -->
<div class="index-module__NV_5cW__chartsGrid">
<!-- 全球威胁情报分析地图 - 移到最上面 -->
<div class="index-module__NV_5cW__chartCard chartCard__wide">
<h3 class="chartCard__title">🛡 全球威胁情报分析与监测</h3>
<div class="chartCard__container map-container">
<ThreatIntelMap />
</div>
</div>
<!-- 行业分布 -->
<IndustryDistribution />
<!-- 高危组件Top 10 -->
<HighRiskComponentRank />
<!-- 情报流 -->
<IntelligenceFeed />
<!-- CVE 月度趋势全球图 区域图 -->
<CveTrendChart />
</div>
</div>
<!-- 编程语言使用趋势堆叠面积图-->
<VulnerabilityLanguageChart />
<!-- 高危漏洞占比图表 -->
<VulnerabilitySeverityChart />
<!-- 编程语言安全态势 -->
<ScatterChart />
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, nextTick, watch, onUnmounted } from 'vue';
import * as echarts from 'echarts';
import { usePageResize } from '@/utils/hooks/usePageResize';
import ThreatIntelMap from './components/ThreatIntelMap.vue';
import IndustryDistribution from './components/IndustryDistribution.vue';
import HighRiskComponentRank from './components/HighRiskComponentRank.vue';
import IntelligenceFeed from './components/IntelligenceFeed.vue';
import CveTrendChart from './components/CveTrendChart.vue';
import VulnerabilityLanguageChart from './components/VulnerabilityLanguageChart.vue';
import VulnerabilitySeverityChart from './components/VulnerabilitySeverityChart.vue';
import ScatterChart from './components/ScatterChart.vue';
import StatsGlass from '@/views/Jyh/security/StatsGlass.vue';
// 页面尺寸响应式
const { widthType } = usePageResize();
// 1. 武汉优秀开源项目数据
const excellentProjects = ref([
{ name: '调问DWSurvey', desc: '问卷表单', org: '极推科技(武汉)有限公司' },
{ name: 'Eova', desc: '低代码开发平台', org: '武汉简码科技有限公司' },
{ name: 'ClickVisual', desc: '轻量级日志查询、分析、报警可视化平台', org: '武汉初心科技有限公司' },
{ name: 'meta2d.js', desc: '2D图元组成的可视化引擎', org: '武汉乐吾乐科技有限责任公司' },
{ name: 'Juno', desc: '分布式微服务管理系统', org: '武汉斗鱼鱼乐网络科技有限公司' },
{ name: 'Opentalent 开源北极星', desc: '开源人才评价系统', org: '湖北大学' },
{ name: 'fountain', desc: '仓颉编程语言实现的JAVA Spring框架的国产替代', org: '江汉大学' },
{ name: 'deepin', desc: '深度操作系统', org: '武汉深之度科技有限公司' },
{ name: 'RustSBI', desc: '安全引导程序', org: '华中科技大学' },
{ name: 'NextDenovo', desc: '高效且精确的长读长纠错与组装工具', org: '武汉希望组生物科技有限公司' }
]);
// 2. 2025武汉市优秀开源软件项目
const excellentProjects2025 = ref([
{ name: '调问DWSurvey', desc: '问卷表单', org: '极推科技(武汉)有限公司' },
{ name: 'Eova', desc: '低代码开发平台', org: '武汉简码科技有限公司' },
{ name: 'ClickVisual', desc: '轻量级日志查询、分析、报警可视化平台', org: '武汉初心科技有限公司' },
{ name: 'meta2d.js', desc: '2D图元组成的可视化引擎', org: '武汉乐吾乐科技有限责任公司' },
{ name: 'Juno', desc: '分布式微服务管理系统', org: '武汉斗鱼鱼乐网络科技有限公司' },
{ name: 'Opentalent 开源北极星', desc: '开源人才评价系统', org: '湖北大学' },
{ name: 'fountain', desc: '仓颉编程语言实现的JAVA Spring框架的国产替代', org: '江汉大学' }
]);
// 3. 图表数据定义
const chartData = ref({
// 高校开源参与分布(包含华中科大、武大、湖北大学、江汉大学等)
university: [
{ value: 30, name: '华中科技大学' },
{ value: 25, name: '武汉大学' },
{ value: 15, name: '湖北大学' },
{ value: 10, name: '江汉大学' },
{ value: 8, name: '武汉理工大学' },
{ value: 7, name: '华中师范大学' },
{ value: 5, name: '其他高校' }
],
// 开源社区区域分布
community: {
xAxis: ['武昌区', '洪山区', '东湖高新区', '江汉区', '汉阳区', '青山区'],
data: [28, 35, 42, 18, 15, 12]
},
// OSS Compass开源项目排行提取OpenRank总分左边数值
ossCompass: {
yAxis: [
'openharmony/docs',
'mindspore/mindspore',
'openharmony/arkui_ace_engine',
'apache/doris',
'openharmony/interface_sdk-js',
'anolis-challenge/activity-school',
'PaddlePaddle/Paddle',
'StarRocks/starrocks',
'openharmony/xts_acts',
'ant-design/ant-design'
],
data: [91.55, 67.35, 62.07, 48.82, 44.06, 39.77, 39.55, 34.2, 30.39, 30.37],
// 完整分数数据用于tooltip
fullData: [
{ openRank: '91.55 | 30.86', quality: '92.06 | 8.905', wxb: '70.69 | -14.8', score: '84.77 | 8.32' },
{ openRank: '67.35 | 27.41', quality: '96.92 | 7.17', wxb: '98.915 | 7.1', score: '87.73 | 13.89' },
{ openRank: '62.07 | 17.98', quality: '71.69 | 2.885', wxb: '76.06 | 10.9', score: '69.94 | 10.59' },
{ openRank: '48.82 | 10.14', quality: '67.26 | 0.573', wxb: '30.5 | 7.3', score: '48.86 | 6.00' },
{ openRank: '44.06 | 11.49', quality: '32.5 | 3.14', wxb: '67.55 | 16.7', score: '48.04 | 10.44' },
{ openRank: '39.77 | 37.64', quality: '31.05 | 0.215', wxb: '24.798 | -18.9', score: '31.87 | 6.32' },
{ openRank: '39.55 | 7.25', quality: '68.55 | -2.937', wxb: '68.26 | -17.3', score: '58.79 | -4.33' },
{ openRank: '34.2 | 2.08', quality: '49.07 | -0.877', wxb: '47.07 | 9.9', score: '43.45 | 3.70' },
{ openRank: '30.39 | 10.97', quality: '29.8 | 5.633', wxb: '27.045 | 4.3', score: '29.08 | 6.97' },
{ openRank: '30.37 | 8.57', quality: '37.1 | 4.295', wxb: '35.097 | 6.8', score: '34.19 | 6.55' }
]
},
// 武汉开源开发者排行榜模拟数据前10名按贡献值排序
developer: {
yAxis: ['张XX', '李XX', '王XX', '刘XX', '陈XX', '杨XX', '黄XX', '赵XX', '吴XX', '周XX'],
data: [980, 850, 720, 650, 580, 520, 480, 420, 380, 350]
},
// 武汉市语言与技术竞争趋势2021-2025多系列
languageTech: {
xAxis: ['2021', '2022', '2023', '2024', '2025'],
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] }
]
},
});
// 空数据标识
const isEmpty = ref({
university: false,
community: false,
ossCompass: false,
developer: false,
languageTech: false
});
// 图表实例存储用于resize时销毁重绘
const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({
universityChart: null,
communityChart: null,
ossCompassChart: null,
developerChart: null,
languageTechChart: null,
cweAttackSurfaceRadar: null
});
// ===================== 新增结束 =====================
// 初始化高校参与分布饼图
const initUniversityChart = () => {
const el = document.getElementById('universityChart');
if (!el) return;
if (chartInstances.value.universityChart) {
chartInstances.value.universityChart.dispose();
}
const myChart = echarts.init(el);
chartInstances.value.universityChart = myChart;
myChart.setOption({
tooltip: {
trigger: 'item',
formatter: (params: any) => `${params.name}: ${params.value}%`
},
legend: {
// orient: 'vertical',
// left: 'left',
bottom: '0',
textStyle: { fontSize: 12 },
itemWidth: 10,
itemHeight: 10
},
series: [
{
name: '参与度',
type: 'pie',
radius: ['40%', '70%'],
data: chartData.value.university,
label: {
show: true,
formatter: '{b}: {c}%',
fontSize: 10
},
color: ['#3b82f6', '#8b5cf6', '#ec4899', '#10b981', '#f59e0b', '#6366f1', '#14b8a6']
}
],
grid: { left: 0, bottom: 0, top: 0, right: 0 }
});
};
// 初始化社区区域分布柱状图
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;
myChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: (params: any) => `${params[0].name}: ${params[0].value}`
},
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
data: chartData.value.community.xAxis,
axisLabel: { fontSize: 12 }
},
yAxis: {
type: 'value',
name: '社区数量',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
},
series: [
{
name: '开源社区数量',
type: 'bar',
data: chartData.value.community.data,
itemStyle: {
color: (params: any) => {
const colorList = ['#3b82f6', '#8b5cf6', '#ec4899', '#10b981', '#f59e0b', '#6366f1'];
return colorList[params.dataIndex];
}
}
}
]
});
};
// 初始化OSS Compass开源项目排行横向柱状图
const initOssCompassChart = () => {
const el = document.getElementById('ossCompassChart');
if (!el) return;
if (chartInstances.value.ossCompassChart) {
chartInstances.value.ossCompassChart.dispose();
}
const myChart = echarts.init(el);
chartInstances.value.ossCompassChart = myChart;
myChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: (params: any) => {
const index = params[0].dataIndex;
const data = chartData.value.ossCompass.fullData[index];
return `
<div>${params[0].name}</div>
<div>OpenRank: ${data.openRank}</div>
<div>代码内生质量: ${data.quality}</div>
<div>WXB分数: ${data.wxb}</div>
<div>综合分数: ${data.score}</div>
`;
}
},
grid: { left: '15%', right: '4%', bottom: '3%', top: '3%', containLabel: true },
xAxis: {
type: 'value',
name: 'OpenRank总分',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
},
yAxis: {
type: 'category',
data: chartData.value.ossCompass.yAxis,
axisLabel: { fontSize: 10, overflow: 'truncate', width: 120 }
},
series: [
{
name: 'OpenRank总分',
type: 'bar',
data: chartData.value.ossCompass.data,
itemStyle: { color: '#2951E0' },
label: {
show: true,
position: 'right',
formatter: '{c}',
fontSize: 10
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: 'rgba(41, 81, 224, 0.24)' },
{ offset: 1, color: 'rgba(41, 81, 224, 0.04)' }
])
}
}
]
});
};
// 初始化武汉开源开发者排行榜(横向柱状图)
const initDeveloperChart = () => {
const el = document.getElementById('developerChart');
if (!el) return;
if (chartInstances.value.developerChart) {
chartInstances.value.developerChart.dispose();
}
const myChart = echarts.init(el);
chartInstances.value.developerChart = myChart;
myChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: (params: any) => `${params[0].name}: ${params[0].value}贡献值`
},
grid: { left: '10%', right: '4%', bottom: '3%', top: '3%', containLabel: true },
xAxis: {
type: 'value',
name: '贡献值',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
},
yAxis: {
type: 'category',
data: chartData.value.developer.yAxis,
axisLabel: { fontSize: 12 }
},
series: [
{
name: '贡献值',
type: 'bar',
data: chartData.value.developer.data,
itemStyle: { color: '#8b5cf6' },
label: {
show: true,
position: 'right',
formatter: '{c}',
fontSize: 12
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: 'rgba(139, 92, 246, 0.24)' },
{ offset: 1, color: 'rgba(139, 92, 246, 0.04)' }
])
}
}
]
});
};
// 初始化武汉市语言与技术竞争趋势(折线图)
const initLanguageTechChart = () => {
const el = document.getElementById('languageTechChart');
if (!el) return;
if (chartInstances.value.languageTechChart) {
chartInstances.value.languageTechChart.dispose();
}
const myChart = echarts.init(el);
chartInstances.value.languageTechChart = myChart;
// 定义系列颜色
const colorList = ['#3b82f6', '#10b981', '#f59e0b', '#ec4899', '#8b5cf6'];
const series = chartData.value.languageTech.series.map((item, index) => ({
name: item.name,
type: 'line',
data: item.data,
symbol: 'circle',
symbolSize: 6,
lineStyle: { color: colorList[index], width: 2 },
itemStyle: { color: colorList[index] },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: `${colorList[index]}33` },
{ offset: 1, color: `${colorList[index]}08` }
])
}
}));
myChart.setOption({
tooltip: {
trigger: 'axis',
formatter: (params: any) => {
let res = `<div>${params[0].axisValue}</div>`;
params.forEach((p: any) => {
res += `<div>${p.seriesName}: ${p.value}</div>`;
});
return res;
}
},
legend: {
top: 0,
textStyle: { fontSize: 12 }
},
grid: { left: '3%', right: '4%', bottom: '3%', top: '10%', containLabel: true },
xAxis: {
type: 'category',
data: chartData.value.languageTech.xAxis,
axisLabel: { fontSize: 12 }
},
yAxis: {
type: 'value',
name: '使用占比/热度',
nameTextStyle: { fontSize: 12 },
axisLabel: { fontSize: 12 }
},
series
});
};
// 统一初始化图表
const initCharts = () => {
// 空数据判断
isEmpty.value = {
university: chartData.value.university.length === 0,
community: chartData.value.community.data.every(item => item === 0),
ossCompass: chartData.value.ossCompass.data.every(item => item === 0),
developer: chartData.value.developer.data.every(item => item === 0),
languageTech: chartData.value.languageTech.series.every(s => s.data.every(d => d === 0)),
};
// 初始化各图表
if (!isEmpty.value.university) nextTick(() => initUniversityChart());
if (!isEmpty.value.community) nextTick(() => initCommunityChart());
if (!isEmpty.value.ossCompass) nextTick(() => initOssCompassChart());
if (!isEmpty.value.developer) nextTick(() => initDeveloperChart());
if (!isEmpty.value.languageTech) nextTick(() => initLanguageTechChart());
};
// 页面resize时重绘图表
watch(widthType, () => {
initCharts();
});
// 挂载时初始化
onMounted(() => {
setTimeout(() => {
initCharts();
}, 100);
});
</script>
<style lang="scss" scoped>
.index-module__NV_5cW__container {
background: #020617;
min-height: 100vh;
padding: 2rem 1rem;
position: relative;
overflow-x: hidden;
}
.index-module__NV_5cW__header {
width: 1400px;
text-align: center;
padding: 1rem;
position: relative;
margin: 0 auto 3rem;
}
.index-module__NV_5cW__floatingElements {
pointer-events: none;
opacity: 0.3;
position: absolute;
inset: 0;
overflow: hidden;
}
.index-module__NV_5cW__floatingElement {
background: linear-gradient(135deg, #3b82f60d, #9333ea0d);
border-radius: 50%;
width: 120px;
height: 120px;
animation: 30s linear infinite index-module__NV_5cW__float;
position: absolute;
}
.index-module__NV_5cW__floatingElement:first-child {
animation-delay: 0s;
top: 10%;
left: 10%;
}
.index-module__NV_5cW__floatingElement:nth-child(2) {
animation-delay: -7s;
top: 60%;
right: 10%;
}
.index-module__NV_5cW__floatingElement:nth-child(3) {
animation-delay: -14s;
bottom: 20%;
left: 50%;
}
@keyframes index-module__NV_5cW__float {
0% {
transform: translateY(0) rotate(0);
}
33% {
transform: translateY(-20px) rotate(120deg);
}
66% {
transform: translateY(10px) rotate(240deg);
}
to {
transform: translateY(0) rotate(360deg);
}
}
.index-module__NV_5cW__titleContainer {
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 1rem;
display: flex;
}
.index-module__NV_5cW__title {
background: #333;
-webkit-text-fill-color: transparent;
letter-spacing: -0.02em;
white-space: nowrap;
-webkit-background-clip: text;
background-clip: text;
margin: 0;
font-size: 2.8rem;
font-weight: 700;
}
.index-module__NV_5cW__titleBadge {
color: #fff;
letter-spacing: 0.05em;
white-space: nowrap;
background: #60a5fa;
border-radius: 1.5rem;
padding: 0.4rem 1rem;
font-size: 1rem;
font-weight: 600;
box-shadow: 0 2px 10px #3b82f64d;
}
.index-module__NV_5cW__subtitle {
color: #666;
text-align: center;
align-items: center;
gap: 0.5rem;
max-width: 500px;
font-size: 1.1rem;
font-weight: 400;
display: flex;
}
.index-module__NV_5cW__statsBar {
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background: #ffffff1a;
border: 1px solid #fff3;
border-radius: 1.5rem;
align-items: center;
gap: 1.5rem;
width: fit-content;
max-width: 90%;
margin-top: 0.75rem;
padding: 0.75rem 1.5rem;
display: flex;
}
.index-module__NV_5cW__stat {
flex-direction: column;
align-items: center;
gap: 0.25rem;
display: flex;
}
.index-module__NV_5cW__statNumber {
color: #60a5fa;
font-size: 1.5rem;
font-weight: 700;
}
.index-module__NV_5cW__statLabel {
color: #333;
font-size: 0.875rem;
font-weight: 500;
}
.index-module__NV_5cW__statDivider {
background: #fff3;
width: 1px;
height: 2rem;
}
.index-module__NV_5cW__content {
max-width: 1400px; /* 加宽容器,适配更多图表 */
margin: 0 auto;
padding: 0 1rem;
}
/* 图表区域样式适配示例的num-empty */
.index-module__NV_5cW__chartsSection {
margin-bottom: 1.5rem;
}
.index-module__NV_5cW__chartsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); /* 调整列宽,适配列表 */
gap: 1.5rem;
padding: 0;
}
.index-module__NV_5cW__chartCard {
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background: #ffffffe6;
border: 1px solid #ffffff4d;
border-radius: 1rem;
padding: 1.5rem;
box-shadow: 0 4px 16px #00000014;
transition: all 0.3s;
}
/* 列表卡片特殊样式 */
.index-module__NV_5cW__chartCard.list-card {
padding: 1.5rem;
}
.index-module__NV_5cW__chartCard:hover {
border-color: #3b82f64d;
transform: translateY(-4px);
box-shadow: 0 8px 24px #0000001f;
}
.chartCard__wide {
grid-column: 1 / -1; /* 跨列显示 */
}
.chartCard__title {
color: #333;
font-size: 1.1rem;
font-weight: 600;
margin: 0 0 1rem 0;
text-align: left;
}
.chartCard__container {
width: 100%;
height: 300px; /* 图表高度 */
position: relative;
}
/* 地图容器特殊样式 */
.chartCard__container.map-container {
height: 500px; /* 地图需要更大的高度 */
border-radius: 0.5rem;
overflow: hidden;
background: transparent; /* 让地图组件自己控制背景 */
}
/* 地图卡片标题样式优化 */
.index-module__NV_5cW__chartCard:has(.map-container) .chartCard__title {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(8px);
border-radius: 20px;
padding: 8px 16px;
margin: 0 auto 16px;
width: fit-content;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(59, 130, 246, 0.2);
}
/* 列表容器样式:隐藏原生滚动条(可选,视觉更美观) */
.chartCard__container.list-container {
height: auto;
max-height: 300px; /* 限制列表高度,超出滚动 */
overflow-y: auto;
padding: 0 0.5rem;
/* 隐藏滚动条 */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
/* 隐藏webkit内核浏览器的滚动条 */
.chartCard__container.list-container::-webkit-scrollbar {
display: none;
}
/* 项目列表样式 */
.project-list {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
.project-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.8rem;
background: #f8f9fa;
border-radius: 0.5rem;
border-left: 3px solid #2951E0;
transition: all 0.2s;
}
/* 鼠标悬停项目项时的样式(可选) */
.project-item:hover {
background: #f0f4ff;
transform: translateX(2px);
}
.project-rank {
min-width: 24px;
height: 24px;
border-radius: 4px;
background: #2951E0;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
padding: 0 4px;
}
.project-rank.cisa {
background: #ff0000;
}
.project-rank.poisoning {
background: #ffa500;
}
.project-rank.cnvd {
background: #ffff00;
color: #000;
}
.project-rank.github {
background: #0000ff;
}
.project-rank.fix {
background: #008000;
}
.project-rank.supply {
background: #800080;
}
.project-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.project-name {
font-size: 14px;
font-weight: 600;
color: #333;
}
.project-desc {
font-size: 12px;
color: #666;
font-style: italic;
}
.project-org {
font-size: 12px;
color: #888;
flex-shrink: 0;
white-space: nowrap;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
}
/* 与示例一致的空数据样式 */
.index-module__NV_5cW__sectionHeader {
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 1rem;
margin-bottom: 2rem;
display: flex;
position: relative;
}
.index-module__NV_5cW__sectionTitle {
color: #333;
text-align: center;
justify-content: center;
align-items: center;
gap: 0.75rem;
width: 100%;
margin: 0;
font-size: 1.5rem;
font-weight: 600;
display: flex;
}
.index-module__NV_5cW__actionSection {
text-align: center;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
background: #ffffffe6;
border: 1px solid #ffffff4d;
border-radius: 1.5rem;
margin: 0;
padding: 2.5rem 2rem;
position: relative;
overflow: hidden;
box-shadow: 0 8px 24px #00000014;
}
.index-module__NV_5cW__actionBackground {
z-index: 1;
background: linear-gradient(135deg, #3b82f60d 0%, #9333ea0d 100%);
position: absolute;
inset: 0;
}
.index-module__NV_5cW__actionContent {
z-index: 2;
max-width: 600px;
margin: 0 auto;
position: relative;
}
.index-module__NV_5cW__actionIcon {
color: #fff;
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
border-radius: 50%;
justify-content: center;
align-items: center;
width: 3rem;
height: 3rem;
margin: 0 auto 1.5rem;
display: flex;
box-shadow: 0 4px 12px #3b82f633;
}
.index-module__NV_5cW__actionTitle {
background: linear-gradient(135deg, #1e293b 0%, #3b82f6 100%);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
background-clip: text;
margin-bottom: 1rem;
font-size: 2rem;
font-weight: 700;
line-height: 1.2;
}
.index-module__NV_5cW__actionDescription {
color: #64748b;
margin-bottom: 2rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
}
.index-module__NV_5cW__actionFeatures {
flex-wrap: wrap;
justify-content: center;
gap: 1.5rem;
margin-bottom: 2rem;
display: flex;
}
.index-module__NV_5cW__feature {
color: #3b82f6;
background: #3b82f614;
border: 1px solid #3b82f626;
border-radius: 1.5rem;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 1rem;
font-size: 0.85rem;
font-weight: 500;
display: flex;
}
.index-module__NV_5cW__createButton {
background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
border: none;
border-radius: 1.5rem;
align-items: center;
gap: 0.5rem;
height: auto;
margin: 0 auto;
padding: 0.75rem 2rem;
font-size: 1rem;
font-weight: 600;
transition: all 0.3s;
display: flex;
box-shadow: 0 4px 12px #3b82f64d;
}
.index-module__NV_5cW__createButton:hover {
background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);
transform: translateY(-2px);
box-shadow: 0 6px 18px #3b82f666;
}
/* 响应式适配 */
@media (max-width: 768px) {
.index-module__NV_5cW__titleContainer {
text-align: center;
flex-direction: column;
gap: 0.5rem;
}
.index-module__NV_5cW__title {
font-size: 2.5rem;
}
.index-module__NV_5cW__titleBadge {
padding: 0.4rem 1.2rem;
font-size: 1rem;
}
.index-module__NV_5cW__container {
padding: 1rem;
}
.index-module__NV_5cW__header {
margin-bottom: 2rem;
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;
}
.index-module__NV_5cW__stat {
text-align: center;
flex: 1;
min-width: 60px;
}
.index-module__NV_5cW__statNumber {
font-size: 1.1rem;
}
.index-module__NV_5cW__statLabel {
white-space: nowrap;
font-size: 0.7rem;
}
.index-module__NV_5cW__statDivider {
width: 1px;
height: 1.2rem;
}
.index-module__NV_5cW__sectionTitle {
text-align: left;
flex: 1;
justify-content: flex-start;
gap: 0.5rem;
font-size: 1.3rem;
}
.index-module__NV_5cW__actionSection {
border-radius: 1rem;
margin: 0;
padding: 2rem 1.5rem;
}
.index-module__NV_5cW__actionTitle {
font-size: 1.6rem;
}
.index-module__NV_5cW__actionFeatures {
flex-direction: column;
align-items: center;
gap: 1rem;
}
.index-module__NV_5cW__createButton {
padding: 0.65rem 1.5rem;
font-size: 0.95rem;
}
.chartCard__container {
height: 250px; /* 移动端图表高度调整 */
}
/* 移动端列表样式调整 */
.project-item {
flex-wrap: wrap;
gap: 0.5rem;
}
.project-org {
width: 100%;
max-width: 100%;
margin-top: 0.2rem;
}
}
@media (max-width: 480px) {
.index-module__NV_5cW__title {
font-size: 1.8rem;
}
.index-module__NV_5cW__actionSection {
padding: 1.5rem 1rem;
}
.index-module__NV_5cW__statsBar {
gap: 0.6rem;
padding: 0.6rem 0.8rem;
}
.index-module__NV_5cW__statNumber {
font-size: 1rem;
}
.index-module__NV_5cW__statLabel {
font-size: 0.65rem;
}
.index-module__NV_5cW__sectionTitle {
gap: 0.4rem;
font-size: 1.2rem;
}
.chartCard__container {
height: 200px; /* 小屏移动端图表高度调整 */
}
.index-module__NV_5cW__chartsGrid {
grid-template-columns: 1fr;
}
/* 小屏列表高度调整 */
.chartCard__container.list-container {
max-height: 300px;
}
}
/* 图表卡片头部样式 */
.chartCard__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
/* 图表操作按钮样式 */
.chartCard__actions {
display: flex;
gap: 8px;
}
.chartCard__actionBtn {
padding: 4px 12px;
border: 1px solid #d1d5db;
background-color: #c1c7cd;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s;
}
.chartCard__actionBtn:hover {
background-color: #f3f4f6;
}
.chartCard__actionBtn--active {
background-color: #3b82f6;
color: white;
border-color: #3b82f6;
}
.chartCard__actionBtn--active:hover {
background-color: #2563eb;
}
</style>