diff --git a/spug_web/package.json b/spug_web/package.json
index 7d320c0..a1ff101 100644
--- a/spug_web/package.json
+++ b/spug_web/package.json
@@ -11,6 +11,7 @@
"lodash": "^4.17.15",
"mobx": "^5.15.0",
"mobx-react": "^6.1.4",
+ "moment": "^2.24.0",
"react": "^16.11.0",
"react-ace": "^8.0.0",
"react-dom": "^16.11.0",
diff --git a/spug_web/src/menus.js b/spug_web/src/menus.js
index 48d0277..0eefd5b 100644
--- a/spug_web/src/menus.js
+++ b/spug_web/src/menus.js
@@ -7,6 +7,7 @@ export default [
{title: '模板管理', path: '/exec/template'},
]
},
+ {icon: 'schedule', title: '任务计划', path: '/schedule'},
{
icon: 'setting', title: '系统管理', child: [
{title: '账户管理', path: '/system/account'},
diff --git a/spug_web/src/pages/schedule/Form.js b/spug_web/src/pages/schedule/Form.js
new file mode 100644
index 0000000..5877dc1
--- /dev/null
+++ b/spug_web/src/pages/schedule/Form.js
@@ -0,0 +1,230 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Modal, Form, Input, Select, Col, Button, Steps, Tabs, InputNumber, DatePicker, Icon, message } from 'antd';
+import { SHEditor } from 'components';
+import http from 'libs/http';
+import store from './store';
+import hostStore from '../host/store';
+import styles from './index.module.css';
+import moment from 'moment';
+
+@observer
+class ComForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: false,
+ type: null,
+ page: 0,
+ args: {[store.record['trigger']]: store.record['trigger_args']},
+ command: store.record['command'],
+ }
+ }
+
+ componentDidMount() {
+ store.targets = store.record.id ? store.record['targets'] : [undefined];
+ if (hostStore.records.length === 0) {
+ hostStore.fetchRecords()
+ }
+ }
+
+ _parse_args = (trigger) => {
+ switch (trigger) {
+ case 'date':
+ return this.state.args['date'].format('YYYY-MM-DD HH:mm:ss');
+ default:
+ return this.state.args[trigger];
+ }
+ };
+
+ handleSubmit = () => {
+ const formData = this.props.form.getFieldsValue();
+ if (formData['trigger'] === 'date' && this.state.args['date'] <= moment()) {
+ return message.error('任务执行时间不能早于当前时间')
+ }
+ this.setState({loading: true});
+ formData['id'] = store.record.id;
+ formData['command'] = this.state.command;
+ formData['targets'] = store.targets.filter(x => x);
+ formData['trigger_args'] = this._parse_args(formData['trigger']);
+ http.post('/api/schedule/', formData)
+ .then(res => {
+ message.success('操作成功');
+ store.formVisible = false;
+ store.fetchRecords()
+ }, () => this.setState({loading: false}))
+ };
+
+ handleAddZone = () => {
+ Modal.confirm({
+ icon: 'exclamation-circle',
+ title: '添加任务类型',
+ content: this.addZoneForm,
+ onOk: () => {
+ if (this.state.type) {
+ store.types.push(this.state.type);
+ this.props.form.setFieldsValue({'type': this.state.type})
+ }
+ },
+ })
+ };
+
+ addZoneForm = (
+
+ this.setState({type: val.target.value})}/>
+
+
+ );
+
+ handleArgs = (type, value) => {
+ const args = Object.assign(this.state.args, {[type]: value});
+ this.setState({args})
+ };
+
+ verifyButtonStatus = () => {
+ const data = this.props.form.getFieldsValue();
+ const b1 = data['type'] && data['name'] && this.state.command;
+ const b2 = store.targets.filter(x => x).length > 0;
+ const b3 = this.state.args[data['trigger']];
+ return [b1, b2, b3];
+ };
+
+ render() {
+ const info = store.record;
+ const {getFieldDecorator} = this.props.form;
+ const {page, args, loading} = this.state;
+ const [b1, b2, b3] = this.verifyButtonStatus();
+ const itemLayout = {
+ labelCol: {span: 6},
+ wrapperCol: {span: 14}
+ };
+ const itemTailLayout = {
+ labelCol: {span: 6},
+ wrapperCol: {span: 14, offset: 6}
+ };
+ return (
+ store.formVisible = false}
+ footer={null}>
+
+
+
+
+
+
+ {page === 2 &&
+ }
+ {page === 0 &&
+ }
+ {page === 1 &&
+ }
+ {page !== 0 &&
+ }
+
+
+
+ )
+ }
+}
+
+export default Form.create()(ComForm)
\ No newline at end of file
diff --git a/spug_web/src/pages/schedule/Table.js b/spug_web/src/pages/schedule/Table.js
new file mode 100644
index 0000000..cacc5d8
--- /dev/null
+++ b/spug_web/src/pages/schedule/Table.js
@@ -0,0 +1,102 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Table, Divider, Modal, Tag, message } from 'antd';
+import ComForm from './Form';
+import http from 'libs/http';
+import store from './store';
+import { LinkButton } from "components";
+
+@observer
+class ComTable extends React.Component {
+ componentDidMount() {
+ store.fetchRecords()
+ }
+
+ colors = ['green', 'orange', 'red'];
+
+ columns = [{
+ title: '序号',
+ key: 'series',
+ render: (_, __, index) => index + 1,
+ width: 80,
+ }, {
+ title: '任务名称',
+ dataIndex: 'name',
+ }, {
+ title: '任务类型',
+ dataIndex: 'type',
+ }, {
+ title: '最新状态',
+ render: info => {
+ if (info.is_active) {
+ return {info['latest_status_alias']}
+ } else {
+ return 未激活
+ }
+ },
+ }, {
+ title: '最近时间',
+ dataIndex: 'latest_run_time',
+ }, {
+ title: '描述信息',
+ dataIndex: 'desc',
+ ellipsis: true
+ }, {
+ title: '操作',
+ render: info => (
+
+ this.handleActive(info)}>{info.is_active ? '禁用' : '激活'}
+
+ store.showForm(info)}>编辑
+
+ this.handleDelete(info)}>删除
+
+ )
+ }];
+
+ handleActive = (text) => {
+ Modal.confirm({
+ title: '删除确认',
+ content: `确定要${text.is_active ? '禁用' : '激活'}任务【${text['name']}】?`,
+ onOk: () => {
+ return http.patch('/api/schedule/', {id: text.id, is_active: !text.is_active})
+ .then(() => {
+ message.success('操作成功');
+ store.fetchRecords()
+ })
+ }
+ })
+ };
+
+ handleDelete = (text) => {
+ Modal.confirm({
+ title: '删除确认',
+ content: `确定要删除【${text['name']}】?`,
+ onOk: () => {
+ return http.delete('/api/schedule/', {params: {id: text.id}})
+ .then(() => {
+ message.success('删除成功');
+ store.fetchRecords()
+ })
+ }
+ })
+ };
+
+ render() {
+ let data = store.records;
+ if (store.f_name) {
+ data = data.filter(item => item['name'].toLowerCase().includes(store.f_name.toLowerCase()))
+ }
+ if (store.f_type) {
+ data = data.filter(item => item['type'].toLowerCase().includes(store.f_type.toLowerCase()))
+ }
+ return (
+
+
+ {store.formVisible && }
+
+ )
+ }
+}
+
+export default ComTable
\ No newline at end of file
diff --git a/spug_web/src/pages/schedule/index.js b/spug_web/src/pages/schedule/index.js
new file mode 100644
index 0000000..9c8c199
--- /dev/null
+++ b/spug_web/src/pages/schedule/index.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Card, Input, Select, Button } from 'antd';
+import { SearchForm } from 'components';
+import ComTable from './Table';
+import store from './store';
+
+export default observer(function () {
+ return (
+
+
+
+
+
+
+ store.f_name = e.target.value} placeholder="请输入"/>
+
+
+
+
+
+
+
+
+
+
+ )
+})
\ No newline at end of file
diff --git a/spug_web/src/pages/schedule/index.module.css b/spug_web/src/pages/schedule/index.module.css
new file mode 100644
index 0000000..6848873
--- /dev/null
+++ b/spug_web/src/pages/schedule/index.module.css
@@ -0,0 +1,10 @@
+.steps {
+ width: 520px;
+ margin: 0 auto 30px;
+}
+
+.delIcon {
+ font-size: 24px;
+ position: relative;
+ top: 4px
+}
\ No newline at end of file
diff --git a/spug_web/src/pages/schedule/routes.js b/spug_web/src/pages/schedule/routes.js
new file mode 100644
index 0000000..6e03f0b
--- /dev/null
+++ b/spug_web/src/pages/schedule/routes.js
@@ -0,0 +1,7 @@
+import { makeRoute } from "../../libs/router";
+import Index from './index';
+
+
+export default [
+ makeRoute('', Index),
+]
\ No newline at end of file
diff --git a/spug_web/src/pages/schedule/store.js b/spug_web/src/pages/schedule/store.js
new file mode 100644
index 0000000..28b865a
--- /dev/null
+++ b/spug_web/src/pages/schedule/store.js
@@ -0,0 +1,43 @@
+import { observable } from "mobx";
+import http from 'libs/http';
+
+class Store {
+ @observable records = [];
+ @observable types = [];
+ @observable record = {};
+ @observable targets = [undefined];
+ @observable isFetching = false;
+ @observable formVisible = false;
+
+ @observable f_name;
+ @observable f_type;
+
+ fetchRecords = () => {
+ this.isFetching = true;
+ http.get('/api/schedule/')
+ .then(({types, tasks}) => {
+ this.records = tasks;
+ this.types = types
+ })
+ .finally(() => this.isFetching = false)
+ };
+
+ showForm = (info = {}) => {
+ this.formVisible = true;
+ this.record = info
+ };
+
+ addTarget = () => {
+ this.targets.push(undefined)
+ };
+
+ editTarget = (index, v) => {
+ this.targets[index] = v
+ };
+
+ delTarget = (index) => {
+ this.targets.splice(index, 1)
+ }
+}
+
+export default new Store()
\ No newline at end of file
diff --git a/spug_web/src/routes.js b/spug_web/src/routes.js
index 1b5d76f..5b77924 100644
--- a/spug_web/src/routes.js
+++ b/spug_web/src/routes.js
@@ -4,6 +4,7 @@ import homeRoutes from './pages/home/routes';
import hostRoutes from './pages/host/routes';
import systemRoutes from './pages/system/routes';
import execRoutes from './pages/exec/routes';
+import scheduleRoutes from './pages/schedule/routes';
export default [
@@ -11,4 +12,5 @@ export default [
makeModuleRoute('/host', hostRoutes),
makeModuleRoute('/system', systemRoutes),
makeModuleRoute('/exec', execRoutes),
+ makeModuleRoute('/schedule', scheduleRoutes),
]
\ No newline at end of file