feat: Add quota info when create volume
1. Add quota info display when create volume 2. Add volume type quota check when create volume 3. Update quota ring/line chart to support infinity value Change-Id: I0f300beb16bcf50008126afab9dac529c1749d09
This commit is contained in:
parent
d133705f6f
commit
f942c48352
@ -23,6 +23,8 @@ import { parse } from 'qs';
|
|||||||
import FormItem from 'components/FormItem';
|
import FormItem from 'components/FormItem';
|
||||||
import { CancelToken } from 'axios';
|
import { CancelToken } from 'axios';
|
||||||
import { getPath, getLinkRender } from 'utils/route-map';
|
import { getPath, getLinkRender } from 'utils/route-map';
|
||||||
|
import InfoButton from 'components/InfoButton';
|
||||||
|
import QuotaChart from 'components/QuotaChart';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
export default class BaseForm extends React.Component {
|
export default class BaseForm extends React.Component {
|
||||||
@ -253,6 +255,14 @@ export default class BaseForm extends React.Component {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showQuota() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get quotaInfo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
getSubmitData(data) {
|
getSubmitData(data) {
|
||||||
return { ...data };
|
return { ...data };
|
||||||
}
|
}
|
||||||
@ -624,6 +634,35 @@ export default class BaseForm extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderQuota() {
|
||||||
|
if (!this.showQuota) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let props = {};
|
||||||
|
if (!this.quotaInfo || !this.quotaInfo.length) {
|
||||||
|
props.loading = true;
|
||||||
|
} else {
|
||||||
|
props = {
|
||||||
|
loading: false,
|
||||||
|
quotas: this.quotaInfo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return <QuotaChart {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRightTopExtra() {
|
||||||
|
const content = this.renderQuota();
|
||||||
|
if (!content) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const checkValue = JSON.stringify(this.quotaInfo);
|
||||||
|
return (
|
||||||
|
<div className={styles['right-top-extra-wrapper']}>
|
||||||
|
<InfoButton content={content} checkValue={checkValue} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const wrapperPadding =
|
const wrapperPadding =
|
||||||
this.listUrl || this.isStep || (this.isModal && this.tips)
|
this.listUrl || this.isStep || (this.isModal && this.tips)
|
||||||
@ -646,6 +685,7 @@ export default class BaseForm extends React.Component {
|
|||||||
>
|
>
|
||||||
<Spin spinning={this.isSubmitting} tip={this.renderSubmittingTip()}>
|
<Spin spinning={this.isSubmitting} tip={this.renderSubmittingTip()}>
|
||||||
{tips}
|
{tips}
|
||||||
|
{this.renderRightTopExtra()}
|
||||||
<div className={classnames(styles.form, 'sl-form')} style={formStyle}>
|
<div className={classnames(styles.form, 'sl-form')} style={formStyle}>
|
||||||
{this.renderForms()}
|
{this.renderForms()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,3 +119,23 @@
|
|||||||
.progress-wrapper {
|
.progress-wrapper {
|
||||||
width: 170px;
|
width: 170px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-top-extra-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 30px;
|
||||||
|
z-index: 100;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 30px 0 rgba(0, 0, 0, 20%);
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.ant-card-head {
|
||||||
|
min-width: 32px;
|
||||||
|
|
||||||
|
.ant-card-extra {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -52,6 +52,7 @@ export default function QuotaInfo(props) {
|
|||||||
pagination={false}
|
pagination={false}
|
||||||
title={() => fullTitle}
|
title={() => fullTitle}
|
||||||
bordered
|
bordered
|
||||||
|
size="small"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -25,13 +25,15 @@ export default function Line(props) {
|
|||||||
title = '',
|
title = '',
|
||||||
secondTitle = t('Quota'),
|
secondTitle = t('Quota'),
|
||||||
} = props;
|
} = props;
|
||||||
let left = limit - used - reserved - add;
|
const isLimit = limit !== -1;
|
||||||
|
const limitStr = !isLimit ? t('Infinity') : limit;
|
||||||
|
let left = isLimit ? limit - used - reserved - add : 1;
|
||||||
left = left < 0 ? 0 : left;
|
left = left < 0 ? 0 : left;
|
||||||
const usedTip = `${t('Used')}: ${used}`;
|
const usedTip = `${t('Used')}: ${used}`;
|
||||||
const reservedTip = reserved ? '' : `${t('Reserved')}: ${reserved}`;
|
const reservedTip = reserved ? '' : `${t('Reserved')}: ${reserved}`;
|
||||||
const newTip = `${t('New')}: ${add}`;
|
const newTip = `${t('New')}: ${add}`;
|
||||||
const leftTip = `${t('Left')}: ${left}`;
|
const leftTip = `${t('Left')}: ${left}`;
|
||||||
const tips = [usedTip, newTip, leftTip];
|
const tips = isLimit ? [usedTip, newTip, leftTip] : [usedTip, newTip];
|
||||||
if (reserved) {
|
if (reserved) {
|
||||||
tips.splice(1, 0, reservedTip);
|
tips.splice(1, 0, reservedTip);
|
||||||
}
|
}
|
||||||
@ -43,17 +45,19 @@ export default function Line(props) {
|
|||||||
const resourceTitle = (
|
const resourceTitle = (
|
||||||
<span>
|
<span>
|
||||||
{`${title} ${secondTitle}: `}{' '}
|
{`${title} ${secondTitle}: `}{' '}
|
||||||
<span style={{ color: usedColor }}>{`${allCount}/${limit}`}</span>
|
<span style={{ color: usedColor }}>{`${allCount}/${limitStr}`}</span>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
const progress = (
|
const progress = isLimit ? (
|
||||||
<Progress
|
<Progress
|
||||||
percent={allPercent}
|
percent={allPercent}
|
||||||
success={{ percent: usedPercent, strokeColor: typeColors.used }}
|
success={{ percent: usedPercent, strokeColor: typeColors.used }}
|
||||||
strokeColor={typeColors.add}
|
strokeColor={typeColors.add}
|
||||||
showInfo={false}
|
showInfo={false}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<Progress percent={0} showInfo={false} />
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
Legend,
|
Legend,
|
||||||
View,
|
View,
|
||||||
Annotation,
|
Annotation,
|
||||||
|
Tooltip,
|
||||||
} from 'bizcharts';
|
} from 'bizcharts';
|
||||||
|
|
||||||
export const typeColors = {
|
export const typeColors = {
|
||||||
@ -54,24 +55,28 @@ export default function Ring(props) {
|
|||||||
secondTitle = t('Quota'),
|
secondTitle = t('Quota'),
|
||||||
hasLabel = false,
|
hasLabel = false,
|
||||||
} = props;
|
} = props;
|
||||||
const left = limit - used - reserved - add;
|
const isLimit = limit !== -1;
|
||||||
|
const showTip = isLimit;
|
||||||
|
const limitNumber = !isLimit ? Infinity : limit;
|
||||||
|
const limitStr = !isLimit ? t('Infinity') : limit;
|
||||||
|
const left = !isLimit ? 1 : limit - used - reserved - add;
|
||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
type: t('Used'),
|
type: t('Used'),
|
||||||
value: used,
|
value: isLimit ? used : 0,
|
||||||
color: typeColors.used,
|
color: typeColors.used,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (reserved) {
|
if (reserved) {
|
||||||
data.push({
|
data.push({
|
||||||
type: t('Reserved'),
|
type: t('Reserved'),
|
||||||
value: reserved,
|
value: isLimit ? reserved : 0,
|
||||||
color: typeColors.reserved,
|
color: typeColors.reserved,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
data.push({
|
data.push({
|
||||||
type: t('New'),
|
type: t('New'),
|
||||||
value: add,
|
value: isLimit ? add : 0,
|
||||||
color: typeColors.add,
|
color: typeColors.add,
|
||||||
});
|
});
|
||||||
data.push({
|
data.push({
|
||||||
@ -79,18 +84,20 @@ export default function Ring(props) {
|
|||||||
value: left,
|
value: left,
|
||||||
color: typeColors.left,
|
color: typeColors.left,
|
||||||
});
|
});
|
||||||
|
|
||||||
const colors = data.map((it) => it.color);
|
const colors = data.map((it) => it.color);
|
||||||
|
|
||||||
const width = hasLabel ? 200 : 120;
|
const width = hasLabel ? 200 : 120;
|
||||||
const style = { width };
|
const style = { width };
|
||||||
const height = width;
|
const height = width;
|
||||||
const allCount = used + add + reserved;
|
const allCount = used + add + reserved;
|
||||||
const percent = (allCount / limit) * 100;
|
const percent = isLimit ? (allCount / limitNumber) * 100 : 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style}>
|
<div style={style}>
|
||||||
<Chart placeholder={false} height={height} padding="auto" autoFit>
|
<Chart placeholder={false} height={height} padding="auto" autoFit>
|
||||||
<Legend visible={hasLabel} />
|
<Legend visible={showTip && hasLabel} />
|
||||||
|
<Tooltip visible={showTip} />
|
||||||
{/* 绘制图形 */}
|
{/* 绘制图形 */}
|
||||||
<View data={data}>
|
<View data={data}>
|
||||||
<Coordinate type="theta" innerRadius={0.75} />
|
<Coordinate type="theta" innerRadius={0.75} />
|
||||||
@ -122,7 +129,7 @@ export default function Ring(props) {
|
|||||||
/>
|
/>
|
||||||
<Annotation.Text
|
<Annotation.Text
|
||||||
position={['50%', '70%']}
|
position={['50%', '70%']}
|
||||||
content={`${allCount}/${limit}`}
|
content={`${allCount}/${limitStr}`}
|
||||||
style={{
|
style={{
|
||||||
lineHeight: '240px',
|
lineHeight: '240px',
|
||||||
fontSize: '14',
|
fontSize: '14',
|
||||||
|
@ -19,8 +19,8 @@ import Line from './Line';
|
|||||||
import QuotaInfo from './Info';
|
import QuotaInfo from './Info';
|
||||||
|
|
||||||
function renderItem(props) {
|
function renderItem(props) {
|
||||||
const { type = 'ring', limit } = props;
|
const { type = 'ring', limit, unlimitByTable = false } = props;
|
||||||
if (limit === -1) {
|
if (limit === -1 && unlimitByTable) {
|
||||||
return <QuotaInfo {...props} />;
|
return <QuotaInfo {...props} />;
|
||||||
}
|
}
|
||||||
if (type === 'ring') {
|
if (type === 'ring') {
|
||||||
|
@ -1805,6 +1805,7 @@
|
|||||||
"Quota is not enough for extend share.": "Quota is not enough for extend share.",
|
"Quota is not enough for extend share.": "Quota is not enough for extend share.",
|
||||||
"Quota is not enough for extend volume.": "Quota is not enough for extend volume.",
|
"Quota is not enough for extend volume.": "Quota is not enough for extend volume.",
|
||||||
"Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).",
|
"Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).",
|
||||||
|
"Quota: Insufficient { name } quota to create resources, please adjust resource quantity or quota(left { left }, input { input }).": "Quota: Insufficient { name } quota to create resources, please adjust resource quantity or quota(left { left }, input { input }).",
|
||||||
"Quota: Project quotas sufficient resources can be created": "Quota: Project quotas sufficient resources can be created",
|
"Quota: Project quotas sufficient resources can be created": "Quota: Project quotas sufficient resources can be created",
|
||||||
"RAM": "RAM",
|
"RAM": "RAM",
|
||||||
"RAM(MiB)": "RAM(MiB)",
|
"RAM(MiB)": "RAM(MiB)",
|
||||||
@ -2790,6 +2791,7 @@
|
|||||||
"{interval, plural, =1 {one week} other {# weeks} } later delete": "{interval, plural, =1 {one week} other {# weeks} } later delete",
|
"{interval, plural, =1 {one week} other {# weeks} } later delete": "{interval, plural, =1 {one week} other {# weeks} } later delete",
|
||||||
"{minutes} minutes {leftSeconds} seconds": "{minutes} minutes {leftSeconds} seconds",
|
"{minutes} minutes {leftSeconds} seconds": "{minutes} minutes {leftSeconds} seconds",
|
||||||
"{name} type": "{name} type",
|
"{name} type": "{name} type",
|
||||||
|
"{name} type gigabytes": "{name} type gigabytes",
|
||||||
"{name} type gigabytes(GiB)": "{name} type gigabytes(GiB)",
|
"{name} type gigabytes(GiB)": "{name} type gigabytes(GiB)",
|
||||||
"{name} type snapshots": "{name} type snapshots",
|
"{name} type snapshots": "{name} type snapshots",
|
||||||
"{name} {id} could not be found.": "{name} {id} could not be found.",
|
"{name} {id} could not be found.": "{name} {id} could not be found.",
|
||||||
|
@ -1805,6 +1805,7 @@
|
|||||||
"Quota is not enough for extend share.": "配额不足以扩容共享。",
|
"Quota is not enough for extend share.": "配额不足以扩容共享。",
|
||||||
"Quota is not enough for extend volume.": "配额不足以扩容云硬盘。",
|
"Quota is not enough for extend volume.": "配额不足以扩容云硬盘。",
|
||||||
"Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "配额:项目配额不足,无法创建资源,请进行资源数量或配额的调整(剩余{ quota },输入{ input })。",
|
"Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).": "配额:项目配额不足,无法创建资源,请进行资源数量或配额的调整(剩余{ quota },输入{ input })。",
|
||||||
|
"Quota: Insufficient { name } quota to create resources, please adjust resource quantity or quota(left { left }, input { input }).": "配额:{ name } 配额不足,无法创建资源,请进行资源数量或配额的调整(剩余{ left },输入{ input })。",
|
||||||
"Quota: Project quotas sufficient resources can be created": "配额:项目配额充足,可创建资源",
|
"Quota: Project quotas sufficient resources can be created": "配额:项目配额充足,可创建资源",
|
||||||
"RAM": "内存",
|
"RAM": "内存",
|
||||||
"RAM(MiB)": "内存(MiB)",
|
"RAM(MiB)": "内存(MiB)",
|
||||||
@ -2790,6 +2791,7 @@
|
|||||||
"{interval, plural, =1 {one week} other {# weeks} } later delete": "{interval}周后删除",
|
"{interval, plural, =1 {one week} other {# weeks} } later delete": "{interval}周后删除",
|
||||||
"{minutes} minutes {leftSeconds} seconds": "{minutes}分{leftSeconds}秒",
|
"{minutes} minutes {leftSeconds} seconds": "{minutes}分{leftSeconds}秒",
|
||||||
"{name} type": "{name} 类型",
|
"{name} type": "{name} 类型",
|
||||||
|
"{name} type gigabytes": "{name} 类型容量",
|
||||||
"{name} type gigabytes(GiB)": "{name} 类型容量(GiB)",
|
"{name} type gigabytes(GiB)": "{name} 类型容量(GiB)",
|
||||||
"{name} type snapshots": "{name} 类型快照",
|
"{name} type snapshots": "{name} 类型快照",
|
||||||
"{name} {id} could not be found.": "您查看的资源{name} {id} 无法获取",
|
"{name} {id} could not be found.": "您查看的资源{name} {id} 无法获取",
|
||||||
|
@ -26,11 +26,11 @@ import globalImageStore from 'stores/glance/image';
|
|||||||
import globalVolumeStore from 'stores/cinder/volume';
|
import globalVolumeStore from 'stores/cinder/volume';
|
||||||
import globalVolumeTypeStore from 'stores/cinder/volume-type';
|
import globalVolumeTypeStore from 'stores/cinder/volume-type';
|
||||||
import globalBackupStore from 'stores/cinder/backup';
|
import globalBackupStore from 'stores/cinder/backup';
|
||||||
import { InputNumber, Badge } from 'antd';
|
import { InputNumber, Badge, message as $message } from 'antd';
|
||||||
import { toJS } from 'mobx';
|
import { toJS } from 'mobx';
|
||||||
import { FormAction } from 'containers/Action';
|
import { FormAction } from 'containers/Action';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { isFinite } from 'lodash';
|
import { isEmpty, isObject } from 'lodash';
|
||||||
import {
|
import {
|
||||||
getImageSystemTabs,
|
getImageSystemTabs,
|
||||||
getImageOS,
|
getImageOS,
|
||||||
@ -94,8 +94,7 @@ export class Create extends FormAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get errorText() {
|
get errorText() {
|
||||||
const { status } = this.state;
|
if (this.msg) {
|
||||||
if (status === 'error') {
|
|
||||||
return t(
|
return t(
|
||||||
'Unable to create volume: insufficient quota to create resources.'
|
'Unable to create volume: insufficient quota to create resources.'
|
||||||
);
|
);
|
||||||
@ -103,6 +102,78 @@ export class Create extends FormAction {
|
|||||||
return super.errorText;
|
return super.errorText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showQuota() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVolumeQuota() {
|
||||||
|
const quotaAll = toJS(this.volumeStore.quotaSet) || {};
|
||||||
|
if (isEmpty(quotaAll)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
Object.values(quotaAll).forEach((it) => {
|
||||||
|
if (isObject(it)) {
|
||||||
|
it.used = it.in_use;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const { volume_type } = this.state;
|
||||||
|
const { name } = volume_type || {};
|
||||||
|
const result = {
|
||||||
|
volumes: quotaAll.volumes,
|
||||||
|
gigabytes: quotaAll.gigabytes,
|
||||||
|
};
|
||||||
|
if (name) {
|
||||||
|
result[`volumes_${name}`] = quotaAll[`volumes_${name}`];
|
||||||
|
result[`gigabytes_${name}`] = quotaAll[`gigabytes_${name}`];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
get quotaInfo() {
|
||||||
|
const quota = this.getVolumeQuota();
|
||||||
|
const { volumes = {}, gigabytes = {} } = quota;
|
||||||
|
const { limit } = volumes || {};
|
||||||
|
if (!limit) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { volume_type, size = 0, count = 1 } = this.state;
|
||||||
|
const { name } = volume_type || {};
|
||||||
|
const volume = {
|
||||||
|
...volumes,
|
||||||
|
add: count,
|
||||||
|
name: 'volume',
|
||||||
|
title: t('Volume'),
|
||||||
|
};
|
||||||
|
const sizeInfo = {
|
||||||
|
...gigabytes,
|
||||||
|
add: count * size,
|
||||||
|
name: 'gigabytes',
|
||||||
|
title: t('volume gigabytes'),
|
||||||
|
type: 'line',
|
||||||
|
};
|
||||||
|
if (!name) {
|
||||||
|
return [volume, sizeInfo];
|
||||||
|
}
|
||||||
|
const typeQuota = quota[`volumes_${name}`] || {};
|
||||||
|
const typeSizeQuota = quota[`gigabytes_${name}`] || {};
|
||||||
|
const detailInfo = {
|
||||||
|
...typeQuota,
|
||||||
|
add: count,
|
||||||
|
name: `volumes_${name}`,
|
||||||
|
title: t('{name} type', { name }),
|
||||||
|
type: 'line',
|
||||||
|
};
|
||||||
|
const detailSizeInfo = {
|
||||||
|
...typeSizeQuota,
|
||||||
|
add: count * size,
|
||||||
|
name: `gigabytes_${name}`,
|
||||||
|
title: t('{name} type gigabytes', { name }),
|
||||||
|
type: 'line',
|
||||||
|
};
|
||||||
|
return [volume, sizeInfo, detailInfo, detailSizeInfo];
|
||||||
|
}
|
||||||
|
|
||||||
get defaultValue() {
|
get defaultValue() {
|
||||||
const size = this.quotaIsLimit && this.maxSize < 10 ? this.maxSize : 10;
|
const size = this.quotaIsLimit && this.maxSize < 10 ? this.maxSize : 10;
|
||||||
const { initVolumeType } = this.state;
|
const { initVolumeType } = this.state;
|
||||||
@ -167,23 +238,19 @@ export class Create extends FormAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get quota() {
|
get quota() {
|
||||||
const { volumes: { limit = 10, in_use = 0 } = {} } =
|
const { volumes = {} } = this.getVolumeQuota();
|
||||||
toJS(this.volumeStore.quotaSet) || {};
|
return volumes;
|
||||||
if (limit === -1) {
|
|
||||||
return Infinity;
|
|
||||||
}
|
|
||||||
return limit - in_use;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get quotaIsLimit() {
|
get quotaIsLimit() {
|
||||||
const { gigabytes: { limit } = {} } = toJS(this.volumeStore.quotaSet) || {};
|
const { gigabytes: { limit } = {} } = this.getVolumeQuota();
|
||||||
return limit !== -1;
|
return limit !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maxSize() {
|
get maxSize() {
|
||||||
const { gigabytes: { limit = 10, in_use = 0 } = {} } =
|
const { gigabytes: { limit = 10, in_use = 0, reserved = 0 } = {} } =
|
||||||
toJS(this.volumeStore.quotaSet) || {};
|
this.getVolumeQuota();
|
||||||
return limit - in_use;
|
return limit !== -1 ? limit - in_use - reserved : 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvailZones() {
|
getAvailZones() {
|
||||||
@ -204,6 +271,7 @@ export class Create extends FormAction {
|
|||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
initVolumeType,
|
initVolumeType,
|
||||||
|
volume_type: types[0],
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.updateFormValue('volume_type', initVolumeType);
|
this.updateFormValue('volume_type', initVolumeType);
|
||||||
@ -325,7 +393,7 @@ export class Create extends FormAction {
|
|||||||
};
|
};
|
||||||
|
|
||||||
get nameForStateUpdate() {
|
get nameForStateUpdate() {
|
||||||
return ['source', 'image', 'snapshot'];
|
return ['source', 'image', 'snapshot', 'size', 'volume_type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
get formItems() {
|
get formItems() {
|
||||||
@ -481,20 +549,9 @@ export class Create extends FormAction {
|
|||||||
onCountChangeCallback() {}
|
onCountChangeCallback() {}
|
||||||
|
|
||||||
onCountChange = (value) => {
|
onCountChange = (value) => {
|
||||||
let msg = t('Quota: Project quotas sufficient resources can be created');
|
|
||||||
let status = 'success';
|
|
||||||
if (isFinite(this.quota) && value > this.quota) {
|
|
||||||
msg = t(
|
|
||||||
'Quota: Insufficient quota to create resources, please adjust resource quantity or quota(left { quota }, input { input }).',
|
|
||||||
{ quota: this.quota, input: value }
|
|
||||||
);
|
|
||||||
status = 'error';
|
|
||||||
}
|
|
||||||
this.msg = msg;
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
count: value,
|
count: value,
|
||||||
status,
|
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if (this.onCountChangeCallback) {
|
if (this.onCountChangeCallback) {
|
||||||
@ -504,23 +561,77 @@ export class Create extends FormAction {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
checkQuotaDetail = (quota) => {
|
||||||
|
if (!quota || isEmpty(quota)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const { limit, add, reserved = 0 } = quota || {};
|
||||||
|
if (limit === -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return limit >= add + reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
getQuotaErrorMsg = (quota) => {
|
||||||
|
const { limit, used, reserved, add, title } = quota;
|
||||||
|
const left = limit - used - reserved;
|
||||||
|
return t(
|
||||||
|
'Quota: Insufficient { name } quota to create resources, please adjust resource quantity or quota(left { left }, input { input }).',
|
||||||
|
{
|
||||||
|
name: title,
|
||||||
|
left,
|
||||||
|
input: add,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkQuotaAll = () => {
|
||||||
|
const results = this.quotaInfo;
|
||||||
|
if (!results.length) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const [quota = {}, sizeQuota = {}, typeQuota = {}, typeSizeQuota = {}] =
|
||||||
|
results;
|
||||||
|
let msg = '';
|
||||||
|
const quotas = [quota, sizeQuota, typeQuota, typeSizeQuota];
|
||||||
|
const errorQuota = quotas.find((it) => !this.checkQuotaDetail(it));
|
||||||
|
if (errorQuota) {
|
||||||
|
msg = this.getQuotaErrorMsg(errorQuota);
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
};
|
||||||
|
|
||||||
renderBadge() {
|
renderBadge() {
|
||||||
const { status } = this.state;
|
const msg = this.checkQuotaAll();
|
||||||
if (status === 'success') {
|
if (!msg) {
|
||||||
|
this.msg = '';
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return <Badge status={status} text={this.msg} />;
|
if (msg && this.msg !== msg) {
|
||||||
|
$message.error(msg);
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
return <Badge status="error" text={msg} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderExtra() {
|
renderExtra() {
|
||||||
return this.renderBadge();
|
return this.renderBadge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCountMax = () => {
|
||||||
|
const { limit, used, reserved } = this.quota;
|
||||||
|
if (!limit || limit === -1) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
return limit - used - reserved;
|
||||||
|
};
|
||||||
|
|
||||||
renderFooterLeft() {
|
renderFooterLeft() {
|
||||||
const { count = 1 } = this.state;
|
const { count = 1 } = this.state;
|
||||||
|
const max = this.getCountMax();
|
||||||
const configs = {
|
const configs = {
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 100,
|
max,
|
||||||
precision: 0,
|
precision: 0,
|
||||||
onChange: this.onCountChange,
|
onChange: this.onCountChange,
|
||||||
formatter: (value) => `$ ${value}`.replace(/\D/g, ''),
|
formatter: (value) => `$ ${value}`.replace(/\D/g, ''),
|
||||||
@ -539,9 +650,9 @@ export class Create extends FormAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSubmit = (data) => {
|
onSubmit = (data) => {
|
||||||
const { count, status } = this.state;
|
const { count } = this.state;
|
||||||
if (status === 'error') {
|
if (this.msg) {
|
||||||
return Promise.reject();
|
return Promise.reject(this.msg);
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
backup,
|
backup,
|
||||||
|
Loading…
Reference in New Issue
Block a user