Complete env page interface docking and page status

77
This commit is contained in:
xiaoyuaiheshui
2020-08-28 18:44:47 +08:00
parent 2a25b10874
commit d341862694
7 changed files with 351 additions and 258 deletions
+4 -4
View File
@@ -1,9 +1,9 @@
// https://umijs.org/config/
import {defineConfig} from 'umi';
import { defineConfig } from 'umi';
import defaultSettings from './defaultSettings';
import proxy from './proxy';
const {REACT_APP_ENV} = process.env;
const { REACT_APP_ENV } = process.env;
export default defineConfig({
hash: true,
antd: {},
@@ -131,8 +131,8 @@ export default defineConfig({
icon: 'table',
path: '/System/Env',
component: './System/Env',
}
]
},
],
},
{
name: 'Capability.Detail',
@@ -0,0 +1,96 @@
.ant-pro-global-header-layout-side {
padding-right: 0;
}
.ant-dropdown-menu-item,
.ant-dropdown-menu-submenu-title {
clear: both;
margin: 0;
padding: 5px 20px;
color: deepskyblue;
font-weight: normal;
font-size: 14px;
line-height: 22px;
white-space: nowrap;
cursor: pointer;
transition: all 0.3s;
border-top: 1px solid #dee4e6;
}
.ant-dropdown-menu-item:nth-child(1) {
border-top: 1px solid #fff;
}
.ant-dropdown-menu-item-active {
background: rgb(0, 21, 41) !important;
}
.ant-dropdown-menu-item-active .box1 {
color: white;
}
.ant-dropdown-menu-item-active .box2 {
color: white;
}
.box {
display: flex;
width: 100%;
flex-direction: column;
}
.box1 {
font-size: 16px;
color: #5095d4;
}
.box2 {
font-size: 12px;
color: #8d959b;
}
.ant-dropdown-trigger {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 100%;
}
.drop-box {
display: flex;
flex-direction: row;
align-items: center;
justify-content: left;
height: 100%;
background: rgb(0, 21, 41);
padding: 0 20px;
}
.btn-box {
display: flex;
flex-direction: column;
align-items: space-between;
overflow: hidden;
margin-right: 20px;
min-width: 120px;
}
.btn-top {
display: flex;
height: 20px;
line-height: 20px;
font-size: 20px;
color: white;
}
.btn-bottom {
display: flex;
height: 20px;
line-height: 20px;
font-size: 12px;
color: #8d959b;
}
.btn-outlined {
fontsize: 15px;
color: #fff;
}
@@ -1,29 +1,32 @@
import { Menu, Dropdown, Button, message } from 'antd';
import { Menu, Dropdown, message } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import React from 'react';
import { connect } from 'dva';
import './WorkSpaceDropDown.css';
@connect(() => ({}))
@connect((env) => ({ envs: env.envs }))
export default class WorkSpaceDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
workSpaceName: '',
envs: [],
namespace: '',
};
}
async componentDidMount() {
const envs = await this.props.dispatch({
type: 'envs/getEnvs', // applist对应models层的命名空间namespace
type: 'envs/getEnvs',
});
if (envs) {
const { name = 'test' } = envs.find((a) => {
return a.current === '*';
const { name, namespace } = envs.find((env) => {
return env.current === '*';
});
this.setState({
envs,
envs: envs,
workSpaceName: name,
namespace: namespace,
});
this.props.dispatch({
type: 'globalData/currentEnv',
@@ -48,6 +51,7 @@ export default class WorkSpaceDropDown extends React.Component {
this.setState(
{
workSpaceName: e.key,
namespace: e.item.props.title,
},
() => {
// 值切换存储
@@ -59,25 +63,37 @@ export default class WorkSpaceDropDown extends React.Component {
});
},
);
await this.props.dispatch({
type: 'envs/getEnvs', // applist对应models层的命名空间namespace
});
};
render() {
const { envs } = this.props;
const menu = (
<Menu onClick={this.handleMenuClick}>
{/* <Menu.Item key="default">default</Menu.Item>
<Menu.Item key="am-system">oam-system</Menu.Item>
<Menu.Item key="linkerd">linkerd</Menu.Item>
<Menu.Item key="rio-system">rio-system</Menu.Item> */}
{this.state.envs.map((item) => {
return <Menu.Item key={item.name}>{item.name}</Menu.Item>;
})}
{envs.envs &&
envs.envs.map((item) => {
return (
<Menu.Item key={item.name} title={item.namespace}>
<div className="box">
<div className="box1">{item.name}</div>
<div className="box2">{item.namespace}</div>
</div>
</Menu.Item>
);
})}
</Menu>
);
return (
<Dropdown overlay={menu}>
<Button style={{ marginTop: '10px' }}>
{this.state.workSpaceName} <DownOutlined />
</Button>
<div className="drop-box">
<div className="btn-box">
<div className="btn-top">{this.state.workSpaceName}</div>
<div className="btn-bottom">{this.state.namespace}</div>
</div>
<DownOutlined style={{ fontSize: '15px', color: '#ffffff' }} />
</div>
</Dropdown>
);
}
@@ -1,84 +0,0 @@
import { Menu, Dropdown, Button, message } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import React from 'react';
import { connect } from 'dva';
@connect(() => ({}))
export default class WorkSpaceDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
workSpaceName: '',
envs: [],
};
}
async componentDidMount() {
const envs = await this.props.dispatch({
type: 'envs/getEnvs', // applist对应models层的命名空间namespace
});
if (envs) {
const { name = 'test' } = envs.find((a) => {
return a.current === '*';
});
this.setState({
envs,
workSpaceName: name,
});
this.props.dispatch({
type: 'globalData/currentEnv',
payload: {
currentEnv: name,
},
});
}
}
handleMenuClick = async (e) => {
// 发送切换envs的接口
const switchResult = await this.props.dispatch({
type: 'envs/switchEnv',
payload: {
currentEnv: e.key,
},
});
if (switchResult) {
message.success(switchResult);
}
this.setState(
{
workSpaceName: e.key,
},
() => {
// 值切换存储
this.props.dispatch({
type: 'globalData/currentEnv',
payload: {
currentEnv: e.key,
},
});
},
);
};
render() {
const menu = (
<Menu onClick={this.handleMenuClick}>
{/* <Menu.Item key="default">default</Menu.Item>
<Menu.Item key="am-system">oam-system</Menu.Item>
<Menu.Item key="linkerd">linkerd</Menu.Item>
<Menu.Item key="rio-system">rio-system</Menu.Item> */}
{this.state.envs.map((item) => {
return <Menu.Item key={item.name}>{item.name}</Menu.Item>;
})}
</Menu>
);
return (
<Dropdown overlay={menu}>
<Button style={{ marginTop: '10px' }}>
{this.state.workSpaceName} <DownOutlined />
</Button>
</Dropdown>
);
}
}
+33 -8
View File
@@ -1,24 +1,49 @@
import {getEnvs, switchEnv, initialEnvs} from '@/services/env';
import { getEnvs, switchEnv, initialEnvs, deleteEnv } from '@/services/env';
const TestModel = {
namespace: 'envs',
state: {
// initailState: '8880'
envs: undefined,
},
effects: {
* getEnvs({payload}, {call}) {
*getEnvs({ payload }, { call, put }) {
const res = yield call(getEnvs, payload);
yield put({
type: 'onGetEnvsSuccess',
payload: res,
});
return res;
},
* switchEnv({payload}, {call}) {
*switchEnv({ payload }, { call }) {
const res = yield call(switchEnv, payload);
return res;
},
* initialEnvs({payload}, {call}) {
const res = yield call(initialEnvs, payload)
*initialEnvs({ payload }, { call, put }) {
yield call(initialEnvs, payload);
const res = yield call(getEnvs, payload);
yield put({
type: 'onGetEnvsSuccess',
payload: res,
});
return res;
}
},
*deleteEnv({ payload }, { call, put }) {
yield call(deleteEnv, payload);
const res = yield call(getEnvs);
yield put({
type: 'onGetEnvsSuccess',
payload: res,
});
return res;
},
},
reducers: {
onGetEnvsSuccess(state, { payload }) {
return {
...state,
envs: payload,
};
},
},
reducers: {},
};
export default TestModel;
+174 -145
View File
@@ -1,30 +1,12 @@
import React from 'react';
import {PageContainer} from '@ant-design/pro-layout';
import {Button, Table, Space, Modal, Form, Input} from 'antd';
import {ExclamationCircleOutlined} from '@ant-design/icons';
import {Link} from 'umi';
import React, { useEffect, useState } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Table, Space, Modal, Form, Input, Tooltip } from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import './index.less';
import {connect} from "dva";
import { connect } from 'dva';
import * as _ from 'lodash';
const {confirm} = Modal;
function showDeleteConfirm(record) {
confirm({
title: `Are you sure delete env ${record.name}?`,
icon: <ExclamationCircleOutlined/>,
width: 500,
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
onOk() {
console.log('OK');
}
});
}
function specifyNamespace(record) {
}
const { confirm } = Modal;
const layout = {
labelCol: {
@@ -35,141 +17,188 @@ const layout = {
},
};
const columns = [
{
title: 'Env',
dataIndex: 'name',
key: 'name'
},
{
title: 'Namespace',
dataIndex: 'namespace',
key: 'namespace',
},
{
title: 'Operations',
dataIndex: 'Operations',
key: 'Operations',
render: (text, record) => {
return (
<Space>
<Button onClick={() => specifyNamespace(record)}>specify namespace</Button>
<Button onClick={() => showDeleteConfirm(record)}>remove</Button>
</Space>
);
},
},
];
const TableList = (props) => {
const { dispatch } = props;
const tableEnvs = _.get(props, 'getEnvs.envs', []);
const [form] = Form.useForm();
const [visible, setVisible] = useState(false);
const [env, setEnv] = useState([]);
@connect(() => ({}))
class TableList extends React.PureComponent {
formRef = React.createRef();
const showModal = () => {
setVisible(true);
};
constructor(props) {
super(props);
this.state = {
visible: false,
envs: []
};
}
const handleOk = async () => {
const fieldsValue = await form.validateFields();
await dispatch({
type: 'envs/initialEnvs',
payload: {
params: fieldsValue,
},
});
setEnv(null);
form.resetFields();
setVisible(false);
};
showModal = () => {
this.setState({
visible: true,
const handleCancel = () => {
setEnv(null);
setVisible(false);
form.resetFields();
};
const deleteEnv = async (record) => {
await dispatch({
type: 'envs/deleteEnv',
payload: {
envName: record.name,
},
});
};
handleOk = async (values) => {
await this.formRef.current.validateFields();
this.setState({
visible: false,
});
// const fieldsValue = await this.formRef.current.validateFields();
// fieldsValue为通过验证的数据,现在可进行提交
// console.log(fieldsValue)
};
handleTest = async () => {
await this.formRef.current.validateFields();
this.setState({
visible: false,
const showDeleteConfirm = (record) => {
confirm({
title: `Are you sure delete env ${record.name}?`,
icon: <ExclamationCircleOutlined />,
width: 500,
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
onOk() {
deleteEnv(record);
},
});
};
handleCancel = () => {
this.setState({
visible: false,
const specifyNamespace = (record) => {
form.setFieldsValue({
name: record.name,
namespace: record.namespace,
});
setEnv(record);
setVisible(true);
};
getInitalData = async () => {
const envs = await this.props.dispatch({
const getInitialData = async () => {
await dispatch({
type: 'envs/getEnvs',
});
this.setState({
envs
});
}
};
useEffect(() => {
getInitialData();
}, []);
componentDidMount() {
this.getInitalData();
}
render() {
return (
<PageContainer>
<div style={{marginBottom: '16px'}}>
const columns = [
{
title: 'Env',
dataIndex: 'name',
key: 'name',
render: (text) => {
if (text.length > 20) {
return <Tooltip title={text}>{text.substr(0, 20)}...</Tooltip>;
}
return text;
},
},
{
title: 'Namespace',
dataIndex: 'namespace',
key: 'namespace',
render: (text) => {
if (text.length > 20) {
return <Tooltip title={text}>{text.substr(0, 20)}...</Tooltip>;
}
return text;
},
},
{
title: 'Current',
dataIndex: 'current',
align: 'center',
key: 'current',
render: (text) => {
return text === '*' ? 'active' : '';
},
},
{
title: 'Operations',
dataIndex: 'Operations',
key: 'Operations',
render: (text, record) => {
return (
<Space>
<Button type="primary" onClick={this.showModal}>
Create
<Button onClick={() => specifyNamespace(record)}>update</Button>
<Button disabled={record.current} onClick={() => showDeleteConfirm(record)}>
remove
</Button>
</Space>
</div>
<Modal
title="Create Env"
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="Env"
rules={[
{
required: true, message: 'Please input Evn!'
},
]}
>
<Input/>
</Form.Item>
<Form.Item
name="namespace"
label="Namespace"
rules={[
{
required: true, message: 'Please specify a Namespace!'
},
]}
>
<Input/>
</Form.Item>
</Form>
</Modal>
<Table
rowKey={(record) => record.name}
columns={columns} dataSource={this.state.envs}/>
</PageContainer>
);
}
}
export default TableList;
);
},
},
];
return (
<PageContainer>
<div style={{ marginBottom: '16px' }}>
<Space>
<Button type="primary" onClick={showModal}>
Create
</Button>
</Space>
</div>
<Modal
getContainer={false}
title={env && env.name ? 'Update Env' : 'Create Env'}
visible={visible}
onOk={handleOk}
onCancel={handleCancel}
footer={[
<Button key="submit" type="primary" onClick={handleOk}>
{env && env.name ? 'Update' : 'Create'}
</Button>,
]}
>
<Form {...layout} form={form} name="control-ref" labelAlign="left">
<Form.Item
name="name"
label="Env"
rules={[
{
required: true,
message: 'Please input Evn!',
},
{
pattern: new RegExp('^[0-9a-zA-Z_]{1,32}$', 'g'),
message: 'Should be combination of numbers,alphabets,underline',
},
]}
>
<Input disabled={!!(env && env.name)} />
</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.name} columns={columns} dataSource={tableEnvs} />
</PageContainer>
);
};
export default connect((env) => {
return {
getEnvs: env.envs,
};
})(TableList);
+12 -1
View File
@@ -1,24 +1,34 @@
import request from '@/utils/request';
/*
* 初始化 envPost /api/envs/
*/
export async function initialEnvs() {
export async function initialEnvs({ params }) {
return request('/api/envs/', {
method: 'post',
data: {
...params,
},
headers: {
'Content-Type': 'application/json',
},
});
}
/*
* env 列表:Get /api/envs/
*/
export async function getEnvs() {
return request('/api/envs/');
}
/*
* 查询 envGet /api/envs/:envName
*/
export async function searchEnvs({ envName }) {
return request(`/api/envs/${envName}`);
}
/*
* 删除 envDelete /api/envs/:envName
*/
@@ -27,6 +37,7 @@ export async function deleteEnv({ envName }) {
method: 'delete',
});
}
/*
* 切换 envPatch /api/envs/:envName
*/