fix(web): 本地优先保存与 design 页体验修复
- 保存先写本地缓存,登录后可选云端同步;退出登录不再因文件操作跳转登录 - 修复 Layui 遮罩残留、design2 保存后线条、toast 被 finally 提前关闭 - 保存过程恢复 loading spinner;成功提示 2.5 秒 - 云端 payload 瘦身与体积超限提示;后端 schema 迁移与 FileService 容错 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -85,16 +85,21 @@
|
||||
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 () {
|
||||
clearSession();
|
||||
redirectToLogin();
|
||||
if (hadSession) {
|
||||
clearSession();
|
||||
redirectToLogin();
|
||||
}
|
||||
return response;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,22 +88,29 @@
|
||||
}
|
||||
});
|
||||
if (hidden.length) canvas.renderAll();
|
||||
window._soonSkipDesign1CdMask = true;
|
||||
var mult = 0.18;
|
||||
var quality = 0.72;
|
||||
var url = '';
|
||||
for (var i = 0; i < 3; i++) {
|
||||
try {
|
||||
var candidate = canvas.toDataURL({ format: 'jpeg', quality: quality, multiplier: mult });
|
||||
if (candidate && candidate.indexOf('data:image/') === 0 && candidate.length <= THUMB_MAX) {
|
||||
url = candidate;
|
||||
break;
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
quality -= 0.18;
|
||||
mult -= 0.04;
|
||||
try {
|
||||
for (var i = 0; i < 3; i++) {
|
||||
try {
|
||||
var candidate = canvas.toDataURL({ format: 'jpeg', quality: quality, multiplier: mult });
|
||||
if (candidate && candidate.indexOf('data:image/') === 0 && candidate.length <= THUMB_MAX) {
|
||||
url = candidate;
|
||||
break;
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
quality -= 0.18;
|
||||
mult -= 0.04;
|
||||
}
|
||||
} finally {
|
||||
window._soonSkipDesign1CdMask = false;
|
||||
}
|
||||
hidden.forEach(function (obj) { obj.visible = true; });
|
||||
if (hidden.length) canvas.renderAll();
|
||||
canvas.renderAll();
|
||||
if (typeof window.soonClearFabricOverlay === 'function') window.soonClearFabricOverlay(canvas);
|
||||
if (canvas.requestRenderAll) canvas.requestRenderAll();
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -134,8 +141,20 @@
|
||||
return soonEnsureSoonExt(name);
|
||||
}
|
||||
|
||||
function dismissLayerMask() {
|
||||
if (typeof window.soonCleanupLayerMask === 'function') {
|
||||
window.soonCleanupLayerMask();
|
||||
return;
|
||||
}
|
||||
soonHideOpenFileLoading();
|
||||
invokeLayerCleanup();
|
||||
}
|
||||
|
||||
function soonReportSaveError(err) {
|
||||
if (typeof window.soonFinishSaveUi === 'function') window.soonFinishSaveUi();
|
||||
else dismissLayerMask();
|
||||
if (err && err.conflictSynced) return;
|
||||
if (err && err.apiNotified) return;
|
||||
if (typeof window.soonShowApiError === 'function') {
|
||||
window.soonShowApiError({
|
||||
status: err && err.status,
|
||||
@@ -149,11 +168,27 @@
|
||||
|
||||
function soonWriteSoonContentWeb(fp, content, prevOpenKey, onDone, onError) {
|
||||
if (!window.platformBridge || !window.platformBridge.writeFile) return false;
|
||||
window.platformBridge.writeFile(fp, content).then(function (res) {
|
||||
if (typeof window.soonShowOpenFileLoading === 'function') window.soonShowOpenFileLoading();
|
||||
var writePromise;
|
||||
try {
|
||||
writePromise = window.platformBridge.writeFile(fp, content);
|
||||
} catch (syncErr) {
|
||||
dismissLayerMask();
|
||||
if (typeof onError === 'function') onError(syncErr);
|
||||
else soonReportSaveError(syncErr);
|
||||
return true;
|
||||
}
|
||||
if (!writePromise || typeof writePromise.then !== 'function') {
|
||||
dismissLayerMask();
|
||||
return true;
|
||||
}
|
||||
writePromise.then(function (res) {
|
||||
if (typeof onDone === 'function') onDone(res, prevOpenKey);
|
||||
}).catch(function (err) {
|
||||
if (typeof onError === 'function') onError(err);
|
||||
else soonReportSaveError(err);
|
||||
}).finally(function () {
|
||||
dismissLayerMask();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -656,37 +691,52 @@
|
||||
});
|
||||
}
|
||||
|
||||
function invokeLayerCleanup() {
|
||||
if (typeof window.soonCleanupLayerMask === 'function') {
|
||||
window.soonCleanupLayerMask();
|
||||
}
|
||||
}
|
||||
|
||||
function soonReportOpenLoadError(message) {
|
||||
soonHideOpenFileLoading();
|
||||
invokeLayerCleanup();
|
||||
soonToast(message || '文件加载失败', 'error');
|
||||
}
|
||||
|
||||
function soonShowOpenFileLoading() {
|
||||
if (typeof layer === 'undefined' || !layer.load) return;
|
||||
soonHideOpenFileLoading();
|
||||
window._soonOpenLoadIdx = layer.load(1, { shade: [0.12, '#000'] });
|
||||
window._soonOpenLoadIdx = layer.load(1, { shade: 0 });
|
||||
}
|
||||
|
||||
function soonHideOpenFileLoading() {
|
||||
if (window._soonOpenLoadIdx == null) return;
|
||||
if (typeof layer !== 'undefined' && layer.close) layer.close(window._soonOpenLoadIdx);
|
||||
if (typeof layer !== 'undefined' && layer.closeAll) {
|
||||
layer.closeAll('loading');
|
||||
}
|
||||
if (window._soonOpenLoadIdx != null && typeof layer !== 'undefined' && layer.close) {
|
||||
try { layer.close(window._soonOpenLoadIdx); } catch (e) { /* ignore */ }
|
||||
}
|
||||
window._soonOpenLoadIdx = null;
|
||||
invokeLayerCleanup();
|
||||
}
|
||||
|
||||
function soonToast(message, type) {
|
||||
if (typeof window.soonToast === 'function') window.soonToast(message, type);
|
||||
}
|
||||
|
||||
function soonRequireLogin(actionLabel) {
|
||||
function soonRequireLogin(actionLabel, options) {
|
||||
options = options || {};
|
||||
if (soonGetAccessToken()) return true;
|
||||
var label = actionLabel || '继续';
|
||||
soonToast('请先登录后再' + label, 'warn');
|
||||
if (typeof window.soonRedirectToLogin === 'function') {
|
||||
setTimeout(function () { window.soonRedirectToLogin(); }, 600);
|
||||
} else if (typeof window.redirectToLogin === 'function') {
|
||||
setTimeout(function () { window.redirectToLogin(); }, 600);
|
||||
} else {
|
||||
window.location.href = 'login.web.html';
|
||||
soonToast('请先登录后再' + (label || '操作'), 'warn');
|
||||
if (options.redirect) {
|
||||
if (typeof window.soonRedirectToLogin === 'function') {
|
||||
setTimeout(function () { window.soonRedirectToLogin(); }, 600);
|
||||
} else if (typeof window.redirectToLogin === 'function') {
|
||||
setTimeout(function () { window.redirectToLogin(); }, 600);
|
||||
} else {
|
||||
window.location.href = 'login.web.html';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -724,8 +774,8 @@
|
||||
else if (info.status === 409) kind = 'warn';
|
||||
else if (info.status === 413) kind = 'warn';
|
||||
soonToast(info.message, kind);
|
||||
if (info.status === 401) {
|
||||
soonRequireLogin('操作');
|
||||
if (info.status === 401 && info.requireLogin) {
|
||||
soonRequireLogin('操作', { redirect: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
if (idx != null) layer.close(idx);
|
||||
if (payIdx != null) layer.close(payIdx);
|
||||
}
|
||||
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
if (fireStay && opts && typeof opts.onStay === 'function') opts.onStay();
|
||||
}
|
||||
|
||||
@@ -93,6 +94,7 @@
|
||||
_gate.payIndex = null;
|
||||
if (typeof layer !== 'undefined' && gateIdx != null) layer.close(gateIdx);
|
||||
_gate = { index: null, payIndex: null, plan: null, gateOpts: null };
|
||||
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
toast('恭喜您,会员已激活!', 'success');
|
||||
if (typeof window.soonLoadMembership === 'function') {
|
||||
window.soonLoadMembership(true).then(function () {
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
if (idx != null && typeof layer !== 'undefined') {
|
||||
try { layer.close(idx); } catch (e) { /* ignore */ }
|
||||
}
|
||||
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
}
|
||||
|
||||
function bindModal(modal) {
|
||||
|
||||
@@ -607,6 +607,7 @@
|
||||
displayChannels: channels,
|
||||
onPaid: function () {
|
||||
layer.close(idx);
|
||||
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
if (typeof opts.onPaid === 'function') opts.onPaid();
|
||||
},
|
||||
onBack: opts.onBack,
|
||||
@@ -617,6 +618,7 @@
|
||||
},
|
||||
end: function () {
|
||||
if (payCtrl) payCtrl.destroy();
|
||||
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
if (typeof opts.onClose === 'function') opts.onClose();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
localStorage.removeItem('soon_access');
|
||||
localStorage.removeItem('soon_refresh');
|
||||
}
|
||||
if (typeof window.soonApplyMembership === 'function') {
|
||||
window.soonApplyMembership(null);
|
||||
}
|
||||
}
|
||||
|
||||
function initClearCache() {
|
||||
|
||||
@@ -1,14 +1,81 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function soonCleanupLayerMask() {
|
||||
if (typeof layer !== 'undefined' && layer.closeAll) {
|
||||
layer.closeAll('loading');
|
||||
}
|
||||
if (window._soonOpenLoadIdx != null && typeof layer !== 'undefined' && layer.close) {
|
||||
try { layer.close(window._soonOpenLoadIdx); } catch (e) { /* ignore */ }
|
||||
window._soonOpenLoadIdx = null;
|
||||
}
|
||||
if (typeof document === 'undefined') return;
|
||||
var layers = document.querySelectorAll('.layui-layer');
|
||||
for (var j = 0; j < layers.length; j++) {
|
||||
if (layers[j].getAttribute('type') !== 'loading') continue;
|
||||
var loadTimes = layers[j].getAttribute('times');
|
||||
if (loadTimes && typeof layer !== 'undefined' && layer.close) {
|
||||
try { layer.close(loadTimes); } catch (e2) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
var shades = document.querySelectorAll('.layui-layer-shade');
|
||||
for (var i = shades.length - 1; i >= 0; i--) {
|
||||
var shade = shades[i];
|
||||
var times = shade.getAttribute('times');
|
||||
var layerEl = times ? document.getElementById('layui-layer' + times) : null;
|
||||
if (!layerEl || layerEl.getAttribute('type') === 'loading') {
|
||||
if (shade.parentNode) shade.parentNode.removeChild(shade);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function soonLayerDismiss(type) {
|
||||
if (typeof layer !== 'undefined' && layer.closeAll) {
|
||||
layer.closeAll(type);
|
||||
}
|
||||
soonCleanupLayerMask();
|
||||
}
|
||||
|
||||
function soonClearFabricOverlay(canvas) {
|
||||
if (!canvas || !canvas.contextTop) return;
|
||||
if (typeof canvas.clearContext === 'function') {
|
||||
canvas.clearContext(canvas.contextTop);
|
||||
}
|
||||
}
|
||||
|
||||
function soonRefreshDesignCanvases() {
|
||||
var names = ['canvas1', 'canvas2', 'canvas'];
|
||||
for (var n = 0; n < names.length; n++) {
|
||||
var c = window[names[n]];
|
||||
if (!c || typeof c.renderAll !== 'function') continue;
|
||||
if (c.discardActiveObject) c.discardActiveObject();
|
||||
soonClearFabricOverlay(c);
|
||||
if (c.requestRenderAll) c.requestRenderAll();
|
||||
else c.renderAll();
|
||||
}
|
||||
}
|
||||
|
||||
function soonFinishSaveUi() {
|
||||
if (typeof window.soonHideOpenFileLoading === 'function') window.soonHideOpenFileLoading();
|
||||
else soonCleanupLayerMask();
|
||||
soonRefreshDesignCanvases();
|
||||
if (typeof requestAnimationFrame === 'function') {
|
||||
requestAnimationFrame(function () {
|
||||
soonCleanupLayerMask();
|
||||
soonRefreshDesignCanvases();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function soonToast(message, type) {
|
||||
if (typeof layer === 'undefined' || !layer.msg) return;
|
||||
var skin = 'soon-toast';
|
||||
if (type === 'success') skin += ' soon-toast--success';
|
||||
else if (type === 'error') skin += ' soon-toast--error';
|
||||
else if (type === 'warn') skin += ' soon-toast--warn';
|
||||
var time = type === 'success' ? 2500 : (type === 'error' ? 4200 : 3600);
|
||||
layer.msg(message, {
|
||||
time: 2400,
|
||||
time: time,
|
||||
shade: 0,
|
||||
offset: '72px',
|
||||
icon: -1,
|
||||
@@ -16,5 +83,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
window.soonCleanupLayerMask = soonCleanupLayerMask;
|
||||
window.soonLayerDismiss = soonLayerDismiss;
|
||||
window.soonClearFabricOverlay = soonClearFabricOverlay;
|
||||
window.soonRefreshDesignCanvases = soonRefreshDesignCanvases;
|
||||
window.soonFinishSaveUi = soonFinishSaveUi;
|
||||
window.soonToast = soonToast;
|
||||
})();
|
||||
|
||||
@@ -102,10 +102,10 @@ function applyFontList(data) {
|
||||
$("#count_font_family").append("<option value='" + item + "'>" + item + "</option>");
|
||||
}
|
||||
}
|
||||
if (typeof ipcRenderer !== 'undefined' && ipcRenderer) {
|
||||
ipcRenderer.on('font-list', function(event, data) { applyFontList(data); });
|
||||
} else if (typeof window !== 'undefined' && window.platformBridge && window.platformBridge.getSystemFonts) {
|
||||
if (typeof window !== 'undefined' && window.platformBridge && window.platformBridge.getSystemFonts) {
|
||||
window.platformBridge.getSystemFonts().then(applyFontList);
|
||||
} else if (typeof ipcRenderer !== 'undefined' && ipcRenderer) {
|
||||
ipcRenderer.on('font-list', function(event, data) { applyFontList(data); });
|
||||
}
|
||||
function applySysLan(data) {
|
||||
let lang = localStorage.getItem("lang");
|
||||
@@ -1816,7 +1816,7 @@ function initCanvasEvents() {
|
||||
const ctx = currentCanvas.getContext();
|
||||
let bgObj = (currentCanvas === canvas1) ? background_image1 : background_image2;
|
||||
|
||||
if (bgObj) {
|
||||
if (bgObj && !window._soonSkipDesign1CdMask) {
|
||||
const width = currentCanvas.width;
|
||||
const height = currentCanvas.height;
|
||||
|
||||
|
||||
@@ -1204,6 +1204,9 @@ window.openFile = function open(file, jAlready) {
|
||||
}).catch(function () {
|
||||
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
|
||||
else if (typeof window.soonHideOpenFileLoading === 'function') window.soonHideOpenFileLoading();
|
||||
}).finally(function () {
|
||||
if (typeof window.soonHideOpenFileLoading === 'function') window.soonHideOpenFileLoading();
|
||||
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
});
|
||||
}
|
||||
try {
|
||||
@@ -1277,6 +1280,8 @@ function soonPageDesignType() {
|
||||
}
|
||||
|
||||
function onCloudWriteDone(res, fallback, callback) {
|
||||
if (typeof window.soonFinishSaveUi === 'function') window.soonFinishSaveUi();
|
||||
else if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
applyCloudWriteResult(res, fallback);
|
||||
if (res && res.fileKey && typeof window.soonClearPendingImport === 'function') {
|
||||
window.soonClearPendingImport();
|
||||
@@ -1284,7 +1289,12 @@ function onCloudWriteDone(res, fallback, callback) {
|
||||
if (typeof window.soonRecentOnCloudSave === 'function') {
|
||||
window.soonRecentOnCloudSave(res, fallback, soonPageDesignType());
|
||||
}
|
||||
layer.msg(language_str("saveSucc") + cloudSaveSuccessLabel(res, fallback));
|
||||
var msg = language_str("saveSucc") + cloudSaveSuccessLabel(res, fallback);
|
||||
if (res && res.syncFailed) {
|
||||
msg = '已保存到本地,云端同步失败';
|
||||
}
|
||||
if (typeof window.soonToast === 'function') window.soonToast(msg, res && res.syncFailed ? 'warn' : 'success');
|
||||
else layer.msg(msg, { shade: 0, time: 2500 });
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
|
||||
@@ -1328,6 +1338,7 @@ function saveAs(op1, callback) {
|
||||
if (!guardWebSave()) return;
|
||||
var prevOpenKey = openAs.name || '';
|
||||
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
|
||||
if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
|
||||
con_o.soonType = 1;
|
||||
var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showSaveDialog ? { showSaveDialog: function(opts) { return window.platformBridge.showSaveDialog(opts); } } : null);
|
||||
if (!dialogApi) return;
|
||||
@@ -1395,6 +1406,7 @@ function save(op1, callback) {
|
||||
if (!guardWebSave()) return;
|
||||
var prevOpenKey = openAs.name || '';
|
||||
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
|
||||
if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
|
||||
con_o.soonType = 1;
|
||||
var content = JSON.stringify(con_o);
|
||||
var needsDialog = typeof window.soonNeedsSaveDialog === 'function'
|
||||
|
||||
@@ -552,10 +552,6 @@ function relayoutDesign2Canvas() {
|
||||
if (typeof window.soonDesign2ScaleBgSources === 'function') {
|
||||
window.soonDesign2ScaleBgSources();
|
||||
}
|
||||
if (window.is_bgi_add) {
|
||||
canvas1.requestRenderAll();
|
||||
canvas2.requestRenderAll();
|
||||
}
|
||||
}
|
||||
|
||||
function soonDesign2ScaleBgSources() {
|
||||
@@ -572,16 +568,6 @@ function soonDesign2ScaleBgSources() {
|
||||
}
|
||||
}
|
||||
|
||||
function soonDesign2BgOverlayMetrics(imgEl) {
|
||||
if (!imgEl || !imgEl.complete || imgEl.naturalWidth <= 0) return null;
|
||||
var z = zoom || window.zoom || 0.5;
|
||||
var w = parseFloat(imgEl.style.width);
|
||||
if (!w || w <= 0) w = imgEl.naturalWidth * z;
|
||||
var h = parseFloat(imgEl.style.height);
|
||||
if (!h || h <= 0) h = imgEl.naturalHeight * z;
|
||||
return { w: w, h: h };
|
||||
}
|
||||
|
||||
function soonDesign2PrepareBgSources(done) {
|
||||
var sf = document.getElementById('source_front');
|
||||
var sb = document.getElementById('source_back');
|
||||
@@ -634,7 +620,6 @@ function soonDesign2Kickoff() {
|
||||
|
||||
window.soonDesign2ScaleBgSources = soonDesign2ScaleBgSources;
|
||||
window.soonDesign2PrepareBgSources = soonDesign2PrepareBgSources;
|
||||
window.soonDesign2BgOverlayMetrics = soonDesign2BgOverlayMetrics;
|
||||
window.soonDesign2Kickoff = soonDesign2Kickoff;
|
||||
|
||||
initRulers();
|
||||
@@ -2037,10 +2022,10 @@ function applyFontList(data) {
|
||||
$('#count_font_family').append("<option value='" + item + "'>" + item + '</option>')
|
||||
}
|
||||
}
|
||||
if (typeof ipcRenderer !== 'undefined' && ipcRenderer) {
|
||||
ipcRenderer.on('font-list', function(event, data) { applyFontList(data); })
|
||||
} else if (typeof window !== 'undefined' && window.platformBridge && window.platformBridge.getSystemFonts) {
|
||||
if (typeof window !== 'undefined' && window.platformBridge && window.platformBridge.getSystemFonts) {
|
||||
window.platformBridge.getSystemFonts().then(applyFontList)
|
||||
} else if (typeof ipcRenderer !== 'undefined' && ipcRenderer) {
|
||||
ipcRenderer.on('font-list', function(event, data) { applyFontList(data); })
|
||||
}
|
||||
function applySysLan(data) {
|
||||
let lang = localStorage.getItem('lang')
|
||||
|
||||
@@ -1096,6 +1096,8 @@ function soonPageDesignType() {
|
||||
}
|
||||
|
||||
function onCloudWriteDone(res, fallback, callback) {
|
||||
if (typeof window.soonFinishSaveUi === 'function') window.soonFinishSaveUi();
|
||||
else if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
applyCloudWriteResult(res, fallback);
|
||||
if (res && res.fileKey && typeof window.soonClearPendingImport === 'function') {
|
||||
window.soonClearPendingImport();
|
||||
@@ -1103,7 +1105,12 @@ function onCloudWriteDone(res, fallback, callback) {
|
||||
if (typeof window.soonRecentOnCloudSave === 'function') {
|
||||
window.soonRecentOnCloudSave(res, fallback, soonPageDesignType());
|
||||
}
|
||||
layer.msg(language_str('saveSucc') + cloudSaveSuccessLabel(res, fallback));
|
||||
var msg = language_str('saveSucc') + cloudSaveSuccessLabel(res, fallback);
|
||||
if (res && res.syncFailed) {
|
||||
msg = '已保存到本地,云端同步失败';
|
||||
}
|
||||
if (typeof window.soonToast === 'function') window.soonToast(msg, res && res.syncFailed ? 'warn' : 'success');
|
||||
else layer.msg(msg, { shade: 0, time: 2500 });
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
|
||||
@@ -1147,6 +1154,7 @@ function saveAs(op1, callback) {
|
||||
if (!guardWebSave()) return;
|
||||
var prevOpenKey = openAs.name || '';
|
||||
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
|
||||
if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
|
||||
con_o.soonType = 2;
|
||||
var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showSaveDialog ? { showSaveDialog: function(opts) { return window.platformBridge.showSaveDialog(opts); } } : null);
|
||||
if (!dialogApi) return;
|
||||
@@ -1166,7 +1174,8 @@ function saveAs(op1, callback) {
|
||||
if (fs) {
|
||||
fs.writeFileSync(fp, content, 'utf8');
|
||||
openAs.name = fp;
|
||||
layer.msg(language_str('saveSucc') + openAs.name);
|
||||
if (typeof window.soonToast === 'function') window.soonToast(language_str('saveSucc') + openAs.name, 'success');
|
||||
else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 });
|
||||
saveHistory();
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
@@ -1212,6 +1221,7 @@ function save(op1, callback) {
|
||||
if (!guardWebSave()) return;
|
||||
var prevOpenKey = openAs.name || '';
|
||||
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
|
||||
if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
|
||||
con_o.soonType = 2;
|
||||
var content = JSON.stringify(con_o);
|
||||
var needsDialog = typeof window.soonNeedsSaveDialog === 'function'
|
||||
@@ -1225,7 +1235,8 @@ function save(op1, callback) {
|
||||
var fs = typeof require !== 'undefined' ? require('fs') : null;
|
||||
if (fs) {
|
||||
fs.writeFileSync(openAs.name, content, 'utf8');
|
||||
layer.msg(language_str('saveSucc') + openAs.name);
|
||||
if (typeof window.soonToast === 'function') window.soonToast(language_str('saveSucc') + openAs.name, 'success');
|
||||
else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 });
|
||||
saveHistory();
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
@@ -1248,7 +1259,8 @@ function save(op1, callback) {
|
||||
if (fs) {
|
||||
fs.writeFileSync(fp, content, 'utf8');
|
||||
openAs.name = fp;
|
||||
layer.msg(language_str('saveSucc') + openAs.name);
|
||||
if (typeof window.soonToast === 'function') window.soonToast(language_str('saveSucc') + openAs.name, 'success');
|
||||
else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 });
|
||||
saveHistory();
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
@@ -1529,6 +1541,9 @@ window.openFile = function open(file, jAlready) {
|
||||
}).catch(function () {
|
||||
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
|
||||
else if (typeof window.soonHideOpenFileLoading === 'function') window.soonHideOpenFileLoading();
|
||||
}).finally(function () {
|
||||
if (typeof window.soonHideOpenFileLoading === 'function') window.soonHideOpenFileLoading();
|
||||
if (typeof window.soonCleanupLayerMask === 'function') window.soonCleanupLayerMask();
|
||||
});
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -365,8 +365,6 @@ $('#version_change').click(function () {
|
||||
if (typeof window.soonDesign2ScaleBgSources === 'function') {
|
||||
window.soonDesign2ScaleBgSources()
|
||||
}
|
||||
canvas1.requestRenderAll()
|
||||
canvas2.requestRenderAll()
|
||||
}
|
||||
sf.onerror = sf.onload
|
||||
sf.src = soonAsset('front_bg' + bg_version + '_2.png')
|
||||
@@ -2703,42 +2701,10 @@ if (typeof window._design2CanvasEventsInitialized === 'undefined') {
|
||||
// - mouse:up (已在 core.js 中处理)
|
||||
// 如果需要 canvas1 特定的处理逻辑,请修改 core.js 中的相应函数
|
||||
|
||||
// 保留 canvas1 特定的 after:render 处理
|
||||
canvas1.on('after:render', function () {
|
||||
if (!window.is_bgi_add) return
|
||||
var image = document.getElementById('source_front')
|
||||
var metrics = typeof window.soonDesign2BgOverlayMetrics === 'function'
|
||||
? window.soonDesign2BgOverlayMetrics(image)
|
||||
: null
|
||||
if (!metrics) return
|
||||
ctx1.drawImage(
|
||||
image,
|
||||
(canvas1.width - metrics.w) / 2,
|
||||
(canvas1.height - metrics.h) / 2,
|
||||
metrics.w,
|
||||
metrics.h
|
||||
)
|
||||
})
|
||||
// 保留 canvas1 特定的 text:changed 处理
|
||||
canvas1.on('text:changed', (e) => {
|
||||
$('#text_text').val(e.target.get('text'))
|
||||
})
|
||||
// 保留 canvas2 特定的 after:render 处理
|
||||
canvas2.on('after:render', function () {
|
||||
if (!window.is_bgi_add) return
|
||||
var image = document.getElementById('source_back')
|
||||
var metrics = typeof window.soonDesign2BgOverlayMetrics === 'function'
|
||||
? window.soonDesign2BgOverlayMetrics(image)
|
||||
: null
|
||||
if (!metrics) return
|
||||
ctx2.drawImage(
|
||||
image,
|
||||
(canvas2.width - metrics.w) / 2,
|
||||
(canvas2.height - metrics.h) / 2,
|
||||
metrics.w,
|
||||
metrics.h
|
||||
)
|
||||
})
|
||||
requestAnimationFrame(function () {
|
||||
requestAnimationFrame(function () {
|
||||
if (typeof window.soonDesign2Kickoff === 'function') {
|
||||
|
||||
+175
-57
@@ -47,6 +47,57 @@
|
||||
'Accept': 'application/json'
|
||||
};
|
||||
|
||||
var CLOUD_JSON_LIMIT = 16 * 1024 * 1024 - 65536;
|
||||
var CLOUD_STRIP_KEYS = [
|
||||
'frontColorPic', 'backColorPic', 'frontBlackPic', 'backBlackPic', 'backDisplayPic'
|
||||
];
|
||||
var CLOUD_REGEN_IMAGE_TYPES = { 2: 1, 8: 1, 9: 1 };
|
||||
|
||||
function slimCloudFabricSide(data, sideKey, metaKey) {
|
||||
var canvas = data[sideKey];
|
||||
var metaList = data[metaKey];
|
||||
if (!canvas || !Array.isArray(canvas.objects)) return;
|
||||
canvas.objects.forEach(function (obj, i) {
|
||||
if (!obj || typeof obj.src !== 'string') return;
|
||||
var meta = Array.isArray(metaList) && i > 0 ? metaList[i - 1] : null;
|
||||
var t = meta && meta.type;
|
||||
if (CLOUD_REGEN_IMAGE_TYPES[t]) delete obj.src;
|
||||
});
|
||||
}
|
||||
|
||||
function slimCloudPayload(data) {
|
||||
CLOUD_STRIP_KEYS.forEach(function (key) { delete data[key]; });
|
||||
var preview = data.frontDisplayPic;
|
||||
if (typeof preview === 'string' && preview.length > 120000) delete data.frontDisplayPic;
|
||||
slimCloudFabricSide(data, 'f', 'fo');
|
||||
slimCloudFabricSide(data, 'b', 'bo');
|
||||
return data;
|
||||
}
|
||||
|
||||
function formatPayloadTooLargeMessage(sizeBytes) {
|
||||
var mb = Math.max(0.1, Math.round(sizeBytes / 1024 / 1024 * 10) / 10);
|
||||
return '设计文件约 ' + mb + 'MB,超过云端上传限制。请减少或压缩自定义图片;'
|
||||
+ '若仍失败,请让管理员将 MySQL max_allowed_packet 调至 32M 以上。';
|
||||
}
|
||||
|
||||
function prepareCloudJsonStr(jsonStr) {
|
||||
if (typeof jsonStr !== 'string') jsonStr = String(jsonStr || '{}');
|
||||
if (jsonStr.length <= CLOUD_JSON_LIMIT) return jsonStr;
|
||||
var slim = jsonStr;
|
||||
try {
|
||||
var data = slimCloudPayload(JSON.parse(jsonStr));
|
||||
slim = JSON.stringify(data);
|
||||
if (slim.length <= CLOUD_JSON_LIMIT) return slim;
|
||||
delete data.frontDisplayPic;
|
||||
slim = JSON.stringify(data);
|
||||
if (slim.length <= CLOUD_JSON_LIMIT) return slim;
|
||||
} catch (e) { /* ignore */ }
|
||||
var err = new Error(formatPayloadTooLargeMessage(slim.length));
|
||||
err.code = 'payload_too_large';
|
||||
err.status = 413;
|
||||
throw err;
|
||||
}
|
||||
|
||||
function parseCloudRef(p) {
|
||||
return typeof window.soonParseFileKey === 'function' ? window.soonParseFileKey(p) : null;
|
||||
}
|
||||
@@ -83,13 +134,12 @@
|
||||
var err = new Error(info.message || 'cloud_error');
|
||||
err.status = info.status;
|
||||
err.code = info.code;
|
||||
err.apiNotified = true;
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
function requireCloudAuth(actionLabel) {
|
||||
if (getAccessToken()) return true;
|
||||
if (typeof window.soonRequireLogin === 'function') return window.soonRequireLogin(actionLabel);
|
||||
return false;
|
||||
return !!getAccessToken();
|
||||
}
|
||||
|
||||
function navigateToPage(pathWithQuery) {
|
||||
@@ -106,21 +156,41 @@
|
||||
return 'en';
|
||||
}
|
||||
|
||||
function getSystemFonts() {
|
||||
var list = [
|
||||
'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia',
|
||||
'Impact', 'Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi', 'FangSong',
|
||||
'Times New Roman', 'Trebuchet MS', 'Verdana', 'PingFang SC', 'Hiragino Sans GB'
|
||||
];
|
||||
var WEB_FONT_FALLBACK = [
|
||||
'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia',
|
||||
'Impact', 'Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi', 'FangSong',
|
||||
'Times New Roman', 'Trebuchet MS', 'Verdana', 'PingFang SC', 'Hiragino Sans GB',
|
||||
'Helvetica Neue', 'Segoe UI', 'Tahoma', 'STHeiti', 'STSong', 'STKaiti', 'STFangsong'
|
||||
];
|
||||
|
||||
function mergeFontNames() {
|
||||
var set = {};
|
||||
WEB_FONT_FALLBACK.forEach(function (name) { set[name] = 1; });
|
||||
if (typeof document !== 'undefined' && document.fonts && document.fonts.forEach) {
|
||||
var set = {};
|
||||
document.fonts.forEach(function (f) {
|
||||
var name = (f.family || '').replace(/^["']|["']$/g, '');
|
||||
if (name) set[name] = 1;
|
||||
});
|
||||
list = Object.keys(set).length ? Object.keys(set).sort() : list;
|
||||
}
|
||||
return Promise.resolve(list);
|
||||
return Object.keys(set).sort();
|
||||
}
|
||||
|
||||
function getSystemFonts() {
|
||||
if (typeof window !== 'undefined' && window.queryLocalFonts) {
|
||||
return window.queryLocalFonts().then(function (fonts) {
|
||||
var set = {};
|
||||
WEB_FONT_FALLBACK.forEach(function (name) { set[name] = 1; });
|
||||
(fonts || []).forEach(function (f) {
|
||||
var name = (f.fullName || f.family || '').replace(/^["']|["']$/g, '');
|
||||
if (name) set[name] = 1;
|
||||
});
|
||||
var merged = Object.keys(set).sort();
|
||||
return merged.length ? merged : mergeFontNames();
|
||||
}).catch(function () {
|
||||
return mergeFontNames();
|
||||
});
|
||||
}
|
||||
return Promise.resolve(mergeFontNames());
|
||||
}
|
||||
|
||||
function listCloudFiles(pageOrLimit, sizeOrOffset) {
|
||||
@@ -152,6 +222,11 @@
|
||||
|
||||
function createCloudFile(name, jsonStr) {
|
||||
if (!requireCloudAuth('保存')) return Promise.reject(new Error('unauthorized'));
|
||||
try {
|
||||
jsonStr = prepareCloudJsonStr(jsonStr);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
return authedFetch('files', {
|
||||
method: 'POST',
|
||||
headers: JSON_HEADERS,
|
||||
@@ -197,6 +272,11 @@
|
||||
|
||||
function updateCloudFile(id, name, jsonStr, version) {
|
||||
if (!requireCloudAuth('保存')) return Promise.reject(new Error('unauthorized'));
|
||||
try {
|
||||
jsonStr = prepareCloudJsonStr(jsonStr);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
var body = { name: normalizeSoonName(name), json: jsonStr };
|
||||
if (version != null) body.version = version;
|
||||
return authedFetch('files/' + id, {
|
||||
@@ -511,14 +591,23 @@
|
||||
var name = input && input.value ? String(input.value).trim() : defaultName;
|
||||
name = resolveSaveDialogName(name, options, isSoon);
|
||||
layer.close(index);
|
||||
if (typeof window.soonCleanupLayerMask === 'function') {
|
||||
setTimeout(function () { window.soonCleanupLayerMask(); }, 0);
|
||||
}
|
||||
resolve({ canceled: false, filePath: name, useCloud: !!isSoon });
|
||||
},
|
||||
btn2: function (index) {
|
||||
layer.close(index);
|
||||
if (typeof window.soonCleanupLayerMask === 'function') {
|
||||
setTimeout(function () { window.soonCleanupLayerMask(); }, 0);
|
||||
}
|
||||
resolve({ canceled: true });
|
||||
},
|
||||
cancel: function (index) {
|
||||
layer.close(index);
|
||||
if (typeof window.soonCleanupLayerMask === 'function') {
|
||||
setTimeout(function () { window.soonCleanupLayerMask(); }, 0);
|
||||
}
|
||||
resolve({ canceled: true });
|
||||
}
|
||||
});
|
||||
@@ -563,59 +652,75 @@
|
||||
return cacheWriteForKey(res.fileKey, meta).then(function () { return res; });
|
||||
}
|
||||
|
||||
if (!getAccessToken()) {
|
||||
if (cloudRef && cloudRef.id) {
|
||||
requireCloudAuth('保存');
|
||||
return Promise.reject(new Error('unauthorized'));
|
||||
function resolveLocalSaveKey() {
|
||||
if (name.indexOf('soondesign_session:') === 0) return name;
|
||||
if (name.indexOf('soondesign_file:') === 0) return name;
|
||||
if (typeof window.soonMakeSessionKey === 'function') {
|
||||
return window.soonMakeSessionKey(name);
|
||||
}
|
||||
var sessionKey = typeof window.soonMakeSessionKey === 'function'
|
||||
? window.soonMakeSessionKey(name)
|
||||
: (name.indexOf('soondesign_session:') === 0
|
||||
? name
|
||||
: 'soondesign_session:' + fileName.replace(/\.soon$/i, ''));
|
||||
var prevSessionKey = name.indexOf('soondesign_session:') === 0 ? name : '';
|
||||
function finishSessionSave() {
|
||||
try { sessionStorage.setItem(sessionKey, 'idb'); } catch (e) { /* ignore */ }
|
||||
return { fileKey: sessionKey, name: fileName, version: 0 };
|
||||
return 'soondesign_session:' + fileName.replace(/\.soon$/i, '');
|
||||
}
|
||||
|
||||
function localSaveMeta(localKey) {
|
||||
if (localKey.indexOf('soondesign_file:') === 0) {
|
||||
return { source: 'cloud', name: fileName, type: parsedJson ? parsedJson.soonType : 1 };
|
||||
}
|
||||
if (prevSessionKey && prevSessionKey !== sessionKey &&
|
||||
typeof window.soonLocalRenameKey === 'function') {
|
||||
return cacheWriteForKey(sessionKey, { source: 'session', name: fileName }).then(function () {
|
||||
return window.soonLocalRenameKey(prevSessionKey, sessionKey, { source: 'session', name: fileName })
|
||||
.then(finishSessionSave);
|
||||
}).catch(function () {
|
||||
return Promise.reject(new Error('save_failed'));
|
||||
return { source: 'session', name: fileName, type: parsedJson ? parsedJson.soonType : 1 };
|
||||
}
|
||||
|
||||
function finishLocalSave(localKey) {
|
||||
if (localKey.indexOf('soondesign_session:') === 0) {
|
||||
try { sessionStorage.setItem(localKey, 'idb'); } catch (e) { /* ignore */ }
|
||||
}
|
||||
return { fileKey: localKey, name: fileName, version: 0, localOnly: true };
|
||||
}
|
||||
|
||||
function saveLocalFirst() {
|
||||
var localKey = resolveLocalSaveKey();
|
||||
var prevKey = (name.indexOf('soondesign_session:') === 0 || name.indexOf('soondesign_file:') === 0) ? name : '';
|
||||
var meta = localSaveMeta(localKey);
|
||||
if (prevKey && prevKey !== localKey && typeof window.soonLocalRenameKey === 'function') {
|
||||
return cacheWriteForKey(localKey, meta).then(function () {
|
||||
return window.soonLocalRenameKey(prevKey, localKey, meta).then(function () {
|
||||
return finishLocalSave(localKey);
|
||||
});
|
||||
});
|
||||
}
|
||||
return cacheWriteForKey(sessionKey, { source: 'session', name: fileName }).then(function () {
|
||||
return finishSessionSave();
|
||||
return cacheWriteForKey(localKey, meta).then(function () {
|
||||
return finishLocalSave(localKey);
|
||||
});
|
||||
}
|
||||
|
||||
function syncCloudIfAuthed(localResult) {
|
||||
if (!getAccessToken()) return Promise.resolve(localResult);
|
||||
if (cloudRef && cloudRef.id) {
|
||||
var ver = cloudRef.version;
|
||||
if (ver == null && window._soonFileMeta && window._soonFileMeta.id === cloudRef.id) {
|
||||
ver = window._soonFileMeta.version;
|
||||
}
|
||||
var cloudSaveName = fileName;
|
||||
if (typeof window.soonResolveCloudFileName === 'function') {
|
||||
var resolved = window.soonResolveCloudFileName(name);
|
||||
if (resolved) cloudSaveName = resolved;
|
||||
}
|
||||
return updateCloudFile(cloudRef.id, cloudSaveName, str, ver).then(function (res) {
|
||||
return afterCloudSave(res, localResult.fileKey || name);
|
||||
}).catch(function () {
|
||||
localResult.syncFailed = true;
|
||||
return localResult;
|
||||
});
|
||||
}
|
||||
var prevKey = localResult.fileKey || name;
|
||||
return createCloudFile(fileName, str).then(function (res) {
|
||||
return afterCloudSave(res, prevKey);
|
||||
}).catch(function () {
|
||||
return Promise.reject(new Error('save_failed'));
|
||||
localResult.syncFailed = true;
|
||||
return localResult;
|
||||
});
|
||||
}
|
||||
|
||||
if (!requireCloudAuth('保存')) {
|
||||
return Promise.reject(new Error('unauthorized'));
|
||||
}
|
||||
|
||||
if (cloudRef && cloudRef.id) {
|
||||
var ver = cloudRef.version;
|
||||
if (ver == null && window._soonFileMeta && window._soonFileMeta.id === cloudRef.id) {
|
||||
ver = window._soonFileMeta.version;
|
||||
}
|
||||
if (typeof window.soonResolveCloudFileName === 'function') {
|
||||
var cloudSaveName = window.soonResolveCloudFileName(name);
|
||||
if (cloudSaveName) fileName = cloudSaveName;
|
||||
}
|
||||
var prevCloudKey = name;
|
||||
return updateCloudFile(cloudRef.id, fileName, str, ver).then(function (res) {
|
||||
return afterCloudSave(res, prevCloudKey);
|
||||
});
|
||||
}
|
||||
|
||||
var prevKey = name.indexOf('soondesign_session:') === 0 ? name : '';
|
||||
return createCloudFile(fileName, str).then(function (res) {
|
||||
return afterCloudSave(res, prevKey);
|
||||
return saveLocalFirst().then(syncCloudIfAuthed).catch(function () {
|
||||
return Promise.reject(new Error('save_failed'));
|
||||
});
|
||||
},
|
||||
readFile: function (pathOrHandle) {
|
||||
@@ -713,6 +818,11 @@
|
||||
window.ipcRenderer = {
|
||||
send: function (ch, a1, a2) {
|
||||
if (ch === 'get-sys-language') { window._sysLanPending = true; setTimeout(function () { if (window._sysLanCb && window._sysLanPending) { window._sysLanCb(null, loc); window._sysLanPending = false; } }, 0); }
|
||||
if (ch === 'get-sys-fonts' && bridge.getSystemFonts) {
|
||||
bridge.getSystemFonts().then(function (fonts) {
|
||||
if (typeof window._fontListCb === 'function') window._fontListCb(null, fonts || []);
|
||||
});
|
||||
}
|
||||
if (ch === 'open-first-page' && bridge.openFirstPage) { bridge.openFirstPage(); }
|
||||
if (ch === 'open-design-page' && bridge.openDesignPage && (a1 !== undefined || a2 !== undefined)) { bridge.openDesignPage(a1 || '', a2 || 1); }
|
||||
if (ch === 'open-help-file' && bridge.openHelp) { bridge.openHelp(); }
|
||||
@@ -720,6 +830,14 @@
|
||||
},
|
||||
on: function (ch, cb) {
|
||||
if (ch === 'sys-lan') { window._sysLanCb = cb; if (window._sysLanPending) setTimeout(function () { if (window._sysLanCb) { window._sysLanCb(null, loc); window._sysLanPending = false; } }, 0); }
|
||||
if (ch === 'font-list') {
|
||||
window._fontListCb = cb;
|
||||
if (bridge.getSystemFonts) {
|
||||
bridge.getSystemFonts().then(function (fonts) {
|
||||
if (typeof window._fontListCb === 'function') window._fontListCb(null, fonts || []);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (ch === 'close') window._closeCb = cb;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user