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:
24kycj
2026-06-29 00:30:20 +08:00
parent efeb3bcc41
commit ce76bb8018
27 changed files with 648 additions and 254 deletions
+1
View File
@@ -42,6 +42,7 @@ scripts\start.bat
- 数据库:在 `backend-web` 目录执行 `mysql ... < database/schema.sql`;已有库 `php scripts/migrate-schema.php` - 数据库:在 `backend-web` 目录执行 `mysql ... < database/schema.sql`;已有库 `php scripts/migrate-schema.php`
- 后端:`backend-web/config/local.php.example``local.php` - 后端:`backend-web/config/local.php.example``local.php`
- 前端 API`frontend-web/config/local.js`(生产配置,勿覆盖) - 前端 API`frontend-web/config/local.js`(生产配置,勿覆盖)
- Web 静态缓存:`scripts/write-web-version.ps1` 会随 `start.bat` 自动写入 `config/version.js`;线上 Nginx 请对齐 [docker/nginx/frontend.conf](docker/nginx/frontend.conf)HTML/JS 禁强缓存)
支付与 notify 说明见 [docs/PAYMENT.md](docs/PAYMENT.md)。列表分页契约见 [docs/API-PAGINATION.md](docs/API-PAGINATION.md)。 支付与 notify 说明见 [docs/PAYMENT.md](docs/PAYMENT.md)。列表分页契约见 [docs/API-PAGINATION.md](docs/API-PAGINATION.md)。
+11 -1
View File
@@ -9,8 +9,18 @@ server {
location = / { return 302 /pages/index.web.html; } location = / { return 302 /pages/index.web.html; }
location = /pages { return 302 /pages/index.web.html; } location = /pages { return 302 /pages/index.web.html; }
location = /pages/ { return 302 /pages/index.web.html; } location = /pages/ { return 302 /pages/index.web.html; }
location ~* \.(html|web\.html)$ {
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
try_files $uri =404;
}
location = /config/version.js {
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
try_files $uri =404;
}
location ~* \.(js|css)$ { location ~* \.(js|css)$ {
add_header Cache-Control "no-store, no-cache, must-revalidate"; add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
try_files $uri =404; try_files $uri =404;
} }
location / { try_files $uri $uri/ =404; } location / { try_files $uri $uri/ =404; }
+2
View File
@@ -70,3 +70,5 @@ $enc = [Text.UTF8Encoding]::new($false)
$webLocal = Join-Path $Root 'frontend-web\config\local.js' $webLocal = Join-Path $Root 'frontend-web\config\local.js'
New-Item -ItemType Directory -Force -Path (Split-Path $webLocal -Parent) | Out-Null New-Item -ItemType Directory -Force -Path (Split-Path $webLocal -Parent) | Out-Null
[IO.File]::WriteAllText($webLocal, $localJs.Trim() + "`n", $enc) [IO.File]::WriteAllText($webLocal, $localJs.Trim() + "`n", $enc)
& (Join-Path $Root 'scripts\write-web-version.ps1') | Out-Null
+91
View File
@@ -0,0 +1,91 @@
server
{
listen 80;
listen 443 ssl;
listen 443 quic;
http2 on;
server_name design.cardsoon.com;
index pages/index.web.html index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/design.cardsoon.com;
#CERT-APPLY-CHECK--START
# 用于SSL证书申请时的文件验证相关配置 -- 请勿删除
include /www/server/panel/vhost/nginx/well-known/design.cardsoon.com.conf;
#CERT-APPLY-CHECK--END
include /www/server/panel/vhost/nginx/extension/design.cardsoon.com/*.conf;
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
ssl_certificate /www/server/panel/vhost/cert/design.cardsoon.com/fullchain.pem;
ssl_certificate_key /www/server/panel/vhost/cert/design.cardsoon.com/privkey.pem;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_tickets on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000";
add_header Alt-Svc 'quic=":443"; h3=":443"; h3-29=":443"; h3-27=":443";h3-25=":443"; h3-T050=":443"; h3-Q050=":443";h3-Q049=":443";h3-Q048=":443"; h3-Q046=":443"; h3-Q043=":443"';
error_page 497 https://$host$request_uri;
#SSL-END
#ERROR-PAGE-START 错误页配置,可以注释、删除或修改
error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START PHP引用配置,可以注释或修改
include enable-php-00.conf;
#PHP-INFO-END
#REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
include /www/server/panel/vhost/rewrite/design.cardsoon.com.conf;
#REWRITE-END
rewrite ^/$ /pages/index.web.html redirect;
rewrite ^/pages/?$ /pages/index.web.html redirect;
location = /config/version.js {
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
}
location ~* \.html$ {
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
}
location ~* \.(js|css)$ {
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
}
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}
#禁止在证书验证目录放入敏感文件
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
return 403;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|webp|ico|svg)$
{
expires 30d;
error_log /dev/null;
access_log /dev/null;
}
access_log /www/wwwlogs/design.cardsoon.com.log;
error_log /www/wwwlogs/design.cardsoon.com.error.log;
}
+103 -37
View File
@@ -75,28 +75,7 @@ function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
tctx.restore(); tctx.restore();
} }
function soonDesign1ApplyDiscMatte(tctx, w, h, holeDiameterMm, color) { function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc) {
var snap = document.createElement('canvas');
snap.width = w;
snap.height = h;
snap.getContext('2d').drawImage(tctx.canvas, 0, 0);
tctx.fillStyle = color;
tctx.fillRect(0, 0, w, h);
tctx.save();
var cx = w / 2;
var cy = h / 2;
var rOuter = w / 2;
var rInner = rOuter * (holeDiameterMm / 120);
tctx.beginPath();
tctx.arc(cx, cy, rOuter, 0, Math.PI * 2);
tctx.moveTo(cx + rInner, cy);
tctx.arc(cx, cy, rInner, 0, Math.PI * 2, true);
tctx.clip('evenodd');
tctx.drawImage(snap, 0, 0);
tctx.restore();
}
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc, discMatte) {
var bg = window.background_image1 || window.background_image; var bg = window.background_image1 || window.background_image;
if (!canvas || !bg) return ''; if (!canvas || !bg) return '';
var srcEl = canvas.lowerCanvasEl; var srcEl = canvas.lowerCanvasEl;
@@ -117,9 +96,7 @@ function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipD
bg.left * retina, bg.top * retina, sw * retina, sh * retina, bg.left * retina, bg.top * retina, sw * retina, sh * retina,
0, 0, outW, outH 0, 0, outW, outH
); );
if (discMatte) { if (clipDisc && format !== 'jpeg') {
soonDesign1ApplyDiscMatte(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas), discMatte);
} else if (clipDisc && format !== 'jpeg') {
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas)); soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
} }
if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality); if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality);
@@ -128,13 +105,79 @@ function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipD
function soonDesign1CaptureDiscPng(canvas, outW, outH) { function soonDesign1CaptureDiscPng(canvas, outW, outH) {
window._soonSkipDesign1CdMask = true; window._soonSkipDesign1CdMask = true;
try {
if (canvas.discardActiveObject) canvas.discardActiveObject(); if (canvas.discardActiveObject) canvas.discardActiveObject();
canvas.renderAll(); canvas.renderAll();
var url = soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true); return soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
} finally {
window._soonSkipDesign1CdMask = false; window._soonSkipDesign1CdMask = false;
canvas.renderAll(); canvas.renderAll();
return url;
} }
}
function soonStripDesign1HeavyFields(con_o) {
if (!con_o) return con_o;
delete con_o.frontColorPic;
delete con_o.backColorPic;
delete con_o.frontBlackPic;
delete con_o.backBlackPic;
delete con_o.frontData;
delete con_o.backData;
delete con_o.backDisplayPic;
if (typeof con_o.frontDisplayPic === 'string' && con_o.frontDisplayPic.length > 120000) {
delete con_o.frontDisplayPic;
}
return con_o;
}
function soonNormalizeSoonJson(j) {
if (!j || typeof j !== 'object') return null;
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;
}
window.soonNormalizeSoonJson = soonNormalizeSoonJson;
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);
})();
}
window.soonRunWhenDesignBgReady = soonRunWhenDesignBgReady;
// 将 display_func 附加到 window 对象,确保全局可访问 // 将 display_func 附加到 window 对象,确保全局可访问
window.display_func = function display_func(img1, img2, img3) { window.display_func = function display_func(img1, img2, img3) {
@@ -1171,14 +1214,20 @@ window.regenerateQrCodesAndBarcodes = function regenerateQrCodesAndBarcodes(canv
window.openFile = function open(file, jAlready) { window.openFile = function open(file, jAlready) {
if (!file) return; if (!file) return;
function doOpenWithJson(j) { function doOpenWithJson(j) {
if (!j || !background_image) return; j = soonNormalizeSoonJson(j);
if (!j.f || !j.f.objects || !Array.isArray(j.f.objects) || j.f.objects.length === 0) return; if (!j) {
if (!j.b || !j.b.objects || !Array.isArray(j.b.objects) || j.b.objects.length === 0) { if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
j.b = { version: '4.6.0', objects: [], hoverCursor: 'move' }; return;
}
function runOpen() {
var bgRef = window.background_image1 || window.background_image;
if (!bgRef) {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
return;
} }
openAs.name = file; openAs.name = file;
var left = background_image.left; var left = bgRef.left;
var top = background_image.top; var top = bgRef.top;
var new_left = j.f.objects[0].left; var new_left = j.f.objects[0].left;
var new_top = j.f.objects[0].top; var new_top = j.f.objects[0].top;
for (var i = 0; i < j.f.objects.length; i++) { for (var i = 0; i < j.f.objects.length; i++) {
@@ -1250,6 +1299,10 @@ window.openFile = function open(file, jAlready) {
} }
}); });
} }
soonRunWhenDesignBgReady(runOpen, function () {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
});
}
if (arguments.length >= 2 && jAlready) { if (arguments.length >= 2 && jAlready) {
doOpenWithJson(jAlready); doOpenWithJson(jAlready);
openAs.name = file; openAs.name = file;
@@ -1257,12 +1310,22 @@ window.openFile = function open(file, jAlready) {
} }
file = resolveDiskPath(file); file = resolveDiskPath(file);
if (window.platformBridge && window.platformBridge.readJsonFile) { if (window.platformBridge && window.platformBridge.readJsonFile) {
if (typeof window.soonShowOpenFileLoading === 'function') window.soonShowOpenFileLoading();
return window.platformBridge.readJsonFile(file).then(function(j) { return window.platformBridge.readJsonFile(file).then(function(j) {
if (typeof window.soonHideOpenFileLoading === 'function') window.soonHideOpenFileLoading();
if (j) { if (j) {
doOpenWithJson(j); doOpenWithJson(j);
openAs.name = file; openAs.name = file;
} else if (typeof window.soonReportOpenLoadError === 'function') {
window.soonReportOpenLoadError('模板/文件加载失败');
} }
}).catch(function() {}); }).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 { try {
var fs = typeof require !== 'undefined' ? require('fs') : null; var fs = typeof require !== 'undefined' ? require('fs') : null;
@@ -1352,6 +1415,9 @@ function saveAs(op1, callback) {
let o = { f: j, b: {}, fo: objs1, bo: objs2 }; let o = { f: j, b: {}, fo: objs1, bo: objs2 };
let con_o = Object.assign(o, op1); let con_o = Object.assign(o, op1);
con_o.soonType = 1;
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
soonStripDesign1HeavyFields(con_o);
var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showSaveDialog ? { showSaveDialog: function(opts) { return window.platformBridge.showSaveDialog(opts); } } : null); var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showSaveDialog ? { showSaveDialog: function(opts) { return window.platformBridge.showSaveDialog(opts); } } : null);
if (!dialogApi) return; if (!dialogApi) return;
dialogApi.showSaveDialog({ dialogApi.showSaveDialog({
@@ -1364,7 +1430,6 @@ function saveAs(op1, callback) {
var pathExt = (typeof window !== 'undefined' && window.path && window.path.extname) ? window.path.extname(fp) : (fp.indexOf('.') >= 0 ? fp.slice(fp.lastIndexOf('.')) : ''); 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'; if (pathExt !== '.soon') fp = (fp || 'design').replace(/\.soon$/i, '') + '.soon';
var content = JSON.stringify(con_o); var content = JSON.stringify(con_o);
con_o.soonType = 1;
if (window.platformBridge && window.platformBridge.writeFile) { if (window.platformBridge && window.platformBridge.writeFile) {
await window.platformBridge.writeFile(result.fileHandle || fp, content); await window.platformBridge.writeFile(result.fileHandle || fp, content);
openAs.name = isWebPortal() ? ('soondesign_session:' + fp) : (result.filePath || fp); openAs.name = isWebPortal() ? ('soondesign_session:' + fp) : (result.filePath || fp);
@@ -1418,8 +1483,10 @@ function save(op1, callback) {
let o = { f: j, b: {}, fo: objs1, bo: objs2 }; let o = { f: j, b: {}, fo: objs1, bo: objs2 };
let con_o = Object.assign(o, op1); let con_o = Object.assign(o, op1);
if (openAs.name != "") {
con_o.soonType = 1; con_o.soonType = 1;
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
soonStripDesign1HeavyFields(con_o);
if (openAs.name != "") {
var content = JSON.stringify(con_o); var content = JSON.stringify(con_o);
if (isWebPortal() && openAs.name.indexOf('soondesign_session:') === 0) { if (isWebPortal() && openAs.name.indexOf('soondesign_session:') === 0) {
webSessionStore(content, openAs.name); webSessionStore(content, openAs.name);
@@ -1463,7 +1530,6 @@ function save(op1, callback) {
var pathExt = (typeof window !== 'undefined' && window.path && window.path.extname) ? window.path.extname(fp) : (fp.indexOf('.') >= 0 ? fp.slice(fp.lastIndexOf('.')) : ''); 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'; if (pathExt !== '.soon') fp = (fp || 'design').replace(/\.soon$/i, '') + '.soon';
var content = JSON.stringify(con_o); var content = JSON.stringify(con_o);
con_o.soonType = 1;
if (window.platformBridge && window.platformBridge.writeFile) { if (window.platformBridge && window.platformBridge.writeFile) {
await window.platformBridge.writeFile(result.fileHandle || fp, content); await window.platformBridge.writeFile(result.fileHandle || fp, content);
openAs.name = isWebPortal() ? ('soondesign_session:' + fp) : (result.filePath || fp); openAs.name = isWebPortal() ? ('soondesign_session:' + fp) : (result.filePath || fp);
File diff suppressed because one or more lines are too long
+4
View File
@@ -0,0 +1,4 @@
(function () {
'use strict';
window.SOON_APP_VERSION = 'efeb3bc';
})();
+2 -1
View File
@@ -36,7 +36,8 @@
window.soonJs = function (rel) { window.soonJs = function (rel) {
var n = String(rel || '').replace(/^\/+/, ''); 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,避免间歇性空白背景 */ /** 等 HTMLImage 解码完成后再 toDataURL / setSrc,避免间歇性空白背景 */
+152 -19
View File
@@ -3,6 +3,7 @@
var FILE_PREFIX = 'soondesign_file:'; var FILE_PREFIX = 'soondesign_file:';
var TEMPLATE_PREFIX = 'soondesign_template:'; var TEMPLATE_PREFIX = 'soondesign_template:';
var SESSION_PREFIX = 'soondesign_session:';
function soonParseTemplateKey(key) { function soonParseTemplateKey(key) {
if (!key || typeof key !== 'string') return null; if (!key || typeof key !== 'string') return null;
@@ -39,12 +40,60 @@
return FILE_PREFIX + id + ':v' + version; return FILE_PREFIX + id + ':v' + version;
} }
function soonIsCloudFileKey(key) {
return !!(key && typeof key === 'string' && key.indexOf(FILE_PREFIX) === 0);
}
function soonIsTemplateKey(key) { function soonIsTemplateKey(key) {
return !!(key && typeof key === 'string' && key.indexOf(TEMPLATE_PREFIX) === 0); return !!(key && typeof key === 'string' && key.indexOf(TEMPLATE_PREFIX) === 0);
} }
/** 打开模板 / 本地 .soon / session 导入:首次保存须另存为;云端文件或已选定保存目标后可原地保存 */
function soonNeedsSaveDialog(key) { 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() { function soonDefaultNewSoonName() {
@@ -623,38 +672,115 @@
return j && j.soonType ? j.soonType : (j && j.backBlackPic ? 2 : 1); return j && j.soonType ? j.soonType : (j && j.backBlackPic ? 2 : 1);
} }
function soonPutSoonSession(j, fileName) { /** 兼容旧版 .soon:补全 f/b/fo/bo,加载时去掉 output 大图字段 */
if (!j) return ''; 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 displayName = soonEnsureSoonExt(fileName || 'design.soon');
var key = soonMakeSessionKey(displayName); var key = soonMakeSessionKey(displayName);
if (typeof window.soonLocalCacheAndThumb === 'function') { var slim = Object.assign({}, normalized);
window.soonLocalCacheAndThumb(key, j, { source: 'session', name: displayName }); if (typeof window.soonStripDesign1HeavyFields === 'function') {
try { sessionStorage.setItem(key, 'idb'); } catch (e) { /* ignore */ } window.soonStripDesign1HeavyFields(slim);
return key;
} }
var payload = '';
try { payload = JSON.stringify(slim); } catch (e) { return null; }
try { try {
sessionStorage.setItem(key, JSON.stringify(j)); sessionStorage.setItem(key, payload);
return key;
} catch (e) { } 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) { function soonOpenSoonJsonLocally(j, fileName) {
var key = soonPutSoonSession(j, fileName); var prep = soonPrepareSoonSessionWrite(j, fileName);
if (!key) { if (!prep) {
soonToast('无法打开文件(存储空间不足)', 'error'); soonToast('无法打开文件(格式无效或存储空间不足)', 'error');
return ''; 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') { if (typeof window.soonRecentUpsertFromOpen === 'function') {
window.soonRecentUpsertFromOpen(key, j); window.soonRecentUpsertFromOpen(prep.key, prep.normalized);
} }
soonScheduleCloudImport(key, fileName); soonScheduleCloudImport(prep.key, fileName);
var type = soonSoonTypeFromJson(j); var type = soonSoonTypeFromJson(prep.normalized);
if (window.platformBridge && window.platformBridge.openDesignPage) { if (window.platformBridge && window.platformBridge.openDesignPage) {
window.platformBridge.openDesignPage(key, type); window.platformBridge.openDesignPage(prep.key, type);
} }
return key; return prep.key;
}).catch(function () {
soonToast('无法打开文件(本地缓存写入失败)', 'error');
return '';
});
} }
var PENDING_IMPORT_KEY = 'soondesign_pending_import'; var PENDING_IMPORT_KEY = 'soondesign_pending_import';
@@ -944,6 +1070,8 @@
window.soonParseTemplateKey = soonParseTemplateKey; window.soonParseTemplateKey = soonParseTemplateKey;
window.soonMakeTemplateKey = soonMakeTemplateKey; window.soonMakeTemplateKey = soonMakeTemplateKey;
window.soonSoonTypeFromJson = soonSoonTypeFromJson; window.soonSoonTypeFromJson = soonSoonTypeFromJson;
window.soonNormalizeSoonJson = soonNormalizeSoonJson;
window.soonRunWhenDesignBgReady = soonRunWhenDesignBgReady;
window.soonLoadMembership = soonLoadMembership; window.soonLoadMembership = soonLoadMembership;
window.soonApplyMembership = soonApplyMembership; window.soonApplyMembership = soonApplyMembership;
window.soonRefreshPortalIdentity = soonRefreshPortalIdentity; window.soonRefreshPortalIdentity = soonRefreshPortalIdentity;
@@ -953,7 +1081,12 @@
window.soonShowApiError = soonShowApiError; window.soonShowApiError = soonShowApiError;
window.soonDisplayFileName = soonDisplayFileName; window.soonDisplayFileName = soonDisplayFileName;
window.soonIsTemplateKey = soonIsTemplateKey; window.soonIsTemplateKey = soonIsTemplateKey;
window.soonIsCloudFileKey = soonIsCloudFileKey;
window.soonNeedsSaveDialog = soonNeedsSaveDialog; window.soonNeedsSaveDialog = soonNeedsSaveDialog;
window.soonOnOpenDesignFile = soonOnOpenDesignFile;
window.soonMarkSaveInPlace = soonMarkSaveInPlace;
window.soonShouldConfirmBeforeLeave = soonShouldConfirmBeforeLeave;
window.soonConfirmLeaveThen = soonConfirmLeaveThen;
window.soonDefaultNewSoonName = soonDefaultNewSoonName; window.soonDefaultNewSoonName = soonDefaultNewSoonName;
window.soonEnsureSoonExt = soonEnsureSoonExt; window.soonEnsureSoonExt = soonEnsureSoonExt;
window.soonSessionSlug = soonSessionSlug; window.soonSessionSlug = soonSessionSlug;
+53
View File
@@ -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);
}
})();
+1 -1
View File
@@ -502,7 +502,7 @@ if (ipcRenderer) {
// 网页端:按顺序动态插入 script,确保 core/ui 执行时 window.$ 已存在 // 网页端:按顺序动态插入 script,确保 core/ui 执行时 window.$ 已存在
function loadScript(src, next) { function loadScript(src, next) {
var s = document.createElement('script'); var s = document.createElement('script');
s.src = src; s.src = (typeof window.soonWithVersion === 'function') ? window.soonWithVersion(src) : src;
s.onload = s.onerror = function () { if (typeof next === 'function') next(); }; s.onload = s.onerror = function () { if (typeof next === 'function') next(); };
document.body.appendChild(s); document.body.appendChild(s);
} }
+7
View File
@@ -262,6 +262,13 @@ if (typeof ipcRenderer !== 'undefined' && ipcRenderer) {
applySysLan(window.platformBridge.getLocale()); applySysLan(window.platformBridge.getLocale());
} }
function onCloseConfirm() { function onCloseConfirm() {
if (typeof window.soonConfirmLeaveThen === 'function') {
window.soonConfirmLeaveThen(function () {
if (ipcRenderer) ipcRenderer.send('run-close');
else if (window.platformBridge && window.platformBridge.runClose) window.platformBridge.runClose();
});
return;
}
layer.confirm(language_str("whetherSave"), { layer.confirm(language_str("whetherSave"), {
btn: [language_str("save"), language_str("noSave"), language_str("cancel")] btn: [language_str("save"), language_str("noSave"), language_str("cancel")]
, btn3: function (index, layero) {} , btn3: function (index, layero) {}
+47 -36
View File
@@ -78,29 +78,7 @@ function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
tctx.restore(); tctx.restore();
} }
/** 圆外与中心孔铺画布底色,保留环形内容(缩略图用) */ function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc) {
function soonDesign1ApplyDiscMatte(tctx, w, h, holeDiameterMm, color) {
var snap = document.createElement('canvas');
snap.width = w;
snap.height = h;
snap.getContext('2d').drawImage(tctx.canvas, 0, 0);
tctx.fillStyle = color;
tctx.fillRect(0, 0, w, h);
tctx.save();
var cx = w / 2;
var cy = h / 2;
var rOuter = w / 2;
var rInner = rOuter * (holeDiameterMm / 120);
tctx.beginPath();
tctx.arc(cx, cy, rOuter, 0, Math.PI * 2);
tctx.moveTo(cx + rInner, cy);
tctx.arc(cx, cy, rInner, 0, Math.PI * 2, true);
tctx.clip('evenodd');
tctx.drawImage(snap, 0, 0);
tctx.restore();
}
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc, discMatte) {
var bg = window.background_image1 || window.background_image; var bg = window.background_image1 || window.background_image;
if (!canvas || !bg) return ''; if (!canvas || !bg) return '';
var srcEl = canvas.lowerCanvasEl; var srcEl = canvas.lowerCanvasEl;
@@ -121,9 +99,7 @@ function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipD
bg.left * retina, bg.top * retina, sw * retina, sh * retina, bg.left * retina, bg.top * retina, sw * retina, sh * retina,
0, 0, outW, outH 0, 0, outW, outH
); );
if (discMatte) { if (clipDisc && format !== 'jpeg') {
soonDesign1ApplyDiscMatte(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas), discMatte);
} else if (clipDisc && format !== 'jpeg') {
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas)); soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
} }
if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality); if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality);
@@ -133,13 +109,32 @@ function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipD
/** 导出/打印:跳过编辑器暗蒙版,按光盘环形裁剪 */ /** 导出/打印:跳过编辑器暗蒙版,按光盘环形裁剪 */
function soonDesign1CaptureDiscPng(canvas, outW, outH) { function soonDesign1CaptureDiscPng(canvas, outW, outH) {
window._soonSkipDesign1CdMask = true; window._soonSkipDesign1CdMask = true;
try {
if (canvas.discardActiveObject) canvas.discardActiveObject(); if (canvas.discardActiveObject) canvas.discardActiveObject();
canvas.renderAll(); canvas.renderAll();
var url = soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true); return soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
} finally {
window._soonSkipDesign1CdMask = false; window._soonSkipDesign1CdMask = false;
canvas.renderAll(); canvas.renderAll();
return url;
} }
}
/** 本地 .soon 不存 output 大图字段,避免文件过大导致缓存/加载失败 */
function soonStripDesign1HeavyFields(con_o) {
if (!con_o) return con_o;
delete con_o.frontColorPic;
delete con_o.backColorPic;
delete con_o.frontBlackPic;
delete con_o.backBlackPic;
delete con_o.frontData;
delete con_o.backData;
delete con_o.backDisplayPic;
if (typeof con_o.frontDisplayPic === 'string' && con_o.frontDisplayPic.length > 120000) {
delete con_o.frontDisplayPic;
}
return con_o;
}
window.soonStripDesign1HeavyFields = soonStripDesign1HeavyFields;
function soonBuildDesign1SavePreviewPic(canvas) { function soonBuildDesign1SavePreviewPic(canvas) {
var THUMB_MAX = 102400; var THUMB_MAX = 102400;
@@ -159,7 +154,7 @@ function soonBuildDesign1SavePreviewPic(canvas) {
var url = ''; var url = '';
var sizes = [280, 240, 200, 180, 160]; var sizes = [280, 240, 200, 180, 160];
for (var i = 0; i < sizes.length; i++) { for (var i = 0; i < sizes.length; i++) {
url = soonDesign1CaptureCanvasRect(canvas, sizes[i], sizes[i], 'png', null, false, '#31373D'); url = soonDesign1CaptureCanvasRect(canvas, sizes[i], sizes[i], 'png', null, true);
if (url && url.indexOf('data:image/') === 0 && url.length <= THUMB_MAX) break; if (url && url.indexOf('data:image/') === 0 && url.length <= THUMB_MAX) break;
url = ''; url = '';
} }
@@ -1223,22 +1218,25 @@ window.regenerateQrCodesAndBarcodes = function regenerateQrCodesAndBarcodes(canv
// 使用 window.openFile 避免与浏览器原生的 window.open 冲突 // 使用 window.openFile 避免与浏览器原生的 window.open 冲突
window.openFile = function open(file, jAlready) { window.openFile = function open(file, jAlready) {
if (!file) return; if (!file) return;
if (typeof window.soonOnOpenDesignFile === 'function') window.soonOnOpenDesignFile(file);
if (typeof window.soonBindFileMeta === 'function') window.soonBindFileMeta(file); if (typeof window.soonBindFileMeta === 'function') window.soonBindFileMeta(file);
function doOpenWithJson(j) { function doOpenWithJson(j) {
if (!j || !background_image) { if (typeof window.soonNormalizeSoonJson === 'function') {
j = window.soonNormalizeSoonJson(j);
}
if (!j) {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败'); if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
return; return;
} }
if (!j.f || !j.f.objects || !Array.isArray(j.f.objects) || j.f.objects.length === 0) { function runOpen() {
var bgRef = window.background_image1 || window.background_image;
if (!bgRef) {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败'); if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
return; return;
} }
if (!j.b || !j.b.objects || !Array.isArray(j.b.objects) || j.b.objects.length === 0) {
j.b = { version: '4.6.0', objects: [], hoverCursor: 'move' };
}
openAs.name = file; openAs.name = file;
var left = background_image.left; var left = bgRef.left;
var top = background_image.top; var top = bgRef.top;
var new_left = j.f.objects[0].left; var new_left = j.f.objects[0].left;
var new_top = j.f.objects[0].top; var new_top = j.f.objects[0].top;
for (var i = 0; i < j.f.objects.length; i++) { for (var i = 0; i < j.f.objects.length; i++) {
@@ -1312,6 +1310,14 @@ window.openFile = function open(file, jAlready) {
} }
}); });
} }
if (typeof window.soonRunWhenDesignBgReady === 'function') {
window.soonRunWhenDesignBgReady(runOpen, function () {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
});
} else {
runOpen();
}
}
if (arguments.length >= 2 && jAlready) { if (arguments.length >= 2 && jAlready) {
doOpenWithJson(jAlready); doOpenWithJson(jAlready);
openAs.name = file; openAs.name = file;
@@ -1384,6 +1390,7 @@ function cleanupSrcFields(jsonObjects, objsArray) {
function applyCloudWriteResult(result, fallbackName) { function applyCloudWriteResult(result, fallbackName) {
if (result && result.fileKey) { if (result && result.fileKey) {
openAs.name = result.fileKey; openAs.name = result.fileKey;
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(result.fileKey);
return result.fileKey; return result.fileKey;
} }
return fallbackName; return fallbackName;
@@ -1467,6 +1474,7 @@ function saveAs(op1, callback) {
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1); if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases(); if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
con_o.soonType = 1; con_o.soonType = 1;
soonStripDesign1HeavyFields(con_o);
var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showSaveDialog ? { showSaveDialog: function(opts) { return window.platformBridge.showSaveDialog(opts); } } : null); var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showSaveDialog ? { showSaveDialog: function(opts) { return window.platformBridge.showSaveDialog(opts); } } : null);
if (!dialogApi) return; if (!dialogApi) return;
dialogApi.showSaveDialog({ dialogApi.showSaveDialog({
@@ -1488,6 +1496,7 @@ function saveAs(op1, callback) {
if (fs) { if (fs) {
fs.writeFileSync(result.filePath || fp, content, 'utf8'); fs.writeFileSync(result.filePath || fp, content, 'utf8');
openAs.name = result.filePath || fp; openAs.name = result.filePath || fp;
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(openAs.name);
layer.msg(language_str("saveSucc") + openAs.name); layer.msg(language_str("saveSucc") + openAs.name);
saveHistory(); saveHistory();
if (callback && typeof callback === 'function') callback(); if (callback && typeof callback === 'function') callback();
@@ -1535,6 +1544,7 @@ function save(op1, callback) {
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1); if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases(); if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
con_o.soonType = 1; con_o.soonType = 1;
soonStripDesign1HeavyFields(con_o);
var content = JSON.stringify(con_o); var content = JSON.stringify(con_o);
var needsDialog = typeof window.soonNeedsSaveDialog === 'function' var needsDialog = typeof window.soonNeedsSaveDialog === 'function'
? window.soonNeedsSaveDialog(openAs.name) ? window.soonNeedsSaveDialog(openAs.name)
@@ -1573,6 +1583,7 @@ function save(op1, callback) {
if (fs) { if (fs) {
fs.writeFileSync(result.filePath || fp, content, 'utf8'); fs.writeFileSync(result.filePath || fp, content, 'utf8');
openAs.name = result.filePath || fp; openAs.name = result.filePath || fp;
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(openAs.name);
layer.msg(language_str("saveSucc") + openAs.name); layer.msg(language_str("saveSucc") + openAs.name);
saveHistory(); saveHistory();
if (callback && typeof callback === 'function') callback(); if (callback && typeof callback === 'function') callback();
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -495,7 +495,7 @@ if (ipcRenderer) {
if (typeof require === 'undefined') { if (typeof require === 'undefined') {
function loadScript(src, next) { function loadScript(src, next) {
var s = document.createElement('script'); var s = document.createElement('script');
s.src = src; s.src = (typeof window.soonWithVersion === 'function') ? window.soonWithVersion(src) : src;
s.onload = s.onerror = function () { if (typeof next === 'function') next(); }; s.onload = s.onerror = function () { if (typeof next === 'function') next(); };
document.body.appendChild(s); document.body.appendChild(s);
} }
+21 -1
View File
@@ -1171,6 +1171,7 @@ function cleanupSrcFields(jsonObjects, objsArray) {
function applyCloudWriteResult(result, fallbackName) { function applyCloudWriteResult(result, fallbackName) {
if (result && result.fileKey) { if (result && result.fileKey) {
openAs.name = result.fileKey; openAs.name = result.fileKey;
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(result.fileKey);
return result.fileKey; return result.fileKey;
} }
return fallbackName; return fallbackName;
@@ -1272,6 +1273,7 @@ function saveAs(op1, callback) {
if (fs) { if (fs) {
fs.writeFileSync(fp, content, 'utf8'); fs.writeFileSync(fp, content, 'utf8');
openAs.name = fp; openAs.name = fp;
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(openAs.name);
if (typeof window.soonToast === 'function') window.soonToast(language_str('saveSucc') + openAs.name, 'success'); if (typeof window.soonToast === 'function') window.soonToast(language_str('saveSucc') + openAs.name, 'success');
else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 }); else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 });
saveHistory(); saveHistory();
@@ -1357,6 +1359,7 @@ function save(op1, callback) {
if (fs) { if (fs) {
fs.writeFileSync(fp, content, 'utf8'); fs.writeFileSync(fp, content, 'utf8');
openAs.name = fp; openAs.name = fp;
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(openAs.name);
if (typeof window.soonToast === 'function') window.soonToast(language_str('saveSucc') + openAs.name, 'success'); if (typeof window.soonToast === 'function') window.soonToast(language_str('saveSucc') + openAs.name, 'success');
else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 }); else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 });
saveHistory(); saveHistory();
@@ -1429,9 +1432,18 @@ function saveHistory() {
// 使用 window.openFile 避免与浏览器原生的 window.open 冲突 // 使用 window.openFile 避免与浏览器原生的 window.open 冲突
window.openFile = function open(file, jAlready) { window.openFile = function open(file, jAlready) {
if (!file) return; if (!file) return;
if (typeof window.soonOnOpenDesignFile === 'function') window.soonOnOpenDesignFile(file);
if (typeof window.soonBindFileMeta === 'function') window.soonBindFileMeta(file); if (typeof window.soonBindFileMeta === 'function') window.soonBindFileMeta(file);
function doOpenWithJson(j) { function doOpenWithJson(j) {
if (!j || !background_image) { if (typeof window.soonNormalizeSoonJson === 'function') {
j = window.soonNormalizeSoonJson(j);
}
if (!j) {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
return;
}
function runOpen() {
if (!background_image) {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败'); if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
return; return;
} }
@@ -1636,6 +1648,14 @@ window.openFile = function open(file, jAlready) {
canvas2Loaded = true canvas2Loaded = true
saveInitialState() saveInitialState()
}) })
}
if (typeof window.soonRunWhenDesignBgReady === 'function') {
window.soonRunWhenDesignBgReady(runOpen, function () {
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
});
} else {
runOpen();
}
} }
if (arguments.length >= 2 && jAlready) { if (arguments.length >= 2 && jAlready) {
doOpenWithJson(jAlready); doOpenWithJson(jAlready);
+9 -16
View File
@@ -1278,22 +1278,6 @@ function getAbsoluteXY(_obj) {
return [_obj.get('left'), _obj.get('top')] return [_obj.get('left'), _obj.get('top')]
} }
$('#open').click(function () { $('#open').click(function () {
if (step.val == 0) {
OpenDialog();
return;
}
layer.confirm(
language_str('whetherSave'),
function (index) {
if (typeof window.output === 'function') {
window.output(OpenDialog);
}
layer.close(index);
},
function (index) {
OpenDialog();
}
)
function OpenDialog() { function OpenDialog() {
var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showOpenDialog ? { showOpenDialog: function(opts) { return window.platformBridge.showOpenDialog(opts); } } : null); var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showOpenDialog ? { showOpenDialog: function(opts) { return window.platformBridge.showOpenDialog(opts); } } : null);
if (!dialogApi) return; if (!dialogApi) return;
@@ -1333,12 +1317,21 @@ $('#open').click(function () {
}) })
.catch(function(err) {}); .catch(function(err) {});
} }
if (typeof window.soonConfirmLeaveThen === 'function') {
window.soonConfirmLeaveThen(OpenDialog);
} else {
OpenDialog();
}
}) })
function goFirstPage() { function goFirstPage() {
if (window.platformBridge && window.platformBridge.openFirstPage) window.platformBridge.openFirstPage(); if (window.platformBridge && window.platformBridge.openFirstPage) window.platformBridge.openFirstPage();
else if (ipcRenderer && ipcRenderer.send) ipcRenderer.send('open-first-page'); else if (ipcRenderer && ipcRenderer.send) ipcRenderer.send('open-first-page');
} }
$('#new').click(function () { $('#new').click(function () {
if (typeof window.soonConfirmLeaveThen === 'function') {
window.soonConfirmLeaveThen(goFirstPage);
return;
}
if (step.val == 0) { if (step.val == 0) {
goFirstPage(); goFirstPage();
return; return;
+4 -1
View File
@@ -810,7 +810,10 @@ layui.use(['layer', 'form', 'jquery'], function () {
} }
window.soonOpenSoonJsonLocally(soonData, fileName); var openP = window.soonOpenSoonJsonLocally(soonData, fileName);
if (openP && typeof openP.then === 'function') {
openP.catch(function () {});
}
}).catch(function (err) { console.log(err); }); }).catch(function (err) { console.log(err); });
+21 -8
View File
@@ -444,18 +444,23 @@
} }
return fetchFromNetwork(key, cacheKey); return fetchFromNetwork(key, cacheKey);
function fetchFromNetwork(key, cacheKey) { function readSessionJson(key, attempt) {
if (key.indexOf('soondesign_session:') === 0) { attempt = attempt || 0;
if (typeof window.soonLocalGet === 'function') { if (typeof window.soonLocalGet === 'function') {
return window.soonLocalGet(cacheKey).then(function (hit) { return window.soonLocalGet(cacheKey).then(function (hit) {
if (hit && hit.json) return hit.json; if (hit && hit.json) return hit.json;
var raw = null;
try { try {
var j = sessionStorage.getItem(key); raw = sessionStorage.getItem(key);
if (!j && typeof localStorage !== 'undefined') j = localStorage.getItem(key); if (!raw && typeof localStorage !== 'undefined') raw = localStorage.getItem(key);
if (!j || j === 'idb') return null; if (raw && raw !== 'idb') return JSON.parse(raw);
var parsed = JSON.parse(j); } catch (e) { /* ignore */ }
return afterNetworkJson(parsed, { source: 'session', name: key }); if (raw === 'idb' && attempt < 8) {
} catch (e) { return null; } return new Promise(function (resolve) {
setTimeout(resolve, 60);
}).then(function () { return readSessionJson(key, attempt + 1); });
}
return null;
}); });
} }
try { try {
@@ -465,6 +470,14 @@
return Promise.resolve(JSON.parse(j)); return Promise.resolve(JSON.parse(j));
} catch (e) { return Promise.resolve(null); } } catch (e) { return Promise.resolve(null); }
} }
function fetchFromNetwork(key, cacheKey) {
if (key.indexOf('soondesign_session:') === 0) {
return readSessionJson(key, 0).then(function (parsed) {
if (!parsed) return null;
return afterNetworkJson(parsed, { source: 'session', name: key });
});
}
if (key && key.indexOf('soondesign_file:') === 0 && typeof fetch !== 'undefined') { if (key && key.indexOf('soondesign_file:') === 0 && typeof fetch !== 'undefined') {
var fileMatch = key.match(/^soondesign_file:(\d+)/); var fileMatch = key.match(/^soondesign_file:(\d+)/);
if (fileMatch && getAccessToken()) { if (fileMatch && getAccessToken()) {
+3
View File
@@ -2,6 +2,8 @@
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../../config/version.js"></script>
<script src="../../js/common/soon-cache.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="SoonDesign 管理后台"> <meta name="description" content="SoonDesign 管理后台">
<link rel="icon" href="../../assets/images/favicon.ico"> <link rel="icon" href="../../assets/images/favicon.ico">
@@ -37,6 +39,7 @@
</div> </div>
</div> </div>
<script src="../../js/common/soon-cache.js"></script>
<script src="../../config/local.js"></script> <script src="../../config/local.js"></script>
<script src="../../js/common/auth-redirect.js"></script> <script src="../../js/common/auth-redirect.js"></script>
<script src="../../vendor/layui/layui.js"></script> <script src="../../vendor/layui/layui.js"></script>
+3
View File
@@ -2,6 +2,8 @@
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../../config/version.js"></script>
<script src="../../js/common/soon-cache.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="SoonDesign 管理后台登录"> <meta name="description" content="SoonDesign 管理后台登录">
<link rel="icon" href="../../assets/images/favicon.ico"> <link rel="icon" href="../../assets/images/favicon.ico">
@@ -41,6 +43,7 @@
</div> </div>
</div> </div>
<script src="../../js/common/soon-cache.js"></script>
<script src="../../config/local.js"></script> <script src="../../config/local.js"></script>
<script src="../../js/common/auth-redirect.js"></script> <script src="../../js/common/auth-redirect.js"></script>
<script src="../../vendor/layui/layui.js"></script> <script src="../../vendor/layui/layui.js"></script>
+3
View File
@@ -3,6 +3,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../config/version.js"></script>
<script src="../js/common/soon-cache.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="SoonDesign 在线卡证设计"> <meta name="description" content="SoonDesign 在线卡证设计">
<link rel="icon" href="../assets/images/favicon.ico"> <link rel="icon" href="../assets/images/favicon.ico">
@@ -869,6 +871,7 @@
<img id="source_back" src="../assets/images/back_bg_1.png" style="display: none" /> <img id="source_back" src="../assets/images/back_bg_1.png" style="display: none" />
</div> </div>
</div> </div>
<script src="../js/common/soon-cache.js"></script>
<script src="../vendor/js/fabric1.min.js"></script> <script src="../vendor/js/fabric1.min.js"></script>
<script src="../vendor/js/guideLines1.js"></script> <script src="../vendor/js/guideLines1.js"></script>
<script src="../vendor/js/print.min.js"></script> <script src="../vendor/js/print.min.js"></script>
+3
View File
@@ -3,6 +3,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../config/version.js"></script>
<script src="../js/common/soon-cache.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="SoonDesign 在线卡证设计"> <meta name="description" content="SoonDesign 在线卡证设计">
<link rel="icon" href="../assets/images/favicon.ico"> <link rel="icon" href="../assets/images/favicon.ico">
@@ -661,6 +663,7 @@
</div> </div>
</div> </div>
<script src="../js/common/soon-cache.js"></script>
<script src="../vendor/js/fabric2.min.js"></script> <script src="../vendor/js/fabric2.min.js"></script>
<script src="../vendor/js/guideLines2.js"></script> <script src="../vendor/js/guideLines2.js"></script>
<script src="../vendor/js/print.min.js"></script> <script src="../vendor/js/print.min.js"></script>
+5
View File
@@ -6,6 +6,9 @@
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../config/version.js"></script>
<script src="../js/common/soon-cache.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="SoonDesign 在线卡证设计"> <meta name="description" content="SoonDesign 在线卡证设计">
@@ -134,6 +137,8 @@
<script src="../js/common/soon-cache.js"></script>
<script src="../config/local.js"></script> <script src="../config/local.js"></script>
<script src="../js/common/auth-redirect.js"></script> <script src="../js/common/auth-redirect.js"></script>
+3
View File
@@ -2,6 +2,8 @@
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../config/version.js"></script>
<script src="../js/common/soon-cache.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="SoonDesign 在线卡证设计"> <meta name="description" content="SoonDesign 在线卡证设计">
<link rel="icon" href="../assets/images/favicon.ico"> <link rel="icon" href="../assets/images/favicon.ico">
@@ -39,6 +41,7 @@
</div> </div>
</div> </div>
<script src="../js/common/soon-cache.js"></script>
<script src="../config/local.js"></script> <script src="../config/local.js"></script>
<script src="../js/common/portal-topbar.js"></script> <script src="../js/common/portal-topbar.js"></script>
<script>SoonPortalTopbar.mount('#portal-topbar', { variant: 'auth' });</script> <script>SoonPortalTopbar.mount('#portal-topbar', { variant: 'auth' });</script>
+3
View File
@@ -2,6 +2,8 @@
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../config/version.js"></script>
<script src="../js/common/soon-cache.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="SoonDesign 在线卡证设计"> <meta name="description" content="SoonDesign 在线卡证设计">
<link rel="icon" href="../assets/images/favicon.ico"> <link rel="icon" href="../assets/images/favicon.ico">
@@ -39,6 +41,7 @@
</div> </div>
</div> </div>
<script src="../js/common/soon-cache.js"></script>
<script src="../config/local.js"></script> <script src="../config/local.js"></script>
<script src="../js/common/portal-topbar.js"></script> <script src="../js/common/portal-topbar.js"></script>
<script>SoonPortalTopbar.mount('#portal-topbar', { variant: 'auth' });</script> <script>SoonPortalTopbar.mount('#portal-topbar', { variant: 'auth' });</script>
+21
View File
@@ -0,0 +1,21 @@
# 写入 frontend-web/config/version.jsstart.bat / sync-config 自动调用,发版上传前也会更新)
param([string]$Version = '')
$ErrorActionPreference = 'Stop'
$Root = Split-Path $PSScriptRoot -Parent
$WebRoot = Join-Path $Root 'frontend-web'
if (-not $Version) {
Push-Location $Root
try { $Version = (git rev-parse --short HEAD 2>$null) } finally { Pop-Location }
if (-not $Version) { $Version = Get-Date -Format 'yyyyMMddHHmmss' }
}
$out = @"
(function () {
'use strict';
window.SOON_APP_VERSION = '$Version';
})();
"@
$path = Join-Path $WebRoot 'config\version.js'
[System.IO.File]::WriteAllText($path, $out, (New-Object System.Text.UTF8Encoding $false))