mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bce8bfd40f | ||
|
|
66855265c8 | ||
|
|
63f52ea556 | ||
|
|
fa37bf0284 |
@@ -68,6 +68,18 @@ export default defineConfig({
|
||||
path: '/ApplicationList/CreateApplication',
|
||||
component: './ApplicationList/CreateApplication',
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.Components',
|
||||
hideInMenu: true,
|
||||
path: '/ApplicationList/:appName/Components',
|
||||
component: './ApplicationList/Components',
|
||||
},
|
||||
{
|
||||
name: 'ApplicationList.Components.createComponent',
|
||||
hideInMenu: true,
|
||||
path: '/ApplicationList/:appName/createComponent',
|
||||
component: './ApplicationList/CreateComponent',
|
||||
},
|
||||
{
|
||||
name: 'Workload',
|
||||
icon: 'table',
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
export default {
|
||||
dev: {
|
||||
'/api': {
|
||||
target: 'http://30.11.171.17:38081/',
|
||||
target: 'http://30.11.171.29:38081/',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* https://github.com/ant-design/ant-design-pro-layout
|
||||
*/
|
||||
import ProLayout from '@ant-design/pro-layout';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Link, useIntl, connect, history } from 'umi';
|
||||
import RightContent from '@/components/GlobalHeader/RightContent';
|
||||
import {
|
||||
@@ -44,6 +44,7 @@ const AddIcon = (menuData) => {
|
||||
const BasicLayout = (props) => {
|
||||
const { settings, dispatch, menus } = props;
|
||||
const [currentSelectKeys, setCurrentSelectedKeys] = useState('');
|
||||
const timerRef = useRef();
|
||||
const getCurrentSelectKeys = () => {
|
||||
const pathnameCur = props.history.location.pathname;
|
||||
if (pathnameCur) {
|
||||
@@ -70,9 +71,14 @@ const BasicLayout = (props) => {
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
props.history.listen((route) => {
|
||||
timerRef.current = props.history.listen((route) => {
|
||||
getCurrentSelectKeys(route.pathname);
|
||||
});
|
||||
return () => {
|
||||
if (timerRef.current) {
|
||||
timerRef.current = null;
|
||||
}
|
||||
};
|
||||
// setCurrentSelectedKeys('applist')
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -38,18 +38,26 @@ class TableList extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitialData(1);
|
||||
this.getInitialData();
|
||||
}
|
||||
|
||||
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', '');
|
||||
getInitialData = async () => {
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
if (appName && envName) {
|
||||
this.setState({
|
||||
envName,
|
||||
appName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
@@ -76,12 +84,6 @@ class TableList extends React.Component {
|
||||
} else if (workloadType && workloadType === 'Deployment') {
|
||||
this.getAcceptTrait('deployment');
|
||||
}
|
||||
if (traitType && times === 1) {
|
||||
await this.setState({
|
||||
visible: true,
|
||||
});
|
||||
this.child.setDefaultValue(traitType);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
108
dashboard/src/pages/ApplicationList/ComponentDetail/Topology.jsx
Normal file
108
dashboard/src/pages/ApplicationList/ComponentDetail/Topology.jsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import G6 from '@antv/g6';
|
||||
|
||||
const Topology = () => {
|
||||
let graph = null;
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 150,
|
||||
y: 50,
|
||||
label: 'node1',
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 250,
|
||||
y: 200,
|
||||
label: 'node2',
|
||||
},
|
||||
{
|
||||
id: 'node3',
|
||||
x: 100,
|
||||
y: 350,
|
||||
label: 'node3',
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2',
|
||||
label: 'edge 1',
|
||||
},
|
||||
{
|
||||
source: 'node2',
|
||||
target: 'node3',
|
||||
label: 'edge 2',
|
||||
},
|
||||
{
|
||||
source: 'node3',
|
||||
target: 'node1',
|
||||
label: 'edge 3',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const width = 1000;
|
||||
const height = 400;
|
||||
|
||||
useEffect(() => {
|
||||
if (!graph) {
|
||||
graph = new G6.Graph({
|
||||
container: 'container',
|
||||
width,
|
||||
height,
|
||||
// translate the graph to align the canvas's center, support by v3.5.1
|
||||
fitCenter: true,
|
||||
defaultNode: {
|
||||
type: 'circle',
|
||||
size: [40],
|
||||
color: '#5B8FF9',
|
||||
style: {
|
||||
fill: '#9EC9FF',
|
||||
lineWidth: 3,
|
||||
},
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: '#1890ff',
|
||||
fontSize: 14,
|
||||
},
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
defaultEdge: {
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
style: {
|
||||
background: {
|
||||
fill: '#ffffff',
|
||||
stroke: '#9EC9FF',
|
||||
padding: [2, 2, 2, 2],
|
||||
radius: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modes: {
|
||||
default: ['drag-canvas', 'drag-node'],
|
||||
},
|
||||
nodeStateStyles: {
|
||||
// style configurations for hover state
|
||||
hover: {
|
||||
fillOpacity: 0.8,
|
||||
},
|
||||
// style configurations for selected state
|
||||
selected: {
|
||||
lineWidth: 5,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
}, []);
|
||||
|
||||
return <div id="container" style={{ overflow: 'auto', width: '100%', height: '400px' }} />;
|
||||
};
|
||||
|
||||
export default Topology;
|
||||
462
dashboard/src/pages/ApplicationList/ComponentDetail/index.jsx
Normal file
462
dashboard/src/pages/ApplicationList/ComponentDetail/index.jsx
Normal file
@@ -0,0 +1,462 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import './index.less';
|
||||
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';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
currentEnv: globalData.currentEnv,
|
||||
}))
|
||||
class TableList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
appDetailData: {},
|
||||
visible: false,
|
||||
traitList: [],
|
||||
availableTraitList: [],
|
||||
envName: '',
|
||||
appName: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitialData();
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (this.props.appName !== nextProps.appName) {
|
||||
this.getInitialData(nextProps.appName);
|
||||
}
|
||||
}
|
||||
|
||||
getInitialData = async (nextAppName) => {
|
||||
let appName = _.get(this.props, 'appName', '');
|
||||
const envName = _.get(this.props, 'envName', '');
|
||||
appName = nextAppName || appName;
|
||||
if (appName && envName) {
|
||||
this.setState({
|
||||
envName,
|
||||
appName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
this.setState({
|
||||
appDetailData: res,
|
||||
});
|
||||
}
|
||||
const traits = await this.props.dispatch({
|
||||
type: 'trait/getTraits',
|
||||
});
|
||||
if (traits) {
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
}
|
||||
const workloadType = _.get(res, 'Workload.workload.kind', '');
|
||||
if (workloadType && workloadType === 'ContainerizedWorkload') {
|
||||
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;
|
||||
});
|
||||
this.setState(() => ({
|
||||
availableTraitList: res,
|
||||
}));
|
||||
};
|
||||
|
||||
deleteApp = async (e) => {
|
||||
e.stopPropagation();
|
||||
const { envName } = this.state;
|
||||
const { appDetailData } = this.state;
|
||||
const appName = _.get(appDetailData, 'Workload.workload.metadata.name', '');
|
||||
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' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
deleteTrait = async (e, item) => {
|
||||
e.stopPropagation();
|
||||
const { appName, envName } = 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) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/deleteOneTrait',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
traitName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
this.getInitialData(2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cancel = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
createTrait = async () => {
|
||||
await this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
await this.child.validateFields();
|
||||
const submitData = this.child.getSelectValue();
|
||||
if (submitData.name) {
|
||||
const submitObj = {
|
||||
name: submitData.name,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(submitData).forEach((currentKey) => {
|
||||
if (currentKey !== 'name' && submitData[currentKey]) {
|
||||
submitObj.flags.push({
|
||||
name: currentKey,
|
||||
value: submitData[currentKey].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
const { envName, appName } = this.state;
|
||||
if (envName && appName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'trait/attachOneTraits',
|
||||
payload: {
|
||||
envName,
|
||||
appName,
|
||||
params: submitObj,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
message.success(res);
|
||||
this.getInitialData(2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.warning('please select a trait type');
|
||||
}
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
hrefClick = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
gotoWorkloadDetail = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
gotoTraitDetail = (e) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
render() {
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
|
||||
const Traits = _.get(this.state.appDetailData, 'Traits', []);
|
||||
let containers = {};
|
||||
containers = _.get(Workload, 'spec.containers[0]', {});
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
const colorObj = {
|
||||
Deployed: '#4CAF51',
|
||||
Staging: '#F44337',
|
||||
UNKNOWN: '#1890ff',
|
||||
};
|
||||
return (
|
||||
<div style={{ margin: '8px' }}>
|
||||
<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)}
|
||||
style={{ background: colorObj[status] || '#1890ff' }}
|
||||
>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{Workload.kind}</p>
|
||||
<p>{Workload.apiVersion}</p>
|
||||
</Col>
|
||||
<Col span="2">
|
||||
<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>
|
||||
<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>
|
||||
);
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (currentKey === 'name') {
|
||||
return <Fragment key={currentKey} />;
|
||||
// eslint-disable-next-line no-else-return
|
||||
} else if (currentKey === 'env') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>env</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{_.get(containers[currentKey], '[0].value', '')}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{containers[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</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 (
|
||||
<div
|
||||
className="summaryBox"
|
||||
onClick={(e) => this.gotoTraitDetail(e, traitItem)}
|
||||
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}>
|
||||
<Col span="8">
|
||||
<p>{currentKey3}:</p>
|
||||
</Col>
|
||||
<Col span="8">
|
||||
<p>{annotations[currentKey3]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</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>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</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();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</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>
|
||||
</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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TableList;
|
||||
@@ -0,0 +1,47 @@
|
||||
.app-detial {
|
||||
.ant-tabs {
|
||||
min-height: 500px;
|
||||
padding: 10px 20px;
|
||||
background-color: #fff;
|
||||
.ant-tabs-content-holder {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.summaryBox1,
|
||||
.summaryBox2,
|
||||
.summaryBox {
|
||||
clear: both;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 10px 10px;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
line-height: 36px;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.summaryBox1 {
|
||||
color: #fff;
|
||||
background: #0097a7;
|
||||
cursor: pointer;
|
||||
}
|
||||
.summaryBox2 {
|
||||
color: #fff;
|
||||
background: #92c47c;
|
||||
}
|
||||
.summaryBox {
|
||||
border: 1px solid black;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hasCursor {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.floatRight {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
126
dashboard/src/pages/ApplicationList/Components/index.jsx
Normal file
126
dashboard/src/pages/ApplicationList/Components/index.jsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'umi';
|
||||
import { Breadcrumb, Button, Menu, Spin } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
import './index.less';
|
||||
import ComponentDetail from '../ComponentDetail/index.jsx';
|
||||
|
||||
@connect(({ loading }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
}))
|
||||
class TableList extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
envName: '',
|
||||
componentName: '',
|
||||
defaultSelectedKeys: 'a1',
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
envName,
|
||||
componentName: appName,
|
||||
});
|
||||
if (appName === 'test33') {
|
||||
this.setState({
|
||||
defaultSelectedKeys: 'a1',
|
||||
});
|
||||
} 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',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { envName, componentName, defaultSelectedKeys } = this.state;
|
||||
const { loadingAll } = this.props;
|
||||
return (
|
||||
<div style={{ height: '100%' }}>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Applications</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>appname</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Components</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>{componentName}</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<Spin spinning={loadingAll}>
|
||||
<div className="appComponent">
|
||||
<div className="left">
|
||||
<Menu
|
||||
mode="inline"
|
||||
onClick={this.changeComponent}
|
||||
defaultSelectedKeys={[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>
|
||||
</Menu.ItemGroup>
|
||||
</Menu>
|
||||
<div className="addComp">
|
||||
<Link
|
||||
to={{
|
||||
// pathname: '/ApplicationList/CreateApplication',
|
||||
pathname: `/ApplicationList/${componentName}/createComponent`,
|
||||
state: { appName: componentName, envName },
|
||||
}}
|
||||
>
|
||||
add a new comp
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="right">
|
||||
<div style={{ margin: '10px', float: 'right' }}>
|
||||
<Button type="primary">Delete App</Button>
|
||||
</div>
|
||||
<ComponentDetail appName={componentName} envName={envName} />
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TableList;
|
||||
35
dashboard/src/pages/ApplicationList/Components/index.less
Normal file
35
dashboard/src/pages/ApplicationList/Components/index.less
Normal file
@@ -0,0 +1,35 @@
|
||||
.appComponent {
|
||||
display: flex;
|
||||
height: calc(100% - 46px);
|
||||
.left {
|
||||
width: 200px;
|
||||
background: #fff;
|
||||
.ant-menu-item-group-title {
|
||||
color: #000;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
border-top: 1px solid #efefef;
|
||||
border-bottom: 1px solid #efefef;
|
||||
}
|
||||
.ant-menu-item-group-list .ant-menu-item,
|
||||
.ant-menu-item-group-list .ant-menu-submenu-title {
|
||||
padding-left: 16px !important;
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
}
|
||||
.addComp {
|
||||
padding: 8px 16px;
|
||||
color: blue;
|
||||
font-size: 16px;
|
||||
text-decoration: underline;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #efefef;
|
||||
border-bottom: 1px solid #efefef;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
flex: 1;
|
||||
// clear: both;
|
||||
}
|
||||
}
|
||||
@@ -52,13 +52,6 @@ class TableList extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
const activeStep = _.get(this.props, 'location.state.activeStep', 0);
|
||||
this.setState(() => ({
|
||||
current: activeStep,
|
||||
}));
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitalData();
|
||||
}
|
||||
@@ -73,20 +66,6 @@ class TableList extends React.Component {
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
const traitType = _.get(this.props, 'location.state.TraitType', '');
|
||||
if (traitType) {
|
||||
this.setState({
|
||||
availableTraitList: traits,
|
||||
traitNum: [
|
||||
{
|
||||
refname: null,
|
||||
initialData: { name: traitType },
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(res) && res.length) {
|
||||
this.setState(
|
||||
() => ({
|
||||
@@ -94,7 +73,13 @@ class TableList extends React.Component {
|
||||
}),
|
||||
() => {
|
||||
if (this.state.current === 0) {
|
||||
const WorkloadType = _.get(this.props, 'location.state.WorkloadType', '');
|
||||
let WorkloadType = '';
|
||||
if (this.props.location.state) {
|
||||
WorkloadType = _.get(this.props, 'location.state.WorkloadType', '');
|
||||
sessionStorage.setItem('WorkloadType', WorkloadType);
|
||||
} else {
|
||||
WorkloadType = sessionStorage.getItem('WorkloadType');
|
||||
}
|
||||
this.formRefStep1.current.setFieldsValue({
|
||||
workload_type: WorkloadType || this.state.workloadList[0].name,
|
||||
});
|
||||
|
||||
642
dashboard/src/pages/ApplicationList/CreateComponent/index.jsx
Normal file
642
dashboard/src/pages/ApplicationList/CreateComponent/index.jsx
Normal file
@@ -0,0 +1,642 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Button, Row, Col, Form, Input, Select, Steps, message, Breadcrumb } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import _ from 'lodash';
|
||||
import CreateTraitItem from '../createTrait/index.jsx';
|
||||
|
||||
const { Option } = Select;
|
||||
const { Step } = Steps;
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 8,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 16,
|
||||
},
|
||||
};
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.workload,
|
||||
currentEnv: globalData.currentEnv,
|
||||
}))
|
||||
class TableList extends React.Component {
|
||||
formRefStep1 = React.createRef();
|
||||
|
||||
formRefStep2All = React.createRef();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
current: 0,
|
||||
isShowMore: false,
|
||||
traitNum: [
|
||||
{
|
||||
refname: null,
|
||||
initialData: {},
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
],
|
||||
traitList: [],
|
||||
availableTraitList: [],
|
||||
workloadList: [],
|
||||
workloadSettings: [],
|
||||
step1SubmitObj: {},
|
||||
step1InitialValues: {
|
||||
workload_type: '',
|
||||
},
|
||||
step1Settings: [],
|
||||
appName: '',
|
||||
envName: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getInitalData();
|
||||
}
|
||||
|
||||
getInitalData = async () => {
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'workload/getWorkload',
|
||||
});
|
||||
const traits = await this.props.dispatch({
|
||||
type: 'trait/getTraits',
|
||||
});
|
||||
this.setState({
|
||||
traitList: traits,
|
||||
});
|
||||
if (Array.isArray(res) && res.length) {
|
||||
this.setState(
|
||||
() => ({
|
||||
workloadList: res,
|
||||
}),
|
||||
() => {
|
||||
if (this.state.current === 0) {
|
||||
let WorkloadType = '';
|
||||
if (this.props.location.state) {
|
||||
WorkloadType = _.get(this.props, 'location.state.WorkloadType', '');
|
||||
sessionStorage.setItem('WorkloadType', WorkloadType);
|
||||
} else {
|
||||
WorkloadType = sessionStorage.getItem('WorkloadType');
|
||||
}
|
||||
this.formRefStep1.current.setFieldsValue({
|
||||
workload_type: WorkloadType || this.state.workloadList[0].name,
|
||||
});
|
||||
this.workloadTypeChange(this.state.workloadList[0].name);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
onFinishStep1 = (values) => {
|
||||
this.setState({
|
||||
current: 1,
|
||||
step1InitialValues: values,
|
||||
isShowMore: false,
|
||||
});
|
||||
};
|
||||
|
||||
onFinishStep2 = async () => {
|
||||
const asyncValidateArray = [];
|
||||
this.state.traitNum.forEach((item) => {
|
||||
asyncValidateArray.push(item.refname.validateFields());
|
||||
});
|
||||
await Promise.all(asyncValidateArray);
|
||||
const newTraitNum = this.state.traitNum.map((item) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
item.initialData = item.refname.getSelectValue();
|
||||
return item;
|
||||
});
|
||||
// 进行trait数据整理,便于第三步展示
|
||||
this.setState(() => ({
|
||||
traitNum: newTraitNum,
|
||||
current: 2,
|
||||
}));
|
||||
};
|
||||
|
||||
gotoStep2 = () => {
|
||||
this.setState({
|
||||
current: 1,
|
||||
isShowMore: false,
|
||||
});
|
||||
};
|
||||
|
||||
gotoStep1 = () => {
|
||||
this.setState({
|
||||
current: 0,
|
||||
});
|
||||
};
|
||||
|
||||
changeShowMore = () => {
|
||||
this.setState({
|
||||
isShowMore: true,
|
||||
});
|
||||
};
|
||||
|
||||
addMore = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState((prev) => ({
|
||||
traitNum: prev.traitNum.concat([
|
||||
{
|
||||
refname: null,
|
||||
initialData: {},
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
]),
|
||||
}));
|
||||
};
|
||||
|
||||
createApp = async () => {
|
||||
const { traitNum } = this.state;
|
||||
const { step1SubmitObj } = this.state;
|
||||
if (step1SubmitObj.env_name !== this.props.currentEnv) {
|
||||
step1SubmitObj.env_name = this.props.currentEnv;
|
||||
}
|
||||
const submitObj = _.cloneDeep(step1SubmitObj);
|
||||
const { workload_name: workloadName } = step1SubmitObj;
|
||||
submitObj.flags.push({
|
||||
name: 'name',
|
||||
value: workloadName.toString(),
|
||||
});
|
||||
// 处理数据为提交的格式
|
||||
if (traitNum.length) {
|
||||
const { env_name: envName } = step1SubmitObj;
|
||||
const step2SubmitObj = [];
|
||||
traitNum.forEach(({ initialData }) => {
|
||||
if (initialData.name) {
|
||||
const initialObj = {
|
||||
name: initialData.name,
|
||||
env_name: envName,
|
||||
workload_name: workloadName,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(initialData).forEach((key) => {
|
||||
if (key !== 'name' && initialData[key]) {
|
||||
initialObj.flags.push({
|
||||
name: key,
|
||||
value: initialData[key].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
step2SubmitObj.push(initialObj);
|
||||
}
|
||||
});
|
||||
submitObj.traits = step2SubmitObj;
|
||||
}
|
||||
const res = await this.props.dispatch({
|
||||
type: 'workload/createWorkload',
|
||||
payload: {
|
||||
params: submitObj,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
const { appName, envName } = this.state;
|
||||
this.props.history.push({
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createWorkload = async () => {
|
||||
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,
|
||||
flags: [],
|
||||
};
|
||||
Object.keys(currentData).forEach((key) => {
|
||||
if (key !== 'workload_name' && key !== 'workload_type' && currentData[key]) {
|
||||
submitObj.flags.push({
|
||||
name: key,
|
||||
value: currentData[key].toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
current: 1,
|
||||
step1InitialValues: currentData,
|
||||
step1Settings: submitObj.flags,
|
||||
step1SubmitObj: submitObj,
|
||||
});
|
||||
this.getAcceptTrait(currentData.workload_type);
|
||||
};
|
||||
|
||||
workloadTypeChange = (value) => {
|
||||
const content = this.formRefStep1.current.getFieldsValue();
|
||||
this.formRefStep1.current.resetFields();
|
||||
const initialObj = {
|
||||
workload_type: content.workload_type,
|
||||
workload_name: content.workload_name,
|
||||
};
|
||||
this.formRefStep1.current.setFieldsValue(initialObj);
|
||||
const currentWorkloadSetting = this.state.workloadList.filter((item) => {
|
||||
return item.name === value;
|
||||
});
|
||||
if (currentWorkloadSetting.length) {
|
||||
this.setState(
|
||||
{
|
||||
workloadSettings: currentWorkloadSetting[0].parameters,
|
||||
},
|
||||
() => {
|
||||
this.state.workloadSettings.forEach((item) => {
|
||||
if (item.default) {
|
||||
initialObj[item.name] = item.default;
|
||||
}
|
||||
});
|
||||
this.formRefStep1.current.setFieldsValue(initialObj);
|
||||
},
|
||||
);
|
||||
}
|
||||
this.setState({
|
||||
traitNum: [
|
||||
{
|
||||
refname: null,
|
||||
initialData: {},
|
||||
uniq: new Date().valueOf(),
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
getAcceptTrait = (workloadType) => {
|
||||
const res = this.state.traitList.filter((item) => {
|
||||
if (item.appliesTo.indexOf(workloadType) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this.setState(() => ({
|
||||
availableTraitList: res,
|
||||
}));
|
||||
};
|
||||
|
||||
deleteTraitItem = (uniq) => {
|
||||
// 删除的时候不要依据数组的index删除,要一个唯一性的值
|
||||
this.state.traitNum = this.state.traitNum.filter((item) => {
|
||||
return item.uniq !== uniq;
|
||||
});
|
||||
this.setState((prev) => ({
|
||||
traitNum: prev.traitNum,
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { appName, envName } = this.state;
|
||||
const { current, step1InitialValues, traitNum, workloadSettings } = this.state;
|
||||
let { workloadList } = this.state;
|
||||
workloadList = Array.isArray(workloadList) ? workloadList : [];
|
||||
let currentDetail;
|
||||
if (current === 0) {
|
||||
currentDetail = (
|
||||
<div>
|
||||
<div className="minBox">
|
||||
<Form
|
||||
initialValues={step1InitialValues}
|
||||
labelAlign="left"
|
||||
{...layout}
|
||||
ref={this.formRefStep1}
|
||||
name="control-ref"
|
||||
onFinish={this.onFinishStep1}
|
||||
style={{ width: '60%' }}
|
||||
>
|
||||
<div style={{ padding: '16px 48px 0px 16px' }}>
|
||||
<Form.Item
|
||||
name="workload_name"
|
||||
label="Name"
|
||||
rules={[
|
||||
{
|
||||
pattern: /^[a-z0-9-_]+$/,
|
||||
message:
|
||||
'Names can only use digits(0-9),lowercase letters(a-z),and dashes(-),Underline.',
|
||||
},
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input name!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="workload_type"
|
||||
label="Workload Type"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select Workload Type!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a Workload Type"
|
||||
allowClear
|
||||
onChange={this.workloadTypeChange}
|
||||
>
|
||||
{workloadList.length ? (
|
||||
workloadList.map((item) => {
|
||||
return (
|
||||
<Option value={item.name} key={item.name}>
|
||||
{item.name}
|
||||
</Option>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item
|
||||
label="Settings"
|
||||
style={{ background: 'rgba(0, 0, 0, 0.04)', paddingLeft: '16px' }}
|
||||
/>
|
||||
<div className="relativeBox">
|
||||
<p className="hasMore">?</p>
|
||||
{Array.isArray(workloadSettings) && workloadSettings.length ? (
|
||||
workloadSettings.map((item) => {
|
||||
if (item.name === 'name') {
|
||||
return <Fragment key={item.name} />;
|
||||
}
|
||||
return item.type === 4 ? (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required,
|
||||
message: `Please input ${item.name}!`,
|
||||
},
|
||||
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
name={item.name}
|
||||
label={item.name}
|
||||
key={item.name}
|
||||
rules={[
|
||||
{
|
||||
required: item.required,
|
||||
message: `Please input ${item.name}!`,
|
||||
},
|
||||
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
<div className="buttonBox">
|
||||
<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>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (current === 1) {
|
||||
currentDetail = (
|
||||
<div>
|
||||
<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>
|
||||
</p>
|
||||
</div>
|
||||
<div style={{ border: '1px solid #eee', padding: '16px 48px 16px 16px' }}>
|
||||
<p className="title">{step1InitialValues.workload_type}</p>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<span>apps/v1</span>
|
||||
<span
|
||||
style={{
|
||||
color: '#1890ff',
|
||||
cursor: 'pointer',
|
||||
display: this.state.isShowMore ? 'none' : 'black',
|
||||
}}
|
||||
onClick={this.changeShowMore}
|
||||
>
|
||||
more...
|
||||
</span>
|
||||
</div>
|
||||
{this.state.isShowMore ? (
|
||||
<div>
|
||||
<p className="title" style={{ marginTop: '16px' }}>
|
||||
Settings:
|
||||
</p>
|
||||
<Row>
|
||||
{this.state.step1Settings.map((item) => {
|
||||
return (
|
||||
<Fragment key={item.name}>
|
||||
<Col span="8">
|
||||
<p>{item.name}:</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{item.value}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
<div ref={this.formRefStep2All}>
|
||||
{traitNum.map((item) => {
|
||||
return (
|
||||
<CreateTraitItem
|
||||
onRef={(ref) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
item.refname = ref;
|
||||
}}
|
||||
key={item.uniq.toString()}
|
||||
availableTraitList={this.state.availableTraitList}
|
||||
uniq={item.uniq}
|
||||
initialValues={item.initialData}
|
||||
deleteTraitItem={this.deleteTraitItem}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<button style={{ marginTop: '16px' }} onClick={this.addMore} type="button">
|
||||
Add More...
|
||||
</button>
|
||||
<div className="buttonBox">
|
||||
<Button type="primary" className="floatRight" onClick={this.onFinishStep2}>
|
||||
Next
|
||||
</Button>
|
||||
<Button className="floatRightGap" onClick={this.gotoStep1}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
currentDetail = (
|
||||
<div>
|
||||
<div className="minBox">
|
||||
<p>
|
||||
Name:<span>{step1InitialValues.workload_name}</span>
|
||||
</p>
|
||||
<Row>
|
||||
<Col span="11">
|
||||
<div className="summaryBox1">
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{step1InitialValues.workload_type}</p>
|
||||
<p>apps/v1</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title hasMargin">Settings:</p>
|
||||
<Row>
|
||||
{this.state.step1Settings.map((item) => {
|
||||
return (
|
||||
<Fragment key={item.name}>
|
||||
<Col span="8">
|
||||
<p>{item.name}:</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{item.value}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span="1" />
|
||||
<Col span="10">
|
||||
{traitNum.map(({ initialData }, index) => {
|
||||
if (initialData.name) {
|
||||
return (
|
||||
<div className="summaryBox" key={index.toString()}>
|
||||
<Row>
|
||||
<Col span="22">
|
||||
<p className="title">{initialData.name}</p>
|
||||
<p>core.oam.dev/v1alpha2</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<p className="title hasMargin">Properties:</p>
|
||||
<Row>
|
||||
{Object.keys(initialData).map((currentKey) => {
|
||||
if (currentKey !== 'name') {
|
||||
return (
|
||||
<Fragment key={currentKey}>
|
||||
<Col span="8">
|
||||
<p>{currentKey}:</p>
|
||||
</Col>
|
||||
<Col span="16">
|
||||
<p>{initialData[currentKey]}</p>
|
||||
</Col>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return <Fragment key={currentKey} />;
|
||||
})}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <Fragment key={index.toString()} />;
|
||||
})}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div className="buttonBox">
|
||||
<Button
|
||||
type="primary"
|
||||
className="floatRight"
|
||||
onClick={() => {
|
||||
this.createApp();
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
<Button className="floatRightGap" onClick={this.gotoStep2}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Applications</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>
|
||||
<Link
|
||||
to={{
|
||||
pathname: `/ApplicationList/${appName}/Components`,
|
||||
state: { appName, envName },
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
{appName}
|
||||
</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>createComponent</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<PageContainer>
|
||||
<div className="create-container create-app">
|
||||
<Steps current={current}>
|
||||
<Step title="Step 1" description="Choose Workload" />
|
||||
<Step title="Step 2" description="Attach Trait" />
|
||||
<Step title="Step 3" description="Review and confirm" />
|
||||
</Steps>
|
||||
{currentDetail}
|
||||
</div>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TableList;
|
||||
@@ -0,0 +1,66 @@
|
||||
.create-container {
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
.create-app {
|
||||
.minBox {
|
||||
margin: 20px 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #eee;
|
||||
.title {
|
||||
font-size: 16px;
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
.buttonBox {
|
||||
clear: both;
|
||||
height: 42px;
|
||||
margin: 0 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
.floatRight {
|
||||
float: right;
|
||||
}
|
||||
.floatRightGap {
|
||||
float: right;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.relativeBox {
|
||||
position: relative;
|
||||
padding: 0 48px 0 16px;
|
||||
.hasMore {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 16px;
|
||||
padding: 4px;
|
||||
color: rgb(24, 144, 255);
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.summaryBox1,
|
||||
.summaryBox2,
|
||||
.summaryBox {
|
||||
clear: both;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 10px 10px;
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.summaryBox1 {
|
||||
color: #fff;
|
||||
background: rgb(24, 144, 255);
|
||||
}
|
||||
.summaryBox2 {
|
||||
background: yellow;
|
||||
}
|
||||
.summaryBox {
|
||||
border: 1px solid black;
|
||||
}
|
||||
.hasMargin {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,34 @@
|
||||
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 } from 'antd';
|
||||
import { Button, Card, Row, Col, Form, Spin, Empty, Breadcrumb, Modal, Input } from 'antd';
|
||||
import { connect } from 'dva';
|
||||
import moment from 'moment';
|
||||
import './index.less';
|
||||
import { Link } from 'umi';
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
span: 6,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 18,
|
||||
},
|
||||
};
|
||||
|
||||
@connect(({ loading, applist, globalData }) => ({
|
||||
loadingAll: loading.models.applist,
|
||||
returnObj: applist.returnObj,
|
||||
currentEnv: globalData.currentEnv,
|
||||
}))
|
||||
class TableList extends React.Component {
|
||||
formRef = React.createRef();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.state = {
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -43,6 +56,23 @@ class TableList extends React.Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
await this.formRef.current.validateFields();
|
||||
// const submitData = await this.formRef.current.validateFields();
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
onFinish = () => {};
|
||||
|
||||
handleChange = () => {};
|
||||
@@ -153,6 +183,40 @@ class TableList extends React.Component {
|
||||
)}
|
||||
</Row>
|
||||
</Spin>
|
||||
<Modal
|
||||
title="Create Application"
|
||||
visible={this.state.visible}
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<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 application name!',
|
||||
},
|
||||
{
|
||||
pattern: /^[a-z0-9-_]+$/,
|
||||
message:
|
||||
'Name can only use digits(0-9),lowercase letters(a-z),and dashes(-),Underline.',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="Description">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Space, Button, Row, Col, message, Spin, Breadcrumb } from 'antd';
|
||||
import { Space, Button, Row, Col, message, Spin, Breadcrumb, Modal } from 'antd';
|
||||
import { Link } from 'umi';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
@connect(({ loading, globalData }) => ({
|
||||
loadingAll: loading.models.capability,
|
||||
currentEnv: globalData.currentEnv,
|
||||
@@ -16,6 +19,7 @@ class TableList extends React.PureComponent {
|
||||
this.state = {
|
||||
workloadList: [],
|
||||
traitList: [],
|
||||
capabilityCenterName: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,7 +35,16 @@ class TableList extends React.PureComponent {
|
||||
const workloadList = [];
|
||||
const traitList = [];
|
||||
if (Array.isArray(res)) {
|
||||
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
let capabilityCenterName = '';
|
||||
if (this.props.location.state) {
|
||||
capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
sessionStorage.setItem('capabilityCenterName', capabilityCenterName);
|
||||
} else {
|
||||
capabilityCenterName = sessionStorage.getItem('capabilityCenterName');
|
||||
}
|
||||
this.setState({
|
||||
capabilityCenterName,
|
||||
});
|
||||
res.forEach((item) => {
|
||||
if (item.center === capabilityCenterName) {
|
||||
if (item.type === 'workload') {
|
||||
@@ -55,7 +68,7 @@ class TableList extends React.PureComponent {
|
||||
|
||||
installSignle = async (e, name) => {
|
||||
e.stopPropagation();
|
||||
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
const { capabilityCenterName } = this.state;
|
||||
const res = await this.props.dispatch({
|
||||
type: 'capability/syncOneCapability',
|
||||
payload: {
|
||||
@@ -65,7 +78,10 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,13 +96,16 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
syncAllSignle = async () => {
|
||||
const capabilityCenterName = _.get(this.props, 'location.state.name', '');
|
||||
const { capabilityCenterName } = this.state;
|
||||
if (capabilityCenterName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'capability/syncCapability',
|
||||
@@ -96,13 +115,51 @@ class TableList extends React.PureComponent {
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
window.location.reload();
|
||||
this.getInitialData();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
showDeleteConfirm = () => {
|
||||
message.info('正在开发中...');
|
||||
// eslint-disable-next-line
|
||||
const _this = this;
|
||||
const { capabilityCenterName } = this.state;
|
||||
if (capabilityCenterName) {
|
||||
confirm({
|
||||
title: `Are you sure delete ${capabilityCenterName}?`,
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
width: 500,
|
||||
content: (
|
||||
<div>
|
||||
<p style={{ margin: '0px' }}>您本次移除 {capabilityCenterName},将会删除的应用列表:</p>
|
||||
<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: {
|
||||
capabilityCenterName,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
_this.props.history.push({ pathname: '/Capability' });
|
||||
}
|
||||
},
|
||||
onCancel() {
|
||||
// console.log('Cancel');
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Table, Space, Modal, Form, Input, message, Spin, Breadcrumb } from 'antd';
|
||||
import { CopyOutlined } from '@ant-design/icons';
|
||||
import { CopyOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import { Link } from 'umi';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
|
||||
const { Column } = Table;
|
||||
const { confirm } = Modal;
|
||||
|
||||
const layout = {
|
||||
labelCol: {
|
||||
@@ -31,6 +32,7 @@ class TableList extends React.PureComponent {
|
||||
this.state = {
|
||||
visible: false,
|
||||
capabilityList: [],
|
||||
isCreateLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,14 +57,19 @@ class TableList extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
showModal = () => {
|
||||
this.setState({
|
||||
showModal = async () => {
|
||||
await this.setState({
|
||||
visible: true,
|
||||
isCreateLoading: false,
|
||||
});
|
||||
this.formRef.current.resetFields();
|
||||
};
|
||||
|
||||
handleOk = async () => {
|
||||
const submitData = await this.formRef.current.validateFields();
|
||||
this.setState({
|
||||
isCreateLoading: true,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'capability/createCapabilityCenter',
|
||||
payload: {
|
||||
@@ -111,7 +118,9 @@ class TableList extends React.PureComponent {
|
||||
this.setState(() => ({
|
||||
capabilityList: newList1,
|
||||
}));
|
||||
window.location.reload();
|
||||
await this.props.dispatch({
|
||||
type: 'menus/getMenuData',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -126,8 +135,40 @@ class TableList extends React.PureComponent {
|
||||
message.success('copy success');
|
||||
};
|
||||
|
||||
showDeleteConfirm = () => {
|
||||
message.info('正在开发中...');
|
||||
showDeleteConfirm = (record) => {
|
||||
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>
|
||||
<p>确认后,移除{record},并且删除相应的应用?</p>
|
||||
</div>
|
||||
),
|
||||
okText: 'Yes',
|
||||
okType: 'danger',
|
||||
cancelText: 'No',
|
||||
async onOk() {
|
||||
const res = await _this.props.dispatch({
|
||||
type: 'capability/deleteCapability',
|
||||
payload: {
|
||||
capabilityCenterName: record,
|
||||
},
|
||||
});
|
||||
if (res) {
|
||||
message.success(res);
|
||||
_this.getInitialData();
|
||||
}
|
||||
},
|
||||
onCancel() {
|
||||
// console.log('Cancel');
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -135,6 +176,7 @@ class TableList extends React.PureComponent {
|
||||
let { loadingList } = this.props;
|
||||
loadingList = loadingList || false;
|
||||
capabilityList = Array.isArray(capabilityList) ? capabilityList : [];
|
||||
const { isCreateLoading } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
@@ -160,7 +202,12 @@ class TableList extends React.PureComponent {
|
||||
onOk={this.handleOk}
|
||||
onCancel={this.handleCancel}
|
||||
footer={[
|
||||
<Button key="submit" type="primary" onClick={this.handleOk}>
|
||||
<Button
|
||||
loading={isCreateLoading}
|
||||
key="submit"
|
||||
type="primary"
|
||||
onClick={this.handleOk}
|
||||
>
|
||||
Create
|
||||
</Button>,
|
||||
]}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Button, Table, Space, Modal, Form, Input, Tooltip } from 'antd';
|
||||
import { Button, Table, Space, Modal, Form, Input, Tooltip, Breadcrumb } from 'antd';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import './index.less';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
const { confirm } = Modal;
|
||||
@@ -147,65 +148,76 @@ const TableList = (props) => {
|
||||
},
|
||||
];
|
||||
return (
|
||||
<PageContainer>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={showModal}>
|
||||
Create
|
||||
</Button>
|
||||
</Space>
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>
|
||||
<Link to="/ApplicationList">Home</Link>
|
||||
</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>System</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>Env</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<Modal
|
||||
getContainer={false}
|
||||
title={env && env.envName ? 'Update Env' : 'Create Env'}
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key="submit" type="primary" onClick={handleOk}>
|
||||
{env && env.envName ? 'Update' : 'Create'}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form {...layout} form={form} name="control-ref" labelAlign="left">
|
||||
<Form.Item
|
||||
name="envName"
|
||||
label="Env"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input Evn!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled={!!(env && env.envName)} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="namespace"
|
||||
label="Namespace"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please specify a Namespace!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Table rowKey={(record) => record.envName} columns={columns} dataSource={tableEnvs} />
|
||||
</PageContainer>
|
||||
<PageContainer>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<Space>
|
||||
<Button type="primary" onClick={showModal}>
|
||||
Create
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<Modal
|
||||
getContainer={false}
|
||||
title={env && env.envName ? 'Update Env' : 'Create Env'}
|
||||
visible={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key="submit" type="primary" onClick={handleOk}>
|
||||
{env && env.envName ? 'Update' : 'Create'}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form {...layout} form={form} name="control-ref" labelAlign="left">
|
||||
<Form.Item
|
||||
name="envName"
|
||||
label="Env"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please input Evn!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled={!!(env && env.envName)} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="namespace"
|
||||
label="Namespace"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please specify a Namespace!',
|
||||
},
|
||||
{
|
||||
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
|
||||
message:
|
||||
'The maximum length is 63,should be combination of numbers,alphabets,underline!',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Table rowKey={(record) => record.envName} columns={columns} dataSource={tableEnvs} />
|
||||
</PageContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default connect((env) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Breadcrumb } from 'antd';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Breadcrumb, Space } from 'antd';
|
||||
import { CheckCircleOutlined } from '@ant-design/icons';
|
||||
import _ from 'lodash';
|
||||
import { connect } from 'dva';
|
||||
@@ -61,6 +61,17 @@ const columns = [
|
||||
dataIndex: 'Age',
|
||||
key: 'Age',
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: () => (
|
||||
<Space>
|
||||
<Button>logs</Button>
|
||||
<Button>exec</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
@@ -144,6 +155,7 @@ class TableList extends React.Component {
|
||||
hasShowEdit2: false,
|
||||
traitItem: {},
|
||||
appName: '',
|
||||
envName: '',
|
||||
appDetailData: {},
|
||||
};
|
||||
}
|
||||
@@ -153,16 +165,27 @@ class TableList extends React.Component {
|
||||
}
|
||||
|
||||
async getInitialData() {
|
||||
const traitItem = _.get(this.props, 'location.state.traitItem', {});
|
||||
let traitItem = '';
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
traitItem = _.get(this.props, 'location.state.traitItem', '');
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('traitItem', traitItem);
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
traitItem = sessionStorage.getItem('traitItem');
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
traitItem,
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
if (appName && envName) {
|
||||
this.setState({
|
||||
appName,
|
||||
});
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
payload: {
|
||||
@@ -203,7 +226,7 @@ class TableList extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasShowEdit, hasShowEdit2 } = this.state;
|
||||
const { hasShowEdit, hasShowEdit2, envName } = this.state;
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const { traitItem, appName } = this.state;
|
||||
const metadata = _.get(traitItem, 'metadata', '');
|
||||
@@ -214,7 +237,6 @@ class TableList extends React.Component {
|
||||
if (traitItem.kind === 'Ingress') {
|
||||
traitType = 2;
|
||||
}
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
return (
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { PageContainer } from '@ant-design/pro-layout';
|
||||
import './index.less';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Spin, Breadcrumb } from 'antd';
|
||||
import { Form, Input, Button, Row, Col, Tabs, Table, Spin, Breadcrumb, Space } from 'antd';
|
||||
import { CheckCircleOutlined } from '@ant-design/icons';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'umi';
|
||||
@@ -61,6 +61,17 @@ const columns = [
|
||||
dataIndex: 'Age',
|
||||
key: 'Age',
|
||||
},
|
||||
{
|
||||
title: 'Action',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: () => (
|
||||
<Space>
|
||||
<Button>logs</Button>
|
||||
<Button>exec</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
@@ -143,6 +154,8 @@ class TableList extends React.PureComponent {
|
||||
hasShowEdit: false,
|
||||
hasShowEdit2: false,
|
||||
appDetailData: {},
|
||||
appName: '',
|
||||
envName: '',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,8 +164,21 @@ class TableList extends React.PureComponent {
|
||||
}
|
||||
|
||||
async getInitialData() {
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
let appName = '';
|
||||
let envName = '';
|
||||
if (this.props.location.state) {
|
||||
appName = _.get(this.props, 'location.state.appName', '');
|
||||
envName = _.get(this.props, 'location.state.envName', '');
|
||||
sessionStorage.setItem('appName', appName);
|
||||
sessionStorage.setItem('envName', envName);
|
||||
} else {
|
||||
appName = sessionStorage.getItem('appName');
|
||||
envName = sessionStorage.getItem('envName');
|
||||
}
|
||||
this.setState({
|
||||
appName,
|
||||
envName,
|
||||
});
|
||||
if (appName && envName) {
|
||||
const res = await this.props.dispatch({
|
||||
type: 'applist/getAppDetail',
|
||||
@@ -194,7 +220,7 @@ class TableList extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasShowEdit, hasShowEdit2 } = this.state;
|
||||
const { hasShowEdit, hasShowEdit2, appName, envName } = this.state;
|
||||
const status = _.get(this.state.appDetailData, 'Status', '');
|
||||
const Workload = _.get(this.state.appDetailData, 'Workload.workload', {});
|
||||
const metadata = _.get(Workload, 'metadata', {});
|
||||
@@ -202,8 +228,6 @@ class TableList extends React.PureComponent {
|
||||
containers = _.get(Workload, 'spec.containers[0]', {});
|
||||
let { loadingAll } = this.props;
|
||||
loadingAll = loadingAll || false;
|
||||
const appName = _.get(this.props, 'location.state.appName', '');
|
||||
const envName = _.get(this.props, 'location.state.envName', '');
|
||||
return (
|
||||
<div>
|
||||
<div className="breadCrumb">
|
||||
|
||||
@@ -31,10 +31,10 @@ export async function syncCapability({ capabilityCenterName }) {
|
||||
});
|
||||
}
|
||||
/*
|
||||
* Delete /api/capabilities/:capabilityName (删除一个 capability)
|
||||
* DELETE /capability-centers/:capabilityCenterName/ (Capability Center delete) (删除一个 capabilityCenter)
|
||||
*/
|
||||
export async function deleteCapability({ capabilityName }) {
|
||||
return request(`/api/capabilities/${capabilityName}`, {
|
||||
export async function deleteCapability({ capabilityCenterName }) {
|
||||
return request(`/api/capability-centers/${capabilityCenterName}`, {
|
||||
method: 'delete',
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user