v1.2.0 独立详情页+赛季选择+季后赛对阵图:/team|player|game|news|person/<id> 详情页(交叉链接);弹窗加查看完整详情;排名页多赛季切换(2024-25/2025-26);季后赛bracket对阵图(playoffs.py);seed_seasons补充数据
This commit is contained in:
+72
-3
@@ -54,7 +54,10 @@ function loadView(v) {
|
||||
else if (v === "games") loadGames("");
|
||||
else if (v === "news") loadNews("");
|
||||
else if (v === "persons") loadPersons("");
|
||||
else if (v === "standings") loadStandings("");
|
||||
else if (v === "standings") {
|
||||
if ($("#season-select").options.length) { loadStandings(); loadPlayoffs(); }
|
||||
else loadSeasons();
|
||||
}
|
||||
}
|
||||
|
||||
/* ================= 对话 ================= */
|
||||
@@ -197,6 +200,7 @@ async function openTeam(id) {
|
||||
<div class="section-title">🧑🏫 主教练</div><div>${esc(t.head_coach)}</div>
|
||||
<div class="section-title">⭐ 主要球员(本赛季场均)</div>${roster || "暂无"}
|
||||
<div class="section-title">📅 近期比赛</div>${games || "暂无"}
|
||||
<div style="margin-top:16px;text-align:right">${DL("team", t.id)}</div>
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -229,6 +233,7 @@ async function openPlayer(id) {
|
||||
</div>
|
||||
<div class="section-title">🏅 荣誉</div><div style="font-size:13.5px;color:#c9d4e0">${esc(p.awards || "暂无")}</div>
|
||||
<div class="section-title">📝 简介</div><div style="font-size:13.5px;color:#c9d4e0">${esc(p.bio || "暂无")}</div>
|
||||
<div style="margin-top:16px;text-align:right">${DL("player", p.id)}</div>
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -280,6 +285,7 @@ async function openGame(id) {
|
||||
<div style="text-align:center;color:var(--sub);font-size:13px">🕐 ${esc(g.game_time)} · ${esc(g.venue)} · ${esc(g.broadcast)}</div>
|
||||
${stats ? `<div class="section-title">📊 球员技术统计</div>
|
||||
<table><tr><th>球队</th><th>球员</th><th>得分</th><th>篮板</th><th>助攻</th><th>抢断</th><th>盖帽</th><th>分钟</th></tr>${stats}</table>` : ""}
|
||||
<div style="margin-top:16px;text-align:right">${DL("game", g.id)}</div>
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -304,6 +310,7 @@ async function openNews(id) {
|
||||
<h2>${esc(n.title)}</h2>
|
||||
<div class="en">🕐 ${esc(n.publish_time)} · ${esc(n.source)} · ${esc(n.author || "")} · ${n.kind === "wiki" ? "百科词条" : "新闻"}</div>
|
||||
<div class="news-body" style="margin-top:12px">${esc(n.content).replace(/\n/g, "<br>")}</div>
|
||||
<div style="margin-top:16px;text-align:right">${DL("news", n.id)}</div>
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -334,19 +341,40 @@ async function openPerson(id) {
|
||||
<div class="en">${esc(p.role_cn)} · ${esc(p.title || "")}${p.team ? " · " + esc(p.team) : ""}</div>
|
||||
<div class="section-title">📝 简介</div><div style="font-size:14px;line-height:1.8;color:#c9d4e0">${esc(p.bio || "暂无")}</div>
|
||||
<div class="section-title">🏅 成就</div><div style="font-size:14px;line-height:1.8;color:#c9d4e0">${esc(p.achievements || "暂无")}</div>
|
||||
<div style="margin-top:16px;text-align:right">${DL("person", p.id)}</div>
|
||||
`);
|
||||
}
|
||||
|
||||
/* ================= 排名 ================= */
|
||||
let standingsConf = "";
|
||||
let standingsSeason = "";
|
||||
let bracketData = null;
|
||||
|
||||
async function loadSeasons() {
|
||||
try {
|
||||
const ss = await getJSON("/api/seasons");
|
||||
if (ss.length) {
|
||||
standingsSeason = ss[0]; // 默认最新赛季
|
||||
$("#season-select").innerHTML = ss.map((s) => `<option value="${esc(s)}">${esc(s)} 赛季</option>`).join("");
|
||||
loadStandings();
|
||||
loadPlayoffs();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
$("#season-select").addEventListener("change", (e) => {
|
||||
standingsSeason = e.target.value;
|
||||
loadStandings();
|
||||
loadPlayoffs();
|
||||
});
|
||||
|
||||
async function loadStandings() {
|
||||
const rows = await getJSON(`/api/standings?conf=${encodeURIComponent(standingsConf)}`);
|
||||
const rows = await getJSON(`/api/standings?conf=${encodeURIComponent(standingsConf)}&season=${encodeURIComponent(standingsSeason || "")}`);
|
||||
const groups = {};
|
||||
rows.forEach((r) => { (groups[r.conference] = groups[r.conference] || []).push(r); });
|
||||
$("#wrap-standings").innerHTML = Object.entries(groups).map(([conf, list]) => `
|
||||
<h3 style="margin:14px 0 8px;color:var(--orange2)">${conf}赛区</h3>
|
||||
<table><tr><th>排名</th><th>球队</th><th>胜</th><th>负</th><th>胜率</th><th>战绩</th></tr>
|
||||
${list.map((r) => `<tr><td class="num">${r.rank}</td><td>${esc(r.team)}</td>
|
||||
${list.map((r) => `<tr><td class="num">${r.rank}</td><td><a class="xlink" href="/team/${r.team_id}">${esc(r.team)}</a></td>
|
||||
<td class="num">${r.wins}</td><td class="num">${r.losses}</td>
|
||||
<td class="num">${r.win_pct}%</td><td class="num">${r.wins}-${r.losses}</td></tr>`).join("")}
|
||||
</table>`).join("");
|
||||
@@ -358,11 +386,52 @@ $$("#view-standings .btn.small").forEach((b) => b.addEventListener("click", () =
|
||||
loadStandings();
|
||||
}));
|
||||
|
||||
/* ================= 季后赛对阵图 ================= */
|
||||
async function loadPlayoffs() {
|
||||
try {
|
||||
bracketData = await getJSON(`/api/playoffs?season=${encodeURIComponent(standingsSeason || "")}`);
|
||||
renderBracket();
|
||||
} catch (e) {
|
||||
$("#wrap-bracket").innerHTML = `<div class="meta">对阵图加载失败:${esc(e.message)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function matchHtml(m) {
|
||||
if (!m) return "";
|
||||
const gid = (m.games || [])[0];
|
||||
const row = (side) => `<div class="mteam ${side.advance ? "win" : ""}" ${gid ? `onclick="openGame(${gid})"` : ""}>
|
||||
<span class="mteam-name">${side.advance ? "🏆 " : ""}${esc(side.team)}</span><span class="mteam-wins">${side.wins}</span></div>`;
|
||||
return `<div class="match">${row(m.home)}${row(m.away)}</div>`;
|
||||
}
|
||||
|
||||
function renderBracket() {
|
||||
const r = (bracketData?.rounds) || {};
|
||||
const first = [...(r.first?.["东部"] || []), ...(r.first?.["西部"] || [])];
|
||||
const semi = [...(r.semi?.["东部"] || []), ...(r.semi?.["西部"] || [])];
|
||||
const conf = [r.conf?.["东部"], r.conf?.["西部"]].filter(Boolean);
|
||||
const final = r.final;
|
||||
const col = (title, items) => `<div class="bracket-col"><div class="bracket-col-title">${title}</div>${items.map(matchHtml).join("")}</div>`;
|
||||
$("#wrap-bracket").innerHTML = `
|
||||
<div style="display:flex;align-items:center;gap:8px;margin:14px 0 8px">
|
||||
<h3 style="color:var(--orange2)">🏆 季后赛对阵图</h3>
|
||||
<span class="season-pill">${esc(bracketData?.season || standingsSeason)} 赛季</span>
|
||||
</div>
|
||||
<div class="bracket">
|
||||
${col("首轮", first)}
|
||||
${col("半决赛", semi)}
|
||||
${col("分区决赛", conf)}
|
||||
${col("总决赛", final ? [final] : [])}
|
||||
</div>
|
||||
<div class="meta" style="margin-top:8px">💡 点击对阵可查看比赛详情 · 胜者显示 🏆</div>`;
|
||||
}
|
||||
|
||||
/* ================= 弹窗 ================= */
|
||||
function openModal(html) {
|
||||
$("#modal-body").innerHTML = html;
|
||||
$("#modal").classList.remove("hidden");
|
||||
}
|
||||
/* 详情页链接(弹窗底部) */
|
||||
const DL = (type, id) => `<a class="detail-link" href="/${type}/${id}" target="_blank">查看完整详情 →</a>`;
|
||||
$("#modal-close").addEventListener("click", () => $("#modal").classList.add("hidden"));
|
||||
$("#modal").addEventListener("click", (e) => { if (e.target.id === "modal") $("#modal").classList.add("hidden"); });
|
||||
document.addEventListener("keydown", (e) => { if (e.key === "Escape") $("#modal").classList.add("hidden"); });
|
||||
@@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>详情 - NBA球迷大全</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
<style>
|
||||
.detail-bar { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; flex-wrap: wrap; }
|
||||
.back-btn { background: var(--bg2); border: 1px solid var(--line); color: var(--txt); border-radius: 999px; padding: 8px 16px; cursor: pointer; font-size: 13.5px; }
|
||||
.back-btn:hover { border-color: var(--orange2); color: var(--orange2); }
|
||||
.detail-title { font-size: 24px; font-weight: 800; }
|
||||
.detail-title .en { color: var(--sub); font-size: 14px; font-weight: 400; margin-left: 8px; }
|
||||
.detail-sub { color: var(--sub); font-size: 13.5px; margin-top: 4px; }
|
||||
.hero { background: linear-gradient(135deg, #1a120b, #1c2333); border: 1px solid var(--line); border-radius: 16px; padding: 24px 26px; margin-bottom: 18px; }
|
||||
.hero .big { font-size: 26px; font-weight: 800; background: linear-gradient(90deg, #fb923c, #f97316, #fbbf24); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
||||
.kv { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px 14px; margin: 16px 0; }
|
||||
.kv div { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; }
|
||||
.kv .k { color: var(--sub); font-size: 11.5px; display: block; }
|
||||
.kv .v { font-size: 14.5px; font-weight: 600; }
|
||||
.section { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 18px 20px; margin-bottom: 16px; }
|
||||
.section h3 { font-size: 15.5px; color: var(--orange2); margin-bottom: 12px; }
|
||||
.section .txt { font-size: 14px; line-height: 1.9; color: #c9d4e0; }
|
||||
.xlink { color: var(--blue); text-decoration: none; }
|
||||
.xlink:hover { text-decoration: underline; color: var(--orange2); }
|
||||
.row-item { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 8px 2px; border-bottom: 1px dashed var(--line); font-size: 13.5px; flex-wrap: wrap; }
|
||||
.row-item:last-child { border-bottom: none; }
|
||||
.num-badge { color: var(--orange2); font-weight: 700; }
|
||||
.win { color: var(--green); font-weight: 700; }
|
||||
.lose { color: var(--sub); }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th, td { padding: 9px 11px; text-align: left; font-size: 13px; border-bottom: 1px solid var(--line); }
|
||||
th { background: var(--bg2); color: var(--sub); font-size: 12px; }
|
||||
td.num { text-align: center; }
|
||||
.score-head { display: flex; align-items: center; justify-content: center; gap: 16px; font-size: 22px; margin: 10px 0 6px; flex-wrap: wrap; }
|
||||
.score-head .s { font-weight: 800; font-size: 30px; }
|
||||
.news-body { line-height: 2; font-size: 14.5px; color: #c9d4e0; white-space: pre-wrap; }
|
||||
.season-pill { display: inline-block; background: rgba(56,189,248,.1); color: var(--blue); border-radius: 999px; padding: 2px 10px; font-size: 12px; margin: 2px 4px 2px 0; }
|
||||
.loading { text-align: center; color: var(--sub); padding: 80px 0; font-size: 15px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header class="topbar">
|
||||
<div class="logo">
|
||||
<span class="logo-ball">🏀</span>
|
||||
<div>
|
||||
<h1 id="site-name">NBA球迷大全</h1>
|
||||
<p class="sub">详情页</p>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="tabs">
|
||||
<a class="tab" href="/">💬 对话</a>
|
||||
<a class="tab" href="/#/teams">🏟️ 球队</a>
|
||||
<a class="tab" href="/#/players">⭐ 球员</a>
|
||||
<a class="tab" href="/#/games">📅 比赛</a>
|
||||
<a class="tab" href="/#/news">📰 新闻</a>
|
||||
<a class="tab" href="/#/persons">👥 人物</a>
|
||||
<a class="tab" href="/#/standings">🏆 排名</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="detail-bar">
|
||||
<button class="back-btn" onclick="history.length > 1 ? history.back() : (location.href='/')">← 返回</button>
|
||||
<div id="crumb"></div>
|
||||
</div>
|
||||
<div id="detail-body"><div class="loading">⏳ 加载中…</div></div>
|
||||
</main>
|
||||
|
||||
<footer class="footer" id="footer-text">NBA球迷大全 · 数据为模拟演示数据</footer>
|
||||
|
||||
<script>
|
||||
fetch("/api/boot").then(r => r.json()).then(b => {
|
||||
if (b.site_name) { document.title = b.site_name + " · 详情"; document.getElementById("site-name").textContent = b.site_name; }
|
||||
if (b.footer_text) document.getElementById("footer-text").textContent = b.footer_text;
|
||||
}).catch(() => {});
|
||||
</script>
|
||||
<script src="/static/detail.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,136 @@
|
||||
/* NBA球迷大全 独立详情页渲染(/team/1 /player/1 /game/1 /news/1 /person/1) */
|
||||
const $ = (s) => document.querySelector(s);
|
||||
const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
||||
const STATUS_CN = { scheduled: "未开始", live: "进行中", finished: "已结束" };
|
||||
|
||||
async function getJSON(url) { const r = await fetch(url); if (!r.ok) throw new Error("记录不存在或已删除"); return r.json(); }
|
||||
|
||||
/* 交叉链接 */
|
||||
const T = (id, name) => `<a class="xlink" href="/team/${id}">${esc(name)}</a>`;
|
||||
const P = (id, name) => `<a class="xlink" href="/player/${id}">${esc(name)}</a>`;
|
||||
const G = (id, text) => `<a class="xlink" href="/game/${id}">${esc(text)}</a>`;
|
||||
const N = (id, title) => `<a class="xlink" href="/news/${id}">${esc(title)}</a>`;
|
||||
const PER = (id, name) => `<a class="xlink" href="/person/${id}">${esc(name)}</a>`;
|
||||
|
||||
/* ================= 球队 ================= */
|
||||
function renderTeam(d) {
|
||||
const t = d.team;
|
||||
const standings = (d.standings || []).map((s) =>
|
||||
`<span class="season-pill">${esc(s.season)} ${esc(s.conference)}第${s.rank}名 · ${s.wins}-${s.losses}</span>`).join("");
|
||||
const roster = (d.roster || []).map((p) => `
|
||||
<div class="row-item">
|
||||
<span>${P(p.id, p.name)} <span class="en">#${p.number} · ${esc(p.position)}</span></span>
|
||||
<span>本季场均 <span class="num-badge">${p.season.pts}</span> 分 / ${p.season.reb} 板 / ${p.season.ast} 助</span>
|
||||
</div>`).join("") || '<div class="txt">暂无球员数据</div>';
|
||||
const games = (d.recent_games || []).map((g) => {
|
||||
const score = g.status === "finished" ? `<span class="num-badge">${g.away_score} : ${g.home_score}</span>` : "VS";
|
||||
return `<div class="row-item">
|
||||
<span>${G(g.id, `${g.away_team} ${score} ${g.home_team}`)}</span>
|
||||
<span class="en">${esc(g.round_name)} · ${esc(g.game_time.slice(0, 16))} · ${STATUS_CN[g.status]}</span>
|
||||
</div>`;
|
||||
}).join("") || '<div class="txt">暂无比赛</div>';
|
||||
$("#crumb").innerHTML = `<span class="en">🏟️ 球队 / ${esc(t.name)}</span>`;
|
||||
$("#detail-body").innerHTML = `
|
||||
<div class="hero">
|
||||
<div class="big">${esc(t.name)}</div>
|
||||
<div class="detail-sub">${esc(t.name_en)} · ${esc(t.city)} · ${esc(t.arena)} · 建队 ${t.founded} 年 · 总冠军 ×${t.champion_count}</div>
|
||||
</div>
|
||||
<div class="section"><h3>📈 各赛季战绩</h3>${standings || '<span class="txt">暂无排名数据</span>'}</div>
|
||||
<div class="section"><h3>📝 球队简介</h3><div class="txt">${esc(t.intro || "暂无简介")}</div></div>
|
||||
<div class="section"><h3>🧑🏫 主教练</h3><div class="txt">${esc(t.head_coach || "未知")}</div></div>
|
||||
<div class="section"><h3>⭐ 主要球员(本赛季场均)</h3>${roster}</div>
|
||||
<div class="section"><h3>📅 近期比赛</h3>${games}</div>`;
|
||||
}
|
||||
|
||||
/* ================= 球员 ================= */
|
||||
function renderPlayer(p) {
|
||||
const logs = (p.game_logs || []).map((g) => {
|
||||
const score = g.home_score !== null ? `${g.away_score} : ${g.home_score}` : "VS";
|
||||
return `<div class="row-item">
|
||||
<span>${G(g.game_id, `${g.away_team} ${score} ${g.home_team}`)} <span class="en">${esc(g.round_name)} · ${esc(g.game_time.slice(0, 10))}</span></span>
|
||||
<span><span class="num-badge">${g.pts}</span>分 ${g.reb}板 ${g.ast}助 ${g.stl}断 ${g.blk}帽 ${g.min}分钟</span>
|
||||
</div>`;
|
||||
}).join("") || '<div class="txt">暂无比赛记录</div>';
|
||||
$("#crumb").innerHTML = `<span class="en">⭐ 球员 / ${esc(p.name)}</span>`;
|
||||
$("#detail-body").innerHTML = `
|
||||
<div class="hero">
|
||||
<div class="big">${esc(p.name)}</div>
|
||||
<div class="detail-sub">${esc(p.name_en || "")} · ${p.team ? T(p.team_id ?? 0, p.team) : "自由球员"} · ${esc(p.position)} · #${p.number} · ${esc(p.country)}</div>
|
||||
</div>
|
||||
<div class="section"><h3>📋 基本信息</h3>
|
||||
<div class="kv">
|
||||
<div><span class="k">身高</span><span class="v">${p.height_cm} cm</span></div>
|
||||
<div><span class="k">体重</span><span class="v">${p.weight_kg} kg</span></div>
|
||||
<div><span class="k">选秀</span><span class="v">${esc(p.draft)}</span></div>
|
||||
<div><span class="k">年薪</span><span class="v">$${(p.salary_m / 100).toFixed(2)} 亿</span></div>
|
||||
<div><span class="k">本季场均</span><span class="v">${p.season.pts}分 ${p.season.reb}板 ${p.season.ast}助</span></div>
|
||||
<div><span class="k">本季防守</span><span class="v">${p.season.stl}断 ${p.season.blk}帽</span></div>
|
||||
<div><span class="k">生涯场均</span><span class="v">${p.career.pts}分 ${p.career.reb}板 ${p.career.ast}助</span></div>
|
||||
<div><span class="k">生涯场次</span><span class="v">${p.career.games} 场</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section"><h3>🏅 主要荣誉</h3><div class="txt">${esc(p.awards || "暂无")}</div></div>
|
||||
<div class="section"><h3>📝 球员简介</h3><div class="txt">${esc(p.bio || "暂无")}</div></div>
|
||||
<div class="section"><h3>🏀 近期比赛记录</h3>${logs}</div>`;
|
||||
}
|
||||
|
||||
/* ================= 比赛 ================= */
|
||||
function renderGame(g) {
|
||||
const finished = g.status === "finished";
|
||||
const head = finished
|
||||
? `<div class="score-head">${T(g.home_team_id, g.home_team)} <span class="s">${g.home_score}</span> : <span class="s">${g.away_score}</span> ${T(g.away_team_id, g.away_team)}</div>`
|
||||
: `<div class="score-head">${T(g.home_team_id, g.home_team)} VS ${T(g.away_team_id, g.away_team)}</div>`;
|
||||
const stats = (g.box_score || []).map((s) => `
|
||||
<tr><td>${esc(s.team_name)}</td><td>${P(s.player_id, s.player_name)}</td>
|
||||
<td class="num">${s.points}</td><td class="num">${s.rebounds}</td><td class="num">${s.assists}</td>
|
||||
<td class="num">${s.steals}</td><td class="num">${s.blocks}</td><td class="num">${s.minutes}</td></tr>`).join("");
|
||||
$("#crumb").innerHTML = `<span class="en">📅 比赛 / ${esc(g.round_name)}</span>`;
|
||||
$("#detail-body").innerHTML = `
|
||||
<div class="hero">
|
||||
<div style="text-align:center;color:var(--sub);font-size:13px">${esc(g.round_name)} · ${STATUS_CN[g.status]}</div>
|
||||
${head}
|
||||
<div style="text-align:center;color:var(--sub);font-size:13px;margin-top:6px">🕐 ${esc(g.game_time)} · ${esc(g.venue)} · ${esc(g.broadcast)}</div>
|
||||
</div>
|
||||
${stats ? `<div class="section"><h3>📊 球员技术统计</h3>
|
||||
<table><tr><th>球队</th><th>球员</th><th>得分</th><th>篮板</th><th>助攻</th><th>抢断</th><th>盖帽</th><th>分钟</th></tr>${stats}</table>
|
||||
</div>` : '<div class="section"><h3>📊 球员技术统计</h3><div class="txt">暂无统计</div></div>'}`;
|
||||
}
|
||||
|
||||
/* ================= 新闻/百科 ================= */
|
||||
function renderNews(n) {
|
||||
$("#crumb").innerHTML = `<span class="en">📰 ${n.kind === "wiki" ? "百科" : "新闻"} / ${esc(n.title)}</span>`;
|
||||
$("#detail-body").innerHTML = `
|
||||
<div class="hero">
|
||||
<div class="big">${esc(n.title)}</div>
|
||||
<div class="detail-sub">🕐 ${esc(n.publish_time || "")} · ${esc(n.source || "")}${n.author ? " · " + esc(n.author) : ""} · ${n.kind === "wiki" ? "百科词条" : "新闻"}${n.tags ? " · " + esc(n.tags) : ""}</div>
|
||||
</div>
|
||||
<div class="section"><h3>📄 正文</h3><div class="news-body">${esc(n.content || "")}</div></div>`;
|
||||
}
|
||||
|
||||
/* ================= 人物 ================= */
|
||||
function renderPerson(p) {
|
||||
$("#crumb").innerHTML = `<span class="en">👥 人物 / ${esc(p.name)}</span>`;
|
||||
$("#detail-body").innerHTML = `
|
||||
<div class="hero">
|
||||
<div class="big">${esc(p.name)}</div>
|
||||
<div class="detail-sub">${esc(p.role_cn || p.role || "")} · ${esc(p.title || "")}${p.team_id ? " · " + T(p.team_id, p.team_name || "相关球队") : ""}</div>
|
||||
</div>
|
||||
<div class="section"><h3>📝 人物简介</h3><div class="txt">${esc(p.bio || "暂无")}</div></div>
|
||||
<div class="section"><h3>🏅 主要成就</h3><div class="txt">${esc(p.achievements || "暂无")}</div></div>`;
|
||||
}
|
||||
|
||||
/* ================= 路由 ================= */
|
||||
const ROUTERS = { team: renderTeam, player: renderPlayer, game: renderGame, news: renderNews, person: renderPerson };
|
||||
const APIS = { team: "/api/teams/", player: "/api/players/", game: "/api/games/", news: "/api/news/", person: "/api/persons/" };
|
||||
|
||||
(async function init() {
|
||||
const m = location.pathname.match(/^\/(team|player|game|news|person)\/(\d+)\/?$/);
|
||||
if (!m) { $("#detail-body").innerHTML = '<div class="loading">❌ 地址无效</div>'; return; }
|
||||
const [etype, eid] = [m[1], parseInt(m[2])];
|
||||
try {
|
||||
const d = await getJSON(APIS[etype] + eid);
|
||||
ROUTERS[etype](d);
|
||||
} catch (e) {
|
||||
$("#detail-body").innerHTML = `<div class="loading">❌ ${esc(e.message)}<br><br><a class="tab" href="/">← 返回首页</a></div>`;
|
||||
}
|
||||
})();
|
||||
@@ -84,11 +84,14 @@
|
||||
</section>
|
||||
<section id="view-standings" class="view">
|
||||
<div class="toolbar">
|
||||
<label class="season-label" for="season-select">📅 赛季</label>
|
||||
<select id="season-select" class="search" style="flex:0 0 auto;max-width:160px"></select>
|
||||
<button class="btn small active" data-conf="">全部</button>
|
||||
<button class="btn small" data-conf="西部">西部</button>
|
||||
<button class="btn small" data-conf="东部">东部</button>
|
||||
</div>
|
||||
<div class="table-wrap" id="wrap-standings"></div>
|
||||
<div id="wrap-bracket"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -140,6 +140,23 @@ th { background: var(--bg2); color: var(--sub); font-weight: 600; font-size: 12.
|
||||
tr:hover td { background: rgba(249,115,22,.05); }
|
||||
td.num { text-align: center; }
|
||||
|
||||
/* ---------- 赛季选择 / 详情页链接 / 对阵图 ---------- */
|
||||
.season-label { color: var(--sub); font-size: 13px; }
|
||||
.xlink { color: var(--blue); text-decoration: none; }
|
||||
.xlink:hover { color: var(--orange2); text-decoration: underline; }
|
||||
.detail-link { display: inline-block; color: var(--orange2); text-decoration: none; font-size: 13.5px; font-weight: 600; border: 1px solid #7c3a1e; background: rgba(249,115,22,.08); border-radius: 999px; padding: 6px 14px; transition: .15s; }
|
||||
.detail-link:hover { background: rgba(249,115,22,.18); }
|
||||
.bracket { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 6px; }
|
||||
.bracket-col { flex: 1; min-width: 190px; background: var(--bg2); border: 1px solid var(--line); border-radius: 12px; padding: 10px; }
|
||||
.bracket-col-title { color: var(--sub); font-size: 12px; text-align: center; margin-bottom: 8px; letter-spacing: 1px; }
|
||||
.match { background: var(--card); border: 1px solid var(--line); border-radius: 10px; margin-bottom: 10px; overflow: hidden; }
|
||||
.mteam { display: flex; justify-content: space-between; align-items: center; padding: 7px 10px; font-size: 12.5px; cursor: pointer; border-bottom: 1px dashed var(--line); }
|
||||
.mteam:last-child { border-bottom: none; }
|
||||
.mteam:hover { background: rgba(249,115,22,.08); }
|
||||
.mteam.win { color: var(--green); font-weight: 700; }
|
||||
.mteam-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.mteam-wins { color: var(--orange2); font-weight: 800; margin-left: 6px; }
|
||||
|
||||
/* ---------- 弹窗 ---------- */
|
||||
.modal { position: fixed; inset: 0; background: rgba(0,0,0,.7); display: flex; align-items: center; justify-content: center; z-index: 100; padding: 20px; }
|
||||
.modal.hidden { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user