重构 monorepo 并完善网页端订阅与首页体验
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构 - 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐 - 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作 - 新增管理后台、支付与云端文件 API,更新 README 与项目规范 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var g = typeof globalThis !== 'undefined' ? globalThis : (typeof window !== 'undefined' ? window : this);
|
||||
if (g.soonAsset) return;
|
||||
|
||||
function computeAssetBase() {
|
||||
if (typeof window === 'undefined' || !window.location) {
|
||||
return '../assets/images/';
|
||||
}
|
||||
var p = window.location.pathname || '/';
|
||||
if (p.indexOf('/pages/') !== -1 || /\.html$/i.test(p)) {
|
||||
return '../assets/images/';
|
||||
}
|
||||
return 'assets/images/';
|
||||
}
|
||||
|
||||
function computeJsBase() {
|
||||
if (typeof window === 'undefined' || !window.location) {
|
||||
return '../js/';
|
||||
}
|
||||
var p = window.location.pathname || '/';
|
||||
if (p.indexOf('/pages/') !== -1 || /\.html$/i.test(p)) {
|
||||
return '../js/';
|
||||
}
|
||||
return 'js/';
|
||||
}
|
||||
|
||||
g.SOON_ASSET_BASE = computeAssetBase();
|
||||
g.SOON_JS_BASE = computeJsBase();
|
||||
|
||||
g.soonAsset = function (name) {
|
||||
var n = String(name || '').replace(/^\/+/, '');
|
||||
return g.SOON_ASSET_BASE + n;
|
||||
};
|
||||
|
||||
g.soonJs = function (rel) {
|
||||
var n = String(rel || '').replace(/^\/+/, '');
|
||||
return g.SOON_JS_BASE + n;
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,164 @@
|
||||
// Fabric.js 扩展类
|
||||
|
||||
(function (fabric) {
|
||||
fabric.CurvedText = fabric.util.createClass(fabric.Object, {
|
||||
type: 'curved-text',
|
||||
diameter: 0,
|
||||
kerning: 0,
|
||||
text: '',
|
||||
flipped: false,
|
||||
fill: '#000',
|
||||
fontFamily: 'Times New Roman',
|
||||
fontSize: 24, // in px
|
||||
fontWeight: 'normal',
|
||||
fontStyle: '', // "normal", "italic" or "oblique".
|
||||
cacheProperties: fabric.Object.prototype.cacheProperties.concat('diameter', 'textBackgroundColor', 'kerning', 'flipped', 'fill', 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'strokeStyle', 'strokeWidth'),
|
||||
strokeStyle: null,
|
||||
strokeWidth: 0,
|
||||
initialize: function (text, options) {
|
||||
options || (options = {});
|
||||
if (typeof text === 'object' && text) {
|
||||
for (let key in text) {
|
||||
this[key] = text[key]
|
||||
}
|
||||
} else {
|
||||
this.text = text;
|
||||
}
|
||||
this.callSuper('initialize', options);
|
||||
this.set('lockUniScaling', true);
|
||||
var canvas = this.getCircularText();
|
||||
this.cropCanvas(canvas);
|
||||
this.set('width', canvas.width);
|
||||
this.set('height', canvas.height);
|
||||
},
|
||||
_getFontDeclaration: function () {
|
||||
return [
|
||||
(fabric.isLikelyNode ? this.fontWeight : this.fontStyle),
|
||||
(fabric.isLikelyNode ? this.fontStyle : this.fontWeight),
|
||||
this.fontSize + 'px',
|
||||
(fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily)
|
||||
].join(' ');
|
||||
},
|
||||
cropCanvas: function (canvas) {
|
||||
var ctx = canvas.getContext('2d'),
|
||||
w = canvas.width,
|
||||
h = canvas.height,
|
||||
pix = { x: [], y: [] }, n,
|
||||
imageData = ctx.getImageData(0, 0, w, h),
|
||||
fn = function (a, b) { return a - b };
|
||||
for (var y = 0; y < h; y++) {
|
||||
for (var x = 0; x < w; x++) {
|
||||
if (imageData.data[((y * w + x) * 4) + 3] > 0) {
|
||||
pix.x.push(x);
|
||||
pix.y.push(y);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果没有找到任何非透明像素,直接返回,不进行裁剪
|
||||
if (pix.x.length === 0 || pix.y.length === 0) {
|
||||
return;
|
||||
}
|
||||
pix.x.sort(fn);
|
||||
pix.y.sort(fn);
|
||||
n = pix.x.length - 1;
|
||||
w = pix.x[n] - pix.x[0];
|
||||
h = pix.y[n] - pix.y[0];
|
||||
// 确保宽度和高度是有效的正数
|
||||
if (w <= 0 || h <= 0 || isNaN(w) || isNaN(h)) {
|
||||
return;
|
||||
}
|
||||
var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
ctx.putImageData(cut, 0, 0);
|
||||
},
|
||||
getCircularText: function () {
|
||||
var text = this.text,
|
||||
diameter = this.diameter,
|
||||
flipped = this.flipped,
|
||||
kerning = this.kerning,
|
||||
fill = this.fill,
|
||||
inwardFacing = true,
|
||||
startAngle = 0,
|
||||
canvas = fabric.util.createCanvasElement(),
|
||||
ctx = canvas.getContext('2d'),
|
||||
cw, // character-width
|
||||
x, // iterator
|
||||
clockwise = -1; // draw clockwise for aligned right. Else Anticlockwise
|
||||
if (flipped) {
|
||||
// startAngle = 180;
|
||||
inwardFacing = false;
|
||||
}
|
||||
startAngle *= Math.PI / 180; // convert to radians
|
||||
var d = document.createElement('div');
|
||||
d.style.fontFamily = this.fontFamily;
|
||||
d.style.fontSize = this.fontSize + 'px';
|
||||
d.style.fontWeight = this.fontWeight;
|
||||
d.style.fontStyle = this.fontStyle;
|
||||
d.textContent = text;
|
||||
document.body.appendChild(d);
|
||||
var textHeight = d.offsetHeight;
|
||||
document.body.removeChild(d);
|
||||
canvas.width = canvas.height = diameter;
|
||||
ctx.font = this._getFontDeclaration();
|
||||
if (inwardFacing) { text = text.split('').reverse().join('') };
|
||||
ctx.translate(diameter / 2, diameter / 2); // Move to center
|
||||
startAngle += (Math.PI * !inwardFacing); // Rotate 180 if outward
|
||||
ctx.textBaseline = 'middle'; // Ensure we draw in exact center
|
||||
ctx.textAlign = 'center'; // Ensure we draw in exact center
|
||||
for (x = 0; x < text.length; x++) {
|
||||
cw = ctx.measureText(text[x]).width;
|
||||
startAngle += ((cw + (x == text.length - 1 ? 0 : kerning)) / (diameter / 2 - textHeight)) / 2 * -clockwise;
|
||||
}
|
||||
ctx.rotate(startAngle);
|
||||
for (x = 0; x < text.length; x++) {
|
||||
cw = ctx.measureText(text[x]).width; // half letter
|
||||
ctx.rotate((cw / 2) / (diameter / 2 - textHeight) * clockwise);
|
||||
if (this.strokeStyle && this.strokeWidth) {
|
||||
ctx.strokeStyle = this.strokeStyle;
|
||||
ctx.lineWidth = this.strokeWidth;
|
||||
ctx.miterLimit = 2;
|
||||
ctx.strokeText(text[x], 0, (inwardFacing ? 1 : -1) * (0 - diameter / 2 + textHeight / 2));
|
||||
}
|
||||
ctx.fillStyle = fill;
|
||||
ctx.fillText(text[x], 0, (inwardFacing ? 1 : -1) * (0 - diameter / 2 + textHeight / 2));
|
||||
ctx.rotate((cw / 2 + kerning) / (diameter / 2 - textHeight) * clockwise); // rotate half letter
|
||||
}
|
||||
return canvas;
|
||||
},
|
||||
_set: function (key, value) {
|
||||
switch (key) {
|
||||
case 'scaleX':
|
||||
this.fontSize *= value;
|
||||
this.diameter *= value;
|
||||
this.width *= value;
|
||||
this.scaleX = 1;
|
||||
if (this.width < 1) { this.width = 1; }
|
||||
break;
|
||||
case 'scaleY':
|
||||
this.height *= value;
|
||||
this.scaleY = 1;
|
||||
if (this.height < 1) { this.height = 1; }
|
||||
break;
|
||||
default:
|
||||
this.callSuper('_set', key, value);
|
||||
break;
|
||||
}
|
||||
},
|
||||
_render: function (ctx) {
|
||||
var canvas = this.getCircularText();
|
||||
this.cropCanvas(canvas);
|
||||
this.set('width', canvas.width);
|
||||
this.set('height', canvas.height);
|
||||
ctx.drawImage(canvas, -this.width / 2, -this.height / 2, this.width, this.height);
|
||||
this.setCoords();
|
||||
},
|
||||
toObject: function (propertiesToInclude) {
|
||||
return this.callSuper('toObject', ['text', 'diameter', 'textBackgroundColor', 'kerning', 'flipped', 'fill', 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'strokeStyle', 'strokeWidth'].concat(propertiesToInclude));
|
||||
}
|
||||
});
|
||||
fabric.CurvedText.fromObject = function (object, callback, forceAsync) {
|
||||
return fabric.Object._fromObject('CurvedText', object, callback, forceAsync, 'curved-text');
|
||||
};
|
||||
})(typeof fabric !== 'undefined' ? fabric : require('fabric').fabric);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 多语言支持模块
|
||||
|
||||
/**
|
||||
* 多语言切换
|
||||
*/
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 多语言字符串获取
|
||||
* @param {string} str - 字符串键
|
||||
* @param {string} s_lan - 当前语言 ('zh', 'ozh', 'en')
|
||||
*/
|
||||
function language_str(str, s_lan) {
|
||||
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." }
|
||||
};
|
||||
return t[str] ? t[str][s_lan] : str;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
langua_ge,
|
||||
language_str
|
||||
};
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
// 公共工具函数模块
|
||||
|
||||
/**
|
||||
* DES加密函数
|
||||
*/
|
||||
function desEncrypt(str, key = "df6a551ca43181fc485f3043bcdd2fbc") {
|
||||
var APIFMS;
|
||||
try {
|
||||
var keyHex_encrypt = CryptoJS.enc.Utf8.parse(key);
|
||||
var encrypted = CryptoJS.DES.encrypt(str, keyHex_encrypt, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
});
|
||||
APIFMS = CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
|
||||
} catch (err) {
|
||||
console.log('des 加密 -------------------------');
|
||||
console.log(err);
|
||||
}
|
||||
return APIFMS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取URL参数
|
||||
*/
|
||||
function GetFile() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式化
|
||||
*/
|
||||
function getDate() {
|
||||
let date = new Date();
|
||||
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的绝对坐标
|
||||
*/
|
||||
function getAbsoluteXY(_obj) {
|
||||
let coord = _obj.get("lineCoords");
|
||||
return [_obj.get("left"), _obj.get("top")];
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两点之间的距离
|
||||
*/
|
||||
function getDisdance(x1, y1, x2, y2) {
|
||||
var dx = Math.abs(x2 - x1); // 计算x轴上的距离差,并取绝对值
|
||||
var dy = Math.abs(y2 - y1); // 计算y轴上的距离差,并取绝对值
|
||||
var distance = Math.sqrt(dx * dx + dy * dy); // 应用勾股定理计算距离
|
||||
return distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算角度(相对于画布中心)
|
||||
*/
|
||||
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 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 resName(pre_name, objs1, objs2) {
|
||||
//返回的是不重复的字段名
|
||||
let num = (objs1.length + objs2.length) - 1;
|
||||
do {
|
||||
num++;
|
||||
}
|
||||
while (!checkName(pre_name + num, objs1, objs2));//重复了就再加1
|
||||
return pre_name + num;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象在画布中的索引
|
||||
*/
|
||||
function getIndex(target, canvas, _canvas = null) {
|
||||
let temp_objs;
|
||||
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 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++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取坐标的最小X值
|
||||
*/
|
||||
function getCoordsMinX(acoords) {
|
||||
let x = acoords[0].x;
|
||||
for (let item of acoords) {
|
||||
if (item.x < x) {
|
||||
x = item.x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取坐标的最大X值
|
||||
*/
|
||||
function getCoordsMaxX(acoords) {
|
||||
let x = acoords[0].x;
|
||||
for (let item of acoords) {
|
||||
if (item.x > x) {
|
||||
x = item.x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取坐标的最小Y值
|
||||
*/
|
||||
function getCoordsMinY(acoords) {
|
||||
let y = acoords[0].y;
|
||||
for (let item of acoords) {
|
||||
if (item.y < y) {
|
||||
y = item.y;
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取坐标的最大Y值
|
||||
*/
|
||||
function getCoordsMaxY(acoords) {
|
||||
let y = acoords[0].y;
|
||||
for (let item of acoords) {
|
||||
if (item.y > y) {
|
||||
y = item.y;
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
desEncrypt,
|
||||
GetFile,
|
||||
getDate,
|
||||
getAbsoluteXY,
|
||||
getDisdance,
|
||||
getDeg,
|
||||
checkName,
|
||||
resName,
|
||||
getIndex,
|
||||
getType,
|
||||
getCoordsMinX,
|
||||
getCoordsMaxX,
|
||||
getCoordsMinY,
|
||||
getCoordsMaxY
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,538 @@
|
||||
// design1.js - 主入口(桌面端资源路径与 design1-back 一致:../assets/images/)
|
||||
if (typeof require !== 'undefined') {
|
||||
try { require('./common/fabric-ext.js'); } catch (e) {}
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
window.SOON_IMG = '../assets/images/';
|
||||
window.soonAsset = function (name) {
|
||||
return window.SOON_IMG + String(name || '').replace(/^\/+/, '');
|
||||
};
|
||||
}
|
||||
|
||||
// 过滤 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 注入 js/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');
|
||||
}
|
||||
|
||||
// 使用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;
|
||||
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 = 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');
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,530 @@
|
||||
// design2.js - 主入口(桌面端资源路径与 design2-back 一致:../assets/images/)
|
||||
if (typeof require !== 'undefined') {
|
||||
try { require('./common/fabric-ext.js'); } catch (e) {}
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
window.SOON_IMG = '../assets/images/';
|
||||
window.soonAsset = function (name) {
|
||||
return window.SOON_IMG + String(name || '').replace(/^\/+/, '');
|
||||
};
|
||||
}
|
||||
|
||||
// 过滤 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);
|
||||
};
|
||||
}
|
||||
|
||||
// 平台桥由 design2.html 注入 js/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) {}
|
||||
}
|
||||
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send('get-sys-fonts');
|
||||
}
|
||||
|
||||
// 使用layui
|
||||
layui.use(['layer', 'slider', 'form', 'colorpicker'], function () {
|
||||
let myDate = new Date();
|
||||
let s_lan = "";
|
||||
if (ipcRenderer) ipcRenderer.send('get-sys-language');
|
||||
|
||||
var $ = layui.$;
|
||||
var layer = layui.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;
|
||||
// design2 中没有定义 dpi,从 window.dpi 读取(在 design2.js 后面会设置)
|
||||
}
|
||||
// 在 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;
|
||||
// design2 中没有定义 dpi,从 global.dpi 读取(在 design2.js 后面会设置)
|
||||
}
|
||||
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 {
|
||||
var keyHex_encrypt = CryptoJS.enc.Utf8.parse(key);
|
||||
var encrypted = CryptoJS.DES.encrypt(str, keyHex_encrypt, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
});
|
||||
APIFMS = CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
|
||||
} catch (err) {
|
||||
}
|
||||
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;
|
||||
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.dpi = 300; // design2使用300 DPI
|
||||
// 确保 dpi 在 global 作用域中也可用(用于 .jsc 文件加载)
|
||||
if (typeof global !== 'undefined') {
|
||||
global.dpi = window.dpi;
|
||||
}
|
||||
window.addState = 0; // 0是不添加
|
||||
window.background_image = undefined;
|
||||
window.background_image1 = undefined;
|
||||
window.background_image2 = undefined;
|
||||
window.bg_version = 1;
|
||||
window.zoom = 0;
|
||||
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;
|
||||
|
||||
// 为了兼容性,同时创建局部变量引用(向后兼容)
|
||||
const dpi = window.dpi;
|
||||
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;
|
||||
|
||||
// 加载 design2 模块:网页端在回调内动态加载(保证 $ 已存在),桌面端用 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') {
|
||||
function loadScript(src, next) {
|
||||
var s = document.createElement('script');
|
||||
s.src = src;
|
||||
s.onload = s.onerror = function () { if (typeof next === 'function') next(); };
|
||||
document.body.appendChild(s);
|
||||
}
|
||||
loadScript('../js/design2/output.js', function () {
|
||||
loadScript('../js/design2/core.js', function () {
|
||||
loadScript('../js/design2/ui.js', function () {});
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const loadModule = (moduleName) => {
|
||||
if (!path || !fs) return;
|
||||
const jsPath = path.join(appPath, 'js', 'design2', 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');
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,319 @@
|
||||
// 平台桥由 js/platform/electron.js 注入
|
||||
var ipcRenderer = typeof window !== 'undefined' ? window.ipcRenderer : null;
|
||||
var dialog = typeof window !== 'undefined' ? window.dialog : null;
|
||||
|
||||
// 辅助函数:获取文件名 (保留扩展名)
|
||||
function get_filename(filePath) {
|
||||
if (!filePath) return "";
|
||||
return filePath.split(/[/\\]/).pop();
|
||||
}
|
||||
|
||||
// 辅助函数:HTML 属性转义,避免 path 中的 "&<> 破坏属性导致点击传参错误
|
||||
function escapeAttr(s) {
|
||||
if (s == null) return "";
|
||||
return String(s).replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
|
||||
// Electron 历史曾被误写 soondesign_session: 前缀,读取时还原为磁盘路径
|
||||
function isWebPortal() {
|
||||
return !!(window.platformBridge && !ipcRenderer);
|
||||
}
|
||||
function resolveDiskPath(p) {
|
||||
if (!p) return p;
|
||||
const prefix = 'soondesign_session:';
|
||||
if (ipcRenderer && p.indexOf(prefix) === 0) return p.substring(prefix.length);
|
||||
return p;
|
||||
}
|
||||
|
||||
let s_lan = "zh";
|
||||
|
||||
function language_str(str) {
|
||||
let t = {
|
||||
"noFile": { zh: "当前文件不存在!", ozh: "This file does not exist!", en: "File not found!" },
|
||||
"saveTime": { zh: "保存时间:", ozh: "保存時間:", en: "Saved on:" },
|
||||
"selectFile": { zh: "请选择文件", ozh: "請選擇文件", en: "Please select a file" },
|
||||
"comfirm": { zh: "确认", ozh: "確認", en: "Confirm" },
|
||||
"cancel": { zh: "取消", ozh: "取消", en: "Cancel" },
|
||||
"SDFile": { zh: "SoonDesign模板文件", ozh: "SoonDesign模闆文件", en: "SoonDesign Template File" },
|
||||
"vers": { zh: "版本:", ozh: "版本:", en: "Version:" },
|
||||
"copyright": { zh: "版权所有 © 2023", ozh: "版權所有 © 2023", en: "Copyright © 2023" },
|
||||
"about": { zh: "关于 SoonDesign", ozh: "關於 SoonDesign", en: "About SoonDesign" },
|
||||
"lost": { zh: "(文件已丢失)", ozh: "(文件已丟失)", en: "(File Lost)" },
|
||||
"clickToDelete": { zh: "文件已丢失,点击删除此记录", ozh: "文件已丟失,點擊刪除此記錄", en: "File lost, click to remove" },
|
||||
"deleted": { zh: "已删除", ozh: "已刪除", en: "Deleted" },
|
||||
"whetherSave": { zh: "是否保存当前文件?", ozh: "是否保存當前文件?", en: "Save current file?" }, // 补全可能用到的翻译
|
||||
"delTitle": { zh: "警告", ozh: "警告", en: "Warning" },
|
||||
"delContent": { zh: "文件已丢失,确认是否删除?", ozh: "文件已丟失,確認是否刪除?", en: "File lost, confirm delete?" }
|
||||
}
|
||||
return t[str][s_lan] || t[str]['zh'];
|
||||
}
|
||||
|
||||
layui.use(['layer', 'form', 'jquery'], function () {
|
||||
var $ = layui.$;
|
||||
var layer = layui.layer
|
||||
, form = layui.form;
|
||||
|
||||
if (window.sysAPI && window.sysAPI.getAppVersion) {
|
||||
window.sysAPI.getAppVersion().then(ver => {
|
||||
$("#app-version").text("v" + ver);
|
||||
}).catch(e => console.log(e));
|
||||
}
|
||||
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send('get-sys-language');
|
||||
ipcRenderer.on('close', (event, message) => {
|
||||
ipcRenderer.send('run-close');
|
||||
});
|
||||
}
|
||||
|
||||
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));
|
||||
})
|
||||
}
|
||||
|
||||
$('#language_select').on('change', function () {
|
||||
langua_ge($(this).find('option:selected').val());
|
||||
s_lan = $(this).find('option:selected').val();
|
||||
localStorage.setItem("lang", s_lan);
|
||||
loadHistory();
|
||||
});
|
||||
|
||||
if (ipcRenderer && ipcRenderer.on) {
|
||||
ipcRenderer.on('sys-lan', (event, data) => {
|
||||
let lang = localStorage.getItem("lang");
|
||||
if (lang) {
|
||||
s_lan = lang;
|
||||
} else {
|
||||
if (data && data.startsWith('zh')) {
|
||||
s_lan = data === 'zh-TW' ? 'ozh' : 'zh';
|
||||
} else {
|
||||
s_lan = 'en';
|
||||
}
|
||||
}
|
||||
langua_ge(s_lan);
|
||||
$("#language_select").val(s_lan);
|
||||
localStorage.setItem("lang", s_lan);
|
||||
loadHistory();
|
||||
});
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 加载列表
|
||||
// ==========================================
|
||||
async function loadHistory() {
|
||||
try {
|
||||
if (!window.sysAPI || typeof window.sysAPI.readHistory !== 'function') {
|
||||
$(".card-list").html("");
|
||||
return;
|
||||
}
|
||||
const j = await window.sysAPI.readHistory();
|
||||
if (!j || !Array.isArray(j.history)) {
|
||||
$(".card-list").html("");
|
||||
return;
|
||||
}
|
||||
let h = "";
|
||||
let needUpdate = false;
|
||||
|
||||
for (let item of j.history) {
|
||||
let src = "";
|
||||
let fileExists = true;
|
||||
let imgStyle = "";
|
||||
let realType = item.type;
|
||||
const diskPath = resolveDiskPath(item.path);
|
||||
if (diskPath !== item.path) {
|
||||
item.path = diskPath;
|
||||
needUpdate = true;
|
||||
}
|
||||
const soonData = await window.sysAPI.readJsonFile(diskPath);
|
||||
if (soonData) {
|
||||
src = soonData.frontDisplayPic;
|
||||
let fileType = soonData.soonType ? soonData.soonType : (soonData.backBlackPic ? 2 : 1);
|
||||
if (item.type != fileType) {
|
||||
item.type = fileType;
|
||||
realType = fileType;
|
||||
needUpdate = true;
|
||||
}
|
||||
} else {
|
||||
fileExists = false;
|
||||
if (realType && (realType == 2 || realType == "2")) {
|
||||
src = soonAsset("bg_2.png");
|
||||
} else {
|
||||
src = soonAsset("bg_1.png");
|
||||
}
|
||||
imgStyle = "opacity: 0.6; filter: grayscale(100%);";
|
||||
}
|
||||
|
||||
// 网页端历史 path 为 soondesign_session:文件名.soon,展示时去掉前缀
|
||||
const sessionPrefix = 'soondesign_session:';
|
||||
let displayName = (item.path && item.path.indexOf(sessionPrefix) === 0)
|
||||
? item.path.substring(sessionPrefix.length)
|
||||
: get_filename(item.path);
|
||||
let cardTitle = (item.path && item.path.indexOf(sessionPrefix) === 0)
|
||||
? displayName
|
||||
: item.path;
|
||||
let cardStyle = "";
|
||||
|
||||
if (!fileExists) {
|
||||
displayName += ` <span style='color:#ff5722;font-size:12px;'>${language_str("lost")}</span>`;
|
||||
cardTitle = language_str("clickToDelete");
|
||||
cardStyle = "border: 1px dashed #ff5722;";
|
||||
}
|
||||
|
||||
h += `<div class="card" data-file="${escapeAttr(item.path)}" title="${escapeAttr(cardTitle)}" style="${cardStyle}">
|
||||
<div class="rect ${realType == 2 ? 'rect1' : ''}">
|
||||
<img src="${src}" style="width:190px; ${imgStyle}">
|
||||
<div class="tip">${displayName}</div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
$(".card-list").html(h);
|
||||
|
||||
if (needUpdate) {
|
||||
await window.sysAPI.writeHistory({ history: j.history });
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error("加载历史记录出错", e);
|
||||
}
|
||||
}
|
||||
|
||||
loadHistory();
|
||||
|
||||
// 按钮事件
|
||||
$("#openfile").click(function () { OpenDialog(); });
|
||||
$("#new1").click(function () {
|
||||
if (window.platformBridge && window.platformBridge.openDesignPage) window.platformBridge.openDesignPage("", 1);
|
||||
else if (ipcRenderer && ipcRenderer.send) ipcRenderer.send('open-design-page', "", 1);
|
||||
});
|
||||
$("#new2").click(function () {
|
||||
if (window.platformBridge && window.platformBridge.openDesignPage) window.platformBridge.openDesignPage("", 2);
|
||||
else if (ipcRenderer && ipcRenderer.send) ipcRenderer.send('open-design-page', "", 2);
|
||||
});
|
||||
|
||||
$("#about").click(async function () {
|
||||
let version = "3.0.0";
|
||||
try {
|
||||
if (window.sysAPI && window.sysAPI.getAppVersion) {
|
||||
const v = await window.sysAPI.getAppVersion();
|
||||
version = "v" + v;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
layer.open({
|
||||
type: 1
|
||||
, title: language_str("about")
|
||||
, area: '450px;'
|
||||
, id: 'LAY_layuipro'
|
||||
, moveType: 1
|
||||
, content: '<div style="padding-top:20px;padding-bottom:30px;background-color: #32363A; color: #fff; font-weight: 300;text-align:center;">' +
|
||||
'<div style="width: 120px;margin: 0 auto;"><img style="width: 120px;" src="' + soonAsset("IconA.png") + '"></div>' +
|
||||
'<div style="font-size:24px;">Soon Design</div>' +
|
||||
'<div style="font-size:14px;">' + language_str("vers") + ' ' + version + '</div>' +
|
||||
'<div style="font-size:14px;">' + language_str("copyright") + '</div>' +
|
||||
'</div>'
|
||||
});
|
||||
});
|
||||
|
||||
// ==========================================
|
||||
// 点击事件 (修改后:完全匹配“保存”弹窗样式)
|
||||
// ==========================================
|
||||
$(".card-list").on("click", '.card', async function () {
|
||||
let filePath = resolveDiskPath($(this).attr("data-file") || $(this).attr("data"));
|
||||
// 再次检查文件是否存在
|
||||
const soonData = await window.sysAPI.readJsonFile(filePath);
|
||||
if (soonData) {
|
||||
// 1. 文件存在 -> 正常打开
|
||||
let type = soonData.soonType ? soonData.soonType : (soonData.backBlackPic ? 2 : 1);
|
||||
const currentData = await window.sysAPI.readHistory();
|
||||
let newHistory = currentData.history.filter(item => resolveDiskPath(item.path) !== filePath);
|
||||
newHistory.unshift({ path: filePath, time: Date.now(), type: type });
|
||||
await window.sysAPI.writeHistory({ history: newHistory });
|
||||
|
||||
if (window.platformBridge && window.platformBridge.openDesignPage) window.platformBridge.openDesignPage(filePath, type);
|
||||
else if (ipcRenderer && ipcRenderer.send) ipcRenderer.send('open-design-page', filePath, type);
|
||||
} else {
|
||||
// 2. 文件不存在 -> 警告确认
|
||||
|
||||
// 【关键修改】
|
||||
// - 纯文字,左对齐 (text-align: left)
|
||||
// - 适当的 padding
|
||||
// - 去除所有图标
|
||||
let contentHtml = `
|
||||
<div style="padding: 20px 20px; text-align: left; color: #333; font-size: 14px;">
|
||||
${language_str("delContent")}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 使用 layer.open
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: language_str("delTitle"), // 标题:警告
|
||||
content: contentHtml,
|
||||
btn: [language_str("comfirm"), language_str("cancel")],
|
||||
btnAlign: 'r', // 【关键修改】按钮右对齐
|
||||
area: ['300px', 'auto'], // 【关键修改】宽度设为 300px,标准小窗口
|
||||
resize: false,
|
||||
shadeClose: true,
|
||||
yes: async function(index){
|
||||
const currentData = await window.sysAPI.readHistory();
|
||||
const newHistory = currentData.history.filter(item => resolveDiskPath(item.path) !== filePath);
|
||||
|
||||
if (newHistory.length !== currentData.history.length) {
|
||||
await window.sysAPI.writeHistory({ history: newHistory });
|
||||
layer.msg(language_str("deleted"), { icon: 1, time: 1000 });
|
||||
loadHistory();
|
||||
}
|
||||
layer.close(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function OpenDialog() {
|
||||
var dialogApi = (typeof dialog !== 'undefined' && dialog) ? dialog : (window.platformBridge && window.platformBridge.showOpenDialog ? { showOpenDialog: function(opts) { return window.platformBridge.showOpenDialog(opts); } } : null);
|
||||
if (!dialogApi) return;
|
||||
dialogApi.showOpenDialog({
|
||||
title: language_str("selectFile"),
|
||||
buttonLabel: language_str("comfirm"),
|
||||
filters: [
|
||||
{ name: language_str("SDFile"), extensions: ['soon'] },
|
||||
]
|
||||
}).then(async function(result) {
|
||||
if (result.canceled) return;
|
||||
var filePath = result.filePaths && result.filePaths[0];
|
||||
var file = (result.files && result.files[0]) || result.file;
|
||||
var soonData;
|
||||
if (file && file.text) {
|
||||
filePath = file.name || 'design.soon';
|
||||
soonData = await window.sysAPI.readJsonFile(file);
|
||||
// 网页端:将内容存到 sessionStorage,设计页通过 sessionKey 读取
|
||||
if (soonData && typeof sessionStorage !== 'undefined' && isWebPortal()) {
|
||||
try {
|
||||
sessionStorage.setItem('soondesign_session:' + filePath, JSON.stringify(soonData));
|
||||
filePath = 'soondesign_session:' + filePath;
|
||||
} catch (e) {}
|
||||
}
|
||||
} else if (filePath) {
|
||||
soonData = await window.sysAPI.readJsonFile(filePath);
|
||||
}
|
||||
if (!soonData) {
|
||||
if (!filePath && !file) return;
|
||||
layer.msg(language_str("noFile"));
|
||||
return;
|
||||
}
|
||||
var type = soonData.soonType ? soonData.soonType : (soonData.backBlackPic ? 2 : 1);
|
||||
var currentData = await window.sysAPI.readHistory();
|
||||
var newHistory = currentData.history.filter(function(item) { return resolveDiskPath(item.path) !== filePath; });
|
||||
newHistory.unshift({ path: filePath, time: Date.now(), type: type });
|
||||
await window.sysAPI.writeHistory({ history: newHistory });
|
||||
if (window.platformBridge && window.platformBridge.openDesignPage) window.platformBridge.openDesignPage(filePath, type);
|
||||
else if (ipcRenderer && ipcRenderer.send) ipcRenderer.send('open-design-page', filePath, type);
|
||||
loadHistory();
|
||||
}).catch(function(err) { console.log(err); });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 平台抽象层 - 统一接口定义
|
||||
* 桌面端由 js/platform/electron.js 实现,网页端由 js/platform/web.js 实现。
|
||||
* 业务代码通过 window.platformBridge 或兼容的 window.sysAPI / window.dialog 调用。
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// 若已存在(由 electron 或 web 注入),则不覆盖
|
||||
if (typeof window.platformBridge !== 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认空实现,避免未注入时报错(仅作占位,实际运行前必须注入 electron 或 web 实现)
|
||||
function notImplemented() {
|
||||
console.warn('[platform] bridge not injected');
|
||||
return Promise.reject(new Error('Platform bridge not available'));
|
||||
}
|
||||
|
||||
window.platformBridge = {
|
||||
readHistory: notImplemented,
|
||||
writeHistory: notImplemented,
|
||||
readJsonFile: notImplemented,
|
||||
showOpenDialog: notImplemented,
|
||||
showSaveDialog: notImplemented,
|
||||
writeFile: notImplemented,
|
||||
readFile: notImplemented,
|
||||
getAppVersion: function () { return Promise.resolve('0.0.0'); },
|
||||
getLocale: function () { return 'zh'; },
|
||||
getUserDataPath: function () { return ''; },
|
||||
getSystemFonts: function () { return Promise.resolve([]); },
|
||||
openDesignPage: function () {},
|
||||
openFirstPage: function () {},
|
||||
onClose: function () {},
|
||||
runClose: function () {},
|
||||
openHelp: function () {},
|
||||
printPdf: notImplemented,
|
||||
getScaleRate: function () { return typeof window !== 'undefined' ? (window.devicePixelRatio || 1) : 1; },
|
||||
clipboard: {
|
||||
readText: function () { return Promise.resolve(''); },
|
||||
writeText: function () { return Promise.resolve(); }
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* 平台抽象层 - Electron 实现
|
||||
* 仅在存在 require 且可加载 electron 时使用;封装 IPC、dialog、fs、remote。
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
if (typeof require === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var ipcRenderer, dialog, path, fs, remote, clipboard;
|
||||
try {
|
||||
ipcRenderer = require('electron').ipcRenderer;
|
||||
dialog = require('@electron/remote').dialog;
|
||||
path = require('path');
|
||||
fs = require('fs');
|
||||
remote = require('@electron/remote');
|
||||
clipboard = require('electron').clipboard;
|
||||
} catch (e) {
|
||||
console.warn('[platform] Electron modules not available', e);
|
||||
return;
|
||||
}
|
||||
|
||||
var exePath = remote.app.getPath('userData');
|
||||
var historyFilePath = path.join(exePath, 'data.json');
|
||||
|
||||
var bridge = {
|
||||
readHistory: function () {
|
||||
return ipcRenderer.invoke('history:read');
|
||||
},
|
||||
writeHistory: function (data) {
|
||||
return ipcRenderer.invoke('history:write', data);
|
||||
},
|
||||
readJsonFile: function (filePath) {
|
||||
if (!filePath) return Promise.resolve(null);
|
||||
return ipcRenderer.invoke('file:read-json', filePath);
|
||||
},
|
||||
showOpenDialog: function (options) {
|
||||
return dialog.showOpenDialog(options).then(function (result) {
|
||||
return { canceled: result.canceled, filePaths: result.filePaths };
|
||||
});
|
||||
},
|
||||
showSaveDialog: function (options) {
|
||||
return dialog.showSaveDialog(options);
|
||||
},
|
||||
writeFile: function (filePath, content) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var buf = Buffer.isBuffer(content) ? content : (typeof content === 'string' ? Buffer.from(content, 'utf8') : Buffer.from(content));
|
||||
fs.writeFile(filePath, buf, 'utf8', function (err) {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
writeFileSync: function (filePath, content) {
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
},
|
||||
readFile: function (filePath) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.readFile(filePath, function (err, data) {
|
||||
if (err) reject(err);
|
||||
else resolve(data);
|
||||
});
|
||||
});
|
||||
},
|
||||
readFileSync: function (filePath) {
|
||||
return fs.readFileSync(filePath);
|
||||
},
|
||||
existsSync: function (p) {
|
||||
return fs.existsSync(p);
|
||||
},
|
||||
getAppVersion: function () {
|
||||
return ipcRenderer.invoke('app:get-version');
|
||||
},
|
||||
getLocale: function () {
|
||||
return remote.app.getLocale();
|
||||
},
|
||||
getUserDataPath: function () {
|
||||
return exePath;
|
||||
},
|
||||
getSystemFonts: function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
ipcRenderer.once('font-list', function (ev, fonts) {
|
||||
resolve(fonts || []);
|
||||
});
|
||||
ipcRenderer.send('get-sys-fonts');
|
||||
setTimeout(function () {
|
||||
resolve([]);
|
||||
}, 10000);
|
||||
});
|
||||
},
|
||||
openDesignPage: function (file, type) {
|
||||
ipcRenderer.send('open-design-page', file || 'empty', type || '1');
|
||||
},
|
||||
openFirstPage: function () {
|
||||
ipcRenderer.send('open-first-page');
|
||||
},
|
||||
onClose: function (callback) {
|
||||
ipcRenderer.on('close', callback);
|
||||
},
|
||||
runClose: function () {
|
||||
ipcRenderer.send('run-close');
|
||||
},
|
||||
openHelp: function () {
|
||||
ipcRenderer.send('open-help-file');
|
||||
},
|
||||
printPdf: function (urlOrBlob) {
|
||||
ipcRenderer.send('print-pdf', urlOrBlob);
|
||||
},
|
||||
getScaleRate: function () {
|
||||
return typeof window !== 'undefined' && window.devicePixelRatio ? window.devicePixelRatio : 1;
|
||||
},
|
||||
clipboard: {
|
||||
readText: function () {
|
||||
return Promise.resolve(clipboard.readText());
|
||||
},
|
||||
writeText: function (text) {
|
||||
clipboard.writeText(text);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.platformBridge = bridge;
|
||||
|
||||
// 兼容现有命名
|
||||
window.sysAPI = {
|
||||
readHistory: bridge.readHistory,
|
||||
writeHistory: bridge.writeHistory,
|
||||
readJsonFile: bridge.readJsonFile,
|
||||
getAppVersion: bridge.getAppVersion
|
||||
};
|
||||
window.dialog = {
|
||||
showOpenDialog: bridge.showOpenDialog,
|
||||
showSaveDialog: bridge.showSaveDialog
|
||||
};
|
||||
window.path = path;
|
||||
window.fs = fs;
|
||||
window.remote = remote;
|
||||
window.ipcRenderer = ipcRenderer;
|
||||
window.clipboard = clipboard;
|
||||
window.exePath = exePath;
|
||||
window.fullPath = historyFilePath;
|
||||
})();
|
||||
Reference in New Issue
Block a user