U web add exec template

pull/22/head
雷二猛 2019-11-21 15:33:46 +08:00
parent f2965dc86e
commit e58c66734a
9 changed files with 301 additions and 0 deletions

View File

@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"ace-builds": "^1.4.7",
"antd": "^3.25.0",
"axios": "^0.19.0",
"history": "^4.10.1",
@ -10,6 +11,7 @@
"mobx": "^5.15.0",
"mobx-react": "^6.1.4",
"react": "^16.11.0",
"react-ace": "^8.0.0",
"react-dom": "^16.11.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0"

View File

@ -1,6 +1,11 @@
export default [
{icon: 'desktop', title: '工作台', path: '/home'},
{icon: 'cloud-server', title: '主机管理', path: '/host'},
{
icon: 'deployment-unit', title: '批量执行', child: [
{title: '执行模板', path: '/exec/template'},
]
},
{
icon: 'setting', title: '系统管理', child: [
{title: '账户管理', path: '/system/account'},

View File

@ -0,0 +1,7 @@
import { makeRoute } from "../../libs/router";
import Template from './template';
export default [
makeRoute('/template', Template),
]

View File

@ -0,0 +1,116 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Modal, Form, Input, Select, Col, Button, message } from 'antd';
import Editor from 'react-ace';
import 'ace-builds/src-noconflict/ext-language_tools';
import 'ace-builds/src-noconflict/mode-sh';
import 'ace-builds/src-noconflict/theme-tomorrow';
import 'ace-builds/src-noconflict/snippets/sh';
import http from 'libs/http';
import store from './store';
@observer
class ComForm extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
type: null,
body: store.record['body'],
}
}
handleSubmit = () => {
this.setState({loading: true});
const formData = this.props.form.getFieldsValue();
formData['id'] = store.record.id;
formData['body'] = this.state.body;
http.post('/api/exec/template/', 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 = (
<Form>
<Form.Item required label="模板类型">
<Input onChange={val => this.setState({type: val.target.value})}/>
</Form.Item>
</Form>
);
render() {
const info = store.record;
const {getFieldDecorator} = this.props.form;
const itemLayout = {
labelCol: {span: 6},
wrapperCol: {span: 14}
};
return (
<Modal
visible
width={800}
maskClosable={false}
title={store.record.id ? '编辑模板' : '新建模板'}
onCancel={() => store.formVisible = false}
confirmLoading={this.state.loading}
onOk={this.handleSubmit}>
<Form>
<Form.Item {...itemLayout} required label="模板类型">
<Col span={16}>
{getFieldDecorator('type', {initialValue: info['type']})(
<Select placeholder="请选择模板类型">
{store.types.map(item => (
<Select.Option value={item} key={item}>{item}</Select.Option>
))}
</Select>
)}
</Col>
<Col span={6} offset={2}>
<Button type="link" onClick={this.handleAddZone}>添加类型</Button>
</Col>
</Form.Item>
<Form.Item {...itemLayout} required label="模板名称">
{getFieldDecorator('name', {initialValue: info['name']})(
<Input placeholder="请输入模板名称"/>
)}
</Form.Item>
<Form.Item {...itemLayout} required label="模板内容">
<Editor
mode="sh"
theme="tomorrow"
value={this.state.body}
onChange={val => this.setState({body: val})}
enableLiveAutocompletion={true}
enableBasicAutocompletion={true}
enableSnippets={true}
height="300px"/>
</Form.Item>
<Form.Item {...itemLayout} label="备注信息">
{getFieldDecorator('desc', {initialValue: info['desc']})(
<Input.TextArea placeholder="请输入模板备注信息"/>
)}
</Form.Item>
</Form>
</Modal>
)
}
}
export default Form.create()(ComForm)

View File

@ -0,0 +1,76 @@
import React from 'react';
import { observer } from 'mobx-react';
import { Table, Divider, Modal, 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()
}
columns = [{
title: '序号',
key: 'series',
render: (_, __, index) => index + 1,
width: 80,
}, {
title: '模版名称',
dataIndex: 'name',
}, {
title: '模版类型',
dataIndex: 'type',
}, {
title: '模版内容',
render: text => text.body,
ellipsis: true
}, {
title: '描述信息',
dataIndex: 'desc',
ellipsis: true
}, {
title: '操作',
render: info => (
<span>
<LinkButton onClick={() => store.showForm(info)}>编辑</LinkButton>
<Divider type="vertical"/>
<LinkButton onClick={() => this.handleDelete(info)}>删除</LinkButton>
</span>
)
}];
handleDelete = (text) => {
Modal.confirm({
title: '删除确认',
content: `确定要删除【${text['name']}】?`,
onOk: () => {
return http.delete('/api/exec/template/', {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 (
<React.Fragment>
<Table rowKey="id" loading={store.isFetching} dataSource={data} columns={this.columns}/>
{store.formVisible && <ComForm/>}
</React.Fragment>
)
}
}
export default ComTable

View File

@ -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 (
<Card>
<SearchForm>
<SearchForm.Item span={8} title="模板类型">
<Select allowClear onChange={v => store.f_type = v} placeholder="请选择">
{store.types.map(item => (
<Select.Option value={item} key={item}>{item}</Select.Option>
))}
</Select>
</SearchForm.Item>
<SearchForm.Item span={8} title="模版名称">
<Input allowClear onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
</SearchForm.Item>
<SearchForm.Item span={8}>
<Button type="primary" icon="sync" onClick={store.fetchRecords}>刷新</Button>
</SearchForm.Item>
</SearchForm>
<div style={{marginBottom: 16}}>
<Button type="primary" icon="plus" onClick={() => store.showForm()}>新建</Button>
</div>
<ComTable/>
</Card>
)
})

View File

@ -0,0 +1,30 @@
import { observable } from "mobx";
import http from 'libs/http';
class Store {
@observable records = [];
@observable types = [];
@observable record = {};
@observable isFetching = false;
@observable formVisible = false;
@observable f_name;
@observable f_type;
fetchRecords = () => {
this.isFetching = true;
http.get('/api/exec/template/')
.then(({types, templates}) => {
this.records = templates;
this.types = types
})
.finally(() => this.isFetching = false)
};
showForm = (info = {}) => {
this.formVisible = true;
this.record = info
}
}
export default new Store()

View File

@ -3,10 +3,12 @@ import { makeModuleRoute } from "./libs/router";
import homeRoutes from './pages/home/routes';
import hostRoutes from './pages/host/routes';
import systemRoutes from './pages/system/routes';
import execRoutes from './pages/exec/routes';
export default [
makeModuleRoute('/home', homeRoutes),
makeModuleRoute('/host', hostRoutes),
makeModuleRoute('/system', systemRoutes),
makeModuleRoute('/exec', execRoutes),
]

View File

@ -1670,6 +1670,11 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
mime-types "~2.1.24"
negotiator "0.6.2"
ace-builds@^1.4.6, ace-builds@^1.4.7:
version "1.4.7"
resolved "https://registry.npm.taobao.org/ace-builds/download/ace-builds-1.4.7.tgz#56e5465270b6c48a48d30e70d6b8f6b92fbf2b08"
integrity sha1-VuVGUnC2xIpI0w5w1rj2uS+/Kwg=
acorn-globals@^4.1.0, acorn-globals@^4.3.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7"
@ -3546,6 +3551,11 @@ detect-port-alt@1.1.6:
address "^1.0.1"
debug "^2.6.0"
diff-match-patch@^1.0.4:
version "1.0.4"
resolved "https://registry.npm.taobao.org/diff-match-patch/download/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1"
integrity sha1-asS1UjdGN2HE2vDcYD64aRJHRLE=
diff-sequences@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
@ -6412,6 +6422,11 @@ lodash.flow@^3.5.0:
resolved "https://registry.npm.taobao.org/lodash.flow/download/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
integrity sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.npm.taobao.org/lodash.get/download/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.isarguments@^3.0.0:
version "3.1.0"
resolved "https://registry.npm.taobao.org/lodash.isarguments/download/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
@ -6422,6 +6437,11 @@ lodash.isarray@^3.0.0:
resolved "https://registry.npm.taobao.org/lodash.isarray/download/lodash.isarray-3.0.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash.isarray%2Fdownload%2Flodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.npm.taobao.org/lodash.isequal/download/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.keys@^3.1.2:
version "3.1.2"
resolved "https://registry.npm.taobao.org/lodash.keys/download/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
@ -9019,6 +9039,17 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-ace@^8.0.0:
version "8.0.0"
resolved "https://registry.npm.taobao.org/react-ace/download/react-ace-8.0.0.tgz#e6fc155ec3cf240e92bdf2e156a50458a78ed0a4"
integrity sha1-5vwVXsPPJA6SvfLhVqUEWKeO0KQ=
dependencies:
ace-builds "^1.4.6"
diff-match-patch "^1.0.4"
lodash.get "^4.4.2"
lodash.isequal "^4.5.0"
prop-types "^15.7.2"
react-app-polyfill@^1.0.4:
version "1.0.4"
resolved "https://registry.npm.taobao.org/react-app-polyfill/download/react-app-polyfill-1.0.4.tgz#4dd2636846b585c2d842b1e44e1bc29044345874"