mirror of https://github.com/openspug/spug
style migrate v3
parent
97bbe7ddf0
commit
b875d9d65a
|
@ -18,10 +18,10 @@ export default observer(function () {
|
|||
return (
|
||||
<AuthDiv auth="deploy.app.view">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>应用发布</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>应用管理</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>应用发布</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>应用管理</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<SearchForm>
|
||||
<SearchForm.Item span={7} title="应用名称">
|
||||
<Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
|
||||
|
@ -31,10 +31,10 @@ export default observer(function () {
|
|||
</SearchForm.Item>
|
||||
</SearchForm>
|
||||
<ComTable/>
|
||||
{store.formVisible && <ComForm />}
|
||||
{store.addVisible && <AddSelect />}
|
||||
{store.ext1Visible && <Ext1Form />}
|
||||
{store.ext2Visible && <Ext2Form />}
|
||||
{store.formVisible && <ComForm/>}
|
||||
{store.addVisible && <AddSelect/>}
|
||||
{store.ext1Visible && <Ext1Form/>}
|
||||
{store.ext2Visible && <Ext2Form/>}
|
||||
</AuthDiv>
|
||||
);
|
||||
})
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Steps, Collapse, PageHeader, Spin, Tag, Button, Icon } from 'antd';
|
||||
import { CaretRightOutlined, LoadingOutlined, PlayCircleOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { Steps, Collapse, PageHeader, Spin, Tag, Button } from 'antd';
|
||||
import { http, history, X_TOKEN } from 'libs';
|
||||
import { AuthDiv } from 'components';
|
||||
import OutView from './OutView';
|
||||
|
@ -96,7 +97,7 @@ class Ext1Index extends React.Component {
|
|||
getStatus = (key, n) => {
|
||||
const step = lds.get(store.outputs, `${key}.step`, -1);
|
||||
const isError = lds.get(store.outputs, `${key}.status`) === 'error';
|
||||
const icon = <Icon type="loading"/>;
|
||||
const icon = <LoadingOutlined />;
|
||||
if (n > step) {
|
||||
return {key: n, status: 'wait'}
|
||||
} else if (n === step) {
|
||||
|
@ -112,12 +113,12 @@ class Ext1Index extends React.Component {
|
|||
if (lds.get(store.outputs, `${item.id}.status`) === 'error') {
|
||||
return <Tag color="red">发布异常</Tag>
|
||||
} else if (lds.get(store.outputs, `${item.id}.step`, -1) < 5) {
|
||||
return <Tag color="blue">发布中</Tag>
|
||||
return <Tag color="orange">发布中</Tag>
|
||||
}
|
||||
}
|
||||
return <Tag color="green">发布成功</Tag>
|
||||
} else {
|
||||
return <Tag>{store.request['status_alias'] || '...'}</Tag>
|
||||
return <Tag color="blue">{store.request['status_alias'] || '...'}</Tag>
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -132,9 +133,9 @@ class Ext1Index extends React.Component {
|
|||
style={{padding: 0}}
|
||||
tags={this.getStatusAlias()}
|
||||
extra={this.log ? (
|
||||
<Button icon="sync" type="primary" onClick={this.fetch}>刷新</Button>
|
||||
<Button icon={<SyncOutlined />} type="primary" onClick={this.fetch}>刷新</Button>
|
||||
) : (
|
||||
<Button icon="play-circle" loading={this.state.loading} type="primary"
|
||||
<Button icon={<PlayCircleOutlined />} loading={this.state.loading} type="primary"
|
||||
disabled={!['1', '-3'].includes(status)}
|
||||
onClick={this.handleDeploy}>发布</Button>
|
||||
)}
|
||||
|
@ -156,7 +157,7 @@ class Ext1Index extends React.Component {
|
|||
<Collapse
|
||||
defaultActiveKey={'0'}
|
||||
className={styles.collapse}
|
||||
expandIcon={({isActive}) => <Icon type="caret-right" style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}>
|
||||
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0} />}>
|
||||
{store.request.targets.map((item, index) => (
|
||||
<Collapse.Panel key={index} header={
|
||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Steps, Collapse, PageHeader, Spin, Tag, Button, Icon } from 'antd';
|
||||
import { CaretRightOutlined, LoadingOutlined, PlayCircleOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { Steps, Collapse, PageHeader, Spin, Tag, Button } from 'antd';
|
||||
import { http, history, X_TOKEN } from 'libs';
|
||||
import { AuthDiv } from 'components';
|
||||
import OutView from './OutView';
|
||||
|
@ -97,7 +98,7 @@ class Ext1Index extends React.Component {
|
|||
getStatus = (key, n) => {
|
||||
const step = lds.get(store.outputs, `${key}.step`, -1);
|
||||
const isError = lds.get(store.outputs, `${key}.status`) === 'error';
|
||||
const icon = <Icon type="loading"/>;
|
||||
const icon = <LoadingOutlined />;
|
||||
if (n > step) {
|
||||
return {key: n, status: 'wait'}
|
||||
} else if (n === step) {
|
||||
|
@ -114,12 +115,12 @@ class Ext1Index extends React.Component {
|
|||
if (lds.get(store.outputs, `${item.id}.status`) === 'error') {
|
||||
return <Tag color="red">发布异常</Tag>
|
||||
} else if (lds.get(store.outputs, `${item.id}.step`, -1) < 100) {
|
||||
return <Tag color="blue">发布中</Tag>
|
||||
return <Tag color="orange">发布中</Tag>
|
||||
}
|
||||
}
|
||||
return <Tag color="green">发布成功</Tag>
|
||||
} else {
|
||||
return <Tag>{store.request['status_alias'] || '...'}</Tag>
|
||||
return <Tag color="blue">{store.request['status_alias'] || '...'}</Tag>
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -134,9 +135,9 @@ class Ext1Index extends React.Component {
|
|||
style={{padding: 0}}
|
||||
tags={this.getStatusAlias()}
|
||||
extra={this.log ? (
|
||||
<Button icon="sync" type="primary" onClick={this.fetch}>刷新</Button>
|
||||
<Button icon={<SyncOutlined />} type="primary" onClick={this.fetch}>刷新</Button>
|
||||
) : (
|
||||
<Button icon="play-circle" loading={this.state.loading} type="primary"
|
||||
<Button icon={<PlayCircleOutlined />} loading={this.state.loading} type="primary"
|
||||
disabled={!['1', '-3'].includes(status)}
|
||||
onClick={this.handleDeploy}>发布</Button>
|
||||
)}
|
||||
|
@ -158,7 +159,7 @@ class Ext1Index extends React.Component {
|
|||
<Collapse
|
||||
defaultActiveKey={'0'}
|
||||
className={styles.collapse}
|
||||
expandIcon={({isActive}) => <Icon type="caret-right" style={{fontSize: 16}} rotate={isActive ? 90 : 0}/>}>
|
||||
expandIcon={({isActive}) => <CaretRightOutlined style={{fontSize: 16}} rotate={isActive ? 90 : 0} />}>
|
||||
{store.request.targets.map((item, index) => (
|
||||
<Collapse.Panel key={index} header={
|
||||
<div style={{display: 'flex', justifyContent: 'space-between'}}>
|
||||
|
|
|
@ -3,58 +3,50 @@
|
|||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Modal, Form, Input, Switch, message } from 'antd';
|
||||
import http from 'libs/http';
|
||||
import store from './store';
|
||||
|
||||
@observer
|
||||
class Approve extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
}
|
||||
}
|
||||
export default observer(function () {
|
||||
const [form] = Form.useForm();
|
||||
const [isPass, setIsPass] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
handleSubmit = () => {
|
||||
this.setState({loading: true});
|
||||
const formData = this.props.form.getFieldsValue();
|
||||
function handleSubmit() {
|
||||
setLoading(true);
|
||||
const formData = form.getFieldsValue();
|
||||
http.patch(`/api/deploy/request/${store.record.id}/`, formData)
|
||||
.then(res => {
|
||||
message.success('操作成功');
|
||||
store.approveVisible = false;
|
||||
store.fetchRecords()
|
||||
}, () => this.setState({loading: false}))
|
||||
};
|
||||
|
||||
render() {
|
||||
const {getFieldDecorator, getFieldValue} = this.props.form;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={600}
|
||||
maskClosable={false}
|
||||
title="审核发布申请"
|
||||
onCancel={() => store.approveVisible = false}
|
||||
confirmLoading={this.state.loading}
|
||||
onOk={this.handleSubmit}>
|
||||
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||
<Form.Item required label="审批结果">
|
||||
{getFieldDecorator('is_pass', {initialValue: true, valuePropName: "checked"})(
|
||||
<Switch checkedChildren="通过" unCheckedChildren="驳回"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item required={getFieldValue('is_pass') === false} label={getFieldValue('is_pass') ? '审批意见' : '驳回原因'}>
|
||||
{getFieldDecorator('reason')(
|
||||
<Input.TextArea placeholder={getFieldValue('is_pass') ? '请输入审批意见' : '请输入驳回原因'}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}, () => setLoading(false))
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create()(Approve)
|
||||
function handleChange(val) {
|
||||
if (val.is_pass !== undefined) {
|
||||
setIsPass(val.is_pass)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={600}
|
||||
maskClosable={false}
|
||||
title="审核发布申请"
|
||||
onCancel={() => store.approveVisible = false}
|
||||
confirmLoading={loading}
|
||||
onOk={handleSubmit}>
|
||||
<Form form={form} labelCol={{span: 6}} wrapperCol={{span: 14}} onValuesChange={handleChange}>
|
||||
<Form.Item required name="is_pass" initialValue={true} valuePropName="checked" label="审批结果">
|
||||
<Switch checkedChildren="通过" unCheckedChildren="驳回"/>
|
||||
</Form.Item>
|
||||
<Form.Item name="reason" required={isPass === false} label={isPass ? '审批意见' : '驳回原因'}>
|
||||
<Input.TextArea placeholder={isPass ? '请输入审批意见' : '请输入驳回原因'}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
})
|
|
@ -3,47 +3,59 @@
|
|||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Modal, Form, Input, Select, Col, Button, Tag, Icon, message } from 'antd';
|
||||
import { LoadingOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { Modal, Form, Input, Select, 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.isReady = false;
|
||||
this.state = {
|
||||
loading: false,
|
||||
fetching: true,
|
||||
git_type: lds.get(store.record, 'extra.0', 'branch'),
|
||||
extra1: lds.get(store.record, 'extra.1'),
|
||||
extra2: lds.get(store.record, 'extra.2'),
|
||||
versions: {},
|
||||
host_ids: store.record['app_host_ids'].concat()
|
||||
}
|
||||
}
|
||||
export default observer(function () {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [fetching, setFetching] = useState(true);
|
||||
const [git_type, setGitType] = useState(lds.get(store.record, 'extra.0', 'branch'));
|
||||
const [extra1, setExtra1] = useState(lds.get(store.record, 'extra.1'));
|
||||
const [extra2, setExtra2] = useState(lds.get(store.record, 'extra.2'));
|
||||
const [versions, setVersions] = useState({});
|
||||
const [host_ids, setHostIds] = useState(lds.clone(store.record.app_host_ids));
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchVersions();
|
||||
useEffect(() => {
|
||||
fetchVersions();
|
||||
if (hostStore.records.length === 0) {
|
||||
hostStore.fetchRecords()
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (extra1 === undefined) {
|
||||
const {branches, tags} = versions;
|
||||
let [extra1, extra2] = [undefined, undefined];
|
||||
if (git_type === 'branch') {
|
||||
if (branches) {
|
||||
extra1 = _getDefaultBranch(branches);
|
||||
extra2 = lds.get(branches[extra1], '0.id')
|
||||
}
|
||||
} else {
|
||||
if (tags) {
|
||||
extra1 = lds.get(Object.keys(tags), 0)
|
||||
}
|
||||
}
|
||||
setExtra1(extra1)
|
||||
setExtra2(extra2)
|
||||
}
|
||||
}, [versions, git_type, extra1])
|
||||
|
||||
function fetchVersions() {
|
||||
setFetching(true);
|
||||
http.get(`/api/app/deploy/${store.record.deploy_id}/versions/`, {timeout: 120000})
|
||||
.then(res => setVersions(res))
|
||||
.finally(() => setFetching(false))
|
||||
}
|
||||
|
||||
fetchVersions = () => {
|
||||
this.setState({fetching: true});
|
||||
http.get(`/api/app/deploy/${store.record.deploy_id}/versions/`, {timeout: 120000})
|
||||
.then(res => {
|
||||
this.setState({versions: res}, this._initExtra1);
|
||||
})
|
||||
.finally(() => this.setState({fetching: false}))
|
||||
};
|
||||
|
||||
_getDefaultBranch = (branches) => {
|
||||
function _getDefaultBranch(branches) {
|
||||
branches = Object.keys(branches);
|
||||
let branch = branches[0];
|
||||
for (let item of store.records) {
|
||||
|
@ -56,149 +68,117 @@ class Ext1Form extends React.Component {
|
|||
}
|
||||
}
|
||||
return branch
|
||||
};
|
||||
}
|
||||
|
||||
_initExtra1 = () => {
|
||||
if (this.isReady === true || this.state.extra1 === undefined) {
|
||||
const {git_type, versions: {branches, tags}} = this.state;
|
||||
let [extra1, extra2] = [undefined, undefined];
|
||||
if (git_type === 'branch') {
|
||||
if (branches) {
|
||||
extra1 = this._getDefaultBranch(branches);
|
||||
extra2 = lds.get(branches[extra1], '0.id')
|
||||
}
|
||||
} else {
|
||||
if (tags) {
|
||||
extra1 = lds.get(Object.keys(tags), 0)
|
||||
}
|
||||
}
|
||||
this.setState({extra1, extra2})
|
||||
} else {
|
||||
this.isReady = true
|
||||
}
|
||||
};
|
||||
function switchType(v) {
|
||||
setExtra1(undefined);
|
||||
setGitType(v)
|
||||
}
|
||||
|
||||
switchType = (v) => {
|
||||
this.setState({git_type: v, extra1: undefined}, this._initExtra1)
|
||||
};
|
||||
|
||||
switchExtra1 = (v) => {
|
||||
let {git_type, extra2, versions: {branches}} = this.state;
|
||||
function switchExtra1(v) {
|
||||
setExtra1(v)
|
||||
if (git_type === 'branch') {
|
||||
extra2 = lds.get(branches[v], '0.id')
|
||||
setExtra2(lds.get(versions.branches[v], '0.id'))
|
||||
}
|
||||
this.setState({extra1: v, extra2})
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
if (this.state.host_ids.length === 0) {
|
||||
function handleSubmit() {
|
||||
if (host_ids.length === 0) {
|
||||
return message.error('请至少选择一个要发布的目标主机')
|
||||
}
|
||||
this.setState({loading: true});
|
||||
const {git_type, extra1, extra2} = this.state;
|
||||
const formData = this.props.form.getFieldsValue();
|
||||
setLoading(true);
|
||||
const formData = form.getFieldsValue();
|
||||
formData['id'] = store.record.id;
|
||||
formData['deploy_id'] = store.record.deploy_id;
|
||||
formData['host_ids'] = this.state.host_ids;
|
||||
formData['host_ids'] = host_ids;
|
||||
formData['extra'] = [git_type, extra1, extra2];
|
||||
http.post('/api/deploy/request/', formData)
|
||||
.then(res => {
|
||||
message.success('操作成功');
|
||||
store.ext1Visible = false;
|
||||
store.fetchRecords()
|
||||
}, () => this.setState({loading: false}))
|
||||
};
|
||||
}, () => setLoading(false))
|
||||
}
|
||||
|
||||
handleChange = (id) => {
|
||||
const host_ids = this.state.host_ids;
|
||||
function handleChange(id) {
|
||||
const index = host_ids.indexOf(id);
|
||||
if (index === -1) {
|
||||
this.setState({host_ids: [id, ...host_ids]})
|
||||
setHostIds([id, ...host_ids])
|
||||
} else {
|
||||
host_ids.splice(index, 1);
|
||||
this.setState({host_ids})
|
||||
setHostIds(host_ids)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const info = store.record;
|
||||
const {host_ids, git_type, extra1, extra2, fetching, versions: {branches, tags}} = this.state;
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={800}
|
||||
maskClosable={false}
|
||||
title="新建发布申请"
|
||||
onCancel={() => store.ext1Visible = false}
|
||||
confirmLoading={this.state.loading}
|
||||
onOk={this.handleSubmit}>
|
||||
<Form labelCol={{span: 5}} wrapperCol={{span: 17}}>
|
||||
<Form.Item required label="申请标题">
|
||||
{getFieldDecorator('name', {initialValue: info['name']})(
|
||||
<Input placeholder="请输入申请标题"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item required label="选择分支/标签/版本" extra={<span>
|
||||
const {branches, tags} = versions;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={800}
|
||||
maskClosable={false}
|
||||
title="新建发布申请"
|
||||
onCancel={() => store.ext1Visible = false}
|
||||
confirmLoading={loading}
|
||||
onOk={handleSubmit}>
|
||||
<Form form={form} initialValues={store.record} labelCol={{span: 5}} wrapperCol={{span: 17}}>
|
||||
<Form.Item required name="name" label="申请标题">
|
||||
<Input placeholder="请输入申请标题"/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="选择分支/标签/版本" style={{marginBottom: 12}} extra={<span>
|
||||
根据网络情况,首次刷新可能会很慢,请耐心等待。
|
||||
<a target="_blank" rel="noopener noreferrer"
|
||||
href="https://spug.dev/docs/install-error/#%E6%96%B0%E5%BB%BA%E5%B8%B8%E8%A7%84%E5%8F%91%E5%B8%83%E7%94%B3%E8%AF%B7-git-clone-%E9%94%99%E8%AF%AF">clone 失败?</a>
|
||||
</span>}>
|
||||
<Col span={19}>
|
||||
<Input.Group compact>
|
||||
<Select value={git_type} onChange={this.switchType} style={{width: 100}}>
|
||||
<Select.Option value="branch">Branch</Select.Option>
|
||||
<Select.Option value="tag">Tag</Select.Option>
|
||||
</Select>
|
||||
<Select
|
||||
showSearch
|
||||
style={{width: 320}}
|
||||
value={extra1}
|
||||
placeholder="请稍等"
|
||||
onChange={this.switchExtra1}
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}>
|
||||
{git_type === 'branch' ? (
|
||||
Object.keys(branches || {}).map(b => <Select.Option key={b} value={b}>{b}</Select.Option>)
|
||||
) : (
|
||||
Object.entries(tags || {}).map(([tag, info]) => (
|
||||
<Select.Option key={tag} value={tag}>{`${tag} ${info.author} ${info.message}`}</Select.Option>
|
||||
))
|
||||
)}
|
||||
</Select>
|
||||
</Input.Group>
|
||||
</Col>
|
||||
<Col span={4} offset={1} style={{textAlign: 'center'}}>
|
||||
{fetching ? <Icon type="loading" style={{fontSize: 18, color: '#1890ff'}}/> :
|
||||
<Button type="link" icon="sync" disabled={fetching} onClick={this.fetchVersions}>刷新</Button>
|
||||
}
|
||||
</Col>
|
||||
</Form.Item>
|
||||
{git_type === 'branch' && (
|
||||
<Form.Item required label="选择Commit ID">
|
||||
<Select value={extra2} placeholder="请选择" onChange={v => this.setState({extra2: v})}>
|
||||
{extra1 && branches ? branches[extra1].map(item => (
|
||||
<Select.Option
|
||||
key={item.id}>{item.id.substr(0, 6)} {item['date']} {item['author']} {item['message']}</Select.Option>
|
||||
)) : null}
|
||||
<Form.Item style={{display: 'inline-block', marginBottom: 0, width: '450px'}}>
|
||||
<Input.Group compact>
|
||||
<Select value={git_type} onChange={switchType} style={{width: 100}}>
|
||||
<Select.Option value="branch">Branch</Select.Option>
|
||||
<Select.Option value="tag">Tag</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="备注信息">
|
||||
{getFieldDecorator('desc', {initialValue: info['desc']})(
|
||||
<Input placeholder="请输入备注信息"/>
|
||||
)}
|
||||
<Select
|
||||
showSearch
|
||||
style={{width: 350}}
|
||||
value={extra1}
|
||||
placeholder="请稍等"
|
||||
onChange={switchExtra1}
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}>
|
||||
{git_type === 'branch' ? (
|
||||
Object.keys(branches || {}).map(b => <Select.Option key={b} value={b}>{b}</Select.Option>)
|
||||
) : (
|
||||
Object.entries(tags || {}).map(([tag, info]) => (
|
||||
<Select.Option key={tag} value={tag}>{`${tag} ${info.author} ${info.message}`}</Select.Option>
|
||||
))
|
||||
)}
|
||||
</Select>
|
||||
</Input.Group>
|
||||
</Form.Item>
|
||||
<Form.Item required label="发布目标主机" help="通过点击主机名称自由选择本次发布的主机。">
|
||||
{info['app_host_ids'].map(id => (
|
||||
<Tag.CheckableTag key={id} checked={host_ids.includes(id)} onChange={() => this.handleChange(id)}>
|
||||
{lds.get(hostStore.idMap, `${id}.name`)}({lds.get(hostStore.idMap, `${id}.hostname`)}:{lds.get(hostStore.idMap, `${id}.port`)})
|
||||
</Tag.CheckableTag>
|
||||
))}
|
||||
<Form.Item style={{display: 'inline-block', width: 82, textAlign: 'center', marginBottom: 0}}>
|
||||
{fetching ? <LoadingOutlined style={{fontSize: 18, color: '#1890ff'}}/> :
|
||||
<Button type="link" icon={<SyncOutlined/>} disabled={fetching} onClick={fetchVersions}>刷新</Button>
|
||||
}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create()(Ext1Form)
|
||||
</Form.Item>
|
||||
{git_type === 'branch' && (
|
||||
<Form.Item required label="选择Commit ID">
|
||||
<Select value={extra2} placeholder="请选择" onChange={v => setExtra2(v)}>
|
||||
{extra1 && branches ? branches[extra1].map(item => (
|
||||
<Select.Option
|
||||
key={item.id}>{item.id.substr(0, 6)} {item['date']} {item['author']} {item['message']}</Select.Option>
|
||||
)) : null}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item name="desc" label="备注信息">
|
||||
<Input placeholder="请输入备注信息"/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="发布目标主机" help="通过点击主机名称自由选择本次发布的主机。">
|
||||
{store.record['app_host_ids'].map(id => (
|
||||
<Tag.CheckableTag key={id} checked={host_ids.includes(id)} onChange={() => handleChange(id)}>
|
||||
{lds.get(hostStore.idMap, `${id}.name`)}({lds.get(hostStore.idMap, `${id}.hostname`)}:{lds.get(hostStore.idMap, `${id}.port`)})
|
||||
</Tag.CheckableTag>
|
||||
))}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
})
|
|
@ -6,7 +6,8 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Modal, Button, Menu, Spin, Icon, Input, Tooltip } from 'antd';
|
||||
import { Modal, Button, Menu, Spin, Input, Tooltip } from 'antd';
|
||||
import { OrderedListOutlined, BuildOutlined } from '@ant-design/icons';
|
||||
import store from './store';
|
||||
import styles from './index.module.css';
|
||||
import envStore from 'pages/config/environment/store';
|
||||
|
@ -100,8 +101,8 @@ class SelectApp extends React.Component {
|
|||
<Button type="primary" className={styles.appBlock} onClick={() => this.handleClick(item)}>
|
||||
<div ref={el => this.handleRef(el, item.id)}
|
||||
style={{width: 135, overflow: 'hidden', textOverflow: 'ellipsis'}}>
|
||||
<Icon type={item.extend === '1' ? 'ordered-list' : 'build'}
|
||||
style={{marginRight: 10}}/>{item['app_name']}
|
||||
{item.extend === '1' ? <OrderedListOutlined/> : <BuildOutlined/>}
|
||||
<span style={{marginLeft: 8}}>{item.app_name}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Table, Modal, Icon, Popover, Tag, message } from 'antd';
|
||||
import { BranchesOutlined, BuildOutlined, TagOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Radio, Modal, Popover, Tag, message } from 'antd';
|
||||
import { http, hasPermission } from 'libs';
|
||||
import { Action } from "components";
|
||||
import { Action, AuthButton, TableCard } from 'components';
|
||||
import store from './store';
|
||||
|
||||
@observer
|
||||
|
@ -25,7 +26,12 @@ class ComTable extends React.Component {
|
|||
|
||||
columns = [{
|
||||
title: '申请标题',
|
||||
dataIndex: 'name',
|
||||
render: info => (
|
||||
<div>
|
||||
{info.type === '2' && <Tag color="#f50">R</Tag>}
|
||||
{info.name}
|
||||
</div>
|
||||
)
|
||||
}, {
|
||||
title: '应用',
|
||||
dataIndex: 'app_name',
|
||||
|
@ -38,18 +44,24 @@ class ComTable extends React.Component {
|
|||
if (info['app_extend'] === '1') {
|
||||
const [type, ext1, ext2] = info.extra;
|
||||
if (type === 'branch') {
|
||||
return <React.Fragment>
|
||||
<Icon type="branches"/> {ext1}#{ext2.substr(0, 6)}
|
||||
</React.Fragment>
|
||||
return (
|
||||
<React.Fragment>
|
||||
<BranchesOutlined/> {ext1}#{ext2.substr(0, 6)}
|
||||
</React.Fragment>
|
||||
)
|
||||
} else {
|
||||
return <React.Fragment>
|
||||
<Icon type="tag"/> {ext1}
|
||||
</React.Fragment>
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TagOutlined/> {ext1}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return <React.Fragment>
|
||||
<Icon type="build"/> {info.extra[0]}
|
||||
</React.Fragment>
|
||||
return (
|
||||
<React.Fragment>
|
||||
<BuildOutlined/> {info.extra[0]}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
}, {
|
||||
|
@ -64,13 +76,13 @@ class ComTable extends React.Component {
|
|||
<span style={{color: '#1890ff'}}>{info['status_alias']}</span>
|
||||
</Popover>
|
||||
} else if (info.status === '2') {
|
||||
return <Tag color="blue">{info['status_alias']}</Tag>
|
||||
return <Tag color="orange">{info['status_alias']}</Tag>
|
||||
} else if (info.status === '3') {
|
||||
return <Tag color="green">{info['status_alias']}</Tag>
|
||||
} else if (info.status === '-3') {
|
||||
return <Tag color="red">{info['status_alias']}</Tag>
|
||||
} else {
|
||||
return <Tag>{info['status_alias']}</Tag>
|
||||
return <Tag color="blue">{info['status_alias']}</Tag>
|
||||
}
|
||||
}
|
||||
}, {
|
||||
|
@ -80,6 +92,11 @@ class ComTable extends React.Component {
|
|||
title: '申请时间',
|
||||
dataIndex: 'created_at',
|
||||
sorter: (a, b) => a['created_at'].localeCompare(b['created_at'])
|
||||
}, {
|
||||
title: '备注',
|
||||
dataIndex: 'desc',
|
||||
ellipsis: true,
|
||||
hide: true
|
||||
}, {
|
||||
title: '操作',
|
||||
className: hasPermission('deploy.request.do|deploy.request.edit|deploy.request.approve|deploy.request.del') ? null : 'none',
|
||||
|
@ -191,10 +208,27 @@ class ComTable extends React.Component {
|
|||
}
|
||||
}
|
||||
return (
|
||||
<Table
|
||||
<TableCard
|
||||
rowKey="id"
|
||||
title="申请列表"
|
||||
loading={store.isFetching}
|
||||
dataSource={data}
|
||||
onReload={store.fetchRecords}
|
||||
actions={[
|
||||
<AuthButton
|
||||
auth="deploy.request.add"
|
||||
type="primary"
|
||||
icon={<PlusOutlined/>}
|
||||
onClick={() => store.addVisible = true}>新建申请</AuthButton>,
|
||||
<Radio.Group value={store.f_status} onChange={e => store.f_status = e.target.value}>
|
||||
<Radio.Button value="all">全部({store.counter['all'] || 0})</Radio.Button>
|
||||
<Radio.Button value="0">待审核({store.counter['0'] || 0})</Radio.Button>
|
||||
<Radio.Button value="1">待发布({store.counter['1'] || 0})</Radio.Button>
|
||||
<Radio.Button value="3">发布成功({store.counter['3'] || 0})</Radio.Button>
|
||||
<Radio.Button value="-3">发布异常({store.counter['-3'] || 0})</Radio.Button>
|
||||
<Radio.Button value="99">其他({store.counter['99'] || 0})</Radio.Button>
|
||||
</Radio.Group>
|
||||
]}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
showLessItems: true,
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Button, Select, DatePicker, Radio, Row, Col, Modal, Form, Input, message } from 'antd';
|
||||
import { SearchForm, AuthFragment, AuthCard } from 'components';
|
||||
import { ExclamationCircleOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
import { Form, Select, DatePicker, Modal, Input, message } from 'antd';
|
||||
import { SearchForm, AuthDiv, AuthButton, Breadcrumb } from 'components';
|
||||
import SelectApp from './SelectApp';
|
||||
import Ext1Form from './Ext1Form';
|
||||
import Ext2Form from './Ext2Form';
|
||||
|
@ -39,15 +40,15 @@ class Index extends React.Component {
|
|||
|
||||
handleBatchDel = () => {
|
||||
Modal.confirm({
|
||||
icon: 'exclamation-circle',
|
||||
icon: <ExclamationCircleOutlined/>,
|
||||
title: '批量删除发布申请',
|
||||
content: (
|
||||
<Form>
|
||||
<Form.Item label="截止日期" help={<div>将删除截止日期<span style={{color: 'red'}}>之前</span>的所有发布申请记录。</div>}>
|
||||
<Form layout="vertical" style={{marginTop: 24}}>
|
||||
<Form.Item label="截止日期 :" help={<div>将删除截止日期<span style={{color: 'red'}}>之前</span>的所有发布申请记录。</div>}>
|
||||
<DatePicker style={{width: 200}} placeholder="请输入"
|
||||
onChange={val => this.setState({expire: val.format('YYYY-MM-DD')})}/>
|
||||
</Form.Item>
|
||||
<Form.Item label="保留记录" help="每个应用每个环境仅保留最新的N条发布申请,优先级高于截止日期">
|
||||
<Form.Item label="保留记录 :" help="每个应用每个环境仅保留最新的N条发布申请,优先级高于截止日期">
|
||||
<Input allowClear style={{width: 200}} placeholder="请输入保留个数"
|
||||
onChange={e => this.setState({count: e.target.value})}/>
|
||||
</Form.Item>
|
||||
|
@ -66,7 +67,12 @@ class Index extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<AuthCard auth="deploy.request.view">
|
||||
<AuthDiv auth="deploy.request.view">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>应用发布</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>发布申请</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<SearchForm>
|
||||
<SearchForm.Item span={6} title="发布环境">
|
||||
<Select allowClear value={store.f_env_id} onChange={v => store.f_env_id = v} placeholder="请选择">
|
||||
|
@ -88,39 +94,19 @@ class Index extends React.Component {
|
|||
onChange={store.updateDate}/>
|
||||
</SearchForm.Item>
|
||||
<SearchForm.Item span={4} style={{textAlign: 'right'}}>
|
||||
<Button type="primary" icon="sync" onClick={store.fetchRecords}>刷新</Button>
|
||||
<AuthButton
|
||||
auth="deploy.request.del"
|
||||
type="danger"
|
||||
icon={<DeleteOutlined/>}
|
||||
onClick={this.handleBatchDel}>批量删除</AuthButton>
|
||||
</SearchForm.Item>
|
||||
</SearchForm>
|
||||
<Row style={{marginBottom: 16}}>
|
||||
<Col span={16}>
|
||||
<Radio.Group value={store.f_status} onChange={e => store.f_status = e.target.value}>
|
||||
<Radio.Button value="all">全部({store.counter['all'] || 0})</Radio.Button>
|
||||
<Radio.Button value="0">待审核({store.counter['0'] || 0})</Radio.Button>
|
||||
<Radio.Button value="1">待发布({store.counter['1'] || 0})</Radio.Button>
|
||||
<Radio.Button value="3">发布成功({store.counter['3'] || 0})</Radio.Button>
|
||||
<Radio.Button value="-3">发布异常({store.counter['-3'] || 0})</Radio.Button>
|
||||
<Radio.Button value="99">其他({store.counter['99'] || 0})</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Col>
|
||||
<Col span={8} style={{textAlign: 'right'}}>
|
||||
<AuthFragment auth="deploy.request.del">
|
||||
<Button type="primary" icon="delete" onClick={this.handleBatchDel}>批量删除</Button>
|
||||
</AuthFragment>
|
||||
<AuthFragment auth="deploy.request.add">
|
||||
<Button
|
||||
type="primary"
|
||||
icon="plus"
|
||||
onClick={() => store.addVisible = true}
|
||||
style={{marginLeft: 20}}>新建发布申请</Button>
|
||||
</AuthFragment>
|
||||
</Col>
|
||||
</Row>
|
||||
<ComTable/>
|
||||
{store.addVisible && <SelectApp/>}
|
||||
{store.ext1Visible && <Ext1Form/>}
|
||||
{store.ext2Visible && <Ext2Form/>}
|
||||
{store.approveVisible && <Approve/>}
|
||||
</AuthCard>
|
||||
</AuthDiv>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ class Store {
|
|||
};
|
||||
|
||||
updateDate = (data) => {
|
||||
if (data.length === 2) {
|
||||
if (data && data.length === 2) {
|
||||
this.f_s_date = data[0].format('YYYY-MM-DD');
|
||||
this.f_e_date = data[1].format('YYYY-MM-DD')
|
||||
} else {
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import { makeRoute } from "../../libs/router";
|
||||
import app from './app';
|
||||
import request from './request';
|
||||
import doExt1Index from './do/Ext1Index';
|
||||
import doExt2Index from './do/Ext2Index';
|
||||
|
||||
|
||||
export default [
|
||||
makeRoute('/app', app),
|
||||
makeRoute('/request', request),
|
||||
makeRoute('/do/ext1/:id', doExt1Index),
|
||||
makeRoute('/do/ext2/:id', doExt2Index),
|
||||
makeRoute('/do/ext1/:id/:log', doExt1Index),
|
||||
makeRoute('/do/ext2/:id/:log', doExt2Index),
|
||||
]
|
|
@ -1,14 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import { makeRoute } from "../../libs/router";
|
||||
import Template from './template';
|
||||
import Task from './task';
|
||||
|
||||
|
||||
export default [
|
||||
makeRoute('/template', Template),
|
||||
makeRoute('/task', Task),
|
||||
]
|
|
@ -1,11 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import { lazy } from 'react';
|
||||
import { makeRoute } from 'libs/router';
|
||||
|
||||
export default [
|
||||
makeRoute('', lazy(() => import('./index'))),
|
||||
]
|
|
@ -64,7 +64,7 @@ export default observer(function () {
|
|||
}
|
||||
|
||||
const ConfirmForm = (props) => (
|
||||
<Form>
|
||||
<Form layout="vertical" style={{marginTop: 24}}>
|
||||
<Form.Item required label="授权密码" help={`用户 ${props.username} 的密码, 该密码仅做首次验证使用,不会存储该密码。`}>
|
||||
<Input.Password onChange={e => setPassword(e.target.value)}/>
|
||||
</Form.Item>
|
||||
|
|
|
@ -19,7 +19,7 @@ export default observer(function () {
|
|||
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>主机管理</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<SearchForm style={{marginBottom: 16}}>
|
||||
<SearchForm>
|
||||
<SearchForm.Item span={6} title="主机类别">
|
||||
<Select allowClear placeholder="请选择" value={store.f_zone} onChange={v => store.f_zone = v}>
|
||||
{store.zones.map(item => (
|
||||
|
|
|
@ -16,19 +16,29 @@ import {
|
|||
SettingOutlined
|
||||
} from '@ant-design/icons';
|
||||
import HomeIndex from './pages/home';
|
||||
|
||||
import HostIndex from './pages/host';
|
||||
|
||||
import ExecTask from './pages/exec/task';
|
||||
import ExecTemplate from './pages/exec/template';
|
||||
|
||||
import DeployApp from './pages/deploy/app';
|
||||
import DeployRequest from './pages/deploy/request';
|
||||
import DoExt1Index from './pages/deploy/do/Ext1Index';
|
||||
import DoExt2Index from './pages/deploy/do/Ext2Index';
|
||||
|
||||
import ScheduleIndex from './pages/schedule';
|
||||
|
||||
import ConfigEnvironment from './pages/config/environment';
|
||||
import ConfigService from './pages/config/service';
|
||||
import ConfigApp from './pages/config/app';
|
||||
|
||||
import MonitorIndex from './pages/monitor';
|
||||
|
||||
import AlarmIndex from './pages/alarm/alarm';
|
||||
import AlarmGroup from './pages/alarm/group';
|
||||
import AlarmContact from './pages/alarm/contact';
|
||||
|
||||
import SystemAccount from './pages/system/account';
|
||||
import SystemRole from './pages/system/role';
|
||||
import SystemSetting from './pages/system/setting';
|
||||
|
@ -49,6 +59,10 @@ export default [
|
|||
icon: <FlagOutlined/>, title: '应用发布', auth: 'deploy.app.view|deploy.request.view', child: [
|
||||
{title: '应用管理', auth: 'deploy.app.view', path: '/deploy/app', component: DeployApp},
|
||||
{title: '发布申请', auth: 'deploy.request.view', path: '/deploy/request', component: DeployRequest},
|
||||
{path: '/deploy/do/ext1/:id', component: DoExt1Index},
|
||||
{path: '/deploy/do/ext2/:id', component: DoExt2Index},
|
||||
{path: '/deploy/do/ext1/:id/:log', component: DoExt1Index},
|
||||
{path: '/deploy/do/ext2/:id/:log', component: DoExt2Index},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue