Component module related

This commit is contained in:
hanxie
2020-09-28 19:37:33 +08:00
parent 6863ac02a1
commit 30127f8a3e
14 changed files with 728 additions and 318 deletions

View File

@@ -42,12 +42,6 @@ export default defineConfig({
path: `/ApplicationList`,
component: './ApplicationList',
},
{
name: 'ApplicationList.ApplicationListDetail',
hideInMenu: true,
path: '/ApplicationList/ApplicationListDetail',
component: './ApplicationList/ApplicationListDetail',
},
{
name: 'ApplicationList.WorkloadDetail',
icon: 'smile',
@@ -62,12 +56,6 @@ export default defineConfig({
component: './Traits/Detail',
hideInMenu: true,
},
{
name: 'ApplicationList.CreateApplication',
hideInMenu: true,
path: '/ApplicationList/CreateApplication',
component: './ApplicationList/CreateApplication',
},
{
name: 'ApplicationList.Components',
hideInMenu: true,

View File

@@ -8,7 +8,7 @@
export default {
dev: {
'/api': {
target: 'http://30.11.171.29:38081/',
target: 'http://30.11.171.39:38081/',
changeOrigin: true,
},
},

View File

@@ -30,6 +30,7 @@ class Trait extends React.Component {
this.state = {
visible: false,
selectValue: null,
compList: [],
};
}
@@ -37,6 +38,19 @@ class Trait extends React.Component {
this.getInitialData();
}
shouldComponentUpdate(nextProps) {
if (nextProps.currentEnv === this.props.currentEnv) {
return true;
}
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
}
getInitialData = async () => {
if (this.props.currentEnv) {
await this.props.dispatch({
@@ -71,7 +85,12 @@ class Trait extends React.Component {
};
const submitData = this.formRefStep2.current.getFieldValue();
Object.keys(submitData).forEach((currentKey) => {
if (currentKey !== 'name' && currentKey !== 'appName' && submitData[currentKey]) {
if (
currentKey !== 'name' &&
currentKey !== 'appName' &&
currentKey !== 'compName' &&
submitData[currentKey]
) {
submitObj.flags.push({
name: currentKey,
value: submitData[currentKey].toString(),
@@ -79,13 +98,14 @@ class Trait extends React.Component {
}
});
const { currentEnv: envName } = this.props;
const { appName } = submitData;
if (envName && appName) {
const { appName, compName } = submitData;
if (envName && appName && compName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
compName,
params: submitObj,
},
});
@@ -96,11 +116,8 @@ class Trait extends React.Component {
message.success(res);
const { history } = this.props.propsObj;
history.push({
pathname: '/ApplicationList/ApplicationListDetail',
state: {
appName,
envName,
},
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
});
}
}
@@ -113,10 +130,30 @@ class Trait extends React.Component {
});
};
onChange = (value) => {
onChange = async (value) => {
this.setState({
selectValue: value,
compList: [],
});
const res = await this.props.dispatch({
type: 'applist/getAppDetail',
payload: {
envName: this.props.currentEnv,
appName: value,
},
});
if (res) {
const compData = _.get(res, 'components', []);
const compList = [];
compData.forEach((item) => {
compList.push({
compName: item.name,
});
});
this.setState({
compList,
});
}
};
onSearch = () => {};
@@ -131,7 +168,11 @@ class Trait extends React.Component {
}
});
}
const appList = _.get(this.props, 'returnObj', []);
let appList = _.get(this.props, 'returnObj', []);
if (!appList) {
appList = [];
}
const { compList = [] } = this.state;
return (
<div>
<div className="breadCrumb">
@@ -212,13 +253,12 @@ class Trait extends React.Component {
initialValues={initialObj}
>
<Form.Item
label="Target"
label="App"
name="appName"
rules={[{ required: true, message: 'Please Select a Application!' }]}
>
<Select
showSearch
allowClear
value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Application"
@@ -230,10 +270,34 @@ class Trait extends React.Component {
}
>
{appList.length ? (
appList.map((item) => {
appList.map((item, index) => {
return (
<Option key={item.name} value={item.name}>
{item.name}
<Option key={index.toString()} value={item.app}>
{item.app}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
<Form.Item
label="Component"
name="compName"
rules={[{ required: true, message: 'Please Select a Component!' }]}
>
<Select
allowClear
// value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Component"
>
{compList.length ? (
compList.map((item) => {
return (
<Option key={item.compName} value={item.compName}>
{item.compName}
</Option>
);
})

View File

@@ -1,12 +1,108 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col, Breadcrumb } from 'antd';
import { Button, Row, Col, Modal, Select, Breadcrumb, Form } from 'antd';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
import './index.less';
export default class Workload extends React.PureComponent {
const { Option } = Select;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
returnObj: applist.returnObj,
}))
export default class Workload extends React.Component {
formRefStep2 = React.createRef();
constructor(props) {
super(props);
this.state = {
visible: false,
};
}
componentDidMount() {
this.getInitialData();
}
shouldComponentUpdate(nextProps) {
if (nextProps.currentEnv === this.props.currentEnv) {
return true;
}
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
}
getInitialData = async () => {
if (this.props.currentEnv) {
await this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${this.props.currentEnv}/apps/`,
},
});
}
};
showModal = () => {
this.setState(
{
visible: true,
},
() => {
if (this.formRefStep2.current) {
this.formRefStep2.current.resetFields();
}
},
);
};
handleOk = async () => {
const submitData = await this.formRefStep2.current.validateFields();
const { history } = this.props.propsObj;
history.push({
pathname: `/ApplicationList/${submitData.appName}/createComponent`,
state: {
...submitData,
isCreate: false,
envName: this.props.currentEnv,
WorkloadType: this.props.propsObj.title,
},
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
onChange = () => {};
onSearch = () => {};
render() {
const { btnValue, pathname, title, crdInfo, state, settings, btnIsShow } = this.props.propsObj;
const { btnValue, title, crdInfo, settings, btnIsShow } = this.props.propsObj;
let appList = _.get(this.props, 'returnObj', []);
if (!appList) {
appList = [];
}
return (
<div>
<div className="breadCrumb">
@@ -56,13 +152,73 @@ export default class Workload extends React.PureComponent {
);
})}
</div>
<Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
{/* <Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Button type="primary" className="create-button">
{btnValue}
</Button>
</Link>
</Link> */}
<Button
type="primary"
className="create-button"
onClick={() => this.showModal()}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{btnValue}
</Button>
</Col>
</Row>
<Modal
title="Add Component"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Next
</Button>,
]}
>
<Form
labelAlign="left"
{...layout}
ref={this.formRefStep2}
name="control-ref"
className="traitItem"
>
<Form.Item
label="App"
name="appName"
rules={[{ required: true, message: 'Please Select a Application!' }]}
>
<Select
showSearch
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{appList.length ? (
appList.map((item, index) => {
return (
<Option key={index.toString()} value={item.app}>
{item.app}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
</Form>
</Modal>
</PageContainer>
</div>
);

View File

@@ -85,3 +85,19 @@ ol {
.ant-breadcrumb a:hover {
color: #1b58f4 !important;
}
// 对齐
.ant-form-item-label > label::before {
display: inline-block;
width: 7.09px;
height: 14px;
margin-right: 4px;
color: rgb(255, 77, 79);
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '';
}
.ant-spin-nested-loading {
height: calc(100% - 46px) !important;
}

View File

@@ -0,0 +1,22 @@
import { getComponentList, getComponentDetail, deleteComponent } from '@/services/components.js';
const TestModel = {
namespace: 'components',
state: {},
effects: {
*getComponentList({ payload }, { call }) {
const res = yield call(getComponentList, payload);
return res;
},
*getComponentDetail({ payload }, { call }) {
const res = yield call(getComponentDetail, payload);
return res;
},
*deleteComponent({ payload }, { call }) {
const res = yield call(deleteComponent, payload);
return res;
},
},
reducers: {},
};
export default TestModel;

View File

@@ -15,12 +15,13 @@ class TableList extends React.Component {
constructor(props) {
super(props);
this.state = {
appDetailData: {},
compDetailData: {},
visible: false,
traitList: [],
availableTraitList: [],
envName: '',
appName: '',
compName: '',
};
}
@@ -29,98 +30,118 @@ class TableList extends React.Component {
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.appName !== nextProps.appName) {
this.getInitialData(nextProps.appName);
if (this.props.compName !== nextProps.compName) {
this.getInitialData(nextProps.compName);
}
}
getInitialData = async (nextAppName) => {
let appName = _.get(this.props, 'appName', '');
getInitialData = async (nextCompName) => {
const appName = _.get(this.props, 'appName', '');
const envName = _.get(this.props, 'envName', '');
appName = nextAppName || appName;
if (appName && envName) {
let compName = _.get(this.props, 'compName', '');
compName = nextCompName || compName;
if (appName && envName && compName) {
this.setState({
envName,
appName,
compName,
});
const res = await this.props.dispatch({
type: 'applist/getAppDetail',
type: 'components/getComponentDetail',
payload: {
envName,
appName,
compName,
},
});
if (res) {
this.setState({
appDetailData: res,
compDetailData: res,
});
}
const traits = await this.props.dispatch({
type: 'trait/getTraits',
});
if (traits) {
this.setState({
traitList: traits,
const traits = await this.props.dispatch({
type: 'trait/getTraits',
});
}
const workloadType = _.get(res, 'Workload.workload.kind', '');
if (workloadType && workloadType === 'ContainerizedWorkload') {
this.getAcceptTrait('containerized');
} else if (workloadType && workloadType === 'Deployment') {
this.getAcceptTrait('deployment');
if (traits) {
this.setState({
traitList: traits,
});
}
const workloadType = _.get(res, 'workload.kind', '');
if (workloadType) {
this.getAcceptTrait(workloadType.toLowerCase());
}
// if (workloadType && workloadType === '') {
// this.getAcceptTrait('containerized');
// } else if (workloadType && workloadType === 'Deployment') {
// this.getAcceptTrait('deployment');
// }
}
}
};
getAcceptTrait = (workloadType) => {
const res = this.state.traitList.filter((item) => {
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
}
return false;
});
// getAcceptTrait = (workloadType) => {
// const res = this.state.traitList.filter((item) => {
// if (item.appliesTo) {
// if(item.appliesTo==='*'){
// return true;
// }
// if (item.appliesTo.indexOf(workloadType) !== -1) {
// return true;
// }
// return false;
// }
// return false;
// });
// this.setState(() => ({
// availableTraitList: res,
// }));
// };
getAcceptTrait = () => {
const res = this.state.traitList;
this.setState(() => ({
availableTraitList: res,
}));
};
deleteApp = async (e) => {
deleteComp = async (e) => {
e.stopPropagation();
const { envName } = this.state;
const { appDetailData } = this.state;
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
if (appName && envName) {
const { envName, appName, compName } = this.state;
if (appName && envName && compName) {
const res = await this.props.dispatch({
type: 'applist/deleteApp',
type: 'components/deleteComponent',
payload: {
appName,
envName,
compName,
},
});
if (res) {
message.success(res);
this.props.history.push({ pathname: '/ApplicationList' });
// 删除当前component成功后刷新当前页面
this.props.getInitCompList();
}
}
};
deleteTrait = async (e, item) => {
e.stopPropagation();
const { appName, envName } = this.state;
const { appName, envName, compName } = this.state;
const traitNameObj = _.get(item, 'trait.metadata.annotations', '');
const traitName = traitNameObj['vela.oam.dev/traitDef'] || traitNameObj['trait.oam.dev/name'];
if (traitName && appName && envName) {
if (traitName && appName && envName && compName) {
const res = await this.props.dispatch({
type: 'trait/deleteOneTrait',
payload: {
envName,
appName,
traitName,
compName,
},
});
if (res) {
message.success(res);
this.getInitialData(2);
this.getInitialData(compName);
}
}
};
@@ -151,13 +172,14 @@ class TableList extends React.Component {
});
}
});
const { envName, appName } = this.state;
if (envName && appName) {
const { envName, appName, compName } = this.state;
if (envName && appName && compName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
compName,
params: submitObj,
},
});
@@ -166,7 +188,7 @@ class TableList extends React.Component {
visible: false,
});
message.success(res);
this.getInitialData(2);
this.getInitialData(compName);
}
}
} else {
@@ -193,11 +215,16 @@ class TableList extends React.Component {
};
render() {
const status = _.get(this.state.appDetailData, 'Status', '');
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const Traits = _.get(this.state.appDetailData, 'Traits', []);
const { compDetailData } = this.state;
const status = _.get(compDetailData, 'status', '');
const Workload = _.get(compDetailData, 'workload', {});
const Traits = _.get(compDetailData, 'traits', []);
let containers = {};
containers = _.get(Workload, 'spec.containers[0]', {});
if (_.get(Workload, 'kind', '') === 'Job') {
containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
} else {
containers = _.get(Workload, 'spec.podSpec.containers[0]', {});
}
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const colorObj = {
@@ -238,6 +265,21 @@ class TableList extends React.Component {
</p>
<p className="title">Settings:</p>
<Row>
{_.get(Workload, 'kind', '') === 'Job' ? (
<Fragment>
<Col span="8">
<p>count</p>
</Col>
<Col span="16">
<p>
{_.get(Workload, 'spec.completions', '') ||
_.get(Workload, 'spec.parallelism', '')}
</p>
</Col>
</Fragment>
) : (
<Fragment />
)}
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
@@ -280,8 +322,8 @@ class TableList extends React.Component {
</Row>
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
title="Are you sure delete this component?"
onConfirm={(e) => this.deleteComp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
@@ -295,11 +337,7 @@ class TableList extends React.Component {
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
let traitType = 1;
const spec = _.get(traitItem, 'spec', {});
if (traitItem.kind === 'Ingress') {
traitType = 2;
}
return (
<div
className="summaryBox"
@@ -338,53 +376,33 @@ class TableList extends React.Component {
</Row>
<p className="title">Properties:</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>domain</p>
</Col>
<Col span="16">
<p>{_.get(spec, 'rules[0].host', '')}</p>
</Col>
<Col span="8">
<p>service</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.serviceName',
'',
)}
</p>
</Col>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>
{_.get(
spec,
'rules[0].http.paths[0].backend.servicePort',
'',
)}
</p>
</Col>
</Fragment>
) : (
Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})
)}
{Object.keys(spec).map((currentKey) => {
if (spec[currentKey].constructor === Object) {
const backend = _.get(spec, `${currentKey}`, {});
return Object.keys(backend).map((currentKey1) => {
return (
<Fragment key={currentKey1}>
<Col span="8">
<p>{currentKey1}</p>
</Col>
<Col span="16">
<p>{backend[currentKey1]}</p>
</Col>
</Fragment>
);
});
}
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
</Col>
</Fragment>
);
})}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm

View File

@@ -1,13 +1,14 @@
import React from 'react';
import { Link } from 'umi';
import { Breadcrumb, Button, Menu, Spin } from 'antd';
import { Breadcrumb, Button, Menu, Spin, Popconfirm, message } from 'antd';
import { connect } from 'dva';
import _ from 'lodash';
import './index.less';
import ComponentDetail from '../ComponentDetail/index.jsx';
@connect(({ loading }) => ({
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
}))
class TableList extends React.PureComponent {
constructor(props) {
@@ -15,59 +16,94 @@ class TableList extends React.PureComponent {
this.state = {
envName: '',
componentName: '',
defaultSelectedKeys: 'a1',
defaultSelectedKeys: '',
appName: '',
compList: [],
};
}
UNSAFE_componentWillMount() {
componentDidMount() {
this.getInitData();
}
getInitData = async () => {
let appName = '';
let envName = '';
let description = '';
let envName = this.props.currentEnv;
if (this.props.location.state) {
appName = _.get(this.props, 'location.state.appName', '');
envName = _.get(this.props, 'location.state.envName', '');
description = _.get(this.props, 'location.state.description', '');
envName = _.get(this.props, 'location.state.envName', this.props.currentEnv);
sessionStorage.setItem('appName', appName);
sessionStorage.setItem('description', description);
sessionStorage.setItem('envName', envName);
} else {
appName = sessionStorage.getItem('appName');
description = sessionStorage.getItem('description');
envName = sessionStorage.getItem('envName');
}
this.setState({
appName,
envName,
componentName: appName,
});
if (appName === 'test33') {
this.setState({
defaultSelectedKeys: 'a1',
const res = await this.props.dispatch({
type: 'applist/getAppDetail',
payload: {
envName,
appName,
},
});
if (res) {
const compData = _.get(res, 'components', []);
const compList = [];
compData.forEach((item) => {
compList.push({
compName: item.name,
});
});
} else if (appName === 'test01') {
this.setState({
defaultSelectedKeys: 'c2',
});
} else {
this.setState({
defaultSelectedKeys: 'c3',
});
}
}
changeComponent = ({ key }) => {
if (key === 'a1') {
this.setState({
componentName: 'test33',
});
} else if (key === 'c2') {
this.setState({
componentName: 'test01',
});
} else {
this.setState({
componentName: 'testoo',
compList,
});
if (compList.length) {
this.changeComponent({
key: compList[0].compName,
});
}
}
};
changeComponent = ({ key }) => {
this.setState({
componentName: key,
defaultSelectedKeys: key,
});
};
deleteApp = async (e) => {
e.stopPropagation();
const { envName } = this.state;
const { appName } = this.state;
if (appName && envName) {
const res = await this.props.dispatch({
type: 'applist/deleteApp',
payload: {
appName,
envName,
},
});
if (res) {
message.success(res);
this.props.history.push({ pathname: '/ApplicationList' });
}
}
};
cancel = (e) => {
e.stopPropagation();
};
render() {
const { envName, componentName, defaultSelectedKeys } = this.state;
const { envName, componentName, defaultSelectedKeys, appName, compList } = this.state;
const { loadingAll } = this.props;
return (
<div style={{ height: '100%' }}>
@@ -79,7 +115,7 @@ class TableList extends React.PureComponent {
<Breadcrumb.Item>
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>appname</Breadcrumb.Item>
<Breadcrumb.Item>{appName}</Breadcrumb.Item>
<Breadcrumb.Item>Components</Breadcrumb.Item>
<Breadcrumb.Item>{componentName}</Breadcrumb.Item>
</Breadcrumb>
@@ -91,31 +127,49 @@ class TableList extends React.PureComponent {
mode="inline"
onClick={this.changeComponent}
defaultSelectedKeys={[defaultSelectedKeys]}
selectedKeys={defaultSelectedKeys}
>
<Menu.ItemGroup key="g1" title="Components">
<Menu.Item key="a1">a1(containerized)</Menu.Item>
<Menu.Item key="c2">c2(deploy)</Menu.Item>
<Menu.Item key="c3">c3(webserver)</Menu.Item>
{compList.map((item) => {
return <Menu.Item key={item.compName}>{item.compName}</Menu.Item>;
})}
</Menu.ItemGroup>
</Menu>
<div className="addComp">
<Link
to={{
// pathname: '/ApplicationList/CreateApplication',
pathname: `/ApplicationList/${componentName}/createComponent`,
state: { appName: componentName, envName },
pathname: `/ApplicationList/${appName}/createComponent`,
state: { appName, envName, isCreate: false },
}}
>
add a new comp
</Link>
</div>
</div>
<div className="right">
<div style={{ margin: '10px', float: 'right' }}>
<Button type="primary">Delete App</Button>
{defaultSelectedKeys ? (
<div className="right">
<div className="btn">
<Popconfirm
title="Are you sure delete this app?"
placement="bottom"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button type="primary">Delete App</Button>
</Popconfirm>
</div>
<ComponentDetail
appName={appName}
compName={componentName}
envName={envName}
getInitCompList={this.getInitData}
/>
</div>
<ComponentDetail appName={componentName} envName={envName} />
</div>
) : (
<div style={{ width: '100%', height: '100%', background: '#FFFFFF' }} />
)}
</div>
</Spin>
</div>

View File

@@ -1,6 +1,12 @@
.ant-spin-nested-loading {
height: 100%;
}
.ant-spin-container {
height: 100%;
}
.appComponent {
display: flex;
height: calc(100% - 46px);
height: 100%;
.left {
width: 200px;
background: #fff;
@@ -24,12 +30,19 @@
text-decoration: underline;
background-color: #fff;
border-top: 1px solid #efefef;
border-right: 1px solid #efefef;
border-bottom: 1px solid #efefef;
cursor: pointer;
}
}
.right {
position: relative;
flex: 1;
// clear: both;
.btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 8;
}
}
}

View File

@@ -46,11 +46,12 @@ class TableList extends React.Component {
workloadSettings: [],
step1SubmitObj: {},
step1InitialValues: {
workload_type: '',
workloadType: '',
},
step1Settings: [],
appName: '',
envName: '',
isCreate: '',
};
}
@@ -61,18 +62,23 @@ class TableList extends React.Component {
getInitalData = async () => {
let appName = '';
let envName = '';
let isCreate = '';
if (this.props.location.state) {
appName = _.get(this.props, 'location.state.appName', '');
envName = _.get(this.props, 'location.state.envName', '');
isCreate = _.get(this.props, 'location.state.isCreate', false);
sessionStorage.setItem('appName', appName);
sessionStorage.setItem('envName', envName);
sessionStorage.setItem('isCreate', isCreate);
} else {
appName = sessionStorage.getItem('appName');
envName = sessionStorage.getItem('envName');
isCreate = sessionStorage.getItem('isCreate');
}
this.setState({
appName,
envName,
isCreate,
});
const res = await this.props.dispatch({
type: 'workload/getWorkload',
@@ -98,9 +104,9 @@ class TableList extends React.Component {
WorkloadType = sessionStorage.getItem('WorkloadType');
}
this.formRefStep1.current.setFieldsValue({
workload_type: WorkloadType || this.state.workloadList[0].name,
workloadType: WorkloadType || this.state.workloadList[0].name,
});
this.workloadTypeChange(this.state.workloadList[0].name);
this.workloadTypeChange(WorkloadType || this.state.workloadList[0].name);
}
},
);
@@ -166,27 +172,31 @@ class TableList extends React.Component {
};
createApp = async () => {
const { traitNum } = this.state;
const { traitNum, isCreate } = this.state;
const { step1SubmitObj } = this.state;
if (step1SubmitObj.env_name !== this.props.currentEnv) {
step1SubmitObj.env_name = this.props.currentEnv;
if (isCreate === true || isCreate === 'true') {
step1SubmitObj.envName = this.props.currentEnv;
} else {
step1SubmitObj.envName = this.state.envName;
}
step1SubmitObj.appName = this.state.appName;
const submitObj = _.cloneDeep(step1SubmitObj);
const { workload_name: workloadName } = step1SubmitObj;
const { workloadName, appName } = step1SubmitObj;
submitObj.flags.push({
name: 'name',
value: workloadName.toString(),
});
// 处理数据为提交的格式
if (traitNum.length) {
const { env_name: envName } = step1SubmitObj;
const { envName } = step1SubmitObj;
const step2SubmitObj = [];
traitNum.forEach(({ initialData }) => {
if (initialData.name) {
const initialObj = {
name: initialData.name,
env_name: envName,
workload_name: workloadName,
envName,
workloadName,
appName,
flags: [],
};
Object.keys(initialData).forEach((key) => {
@@ -210,10 +220,9 @@ class TableList extends React.Component {
});
if (res) {
message.success(res);
const { appName, envName } = this.state;
this.props.history.push({
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
state: { appName, envName: step1SubmitObj.envName },
});
}
};
@@ -222,13 +231,13 @@ class TableList extends React.Component {
await this.formRefStep1.current.validateFields();
const currentData = this.formRefStep1.current.getFieldsValue();
const submitObj = {
env_name: this.props.currentEnv,
workload_type: currentData.workload_type,
workload_name: currentData.workload_name,
envName: this.props.currentEnv,
workloadType: currentData.workloadType,
workloadName: currentData.workloadName,
flags: [],
};
Object.keys(currentData).forEach((key) => {
if (key !== 'workload_name' && key !== 'workload_type' && currentData[key]) {
if (key !== 'workloadName' && key !== 'workloadType' && currentData[key]) {
submitObj.flags.push({
name: key,
value: currentData[key].toString(),
@@ -241,15 +250,15 @@ class TableList extends React.Component {
step1Settings: submitObj.flags,
step1SubmitObj: submitObj,
});
this.getAcceptTrait(currentData.workload_type);
this.getAcceptTrait(currentData.workloadType);
};
workloadTypeChange = (value) => {
const content = this.formRefStep1.current.getFieldsValue();
this.formRefStep1.current.resetFields();
const initialObj = {
workload_type: content.workload_type,
workload_name: content.workload_name,
workloadType: content.workloadType,
workloadName: content.workloadName,
};
this.formRefStep1.current.setFieldsValue(initialObj);
const currentWorkloadSetting = this.state.workloadList.filter((item) => {
@@ -283,8 +292,14 @@ class TableList extends React.Component {
getAcceptTrait = (workloadType) => {
const res = this.state.traitList.filter((item) => {
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
if (item.appliesTo) {
if (item.appliesTo === '*') {
return true;
}
if (item.appliesTo.indexOf(workloadType) !== -1) {
return true;
}
return false;
}
return false;
});
@@ -305,7 +320,7 @@ class TableList extends React.Component {
render() {
const { appName, envName } = this.state;
const { current, step1InitialValues, traitNum, workloadSettings } = this.state;
const { current, step1InitialValues, traitNum, workloadSettings, isCreate } = this.state;
let { workloadList } = this.state;
workloadList = Array.isArray(workloadList) ? workloadList : [];
let currentDetail;
@@ -324,7 +339,7 @@ class TableList extends React.Component {
>
<div style={{ padding: '16px 48px 0px 16px' }}>
<Form.Item
name="workload_name"
name="workloadName"
label="Name"
rules={[
{
@@ -341,7 +356,7 @@ class TableList extends React.Component {
<Input />
</Form.Item>
<Form.Item
name="workload_type"
name="workloadType"
label="Workload Type"
rules={[
{
@@ -420,14 +435,24 @@ class TableList extends React.Component {
<Button type="primary" className="floatRightGap" onClick={this.createWorkload}>
Next
</Button>
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
<Button className="floatRightGap">Cancle</Button>
</Link>
{isCreate === true || isCreate === 'true' ? (
<Link
to={{
pathname: `/ApplicationList`,
}}
>
<Button className="floatRightGap">Cancle</Button>
</Link>
) : (
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
<Button className="floatRightGap">Cancle</Button>
</Link>
)}
</div>
</Form>
</div>
@@ -439,11 +464,11 @@ class TableList extends React.Component {
<div className="minBox" style={{ width: '60%' }}>
<div style={{ padding: '0px 48px 0px 16px', width: '60%' }}>
<p style={{ fontSize: '18px', lineHeight: '32px' }}>
Name:<span>{step1InitialValues.workload_name}</span>
Name:<span>{step1InitialValues.workloadName}</span>
</p>
</div>
<div style={{ border: '1px solid #eee', padding: '16px 48px 16px 16px' }}>
<p className="title">{step1InitialValues.workload_type}</p>
<p className="title">{step1InitialValues.workloadType}</p>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>apps/v1</span>
<span
@@ -517,14 +542,14 @@ class TableList extends React.Component {
<div>
<div className="minBox">
<p>
Name:<span>{step1InitialValues.workload_name}</span>
Name:<span>{step1InitialValues.workloadName}</span>
</p>
<Row>
<Col span="11">
<div className="summaryBox1">
<Row>
<Col span="22">
<p className="title">{step1InitialValues.workload_type}</p>
<p className="title">{step1InitialValues.workloadType}</p>
<p>apps/v1</p>
</Col>
</Row>
@@ -611,15 +636,18 @@ class TableList extends React.Component {
<Link to="/ApplicationList">Applications</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
{' '}
{appName}
</Link>
{isCreate === true || isCreate === 'true' ? (
<span>{appName}</span>
) : (
<Link
to={{
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
}}
>
{appName}
</Link>
)}
</Breadcrumb.Item>
<Breadcrumb.Item>createComponent</Breadcrumb.Item>
</Breadcrumb>

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
import { Button, Card, Row, Col, Form, Spin, Empty, Breadcrumb, Modal, Input } from 'antd';
import { Button, Form, Spin, Breadcrumb, Modal, Input, Table, Space, message } from 'antd';
import { connect } from 'dva';
import moment from 'moment';
import './index.less';
@@ -24,6 +23,63 @@ const layout = {
class TableList extends React.Component {
formRef = React.createRef();
columns = [
{
title: 'Name',
dataIndex: 'app',
key: 'app',
render: (text, record) => {
return (
<Link
to={{
pathname: `/ApplicationList/${record.app}/Components`,
state: { appName: record.app, envName: this.props.currentEnv },
}}
>
{text}
</Link>
);
},
},
{
title: 'Status',
dataIndex: 'status',
key: 'status',
render: (text) => {
return text;
},
},
{
title: 'Components',
dataIndex: 'components',
key: 'components',
render: (text) => {
return text;
},
},
{
title: 'Created At',
dataIndex: 'createdTime',
key: 'createdTime',
render: (text) => {
return this.getFormatDate(text);
},
},
{
title: 'Actions',
dataIndex: 'Actions',
key: 'Actions',
render: (text, record) => {
return (
<Space>
<Button onClick={() => this.goToDetail(record)}>Details</Button>
<Button onClick={() => this.deleteApp(record)}>Delete</Button>
</Space>
);
},
},
];
constructor(props) {
super(props);
this.state = {
@@ -32,15 +88,7 @@ class TableList extends React.Component {
}
componentDidMount() {
const { currentEnv } = this.props;
if (currentEnv) {
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${currentEnv}/apps/`,
},
});
}
this.getInitData();
}
shouldComponentUpdate(nextProps) {
@@ -56,6 +104,43 @@ class TableList extends React.Component {
return true;
}
getInitData = () => {
const { currentEnv } = this.props;
if (currentEnv) {
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${currentEnv}/apps/`,
},
});
}
};
goToDetail = (record) => {
this.props.history.push({
pathname: `/ApplicationList/${record.app}/Components`,
state: { appName: record.app, envName: this.props.currentEnv },
});
};
deleteApp = async (record) => {
const appName = record.app;
const envName = this.props.currentEnv;
if (appName && envName) {
const res = await this.props.dispatch({
type: 'applist/deleteApp',
payload: {
appName,
envName,
},
});
if (res) {
message.success(res);
this.getInitData();
}
}
};
showModal = () => {
this.setState({
visible: true,
@@ -63,8 +148,11 @@ class TableList extends React.Component {
};
handleOk = async () => {
await this.formRef.current.validateFields();
// const submitData = await this.formRef.current.validateFields();
const submitData = await this.formRef.current.validateFields();
this.props.history.push({
pathname: `/ApplicationList/${submitData.appName}/createComponent`,
state: { ...submitData, isCreate: true },
});
};
handleCancel = () => {
@@ -91,14 +179,8 @@ class TableList extends React.Component {
render() {
let { loadingAll, returnObj } = this.props;
const { currentEnv } = this.props;
loadingAll = loadingAll || false;
returnObj = returnObj || [];
const colorObj = {
Deployed: 'first1',
Staging: 'first2',
UNKNOWN: 'first3',
};
loadingAll = loadingAll || false;
return (
<div>
<div className="breadCrumb">
@@ -114,74 +196,17 @@ class TableList extends React.Component {
<div className="applist">
<Form name="horizontal_login" layout="inline" onFinish={this.onFinish}>
<Form.Item>
<Link to="/ApplicationList/CreateApplication">
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
</Link>
<Button onClick={this.showModal} type="primary" style={{ marginBottom: 16 }}>
create
</Button>
</Form.Item>
</Form>
</div>
<Row gutter={16}>
{Array.isArray(returnObj) && returnObj.length ? (
returnObj.map((item, index) => {
const { traits = [] } = item;
return (
<Col span={6} onClick={this.gotoDetail} key={index.toString()}>
<Link
to={{
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName: item.name, envName: currentEnv },
}}
>
<Card
title={item.name}
bordered={false}
extra={this.getFormatDate(item.created)}
>
<div className="cardContent">
<div
className="box2"
style={{ height: this.getHeight(traits.length) }}
/>
<div className="box1">
{traits.length ? (
<div className="box3" style={{ width: '30px' }} />
) : (
''
)}
<div
className={['hasPadding', colorObj[item.status] || 'first3'].join(
' ',
)}
>
<ApartmentOutlined style={{ marginRight: '4px' }} />
{item.workload}
</div>
</div>
{traits.map((item1, index1) => {
return (
<div className="box1" key={index1.toString()}>
<div className="box3" style={{ width: '50px' }} />
<div className="other hasPadding">
<BranchesOutlined style={{ marginRight: '4px' }} />
{item1}
</div>
</div>
);
})}
</div>
</Card>
</Link>
</Col>
);
})
) : (
<div style={{ width: '100%', height: '80%' }}>
<Empty />
</div>
)}
</Row>
<Table
rowKey={(record) => record.app + Math.random(1, 100)}
columns={this.columns}
dataSource={returnObj}
/>
</Spin>
<Modal
title="Create Application"
@@ -196,7 +221,7 @@ class TableList extends React.Component {
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="name"
name="appName"
label="Name"
rules={[
{

View File

@@ -58,6 +58,7 @@ class TableList extends React.PureComponent {
btnValue: 'Create',
hrefAddress: '#',
btnIsShow: true,
history: this.props.history,
};
this.setState({
propsObj,

View File

@@ -0,0 +1,22 @@
import request from '@/utils/request';
/*
* GET /api/envs/:envName/apps/:appName/components/ (component list)
* Same as GET /api/envs/:envName/apps/:appName (app description).
*/
export async function getComponentList({ envName, appName }) {
return request(`/api/envs/${envName}/apps/${appName}/components/`);
}
/*
* GET /api/envs/:envName/apps/:appName/components/:compName (component details)
*/
export async function getComponentDetail({ envName, appName, compName }) {
return request(`/api/envs/${envName}/apps/${appName}/components/${compName}`);
}
/*
* DELETE /api/envs/:envName/apps/:appName/components/:compName (component delete)
*/
export async function deleteComponent({ envName, appName, compName }) {
return request(`/api/envs/${envName}/apps/${appName}/components/${compName}`, {
method: 'delete',
});
}

View File

@@ -12,10 +12,10 @@ export async function getTraits() {
return request('/api/traits/');
}
/*
* POST /api/envs/:envName/apps/:appName/traits/ (attach 单个 trait)
* POST /api/envs/:envName/apps/:appName/components/:compName/traits/ (attach a trait)
*/
export async function attachOneTraits({ envName, appName, params }) {
return request(`/api/envs/${envName}/apps/${appName}/traits/`, {
export async function attachOneTraits({ envName, appName, compName, params }) {
return request(`/api/envs/${envName}/apps/${appName}/components/${compName}/traits/`, {
method: 'post',
data: {
...params,
@@ -26,10 +26,13 @@ export async function attachOneTraits({ envName, appName, params }) {
});
}
/*
* DELETE /api/envs/:envName/apps/:appName/traits/:traitName (detach 单个 trait)
* DELETE /api/envs/:envName/apps/:appName/components/:compName/traits/:traitName (detach a trait)
*/
export async function deleteOneTrait({ envName, appName, traitName }) {
return request(`/api/envs/${envName}/apps/${appName}/traits/${traitName}`, {
method: 'delete',
});
export async function deleteOneTrait({ envName, appName, compName, traitName }) {
return request(
`/api/envs/${envName}/apps/${appName}/components/${compName}/traits/${traitName}`,
{
method: 'delete',
},
);
}