fix(web): 修复 .soon/模板加载、旧文件兼容与另存为离开提示
- 删除 design1 ui 遗留代码,修复加载竞态与新建不弹保存提示 - cloud-files 归一化旧版 .soon,IndexedDB 写入完成后再跳转设计页 - 模板/本地 .soon 首次保存须另存为,离开前统一确认 - 发版缓存 bust(version.js、soon-cache.js)及宝塔 Nginx 配置文档 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -36,7 +36,8 @@
|
||||
|
||||
window.soonJs = function (rel) {
|
||||
var n = String(rel || '').replace(/^\/+/, '');
|
||||
return window.SOON_JS_BASE + n;
|
||||
var url = window.SOON_JS_BASE + n;
|
||||
return (typeof window.soonWithVersion === 'function') ? window.soonWithVersion(url) : url;
|
||||
};
|
||||
|
||||
/** 等 HTMLImage 解码完成后再 toDataURL / setSrc,避免间歇性空白背景 */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
var FILE_PREFIX = 'soondesign_file:';
|
||||
var TEMPLATE_PREFIX = 'soondesign_template:';
|
||||
var SESSION_PREFIX = 'soondesign_session:';
|
||||
|
||||
function soonParseTemplateKey(key) {
|
||||
if (!key || typeof key !== 'string') return null;
|
||||
@@ -39,12 +40,60 @@
|
||||
return FILE_PREFIX + id + ':v' + version;
|
||||
}
|
||||
|
||||
function soonIsCloudFileKey(key) {
|
||||
return !!(key && typeof key === 'string' && key.indexOf(FILE_PREFIX) === 0);
|
||||
}
|
||||
|
||||
function soonIsTemplateKey(key) {
|
||||
return !!(key && typeof key === 'string' && key.indexOf(TEMPLATE_PREFIX) === 0);
|
||||
}
|
||||
|
||||
/** 打开模板 / 本地 .soon / session 导入:首次保存须另存为;云端文件或已选定保存目标后可原地保存 */
|
||||
function soonNeedsSaveDialog(key) {
|
||||
return !key || soonIsTemplateKey(key);
|
||||
if (!key) return true;
|
||||
if (soonIsCloudFileKey(key)) return false;
|
||||
if (window._soonSaveInPlaceKey && key === window._soonSaveInPlaceKey) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function soonOnOpenDesignFile(fileKey) {
|
||||
window._soonSaveInPlaceKey = '';
|
||||
}
|
||||
|
||||
function soonMarkSaveInPlace(fileKey) {
|
||||
if (fileKey) window._soonSaveInPlaceKey = fileKey;
|
||||
}
|
||||
|
||||
/** 离开页/新建前是否需提示保存:有未撤销编辑,或已打开过文件/模板(含未改动的打开态) */
|
||||
function soonShouldConfirmBeforeLeave() {
|
||||
try {
|
||||
if (typeof window.step !== 'undefined' && window.step && window.step.val > 0) return true;
|
||||
} catch (e) { /* ignore */ }
|
||||
var oa = typeof window.openAs !== 'undefined' ? window.openAs : null;
|
||||
return !!(oa && oa.name);
|
||||
}
|
||||
|
||||
function soonConfirmLeaveThen(run) {
|
||||
if (typeof run !== 'function') return;
|
||||
if (typeof window.soonShouldConfirmBeforeLeave === 'function' && !window.soonShouldConfirmBeforeLeave()) {
|
||||
run();
|
||||
return;
|
||||
}
|
||||
if (typeof window.layer === 'undefined' || !window.layer || typeof window.language_str !== 'function') {
|
||||
run();
|
||||
return;
|
||||
}
|
||||
window.layer.confirm(window.language_str('whetherSave'), {
|
||||
btn: [window.language_str('save'), window.language_str('noSave'), window.language_str('cancel')]
|
||||
}, function () {
|
||||
if (typeof window.output === 'function') {
|
||||
window.output(function () { run(); });
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}, function () {
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
||||
function soonDefaultNewSoonName() {
|
||||
@@ -623,38 +672,115 @@
|
||||
return j && j.soonType ? j.soonType : (j && j.backBlackPic ? 2 : 1);
|
||||
}
|
||||
|
||||
function soonPutSoonSession(j, fileName) {
|
||||
if (!j) return '';
|
||||
/** 兼容旧版 .soon:补全 f/b/fo/bo,加载时去掉 output 大图字段 */
|
||||
function soonNormalizeSoonJson(j) {
|
||||
if (!j || typeof j !== 'object') return null;
|
||||
if (typeof window.soonStripDesign1HeavyFields === 'function') {
|
||||
window.soonStripDesign1HeavyFields(j);
|
||||
}
|
||||
if (!j.f || !j.f.objects || !Array.isArray(j.f.objects)) {
|
||||
if (j.objects && Array.isArray(j.objects) && j.objects.length > 0) {
|
||||
j.f = {
|
||||
version: j.version || '4.6.0',
|
||||
objects: j.objects,
|
||||
hoverCursor: j.hoverCursor || 'move'
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (j.f.objects.length === 0) return null;
|
||||
if (!j.b || !j.b.objects || !Array.isArray(j.b.objects)) {
|
||||
j.b = { version: '4.6.0', objects: [], hoverCursor: 'move' };
|
||||
}
|
||||
if (!j.fo) j.fo = [];
|
||||
if (!j.bo) j.bo = [];
|
||||
var o0 = j.f.objects[0];
|
||||
if (o0) {
|
||||
if (o0.left == null || isNaN(o0.left)) o0.left = 0;
|
||||
if (o0.top == null || isNaN(o0.top)) o0.top = 0;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
function soonRunWhenDesignBgReady(fn, onTimeout) {
|
||||
if (typeof fn !== 'function') return;
|
||||
function ready() {
|
||||
return !!(window.background_image1 || window.background_image);
|
||||
}
|
||||
if (ready()) {
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
var n = 0;
|
||||
(function tick() {
|
||||
if (ready()) {
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
if (++n > 120) {
|
||||
if (typeof onTimeout === 'function') onTimeout();
|
||||
return;
|
||||
}
|
||||
setTimeout(tick, 50);
|
||||
})();
|
||||
}
|
||||
|
||||
function soonPrepareSoonSessionWrite(j, fileName) {
|
||||
var normalized = soonNormalizeSoonJson(j);
|
||||
if (!normalized) return null;
|
||||
var displayName = soonEnsureSoonExt(fileName || 'design.soon');
|
||||
var key = soonMakeSessionKey(displayName);
|
||||
if (typeof window.soonLocalCacheAndThumb === 'function') {
|
||||
window.soonLocalCacheAndThumb(key, j, { source: 'session', name: displayName });
|
||||
try { sessionStorage.setItem(key, 'idb'); } catch (e) { /* ignore */ }
|
||||
return key;
|
||||
var slim = Object.assign({}, normalized);
|
||||
if (typeof window.soonStripDesign1HeavyFields === 'function') {
|
||||
window.soonStripDesign1HeavyFields(slim);
|
||||
}
|
||||
var payload = '';
|
||||
try { payload = JSON.stringify(slim); } catch (e) { return null; }
|
||||
try {
|
||||
sessionStorage.setItem(key, JSON.stringify(j));
|
||||
return key;
|
||||
sessionStorage.setItem(key, payload);
|
||||
} catch (e) {
|
||||
return '';
|
||||
try {
|
||||
sessionStorage.setItem(key, 'idb');
|
||||
} catch (e2) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return { key: key, slim: slim, displayName: displayName, normalized: normalized };
|
||||
}
|
||||
|
||||
function soonPutSoonSession(j, fileName) {
|
||||
var prep = soonPrepareSoonSessionWrite(j, fileName);
|
||||
if (!prep) return '';
|
||||
if (typeof window.soonLocalCacheAndThumb === 'function') {
|
||||
window.soonLocalCacheAndThumb(prep.key, prep.slim, { source: 'session', name: prep.displayName });
|
||||
}
|
||||
return prep.key;
|
||||
}
|
||||
|
||||
function soonOpenSoonJsonLocally(j, fileName) {
|
||||
var key = soonPutSoonSession(j, fileName);
|
||||
if (!key) {
|
||||
soonToast('无法打开文件(存储空间不足)', 'error');
|
||||
var prep = soonPrepareSoonSessionWrite(j, fileName);
|
||||
if (!prep) {
|
||||
soonToast('无法打开文件(格式无效或存储空间不足)', 'error');
|
||||
return Promise.resolve('');
|
||||
}
|
||||
var cacheP = typeof window.soonLocalCacheAndThumb === 'function'
|
||||
? window.soonLocalCacheAndThumb(prep.key, prep.slim, { source: 'session', name: prep.displayName })
|
||||
: Promise.resolve(true);
|
||||
return cacheP.then(function () {
|
||||
if (typeof window.soonRecentUpsertFromOpen === 'function') {
|
||||
window.soonRecentUpsertFromOpen(prep.key, prep.normalized);
|
||||
}
|
||||
soonScheduleCloudImport(prep.key, fileName);
|
||||
var type = soonSoonTypeFromJson(prep.normalized);
|
||||
if (window.platformBridge && window.platformBridge.openDesignPage) {
|
||||
window.platformBridge.openDesignPage(prep.key, type);
|
||||
}
|
||||
return prep.key;
|
||||
}).catch(function () {
|
||||
soonToast('无法打开文件(本地缓存写入失败)', 'error');
|
||||
return '';
|
||||
}
|
||||
if (typeof window.soonRecentUpsertFromOpen === 'function') {
|
||||
window.soonRecentUpsertFromOpen(key, j);
|
||||
}
|
||||
soonScheduleCloudImport(key, fileName);
|
||||
var type = soonSoonTypeFromJson(j);
|
||||
if (window.platformBridge && window.platformBridge.openDesignPage) {
|
||||
window.platformBridge.openDesignPage(key, type);
|
||||
}
|
||||
return key;
|
||||
});
|
||||
}
|
||||
|
||||
var PENDING_IMPORT_KEY = 'soondesign_pending_import';
|
||||
@@ -944,6 +1070,8 @@
|
||||
window.soonParseTemplateKey = soonParseTemplateKey;
|
||||
window.soonMakeTemplateKey = soonMakeTemplateKey;
|
||||
window.soonSoonTypeFromJson = soonSoonTypeFromJson;
|
||||
window.soonNormalizeSoonJson = soonNormalizeSoonJson;
|
||||
window.soonRunWhenDesignBgReady = soonRunWhenDesignBgReady;
|
||||
window.soonLoadMembership = soonLoadMembership;
|
||||
window.soonApplyMembership = soonApplyMembership;
|
||||
window.soonRefreshPortalIdentity = soonRefreshPortalIdentity;
|
||||
@@ -953,7 +1081,12 @@
|
||||
window.soonShowApiError = soonShowApiError;
|
||||
window.soonDisplayFileName = soonDisplayFileName;
|
||||
window.soonIsTemplateKey = soonIsTemplateKey;
|
||||
window.soonIsCloudFileKey = soonIsCloudFileKey;
|
||||
window.soonNeedsSaveDialog = soonNeedsSaveDialog;
|
||||
window.soonOnOpenDesignFile = soonOnOpenDesignFile;
|
||||
window.soonMarkSaveInPlace = soonMarkSaveInPlace;
|
||||
window.soonShouldConfirmBeforeLeave = soonShouldConfirmBeforeLeave;
|
||||
window.soonConfirmLeaveThen = soonConfirmLeaveThen;
|
||||
window.soonDefaultNewSoonName = soonDefaultNewSoonName;
|
||||
window.soonEnsureSoonExt = soonEnsureSoonExt;
|
||||
window.soonSessionSlug = soonSessionSlug;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
var v = window.SOON_APP_VERSION || '';
|
||||
|
||||
window.soonWithVersion = function (url) {
|
||||
if (!url || /^https?:\/\//i.test(url) || /^data:/i.test(url)) return url;
|
||||
if (!v) return url;
|
||||
var s = String(url);
|
||||
var hashIdx = s.indexOf('#');
|
||||
var hash = hashIdx >= 0 ? s.slice(hashIdx) : '';
|
||||
var noHash = hashIdx >= 0 ? s.slice(0, hashIdx) : s;
|
||||
return noHash.split('?')[0] + '?v=' + encodeURIComponent(v) + hash;
|
||||
};
|
||||
|
||||
window.soonPatchForward = function (fromEl) {
|
||||
var fn = window.soonWithVersion;
|
||||
if (typeof fn !== 'function') return;
|
||||
var el = fromEl && fromEl.nextElementSibling;
|
||||
while (el) {
|
||||
if (el.tagName === 'SCRIPT') {
|
||||
var src = el.getAttribute('src');
|
||||
if (src && src.indexOf('version.js') < 0 && src.indexOf('soon-cache.js') < 0) {
|
||||
el.src = fn(src);
|
||||
}
|
||||
} else if (el.tagName === 'LINK' && el.rel === 'stylesheet') {
|
||||
var href = el.getAttribute('href');
|
||||
if (href) el.href = fn(href);
|
||||
}
|
||||
el = el.nextElementSibling;
|
||||
}
|
||||
};
|
||||
|
||||
if (v) {
|
||||
var KEY = 'soondesign_app_version';
|
||||
try {
|
||||
var prev = localStorage.getItem(KEY);
|
||||
if (prev && prev !== v) {
|
||||
localStorage.setItem(KEY, v);
|
||||
var url = new URL(window.location.href);
|
||||
if (url.searchParams.get('_sv') !== v) {
|
||||
url.searchParams.set('_sv', v);
|
||||
window.location.replace(url.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
localStorage.setItem(KEY, v);
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
if (document.currentScript) {
|
||||
window.soonPatchForward(document.currentScript);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user