Files
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

113 lines
3.6 KiB
JavaScript

(function () {
'use strict';
var refreshPromise = null;
function getAccessToken() {
try { return localStorage.getItem('soon_access') || ''; } catch (e) { return ''; }
}
function getRefreshToken() {
try { return localStorage.getItem('soon_refresh') || ''; } catch (e) { return ''; }
}
function clearSession() {
try {
localStorage.removeItem('soon_access');
localStorage.removeItem('soon_refresh');
} catch (e) { /* ignore */ }
}
function getApiBase() {
var cfg = (typeof window !== 'undefined' && window.SOON_DEPLOY_CONFIG && window.SOON_DEPLOY_CONFIG.api_v1_base);
if (cfg) return String(cfg).replace(/\/+$/, '');
if (typeof window !== 'undefined' && window.location) {
return window.location.origin + '/api/v1';
}
return '/api/v1';
}
function redirectToLogin() {
var path = 'login.web.html';
try {
var current = window.location.pathname.replace(/^.*\/pages\//, '').replace(/^\//, '');
if (current && current.indexOf('login.web.html') !== 0) {
var redirect = typeof soonSafeRedirect === 'function'
? soonSafeRedirect(current + window.location.search, 'index.web.html')
: 'index.web.html';
path = 'login.web.html?redirect=' + encodeURIComponent(redirect);
}
} catch (e) { /* use default */ }
window.location.href = path;
}
function refreshAccessToken() {
if (refreshPromise) return refreshPromise;
var refresh = getRefreshToken();
if (!refresh) {
return Promise.reject(new Error('no_refresh_token'));
}
refreshPromise = fetch(getApiBase() + '/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refresh }),
}).then(function (r) {
return r.json().then(function (j) {
if (!r.ok || !j.ok || !j.data || !j.data.access_token) {
throw new Error(j.message || 'refresh_failed');
}
localStorage.setItem('soon_access', j.data.access_token);
if (j.data.refresh_token) {
localStorage.setItem('soon_refresh', j.data.refresh_token);
}
return j.data.access_token;
});
}).finally(function () {
refreshPromise = null;
});
return refreshPromise;
}
function soonAuthedFetch(url, opts) {
opts = opts || {};
var headers = Object.assign({}, opts.headers || {});
var token = getAccessToken();
if (token) {
headers['Authorization'] = 'Bearer ' + token;
} else {
delete headers['Authorization'];
delete headers['authorization'];
}
opts.headers = headers;
var fullUrl = url;
if (url.indexOf('http') !== 0) {
fullUrl = getApiBase() + '/' + String(url).replace(/^\/+/, '');
}
var hadSession = !!(token || getRefreshToken());
return fetch(fullUrl, opts).then(function (response) {
if (response.status !== 401) return response;
if (!getRefreshToken()) return response;
return refreshAccessToken().then(function (newToken) {
var retryHeaders = Object.assign({}, opts.headers || {});
retryHeaders['Authorization'] = 'Bearer ' + newToken;
var retryOpts = Object.assign({}, opts, { headers: retryHeaders });
return fetch(fullUrl, retryOpts);
}).catch(function () {
if (hadSession) {
clearSession();
redirectToLogin();
}
return response;
});
});
}
window.soonAuthedFetch = soonAuthedFetch;
window.soonClearAuthSession = clearSession;
window.soonGetAccessToken = getAccessToken;
window.soonRedirectToLogin = redirectToLogin;
})();