|
|
@ -0,0 +1,19 @@
|
||||||
|
node_modules
|
||||||
|
out/
|
||||||
|
logs/
|
||||||
|
run/
|
||||||
|
.idea/
|
||||||
|
data/
|
||||||
|
.vscode/launch.json
|
||||||
|
public/electron/
|
||||||
|
pnpm-lock.yaml
|
||||||
|
.yalc/
|
||||||
|
yalc.lock
|
||||||
|
go/public/
|
||||||
|
go/go.sum
|
||||||
|
build/extraResources/java-app.jar
|
||||||
|
build/extraResources/jre1.8.0_201/
|
||||||
|
python/.venv/
|
||||||
|
python/*.spec
|
||||||
|
python/build/
|
||||||
|
python/dist/
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 安徽烁景智能科技有限公司
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
# EE框架 v3
|
||||||
|
[](https://gitee.com/dromara/electron-egg/stargazers)
|
||||||
|
|
||||||
|
<div align=center>
|
||||||
|
<img src="https://wallace5303.gitee.io/ee/images/electron-egg/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/)
|
||||||
|
|
||||||
|
## 📋 介绍
|
||||||
|
- 🍩 **为什么使用?** 桌面软件(办公方向、 个人工具),仍然是未来十几年PC端需求之一,提高工作效率
|
||||||
|
- 🍉 **简单:** 只需懂 JavaScript
|
||||||
|
- 🍑 **愿景:** 所有开发者都能学会桌面软件研发
|
||||||
|
- 🍰 **gitee:** https://gitee.com/dromara/electron-egg **4100+**
|
||||||
|
- 🍨 **github:** https://github.com/dromara/electron-egg **1200+**
|
||||||
|
- 🏆 码云最有价值开源项目
|
||||||
|

|
||||||
|
|
||||||
|
## 📚 文档
|
||||||
|
- [教程文档](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/)
|
||||||
|
|
||||||
|
## 🐶 项目案例
|
||||||
|
- 🐟 EE框架已经应用于医疗、学校、政务、股票交易、ERP、娱乐、视频、企业等领域客户端
|
||||||
|
|
||||||
|
- 🐸 英雄联盟助手
|
||||||
|

|
||||||
|
|
||||||
|
- [更多项目](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>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"deviceId":"A0-29-42-42-F6-3F",
|
||||||
|
"deviceType":1,
|
||||||
|
"deptId":101
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
建议第三方软件放置在此目录中,打包时会将资源加入安装包内。
|
||||||
|
|
||||||
|
1 config.json 放一个配置文件, 可以读取,
|
||||||
|
手动设置设备的唯一id
|
||||||
|
设备类型
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 745 B |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 318 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
|
@ -0,0 +1,170 @@
|
||||||
|
const { app: electronApp } = require('electron');
|
||||||
|
const { autoUpdater } = require("electron-updater");
|
||||||
|
const is = require('ee-core/utils/is');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const Conf = require('ee-core/config');
|
||||||
|
const CoreWindow = require('ee-core/electron/window');
|
||||||
|
const Electron = require('ee-core/electron');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动升级插件
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class AutoUpdaterAddon {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建
|
||||||
|
*/
|
||||||
|
create () {
|
||||||
|
Log.info('[addon:autoUpdater] load');
|
||||||
|
const cfg = Conf.getValue('addons.autoUpdater');
|
||||||
|
if ((is.windows() && cfg.windows)
|
||||||
|
|| (is.macOS() && cfg.macOS)
|
||||||
|
|| (is.linux() && cfg.linux))
|
||||||
|
{
|
||||||
|
// continue
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否检查更新
|
||||||
|
if (cfg.force) {
|
||||||
|
this.checkUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = {
|
||||||
|
error: -1,
|
||||||
|
available: 1,
|
||||||
|
noAvailable: 2,
|
||||||
|
downloading: 3,
|
||||||
|
downloaded: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
const version = electronApp.getVersion();
|
||||||
|
Log.info('[addon:autoUpdater] current version: ', version);
|
||||||
|
|
||||||
|
// 设置下载服务器地址
|
||||||
|
let server = cfg.options.url;
|
||||||
|
let lastChar = server.substring(server.length - 1);
|
||||||
|
server = lastChar === '/' ? server : server + "/";
|
||||||
|
//Log.info('[addon:autoUpdater] server: ', server);
|
||||||
|
cfg.options.url = server;
|
||||||
|
|
||||||
|
// 是否后台自动下载
|
||||||
|
autoUpdater.autoDownload = cfg.force ? true : false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
autoUpdater.setFeedURL(cfg.options);
|
||||||
|
} catch (error) {
|
||||||
|
Log.error('[addon:autoUpdater] setFeedURL error : ', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
autoUpdater.on('checking-for-update', () => {
|
||||||
|
//sendStatusToWindow('正在检查更新...');
|
||||||
|
})
|
||||||
|
autoUpdater.on('update-available', (info) => {
|
||||||
|
info.status = status.available;
|
||||||
|
info.desc = '有可用更新';
|
||||||
|
this.sendStatusToWindow(info);
|
||||||
|
})
|
||||||
|
autoUpdater.on('update-not-available', (info) => {
|
||||||
|
info.status = status.noAvailable;
|
||||||
|
info.desc = '没有可用更新';
|
||||||
|
this.sendStatusToWindow(info);
|
||||||
|
})
|
||||||
|
autoUpdater.on('error', (err) => {
|
||||||
|
let info = {
|
||||||
|
status: status.error,
|
||||||
|
desc: err
|
||||||
|
}
|
||||||
|
this.sendStatusToWindow(info);
|
||||||
|
})
|
||||||
|
autoUpdater.on('download-progress', (progressObj) => {
|
||||||
|
let percentNumber = parseInt(progressObj.percent);
|
||||||
|
let totalSize = this.bytesChange(progressObj.total);
|
||||||
|
let transferredSize = this.bytesChange(progressObj.transferred);
|
||||||
|
let text = '已下载 ' + percentNumber + '%';
|
||||||
|
text = text + ' (' + transferredSize + "/" + totalSize + ')';
|
||||||
|
|
||||||
|
let info = {
|
||||||
|
status: status.downloading,
|
||||||
|
desc: text,
|
||||||
|
percentNumber: percentNumber,
|
||||||
|
totalSize: totalSize,
|
||||||
|
transferredSize: transferredSize
|
||||||
|
}
|
||||||
|
Log.info('[addon:autoUpdater] progress: ', text);
|
||||||
|
this.sendStatusToWindow(info);
|
||||||
|
})
|
||||||
|
autoUpdater.on('update-downloaded', (info) => {
|
||||||
|
info.status = status.downloaded;
|
||||||
|
info.desc = '下载完成';
|
||||||
|
this.sendStatusToWindow(info);
|
||||||
|
|
||||||
|
// 托盘插件默认会阻止窗口关闭,这里设置允许关闭窗口
|
||||||
|
Electron.extra.closeWindow = true;
|
||||||
|
|
||||||
|
autoUpdater.quitAndInstall();
|
||||||
|
// const mainWindow = CoreWindow.getMainWindow();
|
||||||
|
// if (mainWindow) {
|
||||||
|
// mainWindow.destroy()
|
||||||
|
// }
|
||||||
|
// electronApp.appQuit()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查更新
|
||||||
|
*/
|
||||||
|
checkUpdate () {
|
||||||
|
autoUpdater.checkForUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载更新
|
||||||
|
*/
|
||||||
|
download () {
|
||||||
|
autoUpdater.downloadUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向前端发消息
|
||||||
|
*/
|
||||||
|
sendStatusToWindow(content = {}) {
|
||||||
|
const textJson = JSON.stringify(content);
|
||||||
|
const channel = 'app.updater';
|
||||||
|
const win = CoreWindow.getMainWindow();
|
||||||
|
win.webContents.send(channel, textJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单位转换
|
||||||
|
*/
|
||||||
|
bytesChange (limit) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoUpdaterAddon.toString = () => '[class AutoUpdaterAddon]';
|
||||||
|
module.exports = AutoUpdaterAddon;
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
const { app: electronApp } = require('electron');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const Conf = require('ee-core/config');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 唤醒插件
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class AwakenAddon {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.protocol = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建
|
||||||
|
*/
|
||||||
|
create () {
|
||||||
|
Log.info('[addon:awaken] load');
|
||||||
|
|
||||||
|
const cfg = Conf.getValue('addons.awaken');
|
||||||
|
this.protocol = cfg.protocol;
|
||||||
|
|
||||||
|
electronApp.setAsDefaultProtocolClient(this.protocol);
|
||||||
|
|
||||||
|
this.handleArgv(process.argv);
|
||||||
|
electronApp.on('second-instance', (event, argv) => {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
this.handleArgv(argv)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 仅用于macOS
|
||||||
|
electronApp.on('open-url', (event, urlStr) => {
|
||||||
|
this.handleUrl(urlStr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数处理
|
||||||
|
*/
|
||||||
|
handleArgv(argv) {
|
||||||
|
const offset = electronApp.isPackaged ? 1 : 2;
|
||||||
|
const url = argv.find((arg, i) => i >= offset && arg.startsWith(this.protocol));
|
||||||
|
this.handleUrl(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* url解析
|
||||||
|
*/
|
||||||
|
handleUrl(awakeUrlStr) {
|
||||||
|
if (!awakeUrlStr || awakeUrlStr.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const {hostname, pathname, search} = new URL(awakeUrlStr);
|
||||||
|
let awakeUrlInfo = {
|
||||||
|
urlStr: awakeUrlStr,
|
||||||
|
urlHost: hostname,
|
||||||
|
urlPath: pathname,
|
||||||
|
urlParams: search && search.slice(1)
|
||||||
|
}
|
||||||
|
Log.info('[addon:awaken] awakeUrlInfo:', awakeUrlInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AwakenAddon.toString = () => '[class AwakenAddon]';
|
||||||
|
module.exports = AwakenAddon;
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
const { app, session } = require('electron');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展插件 (electron自身对该功能并不完全支持,官方也不建议使用)
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class ChromeExtensionAddon {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建
|
||||||
|
*/
|
||||||
|
async create () {
|
||||||
|
Log.info('[addon:chromeExtension] load');
|
||||||
|
|
||||||
|
const extensionIds = this.getAllIds();
|
||||||
|
for (let i = 0; i < extensionIds.length; i++) {
|
||||||
|
await this.load(extensionIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取扩展id列表(crx解压后的目录名,即是该扩展的id)
|
||||||
|
*/
|
||||||
|
getAllIds () {
|
||||||
|
const extendsionDir = this.getDirectory();
|
||||||
|
const ids = this.getDirs(extendsionDir);
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展所在目录
|
||||||
|
*/
|
||||||
|
getDirectory () {
|
||||||
|
let extensionDirPath = '';
|
||||||
|
let variablePath = 'build'; // 打包前路径
|
||||||
|
if (app.isPackaged) {
|
||||||
|
variablePath = '..'; // 打包后路径
|
||||||
|
}
|
||||||
|
extensionDirPath = path.join(app.getAppPath(), variablePath, "extraResources", "chromeExtension");
|
||||||
|
|
||||||
|
return extensionDirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载扩展
|
||||||
|
*/
|
||||||
|
async load (extensionId = '') {
|
||||||
|
if (_.isEmpty(extensionId)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const extensionPath = path.join(this.getDirectory(), extensionId);
|
||||||
|
Log.info('[addon:chromeExtension] extensionPath:', extensionPath);
|
||||||
|
await session.defaultSession.loadExtension(extensionPath, { allowFileAccess: true });
|
||||||
|
} catch (e) {
|
||||||
|
Log.info('[addon:chromeExtension] load extension error extensionId:%s, errorInfo:%s', extensionId, e.toString());
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取目录下所有文件夹
|
||||||
|
*/
|
||||||
|
getDirs(dir) {
|
||||||
|
if (!dir) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = [];
|
||||||
|
const files = fs.readdirSync(dir);
|
||||||
|
files.forEach(function(item, index) {
|
||||||
|
const stat = fs.lstatSync(dir + '/' + item);
|
||||||
|
if (stat.isDirectory() === true) {
|
||||||
|
components.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return components;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ChromeExtensionAddon.toString = () => '[class ChromeExtensionAddon]';
|
||||||
|
module.exports = ChromeExtensionAddon;
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const EE = require('ee-core/ee');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全插件
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class SecurityAddon {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建
|
||||||
|
*/
|
||||||
|
create () {
|
||||||
|
Log.info('[addon:security] load');
|
||||||
|
const { CoreApp } = EE;
|
||||||
|
const runWithDebug = process.argv.find(function(e){
|
||||||
|
let isHasDebug = e.includes("--inspect") || e.includes("--inspect-brk") || e.includes("--remote-debugging-port");
|
||||||
|
return isHasDebug;
|
||||||
|
})
|
||||||
|
|
||||||
|
// 不允许远程调试
|
||||||
|
if (runWithDebug) {
|
||||||
|
Log.error('[error] Remote debugging is not allowed, runWithDebug:', runWithDebug);
|
||||||
|
CoreApp.appQuit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SecurityAddon.toString = () => '[class SecurityAddon]';
|
||||||
|
module.exports = SecurityAddon;
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
const { Tray, Menu, app ,BrowserWindow} = require('electron');
|
||||||
|
const path = require('path');
|
||||||
|
const Ps = require('ee-core/ps');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const Electron = require('ee-core/electron');
|
||||||
|
const CoreWindow = require('ee-core/electron/window');
|
||||||
|
const Conf = require('ee-core/config');
|
||||||
|
const EE = require('ee-core/ee');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 托盘插件
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class TrayAddon {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.tray = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建托盘
|
||||||
|
*/
|
||||||
|
create () {
|
||||||
|
// 开发环境,代码热更新开启时,会导致托盘中有残影
|
||||||
|
if (Ps.isDev() && Ps.isHotReload()) return;
|
||||||
|
|
||||||
|
Log.info('[addon:tray] load');
|
||||||
|
const { CoreApp } = EE;
|
||||||
|
const cfg = Conf.getValue('addons.tray');
|
||||||
|
const mainWindow = CoreWindow.getMainWindow();
|
||||||
|
|
||||||
|
// 托盘图标
|
||||||
|
let iconPath = path.join(Ps.getHomeDir(), cfg.icon);
|
||||||
|
|
||||||
|
// 托盘菜单功能列表
|
||||||
|
let trayMenuTemplate = [
|
||||||
|
{
|
||||||
|
label: '显示',
|
||||||
|
click: function () {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '退出',
|
||||||
|
click: function () {
|
||||||
|
console.log('exit click 事件')
|
||||||
|
// CoreApp.appQuit();
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 点击关闭,最小化到托盘 这里要是阻止关闭就完全无法关闭了
|
||||||
|
mainWindow.on('close', (event) => {
|
||||||
|
console.log('close 事件')
|
||||||
|
console.log(event.sender)
|
||||||
|
// if (Electron.extra.closeWindow == true) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// mainWindow.hide();
|
||||||
|
// 禁止关闭
|
||||||
|
// event.preventDefault();
|
||||||
|
//todo: 这个变量控制的方法来区分是否退出 还是不退出
|
||||||
|
if(global.isUserExit == false){
|
||||||
|
event.preventDefault();
|
||||||
|
}else{
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 实例化托盘
|
||||||
|
this.tray = new Tray(iconPath);
|
||||||
|
this.tray.setToolTip(cfg.title);
|
||||||
|
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
|
||||||
|
this.tray.setContextMenu(contextMenu);
|
||||||
|
// 左键单击的时候能够显示主窗口
|
||||||
|
this.tray.on('click', () => {
|
||||||
|
mainWindow.show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrayAddon.toString = () => '[class TrayAddon]';
|
||||||
|
module.exports = TrayAddon;
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* ee-bin 配置
|
||||||
|
* 仅适用于开发环境
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* development serve ("frontend" "electron" )
|
||||||
|
* ee-bin dev
|
||||||
|
*/
|
||||||
|
dev: {
|
||||||
|
frontend: {
|
||||||
|
directory: './frontend',
|
||||||
|
cmd: 'npm',
|
||||||
|
args: ['run', 'dev'],
|
||||||
|
protocol: 'http://',
|
||||||
|
hostname: 'localhost',
|
||||||
|
port: 17680,
|
||||||
|
indexPath: 'index.html'
|
||||||
|
},
|
||||||
|
electron: {
|
||||||
|
directory: './',
|
||||||
|
cmd: 'electron',
|
||||||
|
args: ['.', '--env=local'],
|
||||||
|
loadingPage: '/public/html/loading.html',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建
|
||||||
|
* ee-bin build
|
||||||
|
*/
|
||||||
|
build: {
|
||||||
|
frontend: {
|
||||||
|
directory: './frontend',
|
||||||
|
cmd: 'npm',
|
||||||
|
args: ['run', 'build'],
|
||||||
|
},
|
||||||
|
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: {
|
||||||
|
dist: './frontend/dist',
|
||||||
|
target: './public/dist'
|
||||||
|
},
|
||||||
|
go_static: {
|
||||||
|
dist: './frontend/dist',
|
||||||
|
target: './go/public/dist'
|
||||||
|
},
|
||||||
|
go_config: {
|
||||||
|
dist: './go/config',
|
||||||
|
target: './go/public/config'
|
||||||
|
},
|
||||||
|
go_package: {
|
||||||
|
dist: './package.json',
|
||||||
|
target: './go/public/package.json'
|
||||||
|
},
|
||||||
|
go_images: {
|
||||||
|
dist: './public/images',
|
||||||
|
target: './go/public/images'
|
||||||
|
},
|
||||||
|
python_dist: {
|
||||||
|
dist: './python/dist',
|
||||||
|
target: './build/extraResources/py'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预发布模式(prod)
|
||||||
|
* ee-bin start
|
||||||
|
*/
|
||||||
|
start: {
|
||||||
|
directory: './',
|
||||||
|
cmd: 'electron',
|
||||||
|
args: ['.', '--env=prod']
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*/
|
||||||
|
encrypt: {
|
||||||
|
type: 'confusion',
|
||||||
|
files: [
|
||||||
|
'electron/**/*.(js|json)',
|
||||||
|
'!electron/config/encrypt.js',
|
||||||
|
'!electron/config/nodemon.json',
|
||||||
|
'!electron/config/builder.json',
|
||||||
|
'!electron/config/bin.json',
|
||||||
|
],
|
||||||
|
fileExt: ['.js'],
|
||||||
|
confusionOptions: {
|
||||||
|
compact: true,
|
||||||
|
stringArray: true,
|
||||||
|
stringArrayEncoding: ['none'],
|
||||||
|
deadCodeInjection: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行自定义命令
|
||||||
|
* ee-bin exec
|
||||||
|
*/
|
||||||
|
exec: {
|
||||||
|
node_v: {
|
||||||
|
directory: './',
|
||||||
|
cmd: 'node',
|
||||||
|
args: ['-v'],
|
||||||
|
},
|
||||||
|
npm_v: {
|
||||||
|
directory: './',
|
||||||
|
cmd: 'npm',
|
||||||
|
args: ['-v'],
|
||||||
|
},
|
||||||
|
python: {
|
||||||
|
directory: './python',
|
||||||
|
cmd: 'python',
|
||||||
|
args: ['./main.py', '--port=7074'],
|
||||||
|
stdio: "inherit", // ignore
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"productName": "appCtr",
|
||||||
|
"appId": "com.electron.appCtr",
|
||||||
|
"copyright": "© 2023 安徽烁景智能科技有限公司 Technology Co., Ltd.",
|
||||||
|
"directories": {
|
||||||
|
"output": "out"
|
||||||
|
},
|
||||||
|
"asar": true,
|
||||||
|
"files": [
|
||||||
|
"**/*",
|
||||||
|
"!frontend/",
|
||||||
|
"!run/",
|
||||||
|
"!logs/",
|
||||||
|
"!go/",
|
||||||
|
"!python/",
|
||||||
|
"!data/"
|
||||||
|
],
|
||||||
|
"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": "appCtr"
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"icon": "build/icons/icon.icns",
|
||||||
|
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||||
|
"darkModeSupport": true,
|
||||||
|
"hardenedRuntime": false
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"icon": "build/icons/icon.ico",
|
||||||
|
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"target": "nsis"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"icon": "build/icons",
|
||||||
|
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||||
|
"target": [
|
||||||
|
"deb"
|
||||||
|
],
|
||||||
|
"category": "Utility"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认配置
|
||||||
|
*/
|
||||||
|
module.exports = (appInfo) => {
|
||||||
|
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开发者工具
|
||||||
|
*/
|
||||||
|
config.openDevTools = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用程序顶部菜单
|
||||||
|
*/
|
||||||
|
config.openAppMenu = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主窗口
|
||||||
|
*/
|
||||||
|
config.windowsOption = {
|
||||||
|
title: 'appCtr',
|
||||||
|
width: 980,
|
||||||
|
height: 650,
|
||||||
|
minWidth: 400,
|
||||||
|
minHeight: 300,
|
||||||
|
// 禁止缩小
|
||||||
|
resizable: false,
|
||||||
|
// 禁止最小化
|
||||||
|
minimizable: false,
|
||||||
|
webPreferences: {
|
||||||
|
//webSecurity: false,
|
||||||
|
contextIsolation: false, // false -> 可在渲染进程中使用electron的api,true->需要bridge.js(contextBridge)
|
||||||
|
nodeIntegration: true,
|
||||||
|
//preload: path.join(appInfo.baseDir, 'preload', 'bridge.js'),
|
||||||
|
},
|
||||||
|
frame: true,
|
||||||
|
show: false,
|
||||||
|
icon: path.join(appInfo.home, 'public', 'images', 'logo-32.png'),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ee框架日志
|
||||||
|
*/
|
||||||
|
config.logger = {
|
||||||
|
encoding: 'utf8',
|
||||||
|
level: 'INFO',
|
||||||
|
outputJSON: false,
|
||||||
|
buffer: true,
|
||||||
|
enablePerformanceTimer: false,
|
||||||
|
rotator: 'day',
|
||||||
|
appLogName: 'appCtr.log',
|
||||||
|
coreLogName: 'appCtr-core.log',
|
||||||
|
errorLogName: 'appCtr-error.log'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 远程模式-web地址
|
||||||
|
*/
|
||||||
|
config.remoteUrl = {
|
||||||
|
enable: false,
|
||||||
|
url: 'http://electron-egg.kaka996.com/'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内置socket服务
|
||||||
|
*/
|
||||||
|
config.socketServer = {
|
||||||
|
enable: false,
|
||||||
|
port: 7070,
|
||||||
|
path: "/socket.io/",
|
||||||
|
connectTimeout: 45000,
|
||||||
|
pingTimeout: 30000,
|
||||||
|
pingInterval: 25000,
|
||||||
|
maxHttpBufferSize: 1e8,
|
||||||
|
transports: ["polling", "websocket"],
|
||||||
|
cors: {
|
||||||
|
origin: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内置http服务
|
||||||
|
*/
|
||||||
|
config.httpServer = {
|
||||||
|
enable: false,
|
||||||
|
https: {
|
||||||
|
enable: false,
|
||||||
|
key: '/public/ssl/localhost+1.key',
|
||||||
|
cert: '/public/ssl/localhost+1.pem'
|
||||||
|
},
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 7071,
|
||||||
|
cors: {
|
||||||
|
origin: "*"
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
multipart: true,
|
||||||
|
formidable: {
|
||||||
|
keepExtensions: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterRequest: {
|
||||||
|
uris: [
|
||||||
|
'favicon.ico'
|
||||||
|
],
|
||||||
|
returnData: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主进程
|
||||||
|
*/
|
||||||
|
config.mainServer = {
|
||||||
|
protocol: 'file://',
|
||||||
|
indexPath: '/public/dist/index.html',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port: 7072,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cross-language service
|
||||||
|
* 跨语言服务
|
||||||
|
* 例如:执行go的二进制程序,默认目录为 ./extraResources/
|
||||||
|
*/
|
||||||
|
config.cross = {
|
||||||
|
go: {
|
||||||
|
enable: false,
|
||||||
|
name: 'goapp',
|
||||||
|
args: ['--port=7073'],
|
||||||
|
appExit: true,
|
||||||
|
},
|
||||||
|
python: {
|
||||||
|
enable: false,
|
||||||
|
name: 'pyapp',
|
||||||
|
cmd: './py/pyapp',
|
||||||
|
directory: './py',
|
||||||
|
args: ['--port=7074'],
|
||||||
|
appExit: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 硬件加速
|
||||||
|
*/
|
||||||
|
config.hardGpu = {
|
||||||
|
enable: true
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常捕获
|
||||||
|
*/
|
||||||
|
config.exception = {
|
||||||
|
mainExit: false,
|
||||||
|
childExit: true,
|
||||||
|
rendererExit: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jobs
|
||||||
|
*/
|
||||||
|
config.jobs = {
|
||||||
|
messageLog: true
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件功能
|
||||||
|
*/
|
||||||
|
config.addons = {
|
||||||
|
window: {
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
tray: {
|
||||||
|
enable: true,
|
||||||
|
title: 'appCtr',
|
||||||
|
icon: '/public/images/tray.png'
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
enable: true,
|
||||||
|
},
|
||||||
|
awaken: {
|
||||||
|
enable: true,
|
||||||
|
protocol: 'appCtr',
|
||||||
|
args: []
|
||||||
|
},
|
||||||
|
autoUpdater: {
|
||||||
|
enable: true,
|
||||||
|
windows: false,
|
||||||
|
macOS: false,
|
||||||
|
linux: false,
|
||||||
|
options: {
|
||||||
|
provider: 'generic',
|
||||||
|
url: 'http://kodo.qiniu.com/'
|
||||||
|
},
|
||||||
|
force: false,
|
||||||
|
|
||||||
|
},
|
||||||
|
javaServer: {
|
||||||
|
enable: false,
|
||||||
|
port: 117680,
|
||||||
|
jreVersion: 'jre1.8.0_201',
|
||||||
|
opt: '-server -Xms512M -Xmx512M -Xss512k -Dspring.profiles.active=prod -Dserver.port=${port} -Dlogging.file.path="${path}" ',
|
||||||
|
name: 'java-app.jar'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开发环境配置,覆盖 config.default.js
|
||||||
|
*/
|
||||||
|
module.exports = (appInfo) => {
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开发者工具
|
||||||
|
*/
|
||||||
|
config.openDevTools = {
|
||||||
|
mode: 'undocked'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用程序顶部菜单
|
||||||
|
*/
|
||||||
|
config.openAppMenu = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jobs
|
||||||
|
*/
|
||||||
|
config.jobs = {
|
||||||
|
messageLog: true
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cross-language service
|
||||||
|
* 跨语言服务
|
||||||
|
* 如果有cmd参数,则执行该命令且需要指定 directory
|
||||||
|
*/
|
||||||
|
config.cross = {
|
||||||
|
go: {
|
||||||
|
// 应用运行时启动
|
||||||
|
enable: false,
|
||||||
|
// 程序名
|
||||||
|
name: 'goapp',
|
||||||
|
// 可执行程序
|
||||||
|
cmd: 'go',
|
||||||
|
// 程序目录
|
||||||
|
directory: './go',
|
||||||
|
args: ['run', './main.go', '--env=dev','--basedir=../', '--port=7073'],
|
||||||
|
appExit: true,
|
||||||
|
},
|
||||||
|
python: {
|
||||||
|
enable: false,
|
||||||
|
name: 'pyapp',
|
||||||
|
cmd: 'python',
|
||||||
|
directory: './python',
|
||||||
|
args: ['./main.py', '--port=7074'],
|
||||||
|
stdio: "ignore",
|
||||||
|
appExit: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生产环境配置,覆盖 config.default.js
|
||||||
|
*/
|
||||||
|
module.exports = (appInfo) => {
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开发者工具
|
||||||
|
*/
|
||||||
|
config.openDevTools = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用程序顶部菜单
|
||||||
|
*/
|
||||||
|
config.openAppMenu = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jobs
|
||||||
|
*/
|
||||||
|
config.jobs = {
|
||||||
|
messageLog: false
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"watch": [
|
||||||
|
"electron/"
|
||||||
|
],
|
||||||
|
"ignore": [],
|
||||||
|
"ext": "js,json",
|
||||||
|
"verbose": true,
|
||||||
|
"exec": "ee-bin dev",
|
||||||
|
"restartable": "hr",
|
||||||
|
"colours": true,
|
||||||
|
"events": {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Controller } = require('ee-core');
|
||||||
|
const Cross = require('ee-core/cross');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const HttpClient = require('ee-core/httpclient');
|
||||||
|
const Services = require('ee-core/services');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cross
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class CrossController extends Controller {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View process service information
|
||||||
|
*/
|
||||||
|
info() {
|
||||||
|
const pids = Cross.getPids();
|
||||||
|
Log.info('cross pids:', pids);
|
||||||
|
|
||||||
|
let num = 1;
|
||||||
|
pids.forEach(pid => {
|
||||||
|
let entity = Cross.getProc(pid);
|
||||||
|
Log.info(`server-${num} name:${entity.name}`);
|
||||||
|
Log.info(`server-${num} config:`, entity.config);
|
||||||
|
num++;
|
||||||
|
})
|
||||||
|
|
||||||
|
return 'hello electron-egg';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get service url
|
||||||
|
*/
|
||||||
|
async getUrl(args) {
|
||||||
|
const { name } = args;
|
||||||
|
const serverUrl = Cross.getUrl(name);
|
||||||
|
return serverUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kill service
|
||||||
|
* By default (modifiable), killing the process will exit the electron application.
|
||||||
|
*/
|
||||||
|
async killServer(args) {
|
||||||
|
const { type, name } = args;
|
||||||
|
if (type == 'all') {
|
||||||
|
Cross.killAll();
|
||||||
|
} else {
|
||||||
|
Cross.killByName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create service
|
||||||
|
*/
|
||||||
|
async createServer(args) {
|
||||||
|
const { program } = args;
|
||||||
|
if (program == 'go') {
|
||||||
|
Services.get('cross').createGoServer();
|
||||||
|
} else if (program == 'java') {
|
||||||
|
Services.get('cross').createJavaServer();
|
||||||
|
} else if (program == 'python') {
|
||||||
|
Services.get('cross').createPythonServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the api for the cross service
|
||||||
|
*/
|
||||||
|
async requestApi(args) {
|
||||||
|
const { name, urlPath, params} = args;
|
||||||
|
const hc = new HttpClient();
|
||||||
|
const serverUrl = Cross.getUrl(name);
|
||||||
|
console.log('Server Url:', serverUrl);
|
||||||
|
|
||||||
|
const apiHello = serverUrl + urlPath;
|
||||||
|
const options = {
|
||||||
|
method: 'GET',
|
||||||
|
data: params || {},
|
||||||
|
dataType: 'json',
|
||||||
|
timeout: 1000,
|
||||||
|
};
|
||||||
|
const result = await hc.request(apiHello, options);
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CrossController.toString = () => '[class CrossController]';
|
||||||
|
module.exports = CrossController;
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Controller } = require('ee-core');
|
||||||
|
const { dialog } = require('electron');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const CoreWindow = require('ee-core/electron/window');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 特效 - 功能demo
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class EffectController extends Controller {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择文件
|
||||||
|
*/
|
||||||
|
selectFile() {
|
||||||
|
const filePaths = dialog.showOpenDialogSync({
|
||||||
|
properties: ['openFile']
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_.isEmpty(filePaths)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePaths[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* login window
|
||||||
|
*/
|
||||||
|
loginWindow(args) {
|
||||||
|
const { width, height } = args;
|
||||||
|
const win = CoreWindow.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) {
|
||||||
|
const { width, height } = args;
|
||||||
|
const win = CoreWindow.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]';
|
||||||
|
module.exports = EffectController;
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Controller } = require('ee-core');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const Services = require('ee-core/services');
|
||||||
|
const Addon = require('ee-core/addon');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* example
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class ExampleController extends Controller {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有方法接收两个参数
|
||||||
|
* @param args 前端传的参数
|
||||||
|
* @param event - ipc通信时才有值。详情见:控制器文档
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async test () {
|
||||||
|
|
||||||
|
// const result1 = await Services.get('example').test('electron');
|
||||||
|
// Log.info('service result1:', result1);
|
||||||
|
|
||||||
|
// Services.get('framework').test('electron');
|
||||||
|
|
||||||
|
return 'hello electron-egg';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async testUtils () {
|
||||||
|
let mid = await Utils.machineIdSync(true);
|
||||||
|
Log.info('mid 11111111:', mid);
|
||||||
|
|
||||||
|
Utils.machineId().then((id) => {
|
||||||
|
Log.info('mid 222222222:', id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async testService () {
|
||||||
|
const serviceResult2 = await Services.get('example').test('electron');
|
||||||
|
Log.info('service result2:', serviceResult2);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async testAddon () {
|
||||||
|
const trayResult2 = Addon.get('tray').hello();
|
||||||
|
Log.info('addon result2:', trayResult2);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ExampleController.toString = () => '[class ExampleController]';
|
||||||
|
module.exports = ExampleController;
|
||||||
|
|
@ -0,0 +1,547 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { Controller } = require('ee-core');
|
||||||
|
const { app: electronApp, shell } = require('electron');
|
||||||
|
const dayjs = require('dayjs');
|
||||||
|
const Ps = require('ee-core/ps');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const Services = require('ee-core/services');
|
||||||
|
const Conf = require('ee-core/config');
|
||||||
|
const Addon = require('ee-core/addon');
|
||||||
|
const EE = require('ee-core/ee');
|
||||||
|
const { getNetworkIFaceOne, getMac, getAllMac, getAllPhysicsMac } = require('@lzwme/get-physical-address');
|
||||||
|
const os = require('os');
|
||||||
|
// 网络
|
||||||
|
const net = require('net');
|
||||||
|
// 串口
|
||||||
|
const { SerialPort } = require('serialport')
|
||||||
|
// 保存串口实例
|
||||||
|
var GlobalSeriaPortIns = undefined;
|
||||||
|
/**
|
||||||
|
* electron-egg framework - 功能demo
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class FrameworkController extends Controller {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有方法接收两个参数
|
||||||
|
* @param args 前端传的参数
|
||||||
|
* @param event - ipc通信时才有值。详情见:控制器文档
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送串口消息
|
||||||
|
* @param {*} seriaPort 串口实例
|
||||||
|
*/
|
||||||
|
sendSeriaPort(args) {
|
||||||
|
const port = GlobalSeriaPortIns;
|
||||||
|
const msg = args.msg;
|
||||||
|
port.write(msg, 'hex')
|
||||||
|
// console.log('测试发送消息'+msg);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 连接串口 只执行一次
|
||||||
|
* @param {*} options 串口参数
|
||||||
|
* @param {*} event 回调.
|
||||||
|
*/
|
||||||
|
connectSeriaPort(options, event) {
|
||||||
|
|
||||||
|
// 保证执行一次
|
||||||
|
if (GlobalSeriaPortIns != undefined) {
|
||||||
|
console.log("SerialPort is have");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const channel = 'controller.hardware.connectSeriaPort';
|
||||||
|
const port = new SerialPort(options, (e) => {
|
||||||
|
console.log("SerialPort open");
|
||||||
|
console.log(e);
|
||||||
|
if (e === null) {
|
||||||
|
// 打开成功 把串口发送出去
|
||||||
|
let data2 = {
|
||||||
|
type: 'connect'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
port.on('data', (data) => {
|
||||||
|
let data2 = {
|
||||||
|
type: 'received',
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
console.log(`Received data: ${data2}`)
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
})
|
||||||
|
setInterval(() => {
|
||||||
|
// console.log('setInterval')
|
||||||
|
if (!port.isOpen) {
|
||||||
|
// console.log('setInterval open')
|
||||||
|
port.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1000)
|
||||||
|
port.on('close', () => {
|
||||||
|
let data2 = {
|
||||||
|
type: 'close'
|
||||||
|
}
|
||||||
|
console.log(`SerialPort close: ${data2}`)
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
})
|
||||||
|
port.on('error', (e) => {
|
||||||
|
let data2 = {
|
||||||
|
type: 'error'
|
||||||
|
}
|
||||||
|
console.log(`SerialPort error: ${e}`)
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
})
|
||||||
|
|
||||||
|
GlobalSeriaPortIns = port;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送tcp 消息
|
||||||
|
* @param {*} args 包含host port msg type { 1 hex, 2 ascii}
|
||||||
|
* @param {*} event
|
||||||
|
*/
|
||||||
|
sendTcpSocket(args, event) {
|
||||||
|
const channel = 'controller.example.sendTcpSocket';
|
||||||
|
console.log("tcp params")
|
||||||
|
console.log(args);
|
||||||
|
const client = new net.Socket();
|
||||||
|
// client.setEncoding('ascii');
|
||||||
|
const HOST = args.host;
|
||||||
|
const PORT = args.port;
|
||||||
|
const msg = args.msg;
|
||||||
|
|
||||||
|
// const HOST = '192.168.5.134';
|
||||||
|
// const PORT = 9080;
|
||||||
|
client.connect(PORT, HOST, function () {
|
||||||
|
console.log('Connected to: ' + HOST + ':' + PORT);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('data', function (data) {
|
||||||
|
console.log('Received: ' + data);
|
||||||
|
event.sender.send(`${channel}`, data);
|
||||||
|
});
|
||||||
|
//两种码的发送 ok
|
||||||
|
// client.write('Hello, server!','ascii');
|
||||||
|
if (args.type == 1) {
|
||||||
|
client.write(msg, 'hex');
|
||||||
|
} else {
|
||||||
|
client.write(msg, 'ascii');
|
||||||
|
}
|
||||||
|
|
||||||
|
client.on('close', function () {
|
||||||
|
console.log('Connection closed');
|
||||||
|
});
|
||||||
|
client.end();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取extraResources 目录下的json 配置文件
|
||||||
|
* @param {*} name 配置文件名
|
||||||
|
* @returns 返回json 字符传
|
||||||
|
*/
|
||||||
|
getExResConfig(name) {
|
||||||
|
let configPath = path.join(Ps.getExtraResourcesDir(), name);;
|
||||||
|
|
||||||
|
console.log(configPath)
|
||||||
|
// let configJSON = null;
|
||||||
|
let dataString = null;
|
||||||
|
try {
|
||||||
|
// 同步读取配置文件
|
||||||
|
dataString = fs.readFileSync(configPath, 'utf8');
|
||||||
|
|
||||||
|
// 解析 JSON 格式的配置数据
|
||||||
|
// configJSON = JSON.parse(data);
|
||||||
|
// console.log('读取到的配置:', configJSON);
|
||||||
|
|
||||||
|
// 在这里可以根据需要使用配置数据进行操作
|
||||||
|
} catch (err) {
|
||||||
|
console.error('无法读取配置文件:', err);
|
||||||
|
}
|
||||||
|
return dataString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json数据库操作
|
||||||
|
*/
|
||||||
|
async jsondbOperation(args) {
|
||||||
|
const { action, info, delete_name, update_name, update_age, search_age, data_dir } = args;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
action,
|
||||||
|
result: null,
|
||||||
|
all_list: []
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'add':
|
||||||
|
data.result = await Services.get('database.jsondb').addTestData(info);
|
||||||
|
break;
|
||||||
|
case 'del':
|
||||||
|
data.result = await Services.get('database.jsondb').delTestData(delete_name);
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
data.result = await Services.get('database.jsondb').updateTestData(update_name, update_age);
|
||||||
|
break;
|
||||||
|
case 'get':
|
||||||
|
data.result = await Services.get('database.jsondb').getTestData(search_age);
|
||||||
|
break;
|
||||||
|
case 'getDataDir':
|
||||||
|
data.result = await Services.get('database.jsondb').getDataDir();
|
||||||
|
break;
|
||||||
|
case 'setDataDir':
|
||||||
|
data.result = await Services.get('database.jsondb').setCustomDataDir(data_dir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.all_list = await Services.get('database.jsondb').getAllTestData();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sqlite数据库操作
|
||||||
|
*/
|
||||||
|
async sqlitedbOperation(args) {
|
||||||
|
const { action, info, delete_name, update_name, update_age, search_age, data_dir } = args;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
action,
|
||||||
|
result: null,
|
||||||
|
all_list: [],
|
||||||
|
code: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// test
|
||||||
|
Services.get('database.sqlitedb').getDataDir();
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
data.code = -1;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'add':
|
||||||
|
data.result = await Services.get('database.sqlitedb').addTestDataSqlite(info);;
|
||||||
|
break;
|
||||||
|
case 'del':
|
||||||
|
data.result = await Services.get('database.sqlitedb').delTestDataSqlite(delete_name);;
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
data.result = await Services.get('database.sqlitedb').updateTestDataSqlite(update_name, update_age);
|
||||||
|
break;
|
||||||
|
case 'get':
|
||||||
|
data.result = await Services.get('database.sqlitedb').getTestDataSqlite(search_age);
|
||||||
|
break;
|
||||||
|
case 'getDataDir':
|
||||||
|
data.result = await Services.get('database.sqlitedb').getDataDir();
|
||||||
|
break;
|
||||||
|
case 'setDataDir':
|
||||||
|
data.result = await Services.get('database.sqlitedb').setCustomDataDir(data_dir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.all_list = await Services.get('database.sqlitedb').getAllTestDataSqlite();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用其它程序(exe、bash等可执行程序)
|
||||||
|
*/
|
||||||
|
openSoftware(softName) {
|
||||||
|
if (!softName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let softwarePath = path.join(Ps.getExtraResourcesDir(), softName);
|
||||||
|
Log.info('[openSoftware] softwarePath:', softwarePath);
|
||||||
|
|
||||||
|
// 检查程序是否存在
|
||||||
|
if (!fs.existsSync(softwarePath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 命令行字符串 并 执行
|
||||||
|
let cmdStr = 'start ' + softwarePath;
|
||||||
|
exec(cmdStr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有新版本
|
||||||
|
*/
|
||||||
|
checkForUpdater() {
|
||||||
|
Addon.get('autoUpdater').checkUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载新版本
|
||||||
|
*/
|
||||||
|
downloadApp() {
|
||||||
|
Addon.get('autoUpdater').download();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测http服务是否开启
|
||||||
|
*/
|
||||||
|
async checkHttpServer() {
|
||||||
|
const httpServerConfig = Conf.getValue('httpServer');
|
||||||
|
const url = httpServerConfig.protocol + httpServerConfig.host + ':' + httpServerConfig.port;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
enable: httpServerConfig.enable,
|
||||||
|
server: url
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个http请求访问此方法
|
||||||
|
*/
|
||||||
|
async doHttpRequest() {
|
||||||
|
const { CoreApp } = EE;
|
||||||
|
// http方法
|
||||||
|
const method = CoreApp.request.method;
|
||||||
|
// http get 参数
|
||||||
|
let params = CoreApp.request.query;
|
||||||
|
params = (params instanceof Object) ? params : JSON.parse(JSON.stringify(params));
|
||||||
|
// http post 参数
|
||||||
|
const body = CoreApp.request.body;
|
||||||
|
|
||||||
|
const httpInfo = {
|
||||||
|
method,
|
||||||
|
params,
|
||||||
|
body
|
||||||
|
}
|
||||||
|
Log.info('httpInfo:', httpInfo);
|
||||||
|
|
||||||
|
if (!body.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const dir = electronApp.getPath(body.id);
|
||||||
|
shell.openPath(dir);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个socket io请求访问此方法
|
||||||
|
*/
|
||||||
|
async doSocketRequest(args) {
|
||||||
|
if (!args.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const dir = electronApp.getPath(args.id);
|
||||||
|
shell.openPath(dir);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步消息类型
|
||||||
|
*/
|
||||||
|
async ipcInvokeMsg(args, event) {
|
||||||
|
let timeNow = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
const data = args + ' - ' + timeNow;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步消息类型
|
||||||
|
*/
|
||||||
|
async ipcSendSyncMsg(args) {
|
||||||
|
let timeNow = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
const data = args + ' - ' + timeNow;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 双向异步通信
|
||||||
|
*/
|
||||||
|
async ipcSendMsg(args, event) {
|
||||||
|
const { type, content } = args;
|
||||||
|
const data = await Services.get('framework').bothWayMessage(type, content, event);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不建议使用,请使用electron的api来获取文件的本机路径,然后读取并上传
|
||||||
|
* 使用http的files属性,实际上多余拷贝一次文件
|
||||||
|
*/
|
||||||
|
|
||||||
|
async uploadFile() {
|
||||||
|
const { CoreApp } = EE;
|
||||||
|
let tmpDir = Ps.getLogDir();
|
||||||
|
const files = CoreApp.request.files;
|
||||||
|
let file = files.file;
|
||||||
|
|
||||||
|
let tmpFilePath = path.join(tmpDir, file.originalFilename);
|
||||||
|
try {
|
||||||
|
let tmpFile = fs.readFileSync(file.filepath);
|
||||||
|
fs.writeFileSync(tmpFilePath, tmpFile);
|
||||||
|
} finally {
|
||||||
|
await fs.unlink(file.filepath, function () { });
|
||||||
|
}
|
||||||
|
const fileStream = fs.createReadStream(tmpFilePath);
|
||||||
|
const uploadRes = await Services.get('framework').uploadFileToSMMS(fileStream);
|
||||||
|
|
||||||
|
return uploadRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动java项目
|
||||||
|
*/
|
||||||
|
async startJavaServer() {
|
||||||
|
let data = {
|
||||||
|
code: 0,
|
||||||
|
msg: '',
|
||||||
|
server: ''
|
||||||
|
}
|
||||||
|
const javaCfg = Conf.getValue('addons.javaServer') || {};
|
||||||
|
if (!javaCfg.enable) {
|
||||||
|
data.code = -1;
|
||||||
|
data.msg = 'addon not enabled!';
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Addon.get('javaServer').createServer();
|
||||||
|
|
||||||
|
data.server = 'http://localhost:' + javaCfg.port;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭java项目
|
||||||
|
*/
|
||||||
|
async closeJavaServer() {
|
||||||
|
let data = {
|
||||||
|
code: 0,
|
||||||
|
msg: '',
|
||||||
|
}
|
||||||
|
const javaCfg = Conf.getValue('addons.javaServer') || {};
|
||||||
|
if (!javaCfg.enable) {
|
||||||
|
data.code = -1;
|
||||||
|
data.msg = 'addon not enabled!';
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Addon.get('javaServer').kill();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* java运行状态
|
||||||
|
*/
|
||||||
|
async runStatus() {
|
||||||
|
let data = {
|
||||||
|
code: 0,
|
||||||
|
msg: '',
|
||||||
|
flag: false
|
||||||
|
}
|
||||||
|
const flag = await Addon.get('javaServer').check();
|
||||||
|
//Log.info("[FrameworkController:runStatus] flag-----------"+flag);
|
||||||
|
data.flag = flag;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务
|
||||||
|
*/
|
||||||
|
someJob(args, event) {
|
||||||
|
let jobId = args.id;
|
||||||
|
let action = args.action;
|
||||||
|
|
||||||
|
let result;
|
||||||
|
switch (action) {
|
||||||
|
case 'create':
|
||||||
|
result = Services.get('framework').doJob(jobId, action, event);
|
||||||
|
break;
|
||||||
|
case 'close':
|
||||||
|
Services.get('framework').doJob(jobId, action, event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
jobId,
|
||||||
|
action,
|
||||||
|
result
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建任务池
|
||||||
|
*/
|
||||||
|
async createPool(args, event) {
|
||||||
|
let num = args.number;
|
||||||
|
Services.get('framework').doCreatePool(num, event);
|
||||||
|
|
||||||
|
// test monitor
|
||||||
|
Services.get('framework').monitorJob();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过进程池执行任务
|
||||||
|
*/
|
||||||
|
someJobByPool(args, event) {
|
||||||
|
let jobId = args.id;
|
||||||
|
let action = args.action;
|
||||||
|
|
||||||
|
let result;
|
||||||
|
switch (action) {
|
||||||
|
case 'run':
|
||||||
|
result = Services.get('framework').doJobByPool(jobId, action, event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
jobId,
|
||||||
|
action,
|
||||||
|
result
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试接口
|
||||||
|
*/
|
||||||
|
hello(args) {
|
||||||
|
Log.info('hello ', args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameworkController.toString = () => '[class FrameworkController]';
|
||||||
|
module.exports = FrameworkController;
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Controller } = require('ee-core');
|
||||||
|
const path = require('path');
|
||||||
|
const Ps = require('ee-core/ps');
|
||||||
|
const CoreWindow = require('ee-core/electron/window');
|
||||||
|
const Addon = require('ee-core/addon');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 硬件设备 - 功能demo
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class HardwareController extends Controller {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取打印机列表
|
||||||
|
*/
|
||||||
|
async getPrinterList () {
|
||||||
|
|
||||||
|
//主线程获取打印机列表
|
||||||
|
const win = CoreWindow.getMainWindow();
|
||||||
|
const list = await win.webContents.getPrintersAsync();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印
|
||||||
|
*/
|
||||||
|
print (args, event) {
|
||||||
|
const { view, deviceName } = args;
|
||||||
|
let content = null;
|
||||||
|
if (view.type == 'html') {
|
||||||
|
content = path.join('file://', Ps.getHomeDir(), view.content)
|
||||||
|
} else {
|
||||||
|
content = view.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opt = {
|
||||||
|
title: 'printer window',
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
width: 980,
|
||||||
|
height: 650
|
||||||
|
}
|
||||||
|
const name = 'window-printer';
|
||||||
|
const printWindow = Addon.get('window').create(name, opt);
|
||||||
|
|
||||||
|
printWindow.loadURL(content);
|
||||||
|
printWindow.webContents.once('did-finish-load', () => {
|
||||||
|
// 页面完全加载完成后,开始打印
|
||||||
|
printWindow.webContents.print({
|
||||||
|
silent: false, // 显示打印对话框
|
||||||
|
printBackground: true,
|
||||||
|
deviceName,
|
||||||
|
}, (success, failureReason) => {
|
||||||
|
const channel = 'controller.hardware.printStatus';
|
||||||
|
event.reply(`${channel}`, { success, failureReason });
|
||||||
|
printWindow.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HardwareController.toString = () => '[class HardwareController]';
|
||||||
|
module.exports = HardwareController;
|
||||||
|
|
@ -0,0 +1,634 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
const path = require('path');
|
||||||
|
const { Controller } = require('ee-core');
|
||||||
|
const {
|
||||||
|
app: electronApp, dialog, shell, Notification,
|
||||||
|
powerMonitor, screen, nativeTheme
|
||||||
|
} = require('electron');
|
||||||
|
const Conf = require('ee-core/config');
|
||||||
|
const Ps = require('ee-core/ps');
|
||||||
|
const Services = require('ee-core/services');
|
||||||
|
const Addon = require('ee-core/addon');
|
||||||
|
const { getNetworkIFaceOne, getMac, getAllMac, getAllPhysicsMac } = require('@lzwme/get-physical-address');
|
||||||
|
const os = require('os');
|
||||||
|
const shutdown = require('electron-shutdown-command');
|
||||||
|
// 终端命令
|
||||||
|
const { exec, execSync, execFile } = require("child_process");
|
||||||
|
// 声音控制库 https://github.com/LinusU/node-loudness
|
||||||
|
const loudness = require("loudness");
|
||||||
|
// 文件处理
|
||||||
|
const fs = require('fs');
|
||||||
|
/**
|
||||||
|
* 操作系统 - 功能demo
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class OsController extends Controller {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有方法接收两个参数
|
||||||
|
* @param args 前端传的参数
|
||||||
|
* @param event - ipc通信时才有值。详情见:控制器文档
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// 获取所有的网卡-无参数
|
||||||
|
async getAllMac(args) {
|
||||||
|
// 文档说明 https://github.com/lzwme/get-physical-address/blob/main/.github/README_zh-CN.md
|
||||||
|
const list = getAllPhysicsMac();
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
// 查找到指定进程并关闭? 注意 进程要全, 不然容易误杀
|
||||||
|
deviceKillName(name) {
|
||||||
|
// 最新 taskkill /F /IM program.exe 这个命令一句话能直接杀掉进程
|
||||||
|
const self = this;
|
||||||
|
let rebootShell = "tasklist|findstr " + name;
|
||||||
|
let command = exec(rebootShell, function (err, stdout, stderr) {
|
||||||
|
if (err || stderr) {
|
||||||
|
console.log("tasklist failed" + err + stderr);
|
||||||
|
} else {
|
||||||
|
const lines = stdout.split('\n')
|
||||||
|
console.log(lines);
|
||||||
|
for (let index = 0; index < lines.length; index++) {
|
||||||
|
const element = lines[index];
|
||||||
|
const strs = element.split(' ')
|
||||||
|
const firstNumber = strs.find(item => !isNaN(Number(item)) && item != '')
|
||||||
|
console.log('jincheng id :' + firstNumber);
|
||||||
|
self.deviceKillPid(firstNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
command.stdin.end();
|
||||||
|
command.on("close", function (code) {
|
||||||
|
console.log("tasklist", code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 杀掉pid 进程
|
||||||
|
deviceKillPid(pid) {
|
||||||
|
let rebootShell = "tskill " + pid;
|
||||||
|
let command = exec(rebootShell, function (err, stdout, stderr) {
|
||||||
|
if (err || stderr) {
|
||||||
|
console.log("tskill failed" + err + stderr);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
command.stdin.end();
|
||||||
|
command.on("close", function (code) {
|
||||||
|
console.log("tskill", code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动指定目录的程序 可能有权限问题, 方案1 打包后管理员权限执行, 2 按照egg 的方案把需要执行的程序拷贝到安装包
|
||||||
|
deviceStarExe(path) {
|
||||||
|
|
||||||
|
let rebootShell = 'start ' + path;
|
||||||
|
exec(rebootShell);
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 设备关机
|
||||||
|
*/
|
||||||
|
deviceShutdown() {
|
||||||
|
// 关机
|
||||||
|
// let shutdownShell = "shutdown -s -t 00";
|
||||||
|
console.log('deviceShutdown=============')
|
||||||
|
shutdown.shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
deviceRestart(args) {
|
||||||
|
// 重启
|
||||||
|
let rebootShell = "shutdown -r -t 0";
|
||||||
|
let command = exec(rebootShell, function (err, stdout, stderr) {
|
||||||
|
if (err || stderr) {
|
||||||
|
console.log("shutdown failed" + err + stderr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
command.stdin.end();
|
||||||
|
command.on("close", function (code) {
|
||||||
|
console.log("shutdown", code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 同步执行 声音设置 支持0-100
|
||||||
|
async deviceLoudness(args) {
|
||||||
|
const value = args.value;
|
||||||
|
//操作系统平台
|
||||||
|
const pf = os.platform();
|
||||||
|
console.log("OS: " + pf)
|
||||||
|
// 特殊处理以下
|
||||||
|
if (pf == "linux") {
|
||||||
|
let shellStr = "amixer -D pulse set Master " + value + "% unmute"
|
||||||
|
if (value == 0) {
|
||||||
|
shellStr = "amixer -D pulse set Master mute"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// else{
|
||||||
|
// shellStr = "amixer -c 0 set Master,0 100%,80% unmute"
|
||||||
|
// }
|
||||||
|
|
||||||
|
let command = exec(shellStr, function (err, stdout, stderr) {
|
||||||
|
if (err || stderr) {
|
||||||
|
console.log("amixer failed" + err + stderr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
command.stdin.end();
|
||||||
|
command.on("close", function (code) {
|
||||||
|
console.log("amixer", code);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
/// 兼容模式, try catch 如果第一种方式报错, 在采用第二种
|
||||||
|
try {
|
||||||
|
//0 为静音
|
||||||
|
if (value == 0) {
|
||||||
|
await loudness.setMuted(true)
|
||||||
|
return await loudness.getMuted()
|
||||||
|
}
|
||||||
|
// 设置声音改为不静音 且设置声音
|
||||||
|
await loudness.setMuted(false)
|
||||||
|
await loudness.setVolume(value)
|
||||||
|
const newValue = await loudness.getVolume();
|
||||||
|
return newValue;
|
||||||
|
} catch {
|
||||||
|
const maxVolume = 65535;
|
||||||
|
let volumeValue = Math.round((value / 100) * maxVolume);
|
||||||
|
|
||||||
|
// 确保音量值在有效范围内
|
||||||
|
volumeValue = Math.max(0, Math.min(maxVolume, volumeValue));
|
||||||
|
|
||||||
|
// 将音量值转换为字符串
|
||||||
|
const valueStr = volumeValue.toString();
|
||||||
|
|
||||||
|
let excPath = path.join(Ps.getExtraResourcesDir(), 'nircmd.exe');
|
||||||
|
|
||||||
|
execFile(excPath, ['setsysvolume', valueStr], (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(' Nircmd error: ', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('Nircmd ok ');
|
||||||
|
});
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取电脑信息
|
||||||
|
async getOSMessage(args) {
|
||||||
|
var OSDic = {};
|
||||||
|
var dealTime = (seconds) => {
|
||||||
|
var seconds = seconds | 0;
|
||||||
|
var day = (seconds / (3600 * 24)) | 0;
|
||||||
|
var hours = ((seconds - day * 3600) / 3600) | 0;
|
||||||
|
var minutes = ((seconds - day * 3600 * 24 - hours * 3600) / 60) | 0;
|
||||||
|
var second = seconds % 60;
|
||||||
|
(day < 10) && (day = '0' + day);
|
||||||
|
(hours < 10) && (hours = '0' + hours);
|
||||||
|
(minutes < 10) && (minutes = '0' + minutes);
|
||||||
|
(second < 10) && (second = '0' + second);
|
||||||
|
return [day, hours, minutes, second].join(':');
|
||||||
|
};
|
||||||
|
|
||||||
|
var dealMem = (mem) => {
|
||||||
|
var G = 0,
|
||||||
|
M = 0,
|
||||||
|
KB = 0;
|
||||||
|
(mem > (1 << 30)) && (G = (mem / (1 << 30)).toFixed(2));
|
||||||
|
(mem > (1 << 20)) && (mem < (1 << 30)) && (M = (mem / (1 << 20)).toFixed(2));
|
||||||
|
(mem > (1 << 10)) && (mem > (1 << 20)) && (KB = (mem / (1 << 10)).toFixed(2));
|
||||||
|
return G > 0 ? G + 'G' : M > 0 ? M + 'M' : KB > 0 ? KB + 'KB' : mem + 'B';
|
||||||
|
};
|
||||||
|
|
||||||
|
//cpu架构
|
||||||
|
const arch = os.arch();
|
||||||
|
// console.log("cpu架构:" + arch);
|
||||||
|
OSDic["arch"] = arch;
|
||||||
|
|
||||||
|
//操作系统内核
|
||||||
|
const kernel = os.type();
|
||||||
|
// console.log("操作系统内核:" + kernel);
|
||||||
|
OSDic["kernel"] = kernel;
|
||||||
|
|
||||||
|
//操作系统平台
|
||||||
|
const pf = os.platform();
|
||||||
|
// console.log("平台:" + pf);
|
||||||
|
OSDic["pf"] = pf;
|
||||||
|
|
||||||
|
//系统开机时间
|
||||||
|
const uptime = os.uptime();
|
||||||
|
// console.log("开机时间:" + dealTime(uptime));
|
||||||
|
OSDic["uptime"] = uptime;
|
||||||
|
//主机名
|
||||||
|
const hn = os.hostname();
|
||||||
|
// console.log("主机名:" + hn);
|
||||||
|
OSDic["hostname"] = hn;
|
||||||
|
// //主目录
|
||||||
|
// const hdir = os.homedir();
|
||||||
|
// console.log("主目录:" + hdir);
|
||||||
|
// OSDic["homedir"] = hdir;
|
||||||
|
|
||||||
|
//内存
|
||||||
|
const totalMem = os.totalmem();
|
||||||
|
const freeMem = os.freemem();
|
||||||
|
// console.log("内存大小:" + dealMem(totalMem) + ' 空闲内存:' + dealMem(freeMem));
|
||||||
|
OSDic["totalmem"] = totalMem;
|
||||||
|
OSDic["freeMem"] = freeMem;
|
||||||
|
//cpu
|
||||||
|
const cpus = os.cpus();
|
||||||
|
OSDic["cpuModel"] = cpus[0]["model"];
|
||||||
|
// OSDic["cpus"] = cpus;
|
||||||
|
// console.log('*****cpu信息*******');
|
||||||
|
// cpus.forEach((cpu, idx, arr) => {
|
||||||
|
// var times = cpu.times;
|
||||||
|
// console.log(`cpu${idx}:`);
|
||||||
|
// console.log(`型号:${cpu.model}`);
|
||||||
|
// console.log(`频率:${cpu.speed}MHz`);
|
||||||
|
// console.log(`使用率:${((1 - times.idle / (times.idle + times.user + times.nice + times.sys + times.irq)) * 100).toFixed(2)}%`);
|
||||||
|
// });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const volumeValue = await loudness.getVolume()
|
||||||
|
|
||||||
|
OSDic["volume"] = volumeValue;
|
||||||
|
console.log('volumeValue' + volumeValue);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return OSDic;
|
||||||
|
}
|
||||||
|
// const volume = await loudness.getVolume()
|
||||||
|
// OSDic["volume"] = volume;
|
||||||
|
// return OSDic;
|
||||||
|
// }
|
||||||
|
/**
|
||||||
|
* 获取当前目录的配置
|
||||||
|
* @param {*} name 配置文件名
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getCurrentDirectoryConfig(name) {
|
||||||
|
let configPath = '';
|
||||||
|
configPath = path.join(Ps.getExtraResourcesDir(), name);
|
||||||
|
console.log(configPath)
|
||||||
|
// let configJSON = null;
|
||||||
|
let dataString = null;
|
||||||
|
try {
|
||||||
|
// 同步读取配置文件
|
||||||
|
dataString = fs.readFileSync(configPath, 'utf8');
|
||||||
|
|
||||||
|
// 解析 JSON 格式的配置数据
|
||||||
|
// configJSON = JSON.parse(data);
|
||||||
|
// console.log('读取到的配置:', configJSON);
|
||||||
|
|
||||||
|
// 在这里可以根据需要使用配置数据进行操作
|
||||||
|
} catch (err) {
|
||||||
|
console.error('无法读取配置文件:', err);
|
||||||
|
}
|
||||||
|
return dataString;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 消息提示对话框
|
||||||
|
*/
|
||||||
|
messageShow() {
|
||||||
|
dialog.showMessageBoxSync({
|
||||||
|
type: 'info', // "none", "info", "error", "question" 或者 "warning"
|
||||||
|
title: '自定义标题-message',
|
||||||
|
message: '自定义消息内容',
|
||||||
|
detail: '其它的额外信息'
|
||||||
|
})
|
||||||
|
|
||||||
|
return '打开了消息框';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息提示与确认对话框
|
||||||
|
*/
|
||||||
|
messageShowConfirm() {
|
||||||
|
const res = dialog.showMessageBoxSync({
|
||||||
|
type: 'info',
|
||||||
|
title: '自定义标题-message',
|
||||||
|
message: '自定义消息内容',
|
||||||
|
detail: '其它的额外信息',
|
||||||
|
cancelId: 1, // 用于取消对话框的按钮的索引
|
||||||
|
defaultId: 0, // 设置默认选中的按钮
|
||||||
|
buttons: ['确认', '取消'], // 按钮及索引
|
||||||
|
})
|
||||||
|
let data = (res === 0) ? '点击确认按钮' : '点击取消按钮';
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择目录
|
||||||
|
*/
|
||||||
|
selectFolder() {
|
||||||
|
const filePaths = dialog.showOpenDialogSync({
|
||||||
|
properties: ['openDirectory', 'createDirectory']
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_.isEmpty(filePaths)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePaths[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开目录
|
||||||
|
*/
|
||||||
|
openDirectory(args) {
|
||||||
|
if (!args.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let dir = '';
|
||||||
|
if (path.isAbsolute(args.id)) {
|
||||||
|
dir = args.id;
|
||||||
|
} else {
|
||||||
|
dir = electronApp.getPath(args.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
shell.openPath(dir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择图片
|
||||||
|
*/
|
||||||
|
selectPic() {
|
||||||
|
const filePaths = dialog.showOpenDialogSync({
|
||||||
|
title: 'select pic',
|
||||||
|
properties: ['openFile'],
|
||||||
|
filters: [
|
||||||
|
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
|
||||||
|
]
|
||||||
|
});
|
||||||
|
if (_.isEmpty(filePaths)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePaths[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载视图内容
|
||||||
|
*/
|
||||||
|
loadViewContent(args) {
|
||||||
|
const { type, content } = args;
|
||||||
|
let contentUrl = content;
|
||||||
|
if (type == 'html') {
|
||||||
|
contentUrl = path.join('file://', electronApp.getAppPath(), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.get('os').createBrowserView(contentUrl);
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除视图内容
|
||||||
|
*/
|
||||||
|
removeViewContent() {
|
||||||
|
Services.get('os').removeBrowserView();
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开新窗口
|
||||||
|
*/
|
||||||
|
createWindow(args) {
|
||||||
|
const { type, content, windowName, windowTitle } = args;
|
||||||
|
let contentUrl = null;
|
||||||
|
if (type == 'html') {
|
||||||
|
contentUrl = path.join('file://', electronApp.getAppPath(), content)
|
||||||
|
} else if (type == 'web') {
|
||||||
|
contentUrl = content;
|
||||||
|
} else if (type == 'vue') {
|
||||||
|
let addr = 'http://localhost:17680'
|
||||||
|
if (Ps.isProd()) {
|
||||||
|
const mainServer = Conf.getValue('mainServer');
|
||||||
|
if (Conf.isFileProtocol(mainServer)) {
|
||||||
|
addr = mainServer.protocol + path.join(Ps.getHomeDir(), mainServer.indexPath);
|
||||||
|
} else {
|
||||||
|
addr = mainServer.protocol + mainServer.host + ':' + mainServer.port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentUrl = addr + content;
|
||||||
|
} else {
|
||||||
|
// some
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('contentUrl: ', contentUrl);
|
||||||
|
let opt = {
|
||||||
|
title: windowTitle
|
||||||
|
}
|
||||||
|
const win = Addon.get('window').create(windowName, opt);
|
||||||
|
const winContentsId = win.webContents.id;
|
||||||
|
|
||||||
|
// load page
|
||||||
|
win.loadURL(contentUrl);
|
||||||
|
|
||||||
|
return winContentsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取窗口contents id
|
||||||
|
*/
|
||||||
|
getWCid(args) {
|
||||||
|
// 主窗口的name默认是main,其它窗口name开发者自己定义
|
||||||
|
const name = args;
|
||||||
|
const id = Addon.get('window').getWCid(name);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载扩展程序
|
||||||
|
*/
|
||||||
|
// async loadExtension (args) {
|
||||||
|
// const crxFile = args[0];
|
||||||
|
// if (_.isEmpty(crxFile)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// const extensionId = path.basename(crxFile, '.crx');
|
||||||
|
// const chromeExtensionDir = chromeExtension.getDirectory();
|
||||||
|
// const extensionDir = path.join(chromeExtensionDir, extensionId);
|
||||||
|
|
||||||
|
// Log.info("[api] [example] [loadExtension] extension id:", extensionId);
|
||||||
|
// unzip(crxFile, extensionDir).then(() => {
|
||||||
|
// Log.info("[api] [example] [loadExtension] unzip success!");
|
||||||
|
// chromeExtension.load(extensionId);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建系统通知
|
||||||
|
*/
|
||||||
|
sendNotification(args, event) {
|
||||||
|
const { title, subtitle, body, silent } = args;
|
||||||
|
|
||||||
|
if (!Notification.isSupported()) {
|
||||||
|
return '当前系统不支持通知';
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = {};
|
||||||
|
if (!_.isEmpty(title)) {
|
||||||
|
options.title = title;
|
||||||
|
}
|
||||||
|
if (!_.isEmpty(subtitle)) {
|
||||||
|
options.subtitle = subtitle;
|
||||||
|
}
|
||||||
|
if (!_.isEmpty(body)) {
|
||||||
|
options.body = body;
|
||||||
|
}
|
||||||
|
if (!_.isEmpty(silent)) {
|
||||||
|
options.silent = silent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.get('os').createNotification(options, event);
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电源监控
|
||||||
|
*/
|
||||||
|
initPowerMonitor(args, event) {
|
||||||
|
const channel = 'controller.os.initPowerMonitor';
|
||||||
|
powerMonitor.on('on-ac', (e) => {
|
||||||
|
let data = {
|
||||||
|
type: 'on-ac',
|
||||||
|
msg: '接入了电源'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data)
|
||||||
|
});
|
||||||
|
|
||||||
|
powerMonitor.on('on-battery', (e) => {
|
||||||
|
let data = {
|
||||||
|
type: 'on-battery',
|
||||||
|
msg: '使用电池中'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data)
|
||||||
|
});
|
||||||
|
|
||||||
|
powerMonitor.on('lock-screen', (e) => {
|
||||||
|
let data = {
|
||||||
|
type: 'lock-screen',
|
||||||
|
msg: '锁屏了'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data)
|
||||||
|
});
|
||||||
|
|
||||||
|
powerMonitor.on('unlock-screen', (e) => {
|
||||||
|
let data = {
|
||||||
|
type: 'unlock-screen',
|
||||||
|
msg: '解锁了'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data)
|
||||||
|
});
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取屏幕信息
|
||||||
|
*/
|
||||||
|
getScreen(args) {
|
||||||
|
let data = [];
|
||||||
|
let res = {};
|
||||||
|
if (args == 0) {
|
||||||
|
let res = screen.getCursorScreenPoint();
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
title: '横坐标',
|
||||||
|
desc: res.x
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '纵坐标',
|
||||||
|
desc: res.y
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
if (args == 1) {
|
||||||
|
res = screen.getPrimaryDisplay();
|
||||||
|
}
|
||||||
|
if (args == 2) {
|
||||||
|
let resArr = screen.getAllDisplays();
|
||||||
|
// 数组,只取一个吧
|
||||||
|
res = resArr[0];
|
||||||
|
}
|
||||||
|
// Log.info('[electron] [ipc] [example] [getScreen] res:', res);
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
title: '分辨率',
|
||||||
|
desc: res.bounds.width + ' x ' + res.bounds.height
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '单色显示器',
|
||||||
|
desc: res.monochrome ? '是' : '否'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '色深',
|
||||||
|
desc: res.colorDepth
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '色域',
|
||||||
|
desc: res.colorSpace
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'scaleFactor',
|
||||||
|
desc: res.scaleFactor
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '加速器',
|
||||||
|
desc: res.accelerometerSupport
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '触控',
|
||||||
|
desc: res.touchSupport == 'unknown' ? '不支持' : '支持'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统主题
|
||||||
|
*/
|
||||||
|
getTheme() {
|
||||||
|
let theme = 'system';
|
||||||
|
if (nativeTheme.shouldUseHighContrastColors) {
|
||||||
|
theme = 'light';
|
||||||
|
} else if (nativeTheme.shouldUseInvertedColorScheme) {
|
||||||
|
theme = 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置系统主题
|
||||||
|
*/
|
||||||
|
setTheme(args) {
|
||||||
|
|
||||||
|
// TODO 好像没有什么明显效果
|
||||||
|
nativeTheme.themeSource = args;
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OsController.toString = () => '[class OsController]';
|
||||||
|
module.exports = OsController;
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
const { Application } = require('ee-core');
|
||||||
|
const { app, globalShortcut } = require('electron')
|
||||||
|
const EE = require('ee-core/ee');
|
||||||
|
// 是否用户关闭
|
||||||
|
global.isUserExit = false;
|
||||||
|
const { app: electronApp } = require("electron");
|
||||||
|
|
||||||
|
class Index extends Application {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
// this === eeApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* core app have been loaded
|
||||||
|
*/
|
||||||
|
async ready () {
|
||||||
|
// do some things
|
||||||
|
electronApp.commandLine.appendSwitch('enable-webgl');
|
||||||
|
electronApp.commandLine.appendSwitch("disable-web-security");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* electron app ready
|
||||||
|
*/
|
||||||
|
async electronAppReady () {
|
||||||
|
// do some things
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* main window have been loaded
|
||||||
|
*/
|
||||||
|
async windowReady () {
|
||||||
|
// todo: 注册一个全局快捷键退出
|
||||||
|
globalShortcut.register('CommandOrControl+Shift+Z', () => {
|
||||||
|
console.log("Shift+Alt+Z+M is click")
|
||||||
|
// const { CoreApp } = EE;
|
||||||
|
// CoreApp.appQuit();
|
||||||
|
global.isUserExit = true;
|
||||||
|
app.quit();
|
||||||
|
// const channel = 'shortcut-key';
|
||||||
|
// this.electron.mainWindow.webContents.send(channel, "Shift+Alt+Z+M");
|
||||||
|
})
|
||||||
|
// do some things
|
||||||
|
// 延迟加载,无白屏
|
||||||
|
const winOpt = this.config.windowsOption;
|
||||||
|
if (winOpt.show == false) {
|
||||||
|
const win = this.electron.mainWindow;
|
||||||
|
win.once('ready-to-show', () => {
|
||||||
|
win.show();
|
||||||
|
win.focus();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* before app close
|
||||||
|
*/
|
||||||
|
async beforeClose () {
|
||||||
|
// do some things
|
||||||
|
console.log(" index.js beforeClose")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Index.toString = () => '[class Index]';
|
||||||
|
module.exports = Index;
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
|
||||||
|
exports.welcome = function () {
|
||||||
|
Log.info('[child-process] [jobs/example/hello] welcome ! ');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
const Job = require('ee-core/jobs/baseJobClass');
|
||||||
|
const Loader = require('ee-core/loader');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const Ps = require('ee-core/ps');
|
||||||
|
const { childMessage } = require('ee-core/message');
|
||||||
|
const Hello = Loader.requireJobsModule('./example/hello');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* example - TimerJob
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class TimerJob extends Job {
|
||||||
|
|
||||||
|
constructor(params) {
|
||||||
|
super();
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle()方法是必要的,且会被自动调用
|
||||||
|
*/
|
||||||
|
async handle () {
|
||||||
|
Log.info("[child-process] TimerJob params: ", this.params);
|
||||||
|
|
||||||
|
// 计时器任务
|
||||||
|
|
||||||
|
let number = 0;
|
||||||
|
let jobId = this.params.jobId;
|
||||||
|
let eventName = 'job-timer-progress-' + jobId;
|
||||||
|
let timer = setInterval(function() {
|
||||||
|
Hello.welcome();
|
||||||
|
|
||||||
|
childMessage.send(eventName, {jobId, number, end: false});
|
||||||
|
number++;
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// 用 setTimeout 模拟任务运行时长
|
||||||
|
setTimeout(() => {
|
||||||
|
// 关闭定时器
|
||||||
|
clearInterval(timer);
|
||||||
|
|
||||||
|
// 任务结束,重置前端显示
|
||||||
|
childMessage.send(eventName, {jobId, number:0, pid:0, end: true});
|
||||||
|
|
||||||
|
// 如果是childJob任务,必须调用 Ps.exit() 方法,让进程退出,否则会常驻内存
|
||||||
|
// 如果是childPoolJob任务,常驻内存,等待下一个业务
|
||||||
|
if (Ps.isChildJob()) {
|
||||||
|
Ps.exit();
|
||||||
|
}
|
||||||
|
}, 10 * 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerJob.toString = () => '[class TimerJob]';
|
||||||
|
module.exports = TimerJob;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* 如果启用了上下文隔离,渲染进程无法使用electron的api,
|
||||||
|
* 可通过contextBridge 导出api给渲染进程使用
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { contextBridge, ipcRenderer } = require('electron')
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('electron', {
|
||||||
|
ipcRenderer: ipcRenderer,
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*************************************************
|
||||||
|
** preload为预加载模块,该文件将会在程序启动时加载 **
|
||||||
|
*************************************************/
|
||||||
|
const Addon = require('ee-core/addon');
|
||||||
|
const Services = require('ee-core/services');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预加载模块入口
|
||||||
|
*/
|
||||||
|
module.exports = async () => {
|
||||||
|
|
||||||
|
// 已实现的功能模块,可选择性使用和修改
|
||||||
|
Addon.get('tray').create();
|
||||||
|
Addon.get('security').create();
|
||||||
|
Addon.get('awaken').create();
|
||||||
|
Addon.get('autoUpdater').create();
|
||||||
|
|
||||||
|
//Services.get('cross').createGoServer();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
const Cross = require('ee-core/cross');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const Ps = require('ee-core/ps');
|
||||||
|
const path = require("path");
|
||||||
|
const Is = require('ee-core/utils/is');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cross(service层为单例)
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class CrossService extends Service {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
// 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(Ps.getExtraResourcesDir(), 'goapp'),
|
||||||
|
directory: Ps.getExtraResourcesDir(),
|
||||||
|
args: ['--port=7073'],
|
||||||
|
appExit: true,
|
||||||
|
}
|
||||||
|
const entity = await Cross.run(serviceName, opt);
|
||||||
|
Log.info('server name:', entity.name);
|
||||||
|
Log.info('server config:', entity.config);
|
||||||
|
Log.info('server url:', entity.getUrl());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create java server
|
||||||
|
*/
|
||||||
|
async createJavaServer() {
|
||||||
|
const serviceName = "java";
|
||||||
|
const jarPath = path.join(Ps.getExtraResourcesDir(), 'java-app.jar');
|
||||||
|
const opt = {
|
||||||
|
name: 'javaapp',
|
||||||
|
cmd: path.join(Ps.getExtraResourcesDir(), 'jre1.8.0_201/bin/javaw.exe'),
|
||||||
|
directory: Ps.getExtraResourcesDir(),
|
||||||
|
args: ['-jar', '-server', '-Xms512M', '-Xmx512M', '-Xss512k', '-Dspring.profiles.active=prod', `-Dserver.port=18080`, `-Dlogging.file.path=${Ps.getLogDir()}`, `${jarPath}`],
|
||||||
|
appExit: false,
|
||||||
|
}
|
||||||
|
if (Is.macOS()) {
|
||||||
|
// Setup Java program
|
||||||
|
opt.cmd = path.join(Ps.getExtraResourcesDir(), 'jre1.8.0_201/Contents/Home/bin/java');
|
||||||
|
}
|
||||||
|
if (Is.linux()) {
|
||||||
|
// Setup Java program
|
||||||
|
}
|
||||||
|
|
||||||
|
const entity = await Cross.run(serviceName, opt);
|
||||||
|
Log.info('server name:', entity.name);
|
||||||
|
Log.info('server config:', entity.config);
|
||||||
|
Log.info('server url:', Cross.getUrl(entity.name));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
// 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(Ps.getExtraResourcesDir(), 'py', 'pyapp'),
|
||||||
|
directory: path.join(Ps.getExtraResourcesDir(), 'py'),
|
||||||
|
args: ['--port=7074'],
|
||||||
|
windowsExtname: true,
|
||||||
|
appExit: true,
|
||||||
|
}
|
||||||
|
const entity = await Cross.run(serviceName, opt);
|
||||||
|
Log.info('server name:', entity.name);
|
||||||
|
Log.info('server config:', entity.config);
|
||||||
|
Log.info('server url:', entity.getUrl());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CrossService.toString = () => '[class CrossService]';
|
||||||
|
module.exports = CrossService;
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
const Storage = require('ee-core/storage');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* json数据存储
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class JsondbService extends Service {
|
||||||
|
|
||||||
|
constructor (ctx) {
|
||||||
|
super(ctx);
|
||||||
|
|
||||||
|
// jsondb数据库
|
||||||
|
this.jsonFile = 'demo';
|
||||||
|
this.demoDB = Storage.connection(this.jsonFile);
|
||||||
|
this.demoDBKey = {
|
||||||
|
test_data: 'test_data'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 增 Test data
|
||||||
|
*/
|
||||||
|
async addTestData(user) {
|
||||||
|
const key = this.demoDBKey.test_data;
|
||||||
|
if (!this.demoDB.db.has(key).value()) {
|
||||||
|
this.demoDB.db.set(key, []).write();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = this.demoDB.db
|
||||||
|
.get(key)
|
||||||
|
.push(user)
|
||||||
|
.write();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 删 Test data
|
||||||
|
*/
|
||||||
|
async delTestData(name = '') {
|
||||||
|
const key = this.demoDBKey.test_data;
|
||||||
|
const data = this.demoDB.db
|
||||||
|
.get(key)
|
||||||
|
.remove({name: name})
|
||||||
|
.write();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 改 Test data
|
||||||
|
*/
|
||||||
|
async updateTestData(name= '', age = 0) {
|
||||||
|
const key = this.demoDBKey.test_data;
|
||||||
|
const data = this.demoDB.db
|
||||||
|
.get(key)
|
||||||
|
.find({name: name}) // 修改找到的第一个数据,貌似无法批量修改 todo
|
||||||
|
.assign({age: age})
|
||||||
|
.write();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 查 Test data
|
||||||
|
*/
|
||||||
|
async getTestData(age = 0) {
|
||||||
|
const key = this.demoDBKey.test_data;
|
||||||
|
let data = this.demoDB.db
|
||||||
|
.get(key)
|
||||||
|
//.find({age: age}) 查找单个
|
||||||
|
.filter(function(o) {
|
||||||
|
let isHas = true;
|
||||||
|
isHas = age === o.age ? true : false;
|
||||||
|
return isHas;
|
||||||
|
})
|
||||||
|
//.orderBy(['age'], ['name']) 排序
|
||||||
|
//.slice(0, 10) 分页
|
||||||
|
.value();
|
||||||
|
|
||||||
|
if (_.isEmpty(data)) {
|
||||||
|
data = []
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* all Test data
|
||||||
|
*/
|
||||||
|
async getAllTestData() {
|
||||||
|
const key = this.demoDBKey.test_data;
|
||||||
|
if (!this.demoDB.db.has(key).value()) {
|
||||||
|
this.demoDB.db.set(key, []).write();
|
||||||
|
}
|
||||||
|
let data = this.demoDB.db
|
||||||
|
.get(key)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
if (_.isEmpty(data)) {
|
||||||
|
data = []
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get data dir (sqlite)
|
||||||
|
*/
|
||||||
|
async getDataDir() {
|
||||||
|
const dir = this.demoDB.getStorageDir();
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set custom data dir (sqlite)
|
||||||
|
*/
|
||||||
|
async setCustomDataDir(dir) {
|
||||||
|
if (_.isEmpty(dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the absolute path of the db file
|
||||||
|
const dbFile = path.join(dir, this.jsonFile);
|
||||||
|
this.demoDB = Storage.connection(dbFile);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsondbService.toString = () => '[class JsondbService]';
|
||||||
|
module.exports = JsondbService;
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
const Storage = require('ee-core/storage');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sqlite数据存储
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class SqlitedbService extends Service {
|
||||||
|
|
||||||
|
constructor (ctx) {
|
||||||
|
super(ctx);
|
||||||
|
|
||||||
|
this.sqliteFile = 'sqlite-demo.db';
|
||||||
|
let sqliteOptions = {
|
||||||
|
driver: 'sqlite',
|
||||||
|
default: {
|
||||||
|
timeout: 6000,
|
||||||
|
verbose: console.log // 打印sql语法
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.demoSqliteDB = Storage.connection(this.sqliteFile, sqliteOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 检查并创建表 (sqlite)
|
||||||
|
*/
|
||||||
|
async checkAndCreateTableSqlite(tableName = '') {
|
||||||
|
if (_.isEmpty(tableName)) {
|
||||||
|
throw new Error(`table name is required`);
|
||||||
|
}
|
||||||
|
// 检查表是否存在
|
||||||
|
const userTable = this.demoSqliteDB.db.prepare('SELECT * FROM sqlite_master WHERE type=? AND name = ?');
|
||||||
|
const result = userTable.get('table', tableName);
|
||||||
|
//console.log('result:', result);
|
||||||
|
if (result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建表
|
||||||
|
const create_table_user =
|
||||||
|
`CREATE TABLE ${tableName}
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name CHAR(50) NOT NULL,
|
||||||
|
age INT
|
||||||
|
);`
|
||||||
|
this.demoSqliteDB.db.exec(create_table_user);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 增 Test data (sqlite)
|
||||||
|
*/
|
||||||
|
async addTestDataSqlite(data) {
|
||||||
|
//console.log("add data:", data);
|
||||||
|
|
||||||
|
let table = 'user';
|
||||||
|
await this.checkAndCreateTableSqlite(table);
|
||||||
|
|
||||||
|
const insert = this.demoSqliteDB.db.prepare(`INSERT INTO ${table} (name, age) VALUES (@name, @age)`);
|
||||||
|
insert.run(data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 删 Test data (sqlite)
|
||||||
|
*/
|
||||||
|
async delTestDataSqlite(name = '') {
|
||||||
|
//console.log("delete name:", name);
|
||||||
|
|
||||||
|
let table = 'user';
|
||||||
|
await this.checkAndCreateTableSqlite(table);
|
||||||
|
|
||||||
|
const delUser = this.demoSqliteDB.db.prepare(`DELETE FROM ${table} WHERE name = ?`);
|
||||||
|
delUser.run(name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 改 Test data (sqlite)
|
||||||
|
*/
|
||||||
|
async updateTestDataSqlite(name= '', age = 0) {
|
||||||
|
//console.log("update :", {name, age});
|
||||||
|
|
||||||
|
let table = 'user';
|
||||||
|
await this.checkAndCreateTableSqlite(table);
|
||||||
|
|
||||||
|
const updateUser = this.demoSqliteDB.db.prepare(`UPDATE ${table} SET age = ? WHERE name = ?`);
|
||||||
|
updateUser.run(age, name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 查 Test data (sqlite)
|
||||||
|
*/
|
||||||
|
async getTestDataSqlite(age = 0) {
|
||||||
|
//console.log("select :", {age});
|
||||||
|
|
||||||
|
let table = 'user';
|
||||||
|
await this.checkAndCreateTableSqlite(table);
|
||||||
|
|
||||||
|
const selectUser = this.demoSqliteDB.db.prepare(`SELECT * FROM ${table} WHERE age = @age`);
|
||||||
|
const users = selectUser.all({age: age});
|
||||||
|
//console.log("select users:", users);
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* all Test data (sqlite)
|
||||||
|
*/
|
||||||
|
async getAllTestDataSqlite() {
|
||||||
|
//console.log("select all user");
|
||||||
|
|
||||||
|
let table = 'user';
|
||||||
|
await this.checkAndCreateTableSqlite(table);
|
||||||
|
|
||||||
|
const selectAllUser = this.demoSqliteDB.db.prepare(`SELECT * FROM ${table} `);
|
||||||
|
const allUser = selectAllUser.all();
|
||||||
|
//console.log("select allUser:", allUser);
|
||||||
|
return allUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get data dir (sqlite)
|
||||||
|
*/
|
||||||
|
async getDataDir() {
|
||||||
|
const dir = this.demoSqliteDB.getStorageDir();
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set custom data dir (sqlite)
|
||||||
|
*/
|
||||||
|
async setCustomDataDir(dir) {
|
||||||
|
if (_.isEmpty(dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the absolute path of the db file
|
||||||
|
const dbFile = path.join(dir, this.sqliteFile);
|
||||||
|
const sqliteOptions = {
|
||||||
|
driver: 'sqlite',
|
||||||
|
default: {
|
||||||
|
timeout: 6000,
|
||||||
|
verbose: console.log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.demoSqliteDB = Storage.connection(dbFile, sqliteOptions);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlitedbService.toString = () => '[class SqlitedbService]';
|
||||||
|
module.exports = SqlitedbService;
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* effect(service层为单例)
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class EffectService extends Service {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async test(args) {
|
||||||
|
let obj = {
|
||||||
|
status:'ok',
|
||||||
|
params: args
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectService.toString = () => '[class EffectService]';
|
||||||
|
module.exports = EffectService;
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
const Services = require('ee-core/services');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例服务(service层为单例)
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class ExampleService extends Service {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async test(args) {
|
||||||
|
let obj = {
|
||||||
|
status:'ok',
|
||||||
|
params: args
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info('ExampleService obj:', obj);
|
||||||
|
|
||||||
|
Services.get('framework').test('egg');
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExampleService.toString = () => '[class ExampleService]';
|
||||||
|
module.exports = ExampleService;
|
||||||
|
|
@ -0,0 +1,195 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
const Log = require('ee-core/log');
|
||||||
|
const { ChildJob, ChildPoolJob } = require('ee-core/jobs');
|
||||||
|
const HttpClient = require('ee-core/httpclient');
|
||||||
|
const Ps = require('ee-core/ps');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* framework
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class FrameworkService extends Service {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
|
||||||
|
// 在构造函数中初始化一些变量
|
||||||
|
this.myTimer = null;
|
||||||
|
this.myJob = new ChildJob();
|
||||||
|
this.myJobPool = new ChildPoolJob();
|
||||||
|
this.taskForJob = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async test(args) {
|
||||||
|
let obj = {
|
||||||
|
status:'ok',
|
||||||
|
params: args
|
||||||
|
}
|
||||||
|
Log.info('FrameworkService obj:', obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ipc通信(双向)
|
||||||
|
*/
|
||||||
|
bothWayMessage(type, content, event) {
|
||||||
|
// 前端ipc频道 channel
|
||||||
|
const channel = 'controller.framework.ipcSendMsg';
|
||||||
|
|
||||||
|
if (type == 'start') {
|
||||||
|
// 每隔1秒,向前端页面发送消息
|
||||||
|
// 用定时器模拟
|
||||||
|
this.myTimer = setInterval(function(e, c, msg) {
|
||||||
|
let timeNow = Date.now();
|
||||||
|
let data = msg + ':' + timeNow;
|
||||||
|
e.reply(`${c}`, data)
|
||||||
|
}, 1000, event, channel, content)
|
||||||
|
|
||||||
|
return '开始了'
|
||||||
|
} else if (type == 'end') {
|
||||||
|
clearInterval(this.myTimer);
|
||||||
|
return '停止了'
|
||||||
|
} else {
|
||||||
|
return 'ohther'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行任务
|
||||||
|
*/
|
||||||
|
doJob(jobId, action, event) {
|
||||||
|
let res = {};
|
||||||
|
let oneTask;
|
||||||
|
const channel = 'controller.framework.timerJobProgress';
|
||||||
|
if (action == 'create') {
|
||||||
|
// 执行任务及监听进度
|
||||||
|
let eventName = 'job-timer-progress-' + jobId;
|
||||||
|
const timerTask = this.myJob.exec('./jobs/example/timer', {jobId});
|
||||||
|
timerTask.emitter.on(eventName, (data) => {
|
||||||
|
Log.info('[main-process] timerTask, from TimerJob data:', data);
|
||||||
|
// 发送数据到渲染进程
|
||||||
|
event.sender.send(`${channel}`, data)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 执行任务及监听进度 异步
|
||||||
|
// myjob.execPromise('./jobs/example/timer', {jobId}).then(task => {
|
||||||
|
// task.emitter.on(eventName, (data) => {
|
||||||
|
// Log.info('[main-process] timerTask, from TimerJob data:', data);
|
||||||
|
// // 发送数据到渲染进程
|
||||||
|
// event.sender.send(`${channel}`, data)
|
||||||
|
// })
|
||||||
|
// });
|
||||||
|
|
||||||
|
res.pid = timerTask.pid;
|
||||||
|
this.taskForJob[jobId] = timerTask;
|
||||||
|
}
|
||||||
|
if (action == 'close') {
|
||||||
|
oneTask = this.taskForJob[jobId];
|
||||||
|
oneTask.kill();
|
||||||
|
event.sender.send(`${channel}`, {jobId, number:0, pid:0});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建pool
|
||||||
|
*/
|
||||||
|
doCreatePool(num, event) {
|
||||||
|
const channel = 'controller.framework.createPoolNotice';
|
||||||
|
this.myJobPool.create(num).then(pids => {
|
||||||
|
event.reply(`${channel}`, pids);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过进程池执行任务
|
||||||
|
*/
|
||||||
|
doJobByPool(jobId, action, event) {
|
||||||
|
let res = {};
|
||||||
|
const channel = 'controller.framework.timerJobProgress';
|
||||||
|
if (action == 'run') {
|
||||||
|
// 异步-执行任务及监听进度
|
||||||
|
this.myJobPool.runPromise('./jobs/example/timer', {jobId}).then(task => {
|
||||||
|
|
||||||
|
// 监听器名称唯一,否则会出现重复监听。
|
||||||
|
// 任务完成时,需要移除监听器,防止内存泄漏
|
||||||
|
let eventName = 'job-timer-progress-' + jobId;
|
||||||
|
task.emitter.on(eventName, (data) => {
|
||||||
|
Log.info('[main-process] [ChildPoolJob] timerTask, from TimerJob data:', data);
|
||||||
|
|
||||||
|
// 发送数据到渲染进程
|
||||||
|
event.sender.send(`${channel}`, data)
|
||||||
|
|
||||||
|
// 如果收到任务完成的消息,移除监听器
|
||||||
|
if (data.end) {
|
||||||
|
task.emitter.removeAllListeners(eventName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.pid = task.pid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
monitorJob() {
|
||||||
|
setInterval(() => {
|
||||||
|
let jobPids = this.myJob.getPids();
|
||||||
|
let jobPoolPids = this.myJobPool.getPids();
|
||||||
|
Log.info(`[main-process] [monitorJob] jobPids: ${jobPids}, jobPoolPids: ${jobPoolPids}`);
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传到smms
|
||||||
|
*/
|
||||||
|
async uploadFileToSMMS(tmpFile) {
|
||||||
|
const res = {
|
||||||
|
code: 1000,
|
||||||
|
message: 'unknown error',
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const headersObj = {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
'Authorization': 'aaaaaaaaaaaaa' // 请修改这个token,用你自己的账号token
|
||||||
|
};
|
||||||
|
const url = 'https://sm.ms/api/v2/upload';
|
||||||
|
const hc = new HttpClient();
|
||||||
|
const response = await hc.request(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headersObj,
|
||||||
|
files: {
|
||||||
|
smfile: tmpFile,
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
timeout: 15000,
|
||||||
|
});
|
||||||
|
const result = response.data;
|
||||||
|
if (Ps.isDev()) {
|
||||||
|
Log.info('[FrameworkService] [uploadFileToSMMS]: info result:%j', result);
|
||||||
|
}
|
||||||
|
if (result.code !== 'success') {
|
||||||
|
Log.error('[FrameworkService] [uploadFileToSMMS]: res error result:%j', result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('[FrameworkService] [uploadFileToSMMS]: ERROR ', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameworkService.toString = () => '[class FrameworkService]';
|
||||||
|
module.exports = FrameworkService;
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
// 串口的功能
|
||||||
|
const { SerialPort } = require('serialport')
|
||||||
|
// 保存串口实例
|
||||||
|
var seriaPortIns = undefined;
|
||||||
|
|
||||||
|
// 这个是处理时间的库
|
||||||
|
var moment = require('moment')
|
||||||
|
// Modbus TCP
|
||||||
|
// create an empty modbus client
|
||||||
|
const ModbusRTU = require("modbus-serial")
|
||||||
|
var client = new ModbusRTU();
|
||||||
|
|
||||||
|
// open connection to a tcp line
|
||||||
|
// 创建Modbus TCP连接,IP是15.18.200.23,端口502
|
||||||
|
// client.connectTCP("15.18.200.23", { port: 502 });
|
||||||
|
//-------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hardware(service层为单例)
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class HardwareService extends Service {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息
|
||||||
|
* @param {*} seriaPort 串口实例
|
||||||
|
*/
|
||||||
|
sendSeriaPort(args) {
|
||||||
|
const port = seriaPortIns;
|
||||||
|
const msg = args.msg;
|
||||||
|
port.write(msg, 'hex')
|
||||||
|
console.log('测试发送消息' + msg);
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 连接串口 只执行一次
|
||||||
|
* @param {*} options 串口参数
|
||||||
|
* @param {*} event 回调.
|
||||||
|
*/
|
||||||
|
connectSeriaPort(options, event) {
|
||||||
|
|
||||||
|
if (seriaPortIns != undefined) {
|
||||||
|
console.log("SerialPort is have");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const channel = 'controller.hardware.connectSeriaPort';
|
||||||
|
const port = new SerialPort(options, (e) => {
|
||||||
|
console.log("SerialPort open");
|
||||||
|
console.log(e);
|
||||||
|
if (e === null) {
|
||||||
|
// 打开成功 把串口发送出去
|
||||||
|
let data2 = {
|
||||||
|
type: 'connect'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
port.on('data', (data) => {
|
||||||
|
let data2 = {
|
||||||
|
type: 'received',
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
console.log(`Received data: ${data2}`)
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
})
|
||||||
|
setInterval(() => {
|
||||||
|
// console.log('setInterval')
|
||||||
|
if (!port.isOpen) {
|
||||||
|
// console.log('setInterval open')
|
||||||
|
port.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1000)
|
||||||
|
port.on('close', () => {
|
||||||
|
let data2 = {
|
||||||
|
type: 'close'
|
||||||
|
}
|
||||||
|
console.log(`SerialPort close: ${data2}`)
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
})
|
||||||
|
port.on('error', (e) => {
|
||||||
|
let data2 = {
|
||||||
|
type: 'error'
|
||||||
|
}
|
||||||
|
console.log(`SerialPort error: ${e}`)
|
||||||
|
event.reply(`${channel}`, data2)
|
||||||
|
})
|
||||||
|
|
||||||
|
seriaPortIns = port;
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* test
|
||||||
|
*/
|
||||||
|
async test(args) {
|
||||||
|
let obj = {
|
||||||
|
status: 'ok',
|
||||||
|
params: args
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 样例 ModbusTCP
|
||||||
|
tempModbusTCP() {
|
||||||
|
//http://momentjs.cn/ 时间库
|
||||||
|
//https://www.jianshu.com/p/50954625b158
|
||||||
|
// 读取非甲烷总烃的关于总烃、甲烷、NMHC这3个寄存器(寄存器地址分别为22,25,28)中的浓度
|
||||||
|
// 每隔5秒钟读取保持寄存器的值,从寄存器地址22开始读取,读10个寄存器到data数组中
|
||||||
|
setInterval(function () {
|
||||||
|
// 要连接后才能用
|
||||||
|
client.readHoldingRegisters(22, 10, function (err, data) {
|
||||||
|
// 获取当前时间
|
||||||
|
//moment.locale('zh-cn');
|
||||||
|
// console.log("----------------------------------------------------------------------");
|
||||||
|
// console.log("数据时间是:" + moment().format('YYYY年MM月DD日 HH时mm分ss秒'));
|
||||||
|
// console.log("总烃的浓度是:" + data.data[0] * 0.01 + "ppmV"); // 总烃浓度对应的寄存器地址为22
|
||||||
|
// console.log("CH4的浓度是:" + data.data[3] * 0.01 + "ppmV"); // CH4浓度对应的寄存器地址为22
|
||||||
|
// console.log("NHMC的浓度是:" + data.data[6] * 0.01 + "ppmV"); // NHMC浓度对应的寄存器地址为22
|
||||||
|
// console.log("----------------------------------------------------------------------");
|
||||||
|
|
||||||
|
//console.log(data.data);
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HardwareService.toString = () => '[class HardwareService]';
|
||||||
|
module.exports = HardwareService;
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Service } = require('ee-core');
|
||||||
|
const { BrowserView, Notification } = require('electron');
|
||||||
|
const CoreWindow = require('ee-core/electron/window');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* os(service层为单例)
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class OsService extends Service {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx);
|
||||||
|
this.myBrowserView = null;
|
||||||
|
this.myNotification = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* createBrowserView
|
||||||
|
*/
|
||||||
|
createBrowserView(contentUrl) {
|
||||||
|
|
||||||
|
// electron 实验性功能,慎用
|
||||||
|
const win = CoreWindow.getMainWindow();
|
||||||
|
this.myBrowserView = new BrowserView();
|
||||||
|
win.setBrowserView(this.myBrowserView);
|
||||||
|
this.myBrowserView.setBounds({
|
||||||
|
x: 300,
|
||||||
|
y: 170,
|
||||||
|
width: 650,
|
||||||
|
height: 400
|
||||||
|
});
|
||||||
|
this.myBrowserView.webContents.loadURL(contentUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* removeBrowserView
|
||||||
|
*/
|
||||||
|
removeBrowserView() {
|
||||||
|
// one
|
||||||
|
this.myBrowserView.webContents.loadURL('about:blank')
|
||||||
|
|
||||||
|
// two - electron 11 remove destroy()
|
||||||
|
// this.myBrowserView.webContents.destroy();
|
||||||
|
|
||||||
|
// three
|
||||||
|
// this.myBrowserView.webContents.forcefullyCrashRenderer()
|
||||||
|
|
||||||
|
// fore
|
||||||
|
// this.myBrowserView.webContents.close
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* createNotification
|
||||||
|
*/
|
||||||
|
createNotification(options, event) {
|
||||||
|
const channel = 'controller.os.sendNotification';
|
||||||
|
this.myNotification = new Notification(options);
|
||||||
|
|
||||||
|
if (options.clickEvent) {
|
||||||
|
this.myNotification.on('click', (e) => {
|
||||||
|
let data = {
|
||||||
|
type: 'click',
|
||||||
|
msg: '您点击了通知消息'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.closeEvent) {
|
||||||
|
this.myNotification.on('close', (e) => {
|
||||||
|
let data = {
|
||||||
|
type: 'close',
|
||||||
|
msg: '您关闭了通知消息'
|
||||||
|
}
|
||||||
|
event.reply(`${channel}`, data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.myNotification.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
OsService.toString = () => '[class OsService]';
|
||||||
|
module.exports = OsService;
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
VITE_TITLE=""
|
||||||
|
VITE_GO_URL="http://localhost:8081"
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
VITE_TITLE=""
|
||||||
|
VITE_GO_URL="http://www.test.com"
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
package-lock.json
|
||||||
|
|
@ -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.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "ee",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host --port 17680",
|
||||||
|
"serve": "vite --host --port 17680",
|
||||||
|
"build-staging": "vite build --mode staging",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/icons-vue": "^6.1.0",
|
||||||
|
"ant-design-vue": "2.2.6",
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"element-plus": "^2.4.0",
|
||||||
|
"socket.io-client": "^4.4.1",
|
||||||
|
"store2": "^2.13.2",
|
||||||
|
"vue": "^3.2.33",
|
||||||
|
"vue-router": "^4.0.14",
|
||||||
|
"vuex": "^4.0.2",
|
||||||
|
"xgplayer": "^2.31.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
|
"@vue/compiler-sfc": "^3.2.33",
|
||||||
|
"less": "^4.1.2",
|
||||||
|
"less-loader": "^10.2.0",
|
||||||
|
"postcss": "^8.4.13",
|
||||||
|
"postcss-pxtorem": "^6.0.0",
|
||||||
|
"terser": "^5.19.1",
|
||||||
|
"vite": "^4.4.4",
|
||||||
|
"vite-plugin-compression": "^0.5.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<router-view/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
// websocket
|
||||||
|
import websocket from './utils/websocket'
|
||||||
|
import { updateConfgJson } from "@/utils/api"
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
setup() {
|
||||||
|
document.getElementById('loadingPage').remove()
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// 在这里执行一些需要在组件挂载到 DOM 后进行的初始化逻辑,例如初始化插件、请求数据等
|
||||||
|
|
||||||
|
ipc.invoke(ipcApiRoute.getExResConfig, "config.json").then(res => {
|
||||||
|
const configJSON = JSON.parse(res);
|
||||||
|
updateConfgJson(configJSON)
|
||||||
|
websocket.initWebSocket(configJSON);
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less"></style>
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主进程与渲染进程通信频道定义
|
||||||
|
* Definition of communication channels between main process and rendering process
|
||||||
|
*/
|
||||||
|
const ipcApiRoute = {
|
||||||
|
// framework
|
||||||
|
test: 'controller.example.test',
|
||||||
|
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',
|
||||||
|
// 获取ex资源目录下的配置文件 参数唯文件名
|
||||||
|
getExResConfig: 'controller.framework.getExResConfig',
|
||||||
|
// 发送socket 消息
|
||||||
|
sendTcpSocket: 'controller.framework.sendTcpSocket',
|
||||||
|
// 串口 参数为 整个串口参数
|
||||||
|
connectSeriaPort: 'controller.framework.connectSeriaPort',
|
||||||
|
// 发送串口消息 参数为 msg
|
||||||
|
sendSeriaPort: 'controller.framework.sendSeriaPort',
|
||||||
|
// 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',
|
||||||
|
// 获取mac
|
||||||
|
getAllMac: 'controller.os.getAllMac',
|
||||||
|
// 获取系统信息
|
||||||
|
getOSMessage: 'controller.os.getOSMessage',
|
||||||
|
// 声音设置
|
||||||
|
deviceLoudness: 'controller.os.deviceLoudness',
|
||||||
|
//deviceRestart 重启
|
||||||
|
deviceRestart: 'controller.os.deviceRestart',
|
||||||
|
// 设备关机
|
||||||
|
deviceShutdown: 'controller.os.deviceShutdown',
|
||||||
|
|
||||||
|
// 杀掉进程 参数为进程名称, 模糊搜索,注意可能杀错
|
||||||
|
deviceKillName: 'controller.os.deviceKillName',
|
||||||
|
// 启动程序 参数为程序目录绝对地址
|
||||||
|
deviceStarExe: 'controller.os.deviceStarExe',
|
||||||
|
// hardware
|
||||||
|
getPrinterList: 'controller.hardware.getPrinterList',
|
||||||
|
print: 'controller.hardware.print',
|
||||||
|
printStatus: 'controller.hardware.printStatus',
|
||||||
|
|
||||||
|
// effect
|
||||||
|
selectFile: 'controller.effect.selectFile',
|
||||||
|
loginWindow: 'controller.effect.loginWindow',
|
||||||
|
restoreWindow: 'controller.effect.restoreWindow',
|
||||||
|
|
||||||
|
|
||||||
|
// cross
|
||||||
|
crossInfo: 'controller.cross.info',
|
||||||
|
getCrossUrl: 'controller.cross.getUrl',
|
||||||
|
killCrossServer: 'controller.cross.killServer',
|
||||||
|
createCrossServer: 'controller.cross.createServer',
|
||||||
|
requestApi: 'controller.cross.requestApi',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义频道
|
||||||
|
* custom chennel
|
||||||
|
*/
|
||||||
|
const specialIpcRoute = {
|
||||||
|
appUpdater: 'app.updater', // updater channel
|
||||||
|
window1ToWindow2: 'window1-to-window2', // windows channel
|
||||||
|
window2ToWindow1: 'window2-to-window1', // windows channel
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
ipcApiRoute, specialIpcRoute
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条 */
|
||||||
|
::-webkit-scrollbar{width:8px;height:4px}
|
||||||
|
::-webkit-scrollbar-button{width:10px;height:0}
|
||||||
|
::-webkit-scrollbar-track{background:0 0}
|
||||||
|
::-webkit-scrollbar-thumb{background:#E6FFEE;-webkit-transition:.3s;transition:.3s}
|
||||||
|
::-webkit-scrollbar-thumb:hover{background-color:#07C160}
|
||||||
|
::-webkit-scrollbar-thumb:active{background-color:#07C160}
|
||||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
|
@ -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); // 浮层阴影
|
||||||
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { createFromIconfontCN } from '@ant-design/icons-vue'
|
||||||
|
import { h } from 'vue'
|
||||||
|
|
||||||
|
const IconFont = createFromIconfontCN({
|
||||||
|
scriptUrl: 'https://at.alicdn.com/t/font_2456157_4ovzopz659q.js',
|
||||||
|
extraCommonProps: {
|
||||||
|
type: 'icon-fengche',
|
||||||
|
style: {
|
||||||
|
fontSize: '18px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const DynamicIconFont = props => {
|
||||||
|
return h(IconFont, { type: props.type || 'icon-fengche' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DynamicIconFont
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import iconFont from './iconFont'
|
||||||
|
const modules = import.meta.globEager('./*.vue')
|
||||||
|
const map = {}
|
||||||
|
Object.keys(modules).forEach(file => {
|
||||||
|
const modulesName = file.replace('./', '').replace('.vue', '')
|
||||||
|
map[modulesName] = modules[file].default
|
||||||
|
})
|
||||||
|
const globalComponents = {
|
||||||
|
...map,
|
||||||
|
iconFont,
|
||||||
|
}
|
||||||
|
export default globalComponents
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
<template>
|
||||||
|
<a-layout id="app-layout-sider">
|
||||||
|
<a-layout-sider
|
||||||
|
v-model="collapsed"
|
||||||
|
theme="light"
|
||||||
|
class="layout-sider"
|
||||||
|
width="100"
|
||||||
|
>
|
||||||
|
<div class="logo">
|
||||||
|
<img class="pic-logo" src="~@/assets/logo.png">
|
||||||
|
</div>
|
||||||
|
<a-menu
|
||||||
|
class="menu-item"
|
||||||
|
theme="light"
|
||||||
|
mode="inline"
|
||||||
|
:selectedKeys="[current]"
|
||||||
|
@click="menuHandle"
|
||||||
|
>
|
||||||
|
<a-menu-item v-for="(menuInfo, index) in menu" :key="index">
|
||||||
|
<icon-font :type="menuInfo.icon" />
|
||||||
|
{{ menuInfo.title }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-layout-sider>
|
||||||
|
<a-layout>
|
||||||
|
<a-layout-content class="layout-content">
|
||||||
|
<router-view />
|
||||||
|
</a-layout-content>
|
||||||
|
</a-layout>
|
||||||
|
</a-layout>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AppSider',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
collapsed: true,
|
||||||
|
current: 'menu_1',
|
||||||
|
menu: {
|
||||||
|
'menu_1' : {
|
||||||
|
icon: 'icon-fengche',
|
||||||
|
title: '框架',
|
||||||
|
pageName: 'Framework',
|
||||||
|
params: {
|
||||||
|
// test: 'hello'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'menu_2' : {
|
||||||
|
icon: 'icon-niudan',
|
||||||
|
title: '系统',
|
||||||
|
pageName: 'Os',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
'menu_3' : {
|
||||||
|
icon: 'icon-xiangji',
|
||||||
|
title: '硬件',
|
||||||
|
pageName: 'Hardware',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
'menu_4' : {
|
||||||
|
icon: 'icon-liuxing',
|
||||||
|
title: '特效',
|
||||||
|
pageName: 'Effect',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
'menu_5' : {
|
||||||
|
icon: 'icon-gouwu',
|
||||||
|
title: 'cross',
|
||||||
|
pageName: 'Cross',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.menuHandle()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
menuHandle (e) {
|
||||||
|
console.log('sider menu e:', e);
|
||||||
|
this.current = e ? e.key : this.current;
|
||||||
|
console.log('sider menu current:', this.current);
|
||||||
|
|
||||||
|
const linkInfo = this.menu[this.current]
|
||||||
|
console.log('[home] load linkInfo:', linkInfo);
|
||||||
|
this.$router.push({ name: linkInfo.pageName, params: linkInfo.params})
|
||||||
|
},
|
||||||
|
changeMenu(e) {
|
||||||
|
console.log('sider menu e:', e);
|
||||||
|
//this.current = e.key;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
// 嵌套
|
||||||
|
#app-layout-sider {
|
||||||
|
height: 100%;
|
||||||
|
.logo {
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
.pic-logo {
|
||||||
|
height: 32px;
|
||||||
|
//background: rgba(139, 137, 137, 0.2);
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
.layout-sider {
|
||||||
|
border-top: 1px solid #e8e8e8;
|
||||||
|
border-right: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
.menu-item {
|
||||||
|
.ant-menu-item {
|
||||||
|
background-color: #fff;
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
padding: 0 0px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.layout-content {
|
||||||
|
//background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
<template>
|
||||||
|
<a-layout id="app-menu">
|
||||||
|
<a-layout-sider
|
||||||
|
theme="light"
|
||||||
|
class="layout-sider"
|
||||||
|
>
|
||||||
|
<a-menu
|
||||||
|
theme="light"
|
||||||
|
mode="inline"
|
||||||
|
:selectedKeys="[current]"
|
||||||
|
@click="changeMenu">
|
||||||
|
<a-menu-item v-for="(menuInfo, subIndex) in menu" :key="subIndex">
|
||||||
|
<router-link :to="{ name: menuInfo.pageName, params: menuInfo.params}">
|
||||||
|
<span>{{ menuInfo.title }}</span>
|
||||||
|
</router-link>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-layout-sider>
|
||||||
|
<a-layout>
|
||||||
|
<a-layout-content>
|
||||||
|
<router-view />
|
||||||
|
</a-layout-content>
|
||||||
|
</a-layout>
|
||||||
|
</a-layout>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
// import { reactive } from 'vue';
|
||||||
|
import subMenu from '@/router/subMenu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// setup() {
|
||||||
|
// const state = reactive({
|
||||||
|
// selectedKeys: ['menu_100'],
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const handleClick = e => {
|
||||||
|
// state.selectedKeys = [e.key];
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// state,
|
||||||
|
// handleClick,
|
||||||
|
// };
|
||||||
|
// },
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
menu:{},
|
||||||
|
//selectedKeys: ['menu_100'],
|
||||||
|
current: 'menu_100',
|
||||||
|
keys: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
id: function () {
|
||||||
|
console.log('watch id ----- ', this.id);
|
||||||
|
this.current = 'menu_100';
|
||||||
|
this.menuHandle();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.menuHandle();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
menuHandle () {
|
||||||
|
// 该组件优先被加载了,所以没拿到参数
|
||||||
|
//console.log('params:', this.$route);
|
||||||
|
|
||||||
|
console.log('menu ------ id:', this.id);
|
||||||
|
this.menu = subMenu[this.id];
|
||||||
|
const linkInfo = this.menu[this.current];
|
||||||
|
this.$router.push({ name: linkInfo.pageName, params: linkInfo.params});
|
||||||
|
},
|
||||||
|
changeMenu(e) {
|
||||||
|
console.log('changeMenu e:', e);
|
||||||
|
this.current = e.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-menu {
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
.layout-sider {
|
||||||
|
border-top: 1px solid #e8e8e8;
|
||||||
|
border-right: 1px solid #e8e8e8;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import AppSider from '@/layouts/AppSider'
|
||||||
|
import Menu from '@/layouts/Menu'
|
||||||
|
|
||||||
|
export {
|
||||||
|
AppSider,
|
||||||
|
Menu
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
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';
|
||||||
|
// 导入ele
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
import 'element-plus/dist/index.css'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.config.productionTip = false
|
||||||
|
|
||||||
|
// 挂载ele
|
||||||
|
app.use(ElementPlus)
|
||||||
|
|
||||||
|
// components
|
||||||
|
for (const i in components) {
|
||||||
|
app.component(i, components[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// icon
|
||||||
|
for (const i in AntIcon) {
|
||||||
|
const whiteList = ['createFromIconfontCN', 'getTwoToneColor', 'setTwoToneColor', 'default']
|
||||||
|
if (!whiteList.includes(i)) {
|
||||||
|
app.component(i, AntIcon[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(Antd).use(Router).mount('#app')
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
import routerMap from './routerMap'
|
||||||
|
|
||||||
|
const Router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes: routerMap,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Router
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
/**
|
||||||
|
* 基础路由
|
||||||
|
* @type { *[] }
|
||||||
|
*/
|
||||||
|
|
||||||
|
const constantRouterMap = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('@/layouts/AppSider.vue'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/framework',
|
||||||
|
name: 'Framework',
|
||||||
|
component: () => import('@/layouts/Menu.vue'),
|
||||||
|
props: { id: 'framework' },
|
||||||
|
//props: true,
|
||||||
|
redirect: { name: 'FrameworkSocketIpc' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/framework/socket/ipc',
|
||||||
|
name: 'FrameworkSocketIpc',
|
||||||
|
component: () => import('@/views/framework/socket/Ipc.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/socket/httpserver',
|
||||||
|
name: 'FrameworkSocketHttpServer',
|
||||||
|
component: () => import('@/views/framework/socket/HttpServer.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/socket/socketserver',
|
||||||
|
name: 'FrameworkSocketSocketServer',
|
||||||
|
component: () => import('@/views/framework/socket/SocketServer.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/jsondb/index',
|
||||||
|
name: 'FrameworkJsonDBIndex',
|
||||||
|
component: () => import('@/views/framework/jsondb/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/sqlitedb/index',
|
||||||
|
name: 'FrameworkSqliteDBIndex',
|
||||||
|
component: () => import('@/views/framework/sqlitedb/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/jobs/index',
|
||||||
|
name: 'FrameworkJobsIndex',
|
||||||
|
component: () => import('@/views/framework/jobs/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/updater/index',
|
||||||
|
name: 'FrameworkUpdaterIndex',
|
||||||
|
component: () => import('@/views/framework/updater/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/software/index',
|
||||||
|
name: 'FrameworkSoftwareIndex',
|
||||||
|
component: () => import('@/views/framework/software/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/java/index',
|
||||||
|
name: 'FrameworkJavaIndex',
|
||||||
|
component: () => import('@/views/framework/java/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/framework/testapi/index',
|
||||||
|
name: 'FrameworkTestApiIndex',
|
||||||
|
component: () => import('@/views/framework/testapi/Index.vue')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os',
|
||||||
|
name: 'Os',
|
||||||
|
component: () => import('@/layouts/Menu.vue'),
|
||||||
|
props: { id: 'os' },
|
||||||
|
redirect: { name: 'OsFileIndex' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/os/file/index',
|
||||||
|
name: 'OsFileIndex',
|
||||||
|
component: () => import('@/views/os/file/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/file/pic',
|
||||||
|
name: 'OsFilePic',
|
||||||
|
component: () => import('@/views/os/file/Pic.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/windowview/index',
|
||||||
|
name: 'OsWindowViewIndex',
|
||||||
|
component: () => import('@/views/os/windowview/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/window/index',
|
||||||
|
name: 'OsWindowIndex',
|
||||||
|
component: () => import('@/views/os/window/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/notification/index',
|
||||||
|
name: 'OsNotificationIndex',
|
||||||
|
component: () => import('@/views/os/notification/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/powermonitor/index',
|
||||||
|
name: 'OsPowerMonitorIndex',
|
||||||
|
component: () => import('@/views/os/powermonitor/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/screen/index',
|
||||||
|
name: 'OsScreenIndex',
|
||||||
|
component: () => import('@/views/os/screen/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/theme/index',
|
||||||
|
name: 'OsThemeIndex',
|
||||||
|
component: () => import('@/views/os/theme/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/os/system/index',
|
||||||
|
name: 'OsSystemIndex',
|
||||||
|
component: () => import('@/views/os/system/Index.vue')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/hardware',
|
||||||
|
name: 'Hardware',
|
||||||
|
component: () => import('@/layouts/Menu.vue'),
|
||||||
|
props: { id: 'hardware' },
|
||||||
|
redirect: { name: 'HardwarePrinterIndex' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/hardware/printer/index',
|
||||||
|
name: 'HardwarePrinterIndex',
|
||||||
|
component: () => import('@/views/hardware/printer/Index.vue')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/effect',
|
||||||
|
name: 'Effect',
|
||||||
|
component: () => import('@/layouts/Menu.vue'),
|
||||||
|
props: { id: 'effect' },
|
||||||
|
redirect: { name: 'EffectVideoIndex' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/effect/video/index',
|
||||||
|
name: 'EffectVideoIndex',
|
||||||
|
component: () => import('@/views/effect/video/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/effect/login/index',
|
||||||
|
name: 'EffectLoginIndex',
|
||||||
|
component: () => import('@/views/effect/login/Index.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cross',
|
||||||
|
name: 'Cross',
|
||||||
|
component: () => import('@/layouts/Menu.vue'),
|
||||||
|
props: { id: 'cross' },
|
||||||
|
redirect: { name: 'CrossGoIndex' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/cross/go/index',
|
||||||
|
name: 'CrossGoIndex',
|
||||||
|
component: () => import('@/views/cross/go/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cross/java/index',
|
||||||
|
name: 'CrossJavaIndex',
|
||||||
|
component: () => import('@/views/cross/java/Index.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/cross/python/index',
|
||||||
|
name: 'CrossPythonIndex',
|
||||||
|
component: () => import('@/views/cross/python/Index.vue')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/special',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'subwindow',
|
||||||
|
name: 'SpecialSubwindowIpc',
|
||||||
|
component: () => import('@/views/os/subwindow/Ipc.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'SpecialLoginWindow',
|
||||||
|
component: () => import('@/views/effect/login/Window.vue')
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default constantRouterMap
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
/**
|
||||||
|
* 子菜单
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
framework: {
|
||||||
|
'menu_100' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '通信',
|
||||||
|
pageName: 'FrameworkSocketIpc',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_101' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: 'http服务',
|
||||||
|
pageName: 'FrameworkSocketHttpServer',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_102' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: 'socket服务',
|
||||||
|
pageName: 'FrameworkSocketSocketServer',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_103' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: 'json数据库',
|
||||||
|
pageName: 'FrameworkJsonDBIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_104' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: 'sqlite数据库',
|
||||||
|
pageName: 'FrameworkSqliteDBIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_105' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '任务',
|
||||||
|
pageName: 'FrameworkJobsIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_106' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '自动更新',
|
||||||
|
pageName: 'FrameworkUpdaterIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_107' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '软件调用',
|
||||||
|
pageName: 'FrameworkSoftwareIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_109' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '测试',
|
||||||
|
pageName: 'FrameworkTestApiIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
os: {
|
||||||
|
'menu_100' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '文件',
|
||||||
|
pageName: 'OsFileIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_101' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '视图',
|
||||||
|
pageName: 'OsWindowViewIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_102' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '窗口',
|
||||||
|
pageName: 'OsWindowIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_103' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '桌面通知',
|
||||||
|
pageName: 'OsNotificationIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_104' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '电源监控',
|
||||||
|
pageName: 'OsPowerMonitorIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_105' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '屏幕信息',
|
||||||
|
pageName: 'OsScreenIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_106' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '系统主题',
|
||||||
|
pageName: 'OsThemeIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_110' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '图片',
|
||||||
|
pageName: 'OsFilePic',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hardware: {
|
||||||
|
'menu_100' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '打印机',
|
||||||
|
pageName: 'HardwarePrinterIndex',
|
||||||
|
params: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
effect: {
|
||||||
|
'menu_100' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '视频播放器',
|
||||||
|
pageName: 'EffectVideoIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_110' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: '登录',
|
||||||
|
pageName: 'EffectLoginIndex',
|
||||||
|
params: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cross: {
|
||||||
|
'menu_100' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: 'go服务',
|
||||||
|
pageName: 'CrossGoIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_110' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: 'java服务',
|
||||||
|
pageName: 'CrossJavaIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
'menu_120' : {
|
||||||
|
icon: 'profile',
|
||||||
|
title: 'python服务',
|
||||||
|
pageName: 'CrossPythonIndex',
|
||||||
|
params: {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
const apiBaseUrl = 'http://127.0.0.1:8086';
|
||||||
|
const webSocketUrl = 'ws://127.0.0.1:8086';
|
||||||
|
const apiService = axios.create({
|
||||||
|
baseURL: apiBaseUrl, // 设置基本URL
|
||||||
|
timeout: 5000, // 设置超时时间
|
||||||
|
});
|
||||||
|
// 全局设备配置文件的json
|
||||||
|
var GlobalConfig = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// 请求拦截器
|
||||||
|
apiService.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
// 在发送请求之前做一些处理,例如添加请求头等
|
||||||
|
// 如果需要更详细的可以定义一个字符串的
|
||||||
|
config.headers["deviceType"] = GlobalConfig.deviceType;
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// 请求错误时的处理
|
||||||
|
console.error(error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 响应拦截器
|
||||||
|
apiService.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
// 对响应数据进行处理
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// 响应错误时的处理
|
||||||
|
console.error(error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// 配置的更改
|
||||||
|
export function updateConfgJson(config){
|
||||||
|
GlobalConfig = config;
|
||||||
|
console.log('读取配置文件到内存');
|
||||||
|
console.log(GlobalConfig)
|
||||||
|
}
|
||||||
|
// 全局的config 配置, 频繁使用,可以用此函数获取,内存中的数据
|
||||||
|
export function getConfgJson(){
|
||||||
|
return GlobalConfig;
|
||||||
|
}
|
||||||
|
// todo: 可以直接导入到其他函数使用 也可以在此处定义函数后直接使用
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* api.get("/coreControl/deviceClient/creditCardHome?key=111&deptId=101").then(response=>{
|
||||||
|
console.log(response);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备首页的接口, 根据设备类型,进行不同的修改url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getDeviceHome() {
|
||||||
|
|
||||||
|
return apiService({
|
||||||
|
url: '/coreControl/deviceClient/creditCardHome?key=' + GlobalConfig.deviceId +"&deptId" + GlobalConfig.deptId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
apiService, apiBaseUrl, webSocketUrl
|
||||||
|
};
|
||||||
|
|
@ -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' },
|
||||||
|
]
|
||||||
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
|
||||||
|
// import { ipcApiRoute, specialIpcRoute } from "@/api/main";
|
||||||
|
|
||||||
|
// import vueMain from '../main'
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { webSocketUrl } from '@/utils/api'
|
||||||
|
var isOpen = false;
|
||||||
|
var authKey = ''
|
||||||
|
var socket = undefined
|
||||||
|
var socketHeartTimer = undefined
|
||||||
|
// 定时更新设备信息
|
||||||
|
var socketHeartTimer2 = undefined
|
||||||
|
// 用变量标记是否更新设备信息
|
||||||
|
var socketUpdateInfoCount = 0;
|
||||||
|
// 设备信息
|
||||||
|
var deviceInfo = {
|
||||||
|
volume: 0,
|
||||||
|
hostname: ''
|
||||||
|
|
||||||
|
};
|
||||||
|
var configJSON = null;
|
||||||
|
// 发送设备信息的间隔时间 s
|
||||||
|
const timerInterval = 10;
|
||||||
|
//屏幕设备类型IcbcDeviceType,1反诈屏 2互动屏 3弧形屏 4科技之眼 5光电玻璃 6 logo 设备类型 7 VR眼睛电脑
|
||||||
|
function initWebSocket(config) {
|
||||||
|
configJSON = config;
|
||||||
|
|
||||||
|
const wsUri = webSocketUrl+'/client/websocketServer' + '?deviceKey=' + configJSON.deviceId;
|
||||||
|
// const wsUri = 'ws://127.0.0.1:8080/resource/websocket' + '?deviceKey=' + e;
|
||||||
|
close();
|
||||||
|
socket = new WebSocket(wsUri)//这里面的this都指向vue
|
||||||
|
socket.onerror = webSocketOnError;
|
||||||
|
socket.onmessage = webSocketOnMessage;
|
||||||
|
socket.onclose = closeWebsocket;
|
||||||
|
socket.onopen = openWebSocket;
|
||||||
|
|
||||||
|
if (socketHeartTimer != undefined) {
|
||||||
|
clearInterval(socketHeartTimer);
|
||||||
|
}
|
||||||
|
if (socketHeartTimer2 != undefined){
|
||||||
|
clearInterval(socketHeartTimer2);
|
||||||
|
}
|
||||||
|
|
||||||
|
socketHeartTimer = setInterval(() => {
|
||||||
|
if (isOpen == false) {
|
||||||
|
console.log('重新连接' + isOpen)
|
||||||
|
initWebSocket(configJSON);
|
||||||
|
} else {
|
||||||
|
console.log('socket 心跳' + isOpen);
|
||||||
|
const msg = {
|
||||||
|
type: "heartbeat",// 心跳
|
||||||
|
content: "心跳",
|
||||||
|
deviceKey: configJSON.deviceId,
|
||||||
|
deviceType: configJSON.deviceType,
|
||||||
|
}
|
||||||
|
socket.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 6000);
|
||||||
|
|
||||||
|
|
||||||
|
socketHeartTimer2 = setInterval(() => {
|
||||||
|
ipc.invoke(ipcApiRoute.getOSMessage, {}).then(res => {
|
||||||
|
// 判断数据不相同 提前更新掉
|
||||||
|
if ( deviceInfo.volume != res.volume || deviceInfo.hostname != res.hostname){
|
||||||
|
deviceInfo = res;
|
||||||
|
sendDeviceInfo();
|
||||||
|
socketUpdateInfoCount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deviceInfo = res;
|
||||||
|
socketUpdateInfoCount = socketUpdateInfoCount+1;
|
||||||
|
if (socketUpdateInfoCount >= timerInterval){
|
||||||
|
sendDeviceInfo();
|
||||||
|
socketUpdateInfoCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
// 发送设备信息
|
||||||
|
function sendDeviceInfo(){
|
||||||
|
const msg = {
|
||||||
|
type: "device_info_update",// 设备信息更新
|
||||||
|
content: deviceInfo,
|
||||||
|
deviceKey: configJSON.deviceId,
|
||||||
|
deviceType: configJSON.deviceType,
|
||||||
|
deptId: configJSON.deptId
|
||||||
|
}
|
||||||
|
socket.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
function webSocketOnError(e) {
|
||||||
|
// ElementUI.Notification({
|
||||||
|
// title: '',
|
||||||
|
// message: "WebSocket连接发生错误" + e,
|
||||||
|
// type: 'error',
|
||||||
|
// duration: 0,
|
||||||
|
// });
|
||||||
|
|
||||||
|
}
|
||||||
|
// socket 连接打开
|
||||||
|
function openWebSocket(e) {
|
||||||
|
console.log('socket 连接')
|
||||||
|
isOpen = true;
|
||||||
|
}
|
||||||
|
function webSocketOnMessage(e) {
|
||||||
|
const data = JSON.parse(e.data);
|
||||||
|
console.log('收到socket 消息')
|
||||||
|
console.log(data);
|
||||||
|
// 抛出消息, 全局控制消息直接处理
|
||||||
|
switch(data.type){
|
||||||
|
case 'heartbeat':
|
||||||
|
return;
|
||||||
|
case 'serve_heartbeat':
|
||||||
|
break
|
||||||
|
case 'device_shutdown':
|
||||||
|
ipc.invoke(ipcApiRoute.deviceShutdown, {}).then(res => {
|
||||||
|
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
case 'device_restart':
|
||||||
|
ipc.invoke(ipcApiRoute.deviceRestart, {}).then(res => {
|
||||||
|
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
case 'device_play_video':
|
||||||
|
break;
|
||||||
|
case 'device_update_data':
|
||||||
|
break;
|
||||||
|
case 'scan_qr_game_start':
|
||||||
|
break;
|
||||||
|
case 'game_start_ok':
|
||||||
|
break;
|
||||||
|
case 'game_progress':
|
||||||
|
break;
|
||||||
|
case 'game_result_post':
|
||||||
|
break;
|
||||||
|
case 'device_info_update':
|
||||||
|
break;
|
||||||
|
case 'device_volume_update':
|
||||||
|
console.log("声音设置")
|
||||||
|
console.log(data.content);
|
||||||
|
ipc.invoke(ipcApiRoute.deviceLoudness, {value:data.content}).then(res => {
|
||||||
|
console.log('返回声音设置结果'+res);
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
window.dispatchEvent(new CustomEvent('onMessageWS', {
|
||||||
|
detail: {
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
// var event = document.createEvent("HTMLEvents");
|
||||||
|
// event.initEvent("onMessageWS", true, true);
|
||||||
|
// window.dispatchEvent(event);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
// 关闭websiocket
|
||||||
|
function closeWebsocket() {
|
||||||
|
console.log('连接已关闭...')
|
||||||
|
|
||||||
|
// clearTimeout(socketHeartTimer);
|
||||||
|
isOpen = false;
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
|
||||||
|
if (socket != undefined) {
|
||||||
|
socket.close() // 关闭 websocket
|
||||||
|
socket.onclose = function (e) {
|
||||||
|
console.log(e)//监听关闭事件
|
||||||
|
console.log('关闭')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket = undefined;
|
||||||
|
isOpen = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
function webSocketSend(agentData) {
|
||||||
|
socket.send(agentData);
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
initWebSocket, close, webSocketSend
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-cross-go">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 基础控制
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="create()"> 启动 </a-button>
|
||||||
|
<a-button @click="getUrl()"> 获取地址 </a-button>
|
||||||
|
<a-button @click="kill()"> kill </a-button>
|
||||||
|
<a-button @click="info()"> test </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 发送http请求
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="request(1)"> 前端发送 </a-button>
|
||||||
|
<a-button @click="request(2)"> 主进程发送 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
3. 多个服务
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="create()"> 启动 </a-button>
|
||||||
|
<a-button @click="killAll()"> kill所有 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
type: 1,
|
||||||
|
serverUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
info() {
|
||||||
|
ipc.invoke(ipcApiRoute.crossInfo, {}).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getUrl() {
|
||||||
|
ipc.invoke(ipcApiRoute.getCrossUrl, {name: 'goapp'}).then(url => {
|
||||||
|
this.serverUrl = url;
|
||||||
|
this.$message.info(`服务地址: ${url}`);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
kill() {
|
||||||
|
// name参数是 进程对象上的name,这里仅作为参照
|
||||||
|
ipc.invoke(ipcApiRoute.killCrossServer, {type: 'one', name: 'goapp'})
|
||||||
|
},
|
||||||
|
killAll() {
|
||||||
|
ipc.invoke(ipcApiRoute.killCrossServer, {type: 'all', name: 'goapp'})
|
||||||
|
},
|
||||||
|
create() {
|
||||||
|
ipc.invoke(ipcApiRoute.createCrossServer, { program: 'go' })
|
||||||
|
},
|
||||||
|
request(type) {
|
||||||
|
if (type == 1 && this.serverUrl == "") {
|
||||||
|
this.$message.info("请先获取服务地址");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (type == 1) {
|
||||||
|
const testApi = this.serverUrl + '/api/hello';
|
||||||
|
const cfg = {
|
||||||
|
method: 'get',
|
||||||
|
url: testApi,
|
||||||
|
params: { id: '111'},
|
||||||
|
timeout: 1000,
|
||||||
|
}
|
||||||
|
axios(cfg).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const data = res.data.data || null;
|
||||||
|
this.$message.info(`服务返回: ${data}`);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ipc.invoke(ipcApiRoute.requestApi, {name: 'goapp', urlPath: '/api/hello'}).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const data = res.data || null;
|
||||||
|
this.$message.info(`服务返回: ${data}`);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-cross-go {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-cross-java">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 基础控制
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="create()"> 启动 </a-button>
|
||||||
|
<a-button @click="getUrl()"> 获取地址 </a-button>
|
||||||
|
<a-button @click="kill()"> kill </a-button>
|
||||||
|
<a-button @click="info()"> 查看 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 发送http请求
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="request(1)"> 前端发送 </a-button>
|
||||||
|
<a-button @click="request(2)"> 主进程发送 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
3. 多个服务
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="create()"> 启动 </a-button>
|
||||||
|
<a-button @click="killAll()"> kill所有 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
type: 1,
|
||||||
|
serverUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
info() {
|
||||||
|
ipc.invoke(ipcApiRoute.crossInfo, {}).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getUrl() {
|
||||||
|
ipc.invoke(ipcApiRoute.getCrossUrl, {name: 'javaapp'}).then(url => {
|
||||||
|
this.serverUrl = url;
|
||||||
|
this.$message.info(`服务地址: ${url}`);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
kill() {
|
||||||
|
// name参数是 进程对象上的name,这里仅作为参照
|
||||||
|
ipc.invoke(ipcApiRoute.killCrossServer, {type: 'one', name: 'javaapp'})
|
||||||
|
},
|
||||||
|
killAll() {
|
||||||
|
ipc.invoke(ipcApiRoute.killCrossServer, {type: 'all', name: 'javaapp'})
|
||||||
|
},
|
||||||
|
create() {
|
||||||
|
ipc.invoke(ipcApiRoute.createCrossServer, { program: 'java' })
|
||||||
|
},
|
||||||
|
request(type) {
|
||||||
|
if (type == 1 && this.serverUrl == "") {
|
||||||
|
this.$message.info("请先获取服务地址");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == 1) {
|
||||||
|
const testApi = this.serverUrl + '/test1/get';
|
||||||
|
const cfg = {
|
||||||
|
method: 'get',
|
||||||
|
url: testApi,
|
||||||
|
params: { id: '1111111'},
|
||||||
|
timeout: 1000,
|
||||||
|
}
|
||||||
|
axios(cfg).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const data = res.data || null;
|
||||||
|
this.$message.info(`服务返回: ${data}`);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ipc.invoke(ipcApiRoute.requestApi, {name: 'javaapp', urlPath: '/test1/get', params: { id: '1111111'}}).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const data = res || null;
|
||||||
|
this.$message.info(`服务返回: ${data}`);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-cross-java {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-cross-python">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 基础控制
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="create()"> 启动 </a-button>
|
||||||
|
<a-button @click="getUrl()"> 获取地址 </a-button>
|
||||||
|
<a-button @click="kill()"> kill </a-button>
|
||||||
|
<a-button @click="info()"> test </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 发送http请求
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="request(1)"> 前端发送 </a-button>
|
||||||
|
<a-button @click="request(2)"> 主进程发送 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
3. 多个服务
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="create()"> 启动 </a-button>
|
||||||
|
<a-button @click="killAll()"> kill all </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
type: 1,
|
||||||
|
serverUrl: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
info() {
|
||||||
|
ipc.invoke(ipcApiRoute.crossInfo, {}).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getUrl() {
|
||||||
|
ipc.invoke(ipcApiRoute.getCrossUrl, {name: 'pyapp'}).then(url => {
|
||||||
|
this.serverUrl = url;
|
||||||
|
this.$message.info(`服务地址: ${url}`);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
kill() {
|
||||||
|
// name参数是 进程对象上的name,这里仅作为参照
|
||||||
|
ipc.invoke(ipcApiRoute.killCrossServer, {type: 'one', name: 'pyapp'})
|
||||||
|
},
|
||||||
|
killAll() {
|
||||||
|
ipc.invoke(ipcApiRoute.killCrossServer, {type: 'all', name: 'pyapp'})
|
||||||
|
},
|
||||||
|
create() {
|
||||||
|
ipc.invoke(ipcApiRoute.createCrossServer, { program: 'python' })
|
||||||
|
},
|
||||||
|
request(type) {
|
||||||
|
if (type == 1 && this.serverUrl == "") {
|
||||||
|
this.$message.info("请先获取服务地址");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (type == 1) {
|
||||||
|
const testApi = this.serverUrl + '/api/hello';
|
||||||
|
const cfg = {
|
||||||
|
method: 'get',
|
||||||
|
url: testApi,
|
||||||
|
params: { id: '111'},
|
||||||
|
timeout: 1000,
|
||||||
|
}
|
||||||
|
axios(cfg).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const data = res.data || null;
|
||||||
|
this.$message.info(`服务返回: ${JSON.stringify(data)}`);
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ipc.invoke(ipcApiRoute.requestApi, {name: 'pyapp', urlPath: '/api/hello'}).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const data = res || null;
|
||||||
|
this.$message.info(`服务返回: ${JSON.stringify(data)}`);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-cross-python {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<div id="effect-login-index">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 登录
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="loginWindow()">切换为登录窗口</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loginWindow () {
|
||||||
|
this.$router.push({ name: 'SpecialLoginWindow', params: {}});
|
||||||
|
ipc.invoke(ipcApiRoute.loginWindow, {width: 400, height: 300}).then(r => {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#effect-login-index {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
<template>
|
||||||
|
<div id="effect-login-window">
|
||||||
|
<div class="block-1">
|
||||||
|
<a v-if="!loading" @click="login">
|
||||||
|
<a-button type="primary">
|
||||||
|
登录
|
||||||
|
</a-button>
|
||||||
|
</a>
|
||||||
|
<span v-else>{{ loginText }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
loginText: '正在登陆......'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
login() {
|
||||||
|
this.loading = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$router.push({ name: 'Framework', params: {}});
|
||||||
|
ipc.invoke(ipcApiRoute.restoreWindow, {width: 980, height: 650}).then(r => {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#effect-login-window {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
background: #f0f2f5 url(/src/assets/login.png) no-repeat 50%;
|
||||||
|
display: flex;
|
||||||
|
.block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
align-items: center;
|
||||||
|
margin: auto;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-effect-video">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 视频播放
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="one-block-2">
|
||||||
|
<a-button @click="selectFile()"> 打开 / 浏览 </a-button>
|
||||||
|
</div> -->
|
||||||
|
<div class="one-block-2">
|
||||||
|
<div id="video-player"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import { toRaw } from 'vue';
|
||||||
|
import Player from 'xgplayer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fileUrl: '',
|
||||||
|
p: {},
|
||||||
|
op: {
|
||||||
|
id: 'video-player',
|
||||||
|
volume: 0.3,
|
||||||
|
url:'//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4',
|
||||||
|
poster: "//lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg",
|
||||||
|
playsinline: false,
|
||||||
|
danmu: {
|
||||||
|
comments: [
|
||||||
|
{
|
||||||
|
duration: 15000,
|
||||||
|
id: '1',
|
||||||
|
start: 3000,
|
||||||
|
txt: '这是一个弹幕',
|
||||||
|
style: { //弹幕自定义样式
|
||||||
|
color: '#ff9500',
|
||||||
|
fontSize: '20px',
|
||||||
|
border: 'solid 1px #ff9500',
|
||||||
|
borderRadius: '50px',
|
||||||
|
padding: '5px 11px',
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.1)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
area: {
|
||||||
|
start: 0,
|
||||||
|
end: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
this.p = new Player(toRaw(this.op));
|
||||||
|
},
|
||||||
|
selectFile () {
|
||||||
|
const params = {}
|
||||||
|
ipc.invoke(ipcApiRoute.selectFile, params).then(res => {
|
||||||
|
console.log('res:', res)
|
||||||
|
if (res) {
|
||||||
|
this.fileUrl = res;
|
||||||
|
this.p.start(self.fileUrl);
|
||||||
|
} else {
|
||||||
|
this.$message.warning('请选择视频');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-effect-video {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-other">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
请求java服务接口(废弃,请使用跨语言服务)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="startServer()"> 启动java项目 </a-button>
|
||||||
|
<a-button @click="sendRequest()"> 测试接口 </a-button>
|
||||||
|
<a-button @click="closeServer()"> 关闭java项目 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import axios from 'axios';
|
||||||
|
import storage from 'store2';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
server: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
startServer () {
|
||||||
|
ipc.invoke(ipcApiRoute.startJavaServer, {}).then(r => {
|
||||||
|
if (r.code != 0) {
|
||||||
|
this.$message.error(r.msg);
|
||||||
|
} else {
|
||||||
|
this.$message.info('异步启动');
|
||||||
|
storage.set('javaService', r.server);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
closeServer () {
|
||||||
|
ipc.invoke(ipcApiRoute.closeJavaServer, {}).then(r => {
|
||||||
|
if (r.code != 0) {
|
||||||
|
this.$message.error(r.msg);
|
||||||
|
}
|
||||||
|
this.$message.info('异步关闭');
|
||||||
|
storage.remove('javaService');
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
sendRequest () {
|
||||||
|
const server = storage.get('javaService') || '';
|
||||||
|
if (server == '') {
|
||||||
|
this.$message.error('服务未开启 或 正在启动中');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const testApi = server + '/test1/get';
|
||||||
|
const cfg = {
|
||||||
|
method: 'get',
|
||||||
|
url: testApi,
|
||||||
|
params: { id: '1111111'},
|
||||||
|
timeout: 60000,
|
||||||
|
}
|
||||||
|
axios(cfg).then(res => {
|
||||||
|
const data = res.data || null;
|
||||||
|
this.$message.info(`java服务返回: ${data}`, );
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-other {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-jobs">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 任务 / 并发任务
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="runJob(1, 'create')">执行任务1</a-button>
|
||||||
|
进度:{{ progress1 }} , 进程pid:{{ progress1_pid }}
|
||||||
|
<a-button @click="runJob(1, 'close')">关闭</a-button>
|
||||||
|
</a-space>
|
||||||
|
<p></p>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="runJob(2, 'create')">执行任务2</a-button>
|
||||||
|
进度:{{ progress2 }} , 进程pid:{{ progress2_pid }}
|
||||||
|
<a-button @click="runJob(2, 'close')">关闭</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 任务池 / 并发任务
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="createPool()">创建进程池</a-button>
|
||||||
|
进程pids:{{ processPids }}
|
||||||
|
</a-space>
|
||||||
|
<p></p>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="runJobByPool(3, 'run')">执行任务3</a-button>
|
||||||
|
进度:{{ progress3 }} ,进程pid:{{ progress3_pid }}
|
||||||
|
</a-space>
|
||||||
|
<p></p>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="runJobByPool(4, 'run')">执行任务4</a-button>
|
||||||
|
进度:{{ progress4 }} ,进程pid:{{ progress4_pid }}
|
||||||
|
</a-space>
|
||||||
|
<p></p>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="runJobByPool(5, 'run')">执行任务5</a-button>
|
||||||
|
进度:{{ progress5 }} ,进程pid:{{ progress5_pid }}
|
||||||
|
</a-space>
|
||||||
|
<p></p>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="runJobByPool(6, 'run')">执行任务6</a-button>
|
||||||
|
进度:{{ progress6 }} ,进程pid:{{ progress6_pid }}
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
processPids: '',
|
||||||
|
progress1: 0,
|
||||||
|
progress2: 0,
|
||||||
|
progress3: 0,
|
||||||
|
progress4: 0,
|
||||||
|
progress5: 0,
|
||||||
|
progress6: 0,
|
||||||
|
progress1_pid: 0,
|
||||||
|
progress2_pid: 0,
|
||||||
|
progress3_pid: 0,
|
||||||
|
progress4_pid: 0,
|
||||||
|
progress5_pid: 0,
|
||||||
|
progress6_pid: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
// 避免重复监听,或者将 on 功能写到一个统一的地方,只加载一次
|
||||||
|
ipc.removeAllListeners(ipcApiRoute.timerJobProgress);
|
||||||
|
ipc.removeAllListeners(ipcApiRoute.createPoolNotice);
|
||||||
|
|
||||||
|
// 监听任务进度
|
||||||
|
ipc.on(ipcApiRoute.timerJobProgress, (event, result) => {
|
||||||
|
switch (result.jobId) {
|
||||||
|
case 1:
|
||||||
|
this.progress1 = result.number;
|
||||||
|
this.progress1_pid = result.pid == 0 ? result.pid : this.progress1_pid;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this.progress2 = result.number;
|
||||||
|
this.progress2_pid = result.pid == 0 ? result.pid : this.progress2_pid;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this.progress3 = result.number;
|
||||||
|
this.progress3_pid = result.pid == 0 ? result.pid : this.progress3_pid;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
this.progress4 = result.number;
|
||||||
|
this.progress4_pid = result.pid == 0 ? result.pid : this.progress4_pid;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
this.progress5 = result.number;
|
||||||
|
this.progress5_pid = result.pid == 0 ? result.pid : this.progress5_pid;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
this.progress6 = result.number;
|
||||||
|
this.progress6_pid = result.pid == 0 ? result.pid : this.progress6_pid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听pool
|
||||||
|
ipc.on(ipcApiRoute.createPoolNotice, (event, result) => {
|
||||||
|
let pidsStr = JSON.stringify(result);
|
||||||
|
this.processPids = pidsStr;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
runJob(jobId, operation) {
|
||||||
|
let params = {
|
||||||
|
id: jobId,
|
||||||
|
type: 'timer',
|
||||||
|
action: operation
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.someJob, params).then(data => {
|
||||||
|
if (operation == 'close') return;
|
||||||
|
switch (data.jobId) {
|
||||||
|
case 1:
|
||||||
|
this.progress1_pid = data.result.pid;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this.progress2_pid = data.result.pid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
createPool() {
|
||||||
|
let params = {
|
||||||
|
number: 3,
|
||||||
|
}
|
||||||
|
ipc.send(ipcApiRoute.createPool, params);
|
||||||
|
},
|
||||||
|
runJobByPool(jobId, operation) {
|
||||||
|
let params = {
|
||||||
|
id: jobId,
|
||||||
|
type: 'timer',
|
||||||
|
action: operation
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.someJobByPool, params).then(data => {
|
||||||
|
switch (data.jobId) {
|
||||||
|
case 3:
|
||||||
|
this.progress3_pid = data.result.pid;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
this.progress4_pid = data.result.pid;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
this.progress5_pid = data.result.pid;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
this.progress6_pid = data.result.pid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-jobs {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,271 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-db">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. jsondb本地数据库
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="8">
|
||||||
|
• 小数据量: 0~100M(单库)
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
• json数据库
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
• 兼容lodash语法
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 数据目录
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-input v-model="data_dir" :value="data_dir" addon-before="数据目录" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="2">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="5">
|
||||||
|
<a-button @click="selectDir">
|
||||||
|
修改目录
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="5">
|
||||||
|
<a-button @click="openDir">
|
||||||
|
打开目录
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
3. 测试数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
{{ all_list }}
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
4. 添加数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="name" :value="name" addon-before="姓名" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="age" :value="age" addon-before="年龄" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="dbOperation('add')">
|
||||||
|
添加
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
5. 获取数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="search_age" :value="search_age" addon-before="年龄" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="dbOperation('get')">
|
||||||
|
查找
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
{{ userList }}
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
6. 修改数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="update_name" :value="update_name" addon-before="姓名" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="update_age" :value="update_age" addon-before="年龄" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="dbOperation('update')">
|
||||||
|
更新
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
7. 删除数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="delete_name" :value="delete_name" addon-before="姓名" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="dbOperation('del')">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '张三',
|
||||||
|
age: 10,
|
||||||
|
userList: ['空'],
|
||||||
|
search_age: 10,
|
||||||
|
update_name: '张三',
|
||||||
|
update_age: 21,
|
||||||
|
delete_name: '张三',
|
||||||
|
all_list: ['空'],
|
||||||
|
data_dir: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
const params = {
|
||||||
|
action: 'getDataDir',
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.jsondbOperation, params).then(res => {
|
||||||
|
this.data_dir = res.result;
|
||||||
|
this.getAllTestData();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getAllTestData () {
|
||||||
|
const params = {
|
||||||
|
action: 'all',
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.jsondbOperation, params).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
if (res.all_list.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.all_list = res.all_list;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectDir() {
|
||||||
|
ipc.invoke(ipcApiRoute.selectFolder, '').then(r => {
|
||||||
|
this.data_dir = r;
|
||||||
|
// 修改数据目录
|
||||||
|
this.modifyDataDir(r);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openDir() {
|
||||||
|
// console.log('data_dir:', this.data_dir);
|
||||||
|
ipc.invoke(ipcApiRoute.openDirectory, {id: this.data_dir}).then(res => {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
},
|
||||||
|
modifyDataDir(dir) {
|
||||||
|
const params = {
|
||||||
|
action: 'setDataDir',
|
||||||
|
data_dir: dir
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.jsondbOperation, params).then(res => {
|
||||||
|
this.all_list = res.all_list;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
dbOperation (ac) {
|
||||||
|
const params = {
|
||||||
|
action: ac,
|
||||||
|
info: {
|
||||||
|
name: this.name,
|
||||||
|
age: parseInt(this.age)
|
||||||
|
},
|
||||||
|
search_age: parseInt(this.search_age),
|
||||||
|
update_name: this.update_name,
|
||||||
|
update_age: parseInt(this.update_age),
|
||||||
|
delete_name: this.delete_name,
|
||||||
|
}
|
||||||
|
if (ac == 'add' && this.name.length == 0) {
|
||||||
|
this.$message.error(`请填写数据`);
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.jsondbOperation, params).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
if (ac == 'get') {
|
||||||
|
if (res.result.length == 0) {
|
||||||
|
this.$message.error(`没有数据`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.userList = res.result;
|
||||||
|
}
|
||||||
|
if (res.all_list.length == 0) {
|
||||||
|
this.all_list = ['空'];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.all_list = res.all_list;
|
||||||
|
this.$message.success(`success`);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-db {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-httpserver">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 使用http与主进程通信
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<p>* 状态:{{ currentStatus }}</p>
|
||||||
|
<p>* 地址:{{ servicAddress }}</p>
|
||||||
|
<p>* 发送请求:
|
||||||
|
<a-button @click="sendRequest('pictures')"> 打开【我的图片】 </a-button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 使用http与服务端通信
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<p>
|
||||||
|
<a-button @click="backendRequest()"> 发送请求 </a-button>
|
||||||
|
(请自行创建服务)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import axios from 'axios';
|
||||||
|
import storage from 'store2';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentStatus: '关闭',
|
||||||
|
servicAddress: '无'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
ipc.invoke(ipcApiRoute.checkHttpServer, {}).then(r => {
|
||||||
|
if (r.enable) {
|
||||||
|
this.currentStatus = '开启';
|
||||||
|
this.servicAddress = r.server;
|
||||||
|
storage.set('httpServiceConfig', r);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendRequest (id) {
|
||||||
|
if (this.currentStatus == '关闭') {
|
||||||
|
this.$message.error('http服务未开启');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requestHttp(ipcApiRoute.doHttpRequest, {id}).then(res => {
|
||||||
|
//console.log('res:', res)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessing built-in HTTP services
|
||||||
|
*/
|
||||||
|
requestHttp(uri, parameter) {
|
||||||
|
// URL conversion
|
||||||
|
const config = storage.get('httpServiceConfig');
|
||||||
|
const host = config.server || 'http://localhost:7071';
|
||||||
|
let url = uri.split('.').join('/');
|
||||||
|
url = host + '/' + url;
|
||||||
|
console.log('url:', url);
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
method: 'post',
|
||||||
|
data: parameter,
|
||||||
|
timeout: 60000,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send back-end requests
|
||||||
|
*/
|
||||||
|
backendRequest() {
|
||||||
|
console.log('GO_URL:', import.meta.env.VITE_GO_URL);
|
||||||
|
const cfg = {
|
||||||
|
baseURL: import.meta.env.VITE_GO_URL,
|
||||||
|
method: 'get',
|
||||||
|
url: '/hello',
|
||||||
|
timeout: 60000,
|
||||||
|
}
|
||||||
|
axios(cfg).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
const data = res.data || null;
|
||||||
|
this.$message.info(`go服务返回: ${data}`, );
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-httpserver {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-socket-ipc">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 发送异步消息
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleInvoke">发送 - 回调</a-button>
|
||||||
|
结果:{{ message1 }}
|
||||||
|
</a-space>
|
||||||
|
<p></p>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleInvoke2">发送 - async/await</a-button>
|
||||||
|
结果:{{ message2 }}
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
<!-- 尽量不要使用,任何错误都容易引起卡死 -->
|
||||||
|
2. 同步消息(不推荐,阻塞执行)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleSendSync">同步消息</a-button>
|
||||||
|
结果:{{ message3 }}
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
3. 长消息: 服务端持续向前端页面发消息
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="sendMsgStart">开始</a-button>
|
||||||
|
<a-button @click="sendMsgStop">结束</a-button>
|
||||||
|
结果:{{ messageString }}
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
4. 多窗口通信:子窗口与主进程通信,子窗口互相通信
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="createWindow(0)">打开新窗口2</a-button>
|
||||||
|
<a-button @click="sendTosubWindow()">向新窗口2发消息</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute, specialIpcRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import { toRaw } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
messageString: '',
|
||||||
|
message1: '',
|
||||||
|
message2: '',
|
||||||
|
message3: '',
|
||||||
|
windowName: 'window-ipc',
|
||||||
|
newWcId: 0,
|
||||||
|
views: [
|
||||||
|
{
|
||||||
|
type: 'vue',
|
||||||
|
content: '#/special/subwindow',
|
||||||
|
windowName: 'window-ipc',
|
||||||
|
windowTitle: 'ipc window'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
// 避免重复监听,或者将 on 功能写到一个统一的地方,只加载一次
|
||||||
|
ipc.removeAllListeners(ipcApiRoute.ipcSendMsg);
|
||||||
|
ipc.on(ipcApiRoute.ipcSendMsg, (event, result) => {
|
||||||
|
console.log('[ipcRenderer] [socketMsgStart] result:', result);
|
||||||
|
|
||||||
|
this.messageString = result;
|
||||||
|
// 调用后端的另一个接口
|
||||||
|
event.sender.send(ipcApiRoute.hello, 'electron-egg');
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 窗口2 发来的消息
|
||||||
|
ipc.removeAllListeners(specialIpcRoute.window2ToWindow1);
|
||||||
|
ipc.on(specialIpcRoute.window2ToWindow1, (event, arg) => {
|
||||||
|
this.$message.info(arg);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendMsgStart() {
|
||||||
|
const params = {
|
||||||
|
type: 'start',
|
||||||
|
content: '开始'
|
||||||
|
}
|
||||||
|
ipc.send(ipcApiRoute.ipcSendMsg, params)
|
||||||
|
},
|
||||||
|
sendMsgStop() {
|
||||||
|
const params = {
|
||||||
|
type: 'end',
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
ipc.send(ipcApiRoute.ipcSendMsg, params)
|
||||||
|
},
|
||||||
|
handleInvoke() {
|
||||||
|
ipc.invoke(ipcApiRoute.ipcInvokeMsg, '异步-回调').then(r => {
|
||||||
|
console.log('r:', r);
|
||||||
|
this.message1 = r;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async handleInvoke2() {
|
||||||
|
const msg = await ipc.invoke(ipcApiRoute.ipcInvokeMsg, '异步');
|
||||||
|
console.log('msg:', msg);
|
||||||
|
this.message2 = msg;
|
||||||
|
},
|
||||||
|
handleSendSync() {
|
||||||
|
const msg = ipc.sendSync(ipcApiRoute.ipcSendSyncMsg, '同步');
|
||||||
|
this.message3 = msg;
|
||||||
|
},
|
||||||
|
createWindow(index) {
|
||||||
|
ipc.invoke(ipcApiRoute.createWindow, toRaw(this.views[index])).then(id => {
|
||||||
|
console.log('[createWindow] id:', id);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async sendTosubWindow() {
|
||||||
|
// 新窗口id
|
||||||
|
this.newWcId = await ipc.invoke(ipcApiRoute.getWCid, this.windowName);
|
||||||
|
ipc.sendTo(this.newWcId, specialIpcRoute.window1ToWindow2, '窗口1通过 sendTo 给窗口2发送消息');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-socket-ipc {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-httpserver">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 使用socket与主进程通信
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<p>* 状态:{{ currentStatus }}</p>
|
||||||
|
</a-space>
|
||||||
|
<p>* 地址:{{ servicAddress }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 发送请求
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="sendRequest('downloads')"> 打开【我的下载】 </a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { io } from 'socket.io-client';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentStatus: '关闭',
|
||||||
|
servicAddress: 'ws://localhost:7070'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
this.socket = io(this.servicAddress);
|
||||||
|
this.socket.on('connect', () => {
|
||||||
|
console.log('connect!!!!!!!!');
|
||||||
|
this.currentStatus = '开启';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
sendRequest (id) {
|
||||||
|
if (this.currentStatus == '关闭') {
|
||||||
|
this.$message.error('socketio服务未开启');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const method = ipcApiRoute.doSocketRequest;
|
||||||
|
this.socket.emit('c1', { cmd: method, params: {id: id} }, (response) => {
|
||||||
|
// response为返回值
|
||||||
|
console.log('response:', response)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-httpserver {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-software-open">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 调用其它软件(exe、bash等可执行程序)
|
||||||
|
</span>
|
||||||
|
<p/>
|
||||||
|
<span class="sub-content">
|
||||||
|
注:请先将【powershell.exe】复制到【electron-egg/build/extraResources】目录中
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-list bordered :data-source="data">
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item @click="openSoft(item.id)">
|
||||||
|
{{ item.content }}
|
||||||
|
<a-button type="link">
|
||||||
|
执行
|
||||||
|
</a-button>
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
content: 'powershell.exe',
|
||||||
|
id: 'powershell.exe'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openSoft(id) {
|
||||||
|
ipc.invoke(ipcApiRoute.openSoftware, id).then(result => {
|
||||||
|
if (!result) {
|
||||||
|
this.$message.error('程序不存在');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-software-open {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
.sub-content {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,275 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-db">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. sqlite本地数据库
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="8">
|
||||||
|
• 大数据量: 0-1024GB(单库)
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
• 高性能
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
• 类mysql语法
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 数据目录
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-input v-model="data_dir" :value="data_dir" addon-before="数据目录" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="2">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="5">
|
||||||
|
<a-button @click="selectDir">
|
||||||
|
修改目录
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="5">
|
||||||
|
<a-button @click="openDir">
|
||||||
|
打开目录
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
3. 测试数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
{{ all_list }}
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
4. 添加数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="name" :value="name" addon-before="姓名" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="age" :value="age" addon-before="年龄" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="sqlitedbOperation('add')">
|
||||||
|
添加
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
4. 获取数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="search_age" :value="search_age" addon-before="年龄" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="sqlitedbOperation('get')">
|
||||||
|
查找
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="24">
|
||||||
|
{{ userList }}
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
5. 修改数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="update_name" :value="update_name" addon-before="姓名(条件)" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="update_age" :value="update_age" addon-before="年龄" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="sqlitedbOperation('update')">
|
||||||
|
更新
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
6. 删除数据
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input v-model="delete_name" :value="delete_name" addon-before="姓名" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-button @click="sqlitedbOperation('del')">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
name: '李四',
|
||||||
|
age: 20,
|
||||||
|
userList: ['空'],
|
||||||
|
search_age: 20,
|
||||||
|
update_name: '李四',
|
||||||
|
update_age: 31,
|
||||||
|
delete_name: '李四',
|
||||||
|
all_list: ['空'],
|
||||||
|
data_dir: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
const params = {
|
||||||
|
action: 'getDataDir',
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.sqlitedbOperation, params).then(res => {
|
||||||
|
if (res.code == -1) {
|
||||||
|
this.$message.error('请检查sqlite是否正确安装', 5);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data_dir = res.result;
|
||||||
|
this.getAllTestData();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getAllTestData () {
|
||||||
|
const params = {
|
||||||
|
action: 'all',
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.sqlitedbOperation, params).then(res => {
|
||||||
|
if (res.all_list.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.all_list = res.all_list;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectDir() {
|
||||||
|
ipc.invoke(ipcApiRoute.selectFolder, '').then(r => {
|
||||||
|
this.data_dir = r;
|
||||||
|
// 修改数据目录
|
||||||
|
this.modifyDataDir(r);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openDir() {
|
||||||
|
console.log('dd:', this.data_dir);
|
||||||
|
ipc.invoke(ipcApiRoute.openDirectory, {id: this.data_dir}).then(res => {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
},
|
||||||
|
modifyDataDir(dir) {
|
||||||
|
const params = {
|
||||||
|
action: 'setDataDir',
|
||||||
|
data_dir: dir
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.sqlitedbOperation, params).then(res => {
|
||||||
|
this.all_list = res.all_list;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sqlitedbOperation (ac) {
|
||||||
|
const params = {
|
||||||
|
action: ac,
|
||||||
|
info: {
|
||||||
|
name: this.name,
|
||||||
|
age: parseInt(this.age)
|
||||||
|
},
|
||||||
|
search_age: parseInt(this.search_age),
|
||||||
|
update_name: this.update_name,
|
||||||
|
update_age: parseInt(this.update_age),
|
||||||
|
delete_name: this.delete_name,
|
||||||
|
}
|
||||||
|
if (ac == 'add' && this.name.length == 0) {
|
||||||
|
this.$message.error(`请填写数据`);
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.sqlitedbOperation, params).then(res => {
|
||||||
|
console.log('res:', res);
|
||||||
|
if (ac == 'get') {
|
||||||
|
if (res.result.length == 0) {
|
||||||
|
this.$message.error(`没有数据`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.userList = res.result;
|
||||||
|
}
|
||||||
|
if (res.all_list.length == 0) {
|
||||||
|
this.all_list = ['空'];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.all_list = res.all_list;
|
||||||
|
this.$message.success(`success`);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-db {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-other">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
待开发...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
test () {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-other {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-test-api">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span> 1. 测试一些操作系统api </span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="exec(1)"> 点击 </a-button>
|
||||||
|
<a-button @click="exec2(1)"> 点击2 </a-button>
|
||||||
|
<a-button @click="getMac()"> 获取mac 列表 </a-button>
|
||||||
|
<a-button @click="getOSMessage()"> 获取系统信息 </a-button>
|
||||||
|
<a-button @click="setSound()"> 设置声音50</a-button>
|
||||||
|
<a-button @click="restart()">重启</a-button>
|
||||||
|
</a-space>
|
||||||
|
|
||||||
|
<a-space>
|
||||||
|
<input v-model="progressName" placeholder="请输入进程名称" />
|
||||||
|
<a-button @click="kill()"> kill 指定进程 </a-button>
|
||||||
|
</a-space>
|
||||||
|
<a-space>
|
||||||
|
<input v-model="exePath" placeholder="请输入程序地址" />
|
||||||
|
<a-button @click="starExe()"> 启动 </a-button>
|
||||||
|
</a-space>
|
||||||
|
<a-space>
|
||||||
|
|
||||||
|
<a-button @click="testNet()"> 测试网络请求 </a-button>
|
||||||
|
</a-space>
|
||||||
|
<!-- <el-button type="danger">Danger</el-button> -->
|
||||||
|
|
||||||
|
<!-- <a-space>
|
||||||
|
<a-list size="small" bordered :data-source="iconData">
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item>
|
||||||
|
<icon-font :type="item.type" style="font-size: 36px" />
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</a-space> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from "@/api/main";
|
||||||
|
import iconList from "@/utils/iconList";
|
||||||
|
import { ipc } from "@/utils/ipcRenderer";
|
||||||
|
import { apiBaseUrl , getDeviceHome} from "@/utils/api"
|
||||||
|
// 批量导入图片动态使用, 解决vite 无法动态图片的问题
|
||||||
|
const pngModules = import.meta.globEager("@/assets/*.png");
|
||||||
|
// 把所有的图片都返回了, 可以在 map 中做一些排序等操作,实现动态图片
|
||||||
|
const urls = Object.values(pngModules).map((mod) => mod.default);
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
type: 1,
|
||||||
|
iconData: iconList,
|
||||||
|
// 进程名称
|
||||||
|
progressName: "",
|
||||||
|
exePath: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
exec(id) {
|
||||||
|
console.log("process:", process);
|
||||||
|
const params = {
|
||||||
|
id: id,
|
||||||
|
};
|
||||||
|
ipc.invoke(ipcApiRoute.test, params).then((res) => {
|
||||||
|
console.log("res:", res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
exec2(id) {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
getMac() {
|
||||||
|
ipc.invoke(ipcApiRoute.getAllMac, {}).then((res) => {
|
||||||
|
console.log("macs :", res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getOSMessage() {
|
||||||
|
ipc.invoke(ipcApiRoute.getOSMessage, {}).then((res) => {
|
||||||
|
console.log("OS :", res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setSound() {
|
||||||
|
ipc.invoke(ipcApiRoute.deviceLoudness, { value: 100 }).then((res) => {
|
||||||
|
console.log("设置后的声音 :", res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 重启
|
||||||
|
restart() {
|
||||||
|
ipc.invoke(ipcApiRoute.deviceRestart, {}).then((res) => {
|
||||||
|
console.log("重启 :", res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
kill() {
|
||||||
|
//deviceStarExe
|
||||||
|
console.log(this.progressName);
|
||||||
|
ipc.invoke(ipcApiRoute.deviceKillName, this.progressName).then((res) => {
|
||||||
|
console.log("关闭进程结果 :", res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
starExe(){
|
||||||
|
console.log(this.exePath);
|
||||||
|
ipc.invoke(ipcApiRoute.deviceStarExe, this.exePath).then((res) => {
|
||||||
|
console.log("启动程序 :", res);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
testNet(){
|
||||||
|
console.log(apiBaseUrl);
|
||||||
|
getDeviceHome().then(response=>{
|
||||||
|
console.log(response);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-test-api {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-demo-window">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 自动更新
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="checkForUpdater()">检查更新</a-button>
|
||||||
|
<a-button @click="download()">下载并安装</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 下载进度
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-progress :percent="percentNumber" status="active" />
|
||||||
|
<a-space>
|
||||||
|
{{ progress }}
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute, specialIpcRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: 0, // -1:异常,1:有可用更新,2:没有可用更新,3:下载中, 4:下载完成
|
||||||
|
progress: '',
|
||||||
|
percentNumber: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
ipc.removeAllListeners(specialIpcRoute.appUpdater);
|
||||||
|
ipc.on(specialIpcRoute.appUpdater, (event, result) => {
|
||||||
|
result = JSON.parse(result);
|
||||||
|
this.status = result.status;
|
||||||
|
if (result.status == 3) {
|
||||||
|
this.progress = result.desc;
|
||||||
|
this.percentNumber = result.percentNumber;
|
||||||
|
} else {
|
||||||
|
this.$message.info(result.desc);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
checkForUpdater () {
|
||||||
|
ipc.invoke(ipcApiRoute.checkForUpdater).then(r => {
|
||||||
|
console.log(r);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
download () {
|
||||||
|
if (this.status !== 1) {
|
||||||
|
this.$message.info('没有可用版本');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ipc.invoke(ipcApiRoute.downloadApp).then(r => {
|
||||||
|
console.log(r);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-demo-window {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-hw-bluetooth">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 打印机设备
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-button @click="getPrinter()"> 获取打印机列表 </a-button>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-list size="small" bordered :data-source="printerList">
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item>
|
||||||
|
{{ item.displayName }} {{ defaultDevice(item) }}
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
<template #header>
|
||||||
|
<div>设备列表</div>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 打印内容
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-button @click="doPrint(0)"> 打印一个页面 </a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import { toRaw } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
defaultDeviceName: '',
|
||||||
|
printerList: [],
|
||||||
|
views: [
|
||||||
|
{
|
||||||
|
type: 'html',
|
||||||
|
content: '/public/html/view_example.html'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
// 避免重复监听,或者将 on 功能写到一个统一的地方,只加载一次
|
||||||
|
ipc.removeAllListeners(ipcApiRoute.printStatus);
|
||||||
|
ipc.on(ipcApiRoute.printStatus, (event, result) => {
|
||||||
|
console.log('result', result);
|
||||||
|
this.$message.info('打印中...');
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getPrinter () {
|
||||||
|
ipc.invoke(ipcApiRoute.getPrinterList, {}).then(res => {
|
||||||
|
this.printerList = res;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
doPrint (index) {
|
||||||
|
console.log('defaultDeviceName:', this.defaultDeviceName)
|
||||||
|
const params = {
|
||||||
|
view: toRaw(this.views[index]),
|
||||||
|
deviceName: this.defaultDeviceName
|
||||||
|
};
|
||||||
|
ipc.send(ipcApiRoute.print, params)
|
||||||
|
},
|
||||||
|
defaultDevice (item) {
|
||||||
|
let desc = "";
|
||||||
|
if (item.isDefault) {
|
||||||
|
desc = "- 默认";
|
||||||
|
this.defaultDeviceName = item.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-hw-bluetooth {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-extension">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
<!-- electron的扩展功能不完整,官方也不建议使用 -->
|
||||||
|
1. 上传扩展程序(crx文件格式)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-upload-dragger
|
||||||
|
name="file"
|
||||||
|
:multiple="true"
|
||||||
|
:action="action_url"
|
||||||
|
@change="handleChange"
|
||||||
|
>
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<a-icon type="inbox" />
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">
|
||||||
|
上传
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-hint">
|
||||||
|
</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
2. chrome扩展商店(crx下载)
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
极简插件:https://chrome.zzzmh.cn/
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
action_url: 'localhost:xxxx/api/example/uploadExtension',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleChange(info) {
|
||||||
|
const status = info.file.status;
|
||||||
|
if (status !== 'uploading') {
|
||||||
|
console.log(info.file);
|
||||||
|
}
|
||||||
|
if (status === 'done') {
|
||||||
|
const uploadRes = info.file.response;
|
||||||
|
console.log('uploadRes:', uploadRes)
|
||||||
|
} else if (status === 'error') {
|
||||||
|
this.$message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-extension {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-file">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 系统原生对话框
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="messageShow()">消息提示(ipc)</a-button>
|
||||||
|
<a-button @click="messageShowConfirm()">消息提示与确认(ipc)</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
2. 选择保存目录
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-input v-model="dir_path" :value="dir_path" addon-before="保存目录" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-button @click="selectDir">
|
||||||
|
修改目录
|
||||||
|
</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
3. 打开文件夹
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-list :grid="{ gutter: 16, column: 4 }" :data-source="file_list">
|
||||||
|
<template #renderItem="{ item }">
|
||||||
|
<a-list-item @click="openDirectry(item.id)">
|
||||||
|
<a-card :title="item.content">
|
||||||
|
<a-button type="link">
|
||||||
|
打开
|
||||||
|
</a-button>
|
||||||
|
</a-card>
|
||||||
|
</a-list-item>
|
||||||
|
</template>
|
||||||
|
<!-- <a-list-item slot="renderItem" slot-scope="item" @click="openDirectry(item.id)">
|
||||||
|
<a-card :title="item.content">
|
||||||
|
<a-button type="link">
|
||||||
|
打开
|
||||||
|
</a-button>
|
||||||
|
</a-card>
|
||||||
|
</a-list-item> -->
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
4. 上传文件到图床
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-upload-dragger
|
||||||
|
name="file"
|
||||||
|
:multiple="true"
|
||||||
|
:action="action_url"
|
||||||
|
@change="handleFileChange"
|
||||||
|
>
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">
|
||||||
|
点击 或 拖拽文件到这里
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-hint">
|
||||||
|
注意:请使用您自己的图床token
|
||||||
|
</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import storage from 'store2';
|
||||||
|
|
||||||
|
const fileList = [
|
||||||
|
{
|
||||||
|
content: '【下载】目录',
|
||||||
|
id: 'downloads'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '【图片】目录',
|
||||||
|
id: 'pictures'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '【文档】目录',
|
||||||
|
id: 'documents'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '【音乐】目录',
|
||||||
|
id: 'music'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
file_list: fileList,
|
||||||
|
action_url: '',
|
||||||
|
image_info: [],
|
||||||
|
num: 0,
|
||||||
|
servicAddress: '',
|
||||||
|
dir_path: "D:\\www\\ee",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.getHost();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getHost () {
|
||||||
|
ipc.invoke(ipcApiRoute.checkHttpServer, {}).then(r => {
|
||||||
|
if (r.enable) {
|
||||||
|
this.servicAddress = r.server;
|
||||||
|
storage.set('httpServiceConfig', r);
|
||||||
|
|
||||||
|
// url转换
|
||||||
|
const host = r.server || 'http://localhost:7071';
|
||||||
|
let uri = ipcApiRoute.uploadFile;
|
||||||
|
let url = uri.split('.').join('/');
|
||||||
|
this.action_url = host + '/' + url;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openDirectry (id) {
|
||||||
|
ipc.invoke(ipcApiRoute.openDirectory, {id: id}).then(res => {
|
||||||
|
//console.log('res:', res)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectDir() {
|
||||||
|
ipc.invoke(ipcApiRoute.selectFolder, '').then(r => {
|
||||||
|
this.dir_path = r;
|
||||||
|
this.$message.info(r);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
messageShow() {
|
||||||
|
ipc.invoke(ipcApiRoute.messageShow, '').then(r => {
|
||||||
|
this.$message.info(r);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
messageShowConfirm() {
|
||||||
|
ipc.invoke(ipcApiRoute.messageShowConfirm, '').then(r => {
|
||||||
|
this.$message.info(r);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleFileChange(info) {
|
||||||
|
console.log('handleFileChange-----');
|
||||||
|
if (this.action_url == '') {
|
||||||
|
this.$message.error('http服务未开启');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const status = info.file.status;
|
||||||
|
if (status !== 'uploading') {
|
||||||
|
console.log(info.file);
|
||||||
|
}
|
||||||
|
if (status === 'done') {
|
||||||
|
const uploadRes = info.file.response;
|
||||||
|
console.log('uploadRes:', uploadRes)
|
||||||
|
if (uploadRes.code !== 'success') {
|
||||||
|
this.$message.error(`file upload failed ${uploadRes.code} .`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.num++;
|
||||||
|
const picInfo = uploadRes.data;
|
||||||
|
picInfo.id = this.num;
|
||||||
|
picInfo.imageUrlText = 'image url';
|
||||||
|
this.image_info.push(picInfo);
|
||||||
|
this.$message.success(`${info.file.name} file uploaded successfully.`);
|
||||||
|
} else if (status === 'error') {
|
||||||
|
this.$message.error(`${info.file.name} file upload failed.`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-file {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
<template>
|
||||||
|
<div id="os-file-pic">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 加载本机图片
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="selectPic(0)">选择图片</a-button>
|
||||||
|
</a-space>
|
||||||
|
<p></p>
|
||||||
|
<a-image
|
||||||
|
:width="500"
|
||||||
|
:src=picPath
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
picPath: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectPic () {
|
||||||
|
ipc.invoke(ipcApiRoute.selectPic, {}).then(r => {
|
||||||
|
this.picPath = r;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#os-file-pic {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-notification">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 弹出桌面通知
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="sendNotification(0)">默认</a-button>
|
||||||
|
<a-button @click="sendNotification(1)">发出提示音</a-button>
|
||||||
|
<a-button @click="sendNotification(2)">点击通知触发事件</a-button>
|
||||||
|
<a-button @click="sendNotification(3)">关闭通知触发事件</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
import { toRaw } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
views: [
|
||||||
|
{
|
||||||
|
type: 'main',
|
||||||
|
title: '通知标题',
|
||||||
|
subtitle: '副标题', // macOS系统专有属性
|
||||||
|
body: '这是通知内容-默认',
|
||||||
|
silent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'main',
|
||||||
|
title: '提示音',
|
||||||
|
subtitle: '副标题-提示音',
|
||||||
|
body: '这是通知内容-提示音',
|
||||||
|
silent: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'main',
|
||||||
|
title: '点击通知事件',
|
||||||
|
subtitle: '副标题-点击通知事件',
|
||||||
|
body: '这是通知内容-点击通知事件',
|
||||||
|
clickEvent: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'main',
|
||||||
|
title: '关闭通知事件',
|
||||||
|
subtitle: '副标题-关闭通知事件',
|
||||||
|
body: '这是通知内容-点击通知事件',
|
||||||
|
closeEvent: true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
// 避免重复监听,或者将 on 功能写到一个统一的地方,只加载一次
|
||||||
|
ipc.removeAllListeners(ipcApiRoute.sendNotification);
|
||||||
|
ipc.on(ipcApiRoute.sendNotification, (event, result) => {
|
||||||
|
if (Object.prototype.toString.call(result) == '[object Object]') {
|
||||||
|
this.$message.info(result.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendNotification (index) {
|
||||||
|
ipc.send(ipcApiRoute.sendNotification, toRaw(this.views[index]));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-notification {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-powermonitor">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 监控电源状态
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<p>* 当前状态:{{ currentStatus }}</p>
|
||||||
|
</a-space>
|
||||||
|
<p>* 拔掉电源,使用电池供电</p>
|
||||||
|
<p>* 接入电源</p>
|
||||||
|
<p>* 锁屏</p>
|
||||||
|
<p>* 解锁</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentStatus: '无',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init () {
|
||||||
|
ipc.removeAllListeners(ipcApiRoute.initPowerMonitor);
|
||||||
|
ipc.on(ipcApiRoute.initPowerMonitor, (event, result) => {
|
||||||
|
if (Object.prototype.toString.call(result) == '[object Object]') {
|
||||||
|
this.currentStatus = result.msg;
|
||||||
|
this.$message.info(result.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ipc.send(ipcApiRoute.initPowerMonitor, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-powermonitor {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<div id="app-base-screen">
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
1. 屏幕信息
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="getScreen(0)">获取当前鼠标位置</a-button>
|
||||||
|
<a-button @click="getScreen(1)">获取主屏幕</a-button>
|
||||||
|
<a-button @click="getScreen(2)">获取所有屏幕</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-1">
|
||||||
|
<span>
|
||||||
|
结果:
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="one-block-2">
|
||||||
|
<a-descriptions title="">
|
||||||
|
<a-descriptions-item v-for="(info, index) in data" :key="index" :label="info.title" >
|
||||||
|
{{ info.desc }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { ipcApiRoute } from '@/api/main';
|
||||||
|
import { ipc } from '@/utils/ipcRenderer';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getScreen (index) {
|
||||||
|
ipc.invoke(ipcApiRoute.getScreen, index).then(result => {
|
||||||
|
this.data = result;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
#app-base-screen {
|
||||||
|
padding: 0px 10px;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
.one-block-1 {
|
||||||
|
font-size: 16px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
.one-block-2 {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||