更新优化
This commit is contained in:
@@ -7,3 +7,7 @@ npm-debug.log
|
||||
npm-debug.log.*
|
||||
thumbs.db
|
||||
!.gitkeep
|
||||
|
||||
# 加密相关文件(不要提交到 Git)
|
||||
*.jsc
|
||||
.backup/
|
||||
@@ -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
File diff suppressed because one or more lines are too long
+40
-9
File diff suppressed because one or more lines are too long
+40
-7
@@ -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>
|
||||
Binary file not shown.
+403
-167
@@ -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.js(UI控制)
|
||||
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
File diff suppressed because it is too large
Load Diff
+288
-169
File diff suppressed because one or more lines are too long
+530
-284
File diff suppressed because one or more lines are too long
+278
-54
@@ -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.js(UI控制)
|
||||
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
File diff suppressed because it is too large
Load Diff
+153
-44
@@ -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
File diff suppressed because one or more lines are too long
@@ -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)
|
||||
@@ -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)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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",
|
||||
"修改后需要重新运行加密脚本",
|
||||
"确保映射值唯一,避免冲突"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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} 个文件不存在`);
|
||||
|
||||
@@ -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 目录复制文件回原位置');
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 目录');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user