├── .gitignore ├── README.md ├── src └── main │ ├── resources │ ├── static │ │ ├── img │ │ │ ├── ALdown.png │ │ │ ├── KWHup.png │ │ │ ├── left.png │ │ │ ├── logo.png │ │ │ ├── DFHuser.png │ │ │ ├── KWHLlogo.png │ │ │ ├── KWHdown.png │ │ │ ├── favicon.ico │ │ │ ├── DFpassword.png │ │ │ ├── KWHdownIcon.png │ │ │ ├── KWHicon_01.png │ │ │ ├── KWHicon_02.png │ │ │ ├── KWHicon_03.png │ │ │ ├── KWHmessage.png │ │ │ ├── checkboxImg.png │ │ │ ├── headerLeft.png │ │ │ ├── headerRight.png │ │ │ └── titleLeft.png │ │ ├── js │ │ │ ├── skins │ │ │ │ └── default │ │ │ │ │ ├── icon.png │ │ │ │ │ └── laydate.css │ │ │ ├── bootstrap-table │ │ │ │ ├── locale │ │ │ │ │ └── bootstrap-table-zh-CN.min.js │ │ │ │ ├── bootstrap-table-mobile.min.js │ │ │ │ └── bootstrap-table.min.js │ │ │ ├── need │ │ │ │ └── laydate.css │ │ │ ├── jobLog.js │ │ │ ├── index.js │ │ │ ├── laydate.js │ │ │ └── bootbox.js │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── css │ │ │ ├── reset.css │ │ │ ├── bootstrap-table.min.css │ │ │ └── common.css │ │ ├── jobLog.html │ │ └── index.html │ ├── bootstrap.yml │ └── logback.xml │ └── java │ └── cn │ └── demo │ └── scheduler │ ├── ScheduleApplication.java │ ├── task │ ├── DemoSimpleJob.java │ └── DemoDataflowJob.java │ ├── entity │ ├── ElasticJobConfigLogBean.java │ └── ElasticJobConfigBean.java │ ├── util │ └── BeanTools.java │ ├── config │ ├── JobEventConfig.java │ └── RegistryCenterConfig.java │ ├── service │ └── ElasticJobService.java │ ├── controller │ └── JobController.java │ ├── runner │ └── ElasticJobRunner.java │ └── dao │ └── ElasticJobConfigDao.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | /.project 3 | /target/ 4 | /.classpath 5 | /.factorypath 6 | /.springBeans 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # springboot-elasticjob 2 | springboot2.0+Elastic Job实现微服务调用 3 | 4 | 见博客:https://blog.csdn.net/qq_20280007/article/details/82690157 5 | -------------------------------------------------------------------------------- /src/main/resources/static/img/ALdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/ALdown.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHup.png -------------------------------------------------------------------------------- /src/main/resources/static/img/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/left.png -------------------------------------------------------------------------------- /src/main/resources/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/logo.png -------------------------------------------------------------------------------- /src/main/resources/static/img/DFHuser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/DFHuser.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHLlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHLlogo.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHdown.png -------------------------------------------------------------------------------- /src/main/resources/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/static/img/DFpassword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/DFpassword.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHdownIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHdownIcon.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHicon_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHicon_01.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHicon_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHicon_02.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHicon_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHicon_03.png -------------------------------------------------------------------------------- /src/main/resources/static/img/KWHmessage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/KWHmessage.png -------------------------------------------------------------------------------- /src/main/resources/static/img/checkboxImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/checkboxImg.png -------------------------------------------------------------------------------- /src/main/resources/static/img/headerLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/headerLeft.png -------------------------------------------------------------------------------- /src/main/resources/static/img/headerRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/headerRight.png -------------------------------------------------------------------------------- /src/main/resources/static/img/titleLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/img/titleLeft.png -------------------------------------------------------------------------------- /src/main/resources/static/js/skins/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/js/skins/default/icon.png -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/main/resources/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aLiang-xyl/springboot-elasticjob/HEAD/src/main/resources/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/ScheduleApplication.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.cloud.client.SpringCloudApplication; 5 | import org.springframework.cloud.openfeign.EnableFeignClients; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | @SpringCloudApplication 9 | @ComponentScan(value = { "cn.demo.scheduler"}) 10 | @EnableFeignClients(basePackages = { "cn.demo.api" }) 11 | public class ScheduleApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ScheduleApplication.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/static/js/bootstrap-table/locale/bootstrap-table-zh-CN.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * bootstrap-table - v1.9.0 - 2015-09-30 3 | * https://github.com/wenzhixin/bootstrap-table 4 | * Copyright (c) 2015 zhixin wen 5 | * Licensed MIT License 6 | */ 7 | !function(a){"use strict";a.fn.bootstrapTable.locales["zh-CN"]={formatLoadingMessage:function(){return"正在努力地加载数据中,请稍候……"},formatRecordsPerPage:function(a){return"每页显示 "+a+" 条记录"},formatShowingRows:function(a,b,c){return"显示第 "+a+" 到第 "+b+" 条记录,总共 "+c+" 条记录"},formatSearch:function(){return"搜索"},formatNoMatches:function(){return"没有找到匹配的记录"},formatPaginationSwitch:function(){return"隐藏/显示分页"},formatRefresh:function(){return"刷新"},formatToggle:function(){return"切换"},formatColumns:function(){return"列"}},a.extend(a.fn.bootstrapTable.defaults,a.fn.bootstrapTable.locales["zh-CN"])}(jQuery); 8 | -------------------------------------------------------------------------------- /src/main/resources/static/css/reset.css: -------------------------------------------------------------------------------- 1 | /*初始化*/ 2 | html{font-family:"微软雅黑",Arial,sans-serif} 3 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,form,fieldset,input,button,textarea,p,th,td{padding:0;margin:0;font-family:"微软雅黑",Microsoft YaHei,sans-serif,Arial} 4 | fieldset,img{border:0} 5 | a{text-decoration:none;color:#000;outline:none} 6 | li{list-style:none} 7 | 8 | h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal} 9 | input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit} 10 | input,button,textarea,select{*font-size:100%} 11 | a{-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;-ms-transition:all 0.1s linear;-o-transition:all 0.1s linear} 12 | .clear:after{ 13 | display:block; 14 | content:"clear"; 15 | height:0; 16 | clear:both; 17 | overflow:hidden; 18 | visibility:hidden; 19 | } 20 | .clear{zoom:1;} 21 | .fl{ 22 | float: left; 23 | } 24 | .fr{ 25 | float: right; 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/task/DemoSimpleJob.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.task; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | 7 | import com.dangdang.ddframe.job.api.ShardingContext; 8 | import com.dangdang.ddframe.job.api.simple.SimpleJob; 9 | 10 | import cn.demo.api.TestBean; 11 | import cn.demo.api.TestService; 12 | 13 | public class DemoSimpleJob implements SimpleJob { 14 | 15 | private Logger log = LoggerFactory.getLogger(DemoSimpleJob.class); 16 | 17 | @Autowired 18 | private TestService testService; 19 | 20 | @Override 21 | public void execute(ShardingContext shardingContext) { 22 | String jobParameter = shardingContext.getJobParameter(); 23 | log.info("test--------------------{}", jobParameter); 24 | TestBean testBean = new TestBean(); 25 | testBean.setId("123"); 26 | testBean.setName("Hello World!"); 27 | TestBean test = testService.test(testBean); 28 | log.info(test.toString()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/entity/ElasticJobConfigLogBean.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.entity; 2 | 3 | public class ElasticJobConfigLogBean extends ElasticJobConfigBean { 4 | private String createTime; 5 | 6 | public String getCreateTime() { 7 | return createTime; 8 | } 9 | 10 | public void setCreateTime(String createTime) { 11 | this.createTime = createTime; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return "ElasticJobConfigLogBean [createTime=" + createTime + ", getCreateTime()=" + getCreateTime() + ", getId()=" 17 | + getId() + ", getJobName()=" + getJobName() + ", getCron()=" + getCron() + ", getShardingTotalCount()=" 18 | + getShardingTotalCount() + ", getShardingItemParameters()=" + getShardingItemParameters() 19 | + ", getJobParameter()=" + getJobParameter() + ", getFailover()=" + getFailover() + ", getMisfire()=" 20 | + getMisfire() + ", getDescription()=" + getDescription() + ", getJobClass()=" + getJobClass() 21 | + ", getStreamingProcess()=" + getStreamingProcess() + ", getJobConfig()=" + getJobConfig() 22 | + ", toString()=" + super.toString() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + "]"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/task/DemoDataflowJob.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.task; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.UUID; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import com.dangdang.ddframe.job.api.ShardingContext; 11 | import com.dangdang.ddframe.job.api.dataflow.DataflowJob; 12 | 13 | public class DemoDataflowJob implements DataflowJob { 14 | 15 | private Logger log = LoggerFactory.getLogger(DemoDataflowJob.class); 16 | 17 | /* 18 | * 流式处理数据只有fetchData方法的返回值为null或集合长度为空时,作业才停止抓取,否则作业将一直运行下去; 19 | * 非流式处理数据则只会在每次作业执行过程中执行一次fetchData方法和processData方法,随即完成本次作业。 20 | * 如果采用流式作业处理方式,建议processData处理数据后更新其状态, 避免fetchData再次抓取到,从而使得作业永不停止。 21 | * 流式数据处理参照TbSchedule设计,适用于不间歇的数据处理。 22 | */ 23 | @Override 24 | public List fetchData(final ShardingContext shardingContext) { 25 | List list = new ArrayList(); 26 | list.add(UUID.randomUUID().toString()); 27 | list.add(UUID.randomUUID().toString()); 28 | return list; 29 | } 30 | 31 | @Override 32 | public void processData(final ShardingContext shardingContext, final List data) { 33 | log.info("收到数据{}", data); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/util/BeanTools.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.util; 2 | 3 | import org.springframework.context.ApplicationContext; 4 | import org.springframework.context.ApplicationContextAware; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * 从spring容器中获取bean 9 | * 10 | * @author xing 11 | * 12 | */ 13 | @Configuration 14 | public class BeanTools implements ApplicationContextAware { 15 | 16 | private static ApplicationContext applicationContext; 17 | 18 | @Override 19 | public void setApplicationContext(ApplicationContext context) { 20 | applicationContext = context; 21 | } 22 | 23 | /** 24 | * 根据class类型获取bean 25 | * 26 | * @param classname 27 | * @return 28 | */ 29 | public static T getBean(Class classname) { 30 | try { 31 | T bean = applicationContext.getBean(classname); 32 | return bean; 33 | } catch (Exception e) { 34 | return null; 35 | } 36 | } 37 | 38 | /** 39 | * 根据class类型和名称获取bean 40 | * 41 | * @param classname 42 | * @param name 43 | * @return 44 | */ 45 | public static T getBean(Class classname, String name) { 46 | try { 47 | T bean = applicationContext.getBean(name, classname); 48 | return bean; 49 | } catch (Exception e) { 50 | return null; 51 | } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/config/JobEventConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2015 dangdang.com. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | 18 | package cn.demo.scheduler.config; 19 | 20 | import com.dangdang.ddframe.job.event.JobEventConfiguration; 21 | import com.dangdang.ddframe.job.event.rdb.JobEventRdbConfiguration; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | 25 | import javax.annotation.Resource; 26 | import javax.sql.DataSource; 27 | 28 | /** 29 | * 为job配置数据源 30 | * 31 | * @author xing 32 | * 33 | */ 34 | @Configuration 35 | public class JobEventConfig { 36 | 37 | @Resource 38 | private DataSource dataSource; 39 | 40 | @Bean 41 | public JobEventConfiguration jobEventConfiguration() { 42 | return new JobEventRdbConfiguration(dataSource); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | spring: 4 | profiles: 5 | active: dev 6 | application: 7 | name: springboot-elasticjob 8 | ##配置中心 9 | cloud: 10 | config: 11 | username: username 12 | password: password 13 | profile: ${spring.profiles.active} 14 | discovery: 15 | service-id: config-server 16 | enabled: true 17 | ##数据源 18 | datasource: 19 | type: com.alibaba.druid.pool.DruidDataSource 20 | driver-class-name: com.mysql.jdbc.Driver 21 | url: jdbc:mysql://localhost:3306/elastic_job?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true 22 | username: root 23 | password: 123456 24 | druid: 25 | initial-size: 3 26 | max-active: 10 27 | min-idle: 5 28 | max-wait: 600000 29 | validation-query: SELECT 1 30 | test-on-borrow: true 31 | test-on-return: false 32 | test-while-idle: true 33 | time-between-eviction-runs-millis: 600000 34 | min-evictable-idle-time-millis: 600000 35 | filters: stat,wall 36 | 37 | info: 38 | app_name: ${spring.application.name} 39 | ##注册中心 40 | eureka: 41 | client: 42 | service-url: 43 | defaultZone: http://username:password@127.0.0.1:9011/eureka/ 44 | ##zookeeper配置 45 | demo: 46 | elasticjob: 47 | zk-config: 48 | serverLists: localhost:2181 49 | namespace: elastic-job-lite-demo 50 | baseSleepTimeMilliseconds: 1000 51 | maxSleepTimeMilliseconds: 3000 52 | maxRetries: 4 53 | digest: 'password' 54 | #DEMO-PROVIDER.ribbon.listOfServers: localhost:8088 55 | #ribbon.eureka.enabled: false 56 | 57 | -------------------------------------------------------------------------------- /src/main/resources/static/js/bootstrap-table/bootstrap-table-mobile.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * bootstrap-table - v1.9.0 - 2015-09-30 3 | * https://github.com/wenzhixin/bootstrap-table 4 | * Copyright (c) 2015 zhixin wen 5 | * Licensed MIT License 6 | */ 7 | !function(a){"use strict";var b=function(b,c){b.options.columnsHidden.length>0&&a.each(b.columns,function(d,e){-1!==b.options.columnsHidden.indexOf(e.field)&&e.visible!==c&&b.toggleColumn(a.fn.bootstrapTable.utils.getFieldIndex(b.columns,e.field),c,!0)})},c=function(a){(a.options.height||a.options.showFooter)&&setTimeout(a.resetView,1)},d=function(a,b,d){a.options.minHeight?b<=a.options.minWidth&&d<=a.options.minHeight?e(a):b>a.options.minWidth&&d>a.options.minHeight&&f(a):b<=a.options.minWidth?e(a):b>a.options.minWidth&&f(a),c(a)},e=function(a){g(a,!1),b(a,!1)},f=function(a){g(a,!0),b(a,!0)},g=function(a,b){a.options.cardView=b,a.toggleView()},h=function(a,b){var c;return function(){var d=this,e=arguments,f=function(){c=null,a.apply(d,e)};clearTimeout(c),c=setTimeout(f,b)}};a.extend(a.fn.bootstrapTable.defaults,{mobileResponsive:!1,minWidth:562,minHeight:void 0,heightThreshold:100,checkOnInit:!0,columnsHidden:[]});var i=a.fn.bootstrapTable.Constructor,j=i.prototype.init;i.prototype.init=function(){if(j.apply(this,Array.prototype.slice.apply(arguments)),this.options.mobileResponsive&&this.options.minWidth){var b=this,c={width:a(window).width(),height:a(window).height()};if(a(window).on("resize orientationchange",h(function(){var e=a(this).height(),f=a(this).width();(Math.abs(c.height-e)>b.options.heightThreshold||c.width!=f)&&(d(b,f,e),c={width:f,height:e})},200)),this.options.checkOnInit){var e=a(window).height(),f=a(window).width();d(this,f,e),c={width:f,height:e}}}}}(jQuery); 8 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/service/ElasticJobService.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.service; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import cn.demo.scheduler.dao.ElasticJobConfigDao; 9 | import cn.demo.scheduler.entity.ElasticJobConfigBean; 10 | import cn.demo.scheduler.entity.ElasticJobConfigLogBean; 11 | import cn.demo.scheduler.runner.ElasticJobRunner; 12 | 13 | /** 14 | * job service 15 | * 16 | * @author xing 17 | * 18 | */ 19 | @Service 20 | public class ElasticJobService { 21 | 22 | @Autowired 23 | private ElasticJobConfigDao elasticJobConfigDao; 24 | 25 | public List getElasticJobConfigList(String jobName, Integer startIndex, Integer size) { 26 | return elasticJobConfigDao.getElasticJobConfigList(jobName, startIndex, size); 27 | } 28 | 29 | public long getElasticJobConfigCount(String jobName) { 30 | return elasticJobConfigDao.getElasticJobConfigCount(jobName); 31 | } 32 | 33 | public int insert(ElasticJobConfigBean config) { 34 | return elasticJobConfigDao.insert(config); 35 | } 36 | 37 | /** 38 | * 更新 39 | * 40 | * @param config 41 | * @return 42 | */ 43 | public int update(ElasticJobConfigBean config) { 44 | int status = elasticJobConfigDao.update(config); 45 | if (status == 1) { 46 | elasticJobConfigDao.backups(config); 47 | ElasticJobRunner.setJobConfig(config.getJobName(), config.getJobConfig()); 48 | } 49 | return status; 50 | } 51 | 52 | public ElasticJobConfigBean getElasticJobConfigById(String id) { 53 | return elasticJobConfigDao.getElasticJobConfigById(id); 54 | } 55 | 56 | public int delete(String id) { 57 | ElasticJobConfigBean elasticJobConfigById = getElasticJobConfigById(id); 58 | elasticJobConfigDao.backups(elasticJobConfigById); 59 | return elasticJobConfigDao.delete(id); 60 | } 61 | 62 | public List getElasticJobConfigLogList(String jobName, Integer startIndex, Integer size) { 63 | return elasticJobConfigDao.getElasticJobConfigLogList(jobName,startIndex,size); 64 | } 65 | 66 | public long getElasticJobConfigLogCount(String jobName) { 67 | return elasticJobConfigDao.getElasticJobConfigLogCount(jobName); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/controller/JobController.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.controller; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.ResponseBody; 15 | 16 | import cn.demo.scheduler.entity.ElasticJobConfigBean; 17 | import cn.demo.scheduler.entity.ElasticJobConfigLogBean; 18 | import cn.demo.scheduler.service.ElasticJobService; 19 | 20 | @Controller 21 | @RequestMapping("job") 22 | public class JobController { 23 | 24 | private Logger log = LoggerFactory.getLogger(JobController.class); 25 | 26 | @Autowired 27 | private ElasticJobService elasticJobService; 28 | 29 | @GetMapping("getElasticJobConfigList") 30 | @ResponseBody 31 | public Map getElasticJobConfigList(String jobName, Integer startIndex, Integer size) { 32 | List list = elasticJobService.getElasticJobConfigList(jobName, startIndex, size); 33 | long count = elasticJobService.getElasticJobConfigCount(jobName); 34 | Map result = new HashMap<>(); 35 | result.put("rows", list); 36 | result.put("total", count); 37 | return result; 38 | } 39 | 40 | @PostMapping("addOrUpdateElasticJobConfig") 41 | @ResponseBody 42 | public int addOrUpdateElasticJobConfig(ElasticJobConfigBean elasticJobConfigBean) { 43 | log.info("更新或新增 {} ", elasticJobConfigBean.toString()); 44 | if (elasticJobConfigBean.getId() == null) { 45 | return elasticJobService.insert(elasticJobConfigBean); 46 | } else { 47 | return elasticJobService.update(elasticJobConfigBean); 48 | } 49 | } 50 | 51 | @GetMapping("getElasticJobConfig") 52 | @ResponseBody 53 | public ElasticJobConfigBean getElasticJobConfig(String id) { 54 | return elasticJobService.getElasticJobConfigById(id); 55 | } 56 | 57 | @PostMapping("delete") 58 | @ResponseBody 59 | public int delete(String id) { 60 | return elasticJobService.delete(id); 61 | } 62 | 63 | @GetMapping("getElasticJobConfigLogList") 64 | @ResponseBody 65 | public Map getElasticJobConfigLogList(String jobName, Integer startIndex, Integer size) { 66 | List re = elasticJobService.getElasticJobConfigLogList(jobName, startIndex, size); 67 | long count = elasticJobService.getElasticJobConfigLogCount(jobName); 68 | Map result = new HashMap<>(); 69 | result.put("rows", re); 70 | result.put("total", count); 71 | return result; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 10 | 11 | 12 | 13 | 14 | ${LOG_HOME}-error.log 15 | 16 | 17 | ${LOG_HOME}-error-%d{yyyy-MM-dd}.log 18 | 19 | 3 20 | 21 | 22 | 23 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 24 | UTF-8 25 | 26 | 27 | ERROR 28 | ACCEPT 29 | DENY 30 | 31 | 32 | 33 | ${LOG_HOME}-info.log 34 | 35 | 36 | ${LOG_HOME}-info-%d{yyyy-MM-dd}-%i.log 37 | 38 | 5GB 39 | 40 | 3 41 | 42 | 20GB 43 | 44 | 45 | 46 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 47 | UTF-8 48 | 49 | 50 | ERROR 51 | DENY 52 | ACCEPT 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/entity/ElasticJobConfigBean.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.entity; 2 | 3 | /** 4 | * 定时任务配置 5 | * 6 | * @author xing 7 | * 8 | */ 9 | public class ElasticJobConfigBean { 10 | private Long id; 11 | private String jobName; 12 | private String cron; 13 | private Integer shardingTotalCount; 14 | private String shardingItemParameters; 15 | private String jobParameter; 16 | private String failover; 17 | private String misfire; 18 | private String description; 19 | private String jobClass; 20 | private String streamingProcess; 21 | private String jobConfig; 22 | 23 | public Long getId() { 24 | return id; 25 | } 26 | 27 | public void setId(Long id) { 28 | this.id = id; 29 | } 30 | 31 | public String getJobName() { 32 | return jobName; 33 | } 34 | 35 | public void setJobName(String jobName) { 36 | this.jobName = jobName; 37 | } 38 | 39 | public String getCron() { 40 | return cron; 41 | } 42 | 43 | public void setCron(String cron) { 44 | this.cron = cron; 45 | } 46 | 47 | public Integer getShardingTotalCount() { 48 | return shardingTotalCount; 49 | } 50 | 51 | public void setShardingTotalCount(Integer shardingTotalCount) { 52 | this.shardingTotalCount = shardingTotalCount; 53 | } 54 | 55 | public String getShardingItemParameters() { 56 | return shardingItemParameters; 57 | } 58 | 59 | public void setShardingItemParameters(String shardingItemParameters) { 60 | this.shardingItemParameters = shardingItemParameters; 61 | } 62 | 63 | public String getJobParameter() { 64 | return jobParameter; 65 | } 66 | 67 | public void setJobParameter(String jobParameter) { 68 | this.jobParameter = jobParameter; 69 | } 70 | 71 | public String getFailover() { 72 | return failover; 73 | } 74 | 75 | public void setFailover(String failover) { 76 | this.failover = failover; 77 | } 78 | 79 | public String getMisfire() { 80 | return misfire; 81 | } 82 | 83 | public void setMisfire(String misfire) { 84 | this.misfire = misfire; 85 | } 86 | 87 | public String getDescription() { 88 | return description; 89 | } 90 | 91 | public void setDescription(String description) { 92 | this.description = description; 93 | } 94 | 95 | public String getJobClass() { 96 | return jobClass; 97 | } 98 | 99 | public void setJobClass(String jobClass) { 100 | this.jobClass = jobClass; 101 | } 102 | 103 | public String getStreamingProcess() { 104 | return streamingProcess; 105 | } 106 | 107 | public void setStreamingProcess(String streamingProcess) { 108 | this.streamingProcess = streamingProcess; 109 | } 110 | 111 | public String getJobConfig() { 112 | return jobConfig; 113 | } 114 | 115 | public void setJobConfig(String jobConfig) { 116 | this.jobConfig = jobConfig; 117 | } 118 | 119 | @Override 120 | public String toString() { 121 | return "ElasticJobConfigBean [id=" + id + ", jobName=" + jobName + ", cron=" + cron + ", shardingTotalCount=" 122 | + shardingTotalCount + ", shardingItemParameters=" + shardingItemParameters + ", jobParameter=" 123 | + jobParameter + ", failover=" + failover + ", misfire=" + misfire + ", description=" + description 124 | + ", jobClass=" + jobClass + ", streamingProcess=" + streamingProcess + ", jobConfig=" + jobConfig 125 | + "]"; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/main/resources/static/js/skins/default/laydate.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .laydate-icon{border:1px solid #ccc; background-image:url(icon.png)} 4 | 5 | .laydate_body .laydate_bottom #laydate_hms, 6 | .laydate_body .laydate_time{border:1px solid #ccc;} 7 | 8 | .laydate_body .laydate_box, 9 | .laydate_body .laydate_ym .laydate_yms, 10 | .laydate_body .laydate_time{box-shadow: 2px 2px 5px rgba(0,0,0,.1);} 11 | 12 | .laydate_body .laydate_box{border-top:none; border-bottom:none; background-color:#fff; color:#41c03b;} 13 | .laydate_body .laydate_box input{background:none!important; color:#fff;} 14 | .laydate_body .laydate_box .laydate_void{color:#00E8D7!important;} 15 | .laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{color:#41c03b;} 16 | .laydate_body .laydate_box a:hover{color:#666;} 17 | .laydate_body .laydate_click{background-color:#ff8330!important; color:#fff!important;} 18 | .laydate_body .laydate_top{border-top:1px solid #41c03b; background-color:#41c03b} 19 | .laydate_body .laydate_ym{border:1px solid #41c03b; background-color:#41c03b;} 20 | .laydate_body .laydate_ym .laydate_yms{border:1px solid #41c03b; background-color:#41c03b; color:#fff;} 21 | .laydate_body .laydate_y .laydate_yms a{border-bottom:1px solid #41c03b;} 22 | .laydate_body .laydate_y .laydate_yms .laydate_chdown{border-top:1px solid #41c03b; border-bottom:none;} 23 | .laydate_body .laydate_choose{border-left:1px solid #41c03b;} 24 | .laydate_body .laydate_chprev{border-left:none; border-right:1px solid #41c03b;} 25 | .laydate_body .laydate_choose:hover, 26 | .laydate_body .laydate_y .laydate_yms a:hover{background-color:#00C1B3;} 27 | .laydate_body .laydate_chtop cite{border-bottom-color:#fff;} 28 | .laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{border-top-color:#fff;} 29 | .laydate_body .laydate_chprev cite{border-right-style:solid; border-right-color:#fff;} 30 | .laydate_body .laydate_chnext cite{border-left-style:solid; border-left-color:#fff;} 31 | .laydate_body .laydate_table{width: 240px!important; margin: 0!important; border:1px solid #ccc; border-top:none; border-bottom:none;} 32 | .laydate_body .laydate_table td{border:none; height:21px!important; line-height:21px!important; background-color:#fff; color:#41c03b;} 33 | .laydate_body .laydate_table .laydate_nothis{color:#999;} 34 | .laydate_body .laydate_table thead{border-bottom:1px solid #ccc; height:21px!important; line-height:21px!important;} 35 | .laydate_body .laydate_table thead th{} 36 | .laydate_body .laydate_bottom{border:1px solid #ccc; border-top:none;} 37 | .laydate_body .laydate_bottom #laydate_hms{background-color:#fff;} 38 | .laydate_body .laydate_time{background-color:#fff;} 39 | .laydate_body .laydate_time1{width: 226px!important; height: 152px!important;} 40 | .laydate_body .laydate_bottom .laydate_sj{width:31px!important; border-right:1px solid #ccc; background-color:#fff;} 41 | .laydate_body .laydate_bottom input{background-color:#fff; color:#41c03b;} 42 | .laydate_body .laydate_bottom .laydte_hsmtex{border-bottom:1px solid #ccc;} 43 | .laydate_body .laydate_bottom .laydate_btn{border-right:1px solid #ccc;} 44 | .laydate_body .laydate_bottom .laydate_v{color:#999} 45 | .laydate_body .laydate_bottom .laydate_btn a{border: 1px solid #ccc; border-right:none; background-color:#fff;} 46 | .laydate_body .laydate_bottom .laydate_btn a:hover{background-color:#F6F6F6; color:#41c03b;} 47 | 48 | .laydate_body .laydate_m .laydate_yms span:hover, 49 | .laydate_body .laydate_time .laydate_hmsno span:hover, 50 | .laydate_body .laydate_y .laydate_yms ul li:hover, 51 | .laydate_body .laydate_table td:hover{background-color:#00C1B3; color:#fff;} 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/config/RegistryCenterConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1999-2015 dangdang.com. 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *

16 | */ 17 | 18 | package cn.demo.scheduler.config; 19 | 20 | import org.springframework.boot.context.properties.ConfigurationProperties; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | 24 | import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperConfiguration; 25 | import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter; 26 | 27 | @Configuration 28 | @ConfigurationProperties(prefix = "demo.elasticjob.zk-config") 29 | public class RegistryCenterConfig { 30 | 31 | /** 32 | * 连接Zookeeper服务器的列表. 包括IP地址和端口号. 多个地址用逗号分隔. 如: host1:2181,host2:2181 33 | */ 34 | private String serverLists; 35 | 36 | /** 37 | * 命名空间. 38 | */ 39 | private String namespace; 40 | 41 | /** 42 | * 等待重试的间隔时间的初始值. 单位毫秒. 43 | */ 44 | private int baseSleepTimeMilliseconds; 45 | 46 | /** 47 | * 等待重试的间隔时间的最大值. 单位毫秒. 48 | */ 49 | private int maxSleepTimeMilliseconds; 50 | 51 | /** 52 | * 最大重试次数. 53 | */ 54 | private int maxRetries; 55 | 56 | /** 57 | * 登录权限 58 | */ 59 | private String digest; 60 | 61 | @Bean(initMethod = "init") 62 | public ZookeeperRegistryCenter zookeeperRegistryCenter() { 63 | ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(serverLists, namespace); 64 | zookeeperConfiguration.setMaxRetries(maxRetries); 65 | zookeeperConfiguration.setBaseSleepTimeMilliseconds(baseSleepTimeMilliseconds); 66 | zookeeperConfiguration.setMaxSleepTimeMilliseconds(maxSleepTimeMilliseconds); 67 | zookeeperConfiguration.setDigest(digest); 68 | return new ZookeeperRegistryCenter(zookeeperConfiguration); 69 | } 70 | 71 | public String getServerLists() { 72 | return serverLists; 73 | } 74 | 75 | public void setServerLists(String serverLists) { 76 | this.serverLists = serverLists; 77 | } 78 | 79 | public String getNamespace() { 80 | return namespace; 81 | } 82 | 83 | public void setNamespace(String namespace) { 84 | this.namespace = namespace; 85 | } 86 | 87 | public int getBaseSleepTimeMilliseconds() { 88 | return baseSleepTimeMilliseconds; 89 | } 90 | 91 | public void setBaseSleepTimeMilliseconds(int baseSleepTimeMilliseconds) { 92 | this.baseSleepTimeMilliseconds = baseSleepTimeMilliseconds; 93 | } 94 | 95 | public int getMaxSleepTimeMilliseconds() { 96 | return maxSleepTimeMilliseconds; 97 | } 98 | 99 | public void setMaxSleepTimeMilliseconds(int maxSleepTimeMilliseconds) { 100 | this.maxSleepTimeMilliseconds = maxSleepTimeMilliseconds; 101 | } 102 | 103 | public int getMaxRetries() { 104 | return maxRetries; 105 | } 106 | 107 | public void setMaxRetries(int maxRetries) { 108 | this.maxRetries = maxRetries; 109 | } 110 | 111 | public String getDigest() { 112 | return digest; 113 | } 114 | 115 | public void setDigest(String digest) { 116 | this.digest = digest; 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/resources/static/css/bootstrap-table.min.css: -------------------------------------------------------------------------------- 1 | .bootstrap-table .table{margin-bottom:0!important;border-bottom:1px solid #e4eaec;border-collapse:collapse!important}.bootstrap-table .table,.bootstrap-table .table>tbody>tr>td,.bootstrap-table .table>tbody>tr>th,.bootstrap-table .table>tfoot>tr>td,.bootstrap-table .table>tfoot>tr>th,.bootstrap-table .table>thead>tr>td{padding:8px!important}.bootstrap-table .table.table-no-bordered>tbody>tr>td,.bootstrap-table .table.table-no-bordered>thead>tr>th{border-right:2px solid transparent}.fixed-table-container{position:relative;clear:both;border:1px solid #e4eaec}.fixed-table-container.table-no-bordered{border:1px solid transparent}.fixed-table-footer,.fixed-table-header{height:37px;overflow:hidden}.fixed-table-header{border-bottom:1px solid #e4eaec}.fixed-table-footer{border-top:1px solid #e4eaec}.fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.fixed-table-container table{width:100%}.fixed-table-container thead th{height:0;padding:0;margin:0;border-left:1px solid #e4eaec}.fixed-table-container thead th:first-child{border-left:none}.fixed-table-container tbody td .th-inner,.fixed-table-container thead th .th-inner{padding:8px;line-height:20px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fixed-table-container thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px}.fixed-table-container th.detail{width:30px}.fixed-table-container tbody td{border-left:1px solid #e4eaec}.fixed-table-container tbody tr:first-child td{border-top:none}.fixed-table-container tbody td:first-child{border-left:none}.fixed-table-container .table .icon,.fixed-table-container table .icon{top:auto;margin:0 5px}.fixed-table-container tbody .selected td{background-color:#f3f7f9}.fixed-table-container .bs-checkbox{text-align:center}.fixed-table-container .bs-checkbox .th-inner{padding:8px 0}.fixed-table-container input[type=radio],.fixed-table-container input[type=checkbox]{margin:0 auto!important}.fixed-table-container .no-records-found{text-align:center}.fixed-table-pagination .pagination-detail,.fixed-table-pagination div.pagination{margin-top:10px;margin-bottom:10px}.fixed-table-pagination div.pagination .pagination{margin:0}.fixed-table-pagination .pagination a{padding:6px 12px;line-height:1.428571429}.fixed-table-pagination .pagination-info{line-height:34px;margin-right:5px}.fixed-table-pagination .btn-group{position:relative;display:inline-block;vertical-align:middle}.fixed-table-pagination .dropup .dropdown-menu{margin-bottom:0}.fixed-table-pagination .page-list{display:inline-block}.fixed-table-toolbar .columns-left{margin-right:5px}.fixed-table-toolbar .columns-right{margin-left:5px}.fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:300;line-height:1.428571429}.fixed-table-toolbar .bars,.fixed-table-toolbar .columns,.fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px;line-height:34px}.fixed-table-pagination li.disabled a{pointer-events:none;cursor:default}.fixed-table-loading{display:none;position:absolute;top:42px;right:0;bottom:0;left:0;z-index:6;background-color:#fff;text-align:center}.fixed-table-body .card-view .title{font-weight:400;display:inline-block;min-width:30%;text-align:left!important}.fixed-table-body thead th .th-inner{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.table td,.table th{vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fixed-table-toolbar .dropdown-menu{text-align:left;max-height:300px;overflow:auto}.fixed-table-toolbar .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.fixed-table-toolbar .btn-group>.btn-group>.btn{border-radius:0}.fixed-table-toolbar .btn-group>.btn-group:first-child>.btn{border-top-left-radius:3px;border-bottom-left-radius:3px}.fixed-table-toolbar .btn-group>.btn-group:last-child>.btn{border-top-right-radius:3px;border-bottom-right-radius:3px}.bootstrap-table .table>thead>tr>th{vertical-align:bottom;border-bottom:1px solid #e4eaec}.bootstrap-table .table thead>tr>th{padding:0;margin:0}.bootstrap-table .fixed-table-footer tbody>tr>td{padding:0!important}.bootstrap-table .fixed-table-footer .table{border-bottom:none;border-radius:0}.pull-right .dropdown-menu{right:0;left:auto}p.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden} 2 | -------------------------------------------------------------------------------- /src/main/resources/static/jobLog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 36 | 37 | 38 |
39 |
40 | 41 |
42 |
    43 |
  • 44 | 45 |
    46 | 定时任务 47 |
    48 |
  • 49 |
50 |
51 |

定时任务

52 | 56 |
57 |
58 | 59 | 60 | 61 | 62 |
63 | 64 |
65 | 67 |
    68 |
  • 定时任务
  • 69 |
70 |

71 | 72 |

73 |
74 |
75 | 定时任务配置记录 76 |

77 | 78 | 79 |

80 |
81 | 82 | 83 |
84 | 85 |
86 |
87 | 88 |
89 |
90 | 91 | 92 | 115 | 116 | 117 | 118 | 121 | 124 | 128 | 132 | 136 | 139 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/main/resources/static/js/need/laydate.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @Name: laydate 核心样式 4 | @Author:贤心 5 | @Site:http://sentsin.com/layui/laydate 6 | 7 | **/ 8 | 9 | html{_background-image:url(about:blank); _background-attachment:fixed;} 10 | .laydate_body .laydate_box, .laydate_body .laydate_box *{margin:0; padding:0;} 11 | .laydate-icon, 12 | .laydate-icon-default, 13 | .laydate-icon-danlan, 14 | .laydate-icon-dahong, 15 | .laydate-icon-molv{height:22px; line-height:22px; padding-right:20px; border:1px solid #C6C6C6; background-repeat:no-repeat; background-position:right center; background-color:#fff; outline:0;} 16 | .laydate-icon-default{ background-image:url(../skins/default/icon.png)} 17 | .laydate-icon-danlan{border:1px solid #B1D2EC; background-image:url(../skins/danlan/icon.png)} 18 | .laydate-icon-dahong{background-image:url(../skins/dahong/icon.png)} 19 | .laydate-icon-molv{background-image:url(../skins/molv/icon.png)} 20 | .laydate_body .laydate_box{width:240px; font:12px '\5B8B\4F53'; z-index:99999999; *margin:-2px 0 0 -2px; *overflow:hidden; _margin:0; _position:absolute!important; background-color:#fff;} 21 | .laydate_body .laydate_box li{list-style:none;} 22 | .laydate_body .laydate_box .laydate_void{cursor:text!important;} 23 | .laydate_body .laydate_box a, .laydate_body .laydate_box a:hover{text-decoration:none; blr:expression(this.onFocus=this.blur()); cursor:pointer;} 24 | .laydate_body .laydate_box a:hover{text-decoration:none;} 25 | .laydate_body .laydate_box cite, .laydate_body .laydate_box label{position:absolute; width:0; height:0; border-width:5px; border-style:dashed; border-color:transparent; overflow:hidden; cursor:pointer;} 26 | .laydate_body .laydate_box .laydate_yms, .laydate_body .laydate_box .laydate_time{display:none;} 27 | .laydate_body .laydate_box .laydate_show{display:block;} 28 | .laydate_body .laydate_box input{outline:0; font-size:14px; background-color:#fff;} 29 | .laydate_body .laydate_top{position:relative; height:26px;*width:100%; z-index:99;} 30 | .laydate_body .laydate_ym{position:relative; float:left; height:24px; cursor:pointer;} 31 | .laydate_body .laydate_ym input{float:left; height:24px; line-height:24px; text-align:center; border:none; cursor:pointer;} 32 | .laydate_body .laydate_ym .laydate_yms{position:absolute; left: -1px; top: 24px; height:181px;} 33 | .laydate_body .laydate_y{width:121px; margin-right:6px;} 34 | .laydate_body .laydate_y input{width:64px; margin-right:15px;} 35 | .laydate_body .laydate_y .laydate_yms{width:121px; text-align:center;} 36 | .laydate_body .laydate_y .laydate_yms a{position:relative; display:block; height:20px;} 37 | .laydate_body .laydate_y .laydate_yms ul{height:139px; padding:0; *overflow:hidden;} 38 | .laydate_body .laydate_y .laydate_yms ul li{float:left; width:59px; height:20px; line-height: 20px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;} 39 | .laydate_body .laydate_m{width:99px;} 40 | .laydate_body .laydate_m .laydate_yms{width:99px; padding:0;} 41 | .laydate_body .laydate_m input{width:42px; margin-right:15px;} 42 | .laydate_body .laydate_m .laydate_yms span{display:block; float:left; width:42px; margin: 5px 0 0 5px; line-height:24px; text-align:center; _display:inline;} 43 | .laydate_body .laydate_choose{display:block; float:left; position:relative; width:20px; height:24px;} 44 | .laydate_body .laydate_choose cite, .laydate_body .laydate_tab cite{left:50%; top:50%;} 45 | .laydate_body .laydate_chtop cite{margin:-7px 0 0 -5px; border-bottom-style:solid;} 46 | .laydate_body .laydate_chdown cite, .laydate_body .laydate_ym label{top:50%; margin:-2px 0 0 -5px; border-top-style:solid;} 47 | .laydate_body .laydate_chprev cite{margin:-5px 0 0 -7px;} 48 | .laydate_body .laydate_chnext cite{margin:-5px 0 0 -2px;} 49 | .laydate_body .laydate_ym label{right:28px;} 50 | .laydate_body .laydate_table{ width:230px; margin:0 5px; border-collapse:collapse; border-spacing:0px; } 51 | .laydate_body .laydate_table td{width:31px; height:19px; line-height:19px; text-align: center; cursor:pointer; font-size: 12px;} 52 | .laydate_body .laydate_table thead{height:22px; line-height:22px;} 53 | .laydate_body .laydate_table thead th{font-weight:400; font-size:12px; text-align:center;} 54 | .laydate_body .laydate_bottom{position:relative; height:30px; line-height:20px; padding:5px; font-size:12px;} 55 | .laydate_body .laydate_bottom #laydate_hms{position: relative; z-index: 1; float:left; } 56 | .laydate_body .laydate_time{ position:absolute; left:5px; bottom: 26px; width:129px; height:125px; *overflow:hidden;} 57 | .laydate_body .laydate_time .laydate_hmsno{ padding:5px 0 0 5px;} 58 | .laydate_body .laydate_time .laydate_hmsno span{display:block; float:left; width:24px; height:19px; line-height:19px; text-align:center; cursor:pointer; *margin-bottom:-5px;} 59 | .laydate_body .laydate_time1{width:228px; height:154px;} 60 | .laydate_body .laydate_time1 .laydate_hmsno{padding: 6px 0 0 8px;} 61 | .laydate_body .laydate_time1 .laydate_hmsno span{width:21px; height:20px; line-height:20px;} 62 | .laydate_body .laydate_msg{left:49px; bottom:67px; width:141px; height:auto; overflow: hidden;} 63 | .laydate_body .laydate_msg p{padding:5px 10px;} 64 | .laydate_body .laydate_bottom li{float:left; height:20px; line-height:20px; border-right:none; font-weight:900;} 65 | .laydate_body .laydate_bottom .laydate_sj{width:33px; text-align:center; font-weight:400;} 66 | .laydate_body .laydate_bottom input{float:left; width:21px; height:20px; line-height:20px; border:none; text-align:center; cursor:pointer; font-size:12px; font-weight:400;} 67 | .laydate_body .laydate_bottom .laydte_hsmtex{height:20px; line-height:20px; text-align:center;} 68 | .laydate_body .laydate_bottom .laydte_hsmtex span{position:absolute; width:20px; top:0; right:0px; cursor:pointer;} 69 | .laydate_body .laydate_bottom .laydte_hsmtex span:hover{font-size:14px;} 70 | .laydate_body .laydate_bottom .laydate_btn{position:absolute; right:5px; top:5px;} 71 | .laydate_body .laydate_bottom .laydate_btn a{float:left; height:20px; padding:0 6px; _padding:0 5px;} 72 | .laydate_body .laydate_bottom .laydate_v{position:absolute; left:10px; top:6px; font-family:Courier; z-index:0;} 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/runner/ElasticJobRunner.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.runner; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Field; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import javax.annotation.Resource; 10 | 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.CommandLineRunner; 15 | import org.springframework.stereotype.Component; 16 | 17 | import com.dangdang.ddframe.job.api.ElasticJob; 18 | import com.dangdang.ddframe.job.api.dataflow.DataflowJob; 19 | import com.dangdang.ddframe.job.api.simple.SimpleJob; 20 | import com.dangdang.ddframe.job.config.JobCoreConfiguration; 21 | import com.dangdang.ddframe.job.config.JobTypeConfiguration; 22 | import com.dangdang.ddframe.job.config.dataflow.DataflowJobConfiguration; 23 | import com.dangdang.ddframe.job.config.simple.SimpleJobConfiguration; 24 | import com.dangdang.ddframe.job.event.JobEventConfiguration; 25 | import com.dangdang.ddframe.job.lite.config.LiteJobConfiguration; 26 | import com.dangdang.ddframe.job.lite.spring.api.SpringJobScheduler; 27 | import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter; 28 | 29 | import cn.demo.scheduler.dao.ElasticJobConfigDao; 30 | import cn.demo.scheduler.entity.ElasticJobConfigBean; 31 | import cn.demo.scheduler.util.BeanTools; 32 | 33 | /** 34 | * 定时任务配置 35 | * 36 | * @author xing 37 | * 38 | */ 39 | @Component 40 | public class ElasticJobRunner implements CommandLineRunner { 41 | 42 | private Logger log = LoggerFactory.getLogger(ElasticJobRunner.class); 43 | 44 | private static final Map JOB_CONFIG_MAP = new HashMap<>(); 45 | 46 | @Resource 47 | private ZookeeperRegistryCenter zookeeperRegistryCenter; 48 | 49 | @Resource 50 | private JobEventConfiguration jobEventConfiguration; 51 | 52 | @Resource 53 | private ElasticJobConfigDao elasticJobConfigDao; 54 | 55 | /** 56 | * 根据jobName获取配置 57 | * 58 | * @param jobName 59 | * @return 60 | */ 61 | public static String getJobConfig(String jobName) { 62 | return JOB_CONFIG_MAP.get(jobName); 63 | } 64 | 65 | /** 66 | * 更改jobConfig 67 | * 68 | * @param jobName 69 | * @param jobConfig 70 | */ 71 | public static void setJobConfig(String jobName, String jobConfig) { 72 | JOB_CONFIG_MAP.put(jobName, jobConfig); 73 | } 74 | 75 | /** 76 | * 注入任务 77 | * 78 | * @param elasticJobConfigBean 79 | */ 80 | @SuppressWarnings("unchecked") 81 | private void registryJob(ElasticJobConfigBean elasticJobConfigBean) { 82 | try { 83 | Class jobClass = (Class) Class 84 | .forName(elasticJobConfigBean.getJobClass()); 85 | ElasticJob elasticJob = getInstance(jobClass); 86 | SpringJobScheduler jobScheduler = jobScheduler(elasticJob, jobClass, elasticJobConfigBean); 87 | jobScheduler.init(); 88 | log.info("初始化定时任务 {} ", elasticJobConfigBean.toString()); 89 | } catch (Exception e) { 90 | log.error("注册Job出错:{} ", elasticJobConfigBean.toString(), e); 91 | } 92 | 93 | } 94 | 95 | /** 96 | * 通过反射对有@Resource和@Autowired的属性赋值 97 | * 98 | * @param jobClass 99 | * @return 100 | * @throws InstantiationException 101 | * @throws IllegalAccessException 102 | */ 103 | private ElasticJob getInstance(Class jobClass) 104 | throws InstantiationException, IllegalAccessException { 105 | Field[] declaredFields = jobClass.getDeclaredFields(); 106 | ElasticJob newInstance = jobClass.newInstance(); 107 | for (Field field : declaredFields) { 108 | Annotation[] annotations = field.getAnnotations(); 109 | if (annotations == null || annotations.length == 0) { 110 | continue; 111 | } 112 | boolean flag = false; 113 | for (Annotation annotation : annotations) { 114 | Class annotationType = annotation.annotationType(); 115 | if (annotationType.equals(Resource.class) || annotationType.equals(Autowired.class)) { 116 | flag = true; 117 | break; 118 | } 119 | } 120 | if (flag) { 121 | field.setAccessible(true); 122 | field.set(newInstance, BeanTools.getBean(field.getType())); 123 | } 124 | } 125 | return newInstance; 126 | } 127 | 128 | /** 129 | * 注册SpringJobScheduler 130 | * 131 | * @param elasticJob 132 | * @param jobClass 133 | * @param elasticJobConfigBean 134 | * @return 135 | */ 136 | private SpringJobScheduler jobScheduler(ElasticJob elasticJob, Class jobClass, 137 | ElasticJobConfigBean elasticJobConfigBean) { 138 | LiteJobConfiguration build = LiteJobConfiguration.newBuilder(jobConfiguration(elasticJob, elasticJobConfigBean)) 139 | .overwrite(true).build(); 140 | SpringJobScheduler springJobScheduler = new SpringJobScheduler(elasticJob, zookeeperRegistryCenter, build, 141 | jobEventConfiguration); 142 | return springJobScheduler; 143 | } 144 | 145 | /** 146 | * job配置 147 | * 148 | * @param elasticJob 149 | * @param elasticJobConfigBean 150 | * @return 151 | */ 152 | private JobTypeConfiguration jobConfiguration(final ElasticJob elasticJob, 153 | ElasticJobConfigBean elasticJobConfigBean) { 154 | JobCoreConfiguration jobCoreConfiguration = JobCoreConfiguration 155 | .newBuilder(elasticJobConfigBean.getJobName(), elasticJobConfigBean.getCron(), 156 | elasticJobConfigBean.getShardingTotalCount()) 157 | .shardingItemParameters(elasticJobConfigBean.getShardingItemParameters()) 158 | .misfire(Boolean.valueOf(elasticJobConfigBean.getMisfire())) 159 | .description(elasticJobConfigBean.getDescription()) 160 | .failover(Boolean.valueOf(elasticJobConfigBean.getFailover())) 161 | .jobParameter(elasticJobConfigBean.getJobParameter()) 162 | .build(); 163 | if (elasticJob instanceof SimpleJob) { 164 | return new SimpleJobConfiguration(jobCoreConfiguration, elasticJob.getClass().getCanonicalName()); 165 | } 166 | if (elasticJob instanceof DataflowJob) { 167 | return new DataflowJobConfiguration(jobCoreConfiguration, elasticJob.getClass().getCanonicalName(), 168 | Boolean.valueOf(elasticJobConfigBean.getStreamingProcess())); 169 | } 170 | throw new RuntimeException("未知类型定时任务:" + elasticJob.getClass().getName()); 171 | } 172 | 173 | @Override 174 | public void run(String... args) throws Exception { 175 | List elasticJobConfigList = elasticJobConfigDao.getElasticJobConfigList(); 176 | if (elasticJobConfigList == null || elasticJobConfigList.size() == 0) { 177 | return; 178 | } 179 | elasticJobConfigList.forEach(elasticJobConfig -> { 180 | registryJob(elasticJobConfig); 181 | JOB_CONFIG_MAP.put(elasticJobConfig.getJobName(), elasticJobConfig.getJobConfig()); 182 | }); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 36 | 37 | 38 |
39 |
40 | 41 |
42 |
    43 |
  • 44 | 45 |
    46 | 定时任务 47 |
    48 |
  • 49 |
50 |
51 |

定时任务

52 | 56 |
57 |
58 | 59 | 60 | 61 | 62 |
63 | 64 |
65 | 67 |
    68 |
  • 定时任务
  • 69 |
70 |

71 | 72 |

73 |
74 |
75 | 定时任务配置列表 76 |

77 | 78 | 79 | 80 |

81 |
82 | 83 | 84 |
85 | 86 |
87 |
88 | 89 |
90 |
91 | 92 | 93 | 192 | 193 | 194 | 197 | 200 | 204 | 208 | 212 | 215 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /src/main/java/cn/demo/scheduler/dao/ElasticJobConfigDao.java: -------------------------------------------------------------------------------- 1 | package cn.demo.scheduler.dao; 2 | 3 | import java.time.LocalDateTime; 4 | import java.util.ArrayList; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | import org.springframework.stereotype.Repository; 12 | import org.springframework.util.StringUtils; 13 | 14 | import com.kelaile.common.enums.DateFormatEnum; 15 | import com.kelaile.common.utils.DateUtil; 16 | 17 | import cn.demo.scheduler.entity.ElasticJobConfigBean; 18 | import cn.demo.scheduler.entity.ElasticJobConfigLogBean; 19 | 20 | /** 21 | * 查询配置 22 | * 23 | * @author xing 24 | * 25 | */ 26 | @Repository 27 | public class ElasticJobConfigDao { 28 | 29 | @Autowired 30 | private JdbcTemplate jdbcTemplate; 31 | 32 | /** 33 | * 查询配置信息 34 | * 35 | * @return 36 | */ 37 | public List getElasticJobConfigList() { 38 | String sql = "SELECT * FROM `elastic_job_config`"; 39 | return jdbcTemplate.query(sql.toString(), 40 | new BeanPropertyRowMapper(ElasticJobConfigBean.class)); 41 | } 42 | 43 | /** 44 | * 分页获取 45 | * 46 | * @param jobName 47 | * @param startIndex 48 | * @param size 49 | * @return 50 | */ 51 | public List getElasticJobConfigList(String jobName, Integer startIndex, Integer size) { 52 | List params = new ArrayList(); 53 | StringBuilder sql = new StringBuilder(); 54 | sql.append(" SELECT "); 55 | sql.append(" * "); 56 | sql.append(" FROM "); 57 | sql.append(" `elastic_job_config` "); 58 | if (!StringUtils.isEmpty(jobName)) { 59 | sql.append(" WHERE "); 60 | sql.append(" job_name = ? "); 61 | params.add(jobName); 62 | } 63 | params.add(startIndex); 64 | params.add(size); 65 | sql.append(" LIMIT ?,? "); 66 | return jdbcTemplate.query(sql.toString(), params.toArray(), 67 | new BeanPropertyRowMapper(ElasticJobConfigBean.class)); 68 | } 69 | 70 | /** 71 | * 查询数量 72 | * 73 | * @param jobName 74 | * @return 75 | */ 76 | public long getElasticJobConfigCount(String jobName) { 77 | List params = new ArrayList(); 78 | StringBuilder sql = new StringBuilder(); 79 | sql.append(" SELECT "); 80 | sql.append(" COUNT(1) "); 81 | sql.append(" FROM "); 82 | sql.append(" `elastic_job_config` "); 83 | if (!StringUtils.isEmpty(jobName)) { 84 | sql.append(" WHERE "); 85 | sql.append(" job_name = ? "); 86 | params.add(jobName); 87 | } 88 | return jdbcTemplate.queryForObject(sql.toString(), params.toArray(), Long.class); 89 | } 90 | 91 | /** 92 | * 新增 93 | * 94 | * @param config 95 | * @return 96 | */ 97 | public int insert(ElasticJobConfigBean config) { 98 | StringBuilder sql = new StringBuilder(); 99 | sql.append(" INSERT INTO `elastic_job_config` ( "); 100 | sql.append(" `job_name`, "); 101 | sql.append(" `cron`, "); 102 | sql.append(" `sharding_total_count`, "); 103 | sql.append(" `sharding_item_parameters`, "); 104 | sql.append(" `job_parameter`, "); 105 | sql.append(" `failover`, "); 106 | sql.append(" `misfire`, "); 107 | sql.append(" `description`, "); 108 | sql.append(" `job_class`, "); 109 | sql.append(" `streaming_process`, "); 110 | sql.append(" `job_config` "); 111 | sql.append(" ) "); 112 | sql.append(" VALUES "); 113 | sql.append(" (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "); 114 | List params = getParams(config); 115 | return jdbcTemplate.update(sql.toString(), params.toArray()); 116 | } 117 | 118 | /** 119 | * 更新 120 | * 121 | * @param config 122 | * @return 123 | */ 124 | public int update(ElasticJobConfigBean config) { 125 | StringBuilder sql = new StringBuilder(); 126 | sql.append(" UPDATE `elastic_job_config` "); 127 | sql.append(" SET "); 128 | sql.append(" `job_name` = ?, "); 129 | sql.append(" `cron` = ?, "); 130 | sql.append(" `sharding_total_count` = ?, "); 131 | sql.append(" `sharding_item_parameters` = ?, "); 132 | sql.append(" `job_parameter` = ?, "); 133 | sql.append(" `failover` = ?, "); 134 | sql.append(" `misfire` = ?, "); 135 | sql.append(" `description` = ?, "); 136 | sql.append(" `job_class` = ?, "); 137 | sql.append(" `streaming_process` = ?, "); 138 | sql.append(" `job_config` = ? "); 139 | sql.append(" WHERE "); 140 | sql.append(" `id` = ? "); 141 | List params = getParams(config); 142 | params.add(config.getId()); 143 | return jdbcTemplate.update(sql.toString(), params.toArray()); 144 | } 145 | 146 | /** 147 | * 数据备份 148 | * 149 | * @param config 150 | * @return 151 | */ 152 | public int backups(ElasticJobConfigBean config) { 153 | StringBuilder sql = new StringBuilder(); 154 | sql.append(" INSERT INTO `elastic_job_config_log` ( "); 155 | sql.append(" `job_name`, "); 156 | sql.append(" `cron`, "); 157 | sql.append(" `sharding_total_count`, "); 158 | sql.append(" `sharding_item_parameters`, "); 159 | sql.append(" `job_parameter`, "); 160 | sql.append(" `failover`, "); 161 | sql.append(" `misfire`, "); 162 | sql.append(" `description`, "); 163 | sql.append(" `job_class`, "); 164 | sql.append(" `streaming_process`, "); 165 | sql.append(" `job_config`, "); 166 | sql.append(" `create_time` "); 167 | sql.append(" ) "); 168 | sql.append(" VALUES "); 169 | sql.append(" (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"); 170 | List params = getParams(config); 171 | params.add(DateUtil.formatter(LocalDateTime.now(), DateFormatEnum.YYYY_MM_DD_HH_MM_SS)); 172 | return jdbcTemplate.update(sql.toString(), params.toArray()); 173 | } 174 | 175 | private List getParams(ElasticJobConfigBean config) { 176 | List params = new LinkedList(); 177 | params.add(config.getJobName()); 178 | params.add(config.getCron()); 179 | params.add(config.getShardingTotalCount()); 180 | params.add(config.getShardingItemParameters()); 181 | params.add(config.getJobParameter()); 182 | params.add(config.getFailover()); 183 | params.add(config.getMisfire()); 184 | params.add(config.getDescription()); 185 | params.add(config.getJobClass()); 186 | params.add(config.getStreamingProcess()); 187 | params.add(config.getJobConfig()); 188 | return params; 189 | } 190 | 191 | public ElasticJobConfigBean getElasticJobConfigById(String id) { 192 | String sql = "SELECT * FROM `elastic_job_config` WHERE id=?"; 193 | return jdbcTemplate.queryForObject(sql.toLowerCase(), new Object[] {id}, 194 | new BeanPropertyRowMapper(ElasticJobConfigBean.class)); 195 | } 196 | 197 | public int delete(String id) { 198 | String sql = "DELETE FROM `elastic_job_config` where id=?"; 199 | return jdbcTemplate.update(sql.toString(), new Object[] {id}); 200 | } 201 | 202 | public List getElasticJobConfigLogList(String jobName, Integer startIndex, Integer size) { 203 | List params = new ArrayList(); 204 | StringBuilder sql = new StringBuilder(); 205 | sql.append(" SELECT "); 206 | sql.append(" * "); 207 | sql.append(" FROM "); 208 | sql.append(" `elastic_job_config_log` "); 209 | if (!StringUtils.isEmpty(jobName)) { 210 | sql.append(" WHERE "); 211 | sql.append(" job_name = ? "); 212 | params.add(jobName); 213 | } 214 | params.add(startIndex); 215 | params.add(size); 216 | sql.append(" LIMIT ?,? "); 217 | return jdbcTemplate.query(sql.toString(), params.toArray(), 218 | new BeanPropertyRowMapper(ElasticJobConfigLogBean.class)); 219 | } 220 | 221 | public long getElasticJobConfigLogCount(String jobName) { 222 | List params = new ArrayList(); 223 | StringBuilder sql = new StringBuilder(); 224 | sql.append(" SELECT "); 225 | sql.append(" COUNT(1) "); 226 | sql.append(" FROM "); 227 | sql.append(" `elastic_job_config_log` "); 228 | if (!StringUtils.isEmpty(jobName)) { 229 | sql.append(" WHERE "); 230 | sql.append(" job_name = ? "); 231 | params.add(jobName); 232 | } 233 | return jdbcTemplate.queryForObject(sql.toString(), params.toArray(), Long.class); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | cn.scheduler 8 | springboot-elasticjob 9 | 1.0.0-SNAPSHOT 10 | jar 11 | 12 | springboot-elasticjob 13 | scheduler project for Spring Boot 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | Finchley.RELEASE 25 | 1.8 26 | 2.1.5 27 | 3.4 28 | 2.10.0 29 | 1.1.9 30 | 1.1.9 31 | 32 | 33 | 34 | 35 | cn.demo 36 | api 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-actuator 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starter-config 45 | 46 | 47 | org.springframework.cloud 48 | spring-cloud-starter-netflix-eureka-client 49 | 50 | 51 | org.springframework.cloud 52 | spring-cloud-starter-netflix-hystrix 53 | 54 | 55 | org.springframework.cloud 56 | spring-cloud-starter-netflix-hystrix-dashboard 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-web 61 | 62 | 63 | org.springframework.cloud 64 | spring-cloud-starter-openfeign 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-starter-web 69 | 70 | 71 | ch.qos.logback 72 | logback-core 73 | 74 | 75 | org.apache.logging.log4j 76 | log4j-api 77 | 78 | 79 | org.apache.logging.log4j 80 | log4j-to-slf4j 81 | 82 | 83 | ch.qos.logback 84 | logback-classic 85 | 86 | 87 | org.slf4j 88 | slf4j-api 89 | 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-logging 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-starter-jdbc 99 | 100 | 101 | org.slf4j 102 | slf4j-api 103 | 104 | 105 | 106 | 107 | 108 | mysql 109 | mysql-connector-java 110 | runtime 111 | 112 | 113 | com.alibaba 114 | druid-spring-boot-starter 115 | 116 | 117 | org.springframework.boot 118 | spring-boot-starter-test 119 | test 120 | 121 | 122 | org.slf4j 123 | slf4j-api 124 | 125 | 126 | 127 | 128 | org.springframework.boot 129 | spring-boot-configuration-processor 130 | true 131 | 132 | 133 | 134 | org.apache.curator 135 | curator-framework 136 | 137 | 138 | org.apache.curator 139 | curator-client 140 | 141 | 142 | org.slf4j 143 | slf4j-api 144 | 145 | 146 | 147 | 148 | org.apache.zookeeper 149 | zookeeper 150 | 151 | 152 | log4j 153 | log4j 154 | 155 | 156 | org.slf4j 157 | slf4j-api 158 | 159 | 160 | org.slf4j 161 | slf4j-log4j12 162 | 163 | 164 | 165 | 166 | org.apache.curator 167 | curator-recipes 168 | 169 | 170 | elastic-job-common-core 171 | com.dangdang 172 | 173 | 174 | org.slf4j 175 | slf4j-api 176 | 177 | 178 | 179 | 180 | elastic-job-lite-core 181 | com.dangdang 182 | 183 | 184 | elastic-job-lite-spring 185 | com.dangdang 186 | 187 | 188 | org.apache.commons 189 | commons-lang3 190 | 191 | 192 | 193 | 194 | 195 | org.springframework.cloud 196 | spring-cloud-dependencies 197 | ${spring-cloud.version} 198 | pom 199 | import 200 | 201 | 202 | org.apache.curator 203 | curator-recipes 204 | ${curator.version} 205 | 206 | 207 | org.apache.curator 208 | curator-framework 209 | ${curator.version} 210 | 211 | 212 | org.apache.curator 213 | curator-client 214 | ${curator.version} 215 | 216 | 217 | org.apache.zookeeper 218 | zookeeper 219 | 3.4.6 220 | 221 | 222 | elastic-job-common-core 223 | com.dangdang 224 | ${elastic-job.version} 225 | 226 | 227 | elastic-job-lite-core 228 | com.dangdang 229 | ${elastic-job.version} 230 | 231 | 232 | elastic-job-lite-spring 233 | com.dangdang 234 | ${elastic-job.version} 235 | 236 | 237 | com.alibaba 238 | druid-spring-boot-starter 239 | ${druid.spring.boot.version} 240 | 241 | 242 | cn.demo 243 | api 244 | 1.0.0-SNAPSHOT 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | org.springframework.boot 253 | spring-boot-maven-plugin 254 | 255 | 256 | 257 | 258 | 259 | nexus.kelaile-release 260 | http://127.0.0.1:8081/nexus/content/repositories/releases/ 261 | 262 | 263 | nexus.kelaile-snapshot 264 | http://127.0.0.1:8081/nexus/content/repositories/snapshots/ 265 | 266 | 267 | 268 | 269 | 270 | 271 | nexus.kelaile-release 272 | http://127.0.0.1:8081/nexus/content/groups/public/ 273 | 274 | true 275 | 276 | 277 | true 278 | always 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /src/main/resources/static/js/jobLog.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | // 下拉菜单 3 | $('#KWHproduct') 4 | .on( 5 | 'hidden.bs.collapse', 6 | function() { 7 | $('.KWHleftTitle') 8 | .css('background', 9 | '#f8f9fb url(img/KWHup.png) no-repeat right 10% center') 10 | }); 11 | $('#KWHproduct') 12 | .on( 13 | 'shown.bs.collapse', 14 | function() { 15 | $('.KWHleftTitle') 16 | .css('background', 17 | '#f8f9fb url(img/KWHdown.png) no-repeat right 10% center') 18 | }); 19 | $('#KWHuser') 20 | .on( 21 | 'hidden.bs.collapse', 22 | function() { 23 | $('.KWHrightTitle') 24 | .css('background', 25 | '#f8f9fb url(img/KWHup.png) no-repeat right 10% center') 26 | }); 27 | $('#KWHuser') 28 | .on( 29 | 'shown.bs.collapse', 30 | function() { 31 | $('.KWHrightTitle') 32 | .css('background', 33 | '#f8f9fb url(img/KWHdown.png) no-repeat right 10% center') 34 | }); 35 | 36 | var oBodyHeight = $('body').height(); 37 | $('.KWHmainLeft').css('height', oBodyHeight + 200); 38 | // 获取右侧宽度 39 | getRightWidth(); 40 | // 改变左侧菜单 41 | changeLeftMen(); 42 | $(window).resize(function() { 43 | changeLeftMen(); 44 | }); 45 | 46 | // 点击左侧小图标 47 | $('.KWHcontentIcon li').click(function() { 48 | $('.KWHmainLeft').css('width', '200px'); 49 | $('.KWHleftMenu').show(); 50 | $('.KWHcontentIcon').hide(); 51 | getRightWidth(); 52 | }) 53 | // 划过左侧小图标 54 | $('.KWHcontentIcon li').hover(function() { 55 | $(this).find('.KWHiconLeft').stop().animate({ 56 | 'width' : '100px', 57 | 'opacity' : '1' 58 | }); 59 | }, function() { 60 | $(this).find('.KWHiconLeft').stop().animate({ 61 | 'width' : '0px', 62 | 'opacity' : '0' 63 | }); 64 | }) 65 | 66 | $("#userDataTable").bootstrapTable({ // 对应table标签的id 67 | url: "job/getElasticJobConfigLogList", // 获取表格数据的url 68 | cache: false, // 设置为 false 禁用 AJAX 数据缓存, 默认为true 69 | striped: true, //表格显示条纹,默认为false 70 | pagination: true, // 在表格底部显示分页组件,默认false 71 | pageList: [10, 20], // 设置页面可以显示的数据条数 72 | pageSize: 10, // 页面数据条数 73 | pageNumber: 1, // 首页页码 74 | sidePagination: 'server', // 设置为服务器端分页 75 | queryParams: function (params) { // 请求服务器数据时发送的参数,可以在这里添加额外的查询参数,返回false则终止请求 76 | 77 | return { 78 | size: params.limit, // 每页要显示的数据条数 79 | startIndex: params.offset, // 每页显示数据的开始行号 80 | jobName:$("#jobNames").val() 81 | } 82 | }, 83 | columns: [ 84 | { 85 | field: 'id', // 返回json数据中的name 86 | title: '编号', // 表格表头显示文字 87 | align: 'center', // 左右居中 88 | valign: 'middle', // 上下居中 89 | formatter : function(value, row, index) { 90 | return index + 1; 91 | } 92 | 93 | }, { 94 | field: 'jobName', 95 | title: '任务名称', 96 | align: 'center', 97 | valign: 'middle' 98 | }, { 99 | field: 'cron', 100 | title: '表达式', 101 | align: 'center', 102 | valign: 'middle' 103 | }, { 104 | field: 'shardingTotalCount', 105 | title: '分片数', 106 | align: 'center', 107 | valign: 'middle' 108 | }, { 109 | field: 'shardingItemParameters', 110 | title: '分片参数', 111 | align: 'center', 112 | valign: 'middle' 113 | }, { 114 | field: 'jobParameter', 115 | title: '任务参数', 116 | align: 'center', 117 | valign: 'middle' 118 | }, { 119 | field: 'failover', 120 | title: '任务失效转移', 121 | align: 'center', 122 | valign: 'middle' 123 | }, { 124 | field: 'misfire', 125 | title: '错过任务重新执行', 126 | align: 'center', 127 | valign: 'middle' 128 | }, { 129 | field: 'jobClass', 130 | title: '任务类', 131 | align: 'center', 132 | valign: 'middle' 133 | }, { 134 | field: 'description', 135 | title: '任务描述', 136 | align: 'center', 137 | valign: 'middle' 138 | }, { 139 | field: 'streamingProcess', 140 | title: '流式处理', 141 | align: 'center', 142 | valign: 'middle' 143 | }, { 144 | field: "jobConfig", 145 | title: "额外数据", 146 | align: 'center', 147 | valign: 'middle', 148 | width: 160, // 定义列的宽度,单位为像素px 149 | formatter: function (value, row, index) { 150 | return ''; 151 | } 152 | } 153 | ], 154 | onLoadSuccess: function(){ //加载成功时执行 155 | console.info("加载成功"); 156 | }, 157 | onLoadError: function(){ //加载失败时执行 158 | console.info("加载数据失败"); 159 | } 160 | 161 | }) 162 | // 分页 163 | $('#KWHcontrollLeftMenu').click(function() { 164 | var KWHmainLeftWIdth = $('.KWHmainLeft').css('width'); 165 | if (KWHmainLeftWIdth == (200 + 'px')) { 166 | $('.KWHmainLeft').css('width', '100px'); 167 | $('.KWHleftMenu').hide(); 168 | $('.KWHcontentIcon').show(); 169 | } else { 170 | $('.KWHmainLeft').css('width', '200px'); 171 | $('.KWHleftMenu').show(); 172 | $('.KWHcontentIcon').hide(); 173 | } 174 | getRightWidth(); 175 | }) 176 | 177 | function getRightWidth() { 178 | var oBodyWidth = $('body').width(); 179 | var KWHmainLeftWidth = $('.KWHmainLeft').width(); 180 | var KWHmainRight = oBodyWidth - KWHmainLeftWidth - 18; 181 | $('.KWHmainRight').css('width', KWHmainRight); 182 | } 183 | 184 | function changeLeftMen() { 185 | var windowWidth = $(window).width(); 186 | if (windowWidth <= 768 && windowWidth >= 320) { 187 | $('.KWHmainLeft').css('width', '100px'); 188 | $('.KWHleftMenu').hide(); 189 | $('.KWHcontentIcon').show(); 190 | } else { 191 | $('.KWHmainLeft').css('width', '200px'); 192 | $('.KWHleftMenu').show(); 193 | $('.KWHcontentIcon').hide(); 194 | 195 | } 196 | getRightWidth(); 197 | } 198 | 199 | var trigger = function(jobGroup, jobName) { 200 | alert(jobName); 201 | } 202 | 203 | }) 204 | function shows(value){ 205 | $('#jobConfig').val(value) 206 | } 207 | function logout() { 208 | bootbox.confirm("确定退出?", function(flag) { 209 | if (flag) { 210 | window.location.href = "/logout?a=" + new Date().getTime(); 211 | } 212 | }); 213 | 214 | } 215 | 216 | function operate() { 217 | bootbox.confirm("确定执行操作?", function(flag) { 218 | if (flag) { 219 | var count = $("#shardingTotalCount").val(); 220 | if (count < 1) { 221 | bootbox.alert("分片数必须大于1"); 222 | return; 223 | } 224 | var shardingItemParameters = $("#shardingItemParameters").val(); 225 | if (shardingItemParameters == null || shardingItemParameters.length == 0) { 226 | shardingItemParameters = null; 227 | } 228 | var jobParameter = $("#jobParameter").val(); 229 | if (jobParameter == null || jobParameter.length == 0) { 230 | jobParameter = null; 231 | } 232 | var jobConfig = $("#jobConfig").val(); 233 | if (jobConfig == null || jobConfig.length == 0) { 234 | jobConfig = null; 235 | } 236 | 237 | $.ajax({ 238 | type : 'post', 239 | url : 'job/addOrUpdateElasticJobConfig', 240 | async : false, 241 | data : { 242 | id: $("#id").val(), 243 | jobName: $("#jobName").val(), 244 | cron: $("#cron").val(), 245 | shardingTotalCount: $("#shardingTotalCount").val(), 246 | shardingItemParameters: shardingItemParameters, 247 | jobParameter: jobParameter, 248 | description: $("#description").val(), 249 | jobClass: $("#jobClass").val(), 250 | jobConfig: jobConfig, 251 | failover: $("#failover").val(), 252 | misfire: $("#misfire").val(), 253 | streamingProcess: $("#streamingProcess").val() 254 | }, 255 | // dataType : 'json', 256 | success : function(result) { 257 | if (result == 1) { 258 | bootbox.alert("操作成功"); 259 | $("#userDataTable").bootstrapTable("refresh", { 260 | silent : true 261 | }); 262 | clearData(); 263 | $('#myModal').modal('hide'); 264 | } else { 265 | bootbox.alert("操作失败"); 266 | } 267 | }, 268 | error : function(result) { 269 | bootbox.alert("操作失败"); 270 | } 271 | }); 272 | } 273 | }); 274 | } 275 | function clearData() { 276 | $("#id").val(''); 277 | $("#jobName").val(''); 278 | $("#cron").val(''); 279 | $("#shardingTotalCount").val(''); 280 | $("#shardingItemParameters").val(''); 281 | $("#jobParameter").val(''); 282 | $("#description").val(''); 283 | $("#jobClass").val(''); 284 | $("#jobConfig").val(''); 285 | $("#failover").val('true'); 286 | $("#misfire").val('true'); 287 | $("#streamingProcess").val('true'); 288 | 289 | } 290 | function update(id) { 291 | clearData(); 292 | $.ajax({ 293 | type : 'get', 294 | url : 'job/getElasticJobConfig', 295 | async : false, 296 | data : { 297 | 'id': id 298 | }, 299 | dataType : 'json', 300 | success : function(result) { 301 | if (result != null) { 302 | $("#id").val(result.id); 303 | $("#jobName").val(result.jobName); 304 | $("#cron").val(result.cron); 305 | $("#shardingTotalCount").val(result.shardingTotalCount); 306 | $("#shardingItemParameters").val(result.shardingItemParameters); 307 | $("#jobParameter").val(result.jobParameter); 308 | $("#description").val(result.description); 309 | $("#jobClass").val(result.jobClass); 310 | $("#jobConfig").val(result.jobConfig); 311 | $("#failover").val(result.failover); 312 | $("#misfire").val(result.misfire); 313 | $("#streamingProcess").val(result.streamingProcess); 314 | 315 | } 316 | }, 317 | error : function(result) { 318 | bootbox.alert("操作失败"); 319 | } 320 | }); 321 | } 322 | function deleteData(id) { 323 | bootbox.confirm("确定执行操作?", function(flag) { 324 | if (flag) { 325 | $.ajax({ 326 | type : 'post', 327 | url : 'job/delete', 328 | async : false, 329 | data : { 330 | 'id': id 331 | }, 332 | dataType : 'json', 333 | success : function(result) { 334 | if (result != null && result == 1) { 335 | bootbox.alert("删除成功"); 336 | $("#userDataTable").bootstrapTable("refresh", { 337 | silent : true 338 | }); 339 | } else { 340 | bootbox.alert("删除失败"); 341 | } 342 | }, 343 | error : function(result) { 344 | bootbox.alert("删除失败"); 345 | } 346 | }); 347 | } 348 | }); 349 | } 350 | function refreshTable(){ 351 | $("#userDataTable").bootstrapTable("refresh", { 352 | silent : true 353 | }); 354 | $("#jobNames").val(""); 355 | } -------------------------------------------------------------------------------- /src/main/resources/static/js/index.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | // 下拉菜单 3 | $('#KWHproduct') 4 | .on( 5 | 'hidden.bs.collapse', 6 | function() { 7 | $('.KWHleftTitle') 8 | .css('background', 9 | '#f8f9fb url(img/KWHup.png) no-repeat right 10% center') 10 | }); 11 | $('#KWHproduct') 12 | .on( 13 | 'shown.bs.collapse', 14 | function() { 15 | $('.KWHleftTitle') 16 | .css('background', 17 | '#f8f9fb url(img/KWHdown.png) no-repeat right 10% center') 18 | }); 19 | $('#KWHuser') 20 | .on( 21 | 'hidden.bs.collapse', 22 | function() { 23 | $('.KWHrightTitle') 24 | .css('background', 25 | '#f8f9fb url(img/KWHup.png) no-repeat right 10% center') 26 | }); 27 | $('#KWHuser') 28 | .on( 29 | 'shown.bs.collapse', 30 | function() { 31 | $('.KWHrightTitle') 32 | .css('background', 33 | '#f8f9fb url(img/KWHdown.png) no-repeat right 10% center') 34 | }); 35 | 36 | var oBodyHeight = $('body').height(); 37 | $('.KWHmainLeft').css('height', oBodyHeight + 200); 38 | // 获取右侧宽度 39 | getRightWidth(); 40 | // 改变左侧菜单 41 | changeLeftMen(); 42 | $(window).resize(function() { 43 | changeLeftMen(); 44 | }); 45 | 46 | // 点击左侧小图标 47 | $('.KWHcontentIcon li').click(function() { 48 | $('.KWHmainLeft').css('width', '200px'); 49 | $('.KWHleftMenu').show(); 50 | $('.KWHcontentIcon').hide(); 51 | getRightWidth(); 52 | }) 53 | // 划过左侧小图标 54 | $('.KWHcontentIcon li').hover(function() { 55 | $(this).find('.KWHiconLeft').stop().animate({ 56 | 'width' : '100px', 57 | 'opacity' : '1' 58 | }); 59 | }, function() { 60 | $(this).find('.KWHiconLeft').stop().animate({ 61 | 'width' : '0px', 62 | 'opacity' : '0' 63 | }); 64 | }) 65 | 66 | $("#userDataTable").bootstrapTable({ // 对应table标签的id 67 | url: "job/getElasticJobConfigList", // 获取表格数据的url 68 | cache: false, // 设置为 false 禁用 AJAX 数据缓存, 默认为true 69 | striped: true, //表格显示条纹,默认为false 70 | pagination: true, // 在表格底部显示分页组件,默认false 71 | pageList: [10, 20], // 设置页面可以显示的数据条数 72 | pageSize: 10, // 页面数据条数 73 | pageNumber: 1, // 首页页码 74 | sidePagination: 'server', // 设置为服务器端分页 75 | queryParams: function (params) { // 请求服务器数据时发送的参数,可以在这里添加额外的查询参数,返回false则终止请求 76 | 77 | return { 78 | size: params.limit, // 每页要显示的数据条数 79 | startIndex: params.offset, // 每页显示数据的开始行号 80 | jobName:$("#jobNames").val() 81 | } 82 | }, 83 | columns: [ 84 | { 85 | field: 'id', // 返回json数据中的name 86 | title: '编号', // 表格表头显示文字 87 | align: 'center', // 左右居中 88 | valign: 'middle', // 上下居中 89 | formatter : function(value, row, index) { 90 | return index + 1; 91 | } 92 | 93 | }, { 94 | field: 'jobName', 95 | title: '任务名称', 96 | align: 'center', 97 | valign: 'middle' 98 | }, { 99 | field: 'cron', 100 | title: '表达式', 101 | align: 'center', 102 | valign: 'middle' 103 | }, { 104 | field: 'shardingTotalCount', 105 | title: '分片数', 106 | align: 'center', 107 | valign: 'middle' 108 | }, { 109 | field: 'shardingItemParameters', 110 | title: '分片参数', 111 | align: 'center', 112 | valign: 'middle' 113 | }, { 114 | field: 'jobParameter', 115 | title: '任务参数', 116 | align: 'center', 117 | valign: 'middle' 118 | }, { 119 | field: 'failover', 120 | title: '任务失效转移', 121 | align: 'center', 122 | valign: 'middle' 123 | }, { 124 | field: 'misfire', 125 | title: '错过任务重新执行', 126 | align: 'center', 127 | valign: 'middle' 128 | }, { 129 | field: 'jobClass', 130 | title: '任务类', 131 | align: 'center', 132 | valign: 'middle' 133 | }, { 134 | field: 'description', 135 | title: '任务描述', 136 | align: 'center', 137 | valign: 'middle' 138 | }, { 139 | field: 'streamingProcess', 140 | title: '流式处理', 141 | align: 'center', 142 | valign: 'middle' 143 | }, { 144 | title: "操作", 145 | align: 'center', 146 | valign: 'middle', 147 | width: 160, // 定义列的宽度,单位为像素px 148 | formatter: function (value, row, index) { 149 | return '' 150 | + ''; 151 | } 152 | } 153 | ], 154 | onLoadSuccess: function(){ //加载成功时执行 155 | console.info("加载成功"); 156 | }, 157 | onLoadError: function(){ //加载失败时执行 158 | console.info("加载数据失败"); 159 | } 160 | 161 | }) 162 | 163 | // 分页 164 | $('#KWHcontrollLeftMenu').click(function() { 165 | var KWHmainLeftWIdth = $('.KWHmainLeft').css('width'); 166 | if (KWHmainLeftWIdth == (200 + 'px')) { 167 | $('.KWHmainLeft').css('width', '100px'); 168 | $('.KWHleftMenu').hide(); 169 | $('.KWHcontentIcon').show(); 170 | } else { 171 | $('.KWHmainLeft').css('width', '200px'); 172 | $('.KWHleftMenu').show(); 173 | $('.KWHcontentIcon').hide(); 174 | } 175 | getRightWidth(); 176 | }) 177 | 178 | function getRightWidth() { 179 | var oBodyWidth = $('body').width(); 180 | var KWHmainLeftWidth = $('.KWHmainLeft').width(); 181 | var KWHmainRight = oBodyWidth - KWHmainLeftWidth - 18; 182 | $('.KWHmainRight').css('width', KWHmainRight); 183 | } 184 | 185 | function changeLeftMen() { 186 | var windowWidth = $(window).width(); 187 | if (windowWidth <= 768 && windowWidth >= 320) { 188 | $('.KWHmainLeft').css('width', '100px'); 189 | $('.KWHleftMenu').hide(); 190 | $('.KWHcontentIcon').show(); 191 | } else { 192 | $('.KWHmainLeft').css('width', '200px'); 193 | $('.KWHleftMenu').show(); 194 | $('.KWHcontentIcon').hide(); 195 | 196 | } 197 | getRightWidth(); 198 | } 199 | 200 | var trigger = function(jobGroup, jobName) { 201 | alert(jobName); 202 | } 203 | 204 | }) 205 | function logout() { 206 | bootbox.confirm("确定退出?", function(flag) { 207 | if (flag) { 208 | window.location.href = "/logout?a=" + new Date().getTime(); 209 | } 210 | }); 211 | 212 | } 213 | 214 | function operate() { 215 | bootbox.confirm("确定执行操作?", function(flag) { 216 | if (flag) { 217 | var count = $("#shardingTotalCount").val(); 218 | if (count < 1) { 219 | bootbox.alert("分片数必须大于1"); 220 | return; 221 | } 222 | var jobName = $("#jobName").val(); 223 | if(!jobName){ 224 | bootbox.alert("请补全任务名称"); 225 | return; 226 | } 227 | var cron = $("#cron").val(); 228 | if(!cron){ 229 | bootbox.alert("请补全表达式"); 230 | return; 231 | } 232 | var jobClass = $("#jobClass").val(); 233 | if(!jobClass){ 234 | bootbox.alert("请补全任务类"); 235 | return; 236 | } 237 | var description = $("#description").val(); 238 | if(!description){ 239 | bootbox.alert("请补全任务描述"); 240 | return; 241 | } 242 | var shardingItemParameters = $("#shardingItemParameters").val(); 243 | if (shardingItemParameters == null || shardingItemParameters.length == 0) { 244 | shardingItemParameters = null; 245 | } 246 | var jobParameter = $("#jobParameter").val(); 247 | if (jobParameter == null || jobParameter.length == 0) { 248 | jobParameter = null; 249 | } 250 | var jobConfig = $("#jobConfig").val(); 251 | if (jobConfig == null || jobConfig.length == 0) { 252 | jobConfig = null; 253 | } 254 | 255 | $.ajax({ 256 | type : 'post', 257 | url : 'job/addOrUpdateElasticJobConfig', 258 | async : false, 259 | data : { 260 | id: $("#id").val(), 261 | jobName: $("#jobName").val(), 262 | cron: $("#cron").val(), 263 | shardingTotalCount: $("#shardingTotalCount").val(), 264 | shardingItemParameters: shardingItemParameters, 265 | jobParameter: jobParameter, 266 | description: $("#description").val(), 267 | jobClass: $("#jobClass").val(), 268 | jobConfig: jobConfig, 269 | failover: $("#failover").val(), 270 | misfire: $("#misfire").val(), 271 | streamingProcess: $("#streamingProcess").val() 272 | }, 273 | // dataType : 'json', 274 | success : function(result) { 275 | if (result == 1) { 276 | bootbox.alert("操作成功"); 277 | $("#userDataTable").bootstrapTable("refresh", { 278 | silent : true 279 | }); 280 | clearData(); 281 | $('#myModal').modal('hide'); 282 | } else { 283 | bootbox.alert("操作失败"); 284 | } 285 | }, 286 | error : function(result) { 287 | bootbox.alert("操作失败"); 288 | } 289 | }); 290 | } 291 | }); 292 | } 293 | function clearData() { 294 | $("#id").val(''); 295 | $("#jobName").val(''); 296 | $("#cron").val(''); 297 | $("#shardingTotalCount").val(''); 298 | $("#shardingItemParameters").val(''); 299 | $("#jobParameter").val(''); 300 | $("#description").val(''); 301 | $("#jobClass").val(''); 302 | $("#jobConfig").val(''); 303 | $("#failover").val('true'); 304 | $("#misfire").val('true'); 305 | $("#streamingProcess").val('true'); 306 | 307 | } 308 | function update(id) { 309 | clearData(); 310 | $.ajax({ 311 | type : 'get', 312 | url : 'job/getElasticJobConfig', 313 | async : false, 314 | data : { 315 | 'id': id 316 | }, 317 | dataType : 'json', 318 | success : function(result) { 319 | if (result != null) { 320 | $("#id").val(result.id); 321 | $("#jobName").val(result.jobName); 322 | $("#cron").val(result.cron); 323 | $("#shardingTotalCount").val(result.shardingTotalCount); 324 | $("#shardingItemParameters").val(result.shardingItemParameters); 325 | $("#jobParameter").val(result.jobParameter); 326 | $("#description").val(result.description); 327 | $("#jobClass").val(result.jobClass); 328 | $("#jobConfig").val(result.jobConfig); 329 | $("#failover").val(result.failover); 330 | $("#misfire").val(result.misfire); 331 | $("#streamingProcess").val(result.streamingProcess); 332 | 333 | } 334 | }, 335 | error : function(result) { 336 | bootbox.alert("操作失败"); 337 | } 338 | }); 339 | } 340 | function deleteData(id) { 341 | bootbox.confirm("确定执行操作?", function(flag) { 342 | if (flag) { 343 | $.ajax({ 344 | type : 'post', 345 | url : 'job/delete', 346 | async : false, 347 | data : { 348 | 'id': id 349 | }, 350 | dataType : 'json', 351 | success : function(result) { 352 | if (result != null && result == 1) { 353 | bootbox.alert("删除成功"); 354 | $("#userDataTable").bootstrapTable("refresh", { 355 | silent : true 356 | }); 357 | } else { 358 | bootbox.alert("删除失败"); 359 | } 360 | }, 361 | error : function(result) { 362 | bootbox.alert("删除失败"); 363 | } 364 | }); 365 | } 366 | }); 367 | } 368 | function refreshTable(){ 369 | $("#userDataTable").bootstrapTable("refresh", { 370 | silent : true 371 | }); 372 | $("#jobNames").val(""); 373 | } -------------------------------------------------------------------------------- /src/main/resources/static/css/common.css: -------------------------------------------------------------------------------- 1 | .fl{ 2 | float: left; 3 | } 4 | .fr{ 5 | float: right; 6 | } 7 | .hid{ 8 | display: none; 9 | } 10 | body,html{ 11 | width: 100%; 12 | /*height: 100%;*/ 13 | } 14 | #KWHmain{ 15 | width: 100%; 16 | overflow: hidden; 17 | } 18 | .KWHmainLeft{ 19 | width: 200px; 20 | height:100%; 21 | background-size: 100%; 22 | color: #8f9092; 23 | font-size: 1.6rem; 24 | border-right: 1px solid #ECECEC; 25 | } 26 | .KWHlogo{ 27 | width: 100%; 28 | background: #4b8bef; 29 | } 30 | .KWHlogo img{ 31 | padding: 6% 20%; 32 | } 33 | .KWHleftTitle,.KWHrightTitle{ 34 | width: 100%; 35 | padding-left: 10%; 36 | line-height: 4rem; 37 | background: #f8f9fb url(../img/KWHdown.png) no-repeat right 10% center; 38 | cursor: pointer; 39 | } 40 | .KWHproductLi{ 41 | width: 100%; 42 | padding-left: 30%; 43 | line-height: 4rem; 44 | background: url(../img/KWHicon_01.png) no-repeat left 20% center; 45 | border-left: 3px solid #fff; 46 | font-size: 1.3rem; 47 | cursor: pointer; 48 | } 49 | .KWHproductLi a{ 50 | display: block; 51 | color: #c3c3c3; 52 | } 53 | .KWHproductLi a:hover{ 54 | color: #f9b621; 55 | text-decoration: none; 56 | } 57 | .KWHaddBj{ 58 | border-left: 3px solid #3865c9; 59 | background: #4b8bef url(../img/KWHicon_01.png) no-repeat left 20% center; 60 | color: #fff; 61 | } 62 | .KWHaddBj a{ 63 | color: #fff; 64 | } 65 | .KWHmainRight{ 66 | /*min-height: 1000px;*/ 67 | /*background: #F2DEDE;*/ 68 | color: #c3c3c3; 69 | height: 100%; 70 | overflow: hidden; 71 | } 72 | .KWHmainRightTop{ 73 | width: 100%; 74 | line-height: 5.4rem; 75 | overflow: hidden; 76 | border-bottom: 1px solid #ececec; 77 | } 78 | .KWHmainRightTopMenu{ 79 | width: 46%; 80 | margin-left: 8%; 81 | } 82 | .KWHmainRightTopMenu li{ 83 | float: right; 84 | border-bottom: 2px solid #fff; 85 | padding: 0 3%; 86 | margin: 0 4%; 87 | cursor: pointer; 88 | } 89 | .KWHmainRightTopMenu .KWHactive{ 90 | border-bottom: 2px solid #4b8bef; 91 | color: #4b8bef; 92 | } 93 | .KWHmainRightTopMenu li:first-child{ 94 | margin: 0 0 0 4%; 95 | } 96 | .KWHmainRightTopBtn{ 97 | padding: 2rem; 98 | } 99 | .KWHmessageImg{ 100 | border-left: 1px solid #ececec; 101 | border-right: 1px solid #ececec; 102 | position: relative; 103 | } 104 | .KWHmessageImg img{ 105 | padding: 0 2rem; 106 | } 107 | .KWHmail{ 108 | margin-right: 4%; 109 | padding-right: 1%; 110 | background: url(../img/KWHdownIcon.png) no-repeat right center; 111 | } 112 | .KWHcontentIcon{ 113 | line-height: 5rem; 114 | text-align: center; 115 | } 116 | .KWHcontentIcon a{ 117 | display: block; 118 | } 119 | .KWHcontentIcon a:hover{ 120 | background: #4b8bef; 121 | } 122 | .KWHcontentIcon li{ 123 | position: relative; 124 | } 125 | .KWHcontentIcon li .KWHiconLeft{ 126 | position: absolute; 127 | top: 15px; 128 | left: 99px; 129 | color: #fff; 130 | width: 0px; 131 | height: 30px; 132 | line-height: 30px; 133 | opacity: 0; 134 | text-align: left; 135 | text-indent: 11px; 136 | } 137 | .KWHiconLeft{ 138 | background: url(../img/left.png) no-repeat left center; 139 | } 140 | .KWHiconLeft span{ 141 | background: #494949; 142 | padding: 8px; 143 | font-size: 12px; 144 | } 145 | .KWHmessageImg span{ 146 | width: 1.4rem; 147 | height: 1.4rem; 148 | border-radius: 1.4rem; 149 | background: #f9b621; 150 | color: #fff; 151 | line-height: 1.4rem; 152 | font-size: 0.6rem; 153 | text-align: center; 154 | position: absolute; 155 | right: 1rem; 156 | top: 1rem; 157 | } 158 | .KWHmainRightTitle{ 159 | line-height: 4rem; 160 | padding-left: 2rem; 161 | border-bottom:1px solid #ececec ; 162 | } 163 | .input-group-addon{ 164 | background-color: #4b8bef; 165 | color: #fff; 166 | border: 1px solid #4b8bef; 167 | } 168 | .form-control{ 169 | border: 1px solid #ececec; 170 | height: 3.4rem; 171 | line-height: 3.4rem; 172 | } 173 | .KWHsearch{ 174 | padding: 4rem 14%; 175 | border-bottom:1px solid #ececec ; 176 | } 177 | /*KWHmainCenter star*/ 178 | .KWHmainCenter{ 179 | padding: 4rem 14%; 180 | border-bottom:1px solid #ececec ; 181 | } 182 | .KWHmainCenterTop{ 183 | width: 100%; 184 | overflow: hidden; 185 | } 186 | .KWHmainCenterTopLeft,.KWHmainCenterTopRight{ 187 | width: 50%; 188 | margin: 1rem 0; 189 | } 190 | .KWHmainCenterLabel{ 191 | width: 25%; 192 | text-align: right; 193 | } 194 | .KWHmainCenterTopLeft input,.KWHmainCenterTopRight input,.KWHmainCenterTopLeft select,.KWHmainCenterTopRight select{ 195 | width: 60%; 196 | height: 3rem; 197 | border:1px solid #ececec; 198 | color: #949494; 199 | } 200 | @media only screen and (min-width: 100px) and (max-width: 1100px) { 201 | .KWHmainCenterTopLeft, .KWHmainCenterTopRight{ 202 | width: 100%; 203 | margin: 1rem 0; 204 | } 205 | } 206 | @media only screen and (min-width: 1043px) and (max-width: 1236px) { 207 | .KWHmainRightTopMenu{ 208 | width: 58%; 209 | margin-left: 0; 210 | } 211 | } 212 | .KWHmainBottom{ 213 | padding:2rem 4%; 214 | } 215 | .bootstrap-table .table, .bootstrap-table .table>tbody>tr>td, .bootstrap-table .table>tbody>tr>th, .bootstrap-table .table>tfoot>tr>td, .bootstrap-table .table>tfoot>tr>th, .bootstrap-table .table>thead>tr>td{ 216 | padding: 12px!important; 217 | color: #8a8a8a; 218 | text-align: center; 219 | } 220 | .fixed-table-container tbody td .th-inner, .fixed-table-container thead th .th-inner{ 221 | text-align: center; 222 | color: #656678; 223 | } 224 | .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus{ 225 | background-color: #4b8bef; 226 | border-color: #4b8bef; 227 | } 228 | .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus{ 229 | background-color: #4b8bef; 230 | } 231 | /*KWHmainCenter end*/ 232 | /*KWLmainContentCenter star*/ 233 | .KWLmainContentCenter{ 234 | width: 400px; 235 | background: #fff; 236 | padding: 2rem; 237 | border-radius: 4px; 238 | } 239 | .KWLuser,.KWLpassWord{ 240 | width: 100%; 241 | line-height: 3rem; 242 | height: 3rem; 243 | outline: none; 244 | border: 1px solid #ececec; 245 | text-indent: 4rem; 246 | color: #babbbd; 247 | margin: 1rem 0; 248 | background: url(../img/DFHuser.png) no-repeat left 1rem center; 249 | } 250 | .KWLpassWord{ 251 | background: url(../img/DFpassword.png) no-repeat left 1rem center; 252 | } 253 | .KWLloginBtn{ 254 | -webkit-appearance: none; 255 | -moz-appearance: none; 256 | appearance: none; 257 | border: 0; 258 | outline:none; 259 | -webkit-tap-highlight-color: rgba(0,0,0,0); 260 | -webkit-tap-highlight-color: transparent; 261 | background: #2773ec; 262 | text-align: center; 263 | line-height: 3rem; 264 | color:#fff ; 265 | width: 100%; 266 | margin: 1rem 0; 267 | 268 | } 269 | /*checkbox star*/ 270 | .checkbox_box { 271 | width: 1.4rem; 272 | height: 1.4rem; 273 | border-radius: 4px; 274 | border: 1px solid #b3b3b2; 275 | position: relative; 276 | vertical-align: middle; 277 | background-color:#fff; 278 | float: left; 279 | margin-top: 0.2rem; 280 | } 281 | .checkbox_box input { 282 | position: absolute; 283 | top: 0; 284 | left: 0; 285 | width: 100%; 286 | height: 100%; 287 | opacity: 0; 288 | z-index: 100;/*使input按钮在span的上一层,不加点击区域会出现不灵敏*/ 289 | } 290 | .addChecked{ 291 | width: 14px; 292 | height: 10px; 293 | display: inline-block; 294 | background: url(../img/checkboxImg.png) no-repeat; 295 | position: absolute; 296 | left: 50%; 297 | top: 50%; 298 | margin: -5px 0 0 -7px; 299 | z-index: 1; 300 | -webkit-tap-highlight-color: rgba(255,0,0,0); 301 | } 302 | input[type="checkbox"] + span { 303 | opacity: 0; 304 | } 305 | input[type="checkbox"]:checked + span { 306 | opacity: 1; 307 | } 308 | /*checkbox end*/ 309 | .KWHLcheckboxContent{ 310 | overflow: hidden; 311 | margin: 1rem 0; 312 | color: #a9aaac; 313 | } 314 | .KWHLrember,.KWHLforget{ 315 | margin-left: 0.5rem; 316 | font-size: 1.6rem; 317 | line-height: 1.6rem; 318 | font-weight: normal!important; 319 | } 320 | .KWHLforget{ 321 | margin-left: 40%; 322 | } 323 | #KWLmainContent{ 324 | padding: 10% 0 0 34%; 325 | } 326 | .KWLmainContentLogo{ 327 | margin-bottom:1rem; 328 | text-indent: 4%; 329 | } 330 | /*KWLmainContentCenter end*/ 331 | /*检测工具 star*/ 332 | .KWTheader{ 333 | width: 100%; 334 | overflow: hidden; 335 | color: #fff; 336 | font-size: 20px; 337 | } 338 | .KWTheaderLeft{ 339 | width: 20%; 340 | padding: 20px 0; 341 | background: #41c03b; 342 | } 343 | .KWTheaderLeft span,.KWTheaderRight span{ 344 | background: url(../img/headerLeft.png) no-repeat left center; 345 | margin-left: 10%; 346 | padding-left: 30px; 347 | } 348 | .KWTheaderRight{ 349 | width: 80%; 350 | padding: 20px 0; 351 | background: #3385ff; 352 | } 353 | .KWTheaderRight span{ 354 | background: url(../img/headerRight.png) no-repeat left center; 355 | margin-left: 3%; 356 | } 357 | 358 | .KWTmain{ 359 | overflow: hidden; 360 | width: 100%; 361 | } 362 | .KWTmainLeft{ 363 | width: 20%; 364 | padding:0 2%; 365 | border-right: 1px solid #f2f2f2; 366 | background: #fbfbfb; 367 | } 368 | .KWTmainLeftTitle{ 369 | line-height: 48px; 370 | color: #000; 371 | font-size: 16px; 372 | overflow: hidden; 373 | } 374 | .KWTmainLeftTitle span:first-child{ 375 | width: 4px; 376 | height: 16px; 377 | background: #41c03b; 378 | margin: 17px 8px 0 0; 379 | } 380 | .KWTvin{ 381 | width: 100%; 382 | height: 110px; 383 | border: 1px solid #f2f2f2; 384 | } 385 | .KWTcarBtn input{ 386 | -webkit-appearance: none; 387 | -moz-appearance: none; 388 | appearance: none; 389 | border: 0; 390 | outline:none; 391 | -webkit-tap-highlight-color: rgba(0,0,0,0); 392 | -webkit-tap-highlight-color: transparent; 393 | width: 100%; 394 | color: #fff; 395 | background: #41c03b; 396 | line-height: 30px; 397 | text-align: center; 398 | margin-top:20px; 399 | border-radius: 4px; 400 | } 401 | .KWTcarBtn .KWTchangColor{ 402 | background: #3385ff; 403 | } 404 | .KWTmainRight{ 405 | width: 80%; 406 | } 407 | .KWTmainRightTopTitle{ 408 | padding-left: 4%; 409 | line-height: 50px; 410 | font-size: 16px; 411 | } 412 | .KWTmainRightTopTable,.KWTmainRightBottomTable{ 413 | padding: 0 4%; 414 | } 415 | .KWTmainRightBottomTable{ 416 | margin-bottom: 20px; 417 | } 418 | .KWTtdPadding p{ 419 | border-bottom: 1px solid #ddd; 420 | } 421 | .KWTtdPadding p:last-child{ 422 | border-bottom: none; 423 | } 424 | /*检测工具 end*/ 425 | /*APP登录次数 star*/ 426 | .ALfontColor{ 427 | color: #c3c3c3; 428 | } 429 | .ALfontColorNow{ 430 | color: #4b8bef; 431 | } 432 | .ALTitleLeftImg{ 433 | width: 20px; 434 | cursor: pointer; 435 | margin-right: 1rem; 436 | } 437 | .KWHmainRightName{ 438 | width: 90%; 439 | margin: 0 auto; 440 | border-bottom: 1px solid #ececec; 441 | padding-top: 2rem; 442 | line-height: 4rem; 443 | } 444 | .KWHmainRightName span{ 445 | color: #4b8bef; 446 | margin-right: 1rem; 447 | font-size: 2rem; 448 | } 449 | .ALmainCenter{ 450 | width: 90%; 451 | overflow: hidden; 452 | margin: 2rem auto 0; 453 | border: 1px solid #ececec; 454 | } 455 | .ALmainCenterTop{ 456 | background: #f8f9fb; 457 | padding: 1rem 0; 458 | border-bottom: 1px solid #ececec; 459 | overflow: hidden; 460 | } 461 | .ALmainCenterTop label{ 462 | font-weight: normal; 463 | margin-left: 2rem; 464 | color: #666; 465 | line-height: 2.6rem; 466 | margin-bottom: 0; 467 | } 468 | .ALmainCenterTop select{ 469 | color: #666; 470 | border: 1px solid #ececec; 471 | width: 120px; 472 | height: 2.4rem; 473 | } 474 | .ALmainCenterTop input,.ALexcelBtn input{ 475 | -webkit-appearance: none; 476 | -moz-appearance: none; 477 | appearance: none; 478 | border: 0; 479 | outline:none; 480 | -webkit-tap-highlight-color: rgba(0,0,0,0); 481 | -webkit-tap-highlight-color: transparent; 482 | width: 120px; 483 | height: 2.4rem; 484 | color: #666; 485 | background: #fff; 486 | border: 1px solid #ececec; 487 | } 488 | .ALexcelBtn{ 489 | padding: 1rem 3rem 1rem 0; 490 | overflow: hidden; 491 | } 492 | .ALmainCenterTop .ALsearch{ 493 | background: #4b8bef; 494 | color: #fff; 495 | margin-left: 3rem; 496 | } 497 | .ALMTmargin{ 498 | margin: 0 1rem; 499 | line-height: 2.6rem; 500 | } 501 | .ALmainCenterTop .ALaddBj{ 502 | background: #fff url(../img/ALdown.png) no-repeat right 6px center; 503 | } 504 | @media only screen and (max-width:1400px) { 505 | .clearP{ 506 | clear: both; 507 | height: 16px; 508 | display: block; 509 | } 510 | } 511 | /*APP登录次数 end*/ -------------------------------------------------------------------------------- /src/main/resources/static/js/laydate.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | @Name : layDate v1.1 日期控件 4 | @Author: 贤心 5 | @Date: 2014-06-25 6 | @QQ群:176047195 7 | @Site:http://sentsin.com/layui/laydate 8 | 9 | */ 10 | 11 | ;!function(a){var b={path:"",defSkin:"default",format:"YYYY-MM-DD",min:"1900-01-01 00:00:00",max:"2099-12-31 23:59:59",isv:!1},c={},d=document,e="createElement",f="getElementById",g="getElementsByTagName",h=["laydate_box","laydate_void","laydate_click","LayDateSkin","skins/","/laydate.css"];a.laydate=function(b){b=b||{};try{h.event=a.event?a.event:laydate.caller.arguments[0]}catch(d){}return c.run(b),laydate},laydate.v="1.1",c.getPath=function(){var a=document.scripts,c=a[a.length-1].src;return b.path?b.path:c.substring(0,c.lastIndexOf("/")+1)}(),c.use=function(a,b){var f=d[e]("link");f.type="text/css",f.rel="stylesheet",f.href=c.getPath+a+h[5],b&&(f.id=b),d[g]("head")[0].appendChild(f),f=null},c.trim=function(a){return a=a||"",a.replace(/^\s|\s$/g,"").replace(/\s+/g," ")},c.digit=function(a){return 10>a?"0"+(0|a):a},c.stopmp=function(b){return b=b||a.event,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0,this},c.each=function(a,b){for(var c=0,d=a.length;d>c&&b(c,a[c])!==!1;c++);},c.hasClass=function(a,b){return a=a||{},new RegExp("\\b"+b+"\\b").test(a.className)},c.addClass=function(a,b){return a=a||{},c.hasClass(a,b)||(a.className+=" "+b),a.className=c.trim(a.className),this},c.removeClass=function(a,b){if(a=a||{},c.hasClass(a,b)){var d=new RegExp("\\b"+b+"\\b");a.className=a.className.replace(d,"")}return this},c.removeCssAttr=function(a,b){var c=a.style;c.removeProperty?c.removeProperty(b):c.removeAttribute(b)},c.shde=function(a,b){a.style.display=b?"none":"block"},c.query=function(a){var e,b,h,i,j;return a=c.trim(a).split(" "),b=d[f](a[0].substr(1)),b?a[1]?/^\./.test(a[1])?(i=a[1].substr(1),j=new RegExp("\\b"+i+"\\b"),e=[],h=d.getElementsByClassName?b.getElementsByClassName(i):b[g]("*"),c.each(h,function(a,b){j.test(b.className)&&e.push(b)}),e[0]?e:""):(e=b[g](a[1]),e[0]?b[g](a[1]):""):b:void 0},c.on=function(b,d,e){return b.attachEvent?b.attachEvent("on"+d,function(){e.call(b,a.even)}):b.addEventListener(d,e,!1),c},c.stopMosup=function(a,b){"mouseup"!==a&&c.on(b,"mouseup",function(a){c.stopmp(a)})},c.run=function(a){var d,e,g,b=c.query,f=h.event;try{g=f.target||f.srcElement||{}}catch(i){g={}}if(d=a.elem?b(a.elem):g,f&&g.tagName){if(!d||d===c.elem)return;c.stopMosup(f.type,d),c.stopmp(f),c.view(d,a),c.reshow()}else e=a.event||"click",c.each((0|d.length)>0?d:[d],function(b,d){c.stopMosup(e,d),c.on(d,e,function(b){c.stopmp(b),d!==c.elem&&(c.view(d,a),c.reshow())})})},c.scroll=function(a){return a=a?"scrollLeft":"scrollTop",d.body[a]|d.documentElement[a]},c.winarea=function(a){return document.documentElement[a?"clientWidth":"clientHeight"]},c.isleap=function(a){return 0===a%4&&0!==a%100||0===a%400},c.checkVoid=function(a,b,d){var e=[];return a=0|a,b=0|b,d=0|d,ac.maxs[0]?e=["y",1]:a>=c.mins[0]&&a<=c.maxs[0]&&(a==c.mins[0]&&(bc.maxs[1]?e=["m",1]:b==c.maxs[1]&&d>c.maxs[2]&&(e=["d",1]))),e},c.timeVoid=function(a,b){if(c.ymd[1]+1==c.mins[1]&&c.ymd[2]==c.mins[2]){if(0===b&&ac.maxs[3])return 1;if(1===b&&a>c.maxs[4])return 1;if(2===b&&a>c.maxs[5])return 1}return a>(b?59:23)?1:void 0},c.check=function(){var a=c.options.format.replace(/YYYY|MM|DD|hh|mm|ss/g,"\\d+\\").replace(/\\$/g,""),b=new RegExp(a),d=c.elem[h.elemv],e=d.match(/\d+/g)||[],f=c.checkVoid(e[0],e[1],e[2]);if(""!==d.replace(/\s/g,"")){if(!b.test(d))return c.elem[h.elemv]="",c.msg("日期不符合格式,请重新选择。"),1;if(f[0])return c.elem[h.elemv]="",c.msg("日期不在有效期内,请重新选择。"),1;f.value=c.elem[h.elemv].match(b).join(),e=f.value.match(/\d+/g),e[1]<1?(e[1]=1,f.auto=1):e[1]>12?(e[1]=12,f.auto=1):e[1].length<2&&(f.auto=1),e[2]<1?(e[2]=1,f.auto=1):e[2]>c.months[(0|e[1])-1]?(e[2]=31,f.auto=1):e[2].length<2&&(f.auto=1),e.length>3&&(c.timeVoid(e[3],0)&&(f.auto=1),c.timeVoid(e[4],1)&&(f.auto=1),c.timeVoid(e[5],2)&&(f.auto=1)),f.auto?c.creation([e[0],0|e[1],0|e[2]],1):f.value!==c.elem[h.elemv]&&(c.elem[h.elemv]=f.value)}},c.months=[31,null,31,30,31,30,31,31,30,31,30,31],c.viewDate=function(a,b,d){var f=(c.query,{}),g=new Date;a<(0|c.mins[0])&&(a=0|c.mins[0]),a>(0|c.maxs[0])&&(a=0|c.maxs[0]),g.setFullYear(a,b,d),f.ymd=[g.getFullYear(),g.getMonth(),g.getDate()],c.months[1]=c.isleap(f.ymd[0])?29:28,g.setFullYear(f.ymd[0],f.ymd[1],1),f.FDay=g.getDay(),f.PDay=c.months[0===b?11:b-1]-f.FDay+1,f.NDay=1,c.each(h.tds,function(a,b){var g,d=f.ymd[0],e=f.ymd[1]+1;b.className="",a=f.FDay&&a'+a+"年":'
  • '+(a-7+b)+"年
  • "}),b("#laydate_ys").innerHTML=d,c.each(b("#laydate_ys li"),function(a,b){"y"===c.checkVoid(b.getAttribute("y"))[0]?c.addClass(b,h[1]):c.on(b,"click",function(a){c.stopmp(a).reshow(),c.viewDate(0|this.getAttribute("y"),c.ymd[1],c.ymd[2])})})},c.initDate=function(){var d=(c.query,new Date),e=c.elem[h.elemv].match(/\d+/g)||[];e.length<3&&(e=c.options.start.match(/\d+/g)||[],e.length<3&&(e=[d.getFullYear(),d.getMonth()+1,d.getDate()])),c.inymd=e,c.viewDate(e[0],e[1]-1,e[2])},c.iswrite=function(){var a=c.query,b={time:a("#laydate_hms")};c.shde(b.time,!c.options.istime),c.shde(h.oclear,!("isclear"in c.options?c.options.isclear:1)),c.shde(h.otoday,!("istoday"in c.options?c.options.istoday:1)),c.shde(h.ok,!("issure"in c.options?c.options.issure:1))},c.orien=function(a,b){var d,e=c.elem.getBoundingClientRect();a.style.left=e.left+(b?0:c.scroll(1))+"px",d=e.bottom+a.offsetHeight/1.5<=c.winarea()?e.bottom-1:e.top>a.offsetHeight/1.5?e.top-a.offsetHeight+1:c.winarea()-a.offsetHeight,a.style.top=d+(b?0:c.scroll())+"px"},c.follow=function(a){c.options.fixed?(a.style.position="fixed",c.orien(a,1)):(a.style.position="absolute",c.orien(a))},c.viewtb=function(){var a,b=[],f=["日","一","二","三","四","五","六"],h={},i=d[e]("table"),j=d[e]("thead");return j.appendChild(d[e]("tr")),h.creath=function(a){var b=d[e]("th");b.innerHTML=f[a],j[g]("tr")[0].appendChild(b),b=null},c.each(new Array(6),function(d){b.push([]),a=i.insertRow(0),c.each(new Array(7),function(c){b[d][c]=0,0===d&&h.creath(c),a.insertCell(c)})}),i.insertBefore(j,i.children[0]),i.id=i.className="laydate_table",a=b=null,i.outerHTML.toLowerCase()}(),c.view=function(a,f){var i,g=c.query,j={};f=f||a,c.elem=a,c.options=f,c.options.format||(c.options.format=b.format),c.options.start=c.options.start||"",c.mm=j.mm=[c.options.min||b.min,c.options.max||b.max],c.mins=j.mm[0].match(/\d+/g),c.maxs=j.mm[1].match(/\d+/g),h.elemv=/textarea|input/.test(c.elem.tagName.toLocaleLowerCase())?"value":"innerHTML",c.box?c.shde(c.box):(i=d[e]("div"),i.id=h[0],i.className=h[0],i.style.cssText="position: absolute;",i.setAttribute("name","laydate-v"+laydate.v),i.innerHTML=j.html='
      '+function(){var a="";return c.each(new Array(12),function(b){a+=''+c.digit(b+1)+"月"}),a}()+"
      "+"
      "+"
      "+c.viewtb+'
      '+'
        '+'
      • 时间
      • '+"
      • :
      • "+"
      • :
      • "+"
      • "+"
      "+'
      '+'
      '+'清空'+'今天'+'确认'+"
      "+(b.isv?'laydate-v'+laydate.v+"":"")+"
      ",d.body.appendChild(i),c.box=g("#"+h[0]),c.events(),i=null),c.follow(c.box),f.zIndex?c.box.style.zIndex=f.zIndex:c.removeCssAttr(c.box,"z-index"),c.stopMosup("click",c.box),c.initDate(),c.iswrite(),c.check()},c.reshow=function(){return c.each(c.query("#"+h[0]+" .laydate_show"),function(a,b){c.removeClass(b,"laydate_show")}),this},c.close=function(){c.reshow(),c.shde(c.query("#"+h[0]),1),c.elem=null},c.parse=function(a,d,e){return a=a.concat(d),e=e||(c.options?c.options.format:b.format),e.replace(/YYYY|MM|DD|hh|mm|ss/g,function(){return a.index=0|++a.index,c.digit(a[a.index])})},c.creation=function(a,b){var e=(c.query,c.hmsin),f=c.parse(a,[e[0].value,e[1].value,e[2].value]);c.elem[h.elemv]=f,b||(c.close(),"function"==typeof c.options.choose&&c.options.choose(f))},c.events=function(){var b=c.query,e={box:"#"+h[0]};c.addClass(d.body,"laydate_body"),h.tds=b("#laydate_table td"),h.mms=b("#laydate_ms span"),h.year=b("#laydate_y"),h.month=b("#laydate_m"),c.each(b(e.box+" .laydate_ym"),function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),c.addClass(this[g]("div")[0],"laydate_show"),a||(e.YY=parseInt(h.year.value),c.viewYears(e.YY))})}),c.on(b(e.box),"click",function(){c.reshow()}),e.tabYear=function(a){0===a?c.ymd[0]--:1===a?c.ymd[0]++:2===a?e.YY-=14:e.YY+=14,2>a?(c.viewDate(c.ymd[0],c.ymd[1],c.ymd[2]),c.reshow()):c.viewYears(e.YY)},c.each(b("#laydate_YY .laydate_tab"),function(a,b){c.on(b,"click",function(b){c.stopmp(b),e.tabYear(a)})}),e.tabMonth=function(a){a?(c.ymd[1]++,12===c.ymd[1]&&(c.ymd[0]++,c.ymd[1]=0)):(c.ymd[1]--,-1===c.ymd[1]&&(c.ymd[0]--,c.ymd[1]=11)),c.viewDate(c.ymd[0],c.ymd[1],c.ymd[2])},c.each(b("#laydate_MM .laydate_tab"),function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),e.tabMonth(a)})}),c.each(b("#laydate_ms span"),function(a,b){c.on(b,"click",function(a){c.stopmp(a).reshow(),c.hasClass(this,h[1])||c.viewDate(c.ymd[0],0|this.getAttribute("m"),c.ymd[2])})}),c.each(b("#laydate_table td"),function(a,b){c.on(b,"click",function(a){c.hasClass(this,h[1])||(c.stopmp(a),c.creation([0|this.getAttribute("y"),0|this.getAttribute("m"),0|this.getAttribute("d")]))})}),h.oclear=b("#laydate_clear"),c.on(h.oclear,"click",function(){c.elem[h.elemv]="",c.close()}),h.otoday=b("#laydate_today"),c.on(h.otoday,"click",function(){c.elem[h.elemv]=laydate.now(0,c.options.format),c.close()}),h.ok=b("#laydate_ok"),c.on(h.ok,"click",function(){c.valid&&c.creation([c.ymd[0],c.ymd[1]+1,c.ymd[2]])}),e.times=b("#laydate_time"),c.hmsin=e.hmsin=b("#laydate_hms input"),e.hmss=["小时","分钟","秒数"],e.hmsarr=[],c.msg=function(a,d){var f='
      '+(d||"提示")+"×
      ";"string"==typeof a?(f+="

      "+a+"

      ",c.shde(b("#"+h[0])),c.removeClass(e.times,"laydate_time1").addClass(e.times,"laydate_msg")):(e.hmsarr[a]?f=e.hmsarr[a]:(f+='
      ',c.each(new Array(0===a?24:60),function(a){f+=""+a+""}),f+="
      ",e.hmsarr[a]=f),c.removeClass(e.times,"laydate_msg"),c[0===a?"removeClass":"addClass"](e.times,"laydate_time1")),c.addClass(e.times,"laydate_show"),e.times.innerHTML=f},e.hmson=function(a,d){var e=b("#laydate_hmsno span"),f=c.valid?null:1;c.each(e,function(b,e){f?c.addClass(e,h[1]):c.timeVoid(b,d)?c.addClass(e,h[1]):c.on(e,"click",function(){c.hasClass(this,h[1])||(a.value=c.digit(0|this.innerHTML))})}),c.addClass(e[0|a.value],"laydate_click")},c.each(e.hmsin,function(a,b){c.on(b,"click",function(b){c.stopmp(b).reshow(),c.msg(a,e.hmss[a]),e.hmson(this,a)})}),c.on(d,"mouseup",function(){var a=b("#"+h[0]);a&&"none"!==a.style.display&&(c.check()||c.close())}).on(d,"keydown",function(b){b=b||a.event;var d=b.keyCode;13===d&&c.creation([c.ymd[0],c.ymd[1]+1,c.ymd[2]])})},c.init=function(){c.use("need"),c.use(h[4]+b.defSkin,h[3]),c.skinLink=c.query("#"+h[3])}(),laydate.reset=function(){c.box&&c.elem&&c.follow(c.box)},laydate.now=function(a,b){var d=new Date(0|a?function(a){return 864e5>a?+new Date+864e5*a:a}(parseInt(a)):+new Date);return c.parse([d.getFullYear(),d.getMonth()+1,d.getDate()],[d.getHours(),d.getMinutes(),d.getSeconds()],b)},laydate.skin=function(a){c.skinLink.href=c.getPath+h[4]+a+h[5]}}(window); -------------------------------------------------------------------------------- /src/main/resources/static/js/bootbox.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootbox.js [v4.4.0] 3 | * 4 | * http://bootboxjs.com/license.txt 5 | */ 6 | 7 | // @see https://github.com/makeusabrew/bootbox/issues/180 8 | // @see https://github.com/makeusabrew/bootbox/issues/186 9 | (function (root, factory) { 10 | 11 | "use strict"; 12 | if (typeof define === "function" && define.amd) { 13 | // AMD. Register as an anonymous module. 14 | define(["jquery"], factory); 15 | } else if (typeof exports === "object") { 16 | // Node. Does not work with strict CommonJS, but 17 | // only CommonJS-like environments that support module.exports, 18 | // like Node. 19 | module.exports = factory(require("jquery")); 20 | } else { 21 | // Browser globals (root is window) 22 | root.bootbox = factory(root.jQuery); 23 | } 24 | 25 | }(this, function init($, undefined) { 26 | 27 | "use strict"; 28 | 29 | // the base DOM structure needed to create a modal 30 | var templates = { 31 | dialog: 32 | "", 39 | header: 40 | "", 43 | footer: 44 | "", 45 | closeButton: 46 | "", 47 | form: 48 | "
      ", 49 | inputs: { 50 | text: 51 | "", 52 | textarea: 53 | "", 54 | email: 55 | "", 56 | select: 57 | "", 58 | checkbox: 59 | "
      ", 60 | date: 61 | "", 62 | time: 63 | "", 64 | number: 65 | "", 66 | password: 67 | "" 68 | } 69 | }; 70 | 71 | var defaults = { 72 | // default language 73 | locale: "zh_CN", 74 | // show backdrop or not. Default to static so user has to interact with dialog 75 | backdrop: "static", 76 | // animate the modal in/out 77 | animate: true, 78 | // additional class string applied to the top level dialog 79 | className: null, 80 | // whether or not to include a close button 81 | closeButton: true, 82 | // show the dialog immediately by default 83 | show: true, 84 | // dialog container 85 | container: "body" 86 | }; 87 | 88 | // our public object; augmented after our private API 89 | var exports = {}; 90 | 91 | /** 92 | * @private 93 | */ 94 | function _t(key) { 95 | var locale = locales[defaults.locale]; 96 | return locale ? locale[key] : locales.en[key]; 97 | } 98 | 99 | function processCallback(e, dialog, callback) { 100 | e.stopPropagation(); 101 | e.preventDefault(); 102 | 103 | // by default we assume a callback will get rid of the dialog, 104 | // although it is given the opportunity to override this 105 | 106 | // so, if the callback can be invoked and it *explicitly returns false* 107 | // then we'll set a flag to keep the dialog active... 108 | var preserveDialog = $.isFunction(callback) && callback.call(dialog, e) === false; 109 | 110 | // ... otherwise we'll bin it 111 | if (!preserveDialog) { 112 | dialog.modal("hide"); 113 | } 114 | } 115 | 116 | function getKeyLength(obj) { 117 | // @TODO defer to Object.keys(x).length if available? 118 | var k, t = 0; 119 | for (k in obj) { 120 | t ++; 121 | } 122 | return t; 123 | } 124 | 125 | function each(collection, iterator) { 126 | var index = 0; 127 | $.each(collection, function(key, value) { 128 | iterator(key, value, index++); 129 | }); 130 | } 131 | 132 | function sanitize(options) { 133 | var buttons; 134 | var total; 135 | 136 | if (typeof options !== "object") { 137 | throw new Error("Please supply an object of options"); 138 | } 139 | 140 | if (!options.message) { 141 | throw new Error("Please specify a message"); 142 | } 143 | 144 | // make sure any supplied options take precedence over defaults 145 | options = $.extend({}, defaults, options); 146 | 147 | if (!options.buttons) { 148 | options.buttons = {}; 149 | } 150 | 151 | buttons = options.buttons; 152 | 153 | total = getKeyLength(buttons); 154 | 155 | each(buttons, function(key, button, index) { 156 | 157 | if ($.isFunction(button)) { 158 | // short form, assume value is our callback. Since button 159 | // isn't an object it isn't a reference either so re-assign it 160 | button = buttons[key] = { 161 | callback: button 162 | }; 163 | } 164 | 165 | // before any further checks make sure by now button is the correct type 166 | if ($.type(button) !== "object") { 167 | throw new Error("button with key " + key + " must be an object"); 168 | } 169 | 170 | if (!button.label) { 171 | // the lack of an explicit label means we'll assume the key is good enough 172 | button.label = key; 173 | } 174 | 175 | if (!button.className) { 176 | if (total <= 2 && index === total-1) { 177 | // always add a primary to the main option in a two-button dialog 178 | button.className = "btn-primary"; 179 | } else { 180 | button.className = "btn-default"; 181 | } 182 | } 183 | }); 184 | 185 | return options; 186 | } 187 | 188 | /** 189 | * map a flexible set of arguments into a single returned object 190 | * if args.length is already one just return it, otherwise 191 | * use the properties argument to map the unnamed args to 192 | * object properties 193 | * so in the latter case: 194 | * mapArguments(["foo", $.noop], ["message", "callback"]) 195 | * -> { message: "foo", callback: $.noop } 196 | */ 197 | function mapArguments(args, properties) { 198 | var argn = args.length; 199 | var options = {}; 200 | 201 | if (argn < 1 || argn > 2) { 202 | throw new Error("Invalid argument length"); 203 | } 204 | 205 | if (argn === 2 || typeof args[0] === "string") { 206 | options[properties[0]] = args[0]; 207 | options[properties[1]] = args[1]; 208 | } else { 209 | options = args[0]; 210 | } 211 | 212 | return options; 213 | } 214 | 215 | /** 216 | * merge a set of default dialog options with user supplied arguments 217 | */ 218 | function mergeArguments(defaults, args, properties) { 219 | return $.extend( 220 | // deep merge 221 | true, 222 | // ensure the target is an empty, unreferenced object 223 | {}, 224 | // the base options object for this type of dialog (often just buttons) 225 | defaults, 226 | // args could be an object or array; if it's an array properties will 227 | // map it to a proper options object 228 | mapArguments( 229 | args, 230 | properties 231 | ) 232 | ); 233 | } 234 | 235 | /** 236 | * this entry-level method makes heavy use of composition to take a simple 237 | * range of inputs and return valid options suitable for passing to bootbox.dialog 238 | */ 239 | function mergeDialogOptions(className, labels, properties, args) { 240 | // build up a base set of dialog properties 241 | var baseOptions = { 242 | className: "bootbox-" + className, 243 | buttons: createLabels.apply(null, labels) 244 | }; 245 | 246 | // ensure the buttons properties generated, *after* merging 247 | // with user args are still valid against the supplied labels 248 | return validateButtons( 249 | // merge the generated base properties with user supplied arguments 250 | mergeArguments( 251 | baseOptions, 252 | args, 253 | // if args.length > 1, properties specify how each arg maps to an object key 254 | properties 255 | ), 256 | labels 257 | ); 258 | } 259 | 260 | /** 261 | * from a given list of arguments return a suitable object of button labels 262 | * all this does is normalise the given labels and translate them where possible 263 | * e.g. "ok", "confirm" -> { ok: "OK, cancel: "Annuleren" } 264 | */ 265 | function createLabels() { 266 | var buttons = {}; 267 | 268 | for (var i = 0, j = arguments.length; i < j; i++) { 269 | var argument = arguments[i]; 270 | var key = argument.toLowerCase(); 271 | var value = argument.toUpperCase(); 272 | 273 | buttons[key] = { 274 | label: _t(value) 275 | }; 276 | } 277 | 278 | return buttons; 279 | } 280 | 281 | function validateButtons(options, buttons) { 282 | var allowedButtons = {}; 283 | each(buttons, function(key, value) { 284 | allowedButtons[value] = true; 285 | }); 286 | 287 | each(options.buttons, function(key) { 288 | if (allowedButtons[key] === undefined) { 289 | throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")"); 290 | } 291 | }); 292 | 293 | return options; 294 | } 295 | 296 | exports.alert = function() { 297 | var options; 298 | 299 | options = mergeDialogOptions("alert", ["ok"], ["message", "callback"], arguments); 300 | 301 | if (options.callback && !$.isFunction(options.callback)) { 302 | throw new Error("alert requires callback property to be a function when provided"); 303 | } 304 | 305 | /** 306 | * overrides 307 | */ 308 | options.buttons.ok.callback = options.onEscape = function() { 309 | if ($.isFunction(options.callback)) { 310 | return options.callback.call(this); 311 | } 312 | return true; 313 | }; 314 | 315 | return exports.dialog(options); 316 | }; 317 | 318 | exports.confirm = function() { 319 | var options; 320 | 321 | options = mergeDialogOptions("confirm", ["cancel", "confirm"], ["message", "callback"], arguments); 322 | 323 | /** 324 | * overrides; undo anything the user tried to set they shouldn't have 325 | */ 326 | options.buttons.cancel.callback = options.onEscape = function() { 327 | return options.callback.call(this, false); 328 | }; 329 | 330 | options.buttons.confirm.callback = function() { 331 | return options.callback.call(this, true); 332 | }; 333 | 334 | // confirm specific validation 335 | if (!$.isFunction(options.callback)) { 336 | throw new Error("confirm requires a callback"); 337 | } 338 | 339 | return exports.dialog(options); 340 | }; 341 | 342 | exports.prompt = function() { 343 | var options; 344 | var defaults; 345 | var dialog; 346 | var form; 347 | var input; 348 | var shouldShow; 349 | var inputOptions; 350 | 351 | // we have to create our form first otherwise 352 | // its value is undefined when gearing up our options 353 | // @TODO this could be solved by allowing message to 354 | // be a function instead... 355 | form = $(templates.form); 356 | 357 | // prompt defaults are more complex than others in that 358 | // users can override more defaults 359 | // @TODO I don't like that prompt has to do a lot of heavy 360 | // lifting which mergeDialogOptions can *almost* support already 361 | // just because of 'value' and 'inputType' - can we refactor? 362 | defaults = { 363 | className: "bootbox-prompt", 364 | buttons: createLabels("cancel", "confirm"), 365 | value: "", 366 | inputType: "text" 367 | }; 368 | 369 | options = validateButtons( 370 | mergeArguments(defaults, arguments, ["title", "callback"]), 371 | ["cancel", "confirm"] 372 | ); 373 | 374 | // capture the user's show value; we always set this to false before 375 | // spawning the dialog to give us a chance to attach some handlers to 376 | // it, but we need to make sure we respect a preference not to show it 377 | shouldShow = (options.show === undefined) ? true : options.show; 378 | 379 | /** 380 | * overrides; undo anything the user tried to set they shouldn't have 381 | */ 382 | options.message = form; 383 | 384 | options.buttons.cancel.callback = options.onEscape = function() { 385 | return options.callback.call(this, null); 386 | }; 387 | 388 | options.buttons.confirm.callback = function() { 389 | var value; 390 | 391 | switch (options.inputType) { 392 | case "text": 393 | case "textarea": 394 | case "email": 395 | case "select": 396 | case "date": 397 | case "time": 398 | case "number": 399 | case "password": 400 | value = input.val(); 401 | break; 402 | 403 | case "checkbox": 404 | var checkedItems = input.find("input:checked"); 405 | 406 | // we assume that checkboxes are always multiple, 407 | // hence we default to an empty array 408 | value = []; 409 | 410 | each(checkedItems, function(_, item) { 411 | value.push($(item).val()); 412 | }); 413 | break; 414 | } 415 | 416 | return options.callback.call(this, value); 417 | }; 418 | 419 | options.show = false; 420 | 421 | // prompt specific validation 422 | if (!options.title) { 423 | throw new Error("prompt requires a title"); 424 | } 425 | 426 | if (!$.isFunction(options.callback)) { 427 | throw new Error("prompt requires a callback"); 428 | } 429 | 430 | if (!templates.inputs[options.inputType]) { 431 | throw new Error("invalid prompt type"); 432 | } 433 | 434 | // create the input based on the supplied type 435 | input = $(templates.inputs[options.inputType]); 436 | 437 | switch (options.inputType) { 438 | case "text": 439 | case "textarea": 440 | case "email": 441 | case "date": 442 | case "time": 443 | case "number": 444 | case "password": 445 | input.val(options.value); 446 | break; 447 | 448 | case "select": 449 | var groups = {}; 450 | inputOptions = options.inputOptions || []; 451 | 452 | if (!$.isArray(inputOptions)) { 453 | throw new Error("Please pass an array of input options"); 454 | } 455 | 456 | if (!inputOptions.length) { 457 | throw new Error("prompt with select requires options"); 458 | } 459 | 460 | each(inputOptions, function(_, option) { 461 | 462 | // assume the element to attach to is the input... 463 | var elem = input; 464 | 465 | if (option.value === undefined || option.text === undefined) { 466 | throw new Error("given options in wrong format"); 467 | } 468 | 469 | // ... but override that element if this option sits in a group 470 | 471 | if (option.group) { 472 | // initialise group if necessary 473 | if (!groups[option.group]) { 474 | groups[option.group] = $("").attr("label", option.group); 475 | } 476 | 477 | elem = groups[option.group]; 478 | } 479 | 480 | elem.append(""); 481 | }); 482 | 483 | each(groups, function(_, group) { 484 | input.append(group); 485 | }); 486 | 487 | // safe to set a select's value as per a normal input 488 | input.val(options.value); 489 | break; 490 | 491 | case "checkbox": 492 | var values = $.isArray(options.value) ? options.value : [options.value]; 493 | inputOptions = options.inputOptions || []; 494 | 495 | if (!inputOptions.length) { 496 | throw new Error("prompt with checkbox requires options"); 497 | } 498 | 499 | if (!inputOptions[0].value || !inputOptions[0].text) { 500 | throw new Error("given options in wrong format"); 501 | } 502 | 503 | // checkboxes have to nest within a containing element, so 504 | // they break the rules a bit and we end up re-assigning 505 | // our 'input' element to this container instead 506 | input = $("
      "); 507 | 508 | each(inputOptions, function(_, option) { 509 | var checkbox = $(templates.inputs[options.inputType]); 510 | 511 | checkbox.find("input").attr("value", option.value); 512 | checkbox.find("label").append(option.text); 513 | 514 | // we've ensured values is an array so we can always iterate over it 515 | each(values, function(_, value) { 516 | if (value === option.value) { 517 | checkbox.find("input").prop("checked", true); 518 | } 519 | }); 520 | 521 | input.append(checkbox); 522 | }); 523 | break; 524 | } 525 | 526 | // @TODO provide an attributes option instead 527 | // and simply map that as keys: vals 528 | if (options.placeholder) { 529 | input.attr("placeholder", options.placeholder); 530 | } 531 | 532 | if (options.pattern) { 533 | input.attr("pattern", options.pattern); 534 | } 535 | 536 | if (options.maxlength) { 537 | input.attr("maxlength", options.maxlength); 538 | } 539 | 540 | // now place it in our form 541 | form.append(input); 542 | 543 | form.on("submit", function(e) { 544 | e.preventDefault(); 545 | // Fix for SammyJS (or similar JS routing library) hijacking the form post. 546 | e.stopPropagation(); 547 | // @TODO can we actually click *the* button object instead? 548 | // e.g. buttons.confirm.click() or similar 549 | dialog.find(".btn-primary").click(); 550 | }); 551 | 552 | dialog = exports.dialog(options); 553 | 554 | // clear the existing handler focusing the submit button... 555 | dialog.off("shown.bs.modal"); 556 | 557 | // ...and replace it with one focusing our input, if possible 558 | dialog.on("shown.bs.modal", function() { 559 | // need the closure here since input isn't 560 | // an object otherwise 561 | input.focus(); 562 | }); 563 | 564 | if (shouldShow === true) { 565 | dialog.modal("show"); 566 | } 567 | 568 | return dialog; 569 | }; 570 | 571 | exports.dialog = function(options) { 572 | options = sanitize(options); 573 | 574 | var dialog = $(templates.dialog); 575 | var innerDialog = dialog.find(".modal-dialog"); 576 | var body = dialog.find(".modal-body"); 577 | var buttons = options.buttons; 578 | var buttonStr = ""; 579 | var callbacks = { 580 | onEscape: options.onEscape 581 | }; 582 | 583 | if ($.fn.modal === undefined) { 584 | throw new Error( 585 | "$.fn.modal is not defined; please double check you have included " + 586 | "the Bootstrap JavaScript library. See http://getbootstrap.com/javascript/ " + 587 | "for more details." 588 | ); 589 | } 590 | 591 | each(buttons, function(key, button) { 592 | 593 | // @TODO I don't like this string appending to itself; bit dirty. Needs reworking 594 | // can we just build up button elements instead? slower but neater. Then button 595 | // can just become a template too 596 | buttonStr += ""; 597 | callbacks[key] = button.callback; 598 | }); 599 | 600 | body.find(".bootbox-body").html(options.message); 601 | 602 | if (options.animate === true) { 603 | dialog.addClass("fade"); 604 | } 605 | 606 | if (options.className) { 607 | dialog.addClass(options.className); 608 | } 609 | 610 | if (options.size === "large") { 611 | innerDialog.addClass("modal-lg"); 612 | } else if (options.size === "small") { 613 | innerDialog.addClass("modal-sm"); 614 | } 615 | 616 | if (options.title) { 617 | body.before(templates.header); 618 | } 619 | 620 | if (options.closeButton) { 621 | var closeButton = $(templates.closeButton); 622 | 623 | if (options.title) { 624 | dialog.find(".modal-header").prepend(closeButton); 625 | } else { 626 | closeButton.css("margin-top", "-10px").prependTo(body); 627 | } 628 | } 629 | 630 | if (options.title) { 631 | dialog.find(".modal-title").html(options.title); 632 | } 633 | 634 | if (buttonStr.length) { 635 | body.after(templates.footer); 636 | dialog.find(".modal-footer").html(buttonStr); 637 | } 638 | 639 | 640 | /** 641 | * Bootstrap event listeners; used handle extra 642 | * setup & teardown required after the underlying 643 | * modal has performed certain actions 644 | */ 645 | 646 | dialog.on("hidden.bs.modal", function(e) { 647 | // ensure we don't accidentally intercept hidden events triggered 648 | // by children of the current dialog. We shouldn't anymore now BS 649 | // namespaces its events; but still worth doing 650 | if (e.target === this) { 651 | dialog.remove(); 652 | } 653 | }); 654 | 655 | /* 656 | dialog.on("show.bs.modal", function() { 657 | // sadly this doesn't work; show is called *just* before 658 | // the backdrop is added so we'd need a setTimeout hack or 659 | // otherwise... leaving in as would be nice 660 | if (options.backdrop) { 661 | dialog.next(".modal-backdrop").addClass("bootbox-backdrop"); 662 | } 663 | }); 664 | */ 665 | 666 | dialog.on("shown.bs.modal", function() { 667 | dialog.find(".btn-primary:first").focus(); 668 | }); 669 | 670 | /** 671 | * Bootbox event listeners; experimental and may not last 672 | * just an attempt to decouple some behaviours from their 673 | * respective triggers 674 | */ 675 | 676 | if (options.backdrop !== "static") { 677 | // A boolean true/false according to the Bootstrap docs 678 | // should show a dialog the user can dismiss by clicking on 679 | // the background. 680 | // We always only ever pass static/false to the actual 681 | // $.modal function because with `true` we can't trap 682 | // this event (the .modal-backdrop swallows it) 683 | // However, we still want to sort of respect true 684 | // and invoke the escape mechanism instead 685 | dialog.on("click.dismiss.bs.modal", function(e) { 686 | // @NOTE: the target varies in >= 3.3.x releases since the modal backdrop 687 | // moved *inside* the outer dialog rather than *alongside* it 688 | if (dialog.children(".modal-backdrop").length) { 689 | e.currentTarget = dialog.children(".modal-backdrop").get(0); 690 | } 691 | 692 | if (e.target !== e.currentTarget) { 693 | return; 694 | } 695 | 696 | dialog.trigger("escape.close.bb"); 697 | }); 698 | } 699 | 700 | dialog.on("escape.close.bb", function(e) { 701 | if (callbacks.onEscape) { 702 | processCallback(e, dialog, callbacks.onEscape); 703 | } 704 | }); 705 | 706 | /** 707 | * Standard jQuery event listeners; used to handle user 708 | * interaction with our dialog 709 | */ 710 | 711 | dialog.on("click", ".modal-footer button", function(e) { 712 | var callbackKey = $(this).data("bb-handler"); 713 | 714 | processCallback(e, dialog, callbacks[callbackKey]); 715 | }); 716 | 717 | dialog.on("click", ".bootbox-close-button", function(e) { 718 | // onEscape might be falsy but that's fine; the fact is 719 | // if the user has managed to click the close button we 720 | // have to close the dialog, callback or not 721 | processCallback(e, dialog, callbacks.onEscape); 722 | }); 723 | 724 | dialog.on("keyup", function(e) { 725 | if (e.which === 27) { 726 | dialog.trigger("escape.close.bb"); 727 | } 728 | }); 729 | 730 | // the remainder of this method simply deals with adding our 731 | // dialogent to the DOM, augmenting it with Bootstrap's modal 732 | // functionality and then giving the resulting object back 733 | // to our caller 734 | 735 | $(options.container).append(dialog); 736 | 737 | dialog.modal({ 738 | backdrop: options.backdrop ? "static": false, 739 | keyboard: false, 740 | show: false 741 | }); 742 | 743 | if (options.show) { 744 | dialog.modal("show"); 745 | } 746 | 747 | // @TODO should we return the raw element here or should 748 | // we wrap it in an object on which we can expose some neater 749 | // methods, e.g. var d = bootbox.alert(); d.hide(); instead 750 | // of d.modal("hide"); 751 | 752 | /* 753 | function BBDialog(elem) { 754 | this.elem = elem; 755 | } 756 | 757 | BBDialog.prototype = { 758 | hide: function() { 759 | return this.elem.modal("hide"); 760 | }, 761 | show: function() { 762 | return this.elem.modal("show"); 763 | } 764 | }; 765 | */ 766 | 767 | return dialog; 768 | 769 | }; 770 | 771 | exports.setDefaults = function() { 772 | var values = {}; 773 | 774 | if (arguments.length === 2) { 775 | // allow passing of single key/value... 776 | values[arguments[0]] = arguments[1]; 777 | } else { 778 | // ... and as an object too 779 | values = arguments[0]; 780 | } 781 | 782 | $.extend(defaults, values); 783 | }; 784 | 785 | exports.hideAll = function() { 786 | $(".bootbox").modal("hide"); 787 | 788 | return exports; 789 | }; 790 | 791 | 792 | /** 793 | * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are 794 | * unlikely to be required. If this gets too large it can be split out into separate JS files. 795 | */ 796 | var locales = { 797 | bg_BG : { 798 | OK : "Ок", 799 | CANCEL : "Отказ", 800 | CONFIRM : "Потвърждавам" 801 | }, 802 | br : { 803 | OK : "OK", 804 | CANCEL : "Cancelar", 805 | CONFIRM : "Sim" 806 | }, 807 | cs : { 808 | OK : "OK", 809 | CANCEL : "Zrušit", 810 | CONFIRM : "Potvrdit" 811 | }, 812 | da : { 813 | OK : "OK", 814 | CANCEL : "Annuller", 815 | CONFIRM : "Accepter" 816 | }, 817 | de : { 818 | OK : "OK", 819 | CANCEL : "Abbrechen", 820 | CONFIRM : "Akzeptieren" 821 | }, 822 | el : { 823 | OK : "Εντάξει", 824 | CANCEL : "Ακύρωση", 825 | CONFIRM : "Επιβεβαίωση" 826 | }, 827 | en : { 828 | OK : "OK", 829 | CANCEL : "Cancel", 830 | CONFIRM : "OK" 831 | }, 832 | es : { 833 | OK : "OK", 834 | CANCEL : "Cancelar", 835 | CONFIRM : "Aceptar" 836 | }, 837 | et : { 838 | OK : "OK", 839 | CANCEL : "Katkesta", 840 | CONFIRM : "OK" 841 | }, 842 | fa : { 843 | OK : "قبول", 844 | CANCEL : "لغو", 845 | CONFIRM : "تایید" 846 | }, 847 | fi : { 848 | OK : "OK", 849 | CANCEL : "Peruuta", 850 | CONFIRM : "OK" 851 | }, 852 | fr : { 853 | OK : "OK", 854 | CANCEL : "Annuler", 855 | CONFIRM : "D'accord" 856 | }, 857 | he : { 858 | OK : "אישור", 859 | CANCEL : "ביטול", 860 | CONFIRM : "אישור" 861 | }, 862 | hu : { 863 | OK : "OK", 864 | CANCEL : "Mégsem", 865 | CONFIRM : "Megerősít" 866 | }, 867 | hr : { 868 | OK : "OK", 869 | CANCEL : "Odustani", 870 | CONFIRM : "Potvrdi" 871 | }, 872 | id : { 873 | OK : "OK", 874 | CANCEL : "Batal", 875 | CONFIRM : "OK" 876 | }, 877 | it : { 878 | OK : "OK", 879 | CANCEL : "Annulla", 880 | CONFIRM : "Conferma" 881 | }, 882 | ja : { 883 | OK : "OK", 884 | CANCEL : "キャンセル", 885 | CONFIRM : "確認" 886 | }, 887 | lt : { 888 | OK : "Gerai", 889 | CANCEL : "Atšaukti", 890 | CONFIRM : "Patvirtinti" 891 | }, 892 | lv : { 893 | OK : "Labi", 894 | CANCEL : "Atcelt", 895 | CONFIRM : "Apstiprināt" 896 | }, 897 | nl : { 898 | OK : "OK", 899 | CANCEL : "Annuleren", 900 | CONFIRM : "Accepteren" 901 | }, 902 | no : { 903 | OK : "OK", 904 | CANCEL : "Avbryt", 905 | CONFIRM : "OK" 906 | }, 907 | pl : { 908 | OK : "OK", 909 | CANCEL : "Anuluj", 910 | CONFIRM : "Potwierdź" 911 | }, 912 | pt : { 913 | OK : "OK", 914 | CANCEL : "Cancelar", 915 | CONFIRM : "Confirmar" 916 | }, 917 | ru : { 918 | OK : "OK", 919 | CANCEL : "Отмена", 920 | CONFIRM : "Применить" 921 | }, 922 | sq : { 923 | OK : "OK", 924 | CANCEL : "Anulo", 925 | CONFIRM : "Prano" 926 | }, 927 | sv : { 928 | OK : "OK", 929 | CANCEL : "Avbryt", 930 | CONFIRM : "OK" 931 | }, 932 | th : { 933 | OK : "ตกลง", 934 | CANCEL : "ยกเลิก", 935 | CONFIRM : "ยืนยัน" 936 | }, 937 | tr : { 938 | OK : "Tamam", 939 | CANCEL : "İptal", 940 | CONFIRM : "Onayla" 941 | }, 942 | zh_CN : { 943 | OK : "OK", 944 | CANCEL : "取消", 945 | CONFIRM : "确认" 946 | }, 947 | zh_TW : { 948 | OK : "OK", 949 | CANCEL : "取消", 950 | CONFIRM : "確認" 951 | } 952 | }; 953 | 954 | exports.addLocale = function(name, values) { 955 | $.each(["OK", "CANCEL", "CONFIRM"], function(_, v) { 956 | if (!values[v]) { 957 | throw new Error("Please supply a translation for '" + v + "'"); 958 | } 959 | }); 960 | 961 | locales[name] = { 962 | OK: values.OK, 963 | CANCEL: values.CANCEL, 964 | CONFIRM: values.CONFIRM 965 | }; 966 | 967 | return exports; 968 | }; 969 | 970 | exports.removeLocale = function(name) { 971 | delete locales[name]; 972 | 973 | return exports; 974 | }; 975 | 976 | exports.setLocale = function(name) { 977 | return exports.setDefaults("locale", name); 978 | }; 979 | 980 | exports.init = function(_$) { 981 | return init(_$ || $); 982 | }; 983 | 984 | return exports; 985 | })); 986 | -------------------------------------------------------------------------------- /src/main/resources/static/js/bootstrap-table/bootstrap-table.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * bootstrap-table - v1.9.0 - 2015-09-30 3 | * https://github.com/wenzhixin/bootstrap-table 4 | * Copyright (c) 2015 zhixin wen 5 | * Licensed MIT License 6 | */ 7 | !function(a){"use strict";var b=null,c=function(a){var b=arguments,c=!0,d=1;return a=a.replace(/%s/g,function(){var a=b[d++];return"undefined"==typeof a?(c=!1,""):a}),c?a:""},d=function(b,c,d,e){var f="";return a.each(b,function(a,b){return b[c]===e?(f=b[d],!1):!0}),f},e=function(b,c){var d=-1;return a.each(b,function(a,b){return b.field===c?(d=a,!1):!0}),d},f=function(b){var c,d,e,f=0,g=[];for(c=0;cd;d++)g[c][d]=!1;for(c=0;ce;e++)g[c+e][k]=!0;for(e=0;j>e;e++)g[c][k+e]=!0}},g=function(){if(null===b){var c,d,e=a("

      ").addClass("fixed-table-scroll-inner"),f=a("

      ").addClass("fixed-table-scroll-outer");f.append(e),a("body").append(f),c=e[0].offsetWidth,f.css("overflow","scroll"),d=e[0].offsetWidth,c===d&&(d=f[0].clientWidth),f.remove(),b=c-d}return b},h=function(b,d,e,f){var g=d;if("string"==typeof d){var h=d.split(".");h.length>1?(g=window,a.each(h,function(a,b){g=g[b]})):g=window[d]}return"object"==typeof g?g:"function"==typeof g?g.apply(b,e):!g&&"string"==typeof d&&c.apply(this,[d].concat(e))?c.apply(this,[d].concat(e)):f},i=function(b,c,d){var e=Object.getOwnPropertyNames(b),f=Object.getOwnPropertyNames(c),g="";if(d&&e.length!==f.length)return!1;for(var h=0;h-1&&b[g]!==c[g])return!1;return!0},j=function(a){return"string"==typeof a?a.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'"):a},k=function(b){var c=0;return b.children().each(function(){c','
      ',"top"===this.options.paginationVAlign||"both"===this.options.paginationVAlign?'
      ':"",'
      ','
      ','
      ','
      ',this.options.formatLoadingMessage(),"
      ","
      ",'',"bottom"===this.options.paginationVAlign||"both"===this.options.paginationVAlign?'
      ':"","
      ","
      "].join("")),this.$container.insertAfter(this.$el),this.$tableContainer=this.$container.find(".fixed-table-container"),this.$tableHeader=this.$container.find(".fixed-table-header"),this.$tableBody=this.$container.find(".fixed-table-body"),this.$tableLoading=this.$container.find(".fixed-table-loading"),this.$tableFooter=this.$container.find(".fixed-table-footer"),this.$toolbar=this.$container.find(".fixed-table-toolbar"),this.$pagination=this.$container.find(".fixed-table-pagination"),this.$tableBody.append(this.$el),this.$container.after('
      '),this.$el.addClass(this.options.classes),this.options.striped&&this.$el.addClass("table-striped"),-1!==a.inArray("table-no-bordered",this.options.classes.split(" "))&&this.$tableContainer.addClass("table-no-bordered")},n.prototype.initTable=function(){var b=this,c=[],d=[];this.$header=this.$el.find("thead"),this.$header.length||(this.$header=a("").appendTo(this.$el)),this.$header.find("tr").each(function(){var b=[];a(this).find("th").each(function(){b.push(a.extend({},{title:a(this).html(),"class":a(this).attr("class"),titleTooltip:a(this).attr("title"),rowspan:a(this).attr("rowspan")?+a(this).attr("rowspan"):void 0,colspan:a(this).attr("colspan")?+a(this).attr("colspan"):void 0},a(this).data()))}),c.push(b)}),a.isArray(this.options.columns[0])||(this.options.columns=[this.options.columns]),this.options.columns=a.extend(!0,[],c,this.options.columns),this.columns=[],f(this.options.columns),a.each(this.options.columns,function(c,d){a.each(d,function(d,e){e=a.extend({},n.COLUMN_DEFAULTS,e),"undefined"!=typeof e.fieldIndex&&(b.columns[e.fieldIndex]=e),b.options.columns[c][d]=e})}),this.options.data.length||(this.$el.find("tbody tr").each(function(){var c={};c._id=a(this).attr("id"),c._class=a(this).attr("class"),c._data=l(a(this).data()),a(this).find("td").each(function(d){var e=b.columns[d].field;c[e]=a(this).html(),c["_"+e+"_id"]=a(this).attr("id"),c["_"+e+"_class"]=a(this).attr("class"),c["_"+e+"_rowspan"]=a(this).attr("rowspan"),c["_"+e+"_title"]=a(this).attr("title"),c["_"+e+"_data"]=l(a(this).data())}),d.push(c)}),this.options.data=d)},n.prototype.initHeader=function(){var b=this,d={},e=[];this.header={fields:[],styles:[],classes:[],formatters:[],events:[],sorters:[],sortNames:[],cellStyles:[],searchables:[]},a.each(this.options.columns,function(f,g){e.push(""),0==f&&!b.options.cardView&&b.options.detailView&&e.push(c('
      ',b.options.columns.length)),a.each(g,function(a,f){var g="",h="",i="",j="",k=c(' class="%s"',f["class"]),l=(b.options.sortOrder||f.order,"px"),m=f.width;if(void 0===f.width||b.options.cardView||"string"==typeof f.width&&-1!==f.width.indexOf("%")&&(l="%"),f.width&&"string"==typeof f.width&&(m=f.width.replace("%","").replace("px","")),h=c("text-align: %s; ",f.halign?f.halign:f.align),i=c("text-align: %s; ",f.align),j=c("vertical-align: %s; ",f.valign),j+=c("width: %s; ",!f.checkbox&&!f.radio||m?m?m+l:void 0:"36px"),"undefined"!=typeof f.fieldIndex){if(b.header.fields[f.fieldIndex]=f.field,b.header.styles[f.fieldIndex]=i+j,b.header.classes[f.fieldIndex]=k,b.header.formatters[f.fieldIndex]=f.formatter,b.header.events[f.fieldIndex]=f.events,b.header.sorters[f.fieldIndex]=f.sorter,b.header.sortNames[f.fieldIndex]=f.sortName,b.header.cellStyles[f.fieldIndex]=f.cellStyle,b.header.searchables[f.fieldIndex]=f.searchable,!f.visible)return;if(b.options.cardView&&!f.cardVisible)return;d[f.field]=f}e.push(""),e.push(c('
      ',b.options.sortable&&f.sortable?"sortable both":"")),g=f.title,f.checkbox&&(!b.options.singleSelect&&b.options.checkboxHeader&&(g=''),b.header.stateField=f.field),f.radio&&(g="",b.header.stateField=f.field,b.options.singleSelect=!0),e.push(g),e.push("
      "),e.push('
      '),e.push("
      "),e.push("")}),e.push("")}),this.$header.html(e.join("")),this.$header.find("th[data-field]").each(function(){a(this).data(d[a(this).data("field")])}),this.$container.off("click",".th-inner").on("click",".th-inner",function(c){b.options.sortable&&a(this).parent().data().sortable&&b.onSort(c)}),this.$header.children().children().off("keypress").on("keypress",function(c){if(b.options.sortable&&a(this).data().sortable){var d=c.keyCode||c.which;13==d&&b.onSort(c)}}),!this.options.showHeader||this.options.cardView?(this.$header.hide(),this.$tableHeader.hide(),this.$tableLoading.css("top",0)):(this.$header.show(),this.$tableHeader.show(),this.$tableLoading.css("top",this.$header.outerHeight()+1),this.getCaret()),this.$selectAll=this.$header.find('[name="btSelectAll"]'),this.$container.off("click",'[name="btSelectAll"]').on("click",'[name="btSelectAll"]',function(){var c=a(this).prop("checked");b[c?"checkAll":"uncheckAll"]()})},n.prototype.initFooter=function(){!this.options.showFooter||this.options.cardView?this.$tableFooter.hide():this.$tableFooter.show()},n.prototype.initData=function(a,b){this.data="append"===b?this.data.concat(a):"prepend"===b?[].concat(a).concat(this.data):a||this.options.data,this.options.data="append"===b?this.options.data.concat(a):"prepend"===b?[].concat(a).concat(this.options.data):this.data,"server"!==this.options.sidePagination&&this.initSort()},n.prototype.initSort=function(){var b=this,c=this.options.sortName,d="desc"===this.options.sortOrder?-1:1,e=a.inArray(this.options.sortName,this.header.fields);-1!==e&&this.data.sort(function(f,g){b.header.sortNames[e]&&(c=b.header.sortNames[e]);var i=m(f,c),j=m(g,c),k=h(b.header,b.header.sorters[e],[i,j]);return void 0!==k?d*k:((void 0===i||null===i)&&(i=""),(void 0===j||null===j)&&(j=""),a.isNumeric(i)&&a.isNumeric(j)?(i=parseFloat(i),j=parseFloat(j),j>i?-1*d:d):i===j?0:("string"!=typeof i&&(i=i.toString()),-1===i.localeCompare(j)?-1*d:d))})},n.prototype.onSort=function(b){var c="keypress"===b.type?a(b.currentTarget):a(b.currentTarget).parent(),d=this.$header.find("th").eq(c.index());return this.$header.add(this.$header_).find("span.order").remove(),this.options.sortName===c.data("field")?this.options.sortOrder="asc"===this.options.sortOrder?"desc":"asc":(this.options.sortName=c.data("field"),this.options.sortOrder="asc"===c.data("order")?"desc":"asc"),this.trigger("sort",this.options.sortName,this.options.sortOrder),c.add(d).data("order",this.options.sortOrder),this.getCaret(),"server"===this.options.sidePagination?void this.initServer(this.options.silentSort):(this.initSort(),void this.initBody())},n.prototype.initToolbar=function(){var b,d,f=this,g=[],i=0,j=0;this.$toolbar.html(""),("string"==typeof this.options.toolbar||"object"==typeof this.options.toolbar)&&a(c('
      ',this.options.toolbarAlign)).appendTo(this.$toolbar).append(a(this.options.toolbar)),g=[c('
      ',this.options.buttonsAlign,this.options.buttonsAlign)],"string"==typeof this.options.icons&&(this.options.icons=h(null,this.options.icons)),this.options.showPaginationSwitch&&g.push(c('"),this.options.showRefresh&&g.push(c('"),this.options.showToggle&&g.push(c('"),this.options.showColumns&&(g.push(c('
      ',this.options.formatColumns()),'",'","
      ")),g.push("
      "),(this.showToolbar||g.length>2)&&this.$toolbar.append(g.join("")),this.options.showPaginationSwitch&&this.$toolbar.find('button[name="paginationSwitch"]').off("click").on("click",a.proxy(this.togglePagination,this)),this.options.showRefresh&&this.$toolbar.find('button[name="refresh"]').off("click").on("click",a.proxy(this.refresh,this)),this.options.showToggle&&this.$toolbar.find('button[name="toggle"]').off("click").on("click",function(){f.toggleView()}),this.options.showColumns&&(b=this.$toolbar.find(".keep-open"),j<=this.options.minimumCountColumns&&b.find("input").prop("disabled",!0),b.find("li").off("click").on("click",function(a){a.stopImmediatePropagation()}),b.find("input").off("click").on("click",function(){var b=a(this);f.toggleColumn(e(f.columns,a(this).data("field")),b.prop("checked"),!1),f.trigger("column-switch",a(this).data("field"),b.prop("checked"))})),this.options.search&&(g=[],g.push('"),this.$toolbar.append(g.join("")),d=this.$toolbar.find(".search input"),d.off("keyup drop").on("keyup drop",function(a){clearTimeout(i),i=setTimeout(function(){f.onSearch(a)},f.options.searchTimeOut)}))},n.prototype.onSearch=function(b){var c=a.trim(a(b.currentTarget).val());this.options.trimOnSearch&&a(b.currentTarget).val()!==c&&a(b.currentTarget).val(c),c!==this.searchText&&(this.searchText=c,this.options.pageNumber=1,this.initSearch(),this.updatePagination(),this.trigger("search",c))},n.prototype.initSearch=function(){var b=this;if("server"!==this.options.sidePagination){var c=this.searchText&&this.searchText.toLowerCase(),d=a.isEmptyObject(this.filterColumns)?null:this.filterColumns;this.data=d?a.grep(this.options.data,function(a){for(var b in d)if(a[b]!==d[b])return!1;return!0}):this.options.data,this.data=c?a.grep(this.data,function(d,f){for(var g in d){g=a.isNumeric(g)?parseInt(g,10):g;var i=d[g],j=b.columns[e(b.columns,g)],k=a.inArray(g,b.header.fields);j&&j.searchFormatter&&(i=h(j,b.header.formatters[k],[i,d,f],i));var l=a.inArray(g,b.header.fields);if(-1!==l&&b.header.searchables[l]&&("string"==typeof i||"number"==typeof i))if(b.options.strictSearch){if((i+"").toLowerCase()===c)return!0}else if(-1!==(i+"").toLowerCase().indexOf(c))return!0}return!1}):this.data}},n.prototype.initPagination=function(){if(!this.options.pagination)return void this.$pagination.hide();this.$pagination.show();var b,d,e,f,g,h,i,j,k,l=this,m=[],n=!1,o=this.getData();if("server"!==this.options.sidePagination&&(this.options.totalRows=o.length),this.totalPages=0,this.options.totalRows){if(this.options.pageSize===this.options.formatAllRows())this.options.pageSize=this.options.totalRows,n=!0;else if(this.options.pageSize===this.options.totalRows){var p="string"==typeof this.options.pageList?this.options.pageList.replace("[","").replace("]","").replace(/ /g,"").toLowerCase().split(","):this.options.pageList;p.indexOf(this.options.formatAllRows().toLowerCase())>-1&&(n=!0)}this.totalPages=~~((this.options.totalRows-1)/this.options.pageSize)+1,this.options.totalPages=this.totalPages}this.totalPages>0&&this.options.pageNumber>this.totalPages&&(this.options.pageNumber=this.totalPages),this.pageFrom=(this.options.pageNumber-1)*this.options.pageSize+1,this.pageTo=this.options.pageNumber*this.options.pageSize,this.pageTo>this.options.totalRows&&(this.pageTo=this.options.totalRows),m.push('
      ','',this.options.formatShowingRows(this.pageFrom,this.pageTo,this.options.totalRows),""),m.push('');var q=[c('',"top"===this.options.paginationVAlign||"both"===this.options.paginationVAlign?"dropdown":"dropup"),'",'"),m.push(this.options.formatRecordsPerPage(q.join(""))),m.push(""),m.push("
      ",'"),this.$pagination.html(m.join("")),f=this.$pagination.find(".page-list a"),g=this.$pagination.find(".page-first"),h=this.$pagination.find(".page-pre"),i=this.$pagination.find(".page-next"),j=this.$pagination.find(".page-last"),k=this.$pagination.find(".page-number"),this.options.pageNumber<=1&&(g.addClass("disabled"),h.addClass("disabled")),this.options.pageNumber>=this.totalPages&&(i.addClass("disabled"),j.addClass("disabled")),this.options.smartDisplay&&(this.totalPages<=1&&this.$pagination.find("div.pagination").hide(),(r.length<2||this.options.totalRows<=r[0])&&this.$pagination.find("span.page-list").hide(),this.$pagination[this.getData().length?"show":"hide"]()),n&&(this.options.pageSize=this.options.formatAllRows()),f.off("click").on("click",a.proxy(this.onPageListChange,this)),g.off("click").on("click",a.proxy(this.onPageFirst,this)),h.off("click").on("click",a.proxy(this.onPagePre,this)),i.off("click").on("click",a.proxy(this.onPageNext,this)),j.off("click").on("click",a.proxy(this.onPageLast,this)),k.off("click").on("click",a.proxy(this.onPageNumber,this))},n.prototype.updatePagination=function(b){b&&a(b.currentTarget).hasClass("disabled")||(this.options.maintainSelected||this.resetRows(),this.initPagination(),"server"===this.options.sidePagination?this.initServer():this.initBody(),this.trigger("page-change",this.options.pageNumber,this.options.pageSize))},n.prototype.onPageListChange=function(b){var c=a(b.currentTarget);c.parent().addClass("active").siblings().removeClass("active"),this.options.pageSize=c.text().toUpperCase()===this.options.formatAllRows().toUpperCase()?this.options.formatAllRows():+c.text(),this.$toolbar.find(".page-size").text(this.options.pageSize),this.updatePagination(b)},n.prototype.onPageFirst=function(a){this.options.pageNumber=1,this.updatePagination(a)},n.prototype.onPagePre=function(a){this.options.pageNumber--,this.updatePagination(a)},n.prototype.onPageNext=function(a){this.options.pageNumber++,this.updatePagination(a)},n.prototype.onPageLast=function(a){this.options.pageNumber=this.totalPages,this.updatePagination(a)},n.prototype.onPageNumber=function(b){this.options.pageNumber!==+a(b.currentTarget).text()&&(this.options.pageNumber=+a(b.currentTarget).text(),this.updatePagination(b))},n.prototype.initBody=function(b){var f=this,g=[],i=this.getData();this.trigger("pre-body",i),this.$body=this.$el.find("tbody"),this.$body.length||(this.$body=a("").appendTo(this.$el)),this.options.pagination&&"server"!==this.options.sidePagination||(this.pageFrom=1,this.pageTo=i.length);for(var k=this.pageFrom-1;k"),this.options.cardView&&g.push(c('',this.header.fields.length)),!this.options.cardView&&this.options.detailView&&g.push("",'',c('',this.options.iconsPrefix,this.options.icons.detailOpen),"",""),a.each(this.header.fields,function(b,i){var j="",l=m(n,i),q="",r={},s="",t=f.header.classes[b],u="",v="",w="",x=f.columns[e(f.columns,i)];if(x.visible){if(o=c('style="%s"',p.concat(f.header.styles[b]).join("; ")),l=h(x,f.header.formatters[b],[l,n,k],l),n["_"+i+"_id"]&&(s=c(' id="%s"',n["_"+i+"_id"])),n["_"+i+"_class"]&&(t=c(' class="%s"',n["_"+i+"_class"])),n["_"+i+"_rowspan"]&&(v=c(' rowspan="%s"',n["_"+i+"_rowspan"])),n["_"+i+"_title"]&&(w=c(' title="%s"',n["_"+i+"_title"])),r=h(f.header,f.header.cellStyles[b],[l,n,k],r),r.classes&&(t=c(' class="%s"',r.classes)),r.css){var y=[];for(var z in r.css)y.push(z+": "+r.css[z]);o=c('style="%s"',y.concat(f.header.styles[b]).join("; "))}n["_"+i+"_data"]&&!a.isEmptyObject(n["_"+i+"_data"])&&a.each(n["_"+i+"_data"],function(a,b){"index"!==a&&(u+=c(' data-%s="%s"',a,b))}),x.checkbox||x.radio?(q=x.checkbox?"checkbox":q,q=x.radio?"radio":q,j=[f.options.cardView?'
      ':'',"",f.header.formatters[b]&&"string"==typeof l?l:"",f.options.cardView?"
      ":""].join(""),n[f.header.stateField]=l===!0||l&&l.checked):(l="undefined"==typeof l||null===l?f.options.undefinedText:l,j=f.options.cardView?['
      ',f.options.showHeader?c('%s',o,d(f.columns,"field","title",i)):"",c('%s',l),"
      "].join(""):[c("",s,t,o,u,v,w),l,""].join(""),f.options.cardView&&f.options.smartDisplay&&""===l&&(j="")),g.push(j)}}),this.options.cardView&&g.push(""),g.push("")}g.length||g.push('',c('%s',this.$header.find("th").length,this.options.formatNoMatches()),""),this.$body.html(g.join("")),b||this.scrollTo(0),this.$body.find("> tr[data-index] > td").off("click dblclick").on("click dblclick",function(b){var d=a(this),g=d.parent(),h=f.data[g.data("index")],i=d[0].cellIndex,j=f.header.fields[f.options.detailView&&!f.options.cardView?i-1:i],k=f.columns[e(f.columns,j)],l=m(h,j);if(!d.find(".detail-icon").length&&(f.trigger("click"===b.type?"click-cell":"dbl-click-cell",j,l,h,d),f.trigger("click"===b.type?"click-row":"dbl-click-row",h,g),"click"===b.type&&f.options.clickToSelect&&k.clickToSelect)){var n=g.find(c('[name="%s"]',f.options.selectItemName));n.length&&n[0].click()}}),this.$body.find("> tr[data-index] > td > .detail-icon").off("click").on("click",function(){var b=a(this),d=b.parent().parent(),e=d.data("index"),g=i[e];d.next().is("tr.detail-view")?(b.find("i").attr("class",c("%s %s",f.options.iconsPrefix,f.options.icons.detailOpen)),d.next().remove(),f.trigger("collapse-row",e,g)):(b.find("i").attr("class",c("%s %s",f.options.iconsPrefix,f.options.icons.detailClose)),d.after(c('%s',d.find("td").length,h(f.options,f.options.detailFormatter,[e,g],""))),f.trigger("expand-row",e,g,d.next().find("td"))),f.resetView()}),this.$selectItem=this.$body.find(c('[name="%s"]',this.options.selectItemName)),this.$selectItem.off("click").on("click",function(b){b.stopImmediatePropagation();var c=a(this),d=c.prop("checked"),e=f.data[c.data("index")];f.options.maintainSelected&&a(this).is(":radio")&&a.each(f.options.data,function(a,b){b[f.header.stateField]=!1}),e[f.header.stateField]=d,f.options.singleSelect&&(f.$selectItem.not(this).each(function(){f.data[a(this).data("index")][f.header.stateField]=!1}),f.$selectItem.filter(":checked").not(this).prop("checked",!1)),f.updateSelected(),f.trigger(d?"check":"uncheck",e,c)}),a.each(this.header.events,function(b,c){if(c){"string"==typeof c&&(c=h(null,c));var d=f.header.fields[b],e=a.inArray(d,f.getVisibleFields());f.options.detailView&&!f.options.cardView&&(e+=1);for(var g in c)f.$body.find("tr").each(function(){var b=a(this),h=b.find(f.options.cardView?".card-view":"td").eq(e),i=g.indexOf(" "),j=g.substring(0,i),k=g.substring(i+1),l=c[g];h.find(k).off(j).on(j,function(a){var c=b.data("index"),e=f.data[c],g=e[d];l.apply(this,[a,g,e,c])})})}}),this.updateSelected(),this.resetView(),this.trigger("post-body")},n.prototype.initServer=function(b,c){var d,e=this,f={},g={pageSize:this.options.pageSize===this.options.formatAllRows()?this.options.totalRows:this.options.pageSize,pageNumber:this.options.pageNumber,searchText:this.searchText,sortName:this.options.sortName,sortOrder:this.options.sortOrder};(this.options.url||this.options.ajax)&&("limit"===this.options.queryParamsType&&(g={search:g.searchText,sort:g.sortName,order:g.sortOrder},this.options.pagination&&(g.limit=this.options.pageSize===this.options.formatAllRows()?this.options.totalRows:this.options.pageSize,g.offset=this.options.pageSize===this.options.formatAllRows()?0:this.options.pageSize*(this.options.pageNumber-1))),a.isEmptyObject(this.filterColumnsPartial)||(g.filter=JSON.stringify(this.filterColumnsPartial,null)),f=h(this.options,this.options.queryParams,[g],f),a.extend(f,c||{}),f!==!1&&(b||this.$tableLoading.show(),d=a.extend({},h(null,this.options.ajaxOptions),{type:this.options.method,url:this.options.url,data:"application/json"===this.options.contentType&&"post"===this.options.method?JSON.stringify(f):f,cache:this.options.cache,contentType:this.options.contentType,dataType:this.options.dataType,success:function(a){a=h(e.options,e.options.responseHandler,[a],a),e.load(a),e.trigger("load-success",a)},error:function(a){e.trigger("load-error",a.status,a)},complete:function(){b||e.$tableLoading.hide()}}),this.options.ajax?h(this,this.options.ajax,[d],null):a.ajax(d)))},n.prototype.initSearchText=function(){if(this.options.search&&""!==this.options.searchText){var a=this.$toolbar.find(".search input");a.val(this.options.searchText),this.onSearch({ 8 | currentTarget:a})}},n.prototype.getCaret=function(){var b=this;a.each(this.$header.find("th"),function(c,d){a(d).find(".sortable").removeClass("desc asc").addClass(a(d).data("field")===b.options.sortName?b.options.sortOrder:"both")})},n.prototype.updateSelected=function(){var b=this.$selectItem.filter(":enabled").length&&this.$selectItem.filter(":enabled").length===this.$selectItem.filter(":enabled").filter(":checked").length;this.$selectAll.add(this.$selectAll_).prop("checked",b),this.$selectItem.each(function(){a(this).closest("tr")[a(this).prop("checked")?"addClass":"removeClass"]("selected")})},n.prototype.updateRows=function(){var b=this;this.$selectItem.each(function(){b.data[a(this).data("index")][b.header.stateField]=a(this).prop("checked")})},n.prototype.resetRows=function(){var b=this;a.each(this.data,function(a,c){b.$selectAll.prop("checked",!1),b.$selectItem.prop("checked",!1),b.header.stateField&&(c[b.header.stateField]=!1)})},n.prototype.trigger=function(b){var c=Array.prototype.slice.call(arguments,1);b+=".bs.table",this.options[n.EVENTS[b]].apply(this.options,c),this.$el.trigger(a.Event(b),c),this.options.onAll(b,c),this.$el.trigger(a.Event("all.bs.table"),[b,c])},n.prototype.resetHeader=function(){clearTimeout(this.timeoutId_),this.timeoutId_=setTimeout(a.proxy(this.fitHeader,this),this.$el.is(":hidden")?100:0)},n.prototype.fitHeader=function(){var b,d,e,f,h=this;if(h.$el.is(":hidden"))return void(h.timeoutId_=setTimeout(a.proxy(h.fitHeader,h),100));if(b=this.$tableBody.get(0),d=b.scrollWidth>b.clientWidth&&b.scrollHeight>b.clientHeight+this.$header.outerHeight()?g():0,this.$el.css("margin-top",-this.$header.outerHeight()),e=a(":focus"),e.length>0){var i=e.parents("th");if(i.length>0){var j=i.attr("data-field");if(void 0!==j){var k=this.$header.find("[data-field='"+j+"']");k.length>0&&k.find(":input").addClass("focus-temp")}}}this.$header_=this.$header.clone(!0,!0),this.$selectAll_=this.$header_.find('[name="btSelectAll"]'),this.$tableHeader.css({"margin-right":d}).find("table").css("width",this.$el.outerWidth()).html("").attr("class",this.$el.attr("class")).append(this.$header_),f=a(".focus-temp:visible:eq(0)"),f.length>0&&(f.focus(),this.$header.find(".focus-temp").removeClass("focus-temp")),this.$header.find("th[data-field]").each(function(){h.$header_.find(c('th[data-field="%s"]',a(this).data("field"))).data(a(this).data())});var l=this.getVisibleFields();this.$body.find("tr:first-child:not(.no-records-found) > *").each(function(b){var d=a(this),e=b;h.options.detailView&&!h.options.cardView&&(0===b&&h.$header_.find("th.detail").find(".fht-cell").width(d.innerWidth()),e=b-1),h.$header_.find(c('th[data-field="%s"]',l[e])).find(".fht-cell").width(d.innerWidth())}),this.$tableBody.off("scroll").on("scroll",function(){h.$tableHeader.scrollLeft(a(this).scrollLeft()),h.options.showFooter&&!h.options.cardView&&h.$tableFooter.scrollLeft(a(this).scrollLeft())}),h.trigger("post-header")},n.prototype.resetFooter=function(){var b=this,d=b.getData(),e=[];this.options.showFooter&&!this.options.cardView&&(!this.options.cardView&&this.options.detailView&&e.push('
       
      '),a.each(this.columns,function(a,f){var g="",i="",j=c(' class="%s"',f["class"]);f.visible&&(!b.options.cardView||f.cardVisible)&&(g=c("text-align: %s; ",f.falign?f.falign:f.align),i=c("vertical-align: %s; ",f.valign),e.push(""),e.push('
      '),e.push(h(f,f.footerFormatter,[d]," ")||" "),e.push("
      "),e.push('
      '),e.push(""),e.push(""))}),this.$tableFooter.find("tr").html(e.join("")),clearTimeout(this.timeoutFooter_),this.timeoutFooter_=setTimeout(a.proxy(this.fitFooter,this),this.$el.is(":hidden")?100:0))},n.prototype.fitFooter=function(){var b,c,d;return clearTimeout(this.timeoutFooter_),this.$el.is(":hidden")?void(this.timeoutFooter_=setTimeout(a.proxy(this.fitFooter,this),100)):(c=this.$el.css("width"),d=c>this.$tableBody.width()?g():0,this.$tableFooter.css({"margin-right":d}).find("table").css("width",c).attr("class",this.$el.attr("class")),b=this.$tableFooter.find("td"),void this.$body.find("tr:first-child:not(.no-records-found) > *").each(function(c){var d=a(this);b.eq(c).find(".fht-cell").width(d.innerWidth())}))},n.prototype.toggleColumn=function(a,b,d){if(-1!==a&&(this.columns[a].visible=b,this.initHeader(),this.initSearch(),this.initPagination(),this.initBody(),this.options.showColumns)){var e=this.$toolbar.find(".keep-open input").prop("disabled",!1);d&&e.filter(c('[value="%s"]',a)).prop("checked",b),e.filter(":checked").length<=this.options.minimumCountColumns&&e.filter(":checked").prop("disabled",!0)}},n.prototype.toggleRow=function(a,b,d){-1!==a&&this.$body.find("undefined"!=typeof a?c('tr[data-index="%s"]',a):c('tr[data-uniqueid="%s"]',b))[d?"show":"hide"]()},n.prototype.getVisibleFields=function(){var b=this,c=[];return a.each(this.header.fields,function(a,d){var f=b.columns[e(b.columns,d)];f.visible&&c.push(d)}),c},n.prototype.resetView=function(a){var b=0;if(a&&a.height&&(this.options.height=a.height),this.$selectAll.prop("checked",this.$selectItem.length>0&&this.$selectItem.length===this.$selectItem.filter(":checked").length),this.options.height){var c=k(this.$toolbar),d=k(this.$pagination),e=this.options.height-c-d;this.$tableContainer.css("height",e+"px")}return this.options.cardView?(this.$el.css("margin-top","0"),void this.$tableContainer.css("padding-bottom","0")):(this.options.showHeader&&this.options.height?(this.$tableHeader.show(),this.resetHeader(),b+=this.$header.outerHeight()):(this.$tableHeader.hide(),this.trigger("post-header")),this.options.showFooter&&(this.resetFooter(),this.options.height&&(b+=this.$tableFooter.outerHeight()+1)),this.getCaret(),this.$tableContainer.css("padding-bottom",b+"px"),void this.trigger("reset-view"))},n.prototype.getData=function(b){return!this.searchText&&a.isEmptyObject(this.filterColumns)&&a.isEmptyObject(this.filterColumnsPartial)?b?this.options.data.slice(this.pageFrom-1,this.pageTo):this.options.data:b?this.data.slice(this.pageFrom-1,this.pageTo):this.data},n.prototype.load=function(b){var c=!1;"server"===this.options.sidePagination?(this.options.totalRows=b.total,c=b.fixedScroll,b=b[this.options.dataField]):a.isArray(b)||(c=b.fixedScroll,b=b.data),this.initData(b),this.initSearch(),this.initPagination(),this.initBody(c)},n.prototype.append=function(a){this.initData(a,"append"),this.initSearch(),this.initPagination(),this.initBody(!0)},n.prototype.prepend=function(a){this.initData(a,"prepend"),this.initSearch(),this.initPagination(),this.initBody(!0)},n.prototype.remove=function(b){var c,d,e=this.options.data.length;if(b.hasOwnProperty("field")&&b.hasOwnProperty("values")){for(c=e-1;c>=0;c--)d=this.options.data[c],d.hasOwnProperty(b.field)&&-1!==a.inArray(d[b.field],b.values)&&this.options.data.splice(c,1);e!==this.options.data.length&&(this.initSearch(),this.initPagination(),this.initBody(!0))}},n.prototype.removeAll=function(){this.options.data.length>0&&(this.options.data.splice(0,this.options.data.length),this.initSearch(),this.initPagination(),this.initBody(!0))},n.prototype.getRowByUniqueId=function(a){var b,c,d=this.options.uniqueId,e=this.options.data.length,f=null;for(b=e-1;b>=0;b--)if(c=this.options.data[b],c.hasOwnProperty(d)&&("string"==typeof c[d]?a=a.toString():"number"==typeof c[d]&&(Number(c[d])===c[d]&&c[d]%1===0?a=parseInt(a):c[d]===Number(c[d])&&0!==c[d]&&(a=parseFloat(a))),c[d]===a)){f=c;break}return f},n.prototype.removeByUniqueId=function(a){var b=this.options.data.length,c=this.getRowByUniqueId(a);c&&this.options.data.splice(this.options.data.indexOf(c),1),b!==this.options.data.length&&(this.initSearch(),this.initPagination(),this.initBody(!0))},n.prototype.insertRow=function(a){a.hasOwnProperty("index")&&a.hasOwnProperty("row")&&(this.data.splice(a.index,0,a.row),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0))},n.prototype.updateRow=function(b){b.hasOwnProperty("index")&&b.hasOwnProperty("row")&&(a.extend(this.data[b.index],b.row),this.initSort(),this.initBody(!0))},n.prototype.showRow=function(a){a.hasOwnProperty("index")&&a.hasOwnProperty("uniqueId")&&this.toggleRow(a.index,a.uniqueId,!0)},n.prototype.hideRow=function(a){a.hasOwnProperty("index")&&a.hasOwnProperty("uniqueId")&&this.toggleRow(a.index,a.uniqueId,!1)},n.prototype.getRowsHidden=function(b){var c=a(this.$body[0]).children().filter(":hidden"),d=0;if(b)for(;df||0>g||f>=this.data.length)){for(c=f;f+h>c;c++)for(d=g;g+i>d;d++)j.eq(c).find("td").eq(d).hide();e.attr("rowspan",h).attr("colspan",i).show()}},n.prototype.updateCell=function(a){a.hasOwnProperty("index")&&a.hasOwnProperty("field")&&a.hasOwnProperty("value")&&(this.data[a.index][a.field]=a.value,this.initSort(),this.initBody(!0))},n.prototype.getOptions=function(){return this.options},n.prototype.getSelections=function(){var b=this;return a.grep(this.data,function(a){return a[b.header.stateField]})},n.prototype.getAllSelections=function(){var b=this;return a.grep(this.options.data,function(a){return a[b.header.stateField]})},n.prototype.checkAll=function(){this.checkAll_(!0)},n.prototype.uncheckAll=function(){this.checkAll_(!1)},n.prototype.checkAll_=function(a){var b;a||(b=this.getSelections()),this.$selectAll.add(this.$selectAll_).prop("checked",a),this.$selectItem.filter(":enabled").prop("checked",a),this.updateRows(),a&&(b=this.getSelections()),this.trigger(a?"check-all":"uncheck-all",b)},n.prototype.check=function(a){this.check_(!0,a)},n.prototype.uncheck=function(a){this.check_(!1,a)},n.prototype.check_=function(a,b){this.$selectItem.filter(c('[data-index="%s"]',b)).prop("checked",a),this.data[b][this.header.stateField]=a,this.updateSelected(),this.trigger(a?"check":"uncheck",this.data[b])},n.prototype.checkBy=function(a){this.checkBy_(!0,a)},n.prototype.uncheckBy=function(a){this.checkBy_(!1,a)},n.prototype.checkBy_=function(b,d){if(d.hasOwnProperty("field")&&d.hasOwnProperty("values")){var e=this,f=[];a.each(this.options.data,function(g,h){return h.hasOwnProperty(d.field)?void(-1!==a.inArray(h[d.field],d.values)&&(e.$selectItem.filter(":enabled").filter(c('[data-index="%s"]',g)).prop("checked",b),h[e.header.stateField]=b,f.push(h),e.trigger(b?"check":"uncheck",h))):!1}),this.updateSelected(),this.trigger(b?"check-some":"uncheck-some",f)}},n.prototype.destroy=function(){this.$el.insertBefore(this.$container),a(this.options.toolbar).insertBefore(this.$el),this.$container.next().remove(),this.$container.remove(),this.$el.html(this.$el_.html()).css("margin-top","0").attr("class",this.$el_.attr("class")||"")},n.prototype.showLoading=function(){this.$tableLoading.show()},n.prototype.hideLoading=function(){this.$tableLoading.hide()},n.prototype.togglePagination=function(){this.options.pagination=!this.options.pagination;var a=this.$toolbar.find('button[name="paginationSwitch"] i');this.options.pagination?a.attr("class",this.options.iconsPrefix+" "+this.options.icons.paginationSwitchDown):a.attr("class",this.options.iconsPrefix+" "+this.options.icons.paginationSwitchUp),this.updatePagination()},n.prototype.refresh=function(a){a&&a.url&&(this.options.url=a.url,this.options.pageNumber=1),this.initServer(a&&a.silent,a&&a.query)},n.prototype.resetWidth=function(){this.options.showHeader&&this.options.height&&this.fitHeader(),this.options.showFooter&&this.fitFooter()},n.prototype.showColumn=function(a){this.toggleColumn(e(this.columns,a),!0,!0)},n.prototype.hideColumn=function(a){this.toggleColumn(e(this.columns,a),!1,!0)},n.prototype.getHiddenColumns=function(){return a.grep(this.columns,function(a){return!a.visible})},n.prototype.filterBy=function(b){this.filterColumns=a.isEmptyObject(b)?{}:b,this.options.pageNumber=1,this.initSearch(),this.updatePagination()},n.prototype.scrollTo=function(a){return"string"==typeof a&&(a="bottom"===a?this.$tableBody[0].scrollHeight:0),"number"==typeof a&&this.$tableBody.scrollTop(a),"undefined"==typeof a?this.$tableBody.scrollTop():void 0},n.prototype.getScrollPosition=function(){return this.scrollTo()},n.prototype.selectPage=function(a){a>0&&a<=this.options.totalPages&&(this.options.pageNumber=a,this.updatePagination())},n.prototype.prevPage=function(){this.options.pageNumber>1&&(this.options.pageNumber--,this.updatePagination())},n.prototype.nextPage=function(){this.options.pageNumber tr[data-index="%s"]',b));d.next().is("tr.detail-view")===(a?!1:!0)&&d.find("> td > .detail-icon").click()},n.prototype.expandRow=function(a){this.expandRow_(!0,a)},n.prototype.collapseRow=function(a){this.expandRow_(!1,a)},n.prototype.expandAllRows=function(b){if(b){var d=this.$body.find(c('> tr[data-index="%s"]',0)),e=this,f=null,g=!1,h=-1;if(d.next().is("tr.detail-view")?d.next().next().is("tr.detail-view")||(d.next().find(".detail-icon").click(),g=!0):(d.find("> td > .detail-icon").click(),g=!0),g)try{h=setInterval(function(){f=e.$body.find("tr.detail-view").last().find(".detail-icon"),f.length>0?f.click():clearInterval(h)},1)}catch(i){clearInterval(h)}}else for(var j=this.$body.children(),k=0;k