1484 lines
46 KiB
Vue
1484 lines
46 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="index-module__NV_5cW__container">
|
|||
|
|
<div class="index-module__NV_5cW__header">
|
|||
|
|
<div class="index-module__NV_5cW__titleSection">
|
|||
|
|
<div class="text-center max-w-4xl mx-auto header-content">
|
|||
|
|
<div class="flex items-center justify-center mb-4 header-title-row">
|
|||
|
|
<div class="title-decoration left"></div>
|
|||
|
|
<h2 class="main-title">
|
|||
|
|
开源 · 武汉
|
|||
|
|
</h2>
|
|||
|
|
<div class="title-decoration right"></div>
|
|||
|
|
</div>
|
|||
|
|
<p class="sub-title">
|
|||
|
|
汇聚武汉开源力量,共建城市开源生态
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
<StatsBar />
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__content">
|
|||
|
|
<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-activity" aria-hidden="true">
|
|||
|
|
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
|
|||
|
|
</svg>
|
|||
|
|
<span>武汉开源生态数据可视化</span>
|
|||
|
|
</h2>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartsGrid">
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard list-card">
|
|||
|
|
<h3 class="chartCard__title">武汉优质开源项目榜单</h3>
|
|||
|
|
<div class="chartCard__container list-container"
|
|||
|
|
ref="scrollContainer1"
|
|||
|
|
@mouseenter="pauseScroll(1)"
|
|||
|
|
@mouseleave="resumeScroll(1)">
|
|||
|
|
<div class="project-list">
|
|||
|
|
<div class="project-item" v-for="(item, index) in githubProjects" :key="index">
|
|||
|
|
<div class="project-rank-wrapper">
|
|||
|
|
<span class="project-rank">{{ index + 1 }}</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="project-info">
|
|||
|
|
<span class="project-name">{{ item.name }}</span>
|
|||
|
|
<span class="project-desc">{{ item.desc }}</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="project-meta">
|
|||
|
|
<span class="project-stars">★ {{ item.stars }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<svg class="source-icon" viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
|||
|
|
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.128 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
|||
|
|
fill="#333" />
|
|||
|
|
</svg>
|
|||
|
|
<span class="source-name">GitHub</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard">
|
|||
|
|
<h3 class="chartCard__title">中国城市开源项目数量对比</h3>
|
|||
|
|
<div class="chartCard__container">
|
|||
|
|
<div v-if="isEmpty.cityProject" class="num-empty"></div>
|
|||
|
|
<div v-else id="cityProjectChart" class="chart"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<svg class="source-icon" viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
|||
|
|
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.128 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
|||
|
|
fill="#333" />
|
|||
|
|
</svg>
|
|||
|
|
<span class="source-name">GitHub</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard list-card">
|
|||
|
|
<div class="flex justify-center pb-2">
|
|||
|
|
<div class="language-tab" @click="switchTab">
|
|||
|
|
<span :class="{ active: activeTab === 0 }">开源创新联盟优秀项目</span>
|
|||
|
|
<span :class="{ active: activeTab === 1 }">武汉优秀开源项目</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="chartCard__container list-container"
|
|||
|
|
ref="scrollContainer2"
|
|||
|
|
@mouseenter="pauseScroll(2)"
|
|||
|
|
@mouseleave="resumeScroll(2)">
|
|||
|
|
<div class="project-list">
|
|||
|
|
<div class="project-item" v-for="(item, index) in currentProjectList" :key="index">
|
|||
|
|
<div class="project-rank-wrapper">
|
|||
|
|
<span class="project-rank" :class="{'rank-new': activeTab === 1}">{{ index + 1 }}</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="project-info">
|
|||
|
|
<span class="project-name">{{ item.name }}</span>
|
|||
|
|
<span class="project-desc">{{ item.desc }}</span>
|
|||
|
|
</div>
|
|||
|
|
<span class="project-org-tag">{{ item.org }}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<div class="source-logo-text">Gov</div>
|
|||
|
|
<span class="source-name">武汉市经信局/软件协会</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard">
|
|||
|
|
<h3 class="chartCard__title">城市开源人才数量对比</h3>
|
|||
|
|
<div class="chartCard__container">
|
|||
|
|
<div v-if="isEmpty.talent" class="num-empty"></div>
|
|||
|
|
<div v-else id="talentChart" class="chart"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<img src="@/assets/imgs/logo/gitcode-ico.png" class="source-logo-circle" />
|
|||
|
|
<span class="source-name">GitCode</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard list-card">
|
|||
|
|
<h3 class="chartCard__title">武汉明星开发者贡献榜</h3>
|
|||
|
|
<div class="chartCard__container list-container"
|
|||
|
|
ref="developerScrollRef"
|
|||
|
|
@mouseenter="pauseDeveloperScroll"
|
|||
|
|
@mouseleave="resumeDeveloperScroll">
|
|||
|
|
<div class="developer-list">
|
|||
|
|
<div class="developer-item" v-for="(name, index) in developerNames" :key="index">
|
|||
|
|
<!-- <div class="dev-rank" :class="'rank-' + (index + 1)">{{ index + 1 }}</div>-->
|
|||
|
|
|
|||
|
|
<div class="dev-avatar" :style="{ background: getAvatarBg(index) }">
|
|||
|
|
{{ name.charAt(0) }}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="dev-info">
|
|||
|
|
<div class="dev-name">{{ name }}</div>
|
|||
|
|
<div class="dev-tag">核心贡献者</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="dev-medal">
|
|||
|
|
<svg v-if="index < 3" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24"
|
|||
|
|
fill="#f59e0b" stroke="none">
|
|||
|
|
<path
|
|||
|
|
d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
|
|||
|
|
</svg>
|
|||
|
|
<span v-else class="dev-score">活跃</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<img src="@/assets/imgs/logo/penguin_blue.svg" class="source-logo-circle" />
|
|||
|
|
<span class="source-name">华科开放原子俱乐部</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard">
|
|||
|
|
<div class="flex justify-center pb-2">
|
|||
|
|
<div class="language-tab" @click="switchCommunityTab">
|
|||
|
|
<span :class="{ active: activeCommunityTab === 0 }">城市社区数量排行</span>
|
|||
|
|
<span :class="{ active: activeCommunityTab === 1 }">武汉知名开源社区</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="chartCard__container">
|
|||
|
|
<div v-if="activeCommunityTab === 0" id="communityRankChart" class="chart"></div>
|
|||
|
|
|
|||
|
|
<div v-else class="list-container"
|
|||
|
|
ref="communityScrollRef"
|
|||
|
|
@mouseenter="pauseCommunityScroll"
|
|||
|
|
@mouseleave="resumeCommunityScroll">
|
|||
|
|
<div class="community-scroll-list">
|
|||
|
|
<div class="community-item" v-for="(item, index) in wuhanCommunities" :key="index">
|
|||
|
|
<div class="community-logo">
|
|||
|
|
{{ item.name.charAt(0) }}
|
|||
|
|
</div>
|
|||
|
|
<div class="community-info">
|
|||
|
|
<div class="community-top">
|
|||
|
|
<span class="community-name">{{ item.name }}</span>
|
|||
|
|
<span class="community-tag">{{ item.type }}</span>
|
|||
|
|
</div>
|
|||
|
|
<p class="community-slogan">{{ item.slogan }}</p>
|
|||
|
|
</div>
|
|||
|
|
<div class="community-status">
|
|||
|
|
<span class="status-dot"></span>
|
|||
|
|
活跃
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<img src="@/assets/imgs/logo/penguin_blue.svg" class="source-logo-circle" />
|
|||
|
|
<span class="source-name">华科开放原子俱乐部</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard">
|
|||
|
|
<h3 class="chartCard__title">重点城市高校开源社团数量</h3>
|
|||
|
|
<div class="chartCard__container">
|
|||
|
|
<div v-if="isEmpty.club" class="num-empty"></div>
|
|||
|
|
<div v-else id="clubChart" class="chart"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<img src="@/assets/imgs/logo/penguin_blue.svg" class="source-logo-circle" />
|
|||
|
|
<span class="source-name">华科开放原子俱乐部</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard">
|
|||
|
|
<h3 class="chartCard__title">各省市开源相关政策发布数量</h3>
|
|||
|
|
<div class="chartCard__container">
|
|||
|
|
<div v-if="isEmpty.policy" class="num-empty"></div>
|
|||
|
|
<div v-else id="policyChart" class="chart"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<img src="@/assets/imgs/logo/logo-kfyzjjh.png" class="source-logo-circle" />
|
|||
|
|
<span class="source-name">开放原子开源基金会</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard">
|
|||
|
|
<h3 class="chartCard__title">开放原子校园行·城市活动场次</h3>
|
|||
|
|
<div class="chartCard__container">
|
|||
|
|
<div v-if="isEmpty.campus" class="num-empty"></div>
|
|||
|
|
<div v-else id="campusChart" class="chart"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<img src="@/assets/imgs/logo/logo-kfyzjjh.png" class="source-logo-circle" />
|
|||
|
|
<span class="source-name">开放原子开源基金会</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__chartCard chartCard__wide">
|
|||
|
|
<h3 class="chartCard__title">全国重点城市开源镜像站/节点数量对比</h3>
|
|||
|
|
<div class="chartCard__container">
|
|||
|
|
<div v-if="isEmpty.mirrors" class="num-empty"></div>
|
|||
|
|
<div v-else id="mirrorChart" class="chart"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="source-footer">
|
|||
|
|
<span class="source-label">数据来源:</span>
|
|||
|
|
<div class="source-content">
|
|||
|
|
<img src="@/assets/imgs/logo/penguin_blue.svg" class="source-logo-circle" />
|
|||
|
|
<span class="source-name">华科开放原子俱乐部</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="index-module__NV_5cW__actionSection">
|
|||
|
|
<div class="index-module__NV_5cW__actionBackground"></div>
|
|||
|
|
<div class="index-module__NV_5cW__actionContent">
|
|||
|
|
<div class="index-module__NV_5cW__actionIcon">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none"
|
|||
|
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|||
|
|
class="lucide lucide-rocket">
|
|||
|
|
<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" />
|
|||
|
|
<path
|
|||
|
|
d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" />
|
|||
|
|
<path d="M9 12H4s.55-3.03 2-4c1.62-1.1 4-1 4-1s.25 2.25 1 4c.45 1.04 1 2 2 3h-5z" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<h3 class="index-module__NV_5cW__actionTitle">加入武汉开源社区</h3>
|
|||
|
|
<p class="index-module__NV_5cW__actionDescription">与武汉本地开发者、企业、高校携手共建开源生态</p>
|
|||
|
|
<button type="button" class="action-btn">
|
|||
|
|
<span>加入我们</span>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script lang="ts" setup>
|
|||
|
|
import { onMounted, ref, nextTick, watch, onUnmounted, computed } from 'vue';
|
|||
|
|
import * as echarts from 'echarts';
|
|||
|
|
import { usePageResize } from '@/utils/hooks/usePageResize';
|
|||
|
|
import StatsBar from './StatsBar.vue';
|
|||
|
|
|
|||
|
|
const { widthType } = usePageResize();
|
|||
|
|
|
|||
|
|
// ================= 数据定义 =================
|
|||
|
|
const githubProjects = ref([
|
|||
|
|
{
|
|||
|
|
name: 'bootstrap',
|
|||
|
|
desc: 'The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.',
|
|||
|
|
stars: '174000'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'JavaGuide',
|
|||
|
|
desc: 'Java 学习&面试指南(Go、Python 后端面试通用,计算机基础面试总结)。准备后端技术面试,首选 JavaGuide!',
|
|||
|
|
stars: '153000'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'cpython',
|
|||
|
|
desc: 'The Python programming language',
|
|||
|
|
stars: '71000'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'core',
|
|||
|
|
desc: '🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.',
|
|||
|
|
stars: '52700'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'bevy',
|
|||
|
|
desc: 'A refreshingly simple data-driven game engine built in Rust',
|
|||
|
|
stars: '43900'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'linux-insides',
|
|||
|
|
desc: 'A book-in-progress about the Linux kernel and its insides.',
|
|||
|
|
stars: '31700'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'podman',
|
|||
|
|
desc: 'Podman: A tool for managing OCI containers and pods.',
|
|||
|
|
stars: '30300'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'fastai',
|
|||
|
|
desc: 'The fastai deep learning library',
|
|||
|
|
stars: '27800'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'OI-wiki',
|
|||
|
|
desc: '🌟 Wiki of OI / ICPC for everyone. (某大型游戏线上攻略,内含炫酷算术魔法)',
|
|||
|
|
stars: '25200'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'bilibili-API-collect',
|
|||
|
|
desc: '哔哩哔哩-API收集整理【不断更新中....】',
|
|||
|
|
stars: '20100'
|
|||
|
|
}
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
const activeTab = ref(0);
|
|||
|
|
|
|||
|
|
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': '仓颉语言版 Spring 框架替代', 'org': '江汉大学' },
|
|||
|
|
{ 'name': 'deepin', 'desc': '深度桌面操作系统', 'org': '深之度' },
|
|||
|
|
{ 'name': 'RustSBI', 'desc': 'RISC-V 安全引导程序', 'org': '华中科技大学' },
|
|||
|
|
{ 'name': 'NextDenovo', 'desc': '长读长序列组装工具', 'org': '武汉希望组' }
|
|||
|
|
]);
|
|||
|
|
const chinaExcellentProjects = ref([
|
|||
|
|
{
|
|||
|
|
'name': 'Deepin 深度操作系统',
|
|||
|
|
'desc': '国内领先的国产 Linux 发行版,旨在为全球用户提供美观易用、安全可靠的操作系统体验,是国产软件生态的核心。',
|
|||
|
|
'org': '武汉深之度'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': 'Jupiter 微服务治理框架',
|
|||
|
|
'desc': '由斗鱼研发并开源的面向治理的微服务框架,支持多数据源、多注册中心,适用于高并发场景。',
|
|||
|
|
'org': '武汉斗鱼'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': 'RustSBI 安全引导程序',
|
|||
|
|
'desc': '基于 Rust 语言开发的 RISC-V 指令集体系结构安全引导程序,提供更高内存安全性。',
|
|||
|
|
'org': '华中科技大学'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': '芯片开源 EDA 研发',
|
|||
|
|
'desc': '致力于国产电子设计自动化工具开源化,解决芯片设计环节中的关键技术难题。',
|
|||
|
|
'org': '华中科技大学'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': 'LuoJiaSET 框架与 LuoJiaSET 样本库',
|
|||
|
|
'desc': '专注于智能遥感影像解译,提供海量遥感样本数据集和深度学习框架。',
|
|||
|
|
'org': '武汉大学'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': '鲲鹏安全库 (Kunpengsecl)',
|
|||
|
|
'desc': '基于鲲鹏处理器的机密计算和受信任执行环境安全开发库,支撑云计算与隐私计算。',
|
|||
|
|
'org': '武汉大学'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': '柚坛工具箱 (Uotan Toolbox)',
|
|||
|
|
'desc': '针对 Android 发烧友开发的图形化综合工具箱,集成刷机、系统优化等多种功能。',
|
|||
|
|
'org': '武昌理工学院'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': '凹语言 (Wa Language)',
|
|||
|
|
'desc': '国内首个针对 WebAssembly 设计的通用编程语言,兼具 Web 灵活性与原生运行速度。',
|
|||
|
|
'org': '武汉凹语言'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': 'OpenHIS 开源医疗系统',
|
|||
|
|
'desc': '开源的医院信息系统,旨在通过标准化、模块化方式赋能基层医疗机构数字化升级。',
|
|||
|
|
'org': '武汉新致新源'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
'name': 'NextDenovo',
|
|||
|
|
'desc': '高效且精确的长读长序列纠错和组装工具,主要用于基因组测序领域。',
|
|||
|
|
'org': '武汉希望组生物科技有限公司'
|
|||
|
|
}
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
// 武汉知名社区数据
|
|||
|
|
const wuhanCommunities = ref([
|
|||
|
|
{ name: 'Deepin (深度)', type: '操作系统', slogan: '最大的国产桌面操作系统开源社区' },
|
|||
|
|
{ name: 'OpenHarmony 武汉', type: '万物互联', slogan: '华科/武大等高校深度参与的终端操作系统' },
|
|||
|
|
{ name: 'MindSpore 武汉', type: '人工智能', slogan: '昇思全场景AI框架武汉开发者社区' },
|
|||
|
|
{ name: 'IoTDB 社区', type: '工业物联网', slogan: '源自清华在汉广泛应用的工业时序数据库' },
|
|||
|
|
{ name: 'GoFrame 社区', type: '后端开发', slogan: '模块化、全栈式、模块化的Go语言开发框架' },
|
|||
|
|
{ name: '飞桨 (PaddlePaddle)', type: '深度学习', slogan: '百度武汉开源生态开发者汇聚地' },
|
|||
|
|
{ name: '极狐 GitLab 武汉', type: 'DevOps', slogan: '开源信息基础设施与协同开发平台' }
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
const activeCommunityTab = ref(0);
|
|||
|
|
const communityScrollRef = ref<HTMLElement | null>(null);
|
|||
|
|
let communityTimer: NodeJS.Timeout | null = null;
|
|||
|
|
|
|||
|
|
const switchCommunityTab = () => {
|
|||
|
|
if (activeCommunityTab.value === 1) {
|
|||
|
|
activeCommunityTab.value = 0;
|
|||
|
|
nextTick(() => initCommunityRankChart());
|
|||
|
|
pauseCommunityScroll();
|
|||
|
|
} else {
|
|||
|
|
activeCommunityTab.value = 1;
|
|||
|
|
nextTick(() => startCommunityScroll());
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const startCommunityScroll = () => {
|
|||
|
|
const container = communityScrollRef.value;
|
|||
|
|
if (!container) return;
|
|||
|
|
const list = container.querySelector('.community-scroll-list') as HTMLElement;
|
|||
|
|
if (!list || list.scrollHeight <= container.clientHeight) return;
|
|||
|
|
|
|||
|
|
if (communityTimer) clearInterval(communityTimer);
|
|||
|
|
communityTimer = setInterval(() => {
|
|||
|
|
container.scrollTop += 1;
|
|||
|
|
if (container.scrollTop >= (list.scrollHeight - container.clientHeight)) {
|
|||
|
|
container.scrollTop = 0;
|
|||
|
|
}
|
|||
|
|
}, 45);
|
|||
|
|
};
|
|||
|
|
const pauseCommunityScroll = () => communityTimer && clearInterval(communityTimer);
|
|||
|
|
const resumeCommunityScroll = () => activeCommunityTab.value === 1 && startCommunityScroll();
|
|||
|
|
|
|||
|
|
const currentProjectList = computed(() => {
|
|||
|
|
return activeTab.value === 0 ? chinaExcellentProjects.value : excellentProjects.value;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const switchTab = (index: number) => {
|
|||
|
|
if (activeTab.value === 1) {
|
|||
|
|
activeTab.value = 0;
|
|||
|
|
} else {
|
|||
|
|
activeTab.value = 1;
|
|||
|
|
}
|
|||
|
|
nextTick(() => {
|
|||
|
|
if (scrollContainer2.value) scrollContainer2.value.scrollTop = 0;
|
|||
|
|
pauseScroll(2);
|
|||
|
|
resumeScroll(2);
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const chartData = ref({
|
|||
|
|
cityProject: {
|
|||
|
|
xAxis: ['北京', '上海', '深圳', '杭州', '广州', '成都', '武汉', '南京', '西安', '重庆'],
|
|||
|
|
data: [757259, 678614, 343417, 315406, 177003, 168300, 120827, 111593, 58678, 56564]
|
|||
|
|
},
|
|||
|
|
talent: {
|
|||
|
|
xAxis: ['北京', '上海', '广州', '深圳', '成都', '杭州', '武汉', '南京', '郑州', '长沙'],
|
|||
|
|
data: [2115924, 1591988, 1555240, 1163580, 894108, 830306, 791335, 733581, 553397, 487971]
|
|||
|
|
},
|
|||
|
|
communityRank: {
|
|||
|
|
xAxis: ['Deepin', 'OpenHarmony', 'MindSpore', 'IoTDB', 'GoFrame'],
|
|||
|
|
data: [95, 92, 88, 76, 72]
|
|||
|
|
},
|
|||
|
|
club: {
|
|||
|
|
xAxis: ['成都', '北京', '上海', '武汉','重庆', '深圳', '苏州', '杭州', '合肥', '青岛' ],
|
|||
|
|
data: [10, 7, 5, 4, 3, 2, 2, 2, 2, 2]
|
|||
|
|
},
|
|||
|
|
policy: {
|
|||
|
|
xAxis: [
|
|||
|
|
'江苏', '北京', '浙江', '广东', '上海', '湖北', '天津',
|
|||
|
|
'陕西', '河南', '云南'
|
|||
|
|
// '黑龙江','安徽省', '重庆市', '宁夏回族自治区', '山东省', '湖南省'
|
|||
|
|
],
|
|||
|
|
data: [11, 7, 3, 3, 3, 2, 2, 2, 2, 2
|
|||
|
|
// 2, 2, 1, 1, 1, 1
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
campus: {
|
|||
|
|
xAxis: [
|
|||
|
|
'南京', '武汉', '北京', '长沙', '成都', '上海', '天津', '深圳',
|
|||
|
|
'济南', '广州'
|
|||
|
|
// '呼和浩特', '长春', '吉林', '沈阳', '苏州', '青岛',
|
|||
|
|
// '惠州', '合肥', '佛山', '南昌', '南充', '徐州', '西安', '郑州',
|
|||
|
|
// '贵阳', '线上', '哈尔滨', '宜宾', '杭州', '太原', '大连', '银川',
|
|||
|
|
// '西宁', '中山', '乐山', '内江', '泸州', '常德', '昌吉', '桂林',
|
|||
|
|
// '福州', '常州', '库尔勒', '兰州', '温州', '鄂州', '乌鲁木齐', '日照',
|
|||
|
|
// '无锡', '重庆', '东莞', '赣州'
|
|||
|
|
],
|
|||
|
|
data: [
|
|||
|
|
20, 16, 16, 16, 11, 10, 8, 8, 7, 6
|
|||
|
|
// 5, 4, 3, 3, 3, 3,
|
|||
|
|
// 3, 3, 3, 3, 3, 2, 2, 2,
|
|||
|
|
// 2, 2, 2, 2, 2, 2, 2, 1,
|
|||
|
|
// 1, 1, 1, 1, 1, 1, 1, 1,
|
|||
|
|
// 1, 1, 1, 1, 1, 1, 1, 1,
|
|||
|
|
// 1, 1, 1, 1
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
mirrors: {
|
|||
|
|
xAxis: [
|
|||
|
|
'上海', '北京', '西安', '重庆', '济南', '南京', '武汉', '合肥', '香港',
|
|||
|
|
'长春', '成都', '深圳', '兰州', '哈尔滨', '南阳', '杭州'
|
|||
|
|
],
|
|||
|
|
data: [4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 数据定义
|
|||
|
|
const developerNames = ['白纯骢', '陆红旭', '慕冬亮', '张继德', '蒋周奇', '张天雄', '阮文俊', '李奇', '陈鹏轩', '杨帆'];
|
|||
|
|
|
|||
|
|
const developerScrollRef = ref<HTMLElement | null>(null);
|
|||
|
|
let developerTimer: NodeJS.Timeout | null = null;
|
|||
|
|
|
|||
|
|
// 自动生成头像背景色
|
|||
|
|
const getAvatarBg = (index: number) => {
|
|||
|
|
const colors = ['#6366f1', '#8b5cf6', '#ec4899', '#f43f5e', '#f59e0b', '#10b981', '#06b6d4', '#3b82f6'];
|
|||
|
|
return colors[index % colors.length];
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 滚动逻辑
|
|||
|
|
const startDeveloperScroll = () => {
|
|||
|
|
const container = developerScrollRef.value;
|
|||
|
|
if (!container) return;
|
|||
|
|
const list = container.querySelector('.developer-list') as HTMLElement;
|
|||
|
|
if (!list || list.scrollHeight <= container.clientHeight) return;
|
|||
|
|
|
|||
|
|
if (developerTimer) clearInterval(developerTimer);
|
|||
|
|
developerTimer = setInterval(() => {
|
|||
|
|
container.scrollTop += 1;
|
|||
|
|
if (container.scrollTop >= (list.scrollHeight - container.clientHeight)) {
|
|||
|
|
container.scrollTop = 0;
|
|||
|
|
}
|
|||
|
|
}, 40);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const pauseDeveloperScroll = () => developerTimer && clearInterval(developerTimer);
|
|||
|
|
const resumeDeveloperScroll = () => startDeveloperScroll();
|
|||
|
|
|
|||
|
|
const isEmpty = ref({
|
|||
|
|
cityProject: false, talent: false, developer: false, communityRank: false,
|
|||
|
|
club: false, policy: false, campus: false, mirrors: false
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const chartInstances = ref<{ [key: string]: echarts.ECharts | null }>({});
|
|||
|
|
const scrollContainer1 = ref<HTMLDivElement | null>(null);
|
|||
|
|
const scrollContainer2 = ref<HTMLDivElement | null>(null);
|
|||
|
|
const scrollTimers: { [key: number]: NodeJS.Timeout | null } = { 1: null, 2: null };
|
|||
|
|
|
|||
|
|
const autoScroll = (key: number) => {
|
|||
|
|
const container = key === 1 ? scrollContainer1.value : scrollContainer2.value;
|
|||
|
|
if (!container) return;
|
|||
|
|
const list = container.querySelector('.project-list') as HTMLDivElement;
|
|||
|
|
if (!list || list.scrollHeight <= container.clientHeight) {
|
|||
|
|
if (scrollTimers[key]) clearInterval(scrollTimers[key]!);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (scrollTimers[key]) clearInterval(scrollTimers[key]!);
|
|||
|
|
scrollTimers[key] = setInterval(() => {
|
|||
|
|
container.scrollTop += 1;
|
|||
|
|
if (list.scrollHeight - container.scrollTop <= container.clientHeight + 1) {
|
|||
|
|
container.scrollTop = 0;
|
|||
|
|
}
|
|||
|
|
}, 40);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const pauseScroll = (key: number) => {
|
|||
|
|
if (scrollTimers[key]) clearInterval(scrollTimers[key]!);
|
|||
|
|
};
|
|||
|
|
const resumeScroll = (key: number) => autoScroll(key);
|
|||
|
|
|
|||
|
|
// ================= 图表初始化=================
|
|||
|
|
|
|||
|
|
const commonGrid = { left: '3%', right: '4%', bottom: '15px', top: '15%', containLabel: true };
|
|||
|
|
const colorPalette = ['#3b82f6', '#06b6d4', '#8b5cf6', '#ec4899', '#f59e0b']; // 蓝, 青, 紫, 粉, 橙
|
|||
|
|
|
|||
|
|
const initCityProjectChart = () => {
|
|||
|
|
const el = document.getElementById('cityProjectChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
chartInstances.value.cityProject = myChart;
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (value: any) => value + ' 个' },
|
|||
|
|
grid: commonGrid,
|
|||
|
|
xAxis: { type: 'category', data: chartData.value.cityProject.xAxis, axisLabel: { color: '#666', fontSize: 11 } },
|
|||
|
|
yAxis: { type: 'value', name: '单位:个', splitLine: { lineStyle: { type: 'dashed', color: '#eee' } } },
|
|||
|
|
series: [{
|
|||
|
|
type: 'bar', data: chartData.value.cityProject.data, barWidth: '50%',
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: [4, 4, 0, 0],
|
|||
|
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#4F46E5' }, {
|
|||
|
|
offset: 1,
|
|||
|
|
color: '#818CF8'
|
|||
|
|
}])
|
|||
|
|
}
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initTalentChart = () => {
|
|||
|
|
const el = document.getElementById('talentChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
chartInstances.value.talent = myChart;
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (value: any) => value + ' 人' },
|
|||
|
|
grid: commonGrid,
|
|||
|
|
xAxis: { type: 'category', data: chartData.value.talent.xAxis, axisLabel: { color: '#666' } },
|
|||
|
|
yAxis: { type: 'value', name: '单位:人', splitLine: { lineStyle: { type: 'dashed', color: '#eee' } } },
|
|||
|
|
series: [{
|
|||
|
|
type: 'bar', data: chartData.value.talent.data, barWidth: '50%',
|
|||
|
|
itemStyle: { borderRadius: [4, 4, 0, 0], color: '#F97316' } // 橙色
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initDeveloperChart = () => {
|
|||
|
|
const el = document.getElementById('developerChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
chartInstances.value.developer = myChart;
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (value: any) => value + ' 贡献分' },
|
|||
|
|
grid: { left: '3%', right: '5%', bottom: '15px', top: '5%', containLabel: true },
|
|||
|
|
xAxis: { type: 'value', show: false }, // 隐藏x轴
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: chartData.value.developer.yAxis.reverse(),
|
|||
|
|
axisLine: { show: false },
|
|||
|
|
axisTick: { show: false },
|
|||
|
|
axisLabel: { color: '#333', fontWeight: 500 }
|
|||
|
|
},
|
|||
|
|
series: [{
|
|||
|
|
type: 'bar', data: chartData.value.developer.data.reverse(), barWidth: 16,
|
|||
|
|
itemStyle: {
|
|||
|
|
color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [{ offset: 0, color: '#7C3AED' }, {
|
|||
|
|
offset: 1,
|
|||
|
|
color: '#C4B5FD'
|
|||
|
|
}]), borderRadius: [0, 8, 8, 0]
|
|||
|
|
},
|
|||
|
|
label: { show: false } // **修改点:隐藏具体分数**
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initCommunityRankChart = () => {
|
|||
|
|
const el = document.getElementById('communityRankChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (val: any) => val + ' 个' },
|
|||
|
|
grid: { left: '3%', right: '4%', bottom: '15px', top: '22%', containLabel: true },
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: ['北京', '深圳', '上海', '杭州', '广州', '成都', '南京', '武汉'],
|
|||
|
|
axisLabel: { color: '#666' }
|
|||
|
|
},
|
|||
|
|
yAxis: { type: 'value', name: '单位:个', nameTextStyle: { color: '#999' } },
|
|||
|
|
series: [{
|
|||
|
|
type: 'bar',
|
|||
|
|
data: [69, 35, 31, 25, 18, 15, 12, 12],
|
|||
|
|
barWidth: '40%',
|
|||
|
|
itemStyle: { borderRadius: [4, 4, 0, 0], color: '#3b82f6' }
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initClubChart = () => {
|
|||
|
|
const el = document.getElementById('clubChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
chartInstances.value.club = myChart;
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (value: any) => value + ' 个' },
|
|||
|
|
grid: commonGrid,
|
|||
|
|
xAxis: { type: 'category', data: chartData.value.club.xAxis, axisLabel: { color: '#666' } },
|
|||
|
|
yAxis: { type: 'value', name: '单位:个', splitLine: { lineStyle: { type: 'dashed', color: '#eee' } } },
|
|||
|
|
series: [{
|
|||
|
|
type: 'line', data: chartData.value.club.data,
|
|||
|
|
smooth: true, symbolSize: 8,
|
|||
|
|
lineStyle: { width: 3, color: '#F59E0B' },
|
|||
|
|
itemStyle: { color: '#F59E0B', borderColor: '#fff', borderWidth: 2 },
|
|||
|
|
areaStyle: { opacity: 0.15, color: '#F59E0B' }
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initPolicyChart = () => {
|
|||
|
|
const el = document.getElementById('policyChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
chartInstances.value.policy = myChart;
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (value: any) => value + ' 条' },
|
|||
|
|
grid: commonGrid,
|
|||
|
|
xAxis: { type: 'category', data: chartData.value.policy.xAxis, axisLabel: { color: '#666', interval: 0 } },
|
|||
|
|
yAxis: { type: 'value', name: '单位:条', splitLine: { lineStyle: { type: 'dashed', color: '#eee' } } },
|
|||
|
|
series: [{
|
|||
|
|
type: 'bar', data: chartData.value.policy.data, barWidth: '45%',
|
|||
|
|
itemStyle: { borderRadius: [4, 4, 0, 0], color: '#2563EB' } // 蓝色
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initCampusChart = () => {
|
|||
|
|
const el = document.getElementById('campusChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
chartInstances.value.campus = myChart;
|
|||
|
|
// **修改点:去掉三角形pictorialBar,改为美观的圆角柱状图**
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (value: any) => value + ' 场' },
|
|||
|
|
grid: commonGrid,
|
|||
|
|
xAxis: { type: 'category', data: chartData.value.campus.xAxis, axisLabel: { color: '#666' } },
|
|||
|
|
yAxis: { type: 'value', name: '单位:场次', splitLine: { lineStyle: { type: 'dashed', color: '#eee' } } },
|
|||
|
|
series: [{
|
|||
|
|
type: 'bar',
|
|||
|
|
data: chartData.value.campus.data,
|
|||
|
|
barWidth: '35%',
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: [8, 8, 0, 0], // 圆角
|
|||
|
|
// 活力洋红色渐变
|
|||
|
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|||
|
|
{ offset: 0, color: '#EC4899' },
|
|||
|
|
{ offset: 1, color: '#F472B6' }
|
|||
|
|
])
|
|||
|
|
}
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initMirrorChart = () => {
|
|||
|
|
const el = document.getElementById('mirrorChart');
|
|||
|
|
if (!el) return;
|
|||
|
|
const myChart = echarts.init(el);
|
|||
|
|
chartInstances.value.mirrors = myChart;
|
|||
|
|
myChart.setOption({
|
|||
|
|
tooltip: { trigger: 'axis', valueFormatter: (value: any) => value + ' 个节点' },
|
|||
|
|
grid: { left: '3%', right: '3%', bottom: '15px', top: '15%', containLabel: true },
|
|||
|
|
xAxis: { type: 'category', data: chartData.value.mirrors.xAxis, axisLabel: { color: '#666' } },
|
|||
|
|
yAxis: { type: 'value', name: '单位:个', splitLine: { lineStyle: { type: 'dashed', color: '#eee' } } },
|
|||
|
|
series: [{
|
|||
|
|
type: 'bar', data: chartData.value.mirrors.data,
|
|||
|
|
barWidth: 30,
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: [4, 4, 0, 0],
|
|||
|
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|||
|
|
{ offset: 0, color: '#0EA5E9' },
|
|||
|
|
{ offset: 1, color: '#38BDF8' }
|
|||
|
|
])
|
|||
|
|
}
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const initCharts = () => {
|
|||
|
|
nextTick(() => {
|
|||
|
|
initCityProjectChart();
|
|||
|
|
initTalentChart();
|
|||
|
|
initDeveloperChart();
|
|||
|
|
initCommunityRankChart();
|
|||
|
|
initClubChart();
|
|||
|
|
initPolicyChart();
|
|||
|
|
initCampusChart();
|
|||
|
|
initMirrorChart();
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const init = () => {
|
|||
|
|
nextTick(() => {
|
|||
|
|
initCityProjectChart();
|
|||
|
|
initTalentChart();
|
|||
|
|
initDeveloperChart();
|
|||
|
|
initCommunityRankChart();
|
|||
|
|
initClubChart();
|
|||
|
|
initPolicyChart();
|
|||
|
|
initCampusChart();
|
|||
|
|
initMirrorChart();
|
|||
|
|
|
|||
|
|
autoScroll(1);
|
|||
|
|
autoScroll(2);
|
|||
|
|
startCommunityScroll();
|
|||
|
|
startDeveloperScroll();
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
const unInit = () => {
|
|||
|
|
Object.values(scrollTimers).forEach(t => t && clearInterval(t));
|
|||
|
|
pauseDeveloperScroll();
|
|||
|
|
pauseCommunityScroll();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
watch(widthType, () => {
|
|||
|
|
init();
|
|||
|
|
});
|
|||
|
|
onMounted(() => {
|
|||
|
|
init();
|
|||
|
|
});
|
|||
|
|
onUnmounted(() => {
|
|||
|
|
unInit();
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.index-module__NV_5cW__container {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
//background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%); /* 深蓝到深紫背景 */
|
|||
|
|
padding: 2rem 1rem;
|
|||
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 头部样式 */
|
|||
|
|
.index-module__NV_5cW__header {
|
|||
|
|
width: 1400px;
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 1rem;
|
|||
|
|
position: relative;
|
|||
|
|
margin: 0 auto 3rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-content {
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 10;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.header-title-row {
|
|||
|
|
gap: 1.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.main-title {
|
|||
|
|
font-size: 3rem;
|
|||
|
|
font-weight: 800;
|
|||
|
|
color: #fff;
|
|||
|
|
letter-spacing: 2px;
|
|||
|
|
background: linear-gradient(to right, #fff, #93c5fd);
|
|||
|
|
-webkit-background-clip: text;
|
|||
|
|
-webkit-text-fill-color: transparent;
|
|||
|
|
text-shadow: 0 0 30px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.sub-title {
|
|||
|
|
color: #94a3b8;
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
font-weight: 300;
|
|||
|
|
letter-spacing: 1px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.title-decoration {
|
|||
|
|
width: 60px;
|
|||
|
|
height: 2px;
|
|||
|
|
background: linear-gradient(90deg, transparent, #60a5fa);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.title-decoration.right {
|
|||
|
|
background: linear-gradient(90deg, #60a5fa, transparent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 标题栏图表 Icon */
|
|||
|
|
.index-module__NV_5cW__sectionHeader {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: flex-start;
|
|||
|
|
margin-bottom: 1.5rem;
|
|||
|
|
max-width: 1400px;
|
|||
|
|
margin: 0 auto 1.5rem auto;
|
|||
|
|
padding: 0 0.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.index-module__NV_5cW__sectionTitle {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 0.8rem;
|
|||
|
|
font-size: 1.4rem;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 网格系统 */
|
|||
|
|
.index-module__NV_5cW__chartsGrid {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
|
|||
|
|
gap: 1.5rem;
|
|||
|
|
max-width: 1400px;
|
|||
|
|
margin: 0 auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 卡片玻璃拟态美化 */
|
|||
|
|
.index-module__NV_5cW__chartCard {
|
|||
|
|
background: rgba(255, 255, 255, 0.95); /* 高不透明度白底,保证图表清晰 */
|
|||
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|||
|
|
border-radius: 16px;
|
|||
|
|
padding: 1.25rem;
|
|||
|
|
box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.3);
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|||
|
|
position: relative;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.index-module__NV_5cW__chartCard:hover {
|
|||
|
|
transform: translateY(-5px);
|
|||
|
|
box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.chartCard__wide {
|
|||
|
|
grid-column: 1 / -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.chartCard__title {
|
|||
|
|
font-size: 1.05rem;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: #1e293b;
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
text-align: center;
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.chartCard__container {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 300px;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 列表样式优化 */
|
|||
|
|
.list-container {
|
|||
|
|
height: 300px;
|
|||
|
|
overflow-y: hidden;
|
|||
|
|
padding: 0 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-list {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 0.6rem;
|
|||
|
|
padding-bottom: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 0.75rem;
|
|||
|
|
padding: 0.75rem;
|
|||
|
|
background: #f8fafc;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
border: 1px solid #e2e8f0;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item:hover {
|
|||
|
|
background: #fff;
|
|||
|
|
border-color: #3b82f6;
|
|||
|
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-rank-wrapper {
|
|||
|
|
width: 24px;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-rank {
|
|||
|
|
width: 20px;
|
|||
|
|
height: 20px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
background: #cbd5e1;
|
|||
|
|
color: #fff;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
font-size: 11px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 前三名高亮 */
|
|||
|
|
.project-item:nth-child(1) .project-rank {
|
|||
|
|
background: linear-gradient(135deg, #fbbf24, #d97706);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item:nth-child(2) .project-rank {
|
|||
|
|
background: linear-gradient(135deg, #94a3b8, #64748b);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item:nth-child(3) .project-rank {
|
|||
|
|
background: linear-gradient(135deg, #dba789, #b07d62);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-rank.rank-new {
|
|||
|
|
background: linear-gradient(135deg, #f43f5e, #e11d48) !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-info {
|
|||
|
|
flex: 1;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 2px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-name {
|
|||
|
|
font-size: 13px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-desc {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: #64748b;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-stars {
|
|||
|
|
font-size: 11px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #f59e0b;
|
|||
|
|
background: #fffbeb;
|
|||
|
|
padding: 2px 6px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-org-tag {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: #3b82f6;
|
|||
|
|
background: #eff6ff;
|
|||
|
|
padding: 2px 6px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Tabs 美化 */
|
|||
|
|
.tabs-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
gap: 4px;
|
|||
|
|
background: #f1f5f9;
|
|||
|
|
padding: 4px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
margin: 0 auto 0.8rem auto;
|
|||
|
|
width: fit-content;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tab-pill {
|
|||
|
|
font-size: 12px;
|
|||
|
|
padding: 4px 12px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
color: #64748b;
|
|||
|
|
font-weight: 500;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tab-pill.active {
|
|||
|
|
background: #fff;
|
|||
|
|
color: #3b82f6;
|
|||
|
|
font-weight: 600;
|
|||
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 来源 Footer 样式 (修改版:数据来源:Icon + Text) */
|
|||
|
|
.source-footer {
|
|||
|
|
margin-top: auto;
|
|||
|
|
padding-top: 0.75rem;
|
|||
|
|
border-top: 1px solid #f1f5f9;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: flex-end; /* 右对齐 */
|
|||
|
|
font-size: 11px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.source-label {
|
|||
|
|
color: #94a3b8; /* 浅灰色标签 */
|
|||
|
|
margin-right: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.source-content {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 6px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.source-icon {
|
|||
|
|
width: 14px;
|
|||
|
|
height: 14px;
|
|||
|
|
opacity: 0.8;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.source-logo-circle {
|
|||
|
|
width: 18px;
|
|||
|
|
height: 18px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.source-logo-text {
|
|||
|
|
font-size: 9px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
background: #333;
|
|||
|
|
color: #fff;
|
|||
|
|
padding: 0 3px;
|
|||
|
|
border-radius: 2px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.source-name {
|
|||
|
|
color: #475569;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 深一点的灰色文字 */
|
|||
|
|
|
|||
|
|
|
|||
|
|
/* 行动区域 */
|
|||
|
|
.index-module__NV_5cW__actionSection {
|
|||
|
|
max-width: 1400px;
|
|||
|
|
margin: 2rem auto 0;
|
|||
|
|
text-align: center;
|
|||
|
|
border-radius: 24px;
|
|||
|
|
padding: 3rem;
|
|||
|
|
background: rgba(30, 41, 59, 0.6); /* 深色背景 */
|
|||
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|||
|
|
backdrop-filter: blur(12px);
|
|||
|
|
position: relative;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.index-module__NV_5cW__actionBackground {
|
|||
|
|
position: absolute;
|
|||
|
|
inset: 0;
|
|||
|
|
background: radial-gradient(circle at top right, rgba(59, 130, 246, 0.15), transparent 40%);
|
|||
|
|
z-index: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.index-module__NV_5cW__actionContent {
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.index-module__NV_5cW__actionIcon {
|
|||
|
|
color: #60a5fa;
|
|||
|
|
background: rgba(59, 130, 246, 0.1);
|
|||
|
|
border-radius: 50%;
|
|||
|
|
width: 3.5rem;
|
|||
|
|
height: 3.5rem;
|
|||
|
|
margin: 0 auto 1.5rem;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: center;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.index-module__NV_5cW__actionTitle {
|
|||
|
|
font-size: 1.8rem;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: #fff;
|
|||
|
|
margin-bottom: 0.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.index-module__NV_5cW__actionDescription {
|
|||
|
|
color: #cbd5e1;
|
|||
|
|
margin-bottom: 1.5rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-btn {
|
|||
|
|
background: linear-gradient(135deg, #3b82f6, #6366f1);
|
|||
|
|
border: none;
|
|||
|
|
color: white;
|
|||
|
|
padding: 0.8rem 2.5rem;
|
|||
|
|
border-radius: 50px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
cursor: pointer;
|
|||
|
|
font-size: 1rem;
|
|||
|
|
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4);
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-btn:hover {
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 工具类 */
|
|||
|
|
.chart {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.num-empty {
|
|||
|
|
height: 2px;
|
|||
|
|
width: 50%;
|
|||
|
|
background: #ddd;
|
|||
|
|
margin: 50% auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.language-tab {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 0.5rem;
|
|||
|
|
background: linear-gradient(135deg, #f0f4ff, #faf5ff);
|
|||
|
|
border-radius: 1rem;
|
|||
|
|
padding: 0.3rem;
|
|||
|
|
cursor: pointer;
|
|||
|
|
user-select: none;
|
|||
|
|
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
|
|||
|
|
border: 1px solid rgba(59, 130, 246, 0.1);
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.language-tab:hover {
|
|||
|
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
|
|||
|
|
border-color: rgba(59, 130, 246, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.language-tab span {
|
|||
|
|
padding: 0.45rem 1.2rem;
|
|||
|
|
border-radius: 0.75rem;
|
|||
|
|
font-size: 0.875rem;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #666;
|
|||
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|||
|
|
position: relative;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.language-tab span::before {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
top: 50%;
|
|||
|
|
left: 50%;
|
|||
|
|
width: 0;
|
|||
|
|
height: 0;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background: rgba(255, 255, 255, 0.3);
|
|||
|
|
transform: translate(-50%, -50%);
|
|||
|
|
transition: width 0.6s, height 0.6s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.language-tab span:hover::before {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.language-tab span.active {
|
|||
|
|
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
|||
|
|
color: #fff;
|
|||
|
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
|||
|
|
transform: translateY(-1px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.language-tab span.active::after {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.2), transparent);
|
|||
|
|
border-radius: 0.75rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-scroll-list {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 10px;
|
|||
|
|
padding-bottom: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 12px;
|
|||
|
|
background: #f8fafc;
|
|||
|
|
border-radius: 10px;
|
|||
|
|
border: 1px solid #eef2f6;
|
|||
|
|
transition: all 0.3s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-item:hover {
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-color: #3b82f6;
|
|||
|
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.08);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-logo {
|
|||
|
|
width: 40px;
|
|||
|
|
height: 40px;
|
|||
|
|
background: linear-gradient(135deg, #3b82f6, #6366f1);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 18px;
|
|||
|
|
margin-right: 12px;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-info {
|
|||
|
|
flex: 1;
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-top {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-name {
|
|||
|
|
font-weight: 700;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #1e293b;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-tag {
|
|||
|
|
font-size: 10px;
|
|||
|
|
padding: 1px 6px;
|
|||
|
|
background: #e0f2fe;
|
|||
|
|
color: #0369a1;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-slogan {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #64748b;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.community-status {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 4px;
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: #10b981;
|
|||
|
|
background: #ecfdf5;
|
|||
|
|
padding: 2px 8px;
|
|||
|
|
border-radius: 20px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.developer-list {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 8px;
|
|||
|
|
padding: 4px 2px 20px 2px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.developer-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 10px 12px;
|
|||
|
|
background: #ffffff;
|
|||
|
|
border: 1px solid #f1f5f9;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.developer-item:hover {
|
|||
|
|
transform: scale(1.02);
|
|||
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|||
|
|
border-color: #e2e8f0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 排名样式 */
|
|||
|
|
.dev-rank {
|
|||
|
|
width: 24px;
|
|||
|
|
font-weight: 800;
|
|||
|
|
font-style: italic;
|
|||
|
|
color: #94a3b8;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dev-rank.rank-1 {
|
|||
|
|
color: #fbbf24;
|
|||
|
|
font-size: 18px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dev-rank.rank-2 {
|
|||
|
|
color: #94a3b8;
|
|||
|
|
font-size: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dev-rank.rank-3 {
|
|||
|
|
color: #b45309;
|
|||
|
|
font-size: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 头像占位 */
|
|||
|
|
.dev-avatar {
|
|||
|
|
width: 36px;
|
|||
|
|
height: 36px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: white;
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 16px;
|
|||
|
|
margin-right: 12px;
|
|||
|
|
box-shadow: inset 0 -2px 4px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dev-info {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dev-name {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: #1e293b;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dev-tag {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: #64748b;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dev-score {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: #10b981;
|
|||
|
|
background: #f0fdf4;
|
|||
|
|
padding: 2px 8px;
|
|||
|
|
border-radius: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
.main-title {
|
|||
|
|
font-size: 2rem;
|
|||
|
|
}
|
|||
|
|
.index-module__NV_5cW__chartsGrid {
|
|||
|
|
grid-template-columns: 1fr;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|