init
22
.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
node_modules
|
||||
out/
|
||||
logs/
|
||||
runtime/
|
||||
.idea/
|
||||
package-lock.json
|
||||
data/
|
||||
.vscode/launch.json
|
||||
public/electron/
|
||||
public/dist/
|
||||
pnpm-lock.yaml
|
||||
.yalc/
|
||||
yalc.lock
|
||||
go/tmp/
|
||||
build/extraResources/java-app.jar
|
||||
build/extraResources/jre1.8.0_201/
|
||||
python/.venv/
|
||||
python/*.spec
|
||||
python/build/
|
||||
python/dist/
|
||||
*DS_Store
|
||||
yalc.lock
|
||||
4
.npmrc
Normal file
@@ -0,0 +1,4 @@
|
||||
registry=https://registry.npmmirror.com/
|
||||
disturl=https://registry.npmmirror.com/-/binary/node
|
||||
electron_mirror=https://npmmirror.com/mirrors/electron/
|
||||
electron-builder-binaries_mirror=https://registry.npmmirror.com/-/binary/electron-builder-binaries/
|
||||
221
README.md
Normal file
@@ -0,0 +1,221 @@
|
||||
[](https://gitee.com/dromara/electron-egg/stargazers)
|
||||
|
||||
<div align=center>
|
||||
<h3>🎉🎉🎉 ElectronEgg V4 已发布! 🎉🎉🎉</h3>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div align=center>
|
||||
<img src="./public/images/example/logo.png" width="150" height="150" />
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
<h3><strong>一个入门简单、跨平台、企业级桌面软件开发框架</strong></h3>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!-- ## 🌏 [English](https://www.yuque.com/u34495/ee-doc) | [中文](https://www.kaka996.com/) -->
|
||||
|
||||
## 📋 介绍
|
||||
|
||||
> 框架已经广泛应用于记账、政务、企业、医疗、学校、股票交易、ERP、娱乐、视频等领域客户端,请放心使用!
|
||||
|
||||
## 👦 谁可以使用
|
||||
|
||||
项目已经有 5 个交流群,覆盖`前端`、`java`、`go`、`python`、`php` 等开发者。
|
||||
|
||||
无论你是前端、服务端、运维、游戏、客户端等,都可以很快入门,
|
||||
|
||||
## 🐶 精彩案例
|
||||
|
||||
- [**点击查看**](#项目案例)
|
||||
|
||||
## 📺 特点
|
||||
- 🍩 **为什么使用?** 桌面软件(办公方向、 个人工具),仍然是未来十几年PC端需求之一,提高工作效率
|
||||
- 🍉 **简单:** 只需懂 JavaScript
|
||||
- 🍑 **愿景:** 所有开发者都能学会桌面软件研发
|
||||
- 🍰 **gitee:** https://gitee.com/dromara/electron-egg **5100+**
|
||||
- 🍨 **github:** https://github.com/dromara/electron-egg **1800+**
|
||||
- 🏆 码云最有价值开源项目
|
||||

|
||||
|
||||
## 📚 文档
|
||||
- 快速体验:[教程文档](https://www.kaka996.com/)
|
||||

|
||||
|
||||
## 📦 特性
|
||||
1. 🍄 跨平台:一套代码,可以打包成windows版、Mac版、Linux版、国产UOS、Deepin、麒麟等
|
||||
2. 🌹 架构:单业务进程/模块化/多任务(进程,线程,渲染进程),让开发大型项目变的简单。
|
||||
3. 🌱 简单高效:只需学习 js 语言
|
||||
4. 🌴 前端独立:理论上支持任何前端技术,如:vue、react、html等等
|
||||
5. 🍁 工程化:可以用前端、服务端的开发思维,来编写桌面软件
|
||||
6. 🌷 高性能:事件驱动、非阻塞式IO
|
||||
7. 🌰 功能丰富:配置、通信、插件、数据库、升级、打包、工具... 应有尽有
|
||||
8. 💐 安全:支持字节码加密、压缩混淆加密
|
||||
9. 🌻 功能demo:桌面软件常见功能,框架集成或提供demo
|
||||
|
||||
## ✈️ 使用场景
|
||||
|
||||
### 1. 🚀 常规桌面软件
|
||||
- 🚖 windows平台
|
||||
|
||||

|
||||
|
||||
- 🚍 macOS平台
|
||||

|
||||
|
||||
- 🚔 linux平台 - 国产UOS、Deepin
|
||||

|
||||
|
||||
- 🚔 linux平台 - ubuntu
|
||||

|
||||
|
||||
### 🚐 2. vue、react、angular、web 转换成桌面软件
|
||||
- 🚙 vue-ant-design(本地)
|
||||
|
||||

|
||||
|
||||
- 🚙 禅道项目管理(web项目地址)
|
||||
|
||||

|
||||
|
||||
### 🚂 3. 游戏(h5相关技术开发)
|
||||
- 🚊 忍者100层
|
||||
|
||||

|
||||
|
||||
|
||||
## 📒 开始使用
|
||||
|
||||
- ✒️ [安装文档](https://www.kaka996.com/pages/e64ff6/)
|
||||
|
||||
## 项目案例
|
||||
- 🐟 框架已经应用于医疗、学校、政务、股票交易、ERP、娱乐、视频、企业等领域客户端
|
||||
|
||||
### 🐸 远控
|
||||
|
||||
- RQ Center
|
||||

|
||||

|
||||
|
||||
### 🐸 云盘
|
||||
|
||||
- FM Cloud
|
||||

|
||||

|
||||

|
||||
|
||||
### 🐸 IM
|
||||
|
||||
- Cede IM
|
||||

|
||||

|
||||

|
||||
|
||||
### 🐸 壁纸
|
||||
|
||||
- warpar
|
||||

|
||||
|
||||
### 🐸 英雄联盟助手
|
||||
|
||||
- Serendlplty
|
||||

|
||||
|
||||
### 🐸 更多
|
||||
|
||||
- [更多案例](https://www.kaka996.com/pages/eadf46/)
|
||||
|
||||
## 💬 交流
|
||||
1. [讨论](https://www.kaka996.com/pages/c2720e/)
|
||||
|
||||
## 📌 关于pr
|
||||
请前往[GitHub项目](https://github.com/dromara/electron-egg)提pr(避免代码同步后,pr被覆盖掉),感谢!
|
||||
|
||||
地址:https://github.com/dromara/electron-egg
|
||||
|
||||
## 📔 框架核心包 ee-core
|
||||
ee-core:[https://github.com/wallace5303/ee-core](https://github.com/wallace5303/ee-core)
|
||||
|
||||
## 📚 Dromara 成员项目
|
||||
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/dromara/TLog" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/tlog2.png" title="一个轻量级的分布式日志标记追踪神器,10分钟即可接入,自动对日志打标签完成微服务的链路追踪" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/liteFlow" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/liteflow.png" title="轻量,快速,稳定,可编排的组件式流程引擎" width="15%">
|
||||
</a>
|
||||
<a href="https://hutool.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/hutool.jpg" title="小而全的Java工具类库,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。" width="15%">
|
||||
</a>
|
||||
<a href="https://sa-token.dev33.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/sa-token.png" title="一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/hmily" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/hmily.png" title="高性能一站式分布式事务解决方案。" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/Raincat" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/raincat.png" title="强一致性分布式事务解决方案。" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/dromara/myth" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/myth.png" title="可靠消息分布式事务解决方案。" width="15%">
|
||||
</a>
|
||||
<a href="https://cubic.jiagoujishu.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/cubic.png" title="一站式问题定位平台,以agent的方式无侵入接入应用,完整集成arthas功能模块,致力于应用级监控,帮助开发人员快速定位问题" width="15%">
|
||||
</a>
|
||||
<a href="https://maxkey.top/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/maxkey.png" title="业界领先的身份管理和认证产品" width="15%">
|
||||
</a>
|
||||
<a href="http://forest.dtflyx.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/forest-logo.png" title="Forest能够帮助您使用更简单的方式编写Java的HTTP客户端" width="15%">
|
||||
</a>
|
||||
<a href="https://jpom.io/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/jpom.png" title="一款简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件" width="15%">
|
||||
</a>
|
||||
<a href="https://su.usthe.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/sureness.png" title="面向 REST API 的高性能认证鉴权框架" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://easy-es.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/easy-es2.png" title="傻瓜级ElasticSearch搜索引擎ORM框架" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/northstar" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/northstar_logo.png" title="Northstar盈富量化交易平台" width="15%">
|
||||
</a>
|
||||
<a href="https://hertzbeat.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/hertzbeat_brand.jpg" title="易用友好的云监控系统" width="15%">
|
||||
</a>
|
||||
<a href="https://plugins.sheng90.wang/fast-request/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/fast-request.gif" title="Idea 版 Postman,为简化调试API而生" width="15%">
|
||||
</a>
|
||||
<a href="https://www.jeesuite.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/mendmix.png" title="开源分布式云原生架构一站式解决方案" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/koalas-rpc" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/koalas-rpc2.png" title="企业生产级百亿日PV高可用可拓展的RPC框架。" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://async.sizegang.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/gobrs-async.png" title="配置极简功能强大的异步任务动态编排框架" width="15%">
|
||||
</a>
|
||||
<a href="https://dynamictp.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/dynamic-tp.png" title="基于配置中心的轻量级动态可监控线程池" width="15%">
|
||||
</a>
|
||||
<a href="https://www.x-easypdf.cn" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/x-easypdf.png" title="一个用搭积木的方式构建pdf的框架(基于pdfbox)" width="15%">
|
||||
</a>
|
||||
<a href="http://dromara.gitee.io/image-combiner" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/image-combiner.png" title="一个专门用于图片合成的工具,没有很复杂的功能,简单实用,却不失强大" width="15%">
|
||||
</a>
|
||||
<a href="https://www.herodotus.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/dante-cloud2.png" title="Dante-Cloud 是一款企业级微服务架构和服务能力开发平台。" width="15%">
|
||||
</a>
|
||||
<a href="https://dromara.org/zh/projects/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/dromara.png" title="让每一位开源爱好者,体会到开源的快乐。" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
221
README.zh-CN.md
Normal file
@@ -0,0 +1,221 @@
|
||||
[](https://gitee.com/dromara/electron-egg/stargazers)
|
||||
|
||||
<div align=center>
|
||||
<h3>🎉🎉🎉 ElectronEgg V4 已发布! 🎉🎉🎉</h3>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div align=center>
|
||||
<img src="./public/images/example/logo.png" width="150" height="150" />
|
||||
</div>
|
||||
|
||||
<div align=center>
|
||||
<h3><strong>一个入门简单、跨平台、企业级桌面软件开发框架</strong></h3>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!-- ## 🌏 [English](https://www.yuque.com/u34495/ee-doc) | [中文](https://www.kaka996.com/) -->
|
||||
|
||||
## 📋 介绍
|
||||
|
||||
> 框架已经广泛应用于记账、政务、企业、医疗、学校、股票交易、ERP、娱乐、视频等领域客户端,请放心使用!
|
||||
|
||||
## 👦 谁可以使用
|
||||
|
||||
项目已经有 5 个交流群,覆盖`前端`、`java`、`go`、`python`、`php` 等开发者。
|
||||
|
||||
无论你是前端、服务端、运维、游戏、客户端等,都可以很快入门,
|
||||
|
||||
## 🐶 精彩案例
|
||||
|
||||
- [**点击查看**](#项目案例)
|
||||
|
||||
## 📺 特点
|
||||
- 🍩 **为什么使用?** 桌面软件(办公方向、 个人工具),仍然是未来十几年PC端需求之一,提高工作效率
|
||||
- 🍉 **简单:** 只需懂 JavaScript
|
||||
- 🍑 **愿景:** 所有开发者都能学会桌面软件研发
|
||||
- 🍰 **gitee:** https://gitee.com/dromara/electron-egg **5100+**
|
||||
- 🍨 **github:** https://github.com/dromara/electron-egg **1800+**
|
||||
- 🏆 码云最有价值开源项目
|
||||

|
||||
|
||||
## 📚 文档
|
||||
- 快速体验:[教程文档](https://www.kaka996.com/)
|
||||

|
||||
|
||||
## 📦 特性
|
||||
1. 🍄 跨平台:一套代码,可以打包成windows版、Mac版、Linux版、国产UOS、Deepin、麒麟等
|
||||
2. 🌹 架构:单业务进程/模块化/多任务(进程,线程,渲染进程),让开发大型项目变的简单。
|
||||
3. 🌱 简单高效:只需学习 js 语言
|
||||
4. 🌴 前端独立:理论上支持任何前端技术,如:vue、react、html等等
|
||||
5. 🍁 工程化:可以用前端、服务端的开发思维,来编写桌面软件
|
||||
6. 🌷 高性能:事件驱动、非阻塞式IO
|
||||
7. 🌰 功能丰富:配置、通信、插件、数据库、升级、打包、工具... 应有尽有
|
||||
8. 💐 安全:支持字节码加密、压缩混淆加密
|
||||
9. 🌻 功能demo:桌面软件常见功能,框架集成或提供demo
|
||||
|
||||
## ✈️ 使用场景
|
||||
|
||||
### 1. 🚀 常规桌面软件
|
||||
- 🚖 windows平台
|
||||
|
||||

|
||||
|
||||
- 🚍 macOS平台
|
||||

|
||||
|
||||
- 🚔 linux平台 - 国产UOS、Deepin
|
||||

|
||||
|
||||
- 🚔 linux平台 - ubuntu
|
||||

|
||||
|
||||
### 🚐 2. vue、react、angular、web 转换成桌面软件
|
||||
- 🚙 vue-ant-design(本地)
|
||||
|
||||

|
||||
|
||||
- 🚙 禅道项目管理(web项目地址)
|
||||
|
||||

|
||||
|
||||
### 🚂 3. 游戏(h5相关技术开发)
|
||||
- 🚊 忍者100层
|
||||
|
||||

|
||||
|
||||
|
||||
## 📒 开始使用
|
||||
|
||||
- ✒️ [安装文档](https://www.kaka996.com/pages/e64ff6/)
|
||||
|
||||
## 项目案例
|
||||
- 🐟 框架已经应用于医疗、学校、政务、股票交易、ERP、娱乐、视频、企业等领域客户端
|
||||
|
||||
### 🐸 远控
|
||||
|
||||
- RQ Center
|
||||

|
||||

|
||||
|
||||
### 🐸 云盘
|
||||
|
||||
- FM Cloud
|
||||

|
||||

|
||||

|
||||
|
||||
### 🐸 IM
|
||||
|
||||
- Cede IM
|
||||

|
||||

|
||||

|
||||
|
||||
### 🐸 壁纸
|
||||
|
||||
- warpar
|
||||

|
||||
|
||||
### 🐸 英雄联盟助手
|
||||
|
||||
- Serendlplty
|
||||

|
||||
|
||||
### 🐸 更多
|
||||
|
||||
- [更多案例](https://www.kaka996.com/pages/eadf46/)
|
||||
|
||||
## 💬 交流
|
||||
1. [讨论](https://www.kaka996.com/pages/c2720e/)
|
||||
|
||||
## 📌 关于pr
|
||||
请前往[GitHub项目](https://github.com/dromara/electron-egg)提pr(避免代码同步后,pr被覆盖掉),感谢!
|
||||
|
||||
地址:https://github.com/dromara/electron-egg
|
||||
|
||||
## 📔 框架核心包 ee-core
|
||||
ee-core:[https://github.com/wallace5303/ee-core](https://github.com/wallace5303/ee-core)
|
||||
|
||||
## 📚 Dromara 成员项目
|
||||
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/dromara/TLog" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/tlog2.png" title="一个轻量级的分布式日志标记追踪神器,10分钟即可接入,自动对日志打标签完成微服务的链路追踪" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/liteFlow" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/liteflow.png" title="轻量,快速,稳定,可编排的组件式流程引擎" width="15%">
|
||||
</a>
|
||||
<a href="https://hutool.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/hutool.jpg" title="小而全的Java工具类库,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。" width="15%">
|
||||
</a>
|
||||
<a href="https://sa-token.dev33.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/sa-token.png" title="一个轻量级 java 权限认证框架,让鉴权变得简单、优雅!" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/hmily" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/hmily.png" title="高性能一站式分布式事务解决方案。" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/Raincat" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/raincat.png" title="强一致性分布式事务解决方案。" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/dromara/myth" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/myth.png" title="可靠消息分布式事务解决方案。" width="15%">
|
||||
</a>
|
||||
<a href="https://cubic.jiagoujishu.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/cubic.png" title="一站式问题定位平台,以agent的方式无侵入接入应用,完整集成arthas功能模块,致力于应用级监控,帮助开发人员快速定位问题" width="15%">
|
||||
</a>
|
||||
<a href="https://maxkey.top/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/maxkey.png" title="业界领先的身份管理和认证产品" width="15%">
|
||||
</a>
|
||||
<a href="http://forest.dtflyx.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/forest-logo.png" title="Forest能够帮助您使用更简单的方式编写Java的HTTP客户端" width="15%">
|
||||
</a>
|
||||
<a href="https://jpom.io/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/jpom.png" title="一款简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件" width="15%">
|
||||
</a>
|
||||
<a href="https://su.usthe.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/sureness.png" title="面向 REST API 的高性能认证鉴权框架" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://easy-es.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/easy-es2.png" title="傻瓜级ElasticSearch搜索引擎ORM框架" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/northstar" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/northstar_logo.png" title="Northstar盈富量化交易平台" width="15%">
|
||||
</a>
|
||||
<a href="https://hertzbeat.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/hertzbeat_brand.jpg" title="易用友好的云监控系统" width="15%">
|
||||
</a>
|
||||
<a href="https://plugins.sheng90.wang/fast-request/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/fast-request.gif" title="Idea 版 Postman,为简化调试API而生" width="15%">
|
||||
</a>
|
||||
<a href="https://www.jeesuite.com/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/mendmix.png" title="开源分布式云原生架构一站式解决方案" width="15%">
|
||||
</a>
|
||||
<a href="https://gitee.com/dromara/koalas-rpc" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/koalas-rpc2.png" title="企业生产级百亿日PV高可用可拓展的RPC框架。" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://async.sizegang.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/gobrs-async.png" title="配置极简功能强大的异步任务动态编排框架" width="15%">
|
||||
</a>
|
||||
<a href="https://dynamictp.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/dynamic-tp.png" title="基于配置中心的轻量级动态可监控线程池" width="15%">
|
||||
</a>
|
||||
<a href="https://www.x-easypdf.cn" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/x-easypdf.png" title="一个用搭积木的方式构建pdf的框架(基于pdfbox)" width="15%">
|
||||
</a>
|
||||
<a href="http://dromara.gitee.io/image-combiner" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/image-combiner.png" title="一个专门用于图片合成的工具,没有很复杂的功能,简单实用,却不失强大" width="15%">
|
||||
</a>
|
||||
<a href="https://www.herodotus.cn/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/dante-cloud2.png" title="Dante-Cloud 是一款企业级微服务架构和服务能力开发平台。" width="15%">
|
||||
</a>
|
||||
<a href="https://dromara.org/zh/projects/" target="_blank">
|
||||
<img src="https://oss.dev33.cn/sa-token/link/dromara.png" title="让每一位开源爱好者,体会到开源的快乐。" width="15%">
|
||||
</a>
|
||||
</p>
|
||||
1
build/extraResources/chromeExtension/read.txt
Normal file
@@ -0,0 +1 @@
|
||||
chrome应用商店ctx文件,解压后,放置在此目录中,打包时会将资源加入安装包内。
|
||||
1
build/extraResources/read.txt
Normal file
@@ -0,0 +1 @@
|
||||
建议第三方软件放置在此目录中,打包时会将资源加入安装包内。
|
||||
BIN
build/icons/256x256.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
build/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
build/icons/512x512.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
build/icons/64x64.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
BIN
build/icons/favicon.ico
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
build/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 256 KiB |
0
build/script/installer.nsh
Normal file
199
cmd/bin.js
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* ee-bin 配置
|
||||
* 仅适用于开发环境
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* development serve ("frontend" "electron" )
|
||||
* ee-bin dev
|
||||
*/
|
||||
dev: {
|
||||
frontend: {
|
||||
directory: './frontend',
|
||||
cmd: 'npm',
|
||||
args: ['run', 'dev'],
|
||||
port: 8080,
|
||||
},
|
||||
electron: {
|
||||
directory: './',
|
||||
cmd: 'electron',
|
||||
args: ['.', '--env=local'],
|
||||
watch: true,
|
||||
delay: 1000,
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 构建
|
||||
* ee-bin build
|
||||
*/
|
||||
build: {
|
||||
frontend: {
|
||||
directory: './frontend',
|
||||
cmd: 'npm',
|
||||
args: ['run', 'build'],
|
||||
},
|
||||
electron: {
|
||||
type: 'typescript',
|
||||
},
|
||||
win64: {
|
||||
cmd: 'electron-builder',
|
||||
directory: './',
|
||||
args: ['--config=./cmd/builder.json', '-w=nsis', '--x64'],
|
||||
},
|
||||
win32: {
|
||||
args: ['--config=./cmd/builder.json', '-w=nsis', '--ia32'],
|
||||
},
|
||||
win_e: {
|
||||
args: ['--config=./cmd/builder.json', '-w=portable', '--x64'],
|
||||
},
|
||||
win_7z: {
|
||||
args: ['--config=./cmd/builder.json', '-w=7z', '--x64'],
|
||||
},
|
||||
mac: {
|
||||
args: ['--config=./cmd/builder-mac.json', '-m'],
|
||||
},
|
||||
mac_arm64: {
|
||||
args: ['--config=./cmd/builder-mac-arm64.json', '-m', '--arm64'],
|
||||
},
|
||||
linux: {
|
||||
args: ['--config=./cmd/builder-linux.json', '-l=deb', '--x64'],
|
||||
},
|
||||
linux_arm64: {
|
||||
args: ['--config=./cmd/builder-linux.json', '-l=deb', '--arm64'],
|
||||
},
|
||||
go_w: {
|
||||
directory: './go',
|
||||
cmd: 'go',
|
||||
args: ['build', '-o=../build/extraResources/goapp.exe'],
|
||||
},
|
||||
go_m: {
|
||||
directory: './go',
|
||||
cmd: 'go',
|
||||
args: ['build', '-o=../build/extraResources/goapp'],
|
||||
},
|
||||
go_l: {
|
||||
directory: './go',
|
||||
cmd: 'go',
|
||||
args: ['build', '-o=../build/extraResources/goapp'],
|
||||
},
|
||||
python: {
|
||||
directory: './python',
|
||||
cmd: 'python',
|
||||
args: ['./setup.py', 'build'],
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 移动资源
|
||||
* ee-bin move
|
||||
*/
|
||||
move: {
|
||||
frontend_dist: {
|
||||
src: './frontend/dist',
|
||||
dest: './public/dist'
|
||||
},
|
||||
go_static: {
|
||||
src: './frontend/dist',
|
||||
dest: './go/public/dist'
|
||||
},
|
||||
go_config: {
|
||||
src: './go/config',
|
||||
dest: './go/public/config'
|
||||
},
|
||||
go_package: {
|
||||
src: './package.json',
|
||||
dest: './go/public/package.json'
|
||||
},
|
||||
go_images: {
|
||||
src: './public/images',
|
||||
dest: './go/public/images'
|
||||
},
|
||||
python_dist: {
|
||||
src: './python/dist',
|
||||
dest: './build/extraResources/py'
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 预发布模式(prod)
|
||||
* ee-bin start
|
||||
*/
|
||||
start: {
|
||||
directory: './',
|
||||
cmd: 'electron',
|
||||
args: ['.', '--env=prod']
|
||||
},
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*/
|
||||
encrypt: {
|
||||
frontend: {
|
||||
type: 'none',
|
||||
files: [
|
||||
'./public/dist/**/*.(js|json)',
|
||||
],
|
||||
cleanFiles: ['./public/dist'],
|
||||
confusionOptions: {
|
||||
compact: true,
|
||||
stringArray: true,
|
||||
stringArrayEncoding: ['none'],
|
||||
stringArrayCallsTransform: true,
|
||||
numbersToExpressions: true,
|
||||
target: 'browser',
|
||||
}
|
||||
},
|
||||
electron: {
|
||||
type: 'confusion',
|
||||
files: [
|
||||
'./public/electron/**/*.(js|json)',
|
||||
],
|
||||
cleanFiles: ['./public/electron'],
|
||||
specificFiles: [
|
||||
'./public/electron/main.js',
|
||||
'./public/electron/preload/bridge.js',
|
||||
],
|
||||
confusionOptions: {
|
||||
compact: true,
|
||||
stringArray: true,
|
||||
stringArrayEncoding: ['none'],
|
||||
deadCodeInjection: false,
|
||||
stringArrayCallsTransform: true,
|
||||
numbersToExpressions: true,
|
||||
target: 'node',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 执行自定义命令
|
||||
* ee-bin exec
|
||||
*/
|
||||
exec: {
|
||||
// 单独调试,air 实现 go 热重载
|
||||
go: {
|
||||
directory: './go',
|
||||
cmd: 'air',
|
||||
args: ['-c=config/.air.toml' ],
|
||||
},
|
||||
// windows 单独调试,air 实现 go 热重载
|
||||
go_w: {
|
||||
directory: './go',
|
||||
cmd: 'air',
|
||||
args: ['-c=config/.air.windows.toml' ],
|
||||
},
|
||||
// 单独调试,以基础方式启动 go
|
||||
go2: {
|
||||
directory: './go',
|
||||
cmd: 'go',
|
||||
args: ['run', './main.go', '--env=dev','--basedir=../', '--port=7073'],
|
||||
},
|
||||
python: {
|
||||
directory: './python',
|
||||
cmd: 'python',
|
||||
args: ['./main.py', '--port=7074'],
|
||||
stdio: "inherit", // ignore
|
||||
},
|
||||
},
|
||||
};
|
||||
40
cmd/builder-linux.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"productName": "PQS9100工具箱",
|
||||
"appId": "com.njcn.pqs9100.tool",
|
||||
"copyright": "© 2025 njcn Technology Co., Ltd.",
|
||||
"directories": {
|
||||
"output": "out"
|
||||
},
|
||||
"asar": true,
|
||||
"files": [
|
||||
"**/*",
|
||||
"!cmd/",
|
||||
"!data/",
|
||||
"!electron/",
|
||||
"!frontend/",
|
||||
"!logs/",
|
||||
"!out/",
|
||||
"!go/",
|
||||
"!python/"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "build/extraResources",
|
||||
"to": "extraResources"
|
||||
}
|
||||
],
|
||||
"publish": [
|
||||
{
|
||||
"provider": "generic",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
"icon": "build/icons/icon.icns",
|
||||
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||
"target": [
|
||||
"deb"
|
||||
],
|
||||
"category": "Utility"
|
||||
}
|
||||
}
|
||||
38
cmd/builder-mac-arm64.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"productName": "PQS9100工具箱",
|
||||
"appId": "com.njcn.pqs9100.tool",
|
||||
"copyright": "© 2025 njcn Technology Co., Ltd.",
|
||||
"directories": {
|
||||
"output": "out"
|
||||
},
|
||||
"asar": true,
|
||||
"files": [
|
||||
"**/*",
|
||||
"!cmd/",
|
||||
"!data/",
|
||||
"!electron/",
|
||||
"!frontend/",
|
||||
"!logs/",
|
||||
"!out/",
|
||||
"!go/",
|
||||
"!python/"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "build/extraResources",
|
||||
"to": "extraResources"
|
||||
}
|
||||
],
|
||||
"publish": [
|
||||
{
|
||||
"provider": "generic",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"mac": {
|
||||
"icon": "build/icons/icon.icns",
|
||||
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||
"darkModeSupport": true,
|
||||
"hardenedRuntime": false
|
||||
}
|
||||
}
|
||||
38
cmd/builder-mac.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"productName": "PQS9100工具箱",
|
||||
"appId": "com.njcn.pqs9100.tool",
|
||||
"copyright": "© 2025 njcn Technology Co., Ltd.",
|
||||
"directories": {
|
||||
"output": "out"
|
||||
},
|
||||
"asar": true,
|
||||
"files": [
|
||||
"**/*",
|
||||
"!cmd/",
|
||||
"!data/",
|
||||
"!electron/",
|
||||
"!frontend/",
|
||||
"!logs/",
|
||||
"!out/",
|
||||
"!go/",
|
||||
"!python/"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "build/extraResources",
|
||||
"to": "extraResources"
|
||||
}
|
||||
],
|
||||
"publish": [
|
||||
{
|
||||
"provider": "generic",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"mac": {
|
||||
"icon": "build/icons/icon.icns",
|
||||
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||
"darkModeSupport": true,
|
||||
"hardenedRuntime": false
|
||||
}
|
||||
}
|
||||
50
cmd/builder.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"productName": "PQS9100工具箱",
|
||||
"appId": "com.njcn.pqs9100.tool",
|
||||
"copyright": "© 2025 njcn Technology Co., Ltd.",
|
||||
"directories": {
|
||||
"output": "out"
|
||||
},
|
||||
"asar": true,
|
||||
"files": [
|
||||
"**/*",
|
||||
"!cmd/",
|
||||
"!data/",
|
||||
"!electron/",
|
||||
"!frontend/",
|
||||
"!logs/",
|
||||
"!out/",
|
||||
"!go/",
|
||||
"!python/"
|
||||
],
|
||||
"extraResources": {
|
||||
"from": "build/extraResources/",
|
||||
"to": "extraResources"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowElevation": true,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"installerIcon": "build/icons/icon.ico",
|
||||
"uninstallerIcon": "build/icons/icon.ico",
|
||||
"installerHeaderIcon": "build/icons/icon.ico",
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "PQS9100工具箱"
|
||||
},
|
||||
"publish": [
|
||||
{
|
||||
"provider": "generic",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"win": {
|
||||
"icon": "build/icons/icon.ico",
|
||||
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
66
electron/config/config.default.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import path from 'path';
|
||||
import {getBaseDir} from 'ee-core/ps';
|
||||
import {type AppConfig} from 'ee-core/config';
|
||||
|
||||
const config: () => AppConfig = () => {
|
||||
return {
|
||||
openDevTools: false,
|
||||
singleLock: true,
|
||||
windowsOption: {
|
||||
title: 'PQS9100工具箱', // 软件标题
|
||||
width: 980, // 软件窗口宽度
|
||||
height: 650, // 软件窗口高度
|
||||
minWidth: 800, // 软件窗口最小宽度
|
||||
minHeight: 650, // 软件窗口最小高度
|
||||
autoHideMenuBar: true, // 默认不显示菜单栏,
|
||||
webPreferences: {
|
||||
webSecurity: true,
|
||||
contextIsolation: false,
|
||||
nodeIntegration: true,
|
||||
},
|
||||
frame: true,
|
||||
show: false,
|
||||
icon: path.join(getBaseDir(), 'public', 'images', 'logo-32.png'),
|
||||
},
|
||||
logger: {
|
||||
level: 'INFO',
|
||||
outputJSON: false,
|
||||
appLogName: 'pqs-9100_tool.log',
|
||||
coreLogName: 'pqs-9100_tool-core.log',
|
||||
errorLogName: 'pqs-9100_tool-error.log',
|
||||
},
|
||||
remote: {
|
||||
enable: false,
|
||||
url: '',
|
||||
},
|
||||
socketServer: {
|
||||
enable: true,
|
||||
port: 7070,
|
||||
path: "/socket.io/",
|
||||
connectTimeout: 45000,
|
||||
pingTimeout: 30000,
|
||||
pingInterval: 25000,
|
||||
maxHttpBufferSize: 1e8,
|
||||
transports: ["polling", "websocket"],
|
||||
cors: {
|
||||
origin: true,
|
||||
},
|
||||
channel: 'socket-channel',
|
||||
},
|
||||
httpServer: {
|
||||
enable: true,
|
||||
https: {
|
||||
enable: false,
|
||||
key: '/public/ssl/localhost+1.key',
|
||||
cert: '/public/ssl/localhost+1.pem',
|
||||
},
|
||||
host: '127.0.0.1',
|
||||
port: 7071,
|
||||
},
|
||||
mainServer: {
|
||||
indexPath: '/public/dist/index.html',
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default config;
|
||||
14
electron/config/config.local.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { type AppConfig } from 'ee-core/config';
|
||||
|
||||
const config: () => AppConfig = () => {
|
||||
return {
|
||||
openDevTools: {
|
||||
mode: 'bottom'
|
||||
},
|
||||
jobs: {
|
||||
messageLog: false
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default config;
|
||||
9
electron/config/config.prod.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { type AppConfig } from 'ee-core/config';
|
||||
|
||||
const config: () => AppConfig = () => {
|
||||
return {
|
||||
openDevTools: false,
|
||||
};
|
||||
};
|
||||
|
||||
export default config;
|
||||
63
electron/controller/cross.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { crossService } from '../service/cross';
|
||||
|
||||
/**
|
||||
* Cross
|
||||
* @class
|
||||
*/
|
||||
class CrossController {
|
||||
|
||||
/**
|
||||
* View process service information
|
||||
*/
|
||||
info() {
|
||||
crossService.info();
|
||||
return 'hello electron-egg';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get service url
|
||||
*/
|
||||
async getUrl(args: { name: string }): Promise<string> {
|
||||
const { name } = args;
|
||||
const serverUrl = crossService.getUrl(name);
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* kill service
|
||||
* By default (modifiable), killing the process will exit the electron application.
|
||||
*/
|
||||
async killServer(args: { type: string; name: string }): Promise<void> {
|
||||
const { type, name } = args;
|
||||
crossService.killServer(type, name);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* create service
|
||||
*/
|
||||
async createServer(args: { program: string }): Promise<void> {
|
||||
const { program } = args;
|
||||
if (program == 'go') {
|
||||
crossService.createGoServer();
|
||||
} else if (program == 'java') {
|
||||
crossService.createJavaServer();
|
||||
} else if (program == 'python') {
|
||||
crossService.createPythonServer();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the api for the cross service
|
||||
*/
|
||||
async requestApi(args: { name: string; urlPath: string; params: any }): Promise<any> {
|
||||
const { name, urlPath, params} = args;
|
||||
const data = await crossService.requestApi(name, urlPath, params);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
CrossController.toString = () => '[class CrossController]';
|
||||
|
||||
export default CrossController;
|
||||
63
electron/controller/effect.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { dialog } from 'electron';
|
||||
import { getMainWindow } from 'ee-core/electron';
|
||||
|
||||
/**
|
||||
* effect - demo
|
||||
* @class
|
||||
*/
|
||||
class EffectController {
|
||||
|
||||
/**
|
||||
* select file
|
||||
*/
|
||||
selectFile(): string | null {
|
||||
const filePaths = dialog.showOpenDialogSync({
|
||||
properties: ['openFile']
|
||||
});
|
||||
|
||||
if (!filePaths) {
|
||||
return null
|
||||
}
|
||||
|
||||
return filePaths[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* login window
|
||||
*/
|
||||
loginWindow(args: { width?: number; height?: number }): void {
|
||||
const { width, height } = args;
|
||||
const win = getMainWindow();
|
||||
|
||||
const size = {
|
||||
width: width || 400,
|
||||
height: height || 300
|
||||
}
|
||||
win.setSize(size.width, size.height);
|
||||
win.setResizable(true);
|
||||
win.center();
|
||||
win.show();
|
||||
win.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* restore window
|
||||
*/
|
||||
restoreWindow(args: { width?: number; height?: number }): void {
|
||||
const { width, height } = args;
|
||||
const win = getMainWindow();
|
||||
|
||||
const size = {
|
||||
width: width || 980,
|
||||
height: height || 650
|
||||
}
|
||||
win.setSize(size.width, size.height);
|
||||
win.setResizable(true);
|
||||
win.center();
|
||||
win.show();
|
||||
win.focus();
|
||||
}
|
||||
}
|
||||
EffectController.toString = () => '[class EffectController]';
|
||||
|
||||
export default EffectController;
|
||||
16
electron/controller/example.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* example
|
||||
* @class
|
||||
*/
|
||||
class ExampleController {
|
||||
|
||||
/**
|
||||
* test
|
||||
*/
|
||||
async test(): Promise<string> {
|
||||
return 'hello electron-egg';
|
||||
}
|
||||
}
|
||||
ExampleController.toString = () => '[class ExampleController]';
|
||||
|
||||
export default ExampleController;
|
||||
167
electron/controller/os.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { app as electronApp, dialog, shell } from 'electron';
|
||||
import { windowService } from '../service/os/window';
|
||||
|
||||
/**
|
||||
* example
|
||||
* @class
|
||||
*/
|
||||
class OsController {
|
||||
|
||||
/**
|
||||
* All methods receive two parameters
|
||||
* @param args Parameters transmitted by the frontend
|
||||
* @param event - Event are only available during IPC communication. For details, please refer to the controller documentation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Message prompt dialog box
|
||||
*/
|
||||
messageShow(): string {
|
||||
dialog.showMessageBoxSync({
|
||||
type: 'info', // "none", "info", "error", "question" 或者 "warning"
|
||||
title: 'Custom Title',
|
||||
message: 'Customize message content',
|
||||
detail: 'Other additional information'
|
||||
})
|
||||
|
||||
return 'Opened the message box';
|
||||
}
|
||||
|
||||
/**
|
||||
* Message prompt and confirmation dialog box
|
||||
*/
|
||||
messageShowConfirm(): string {
|
||||
const res = dialog.showMessageBoxSync({
|
||||
type: 'info',
|
||||
title: 'Custom Title',
|
||||
message: 'Customize message content',
|
||||
detail: 'Other additional information',
|
||||
cancelId: 1, // Index of buttons used to cancel dialog boxes
|
||||
defaultId: 0, // Set default selected button
|
||||
buttons: ['confirm', 'cancel'],
|
||||
})
|
||||
let data = (res === 0) ? 'click the confirm button' : 'click the cancel button';
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select Directory
|
||||
*/
|
||||
selectFolder() {
|
||||
const filePaths = dialog.showOpenDialogSync({
|
||||
properties: ['openDirectory', 'createDirectory']
|
||||
});
|
||||
|
||||
if (!filePaths) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filePaths[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* open directory
|
||||
*/
|
||||
openDirectory(args: { id: any }): boolean {
|
||||
const { id } = args;
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
let dir = '';
|
||||
if (path.isAbsolute(id)) {
|
||||
dir = id;
|
||||
} else {
|
||||
dir = electronApp.getPath(id);
|
||||
}
|
||||
|
||||
shell.openPath(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select Picture
|
||||
*/
|
||||
selectPic(): string | null {
|
||||
const filePaths = dialog.showOpenDialogSync({
|
||||
title: 'select pic',
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
|
||||
]
|
||||
});
|
||||
if (!filePaths) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const data = fs.readFileSync(filePaths[0]);
|
||||
const pic = 'data:image/jpeg;base64,' + data.toString('base64');
|
||||
return pic;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new window
|
||||
*/
|
||||
createWindow(args: any): any {
|
||||
const wcid = windowService.createWindow(args);
|
||||
return wcid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Window contents id
|
||||
*/
|
||||
getWCid(args: any): any {
|
||||
const wcid = windowService.getWCid(args);
|
||||
return wcid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Realize communication between two windows through the transfer of the main process
|
||||
*/
|
||||
window1ToWindow2(args: any): void {
|
||||
windowService.communicate(args);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Realize communication between two windows through the transfer of the main process
|
||||
*/
|
||||
window2ToWindow1(args: any): void {
|
||||
windowService.communicate(args);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create system notifications
|
||||
*/
|
||||
sendNotification(args: { title?: string; subtitle?: string; body?: string; silent?: boolean }, event: any): boolean {
|
||||
const { title, subtitle, body, silent} = args;
|
||||
|
||||
const options: any = {};
|
||||
if (title) {
|
||||
options.title = title;
|
||||
}
|
||||
if (subtitle) {
|
||||
options.subtitle = subtitle;
|
||||
}
|
||||
if (body) {
|
||||
options.body = body;
|
||||
}
|
||||
if (silent !== undefined) {
|
||||
options.silent = silent;
|
||||
}
|
||||
windowService.createNotification(options, event);
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
OsController.toString = () => '[class OsController]';
|
||||
|
||||
export default OsController;
|
||||
10
electron/jobs/example/hello.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { logger } from 'ee-core/log';
|
||||
|
||||
/**
|
||||
* Welcome function
|
||||
*/
|
||||
function welcome(): void {
|
||||
logger.info('[child-process] [jobs/example/hello] welcome !');
|
||||
}
|
||||
|
||||
export { welcome };
|
||||
98
electron/jobs/example/timer.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { logger } from 'ee-core/log';
|
||||
import { isChildJob, exit } from 'ee-core/ps';
|
||||
import { childMessage } from 'ee-core/message';
|
||||
import { welcome } from './hello';
|
||||
import { UserService } from '../../service/job/user';
|
||||
import { sqlitedbService } from '../../service/database/sqlitedb';
|
||||
|
||||
/**
|
||||
* example - TimerJob
|
||||
* @class
|
||||
*/
|
||||
class TimerJob {
|
||||
timer: NodeJS.Timeout | undefined;
|
||||
timeoutTimer: NodeJS.Timeout | undefined;
|
||||
number: number;
|
||||
countdown: number;
|
||||
|
||||
constructor() {
|
||||
this.timer = undefined;
|
||||
this.timeoutTimer = undefined;
|
||||
this.number = 0;
|
||||
this.countdown = 10; // 倒计时
|
||||
sqlitedbService.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* handle() method is necessary and will be automatically called
|
||||
* params transferred parameters
|
||||
*/
|
||||
async handle(params: any): Promise<void> {
|
||||
logger.info("[child-process] TimerJob params: ", params);
|
||||
const { jobId } = params;
|
||||
|
||||
// Use service in child process
|
||||
// 1. Ensure that the service does not have Electron's API or dependencies, as Electron does not support them
|
||||
const userService = new UserService();
|
||||
userService.hello('job');
|
||||
|
||||
// Execute the task
|
||||
this.number = 0;
|
||||
this.countdown = 10;
|
||||
this.doTimer(jobId);
|
||||
|
||||
// sqlite
|
||||
const userList = await sqlitedbService.getAllTestDataSqlite();
|
||||
logger.info('[child-process] Sqlite userList:', userList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the job
|
||||
*/
|
||||
async pause(jobId: string): Promise<void> {
|
||||
logger.info("[child-process] Pause timerJob, jobId: ", jobId);
|
||||
clearInterval(this.timer);
|
||||
clearInterval(this.timeoutTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume the job
|
||||
*/
|
||||
async resume(jobId: string, pid: number): Promise<void> {
|
||||
logger.info("[child-process] Resume timerJob, jobId: ", jobId, ", pid: ", pid);
|
||||
this.doTimer(jobId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the task
|
||||
*/
|
||||
async doTimer(jobId) {
|
||||
// Timer to simulate the task
|
||||
const eventName = 'job-timer-progress-' + jobId;
|
||||
this.timer = setInterval(() => {
|
||||
welcome();
|
||||
|
||||
childMessage.send(eventName, {jobId, number: this.number, end: false});
|
||||
this.number++;
|
||||
this.countdown--;
|
||||
}, 1000);
|
||||
|
||||
// Use setTimeout to simulate the task duration
|
||||
this.timeoutTimer = setTimeout(() => {
|
||||
// Stop the timer to simulate the task
|
||||
clearInterval(this.timer);
|
||||
|
||||
// Task completed, reset the front-end display
|
||||
childMessage.send(eventName, {jobId, number:0, pid:0, end: true});
|
||||
|
||||
// If it is a childJob task, call exit() to exit the process, otherwise it will stay in memory
|
||||
// If it is a childPoolJob task, stay in memory and wait for the next business
|
||||
if (isChildJob()) {
|
||||
exit();
|
||||
}
|
||||
}, this.countdown * 1000)
|
||||
}
|
||||
}
|
||||
TimerJob.toString = () => '[class TimerJob]';
|
||||
|
||||
export default TimerJob;
|
||||
19
electron/main.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ElectronEgg } from 'ee-core';
|
||||
import { Lifecycle } from './preload/lifecycle';
|
||||
import { preload } from './preload';
|
||||
|
||||
// New app
|
||||
const app = new ElectronEgg();
|
||||
|
||||
// Register lifecycle
|
||||
const life = new Lifecycle();
|
||||
app.register("ready", life.ready);
|
||||
app.register("electron-app-ready", life.electronAppReady);
|
||||
app.register("window-ready", life.windowReady);
|
||||
app.register("before-close", life.beforeClose);
|
||||
|
||||
// Register preload
|
||||
app.register("preload", preload);
|
||||
|
||||
// Run
|
||||
app.run();
|
||||
16
electron/preload/bridge.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 如果启用了上下文隔离,渲染进程无法使用electron的api,
|
||||
* 可通过contextBridge 导出api给渲染进程使用
|
||||
*/
|
||||
|
||||
import { type IpcRenderer, contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
// 确保contextBridge.exposeInMainWorld的参数类型正确,这里进行简单的类型定义示例
|
||||
type ElectronApi = {
|
||||
ipcRenderer: IpcRenderer;
|
||||
};
|
||||
|
||||
const ele: ElectronApi = {
|
||||
ipcRenderer,
|
||||
};
|
||||
contextBridge.exposeInMainWorld('electron', ele);
|
||||
21
electron/preload/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Preload module, this file will be loaded when the program starts.
|
||||
*/
|
||||
|
||||
import {logger} from 'ee-core/log';
|
||||
import {trayService} from '../service/os/tray';
|
||||
import {securityService} from '../service/os/security';
|
||||
import {autoUpdaterService} from '../service/os/auto_updater';
|
||||
|
||||
function preload(): void {
|
||||
// Example feature module, optional to use and modify
|
||||
logger.info('[preload] load 5');
|
||||
trayService.create();
|
||||
securityService.create();
|
||||
autoUpdaterService.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point of the preload module
|
||||
*/
|
||||
export { preload };
|
||||
70
electron/preload/lifecycle.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { app as electronApp, screen } from 'electron';
|
||||
import { logger } from 'ee-core/log';
|
||||
import { getConfig } from 'ee-core/config';
|
||||
import { getMainWindow } from 'ee-core/electron';
|
||||
|
||||
class Lifecycle {
|
||||
/**
|
||||
* Core app has been loaded
|
||||
*/
|
||||
async ready(): Promise<void> {
|
||||
logger.info('[lifecycle] ready');
|
||||
}
|
||||
|
||||
/**
|
||||
* Electron app is ready
|
||||
*/
|
||||
async electronAppReady(): Promise<void> {
|
||||
logger.info('[lifecycle] electron-app-ready');
|
||||
|
||||
// When double clicking the icon, display the opened window
|
||||
electronApp.on('second-instance', () => {
|
||||
const win = getMainWindow();
|
||||
if (win.isMinimized()) {
|
||||
win.restore();
|
||||
}
|
||||
win.show();
|
||||
win.focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Main window has been loaded
|
||||
*/
|
||||
async windowReady(): Promise<void> {
|
||||
logger.info('[lifecycle] window-ready');
|
||||
|
||||
const win = getMainWindow();
|
||||
|
||||
// The window is centered and scaled proportionally
|
||||
// Obtain the size information of the main screen, calculate the width and height of the window as a percentage of the screen,
|
||||
// and calculate the coordinates of the upper left corner when the window is centered
|
||||
const mainScreen = screen.getPrimaryDisplay();
|
||||
const { width, height } = mainScreen.workAreaSize;
|
||||
const windowWidth = Math.floor(width * 0.6);
|
||||
const windowHeight = Math.floor(height * 0.8);
|
||||
const x = Math.floor((width - windowWidth) / 2);
|
||||
const y = Math.floor((height - windowHeight) / 2);
|
||||
win.setBounds({ x, y, width: windowWidth, height: windowHeight });
|
||||
|
||||
// Delay loading, no white screen
|
||||
const config = getConfig();
|
||||
const { windowsOption } = config;
|
||||
if (windowsOption?.show == false) {
|
||||
win.once('ready-to-show', () => {
|
||||
win.show();
|
||||
win.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Before app close
|
||||
*/
|
||||
async beforeClose(): Promise<void> {
|
||||
logger.info('[lifecycle] before-close');
|
||||
}
|
||||
}
|
||||
Lifecycle.toString = () => '[class Lifecycle]';
|
||||
|
||||
export { Lifecycle };
|
||||
144
electron/service/cross.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { logger } from 'ee-core/log';
|
||||
import { getExtraResourcesDir, getLogDir } from 'ee-core/ps';
|
||||
import path from 'path';
|
||||
import axios from 'axios';
|
||||
import { is } from 'ee-core/utils';
|
||||
import { cross } from 'ee-core/cross';
|
||||
|
||||
/**
|
||||
* cross
|
||||
* @class
|
||||
*/
|
||||
class CrossService {
|
||||
|
||||
info(): string {
|
||||
const pids = cross.getPids();
|
||||
logger.info('cross pids:', pids);
|
||||
|
||||
let num = 1;
|
||||
pids.forEach(pid => {
|
||||
let entity = cross.getProc(pid);
|
||||
logger.info(`server-${num} name:${entity.name}`);
|
||||
logger.info(`server-${num} config:`, entity.config);
|
||||
num++;
|
||||
})
|
||||
|
||||
return 'hello electron-egg';
|
||||
}
|
||||
|
||||
getUrl(name: string): string {
|
||||
const serverUrl = cross.getUrl(name);
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
killServer(type: string, name: string): void {
|
||||
if (type == 'all') {
|
||||
cross.killAll();
|
||||
} else {
|
||||
cross.killByName(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create go service
|
||||
* In the default configuration, services can be started with applications.
|
||||
* Developers can turn off the configuration and create it manually.
|
||||
*/
|
||||
async createGoServer(): Promise<void> {
|
||||
// method 1: Use the default Settings
|
||||
//const entity = await cross.run(serviceName);
|
||||
|
||||
// method 2: Use custom configuration
|
||||
const serviceName = "go";
|
||||
const opt = {
|
||||
name: 'goapp',
|
||||
cmd: path.join(getExtraResourcesDir(), 'goapp'),
|
||||
directory: getExtraResourcesDir(),
|
||||
args: ['--port=7073'],
|
||||
appExit: true,
|
||||
}
|
||||
const entity = await cross.run(serviceName, opt);
|
||||
logger.info('server name:', entity.name);
|
||||
logger.info('server config:', entity.config);
|
||||
logger.info('server url:', entity.getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* create java server
|
||||
*/
|
||||
async createJavaServer(): Promise<void> {
|
||||
const serviceName = "java";
|
||||
const jarPath = path.join(getExtraResourcesDir(), 'java-app.jar');
|
||||
const opt = {
|
||||
name: 'javaapp',
|
||||
cmd: path.join(getExtraResourcesDir(), 'jre1.8.0_201/bin/javaw.exe'),
|
||||
directory: getExtraResourcesDir(),
|
||||
args: ['-jar', '-server', '-Xms512M', '-Xmx512M', '-Xss512k', '-Dspring.profiles.active=prod', `-Dserver.port=18080`, `-Dlogging.file.path=${getLogDir()}`, `${jarPath}`],
|
||||
appExit: false,
|
||||
}
|
||||
if (is.macOS()) {
|
||||
// Setup Java program
|
||||
opt.cmd = path.join(getExtraResourcesDir(), 'jre1.8.0_201.jre/Contents/Home/bin/java');
|
||||
}
|
||||
if (is.linux()) {
|
||||
// Setup Java program
|
||||
}
|
||||
|
||||
const entity = await cross.run(serviceName, opt);
|
||||
logger.info('server name:', entity.name);
|
||||
logger.info('server config:', entity.config);
|
||||
logger.info('server url:', cross.getUrl(entity.name));
|
||||
}
|
||||
|
||||
/**
|
||||
* create python service
|
||||
* In the default configuration, services can be started with applications.
|
||||
* Developers can turn off the configuration and create it manually.
|
||||
*/
|
||||
async createPythonServer(): Promise<void> {
|
||||
// method 1: Use the default Settings
|
||||
//const entity = await cross.run(serviceName);
|
||||
|
||||
// method 2: Use custom configuration
|
||||
const serviceName = "python";
|
||||
const opt = {
|
||||
name: 'pyapp',
|
||||
cmd: path.join(getExtraResourcesDir(), 'py', 'pyapp'),
|
||||
directory: path.join(getExtraResourcesDir(), 'py'),
|
||||
args: ['--port=7074'],
|
||||
windowsExtname: true,
|
||||
appExit: true,
|
||||
}
|
||||
const entity = await cross.run(serviceName, opt);
|
||||
logger.info('server name:', entity.name);
|
||||
logger.info('server config:', entity.config);
|
||||
logger.info('server url:', entity.getUrl());
|
||||
}
|
||||
|
||||
async requestApi(name: string, urlPath: string, params: any): Promise<any> {
|
||||
const serverUrl = cross.getUrl(name);
|
||||
const apiHello = serverUrl + urlPath;
|
||||
console.log('Server Url:', serverUrl);
|
||||
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
url: apiHello,
|
||||
timeout: 1000,
|
||||
params,
|
||||
proxy: false,
|
||||
});
|
||||
if (response.status == 200) {
|
||||
const { data } = response;
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
CrossService.toString = () => '[class CrossService]';
|
||||
const crossService = new CrossService();
|
||||
|
||||
export {
|
||||
CrossService,
|
||||
crossService
|
||||
};
|
||||
23
electron/service/effect.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { logger } from 'ee-core/log';
|
||||
|
||||
// effect service
|
||||
class EffectService {
|
||||
|
||||
// hello
|
||||
async hello(args: any): Promise<{ status: string; params: any }> {
|
||||
let obj = {
|
||||
status:'ok',
|
||||
params: args
|
||||
}
|
||||
logger.info('EffectService obj:', obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
EffectService.toString = () => '[class EffectService]';
|
||||
const effectService = new EffectService();
|
||||
|
||||
export {
|
||||
EffectService,
|
||||
effectService
|
||||
}
|
||||
21
electron/service/example.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { logger } from 'ee-core/log';
|
||||
|
||||
// example service
|
||||
class ExampleService {
|
||||
|
||||
async test(args: any): Promise<{ status: string; params: any }> {
|
||||
let obj = {
|
||||
status:'ok',
|
||||
params: args
|
||||
}
|
||||
logger.info('ExampleService obj:', obj);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
ExampleService.toString = () => '[class ExampleService]';
|
||||
const exampleService = new ExampleService();
|
||||
|
||||
export {
|
||||
ExampleService,
|
||||
exampleService
|
||||
};
|
||||
19
electron/service/job/user.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { logger } from 'ee-core/log';
|
||||
|
||||
/**
|
||||
* UserService class
|
||||
*/
|
||||
class UserService {
|
||||
|
||||
async hello(args: any): Promise<{ status: string; params: any }> {
|
||||
const obj = {
|
||||
status: 'ok',
|
||||
params: args,
|
||||
};
|
||||
logger.info('UserService obj:', obj);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
UserService.toString = () => '[class UserService]';
|
||||
|
||||
export { UserService };
|
||||
177
electron/service/os/auto_updater.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { app as electronApp } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import { is } from 'ee-core/utils';
|
||||
import { logger } from 'ee-core/log';
|
||||
import { getMainWindow, setCloseAndQuit } from 'ee-core/electron';
|
||||
|
||||
/**
|
||||
* AutoUpdaterService class for automatic updates
|
||||
*/
|
||||
class AutoUpdaterService {
|
||||
private config: {
|
||||
windows: boolean;
|
||||
macOS: boolean;
|
||||
linux: boolean;
|
||||
options: any;
|
||||
};
|
||||
constructor() {
|
||||
this.config = {
|
||||
windows: false,
|
||||
macOS: false,
|
||||
linux: false,
|
||||
options: {
|
||||
provider: 'generic',
|
||||
url: 'http://kodo.qiniu.com/'
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and configure the auto updater
|
||||
*/
|
||||
create(): void {
|
||||
logger.info('[autoUpdater] load');
|
||||
const cfg = this.config;
|
||||
if ((is.windows() && cfg.windows) ||
|
||||
(is.macOS() && cfg.macOS) ||
|
||||
(is.linux() && cfg.linux)) {
|
||||
// continue
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const status = {
|
||||
error: -1,
|
||||
available: 1,
|
||||
noAvailable: 2,
|
||||
downloading: 3,
|
||||
downloaded: 4,
|
||||
};
|
||||
|
||||
const version = electronApp.getVersion();
|
||||
logger.info('[autoUpdater] current version: ', version);
|
||||
|
||||
// Set the download server address
|
||||
let server = cfg.options.url;
|
||||
const lastChar = server.substring(server.length - 1);
|
||||
server = lastChar === '/' ? server : server + "/";
|
||||
cfg.options.url = server;
|
||||
|
||||
try {
|
||||
autoUpdater.setFeedURL(cfg.options);
|
||||
} catch (error) {
|
||||
logger.error('[autoUpdater] setFeedURL error : ', error);
|
||||
}
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
// sendStatusToWindow('正在检查更新...');
|
||||
});
|
||||
autoUpdater.on('update-available', () => {
|
||||
const data = {
|
||||
status: status.available,
|
||||
desc: '有可用更新',
|
||||
};
|
||||
this.sendStatusToWindow(data);
|
||||
});
|
||||
autoUpdater.on('update-not-available', () => {
|
||||
const data = {
|
||||
status: status.noAvailable,
|
||||
desc: '没有可用更新',
|
||||
};
|
||||
this.sendStatusToWindow(data);
|
||||
});
|
||||
autoUpdater.on('error', (err) => {
|
||||
const data = {
|
||||
status: status.error,
|
||||
desc: err,
|
||||
};
|
||||
this.sendStatusToWindow(data);
|
||||
});
|
||||
autoUpdater.on('download-progress', (progressObj) => {
|
||||
const percentNumber = progressObj.percent;
|
||||
const totalSize = this.bytesChange(progressObj.total);
|
||||
const transferredSize = this.bytesChange(progressObj.transferred);
|
||||
let text = '已下载 ' + percentNumber + '%';
|
||||
text = text + ' (' + transferredSize + "/" + totalSize + ')';
|
||||
|
||||
const data = {
|
||||
status: status.downloading,
|
||||
desc: text,
|
||||
percentNumber,
|
||||
totalSize,
|
||||
transferredSize,
|
||||
};
|
||||
logger.info('[addon:autoUpdater] progress: ', text);
|
||||
this.sendStatusToWindow(data);
|
||||
});
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
const data = {
|
||||
status: status.downloaded,
|
||||
desc: '下载完成',
|
||||
};
|
||||
this.sendStatusToWindow(data);
|
||||
|
||||
// Allow the window to close
|
||||
setCloseAndQuit(true);
|
||||
|
||||
// Install updates and exit the application
|
||||
autoUpdater.quitAndInstall();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for updates
|
||||
*/
|
||||
checkUpdate(): void {
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Download updates
|
||||
*/
|
||||
download(): void {
|
||||
autoUpdater.downloadUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send status to the frontend
|
||||
*/
|
||||
sendStatusToWindow(content: any = {}): void {
|
||||
const textJson = JSON.stringify(content);
|
||||
const channel = 'custom/app/updater';
|
||||
const win = getMainWindow();
|
||||
win.webContents.send(channel, textJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bytes to a more readable format
|
||||
*/
|
||||
bytesChange(limit: number): string {
|
||||
let size = "";
|
||||
if (limit < 0.1 * 1024) {
|
||||
size = limit.toFixed(2) + "B";
|
||||
} else if (limit < 0.1 * 1024 * 1024) {
|
||||
size = (limit / 1024).toFixed(2) + "KB";
|
||||
} else if (limit < 0.1 * 1024 * 1024 * 1024) {
|
||||
size = (limit / (1024 * 1024)).toFixed(2) + "MB";
|
||||
} else {
|
||||
size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB";
|
||||
}
|
||||
|
||||
let sizeStr = size + "";
|
||||
let index = sizeStr.indexOf(".");
|
||||
let dou = sizeStr.substring(index + 1, index + 3);
|
||||
if (dou === "00") {
|
||||
return sizeStr.substring(0, index) + sizeStr.substring(index + 3, index + 5);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
}
|
||||
AutoUpdaterService.toString = () => '[class AutoUpdaterService]';
|
||||
const autoUpdaterService = new AutoUpdaterService();
|
||||
|
||||
export {
|
||||
AutoUpdaterService,
|
||||
autoUpdaterService
|
||||
};
|
||||
15
electron/service/os/icon.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const {getMainWindow} = require("ee-core/electron");
|
||||
|
||||
|
||||
class IconService {
|
||||
|
||||
|
||||
|
||||
update(iconPath) {
|
||||
const win = getMainWindow();
|
||||
win.setIcon(iconPath);
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
iconService: new IconService()
|
||||
};
|
||||
31
electron/service/os/security.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { logger } from 'ee-core/log';
|
||||
import { app as electronApp } from 'electron';
|
||||
|
||||
/**
|
||||
* SecurityService class for handling security-related operations
|
||||
*/
|
||||
class SecurityService {
|
||||
/**
|
||||
* Create and configure the security service
|
||||
*/
|
||||
create(): void {
|
||||
logger.info('[security] load');
|
||||
const runWithDebug = process.argv.find((e) => {
|
||||
const isHasDebug = e.includes('--inspect') || e.includes('--inspect-brk') || e.includes('--remote-debugging-port');
|
||||
return isHasDebug;
|
||||
});
|
||||
|
||||
// Do not allow remote debugging
|
||||
if (runWithDebug) {
|
||||
logger.error('[error] Remote debugging is not allowed, runWithDebug:', runWithDebug);
|
||||
electronApp.quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
SecurityService.toString = () => '[class SecurityService]';
|
||||
const securityService = new SecurityService();
|
||||
|
||||
export {
|
||||
SecurityService,
|
||||
securityService
|
||||
};
|
||||
81
electron/service/os/tray.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Tray, Menu } from 'electron';
|
||||
import path from 'path';
|
||||
import { isDev, getBaseDir } from 'ee-core/ps';
|
||||
import { logger } from 'ee-core/log';
|
||||
import { app as electronApp } from 'electron';
|
||||
import { getMainWindow, getCloseAndQuit, setCloseAndQuit } from 'ee-core/electron';
|
||||
|
||||
/**
|
||||
* 托盘
|
||||
* @class
|
||||
*/
|
||||
class TrayService {
|
||||
tray: Tray | null;
|
||||
config: {
|
||||
title: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.tray = null;
|
||||
this.config = {
|
||||
title: 'electron-egg',
|
||||
icon: '/public/images/tray.png',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the tray icon
|
||||
*/
|
||||
create () {
|
||||
logger.info('[tray] load');
|
||||
|
||||
const cfg = this.config;
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
// tray icon
|
||||
const iconPath = path.join(getBaseDir(), cfg.icon);
|
||||
|
||||
// Tray menu items
|
||||
const trayMenuTemplate = [
|
||||
{
|
||||
label: '显示',
|
||||
click: function () {
|
||||
mainWindow.show();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '退出',
|
||||
click: function () {
|
||||
electronApp.quit();
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// Set a flag to minimize to tray instead of closing
|
||||
setCloseAndQuit(false);
|
||||
mainWindow.on('close', (event: any) => {
|
||||
if (getCloseAndQuit()) {
|
||||
return;
|
||||
}
|
||||
mainWindow.hide();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Initialize the tray
|
||||
this.tray = new Tray(iconPath);
|
||||
this.tray.setToolTip(cfg.title);
|
||||
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
|
||||
this.tray.setContextMenu(contextMenu);
|
||||
// Show the main window when the tray icon is clicked
|
||||
this.tray.on('click', () => {
|
||||
mainWindow.show()
|
||||
})
|
||||
}
|
||||
}
|
||||
TrayService.toString = () => '[class TrayService]';
|
||||
const trayService = new TrayService();
|
||||
|
||||
export {
|
||||
trayService
|
||||
}
|
||||
131
electron/service/os/window.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import path from 'path';
|
||||
import { BrowserWindow, Notification } from 'electron';
|
||||
import { getMainWindow } from 'ee-core/electron';
|
||||
import { isProd, getBaseDir } from 'ee-core/ps';
|
||||
import { getConfig } from 'ee-core/config';
|
||||
import { isFileProtocol } from 'ee-core/utils';
|
||||
import { logger } from 'ee-core/log';
|
||||
|
||||
/**
|
||||
* Window
|
||||
* @class
|
||||
*/
|
||||
class WindowService {
|
||||
myNotification: Notification | null;
|
||||
windows: { [key: string]: BrowserWindow };
|
||||
|
||||
constructor() {
|
||||
this.myNotification = null;
|
||||
this.windows = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new window
|
||||
*/
|
||||
createWindow(args: { type: string; content: string; windowName: string; windowTitle: string }): number {
|
||||
const { type, content, windowName, windowTitle } = args;
|
||||
let contentUrl: string = '';
|
||||
if (type == 'html') {
|
||||
contentUrl = path.join('file://', getBaseDir(), content)
|
||||
} else if (type == 'web') {
|
||||
contentUrl = content;
|
||||
} else if (type == 'vue') {
|
||||
let addr = 'http://localhost:8080'
|
||||
if (isProd()) {
|
||||
const { mainServer } = getConfig();
|
||||
if (mainServer && mainServer.protocol && isFileProtocol(mainServer.protocol)) {
|
||||
addr = mainServer.protocol + path.join(getBaseDir(), mainServer.indexPath);
|
||||
}
|
||||
}
|
||||
|
||||
contentUrl = addr + content;
|
||||
}
|
||||
|
||||
logger.info('[createWindow] url: ', contentUrl);
|
||||
const opt = {
|
||||
title: windowTitle,
|
||||
x: 10,
|
||||
y: 10,
|
||||
width: 980,
|
||||
height: 650,
|
||||
webPreferences: {
|
||||
contextIsolation: false,
|
||||
nodeIntegration: true,
|
||||
},
|
||||
}
|
||||
const win = new BrowserWindow(opt);
|
||||
const winContentsId = win.webContents.id;
|
||||
win.loadURL(contentUrl);
|
||||
win.webContents.openDevTools();
|
||||
this.windows[windowName] = win;
|
||||
|
||||
return winContentsId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get window contents id
|
||||
*/
|
||||
getWCid(args: { windowName: string }): number {
|
||||
const { windowName } = args;
|
||||
let win: BrowserWindow;
|
||||
if (windowName == 'main') {
|
||||
win = getMainWindow();
|
||||
} else {
|
||||
win = this.windows[windowName];
|
||||
}
|
||||
|
||||
return win.webContents.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Realize communication between two windows through the transfer of the main process
|
||||
*/
|
||||
communicate(args: { receiver: string; content: any }): void {
|
||||
const { receiver, content } = args;
|
||||
if (receiver == 'main') {
|
||||
const win = getMainWindow();
|
||||
win.webContents.send('controller/os/window2ToWindow1', content);
|
||||
} else if (receiver == 'window2') {
|
||||
const win = this.windows[receiver];
|
||||
win.webContents.send('controller/os/window1ToWindow2', content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* createNotification
|
||||
*/
|
||||
createNotification(options: any, event: any): void {
|
||||
const channel = 'controller/os/sendNotification';
|
||||
this.myNotification = new Notification(options);
|
||||
|
||||
if (options.clickEvent) {
|
||||
this.myNotification.on('click', () => {
|
||||
let data = {
|
||||
type: 'click',
|
||||
msg: '您点击了通知消息'
|
||||
}
|
||||
event.reply(`${channel}`, data)
|
||||
});
|
||||
}
|
||||
|
||||
if (options.closeEvent) {
|
||||
this.myNotification.on('close', () => {
|
||||
let data = {
|
||||
type: 'close',
|
||||
msg: '您关闭了通知消息'
|
||||
}
|
||||
event.reply(`${channel}`, data)
|
||||
});
|
||||
}
|
||||
|
||||
this.myNotification.show();
|
||||
}
|
||||
|
||||
}
|
||||
WindowService.toString = () => '[class WindowService]';
|
||||
const windowService = new WindowService();
|
||||
|
||||
export {
|
||||
WindowService,
|
||||
windowService
|
||||
}
|
||||
14
frontend/.editorconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
# https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
||||
4
frontend/.env.development
Normal file
@@ -0,0 +1,4 @@
|
||||
VITE_TITLE="NPQS9100-自动检测平台工具箱"
|
||||
|
||||
VITE_RSA_PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFMmIVanMxsW5S/qP8Wcxf/J3/i4631BP3UtWkRzO7jAw9HIAgK4Y7X53hXj6zMbfme1vMjQc0mq7m/KrH4WlTYpFexLO6Gnk8oH40F04tp+ABZIq93zNOydPEaVoZeTPH/LlkwrrxVGAMNNIKuebcqapp25JiWtlSFMv4kH/nDAj+2m8+P4zYVM1Ed6gO01eKDEYE3SBA1Ket2BfHTgviR/F8WKwlXh11enywsJnrHTM5dJQdlUxCjHy214TpheYOz/cv9elQnDfFAbmZW8mH5/hgMSTkm3h4uR7ITin6Erg+yc/t1kGaTWrzloyBRMSiFN/Pwr5yQjj+1wQqqUkwIDAQAB"
|
||||
VITE_RSA_PRIVATE_KEY="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcUyYhVqczGxblL+o/xZzF/8nf+LjrfUE/dS1aRHM7uMDD0cgCArhjtfneFePrMxt+Z7W8yNBzSarub8qsfhaVNikV7Es7oaeTygfjQXTi2n4AFkir3fM07J08RpWhl5M8f8uWTCuvFUYAw00gq55typqmnbkmJa2VIUy/iQf+cMCP7abz4/jNhUzUR3qA7TV4oMRgTdIEDUp63YF8dOC+JH8XxYrCVeHXV6fLCwmesdMzl0lB2VTEKMfLbXhOmF5g7P9y/16VCcN8UBuZlbyYfn+GAxJOSbeHi5HshOKfoSuD7Jz+3WQZpNavOWjIFExKIU38/CvnJCOP7XBCqpSTAgMBAAECggEAYeWokWRE3TpvwiOZnUpR/aVMdVi75a3ROL5XIpqPV61B+t/bU3cEpl0GF9C5pUeiRi0IoStZb3mI9D1KPW/REKyUWkhabQO1gFYbTnRlkNOn6MILzKX4cwJjDaZeeo4EBPU7N+qHyOOXrU6hdH5FfxhMdV983ajm5eeuupxER1C2kAcIklTeVpTX6EKOgZb5LBp5ssOVm2P42pOauvcRozRcvZmqnErXmukv0H4l3EVNt4rHpTn9riHUC63e8JfiYzVaF6zuNUxv6nHEft0/SRMw11XSTnNfDzcKqgjz6ksFBS/6eQQYKESk+ONC53HUuYHFAknkwsPupDCT2W8FIQKBgQDLHT/xCU3nxGr4vFKBDNaO2D5oK20ECbBO4oDvLWWmQG7f+6TsMy8PgVdMnoL4RfqGlwFAKEpS6KVFHnBVqnNEhcdy9uCI7x7Xx8UnyUtxj1EDTm76uta9Ki9OrlqB6tImDM9+Ya3vGktW37ht4WOx2OsJRhG1dbf6RLwFlH7DWwKBgQDFBxvi5I1BR6hg6Tj7xd2SqOT2Y+BED3xuSYENhWbmMhLJDResaB7mjztbxlYaY2mOE0holWm2uDmVFFhMh4jYXik4hYH8nmDzq9mDpZCZ9pyjYqnAP8THoAa8EbgrUWB8A6BPH4iL3KbMnBfBKY0pIr2xrvnjQjNBAgta7KDRKQKBgCe6oe4wxrdF2TKsC2tIqpMoQxS3Icy/ZGgZr+SYuaBKTCWtoDW/UT40K3JGMxIDBhzbXphBCUCsVt9tM8Xd4EwP6tJW7dZ7B0pnve2pVwNwaAVAiz6p2yUHIle+jN+Koe5lZRSwYIg7WW81tWpwwsJfzqFyvjYDP6hJV4mz4ROvAoGAaRcdnKvjXApomShMqJ4lTPChD3q+SA8qg3jZSOj6tZXHx00gb2kp8jg7pPvpOTIFPy6x1Ha9aCRjMk0ju84fA6lVuzwa1S907wOehUVuF3Eeo1cgy9Y3k3KbpPyeixxgpkUY4JslLdSHc2NemD0dee951qhJyRmqVOZOQDUuoeECgYEAqBw2cAFk3vM97WY06TSldGA8ajVHx3BYRjj+zl62NTQthy8fw3tqxb3c5e8toOmZWKjZvDhg2TRLhsDDQWEYg3LZG87REqVIjgEPcpjNLidjygGX8n3JF2o0O5I/EMvl0s/+LVQONfduOBvhwDqr8QNisbLsyneiAq7umewMolo="
|
||||
3
frontend/.env.production
Normal file
@@ -0,0 +1,3 @@
|
||||
VITE_TITLE="NPQS9100-自动检测平台工具箱"
|
||||
VITE_RSA_PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFMmIVanMxsW5S/qP8Wcxf/J3/i4631BP3UtWkRzO7jAw9HIAgK4Y7X53hXj6zMbfme1vMjQc0mq7m/KrH4WlTYpFexLO6Gnk8oH40F04tp+ABZIq93zNOydPEaVoZeTPH/LlkwrrxVGAMNNIKuebcqapp25JiWtlSFMv4kH/nDAj+2m8+P4zYVM1Ed6gO01eKDEYE3SBA1Ket2BfHTgviR/F8WKwlXh11enywsJnrHTM5dJQdlUxCjHy214TpheYOz/cv9elQnDfFAbmZW8mH5/hgMSTkm3h4uR7ITin6Erg+yc/t1kGaTWrzloyBRMSiFN/Pwr5yQjj+1wQqqUkwIDAQAB"
|
||||
VITE_RSA_PRIVATE_KEY="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcUyYhVqczGxblL+o/xZzF/8nf+LjrfUE/dS1aRHM7uMDD0cgCArhjtfneFePrMxt+Z7W8yNBzSarub8qsfhaVNikV7Es7oaeTygfjQXTi2n4AFkir3fM07J08RpWhl5M8f8uWTCuvFUYAw00gq55typqmnbkmJa2VIUy/iQf+cMCP7abz4/jNhUzUR3qA7TV4oMRgTdIEDUp63YF8dOC+JH8XxYrCVeHXV6fLCwmesdMzl0lB2VTEKMfLbXhOmF5g7P9y/16VCcN8UBuZlbyYfn+GAxJOSbeHi5HshOKfoSuD7Jz+3WQZpNavOWjIFExKIU38/CvnJCOP7XBCqpSTAgMBAAECggEAYeWokWRE3TpvwiOZnUpR/aVMdVi75a3ROL5XIpqPV61B+t/bU3cEpl0GF9C5pUeiRi0IoStZb3mI9D1KPW/REKyUWkhabQO1gFYbTnRlkNOn6MILzKX4cwJjDaZeeo4EBPU7N+qHyOOXrU6hdH5FfxhMdV983ajm5eeuupxER1C2kAcIklTeVpTX6EKOgZb5LBp5ssOVm2P42pOauvcRozRcvZmqnErXmukv0H4l3EVNt4rHpTn9riHUC63e8JfiYzVaF6zuNUxv6nHEft0/SRMw11XSTnNfDzcKqgjz6ksFBS/6eQQYKESk+ONC53HUuYHFAknkwsPupDCT2W8FIQKBgQDLHT/xCU3nxGr4vFKBDNaO2D5oK20ECbBO4oDvLWWmQG7f+6TsMy8PgVdMnoL4RfqGlwFAKEpS6KVFHnBVqnNEhcdy9uCI7x7Xx8UnyUtxj1EDTm76uta9Ki9OrlqB6tImDM9+Ya3vGktW37ht4WOx2OsJRhG1dbf6RLwFlH7DWwKBgQDFBxvi5I1BR6hg6Tj7xd2SqOT2Y+BED3xuSYENhWbmMhLJDResaB7mjztbxlYaY2mOE0holWm2uDmVFFhMh4jYXik4hYH8nmDzq9mDpZCZ9pyjYqnAP8THoAa8EbgrUWB8A6BPH4iL3KbMnBfBKY0pIr2xrvnjQjNBAgta7KDRKQKBgCe6oe4wxrdF2TKsC2tIqpMoQxS3Icy/ZGgZr+SYuaBKTCWtoDW/UT40K3JGMxIDBhzbXphBCUCsVt9tM8Xd4EwP6tJW7dZ7B0pnve2pVwNwaAVAiz6p2yUHIle+jN+Koe5lZRSwYIg7WW81tWpwwsJfzqFyvjYDP6hJV4mz4ROvAoGAaRcdnKvjXApomShMqJ4lTPChD3q+SA8qg3jZSOj6tZXHx00gb2kp8jg7pPvpOTIFPy6x1Ha9aCRjMk0ju84fA6lVuzwa1S907wOehUVuF3Eeo1cgy9Y3k3KbpPyeixxgpkUY4JslLdSHc2NemD0dee951qhJyRmqVOZOQDUuoeECgYEAqBw2cAFk3vM97WY06TSldGA8ajVHx3BYRjj+zl62NTQthy8fw3tqxb3c5e8toOmZWKjZvDhg2TRLhsDDQWEYg3LZG87REqVIjgEPcpjNLidjygGX8n3JF2o0O5I/EMvl0s/+LVQONfduOBvhwDqr8QNisbLsyneiAq7umewMolo="
|
||||
6
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
package-lock.json
|
||||
105
frontend/index.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" />
|
||||
<title></title>
|
||||
<!-- 优化:vue渲染未完成之前,先加一个css动画 -->
|
||||
<style>
|
||||
#loadingPage {
|
||||
background-color: #dedede;
|
||||
font-size: 12px;
|
||||
}
|
||||
.base {
|
||||
height: 9em;
|
||||
left: 50%;
|
||||
margin: -7.5em;
|
||||
padding: 3em;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 9em;
|
||||
transform: rotateX(45deg) rotateZ(45deg);
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
.cube,
|
||||
.cube:after,
|
||||
.cube:before {
|
||||
content: '';
|
||||
float: left;
|
||||
height: 3em;
|
||||
position: absolute;
|
||||
width: 3em;
|
||||
}
|
||||
/* Top */
|
||||
.cube {
|
||||
background-color: #06cf68;
|
||||
position: relative;
|
||||
transform: translateZ(3em);
|
||||
transform-style: preserve-3d;
|
||||
transition: .25s;
|
||||
box-shadow: 13em 13em 1.5em rgba(0, 0, 0, 0.1);
|
||||
animation: anim 1s infinite;
|
||||
}
|
||||
.cube:after {
|
||||
background-color: #05a151;
|
||||
transform: rotateX(-90deg) translateY(3em);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
.cube:before {
|
||||
background-color: #026934;
|
||||
transform: rotateY(90deg) translateX(3em);
|
||||
transform-origin: 100% 0;
|
||||
}
|
||||
.cube:nth-child(1) {
|
||||
animation-delay: 0.05s;
|
||||
}
|
||||
.cube:nth-child(2) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
.cube:nth-child(3) {
|
||||
animation-delay: 0.15s;
|
||||
}
|
||||
.cube:nth-child(4) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
.cube:nth-child(5) {
|
||||
animation-delay: 0.25s;
|
||||
}
|
||||
.cube:nth-child(6) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
.cube:nth-child(7) {
|
||||
animation-delay: 0.35s;
|
||||
}
|
||||
.cube:nth-child(8) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
.cube:nth-child(9) {
|
||||
animation-delay: 0.45s;
|
||||
}
|
||||
@keyframes anim {
|
||||
50% {
|
||||
transform: translateZ(0.5em);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="loadingPage">
|
||||
<div class='base'>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
<div class='cube'></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
38
frontend/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "ee",
|
||||
"version": "4.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite --host --port 8080",
|
||||
"serve": "vite --host --port 8080",
|
||||
"build-staging": "vite build --mode staging",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"ant-design-vue": "^3.2.20",
|
||||
"axios": "^1.12.2",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"node-forge": "^1.3.1",
|
||||
"pinia": "^3.0.3",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"store2": "^2.14.4",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.6.2",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.16.0",
|
||||
"@types/node-forge": "^1.3.14",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/compiler-sfc": "^3.5.22",
|
||||
"less": "^4.4.2",
|
||||
"less-loader": "^12.3.0",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-pxtorem": "^6.1.0",
|
||||
"terser": "^5.44.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^6.4.0",
|
||||
"vite-plugin-compression": "^0.5.1"
|
||||
}
|
||||
}
|
||||
15
frontend/src/App.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<router-view/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {onMounted} from 'vue';
|
||||
|
||||
onMounted(() => {
|
||||
const loadingElement = document.getElementById('loadingPage');
|
||||
if (loadingElement) {
|
||||
(loadingElement as HTMLElement).remove();
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
84
frontend/src/api/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
/**
|
||||
* Definition of communication channel between main process and rendering process
|
||||
* format:controller/filename/method
|
||||
* Definition of communication channels between main process and rendering process
|
||||
*/
|
||||
const ipcApiRoute = {
|
||||
example: {
|
||||
test: 'controller/example/test',
|
||||
},
|
||||
framework: {
|
||||
checkForUpdater: 'controller/framework/checkForUpdater',
|
||||
downloadApp: 'controller/framework/downloadApp',
|
||||
jsondbOperation: 'controller/framework/jsondbOperation',
|
||||
sqlitedbOperation: 'controller/framework/sqlitedbOperation',
|
||||
uploadFile: 'controller/framework/uploadFile',
|
||||
checkHttpServer: 'controller/framework/checkHttpServer',
|
||||
doHttpRequest: 'controller/framework/doHttpRequest',
|
||||
doSocketRequest: 'controller/framework/doSocketRequest',
|
||||
ipcInvokeMsg: 'controller/framework/ipcInvokeMsg',
|
||||
ipcSendSyncMsg: 'controller/framework/ipcSendSyncMsg',
|
||||
ipcSendMsg: 'controller/framework/ipcSendMsg',
|
||||
startJavaServer: 'controller/framework/startJavaServer',
|
||||
closeJavaServer: 'controller/framework/closeJavaServer',
|
||||
someJob: 'controller/framework/someJob',
|
||||
timerJobProgress: 'controller/framework/timerJobProgress',
|
||||
createPool: 'controller/framework/createPool',
|
||||
createPoolNotice: 'controller/framework/createPoolNotice',
|
||||
someJobByPool: 'controller/framework/someJobByPool',
|
||||
hello: 'controller/framework/hello',
|
||||
openSoftware: 'controller/framework/openSoftware',
|
||||
},
|
||||
|
||||
// os
|
||||
os: {
|
||||
messageShow: 'controller/os/messageShow',
|
||||
messageShowConfirm: 'controller/os/messageShowConfirm',
|
||||
selectFolder: 'controller/os/selectFolder',
|
||||
selectPic: 'controller/os/selectPic',
|
||||
openDirectory: 'controller/os/openDirectory',
|
||||
loadViewContent: 'controller/os/loadViewContent',
|
||||
removeViewContent: 'controller/os/removeViewContent',
|
||||
createWindow: 'controller/os/createWindow',
|
||||
getWCid: 'controller/os/getWCid',
|
||||
sendNotification: 'controller/os/sendNotification',
|
||||
initPowerMonitor: 'controller/os/initPowerMonitor',
|
||||
getScreen: 'controller/os/getScreen',
|
||||
autoLaunch: 'controller/os/autoLaunch',
|
||||
setTheme: 'controller/os/setTheme',
|
||||
getTheme: 'controller/os/getTheme',
|
||||
window1ToWindow2: 'controller/os/window1ToWindow2',
|
||||
window2ToWindow1: 'controller/os/window2ToWindow1',
|
||||
},
|
||||
|
||||
// effect
|
||||
effect: {
|
||||
selectFile: 'controller/effect/selectFile',
|
||||
loginWindow: 'controller/effect/loginWindow',
|
||||
restoreWindow: 'controller/effect/restoreWindow',
|
||||
},
|
||||
|
||||
// cross
|
||||
cross: {
|
||||
crossInfo: 'controller/cross/info',
|
||||
getCrossUrl: 'controller/cross/getUrl',
|
||||
killCrossServer: 'controller/cross/killServer',
|
||||
createCrossServer: 'controller/cross/createServer',
|
||||
requestApi: 'controller/cross/requestApi',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize Channel
|
||||
* Format: Custom (recommended to add a prefix)
|
||||
*/
|
||||
const specialIpcRoute = {
|
||||
appUpdater: 'custom/app/updater', // updater channel
|
||||
}
|
||||
|
||||
export {
|
||||
ipcApiRoute,
|
||||
specialIpcRoute
|
||||
}
|
||||
|
||||
15
frontend/src/assets/global.less
Normal file
@@ -0,0 +1,15 @@
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
/* 滚动条 */
|
||||
::-webkit-scrollbar{width:8px;height:4px}
|
||||
::-webkit-scrollbar-button{width:10px;height:0}
|
||||
::-webkit-scrollbar-track{background:0 0}
|
||||
::-webkit-scrollbar-thumb{background: #ecf3fb; border-radius: 4px;-webkit-transition:.3s;transition:.3s}
|
||||
::-webkit-scrollbar-thumb:hover{background-color:#1890ff}
|
||||
::-webkit-scrollbar-thumb:active{background-color:#1890ff}
|
||||
BIN
frontend/src/assets/login.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
frontend/src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
17
frontend/src/assets/theme.less
Normal file
@@ -0,0 +1,17 @@
|
||||
@import 'ant-design-vue/dist/antd.less';
|
||||
|
||||
// 可自定义主题颜色
|
||||
//@primary-color: #07C160; // 全局主色
|
||||
@link-color: #1890ff; // 链接色
|
||||
@success-color: #52c41a; // 成功色
|
||||
@warning-color: #faad14; // 警告色
|
||||
@error-color: #f5222d; // 错误色
|
||||
@font-size-base: 14px; // 主字号
|
||||
@heading-color: rgba(0, 0, 0, 0.85); // 标题色
|
||||
@text-color: rgba(0, 0, 0, 0.65); // 主文本色
|
||||
@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
|
||||
@disabled-color: rgba(0, 0, 0, 0.25); // 失效色
|
||||
@border-radius-base: 4px; // 组件/浮层圆角
|
||||
@border-color-base: #dce3e8; // 边框色
|
||||
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // 浮层阴影
|
||||
|
||||
22
frontend/src/components/global/iconFont.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createFromIconfontCN } from '@ant-design/icons-vue'
|
||||
import { h, VNode } from 'vue'
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: 'https://at.alicdn.com/t/font_2456157_4ovzopz659q.js',
|
||||
extraCommonProps: {
|
||||
type: 'icon-fengche',
|
||||
style: {
|
||||
fontSize: '18px',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
interface Props {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const DynamicIconFont = (props: Props): VNode => {
|
||||
return h(IconFont, { type: props.type || 'icon-fengche' })
|
||||
}
|
||||
|
||||
export default DynamicIconFont
|
||||
19
frontend/src/components/global/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import iconFont from './iconFont';
|
||||
|
||||
// Use import.meta.globEager to dynamically import all .vue files in the directory
|
||||
const modules: { [key: string]: { default: any } } = import.meta.glob('./*.vue', { eager: true });
|
||||
|
||||
// Create a map of component names to their default exports
|
||||
const map: { [key: string]: any } = {};
|
||||
Object.keys(modules).forEach(file => {
|
||||
const moduleName = file.replace('./', '').replace('.vue', '');
|
||||
map[moduleName] = modules[file].default;
|
||||
});
|
||||
|
||||
// Combine the dynamically imported components with the iconFont component
|
||||
const globalComponents = {
|
||||
...map,
|
||||
iconFont,
|
||||
};
|
||||
|
||||
export default globalComponents;
|
||||
112
frontend/src/layouts/AppSider.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<a-layout has-sider id="app-layout-sider">
|
||||
<a-layout-sider
|
||||
v-model:collapsed="collapsed" collapsible
|
||||
theme="light"
|
||||
class="layout-sider"
|
||||
>
|
||||
<div class="logo">
|
||||
<img src="@/assets/logo.png" class="pic-logo" />
|
||||
<h4>PQS9100工具箱</h4>
|
||||
</div>
|
||||
<a-menu
|
||||
theme="light"
|
||||
mode="inline"
|
||||
:selectedKeys="[current]"
|
||||
@click="menuHandle"
|
||||
>
|
||||
<a-menu-item v-for="(menuInfo, index) in menu" :key="index">
|
||||
<component :is="menuInfo.icon"></component>
|
||||
<span>{{ menuInfo.title }}</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-layout-sider>
|
||||
<a-layout :style="{ marginLeft: '200px' }">
|
||||
<a-layout-content class="layout-content">
|
||||
<router-view />
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {onMounted, ref} from 'vue';
|
||||
import {useRouter} from 'vue-router';
|
||||
import {FileProtectOutlined} from "@ant-design/icons-vue"; // 定义菜单项的类型
|
||||
|
||||
// 定义菜单项的类型
|
||||
interface MenuItem {
|
||||
icon: any;
|
||||
title: string;
|
||||
pageName: string;
|
||||
}
|
||||
|
||||
// 定义菜单的类型
|
||||
interface Menu {
|
||||
[key: string]: MenuItem;
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const collapsed = ref<boolean>(false);
|
||||
const current = ref<string>('menu_1');
|
||||
const menu = ref<Menu>({
|
||||
'menu_1': {
|
||||
icon: FileProtectOutlined,
|
||||
title: '设备激活',
|
||||
pageName: 'Activate'
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
menuHandle(null);
|
||||
});
|
||||
|
||||
const menuHandle = (e: any): void => {
|
||||
console.log('sider menu e:', e);
|
||||
if (e) {
|
||||
current.value = e.key;
|
||||
}
|
||||
console.log('sider menu current:', current.value);
|
||||
|
||||
const linkInfo = menu.value[current.value];
|
||||
console.log('[home] load linkInfo:', linkInfo);
|
||||
router.push({ name: linkInfo.pageName});
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
// 嵌套
|
||||
#app-layout-sider {
|
||||
height: 100%;
|
||||
.logo {
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
padding:10px;
|
||||
user-select: none;
|
||||
}
|
||||
.pic-logo {
|
||||
height: 32px;
|
||||
margin: 10px;
|
||||
}
|
||||
.layout-sider {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
overflow: auto;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.menu-item {
|
||||
.ant-menu-item {
|
||||
background-color: #fff;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
.layout-content {
|
||||
width: 96%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
5
frontend/src/layouts/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import AppSider from '@/layouts/AppSider.vue'
|
||||
|
||||
export {
|
||||
AppSider
|
||||
}
|
||||
30
frontend/src/main.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import * as AntIcon from '@ant-design/icons-vue';
|
||||
import Antd from 'ant-design-vue';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import './assets/global.less';
|
||||
import './assets/theme.less';
|
||||
import components from './components/global';
|
||||
import Router from './router/index';
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
// components
|
||||
type ComponentsType = typeof components;
|
||||
for (const componentName in components) {
|
||||
if (Object.prototype.hasOwnProperty.call(components, componentName)) {
|
||||
const component = components[componentName as keyof ComponentsType];
|
||||
app.component(componentName, component);
|
||||
}
|
||||
}
|
||||
|
||||
// icon
|
||||
const whiteList = ['createFromIconfontCN', 'getTwoToneColor', 'setTwoToneColor', 'default']
|
||||
const iconKeys = Object.keys(AntIcon) as Array<keyof typeof AntIcon>;
|
||||
iconKeys.forEach(key => {
|
||||
if (!whiteList.includes(key as typeof whiteList[number])) {
|
||||
app.component(key.toString(), AntIcon[key]);
|
||||
}
|
||||
});
|
||||
|
||||
app.use(Antd).use(Router).mount('#app')
|
||||
9
frontend/src/router/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
|
||||
import routerMap from './routerMap'
|
||||
|
||||
const Router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: routerMap as RouteRecordRaw[],
|
||||
})
|
||||
|
||||
export default Router
|
||||
21
frontend/src/router/routerMap.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 基础路由
|
||||
* @type { *[] }
|
||||
*/
|
||||
|
||||
const constantRouterMap = [
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('@/layouts/AppSider.vue'),
|
||||
children: [
|
||||
{
|
||||
path: '/activate',
|
||||
name: 'Activate',
|
||||
component: () => import('@/views/activate/index.vue'),
|
||||
props: { id: 'activate' }
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
export default constantRouterMap
|
||||
12
frontend/src/types/env.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/// <reference types="vite/client" />
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
// declare global {
|
||||
// interface Window {
|
||||
// electron?: any;
|
||||
// }
|
||||
// }
|
||||
0
frontend/src/types/pinia.d.ts
vendored
Normal file
0
frontend/src/types/shim.d.ts
vendored
Normal file
7
frontend/src/types/source.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// 声明一个模块,防止引入文件时报错
|
||||
declare module '*.json';
|
||||
declare module '*.png';
|
||||
declare module '*.jpg';
|
||||
declare module '*.scss';
|
||||
declare module '*.ts';
|
||||
declare module '*.js';
|
||||
27
frontend/src/utils/iconList.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export default [
|
||||
{ name: '对话框', type: 'icon-duihuakuang' },
|
||||
{ name: '闹钟', type: 'icon-naozhong' },
|
||||
{ name: '笑脸', type: 'icon-xiaolian' },
|
||||
{ name: 'ok', type: 'icon-ok' },
|
||||
{ name: '风车', type: 'icon-fengche' },
|
||||
{ name: '汗颜', type: 'icon-hanyan' },
|
||||
{ name: '相机', type: 'icon-xiangji' },
|
||||
{ name: '礼物', type: 'icon-liwu' },
|
||||
{ name: '礼花', type: 'icon-lihua' },
|
||||
{ name: '扭蛋', type: 'icon-niudan' },
|
||||
{ name: '流星', type: 'icon-liuxing' },
|
||||
{ name: '风筝', type: 'icon-fengzheng' },
|
||||
{ name: '蛋糕', type: 'icon-dangao' },
|
||||
{ name: '泡泡', type: 'icon-paopao' },
|
||||
{ name: '购物', type: 'icon-gouwu' },
|
||||
{ name: '饮料', type: 'icon-yinliao' },
|
||||
{ name: '云彩', type: 'icon-yuncai' },
|
||||
{ name: '彩铅', type: 'icon-caiqian' },
|
||||
{ name: '纸飞机', type: 'icon-zhifeiji' },
|
||||
{ name: '点赞', type: 'icon-dianzan' },
|
||||
{ name: '煎蛋', type: 'icon-jiandan' },
|
||||
{ name: '小熊', type: 'icon-xiaoxiong' },
|
||||
{ name: '花', type: 'icon-hua' },
|
||||
{ name: '眼睛', type: 'icon-yanjing' },
|
||||
]
|
||||
|
||||
39
frontend/src/utils/ipcRenderer.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
electron?: any;
|
||||
}
|
||||
}
|
||||
|
||||
const Renderer = (window.require && window.require('electron')) || window.electron || {};
|
||||
|
||||
/**
|
||||
* ipc
|
||||
* 官方api说明:https://www.electronjs.org/zh/docs/latest/api/ipc-renderer
|
||||
*
|
||||
* 属性/方法
|
||||
* ipc.invoke(channel, param) - 发送异步消息(invoke/handle 模型)
|
||||
* ipc.sendSync(channel, param) - 发送同步消息(send/on 模型)
|
||||
* ipc.on(channel, listener) - 监听 channel, 当新消息到达,调用 listener
|
||||
* ipc.once(channel, listener) - 添加一次性 listener 函数
|
||||
* ipc.removeListener(channel, listener) - 为特定的 channel 从监听队列中删除特定的 listener 监听者
|
||||
* ipc.removeAllListeners(channel) - 移除所有的监听器,当指定 channel 时只移除与其相关的所有监听器
|
||||
* ipc.send(channel, ...args) - 通过channel向主进程发送异步消息
|
||||
* ipc.postMessage(channel, message, [transfer]) - 发送消息到主进程
|
||||
* ipc.sendTo(webContentsId, channel, ...args) - 通过 channel 发送消息到带有 webContentsId 的窗口
|
||||
* ipc.sendToHost(channel, ...args) - 消息会被发送到 host 页面上的 <webview> 元素
|
||||
*/
|
||||
|
||||
/**
|
||||
* ipc
|
||||
*/
|
||||
const ipc = Renderer.ipcRenderer || undefined;
|
||||
|
||||
/**
|
||||
* 是否为EE环境
|
||||
*/
|
||||
const isEE = ipc ? true : false;
|
||||
|
||||
export {
|
||||
Renderer, ipc, isEE
|
||||
};
|
||||
|
||||
47
frontend/src/utils/rsa.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import JSEncrypt from 'jsencrypt'
|
||||
|
||||
// 获取 RSA 公钥
|
||||
const publicKey = import.meta.env.VITE_RSA_PUBLIC_KEY
|
||||
|
||||
// 获取 RSA 私钥
|
||||
const privateKey = import.meta.env.VITE_RSA_PRIVATE_KEY
|
||||
|
||||
// RSA加密
|
||||
const encrypt = (data: string): string => {
|
||||
try {
|
||||
const encrypt = new JSEncrypt()
|
||||
encrypt.setPublicKey(publicKey)
|
||||
const encrypted = encrypt.encrypt(data)
|
||||
if (encrypted) {
|
||||
return encrypted
|
||||
} else {
|
||||
throw new Error('加密失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加密失败:', error)
|
||||
throw new Error('RSA加密失败')
|
||||
}
|
||||
}
|
||||
|
||||
// RSA解密
|
||||
const decrypt = (encryptedData: string): string => {
|
||||
try {
|
||||
const decrypt = new JSEncrypt()
|
||||
decrypt.setPrivateKey(privateKey)
|
||||
const decrypted = decrypt.decrypt(encryptedData)
|
||||
if (decrypted) {
|
||||
return decrypted as string
|
||||
} else {
|
||||
throw new Error('解密失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解密失败:', error)
|
||||
throw new Error('RSA解密失败')
|
||||
}
|
||||
}
|
||||
export default {
|
||||
encrypt,
|
||||
decrypt,
|
||||
publicKey,
|
||||
privateKey
|
||||
}
|
||||
56
frontend/src/views/activate/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
export namespace Activate {
|
||||
|
||||
export interface ApplicationModule {
|
||||
/**
|
||||
* 是否申请 1是 0否
|
||||
*/
|
||||
apply: number;
|
||||
|
||||
}
|
||||
|
||||
export interface ActivateModule extends ApplicationModule {
|
||||
/**
|
||||
* 是否永久 1是 0否
|
||||
*/
|
||||
permanently: number;
|
||||
}
|
||||
|
||||
export interface ApplicationCodePlaintext {
|
||||
|
||||
/**
|
||||
* 模拟式模块
|
||||
*/
|
||||
simulate: ApplicationModule;
|
||||
|
||||
/**
|
||||
* 数字式模块
|
||||
*/
|
||||
digital: ApplicationModule;
|
||||
|
||||
/**
|
||||
* 比对式模块
|
||||
*/
|
||||
contrast: ApplicationModule;
|
||||
}
|
||||
export interface ActivationCodePlaintext {
|
||||
|
||||
/**
|
||||
* 模拟式模块
|
||||
*/
|
||||
simulate: ActivateModule;
|
||||
|
||||
/**
|
||||
* 数字式模块
|
||||
*/
|
||||
digital: ActivateModule;
|
||||
|
||||
/**
|
||||
* 比对式模块
|
||||
*/
|
||||
contrast: ActivateModule;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
205
frontend/src/views/activate/index.vue
Normal file
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div class="activation-page">
|
||||
<a-card title="RSA密钥配置" style="margin-bottom: 20px;">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="24">
|
||||
<a-alert
|
||||
message="注意:请妥善保管私钥,不要泄露给他人"
|
||||
type="warning"
|
||||
show-icon
|
||||
style="margin-bottom: 16px;"
|
||||
/>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="RSA公钥">
|
||||
<a-textarea
|
||||
v-model:value="rsaKeys.publicKey"
|
||||
:rows="5"
|
||||
readonly
|
||||
placeholder="RSA公钥内容"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
ghost
|
||||
size="small"
|
||||
@click="copyToClipboard(rsaKeys.publicKey)"
|
||||
:disabled="!rsaKeys.publicKey"
|
||||
style="margin-top: 8px;"
|
||||
>
|
||||
复制公钥
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="RSA私钥">
|
||||
<a-textarea
|
||||
v-model:value="rsaKeys.privateKey"
|
||||
:rows="5"
|
||||
readonly
|
||||
placeholder="RSA私钥内容"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
ghost
|
||||
size="small"
|
||||
@click="copyToClipboard(rsaKeys.privateKey)"
|
||||
:disabled="!rsaKeys.privateKey"
|
||||
style="margin-top: 8px;"
|
||||
>
|
||||
复制私钥
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
||||
<a-card title="设备激活">
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="24">
|
||||
<a-divider orientation="left" orientation-margin="0px">设备申请码</a-divider>
|
||||
<a-form-item>
|
||||
<a-textarea
|
||||
v-model:value="activationForm.requestCode"
|
||||
:rows="3"
|
||||
placeholder="请输入设备申请码"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="24">
|
||||
<a-divider orientation="left" orientation-margin="0px">设备申请码</a-divider>
|
||||
<a-form-item>
|
||||
<a-textarea
|
||||
v-model:value="activationForm.activationCode"
|
||||
:rows="3"
|
||||
placeholder="生成的激活码将显示在这里"
|
||||
readonly
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="24">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="generateActivationCode"
|
||||
:loading="generating"
|
||||
:disabled="!activationForm.requestCode.trim()"
|
||||
>
|
||||
生成激活码
|
||||
</a-button>
|
||||
|
||||
<a-button
|
||||
@click="copyToClipboard(activationForm.activationCode)"
|
||||
:disabled="!activationForm.activationCode"
|
||||
>
|
||||
复制激活码
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {ref} from 'vue'
|
||||
import {message} from 'ant-design-vue'
|
||||
import rsa from '@/utils/rsa'
|
||||
import {Activate} from "@/views/activate/index";
|
||||
|
||||
const rsaKeys = ref({
|
||||
publicKey: rsa.publicKey,
|
||||
privateKey: rsa.privateKey
|
||||
})
|
||||
|
||||
const activationForm = ref({
|
||||
requestCode: '',
|
||||
activationCode: ''
|
||||
})
|
||||
|
||||
const generating = ref(false)
|
||||
|
||||
|
||||
// 生成激活码
|
||||
const generateActivationCode = () => {
|
||||
if (!activationForm.value.requestCode.trim()) {
|
||||
message.error('请输入设备申请码')
|
||||
return
|
||||
}
|
||||
generating.value = true
|
||||
let activationCodePlaintext
|
||||
try {
|
||||
const plaintext = rsa.decrypt(activationForm.value.requestCode)
|
||||
activationCodePlaintext = JSON.parse(plaintext) as Activate.ActivationCodePlaintext
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
if (!activationCodePlaintext) {
|
||||
generating.value = false
|
||||
message.error('无效的设备申请码')
|
||||
return
|
||||
}
|
||||
const contrast = activationCodePlaintext.contrast
|
||||
const digital = activationCodePlaintext.digital
|
||||
const simulate = activationCodePlaintext.simulate
|
||||
if (!contrast && !digital && !simulate) {
|
||||
generating.value = false
|
||||
message.error('无效的设备申请码')
|
||||
return
|
||||
}
|
||||
if (contrast.apply === 1) {
|
||||
activationCodePlaintext.contrast.permanently = 1
|
||||
}
|
||||
if (digital.apply === 1) {
|
||||
activationCodePlaintext.digital.permanently = 1
|
||||
}
|
||||
if (simulate.apply === 1) {
|
||||
activationCodePlaintext.simulate.permanently = 1
|
||||
}
|
||||
const data = JSON.stringify(activationCodePlaintext)
|
||||
try {
|
||||
setTimeout(() => {
|
||||
activationForm.value.activationCode = rsa.encrypt(data)
|
||||
generating.value = false
|
||||
}, 1000)
|
||||
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
generating.value = false
|
||||
message.error('生成激活码失败')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 复制到剪贴板
|
||||
const copyToClipboard = (text: string) => {
|
||||
if (!text) {
|
||||
message.warning('没有内容可复制')
|
||||
return
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
message.success('复制成功')
|
||||
}).catch(() => {
|
||||
message.error('复制失败')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.activation-page {
|
||||
:deep(textarea) {
|
||||
font-family: consolas, monospace;
|
||||
resize: none;
|
||||
}
|
||||
:deep(.ant-form-item-label) {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
:deep(.ant-card) {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
35
frontend/tsconfig.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
|
||||
"jsx": "preserve",
|
||||
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"importHelpers": true,
|
||||
"resolveJsonModule": true,
|
||||
|
||||
// 定义一个变量就必须给它一个初始值
|
||||
"strictPropertyInitialization": false,
|
||||
// 允许使用obj[key]访问对象属性
|
||||
//"suppressImplicitAnyIndexErrors": true,
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
],
|
||||
}
|
||||
},
|
||||
"include":["src/**/*.ts", "src/**/*.vue", "src/**/*.tsx", "src/**/*.d.ts", "*.d.ts"],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
54
frontend/vite.config.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { defineConfig } from 'vite';
|
||||
import viteCompression from 'vite-plugin-compression';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig((mode) => {
|
||||
return {
|
||||
// Project plugins
|
||||
plugins: [
|
||||
vue(),
|
||||
viteCompression({
|
||||
verbose: true,
|
||||
disable: false,
|
||||
threshold: 1025,
|
||||
algorithm: 'gzip',
|
||||
ext: '.gz',
|
||||
}),
|
||||
],
|
||||
// Base configuration
|
||||
base: './',
|
||||
publicDir: 'public',
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
less: {
|
||||
modifyVars: {
|
||||
'@border-color-base': '#dce3e8',
|
||||
},
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
assetsDir: 'assets',
|
||||
assetsInlineLimit: 4096,
|
||||
cssCodeSplit: true,
|
||||
brotliSize: false,
|
||||
sourcemap: false,
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
// Remove console and debugger in production
|
||||
drop_console: false,
|
||||
drop_debugger: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
50
package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "pqs9100_tool",
|
||||
"version": "0.0.1",
|
||||
"description": "pqs-9100 tool client",
|
||||
"main": "./public/electron/main.js",
|
||||
"scripts": {
|
||||
"dev": "ee-bin dev",
|
||||
"build": "npm run build-frontend && npm run build-electron && ee-bin encrypt",
|
||||
"start": "ee-bin start",
|
||||
"dev-frontend": "ee-bin dev --serve=frontend",
|
||||
"dev-electron": "ee-bin dev --serve=electron",
|
||||
"build-frontend": "ee-bin build --cmds=frontend && ee-bin move --flag=frontend_dist",
|
||||
"build-electron": "ee-bin build --cmds=electron",
|
||||
"encrypt": "ee-bin encrypt",
|
||||
"icon": "ee-bin icon",
|
||||
"build-w": "ee-bin build --cmds=win64",
|
||||
"build-we": "ee-bin build --cmds=win_e",
|
||||
"build-m": "ee-bin build --cmds=mac",
|
||||
"build-m-arm64": "ee-bin build --cmds=mac_arm64",
|
||||
"build-l": "ee-bin build --cmds=linux",
|
||||
"debug-dev": "cross-env DEBUG=ee-* ee-bin dev",
|
||||
"debug-encrypt": "ee-bin encrypt",
|
||||
"debug-electron": "cross-env DEBUG=ee-* ee-bin dev --serve=electron",
|
||||
"debug-move": "ee-bin move --flag=frontend_dist"
|
||||
},
|
||||
"repository": "https://github.com/dromara/electron-egg.git",
|
||||
"keywords": [
|
||||
"Electron",
|
||||
"electron-egg",
|
||||
"ElectronEgg"
|
||||
],
|
||||
"author": "njcn",
|
||||
"devDependencies": {
|
||||
"@electron/rebuild": "^3.7.1",
|
||||
"@types/node": "^20.16.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"debug": "^4.4.0",
|
||||
"ee-bin": "^4.1.10",
|
||||
"electron": "^31.7.6",
|
||||
"electron-builder": "^25.1.8",
|
||||
"icon-gen": "^5.0.0",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"dayjs": "^1.11.13",
|
||||
"ee-core": "^4.1.5",
|
||||
"electron-updater": "^6.3.8"
|
||||
}
|
||||
}
|
||||
94
public/html/loading.html
Normal file
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" />
|
||||
<style>
|
||||
#loadingPage {
|
||||
background-color: #dedede;
|
||||
font-size: 12px;
|
||||
}
|
||||
.base {
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.desc {
|
||||
margin: 0, 0, 20px, 0;
|
||||
}
|
||||
.loading,
|
||||
.loading > div {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.loading {
|
||||
display: block;
|
||||
font-size: 0;
|
||||
color: #06b359;
|
||||
}
|
||||
.loading.la-dark {
|
||||
color: #07C160;
|
||||
}
|
||||
.loading > div {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
background-color: currentColor;
|
||||
border: 0 solid currentColor;
|
||||
}
|
||||
.loading {
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
}
|
||||
.loading > div {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
background: transparent;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
border-radius: 100%;
|
||||
animation: ball-clip-rotate-multiple-rotate 1s ease-in-out infinite;
|
||||
}
|
||||
.loading > div:first-child {
|
||||
position: absolute;
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
border-right-color: transparent;
|
||||
border-left-color: transparent;
|
||||
}
|
||||
.loading > div:last-child {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
animation-duration: 0.5s;
|
||||
animation-direction: reverse;
|
||||
}
|
||||
@keyframes ball-clip-rotate-multiple-rotate {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(-50%, -50%) rotate(180deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="boot">
|
||||
<div class='base'>
|
||||
<div class="loading">
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
22
public/html/view_example.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css">
|
||||
body{
|
||||
margin:0px auto;
|
||||
}
|
||||
#content {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 35%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
这是一个html页面
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
public/images/logo-32.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
public/images/logo.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
public/images/tray.png
Normal file
|
After Width: | Height: | Size: 943 B |
28
public/ssl/localhost+1.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDpWYqPkljVtDNp
|
||||
JVwlcOxNVihQPf4T3Q/tuIt5znV5ImBmlWg+OyuG47Y5e+qPMjFCSX4ebTAtBMbY
|
||||
m6AJihkKy0iKj1mVS9TPCzqcHFnUliCIqJMKFsJTWygNGgogjXhlxFaJgCZO6Gw6
|
||||
ocWp6nw1gMhrMIxqT2MQIQX16SD1IH/F4JMoaYuifnR+OOgbS3yUKHDTFApkZAWn
|
||||
dS4GpbT39rW9cmbJrGHCvl8bsm8MXMdXars10A++wjjmHbtZu8beFv+zKbDw4iAu
|
||||
ls8p/lAoIvQy8MkLLM5b392CLMaK0517+qM3VdEZ0ph+32m9IEoj5QV8xgaPdnyP
|
||||
8/8a2bU/AgMBAAECggEBAIzFZ8GVF+JUA2+7CgvMQ8Gj6E4AF/cDtUhDvGCPHG8n
|
||||
PeCk4W4pY+jMFnI3PxmDvhOvIlZYqGeAKjUiLTmUBedtGyX7tJ9MT+VXcNQchlSo
|
||||
/Jd0mr/LWw/OPispOlLJBYjfGRV6KaIQtLnqPcRzoNrmBgIkF5FKswhX47CmIyu8
|
||||
eEXmDsqXzWmYwdOHLNDshLOrCgFgRnqK7HjxvNqb9k4qv3V9WxlJYITK7L4eJNcZ
|
||||
XYvGl+QV1+n153phyYnHcbohzvE44Hv8e4hiY9uYNdWckNBNFLpk9vEmUjewCRT1
|
||||
kL30woifUhZCalXBIfKEHNZg23teOMHNZTqcS+ER64ECgYEA7BAWVWS/8b9h1AmP
|
||||
SYhJgudb+ck1ItuvEDjonKuUCTBYm0iQI7cgAimasIBybRO8zwKstvWGDnUJJEkB
|
||||
Oh7AXF6CbKuP7navTBAaF3AXsNfDgQuEwtEg78iqiQzTyp6FOvvOT2aOtzi86Ju1
|
||||
zwKu+DLV3ETeokHWrQmV1uZnbZMCgYEA/Q7LdEEvZxz8OqOjtj8iKLWAv0WvnHTO
|
||||
Zjqn/BbXi2PyI8d5ntIUPEgm/MRpvXTa465Uu8Orujfq51Z/oFbyvhhEOWT/sVn/
|
||||
fzzbH4xXb3bLJ3D+LILQzsm4d8yrV66Re1ehFVxRPIy5RTpssED7bOns9ds4lsHT
|
||||
W9p7ibYRBSUCgYAMpJftnuXA1tUwfAqWj5wQTL/aUvJrmYR4w/OBYJcfHt3AA1Tk
|
||||
9MvcEcpdJaP7P5FfLO9/JQs2/wGsVdSg/kCjMdSeaVneFbExy7L6CmDacdPgt3M2
|
||||
0+iFryOjD3LQaUkNbasRCZcfLQTBGIXWPniMhnx5vZ6G5ivPPLIvvktPzQKBgQCd
|
||||
s/yi5ISwE+Y0fQpnZwzYpdQoXztDm5+NIfzSI0IMgirClWt7yJwHvUdeuuDSyuIm
|
||||
hdwUb6qzkGl55fP/bnA0e1b5FbIrSlTpbHl6PbG3qyaL2+TqxFNwq1Gkhw44xHex
|
||||
kDi44SFXRLOpKvHVHYoSo+2igg3QFdasJYpblfUhaQKBgF0l9PpMbDLdPlI33IQz
|
||||
bEzw0ig8R8nHocJzOkK/BdLI8WiItYGgq4mcZGDWsztNg17QQGTEFrH7H9B8DKAJ
|
||||
p75jz5O83arjMECqAiXlSGOWtq6NhbgyJcQJxvvvN8wObVFoVkLoEbqE1TkDqZfI
|
||||
CqiusA5zgG89vzP9xFhW2ia2
|
||||
-----END PRIVATE KEY-----
|
||||
26
public/ssl/localhost+1.pem
Normal file
@@ -0,0 +1,26 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEXzCCAsegAwIBAgIRAOLUY4uS9d2yXx0vd6qql30wDQYJKoZIhvcNAQELBQAw
|
||||
gZExHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEzMDEGA1UECwwqQklM
|
||||
SUJJTElcZ2Fvc2h1YWl4aW5nQENOMjEwMTAyMjc0ICjljaHor7opMTowOAYDVQQD
|
||||
DDFta2NlcnQgQklMSUJJTElcZ2Fvc2h1YWl4aW5nQENOMjEwMTAyMjc0ICjljaHo
|
||||
r7opMB4XDTIyMDcyNzA4NDcyOFoXDTI0MTAyNzA4NDcyOFowXjEnMCUGA1UEChMe
|
||||
bWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTMwMQYDVQQLDCpCSUxJQklM
|
||||
SVxnYW9zaHVhaXhpbmdAQ04yMTAxMDIyNzQgKOWNoeivuikwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDpWYqPkljVtDNpJVwlcOxNVihQPf4T3Q/tuIt5
|
||||
znV5ImBmlWg+OyuG47Y5e+qPMjFCSX4ebTAtBMbYm6AJihkKy0iKj1mVS9TPCzqc
|
||||
HFnUliCIqJMKFsJTWygNGgogjXhlxFaJgCZO6Gw6ocWp6nw1gMhrMIxqT2MQIQX1
|
||||
6SD1IH/F4JMoaYuifnR+OOgbS3yUKHDTFApkZAWndS4GpbT39rW9cmbJrGHCvl8b
|
||||
sm8MXMdXars10A++wjjmHbtZu8beFv+zKbDw4iAuls8p/lAoIvQy8MkLLM5b392C
|
||||
LMaK0517+qM3VdEZ0ph+32m9IEoj5QV8xgaPdnyP8/8a2bU/AgMBAAGjZDBiMA4G
|
||||
A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBQn
|
||||
B1E5Js/cFhxBwpZL59aoK/skLjAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEw
|
||||
DQYJKoZIhvcNAQELBQADggGBAIaUncQj2XN2rNn6sE0MuaWboFqwpkydyei6FvtN
|
||||
c/TY9RWW3QRYICcO721l/2jBiWplQt/ZYaJ+IWN+C+3JSAz9IYsM/nMgxHL2azLQ
|
||||
zHKnASEjxptW9+mlsgVk2LTrBfbc197ikLu80M/0jQYaIBeoEOaMlhBjno139nTO
|
||||
evNheyFKvAhggOseD00I9VBZkKDBxvqr6PHnGjyAU43C1/HkNjglIbQjAZdBmXlX
|
||||
HuelQ97glfhzyApvmczPrc8IAqPhtYn2nJ5P6Ea35LEc3D7uVExywcjDFcSwMJCb
|
||||
TXqouzM/U8pO+DGeuvgwkYrBGlA7iEE+ZQgxCBatOXwG95THtFlfW+H0ILHB2tcX
|
||||
P+Kztwd+4ipPciJz+1NK7z7erwfxHO5hmXJskH9YWi6YJsIw5g1iYs0pJJ/4p7Bd
|
||||
8qSGEhri/+iijcC76q+1N0xhJxQrDDlC0pKp6oAYFDGKirzwmlAf/eJBy0ORWjCj
|
||||
yk+d9T622yzcXa5fw3HBZh1o6A==
|
||||
-----END CERTIFICATE-----
|
||||
28
tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"useDefineForClassFields": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["node"],
|
||||
"esModuleInterop": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "node", // node
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitAny": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
},
|
||||
"include": [
|
||||
"./electron/**/*"
|
||||
]
|
||||
}
|
||||
|
||||