v0.5.109: hero role toolbar, skill side-by-side layout, taller detail panel.
Move role filters between grid and detail, fix skill video/text layout and detail height jump, unify item dividers, drop OpenDota matchup cross, sync docs and assets. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+116
-107
@@ -56,6 +56,21 @@ function rankIconSrc(file) {
|
||||
return assetUrl(`rank/${name}`);
|
||||
}
|
||||
|
||||
const ROLE_ICON_FILES = {
|
||||
核心: "Carry.png",
|
||||
辅助: "Support.png",
|
||||
推进: "Pusher.png",
|
||||
逃生: "Escape.png",
|
||||
爆发: "Nuker.png",
|
||||
先手: "Initiator.png",
|
||||
控制: "Disabler.png",
|
||||
耐久: "Durable.png",
|
||||
};
|
||||
|
||||
function roleIconSrc(file) {
|
||||
return assetUrl(`role-icon/${encodeURIComponent(file)}`);
|
||||
}
|
||||
|
||||
function siteVersionLabel() {
|
||||
const v = typeof SITE_VERSION === "string" ? SITE_VERSION.trim() : "";
|
||||
return v ? `v${v}` : "";
|
||||
@@ -243,10 +258,37 @@ function renderTagbar() {
|
||||
if (!bar) return;
|
||||
const order = state.data.tag_order || [];
|
||||
bar.innerHTML = "";
|
||||
|
||||
const addLabel = (button, text, iconFile) => {
|
||||
if (iconFile) {
|
||||
const icon = document.createElement("img");
|
||||
icon.className = "tagbar-role-icon";
|
||||
icon.src = roleIconSrc(iconFile);
|
||||
icon.alt = "";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
button.appendChild(icon);
|
||||
} else {
|
||||
const icon = document.createElement("span");
|
||||
icon.className = "tagbar-all-icon";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
icon.innerHTML =
|
||||
'<svg viewBox="0 0 16 16" focusable="false"><path d="M1.5 1.5h5v5h-5zM9.5 1.5h5v5h-5zM1.5 9.5h5v5h-5zM9.5 9.5h5v5h-5z"/></svg>';
|
||||
button.appendChild(icon);
|
||||
}
|
||||
const label = document.createElement("span");
|
||||
label.textContent = text;
|
||||
button.appendChild(label);
|
||||
};
|
||||
|
||||
const selectedHero = state.selectedKey ? heroByKey(state.selectedKey) : null;
|
||||
const heroTags = new Set(selectedHero?.tags || []);
|
||||
|
||||
const allBtn = document.createElement("button");
|
||||
allBtn.type = "button";
|
||||
allBtn.textContent = "全部";
|
||||
allBtn.className = state.tagFilters.size ? "" : "active";
|
||||
addLabel(allBtn, "全部");
|
||||
// "全部" only when no filter and no hero selected; hero roles use `.matched`.
|
||||
allBtn.className =
|
||||
!state.tagFilters.size && !state.selectedKey ? "active" : "";
|
||||
allBtn.addEventListener("click", () => {
|
||||
state.tagFilters.clear();
|
||||
syncStateToUrl({ replace: true });
|
||||
@@ -254,10 +296,16 @@ function renderTagbar() {
|
||||
});
|
||||
bar.appendChild(allBtn);
|
||||
for (const tag of order) {
|
||||
const iconFile = ROLE_ICON_FILES[tag];
|
||||
if (!iconFile) continue;
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.textContent = tag;
|
||||
addLabel(btn, tag, iconFile);
|
||||
if (state.tagFilters.has(tag)) btn.classList.add("active");
|
||||
if (heroTags.has(tag)) {
|
||||
btn.classList.add("matched");
|
||||
btn.title = "该英雄定位";
|
||||
}
|
||||
btn.addEventListener("click", () => {
|
||||
if (state.tagFilters.has(tag)) state.tagFilters.delete(tag);
|
||||
else state.tagFilters.add(tag);
|
||||
@@ -1674,7 +1722,6 @@ function stratzMatchupPack() {
|
||||
kind: "global_aggregate",
|
||||
label_zh: "全局聚合(未按段位 / 分路 / 周过滤)",
|
||||
},
|
||||
cross_source: null,
|
||||
stats: {},
|
||||
}
|
||||
);
|
||||
@@ -1985,13 +2032,6 @@ function formatMatchupWr(wr) {
|
||||
return `${(Number(wr) * 100).toFixed(1)}%`;
|
||||
}
|
||||
|
||||
function matchupCrossLabel(cross) {
|
||||
const status = cross?.status;
|
||||
if (status === "agree") return "来源一致";
|
||||
if (status === "conflict") return "来源分歧";
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildMatchupColumn(title, entries, scoreKey, scoreLabel) {
|
||||
const col = document.createElement("div");
|
||||
col.className = "matchup-col";
|
||||
@@ -2040,40 +2080,20 @@ function buildMatchupColumn(title, entries, scoreKey, scoreLabel) {
|
||||
const meta = document.createElement("span");
|
||||
meta.className = "matchup-meta detail-muted";
|
||||
const wrText = formatMatchupWr(row.wr);
|
||||
const crossLab = matchupCrossLabel(row.cross);
|
||||
meta.textContent = [
|
||||
`胜率 ${wrText}`,
|
||||
`${formatPickCount(row.games)} 场`,
|
||||
crossLab,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" · ");
|
||||
meta.textContent = [`胜率 ${wrText}`, `${formatPickCount(row.games)} 场`].join(
|
||||
" · "
|
||||
);
|
||||
mid.appendChild(name);
|
||||
mid.appendChild(meta);
|
||||
|
||||
const score = document.createElement("span");
|
||||
score.className = "matchup-score";
|
||||
score.textContent = formatAdvantage(row[scoreKey]);
|
||||
const crossStatus = row.cross?.status;
|
||||
if (crossStatus === "agree") score.classList.add("is-agree");
|
||||
if (crossStatus === "conflict") score.classList.add("is-conflict");
|
||||
const odotaTip =
|
||||
row.cross && row.cross.opendota_games != null
|
||||
? ` · OpenDota 基线差 ${
|
||||
row.cross.opendota_adv == null
|
||||
? "—"
|
||||
: formatAdvantage(row.cross.opendota_adv * 100)
|
||||
}pp / ${formatPickCount(row.cross.opendota_games)} 场`
|
||||
: "";
|
||||
score.title = [
|
||||
`STRATZ 相对优势 ${formatAdvantage(row[scoreKey])}(非胜率百分点)`,
|
||||
`对局胜率 ${wrText}`,
|
||||
`${formatPickCount(row.games)} 场`,
|
||||
crossLab ? `交叉:${crossLab}` : "",
|
||||
odotaTip,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" · ");
|
||||
].join(" · ");
|
||||
|
||||
item.appendChild(img);
|
||||
item.appendChild(mid);
|
||||
@@ -2115,20 +2135,9 @@ function buildMatchupsPanel(heroKey) {
|
||||
const notes = document.createElement("div");
|
||||
notes.className = "matchup-notes detail-muted";
|
||||
|
||||
const hintBits = [
|
||||
"数字是相对表现(综合胜率与自身强弱),不是直接胜率;也不分段位、分路,和「走势」页不是同一套数据。",
|
||||
];
|
||||
const cross = pack.cross_source;
|
||||
if (cross && cross.available === false) {
|
||||
hintBits.push("暂无其他数据源可对照,交叉结论仅供参考。");
|
||||
} else if (cross && cross.available) {
|
||||
hintBits.push(
|
||||
"「来源一致 / 分歧」表示是否与 OpenDota 公开统计方向相同,仅作对照。"
|
||||
);
|
||||
}
|
||||
const hint = document.createElement("p");
|
||||
hint.className = "matchup-note-line";
|
||||
hint.textContent = hintBits.join(" ");
|
||||
hint.textContent = "数据来自 STRATZ(相对优势,全局聚合)。";
|
||||
notes.appendChild(hint);
|
||||
|
||||
const stale = cell.stale === true;
|
||||
@@ -2369,7 +2378,7 @@ function mountItemInspect(root, meta, { onPickItem, hideIcon = false } = {}) {
|
||||
|
||||
const nameRow = document.createElement("div");
|
||||
nameRow.className = "item-detail-name-row";
|
||||
const title = document.createElement("div");
|
||||
const title = document.createElement("h3");
|
||||
title.className = "item-detail-name";
|
||||
title.textContent = meta.name_loc || meta.key;
|
||||
nameRow.appendChild(title);
|
||||
@@ -2590,28 +2599,6 @@ function appendVitalBar(parent, kind, label, value, regen, scale) {
|
||||
parent.appendChild(bar);
|
||||
}
|
||||
|
||||
/** Hero role tags (skills tab center column, above skill icons). */
|
||||
function buildHeroTagsRow(hero) {
|
||||
const tags = document.createElement("div");
|
||||
tags.className = "detail-tags";
|
||||
tags.setAttribute("aria-label", "英雄定位");
|
||||
const tagList = hero.tags || [];
|
||||
if (!tagList.length) {
|
||||
const none = document.createElement("span");
|
||||
none.className = "detail-muted";
|
||||
none.textContent = "暂无定位";
|
||||
tags.appendChild(none);
|
||||
return tags;
|
||||
}
|
||||
for (const t of tagList) {
|
||||
const chip = document.createElement("span");
|
||||
chip.className = "tag-chip";
|
||||
chip.textContent = t;
|
||||
tags.appendChild(chip);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
/** Left column: HP/Mana + STR/AGI/INT (skills tab only). */
|
||||
function buildHeroVitalsAttrs(hero) {
|
||||
if (hero.base_str == null && hero.health == null) return null;
|
||||
@@ -2672,38 +2659,67 @@ function buildHeroVitalsAttrs(hero) {
|
||||
return strip.childNodes.length ? strip : null;
|
||||
}
|
||||
|
||||
/** Official dota2.com.cn herostatic/stats icons (served from /ui-icon/). */
|
||||
function combatStatIconSrc(name) {
|
||||
return assetUrl(`ui-icon/${name}`);
|
||||
}
|
||||
|
||||
/** Right rail: Attack / Defense / Mobility as 3 side-by-side columns (skills tab). */
|
||||
function buildHeroCombatStats(hero) {
|
||||
// Rows: [iconFile, labelZh, value] — icons match dota2.com.cn hero pages.
|
||||
const combatGroups = [
|
||||
{
|
||||
title: "攻击",
|
||||
rows: [
|
||||
hero.damage_min != null && hero.damage_max != null
|
||||
? ["伤害", `${hero.damage_min}–${hero.damage_max}`]
|
||||
? ["icon_damage.png", "伤害", `${hero.damage_min}–${hero.damage_max}`]
|
||||
: null,
|
||||
hero.attack_rate != null
|
||||
? ["icon_attack_time.png", "间隔", fmtNum(hero.attack_rate, 1)]
|
||||
: null,
|
||||
hero.attack_range != null
|
||||
? ["icon_attack_range.png", "距离", String(hero.attack_range)]
|
||||
: null,
|
||||
hero.attack_rate != null ? ["间隔", fmtNum(hero.attack_rate, 1)] : null,
|
||||
hero.attack_range != null ? ["距离", String(hero.attack_range)] : null,
|
||||
// Melee heroes often have projectile_speed 0 in OpenDota — skip those.
|
||||
hero.projectile_speed != null && Number(hero.projectile_speed) > 0
|
||||
? ["弹道", String(hero.projectile_speed)]
|
||||
? [
|
||||
"icon_projectile_speed.png",
|
||||
"弹道",
|
||||
String(hero.projectile_speed),
|
||||
]
|
||||
: null,
|
||||
].filter(Boolean),
|
||||
},
|
||||
{
|
||||
title: "防御",
|
||||
rows: [
|
||||
hero.armor != null ? ["护甲", fmtNum(hero.armor, 1)] : null,
|
||||
hero.magic_resist != null ? ["魔抗", `${hero.magic_resist}%`] : null,
|
||||
hero.armor != null
|
||||
? ["icon_armor.png", "护甲", fmtNum(hero.armor, 1)]
|
||||
: null,
|
||||
hero.magic_resist != null
|
||||
? ["icon_magic_resist.png", "魔抗", `${hero.magic_resist}%`]
|
||||
: null,
|
||||
].filter(Boolean),
|
||||
},
|
||||
{
|
||||
title: "机动性",
|
||||
rows: [
|
||||
hero.move_speed != null ? ["移速", String(hero.move_speed)] : null,
|
||||
hero.turn_rate != null ? ["转身", fmtNum(hero.turn_rate, 1)] : null,
|
||||
hero.vision_day != null && hero.vision_night != null
|
||||
? ["视野", `白天:${hero.vision_day}\n夜晚:${hero.vision_night}`]
|
||||
hero.move_speed != null
|
||||
? ["icon_movement_speed.png", "移速", String(hero.move_speed)]
|
||||
: null,
|
||||
// OpenDota omits the engine default 0.6; show it like dota2.com.cn.
|
||||
[
|
||||
"icon_turn_rate.png",
|
||||
"转身",
|
||||
fmtNum(hero.turn_rate != null ? hero.turn_rate : 0.6, 1),
|
||||
],
|
||||
[
|
||||
"icon_vision.png",
|
||||
"视野",
|
||||
`${hero.vision_day != null ? hero.vision_day : 1800} / ${
|
||||
hero.vision_night != null ? hero.vision_night : 800
|
||||
}`,
|
||||
],
|
||||
].filter(Boolean),
|
||||
},
|
||||
].filter((g) => g.rows.length);
|
||||
@@ -2730,30 +2746,21 @@ function buildHeroCombatStats(hero) {
|
||||
section.appendChild(title);
|
||||
const list = document.createElement("dl");
|
||||
list.className = "hero-stats-combat-list";
|
||||
for (const [label, val] of g.rows) {
|
||||
for (const [icon, label, val] of g.rows) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "hero-stat-row";
|
||||
const dt = document.createElement("dt");
|
||||
dt.textContent = label;
|
||||
const img = document.createElement("img");
|
||||
img.className = "hero-stat-icon";
|
||||
img.src = combatStatIconSrc(icon);
|
||||
img.alt = label;
|
||||
img.title = label;
|
||||
img.width = 20;
|
||||
img.height = 20;
|
||||
img.decoding = "async";
|
||||
dt.appendChild(img);
|
||||
const dd = document.createElement("dd");
|
||||
if (typeof val === "string" && val.includes("\n")) {
|
||||
for (const line of val.split("\n")) {
|
||||
const div = document.createElement("div");
|
||||
const idx = line.indexOf(":");
|
||||
if (idx > 0) {
|
||||
const sub = document.createElement("span");
|
||||
sub.className = "hero-stat-row-sub";
|
||||
sub.textContent = line.slice(0, idx + 1);
|
||||
div.appendChild(sub);
|
||||
div.appendChild(document.createTextNode(line.slice(idx + 1)));
|
||||
} else {
|
||||
div.textContent = line;
|
||||
}
|
||||
dd.appendChild(div);
|
||||
}
|
||||
} else {
|
||||
dd.textContent = val;
|
||||
}
|
||||
dd.textContent = val;
|
||||
row.appendChild(dt);
|
||||
row.appendChild(dd);
|
||||
list.appendChild(row);
|
||||
@@ -2771,12 +2778,11 @@ function renderDetail() {
|
||||
root.innerHTML = "";
|
||||
|
||||
if (!state.selectedKey) {
|
||||
root.classList.add("empty");
|
||||
root.textContent = "点击英雄查看定位、技能、核心装备、公开数据与被克装备";
|
||||
root.classList.add("empty", "is-collapsed");
|
||||
return;
|
||||
}
|
||||
|
||||
root.classList.remove("empty");
|
||||
root.classList.remove("is-collapsed", "empty");
|
||||
const hero = heroByKey(state.selectedKey);
|
||||
if (!hero) {
|
||||
root.classList.add("empty");
|
||||
@@ -2786,7 +2792,7 @@ function renderDetail() {
|
||||
|
||||
const head = document.createElement("div");
|
||||
head.className = "detail-head";
|
||||
const title = document.createElement("div");
|
||||
const title = document.createElement("h2");
|
||||
title.className = "detail-name";
|
||||
title.textContent = hero.name_loc || hero.key;
|
||||
head.appendChild(title);
|
||||
@@ -2887,7 +2893,6 @@ function renderDetail() {
|
||||
|
||||
const center = document.createElement("div");
|
||||
center.className = "detail-skills-main";
|
||||
center.appendChild(buildHeroTagsRow(hero));
|
||||
center.appendChild(buildSkillsPanel(state.selectedKey));
|
||||
center.appendChild(renderHeroInspect(state.selectedKey));
|
||||
layout.appendChild(center);
|
||||
@@ -3739,6 +3744,7 @@ function renderMatchesPage() {
|
||||
const body = $("#matches-body");
|
||||
const originsEl = $("#matches-origins");
|
||||
const playersEl = $("#matches-players");
|
||||
const sub = $("#matches-sub");
|
||||
if (!body) return;
|
||||
|
||||
const origin = MATCH_ORIGIN_FILTERS.some((o) => o.id === state.matchesOrigin)
|
||||
@@ -3845,6 +3851,7 @@ function renderMatchesPage() {
|
||||
const summary = total
|
||||
? `共 ${total} 场 · 第 ${page}/${pageCount} 页`
|
||||
: "";
|
||||
if (sub) sub.textContent = summary;
|
||||
|
||||
if (!rows.length) {
|
||||
body.innerHTML = state.matchesPlayerId
|
||||
@@ -5385,7 +5392,8 @@ function syncChrome() {
|
||||
btn.classList.toggle("active", btn.dataset.page === state.page);
|
||||
});
|
||||
const heroesTb = $("#heroes-toolbar");
|
||||
const itemsTb = $("#items-toolbar");
|
||||
const heroSearch = $("#q");
|
||||
const itemSearch = $("#q-item");
|
||||
const heroesView = $("#heroes-view");
|
||||
const rankingsView = $("#rankings-view");
|
||||
const matchesView = $("#matches-view");
|
||||
@@ -5396,7 +5404,8 @@ function syncChrome() {
|
||||
const patchesView = $("#patches-view");
|
||||
const detail = $("#detail");
|
||||
if (heroesTb) heroesTb.classList.toggle("hidden", state.page !== "heroes");
|
||||
if (itemsTb) itemsTb.classList.toggle("hidden", state.page !== "items");
|
||||
if (heroSearch) heroSearch.classList.toggle("hidden", state.page !== "heroes");
|
||||
if (itemSearch) itemSearch.classList.toggle("hidden", state.page !== "items");
|
||||
if (heroesView) heroesView.classList.toggle("hidden", state.page !== "heroes");
|
||||
if (rankingsView) rankingsView.classList.toggle("hidden", state.page !== "rankings");
|
||||
if (matchesView) matchesView.classList.toggle("hidden", state.page !== "matches");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Local defaults; production export overwrites via export_relations_site.py. */
|
||||
var SITE_VERSION = "0.5.87";
|
||||
var SITE_VERSION = "0.5.109";
|
||||
var ABILITY_VIDEO_BASE = "";
|
||||
var STATIC_ASSET_BASE = "";
|
||||
|
||||
|
||||
+58
-45
@@ -5,17 +5,18 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>DOTA2 上分帝</title>
|
||||
<link rel="icon" href="/ui-icon/dota2_logo.png" type="image/png" />
|
||||
<link rel="stylesheet" href="/style.css?v=0.5.87" />
|
||||
<script src="/mobile-gate.js?v=0.5.87"></script>
|
||||
<link rel="stylesheet" href="/style.css?v=0.5.109" />
|
||||
<script src="/mobile-gate.js?v=0.5.109"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="sr-only">DOTA2 上分帝</h1>
|
||||
<div id="mobile-gate" class="mobile-gate" role="dialog" aria-labelledby="mobile-gate-title" aria-modal="true">
|
||||
<div class="mobile-gate-card">
|
||||
<div class="mobile-gate-brand" aria-hidden="true">
|
||||
<img class="mobile-gate-logo" src="/ui-icon/dota2_logo_wordmark.png" alt="" />
|
||||
<span class="mobile-gate-brand-title">上分帝</span>
|
||||
</div>
|
||||
<h1 id="mobile-gate-title" class="mobile-gate-title">暂不支持移动端</h1>
|
||||
<h2 id="mobile-gate-title" class="mobile-gate-title">暂不支持移动端</h2>
|
||||
<p class="mobile-gate-desc">请从 PC 端浏览器访问本站。</p>
|
||||
<button type="button" id="mobile-demand-btn" class="mobile-demand-btn">催更移动端</button>
|
||||
<p id="mobile-demand-count" class="mobile-demand-count" hidden>
|
||||
@@ -25,60 +26,59 @@
|
||||
</div>
|
||||
|
||||
<header class="topbar">
|
||||
<div class="brand" aria-label="Dota 2 上分帝">
|
||||
<img class="brand-logo" src="/ui-icon/dota2_logo_wordmark.png" alt="Dota 2" />
|
||||
<span class="brand-title">上分帝</span>
|
||||
</div>
|
||||
<a
|
||||
class="contact-mail"
|
||||
href="mailto:c9mhs8vfmuyv@outlook.com"
|
||||
aria-label="邮件联系"
|
||||
title="c9mhs8vfmuyv@outlook.com"
|
||||
>
|
||||
<svg class="contact-mail-icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false">
|
||||
<path fill="currentColor" d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4-8 5-8-5V6l8 5 8-5v2z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<nav class="main-tabs" aria-label="主分页">
|
||||
<button type="button" class="main-tab active" data-page="heroes">英雄</button>
|
||||
<button type="button" class="main-tab" data-page="rankings">排行</button>
|
||||
<button type="button" class="main-tab" data-page="streamers">主播</button>
|
||||
<button type="button" class="main-tab" data-page="matches">比赛</button>
|
||||
<button type="button" class="main-tab" data-page="trends">走势</button>
|
||||
<button type="button" class="main-tab" data-page="mechanics">机制</button>
|
||||
<button type="button" class="main-tab" data-page="items">物品</button>
|
||||
<button type="button" class="main-tab" data-page="patches">版本</button>
|
||||
</nav>
|
||||
|
||||
<div id="heroes-toolbar" class="toolbar">
|
||||
<div class="tagbar" id="tagbar" aria-label="定位筛选"></div>
|
||||
<input id="q" class="search" type="search" placeholder="搜索英雄或别名" autocomplete="off" spellcheck="false" aria-label="搜索英雄" />
|
||||
</div>
|
||||
|
||||
<div id="items-toolbar" class="toolbar hidden">
|
||||
<div class="item-tools">
|
||||
<input id="q-item" class="search search-inline" type="search" placeholder="搜索物品" autocomplete="off" spellcheck="false" aria-label="搜索物品" />
|
||||
<div class="topbar-primary">
|
||||
<div class="brand" aria-label="Dota 2 上分帝">
|
||||
<img class="brand-logo" src="/ui-icon/dota2_logo_wordmark.png" alt="Dota 2" />
|
||||
<span class="brand-title">上分帝</span>
|
||||
</div>
|
||||
<nav class="main-tabs" aria-label="主分页">
|
||||
<button type="button" class="main-tab active" data-page="heroes">英雄</button>
|
||||
<button type="button" class="main-tab" data-page="rankings">排行</button>
|
||||
<button type="button" class="main-tab" data-page="streamers">主播</button>
|
||||
<button type="button" class="main-tab" data-page="matches">比赛</button>
|
||||
<button type="button" class="main-tab" data-page="trends">走势</button>
|
||||
<button type="button" class="main-tab" data-page="mechanics">机制</button>
|
||||
<button type="button" class="main-tab" data-page="items">物品</button>
|
||||
<button type="button" class="main-tab" data-page="patches">版本</button>
|
||||
</nav>
|
||||
<div class="topbar-tools">
|
||||
<input id="q" class="search" type="search" placeholder="搜索英雄或别名" autocomplete="off" spellcheck="false" aria-label="搜索英雄" />
|
||||
<input id="q-item" class="search search-inline hidden" type="search" placeholder="搜索物品" autocomplete="off" spellcheck="false" aria-label="搜索物品" />
|
||||
<a
|
||||
class="contact-mail"
|
||||
href="mailto:c9mhs8vfmuyv@outlook.com"
|
||||
aria-label="邮件联系"
|
||||
title="c9mhs8vfmuyv@outlook.com"
|
||||
>
|
||||
<svg class="contact-mail-icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" focusable="false">
|
||||
<path fill="currentColor" d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4-8 5-8-5V6l8 5 8-5v2z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</header>
|
||||
|
||||
<main id="heroes-view" class="board">
|
||||
<div class="columns" id="columns"></div>
|
||||
</main>
|
||||
<div id="heroes-toolbar" class="toolbar hero-role-toolbar">
|
||||
<div class="tagbar" id="tagbar" aria-label="英雄定位筛选"></div>
|
||||
</div>
|
||||
|
||||
<main id="rankings-view" class="board rankings-board hidden">
|
||||
<div class="rankings-cluster">
|
||||
<div class="rankings-center-wrap">
|
||||
<div class="rankings-body" id="rankings-body"></div>
|
||||
<aside class="rankings-aside" aria-label="排行筛选">
|
||||
<h2 class="rankings-title">
|
||||
<h2 class="rankings-title page-title">
|
||||
<span class="rankings-title-row">
|
||||
<span class="rankings-kicker" id="rankings-kicker">Immortal 排行榜</span>
|
||||
<span id="rankings-tip-slot"></span>
|
||||
</span>
|
||||
</h2>
|
||||
<div class="rankings-regions" id="rankings-regions" role="tablist" aria-label="选择地区"></div>
|
||||
<p class="rankings-sub" id="rankings-sub"></p>
|
||||
<p class="rankings-sub page-sub" id="rankings-sub"></p>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@@ -89,8 +89,14 @@
|
||||
<div class="rankings-center-wrap matches-center-wrap">
|
||||
<div class="rankings-body matches-body" id="matches-body"></div>
|
||||
<aside class="rankings-aside" aria-label="比赛筛选">
|
||||
<h2 class="rankings-title page-title">
|
||||
<span class="rankings-title-row">
|
||||
<span class="rankings-kicker" id="matches-kicker">明星比赛</span>
|
||||
</span>
|
||||
</h2>
|
||||
<div class="matches-origins" id="matches-origins" role="tablist" aria-label="职业或国服"></div>
|
||||
<div class="matches-players" id="matches-players" role="tablist" aria-label="选择选手"></div>
|
||||
<p class="rankings-sub page-sub" id="matches-sub"></p>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@@ -110,14 +116,14 @@
|
||||
<div class="rankings-center-wrap trends-center-wrap">
|
||||
<div class="rankings-body" id="trends-body"></div>
|
||||
<aside class="rankings-aside" aria-label="走势筛选">
|
||||
<h2 class="rankings-title">
|
||||
<h2 class="rankings-title page-title">
|
||||
<span class="rankings-title-row">
|
||||
<span class="rankings-kicker" id="trends-kicker">过去 8 周走势</span>
|
||||
</span>
|
||||
</h2>
|
||||
<div class="trends-brackets" id="trends-brackets" aria-label="选择段位"></div>
|
||||
<div class="trends-sort" id="trends-sort" role="tablist" aria-label="排序"></div>
|
||||
<p class="rankings-sub" id="trends-sub"></p>
|
||||
<p class="rankings-sub page-sub" id="trends-sub"></p>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
@@ -128,7 +134,7 @@
|
||||
<div class="rankings-center-wrap mechanics-center-wrap">
|
||||
<div class="rankings-body mechanics-body" id="mechanics-body"></div>
|
||||
<aside class="rankings-aside" aria-label="机制筛选">
|
||||
<h2 class="rankings-title">
|
||||
<h2 class="rankings-title page-title">
|
||||
<span class="rankings-title-row">
|
||||
<span class="rankings-kicker">机制查询</span>
|
||||
</span>
|
||||
@@ -142,6 +148,10 @@
|
||||
<main id="items-view" class="board items-board hidden">
|
||||
<div class="items-cluster">
|
||||
<div class="items-main">
|
||||
<header class="page-header items-page-header">
|
||||
<h2 class="page-title">物品商店</h2>
|
||||
<p class="page-sub">基础 / 合成分类目录</p>
|
||||
</header>
|
||||
<div class="shop-col-headers" aria-hidden="true">
|
||||
<span class="shop-col-header basic">基础分类</span>
|
||||
<span class="shop-col-header upgraded">合成分类</span>
|
||||
@@ -156,7 +166,10 @@
|
||||
|
||||
<main id="patches-view" class="board patches-board hidden">
|
||||
<div class="patches-header">
|
||||
<h2 class="patches-title"><span class="patches-kicker">游戏性更新</span><span class="patches-ver" id="patch-ver"></span></h2>
|
||||
<h2 class="patches-title">
|
||||
<span class="patches-kicker page-kicker">游戏性更新</span>
|
||||
<span class="patches-ver" id="patch-ver"></span>
|
||||
</h2>
|
||||
<select id="patch-select" aria-label="选择版本"></select>
|
||||
</div>
|
||||
<div class="patches-detail" id="patches-detail"></div>
|
||||
@@ -165,8 +178,8 @@
|
||||
|
||||
<section class="detail" id="detail" aria-live="polite"></section>
|
||||
|
||||
<script src="/config.js?v=0.5.87"></script>
|
||||
<script src="/router.js?v=0.5.87"></script>
|
||||
<script src="/app.js?v=0.5.87"></script>
|
||||
<script src="/config.js?v=0.5.109"></script>
|
||||
<script src="/router.js?v=0.5.109"></script>
|
||||
<script src="/app.js?v=0.5.109"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+506
-261
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user