Compare commits
6 Commits
0f196bdec8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 381a48b5c5 | |||
| 0f26163ad7 | |||
| ce76bb8018 | |||
| efeb3bcc41 | |||
| c24004d6c7 | |||
| 784cacf259 |
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
description: .soon 文件 JSON 契约(与 soonDesign-back / soonWorkerDisk 对齐)
|
||||||
|
globs:
|
||||||
|
- frontend-web/js/design1/**
|
||||||
|
- frontend-web/js/design2/**
|
||||||
|
- frontend-electron/js/design1/**
|
||||||
|
- frontend-electron/js/design2/**
|
||||||
|
- frontend-web/js/common/cloud-files.js
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# .soon 文件格式(不可擅自变更)
|
||||||
|
|
||||||
|
## 权威参考
|
||||||
|
|
||||||
|
- 旧版程序:`soonDesign-back/lib/design1/output.js`、`design2/output.js`(gitignore,本机对照)
|
||||||
|
- 下游消费:`soonWorkerDisk/.../workAdd.vue`(依赖 `frontData`、`backData`、`frontDisplayPic` 等)
|
||||||
|
|
||||||
|
## 保存契约
|
||||||
|
|
||||||
|
`output()` 生成的生产字段与画布字段**必须完整写入**,禁止在 save/upload/session/IDB 路径删除或改名:
|
||||||
|
|
||||||
|
- 生产:`frontData`、`backData`、`frontColorPic`、`backColorPic`、`frontBlackPic`、`backBlackPic`、`frontDisplayPic`、`backDisplayPic`、`flag`、`soonType`
|
||||||
|
- 画布:`f`、`b`、`fo`、`bo`(及旧版可能有的 `fp`、`bp`)
|
||||||
|
|
||||||
|
保存模式:`Object.assign({ f, b, fo, bo, ... }, op1)` → `JSON.stringify`,与 `soonDesign-back` 一致。
|
||||||
|
|
||||||
|
## 禁止
|
||||||
|
|
||||||
|
- 保存/上传/缓存前 `delete` 上述生产字段(历史上 `soonStripDesign1HeavyFields` 类逻辑)
|
||||||
|
- 仅保留 `f/b/fo/bo` 的「精简 .soon」写法
|
||||||
|
|
||||||
|
## 加载
|
||||||
|
|
||||||
|
`soonNormalizeSoonJson` 只允许补全缺失的 `f/b/fo/bo`(兼容极旧文件),**不得 strip** 生产字段,以便打开后另存仍满足下游。
|
||||||
@@ -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)。
|
||||||
|
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -325,12 +325,19 @@ textarea:hover{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.transparent-button{
|
.transparent-button{
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
height: 19px;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 6px;
|
||||||
border: #646E76 solid 1px;
|
border: #646E76 solid 1px;
|
||||||
position: relative;
|
font-size: 12px;
|
||||||
bottom: 2px;
|
line-height: 1;
|
||||||
padding: 3px;
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.transparent-button:hover{
|
.transparent-button:hover{
|
||||||
background-color: #646E76;
|
background-color: #646E76;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 32 KiB |
@@ -1751,7 +1751,7 @@ function initCanvasEvents() {
|
|||||||
const ctx = currentCanvas.getContext();
|
const ctx = currentCanvas.getContext();
|
||||||
let bgObj = (currentCanvas === canvas1) ? background_image1 : background_image2;
|
let bgObj = (currentCanvas === canvas1) ? background_image1 : background_image2;
|
||||||
|
|
||||||
if (bgObj) {
|
if (bgObj && !window._soonSkipDesign1CdMask) {
|
||||||
const width = currentCanvas.width;
|
const width = currentCanvas.width;
|
||||||
const height = currentCanvas.height;
|
const height = currentCanvas.height;
|
||||||
|
|
||||||
@@ -1795,7 +1795,7 @@ function initCanvasEvents() {
|
|||||||
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = "rgba(0, 0, 0, 0.9)"; // 90% 遮罩
|
ctx.fillStyle = '#31373D';
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,118 @@ async function saveImageAsPNG(buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function soonDesign1GetHoleDiameterMm(canvas) {
|
||||||
|
var hole = 24;
|
||||||
|
if (canvas === window.canvas1 && typeof window.bg_version !== 'undefined' && window.bg_version === 2) {
|
||||||
|
hole = 42;
|
||||||
|
}
|
||||||
|
return hole;
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
|
||||||
|
var cx = w / 2;
|
||||||
|
var cy = h / 2;
|
||||||
|
var rOuter = w / 2;
|
||||||
|
var rInner = rOuter * (holeDiameterMm / 120);
|
||||||
|
tctx.save();
|
||||||
|
tctx.globalCompositeOperation = 'destination-in';
|
||||||
|
tctx.fillStyle = '#fff';
|
||||||
|
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.fill('evenodd');
|
||||||
|
tctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc) {
|
||||||
|
var bg = window.background_image1 || window.background_image;
|
||||||
|
if (!canvas || !bg) return '';
|
||||||
|
var srcEl = canvas.lowerCanvasEl;
|
||||||
|
if (!srcEl || !srcEl.getContext) return '';
|
||||||
|
var sx = bg.scaleX == null ? 1 : bg.scaleX;
|
||||||
|
var sy = bg.scaleY == null ? 1 : bg.scaleY;
|
||||||
|
var sw = bg.width * sx;
|
||||||
|
var sh = bg.height * sy;
|
||||||
|
var retina = (typeof canvas.getRetinaScaling === 'function') ? canvas.getRetinaScaling() : 1;
|
||||||
|
outW = outW || Math.round(sw);
|
||||||
|
outH = outH || Math.round(sh);
|
||||||
|
var tmp = document.createElement('canvas');
|
||||||
|
tmp.width = outW;
|
||||||
|
tmp.height = outH;
|
||||||
|
var tctx = tmp.getContext('2d');
|
||||||
|
tctx.drawImage(
|
||||||
|
srcEl,
|
||||||
|
bg.left * retina, bg.top * retina, sw * retina, sh * retina,
|
||||||
|
0, 0, outW, outH
|
||||||
|
);
|
||||||
|
if (clipDisc && format !== 'jpeg') {
|
||||||
|
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
|
||||||
|
}
|
||||||
|
if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality);
|
||||||
|
return tmp.toDataURL('image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1CaptureDiscPng(canvas, outW, outH) {
|
||||||
|
window._soonSkipDesign1CdMask = true;
|
||||||
|
try {
|
||||||
|
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||||
|
canvas.renderAll();
|
||||||
|
return soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||||
|
} finally {
|
||||||
|
window._soonSkipDesign1CdMask = false;
|
||||||
|
canvas.renderAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonNormalizeSoonJson(j) {
|
||||||
|
if (!j || typeof j !== 'object') return null;
|
||||||
|
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) {
|
||||||
$("#base_control").hide();
|
$("#base_control").hide();
|
||||||
@@ -217,13 +329,16 @@ window.display_func = function display_func(img1, img2, img3) {
|
|||||||
left: all_left
|
left: all_left
|
||||||
});
|
});
|
||||||
|
|
||||||
g3.push(img1);
|
var hiddenForPreview = [];
|
||||||
let url3 = new fabric.Group(g3).toDataURL({
|
canvas1.getObjects().forEach(function (o) {
|
||||||
height: 2787,
|
if (o.isGuideLine) {
|
||||||
width: 2787,
|
hiddenForPreview.push(o);
|
||||||
top: all_top,
|
o.visible = false;
|
||||||
left: all_left
|
}
|
||||||
});
|
});
|
||||||
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
|
hiddenForPreview.forEach(function (o) { o.visible = true; });
|
||||||
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
height: 2787,
|
height: 2787,
|
||||||
width: 2787,
|
width: 2787,
|
||||||
@@ -596,12 +711,16 @@ window.output = function output(callback = null, _save = save) {
|
|||||||
left: all_left
|
left: all_left
|
||||||
});
|
});
|
||||||
g3.push(img1);
|
g3.push(img1);
|
||||||
let url3 = new fabric.Group(g3).toDataURL({
|
var hiddenForSave = [];
|
||||||
height: 2787,
|
canvas1.getObjects().forEach(function (o) {
|
||||||
width: 2787,
|
if (o.isGuideLine) {
|
||||||
top: all_top,
|
hiddenForSave.push(o);
|
||||||
left: all_left
|
o.visible = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
|
hiddenForSave.forEach(function (o) { o.visible = true; });
|
||||||
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
height: 2787,
|
height: 2787,
|
||||||
width: 2787,
|
width: 2787,
|
||||||
@@ -1079,14 +1198,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++) {
|
||||||
@@ -1158,6 +1283,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;
|
||||||
@@ -1165,12 +1294,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;
|
||||||
@@ -1260,6 +1399,8 @@ 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);
|
||||||
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({
|
||||||
@@ -1272,7 +1413,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);
|
||||||
@@ -1326,8 +1466,9 @@ 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);
|
||||||
|
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);
|
||||||
@@ -1371,7 +1512,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);
|
||||||
|
|||||||
@@ -781,17 +781,22 @@ $('#down_obj').click(function () {
|
|||||||
$('#set_bg').click(function () {
|
$('#set_bg').click(function () {
|
||||||
//设置为背景
|
//设置为背景
|
||||||
let obj = canvas.getActiveObject()
|
let obj = canvas.getActiveObject()
|
||||||
let idx = getIndex(canvas.getActiveObject())
|
if (!obj) return;
|
||||||
|
let idx = getIndex(obj)
|
||||||
|
if (idx <= 0) return;
|
||||||
const scaleX = 1012 / obj.width
|
const scaleX = 1012 / obj.width
|
||||||
const scaleY = 648 / obj.height
|
const scaleY = 648 / obj.height
|
||||||
// 使用缩放后的实际尺寸计算位置,确保与初始背景图片位置一致
|
// 使用缩放后的实际尺寸计算位置,确保与初始背景图片位置一致
|
||||||
|
// 修正:使用当前活动画布的容器宽度,背面应取 canvas2 的容器宽度
|
||||||
|
const containerW = canvas.width || 1012;
|
||||||
|
const containerH = canvas.height || 648;
|
||||||
const scaledWidth = obj.width * scaleX;
|
const scaledWidth = obj.width * scaleX;
|
||||||
const scaledHeight = obj.height * scaleY;
|
const scaledHeight = obj.height * scaleY;
|
||||||
obj.set({
|
obj.set({
|
||||||
scaleX: scaleX,
|
scaleX: scaleX,
|
||||||
scaleY: scaleY,
|
scaleY: scaleY,
|
||||||
left: ($('#canvas1').width() - scaledWidth) / 2,
|
left: (containerW - scaledWidth) / 2,
|
||||||
top: ($('#canvas1').height() - scaledHeight) / 2
|
top: (containerH - scaledHeight) / 2
|
||||||
})
|
})
|
||||||
$('#text_w').val(1012)
|
$('#text_w').val(1012)
|
||||||
$('#text_h').val(648)
|
$('#text_h').val(648)
|
||||||
@@ -810,12 +815,31 @@ $('#set_bg').click(function () {
|
|||||||
evented: false
|
evented: false
|
||||||
});
|
});
|
||||||
$("#lock_img").attr("src", soonAsset("lock20.png"))
|
$("#lock_img").attr("src", soonAsset("lock20.png"))
|
||||||
canvas.sendToBack(canvas.getActiveObject())
|
|
||||||
canvas.bringForward(canvas.getActiveObject())
|
// 修正:先 remove 当前画布上的旧背景占位图(正面 background_image1 / 背面 background_image2),
|
||||||
|
// 否则 sendToBack 后用户图被压到最底层,仍会被占位图覆盖,肉眼看不到。
|
||||||
|
var oldBg = (canvas === canvas2) ? background_image2 : background_image1;
|
||||||
|
if (oldBg && oldBg !== obj) {
|
||||||
|
canvas.remove(oldBg);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.sendToBack(obj);
|
||||||
|
canvas.bringForward(obj);
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
let objItem = objs.splice(idx - 1, 1)[0]
|
let objItem = objs.splice(idx - 1, 1)[0]
|
||||||
objs.unshift(objItem)
|
objs.unshift(objItem)
|
||||||
|
|
||||||
|
// 同步 background_image 引用:背面时更新 background_image2,正面更新 background_image1
|
||||||
|
if (canvas === canvas2) {
|
||||||
|
background_image2 = obj;
|
||||||
|
window.background_image2 = obj;
|
||||||
|
} else {
|
||||||
|
background_image1 = obj;
|
||||||
|
window.background_image1 = obj;
|
||||||
|
}
|
||||||
|
background_image = obj;
|
||||||
|
window.background_image = obj;
|
||||||
|
|
||||||
// 确保背景图(索引0)的锁定属性被正确设置
|
// 确保背景图(索引0)的锁定属性被正确设置
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let bgObj = canvas.getObjects()[0];
|
let bgObj = canvas.getObjects()[0];
|
||||||
|
|||||||
@@ -355,12 +355,19 @@ textarea:hover{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.transparent-button{
|
.transparent-button{
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
height: 19px;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 40px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 6px;
|
||||||
border: #646E76 solid 1px;
|
border: #646E76 solid 1px;
|
||||||
position: relative;
|
font-size: 12px;
|
||||||
bottom: 2px;
|
line-height: 1;
|
||||||
padding: 3px;
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.transparent-button:hover{
|
.transparent-button:hover{
|
||||||
background-color: #646E76;
|
background-color: #646E76;
|
||||||
|
|||||||
|
After Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
window.SOON_APP_VERSION = '0f26163';
|
||||||
|
})();
|
||||||
@@ -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,避免间歇性空白背景 */
|
||||||
|
|||||||
@@ -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() {
|
||||||
@@ -77,9 +126,66 @@
|
|||||||
return soonEnsureSoonExt(soonSessionSlug(keyOrName));
|
return soonEnsureSoonExt(soonSessionSlug(keyOrName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 缩略图截取框:对齐 background_image 设计边界(design1 光盘外接方 / design2 卡面) */
|
||||||
|
function soonResolveSavePreviewCrop(canvas) {
|
||||||
|
if (!canvas) return null;
|
||||||
|
var bg = null;
|
||||||
|
if (typeof window !== 'undefined' && window.canvas1 === canvas && window.background_image1) {
|
||||||
|
bg = window.background_image1;
|
||||||
|
}
|
||||||
|
if (!bg && typeof canvas.getObjects === 'function') {
|
||||||
|
var objs = canvas.getObjects();
|
||||||
|
if (objs.length && objs[0] && objs[0].type === 'image') bg = objs[0];
|
||||||
|
}
|
||||||
|
if (!bg || !bg.width || !bg.height) return null;
|
||||||
|
var sx = bg.scaleX == null ? 1 : bg.scaleX;
|
||||||
|
var sy = bg.scaleY == null ? 1 : bg.scaleY;
|
||||||
|
var cw = canvas.width || 0;
|
||||||
|
var ch = canvas.height || 0;
|
||||||
|
var w;
|
||||||
|
var h;
|
||||||
|
var left = bg.left;
|
||||||
|
var top = bg.top;
|
||||||
|
w = bg.width * sx;
|
||||||
|
h = bg.height * sy;
|
||||||
|
if (!(w > 0 && h > 0)) return null;
|
||||||
|
if (cw > 0 && ch > 0) {
|
||||||
|
var x1 = Math.max(0, left);
|
||||||
|
var y1 = Math.max(0, top);
|
||||||
|
var x2 = Math.min(cw, left + w);
|
||||||
|
var y2 = Math.min(ch, top + h);
|
||||||
|
w = x2 - x1;
|
||||||
|
h = y2 - y1;
|
||||||
|
if (w <= 0 || h <= 0) return null;
|
||||||
|
left = x1;
|
||||||
|
top = y1;
|
||||||
|
}
|
||||||
|
return { left: left, top: top, width: w, height: h };
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonIsDesign2SaveCanvas(canvas) {
|
||||||
|
return !!(typeof window.soonBuildDesign2SavePreviewPic === 'function'
|
||||||
|
&& window.canvas1 && canvas === window.canvas1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonIsDesign1SaveCanvas(canvas) {
|
||||||
|
return !!(typeof window.soonBuildDesign1SavePreviewPic === 'function'
|
||||||
|
&& window.canvas1 && canvas === window.canvas1);
|
||||||
|
}
|
||||||
|
|
||||||
function soonBuildSavePreviewPic(canvas) {
|
function soonBuildSavePreviewPic(canvas) {
|
||||||
var THUMB_MAX = 102400;
|
var THUMB_MAX = 102400;
|
||||||
if (!canvas || typeof canvas.getObjects !== 'function') return '';
|
if (!canvas || typeof canvas.getObjects !== 'function') return '';
|
||||||
|
if (soonIsDesign2SaveCanvas(canvas)) {
|
||||||
|
var d2Pic = window.soonBuildDesign2SavePreviewPic(canvas);
|
||||||
|
if (d2Pic) return d2Pic;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (soonIsDesign1SaveCanvas(canvas)) {
|
||||||
|
var d1Pic = window.soonBuildDesign1SavePreviewPic(canvas);
|
||||||
|
if (d1Pic) return d1Pic;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
var hidden = [];
|
var hidden = [];
|
||||||
canvas.getObjects().forEach(function (obj) {
|
canvas.getObjects().forEach(function (obj) {
|
||||||
if (obj.isGuideLine) {
|
if (obj.isGuideLine) {
|
||||||
@@ -88,14 +194,21 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (hidden.length) canvas.renderAll();
|
if (hidden.length) canvas.renderAll();
|
||||||
window._soonSkipDesign1CdMask = true;
|
var crop = soonResolveSavePreviewCrop(canvas);
|
||||||
var mult = 0.18;
|
var mult = crop ? 0.28 : 0.18;
|
||||||
var quality = 0.72;
|
var quality = 0.72;
|
||||||
var url = '';
|
var url = '';
|
||||||
try {
|
|
||||||
for (var i = 0; i < 3; i++) {
|
for (var i = 0; i < 3; i++) {
|
||||||
try {
|
try {
|
||||||
var candidate = canvas.toDataURL({ format: 'jpeg', quality: quality, multiplier: mult });
|
var opts = { format: 'jpeg', quality: quality, multiplier: mult };
|
||||||
|
if (crop) {
|
||||||
|
opts.left = crop.left;
|
||||||
|
opts.top = crop.top;
|
||||||
|
opts.width = crop.width;
|
||||||
|
opts.height = crop.height;
|
||||||
|
}
|
||||||
|
canvas.renderAll();
|
||||||
|
var candidate = canvas.toDataURL(opts);
|
||||||
if (candidate && candidate.indexOf('data:image/') === 0 && candidate.length <= THUMB_MAX) {
|
if (candidate && candidate.indexOf('data:image/') === 0 && candidate.length <= THUMB_MAX) {
|
||||||
url = candidate;
|
url = candidate;
|
||||||
break;
|
break;
|
||||||
@@ -104,9 +217,6 @@
|
|||||||
quality -= 0.18;
|
quality -= 0.18;
|
||||||
mult -= 0.04;
|
mult -= 0.04;
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
window._soonSkipDesign1CdMask = false;
|
|
||||||
}
|
|
||||||
hidden.forEach(function (obj) { obj.visible = true; });
|
hidden.forEach(function (obj) { obj.visible = true; });
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
if (typeof window.soonClearFabricOverlay === 'function') window.soonClearFabricOverlay(canvas);
|
if (typeof window.soonClearFabricOverlay === 'function') window.soonClearFabricOverlay(canvas);
|
||||||
@@ -562,38 +672,108 @@
|
|||||||
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,保留生产字段(frontData 等) */
|
||||||
if (!j) return '';
|
function soonNormalizeSoonJson(j) {
|
||||||
|
if (!j || typeof j !== 'object') return null;
|
||||||
|
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 payload = '';
|
||||||
window.soonLocalCacheAndThumb(key, j, { source: 'session', name: displayName });
|
try { payload = JSON.stringify(normalized); } catch (e) { return null; }
|
||||||
try { sessionStorage.setItem(key, 'idb'); } catch (e) { /* ignore */ }
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
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: normalized, 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';
|
||||||
@@ -883,6 +1063,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;
|
||||||
@@ -892,7 +1074,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;
|
||||||
|
|||||||
@@ -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.$ 已存在
|
// 网页端:按顺序动态插入 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {}
|
||||||
@@ -448,21 +455,47 @@ function addBackground() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 将 updateControls 附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
|
// 将 updateControls 附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
|
||||||
window.updateControls = function updateControls() {
|
window._soonSyncingFontControls = false;
|
||||||
// 使用 window.canvas 和 window.background_image 作为后备,确保在 eval() 执行时也能访问
|
|
||||||
|
function soonFontSizeUiVal(fontSizePx) {
|
||||||
|
var d = window.dpi || 600;
|
||||||
|
var uiVal = parseFloat(fontSizePx) * 72 / d;
|
||||||
|
if (uiVal % 0.5 !== 0) uiVal = Math.round(uiVal * 2) / 2;
|
||||||
|
return uiVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonSyncActiveFontSizeInput(activeObj, currentCanvas) {
|
||||||
|
if (!activeObj) return;
|
||||||
|
var idx = getIndex(activeObj, currentCanvas);
|
||||||
|
if (idx <= 0) return;
|
||||||
|
var objData = (window.objs || objs)[idx - 1];
|
||||||
|
if (!objData) return;
|
||||||
|
var uiVal = soonFontSizeUiVal(activeObj.get('fontSize'));
|
||||||
|
if (objData.type === 3) $("#text_size").val(uiVal);
|
||||||
|
else if (objData.type === 4) $("#out_text_size").val(uiVal);
|
||||||
|
else if (objData.type === 10) $("#count_size").val(uiVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.updateControls = function () {
|
||||||
const currentCanvas = window.canvas || canvas;
|
const currentCanvas = window.canvas || canvas;
|
||||||
const currentBackgroundImage = window.background_image || background_image;
|
const currentBackgroundImage = window.background_image || background_image;
|
||||||
if (!currentCanvas || !currentCanvas.getActiveObject()) return;
|
if (!currentCanvas || !currentCanvas.getActiveObject()) return;
|
||||||
$("#text_r").val(currentCanvas.getActiveObject().get("angle").toFixed(0));
|
const activeObj = currentCanvas.getActiveObject();
|
||||||
$("#text_x").val((currentCanvas.getActiveObject().get("left") - currentBackgroundImage.left).toFixed(0));
|
window._soonSyncingFontControls = true;
|
||||||
$("#text_y").val((currentCanvas.getActiveObject().get("top") - currentBackgroundImage.top).toFixed(0));
|
try {
|
||||||
$("#text_h").val((currentCanvas.getActiveObject().get("scaleY") * currentCanvas.getActiveObject().get("height")).toFixed(0));
|
$("#text_r").val(activeObj.get("angle").toFixed(0));
|
||||||
$("#text_w").val((currentCanvas.getActiveObject().get("scaleX") * currentCanvas.getActiveObject().get("width")).toFixed(0));
|
$("#text_x").val((activeObj.get("left") - currentBackgroundImage.left).toFixed(0));
|
||||||
// 根据锁定状态显示图标(使用lockMovementX判断,因为锁定时会设置所有lock属性)
|
$("#text_y").val((activeObj.get("top") - currentBackgroundImage.top).toFixed(0));
|
||||||
if (currentCanvas.getActiveObject().get("lockMovementX")) {
|
$("#text_h").val((activeObj.get("scaleY") * activeObj.get("height")).toFixed(0));
|
||||||
$("#lock_img").attr("src", soonAsset("lock20.png"))//已锁定
|
$("#text_w").val((activeObj.get("scaleX") * activeObj.get("width")).toFixed(0));
|
||||||
|
soonSyncActiveFontSizeInput(activeObj, currentCanvas);
|
||||||
|
if (activeObj.get("lockMovementX")) {
|
||||||
|
$("#lock_img").attr("src", soonAsset("lock20.png"));
|
||||||
} else {
|
} else {
|
||||||
$("#lock_img").attr("src", soonAsset("unlock20.png"))//未锁定
|
$("#lock_img").attr("src", soonAsset("unlock20.png"));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
window._soonSyncingFontControls = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 向后兼容
|
// 向后兼容
|
||||||
@@ -1786,18 +1819,21 @@ function initCanvasEvents() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let target = event.target;
|
let target = event.target;
|
||||||
|
var modAction = event.action || (event.transform && event.transform.action);
|
||||||
|
|
||||||
if ([3, 4, 10].includes(objData.type)) {
|
if ([3, 4, 10].includes(objData.type) && modAction === 'scale') {
|
||||||
target.fontSize = (target.fontSize * target.scaleX).toFixed(0);
|
target.set({
|
||||||
target.scaleX = 1; target.scaleY = 1;
|
fontSize: Math.round(target.fontSize * (target.scaleX || 1)),
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
});
|
||||||
target._clearCache();
|
target._clearCache();
|
||||||
if (objData.type == 3) $("#text_size").val(target.fontSize);
|
|
||||||
if (objData.type == 4) $("#out_text_size").val(target.fontSize);
|
|
||||||
if (objData.type == 10) $("#count_size").val(target.fontSize);
|
|
||||||
currentCanvas.renderAll();
|
currentCanvas.renderAll();
|
||||||
} else if (objData.type == 5) {
|
} else if (objData.type == 5 && modAction === 'scale') {
|
||||||
target.width *= target.scaleX; target.height *= target.scaleY;
|
target.width *= target.scaleX || 1;
|
||||||
target.scaleX = 1; target.scaleY = 1;
|
target.height *= target.scaleY || 1;
|
||||||
|
target.scaleX = 1;
|
||||||
|
target.scaleY = 1;
|
||||||
}
|
}
|
||||||
window.recordState();
|
window.recordState();
|
||||||
// 更新对象列表以同步锁状态
|
// 更新对象列表以同步锁状态
|
||||||
@@ -1860,7 +1896,7 @@ function initCanvasEvents() {
|
|||||||
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
ctx.arc(cx, cy, r_inner, 0, Math.PI * 2, false);
|
||||||
|
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
ctx.fillStyle = "rgba(0, 0, 0, 0.9)"; // 90% 遮罩
|
ctx.fillStyle = '#31373D';
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,104 @@ async function saveImageAsPNG(buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 孔径规则与 core.js after:render 一致 */
|
||||||
|
function soonDesign1GetHoleDiameterMm(canvas) {
|
||||||
|
var hole = 24;
|
||||||
|
if (canvas === window.canvas1 && typeof window.bg_version !== 'undefined' && window.bg_version === 2) {
|
||||||
|
hole = 42;
|
||||||
|
}
|
||||||
|
return hole;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 圆外四角与中心孔 alpha 置 0,保留环形可打印区 */
|
||||||
|
function soonDesign1ApplyDiscAlpha(tctx, w, h, holeDiameterMm) {
|
||||||
|
var cx = w / 2;
|
||||||
|
var cy = h / 2;
|
||||||
|
var rOuter = w / 2;
|
||||||
|
var rInner = rOuter * (holeDiameterMm / 120);
|
||||||
|
tctx.save();
|
||||||
|
tctx.globalCompositeOperation = 'destination-in';
|
||||||
|
tctx.fillStyle = '#fff';
|
||||||
|
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.fill('evenodd');
|
||||||
|
tctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign1CaptureCanvasRect(canvas, outW, outH, format, quality, clipDisc) {
|
||||||
|
var bg = window.background_image1 || window.background_image;
|
||||||
|
if (!canvas || !bg) return '';
|
||||||
|
var srcEl = canvas.lowerCanvasEl;
|
||||||
|
if (!srcEl || !srcEl.getContext) return '';
|
||||||
|
var sx = bg.scaleX == null ? 1 : bg.scaleX;
|
||||||
|
var sy = bg.scaleY == null ? 1 : bg.scaleY;
|
||||||
|
var sw = bg.width * sx;
|
||||||
|
var sh = bg.height * sy;
|
||||||
|
var retina = (typeof canvas.getRetinaScaling === 'function') ? canvas.getRetinaScaling() : 1;
|
||||||
|
outW = outW || Math.round(sw);
|
||||||
|
outH = outH || Math.round(sh);
|
||||||
|
var tmp = document.createElement('canvas');
|
||||||
|
tmp.width = outW;
|
||||||
|
tmp.height = outH;
|
||||||
|
var tctx = tmp.getContext('2d');
|
||||||
|
tctx.drawImage(
|
||||||
|
srcEl,
|
||||||
|
bg.left * retina, bg.top * retina, sw * retina, sh * retina,
|
||||||
|
0, 0, outW, outH
|
||||||
|
);
|
||||||
|
if (clipDisc && format !== 'jpeg') {
|
||||||
|
soonDesign1ApplyDiscAlpha(tctx, outW, outH, soonDesign1GetHoleDiameterMm(canvas));
|
||||||
|
}
|
||||||
|
if (format === 'jpeg') return tmp.toDataURL('image/jpeg', quality == null ? 0.75 : quality);
|
||||||
|
return tmp.toDataURL('image/png');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出/打印:跳过编辑器暗蒙版,按光盘环形裁剪 */
|
||||||
|
function soonDesign1CaptureDiscPng(canvas, outW, outH) {
|
||||||
|
window._soonSkipDesign1CdMask = true;
|
||||||
|
try {
|
||||||
|
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||||
|
canvas.renderAll();
|
||||||
|
return soonDesign1CaptureCanvasRect(canvas, outW, outH, 'png', null, true);
|
||||||
|
} finally {
|
||||||
|
window._soonSkipDesign1CdMask = false;
|
||||||
|
canvas.renderAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonBuildDesign1SavePreviewPic(canvas) {
|
||||||
|
var THUMB_MAX = 102400;
|
||||||
|
var c1 = window.canvas1;
|
||||||
|
if (!canvas || (c1 && canvas !== c1)) return '';
|
||||||
|
var hidden = [];
|
||||||
|
canvas.getObjects().forEach(function (obj) {
|
||||||
|
if (obj.isGuideLine) {
|
||||||
|
hidden.push(obj);
|
||||||
|
obj.visible = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (canvas.discardActiveObject) canvas.discardActiveObject();
|
||||||
|
window._soonSkipDesign1CdMask = true;
|
||||||
|
canvas.renderAll();
|
||||||
|
|
||||||
|
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, true);
|
||||||
|
if (url && url.indexOf('data:image/') === 0 && url.length <= THUMB_MAX) break;
|
||||||
|
url = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
window._soonSkipDesign1CdMask = false;
|
||||||
|
hidden.forEach(function (obj) { obj.visible = true; });
|
||||||
|
canvas.renderAll();
|
||||||
|
if (canvas.requestRenderAll) canvas.requestRenderAll();
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
window.soonBuildDesign1SavePreviewPic = soonBuildDesign1SavePreviewPic;
|
||||||
|
|
||||||
window.display_func = function display_func(img1, img2, img3) {
|
window.display_func = function display_func(img1, img2, img3) {
|
||||||
$("#base_control").hide();
|
$("#base_control").hide();
|
||||||
$("#line_control").hide();
|
$("#line_control").hide();
|
||||||
@@ -217,13 +315,16 @@ window.display_func = function display_func(img1, img2, img3) {
|
|||||||
left: all_left
|
left: all_left
|
||||||
});
|
});
|
||||||
|
|
||||||
g3.push(img1);
|
var hiddenForPreview = [];
|
||||||
let url3 = new fabric.Group(g3).toDataURL({
|
canvas1.getObjects().forEach(function (o) {
|
||||||
height: 2787,
|
if (o.isGuideLine) {
|
||||||
width: 2787,
|
hiddenForPreview.push(o);
|
||||||
top: all_top,
|
o.visible = false;
|
||||||
left: all_left
|
}
|
||||||
});
|
});
|
||||||
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
|
hiddenForPreview.forEach(function (o) { o.visible = true; });
|
||||||
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
height: 2787,
|
height: 2787,
|
||||||
width: 2787,
|
width: 2787,
|
||||||
@@ -614,12 +715,16 @@ window.output = function output(callback = null, _save = save) {
|
|||||||
left: all_left
|
left: all_left
|
||||||
});
|
});
|
||||||
g3.push(img1);
|
g3.push(img1);
|
||||||
let url3 = new fabric.Group(g3).toDataURL({
|
var hiddenForSave = [];
|
||||||
height: 2787,
|
canvas1.getObjects().forEach(function (o) {
|
||||||
width: 2787,
|
if (o.isGuideLine) {
|
||||||
top: all_top,
|
hiddenForSave.push(o);
|
||||||
left: all_left
|
o.visible = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
let url3 = soonDesign1CaptureDiscPng(canvas1, 2787, 2787);
|
||||||
|
hiddenForSave.forEach(function (o) { o.visible = true; });
|
||||||
|
canvas1.renderAll();
|
||||||
let url5 = new fabric.Group(g5).toDataURL({
|
let url5 = new fabric.Group(g5).toDataURL({
|
||||||
height: 2787,
|
height: 2787,
|
||||||
width: 2787,
|
width: 2787,
|
||||||
@@ -1096,22 +1201,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++) {
|
||||||
@@ -1185,6 +1293,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;
|
||||||
@@ -1257,6 +1373,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;
|
||||||
@@ -1361,6 +1478,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();
|
||||||
@@ -1446,6 +1564,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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,13 @@ function initRulers() {
|
|||||||
// background_image在画布坐标系中的left值,需要转换到标尺坐标系
|
// background_image在画布坐标系中的left值,需要转换到标尺坐标系
|
||||||
// 画布从标尺的marginOffset位置开始,所以:标尺0点 = marginOffset + background_image.left (如果存在)
|
// 画布从标尺的marginOffset位置开始,所以:标尺0点 = marginOffset + background_image.left (如果存在)
|
||||||
let originX = marginOffset;
|
let originX = marginOffset;
|
||||||
if (typeof background_image !== 'undefined' && background_image) {
|
var rulerSide = ($('#back_side').hasClass('ui-button-active')) ? canvas2 : canvas1;
|
||||||
|
var anchorX = typeof soonDesign2GetFrameAnchor === 'function'
|
||||||
|
? soonDesign2GetFrameAnchor(rulerSide)
|
||||||
|
: null;
|
||||||
|
if (anchorX) {
|
||||||
|
originX = marginOffset + anchorX.left;
|
||||||
|
} else if (typeof background_image !== 'undefined' && background_image) {
|
||||||
originX = marginOffset + background_image.left;
|
originX = marginOffset + background_image.left;
|
||||||
}
|
}
|
||||||
const startX = 0;
|
const startX = 0;
|
||||||
@@ -257,7 +263,13 @@ function initRulers() {
|
|||||||
// background_image在画布坐标系中的top值,需要转换到标尺坐标系
|
// background_image在画布坐标系中的top值,需要转换到标尺坐标系
|
||||||
// 画布从标尺的marginOffset + rulerHeightOffset位置开始,所以:标尺0点 = marginOffset + rulerHeightOffset + background_image.top (如果存在)
|
// 画布从标尺的marginOffset + rulerHeightOffset位置开始,所以:标尺0点 = marginOffset + rulerHeightOffset + background_image.top (如果存在)
|
||||||
let originY = marginOffset + rulerHeightOffset;
|
let originY = marginOffset + rulerHeightOffset;
|
||||||
if (typeof background_image !== 'undefined' && background_image) {
|
var rulerSideY = ($('#back_side').hasClass('ui-button-active')) ? canvas2 : canvas1;
|
||||||
|
var anchorY = typeof soonDesign2GetFrameAnchor === 'function'
|
||||||
|
? soonDesign2GetFrameAnchor(rulerSideY)
|
||||||
|
: null;
|
||||||
|
if (anchorY) {
|
||||||
|
originY = marginOffset + rulerHeightOffset + anchorY.top;
|
||||||
|
} else if (typeof background_image !== 'undefined' && background_image) {
|
||||||
originY = marginOffset + rulerHeightOffset + background_image.top;
|
originY = marginOffset + rulerHeightOffset + background_image.top;
|
||||||
}
|
}
|
||||||
const startY = 0;
|
const startY = 0;
|
||||||
@@ -355,15 +367,19 @@ function initRulers() {
|
|||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听画布缩放/渲染变化,仅用于重绘标尺
|
canvas1.off('after:render');
|
||||||
// 辅助线交给 Fabric 自己渲染,否则会出现“重影 / 变两三根”的现象
|
canvas2.off('after:render');
|
||||||
canvas1.on('after:render', function () {
|
canvas1.on('after:render', function () {
|
||||||
drawHorizontalRuler();
|
drawHorizontalRuler();
|
||||||
drawVerticalRuler();
|
drawVerticalRuler();
|
||||||
|
soonDesign2DrawCardMask(canvas1, ctx1, document.getElementById('source_front'));
|
||||||
|
soonDesign2DrawTemplateOverlay(canvas1, ctx1, document.getElementById('source_front'));
|
||||||
});
|
});
|
||||||
canvas2.on('after:render', function () {
|
canvas2.on('after:render', function () {
|
||||||
drawHorizontalRuler();
|
drawHorizontalRuler();
|
||||||
drawVerticalRuler();
|
drawVerticalRuler();
|
||||||
|
soonDesign2DrawCardMask(canvas2, ctx2, document.getElementById('source_back'));
|
||||||
|
soonDesign2DrawTemplateOverlay(canvas2, ctx2, document.getElementById('source_back'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- 辅助线功能 (参考 design1) ---
|
// --- 辅助线功能 (参考 design1) ---
|
||||||
@@ -568,6 +584,221 @@ function soonDesign2ScaleBgSources() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function soonDesign2BgOverlayMetrics(imgEl) {
|
||||||
|
if (!imgEl || !imgEl.complete || imgEl.naturalWidth <= 0) return null;
|
||||||
|
var z = zoom || window.zoom || 0.5;
|
||||||
|
var w = parseFloat(imgEl.style.width);
|
||||||
|
if (!w || w <= 0) w = imgEl.naturalWidth * z;
|
||||||
|
var h = parseFloat(imgEl.style.height);
|
||||||
|
if (!h || h <= 0) h = imgEl.naturalHeight * z;
|
||||||
|
return { w: w, h: h };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 卡面区域:与 background_image 对齐(1012×648),fallback 模板线框 */
|
||||||
|
function soonDesign2ResolveCardFrame(fabricCanvas, imgEl) {
|
||||||
|
if (!fabricCanvas) return null;
|
||||||
|
var metaList = (fabricCanvas === canvas2) ? (window.objs2 || objs2) : (window.objs1 || objs1);
|
||||||
|
if (soonDesign2IsUserBgMeta(metaList)) {
|
||||||
|
var anchor = soonDesign2GetFrameAnchor(fabricCanvas);
|
||||||
|
var card = soonDesign2CardSize();
|
||||||
|
if (anchor) {
|
||||||
|
return { left: anchor.left, top: anchor.top, width: card.w, height: card.h };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var bg = (fabricCanvas === canvas1) ? background_image1 : background_image2;
|
||||||
|
if (bg && bg.width) {
|
||||||
|
var sx = bg.scaleX == null ? 1 : bg.scaleX;
|
||||||
|
var sy = bg.scaleY == null ? 1 : bg.scaleY;
|
||||||
|
return {
|
||||||
|
left: bg.left,
|
||||||
|
top: bg.top,
|
||||||
|
width: bg.width * sx,
|
||||||
|
height: bg.height * sy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var cw = fabricCanvas.width || 1012;
|
||||||
|
var ch = fabricCanvas.height || 648;
|
||||||
|
if (imgEl) {
|
||||||
|
var metrics = soonDesign2BgOverlayMetrics(imgEl);
|
||||||
|
if (metrics) {
|
||||||
|
return {
|
||||||
|
left: (cw - metrics.w) / 2,
|
||||||
|
top: (ch - metrics.h) / 2,
|
||||||
|
width: metrics.w,
|
||||||
|
height: metrics.h
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { left: 0, top: 0, width: cw, height: ch };
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2CardSize() {
|
||||||
|
return { w: 1012, h: 648 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2IsUserBgMeta(metaList) {
|
||||||
|
return !!(metaList && metaList.length > 0 && metaList[0] && metaList[0].type === 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2SyncFrameAnchor(fabricCanvas, frame) {
|
||||||
|
if (!frame) return;
|
||||||
|
var pt = { left: frame.left, top: frame.top };
|
||||||
|
if (fabricCanvas === canvas2) {
|
||||||
|
window.soonDesign2FrameAnchor2 = pt;
|
||||||
|
} else {
|
||||||
|
window.soonDesign2FrameAnchor1 = pt;
|
||||||
|
}
|
||||||
|
var active = window.canvas || canvas;
|
||||||
|
if (!active || fabricCanvas === active) {
|
||||||
|
window.soonDesign2FrameAnchor = pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2GetFrameAnchor(fabricCanvas) {
|
||||||
|
if (fabricCanvas === canvas2 && window.soonDesign2FrameAnchor2) {
|
||||||
|
return window.soonDesign2FrameAnchor2;
|
||||||
|
}
|
||||||
|
if (window.soonDesign2FrameAnchor1) {
|
||||||
|
return window.soonDesign2FrameAnchor1;
|
||||||
|
}
|
||||||
|
return window.soonDesign2FrameAnchor || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 用户设为背景的对象:坐标系锚点,属性面板不应再移动它 */
|
||||||
|
function soonDesign2ActiveIsAnchorBg() {
|
||||||
|
var c = window.canvas || canvas;
|
||||||
|
if (!c) return false;
|
||||||
|
var ao = c.getActiveObject();
|
||||||
|
if (!ao) return false;
|
||||||
|
var meta = (c === canvas2) ? (window.objs2 || objs2) : (window.objs1 || objs1);
|
||||||
|
if (c === (window.canvas || canvas)) {
|
||||||
|
meta = window.objs || objs || meta;
|
||||||
|
}
|
||||||
|
if (!soonDesign2IsUserBgMeta(meta)) return false;
|
||||||
|
var bg = (c === canvas2) ? (window.background_image2 || background_image2) : (window.background_image1 || background_image1);
|
||||||
|
return ao === bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2FrameOrigin(fabricCanvas) {
|
||||||
|
var frame = soonDesign2ResolveCardFrame(fabricCanvas, null);
|
||||||
|
if (frame) return frame;
|
||||||
|
var card = soonDesign2CardSize();
|
||||||
|
var cw = (fabricCanvas && fabricCanvas.width) || card.w;
|
||||||
|
var ch = (fabricCanvas && fabricCanvas.height) || card.h;
|
||||||
|
return { left: (cw - card.w) / 2, top: (ch - card.h) / 2, width: card.w, height: card.h };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 用户图铺满卡面框(1012×648),与蒙版/线框同源坐标 */
|
||||||
|
function soonDesign2SnapImageToCardFrame(img, fabricCanvas, frame) {
|
||||||
|
if (!img || !img.width) return frame || soonDesign2FrameOrigin(fabricCanvas);
|
||||||
|
frame = frame || soonDesign2FrameOrigin(fabricCanvas);
|
||||||
|
var card = soonDesign2CardSize();
|
||||||
|
img.set({
|
||||||
|
originX: 'left',
|
||||||
|
originY: 'top',
|
||||||
|
scaleX: card.w / img.width,
|
||||||
|
scaleY: card.h / img.height,
|
||||||
|
left: frame.left,
|
||||||
|
top: frame.top
|
||||||
|
});
|
||||||
|
img.setCoords();
|
||||||
|
soonDesign2SyncFrameAnchor(fabricCanvas, frame);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2ApplyUserBgAsCanvas0(userImg, fabricCanvas, oldBg) {
|
||||||
|
if (!userImg || !fabricCanvas) return;
|
||||||
|
if (oldBg && oldBg !== userImg) {
|
||||||
|
fabricCanvas.remove(oldBg);
|
||||||
|
}
|
||||||
|
fabricCanvas.sendToBack(userImg);
|
||||||
|
if (fabricCanvas === canvas2) {
|
||||||
|
background_image2 = userImg;
|
||||||
|
window.background_image2 = userImg;
|
||||||
|
} else {
|
||||||
|
background_image1 = userImg;
|
||||||
|
window.background_image1 = userImg;
|
||||||
|
}
|
||||||
|
var activeCanvas = window.canvas || canvas;
|
||||||
|
if (fabricCanvas === activeCanvas) {
|
||||||
|
background_image = userImg;
|
||||||
|
window.background_image = userImg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2LockBgObject(bgObj) {
|
||||||
|
if (!bgObj) return;
|
||||||
|
bgObj.set({
|
||||||
|
lockMovementX: true,
|
||||||
|
lockMovementY: true,
|
||||||
|
lockRotation: true,
|
||||||
|
lockScalingX: true,
|
||||||
|
lockScalingY: true,
|
||||||
|
lockSkewingX: true,
|
||||||
|
lockSkewingY: true,
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
hoverCursor: 'default'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开旧文件:把 canvas[0] 的用户背景图贴齐卡框并锁定。
|
||||||
|
* 设计前提:保存 .soon 时 canvas[0] == 用户背景图(set_bg 已把它放到 0 位)。
|
||||||
|
* 之前的实现会 fabricCanvas.remove(all[0]) 并把 all[1](前景)当成新 userBg,导致重打开后
|
||||||
|
* 用户背景图丢失、前景元素被错误地缩放并锁定到卡框大小。本版本不再删除 all[0],
|
||||||
|
* 改为直接把已有的 all[0] 当成用户背景图处理。 */
|
||||||
|
function soonDesign2NormalizeUserBg(fabricCanvas, metaList) {
|
||||||
|
if (!soonDesign2IsUserBgMeta(metaList) || !fabricCanvas) return null;
|
||||||
|
var all = fabricCanvas.getObjects().filter(function (o) { return !o.isGuideLine; });
|
||||||
|
if (!all.length) return null;
|
||||||
|
var userBg = all[0];
|
||||||
|
if (!userBg) return null;
|
||||||
|
var frame = soonDesign2FrameOrigin(fabricCanvas);
|
||||||
|
soonDesign2SnapImageToCardFrame(userBg, fabricCanvas, frame);
|
||||||
|
soonDesign2ApplyUserBgAsCanvas0(userBg, fabricCanvas, null);
|
||||||
|
soonDesign2LockBgObject(userBg);
|
||||||
|
return userBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.soonDesign2CardSize = soonDesign2CardSize;
|
||||||
|
window.soonDesign2IsUserBgMeta = soonDesign2IsUserBgMeta;
|
||||||
|
window.soonDesign2SyncFrameAnchor = soonDesign2SyncFrameAnchor;
|
||||||
|
window.soonDesign2GetFrameAnchor = soonDesign2GetFrameAnchor;
|
||||||
|
window.soonDesign2ActiveIsAnchorBg = soonDesign2ActiveIsAnchorBg;
|
||||||
|
window.soonDesign2FrameOrigin = soonDesign2FrameOrigin;
|
||||||
|
window.soonDesign2SnapImageToCardFrame = soonDesign2SnapImageToCardFrame;
|
||||||
|
window.soonDesign2ApplyUserBgAsCanvas0 = soonDesign2ApplyUserBgAsCanvas0;
|
||||||
|
window.soonDesign2LockBgObject = soonDesign2LockBgObject;
|
||||||
|
window.soonDesign2NormalizeUserBg = soonDesign2NormalizeUserBg;
|
||||||
|
|
||||||
|
function soonDesign2DrawCardMask(fabricCanvas, ctx, imgEl) {
|
||||||
|
if (window._soonSkipDesign2CardMask || !fabricCanvas || !ctx) return;
|
||||||
|
var frame = soonDesign2ResolveCardFrame(fabricCanvas, imgEl);
|
||||||
|
if (!frame) return;
|
||||||
|
var left = frame.left;
|
||||||
|
var top = frame.top;
|
||||||
|
var cardW = frame.width;
|
||||||
|
var cardH = frame.height;
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(0, 0, fabricCanvas.width, fabricCanvas.height);
|
||||||
|
ctx.moveTo(left + cardW, top);
|
||||||
|
ctx.lineTo(left, top);
|
||||||
|
ctx.lineTo(left, top + cardH);
|
||||||
|
ctx.lineTo(left + cardW, top + cardH);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.9)';
|
||||||
|
ctx.fill('evenodd');
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonDesign2DrawTemplateOverlay(fabricCanvas, ctx, imgEl) {
|
||||||
|
if (!window.is_bgi_add || !fabricCanvas || !ctx || !imgEl) return;
|
||||||
|
var frame = soonDesign2ResolveCardFrame(fabricCanvas, imgEl);
|
||||||
|
if (!frame) return;
|
||||||
|
ctx.drawImage(imgEl, frame.left, frame.top, frame.width, frame.height);
|
||||||
|
}
|
||||||
|
|
||||||
function soonDesign2PrepareBgSources(done) {
|
function soonDesign2PrepareBgSources(done) {
|
||||||
var sf = document.getElementById('source_front');
|
var sf = document.getElementById('source_front');
|
||||||
var sb = document.getElementById('source_back');
|
var sb = document.getElementById('source_back');
|
||||||
@@ -647,21 +878,47 @@ canvas2.preserveObjectStacking = true
|
|||||||
// 对象选择和属性更新函数
|
// 对象选择和属性更新函数
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 将 updateControls 附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
|
// 将 updateControls 附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
|
||||||
window.updateControls = function updateControls() {
|
window._soonSyncingFontControls = false;
|
||||||
// 使用 window.canvas 和 window.background_image 作为后备,确保在 eval() 执行时也能访问
|
|
||||||
|
function soonFontSizeUiVal(fontSizePx) {
|
||||||
|
var d = window.dpi || 300;
|
||||||
|
var uiVal = parseFloat(fontSizePx) * 72 / d;
|
||||||
|
if (uiVal % 0.5 !== 0) uiVal = Math.round(uiVal * 2) / 2;
|
||||||
|
return uiVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonSyncActiveFontSizeInput(activeObj, currentCanvas) {
|
||||||
|
if (!activeObj || !currentCanvas) return;
|
||||||
|
var idx = getIndex(activeObj, currentCanvas);
|
||||||
|
if (idx <= 0) return;
|
||||||
|
var objData = (currentCanvas === canvas1 ? objs1 : objs2)[idx - 1];
|
||||||
|
if (!objData) return;
|
||||||
|
var uiVal = soonFontSizeUiVal(activeObj.get('fontSize'));
|
||||||
|
if (objData.type === 3) $("#text_size").val(uiVal);
|
||||||
|
else if (objData.type === 4) $("#out_text_size").val(uiVal);
|
||||||
|
else if (objData.type === 10) $("#count_size").val(uiVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.updateControls = function () {
|
||||||
const currentCanvas = window.canvas || canvas;
|
const currentCanvas = window.canvas || canvas;
|
||||||
const currentBackgroundImage = window.background_image || background_image;
|
const currentBackgroundImage = window.background_image || background_image;
|
||||||
if (!currentCanvas || !currentCanvas.getActiveObject()) return;
|
if (!currentCanvas || !currentCanvas.getActiveObject()) return;
|
||||||
$("#text_r").val(currentCanvas.getActiveObject().get("angle").toFixed(0));
|
const activeObj = currentCanvas.getActiveObject();
|
||||||
$("#text_x").val((currentCanvas.getActiveObject().get("left") - currentBackgroundImage.left).toFixed(0));
|
window._soonSyncingFontControls = true;
|
||||||
$("#text_y").val((currentCanvas.getActiveObject().get("top") - currentBackgroundImage.top).toFixed(0));
|
try {
|
||||||
$("#text_h").val((currentCanvas.getActiveObject().get("scaleY") * currentCanvas.getActiveObject().get("height")).toFixed(0));
|
$("#text_r").val(activeObj.get("angle").toFixed(0));
|
||||||
$("#text_w").val((currentCanvas.getActiveObject().get("scaleX") * currentCanvas.getActiveObject().get("width")).toFixed(0));
|
$("#text_x").val((activeObj.get("left") - currentBackgroundImage.left).toFixed(0));
|
||||||
// 根据锁定状态显示图标(使用lockMovementX判断,因为锁定时会设置所有lock属性)
|
$("#text_y").val((activeObj.get("top") - currentBackgroundImage.top).toFixed(0));
|
||||||
if (currentCanvas.getActiveObject().get("lockMovementX")) {
|
$("#text_h").val((activeObj.get("scaleY") * activeObj.get("height")).toFixed(0));
|
||||||
$("#lock_img").attr("src", soonAsset("lock20.png"))//已锁定
|
$("#text_w").val((activeObj.get("scaleX") * activeObj.get("width")).toFixed(0));
|
||||||
|
soonSyncActiveFontSizeInput(activeObj, currentCanvas);
|
||||||
|
if (activeObj.get("lockMovementX")) {
|
||||||
|
$("#lock_img").attr("src", soonAsset("lock20.png"));
|
||||||
} else {
|
} else {
|
||||||
$("#lock_img").attr("src", soonAsset("unlock20.png"))//未锁定
|
$("#lock_img").attr("src", soonAsset("unlock20.png"));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
window._soonSyncingFontControls = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 向后兼容
|
// 向后兼容
|
||||||
@@ -1052,8 +1309,10 @@ function handleObjectSelected(e) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 获取索引
|
// 3. 解析 objs 下标(用户设为背景时在 canvas[0],不能仅用 getIndex)
|
||||||
let i = getIndex(select_obj, canvas);
|
let metaIdx = typeof soonDesign2ResolveObjMetaIndex === 'function'
|
||||||
|
? soonDesign2ResolveObjMetaIndex(select_obj, canvas)
|
||||||
|
: (getIndex(select_obj, canvas) > 0 ? getIndex(select_obj, canvas) - 1 : -1);
|
||||||
|
|
||||||
// 如果不是通过对象列表选中的,且对象被锁定,则取消选中
|
// 如果不是通过对象列表选中的,且对象被锁定,则取消选中
|
||||||
if (!window._selectingFromList && isObjectLocked(select_obj)) {
|
if (!window._selectingFromList && isObjectLocked(select_obj)) {
|
||||||
@@ -1065,58 +1324,52 @@ function handleObjectSelected(e) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != 0) {
|
if (metaIdx >= 0) {
|
||||||
// --- 选中了普通对象 ---
|
if (objs[metaIdx] && typeof objs[metaIdx].type !== 'undefined') {
|
||||||
--i; // 转换为 objs 数组下标
|
switch (objs[metaIdx].type) {
|
||||||
|
|
||||||
// 安全检查:确保 objs[i] 存在且有 type 属性
|
|
||||||
if (objs[i] && typeof objs[i].type !== 'undefined') {
|
|
||||||
switch (objs[i].type) {
|
|
||||||
case 1: // 图片
|
case 1: // 图片
|
||||||
$("#component_type").text(language_str("img"));
|
$("#component_type").text(language_str("img"));
|
||||||
selectObject(select_obj, i); selectPic(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectPic(select_obj, metaIdx); break;
|
||||||
case 2: // 合成图片
|
case 2: // 合成图片
|
||||||
$("#component_type").text(language_str("simg"));
|
$("#component_type").text(language_str("simg"));
|
||||||
selectObject(select_obj, i); selectOutPic(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectOutPic(select_obj, metaIdx); break;
|
||||||
case 3: // 文本
|
case 3: // 文本
|
||||||
$("#component_type").text(language_str("text"));
|
$("#component_type").text(language_str("text"));
|
||||||
selectObject(select_obj, i); selectText(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectText(select_obj, metaIdx); break;
|
||||||
case 4: // 合成文本
|
case 4: // 合成文本
|
||||||
$("#component_type").text(language_str("stext"));
|
$("#component_type").text(language_str("stext"));
|
||||||
selectObject(select_obj, i); selectOutText(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectOutText(select_obj, metaIdx); break;
|
||||||
case 5: // 矩形
|
case 5: // 矩形
|
||||||
$("#component_type").text(language_str("rect"));
|
$("#component_type").text(language_str("rect"));
|
||||||
selectObject(select_obj, i); selectRect(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectRect(select_obj, metaIdx); break;
|
||||||
case 6: // 圆形
|
case 6: // 圆形
|
||||||
$("#component_type").text(language_str("circ"));
|
$("#component_type").text(language_str("circ"));
|
||||||
selectObject(select_obj, i); selectCircle(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectCircle(select_obj, metaIdx); break;
|
||||||
case 7: // 直线
|
case 7: // 直线
|
||||||
$("#component_type").text(language_str("line"));
|
$("#component_type").text(language_str("line"));
|
||||||
selectObject(select_obj, i); selectLine(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectLine(select_obj, metaIdx); break;
|
||||||
case 8: // 二维码
|
case 8: // 二维码
|
||||||
$("#component_type").text(language_str("qrc"));
|
$("#component_type").text(language_str("qrc"));
|
||||||
selectObject(select_obj, i); selectQrCode(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectQrCode(select_obj, metaIdx); break;
|
||||||
case 9: // 条形码
|
case 9: // 条形码
|
||||||
$("#component_type").text(language_str("barc"));
|
$("#component_type").text(language_str("barc"));
|
||||||
selectObject(select_obj, i); selectBarCode(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectBarCode(select_obj, metaIdx); break;
|
||||||
case 10: // 计数器
|
case 10: // 计数器
|
||||||
$("#component_type").text(language_str("counter"));
|
$("#component_type").text(language_str("counter"));
|
||||||
selectObject(select_obj, i); selectCounter(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectCounter(select_obj, metaIdx); break;
|
||||||
case 11: // 圆形文本
|
case 11: // 圆形文本
|
||||||
$("#component_type").text(language_str("ctext"));
|
$("#component_type").text(language_str("ctext"));
|
||||||
selectObject(select_obj, i); selectCircleText(select_obj, i); break;
|
selectObject(select_obj, metaIdx); selectCircleText(select_obj, metaIdx); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 【关键】这里传入 true!
|
selectObj(metaIdx, true);
|
||||||
// 意思是:因为我已经是在画布上选中它了,selectObj 函数只负责把左边的条目变灰,不要再回头去 setActiveObject 了
|
|
||||||
selectObj(i, true);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// --- 选中了背景 ---
|
// --- 模板背景(canvas[0] 且不在 objs 中)---
|
||||||
$("#base_control, #line_control, #pic_control, #out_pic_control, #text_control, #circle_text_control, #out_text_control, #rect_control, #circle_control, #counter_control, #qrcode_Controll, #barcode_controll").hide();
|
$("#base_control, #line_control, #pic_control, #out_pic_control, #text_control, #circle_text_control, #out_text_control, #rect_control, #circle_control, #counter_control, #qrcode_Controll, #barcode_controll").hide();
|
||||||
selectObj(-1, true);
|
|
||||||
$("#component_type").text(language_str("bg"));
|
$("#component_type").text(language_str("bg"));
|
||||||
|
selectObj(-1, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1363,22 +1616,24 @@ function initCanvasEvents() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let target = event.target;
|
let target = event.target;
|
||||||
|
var modAction = event.action || (event.transform && event.transform.action);
|
||||||
|
|
||||||
if ([3, 4, 10].includes(objData.type)) {
|
if ([3, 4, 10].includes(objData.type) && modAction === 'scale') {
|
||||||
target.fontSize = (target.fontSize * target.scaleX).toFixed(0);
|
target.set({
|
||||||
target.scaleX = 1; target.scaleY = 1;
|
fontSize: Math.round(target.fontSize * (target.scaleX || 1)),
|
||||||
|
scaleX: 1,
|
||||||
|
scaleY: 1,
|
||||||
|
});
|
||||||
target._clearCache();
|
target._clearCache();
|
||||||
if (objData.type == 3) $("#text_size").val(target.fontSize);
|
|
||||||
if (objData.type == 4) $("#out_text_size").val(target.fontSize);
|
|
||||||
if (objData.type == 10) $("#count_size").val(target.fontSize);
|
|
||||||
currentCanvas.renderAll();
|
currentCanvas.renderAll();
|
||||||
updateControls();
|
} else if (objData.type == 5 && modAction === 'scale') {
|
||||||
} else if (objData.type == 5) {
|
target.width *= target.scaleX || 1;
|
||||||
target.width *= target.scaleX; target.height *= target.scaleY;
|
target.height *= target.scaleY || 1;
|
||||||
target.scaleX = 1; target.scaleY = 1;
|
target.scaleX = 1;
|
||||||
updateControls();
|
target.scaleY = 1;
|
||||||
}
|
}
|
||||||
recordState();
|
recordState();
|
||||||
|
updateControls();
|
||||||
// 更新对象列表以同步锁状态
|
// 更新对象列表以同步锁状态
|
||||||
if (typeof window.updateList === 'function') {
|
if (typeof window.updateList === 'function') {
|
||||||
window.updateList();
|
window.updateList();
|
||||||
@@ -1430,8 +1685,10 @@ window.updateList = function updateList() {
|
|||||||
// 检查锁定状态(通过lockMovementX判断,因为锁定时会设置所有lock属性)
|
// 检查锁定状态(通过lockMovementX判断,因为锁定时会设置所有lock属性)
|
||||||
// 注意:索引0是背景图,objs数组是从下标0开始存第1个对象的,所以是 i+1
|
// 注意:索引0是背景图,objs数组是从下标0开始存第1个对象的,所以是 i+1
|
||||||
let isLocked = false;
|
let isLocked = false;
|
||||||
if (currentCanvas.getObjects()[i + 1]) {
|
var canvasObj = typeof window.soonCanvasObjForListIndex === 'function'
|
||||||
let canvasObj = currentCanvas.getObjects()[i + 1];
|
? window.soonCanvasObjForListIndex(i)
|
||||||
|
: currentCanvas.getObjects()[i + 1];
|
||||||
|
if (canvasObj) {
|
||||||
isLocked = canvasObj.get('lockMovementX') === true;
|
isLocked = canvasObj.get('lockMovementX') === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1454,9 +1711,9 @@ window.updateList = function updateList() {
|
|||||||
|
|
||||||
// 重新绑定点击事件(防止 updateList 后点击失效)
|
// 重新绑定点击事件(防止 updateList 后点击失效)
|
||||||
$(".obj_list > div").on("click", function () {
|
$(".obj_list > div").on("click", function () {
|
||||||
let imgId = $(this).find("img").attr("id");
|
let imgId = $(this).find("img[id^='obj']").attr("id");
|
||||||
if (imgId) {
|
if (imgId) {
|
||||||
let idx = parseInt(imgId.replace("obj", ""));
|
let idx = parseInt(imgId.replace("obj", ""), 10);
|
||||||
selectObj(idx);
|
selectObj(idx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1465,6 +1722,40 @@ window.updateList = function updateList() {
|
|||||||
// 标记是否通过对象列表选中(用于区分鼠标点击和列表点击)
|
// 标记是否通过对象列表选中(用于区分鼠标点击和列表点击)
|
||||||
window._selectingFromList = false;
|
window._selectingFromList = false;
|
||||||
|
|
||||||
|
// 对象列表下标 → 画布对象(设为背景后 objs 与 canvas 一一对应)
|
||||||
|
function soonCanvasObjForListIndex(listIdx) {
|
||||||
|
var c = window.canvas || canvas;
|
||||||
|
var metaList = window.objs || objs;
|
||||||
|
if (!c || listIdx < 0 || listIdx >= metaList.length) return null;
|
||||||
|
var all = c.getObjects().filter(function (o) { return !o.isGuideLine; });
|
||||||
|
var bgListedInObjs = metaList.length >= all.length;
|
||||||
|
if (bgListedInObjs) {
|
||||||
|
return all[listIdx] || null;
|
||||||
|
}
|
||||||
|
for (var j = 0; j < all.length; j++) {
|
||||||
|
var o = all[j];
|
||||||
|
var ci = getIndex(o, c);
|
||||||
|
if (ci > 0 && ci - 1 === listIdx) return o;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
window.soonCanvasObjForListIndex = soonCanvasObjForListIndex;
|
||||||
|
|
||||||
|
/** 画布选中对象 → objs 下标;-1 表示模板背景(不在 objs 中) */
|
||||||
|
function soonDesign2ResolveObjMetaIndex(select_obj, fabricCanvas) {
|
||||||
|
if (!select_obj || !fabricCanvas) return -1;
|
||||||
|
var metaList = fabricCanvas === canvas2 ? (window.objs2 || objs2) : (window.objs1 || objs1);
|
||||||
|
if (fabricCanvas === (window.canvas || canvas)) {
|
||||||
|
metaList = window.objs || objs || metaList;
|
||||||
|
}
|
||||||
|
if (!metaList || !metaList.length) return -1;
|
||||||
|
for (var li = 0; li < metaList.length; li++) {
|
||||||
|
if (soonCanvasObjForListIndex(li) === select_obj) return li;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
window.soonDesign2ResolveObjMetaIndex = soonDesign2ResolveObjMetaIndex;
|
||||||
|
|
||||||
// 将 selectObj 附加到 window 对象,确保全局可访问
|
// 将 selectObj 附加到 window 对象,确保全局可访问
|
||||||
window.selectObj = function selectObj(objIndices, isFromCanvas = false) {
|
window.selectObj = function selectObj(objIndices, isFromCanvas = false) {
|
||||||
let indicesArray = (Array.isArray(objIndices)) ? objIndices : ((typeof objIndices === 'number' && objIndices >= 0) ? [objIndices] : []);
|
let indicesArray = (Array.isArray(objIndices)) ? objIndices : ((typeof objIndices === 'number' && objIndices >= 0) ? [objIndices] : []);
|
||||||
@@ -1474,10 +1765,10 @@ window.selectObj = function selectObj(objIndices, isFromCanvas = false) {
|
|||||||
|
|
||||||
// 遍历列表查找并高亮
|
// 遍历列表查找并高亮
|
||||||
$('.obj_list > div').each(function () {
|
$('.obj_list > div').each(function () {
|
||||||
let $img = $(this).find("img");
|
let $img = $(this).find("img[id^='obj']");
|
||||||
if ($img.length === 0) return;
|
if ($img.length === 0) return;
|
||||||
|
|
||||||
let idIndex = parseInt($img.attr("id").replace("obj", ""));
|
let idIndex = parseInt($img.attr("id").replace("obj", ""), 10);
|
||||||
|
|
||||||
if (indicesArray.includes(idIndex)) {
|
if (indicesArray.includes(idIndex)) {
|
||||||
$(this).css("background-color", "#6F7A84");
|
$(this).css("background-color", "#6F7A84");
|
||||||
@@ -1491,7 +1782,7 @@ window.selectObj = function selectObj(objIndices, isFromCanvas = false) {
|
|||||||
|
|
||||||
// 如果是点击列表,触发画布选中
|
// 如果是点击列表,触发画布选中
|
||||||
if (!isFromCanvas && indicesArray.length === 1) {
|
if (!isFromCanvas && indicesArray.length === 1) {
|
||||||
let target = canvas.getObjects()[indicesArray[0] + 1];
|
let target = soonCanvasObjForListIndex(indicesArray[0]);
|
||||||
if (target) {
|
if (target) {
|
||||||
// 检查对象是否被锁定
|
// 检查对象是否被锁定
|
||||||
let isLocked = isObjectLocked(target);
|
let isLocked = isObjectLocked(target);
|
||||||
@@ -1733,6 +2024,15 @@ function soonPlaceBgOnCanvas(image, canvasSelector) {
|
|||||||
image.selectable = false
|
image.selectable = false
|
||||||
image.hoverable = false
|
image.hoverable = false
|
||||||
image.hoverCursor = 'default'
|
image.hoverCursor = 'default'
|
||||||
|
var fc = (canvasSelector === '#canvas2') ? canvas2 : canvas1;
|
||||||
|
if (fc && typeof soonDesign2SyncFrameAnchor === 'function') {
|
||||||
|
soonDesign2SyncFrameAnchor(fc, {
|
||||||
|
left: image.left,
|
||||||
|
top: image.top,
|
||||||
|
width: image.width * (image.get('scaleX') || 1),
|
||||||
|
height: image.height * (image.get('scaleY') || 1)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function soonOpenPendingDesignFile() {
|
function soonOpenPendingDesignFile() {
|
||||||
@@ -2264,7 +2564,12 @@ var out_pic_op = slider.render({
|
|||||||
if (canvas.getActiveObject()) {
|
if (canvas.getActiveObject()) {
|
||||||
canvas.getActiveObject().set('opacity', 1 - value / 100);
|
canvas.getActiveObject().set('opacity', 1 - value / 100);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
|
window._soonSyncingFontControls = true;
|
||||||
|
try {
|
||||||
updateControls();
|
updateControls();
|
||||||
|
} finally {
|
||||||
|
window._soonSyncingFontControls = false;
|
||||||
|
}
|
||||||
recordState();
|
recordState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2279,7 +2584,12 @@ var pic_op = slider.render({
|
|||||||
if (canvas.getActiveObject()) {
|
if (canvas.getActiveObject()) {
|
||||||
canvas.getActiveObject().set('opacity', 1 - value / 100);
|
canvas.getActiveObject().set('opacity', 1 - value / 100);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
|
window._soonSyncingFontControls = true;
|
||||||
|
try {
|
||||||
updateControls();
|
updateControls();
|
||||||
|
} finally {
|
||||||
|
window._soonSyncingFontControls = false;
|
||||||
|
}
|
||||||
recordState();
|
recordState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,99 @@
|
|||||||
|
/** design2 正面预览/缩略图:1012×648 视口,与 display_func 中 url3 一致 */
|
||||||
|
function soonCollectDesign2FrontPreview(canvas, bgImage, objMetaList) {
|
||||||
|
if (!canvas || !bgImage || typeof canvas.getObjects !== 'function') return null;
|
||||||
|
var list = canvas.getObjects().filter(function (obj) { return !obj.isGuideLine; });
|
||||||
|
var g3 = [];
|
||||||
|
var all_left = 0;
|
||||||
|
var all_top = 0;
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
var item = list[i];
|
||||||
|
var idx = getIndex(item, canvas);
|
||||||
|
var metaIdx = typeof window.soonDesign2ResolveObjMetaIndex === 'function'
|
||||||
|
? window.soonDesign2ResolveObjMetaIndex(item, canvas)
|
||||||
|
: (idx > 0 ? idx - 1 : -1);
|
||||||
|
if (metaIdx < 0 && idx !== 0) continue;
|
||||||
|
if (metaIdx >= 0 && (!objMetaList[metaIdx] || typeof objMetaList[metaIdx].type === 'undefined')) continue;
|
||||||
|
if (getCoordsMinX(bgImage.getCoords()) - getCoordsMinX(item.getCoords()) > all_left) {
|
||||||
|
all_left = -(getCoordsMinX(item.getCoords()) - getCoordsMinX(bgImage.getCoords()));
|
||||||
|
}
|
||||||
|
if (getCoordsMinY(bgImage.getCoords()) - getCoordsMinY(item.getCoords()) > all_top) {
|
||||||
|
all_top = -(getCoordsMinY(item.getCoords()) - getCoordsMinY(bgImage.getCoords()));
|
||||||
|
}
|
||||||
|
g3.push(fabric.util.object.clone(item));
|
||||||
|
}
|
||||||
|
return { g3: g3, all_left: all_left, all_top: all_top };
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonExportDesign2PreviewDataUrl(g3, all_left, all_top, quality, multiplier) {
|
||||||
|
var z = (typeof zoom !== 'undefined' ? zoom : null) || window.zoom || 1;
|
||||||
|
var opts = {
|
||||||
|
height: 648,
|
||||||
|
width: 1012,
|
||||||
|
top: all_top / z,
|
||||||
|
left: all_left / z,
|
||||||
|
format: 'jpeg',
|
||||||
|
quality: quality == null ? 0.72 : quality
|
||||||
|
};
|
||||||
|
if (multiplier != null && multiplier > 0) opts.multiplier = multiplier;
|
||||||
|
return new fabric.Group(g3).toDataURL(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 缩略图/预览:叠加系统卡面模板 front_bg(与编辑区 source_front 一致) */
|
||||||
|
function soonDesign2AppendTemplateOverlayToGroup(g3, canvas, bgImage) {
|
||||||
|
if (!g3 || !canvas) return false;
|
||||||
|
var sf = document.getElementById('source_front');
|
||||||
|
if (!sf || !(sf.complete && sf.naturalWidth > 0)) return false;
|
||||||
|
var frame = typeof window.soonDesign2FrameOrigin === 'function'
|
||||||
|
? window.soonDesign2FrameOrigin(canvas)
|
||||||
|
: null;
|
||||||
|
if (!frame && bgImage && bgImage.width) {
|
||||||
|
var sx = bgImage.scaleX == null ? 1 : bgImage.scaleX;
|
||||||
|
var sy = bgImage.scaleY == null ? 1 : bgImage.scaleY;
|
||||||
|
frame = { left: bgImage.left, top: bgImage.top, width: bgImage.width * sx, height: bgImage.height * sy };
|
||||||
|
}
|
||||||
|
if (!frame || !(frame.width > 0 && frame.height > 0)) return false;
|
||||||
|
g3.push(new fabric.Image(sf, {
|
||||||
|
left: frame.left,
|
||||||
|
top: frame.top,
|
||||||
|
originX: 'left',
|
||||||
|
originY: 'top',
|
||||||
|
scaleX: frame.width / sf.naturalWidth,
|
||||||
|
scaleY: frame.height / sf.naturalHeight
|
||||||
|
}));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function soonBuildDesign2SavePreviewPic(canvas) {
|
||||||
|
var THUMB_MAX = 102400;
|
||||||
|
var c1 = window.canvas1;
|
||||||
|
if (!canvas || (c1 && canvas !== c1)) return '';
|
||||||
|
var bg = window.background_image1 || window.background_image;
|
||||||
|
var meta = window.objs1;
|
||||||
|
if (!bg || !Array.isArray(meta)) return '';
|
||||||
|
if (typeof canvas.renderAll === 'function') canvas.renderAll();
|
||||||
|
var pack = soonCollectDesign2FrontPreview(canvas, bg, meta);
|
||||||
|
if (!pack || !pack.g3.length) return '';
|
||||||
|
soonDesign2AppendTemplateOverlayToGroup(pack.g3, canvas, bg);
|
||||||
|
var quality = 0.72;
|
||||||
|
var mult = 0.55;
|
||||||
|
var url = '';
|
||||||
|
for (var i = 0; i < 4; i++) {
|
||||||
|
try {
|
||||||
|
url = soonExportDesign2PreviewDataUrl(pack.g3, pack.all_left, pack.all_top, quality, mult);
|
||||||
|
if (url && url.indexOf('data:image/') === 0 && url.length <= THUMB_MAX) return url;
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
quality -= 0.16;
|
||||||
|
mult -= 0.08;
|
||||||
|
if (mult < 0.28) mult = 0.28;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
url = soonExportDesign2PreviewDataUrl(pack.g3, pack.all_left, pack.all_top, 0.32, 0.28);
|
||||||
|
if (url && url.indexOf('data:image/') === 0) return url;
|
||||||
|
} catch (e2) { /* ignore */ }
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
window.soonBuildDesign2SavePreviewPic = soonBuildDesign2SavePreviewPic;
|
||||||
|
|
||||||
window.display_func = function display_func(img1, img2, img3) {
|
window.display_func = function display_func(img1, img2, img3) {
|
||||||
$('#base_control').hide()
|
$('#base_control').hide()
|
||||||
$('#line_control').hide()
|
$('#line_control').hide()
|
||||||
@@ -1046,10 +1142,12 @@ window.cleanupSrcFields = function cleanupSrcFields(jsonObjects, objsArray) {
|
|||||||
let obj = jsonObjects[i];
|
let obj = jsonObjects[i];
|
||||||
// 只处理 fabric.Image 对象且有 src 字段的
|
// 只处理 fabric.Image 对象且有 src 字段的
|
||||||
if (obj && obj.type === 'image' && obj.src) {
|
if (obj && obj.type === 'image' && obj.src) {
|
||||||
// 背景图片(索引 0)- 删除 src
|
// 模板背景(索引 0)删 src;用户设为背景时 fo[0] 为 type 1,须保留
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
|
if (!(objsArray[0] && objsArray[0].type === 1)) {
|
||||||
delete obj.src;
|
delete obj.src;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 其他对象,通过 objs 数组判断类型
|
// 其他对象,通过 objs 数组判断类型
|
||||||
else if (i - 1 < objsArray.length) {
|
else if (i - 1 < objsArray.length) {
|
||||||
let objMeta = objsArray[i - 1];
|
let objMeta = objsArray[i - 1];
|
||||||
@@ -1073,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;
|
||||||
@@ -1174,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();
|
||||||
@@ -1259,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();
|
||||||
@@ -1331,13 +1432,31 @@ 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;
|
||||||
|
}
|
||||||
|
// 防御性:j.b 缺失时给一个空画布占位(避免 canvas2.loadFromJSON 报 undefined)
|
||||||
|
if (!j.b || typeof j.b !== 'object' || !Array.isArray(j.b.objects)) {
|
||||||
|
if (typeof console !== 'undefined' && console.warn) {
|
||||||
|
console.warn('[design2.openFile] j.b 缺失,自动以空背面回退。文件:', file);
|
||||||
|
}
|
||||||
|
j.b = { version: '4.6.0', objects: [], hoverCursor: 'move' };
|
||||||
|
}
|
||||||
|
function runOpen() {
|
||||||
|
if (!background_image) {
|
||||||
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
|
if (typeof window.soonReportOpenLoadError === 'function') window.soonReportOpenLoadError('模板/文件加载失败');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openAs.name = file;
|
openAs.name = file;
|
||||||
|
const fo = j.fo || []
|
||||||
|
const bo = j.bo || []
|
||||||
var left = background_image.left
|
var left = background_image.left
|
||||||
let top = background_image.top
|
let top = background_image.top
|
||||||
let new_left = j.f.objects[0].left
|
let new_left = j.f.objects[0].left
|
||||||
@@ -1350,18 +1469,25 @@ window.openFile = function open(file, jAlready) {
|
|||||||
o.left = o.left - new_left + left
|
o.left = o.left - new_left + left
|
||||||
o.top = o.top - new_top + top
|
o.top = o.top - new_top + top
|
||||||
}
|
}
|
||||||
// 如果背景图片没有src,从路径恢复(因为保存时删除了src以减小文件大小)
|
var isUserBgFront = typeof window.soonDesign2IsUserBgMeta === 'function'
|
||||||
if (j.f.objects[0] && !j.f.objects[0].src) {
|
? window.soonDesign2IsUserBgMeta(fo)
|
||||||
|
: (fo[0] && fo[0].type === 1)
|
||||||
|
var isUserBgBack = typeof window.soonDesign2IsUserBgMeta === 'function'
|
||||||
|
? window.soonDesign2IsUserBgMeta(bo)
|
||||||
|
: (bo[0] && bo[0].type === 1)
|
||||||
|
if (j.f.objects[0] && !j.f.objects[0].src && !isUserBgFront) {
|
||||||
|
j.f.objects[0].src = soonAsset('bg_front_2.png')
|
||||||
|
} else if (j.f.objects[0] && !j.f.objects[0].src && isUserBgFront && j.f.objects.length > 1) {
|
||||||
j.f.objects[0].src = soonAsset('bg_front_2.png')
|
j.f.objects[0].src = soonAsset('bg_front_2.png')
|
||||||
}
|
}
|
||||||
if (j.b.objects[0] && !j.b.objects[0].src) {
|
if (j.b.objects[0] && !j.b.objects[0].src && !isUserBgBack) {
|
||||||
|
j.b.objects[0].src = soonAsset('bg_back_2.png')
|
||||||
|
} else if (j.b.objects[0] && !j.b.objects[0].src && isUserBgBack && j.b.objects.length > 1) {
|
||||||
j.b.objects[0].src = soonAsset('bg_back_2.png')
|
j.b.objects[0].src = soonAsset('bg_back_2.png')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为二维码和条形码对象创建临时的 src(Fabric.js 需要 src 来创建 Image 对象)
|
// 为二维码和条形码对象创建临时的 src(Fabric.js 需要 src 来创建 Image 对象)
|
||||||
// 加载完成后会重新生成,所以这里只需要一个占位符
|
// 加载完成后会重新生成,所以这里只需要一个占位符
|
||||||
const fo = j.fo || []
|
|
||||||
const bo = j.bo || []
|
|
||||||
const placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='
|
const placeholder = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='
|
||||||
|
|
||||||
// 为正面二维码、条形码和合成图片创建临时 src
|
// 为正面二维码、条形码和合成图片创建临时 src
|
||||||
@@ -1492,6 +1618,9 @@ window.openFile = function open(file, jAlready) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canvas1.loadFromJSON(j.f, function () {
|
canvas1.loadFromJSON(j.f, function () {
|
||||||
|
if (typeof window.soonDesign2NormalizeUserBg === 'function') {
|
||||||
|
window.soonDesign2NormalizeUserBg(canvas1, fo);
|
||||||
|
}
|
||||||
// 加载后解锁所有对象(背景除外)
|
// 加载后解锁所有对象(背景除外)
|
||||||
if (typeof window.unlockAllObjects === 'function') {
|
if (typeof window.unlockAllObjects === 'function') {
|
||||||
window.unlockAllObjects(canvas1);
|
window.unlockAllObjects(canvas1);
|
||||||
@@ -1508,6 +1637,9 @@ window.openFile = function open(file, jAlready) {
|
|||||||
saveInitialState()
|
saveInitialState()
|
||||||
})
|
})
|
||||||
canvas2.loadFromJSON(j.b, function () {
|
canvas2.loadFromJSON(j.b, function () {
|
||||||
|
if (typeof window.soonDesign2NormalizeUserBg === 'function') {
|
||||||
|
window.soonDesign2NormalizeUserBg(canvas2, bo);
|
||||||
|
}
|
||||||
// 加载后解锁所有对象(背景除外)
|
// 加载后解锁所有对象(背景除外)
|
||||||
if (typeof window.unlockAllObjects === 'function') {
|
if (typeof window.unlockAllObjects === 'function') {
|
||||||
window.unlockAllObjects(canvas2);
|
window.unlockAllObjects(canvas2);
|
||||||
@@ -1523,6 +1655,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);
|
||||||
|
|||||||
@@ -365,6 +365,8 @@ $('#version_change').click(function () {
|
|||||||
if (typeof window.soonDesign2ScaleBgSources === 'function') {
|
if (typeof window.soonDesign2ScaleBgSources === 'function') {
|
||||||
window.soonDesign2ScaleBgSources()
|
window.soonDesign2ScaleBgSources()
|
||||||
}
|
}
|
||||||
|
canvas1.requestRenderAll()
|
||||||
|
canvas2.requestRenderAll()
|
||||||
}
|
}
|
||||||
sf.onerror = sf.onload
|
sf.onerror = sf.onload
|
||||||
sf.src = soonAsset('front_bg' + bg_version + '_2.png')
|
sf.src = soonAsset('front_bg' + bg_version + '_2.png')
|
||||||
@@ -785,62 +787,41 @@ $('#down_obj').click(function () {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
$('#set_bg').click(function () {
|
$('#set_bg').click(function () {
|
||||||
//设置为背景
|
|
||||||
let obj = canvas.getActiveObject()
|
let obj = canvas.getActiveObject()
|
||||||
let idx = getIndex(canvas.getActiveObject())
|
let idx = getIndex(obj)
|
||||||
const scaleX = 1012 / obj.width
|
// 旧背景:取当前画布对应的占位背景图(正面->background_image1,背面->background_image2),
|
||||||
const scaleY = 648 / obj.height
|
// 背面场景下 background_image 已被 #back_side 切换为 background_image2。
|
||||||
// 使用缩放后的实际尺寸计算位置,确保与初始背景图片位置一致
|
var oldBg = (canvas === canvas2) ? background_image2 : background_image1
|
||||||
const scaledWidth = obj.width * scaleX;
|
if (!oldBg) oldBg = background_image
|
||||||
const scaledHeight = obj.height * scaleY;
|
var frame = typeof window.soonDesign2FrameOrigin === 'function'
|
||||||
obj.set({
|
? window.soonDesign2FrameOrigin(canvas)
|
||||||
scaleX: scaleX,
|
: { left: oldBg.left, top: oldBg.top }
|
||||||
scaleY: scaleY,
|
if (typeof window.soonDesign2SnapImageToCardFrame === 'function') {
|
||||||
left: ($('#canvas1').width() - scaledWidth) / 2,
|
window.soonDesign2SnapImageToCardFrame(obj, canvas, frame)
|
||||||
top: ($('#canvas1').height() - scaledHeight) / 2
|
}
|
||||||
})
|
var card = typeof window.soonDesign2CardSize === 'function'
|
||||||
$('#text_w').val(1012)
|
? window.soonDesign2CardSize()
|
||||||
$('#text_h').val(648)
|
: { w: 1012, h: 648 }
|
||||||
|
window._soonSyncingFontControls = true;
|
||||||
|
try {
|
||||||
|
$('#text_w').val(card.w)
|
||||||
|
$('#text_h').val(card.h)
|
||||||
$('#text_x').val(0)
|
$('#text_x').val(0)
|
||||||
$('#text_y').val(0)
|
$('#text_y').val(0)
|
||||||
// 设为背景时,锁定所有编辑操作,并设置为不可选中
|
} finally {
|
||||||
obj.set({
|
window._soonSyncingFontControls = false;
|
||||||
lockMovementX: true,
|
}
|
||||||
lockMovementY: true,
|
if (typeof window.soonDesign2LockBgObject === 'function') {
|
||||||
lockRotation: true,
|
window.soonDesign2LockBgObject(obj)
|
||||||
lockScalingX: true,
|
}
|
||||||
lockScalingY: true,
|
|
||||||
lockSkewingX: true,
|
|
||||||
lockSkewingY: true,
|
|
||||||
selectable: false,
|
|
||||||
evented: false
|
|
||||||
});
|
|
||||||
$("#lock_img").attr("src", soonAsset("lock20.png"))
|
$("#lock_img").attr("src", soonAsset("lock20.png"))
|
||||||
canvas.sendToBack(canvas.getActiveObject())
|
if (typeof window.soonDesign2ApplyUserBgAsCanvas0 === 'function') {
|
||||||
canvas.bringForward(canvas.getActiveObject())
|
window.soonDesign2ApplyUserBgAsCanvas0(obj, canvas, oldBg)
|
||||||
|
}
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
let objItem = objs.splice(idx - 1, 1)[0]
|
let objItem = objs.splice(idx - 1, 1)[0]
|
||||||
objs.unshift(objItem)
|
objs.unshift(objItem)
|
||||||
|
|
||||||
// 确保背景图(索引0)的锁定属性被正确设置
|
|
||||||
setTimeout(() => {
|
|
||||||
let bgObj = canvas.getObjects()[0];
|
|
||||||
if (bgObj) {
|
|
||||||
bgObj.set({
|
|
||||||
lockMovementX: true,
|
|
||||||
lockMovementY: true,
|
|
||||||
lockRotation: true,
|
|
||||||
lockScalingX: true,
|
|
||||||
lockScalingY: true,
|
|
||||||
lockSkewingX: true,
|
|
||||||
lockSkewingY: true,
|
|
||||||
selectable: false,
|
|
||||||
evented: false
|
|
||||||
});
|
|
||||||
canvas.renderAll();
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
recordState()
|
recordState()
|
||||||
if (typeof window.updateList === 'function') {
|
if (typeof window.updateList === 'function') {
|
||||||
window.updateList();
|
window.updateList();
|
||||||
@@ -1300,22 +1281,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;
|
||||||
@@ -1355,12 +1320,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;
|
||||||
@@ -1385,23 +1359,6 @@ function selectObject(target, index) {
|
|||||||
$('#base_control').show()
|
$('#base_control').show()
|
||||||
//$("#text_x").val((target.left - background_image.left).toFixed(1));
|
//$("#text_x").val((target.left - background_image.left).toFixed(1));
|
||||||
}
|
}
|
||||||
function updateControls() {
|
|
||||||
// 检查是否有选中对象,如果没有则直接返回
|
|
||||||
if (!canvas || !canvas.getActiveObject()) return;
|
|
||||||
|
|
||||||
const activeObj = canvas.getActiveObject();
|
|
||||||
$('#text_r').val(activeObj.get('angle').toFixed(0))
|
|
||||||
$('#text_x').val((activeObj.get('left') - background_image.left).toFixed(0))
|
|
||||||
$('#text_y').val((activeObj.get('top') - background_image.top).toFixed(0))
|
|
||||||
$('#text_h').val((activeObj.get('scaleY') * activeObj.get('height')).toFixed(0))
|
|
||||||
$('#text_w').val((activeObj.get('scaleX') * activeObj.get('width')).toFixed(0))
|
|
||||||
// 根据锁定状态显示图标(使用lockMovementX判断,因为锁定时会设置所有lock属性)
|
|
||||||
if (activeObj.get('lockMovementX')) {
|
|
||||||
$('#lock_img').attr('src', soonAsset('lock20.png')) //已锁定
|
|
||||||
} else {
|
|
||||||
$('#lock_img').attr('src', soonAsset('unlock20.png')) //未锁定
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function checkName(name) {
|
function checkName(name) {
|
||||||
//检查字段名是否重复了
|
//检查字段名是否重复了
|
||||||
for (let item of objs1) {
|
for (let item of objs1) {
|
||||||
@@ -1444,8 +1401,8 @@ function selectPic(target, index) {
|
|||||||
|
|
||||||
let activeObj = canvas.getActiveObject();
|
let activeObj = canvas.getActiveObject();
|
||||||
|
|
||||||
// 检查对象是否被锁定
|
// 列表选中时允许锁定对象保持选中;画布点击仍不可选
|
||||||
if (isObjectLocked(activeObj)) {
|
if (!window._selectingFromList && isObjectLocked(activeObj)) {
|
||||||
canvas.discardActiveObject();
|
canvas.discardActiveObject();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
return;
|
return;
|
||||||
@@ -2092,6 +2049,8 @@ function getCoordsMaxY(acoords) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#text_y').bind('input propertychange', function () {
|
$('#text_y').bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
|
if (typeof window.soonDesign2ActiveIsAnchorBg === 'function' && window.soonDesign2ActiveIsAnchorBg()) return;
|
||||||
if ($('#text_y').val() == '') {
|
if ($('#text_y').val() == '') {
|
||||||
$('#text_y').val(0)
|
$('#text_y').val(0)
|
||||||
}
|
}
|
||||||
@@ -2104,6 +2063,8 @@ $('#text_y').bind('input propertychange', function () {
|
|||||||
recordState()
|
recordState()
|
||||||
})
|
})
|
||||||
$('#text_x').bind('input propertychange', function () {
|
$('#text_x').bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
|
if (typeof window.soonDesign2ActiveIsAnchorBg === 'function' && window.soonDesign2ActiveIsAnchorBg()) return;
|
||||||
if ($('#text_x').val() == '') {
|
if ($('#text_x').val() == '') {
|
||||||
$('#text_x').val(0)
|
$('#text_x').val(0)
|
||||||
}
|
}
|
||||||
@@ -2116,11 +2077,17 @@ $('#text_x').bind('input propertychange', function () {
|
|||||||
recordState()
|
recordState()
|
||||||
})
|
})
|
||||||
$('#text_w').bind('input propertychange', function () {
|
$('#text_w').bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
|
if (typeof window.soonDesign2ActiveIsAnchorBg === 'function' && window.soonDesign2ActiveIsAnchorBg()) return;
|
||||||
let val = '0'
|
let val = '0'
|
||||||
if ($('#text_w').val() != '') {
|
if ($('#text_w').val() != '') {
|
||||||
val = $('#text_w').val()
|
val = $('#text_w').val()
|
||||||
}
|
}
|
||||||
let type = objs[getIndex(canvas.getActiveObject()) - 1].type
|
var metaIdx = typeof window.soonDesign2ResolveObjMetaIndex === 'function'
|
||||||
|
? window.soonDesign2ResolveObjMetaIndex(canvas.getActiveObject(), canvas)
|
||||||
|
: getIndex(canvas.getActiveObject()) - 1;
|
||||||
|
if (metaIdx < 0 || !objs[metaIdx]) return;
|
||||||
|
let type = objs[metaIdx].type
|
||||||
if (type == 3 || type == 4) {
|
if (type == 3 || type == 4) {
|
||||||
canvas.getActiveObject().set('width', parseInt(val))
|
canvas.getActiveObject().set('width', parseInt(val))
|
||||||
} else {
|
} else {
|
||||||
@@ -2131,8 +2098,13 @@ $('#text_w').bind('input propertychange', function () {
|
|||||||
recordState()
|
recordState()
|
||||||
})
|
})
|
||||||
$('#text_h').bind('input propertychange', function () {
|
$('#text_h').bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
|
if (typeof window.soonDesign2ActiveIsAnchorBg === 'function' && window.soonDesign2ActiveIsAnchorBg()) return;
|
||||||
let idx = getIndex(canvas.getActiveObject())
|
let idx = getIndex(canvas.getActiveObject())
|
||||||
if (objs[idx - 1].type == 7) {
|
var metaIdx = typeof window.soonDesign2ResolveObjMetaIndex === 'function'
|
||||||
|
? window.soonDesign2ResolveObjMetaIndex(canvas.getActiveObject(), canvas)
|
||||||
|
: idx - 1;
|
||||||
|
if (metaIdx >= 0 && objs[metaIdx] && objs[metaIdx].type == 7) {
|
||||||
if ($('#text_h').val() == '') {
|
if ($('#text_h').val() == '') {
|
||||||
$('#text_h').val(canvas.getActiveObject().get('strokeWidth'))
|
$('#text_h').val(canvas.getActiveObject().get('strokeWidth'))
|
||||||
}
|
}
|
||||||
@@ -2144,7 +2116,8 @@ $('#text_h').bind('input propertychange', function () {
|
|||||||
if ($('#text_h').val() != '') {
|
if ($('#text_h').val() != '') {
|
||||||
val = $('#text_h').val()
|
val = $('#text_h').val()
|
||||||
}
|
}
|
||||||
let type = objs[getIndex(canvas.getActiveObject()) - 1].type
|
if (metaIdx < 0 || !objs[metaIdx]) return;
|
||||||
|
let type = objs[metaIdx].type
|
||||||
if (type == 3 || type == 4) {
|
if (type == 3 || type == 4) {
|
||||||
canvas.getActiveObject().set('height', parseInt(val))
|
canvas.getActiveObject().set('height', parseInt(val))
|
||||||
} else {
|
} else {
|
||||||
@@ -2155,6 +2128,7 @@ $('#text_h').bind('input propertychange', function () {
|
|||||||
recordState()
|
recordState()
|
||||||
})
|
})
|
||||||
$('#text_r').bind('input propertychange', function () {
|
$('#text_r').bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
if ($('#text_r').val() == '') {
|
if ($('#text_r').val() == '') {
|
||||||
$('#text_r').val('0')
|
$('#text_r').val('0')
|
||||||
}
|
}
|
||||||
@@ -2257,6 +2231,7 @@ $('#out_text_back_color').bind('input propertychange', function () {
|
|||||||
recordState()
|
recordState()
|
||||||
})
|
})
|
||||||
$("#text_size").bind('input propertychange', function () {
|
$("#text_size").bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
let val = 1
|
let val = 1
|
||||||
if (parseFloat($("#text_size").val())) {
|
if (parseFloat($("#text_size").val())) {
|
||||||
val = parseFloat($("#text_size").val())
|
val = parseFloat($("#text_size").val())
|
||||||
@@ -2312,6 +2287,7 @@ $('#out_text_color').bind('input propertychange', function () {
|
|||||||
recordState()
|
recordState()
|
||||||
})
|
})
|
||||||
$("#out_text_size").bind('input propertychange', function () {
|
$("#out_text_size").bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
let val = 1
|
let val = 1
|
||||||
if (parseFloat($("#out_text_size").val())) {
|
if (parseFloat($("#out_text_size").val())) {
|
||||||
val = parseFloat($("#out_text_size").val())
|
val = parseFloat($("#out_text_size").val())
|
||||||
@@ -2417,6 +2393,7 @@ $('#counter_color').bind('input propertychange', function () {
|
|||||||
recordState()
|
recordState()
|
||||||
})
|
})
|
||||||
$("#count_size").bind('input propertychange', function () {
|
$("#count_size").bind('input propertychange', function () {
|
||||||
|
if (window._soonSyncingFontControls) return;
|
||||||
let val = 1
|
let val = 1
|
||||||
if (parseFloat($("#count_size").val())) {
|
if (parseFloat($("#count_size").val())) {
|
||||||
val = parseFloat($("#count_size").val())
|
val = parseFloat($("#count_size").val())
|
||||||
|
|||||||
@@ -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); });
|
||||||
|
|
||||||
|
|||||||
@@ -423,6 +423,13 @@
|
|||||||
typeof window.soonBindFileMeta === 'function') {
|
typeof window.soonBindFileMeta === 'function') {
|
||||||
window.soonBindFileMeta(cacheKey, { name: hit.meta.name });
|
window.soonBindFileMeta(cacheKey, { name: hit.meta.name });
|
||||||
}
|
}
|
||||||
|
// 兜底:design2(背面文件)若缓存里 j.b 缺失,主动补一个空背面结构,
|
||||||
|
// 避免 design2.canvas2.loadFromJSON(undefined) 抛错或吃掉整段加载。
|
||||||
|
if ((hit.json.soonType === 2 || hit.json.backBlackPic || (hit.json.bo && hit.json.bo.length) || (hit.json.f && hit.json.b === undefined && typeof hit.json.b === 'undefined'))) {
|
||||||
|
if (!hit.json.b || typeof hit.json.b !== 'object' || !Array.isArray(hit.json.b.objects)) {
|
||||||
|
hit.json.b = { version: '4.6.0', objects: [], hoverCursor: 'move' };
|
||||||
|
}
|
||||||
|
}
|
||||||
return Promise.resolve(hit.json);
|
return Promise.resolve(hit.json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,18 +451,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 +477,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()) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,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,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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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))
|
||||||