Files
SoonDesign/frontend-web/js/common/member-login-gate.js
T
24kycj 5fb269cc0a fix(web): 本地优先保存与 design 页体验修复
- 保存先写本地缓存,登录后可选云端同步;退出登录不再因文件操作跳转登录

- 修复 Layui 遮罩残留、design2 保存后线条、toast 被 finally 提前关闭

- 保存过程恢复 loading spinner;成功提示 2.5 秒

- 云端 payload 瘦身与体积超限提示;后端 schema 迁移与 FileService 容错

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 19:24:34 +08:00

168 lines
6.5 KiB
JavaScript

(function () {
'use strict';
var _gate = { index: null, opts: null };
function esc(s) {
if (s == null) return '';
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function toast(msg, type) {
if (typeof window.soonToast === 'function') window.soonToast(msg, type);
}
function apiBase() {
var cfg = window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base;
if (cfg) return String(cfg).replace(/\/+$/, '');
if (window.location) return window.location.origin + '/api/v1';
return '/api/v1';
}
function ensureLayer(cb) {
if (typeof layer !== 'undefined' && layer.open) {
if (layer.config) layer.config({ skin: 'soon-layer' });
cb();
return;
}
if (typeof layui !== 'undefined') {
layui.use(['layer'], function () {
window.layer = layui.layer;
layer.config({ skin: 'soon-layer' });
cb();
});
return;
}
toast('登录功能暂不可用,请刷新页面后重试', 'warn');
}
function shellHtml(reason) {
var mailIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6h16v12H4z"/><path d="M4 8l8 5 8-5"/></svg>';
var lockIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="11" width="14" height="10" rx="1"/><path d="M8 11V8a4 4 0 118 0v3"/></svg>';
return '<div class="soon-login-gate">' +
'<header class="soon-login-gate__head">' +
'<span class="soon-login-gate__eyebrow">登录</span>' +
'<h3 class="soon-login-gate__title">登录后继续</h3>' +
'<p class="soon-login-gate__desc">预览内' + esc(reason || '导出或打印') + '成品需先登录账号。</p>' +
'</header>' +
'<form class="soon-login-gate__form" data-role="login-form">' +
'<div class="soon-input-wrap soon-login-gate__field">' +
'<span class="soon-input-icon" aria-hidden="true">' + mailIcon + '</span>' +
'<input type="email" name="email" required class="soon-input soon-login-gate__input" placeholder="邮箱" autocomplete="email">' +
'</div>' +
'<div class="soon-input-wrap soon-login-gate__field">' +
'<span class="soon-input-icon" aria-hidden="true">' + lockIcon + '</span>' +
'<input type="password" name="password" required class="soon-input soon-login-gate__input" placeholder="密码" autocomplete="current-password">' +
'</div>' +
'<p class="soon-login-gate__error" data-role="login-error" hidden></p>' +
'<button type="submit" class="soon-btn soon-btn--block soon-login-gate__submit">登录</button>' +
'<p class="soon-login-gate__links">还没有账号?<a href="register.web.html" target="_blank" rel="noopener">立即注册</a></p>' +
'</form>' +
'<footer class="soon-login-gate__foot">' +
'<button type="button" class="soon-login-gate__stay" data-action="stay">继续设计</button>' +
'</footer></div>';
}
function closeGate() {
var idx = _gate.index;
_gate = { index: null, opts: null };
if (idx != null && typeof layer !== 'undefined') {
try { layer.close(idx); } catch (e) { /* ignore */ }
}
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();
}
});
if (!form) return;
form.addEventListener('submit', function (e) {
e.preventDefault();
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', {
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 (errEl) {
errEl.textContent = (j && j.message) || '登录失败';
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();
});
}).catch(function () {
if (errEl) {
errEl.textContent = '网络错误,请稍后重试';
errEl.hidden = false;
}
});
});
}
function soonShowLoginGate(opts) {
opts = opts || {};
ensureLayer(function () {
if (_gate.index != null) {
try { layer.close(_gate.index); } catch (e) { /* ignore */ }
}
_gate.opts = opts;
var width = Math.min(400, window.innerWidth - 32);
layer.open({
type: 1,
skin: 'soon-layer',
title: false,
closeBtn: 1,
shade: [0.62, '#000'],
shadeClose: true,
area: [width + 'px', 'auto'],
offset: 'auto',
content: shellHtml(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');
var content = layerEl && layerEl.querySelector ? layerEl.querySelector('.layui-layer-content') : null;
if (content) {
content.style.padding = '0';
content.style.overflow = 'visible';
content.style.background = 'transparent';
}
var modal = layerEl.querySelector('.soon-login-gate');
_gate.index = index;
_gate.opts = opts;
if (modal) bindModal(modal);
},
end: function () {
_gate = { index: null, opts: null };
},
});
});
}
window.soonShowLoginGate = soonShowLoginGate;
})();