refactor: Refactor console overview codes

Refactor console overview codes

Change-Id: I1c64a6def03979d08316d485751acca6dc6341c2
This commit is contained in:
Jingwei.Zhang 2021-09-18 11:03:33 +08:00
parent cc7222106f
commit e182d62b34
7 changed files with 279 additions and 483 deletions

View File

@ -92,7 +92,6 @@
"Any(Random)": "Any(Random)",
"Application Credentials": "Application Credentials",
"Application Template": "Application Template",
"Apply for extended quota": "Apply for extended quota",
"Applying": "Applying",
"Arch": "Arch",
"Architecture": "Architecture",
@ -1330,7 +1329,6 @@
"Resource Class Properties": "Resource Class Properties",
"Resource Not Found": "Resource Not Found",
"Resource Pool": "Resource Pool",
"Resource Statistic (Last 15 Days)": "Resource Statistic (Last 15 Days)",
"Resource Status": "Resource Status",
"Resource Status Reason": "Resource Status Reason",
"Resource Type": "Resource Type",
@ -1702,7 +1700,6 @@
"VNIC Type": "VNIC Type",
"VPN": "VPN",
"VPN EndPoint Group": "VPN EndPoint Group",
"VPN Endpoint Group": "VPN Endpoint Group",
"VPN Gateway": "VPN Gateway",
"VPN Service": "VPN Service",
"VPN Service ID": "VPN Service ID",

View File

@ -92,7 +92,6 @@
"Any(Random)": "任意(随机)",
"Application Credentials": "应用凭证",
"Application Template": "应用模板",
"Apply for extended quota": "申请扩展配额",
"Applying": "使用中",
"Arch": "",
"Architecture": "架构",
@ -1330,7 +1329,6 @@
"Resource Class Properties": "资源类属性",
"Resource Not Found": "资源未找到",
"Resource Pool": "资源池",
"Resource Statistic (Last 15 Days)": "资源统计(最近15天)",
"Resource Status": "资源状态",
"Resource Status Reason": "资源状态信息",
"Resource Type": "资源类型",
@ -1702,7 +1700,6 @@
"VNIC Type": "VNIC类型",
"VPN": "VPN",
"VPN EndPoint Group": "VPN端点组",
"VPN Endpoint Group": "VPN端点组",
"VPN Gateway": "VPN网关",
"VPN Service": "VPN服务",
"VPN Service ID": "VPN服务ID",

View File

@ -40,46 +40,108 @@ export class ProjectInfo extends Component {
);
};
render() {
const { rootStore = {} } = this.props;
const { roles = [], baseRoles = [], user = {} } = rootStore;
get rootStore() {
return this.props.rootStore || {};
}
get currentUser() {
const { user: { user } = {} } = this.rootStore;
return user || {};
}
get showRoles() {
const { roles = [], baseRoles = [] } = this.rootStore;
return roles.filter((it) => baseRoles.indexOf(it.name) === -1);
}
get baseRoles() {
const { roles = [], baseRoles = [] } = this.rootStore;
return roles.filter((it) => baseRoles.indexOf(it.name) !== -1);
}
renderAccount() {
return (
<Descriptions.Item
label={t('User Account')}
labelStyle={{ fontSize: 14 }}
contentStyle={{ fontSize: 14 }}
>
{this.currentUser.name}
</Descriptions.Item>
);
}
renderShowRole() {
return (
<Descriptions.Item
label={t('My Role')}
labelStyle={{ fontSize: 14 }}
contentStyle={{ fontSize: 14 }}
>
{this.showRoles.map((item) => item.name).join(', ')}
</Descriptions.Item>
);
}
renderDomain() {
return (
<Descriptions.Item
label={t('Affiliated Domain')}
labelStyle={{ fontSize: 14 }}
contentStyle={{ fontSize: 14 }}
>
{this.currentUser.domain.name}
</Descriptions.Item>
);
}
renderBaseRole() {
const { collapsed } = this.state;
const icon = collapsed ? <DownOutlined /> : <UpOutlined />;
if (!user) {
if (collapsed) {
return null;
}
const { user: currentUser } = user;
const showRole = roles.filter((it) => baseRoles.indexOf(it.name) === -1);
const baseRole = roles.filter((it) => baseRoles.indexOf(it.name) !== -1);
return (
<Descriptions.Item
label={t('Base Role')}
labelStyle={{ fontSize: 14 }}
contentStyle={{ fontSize: 14 }}
>
{this.baseRoles.map((item) => item.name).join(', ')}
</Descriptions.Item>
);
}
renderButton() {
const { collapsed } = this.state;
const icon = collapsed ? <DownOutlined /> : <UpOutlined />;
return (
<Button
onClick={this.handleDetailInfo}
icon={icon}
type="link"
className={styles.role_button}
/>
);
}
render() {
if (!this.currentUser.name) {
return null;
}
return (
<Card
className={styles.top}
title={`Hello, ${currentUser.name}`}
className={styles.project}
title={`Hello, ${this.currentUser.name}`}
bordered={false}
>
<Descriptions column={1}>
<Descriptions.Item label={t('User Account')}>
{currentUser.name}
</Descriptions.Item>
<Descriptions.Item label={t('My Role')}>
{showRole.map((item) => item.name).join(', ')}
</Descriptions.Item>
<Descriptions.Item label={t('Affiliated Domain')}>
{currentUser.domain.name}
</Descriptions.Item>
{collapsed ? null : (
<Descriptions.Item label={t('Base Role')}>
{baseRole.map((item) => item.name).join(', ')}
</Descriptions.Item>
)}
{this.renderAccount()}
{this.renderShowRole()}
{this.renderDomain()}
{this.renderBaseRole()}
</Descriptions>
<Button
onClick={this.handleDetailInfo}
icon={icon}
type="link"
className={styles.role_button}
/>
{this.renderButton()}
</Card>
);
}

View File

@ -27,6 +27,58 @@ const colors = {
full: { color: '#E8684A', text: t('Full') },
};
export const quotaCardList = [
{
text: t('Compute'),
type: 'compute',
value: [
{ text: t('Instances'), key: 'instances' },
{ text: t('vCPUs'), key: 'cores' },
{ text: t('Memory'), key: 'ram' },
{ text: t('Server Group'), key: 'server_groups' },
],
},
{
text: t('Storage'),
type: 'storage',
value: [
{
text: t('volumes'),
key: 'volumes',
},
{
text: t('Gigabytes(GB)'),
key: 'gigabytes',
},
{
text: t('Snapshots'),
key: 'snapshots',
},
{
text: t('backups'),
key: 'backups',
},
{
text: t('backup gigabytes (GiB)'),
key: 'backup_gigabytes',
},
],
},
{
text: t('Network'),
type: 'network',
value: [
{ text: t('Router'), key: 'router' },
{ text: t('Network'), key: 'network' },
{ text: t('Subnet'), key: 'subnet' },
{ text: t('Floating IP'), key: 'floatingip' },
{ text: `${t('port')}`, key: 'port' },
{ text: t('Security Group'), key: 'security_group' },
{ text: t('Security Group Rule'), key: 'security_group_rule' },
],
},
];
export class QuotaOverview extends Component {
constructor(props) {
super(props);
@ -39,42 +91,56 @@ export class QuotaOverview extends Component {
this.getData();
}
getData = async () => {
async getData() {
const { user } = this.props.rootStore;
const { project: { id: projectId = '' } = {} } = user;
await globalProjectStore.fetchProjectQuota({ project_id: projectId });
await globalVolumeTypeStore.fetchList();
await globalKeypairStore.fetchList();
await Promise.all([
globalProjectStore.fetchProjectQuota({ project_id: projectId }),
globalVolumeTypeStore.fetchList(),
globalKeypairStore.fetchList(),
]);
this.setState({
isLoading: false,
});
};
}
get volumeTypesQuota() {
const volumeTypes = globalVolumeTypeStore.list.data.map((item, index) => {
return {
index,
value: [
{
text: t('{name} type', { name: item.name }),
key: `volumes_${item.name}`,
},
{
text: t('{name} type gigabytes(GB)', { name: item.name }),
key: `gigabytes_${item.name}`,
},
{
text: t('{name} type snapshots', { name: item.name }),
key: `snapshots_${item.name}`,
},
],
};
});
return {
text: t('Storage Types'),
type: 'VolumeTypes',
value: volumeTypes,
};
}
get quotaCardList() {
return this.props.quotaCardList || quotaCardList;
}
get quotaAction() {
return this.props.quotaAction;
}
getFilteredValue = (value) => value.filter((it) => !it.hidden);
renderQuotaCardList = (quotaCardList) => {
const { isLoading } = this.state;
return (
<Row className={styles.content}>
{Object.keys(quotaCardList).map((item, index) => (
<Col
className={styles.card}
span={index === 3 ? 24 : 12}
key={quotaCardList[item].text}
>
<Card
title={quotaCardList[item].text}
bordered={false}
loading={isLoading}
>
{this.renderQuotaCardContent(index, quotaCardList, item)}
</Card>
</Col>
))}
</Row>
);
};
getItemInfo = (data, i) => {
let percent = 0;
if (data[i.key] && isNumber(data[i.key].used) && data[i.key].limit !== -1) {
@ -115,134 +181,74 @@ export class QuotaOverview extends Component {
);
};
renderQuotaCardContent(index, quotaCardList, item) {
renderQuotaCardList = () => {
const { isLoading } = this.state;
return (
<Row className={styles.content}>
{this.quotaCardList.map((item) => (
<Col className={styles.card} span={12} key={item.type}>
<Card title={item.text} bordered={false} loading={isLoading}>
{this.renderQuotaCardContent(item)}
</Card>
</Col>
))}
<Col className={styles.card} span={24} key={this.volumeTypesQuota.type}>
<Card
title={this.volumeTypesQuota.text}
bordered={false}
loading={isLoading}
>
{this.renderVolumeTypes()}
</Card>
</Col>
</Row>
);
};
renderQuotaCardContent(item) {
const { isLoading } = this.state;
if (isLoading) {
return <Spin />;
}
return index === 3
? this.renderVolumeTypes(
globalProjectStore.quota,
quotaCardList[item].value
)
: this.renderQuotaCart(
globalProjectStore.quota,
this.getFilteredValue(quotaCardList[item].value)
);
return this.renderQuotaCart(
globalProjectStore.quota,
this.getFilteredValue(item.value)
);
}
renderQuotaCart = (data, item = []) =>
item.map((i) => <div key={i.text}>{this.getItemInfo(data, i)}</div>);
renderVolumeTypes = (data, listData) => (
<List
itemLayout="vertical"
size="large"
pagination={{
hideOnSinglePage: true,
pageSize: 5,
size: 'small',
}}
dataSource={listData}
renderItem={(item) => (
<Row key={item.index} gutter={[16]}>
{item.value.map((i) => (
<Col span={8} key={i.text}>
{this.getItemInfo(data, i)}
</Col>
))}
</Row>
)}
/>
);
renderVolumeTypes = () => {
const { isLoading } = this.state;
if (isLoading) {
return <Spin />;
}
return (
<List
itemLayout="vertical"
size="large"
pagination={{
hideOnSinglePage: true,
pageSize: 5,
size: 'small',
}}
dataSource={this.volumeTypesQuota.value}
renderItem={(item) => (
<Row key={item.index} gutter={[16]}>
{item.value.map((i) => (
<Col span={8} key={i.text}>
{this.getItemInfo(globalProjectStore.quota, i)}
</Col>
))}
</Row>
)}
/>
);
};
render() {
const { isLoading } = this.state;
const storage = [
{
text: t('volumes'),
key: 'volumes',
},
{
text: t('Gigabytes(GB)'),
key: 'gigabytes',
},
{
text: t('Snapshots'),
key: 'snapshots',
},
{
text: t('backups'),
key: 'backups',
},
{
text: t('backup gigabytes (GiB)'),
key: 'backup_gigabytes',
},
];
const volumeTypes = [];
globalVolumeTypeStore.list.data.forEach((item, index) => {
volumeTypes.push({
index,
value: [
{
text: t('{name} type', { name: item.name }),
key: `volumes_${item.name}`,
},
{
text: t('{name} type gigabytes(GB)', { name: item.name }),
key: `gigabytes_${item.name}`,
},
{
text: t('{name} type snapshots', { name: item.name }),
key: `snapshots_${item.name}`,
},
],
});
});
const quotaCardList = {
Compute: {
text: t('Compute'),
value: [
{ text: t('Instances'), key: 'instances' },
{ text: t('vCPUs'), key: 'cores' },
{ text: t('Memory'), key: 'ram' },
{ text: t('Server Group'), key: 'server_groups' },
// { text: t('Server Group Member'), key: 'server_group_members' },
// { text: t('keypair'), key: 'key_pairs' },
],
},
Storage: {
text: t('Storage'),
value: storage,
},
Network: {
text: t('Network'),
value: [
{ text: t('Router'), key: 'router' },
{ text: t('Network'), key: 'network' },
{ text: t('Subnet'), key: 'subnet' },
{ text: t('Floating IP'), key: 'floatingip' },
{ text: `${t('port')}`, key: 'port' },
{ text: t('Security Group'), key: 'security_group' },
{ text: t('Security Group Rule'), key: 'security_group_rule' },
// TODO wait for add.
// { text: `${t('VPN')}`, key: 'vpnservice' },
// { text: `${t('VPN Tunnel')}`, key: 'vpnservice' },
// { text: `${t('VPN Endpoint Group')}`, key: 'endpoint_group' },
// { text: `${t('Load Balancer')}`, key: 'loadbalancer' },
],
},
VolumeTypes: {
text: t('Storage Types'),
value: volumeTypes,
},
};
return (
<Card
className={styles.bottom}
@ -258,13 +264,9 @@ export class QuotaOverview extends Component {
))}
</div>
}
// extra={
// <div className={styles.action} onClick={this.handleApplyQuota}>
// {t('Apply for extended quota')}
// </div>
// }
extra={this.quotaAction}
>
{this.renderQuotaCardList(quotaCardList)}
{this.renderQuotaCardList()}
</Card>
);
}

View File

@ -1,234 +0,0 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Card, Col, Row } from 'antd';
import { Chart, Legend, Line, Tooltip } from 'bizcharts';
import { inject, observer } from 'mobx-react';
import styles from '../style.less';
@inject('rootStore')
@observer
class ResourceStatistic extends Component {
componentDidMount() {
const { user } = this.props.rootStore || {};
const { project: { id: project_id } = {} } = user || {};
this.props.store.getResourceStatisticData(project_id);
}
render() {
const { resourceStatisticLoading } = this.props.store;
const { chartArray } = this.props;
return (
<Card
loading={resourceStatisticLoading}
className={styles.top}
bodyStyle={{ padding: 0 }}
title={t('Resource Statistic (Last 15 Days)')}
bordered={false}
>
<Row>
{Object.keys(chartArray).map((item, index) => (
<Col span={12} key={item}>
{index % 6 === 1 ? (
<div style={{ paddingTop: '20px' }}>
<Chart
scale={{ temperature: { min: 0 } }}
padding={[50, 20, 40, 20]}
autoFit
height={198}
data={data2}
>
<Line position="month*temperature" color="city" />
<Tooltip shared showCrosshairs />
<Legend
itemName={{
style: {
fill: '#333',
},
}}
position="top-left"
layout="vertical"
/>
</Chart>
</div>
) : (
<div style={{ paddingTop: '20px', position: 'relative' }}>
<h2 style={{ position: 'absolute', left: '20px' }}>
{chartArray[item]}
</h2>
<Chart
scale={{ temperature: { min: 0 } }}
padding={[50, 20, 40, 50]}
autoFit
height={198}
data={data1}
>
<Line shape="smooth" position="year*value" />
<Tooltip showCrosshairs />
</Chart>
</div>
)}
</Col>
))}
</Row>
</Card>
);
}
}
//
const data1 = [
{ year: '1991', value: 3 },
{ year: '1992', value: 4 },
{ year: '1993', value: 3.5 },
{ year: '1994', value: 5 },
{ year: '1995', value: 4.9 },
{ year: '1996', value: 6 },
{ year: '1997', value: 7 },
{ year: '1998', value: 9 },
{ year: '1999', value: 13 },
];
const data2 = [
{
month: 'Jan',
city: 'Total Mem',
temperature: 7,
},
{
month: 'Jan',
city: 'Total CPU',
temperature: 3.9,
},
{
month: 'Feb',
city: 'Total Mem',
temperature: 6.9,
},
{
month: 'Feb',
city: 'Total CPU',
temperature: 4.2,
},
{
month: 'Mar',
city: 'Total Mem',
temperature: 9.5,
},
{
month: 'Mar',
city: 'Total CPU',
temperature: 5.7,
},
{
month: 'Apr',
city: 'Total Mem',
temperature: 14.5,
},
{
month: 'Apr',
city: 'Total CPU',
temperature: 8.5,
},
{
month: 'May',
city: 'Total Mem',
temperature: 18.4,
},
{
month: 'May',
city: 'Total CPU',
temperature: 11.9,
},
{
month: 'Jun',
city: 'Total Mem',
temperature: 21.5,
},
{
month: 'Jun',
city: 'Total CPU',
temperature: 15.2,
},
{
month: 'Jul',
city: 'Total Mem',
temperature: 25.2,
},
{
month: 'Jul',
city: 'Total CPU',
temperature: 17,
},
{
month: 'Aug',
city: 'Total Mem',
temperature: 26.5,
},
{
month: 'Aug',
city: 'Total CPU',
temperature: 16.6,
},
{
month: 'Sep',
city: 'Total Mem',
temperature: 23.3,
},
{
month: 'Sep',
city: 'Total CPU',
temperature: 14.2,
},
{
month: 'Oct',
city: 'Total Mem',
temperature: 18.3,
},
{
month: 'Oct',
city: 'Total CPU',
temperature: 10.3,
},
{
month: 'Nov',
city: 'Total Mem',
temperature: 13.9,
},
{
month: 'Nov',
city: 'Total CPU',
temperature: 6.6,
},
{
month: 'Dec',
city: 'Total Mem',
temperature: 9.6,
},
{
month: 'Dec',
city: 'Total CPU',
temperature: 4.8,
},
];
ResourceStatistic.propTypes = {
chartArray: PropTypes.object.isRequired,
store: PropTypes.object.isRequired,
};
export default ResourceStatistic;

View File

@ -13,7 +13,6 @@
// limitations under the License.
import React, { Component } from 'react';
// import { PropTypes } from 'prop-types';
import { observer } from 'mobx-react';
import { Row, Col } from 'antd';
import overviewInstance from 'asset/image/overview-instance.svg';
@ -27,7 +26,7 @@ import ProjectInfo from './components/ProjectInfo';
const actions = [
{
key: 'createInstance',
key: 'nstance',
label: t('Instance'),
avatar: overviewInstance,
to: '/compute/instance',
@ -39,32 +38,51 @@ const actions = [
to: '/storage/volume',
},
{
key: 'createNetwork',
key: 'network',
label: t('Network'),
avatar: overviewNetwork,
to: '/network/networks',
},
{
key: 'createRouter',
key: 'router',
label: t('Router'),
avatar: overviewRouter,
to: '/network/router',
},
];
@observer
class Overview extends Component {
export class Overview extends Component {
renderAction = (item) => (
<Row className={styles.actionButton} gutter={[8]}>
<Col span={12} className={styles.main_icon}>
<Col span={8} className={styles.main_icon}>
<img alt="avatar" src={item.avatar} className={styles.actionIcon} />
</Col>
<Col span={12} style={{ textAlign: 'center' }}>
<Col span={16} style={{ textAlign: 'center' }}>
{item.label}
</Col>
</Row>
);
renderActions() {
return actions.map((item) => (
<Col span={6} key={item.key}>
<Link to={item.to}>{this.renderAction(item)}</Link>
</Col>
));
}
renderQuota() {
return <QuotaOverview />;
}
renderProject() {
return <ProjectInfo />;
}
renderExtra() {
return null;
}
render() {
return (
<div className={styles.container}>
@ -73,19 +91,15 @@ class Overview extends Component {
gutter={[22, 22]}
style={{ marginBottom: '22px' }}
>
{actions.map((item) => (
<Col span={6} key={item.key}>
<Link to={item.to}>{this.renderAction(item)}</Link>
</Col>
))}
{this.renderActions()}
</Row>
<Row gutter={16}>
<Col span={16} className={styles.left}>
{/* <ResourceStatistic store={store} chartArray={chartArray} /> */}
<QuotaOverview />
{this.renderQuota()}
</Col>
<Col span={8} className={styles.right}>
<ProjectInfo />
{this.renderExtra()}
</Col>
</Row>
</div>
@ -93,4 +107,4 @@ class Overview extends Component {
}
}
export default Overview;
export default observer(Overview);

View File

@ -126,7 +126,8 @@
.right {
height: 100%;
.top {
.project {
position: relative;
.meta {
padding-bottom: 16px;
margin-bottom: 16px;
@ -140,55 +141,12 @@
}
.role_button {
float: right;
}
.money {
color: #0068ff;
line-height: 42px;
font-size: 30px;
position: absolute;
bottom: 0px;
right: 24px;
}
}
.middle {
margin-top: 1.33rem;
background-color: #ffffff;
.row {
text-align: center;
.alert_value {
font-size: 56px;
}
.alert_key {
color: #252525;
font-size: 16px;
}
span {
display: block;
}
}
}
.bottom {
margin-top: 1.33rem;
background-color: #ffffff;
.timeline {
.time {
font-size: 14px;
color: #0068ff;
margin-bottom: 6px;
}
.bulletin {
font-size: 14px;
color: #252525;
}
}
}
}
:global {