Web 端本地缓存、首页与会员弹窗体验优化
新增 IndexedDB 与最近文件 L1 缓存、云保存/打开链路;首页支持下载与清空本地缓存;修复清空 storage 后语言初始化报错;优化 design2 背景加载与侧栏标签样式;完善会员激活/支付弹层样式;后端补充文件缩略图字段与 thumb 接口。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -150,6 +150,7 @@ function applySysLan(data) {
|
||||
localStorage.setItem("lang", "zh");
|
||||
break;
|
||||
case 'zh-TW':
|
||||
case 'ozh':
|
||||
s_lan = "ozh";
|
||||
// 同步到全局作用域(用于 .jsc 文件加载)
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -375,64 +376,73 @@ window.rotate = function (id) {
|
||||
$(`#${id}`).attr("angle", angle);
|
||||
}
|
||||
|
||||
function soonPlaceBgOnCanvas(image, canvasSelector) {
|
||||
var cw = $(canvasSelector).width() || (document.querySelector(canvasSelector) && document.querySelector(canvasSelector).clientWidth) || 800;
|
||||
var ch = $(canvasSelector).height() || (document.querySelector(canvasSelector) && document.querySelector(canvasSelector).clientHeight) || 600;
|
||||
image.left = (cw - image.width * image.get("scaleX")) / 2;
|
||||
image.top = (ch - image.height * image.get("scaleY")) / 2;
|
||||
image.selectable = false;
|
||||
image.hoverable = false;
|
||||
image.hoverCursor = "default";
|
||||
}
|
||||
|
||||
function soonOpenPendingDesignFile() {
|
||||
var file = typeof window.soonResolveOpenFileKey === 'function'
|
||||
? window.soonResolveOpenFileKey(GetFile)
|
||||
: (GetFile().get('file') || '');
|
||||
if (file && file != 'empty') {
|
||||
if (typeof window.soonConsumeOpenMeta === 'function') {
|
||||
window.soonConsumeOpenMeta(file);
|
||||
}
|
||||
window.openFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
function addBackground() {
|
||||
// 类型改变
|
||||
fabric.Image.fromURL(soonAsset('bg_front_1.png'), function (image) {
|
||||
let width = $("#canvas1").width()
|
||||
if (is_bgi_add || window.is_bgi_add) return;
|
||||
var loadBg = typeof window.soonFabricImageFromAsset === 'function'
|
||||
? window.soonFabricImageFromAsset
|
||||
: null;
|
||||
var whenReady = typeof window.soonRunWhenCanvasReady === 'function'
|
||||
? window.soonRunWhenCanvasReady
|
||||
: function (_sel, fn) { fn(); };
|
||||
|
||||
image.left = ($("#canvas1").width() - image.width * image.get("scaleX")) / 2;
|
||||
image.top = ($("#canvas1").height() - image.height * image.get("scaleY")) / 2;
|
||||
function finishBack(backImg) {
|
||||
if (backImg) {
|
||||
soonPlaceBgOnCanvas(backImg, '#canvas2');
|
||||
background_image2 = backImg;
|
||||
window.background_image2 = backImg;
|
||||
canvas2.add(backImg);
|
||||
canvas2.renderAll();
|
||||
recordObjs2.push(JSON.stringify(objs2));
|
||||
recordJson2.push(canvas2.toJSON(TO_JSON_PROPERTIES));
|
||||
}
|
||||
is_bgi_add = true;
|
||||
window.is_bgi_add = true;
|
||||
soonOpenPendingDesignFile();
|
||||
}
|
||||
|
||||
image.selectable = false;
|
||||
image.hoverable = false;
|
||||
image.hoverCursor = "default"
|
||||
image.setSrc(image.toDataURL(), function (image) {
|
||||
image.scale(1, 1);
|
||||
background_image1 = image;
|
||||
background_image = background_image1;
|
||||
window.background_image1 = background_image1;
|
||||
window.background_image = background_image;
|
||||
canvas.add(image);
|
||||
function startLoad() {
|
||||
if (!loadBg) return;
|
||||
loadBg('bg_front_1.png', function (frontImg) {
|
||||
if (!frontImg) return;
|
||||
frontImg.scale(1, 1);
|
||||
soonPlaceBgOnCanvas(frontImg, '#canvas1');
|
||||
background_image1 = frontImg;
|
||||
background_image = frontImg;
|
||||
window.background_image1 = frontImg;
|
||||
window.background_image = frontImg;
|
||||
canvas.add(frontImg);
|
||||
canvas.renderAll();
|
||||
//pre_objs1.push(JSON.stringify(objs1));
|
||||
//pre_json1.push(canvas1.toJSON(["selectable","hoverable","hoverCursor"]));
|
||||
|
||||
recordObjs1.push(JSON.stringify(objs1));
|
||||
recordJson1.push(canvas1.toJSON(TO_JSON_PROPERTIES));
|
||||
loadBg('bg_back.png', finishBack, function () { loadBg('bg_back.png', finishBack); });
|
||||
}, function () {
|
||||
setTimeout(startLoad, 200);
|
||||
});
|
||||
// 类型改变
|
||||
fabric.Image.fromURL(soonAsset('bg_back.png'), function (image) {
|
||||
image.left = ($("#canvas2").width() - image.width) / 2;
|
||||
image.top = ($("#canvas2").height() - image.height) / 2;
|
||||
}
|
||||
|
||||
image.selectable = false;
|
||||
image.hoverable = false;
|
||||
image.hoverCursor = "default"
|
||||
|
||||
image.setSrc(image.toDataURL(), function (image) {
|
||||
background_image2 = image;
|
||||
window.background_image2 = background_image2;
|
||||
canvas2.add(image);
|
||||
canvas2.renderAll();
|
||||
//pre_objs2.push(JSON.stringify(objs2));
|
||||
//pre_json2.push(canvas2.toJSON(["selectable","hoverable","hoverCursor"]));
|
||||
|
||||
recordObjs2.push(JSON.stringify(objs2));
|
||||
recordJson2.push(canvas2.toJSON(TO_JSON_PROPERTIES));
|
||||
|
||||
var file = typeof window.soonResolveOpenFileKey === 'function'
|
||||
? window.soonResolveOpenFileKey(GetFile)
|
||||
: (GetFile().get('file') || '');
|
||||
if (file && file != 'empty') {
|
||||
if (typeof window.soonConsumeOpenMeta === 'function') {
|
||||
window.soonConsumeOpenMeta(file);
|
||||
}
|
||||
window.openFile(file);
|
||||
}
|
||||
});
|
||||
});
|
||||
is_bgi_add = true;
|
||||
});
|
||||
whenReady('#canvas1', startLoad);
|
||||
}
|
||||
|
||||
// 将 updateControls 附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
|
||||
|
||||
@@ -1181,6 +1181,7 @@ window.openFile = function open(file, jAlready) {
|
||||
recordJson2.push(j2);
|
||||
}, 0);
|
||||
if (typeof window.soonTryConsumeCloudImport === 'function') window.soonTryConsumeCloudImport(file);
|
||||
if (typeof window.soonRecentUpsertFromOpen === 'function') window.soonRecentUpsertFromOpen(file, j);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1263,11 +1264,23 @@ function cloudSaveSuccessLabel(res, fallback) {
|
||||
return fallback || '';
|
||||
}
|
||||
|
||||
function soonPageDesignType() {
|
||||
try {
|
||||
var p = new URLSearchParams(location.search);
|
||||
return Number(p.get('type')) || 1;
|
||||
} catch (e) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
function onCloudWriteDone(res, fallback, callback) {
|
||||
applyCloudWriteResult(res, fallback);
|
||||
if (res && res.fileKey && typeof window.soonClearPendingImport === 'function') {
|
||||
window.soonClearPendingImport();
|
||||
}
|
||||
if (typeof window.soonRecentOnCloudSave === 'function') {
|
||||
window.soonRecentOnCloudSave(res, fallback, soonPageDesignType());
|
||||
}
|
||||
layer.msg(language_str("saveSucc") + cloudSaveSuccessLabel(res, fallback));
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
@@ -1276,15 +1289,6 @@ function guardWebSave() {
|
||||
return typeof window.soonGuardCloudSave === 'function' ? window.soonGuardCloudSave() : true;
|
||||
}
|
||||
|
||||
function reportSaveError(err) {
|
||||
if (err && err.status) return;
|
||||
if (typeof window.soonShowApiError === 'function') {
|
||||
window.soonShowApiError({ status: err && err.status, message: (err && err.message) || '保存失败' });
|
||||
} else if (typeof window.soonToast === 'function') {
|
||||
window.soonToast('保存失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function saveAs(op1, callback) {
|
||||
// 临时移除辅助线,保存后再恢复
|
||||
let guideLines1 = [];
|
||||
@@ -1319,26 +1323,26 @@ function saveAs(op1, callback) {
|
||||
let o = { f: j, b: {}, fo: objs1, bo: objs2 };
|
||||
let con_o = Object.assign(o, op1);
|
||||
if (!guardWebSave()) return;
|
||||
var prevOpenKey = openAs.name || '';
|
||||
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
|
||||
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;
|
||||
dialogApi.showSaveDialog({
|
||||
title: language_str("saveFile"),
|
||||
filters: [{ name: 'Soon File Type', extensions: ['soon'] }],
|
||||
defaultPath: openAs.name || undefined
|
||||
defaultPath: typeof window.soonSaveDialogDefaultPath === 'function' ? window.soonSaveDialogDefaultPath(openAs.name) : openAs.name
|
||||
}).then(function(result) {
|
||||
if (result.canceled) return;
|
||||
var fp = result.filePath || (result.fileHandle && result.fileHandle.name);
|
||||
var fp = typeof window.soonEnsureSaveFileName === 'function'
|
||||
? window.soonEnsureSaveFileName(result.filePath || (result.fileHandle && result.fileHandle.name))
|
||||
: (result.filePath || (result.fileHandle && result.fileHandle.name));
|
||||
if (!fp) return;
|
||||
var pathExt = (typeof window !== 'undefined' && window.path && window.path.extname) ? window.path.extname(fp) : (fp.indexOf('.') >= 0 ? fp.slice(fp.lastIndexOf('.')) : '');
|
||||
if (pathExt !== '.soon') fp = (fp || 'design').replace(/\.soon$/i, '') + '.soon';
|
||||
var content = JSON.stringify(con_o);
|
||||
con_o.soonType = 1;
|
||||
if (window.platformBridge && window.platformBridge.writeFile) {
|
||||
window.platformBridge.writeFile(fp, content).then(function (res) {
|
||||
onCloudWriteDone(res, fp, callback);
|
||||
}).catch(reportSaveError);
|
||||
return;
|
||||
}
|
||||
if (typeof window.soonWriteSoonContentWeb === 'function' &&
|
||||
window.soonWriteSoonContentWeb(fp, content, prevOpenKey, function (res, pk) {
|
||||
onCloudWriteDone(res, pk, callback);
|
||||
})) return;
|
||||
var fs = typeof require !== 'undefined' ? require('fs') : null;
|
||||
if (fs) {
|
||||
fs.writeFileSync(result.filePath || fp, content, 'utf8');
|
||||
@@ -1347,7 +1351,9 @@ function saveAs(op1, callback) {
|
||||
saveHistory();
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
}).catch(reportSaveError);
|
||||
}).catch(function (err) {
|
||||
if (typeof window.soonReportSaveError === 'function') window.soonReportSaveError(err);
|
||||
});
|
||||
}
|
||||
|
||||
function save(op1, callback) {
|
||||
@@ -1384,15 +1390,18 @@ function save(op1, callback) {
|
||||
let o = { f: j, b: {}, fo: objs1, bo: objs2 };
|
||||
let con_o = Object.assign(o, op1);
|
||||
if (!guardWebSave()) return;
|
||||
if (openAs.name != "") {
|
||||
con_o.soonType = 1;
|
||||
var content = JSON.stringify(con_o);
|
||||
if (window.platformBridge && window.platformBridge.writeFile) {
|
||||
window.platformBridge.writeFile(openAs.name, content).then(function (res) {
|
||||
onCloudWriteDone(res, openAs.name, callback);
|
||||
}).catch(reportSaveError);
|
||||
return;
|
||||
}
|
||||
var prevOpenKey = openAs.name || '';
|
||||
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
|
||||
con_o.soonType = 1;
|
||||
var content = JSON.stringify(con_o);
|
||||
var needsDialog = typeof window.soonNeedsSaveDialog === 'function'
|
||||
? window.soonNeedsSaveDialog(openAs.name)
|
||||
: !openAs.name;
|
||||
if (openAs.name && !needsDialog) {
|
||||
if (typeof window.soonWriteSoonContentWeb === 'function' &&
|
||||
window.soonWriteSoonContentWeb(openAs.name, content, prevOpenKey, function (res, pk) {
|
||||
onCloudWriteDone(res, pk, callback);
|
||||
})) return;
|
||||
var fs = typeof require !== 'undefined' ? require('fs') : null;
|
||||
if (fs) {
|
||||
fs.writeFileSync(openAs.name, content, 'utf8');
|
||||
@@ -1407,21 +1416,17 @@ function save(op1, callback) {
|
||||
dialogApi.showSaveDialog({
|
||||
title: language_str("saveFile"),
|
||||
filters: [{ name: 'Soon File Type', extensions: ['soon'] }],
|
||||
defaultPath: openAs.name || undefined
|
||||
}).then(async function(result) {
|
||||
defaultPath: typeof window.soonSaveDialogDefaultPath === 'function' ? window.soonSaveDialogDefaultPath(openAs.name) : openAs.name
|
||||
}).then(function(result) {
|
||||
if (result.canceled) return;
|
||||
var fp = result.filePath || (result.fileHandle && result.fileHandle.name);
|
||||
var fp = typeof window.soonEnsureSaveFileName === 'function'
|
||||
? window.soonEnsureSaveFileName(result.filePath || (result.fileHandle && result.fileHandle.name))
|
||||
: (result.filePath || (result.fileHandle && result.fileHandle.name));
|
||||
if (!fp) return;
|
||||
var pathExt = (typeof window !== 'undefined' && window.path && window.path.extname) ? window.path.extname(fp) : (fp.indexOf('.') >= 0 ? fp.slice(fp.lastIndexOf('.')) : '');
|
||||
if (pathExt !== '.soon') fp = (fp || 'design').replace(/\.soon$/i, '') + '.soon';
|
||||
var content = JSON.stringify(con_o);
|
||||
con_o.soonType = 1;
|
||||
if (window.platformBridge && window.platformBridge.writeFile) {
|
||||
window.platformBridge.writeFile(fp, content).then(function (res) {
|
||||
onCloudWriteDone(res, fp, callback);
|
||||
}).catch(reportSaveError);
|
||||
return;
|
||||
}
|
||||
if (typeof window.soonWriteSoonContentWeb === 'function' &&
|
||||
window.soonWriteSoonContentWeb(fp, content, prevOpenKey, function (res, pk) {
|
||||
onCloudWriteDone(res, pk, callback);
|
||||
})) return;
|
||||
var fs = typeof require !== 'undefined' ? require('fs') : null;
|
||||
if (fs) {
|
||||
fs.writeFileSync(result.filePath || fp, content, 'utf8');
|
||||
@@ -1430,12 +1435,27 @@ function save(op1, callback) {
|
||||
saveHistory();
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
}
|
||||
}).catch(reportSaveError);
|
||||
}).catch(function (err) {
|
||||
if (typeof window.soonReportSaveError === 'function') window.soonReportSaveError(err);
|
||||
});
|
||||
}
|
||||
|
||||
window.saveHistory = function saveHistory() {
|
||||
var currentPath = openAs.name;
|
||||
if (!currentPath) return;
|
||||
if (window.platformBridge && window.fs == null) {
|
||||
if (typeof window.soonRecentUpsert === 'function') {
|
||||
window.soonRecentUpsert({
|
||||
key: currentPath,
|
||||
name: typeof window.soonDisplayFileName === 'function' ? window.soonDisplayFileName(currentPath) : currentPath,
|
||||
type: soonPageDesignType(),
|
||||
kind: typeof window.soonRecentKindFromKey === 'function' ? window.soonRecentKindFromKey(currentPath) : 'local',
|
||||
thumbRef: currentPath
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
function doWrite(j) {
|
||||
var currentPath = openAs.name;
|
||||
if (!currentPath) return;
|
||||
var pathForHistory = currentPath;
|
||||
if (window.platformBridge && currentPath.indexOf('soondesign_session:') !== 0 && currentPath.indexOf('soondesign_file:') !== 0) {
|
||||
|
||||
Reference in New Issue
Block a user