feat: 统一文件上传接口 + 模板图片展示 + 导航页 + 侧边栏调整

This commit is contained in:
admin
2026-07-10 20:43:02 +08:00
parent cbd0430452
commit ce6823e282
25 changed files with 933 additions and 166 deletions

View File

@@ -918,8 +918,7 @@ function showToast(msg){
}
/* 生成封面占位图(SVG data URI,避免外部依赖) */
function coverImage(game){
if(game.cover && game.cover.startsWith('http')) return game.cover;
function placeholderImage(game){
const palettes = [
['#ff0080','#8b00ff'],['#00f0ff','#0066ff'],['#8b00ff','#ff0080'],
['#00f0ff','#39ff14'],['#ff0080','#ffe600'],['#0066ff','#8b00ff'],
@@ -947,11 +946,17 @@ function coverImage(game){
</svg>`;
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
}
function coverImage(game){
if(game.cover) return game.cover;
return placeholderImage(game);
}
function coverFallback(img, id){
img.onerror=null;
img.src=placeholderImage({id:id, name:img.alt||'UNKNOWN'});
}
/* 截图占位图 */
function shotImage(game, idx){
const url = (game.screenshots && parseJSON(game.screenshots)[idx]);
if(url && url.startsWith('http')) return url;
function shotPlaceholder(game, idx){
const colors = ['#00f0ff','#ff0080','#8b00ff','#39ff14','#ffe600'];
const c = colors[(idx+game.id) % colors.length];
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="680" height="380" viewBox="0 0 680 380">
@@ -963,6 +968,15 @@ function shotImage(game, idx){
</svg>`;
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
}
function shotImage(game, idx){
const url = (game.screenshots && parseJSON(game.screenshots)[idx]);
if(url) return url;
return shotPlaceholder(game, idx);
}
function shotFallback(img, gameId, idx){
img.onerror=null;
img.src=shotPlaceholder({id:gameId}, idx);
}
/* ============================================================
模拟数据(后端不可用时自动回退)
@@ -1166,7 +1180,7 @@ function gameCardHTML(game, opts){
${game.is_hot ? '<span class="badge hot">HOT</span>' : ''}
${game.is_new ? '<span class="badge new">NEW</span>' : ''}
<div class="rating-float">&#9733; ${parseFloat(game.rating||0).toFixed(1)}</div>
<img src="${coverImage(game)}" alt="${game.name}" loading="lazy">
<img src="${coverImage(game)}" alt="${game.name}" loading="lazy" onerror="coverFallback(this, ${game.id})">
<div class="cover-grad"></div>
</div>
<div class="card-body">
@@ -1381,7 +1395,7 @@ function renderModal(game){
modal.innerHTML = `
<button class="modal-close" onclick="closeModal()">&times;</button>
<div class="modal-hero">
<img src="${coverImage(game)}" alt="${game.name}">
<img src="${coverImage(game)}" alt="${game.name}" onerror="coverFallback(this, ${game.id})">
<div class="modal-hero-grad"></div>
<div class="modal-hero-info">
<div class="modal-cat-tag">${game.category_name||''} // ID:${game.id}</div>
@@ -1428,7 +1442,7 @@ function renderModal(game){
${shots.length ? `
<div class="modal-section-label">游戏截图 / SCREENSHOTS</div>
<div class="modal-shots">
${shots.map((s,i)=>`<img src="${shotImage(game,i)}" alt="截图${i+1}" loading="lazy">`).join('')}
${shots.map((s,i)=>`<img src="${shotImage(game,i)}" alt="截图${i+1}" loading="lazy" onerror="shotFallback(this, ${game.id}, ${i})">`).join('')}
</div>` : ''}
<div class="modal-actions">