更新 README 界面截图,同步 Feed 排序与 TipTap 发帖等新 UI。
改进截图脚本:登录后抓取发帖页,使用富文本示例帖并优化等待逻辑。 Co-authored-by: Cursor <cursoragent@cursor.com>
11
README.md
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<img src="docs/screenshots/home-light.png" alt="姜十三论坛首页 - 浅色主题三栏布局" width="92%">
|
<img src="docs/screenshots/home-light.png" alt="姜十三论坛首页 - 浅色主题三栏布局" width="92%">
|
||||||
|
|
||||||
<sub>浅色主题 · V2EX/NGA 风格三栏布局 · 虚拟滚动帖列表</sub>
|
<sub>浅色主题 · 三栏布局 · Feed 排序 · 虚拟滚动帖列表</sub>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<td width="50%" align="center">
|
<td width="50%" align="center">
|
||||||
<img src="docs/screenshots/home-light.png" alt="浅色主题首页" width="100%">
|
<img src="docs/screenshots/home-light.png" alt="浅色主题首页" width="100%">
|
||||||
<br><b>浅色主题</b><br>
|
<br><b>浅色主题</b><br>
|
||||||
<sub>左栏板块导航 · 中间帖列表 · 右栏热门/动态/在线</sub>
|
<sub>左栏板块导航 · Feed 排序切换 · 右栏热门/动态/在线</sub>
|
||||||
</td>
|
</td>
|
||||||
<td width="50%" align="center">
|
<td width="50%" align="center">
|
||||||
<img src="docs/screenshots/home-dark.png" alt="暗色主题首页" width="100%">
|
<img src="docs/screenshots/home-dark.png" alt="暗色主题首页" width="100%">
|
||||||
@@ -55,19 +55,19 @@
|
|||||||
<td width="50%" align="center">
|
<td width="50%" align="center">
|
||||||
<img src="docs/screenshots/post-detail.png" alt="帖子详情页" width="100%">
|
<img src="docs/screenshots/post-detail.png" alt="帖子详情页" width="100%">
|
||||||
<br><b>帖子详情</b><br>
|
<br><b>帖子详情</b><br>
|
||||||
<sub>楼层式回复 · 引用回复 · 登录可见内容</sub>
|
<sub>TipTap 富文本渲染 · 标签展示 · 点赞收藏互动</sub>
|
||||||
</td>
|
</td>
|
||||||
<td width="50%" align="center">
|
<td width="50%" align="center">
|
||||||
<img src="docs/screenshots/mobile-home.png" alt="移动端首页" width="280">
|
<img src="docs/screenshots/mobile-home.png" alt="移动端首页" width="280">
|
||||||
<br><b>移动端适配</b><br>
|
<br><b>移动端适配</b><br>
|
||||||
<sub>侧栏自动收起 · 触控友好 · 板块快捷筛选</sub>
|
<sub>板块快捷筛选 · Feed 排序 · 触控友好列表</sub>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="docs/screenshots/compose.png" alt="发帖页" width="360">
|
<img src="docs/screenshots/compose.png" alt="发帖页" width="360">
|
||||||
<br><b>发帖</b> — TipTap 富文本编辑器,支持本地上传图片
|
<br><b>发帖</b> — 板块胶囊选择 · TipTap 工具栏 · 本地上传图片
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -191,6 +191,7 @@ make dev # Linux / macOS
|
|||||||
|
|
||||||
- 修改 Go 代码、HTML 模板、embed 静态资源 → `go build` / `make build`
|
- 修改 Go 代码、HTML 模板、embed 静态资源 → `go build` / `make build`
|
||||||
- 发布单二进制前 → `npm run build` + `make build`
|
- 发布单二进制前 → `npm run build` + `make build`
|
||||||
|
- 更新 README 界面截图 → 启动服务后执行 `node scripts/capture-screenshots.mjs`
|
||||||
|
|
||||||
> 直接访问 `:3000` 看到的是上次 build 嵌入的前端;开发时请用 `:5173`。
|
> 直接访问 `:3000` 看到的是上次 build 嵌入的前端;开发时请用 `:5173`。
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 246 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 503 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 509 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 420 KiB After Width: | Height: | Size: 315 KiB |
@@ -7,6 +7,11 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|||||||
const outDir = path.join(__dirname, '..', 'docs', 'screenshots');
|
const outDir = path.join(__dirname, '..', 'docs', 'screenshots');
|
||||||
const base = process.env.J13_URL || 'http://localhost:8080';
|
const base = process.env.J13_URL || 'http://localhost:8080';
|
||||||
|
|
||||||
|
/** 用于发帖页截图的演示账号 */
|
||||||
|
const DEMO_USER = { username: 'admin', password: 'admin123' };
|
||||||
|
/** 富文本示例帖,用于详情页截图 */
|
||||||
|
const DEMO_POST_ID = Number(process.env.J13_POST_ID || 36);
|
||||||
|
|
||||||
async function shot(page, name, opts = {}) {
|
async function shot(page, name, opts = {}) {
|
||||||
const file = path.join(outDir, name);
|
const file = path.join(outDir, name);
|
||||||
await page.screenshot({ path: file, type: 'png', ...opts });
|
await page.screenshot({ path: file, type: 'png', ...opts });
|
||||||
@@ -21,6 +26,16 @@ async function setTheme(page, theme) {
|
|||||||
}, theme);
|
}, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 通过登录页写入 Cookie,供发帖页等需登录场景使用 */
|
||||||
|
async function login(page) {
|
||||||
|
await page.goto(`${base}/login`, { waitUntil: 'networkidle' });
|
||||||
|
await page.fill('input[name="username"], #username, input[autocomplete="username"]', DEMO_USER.username);
|
||||||
|
await page.fill('input[name="password"], #password, input[type="password"]', DEMO_USER.password);
|
||||||
|
await page.click('button[type="submit"]');
|
||||||
|
await page.waitForURL((url) => !url.pathname.endsWith('/login'), { timeout: 10000 });
|
||||||
|
await page.waitForTimeout(600);
|
||||||
|
}
|
||||||
|
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const context = await browser.newContext({ deviceScaleFactor: 2 });
|
const context = await browser.newContext({ deviceScaleFactor: 2 });
|
||||||
|
|
||||||
@@ -33,7 +48,8 @@ try {
|
|||||||
await page.setViewportSize({ width: 1440, height: 900 });
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
await setTheme(page, 'light');
|
await setTheme(page, 'light');
|
||||||
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
||||||
await page.waitForTimeout(800);
|
await page.waitForSelector('.feed-sort-bar, .virtual-post-list, .post-list', { timeout: 10000 });
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
await shot(page, 'home-light.png');
|
await shot(page, 'home-light.png');
|
||||||
await page.close();
|
await page.close();
|
||||||
}
|
}
|
||||||
@@ -44,30 +60,42 @@ try {
|
|||||||
await page.setViewportSize({ width: 1440, height: 900 });
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
await setTheme(page, 'dark');
|
await setTheme(page, 'dark');
|
||||||
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
||||||
await page.waitForTimeout(800);
|
await page.waitForSelector('.feed-sort-bar, .virtual-post-list, .post-list', { timeout: 10000 });
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
await shot(page, 'home-dark.png');
|
await shot(page, 'home-dark.png');
|
||||||
await page.close();
|
await page.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 帖子详情
|
// 帖子详情(富文本 + 楼层回复)
|
||||||
{
|
{
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
await page.setViewportSize({ width: 1440, height: 900 });
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
await setTheme(page, 'light');
|
await setTheme(page, 'light');
|
||||||
await page.goto(`${base}/post/2`, { waitUntil: 'networkidle' });
|
await page.goto(`${base}/post/${DEMO_POST_ID}`, { waitUntil: 'networkidle' });
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForSelector('.post-detail-page, .post-detail-title', { timeout: 10000 });
|
||||||
|
await page.waitForTimeout(1200);
|
||||||
await shot(page, 'post-detail.png');
|
await shot(page, 'post-detail.png');
|
||||||
await page.close();
|
await page.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发帖编辑器
|
// 发帖编辑器(需登录)
|
||||||
{
|
{
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
await page.setViewportSize({ width: 1440, height: 900 });
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
await setTheme(page, 'light');
|
await setTheme(page, 'light');
|
||||||
|
await login(page);
|
||||||
await page.goto(`${base}/compose`, { waitUntil: 'networkidle' });
|
await page.goto(`${base}/compose`, { waitUntil: 'networkidle' });
|
||||||
|
await page.waitForSelector('.compose-page, .compose-canvas, .article-editor', { timeout: 10000 });
|
||||||
|
await page.fill('.compose-title', '分享你的技术见解');
|
||||||
|
const editor = page.locator('.article-editor .ProseMirror, .article-editor [contenteditable="true"]').first();
|
||||||
|
if (await editor.count()) {
|
||||||
|
await editor.click();
|
||||||
|
await editor.fill('支持 **粗体**、列表、代码块与图片上传的 TipTap 编辑器。');
|
||||||
|
}
|
||||||
await page.waitForTimeout(800);
|
await page.waitForTimeout(800);
|
||||||
await shot(page, 'compose.png');
|
await shot(page, 'compose.png', {
|
||||||
|
clip: { x: 0, y: 56, width: 1440, height: 844 },
|
||||||
|
});
|
||||||
await page.close();
|
await page.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +105,8 @@ try {
|
|||||||
await page.setViewportSize({ width: 390, height: 844 });
|
await page.setViewportSize({ width: 390, height: 844 });
|
||||||
await setTheme(page, 'light');
|
await setTheme(page, 'light');
|
||||||
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
||||||
await page.waitForTimeout(800);
|
await page.waitForSelector('.feed-sort-bar, .virtual-post-list, .post-list', { timeout: 10000 });
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
await shot(page, 'mobile-home.png', { fullPage: true });
|
await shot(page, 'mobile-home.png', { fullPage: true });
|
||||||
await page.close();
|
await page.close();
|
||||||
}
|
}
|
||||||
|
|||||||