主机、任务日志列表增加搜索功能

pull/21/merge
ouqiang 8 years ago
parent 9339137bca
commit 09c6927884

@ -1,6 +1,9 @@
package models
import "github.com/ouqiang/gocron/modules/ssh"
import (
"github.com/ouqiang/gocron/modules/ssh"
"github.com/go-xorm/xorm"
)
@ -60,10 +63,12 @@ func (host *Host) NameExists(name string, id int16) (bool, error) {
return count > 0, err
}
func (host *Host) List() ([]Host, error) {
func (host *Host) List(params CommonMap) ([]Host, error) {
host.parsePageAndPageSize()
list := make([]Host, 0)
err := Db.Desc("id").Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
session := Db.Desc("id")
host.parseWhere(session, params)
err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
return list, err
}
@ -80,6 +85,21 @@ func (host *Host) Total() (int64, error) {
return Db.Count(host)
}
// 解析where
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
if len(params) == 0 {
return
}
id, ok := params["Id"]
if ok && id.(int) > 0 {
session.And("id = ?", id)
}
name, ok := params["Name"]
if ok && name.(string) != "" {
session.And("name = ?", name)
}
}
func (host *Host) parsePageAndPageSize() {
if host.Page <= 0 {
host.Page = Page

@ -130,7 +130,7 @@ func (task *Task) List(params CommonMap) ([]TaskHost, error) {
list := make([]TaskHost, 0)
fields := "t.*, host.alias"
session := Db.Alias("t").Join("LEFT", "host", "t.host_id=host.id")
parseWhere(session, params)
task.parseWhere(session, params)
err := session.Cols(fields).Desc("t.id").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
return list, err
@ -141,17 +141,21 @@ func (task *Task) Total() (int64, error) {
}
// 解析where
func parseWhere(session *xorm.Session, params CommonMap) {
func (task *Task) parseWhere(session *xorm.Session, params CommonMap) {
if len(params) == 0 {
return
}
id, ok := params["Id"]
if ok && id.(int) > 0 {
session.And("t.id = ?", id)
}
hostId, ok := params["HostId"]
if ok && hostId.(int) > 0 {
session.And("host_id = ? ", hostId)
session.And("host_id = ?", hostId)
}
name, ok := params["Name"]
if ok && name.(string) != "" {
session.And("name = ?", name)
session.And("t.name LIKE ?", "%" + name.(string) + "%")
}
protocol, ok := params["Protocol"]
if ok && protocol.(int) > 0 {

@ -2,6 +2,7 @@ package models
import (
"time"
"github.com/go-xorm/xorm"
)
type TaskType int8
@ -20,7 +21,7 @@ type TaskLog struct {
Hostname string `xorm:"varchar(512) notnull defalut '' "` // SSH主机名逗号分隔
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
Status Status `xorm:"tinyint notnull default 1"` // 状态 1:执行中 2:执行完毕 0:执行失败
Status Status `xorm:"tinyint notnull default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕
Result string `xorm:"varchar(65535) notnull defalut '' "` // 执行结果
Page int `xorm:"-"`
PageSize int `xorm:"-"`
@ -47,10 +48,12 @@ func (taskLog *TaskLog) setStatus(id int64, status Status) (int64, error) {
return taskLog.Update(id, CommonMap{"status": status})
}
func (taskLog *TaskLog) List() ([]TaskLog, error) {
func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
taskLog.parsePageAndPageSize()
list := make([]TaskLog, 0)
err := Db.Desc("id").Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
session := Db.Desc("id")
taskLog.parseWhere(session, params)
err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
if len(list) > 0 {
for i, item := range list {
endTime := item.EndTime
@ -85,4 +88,23 @@ func (taskLog *TaskLog) parsePageAndPageSize() {
func (taskLog *TaskLog) pageLimitOffset() int {
return (taskLog.Page - 1) * taskLog.PageSize
}
// 解析where
func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) {
if len(params) == 0 {
return
}
taskId, ok := params["TaskId"]
if ok && taskId.(int) > 0 {
session.And("task_id = ?", taskId)
}
protocol, ok := params["Protocol"]
if ok && protocol.(int) > 0 {
session.And("protocol = ?", protocol)
}
status, ok := params["Status"]
if ok && status.(int) > -1 {
session.And("status = ?", status)
}
}

@ -24,8 +24,6 @@ function Util() {
swal(FAILURE_MESSAGE, '未知错误', 'error');
};
util.get = function(url, callback) {
var SUCCESS = 0; // 操作成功
var FAILURE_MESSAGE = '操作失败';
$.get(
url,
function(response) {
@ -76,4 +74,25 @@ function Util() {
return util;
}
function registerSelectFormValidation(type, $form, $select, selectName) {
$.fn.form.settings.rules[type] = function(value) {
var success = true;
var selectedIndex = $($form).form("get value", selectName);
$($select).find("option").each(function() {
var value = $(this).val();
var match = $(this).data("match");
var validateType = $(this).data("validate-type");
if (selectedIndex == value && validateType == type && match) {
var matchValue = $($form).form("get value", match);
if (!$.trim(matchValue)) {
success = false;
return false;
}
}
});
return success;
};
}
var util = new Util();

@ -12,12 +12,14 @@ import (
func Index(ctx *macaron.Context) {
hostModel := new(models.Host)
hosts, err := hostModel.List()
queryParams := parseQueryParams(ctx)
hosts, err := hostModel.List(queryParams)
if err != nil {
logger.Error(err)
}
ctx.Data["Title"] = "主机列表"
ctx.Data["Hosts"] = hosts
ctx.Data["Params"] = queryParams
ctx.HTML(200, "host/index")
}
@ -142,4 +144,15 @@ func Remove(ctx *macaron.Context) string {
}
return json.Success("操作成功", nil)
}
// 解析查询参数
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
var params models.CommonMap = models.CommonMap{}
params["Id"] = ctx.QueryInt("id")
params["Name"] = ctx.QueryTrim("name")
params["Page"] = ctx.QueryInt("page")
params["PageSize"] = ctx.QueryInt("page_size")
return params
}

@ -11,7 +11,6 @@ import (
"github.com/go-macaron/session"
"github.com/go-macaron/csrf"
"github.com/go-macaron/toolbox"
"github.com/go-macaron/gzip"
"strings"
"github.com/ouqiang/gocron/modules/app"
)
@ -35,6 +34,7 @@ func Register(m *macaron.Macaron) {
})
// 50x错误
m.InternalServerError(func(ctx *macaron.Context) {
logger.Debug("500错误")
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR"
ctx.HTML(500, "error/500")
@ -85,7 +85,6 @@ func Register(m *macaron.Macaron) {
func RegisterMiddleware(m *macaron.Macaron) {
m.Use(macaron.Logger())
m.Use(macaron.Recovery())
m.Use(gzip.Gziper())
m.Use(macaron.Static(StaticDir))
m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: "templates",
@ -106,7 +105,7 @@ func RegisterMiddleware(m *macaron.Macaron) {
// 系统未安装,重定向到安装页面
m.Use(func(ctx *macaron.Context) {
installUrl := "/install"
if strings.HasPrefix(ctx.Req.RequestURI, installUrl) {
if strings.HasPrefix(ctx.Req.URL.Path, installUrl) {
return
}
if !app.Installed {
@ -116,6 +115,16 @@ func RegisterMiddleware(m *macaron.Macaron) {
// 设置模板共享变量
m.Use(func(ctx *macaron.Context) {
ctx.Data["URI"] = ctx.Req.URL.Path
urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/")
paths := strings.Split(urlPath, "/")
ctx.Data["Controller"] = ""
ctx.Data["Action"] = ""
if len(paths) > 0 {
ctx.Data["Controller"] = paths[0]
}
if len(paths) > 1 {
ctx.Data["Action"] = paths[1]
}
})
}

@ -46,7 +46,7 @@ func Create(ctx *macaron.Context) {
func Edit(ctx *macaron.Context) {
id := ctx.ParamsInt(":id")
hostModel := new(models.Host)
hosts, err := hostModel.List()
hosts, err := hostModel.List(models.CommonMap{})
if err != nil || len(hosts) == 0 {
logger.Error(err)
}
@ -87,11 +87,16 @@ func Store(ctx *macaron.Context, form TaskForm) string {
return json.CommonFailure("请选择主机名")
}
if form.Protocol != models.TaskHTTP {
taskModel.HostId = form.HostId
} else {
taskModel.HostId = 0
}
taskModel.Name = form.Name
taskModel.Protocol = form.Protocol
taskModel.Command = form.Command
taskModel.Timeout = form.Timeout
taskModel.HostId = form.HostId
taskModel.Remark = form.Remark
taskModel.Status = form.Status
taskModel.RetryTimes = form.RetryTimes
@ -195,21 +200,20 @@ func addTaskToTimer(id int) {
// 解析查询参数
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
var params models.CommonMap = models.CommonMap{}
params["Id"] = ctx.QueryInt("id")
params["HostId"] = ctx.QueryInt("host_id")
params["Name"] = ctx.QueryTrim("name")
params["Protocol"] = ctx.QueryInt("protocol")
params["Status"] = ctx.QueryInt("status")
params["Status"] = ctx.QueryInt("status") - 1
params["Page"] = ctx.QueryInt("page")
params["PageSize"] = ctx.QueryInt("page_size")
logger.Debug("%+v", params)
return params
}
func setHostsToTemplate(ctx *macaron.Context) {
hostModel := new(models.Host)
hosts, err := hostModel.List()
hosts, err := hostModel.List(models.CommonMap{})
if err != nil || len(hosts) == 0 {
logger.Error(err)
}

@ -12,12 +12,14 @@ import (
func Index(ctx *macaron.Context) {
logModel := new(models.TaskLog)
logs, err := logModel.List()
queryParams := parseQueryParams(ctx)
logs, err := logModel.List(queryParams)
if err != nil {
logger.Error(err)
}
ctx.Data["Title"] = "任务日志"
ctx.Data["Logs"] = logs
ctx.Data["Params"] = queryParams
ctx.HTML(200, "task/log")
}
@ -31,4 +33,16 @@ func Clear(ctx *macaron.Context) string {
}
return json.Success(utils.SuccessContent, nil)
}
// 解析查询参数
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
var params models.CommonMap = models.CommonMap{}
params["TaskId"] = ctx.QueryInt("task_id")
params["Protocol"] = ctx.QueryInt("protocol")
params["Status"] = ctx.QueryInt("status") - 1
params["Page"] = ctx.QueryInt("page")
params["PageSize"] = ctx.QueryInt("page_size")
return params
}

@ -57,6 +57,7 @@ func (task *Task) Add(taskModel models.TaskHost) {
}
cronName := strconv.Itoa(taskModel.Id)
// Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题
Cron.RemoveJob(cronName)
err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName)
if err != nil {
@ -123,20 +124,17 @@ func (h *HTTPHandler) Run(taskModel models.TaskHost) (result string, err error)
req.Header.Set("User-Agent", "golang/gocron")
resp, err := client.Do(req)
defer func() {
if resp != nil {
resp.Body.Close()
}
}()
if err != nil {
logger.Error("任务处理HTTP请求错误-", err.Error())
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
logger.Error("任务处理#读取HTTP请求返回值失败-", err.Error())
return
}
// 返回状态码非200均为失败
if resp.StatusCode != 200 {
return string(body), errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode))
}
@ -248,10 +246,10 @@ func execJob(handler Handler, taskModel models.TaskHost) TaskResult {
return TaskResult{Result: output, Err: err, RetryTimes: i}
}
i++
// 重试规则每次递增1分钟
time.Sleep( time.Duration(i) * 60 * time.Second)
if i < execTimes {
logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error())
// 重试间隔时间每次递增1分钟
time.Sleep( time.Duration(i) * time.Minute)
}
}

@ -5,7 +5,7 @@
<div class="bigcontainer">
<div class="fl">
<p>&copy; 2017 gocron
<i class="Github Alternate icon"></i><a href="https://github.com/ouqiang/cron-scheduler" target="_blank">
<i class="Github Alternate icon"></i><a href="https://github.com/ouqiang/gocron" target="_blank">
GitHub
</a>
</p>

@ -18,6 +18,12 @@
body {
overflow-x: hidden;
}
select {
height: 40px;
}
.fields.search > .field {
margin-right: 5px;
}
</style>
</head>
<body>
@ -53,11 +59,11 @@
<div class="ui teal inverted menu">
<div class="bigcontainer">
<div class="right menu">
<a class="item {{{if eq .URI "/"}}}active{{{end}}}" href="/"><i class="home icon"></i>首页</a>
<a class="item {{{if eq .URI "/task"}}}active{{{end}}}" href="/task"><i class="tasks icon"></i>任务</a>
<a class="item {{{if eq .URI "/host"}}}active{{{end}}}" href="/host"><i class="linux icon"></i>主机</a>
<a class="item {{{if eq .URI "/user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>账户</a>
<a class="item {{{if eq .URI "/admin"}}}active{{{end}}}" href="/admin"><i class="settings icon"></i>管理</a>
<a class="item {{{if eq .Controller ""}}}active{{{end}}}" href="/"><i class="home icon"></i>首页</a>
<a class="item {{{if eq .Controller "task"}}}active{{{end}}}" href="/task"><i class="tasks icon"></i>任务</a>
<a class="item {{{if eq .Controller "host"}}}active{{{end}}}" href="/host"><i class="linux icon"></i>主机</a>
<a class="item {{{if eq .Controller "user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>账户</a>
<a class="item {{{if eq .Controller "admin"}}}active{{{end}}}" href="/admin"><i class="settings icon"></i>管理</a>
</div>
</div>
</div>

@ -1,7 +1,6 @@
{{{ template "common/header" . }}}
<div class="ui grid">
<!--the vertical menu-->
{{{ template "host/menu" . }}}
<div class="twelve wide column">
@ -48,19 +47,10 @@
<div class="four fields">
<div class="field">
<label>认证方式</label>
<div class="ui dropdown selection">
{{{ if .Host }}}
<input type="hidden" name="auth_type" value="{{{if eq .Host.AuthType 1 }}}1{{{else}}}2{{{end}}}">
{{{else}}}
<input type="hidden" name="auth_type" value="2">
{{{end}}}
<div class="default text">公钥</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="2">公钥</div>
<div class="item" data-value="1">密码</div>
</div>
</div>
<select name="auth_type" id="authType">
<option value="2" {{{if .Host}}} {{{if eq .Host.AuthType 2}}}selected {{{end}}} {{{end}}} data-validate-type="selectPrivateKey" data-match="private_key">公钥</option>
<option value="1" {{{if .Host}}} {{{if eq .Host.AuthType 1}}}selected {{{end}}} {{{end}}} data-validate-type="selectPassword" data-match="password">密码</option>
</select>
</div>
</div>
<div class="two fields">
@ -91,12 +81,14 @@
<div class="ui primary submit button">保存</div>
</form>
</div>
<!--the newDevice form-->
</div>
<script type="text/javascript">
$('.ui.form').form(
var $uiForm = $('.ui.form');
registerSelectFormValidation("selectPrivateKey", $uiForm, $('#authType'), 'auth_type');
registerSelectFormValidation("selectPassword", $uiForm, $('#authType'), 'auth_type');
$($uiForm).form(
{
onSuccess: function(event, fields) {
util.post('/host/store', fields, function(code, message) {
@ -137,8 +129,26 @@
identifier : 'port',
rules: [
{
type : 'integer',
prompt : '请输入SSH端口'
type : 'integer[1..65535]',
prompt : '请输入有效的端口号'
}
]
},
PrivateKey: {
identifier : 'private_key',
rules: [
{
type : 'selectPrivateKey',
prompt : '请输入私钥'
}
]
},
Password: {
identifier : 'password',
rules: [
{
type : 'selectPassword',
prompt : '请输入密码'
}
]
}

@ -14,9 +14,23 @@
</h3>
</div>
</div>
<form class="ui form">
<div class="three fields">
<div class="field">
<input type="text" placeholder="ID" name="id" value="{{{if gt .Params.Id 0}}}{{{.Params.Id}}}{{{end}}}">
</div>
<div class="field">
<input type="text" placeholder="主机名" name="name" value="{{{.Params.Name}}}">
</div>
<div class="field">
<button class="ui linkedin submit button">搜索</button>
</div>
</div>
</form>
<table class="ui striped table">
<thead>
<tr>
<th>ID</th>
<th>主机名</th>
<th>别名</th>
<th>用户名</th>
@ -28,6 +42,7 @@
<tbody>
{{{range $i, $v := .Hosts}}}
<tr>
<td>{{{.Id}}}</td>
<td>{{{.Name}}}</td>
<td>{{{.Alias}}}</td>
<td>{{{.Username}}}</td>

@ -1,5 +1,4 @@
{{{ template "common/header" . }}}
<div class="ui grid">
{{{template "task/menu" .}}}
<div class="twelve wide column">
@ -14,107 +13,74 @@
</div>
</div>
<form class="ui form">
<div class="five fields">
<div class="six fields search">
<div class="one wide field">
<input type="text" placeholder="任务ID" name="id" value="{{{if gt .Params.Id 0}}}{{{.Params.Id}}}{{{end}}}">
</div>
<div class="field">
<input type="text" placeholder="任务名称" name="name">
<input type="text" placeholder="任务名称" name="name" value="{{{.Params.Name}}}">
</div>
<div class="field">
<label>主机</label>
<div class="ui dropdown selection">
<input type="hidden" name="protocol" value="{{{if gt .Params.Protocol 0}}}{{{.Params.Protocol}}}{{{else}}}3{{{end}}}">
<div class="default text">本地命令</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="3">本地命令</div>
<div class="item" data-value="2">SSH</div>
<div class="item" data-value="1">HTTP</div>
</div>
</div>
<div class="inline fields">
<select name="host_id" id="hostId">
<option value="">选择主机</option>
{{{range $i, $v := .Hosts}}}
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="host_id" tabindex="0" class="hidden" value="{{{.Id}}}"
{{{if eq $.Params.HostId .Id }}} checked {{{end}}}>
</div>
</div>
<option value="{{{.Id}}}" {{{if eq $.Params.HostId .Id }}} selected {{{end}}} >{{{.Alias}}}-{{{.Name}}}</option>
{{{end}}}
</div>
</select>
</div>
<div class="field">
<div class="ui dropdown selection">
<input type="hidden" name="protocol" value="{{{if gt .Params.Protocol 0}}}{{{.Params.Protocol}}}{{{else}}}3{{{end}}}">
<div class="default text">本地命令</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="-1">协议</div>
<div class="item" data-value="3">本地命令</div>
<div class="item" data-value="2">SSH</div>
<div class="item" data-value="1">HTTP</div>
</div>
</div>
<select name="protocol" id="protocol">
<option value="0">选择协议</option>
<option value="3" {{{if eq .Params.Protocol 3}}}selected{{{end}}}>本地命令</option>
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
</select>
</div>
<div class="field">
<div class="ui dropdown selection">
<input type="hidden" name="status" value="{{{if eq .Params.Status 1}}}1{{{else}}}2{{{end}}}">
<div class="default text">暂停</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="-1">状态</div>
<div class="item" data-value="2">暂停</div>
<div class="item" data-value="1">激活</div>
</div>
</div>
<select name="status">
<option value="0">状态</option>
<option value="1" {{{if eq .Params.Status 0}}}selected{{{end}}} >暂停</option>
<option value="2" {{{if eq .Params.Status 1}}}selected{{{end}}}>激活</option>
</select>
</div>
<div class="field">
<button class="ui linkedin submit button">搜索</button>
</div>
</div>
</form>
<table class="ui violet table">
<thead>
<tr>
<th>任务名称</th>
<th>cron表达式</th>
<th>协议</th>
<th width="5%">命令</th>
<th>超时时间(秒)</th>
<th>重试次数</th>
<th>主机</th>
<th>备注</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{{{range $i, $v := .Tasks}}}
<tr>
<td>{{{.Task.Name}}}</td>
<td>{{{.Spec}}}</td>
<td>{{{if eq .Protocol 1}}} HTTP {{{else}}} SSH {{{end}}}</td>
<td>{{{.Command}}}</td>
<td>{{{if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</td>
<td>{{{.RetryTimes}}}</td>
<td>{{{.Alias}}}</td>
<td>{{{.Remark}}}</td>
<td>{{{if eq .Status 1}}}<i class="large checkmark blue icon"></i> {{{else}}} <i class="large red minus icon"></i> {{{end}}}</td>
<td>
<a class="ui purple button" href="/task/edit/{{{.Id}}}">编辑</a>
{{{if eq .Status 1}}}
<button class="ui primary button" @click="changeStatus({{{.Id}}},{{{.Status}}})">暂停</button>
{{{else}}}
<button class="ui blue button" @click="changeStatus({{{.Id}}},{{{.Status}}})">激活 </button>
{{{end}}}
<button class="ui positive button" @click="remove({{{.Id}}})">删除</button> <br>
<div style="margin-top:10px;">
<button class="ui twitter button" @click="run({{{.Id}}})">手动运行</button>
<a class="ui instagram button" href="/task/log?task_id={{{.Id}}}">查看日志</a>
</div>
</td>
</tr>
{{{end}}}
</tbody>
</table>
{{{range $i, $v := .Tasks}}}
<div class="ui device two column middle aligned vertical grid list segment">
<div class="column verborder">
<div class="ui info segment">
<h5 class="ui header">{{{.Task.Name}}} {{{if eq .Status 1}}}<i class="large checkmark blue icon"></i> {{{else}}} <i class="large red minus icon"></i> {{{end}}}
</h5>
<p>任务ID <span class="stress">{{{.Id}}}</span></p>
<p>cron表达式 {{{.Spec}}}</p>
<p>协议: {{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} SSH {{{else if eq .Protocol 3}}}本地命令{{{end}}}</p>
<p class="sensorStatus">命令:{{{.Command}}}</p>
<p class="sensorStatus">超时时间:{{{if gt .Timeout 0}}}{{{.Timeout}}}秒{{{else}}}不限制{{{end}}}</p>
<p>重试次数: {{{.RetryTimes}}}</p>
{{{if eq .Protocol 2}}}
<p>主机: {{{.Alias}}}</p>
{{{end}}}
<p>备注: {{{.Remark}}}</p>
</div>
</div>
<div class="center aligned column">
<div class="ui buttons">
<a class="ui purple button" href="/task/edit/{{{.Id}}}">编辑</a>
{{{if eq .Status 1}}}
<button class="ui primary button" @click="changeStatus({{{.Id}}},{{{.Status}}})">暂停</button>
{{{else}}}
<button class="ui blue button" @click="changeStatus({{{.Id}}},{{{.Status}}})">激活 </button>
{{{end}}}
<button class="ui positive button" @click="remove({{{.Id}}})">删除</button> <br>
<button class="ui twitter button" @click="run({{{.Id}}})">手动运行</button>
<a class="ui instagram button" href="/task/log?task_id={{{.Id}}}">查看日志</a>
</div>
</div>
</div>
{{{end}}}
</div>
</div>
@ -124,7 +90,7 @@
var vue = new Vue(
{
el: '.ui.violet.table',
el: '.ui.list',
methods: {
changeStatus: function (id ,status) {
var url = '';
@ -155,4 +121,4 @@
</script>
{{{ template "common/footer" . }}}
{{{ template "common/footer" . }}}

@ -22,9 +22,36 @@
</h3>
</div>
</div>
<form class="ui form">
<div class="six fields search">
<div class="field">
<input type="text" placeholder="任务ID" name="task_id" value="{{{if gt .Params.TaskId 0}}}{{{.Params.TaskId}}}{{{end}}}">
</div>
<div class="field">
<select name="protocol" id="protocol">
<option value="0">协议</option>
<option value="3" {{{if eq .Params.Protocol 3}}}selected{{{end}}}>本地命令</option>
<option value="2" {{{if eq .Params.Protocol 2}}}selected{{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
<option value="1" {{{if eq .Params.Protocol 1}}}selected{{{end}}}>HTTP</option>
</select>
</div>
<div class="field">
<select name="status">
<option value="0">状态</option>
<option value="1" {{{if eq .Params.Status 0}}}selected{{{end}}} >失败</option>
<option value="2" {{{if eq .Params.Status 1}}}selected{{{end}}}>执行中</option>
<option value="3" {{{if eq .Params.Status 2}}}selected{{{end}}}>成功</option>
</select>
</div>
<div class="field">
<button class="ui linkedin submit button">搜索</button>
</div>
</div>
</form>
<table class="ui pink table">
<thead>
<tr>
<th>任务ID</th>
<th>任务名称</th>
<th>cron表达式</th>
<th>协议</th>
@ -39,6 +66,7 @@
<tbody>
{{{range $i, $v := .Logs}}}
<tr>
<td><a href="/task?id={{{.TaskId}}}">{{{.TaskId}}}</a></td>
<td>{{{.Name}}}</td>
<td>{{{.Spec}}}</td>
<td>{{{if eq .Protocol 1}}} HTTP {{{else if eq .Protocol 2}}} SSH {{{else}}} 本地命令 {{{end}}}</td>
@ -128,55 +156,5 @@
});
});
}
$('.ui.form').form(
{
onSuccess: function(event, fields) {
util.post('/host/store', fields, function(code, message) {
location.reload();
});
return false;
},
fields: {
name: {
identifier : 'name',
rules: [
{
type : 'empty',
prompt : '请输入主机名'
}
]
},
alias: {
identifier : 'alias',
rules: [
{
type : 'empty',
prompt : '请输入主机别名'
}
]
},
username: {
identifier : 'username',
rules: [
{
type : 'empty',
prompt : '请输入SSH用户名'
}
]
},
port: {
identifier : 'port',
rules: [
{
type : 'integer',
prompt : '请输入SSH端口'
}
]
}
},
inline : true
});
</script>
{{{ template "common/footer" . }}}

@ -1,5 +1,4 @@
{{{ template "common/header" . }}}
<div class="ui grid">
{{{template "task/menu" .}}}
<div class="twelve wide column">
@ -25,47 +24,31 @@
</div>
<div class="two fields">
<div class="field">
<label>crontab表达式 (每行一个表达式)</label>
<textarea rows="5" name="spec">{{{.Task.Spec}}}</textarea>
<label>crontab表达式</label>
<div class="ui small left icon input">
<textarea rows="5" name="spec">{{{.Task.Spec}}}</textarea>
</div>
</div>
</div>
<div class="three fields">
<div class="field">
<label>协议</label>
<div class="ui dropdown selection">
{{{if .Task}}}
<input type="hidden" name="protocol" value="{{{if (gt .Task.Protocol 0)}}}{{{.Task.Protocol}}}{{{else}}}3{{{end}}}">
{{{else}}}
<input type="hidden" name="protocol" value="3">
{{{end}}}
<div class="default text">本地命令</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="3">本地命令</div>
<div class="item" data-value="2">SSH</div>
<div class="item" data-value="1">HTTP</div>
</div>
</div>
<select name="protocol" id="protocol">
<option value="3" {{{if .Task}}} {{{if eq .Task.Protocol 3}}}selected{{{end}}} {{{end}}}>本地命令</option>
<option value="2" {{{if .Task}}} {{{if eq .Task.Protocol 2}}}selected{{{end}}} {{{end}}} data-match="host_id" data-validate-type="selectProtocol">SSH</option>
<option value="1" {{{if .Task}}} {{{if eq .Task.Protocol 1}}}selected{{{end}}} {{{end}}}>HTTP</option>
</select>
</div>
</div>
<div class="field">
<label>主机</label>
<div class="inline fields">
<select name="host_id" id="hostId">
<option value="">选择主机</option>
{{{range $i, $v := .Hosts}}}
<div class="field">
<div class="ui radio checkbox">
{{{if $.Task}}}
<input type="radio" name="host_id" tabindex="0" class="hidden" value="{{{.Id}}}"
{{{if and (eq $.Task.Protocol 2) (eq $.Task.HostId .Id) }}} checked {{{end}}}
>
{{{else}}}
<input type="radio" name="host_id" tabindex="0" class="hidden" value="{{{.Id}}}">
{{{end}}}
<label>{{{.Alias}}}-{{{.Name}}}</label>
</div>
<option value="{{{.Id}}}" {{{if $.Task}}}{{{if eq $.Task.HostId .Id }}} selected {{{end}}} {{{end}}}>{{{.Alias}}}-{{{.Name}}}</option>
</div>
{{{end}}}
</div>
</select>
</div>
<div class="two fields">
<div class="field">
@ -73,32 +56,23 @@
<textarea rows="5" name="command">{{{.Task.Command}}}</textarea>
</div>
</div>
<div class="two fields">
<div class="three fields">
<div class="field">
<label>任务超时时间 (单位秒, 默认0不限制超时)</label>
<input type="text" name="timeout" value="{{{.Task.Timeout}}}">
</div>
<div class="field">
<label>任务执行事变 (单位秒,0不限制,不能超过24小时)</label>
<label>任务重试次数 (默认0, 取值范围1-10)</label>
<input type="text" name="timeout" value="{{{.Task.Timeout}}}">
</div>
</div>
<div class="three fields">
<div class="field">
<label>任务状态 (任务添加成功后,是否立即调度)</label>
<div class="ui dropdown selection">
{{{if .Task}}}
<input type="hidden" name="status" value="{{{if and .Task.Status (eq .Task.Status 1)}}}1{{{else}}}2{{{end}}}">
{{{else}}}
<input type="hidden" name="status" value="2">
{{{end}}}
<div class="default text">暂停</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="2">暂停</div>
<div class="item" data-value="1">激活</div>
</div>
</div>
<select name="status">
<option value="2"{{{if .Task}}} {{{if eq .Task.Status 2}}}selected{{{end}}} {{{end}}}>暂停</option>
<option value="1" {{{if .Task}}} {{{if eq .Task.Status 1}}}selected{{{end}}} {{{end}}}>激活</option>
</select>
</div>
</div>
<div class="two fields">
@ -117,7 +91,9 @@
$('.ui.checkbox')
.checkbox()
;
$('.ui.form').form(
var $uiForm = $('.ui.form');
registerSelectFormValidation("selectProtocol", $uiForm, $('#protocol'), 'protocol');
$($uiForm).form(
{
onSuccess: function(event, fields) {
util.post('/task/store', fields, function(code, message) {
@ -153,11 +129,12 @@
prompt : '请输入任务命令'
}
]
}, hosts: {
identifier : 'hosts',
},
hosts: {
identifier : 'host_id',
rules: [
{
type : 'checked',
type : 'selectProtocol',
prompt : '请选择主机'
}
]

Loading…
Cancel
Save