Files
planet/frontend/public/earth/js/about.js
rayd1o 5bf5c73ca0
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
release: bump version to 0.66.0
2026-05-26 03:41:47 +08:00

97 lines
3.1 KiB
JavaScript

const EARTH_ABOUT_API = "/api/v1/earth/about";
const DEFAULT_ABOUT = {
logo_src: "./assets/brand/lim-logo.png",
kicker: "About",
title: "智能星球计划",
version: "v0.65.2",
description:
"面向临空场景下的智能媒体研究、全球态势感知与多源开放数据巡航,提供可视化观测、事件聚合与交互式探索能力。",
meta: [
{ label: "出品方", value: "浙江大学临空智能媒体研究院" },
{ label: "策划人", value: "方兴东、黄柳青" },
{ label: "产品兼开发者", value: "钱坤、张鸽、齐鹏" },
],
};
function escapeHtml(value = "") {
return String(value)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function escapeAttribute(value = "") {
return escapeHtml(value);
}
function normalizeAbout(payload) {
const about = payload && typeof payload === "object" ? payload : {};
const meta = Array.isArray(about.meta) ? about.meta : DEFAULT_ABOUT.meta;
return {
...DEFAULT_ABOUT,
...about,
logo_src: about.logo_src || DEFAULT_ABOUT.logo_src,
kicker: about.kicker || DEFAULT_ABOUT.kicker,
title: about.title || DEFAULT_ABOUT.title,
version: about.version || DEFAULT_ABOUT.version,
description: about.description || DEFAULT_ABOUT.description,
meta: meta
.map((item) => ({
label: String(item?.label || "").trim(),
value: String(item?.value || "").trim(),
}))
.filter((item) => item.label || item.value),
};
}
function renderAboutCard(about, isMobile = false) {
const metaClass = isMobile ? "earth-about-meta-list" : "earth-about-grid";
const metaMarkup = about.meta
.map(
(item) => `
<div class="earth-about-meta">
<span>${escapeHtml(item.label)}</span>
<strong>${escapeHtml(item.value)}</strong>
</div>
`,
)
.join("");
return `
<div class="earth-about-logo-frame">
<img class="earth-about-logo" src="${escapeAttribute(about.logo_src)}" alt="${escapeAttribute(about.title)} Logo">
</div>
<div class="earth-about-heading">
<span class="earth-about-kicker">${escapeHtml(about.kicker)}</span>
<strong>${escapeHtml(about.title)}</strong>
<span>${escapeHtml(about.version)}</span>
</div>
<p class="earth-about-copy">${escapeHtml(about.description)}</p>
<div class="${metaClass}">
${metaMarkup}
</div>
`.trim();
}
function applyAboutConfig(about) {
document.querySelectorAll(".earth-about-card").forEach((card) => {
const isMobile = card.classList.contains("earth-about-card--mobile");
card.innerHTML = renderAboutCard(about, isMobile);
});
}
export async function initEarthAbout() {
applyAboutConfig(normalizeAbout(DEFAULT_ABOUT));
try {
const response = await fetch(EARTH_ABOUT_API, { cache: "no-store" });
if (!response.ok) throw new Error(`Earth about request failed: ${response.status}`);
const payload = await response.json();
applyAboutConfig(normalizeAbout(payload?.about));
} catch (error) {
console.warn("Earth about config unavailable, using defaults.", error);
}
}