Skip to content

Commit b6fa8eb

Browse files
author
TARS
committed
Merge branch 'feature/multi-user-ranking' into master for v1.1.0 release
2 parents 6dde3de + 533a4e8 commit b6fa8eb

37 files changed

Lines changed: 2676 additions & 7 deletions

CHANGELOG.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,36 @@
1111

1212
### 计划功能
1313

14-
- **多人 PK 模式**:并排对比多个用户的数据
15-
- **排行榜视图**:用户排名系统
1614
- **活跃热力图**:可视化展示提交频率时间线
1715
- **题目标签分析**:按分类/标签统计解题情况
1816
- **连续打卡追踪**:记录连续刷题天数
1917
- **数据导出功能**:支持 CSV/JSON 格式导出统计
2018

2119
---
2220

21+
## [1.1.0] - 2026-02-06
22+
23+
### 新增
24+
25+
- **多人竞技场**:查询 2 人及以上时自动显示排名面板
26+
- 7 维度排名:刷题数、积分、效率、难度、活跃度、夜猫子、速度
27+
- 趣味头衔系统:根据排名百分比动态分配头衔(题海战术家、一次过、深夜战神等)
28+
- PK 模式:任选两人 1v1 全维度对比,自动统计获胜局数
29+
- 排行榜:实时差距计算,显示与前一名的具体差距
30+
- **排名面板可折叠**:默认折叠显示榜首信息,点击展开查看完整功能
31+
- **紧凑排行榜**:行式布局替代卡片式,提高信息密度,减少篇幅占用
32+
33+
### 变更
34+
35+
- **国际化完善**:新增排名相关翻译(中英文)
36+
- **移动端适配优化**:排名 Tab 支持横向滚动,小屏幕友好
37+
38+
---
39+
40+
## [1.0.4] - 2026-02-06
41+
42+
---
43+
2344
## [1.0.4] - 2026-02-06
2445

2546
### 变更

app-en-mobile.png

53.2 KB
Loading

app-en-ranking.png

61.5 KB
Loading

app-zh-home.png

62.3 KB
Loading

app-zh-mobile.png

53.2 KB
Loading

app-zh-ranking.png

62.2 KB
Loading

capture-3users.cjs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
const puppeteer = require('puppeteer-core');
2+
3+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
4+
5+
async function capture() {
6+
const browser = await puppeteer.launch({
7+
headless: 'new',
8+
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
9+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
10+
});
11+
12+
const page = await browser.newPage();
13+
await page.setViewport({ width: 1280, height: 2500 });
14+
15+
// 访问应用
16+
await page.goto('http://localhost:5174/', { waitUntil: 'networkidle0' });
17+
await sleep(3000);
18+
19+
// 点击"最近30天"按钮
20+
const buttons = await page.$$('button');
21+
for (const btn of buttons) {
22+
const text = await btn.evaluate(el => el.textContent);
23+
if (text?.includes('最近30天') || text?.includes('Last 30 Days')) {
24+
await btn.click();
25+
console.log('Clicked 最近30天');
26+
break;
27+
}
28+
}
29+
await sleep(1000);
30+
31+
// 输入三个用户
32+
const input = await page.$('input');
33+
if (input) {
34+
await input.click();
35+
await input.type('yume7,jiangly,tourist');
36+
await sleep(1000);
37+
38+
// 点击查询按钮
39+
const searchButtons = await page.$$('button');
40+
for (const btn of searchButtons) {
41+
const text = await btn.evaluate(el => el.textContent);
42+
if (text?.includes('查询') || text?.includes('Search')) {
43+
await btn.click();
44+
console.log('Clicked search');
45+
break;
46+
}
47+
}
48+
49+
// 等待加载完成
50+
await sleep(12000);
51+
52+
// 截图 - 中文 PC 完整页面
53+
await page.screenshot({ path: 'final-zh-3users.png', fullPage: true });
54+
console.log('3 users screenshot saved');
55+
56+
// 滚动到 PK 区域
57+
await page.evaluate(() => {
58+
const pkTitle = Array.from(document.querySelectorAll('h3')).find(el => el.textContent.includes('PK') || el.textContent.includes('对比'));
59+
if (pkTitle) pkTitle.scrollIntoView({ behavior: 'instant', block: 'start' });
60+
});
61+
await sleep(1000);
62+
63+
// 确保 PK 区域是 yume7 vs jiangly(可能需要选择)
64+
// 先截图当前状态
65+
await page.screenshot({ path: 'final-pk-yume7-jiangly.png', fullPage: false });
66+
console.log('PK screenshot saved');
67+
68+
// 尝试设置挑战者为 yume7,对手为 jiangly
69+
const selects = await page.$$('select');
70+
if (selects.length >= 2) {
71+
// 第一个 select 是挑战者
72+
await selects[0].select('yume7');
73+
await sleep(500);
74+
// 第二个 select 是对手
75+
await selects[1].select('jiangly');
76+
await sleep(1000);
77+
78+
await page.screenshot({ path: 'final-pk-result.png', fullPage: false });
79+
console.log('PK result screenshot saved');
80+
}
81+
}
82+
83+
await browser.close();
84+
console.log('All done');
85+
}
86+
87+
capture().catch(console.error);

capture-all.cjs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const puppeteer = require('puppeteer-core');
2+
3+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
4+
5+
async function captureScreenshots() {
6+
const browser = await puppeteer.launch({
7+
headless: 'new',
8+
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
9+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
10+
});
11+
12+
// 设置视口尺寸
13+
const viewports = {
14+
pc: { width: 1280, height: 900 },
15+
mobile: { width: 375, height: 812 }
16+
};
17+
18+
const scenarios = [
19+
{ name: 'zh-pc', lang: 'zh-CN', viewport: viewports.pc },
20+
{ name: 'en-pc', lang: 'en', viewport: viewports.pc },
21+
{ name: 'zh-mobile', lang: 'zh-CN', viewport: viewports.mobile },
22+
{ name: 'en-mobile', lang: 'en', viewport: viewports.mobile },
23+
];
24+
25+
for (const scenario of scenarios) {
26+
const page = await browser.newPage();
27+
await page.setViewport(scenario.viewport);
28+
29+
// 设置语言
30+
await page.setExtraHTTPHeaders({
31+
'Accept-Language': scenario.lang
32+
});
33+
34+
// 设置 localStorage 语言
35+
await page.evaluateOnNewDocument((lang) => {
36+
localStorage.setItem('i18nextLng', lang);
37+
}, scenario.lang);
38+
39+
await page.goto('http://localhost:8080/ranking-preview.html', { waitUntil: 'networkidle0' });
40+
await sleep(1000);
41+
42+
// 如果是英文模式,点击切换语言按钮(假设有)
43+
if (scenario.lang === 'en') {
44+
// 在预览页面,我需要模拟语言切换
45+
// 由于预览页面是静态HTML,我会直接截图
46+
}
47+
48+
await page.screenshot({
49+
path: `screenshot-${scenario.name}.png`,
50+
fullPage: true
51+
});
52+
53+
console.log(`Screenshot saved: screenshot-${scenario.name}.png`);
54+
await page.close();
55+
}
56+
57+
await browser.close();
58+
}
59+
60+
captureScreenshots().catch(console.error);

capture-final.cjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const puppeteer = require('puppeteer-core');
2+
3+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
4+
5+
async function captureRanking() {
6+
const browser = await puppeteer.launch({
7+
headless: 'new',
8+
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
9+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
10+
});
11+
12+
const page = await browser.newPage();
13+
await page.setViewport({ width: 1280, height: 2500 });
14+
15+
// 访问应用
16+
await page.goto('http://localhost:5174/', { waitUntil: 'networkidle0' });
17+
await sleep(3000);
18+
19+
// 查找输入框并输入用户
20+
const input = await page.$('input');
21+
if (input) {
22+
await input.type('yume7,jiangly');
23+
await sleep(1000);
24+
25+
// 查找并点击查询按钮
26+
const buttons = await page.$$('button');
27+
for (const btn of buttons) {
28+
const text = await btn.evaluate(el => el.textContent);
29+
if (text?.includes('查询') || text?.includes('Search')) {
30+
await btn.click();
31+
console.log('Clicked search button');
32+
break;
33+
}
34+
}
35+
36+
// 等待加载完成 - 等待更长时间让API返回数据
37+
console.log('Waiting for data to load...');
38+
await sleep(10000);
39+
40+
// 截图中文PC
41+
await page.screenshot({ path: 'ranking-zh-pc-full.png', fullPage: true });
42+
console.log('Chinese PC ranking screenshot saved');
43+
44+
// 滚动页面确保看到排名区域
45+
await page.evaluate(() => window.scrollTo(0, 600));
46+
await sleep(1000);
47+
await page.screenshot({ path: 'ranking-zh-pc-scroll.png', fullPage: false });
48+
console.log('Chinese PC scrolled screenshot saved');
49+
}
50+
51+
await browser.close();
52+
console.log('All screenshots done');
53+
}
54+
55+
captureRanking().catch(err => {
56+
console.error(err);
57+
process.exit(1);
58+
});

capture-optimized.cjs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const puppeteer = require('puppeteer-core');
2+
3+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
4+
5+
async function capture() {
6+
const browser = await puppeteer.launch({
7+
headless: 'new',
8+
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
9+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
10+
});
11+
12+
const page = await browser.newPage();
13+
await page.setViewport({ width: 1280, height: 1200 });
14+
15+
await page.goto('http://localhost:5174/', { waitUntil: 'networkidle0' });
16+
await sleep(3000);
17+
18+
// 点击最近30天
19+
const buttons = await page.$$('button');
20+
for (const btn of buttons) {
21+
const text = await btn.evaluate(el => el.textContent);
22+
if (text?.includes('最近30天')) {
23+
await btn.click();
24+
break;
25+
}
26+
}
27+
await sleep(500);
28+
29+
// 输入3个用户
30+
const input = await page.$('input');
31+
if (input) {
32+
await input.type('yume7,jiangly,tourist');
33+
await sleep(500);
34+
35+
const searchButtons = await page.$$('button');
36+
for (const btn of searchButtons) {
37+
const text = await btn.evaluate(el => el.textContent);
38+
if (text?.includes('查询')) {
39+
await btn.click();
40+
break;
41+
}
42+
}
43+
44+
await sleep(12000);
45+
46+
// 截图 - 折叠状态
47+
await page.screenshot({ path: 'optimized-collapsed.png', fullPage: false });
48+
console.log('Collapsed screenshot saved');
49+
50+
// 找到并点击展开按钮
51+
const expandButtons = await page.$$('button');
52+
for (const btn of expandButtons) {
53+
const text = await btn.evaluate(el => el.textContent);
54+
if (text?.includes('展开') || text?.includes('多人竞技场')) {
55+
await btn.click();
56+
console.log('Clicked expand');
57+
break;
58+
}
59+
}
60+
61+
await sleep(1000);
62+
await page.screenshot({ path: 'optimized-expanded.png', fullPage: true });
63+
console.log('Expanded screenshot saved');
64+
}
65+
66+
await browser.close();
67+
}
68+
69+
capture().catch(console.error);

0 commit comments

Comments
 (0)