refactor: Refactor project detail quota codes

1. Refactor project quota in project detail page
2. Add keypair quota display in overview/project detail quota page

Change-Id: I40e5925909d767a87eadd09cfb9c11ea1624cead
This commit is contained in:
Jingwei.Zhang 2021-09-22 15:06:54 +08:00
parent e182d62b34
commit ae266d3d74
7 changed files with 114 additions and 405 deletions

View File

@ -885,6 +885,7 @@
"Kernel ID": "Kernel ID",
"Kernel Image": "Kernel Image",
"Key": "Key",
"Key Pair": "Key Pair",
"Key Pairs": "Key Pairs",
"Key Size (bits)": "Key Size (bits)",
"Keypair": "Keypair",
@ -1791,7 +1792,6 @@
"compute hosts": "compute hosts",
"compute services": "compute services",
"connect subnet": "connect subnet",
"core": "core",
"create DSCP marking rule": "create DSCP marking rule",
"create a new network/subnet": "create a new network/subnet",
"create a new security group": "create a new security group",
@ -1925,7 +1925,6 @@
"qoS policy": "qoS policy",
"qos specs": "qos specs",
"quota set to -1 means there is no quota limit on the current resource": "quota set to -1 means there is no quota limit on the current resource",
"ram": "ram",
"reboot instance": "reboot instance",
"rebuild instance": "rebuild instance",
"recover instance": "recover instance",

View File

@ -885,6 +885,7 @@
"Kernel ID": "内核ID",
"Kernel Image": "Kernel镜像",
"Key": "键",
"Key Pair": "密钥",
"Key Pairs": "密钥",
"Key Size (bits)": "密钥大小(比特)",
"Keypair": "SSH密钥对",
@ -1791,7 +1792,6 @@
"compute hosts": "计算节点",
"compute services": "计算服务",
"connect subnet": "连接子网",
"core": "虚拟CPU",
"create DSCP marking rule": "创建DSCP标记规则",
"create a new network/subnet": "新建网络/子网",
"create a new security group": "新建安全组",
@ -1925,7 +1925,6 @@
"qoS policy": "QoS策略",
"qos specs": "QoS规格",
"quota set to -1 means there is no quota limit on the current resource": "配额为设为 -1 时表示当前资源无配额限制",
"ram": "内存(GB)",
"reboot instance": "重启云主机",
"rebuild instance": "重建云主机",
"recover instance": "恢复云主机",

View File

@ -16,7 +16,6 @@ import React, { Component } from 'react';
import { Badge, Card, Col, List, Progress, Row, Spin, Tooltip } from 'antd';
import { inject, observer } from 'mobx-react';
import globalVolumeTypeStore from 'stores/cinder/volume-type';
import globalKeypairStore from 'stores/nova/keypair';
import globalProjectStore from 'stores/keystone/project';
import { isNumber } from 'lodash';
import styles from '../style.less';
@ -36,6 +35,7 @@ export const quotaCardList = [
{ text: t('vCPUs'), key: 'cores' },
{ text: t('Memory'), key: 'ram' },
{ text: t('Server Group'), key: 'server_groups' },
{ text: t('Key Pair'), key: 'key_pairs' },
],
},
{
@ -85,6 +85,9 @@ export class QuotaOverview extends Component {
this.state = {
isLoading: true,
};
const { projectStore, volumeTypeStore } = props;
this.projectStore = projectStore || globalProjectStore;
this.volumeTypeStore = volumeTypeStore || globalVolumeTypeStore;
}
componentDidMount() {
@ -92,20 +95,29 @@ export class QuotaOverview extends Component {
}
async getData() {
const { user } = this.props.rootStore;
const { project: { id: projectId = '' } = {} } = user;
await Promise.all([
globalProjectStore.fetchProjectQuota({ project_id: projectId }),
globalVolumeTypeStore.fetchList(),
globalKeypairStore.fetchList(),
]);
const { getData } = this.props;
if (getData) {
await getData();
} else {
const { user } = this.props.rootStore;
const { project: { id: projectId = '' } = {} } = user;
await Promise.all([
this.projectStore.fetchProjectQuota({ project_id: projectId }),
this.volumeTypeStore.fetchList(),
]);
}
this.setState({
isLoading: false,
});
}
get volumeTypeData() {
const { volumeTypeData } = this.props;
return volumeTypeData || this.volumeTypeStore.list.data;
}
get volumeTypesQuota() {
const volumeTypes = globalVolumeTypeStore.list.data.map((item, index) => {
const volumeTypes = this.volumeTypeData.map((item, index) => {
return {
index,
value: [
@ -211,7 +223,7 @@ export class QuotaOverview extends Component {
return <Spin />;
}
return this.renderQuotaCart(
globalProjectStore.quota,
this.projectStore.quota,
this.getFilteredValue(item.value)
);
}
@ -238,7 +250,7 @@ export class QuotaOverview extends Component {
<Row key={item.index} gutter={[16]}>
{item.value.map((i) => (
<Col span={8} key={i.text}>
{this.getItemInfo(globalProjectStore.quota, i)}
{this.getItemInfo(this.projectStore.quota, i)}
</Col>
))}
</Row>

View File

@ -55,72 +55,6 @@
}
}
}
.bottom {
//margin: 16px 0;
:global {
.ant-card {
box-shadow: unset;
}
}
.title {
.text {
font-size: 16px;
color: #252525;
}
.badge {
margin-left: 22px;
:global {
.ant-badge-status-dot {
width: 10px;
height: 10px;
}
.ant-badge-status-text {
font-size: 14px;
}
}
}
}
.action {
font-size: 12px;
color: #0068ff;
float: right;
margin-top: 6px;
cursor: pointer;
}
.content {
.card {
padding: 8px;
:global {
.ant-card-head {
border-bottom: none;
.ant-card-head-title {
font-size: 16px;
font-weight: 500;
color: #565656;
padding-bottom: 0;
}
}
}
.progress_title {
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
.right {
@ -176,3 +110,69 @@
display: none;
}
}
.bottom {
//margin: 16px 0;
:global {
.ant-card {
box-shadow: unset;
}
}
.title {
.text {
font-size: 16px;
color: #252525;
}
.badge {
margin-left: 22px;
:global {
.ant-badge-status-dot {
width: 10px;
height: 10px;
}
.ant-badge-status-text {
font-size: 14px;
}
}
}
}
.action {
font-size: 12px;
color: #0068ff;
float: right;
margin-top: 6px;
cursor: pointer;
}
.content {
.card {
padding: 8px;
:global {
.ant-card-head {
border-bottom: none;
.ant-card-head-title {
font-size: 16px;
font-weight: 500;
color: #565656;
padding-bottom: 0;
}
}
}
.progress_title {
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}

View File

@ -12,180 +12,43 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { observer, inject } from 'mobx-react';
import globalProjectStore from 'stores/keystone/project';
import { Card, Col, Row, List } from 'antd';
import Progress from 'components/ProjectProgress';
import classnames from 'classnames';
import React, { Component } from 'react';
import { inject, observer } from 'mobx-react';
import QuotaOverview from 'pages/base/containers/Overview/components/QuotaOverview';
import { VolumeTypeStore } from 'stores/cinder/volume-type';
import { ProjectStore } from 'stores/keystone/project';
import styles from './index.less';
export class Quota extends React.Component {
export class Quota extends Component {
constructor(props) {
super(props);
this.state = {};
this.init();
this.projectStore = new ProjectStore();
this.volumeTypeStore = new VolumeTypeStore();
}
componentDidMount() {
this.getData();
get volumeTypeData() {
return this.volumeTypeStore.projectVolumeTypes;
}
getData = async () => {
const { id: project_id } = this.props.match.params;
await this.store.fetchProjectQuota({
project_id,
});
await this.volumeTypeStore.fetchProjectVolumeTypes(project_id);
return Promise.all([
this.projectStore.fetchProjectQuota({
project_id,
}),
this.volumeTypeStore.fetchProjectVolumeTypes(project_id),
]);
};
getItemInfo = (i) => {
const { quota } = this.store;
const resource = quota[i.key];
return <Progress resource={resource} name={i.text} nameSpan={8} />;
};
init() {
this.store = globalProjectStore;
this.volumeTypeStore = new VolumeTypeStore();
}
renderVolumeTypes = (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(i)}
</Col>
))}
</Row>
)}
/>
);
render() {
const { quota } = this.store;
const volumeTypes = [];
this.volumeTypeStore.projectVolumeTypes.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}`,
},
],
});
});
return (
<div className={classnames(styles.wrapper, this.className)}>
<Card>
{quota.instances ? (
<div>
<Row>
<Col xs={{ span: 8, offset: 1 }} lg={{ span: 8 }}>
<div style={{ fontSize: 16, marginBottom: 12 }}>
{t('Compute')}
</div>
<Progress resource={quota.instances} name={t('instance')} />
<Progress resource={quota.cores} name={t('core')} />
<Progress resource={quota.ram} name={t('ram')} />
{/* <Progress resource={quota.key_pairs} name={t('keypair')} /> */}
<div>
<p style={{ float: 'right' }}>
{t('Member in group')} :{' '}
{quota.server_group_members &&
quota.server_group_members.limit === -1
? t('Unlimit')
: quota.server_group_members.limit}{' '}
</p>
<Progress
resource={quota.server_groups}
name={t('Server Group')}
/>
</div>
{/* <Progress resource={quota.server_group_members} name={t('Server Group Member')} /> */}
</Col>
</Row>
</div>
) : null}
{quota.volumes ? (
<div>
<Row style={{ marginTop: 30 }}>
<Col xs={{ span: 8, offset: 1 }} lg={{ span: 8 }}>
<div style={{ fontSize: 16, marginBottom: 12 }}>
{t('Storage')}
</div>
<Progress name={t('Volume')} resource={quota.volumes} />
<Progress
name={`${t('gigabytes')}(GB)`}
resource={quota.gigabytes}
/>
<Progress name={t('backups')} resource={quota.backups} />
<Progress
name={t('Backup Capacity')}
resource={quota.backup_gigabytes}
/>
<Progress name={t('Snapshot')} resource={quota.snapshots} />
</Col>
<Col xs={{ span: 8, offset: 1 }} lg={{ span: 8, offset: 6 }}>
<div style={{ fontSize: 16, marginBottom: 12 }}>
{t('Network')}
</div>
<Progress resource={quota.router} name={t('router')} />
<Progress resource={quota.network} name={t('network')} />
<Progress resource={quota.subnet} name={t('subnet')} />
<Progress
resource={quota.floatingip}
name={t('floatingip')}
/>
<Progress resource={quota.port} name={t('port')} />
<Progress
name={t('security_group')}
resource={quota.security_group}
/>
<Progress
name={t('Security Group Rule')}
resource={quota.security_group_rule}
/>
</Col>
</Row>
</div>
) : null}
{volumeTypes[0] ? (
<div>
<Row style={{ marginTop: 30 }}>
<Col xs={{ span: 22, offset: 1 }} lg={{ span: 22 }}>
<div style={{ fontSize: 16, marginBottom: 12 }}>
{t('Storage Types')}
</div>
{this.renderVolumeTypes(volumeTypes)}
</Col>
</Row>
</div>
) : null}
</Card>
<div className={styles.wrapper}>
<QuotaOverview
getData={this.getData}
projectStore={this.projectStore}
volumeTypeStore={this.volumeTypeStore}
volumeTypeData={this.volumeTypeData}
/>
</div>
);
}

View File

@ -2,5 +2,5 @@
.wrapper {
min-height: calc(100vh - 108px);
padding: 0 @body-padding;
padding: 0 @body-padding @body-padding @body-padding;
}

View File

@ -1,164 +0,0 @@
@import '~antd/lib/style/themes/default.less';
.container {
height: 100%;
padding: 16px 12px;
overflow: auto;
:global {
.ant-card {
box-shadow: 0px 2px 20px 0px rgba(0, 0, 0, 0.09);
}
}
.left {
height: 100%;
.top {
.title {
display: table-cell;
vertical-align: bottom;
.text {
font-size: 21px;
color: #252525;
}
.action {
font-size: 16px;
color: #000000;
margin-left: 40px;
}
}
}
.bottom {
margin: 16px 0;
:global {
.ant-card {
box-shadow: unset;
}
}
.title {
.text {
font-size: 16px;
color: #252525;
}
.badge {
margin-left: 22px;
:global {
.ant-badge-status-dot {
width: 10px;
height: 10px;
}
.ant-badge-status-text {
font-size: 14px;
}
}
}
}
.action {
font-size: 12px;
color: #0068FF;
float: right;
margin-top: 6px;
cursor: pointer;
}
.content {
.card {
padding: 8px;
:global {
.ant-card-head {
font-size: 14px;
}
}
.progress_title {
font-size: 14px;
}
}
}
}
}
.right {
height: 100%;
.top {
.meta {
padding-bottom: 16px;
margin-bottom: 16px;
border-bottom: 1px dashed #e8e8e8;
}
:global {
.ant-descriptions-item-label {
width: 130px;
}
}
.money {
color: #0068FF;
line-height: 42px;
font-size: 30px;
}
}
.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 {
.ant-card-head {
font-size: 16px;
}
}
}