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:
@@ -42,6 +42,7 @@ scripts\start.bat
|
||||
- 数据库:在 `backend-web` 目录执行 `mysql ... < database/schema.sql`;已有库 `php scripts/migrate-schema.php`
|
||||
- 后端:`backend-web/config/local.php.example` → `local.php`
|
||||
- 前端 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)。
|
||||
|
||||
|
||||
@@ -9,8 +9,18 @@ server {
|
||||
location = / { 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)$ {
|
||||
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;
|
||||
}
|
||||
location / { try_files $uri $uri/ =404; }
|
||||
|
||||
@@ -70,3 +70,5 @@ $enc = [Text.UTF8Encoding]::new($false)
|
||||
$webLocal = Join-Path $Root 'frontend-web\config\local.js'
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path $webLocal -Parent) | Out-Null
|
||||
[IO.File]::WriteAllText($webLocal, $localJs.Trim() + "`n", $enc)
|
||||
|
||||
& (Join-Path $Root 'scripts\write-web-version.ps1') | Out-Null
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -75,28 +75,7 @@ function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
|
||||
tctx.restore();
|
||||
}
|
||||
|
||||
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) {
|
||||
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc) {
|
||||
var bg = window.background_image1 || window.background_image;
|
||||
if (!canvas || !bg) return '';
|
||||
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,
|
||||
0, 0, outW, outH
|
||||
);
|
||||
if (discMatte) {
|
||||
soonDesign1ApplyDiscMatte(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas), discMatte);
|
||||
} else if (clipDisc && format !== 'jpeg') {
|
||||
if (clipDisc && format !== 'jpeg') {
|
||||
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
|
||||
}
|
||||
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) {
|
||||
window._soonSkipDesign1CdMask = true;
|
||||
try {
|
||||
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||
canvas.renderAll();
|
||||
var url = soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||
return soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||
} finally {
|
||||
window._soonSkipDesign1CdMask = false;
|
||||
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 对象,确保全局可访问
|
||||
window.display_func = function display_func(img1, img2, img3) {
|
||||
@@ -1171,14 +1214,20 @@ window.regenerateQrCodesAndBarcodes = function regenerateQrCodesAndBarcodes(canv
|
||||
window.openFile = function open(file, jAlready) {
|
||||
if (!file) return;
|
||||
function doOpenWithJson(j) {
|
||||
if (!j || !background_image) return;
|
||||
if (!j.f || !j.f.objects || !Array.isArray(j.f.objects) || j.f.objects.length === 0) 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' };
|
||||
j = soonNormalizeSoonJson(j);
|
||||
if (!j) {
|
||||
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
|
||||
return;
|
||||
}
|
||||
function runOpen() {
|
||||
var bgRef = window.background_image1 || window.background_image;
|
||||
if (!bgRef) {
|
||||
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
|
||||
return;
|
||||
}
|
||||
openAs.name = file;
|
||||
var left = background_image.left;
|
||||
var top = background_image.top;
|
||||
var left = bgRef.left;
|
||||
var top = bgRef.top;
|
||||
var new_left = j.f.objects[0].left;
|
||||
var new_top = j.f.objects[0].top;
|
||||
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) {
|
||||
doOpenWithJson(jAlready);
|
||||
openAs.name = file;
|
||||
@@ -1257,12 +1310,22 @@ window.openFile = function open(file, jAlready) {
|
||||
}
|
||||
file = resolveDiskPath(file);
|
||||
if (window.platformBridge && window.platformBridge.readJsonFile) {
|
||||
if (typeof window.soonShowOpenFileLoading === 'function') window.soonShowOpenFileLoading();
|
||||
return window.platformBridge.readJsonFile(file).then(function(j) {
|
||||
if (typeof window.soonHideOpenFileLoading === 'function') window.soonHideOpenFileLoading();
|
||||
if (j) {
|
||||
doOpenWithJson(j);
|
||||
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 {
|
||||
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 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);
|
||||
if (!dialogApi) return;
|
||||
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('.')) : '');
|
||||
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) {
|
||||
await window.platformBridge.writeFile(result.fileHandle || fp, content);
|
||||
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 con_o = Object.assign(o, op1);
|
||||
if (openAs.name != "") {
|
||||
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);
|
||||
if (isWebPortal() && openAs.name.indexOf('soondesign_session:') === 0) {
|
||||
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('.')) : '');
|
||||
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) {
|
||||
await window.platformBridge.writeFile(result.fileHandle || fp, content);
|
||||
openAs.name = isWebPortal() ? ('soondesign_session:' + fp) : (result.filePath || fp);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
window.SOON_APP_VERSION = 'efeb3bc';
|
||||
})();
|
||||
@@ -36,7 +36,8 @@
|
||||
|
||||
window.soonJs = function (rel) {
|
||||
var n = String(rel || '').replace(/^\/+/, '');
|
||||
return window.SOON_JS_BASE + n;
|
||||
var url = window.SOON_JS_BASE + n;
|
||||
return (typeof window.soonWithVersion === 'function') ? window.soonWithVersion(url) : url;
|
||||
};
|
||||
|
||||
/** 等 HTMLImage 解码完成后再 toDataURL / setSrc,避免间歇性空白背景 */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
var FILE_PREFIX = 'soondesign_file:';
|
||||
var TEMPLATE_PREFIX = 'soondesign_template:';
|
||||
var SESSION_PREFIX = 'soondesign_session:';
|
||||
|
||||
function soonParseTemplateKey(key) {
|
||||
if (!key || typeof key !== 'string') return null;
|
||||
@@ -39,12 +40,60 @@
|
||||
return FILE_PREFIX + id + ':v' + version;
|
||||
}
|
||||
|
||||
function soonIsCloudFileKey(key) {
|
||||
return !!(key && typeof key === 'string' && key.indexOf(FILE_PREFIX) === 0);
|
||||
}
|
||||
|
||||
function soonIsTemplateKey(key) {
|
||||
return !!(key && typeof key === 'string' && key.indexOf(TEMPLATE_PREFIX) === 0);
|
||||
}
|
||||
|
||||
/** 打开模板 / 本地 .soon / session 导入:首次保存须另存为;云端文件或已选定保存目标后可原地保存 */
|
||||
function soonNeedsSaveDialog(key) {
|
||||
return !key || soonIsTemplateKey(key);
|
||||
if (!key) return true;
|
||||
if (soonIsCloudFileKey(key)) return false;
|
||||
if (window._soonSaveInPlaceKey && key === window._soonSaveInPlaceKey) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function soonOnOpenDesignFile(fileKey) {
|
||||
window._soonSaveInPlaceKey = '';
|
||||
}
|
||||
|
||||
function soonMarkSaveInPlace(fileKey) {
|
||||
if (fileKey) window._soonSaveInPlaceKey = fileKey;
|
||||
}
|
||||
|
||||
/** 离开页/新建前是否需提示保存:有未撤销编辑,或已打开过文件/模板(含未改动的打开态) */
|
||||
function soonShouldConfirmBeforeLeave() {
|
||||
try {
|
||||
if (typeof window.step !== 'undefined' && window.step && window.step.val > 0) return true;
|
||||
} catch (e) { /* ignore */ }
|
||||
var oa = typeof window.openAs !== 'undefined' ? window.openAs : null;
|
||||
return !!(oa && oa.name);
|
||||
}
|
||||
|
||||
function soonConfirmLeaveThen(run) {
|
||||
if (typeof run !== 'function') return;
|
||||
if (typeof window.soonShouldConfirmBeforeLeave === 'function' && !window.soonShouldConfirmBeforeLeave()) {
|
||||
run();
|
||||
return;
|
||||
}
|
||||
if (typeof window.layer === 'undefined' || !window.layer || typeof window.language_str !== 'function') {
|
||||
run();
|
||||
return;
|
||||
}
|
||||
window.layer.confirm(window.language_str('whetherSave'), {
|
||||
btn: [window.language_str('save'), window.language_str('noSave'), window.language_str('cancel')]
|
||||
}, function () {
|
||||
if (typeof window.output === 'function') {
|
||||
window.output(function () { run(); });
|
||||
} else {
|
||||
run();
|
||||
}
|
||||
}, function () {
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
||||
function soonDefaultNewSoonName() {
|
||||
@@ -623,38 +672,115 @@
|
||||
return j && j.soonType ? j.soonType : (j && j.backBlackPic ? 2 : 1);
|
||||
}
|
||||
|
||||
function soonPutSoonSession(j, fileName) {
|
||||
if (!j) return '';
|
||||
/** 兼容旧版 .soon:补全 f/b/fo/bo,加载时去掉 output 大图字段 */
|
||||
function soonNormalizeSoonJson(j) {
|
||||
if (!j || typeof j !== 'object') return null;
|
||||
if (typeof window.soonStripDesign1HeavyFields === 'function') {
|
||||
window.soonStripDesign1HeavyFields(j);
|
||||
}
|
||||
if (!j.f || !j.f.objects || !Array.isArray(j.f.objects)) {
|
||||
if (j.objects && Array.isArray(j.objects) && j.objects.length > 0) {
|
||||
j.f = {
|
||||
version: j.version || '4.6.0',
|
||||
objects: j.objects,
|
||||
hoverCursor: j.hoverCursor || 'move'
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (j.f.objects.length === 0) return null;
|
||||
if (!j.b || !j.b.objects || !Array.isArray(j.b.objects)) {
|
||||
j.b = { version: '4.6.0', objects: [], hoverCursor: 'move' };
|
||||
}
|
||||
if (!j.fo) j.fo = [];
|
||||
if (!j.bo) j.bo = [];
|
||||
var o0 = j.f.objects[0];
|
||||
if (o0) {
|
||||
if (o0.left == null || isNaN(o0.left)) o0.left = 0;
|
||||
if (o0.top == null || isNaN(o0.top)) o0.top = 0;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
function soonRunWhenDesignBgReady(fn, onTimeout) {
|
||||
if (typeof fn !== 'function') return;
|
||||
function ready() {
|
||||
return !!(window.background_image1 || window.background_image);
|
||||
}
|
||||
if (ready()) {
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
var n = 0;
|
||||
(function tick() {
|
||||
if (ready()) {
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
if (++n > 120) {
|
||||
if (typeof onTimeout === 'function') onTimeout();
|
||||
return;
|
||||
}
|
||||
setTimeout(tick, 50);
|
||||
})();
|
||||
}
|
||||
|
||||
function soonPrepareSoonSessionWrite(j, fileName) {
|
||||
var normalized = soonNormalizeSoonJson(j);
|
||||
if (!normalized) return null;
|
||||
var displayName = soonEnsureSoonExt(fileName || 'design.soon');
|
||||
var key = soonMakeSessionKey(displayName);
|
||||
if (typeof window.soonLocalCacheAndThumb === 'function') {
|
||||
window.soonLocalCacheAndThumb(key, j, { source: 'session', name: displayName });
|
||||
try { sessionStorage.setItem(key, 'idb'); } catch (e) { /* ignore */ }
|
||||
return key;
|
||||
var slim = Object.assign({}, normalized);
|
||||
if (typeof window.soonStripDesign1HeavyFields === 'function') {
|
||||
window.soonStripDesign1HeavyFields(slim);
|
||||
}
|
||||
var payload = '';
|
||||
try { payload = JSON.stringify(slim); } catch (e) { return null; }
|
||||
try {
|
||||
sessionStorage.setItem(key, JSON.stringify(j));
|
||||
return key;
|
||||
sessionStorage.setItem(key, payload);
|
||||
} catch (e) {
|
||||
return '';
|
||||
try {
|
||||
sessionStorage.setItem(key, 'idb');
|
||||
} catch (e2) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return { key: key, slim: slim, displayName: displayName, normalized: normalized };
|
||||
}
|
||||
|
||||
function soonPutSoonSession(j, fileName) {
|
||||
var prep = soonPrepareSoonSessionWrite(j, fileName);
|
||||
if (!prep) return '';
|
||||
if (typeof window.soonLocalCacheAndThumb === 'function') {
|
||||
window.soonLocalCacheAndThumb(prep.key, prep.slim, { source: 'session', name: prep.displayName });
|
||||
}
|
||||
return prep.key;
|
||||
}
|
||||
|
||||
function soonOpenSoonJsonLocally(j, fileName) {
|
||||
var key = soonPutSoonSession(j, fileName);
|
||||
if (!key) {
|
||||
soonToast('无法打开文件(存储空间不足)', 'error');
|
||||
return '';
|
||||
var prep = soonPrepareSoonSessionWrite(j, fileName);
|
||||
if (!prep) {
|
||||
soonToast('无法打开文件(格式无效或存储空间不足)', 'error');
|
||||
return Promise.resolve('');
|
||||
}
|
||||
var cacheP = typeof window.soonLocalCacheAndThumb === 'function'
|
||||
? window.soonLocalCacheAndThumb(prep.key, prep.slim, { source: 'session', name: prep.displayName })
|
||||
: Promise.resolve(true);
|
||||
return cacheP.then(function () {
|
||||
if (typeof window.soonRecentUpsertFromOpen === 'function') {
|
||||
window.soonRecentUpsertFromOpen(key, j);
|
||||
window.soonRecentUpsertFromOpen(prep.key, prep.normalized);
|
||||
}
|
||||
soonScheduleCloudImport(key, fileName);
|
||||
var type = soonSoonTypeFromJson(j);
|
||||
soonScheduleCloudImport(prep.key, fileName);
|
||||
var type = soonSoonTypeFromJson(prep.normalized);
|
||||
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';
|
||||
@@ -944,6 +1070,8 @@
|
||||
window.soonParseTemplateKey = soonParseTemplateKey;
|
||||
window.soonMakeTemplateKey = soonMakeTemplateKey;
|
||||
window.soonSoonTypeFromJson = soonSoonTypeFromJson;
|
||||
window.soonNormalizeSoonJson = soonNormalizeSoonJson;
|
||||
window.soonRunWhenDesignBgReady = soonRunWhenDesignBgReady;
|
||||
window.soonLoadMembership = soonLoadMembership;
|
||||
window.soonApplyMembership = soonApplyMembership;
|
||||
window.soonRefreshPortalIdentity = soonRefreshPortalIdentity;
|
||||
@@ -953,7 +1081,12 @@
|
||||
window.soonShowApiError = soonShowApiError;
|
||||
window.soonDisplayFileName = soonDisplayFileName;
|
||||
window.soonIsTemplateKey = soonIsTemplateKey;
|
||||
window.soonIsCloudFileKey = soonIsCloudFileKey;
|
||||
window.soonNeedsSaveDialog = soonNeedsSaveDialog;
|
||||
window.soonOnOpenDesignFile = soonOnOpenDesignFile;
|
||||
window.soonMarkSaveInPlace = soonMarkSaveInPlace;
|
||||
window.soonShouldConfirmBeforeLeave = soonShouldConfirmBeforeLeave;
|
||||
window.soonConfirmLeaveThen = soonConfirmLeaveThen;
|
||||
window.soonDefaultNewSoonName = soonDefaultNewSoonName;
|
||||
window.soonEnsureSoonExt = soonEnsureSoonExt;
|
||||
window.soonSessionSlug = soonSessionSlug;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
var v = window.SOON_APP_VERSION || '';
|
||||
|
||||
window.soonWithVersion = function (url) {
|
||||
if (!url || /^https?:\/\//i.test(url) || /^data:/i.test(url)) return url;
|
||||
if (!v) return url;
|
||||
var s = String(url);
|
||||
var hashIdx = s.indexOf('#');
|
||||
var hash = hashIdx >= 0 ? s.slice(hashIdx) : '';
|
||||
var noHash = hashIdx >= 0 ? s.slice(0, hashIdx) : s;
|
||||
return noHash.split('?')[0] + '?v=' + encodeURIComponent(v) + hash;
|
||||
};
|
||||
|
||||
window.soonPatchForward = function (fromEl) {
|
||||
var fn = window.soonWithVersion;
|
||||
if (typeof fn !== 'function') return;
|
||||
var el = fromEl && fromEl.nextElementSibling;
|
||||
while (el) {
|
||||
if (el.tagName === 'SCRIPT') {
|
||||
var src = el.getAttribute('src');
|
||||
if (src && src.indexOf('version.js') < 0 && src.indexOf('soon-cache.js') < 0) {
|
||||
el.src = fn(src);
|
||||
}
|
||||
} else if (el.tagName === 'LINK' && el.rel === 'stylesheet') {
|
||||
var href = el.getAttribute('href');
|
||||
if (href) el.href = fn(href);
|
||||
}
|
||||
el = el.nextElementSibling;
|
||||
}
|
||||
};
|
||||
|
||||
if (v) {
|
||||
var KEY = 'soondesign_app_version';
|
||||
try {
|
||||
var prev = localStorage.getItem(KEY);
|
||||
if (prev && prev !== v) {
|
||||
localStorage.setItem(KEY, v);
|
||||
var url = new URL(window.location.href);
|
||||
if (url.searchParams.get('_sv') !== v) {
|
||||
url.searchParams.set('_sv', v);
|
||||
window.location.replace(url.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
localStorage.setItem(KEY, v);
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
if (document.currentScript) {
|
||||
window.soonPatchForward(document.currentScript);
|
||||
}
|
||||
})();
|
||||
@@ -502,7 +502,7 @@ if (ipcRenderer) {
|
||||
// 网页端:按顺序动态插入 script,确保 core/ui 执行时 window.$ 已存在
|
||||
function loadScript(src, next) {
|
||||
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(); };
|
||||
document.body.appendChild(s);
|
||||
}
|
||||
|
||||
@@ -262,6 +262,13 @@ if (typeof ipcRenderer !== 'undefined' && ipcRenderer) {
|
||||
applySysLan(window.platformBridge.getLocale());
|
||||
}
|
||||
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"), {
|
||||
btn: [language_str("save"), language_str("noSave"), language_str("cancel")]
|
||||
, btn3: function (index, layero) {}
|
||||
|
||||
@@ -78,29 +78,7 @@ function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
|
||||
tctx.restore();
|
||||
}
|
||||
|
||||
/** 圆外与中心孔铺画布底色,保留环形内容(缩略图用) */
|
||||
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) {
|
||||
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc) {
|
||||
var bg = window.background_image1 || window.background_image;
|
||||
if (!canvas || !bg) return '';
|
||||
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,
|
||||
0, 0, outW, outH
|
||||
);
|
||||
if (discMatte) {
|
||||
soonDesign1ApplyDiscMatte(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas), discMatte);
|
||||
} else if (clipDisc && format !== 'jpeg') {
|
||||
if (clipDisc && format !== 'jpeg') {
|
||||
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
|
||||
}
|
||||
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) {
|
||||
window._soonSkipDesign1CdMask = true;
|
||||
try {
|
||||
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||
canvas.renderAll();
|
||||
var url = soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||
return soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||
} finally {
|
||||
window._soonSkipDesign1CdMask = false;
|
||||
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) {
|
||||
var THUMB_MAX = 102400;
|
||||
@@ -159,7 +154,7 @@ function soonBuildDesign1SavePreviewPic(canvas) {
|
||||
var url = '';
|
||||
var sizes = [280, 240, 200, 180, 160];
|
||||
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;
|
||||
url = '';
|
||||
}
|
||||
@@ -1223,22 +1218,25 @@ window.regenerateQrCodesAndBarcodes = function regenerateQrCodesAndBarcodes(canv
|
||||
// 使用 window.openFile 避免与浏览器原生的 window.open 冲突
|
||||
window.openFile = function open(file, jAlready) {
|
||||
if (!file) return;
|
||||
if (typeof window.soonOnOpenDesignFile === 'function') window.soonOnOpenDesignFile(file);
|
||||
if (typeof window.soonBindFileMeta === 'function') window.soonBindFileMeta(file);
|
||||
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;
|
||||
}
|
||||
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('模板/文件加载失败');
|
||||
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;
|
||||
var left = background_image.left;
|
||||
var top = background_image.top;
|
||||
var left = bgRef.left;
|
||||
var top = bgRef.top;
|
||||
var new_left = j.f.objects[0].left;
|
||||
var new_top = j.f.objects[0].top;
|
||||
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) {
|
||||
doOpenWithJson(jAlready);
|
||||
openAs.name = file;
|
||||
@@ -1384,6 +1390,7 @@ function cleanupSrcFields(jsonObjects, objsArray) {
|
||||
function applyCloudWriteResult(result, fallbackName) {
|
||||
if (result && result.fileKey) {
|
||||
openAs.name = result.fileKey;
|
||||
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(result.fileKey);
|
||||
return result.fileKey;
|
||||
}
|
||||
return fallbackName;
|
||||
@@ -1467,6 +1474,7 @@ function saveAs(op1, callback) {
|
||||
if (typeof window.soonApplySavePreview === 'function') window.soonApplySavePreview(con_o, canvas1);
|
||||
if (typeof window.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
|
||||
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);
|
||||
if (!dialogApi) return;
|
||||
dialogApi.showSaveDialog({
|
||||
@@ -1488,6 +1496,7 @@ function saveAs(op1, callback) {
|
||||
if (fs) {
|
||||
fs.writeFileSync(result.filePath || fp, content, 'utf8');
|
||||
openAs.name = result.filePath || fp;
|
||||
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(openAs.name);
|
||||
layer.msg(language_str("saveSucc") + openAs.name);
|
||||
saveHistory();
|
||||
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.soonRefreshDesignCanvases === 'function') window.soonRefreshDesignCanvases();
|
||||
con_o.soonType = 1;
|
||||
soonStripDesign1HeavyFields(con_o);
|
||||
var content = JSON.stringify(con_o);
|
||||
var needsDialog = typeof window.soonNeedsSaveDialog === 'function'
|
||||
? window.soonNeedsSaveDialog(openAs.name)
|
||||
@@ -1573,6 +1583,7 @@ function save(op1, callback) {
|
||||
if (fs) {
|
||||
fs.writeFileSync(result.filePath || fp, content, 'utf8');
|
||||
openAs.name = result.filePath || fp;
|
||||
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(openAs.name);
|
||||
layer.msg(language_str("saveSucc") + openAs.name);
|
||||
saveHistory();
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -495,7 +495,7 @@ if (ipcRenderer) {
|
||||
if (typeof require === 'undefined') {
|
||||
function loadScript(src, next) {
|
||||
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(); };
|
||||
document.body.appendChild(s);
|
||||
}
|
||||
|
||||
@@ -1171,6 +1171,7 @@ function cleanupSrcFields(jsonObjects, objsArray) {
|
||||
function applyCloudWriteResult(result, fallbackName) {
|
||||
if (result && result.fileKey) {
|
||||
openAs.name = result.fileKey;
|
||||
if (typeof window.soonMarkSaveInPlace === 'function') window.soonMarkSaveInPlace(result.fileKey);
|
||||
return result.fileKey;
|
||||
}
|
||||
return fallbackName;
|
||||
@@ -1272,6 +1273,7 @@ function saveAs(op1, callback) {
|
||||
if (fs) {
|
||||
fs.writeFileSync(fp, content, 'utf8');
|
||||
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');
|
||||
else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 });
|
||||
saveHistory();
|
||||
@@ -1357,6 +1359,7 @@ function save(op1, callback) {
|
||||
if (fs) {
|
||||
fs.writeFileSync(fp, content, 'utf8');
|
||||
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');
|
||||
else layer.msg(language_str('saveSucc') + openAs.name, { shade: 0 });
|
||||
saveHistory();
|
||||
@@ -1429,9 +1432,18 @@ function saveHistory() {
|
||||
// 使用 window.openFile 避免与浏览器原生的 window.open 冲突
|
||||
window.openFile = function open(file, jAlready) {
|
||||
if (!file) return;
|
||||
if (typeof window.soonOnOpenDesignFile === 'function') window.soonOnOpenDesignFile(file);
|
||||
if (typeof window.soonBindFileMeta === 'function') window.soonBindFileMeta(file);
|
||||
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('模板/文件加载失败');
|
||||
return;
|
||||
}
|
||||
@@ -1636,6 +1648,14 @@ window.openFile = function open(file, jAlready) {
|
||||
canvas2Loaded = true
|
||||
saveInitialState()
|
||||
})
|
||||
}
|
||||
if (typeof window.soonRunWhenDesignBgReady === 'function') {
|
||||
window.soonRunWhenDesignBgReady(runOpen, function () {
|
||||
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
|
||||
});
|
||||
} else {
|
||||
runOpen();
|
||||
}
|
||||
}
|
||||
if (arguments.length >= 2 && jAlready) {
|
||||
doOpenWithJson(jAlready);
|
||||
|
||||
@@ -1278,22 +1278,6 @@ function getAbsoluteXY(_obj) {
|
||||
return [_obj.get('left'), _obj.get('top')]
|
||||
}
|
||||
$('#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() {
|
||||
var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showOpenDialog ? { showOpenDialog: function(opts) { return window.platformBridge.showOpenDialog(opts); } } : null);
|
||||
if (!dialogApi) return;
|
||||
@@ -1333,12 +1317,21 @@ $('#open').click(function () {
|
||||
})
|
||||
.catch(function(err) {});
|
||||
}
|
||||
if (typeof window.soonConfirmLeaveThen === 'function') {
|
||||
window.soonConfirmLeaveThen(OpenDialog);
|
||||
} else {
|
||||
OpenDialog();
|
||||
}
|
||||
})
|
||||
function goFirstPage() {
|
||||
if (window.platformBridge && window.platformBridge.openFirstPage) window.platformBridge.openFirstPage();
|
||||
else if (ipcRenderer && ipcRenderer.send) ipcRenderer.send('open-first-page');
|
||||
}
|
||||
$('#new').click(function () {
|
||||
if (typeof window.soonConfirmLeaveThen === 'function') {
|
||||
window.soonConfirmLeaveThen(goFirstPage);
|
||||
return;
|
||||
}
|
||||
if (step.val == 0) {
|
||||
goFirstPage();
|
||||
return;
|
||||
|
||||
@@ -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); });
|
||||
|
||||
|
||||
@@ -444,18 +444,23 @@
|
||||
}
|
||||
return fetchFromNetwork(key, cacheKey);
|
||||
|
||||
function fetchFromNetwork(key, cacheKey) {
|
||||
if (key.indexOf('soondesign_session:') === 0) {
|
||||
function readSessionJson(key, attempt) {
|
||||
attempt = attempt || 0;
|
||||
if (typeof window.soonLocalGet === 'function') {
|
||||
return window.soonLocalGet(cacheKey).then(function (hit) {
|
||||
if (hit && hit.json) return hit.json;
|
||||
var raw = null;
|
||||
try {
|
||||
var j = sessionStorage.getItem(key);
|
||||
if (!j && typeof localStorage !== 'undefined') j = localStorage.getItem(key);
|
||||
if (!j || j === 'idb') return null;
|
||||
var parsed = JSON.parse(j);
|
||||
return afterNetworkJson(parsed, { source: 'session', name: key });
|
||||
} catch (e) { return null; }
|
||||
raw = sessionStorage.getItem(key);
|
||||
if (!raw && typeof localStorage !== 'undefined') raw = localStorage.getItem(key);
|
||||
if (raw && raw !== 'idb') return JSON.parse(raw);
|
||||
} catch (e) { /* ignore */ }
|
||||
if (raw === 'idb' && attempt < 8) {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(resolve, 60);
|
||||
}).then(function () { return readSessionJson(key, attempt + 1); });
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
try {
|
||||
@@ -465,6 +470,14 @@
|
||||
return Promise.resolve(JSON.parse(j));
|
||||
} 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') {
|
||||
var fileMatch = key.match(/^soondesign_file:(\d+)/);
|
||||
if (fileMatch && getAccessToken()) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<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="description" content="SoonDesign 管理后台">
|
||||
<link rel="icon" href="../../assets/images/favicon.ico">
|
||||
@@ -37,6 +39,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../js/common/soon-cache.js"></script>
|
||||
<script src="../../config/local.js"></script>
|
||||
<script src="../../js/common/auth-redirect.js"></script>
|
||||
<script src="../../vendor/layui/layui.js"></script>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<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="description" content="SoonDesign 管理后台登录">
|
||||
<link rel="icon" href="../../assets/images/favicon.ico">
|
||||
@@ -41,6 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../js/common/soon-cache.js"></script>
|
||||
<script src="../../config/local.js"></script>
|
||||
<script src="../../js/common/auth-redirect.js"></script>
|
||||
<script src="../../vendor/layui/layui.js"></script>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
<head>
|
||||
<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="description" content="SoonDesign 在线卡证设计">
|
||||
<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" />
|
||||
</div>
|
||||
</div>
|
||||
<script src="../js/common/soon-cache.js"></script>
|
||||
<script src="../vendor/js/fabric1.min.js"></script>
|
||||
<script src="../vendor/js/guideLines1.js"></script>
|
||||
<script src="../vendor/js/print.min.js"></script>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
<head>
|
||||
<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="description" content="SoonDesign 在线卡证设计">
|
||||
<link rel="icon" href="../assets/images/favicon.ico">
|
||||
@@ -661,6 +663,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/common/soon-cache.js"></script>
|
||||
<script src="../vendor/js/fabric2.min.js"></script>
|
||||
<script src="../vendor/js/guideLines2.js"></script>
|
||||
<script src="../vendor/js/print.min.js"></script>
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
<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="description" content="SoonDesign 在线卡证设计">
|
||||
@@ -134,6 +137,8 @@
|
||||
|
||||
|
||||
|
||||
<script src="../js/common/soon-cache.js"></script>
|
||||
|
||||
<script src="../config/local.js"></script>
|
||||
|
||||
<script src="../js/common/auth-redirect.js"></script>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<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="description" content="SoonDesign 在线卡证设计">
|
||||
<link rel="icon" href="../assets/images/favicon.ico">
|
||||
@@ -39,6 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/common/soon-cache.js"></script>
|
||||
<script src="../config/local.js"></script>
|
||||
<script src="../js/common/portal-topbar.js"></script>
|
||||
<script>SoonPortalTopbar.mount('#portal-topbar', { variant: 'auth' });</script>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<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="description" content="SoonDesign 在线卡证设计">
|
||||
<link rel="icon" href="../assets/images/favicon.ico">
|
||||
@@ -39,6 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/common/soon-cache.js"></script>
|
||||
<script src="../config/local.js"></script>
|
||||
<script src="../js/common/portal-topbar.js"></script>
|
||||
<script>SoonPortalTopbar.mount('#portal-topbar', { variant: 'auth' });</script>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# 写入 frontend-web/config/version.js(start.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))
|
||||
Reference in New Issue
Block a user