merge: integrate bug1.0 updates
This commit is contained in:
+150
-13
@@ -1,3 +1,30 @@
|
||||
const ROUTE_MATHEMATICIANS = {
|
||||
believer: [
|
||||
{ name: "高斯", portrait: "/static/img/mathematicians/1000.png", identity: "定理工匠" },
|
||||
{ name: "祖冲之", portrait: "/static/img/mathematicians/1010.png", identity: "穷竭守尺人" },
|
||||
{ name: "欧拉", portrait: "/static/img/mathematicians/1001.png", identity: "公式狂僧" },
|
||||
{ name: "诺特", portrait: "/static/img/mathematicians/1011.png", identity: "抽象代数宗师" },
|
||||
],
|
||||
spreader: [
|
||||
{ name: "斐波那契", portrait: "/static/img/mathematicians/1100.png", identity: "商路算郎" },
|
||||
{ name: "华罗庚", portrait: "/static/img/mathematicians/1110.png", identity: "双法布道者" },
|
||||
{ name: "伽罗瓦", portrait: "/static/img/mathematicians/1101.png", identity: "决斗狂热者" },
|
||||
{ name: "Lovelace", portrait: "/static/img/mathematicians/1111.png", identity: "织机先知" },
|
||||
],
|
||||
applier: [
|
||||
{ name: "秦九韶", portrait: "/static/img/mathematicians/0100.png", identity: "大衍谋士" },
|
||||
{ name: "牛顿", portrait: "/static/img/mathematicians/0110.png", identity: "宇宙立法者" },
|
||||
{ name: "图灵", portrait: "/static/img/mathematicians/0111.png", identity: "密码破译使" },
|
||||
{ name: "冯·诺依曼", portrait: "/static/img/mathematicians/0101.png", identity: "博弈游侠" },
|
||||
],
|
||||
seer: [
|
||||
{ name: "希帕提娅", portrait: "/static/img/mathematicians/0000.png", identity: "几何殉道者" },
|
||||
{ name: "赵爽", portrait: "/static/img/mathematicians/0010.png", identity: "弦图先觉" },
|
||||
{ name: "庞加莱", portrait: "/static/img/mathematicians/0001.png", identity: "拓扑游方" },
|
||||
{ name: "约翰逊", portrait: "/static/img/mathematicians/0011.png", identity: "人脑计算机" },
|
||||
],
|
||||
};
|
||||
|
||||
const state = {
|
||||
user: null,
|
||||
stories: [],
|
||||
@@ -184,25 +211,100 @@ function card({ meta, title, body, foot, action, onClick, disabled = false }) {
|
||||
return article;
|
||||
}
|
||||
|
||||
function renderRouteMathematicians() {
|
||||
Object.entries(ROUTE_MATHEMATICIANS).forEach(([clan, mathematicians]) => {
|
||||
const root = $(`[data-mathematicians="${clan}"]`);
|
||||
if (!root) return;
|
||||
root.replaceChildren(...mathematicians.map((person) => {
|
||||
const item = document.createElement("div");
|
||||
item.className = "route-mathematician";
|
||||
const img = document.createElement("img");
|
||||
img.src = person.portrait;
|
||||
img.alt = person.name;
|
||||
img.loading = "lazy";
|
||||
const name = document.createElement("b");
|
||||
name.textContent = person.name;
|
||||
const identity = document.createElement("small");
|
||||
identity.textContent = person.identity;
|
||||
item.append(img, name, identity);
|
||||
return item;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
async function loadStories() {
|
||||
const root = $("#story-list");
|
||||
try {
|
||||
state.stories = await api("math-life/stories/");
|
||||
root.classList.remove("loading");
|
||||
root.replaceChildren(...state.stories.map((story) => card({
|
||||
meta: story.kind === "flagship" ? "旗舰人生" : story.kind === "skill" ? "人物 Skill" : "特别篇",
|
||||
title: story.title,
|
||||
body: story.summary || "在不完整信息中判断机会、关系与代价。",
|
||||
foot: `约 ${story.estimated_minutes} 分钟`,
|
||||
action: story.available ? "开始人生 →" : "查看预告",
|
||||
disabled: !story.available,
|
||||
onClick: () => beginStory(story.slug),
|
||||
})));
|
||||
renderRouteBadges();
|
||||
renderSkillList();
|
||||
} catch (error) {
|
||||
root.textContent = error.message;
|
||||
showToast(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function renderRouteBadges() {
|
||||
const flagship = state.stories.find((story) => story.kind === "flagship" && story.available);
|
||||
const skillCount = state.stories.filter((story) => story.kind === "skill").length;
|
||||
const alumniCount = $("#alumni-count");
|
||||
alumniCount.textContent = skillCount > 0 ? `当前 ${skillCount} 个校友人生可体验` : "即将上线校友访谈";
|
||||
if (flagship) {
|
||||
const route = flagship.slug.includes("believer") ? "believer" : "believer";
|
||||
const card = $(`.route-card[data-route="${route}"]`);
|
||||
if (card) {
|
||||
card.querySelector(".route-status").textContent = `可体验 · 约 ${flagship.estimated_minutes} 分钟`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderSkillList() {
|
||||
const root = $("#skill-list");
|
||||
const skills = state.stories.filter((story) => story.kind === "skill");
|
||||
if (skills.length === 0) {
|
||||
root.classList.remove("loading");
|
||||
root.textContent = "校友访谈即将上线,敬请期待。";
|
||||
return;
|
||||
}
|
||||
root.classList.remove("loading");
|
||||
root.replaceChildren(...skills.map((story) => {
|
||||
const article = document.createElement("article");
|
||||
article.className = "skill-card";
|
||||
const header = document.createElement("div");
|
||||
header.className = "skill-header";
|
||||
const meta = document.createElement("span");
|
||||
meta.className = "skill-meta";
|
||||
meta.textContent = story.kind === "skill" ? "校友访谈" : "特别篇";
|
||||
header.append(meta);
|
||||
const body = document.createElement("div");
|
||||
body.className = "skill-body";
|
||||
const title = document.createElement("h3");
|
||||
title.textContent = story.title;
|
||||
const summary = document.createElement("p");
|
||||
summary.textContent = story.summary || "在不完整信息中判断机会、关系与代价。";
|
||||
const footer = document.createElement("footer");
|
||||
const duration = document.createElement("span");
|
||||
duration.textContent = `约 ${story.estimated_minutes} 分钟`;
|
||||
const button = document.createElement("button");
|
||||
button.textContent = story.available ? "开始人生 →" : "查看预告";
|
||||
button.disabled = !story.available;
|
||||
if (story.available) button.addEventListener("click", () => beginStory(story.slug));
|
||||
footer.append(duration, button);
|
||||
body.append(title, summary, footer);
|
||||
article.append(header, body);
|
||||
return article;
|
||||
}));
|
||||
}
|
||||
|
||||
function showLifeHub() {
|
||||
$("#life-hub").classList.remove("hidden");
|
||||
$("#life-skills").classList.add("hidden");
|
||||
}
|
||||
|
||||
function showLifeSkills() {
|
||||
$("#life-hub").classList.add("hidden");
|
||||
$("#life-skills").classList.remove("hidden");
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
|
||||
async function beginStory(slug) {
|
||||
if (!requireAuth()) return;
|
||||
try {
|
||||
@@ -958,6 +1060,18 @@ function switchTool(tool) {
|
||||
window.HuluToolbox?.activate(tool);
|
||||
}
|
||||
|
||||
function renderLatexPreview(source) {
|
||||
const target = $("#latex-preview");
|
||||
target.replaceChildren();
|
||||
if (!source.trim()) return;
|
||||
if (typeof katex === "undefined") { target.textContent = source; return; }
|
||||
try {
|
||||
katex.render(source, target, { throwOnError: false, displayMode: true });
|
||||
} catch {
|
||||
target.textContent = source;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveFormula() {
|
||||
if (!requireAuth()) return;
|
||||
try {
|
||||
@@ -977,13 +1091,34 @@ function bindUI() {
|
||||
$$("[data-open-auth]").forEach((button) => button.addEventListener("click", openAuth));
|
||||
$("#start-mathbti").addEventListener("click", startMathBTI);
|
||||
$("#save-formula").addEventListener("click", saveFormula);
|
||||
$("#latex-source").addEventListener("input", (event) => { $("#latex-preview").textContent = event.target.value; });
|
||||
$("#latex-source").addEventListener("input", (event) => { renderLatexPreview(event.target.value); });
|
||||
$$(".tool-card").forEach((button) => {
|
||||
button.addEventListener("click", () => switchTool(button.dataset.tool));
|
||||
});
|
||||
$("#symbol-search").addEventListener("input", (event) => {
|
||||
renderSymbols(event.target.value);
|
||||
});
|
||||
$("#enter-alumni").addEventListener("click", showLifeSkills);
|
||||
$("#back-to-hub").addEventListener("click", showLifeHub);
|
||||
$$(".route-card").forEach((card) => {
|
||||
card.addEventListener("click", (event) => {
|
||||
if (event.target.closest(".route-enter")) return;
|
||||
const route = card.dataset.route;
|
||||
const flagship = state.stories.find(
|
||||
(story) => story.kind === "flagship" && story.available && story.slug.includes(route),
|
||||
);
|
||||
if (flagship) beginStory(flagship.slug);
|
||||
else showToast(`${card.querySelector(".route-spirit b").textContent}人生即将开放`);
|
||||
});
|
||||
card.querySelector(".route-enter").addEventListener("click", () => {
|
||||
const route = card.dataset.route;
|
||||
const flagship = state.stories.find(
|
||||
(story) => story.kind === "flagship" && story.available && story.slug.includes(route),
|
||||
);
|
||||
if (flagship) beginStory(flagship.slug);
|
||||
else showToast(`${card.querySelector(".route-spirit b").textContent}人生即将开放`);
|
||||
});
|
||||
});
|
||||
$$(".ability-node").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
state.videoAbility =
|
||||
@@ -1041,6 +1176,7 @@ function bindUI() {
|
||||
|
||||
async function boot() {
|
||||
bindUI();
|
||||
renderRouteMathematicians();
|
||||
window.HuluToolbox?.init();
|
||||
window.HuluRealtime?.init();
|
||||
renderSymbols();
|
||||
@@ -1051,6 +1187,7 @@ async function boot() {
|
||||
loadContent(),
|
||||
window.HuluGames?.load(),
|
||||
]);
|
||||
renderLatexPreview($("#latex-source").value);
|
||||
}
|
||||
|
||||
boot();
|
||||
|
||||
Reference in New Issue
Block a user