diff --git a/spug_web/src/menus.js b/spug_web/src/menus.js
index 6039b89..9742a52 100644
--- a/spug_web/src/menus.js
+++ b/spug_web/src/menus.js
@@ -10,6 +10,7 @@ export default [
{
icon: 'flag', title: '应用发布', child: [
{title: '应用管理', path: '/deploy/app'},
+ {title: '发布申请', path: '/deploy/request'},
]
},
{icon: 'schedule', title: '任务计划', path: '/schedule'},
diff --git a/spug_web/src/pages/deploy/request/Ext1Form.js b/spug_web/src/pages/deploy/request/Ext1Form.js
new file mode 100644
index 0000000..4fa0bc5
--- /dev/null
+++ b/spug_web/src/pages/deploy/request/Ext1Form.js
@@ -0,0 +1,98 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Modal, Form, Input, Select, Col, Button, Tag, message } from 'antd';
+import hostStore from 'pages/host/store';
+import http from 'libs/http';
+import store from './store';
+import lds from 'lodash';
+
+@observer
+class Ext1Form extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: false,
+ type: null,
+ host_ids: store.record['host_ids'].concat()
+ }
+ }
+
+ handleSubmit = () => {
+ if (this.state.host_ids.length === 0) {
+ return message.error('请至少选择一个要发布的目标主机')
+ }
+ 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}))
+ };
+
+ handleChange = (id, v) => {
+ const host_ids = this.state.host_ids;
+ const index = host_ids.indexOf(id);
+ if (index === -1) {
+ this.setState({host_ids: [id, ...host_ids]})
+ } else {
+ host_ids.splice(index, 1);
+ this.setState({host_ids})
+ }
+ };
+
+ render() {
+ const info = store.record;
+ const {host_ids} = this.state;
+ const {getFieldDecorator} = this.props.form;
+ return (
+ store.ext1Visible = false}
+ confirmLoading={this.state.loading}
+ onOk={this.handleSubmit}>
+
+ {getFieldDecorator('name', {initialValue: info['name']})(
+
+ )}
+
+
+
+ {getFieldDecorator('name', {initialValue: info['name']})(
+
+ )}
+
+
+
+
+
+
+ {getFieldDecorator('desc', {initialValue: info['desc']})(
+
+ )}
+
+
+ {info['host_ids'].map(id => (
+ this.handleChange(id, v)}>
+ {lds.get(hostStore.idMap, `${id}.name`)}({lds.get(hostStore.idMap, `${id}.hostname`)}:{lds.get(hostStore.idMap, `${id}.port`)})
+
+ ))}
+
+
+
+ )
+ }
+}
+
+export default Form.create()(Ext1Form)
\ No newline at end of file
diff --git a/spug_web/src/pages/deploy/request/Form.js b/spug_web/src/pages/deploy/request/Form.js
new file mode 100644
index 0000000..0c7cf5d
--- /dev/null
+++ b/spug_web/src/pages/deploy/request/Form.js
@@ -0,0 +1,104 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Modal, Form, Input, Select, Col, Button, message } from 'antd';
+import { ACEditor } from 'components';
+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 = (
+
+ this.setState({type: val.target.value})}/>
+
+
+ );
+
+ render() {
+ const info = store.record;
+ const {getFieldDecorator} = this.props.form;
+ return (
+ store.formVisible = false}
+ confirmLoading={this.state.loading}
+ onOk={this.handleSubmit}>
+
+
+ {getFieldDecorator('type', {initialValue: info['type']})(
+
+ )}
+
+
+
+
+
+
+ {getFieldDecorator('name', {initialValue: info['name']})(
+
+ )}
+
+
+ this.setState({body: val})}
+ height="300px"/>
+
+
+ {getFieldDecorator('desc', {initialValue: info['desc']})(
+
+ )}
+
+
+
+ )
+ }
+}
+
+export default Form.create()(ComForm)
\ No newline at end of file
diff --git a/spug_web/src/pages/deploy/request/SelectApp.js b/spug_web/src/pages/deploy/request/SelectApp.js
new file mode 100644
index 0000000..d30da5e
--- /dev/null
+++ b/spug_web/src/pages/deploy/request/SelectApp.js
@@ -0,0 +1,86 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Modal, Button, Menu, Icon } from 'antd';
+import store from './store';
+import styles from './index.module.css';
+import envStore from 'pages/config/environment/store';
+import hostStore from 'pages/host/store';
+import appStore from '../app/store';
+import lds from 'lodash';
+
+@observer
+class SelectApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ env_id: 0
+ }
+ }
+
+ componentDidMount() {
+ if (envStore.records.length === 0) {
+ envStore.fetchRecords().then(this._initEnv)
+ } else {
+ this._initEnv()
+ }
+ if (appStore.records.length === 0) {
+ appStore.fetchRecords()
+ }
+ if (hostStore.records.length === 0) {
+ hostStore.fetchRecords()
+ }
+ }
+
+ _initEnv = () => {
+ if (envStore.records.length) {
+ this.setState({env_id: envStore.records[0].id})
+ }
+ };
+
+ handleClick = (app) => {
+ store.record = app;
+ if (app.extend === '1') {
+ store.ext1Visible = true
+ } else {
+ store.ext2Visible = true
+ }
+ };
+
+ render() {
+ const {env_id} = this.state;
+ return (
+ store.addVisible = false}
+ footer={null}>
+
+
+
+
+
+
{lds.get(envStore.idMap, `${env_id}.name`)}
+ {appStore.records.map(item => (
+
+ ))}
+
+
+
+ )
+ }
+}
+
+export default SelectApp
\ No newline at end of file
diff --git a/spug_web/src/pages/deploy/request/Table.js b/spug_web/src/pages/deploy/request/Table.js
new file mode 100644
index 0000000..62280f1
--- /dev/null
+++ b/spug_web/src/pages/deploy/request/Table.js
@@ -0,0 +1,72 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Table, Divider, Modal, message } from 'antd';
+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 => (
+
+ store.showForm(info)}>编辑
+
+ this.handleDelete(info)}>删除
+
+ )
+ }];
+
+ 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 (
+
+ )
+ }
+}
+
+export default ComTable
\ No newline at end of file
diff --git a/spug_web/src/pages/deploy/request/index.js b/spug_web/src/pages/deploy/request/index.js
new file mode 100644
index 0000000..f991a81
--- /dev/null
+++ b/spug_web/src/pages/deploy/request/index.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Card, Input, Select, Button } from 'antd';
+import { SearchForm } from 'components';
+import SelectApp from './SelectApp';
+import Ext1Form from './Ext1Form';
+import ComTable from './Table';
+import store from './store';
+
+export default observer(function () {
+ return (
+
+
+
+
+
+
+ store.f_name = e.target.value} placeholder="请输入"/>
+
+
+
+
+
+
+
+
+
+ {store.addVisible && }
+ {store.ext1Visible && }
+
+ )
+})
\ No newline at end of file
diff --git a/spug_web/src/pages/deploy/request/index.module.css b/spug_web/src/pages/deploy/request/index.module.css
new file mode 100644
index 0000000..e243228
--- /dev/null
+++ b/spug_web/src/pages/deploy/request/index.module.css
@@ -0,0 +1,30 @@
+.container {
+ display: flex;
+ background-color: #fff;
+ padding: 16px 0;
+ min-height: 500px;
+}
+.left {
+ flex: 2;
+ border-right: 1px solid #e8e8e8;
+}
+.right {
+ flex: 7;
+ padding: 8px 40px;
+}
+
+.title {
+ margin-bottom: 12px;
+ color: rgba(0, 0, 0, .85);
+ font-weight: 500;
+ font-size: 20px;
+ line-height: 28px;
+}
+
+.appBlock {
+ margin-top: 20px;
+ width: 165px;
+ height: 60px;
+ font-size: 18px;
+ margin-right: 20px;
+}
\ No newline at end of file
diff --git a/spug_web/src/pages/deploy/request/store.js b/spug_web/src/pages/deploy/request/store.js
new file mode 100644
index 0000000..9a49258
--- /dev/null
+++ b/spug_web/src/pages/deploy/request/store.js
@@ -0,0 +1,24 @@
+import { observable } from "mobx";
+import http from 'libs/http';
+
+class Store {
+ @observable records = [];
+ @observable types = [];
+ @observable record = {};
+ @observable isFetching = false;
+ @observable addVisible = false;
+ @observable ext1Visible = false;
+ @observable ext2Visible = false;
+
+ @observable f_name;
+ @observable f_type;
+
+ fetchRecords = () => {
+ this.isFetching = true;
+ http.get('/api/app/')
+ .then(res => this.records = res)
+ .finally(() => this.isFetching = false)
+ };
+}
+
+export default new Store()
\ No newline at end of file
diff --git a/spug_web/src/pages/deploy/routes.js b/spug_web/src/pages/deploy/routes.js
index 4a4547b..64b9951 100644
--- a/spug_web/src/pages/deploy/routes.js
+++ b/spug_web/src/pages/deploy/routes.js
@@ -1,7 +1,9 @@
import { makeRoute } from "../../libs/router";
import app from './app';
+import request from './request';
export default [
makeRoute('/app', app),
+ makeRoute('/request', request),
]
\ No newline at end of file
diff --git a/spug_web/src/pages/host/store.js b/spug_web/src/pages/host/store.js
index 0092b4f..d593820 100644
--- a/spug_web/src/pages/host/store.js
+++ b/spug_web/src/pages/host/store.js
@@ -5,6 +5,7 @@ class Store {
@observable records = [];
@observable zones = [];
@observable record = {};
+ @observable idMap = {};
@observable isFetching = false;
@observable formVisible = false;
@@ -17,6 +18,9 @@ class Store {
.then(({hosts, zones}) => {
this.records = hosts;
this.zones = zones;
+ for (let item of hosts) {
+ this.idMap[item.id] = item
+ }
})
.finally(() => this.isFetching = false)
};