mirror of https://github.com/shunfei/cronsun
添加rest client,完善一些组件
parent
4460c5f4c1
commit
3f293a91ab
|
@ -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">
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import JobToolbar from './JobToolbar.vue';
|
||||
|
||||
export default {
|
||||
name: 'job',
|
||||
components: {
|
||||
|
|
|
@ -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' ? '添加' : '更新'}}任务
|
||||
<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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -50,6 +50,12 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
devServer: {
|
||||
proxy: {
|
||||
'/v1': {
|
||||
target: 'http://127.0.0.1:7079',
|
||||
secure: false
|
||||
}
|
||||
},
|
||||
historyApiFallback: true,
|
||||
noInfo: true
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue