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 || {};