feat: update articles or snippets

This commit is contained in:
wx-chevalier
2020-11-13 21:36:47 +08:00
parent ff8454be07
commit 53761e10fd
535 changed files with 5 additions and 47101 deletions
+5 -5
View File
@@ -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 | 延伸阅读
-7
View File
@@ -1,7 +0,0 @@
*.log
*.tsbuildinfo
.cache
.vscode
coverage
dist
node_modules
-13
View File
@@ -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,
},
};
-15
View File
@@ -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
-8
View File
@@ -1,8 +0,0 @@
{
"eslint.validate": [
"javascript",
"javascriptreact",
{ "language": "typescript", "autoFix": true },
{ "language": "typescriptreact", "autoFix": true }
]
}
-143
View File
@@ -1,143 +0,0 @@
![](https://i.postimg.cc/0N7w0mnN/image.png)
# 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)等基本的原则。
![微前端项目结构](https://user-images.githubusercontent.com/5803001/44003230-de68ac5c-9e81-11e8-81f5-8092f7a9b421.png)
当我们考量项目框架、模板或者脚手架的时候,首先想到的点就是希望尽可能对上层屏蔽细节,但是对于长期维护的、多人协作的中大型项目而言,如果项目的主导者直接使用了部分抽象的脚手架,不免会给未来的更新、迭代带来一定的技术负债;同时,目前也有很多成熟的工程化脚手架,因此笔者选择以项目模板的形式抽象出微前端中所需要的部分。尽可能地遵循简约、直观的原则,减少抽象/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 与大前端、服务端开发实践与工程架构、分布式基础架构、人工智能与深度学习、产品运营与创业等多类目的书籍列表:
![NGTE Books](https://s2.ax1x.com/2020/01/18/19uXtI.png)
-26
View File
@@ -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
-162
View File
@@ -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
-74
View File
@@ -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
}
}
}
Binary file not shown.

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 881 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 693 KiB

Some files were not shown because too many files have changed in this diff Show More