diff --git a/docs/en/develop/3-1-BaseList-introduction.md b/docs/en/develop/3-1-BaseList-introduction.md
index f70b92ef..d3379977 100644
--- a/docs/en/develop/3-1-BaseList-introduction.md
+++ b/docs/en/develop/3-1-BaseList-introduction.md
@@ -518,7 +518,7 @@ English | [Chinese](../../zh/develop/3-1-BaseList-introduction.md)
## Properties and functions that do not need to be overridden
-- `isInDetailPage`
+- `inDetailPage`
- Identifies whether the current page is a list page under the details page
- `location`
- Page routing information
diff --git a/docs/en/develop/3-3-BaseDetail-introduction.md b/docs/en/develop/3-3-BaseDetail-introduction.md
index e44ce158..cb640fb4 100644
--- a/docs/en/develop/3-3-BaseDetail-introduction.md
+++ b/docs/en/develop/3-3-BaseDetail-introduction.md
@@ -167,7 +167,7 @@ English | [Chinese](../../zh/develop/3-3-BaseDetail-introduction.md)
```javascript
updateFetchParamsByPage = (params) => {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
const { id, ...rest } = params;
return {
volume_id: id,
diff --git a/docs/zh/develop/3-1-BaseList-introduction.md b/docs/zh/develop/3-1-BaseList-introduction.md
index 64cb7e16..369cb3cc 100644
--- a/docs/zh/develop/3-1-BaseList-introduction.md
+++ b/docs/zh/develop/3-1-BaseList-introduction.md
@@ -518,7 +518,7 @@
## 不需要复写的属性与函数
-- `isInDetailPage`
+- `inDetailPage`
- 标识当前页面是否为详情页下的列表页
- `location`
- 页面的路由信息
diff --git a/docs/zh/develop/3-3-BaseDetail-introduction.md b/docs/zh/develop/3-3-BaseDetail-introduction.md
index 41ba6b8e..04f7b7ea 100644
--- a/docs/zh/develop/3-3-BaseDetail-introduction.md
+++ b/docs/zh/develop/3-3-BaseDetail-introduction.md
@@ -167,7 +167,7 @@
```javascript
updateFetchParamsByPage = (params) => {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
const { id, ...rest } = params;
return {
volume_id: id,
diff --git a/src/containers/List/index.jsx b/src/containers/List/index.jsx
index 14fb46b0..60bcdde6 100644
--- a/src/containers/List/index.jsx
+++ b/src/containers/List/index.jsx
@@ -91,7 +91,7 @@ export default class BaseList extends React.Component {
}
componentDidUpdate(prevProps) {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
const { detail: oldDetail } = prevProps;
const { detail: newDetail } = this.props;
if (
@@ -132,7 +132,7 @@ export default class BaseList extends React.Component {
return '';
}
- get isInDetailPage() {
+ get inDetailPage() {
const { detail } = this.props;
return !!detail;
}
@@ -798,7 +798,7 @@ export default class BaseList extends React.Component {
silent: !force,
};
this.handleFetch(params, true);
- if (this.isInDetailPage && force && this.shouldRefreshDetail) {
+ if (this.inDetailPage && force && this.shouldRefreshDetail) {
this.refreshDetailData();
}
};
diff --git a/src/pages/compute/containers/Instance/actions/CreateIronic/index.jsx b/src/pages/compute/containers/Instance/actions/CreateIronic/index.jsx
index fb805403..5a1e6e70 100644
--- a/src/pages/compute/containers/Instance/actions/CreateIronic/index.jsx
+++ b/src/pages/compute/containers/Instance/actions/CreateIronic/index.jsx
@@ -59,10 +59,9 @@ export class CreateIronic extends StepAction {
static allowed(_, containerProps) {
const { isAdminPage = false } = containerProps;
const { match } = containerProps || {};
- const isInServerGroupDetailPage =
- match.path.indexOf('/compute/server') >= 0;
+ const inServerGroupDetailPage = match.path.indexOf('/compute/server') >= 0;
return Promise.resolve(
- !isInServerGroupDetailPage && !isAdminPage && canCreateIronicByLicense()
+ !inServerGroupDetailPage && !isAdminPage && canCreateIronicByLicense()
);
}
diff --git a/src/pages/compute/containers/Instance/index.jsx b/src/pages/compute/containers/Instance/index.jsx
index 374e4e29..22ce3a92 100644
--- a/src/pages/compute/containers/Instance/index.jsx
+++ b/src/pages/compute/containers/Instance/index.jsx
@@ -31,9 +31,9 @@ import actionConfigs from './actions';
@observer
export default class Instance extends Base {
init() {
- if (!this.isInDetailPage) {
+ if (!this.inDetailPage) {
this.store = globalServerStore;
- } else if (this.isInServerGroupDetailPage) {
+ } else if (this.inServerGroupDetailPage) {
this.store = new ServerGroupInstanceStore();
} else {
this.store = new ServerStore();
@@ -56,8 +56,8 @@ export default class Instance extends Base {
return t('instances');
}
- get isInServerGroupDetailPage() {
- if (!this.isInDetailPage) {
+ get inServerGroupDetailPage() {
+ if (!this.inDetailPage) {
return false;
}
const {
@@ -66,8 +66,8 @@ export default class Instance extends Base {
return path.indexOf('server-group') >= 0;
}
- get isInHostDetailPage() {
- if (!this.isInDetailPage) {
+ get inHostDetailPage() {
+ if (!this.inDetailPage) {
return false;
}
const {
@@ -76,8 +76,8 @@ export default class Instance extends Base {
return path.indexOf('hypervisors') >= 0;
}
- get isInFlavorDetailPage() {
- if (!this.isInDetailPage) {
+ get inFlavorDetailPage() {
+ if (!this.inDetailPage) {
return false;
}
const {
@@ -87,7 +87,7 @@ export default class Instance extends Base {
}
get isFilterByBackend() {
- return !this.isInServerGroupDetailPage;
+ return !this.inServerGroupDetailPage;
}
get isSortByBackend() {
@@ -222,10 +222,10 @@ export default class Instance extends Base {
valueRender: 'sinceTime',
},
];
- if (this.isInFlavorDetailPage) {
+ if (this.inFlavorDetailPage) {
return columns.filter((it) => it.dataIndex !== 'flavor');
}
- if (this.isInHostDetailPage) {
+ if (this.inHostDetailPage) {
return columns.filter((it) => it.dataIndex !== 'host');
}
return columns;
@@ -239,7 +239,7 @@ export default class Instance extends Base {
batchActions,
};
}
- if (this.isInFlavorDetailPage) {
+ if (this.inFlavorDetailPage) {
return {
...actionConfigs.actionConfigs,
primaryActions: [],
@@ -266,7 +266,7 @@ export default class Instance extends Base {
},
]
: []),
- ...(this.isAdminPage && !this.isInHostDetailPage
+ ...(this.isAdminPage && !this.inHostDetailPage
? [
{
label: t('Host'),
@@ -281,11 +281,11 @@ export default class Instance extends Base {
updateFetchParamsByPage = (params) => {
const { id, ...rest } = params;
const newParams = { ...rest };
- if (this.isInHostDetailPage) {
+ if (this.inHostDetailPage) {
const { detail: { service: { host } = {} } = {} } = this.props;
newParams.host = host;
}
- if (this.isInFlavorDetailPage) {
+ if (this.inFlavorDetailPage) {
const { detail: { id: flavor } = {} } = this.props;
newParams.flavor_id = flavor;
}
@@ -297,7 +297,7 @@ export default class Instance extends Base {
const { members } = detail;
const { id, ...rest } = params;
const newParams = { ...rest };
- if (this.isInServerGroupDetailPage) {
+ if (this.inServerGroupDetailPage) {
newParams.members = members;
newParams.isServerGroup = true;
}
diff --git a/src/pages/identity/containers/User/index.jsx b/src/pages/identity/containers/User/index.jsx
index b6e43cf9..ab49e8da 100644
--- a/src/pages/identity/containers/User/index.jsx
+++ b/src/pages/identity/containers/User/index.jsx
@@ -14,16 +14,18 @@
import React from 'react';
import { observer, inject } from 'mobx-react';
-import { Badge } from 'antd';
+import { Badge, Table, Popover } from 'antd';
import Base from 'containers/List';
-import globalUserStore from 'stores/keystone/user';
+import globalUserStore, { UserStore } from 'stores/keystone/user';
import { yesNoOptions, emptyActionConfig } from 'utils/constants';
+import { Link } from 'react-router-dom';
+import { FileTextOutlined } from '@ant-design/icons';
import actionConfigs from './actions';
import actionConfigsInDomain from './actionsInDomain';
export class User extends Base {
init() {
- this.store = globalUserStore;
+ this.store = this.inDetailPage ? new UserStore() : globalUserStore;
this.getDomains();
}
@@ -43,11 +45,39 @@ export class User extends Base {
return t('users');
}
- getColumns = () => {
+ get inDomainDetail() {
const {
match: { path },
} = this.props;
- const components = [
+ return this.inDetailPage && path.includes('domain-admin/detail');
+ }
+
+ get inProjectDetail() {
+ const {
+ match: { path },
+ } = this.props;
+ return this.inDetailPage && path.includes('project-admin/detail');
+ }
+
+ get inUserGroupDetail() {
+ const {
+ match: { path },
+ } = this.props;
+ return this.inDetailPage && path.includes('identity/user-group');
+ }
+
+ get inRoleDetail() {
+ const {
+ match: { path },
+ } = this.props;
+ return this.inDetailPage && path.includes('identity/role-admin');
+ }
+
+ getColumns = () => {
+ // const {
+ // match: { path },
+ // } = this.props;
+ const columns = [
{
title: t('User ID/Name'),
dataIndex: 'name',
@@ -55,13 +85,18 @@ export class User extends Base {
},
{
title: t('Project Scope'),
- dataIndex: 'projectScope',
+ dataIndex: 'projects',
isHideable: true,
- render: (projectScope) => {
- if (projectScope && projectScope[0]) {
- return projectScope.map((it) =>
{it}
);
+ render: (value) => {
+ if (value && value.length) {
+ return value.map((it) => {
+ const { id, name } = it;
+ const url = `/identity/project-admin/detail/${id}`;
+ return {name};
+ });
}
},
+ stringify: (value) => value.map((it) => it.name).join('; '),
},
{
title: t('Roles'),
@@ -93,16 +128,76 @@ export class User extends Base {
return ;
},
},
+ {
+ title: t('Project Num'),
+ dataIndex: 'projectItems',
+ render: (_, record) => {
+ const { project_num } = record;
+ if (project_num === 0) {
+ return ;
+ }
+ const { projectItems = [] } = record;
+ const projectColumns = [
+ {
+ title: t('Project'),
+ dataIndex: 'name',
+ key: 'id',
+ render: (value, data) => {
+ const url = `/identity/project-admin/detail/${data.id}`;
+ return {value};
+ },
+ },
+ {
+ title: t('Role'),
+ dataIndex: 'roles',
+ key: 'roles',
+ render: (value) => {
+ if (!value) {
+ return '-';
+ }
+ return value.map((it) => it.name).join(', ');
+ },
+ },
+ ];
+ const table = (
+
+ );
+ return (
+ <>
+
+ node.parentNode}
+ placement="right"
+ content={table}
+ destroyTooltipOnHide
+ >
+
+
+ >
+ );
+ },
+ stringify: (value, record) => {
+ const { project_num, projectItems = [] } = record;
+ const projectRoleStr = projectItems
+ .map((it) => {
+ const { name, roles } = it;
+ const roleStr = roles.map((role) => role.name).join(', ');
+ return `${name}: ${roleStr}`;
+ })
+ .join('\n');
+ return `${project_num}\n${projectRoleStr}`;
+ },
+ },
{
title: t('Email'),
dataIndex: 'email',
isHideable: true,
- // render: (name) => {
- // if (name) {
- // return {name};
- // }
- // return '-';
- // },
},
{
title: t('phone'),
@@ -122,36 +217,56 @@ export class User extends Base {
stringify: (val) => (val ? t('Yes') : t('No')),
},
];
-
- if (!path.includes('role-admin/detail')) {
- components.splice(1, 1);
+ if (!this.inDetailPage) {
+ return columns.filter(
+ (it) =>
+ !['project_roles', 'projects', 'project_num'].includes(it.dataIndex)
+ );
}
- if (!path.includes('user-admin')) {
- components.splice(5, 1);
+ if (this.inUserGroupDetail) {
+ return columns.filter(
+ (it) =>
+ !['project_roles', 'projects', 'projectItems'].includes(it.dataIndex)
+ );
}
- if (!path.includes('project-admin')) {
- components.splice(2, 1);
+ if (this.inDomainDetail) {
+ return columns.filter(
+ (it) =>
+ ![
+ 'project_roles',
+ 'projects',
+ 'domain_name',
+ 'projectItems',
+ ].includes(it.dataIndex)
+ );
}
- return components;
+ if (this.inRoleDetail) {
+ return columns.filter(
+ (it) =>
+ !['project_roles', 'project_num', 'projectItems'].includes(
+ it.dataIndex
+ )
+ );
+ }
+ if (this.inProjectDetail) {
+ return columns.filter(
+ (it) => !['projects', 'projectItems'].includes(it.dataIndex)
+ );
+ }
+ return columns;
};
get actionConfigs() {
- const {
- match: { path },
- } = this.props;
- if (
- path.includes('identity/user') &&
- !path.includes('identity/user-group')
- ) {
- return this.isAdminPage
- ? actionConfigs.adminConfigs
- : actionConfigs.actionConfigs;
- }
- if (path.includes('domain-admin/detail')) {
+ if (this.inDomainDetail) {
return this.isAdminPage
? actionConfigsInDomain.adminConfigs
: actionConfigsInDomain.actionConfigs;
}
+ if (!this.inDetailPage) {
+ return this.isAdminPage
+ ? actionConfigs.adminConfigs
+ : actionConfigs.actionConfigs;
+ }
return emptyActionConfig;
}
@@ -175,22 +290,21 @@ export class User extends Base {
async getData({ silent, ...params } = {}) {
const { match } = this.props;
- const { path } = match;
const newParams = { ...params };
silent && (this.list.silent = true);
- if (path.includes('domain-admin/detail')) {
+ if (this.inDomainDetail) {
const { id } = match.params;
newParams.domainId = id;
await this.store.fetchListInDomainDetail(newParams);
- } else if (path.includes('project-admin/detail')) {
+ } else if (this.inProjectDetail) {
const { id } = match.params;
newParams.projectId = id;
await this.store.fetchListInProjectDetail(newParams);
- } else if (path.includes('user-group-admin/detail')) {
+ } else if (this.inUserGroupDetail) {
const { id } = match.params;
newParams.groupId = id;
await this.store.fetchListInGroupDetail(newParams);
- } else if (path.includes('role-admin/detail')) {
+ } else if (this.inRoleDetail) {
const { id } = match.params;
newParams.roleId = id;
await this.store.fetchListInRoleDetail(newParams);
diff --git a/src/pages/network/containers/FloatingIp/index.jsx b/src/pages/network/containers/FloatingIp/index.jsx
index 61918c8d..5cb4ef8b 100644
--- a/src/pages/network/containers/FloatingIp/index.jsx
+++ b/src/pages/network/containers/FloatingIp/index.jsx
@@ -45,7 +45,7 @@ export default class FloatingIps extends Base {
}
async getData({ silent, ...params } = {}) {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
silent && (this.list.silent = true);
const { detail: { addresses = [] } = {} } = this.props;
const ips = [];
@@ -96,7 +96,7 @@ export default class FloatingIps extends Base {
if (this.isRecycleBinDetail) {
return emptyActionConfig;
}
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
return this.isAdminPage
? actionConfigs.instanceDetailAdminConfigs
: actionConfigs.instanceDetailConfigs;
diff --git a/src/pages/network/containers/Router/index.jsx b/src/pages/network/containers/Router/index.jsx
index 1e4e2259..c682a2b7 100644
--- a/src/pages/network/containers/Router/index.jsx
+++ b/src/pages/network/containers/Router/index.jsx
@@ -15,12 +15,12 @@
import { observer, inject } from 'mobx-react';
import Base from 'containers/List';
import { getRouterColumns, routerFitlers } from 'resources/router';
-import { RouterStore } from 'stores/neutron/router';
+import globalRouterStore, { RouterStore } from 'stores/neutron/router';
import actionConfigs from './actions';
export class Routes extends Base {
init() {
- this.store = new RouterStore();
+ this.store = this.inDetailPage ? new RouterStore() : globalRouterStore;
this.downloadStore = new RouterStore();
}
diff --git a/src/pages/network/containers/VirtualAdapter/index.jsx b/src/pages/network/containers/VirtualAdapter/index.jsx
index 734ba16d..c7dabd60 100644
--- a/src/pages/network/containers/VirtualAdapter/index.jsx
+++ b/src/pages/network/containers/VirtualAdapter/index.jsx
@@ -27,7 +27,7 @@ import actionConfigs from './actions';
@observer
export default class VirtualAdapter extends Base {
init() {
- this.store = this.isInDetailPage
+ this.store = this.inDetailPage
? new VirtualAdapterStore()
: globalVirtualAdapterStore;
this.downloadStore = new VirtualAdapterStore();
@@ -108,7 +108,7 @@ export default class VirtualAdapter extends Base {
if (this.isAdminPage) {
return actionConfigs.adminActions;
}
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
if (this.isInstanceDetail) {
return actionConfigs.actionConfigsInDetail;
}
diff --git a/src/pages/storage/containers/Backup/index.jsx b/src/pages/storage/containers/Backup/index.jsx
index 0ac34976..00c0f740 100644
--- a/src/pages/storage/containers/Backup/index.jsx
+++ b/src/pages/storage/containers/Backup/index.jsx
@@ -35,7 +35,7 @@ export default class Backup extends Base {
if (this.isAdminPage) {
return actionConfigsAdmin;
}
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
return {
...actionConfigsProject,
primaryActions: [CreateBackup],
@@ -61,7 +61,7 @@ export default class Backup extends Base {
}
init() {
- this.store = globalBackupStore;
+ this.store = this.inDetailPage ? new BackupStore() : globalBackupStore;
this.downloadStore = new BackupStore();
}
@@ -108,7 +108,7 @@ export default class Backup extends Base {
valueRender: 'sinceTime',
},
];
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
return columns.filter((it) => it.dataIndex !== 'volume_name');
}
return columns;
@@ -124,7 +124,7 @@ export default class Backup extends Base {
}
updateFetchParamsByPage = (params) => {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
const { id, ...rest } = params;
return {
volume_id: id,
diff --git a/src/pages/storage/containers/Snapshot/index.jsx b/src/pages/storage/containers/Snapshot/index.jsx
index 943a78f1..1b5e260e 100644
--- a/src/pages/storage/containers/Snapshot/index.jsx
+++ b/src/pages/storage/containers/Snapshot/index.jsx
@@ -22,7 +22,7 @@ import actionConfigs from './actions';
@observer
export default class Snapshots extends Base {
init() {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
this.store = new SnapshotStore();
this.downloadStore = this.store;
} else {
@@ -68,7 +68,7 @@ export default class Snapshots extends Base {
updateFetchParamsByPage = (params) => {
const { tab, id, ...rest } = params;
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
return {
...rest,
volume_id: id,
diff --git a/src/pages/storage/containers/Volume/index.jsx b/src/pages/storage/containers/Volume/index.jsx
index e545c2a5..3401668c 100644
--- a/src/pages/storage/containers/Volume/index.jsx
+++ b/src/pages/storage/containers/Volume/index.jsx
@@ -34,7 +34,7 @@ import actionConfigs from './actions';
@observer
export default class Volume extends Base {
init() {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
this.store = new InstanceVolumeStore();
this.downloadStore = this.store;
} else {
@@ -61,11 +61,11 @@ export default class Volume extends Base {
return emptyActionConfig;
}
if (this.isAdminPage) {
- return this.isInDetailPage
+ return this.inDetailPage
? actionConfigs.instanceDetailAdminConfig
: actionConfigs.adminConfig;
}
- return this.isInDetailPage
+ return this.inDetailPage
? actionConfigs.instanceDetailConfig
: actionConfigs.actionConfigs;
}
@@ -75,7 +75,7 @@ export default class Volume extends Base {
}
get isFilterByBackend() {
- return !this.isInDetailPage;
+ return !this.inDetailPage;
}
get isSortByBackend() {
@@ -194,7 +194,7 @@ export default class Volume extends Base {
stringify: (value) => toLocalTimeFilter(value),
},
];
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
return columns.filter((it) => it.dataIndex !== 'attachments');
}
return columns;
@@ -205,7 +205,7 @@ export default class Volume extends Base {
}
updateFetchParams = (params) => {
- if (this.isInDetailPage) {
+ if (this.inDetailPage) {
const { match, detail } = this.props;
const { id } = match.params;
const { tenant_id: projectId, name } = detail || {};