节点分组

pull/1/head
Doflatango 2017-02-17 14:20:58 +08:00 committed by miraclesu
parent a615b32ca9
commit 5a5faeb88b
12 changed files with 205 additions and 44 deletions

View File

@ -47,20 +47,20 @@ func InitRouters() (s *http.Server, err error) {
subrouter.Handle("/nodes", h).Methods("GET")
// get node group list
h = BaseHandler{Handle: nodeHandler.GetGroups}
subrouter.Handle("/nodes/groups", h).Methods("GET")
subrouter.Handle("/node/groups", h).Methods("GET")
// get a node group by group id
h = BaseHandler{Handle: nodeHandler.GetGroupByGroupId}
subrouter.Handle("/nodes/group/{id}", h).Methods("GET")
subrouter.Handle("/node/group/{id}", h).Methods("GET")
// create/update a node group
h = BaseHandler{Handle: nodeHandler.UpdateGroup}
subrouter.Handle("/nodes/group", h).Methods("PUT")
subrouter.Handle("/node/group", h).Methods("PUT")
// delete a node group
h = BaseHandler{Handle: nodeHandler.DeleteGroup}
subrouter.Handle("/nodes/group", h).Methods("DELETE")
subrouter.Handle("/node/group/{id}", h).Methods("DELETE")
uidir := conf.Config.Web.UIDir
if len(uidir) == 0 {
uidir = path.Join("..", "..", "web", "ui", "dist")
uidir = path.Join("web", "ui", "dist")
}
r.PathPrefix("/ui/").Handler(http.StripPrefix("/ui/", http.FileServer(http.Dir(uidir))))

28
web/ui/dist/build.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
<router-link class="item" to="/" v-bind:class="{active: this.$route.path == '/'}"><i class="dashboard icon"></i> 仪表盘</router-link>
<router-link class="item" to="/log" v-bind:class="{active: this.$route.path.indexOf('/log') === 0}"><i class="file text icon"></i> 日志</router-link>
<router-link class="item" to="/job" v-bind:class="{active: this.$route.path.indexOf('/job') === 0}"><i class="calendar icon"></i> 任务</router-link>
<router-link class="item" to="/node" v-bind:class="{active: this.$route.path.indexOf('/nodes') === 0}"><i class="server icon"></i> 节点</router-link>
<router-link class="item" to="/node" v-bind:class="{active: this.$route.path.indexOf('/node') === 0}"><i class="server icon"></i> 节点</router-link>
</div>
<div style="height: 55px;"></div>
<div class="ui container">

View File

@ -40,7 +40,7 @@
</td>
<td class="center aligned"><i class="icon" v-bind:class="{pause: job.pause, play: !job.pause, green: !job.pause}"></i></td>
<td>{{job.group}}</td>
<td>{{job.name}}</td>
<td><router-link :to="'/job/edit/'+job.group+'/'+job.id">{{job.name}}</router-link></td>
<td>
<span v-if="!job.latestStatus">-</span>
<span v-else>{{formatTime(job.latestStatus.beginTime, job.latestStatus.endTime)}} {{job.latestStatus.node}} {{formatDuration(job.latestStatus.beginTime, job.latestStatus.endTime)}}</span>

View File

@ -17,7 +17,7 @@
</div>
<div class="field">
<label>任务分组</label>
<Dropdown title="选择分组" allowAdditions=true v-bind:items="groups" v-bind:selected="job.group" v-on:change="changeGroup"/>
<Dropdown title="选择分组" v-bind:items="groups" v-bind:selected="job.group" v-on:change="changeGroup"/>
</div>
</div>
<div class="fields">
@ -90,9 +90,11 @@ export default {
removeRule: function(index){
this.job.rules.splice(index, 1);
},
changeRule: function(index, key, val){
this.job.rules[index][key] = val;
},
submit: function(){
var exceptCode = this.action == 'CREATE' ? 201 : 200;
this.loading = true;

View File

@ -9,7 +9,7 @@
</div>
</div>
<div class="field">
<Dropdown title="节点分组" v-bind:items="nodeGroups" multiple="true"></Dropdown>
<Dropdown title="节点分组" v-bind:items="nodeGroups" v-bind:selected="rule.gids" multiple="true" v-on:change="changeNodeGroups($event)"></Dropdown>
</div>
</div>
<div class="field">
@ -43,13 +43,15 @@ export default {
vm.activityNodes.push(resp[i].id);
}
}).do();
this.$rest.GET('nodes/groups').onsucceed(200, (resp)=>{
this.$rest.GET('node/groups').onsucceed(200, (resp)=>{
var groups = [];
for (var i in resp) {
groups.push({value: resp[i].id, name: resp[i].name});
}
vm.nodeGroups = groups;
});
}).do();
$(this.$refs.ruletip).popup();
},
@ -61,6 +63,10 @@ export default {
change: function(key, val){
this.$emit('change', this.index, key, val);
},
changeNodeGroups: function(val){
var gids = val.trim().length === 0 ? [] : val.split(',');
this.change('gids', gids);
},
changeIncludeNodes: function(val){
var nids = val.trim().length === 0 ? [] : val.split(',');
this.change('nids', nids);

View File

@ -1,12 +0,0 @@
<template>
<div>
<router-link class="ui right floated primary button" to="/job/create"><i class="add to calendar icon"></i> 新任务</router-link>
<button class="ui right floated icon button"><i class="refresh icon"></i></button>
</div>
</template>
<script>
export default {
name: 'job-toolbar'
}
</script>

View File

@ -1,10 +1,12 @@
<template>
<div>
<h3 class="ui horizontal divider header">当前节点 {{count}}</h3>
<div class="ui label" title="手动下线/维护中的"><i class="cube icon"></i> 已下线节点</div>
<div class="ui label" title="正常运行的节点"><i class="green cube icon"></i> 正常节点</div>
<div class="ui label" title="节点因自身或网络等原因未检测到"><i class="red cube icon"></i> 故障节点</div>
<h3 class="ui horizontal divider header"> </h3>
<div class="clearfix">
<router-link class="ui right floated primary button" to="/node/group"><i class="cubes icon"></i> 管理分组</router-link>
<div class="ui label" title="手动下线/维护中的"><i class="cube icon"></i> 离线节点</div>
<div class="ui label" title="正常运行的节点"><i class="green cube icon"></i> 正常节点</div>
<div class="ui label" title="因自身或网络等原因未检测到节点存活"><i class="red cube icon"></i> 故障节点</div>
</div>
<h4 class="ui horizontal divider header">当前节点 {{count}}</h4>
<div v-for="node in nodes" class="ui label"><i v-bind:class="{green: node.alived && node.connected, red: node.alived && !node.connected}" class="cube icon"></i> {{node.id}}</div
</div>
</template>

View File

@ -0,0 +1,68 @@
<style scope>
#nodeGroups {
margin: 15px 0;
position: relative;
column-width: 250px;
}
#nodeGroups>div {
width: 100%;
display: inline-block;
border: 1px solid #D4D4D5; box-shadow: none; margin: 0px;
margin-bottom: 1em;
}
#nodeGroups .ui.card>.content {padding: 1em 0 0;}
#nodeGroups .ui.list:first-child {border-top: 1px solid #eee;}
#nodeGroups .ui.divided.list>.item {padding: 0.5em 1em;}
#nodeGroups .ui.card>.content>.header:not(.ui) {margin: 0 1em;}
</style>
<template>
<div>
<div class="clearfix">
<router-link class="ui right floated primary button" to="/node/group/create"><i class="add icon"></i> 新分组</router-link>
<button class="ui right floated icon button" v-on:click="refresh"><i class="refresh icon"></i></button>
</div>
<div v-if="error != ''" class="header"><i class="attention icon"></i> {{error}}</div>
<div id="nodeGroups">
<div class="ui card" v-for="g in groups">
<div class="content">
<router-link class="header" :to="'/node/group/'+g.id">{{g.name}}</router-link>
<div class="description">
<div class="ui middle large aligned divided list">
<div class="item" v-for="n in g.nids">{{n}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'node_group',
data: function(){
return {
error: '',
groups: []
}
},
mounted: function(){
this.refresh();
},
methods: {
refresh(){
var vm = this;
this.$rest.GET('node/groups').
onsucceed(200, (resp)=>{
vm.groups = resp;
}).
onfailed((data)=>{vm.error = data}).
onend(()=>{vm.loading = false}).
do();
}
}
}
</script>

View File

@ -0,0 +1,90 @@
<template>
<div v-if="error != ''" class="ui negative message">
<div class="header"><i class="attention icon"></i> {{error}}</div>
</div>
<form v-else class="ui form" v-bind:class="{loading:loading}" v-on:submit.prevent>
<h3 class="ui header">{{action == 'CREATE' ? '添加' : '更新'}}节点分组</h3>
<div class="field">
<input type="text" ref="name" v-model:value="group.name" placeholder="分组名称">
</div>
<div class="field">
<label>分组节点</label>
<Dropdown title="选择节点" multiple="true" v-bind:items="allNodes" v-bind:selected="group.nids" v-on:change="changeGroup"/>
</div>
<div class="field">
<button class="fluid blue ui button" type="button" v-on:click="submit"><i class="upload icon"></i> 保存分组</button>
</div>
</form>
</template>
<script>
import Dropdown from './basic/Dropdown.vue';
export default {
name: 'node_group_edit',
data(){
return {
error: '',
loading: false,
action: '',
allNodes: [],
group: {
id: '',
name: '',
nids: ''
}
}
},
mounted(){
var vm = this;
if (this.$route.path.indexOf('/node/group/create') === 0) {
this.action = 'CREATE';
} else {
this.action = 'UPDATE';
this.loading = true;
this.$rest.GET('node/group/'+this.$route.params.id).
onsucceed(200, (resp)=>{
vm.group = resp;
}).
onfailed((data)=>{vm.error = data.error}).
onend(()=>{vm.loading = false}).
do();
}
this.$rest.GET('nodes').onsucceed(200, (resp)=>{
var allNodes = [];
for (var i in resp) {
allNodes.push(resp[i].id);
}
vm.allNodes = allNodes;
}).do();
},
methods: {
changeGroup(val, text){
if (val.length == 0) {
this.group.nids = [];
return;
}
this.group.nids = val.split(',');
},
submit(){
var exceptCode = this.action == 'CREATE' ? 201 : 200;
this.loading = true;
var vm = this;
this.$rest.PUT('node/group', this.group)
.onsucceed(exceptCode, ()=>{vm.$router.push('/node/group')})
.onfailed((resp)=>{console.log(resp)})
.onend(()=>{vm.loading=false})
.do();
}
},
components: {
Dropdown
}
}
</script>

View File

@ -27,6 +27,8 @@ import LogDetail from './components/LogDetail.vue';
import Job from './components/Job.vue';
import JobEdit from './components/JobEdit.vue';
import Node from './components/Node.vue';
import NodeGroup from './components/NodeGroup.vue';
import NodeGroupEdit from './components/NodeGroupEdit.vue';
var routes = [
{path: '/', component: Dash},
@ -35,7 +37,10 @@ var routes = [
{path: '/job', component: Job},
{path: '/job/create', component: JobEdit},
{path: '/job/edit/:group/:id', component: JobEdit},
{path: '/node', component: Node}
{path: '/node', component: Node},
{path: '/node/group', component: NodeGroup},
{path: '/node/group/create', component: NodeGroupEdit},
{path: '/node/group/:id', component: NodeGroupEdit}
];
var router = new VueRouter({