验证20%

This commit is contained in:
rain
2026-07-31 11:31:26 +08:00
parent 309598bfa6
commit ac5823547a
24 changed files with 751 additions and 279 deletions
+66 -4
View File
@@ -543,6 +543,68 @@
box-shadow: 0 24px 60px rgba(57, 48, 36, .11);
}
.module-card.is-unavailable {
cursor: not-allowed;
opacity: .68;
}
.module-card.is-unavailable:after {
display: none;
}
.module-card.is-unavailable .pill {
display: inline-flex;
margin-top: 12px;
color: #6b542d;
background: #fff0cf;
}
.module-nav-unavailable {
padding: 14px 16px;
border-radius: 14px;
color: var(--muted);
background: #f4f1eb;
font-weight: 900;
}
.profile-path {
display: flex;
align-items: center;
gap: 8px;
margin: 0 0 18px;
color: #6f766f;
font-size: 14px;
font-weight: 800;
}
.profile-path a {
color: var(--red);
}
.profile-path a:hover {
text-decoration: underline;
}
.profile-path span {
color: #a89f91;
}
.profile-path strong {
min-width: 0;
overflow: hidden;
color: var(--ink);
text-overflow: ellipsis;
white-space: nowrap;
}
.module-nav-unavailable small {
display: block;
margin-top: 4px;
color: #8a5a3b;
font-size: 12px;
font-weight: 700;
}
.module-card.is-selected {
border-color: rgba(200, 59, 50, .5);
background: #fff6f1;
@@ -825,6 +887,10 @@
}
@media (max-width: 720px) {
.page-profile-module .module-nav {
display: none;
}
.module-title-row {
display: grid;
}
@@ -833,10 +899,6 @@
font-size: 38px;
}
.module-nav {
grid-template-columns: 1fr;
}
.lineage-filter-toolbar {
grid-template-columns: 1fr;
}
+32 -1
View File
@@ -33,6 +33,7 @@
.profile-card,
.profile-stats,
.profile-next-card,
.panel,
.side-card {
border: 1px solid rgba(224, 211, 189, .84);
@@ -94,6 +95,13 @@
line-height: 1.9;
}
.profile-info .profile-load-status {
margin: 14px 0 0;
color: var(--muted);
font-size: 14px;
font-weight: 800;
}
.profile-tags,
.mini-actions {
display: flex;
@@ -118,6 +126,29 @@
gap: 14px;
}
.profile-next-card {
padding: 32px;
}
.profile-next-card .eyebrow {
margin-bottom: 14px;
}
.profile-next-card h2 {
margin-bottom: 12px;
font-size: 30px;
}
.profile-next-card p {
margin-bottom: 22px;
color: var(--muted);
line-height: 1.8;
}
.profile-hero-actions {
margin-top: 22px;
}
.profile-stats div {
min-height: 116px;
padding: 20px;
@@ -610,4 +641,4 @@
.logout-actions {
grid-template-columns: 1fr;
}
}
}
+156
View File
@@ -186,6 +186,39 @@ img {
white-space: nowrap;
}
.profile-current-genealogy {
min-width: 0;
max-width: 240px;
display: grid;
gap: 2px;
margin-left: auto;
padding: 7px 12px;
border: 1px solid rgba(255, 248, 230, .28);
border-radius: 12px;
color: #fff8e6;
background: rgba(79, 29, 26, .18);
line-height: 1.2;
}
.profile-current-genealogy span {
color: rgba(255, 248, 230, .72);
font-size: 11px;
font-weight: 700;
}
.profile-current-genealogy strong {
overflow: hidden;
color: #fff8e6;
font-size: 14px;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-mobile-menu-toggle,
.profile-mobile-menu {
display: none;
}
.btn {
min-width: 118px;
height: 44px;
@@ -790,6 +823,78 @@ textarea {
background: #fff4ef;
}
@media (max-width: 1320px) {
.page-profile .nav-links,
.page-profile-module .nav-links {
display: none;
}
.profile-mobile-menu-toggle {
min-width: 58px;
height: 38px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 12px;
border: 1px solid rgba(255, 248, 230, .38);
border-radius: 10px;
color: #fff8e6;
background: rgba(79, 29, 26, .2);
font: inherit;
font-size: 13px;
font-weight: 800;
}
.page-profile .nav-actions,
.page-profile-module .nav-actions {
display: none;
}
.profile-mobile-menu.is-open {
display: block;
position: absolute;
top: 100%;
right: 0;
left: 0;
padding: 14px 0 18px;
border-bottom: 1px solid rgba(255, 253, 248, .24);
background: rgba(121, 42, 35, .99);
box-shadow: 0 22px 38px rgba(83, 32, 26, .28);
}
.profile-mobile-menu-inner {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.profile-mobile-menu a,
.profile-mobile-menu button {
min-height: 46px;
display: flex;
align-items: center;
padding: 10px 14px;
border: 1px solid rgba(255, 248, 230, .16);
border-radius: 10px;
color: #fff8e6;
background: rgba(255, 253, 248, .08);
font: inherit;
font-weight: 800;
text-align: left;
}
.profile-mobile-menu button {
width: 100%;
cursor: pointer;
}
.profile-mobile-menu a.is-current {
border-color: rgba(255, 241, 202, .76);
color: #fff7d8;
background: rgba(255, 253, 248, .2);
}
}
@media (max-width: 1120px) {
.nav-links {
display: none;
@@ -819,6 +924,57 @@ textarea {
display: none;
}
.page-profile-module .nav,
.page-profile .nav {
height: 70px;
gap: 10px;
}
.page-profile-module .brand,
.page-profile .brand {
padding: 7px 10px;
}
.page-profile-module .brand-logo,
.page-profile .brand-logo {
width: 114px;
}
.page-profile-module .brand-logo-mark {
width: 38px;
height: 38px;
}
.page-profile-module .brand-text {
display: none;
}
.page-profile-module .nav-actions {
display: none;
}
.profile-current-genealogy {
max-width: 112px;
padding: 7px 9px;
}
.profile-current-genealogy span {
display: none;
}
.profile-current-genealogy strong {
font-size: 13px;
}
.profile-mobile-menu-toggle {
min-width: 50px;
padding: 0 9px;
}
.profile-mobile-menu-inner {
grid-template-columns: 1fr;
}
.inner-hero .container {
padding: 46px 0 54px;
}
+37 -2
View File
@@ -104,6 +104,39 @@
return config.successUrl || '';
}
function normalizeGenealogyId(value) {
var id = value === undefined || value === null ? '' : String(value).trim();
if (typeof value === 'number' && !Number.isSafeInteger(value)) return '';
return /^[1-9][0-9]*$/.test(id) ? id : '';
}
function getPostLoginUrl(genealogies) {
var ids = (Array.isArray(genealogies) ? genealogies : []).map(function (item) {
return normalizeGenealogyId(item && item.genealogyId);
}).filter(Boolean).filter(function (id, index, values) {
return values.indexOf(id) === index;
});
if (ids.length === 1) {
return 'profile-family-home.html?genealogyId=' + encodeURIComponent(ids[0]);
}
return 'profile-families.html';
}
async function resolvePostLoginUrl(api, formType) {
if ((formType !== 'login' && formType !== 'sms-login') || !api || !api.genealogiesMine) {
return getSuccessUrl(formType);
}
try {
return getPostLoginUrl(await api.genealogiesMine());
} catch (error) {
return getSuccessUrl(formType);
}
}
function getFormCaptchaOperation(form, formType) {
// 业务动作由当前脚本统一管理,避免散落在 HTML 属性中。
var operationCode = getCaptchaOperation(formType);
@@ -327,7 +360,7 @@
setBusy(button, true);
try {
await api.login(buildLoginBody(values));
root.location.href = getSuccessUrl('login');
root.location.href = await resolvePostLoginUrl(api, 'login');
} catch (error) {
showMessage(error.message || '登录失败');
} finally {
@@ -348,7 +381,7 @@
setBusy(button, true);
try {
await api.loginBySms(buildSmsLoginBody(values));
root.location.href = getSuccessUrl('sms-login');
root.location.href = await resolvePostLoginUrl(api, 'sms-login');
} catch (error) {
showMessage(error.message || '短信登录失败');
} finally {
@@ -489,6 +522,8 @@
getCaptchaOperation: getCaptchaOperation,
getFormCaptchaOperation: getFormCaptchaOperation,
getSuccessUrl: getSuccessUrl,
getPostLoginUrl: getPostLoginUrl,
resolvePostLoginUrl: resolvePostLoginUrl,
validateAuthValues: validateAuthValues,
init: init
};
+4
View File
@@ -163,7 +163,11 @@
try {
result = await loadFamilyHome(api, genealogyId);
if (root.ProfileUI && root.ProfileUI.setCurrentGenealogyContext) {
root.ProfileUI.setCurrentGenealogyContext(result.overview.genealogyId, result.overview.genealogyName);
}
setText('[data-family-home-title]', result.overview.genealogyName);
setText('[data-family-home-context]', result.overview.genealogyName);
query('[data-family-home-overview]').innerHTML = renderFamilyOverview(result.overview);
setText('[data-family-home-member-count]', String(result.overview.memberCount));
setText('[data-family-home-person-count]', String(result.overview.personCount));
+12
View File
@@ -209,6 +209,9 @@
if (!created) throw new Error('创建响应缺少有效家谱信息');
verified = normalizeCreatedGenealogy(await api.genealogyDetail(created.genealogyId));
if (!verified || verified.genealogyId !== created.genealogyId) throw new Error('创建后无法读取同一家谱');
if (root.ProfileUI && root.ProfileUI.setCurrentGenealogyContext) {
root.ProfileUI.setCurrentGenealogyContext(verified.genealogyId, verified.genealogyName);
}
if (root.location) root.location.href = buildCreatedGenealogyUrl(verified);
} catch (error) {
if (shouldRedirectToLogin(api, error)) {
@@ -266,6 +269,7 @@
].filter(Boolean).join(' · ');
return '<a class="module-row" data-genealogy-id="' + escapeHtml(item.genealogyId) +
'" data-genealogy-name="' + escapeHtml(item.genealogyName) +
'" href="' + escapeHtml(buildEntryUrl(item.genealogyId, target)) + '">' +
'<div><h3>' + escapeHtml(item.genealogyName) + '</h3><p>' + escapeHtml(meta) +
'</p></div><span class="pill">' + (item.canManage ? '管理并进入' : '进入家谱') + '</span></a>';
@@ -284,6 +288,10 @@
var result;
if (!status) return;
if (shouldRedirectToLogin(api)) {
redirectToLogin(api);
return;
}
if (!api || !api.genealogyQuota || !api.genealogiesMine) {
status.textContent = '家谱入口接口初始化失败,请刷新后重试。';
return;
@@ -294,6 +302,10 @@
renderGenealogies(container, result[0], target);
status.textContent = buildStatus(result[1]);
} catch (error) {
if (shouldRedirectToLogin(api, error)) {
redirectToLogin(api);
return;
}
status.textContent = '无法读取我的家谱,请稍后重试。';
}
}
+187
View File
@@ -5,6 +5,77 @@
var layuiReady = false;
var layerApi = null;
var genealogyContextStorageKey = 'genealogy_current_context_v1';
var mobileMenuId = 'profile-mobile-menu';
function getSessionStorage() {
try {
return window.sessionStorage;
} catch (error) {
return null;
}
}
function normalizeGenealogyName(value) {
var name = String(value === undefined || value === null ? '' : value).trim();
return name && name.length <= 80 ? name : '';
}
function getStoredGenealogyContext() {
var storage = getSessionStorage();
var stored;
var context;
var genealogyId;
var genealogyName;
if (!storage) return null;
try {
stored = storage.getItem(genealogyContextStorageKey);
context = stored ? JSON.parse(stored) : null;
} catch (error) {
return null;
}
genealogyId = normalizeGenealogyId(context && context.genealogyId);
genealogyName = normalizeGenealogyName(context && context.genealogyName);
return genealogyId && genealogyName ? {
genealogyId: genealogyId,
genealogyName: genealogyName
} : null;
}
function setCurrentGenealogyContext(genealogyId, genealogyName) {
var storage = getSessionStorage();
var context = {
genealogyId: normalizeGenealogyId(genealogyId),
genealogyName: normalizeGenealogyName(genealogyName)
};
if (!context.genealogyId || !context.genealogyName) return null;
if (storage) {
try {
storage.setItem(genealogyContextStorageKey, JSON.stringify(context));
} catch (error) {}
}
renderCurrentGenealogyContext();
return context;
}
function clearCurrentGenealogyContext() {
var storage = getSessionStorage();
if (!storage) return;
try {
storage.removeItem(genealogyContextStorageKey);
} catch (error) {}
}
function getCurrentGenealogyContext() {
var genealogyId = getGenealogyId();
var context = getStoredGenealogyContext();
return context && context.genealogyId === genealogyId ? context : null;
}
function openLegacyLogout() {
var $modal = $('#logoutModal');
@@ -58,6 +129,7 @@
function performLogout(targetUrl) {
var api = window.GenealogyApi && window.GenealogyApi.defaultClient;
var redirect = function () {
clearCurrentGenealogyContext();
window.location.href = targetUrl || 'index.html';
};
@@ -172,6 +244,113 @@
});
}
function renderCurrentGenealogyContext() {
var context = getCurrentGenealogyContext();
var name = context ? context.genealogyName : '正在识别家谱';
$('[data-current-genealogy-name]').text(name);
$('[data-current-genealogy-link]').attr('aria-label', context ?
'当前家谱:' + context.genealogyName + ',点击切换家谱' :
'当前家谱,点击切换家谱');
}
function refreshCurrentGenealogyContext() {
var genealogyId = getGenealogyId();
var api = window.GenealogyApi && window.GenealogyApi.defaultClient;
if (!genealogyId || getCurrentGenealogyContext() || !api || !api.genealogyDetail) return;
api.genealogyDetail(genealogyId).then(function (detail) {
var detailId = normalizeGenealogyId(detail && detail.genealogyId);
var detailName = normalizeGenealogyName(detail && detail.genealogyName);
if (detailId === genealogyId && detailName) {
setCurrentGenealogyContext(detailId, detailName);
}
}).catch(function () {
$('[data-current-genealogy-name]').text('当前家谱');
});
}
function initCurrentGenealogyContext() {
var genealogyId = getGenealogyId();
var $nav;
var $actions;
var $context;
if (!genealogyId) return;
$nav = $('.site-header .nav').first();
if (!$nav.length || $nav.find('[data-current-genealogy-link]').length) return;
$context = $('<a class="profile-current-genealogy" data-current-genealogy-link href="profile-families.html">' +
'<span>当前家谱</span><strong data-current-genealogy-name>正在识别家谱</strong></a>');
$actions = $nav.find('.nav-actions').first();
if ($actions.length) $context.insertBefore($actions);
else $nav.append($context);
renderCurrentGenealogyContext();
refreshCurrentGenealogyContext();
}
function setMobileMenuOpen(isOpen) {
var $menu = $('[data-profile-mobile-menu]').first();
var $toggle = $('[data-profile-mobile-menu-toggle]').first();
if (!$menu.length || !$toggle.length) return;
$menu.prop('hidden', !isOpen).toggleClass('is-open', !!isOpen).attr('aria-hidden', isOpen ? 'false' : 'true');
$toggle.attr('aria-expanded', isOpen ? 'true' : 'false');
}
function initMobileMenu() {
var $header = $('.site-header').first();
var $nav;
var currentPage;
if (!$header.length || $header.find('[data-profile-mobile-menu]').length) return;
$nav = $header.find('.nav').first();
if (!$nav.length) return;
$nav.append('<button class="profile-mobile-menu-toggle" type="button" data-profile-mobile-menu-toggle ' +
'aria-expanded="false" aria-controls="' + mobileMenuId + '">菜单</button>');
$header.append('<div class="profile-mobile-menu" id="' + mobileMenuId + '" data-profile-mobile-menu hidden aria-hidden="true">' +
'<div class="container profile-mobile-menu-inner">' +
'<a href="profile.html">个人中心</a>' +
'<a href="profile-families.html">我的家谱</a>' +
'<a href="profile-create-family.html">创建家谱</a>' +
'<a href="profile-family-home.html" data-genealogy-context-link>家谱主页</a>' +
'<a href="profile-content.html" data-genealogy-context-link>内容发布</a>' +
'<a href="profile-family-admin.html" data-genealogy-context-link>家族管理</a>' +
'<a href="profile-data.html">个人资料</a>' +
'<a href="profile-messages.html">消息中心</a>' +
'<a href="profile-services.html">帮助与服务</a>' +
'<button type="button" data-logout-open data-logout-url="index.html">退出登录</button>' +
'</div></div>');
currentPage = (window.location.pathname.split('/').pop() || 'profile.html').split('?')[0];
$header.find('[data-profile-mobile-menu] a').each(function () {
var $link = $(this);
var target = ($link.attr('href') || '').split('?')[0];
if (target === currentPage) $link.addClass('is-current').attr('aria-current', 'page');
});
}
function bindGenealogySelection() {
$(document).on('click', '[data-genealogy-id][data-genealogy-name]', function () {
setCurrentGenealogyContext($(this).attr('data-genealogy-id'), $(this).attr('data-genealogy-name'));
});
}
function bindMobileMenu() {
$(document).on('click', '[data-profile-mobile-menu-toggle]', function () {
setMobileMenuOpen($(this).attr('aria-expanded') !== 'true');
});
$(document).on('click', '[data-profile-mobile-menu] a, [data-profile-mobile-menu] button', function () {
setMobileMenuOpen(false);
});
$(document).on('click', function (event) {
if (!$(event.target).closest('.site-header').length) setMobileMenuOpen(false);
});
$(document).on('keydown', function (event) {
if (event.key === 'Escape') setMobileMenuOpen(false);
});
}
function updateDisplay($trigger, value) {
var target = $trigger.attr('data-update-target');
var $target = target ? $(target) : $trigger.closest('p').find('b').first();
@@ -277,6 +456,10 @@
bindSelectActions();
bindMessageActions();
bindSelectableCards();
bindGenealogySelection();
bindMobileMenu();
initCurrentGenealogyContext();
initMobileMenu();
syncGenealogyContextLinks();
});
@@ -288,6 +471,10 @@
normalizeGenealogyId: normalizeGenealogyId,
withGenealogyId: withGenealogyId,
getGenealogyEntryUrl: getGenealogyEntryUrl,
getCurrentGenealogyContext: getCurrentGenealogyContext,
setCurrentGenealogyContext: setCurrentGenealogyContext,
clearCurrentGenealogyContext: clearCurrentGenealogyContext,
refreshCurrentGenealogyContext: refreshCurrentGenealogyContext,
syncGenealogyContextLinks: syncGenealogyContextLinks
};
})(window, window.jQuery || (window.layui && window.layui.$));
+21
View File
@@ -155,6 +155,22 @@
});
}
function setProfileLoadStatus(message) {
var statusNode = query('[data-profile-load-status]');
if (statusNode) statusNode.textContent = message || '';
}
function buildUnavailableProfileView() {
return {
displayName: '账户资料暂时无法读取',
avatarText: '',
phone: '暂无数据',
birthday: '暂无数据',
regionText: '暂无数据'
};
}
function getFormValues(form) {
var values = {};
@@ -236,6 +252,7 @@
if (!api || !documentRef || !documentRef.querySelector('[data-profile-page]')) return;
if (redirectUnauthorized(api)) return;
setProfileLoadStatus('正在读取账户资料…');
try {
profile = await api.currentProfile();
@@ -244,8 +261,11 @@
fillProfileForm(profile);
fillRegionPicker(profile);
renderRegionText(profile, requestId);
setProfileLoadStatus('');
} catch (error) {
if (redirectUnauthorized(api, error)) return;
renderProfile(buildUnavailableProfileView());
setProfileLoadStatus('资料读取失败,请刷新页面后重试。');
showMessage(error.message || '个人资料加载失败');
}
}
@@ -293,6 +313,7 @@
getFinalRegionCode: getFinalRegionCode,
buildRegionText: buildRegionText,
buildProfileView: buildProfileView,
buildUnavailableProfileView: buildUnavailableProfileView,
buildProfileUpdateBody: buildProfileUpdateBody,
shouldRedirectToLogin: shouldRedirectToLogin,
reloadProfile: loadProfile,