mirror of https://github.com/openspug/spug
				
				
				
			U web add exec template
							parent
							
								
									f2965dc86e
								
							
						
					
					
						commit
						e58c66734a
					
				| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
import { makeRoute } from "../../libs/router";
 | 
			
		||||
import Template from './template';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default [
 | 
			
		||||
  makeRoute('/template', Template),
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			@ -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),
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue