Initial commit
This commit is contained in:
parent
72f3cc1ad8
commit
a20d1def1b
|
@ -25,3 +25,6 @@ build/Release
|
|||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
#WebStorm
|
||||
.idea
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/18.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var join = path.join;
|
||||
// module to control application life
|
||||
var app = require('app');
|
||||
var ipc = require('ipc-main');
|
||||
var dialog = require('dialog');
|
||||
var shell = require('shell');
|
||||
|
||||
const USERDATA = app.getPath('userData');
|
||||
const USERDESKTOP = app.getPath('userDesktop');
|
||||
const CONFIGURENAME = 'command-manager.config';
|
||||
const CONFIGUREPATH = join(USERDATA, CONFIGURENAME);
|
||||
const DEFAULTCONFIGURE = { projects: [] };
|
||||
const ERRORMESSAGE = {
|
||||
NONEXISTS: '不存在',
|
||||
READERROR: '读取失败',
|
||||
WRITEERROR: '写入失败',
|
||||
PARSEERROR: '解析失败',
|
||||
VALIDERROR: '校验失败'
|
||||
};
|
||||
|
||||
/**
|
||||
* ConfigureError
|
||||
* @param code
|
||||
* @param message
|
||||
* @constructor
|
||||
*/
|
||||
function ConfigureError(code, message){
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.name = 'ConfigureError';
|
||||
}
|
||||
|
||||
// ConfigureError prototype
|
||||
ConfigureError.prototype = Object.create(Error.prototype);
|
||||
ConfigureError.prototype.constructor = ConfigureError;
|
||||
|
||||
/**
|
||||
* verify configure
|
||||
* @param configure
|
||||
* @returns {*}
|
||||
*/
|
||||
function verifyConfigure(configure){
|
||||
if (!configure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Array.isArray(configure.projects)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return configure.projects.every(function (project){
|
||||
if (!project.name || typeof project.name !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(project.env)) {
|
||||
if (
|
||||
!project.env.every(function (env){
|
||||
return env.name && typeof env.name === 'string'
|
||||
&& env.value && typeof env.value === 'string';
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(project.command)) {
|
||||
if (
|
||||
project.command.every(function (command){
|
||||
return command.name && typeof command.name === 'string'
|
||||
&& command.value && typeof command.value === 'string';
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* unique array by a track
|
||||
* @param array
|
||||
* @param progress
|
||||
* @param track
|
||||
* @returns {Array}
|
||||
*/
|
||||
function unique(array, progress, track){
|
||||
var cache = {};
|
||||
|
||||
progress = typeof progress === 'function' ? progress : function (){};
|
||||
track = track && typeof track === 'string' ? track : 'name';
|
||||
|
||||
return array.filter(function (item){
|
||||
var key = item[track];
|
||||
|
||||
if (cache[key]) {
|
||||
return false;
|
||||
} else {
|
||||
cache[key] = true;
|
||||
|
||||
progress.apply(this, arguments);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* filter configure
|
||||
* @param configure
|
||||
* @returns {*}
|
||||
*/
|
||||
function filterConfigure(configure){
|
||||
configure.projects = unique(configure.projects, function (project){
|
||||
if (project.env) {
|
||||
project.env = unique(project.env);
|
||||
}
|
||||
|
||||
if (project.command) {
|
||||
project.command = unique(project.command);
|
||||
}
|
||||
});
|
||||
|
||||
return configure;
|
||||
}
|
||||
|
||||
/**
|
||||
* AppConfigure
|
||||
* @param window
|
||||
* @param tray
|
||||
* @constructor
|
||||
*/
|
||||
function AppConfigure(window, tray){
|
||||
this.window = window;
|
||||
this.tray = tray;
|
||||
this.title = window.getTitle();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* AppConfigure prototype
|
||||
*/
|
||||
AppConfigure.prototype = {
|
||||
init: function (){
|
||||
var context = this;
|
||||
|
||||
this.create();
|
||||
|
||||
ipc.on('app-configure', function (event, command){
|
||||
switch (command) {
|
||||
case 'import':
|
||||
context.import(function (configure){
|
||||
this.showMessageBox('配置文件导入成功!', { type: 'info' });
|
||||
event.sender.send('app-configure', 'refresh', configure);
|
||||
}, function (error){
|
||||
this.showMessageBox('配置文件' + error.message + '!');
|
||||
});
|
||||
break;
|
||||
case 'export':
|
||||
context.export(function (path){
|
||||
this.showMessageBox('配置文件导出成功!', { type: 'info' }, function (){
|
||||
shell.showItemInFolder(path);
|
||||
});
|
||||
}, function (){
|
||||
this.showMessageBox('配置文件导出失败!');
|
||||
});
|
||||
break;
|
||||
case 'refresh':
|
||||
context.read(function (configure){
|
||||
event.sender.send('app-configure', 'refresh', configure);
|
||||
}, function (error){
|
||||
context.showMessageBox('配置文件' + error.message + '!', function (){
|
||||
context.window.close();
|
||||
});
|
||||
});
|
||||
break;
|
||||
case 'add':
|
||||
context.save(function (configure){
|
||||
|
||||
}, function (configure){
|
||||
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
create: function (){
|
||||
var context = this;
|
||||
|
||||
fs.stat(CONFIGUREPATH, function (error, stats){
|
||||
if (error || !stats.isFile()) {
|
||||
context.save(DEFAULTCONFIGURE, null, function (){
|
||||
context.showMessageBox('配置文件创建失败,请用管理员模式运行重试!', function (){
|
||||
context.window.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
save: function (configure, done, fail){
|
||||
var context = this;
|
||||
|
||||
done = typeof done === 'function' ? done : function (){};
|
||||
fail = typeof fail === 'function' ? fail : function (){};
|
||||
|
||||
fs.writeFile(CONFIGUREPATH, JSON.stringify(configure), function (error){
|
||||
if (error) {
|
||||
var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'WRITEERROR';
|
||||
|
||||
fail.call(context, new ConfigureError(code, ERRORMESSAGE[code]));
|
||||
} else {
|
||||
done.call(context, configure);
|
||||
}
|
||||
});
|
||||
},
|
||||
read: function (done, fail){
|
||||
var context = this;
|
||||
|
||||
done = typeof done === 'function' ? done : function (){};
|
||||
fail = typeof fail === 'function' ? fail : function (){};
|
||||
|
||||
fs.readFile(CONFIGUREPATH, function (error, configure){
|
||||
if (error) {
|
||||
var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'READERROR';
|
||||
|
||||
fail.call(context, new ConfigureError(code, ERRORMESSAGE[code]));
|
||||
} else {
|
||||
try {
|
||||
configure = JSON.parse(configure);
|
||||
} catch (error) {
|
||||
return fail.call(context, new ConfigureError('PARSEERROR', ERRORMESSAGE.PARSEERROR));
|
||||
}
|
||||
}
|
||||
|
||||
done.call(context, configure);
|
||||
});
|
||||
},
|
||||
import: function (done, fail){
|
||||
var context = this;
|
||||
|
||||
done = typeof done === 'function' ? done : function (){};
|
||||
fail = typeof fail === 'function' ? fail : function (){};
|
||||
|
||||
// show open dialog
|
||||
dialog.showOpenDialog(this.window, {
|
||||
title: this.title,
|
||||
defaultPath: CONFIGURENAME,
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'Config Files', extensions: ['config'] }]
|
||||
}, function (paths){
|
||||
if (paths) {
|
||||
fs.readFile(paths[0], function (error, configure){
|
||||
if (error) {
|
||||
var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'READERROR';
|
||||
|
||||
fail.call(context, new ConfigureError(code, ERRORMESSAGE[code]));
|
||||
} else {
|
||||
try {
|
||||
configure = JSON.parse(configure);
|
||||
} catch (error) {
|
||||
return fail.call(context, new ConfigureError('PARSEERROR', ERRORMESSAGE.PARSEERROR));
|
||||
}
|
||||
|
||||
// verify configure
|
||||
var invalid = !verifyConfigure(configure);
|
||||
|
||||
// invalid configure
|
||||
if (invalid) {
|
||||
return fail.call(context, new ConfigureError('VALIDERROR', ERRORMESSAGE.VALIDERROR));
|
||||
}
|
||||
|
||||
// filter configure
|
||||
configure = filterConfigure(configure);
|
||||
|
||||
// save configure
|
||||
context.save(configure, done, fail);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
export: function (done, fail){
|
||||
var context = this;
|
||||
|
||||
done = typeof done === 'function' ? done : function (){};
|
||||
fail = typeof fail === 'function' ? fail : function (){};
|
||||
|
||||
// show save dialog
|
||||
dialog.showSaveDialog(this.window, {
|
||||
title: this.title,
|
||||
defaultPath: join(USERDESKTOP, CONFIGURENAME),
|
||||
filters: [{ name: 'Config Files', extensions: ['config'] }]
|
||||
}, function (path){
|
||||
if (path) {
|
||||
fs.createReadStream(CONFIGUREPATH)
|
||||
.on('error', function (error){
|
||||
var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'READERROR';
|
||||
|
||||
fail.call(context, new ConfigureError(code, ERRORMESSAGE[code]));
|
||||
})
|
||||
.pipe(fs.createWriteStream(path))
|
||||
.on('finish', function (){
|
||||
done.call(context, path);
|
||||
})
|
||||
.on('error', function (error){
|
||||
var code = error.code === 'ENOENT' ? 'NONEXISTS' : 'WRITEERROR';
|
||||
|
||||
fail.call(context, new ConfigureError(code, ERRORMESSAGE[code]));
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
showMessageBox: function (message, options, callback){
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = { message: message };
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
options.title = this.title;
|
||||
options.message = message;
|
||||
options.type = options.type || 'error';
|
||||
options.buttons = options.buttons || [];
|
||||
|
||||
dialog.showMessageBox(this.window, options, callback);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = AppConfigure;
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/18.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// module to control application life
|
||||
var ipc = require('ipc-main');
|
||||
var dialog = require('dialog');
|
||||
|
||||
/**
|
||||
* open directory
|
||||
* @param window
|
||||
*/
|
||||
module.exports = function (window){
|
||||
// listen open directory ipc
|
||||
ipc.on('open-directory', function (event, path, uid){
|
||||
dialog.showOpenDialog(window, {
|
||||
title: window.getTitle(),
|
||||
properties: ['openDirectory'],
|
||||
defaultPath: path || ''
|
||||
}, function (directorys){
|
||||
if (directorys) {
|
||||
event.sender.send('select-directory', directorys, uid);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/18.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// module to control application life
|
||||
var ipc = require('ipc-main');
|
||||
|
||||
/**
|
||||
* window control
|
||||
* @param icon
|
||||
* @param window
|
||||
* @param tray
|
||||
*/
|
||||
module.exports = function (icon, window, tray){
|
||||
// bind maximize event
|
||||
window.on('maximize', function (event){
|
||||
event.sender.send('is-maximized', true);
|
||||
});
|
||||
|
||||
// bind unmaximize event
|
||||
window.on('unmaximize', function (event){
|
||||
event.sender.send('is-maximized', false);
|
||||
});
|
||||
|
||||
// bind tray double-click event
|
||||
tray.on('double-click', function (){
|
||||
window.show();
|
||||
});
|
||||
|
||||
// listen window ipc
|
||||
ipc.on('window', function (event, command){
|
||||
switch (command) {
|
||||
case 'tray':
|
||||
var title = window.getTitle();
|
||||
|
||||
window.hide();
|
||||
tray.displayBalloon({
|
||||
icon: icon,
|
||||
title: title,
|
||||
content: title + '正在后台运行!'
|
||||
});
|
||||
break;
|
||||
case 'close':
|
||||
window.close();
|
||||
break;
|
||||
case 'maximize':
|
||||
window.maximize();
|
||||
break;
|
||||
case 'unmaximize':
|
||||
window.unmaximize();
|
||||
break;
|
||||
case 'is-maximized':
|
||||
event.sender.send('is-maximized', window.isMaximized());
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,136 @@
|
|||
@charset "utf-8";
|
||||
|
||||
/* alice.base 样式模块 */
|
||||
|
||||
/* 防止用户自定义背景颜色对网页的影响,添加让用户可以自定义字体 */
|
||||
html {
|
||||
color: #000; background: #fff;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
/* 内外边距通常让各个浏览器样式的表现位置不同 */
|
||||
body, div, dl, dt, dd, ul,
|
||||
ol, li, h1, h2, h3, h4, h5,
|
||||
h6, pre, code, form, fieldset,
|
||||
legend, input, textarea, p, blockquote,
|
||||
th, td, hr, button, article, aside, details,
|
||||
figcaption, figure, footer, header, hgroup, menu, nav, section {
|
||||
margin: 0; padding: 0;
|
||||
}
|
||||
/* 重设 HTML5 标签, IE 需要在 js 中 createElement(TAG) */
|
||||
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
/* HTML5 媒体文件跟 img 保持一致 */
|
||||
audio, canvas, video {
|
||||
display: inline-block; *display: inline; *zoom: 1;
|
||||
}
|
||||
/* 要注意表单元素并不继承父级 font 的问题 */
|
||||
body, button, input, select, textarea {
|
||||
font: 12px/1.5 Microsoft Yahei, tahoma, arial, "Hiragino Sans GB", \5b8b\4f53;
|
||||
}
|
||||
input, select, textarea {
|
||||
font-size: 100%;
|
||||
}
|
||||
/* 去掉各Table cell 的边距并让其边重合 */
|
||||
table {
|
||||
border-collapse: collapse; border-spacing: 0;
|
||||
}
|
||||
/* IE bug fixed: th 不继承 text-align*/
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
/* 去除默认边框 */
|
||||
fieldset, img {
|
||||
border: 0;
|
||||
}
|
||||
/* ie6 7 8(q) bug 显示为行内表现 */
|
||||
iframe {
|
||||
display: block;
|
||||
}
|
||||
/* 去掉 firefox 下此元素的边框 */
|
||||
abbr, acronym {
|
||||
border: 0; font-variant: normal;
|
||||
}
|
||||
/* 一致的 del 样式 */
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
address, caption, cite, code, dfn, em, th, var {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
/* 去掉列表前的标识, li 会继承 */
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
/* 对齐是排版最重要的因素, 别让什么都居中 */
|
||||
caption, th {
|
||||
text-align: left;
|
||||
}
|
||||
/* 来自yahoo, 让标题都自定义, 适应多个系统应用 */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: 100%;
|
||||
font-weight: 500;
|
||||
}
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
}
|
||||
/* 统一上标和下标 */
|
||||
sub, sup {
|
||||
font-size: 75%; line-height: 0; position: relative; vertical-align: baseline;
|
||||
}
|
||||
sup { top: -0.5em; }
|
||||
sub { bottom: -0.25em; }
|
||||
/* 正常链接 未访问 */
|
||||
a:link {
|
||||
color: #08c;
|
||||
}
|
||||
/* 鼠标悬停 */
|
||||
a:hover {
|
||||
color: #08c;
|
||||
text-decoration: underline;
|
||||
}
|
||||
/* 默认不显示下划线,保持页面简洁 */
|
||||
ins, a {
|
||||
text-decoration: none;
|
||||
}
|
||||
/* 代码字体 */
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
/* 清理浮动 */
|
||||
.fn-clear:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: " ";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
.fn-clear {
|
||||
zoom: 1; /* for IE6 IE7 */
|
||||
}
|
||||
/* 隐藏, 通常用来与 JS 配合 */
|
||||
body .fn-hide {
|
||||
display: none;
|
||||
}
|
||||
/* visibility 隐藏, 通常用来与 JS 配合 */
|
||||
body .fn-invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
/* 设置内联, 减少浮动带来的bug */
|
||||
.fn-left,
|
||||
.fn-right {
|
||||
display: inline;
|
||||
}
|
||||
.fn-left {
|
||||
float: left;
|
||||
}
|
||||
.fn-right {
|
||||
float: right;
|
||||
}
|
|
@ -0,0 +1,456 @@
|
|||
@font-face {
|
||||
font-family: font-icon;
|
||||
src: url('../fonts/font-icon.svg#font-icon') format('svg'),
|
||||
url("../fonts/font-icon.woff") format("woff"),
|
||||
url("../fonts/font-icon.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
}
|
||||
[class^="icon-"],
|
||||
[class*=" icon-"] {
|
||||
speak: none;
|
||||
outline: none;
|
||||
line-height: 1;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
font-family: font-icon, sans-serif;
|
||||
/* enable ligatures */
|
||||
-webkit-font-feature-settings: "liga";
|
||||
font-feature-settings: "liga";
|
||||
/* better font rendering */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* min font size */
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
.icon-play:before {
|
||||
content: "\e600";
|
||||
font-size: 16px;
|
||||
color: #2f9c23;
|
||||
vertical-align: 3px;
|
||||
}
|
||||
.icon-stop:before {
|
||||
content: "\e601";
|
||||
font-size: 15px;
|
||||
color: #f00;
|
||||
vertical-align: 3px;
|
||||
}
|
||||
.icon-folder:before {
|
||||
content: "\e602";
|
||||
font-size: 16px;
|
||||
margin-right: 3px;
|
||||
vertical-align: 5px;
|
||||
}
|
||||
.icon-maximize:before {
|
||||
content: "\e603";
|
||||
font-size: 17px;
|
||||
vertical-align: 2px;
|
||||
}
|
||||
.icon-unmaximize:before {
|
||||
content: "\e604";
|
||||
font-size: 17px;
|
||||
vertical-align: 2px;
|
||||
}
|
||||
.icon-plus:before {
|
||||
content: "\e605";
|
||||
font-size: 17px;
|
||||
vertical-align: 2px;
|
||||
}
|
||||
.icon-cross:before {
|
||||
content: "\e606";
|
||||
font-size: 24px;
|
||||
}
|
||||
.icon-import:before {
|
||||
content: "\e607";
|
||||
font-size: 14px;
|
||||
vertical-align: 3px;
|
||||
}
|
||||
.icon-export:before {
|
||||
content: "\e608";
|
||||
font-size: 14px;
|
||||
vertical-align: 3px;
|
||||
}
|
||||
.icon-minimize-tray:before {
|
||||
content: "\e609";
|
||||
font-size: 24px;
|
||||
}
|
||||
.icon-expand:before {
|
||||
content: "\e610";
|
||||
font-size: 20px;
|
||||
}
|
||||
.icon-ellipsis:before {
|
||||
content: "\e611";
|
||||
font-size: 20px;
|
||||
}
|
||||
.icon-trash:before {
|
||||
content: "\e612";
|
||||
font-size: 16px;
|
||||
color: #f00;
|
||||
vertical-align: 2px;
|
||||
}
|
||||
.icon-gear:before {
|
||||
content: "\e613";
|
||||
font-size: 18px;
|
||||
vertical-align: 1px;
|
||||
}
|
||||
.icon-ellipsis {
|
||||
width: 40px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
color: #fff !important;
|
||||
background-color: #f57527;
|
||||
transition: box-shadow .25s ease-in-out;
|
||||
}
|
||||
.icon-ellipsis:hover {
|
||||
box-shadow: 0 0 rgba(34, 25, 25, 0.15) inset, 0 0 rgba(255, 255, 255, 0.8), 0 0 3px rgb(0, 120, 255);
|
||||
}
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background-color: #fff;
|
||||
border-radius: 0;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
height: 50px;
|
||||
background-color: #999;
|
||||
border-radius: 4px;
|
||||
outline: 2px solid #fff;
|
||||
outline-offset: -2px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
height: 50px;
|
||||
background-color: #9f9f9f;
|
||||
border-radius: 4px;
|
||||
}
|
||||
input[type=text] {
|
||||
resize: none;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
outline: none;
|
||||
padding: 0 5px;
|
||||
font-size: 13px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #ffffcd;
|
||||
box-shadow: 2px 2px 3px #ededed inset;
|
||||
transition: border .25s ease-in-out, box-shadow .25s ease-in-out;
|
||||
}
|
||||
input[type=text]:focus {
|
||||
border: 1px solid rgba(82, 162, 235, .8);
|
||||
box-shadow: 0 0 rgba(34, 25, 25, 0.15) inset, 0 0 rgba(255, 255, 255, 0.8), 0 0 6px rgba(82, 162, 235, 1);
|
||||
}
|
||||
html,
|
||||
body {
|
||||
width: 100%; height: 100%; overflow: hidden;
|
||||
}
|
||||
body {
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
min-width: 1024px; !important;
|
||||
min-height: 768px !important;
|
||||
}
|
||||
a {
|
||||
outline: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-button {
|
||||
color: #fff;
|
||||
border: none;
|
||||
outline: none;
|
||||
background-color: #0e7fd4;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
padding: 0 13px;
|
||||
font-size: 13px;
|
||||
overflow: auto;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
transition: box-shadow .25s ease-in-out;
|
||||
}
|
||||
.ui-button:hover {
|
||||
box-shadow: 0 0 rgba(34, 25, 25, 0.15) inset, 0 0 rgba(255, 255, 255, 0.8), 0 0 3px rgb(0, 120, 255);
|
||||
}
|
||||
.ui-button-orange {
|
||||
background-color: #f57527;
|
||||
}
|
||||
header {
|
||||
padding: 3px 6px;
|
||||
position: relative;
|
||||
-webkit-app-region: drag;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
header [class^="icon-"],
|
||||
header [class*=" icon-"] {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
.icon-trash,
|
||||
.ui-project-add,
|
||||
.ui-import-configure,
|
||||
.ui-export-configure {
|
||||
transition: transform .09s linear;
|
||||
}
|
||||
.icon-gear {
|
||||
transition: transform .15s linear;
|
||||
}
|
||||
.ui-window-control a {
|
||||
display: inline-block; color: #9c9c9c;
|
||||
transition: transform .25s linear;
|
||||
}
|
||||
.icon-gear:hover,
|
||||
.ui-window-control a:hover {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
.icon-trash:hover,
|
||||
.ui-project-add:hover,
|
||||
.ui-import-configure:hover,
|
||||
.ui-export-configure:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
.ui-project-add,
|
||||
.ui-import-configure,
|
||||
.ui-export-configure {
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ui-project-add .icon-expand {
|
||||
margin-left: -5px;
|
||||
}
|
||||
.ui-import-configure {
|
||||
margin: 0 1px 0 6px;
|
||||
}
|
||||
.ui-popup {
|
||||
position: absolute;
|
||||
top: 31px;
|
||||
left: 5px;
|
||||
border: 1px solid #ccc;
|
||||
z-index: 1;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px 0 #a9a9a9;
|
||||
}
|
||||
.ui-popup input,
|
||||
.ui-project-configure input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ui-popup-arrow {
|
||||
position: absolute;
|
||||
top: -8px; left: 9px;
|
||||
z-index: 1;
|
||||
}
|
||||
.ui-popup-arrow em,
|
||||
.ui-popup-arrow span {
|
||||
width: 0; height: 0;
|
||||
border: solid rgba(255, 255, 255, 0);
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ui-popup-arrow em {
|
||||
border-width: 0 7px 7px;
|
||||
border-bottom-color: #ccc;
|
||||
}
|
||||
.ui-popup-arrow span {
|
||||
top: 1px;
|
||||
border-width: 0 7px 7px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.ui-popup-content {
|
||||
padding: 20px;
|
||||
}
|
||||
.ui-popup-content li,
|
||||
.ui-project-configure li {
|
||||
color: #333;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
.ui-popup-content strong,
|
||||
.ui-project-configure strong {
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui-popup-content input[type=text],
|
||||
.ui-project-configure input[type=text] {
|
||||
width: 180px;
|
||||
}
|
||||
.ui-popup-content .ui-popup-control {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.ui-project-tree {
|
||||
box-sizing: border-box;
|
||||
width: 20%;
|
||||
min-height: 735px !important;
|
||||
height: calc(100% - 31px);
|
||||
overflow: auto;
|
||||
}
|
||||
.ui-project-tree li:nth-child(odd) {
|
||||
background-color: #eee;
|
||||
}
|
||||
.ui-project-tree li {
|
||||
color: #333;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ui-project-tree li a {
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0 0 0 10px;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ui-project-main {
|
||||
box-sizing: border-box;
|
||||
width: 80%;
|
||||
height: calc(100% - 31px);
|
||||
min-height: 735px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ui-project-stage,
|
||||
.ui-project-setting,
|
||||
.ui-project-setting form {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-control-bar {
|
||||
height: 39px;
|
||||
line-height: 39px;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
.ui-control-bar a {
|
||||
display: inline-block;
|
||||
}
|
||||
.ui-control-operate a {
|
||||
height: 39px;
|
||||
line-height: 39px;
|
||||
}
|
||||
.ui-control-bar .icon-trash {
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
.ui-command {
|
||||
position: relative;
|
||||
}
|
||||
.ui-command li {
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
.ui-command a {
|
||||
padding: 0 6px;
|
||||
}
|
||||
.ui-command-more .icon-expand {
|
||||
font-size: 24px;
|
||||
color: #08c;
|
||||
margin: -4px 0 0 -4px;
|
||||
}
|
||||
.ui-command-popup {
|
||||
position: absolute;
|
||||
top: 39px; right: 0;
|
||||
background-color: #eee;
|
||||
}
|
||||
.ui-command-popup li {
|
||||
float: none;
|
||||
line-height: 30px;
|
||||
}
|
||||
.ui-command-popup li a {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.ui-project-tree a,
|
||||
.ui-command li a,
|
||||
.ui-command-popup li a {
|
||||
color: inherit;
|
||||
}
|
||||
.ui-project-tree a:hover,
|
||||
.ui-project-tree .active,
|
||||
.ui-command a:hover {
|
||||
background-color: #0e7fd4;
|
||||
color: #fff;
|
||||
}
|
||||
.ui-command a:hover .icon-play:before {
|
||||
color: #34bd24;
|
||||
}
|
||||
.ui-project-tree a:hover .icon-folder:before,
|
||||
.ui-command a:hover .icon-expand:before {
|
||||
color: #fff;
|
||||
}
|
||||
.ui-project-terminal,
|
||||
.ui-project-configure {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - 39px);
|
||||
border-top: 1px dashed #ccc;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
.ui-project-terminal {
|
||||
background-color: #181818;
|
||||
border-left: 1px dashed #ccc;
|
||||
}
|
||||
.ui-control-bar .ui-button-orange {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
.ui-project-configure {
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
}
|
||||
.ui-project-configure ul {
|
||||
padding: 30px 0;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
}
|
||||
.ui-sub-item > label,
|
||||
.ui-sub-item ul {
|
||||
vertical-align: top;
|
||||
}
|
||||
.ui-sub-item > label {
|
||||
vertical-align: -3px;
|
||||
}
|
||||
.ui-sub-item ul {
|
||||
padding: 0;
|
||||
}
|
||||
.ui-sub-item .ui-action-bar {
|
||||
margin: 0;
|
||||
}
|
||||
.ui-sub-item span {
|
||||
width: 192px;
|
||||
line-height: 25px;
|
||||
border-bottom: 1px dotted #ccc;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ui-sub-item .icon-trash {
|
||||
cursor: pointer;
|
||||
margin-left: 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ui-project-tree a,
|
||||
.ui-sub-item span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ui-sub-item .ui-item-error {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
.ui-item-error span {
|
||||
color: #f00;
|
||||
border-bottom-color: #f00;
|
||||
}
|
||||
.ui-item-error .icon-expand {
|
||||
position: absolute;
|
||||
transform: rotate(180deg);
|
||||
margin-top: -11px;
|
||||
margin-left: -5px;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="font-icon" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||
<glyph unicode="" glyph-name="play" d="M170.667 42.667v768l640-384zM644.667 426.667l-388.667 233.333v-466.667z" />
|
||||
<glyph unicode="" glyph-name="stop" d="M256 810.667h512q53 0 90.5-37.5t37.5-90.5v-512q0-53-37.5-90.5t-90.5-37.5h-512q-53 0-90.5 37.5t-37.5 90.5v512q0 53 37.5 90.5t90.5 37.5zM768 725.334h-512q-17.667 0-30.167-12.5t-12.5-30.167v-512q0-17.667 12.5-30.167t30.167-12.5h512q17.667 0 30.167 12.5t12.5 30.167v512q0 17.667-12.5 30.167t-30.167 12.5z" />
|
||||
<glyph unicode="" glyph-name="folder" d="M170.667 853.334h213.333l85.333-128h384q53 0 90.5-37.5t37.5-90.5v-469.333q0-53-37.5-90.5t-90.5-37.5h-682.667q-53 0-90.5 37.5t-37.5 90.5v597.333q0 53 37.5 90.5t90.5 37.5zM423.667 640l-82.333 128h-170.667q-17.667 0-30.167-12.5t-12.5-30.167v-597.333q0-17.667 12.5-30.167t30.167-12.5h682.667q17.667 0 30.167 12.5t12.5 30.167v469.333q0 17.667-12.5 30.167t-30.167 12.5h-429.667z" />
|
||||
<glyph unicode="" glyph-name="maximize" d="M597.333 384.334q17.667 0 30.333-12.667l268.333-268.333v195.667q0 17.667 12.5 30.167t30.167 12.5 30.167-12.5 12.5-30.167v-298.667q0-17.667-12.667-30.333t-30.333-12.667h-298.667q-17.667 0-30.167 12.5t-12.5 30.167 12.5 30.167 30.167 12.5h195.667l-268.333 268.333q-12.333 13-12.333 30.333 0 17.667 12.5 30.333t30.167 12.667zM85.333 896h298.667q17.667 0 30.167-12.5t12.5-30.167-12.5-30.167-30.167-12.5h-195.667l268.333-268q12.667-12.667 12.667-30.333t-12.667-30.167-30.333-12.5q-17 0-30.333 12.333l-268 268.333v-195.667q0-17.667-12.5-30.167t-30.167-12.5-30.167 12.5-12.5 30.167v298.667q0 17.667 12.5 30.167t30.167 12.5z" />
|
||||
<glyph unicode="" glyph-name="unmaximize" d="M85.333 896q18 0 30.333-12.333l268.333-268.333v195.667q0 17.667 12.5 30.167t30.167 12.5 30.167-12.5 12.5-30.167v-298.667q0-17.667-12.667-30.333t-30.333-12.667h-298.667q-17.667 0-30.167 12.5t-12.5 30.167 12.5 30.167 30.167 12.5h195.667l-268.333 268.333q-12.333 12.333-12.333 30.333 0 18.333 12.167 30.5t30.5 12.167zM597.667 384h298.667q17.667 0 30.167-12.5t12.5-30.167-12.5-30.167-30.167-12.5h-195.667l268-268.333q12.667-12.667 12.667-30.333t-12.5-30.167-30.167-12.5q-18 0-30.333 12.333l-268.333 268.333v-195.667q0-17.667-12.5-30.167t-30.167-12.5-30.167 12.5-12.5 30.167v298.667q0 17.667 12.667 30.333t30.333 12.667z" />
|
||||
<glyph unicode="" glyph-name="plus" d="M512 853.334q17.667 0 30.167-12.5t12.5-30.167v-341.333h341.333q17.667 0 30.167-12.5t12.5-30.167-12.5-30.167-30.167-12.5h-341.333v-341.333q0-17.667-12.5-30.167t-30.167-12.5-30.167 12.5-12.5 30.167v341.333h-341.333q-17.667 0-30.167 12.5t-12.5 30.167 12.5 30.167 30.167 12.5h341.333v341.333q0 17.667 12.5 30.167t30.167 12.5z" />
|
||||
<glyph unicode="" glyph-name="cross" d="M810.667 768q18.333 0 30.5-12.167t12.167-30.5q0-18-12.333-30.333l-268.667-268.333 268.667-268.333q12.333-12.333 12.333-30.333 0-18.333-12.167-30.5t-30.5-12.167q-18 0-30.333 12.333l-268.333 268.667-268.333-268.667q-12.333-12.333-30.333-12.333-18.333 0-30.5 12.167t-12.167 30.5q0 18 12.333 30.333l268.667 268.333-268.667 268.333q-12.333 12.333-12.333 30.333 0 18.333 12.167 30.5t30.5 12.167q18 0 30.333-12.333l268.333-268.667 268.333 268.667q12.333 12.333 30.333 12.333z" />
|
||||
<glyph unicode="" glyph-name="import" d="M512 896q17.667 0 30.167-12.5t12.5-30.167v-494.333l140.333 140.667q12.333 12.333 30.333 12.333 18.333 0 30.5-12.167t12.167-30.5q0-18-12.333-30.333l-213.333-213.333q-12.333-12.333-30.333-12.333t-30.333 12.333l-213.333 213.333q-12.333 13-12.333 30.333 0 17.667 12.5 30.167t30.167 12.5q18 0 30.333-12.333l140.333-140.667v494.333q0 17.667 12.5 30.167t30.167 12.5zM938.667 298.667q17.667 0 30.167-12.5t12.5-30.167v-170.667q0-53.333-37-90.333-37.667-37.667-90-37.667h-683.667q-52.333 0-90.667 37.333-37.333 38.333-37.333 90.667v170.667q0 17.667 12.5 30.167t30.167 12.5 30.167-12.5 12.5-30.167v-170.667q0-17.667 12.5-30.167t30.167-12.5h683.667q17.333 0 29.5 12.5t12.167 30.167v170.667q0 17.667 12.5 30.167t30.167 12.5z" />
|
||||
<glyph unicode="" glyph-name="export" d="M512 894q18 0 30.333-12.333l213.333-213.333q12.333-12.333 12.333-30.333 0-18.333-12.167-30.5t-30.5-12.167q-18 0-30.333 12.333l-140.333 140.667v-494.333q0-17.667-12.5-30.167t-30.167-12.5-30.167 12.5-12.5 30.167v494.333l-140.333-140.667q-13-12.333-30.333-12.333-17.667 0-30.167 12.5t-12.5 30.167q0 17.333 12.333 30.333l213.333 213.333q12.333 12.333 30.333 12.333zM938.667 296.667q17.667 0 30.167-12.5t12.5-30.167v-170.667q0-53.333-37-90.333-37.667-37.667-90-37.667h-683.667q-52.333 0-90.667 37.333-37.333 38.333-37.333 90.667v170.667q0 17.667 12.5 30.167t30.167 12.5 30.167-12.5 12.5-30.167v-170.667q0-17.667 12.5-30.167t30.167-12.5h683.667q17.333 0 29.5 12.5t12.167 30.167v170.667q0 17.667 12.5 30.167t30.167 12.5z" />
|
||||
<glyph unicode="" glyph-name="minimize-tray" d="M854 708.667l-496-496h282v-84h-426v426h84v-282l496 496z" />
|
||||
<glyph unicode="" glyph-name="expand" d="M708 572.667l60-60-256-256-256 256 60 60 196-196z" />
|
||||
<glyph unicode="" glyph-name="ellipsis" d="M512 512.667q34 0 60-26t26-60-26-60-60-26-60 26-26 60 26 60 60 26zM768 512.667q34 0 60-26t26-60-26-60-60-26-60 26-26 60 26 60 60 26zM256 512.667q34 0 60-26t26-60-26-60-60-26-60 26-26 60 26 60 60 26z" />
|
||||
<glyph unicode="" glyph-name="trash" d="M967.111 682.667h-227.556v170.667c0 31.417-25.472 56.889-56.889 56.889h-341.333c-31.417 0-56.889-25.472-56.889-56.889v-170.667h-227.556c-31.417 0-56.889-25.472-56.889-56.889v-56.889h113.778v-512c0-31.417 25.472-56.889 56.889-56.889h682.667c31.417 0 56.889 25.472 56.889 56.889v512h113.778v56.889c0 31.417-25.472 56.889-56.889 56.889zM398.222 796.444h227.556v-113.778h-227.556v113.778zM796.444 113.778h-568.889v455.111h568.889v-455.111zM369.778 227.555h56.889c15.723 0 28.444 12.736 28.444 28.444v170.667c0 15.708-12.722 28.444-28.444 28.444h-56.889c-15.723 0-28.444-12.736-28.444-28.444v-170.667c0-15.708 12.722-28.444 28.444-28.444zM597.333 227.555h56.889c15.723 0 28.444 12.736 28.444 28.444v170.667c0 15.708-12.722 28.444-28.444 28.444h-56.889c-15.723 0-28.444-12.736-28.444-28.444v-170.667c0-15.708 12.722-28.444 28.444-28.444z" />
|
||||
<glyph unicode="" glyph-name="gear" d="M919.584 519.931l-75.388 12.555c-8.695 37.388-23.5 72.347-43.333 103.902l41.972 58.777c16 22.416 13.472 53.111-6 72.597l-12.194 12.181c-19.472 19.472-50.167 22.014-72.583 6l-58.777-41.986c-31.556 19.847-66.5 34.667-103.89 43.348l-12.583 75.374c-4.556 27.433-28.308 47.543-56.112 47.543h-17.388c-27.806 0-51.556-20.11-56.11-47.541l-12.583-75.374c-37.388-8.681-72.334-23.5-103.89-43.348l-58.777 41.986c-22.416 16.014-53.111 13.472-72.583-6l-12.194-12.181c-19.472-19.486-22-50.181-6-72.597l41.972-58.777c-19.833-31.556-34.638-66.514-43.333-103.902l-75.388-12.555c-27.42-4.585-47.531-28.322-47.531-56.126v-17.388c0-27.806 20.11-51.541 47.527-56.11l75.388-12.569c8.695-37.388 23.5-72.347 43.333-103.902l-41.972-58.777c-16-22.416-13.472-53.111 6-72.597l12.194-12.181c19.472-19.472 50.167-22.014 72.583-6l58.777 41.986c31.556-19.847 66.5-34.667 103.89-43.348l12.583-75.374c4.555-27.431 28.306-47.541 56.11-47.541h17.388c27.806 0 51.556 20.11 56.11 47.541l12.583 75.374c37.388 8.681 72.334 23.5 103.89 43.348l58.777-41.986c22.416-16.014 53.111-13.472 72.583 6l12.194 12.181c19.472 19.486 22 50.181 6 72.597l-41.972 58.777c19.833 31.556 34.638 66.514 43.333 103.902l75.388 12.569c27.417 4.569 47.527 28.306 47.527 56.11v17.388c0.005 27.804-20.105 51.541-47.522 56.124zM512 284.444c-94.251 0-170.667 76.416-170.667 170.667s76.416 170.667 170.667 170.667 170.667-76.416 170.667-170.667-76.416-170.667-170.667-170.667z" />
|
||||
</font></defs></svg>
|
After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>命令管理器</title>
|
||||
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
|
||||
<link href="./css/base.css" rel="stylesheet" type="text/css"/>
|
||||
<link href="./css/index.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="./js/app/index.js"></script>
|
||||
</head>
|
||||
<body id="app" class="fn-clear">
|
||||
<header class="fn-clear">
|
||||
<app-configure :configure.sync="configure"></app-configure>
|
||||
<window-control></window-control>
|
||||
</header>
|
||||
<nav class="ui-project-tree fn-left">
|
||||
<app-nav :configure.sync="configure" :active-index.sync="activeIndex"></app-nav>
|
||||
</nav>
|
||||
<main class="ui-project-main fn-right">
|
||||
<div class="ui-project-stage fn-hide">
|
||||
<div class="ui-control-bar fn-clear">
|
||||
<div class="ui-control-operate fn-left">
|
||||
<a title="删除项目" href="javascript:;"><i class="icon-trash"></i></a>
|
||||
<a title="设置项目" href="javascript:;"><i class="icon-gear"></i></a>
|
||||
</div>
|
||||
<div class="fn-right">
|
||||
<ul class="ui-command fn-clear">
|
||||
<li><a href="javascript:;"><i class="icon-play"></i>命令1</a></li>
|
||||
<li><a href="javascript:;"><i class="icon-stop"></i>命令2</a></li>
|
||||
<li><a href="javascript:;"><i class="icon-play"></i>命令3</a></li>
|
||||
<li class="ui-command-more">
|
||||
<a href="javascript:;">更多 <i class="icon-expand"></i></a>
|
||||
<ul class="ui-command-popup fn-hide">
|
||||
<li><a href="javascript:;"><i class="icon-play"></i>命令4</a></li>
|
||||
<li><a href="javascript:;"><i class="icon-stop"></i>命令5</a></li>
|
||||
<li><a href="javascript:;"><i class="icon-play"></i>命令6</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-project-terminal"></div>
|
||||
</div>
|
||||
<div class="ui-project-setting">
|
||||
<form>
|
||||
<div class="ui-control-bar fn-clear">
|
||||
<div class="fn-right">
|
||||
<input type="submit" class="ui-button" value="确认"/>
|
||||
<input type="button" class="ui-button ui-button-orange" value="取消"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-project-configure">
|
||||
<ul>
|
||||
<li>
|
||||
<label>项目名称:</label>
|
||||
<input type="text" placeholder="项目名称"/>
|
||||
</li>
|
||||
<li id="open-dir">
|
||||
<directory label="项目路径"></directory>
|
||||
</li>
|
||||
<li id="add-env" class="ui-sub-item">
|
||||
<label>环境变量:</label>
|
||||
<dynamic-item name-label="变量名" value-label="变量值"></dynamic-item>
|
||||
</li>
|
||||
<li id="add-cmd" class="ui-sub-item">
|
||||
<label>项目命令:</label>
|
||||
<dynamic-item name-label="名称" value-label="命令"></dynamic-item>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/16.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ipc = require('ipc-renderer');
|
||||
var Vue = require('./js/vue/vue');
|
||||
|
||||
require('./js/components/app-configure');
|
||||
require('./js/components/window-control');
|
||||
require('./js/components/app-nav');
|
||||
require('./js/components/directory');
|
||||
require('./js/components/dynamic-item');
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function (){
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
activeIndex: 0,
|
||||
configure: {}
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('app-configure', function (event, command, configure){
|
||||
switch (command) {
|
||||
case 'refresh':
|
||||
app.activeIndex = 0;
|
||||
app.configure = configure;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
ipc.send('app-configure', 'refresh');
|
||||
}, false);
|
|
@ -0,0 +1,35 @@
|
|||
<div class="fn-left">
|
||||
<a @click="appConfigure('save', configure)" title="添加项目" class="ui-project-add" href="javascript:;">
|
||||
<i class="icon-plus"></i>
|
||||
<i class="icon-expand"></i>
|
||||
</a>
|
||||
<a @click="appConfigure('import')" class="ui-import-configure" title="导入配置" href="javascript:;">
|
||||
<i class="icon-import"></i>
|
||||
</a>
|
||||
<a @click="appConfigure('export')" class="ui-export-configure" title="导出配置" href="javascript:;">
|
||||
<i class="icon-export"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ui-popup fn-hide">
|
||||
<div class="ui-popup-arrow">
|
||||
<em></em>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="ui-popup-content">
|
||||
<form>
|
||||
<ul>
|
||||
<li>
|
||||
<label>项目名称:</label>
|
||||
<input type="text" placeholder="项目名称"/>
|
||||
</li>
|
||||
<li id="popup-open-dir">
|
||||
<directory label="项目路径"></directory>
|
||||
</li>
|
||||
<li class="ui-popup-control">
|
||||
<input type="submit" class="ui-button" value="确定"/>
|
||||
<input type="button" class="ui-button ui-button-orange" value="取消"/>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/19.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var ipc = require('ipc-renderer');
|
||||
var Vue = require('../../vue/vue');
|
||||
|
||||
module.exports = Vue.component('app-configure', {
|
||||
template: fs.readFileSync(path.join(__dirname, 'app-configure.html')).toString(),
|
||||
props: {
|
||||
configure: {
|
||||
type: Object,
|
||||
twoWay: true,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
appConfigure: function (command, configure){
|
||||
ipc.send('app-configure', command, configure);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/20.
|
||||
*/
|
|
@ -0,0 +1,7 @@
|
|||
<ul>
|
||||
<li v-for="(index, project) in configure.projects">
|
||||
<a href="javascript:;" :class="{ active: activeIndex === index }" :title="project.name" @click="select(index)">
|
||||
<i class="icon-folder"></i>{{ project.name }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/20.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var Vue = require('../../vue/vue');
|
||||
|
||||
module.exports = Vue.component('app-nav', {
|
||||
template: fs.readFileSync(path.join(__dirname, 'app-nav.html')).toString(),
|
||||
props: {
|
||||
activeIndex: {
|
||||
type: Number,
|
||||
twoWay: true,
|
||||
required: true
|
||||
},
|
||||
configure: {
|
||||
type: Object,
|
||||
twoWay: true,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
select: function (index){
|
||||
this.activeIndex = index;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
<label>{{label}}:</label>
|
||||
<input type="text" :title="path" :placeholder="label" v-model="path" lazy/>
|
||||
<a title="选择项目" href="javascript:;" class="icon-ellipsis" @click="open"></a>
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/17.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var ipc = require('ipc-renderer');
|
||||
var Vue = require('../../vue/vue');
|
||||
|
||||
module.exports = Vue.component('directory', {
|
||||
// camelCase in JavaScript
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
template: fs.readFileSync(path.join(__dirname, 'directory.html')).toString(),
|
||||
methods: {
|
||||
open: function (){
|
||||
ipc.send('open-directory', this.path, this._uid);
|
||||
}
|
||||
},
|
||||
created: function (){
|
||||
var context = this;
|
||||
|
||||
ipc.on('select-directory', function (event, paths, uid){
|
||||
if (context._uid === uid) {
|
||||
context.path = paths[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
<ul>
|
||||
<li v-for="(index, item) in items">
|
||||
<span :title="item.name">{{ item.name }}</span>
|
||||
<span :title="item.value">{{ item.value }}</span>
|
||||
<i @click="remove(index)" title="删除" class="icon-trash"></i>
|
||||
</li>
|
||||
<li class="ui-action-bar">
|
||||
<input type="text" :placeholder="nameLabel" v-model="name" lazy @focus="focus('nameError')"/>
|
||||
<input type="text" :placeholder="valueLabel" v-model="value" lazy @focus="focus('valueError')"/>
|
||||
<input type="button" class="ui-button" @click="add" value="添加"/>
|
||||
</li>
|
||||
<li v-show="nameError || valueError" class="ui-item-error">
|
||||
<span :class="nameError ? '' : 'fn-invisible'"><i class="icon-expand"></i>{{ nameLabel }}{{ nameError }}</span>
|
||||
<span :class="valueError ? '' : 'fn-invisible'"><i class="icon-expand"></i>{{ valueLabel }}{{ valueError }}</span>
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/17.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var Vue = require('../../vue/vue');
|
||||
|
||||
module.exports = Vue.component('dynamic-item', {
|
||||
// camelCase in JavaScript
|
||||
props: {
|
||||
nameLabel: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
valueLabel: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: ''
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: function (){
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
template: fs.readFileSync(path.join(__dirname, 'dynamic-item.html')).toString(),
|
||||
data: function (){
|
||||
return {
|
||||
name: '',
|
||||
value: '',
|
||||
_cached: {},
|
||||
nameError: '',
|
||||
valueError: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
add: function (){
|
||||
// trim value
|
||||
this.name = this.name.trim();
|
||||
this.value = this.value.trim();
|
||||
|
||||
// name error
|
||||
if (!this.name) {
|
||||
this.nameError = '不能为空';
|
||||
} else if (this.$data._cached[this.name]) {
|
||||
this.nameError = ' ' + this.name + ' 已存在';
|
||||
} else {
|
||||
this.nameError = '';
|
||||
}
|
||||
|
||||
// value error
|
||||
if (!this.value) {
|
||||
this.valueError = '不能为空';
|
||||
} else {
|
||||
this.valueError = '';
|
||||
}
|
||||
|
||||
// add item
|
||||
if (this.name && this.value && !this.$data._cached[this.name]) {
|
||||
// cache name
|
||||
this.$data._cached[this.name] = true;
|
||||
// add item
|
||||
this.items.push({ name: this.name, value: this.value });
|
||||
|
||||
// clean input
|
||||
this.name = '';
|
||||
this.value = '';
|
||||
}
|
||||
},
|
||||
focus: function (key){
|
||||
this[key] = '';
|
||||
},
|
||||
remove: function (index){
|
||||
var item = this.items[index];
|
||||
|
||||
this.items.splice(index, 1);
|
||||
delete this.$data._cached[item.name];
|
||||
}
|
||||
},
|
||||
created: function (){
|
||||
this.items.forEach(function (item){
|
||||
this.$data._cached[item.name] = true;
|
||||
}, this);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/20.
|
||||
*/
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/19.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var ipc = require('ipc-renderer');
|
||||
var Vue = require('../../vue/vue');
|
||||
|
||||
module.exports = Vue.component('window-control', {
|
||||
data: function (){
|
||||
return {
|
||||
isMaximized: false
|
||||
}
|
||||
},
|
||||
template: fs.readFileSync(path.join(__dirname, 'window-control.html')).toString(),
|
||||
methods: {
|
||||
tray: function (){
|
||||
ipc.send('window', 'tray');
|
||||
},
|
||||
close: function (){
|
||||
ipc.send('window', 'close');
|
||||
},
|
||||
maximize: function (){
|
||||
ipc.send('window', this.isMaximized ? 'unmaximize' : 'maximize');
|
||||
}
|
||||
},
|
||||
created: function (){
|
||||
var context = this;
|
||||
|
||||
ipc.on('is-maximized', function (event, maximized){
|
||||
context.isMaximized = maximized;
|
||||
});
|
||||
|
||||
ipc.send('window', 'is-maximized');
|
||||
}
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
<div class="ui-window-control fn-right">
|
||||
<a @click="tray" title="缩小到托盘区" href="javascript:;">
|
||||
<i class="icon-minimize-tray"></i>
|
||||
</a>
|
||||
<a @click="maximize" :title="isMaximized?'还原':'最大化'" href="javascript:;">
|
||||
<i :class="{ 'icon-maximize' : !isMaximized, 'icon-unmaximize' : isMaximized }"></i>
|
||||
</a>
|
||||
<a @click="close" title="关闭" href="javascript:;">
|
||||
<i class="icon-cross"></i>
|
||||
</a>
|
||||
</div>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Created by nuintun on 2015/11/5.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// node module
|
||||
var path = require('path');
|
||||
// module to control application life
|
||||
var app = require('app');
|
||||
var Menu = require('menu');
|
||||
var Tray = require('tray');
|
||||
// module to create native browser window
|
||||
var BrowserWindow = require('browser-window');
|
||||
// custom module
|
||||
var windowControl = require('./bin/window-control');
|
||||
var openDirectory = require('./bin/open-directory');
|
||||
var AppConfigure = require('./bin/app-configure');
|
||||
|
||||
// keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the javascript object is GCed
|
||||
var mainTray = null;
|
||||
var mainWindow = null;
|
||||
// const var
|
||||
const APPNAME = '命令管理器';
|
||||
const ICON = path.join(__dirname, './app.ico');
|
||||
const INDEX = 'file:///' + path.join(__dirname, 'index.html');
|
||||
|
||||
// quit when all windows are closed
|
||||
app.on('window-all-closed', function (){
|
||||
app.quit();
|
||||
});
|
||||
|
||||
// this method will be called when atom-shell has done everything
|
||||
// initialization and ready for creating browser windows
|
||||
app.on('ready', function (){
|
||||
// create the tray window
|
||||
mainTray = new Tray(ICON);
|
||||
|
||||
mainTray.setToolTip(APPNAME);
|
||||
mainTray.setContextMenu(Menu.buildFromTemplate([
|
||||
{
|
||||
label: '显示窗口',
|
||||
click: function (){
|
||||
mainWindow.show();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '退出程序',
|
||||
click: function (){
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
]));
|
||||
|
||||
// create the browser window
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1024,
|
||||
height: 768,
|
||||
icon: ICON,
|
||||
'min-width': 1024,
|
||||
'min-height': 768,
|
||||
title: APPNAME,
|
||||
frame: false,
|
||||
center: true,
|
||||
'use-content-size': true
|
||||
});
|
||||
|
||||
// and load the index.html of the app
|
||||
mainWindow.loadURL(INDEX);
|
||||
// emitted when the window is closed
|
||||
mainWindow.on('closed', function (){
|
||||
// dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element
|
||||
mainTray.destroy();
|
||||
|
||||
mainTray = null;
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
// window control
|
||||
windowControl(ICON, mainWindow, mainTray);
|
||||
|
||||
// open directory
|
||||
openDirectory(mainWindow);
|
||||
|
||||
// app configure
|
||||
new AppConfigure(mainWindow, mainTray);
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "command-manager",
|
||||
"version": "0.0.1",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron ."
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue