更新优化

This commit is contained in:
24kycj
2025-12-12 00:10:13 +08:00
parent 026d564c92
commit 50ff0d347a
34 changed files with 8669 additions and 7884 deletions
+4
View File
@@ -7,3 +7,7 @@ npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep
# 加密相关文件(不要提交到 Git)
*.jsc
.backup/
+160
View File
@@ -0,0 +1,160 @@
# SoonDesign 设计工具
简单易用的桌面设计软件,支持光盘和卡片模板设计。
---
## 🚀 快速开始
### 第一次使用
```bash
# 1. 安装依赖(只需一次)
npm install
# 2. 启动程序
npm start
```
> **前置要求**:需要安装 [Node.js](https://nodejs.org/)(推荐 16 或更高版本)
---
## 📦 发布流程(傻瓜式操作)
### 方式一:一键发布 ⭐ 推荐
```bash
# Windows 系统
npm run release:win
# Mac 系统
npm run release:mac
# Linux 系统
npm run release:linux
```
**自动完成**:加密 → 测试 → 清理 → 打包
**安装包位置**`build` 文件夹
---
### 方式二:分步操作(手动控制)
#### 步骤 1:加密代码
```bash
npm run encrypt:win # Windows
npm run encrypt:linux # Mac/Linux
```
✅ 生成 `.jsc` 加密文件(自动覆盖旧文件)
#### 步骤 2:测试
```bash
npm start
```
✅ 确认程序正常运行,无报错
#### 步骤 3:清理源文件
```bash
npm run cleanup
```
✅ 删除 `.js` 源文件,自动备份到 `.backup` 文件夹
#### 步骤 4:打包
```bash
npm run build:win # Windows
npm run build:mac # Mac
npm run build:linux # Linux
```
✅ 生成安装包到 `build` 文件夹
---
## 🔧 其他常用命令
### 恢复源文件
```bash
npm run restore
```
> 从 `.backup` 文件夹恢复所有源文件(误删除时使用)
### 清理加密文件
```bash
npm run cleanup:jsc
```
> 删除所有 `.jsc` 文件(重新加密前使用)
### 修改文件名映射
编辑 `lib/module-map.json`
```json
{
"mappings": {
"design1": {
"core": "core1", // 修改这里
"output": "output1",
"ui": "ui1"
}
}
}
```
然后重新运行 `npm run encrypt:win`
---
## ⚠️ 注意事项
- **开发时**:保留 `.js` 源文件,不要运行 `cleanup`
- **发布时**:必须先加密、再清理、最后打包
- **备份在**`.backup` 文件夹(不会被打包进应用)
---
## 📁 项目结构
```
lib/
├── module-map.json - 文件名映射配置
├── index.js - 首页入口
├── design1.js - 光盘模板入口
├── design2.js - 卡片模板入口
├── design1/ - 光盘模板功能模块
│ ├── core.js - 核心功能
│ ├── output.js - 导出/保存
│ └── ui.js - 界面交互
└── design2/ - 卡片模板功能模块
├── core.js
├── output.js
└── ui.js
scripts/encrypt/ - 加密脚本(不会被打包)
.backup/ - 源文件备份(不会被打包)
```
---
## 🔧 调试模式
`F12``Ctrl+Shift+I` 打开开发者工具查看错误信息。
---
## 📚 更多文档
- [加密详细说明](scripts/encrypt/README.md)
- 当前版本:v2.3.017
+39 -8
View File
File diff suppressed because one or more lines are too long
+40 -9
View File
File diff suppressed because one or more lines are too long
+40 -7
View File
@@ -216,13 +216,46 @@
<script src="./layui/layui.js"></script>
<script>
require('bytenode');
require.extensions['.dl'] = require.extensions['.jsc'];
try {
require('./lib/index.js');
} catch (error) {
require('./lib/directx9.dl');
}
(function() {
const path = require('path'), fs = require('fs'), remote = require('@electron/remote');
const appPath = (() => { try { return remote.app.getAppPath(); } catch(e) { return __dirname || process.cwd(); } })();
// 读取模块映射配置
let mappedName = 'index';
try {
const moduleMapPath = path.join(appPath, 'lib', 'module-map.json');
if (fs.existsSync(moduleMapPath)) {
const moduleMap = JSON.parse(fs.readFileSync(moduleMapPath, 'utf8'));
mappedName = moduleMap.mappings?.main?.index || 'index';
}
} catch(e) {}
const jscPath = path.join(appPath, 'lib', `${mappedName}.jsc`);
const jsPath = path.join(appPath, 'lib', 'index.js');
// 优先加载映射的 .jsc 文件
try {
require('bytenode');
require.extensions['.dl'] = require.extensions['.jsc'];
if (fs.existsSync(jscPath)) {
require(jscPath);
console.log(`[加载] ✅ index → ${mappedName}.jsc`);
return;
}
} catch(e) { console.error('[加载] .jsc 加载失败:', e.message); }
// 回退到 .js 源文件
if (fs.existsSync(jsPath)) {
try {
require(jsPath);
console.log('[加载] ✅ index → index.js');
} catch(e) {
alert('文件加载失败!\n\n错误: ' + e.message);
}
} else {
alert('文件不存在!\n\n请检查:\n1. ' + jscPath + '\n2. ' + jsPath);
}
})();
</script>
</body>
</html>
BIN
View File
Binary file not shown.
+403 -167
View File
@@ -5,7 +5,7 @@ require('./common/fabric-ext.js');
// 过滤 Canvas2D willReadFrequently 警告(不影响功能,只是性能提示)
if (typeof console !== 'undefined' && console.warn) {
const originalWarn = console.warn;
console.warn = function(...args) {
console.warn = function (...args) {
const message = args.join(' ');
// 过滤掉 willReadFrequently 相关的警告
if (message.includes('willReadFrequently') || message.includes('getImageData')) {
@@ -17,117 +17,219 @@ if (typeof console !== 'undefined' && console.warn) {
// Electron相关导入
const remote = require('@electron/remote');
var path = require('path');
var path = require('path');
const exePath = remote.app.getPath('userData');
const { ipcRenderer } = require('electron');
const { dialog } = require('@electron/remote');
var jrQrcode = require('jr-qrcode');
var JsBarcode = require('jsbarcode');
var jrQrcode = require('jr-qrcode');
var JsBarcode = require('jsbarcode');
const { clipboard } = require('electron');
const dpi = 600;
// 发送IPC消息
ipcRenderer.send('get-sys-fonts');
ipcRenderer.send('get-scale-rate');
ipcRenderer.send('get-sys-fonts');
ipcRenderer.send('get-scale-rate');
// 使用layui
layui.use(['layer', 'slider', 'form', 'colorpicker'], function () {
let myDate = new Date();
let s_lan = "";
ipcRenderer.send('get-sys-language');
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;
}
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.join(exePath, 'data.json');
// 确保 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) {
console.log('des 加密错误:', err);
APIFMS = '';
}
return APIFMS;
};
// 向后兼容
function desEncrypt(str, key = "df6a551ca43181fc485f3043bcdd2fbc") {
var keyHex = CryptoJS.enc.Utf8.parse(key);
var encrypted = CryptoJS.DES.encrypt(str, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
return window.desEncrypt(str, key);
}
// 多语言切换
function langua_ge(lan = 'zh') {
$("[language='m']").each(function (i) {
$(this).html($(this).attr(lan));
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));
$("[language='t']").each(function (i) {
$(this).attr("title", $(this).attr(lan));
});
}
};
// 向后兼容
function langua_ge(lan = 'zh') {
return window.langua_ge(lan);
}
// 多语言字符串获取
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." }
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." }
};
return t[str] ? t[str][s_lan] : str;
// 从全局作用域获取 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参数
function GetFile() {
window.GetFile = function GetFile() {
const params = new URLSearchParams(window.location.search);
return params;
};
// 向后兼容
function GetFile() {
return window.GetFile();
}
// 日期格式化
function getDate() {
let date = new Date();
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
}
window.getDate = function getDate() {
let date = new Date();
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
};
// 向后兼容
function getDate() {
return window.getDate();
}
// 获取对象的绝对坐标
function getAbsoluteXY(_obj) {
let coord = _obj.get("lineCoords");
window.getAbsoluteXY = function getAbsoluteXY(_obj) {
let coord = _obj.get("lineCoords");
return [_obj.get("left"), _obj.get("top")];
};
// 向后兼容
function getAbsoluteXY(_obj) {
return window.getAbsoluteXY(_obj);
}
// 计算两点之间的距离
function getDisdance(x1, y1, x2, y2) {
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);
}
// 计算角度(相对于画布中心)
function getDeg(pointer, canvas) {
window.getDeg = function getDeg(pointer, canvas) {
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var mouseX = pointer.x;
@@ -141,130 +243,169 @@ const dpi = 600;
}
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) {
for (let item of objs1) {
if (item.name == name) {
return false;
}
}
for (let item of objs2) {
if (item.name == name) {
return false;
}
}
return true;
}
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) {
let num = (objs1.length + objs2.length) - 1;
do {
num++;
}
while (!checkName(pre_name + num, objs1, objs2));
return pre_name + num;
}
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) {
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;
}
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) {
let temp_objs = canvas.getObjects();
let i = 0;
for (let res of temp_objs) {
if (target == res) {
return objs[i].type;
}
i++;
}
}
return window.getType(target, canvas, objs);
}
// 获取坐标的最小X值
function getCoordsMinX(acoords) {
window.getCoordsMinX = function getCoordsMinX(acoords) {
let x = acoords[0].x;
for (let item of acoords) {
if (item.x < x) {
for (let item of acoords) {
if (item.x < x) {
x = item.x;
}
}
return x;
}
}
}
return x;
};
// 向后兼容
function getCoordsMinX(acoords) {
return window.getCoordsMinX(acoords);
}
// 获取坐标的最大X值
function getCoordsMaxX(acoords) {
window.getCoordsMaxX = function getCoordsMaxX(acoords) {
let x = acoords[0].x;
for (let item of acoords) {
if (item.x > x) {
for (let item of acoords) {
if (item.x > x) {
x = item.x;
}
}
return x;
}
}
}
return x;
};
// 向后兼容
function getCoordsMaxX(acoords) {
return window.getCoordsMaxX(acoords);
}
// 获取坐标的最小Y值
function getCoordsMinY(acoords) {
window.getCoordsMinY = function getCoordsMinY(acoords) {
let y = acoords[0].y;
for (let item of acoords) {
if (item.y < y) {
for (let item of acoords) {
if (item.y < y) {
y = item.y;
}
}
return y;
}
}
}
return y;
};
// 向后兼容
function getCoordsMinY(acoords) {
return window.getCoordsMinY(acoords);
}
// 获取坐标的最大Y值
function getCoordsMaxY(acoords) {
window.getCoordsMaxY = function getCoordsMaxY(acoords) {
let y = acoords[0].y;
for (let item of acoords) {
if (item.y > y) {
for (let item of acoords) {
if (item.y > y) {
y = item.y;
}
}
return y;
}
}
}
return y;
};
// 向后兼容
function getCoordsMaxY(acoords) {
return window.getCoordsMaxY(acoords);
}
// ==========================================
// 全局变量定义(所有模块共享)
// 注意:所有变量都附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
// ==========================================
let addState = 0; // 0是不添加
let background_image, background_image1, background_image2;
let bg_version = 1; // front_bg1.png front_bg2.png
var zoom = 1;
let pre_add_image; // 预添加的图片对象(用于addPic)
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)
// 内部剪贴板
let clipboardData = {
window.clipboardData = {
data: null,
offset: 10 // 粘贴位置偏移量
};
// 文件管理
var openAs = {
window.openAs = {
_name: "",
set name(val) {
if (val == "") {
$("title").html('Soon Design');
} else {
} else {
$("title").html('Soon Design - ' + val);
}
this._name = val;
@@ -275,46 +416,141 @@ const dpi = 600;
};
// 对象数组和历史记录
let objs1 = [];
let objs2 = [];
let step1 = { val: 0 }, step2 = { val: 0 };
let step = step1;
let objs = objs1;
let recordJson1 = [], recordJson2 = [], recordJson = recordJson1, recordObjs1 = [], recordObjs2 = [], recordObjs = recordObjs1;
let pre_objs1 = [], pre_objs2 = [], next_objs1 = [], next_objs2 = [], pre_objs = pre_objs1, next_objs = next_objs1;
let pre_json1 = [], pre_json2 = [], next_json1 = [], next_json2 = [], pre_json = pre_json1, next_json = next_json1;
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中初始化)
var canvas1, canvas2;
var canvas; // 当前活动的画布(canvas1 或 canvas2
var ctx1, ctx2;
let is_bgi_add = false;
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模块
// 由于模块代码需要在layui.use内部执行,我们通过动态加载的方式
const fs = require('fs');
// 加载core.js(核心功能)
let bytenode;
try {
const coreCode = fs.readFileSync(__dirname + '/design1/core.js', 'utf8');
eval(coreCode);
bytenode = require('bytenode');
require.extensions['.dl'] = require.extensions['.jsc'];
} catch (e) {
console.error('加载core.js失败:', e);
console.warn('[加载模块] bytenode 未安装,无法加载 .jsc 文件');
}
// 加载output.js(输出功能,需要在ui.js之前加载,因为ui.js中的saveAs会调用output.js中的函数)
const appPath = (() => { try { return remote.app.getAppPath(); } catch (e) { return __dirname || process.cwd(); } })();
// 读取模块映射配置(从 lib 目录读取,跨平台兼容)
let moduleMap = {};
try {
const outputCode = fs.readFileSync(__dirname + '/design1/output.js', 'utf8');
eval(outputCode);
const moduleMapPath = path.join(appPath, 'lib', 'module-map.json');
if (fs.existsSync(moduleMapPath)) {
moduleMap = JSON.parse(fs.readFileSync(moduleMapPath, 'utf8'));
}
} catch (e) {
console.error('加载output.js失败:', e);
console.warn('[加载模块] 无法读取模块映射配置,使用默认文件名');
}
// 加载ui.jsUI控制
try {
const uiCode = fs.readFileSync(__dirname + '/design1/ui.js', 'utf8');
eval(uiCode);
} catch (e) {
console.error('加载ui.js失败:', e);
// 获取映射后的文件名(跨平台兼容
function getMappedFileName(moduleName) {
if (moduleMap.mappings && moduleMap.mappings.design1 && moduleMap.mappings.design1[moduleName]) {
return moduleMap.mappings.design1[moduleName];
}
return moduleName; // 默认使用原文件名
}
const loadModule = (moduleName) => {
const mappedName = getMappedFileName(moduleName);
const jscPath = path.join(appPath, 'lib', 'design1', `${mappedName}.jsc`);
const jsPath = path.join(appPath, 'lib', 'design1', `${moduleName}.js`);
// 优先加载映射的 .jsc 文件
if (fs.existsSync(jscPath) && bytenode) {
try {
require(jscPath);
console.log(`[加载模块] ✅ ${moduleName}${mappedName}.jsc`);
return;
} catch (e) {
console.error(`[加载模块] ❌ ${moduleName}.jsc 加载失败:`, e.message);
}
}
// 回退到 .js 源文件
if (fs.existsSync(jsPath)) {
try {
eval(fs.readFileSync(jsPath, 'utf8'));
console.log(`[加载模块] ✅ ${moduleName}${moduleName}.js`);
} catch (e) {
console.error(`[加载模块] ❌ ${moduleName}.js 加载失败:`, e.message);
alert(`加载 ${moduleName} 失败!\n\n错误: ${e.message}`);
}
} else {
alert(`文件不存在!\n\n请检查:\n1. ${jscPath}\n2. ${jsPath}`);
}
};
loadModule('output'); // 先加载 output,因为 core 中的 addBackground() 需要调用 output 中的 open()
loadModule('core');
loadModule('ui');
});
+236 -951
View File
File diff suppressed because it is too large Load Diff
+288 -169
View File
File diff suppressed because one or more lines are too long
+530 -284
View File
File diff suppressed because one or more lines are too long
+278 -54
View File
@@ -38,17 +38,67 @@ ipcRenderer.send('get-scale-rate');
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.join(exePath, 'data.json');
// 确保 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加密函数
function desEncrypt(str, key = "df6a551ca43181fc485f3043bcdd2fbc") {
window.desEncrypt = function desEncrypt(str, key = "df6a551ca43181fc485f3043bcdd2fbc") {
var APIFMS;
try {
var keyHex_encrypt = CryptoJS.enc.Utf8.parse(key);
@@ -62,20 +112,28 @@ ipcRenderer.send('get-scale-rate');
console.log(err);
}
return APIFMS;
};
// 向后兼容
function desEncrypt(str, key = "df6a551ca43181fc485f3043bcdd2fbc") {
return window.desEncrypt(str, key);
}
// 多语言切换
function langua_ge(lan = 'zh') {
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);
}
// 多语言字符串获取
function language_str(str) {
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" },
@@ -103,37 +161,64 @@ ipcRenderer.send('get-scale-rate');
"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;
// 从全局作用域获取 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参数
function GetFile() {
window.GetFile = function GetFile() {
const params = new URLSearchParams(window.location.search);
return params;
};
// 向后兼容
function GetFile() {
return window.GetFile();
}
// 日期格式化
function getDate() {
window.getDate = function getDate() {
let date = new Date();
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
};
// 向后兼容
function getDate() {
return window.getDate();
}
// 获取对象的绝对坐标
function getAbsoluteXY(_obj) {
window.getAbsoluteXY = function getAbsoluteXY(_obj) {
let coord = _obj.get("lineCoords");
return [_obj.get("left"), _obj.get("top")];
};
// 向后兼容
function getAbsoluteXY(_obj) {
return window.getAbsoluteXY(_obj);
}
// 计算两点之间的距离
function getDisdance(x1, y1, x2, y2) {
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);
}
// 计算角度(相对于画布中心)
function getDeg(pointer, canvas) {
window.getDeg = function getDeg(pointer, canvas) {
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var mouseX = pointer.x;
@@ -147,10 +232,14 @@ ipcRenderer.send('get-scale-rate');
}
angleDeg += 90;
return angleDeg;
};
// 向后兼容
function getDeg(pointer, canvas) {
return window.getDeg(pointer, canvas);
}
// 检查字段名是否重复
function checkName(name, objs1, objs2) {
window.checkName = function checkName(name, objs1, objs2) {
for (let item of objs1) {
if (item.name == name) {
return false;
@@ -162,20 +251,28 @@ ipcRenderer.send('get-scale-rate');
}
}
return true;
};
// 向后兼容
function checkName(name, objs1, objs2) {
return window.checkName(name, objs1, objs2);
}
// 返回不重复的字段名
function resName(pre_name, objs1, objs2) {
window.resName = function resName(pre_name, objs1, objs2) {
let num = (objs1.length + objs2.length) - 1;
do {
num++;
}
while (!checkName(pre_name + num, objs1, objs2));
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);
}
// 获取对象在画布中的索引
function getIndex(target, canvas, _canvas = null) {
window.getIndex = function getIndex(target, canvas, _canvas = null) {
let temp_objs;
if (_canvas == null) {
temp_objs = canvas.getObjects();
@@ -190,10 +287,14 @@ ipcRenderer.send('get-scale-rate');
i++;
}
return 0;
};
// 向后兼容
function getIndex(target, canvas, _canvas = null) {
return window.getIndex(target, canvas, _canvas);
}
// 获取对象类型
function getType(target, canvas, objs) {
window.getType = function getType(target, canvas, objs) {
let temp_objs = canvas.getObjects();
let i = 0;
for (let res of temp_objs) {
@@ -202,10 +303,14 @@ ipcRenderer.send('get-scale-rate');
}
i++;
}
};
// 向后兼容
function getType(target, canvas, objs) {
return window.getType(target, canvas, objs);
}
// 获取坐标的最小X值
function getCoordsMinX(acoords) {
window.getCoordsMinX = function getCoordsMinX(acoords) {
let x = acoords[0].x;
for (let item of acoords) {
if (item.x < x) {
@@ -213,10 +318,14 @@ ipcRenderer.send('get-scale-rate');
}
}
return x;
};
// 向后兼容
function getCoordsMinX(acoords) {
return window.getCoordsMinX(acoords);
}
// 获取坐标的最大X值
function getCoordsMaxX(acoords) {
window.getCoordsMaxX = function getCoordsMaxX(acoords) {
let x = acoords[0].x;
for (let item of acoords) {
if (item.x > x) {
@@ -224,10 +333,14 @@ ipcRenderer.send('get-scale-rate');
}
}
return x;
};
// 向后兼容
function getCoordsMaxX(acoords) {
return window.getCoordsMaxX(acoords);
}
// 获取坐标的最小Y值
function getCoordsMinY(acoords) {
window.getCoordsMinY = function getCoordsMinY(acoords) {
let y = acoords[0].y;
for (let item of acoords) {
if (item.y < y) {
@@ -235,10 +348,14 @@ ipcRenderer.send('get-scale-rate');
}
}
return y;
};
// 向后兼容
function getCoordsMinY(acoords) {
return window.getCoordsMinY(acoords);
}
// 获取坐标的最大Y值
function getCoordsMaxY(acoords) {
window.getCoordsMaxY = function getCoordsMaxY(acoords) {
let y = acoords[0].y;
for (let item of acoords) {
if (item.y > y) {
@@ -246,26 +363,37 @@ ipcRenderer.send('get-scale-rate');
}
}
return y;
};
// 向后兼容
function getCoordsMaxY(acoords) {
return window.getCoordsMaxY(acoords);
}
// ==========================================
// 全局变量定义(所有模块共享)
// 注意:所有变量都附加到 window 对象,确保在 eval() 加载 .jsc 文件时也能访问
// ==========================================
const dpi = 300; // design2使用300 DPI
let addState = 0; // 0是不添加
let background_image, background_image1, background_image2;
let bg_version = 1;
var zoom = 0;
let pre_add_image; // 预添加的图片对象(用于addPic)
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)
// 内部剪贴板
let clipboardData = {
window.clipboardData = {
data: null,
offset: 10 // 粘贴位置偏移量
};
// 文件管理
var openAs = {
window.openAs = {
_name: "",
set name(val) {
if (val == "") {
@@ -281,46 +409,142 @@ ipcRenderer.send('get-scale-rate');
};
// 对象数组和历史记录
let objs1 = [];
let objs2 = [];
let step1 = { val: 0 }, step2 = { val: 0 };
let step = step1;
let objs = objs1;
let recordJson1 = [], recordJson2 = [], recordJson = recordJson1, recordObjs1 = [], recordObjs2 = [], recordObjs = recordObjs1;
let pre_objs1 = [], pre_objs2 = [], next_objs1 = [], next_objs2 = [], pre_objs = pre_objs1, next_objs = next_objs1;
let pre_json1 = [], pre_json2 = [], next_json1 = [], next_json2 = [], pre_json = pre_json1, next_json = next_json1;
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中初始化)
var canvas1, canvas2;
var canvas; // 当前活动的画布(canvas1 或 canvas2
var ctx1, ctx2;
let is_bgi_add = false;
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模块
// 由于模块代码需要在layui.use内部执行,我们通过动态加载的方式
const fs = require('fs');
// 加载core.js(核心功能)
let bytenode;
try {
const coreCode = fs.readFileSync(__dirname + '/design2/core.js', 'utf8');
eval(coreCode);
} catch (e) {
console.error('加载core.js失败:', e);
bytenode = require('bytenode');
require.extensions['.dl'] = require.extensions['.jsc'];
} catch(e) {
console.warn('[加载模块] bytenode 未安装,无法加载 .jsc 文件');
}
// 加载ui.jsUI控制)
const appPath = (() => { try { return remote.app.getAppPath(); } catch(e) { return __dirname || process.cwd(); } })();
// 读取模块映射配置
let moduleMap = {};
try {
const uiCode = fs.readFileSync(__dirname + '/design2/ui.js', 'utf8');
eval(uiCode);
const moduleMapPath = path.join(appPath, 'lib', 'module-map.json');
if (fs.existsSync(moduleMapPath)) {
moduleMap = JSON.parse(fs.readFileSync(moduleMapPath, 'utf8'));
}
} catch (e) {
console.error('加载ui.js失败:', e);
console.warn('[加载模块] 无法读取模块映射配置,使用默认文件名');
}
// 加载output.js(输出功能
try {
const outputCode = fs.readFileSync(__dirname + '/design2/output.js', 'utf8');
eval(outputCode);
// 获取映射后的文件名(跨平台兼容
function getMappedFileName(moduleName) {
if (moduleMap.mappings && moduleMap.mappings.design2 && moduleMap.mappings.design2[moduleName]) {
return moduleMap.mappings.design2[moduleName];
}
return moduleName; // 默认使用原文件名
}
const loadModule = (moduleName) => {
const mappedName = getMappedFileName(moduleName);
const jscPath = path.join(appPath, 'lib', 'design2', `${mappedName}.jsc`);
const jsPath = path.join(appPath, 'lib', 'design2', `${moduleName}.js`);
// 优先加载映射的 .jsc 文件
if (fs.existsSync(jscPath) && bytenode) {
try {
require(jscPath);
console.log(`[加载模块] ✅ ${moduleName}${mappedName}.jsc`);
return;
} catch (e) {
console.error(`[加载模块] ❌ ${moduleName}.jsc 加载失败:`, e.message);
}
}
// 回退到 .js 源文件
if (fs.existsSync(jsPath)) {
try {
eval(fs.readFileSync(jsPath, 'utf8'));
console.log(`[加载模块] ✅ ${moduleName}${moduleName}.js`);
} catch (e) {
console.error('加载output.js失败:', e);
console.error(`[加载模块] ❌ ${moduleName}.js 加载失败:`, e.message);
alert(`加载 ${moduleName} 失败!\n\n错误: ${e.message}`);
}
} else {
alert(`文件不存在!\n\n请检查:\n1. ${jscPath}\n2. ${jsPath}`);
}
};
loadModule('output'); // 先加载 output,因为 core 中的 addBackground() 需要调用 output 中的 open()
loadModule('core');
loadModule('ui');
});
+256 -957
View File
File diff suppressed because it is too large Load Diff
+153 -44
View File
@@ -1,4 +1,5 @@
function display_func(img1, img2, img3) {
// 将 display_func 附加到 window 对象,确保全局可访问
window.display_func = function display_func(img1, img2, img3) {
$('#base_control').hide()
$('#line_control').hide()
$('#pic_control').hide()
@@ -15,6 +16,27 @@ function display_func(img1, img2, img3) {
$('#component_type').text(language_str('bg')) //"背景"
canvas1.discardActiveObject().renderAll()
canvas2.discardActiveObject().renderAll()
// 保存所有对象的 selectable 和 evented 状态,确保预览后能恢复
const objStates1 = [];
const objStates2 = [];
canvas1.getObjects().forEach((obj, index) => {
if (!obj.isGuideLine) {
objStates1[index] = {
selectable: obj.selectable,
evented: obj.evented
};
}
});
canvas2.getObjects().forEach((obj, index) => {
if (!obj.isGuideLine) {
objStates2[index] = {
selectable: obj.selectable,
evented: obj.evented
};
}
});
// 过滤掉辅助线
let _objs1 = canvas1.getObjects().filter(obj => !obj.isGuideLine)
let g1 = []
@@ -114,10 +136,9 @@ function display_func(img1, img2, img3) {
})
// g3.push(img1)
// 预览图缩小尺寸以减小文件大小,使用 multiplier 来缩放整个 Group,保持所有内容完整
let url3 = new fabric.Group(g3).toDataURL({
format: 'png',
multiplier: 0.6, // 缩小到原来的 60%,保持宽高比和所有内容
height: 648,
width: 1012,
top: all_top / zoom,
left: all_left / zoom
})
@@ -225,10 +246,9 @@ function display_func(img1, img2, img3) {
width: 1012
})
// g4.push(img2)
// 预览图缩小尺寸以减小文件大小,使用 multiplier 来缩放整个 Group,保持所有内容完整
let url4 = new fabric.Group(g4).toDataURL({
format: 'png',
multiplier: 0.6, // 缩小到原来的 60%,保持宽高比和所有内容
height: 648,
width: 1012,
top: all_top / zoom,
left: all_left / zoom
})
@@ -323,23 +343,53 @@ function display_func(img1, img2, img3) {
</div>`,
btn: btns, //'导出'
btn1: function (index, layero) {
savePdf(Buffer.from(pdfBuffer))
// 只处理导出(保存PDF
if (typeof window.savePdf === 'function') {
window.savePdf(Buffer.from(pdfBuffer));
} else {
console.error('savePdf 函数未定义!');
}
},
btn2: function () {
// 根据 btns[1] 的值决定打印哪一面
if (btns[1] === '打印正面') {
// 打印正面
printJS({ printable: printPath3, type: 'image', style: 'img { width: 100%; height: auto; }' })
} else {
} else if (btns[1] === '打印背面') {
// 打印背面(只有背面时)
printJS({ printable: printPath4, type: 'image', style: 'img { width: 100%; height: auto; }' })
}
},
btn3: function () {
//打印背面
// 打印背面(两面都有时,btn3 对应 btns[2] = '打印背面'
printJS({ printable: printPath4, type: 'image', style: 'img { width: 100%; height: auto; }' })
},
end: function() {
// 预览窗口关闭后,恢复所有对象的 selectable 和 evented 状态
canvas1.getObjects().forEach((obj, index) => {
if (!obj.isGuideLine && objStates1[index]) {
obj.set({
selectable: objStates1[index].selectable,
evented: objStates1[index].evented
});
}
});
canvas2.getObjects().forEach((obj, index) => {
if (!obj.isGuideLine && objStates2[index]) {
obj.set({
selectable: objStates2[index].selectable,
evented: objStates2[index].evented
});
}
});
canvas1.renderAll();
canvas2.renderAll();
}
})
}
function output(callback = null, _save = save) {
// 将 output 附加到 window 对象,确保全局可访问
window.output = function output(callback = null, _save = save) {
// 类型改变
fabric.Image.fromURL('./public/images/op_2.png', function (i1) {
i1.left = background_image.left
@@ -464,10 +514,9 @@ function output(callback = null, _save = save) {
left: left / zoom
})
g3.push(img1)
// 预览图缩小尺寸以减小文件大小,使用 multiplier 来缩放整个 Group,保持所有内容完整
let url3 = new fabric.Group(g3).toDataURL({
format: 'png',
multiplier: 0.6, // 缩小到原来的 60%,保持宽高比和所有内容
height: 648,
width: 1012,
top: all_top / zoom,
left: all_left / zoom
})
@@ -571,10 +620,9 @@ function output(callback = null, _save = save) {
width: 1012
})
g4.push(img2)
// 预览图缩小尺寸以减小文件大小,使用 multiplier 来缩放整个 Group,保持所有内容完整
let url4 = new fabric.Group(g4).toDataURL({
format: 'png',
multiplier: 0.6, // 缩小到原来的 60%,保持宽高比和所有内容
height: 648,
width: 1012,
top: all_top / zoom,
left: all_left / zoom
})
@@ -902,7 +950,8 @@ function output(callback = null, _save = save) {
}
// 重新生成二维码和条形码(加载文件后调用)
function regenerateQrCodesAndBarcodes(canvas, objsArray) {
// 将 regenerateQrCodesAndBarcodes 附加到 window 对象,确保全局可访问
window.regenerateQrCodesAndBarcodes = function regenerateQrCodesAndBarcodes(canvas, objsArray) {
if (!canvas || !objsArray || !Array.isArray(objsArray)) return
const objects = canvas.getObjects()
@@ -924,7 +973,7 @@ function regenerateQrCodesAndBarcodes(canvas, objsArray) {
padding: 0,
foreground: objMeta.color || '#000000'
})
obj.setSrc(qr, function(img) {
obj.setSrc(qr, function (img) {
canvas.renderAll()
})
}
@@ -938,7 +987,7 @@ function regenerateQrCodesAndBarcodes(canvas, objsArray) {
})
const barcode = document.getElementById('barcode')
const bar = barcode.toDataURL("image/png")
obj.setSrc(bar, function(img) {
obj.setSrc(bar, function (img) {
canvas.renderAll()
})
}
@@ -946,7 +995,8 @@ function regenerateQrCodesAndBarcodes(canvas, objsArray) {
}
// 清理不需要的 src 字段(保留自定义图片的 src)
function cleanupSrcFields(jsonObjects, objsArray) {
// 将 cleanupSrcFields 附加到 window 对象,确保在 ui.js 中的 saveAs 函数也能访问
window.cleanupSrcFields = function cleanupSrcFields(jsonObjects, objsArray) {
if (!jsonObjects || !Array.isArray(jsonObjects)) return;
if (!objsArray || !Array.isArray(objsArray)) return;
@@ -972,6 +1022,10 @@ function cleanupSrcFields(jsonObjects, objsArray) {
}
}
}
};
// 向后兼容
function cleanupSrcFields(jsonObjects, objsArray) {
return window.cleanupSrcFields(jsonObjects, objsArray);
}
function saveAs(op1, callback) {
@@ -1009,18 +1063,27 @@ function saveAs(op1, callback) {
dialog
.showSaveDialog({
title: language_str('saveFile'), //'保存文件'
filters: [{ name: 'Soon File Type', extensions: ['soon'] }]
filters: [{ name: 'Soon File Type', extensions: ['soon'] }],
defaultPath: openAs.name || undefined
})
.then((result) => {
if (result.filePath == '') {
return
}
if (result.filePath.substring(result.filePath.length - 5).indexOf('.') == -1) {
result.filePath += '.soon'
if (result.filePath == null || result.filePath == undefined) {
return
}
// 检查文件扩展名,确保以.soon结尾
const path = require('path')
let filePath = result.filePath
const ext = path.extname(filePath).toLowerCase()
if (ext !== '.soon') {
filePath = filePath + '.soon'
}
result.filePath = filePath
let fs = require('fs')
con_o.soonType = 2
fs.writeFileSync(result.filePath, JSON.stringify(con_o))
fs.writeFileSync(result.filePath, JSON.stringify(con_o), 'utf8')
openAs.name = result.filePath
layer.msg(language_str('saveSucc') + openAs.name) //'保存成功至'
saveHistory()
@@ -1068,7 +1131,7 @@ function save(op1, callback) {
//打开的文件
let fs = require('fs')
con_o.soonType = 2
fs.writeFileSync(openAs.name, JSON.stringify(con_o))
fs.writeFileSync(openAs.name, JSON.stringify(con_o), 'utf8')
layer.msg(language_str('saveSucc') + openAs.name) //'保存成功至'
saveHistory()
if (callback && typeof callback === 'function') {
@@ -1079,18 +1142,27 @@ function save(op1, callback) {
dialog
.showSaveDialog({
title: language_str('saveFile'), //'保存文件'
filters: [{ name: 'Soon File Type', extensions: ['soon'] }]
filters: [{ name: 'Soon File Type', extensions: ['soon'] }],
defaultPath: openAs.name || undefined
})
.then((result) => {
if (result.filePath == '') {
return
}
if (result.filePath.substring(result.filePath.length - 5).indexOf('.') == -1) {
result.filePath += '.soon'
if (result.filePath == null || result.filePath == undefined) {
return
}
// 检查文件扩展名,确保以.soon结尾
const path = require('path')
let filePath = result.filePath
const ext = path.extname(filePath).toLowerCase()
if (ext !== '.soon') {
filePath = filePath + '.soon'
}
result.filePath = filePath
let fs = require('fs')
con_o.soonType = 2
fs.writeFileSync(result.filePath, JSON.stringify(con_o))
fs.writeFileSync(result.filePath, JSON.stringify(con_o), 'utf8')
openAs.name = result.filePath
layer.msg(language_str('saveSucc') + openAs.name) //'保存成功至'
saveHistory()
@@ -1102,7 +1174,8 @@ function save(op1, callback) {
})
}
function saveHistory() {
// 将 saveHistory 附加到 window 对象,确保在 ui.js 中也能访问
window.saveHistory = function saveHistory() {
let fs = require('fs')
let j = {}
try {
@@ -1111,19 +1184,24 @@ function saveHistory() {
for (let item of j.history) {
if (item.path == openAs.name) {
item.time = getDate()
fs.writeFileSync(fullPath, JSON.stringify(j))
fs.writeFileSync(fullPath, JSON.stringify(j), 'utf8')
return
}
}
j.history.push({ time: getDate(), path: openAs.name })
fs.writeFileSync(fullPath, JSON.stringify(j))
fs.writeFileSync(fullPath, JSON.stringify(j), 'utf8')
} catch (e) {
let j = { history: { time: getDate(), path: openAs.name } }
fs.writeFileSync(fullPath, JSON.stringify(j))
fs.writeFileSync(fullPath, JSON.stringify(j), 'utf8')
}
};
// 向后兼容
function saveHistory() {
return window.saveHistory();
}
function open(file) {
// 使用 window.openFile 避免与浏览器原生的 window.open 冲突
window.openFile = function open(file) {
let fs = require('fs')
var fsData = fs.readFileSync(file)
openAs.name = file
@@ -1169,12 +1247,16 @@ function open(file) {
})
// 加载 JSON,并在回调中重新生成二维码和条形码
canvas1.loadFromJSON(j.f, function() {
regenerateQrCodesAndBarcodes(canvas1, fo)
canvas1.loadFromJSON(j.f, function () {
if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.regenerateQrCodesAndBarcodes(canvas1, fo);
}
canvas1.renderAll()
})
canvas2.loadFromJSON(j.b, function() {
regenerateQrCodesAndBarcodes(canvas2, bo)
canvas2.loadFromJSON(j.b, function () {
if (typeof window.regenerateQrCodesAndBarcodes === 'function') {
window.regenerateQrCodesAndBarcodes(canvas2, bo);
}
canvas2.renderAll()
})
background_image1 = canvas1.getObjects()[0]
@@ -1203,10 +1285,22 @@ function open(file) {
}
canvas1.renderAll()
canvas2.renderAll()
updateList() /*
修复打开新文件后无法撤销的bug
2022-07-16
*/
// 调用 updateList,现在它已附加到 window 对象
if (typeof window.updateList === 'function') {
window.updateList(); /*
修复打开新文件后无法撤销的bug
2022-07-16
*/
} else {
// 延迟检查,确保 core.js 已加载
setTimeout(() => {
if (typeof window.updateList === 'function') {
window.updateList();
} else {
console.error('[open函数] ❌ updateList 函数未定义!');
}
}, 100);
}
setTimeout(() => {
recordObjs1.push(JSON.stringify(objs1))
let j1 = canvas1.toJSON(['selectable', 'hoverable', 'hoverCursor', 'text', 'fontStyle', 'fontWeight', 'underline'])
@@ -1257,7 +1351,22 @@ function clearAll() {
openAs.name = ''
canvas1.renderAll()
canvas2.renderAll()
updateList()
// 调用 updateList,现在它已附加到 window 对象
if (typeof window.updateList === 'function') {
window.updateList();
} else {
// 延迟检查,确保 core.js 已加载
setTimeout(() => {
if (typeof window.updateList === 'function') {
window.updateList();
} else {
console.error('[clearAll] ❌ updateList 函数未定义!');
}
}, 100);
}
console.log()
}
// 将 clearAll 附加到 window 对象,确保全局可访问
window.clearAll = clearAll;
+1036 -839
View File
File diff suppressed because one or more lines are too long
-21
View File
@@ -1,21 +0,0 @@
Example:
nvm install v0.10.32 Install a specific version number
nvm use 0.10 Use the latest available 0.10.x release
nvm run 0.10.32 app.js Run app.js using node v0.10.32
nvm exec 0.10.32 node app.js Run `node app.js` with the PATH pointing to node v0.10.32
nvm alias default 0.10.32 Set default node version on a shell
Note:
to remove, delete, or uninstall nvm - just remove the `$NVM_DIR` folder (usually `~/.nvm`)
Xupnei:~ xuwenwei$ nvm install v16.5.0
Version 'v16.5.0' not found - try `nvm ls-remote` to browse available versions.
Xupnei:~ xuwenwei$
Xupnei:~ xuwenwei$ nvm install v16.5.0
Downloading and installing node v16.5.0...
Downloading https://nodejs.org/dist/v16.5.0/node-v16.5.0-darwin-arm64.tar.gz...
######################################################################### 100.0%
Computing checksum with shasum -a 256
Checksums matched!
Now using node v16.5.0 (npm v7.19.1)
-21
View File
@@ -1,21 +0,0 @@
Example:
nvm install v0.10.32 Install a specific version number
nvm use 0.10 Use the latest available 0.10.x release
nvm run 0.10.32 app.js Run app.js using node v0.10.32
nvm exec 0.10.32 node app.js Run `node app.js` with the PATH pointing to node v0.10.32
nvm alias default 0.10.32 Set default node version on a shell
Note:
to remove, delete, or uninstall nvm - just remove the `$NVM_DIR` folder (usually `~/.nvm`)
Xupnei:~ xuwenwei$ nvm install v16.5.0
Version 'v16.5.0' not found - try `nvm ls-remote` to browse available versions.
Xupnei:~ xuwenwei$
Xupnei:~ xuwenwei$ nvm install v16.5.0
Downloading and installing node v16.5.0...
Downloading https://nodejs.org/dist/v16.5.0/node-v16.5.0-darwin-arm64.tar.gz...
######################################################################### 100.0%
Computing checksum with shasum -a 256
Checksums matched!
Now using node v16.5.0 (npm v7.19.1)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+28
View File
@@ -0,0 +1,28 @@
{
"description": "模块文件名映射配置 - 用于加密后重命名文件",
"version": "1.0.0",
"mappings": {
"design1": {
"core": "core1",
"output": "output1",
"ui": "ui1"
},
"design2": {
"core": "core2",
"output": "output2",
"ui": "ui2"
},
"main": {
"index": "index1",
"design1": "design11",
"design2": "design22"
}
},
"notes": [
"修改此文件中的映射值可以重命名加密后的文件",
"例如:将 'core' 改为 'core_v2',加密后会生成 core_v2.jsc",
"修改后需要重新运行加密脚本",
"确保映射值唯一,避免冲突"
]
}
+58 -6
View File
@@ -152,26 +152,78 @@ function createWindow() {
const fileName = 'User Manual.pdf';
if (app.isPackaged) {
helpPath = path.join(process.resourcesPath, 'help', fileName);
// Linux打包后的路径:resources/help/ 或 resources/app.asar/help/
// Windows/Mac打包后的路径:resources/help/
if (process.platform === 'linux') {
// Linux上,尝试多个可能的路径
const possiblePaths = [
path.join(process.resourcesPath, 'help', fileName),
path.join(process.resourcesPath, 'app.asar', 'help', fileName),
path.join(__dirname, 'help', fileName),
path.join(process.resourcesPath, '..', 'help', fileName)
];
for (const testPath of possiblePaths) {
if (fs.existsSync(testPath)) {
helpPath = testPath;
break;
}
}
} else {
helpPath = path.join(process.resourcesPath, 'help', fileName);
}
} else {
helpPath = path.join(process.cwd(), 'help', fileName);
}
console.log("Help file path: ", helpPath);
if (fs.existsSync(helpPath)) {
if (helpPath && fs.existsSync(helpPath)) {
try {
const errorMessage = await shell.openPath(helpPath);
if (errorMessage) {
console.error('Error opening help file:', errorMessage);
// Linux上使用shell.openExternal更可靠
if (process.platform === 'linux') {
await shell.openExternal(`file://${helpPath}`);
} else {
console.log('Help file opened successfully');
const errorMessage = await shell.openPath(helpPath);
if (errorMessage) {
console.error('Error opening help file:', errorMessage);
// 如果openPath失败,尝试openExternal
await shell.openExternal(`file://${helpPath}`);
}
}
console.log('Help file opened successfully');
} catch (err) {
console.error('Exception when opening file:', err);
// 尝试使用openExternal作为后备方案
try {
await shell.openExternal(`file://${helpPath}`);
} catch (err2) {
console.error('Failed to open with openExternal:', err2);
}
}
} else {
console.error('Help file not found at:', helpPath);
// 尝试查找help文件的其他可能位置
const searchPaths = [
path.join(__dirname, 'help', fileName),
path.join(process.cwd(), 'help', fileName),
path.join(app.getAppPath(), 'help', fileName)
];
for (const searchPath of searchPaths) {
if (fs.existsSync(searchPath)) {
console.log('Found help file at alternative path:', searchPath);
try {
if (process.platform === 'linux') {
await shell.openExternal(`file://${searchPath}`);
} else {
await shell.openPath(searchPath);
}
return;
} catch (err) {
console.error('Failed to open help file:', err);
}
}
}
}
});
+30 -4
View File
@@ -5,11 +5,20 @@
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder",
"encrypt:win": "scripts/encrypt/encrypt-electron-win.bat",
"encrypt:linux": "./scripts/encrypt/encrypt-electron.sh",
"cleanup:jsc": "node scripts/encrypt/cleanup-jsc.js",
"cleanup": "node scripts/encrypt/cleanup.js",
"restore": "node scripts/encrypt/restore.js",
"build:win": "electron-builder --win --x64",
"build:mac": "electron-builder --mac --x64",
"build:linux": "electron-builder --linux --x64",
"build:all": "electron-builder -mwl"
"build:linux:arm64": "electron-builder --linux --arm64",
"build:all": "electron-builder -mwl",
"release:win": "npm run encrypt:win && npm run cleanup && npm run build:win",
"release:mac": "npm run encrypt:linux && npm run cleanup && npm run build:mac",
"release:linux": "npm run encrypt:linux && npm run cleanup && npm run build:linux",
"release:linux:arm64": "npm run build:linux:arm64"
},
"build": {
"productName": "SoonDesign",
@@ -21,6 +30,23 @@
"extraFiles": [
"lib"
],
"files": [
"**/*",
"!.backup/**",
"!scripts/**",
"!**/*.jsc.map",
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
"!**/node_modules/*.d.ts",
"!**/node_modules/.bin",
"!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
"!.editorconfig",
"!**/._*",
"!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
"!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
"!**/{appveyor.yml,.travis.yml,circle.yml}",
"!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}"
],
"nsis": {
"oneClick": false,
"allowElevation": true,
@@ -65,8 +91,8 @@
},
"linux": {
"target": [
{ "target": "AppImage", "arch": ["x64"] },
{ "target": "deb", "arch": ["x64"] }
{ "target": "AppImage", "arch": ["x64", "arm64"] },
{ "target": "deb", "arch": ["x64", "arm64"] }
],
"category": "Utility",
"icon": "public/images/IconA256.png",
+38 -2
View File
@@ -77,6 +77,10 @@ function initAligningGuidelines(canvas) {
if (!transform) return;
// 清空辅助线数组,避免累积
verticalLines.length = 0;
horizontalLines.length = 0;
// It should be trivial to DRY this up by encapsulating (repeating) creation of x1, x2, y1, and y2 into functions,
// but we're not doing it here for perf. reasons -- as this a function that's invoked on every mouse move
@@ -84,6 +88,9 @@ function initAligningGuidelines(canvas) {
if (canvasObjects[i] === activeObject) continue;
// 跳过辅助线对象,避免辅助线之间相互对齐产生重影
if (canvasObjects[i].isGuideLine) continue;
var objectCenter = canvasObjects[i].getCenterPoint(),
objectLeft = objectCenter.x,
objectTop = objectCenter.y,
@@ -202,11 +209,40 @@ function initAligningGuidelines(canvas) {
});
canvas.on('after:render', function() {
// 合并相同 x 坐标的竖向辅助线(使用四舍五入处理浮点数精度问题)
var mergedVerticalLines = {};
for (var i = verticalLines.length; i--; ) {
drawVerticalLine(verticalLines[i]);
var line = verticalLines[i];
// 使用四舍五入到整数作为 key,避免浮点数精度问题
var key = Math.round(line.x);
if (!mergedVerticalLines[key]) {
mergedVerticalLines[key] = { x: line.x, y1: line.y1, y2: line.y2 };
} else {
// 合并 y 范围
mergedVerticalLines[key].y1 = Math.min(mergedVerticalLines[key].y1, line.y1);
mergedVerticalLines[key].y2 = Math.max(mergedVerticalLines[key].y2, line.y2);
}
}
for (var key in mergedVerticalLines) {
drawVerticalLine(mergedVerticalLines[key]);
}
// 合并相同 y 坐标的横向辅助线(使用四舍五入处理浮点数精度问题)
var mergedHorizontalLines = {};
for (var i = horizontalLines.length; i--; ) {
drawHorizontalLine(horizontalLines[i]);
var line = horizontalLines[i];
// 使用四舍五入到整数作为 key,避免浮点数精度问题
var key = Math.round(line.y);
if (!mergedHorizontalLines[key]) {
mergedHorizontalLines[key] = { y: line.y, x1: line.x1, x2: line.x2 };
} else {
// 合并 x 范围
mergedHorizontalLines[key].x1 = Math.min(mergedHorizontalLines[key].x1, line.x1);
mergedHorizontalLines[key].x2 = Math.max(mergedHorizontalLines[key].x2, line.x2);
}
}
for (var key in mergedHorizontalLines) {
drawHorizontalLine(mergedHorizontalLines[key]);
}
verticalLines.length = horizontalLines.length = 0;
});
+38 -2
View File
@@ -77,6 +77,10 @@ function initAligningGuidelines(canvas) {
if (!transform) return;
// 清空辅助线数组,避免累积
verticalLines.length = 0;
horizontalLines.length = 0;
// It should be trivial to DRY this up by encapsulating (repeating) creation of x1, x2, y1, and y2 into functions,
// but we're not doing it here for perf. reasons -- as this a function that's invoked on every mouse move
@@ -84,6 +88,9 @@ function initAligningGuidelines(canvas) {
if (canvasObjects[i] === activeObject) continue;
// 跳过辅助线对象,避免辅助线之间相互对齐产生重影
if (canvasObjects[i].isGuideLine) continue;
var objectCenter = canvasObjects[i].getCenterPoint(),
objectLeft = objectCenter.x,
objectTop = objectCenter.y,
@@ -202,11 +209,40 @@ function initAligningGuidelines(canvas) {
});
canvas.on('after:render', function() {
// 合并相同 x 坐标的竖向辅助线(使用四舍五入处理浮点数精度问题)
var mergedVerticalLines = {};
for (var i = verticalLines.length; i--; ) {
drawVerticalLine(verticalLines[i]);
var line = verticalLines[i];
// 使用四舍五入到整数作为 key,避免浮点数精度问题
var key = Math.round(line.x);
if (!mergedVerticalLines[key]) {
mergedVerticalLines[key] = { x: line.x, y1: line.y1, y2: line.y2 };
} else {
// 合并 y 范围
mergedVerticalLines[key].y1 = Math.min(mergedVerticalLines[key].y1, line.y1);
mergedVerticalLines[key].y2 = Math.max(mergedVerticalLines[key].y2, line.y2);
}
}
for (var key in mergedVerticalLines) {
drawVerticalLine(mergedVerticalLines[key]);
}
// 合并相同 y 坐标的横向辅助线(使用四舍五入处理浮点数精度问题)
var mergedHorizontalLines = {};
for (var i = horizontalLines.length; i--; ) {
drawHorizontalLine(horizontalLines[i]);
var line = horizontalLines[i];
// 使用四舍五入到整数作为 key,避免浮点数精度问题
var key = Math.round(line.y);
if (!mergedHorizontalLines[key]) {
mergedHorizontalLines[key] = { y: line.y, x1: line.x1, x2: line.x2 };
} else {
// 合并 x 范围
mergedHorizontalLines[key].x1 = Math.min(mergedHorizontalLines[key].x1, line.x1);
mergedHorizontalLines[key].x2 = Math.max(mergedHorizontalLines[key].x2, line.x2);
}
}
for (var key in mergedHorizontalLines) {
drawHorizontalLine(mergedHorizontalLines[key]);
}
verticalLines.length = horizontalLines.length = 0;
});
+116
View File
@@ -0,0 +1,116 @@
# 加密脚本使用说明
## 📁 文件说明
- `encrypt-electron.js` - 主加密脚本
- `encrypt-electron-win.bat` - Windows 批处理脚本
- `encrypt-electron.sh` - Linux/Mac Shell 脚本
- `cleanup-jsc.js` - 清理 .jsc 文件脚本
- `cleanup.js` - 清理源文件脚本(备份到 .backup)
- `restore.js` - 恢复源文件脚本(从 .backup)
- `module-map.json` - **模块文件名映射配置**(位于 `lib` 目录)
## 🔧 模块文件名映射配置
### 功能说明
`module-map.json` 允许你自定义加密后生成的文件名。这对于:
- 版本管理:不同版本使用不同的文件名
- 安全加固:使用不易猜测的文件名
- 多版本共存:同时保留多个版本的加密文件
### 配置文件结构
```json
{
"mappings": {
"design1": {
"core": "core", // 原文件名 → 加密后文件名(不含扩展名)
"output": "output",
"ui": "ui"
},
"design2": {
"core": "core",
"output": "output",
"ui": "ui"
},
"main": {
"index": "index",
"design1": "design1",
"design2": "design2"
}
}
}
```
### 使用示例
#### 示例 1:添加版本号
```json
{
"mappings": {
"design1": {
"core": "core_v2",
"output": "output_v2",
"ui": "ui_v2"
}
}
}
```
加密后会生成:`core_v2.jsc`, `output_v2.jsc`, `ui_v2.jsc`
#### 示例 2:使用随机名称
```json
{
"mappings": {
"design1": {
"core": "a1b2c3",
"output": "x9y8z7",
"ui": "m5n6o4"
}
}
}
```
加密后会生成:`a1b2c3.jsc`, `x9y8z7.jsc`, `m5n6o4.jsc`
### 使用步骤
1. **修改配置文件**
```bash
# 编辑 lib/module-map.json
# 修改 mappings 中的值
```
2. **运行加密脚本**
```bash
npm run encrypt:win # Windows
npm run encrypt:linux # Linux/Mac
```
3. **验证结果**
- 检查 `lib/design1/``lib/design2/` 目录
- 确认生成了映射后的 `.jsc` 文件
### ⚠️ 注意事项
1. **唯一性**:确保映射值唯一,避免文件名冲突
2. **重新加密**:修改配置后必须重新运行加密脚本
3. **备份**:修改前建议备份配置文件
4. **兼容性**:映射后的文件名会在运行时自动识别,无需修改代码
5. **清理**:清理脚本会自动识别映射后的文件名
### 🔄 恢复默认
如果不想使用映射,将所有值改回原文件名即可:
```json
{
"mappings": {
"design1": {
"core": "core",
"output": "output",
"ui": "ui"
}
}
}
```
+95
View File
@@ -0,0 +1,95 @@
const fs = require('fs');
const path = require('path');
// 获取项目根目录(脚本在 scripts/encrypt 目录下,需要向上两级)
const projectRoot = path.resolve(__dirname, '../..');
// 读取模块映射配置(从 lib 目录读取)
const moduleMapPath = path.join(projectRoot, 'lib', 'module-map.json');
let moduleMap = {};
if (fs.existsSync(moduleMapPath)) {
try {
moduleMap = JSON.parse(fs.readFileSync(moduleMapPath, 'utf8'));
} catch (error) {
console.warn(`⚠️ 读取模块映射配置失败: ${error.message},将清理所有 .jsc 文件\n`);
}
}
// 文件映射函数:根据配置返回加密后的文件名(跨平台兼容)
function getEncryptedFileName(originalPath) {
// 使用 path 模块处理路径,确保跨平台兼容
const normalizedPath = originalPath.replace(/\\/g, '/'); // 统一使用正斜杠
const parts = normalizedPath.split('/');
const fileName = parts[parts.length - 1].replace('.js', '');
const dirName = parts[parts.length - 2];
// 检查是否有映射配置
if (moduleMap.mappings) {
if (dirName === 'design1' && moduleMap.mappings.design1 && moduleMap.mappings.design1[fileName]) {
return moduleMap.mappings.design1[fileName] + '.jsc';
}
if (dirName === 'design2' && moduleMap.mappings.design2 && moduleMap.mappings.design2[fileName]) {
return moduleMap.mappings.design2[fileName] + '.jsc';
}
if (dirName === 'lib' && moduleMap.mappings.main && moduleMap.mappings.main[fileName]) {
return moduleMap.mappings.main[fileName] + '.jsc';
}
}
// 默认:原文件名 + 'c'
return fileName + '.jsc';
}
const filesToClean = [
'lib/index.js', 'lib/design1.js', 'lib/design2.js',
'lib/design1/core.js', 'lib/design1/output.js', 'lib/design1/ui.js',
'lib/design2/core.js', 'lib/design2/output.js', 'lib/design2/ui.js'
];
console.log('正在删除所有 .jsc 文件...\n');
let deletedCount = 0;
let notFoundCount = 0;
filesToClean.forEach(filePath => {
// 跨平台兼容的路径处理
const fullPath = path.join(projectRoot, filePath);
const dir = path.dirname(fullPath);
const encryptedFileName = getEncryptedFileName(filePath);
// 尝试删除映射后的文件名
const mappedJscPath = path.join(dir, encryptedFileName);
const defaultJscPath = fullPath + 'c';
// 使用 path.resolve 进行规范化比较,确保跨平台兼容
const normalizedDefault = path.resolve(defaultJscPath);
const normalizedMapped = path.resolve(mappedJscPath);
if (fs.existsSync(mappedJscPath)) {
try {
fs.unlinkSync(mappedJscPath);
console.log(`✅ 已删除: ${mappedJscPath}`);
deletedCount++;
} catch (error) {
console.error(`❌ 删除失败: ${mappedJscPath} - ${error.message}`);
}
}
// 也尝试删除默认名称的 .jsc 文件(兼容旧版本)
if (fs.existsSync(defaultJscPath) && normalizedMapped !== normalizedDefault) {
try {
fs.unlinkSync(defaultJscPath);
console.log(`✅ 已删除: ${defaultJscPath}`);
deletedCount++;
} catch (error) {
console.error(`❌ 删除失败: ${defaultJscPath} - ${error.message}`);
}
}
if (!fs.existsSync(mappedJscPath) && !fs.existsSync(defaultJscPath)) {
notFoundCount++;
}
});
console.log(`\n完成!已删除 ${deletedCount} 个文件,${notFoundCount} 个文件不存在`);
+162
View File
@@ -0,0 +1,162 @@
/**
* 清理脚本 - 删除源 JS 文件保留 .jsc 文件
*
* 使用方法
* node cleanup.js
*
* 注意此脚本会自动备份源文件到 .backup 目录
*/
const fs = require('fs');
const path = require('path');
// 获取项目根目录(脚本在 scripts/encrypt 目录下,需要向上两级)
const projectRoot = path.resolve(__dirname, '../..');
// 读取模块映射配置(从 lib 目录读取,跨平台兼容)
const moduleMapPath = path.join(projectRoot, 'lib', 'module-map.json');
let moduleMap = {};
if (fs.existsSync(moduleMapPath)) {
try {
moduleMap = JSON.parse(fs.readFileSync(moduleMapPath, 'utf8'));
} catch (error) {
console.warn(`⚠️ 读取模块映射配置失败: ${error.message},将使用默认文件名\n`);
}
}
// 文件映射函数:根据配置返回加密后的文件名(跨平台兼容)
function getEncryptedFileName(originalPath) {
// 使用 path 模块处理路径,确保跨平台兼容
const normalizedPath = originalPath.replace(/\\/g, '/'); // 统一使用正斜杠
const parts = normalizedPath.split('/');
const fileName = parts[parts.length - 1].replace('.js', '');
const dirName = parts[parts.length - 2];
// 检查是否有映射配置
if (moduleMap.mappings) {
if (dirName === 'design1' && moduleMap.mappings.design1 && moduleMap.mappings.design1[fileName]) {
return moduleMap.mappings.design1[fileName] + '.jsc';
}
if (dirName === 'design2' && moduleMap.mappings.design2 && moduleMap.mappings.design2[fileName]) {
return moduleMap.mappings.design2[fileName] + '.jsc';
}
if (dirName === 'lib' && moduleMap.mappings.main && moduleMap.mappings.main[fileName]) {
return moduleMap.mappings.main[fileName] + '.jsc';
}
}
// 默认:原文件名 + 'c'
return fileName + '.jsc';
}
// 需要删除的源文件列表(对应的 .jsc 文件会被保留)
const filesToDelete = [
'lib/index.js',
'lib/design1.js',
'lib/design2.js',
'lib/design1/core.js',
'lib/design1/output.js',
'lib/design1/ui.js',
'lib/design2/core.js',
'lib/design2/output.js',
'lib/design2/ui.js',
];
// 备份目录
const backupDir = path.join(projectRoot, '.backup');
console.log('开始清理源文件...\n');
console.log('⚠️ 警告:此操作将删除源 JS 文件,只保留 .jsc 文件!\n');
// 创建备份目录
if (!fs.existsSync(backupDir)) {
fs.mkdirSync(backupDir, { recursive: true });
console.log(`✅ 已创建备份目录: ${backupDir}\n`);
}
let deletedCount = 0;
let notFoundCount = 0;
let errorCount = 0;
let backedUpCount = 0;
filesToDelete.forEach(filePath => {
try {
const fullPath = path.join(projectRoot, filePath);
const dir = path.dirname(fullPath);
// 检查默认名称的 .jsc 文件
const defaultJscPath = fullPath + 'c';
// 检查映射后的 .jsc 文件
const encryptedFileName = getEncryptedFileName(filePath);
const mappedJscPath = path.join(dir, encryptedFileName);
// 检查是否存在 .jsc 文件(默认或映射后的)
const jscExists = fs.existsSync(defaultJscPath) || fs.existsSync(mappedJscPath);
if (!jscExists) {
console.warn(`⚠️ 跳过: ${filePath} (对应的 .jsc 文件不存在)`);
notFoundCount++;
return;
}
// 备份源文件
if (fs.existsSync(fullPath)) {
const backupPath = path.join(backupDir, filePath);
const backupDirPath = path.dirname(backupPath);
// 确保备份目录存在
if (!fs.existsSync(backupDirPath)) {
fs.mkdirSync(backupDirPath, { recursive: true });
}
// 复制文件到备份目录
fs.copyFileSync(fullPath, backupPath);
console.log(`📦 已备份: ${filePath} -> .backup/${filePath}`);
backedUpCount++;
// 删除源文件
fs.unlinkSync(fullPath);
console.log(`✅ 已删除: ${filePath}`);
deletedCount++;
} else {
console.log(`️ 不存在: ${filePath} (可能已删除)`);
notFoundCount++;
}
} catch (error) {
console.error(`❌ 处理失败: ${filePath}`);
console.error(` 错误: ${error.message}`);
errorCount++;
}
});
// 创建备份信息文件
const backupInfo = {
timestamp: new Date().toISOString(),
files: filesToDelete.filter((filePath, index) => {
const fullPath = path.join(projectRoot, filePath);
return fs.existsSync(path.join(backupDir, filePath));
})
};
const backupInfoPath = path.join(backupDir, 'backup-info.json');
fs.writeFileSync(backupInfoPath, JSON.stringify(backupInfo, null, 2));
console.log(`\n📝 已创建备份信息文件: .backup/backup-info.json`);
console.log('\n' + '='.repeat(50));
console.log(`清理完成!`);
console.log(` 已备份: ${backedUpCount}`);
console.log(` 已删除: ${deletedCount}`);
console.log(` 未找到: ${notFoundCount}`);
console.log(` 错误: ${errorCount}`);
console.log(` 备份位置: ${backupDir}`);
console.log('='.repeat(50));
if (errorCount > 0) {
console.log('\n⚠️ 有文件处理失败,请检查错误信息');
process.exit(1);
} else {
console.log('\n✅ 清理完成!源文件已备份到 .backup 目录');
console.log('\n💡 恢复方法:');
console.log(' 运行: node scripts/encrypt/restore.js');
console.log(' 或手动从 .backup 目录复制文件回原位置');
}
+8
View File
@@ -0,0 +1,8 @@
@echo off
chcp 65001 >nul
set ELECTRON_RUN_AS_NODE=true
set NODE_OPTIONS=--max-old-space-size=4096
cd /d "%~dp0\..\.."
electron scripts\encrypt\encrypt-electron.js
pause
+154
View File
@@ -0,0 +1,154 @@
// 设置输出编码为 UTF-8(修复 Windows 中文乱码问题)
if (process.platform === 'win32') {
process.stdout.setDefaultEncoding('utf8');
process.stderr.setDefaultEncoding('utf8');
// 设置环境变量确保中文正确显示
if (!process.env.CHCP) {
process.env.CHCP = '65001';
}
}
// 确保在 Electron 环境中运行
if (!process.versions.electron) {
console.error('❌ 错误:此脚本必须在 Electron 环境中运行!');
console.error('请使用以下方式运行:');
console.error(' Windows: encrypt-electron-win.bat');
console.error(' Linux/Mac: ./encrypt-electron.sh');
console.error(' 或: npm run encrypt:win / npm run encrypt:linux');
process.exit(1);
}
const bytenode = require('bytenode');
const fs = require('fs');
const path = require('path');
// 获取项目根目录(脚本在 scripts/encrypt 目录下,需要向上两级)
const projectRoot = path.resolve(__dirname, '../..');
// 读取模块映射配置(从 lib 目录读取)
const moduleMapPath = path.join(projectRoot, 'lib', 'module-map.json');
let moduleMap = {};
if (fs.existsSync(moduleMapPath)) {
try {
moduleMap = JSON.parse(fs.readFileSync(moduleMapPath, 'utf8'));
console.log('✅ 已加载模块映射配置\n');
} catch (error) {
console.warn(`⚠️ 读取模块映射配置失败: ${error.message},使用默认文件名\n`);
}
}
// 文件映射函数:根据配置返回加密后的文件名(跨平台兼容)
function getEncryptedFileName(originalPath) {
// 使用 path 模块处理路径,确保跨平台兼容
const normalizedPath = originalPath.replace(/\\/g, '/'); // 统一使用正斜杠
const parts = normalizedPath.split('/');
const fileName = parts[parts.length - 1].replace('.js', '');
const dirName = parts[parts.length - 2];
// 检查是否有映射配置
if (moduleMap.mappings) {
if (dirName === 'design1' && moduleMap.mappings.design1 && moduleMap.mappings.design1[fileName]) {
return moduleMap.mappings.design1[fileName] + '.jsc';
}
if (dirName === 'design2' && moduleMap.mappings.design2 && moduleMap.mappings.design2[fileName]) {
return moduleMap.mappings.design2[fileName] + '.jsc';
}
if (dirName === 'lib' && moduleMap.mappings.main && moduleMap.mappings.main[fileName]) {
return moduleMap.mappings.main[fileName] + '.jsc';
}
}
// 默认:原文件名 + 'c'
return fileName + '.jsc';
}
const filesToEncrypt = [
'lib/index.js', 'lib/design1.js', 'lib/design2.js',
'lib/design1/core.js', 'lib/design1/output.js', 'lib/design1/ui.js',
'lib/design2/core.js', 'lib/design2/output.js', 'lib/design2/ui.js'
];
// 先删除所有旧的 .jsc 文件(包括映射后的文件名)
console.log('正在删除旧的 .jsc 文件...');
let deletedCount = 0;
filesToEncrypt.forEach(filePath => {
// 删除默认名称的 .jsc 文件(跨平台兼容)
const fullPath = path.join(projectRoot, filePath);
const defaultJscPath = fullPath + 'c';
if (fs.existsSync(defaultJscPath)) {
try {
fs.unlinkSync(defaultJscPath);
deletedCount++;
} catch (error) {
console.error(`⚠️ 删除失败: ${defaultJscPath} - ${error.message}`);
}
}
// 删除映射后的 .jsc 文件(跨平台兼容)
const dir = path.dirname(fullPath);
const encryptedFileName = getEncryptedFileName(filePath);
const mappedJscPath = path.join(dir, encryptedFileName);
// 使用 path.resolve 进行规范化比较,确保跨平台兼容
const normalizedDefault = path.resolve(defaultJscPath);
const normalizedMapped = path.resolve(mappedJscPath);
if (fs.existsSync(mappedJscPath) && normalizedMapped !== normalizedDefault) {
try {
fs.unlinkSync(mappedJscPath);
deletedCount++;
} catch (error) {
console.error(`⚠️ 删除失败: ${mappedJscPath} - ${error.message}`);
}
}
});
console.log(`已删除 ${deletedCount} 个旧的 .jsc 文件\n`);
// 开始编译
let successCount = 0, failCount = 0;
filesToEncrypt.forEach(filePath => {
try {
const fullPath = path.join(projectRoot, filePath);
if (!fs.existsSync(fullPath)) {
console.error(`❌ 文件不存在: ${filePath}`);
failCount++;
return;
}
// 编译文件(bytenode 会生成默认的 .jsc 文件)
bytenode.compileFile({ filename: fullPath });
// 检查默认的 .jsc 文件是否生成
const defaultJscPath = fullPath + 'c';
if (!fs.existsSync(defaultJscPath)) {
console.error(`❌ 编译失败: ${filePath}`);
failCount++;
return;
}
// 如果配置了映射,重命名文件(跨平台兼容)
const dir = path.dirname(fullPath);
const encryptedFileName = getEncryptedFileName(filePath);
const mappedJscPath = path.join(dir, encryptedFileName);
// 使用 path.resolve 进行规范化比较,确保跨平台兼容
const normalizedDefault = path.resolve(defaultJscPath);
const normalizedMapped = path.resolve(mappedJscPath);
if (normalizedMapped !== normalizedDefault) {
// 重命名文件
fs.renameSync(defaultJscPath, mappedJscPath);
console.log(`${filePath}${encryptedFileName}`);
} else {
console.log(`${filePath}${path.basename(defaultJscPath)}`);
}
successCount++;
} catch (error) {
console.error(`❌ 编译失败: ${filePath} - ${error.message}`);
failCount++;
}
});
console.log(`\n编译完成!成功: ${successCount}, 失败: ${failCount}`);
process.exit(failCount > 0 ? 1 : 0);
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# 设置 UTF-8 编码环境变量,确保中文正确显示
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
export LC_CTYPE=en_US.UTF-8
export ELECTRON_RUN_AS_NODE=true
export NODE_OPTIONS=--max-old-space-size=4096
# 切换到项目根目录
cd "$(dirname "$0")/../.."
# 运行加密脚本
electron scripts/encrypt/encrypt-electron.js
+126
View File
@@ -0,0 +1,126 @@
/**
* 恢复脚本 - 从备份目录恢复源 JS 文件
*
* 使用方法
* node restore.js
*
* 此脚本会从 .backup 目录恢复所有备份的源文件
*/
const fs = require('fs');
const path = require('path');
// 获取项目根目录(脚本在 scripts/encrypt 目录下,需要向上两级)
const projectRoot = path.resolve(__dirname, '../..');
// 备份目录
const backupDir = path.join(projectRoot, '.backup');
const backupInfoPath = path.join(backupDir, 'backup-info.json');
console.log('开始恢复源文件...\n');
// 检查备份目录是否存在
if (!fs.existsSync(backupDir)) {
console.error('❌ 错误:备份目录不存在!');
console.error(` 路径: ${backupDir}`);
console.error('\n💡 提示:如果没有备份,请从 Git 仓库恢复文件');
process.exit(1);
}
// 检查备份信息文件
if (!fs.existsSync(backupInfoPath)) {
console.warn('⚠️ 警告:备份信息文件不存在,将尝试恢复所有 .js 文件\n');
}
let restoredCount = 0;
let notFoundCount = 0;
let errorCount = 0;
// 读取备份信息
let filesToRestore = [];
if (fs.existsSync(backupInfoPath)) {
try {
const backupInfo = JSON.parse(fs.readFileSync(backupInfoPath, 'utf8'));
filesToRestore = backupInfo.files || [];
console.log(`📋 从备份信息读取到 ${filesToRestore.length} 个文件\n`);
} catch (error) {
console.warn(`⚠️ 无法读取备份信息文件: ${error.message}\n`);
}
}
// 如果没有备份信息,扫描备份目录
if (filesToRestore.length === 0) {
console.log('📂 扫描备份目录...\n');
function scanDir(dir, baseDir = '') {
const files = [];
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
const relativePath = path.join(baseDir, entry.name);
if (entry.isDirectory()) {
files.push(...scanDir(fullPath, relativePath));
} else if (entry.isFile() && entry.name.endsWith('.js')) {
files.push(relativePath);
}
}
return files;
}
filesToRestore = scanDir(backupDir);
console.log(`📋 扫描到 ${filesToRestore.length} 个备份文件\n`);
}
if (filesToRestore.length === 0) {
console.error('❌ 错误:没有找到任何备份文件!');
process.exit(1);
}
// 恢复文件
filesToRestore.forEach(filePath => {
try {
const backupPath = path.join(backupDir, filePath);
const restorePath = path.join(projectRoot, filePath);
// 检查备份文件是否存在
if (!fs.existsSync(backupPath)) {
console.warn(`⚠️ 备份文件不存在: ${filePath}`);
notFoundCount++;
return;
}
// 确保目标目录存在
const restoreDir = path.dirname(restorePath);
if (!fs.existsSync(restoreDir)) {
fs.mkdirSync(restoreDir, { recursive: true });
}
// 复制文件
fs.copyFileSync(backupPath, restorePath);
console.log(`✅ 已恢复: ${filePath}`);
restoredCount++;
} catch (error) {
console.error(`❌ 恢复失败: ${filePath}`);
console.error(` 错误: ${error.message}`);
errorCount++;
}
});
console.log('\n' + '='.repeat(50));
console.log(`恢复完成!`);
console.log(` 已恢复: ${restoredCount}`);
console.log(` 未找到: ${notFoundCount}`);
console.log(` 错误: ${errorCount}`);
console.log('='.repeat(50));
if (errorCount > 0) {
console.log('\n⚠️ 有文件恢复失败,请检查错误信息');
process.exit(1);
} else {
console.log('\n✅ 恢复完成!源文件已从备份目录恢复');
console.log('\n💡 提示:');
console.log(' - 备份文件仍保留在 .backup 目录');
console.log(' - 如需删除备份,请手动删除 .backup 目录');
}