feat: update articles or snippets
@@ -33,12 +33,10 @@
|
||||
|
||||
项目中我们使用了 Electron 进行 Windows 设备上的软件安装、软件运行监控与日志动态监控,使用 [jsmpeg-vnc](https://github.com/phoboslab/jsmpeg-vnc) 提供的 Windows 上实时屏幕共享的能力,其基于 WebSocket 传输标准 VNC 协议的数据;如果需要进行公网 WebSocket 转发可以自行搭建 WebSocket Forward 服务。
|
||||
|
||||
目前本项目正在尝试使用 FFMpeg + WebSocket 方式以及原生桌面流推向 WebRTC 两种方式,分别参阅不同的目录。
|
||||
目前本项目正在尝试使用 Mpegts + WebSocket 方式以及原生桌面流推向 WebRTC 两种方式,分别参阅不同的目录,对于 Electron 客户端请参阅 [m-fe-electron](https://github.com/wx-chevalier/m-fe-electron)。
|
||||
|
||||
## Nav | 导航
|
||||
|
||||
- Electron 项目模板参考 [wx-chevalier/m-fe-electron](https://github.com/wx-chevalier/m-fe-electron)。
|
||||
|
||||
# Getting Started
|
||||
|
||||
To get a local copy up and running follow these simple steps.
|
||||
@@ -105,9 +103,11 @@ Distributed under the MIT License. See `LICENSE` for more information.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- [Awesome-Lists](https://github.com/wx-chevalier/Awesome-Lists): 📚 Guide to Galaxy, curated, worthy and up-to-date links/reading list for ITCS-Coding/Algorithm/SoftwareArchitecture/AI. 💫 ITCS-编程/算法/软件架构/人工智能等领域的文章/书籍/资料/项目链接精选。
|
||||
- [Awesome-Lists #Project#](https://github.com/wx-chevalier/Awesome-Lists): 📚 Guide to Galaxy, curated, worthy and up-to-date links/reading list for ITCS-Coding/Algorithm/SoftwareArchitecture/AI. 💫 ITCS-编程/算法/软件架构/人工智能等领域的文章/书籍/资料/项目链接精选。
|
||||
|
||||
- [Awesome-CS-Books](https://github.com/wx-chevalier/Awesome-CS-Books): :books: Awesome CS Books/Series(.pdf by git lfs) Warehouse for Geeks, ProgrammingLanguage, SoftwareEngineering, Web, AI, ServerSideApplication, Infrastructure, FE etc. :dizzy: 优秀计算机科学与技术领域相关的书籍归档。
|
||||
- [Awesome-CS-Books #Project#](https://github.com/wx-chevalier/Awesome-CS-Books): :books: Awesome CS Books/Series(.pdf by git lfs) Warehouse for Geeks, ProgrammingLanguage, SoftwareEngineering, Web, AI, ServerSideApplication, Infrastructure, FE etc. :dizzy: 优秀计算机科学与技术领域相关的书籍归档。
|
||||
|
||||
- [jsmpeg #Project#](https://github.com/phoboslab/jsmpeg): JSMpeg is a Video Player written in JavaScript. It consists of an MPEG-TS demuxer, MPEG1 video & MP2 audio decoders, WebGL & Canvas2D renderers and WebAudio sound output. JSMpeg can load static videos via Ajax and allows low latency streaming (~50ms) via WebSockets.
|
||||
|
||||
## Copyright & More | 延伸阅读
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
*.log
|
||||
*.tsbuildinfo
|
||||
.cache
|
||||
.vscode
|
||||
coverage
|
||||
dist
|
||||
node_modules
|
||||
@@ -1,13 +0,0 @@
|
||||
module.exports = {
|
||||
extends: '@m-fe/eslint-config/base',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-extraneous-class': 0,
|
||||
'@typescript-eslint/member-naming': 0,
|
||||
},
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
# Node files
|
||||
node_modules
|
||||
|
||||
# OS junk files
|
||||
.DS_Store
|
||||
.stylelintcache
|
||||
.eslintcache
|
||||
|
||||
# Project specific stuff
|
||||
.cache-loader
|
||||
@coverage
|
||||
*.log
|
||||
dist
|
||||
build
|
||||
out
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
{ "language": "typescript", "autoFix": true },
|
||||
{ "language": "typescriptreact", "autoFix": true }
|
||||
]
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||

|
||||
|
||||
# m-fe/react-ts-electron
|
||||
|
||||
mf-rte 是 [fe-boilerplates](https://github.com/wx-chevalier/fe-boilerplates) 的一部分,在 [Web 开发导论/微前端与大前端](https://github.com/wx-chevalier/Web-Series)一文中,笔者简述了微服务与微前端的设计理念以及微前端的潜在可行方案。微服务与微前端,都是希望将某个单一的单体应用,转化为多个可以独立运行、独立开发、独立部署、独立维护的服务或者应用的聚合,从而满足业务快速变化及分布式多团队并行开发的需求。如康威定律(Conway’s Law)所言,设计系统的组织,其产生的设计和架构等价于组织间的沟通结构;微服务与微前端不仅仅是技术架构的变化,还包含了组织方式、沟通方式的变化。微服务与微前端原理和软件工程,面向对象设计中的原理同样相通,都是遵循单一职责(Single Responsibility)、关注分离(Separation of Concerns)、模块化(Modularity)与分而治之(Divide & Conquer)等基本的原则。
|
||||
|
||||

|
||||
|
||||
当我们考量项目框架、模板或者脚手架的时候,首先想到的点就是希望尽可能对上层屏蔽细节,但是对于长期维护的、多人协作的中大型项目而言,如果项目的主导者直接使用了部分抽象的脚手架,不免会给未来的更新、迭代带来一定的技术负债;同时,目前也有很多成熟的工程化脚手架,因此笔者选择以项目模板的形式抽象出微前端中所需要的部分。尽可能地遵循简约、直观的原则,减少抽象/Magic Function 等;大型项目可能会抽象出专用的开发工具流,但是对于大部分项目而言,在现有框架/工具链的基础上进行适当封装会是较优选择。
|
||||
|
||||
# Develop
|
||||
|
||||
```sh
|
||||
# 拉取并且提取出子项目
|
||||
$ git clone https://github.com/wx-chevalier/m-fe-rte
|
||||
|
||||
# 添加全局的依赖更新工具
|
||||
$ yarn global add npm-check-updates npm-run-all copyfiles cross-env
|
||||
|
||||
# 为各个子项目安装依赖,以及链接各个子项目
|
||||
$ ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ ELECTRON_CUSTOM_DIR=8.2.0 yarn install --registry https://registry.npm.taobao.org/
|
||||
|
||||
# 编译依赖
|
||||
$ yarn build
|
||||
# 启用 Web App & Node 的开发服务器
|
||||
$ yarn run dev
|
||||
# 打开 Electron 应用
|
||||
$ yarn start
|
||||
|
||||
# 执行 Lint 操作
|
||||
$ yarn lint
|
||||
|
||||
# 构建
|
||||
# 构建应用程序代码
|
||||
$ npm run build
|
||||
# 构建对应的 Windows 系统发布包
|
||||
$ npm run build:exe
|
||||
```
|
||||
|
||||
值得说明的是,微前端作为概念对于不同人承载了不同的考量,其实现方式、落地路径也是见仁见智,若有不妥,敬请指教。
|
||||
|
||||
## Tips
|
||||
|
||||
1. 因为有指定 Windows 平台的依赖包存在,因此在 `*nix` 系统中运行 yanr 系列命令会报 incompatible platform 的错误。解决方法:在命令中加入 --ignore-platform 选项。
|
||||
|
||||
2. 为了解决 Windows 平台上的应用拖动问题,使用了 `-webkit-app-region` CSS 属性,这也导致了继承了该规则的元素无法响应点击事件。解决方法:不需要响应拖动的元素设置 `-webkit-app-region: no-drag;`
|
||||
|
||||
## Windows XP
|
||||
|
||||
- 下载 miniblink 的编译文件
|
||||
|
||||
下载最新的 miniblink 编译文件。例如:我们选择这个版本 2018-10-11 版,SDK 下载地址:https://pan.baidu.com/s/1sGkmAF34vZxXJj8dfekCXA
|
||||
|
||||
- 找出替换 electron 的关键文件
|
||||
|
||||
下载后,解压文件,将 electron 必要的几个文件或者文件夹拷贝出来。miniblink 替换 electron 用到 3 个文件:文件 node.dll 和 mini-electron.exe 文件夹 resources。我们把这个几个文件拷贝到 D:\myApp\ 中:
|
||||
|
||||
```
|
||||
- D:\myApp\
|
||||
- node.dll
|
||||
- `mini-electron.exe`
|
||||
- resources\
|
||||
- miniblink.asar\
|
||||
3.3 从 electron 迁移到 miniblink
|
||||
拿 桌面版脑图 来举例,下载最新的 桌面版脑图编译文件 。
|
||||
```
|
||||
|
||||
我们下载文件 DesktopNaotu-win32-ia32.zip ,解压后可以看到 桌面版脑图 编译后的目录结构如下:
|
||||
|
||||
```
|
||||
- DesktopNaotu-win32-ia32\
|
||||
|
||||
- electron 相关的其他文件(dll,exe,...)
|
||||
- resources\
|
||||
- app\
|
||||
- electron.asar
|
||||
我们把资源文件夹 resources\ 下的 electron.asar 和 app 拷贝到,miniblink 的 resources\ 下。
|
||||
|
||||
- D:\myApp\
|
||||
|
||||
- node.dll
|
||||
- mini-electron.exe
|
||||
- resources\
|
||||
- miniblink.asar\
|
||||
- app\
|
||||
- electron.asar
|
||||
到这一步,可以打开 mini-electron.exe ,从 miniblink 中打开应用程序了。我们继续优化。
|
||||
```
|
||||
|
||||
- 优化应用程序
|
||||
|
||||
修改应用程序名称。如:把 mini-electron.exe 改为 DesktopNaotu.exe,修改图标和文件信息。通过 reshacker 来修改,360 可能会提示病毒操作,全部允许即可。完成,我把示例程序放到 Release 中,感兴趣的可以下载查看。
|
||||
|
||||
## Nav | 关联项目
|
||||
|
||||
- [m-fe-configs](https://github.com/wx-chevalier/m-fe-configs):Common Dev Configs(ESLint, Prettier, Husky, etc.) for Micro-Frontend Apps
|
||||
|
||||
- [m-fe-rtw](https://github.com/wx-chevalier/m-fe-rtw): Micro-Frontend boilerplate with React & TypeScript & Webpack, for complicated cooperative applications. | 微前端项目模板
|
||||
|
||||
- [m-fe-vtw](https://github.com/wx-chevalier/m-fe-vtw): Micro-Frontend boilerplate with Vue & TypeScript & Webpack, for complicated cooperative applications. | 微前端项目模板
|
||||
|
||||
- [m-fe-rm](https://github.com/wx-chevalier/m-fe-rm): 基于 React & TS & Webpack & APICloud 提供快速移动端应用开发的能力
|
||||
|
||||
- [m-fe-taro](https://github.com/wx-chevalier/m-fe-taro): 基于 Taro & TS 的多端小程序开发模板。
|
||||
|
||||
- [m-fe-scaffold](https://github.com/wx-chevalier/m-fe-scaffold/): Cli Toolkits for Web Development & Deploy on Kubernetes,微前端实践中沉淀的一系列脚手架工具。
|
||||
|
||||
- [m-fe-libs](https://github.com/wx-chevalier/m-fe-libs): Micro-Frontend boilerplate with React & TypeScript & Webpack, for complicated cooperative applications. | 微前端项目模板
|
||||
|
||||
--
|
||||
|
||||
- [react-snippets](https://github.com/wx-chevalier/react-snippets): React Snippets(.ts/.tsx), about design patterns/techniques used while developing with React and TypeScript.
|
||||
|
||||
- [vue-snippets](https://github.com/wx-chevalier/vue-snippets): Vue Snippets(.js/.ts), about design patterns/techniques used while developing with Vue and JavaScript/TypeScript.
|
||||
|
||||
- [fractal-components](https://github.com/wx-chevalier/fractal-components): Massive Fractal Components in Several Libraries(Vanilla, React, Vue, Weapp), for building your great apps easily again
|
||||
|
||||
- [Legoble](https://github.com/wx-chevalier/Legoble): Build your apps like stacking Lego blocks 💫 总想自己实现一款可视化配置的动态应用构建工具,动态表单、动态布局、动态报告、动态规则、动态选择、动态流程
|
||||
|
||||
# About
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- [electron-process-reporter](https://github.com/getstation/electron-process-reporter)
|
||||
|
||||
- [electron-about-window](https://github.com/rhysd/electron-about-window)
|
||||
|
||||
- [node-auto-launch](https://github.com/Teamwork/node-auto-launch)
|
||||
|
||||
- [nucleus](https://github.com/atlassian/nucleus): A configurable and versatile update server for all your Electron apps.
|
||||
|
||||
- [jsmpeg-vnc](https://github.com/phoboslab/jsmpeg-vnc): A low latency, high framerate screen sharing server for Windows and client for browsers.
|
||||
|
||||
## Todos
|
||||
|
||||
- [x] 使用 ESLint 替换 TSLint
|
||||
- [x] 将现有项目中的通用配置抽出到 m-fe-configs 中
|
||||
|
||||
## Copyright & More | 延伸阅读
|
||||
|
||||
笔者所有文章遵循 [知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh),欢迎转载,尊重版权。您还可以前往 [NGTE Books](https://ng-tech.icu/books/) 主页浏览包含知识体系、编程语言、软件工程、模式与架构、Web 与大前端、服务端开发实践与工程架构、分布式基础架构、人工智能与深度学习、产品运营与创业等多类目的书籍列表:
|
||||
|
||||

|
||||
@@ -1,26 +0,0 @@
|
||||
appId: com.biz
|
||||
buildVersion: 0.0.1
|
||||
productName: Biz App
|
||||
artifactName: ${productName}-${version}.${ext}
|
||||
extraResources: extra/
|
||||
directories:
|
||||
output: './out'
|
||||
files:
|
||||
- '**/*'
|
||||
- '!**/*.map'
|
||||
mac:
|
||||
target: pkg
|
||||
win:
|
||||
target: nsis
|
||||
requestedExecutionLevel: requireAdministrator
|
||||
nsis:
|
||||
oneClick: false
|
||||
perMachine: true
|
||||
allowToChangeInstallationDirectory: true
|
||||
installerLanguages: zh-CN
|
||||
language: 2052
|
||||
menuCategory: Biz App
|
||||
deleteAppDataOnUninstall: true
|
||||
publish:
|
||||
provider: generic
|
||||
url: http://tool.bizapp
|
||||
@@ -1,71 +0,0 @@
|
||||
' Notes: wanted to implement this using a class but:
|
||||
' 1. No matter what I did I could not assign the result of GetObject to a private member
|
||||
' 2. It looks as if all methods were treated as subs from the outside world which is not good since
|
||||
' some of these need to return a value
|
||||
'
|
||||
|
||||
Set private_oReg = GetObject("winmgmts:\root\default:StdRegProv")
|
||||
|
||||
Function SetStringValue(constHive, strSubKey, strValueName, strValue)
|
||||
SetStringValue = private_oReg.SetStringValue(constHive, strSubKey, strValueName, strValue)
|
||||
End Function
|
||||
|
||||
Sub GetStringValue(constHive, strKey, strValueName, strValue)
|
||||
private_oReg.GetStringValue constHive, strKey, strValueName, strValue
|
||||
End Sub
|
||||
|
||||
Function SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
|
||||
SetExpandedStringValue = private_oReg.SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
|
||||
End Function
|
||||
|
||||
Sub GetExpandedStringValue(constHive, strKey, strValueName, strValue)
|
||||
private_oReg.GetExpandedStringValue constHive, strKey, strValueName, strValue
|
||||
End Sub
|
||||
|
||||
Function SetMultiStringValue(constHive, strSubKey, strValueName, arrValue)
|
||||
SetMultiStringValue = private_oReg.SetMultiStringValue(constHive, strSubKey, strValueName, arrValue)
|
||||
End Function
|
||||
|
||||
Sub GetMultiStringValue(constHive, strKey, strValueName, arrStrValue)
|
||||
private_oReg.GetMultiStringValue constHive, strKey, strValueName, arrStrValue
|
||||
End Sub
|
||||
|
||||
Function SetDWORDValue(constHive, strSubKey, strValueName, arrValue)
|
||||
SetDWORDValue = private_oReg.SetDWORDValue(constHive, strSubKey, strValueName, arrValue)
|
||||
End Function
|
||||
|
||||
Sub GetDWORDValue(constHive, strKey, strValueName, intDWordValue)
|
||||
private_oReg.GetDWORDValue constHive, strKey, strValueName, intDWordValue
|
||||
End Sub
|
||||
|
||||
Function SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue)
|
||||
SetQWORDValue = private_oReg.SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue)
|
||||
End Function
|
||||
|
||||
Sub GetQWORDValue(constHive, strKey, strValueName, intQWordValue)
|
||||
private_oReg.GetQWORDValue constHive, strKey, strValueName, intQWordValue
|
||||
End Sub
|
||||
|
||||
Function SetBinaryValue(constHive, strSubKey, strValueName, arrValue)
|
||||
SetBinaryValue = private_oReg.SetBinaryValue(constHive, strSubKey, strValueName, arrValue)
|
||||
End Function
|
||||
|
||||
Sub GetBinaryValue(constHive, strKey, strValueName, arrBinaryValue)
|
||||
private_oReg.GetBinaryValue constHive, strKey, strValueName, arrBinaryValue
|
||||
End Sub
|
||||
|
||||
Function EnumKey(constHive, strSubKey, arrKeyNames)
|
||||
EnumKey = private_oReg.EnumKey(constHive, strSubKey, arrKeyNames)
|
||||
End Function
|
||||
|
||||
Function EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes)
|
||||
EnumValues = private_oReg.EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes)
|
||||
End Function
|
||||
|
||||
Function CreateKey(constHive, strSubKey)
|
||||
CreateKey = private_oReg.CreateKey(constHive, strSubKey)
|
||||
End Function
|
||||
|
||||
Function DeleteKey(constHive, strSubKey)
|
||||
DeleteKey = private_oReg.DeleteKey(constHive, strSubKey)
|
||||
End Function
|
||||
@@ -1,358 +0,0 @@
|
||||
' Notes: wanted to implement this using a class but:
|
||||
' 1. No matter what I did I could not assign the result of GetObject to a private member
|
||||
' 2. It looks as if all methods were treated as subs from the outside world which is not good since
|
||||
' some of these need to return a value
|
||||
|
||||
' should be removed when migration is complete
|
||||
Set private_oReg = GetObject("winmgmts:\root\default:StdRegProv")
|
||||
|
||||
Set private_oCtx = CreateObject("WbemScripting.SWbemNamedValueSet")
|
||||
private_oCtx.Add "__ProviderArchitecture", CInt(OSArchitecture)
|
||||
|
||||
Set private_oLocator = CreateObject("Wbemscripting.SWbemLocator")
|
||||
Set private_oServices = private_oLocator.ConnectServer(".", "root\default","","",,,,private_oCtx)
|
||||
Set private_oRegSpecific = private_oServices.Get("StdRegProv")
|
||||
|
||||
Function CheckAccess(hDefKey,sSubKeyName,uRequired, bGranted )
|
||||
Set Inparams = private_oRegSpecific.Methods_("CheckAccess").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.uRequired = uRequired
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("CheckAccess", Inparams,,private_oCtx)
|
||||
|
||||
bGranted = Outparams.bGranted
|
||||
|
||||
|
||||
CheckAccess = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function CreateKey(hDefKey,sSubKeyName)
|
||||
Set Inparams = private_oRegSpecific.Methods_("CreateKey").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("CreateKey", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
CreateKey = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function DeleteKey(hDefKey,sSubKeyName)
|
||||
Set Inparams = private_oRegSpecific.Methods_("DeleteKey").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("DeleteKey", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
DeleteKey = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function DeleteValue(hDefKey,sSubKeyName,sValueName)
|
||||
Set Inparams = private_oRegSpecific.Methods_("DeleteValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("DeleteValue", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
DeleteValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function EnumKey(hDefKey,sSubKeyName, sNames )
|
||||
Set Inparams = private_oRegSpecific.Methods_("EnumKey").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("EnumKey", Inparams,,private_oCtx)
|
||||
|
||||
sNames = Outparams.sNames
|
||||
|
||||
|
||||
EnumKey = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function EnumValues(hDefKey,sSubKeyName, sNames,Types )
|
||||
Set Inparams = private_oRegSpecific.Methods_("EnumValues").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("EnumValues", Inparams,,private_oCtx)
|
||||
|
||||
sNames = Outparams.sNames
|
||||
|
||||
Types = Outparams.Types
|
||||
|
||||
|
||||
EnumValues = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function GetBinaryValue(hDefKey,sSubKeyName,sValueName, uValue )
|
||||
Set Inparams = private_oRegSpecific.Methods_("GetBinaryValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("GetBinaryValue", Inparams,,private_oCtx)
|
||||
|
||||
uValue = Outparams.uValue
|
||||
|
||||
|
||||
GetBinaryValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function GetDWORDValue(hDefKey,sSubKeyName,sValueName, uValue )
|
||||
Set Inparams = private_oRegSpecific.Methods_("GetDWORDValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("GetDWORDValue", Inparams,,private_oCtx)
|
||||
|
||||
uValue = Outparams.uValue
|
||||
|
||||
|
||||
GetDWORDValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function GetExpandedStringValue(hDefKey,sSubKeyName,sValueName, sValue )
|
||||
Set Inparams = private_oRegSpecific.Methods_("GetExpandedStringValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("GetExpandedStringValue", Inparams,,private_oCtx)
|
||||
|
||||
sValue = Outparams.sValue
|
||||
|
||||
|
||||
GetExpandedStringValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function GetMultiStringValue(hDefKey,sSubKeyName,sValueName, sValue )
|
||||
Set Inparams = private_oRegSpecific.Methods_("GetMultiStringValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("GetMultiStringValue", Inparams,,private_oCtx)
|
||||
|
||||
sValue = Outparams.sValue
|
||||
|
||||
|
||||
GetMultiStringValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function GetQWORDValue(hDefKey,sSubKeyName,sValueName, uValue )
|
||||
Set Inparams = private_oRegSpecific.Methods_("GetQWORDValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("GetQWORDValue", Inparams,,private_oCtx)
|
||||
|
||||
uValue = Outparams.uValue
|
||||
|
||||
|
||||
GetQWORDValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function GetSecurityDescriptor(hDefKey,sSubKeyName, Descriptor )
|
||||
Set Inparams = private_oRegSpecific.Methods_("GetSecurityDescriptor").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("GetSecurityDescriptor", Inparams,,private_oCtx)
|
||||
|
||||
Descriptor = Outparams.Descriptor
|
||||
|
||||
|
||||
GetSecurityDescriptor = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function GetStringValue(hDefKey,sSubKeyName,sValueName, sValue )
|
||||
Set Inparams = private_oRegSpecific.Methods_("GetStringValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("GetStringValue", Inparams,,private_oCtx)
|
||||
|
||||
sValue = Outparams.sValue
|
||||
|
||||
|
||||
GetStringValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function SetBinaryValue(hDefKey,sSubKeyName,sValueName,uValue)
|
||||
Set Inparams = private_oRegSpecific.Methods_("SetBinaryValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
Inparams.uValue = uValue
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("SetBinaryValue", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
SetBinaryValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function SetDWORDValue(hDefKey,sSubKeyName,sValueName,uValue)
|
||||
Set Inparams = private_oRegSpecific.Methods_("SetDWORDValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
Inparams.uValue = uValue
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("SetDWORDValue", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
SetDWORDValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function SetExpandedStringValue(hDefKey,sSubKeyName,sValueName,sValue)
|
||||
Set Inparams = private_oRegSpecific.Methods_("SetExpandedStringValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
Inparams.sValue = sValue
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("SetExpandedStringValue", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
SetExpandedStringValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function SetMultiStringValue(hDefKey,sSubKeyName,sValueName,sValue)
|
||||
Set Inparams = private_oRegSpecific.Methods_("SetMultiStringValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
Inparams.sValue = sValue
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("SetMultiStringValue", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
SetMultiStringValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function SetQWORDValue(hDefKey,sSubKeyName,sValueName,uValue)
|
||||
Set Inparams = private_oRegSpecific.Methods_("SetQWORDValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
Inparams.uValue = uValue
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("SetQWORDValue", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
SetQWORDValue = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function SetSecurityDescriptor(hDefKey,sSubKeyName,Descriptor)
|
||||
Set Inparams = private_oRegSpecific.Methods_("SetSecurityDescriptor").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.Descriptor = Descriptor
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("SetSecurityDescriptor", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
SetSecurityDescriptor = 0
|
||||
|
||||
End Function
|
||||
|
||||
Function SetStringValue(hDefKey,sSubKeyName,sValueName,sValue)
|
||||
Set Inparams = private_oRegSpecific.Methods_("SetStringValue").Inparameters
|
||||
|
||||
Inparams.hDefKey = hDefKey
|
||||
|
||||
Inparams.sSubKeyName = sSubKeyName
|
||||
|
||||
Inparams.sValueName = sValueName
|
||||
|
||||
Inparams.sValue = sValue
|
||||
|
||||
set Outparams = private_oRegSpecific.ExecMethod_("SetStringValue", Inparams,,private_oCtx)
|
||||
|
||||
|
||||
SetStringValue = 0
|
||||
|
||||
End Function
|
||||
@@ -1,7 +0,0 @@
|
||||
<job id="JsonSafeStreamTest">
|
||||
<script language="VBScript" src="util.vbs" />
|
||||
<script language="VBScript">
|
||||
str = """" & vbcrlf & "测试\"
|
||||
Write("{ ""a"": """ & JsonSafe(str) & """}" & vbcrlf)
|
||||
</script>
|
||||
</job>
|
||||
@@ -1,32 +0,0 @@
|
||||
<job id="createKeyStream">
|
||||
<script language="VBScript" src="util.vbs" />
|
||||
<script language="VBScript" src="regUtil.vbs" />
|
||||
<script language="VBScript">
|
||||
|
||||
CheckZeroArgs("usage: cscript regCreateKey.wsf architecture")
|
||||
DetermineOSArchitecture()
|
||||
LoadRegistryImplementationByOSArchitecture()
|
||||
|
||||
Do While Not stdin.AtEndOfLine
|
||||
strLine = stdin.ReadLine()
|
||||
strLine = unescape(trim(strLine))
|
||||
|
||||
If IsNull(strLine) or strLine = "" Then
|
||||
WScript.Quit 25127
|
||||
End If
|
||||
|
||||
ParseHiveAndSubKey strLine, constHive, strSubKey
|
||||
|
||||
if IsNull(constHive) Then
|
||||
WriteLineErr "unsupported hive " & strLine
|
||||
WScript.Quit 25122
|
||||
End If
|
||||
|
||||
Result = CreateKey(constHive, strSubKey)
|
||||
|
||||
If Not Result = 0 Then
|
||||
WScript.Quit Result
|
||||
End If
|
||||
Loop
|
||||
</script>
|
||||
</job>
|
||||
@@ -1,29 +0,0 @@
|
||||
<job id="deleteKey">
|
||||
<script language="VBScript" src="util.vbs" />
|
||||
<script language="VBScript" src="regUtil.vbs" />
|
||||
<script language="VBScript">
|
||||
|
||||
CheckZeroArgs("usage: cscript regDeleteKey.wsf architecture")
|
||||
DetermineOSArchitecture()
|
||||
LoadRegistryImplementationByOSArchitecture()
|
||||
|
||||
Do While Not stdin.AtEndOfLine
|
||||
|
||||
strLine = stdin.ReadLine()
|
||||
strLine = unescape(trim(strLine))
|
||||
|
||||
ParseHiveAndSubKey strLine, constHive, strSubKey
|
||||
|
||||
if IsNull(constHive) Then
|
||||
WriteLineErr "unsupported hive " & strLine
|
||||
WScript.Quit 25122
|
||||
End If
|
||||
|
||||
Result = DeleteKey(constHive, strSubKey)
|
||||
|
||||
If Not Result = 0 Then
|
||||
WScript.Quit Result
|
||||
End If
|
||||
Loop
|
||||
</script>
|
||||
</job>
|
||||
@@ -1,46 +0,0 @@
|
||||
'
|
||||
' Lists the sub keys and values of a given registry key
|
||||
'
|
||||
' cscript regList.wsg HKLM\Software
|
||||
'
|
||||
' Will Yield:
|
||||
'
|
||||
' {
|
||||
' "hklm\software": {
|
||||
' "keys": [ .. array of sub keys .. ],
|
||||
' "values": {
|
||||
' "moo": {
|
||||
' "type": "REG_SZ",
|
||||
' "value": "bar"
|
||||
' }
|
||||
' }
|
||||
' }
|
||||
' }
|
||||
<job id="list">
|
||||
<script language="VBScript" src="util.vbs" />
|
||||
<script language="VBScript" src="regUtil.vbs" />
|
||||
<script language="VBScript">
|
||||
CheckZeroArgs("usage: cscript regList.wsf architecture regpath1 [regpath2] ... [regpathN]")
|
||||
DetermineOSArchitecture()
|
||||
LoadRegistryImplementationByOSArchitecture()
|
||||
|
||||
Write "{"
|
||||
For v = 1 To args.Count - 1
|
||||
if (v > 1) Then
|
||||
Write ","
|
||||
End If
|
||||
|
||||
Write """" & JsonSafe(args(v)) & """: "
|
||||
|
||||
ParseHiveAndSubKey args(v), constHive, strSubKey
|
||||
|
||||
if IsNull(constHive) Then
|
||||
WriteLineErr "unsupported hive " & args(v)
|
||||
WScript.Quit 25122
|
||||
End If
|
||||
|
||||
ListChildrenAsJson constHive, strSubKey
|
||||
Next
|
||||
Write "}"
|
||||
</script>
|
||||
</job>
|
||||
@@ -1,46 +0,0 @@
|
||||
'
|
||||
' Lists the sub keys and values of a given registry key, this script is slightly different
|
||||
' than regList because it reads stdin for the keys to list
|
||||
'
|
||||
' cscript regList.wsg HKLM\Software
|
||||
'
|
||||
' Will Yield:
|
||||
'
|
||||
' {
|
||||
' "hklm\software": {
|
||||
' "keys": [ .. array of sub keys .. ],
|
||||
' "values": {
|
||||
' "moo": {
|
||||
' "type": "REG_SZ",
|
||||
' "value": "bar"
|
||||
' }
|
||||
' }
|
||||
' }
|
||||
' }
|
||||
<job id="listStream">
|
||||
<script language="VBScript" src="util.vbs" />
|
||||
<script language="VBScript" src="regUtil.vbs" />
|
||||
<script language="VBScript">
|
||||
CheckZeroArgs("usage: cscript regList.wsf architecture")
|
||||
DetermineOSArchitecture()
|
||||
LoadRegistryImplementationByOSArchitecture()
|
||||
|
||||
Do While Not stdin.AtEndOfLine
|
||||
|
||||
strLine = stdin.ReadLine()
|
||||
strLine = unescape(trim(strLine))
|
||||
|
||||
ParseHiveAndSubKey strLine, constHive, strSubKey
|
||||
|
||||
if IsNull(constHive) Then
|
||||
WriteLineErr "unsupported hive " & strLine
|
||||
WScript.Quit 25122
|
||||
End If
|
||||
|
||||
Write "{ ""key"" : """ & JsonSafe(strLine) & """, ""data"": "
|
||||
ListChildrenAsJson constHive, strSubKey
|
||||
Write "}" & vbcrlf
|
||||
Loop
|
||||
|
||||
</script>
|
||||
</job>
|
||||
@@ -1,56 +0,0 @@
|
||||
<job id="putValue">
|
||||
<script language="VBScript" src="util.vbs" />
|
||||
<script language="VBScript" src="regUtil.vbs" />
|
||||
<script language="VBScript">
|
||||
usage = "usage: cscript regPutValue.wsf architecture" & vbNewLine _
|
||||
& "types: REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_MULTI_SZ, REG_QWORD"
|
||||
|
||||
CheckZeroArgs(usage)
|
||||
DetermineOSArchitecture()
|
||||
LoadRegistryImplementationByOSArchitecture()
|
||||
|
||||
ReadCount = 0
|
||||
Dim lineArgs(3)
|
||||
|
||||
Do While Not stdin.AtEndOfLine
|
||||
strLine = stdin.ReadLine()
|
||||
strLine = unescape(trim(strLine))
|
||||
|
||||
If IsNull(strLine) or strLine = "" Then
|
||||
WScript.Quit 25127
|
||||
End If
|
||||
|
||||
lineArgs(ReadCount) = strLine
|
||||
|
||||
ReadCount = ReadCount + 1
|
||||
|
||||
If ReadCount = 4 Then
|
||||
|
||||
ParseHiveAndSubKey lineArgs(0), constHive, strSubKey
|
||||
|
||||
if IsNull(constHive) Then
|
||||
WriteLineErr "unsupported hive " & lineArgs(0)
|
||||
WScript.Quit 25122
|
||||
End If
|
||||
|
||||
strValueName = lineArgs(1)
|
||||
strValue = lineArgs(2)
|
||||
strType = lineArgs(3)
|
||||
|
||||
Result = PutValue(constHive, strSubKey, strValueName, strValue, strType)
|
||||
|
||||
If Not Result = 0 Then
|
||||
WriteLineErr "error while putting value: " & result
|
||||
WScript.Quit Result
|
||||
End If
|
||||
|
||||
ReadCount = 0
|
||||
Erase lineArgs
|
||||
End If
|
||||
Loop
|
||||
|
||||
If ReadCount <> 0 Then
|
||||
WScript.Quit 25123
|
||||
End If
|
||||
</script>
|
||||
</job>
|
||||
@@ -1,326 +0,0 @@
|
||||
' TODO: consider incorporating a json writer of some sort instead of adhoc solution like the following
|
||||
' e.g: http://demon.tw/my-work/vbs-json.html
|
||||
|
||||
const HKEY_CLASSES_ROOT = &H80000000
|
||||
const HKEY_CURRENT_USER = &H80000001
|
||||
const HKEY_LOCAL_MACHINE = &H80000002
|
||||
const HKEY_USERS = &H80000003
|
||||
const HKEY_CURRENT_CONFIG = &H80000005
|
||||
|
||||
Sub LoadRegistryImplementationByOSArchitecture()
|
||||
If IsNull(OSArchitecture) Then
|
||||
WriteLineErr "missing OSArchitecture global. did not call util.DetermineOSArchitecture? or Forgot to load util.vbs?"
|
||||
WScript.Quit 25125
|
||||
End If
|
||||
|
||||
If OSArchitecture = "A" Then
|
||||
Include "ArchitectureAgnosticRegistry.vbs"
|
||||
Else
|
||||
Include "ArchitectureSpecificRegistry.vbs"
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Function PutValue(constHive, strSubKey, strValueName, strValue, strType)
|
||||
Select Case UCase(strType)
|
||||
|
||||
Case "REG_SZ"
|
||||
PutValue = SetStringValue(constHive, strSubKey, strValueName, strValue)
|
||||
|
||||
Case "REG_EXPAND_SZ"
|
||||
PutValue = SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
|
||||
|
||||
Case "REG_BINARY"
|
||||
PutValue = SetBinaryValue(constHive, strSubKey, strValueName, ToBinaryValue(strValue))
|
||||
|
||||
' TODO: need to check that indeed int is the right type here
|
||||
Case "REG_DWORD"
|
||||
PutValue = SetDWORDValue(constHive, strSubKey, strValueName, CDbl(strValue))
|
||||
|
||||
Case "REG_MULTI_SZ"
|
||||
PutValue = SetMultiStringValue(constHive, strSubKey, strValueName, Split(strValue, ","))
|
||||
|
||||
Case "REG_QWORD"
|
||||
PutValue = SetQWORDValue(constHive, strSubKey, strValueName, strValue)
|
||||
|
||||
Case "REG_DEFAULT"
|
||||
PutValue = SetStringValue(constHive, strSubKey, "", strValue)
|
||||
|
||||
Case Else
|
||||
PutValue = SetStringValue(constHive, strSubKey, strValueName, strValue)
|
||||
|
||||
End Select
|
||||
End Function
|
||||
|
||||
' render the child of a sub path strSubKey in hive constHive
|
||||
' as json.
|
||||
Sub ListChildrenAsJson(constHive, strSubKey)
|
||||
|
||||
Dim e1: e1 = EnumKey (constHive, strSubKey, arrKeyNames)
|
||||
If e1 <> 0 Then
|
||||
WScript.Quit e1
|
||||
End If
|
||||
|
||||
Dim e2: e2 = EnumValues (constHive, strSubKey, arrValueNames, arrValueTypes)
|
||||
|
||||
If e2 <> 0 Then
|
||||
WScript.Quit e2
|
||||
End If
|
||||
|
||||
' start outputting json to stdout
|
||||
Write "{"
|
||||
If Not IsNull(arrKeyNames) Then
|
||||
Write """keys"": ["
|
||||
For x = 0 To UBound(arrKeyNames)
|
||||
If (x > 0) Then
|
||||
Write ","
|
||||
End If
|
||||
|
||||
Write """" & JsonSafe(arrKeyNames(x)) & """"
|
||||
Next
|
||||
Write "]"
|
||||
End If
|
||||
|
||||
' TODO: some duplicity of code between the two paths of this condition, this needs to be address at some point
|
||||
If Not IsNull(arrValueNames) Then
|
||||
|
||||
If Not IsNull(arrKeyNames) Then
|
||||
Write ","
|
||||
End If
|
||||
|
||||
Write """values"":{"
|
||||
For y = 0 To UBound(arrValueNames)
|
||||
If y > 0 Then
|
||||
Write ","
|
||||
End If
|
||||
|
||||
strValueName = arrValueNames(y)
|
||||
intValueType = arrValueTypes(y)
|
||||
|
||||
' assign the value to varValue
|
||||
GetValueByType constHive, strSubKey, strValueName, intValueType, varValue
|
||||
|
||||
WriteValue strValueName, intValueType, varValue
|
||||
Next
|
||||
Write "}"
|
||||
Else
|
||||
' fix for keys with only default values in them
|
||||
' see http://stackoverflow.com/questions/8840343/how-to-read-the-default-value-from-registry-in-vbscript
|
||||
GetStringValue constHive, strSubKey, "", strDefaultValue
|
||||
|
||||
If IsNull(strDefaultValue) = false and strDefaultValue <> "" Then
|
||||
If Not IsNull(arrKeyNames) Then
|
||||
Write ","
|
||||
End If
|
||||
|
||||
Write """values"":{"
|
||||
' write the default value with REG_SZ
|
||||
WriteValue "", 1, strDefaultValue
|
||||
Write "}"
|
||||
End If
|
||||
End If
|
||||
|
||||
Write "}"
|
||||
End Sub
|
||||
|
||||
Sub WriteValue (strValueName, intValueType, varValue)
|
||||
Write """"
|
||||
Write JsonSafe(strValueName)
|
||||
Write """:{"
|
||||
Write """type"": """
|
||||
Write RenderType(intValueType)
|
||||
Write ""","
|
||||
Write """value"":"
|
||||
Write RenderValueByType(intValueType, varValue)
|
||||
Write "}"
|
||||
End Sub
|
||||
|
||||
' give a raw HKLM\something\somewhere
|
||||
' output the hive constant and the subkey, in this case:
|
||||
' HKEY_LOCAL_MACHINE will be assigned to outConstHive
|
||||
' and something\somewhere will be assigned to outStrSubKey
|
||||
Sub ParseHiveAndSubKey(strRawKey, outConstHive, outStrSubKey)
|
||||
' split into two parts to deduce the hive and the sub key
|
||||
arrSplitted = Split(strRawKey, "\", 2, 1)
|
||||
|
||||
If UBound(arrSplitted) > 0 Then
|
||||
strHive = arrSplitted(0)
|
||||
outStrSubKey = arrSplitted(1)
|
||||
Else
|
||||
strHive = strRawKey
|
||||
outStrSubKey = ""
|
||||
End If
|
||||
|
||||
outConstHive = StringToHiveConst(UCase(strHive))
|
||||
End Sub
|
||||
|
||||
Function StringToHiveConst(strHive)
|
||||
|
||||
Select Case strHive
|
||||
Case "HKCR"
|
||||
StringToHiveConst = HKEY_CLASSES_ROOT
|
||||
Case "HKCU"
|
||||
StringToHiveConst = HKEY_CURRENT_USER
|
||||
Case "HKLM"
|
||||
StringToHiveConst = HKEY_LOCAL_MACHINE
|
||||
Case "HKU"
|
||||
StringToHiveConst = HKEY_USERS
|
||||
Case "HKCC"
|
||||
StringToHiveConst = HKEY_CURRENT_CONFIG
|
||||
Case Else
|
||||
StringToHiveConst = Null
|
||||
End Select
|
||||
|
||||
End Function
|
||||
|
||||
' TODO: this entire "by type" should be transformed into OOP style
|
||||
' where each type will have a class with render(), getValue() etc...
|
||||
|
||||
' convert a value type number into a string label
|
||||
Function RenderType(intType)
|
||||
RenderType = "REG_UNKNOWN"
|
||||
|
||||
Select Case intType
|
||||
Case 1
|
||||
RenderType = "REG_SZ"
|
||||
Case 2
|
||||
RenderType = "REG_EXPAND_SZ"
|
||||
Case 3
|
||||
RenderType = "REG_BINARY"
|
||||
Case 4
|
||||
RenderType = "REG_DWORD"
|
||||
Case 7
|
||||
RenderType = "REG_MULTI_SZ"
|
||||
Case 11
|
||||
RenderType = "REG_QWORD"
|
||||
Case Else
|
||||
' TODO: should report / throw an error here
|
||||
WriteErr("invalid Registry Value Type " & intType)
|
||||
|
||||
End Select
|
||||
|
||||
End Function
|
||||
|
||||
' render by value type:
|
||||
' string will return as a string with double quotes, e.g "value"
|
||||
' multi string values which return as an array ot strings "["1", "2"]" (double quotes included ofc)
|
||||
' numeric values like DWORD and QWORD just return as the number e.g. 1
|
||||
' byte arrays such as reg_binary return as an array of ints, e.g [1,2,3]
|
||||
Function RenderValueByType(intType, varValue)
|
||||
|
||||
Select Case intType
|
||||
' REG_SZ
|
||||
Case 1
|
||||
RenderValueByType = """" & JsonSafe(varValue) & """"
|
||||
|
||||
' REG_EXPAND_SZ
|
||||
Case 2
|
||||
RenderValueByType = """" & JsonSafe(varValue) & """"
|
||||
|
||||
' REG_BINARY
|
||||
Case 3
|
||||
RenderValueByType = RenderByteArray(varValue)
|
||||
|
||||
' REG_DWORD
|
||||
Case 4
|
||||
RenderValueByType= varValue
|
||||
|
||||
' REG_MULYI_SZ'
|
||||
Case 7
|
||||
|
||||
RenderValueByType = RenderStringArray(varValue)
|
||||
' REG_QWORD
|
||||
Case 11
|
||||
RenderValueByType = varValue
|
||||
Case Else
|
||||
' TODO: should report / throw an error here
|
||||
WriteErr("invalid Registry Value Type " & intType)
|
||||
End Select
|
||||
|
||||
End Function
|
||||
|
||||
' get the value of a registry based on its value type and assign it to out parameter outVarValue
|
||||
Sub GetValueByType(constHive, strKey, strValueName, intType, outVarValue)
|
||||
|
||||
Select Case intType
|
||||
' REG_SZ
|
||||
Case 1
|
||||
GetStringValue constHive, strKey, strValueName, outVarValue
|
||||
Exit Sub
|
||||
|
||||
' REG_EXPAND_SZ
|
||||
Case 2
|
||||
GetExpandedStringValue constHive, strKey, strValueName, outVarValue
|
||||
Exit Sub
|
||||
|
||||
' REG_BINARY
|
||||
Case 3
|
||||
GetBinaryValue constHive, strKey, strValueName, outVarValue
|
||||
Exit Sub
|
||||
|
||||
' REG_DWORD
|
||||
Case 4
|
||||
GetDWORDValue constHive, strKey, strValueName, outVarValue
|
||||
|
||||
' #21 - VBS does not support UInt32. This is the workaround
|
||||
If outVarValue < 0 Then outVarValue = 4294967296 + outVarValue
|
||||
|
||||
Exit Sub
|
||||
|
||||
' REG_MULYI_SZ'
|
||||
Case 7
|
||||
GetMultiStringValue constHive, strKey, strValueName, outVarValue
|
||||
Exit Sub
|
||||
|
||||
' REG_QWORD
|
||||
Case 11
|
||||
GetQWORDValue constHive, strKey, strValueName, outVarValue
|
||||
Exit Sub
|
||||
|
||||
Case Else
|
||||
' TODO: should report / throw an error here
|
||||
WriteErr("invalid Registry Value Type " & intType)
|
||||
End Select
|
||||
|
||||
End Sub
|
||||
|
||||
' render a byte array as a json array of numbers
|
||||
Function RenderByteArray(arr)
|
||||
RenderByteArray = "[]"
|
||||
|
||||
If Not IsNull(arr) Then
|
||||
RenderByteArray = "[" & Join(arr, ",") & "]"
|
||||
End If
|
||||
End Function
|
||||
|
||||
' render a string array as json string array
|
||||
Function RenderStringArray(arr)
|
||||
Result = "["
|
||||
If Not IsNull(arr) Then
|
||||
For t = 0 To UBound(arr)
|
||||
If (t > 0) Then
|
||||
Result = Result & ","
|
||||
End If
|
||||
|
||||
Result = Result & """" & JsonSafe(arr(t)) & """"
|
||||
Next
|
||||
End If
|
||||
Result = Result & "]"
|
||||
|
||||
RenderStringArray = Result
|
||||
End Function
|
||||
|
||||
Function ToBinaryValue(strValue)
|
||||
|
||||
arrValue = Split(strValue, ",")
|
||||
|
||||
If IsNull(arrValue) Then
|
||||
ToBinaryValue = Array()
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
For i = 0 To UBound(arrValue)
|
||||
arrValue(i) = CInt(arrValue(i))
|
||||
Next
|
||||
|
||||
ToBinaryValue = arrValue
|
||||
End Function
|
||||
@@ -1,162 +0,0 @@
|
||||
Set stdout = WScript.StdOut
|
||||
Set stderr = WScript.StdErr
|
||||
Set stdin = WScript.StdIn
|
||||
Set args = WScript.Arguments
|
||||
Set fs = CreateObject("scripting.filesystemobject")
|
||||
Dim OSArchitecture
|
||||
|
||||
Sub WriteErr(message)
|
||||
stderr.Write message
|
||||
End Sub
|
||||
|
||||
Sub WriteLineErr(message)
|
||||
stderr.WriteLine message
|
||||
End Sub
|
||||
|
||||
Sub Write(message)
|
||||
stdout.Write message
|
||||
End Sub
|
||||
|
||||
Sub WriteLine(message)
|
||||
stdout.WriteLine message
|
||||
End Sub
|
||||
|
||||
Function IndexOf(varNeedle, arrHaystack)
|
||||
IndexOf = -1
|
||||
|
||||
If Not IsArray(arrHaystack) Then
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
For xyz = 0 To UBound(arrHaystack)
|
||||
If arrHaystack(xyz) = varNeedle Then
|
||||
IndexOf = xyz
|
||||
Exit Function
|
||||
End If
|
||||
Next
|
||||
End Function
|
||||
|
||||
Sub CheckZeroArgs(message)
|
||||
' bail if args are missing
|
||||
If args.Count = 0 Then
|
||||
WriteLineErr message
|
||||
WScript.Quit 25121
|
||||
End If
|
||||
End Sub
|
||||
|
||||
Dim ALLOWED_OS_ARCHITECTURE_VALUES: ALLOWED_OS_ARCHITECTURE_VALUES = Array("S", "A", "32", "64")
|
||||
|
||||
'
|
||||
' determine the architecture of the operating system, that will be used. there are 4 possibilities:
|
||||
' A - means agnostic
|
||||
' S - means that we want to use a specific architecture, but auto detect Item
|
||||
' 32 - explicitly use 32 bit architecture
|
||||
' 64 - explicitly use 64 bit architecture
|
||||
'
|
||||
Sub DetermineOSArchitecture()
|
||||
strArchitecture = args(0)
|
||||
|
||||
If IsNull(strArchitecture) Then
|
||||
WriteLineErr "missing architecture argument"
|
||||
WScript.Quit 25124
|
||||
End If
|
||||
|
||||
strArchitecture = UCase(strArchitecture)
|
||||
|
||||
If IndexOf(strArchitecture, ALLOWED_OS_ARCHITECTURE_VALUES) = -1 Then
|
||||
WriteLineErr "invalid architecture argument"
|
||||
WScript.Quit 25124
|
||||
End If
|
||||
|
||||
If (strArchitecture = "S") Then
|
||||
OSArchitecture = GetOSArchitecture()
|
||||
If OSArchitecture = -1 Then
|
||||
WriteLineErr "invalid os architecture detected " & OSArchitecture
|
||||
WScript.Quit 25126
|
||||
End If
|
||||
Else
|
||||
OSArchitecture = strArchitecture
|
||||
End If
|
||||
|
||||
End Sub
|
||||
|
||||
Sub Include(sPath)
|
||||
' TODO this is fragile, but should work for "modules" nested relatively to script root
|
||||
include_ScriptPath = Left(WScript.ScriptFullName, InStr(WScript.ScriptFullName, WScript.ScriptName) - 2)
|
||||
sPath = include_ScriptPath & "\" & sPath
|
||||
|
||||
include_code = fs.OpenTextFile(sPath).ReadAll
|
||||
ExecuteGlobal include_code
|
||||
End Sub
|
||||
|
||||
Function GetOSArchitecture()
|
||||
|
||||
Dim ObjWMI, ColSettings, ObjProcessor
|
||||
Dim StrComputer, ObjNetwork
|
||||
|
||||
Set ObjWMI = GetObject("winmgmts:\Root\CIMV2")
|
||||
Set ColSettings = ObjWMI.ExecQuery ("SELECT DataWidth, AddressWidth, Architecture FROM Win32_Processor")
|
||||
|
||||
' TODO: I make two assumptions here:
|
||||
' 1. Eveyone will have CPU0 device
|
||||
' 2. There is only one cpu defined in the wmi database (and if not, then they are all of the same architecture)
|
||||
Set ObjProcessor = ColSettings.Item("Win32_Processor.DeviceID=""CPU0""")
|
||||
|
||||
If ObjProcessor.Architecture = 0 AND ObjProcessor.AddressWidth = 32 Then
|
||||
GetOSArchitecture = 32
|
||||
ElseIf (ObjProcessor.Architecture = 6 OR ObjProcessor.Architecture = 9) AND ObjProcessor.DataWidth = 64 AND ObjProcessor.AddressWidth = 32 Then
|
||||
GetOSArchitecture = 32
|
||||
ElseIf (ObjProcessor.Architecture = 6 OR ObjProcessor.Architecture = 9) AND ObjProcessor.DataWidth = 64 AND ObjProcessor.AddressWidth = 64 Then
|
||||
GetOSArchitecture = 64
|
||||
Else
|
||||
GetOSArchitecture = -1
|
||||
End If
|
||||
|
||||
End Function
|
||||
|
||||
Function JsonSafe(outStrText)
|
||||
If outStrText = "" Then
|
||||
JsonSafe = ""
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
outStrText = Replace(outStrText, "\", "\\")
|
||||
outStrText = Replace(outStrText, vbcrlf, "\\r\\n")
|
||||
outStrText = Replace(outStrText, vblf, "\\n")
|
||||
outStrText = Replace(outStrText, vbcr, "\\r")
|
||||
outStrText = Replace(outStrText, """", "\""")
|
||||
outStrText = JsonU(outStrText)
|
||||
JsonSafe = outStrText
|
||||
End Function
|
||||
|
||||
'TODO: need to change this function's name to something more appropriate
|
||||
Function JsonU(astr)
|
||||
|
||||
If isNull(astr) Then
|
||||
JsonU = ""
|
||||
Exit Function
|
||||
End If
|
||||
|
||||
Dim c
|
||||
Dim utftext: utftext = ""
|
||||
|
||||
For n = 1 To Len(astr)
|
||||
c = CLng(AscW(Mid(astr, n, 1)))
|
||||
|
||||
If c < 0 Then
|
||||
c = &H10000 + c
|
||||
End If
|
||||
|
||||
If c < &H80 Then
|
||||
utftext = utftext & Mid(astr, n, 1)
|
||||
ElseIf c < &H100 Then
|
||||
utftext = utftext & "\u00" & Hex(c)
|
||||
ElseIf c < &H1000 Then
|
||||
utftext = utftext & "\u0" & Hex(c)
|
||||
Else
|
||||
utftext = utftext & "\u" & Hex(c)
|
||||
End If
|
||||
Next
|
||||
|
||||
JsonU = utftext
|
||||
End Function
|
||||
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"name": "m-fe-rte",
|
||||
"version": "0.0.1",
|
||||
"description": "Micro-Frontend Boilerplate, with React & TS & Webpack",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/wx-chevalier/m-fe-rte"
|
||||
},
|
||||
"author": "wx-chevalier@github",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"keywords": [
|
||||
"react",
|
||||
"redux",
|
||||
"mobx",
|
||||
"webpack",
|
||||
"typescript"
|
||||
],
|
||||
"scripts": {
|
||||
"bootstrap": "yarn install && yarn run build",
|
||||
"build": "npm run clean && yarn workspaces run build && mkdir -p ./build/assets && cp -r ./packages/rte-host-app/build/ ./build/assets/ && cp -r ./packages/rte-node/build/ ./build/ && cp -r ./extra ./build",
|
||||
"build:exe": "(cd ./build && yarn add electron -D && electron-builder install-app-deps) && npm run build:mac && npm run build:win",
|
||||
"build:win": "(cd ./build && electron-builder --win --x64 --config ../electron-builder.yml)",
|
||||
"build:mac": "(cd ./build && electron-builder --mac --x64 --config ../electron-builder.yml)",
|
||||
"clean": "rimraf build && yarn workspaces run clean",
|
||||
"clean:cov": "yarn workspaces run clean:cov",
|
||||
"lint": "./scripts/tools/lint_pkgs.sh",
|
||||
"lint-staged": "lint-staged",
|
||||
"postinstall": "node ./node_modules/husky/lib/installer/bin install",
|
||||
"prettier-all": "prettier --write 'packages/**/src/**/*.{ts,tsx}' '!src/{assets,datas}/**'",
|
||||
"dev": "run-p dev:*",
|
||||
"dev:render": "(cd packages/rte-host-app && npm start)",
|
||||
"dev:electron": "(cd packages/rte-node && npm start)",
|
||||
"start": "(cd packages/rte-node && npm run start:main)",
|
||||
"test": "yarn workspaces run test",
|
||||
"test:cov": "yarn workspaces run test:cov",
|
||||
"test:watch": "yarn workspaces run test:watch",
|
||||
"upgrade": "./scripts/tools/upgrade_pkgs.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@m-fe/app-config": "^0.4.4",
|
||||
"@svgr/webpack": "^5.1.0",
|
||||
"webpack": "4.41.2"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @m-fe/browserslist-config/modern"
|
||||
],
|
||||
"commitlint": {
|
||||
"extends": [
|
||||
"@m-fe"
|
||||
]
|
||||
},
|
||||
"prettier": "@m-fe/prettier-config/semi",
|
||||
"remarkConfig": {
|
||||
"plugins": [
|
||||
"@m-fe/remark-config"
|
||||
]
|
||||
},
|
||||
"stylelint": {
|
||||
"extends": [
|
||||
"@m-fe/stylelint-config",
|
||||
"@m-fe/stylelint-config/modules"
|
||||
],
|
||||
"rules": {
|
||||
"font-family-no-missing-generic-family-keyword": null,
|
||||
"no-descending-specificity": null,
|
||||
"plugin/no-unsupported-browser-features": null,
|
||||
"plugin/no-low-performance-animation-properties": null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
{
|
||||
"name": "rte-core",
|
||||
"version": "0.0.1",
|
||||
"description": "rte-core",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/wx-chevalier/fe-boilerplates"
|
||||
},
|
||||
"author": "wx-chevalier@github",
|
||||
"license": "MIT",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/es/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"keywords": [
|
||||
"webpack",
|
||||
"react"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build:es && npm run build:cjs",
|
||||
"build:cjs": "tsc --project ./tsconfig.cjs.json",
|
||||
"build:es": "tsc --project ./tsconfig.es.json",
|
||||
"clean": "rimraf dist",
|
||||
"clean:r": "rimraf ./dist/*.map && rimraf ./dist/**/*.map && rimraf ./dist/**/*.tsbuildinfo",
|
||||
"dev": "tsc -w --project ./tsconfig.cjs.json",
|
||||
"lint": "run-p lint:*",
|
||||
"lint:es": "cross-env PARSER_NO_WATCH=true eslint . --cache --ext js,md,ts,tsx -f friendly --max-warnings 10",
|
||||
"lint:ts": "tslint -p . -t stylish",
|
||||
"lint:tsc": "tsc -p tsconfig.json --incremental false --noEmit",
|
||||
"test": "jest --config ./scripts/jest/jest.config.js",
|
||||
"test:cov": "npm run cleanCov && npm test -- --coverage",
|
||||
"test:watch": "npm test -- --watch",
|
||||
"prepublish": "npm run clean:r"
|
||||
},
|
||||
"dependencies": {
|
||||
"@m-fe/utils": "^0.2.6",
|
||||
"ufc-schema": "^0.0.44"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@m-fe/app-config": "^0.4.4",
|
||||
"cross-env": "^6.0.3",
|
||||
"webpack": "^4.41.2"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @m-fe/browserslist-config"
|
||||
],
|
||||
"commitlint": {
|
||||
"extends": [
|
||||
"@m-fe"
|
||||
]
|
||||
},
|
||||
"prettier": "@m-fe/prettier-config/semi",
|
||||
"remarkConfig": {
|
||||
"plugins": [
|
||||
"@m-fe/remark-config"
|
||||
]
|
||||
},
|
||||
"stylelint": {
|
||||
"extends": [
|
||||
"@m-fe/stylelint-config",
|
||||
"@m-fe/stylelint-config/modules"
|
||||
],
|
||||
"rules": {
|
||||
"font-family-no-missing-generic-family-keyword": null,
|
||||
"no-descending-specificity": null,
|
||||
"plugin/no-unsupported-browser-features": null,
|
||||
"plugin/no-low-performance-animation-properties": null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { BaseEntity } from '@m-fe/utils';
|
||||
|
||||
export class Event<T = string> extends BaseEntity {
|
||||
type: string;
|
||||
message: T;
|
||||
}
|
||||
|
||||
export class FileEvent extends Event {
|
||||
type: 'add' | 'change';
|
||||
path: string;
|
||||
}
|
||||
|
||||
export class AppEvent extends Event {
|
||||
type:
|
||||
| 'running'
|
||||
// 僵尸进程
|
||||
| 'zombie';
|
||||
path: string;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { App } from '../models';
|
||||
|
||||
import { Event } from './Event';
|
||||
|
||||
export type IPC_EVENT_TYPE =
|
||||
// 全局配置相关
|
||||
| 'get-local-config'
|
||||
| 'set-local-config'
|
||||
// App 相关
|
||||
| 'search-app-location'
|
||||
| 'run-app'
|
||||
// 设备相关
|
||||
| 'select-dirs';
|
||||
|
||||
export class IpcEvent<T = string> extends Event<T> {
|
||||
type: IPC_EVENT_TYPE;
|
||||
|
||||
app: App;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './Event';
|
||||
export * from './IpcEvent';
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './models';
|
||||
export * from './events';
|
||||
@@ -1,48 +0,0 @@
|
||||
import * as S from '@m-fe/utils';
|
||||
|
||||
export type APP_NAME_TYPE = 'UFCON' | 'RSCON_SIMULATE';
|
||||
|
||||
/** 应用定义 */
|
||||
export class App extends S.BaseEntity<App> {
|
||||
// 是否隐藏
|
||||
hidden: boolean;
|
||||
|
||||
// 全名
|
||||
name: APP_NAME_TYPE;
|
||||
|
||||
// 默认快捷方式名
|
||||
lnk: string;
|
||||
|
||||
// id
|
||||
key: string;
|
||||
|
||||
// 版本
|
||||
version: string;
|
||||
|
||||
// 简称
|
||||
label: string;
|
||||
|
||||
// 下载链接
|
||||
url: string;
|
||||
|
||||
// 图标
|
||||
icon: string;
|
||||
|
||||
// exe 文件名称
|
||||
exe: string;
|
||||
// 是否为安装包
|
||||
isInstaller: boolean;
|
||||
|
||||
// 安装包 md5,用于验证版本
|
||||
md5: string;
|
||||
|
||||
order: number;
|
||||
|
||||
// 本机路径
|
||||
location = '';
|
||||
regeditLocation: string;
|
||||
|
||||
get isInstalled() {
|
||||
return !!this.location;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { BaseEntity } from '@m-fe/utils';
|
||||
|
||||
import { App } from './App';
|
||||
|
||||
export class AppSeries extends BaseEntity {
|
||||
name: string;
|
||||
|
||||
key: string;
|
||||
|
||||
apps: App[];
|
||||
|
||||
order: number;
|
||||
|
||||
description: string;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import * as U from '@m-fe/utils';
|
||||
import * as S from 'ufc-schema';
|
||||
|
||||
import { App } from './App';
|
||||
|
||||
export class LocalConfig extends U.BaseEntity<LocalConfig> {
|
||||
// 应用本身的配置
|
||||
clientApp: App;
|
||||
|
||||
// 关联的打印设备
|
||||
printer: S.D3Printer;
|
||||
|
||||
// 本地的一些其他配置
|
||||
// 本地控制软件的根目录
|
||||
ctrlAgentBaseDir: string;
|
||||
|
||||
get hasPrinterConfigured() {
|
||||
// 判断是否设置过控制软件的版本和地址
|
||||
return !!this.ctrlAgentBaseDir && !!this.printer.code;
|
||||
}
|
||||
|
||||
constructor(data: Partial<LocalConfig> = {}) {
|
||||
super(data);
|
||||
|
||||
if (this.clientApp) {
|
||||
this.clientApp = new App(this.clientApp);
|
||||
}
|
||||
|
||||
if (this.printer) {
|
||||
this.printer = new S.D3Printer(this.printer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './App';
|
||||
export * from './AppSeries';
|
||||
export * from './LocalConfig';
|
||||
@@ -1,12 +0,0 @@
|
||||
!/.*.js
|
||||
*.min.*
|
||||
*.production.*
|
||||
*.md
|
||||
*.js
|
||||
*.json
|
||||
|
||||
coverage
|
||||
dist
|
||||
node_modules
|
||||
build
|
||||
scripts
|
||||
@@ -1,17 +0,0 @@
|
||||
const path = require('path');
|
||||
|
||||
const baseConfig = require('../../.eslintrc.js');
|
||||
|
||||
module.exports = {
|
||||
...baseConfig,
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
webpack: {
|
||||
config: path.resolve(
|
||||
__dirname,
|
||||
'./scripts/webpack/webpack.config.resolve.js',
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
{ "language": "typescript", "autoFix": true },
|
||||
{ "language": "typescriptreact", "autoFix": true }
|
||||
]
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@m-fe',
|
||||
{
|
||||
import: true,
|
||||
react: true,
|
||||
typescript: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
@@ -1,112 +0,0 @@
|
||||
{
|
||||
"name": "rte-host-app",
|
||||
"version": "0.0.1",
|
||||
"description": "rte-host-app",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/wx-chevalier/fe-boilerplates"
|
||||
},
|
||||
"author": "wx-chevalier@github",
|
||||
"license": "MIT",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/es/index.js",
|
||||
"types": "dist/types/index.d.ts",
|
||||
"keywords": [
|
||||
"webpack",
|
||||
"react"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production webpack --config ./scripts/webpack/webpack.config.prod.js -p",
|
||||
"clean": "rimraf dist/* && rimraf build/*",
|
||||
"cleanCov": "rimraf @coverage",
|
||||
"dev": "webpack-dev-server --config ./scripts/webpack/webpack.config.dev.js --watch --progress",
|
||||
"lint": "run-p lint:*",
|
||||
"lint:es": "cross-env PARSER_NO_WATCH=true eslint . --cache --ext js,md,ts,tsx -f friendly --max-warnings 10",
|
||||
"lint:style": "stylelint **/*.less --cache",
|
||||
"lint:ts": "tslint -p . -t stylish",
|
||||
"lint:tsc": "tsc -p tsconfig.json --incremental false --noEmit",
|
||||
"prebuild": "npm run clean && npm run cleanCov",
|
||||
"start": "npm run dev",
|
||||
"test": "jest --config ../../scripts/jest/jest.config.js",
|
||||
"test:cov": "npm run cleanCov && npm test -- --coverage",
|
||||
"test:update": "npm test -- --updateSnapshot",
|
||||
"test:watch": "npm test -- --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.0.3",
|
||||
"@ant-design/pro-layout": "^5.0.7",
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@m-fe/utils": "^0.2.6",
|
||||
"antd": "^4.0.3",
|
||||
"axios": "^0.19.2",
|
||||
"connected-react-router": "^6.8.0",
|
||||
"dayjs": "^1.8.23",
|
||||
"dayjs-ext": "^2.2.0",
|
||||
"electron-better-ipc": "^0.8.0",
|
||||
"immer": "^6.0.3",
|
||||
"interactjs": "^1.9.7",
|
||||
"jwt-decode": "^2.2.0",
|
||||
"lodash": "^4.17.15",
|
||||
"mobile-detect": "^1.4.4",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-fxxking-hooks": "^1.0.2",
|
||||
"react-helmet": "^5.2.1",
|
||||
"react-inlinesvg": "^1.2.0",
|
||||
"react-intl": "^4.2.2",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-router": "^5.1.2",
|
||||
"redux-actions": "^2.6.5",
|
||||
"redux-pack": "^0.1.5",
|
||||
"redux-pack-fsa": "^0.0.4",
|
||||
"redux-promise-middleware": "^6.1.2",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"rte-core": "*",
|
||||
"smoothscroll-polyfill": "^0.4.4",
|
||||
"ufc-schema": "^0.0.44",
|
||||
"umi-request": "^1.2.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@m-fe/app-config": "^0.4.4",
|
||||
"@types/jwt-decode": "^2.2.1",
|
||||
"@types/react-helmet": "^5.0.15",
|
||||
"babel-loader": "^8.1.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"react-hot-loader": "^4.12.20",
|
||||
"rimraf": "^3.0.2",
|
||||
"style-loader": "^1.1.3",
|
||||
"stylelint": "^13.2.1",
|
||||
"tslint": "^6.1.0",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.42.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @m-fe/browserslist-config/modern"
|
||||
],
|
||||
"commitlint": {
|
||||
"extends": [
|
||||
"@m-fe"
|
||||
]
|
||||
},
|
||||
"prettier": "@m-fe/prettier-config/semi",
|
||||
"remarkConfig": {
|
||||
"plugins": [
|
||||
"@m-fe/remark-config"
|
||||
]
|
||||
},
|
||||
"stylelint": {
|
||||
"extends": [
|
||||
"@m-fe/stylelint-config",
|
||||
"@m-fe/stylelint-config/modules"
|
||||
],
|
||||
"rules": {
|
||||
"font-family-no-missing-generic-family-keyword": null,
|
||||
"no-descending-specificity": null,
|
||||
"plugin/no-unsupported-browser-features": null,
|
||||
"plugin/no-low-performance-animation-properties": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,18 +0,0 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||
<title>RTW</title>
|
||||
</head>
|
||||
<body>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<script>
|
||||
window.__DEV_APP__ = {};
|
||||
</script>
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"short_name": "RTW",
|
||||
"name": "Micro Frontend Boilerplate",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/* globals workbox */
|
||||
workbox.core.setCacheNameDetails({
|
||||
prefix: 'antd-pro',
|
||||
suffix: 'v1'
|
||||
});
|
||||
// Control all opened tabs ASAP
|
||||
workbox.clientsClaim();
|
||||
|
||||
/**
|
||||
* Use precaching list generated by workbox in build process.
|
||||
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
|
||||
*/
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
|
||||
|
||||
/**
|
||||
* Register a navigation route.
|
||||
* https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
|
||||
*/
|
||||
workbox.routing.registerNavigationRoute('/index.html');
|
||||
|
||||
/**
|
||||
* Use runtime cache:
|
||||
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
|
||||
*
|
||||
* Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
|
||||
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle API requests
|
||||
*/
|
||||
workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst());
|
||||
|
||||
/**
|
||||
* Handle third party requests
|
||||
*/
|
||||
workbox.routing.registerRoute(
|
||||
/^https:\/\/gw.alipayobjects.com\//,
|
||||
workbox.strategies.networkFirst()
|
||||
);
|
||||
workbox.routing.registerRoute(
|
||||
/^https:\/\/cdnjs.cloudflare.com\//,
|
||||
workbox.strategies.networkFirst()
|
||||
);
|
||||
workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst());
|
||||
|
||||
/**
|
||||
* Response to client after skipping waiting with MessageChannel
|
||||
*/
|
||||
addEventListener('message', event => {
|
||||
const replyPort = event.ports[0];
|
||||
const message = event.data;
|
||||
if (replyPort && message && message.type === 'skip-waiting') {
|
||||
event.waitUntil(
|
||||
self
|
||||
.skipWaiting()
|
||||
.then(
|
||||
() => replyPort.postMessage({ error: null }),
|
||||
error => replyPort.postMessage({ error })
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
const path = require('path');
|
||||
const merge = require('webpack-merge');
|
||||
|
||||
const { devConfig } = require('../../../../scripts/webpack/webpack.config');
|
||||
|
||||
const themeConfig = require('./webpack.config.theme');
|
||||
|
||||
const config = merge(devConfig, themeConfig, {
|
||||
entry: {
|
||||
index: [
|
||||
'react-hot-loader/patch',
|
||||
path.resolve(__dirname, '../../src/index'),
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.resolve(__dirname, '../../public'),
|
||||
hot: false,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
@@ -1,17 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const themeConfig = require('./webpack.config.theme');
|
||||
|
||||
const prodConfig = require('../../../../scripts/webpack/webpack.config')
|
||||
.prodConfig;
|
||||
|
||||
const config = merge(themeConfig, prodConfig, {
|
||||
entry: {
|
||||
index: path.resolve(__dirname, '../../src/index'),
|
||||
},
|
||||
output: {
|
||||
publicPath: 'a/b',
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
@@ -1,9 +0,0 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, '../..', 'src/'),
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
const ThemeColorReplacer = require('webpack-theme-color-replacer');
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new ThemeColorReplacer({
|
||||
fileName: 'theme-colors-[contenthash:8].css',
|
||||
matchColors: [...getAntdSerials('#1890ff'), ...getAntdSerials('#5d4bff')], // 主色系列
|
||||
// 改变样式选择器,解决样式覆盖问题
|
||||
changeSelector(selector) {
|
||||
switch (selector) {
|
||||
case '.ant-calendar-today .ant-calendar-date':
|
||||
return ':not(.ant-calendar-selected-date)' + selector;
|
||||
case '.ant-btn:focus,.ant-btn:hover':
|
||||
return '.ant-btn:focus:not(.ant-btn-primary),.ant-btn:hover:not(.ant-btn-primary)';
|
||||
case '.ant-btn.active,.ant-btn:active':
|
||||
return '.ant-btn.active:not(.ant-btn-primary),.ant-btn:active:not(.ant-btn-primary)';
|
||||
default:
|
||||
return selector;
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
/** 获取系列颜色 */
|
||||
function getAntdSerials(color) {
|
||||
var lightens = new Array(9).fill().map((t, i) => {
|
||||
return ThemeColorReplacer.varyColor.lighten(color, i / 10);
|
||||
});
|
||||
// 此处为了简化,采用了darken。实际按color.less需求可以引入tinycolor, colorPalette变换得到颜色值
|
||||
var darkens = new Array(6).fill().map((t, i) => {
|
||||
return ThemeColorReplacer.varyColor.darken(color, i / 10);
|
||||
});
|
||||
return lightens.concat(darkens);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
const path = require('path');
|
||||
const merge = require('webpack-merge');
|
||||
|
||||
const themeConfig = require('./webpack.config.theme');
|
||||
const { umdConfig } = require('../../../../scripts/webpack/webpack.config');
|
||||
|
||||
module.exports = merge(umdConfig, themeConfig, {
|
||||
entry: {
|
||||
index: path.resolve(__dirname, '../../src/index.umd'),
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.resolve(__dirname, '../../public'),
|
||||
port: 8081,
|
||||
},
|
||||
output: {
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
const merge = require('webpack-merge');
|
||||
|
||||
const themeConfig = require('./webpack.config.theme');
|
||||
const umdConfig = require('../../../../scripts/webpack/webpack.config')
|
||||
.umdConfig;
|
||||
|
||||
module.exports = merge(umdConfig, themeConfig, {
|
||||
entry: {
|
||||
index: path.resolve(__dirname, '../../src/index.umd'),
|
||||
},
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
export * from './login';
|
||||
@@ -1,316 +0,0 @@
|
||||
/** 用户鉴权相关操作 */
|
||||
import { message, notification } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import * as S from 'ufc-schema';
|
||||
|
||||
import { i18nFormat } from '@/i18n';
|
||||
import {
|
||||
HOST,
|
||||
getToken,
|
||||
history,
|
||||
isTokenExpired,
|
||||
setAuthority,
|
||||
setToken,
|
||||
umiRequest,
|
||||
} from '@/skeleton';
|
||||
|
||||
const getProfile = async () => {
|
||||
return new S.User();
|
||||
};
|
||||
|
||||
/**
|
||||
* @start Person Token 登陆接口
|
||||
*/
|
||||
|
||||
/** 通过用户名密码登陆 */
|
||||
export async function loginByUsername(username?: string, password?: string) {
|
||||
const {
|
||||
data: { token },
|
||||
} = await umiRequest.post<{ data: any }>(`${HOST}/noauth/login/person`, {
|
||||
data: { username, credential: password, type: 'PASSWORD' },
|
||||
errorHandler: () => {
|
||||
notification.error({
|
||||
message: `${i18nFormat('登录失败')}`,
|
||||
description: `${i18nFormat('用户名或密码错误')}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/** 使用手机号登录 */
|
||||
export async function loginByPhone(phone: string, verifyCode: string) {
|
||||
const {
|
||||
data: { token },
|
||||
} = await umiRequest.post<{ data: any }>(`${HOST}/noauth/login/person`, {
|
||||
data: {
|
||||
username: phone,
|
||||
credential: verifyCode,
|
||||
type: 'VERIFICATION_CODE',
|
||||
},
|
||||
errorHandler: () => {
|
||||
notification.error({
|
||||
message: `${i18nFormat('登录失败')}`,
|
||||
description: `${i18nFormat('用户名或密码错误')}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @end Person Token 登陆接口
|
||||
*/
|
||||
export async function getPersonInfoAndLoginAsUser(personToken: string) {
|
||||
const { data: personInfo } = await umiRequest.get<{ data: S.Person }>(
|
||||
`${HOST}/noauth/person`,
|
||||
{
|
||||
params: {
|
||||
token: `Bearer ${personToken}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!S.isValidArray(personInfo.userIds)) {
|
||||
message.error('您还没有加入到任何租户,请联系管理员添加!');
|
||||
throw new Error('');
|
||||
}
|
||||
|
||||
// 执行登陆操作,默认登录到首个租户
|
||||
const {
|
||||
data: { token },
|
||||
} = await umiRequest.post<{ data: { token: string } }>(
|
||||
`${HOST}/noauth/login/user`,
|
||||
{
|
||||
data: {
|
||||
personId: personInfo.id,
|
||||
personToken: `Bearer ${personToken}`,
|
||||
userId: personInfo.userIds[0],
|
||||
},
|
||||
errorHandler: () => {
|
||||
notification.error({
|
||||
message: `${i18nFormat('登录失败')}`,
|
||||
description: `${i18nFormat('您没有租户权限!')}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!token) {
|
||||
message.error('登陆租户失败,您没有租户权限!');
|
||||
throw new Error('');
|
||||
}
|
||||
|
||||
await postLogin(token);
|
||||
}
|
||||
|
||||
/** 使用 PersonInfo 登录 */
|
||||
export async function loginAsUserWithPersonInfo(
|
||||
personInfo: S.Person,
|
||||
personToken: string,
|
||||
userId: string,
|
||||
) {
|
||||
// 执行登陆操作,默认登录到首个租户
|
||||
const {
|
||||
data: { token },
|
||||
} = await umiRequest.post<{ data: { token: string } }>(
|
||||
`${HOST}/noauth/login/user`,
|
||||
{
|
||||
data: {
|
||||
personId: personInfo.id,
|
||||
personToken: `Bearer ${personToken}`,
|
||||
userId,
|
||||
},
|
||||
errorHandler: () => {
|
||||
notification.error({
|
||||
message: `${i18nFormat('登录失败')}`,
|
||||
description: `${i18nFormat('您没有租户权限!')}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!token) {
|
||||
message.error('登陆租户失败,您没有租户权限!');
|
||||
throw new Error('');
|
||||
}
|
||||
|
||||
await postLogin(token);
|
||||
}
|
||||
|
||||
export async function changeTenantUserWithTenantId(tenantId: S.Id) {
|
||||
// 获取关联的 userId
|
||||
const users = getGlobalUser().person.users;
|
||||
const userId = users.find((u: S.User) => u.tenantId === tenantId).id;
|
||||
|
||||
await changeTenantUser(userId);
|
||||
}
|
||||
|
||||
/** 当前用户更改关联的租户 */
|
||||
export async function changeTenantUser(userId: S.Id) {
|
||||
// 首先获取 Token
|
||||
const token = getToken();
|
||||
|
||||
const {
|
||||
data: { token: newToken },
|
||||
} =
|
||||
(await umiRequest.patch<{ data: { token: string } }>(`${HOST}/user/login`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
params: { userId },
|
||||
errorHandler: () => {
|
||||
message.error(`${i18nFormat('切换失败,请稍候重试')}`);
|
||||
|
||||
// 清空本地的 Token
|
||||
setToken(null);
|
||||
setAuthority(null);
|
||||
},
|
||||
})) || ({ data: {} } as any);
|
||||
|
||||
if (newToken) {
|
||||
// 设置获取到的新 token
|
||||
await postLogin(newToken);
|
||||
|
||||
message.success(`已经将身份切换到:${getGlobalUser().tenant.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @start User Token 登陆接口
|
||||
*/
|
||||
export async function loginByUserToken() {
|
||||
// 首先获取 Token
|
||||
const token = getToken();
|
||||
|
||||
// 判断 token 是否过期,
|
||||
if (!isTokenExpired(token, 24 * 3600)) {
|
||||
await postLogin(token);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (token) {
|
||||
const {
|
||||
data: { token: newToken },
|
||||
} =
|
||||
(await umiRequest.patch<{ data: { token: string } }>(
|
||||
`${HOST}/user/login`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
errorHandler: () => {
|
||||
message.error(`${i18nFormat('登录失效,跳转重新登录')}`);
|
||||
|
||||
// 清空本地的 Token
|
||||
setToken(null);
|
||||
setAuthority(null);
|
||||
},
|
||||
},
|
||||
)) || ({ data: {} } as any);
|
||||
|
||||
if (newToken) {
|
||||
// 设置获取到的新 token
|
||||
await postLogin(newToken);
|
||||
}
|
||||
} else {
|
||||
await postLogin(null);
|
||||
}
|
||||
}
|
||||
|
||||
/** 统一的登陆后处理 */
|
||||
async function postLogin(token: string | null) {
|
||||
if (token) {
|
||||
setToken(token);
|
||||
|
||||
// 这里获取用户信息,并且设置权限
|
||||
const profile = await getProfile();
|
||||
|
||||
setGlobalUser(profile);
|
||||
|
||||
if (!profile) {
|
||||
setToken(null);
|
||||
setAuthority(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取到用户的权限信息
|
||||
const permissionNames = (profile.permissions || []).map((p: any) =>
|
||||
p.name.trim(),
|
||||
);
|
||||
|
||||
// 注册权限信息
|
||||
const authority = [profile.authority, ...permissionNames];
|
||||
|
||||
if (authority.indexOf('SYS_ADMIN') > -1) {
|
||||
authority.push('TENANT_ADMIN');
|
||||
}
|
||||
|
||||
if (authority.indexOf('TENANT_ADMIN') > -1) {
|
||||
authority.push('TENANT_USER');
|
||||
}
|
||||
|
||||
if (authority.indexOf('TENANT_ADMIN') > -1) {
|
||||
authority.push(...['PRODUCT_BOARD', 'MACHINE_BOARD']);
|
||||
}
|
||||
|
||||
setAuthority(authority);
|
||||
} else {
|
||||
setToken(null);
|
||||
setAuthority(null);
|
||||
}
|
||||
}
|
||||
|
||||
export async function setGlobalUser(profile: S.User) {
|
||||
if (profile) {
|
||||
window.gConfig.user = profile;
|
||||
|
||||
if (window.Sentry) {
|
||||
window.Sentry.configureScope((scope: any) => {
|
||||
scope.setUser({ ...profile });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getGlobalUser() {
|
||||
return window.gConfig.user;
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
setToken(null);
|
||||
setAuthority(null);
|
||||
window.gConfig.user = null;
|
||||
history.push('/auth/login');
|
||||
|
||||
// 需要刷新界面,清空 redux
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/** 获取登录验证码 */
|
||||
export async function getLoginCode(sendDst: string) {
|
||||
const { status } = await umiRequest.post<{ status: string }>(
|
||||
`${HOST}/noauth/verification_code`,
|
||||
{
|
||||
data: {
|
||||
channel: 'SMS',
|
||||
sendDst,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return status === 'ok';
|
||||
}
|
||||
|
||||
/** 获取 personInfo */
|
||||
export async function getPersonInfoWithToken(personToken: string) {
|
||||
const { data: personInfo } = await umiRequest.get<{ data: S.Person }>(
|
||||
`${HOST}/noauth/person`,
|
||||
{
|
||||
params: {
|
||||
token: `Bearer ${personToken}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return new S.Person(personInfo);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './user';
|
||||
export * from './ipc';
|
||||
@@ -1,11 +0,0 @@
|
||||
import { ipcRenderer } from 'electron-better-ipc';
|
||||
|
||||
import { IPC_EVENT_TYPE, IpcEvent } from 'rte-core';
|
||||
|
||||
/** 调用远端函数 */
|
||||
export async function callMain<R = string>(
|
||||
type: IPC_EVENT_TYPE,
|
||||
e: IpcEvent = new IpcEvent(),
|
||||
) {
|
||||
return ipcRenderer.callMain<IpcEvent, R>(type, e);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import * as S from 'ufc-schema';
|
||||
|
||||
import { IpcEvent, LocalConfig } from 'rte-core';
|
||||
|
||||
import { callMain } from './base';
|
||||
|
||||
/** 获取本地配置 */
|
||||
export async function getLocalConfig() {
|
||||
const resp = await callMain<string>('get-local-config');
|
||||
|
||||
return new LocalConfig(S.parseJson(resp));
|
||||
}
|
||||
|
||||
/** 更新本地配置 */
|
||||
export async function updateLocalConfig(localConfig: LocalConfig) {
|
||||
const status = await callMain(
|
||||
'set-local-config',
|
||||
new IpcEvent({
|
||||
// 对于 LocalConfig 类,比较大,因此都是序列化之后传递
|
||||
message: JSON.stringify(localConfig),
|
||||
}),
|
||||
);
|
||||
|
||||
return status === 'ok';
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './base';
|
||||
export * from './client';
|
||||
export * from './ui';
|
||||
@@ -1,5 +0,0 @@
|
||||
import { callMain } from './base';
|
||||
|
||||
export async function selectDir() {
|
||||
return callMain<string[]>('select-dirs');
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export const getUsers = async (): Promise<string[]> => {
|
||||
return [];
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import cn from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
export interface LoginPageProps {
|
||||
className?: string;
|
||||
style?: Record<string, string | number>;
|
||||
}
|
||||
|
||||
export const LoginPage = ({ className, style }: LoginPageProps) => {
|
||||
return (
|
||||
<div className={cn(className, styles.container)} style={style}>
|
||||
LoginPage
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
@import '~@/skeleton/styles/variables.less';
|
||||
|
||||
.container {
|
||||
.appContainer;
|
||||
|
||||
box-shadow: none;
|
||||
background-image: url('../../../../assets/bg4.png');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.body {
|
||||
background: #fff;
|
||||
border: 1px solid #9b9b9b;
|
||||
border-radius: 10px;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 16px;
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
import { Button, Descriptions, Divider, Input, Tabs } from 'antd';
|
||||
import produce from 'immer';
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
|
||||
import { selectDir, updateLocalConfig } from '@/apis';
|
||||
import { AppState } from '@/ducks';
|
||||
import { commonActions } from '@/ducks/common';
|
||||
import { PageHeader, history } from '@/skeleton';
|
||||
import { LocalConfig } from 'rte-core';
|
||||
|
||||
import * as styles from './index.less';
|
||||
// import * as S from 'ufc-schema';
|
||||
|
||||
export interface DeviceConfigProps extends RouteComponentProps {
|
||||
localConfig: LocalConfig;
|
||||
}
|
||||
|
||||
export interface DeviceConfigState {
|
||||
isDirty: boolean;
|
||||
isEditing: boolean;
|
||||
localConfig: LocalConfig;
|
||||
}
|
||||
|
||||
export class DeviceConfigComp extends React.PureComponent<
|
||||
DeviceConfigProps,
|
||||
DeviceConfigState
|
||||
> {
|
||||
constructor(props: DeviceConfigProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isDirty: false,
|
||||
isEditing: false,
|
||||
localConfig: props.localConfig,
|
||||
};
|
||||
}
|
||||
|
||||
/** 保存到本地配置中 */
|
||||
onUpdatLocalConfig(onDraftModifier: (draft: LocalConfig) => void) {
|
||||
this.setState({
|
||||
isDirty: true,
|
||||
localConfig: produce(this.state.localConfig, onDraftModifier),
|
||||
});
|
||||
}
|
||||
|
||||
onSaveLocalConfig = async () => {
|
||||
await updateLocalConfig(this.state.localConfig);
|
||||
|
||||
// 将本地的 LocalConfig 保存
|
||||
this.setState({ isDirty: false });
|
||||
};
|
||||
|
||||
/** 设备信息的配置 */
|
||||
renderDeviceInfo() {
|
||||
const { localConfig, isEditing } = this.state;
|
||||
|
||||
console.log(localConfig);
|
||||
|
||||
return (
|
||||
<div className={styles.info}>
|
||||
<Descriptions title="铭牌参数" column={1}>
|
||||
<Descriptions.Item label="设备厂商">联泰</Descriptions.Item>
|
||||
<Descriptions.Item label="设备型号">Lite600</Descriptions.Item>
|
||||
<Descriptions.Item label="设备编号">
|
||||
{isEditing ? (
|
||||
<Input
|
||||
value={localConfig.printer.code}
|
||||
onChange={e => {
|
||||
this.onUpdatLocalConfig(draft => {
|
||||
draft.printer.code = e.target.value;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
localConfig.printer.code
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="设备名称">Zhou Maomao</Descriptions.Item>
|
||||
<Descriptions.Item label="控制软件">
|
||||
<span>RSCON</span>
|
||||
<Divider type="vertical" />
|
||||
<span>{localConfig.ctrlAgentBaseDir || '尚未选择软件路径'}</span>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
const paths = await selectDir();
|
||||
|
||||
this.onUpdatLocalConfig(draft => {
|
||||
draft.ctrlAgentBaseDir = paths[0];
|
||||
});
|
||||
}}
|
||||
>
|
||||
配置软件路径
|
||||
</Button>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<Descriptions title="生产参数" column={1}>
|
||||
<Descriptions.Item label="使用材料">-</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isDirty } = this.state;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.body}>
|
||||
<PageHeader
|
||||
title="设备配置"
|
||||
extra={
|
||||
<div style={{ marginRight: 16 }}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.setState({ isEditing: true });
|
||||
}}
|
||||
style={{ marginRight: 16 }}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button disabled={!isDirty} onClick={this.onSaveLocalConfig}>
|
||||
保存
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
onBack={() => {
|
||||
history.push('/device');
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className={styles.tabs}>
|
||||
<Tabs>
|
||||
<Tabs.TabPane tab="设备信息" key="info">
|
||||
{this.renderDeviceInfo()}
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="网关配置" key="gateway" />
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const DeviceConfig = connect(
|
||||
(_state: AppState) => ({
|
||||
localConfig: _state.common.localConfig,
|
||||
}),
|
||||
{
|
||||
loadLocalConfig: commonActions.loadLocalConfig,
|
||||
},
|
||||
)(withRouter(DeviceConfigComp));
|
||||
@@ -1,5 +0,0 @@
|
||||
@import '~@/skeleton/styles/variables.less';
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Route, RouteComponentProps, Switch, withRouter } from 'react-router';
|
||||
|
||||
import { AppState } from '@/ducks';
|
||||
|
||||
import { DeviceConfig } from '../DeviceConfig';
|
||||
import { DeviceOperation } from '../DeviceOperation';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export interface DeviceHomeProps extends RouteComponentProps {}
|
||||
|
||||
export interface DeviceHomeState {}
|
||||
|
||||
export class DeviceHomeComp extends React.PureComponent<
|
||||
DeviceHomeProps,
|
||||
DeviceHomeState
|
||||
> {
|
||||
constructor(props: DeviceHomeProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.body}>
|
||||
<Switch>
|
||||
<Route
|
||||
key="device-config"
|
||||
path="/device/config"
|
||||
exact={true}
|
||||
component={DeviceConfig}
|
||||
/>
|
||||
<Route
|
||||
key="device-operation"
|
||||
path="/device"
|
||||
component={DeviceOperation}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const DeviceHome = connect(
|
||||
(_state: AppState) => ({}),
|
||||
{},
|
||||
)(withRouter(DeviceHomeComp));
|
||||
@@ -1,16 +0,0 @@
|
||||
@import '~@/skeleton/styles/variables.less';
|
||||
|
||||
.container {
|
||||
.appContainer;
|
||||
|
||||
box-shadow: none;
|
||||
background-image: url('../../../../assets/bg4.png');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.body {
|
||||
background: #fff;
|
||||
border: 1px solid #9b9b9b;
|
||||
border-radius: 10px;
|
||||
min-height: 100px;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import { Button, Result } from 'antd';
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import * as S from 'ufc-schema';
|
||||
|
||||
import { AppState } from '@/ducks';
|
||||
import { PageHeader, history } from '@/skeleton';
|
||||
import { LocalConfig } from 'rte-core';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export interface DeviceOperationProps extends RouteComponentProps {
|
||||
localConfig: LocalConfig;
|
||||
}
|
||||
|
||||
export interface DeviceOperationState {}
|
||||
|
||||
export class DeviceOperationComp extends React.PureComponent<
|
||||
DeviceOperationProps,
|
||||
DeviceOperationState
|
||||
> {
|
||||
constructor(props: DeviceOperationProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
console.log(S);
|
||||
}
|
||||
|
||||
/** 渲染空白配置 */
|
||||
renderEmpty() {
|
||||
return (
|
||||
<Result
|
||||
title="该设备尚未配置"
|
||||
extra={
|
||||
<Button
|
||||
type="primary"
|
||||
key="console"
|
||||
onClick={() => {
|
||||
history.push('/device/config');
|
||||
}}
|
||||
>
|
||||
点击前往配置
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/** 渲染实时设备状态 */
|
||||
renderDeviceStatus() {
|
||||
const { localConfig } = this.props;
|
||||
|
||||
return (
|
||||
<div className={styles.status}>
|
||||
<PageHeader
|
||||
title={
|
||||
localConfig.printer.name || localConfig.printer.code || '设备状态'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { localConfig } = this.props;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.body}>
|
||||
{localConfig && localConfig.hasPrinterConfigured
|
||||
? this.renderDeviceStatus()
|
||||
: this.renderEmpty()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const DeviceOperation = connect(
|
||||
(_state: AppState) => ({ localConfig: _state.common.localConfig }),
|
||||
{},
|
||||
)(withRouter(DeviceOperationComp));
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './DeviceConfig';
|
||||
export * from './DeviceHome';
|
||||
export * from './DeviceOperation';
|
||||
@@ -1 +0,0 @@
|
||||
export * from './containers';
|
||||
@@ -1,26 +0,0 @@
|
||||
.productLineContainer {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
padding: 10px 60px 10px 40px;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.productLineName {
|
||||
font-size: 16px;
|
||||
color: #2e72b8;
|
||||
letter-spacing: 0.57px;
|
||||
line-height: 22px;
|
||||
margin: 0 0 15px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.productList {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-column-gap: 68px;
|
||||
grid-row-gap: 12px;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { App, AppSeries } from 'rte-core';
|
||||
|
||||
import { InstallableAppView } from '../InstallableAppView';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export interface InstallableAppSeriesView {
|
||||
name: string;
|
||||
id: string;
|
||||
children: App[];
|
||||
}
|
||||
|
||||
interface InstallableAppSeriesViewProps {
|
||||
data: AppSeries;
|
||||
}
|
||||
|
||||
export const InstallableAppSeriesView = (
|
||||
props: InstallableAppSeriesViewProps,
|
||||
): JSX.Element => {
|
||||
const appSeries = props.data;
|
||||
return (
|
||||
<div className={styles.productLineContainer}>
|
||||
<p className={styles.productLineName}>{appSeries.name}</p>
|
||||
<div className={styles.productList} id={appSeries.key}>
|
||||
{appSeries.apps.map(p => (
|
||||
<InstallableAppView app={p} key={p.key} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,113 +0,0 @@
|
||||
.productContainer {
|
||||
width: 80px;
|
||||
height: 81px;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&:hover {
|
||||
.productButton {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.productIcon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.productName {
|
||||
width: 100%;
|
||||
height: 17px;
|
||||
line-height: 17px;
|
||||
font-size: 12px;
|
||||
color: #4a4a4a;
|
||||
letter-spacing: 0.43px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.productButton {
|
||||
width: 38px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
border: 1px solid #2e72b8;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease;
|
||||
text-align: center;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.buttonRun {
|
||||
color: #2e72b8;
|
||||
background-color: #fff;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background-color: rgba(46, 114, 184, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.buttonDownload {
|
||||
color: #fff;
|
||||
background-color: rgba(46, 114, 184, 0.9);
|
||||
}
|
||||
|
||||
.buttonDisabled {
|
||||
justify-content: center;
|
||||
cursor: not-allowed;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
font-weight: 400;
|
||||
background-color: #f5f5f5;
|
||||
border-color: #d9d9d9;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.buttonDownloading {
|
||||
padding: 0;
|
||||
border: none;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.ant-progress-bg {
|
||||
border-radius: 3px;
|
||||
background-color: rgba(46, 114, 184, 0.9);
|
||||
}
|
||||
|
||||
.ant-progress-inner {
|
||||
border-radius: 3px;
|
||||
background-color: #9b9b9b;
|
||||
}
|
||||
|
||||
.ant-progress-text {
|
||||
margin: 0;
|
||||
width: auto;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ant-progress-outer {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ant-progress {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import { Progress } from 'antd';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { App } from 'rte-core';
|
||||
|
||||
import { AppMgt } from '../../services/AppMgt';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
const icons = {};
|
||||
|
||||
interface InstallableAppViewProps {
|
||||
app: App;
|
||||
}
|
||||
|
||||
interface InstallableAppViewState {
|
||||
appMgt: AppMgt;
|
||||
}
|
||||
|
||||
export class InstallableAppView extends Component<
|
||||
InstallableAppViewProps,
|
||||
InstallableAppViewState
|
||||
> {
|
||||
constructor(props: InstallableAppViewProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
appMgt: new AppMgt(props.app),
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { appMgt } = this.state;
|
||||
|
||||
const { downloading, downloadProgess } = appMgt;
|
||||
|
||||
const { label, icon, isInstalled, hidden, order } = appMgt.app;
|
||||
|
||||
if (hidden) {
|
||||
return null;
|
||||
}
|
||||
let button: JSX.Element;
|
||||
if (downloading) {
|
||||
button = (
|
||||
<div className={`${styles.productButton} ${styles.buttonDownloading}`}>
|
||||
<Progress
|
||||
percent={downloadProgess}
|
||||
showInfo={true}
|
||||
strokeWidth={18}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (isInstalled) {
|
||||
button = (
|
||||
<div
|
||||
className={`${styles.productButton} ${styles.buttonRun}`}
|
||||
onClick={() => appMgt.run()}
|
||||
>
|
||||
<span>打开</span>
|
||||
</div>
|
||||
);
|
||||
} else if (!appMgt.app.url) {
|
||||
button = (
|
||||
<div className={`${styles.productButton} ${styles.buttonDisabled}`}>
|
||||
<span>即将上线</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
button = (
|
||||
<div
|
||||
className={`${styles.productButton} ${styles.buttonDownload}`}
|
||||
onClick={() => appMgt.install()}
|
||||
>
|
||||
<span>安装</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.productContainer} style={{ order: order }}>
|
||||
<div
|
||||
className={styles.productIcon}
|
||||
style={{ backgroundImage: `url(${icons[icon]})` }}
|
||||
onClick={() => isInstalled && appMgt.run()}
|
||||
/>
|
||||
<div className={styles.productName}>{label}</div>
|
||||
{button}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
.menuContainer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 300;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.memuItemContainer {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
color: #fff;
|
||||
margin-bottom: 18px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.menuItemContent {
|
||||
max-width: 90px;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
.anticon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.memuItemContainer.menuItemSelected,
|
||||
.memuItemContainer:hover {
|
||||
background-color: #fff;
|
||||
color: #2e72b8;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import classnames from 'classnames';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
type Key = React.Key | null;
|
||||
|
||||
interface MenuItemProps {
|
||||
isSelected?: boolean;
|
||||
children?: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const MenuItem = (props: MenuItemProps) => {
|
||||
const cl: string = props.isSelected
|
||||
? classnames(styles.memuItemContainer, styles.menuItemSelected)
|
||||
: styles.memuItemContainer;
|
||||
return (
|
||||
<div className={cl} onClick={props.onClick}>
|
||||
<div className={styles.menuItemContent}>{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface MenuProps {
|
||||
defaultKey?: string;
|
||||
selectedKey?: string;
|
||||
onSelect?: (key: Key) => void;
|
||||
}
|
||||
|
||||
interface MenuState {
|
||||
selectedKey: Key;
|
||||
}
|
||||
|
||||
export class Menu extends Component<MenuProps, MenuState> {
|
||||
static Item = MenuItem;
|
||||
|
||||
constructor(props: MenuProps) {
|
||||
super(props);
|
||||
let selectedKey: Key = null;
|
||||
if ('defaultKey' in props) {
|
||||
selectedKey = props.defaultKey!;
|
||||
}
|
||||
if ('selectedKey' in props) {
|
||||
selectedKey = props.selectedKey!;
|
||||
}
|
||||
this.state = {
|
||||
selectedKey: selectedKey,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps: MenuProps) {
|
||||
if ('selectedKey' in newProps) {
|
||||
this.setState({
|
||||
selectedKey: newProps.selectedKey!,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleItemClick = (key: Key) => {
|
||||
if (!('selectedKey' in this.props)) {
|
||||
this.setState({
|
||||
selectedKey: key,
|
||||
});
|
||||
}
|
||||
if (typeof this.props.onSelect === 'function') {
|
||||
this.props.onSelect(key);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.menuContainer}>
|
||||
{React.Children.map(
|
||||
this.props.children,
|
||||
(item: React.ReactElement<MenuItemProps>) => {
|
||||
return React.cloneElement(item, {
|
||||
isSelected: this.state.selectedKey === item.key,
|
||||
onClick: () => this.handleItemClick(item.key),
|
||||
} as MenuItemProps);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.toolbarContainer {
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
z-index: 100;
|
||||
color: #000;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export const Toolbar = (props: any) => {
|
||||
return <div className={styles.toolbarContainer}>{props.children}</div>;
|
||||
};
|
||||
@@ -1,275 +0,0 @@
|
||||
@keyframes gridTemplateColumns_60_130 {
|
||||
0% {
|
||||
display: grid;
|
||||
grid-template-columns: 60px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
10% {
|
||||
display: grid;
|
||||
grid-template-columns: 67px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
20% {
|
||||
display: grid;
|
||||
grid-template-columns: 74px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
30% {
|
||||
display: grid;
|
||||
grid-template-columns: 81px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
40% {
|
||||
display: grid;
|
||||
grid-template-columns: 88px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
50% {
|
||||
display: grid;
|
||||
grid-template-columns: 95px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
60% {
|
||||
display: grid;
|
||||
grid-template-columns: 102px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
70% {
|
||||
display: grid;
|
||||
grid-template-columns: 109px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
80% {
|
||||
display: grid;
|
||||
grid-template-columns: 116px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
90% {
|
||||
display: grid;
|
||||
grid-template-columns: 123px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
100% {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes gridTemplateColumns_60_130 {
|
||||
0% {
|
||||
display: grid;
|
||||
grid-template-columns: 60px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
10% {
|
||||
display: grid;
|
||||
grid-template-columns: 67px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
20% {
|
||||
display: grid;
|
||||
grid-template-columns: 74px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
30% {
|
||||
display: grid;
|
||||
grid-template-columns: 81px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
40% {
|
||||
display: grid;
|
||||
grid-template-columns: 88px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
50% {
|
||||
display: grid;
|
||||
grid-template-columns: 95px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
60% {
|
||||
display: grid;
|
||||
grid-template-columns: 102px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
70% {
|
||||
display: grid;
|
||||
grid-template-columns: 109px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
80% {
|
||||
display: grid;
|
||||
grid-template-columns: 116px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
90% {
|
||||
display: grid;
|
||||
grid-template-columns: 123px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
100% {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes gridTemplateColumns_130_60 {
|
||||
0% {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
10% {
|
||||
display: grid;
|
||||
grid-template-columns: 123px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
20% {
|
||||
display: grid;
|
||||
grid-template-columns: 116px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
30% {
|
||||
display: grid;
|
||||
grid-template-columns: 109px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
40% {
|
||||
display: grid;
|
||||
grid-template-columns: 102px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
50% {
|
||||
display: grid;
|
||||
grid-template-columns: 95px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
60% {
|
||||
display: grid;
|
||||
grid-template-columns: 88px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
70% {
|
||||
display: grid;
|
||||
grid-template-columns: 81px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
80% {
|
||||
display: grid;
|
||||
grid-template-columns: 74px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
90% {
|
||||
display: grid;
|
||||
grid-template-columns: 67px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
100% {
|
||||
display: grid;
|
||||
grid-template-columns: 60 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes gridTemplateColumns_130_60 {
|
||||
0% {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
10% {
|
||||
display: grid;
|
||||
grid-template-columns: 123px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
20% {
|
||||
display: grid;
|
||||
grid-template-columns: 116px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
30% {
|
||||
display: grid;
|
||||
grid-template-columns: 109px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
40% {
|
||||
display: grid;
|
||||
grid-template-columns: 102px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
50% {
|
||||
display: grid;
|
||||
grid-template-columns: 95px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
60% {
|
||||
display: grid;
|
||||
grid-template-columns: 88px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
70% {
|
||||
display: grid;
|
||||
grid-template-columns: 81px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
80% {
|
||||
display: grid;
|
||||
grid-template-columns: 74px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
90% {
|
||||
display: grid;
|
||||
grid-template-columns: 67px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
100% {
|
||||
display: grid;
|
||||
grid-template-columns: 60 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@import './girdtemp.less';
|
||||
@import './logo.less';
|
||||
@import './text.less';
|
||||
@@ -1,187 +0,0 @@
|
||||
@keyframes logoSize_30_60 {
|
||||
0% {
|
||||
width: 30px;
|
||||
height: 45px;
|
||||
}
|
||||
10% {
|
||||
width: 33px;
|
||||
height: 50px;
|
||||
}
|
||||
20% {
|
||||
width: 36px;
|
||||
height: 55px;
|
||||
}
|
||||
30% {
|
||||
width: 39px;
|
||||
height: 60px;
|
||||
}
|
||||
40% {
|
||||
width: 42px;
|
||||
height: 65px;
|
||||
}
|
||||
50% {
|
||||
width: 45px;
|
||||
height: 70px;
|
||||
}
|
||||
60% {
|
||||
width: 48px;
|
||||
height: 75px;
|
||||
}
|
||||
70% {
|
||||
width: 51px;
|
||||
height: 80px;
|
||||
}
|
||||
80% {
|
||||
width: 54px;
|
||||
height: 85px;
|
||||
}
|
||||
90% {
|
||||
width: 57px;
|
||||
height: 90px;
|
||||
}
|
||||
100% {
|
||||
width: 60px;
|
||||
height: 95px;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes logoSize_30_60 {
|
||||
0% {
|
||||
width: 30px;
|
||||
height: 45px;
|
||||
}
|
||||
10% {
|
||||
width: 33px;
|
||||
height: 50px;
|
||||
}
|
||||
20% {
|
||||
width: 36px;
|
||||
height: 55px;
|
||||
}
|
||||
30% {
|
||||
width: 39px;
|
||||
height: 60px;
|
||||
}
|
||||
40% {
|
||||
width: 42px;
|
||||
height: 65px;
|
||||
}
|
||||
50% {
|
||||
width: 45px;
|
||||
height: 70px;
|
||||
}
|
||||
60% {
|
||||
width: 48px;
|
||||
height: 75px;
|
||||
}
|
||||
70% {
|
||||
width: 51px;
|
||||
height: 80px;
|
||||
}
|
||||
80% {
|
||||
width: 54px;
|
||||
height: 85px;
|
||||
}
|
||||
90% {
|
||||
width: 57px;
|
||||
height: 90px;
|
||||
}
|
||||
100% {
|
||||
width: 60px;
|
||||
height: 95px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes logoSize_60_30 {
|
||||
0% {
|
||||
width: 60px;
|
||||
height: 95px;
|
||||
}
|
||||
10% {
|
||||
width: 57px;
|
||||
height: 90px;
|
||||
}
|
||||
20% {
|
||||
width: 54px;
|
||||
height: 85px;
|
||||
}
|
||||
30% {
|
||||
width: 51px;
|
||||
height: 80px;
|
||||
}
|
||||
40% {
|
||||
width: 48px;
|
||||
height: 75px;
|
||||
}
|
||||
50% {
|
||||
width: 45px;
|
||||
height: 70px;
|
||||
}
|
||||
60% {
|
||||
width: 42px;
|
||||
height: 65px;
|
||||
}
|
||||
70% {
|
||||
width: 39px;
|
||||
height: 60px;
|
||||
}
|
||||
80% {
|
||||
width: 36px;
|
||||
height: 55px;
|
||||
}
|
||||
90% {
|
||||
width: 33px;
|
||||
height: 50px;
|
||||
}
|
||||
100% {
|
||||
width: 30px;
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes logoSize_60_30 {
|
||||
0% {
|
||||
width: 60px;
|
||||
height: 95px;
|
||||
}
|
||||
10% {
|
||||
width: 57px;
|
||||
height: 90px;
|
||||
}
|
||||
20% {
|
||||
width: 54px;
|
||||
height: 85px;
|
||||
}
|
||||
30% {
|
||||
width: 51px;
|
||||
height: 80px;
|
||||
}
|
||||
40% {
|
||||
width: 48px;
|
||||
height: 75px;
|
||||
}
|
||||
50% {
|
||||
width: 45px;
|
||||
height: 70px;
|
||||
}
|
||||
60% {
|
||||
width: 42px;
|
||||
height: 65px;
|
||||
}
|
||||
70% {
|
||||
width: 39px;
|
||||
height: 60px;
|
||||
}
|
||||
80% {
|
||||
width: 36px;
|
||||
height: 55px;
|
||||
}
|
||||
90% {
|
||||
width: 33px;
|
||||
height: 50px;
|
||||
}
|
||||
100% {
|
||||
width: 30px;
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
@keyframes text_0_1 {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes text_0_1 {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes text_1_0 {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes text_1_0 {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
@import './animations/index.less';
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.appContainer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
min-width: 900px;
|
||||
|
||||
.updateMask {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.65);
|
||||
padding: 0 180px;
|
||||
|
||||
&.flexContainer {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ant-progress-text {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.updateTip {
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.sideBar {
|
||||
position: relative;
|
||||
grid-area: sidebar;
|
||||
background-color: #2e72b8;
|
||||
overflow: hidden;
|
||||
|
||||
.logoBox {
|
||||
height: 154px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
background-image: url('../../../../assets/logo_u_white.svg');
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.logoExpand {
|
||||
.logo;
|
||||
|
||||
width: 60px;
|
||||
height: 95px;
|
||||
animation: logoSize_30_60 0.15s ease both;
|
||||
-webkit-animation: logoSize_30_60 0.15s ease both;
|
||||
}
|
||||
|
||||
.logoUnExpand {
|
||||
.logo;
|
||||
|
||||
width: 30px;
|
||||
height: 45px;
|
||||
animation: logoSize_60_30 0.15s ease both;
|
||||
-webkit-animation: logoSize_60_30 0.15s ease both;
|
||||
}
|
||||
|
||||
.version {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: 200;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-area: content;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toolbar-icon {
|
||||
margin-right: 5px;
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.anticon {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.subApp {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sideExpand {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.appContainerUnExpand {
|
||||
.appContainer;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 60px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
animation: gridTemplateColumns_130_60 0.15s ease both;
|
||||
-webkit-animation: gridTemplateColumns_130_60 0.15s ease both;
|
||||
}
|
||||
|
||||
.appContainerExpand {
|
||||
.appContainer;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
grid-template-rows: 100%;
|
||||
grid-template-areas: 'sidebar content';
|
||||
animation: gridTemplateColumns_60_130 0.15s ease both;
|
||||
-webkit-animation: gridTemplateColumns_60_130 0.15s ease both;
|
||||
}
|
||||
|
||||
.iconExpand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: scale(1);
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
|
||||
.iconUnExpand {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: scale(1.5);
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
|
||||
.arrowExpand {
|
||||
transition: transform 0.15s;
|
||||
transform: rotateY(0deg);
|
||||
}
|
||||
|
||||
.arrowUnExpand {
|
||||
transition: transform 0.15s;
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
|
||||
.textShow {
|
||||
display: flex;
|
||||
animation: text_0_1 0.15s ease both;
|
||||
-webkit-animation: text_0_1 0.15s ease both;
|
||||
}
|
||||
|
||||
.textHidden {
|
||||
display: none;
|
||||
animation: text_1_0 0.15s ease both;
|
||||
-webkit-animation: text_1_0 0.15s ease both;
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
import { LeftOutlined } from '@ant-design/icons';
|
||||
import { Progress } from 'antd';
|
||||
import { ipcRenderer } from 'electron-better-ipc';
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
|
||||
import { AppState } from '@/ducks';
|
||||
import { commonActions } from '@/ducks/common';
|
||||
import { PageLoading, history } from '@/skeleton';
|
||||
import { LocalConfig } from 'rte-core';
|
||||
|
||||
import { Menu } from '../../components/Menu';
|
||||
|
||||
import { menuConfigs } from './manifest';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export interface HomeProps extends RouteComponentProps {
|
||||
localConfig: LocalConfig;
|
||||
|
||||
loadLocalConfig: () => void;
|
||||
}
|
||||
|
||||
export interface HomeState {
|
||||
downloadProgress?: number;
|
||||
|
||||
sideExpand: boolean;
|
||||
}
|
||||
|
||||
export class HomeComp extends React.PureComponent<HomeProps, HomeState> {
|
||||
constructor(props: HomeProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
sideExpand: true,
|
||||
};
|
||||
|
||||
ipcRenderer.on('update-download-progress', (_: any, percent: number) => {
|
||||
this.setState({
|
||||
downloadProgress: percent,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.loadLocalConfig();
|
||||
}
|
||||
|
||||
renderMask(): JSX.Element {
|
||||
const downloadProgress = this.state.downloadProgress;
|
||||
return (
|
||||
<div className={`${styles.updateMask} ${styles.flexContainer}`}>
|
||||
<p className={styles.updateTip}>当前版本过低,正在下载最新版...</p>
|
||||
<Progress percent={downloadProgress} strokeWidth={12} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { location, localConfig } = this.props;
|
||||
const { downloadProgress, sideExpand } = this.state;
|
||||
|
||||
// 默认跳转到软件管理的页面
|
||||
const currentPath =
|
||||
location.pathname === '/' ? '/software' : location.pathname;
|
||||
|
||||
if (!localConfig || !localConfig.clientApp) {
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<PageLoading />
|
||||
<h3 style={{ marginTop: 16 }}>加载本地配置中</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div
|
||||
className={
|
||||
sideExpand ? styles.appContainerExpand : styles.appContainerUnExpand
|
||||
}
|
||||
>
|
||||
<div className={styles.sideBar}>
|
||||
<div className={styles.logoBox}>
|
||||
<div
|
||||
className={sideExpand ? styles.logoExpand : styles.logoUnExpand}
|
||||
/>
|
||||
</div>
|
||||
<Menu
|
||||
defaultKey={history.location.pathname}
|
||||
onSelect={path => history.push(String(path))}
|
||||
>
|
||||
{menuConfigs
|
||||
.filter(m => !m.menuProps.disabled)
|
||||
.map(m => {
|
||||
const { icon, text } = m.menuProps;
|
||||
const { path } = m.routeProps;
|
||||
const isSelected = currentPath.startsWith(
|
||||
m.routeProps.path as string,
|
||||
);
|
||||
|
||||
return (
|
||||
<Menu.Item key={path as string} isSelected={isSelected}>
|
||||
<span
|
||||
className={
|
||||
sideExpand ? styles.iconExpand : styles.iconUnExpand
|
||||
}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
<span
|
||||
className={
|
||||
sideExpand ? styles.textShow : styles.textHidden
|
||||
}
|
||||
style={{ marginLeft: 6 }}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
|
||||
<div className={styles.version}>
|
||||
<div
|
||||
className={styles.sideExpand}
|
||||
onClick={() => this.setState({ sideExpand: !sideExpand })}
|
||||
>
|
||||
<LeftOutlined
|
||||
className={
|
||||
sideExpand ? styles.arrowExpand : styles.arrowUnExpand
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
{menuConfigs.map(m => {
|
||||
const isSelected = currentPath.startsWith(
|
||||
m.routeProps.path as string,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.subApp}
|
||||
key={m.routeProps.path as string}
|
||||
style={{
|
||||
opacity: isSelected ? 1 : 0,
|
||||
zIndex: isSelected ? 1 : -1,
|
||||
}}
|
||||
>
|
||||
<m.routeProps.component {...this.props} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{downloadProgress ? this.renderMask() : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Home = connect(
|
||||
(_state: AppState) => ({
|
||||
localConfig: _state.common.localConfig,
|
||||
}),
|
||||
{
|
||||
loadLocalConfig: commonActions.loadLocalConfig,
|
||||
},
|
||||
)(withRouter(HomeComp));
|
||||
@@ -1,73 +0,0 @@
|
||||
import {
|
||||
ApiOutlined,
|
||||
AppstoreOutlined,
|
||||
CompassOutlined,
|
||||
MessageOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import React from 'react';
|
||||
import { RouteProps } from 'react-router-dom';
|
||||
|
||||
import { DeviceHome } from '@/apps/device/containers/DeviceHome';
|
||||
import { Workspace } from '@/apps/operator/containers/Workspace';
|
||||
|
||||
import { SoftwareMesh } from '../SoftwareMesh';
|
||||
import { Support } from '../Support';
|
||||
|
||||
export interface MenuConfig {
|
||||
menuProps: {
|
||||
disabled?: boolean;
|
||||
text: string;
|
||||
icon: string;
|
||||
};
|
||||
routeProps: RouteProps;
|
||||
}
|
||||
|
||||
export const menuConfigs: MenuConfig[] = [
|
||||
{
|
||||
menuProps: {
|
||||
disabled: false,
|
||||
text: '工作区',
|
||||
icon: ((<AppstoreOutlined />) as unknown) as string,
|
||||
},
|
||||
routeProps: {
|
||||
path: '/workspace',
|
||||
component: Workspace,
|
||||
exact: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
menuProps: {
|
||||
disabled: false,
|
||||
text: '设备管理',
|
||||
icon: ((<ApiOutlined />) as unknown) as string,
|
||||
},
|
||||
routeProps: {
|
||||
path: '/device',
|
||||
component: DeviceHome,
|
||||
exact: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
menuProps: {
|
||||
disabled: false,
|
||||
text: '软件工具',
|
||||
icon: ((<CompassOutlined />) as unknown) as string,
|
||||
},
|
||||
routeProps: {
|
||||
path: '/software',
|
||||
component: SoftwareMesh,
|
||||
exact: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
menuProps: {
|
||||
disabled: false,
|
||||
text: '联系我们',
|
||||
icon: ((<MessageOutlined />) as unknown) as string,
|
||||
},
|
||||
routeProps: {
|
||||
path: '/support',
|
||||
component: Support,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,22 +0,0 @@
|
||||
.mobileContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('../../../../assets/bg4.png');
|
||||
background-size: cover;
|
||||
padding: 85px 30px;
|
||||
|
||||
.qrcodeWrapper {
|
||||
opacity: 0.92;
|
||||
background: #fff;
|
||||
border: 1px solid #9b9b9b;
|
||||
border-radius: 10px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
background-size: cover;
|
||||
width: 690px;
|
||||
height: 410px;
|
||||
margin: 10px auto;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export const Mobile = () => (
|
||||
<div className={styles.mobileContainer}>
|
||||
<div className={styles.qrcodeWrapper}>
|
||||
<div className={styles.qrcode} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1,44 +0,0 @@
|
||||
.homeContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.banner {
|
||||
height: 183px;
|
||||
background-image: url('../../../../assets/bg1.png');
|
||||
background-size: cover;
|
||||
padding-top: 22px;
|
||||
padding-left: 100px;
|
||||
color: #fff;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.slogonBig {
|
||||
font-size: 22px;
|
||||
letter-spacing: 1.18px;
|
||||
margin-bottom: 19px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.slogonSmall {
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.54px;
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
#sg-tools {
|
||||
grid-template-areas: 'yhwsgzb yhwyqzb sgtb . .';
|
||||
grid-template-columns: repeat(3, auto) 1fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
}
|
||||
|
||||
#jl-tools {
|
||||
grid-template-rows: auto;
|
||||
grid-template-columns: repeat(2, auto) 1fr;
|
||||
grid-template-areas: 'jlzb jltb';
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { AppSeries } from 'rte-core';
|
||||
|
||||
import { InstallableAppSeriesView } from '../../components/InstallableAppSeriesView';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
interface SoftwareMeshProps {}
|
||||
|
||||
export class SoftwareMesh extends Component<SoftwareMeshProps, {}> {
|
||||
componentDidMount() {}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.homeContainer}>
|
||||
<div className={styles.banner}>
|
||||
<div className={styles.slogonBig}>
|
||||
<p>简单、易操作</p>
|
||||
<p>生成文件全面、清晰</p>
|
||||
<p>智能查错</p>
|
||||
</div>
|
||||
<div className={styles.slogonSmall}>
|
||||
<p>专用工具软件,兼容性强,支持XP/WIN7/WIN8/WIN10环境下运行。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.tools}>
|
||||
{[].map((pl: AppSeries) => (
|
||||
<InstallableAppSeriesView data={pl} key={pl.name} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
.consultContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('../../../../assets/bg5.png');
|
||||
background-size: cover;
|
||||
padding: 30px;
|
||||
padding-bottom: 66px;
|
||||
position: relative;
|
||||
|
||||
.tips {
|
||||
padding: 20px 25px 20px 40px;
|
||||
background: #fff;
|
||||
border: 1px solid #9b9b9b;
|
||||
border-radius: 10px;
|
||||
-webkit-user-select: text;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tipsList {
|
||||
margin-bottom: 0;
|
||||
|
||||
li p:first-child {
|
||||
margin-left: -25px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.anticon-heart {
|
||||
margin-right: 15px;
|
||||
margin-top: -5px;
|
||||
font-size: 10px;
|
||||
color: #108ee9;
|
||||
}
|
||||
}
|
||||
|
||||
.clsh {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.clsh-list {
|
||||
p:nth-child(n + 2) {
|
||||
text-indent: 22px;
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.openQq {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
right: 30px;
|
||||
bottom: 20px;
|
||||
width: 107px;
|
||||
height: 36px;
|
||||
background-color: rgba(16, 142, 233, 0.9);
|
||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.12), 0 8px 8px 0 rgba(0, 0, 0, 0.24);
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.1s ease;
|
||||
cursor: pointer;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(16, 142, 233, 1);
|
||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.22), 0 8px 8px 0 rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
|
||||
.qqIcon {
|
||||
width: 19px;
|
||||
height: 20px;
|
||||
background-image: url('../../../../assets/qq.svg');
|
||||
background-size: contain;
|
||||
background-position: center center;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
letter-spacing: 0.44px;
|
||||
font-weight: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.consultButton {
|
||||
height: 0;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
background-color: #449cdb;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.12), 0 4px 4px 0 rgba(0, 0, 0, 0.24);
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
bottom: 50px;
|
||||
right: 90px;
|
||||
cursor: pointer;
|
||||
line-height: 40px;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(16, 142, 233, 1);
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.22), 0 4px 4px 0 rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
}
|
||||
|
||||
.jlButton {
|
||||
position: absolute;
|
||||
opacity: 1;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
bottom: 80px;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.sgButton {
|
||||
position: absolute;
|
||||
opacity: 1;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
bottom: 80px;
|
||||
right: 97px;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import { HeartOutlined } from '@ant-design/icons';
|
||||
import classnames from 'classnames';
|
||||
import { shell } from 'electron';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
interface SupportState {
|
||||
expandButton: boolean;
|
||||
}
|
||||
|
||||
enum QQGroup {
|
||||
JL,
|
||||
SG,
|
||||
}
|
||||
|
||||
export class Support extends Component<{}, SupportState> {
|
||||
state = {
|
||||
expandButton: false,
|
||||
};
|
||||
|
||||
getQQ(group: QQGroup): string {
|
||||
const KEY = 'kefu_qq' + group;
|
||||
let qq = localStorage.getItem(KEY);
|
||||
if (!qq) {
|
||||
let qqList: string[] | null;
|
||||
switch (group) {
|
||||
case QQGroup.JL:
|
||||
qqList = ['3014437365'];
|
||||
break;
|
||||
case QQGroup.SG:
|
||||
qqList = ['79157865', '1007008187'];
|
||||
break;
|
||||
default:
|
||||
qqList = null;
|
||||
}
|
||||
if (!qqList || qqList.length === 0) {
|
||||
return '';
|
||||
}
|
||||
const randomIndex = Math.floor(Math.random() * qqList.length);
|
||||
qq = qqList[randomIndex];
|
||||
localStorage.setItem(KEY, qq);
|
||||
}
|
||||
return qq;
|
||||
}
|
||||
|
||||
handleOpenQQ = (group: QQGroup): void => {
|
||||
const qq = this.getQQ(group);
|
||||
if (!qq) {
|
||||
alert('客服不在线');
|
||||
return;
|
||||
}
|
||||
console.log(qq);
|
||||
shell.openExternal(
|
||||
`http://wpa.qq.com/msgrd?v=3&uin=${qq}&site=qq&menu=yes`,
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { expandButton } = this.state;
|
||||
const sgc = expandButton
|
||||
? classnames(styles.consultButton, styles.sgButton)
|
||||
: styles.consultButton;
|
||||
const jlc = styles.consultButton
|
||||
? classnames(styles.consultButton, styles.jlButton)
|
||||
: styles.consultButton;
|
||||
return (
|
||||
<div className={styles.consultContainer}>
|
||||
<div className={styles.tips}>
|
||||
<ul className={styles.tipsList}>
|
||||
<li>
|
||||
<p>
|
||||
<HeartOutlined />
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
className={styles.openQq}
|
||||
onClick={() => this.setState({ expandButton: !expandButton })}
|
||||
style={{ opacity: expandButton ? 0.3 : 1 }}
|
||||
>
|
||||
<div className={styles.qqIcon} />
|
||||
<span>在线咨询</span>
|
||||
</div>
|
||||
<div className={sgc} onClick={() => this.handleOpenQQ(QQGroup.SG)}>
|
||||
QQ 1
|
||||
</div>
|
||||
<div className={jlc} onClick={() => this.handleOpenQQ(QQGroup.JL)}>
|
||||
QQ 2
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import { callMain } from '@/apis';
|
||||
import { App, IpcEvent } from 'rte-core';
|
||||
|
||||
/** App 管理器,用来判断某个 App 是否已经安装、安装地址等信息 */
|
||||
export class AppMgt {
|
||||
downloadProgess = 0;
|
||||
|
||||
downloading = false;
|
||||
|
||||
constructor(public app: App) {
|
||||
// 安装完毕后,缓存好 Installer 的地址
|
||||
ipcRenderer.on(`get-installer-${this.key}`, (_event: any, path: string) => {
|
||||
localStorage.setItem(`installer-${this.key}`, path);
|
||||
});
|
||||
}
|
||||
|
||||
get key() {
|
||||
return this.app.key;
|
||||
}
|
||||
|
||||
setProgress = (_: any, percent: number) => {
|
||||
if (percent >= 100 || percent < 0) {
|
||||
this.downloading = false;
|
||||
this.downloadProgess = 0;
|
||||
} else {
|
||||
this.downloading = true;
|
||||
this.downloadProgess = percent;
|
||||
}
|
||||
};
|
||||
|
||||
async getLocation(): Promise<string> {
|
||||
if (this.app.location) {
|
||||
return this.app.location;
|
||||
}
|
||||
|
||||
const location = await callMain(
|
||||
'search-app-location',
|
||||
new IpcEvent({ app: this.app }),
|
||||
);
|
||||
|
||||
this.app.location = location;
|
||||
}
|
||||
|
||||
async run(): Promise<string> {
|
||||
if (!this.app.location) {
|
||||
return;
|
||||
}
|
||||
return await callMain('run-app', new IpcEvent({}));
|
||||
}
|
||||
|
||||
install() {
|
||||
if (!this.app.url) {
|
||||
alert('抱歉, 产品即将上线。');
|
||||
return;
|
||||
}
|
||||
|
||||
const cachePath = localStorage.getItem(`installer-${this.key}`);
|
||||
this.downloading = true;
|
||||
|
||||
ipcRenderer.on(`download-progress-${this.key}`, this.setProgress);
|
||||
ipcRenderer.once(`download-complete-${this.key}`, () => {
|
||||
ipcRenderer.removeListener(
|
||||
`download-progress-${this.key}`,
|
||||
this.setProgress,
|
||||
);
|
||||
this.downloading = false;
|
||||
});
|
||||
ipcRenderer.once(`get-location-${this.key}`, (_: any, location: string) => {
|
||||
this.app.location = location;
|
||||
});
|
||||
|
||||
// 触发后端开启下载监听
|
||||
ipcRenderer.send('begin-download', this, cachePath);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
@import '~@/skeleton/styles/variables.less';
|
||||
|
||||
.container {
|
||||
.appContainer;
|
||||
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.iframe {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
// import * as S from 'ufc-schema';
|
||||
|
||||
import { AppState } from '@/ducks';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export interface UfcCloudProps extends RouteComponentProps {}
|
||||
|
||||
export interface UfcCloudState {}
|
||||
|
||||
export class UfcCloudComp extends React.PureComponent<
|
||||
UfcCloudProps,
|
||||
UfcCloudState
|
||||
> {
|
||||
constructor(props: UfcCloudProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<iframe
|
||||
src="http://cloud.unionfab.com/"
|
||||
className={styles.iframe}
|
||||
frameBorder={0}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const UfcCloud = connect(
|
||||
(_state: AppState) => ({}),
|
||||
{},
|
||||
)(withRouter(UfcCloudComp));
|
||||
@@ -1,3 +0,0 @@
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
import * as S from 'ufc-schema';
|
||||
|
||||
import { AppState } from '@/ducks';
|
||||
|
||||
import * as styles from './index.less';
|
||||
|
||||
export interface WorkOrderMeshProps extends RouteComponentProps {}
|
||||
|
||||
export interface WorkOrderMeshState {}
|
||||
|
||||
export class WorkOrderMeshComp extends React.PureComponent<
|
||||
WorkOrderMeshProps,
|
||||
WorkOrderMeshState
|
||||
> {
|
||||
constructor(props: WorkOrderMeshProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
console.log(S);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className={styles.container}>WorkOrderMesh</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export const WorkOrderMesh = connect(
|
||||
(_state: AppState) => ({}),
|
||||
{},
|
||||
)(withRouter(WorkOrderMeshComp));
|
||||
@@ -1,8 +0,0 @@
|
||||
@import '~@/skeleton/styles/variables.less';
|
||||
|
||||
.container {
|
||||
.appContainer;
|
||||
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
|
||||
import { Toolbar } from '@/apps/home/components/Toolbar';
|
||||
import { AppState } from '@/ducks';
|
||||
|
||||
import { UfcCloud } from '../UfcCloud';
|
||||
|
||||
import * as styles from './index.less';
|
||||
// import * as S from 'ufc-schema';
|
||||
|
||||
export interface WorkspaceProps extends RouteComponentProps {}
|
||||
|
||||
export interface WorkspaceState {}
|
||||
|
||||
export class WorkspaceComp extends React.PureComponent<
|
||||
WorkspaceProps,
|
||||
WorkspaceState
|
||||
> {
|
||||
constructor(props: WorkspaceProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Toolbar>
|
||||
<div />
|
||||
</Toolbar>
|
||||
<UfcCloud />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Workspace = connect(
|
||||
(_state: AppState) => ({}),
|
||||
{},
|
||||
)(withRouter(WorkspaceComp));
|
||||
@@ -1,26 +0,0 @@
|
||||
<svg viewBox="0 0 782 294" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<linearGradient x1="36.819%" y1="72.252%" x2="89.271%" y2="53.881%" id="b">
|
||||
<stop stop-color="#989CFC" offset="0%" />
|
||||
<stop stop-color="#FFF" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="41.071%" y1="0%" x2="55.892%" y2="95.616%" id="c">
|
||||
<stop stop-color="#FFF" offset="0%" />
|
||||
<stop stop-color="#CED3FF" offset="100%" />
|
||||
</linearGradient>
|
||||
<path d="M94.786 75.452l-6.929 2.314V64.394l6.928-2.316v13.374zm8.785 8.806l-8.785 2.936V96l8.785-2.935v-8.807zm14.643-12.72l-8.785 2.935v11.742l8.785-2.935V71.538zm0-22.506L123 47.433v10.764l-4.785 1.599V49.032zm-23.428 26.42l8.785-2.936V53.924l5.858-1.956V63.71l8.785-2.936v10.764L123 69.939v22.656C123 126.508 95.466 154 61.5 154S0 126.508 0 92.595v-75.05C0 7.854 7.867 0 17.571 0c9.705 0 17.572 7.855 17.572 17.544v71.152c0 14.534 11.8 26.317 26.357 26.317 14.283 0 25.912-11.343 26.345-25.5l6.94-2.32v-11.74zm14.643-35.226l-5.858 1.956v-7.347l5.857-2.275v7.666zm8.785 8.806l-8.785 2.936V40.226l8.785-2.965v11.771z" id="a" />
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path d="M632.376 199.006c2.843.897 5.607 2.046 8.406 3.084.558.207 1.111.427 1.869.719.08-1.628.179-3.082.213-4.54.03-1.218.533-1.918 1.818-1.908 1.772.015 3.545.04 5.317.042 4.66.006 9.32.002 14.1.002v95.793c-8.574.194-17.222 1.561-25.452-2.145-1.016-.457-1.842-.456-2.86-.02-24.277 10.371-49.548-1.288-60.38-19.549-9.343-15.748-10.097-32.097-1.293-48.325 7.252-13.368 18.63-21.72 33.515-24.951 8.355-1.815 16.65-.756 24.747 1.798zm-286.212-3.08c27.42.181 49.068 21.8 48.929 48.862-.142 27.32-22.13 49.31-49.116 49.12-27.015-.19-49.161-22.467-48.952-49.24.212-27.162 22.149-48.921 49.139-48.742zm358.32-44.12V200.8c2.493-.396 4.816-.75 7.134-1.136 5.894-.98 11.75-2.378 17.685-2.892 19.06-1.652 33.787 5.943 44.007 21.966 5.995 9.4 8.577 19.891 7.687 31.046-.744 9.308-4.477 17.516-10.273 24.768-6.666 8.343-14.97 14.411-25.377 17.173-27.63 7.33-55.205-9.436-60.465-37.595-.62-3.317-.906-6.742-.922-10.12-.134-27.389-.167-54.779-.227-82.168-.008-3.064.34-3.459 3.282-4.395 5.161-1.642 10.306-3.338 15.458-5.01.574-.186 1.15-.36 2.01-.63zM172.377 195.9c.99-.009 1.434.243 1.567 1.301.21 1.694.623 3.362.97 5.146 2.83-.82 5.524-1.677 8.258-2.378 7.942-2.033 16.036-2.78 23.858-.065 16.254 5.64 26.936 16.63 29.965 33.933.9 5.145.63 10.52.646 15.789.04 13.797-.059 27.593-.106 41.39 0 .291-.044.582-.074.942h-20.428c-.072-1.492-.2-2.944-.204-4.396-.029-12.935.036-25.87-.09-38.804-.041-4.243-.089-8.58-.943-12.707-3.082-14.9-20.92-21.46-33.24-12.479-6.21 4.526-8.952 10.624-8.931 18.18.044 15.977.037 31.955.05 47.933v2.09H153v-95.856h5.226c4.718 0 9.435.024 14.152-.019zm258.497-.01c1.192-.014 1.592.383 1.678 1.514.13 1.707.398 3.404.605 5.083 3.67-1.074 7.176-2.233 10.75-3.122 22.545-5.607 41.07 8.284 47.791 21.088 2.514 4.789 3.838 9.887 4.181 15.267 1.02 16.017.573 32.042.346 48.067-.037 2.625-.005 5.251-.005 7.94-.59.045-.981.1-1.373.1-5.833.006-11.666-.023-17.499.028-1.121.01-1.535-.326-1.523-1.45.032-3.043-.023-6.086-.028-9.13l-.02-25.055a5231.4 5231.4 0 00-.028-12.529c-.018-5.536-1.234-10.745-4.447-15.38-5.006-7.22-14.247-10.397-23.347-7.99-8.534 2.257-14.746 9.598-15.433 18.32-.167 2.12-.21 4.255-.213 6.383-.013 10.009-.015 20.018-.015 30.027v16.894h-20.492v-95.94c.535-.032.978-.082 1.42-.083 5.885-.004 11.768.032 17.652-.032zm-151.506-.619v96.669h-20.183v-89.957l20.183-6.712zM565.955 141v21.305c-1.019.306-2.166.64-3.308.993-6.069 1.873-11.703 4.536-16.382 8.959-5.645 5.334-8.298 11.99-8.678 19.637-.09 1.807-.013 3.624-.013 5.626h28.767v19.274h-28.858v75.136h-21.348c-.053-.803-.152-1.6-.152-2.399 0-30.99-.027-61.98.037-92.97.023-11.38 2.59-22.119 8.937-31.729 7.602-11.509 18.433-18.444 31.614-21.905 3.017-.792 6.117-1.265 9.384-1.927zm50.636 76.366c-15.614.529-27.792 13.281-27.266 28.578.52 15.126 12.975 26.822 27.808 26.764 16.058-.265 28.086-12.851 27.758-28.366-.318-15.028-13.495-27.478-28.3-26.976zM318.424 244.36c-.265 15.47 11.825 27.984 27.235 28.19 15.52.208 28.022-12 28.248-27.583.214-14.749-12.376-27.89-26.943-28.128-15.553-.252-28.276 12.017-28.54 27.521zm386.282-.367c-.388 15.54 11.857 28.228 27.49 28.487 14.971.25 27.563-11.959 27.843-26.996.29-15.526-11.934-28.192-27.52-28.514-14.572-.3-27.443 12.206-27.813 27.023zM279.245 155.51c1.032 7.536.18 13.916-7.914 16.862-3.424 1.247-6.867 2.444-10.305 3.654-.462.163-.943.274-1.585.458.08-3.539-.453-6.942.896-10.199 1.029-2.482 2.663-4.58 5.108-5.603 4.516-1.89 9.167-3.457 13.8-5.172z" fill="#FFF" />
|
||||
<g transform="translate(0 140)">
|
||||
<mask id="d" fill="#fff">
|
||||
<use xlink:href="#a" />
|
||||
</mask>
|
||||
<use fill="url(#b)" xlink:href="#a" />
|
||||
<path d="M0 17.544C0 7.854 7.867 0 17.571 0c9.705 0 17.572 7.855 17.572 17.544v71.152c0 14.534 4.773 43.282 28.97 51.674 24.199 8.393 37.175 1.541 37.175 1.541s-3.644 19.098-43.432 17.278C23.914 157.636 0 126.508 0 92.595v-75.05z" fill="url(#c)" mask="url(#d)" />
|
||||
</g>
|
||||
<text font-family="PingFangSC-Medium, PingFang SC" font-size="120" font-weight="400" fill="#FFF" transform="translate(0 -28)">
|
||||
<tspan x="211" y="127">优联云</tspan>
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -1,6 +0,0 @@
|
||||
.container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import cn from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
export interface CopyrightProps {
|
||||
className?: string;
|
||||
style?: Record<string, string | number>;
|
||||
}
|
||||
|
||||
export const Copyright = ({ className, style }: CopyrightProps) => {
|
||||
return (
|
||||
<div className={cn(className, styles.container)} style={style}>
|
||||
© 2019-2020 Unionfab 版权所有
|
||||
<span style={{ marginLeft: 16 }}>
|
||||
ICP 证:
|
||||
<a href="http://www.beian.miit.gov.cn/publish/query/indexFirst.action">
|
||||
沪ICP备17023219号
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: fit-content;
|
||||
|
||||
.header {
|
||||
height: 75px;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
height: 100%;
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
float: left;
|
||||
margin-left: 32px;
|
||||
font-size: 18px;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.actions {
|
||||
height: 100%;
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
float: right;
|
||||
margin-right: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fields {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
|
||||
.field_item {
|
||||
height: 50px;
|
||||
margin-left: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.field_name {
|
||||
font-size: 16px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.field_value {
|
||||
min-width: 200px;
|
||||
margin-left: 16px;
|
||||
font-size: 16px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
import { Button, DatePicker, Input, InputNumber, Select } from 'antd';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
export type FieldsEditType = 'INPUT' | 'NUMBER' | 'SELECT' | 'DATE';
|
||||
|
||||
interface FieldsDetailProps {
|
||||
title?: string;
|
||||
fields: FieldType[];
|
||||
dataSource: Record<string, any>;
|
||||
|
||||
edit?: boolean;
|
||||
delete?: boolean;
|
||||
|
||||
saveFun?: (data: Record<string, any>) => void;
|
||||
deleteFun?: () => void;
|
||||
}
|
||||
|
||||
interface FieldsDetailState {
|
||||
data: Record<string, any>;
|
||||
|
||||
isEditing: boolean;
|
||||
}
|
||||
|
||||
export interface FieldType {
|
||||
field: string;
|
||||
fieldLv2?: string;
|
||||
name: string;
|
||||
edit?: FieldsEditType;
|
||||
hidden?: boolean;
|
||||
editField?: string;
|
||||
options?: Record<string, string>[];
|
||||
}
|
||||
|
||||
export class FieldsDetail extends React.Component<
|
||||
FieldsDetailProps,
|
||||
FieldsDetailState
|
||||
> {
|
||||
constructor(props: FieldsDetailProps) {
|
||||
super(props);
|
||||
this.state = { data: null, isEditing: false };
|
||||
}
|
||||
componentDidUpdate() {
|
||||
if (this.props.dataSource && !this.state.data) {
|
||||
this.setState({ data: this.props.dataSource });
|
||||
}
|
||||
}
|
||||
|
||||
changeFieldValue = (item: FieldType, value: string | number) => {
|
||||
const newData = { ...this.state.data };
|
||||
if (item.editField) {
|
||||
item.fieldLv2 && newData[item.field]
|
||||
? (newData[item.field][item.editField] = value)
|
||||
: null;
|
||||
!item.fieldLv2 ? (newData[item.editField] = value) : null;
|
||||
} else {
|
||||
item.fieldLv2 && newData[item.field]
|
||||
? (newData[item.field][item.fieldLv2] = value)
|
||||
: null;
|
||||
!item.fieldLv2 ? (newData[item.field] = value) : null;
|
||||
}
|
||||
this.setState({
|
||||
data: newData,
|
||||
});
|
||||
};
|
||||
|
||||
getValueByField = (item: FieldType, type?: string) => {
|
||||
let res = null;
|
||||
if (item.editField) {
|
||||
if (item.fieldLv2) {
|
||||
res =
|
||||
this.state.data && this.state.data[item.field]
|
||||
? this.state.data[item.field][item.editField]
|
||||
: '';
|
||||
} else {
|
||||
res = this.state.data ? this.state.data[item.editField] : '';
|
||||
}
|
||||
} else {
|
||||
if (item.fieldLv2) {
|
||||
res =
|
||||
this.state.data && this.state.data[item.field]
|
||||
? this.state.data[item.field][item.fieldLv2]
|
||||
: '';
|
||||
} else {
|
||||
res = this.state.data ? this.state.data[item.field] : '';
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'NUMBER') {
|
||||
return res !== '' ? (res as number) : 0;
|
||||
}
|
||||
if (type === 'DATE') {
|
||||
return res !== '' ? moment(res) : moment(new Date());
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
getValueToView = (item: FieldType) => {
|
||||
if (item.fieldLv2) {
|
||||
return this.props.dataSource && this.props.dataSource[item.field]
|
||||
? this.props.dataSource[item.field][item.fieldLv2]
|
||||
: '';
|
||||
} else {
|
||||
return this.props.dataSource ? this.props.dataSource[item.field] : '';
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.header}>
|
||||
{this.props.title && (
|
||||
<div className={styles.title}>{this.props.title}</div>
|
||||
)}
|
||||
<div className={styles.actions}>
|
||||
{this.state.isEditing
|
||||
? [
|
||||
<Button
|
||||
key="btn-edit"
|
||||
style={{ minWidth: 200, height: 32, marginRight: 16 }}
|
||||
onClick={() => this.props.saveFun(this.state.data)}
|
||||
>
|
||||
确定
|
||||
</Button>,
|
||||
<Button
|
||||
key="btn-cancel"
|
||||
style={{ minWidth: 200, height: 32 }}
|
||||
onClick={() =>
|
||||
this.setState({ data: null, isEditing: false })
|
||||
}
|
||||
>
|
||||
取消
|
||||
</Button>,
|
||||
]
|
||||
: [
|
||||
<Button
|
||||
key="btn-edit"
|
||||
style={{ width: 120, height: 32, marginRight: 16 }}
|
||||
hidden={!this.props.edit}
|
||||
onClick={() => this.setState({ isEditing: true })}
|
||||
>
|
||||
编辑
|
||||
</Button>,
|
||||
<Button
|
||||
key="btn-delete"
|
||||
style={{ width: 120, height: 32 }}
|
||||
hidden={!this.props.delete}
|
||||
onClick={() => this.props.deleteFun()}
|
||||
>
|
||||
删除
|
||||
</Button>,
|
||||
]}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.fields}>
|
||||
{this.props.fields.map((item: FieldType) => {
|
||||
if (!this.state.isEditing || !item.edit) {
|
||||
return (
|
||||
<div
|
||||
key={`${item.field}${item.fieldLv2 ? item.fieldLv2 : ''}`}
|
||||
className={styles.fieldItem}
|
||||
>
|
||||
<div className={styles.fieldName}>{item.name}:</div>
|
||||
<div className={styles.fieldValue}>
|
||||
{this.getValueToView(item)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (item.edit === 'INPUT') {
|
||||
return (
|
||||
<div
|
||||
key={`${item.field}${item.fieldLv2 ? item.fieldLv2 : ''}`}
|
||||
className={styles.fieldItem}
|
||||
>
|
||||
<div className={styles.fieldName}>{item.name}:</div>
|
||||
<div className={styles.fieldValue}>
|
||||
<Input
|
||||
style={{ minWidth: 120 }}
|
||||
value={this.getValueByField(item)}
|
||||
onChange={e =>
|
||||
this.changeFieldValue(item, e.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (item.edit === 'SELECT') {
|
||||
return (
|
||||
<div
|
||||
key={`${item.field}${item.fieldLv2 ? item.fieldLv2 : ''}`}
|
||||
className={styles.fieldItem}
|
||||
>
|
||||
<div className={styles.fieldName}>{item.name}:</div>
|
||||
<div className={styles.fieldValue}>
|
||||
<Select
|
||||
style={{ minWidth: 120 }}
|
||||
value={this.getValueByField(item)}
|
||||
onChange={(e: string) => this.changeFieldValue(item, e)}
|
||||
>
|
||||
{item.options.map(o => (
|
||||
<Select.Option key={o.id} value={o.id}>
|
||||
{o.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (item.edit === 'NUMBER') {
|
||||
return (
|
||||
<div
|
||||
key={`${item.field}${item.fieldLv2 ? item.fieldLv2 : ''}`}
|
||||
className={styles.fieldItem}
|
||||
>
|
||||
<div className={styles.fieldName}>{item.name}:</div>
|
||||
<div className={styles.fieldValue}>
|
||||
<InputNumber
|
||||
step={1}
|
||||
style={{ minWidth: 120 }}
|
||||
value={this.getValueByField(item, 'NUMBER')}
|
||||
onChange={e => this.changeFieldValue(item, e)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (item.edit === 'DATE') {
|
||||
return (
|
||||
<div
|
||||
key={`${item.field}${item.fieldLv2 ? item.fieldLv2 : ''}`}
|
||||
className={styles.fieldItem}
|
||||
>
|
||||
<div className={styles.fieldName}>{item.name}:</div>
|
||||
<div className={styles.fieldValue}>
|
||||
<DatePicker
|
||||
style={{ minWidth: 120 }}
|
||||
value={this.getValueByField(item, 'DATE')}
|
||||
onChange={(e: moment.Moment) =>
|
||||
this.changeFieldValue(item, e.format('YYYY-MM-DD'))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<svg width="1440" height="900" viewBox="0 0 1440 900"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient x1="111.535%" y1="110.503%" x2="-1.294%" y2="-2.148%" id="a">
|
||||
<stop stop-color="#434EAD" offset="0%" />
|
||||
<stop stop-color="#8C8DFF" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="109.596%" y1="94.799%" x2="-25.051%" y2="7.677%" id="b">
|
||||
<stop stop-color="#1B3BD8" offset="0%" />
|
||||
<stop stop-color="#176CE0" offset="31.474%" />
|
||||
<stop stop-color="#0B92E7" offset="49.184%" />
|
||||
<stop stop-color="#0BB6EB" offset="71.324%" />
|
||||
<stop stop-color="#00E0F2" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="-11.319%" y1="-27.395%" x2="100%" y2="106.924%" id="c">
|
||||
<stop stop-color="#1B3BD8" offset="0%" />
|
||||
<stop stop-color="#176CE0" offset="31.474%" />
|
||||
<stop stop-color="#0B92E7" offset="49.184%" />
|
||||
<stop stop-color="#0BB6EB" offset="71.324%" />
|
||||
<stop stop-color="#00E0F2" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="19.94%" y1="-10.862%" x2="123.121%" y2="138.706%" id="d">
|
||||
<stop stop-color="#1B3BD8" offset="0%" />
|
||||
<stop stop-color="#176CE0" offset="31.474%" />
|
||||
<stop stop-color="#0B92E7" offset="49.184%" />
|
||||
<stop stop-color="#0BB6EB" offset="71.324%" />
|
||||
<stop stop-color="#00E0F2" offset="100%" />
|
||||
</linearGradient>
|
||||
<linearGradient x1="-8.377%" y1="50.014%" x2="99.467%" y2="50.014%" id="e">
|
||||
<stop stop-color="#54AAF6" offset="0%" />
|
||||
<stop stop-color="#529EFB" offset="24.289%" />
|
||||
<stop stop-color="#4976DB" offset="58.209%" />
|
||||
<stop stop-color="#3969D3" offset="92.422%" />
|
||||
<stop stop-color="#456DD0" offset="100%" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#FFF" d="M1 0h1440v900H1z" />
|
||||
<path d="M1086.659 1159.318l-140.068 37.53c-5.335 1.43-10.818-1.736-12.248-7.07-1.43-5.335 1.737-10.818 7.071-12.248l136.115-36.472-3.834-7.668-24.104 6.459c-5.335 1.43-10.818-1.737-12.248-7.071-1.43-5.335 1.737-10.818 7.071-12.248l20.151-5.4L716 418h900v900h-450l-79.341-158.682z" fill="url(#a)" transform="translate(-176 -418)" />
|
||||
<g opacity=".6">
|
||||
<path d="M1844 1031.5c0 132.083-107.417 239.5-239.5 239.5S1365 1163.98 1365 1031.5c0-132.083 107.417-239.5 239.5-239.5 132.48 0 239.5 107.417 239.5 239.5z" fill="url(#b)" fill-rule="nonzero" opacity=".1" transform="scale(-1 1) rotate(-28 -750.253 6904.85)" />
|
||||
<path d="M355 946.5c0 44.395-36.105 80.5-80.5 80.5S194 991.03 194 946.5c0-44.395 36.105-80.5 80.5-80.5 44.53 0 80.5 36.105 80.5 80.5z" fill="url(#c)" fill-rule="nonzero" opacity=".091" transform="translate(-176 -418)" />
|
||||
<path d="M508 254c0 140.08-113.92 254-254 254C113.92 508 0 394.502 0 254 0 113.92 113.92 0 254 0c140.502 0 254 113.92 254 254z" fill="url(#b)" fill-rule="nonzero" opacity=".1" transform="translate(-176 -418)" />
|
||||
<path d="M862 1196.5c0 44.395-36.105 80.5-80.5 80.5s-80.5-35.97-80.5-80.5c0-44.395 36.105-80.5 80.5-80.5 44.53 0 80.5 36.105 80.5 80.5zm335 101c0 44.395-36.105 80.5-80.5 80.5s-80.5-35.97-80.5-80.5c0-44.395 36.105-80.5 80.5-80.5 44.53 0 80.5 36.105 80.5 80.5z" fill="url(#c)" fill-rule="nonzero" opacity=".091" transform="translate(-176 -418)" />
|
||||
<g opacity=".228">
|
||||
<path d="M80 40c0 22.06-17.94 40-40 40S0 62.126 0 40C0 17.94 17.94 0 40 0c22.126 0 40 17.94 40 40z" opacity=".2" transform="translate(1289 104)" fill="url(#d)" fill-rule="nonzero" />
|
||||
</g>
|
||||
<g opacity=".258">
|
||||
<path d="M109 54.5c0 30.056-24.444 54.5-54.5 54.5S0 84.647 0 54.5C0 24.444 24.444 0 54.5 0 84.647 0 109 24.444 109 54.5z" opacity=".1" transform="matrix(-1 0 0 1 829 35)" fill="url(#e)" fill-rule="nonzero" />
|
||||
</g>
|
||||
</g>
|
||||
<path d="M917.855 772.726l180.565-48.383c5.335-1.43 10.819 1.737 12.248 7.071 1.43 5.335-1.736 10.818-7.071 12.248l-176.81 47.376-8.932-18.312zm-40.505 58.293l148.07-39.676c5.335-1.43 10.819 1.737 12.248 7.071 1.43 5.335-1.736 10.818-7.071 12.248l-239.55 64.187a9.967 9.967 0 01-4.047.236l90.35-44.066z" fill="#FFF" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 280 KiB |
|
Before Width: | Height: | Size: 634 KiB |
|
Before Width: | Height: | Size: 881 KiB |
|
Before Width: | Height: | Size: 693 KiB |