first commit
21
.gitignore
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/npm-debug.log*
|
||||
/yarn-error.log
|
||||
/yarn.lock
|
||||
/package-lock.json
|
||||
/build
|
||||
/demos
|
||||
/dist
|
||||
问题.txt
|
||||
plugin.jpg
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
# umi
|
||||
/src/.umi/
|
||||
/.env.local
|
||||
.idea/
|
||||
2
.npmrc
Executable file
@@ -0,0 +1,2 @@
|
||||
ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/
|
||||
ELECTRON_BUILDER_BINARIES_MIRROR=http://npm.taobao.org/mirrors/electron-builder-binaries/
|
||||
69
README.md
Executable file
@@ -0,0 +1,69 @@
|
||||
<p align="center">
|
||||
<img style="width: 100px;height: 100px;" src="./website/img/logo.png"/>
|
||||
</p>
|
||||
<p align="center">
|
||||
一款前端工程化项目的GUI管理工具
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://nodejs.org/en/download/">
|
||||
<img src="https://img.shields.io/badge/node.js-v14.19.0-blue.svg" alt="nodejs">
|
||||
</a>
|
||||
<a href="https://www.electronjs.org/">
|
||||
<img src="https://img.shields.io/badge/electron-v13.5.1-brightgreen" alt="electron">
|
||||
</a>
|
||||
<a href="https://www.typescriptlang.org/">
|
||||
<img src="https://img.shields.io/badge/typescript-v4.4.4-blue" alt="typescript">
|
||||
</a>
|
||||
<a href="https://gulpjs.com/">
|
||||
<img src="https://img.shields.io/badge/gulp-v4.0.2-orange" alt="gulp">
|
||||
</a>
|
||||
<a href="https://xtermjs.org/">
|
||||
<img src="https://img.shields.io/badge/xterm-v4.9.0-brightgreen" alt="xterm">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 简介
|
||||
|
||||
`EasyProject` 是一款`Mac`平台下的前端项目管理工具,能够一键导入本地的前端项目,帮助您将零碎零散的前端项目进行统一化GUI管理和配置,协助您提高项目开发的效率。
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### 官网
|
||||
|
||||
> [https://helpcode.github.io/EasyProject/website/index.html](https://helpcode.github.io/EasyProject/website/index.html)
|
||||
|
||||
### 下载
|
||||
|
||||
打开链接,选最新版本,然后从`Assets`中选择`EasyProject-x.x.x.dmg`进行下载即可。
|
||||
|
||||
> [releases](https://github.com/helpcode/EasyProject/releases)
|
||||
|
||||
## 功能介绍
|
||||
|
||||
已实现功能:
|
||||
|
||||
- [x] 1: 欢迎页面
|
||||
- [x] 2: 导入项目,包含从 `menu`,`touchbar`,`tray`。
|
||||
- [x] 4: 移除项目,修改项目名称,图标,在访达打开,在终端打开。
|
||||
- [x] 5: 读取展示 项目的 `package.json`,并实时同步软件中修改的`package.json`到本地。
|
||||
- [x] 6: 读取展示 项目的 `script` 脚本,可以一键启停,复制 & 删除 终端日志,打开链接,滚动日志到底部。
|
||||
- [x] 7: 当停止 `script` 脚本时,增加"进程锁",等待子进程结束才能再次使用命令。
|
||||
- [x] 8: 读取展示 项目的 `devDependencies` 和 `dependencies`, 如果当前项目没有初始化依赖,软件会自动弹窗提示询问是否进行安装,安装成功后:提供 一键安装插件,一键升级,一键删除,查看插件官方网址等功能。
|
||||
- [x] 9: 自动监听已导入的项目路径,路径发生改变,软件自动移除相关项目。
|
||||
- [x] 10: 提供自定义主题功能,支持本地 & 网络 上传手机壁纸,拖动调节 透明度,背景模糊度等。
|
||||
- [x] 11: 可以自定义 `Node.js`解释器安装路径,避免`MacOS`通过`GUI`启动`xx.app`时出现的无法继承用户环境变量。
|
||||
- [x] 12: 可根据喜好在软件中全局设置 你想要的 包管理工具。
|
||||
- [x] 13: 一键勾选 是否 开机自启。
|
||||
- [x] 14: 打开应用自动升级检测,或是 点击检查更新 进行新版本下载。
|
||||
- [x] 15: 增加状态栏顶部 图标,实现 **杀死EasyProject** 时正在被运行的 命令进程 可以被全部关闭。
|
||||
- [x] 16: 实现隐藏应用GUI,只保留顶部状态栏图标,简化操作,随取随用。
|
||||
|
||||
待实现功能:
|
||||
|
||||
- [ ] 1: 支持从`Git` 或 `SVN` 仓库导入项目
|
||||
- [ ] 2: 把当前正在使用项目的所有命令放到`Touchbar上`,可以在`Touchbar`上启停。
|
||||
- [ ] 3: 把项目命令运行时的内存占用,CPU占用显示在顶部菜单栏。
|
||||
- [ ] 4: 修改框架创建窗口的数据存储方式,让升级弹窗基于`BrowserWindow`实现。
|
||||
54
gulpfile.js
Executable file
@@ -0,0 +1,54 @@
|
||||
const gulp = require('gulp')
|
||||
// 编译jade文件为hrml
|
||||
const jade = require('gulp-jade')
|
||||
// gulp 文件监听
|
||||
const watch = require('gulp-watch')
|
||||
// 处理gulp错误,防止程序报错终止
|
||||
const plumber = require('gulp-plumber')
|
||||
const path = require('path')
|
||||
const less = require('gulp-less')
|
||||
|
||||
/**
|
||||
* 编译jade为html
|
||||
*/
|
||||
gulp.task('jade', function () {
|
||||
return gulp.src("./src/application/page/**/*.jade")
|
||||
.pipe(plumber({
|
||||
errHandler: e => {
|
||||
gutil.beep()
|
||||
gutil.log(e)
|
||||
}
|
||||
}))
|
||||
.pipe(jade({
|
||||
pretty: true
|
||||
}))
|
||||
.pipe(gulp.dest("./dist/application/page"))
|
||||
})
|
||||
|
||||
/**
|
||||
* 编译less为css
|
||||
*/
|
||||
gulp.task('less', function () {
|
||||
return gulp.src('src/application/assets/less/**/*.less')
|
||||
.pipe(less())
|
||||
.pipe(gulp.dest('./dist/application/assets/css'))
|
||||
});
|
||||
|
||||
gulp.task('copy', function() {
|
||||
return gulp.src(['src/application/assets/**/*','!src/**/less/**','!src/**/less'])
|
||||
.pipe(gulp.dest('./dist/application/assets'))
|
||||
});
|
||||
|
||||
/**
|
||||
* 监听用户在 src/ 的所有文件操作
|
||||
*/
|
||||
gulp.task('file', function () {
|
||||
gulp.watch(
|
||||
['src/application/page/**/*','src/application/layout/*','src/application/components/*'],
|
||||
gulp.parallel('jade')
|
||||
)
|
||||
gulp.watch('src/application/assets/less/**/*.less', gulp.parallel('less'))
|
||||
gulp.watch('src/application/assets/**/*', gulp.parallel('copy'))
|
||||
});
|
||||
|
||||
gulp.task('default', gulp.series('file'))
|
||||
15
moduleAlias.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const moduleAlias = require('module-alias')
|
||||
|
||||
moduleAlias.addAlias('@', __dirname + '/')
|
||||
moduleAlias.addAlias('@type', __dirname + '/core/types')
|
||||
moduleAlias.addAlias('@run', __dirname + '/core/run')
|
||||
moduleAlias.addAlias('@controller', __dirname + '/core/controller')
|
||||
moduleAlias.addAlias('@utils', __dirname + '/core/utils')
|
||||
moduleAlias.addAlias('@config', __dirname + '/core/config')
|
||||
moduleAlias.addAlias('@model', __dirname + '/core/model')
|
||||
moduleAlias.addAlias('@ipc', __dirname + '/core/ipc')
|
||||
moduleAlias.addAlias('@service', __dirname + '/core/service')
|
||||
moduleAlias.addAlias('@net', __dirname + '/core/net')
|
||||
moduleAlias.addAlias('@annotation', __dirname + '/core/annotation')
|
||||
moduleAlias.addAlias('@interactive', __dirname + '/core/interactive')
|
||||
moduleAlias.addAlias('@img', __dirname + '/view/assets/img')
|
||||
95
package.json
Executable file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"name": "EasyProject",
|
||||
"version": "1.0.4",
|
||||
"description": "一款前端项目管理工具",
|
||||
"main": "dist/App.js",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"tsc -w\" \"gulp\" ",
|
||||
"el": "/Applications/Electron.app/Contents/MacOS/Electron .",
|
||||
"mac": "electron-builder --mac --x64",
|
||||
"win": "electron-builder --win"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.bmy.EasyProject",
|
||||
"productName": "EasyProject",
|
||||
"asar": true,
|
||||
"copyright": "Copyright © 2022 bmy",
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
],
|
||||
"directories": {
|
||||
"output": "./build"
|
||||
},
|
||||
"dmg": {
|
||||
"background": "build/data/back.png",
|
||||
"window": {
|
||||
"width": 514,
|
||||
"height": 409
|
||||
},
|
||||
"contents": [
|
||||
{
|
||||
"x": 410,
|
||||
"y": 190,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
},
|
||||
{
|
||||
"x": 130,
|
||||
"y": 190,
|
||||
"type": "file"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mac": {
|
||||
"icon": "build/data/EasyProject.icns",
|
||||
"hardenedRuntime": true,
|
||||
"gatekeeperAssess": false,
|
||||
"target": [
|
||||
"dmg",
|
||||
"7z"
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"icon": "pic.ico",
|
||||
"target": [
|
||||
"7z"
|
||||
]
|
||||
}
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "bmy",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/deep-diff": "^1.0.0",
|
||||
"@types/jade": "^0.0.30",
|
||||
"@types/lowdb": "^1.0.9",
|
||||
"@types/node": "^17.0.10",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"electron-reload": "^2.0.0-alpha.1",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-jade": "^1.1.0",
|
||||
"gulp-less": "^4.0.1",
|
||||
"gulp-plumber": "^1.2.1",
|
||||
"gulp-watch": "^5.0.1",
|
||||
"gulp-watch-path": "^0.1.0",
|
||||
"electron": "^17.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"deep-diff": "^1.0.2",
|
||||
"execa": "^4.1.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"globby": "^11.0.1",
|
||||
"jade": "^1.11.0",
|
||||
"lowdb": "^1.0.0",
|
||||
"module-alias": "^2.2.2",
|
||||
"moment": "^2.29.1",
|
||||
"monaco-editor": "^0.33.0",
|
||||
"monaco-themes": "^0.4.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"tree-kill": "^1.2.2",
|
||||
"xterm": "^4.9.0",
|
||||
"xterm-addon-web-links": "^0.4.0"
|
||||
}
|
||||
}
|
||||
1
src/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
69cbd5c4-abd0-4e6b-82a5-1d2e269b0d71
|
||||
29
src/App.ts
Executable file
@@ -0,0 +1,29 @@
|
||||
require('./moduleAlias');
|
||||
import { app, dialog } from "electron";
|
||||
import { Run } from "@run/Init.run";
|
||||
import Windows from "@/core/model/Windows.model";
|
||||
import { WhichBin } from "@utils/whichBin.utils"
|
||||
|
||||
app.on('ready', async () => {
|
||||
/**
|
||||
* 解决 Electron 打包发布后,通过GUI点击 xx.app 启动成程序
|
||||
* child_process 无法执行导致应用的 命令 无法启动。主要原因
|
||||
* 是从GUI启动时 $PATH 环境变量错误,这边需要手动合并环境变量。
|
||||
* windows 不会出问题,linux 和 macos 都存在
|
||||
*/
|
||||
if (process.platform != 'win32') {
|
||||
let which = new WhichBin()
|
||||
await which.initEnvPath();
|
||||
}
|
||||
new Run()
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
Windows.CurrentBrowserWindow.show()
|
||||
});
|
||||
1
src/application/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
b79ca5c5-2f7b-4625-9c9f-3afa4987dba8
|
||||
1
src/application/assets/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
df80fe02-b8bf-46f7-a46b-62d5a7133404
|
||||
BIN
src/application/assets/img/+normal@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
1
src/application/assets/img/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
41c66e59-5634-4703-b835-9c99eae570a3
|
||||
BIN
src/application/assets/img/GenericFolder.png
Normal file
|
After Width: | Height: | Size: 576 KiB |
BIN
src/application/assets/img/SidebarAllMyFiles.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/application/assets/img/SidebarDropBoxFolder.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/application/assets/img/SidebarHomeFolder.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/application/assets/img/SidebarSmartFolder.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/application/assets/img/help.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/application/assets/img/logo.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
src/application/assets/img/logoTemplate.png
Normal file
|
After Width: | Height: | Size: 447 B |
BIN
src/application/assets/img/logoTemplate@2x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/application/assets/img/npm.png
Normal file
|
After Width: | Height: | Size: 685 B |
BIN
src/application/assets/img/project.png
Normal file
|
After Width: | Height: | Size: 349 B |
BIN
src/application/assets/img/project_1.png
Executable file
|
After Width: | Height: | Size: 12 KiB |
1
src/application/assets/js/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
23b479ab-9234-45a0-9523-6d163a593114
|
||||
1
src/application/assets/js/Utils/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
af27e24b-3568-4ac6-b7b0-a29d7871be4f
|
||||
69
src/application/assets/js/Utils/index.js
Executable file
@@ -0,0 +1,69 @@
|
||||
const {readFileSync} = require("fs");
|
||||
const {join, resolve} = require("path");
|
||||
const amdLoader = require('monaco-editor/min/vs/loader.js');
|
||||
|
||||
class Utils {
|
||||
|
||||
// 保存 require 对象
|
||||
#amdRequire = amdLoader.require;
|
||||
#module = null;
|
||||
#themeData = null;
|
||||
|
||||
constructor() {
|
||||
// 使用插件 monaco-themes 里面提供的json配置文件
|
||||
// this.#themeData = require("monaco-themes/themes/GitHub.json")
|
||||
this.#amdRequire.config({
|
||||
baseUrl: this.uriFromPath(join(__dirname, '../../../../../node_modules/monaco-editor/min'))
|
||||
});
|
||||
|
||||
// 设置中文语言
|
||||
this.#amdRequire.config({
|
||||
'vs/nls': { availableLanguages: {'*':'zh-cn'}}
|
||||
});
|
||||
}
|
||||
|
||||
uriFromPath(_path) {
|
||||
var pathName = resolve(_path).replace(/\\/g, '/');
|
||||
if (pathName.length > 0 && pathName.charAt(0) !== '/') {
|
||||
pathName = '/' + pathName;
|
||||
}
|
||||
return encodeURI('file://' + pathName);
|
||||
}
|
||||
|
||||
Html(path) {
|
||||
return readFileSync(join(__dirname, `../../../page${path}`)).toString("utf-8")
|
||||
}
|
||||
|
||||
initVscodeEdit() {
|
||||
return new Promise((success, error) => {
|
||||
this.module = undefined;
|
||||
this.#amdRequire(['vs/editor/editor.main'], () => {
|
||||
// 自定义主题
|
||||
// monaco.editor.defineTheme('mytheme', this.#themeData);
|
||||
// monaco.editor.setTheme('mytheme');
|
||||
let el = document.querySelector(".package");
|
||||
|
||||
// 当切换tabs的时候清空上一个项目的package.json配置
|
||||
el.innerHTML = ""
|
||||
var editor = monaco.editor.create(
|
||||
el,
|
||||
{
|
||||
value: '',
|
||||
language: 'json',
|
||||
autoIndent: true,
|
||||
formatOnPaste: true,
|
||||
formatOnType: true,
|
||||
tabSize: 2,
|
||||
fontSize: "16px",
|
||||
scrollBeyondLastColumn: 2,
|
||||
automaticLayout: true
|
||||
}
|
||||
);
|
||||
success(editor)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new Utils();
|
||||
1
src/application/assets/js/config/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
adb86b38-623e-4625-a2ae-cff5b2b01c72
|
||||
7
src/application/assets/js/config/index.js
Executable file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
env: [
|
||||
{
|
||||
name: 'Utils', path: '../../assets/js/Utils/'
|
||||
}
|
||||
]
|
||||
}
|
||||
1
src/application/assets/js/lib/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
94f4d244-3df7-4561-a854-4d2405a0f8ae
|
||||
3
src/application/assets/js/lib/axios.js
Executable file
BIN
src/application/assets/js/lib/element-icons.ttf
Normal file
BIN
src/application/assets/js/lib/element-icons.woff
Normal file
1
src/application/assets/js/lib/font.js
Normal file
62
src/application/assets/js/lib/http.js
Executable file
@@ -0,0 +1,62 @@
|
||||
class Utils {
|
||||
/**
|
||||
* 发送GET 请求
|
||||
* @param URL 地址
|
||||
* @param params 参数
|
||||
* @param callback 回调
|
||||
* @constructor
|
||||
*/
|
||||
Http(URL, params,callback) {
|
||||
$.ajax({
|
||||
url: `http://www.bmycode.com:3000/api${URL}`,
|
||||
type: 'GET',
|
||||
data: params,
|
||||
success: (res)=> {
|
||||
callback(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据时间戳返回多久之前的时间
|
||||
* @param timespan 时间戳
|
||||
* @returns {string}
|
||||
*/
|
||||
formatTime (timespan) {
|
||||
var dateTime = new Date(timespan);
|
||||
var year = dateTime.getFullYear();
|
||||
var month = dateTime.getMonth() + 1;
|
||||
var day = dateTime.getDate();
|
||||
var hour = dateTime.getHours();
|
||||
var minute = dateTime.getMinutes();
|
||||
var second = dateTime.getSeconds();
|
||||
var now = new Date();
|
||||
var now_new = Date.parse(now.toDateString()); //typescript转换写法
|
||||
|
||||
var milliseconds = 0;
|
||||
var timeSpanStr;
|
||||
|
||||
milliseconds = now_new - timespan;
|
||||
|
||||
if (milliseconds <= 1000 * 60 * 1) {
|
||||
timeSpanStr = '刚刚';
|
||||
}
|
||||
else if (1000 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60) {
|
||||
timeSpanStr = Math.round((milliseconds / (1000 * 60))) + '分钟前';
|
||||
}
|
||||
else if (1000 * 60 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24) {
|
||||
timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60)) + '小时前';
|
||||
}
|
||||
else if (1000 * 60 * 60 * 24 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24 * 15) {
|
||||
timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60 * 24)) + '天前';
|
||||
}
|
||||
else if (milliseconds > 1000 * 60 * 60 * 24 * 15 && year == now.getFullYear()) {
|
||||
timeSpanStr = month + '月' + day + '号' + hour + ':' + minute;
|
||||
} else {
|
||||
timeSpanStr = year + '年' + month + '月' + day + '号' + hour + ':' + minute;
|
||||
}
|
||||
return timeSpanStr;
|
||||
};
|
||||
}
|
||||
|
||||
window.$Utils = new Utils()
|
||||
1
src/application/assets/js/lib/index.css
Normal file
1
src/application/assets/js/lib/index.js
Normal file
3074
src/application/assets/js/lib/vue-router.js
Executable file
6
src/application/assets/js/lib/vue.js
Executable file
1250
src/application/assets/js/lib/vuex.js
Normal file
1
src/application/assets/js/page/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
839a8d36-2a7b-4fef-b510-4e4e02728428
|
||||
336
src/application/assets/js/page/Home.js
Executable file
@@ -0,0 +1,336 @@
|
||||
const {ipcRenderer} = require("electron");
|
||||
const router = require("../../assets/js/router/index.js");
|
||||
const store = require("../../assets/js/store/index.js");
|
||||
const config = require("../../assets/js/config/index.js");
|
||||
|
||||
let Home = new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
store,
|
||||
data: {
|
||||
// 左侧菜单的状态 false 非全屏, true 全屏
|
||||
menuStatus: false,
|
||||
menuIconRotate: 180,
|
||||
// 控制重命名项目弹窗是否显示
|
||||
isEditDialog: false,
|
||||
// 项目图标选择
|
||||
isIconDialog: false,
|
||||
|
||||
// 选择主题的下标
|
||||
selectThemeIndex: 0,
|
||||
|
||||
// 你点击的项目下标
|
||||
clickProjectIndex: -1,
|
||||
|
||||
// 保存 重命名项目 的输入值
|
||||
EditProjectName: "",
|
||||
// 操作的当前项目 就是 name 名称
|
||||
CurrentProjectName: "",
|
||||
|
||||
// 保存用户选择的图片类名
|
||||
ProjectIcon: "",
|
||||
ProjectListData: [],
|
||||
IconList: [
|
||||
{icon: '#icon-vue', title: 'Vue'},
|
||||
{icon: '#icon-react', title: 'React'},
|
||||
{icon: '#icon-angular', title: 'Angular'},
|
||||
{icon: '#icon-typescript', title: 'TypeScript'},
|
||||
{icon: '#icon-nodejs', title: 'Nodejs'},
|
||||
{icon: '#icon-npm1', title: 'Npm'},
|
||||
{icon: '#icon-console', title: 'Cli'},
|
||||
{icon: '#icon-gulp', title: 'Gulp'},
|
||||
{icon: '#icon-graphql', title: 'Graphql'},
|
||||
{icon: '#icon-webpack', title: 'Webpack'},
|
||||
{icon: '#icon-nuxt', title: 'Nuxt'},
|
||||
{icon: '#icon-nest', title: 'Nest'},
|
||||
],
|
||||
MoreList: [
|
||||
{title: '修改图标', icon: 'el-icon-magic-stick', id: 'editIcon'},
|
||||
{title: '重命名项目', icon: 'el-icon-edit', id: 'rename'},
|
||||
{title: '移除项目', icon: 'el-icon-delete', id: 'delete'},
|
||||
]
|
||||
},
|
||||
created() {
|
||||
// 调用升级检测
|
||||
this.onMainWebContents();
|
||||
this.GetProjectList();
|
||||
},
|
||||
mounted() {
|
||||
// this.$store.dispatch('checkUpdate','default')
|
||||
},
|
||||
methods: {
|
||||
downNowDmg(url) {
|
||||
ipcRenderer.sendSync('openExternal', {
|
||||
action: '浏览器打开',
|
||||
url: url,
|
||||
});
|
||||
},
|
||||
// 帮设置页面提前获取当前应用版本号
|
||||
changeMenu() {
|
||||
if (this.menuStatus) {
|
||||
this.menuStatus = false
|
||||
} else {
|
||||
this.menuStatus = true
|
||||
}
|
||||
},
|
||||
|
||||
Mouseover(e) {
|
||||
console.log("全屏按钮被悬浮")
|
||||
if (this.menuStatus) {
|
||||
this.menuIconRotate = 0
|
||||
} else {
|
||||
this.menuIconRotate = 180
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 监听主进程发过来的消息
|
||||
*/
|
||||
onMainWebContents() {
|
||||
|
||||
// 主进程发送过来的消息,打开设置页面
|
||||
ipcRenderer.on('openSetting', (event, message) => {
|
||||
this.$router.push("/setting")
|
||||
})
|
||||
|
||||
// 主进程发送过来的消息,在 应用内打开
|
||||
ipcRenderer.on('openInfo', (event, arg) => {
|
||||
console.log("应用内打开: ", arg)
|
||||
this.openInfo(arg.item, arg.index)
|
||||
})
|
||||
|
||||
// 文件被删除
|
||||
ipcRenderer.on('DirRemove', (event, arg) => {
|
||||
console.log("检测到您的文件路径发生变化: ", arg)
|
||||
ipcRenderer.send('removeListProject', {
|
||||
action: '从列表移除',
|
||||
name: arg.name,
|
||||
});
|
||||
this.removeProjectItem("autoWatch", arg.FolderName);
|
||||
// this.$confirm(`该路径下文件:${arg.Fullpath} 发生变化,即将移除项目!`, '提示', {
|
||||
// confirmButtonText: '确定',
|
||||
// cancelButtonText: '取消',
|
||||
// type: 'error'
|
||||
// }).then(() => {
|
||||
//
|
||||
// }).catch(() => {
|
||||
// });
|
||||
})
|
||||
|
||||
// 使用 touchbar导入项目
|
||||
ipcRenderer.on('TouchBarImportProject', (event, message) => {
|
||||
if (message != undefined) {
|
||||
this.$store.commit('SET_LIST', {
|
||||
res: message,
|
||||
type: 'ChoiceFile'
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
let result = ipcRenderer.sendSync('getVersion', {
|
||||
action: '获取设置页面的当前版本号'
|
||||
});
|
||||
this.$store.commit('set_vsersion', result.version);
|
||||
|
||||
let res = ipcRenderer.sendSync('getSettingConfig', {
|
||||
action: '获取设置页面配置参数'
|
||||
});
|
||||
|
||||
// 把从json数据库中获取到的配置向 vuex 中合并
|
||||
this.$store.replaceState(Object.assign({}, this.$store.state, res))
|
||||
console.log("设置的参数:", this.$store.state)
|
||||
|
||||
},
|
||||
openInfo(v, i) {
|
||||
this.clickProjectIndex = i;
|
||||
this.$router.push({
|
||||
name: 'info',
|
||||
query: {
|
||||
name: v.FolderName,
|
||||
active: v.CurrentTabs,
|
||||
isNeedWatch: true
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 项目列表 悬浮下拉项被点击
|
||||
* @constructor
|
||||
*/
|
||||
ShowActionClick(item) {
|
||||
console.log("item: ", item)
|
||||
// 保存当前操作的项目文件夹名称
|
||||
this.CurrentProjectName = item[0]
|
||||
|
||||
switch (item[1]) {
|
||||
case "delete": // 移除项目
|
||||
this.$confirm('【从磁盘删除】将会从电脑上删除您的项目,【从列表移除】将从项目列表删除选中的项目。', '移除项目', {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: '从列表移除',
|
||||
cancelButtonText: '从磁盘删除',
|
||||
roundButton: true,
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
ipcRenderer.send('removeListProject', {
|
||||
action: '从列表移除',
|
||||
name: this.CurrentProjectName,
|
||||
});
|
||||
this.removeProjectItem();
|
||||
}).catch((action) => {
|
||||
if (action == "cancel") {
|
||||
this.$confirm('项目将会被删除到【废纸篓】,确定继续?', '删除提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
let status = ipcRenderer.sendSync('removeDistProject', {
|
||||
action: '从磁盘删除',
|
||||
name: this.CurrentProjectName,
|
||||
});
|
||||
if (status == "success") {
|
||||
this.$notify({title: '提示', message: '项目删除成功', type: 'success'});
|
||||
} else {
|
||||
this.$notify.error({title: '提示', message: status, type: 'error'});
|
||||
}
|
||||
this.removeProjectItem();
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "rename": // 重命名项目
|
||||
this.isEditDialog = true;
|
||||
break;
|
||||
case "editIcon": // 修改图标
|
||||
this.isIconDialog = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 从列表删除 和 从 磁盘删除,需要减少列表数据
|
||||
* @param type
|
||||
* default 表示从 列表 和 磁盘进行删除
|
||||
* autoWatch 表示检测到的自动删除
|
||||
* @param condition autoWatch 的筛选条件
|
||||
*/
|
||||
removeProjectItem(type = "default", condition) {
|
||||
// 从原数组中去除你要删除的
|
||||
let list = this.$store.getters.GET_PROJECT.filter((v, i) => {
|
||||
if (type == "default") {
|
||||
if (i != this.clickProjectIndex) {
|
||||
return v;
|
||||
}
|
||||
} else {
|
||||
if (v.FolderName != condition) {
|
||||
return v;
|
||||
} else {
|
||||
// 将你当前移动文件位置的项目路径保存起来,为下面自动激活做准备
|
||||
this.clickProjectIndex = i
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$store.commit('SET_LIST', {
|
||||
res: list,
|
||||
type: "ProjectList"
|
||||
})
|
||||
|
||||
// 当前项目被你删除,自动跳转激活到上一个项目
|
||||
// 处理越界
|
||||
let index = this.clickProjectIndex - 1;
|
||||
index < 0 ? index = 0 : index - 1
|
||||
|
||||
// 如果所有项目都被删完了,调回 main 主页
|
||||
list.length == 0
|
||||
? this.$router.replace("/main")
|
||||
: this.openInfo(
|
||||
this.$store.getters.GET_PROJECT[index],
|
||||
index
|
||||
)
|
||||
},
|
||||
|
||||
/**
|
||||
* 选择图标
|
||||
* @param item
|
||||
* @constructor
|
||||
*/
|
||||
SelectIcon(item) {
|
||||
this.$store.getters.GET_PROJECT[this.clickProjectIndex].FolderIcon = item.icon
|
||||
let status = ipcRenderer.sendSync('ChangeIcon', {
|
||||
action: '修改项目图标',
|
||||
name: this.CurrentProjectName,
|
||||
newIcon: item.icon
|
||||
});
|
||||
|
||||
if (status == "success") {
|
||||
this.isIconDialog = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 重命名项目
|
||||
* @constructor
|
||||
*/
|
||||
SubmitEdit() {
|
||||
if (this.EditProjectName.length != 0) {
|
||||
this.$store.getters.GET_PROJECT[this.clickProjectIndex].name = this.EditProjectName
|
||||
let status = ipcRenderer.sendSync('reNameProject', {
|
||||
action: '重命名项目',
|
||||
oldName: this.CurrentProjectName,
|
||||
newName: this.EditProjectName
|
||||
});
|
||||
if (status == "success") {
|
||||
this.EditProjectName = ""
|
||||
this.isEditDialog = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
OpenPropject(type, item) {
|
||||
console.log(item)
|
||||
ipcRenderer.sendSync('OpenProject', {
|
||||
action: '选择项目',
|
||||
type: type,
|
||||
path: item.Fullpath
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 添加项目 按钮被点击,弹窗选择文件
|
||||
* @constructor
|
||||
*/
|
||||
ChoiceFile() {
|
||||
let list = ipcRenderer.sendSync('openDirectory', {
|
||||
action: '选择项目'
|
||||
})
|
||||
if (list != undefined) {
|
||||
console.log("导入项目:", list)
|
||||
this.$store.commit('SET_LIST', {
|
||||
res: list,
|
||||
type: 'ChoiceFile'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取项目列表
|
||||
* @constructor
|
||||
*/
|
||||
GetProjectList() {
|
||||
let list = ipcRenderer.sendSync('ProjectList', {
|
||||
action: '获取默认列表'
|
||||
});
|
||||
console.log("GetProjectList ==== ", list)
|
||||
// 如果没有项目列表了,那么就不需要展示项目详情页面
|
||||
list.length == 0 ? this.$router.push("/") : ""
|
||||
// 请求到的数据存入vuex中
|
||||
this.$store.commit('SET_LIST', {
|
||||
res: list,
|
||||
type: "ProjectList"
|
||||
})
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
key() {
|
||||
return this.$route.name ? this.$route.name + +new Date() : this.$route + +new Date()
|
||||
}
|
||||
}
|
||||
});
|
||||
12
src/application/assets/js/page/Main.js
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
10
src/application/assets/js/page/PlugInfo.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const { ipcRenderer } = require('electron');
|
||||
let PlugInfo = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '测试'
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
});
|
||||
610
src/application/assets/js/page/ProjectInfo.js
Executable file
@@ -0,0 +1,610 @@
|
||||
const {ipcRenderer, shell} = require("electron");
|
||||
const {Terminal} = require("xterm");
|
||||
const {WebLinksAddon} = require("xterm-addon-web-links");
|
||||
const Utils = require("../Utils/index.js");
|
||||
const {ProcessUtils} = require("../../../../core/utils/process.utils");
|
||||
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
isNeedWatch: true,
|
||||
isShowError: false, // 是否显示错误弹窗
|
||||
ErrorText: '', // 错误具体信息
|
||||
activeName: '',
|
||||
// 保存的就是 FolderName
|
||||
projectName: '',
|
||||
isRunScript: false, // 是否启动过命令
|
||||
titleTips: '默认显示 [任务] 中展开的命令', // 提示文字
|
||||
currentSrcipt: '', // 保存当前你点击过的折叠面板,也就是 ScriptName
|
||||
isShowInstallDev: false, // 安装依赖弹窗是否显示
|
||||
Environment: '', // 你选择安装插件的环境
|
||||
PlugInput: '', // 用户输入的插件名称
|
||||
currentProcess: [], // 当前命令的进程占用
|
||||
PlugList: [],
|
||||
TerminalConfig: {
|
||||
scrollback: 5000,
|
||||
disableStdin: true,
|
||||
useFlowControl: true,
|
||||
cols: 80,
|
||||
rows: 16,
|
||||
convertEol: true,
|
||||
theme: {
|
||||
background: '#1d2935',
|
||||
cursor: 'rgba(255, 255, 255, .4)',
|
||||
selection: 'rgba(255, 255, 255, 0.3)',
|
||||
magenta: '#e83030',
|
||||
brightMagenta: '#e83030',
|
||||
black: "#000000",
|
||||
red: "#e83030",
|
||||
brightRed: "#e83030",
|
||||
green: "#42b983",
|
||||
brightGreen: "#42b983",
|
||||
brightYellow: "#ea6e00",
|
||||
yellow: "#ea6e00",
|
||||
cyan: "#03c2e6",
|
||||
brightBlue: "#03c2e6",
|
||||
brightCyan: "#03c2e6",
|
||||
blue: "#03c2e6",
|
||||
white: "#d0d0d0",
|
||||
brightBlack: "#808080",
|
||||
brightWhite: "#ffffff",
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.GetShellMessage();
|
||||
this.reDependentSuccess();
|
||||
this.onClose();
|
||||
this.projectName = this.$route.query.name;
|
||||
this.activeName = this.$route.query.active;
|
||||
this.isNeedWatch = this.$route.query.isNeedWatch;
|
||||
this.SwitcTabs(this.activeName)
|
||||
},
|
||||
watch: {
|
||||
$route: async function (to, from) {
|
||||
console.log("1 watch监听的$route为: ", this.$route)
|
||||
|
||||
this.projectName = this.$route.query.name;
|
||||
this.activeName = this.$route.query.active;
|
||||
this.isNeedWatch = this.$route.query.isNeedWatch;
|
||||
|
||||
// 如果进入页面是 home(配置)的话就获取最新的package.json
|
||||
if (this.activeName == "home") {
|
||||
this.SwitcTabs(this.$route.query.active)
|
||||
}
|
||||
|
||||
if (this.isNeedWatch) {
|
||||
this.currentSrcipt = ""
|
||||
}
|
||||
|
||||
// 如果进入tabs的 task 任务栏
|
||||
// 1:从其他项目切换回来,RunLogs 不为空但是 Terminal 所对应的html被刷没了
|
||||
// 这时候 this.isHasTerminal() 返回 false,所以重新初始化终端创建html并载入日志
|
||||
// 2:在当前项目中切换tab,因为页面被 keep-alive 缓存,所以Terminal 的 html还在
|
||||
// 这时候 this.isHasTerminal() 返回 true,所以不需要重新创建终端
|
||||
if (this.activeName == "task") {
|
||||
let current = this.CurrentTask(this.currentSrcipt);
|
||||
if (current && current.Terminal != null &&
|
||||
!(await this.isHasTerminal())) {
|
||||
console.warn("2: watch 创建终端")
|
||||
// 为默认展开的 折叠面板 重新创建终端写入之前的日志
|
||||
if (this.currentSrcipt == current.ScriptName) {
|
||||
current.Terminal = null
|
||||
console.warn("3: 为默认展开的 折叠面板 创建终端")
|
||||
this.newTerminal(current)
|
||||
this.setContent(current.Terminal, current.RunLogs)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
||||
methods: {
|
||||
ShowSetting() {
|
||||
let res = this.CurrentTask(this.currentSrcipt);
|
||||
if (res != null && res.pid != 0) {
|
||||
console.log("进程的pid进程: ", res)
|
||||
let process = ipcRenderer.sendSync('getProcess', {
|
||||
action: '通过pid获取进程的CPU,内存占用',
|
||||
pid: res.pid
|
||||
});
|
||||
|
||||
console.log("进程信息为:", process)
|
||||
|
||||
if (process.hasOwnProperty("isRunScript")) {
|
||||
this.titleTips = process.message;
|
||||
this.isRunScript = false;
|
||||
this.$forceUpdate();
|
||||
} else {
|
||||
this.currentProcess = [process];
|
||||
this.isRunScript = true;
|
||||
}
|
||||
} else {
|
||||
this.isRunScript = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 展示package.json 配置文件
|
||||
* @returns {Promise<void>}
|
||||
* @constructor
|
||||
*/
|
||||
async ShowPackageJson() {
|
||||
this.$nextTick(async () => {
|
||||
// 避免每次点击回 配置 时候,重复初始化创建编辑器导致删除的时候多删文字的问题
|
||||
let el = document.querySelector(`.package`);
|
||||
if (el.children.length == 0) {
|
||||
let JsonCode = ipcRenderer.sendSync('getPackageJson', {
|
||||
action: '获取项目的配置文件',
|
||||
project: this.projectName
|
||||
});
|
||||
console.log("读取到的 package.json文件代码为 : ", JsonCode)
|
||||
let editor = await Utils.initVscodeEdit(this.projectName);
|
||||
editor.setValue(JSON.stringify(JsonCode))
|
||||
// 格式化json代码
|
||||
setTimeout(() => editor.getAction('editor.action.formatDocument').run(), 300)
|
||||
// 监听文件修改,实时获取代码,传给 主进程 来写到对应项目的package.json中
|
||||
editor.onDidChangeModelContent((e) => {
|
||||
// console.log(editor.getValue())
|
||||
ipcRenderer.send('setPackageJson', {
|
||||
action: '保存前端修改的配置文件',
|
||||
project: this.projectName,
|
||||
code: editor.getValue()
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 安装插件
|
||||
* @constructor
|
||||
*/
|
||||
async SearchList() {
|
||||
if (this.PlugInput.length != 0 && this.Environment.length != 0) {
|
||||
|
||||
this.$store.dispatch('loading', "安装插件很慢,请耐心等待...")
|
||||
ipcRenderer.send('installPlug', {
|
||||
action: '安装插件',
|
||||
project: this.projectName,
|
||||
name: this.PlugInput,
|
||||
status: this.Environment
|
||||
});
|
||||
this.isShowInstallDev = false
|
||||
|
||||
} else {
|
||||
this.$notify.error({
|
||||
title: '提示',
|
||||
message: '插件名和安装环境都不能为空'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断tabs点击类型
|
||||
* @param active
|
||||
* @constructor
|
||||
*/
|
||||
SwitcTabs(active) {
|
||||
console.log("你点击的tabs类型: ", active)
|
||||
switch (active) {
|
||||
case 'home':
|
||||
this.ShowPackageJson()
|
||||
break;
|
||||
case 'task':
|
||||
this.GetTaskListData()
|
||||
break;
|
||||
case 'dependent':
|
||||
this.GetDependentListData()
|
||||
break;
|
||||
case 'outline':
|
||||
console.log("概要...")
|
||||
// this.ShowSetting()
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 在你点击的命令运行前,先初始化终端
|
||||
*/
|
||||
initTerminal(item) {
|
||||
// 如果没有运行过终端,那么创建,避免重复创建
|
||||
if (item.Terminal == null) {
|
||||
this.newTerminal(item)
|
||||
}
|
||||
},
|
||||
|
||||
newTerminal(item) {
|
||||
item.Terminal = new Terminal(this.TerminalConfig);
|
||||
// 终端链接被点击时候,打开链接
|
||||
item.Terminal.loadAddon(new WebLinksAddon((event, uri) => {
|
||||
this.WebSite(uri)
|
||||
}));
|
||||
let refKey = this.projectName + "_" + item.ScriptName;
|
||||
this.$nextTick(() => {
|
||||
item.Terminal.open(this.$refs[refKey][0]);
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 命令运行后,nodejs 会通过 sendMessage 实时输出日志,
|
||||
* @constructor
|
||||
*/
|
||||
GetShellMessage() {
|
||||
let setLogCallback = (event, arg) => {
|
||||
let e = this.$store.getters.GET_CURRENTTASK(arg.projectName)
|
||||
let curr = e.filter(v => {
|
||||
if (v.ScriptName == arg.ScriptName) {
|
||||
return v;
|
||||
}
|
||||
});
|
||||
let res = curr[0]
|
||||
res.RunLogs += arg.log;
|
||||
res.pid = arg.pid;
|
||||
this.setContent(res.Terminal, arg.log)
|
||||
}
|
||||
ipcRenderer.on('sendMessage', setLogCallback);
|
||||
this.removeListener('sendMessage', setLogCallback)
|
||||
},
|
||||
|
||||
// 监听进程关闭的通知
|
||||
onClose() {
|
||||
let closeCallback = async (event, arg) => {
|
||||
let e = this.$store.getters.GET_CURRENTTASK(arg.projectName)
|
||||
console.log("***监听进程关闭的通知****: ", e)
|
||||
let curr = e.filter(v => {
|
||||
if (v.ScriptName == arg.ScriptName) {
|
||||
return v;
|
||||
}
|
||||
});
|
||||
let res = curr[0]
|
||||
res.lock = true;
|
||||
res.pid = 0;
|
||||
res.IsRuning = "idle";
|
||||
res.RunLogs += "命令已结束...";
|
||||
res.Terminal.writeln(`命令已结束..`)
|
||||
res.Terminal.writeln(`运行时间: ${arg.time}s..`)
|
||||
}
|
||||
ipcRenderer.on('close', closeCallback);
|
||||
this.removeListener('close', closeCallback)
|
||||
},
|
||||
|
||||
/**
|
||||
* ipcRenderer.on 使用的是 EventEmitter
|
||||
* Nodejs 的 EventEmitter.on 没有做重复检查,所以会出现 进入设置页面,再返回的时候
|
||||
* ipcRenderer.on 多次监听导致命令运行+停止的日志出现重复。
|
||||
* 所以这里需要对 on 的次数进行判断,多了则删除。
|
||||
* @param channel 事件名
|
||||
* @param listener 回调函数
|
||||
*/
|
||||
removeListener(channel, listener) {
|
||||
let count = ipcRenderer.listenerCount(channel);
|
||||
if (count > 1) {
|
||||
ipcRenderer.removeListener(channel, listener)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止命令运行
|
||||
* @param item
|
||||
* @constructor
|
||||
*/
|
||||
StopCmd(item) {
|
||||
item.lock = false;
|
||||
let StopStatus = ipcRenderer.send('StopCmd', {
|
||||
action: '运行项目',
|
||||
name: this.projectName,
|
||||
ScriptName: item.ScriptName
|
||||
});
|
||||
|
||||
if (StopStatus != null) {
|
||||
this.$notify.error({
|
||||
title: '命令执行失败',
|
||||
message: StopStatus
|
||||
});
|
||||
} else {
|
||||
item.IsRuning = "idle";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 运行命令
|
||||
* @param key
|
||||
* @constructor
|
||||
*/
|
||||
RunCmd(item) {
|
||||
if (item.lock) {
|
||||
item.IsRuning = "runing";
|
||||
this.initTerminal(item);
|
||||
ipcRenderer.send('RunCmd', {
|
||||
action: '运行项目',
|
||||
name: this.projectName,
|
||||
ScriptName: item.ScriptName
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 格式化日志
|
||||
* @param term
|
||||
* @param value
|
||||
* @param ln
|
||||
*/
|
||||
setContent(term, value, ln = true) {
|
||||
if (value != undefined && value.indexOf('\n') !== -1) {
|
||||
value.split('\n').forEach(
|
||||
t => this.setContent(term, t)
|
||||
)
|
||||
return
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
term[ln ? 'writeln' : 'write'](value)
|
||||
} else {
|
||||
term.writeln('')
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除日志
|
||||
* @param v
|
||||
*/
|
||||
clearLog(v) {
|
||||
v.Terminal.clear()
|
||||
v.RunLogs = ""
|
||||
},
|
||||
|
||||
/**
|
||||
* 滚动到底部
|
||||
* @param v
|
||||
* @constructor
|
||||
*/
|
||||
ToBottom(v) {
|
||||
v.Terminal.scrollToBottom()
|
||||
},
|
||||
|
||||
/**
|
||||
* 复制日志
|
||||
* @param v
|
||||
*/
|
||||
copyContent(v) {
|
||||
const textarea = v.Terminal.textarea
|
||||
const textValue = textarea.value
|
||||
const emptySelection = !v.Terminal.hasSelection()
|
||||
try {
|
||||
if (emptySelection) {
|
||||
v.Terminal.selectAll()
|
||||
}
|
||||
var selection = v.Terminal.getSelection()
|
||||
textarea.value = selection
|
||||
textarea.select()
|
||||
document.execCommand('copy')
|
||||
this.$notify({
|
||||
title: '提示',
|
||||
message: '日志复制成功',
|
||||
type: 'success'
|
||||
});
|
||||
} finally {
|
||||
textarea.value = textValue
|
||||
if (emptySelection) {
|
||||
v.Terminal.clearSelection()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开依赖的详情页
|
||||
* @constructor
|
||||
*/
|
||||
PlugsInfo(item) {
|
||||
// ipcRenderer.send('openWindow', {
|
||||
// action: 'Home.controller/PlugInfo',
|
||||
// parent: true,
|
||||
// data: item
|
||||
// });
|
||||
console.log("item :", item)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除依赖包
|
||||
* @param va
|
||||
* @param type
|
||||
* @param PackName
|
||||
*/
|
||||
deletePack(va, type, PackName) {
|
||||
this.$confirm(`确定删除依赖:${PackName}吗?删除后,请修改项目代码!`, '删除提示', {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$store.dispatch('loading', "删除依赖很慢,请耐心等待...")
|
||||
va.class = type
|
||||
ipcRenderer.send('dDependencies', {
|
||||
action: '删除依赖',
|
||||
type: va,
|
||||
project: this.projectName
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
// type == 0 生产环境依赖, == 1 开发环境依赖
|
||||
/**
|
||||
* 更新依赖包
|
||||
* @param type
|
||||
* @param PackName
|
||||
*/
|
||||
updatePack(type, PackName) {
|
||||
this.$store.dispatch('loading', "更新依赖很慢,请耐心等待...")
|
||||
ipcRenderer.send('uDependencies', {
|
||||
action: '更新依赖',
|
||||
type: type,
|
||||
name: PackName,
|
||||
project: this.projectName
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开依赖包主页
|
||||
* @param url
|
||||
* @constructor
|
||||
*/
|
||||
WebSite(url) {
|
||||
ipcRenderer.sendSync('openExternal', {
|
||||
action: '浏览器打开',
|
||||
url: url,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取启动命令
|
||||
* @constructor
|
||||
*/
|
||||
GetTaskListData() {
|
||||
if (this.CurrentTaskList.length == 0) {
|
||||
let a = ipcRenderer.sendSync('GetTaskList', {
|
||||
action: '获取项目的运行命令',
|
||||
name: this.projectName
|
||||
});
|
||||
console.log("项目的运行命令:", a)
|
||||
this.$store.commit('SET_TASKRUNLIST', {
|
||||
id: this.projectName,
|
||||
TaskRunList: a
|
||||
});
|
||||
console.log("ProjectList: ", this.$store.state.ProjectList)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取项目的依赖信息
|
||||
* @constructor
|
||||
*/
|
||||
GetDependentListData() {
|
||||
let status = ipcRenderer.sendSync('GetDependentList', {
|
||||
action: '依赖信息',
|
||||
name: this.projectName
|
||||
});
|
||||
|
||||
if (status == undefined) {
|
||||
this.$confirm(`项目的 node_modules 缺失部分依赖,点击确定重新安装依赖!`, '依赖缺失', {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
this.$store.dispatch('loading', "安装依赖很慢,请耐心等待...")
|
||||
ipcRenderer.send('reDependent', {
|
||||
action: '更新项目',
|
||||
name: this.projectName
|
||||
});
|
||||
})
|
||||
} else {
|
||||
this.$store.commit('SET_DEPENDENTLIST', {
|
||||
id: this.projectName,
|
||||
DependentList: status
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 重新安装依赖完成,后端发送的回调
|
||||
*/
|
||||
reDependentSuccess() {
|
||||
let success = (event, arg) => {
|
||||
this.$store.state.loadingService.close();
|
||||
if (arg == "ok") {
|
||||
this.GetDependentListData();
|
||||
} else {
|
||||
this.isShowError = true;
|
||||
this.ErrorText = arg.message
|
||||
}
|
||||
}
|
||||
ipcRenderer.on('reDependentSuccess', success)
|
||||
this.removeListener('reDependentSuccess', success)
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 顶部tabs点击事件
|
||||
* @param v
|
||||
*/
|
||||
handleClick(v) {
|
||||
console.log("handleClick 顶部tabs点击事件: ", v)
|
||||
this.isNeedWatch = false;
|
||||
|
||||
// 切换tabs的时候,将这个项目用户打开的对应tabs name 保存到 ProjectList 中
|
||||
this.$store.commit('SET_CURRENTTABS', {id: this.projectName, currentTabs: this.activeName});
|
||||
this.$router.push({query: {name: this.projectName, active: this.activeName, isNeedWatch: true}})
|
||||
this.SwitcTabs(v.name)
|
||||
},
|
||||
|
||||
/**
|
||||
* 折叠面板被激活时候触发
|
||||
* @param activeNames 折叠面板被点击 serve_2
|
||||
* 解释:serve 是这个项目所有命令的名称
|
||||
* 2 是这个命令在数组中的下标
|
||||
*/
|
||||
async collapseChange(activeNames) {
|
||||
console.log("1 折叠面板被激活时候触发: ", activeNames)
|
||||
// !=0 表示你是展开折叠面板
|
||||
if (activeNames.length != 0) {
|
||||
let d = activeNames.split("_")
|
||||
console.log("2 折叠面板被激活时候触发 d : ", d)
|
||||
|
||||
// 获取你点击的命令
|
||||
this.currentSrcipt = d[0];
|
||||
// 数组加下标获取到数组中对应的那个命令对象
|
||||
let cTask = this.CurrentTaskList[d[1]]
|
||||
console.log("3 折叠面板被激活时候触发 cTask : ", cTask)
|
||||
// 你点击折叠面板标题的时候,获取 折叠面板下终端的html
|
||||
// 如果 你点击的 折叠面板 表示曾经运行过了终端
|
||||
// 并且 折叠面板 对应的 终端 没有html(出现这种情况的原因是因为,你切换了项目导致的)
|
||||
if (
|
||||
cTask.Terminal != null &&
|
||||
!(await this.isHasTerminal())
|
||||
) {
|
||||
console.log("4 折叠面板被激活时候触发 cTask.Terminal != null : ", cTask)
|
||||
cTask.Terminal = null
|
||||
this.newTerminal(cTask)
|
||||
this.setContent(cTask.Terminal, cTask.RunLogs)
|
||||
}
|
||||
} else {
|
||||
this.currentSrcipt = activeNames
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// 判断是否有已经终端
|
||||
async isHasTerminal() {
|
||||
await this.$nextTick()
|
||||
let el = document.querySelector(`.${this.projectName}_${this.currentSrcipt}`);
|
||||
// 返回 true 表示有终端,false 表示没有
|
||||
return el && el.children.length == 0 ? false : true
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
||||
// 返回当前项目中的所有script运行命令
|
||||
CurrentTaskList() {
|
||||
return this.$store.getters.GET_CURRENTTASK(this.projectName)
|
||||
},
|
||||
// 返回当前你打开的命令
|
||||
CurrentTask() {
|
||||
return ScriptName => {
|
||||
console.log("返回当前你打开的命令 ScriptName: ", ScriptName)
|
||||
let curr = this.CurrentTaskList.filter(v => {
|
||||
if (v.ScriptName == ScriptName) {
|
||||
return v;
|
||||
}
|
||||
});
|
||||
console.log("curr: [] ", curr)
|
||||
return curr[0]
|
||||
}
|
||||
},
|
||||
// 返回当前的依赖
|
||||
CurrentDependenList() {
|
||||
return this.$store.getters.GET_DEPENDENTLIST(this.projectName)
|
||||
}
|
||||
}
|
||||
}
|
||||
200
src/application/assets/js/page/Setting.js
Normal file
@@ -0,0 +1,200 @@
|
||||
const {ipcRenderer,app} = require("electron");
|
||||
|
||||
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
isShowDialog: false,
|
||||
visible: false,
|
||||
networkURL: '',
|
||||
slider0pacity: '',
|
||||
typeText: this.$store.state.packageType[this.$store.state.packageTypeSelect],
|
||||
custom: {
|
||||
type: "img",
|
||||
text: "",
|
||||
BgColor: "#000000",
|
||||
textColor: "#ffffff",
|
||||
activeBgColor: "#000000",
|
||||
system: true
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
// 重新选择nodejs的路径
|
||||
selectEnvPath() {
|
||||
ipcRenderer.send('selectEnvPath', {
|
||||
action: '重新选择nodejs的路径'
|
||||
});
|
||||
},
|
||||
TypeChange(index) {
|
||||
|
||||
let res = ipcRenderer.sendSync('setPackageTypeindex', {
|
||||
action: '更新你选择的包管理工具',
|
||||
keyName: "packageTypeSelect",
|
||||
value: index,
|
||||
packageType: this.$store.state.packageType[index]
|
||||
});
|
||||
|
||||
console.log(res)
|
||||
if (!res.status) {
|
||||
this.$confirm(`出现错误:${res.message}`, '提示', {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: '我知道了',
|
||||
showCancelButton: false,
|
||||
roundButton: true,
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
this.typeText = this.$store.state.packageType[this.$store.state.packageTypeSelect]
|
||||
})
|
||||
} else {
|
||||
this.$store.state.packageTypeSelect = index;
|
||||
}
|
||||
},
|
||||
// 开启透明度 的 checkbox 被选择的时候
|
||||
openChange(val) {
|
||||
ipcRenderer.send('setSettingConfig', {
|
||||
action: '更新 checkbox 是否开启透明度的开关',
|
||||
keyName: "mask.open",
|
||||
value: val
|
||||
});
|
||||
},
|
||||
|
||||
// slider 滑动的时候 改变 body 上的 style
|
||||
change(e) {
|
||||
ipcRenderer.send('setSettingConfig', {
|
||||
action: '更新 透明的的数值',
|
||||
keyName: e == "opacity"
|
||||
? "mask.opacity"
|
||||
: "mask.blur"
|
||||
,
|
||||
value: e == "opacity"
|
||||
? this.$store.state.mask.opacity
|
||||
: this.$store.state.mask.blur
|
||||
});
|
||||
|
||||
// 因为 --fill-color 这种方式需要通过style把颜色变量定义在父组件上
|
||||
// 然后 子组件 使用变量,而这边 el-slider 组件的数值提示 tooltip 框
|
||||
// 在 body 下,所以这边为 body 加 --fill-color
|
||||
let body = document.querySelector("body")
|
||||
body.style = `--fill-color: ${
|
||||
this.$store.getters.get_list[this.$store.getters.get_theme].type == 'color'
|
||||
? `${this.$store.getters.get_list[this.$store.getters.get_theme].BgColor}`
|
||||
: `${this.$store.getters.get_list[this.$store.getters.get_theme].activeBgColor}`
|
||||
}`
|
||||
},
|
||||
|
||||
// 格式化选中的 透明度 数值
|
||||
formatTooltip(val) {
|
||||
return val / 10;
|
||||
},
|
||||
|
||||
// 网络图片输入框被输入的时候触发
|
||||
networkChange(value) {
|
||||
if (value.length == 0 || !value.includes("http")) {
|
||||
return false;
|
||||
}
|
||||
this.visible = false;
|
||||
this.networkURL = ""
|
||||
this.custom.BgColor = value;
|
||||
this.custom.text = "网络图片";
|
||||
},
|
||||
|
||||
// 删除主题
|
||||
deleteTheme(item, index) {
|
||||
console.log("item: ", item)
|
||||
console.log("index: ", index)
|
||||
console.log("index - 1: ", index - 1)
|
||||
|
||||
// 如果当前主题正是你点击要删除的主题
|
||||
if (this.$store.getters.get_theme == index) {
|
||||
// 那么设置当前主题为你点击主题的上一个
|
||||
this.changeTheme(index - 1);
|
||||
}
|
||||
|
||||
// 删除你点击的主题
|
||||
this.$store.commit("set_themeList", this.$store.getters.get_list.filter(v => {
|
||||
if (v.BgColor != item.BgColor) {
|
||||
return v
|
||||
}
|
||||
}));
|
||||
ipcRenderer.send('delete_themeList', {
|
||||
action: '删除你点击的主题',
|
||||
keyName: "list",
|
||||
value: item
|
||||
});
|
||||
// this.$store.commit("push_themeList", this.$store.getters.get_list)
|
||||
},
|
||||
|
||||
// 完成选择
|
||||
completeSelection() {
|
||||
if (/^#/.test(this.custom.BgColor)) {
|
||||
this.$notify({
|
||||
title: '提示',
|
||||
duration: 2000,
|
||||
type: 'warning',
|
||||
message: '请上传图片!'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// 深拷贝一下,不能直接改变 BgColor,要不然BgColor变了页面上的图片无法显示
|
||||
let color = JSON.parse(JSON.stringify(this.custom))
|
||||
color.BgColor = `url("${color.BgColor}")`;
|
||||
this.$store.commit("push_themeList", color)
|
||||
this.resetCustom()
|
||||
this.isShowDialog = false;
|
||||
},
|
||||
|
||||
// 重新选图
|
||||
reselect() {
|
||||
this.custom.text = ""
|
||||
this.custom.BgColor = "#000000"
|
||||
this.custom.textColor = "#ffffff"
|
||||
this.custom.activeBgColor = "#000000"
|
||||
},
|
||||
|
||||
// 选择上传文件
|
||||
uploadFile(e) {
|
||||
let file = e.target.files[0];
|
||||
this.custom.text = file.name.split(".")[0];
|
||||
this.custom.BgColor = file.path;
|
||||
console.log(this.custom)
|
||||
},
|
||||
|
||||
// 是否开机自启
|
||||
changeIsOpen(status) {
|
||||
this.$store.commit("set_isOpen", status)
|
||||
ipcRenderer.send('setSettingConfig', {
|
||||
action: '更新设置',
|
||||
keyName: "isOpen",
|
||||
value: status
|
||||
});
|
||||
},
|
||||
|
||||
// 点击切换主题
|
||||
changeTheme(index) {
|
||||
this.$store.commit('set_themeIndex', index)
|
||||
ipcRenderer.send('setSettingConfig', {
|
||||
action: '更新设置选中的主题下标',
|
||||
keyName: "selectThemeIndex",
|
||||
value: index
|
||||
});
|
||||
// 每次点击切换主题,同时修改body
|
||||
this.change()
|
||||
},
|
||||
|
||||
// 重置样式的变量
|
||||
resetCustom() {
|
||||
this.custom = {
|
||||
type: "img",
|
||||
text: "",
|
||||
BgColor: "#000000",
|
||||
textColor: "#ffffff",
|
||||
activeBgColor: "#000000",
|
||||
system: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/application/assets/js/page/Welcome.js
Executable file
@@ -0,0 +1,16 @@
|
||||
const { ipcRenderer } = require('electron');
|
||||
let Welcome = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
msg: '测试'
|
||||
},
|
||||
methods: {
|
||||
OpenHome () {
|
||||
ipcRenderer.send('openWindow', {
|
||||
action: 'Home.controller/Home',
|
||||
closeParent: true,
|
||||
data: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
1
src/application/assets/js/router/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
21274de1-398c-4c09-8da1-9da1510ed73e
|
||||
52
src/application/assets/js/router/index.js
Executable file
@@ -0,0 +1,52 @@
|
||||
const Utils = require("../Utils/index.js");
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: {
|
||||
name: 'main'
|
||||
},
|
||||
meta: {
|
||||
keepAlive: false
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/main',
|
||||
name: 'main',
|
||||
component: {
|
||||
template: Utils.Html("/Home/Main.html"),
|
||||
...require("../page/Main.js")
|
||||
},
|
||||
meta: {
|
||||
keepAlive: false
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/info',
|
||||
name: 'info',
|
||||
component: {
|
||||
template: Utils.Html("/Home/ProjectInfo.html"),
|
||||
...require("../page/ProjectInfo.js")
|
||||
},
|
||||
meta: {
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/setting',
|
||||
name: 'setting',
|
||||
component: {
|
||||
template: Utils.Html("/Setting/Setting.html"),
|
||||
...require("../page/Setting.js")
|
||||
},
|
||||
meta: {
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
routes
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
1
src/application/assets/js/store/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
fff304ef-3209-4fbf-b4d8-dffbb8bde3b6
|
||||
147
src/application/assets/js/store/index.js
Normal file
@@ -0,0 +1,147 @@
|
||||
const {ipcRenderer} = require("electron");
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
console.log(window)
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
loadingService: null,
|
||||
ProjectList: [],
|
||||
currentVsersion: null, // 保存当前版本号
|
||||
isUpdate: false, // 决定 升级弹窗是否显示,
|
||||
updateInfo: {}// 如果需要升级,那么这里面保存的就是升级的一些详细信息
|
||||
},
|
||||
getters: {
|
||||
get_vsersion: state => {
|
||||
return state.currentVsersion;
|
||||
},
|
||||
get_isOpen: state => {
|
||||
return state.isOpen;
|
||||
},
|
||||
get_list: state => {
|
||||
return state.list;
|
||||
},
|
||||
get_theme: state => {
|
||||
return state.selectThemeIndex;
|
||||
},
|
||||
GET_PROJECT: state => {
|
||||
return state.ProjectList
|
||||
},
|
||||
GET_CURRENTSCRIPT: (state) => (projectName, currentScript) => {
|
||||
console.log(projectName, currentScript)
|
||||
let a = state.ProjectList.find(v => v.FolderName == projectName);
|
||||
console.log(a.TaskRunList.find(v => v.ScriptName = currentScript))
|
||||
},
|
||||
GET_CURRENTTASK: (state) => (id) => {
|
||||
try {
|
||||
let a = state.ProjectList.find(v => v.FolderName == id)
|
||||
return a.TaskRunList;
|
||||
} catch (e) {}
|
||||
},
|
||||
GET_DEPENDENTLIST: (state) => (id) => {
|
||||
try {
|
||||
let a = state.ProjectList.find(v => v.FolderName == id)
|
||||
return a.DependentList;
|
||||
} catch (e) {}
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
set_isUpdate(state, status) {
|
||||
state.isUpdate = status
|
||||
},
|
||||
set_updateInfo(state, info) {
|
||||
state.updateInfo = info
|
||||
},
|
||||
set_vsersion(state, vsersion) {
|
||||
state.currentVsersion = vsersion
|
||||
},
|
||||
set_isOpen(state, status) {
|
||||
state.isOpen = status
|
||||
},
|
||||
set_themeIndex(state, index) {
|
||||
state.selectThemeIndex = index;
|
||||
},
|
||||
// 向主题数组里面追加用户新增的样式
|
||||
push_themeList(state, data) {
|
||||
state.list.push(data)
|
||||
// 将用户自定义的配置保存到json数据库
|
||||
ipcRenderer.send('push_themeList', {
|
||||
action: '保存自定义主题配置',
|
||||
keyName: "list",
|
||||
value: data
|
||||
});
|
||||
},
|
||||
set_themeList(state, data) {
|
||||
state.list = data;
|
||||
},
|
||||
// 保存项目的数据
|
||||
SET_LIST(state, data) {
|
||||
if (data.type == "ChoiceFile") {
|
||||
state.ProjectList.push(data.res);
|
||||
} else {
|
||||
if (data.res) {
|
||||
state.ProjectList = data.res;
|
||||
}
|
||||
}
|
||||
},
|
||||
// 进入详情页后,点击 任务,将从后端获取到的项目script命令
|
||||
// 数据保存到对应 ProjectList 中
|
||||
SET_TASKRUNLIST(state, params) {
|
||||
state.ProjectList.forEach(v => {
|
||||
if (v.FolderName == params.id) {
|
||||
// v.TaskRunList.forEach(j => {
|
||||
// if (j.Terminal != null)
|
||||
// })
|
||||
v.TaskRunList = params.TaskRunList
|
||||
}
|
||||
});
|
||||
},
|
||||
SET_DEPENDENTLIST(state, params) {
|
||||
state.ProjectList.forEach(v => {
|
||||
if (v.FolderName == params.id) {
|
||||
v.DependentList = params.DependentList
|
||||
}
|
||||
});
|
||||
},
|
||||
// 修改 ProjectList 中的激活tabs
|
||||
SET_CURRENTTABS(state, params) {
|
||||
state.ProjectList.forEach(v => {
|
||||
if (v.FolderName == params.id) {
|
||||
v.CurrentTabs = params.currentTabs
|
||||
}
|
||||
})
|
||||
},
|
||||
set_loading(state, intloading) {
|
||||
state.loadingService = intloading;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
loading({ commit }, title) {
|
||||
let load = ELEMENT.Loading.service({
|
||||
text: `${title}..`,
|
||||
fullscreen: true
|
||||
});
|
||||
commit("set_loading", load)
|
||||
},
|
||||
checkUpdate({ commit }, type) {
|
||||
let res = ipcRenderer.sendSync('checkUpdate', {
|
||||
action: '检查新版本更新',
|
||||
});
|
||||
if (res.isUpdate) {
|
||||
commit("set_updateInfo", res)
|
||||
commit("set_isUpdate", res.isUpdate)
|
||||
} else {
|
||||
if (type != "default")
|
||||
ELEMENT.Notification({
|
||||
title: '检查升级',
|
||||
message: '已是最新版本,无需升级',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
modules: {}
|
||||
})
|
||||
|
||||
module.exports = store
|
||||
1
src/application/assets/less/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
5678f8c1-8086-4b5b-8d8f-33607e1cca0b
|
||||
476
src/application/assets/less/Home.less
Executable file
@@ -0,0 +1,476 @@
|
||||
@import "color";
|
||||
|
||||
[v-cloak]{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.home {
|
||||
display: flex;
|
||||
box-shadow: 7px 7px 9px #1f1d1d47;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
border-top: 0.5px solid @lineColoe;
|
||||
border-radius: 10px;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
.td {
|
||||
height: 30px;
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
}
|
||||
.project_list {
|
||||
height: 100%;
|
||||
width: 21%;
|
||||
border-right: 1px solid @lineColoe;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
border-top-left-radius: 10px;
|
||||
border-bottom-left-radius: 10px;
|
||||
.back {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
border-bottom-left-radius: 10px;
|
||||
background: rgba(0,0,0,0.4);
|
||||
}
|
||||
.mask {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
background-size: cover !important;
|
||||
background-position: center !important;
|
||||
z-index: -2;
|
||||
border-bottom-left-radius: 10px;
|
||||
}
|
||||
.list_title {
|
||||
font-size: 15px;
|
||||
color: @textColor2;
|
||||
}
|
||||
|
||||
.list_item {
|
||||
overflow-y: scroll;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
flex: 1;
|
||||
padding-top: 30px;
|
||||
li {
|
||||
color: @textColor2;
|
||||
padding: 9px 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-radius: 4px;
|
||||
padding-left: 10px;
|
||||
.left {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 10px;
|
||||
img {
|
||||
margin-right: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
margin-right: 5px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
padding-right: 10px;
|
||||
.icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.addfile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 35px;
|
||||
padding: 0 15px;
|
||||
font-size: 14px;
|
||||
line-height: 35px;
|
||||
cursor: pointer;
|
||||
.left {
|
||||
span {
|
||||
margin-left: 7px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
i {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.project_info {
|
||||
transition: all 0.8s cubic-bezier(0.58, -0.09, 0.45, 0.96);
|
||||
overflow-y: scroll;
|
||||
border-bottom-right-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
background: @while;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
.full_screen {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 20px;
|
||||
top: 0;
|
||||
left: 0px;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
svg {
|
||||
font-size: 18px;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
color: @textColor;
|
||||
transition: all 0.8s;
|
||||
}
|
||||
&:hover {
|
||||
svg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.full {
|
||||
width: 100%;
|
||||
}
|
||||
.half {
|
||||
width: 79%;
|
||||
}
|
||||
}
|
||||
.update_content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.c_text {
|
||||
margin-left: 30px;
|
||||
flex: 1;
|
||||
height: 284px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.title {
|
||||
font-size: 23px;
|
||||
margin-bottom: 3px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.version {
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
color: #464646;
|
||||
}
|
||||
ul {
|
||||
padding-left: 22px;
|
||||
flex: 1;
|
||||
height: 219px;
|
||||
overflow-y: scroll;
|
||||
li {
|
||||
margin-bottom: 5px;
|
||||
color: #464646;
|
||||
list-style: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabContent {
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
.ProjectInfo,
|
||||
.dependentList {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
//border: 1px solid #eaeaea;
|
||||
margin-bottom: 5px;
|
||||
padding: 5px 8px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #d0d0d0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ProjectInfo {
|
||||
height: 100%;
|
||||
.icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.icon {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
}
|
||||
}
|
||||
#pane-home {
|
||||
height: 100%;
|
||||
.package {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.runList {
|
||||
-webkit-app-region: no-drag;
|
||||
border-top: none !important;
|
||||
.terminals {
|
||||
width: 100%;
|
||||
color: #ccc;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
background: #1d2935;
|
||||
height: 336px;
|
||||
.xterm-render {
|
||||
height: 294px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.terminal_list {
|
||||
padding-left: 15px;
|
||||
}
|
||||
.terminal_action {
|
||||
background: #1d2935;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
padding: 0 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.a_left {
|
||||
.run {
|
||||
font-size: 20px;
|
||||
color: #c4e791;
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.a_right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
&:last-child {
|
||||
margin-right: 0 !important;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.run_icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 100%;
|
||||
margin-right: 20px;
|
||||
background: #c4e791;
|
||||
//background: var(--fill-color);
|
||||
}
|
||||
.icon-npm1 {
|
||||
margin-right: 10px;
|
||||
color: #d52a29;
|
||||
font-size: 20px;
|
||||
}
|
||||
.text {
|
||||
font-size: 14px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
width: 90%;
|
||||
.ScriptName {
|
||||
|
||||
}
|
||||
.ScriptShell {
|
||||
font-size: 12px !important;
|
||||
margin-left: 9px;
|
||||
color: @textColor;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
//.left {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// flex: 1;
|
||||
// overflow: hidden;
|
||||
// text-overflow: ellipsis;
|
||||
// white-space: nowrap;
|
||||
//
|
||||
// i {
|
||||
// margin-right: 10px;
|
||||
// color: #d52a29;
|
||||
// font-size: 25px;
|
||||
// }
|
||||
//
|
||||
// span {
|
||||
// font-size: 12px;
|
||||
// margin-left: 9px;
|
||||
// color: @textColor;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
.dependent_action {
|
||||
margin-bottom: 25px;
|
||||
.title {
|
||||
font-size: 17px;
|
||||
//font-weight: bold;
|
||||
margin-bottom: 9px;
|
||||
//border-bottom: 1px solid #E4E7ED;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.empty {
|
||||
text-align: center;
|
||||
margin-top: 15%;
|
||||
i {
|
||||
font-size: 45px;
|
||||
margin-bottom: 15px;
|
||||
color: #706f6f;
|
||||
}
|
||||
.title_tips {
|
||||
font-size: 14px;
|
||||
color: @textColor3;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.tips {
|
||||
font-size: 12px;
|
||||
color: @lineColoe;
|
||||
}
|
||||
}
|
||||
.action_list {
|
||||
height: 45px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
font-size: 25px;
|
||||
color: @textColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dependent {
|
||||
margin-bottom: 30px;
|
||||
|
||||
.title {
|
||||
font-size: 17px;
|
||||
//font-weight: bold;
|
||||
margin-bottom: 9px;
|
||||
border-bottom: 1px solid #E4E7ED;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.dependentList {
|
||||
padding-left: 20px;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
img {
|
||||
margin-right: 10px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
.text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.dTitle {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.version {
|
||||
font-size: 12px;
|
||||
color: @textColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
margin-right: 15px;
|
||||
&:last-child {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.no_dependent {
|
||||
margin-top: 50px;
|
||||
color: @textColor;
|
||||
.icon-empt {
|
||||
font-size: 35px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chiicon {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
&:hover {
|
||||
box-shadow: 0px 2px 11px #e4dddd;
|
||||
}
|
||||
.icon {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
.main {
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 30px;
|
||||
color: @textColor;
|
||||
}
|
||||
ul {
|
||||
padding-left: 40px;
|
||||
li {
|
||||
list-style: decimal;
|
||||
margin-bottom: 15px;
|
||||
color: @textColor;
|
||||
}
|
||||
}
|
||||
.logo {
|
||||
text-align: center;
|
||||
img {
|
||||
width: 188px;
|
||||
margin-top: 5%;
|
||||
margin-bottom: 6%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.Dependencies {
|
||||
.Dependencies_item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
3
src/application/assets/less/PlugInfo.less
Normal file
@@ -0,0 +1,3 @@
|
||||
.PlugInfo {
|
||||
|
||||
}
|
||||
210
src/application/assets/less/Setting.less
Normal file
@@ -0,0 +1,210 @@
|
||||
@import "color";
|
||||
|
||||
|
||||
.Setting {
|
||||
.title {
|
||||
font-size: 18px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 0.6px solid @lineColoe;
|
||||
i {
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.warp {
|
||||
margin: 20px 0;
|
||||
border-bottom: 0.5px solid @lineColoe;
|
||||
.warp_title {
|
||||
font-size: 17px;
|
||||
color: #303133;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
//.custom {
|
||||
// margin-right: 15px;
|
||||
// font-size: 14px;
|
||||
// color: #8d8d8d;
|
||||
// cursor: pointer;
|
||||
//}
|
||||
}
|
||||
.theme_list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
li {
|
||||
width: 18%;
|
||||
height: 50px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 11px;
|
||||
text-align: center;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
background-size: cover !important;
|
||||
background-position: center !important;
|
||||
span {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
font-size: 14px;
|
||||
}
|
||||
i {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: 6px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.back {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
.add {
|
||||
width: 18%;
|
||||
.custom {
|
||||
height: 50px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 11px;
|
||||
margin-right: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0.6px solid #8b8b8b;
|
||||
color: #8b8b8b;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.login_start {
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
.silder_opacity {
|
||||
width: 20% ;
|
||||
.el-slider__runway {
|
||||
margin: 0 !important;
|
||||
.el-slider__button-wrapper {
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
top: -9px !important;
|
||||
}
|
||||
}
|
||||
.el-slider__bar {
|
||||
background-color: var(--fill-color) !important;
|
||||
}
|
||||
.el-slider__button {
|
||||
border-color: var(--fill-color) !important;
|
||||
}
|
||||
}
|
||||
.el-checkbox {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.version {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.current_version {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.check_version {
|
||||
padding: 1px 8px;
|
||||
border: 1px solid var(--fill-color);
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
color: var(--fill-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.path {
|
||||
|
||||
}
|
||||
.el-link {
|
||||
margin-left: 15px;
|
||||
color: var(--fill-color) !important;
|
||||
&:hover::after {
|
||||
border-bottom: 1px solid var(--fill-color) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.children {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
.custom_dialog {
|
||||
.el-dialog__body {
|
||||
padding-bottom: 15px !important;
|
||||
}
|
||||
.custom_wrap {
|
||||
height: 300px;
|
||||
.action {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
.upload_local,
|
||||
.upload_network {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
input {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.upload_network {
|
||||
|
||||
}
|
||||
p {
|
||||
margin-top: 9px;
|
||||
}
|
||||
i {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
.el-image {
|
||||
overflow: scroll !important;
|
||||
.el-image__inner {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.custom_footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 17px;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.color_block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 12px;
|
||||
span {}
|
||||
input {
|
||||
width: 18px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/application/assets/less/Welcome.less
Executable file
@@ -0,0 +1,37 @@
|
||||
@import "color";
|
||||
|
||||
.welcome {
|
||||
padding: 55px 30px 30px 30px;
|
||||
text-align: center;
|
||||
border-radius: 10px;
|
||||
//background: #fff;
|
||||
//width: calc(100% - 15px);
|
||||
//height: calc(100% - 15px);
|
||||
box-sizing: border-box;
|
||||
//border: 0.5px solid @lineColoe;
|
||||
//box-shadow: 7px 7px 9px #1f1d1d47;
|
||||
user-select: none;
|
||||
.title {
|
||||
font-size: 25px;
|
||||
letter-spacing: 2px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.desc {
|
||||
font-size: 15px;
|
||||
color: @textColor3;
|
||||
width: 74%;
|
||||
margin: auto;
|
||||
margin-bottom: 11%;
|
||||
}
|
||||
.version {
|
||||
font-size: 13px;
|
||||
color: @textColor;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.logo {
|
||||
width: 188px;
|
||||
margin-top: 5%;
|
||||
margin-bottom: 7%;
|
||||
}
|
||||
}
|
||||
174
src/application/assets/less/clear.less
Executable file
@@ -0,0 +1,174 @@
|
||||
@import "color";
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-app-region: drag;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul, li, h1,h2,h3,h4,h5,h6,p{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: #fff0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.nodata {
|
||||
text-align: center;
|
||||
margin-top: 22px;
|
||||
color: @textColor;
|
||||
}
|
||||
|
||||
//不可以拖动也不可以选中文字
|
||||
.nodragText {
|
||||
-webkit-user-select: none;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.dragText {
|
||||
-webkit-user-select: text;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
click {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
//不可以拖动
|
||||
.nodrag {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
//可以拖动
|
||||
.drag {
|
||||
-webkit-app-region: drag !important;
|
||||
}
|
||||
|
||||
// 页面切换动画
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .5s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar{
|
||||
width : 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.padding30 {
|
||||
padding: 30px 30px 0px 30px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//复写ui框架的样式
|
||||
.el-tabs__nav-wrap::after,
|
||||
.el-tabs__active-bar {
|
||||
height: 1px !important;
|
||||
}
|
||||
|
||||
.is-dark {
|
||||
background: var(--fill-color) !important;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
padding: 5px 12px !important;
|
||||
border-radius: 13px !important;
|
||||
user-select: none !important;
|
||||
box-shadow: 0.5px 1px 1px #e4e4e4 !important;
|
||||
}
|
||||
|
||||
.el-tabs {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
flex: 1;
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-dropdown {
|
||||
color: inherit !important;
|
||||
}
|
||||
.el-message-box,
|
||||
.el-dialog {
|
||||
border-radius: 10px !important;
|
||||
background: #ededed;
|
||||
}
|
||||
|
||||
.el-tabs__item:focus.is-active.is-focus:not(:active) {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.el-tabs__item.is-active,
|
||||
.el-tabs__item:hover{
|
||||
color: var(--fill-color) !important;
|
||||
}
|
||||
.el-tabs__active-bar {
|
||||
background-color: var(--fill-color) !important;
|
||||
}
|
||||
.el-checkbox {
|
||||
.el-checkbox__label {
|
||||
color: var(--fill-color) !important;
|
||||
}
|
||||
}
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner,
|
||||
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
|
||||
background-color: var(--fill-color) !important;
|
||||
border-color: var(--fill-color) !important;
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-focus .el-checkbox__inner,
|
||||
.el-checkbox__inner:hover {
|
||||
border-color: var(--fill-color) !important;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 10px 20px 16px !important;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 39px 20px 1px 20px !important;
|
||||
}
|
||||
|
||||
.el-dialog__header {
|
||||
border-bottom: 0.5px solid #e9e9e9;
|
||||
}
|
||||
6
src/application/assets/less/color.less
Executable file
@@ -0,0 +1,6 @@
|
||||
@while: #fff;
|
||||
@themeBack: #e5e3e4;
|
||||
@textColor: #777c83;
|
||||
@textColor2: #222;
|
||||
@textColor3: #929192;
|
||||
@lineColoe: #d8d6d7;
|
||||
172
src/application/assets/less/xterm.less
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
||||
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||
* https://github.com/chjj/term.js
|
||||
* @license MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Originally forked from (with the author's permission):
|
||||
* Fabrice Bellard's javascript vt100 for jslinux:
|
||||
* http://bellard.org/jslinux/
|
||||
* Copyright (c) 2011 Fabrice Bellard
|
||||
* The original design remains. The terminal itself
|
||||
* has been extended to include xterm CSI codes, among
|
||||
* other features.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default styles for xterm.js
|
||||
*/
|
||||
|
||||
.xterm {
|
||||
font-feature-settings: "liga" 0;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.xterm.focus,
|
||||
.xterm:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.xterm .xterm-helpers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
/**
|
||||
* The z-index of the helpers must be higher than the canvases in order for
|
||||
* IMEs to appear on top.
|
||||
*/
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.xterm .xterm-helper-textarea {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: -9999em;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: -5;
|
||||
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.xterm .composition-view {
|
||||
/* TODO: Composition position got messed up somewhere */
|
||||
background: #000;
|
||||
color: #FFF;
|
||||
display: none;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.xterm .composition-view.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.xterm .xterm-viewport {
|
||||
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||
background-color: #000;
|
||||
overflow-y: scroll;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-scroll-area {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.xterm-char-measure-element {
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -9999em;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.xterm {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.xterm.enable-mouse-events {
|
||||
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.xterm.xterm-cursor-pointer {
|
||||
cursor: pointer;
|
||||
color: #3a8ee6;
|
||||
}
|
||||
|
||||
.xterm.column-select.focus {
|
||||
/* Column selection mode */
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.xterm .xterm-accessibility,
|
||||
.xterm .xterm-message {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.xterm .live-region {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.xterm-dim {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.xterm-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
1
src/application/components/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
7e4b2960-67b6-4e37-b953-861eb2422c55
|
||||
1
src/application/components/footer.jade
Executable file
@@ -0,0 +1 @@
|
||||
h2 底部
|
||||
1
src/application/components/header.jade
Executable file
@@ -0,0 +1 @@
|
||||
.action header
|
||||
1
src/application/layout/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
ad4c9df0-ddf8-4ce6-9c8e-0ca94e36c2e8
|
||||
21
src/application/layout/index.jade
Executable file
@@ -0,0 +1,21 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
meta(charset="utf-8")
|
||||
meta(http-equiv="X-UA-Compatible" content="IE=edge")
|
||||
meta(name="viewport" content="width=device-width, initial-scale=1")
|
||||
meta(name="renderer" content="webkit")
|
||||
meta(http-equiv="Cache-Control" content="no-siteapp")
|
||||
link(rel='stylesheet', href='../../assets/css/clear.css')
|
||||
each v in globals !== undefined ? globals.css : []
|
||||
link(rel='stylesheet', href=`${v}`)
|
||||
each v in globals !== undefined ? globals.js : []
|
||||
script(src=`${v}`)
|
||||
|
||||
block title
|
||||
body
|
||||
// - if (header)
|
||||
// include ../components/header
|
||||
block content
|
||||
|
||||
block script
|
||||
1
src/application/page/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
5f13dc73-1b47-47f3-8e03-d9d8b06fea3c
|
||||
1
src/application/page/Home/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
68dc1f7b-f790-4826-aedb-632b7bbb9ad1
|
||||
92
src/application/page/Home/Home.jade
Executable file
@@ -0,0 +1,92 @@
|
||||
extends ../../layout/index
|
||||
|
||||
block title
|
||||
|
||||
title #{title}
|
||||
link(rel='stylesheet', href='../../assets/css/Home.css')
|
||||
link(rel='stylesheet', href='../../assets/css/xterm.css')
|
||||
|
||||
block content
|
||||
#app(v-cloak)
|
||||
.home
|
||||
.project_list.nodrag
|
||||
.mask(v-bind:style="{ background : $store.getters.get_list[$store.getters.get_theme].BgColor, filter: `blur(${$store.state.mask.blur}px)`}")
|
||||
.back(v-if="$store.getters.get_list[$store.getters.get_theme].type == 'img' && $store.state.mask.open " v-bind:style="{ background: `rgba(0,0,0,${$store.state.mask.opacity})` }")
|
||||
ul.list_item.nodrag
|
||||
li(:title="v.name" v-bind:style="{ color : $store.getters.get_list[$store.getters.get_theme].textColor, background: i == clickProjectIndex ? $store.getters.get_list[$store.getters.get_theme].activeBgColor : '' }"
|
||||
@click="openInfo(v,i)"
|
||||
v-for="(v,i) in $store.getters.GET_PROJECT")
|
||||
.left
|
||||
img(v-if="v.FolderIcon.includes('.png')"
|
||||
src="../../assets/img/GenericFolder.png")
|
||||
|
||||
svg.icon(v-else="" aria-hidden='true' @click.stop="OpenPropject('cmd', v)" title="在终端打开")
|
||||
use(v-bind:xlink:href='v.FolderIcon')
|
||||
| {{ v.name }}
|
||||
|
||||
.right(v-if="i == clickProjectIndex" @click.stop="")
|
||||
svg.icon(aria-hidden='true' @click.stop="OpenPropject('file', v)" title="在文件管理打开")
|
||||
use(xlink:href='#icon-wenjianjia2')
|
||||
svg.icon(aria-hidden='true' @click.stop="OpenPropject('cmd', v)" title="在终端打开")
|
||||
use(xlink:href='#icon-config-command')
|
||||
|
||||
el-dropdown(size="medium" @command="ShowActionClick")
|
||||
svg.icon(aria-hidden='true')
|
||||
use(xlink:href='#icon-gengduo')
|
||||
|
||||
el-dropdown-menu(slot='dropdown')
|
||||
el-dropdown-item(v-for="(item,index) in MoreList" v-bind:icon="item.icon" v-bind:key="index"
|
||||
v-bind:command="[ v.name, item.id ]") {{ item.title }}
|
||||
|
||||
.addfile(v-bind:style="{ color : $store.getters.get_list[$store.getters.get_theme].textColor}")
|
||||
.left(@click="ChoiceFile")
|
||||
i.el-icon-circle-plus-outline
|
||||
span 导入项目
|
||||
i.el-icon-setting(@click="$router.push('/setting')")
|
||||
|
||||
// 需要根据当前是否全屏,动态添加类名。同时 因为全屏后左下角是直角,所以需要动态判断处理圆角
|
||||
.project_info.nodrag(v-bind:class=" menuStatus ? 'full' : 'half' "
|
||||
v-bind:style="{ 'border-bottom-left-radius': menuStatus ? '10px': '0px' }")
|
||||
|
||||
keep-alive
|
||||
transition(name="fade")
|
||||
router-view(v-if="$route.meta.keepAlive" class="padding30")
|
||||
router-view(v-if="!$route.meta.keepAlive" class="padding30")
|
||||
//全屏箭头
|
||||
.full_screen
|
||||
svg.icon(aria-hidden='true' v-bind:style="{ transform : `rotate(${menuIconRotate}deg)`}"
|
||||
@mouseover="Mouseover"
|
||||
@click="changeMenu"
|
||||
@mouseout="Mouseover")
|
||||
use(xlink:href='#icon-jinru')
|
||||
.td.drag
|
||||
el-dialog.nodrag(title="重命名项目"
|
||||
v-bind:close-on-click-modal="true"
|
||||
v-bind:visible.sync="isEditDialog")
|
||||
el-input(v-model="EditProjectName" placeholder="请输入新的文件夹名称")
|
||||
.dialog-footer(slot='footer')
|
||||
el-button(@click='SubmitEdit' round) 确 定
|
||||
|
||||
el-dialog.nodrag(title="请选择项目图标" v-bind:visible.sync="isIconDialog")
|
||||
el-row
|
||||
el-col.chiicon(v-bind:span="6"
|
||||
v-for="(item,index) in IconList"
|
||||
v-bind:title="item.title")
|
||||
svg.icon(aria-hidden='true' @click="SelectIcon(item)")
|
||||
use(v-bind:xlink:href="item.icon")
|
||||
|
||||
el-dialog(title='检查升级', v-bind:show-close="false", v-bind:close-on-click-modal="false" ,v-bind:visible.sync='$store.state.isUpdate', width='50%',)
|
||||
.update_content
|
||||
img(src="../../assets/img/logo.png")
|
||||
.c_text
|
||||
.title {{ $store.state.updateInfo.title }}
|
||||
.version 版本:v{{ $store.state.updateInfo.version }}
|
||||
ul
|
||||
li(v-for="(item,index) in $store.state.updateInfo.detailed" v-bind:key="index") {{ item }}
|
||||
span.dialog-footer(slot='footer')
|
||||
el-button(@click='$store.state.isUpdate = false ') 下次一定
|
||||
el-button(type='primary', @click='downNowDmg($store.state.updateInfo.down)') 立即更新
|
||||
|
||||
|
||||
block script
|
||||
script(src="../../assets/js/page/Home.js")
|
||||
17
src/application/page/Home/Main.jade
Normal file
@@ -0,0 +1,17 @@
|
||||
.main
|
||||
.logo
|
||||
img(src="../../assets/img/project_1.png")
|
||||
h3 Easy Project
|
||||
p 一款前端工程化项目的GUI管理工具,能够一键导入本地的前端项目,帮助您将零碎零散的前端项目统一化管理和配置。
|
||||
|
||||
h3 功能列表
|
||||
ul
|
||||
li 支持实时解析项目的开发和正式环境依赖,通过简洁的页面展示给开发者,开发者可以通过鼠标操作,对依赖包进行一键升级,删除,更新到指定版本,查看依赖包的详情和使用文档等。
|
||||
li 支持自动解析项目的运行命令,通过鼠标快速点击实现项目的运行,停止,查看终端日志,点击终端日志打开网页链接。
|
||||
li 能够查看项目的CPU,内存等内存占用情况,项目依赖关系分析等,最大限度用简单的方式去避免开发者频繁打开各种命令行窗口,输入各种命令,在无数的命令行窗口中迷失自我的尴尬情况。
|
||||
li 能够对项目进行个性化的设置,可以通过负载均衡最大限度发挥电脑的性能来提高我们前端项目的访问速度,一键中止被恶意占用的端口,在项目仪表盘中一键添加常用命令,设置开机自启,夜间模式,切换主题,查看最新技术文章等等等...
|
||||
|
||||
//h3 项目概览
|
||||
//ul
|
||||
// li
|
||||
// a() Github: http://github.com/helpcode
|
||||
130
src/application/page/Home/ProjectInfo.jade
Executable file
@@ -0,0 +1,130 @@
|
||||
.ProjectInfo
|
||||
el-tabs(v-model="activeName" @tab-click="handleClick"
|
||||
v-bind:style="{'--fill-color': $store.getters.get_list[$store.getters.get_theme].type == 'color' ? $store.getters.get_list[$store.getters.get_theme].BgColor : $store.getters.get_list[$store.getters.get_theme].activeBgColor }")
|
||||
el-tab-pane(name='outline' v-bind:key="projectName")
|
||||
span(slot='label' class="icons")
|
||||
svg.icon(aria-hidden='true')
|
||||
use(xlink:href="#icon-shouyeshouye")
|
||||
span 概要
|
||||
.tabContent
|
||||
el-form(ref='form', label-width='90px')
|
||||
el-form-item(label='项目名称:')
|
||||
span.dragText uni-project
|
||||
el-form-item(label='文件夹名:')
|
||||
span.dragText uni-project
|
||||
el-form-item(label='磁盘路径:')
|
||||
span.dragText /Users/bmy/Desktop/uni-project
|
||||
el-divider
|
||||
el-form-item(label='源码大小:')
|
||||
span.dragText 3MB
|
||||
el-form-item(label='依赖统计:')
|
||||
span.dragText 13 个依赖
|
||||
el-link(v-bind:underline='false') 查看
|
||||
|
||||
|
||||
|
||||
el-tab-pane(name='home')
|
||||
span(slot='label' class="icons")
|
||||
//i(class="iconfont icon-yibiaopan") 仪表盘
|
||||
//img(src="../../assets/img/SidebarHomeFolder.png")
|
||||
svg.icon(aria-hidden='true')
|
||||
use(xlink:href="#icon-10json")
|
||||
span 配置
|
||||
div(class="package" v-bind:key="projectName")
|
||||
|
||||
el-tab-pane(name='task' v-bind:key="`${projectName}`")
|
||||
span(slot='label' class="icons")
|
||||
//i(class="iconfont icon-config-command") 任务
|
||||
//img(src="../../assets/img/SidebarAllMyFiles.png")
|
||||
svg.icon(aria-hidden='true')
|
||||
use(xlink:href="#icon-config-command")
|
||||
span 任务
|
||||
.tabContent
|
||||
el-collapse.runList(@change="collapseChange" v-bind:accordion="true")
|
||||
el-collapse-item(v-bind:name="`${v.ScriptName}_${i}`" v-for="(v,i) in CurrentTaskList"
|
||||
v-bind:key="`${projectName}_${v.ScriptName}_${i}`")
|
||||
template(slot="title")
|
||||
.run_icon(v-if="v.IsRuning == 'runing'")
|
||||
svg.icon.icon-npm1(aria-hidden='true' v-else)
|
||||
use(xlink:href='#icon-npm1')
|
||||
.text
|
||||
span.ScriptName {{ v.ScriptName }}
|
||||
span(v-if="v.IsRuning == 'runing'") Pid:{{ v.pid }}
|
||||
span.ScriptShell {{ v.ScriptShell }}
|
||||
.terminals
|
||||
.terminal_action
|
||||
.a_left
|
||||
svg.icon.run(aria-hidden='true' v-bind:title="v.IsRuning == 'idle' ? '点击运行' : '点击停止' "
|
||||
@click="v.IsRuning == 'idle' ? RunCmd(v) : StopCmd(v) ")
|
||||
use(v-bind:xlink:href=" v.IsRuning == 'idle' ? '#icon-qidong' : '#icon-zanting2' ")
|
||||
.a_right
|
||||
svg.icon.icon-copy(aria-hidden='true' title="复制" @click="copyContent(v)")
|
||||
use(xlink:href='#icon-copy')
|
||||
svg.icon.icon-dibu(aria-hidden='true' title="到底部" @click="ToBottom(v)")
|
||||
use(xlink:href='#icon-dibu')
|
||||
svg.icon.icon-clearup(aria-hidden='true' title="清除日志" @click="clearLog(v)")
|
||||
use(xlink:href='#icon-delete')
|
||||
|
||||
div(v-bind:ref="`${projectName}_${v.ScriptName}`"
|
||||
v-bind:key="projectName+v.ScriptName"
|
||||
v-bind:class="`terminal_list ${projectName}_${v.ScriptName}`")
|
||||
|
||||
el-tab-pane(name='dependent')
|
||||
span(slot='label' class="icons")
|
||||
//i(class="iconfont icon-yilaiguanxi") 依赖
|
||||
//img(src="../../assets/img/SidebarDropBoxFolder.png")
|
||||
svg.icon(aria-hidden='true')
|
||||
use(xlink:href="#icon-gengduoguanxi")
|
||||
span 依赖
|
||||
.tabContent
|
||||
.dependent_action
|
||||
.title 安装依赖
|
||||
.action_list
|
||||
el-tooltip(effect='dark', content='安装依赖', placement='bottom-start')
|
||||
svg.icon(aria-hidden='true' @click="isShowInstallDev = true")
|
||||
use(xlink:href='#icon-financial_install')
|
||||
//- 正式环境依赖
|
||||
.dependent(v-for="(v,i) in CurrentDependenList" v-bind:key="i")
|
||||
.title {{ v.title }}
|
||||
ul.dependentList
|
||||
div(v-if="v.list.length != 0")
|
||||
li.item.nodrag(v-for="(va,index) in v.list" v-bind:key="index" @click="PlugsInfo(va)")
|
||||
.left
|
||||
img(v-bind:src="va.logoImg")
|
||||
.text
|
||||
span.dTitle
|
||||
| {{ va.name }}
|
||||
span.version 版本:{{ va.currentVersion }}
|
||||
span.version {{ va.description }}
|
||||
.right
|
||||
svg.icon.icon-shengji(aria-hidden='true' v-bind:title="'升级 '+va.name" @click.stop="updatePack(i, va.name)")
|
||||
use(xlink:href='#icon-shengji')
|
||||
svg.icon.icon-delete(aria-hidden='true' v-bind:title="'删除:'+va.name" @click.stop="deletePack(va,i, va.name)")
|
||||
use(xlink:href='#icon-delete')
|
||||
svg.icon.icon-shouyeshouye(aria-hidden='true' title="官网" @click.stop="WebSite(va.website)")
|
||||
use(xlink:href='#icon-shouyeshouye')
|
||||
.no_dependent(v-else)
|
||||
svg.icon.icon-empt(aria-hidden='true')
|
||||
use(xlink:href='#icon-empty')
|
||||
div 无依赖
|
||||
|
||||
el-dialog(title='操作错误', v-bind:destroy-on-close="true"
|
||||
,v-bind:show-close="false"
|
||||
,v-bind:visible.sync='isShowError', width='60%')
|
||||
.errorText(v-html="ErrorText")
|
||||
span.dialog-footer(slot='footer')
|
||||
el-button(type='primary', @click='isShowError = false') 确 定
|
||||
|
||||
el-dialog.Dependencies.nodrag(title="安装依赖" v-bind:visible.sync="isShowInstallDev"
|
||||
@close="isShowInstallDev = false"
|
||||
width="60%" v-bind:close-on-click-modal="false" v-bind:destroy-on-close="true")
|
||||
.Dependencies_item
|
||||
el-input.input-with-select(v-model="PlugInput" placeholder='请输入插件名' clearable)
|
||||
el-select(placeholder='选择安装环境' v-model="Environment" clearable)
|
||||
el-option(label='生产环境', v-bind:value='0')
|
||||
el-option(label='开发环境', v-bind:value='1')
|
||||
|
||||
el-button(plain @click="SearchList") 安 装
|
||||
|
||||
|
||||
.nodata(v-else) 没有数据
|
||||
1
src/application/page/PlugInfo/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
2c7b3364-a2c8-4c89-a043-190ec49c950d
|
||||
13
src/application/page/PlugInfo/PlugInfo.jade
Normal file
@@ -0,0 +1,13 @@
|
||||
extends ../../layout/index
|
||||
|
||||
block title
|
||||
|
||||
title #{title}
|
||||
link(rel='stylesheet', href='../../assets/css/PlugInfo.css')
|
||||
|
||||
block content
|
||||
#app
|
||||
.PlugInfo PlugInfo
|
||||
|
||||
block script
|
||||
script(src="../../assets/js/page/PlugInfo.js")
|
||||
1
src/application/page/Setting/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
a5a530a8-507a-4fa1-9518-27ff186de94e
|
||||
109
src/application/page/Setting/Setting.jade
Normal file
@@ -0,0 +1,109 @@
|
||||
.Setting
|
||||
.title
|
||||
i(class="el-icon-arrow-left" @click="$router.back()")
|
||||
span 偏好设置
|
||||
div.warp
|
||||
.warp_title
|
||||
span 主题设置
|
||||
//- span.custom 自定义
|
||||
ul.theme_list
|
||||
li(v-for="(item,index) in $store.getters.get_list"
|
||||
@click="changeTheme(index)"
|
||||
v-bind:style="{ background : item.BgColor, color : item.textColor }")
|
||||
span {{ item.text }}
|
||||
i.el-icon-circle-close(v-if="item.system" title="删除" @click.stop="deleteTheme(item,index)")
|
||||
.back(v-bind:style="{ background : item.activeBgColor }")
|
||||
|
||||
el-popover(class="add" placement='bottom-start', title='注意(必看):', width='280', trigger='hover')
|
||||
div 1:建议使用竖图尺寸壁纸,显示效果最佳
|
||||
div 2:本地图片改文件名为主题名,长度<=8
|
||||
div 3:本地图片上传后,勿要再移动图片位置
|
||||
div 4:自定义主题不会上传您的图片,请安心
|
||||
.custom(slot='reference' @click="isShowDialog = true")
|
||||
i.el-icon-plus
|
||||
.login_start
|
||||
span 开启透明度:
|
||||
el-checkbox(v-model='$store.state.mask.open'
|
||||
@change="openChange"
|
||||
v-bind:style="{'--fill-color': $store.getters.get_list[$store.getters.get_theme].type == 'color' ? $store.getters.get_list[$store.getters.get_theme].BgColor : $store.getters.get_list[$store.getters.get_theme].activeBgColor }")
|
||||
|
||||
el-slider(v-if="$store.state.mask.open"
|
||||
class="silder_opacity"
|
||||
input-size="mini"
|
||||
@input="change('opacity')"
|
||||
v-bind:style="{'--fill-color': $store.getters.get_list[$store.getters.get_theme].type == 'color' ? $store.getters.get_list[$store.getters.get_theme].BgColor : $store.getters.get_list[$store.getters.get_theme].activeBgColor }"
|
||||
v-bind:max="1"
|
||||
tooltip-class="tooltip"
|
||||
v-bind:step="0.025"
|
||||
v-model='$store.state.mask.opacity')
|
||||
|
||||
.login_start
|
||||
span(style="margin-right: 9px;") 背景模糊度:
|
||||
el-slider(class="silder_opacity"
|
||||
input-size="mini"
|
||||
@input="change('blur')"
|
||||
v-bind:style="{'--fill-color': $store.getters.get_list[$store.getters.get_theme].type == 'color' ? $store.getters.get_list[$store.getters.get_theme].BgColor : $store.getters.get_list[$store.getters.get_theme].activeBgColor }"
|
||||
v-bind:max="20"
|
||||
tooltip-class="tooltip"
|
||||
v-bind:step="0.025"
|
||||
v-model='$store.state.mask.blur')
|
||||
|
||||
.login_start
|
||||
span(style="margin-right: 9px;") 包管理工具选择:
|
||||
el-select(v-model="typeText", placeholder="请选择", @change="TypeChange")
|
||||
el-option(v-for='(item, index) in $store.state.packageType', v-bind:key="index", v-bind:label="item", v-bind:value="index")
|
||||
|
||||
.login_start
|
||||
span(style="margin-right: 9px;") Node 解释器:
|
||||
.path {{ $store.state.envVariable }}
|
||||
el-link(@click="selectEnvPath" target='_blank' v-bind:style="{'--fill-color': $store.getters.get_list[$store.getters.get_theme].type == 'color' ? $store.getters.get_list[$store.getters.get_theme].BgColor : $store.getters.get_list[$store.getters.get_theme].activeBgColor }") 自定义
|
||||
|
||||
|
||||
|
||||
div.warp
|
||||
.warp_title 系统设置
|
||||
.login_start
|
||||
span 是否开机自启:
|
||||
el-checkbox(v-model='$store.state.isOpen' v-bind:key="$store.getters.get_theme"
|
||||
@change="changeIsOpen"
|
||||
v-bind:style="{'--fill-color': $store.getters.get_list[$store.getters.get_theme].type == 'color' ? $store.getters.get_list[$store.getters.get_theme].BgColor : $store.getters.get_list[$store.getters.get_theme].activeBgColor }")
|
||||
|
||||
.login_start
|
||||
span 版本:
|
||||
.version
|
||||
.current_version 当前版本 v{{$store.getters.get_vsersion}}
|
||||
.check_version(@click="$store.dispatch('checkUpdate')") 检查更新
|
||||
|
||||
el-dialog.custom_dialog(title='自定义主题', :visible.sync='isShowDialog', width='80%')
|
||||
.custom_wrap
|
||||
.action(v-if="/^#/.test(custom.BgColor)")
|
||||
.upload_local
|
||||
input(type="file" @change="uploadFile")
|
||||
i.el-icon-upload2
|
||||
p 本地上传
|
||||
|
||||
.upload_network
|
||||
el-popover(placement='left', width='180', v-model='visible')
|
||||
el-input(v-model='networkURL', clearable, placeholder='粘贴图片地址'
|
||||
@input="networkChange")
|
||||
div(slot='reference')
|
||||
i.el-icon-upload
|
||||
p 网络上传
|
||||
|
||||
el-image(style="width: 100%;height: 100%;" v-else v-bind:src='custom.BgColor | ', fit='cover')
|
||||
|
||||
.custom_footer
|
||||
.left
|
||||
.color_block
|
||||
span 文字色
|
||||
input(type="color"
|
||||
v-bind:value="custom.textColor"
|
||||
v-model="custom.textColor")
|
||||
.color_block
|
||||
span 激活背景色
|
||||
input(type="color" v-bind:value="custom.activeBgColor" v-model="custom.activeBgColor")
|
||||
.right
|
||||
el-button(@click='reselect') 重新选图
|
||||
el-button(type='primary', @click='completeSelection') 完成选择
|
||||
|
||||
link(rel='stylesheet', href='../../assets/css/Setting.css')
|
||||
1
src/application/page/Welcome/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
26594073-16dc-4101-9ea5-3c6d379d7b47
|
||||
17
src/application/page/Welcome/Welcome.jade
Executable file
@@ -0,0 +1,17 @@
|
||||
extends ../../layout/index
|
||||
|
||||
block title
|
||||
|
||||
title #{title}
|
||||
link(rel='stylesheet', href='../../assets/css/Welcome.css')
|
||||
|
||||
block content
|
||||
#app
|
||||
.welcome
|
||||
.title Easy Project
|
||||
img.logo(src="../../assets/img/project_1.png")
|
||||
.desc 统一管理组织凌乱无序的软件项目,带来一站式的快感。
|
||||
el-button.nodrag(type="text" @click="OpenHome") 开始使用
|
||||
|
||||
block script
|
||||
script(src="../../assets/js/page/Welcome.js")
|
||||
1
src/core/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
42927cff-8842-4b5c-98b9-4ef72d76be57
|
||||
1
src/core/annotation/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
ae18ca61-49b8-41bd-b4e6-8d01591c8336
|
||||
63
src/core/annotation/Creted.annotation.ts
Executable file
@@ -0,0 +1,63 @@
|
||||
import Config from "@config/Index.config";
|
||||
import Utils from "@utils/Index.utils";
|
||||
import {Http} from "@net/Http.net";
|
||||
|
||||
/**
|
||||
* @Ipc()
|
||||
* 实现居中IPC的注册,用在controller方法上
|
||||
* @param IpcParams 传入需要被注册的ipc类
|
||||
* @constructor
|
||||
*/
|
||||
export function Ipc(IpcParams: { new (...args: any[]): {}; }[] ) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
/**
|
||||
* 因为TS没有提供像java那种可以直接提取某个类中所有方法的系统方法
|
||||
* 这边原本使用 Object.getOwnPropertyNames 能够正常提取类中方法,但是如果类中有被 @Inject() 依赖注入的属性
|
||||
* 那么这个属性也会被 (new IpcParams())[val](),这样是肯定报错的,所以做下面约定!
|
||||
* 在IPC 类中,如果在类的属性上使用 @Inject() 注入。那么请在类的属性名前加上 _,例如:
|
||||
* @Inject()
|
||||
* private readonly _Net!: Http;
|
||||
*/
|
||||
IpcParams.forEach((val: { new (): any; } ) => {
|
||||
Object.getOwnPropertyNames(val.prototype).splice(1)
|
||||
.filter(f => !f.includes("_"))
|
||||
.forEach(v => (new val())[v]())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @CreateApplicationIpc()
|
||||
* 实现全局IPC的注册
|
||||
* 能够自动实例化传入的类数组中的所有类,并自动调用类中的所有的方法
|
||||
* @param IpcParams 类数组,实例:[ classIpc1, classIpc2, classIpc3 ]
|
||||
* @constructor
|
||||
*/
|
||||
export function CreateApplicationIpc(IpcParams: { new (...args: any[]): {}; }[]): any {
|
||||
return (_constructor: { new (...args: any[]): {}; } ) => {
|
||||
IpcParams.forEach((val: { new (): any; } ) => {
|
||||
Object.getOwnPropertyNames(val.prototype).splice(1)
|
||||
.filter(f => !f.includes("_"))
|
||||
.forEach(v => (new val())[v]())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Render()
|
||||
* 创建窗口
|
||||
* @param templateName 要渲染的模板名称
|
||||
* @constructor
|
||||
*/
|
||||
export function Render(templateName?: string) {
|
||||
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
||||
/**
|
||||
* 如果被打上注解的类中某个方法是配置文件中的默认启动窗口,那么调用这个方法来创建窗体
|
||||
* 其他的方法一律不管!由js前端渲染进程触发ipc创建
|
||||
*/
|
||||
if (propertyKey.includes((Config.StartPage.split("/"))[1])) {
|
||||
// 如果没有传参数,那么采用方法名来创建窗体!
|
||||
templateName == undefined ? Utils.startWindows(target,propertyKey) : Utils.startWindows(target,templateName)
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/core/annotation/Http.annotation.ts
Executable file
@@ -0,0 +1,40 @@
|
||||
import Config from "@config/Index.config";
|
||||
import {Http} from "@net/Http.net";
|
||||
|
||||
/**
|
||||
* @GET()
|
||||
* 用在service类的方法上。
|
||||
* 如果不传参数将自动根据方法名获取接口请求的地址
|
||||
* 如果传参了,那么使用用户传入的地址去发送网络请求
|
||||
*
|
||||
* 如果 useHandle 为 true 那么方法将获得ajax请求的数据
|
||||
* 否则 不会将数据给方法
|
||||
* @param RequestParams
|
||||
* @constructor
|
||||
*/
|
||||
export function GET (RequestParams?: { url?: string, useHandle?: boolean, header?: { [index: string]: any } }) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
let url: string = Config.ApiUrl.ApiList[propertyKey],
|
||||
handle: boolean = false,
|
||||
header: object = {};
|
||||
|
||||
(RequestParams && RequestParams.url)
|
||||
? url = RequestParams.url
|
||||
: (RequestParams && RequestParams.useHandle)
|
||||
? handle = RequestParams.useHandle
|
||||
: (RequestParams && RequestParams.header)
|
||||
? header = RequestParams.header
|
||||
: '';
|
||||
|
||||
let OldMethods = descriptor.value;
|
||||
if (handle) {
|
||||
descriptor.value = async (data: { [key:string]: any }) =>
|
||||
await OldMethods.apply(target, [ await (new Http).GET({ url: url, data: data, header: header }) ])
|
||||
return descriptor
|
||||
} else {
|
||||
descriptor.value = async (data: { [key:string]: any }) =>
|
||||
await (new Http).GET({ url: url, data: data, header: header })
|
||||
return descriptor
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/core/annotation/Ioc.annotation.ts
Executable file
@@ -0,0 +1,37 @@
|
||||
import 'reflect-metadata';
|
||||
import IocModel from "@model/Ioc.model";
|
||||
|
||||
/**
|
||||
* 收集类依赖
|
||||
* @constructor
|
||||
*/
|
||||
export function Injectable() {
|
||||
return (_constructor: { new (...args: any[]): {}; }) => {
|
||||
if(IocModel.classPool.indexOf(_constructor) !== -1) {
|
||||
throw new Error('无需重复收集类');
|
||||
} else {
|
||||
//注册
|
||||
IocModel.classPool = [_constructor]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将类依赖实例化然后注入到被装饰的属性中
|
||||
* @constructor
|
||||
*/
|
||||
export function Inject() {
|
||||
return function (target: any, propertyName: string) {
|
||||
/**
|
||||
* 使用 reflect-metadata 提供的内置 类型元数据键 design:type 通过反射拿到被装饰属性的类型
|
||||
* 也就是 类属性要实例化 的 service 类
|
||||
*/
|
||||
const propertyType: any = Reflect.getMetadata('design:type', target, propertyName);
|
||||
if (IocModel.classPool.indexOf(propertyType) == -1) {
|
||||
throw new Error('被装饰的属性所属的变量类型类,没有被装饰器@Injectable()注入,请检查!');
|
||||
} else {
|
||||
// 从存取器的数组中通过下标取出被装饰属性对应的service类,然后实例化这个类,在放入被装饰的属性中
|
||||
target[propertyName] = new (IocModel.classPool[IocModel.classPool.indexOf(propertyType)])()
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/core/annotation/Run.annotation.ts
Executable file
@@ -0,0 +1,16 @@
|
||||
import Utils from "@utils/Index.utils";
|
||||
import {ApplicationMenu} from "@interactive/ApplicationMenu.interactive";
|
||||
|
||||
export function StartWindow(): any {
|
||||
return (_constructor: {new(...args:any[]):{}} ) => {
|
||||
return class extends _constructor {
|
||||
constructor() {
|
||||
// 这里只要动态 require 导入类即可,然后类上的装饰器就会运行
|
||||
Utils.GetController()
|
||||
// 创建顶部菜单
|
||||
new ApplicationMenu()
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/core/config/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
3392576f-5dfc-4270-9335-0fcac5cdbeb3
|
||||
401
src/core/config/Index.config.ts
Executable file
@@ -0,0 +1,401 @@
|
||||
import { PagePath, PageSize } from "@type/PageConfig.types";
|
||||
import { TrayConfig } from "@type/Tray.types";
|
||||
import { ApiUrlConfig } from "@type/ApiConfig";
|
||||
import {
|
||||
AboutPanelOptionsOptions,
|
||||
app,
|
||||
dialog,
|
||||
MenuItem,
|
||||
MenuItemConstructorOptions,
|
||||
nativeImage,
|
||||
shell,
|
||||
TouchBar,
|
||||
TouchBarConstructorOptions
|
||||
} from "electron";
|
||||
|
||||
import Windows from "@/core/model/Windows.model";
|
||||
import { join } from "path";
|
||||
import { JadeOptions } from "jade";
|
||||
import Utils from "@utils/Index.utils";
|
||||
import JsonDB from "@utils/db.utils";
|
||||
import { spawn } from "child_process";
|
||||
|
||||
const { TouchBarButton, TouchBarPopover } = TouchBar;
|
||||
|
||||
export default class Config {
|
||||
/**
|
||||
* 启动页面默认为 Home.controller/Home
|
||||
* 程序内部根据这个配置自动载入Home.controller.ts文件,并自动调用类里面的Home()方法实现窗口创建
|
||||
* 可以改成其他,例如 PlayVideo.controller/Theme
|
||||
*/
|
||||
public static StartPage: string = 'Home.controller/Home';
|
||||
|
||||
public static isMac: Boolean = process.platform === 'darwin';
|
||||
public static isWindows: Boolean = process.platform === 'win32';
|
||||
public static isLinux: Boolean = process.platform === 'linux';
|
||||
public static LogsPath: string = "../../../logs/";
|
||||
|
||||
public static isShowDockIcon: boolean = JsonDB.findOne("isShowDockIcon", "setting");
|
||||
// 是否开机自启 默认 false
|
||||
public static isLoginOpen: boolean = JsonDB.findOne("isOpen", "setting");
|
||||
|
||||
public static default = Config.isShowDockIcon ? app.dock.hide() : app.dock.show()
|
||||
// 升级地址
|
||||
public static UpdateUrl: string = "https://api.github.com/repos/helpcode/EasyProject/releases/latest";
|
||||
|
||||
// ajax 请求地址
|
||||
public static ApiUrl: ApiUrlConfig = {
|
||||
BaseUrl: 'https://registry.npmjs.org',
|
||||
ApiList: {
|
||||
PlayList: '/list', // 获取视频列表
|
||||
PalyVideo: '/play', // 获取视频播放地址
|
||||
VideoComment: '/CommentList', // 获取视频评论
|
||||
}
|
||||
};
|
||||
|
||||
// jade 模板引擎配置,更多参数自行阅读声明文件
|
||||
public static jadeCompile0ptions: JadeOptions = {
|
||||
cache: true,
|
||||
pretty: true, // 编译输出后是否保留源码格式,true 保持
|
||||
globals: {
|
||||
css: [
|
||||
'../../assets/js/lib/index.css',
|
||||
],
|
||||
js: [
|
||||
'../../assets/js/lib/vue.js',
|
||||
'../../assets/js/lib/vuex.js',
|
||||
'../../assets/js/lib/vue-router.js',
|
||||
'../../assets/js/lib/axios.js',
|
||||
'../../assets/js/lib/index.js',
|
||||
'../../assets/js/lib/font.js'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// 应用程序的页面地址,地址为打包后的相对路径
|
||||
public static PagePath: PagePath = {
|
||||
Welcome: join(__dirname, '../../application/page/Welcome/Welcome.html'),
|
||||
Home: join(__dirname, '../../application/page/Home/Home.html'),
|
||||
PlugInfo: join(__dirname, '../../application/page/PlugInfo/PlugInfo.html')
|
||||
};
|
||||
|
||||
// 关于App
|
||||
public static appAbout: AboutPanelOptionsOptions = {
|
||||
applicationName: "EasyProject",
|
||||
copyright: "copyright @bmy",
|
||||
website: "https://github.com/helpcode",
|
||||
credits: "统一管理组织凌乱无序的软件项目,带来一站式的快感。"
|
||||
};
|
||||
|
||||
// 所有页面窗体配置
|
||||
public static PageSize: PageSize = {
|
||||
Welcome: {
|
||||
width: 330,
|
||||
height: 520,
|
||||
frame: false,
|
||||
backgroundColor: '#00000000',
|
||||
titleBarStyle: 'hidden',
|
||||
transparent: true,
|
||||
simpleFullscreen: true,
|
||||
vibrancy: 'menu',
|
||||
hasShadow: false,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
webSecurity: false,
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
},
|
||||
Home: {
|
||||
width: 1290,
|
||||
height: 810,
|
||||
minHeight: 630,
|
||||
minWidth: 954,
|
||||
frame: false,
|
||||
backgroundColor: '#00000000',
|
||||
titleBarStyle: 'hidden',
|
||||
transparent: true,
|
||||
simpleFullscreen: true,
|
||||
hasShadow: true,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
webSecurity: false,
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
},
|
||||
PlugInfo: {
|
||||
width: 860,
|
||||
height: 500,
|
||||
frame: false,
|
||||
modal: true,
|
||||
backgroundColor: '#fff',
|
||||
titleBarStyle: 'hidden',
|
||||
transparent: true,
|
||||
simpleFullscreen: true,
|
||||
webPreferences: {
|
||||
webSecurity: false,
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Mac系统顶部全局菜单 右边图标
|
||||
public static TrayConfig: TrayConfig = {
|
||||
TopMenuRightImage: join(__dirname, '../../application/assets/img/logoTemplate.png'),
|
||||
TopMenuRightDropdown: [
|
||||
{
|
||||
label: '显示主窗口',
|
||||
click: (menuItem: any, browserWindow: any, event: any) => {
|
||||
Windows.CurrentBrowserWindow.show();
|
||||
}
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: '导入项目',
|
||||
accelerator: "Cmd+i",
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
let res = await Utils.ImportProject((item) => {
|
||||
console.log("Tray 导入的项目监听后被修改路径:", item)
|
||||
// event.reply('DirRemove', item)
|
||||
Windows.CurrentBrowserWindow.webContents.send('DirRemove', item)
|
||||
});
|
||||
if (!Windows.CurrentBrowserWindow.webContents.getURL().includes("Welcome.html")) {
|
||||
Windows.CurrentBrowserWindow.webContents.send('TouchBarImportProject', res)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// TODO 导入 项目 的时候,这边项目列表不更新
|
||||
// label: '项目列表(功能暂未全实现)',
|
||||
label: '项目列表',
|
||||
submenu: (JsonDB.project() as Array<any>).map((v:any,index) => {
|
||||
return {
|
||||
label: `${v.name}`,
|
||||
submenu: [
|
||||
{
|
||||
label: '在访达打开',
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
await shell.openPath(v.Fullpath);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '在终端打开',
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
spawn ('open', [ '-a', 'Terminal', v.Fullpath ])
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '在应用打开',
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
Windows.CurrentBrowserWindow.show();
|
||||
Windows.CurrentBrowserWindow.webContents.send('openInfo', {
|
||||
item: v, index
|
||||
})
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: "设置...",
|
||||
accelerator: "Cmd+,",
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
if (!Windows.CurrentBrowserWindow.webContents.getURL().includes("Welcome.html")) {
|
||||
Windows.CurrentBrowserWindow.show();
|
||||
Windows.CurrentBrowserWindow.webContents.send('openSetting')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "开机自启",
|
||||
type: "checkbox",
|
||||
checked: Config.isLoginOpen,
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
console.log("menuItem: ", menuItem.checked)
|
||||
JsonDB.update("isOpen", menuItem.checked, "setting");
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '隐藏Dock图标',
|
||||
type: 'checkbox',
|
||||
checked: Config.isShowDockIcon,
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
JsonDB.update("isShowDockIcon", menuItem.checked, "setting");
|
||||
menuItem.checked ? app.dock.hide() : app.dock.show()
|
||||
}
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: `重启应用`,
|
||||
click: () => {
|
||||
Utils.killAllTask(() => {
|
||||
app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) })
|
||||
app.exit(0)
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: `杀死 ${ app.name }`,
|
||||
click: () => {
|
||||
Utils.killAllTask(() => {
|
||||
app.exit(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
],
|
||||
TopMenuRightTips: '测试提醒'
|
||||
};
|
||||
|
||||
// Mac or win 系统顶部全局菜单
|
||||
public static TemplateMenu: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||
{
|
||||
label: app.name,
|
||||
submenu: [
|
||||
{ label: `关于 ${ app.name }`, role: 'about' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: "偏好设置",
|
||||
accelerator: "Cmd+,",
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
if (!Windows.CurrentBrowserWindow.webContents.getURL().includes("Welcome.html")) {
|
||||
Windows.CurrentBrowserWindow.show();
|
||||
Windows.CurrentBrowserWindow.webContents.send('openSetting')
|
||||
}
|
||||
}
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: '服务', role: 'services' },
|
||||
{ type: 'separator' },
|
||||
{ label: `隐藏 ${ app.name }`, role: 'hide' },
|
||||
{ label: '隐藏其他', role: 'hideOthers' },
|
||||
{ label: '隐藏其他2', role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{ label: `退出${ app.name }`, role: 'quit' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "文件",
|
||||
submenu: [
|
||||
{
|
||||
label: '导入项目',
|
||||
accelerator: "Cmd+i",
|
||||
click: async (menuItem: any, browserWindow: any, event: any) => {
|
||||
let res = await Utils.ImportProject(item => {
|
||||
Windows.CurrentBrowserWindow.webContents.send('DirRemove', item)
|
||||
});
|
||||
if (!Windows.CurrentBrowserWindow.webContents.getURL().includes("Welcome.html")) {
|
||||
Windows.CurrentBrowserWindow.show();
|
||||
Windows.CurrentBrowserWindow.webContents.send('TouchBarImportProject', res)
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '编辑',
|
||||
submenu: [
|
||||
{ label: '撤销', role: 'undo' },
|
||||
{ label: '恢复', role: 'redo' },
|
||||
{ type: 'separator' },
|
||||
{ label: '剪切', role: 'cut' },
|
||||
{ label: '复制', role: 'copy' },
|
||||
{ label: '粘贴', role: 'paste' },
|
||||
{ type: 'separator' },
|
||||
{ label: '粘贴保留样式', role: 'pasteAndMatchStyle' },
|
||||
{ label: '删除', role: 'delete' },
|
||||
{ label: '全选', role: 'selectAll' },
|
||||
{ type: 'separator' },
|
||||
{ label: '听写', submenu: [ { label: '开始听写', role: 'startSpeaking' }, { label: '停止听写', role: 'stopSpeaking' } ] }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '窗口',
|
||||
submenu: [
|
||||
{ label: '最小化', role: 'minimize' },
|
||||
{ label: '最大化', role: 'zoom' },
|
||||
{ label: '关闭', role: 'close' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '帮助',
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: '联系作者',
|
||||
click: async () => {
|
||||
await dialog.showMessageBox({
|
||||
type: "info",
|
||||
title: '联系作者',
|
||||
message: "如果你有任何使用建议或者反馈Bug\n请添加QQ:2271608011"
|
||||
})
|
||||
}
|
||||
},
|
||||
app.isPackaged
|
||||
? {
|
||||
label: 'GitHub 主页',
|
||||
click: async () => {
|
||||
await shell.openExternal("https://github.com/helpcode")
|
||||
}
|
||||
}
|
||||
: { label: '切换开发人员工具', role: 'toggleDevTools' },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Mac touchbar 菜单
|
||||
public static TouchBarConfig: TouchBarConstructorOptions = {
|
||||
items: [
|
||||
new TouchBarButton({
|
||||
label: '导入项目',
|
||||
iconPosition: "left",
|
||||
icon: join(__dirname, '../../application/assets/img/+normal@2x.png'),
|
||||
click: async () => {
|
||||
let res = await Utils.ImportProject((item) => {
|
||||
console.log("TouchBar 导入的项目监听后被修改路径:", item)
|
||||
Windows.CurrentBrowserWindow.webContents.send('DirRemove', item)
|
||||
});
|
||||
if (!Windows.CurrentBrowserWindow.webContents.getURL().includes("Welcome.html")) {
|
||||
Windows.CurrentBrowserWindow.webContents.send('TouchBarImportProject', res)
|
||||
}
|
||||
}
|
||||
}),
|
||||
new TouchBarPopover({
|
||||
label: '帮助',
|
||||
showCloseButton: true,
|
||||
icon: (nativeImage.createFromPath(join(__dirname, '../../application/assets/img/help.png'))).resize({
|
||||
width: 16,
|
||||
height: 16
|
||||
}),
|
||||
items: new TouchBar({
|
||||
items: [
|
||||
new TouchBarButton({
|
||||
label: '联系作者',
|
||||
backgroundColor: '#3a3a3c',
|
||||
click: async () => {
|
||||
await dialog.showMessageBox({
|
||||
type: "info",
|
||||
title: '联系作者',
|
||||
message: "如果你有任何使用建议或者反馈Bug\n请添加 QQ:2271608011"
|
||||
})
|
||||
}
|
||||
}),
|
||||
new TouchBarButton({
|
||||
label: 'Github主页',
|
||||
backgroundColor: '#3a3a3c',
|
||||
click: async () => {
|
||||
await shell.openExternal("https://github.com/helpcode")
|
||||
}
|
||||
})
|
||||
]
|
||||
})
|
||||
}),
|
||||
]
|
||||
};
|
||||
}
|
||||
1
src/core/controller/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
d66ac9d5-fd1e-43c7-9839-9363bad2256d
|
||||
47
src/core/controller/Home.controller.ts
Executable file
@@ -0,0 +1,47 @@
|
||||
import { Render, Ipc } from "@annotation/Creted.annotation";
|
||||
import { HomeImplService } from "@service/impl/Home.impl.service";
|
||||
import { Inject } from "@annotation/Ioc.annotation";
|
||||
import { FileChice } from "@ipc/module/File.ipc";
|
||||
import {app, Menu, shell, MenuItemConstructorOptions, MenuItem} from "electron";
|
||||
import { Http } from "@net/Http.net";
|
||||
import Config from "@config/Index.config";
|
||||
|
||||
export class HomeController {
|
||||
|
||||
@Inject()
|
||||
public readonly HomeImplService!: HomeImplService;
|
||||
|
||||
@Inject()
|
||||
public readonly _Http!: Http;
|
||||
|
||||
/**
|
||||
* 启动欢迎页面
|
||||
* @constructor
|
||||
*/
|
||||
@Render()
|
||||
public async Welcome() {
|
||||
return {
|
||||
title: '欢迎'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主页面
|
||||
* @constructor
|
||||
*/
|
||||
@Render()
|
||||
@Ipc([ FileChice ])
|
||||
public async Home(params?: { [key:string]:any }) {
|
||||
return {
|
||||
title: '首页'
|
||||
}
|
||||
}
|
||||
|
||||
@Render()
|
||||
public async PlugInfo(params?: { [ key:string ] :any }) {
|
||||
console.log("插件详情页面接收到的参数:", params);
|
||||
return {
|
||||
title: '插件详情页面'
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/core/interactive/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
c4c2b685-cd72-4295-bff5-a47af45c77a9
|
||||
16
src/core/interactive/ApplicationMenu.interactive.ts
Executable file
@@ -0,0 +1,16 @@
|
||||
import {app, Menu, shell, MenuItemConstructorOptions, MenuItem} from "electron";
|
||||
import Config from "@config/Index.config";
|
||||
|
||||
export class ApplicationMenu {
|
||||
private AppMenu!: Menu;
|
||||
constructor() {
|
||||
this.buildFromTemplate();
|
||||
}
|
||||
|
||||
buildFromTemplate(): void {
|
||||
this.AppMenu = Menu.buildFromTemplate(Config.TemplateMenu);
|
||||
Menu.setApplicationMenu(this.AppMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
src/core/interactive/Dialog.interactive.ts
Executable file
@@ -0,0 +1,26 @@
|
||||
import { dialog, Notification, OpenDialogOptions} from "electron";
|
||||
import Windows from "@model/Windows.model"
|
||||
export class Dialog {
|
||||
|
||||
/**
|
||||
* 错误弹窗
|
||||
* @param title
|
||||
* @param content
|
||||
* @constructor
|
||||
*/
|
||||
public static async ErrorBox(title: string, content: string): Promise<void> {
|
||||
await dialog.showErrorBox(title, content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择文件
|
||||
* @param title
|
||||
* @param message
|
||||
*/
|
||||
public static async showDialog(params: OpenDialogOptions): Promise<any> {
|
||||
let SaveFile = await dialog.showOpenDialog(Windows.CurrentBrowserWindow, params);
|
||||
if(!SaveFile.canceled) {
|
||||
return SaveFile.filePaths
|
||||
}
|
||||
}
|
||||
}
|
||||
0
src/core/interactive/ProgressBar.interactive.ts
Executable file
50
src/core/interactive/Touchbar.interactive.ts
Executable file
@@ -0,0 +1,50 @@
|
||||
import { shell, TouchBar, TouchBarButton as TB, nativeImage } from "electron";
|
||||
import Windows from "@/core/model/Windows.model";
|
||||
import Config from "@config/Index.config";
|
||||
import JsonDB from "@utils/db.utils";
|
||||
import { join } from "path";
|
||||
|
||||
const { TouchBarLabel, TouchBarButton, TouchBarPopover } = TouchBar;
|
||||
|
||||
export class Touchbar {
|
||||
constructor() {
|
||||
this.createdAllProject()
|
||||
}
|
||||
|
||||
|
||||
public createdAllProject(): void {
|
||||
let allProject = JsonDB.project();
|
||||
let touchBarButtonArr: Array<TB> = [];
|
||||
// 生成 项目列表 下的项目按钮
|
||||
(allProject as Array<any>).forEach((v, i) => {
|
||||
touchBarButtonArr.push(
|
||||
new TouchBarButton({
|
||||
label: v.name,
|
||||
backgroundColor: '#3a3a3c',
|
||||
click: async () => {
|
||||
await shell.openPath(v.Fullpath)
|
||||
}
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
// 项目列表 Icon
|
||||
let projectIcon = nativeImage.createFromPath(join(__dirname, '../../application/assets/img/project.png'));
|
||||
|
||||
// @ts-ignore
|
||||
if (Config.TouchBarConfig.items[1].label == "项目列表") {
|
||||
// @ts-ignore
|
||||
Config.TouchBarConfig.items.splice(1, 1)
|
||||
}
|
||||
// @ts-ignore
|
||||
Config.TouchBarConfig.items.splice(1, 0, new TouchBarPopover({
|
||||
label: '项目列表',
|
||||
icon: projectIcon.resize({ width: 18, height: 18 }),
|
||||
showCloseButton: true,
|
||||
items: new TouchBar({
|
||||
items: touchBarButtonArr
|
||||
})
|
||||
}))
|
||||
Windows.CurrentBrowserWindow.setTouchBar(new TouchBar(Config.TouchBarConfig))
|
||||
}
|
||||
}
|
||||
32
src/core/interactive/Tray.interactive.ts
Executable file
@@ -0,0 +1,32 @@
|
||||
import { nativeImage, NativeImage, Menu, Tray } from "electron";
|
||||
import Config from "@config/Index.config";
|
||||
import JsonDB from "@utils/db.utils";
|
||||
import Windows from "@/core/model/Windows.model";
|
||||
import Utils from "@utils/Index.utils";
|
||||
|
||||
export class TrayInteractive {
|
||||
|
||||
private tray!: Tray;
|
||||
|
||||
constructor() {
|
||||
this.tray = new Tray(Config.TrayConfig.TopMenuRightImage);
|
||||
Windows.tray = this.tray;
|
||||
this.buildTrayMenu()
|
||||
}
|
||||
|
||||
private buildTrayMenu() {
|
||||
let contextMenu: Menu = Menu.buildFromTemplate(Config.TrayConfig.TopMenuRightDropdown);
|
||||
this.tray.setToolTip(Config.TrayConfig.TopMenuRightTips)
|
||||
Utils.setTrayTitleNums();
|
||||
this.tray.setContextMenu(contextMenu)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 NativeImage 图片,图片会随着Mac系统主题的切换而自定变纯黑或纯白
|
||||
*/
|
||||
// private setTemplateImage(): NativeImage {
|
||||
// let image: NativeImage = nativeImage.createFromPath(Config.TrayConfig.TopMenuRightImage);
|
||||
// image.setTemplateImage(true)
|
||||
// return image.resize({ width: 20, height: 20 })
|
||||
// }
|
||||
}
|
||||
0
src/core/interactive/dock.interactive.ts
Executable file
1
src/core/ipc/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
f5e29150-31cc-40cb-99ed-6527fc3038a9
|
||||
67
src/core/ipc/Application.ipc.ts
Executable file
@@ -0,0 +1,67 @@
|
||||
import {ipcMain, shell} from "electron";
|
||||
import Utils from "@utils/Index.utils";
|
||||
import Windows from "@model/Windows.model";
|
||||
import Config from "@config/Index.config";
|
||||
import { spawn, exec} from "child_process";
|
||||
export class Application {
|
||||
CreatedWindow() {
|
||||
ipcMain.on('openWindow', async (event, arg: any) => {
|
||||
|
||||
let ControllerName: string = arg.action.split("/")[0],
|
||||
MethodsName: string = arg.action.split("/")[1],
|
||||
data: object = arg.data || {}
|
||||
|
||||
// parent false 创建单独的窗体
|
||||
if (!arg.parent || !arg.hasOwnProperty("parent")) {
|
||||
// 是否要关闭上一级窗口
|
||||
if(arg.hasOwnProperty("closeParent") && arg.closeParent) {
|
||||
Windows.CurrentBrowserWindow.hide()
|
||||
}
|
||||
let a = require(`../controller/${ControllerName}.js`);
|
||||
Utils.startWindows(new a[Utils.toUpperCase(ControllerName)](), MethodsName, data)
|
||||
event.returnValue = ""
|
||||
return false
|
||||
}
|
||||
|
||||
// parent true 创建默认依附主窗口的子窗口
|
||||
if (arg.hasOwnProperty("parent") || arg.parent) {
|
||||
// @ts-ignore 合并配置参数,注入 parent: 当前默认启动的父类窗口实例
|
||||
Object.assign(Config.PageSize[MethodsName], { parent: Windows.CurrentBrowserWindow });
|
||||
// 实例化子窗口类,创建窗口
|
||||
let a = require(`../controller/${ControllerName}.js`);
|
||||
Utils.startWindows(new a[Utils.toUpperCase(ControllerName)](), MethodsName, data)
|
||||
event.returnValue = ""
|
||||
return false
|
||||
// 创建默认依附在自定义窗口的子窗口
|
||||
} else {
|
||||
console.log("创建默认依附在自定义窗口的子窗口")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开浏览器窗口
|
||||
*/
|
||||
shellWindows() {
|
||||
ipcMain.on("openExternal", async (event, arg:any) => {
|
||||
await shell.openExternal(arg.url)
|
||||
event.returnValue = "success"
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 在文件管理器打开
|
||||
* 在终端打开(mac系统和win)
|
||||
*/
|
||||
shellOpen() {
|
||||
ipcMain.on("OpenProject", async (event, arg:any) => {
|
||||
arg.type == 'file'
|
||||
? await shell.openPath(arg.path)
|
||||
: Config.isMac
|
||||
? spawn ('open', [ '-a', 'Terminal', arg.path ])
|
||||
: exec(`start cmd.exe /K cd /D ${arg.path}`)
|
||||
|
||||
event.returnValue = "success"
|
||||
});
|
||||
}
|
||||
}
|
||||
1
src/core/ipc/module/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
74566167-ef2f-4b0c-9692-89f4aeef3960
|
||||
543
src/core/ipc/module/File.ipc.ts
Executable file
@@ -0,0 +1,543 @@
|
||||
import { app, ipcMain, shell } from "electron";
|
||||
import { Dialog } from "@interactive/Dialog.interactive";
|
||||
import { Inject } from "@annotation/Ioc.annotation";
|
||||
import { Http } from "@net/Http.net";
|
||||
import Utils from "@utils/Index.utils";
|
||||
import JsonDB from "@utils/db.utils";
|
||||
import { existsSync, readJsonSync, remove, watch } from "fs-extra";
|
||||
import { exec, execSync, spawn } from "child_process";
|
||||
import Config from "@config/Index.config";
|
||||
import kill from "tree-kill";
|
||||
import { basename, dirname, resolve } from "path";
|
||||
import { ProcessUtils } from "@utils/process.utils";
|
||||
import { Package } from "@utils/package.utils";
|
||||
import Windows from "@model/Windows.model";
|
||||
import chokidar, { FSWatcher } from "chokidar";
|
||||
import { WhichBin } from "@utils/whichBin.utils"
|
||||
import moment from "moment"
|
||||
|
||||
export class FileChice {
|
||||
|
||||
@Inject()
|
||||
private readonly _Net!: Http;
|
||||
|
||||
@Inject()
|
||||
private readonly _Package!: Package;
|
||||
|
||||
@Inject()
|
||||
private readonly _WhichBin!: WhichBin;
|
||||
|
||||
// @Inject()
|
||||
private readonly _process: ProcessUtils = new ProcessUtils();
|
||||
|
||||
sendSettingConfig() {
|
||||
ipcMain.on("checkUpdate", async (event, args) => {
|
||||
console.log("升级...")
|
||||
let returnVal: any = {};
|
||||
let res = await this._Net.GET({
|
||||
url: Config.UpdateUrl,
|
||||
custom: undefined,
|
||||
header: {
|
||||
'Authorization': 'Basic aGVscGNvZGU6Z2hwXzBEZmx3cDFQY2l6VVRNOFkxTTNBMDZxQlhTZGpvSjRHak4xMw=='
|
||||
}
|
||||
});
|
||||
if (res != undefined) {
|
||||
// 如果需要更新
|
||||
if (Utils.compare(res.tag_name, app.getVersion())) {
|
||||
returnVal.isUpdate = true;
|
||||
returnVal.detailed = res.body.split("\r\n");
|
||||
returnVal.version = res.tag_name;
|
||||
returnVal.title = res.name;
|
||||
returnVal.down = res.assets[ 0 ].browser_download_url;
|
||||
returnVal.updatedTime = res.assets[ 0 ].updated_at;
|
||||
} else {
|
||||
returnVal.isUpdate = false;
|
||||
}
|
||||
event.returnValue = returnVal
|
||||
} else {
|
||||
returnVal.isUpdate = false;
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on("selectEnvPath", async (event, args) => {
|
||||
let DirectoryPath = await Dialog.showDialog({
|
||||
message: "选择您的Node脚本所在路径",
|
||||
buttonLabel: '选择',
|
||||
properties: [ 'openDirectory', 'showHiddenFiles' ]
|
||||
});
|
||||
if (DirectoryPath) {
|
||||
JsonDB.update("envVariable", DirectoryPath[0])
|
||||
await this._WhichBin.initEnvPath();
|
||||
Utils.killAllTask(() => {
|
||||
app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) })
|
||||
app.exit(0)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on("getVersion", async (event, args) => {
|
||||
event.returnValue = { version: app.getVersion() }
|
||||
})
|
||||
|
||||
ipcMain.on("delete_themeList", async (event, args) => {
|
||||
event.returnValue = JsonDB.remove(args.value, `setting.${ args.keyName }`)
|
||||
})
|
||||
|
||||
ipcMain.on("push_themeList", async (event, args) => {
|
||||
event.returnValue = JsonDB.saveProject(args.value, `setting.${ args.keyName }`)
|
||||
})
|
||||
|
||||
ipcMain.on("setPackageTypeindex", async (event, args) => {
|
||||
try {
|
||||
let res = execSync(`which ${args.packageType}`);
|
||||
console.log("res: ", res.toString())
|
||||
JsonDB.update(args.keyName, args.value)
|
||||
event.returnValue = { status: true, message: null }
|
||||
} catch (e) {
|
||||
event.returnValue = { status: false, message: e.message }
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
ipcMain.on("getSettingConfig", async (event, args) => {
|
||||
event.returnValue = JsonDB.project("setting")
|
||||
})
|
||||
|
||||
ipcMain.on("setSettingConfig", async (event, args) => {
|
||||
JsonDB.update(args.keyName, args.value)
|
||||
if (args.keyName == "isOpen") {
|
||||
const appFolder = dirname(process.execPath)
|
||||
const updateExe = resolve(appFolder, '..', 'EasyProject.app')
|
||||
const exeName = basename(process.execPath)
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: args.value,
|
||||
path: updateExe,
|
||||
args: [
|
||||
'--processStart', `"${ exeName }"`,
|
||||
'--process-start-args', `"--hidden"`
|
||||
]
|
||||
});
|
||||
}
|
||||
// event.returnValue = JsonDB.project("setting")
|
||||
})
|
||||
}
|
||||
|
||||
getProcess() {
|
||||
ipcMain.on("getProcess", async (event, args) => {
|
||||
let res = await this._process.GetAllProcess(args.pid);
|
||||
event.returnValue = res
|
||||
})
|
||||
}
|
||||
|
||||
setPackageJson() {
|
||||
ipcMain.on("setPackageJson", async (event, args) => {
|
||||
let pkg = JsonDB.findName(args.project);
|
||||
let packagePath = `${ pkg.Fullpath }/package.json`;
|
||||
Utils.writePackage(packagePath, args.code)
|
||||
})
|
||||
}
|
||||
|
||||
getPackageJson() {
|
||||
ipcMain.on("getPackageJson", async (event, args) => {
|
||||
let pkg = JsonDB.findName(args.project);
|
||||
let packagePath = `${ pkg.Fullpath }/package.json`;
|
||||
try {
|
||||
event.returnValue = Utils.readPackage(packagePath)
|
||||
} catch (e) {
|
||||
event.returnValue = e.message
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
SaveFile() {
|
||||
ipcMain.on('SaveFile', async (event, arg) => {
|
||||
let SaveFilePath = await Dialog.showDialog({
|
||||
title: "选择路径",
|
||||
message: "选择下载路径",
|
||||
buttonLabel: '亲,点我确认选择!',
|
||||
filters: [
|
||||
{ name: 'All', extensions: [ '*' ] },
|
||||
],
|
||||
properties: [
|
||||
'openDirectory',
|
||||
'createDirectory'
|
||||
]
|
||||
});
|
||||
if (SaveFilePath != undefined) {
|
||||
Utils.DownFile(arg.url, SaveFilePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 从项目列表删除项目
|
||||
*/
|
||||
removeListProject() {
|
||||
// 从项目列表删除项目
|
||||
ipcMain.on("removeListProject", async (event, args) => {
|
||||
let { Fullpath } = JsonDB.findName(args.name,"name");
|
||||
Utils.removeDirWatch(Fullpath);
|
||||
let a = JsonDB.remove({ name: args.name });
|
||||
if (a != undefined) {
|
||||
Utils.updateTouchbarList();
|
||||
Utils.setTrayTitleNums();
|
||||
event.returnValue = "success"
|
||||
} else {
|
||||
event.returnValue = undefined
|
||||
}
|
||||
});
|
||||
|
||||
// 从磁盘删除项目
|
||||
ipcMain.on("removeDistProject", async (event, args) => {
|
||||
let project = JsonDB.findName(args.name, "name");
|
||||
Utils.removeDirWatch(project.Fullpath);
|
||||
try {
|
||||
// 删除到回收站
|
||||
await shell.trashItem(resolve(project.Fullpath))
|
||||
// await remove();
|
||||
// 从本地数据库删除
|
||||
JsonDB.remove({ name: args.name });
|
||||
event.returnValue = "success"
|
||||
} catch (err) {
|
||||
event.returnValue = err.message
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名项目
|
||||
*/
|
||||
reNameProject() {
|
||||
ipcMain.on("reNameProject", async (event, args) => {
|
||||
JsonDB.db.get('projects')
|
||||
.find({ name: args.oldName })
|
||||
.assign({ name: args.newName })
|
||||
.write()
|
||||
Utils.updateTouchbarList();
|
||||
event.returnValue = "success"
|
||||
});
|
||||
}
|
||||
|
||||
changeIcon() {
|
||||
ipcMain.on("ChangeIcon", async (event, args) => {
|
||||
JsonDB.db.get('projects')
|
||||
.find({ name: args.name })
|
||||
.assign({ FolderIcon: args.newIcon })
|
||||
.write()
|
||||
event.returnValue = "success"
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入项目
|
||||
*/
|
||||
openDirectory() {
|
||||
ipcMain.on("openDirectory", async (event, args) => {
|
||||
let res = await Utils.ImportProject((item) => {
|
||||
console.log("导入的项目监听后被修改路径:", item)
|
||||
event.reply('DirRemove', item)
|
||||
});
|
||||
event.returnValue = res;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止命令
|
||||
* @constructor
|
||||
*/
|
||||
StopProject(): void {
|
||||
ipcMain.on("StopCmd", async (event, args) => {
|
||||
console.log("停止命令 args: ", args)
|
||||
let currentTask: any = Utils.findOneTask(args.name, args.ScriptName);
|
||||
kill(currentTask.Child.pid, (err: Error | undefined) => {
|
||||
if (err) {
|
||||
event.returnValue = err.message
|
||||
} else {
|
||||
currentTask.Child = null;
|
||||
currentTask.IsRuning = "idle"
|
||||
currentTask.RunLogs = ""
|
||||
event.returnValue = null
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行命令
|
||||
* @constructor
|
||||
*/
|
||||
RunProject(): void {
|
||||
ipcMain.on("RunCmd", async (event, args) => {
|
||||
console.log("运行命令: ", args)
|
||||
let currentTask: any = Utils.findOneTask(args.name, args.ScriptName);
|
||||
let cmd = args.ScriptName == 'install'
|
||||
? `cd ${ currentTask.path } && ${this._Package.install("i")}`
|
||||
: `cd ${ currentTask.path } && ${this._Package.run(currentTask.ScriptName)}`;
|
||||
|
||||
try {
|
||||
currentTask.Child = spawn(cmd, {
|
||||
cwd: process.cwd(),
|
||||
stdio: [ 'inherit', 'pipe', 'pipe' ],
|
||||
shell: true
|
||||
});
|
||||
|
||||
// 修改运行状态
|
||||
currentTask.IsRuning = "runing"
|
||||
currentTask.time = Date.now()
|
||||
|
||||
currentTask.Child.stdout.on('data', (data: Buffer) => {
|
||||
|
||||
currentTask.RunLogs += data.toString();
|
||||
if (currentTask.Child != null) {
|
||||
currentTask.pid = currentTask.Child.pid;
|
||||
|
||||
// 保存 你正在运行的 pid
|
||||
Windows.runPids.add(currentTask.pid);
|
||||
console.log("运行命令,添加pid: ", Windows.runPids)
|
||||
|
||||
// 后端返回 ScriptName 为前端助力,让前端的 sendMessage 监听
|
||||
// 中知道把 log 日志存放到数组的哪个 v.Terminal 中
|
||||
event.sender.send('sendMessage', {
|
||||
log: data.toString(),
|
||||
projectName: args.name,
|
||||
ScriptName: currentTask.ScriptName,
|
||||
pid: currentTask.Child.pid
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 命令自动运行完成的时候,向前端发送进程关闭的通知
|
||||
currentTask.Child.stdout.on('close', (code: Number, signal: string) => {
|
||||
console.log("5 进程结束")
|
||||
|
||||
// 删除已经停止的 pid
|
||||
Windows.runPids.delete(currentTask.pid);
|
||||
currentTask.pid = 0;
|
||||
|
||||
console.log("停止命令,删除pid: ", Windows.runPids)
|
||||
|
||||
// 关闭进程
|
||||
currentTask.IsRuning = "idle"
|
||||
currentTask.RunLogs = ""
|
||||
currentTask.Child = null;
|
||||
currentTask.time = Date.now() - currentTask.time
|
||||
|
||||
event.sender.send('close', {
|
||||
ScriptName: currentTask.ScriptName,
|
||||
projectName: args.name,
|
||||
time: moment(currentTask.time).format("mm:ss")
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
currentTask.Child.stdout.on('error', (e: any) => {
|
||||
console.log("------121 运行命令产生错误了------:",e)
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("------2 运行命令产生错误了------:",e)
|
||||
}
|
||||
event.returnValue = ""
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目的运行命令
|
||||
* @constructor
|
||||
*/
|
||||
GetTaskList() {
|
||||
ipcMain.on("GetTaskList", async (event, args) => {
|
||||
let project = JsonDB.findName(args.name);
|
||||
|
||||
let scriptList = (Utils.readPackage(`${ project.Fullpath }/package.json`)).scripts;
|
||||
let NewSrciptList = []
|
||||
for (const key in scriptList) {
|
||||
NewSrciptList.push({
|
||||
/**
|
||||
* 进程状态锁
|
||||
* true 表示命令未运行(进程停止时会变为true),前端运行按钮可以点击,
|
||||
* false 在你点击停止按钮后,进程还未被完全杀死前,再次点击运行按钮将无反应
|
||||
* 必须等待进程被完全杀死后,运行按钮才能再次使用
|
||||
* 如果不加锁,在前端 急速反复点击 运行停止按钮 的时候
|
||||
* 会导致进程运行后直接闪退,Mccos上存在
|
||||
*/
|
||||
lock: true,
|
||||
ScriptName: key, // 命令的名称
|
||||
IsRuning: "idle", // idle 未运行 runing 运行
|
||||
RunLogs: '', // 命令运行产生的日志
|
||||
ScriptShell: scriptList[ key ], // 命令的脚本
|
||||
Terminal: null,
|
||||
Child: null,
|
||||
pid: 0,
|
||||
time: 0,
|
||||
})
|
||||
}
|
||||
NewSrciptList.unshift({
|
||||
lock: true,
|
||||
ScriptName: 'install',
|
||||
IsRuning: "idle",
|
||||
RunLogs: '',
|
||||
ScriptShell: this._Package.install("i"),
|
||||
Terminal: null,
|
||||
Child: null,
|
||||
pid: 0,
|
||||
time: 0,
|
||||
});
|
||||
|
||||
// 深拷贝,去除再次回到【任务模块】的时候,Child中保存的子进程无法被ipc发送的问题
|
||||
let newItem = Utils.DeepCopy(Utils.setTask(project.Fullpath, NewSrciptList))
|
||||
newItem.forEach((v: any) => v.Child != null ? v.Child = null : '');
|
||||
event.returnValue = newItem
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用一打开的时候获取项目列表
|
||||
* @constructor
|
||||
*/
|
||||
ProjectList() {
|
||||
ipcMain.on("ProjectList", async (event, args) => {
|
||||
let allProject = JsonDB.project();
|
||||
event.returnValue = allProject.filter((v:any,index: number) => {
|
||||
// 打开软件的时候,判断本地项目路径是否存在
|
||||
// 把存在的返回数据给前端,来展示项目列表
|
||||
if (existsSync(v.Fullpath)) {
|
||||
Utils.addDirWatch(v.Fullpath, () => {
|
||||
event.reply('DirRemove', v)
|
||||
})
|
||||
return v
|
||||
} else {
|
||||
// 路径已经不存在了,删除json
|
||||
console.log("路径已经不存在了,删除json")
|
||||
JsonDB.remove({ name: v.name });
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取依赖
|
||||
* @constructor
|
||||
*/
|
||||
GetDependentList() {
|
||||
ipcMain.on("GetDependentList", async (event, args) => {
|
||||
console.log("获取依赖 ==== args: ", args)
|
||||
let project = JsonDB.findName(args.name);
|
||||
let pkg = Utils.readPackage(`${ project.Fullpath }/package.json`);
|
||||
let ListArr: { title: string, list: { [ key: string ]: any }[] }[] = [];
|
||||
|
||||
// 检查项目中是否有 node_modules
|
||||
if (existsSync(`${project.Fullpath}/node_modules`)) {
|
||||
// 头像地址:https://avatars.dicebear.com/v2/identicon/插件名.svg
|
||||
// 请求地址:https://registry.npmjs.org/插件名
|
||||
[ pkg.dependencies, pkg.devDependencies ].forEach((v, i) => {
|
||||
ListArr.push({
|
||||
title: i == 0 ? '生产环境依赖' : '开发环境依赖',
|
||||
list: []
|
||||
})
|
||||
for (const key in v) {
|
||||
let otherPath = `${project.Fullpath}/node_modules/${key}/package.json`
|
||||
if (existsSync(otherPath)) {
|
||||
const modeulePackage = Utils.readPackage(otherPath);
|
||||
ListArr[ i ].list.push({
|
||||
name: key,
|
||||
logoImg: `https://avatars.dicebear.com/v2/identicon/${ key.replace("/", "-") }.svg`,
|
||||
currentVersion: v[ key ],
|
||||
description: modeulePackage.description,
|
||||
website: modeulePackage.homepage || (modeulePackage.repository && modeulePackage.repository.url) || `https://www.npmjs.com/package/${ key.replace('/', '%2F') }`
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
event.returnValue = undefined
|
||||
}
|
||||
event.returnValue = ListArr
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装依赖
|
||||
* @constructor
|
||||
*/
|
||||
InstallPlug() {
|
||||
ipcMain.on("installPlug", async (event, args) => {
|
||||
let pkg = JsonDB.findName(args.project);
|
||||
let cmd = `cd ${ pkg.Fullpath } && ${this._Package.install("other", args)}`
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
if (error == null) {
|
||||
event.reply('reDependentSuccess', 'ok')
|
||||
} else {
|
||||
// event.returnValue = stderr
|
||||
event.reply('reDependentSuccess', {
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新依赖包
|
||||
*/
|
||||
updateDependencies() {
|
||||
ipcMain.on("uDependencies", async (event, args) => {
|
||||
let cmd, pkg = JsonDB.findName(args.project);
|
||||
cmd = `cd ${ pkg.Fullpath } && ${this._Package.up(args)}`
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
if (error == null) {
|
||||
event.reply('reDependentSuccess', 'ok')
|
||||
} else {
|
||||
console.log(error.message)
|
||||
// event.returnValue = stderr
|
||||
event.reply('reDependentSuccess', {
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除依赖包
|
||||
*/
|
||||
deleteDependencies() {
|
||||
ipcMain.on("dDependencies", async (event, args) => {
|
||||
let cmd, pkg = JsonDB.findName(args.project);
|
||||
cmd = `cd ${ pkg.Fullpath } && ${this._Package.uninstall(args.type)}`;
|
||||
console.log("删除依赖包: ", cmd)
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
console.log(stdout)
|
||||
if (error == null) {
|
||||
event.reply('reDependentSuccess', 'ok')
|
||||
} else {
|
||||
event.reply('reDependentSuccess', {
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化依赖
|
||||
*/
|
||||
installDependent() {
|
||||
ipcMain.on("reDependent", async (event, args) => {
|
||||
console.log("args.name: ", args.name)
|
||||
let pkg = JsonDB.findName(args.name)
|
||||
let cmd = `cd ${ pkg.Fullpath } && rm -rf node_modules && ${this._Package.install("i")}`
|
||||
console.log("初始化依赖: ",cmd)
|
||||
exec(cmd, (error, stdout, stderr) => {
|
||||
if (error == null) {
|
||||
event.reply('reDependentSuccess', 'ok')
|
||||
} else {
|
||||
// event.returnValue = stderr
|
||||
event.reply('reDependentSuccess', {
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
12
src/core/ipc/module/terminate.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
terminateTree() {
|
||||
for cpid in $(/usr/bin/pgrep -P $1); do
|
||||
terminateTree $cpid
|
||||
done
|
||||
kill -9 $1 > /dev/null 2>&1
|
||||
}
|
||||
|
||||
for pid in $*; do
|
||||
terminateTree $pid
|
||||
done
|
||||
1
src/core/model/.pydio
Normal file
@@ -0,0 +1 @@
|
||||
0aab50fe-8286-4b21-b0e4-396e502553da
|
||||
13
src/core/model/Ioc.model.ts
Executable file
@@ -0,0 +1,13 @@
|
||||
class IocModel {
|
||||
private _classPool: Array<{ new (...args: any[]): {}; }> = [];
|
||||
|
||||
get classPool(): Array<{ new(...args: any[]): {} }> {
|
||||
return this._classPool;
|
||||
}
|
||||
|
||||
set classPool(value: Array<{ new(...args: any[]): {} }>) {
|
||||
this._classPool = [...value, ...this._classPool]
|
||||
}
|
||||
}
|
||||
|
||||
export default new IocModel();
|
||||