修改页面,完成单页模式新增首页返回上一页按钮

This commit is contained in:
2026-08-31 14:48:32 +08:00
parent aaed0584b1
commit 15b301b358
363 changed files with 10895 additions and 213 deletions
+211 -12
View File
@@ -1,31 +1,228 @@
(function () {
function syncPublicNavigation() {
let token = "";
const blockedNavigationProtocol = /^(?:javascript|data|vbscript|mailto|tel):/i;
const publicUserKey = "genealogy_public_user_v1";
let publicApiPromise;
function getStorage() {
try {
return window.localStorage;
} catch (error) {
return null;
}
}
function normalizeAvatarUrl(value) {
try {
const url = new URL(String(value || "").trim());
return url.protocol === "https:" || url.protocol === "http:" ? url.href : "";
} catch (error) {
return "";
}
}
function normalizePublicUser(profile) {
const nickName = String(profile && profile.nickName || "").trim();
const avatarUrl = normalizeAvatarUrl(
profile && (profile.avatarUrl || profile.avatarFile && profile.avatarFile.accessUrl)
);
if (!nickName) return null;
return { nickName, avatarUrl };
}
function readPublicUser() {
const storage = getStorage();
if (!storage) return null;
try {
return normalizePublicUser(JSON.parse(storage.getItem(publicUserKey) || "null"));
} catch (error) {
return null;
}
}
function getAuthToken() {
const storage = getStorage();
try {
token = window.localStorage.getItem("genealogy_auth_token") || "";
return storage && storage.getItem("genealogy_auth_token") || "";
} catch (error) {
return;
return "";
}
}
function createUserEntry(user) {
const link = document.createElement("a");
const avatar = document.createElement("span");
const fallback = document.createElement("span");
const name = document.createElement("span");
const initial = Array.from(user.nickName)[0] || "家";
link.className = "nav-user-entry";
link.href = "profile.html";
link.setAttribute("aria-label", `${user.nickName}的个人中心`);
avatar.className = "nav-user-avatar";
fallback.className = "nav-user-avatar-fallback";
fallback.textContent = initial;
avatar.appendChild(fallback);
if (user.avatarUrl) {
const image = document.createElement("img");
image.src = user.avatarUrl;
image.alt = "";
image.loading = "lazy";
image.decoding = "async";
image.addEventListener("error", () => image.remove());
avatar.prepend(image);
}
name.className = "nav-user-name";
name.textContent = user.nickName;
link.append(avatar, name);
return link;
}
function loadScript(src) {
return new Promise((resolve, reject) => {
const existing = document.querySelector(`script[src="${src}"]`);
if (existing && existing.dataset.loaded === "true") {
resolve();
return;
}
if (existing) {
resolve();
return;
}
const script = existing || document.createElement("script");
script.addEventListener("load", () => {
script.dataset.loaded = "true";
resolve();
}, { once: true });
script.addEventListener("error", reject, { once: true });
if (!existing) {
script.src = src;
document.head.appendChild(script);
}
});
}
function getPublicApi() {
if (window.GenealogyApi && window.GenealogyApi.defaultClient) {
return Promise.resolve(window.GenealogyApi.defaultClient);
}
if (!publicApiPromise) {
publicApiPromise = [
"utils/StorageUtil.js",
"utils/axios.js",
"utils/AxiosRequestUtil.js",
"utils/ApiClient.js"
].reduce((ready, src) => ready.then(() => loadScript(src)), Promise.resolve())
.then(() => window.GenealogyApi && window.GenealogyApi.defaultClient);
}
return publicApiPromise;
}
function shouldOpenInNewWindow(link) {
if (!link || link.tagName !== "A" || link.hasAttribute("download")) return false;
const href = (link.getAttribute("href") || "").trim();
return Boolean(href && href !== "#" && !href.startsWith("#") && !blockedNavigationProtocol.test(href));
}
function prepareLink(link) {
if (!shouldOpenInNewWindow(link)) return;
if (link.classList.contains("brand") && link.getAttribute("href") !== "index.html") {
link.setAttribute("href", "index.html");
}
if (link.target !== "_blank") link.target = "_blank";
const relValues = (link.getAttribute("rel") || "").split(/\s+/).filter(Boolean);
if (!relValues.includes("noopener")) {
link.setAttribute("rel", [...relValues, "noopener"].join(" "));
}
}
function prepareLinks(container) {
if (container.nodeType !== 1) return;
if (container.matches("a[href]")) prepareLink(container);
container.querySelectorAll("a[href]").forEach(prepareLink);
}
function installNewWindowNavigation() {
prepareLinks(document.documentElement);
new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "attributes") {
prepareLink(mutation.target);
return;
}
mutation.addedNodes.forEach(prepareLinks);
});
}).observe(document.documentElement, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ["href", "target", "download"]
});
}
function syncPublicNavigation() {
const token = getAuthToken();
const user = readPublicUser() || { nickName: "我的账户", avatarUrl: "" };
if (!token) return;
document.querySelectorAll(".nav-actions").forEach((actions) => {
const loginLink = actions.querySelector('a[href="login.html"]');
const registerLink = actions.querySelector('a[href="register.html"]');
const accountLink = actions.querySelector('a[href="profile-data.html"]');
const currentUserLink = actions.querySelector(".nav-user-entry");
const createLink = actions.querySelector('a[href="create-genealogy.html"]');
if (loginLink) {
loginLink.href = "profile.html";
loginLink.textContent = "个人中心";
}
if (registerLink) {
registerLink.href = "profile-data.html";
registerLink.textContent = "账户资料";
}
const userEntry = createUserEntry(user);
const insertionPoint = currentUserLink || loginLink || accountLink || createLink;
if (insertionPoint) insertionPoint.replaceWith(userEntry);
else actions.prepend(userEntry);
if (loginLink && loginLink.isConnected) loginLink.remove();
if (accountLink && accountLink.isConnected) accountLink.remove();
if (currentUserLink && currentUserLink.isConnected) currentUserLink.remove();
if (registerLink) registerLink.remove();
if (createLink) createLink.href = "profile-create-family.html";
});
}
window.PublicNavigation = {
storeUser: function (profile) {
const storage = getStorage();
const user = normalizePublicUser(profile);
if (!storage || !user) return false;
storage.setItem(publicUserKey, JSON.stringify(user));
syncPublicNavigation();
return true;
},
clearUser: function () {
const storage = getStorage();
if (storage) storage.removeItem(publicUserKey);
}
};
async function refreshPublicUser() {
if (!getAuthToken() || readPublicUser() || document.body.matches(".page-profile, .page-profile-module")) return;
try {
const api = await getPublicApi();
if (api) window.PublicNavigation.storeUser(await api.currentProfile());
} catch (error) {
// 公共页保持可用的账户入口;资料接口恢复后,登录或进入个人中心会重新同步。
window.PublicNavigation.clearUser();
}
}
function initSiteMenu() {
const body = document.body;
const header = document.querySelector(".site-header");
@@ -86,8 +283,10 @@
});
}
installNewWindowNavigation();
syncPublicNavigation();
initSiteMenu();
refreshPublicUser();
// 只在精确指针设备上启用微动效,触屏和减少动效偏好下保持静态。
const finePointer = window.matchMedia("(hover: hover) and (pointer: fine)").matches;