style migrate v3

pull/289/head
vapao 2020-11-26 12:16:08 +08:00
parent a1a9792f1d
commit 97bbe7ddf0
12 changed files with 221 additions and 223 deletions

View File

@ -5,7 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Modal, Card, Icon } from 'antd'; import { BuildOutlined, OrderedListOutlined } from '@ant-design/icons';
import { Modal, Card } from 'antd';
import store from './store'; import store from './store';
import styles from './index.module.css'; import styles from './index.module.css';
@ -58,7 +59,7 @@ class AddSelect extends React.Component {
bodyStyle={{display: 'flex'}} bodyStyle={{display: 'flex'}}
onClick={this.switchExt1}> onClick={this.switchExt1}>
<div style={{marginRight: 16}}> <div style={{marginRight: 16}}>
<Icon type="ordered-list" style={{fontSize: 36, color: '#1890ff'}}/> <OrderedListOutlined style={{fontSize: 36, color: '#1890ff'}} />
</div> </div>
<div> <div>
<div className={styles.cardTitle}>常规发布</div> <div className={styles.cardTitle}>常规发布</div>
@ -72,7 +73,7 @@ class AddSelect extends React.Component {
bodyStyle={{display: 'flex'}} bodyStyle={{display: 'flex'}}
onClick={this.switchExt2}> onClick={this.switchExt2}>
<div style={{marginRight: 16}}> <div style={{marginRight: 16}}>
<Icon type="build" style={{fontSize: 36, color: '#1890ff'}}/> <BuildOutlined style={{fontSize: 36, color: '#1890ff'}} />
</div> </div>
<div> <div>
<div className={styles.cardTitle}>自定义发布</div> <div className={styles.cardTitle}>自定义发布</div>

View File

@ -49,7 +49,7 @@ class CloneConfirm extends React.Component {
render() { render() {
const options = this.handleData(Object.values(toJS(store.records))); const options = this.handleData(Object.values(toJS(store.records)));
return ( return (
<Form> <Form layout="vertical" style={{marginTop: 24}}>
<Form.Item <Form.Item
required required
label="应用及环境" label="应用及环境"

View File

@ -6,7 +6,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Switch, Col, Form, Input, Select, Button } from "antd"; import { Switch, Form, Input, Select, Button } from 'antd';
import envStore from 'pages/config/environment/store'; import envStore from 'pages/config/environment/store';
import store from './store'; import store from './store';
@ -29,17 +29,17 @@ export default observer(function Ext1Setup1() {
const info = store.deploy; const info = store.deploy;
return ( return (
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}> <Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
<Form.Item required label="发布环境"> <Form.Item required label="发布环境" style={{marginBottom: 0}}>
<Col span={16}> <Form.Item style={{display: 'inline-block', width: '80%'}}>
<Select disabled={store.isReadOnly} value={info.env_id} onChange={v => info.env_id = v} placeholder="请选择发布环境"> <Select disabled={store.isReadOnly} value={info.env_id} onChange={v => info.env_id = v} placeholder="请选择发布环境">
{envStore.records.map(item => ( {envStore.records.map(item => (
<Select.Option disabled={envs.includes(item.id)} value={item.id} key={item.id}>{item.name}</Select.Option> <Select.Option disabled={envs.includes(item.id)} value={item.id} key={item.id}>{item.name}</Select.Option>
))} ))}
</Select> </Select>
</Col> </Form.Item>
<Col span={6} offset={2}> <Form.Item style={{display: 'inline-block', width: '20%', textAlign: 'right'}}>
<Link disabled={store.isReadOnly} to="/config/environment">新建环境</Link> <Link disabled={store.isReadOnly} to="/config/environment">新建环境</Link>
</Col> </Form.Item>
</Form.Item> </Form.Item>
<Form.Item required label="Git仓库地址"> <Form.Item required label="Git仓库地址">
<Input disabled={store.isReadOnly} value={info['git_repo']} onChange={e => info['git_repo'] = e.target.value} <Input disabled={store.isReadOnly} value={info['git_repo']} onChange={e => info['git_repo'] = e.target.value}
@ -58,20 +58,21 @@ export default observer(function Ext1Setup1() {
<a target="_blank" rel="noopener noreferrer" <a target="_blank" rel="noopener noreferrer"
href="https://spug.dev/docs/install-error/#%E9%92%89%E9%92%89%E6%94%B6%E4%B8%8D%E5%88%B0%E9%80%9A%E7%9F%A5%EF%BC%9F">钉钉收不到通知</a> href="https://spug.dev/docs/install-error/#%E9%92%89%E9%92%89%E6%94%B6%E4%B8%8D%E5%88%B0%E9%80%9A%E7%9F%A5%EF%BC%9F">钉钉收不到通知</a>
</span>}> </span>}>
<Input addonBefore={( <Input
<Select disabled={store.isReadOnly} addonBefore={(
value={info['rst_notify']['mode']} style={{width: 100}} <Select disabled={store.isReadOnly}
onChange={v => info['rst_notify']['mode'] = v}> value={info['rst_notify']['mode']} style={{width: 100}}
<Select.Option value="0">关闭</Select.Option> onChange={v => info['rst_notify']['mode'] = v}>
<Select.Option value="1">钉钉</Select.Option> <Select.Option value="0">关闭</Select.Option>
<Select.Option value="3">企业微信</Select.Option> <Select.Option value="1">钉钉</Select.Option>
<Select.Option value="2">Webhook</Select.Option> <Select.Option value="3">企业微信</Select.Option>
</Select> <Select.Option value="2">Webhook</Select.Option>
)} </Select>
disabled={store.isReadOnly || info['rst_notify']['mode'] === '0'} )}
value={info['rst_notify']['value']} disabled={store.isReadOnly || info['rst_notify']['mode'] === '0'}
onChange={e => info['rst_notify']['value'] = e.target.value} value={info['rst_notify']['value']}
placeholder="请输入"/> onChange={e => info['rst_notify']['value'] = e.target.value}
placeholder="请输入"/>
</Form.Item> </Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>
<Button <Button

View File

@ -5,7 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Form, Input, Select, Button, Icon, message } from "antd"; import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Form, Input, Select, Button, message } from "antd";
import { hasHostPermission } from 'libs'; import { hasHostPermission } from 'libs';
import store from './store'; import store from './store';
import hostStore from 'pages/host/store'; import hostStore from 'pages/host/store';
@ -55,7 +56,7 @@ class Ext1Setup2 extends React.Component {
showSearch showSearch
placeholder="请选择" placeholder="请选择"
disabled={store.isReadOnly} disabled={store.isReadOnly}
style={{width: '80%', marginRight: 10}} style={{width: '80%', marginRight: 10, marginBottom: 12}}
optionFilterProp="children" optionFilterProp="children"
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
onChange={v => store.editHost(index, v)}> onChange={v => store.editHost(index, v)}>
@ -66,14 +67,14 @@ class Ext1Setup2 extends React.Component {
))} ))}
</Select> </Select>
{!store.isReadOnly && info['host_ids'].length > 1 && ( {!store.isReadOnly && info['host_ids'].length > 1 && (
<Icon className={styles.delIcon} type="minus-circle-o" onClick={() => store.delHost(index)}/> <MinusCircleOutlined className={styles.delIcon} onClick={() => store.delHost(index)} />
)} )}
</React.Fragment> </React.Fragment>
))} ))}
</Form.Item> </Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>
<Button disabled={store.isReadOnly} type="dashed" style={{width: '80%'}} onClick={store.addHost}> <Button disabled={store.isReadOnly} type="dashed" style={{width: '80%'}} onClick={store.addHost}>
<Icon type="plus"/>添加目标主机 <PlusOutlined />添加目标主机
</Button> </Button>
</Form.Item> </Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>

View File

@ -5,7 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Form, Row, Col, Button, Radio, Icon, Tooltip, message } from "antd"; import { GitlabOutlined, InfoCircleOutlined, SettingOutlined, SwapOutlined } from '@ant-design/icons';
import { Form, Row, Col, Button, Radio, Tooltip, message } from 'antd';
import { LinkButton } from 'components'; import { LinkButton } from 'components';
import Editor from 'react-ace'; import Editor from 'react-ace';
import 'ace-builds/src-noconflict/mode-text'; import 'ace-builds/src-noconflict/mode-text';
@ -51,9 +52,17 @@ class Ext1Setup3 extends React.Component {
}, () => this.setState({loading: false})) }, () => this.setState({loading: false}))
}; };
handleFullscreen = (id) => {
if (this.state.full) {
this.setState({full: ''})
} else {
this.setState({full: id})
}
}
FilterLabel = (props) => ( FilterLabel = (props) => (
<div style={{display: 'inline-block', height: 39, width: 390}}> <div style={{display: 'flex', alignItems: 'center', height: 40}}>
<span style={{float: 'left'}}>文件过滤<span style={{margin: '0 8px 0 2px'}}>:</span></span> <div>文件过滤 :</div>
<Radio.Group <Radio.Group
disabled={store.isReadOnly} disabled={store.isReadOnly}
style={{marginLeft: 20, float: 'left'}} style={{marginLeft: 20, float: 'left'}}
@ -61,36 +70,30 @@ class Ext1Setup3 extends React.Component {
onChange={e => store.deploy['filter_rule']['type'] = e.target.value}> onChange={e => store.deploy['filter_rule']['type'] = e.target.value}>
<Radio value="contain">包含 <Radio value="contain">包含
<Tooltip title="请输入相对于项目根目录的文件路径,仅将匹配到文件传输至要发布的目标主机。"> <Tooltip title="请输入相对于项目根目录的文件路径,仅将匹配到文件传输至要发布的目标主机。">
<Icon type="info-circle" style={{color: '#515151', marginLeft: 8}}/> <InfoCircleOutlined style={{color: '#515151', marginLeft: 8}}/>
</Tooltip> </Tooltip>
</Radio> </Radio>
<Radio value="exclude">排除 <Radio value="exclude">排除
<Tooltip title="支持模糊匹配,如果路径以 / 开头则基于项目根目录匹配,匹配到文件将不会被传输。"> <Tooltip title="支持模糊匹配,如果路径以 / 开头则基于项目根目录匹配,匹配到文件将不会被传输。">
<Icon type="info-circle" style={{color: '#515151', marginLeft: 8}}/> <InfoCircleOutlined style={{color: '#515151', marginLeft: 8}}/>
</Tooltip> </Tooltip>
</Radio> </Radio>
</Radio.Group> </Radio.Group>
{this.state.full === '1' ? ( <div style={{flex: 1, textAlign: 'right'}}>
<LinkButton onClick={() => this.setState({full: ''})}>退出全屏</LinkButton> <LinkButton onClick={() => this.handleFullscreen('1')}>{this.state.full ? '退出全屏' : '全屏'}</LinkButton>
) : ( </div>
<LinkButton onClick={() => this.setState({full: '1'})}>全屏</LinkButton>
)}
</div> </div>
); );
NormalLabel = (props) => ( NormalLabel = (props) => (
<div style={{display: 'inline-block', height: 39, width: 390}}> <div style={{display: 'flex', alignItems: 'center', height: 40}}>
<span style={{float: 'left'}}> <div style={{marginRight: 8}}>{props.title} :</div>
{props.title}<span style={{margin: '0 8px 0 2px'}}>:</span> <Tooltip title={this.helpMap[props.id]}>
<Tooltip title={this.helpMap[props.id]}> <InfoCircleOutlined style={{color: '#515151'}}/>
<Icon type="info-circle" style={{color: '#515151'}}/> </Tooltip>
</Tooltip> <div style={{flex: 1, textAlign: 'right'}}>
</span> <LinkButton onClick={() => this.handleFullscreen(props.id)}>{this.state.full ? '退出全屏' : '全屏'}</LinkButton>
{this.state.full ? ( </div>
<span style={{color: '#1890ff', cursor: 'pointer'}} onClick={() => this.setState({full: ''})}>退出全屏</span>
) : (
<span style={{color: '#1890ff', cursor: 'pointer'}} onClick={() => this.setState({full: props.id})}>全屏</span>
)}
</div> </div>
); );
@ -101,10 +104,8 @@ class Ext1Setup3 extends React.Component {
<React.Fragment> <React.Fragment>
<Row> <Row>
<Col span={11}> <Col span={11}>
<Form.Item <div className={full === '1' ? styles.fullScreen : null} style={{marginBottom: 24}}>
colon={false} <this.FilterLabel type={info['filter_rule']['type']}/>
className={full === '1' ? styles.fullScreen : null}
label={<this.FilterLabel type={info['filter_rule']['type']}/>}>
<Editor <Editor
readOnly={store.isReadOnly} readOnly={store.isReadOnly}
mode="text" mode="text"
@ -115,11 +116,9 @@ class Ext1Setup3 extends React.Component {
value={info['filter_rule']['data']} value={info['filter_rule']['data']}
onChange={v => info['filter_rule']['data'] = cleanCommand(v)} onChange={v => info['filter_rule']['data'] = cleanCommand(v)}
style={{border: '1px solid #e8e8e8'}}/> style={{border: '1px solid #e8e8e8'}}/>
</Form.Item> </div>
<Form.Item <div className={full === '3' ? styles.fullScreen : null} style={{marginBottom: 24}}>
colon={false} <this.NormalLabel title="代码检出前执行" id="3"/>
className={full === '3' ? styles.fullScreen : null}
label={<this.NormalLabel title="代码检出前执行" id="3"/>}>
<Editor <Editor
readOnly={store.isReadOnly} readOnly={store.isReadOnly}
mode="sh" mode="sh"
@ -130,11 +129,9 @@ class Ext1Setup3 extends React.Component {
value={info['hook_pre_server']} value={info['hook_pre_server']}
onChange={v => info['hook_pre_server'] = cleanCommand(v)} onChange={v => info['hook_pre_server'] = cleanCommand(v)}
style={{border: '1px solid #e8e8e8'}}/> style={{border: '1px solid #e8e8e8'}}/>
</Form.Item> </div>
<Form.Item <div className={full === '5' ? styles.fullScreen : null} style={{marginBottom: 24}}>
colon={false} <this.NormalLabel title="应用发布前执行" id="5"/>
className={full === '5' ? styles.fullScreen : null}
label={<this.NormalLabel title="应用发布前执行" id="5"/>}>
<Editor <Editor
readOnly={store.isReadOnly} readOnly={store.isReadOnly}
mode="sh" mode="sh"
@ -145,27 +142,25 @@ class Ext1Setup3 extends React.Component {
value={info['hook_pre_host']} value={info['hook_pre_host']}
onChange={v => info['hook_pre_host'] = cleanCommand(v)} onChange={v => info['hook_pre_host'] = cleanCommand(v)}
style={{border: '1px solid #e8e8e8'}}/> style={{border: '1px solid #e8e8e8'}}/>
</Form.Item> </div>
</Col> </Col>
<Col span={2}> <Col span={2}>
<div className={styles.deployBlock} style={{marginTop: 39}}> <div className={styles.deployBlock} style={{marginTop: 39}}>
<Icon type="setting" style={{fontSize: 32}}/> <SettingOutlined style={{fontSize: 32}}/>
<span style={{fontSize: 12, marginTop: 5}}>基础设置</span> <span style={{fontSize: 12, marginTop: 5}}>基础设置</span>
</div> </div>
<div className={styles.deployBlock}> <div className={styles.deployBlock}>
<Icon type="gitlab" style={{fontSize: 32}}/> <GitlabOutlined style={{fontSize: 32}}/>
<span style={{fontSize: 12, marginTop: 5}}>检出代码</span> <span style={{fontSize: 12, marginTop: 5}}>检出代码</span>
</div> </div>
<div className={styles.deployBlock}> <div className={styles.deployBlock}>
<Icon type="swap" style={{fontSize: 32}}/> <SwapOutlined style={{fontSize: 32}}/>
<span style={{fontSize: 12, marginTop: 5}}>版本切换</span> <span style={{fontSize: 12, marginTop: 5}}>版本切换</span>
</div> </div>
</Col> </Col>
<Col span={11}> <Col span={11}>
<Form.Item <div className={full === '2' ? styles.fullScreen : null} style={{marginBottom: 24}}>
colon={false} <this.NormalLabel title="自定义全局变量" id="2"/>
className={full === '2' ? styles.fullScreen : null}
label={<this.NormalLabel title="自定义全局变量" id="2"/>}>
<Editor <Editor
readOnly={store.isReadOnly} readOnly={store.isReadOnly}
mode="text" mode="text"
@ -176,11 +171,9 @@ class Ext1Setup3 extends React.Component {
value={info['custom_envs']} value={info['custom_envs']}
onChange={v => info['custom_envs'] = cleanCommand(v)} onChange={v => info['custom_envs'] = cleanCommand(v)}
style={{border: '1px solid #e8e8e8'}}/> style={{border: '1px solid #e8e8e8'}}/>
</Form.Item> </div>
<Form.Item <div className={full === '4' ? styles.fullScreen : null} style={{marginBottom: 24}}>
colon={false} <this.NormalLabel title="代码检出后执行" id="4"/>
className={full === '4' ? styles.fullScreen : null}
label={<this.NormalLabel title="代码检出后执行" id="4"/>}>
<Editor <Editor
readOnly={store.isReadOnly} readOnly={store.isReadOnly}
mode="sh" mode="sh"
@ -191,11 +184,9 @@ class Ext1Setup3 extends React.Component {
value={info['hook_post_server']} value={info['hook_post_server']}
onChange={v => info['hook_post_server'] = cleanCommand(v)} onChange={v => info['hook_post_server'] = cleanCommand(v)}
style={{border: '1px solid #e8e8e8'}}/> style={{border: '1px solid #e8e8e8'}}/>
</Form.Item> </div>
<Form.Item <div className={full === '6' ? styles.fullScreen : null} style={{marginBottom: 24}}>
colon={false} <this.NormalLabel title="应用发布后执行" id="6"/>
className={full === '6' ? styles.fullScreen : null}
label={<this.NormalLabel title="应用发布后执行" id="6"/>}>
<Editor <Editor
readOnly={store.isReadOnly} readOnly={store.isReadOnly}
mode="sh" mode="sh"
@ -206,7 +197,7 @@ class Ext1Setup3 extends React.Component {
value={info['hook_post_host']} value={info['hook_post_host']}
onChange={v => info['hook_post_host'] = cleanCommand(v)} onChange={v => info['hook_post_host'] = cleanCommand(v)}
style={{border: '1px solid #e8e8e8'}}/> style={{border: '1px solid #e8e8e8'}}/>
</Form.Item> </div>
</Col> </Col>
</Row> </Row>
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>

View File

@ -6,7 +6,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Switch, Col, Form, Select, Button, Input } from "antd"; import { Form, Switch, Select, Button, Input } from "antd";
import envStore from 'pages/config/environment/store'; import envStore from 'pages/config/environment/store';
import store from './store'; import store from './store';
@ -29,17 +29,17 @@ export default observer(function Ext2Setup1() {
const info = store.deploy; const info = store.deploy;
return ( return (
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}> <Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
<Form.Item required label="发布环境"> <Form.Item required label="发布环境" style={{marginBottom: 0}}>
<Col span={16}> <Form.Item style={{display: 'inline-block', width: '80%'}}>
<Select disabled={store.isReadOnly} value={info.env_id} onChange={v => info.env_id = v} placeholder="请选择发布环境"> <Select disabled={store.isReadOnly} value={info.env_id} onChange={v => info.env_id = v} placeholder="请选择发布环境">
{envStore.records.map(item => ( {envStore.records.map(item => (
<Select.Option disabled={envs.includes(item.id)} value={item.id} key={item.id}>{item.name}</Select.Option> <Select.Option disabled={envs.includes(item.id)} value={item.id} key={item.id}>{item.name}</Select.Option>
))} ))}
</Select> </Select>
</Col> </Form.Item>
<Col span={6} offset={2}> <Form.Item style={{display: 'inline-block', width: '20%', textAlign: 'right'}}>
<Link disabled={store.isReadOnly} to="/config/environment">新建环境</Link> <Link disabled={store.isReadOnly} to="/config/environment">新建环境</Link>
</Col> </Form.Item>
</Form.Item> </Form.Item>
<Form.Item label="发布审核"> <Form.Item label="发布审核">
<Switch <Switch
@ -54,20 +54,21 @@ export default observer(function Ext2Setup1() {
<a target="_blank" rel="noopener noreferrer" <a target="_blank" rel="noopener noreferrer"
href="https://spug.dev/docs/install-error/#%E9%92%89%E9%92%89%E6%94%B6%E4%B8%8D%E5%88%B0%E9%80%9A%E7%9F%A5%EF%BC%9F">钉钉收不到通知</a> href="https://spug.dev/docs/install-error/#%E9%92%89%E9%92%89%E6%94%B6%E4%B8%8D%E5%88%B0%E9%80%9A%E7%9F%A5%EF%BC%9F">钉钉收不到通知</a>
</span>}> </span>}>
<Input addonBefore={( <Input
<Select disabled={store.isReadOnly} addonBefore={(
value={info['rst_notify']['mode']} style={{width: 100}} <Select disabled={store.isReadOnly}
onChange={v => info['rst_notify']['mode'] = v}> value={info['rst_notify']['mode']} style={{width: 100}}
<Select.Option value="0">关闭</Select.Option> onChange={v => info['rst_notify']['mode'] = v}>
<Select.Option value="1">钉钉</Select.Option> <Select.Option value="0">关闭</Select.Option>
<Select.Option value="3">企业微信</Select.Option> <Select.Option value="1">钉钉</Select.Option>
<Select.Option value="2">Webhook</Select.Option> <Select.Option value="3">企业微信</Select.Option>
</Select> <Select.Option value="2">Webhook</Select.Option>
)} </Select>
disabled={store.isReadOnly || info['rst_notify']['mode'] === '0'} )}
value={info['rst_notify']['value']} disabled={store.isReadOnly || info['rst_notify']['mode'] === '0'}
onChange={e => info['rst_notify']['value'] = e.target.value} value={info['rst_notify']['value']}
placeholder="请输入"/> onChange={e => info['rst_notify']['value'] = e.target.value}
placeholder="请输入"/>
</Form.Item> </Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>
<Button <Button

View File

@ -5,7 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Form, Select, Button, Icon } from "antd"; import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Form, Select, Button } from 'antd';
import { hasHostPermission } from 'libs'; import { hasHostPermission } from 'libs';
import store from './store'; import store from './store';
import hostStore from 'pages/host/store'; import hostStore from 'pages/host/store';
@ -32,7 +33,7 @@ class Ext2Setup2 extends React.Component {
disabled={store.isReadOnly} disabled={store.isReadOnly}
placeholder="请选择" placeholder="请选择"
optionFilterProp="children" optionFilterProp="children"
style={{width: '80%', marginRight: 10}} style={{width: '80%', marginRight: 10, marginBottom: 12}}
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
onChange={v => store.editHost(index, v)}> onChange={v => store.editHost(index, v)}>
{hostStore.records.filter(x => hasHostPermission(x.id)).map(item => ( {hostStore.records.filter(x => hasHostPermission(x.id)).map(item => (
@ -42,14 +43,14 @@ class Ext2Setup2 extends React.Component {
))} ))}
</Select> </Select>
{!store.isReadOnly && info['host_ids'].length > 1 && ( {!store.isReadOnly && info['host_ids'].length > 1 && (
<Icon className={styles.delIcon} type="minus-circle-o" onClick={() => store.delHost(index)}/> <MinusCircleOutlined className={styles.delIcon} onClick={() => store.delHost(index)} />
)} )}
</React.Fragment> </React.Fragment>
))} ))}
</Form.Item> </Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>
<Button disabled={store.isReadOnly} type="dashed" style={{width: '80%'}} onClick={store.addHost}> <Button disabled={store.isReadOnly} type="dashed" style={{width: '80%'}} onClick={store.addHost}>
<Icon type="plus"/>添加目标主机 <PlusOutlined />添加目标主机
</Button> </Button>
</Form.Item> </Form.Item>
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>

View File

@ -5,7 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Form, Input, Button, message, Divider, Alert, Icon, Select } from 'antd'; import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Form, Input, Button, message, Divider, Alert, Select } from 'antd';
import Editor from 'react-ace'; import Editor from 'react-ace';
import 'ace-builds/src-noconflict/mode-sh'; import 'ace-builds/src-noconflict/mode-sh';
import 'ace-builds/src-noconflict/theme-tomorrow'; import 'ace-builds/src-noconflict/theme-tomorrow';
@ -81,7 +82,7 @@ class Ext2Setup3 extends React.Component {
</Form.Item> </Form.Item>
{!store.isReadOnly && ( {!store.isReadOnly && (
<div className={styles.delAction} onClick={() => server_actions.splice(index, 1)}> <div className={styles.delAction} onClick={() => server_actions.splice(index, 1)}>
<Icon type="minus-circle"/>移除 <MinusCircleOutlined />移除
</div> </div>
)} )}
</div> </div>
@ -89,7 +90,7 @@ class Ext2Setup3 extends React.Component {
{!store.isReadOnly && ( {!store.isReadOnly && (
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>
<Button type="dashed" block onClick={() => server_actions.push({})}> <Button type="dashed" block onClick={() => server_actions.push({})}>
<Icon type="plus"/>添加本地执行动作在服务端本地执行 <PlusOutlined />添加本地执行动作在服务端本地执行
</Button> </Button>
</Form.Item> </Form.Item>
)} )}
@ -159,7 +160,7 @@ class Ext2Setup3 extends React.Component {
)} )}
{!store.isReadOnly && ( {!store.isReadOnly && (
<div className={styles.delAction} onClick={() => host_actions.splice(index, 1)}> <div className={styles.delAction} onClick={() => host_actions.splice(index, 1)}>
<Icon type="minus-circle"/>移除 <MinusCircleOutlined />移除
</div> </div>
)} )}
</div> </div>
@ -167,14 +168,15 @@ class Ext2Setup3 extends React.Component {
{!store.isReadOnly && ( {!store.isReadOnly && (
<Form.Item wrapperCol={{span: 14, offset: 6}}> <Form.Item wrapperCol={{span: 14, offset: 6}}>
<Button disabled={store.isReadOnly} type="dashed" block onClick={() => host_actions.push({})}> <Button disabled={store.isReadOnly} type="dashed" block onClick={() => host_actions.push({})}>
<Icon type="plus"/>添加目标主机执行动作在部署目标主机执行 <PlusOutlined />添加目标主机执行动作在部署目标主机执行
</Button> </Button>
<Button <Button
block block
type="dashed" type="dashed"
style={{marginTop: 8}}
disabled={store.isReadOnly || lds.findIndex(host_actions, x => x.type === 'transfer') !== -1} disabled={store.isReadOnly || lds.findIndex(host_actions, x => x.type === 'transfer') !== -1}
onClick={() => host_actions.push({type: 'transfer', title: '数据传输', mode: '0', src_mode: '0'})}> onClick={() => host_actions.push({type: 'transfer', title: '数据传输', mode: '0', src_mode: '0'})}>
<Icon type="plus"/>添加数据传输动作仅能添加一个 <PlusOutlined />添加数据传输动作仅能添加一个
</Button> </Button>
</Form.Item> </Form.Item>
)} )}

View File

@ -3,65 +3,48 @@
* Copyright (c) <spug.dev@gmail.com> * Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License. * Released under the AGPL-3.0 License.
*/ */
import React from 'react'; import React, { useState } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Modal, Form, Input, message } from 'antd'; import { Modal, Form, Input, message } from 'antd';
import http from 'libs/http'; import http from 'libs/http';
import store from './store'; import store from './store';
@observer export default observer(function () {
class ComForm extends React.Component { const [form] = Form.useForm();
constructor(props) { const [loading, setLoading] = useState(false);
super(props);
this.state = {
loading: false,
}
}
handleSubmit = () => { function handleSubmit() {
this.setState({loading: true}); setLoading(true);
const formData = this.props.form.getFieldsValue(); const formData = form.getFieldsValue();
formData['id'] = store.record.id; formData['id'] = store.record.id;
http.post('/api/app/', formData) http.post('/api/app/', formData)
.then(res => { .then(res => {
message.success('操作成功'); message.success('操作成功');
store.formVisible = false; store.formVisible = false;
store.fetchRecords() store.fetchRecords()
}, () => this.setState({loading: false})) }, () => setLoading(false))
};
render() {
const info = store.record;
const {getFieldDecorator} = this.props.form;
return (
<Modal
visible
width={800}
maskClosable={false}
title={store.record.id ? '编辑应用' : '新建应用'}
onCancel={() => store.formVisible = false}
confirmLoading={this.state.loading}
onOk={this.handleSubmit}>
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
<Form.Item required label="应用名称">
{getFieldDecorator('name', {initialValue: info['name']})(
<Input placeholder="请输入应用名称,例如:订单服务"/>
)}
</Form.Item>
<Form.Item required label="唯一标识符">
{getFieldDecorator('key', {initialValue: info['key']})(
<Input placeholder="请输入唯一标识符例如api_order"/>
)}
</Form.Item>
<Form.Item label="备注信息">
{getFieldDecorator('desc', {initialValue: info['desc']})(
<Input.TextArea placeholder="请输入备注信息"/>
)}
</Form.Item>
</Form>
</Modal>
)
} }
}
export default Form.create()(ComForm) return (
<Modal
visible
width={800}
maskClosable={false}
title={store.record.id ? '编辑应用' : '新建应用'}
onCancel={() => store.formVisible = false}
confirmLoading={loading}
onOk={handleSubmit}>
<Form form={form} initialValues={store.record} labelCol={{span: 6}} wrapperCol={{span: 14}}>
<Form.Item required name="name" label="应用名称">
<Input placeholder="请输入应用名称,例如:订单服务"/>
</Form.Item>
<Form.Item required name="key" label="唯一标识符">
<Input placeholder="请输入唯一标识符例如api_order"/>
</Form.Item>
<Form.Item name="desc" label="备注信息">
<Input.TextArea placeholder="请输入备注信息"/>
</Form.Item>
</Form>
</Modal>
)
})

View File

@ -4,12 +4,19 @@
* Released under the AGPL-3.0 License. * Released under the AGPL-3.0 License.
*/ */
import React from 'react'; import React from 'react';
import { toJS } from 'mobx';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Table, Modal, Tag, Icon, Divider, message } from 'antd'; import {
BuildOutlined,
DownSquareOutlined,
ExclamationCircleOutlined,
OrderedListOutlined,
UpSquareOutlined,
PlusOutlined
} from '@ant-design/icons';
import { Table, Modal, Tag, Divider, message } from 'antd';
import { http, hasPermission } from 'libs'; import { http, hasPermission } from 'libs';
import store from './store'; import store from './store';
import { Action } from "components"; import { Action, TableCard, AuthButton } from "components";
import CloneConfirm from './CloneConfirm'; import CloneConfirm from './CloneConfirm';
import envStore from 'pages/config/environment/store'; import envStore from 'pages/config/environment/store';
import lds from 'lodash'; import lds from 'lodash';
@ -32,7 +39,7 @@ class ComTable extends React.Component {
e.stopPropagation(); e.stopPropagation();
this.cloneObj = null; this.cloneObj = null;
Modal.confirm({ Modal.confirm({
icon: 'exclamation-circle', icon: <ExclamationCircleOutlined/>,
title: '选择克隆对象', title: '选择克隆对象',
content: <CloneConfirm onChange={v => this.cloneObj = v[1]}/>, content: <CloneConfirm onChange={v => this.cloneObj = v[1]}/>,
onOk: () => { onOk: () => {
@ -88,49 +95,51 @@ class ComTable extends React.Component {
store.loadDeploys(record.id) store.loadDeploys(record.id)
} }
return <Table
rowKey="id"
loading={record['deploys'] === undefined}
dataSource={record['deploys']}
pagination={false}>
<Table.Column width={80} title="模式" dataIndex="extend" render={value => value === '1' ?
<Icon style={{fontSize: 20, color: '#1890ff'}} type="ordered-list"/> :
<Icon style={{fontSize: 20, color: '#1890ff'}} type="build"/>}/>
<Table.Column title="发布环境" dataIndex="env_id" render={value => lds.get(envStore.idMap, `${value}.name`)}/>
<Table.Column title="关联主机" dataIndex="host_ids" render={value => `${value.length}`}/>
<Table.Column title="发布审核" dataIndex="is_audit"
render={value => value ? <Tag color="green">开启</Tag> : <Tag color="red"></Tag>}/>
{hasPermission('deploy.app.config|deploy.app.edit') && (
<Table.Column title="操作" render={info => (
<Action>
<Action.Button
auth="deploy.app.config"
onClick={e => store.showExtForm(e, record.id, info, false, true)}>查看</Action.Button>
<Action.Button auth="deploy.app.edit"
onClick={e => store.showExtForm(e, record.id, info)}>编辑</Action.Button>
<Action.Button auth="deploy.app.edit" onClick={() => this.handleDeployDelete(info)}>删除</Action.Button>
</Action>
)}/>
)}
</Table>
};
render() {
let data = Object.values(toJS(store.records));
if (store.f_name) {
data = data.filter(item => item['name'].toLowerCase().includes(store.f_name.toLowerCase()))
}
if (store.f_desc) {
data = data.filter(item => item['desc'] && item['desc'].toLowerCase().includes(store.f_desc.toLowerCase()))
}
return ( return (
<Table <Table
rowKey="id" rowKey="id"
expandRowByClick loading={record['deploys'] === undefined}
dataSource={record['deploys']}
pagination={false}>
<Table.Column width={80} title="模式" dataIndex="extend" render={value => value === '1' ?
<OrderedListOutlined style={{fontSize: 20, color: '#1890ff'}}/> :
<BuildOutlined style={{fontSize: 20, color: '#1890ff'}}/>}/>
<Table.Column title="发布环境" dataIndex="env_id" render={value => lds.get(envStore.idMap, `${value}.name`)}/>
<Table.Column title="关联主机" dataIndex="host_ids" render={value => `${value.length}`}/>
<Table.Column title="发布审核" dataIndex="is_audit"
render={value => value ? <Tag color="green">开启</Tag> : <Tag color="red"></Tag>}/>
{hasPermission('deploy.app.config|deploy.app.edit') && (
<Table.Column title="操作" render={info => (
<Action>
<Action.Button
auth="deploy.app.config"
onClick={e => store.showExtForm(e, record.id, info, false, true)}>查看</Action.Button>
<Action.Button auth="deploy.app.edit"
onClick={e => store.showExtForm(e, record.id, info)}>编辑</Action.Button>
<Action.Button auth="deploy.app.edit" onClick={() => this.handleDeployDelete(info)}>删除</Action.Button>
</Action>
)}/>
)}
</Table>
)
};
render() {
return (
<TableCard
title="应用列表"
rowKey="id"
loading={store.isFetching} loading={store.isFetching}
dataSource={data} dataSource={store.dataSource}
expandedRowRender={this.expandedRowRender} expandable={{expandRowByClick: true, expandedRowRender: this.expandedRowRender}}
onReload={store.fetchRecords}
actions={[
<AuthButton
auth="deploy.app.add"
type="primary"
icon={<PlusOutlined/>}
onClick={() => store.showForm()}>新建</AuthButton>
]}
pagination={{ pagination={{
showSizeChanger: true, showSizeChanger: true,
showLessItems: true, showLessItems: true,
@ -140,11 +149,13 @@ class ComTable extends React.Component {
}}> }}>
<Table.Column width={80} title="排序" key="series" render={(info) => ( <Table.Column width={80} title="排序" key="series" render={(info) => (
<div> <div>
<Icon onClick={e => this.handleSort(e, info, 'up')} type="up-square" <UpSquareOutlined
style={{cursor: 'pointer', color: '#1890ff'}}/> onClick={e => this.handleSort(e, info, 'up')}
style={{cursor: 'pointer', color: '#1890ff'}}/>
<Divider type="vertical"/> <Divider type="vertical"/>
<Icon onClick={e => this.handleSort(e, info, 'down')} type="down-square" <DownSquareOutlined
style={{cursor: 'pointer', color: '#1890ff'}}/> onClick={e => this.handleSort(e, info, 'down')}
style={{cursor: 'pointer', color: '#1890ff'}}/>
</div> </div>
)}/> )}/>
<Table.Column title="应用名称" dataIndex="name"/> <Table.Column title="应用名称" dataIndex="name"/>
@ -160,7 +171,7 @@ class ComTable extends React.Component {
</Action> </Action>
)}/> )}/>
)} )}
</Table> </TableCard>
) )
} }
} }

View File

@ -5,8 +5,8 @@
*/ */
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Input, Button } from 'antd'; import { Input } from 'antd';
import { SearchForm, AuthDiv, AuthCard } from 'components'; import { SearchForm, AuthDiv, Breadcrumb } from 'components';
import ComTable from './Table'; import ComTable from './Table';
import ComForm from './Form'; import ComForm from './Form';
import Ext1Form from './Ext1Form'; import Ext1Form from './Ext1Form';
@ -16,26 +16,25 @@ import store from './store';
export default observer(function () { export default observer(function () {
return ( return (
<AuthCard auth="deploy.app.view"> <AuthDiv auth="deploy.app.view">
<Breadcrumb>
<Breadcrumb.Item>首页</Breadcrumb.Item>
<Breadcrumb.Item>应用发布</Breadcrumb.Item>
<Breadcrumb.Item>应用管理</Breadcrumb.Item>
</Breadcrumb>
<SearchForm> <SearchForm>
<SearchForm.Item span={6} title="应用名称"> <SearchForm.Item span={7} title="应用名称">
<Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入"/> <Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
</SearchForm.Item> </SearchForm.Item>
<SearchForm.Item span={6} title="描述信息"> <SearchForm.Item span={7} title="描述信息">
<Input allowClear value={store.f_desc} onChange={e => store.f_desc = e.target.value} placeholder="请输入"/> <Input allowClear value={store.f_desc} onChange={e => store.f_desc = e.target.value} placeholder="请输入"/>
</SearchForm.Item> </SearchForm.Item>
<SearchForm.Item span={8}>
<Button type="primary" icon="sync" onClick={store.fetchRecords}>刷新</Button>
</SearchForm.Item>
</SearchForm> </SearchForm>
<AuthDiv auth="deploy.app.add" style={{marginBottom: 16}}>
<Button type="primary" icon="plus" onClick={() => store.showForm()}>新建</Button>
</AuthDiv>
<ComTable/> <ComTable/>
{store.formVisible && <ComForm />} {store.formVisible && <ComForm />}
{store.addVisible && <AddSelect />} {store.addVisible && <AddSelect />}
{store.ext1Visible && <Ext1Form />} {store.ext1Visible && <Ext1Form />}
{store.ext2Visible && <Ext2Form />} {store.ext2Visible && <Ext2Form />}
</AuthCard> </AuthDiv>
) );
}) })

View File

@ -3,7 +3,7 @@
* Copyright (c) <spug.dev@gmail.com> * Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License. * Released under the AGPL-3.0 License.
*/ */
import { observable, computed } from "mobx"; import { observable, computed, toJS } from 'mobx';
import http from 'libs/http'; import http from 'libs/http';
class Store { class Store {
@ -22,6 +22,13 @@ class Store {
@observable f_name; @observable f_name;
@observable f_desc; @observable f_desc;
@computed get dataSource() {
let records = Object.values(toJS(this.records));
if (this.f_name) records = records.filter(x => x.name.toLowerCase().includes(this.f_name.toLowerCase()));
if (this.f_desc) records = records.filter(x => x.desc && x.desc.toLowerCase().includes(this.f_desc.toLowerCase()));
return records
}
@computed get currentRecord() { @computed get currentRecord() {
return this.records[`a${this.app_id}`] return this.records[`a${this.app_id}`]
} }