docs: 文档首页新增 stars 对比图

This commit is contained in:
click33
2026-01-24 04:19:42 +08:00
parent 84795e9014
commit cefa368f4d
3 changed files with 464 additions and 1 deletions

View File

@@ -41,16 +41,22 @@ Sa-Token 无意发明任何晦涩概念提升逼格,但在处理 issue 、Q群
- 单地登录:指同一时间只能在一个地方登录,新登录会挤掉旧登录,也可以叫:单端登录。
- 多地登录:指同一时间可以在不同地方登录,新登录会和旧登录共存,也可以叫:多端登录。
- 同端互斥登录在同一类型设备上只允许单地点登录在不同类型设备上允许同时在线参考腾讯QQ的登录模式手机和电脑可以同时在线但不能两个手机同时在线。
- 限量登录:限定账号登录设备总数量,低于此数量时可以正常登录,高于此数量后每次登录自动清退一个之前的登录。
- 记住我模式:指在一个设备终端登录成功,该设备重启之后依然保持登录状态。
- 单点登录:在进入多个系统时,只需要登录一次即可。解决用户在不同系统间频繁登录的问题。
- 同端多登录:指在一个终端可以同时登录多个账号。
- 记住我模式:指在一个设备终端登录成功,该设备重启之后依然保持登录状态。
#### 几种注销策略:
- 单端注销:只在调用登录的一端注销。
- 全端注销:一端注销,全端下线。
- 同端注销:发起注销后,同类型设备端一起下线,不同设备类型不受影响。
- 单点注销:与单点登录对应,一个系统注销,所有系统一起下线。
<p><a class="case-btn case-btn-video" href="https://www.bilibili.com/video/BV1XrzABbEa1/" target="_blank">
视频讲解:如何设计出最优的登录会话策略?单端登录、强制下线、多端互踢、记住我登录
</a></p>
#### 几种鉴权方式:
- 代码鉴权:在代码里直接调用 `StpUtil.checkXxx` 相关 API 进行鉴权。

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,412 @@
<!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>