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

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
+11
View File
@@ -371,6 +371,7 @@
gap: 10px;
flex-wrap: wrap;
justify-content: flex-end;
margin-top: 16px;
}
.row-actions button.pill {
@@ -812,6 +813,16 @@
display: flex;
gap: 14px;
flex-wrap: wrap;
margin-top: 16px;
}
/* 网格和横向行已经通过 gap 管理间距,避免操作区重复叠加外边距。 */
.editor-form > .bottom-actions,
.editor-field > .bottom-actions,
.editor-two .bottom-actions,
.module-list > .row-actions,
.module-row > .row-actions {
margin-top: 0;
}
.editor-form {
+8
View File
@@ -102,6 +102,10 @@
font-weight: 800;
}
.profile-info .profile-load-status:empty {
display: none;
}
.profile-tags,
.mini-actions {
display: flex;
@@ -109,6 +113,10 @@
gap: 10px;
}
.mini-actions {
margin-top: 12px;
}
.profile-tags span,
.mini-actions span {
padding: 7px 12px;
+46
View File
@@ -198,6 +198,52 @@ img {
white-space: nowrap;
}
.nav-user-entry {
min-width: 0;
display: inline-flex;
align-items: center;
gap: 8px;
font-weight: 700;
}
.nav-user-avatar {
width: 32px;
height: 32px;
position: relative;
flex: 0 0 auto;
display: inline-grid;
place-items: center;
overflow: hidden;
border: 1px solid rgba(255, 248, 230, .56);
border-radius: 50%;
color: var(--red-deep);
background: #fff4dc;
font-size: 13px;
font-weight: 900;
}
.nav-user-avatar img {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
object-fit: cover;
}
.nav-user-avatar-fallback {
position: absolute;
inset: 0;
display: grid;
place-items: center;
}
.nav-user-name {
max-width: 96px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-current-genealogy {
min-width: 0;
max-width: 240px;
+1 -3
View File
@@ -111,9 +111,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function isForbidden(error) {
+3 -5
View File
@@ -368,9 +368,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -662,7 +660,7 @@
if (!savedId) throw new Error('保存响应缺少 albumId');
verified = findAlbumById(await api.albums(genealogyId), savedId);
if (!verified) throw new Error('保存后相册列表未返回同一相册');
if (root.location) root.location.href = buildDetailUrl(genealogyId, savedId);
root.NavigationUtil.open(buildDetailUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setStatus(
@@ -725,7 +723,7 @@
try {
await api.deleteAlbum(genealogyId, albumId);
if (query('[data-album-page]')) await loadAlbums();
else if (root.location) root.location.href = buildListUrl(genealogyId);
else root.NavigationUtil.open(buildListUrl(genealogyId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(isForbidden(error) ? '当前账号无权删除该相册。' : (error.message || '相册删除失败'));
+2 -4
View File
@@ -260,9 +260,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -549,7 +547,7 @@
if (!matchesSavedArticle(await api.articleDetail(genealogyId, savedId), savedId)) {
throw new Error('保存后重读未返回同一篇谱文');
}
if (root.location) root.location.href = buildListUrl(genealogyId, savedId);
root.NavigationUtil.open(buildListUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setFormStatus(
+17 -4
View File
@@ -91,6 +91,17 @@
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
async function cacheCurrentUser(api) {
if (!root.PublicNavigation) return;
root.PublicNavigation.clearUser();
try {
root.PublicNavigation.storeUser(await api.currentProfile());
} catch (error) {
// 账户摘要加载失败不应阻断已经成功的登录,个人中心会再次读取并同步资料。
root.PublicNavigation.clearUser();
}
}
function getCaptchaOperation(formType) {
// PC 验证中心按业务动作解析服务端已激活的验证场景。
var config = FORM_CONFIG[formType] || {};
@@ -385,7 +396,8 @@
setFormStatus(form, '正在验证账号…', 'loading');
try {
await api.login(buildLoginBody(values));
root.location.href = await resolvePostLoginUrl(api, 'login');
await cacheCurrentUser(api);
root.NavigationUtil.open(await resolvePostLoginUrl(api, 'login'));
} catch (error) {
setFormStatus(form, error.message || '登录失败', getApiStateType(error));
} finally {
@@ -407,7 +419,8 @@
setFormStatus(form, '正在验证短信验证码…', 'loading');
try {
await api.loginBySms(buildSmsLoginBody(values));
root.location.href = await resolvePostLoginUrl(api, 'sms-login');
await cacheCurrentUser(api);
root.NavigationUtil.open(await resolvePostLoginUrl(api, 'sms-login'));
} catch (error) {
setFormStatus(form, error.message || '短信登录失败', getApiStateType(error));
} finally {
@@ -429,7 +442,7 @@
setFormStatus(form, '正在创建账号…', 'loading');
try {
await api.register(buildRegisterBody(values));
root.location.href = getSuccessUrl('register');
root.NavigationUtil.open(getSuccessUrl('register'));
} catch (error) {
setFormStatus(form, error.message || '注册失败', getApiStateType(error));
} finally {
@@ -490,7 +503,7 @@
try {
await api.resetPassword(buildPasswordResetBody(values));
showMessage('密码已重设,请重新登录');
root.location.href = getSuccessUrl('password-reset');
root.NavigationUtil.open(getSuccessUrl('password-reset'));
} catch (error) {
setFormStatus(form, error.message || '重设密码失败', getApiStateType(error));
} finally {
+3 -5
View File
@@ -486,9 +486,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -876,7 +874,7 @@
if (!savedId) throw new Error('保存响应缺少 ceremonyId');
verified = normalizeCeremony(await api.ceremonyDetail(genealogyId, savedId));
if (!verified || verified.ceremonyId !== savedId) throw new Error('保存后未能重读同一活动');
if (root.location) root.location.href = buildDetailUrl(genealogyId, savedId);
root.NavigationUtil.open(buildDetailUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setStatus(
@@ -974,7 +972,7 @@
try {
await api.deleteCeremony(genealogyId, ceremonyId);
if (query('[data-ceremony-list-page]')) await loadCeremonies();
else if (root.location) root.location.href = buildListUrl(genealogyId);
else root.NavigationUtil.open(buildListUrl(genealogyId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(isForbidden(error) ? '当前账号无权删除该活动。' : (error.message || '活动删除失败'));
+1 -6
View File
@@ -47,12 +47,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
+1 -1
View File
@@ -116,7 +116,7 @@
if (!page) return;
if (!api || !api.getToken || !api.getToken()) {
if (root.location) root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return;
}
+2 -6
View File
@@ -130,9 +130,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function query(selector) {
@@ -190,9 +188,7 @@
}
genealogyId = root.ProfileUI && root.ProfileUI.getGenealogyId();
if (!genealogyId) {
if (root.location && typeof root.location.replace === 'function') {
root.location.replace('profile-families.html?next=profile-family-home.html');
}
root.NavigationUtil.open('profile-families.html?next=profile-family-home.html');
return;
}
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) {
+1 -1
View File
@@ -67,7 +67,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (root.location) root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
async function init() {
+4 -9
View File
@@ -48,12 +48,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -179,7 +174,7 @@
if (!feedId || !genealogyId) return;
if (!query('[data-feed-detail-page]')) {
if (root.location) root.location.href = buildFeedUrl('detail', genealogyId, feedId);
root.NavigationUtil.open(buildFeedUrl('detail', genealogyId, feedId));
return;
}
if (getCurrentFeedId() !== toId(feedId)) return;
@@ -883,7 +878,7 @@
await api.feedDetail(genealogyId, savedFeedId);
}
showMessage('动态已保存');
root.location.href = buildPostSaveUrl(genealogyId, savedFeedId, body.status);
root.NavigationUtil.open(buildPostSaveUrl(genealogyId, savedFeedId, body.status));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setFeedFormStatus(
@@ -930,7 +925,7 @@
return withActionLock('delete:' + feedId, async function () {
try {
await api.deleteFeed(genealogyId, feedId);
if (query('[data-feed-detail-page]')) root.location.href = buildFeedUrl('list', genealogyId);
if (query('[data-feed-detail-page]')) root.NavigationUtil.open(buildFeedUrl('list', genealogyId));
else await loadFeeds(currentFeedPage);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
+2 -4
View File
@@ -188,9 +188,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function isForbidden(error) {
@@ -339,7 +337,7 @@
throw new Error('提交后无法读取同一反馈记录');
}
if (pageMode === 'ticket') {
if (root.location) root.location.href = buildFeedbackDetailUrl(created.feedbackId);
root.NavigationUtil.open(buildFeedbackDetailUrl(created.feedbackId));
return;
}
setFeedbackList(list, refreshed, 'ticket-detail.html');
+2 -4
View File
@@ -142,9 +142,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function getFormValues(form) {
@@ -269,7 +267,7 @@
if (root.ProfileUI && root.ProfileUI.setCurrentGenealogyContext) {
root.ProfileUI.setCurrentGenealogyContext(verified.genealogyId, verified.genealogyName);
}
if (root.location) root.location.href = buildCreatedGenealogyUrl(verified);
root.NavigationUtil.open(buildCreatedGenealogyUrl(verified));
} catch (error) {
if (shouldRedirectToLogin(api, error)) {
redirectToLogin(api);
+2 -2
View File
@@ -58,7 +58,7 @@
if (!page) return;
if (!api || !api.getToken || !api.getToken()) {
if (root.location) root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return;
}
if (!genealogyId) {
@@ -129,7 +129,7 @@
genealogyName: nameInput.value.trim(), smsCode: codeInput.value.trim()
});
if (root.ProfileUI && root.ProfileUI.clearCurrentGenealogyContext) root.ProfileUI.clearCurrentGenealogyContext();
if (root.location) root.location.href = 'profile-families.html';
root.NavigationUtil.open('profile-families.html');
} catch (error) { setStatus('error', error.message || '永久删除提交失败'); }
finally { setPending(false); }
});
+1 -6
View File
@@ -44,12 +44,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
+2 -7
View File
@@ -340,12 +340,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -655,7 +650,7 @@
if (!savedId) throw new Error('保存响应缺少 recordId');
verifiedDetail = await api.growthRecordDetail(genealogyId, savedId);
if (!matchesSavedRecord(verifiedDetail, savedId)) throw new Error('保存后重读未返回同一条成长记录');
if (root.location) root.location.href = buildListUrl(genealogyId, savedId);
root.NavigationUtil.open(buildListUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setFormStatus(
+1 -3
View File
@@ -128,9 +128,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function isForbidden(error) {
+2 -4
View File
@@ -194,9 +194,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function getApiStateType(error) {
@@ -351,7 +349,7 @@
if (!refreshed.some(function (item) { return item.applyId === created.applyId; })) {
throw new Error('提交后无法读取同一申请');
}
if (root.location) root.location.href = 'profile-join-family.html';
root.NavigationUtil.open('profile-join-family.html');
} catch (error) {
if (shouldRedirectToLogin(api, error)) {
redirectToLogin(api);
+1 -3
View File
@@ -119,9 +119,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function getApiStateType(error) {
+1 -6
View File
@@ -52,12 +52,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
+2 -4
View File
@@ -240,9 +240,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -532,7 +530,7 @@
setStatus('[data-member-action-status]', '正在退出当前家谱…', 'loading');
try {
await api.leaveGenealogy(genealogyId);
if (root.location) root.location.href = 'profile-families.html';
root.NavigationUtil.open('profile-families.html');
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setStatus('[data-member-action-status]', isForbidden(error) ? '当前账号无权退出该家谱。' : (error.message || '退出家谱失败'), isForbidden(error) ? 'forbidden' : 'error');
+2 -4
View File
@@ -308,9 +308,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -597,7 +595,7 @@
if (!savedId) throw new Error('保存响应缺少 memoId');
verifiedDetail = await api.memoDetail(genealogyId, savedId);
if (!matchesSavedMemo(verifiedDetail, savedId)) throw new Error('保存后重读未返回同一条备忘录');
if (root.location) root.location.href = buildListUrl(genealogyId, savedId);
root.NavigationUtil.open(buildListUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setFormStatus(
+2 -4
View File
@@ -365,9 +365,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -664,7 +662,7 @@
if (!savedId) throw new Error('保存响应缺少 meritId');
verifiedDetail = await api.meritRecordDetail(genealogyId, savedId);
if (!matchesSavedMerit(verifiedDetail, savedId)) throw new Error('保存后重读未返回同一条功德记录');
if (root.location) root.location.href = buildListUrl(genealogyId, savedId);
root.NavigationUtil.open(buildListUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setFormStatus(
+1 -3
View File
@@ -172,9 +172,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
+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;
+2 -2
View File
@@ -123,7 +123,7 @@
if (!page) return;
if (!api || !api.getToken || !api.getToken()) {
if (root.location) root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return;
}
if (!genealogyId) {
@@ -218,7 +218,7 @@
);
if (!access || !access.accessUrl) throw new Error('文件访问响应缺少地址');
if (accessWindow) accessWindow.location.replace(access.accessUrl);
else root.location.href = access.accessUrl;
else root.NavigationUtil.open(access.accessUrl);
return;
}
if (unlockButton) {
+5 -3
View File
@@ -253,7 +253,8 @@
var api = window.GenealogyApi && window.GenealogyApi.defaultClient;
var redirect = function () {
clearCurrentGenealogyContext();
window.location.href = targetUrl || 'index.html';
if (window.PublicNavigation) window.PublicNavigation.clearUser();
window.NavigationUtil.open(targetUrl || 'index.html');
};
if (!api || !api.logout) {
@@ -310,7 +311,7 @@
content: $item.attr('data-layer-confirm') || '确定继续吗?',
confirmText: $item.attr('data-confirm-text') || '确定',
onConfirm: function () {
if (href && href !== '#') window.location.href = href;
if (href && href !== '#') window.NavigationUtil.open(href);
}
});
});
@@ -489,6 +490,7 @@
var $sidebar = $('.module-nav').first();
var $header = $('.site-header').first();
var headerItems = [
{ label: '首页', href: 'index.html' },
{ label: '我的家谱', href: 'profile-families.html' },
{ label: '家族管理', href: 'profile-family-admin.html', context: true },
{ label: '内容发布', href: 'profile-content.html', context: true },
@@ -498,7 +500,7 @@
var currentSection = getHeaderSection(currentPage);
if ($header.length) {
$header.find('.brand').first().attr('href', 'profile.html').html(
$header.find('.brand').first().attr('href', 'index.html').html(
'<img class="brand-logo brand-logo-mark" src="public/images/logo-mark.png" alt="" />' +
'<span class="brand-text">个人中心</span>'
);
+2 -6
View File
@@ -104,12 +104,7 @@
}
function redirectToLogin() {
if (!root.location) return;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function redirectUnauthorized(api, error) {
@@ -246,6 +241,7 @@
try {
profile = await api.currentProfile();
if (requestId !== latestProfileRequestId) return;
if (root.PublicNavigation) root.PublicNavigation.storeUser(profile);
renderProfile(buildProfileView(profile));
fillProfileForm(profile);
setProfileLoadStatus('', '');
+1 -1
View File
@@ -56,7 +56,7 @@
if (!page) return;
if (!api || !api.getToken || !api.getToken()) {
if (root.location) root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return;
}
if (!genealogyId) {
+1 -6
View File
@@ -92,12 +92,7 @@
}
function redirectToLogin() {
if (!root.location) return;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function redirectUnauthorized(api, error) {
+2 -4
View File
@@ -364,9 +364,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -657,7 +655,7 @@
if (!savedId) throw new Error('保存响应缺少 relativeId');
verifiedDetail = await api.relativeRecordDetail(genealogyId, savedId);
if (!matchesSavedRecord(verifiedDetail, savedId)) throw new Error('保存后重读未返回同一条亲友记录');
if (root.location) root.location.href = buildListUrl(genealogyId, savedId);
root.NavigationUtil.open(buildListUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setFormStatus(
+3 -9
View File
@@ -91,13 +91,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
function redirectUnauthorized(api, error) {
@@ -417,7 +411,7 @@
setStatus(form, 'loading', '正在提交注销申请…');
await api.deactivateAccount(buildDeactivateBody(values));
showMessage('账号已提交注销');
root.location.href = 'index.html';
root.NavigationUtil.open('index.html');
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setStatus(form, 'error', '注销失败,请稍后重试。');
@@ -437,7 +431,7 @@
} catch (error) {
// 退出请求失败时客户端仍会清理本地令牌,页面必须离开受保护区域。
} finally {
root.location.href = 'index.html';
root.NavigationUtil.open('index.html');
}
}
+1 -6
View File
@@ -51,12 +51,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
+2 -7
View File
@@ -285,12 +285,7 @@
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
return true;
}
@@ -587,7 +582,7 @@
savedId = videoId || (saved && saved.videoId);
if (!savedId) throw new Error('保存响应缺少 videoId');
await api.videoDetail(genealogyId, savedId);
if (root.location) root.location.href = buildListUrl(genealogyId, savedId);
root.NavigationUtil.open(buildListUrl(genealogyId, savedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setFormStatus(
+1 -3
View File
@@ -313,9 +313,7 @@
function redirectToLogin(api) {
if (api && api.clearToken) api.clearToken();
if (!root.location) return;
if (typeof root.location.replace === 'function') root.location.replace('login.html');
else root.location.href = 'login.html';
root.NavigationUtil.open('login.html');
}
async function init() {