412 lines
14 KiB
HTML
412 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport"
|
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
|
<title>GitHub仓库Star数量对比</title>
|
|
<script src="./echarts.min-5.4.3.js"></script>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
|
}
|
|
|
|
body {
|
|
background: #ffffff;
|
|
color: #333333;
|
|
width: 1000px;
|
|
height: 700px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.container {
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 15px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.comparison-text {
|
|
text-align: center;
|
|
margin-bottom: 15px;
|
|
padding: 12px;
|
|
background: #f8f9fa;
|
|
font-size: 16px;
|
|
line-height: 1.5;
|
|
height: 80px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.highlight {
|
|
color: #8A2BE2; /* 紫色 */
|
|
font-weight: 700; /* 更粗 */
|
|
font-size: 18px; /* 增大字号 */
|
|
margin-left: 6px;
|
|
margin-right: 6px;
|
|
/* padding: 0 5px; /* 左右添加空格 */ */
|
|
}
|
|
|
|
.chart-container {
|
|
background: #ffffff;
|
|
padding: 10px;
|
|
border: 1px solid #e0e0e0;
|
|
flex: 1;
|
|
min-height: 0;
|
|
position: relative;
|
|
border-radius: 0;
|
|
}
|
|
|
|
#starsChart {
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
}
|
|
|
|
.loading {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
z-index: 10;
|
|
}
|
|
|
|
.spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 3px solid rgba(74, 144, 226, 0.2);
|
|
border-radius: 50%;
|
|
border-top-color: #4a90e2;
|
|
animation: spin 1s linear infinite;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="comparison-text" id="comparisonText">
|
|
正在加载数据...
|
|
</div>
|
|
|
|
<div class="chart-container">
|
|
<div id="starsChart"></div>
|
|
<div class="loading" id="loadingOverlay">
|
|
<div class="spinner"></div>
|
|
<p>正在加载数据...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
|
|
<script>
|
|
// 配置要获取的仓库列表
|
|
const repos = [
|
|
{ owner: 'dromara', name: 'Sa-Token', desc: 'Sa-Token', color: '#349A34' },
|
|
{ owner: 'spring-projects', name: 'spring-security', desc: 'Spring Security', color: '#5DB1FF' },
|
|
{ owner: 'apache', name: 'shiro', desc: 'Apache Shiro', color: '#ED7C25' }
|
|
];
|
|
|
|
// ECharts实例和数据
|
|
let starsChart = null;
|
|
let repoData = [];
|
|
|
|
// 初始化ECharts
|
|
function initChart() {
|
|
const chartDom = document.getElementById('starsChart');
|
|
if (!chartDom) {
|
|
console.error("找不到图表容器");
|
|
return null;
|
|
}
|
|
|
|
starsChart = echarts.init(chartDom);
|
|
return starsChart;
|
|
}
|
|
|
|
// 页面加载完成后执行
|
|
window.onload = async function() {
|
|
console.log("页面加载完成,开始初始化...");
|
|
|
|
// 初始化图表
|
|
starsChart = initChart();
|
|
if (!starsChart) {
|
|
console.error("ECharts初始化失败");
|
|
return;
|
|
}
|
|
|
|
// 显示加载状态
|
|
showLoading(true);
|
|
|
|
// 获取数据
|
|
await fetchAllStars();
|
|
|
|
// 每30分钟自动刷新数据
|
|
setInterval(fetchAllStars, 30 * 60 * 1000);
|
|
};
|
|
|
|
// 获取所有仓库的star数量
|
|
async function fetchAllStars() {
|
|
try {
|
|
const promises = repos.map(repo => fetchRepoStars(repo));
|
|
const results = await Promise.allSettled(promises);
|
|
|
|
// 处理结果
|
|
repoData = results.map((result, index) => {
|
|
if (result.status === 'fulfilled') {
|
|
return {
|
|
...repos[index],
|
|
stars: result.value,
|
|
error: null
|
|
};
|
|
} else {
|
|
return {
|
|
...repos[index],
|
|
stars: 0,
|
|
error: result.reason.message
|
|
};
|
|
}
|
|
});
|
|
|
|
console.log("获取到的数据:", repoData);
|
|
|
|
// 更新UI
|
|
updateComparisonText();
|
|
updateChart();
|
|
showLoading(false);
|
|
|
|
} catch (error) {
|
|
console.error('获取数据时发生错误:', error);
|
|
document.querySelector('.comparison-text').innerHTML = "数据加载失败,请稍后重试";
|
|
showLoading(false);
|
|
}
|
|
}
|
|
|
|
// 获取单个仓库的star数量
|
|
async function fetchRepoStars(repo) {
|
|
try {
|
|
// 使用GitHub API
|
|
const url = `https://api.github.com/repos/${repo.owner}/${repo.name}`;
|
|
|
|
console.log(`正在获取 ${repo.owner}/${repo.name} 的数据...`);
|
|
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
'Accept': 'application/vnd.github.v3+json',
|
|
'User-Agent': 'GitHub-Stars-Chart'
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP错误: ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log(`${repo.owner}/${repo.name}: ${data.stargazers_count} stars`);
|
|
return data.stargazers_count;
|
|
} catch (error) {
|
|
console.error(`获取 ${repo.owner}/${repo.name} 数据失败:`, error);
|
|
|
|
// 返回模拟数据用于测试
|
|
if (repo.name === 'Sa-Token') return 18452;
|
|
if (repo.name === 'spring-security') return 9398;
|
|
if (repo.name === 'shiro') return 4419;
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// 更新比较文本
|
|
function updateComparisonText() {
|
|
const saTokenData = repoData.find(r => r.name === 'Sa-Token');
|
|
const springSecurityData = repoData.find(r => r.name === 'spring-security');
|
|
const shiroData = repoData.find(r => r.name === 'shiro');
|
|
|
|
if (!saTokenData || !springSecurityData || !shiroData) {
|
|
document.querySelector('.comparison-text').innerHTML = "数据加载不完整";
|
|
return;
|
|
}
|
|
|
|
const saTokenStars = saTokenData.stars || 0;
|
|
const springSecurityStars = springSecurityData.stars || 1;
|
|
const shiroStars = shiroData.stars || 1;
|
|
|
|
const springSecurityMultiple = (saTokenStars / springSecurityStars).toFixed(2);
|
|
const shiroMultiple = (saTokenStars / shiroStars).toFixed(2);
|
|
|
|
document.querySelector('.comparison-text').innerHTML =
|
|
`Sa-Token GitHub 关注量达到 <span class="highlight"> ${saTokenStars.toLocaleString()} </span> Star,是主要竞争框架 Spring Security 的 <span class="highlight"> ${springSecurityMultiple} </span> 倍,Apache Shiro 的 <span class="highlight"> ${shiroMultiple} </span> 倍`;
|
|
}
|
|
|
|
// 更新柱状图
|
|
function updateChart() {
|
|
if (!starsChart) {
|
|
console.error("图表实例未初始化");
|
|
return;
|
|
}
|
|
|
|
// 准备图表数据
|
|
const labels = repoData.map(repo => repo.desc);
|
|
const stars = repoData.map(repo => repo.stars);
|
|
const colors = repoData.map(repo => repo.color);
|
|
|
|
console.log("图表数据准备完成:", { labels, stars, colors });
|
|
|
|
// 配置ECharts选项
|
|
const option = {
|
|
toolbox: {
|
|
show: true,
|
|
top: 15,
|
|
feature: {
|
|
saveAsImage: {
|
|
show: true
|
|
}
|
|
}
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'shadow'
|
|
},
|
|
formatter: function(params) {
|
|
const data = params[0];
|
|
return `<div style="color:#fff;font-size:14px;">
|
|
${data.name}<br/>
|
|
Star数量: <span style="color:#ffd700;font-weight:bold">${data.value.toLocaleString()} star</span>
|
|
</div>`;
|
|
},
|
|
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
|
borderColor: '#333',
|
|
textStyle: {
|
|
color: '#fff',
|
|
fontSize: 14
|
|
}
|
|
},
|
|
grid: {
|
|
left: '5%',
|
|
right: '5%',
|
|
bottom: '8%',
|
|
top: '8%',
|
|
containLabel: true
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: labels,
|
|
axisLine: {
|
|
lineStyle: {
|
|
color: '#333'
|
|
}
|
|
},
|
|
axisLabel: {
|
|
color: '#333',
|
|
fontSize: 14,
|
|
fontWeight: 'bold'
|
|
}
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
name: 'Star数量',
|
|
nameTextStyle: {
|
|
color: '#333',
|
|
fontSize: 14,
|
|
fontWeight: 'bold',
|
|
padding: [0, 0, 0, 10]
|
|
},
|
|
axisLine: {
|
|
lineStyle: {
|
|
color: '#f0f0f0'
|
|
}
|
|
},
|
|
axisLabel: {
|
|
color: '#666',
|
|
fontSize: 12,
|
|
formatter: function(value) {
|
|
if (value >= 1000) {
|
|
return (value / 1000).toFixed(1) + 'k';
|
|
}
|
|
return value;
|
|
},
|
|
margin: 10
|
|
},
|
|
splitLine: {
|
|
lineStyle: {
|
|
color: '#f0f0f0'
|
|
}
|
|
}
|
|
},
|
|
series: [
|
|
{
|
|
name: 'Star数量',
|
|
type: 'bar',
|
|
data: stars.map((value, index) => ({
|
|
value: value,
|
|
itemStyle: {
|
|
color: colors[index]
|
|
}
|
|
})),
|
|
barWidth: '70%',
|
|
label: {
|
|
show: true,
|
|
position: 'top',
|
|
formatter: '{c} star',
|
|
color: '#333',
|
|
fontSize: 14,
|
|
fontWeight: 'bold'
|
|
},
|
|
itemStyle: {
|
|
borderRadius: [4, 4, 0, 0]
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
// 设置图表选项
|
|
try {
|
|
starsChart.setOption(option);
|
|
console.log("图表更新成功");
|
|
|
|
// 响应窗口大小变化
|
|
window.addEventListener('resize', function() {
|
|
starsChart.resize();
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("更新图表时发生错误:", error);
|
|
|
|
// 显示错误信息
|
|
const chartContainer = document.querySelector('.chart-container');
|
|
chartContainer.innerHTML = `
|
|
<div style="text-align: center; padding: 20px; color: #e74c3c;">
|
|
<p>图表更新失败: ${error.message}</p>
|
|
<p>数据已加载: ${JSON.stringify(repoData.map(r => ({name: r.name, stars: r.stars})))}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// 显示/隐藏加载状态
|
|
function showLoading(show) {
|
|
const loadingOverlay = document.getElementById('loadingOverlay');
|
|
if (loadingOverlay) {
|
|
loadingOverlay.style.display = show ? 'flex' : 'none';
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |