Merge pull request #222 from hanxie-crypto/master

Capability interface , Page loading effect And app details page
This commit is contained in:
Sun Jianbo
2020-08-28 17:44:42 +08:00
committed by GitHub
18 changed files with 889 additions and 651 deletions

22
dashboard/.prettierignore Normal file
View File

@@ -0,0 +1,22 @@
**/*.svg
package.json
.umi
.umi-production
/dist
.dockerignore
.DS_Store
.eslintignore
*.png
*.toml
docker
.editorconfig
Dockerfile*
.gitignore
.prettierignore
LICENSE
.eslintcache
*.lock
yarn-error.log
.history
CNAME
/build

5
dashboard/.prettierrc.js Normal file
View File

@@ -0,0 +1,5 @@
const fabric = require('@umijs/fabric');
module.exports = {
...fabric.prettier,
};

View File

@@ -35,6 +35,11 @@ export default class CreateTraitItem extends React.PureComponent {
return this.formRefStep2.current.getFieldsValue();
};
setDefaultValue = (traitType) => {
this.formRefStep2.current.setFieldsValue({ name: traitType });
this.traitSelectChange(traitType);
};
traitSelectChange = async (value, isType = 1) => {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
@@ -47,7 +52,7 @@ export default class CreateTraitItem extends React.PureComponent {
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else {
} else if (isType) {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {

View File

@@ -51,7 +51,11 @@ class Trait extends React.Component {
const { history } = this.props.propsObj;
history.push({
pathname: '/ApplicationList/ApplicationListDetail',
state: { appName: selectValue, envName: this.props.currentEnv },
state: {
appName: selectValue,
envName: this.props.currentEnv,
traitType: this.props.propsObj.title,
},
});
} else {
message.warn('please select a application');
@@ -73,7 +77,7 @@ class Trait extends React.Component {
onSearch = () => {};
render() {
const { btnValue, title, settings, btnIsShow } = this.props.propsObj;
const { btnValue, title, settings, btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
const appList = _.get(this.props, 'returnObj', []);
return (
<PageContainer>
@@ -83,16 +87,17 @@ class Trait extends React.Component {
<Row>
<Col span="22">
<p className="title">{title}</p>
<p>core.oam.dev/v1alpha2</p>
<p>
{crdInfo.apiVersion},kind={crdInfo.kind}
</p>
</Col>
</Row>
<Row>
<Col span="22">
<p className="title">Applies To</p>
<p>*.apps/v1</p>
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
</Col>
</Row>
<p className="title">Conflicts With:</p>
<p className="title">Configurable Properties:</p>
{settings.map((item, index) => {
return (
@@ -101,7 +106,7 @@ class Trait extends React.Component {
<p>{item.name}</p>
</Col>
<Col span="16">
<p>{item.value}</p>
<p>{item.default || item.usage}</p>
</Col>
</Row>
);

View File

@@ -11,6 +11,7 @@ export default class Workload extends React.PureComponent {
btnValue,
pathname,
title,
crdInfo,
state,
settings,
hrefAddress,
@@ -25,7 +26,9 @@ export default class Workload extends React.PureComponent {
<Row>
<Col span="22">
<p className="title">{title}</p>
<p>apps/v1</p>
<p>
{crdInfo.apiVersion},kind={crdInfo.kind}
</p>
</Col>
</Row>
<p className="title">Configurable Settings:</p>
@@ -36,7 +39,10 @@ export default class Workload extends React.PureComponent {
<p>{item.name}</p>
</Col>
<Col span="16">
<p>{item.value}</p>
{
// eslint-disable-next-line consistent-return
}
<p>{item.default || item.usage}</p>
</Col>
</Row>
);

View File

@@ -8,14 +8,15 @@ import React from 'react';
import { Link, useIntl, connect, history } from 'umi';
import RightContent from '@/components/GlobalHeader/RightContent';
const menuDataRender = (menuList) =>
menuList.map((item) => {
const menuDataRender = (menuList) => {
return menuList.map((item) => {
const localItem = {
...item,
children: item.children ? menuDataRender(item.children) : undefined,
};
return localItem;
});
};
const BasicLayout = (props) => {
const { settings } = props;

View File

@@ -1,7 +1,8 @@
import {
getCapabilityCenterlist,
createCapability,
createCapabilityCenter,
syncCapability,
deleteCapability,
syncOneCapability,
deleteOneCapability,
capabilityList,
@@ -17,16 +18,20 @@ const TestModel = {
const res = yield call(getCapabilityCenterlist, payload);
return res;
},
*createCapability({ payload }, { call }) {
*createCapabilityCenter({ payload }, { call }) {
// 如果 method = Getdata 类型 = list/json 否则data 类型 = string存储的是操作成功的信息
// 非get请求将结果返回在调用页面进行async await 来进行操作结果提示
const res = yield call(createCapability, payload);
const res = yield call(createCapabilityCenter, payload);
return res;
},
*syncCapability({ payload }, { call }) {
const res = yield call(syncCapability, payload);
return res;
},
*deleteCapability({ payload }, { call }) {
const res = yield call(deleteCapability, payload);
return res;
},
*syncOneCapability({ payload }, { call }) {
const res = yield call(syncOneCapability, payload);
return res;

View File

@@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import './index.less';
import { Button, Row, Col, Tabs, Popconfirm, message, Tooltip, Modal } from 'antd';
import { Button, Row, Col, Tabs, Popconfirm, message, Tooltip, Modal, Spin } from 'antd';
import { connect } from 'dva';
import _ from 'lodash';
import CreateTraitItem from '../../../components/AttachOneTrait/index.jsx';
@@ -27,12 +27,13 @@ class TableList extends React.Component {
}
componentDidMount() {
this.getInitialData();
this.getInitialData(1);
}
getInitialData = async () => {
getInitialData = async (times) => {
const appName = _.get(this.props, 'location.state.appName', '');
const envName = _.get(this.props, 'location.state.envName', '');
const traitType = _.get(this.props, 'location.state.traitType', '');
if (appName && envName) {
this.setState({
envName,
@@ -62,6 +63,14 @@ class TableList extends React.Component {
} else if (workloadType && workloadType === 'Deployment') {
this.getAcceptTrait('deployment');
}
// 如果traitType存在是从特定trait跳转来新增单个trait的
if (traitType && times === 1) {
// this.createTrait(traitType)
await this.setState({
visible: true,
});
this.child.setDefaultValue(traitType);
}
}
};
@@ -100,13 +109,8 @@ class TableList extends React.Component {
deleteTrait = async (e, item) => {
e.stopPropagation();
const { appName, envName } = this.state;
const kind = _.get(item, 'trait.kind', '');
let traitName = '';
if (kind && kind === 'SimpleRolloutTrait') {
traitName = 'rollout';
} else if (kind && kind === 'ManualScalerTrait') {
traitName = 'scale';
}
const traitNameObj = _.get(item, 'trait.metadata.annotations', '');
const traitName = traitNameObj['vela.oam.dev/traitDef'];
if (traitName && appName && envName) {
const res = await this.props.dispatch({
type: 'trait/deleteOneTrait',
@@ -118,7 +122,7 @@ class TableList extends React.Component {
});
if (res) {
message.success(res);
this.getInitialData();
this.getInitialData(2);
}
}
};
@@ -128,7 +132,7 @@ class TableList extends React.Component {
};
createTrait = async () => {
this.setState({
await this.setState({
visible: true,
});
};
@@ -163,7 +167,7 @@ class TableList extends React.Component {
visible: false,
});
message.success(res);
this.getInitialData();
this.getInitialData(2);
}
}
} else {
@@ -181,52 +185,77 @@ class TableList extends React.Component {
e.stopPropagation();
};
gotoWorkloadDetail = () => {
this.props.history.push({ pathname: '/Workload/Detail' });
gotoWorkloadDetail = (e, kind) => {
e.stopPropagation();
this.props.history.push({ pathname: '/Workload/Detail', state: { kind } });
};
gotoTraitDetail = () => {
this.props.history.push({ pathname: '/Traits/Detail' });
gotoTraitDetail = (e, annotations) => {
e.stopPropagation();
const traitName = annotations['vela.oam.dev/traitDef'];
this.props.history.push({ pathname: '/Traits/Detail', state: { traitName } });
};
render() {
const status = _.get(this.state.appDetailData, 'Status', '');
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
const Traits = _.get(this.state.appDetailData, 'Traits', []);
const containers = _.get(Workload, 'spec.containers[0]', {});
const ports = _.get(Workload, 'spec.containers[0].ports[0]', {});
let containers = {};
if (Workload.kind === 'ContainerizedWorkload') {
containers = _.get(Workload, 'spec.containers[0]', {});
} else if (Workload.kind === 'Deployment') {
containers = _.get(Workload, 'spec.template.spec.containers[0]', {});
}
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
return (
<PageContainer>
<div className="card-container app-detial">
<h2>{_.get(Workload, 'metadata.name')}</h2>
<p style={{ marginBottom: '20px' }}>
{Workload.apiVersion}, Kind={Workload.kind}
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<Row>
<Col span="11">
<div className="summaryBox1" onClick={this.gotoWorkloadDetail}>
<Row>
<Col span="22">
<p className="title">{Workload.kind}</p>
<p>{Workload.apiVersion}</p>
</Col>
<Col span="2">
{/* <a href="JavaScript:;">?</a> */}
<p className="title hasCursor" onClick={this.hrefClick}>
?
</p>
</Col>
</Row>
<p className="title">
Name:<span>{_.get(Workload, 'metadata.name')}</span>
</p>
<p className="title">Settings:</p>
<p>#可编辑</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey !== 'ports') {
<Spin spinning={loadingAll}>
<div className="card-container app-detial">
<h2>{_.get(Workload, 'metadata.name')}</h2>
<p style={{ marginBottom: '20px' }}>
{Workload.apiVersion}, Kind={Workload.kind}
</p>
<Tabs>
<TabPane tab="Summary" key="1">
<Row>
<Col span="11">
<div
className="summaryBox1"
onClick={(e) => this.gotoWorkloadDetail(e, Workload.kind)}
>
{/* <div className="summaryBox1"> */}
<Row>
<Col span="22">
<p className="title">{Workload.kind}</p>
<p>{Workload.apiVersion}</p>
</Col>
<Col span="2">
{/* <a href="JavaScript:;">?</a> */}
<p className="title hasCursor" onClick={this.hrefClick}>
?
</p>
</Col>
</Row>
<p className="title">
Name:<span>{_.get(Workload, 'metadata.name')}</span>
</p>
<p className="title">Settings:</p>
<p>#可编辑</p>
<Row>
{Object.keys(containers).map((currentKey) => {
if (currentKey === 'ports') {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>port</p>
</Col>
<Col span="16">
<p>{_.get(containers[currentKey], '[0].containerPort', '')}</p>
</Col>
</Fragment>
);
}
return (
<Fragment key={currentKey}>
<Col span="8">
@@ -237,174 +266,212 @@ class TableList extends React.Component {
</Col>
</Fragment>
);
})}
</Row>
</div>
<div className="summaryBox2">
<p className="title">Status:</p>
<p>{status}</p>
{/* <Row>
<Col span="8">
<p>Available Replicas</p>
<p>Ready Replicas</p>
</Col>
<Col span="16">
<p>1</p>
<p>1</p>
</Col>
</Row> */}
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button danger>Delete</Button>
</Popconfirm>
</Col>
<Col span="1" />
<Col span="10">
{Traits.length ? (
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 Object.keys(ports).map((currentKey1) => {
return (
<Fragment key={currentKey1}>
<Col span="8">
<p>{currentKey1}</p>
return (
<div
className="summaryBox"
onClick={(e) => this.gotoTraitDetail(e, annotations)}
key={index.toString()}
>
<Row>
<Col span="22">
<p className="title">{traitItem.kind}</p>
<p>{traitItem.apiVersion}</p>
</Col>
<Col span="16">
<p>{ports[currentKey1]}</p>
<Col span="2">
<p
className="title hasCursor"
onClick={(e) => {
e.stopPropagation();
}}
>
?
</p>
</Col>
</Fragment>
);
});
})}
</Row>
</div>
<div className="summaryBox2">
<p className="title">Status:</p>
<p>{status}</p>
{/* <Row>
<Col span="8">
<p>Available Replicas</p>
<p>Ready Replicas</p>
</Col>
<Col span="16">
<p>1</p>
<p>1</p>
</Col>
</Row> */}
</div>
<Popconfirm
title="Are you sure delete this app?"
onConfirm={(e) => this.deleteApp(e)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button danger>Delete</Button>
</Popconfirm>
</Col>
<Col span="1" />
<Col span="10">
{Traits.length ? (
Traits.map((item, index) => {
const traitItem = _.get(item, 'trait', {});
// const spec = _.get(traitItem, 'spec', {});
const annotations = _.get(traitItem, 'metadata.annotations', {});
// const traitPorts = _.get(Workload, 'spec.containers[0].ports[0]',{});
return (
<div
className="summaryBox"
onClick={this.gotoTraitDetail}
key={index.toString()}
>
<Row>
<Col span="22">
<p className="title">{traitItem.kind}</p>
<p>{traitItem.apiVersion}</p>
</Col>
<Col span="2">
<p
className="title hasCursor"
onClick={(e) => {
e.stopPropagation();
}}
>
?
</p>
</Col>
</Row>
<Row>
{Object.keys(annotations).map((currentKey3) => {
return (
<Fragment key={currentKey3}>
</Row>
<Row>
{Object.keys(annotations).map((currentKey3) => {
return (
<Fragment key={currentKey3}>
<Col span="8">
<p>{currentKey3}:</p>
</Col>
<Col span="8">
<p>{annotations[currentKey3]}</p>
</Col>
</Fragment>
);
})}
</Row>
<p className="title">Properties:</p>
<p>#可编辑</p>
<Row>
{traitType === 2 ? (
<Fragment>
<Col span="8">
<p>{currentKey3}:</p>
</Col>
<Col span="8">
<p>{annotations[currentKey3]}</p>
</Col>
</Fragment>
);
})}
</Row>
<p className="title">Properties:</p>
<p>#可编辑</p>
<Row>
{/* {Object.keys(spec).map((currentKey) => {
return (
<Fragment key={currentKey}>
<Col span="8">
<p>{currentKey}</p>
<p>domain</p>
</Col>
<Col span="16">
<p>{spec[currentKey]}</p>
<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>
);
})} */}
</Row>
<div style={{ clear: 'both', height: '32px' }}>
<Popconfirm
title="Are you sure delete this trait?"
onConfirm={(e) => this.deleteTrait(e, item)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
<Button
danger
className="floatRight"
onClick={(e) => {
e.stopPropagation();
}}
) : (
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) => {
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
title="Are you sure delete this trait?"
onConfirm={(e) => this.deleteTrait(e, item)}
onCancel={this.cancel}
okText="Yes"
cancelText="No"
>
Delete
</Button>
</Popconfirm>
<Button
danger
className="floatRight"
onClick={(e) => {
e.stopPropagation();
}}
>
Delete
</Button>
</Popconfirm>
</div>
</div>
</div>
);
})
) : (
<Fragment />
)}
<Tooltip placement="top" title="Attach Trait">
<p
className="hasCursor"
style={{
fontSize: '30px',
display: 'inline-flex',
}}
onClick={this.createTrait}
>
+
</p>
</Tooltip>
</Col>
</Row>
</TabPane>
<TabPane tab="Topology" key="2">
{/* <p>Topology</p> */}
<Topology />
</TabPane>
</Tabs>
</div>
<Modal
title="attach a trait"
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}>
Confirm
</Button>,
]}
>
<CreateTraitItem
onRef={(ref) => {
this.child = ref;
}}
availableTraitList={this.state.availableTraitList}
initialValues={{}}
/>
</Modal>
);
})
) : (
<Fragment />
)}
<Tooltip placement="top" title="Attach Trait">
<p
className="hasCursor"
style={{
fontSize: '30px',
display: 'inline-flex',
}}
onClick={this.createTrait}
>
+
</p>
</Tooltip>
</Col>
</Row>
</TabPane>
<TabPane tab="Topology" key="2">
{/* <p>Topology</p> */}
<Topology />
</TabPane>
</Tabs>
</div>
<Modal
title="attach a trait"
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}>
Confirm
</Button>,
]}
>
<CreateTraitItem
onRef={(ref) => {
this.child = ref;
}}
availableTraitList={this.state.availableTraitList}
initialValues={{}}
/>
</Modal>
</Spin>
</PageContainer>
);
}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { SearchOutlined, BranchesOutlined, ApartmentOutlined } from '@ant-design/icons';
import { Button, Card, Row, Col, Form, Select, DatePicker, Spin } from 'antd';
import { Button, Card, Row, Col, Form, Select, DatePicker, Spin, Empty } from 'antd';
import { connect } from 'dva';
import moment from 'moment';
import './index.less';
@@ -161,7 +161,9 @@ class TableList extends React.Component {
);
})
) : (
<div style={{ paddingLeft: '8px' }}>暂无数据</div>
<div style={{ width: '100%', height: '80%' }}>
<Empty />
</div>
)}
</Row>
</Spin>

View File

@@ -1,15 +1,16 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Space, Modal, Button, Row, Col, message } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Space, Button, Row, Col, message, Spin } from 'antd';
// import { Space, Modal, Button, Row, Col, message, Spin } from 'antd';
// import { ExclamationCircleOutlined } from '@ant-design/icons';
import './index.less';
import { connect } from 'dva';
import _ from 'lodash';
const { confirm } = Modal;
// const { confirm } = Modal;
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
loadingAll: loading.models.capability,
currentEnv: globalData.currentEnv,
}))
class TableList extends React.PureComponent {
@@ -33,11 +34,14 @@ class TableList extends React.PureComponent {
const workloadList = [];
const traitList = [];
if (Array.isArray(res)) {
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
res.forEach((item) => {
if (item.type === 'workload') {
workloadList.push(item);
} else if (item.type === 'trait') {
traitList.push(item);
if (item.center === capabilityCenterName) {
if (item.type === 'workload') {
workloadList.push(item);
} else if (item.type === 'trait') {
traitList.push(item);
}
}
});
this.setState({
@@ -69,6 +73,24 @@ class TableList extends React.PureComponent {
}
};
uninstallSignle = async (e, name) => {
e.stopPropagation();
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
if (name) {
const res = await this.props.dispatch({
type: 'capability/deleteOneCapability',
payload: {
// capabilityCenterName,
capabilityName: name,
},
});
if (res) {
message.success(res);
this.getInitialData();
}
}
};
syncAllSignle = async () => {
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
if (capabilityCenterName) {
@@ -86,114 +108,131 @@ class TableList extends React.PureComponent {
};
showDeleteConfirm = () => {
// eslint-disable-next-line
const _this = this;
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
confirm({
title: `Are you sure delete ${capabilityCenterName}?`,
icon: <ExclamationCircleOutlined />,
width: 500,
content: (
<div>
<p style={{ margin: '0px' }}>您本次移除 {capabilityCenterName}将会删除的应用列表</p>
<Space>
<span>abc</span>
<span>abc</span>
<span>abc</span>
<span>abc</span>
</Space>
<p style={{ margin: '0px' }}>确认后移除 {capabilityCenterName}并且删除相应的应用</p>
</div>
),
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
async onOk() {
const res = await _this.props.dispatch({
type: 'capability/deleteOneCapability',
payload: {
capabilityName: capabilityCenterName,
},
});
if (res) {
message.success(res);
_this.props.history.push({ pathname: '/Capability' });
}
},
onCancel() {
// console.log('Cancel');
},
});
message.info('正在开发中...');
// // eslint-disable-next-line
// const _this = this;
// const capabilityCenterName = _.get(this.props, 'location.state.name', '');
// if (capabilityCenterName) {
// confirm({
// title: `Are you sure delete ${capabilityCenterName}?`,
// icon: <ExclamationCircleOutlined />,
// width: 500,
// content: (
// <div>
// <p style={{ margin: '0px' }}>您本次移除 {capabilityCenterName},将会删除的应用列表:</p>
// <Space>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// </Space>
// <p style={{ margin: '0px' }}>
// 确认后,移除 {capabilityCenterName},并且删除相应的应用?
// </p>
// </div>
// ),
// okText: 'Yes',
// okType: 'danger',
// cancelText: 'No',
// async onOk() {
// const res = await _this.props.dispatch({
// type: 'capability/deleteCapability',
// payload: {
// capabilityName: capabilityCenterName,
// },
// });
// if (res) {
// message.success(res);
// _this.props.history.push({ pathname: '/Capability' });
// }
// },
// onCancel() {
// // console.log('Cancel');
// },
// });
// }
};
render() {
const { workloadList = [], traitList = [] } = this.state;
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
return (
<PageContainer>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.syncAllSignle}>
Install all
</Button>
<Button type="default" onClick={this.showDeleteConfirm}>
Remove
</Button>
</Space>
</div>
<div>
<h3>Workloads</h3>
<Row>
{workloadList.length ? (
workloadList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button />
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment />
)}
</Row>
</div>
<div>
<h3>Traits</h3>
<Row>
{traitList.length ? (
traitList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button />
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment />
)}
</Row>
</div>
<Spin spinning={loadingAll}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.syncAllSignle}>
Install all
</Button>
<Button type="default" onClick={this.showDeleteConfirm}>
Remove
</Button>
</Space>
</div>
<div>
<h3>Workloads</h3>
<Row>
{workloadList.length ? (
workloadList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的workload</div>
</Fragment>
)}
</Row>
</div>
<div>
<h3>Traits</h3>
<Row>
{traitList.length ? (
traitList.map((item) => {
return (
<Col span="4" key={item.name}>
<div className="itemBox" onClick={this.gotoOtherPage}>
<img
src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1109866916,1852667152&fm=26&gp=0.jpg"
alt="workload"
/>
<p>{item.name}</p>
{item.status === 'installed' ? (
<Button onClick={(e) => this.uninstallSignle(e, item.name)}>
uninstall
</Button>
) : (
<Button onClick={(e) => this.installSignle(e, item.name)}>install</Button>
)}
</div>
</Col>
);
})
) : (
<Fragment>
<div>暂无可用的trait</div>
</Fragment>
)}
</Row>
</div>
</Spin>
</PageContainer>
);
}

View File

@@ -1,65 +1,15 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Table, Space, Modal, Form, Input, message } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Button, Table, Space, Modal, Form, Input, message, Spin } from 'antd';
// import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Link } from 'umi';
import './index.less';
import { connect } from 'dva';
import _ from 'lodash';
const { confirm } = Modal;
// const { confirm } = Modal;
const { Column } = Table;
// const syncSignle = async (text,record) =>{
// // console.log(text,record)
// const res = await this.props.dispatch({
// type: 'capability/syncCapability',
// payload: {
// capabilityCenterName: record.name
// }
// })
// if(res){
// message.success(res);
// }
// };
// function showDeleteConfirm(record) {
// confirm({
// title: `Are you sure delete ${record.name}?`,
// icon: <ExclamationCircleOutlined />,
// width: 500,
// content: (
// <div>
// <p>您本次移除 { record.name },将会删除的应用列表:</p>
// <Space>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// </Space>
// <p>确认后,移除{ record.name },并且删除相应的应用?</p>
// </div>
// ),
// okText: 'Yes',
// okType: 'danger',
// cancelText: 'No',
// async onOk() {
// const res = await this.props.dispatch({
// type: 'capability/deleteOneCapability',
// payload: {
// capabilityName: record.name
// }
// })
// if(res){
// message.success(res);
// this.getInitialData()
// }
// },
// onCancel() {
// // console.log('Cancel');
// },
// });
// }
const layout = {
labelCol: {
span: 4,
@@ -70,7 +20,8 @@ const layout = {
};
@connect(({ loading, globalData }) => ({
loadingAll: loading.models.applist,
loadingAll: loading.models.capability,
loadingList: loading.effects['capability/getCapabilityCenterlist'],
currentEnv: globalData.currentEnv,
}))
class TableList extends React.PureComponent {
@@ -93,8 +44,14 @@ class TableList extends React.PureComponent {
type: 'capability/getCapabilityCenterlist',
});
if (res) {
let newRes = _.cloneDeep(res);
newRes = newRes.map((item) => {
// eslint-disable-next-line no-param-reassign
item.btnSyncLoading = false;
return item;
});
this.setState({
capabilityList: res,
capabilityList: newRes,
});
}
};
@@ -108,7 +65,7 @@ class TableList extends React.PureComponent {
handleOk = async () => {
const submitData = await this.formRef.current.validateFields();
const res = await this.props.dispatch({
type: 'capability/createCapability',
type: 'capability/createCapabilityCenter',
payload: {
params: submitData,
},
@@ -119,6 +76,12 @@ class TableList extends React.PureComponent {
visible: false,
});
this.getInitialData();
} else {
// 目前创建分为两部创建列表和安装相关依赖如果成功一个目前返回500而此时可能列表已经创建成功只是依赖安装失败
this.setState({
visible: false,
});
this.getInitialData();
}
};
@@ -135,150 +98,179 @@ class TableList extends React.PureComponent {
});
};
syncSignle = async (record) => {
const res = await this.props.dispatch({
type: 'capability/syncCapability',
payload: {
capabilityCenterName: record.name,
},
});
if (res) {
message.success(res);
this.getInitialData();
syncSignle = async (text, index) => {
if (text) {
const newList = _.cloneDeep(this.state.capabilityList);
newList[index].btnSyncLoading = true;
this.setState(() => ({
capabilityList: newList,
}));
const res = await this.props.dispatch({
type: 'capability/syncCapability',
payload: {
capabilityCenterName: text,
},
});
if (res) {
message.success(res);
// this.getInitialData();
}
const newList1 = _.cloneDeep(this.state.capabilityList);
newList1[index].btnSyncLoading = false;
this.setState(() => ({
capabilityList: newList1,
}));
}
};
showDeleteConfirm = (record) => {
// eslint-disable-next-line
const _this = this;
confirm({
title: `Are you sure delete ${record.name}?`,
icon: <ExclamationCircleOutlined />,
width: 500,
content: (
<div>
<p>您本次移除 {record.name}将会删除的应用列表</p>
<Space>
<span>abc</span>
<span>abc</span>
<span>abc</span>
<span>abc</span>
</Space>
<p>确认后移除{record.name}并且删除相应的应用</p>
</div>
),
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
async onOk() {
const res = await _this.props.dispatch({
type: 'capability/deleteOneCapability',
payload: {
capabilityName: record.name,
},
});
if (res) {
message.success(res);
_this.getInitialData();
}
},
onCancel() {
// console.log('Cancel');
},
});
showDeleteConfirm = () => {
message.info('正在开发中...');
// if (record) {
// // eslint-disable-next-line
// const _this = this;
// confirm({
// title: `Are you sure delete ${record}?`,
// icon: <ExclamationCircleOutlined />,
// width: 500,
// content: (
// <div>
// <p>您本次移除 {record},将会删除的应用列表:</p>
// <Space>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// <span>abc</span>
// </Space>
// <p>确认后,移除{record},并且删除相应的应用?</p>
// </div>
// ),
// okText: 'Yes',
// okType: 'danger',
// cancelText: 'No',
// async onOk() {
// const res = await _this.props.dispatch({
// type: 'capability/deleteCapability',
// payload: {
// capabilityName: record,
// },
// });
// if (res) {
// message.success(res);
// _this.getInitialData();
// }
// },
// onCancel() {
// // console.log('Cancel');
// },
// });
// }
};
render() {
let { capabilityList } = this.state;
let { loadingList } = this.props;
loadingList = loadingList || false;
capabilityList = Array.isArray(capabilityList) ? capabilityList : [];
return (
<PageContainer>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.showModal}>
Create
</Button>
{/* <Button type="default">Sync All</Button> */}
</Space>
</div>
<Modal
title="Create Capability Center"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
// <Button key="test" onClick={this.handleTest}>
// Test
// </Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Create
</Button>,
]}
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="Name"
label="Name"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="Address"
label="URL"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
<Table dataSource={capabilityList}>
<Column
title="Name"
dataIndex="name"
key="name"
render={(text, record) => {
return (
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
{text}
</Link>
);
}}
/>
<Column
title="URL"
dataIndex="url"
key="url"
render={(text) => {
return (
<a href={text} target="_blank" rel="noreferrer">
{text}
</a>
);
}}
/>
<Column
title="Operations"
dataIndex="name"
key="name"
render={(text, record) => {
return (
<Space>
<Button onClick={() => this.syncSignle(record)}>sync</Button>
<Button onClick={() => this.showDeleteConfirm(record)}>remove</Button>
</Space>
);
}}
/>
</Table>
<Spin spinning={loadingList}>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={this.showModal}>
Create
</Button>
{/* <Button type="default">Sync All</Button> */}
</Space>
</div>
<Modal
title="Create Capability Center"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
// <Button key="test" onClick={this.handleTest}>
// Test
// </Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Create
</Button>,
]}
>
<Form {...layout} ref={this.formRef} name="control-ref" labelAlign="left">
<Form.Item
name="Name"
label="Name"
rules={[
{
required: true,
message: 'Please input name!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="Address"
label="URL"
rules={[
// { pattern: '/^((https|http|ftp|rtsp|mms){0,1}(:\/\/){0,1})\.(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/',
// message: 'please input correct URL'
// },
{
required: true,
message: 'Please input URL!',
},
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
<Table dataSource={capabilityList} pagination={false} rowKey={(record) => record.name}>
<Column
title="Name"
dataIndex="name"
key="name"
render={(text, record) => {
return (
<Link to={{ pathname: '/Capability/Detail', state: { name: record.name } }}>
{text}
</Link>
);
}}
/>
<Column
title="URL"
dataIndex="url"
key="url"
render={(text) => {
return (
<a href={text} target="_blank" rel="noreferrer">
{text}
</a>
);
}}
/>
<Column
title="Operations"
dataIndex="name"
key="name"
render={(text, record, index) => {
return (
<Space>
<Button
loading={record.btnSyncLoading}
onClick={() => this.syncSignle(text, index)}
>
sync
</Button>
<Button onClick={() => this.showDeleteConfirm(text)}>remove</Button>
</Space>
);
}}
/>
</Table>
</Spin>
</PageContainer>
);
}

View File

@@ -139,6 +139,10 @@ class TableList extends React.Component {
};
}
// componentDidMount(){
// console.log(this.props.location.state)
// }
onFinishStep1 = () => {
this.setState(() => ({
hasShowEdit: false,

View File

@@ -1,31 +1,56 @@
import React from 'react';
import React, { Fragment } from 'react';
import { Spin } from 'antd';
import { connect } from 'dva';
import Trait from '../../../components/Trait';
@connect(({ loading }) => ({
loadingAll: loading.models.trait,
}))
class TableList extends React.PureComponent {
render() {
const propsObj = {
title: 'Rollout',
settings: [
{
name: 'Strategy',
value: 'description,schema',
},
{
name: 'Step',
value: 'description,schema',
},
],
pathname: '/ApplicationList/CreateApplication',
state: {
activeStep: 1,
TraitType: 'rollout',
},
btnValue: 'Attach to',
hrefAddress: '#',
btnIsShow: true,
history: this.props.history,
constructor(props) {
super(props);
this.state = {
propsObj: {},
};
return <Trait propsObj={propsObj} />;
}
componentDidMount() {
this.getInitialData();
}
getInitialData = async () => {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: 'rollout',
},
});
if (res) {
let propsObj = {};
propsObj = {
title: res.name,
settings: res.parameters,
crdInfo: res.crdInfo,
appliesTo: res.appliesTo,
btnValue: 'Attach to',
btnIsShow: true,
history: this.props.history,
};
this.setState({
propsObj,
});
}
};
render() {
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const { propsObj } = this.state;
return (
<Spin spinning={loadingAll}>
{propsObj.title ? <Trait propsObj={propsObj} /> : <Fragment />}
</Spin>
);
}
}

View File

@@ -1,31 +1,56 @@
import React from 'react';
import React, { Fragment } from 'react';
import { Spin } from 'antd';
import { connect } from 'dva';
import Trait from '../../../components/Trait';
@connect(({ loading }) => ({
loadingAll: loading.models.trait,
}))
class TableList extends React.PureComponent {
render() {
const propsObj = {
title: 'Scale',
settings: [
{
name: 'Max Instance',
value: 'description,schema',
},
{
name: 'Min Instance',
value: 'description,schema',
},
],
pathname: '/ApplicationList/CreateApplication',
state: {
activeStep: 1,
TraitType: 'scale',
},
btnValue: 'Attach to',
hrefAddress: '#',
btnIsShow: true,
history: this.props.history,
constructor(props) {
super(props);
this.state = {
propsObj: {},
};
return <Trait propsObj={propsObj} />;
}
componentDidMount() {
this.getInitialData();
}
getInitialData = async () => {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: 'scale',
},
});
if (res) {
let propsObj = {};
propsObj = {
title: res.name,
settings: res.parameters,
crdInfo: res.crdInfo,
appliesTo: res.appliesTo,
btnValue: 'Attach to',
btnIsShow: true,
history: this.props.history,
};
this.setState({
propsObj,
});
}
};
render() {
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const { propsObj } = this.state;
return (
<Spin spinning={loadingAll}>
{propsObj.title ? <Trait propsObj={propsObj} /> : <Fragment />}
</Spin>
);
}
}

View File

@@ -1,41 +1,59 @@
import React from 'react';
import React, { Fragment } from 'react';
import { connect } from 'dva';
import { Spin } from 'antd';
import Workload from '../../../components/Workload';
@connect(({ loading }) => ({
loadingAll: loading.models.workload,
}))
class TableList extends React.PureComponent {
render() {
const propsObj = {
title: 'containerized',
settings: [
{
name: 'Deployment Strategy',
value: 'RollingUpdate',
},
{
name: 'Rolling Update Strategy',
value: 'Max Surge 25%, Max Unavaiable 25%',
},
{
name: 'Min Ready Seconds',
value: 0,
},
{
name: 'Revision History Limit',
value: 10,
},
{
name: 'Replicas',
value: 0,
},
],
pathname: '/ApplicationList/CreateApplication',
state: {
WorkloadType: 'containerized',
},
btnValue: 'Create',
hrefAddress: '#',
btnIsShow: true,
constructor(props) {
super(props);
this.state = {
propsObj: {},
};
return <Workload propsObj={propsObj} />;
}
componentDidMount() {
this.getInitialData();
}
getInitialData = async () => {
const res = await this.props.dispatch({
type: 'workload/getWorkloadByName',
payload: {
workloadName: 'containerized',
},
});
if (res) {
let propsObj = {};
propsObj = {
title: res.name,
settings: res.parameters,
pathname: '/ApplicationList/CreateApplication',
state: {
WorkloadType: res.name,
},
crdInfo: res.crdInfo,
btnValue: 'Create',
hrefAddress: '#',
btnIsShow: true,
};
this.setState({
propsObj,
});
}
};
render() {
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const { propsObj } = this.state;
return (
<Spin spinning={loadingAll}>
{propsObj.title ? <Workload propsObj={propsObj} /> : <Fragment />}
</Spin>
);
}
}

View File

@@ -1,54 +1,59 @@
import React from 'react';
import React, { Fragment } from 'react';
import { connect } from 'dva';
import { Spin } from 'antd';
import Workload from '../../../components/Workload';
@connect(({ loading }) => ({
loadingAll: loading.models.applist,
loadingAll: loading.models.workload,
}))
class TableList extends React.PureComponent {
// async componentDidMount(){
// await this.props.dispatch({
// type:'workload/getWorkloadByName',
// payload: {
// workloadName: 'containerized'
// }
// })
// };
constructor(props) {
super(props);
this.state = {
propsObj: {},
};
}
componentDidMount() {
this.getInitialData();
}
getInitialData = async () => {
const res = await this.props.dispatch({
type: 'workload/getWorkloadByName',
payload: {
workloadName: 'deployment',
},
});
if (res) {
let propsObj = {};
propsObj = {
title: res.name,
settings: res.parameters,
pathname: '/ApplicationList/CreateApplication',
state: {
WorkloadType: res.name,
},
crdInfo: res.crdInfo,
btnValue: 'Create',
hrefAddress: '#',
btnIsShow: true,
};
this.setState({
propsObj,
});
}
};
render() {
const propsObj = {
title: 'Deployment',
settings: [
{
name: 'Deployment Strategy',
value: 'RollingUpdate',
},
{
name: 'Rolling Update Strategy',
value: 'Max Surge 25%, Max Unavaiable 25%',
},
{
name: 'Min Ready Seconds',
value: 0,
},
{
name: 'Revision History Limit',
value: 10,
},
{
name: 'Replicas',
value: 0,
},
],
pathname: '/ApplicationList/CreateApplication',
state: {
WorkloadType: 'Deployment',
},
btnValue: 'Create',
hrefAddress: '#',
btnIsShow: true,
};
return <Workload propsObj={propsObj} />;
let { loadingAll } = this.props;
loadingAll = loadingAll || false;
const { propsObj } = this.state;
return (
<Spin spinning={loadingAll}>
{propsObj.title ? <Workload propsObj={propsObj} /> : <Fragment />}
</Spin>
);
}
}

View File

@@ -126,7 +126,7 @@ const demoText = `H4sIAAAAAAAA/6xUwW7bOhD8lYc9U4lsWfazgB4C5Na0DZK0lyKHNbmyWVMkQ6
rio.cattle.io/mesh true
Controlled By cool-aryabhata-v0fnxq6`;
class TableList extends React.Component {
class TableList extends React.PureComponent {
formRefStep1 = React.createRef();
formRefStep2 = React.createRef();
@@ -139,6 +139,10 @@ class TableList extends React.Component {
};
}
// componentDidMount(){
// console.log(this.props.location.state)
// }
onFinishStep1 = () => {
this.setState({
hasShowEdit: false,

View File

@@ -8,7 +8,7 @@ export async function getCapabilityCenterlist() {
/*
* Put /capability-centers/ (添加 Capability Center)
*/
export async function createCapability({ params }) {
export async function createCapabilityCenter({ params }) {
return request('/api/capability-centers/', {
method: 'put',
// body:JSON.stringify(params),
@@ -31,6 +31,14 @@ export async function syncCapability({ capabilityCenterName }) {
},
});
}
/*
* Delete /api/capabilities/:capabilityName (删除一个 capability)
*/
export async function deleteCapability({ capabilityName }) {
return request(`/api/capabilities/${capabilityName}`, {
method: 'delete',
});
}
/*
* Put /capability-centers/:capabilityCenterName/capabilities/:capabilityName (安装一个 capability)
*/