Files
SoonDesign/frontend-web/js/design1.js
T
24kycj ce76bb8018 fix(web): 修复 .soon/模板加载、旧文件兼容与另存为离开提示
- 删除 design1 ui 遗留代码,修复加载竞态与新建不弹保存提示
- cloud-files 归一化旧版 .soon,IndexedDB 写入完成后再跳转设计页
- 模板/本地 .soon 首次保存须另存为,离开前统一确认
- 发版缓存 bust(version.js、soon-cache.js)及宝塔 Nginx 配置文档

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 00:30:20 +08:00

534 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// design1.js - 主入口文件
// 导入公共模块(不依赖jQuery的部分);网页端由页面 script 标签提前加载
if (typeof require !== 'undefined') { try { require('./common/fabric-ext.js'); } catch (e) {} }
// 过滤 Canvas2D willReadFrequently 警告(不影响功能,只是性能提示)
if (typeof console !== 'undefined' && console.warn) {
const originalWarn = console.warn;
console.warn = function (...args) {
const message = args.join(' ');
// 过滤掉 willReadFrequently 相关的警告
if (message.includes('willReadFrequently') || message.includes('getImageData')) {
return; // 不输出这个警告
}
originalWarn.apply(console, args);
};
}
// 使用平台桥(桌面端由 design1.html 先加载 lib/platform/electron.js 注入)
var remote = typeof window !== 'undefined' ? window.remote : null;
var path = typeof window !== 'undefined' ? window.path : null;
var exePath = typeof window !== 'undefined' ? window.exePath : '';
var ipcRenderer = typeof window !== 'undefined' ? window.ipcRenderer : null;
var dialog = typeof window !== 'undefined' ? window.dialog : null;
var clipboard = typeof window !== 'undefined' ? window.clipboard : null;
var jrQrcode, JsBarcode;
if (typeof require !== 'undefined') {
try { jrQrcode = require('jr-qrcode'); } catch (e) {}
try { JsBarcode = require('jsbarcode'); } catch (e) {}
}
var dpi = 600;
if (ipcRenderer) {
ipcRenderer.send('get-sys-fonts');
ipcRenderer.send('get-scale-rate');
}
// 使用layui
layui.use(['layer', 'slider', 'form', 'colorpicker'], function () {
let myDate = new Date();
let s_lan = "";
// 确保 s_lan 在全局作用域中可用(用于 .jsc 文件加载)
if (typeof window !== 'undefined') {
window.s_lan = s_lan;
}
if (typeof global !== 'undefined') {
global.s_lan = s_lan;
}
if (ipcRenderer) ipcRenderer.send('get-sys-language');
var $ = layui.$;
var layer = layui.layer;
layer.config({ skin: 'soon-layer' });
var slider = layui.slider;
var form = layui.form;
var colorpicker = layui.colorpicker;
// 确保所有必要的变量在全局作用域中可用(用于 .jsc 文件加载)
// 在 Electron 的渲染进程中,需要同时设置 window 和 global
if (typeof window !== 'undefined') {
window.$ = $;
window.jQuery = $;
window.ipcRenderer = ipcRenderer;
window.layer = layer;
window.slider = slider;
window.form = form;
window.colorpicker = colorpicker;
window.dialog = dialog;
window.remote = remote;
window.path = path;
window.jrQrcode = jrQrcode;
window.JsBarcode = JsBarcode;
window.clipboard = clipboard;
window.dpi = dpi;
// language_str 会在后面定义,但先确保引用正确
}
// 在 Node.js 上下文中也设置(require 加载 .jsc 文件时使用)
if (typeof global !== 'undefined') {
global.$ = $;
global.jQuery = $;
global.ipcRenderer = ipcRenderer;
global.layer = layer;
global.slider = slider;
global.form = form;
global.colorpicker = colorpicker;
global.dialog = dialog;
global.remote = remote;
global.path = path;
global.jrQrcode = jrQrcode;
global.JsBarcode = JsBarcode;
global.clipboard = clipboard;
global.dpi = dpi;
}
var layer = layui.layer;
var slider = layui.slider;
var colorpicker = layui.colorpicker;
// 历史记录文件路径(供 output.js 使用)
var fullPath = (path && exePath) ? path.join(exePath, 'data.json') : (typeof window !== 'undefined' ? window.fullPath : '');
// 确保 fullPath 在全局作用域中可用(用于 .jsc 文件加载)
if (typeof window !== 'undefined') {
window.fullPath = fullPath;
window.exePath = exePath;
}
if (typeof global !== 'undefined') {
global.fullPath = fullPath;
global.exePath = exePath;
}
// ==========================================
// 公共函数定义(需要在layui.use回调内部,因为依赖jQuery
// 注意:所有函数都附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
// ==========================================
// DES加密函数
window.desEncrypt = function desEncrypt(str, key = "df6a551ca43181fc485f3043bcdd2fbc") {
var APIFMS;
try {
// 确保输入字符串是 UTF-8 编码
var keyHex = CryptoJS.enc.Utf8.parse(key);
var encrypted = CryptoJS.DES.encrypt(str, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
// 使用 Base64 编码确保中文字符正确输出
APIFMS = CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
} catch (err) {
APIFMS = '';
}
return APIFMS;
};
// 向后兼容
function desEncrypt(str, key = "df6a551ca43181fc485f3043bcdd2fbc") {
return window.desEncrypt(str, key);
}
// 多语言切换
window.langua_ge = function langua_ge(lan = 'zh') {
$("[language='m']").each(function (i) {
$(this).html($(this).attr(lan));
});
$("[language='t']").each(function (i) {
$(this).attr("title", $(this).attr(lan));
});
};
// 向后兼容
function langua_ge(lan = 'zh') {
return window.langua_ge(lan);
}
// 多语言字符串获取
window.language_str = function language_str(str) {
let t = {
"whetherSave": { zh: "是否保存当前文件?", ozh: "是否保存當前文件?", en: "Do you want to save the current file?" },
"save": { zh: "保存", ozh: "保存", en: "Save" },
"noSave": { zh: "不保存", ozh: "不保存", en: "No saving" },
"cancel": { zh: "取消", ozh: "取消", en: "Cancel" },
"saveSucc": { zh: "保存成功至", ozh: "保存成功至", en: "Save successfully to" },
"saveFile": { zh: "保存文件", ozh: "保存文件", en: "Save file" },
"display": { zh: "预览", ozh: "預覽", en: "Display" },
"output": { zh: "导出", ozh: "導出", en: "Export" },
"bg": { zh: "背景", ozh: "背景", en: "Background" },
"selectPic": { zh: "请选择图片", ozh: "請選擇圖片", en: "Please select a picture" },
"img": { zh: "圖片", ozh: "圖片", en: "Image" },
"simg": { zh: "合成圖片", ozh: "合成圖片", en: "Synthesized Image" },
"text": { zh: "文本", ozh: "文本", en: "Text" },
"ctext": { zh: "圆形文本", ozh: "圆形文本", en: "Circle Text" },
"stext": { zh: "合成文本", ozh: "合成文本", en: "Synthesized Text" },
"rect": { zh: "矩形", ozh: "矩形", en: "Rectangle" },
"circ": { zh: "圓形", ozh: "圓形", en: "Circle" },
"line": { zh: "直線", ozh: "直線", en: "Line" },
"qrc": { zh: "二維碼", ozh: "二維碼", en: "QR code" },
"barc": { zh: "條形碼", ozh: "條形碼", en: "Barcode" },
"counter": { zh: "計數器", ozh: "计数器", en: "Counter" },
"about": { zh: "关于Soon Design", ozh: "關於Soon Design", en: "About Soon Design" },
"vers": { zh: "版本", ozh: "版本", en: "Version" },
"comf": { zh: "确认", ozh: "確認", en: "Select" },
"copyright": { zh: "© 2022 cardsoon Co., Ltd. All rights reserved.", ozh: "© 2022 cardsoon Co., Ltd. All rights reserved.", en: "© 2022 cardsoon Co., Ltd. All rights reserved." }
};
// 从全局作用域获取 s_lan(支持 .jsc 文件加载)
const currentLang = (typeof global !== 'undefined' && global.s_lan) ? global.s_lan :
(typeof window !== 'undefined' && window.s_lan) ? window.s_lan : s_lan;
return t[str] ? (t[str][currentLang] || t[str]['zh'] || str) : str;
};
// 确保 language_str 在 global 作用域中也可用(用于 .jsc 文件加载)
if (typeof global !== 'undefined') {
global.language_str = window.language_str;
}
// 向后兼容
function language_str(str) {
return window.language_str(str);
}
// 获取URL参数
window.GetFile = function GetFile() {
const params = new URLSearchParams(window.location.search);
return params;
};
// 向后兼容
function GetFile() {
return window.GetFile();
}
// 日期格式化
window.getDate = function getDate() {
let date = new Date();
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
};
// 向后兼容
function getDate() {
return window.getDate();
}
// 获取对象的绝对坐标
window.getAbsoluteXY = function getAbsoluteXY(_obj) {
let coord = _obj.get("lineCoords");
return [_obj.get("left"), _obj.get("top")];
};
// 向后兼容
function getAbsoluteXY(_obj) {
return window.getAbsoluteXY(_obj);
}
// 计算两点之间的距离
window.getDisdance = function getDisdance(x1, y1, x2, y2) {
var dx = Math.abs(x2 - x1);
var dy = Math.abs(y2 - y1);
var distance = Math.sqrt(dx * dx + dy * dy);
return distance;
};
// 向后兼容
function getDisdance(x1, y1, x2, y2) {
return window.getDisdance(x1, y1, x2, y2);
}
// 计算角度(相对于画布中心)
window.getDeg = function getDeg(pointer, canvas) {
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var mouseX = pointer.x;
var mouseY = pointer.y;
var dx = mouseX - centerX;
var dy = mouseY - centerY;
var angleRad = Math.atan2(dy, dx);
var angleDeg = angleRad * (180 / Math.PI);
if (angleDeg < 0) {
angleDeg += 360;
}
angleDeg += 90;
return angleDeg;
};
// 向后兼容
function getDeg(pointer, canvas) {
return window.getDeg(pointer, canvas);
}
// 检查字段名是否重复
window.checkName = function checkName(name, objs1, objs2) {
for (let item of objs1) {
if (item.name == name) {
return false;
}
}
for (let item of objs2) {
if (item.name == name) {
return false;
}
}
return true;
};
// 向后兼容
function checkName(name, objs1, objs2) {
return window.checkName(name, objs1, objs2);
}
// 返回不重复的字段名
window.resName = function resName(pre_name, objs1, objs2) {
let num = (objs1.length + objs2.length) - 1;
do {
num++;
}
while (!window.checkName(pre_name + num, objs1, objs2));
return pre_name + num;
};
// 向后兼容
function resName(pre_name, objs1, objs2) {
return window.resName(pre_name, objs1, objs2);
}
// 获取对象在画布中的索引
window.getIndex = function getIndex(target, canvas, _canvas = null) {
let temp_objs;
let useCanvas = (_canvas == null) ? canvas : _canvas;
if (_canvas == null) {
temp_objs = canvas.getObjects();
} else {
temp_objs = _canvas.getObjects();
}
let i = 0;
for (let res of temp_objs) {
if (target == res) {
return i;
}
i++;
}
return 0;
};
// 向后兼容
function getIndex(target, canvas, _canvas = null) {
return window.getIndex(target, canvas, _canvas);
}
// 获取对象类型
window.getType = function getType(target, canvas, objs) {
let temp_objs = canvas.getObjects();
let i = 0;
for (let res of temp_objs) {
if (target == res) {
return objs[i].type;
}
i++;
}
};
// 向后兼容
function getType(target, canvas, objs) {
return window.getType(target, canvas, objs);
}
// 获取坐标的最小X值
window.getCoordsMinX = function getCoordsMinX(acoords) {
let x = acoords[0].x;
for (let item of acoords) {
if (item.x < x) {
x = item.x;
}
}
return x;
};
// 向后兼容
function getCoordsMinX(acoords) {
return window.getCoordsMinX(acoords);
}
// 获取坐标的最大X值
window.getCoordsMaxX = function getCoordsMaxX(acoords) {
let x = acoords[0].x;
for (let item of acoords) {
if (item.x > x) {
x = item.x;
}
}
return x;
};
// 向后兼容
function getCoordsMaxX(acoords) {
return window.getCoordsMaxX(acoords);
}
// 获取坐标的最小Y值
window.getCoordsMinY = function getCoordsMinY(acoords) {
let y = acoords[0].y;
for (let item of acoords) {
if (item.y < y) {
y = item.y;
}
}
return y;
};
// 向后兼容
function getCoordsMinY(acoords) {
return window.getCoordsMinY(acoords);
}
// 获取坐标的最大Y值
window.getCoordsMaxY = function getCoordsMaxY(acoords) {
let y = acoords[0].y;
for (let item of acoords) {
if (item.y > y) {
y = item.y;
}
}
return y;
};
// 向后兼容
function getCoordsMaxY(acoords) {
return window.getCoordsMaxY(acoords);
}
// ==========================================
// 全局变量定义(所有模块共享)
// 注意:所有变量都附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
// ==========================================
window.addState = 0; // 0是不添加
window.background_image = undefined;
window.background_image1 = undefined;
window.background_image2 = undefined;
window.bg_version = 1; // front_bg1.png front_bg2.png
window.zoom = 1;
window.pre_add_image = undefined; // 预添加的图片对象(用于addPic)
// 内部剪贴板
window.clipboardData = {
data: null,
offset: 10 // 粘贴位置偏移量
};
// 文件管理
window.openAs = {
_name: "",
set name(val) {
if (val == "") {
$("title").html('Soon Design');
} else {
$("title").html('Soon Design - ' + val);
}
this._name = val;
},
get name() {
return this._name
},
};
// 对象数组和历史记录
window.objs1 = [];
window.objs2 = [];
window.step1 = { val: 0 };
window.step2 = { val: 0 };
window.step = window.step1;
window.objs = window.objs1;
window.recordJson1 = [];
window.recordJson2 = [];
window.recordJson = window.recordJson1;
window.recordObjs1 = [];
window.recordObjs2 = [];
window.recordObjs = window.recordObjs1;
window.pre_objs1 = [];
window.pre_objs2 = [];
window.next_objs1 = [];
window.next_objs2 = [];
window.pre_objs = window.pre_objs1;
window.next_objs = window.next_objs1;
window.pre_json1 = [];
window.pre_json2 = [];
window.next_json1 = [];
window.next_json2 = [];
window.pre_json = window.pre_json1;
window.next_json = window.next_json1;
// Canvas对象(将在core.js中初始化)
window.canvas1 = undefined;
window.canvas2 = undefined;
window.canvas = undefined; // 当前活动的画布(canvas1 或 canvas2
window.ctx1 = undefined;
window.ctx2 = undefined;
window.is_bgi_add = false;
// 为了兼容性,同时创建局部变量引用(向后兼容)
let addState = window.addState;
let background_image = window.background_image;
let background_image1 = window.background_image1;
let background_image2 = window.background_image2;
let bg_version = window.bg_version;
var zoom = window.zoom;
let pre_add_image = window.pre_add_image;
let clipboardData = window.clipboardData;
var openAs = window.openAs;
let objs1 = window.objs1;
let objs2 = window.objs2;
let step1 = window.step1;
let step2 = window.step2;
let step = window.step;
let objs = window.objs;
let recordJson1 = window.recordJson1;
let recordJson2 = window.recordJson2;
let recordJson = window.recordJson;
let recordObjs1 = window.recordObjs1;
let recordObjs2 = window.recordObjs2;
let recordObjs = window.recordObjs;
let pre_objs1 = window.pre_objs1;
let pre_objs2 = window.pre_objs2;
let next_objs1 = window.next_objs1;
let next_objs2 = window.next_objs2;
let pre_objs = window.pre_objs;
let next_objs = window.next_objs;
let pre_json1 = window.pre_json1;
let pre_json2 = window.pre_json2;
let next_json1 = window.next_json1;
let next_json2 = window.next_json2;
let pre_json = window.pre_json;
let next_json = window.next_json;
var canvas1 = window.canvas1;
var canvas2 = window.canvas2;
var canvas = window.canvas;
var ctx1 = window.ctx1;
var ctx2 = window.ctx2;
let is_bgi_add = window.is_bgi_add;
// 加载 design1 模块:网页端在回调内动态加载(保证 $ 已存在),桌面端用 require+eval
var fs = typeof window !== 'undefined' ? window.fs : null;
var appPath = (remote && remote.app && remote.app.getAppPath) ? remote.app.getAppPath() : (typeof __dirname !== 'undefined' ? __dirname : '.');
if (typeof require === 'undefined') {
// 网页端:按顺序动态插入 script,确保 core/ui 执行时 window.$ 已存在
function loadScript(src, next) {
var s = document.createElement('script');
s.src = (typeof window.soonWithVersion === 'function') ? window.soonWithVersion(src) : src;
s.onload = s.onerror = function () { if (typeof next === 'function') next(); };
document.body.appendChild(s);
}
loadScript('../js/design1/output.js', function () {
loadScript('../js/design1/core.js', function () {
loadScript('../js/design1/ui.js', function () {});
});
});
return;
}
const loadModule = (moduleName) => {
if (!path || !fs) return;
const jsPath = path.join(appPath, 'js', 'design1', moduleName + '.js');
if (fs.existsSync(jsPath)) {
try {
eval(fs.readFileSync(jsPath, 'utf8'));
} catch (e) {
alert('加载 ' + moduleName + ' 失败!\n\n错误: ' + e.message);
}
} else {
alert('文件不存在!\n\n请检查:' + jsPath);
}
};
loadModule('output');
loadModule('core');
loadModule('ui');
});