fix: Update link generate for security group rule

1. Update link generate for security group rule
2. Update export

Change-Id: I12ea81aab8ff697e1b0764ae9592d0de4ee00bc4
This commit is contained in:
Jingwei.Zhang 2021-09-06 16:18:06 +08:00
parent ca4f999c7f
commit f21c5a6daa
26 changed files with 202 additions and 216 deletions

View File

@ -21,6 +21,10 @@ export default class RuleButton extends Component {
return this.isAdminPage ? `${path}${adminStr || '-admin'}` : path; return this.isAdminPage ? `${path}${adminStr || '-admin'}` : path;
} }
getDetailUrl(id) {
return `${this.getUrl('/network/security-group')}/detail/${id}`;
}
render() { render() {
const { item: { security_group_rules: datas = [] } = {} } = this.props; const { item: { security_group_rules: datas = [] } = {} } = this.props;
const configs = { const configs = {

View File

@ -21,9 +21,7 @@ import { emptyActionConfig } from 'utils/constants';
import actionConfigs from '../actions'; import actionConfigs from '../actions';
import BaseDetail from './BaseDetail'; import BaseDetail from './BaseDetail';
@inject('rootStore') export class Detail extends Base {
@observer
export default class Detail extends Base {
get name() { get name() {
return t('flavor'); return t('flavor');
} }
@ -96,3 +94,5 @@ export default class Detail extends Base {
this.store = new FlavorStore(); this.store = new FlavorStore();
} }
} }
export default inject('rootStore')(observer(Detail));

View File

@ -68,6 +68,10 @@ export class SecurityGroup extends React.Component {
return this.isAdminPage ? `${path}${adminStr || '-admin'}` : path; return this.isAdminPage ? `${path}${adminStr || '-admin'}` : path;
} }
getDetailUrl(id) {
return `${this.getUrl('/network/security-group')}/detail/${id}`;
}
actionCallback = async (first) => { actionCallback = async (first) => {
const { const {
match: { match: {

View File

@ -19,7 +19,7 @@ import globalProjectStore from 'stores/keystone/project';
import { regex } from 'utils/validate'; import { regex } from 'utils/validate';
import { statusTypes } from 'utils/constants'; import { statusTypes } from 'utils/constants';
class CreateForm extends ModalAction { export class CreateForm extends ModalAction {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {

View File

@ -17,7 +17,7 @@ import { ModalAction } from 'containers/Action';
import globalProjectStore from 'stores/keystone/project'; import globalProjectStore from 'stores/keystone/project';
import { statusTypes } from 'utils/constants'; import { statusTypes } from 'utils/constants';
class EditForm extends ModalAction { export class EditForm extends ModalAction {
init() { init() {
this.store = globalProjectStore; this.store = globalProjectStore;
this.store.fetchList(); this.store.fetchList();

View File

@ -18,13 +18,10 @@ import { Divider, Badge, Tag, Tooltip } from 'antd';
import Base from 'containers/List'; import Base from 'containers/List';
import globalProjectStore from 'stores/keystone/project'; import globalProjectStore from 'stores/keystone/project';
import { yesNoOptions, projectTagsColors } from 'utils/constants'; import { yesNoOptions, projectTagsColors } from 'utils/constants';
import actionConfigs from './actions'; import actionConfigs from './actions';
import styles from './index.less'; import styles from './index.less';
@inject('rootStore') export class Projects extends Base {
@observer
export default class Projects extends Base {
init() { init() {
this.store = globalProjectStore; this.store = globalProjectStore;
} }
@ -222,3 +219,5 @@ export default class Projects extends Base {
this.list.silent = false; this.list.silent = false;
} }
} }
export default inject('rootStore')(observer(Projects));

View File

@ -28,7 +28,7 @@ import {
} from 'utils/validate'; } from 'utils/validate';
import { statusTypes } from 'utils/constants'; import { statusTypes } from 'utils/constants';
class CreateForm extends FormAction { export class CreateForm extends FormAction {
constructor(props) { constructor(props) {
super(props); super(props);

View File

@ -18,7 +18,7 @@ import globalUserStore from 'stores/keystone/user';
import globalDomainStore from 'stores/keystone/domain'; import globalDomainStore from 'stores/keystone/domain';
import { phoneNumberValidate, emailValidate } from 'utils/validate'; import { phoneNumberValidate, emailValidate } from 'utils/validate';
class EditForm extends ModalAction { export class EditForm extends ModalAction {
init() { init() {
this.store = globalUserStore; this.store = globalUserStore;
this.domainStore = globalDomainStore; this.domainStore = globalDomainStore;

View File

@ -21,9 +21,7 @@ import actionConfigs from '../actions';
import BaseDetail from './BaseDetail'; import BaseDetail from './BaseDetail';
import PortForwarding from './PortForwarding'; import PortForwarding from './PortForwarding';
@inject('rootStore') export class FloatingIpDetail extends Base {
@observer
export default class FloatingIpDetail extends Base {
get name() { get name() {
return t('floating ip'); return t('floating ip');
} }
@ -92,3 +90,5 @@ export default class FloatingIpDetail extends Base {
this.store = new FloatingIpStore(); this.store = new FloatingIpStore();
} }
} }
export default inject('rootStore')(observer(FloatingIpDetail));

View File

@ -31,6 +31,10 @@ export class Rule extends Base {
return t('security group rules'); return t('security group rules');
} }
getDetailUrl(id) {
return `${this.getUrl('/network/security-group')}/detail/${id}`;
}
getColumns = () => getSelfColumns(this); getColumns = () => getSelfColumns(this);
get actionConfigs() { get actionConfigs() {

View File

@ -30,9 +30,7 @@ import styles from './index.less';
const { Panel } = Collapse; const { Panel } = Collapse;
@inject('rootStore') export class SecurityGroup extends React.Component {
@observer
export default class SecurityGroup extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.store = new VirtualAdapterStore(); this.store = new VirtualAdapterStore();
@ -46,6 +44,10 @@ export default class SecurityGroup extends React.Component {
return this.isAdminPage ? `${path}${adminStr || '-admin'}` : path; return this.isAdminPage ? `${path}${adminStr || '-admin'}` : path;
} }
getDetailUrl(id) {
return `${this.getUrl('/network/security-group')}/detail/${id}`;
}
get portId() { get portId() {
const { const {
detail: { id }, detail: { id },
@ -66,6 +68,7 @@ export default class SecurityGroup extends React.Component {
const { const {
security_groups: { data }, security_groups: { data },
} = this.store; } = this.store;
const detailUrl = this.getDetailUrl(item);
return ( return (
<Row> <Row>
<Col span={18}> <Col span={18}>
@ -76,10 +79,7 @@ export default class SecurityGroup extends React.Component {
<Col span={6}> <Col span={6}>
{!this.isAdminPage && ( {!this.isAdminPage && (
<> <>
<Link <Link style={{ fontSize: 12, marginRight: 16 }} to={detailUrl}>
style={{ fontSize: 12, marginRight: 16 }}
to={`/network/security-group/detail/${item.id}`}
>
{t('Edit Rule')} {t('Edit Rule')}
</Link> </Link>
</> </>
@ -160,3 +160,5 @@ export default class SecurityGroup extends React.Component {
); );
} }
} }
export default inject('rootStore')(observer(SecurityGroup));

View File

@ -22,9 +22,7 @@ import AllowedAddressPair from './AllowedAddressPair';
import BaseDetail from './BaseDetail'; import BaseDetail from './BaseDetail';
import actionConfigs from '../actions'; import actionConfigs from '../actions';
@inject('rootStore') export class VirtualAdapterDetail extends Base {
@observer
export default class VirtualAdapterDetail extends Base {
get name() { get name() {
return t('virtual adapter'); return t('virtual adapter');
} }
@ -123,3 +121,5 @@ export default class VirtualAdapterDetail extends Base {
this.store = new VirtualAdapterStore(); this.store = new VirtualAdapterStore();
} }
} }
export default inject('rootStore')(observer(VirtualAdapterDetail));

View File

@ -17,9 +17,7 @@ import { Link } from 'react-router-dom';
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import Base from 'containers/BaseDetail'; import Base from 'containers/BaseDetail';
@inject('rootStore') export class BaseDetail extends Base {
@observer
export default class BaseDetail extends Base {
get leftCards() { get leftCards() {
return [this.volumeCard]; return [this.volumeCard];
} }
@ -50,3 +48,5 @@ export default class BaseDetail extends Base {
}; };
} }
} }
export default inject('rootStore')(observer(BaseDetail));

View File

@ -19,9 +19,7 @@ import { volumeStatus } from 'resources/volume';
import BaseDetail from './BaseDetail'; import BaseDetail from './BaseDetail';
import actionConfigs from '../actions'; import actionConfigs from '../actions';
@inject('rootStore') export class Detail extends Base {
@observer
export default class Detail extends Base {
get name() { get name() {
return t('snapshot'); return t('snapshot');
} }
@ -84,3 +82,5 @@ export default class Detail extends Base {
this.store = new SnapshotStore(); this.store = new SnapshotStore();
} }
} }
export default inject('rootStore')(observer(Detail));

View File

@ -16,9 +16,7 @@ import { inject, observer } from 'mobx-react';
import { ModalAction } from 'containers/Action'; import { ModalAction } from 'containers/Action';
import globalVolumeStore from 'stores/cinder/volume'; import globalVolumeStore from 'stores/cinder/volume';
@inject('rootStore') export class CreateVolume extends ModalAction {
@observer
export default class CreateVolume extends ModalAction {
static id = 'create'; static id = 'create';
static title = t('Create Volume'); static title = t('Create Volume');
@ -36,11 +34,15 @@ export default class CreateVolume extends ModalAction {
static allowed = () => Promise.resolve(true); static allowed = () => Promise.resolve(true);
get volumeTypeParams() {
return {};
}
async getVolumeTypes() { async getVolumeTypes() {
const { volume_id: id } = this.item; const { volume_id: id } = this.item;
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const [_, volume] = await Promise.all([ const [_, volume] = await Promise.all([
this.volumeStore.fetchVolumeTypes(), this.volumeStore.fetchVolumeTypes(this.volumeTypeParams),
this.volumeStore.fetchDetail({ id }), this.volumeStore.fetchDetail({ id }),
]); ]);
const { volume_type: volumeType } = volume; const { volume_type: volumeType } = volume;
@ -122,3 +124,5 @@ export default class CreateVolume extends ModalAction {
return globalVolumeStore.create(body); return globalVolumeStore.create(body);
}; };
} }
export default inject('rootStore')(observer(CreateVolume));

View File

@ -18,9 +18,7 @@ import { volumeStatus, snapshotTransitionStatuses } from 'resources/volume';
import globalSnapshotStore, { SnapshotStore } from 'stores/cinder/snapshot'; import globalSnapshotStore, { SnapshotStore } from 'stores/cinder/snapshot';
import actionConfigs from './actions'; import actionConfigs from './actions';
@inject('rootStore') export class Snapshots extends Base {
@observer
export default class Snapshots extends Base {
init() { init() {
if (this.inDetailPage) { if (this.inDetailPage) {
this.store = new SnapshotStore(); this.store = new SnapshotStore();
@ -150,3 +148,5 @@ export default class Snapshots extends Base {
]; ];
} }
} }
export default inject('rootStore')(observer(Snapshots));

View File

@ -476,6 +476,8 @@ export class Create extends FormAction {
]; ];
} }
onCountChangeCallback() {}
onCountChange = (value) => { onCountChange = (value) => {
let msg = t('Quota: Project quotas sufficient resources can be created'); let msg = t('Quota: Project quotas sufficient resources can be created');
let status = 'success'; let status = 'success';
@ -487,10 +489,17 @@ export class Create extends FormAction {
status = 'error'; status = 'error';
} }
this.msg = msg; this.msg = msg;
this.setState({ this.setState(
{
count: value, count: value,
status, status,
}); },
() => {
if (this.onCountChangeCallback) {
this.onCountChangeCallback();
}
}
);
}; };
renderBadge() { renderBadge() {
@ -501,6 +510,10 @@ export class Create extends FormAction {
return <Badge status={status} text={this.msg} />; return <Badge status={status} text={this.msg} />;
} }
renderExtra() {
return this.renderBadge();
}
renderFooterLeft() { renderFooterLeft() {
const { count = 1 } = this.state; const { count = 1 } = this.state;
const configs = { const configs = {
@ -518,7 +531,7 @@ export class Create extends FormAction {
value={count} value={count}
className={classnames(styles.input, 'volume-count')} className={classnames(styles.input, 'volume-count')}
/> />
{this.renderBadge()} {this.renderExtra()}
</div> </div>
); );
} }

View File

@ -74,10 +74,11 @@ export class CreateBackup extends ModalAction {
onSubmit = (values) => { onSubmit = (values) => {
const { id } = this.item; const { id } = this.item;
const { volume, ...rest } = values; const { name, incremental } = values;
const force = isInUse(this.item); const force = isInUse(this.item);
const body = { const body = {
...rest, name,
incremental,
volume_id: id, volume_id: id,
force, force,
}; };

View File

@ -18,9 +18,7 @@ import { ModalAction } from 'containers/Action';
import globalServerStore from 'stores/nova/instance'; import globalServerStore from 'stores/nova/instance';
import { isInUse, isOsDisk } from 'resources/volume'; import { isInUse, isOsDisk } from 'resources/volume';
@inject('rootStore') export class Detach extends ModalAction {
@observer
export default class Detach extends ModalAction {
static id = 'detach'; static id = 'detach';
static title = t('Detach'); static title = t('Detach');
@ -92,10 +90,6 @@ export default class Detach extends ModalAction {
label: t('Name'), label: t('Name'),
name: 'name', name: 'name',
}, },
// {
// label: t('IP'),
// name: 'private_ip',
// },
], ],
columns: [ columns: [
{ {
@ -106,36 +100,6 @@ export default class Detach extends ModalAction {
title: t('Attached To'), title: t('Attached To'),
dataIndex: 'device', dataIndex: 'device',
}, },
// {
// title: t('Image'),
// dataIndex: ['image', 'os_distro'],
// render: value => <ImageType type={value} />,
// },
// {
// title: t('Fixed IP'),
// dataIndex: 'private_ip',
// render: private_ip => (private_ip || []).map(it => <span key={it.ip}>{it.ip}<br /></span>),
// },
// {
// title: t('Floating IP'),
// dataIndex: 'addresses',
// render: (addresses) => {
// if (!addresses || !addresses['pub-net']) {
// return '-';
// }
// return addresses['pub-net'].map(it => <span key={it.addr}>{it.addr}<br /></span>);
// },
// },
// {
// title: t('Flavor'),
// dataIndex: 'flavor',
// render: flavor => `${flavor.disk}G/${Number.parseInt(flavor.ram / 1024, 10)}G`,
// },
// {
// title: t('Created At'),
// dataIndex: 'created',
// valueRender: 'sinceTime',
// },
], ],
}, },
]; ];
@ -151,3 +115,5 @@ export default class Detach extends ModalAction {
); );
}; };
} }
export default inject('rootStore')(observer(Detach));

View File

@ -78,7 +78,7 @@ export class ExtendVolume extends ModalAction {
} }
onSubmit = async (values) => { onSubmit = async (values) => {
const { volume, ...rest } = values; const { new_size } = values;
const { id } = this.item; const { id } = this.item;
const instanceId = get(this.item, 'attachments[0].server_id'); const instanceId = get(this.item, 'attachments[0].server_id');
@ -99,7 +99,7 @@ export class ExtendVolume extends ModalAction {
return; return;
} }
} }
return this.store.extendSize(id, rest); return this.store.extendSize(id, { new_size });
}; };
} }

View File

@ -12,27 +12,19 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import React from 'react';
import { Link } from 'react-router-dom';
import { observer, inject } from 'mobx-react'; import { observer, inject } from 'mobx-react';
import Base from 'containers/List'; import Base from 'containers/List';
import { import {
volumeStatus,
diskTag,
volumeTransitionStatuses, volumeTransitionStatuses,
bootableType,
volumeFilters, volumeFilters,
multiTip, getVolumnColumnsList,
} from 'resources/volume'; } from 'resources/volume';
import globalVolumeStore, { VolumeStore } from 'stores/cinder/volume'; import globalVolumeStore, { VolumeStore } from 'stores/cinder/volume';
import { InstanceVolumeStore } from 'stores/nova/instance-volume'; import { InstanceVolumeStore } from 'stores/nova/instance-volume';
import { toLocalTimeFilter } from 'utils/index';
import { emptyActionConfig } from 'utils/constants'; import { emptyActionConfig } from 'utils/constants';
import actionConfigs from './actions'; import actionConfigs from './actions';
@inject('rootStore') export class Volume extends Base {
@observer
export default class Volume extends Base {
init() { init() {
if (this.inDetailPage) { if (this.inDetailPage) {
this.store = new InstanceVolumeStore(); this.store = new InstanceVolumeStore();
@ -91,113 +83,7 @@ export default class Volume extends Base {
} }
getColumns = () => { getColumns = () => {
const columns = [ return getVolumnColumnsList(this);
{
title: t('ID/Name'),
dataIndex: 'name',
linkPrefix: `/storage/${this.getUrl('volume')}/detail`,
stringify: (name, record) => name || record.id,
sortKey: 'name',
},
{
title: t('Project ID/Name'),
dataIndex: 'project_name',
hidden: !this.isAdminPage,
isHideable: true,
sorter: false,
},
{
title: t('Host'),
dataIndex: 'host',
isHideable: true,
hidden: !this.isAdminPage,
sorter: false,
},
{
title: t('Size'),
dataIndex: 'size',
isHideable: true,
render: (value) => `${value}GB`,
},
{
title: t('Status'),
dataIndex: 'status',
render: (value) => volumeStatus[value] || '-',
},
{
title: t('Type'),
dataIndex: 'volume_type',
isHideable: true,
width: 100,
sorter: false,
},
{
title: t('Disk Tag'),
dataIndex: 'disk_tag',
isHideable: true,
render: (value) => diskTag[value] || '-',
sorter: false,
},
{
title: t('Attached To'),
dataIndex: 'attachments',
isHideable: true,
sorter: false,
render: (value) => {
if (value && value.length > 0) {
return value.map((it) => (
<div key={it.server_id}>
{it.device} on{' '}
<Link
to={`${this.getUrl('/compute/instance')}/detail/${
it.server_id
}?tab=volumes`}
>
{it.server_name}
</Link>
</div>
));
}
return '-';
},
stringify: (value) => {
if (value && value.length) {
return value
.map((it) => `${it.server_name}(${it.server_id})`)
.join(',');
}
return '-';
},
},
{
title: t('Bootable'),
titleTip: t(
'When the volume is "bootable" and the status is "available", it can be used as a startup source to create an instance.'
),
dataIndex: 'bootable',
isHideable: true,
render: (value) => bootableType[value] || '-',
},
{
title: t('Shared'),
dataIndex: 'multiattach',
valueRender: 'yesNo',
titleTip: multiTip,
width: 80,
sorter: false,
},
{
title: t('Created At'),
dataIndex: 'created_at',
isHideable: true,
valueRender: 'sinceTime',
stringify: (value) => toLocalTimeFilter(value),
},
];
if (this.inDetailPage) {
return columns.filter((it) => it.dataIndex !== 'attachments');
}
return columns;
}; };
get searchFilters() { get searchFilters() {
@ -219,3 +105,5 @@ export default class Volume extends Base {
return params; return params;
}; };
} }
export default inject('rootStore')(observer(Volume));

View File

@ -88,19 +88,8 @@ export const getSelfColumns = (self) => [
dataIndex: 'remote_group_id', dataIndex: 'remote_group_id',
isHideable: true, isHideable: true,
render: (value) => { render: (value) => {
return ( const url = self.getDetailUrl(value);
<div> return <div>{value ? <Link to={url}>{value}</Link> : '-'}</div>;
{value ? (
<Link
to={`/network/${self.getUrl('security-group')}/detail/${value}`}
>
{value}
</Link>
) : (
'-'
)}
</div>
);
}, },
}, },
{ {

View File

@ -14,6 +14,8 @@
import React from 'react'; import React from 'react';
import { yesNoOptions } from 'utils/constants'; import { yesNoOptions } from 'utils/constants';
import { toLocalTimeFilter } from 'utils/index';
import { Link } from 'react-router-dom';
export const volumeStatus = { export const volumeStatus = {
available: t('Available'), available: t('Available'),
@ -221,3 +223,113 @@ export const snapshotTypeTip = (
</p> </p>
</> </>
); );
export const getVolumnColumnsList = (self) => {
const columns = [
{
title: t('ID/Name'),
dataIndex: 'name',
linkPrefix: `/storage/${self.getUrl('volume')}/detail`,
stringify: (name, record) => name || record.id,
sortKey: 'name',
},
{
title: t('Project ID/Name'),
dataIndex: 'project_name',
hidden: !self.isAdminPage,
isHideable: true,
sorter: false,
},
{
title: t('Host'),
dataIndex: 'host',
isHideable: true,
hidden: !self.isAdminPage,
sorter: false,
},
{
title: t('Size'),
dataIndex: 'size',
isHideable: true,
render: (value) => `${value}GB`,
},
{
title: t('Status'),
dataIndex: 'status',
render: (value) => volumeStatus[value] || '-',
},
{
title: t('Type'),
dataIndex: 'volume_type',
isHideable: true,
width: 100,
sorter: false,
},
{
title: t('Disk Tag'),
dataIndex: 'disk_tag',
isHideable: true,
render: (value) => diskTag[value] || '-',
sorter: false,
},
{
title: t('Attached To'),
dataIndex: 'attachments',
isHideable: true,
sorter: false,
render: (value) => {
if (value && value.length > 0) {
return value.map((it) => (
<div key={it.server_id}>
{it.device} on{' '}
<Link
to={`${self.getUrl('/compute/instance')}/detail/${
it.server_id
}?tab=volumes`}
>
{it.server_name}
</Link>
</div>
));
}
return '-';
},
stringify: (value) => {
if (value && value.length) {
return value
.map((it) => `${it.server_name}(${it.server_id})`)
.join(',');
}
return '-';
},
},
{
title: t('Bootable'),
titleTip: t(
'When the volume is "bootable" and the status is "available", it can be used as a startup source to create an instance.'
),
dataIndex: 'bootable',
isHideable: true,
render: (value) => bootableType[value] || '-',
},
{
title: t('Shared'),
dataIndex: 'multiattach',
valueRender: 'yesNo',
titleTip: multiTip,
width: 80,
sorter: false,
},
{
title: t('Created At'),
dataIndex: 'created_at',
isHideable: true,
valueRender: 'sinceTime',
stringify: (value) => toLocalTimeFilter(value),
},
];
if (self.inDetailPage) {
return columns.filter((it) => it.dataIndex !== 'attachments');
}
return columns;
};