feat(assets): 优化资产预览性能并添加资源管理器连接状态提示

- 使用缩略图替代原图展示,通过 sharp 库生成缓存缩略图
- 优化资产分组逻辑,避免不必要的重计算
- 添加 WebSocket 连接状态提示到输入框
- 使用 `useCallback` 和 `useRef` 优化组件渲染性能
- 添加 AbortController 支持请求取消,防止内存泄漏
- 添加 `disconnected` 事件处理,自动重置会话状态
This commit is contained in:
2026-05-08 02:47:23 +08:00
parent a92379e20e
commit 7440ade66d
13 changed files with 680 additions and 76 deletions

View File

@@ -76,7 +76,8 @@ assetsRouter.post('/scan', async (_req, res) => {
manifest = JSON.parse(await fs.readFile(manifestPath, 'utf-8'));
} catch { continue; }
const accountId = manifest.account?.id || '';
const accountId = typeof manifest.account === 'string' ? manifest.account
: manifest.account?.id || '';
// Scan images
const imagesDir = path.join(outputDir, dir.name, 'images');
@@ -131,5 +132,35 @@ assetsRouter.get('/file', (req, res) => {
const fullPath = path.resolve(PROJECT_ROOT, filePath);
if (!fullPath.startsWith(PROJECT_ROOT)) return res.status(403).send('Forbidden');
if (!fss.existsSync(fullPath)) return res.status(404).send('Not found');
// Cache static assets for 1 hour
res.setHeader('Cache-Control', 'public, max-age=3600');
res.sendFile(fullPath);
});
// Serve resized thumbnail for images
assetsRouter.get('/thumb', async (req, res) => {
const filePath = req.query.path as string;
const size = Math.min(parseInt(req.query.size as string) || 200, 400);
if (!filePath) return res.status(400).send('Missing path');
const fullPath = path.resolve(PROJECT_ROOT, filePath);
if (!fullPath.startsWith(PROJECT_ROOT)) return res.status(403).send('Forbidden');
if (!fss.existsSync(fullPath)) return res.status(404).send('Not found');
// For non-images, redirect to full file
if (!/\.(jpe?g|png|webp)$/i.test(fullPath)) {
return res.redirect(`/api/assets/file?path=${encodeURIComponent(filePath)}`);
}
try {
const sharp = (await import('sharp')).default;
const buffer = await sharp(fullPath)
.resize(size, size, { fit: 'cover' })
.jpeg({ quality: 70 })
.toBuffer();
res.setHeader('Cache-Control', 'public, max-age=86400');
res.setHeader('Content-Type', 'image/jpeg');
res.send(buffer);
} catch {
res.sendFile(fullPath);
}
});