添加rest client,完善一些组件

pull/1/head
Doflatango 2017-01-21 15:55:36 +08:00 committed by miraclesu
parent 4460c5f4c1
commit 3f293a91ab
9 changed files with 313 additions and 34 deletions

View File

@ -1,10 +1,11 @@
<template>
<div id="app">
<div class="ui purple inverted menu fixed">
<router-link class="active item" to="/">仪表盘</router-link>
<router-link class="item" to="/log">日志</router-link>
<router-link class="item" to="/job">任务</router-link>
<router-link class="item" to="/node">节点</router-link>
<div class="ui blue inverted menu fixed">
<div class="item">CRONSUN</div>
<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('/node') === 0}"><i class="tasks icon"></i> 节点</router-link>
</div>
<div style="height: 55px;"></div>
<div class="ui container">

View File

@ -5,8 +5,8 @@
</template>
<script>
import Vue from 'vue';
import JobToolbar from './JobToolbar.vue';
export default {
name: 'job',
components: {

View File

@ -1,65 +1,123 @@
<template>
<form class="ui form segment" v-on:submit.preven>
<h3 class="ui header">新建任务</h3>
<form class="ui form segment" v-bind:class="{loading:loading}" v-on:submit.prevent>
<h3 class="ui header">{{action == 'CREATE' ? '添加' : '更新'}}任务&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div class="ui toggle checkbox">
<input type="checkbox" class="hidden" v-bind:checked="!job.pause">
<label v-bind:style="{color: (job.pause?'red':'green')+' !important'}">{{job.pause ? '任务已暂停' : '开启'}}</label>
</div>
</h3>
<div class="two fields">
<div class="field">
<label>任务名称</label>
<input type="text" v-model="name" placeholder="任务名称">
<input type="text" ref="name" v-bind:value="job.name" v-on:input="updateValue($event.target.value)" placeholder="任务名称">
</div>
<div class="field">
<label>任务分组</label>
<div class="ui dropdown selection">
<input type="hidden">
<div class="default text">选择分组</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="default">Default</div>
<div class="item" data-value="ssp">SSP</div>
<div class="item" data-value="dc">数据中心</div>
</div>
</div>
<Dropdown title="选择分组" allowAdditions=true v-bind:items="groups" v-bind:selected="job.group" v-on:change="changeGroup"/>
</div>
</div>
<div class="field">
<label>任务脚本</label>
<input type="text" v-model="cmd" placeholder="任务脚本">
<input type="text" v-model="job.cmd" placeholder="任务脚本">
</div>
<div class="field">
<div class="ui toggle checkbox">
<input type="checkbox" class="hidden" v-bind:checked="!pause">
<label>{{pause ? '暂停' : '开启'}}</label>
<span v-if="job.rules.length == 0"><i class="warning circle icon"></i></span>
</div>
<JobEditRule v-for="(rule, index) in job.rules" :rule="rule" :index="index" v-on:remove="removeRule" v-on:change="changeRule"/>
<div class="two fields">
<div class="field">
<button class="fluid ui button" v-on:click="addNewTimer" type="button"><i class="history icon"></i> 添加定时器</button>
</div>
<div class="field">
<button class="fluid blue ui button" type="button" v-on:click="submit"><i class="upload icon"></i> 保存任务</button>
</div>
</div>
<button class="fluid blue ui button" type="submit">创建</button>
</form>
</template>
<script>
import JobEditRule from './JobEditRule.vue';
import Dropdown from './basic/Dropdown.vue';
export default {
name: 'job-edit',
data: function(){
return {
name: '',
group: 'default',
cmd: '',
pause: false
action: 'CREATE',
groups: [],
loading: false,
job: {
id: '',
name: '',
group: 'default',
cmd: '',
pause: false,
rules: []
}
}
},
methods: {
updateValue: function(v){
var tv = v.replace(/[\*\/]/g, '');
this.job.name = tv;
if (tv !== v) {
this.$refs.name.value = tv;
}
},
addNewTimer: function(){
this.job.rules.push({});
},
changeGroup: function(val, text){
this.job.group = val;
},
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;
var vm = this;
this.$rest.PUT('job', JSON.stringify(this.job))
.onsucceed(exceptCode, ()=>{vm.$router.push('/job')})
.onfailed((resp)=>{console.log(resp)})
.onend(()=>{vm.loading=false})
.do();
}
},
mounted: function(){
this.action = this.$route.path.indexOf('/job/create') === 0 ? 'CREATE' : 'UPDATE';
var vm = this;
this.$rest.GET('job/groups').onsucceed(200, (resp)=>{
!resp.includes('default') && resp.unshift('default');
vm.groups = resp;
}).do();
$(this.$el).find('.checkbox').checkbox({
onChange: function(){
vm.pause = !vm.pause;
vm.job.pause = !vm.job.pause;
}
});
$(this.$el).find('.dropdown').dropdown({
allowAdditions: true,
onChange: function(value, text, $choice){
vm.group = value;
vm.job.group = value;
}
}).dropdown('set selected', this.group);
}).dropdown('set selected', this.job.group);
},
components: {
JobEditRule,
Dropdown
}
}
</script>

View File

@ -1,9 +1,61 @@
<template>
<router-link class="ui button" to="/job/create">新建任务</router-link>
<div style="margin-bottom:20px;">
<h4 class="ui horizontal divider header">定时器 - {{index}} <a href="#" v-on:click.prevent="remove">删除</a></h4>
<div class="two fields">
<div class="field">
<input type="text" v-bind:value="rule.timer" v-on:input="change('timer', $event.target.value)" placeholder="定时 * * * * *crontab 格式)"/>
</div>
<div class="field">
<Dropdown title="节点分组" v-bind:items="nodeGroups" multiple="true"></Dropdown>
</div>
</div>
<div class="field">
<label><strong style="color:green;">+</strong> 同时在这些节点上面运行任务</label>
<Dropdown title="选择节点" v-bind:items="activityNodes" multiple="true"></Dropdown>
</div>
<div class="field">
<label><strong style="color:red;">-</strong> 不在这些节点上面运行任务</label>
<Dropdown title="选择节点" v-bind:items="activityNodes" multiple="true"></Dropdown>
</div>
</div>
</template>
<script>
import Dropdown from './basic/Dropdown.vue';
export default {
name: 'job-edit-rule'
name: 'job-edit-rule',
props: ['rule', 'index'],
data: function(){
return {
nodeGroups: [],
activityNodes: []
}
},
mounted: function(){
var vm = this;
this.$rest.GET('node/activitys').onsucceed(200, (resp)=>{vm.activityNodes = resp}).do();
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;
});
},
methods: {
remove: function(){
this.$emit('remove', this.index);
},
change: function(key, val){
this.$emit('change', this.index, key, val);
}
},
components: {
Dropdown
}
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<router-link class="ui button" to="/job/create">任务</router-link>
<router-link class="ui button" to="/job/create"><i class="add to calendar icon"></i> 任务</router-link>
</template>
<script>

View File

@ -0,0 +1,35 @@
<template>
<div class="ui search selection dropdown" v-bind:class="{multiple: multiple}">
<input type="hidden">
<div class="default text">{{title}}</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" v-for="item in items" v-bind:data-value="typeof item === 'object' ? item.value : item">{{typeof item === 'object' ? item.name : item}}</div>
</div>
</div>
</template>
<script>
export default {
name: 'dropdown',
props: ['title', 'items', 'selected', 'allowAdditions', 'multiple'],
mounted: function() {
if (this.title.length === 0) this.title = '选择分组';
var vm = this;
$(this.$el).dropdown({
allowAdditions: !!this.allowAdditions,
hideAdditions: false,
forceSelection: false,
onChange: function(value, text, $choice){
vm.$emit('change', value, text);
}
});
if (this.selected !== null) {
$(this.$el).dropdown('set selected', this.selected);
}
}
}
</script>

View File

@ -0,0 +1,115 @@
var sendXHR = function(opt) {
var xhr = new XMLHttpRequest();
xhr.open(opt.method, opt.url, true);
if (typeof opt.onexception == 'function') {
var warpExceptionHandler = ()=>{
opt.onexception(msg);
typeof opt.onend == 'function' && opt.onend(xhr);
}
xhr.onabort=()=>{warpExceptionHandler('request aborted.')};
xhr.onerror=()=>{warpExceptionHandler('request error.')};
xhr.ontimeout=()=>{warpExceptionHandler('request timeout.')};
}
xhr.onreadystatechange = function(){
if (xhr.readyState !== XMLHttpRequest.DONE) {
return;
}
var data;
if (typeof xhr.response != 'object') {
try {
data = JSON.parse(xhr.response)
} catch(e) {
data = xhr.response;
}
} else {
data = xhr.response;
}
if (xhr.status != opt.successCode) {
typeof opt.onfailed == 'function' && opt.onfailed(data, xhr);
} else if (typeof opt.onsucceed == 'function') {
opt.onsucceed(data, xhr);
}
typeof opt.onend == 'function' && opt.onend(xhr);
}
xhr.send(opt.data);
}
class request {
constructor(url, method, data){
this._url = url;
this._method = method;
this._data = data;
}
do(){
sendXHR({
method: this._method,
url: this._url,
data: this._data,
successCode: this._successCode,
onsucceed: this._onsucceed,
onfailed: this._onfailed,
onexception: this._onexception,
onend: this._onend,
});
}
onsucceed(successCode, f){
this._successCode = successCode;
this._onsucceed = f;
return this;
}
onfailed(f){
this._onfailed = f;
return this;
}
onexception(f){
this._onexception = f;
return this;
}
onend(f){
this._onend = f;
return this;
}
}
export default class Rest {
constructor(prefix, defaultFailedHandler, defaultExceptionHandler){
this.prefix = prefix;
this.defaultFailedHandler = defaultFailedHandler; // function(url, resp){}
this.defaultExceptionHandler = defaultExceptionHandler;
};
GET(url){
return new request(this.prefix+url, 'GET')
.onfailed(this.defaultFailedHandler)
.onexception(this.defaultExceptionHandler);
};
POST(url, formdata){
return new request(this.prefix+url, 'POST', formdata)
.onfailed(this.defaultFailedHandler)
.onexception(this.defaultExceptionHandler);
};
PUT(url, formdata){
return new request(this.prefix+url, 'PUT', formdata)
.onfailed(this.defaultFailedHandler)
.onexception(this.defaultExceptionHandler);
};
DELETE(url){
return new request(this.prefix+url, 'DELETE')
.onfailed(this.defaultFailedHandler)
.onexception(this.defaultExceptionHandler);
}
}

View File

@ -5,6 +5,18 @@ require('semantic-ui/dist/semantic.min.css');
import Vue from 'vue';
Vue.config.debug = true;
// global restful client
import Rest from './libraries/rest-client.js';
const RestApi =(Vue, options)=>{
Vue.prototype.$rest = new Rest('/v1/');
};
Vue.use(RestApi);
// global event bus
Vue.use((Vue)=>{
Vue.prototype.$bus = new Vue();
});
import VueRouter from 'vue-router';
Vue.use(VueRouter);

View File

@ -50,6 +50,12 @@ module.exports = {
}
},
devServer: {
proxy: {
'/v1': {
target: 'http://127.0.0.1:7079',
secure: false
}
},
historyApiFallback: true,
noInfo: true
},