diff --git a/frontend-web/js/common/member-login-gate.js b/frontend-web/js/common/member-login-gate.js index 1ddb540..a01d0c5 100644 --- a/frontend-web/js/common/member-login-gate.js +++ b/frontend-web/js/common/member-login-gate.js @@ -2,6 +2,8 @@ 'use strict'; var _gate = { index: null, opts: null }; + var MAIL_ICON = ''; + var LOCK_ICON = ''; function esc(s) { if (s == null) return ''; @@ -36,28 +38,36 @@ toast('登录功能暂不可用,请刷新页面后重试', 'warn'); } - function shellHtml(reason) { - var mailIcon = ''; - var lockIcon = ''; + function shellHtml(panel, reason) { + var isReg = panel === 'register'; + var action = esc(reason || '导出或打印'); return '
' + '
' + - '' + - '' + - '' + - '
' + - '
' + + '' + + '' + + '' + + '' + '' + '' + - '' + - '' + - '' + - '
' + + '' + + '' + + '' + '
'; @@ -72,49 +82,59 @@ if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask(); } - function bindModal(modal) { - var form = modal.querySelector('[data-role="login-form"]'); - var errEl = modal.querySelector('[data-role="login-error"]'); - modal.addEventListener('click', function (e) { - var stay = e.target.closest('[data-action="stay"]'); - if (stay) { - e.preventDefault(); - closeGate(); - } + function finishAuthSuccess() { + var opts = _gate.opts; + closeGate(); + var chain = typeof window.soonRefreshPortalAuthUi === 'function' + ? Promise.resolve(window.soonRefreshPortalAuthUi()) + : Promise.resolve(); + return chain.then(function () { + if (typeof window.soonReloadRecentFiles === 'function') window.soonReloadRecentFiles(); + if (opts && typeof opts.onSuccess === 'function') opts.onSuccess(); }); + } + + function storeTokens(data) { + if (!data || !data.access_token) return false; + localStorage.setItem('soon_access', data.access_token); + if (data.refresh_token) localStorage.setItem('soon_refresh', data.refresh_token); + return true; + } + + function swapPanel(modal, panel) { + if (!modal || !modal.parentNode) return; + var parent = modal.parentNode; + var reason = _gate.opts && _gate.opts.reason; + modal.outerHTML = shellHtml(panel, reason); + var root = parent.querySelector('.soon-login-gate'); + if (root) bindModal(root); + } + + function bindAuthForm(modal, role, path, failMsg) { + var form = modal.querySelector('[data-role="' + role + '-form"]'); if (!form) return; form.addEventListener('submit', function (e) { e.preventDefault(); + var errEl = modal.querySelector('[data-role="' + role + '-error"]'); if (errEl) { errEl.hidden = true; errEl.textContent = ''; } var email = (form.email && form.email.value || '').trim(); var password = form.password ? form.password.value : ''; - fetch(apiBase() + '/auth/login', { + fetch(apiBase() + path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, password: password }), }).then(function (r) { return r.json(); }).then(function (j) { - if (!j.ok || !j.data || !j.data.access_token) { + if (!j.ok || !storeTokens(j.data)) { if (errEl) { - errEl.textContent = (j && j.message) || '登录失败'; + errEl.textContent = (j && j.message) || failMsg; errEl.hidden = false; } return; } - localStorage.setItem('soon_access', j.data.access_token); - if (j.data.refresh_token) localStorage.setItem('soon_refresh', j.data.refresh_token); - var opts = _gate.opts; - closeGate(); - var load = typeof window.soonLoadMembership === 'function' - ? window.soonLoadMembership(true) - : Promise.resolve(); - load.then(function () { - if (typeof window.soonRefreshPortalIdentity === 'function') window.soonRefreshPortalIdentity(); - if (typeof window.soonReloadRecentFiles === 'function') window.soonReloadRecentFiles(); - if (opts && typeof opts.onSuccess === 'function') opts.onSuccess(); - }); + finishAuthSuccess(); }).catch(function () { if (errEl) { errEl.textContent = '网络错误,请稍后重试'; @@ -124,7 +144,28 @@ }); } - function soonShowLoginGate(opts) { + function bindModal(modal) { + modal.addEventListener('click', function (e) { + if (e.target.closest('[data-action="stay"]')) { + e.preventDefault(); + closeGate(); + return; + } + if (e.target.closest('[data-action="show-register"]')) { + e.preventDefault(); + swapPanel(modal, 'register'); + return; + } + if (e.target.closest('[data-action="show-login"]')) { + e.preventDefault(); + swapPanel(modal, 'login'); + } + }); + bindAuthForm(modal, 'login', '/auth/login', '登录失败'); + bindAuthForm(modal, 'register', '/auth/register', '注册失败'); + } + + function openGate(opts, panel) { opts = opts || {}; ensureLayer(function () { if (_gate.index != null) { @@ -141,7 +182,7 @@ shadeClose: true, area: [width + 'px', 'auto'], offset: 'auto', - content: shellHtml(opts.reason), + content: shellHtml(panel, opts.reason), success: function (layero, index) { var layerEl = layero && layero[0] ? layero[0] : layero; if (layerEl && layerEl.classList) layerEl.classList.add('soon-layer--login-gate'); @@ -153,7 +194,6 @@ } var modal = layerEl.querySelector('.soon-login-gate'); _gate.index = index; - _gate.opts = opts; if (modal) bindModal(modal); }, end: function () { @@ -163,5 +203,6 @@ }); } - window.soonShowLoginGate = soonShowLoginGate; + window.soonShowLoginGate = function (opts) { openGate(opts, 'login'); }; + window.soonShowRegisterGate = function (opts) { openGate(opts, 'register'); }; })(); diff --git a/frontend-web/js/common/portal-auth.js b/frontend-web/js/common/portal-auth.js index 61778c0..99e80dc 100644 --- a/frontend-web/js/common/portal-auth.js +++ b/frontend-web/js/common/portal-auth.js @@ -1,30 +1,70 @@ (function () { 'use strict'; + var _opts = {}; + var _logoutBound = false; + function showAuthedNav() { var guest = document.getElementById('auth_guest'); var account = document.getElementById('auth_account'); var session = document.getElementById('auth_session'); + var logout = document.getElementById('auth_logout'); if (guest) guest.style.display = 'none'; if (account) account.style.display = 'inline-flex'; if (session) session.style.display = 'inline-flex'; + if (logout) logout.style.display = 'inline-flex'; + } + + function showGuestNav() { + var guest = document.getElementById('auth_guest'); + var account = document.getElementById('auth_account'); + var session = document.getElementById('auth_session'); + var admin = document.getElementById('auth_admin'); + var avatar = document.getElementById('auth_avatar'); + var identity = document.getElementById('auth_identity'); + var logout = document.getElementById('auth_logout'); + if (guest) guest.style.display = ''; + if (account) account.style.display = 'none'; + if (session) session.style.display = 'none'; + if (admin) admin.style.display = 'none'; + if (logout) logout.style.display = 'none'; + if (avatar) { + avatar.style.display = 'none'; + avatar.textContent = ''; + avatar.title = ''; + } + if (identity) { + identity.style.display = 'none'; + identity.textContent = ''; + } } function clearSession() { - if (typeof window.soonClearAuthSession === 'function') { - window.soonClearAuthSession(); - } else { + if (typeof window.soonClearAuthSession === 'function') window.soonClearAuthSession(); + else { localStorage.removeItem('soon_access'); localStorage.removeItem('soon_refresh'); } - if (typeof window.soonApplyMembership === 'function') { - window.soonApplyMembership(null); + if (typeof window.soonApplyMembership === 'function') window.soonApplyMembership(null); + } + + function applyUserUi(data) { + if (typeof window.soonApplyMembership === 'function') window.soonApplyMembership(data); + var admin = document.getElementById('auth_admin'); + var avatar = document.getElementById('auth_avatar'); + if (data.role === 'admin' && admin) admin.style.display = 'inline-flex'; + if (avatar && data.email) { + avatar.style.display = 'inline-flex'; + avatar.textContent = data.email.charAt(0).toUpperCase(); + avatar.title = data.email; } + if (typeof window.soonRefreshPortalIdentity === 'function') window.soonRefreshPortalIdentity(); } function initClearCache() { var btn = document.getElementById('auth_clear_cache'); - if (!btn) return; + if (!btn || btn.getAttribute('data-soon-bound') === '1') return; + btn.setAttribute('data-soon-bound', '1'); btn.addEventListener('click', function (e) { e.preventDefault(); if (typeof layui === 'undefined' || typeof window.soonClearLocalDesignCache !== 'function') return; @@ -49,45 +89,82 @@ else location.reload(); }, 400); }); - } + }, }); }); }); } - function initTopbar(options) { - options = options || {}; + function bindLogoutOnce(options) { + if (_logoutBound) return; + var logout = document.getElementById('auth_logout'); + if (!logout) return; + _logoutBound = true; + logout.addEventListener('click', function (e) { + e.preventDefault(); + clearSession(); + showGuestNav(); + if (options.logoutReload !== false) location.reload(); + else location.href = 'index.web.html'; + }); + } + + function bindPopupLink(id, fn) { + var el = document.getElementById(id); + if (!el || el.getAttribute('data-soon-popup-bound') === '1') return; + el.setAttribute('data-soon-popup-bound', '1'); + el.addEventListener('click', function (e) { + e.preventDefault(); + fn(); + }); + } + + function bindPopupAuthLinks() { + if (typeof window.soonShowLoginGate !== 'function') return; + bindPopupLink('auth_login', function () { + window.soonShowLoginGate({ reason: '登录账号' }); + }); + if (typeof window.soonShowRegisterGate === 'function') { + bindPopupLink('auth_register', function () { + window.soonShowRegisterGate({ reason: '注册账号' }); + }); + } + } + + function refreshAuthUi(options) { + options = options || _opts || {}; initClearCache(); + bindLogoutOnce(options); + bindPopupAuthLinks(); var tok = localStorage.getItem('soon_access') || ''; - var login = document.getElementById('auth_login'); - var reg = document.getElementById('auth_register'); - var admin = document.getElementById('auth_admin'); - var logout = document.getElementById('auth_logout'); - var avatar = document.getElementById('auth_avatar'); - - if (!tok) return; + if (!tok) { + showGuestNav(); + return Promise.resolve(); + } showAuthedNav(); - if (login) login.style.display = 'none'; - if (reg) reg.style.display = 'none'; - if (logout) logout.style.display = 'inline-flex'; var base = (window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base) || ''; - if (!base) return; + if (!base) { + if (typeof window.soonRefreshPortalIdentity === 'function') window.soonRefreshPortalIdentity(); + return Promise.resolve(); + } var meFetch = typeof window.soonAuthedFetch === 'function' ? window.soonAuthedFetch(base + '/auth/me', { headers: { Authorization: 'Bearer ' + tok } }) : fetch(base + '/auth/me', { headers: { Authorization: 'Bearer ' + tok } }); - meFetch + + return meFetch .then(function (r) { if (r.status === 401) { clearSession(); - location.reload(); + showGuestNav(); return null; } if (!r.ok) { if (typeof window.soonToast === 'function') window.soonToast('网络异常,部分功能可能不可用', 'warn'); + if (typeof window.soonRefreshPortalIdentity === 'function') window.soonRefreshPortalIdentity(); return null; } return r.json(); @@ -96,36 +173,23 @@ if (!j) return; if (!j.ok || !j.data) { clearSession(); - location.reload(); + showGuestNav(); return; } - if (typeof window.soonApplyMembership === 'function') { - window.soonApplyMembership(j.data); - } - if (j.data.role === 'admin' && admin) admin.style.display = 'inline-flex'; - if (avatar && j.data.email) { - avatar.style.display = 'inline-flex'; - avatar.textContent = j.data.email.charAt(0).toUpperCase(); - avatar.title = j.data.email; - } - if (typeof window.soonRefreshPortalIdentity === 'function') { - window.soonRefreshPortalIdentity(); - } + applyUserUi(j.data); if (typeof options.onAuthed === 'function') options.onAuthed(j.data); }) .catch(function () { if (typeof window.soonToast === 'function') window.soonToast('网络异常,部分功能可能不可用', 'warn'); + if (typeof window.soonRefreshPortalIdentity === 'function') window.soonRefreshPortalIdentity(); }); - - if (logout) { - logout.addEventListener('click', function (e) { - e.preventDefault(); - clearSession(); - if (options.logoutReload !== false) location.reload(); - else location.href = 'index.web.html'; - }); - } } - window.soonPortalAuth = { init: initTopbar }; + function initTopbar(options) { + _opts = options || {}; + return refreshAuthUi(_opts); + } + + window.soonRefreshPortalAuthUi = refreshAuthUi; + window.soonPortalAuth = { init: initTopbar, refresh: refreshAuthUi }; })();