优化bug

This commit is contained in:
24kycj
2025-12-19 19:37:41 +08:00
parent 94f7841873
commit 9056f144da
10 changed files with 2876 additions and 2657 deletions
+15 -15
View File
@@ -10,10 +10,10 @@
```bash ```bash
# 1. 安装依赖(只需一次) # 1. 安装依赖(只需一次)
npm install yarn install
# 2. 启动程序 # 2. 启动程序
npm start yarn start
``` ```
> **前置要求**:需要安装 [Node.js](https://nodejs.org/)(推荐 16 或更高版本) > **前置要求**:需要安装 [Node.js](https://nodejs.org/)(推荐 16 或更高版本)
@@ -26,13 +26,13 @@ npm start
```bash ```bash
# Windows 系统 # Windows 系统
npm run release:win yarn release:win
# Mac 系统 # Mac 系统
npm run release:mac yarn release:mac
# Linux 系统 # Linux 系统
npm run release:linux yarn release:linux
``` ```
**自动完成**:加密 → 测试 → 清理 → 打包 **自动完成**:加密 → 测试 → 清理 → 打包
@@ -45,8 +45,8 @@ npm run release:linux
#### 步骤 1:加密代码 #### 步骤 1:加密代码
```bash ```bash
npm run encrypt:win # Windows yarn encrypt:win # Windows
npm run encrypt:linux # Mac/Linux yarn encrypt:linux # Mac/Linux
``` ```
✅ 生成 `.jsc` 加密文件(自动覆盖旧文件) ✅ 生成 `.jsc` 加密文件(自动覆盖旧文件)
@@ -54,7 +54,7 @@ npm run encrypt:linux # Mac/Linux
#### 步骤 2:测试 #### 步骤 2:测试
```bash ```bash
npm start yarn start
``` ```
✅ 确认程序正常运行,无报错 ✅ 确认程序正常运行,无报错
@@ -62,7 +62,7 @@ npm start
#### 步骤 3:清理源文件 #### 步骤 3:清理源文件
```bash ```bash
npm run cleanup yarn cleanup
``` ```
✅ 删除 `.js` 源文件,自动备份到 `.backup` 文件夹 ✅ 删除 `.js` 源文件,自动备份到 `.backup` 文件夹
@@ -70,9 +70,9 @@ npm run cleanup
#### 步骤 4:打包 #### 步骤 4:打包
```bash ```bash
npm run build:win # Windows yarn build:win # Windows
npm run build:mac # Mac yarn build:mac # Mac
npm run build:linux # Linux yarn build:linux # Linux
``` ```
✅ 生成安装包到 `build` 文件夹 ✅ 生成安装包到 `build` 文件夹
@@ -84,7 +84,7 @@ npm run build:linux # Linux
### 恢复源文件 ### 恢复源文件
```bash ```bash
npm run restore yarn restore
``` ```
> 从 `.backup` 文件夹恢复所有源文件(误删除时使用) > 从 `.backup` 文件夹恢复所有源文件(误删除时使用)
@@ -92,7 +92,7 @@ npm run restore
### 清理加密文件 ### 清理加密文件
```bash ```bash
npm run cleanup:jsc yarn cleanup:jsc
``` ```
> 删除所有 `.jsc` 文件(重新加密前使用) > 删除所有 `.jsc` 文件(重新加密前使用)
@@ -113,7 +113,7 @@ npm run cleanup:jsc
} }
``` ```
然后重新运行 `npm run encrypt:win` 然后重新运行 `yarn encrypt:win`
--- ---
+50 -25
View File
@@ -41,6 +41,8 @@ fabric.Object.prototype.objectCaching = false;
ctx1 = canvas1.getSelectionContext(); ctx1 = canvas1.getSelectionContext();
ctx2 = canvas2.getSelectionContext(); ctx2 = canvas2.getSelectionContext();
is_bgi_add = false; is_bgi_add = false;
// 撤销/重做过程中使用的标志位,防止并发操作和误记录历史
window._isApplyingHistory = false;
// 同步到 window 对象 // 同步到 window 对象
window.ctx1 = ctx1; window.ctx1 = ctx1;
window.ctx2 = ctx2; window.ctx2 = ctx2;
@@ -1224,6 +1226,10 @@ window.selectObj = function selectObj(objIndices, isFromCanvas = false) {
} }
window.recordState = function recordState() { window.recordState = function recordState() {
// 撤销/重做过程中不记录历史,避免产生“中间状态”,导致需要点多次才能回到预期
if (window._isApplyingHistory) {
return;
}
//pre_objs.push(JSON.stringify(objs)); //pre_objs.push(JSON.stringify(objs));
//pre_json.push(canvas.toJSON(["selectable","hoverable","hoverCursor","text","fontStyle","fontWeight","underline"])); //pre_json.push(canvas.toJSON(["selectable","hoverable","hoverCursor","text","fontStyle","fontWeight","underline"]));
//next_json = []; //next_json = [];
@@ -1492,8 +1498,11 @@ function isObjectLocked(obj) {
window.isObjectLocked = isObjectLocked; window.isObjectLocked = isObjectLocked;
// 统一的 toJSON 属性列表,不包含锁定相关属性(锁定状态不应被保存) // 统一的 toJSON 属性列表,不包含锁定相关属性(锁定状态不应被保存)
// 统一的 toJSON 属性列表,不包含锁定相关属性(锁定状态不应被保存)
// 包含所有需要保存的属性,特别是自定义对象的属性(如 CurvedText 的 diameter, flipped
const TO_JSON_PROPERTIES = [ const TO_JSON_PROPERTIES = [
"selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline", "evented" "selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline",
"evented", "linethrough", "textBackgroundColor", "diameter", "flipped"
]; ];
// 暴露到全局作用域 // 暴露到全局作用域
window.TO_JSON_PROPERTIES = TO_JSON_PROPERTIES; window.TO_JSON_PROPERTIES = TO_JSON_PROPERTIES;
@@ -1608,13 +1617,14 @@ function initCanvasEvents() {
handleObjectSelected(e); handleObjectSelected(e);
}); });
// 阻止被锁定的对象被框选选中(只过滤被锁定的对象,保留未锁定的对象) // 阻止被锁定的对象和辅助线被框选选中
// - 锁定对象:不能被框选(只能通过列表选中)
// - 辅助线:永远不进入选区,避免选区被拉得很大
currentCanvas.on('before:selection:created', (e) => { currentCanvas.on('before:selection:created', (e) => {
if (!e.targets || e.targets.length === 0) return; if (!e.targets || e.targets.length === 0) return;
// 过滤掉被锁定的对象,保留辅助线和未锁定的对象
e.targets = e.targets.filter(target => { e.targets = e.targets.filter(target => {
if (target.isGuideLine) return true; // 保留辅助线 if (target.isGuideLine) return false; // 完全排除辅助线
return !isObjectLocked(target); // 除被锁定的对象,保留未锁定的对象 return !isObjectLocked(target); // 除被锁定的普通对象
}); });
}); });
@@ -1850,6 +1860,11 @@ function initRulers() {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
// 防止重复调用:如果正在拖拽,直接返回
if ($dragLine && $dragLine.length > 0) {
return;
}
// 创建临时显示的线 // 创建临时显示的线
$dragLine = $('<div class="guide-line-drag"></div>'); $dragLine = $('<div class="guide-line-drag"></div>');
$('.canvas-wrapper').append($dragLine); $('.canvas-wrapper').append($dragLine);
@@ -1922,18 +1937,13 @@ function initRulers() {
let canvasX = relX / zoom; let canvasX = relX / zoom;
let canvasY = relY / zoom; let canvasY = relY / zoom;
// 获取画布的实际尺寸和可见区域尺寸 // 获取画布的实际尺寸(用于决定辅助线的长度)
let canvasWidth = canvas.width || 10000; let canvasWidth = canvas.width || 2000;
let canvasHeight = canvas.height || 10000; let canvasHeight = canvas.height || 2000;
// 获取可见区域的尺寸(考虑缩放) // 使用与画布尺寸相关的长度,避免“整块区域都像辅助线”,同时保证能横穿整个画布
let visibleWidth = canvasWrapper.width() || canvasWidth; let lineExtent = Math.max(canvasWidth, canvasHeight) * 2;
let visibleHeight = canvasWrapper.height() || canvasHeight;
// 使用足够大的固定值,确保辅助线能够覆盖整个画布和可见区域
// 使用 100000 确保无论画布多大、如何缩放,都能覆盖整个可见区域
let lineExtent = 100000;
// 构造线对象,使用足够大的范围确保覆盖整个画布 // 构造线对象
// 线的起点和终点要足够远,确保覆盖整个可见区域
let linePoints = []; let linePoints = [];
if (type === 'horizontal') { if (type === 'horizontal') {
// 水平线:从很远的左边到很远的右边 // 水平线:从很远的左边到很远的右边
@@ -1962,19 +1972,34 @@ function initRulers() {
lockScalingX: true, lockScalingX: true,
lockScalingY: true, lockScalingY: true,
hoverCursor: type === 'horizontal' ? 'ns-resize' : 'ew-resize', hoverCursor: type === 'horizontal' ? 'ns-resize' : 'ew-resize',
perPixelTargetFind: false, // 使用边界框检测而不是像素检测 // design1 中改回边框命中模型,保证更容易点中拖拽
targetFindTolerance: 10, // 增加点击容差,扩大可点击区域(像素) perPixelTargetFind: false,
padding: 10, // 增加选择边界的内边距 targetFindTolerance: 10,
padding: 10,
isGuideLine: true, isGuideLine: true,
ignoreSave: true ignoreSave: true
}); });
// 限制移动方向 // 限制移动方向(只约束方向,不把整条线拖到极远,避免“整片区域是辅助线”的感觉)
guideLine.on('moving', function (e) { guideLine.on('moving', function () {
if (type === 'horizontal') { if (type === 'horizontal') {
this.left = -lineExtent; // 水平线:根据当前中心点的 Y,重算 x1/x2,使其横穿画布
const cy = this.getCenterPoint().y;
this.set({
x1: -lineExtent,
y1: cy,
x2: lineExtent,
y2: cy
});
} else { } else {
this.top = -lineExtent; // 垂直线:根据当前中心点的 X,重算 y1/y2,使其竖穿画布
const cx = this.getCenterPoint().x;
this.set({
x1: cx,
y1: -lineExtent,
x2: cx,
y2: lineExtent
});
} }
}); });
@@ -2200,8 +2225,8 @@ window.copySelection = function copySelection() {
let appData = (idx > 0 && objs[idx - 1]) ? objs[idx - 1] : { type: 0 }; let appData = (idx > 0 && objs[idx - 1]) ? objs[idx - 1] : { type: 0 };
return { return {
// 1. 保存画面样式 (坐标、颜色、字号等) // 1. 保存画面样式 (坐标、颜色、字号等,包含图片的 src)
fabricJson: obj.toObject(['id', 'name', 'selectable', 'evented', 'data']), fabricJson: obj.toObject(['id', 'name', 'selectable', 'evented', 'data', 'src']),
// 2. 保存业务属性 (深拷贝,防止引用冲突) // 2. 保存业务属性 (深拷贝,防止引用冲突)
appData: JSON.parse(JSON.stringify(appData)) appData: JSON.parse(JSON.stringify(appData))
}; };
+47 -3
View File
@@ -917,6 +917,11 @@ $("#set_bg").click(function () {//设置为背?
// onStateRestored 函数在 core.js 中定义并附加到 window 对象 // onStateRestored 函数在 core.js 中定义并附加到 window 对象
// ========================================== // ==========================================
$("#previous").click(function () { $("#previous").click(function () {
// 正在应用历史(撤销/重做)时,直接忽略新的点击,防止并发操作
if (window._isApplyingHistory) {
return;
}
if (step.val == 0) return; if (step.val == 0) return;
step.val--; step.val--;
@@ -928,29 +933,50 @@ $("#previous").click(function () {
objs = objs1; objs = objs1;
// 2. 恢复画布 (使用回调函数) // 2. 恢复画布 (使用回调函数)
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson1[step.val], function () { canvas.loadFromJSON(recordJson1[step.val], function () {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') { if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas); window.unlockAllObjects(canvas);
} }
// 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.regenerateQrCodesAndBarcodes(canvas, objs1);
}
if (typeof window.onStateRestored === 'function') { if (typeof window.onStateRestored === 'function') {
window.onStateRestored(); // 图片加载完才执行这一行 window.onStateRestored(); // 图片加载完才执行这一行
} }
window._isApplyingHistory = false;
}); });
} else { } else {
// 背面逻辑 // 背面逻辑
objs2 = JSON.parse(recordObjs2[step.val]); objs2 = JSON.parse(recordObjs2[step.val]);
objs = objs2; objs = objs2;
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson2[step.val], function () { canvas.loadFromJSON(recordJson2[step.val], function () {
// 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas);
}
// 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.regenerateQrCodesAndBarcodes(canvas, objs2);
}
if (typeof window.onStateRestored === 'function') { if (typeof window.onStateRestored === 'function') {
window.onStateRestored(); window.onStateRestored();
} }
window._isApplyingHistory = false;
}); });
} }
}); });
// ========================================== // ==========================================
$("#next").click(function () { $("#next").click(function () {
// 正在应用历史(撤销/重做)时,直接忽略新的点击,防止并发操作
if (window._isApplyingHistory) {
return;
}
// 获取当前历史记录的最大长度 // 获取当前历史记录的最大长度
let maxLen = $("#front_side").hasClass("ui-button-active") ? recordObjs1.length : recordObjs2.length; let maxLen = $("#front_side").hasClass("ui-button-active") ? recordObjs1.length : recordObjs2.length;
@@ -962,23 +988,39 @@ $("#next").click(function () {
objs1 = JSON.parse(recordObjs1[step.val]); objs1 = JSON.parse(recordObjs1[step.val]);
objs = objs1; objs = objs1;
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson1[step.val], function () { canvas.loadFromJSON(recordJson1[step.val], function () {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') { if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas); window.unlockAllObjects(canvas);
} }
// 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.regenerateQrCodesAndBarcodes(canvas, objs1);
}
if (typeof window.onStateRestored === 'function') { if (typeof window.onStateRestored === 'function') {
window.onStateRestored(); // 图片加载完才执行这一行 window.onStateRestored(); // 图片加载完才执行这一行
} }
window._isApplyingHistory = false;
}); });
} else { } else {
objs2 = JSON.parse(recordObjs2[step.val]); objs2 = JSON.parse(recordObjs2[step.val]);
objs = objs2; objs = objs2;
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson2[step.val], function () { canvas.loadFromJSON(recordJson2[step.val], function () {
// 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas);
}
// 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.regenerateQrCodesAndBarcodes(canvas, objs2);
}
if (typeof window.onStateRestored === 'function') { if (typeof window.onStateRestored === 'function') {
window.onStateRestored(); window.onStateRestored();
} }
window._isApplyingHistory = false;
}); });
} }
}); });
@@ -1280,8 +1322,9 @@ $("#circle_stroke_transparent").click(function () {
// saveAs 函数保留,因为它被作为回调传递给 output 函数(第52行) // saveAs 函数保留,因为它被作为回调传递给 output 函数(第52行)
function saveAs(op1, callback) { function saveAs(op1, callback) {
let j = canvas1.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"]); const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented', 'linethrough', 'textBackgroundColor', 'diameter', 'flipped'];
let b = canvas2.toJSON(["selectable", "hoverable", "hoverCursor", "text", "fontStyle", "fontWeight", "underline"]); let j = canvas1.toJSON(props);
let b = canvas2.toJSON(props);
// 清理不需要的 src 字段(使用 window.cleanupSrcFields,在 output.js 中定义) // 清理不需要的 src 字段(使用 window.cleanupSrcFields,在 output.js 中定义)
if (typeof window.cleanupSrcFields === 'function') { if (typeof window.cleanupSrcFields === 'function') {
@@ -1634,7 +1677,8 @@ $("#circle_text_back_color").bind('input propertychange', function () {
}) })
$("#circle_text_text").bind('input propertychange', function () { $("#circle_text_text").bind('input propertychange', function () {
let val = $("#circle_text_text").val().trim() let val = $("#circle_text_text").val().trim()
canvas.getActiveObject().set("text", ` ${val} `); // 有内容时不加首行缩进(前面的空格),只保留后面的空格
canvas.getActiveObject().set("text", val ? `${val} ` : ` ${val} `);
canvas.renderAll(); canvas.renderAll();
if (typeof window.updateList === 'function') { if (typeof window.updateList === 'function') {
window.updateList(); window.updateList();
+2534 -2403
View File
File diff suppressed because it is too large Load Diff
+108 -52
View File
@@ -1246,7 +1246,111 @@ window.openFile = function open(file) {
} }
}) })
// 先清空历史记录和重置 step
objs2 = j.bo || []
objs1 = j.fo || []
step1.val = 0
step2.val = 0
recordJson1 = []
recordJson2 = []
recordObjs1 = []
recordObjs2 = []
// 同步更新 window 变量
window.objs1 = objs1
window.objs2 = objs2
window.step1 = step1
window.step2 = step2
window.recordJson1 = recordJson1
window.recordJson2 = recordJson2
window.recordObjs1 = recordObjs1
window.recordObjs2 = recordObjs2
if ($('#front_side').hasClass('ui-button-active')) {
objs = objs1
recordObjs = recordObjs1
recordJson = recordJson1
step = step1
window.objs = objs1
window.recordObjs = recordObjs1
window.recordJson = recordJson1
window.step = step1
} else if ($('#back_side').hasClass('ui-button-active')) {
objs = objs2
recordObjs = recordObjs2
recordJson = recordJson2
background_image = background_image2
step = step2
window.objs = objs2
window.recordObjs = recordObjs2
window.recordJson = recordJson2
window.step = step2
}
// 加载 JSON,并在回调中重新生成二维码和条形码 // 加载 JSON,并在回调中重新生成二维码和条形码
// 使用 Promise 确保两个画布都加载完成后再保存初始状态
let canvas1Loaded = false
let canvas2Loaded = false
function saveInitialState() {
// 两个画布都加载完成后才保存初始状态
if (!canvas1Loaded || !canvas2Loaded) return
// 确保使用正确的 objs1 和 objs2(从文件加载的 fo 和 bo
// 注意:必须使用 fo 和 bo,因为它们是直接从文件 JSON 中读取的
// 同时确保 objs1 和 objs2 也被正确设置
objs1 = fo || []
objs2 = bo || []
window.objs1 = objs1
window.objs2 = objs2
// 更新背景图引用
background_image1 = canvas1.getObjects()[0]
background_image2 = canvas2.getObjects()[0]
window.background_image1 = background_image1
window.background_image2 = background_image2
if ($('#front_side').hasClass('ui-button-active')) {
background_image = background_image1
window.background_image = background_image1
objs = objs1
window.objs = objs1
} else if ($('#back_side').hasClass('ui-button-active')) {
background_image = background_image2 || background_image1
window.background_image = background_image
objs = objs2
window.objs = objs2
}
// 保存初始状态(使用从文件加载的 objs 数组)
recordObjs1.push(JSON.stringify(objs1))
const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented', 'linethrough', 'textBackgroundColor', 'diameter', 'flipped'];
let j1 = canvas1.toJSON(props)
j1.objects[0].hoverCursor = 'default'
recordJson1.push(j1)
recordObjs2.push(JSON.stringify(objs2))
let j2 = canvas2.toJSON(props)
j2.objects[0].hoverCursor = 'default'
recordJson2.push(j2)
// 同步更新 window 变量和局部变量
window.recordObjs1 = recordObjs1
window.recordJson1 = recordJson1
window.recordObjs2 = recordObjs2
window.recordJson2 = recordJson2
// 确保局部变量也同步
if ($('#front_side').hasClass('ui-button-active')) {
recordObjs = recordObjs1
recordJson = recordJson1
step = step1
} else if ($('#back_side').hasClass('ui-button-active')) {
recordObjs = recordObjs2
recordJson = recordJson2
step = step2
}
}
canvas1.loadFromJSON(j.f, function () { canvas1.loadFromJSON(j.f, function () {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') { if (typeof window.unlockAllObjects === 'function') {
@@ -1260,6 +1364,8 @@ window.openFile = function open(file) {
window.updateList(); window.updateList();
} }
canvas1.renderAll() canvas1.renderAll()
canvas1Loaded = true
saveInitialState()
}) })
canvas2.loadFromJSON(j.b, function () { canvas2.loadFromJSON(j.b, function () {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
@@ -1274,59 +1380,9 @@ window.openFile = function open(file) {
window.updateList(); window.updateList();
} }
canvas2.renderAll() canvas2.renderAll()
canvas2Loaded = true
saveInitialState()
}) })
background_image1 = canvas1.getObjects()[0]
background_image2 = canvas2.getObjects()[0]
objs2 = j.bo
objs1 = j.fo
step1.val = 0
step2.val = 0
recordJson1 = []
recordJson2 = []
recordObjs1 = []
recordObjs2 = []
if ($('#front_side').hasClass('ui-button-active')) {
objs = objs1
recordObjs = recordObjs1
recordJson = recordJson1
step = step1
background_image = background_image1
} else if ($('#back_side').hasClass('ui-button-active')) {
objs = objs2
recordObjs = recordObjs2
recordJson = recordJson2
background_image = background_image2
step = step2
}
canvas1.renderAll()
canvas2.renderAll()
// 调用 updateList,现在它已附加到 window 对象
if (typeof window.updateList === 'function') {
window.updateList(); /*
修复打开新文件后无法撤销的bug
2022-07-16
*/
} else {
// 延迟检查,确保 core.js 已加载
setTimeout(() => {
if (typeof window.updateList === 'function') {
window.updateList();
}
}, 100);
}
setTimeout(() => {
recordObjs1.push(JSON.stringify(objs1))
const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented'];
let j1 = canvas1.toJSON(props)
j1.objects[0].hoverCursor = 'default'
recordJson1.push(j1)
recordObjs2.push(JSON.stringify(objs2))
let j2 = canvas2.toJSON(props)
j2.objects[0].hoverCursor = 'default'
recordJson2.push(j2)
}, 0)
} }
function clearAll() { function clearAll() {
+113 -150
View File
@@ -28,69 +28,10 @@ $('body').keydown(function (event) {
const isModifierKey = event.ctrlKey || event.metaKey; // 兼容三端 const isModifierKey = event.ctrlKey || event.metaKey; // 兼容三端
// 快捷键处理(在输入框或文本区域中时不触发,除了删除相关的) // 快捷键处理(在输入框或文本区域中时不触发,除了删除相关的)
// 注意:大部分快捷键已在 core.js 中统一处理,此处只保留 ui.js 特有的快捷键
// 已移除的重复快捷键:CTRL/CMD+N/O/S/Z/Y/C/V/SHIFT+DEL/DEL(已在 core.js 中处理)
if (!_inputEdit && !_textEdit) { if (!_inputEdit && !_textEdit) {
// CTRL/CMD+N - 新建 // ui.js 特有的快捷键可以在这里添加
if (isModifierKey && event.keyCode == 78) {
event.preventDefault();
$('#new').click();
return;
}
// CTRL/CMD+O - 打开
if (isModifierKey && event.keyCode == 79) {
event.preventDefault();
$('#open').click();
return;
}
// CTRL/CMD+S - 保存
if (isModifierKey && !event.shiftKey && event.keyCode == 83) {
event.preventDefault();
$('#save').click();
return;
}
// CTRL/CMD+SHIFT+S - 保存副本
if (isModifierKey && event.shiftKey && event.keyCode == 83) {
event.preventDefault();
if (typeof window.output === 'function') {
window.output(null, saveAs);
}
return;
}
// CTRL/CMD+Z - 还原
if (isModifierKey && !event.shiftKey && event.keyCode == 90) {
event.preventDefault();
$('#previous').click();
return;
}
// CTRL/CMD+Y - 重做
if (isModifierKey && !event.shiftKey && event.keyCode == 89) {
event.preventDefault();
$('#next').click();
return;
}
// CTRL/CMD+C - 复制
if (isModifierKey && event.keyCode == 67 && event.target.nodeName == 'BODY') {
event.preventDefault();
window.copySelection();
return;
}
// CTRL/CMD+V - 粘贴
if (isModifierKey && event.keyCode == 86 && event.target.nodeName == 'BODY') {
event.preventDefault();
window.pasteFromClipboard();
return;
}
// CTRL/CMD+SHIFT+DEL (Windows) 或 CMD+SHIFT+Backspace (Mac) - 清空
if (isModifierKey && event.shiftKey && (event.keyCode == 46 || event.keyCode == 8)) {
event.preventDefault();
$('#clean').click();
return;
}
// CTRL/CMD+DEL (Windows) 或 CMD+Backspace (Mac) - 删除
if (isModifierKey && !event.shiftKey && (event.keyCode == 46 || event.keyCode == 8)) {
event.preventDefault();
$('#delete').click();
return;
}
} }
// 兼容三端的 modifier key 检测(Windows/Linux: Ctrl, Mac: Cmd/Meta // 兼容三端的 modifier key 检测(Windows/Linux: Ctrl, Mac: Cmd/Meta
if (event.keyCode == 17 || event.metaKey) { // Ctrl 键或 Meta 键(Mac Cmd if (event.keyCode == 17 || event.metaKey) { // Ctrl 键或 Meta 键(Mac Cmd
@@ -439,6 +380,10 @@ $('#front_side').click(function () {
recordObjs = recordObjs1 recordObjs = recordObjs1
recordJson = recordJson1 recordJson = recordJson1
step = step1 step = step1
// 同步到 window 对象,确保 recordState 使用正确的数组
window.recordObjs = recordObjs1
window.recordJson = recordJson1
window.step = step1
if (typeof window.updateList === 'function') { if (typeof window.updateList === 'function') {
window.updateList(); window.updateList();
} }
@@ -476,6 +421,10 @@ $('#back_side').click(function () {
recordObjs = recordObjs2 recordObjs = recordObjs2
recordJson = recordJson2 recordJson = recordJson2
step = step2 step = step2
// 同步到 window 对象,确保 recordState 使用正确的数组
window.recordObjs = recordObjs2
window.recordJson = recordJson2
window.step = step2
if (typeof window.updateList === 'function') { if (typeof window.updateList === 'function') {
window.updateList(); window.updateList();
} }
@@ -894,147 +843,155 @@ $('#set_bg').click(function () {
} }
}) })
$('#previous').click(function () { $('#previous').click(function () {
// 正在应用历史(撤销/重做)时,直接忽略新的点击,防止并发操作
if (window._isApplyingHistory) {
return;
}
//撤销一次操作 //撤销一次操作
if (step.val == 0) { if (step.val == 0) {
return return
} }
step.val-- step.val--
if ($('#front_side').hasClass('ui-button-active')) { if ($('#front_side').hasClass('ui-button-active')) {
// 使用 recordJson1 和 recordObjs1 // 使用 recordJson1 和 recordObjs1
if (recordObjs1 && recordObjs1[step.val]) {
objs1 = JSON.parse(recordObjs1[step.val]);
objs = objs1;
// 同步更新 window 变量
window.objs1 = objs1;
window.objs = objs;
}
if (recordJson1 && recordJson1[step.val]) { if (recordJson1 && recordJson1[step.val]) {
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson1[step.val], function() { canvas.loadFromJSON(recordJson1[step.val], function() {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') { if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas); window.unlockAllObjects(canvas);
} }
// 加载后更新列表和图标显示 // 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.updateList === 'function') { if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.updateList(); window.regenerateQrCodesAndBarcodes(canvas, objs1);
} }
canvas.renderAll(); // 调用状态恢复函数
}) if (typeof window.onStateRestored === 'function') {
} window.onStateRestored();
if (recordObjs1 && recordObjs1[step.val]) { }
objs1 = JSON.parse(recordObjs1[step.val]) window._isApplyingHistory = false;
objs = objs1 });
} }
} else if ($('#back_side').hasClass('ui-button-active')) { } else if ($('#back_side').hasClass('ui-button-active')) {
// 使用 recordJson2 和 recordObjs2 // 使用 recordJson2 和 recordObjs2
if (recordObjs2 && recordObjs2[step.val]) {
objs2 = JSON.parse(recordObjs2[step.val]);
objs = objs2;
// 同步更新 window 变量
window.objs2 = objs2;
window.objs = objs;
}
if (recordJson2 && recordJson2[step.val]) { if (recordJson2 && recordJson2[step.val]) {
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson2[step.val], function() { canvas.loadFromJSON(recordJson2[step.val], function() {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') { if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas); window.unlockAllObjects(canvas);
} }
// 加载后更新列表和图标显示 // 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.updateList === 'function') { if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.updateList(); window.regenerateQrCodesAndBarcodes(canvas, objs2);
} }
canvas.renderAll(); // 调用状态恢复函数
}) if (typeof window.onStateRestored === 'function') {
} window.onStateRestored();
if (recordObjs2 && recordObjs2[step.val]) { }
objs2 = JSON.parse(recordObjs2[step.val]) window._isApplyingHistory = false;
objs = objs2 });
} }
} }
background_image = canvas.getObjects()[0]
canvas.renderAll()
//recordState();
if (typeof window.updateList === 'function') {
window.updateList();
}
selectObj(-1)
$('#base_control').hide()
$('#line_control').hide()
$('#pic_control').hide()
$("#set_bg").hide();
$('#out_pic_control').hide()
$('#text_control').hide()
$("#circle_text_control").hide();
$('#out_text_control').hide()
$('#rect_control').hide()
$('#circle_control').hide()
$('#counter_control').hide()
$('#qrcode_Controll').hide()
$('#barcode_controll').hide()
}) })
$('#next').click(function () { $('#next').click(function () {
// 正在应用历史(撤销/重做)时,直接忽略新的点击,防止并发操作
if (window._isApplyingHistory) {
return;
}
//撤销一次撤销操作 //撤销一次撤销操作
// 根据当前是正面还是背面检查对应的 recordObjs 长度 // 根据当前是正面还是背面检查对应的 recordObjs 长度
let maxLen;
if ($('#front_side').hasClass('ui-button-active')) { if ($('#front_side').hasClass('ui-button-active')) {
if (step.val >= recordObjs1.length - 1) { maxLen = recordObjs1 ? recordObjs1.length : 0;
if (step.val >= maxLen - 1) {
return return
} }
} else if ($('#back_side').hasClass('ui-button-active')) { } else if ($('#back_side').hasClass('ui-button-active')) {
if (step.val >= recordObjs2.length - 1) { maxLen = recordObjs2 ? recordObjs2.length : 0;
if (step.val >= maxLen - 1) {
return return
} }
} else {
return;
} }
++step.val ++step.val
if ($('#front_side').hasClass('ui-button-active')) { if ($('#front_side').hasClass('ui-button-active')) {
// 使用 recordJson1 和 recordObjs1 // 使用 recordJson1 和 recordObjs1
if (recordObjs1 && recordObjs1[step.val]) {
objs1 = JSON.parse(recordObjs1[step.val]);
objs = objs1;
// 同步更新 window 变量
window.objs1 = objs1;
window.objs = objs;
}
if (recordJson1 && recordJson1[step.val]) { if (recordJson1 && recordJson1[step.val]) {
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson1[step.val], function() { canvas.loadFromJSON(recordJson1[step.val], function() {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') { if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas); window.unlockAllObjects(canvas);
} }
// 加载后更新列表和图标显示 // 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.updateList === 'function') { if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.updateList(); window.regenerateQrCodesAndBarcodes(canvas, objs1);
} }
canvas.renderAll(); // 调用状态恢复函数
}) if (typeof window.onStateRestored === 'function') {
} window.onStateRestored();
if (recordObjs1 && recordObjs1[step.val]) { }
objs1 = JSON.parse(recordObjs1[step.val]) window._isApplyingHistory = false;
objs = objs1 });
} }
} else if ($('#back_side').hasClass('ui-button-active')) { } else if ($('#back_side').hasClass('ui-button-active')) {
// 使用 recordJson2 和 recordObjs2 // 使用 recordJson2 和 recordObjs2
if (recordObjs2 && recordObjs2[step.val]) {
objs2 = JSON.parse(recordObjs2[step.val]);
objs = objs2;
// 同步更新 window 变量
window.objs2 = objs2;
window.objs = objs;
}
if (recordJson2 && recordJson2[step.val]) { if (recordJson2 && recordJson2[step.val]) {
window._isApplyingHistory = true;
canvas.loadFromJSON(recordJson2[step.val], function() { canvas.loadFromJSON(recordJson2[step.val], function() {
// 加载后解锁所有对象(背景除外) // 加载后解锁所有对象(背景除外)
if (typeof window.unlockAllObjects === 'function') { if (typeof window.unlockAllObjects === 'function') {
window.unlockAllObjects(canvas); window.unlockAllObjects(canvas);
} }
// 加载后更新列表和图标显示 // 重新生成二维码和条码(因为它们需要根据 objs 数据动态生成)
if (typeof window.updateList === 'function') { if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.updateList(); window.regenerateQrCodesAndBarcodes(canvas, objs2);
} }
canvas.renderAll(); // 调用状态恢复函数
}) if (typeof window.onStateRestored === 'function') {
window.onStateRestored();
}
window._isApplyingHistory = false;
});
} }
if (recordObjs2 && recordObjs2[step.val]) {
objs2 = JSON.parse(recordObjs2[step.val])
objs = objs2
}
}
background_image = canvas.getObjects()[0]
canvas.renderAll()
selectObj(-1)
$('#base_control').hide()
$('#line_control').hide()
$('#pic_control').hide()
$("#set_bg").hide();
$('#out_pic_control').hide()
$('#text_control').hide()
$("#circle_text_control").hide();
$('#out_text_control').hide()
$('#rect_control').hide()
$('#circle_control').hide()
$('#counter_control').hide()
$('#qrcode_Controll').hide()
$('#barcode_controll').hide()
if (typeof window.updateList === 'function') {
window.updateList();
} }
}) })
$('#clean').click(function () { $('#clean').click(function () {
@@ -1316,8 +1273,9 @@ $('#circle_stroke_transparent').click(function () {
// cleanupSrcFields 函数已在 output.js 中定义 // cleanupSrcFields 函数已在 output.js 中定义
function saveAs(op1, callback) { function saveAs(op1, callback) {
let j = canvas1.toJSON(['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline']) const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented', 'linethrough', 'textBackgroundColor', 'diameter', 'flipped'];
let b = canvas2.toJSON(['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline']) let j = canvas1.toJSON(props)
let b = canvas2.toJSON(props)
// 清理不需要的 src 字段(使用 window.cleanupSrcFields,在 output.js 中定义) // 清理不需要的 src 字段(使用 window.cleanupSrcFields,在 output.js 中定义)
if (typeof window.cleanupSrcFields === 'function') { if (typeof window.cleanupSrcFields === 'function') {
@@ -1464,7 +1422,7 @@ function addBackground() {
//pre_objs1.push(JSON.stringify(objs1)); //pre_objs1.push(JSON.stringify(objs1));
recordObjs1.push(JSON.stringify(objs1)) recordObjs1.push(JSON.stringify(objs1))
const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented']; const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented', 'linethrough', 'textBackgroundColor', 'diameter', 'flipped'];
recordJson1.push(canvas1.toJSON(props)) recordJson1.push(canvas1.toJSON(props))
}) })
// 类型改变 // 类型改变
@@ -1483,7 +1441,7 @@ function addBackground() {
//pre_objs2.push(JSON.stringify(objs2)); //pre_objs2.push(JSON.stringify(objs2));
recordObjs2.push(JSON.stringify(objs2)) recordObjs2.push(JSON.stringify(objs2))
const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented']; const props = window.TO_JSON_PROPERTIES || ['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline', 'evented', 'linethrough', 'textBackgroundColor', 'diameter', 'flipped'];
recordJson2.push(canvas2.toJSON(props)) recordJson2.push(canvas2.toJSON(props))
let file = GetFile().get('file') let file = GetFile().get('file')
@@ -1635,6 +1593,7 @@ function selectOutPic(target, index) {
} }
function addText(pointer) { function addText(pointer) {
// 设置默认字号为30px(像素值),这样显示的字号约为 30 * 72 / 300 = 7.2pt
let text = new fabric.IText('TEXT', { let text = new fabric.IText('TEXT', {
//源:文本 //源:文本
left: pointer.x, left: pointer.x,
@@ -1643,6 +1602,7 @@ function addText(pointer) {
textBackgroundColor: '', textBackgroundColor: '',
fontStyle: 'normal', fontStyle: 'normal',
fontWeight: 'normal', fontWeight: 'normal',
fontSize: 30,
underline: false, underline: false,
linethrough: false, linethrough: false,
fontFamily: 'Arial' fontFamily: 'Arial'
@@ -2405,7 +2365,8 @@ $("#circle_text_back_color").bind('input propertychange', function () {
}) })
$("#circle_text_text").bind('input propertychange', function () { $("#circle_text_text").bind('input propertychange', function () {
let val = $("#circle_text_text").val().trim() let val = $("#circle_text_text").val().trim()
canvas.getActiveObject().set("text", ` ${val} `); // 有内容时不加首行缩进(前面的空格),只保留后面的空格
canvas.getActiveObject().set("text", val ? `${val} ` : ` ${val} `);
canvas.renderAll(); canvas.renderAll();
if (typeof window.updateList === 'function') { if (typeof window.updateList === 'function') {
window.updateList(); window.updateList();
@@ -2448,7 +2409,8 @@ $("#out_text_size").bind('input propertychange', function () {
}) })
$('#out_text_text').bind('input propertychange', function () { $('#out_text_text').bind('input propertychange', function () {
let val = $("#out_text_text").val().trim() let val = $("#out_text_text").val().trim()
canvas.getActiveObject().set("text", ` ${val} `); // 有内容时不加首行缩进(前面的空格),只保留后面的空格
canvas.getActiveObject().set("text", val ? `${val} ` : ` ${val} `);
canvas.renderAll() canvas.renderAll()
if (typeof window.updateList === 'function') { if (typeof window.updateList === 'function') {
window.updateList(); window.updateList();
@@ -2565,7 +2527,8 @@ $('#line_dist').bind('input propertychange', function () {
}) })
$('#text_text').bind('input propertychange', function () { $('#text_text').bind('input propertychange', function () {
let val = $("#text_text").val().trim() let val = $("#text_text").val().trim()
canvas.getActiveObject().set("text", ` ${val} `); // 有内容时不加首行缩进(前面的空格),只保留后面的空格
canvas.getActiveObject().set("text", val ? `${val} ` : ` ${val} `);
canvas.renderAll() canvas.renderAll()
if (typeof window.updateList === 'function') { if (typeof window.updateList === 'function') {
window.updateList(); window.updateList();
+1 -1
View File
@@ -8,7 +8,7 @@
"package:x64": "electron-forge package --arch=x64", "package:x64": "electron-forge package --arch=x64",
"package:arm64": "electron-forge package --arch=arm64", "package:arm64": "electron-forge package --arch=arm64",
"merge": "node ./merge.js", "merge": "node ./merge.js",
"package:universal": "npm run package:x64 && npm run package:arm64 && npm run merge" "package:universal": "yarn package:x64 && yarn package:arm64 && yarn merge"
}, },
"electronPackagerConfig": { "electronPackagerConfig": {
"packageManager": "npm", "packageManager": "npm",
+5 -5
View File
@@ -15,10 +15,10 @@
"build:linux": "electron-builder --linux --x64", "build:linux": "electron-builder --linux --x64",
"build:linux:arm64": "electron-builder --linux --arm64", "build:linux:arm64": "electron-builder --linux --arm64",
"build:all": "electron-builder -mwl", "build:all": "electron-builder -mwl",
"release:win": "npm run encrypt:win && npm run cleanup && npm run build:win", "release:win": "yarn encrypt:win && yarn cleanup && yarn build:win",
"release:mac": "npm run encrypt:linux && npm run cleanup && npm run build:mac", "release:mac": "yarn encrypt:linux && yarn cleanup && yarn build:mac",
"release:linux": "npm run encrypt:linux && npm run cleanup && npm run build:linux", "release:linux": "yarn encrypt:linux && yarn cleanup && yarn build:linux",
"release:linux:arm64": "npm run build:linux:arm64" "release:linux:arm64": "yarn build:linux:arm64"
}, },
"build": { "build": {
"productName": "SoonDesign", "productName": "SoonDesign",
@@ -103,7 +103,7 @@
} }
}, },
"electronPackagerConfig": { "electronPackagerConfig": {
"packageManager": "npm", "packageManager": "yarn",
"icon": "./public/images/favicon.ico" "icon": "./public/images/favicon.ico"
}, },
+2 -2
View File
@@ -83,8 +83,8 @@
2. **运行加密脚本** 2. **运行加密脚本**
```bash ```bash
npm run encrypt:win # Windows yarncrypt:win # Windows
npm run encrypt:linux # Linux/Mac yarncrypt:linux # Linux/Mac
``` ```
3. **验证结果** 3. **验证结果**
+1 -1
View File
@@ -14,7 +14,7 @@ if (!process.versions.electron) {
console.error('请使用以下方式运行:'); console.error('请使用以下方式运行:');
console.error(' Windows: encrypt-electron-win.bat'); console.error(' Windows: encrypt-electron-win.bat');
console.error(' Linux/Mac: ./encrypt-electron.sh'); console.error(' Linux/Mac: ./encrypt-electron.sh');
console.error(' 或: npm run encrypt:win / npm run encrypt:linux'); console.error(' 或: yarncrypt:win / yaryarnpt:linux');
process.exit(1); process.exit(1);
} }