v1.2.9 比赛页增强:网格卡片布局(固定宽度280px,宽度足够一行多个) + 分页控件(12条/页),可与状态筛选/搜索组合

This commit is contained in:
2026-08-18 00:05:50 +08:00
parent 14317a44a2
commit 3d59c9a975
5 changed files with 38 additions and 18 deletions
+3 -6
View File
@@ -149,12 +149,9 @@ def api_player(pid):
def api_games(): def api_games():
q = request.args.get("q", "") q = request.args.get("q", "")
status = request.args.get("status", "") status = request.args.get("status", "")
limit = int(request.args.get("limit", 30)) page = max(1, int(request.args.get("page", 1)))
r = tools.search_games(q, limit=limit) size = min(40, max(1, int(request.args.get("size", 12))))
results = r.get("results", []) return jsonify(tools.list_games(q, status=status, page=page, size=size))
if status:
results = [g for g in results if g["status"] == status]
return jsonify(results)
@app.route("/api/games/<int:gid>") @app.route("/api/games/<int:gid>")
+17 -11
View File
@@ -86,7 +86,12 @@ function loadView(v) {
$$("#view-players .btn.small").forEach((x) => x.classList.toggle("active", x.dataset.pos === "")); $$("#view-players .btn.small").forEach((x) => x.classList.toggle("active", x.dataset.pos === ""));
loadPlayers(); loadPlayers();
} }
else if (v === "games") loadGames(""); else if (v === "games") {
gamesPage = 1; gamesQ = ""; gamesStatus = "";
$("#search-games").value = "";
$$("#view-games .btn.small").forEach((x) => x.classList.toggle("active", x.dataset.status === ""));
loadGames();
}
else if (v === "news") loadNews(""); else if (v === "news") loadNews("");
else if (v === "persons") loadPersons(""); else if (v === "persons") loadPersons("");
else if (v === "standings") { else if (v === "standings") {
@@ -405,7 +410,7 @@ function renderPager(d, key) {
} }
function goPage(key, p) { function goPage(key, p) {
if (key === "players") { playersPage = p; loadPlayers(); } if (key === "players") { playersPage = p; loadPlayers(); }
else if (key === "games") { gamesPage = p; loadGames($("#search-games").value.trim()); } else if (key === "games") { gamesPage = p; loadGames(); }
} }
$("#btn-players").addEventListener("click", () => { playersQ = $("#search-players").value.trim(); playersPage = 1; loadPlayers(); }); $("#btn-players").addEventListener("click", () => { playersQ = $("#search-players").value.trim(); playersPage = 1; loadPlayers(); });
@@ -439,12 +444,11 @@ async function openPlayer(id) {
} }
/* ================= 比赛 ================= */ /* ================= 比赛 ================= */
let gameStatusFilter = ""; let gamesPage = 1, gamesQ = "", gamesStatus = "";
async function loadGames(q) { async function loadGames() {
const url = `/api/games?q=${encodeURIComponent(q)}&limit=40`; const d = await getJSON(`/api/games?q=${encodeURIComponent(gamesQ)}&status=${gamesStatus}&page=${gamesPage}&size=12`);
const games = await getJSON(url); const games = d.results || [];
const list = games.filter((g) => !gameStatusFilter || g.status === gameStatusFilter); $("#list-games").innerHTML = games.map((g) => {
$("#list-games").innerHTML = list.map((g) => {
const finished = g.status === "finished"; const finished = g.status === "finished";
const score = finished ? `<span class="score-big">${g.away_score}</span> : <span class="score-big">${g.home_score}</span>` : "VS"; const score = finished ? `<span class="score-big">${g.away_score}</span> : <span class="score-big">${g.home_score}</span>` : "VS";
return `<div class="list-item" onclick="openGame(${g.id})"> return `<div class="list-item" onclick="openGame(${g.id})">
@@ -460,14 +464,16 @@ async function loadGames(q) {
<div class="meta">🕐 ${esc(g.game_time)} · ${esc(g.venue)} · ${esc(g.broadcast)}</div> <div class="meta">🕐 ${esc(g.game_time)} · ${esc(g.venue)} · ${esc(g.broadcast)}</div>
</div>`; </div>`;
}).join("") || '<div class="meta">没有符合条件的比赛</div>'; }).join("") || '<div class="meta">没有符合条件的比赛</div>';
renderPager(d, "games");
} }
$$("#view-games .btn.small").forEach((b) => b.addEventListener("click", () => { $$("#view-games .btn.small").forEach((b) => b.addEventListener("click", () => {
$$("#view-games .btn.small").forEach((x) => x.classList.remove("active")); $$("#view-games .btn.small").forEach((x) => x.classList.remove("active"));
b.classList.add("active"); b.classList.add("active");
gameStatusFilter = b.dataset.status; gamesStatus = b.dataset.status;
loadGames($("#search-games").value.trim()); gamesPage = 1;
loadGames();
})); }));
$("#btn-games").addEventListener("click", () => loadGames($("#search-games").value.trim())); $("#btn-games").addEventListener("click", () => { gamesQ = $("#search-games").value.trim(); gamesPage = 1; loadGames(); });
$("#search-games").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-games").click(); }); $("#search-games").addEventListener("keydown", (e) => { if (e.key === "Enter") $("#btn-games").click(); });
async function openGame(id) { async function openGame(id) {
const g = await getJSON(`/api/games/${id}`); const g = await getJSON(`/api/games/${id}`);
+2 -1
View File
@@ -78,7 +78,8 @@
<input class="search" id="search-games" placeholder="搜索:如 湖人 最近"> <input class="search" id="search-games" placeholder="搜索:如 湖人 最近">
<button class="btn" id="btn-games">搜索</button> <button class="btn" id="btn-games">搜索</button>
</div> </div>
<div class="list" id="list-games"></div> <div class="grid games-grid" id="list-games"></div>
<div class="pager" id="pager-games"></div>
</section> </section>
<section id="view-news" class="view"> <section id="view-news" class="view">
<div class="toolbar"><input class="search" id="search-news" placeholder="搜索新闻关键词"><button class="btn" id="btn-news">搜索</button></div> <div class="toolbar"><input class="search" id="search-news" placeholder="搜索新闻关键词"><button class="btn" id="btn-news">搜索</button></div>
+4
View File
@@ -132,6 +132,10 @@ main { flex: 1; width: 100%; max-width: 1200px; margin: 0 auto; padding: 20px 16
.pager button.active { background: linear-gradient(135deg, #f97316, #ea580c); color: #fff; border-color: transparent; } .pager button.active { background: linear-gradient(135deg, #f97316, #ea580c); color: #fff; border-color: transparent; }
.pager button:disabled { opacity: .35; cursor: default; } .pager button:disabled { opacity: .35; cursor: default; }
.pager span { margin: 0 4px; } .pager span { margin: 0 4px; }
/* ---------- 比赛网格卡片(固定宽度,宽度足够一行多个) ---------- */
.games-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); align-items: stretch; }
.games-grid .list-item { display: flex; flex-direction: column; justify-content: space-between; gap: 8px; height: 100%; padding: 14px 16px; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; cursor: pointer; transition: .18s; } .card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; cursor: pointer; transition: .18s; }
.card:hover { transform: translateY(-3px); border-color: var(--orange2); box-shadow: 0 6px 20px rgba(0,0,0,.35); } .card:hover { transform: translateY(-3px); border-color: var(--orange2); box-shadow: 0 6px 20px rgba(0,0,0,.35); }
.card h3 { font-size: 15.5px; margin-bottom: 4px; } .card h3 { font-size: 15.5px; margin-bottom: 4px; }
+12
View File
@@ -279,6 +279,18 @@ def get_game_detail(game_id):
return detail return detail
# ================================================================== 比赛列表(前台分页版)
def list_games(q="", status="", page=1, size=12):
"""前台比赛列表:关键词 + 状态筛选 + 分页(数据量小,全量匹配后切片)"""
r = search_games(q, limit=500)
results = r.get("results", [])
if status:
results = [g for g in results if g["status"] == status]
total = len(results)
start = (page - 1) * size
return {"results": results[start:start + size], "total": total, "page": page, "size": size}
# ================================================================== 排名 # ================================================================== 排名
def search_standings(query_text, limit=20, season=None): def search_standings(query_text, limit=20, season=None):
q = (query_text or "").strip() q = (query_text or "").strip()