Merge remote-tracking branch 'origin/master'

pull/50/head
shao1121353141 2023-09-16 23:32:38 +08:00
commit 03369ef09a
21 changed files with 136 additions and 65 deletions

View File

@ -47,7 +47,7 @@
"@ant-design/charts": "^1.4.2", "@ant-design/charts": "^1.4.2",
"@ant-design/icons": "^5.2.6", "@ant-design/icons": "^5.2.6",
"@ant-design/maps": "^1.0.7", "@ant-design/maps": "^1.0.7",
"@ant-design/pro-components": "^2.6.18", "@ant-design/pro-components": "^2.6.19",
"ahooks": "^3.7.8", "ahooks": "^3.7.8",
"antd": "^5.9.1", "antd": "^5.9.1",
"antd-img-crop": "^4.12.2", "antd-img-crop": "^4.12.2",

View File

@ -113,7 +113,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, loading }) => {
heightLayoutHeader: showBanner ? 78 : 56, heightLayoutHeader: showBanner ? 78 : 56,
}, },
pageContainer: { pageContainer: {
paddingBlockPageContainerContent: 12, paddingBlockPageContainerContent: 6,
paddingInlinePageContainerContent: 24, paddingInlinePageContainerContent: 24,
}, },
}, },

View File

@ -19,14 +19,17 @@ import { history } from '@@/core/history';
import { PageContainer } from '@ant-design/pro-components'; import { PageContainer } from '@ant-design/pro-components';
import { useMount } from 'ahooks'; import { useMount } from 'ahooks';
import { App } from 'antd'; import { App, Button } from 'antd';
import { useState } from 'react'; import React, { useState } from 'react';
import LoginAudit from './components/LoginAudit'; import LoginAudit from './components/LoginAudit';
import UserInfo from './components/UserInfo'; import UserInfo from './components/UserInfo';
import { UserDetailTabs } from './constant'; import { UserDetailTabs } from './constant';
import queryString from 'query-string'; import queryString from 'query-string';
import { useLocation } from '@umijs/max'; import { useLocation } from '@umijs/max';
import { useIntl } from '@@/exports'; import { useIntl } from '@@/exports';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { removeUser } from '@/services/account';
import AppAccess from './components/AppAccess';
export default () => { export default () => {
const location = useLocation(); const location = useLocation();
@ -38,6 +41,7 @@ export default () => {
type: UserDetailTabs; type: UserDetailTabs;
}; };
const [tabActiveKey, setTabActiveKey] = useState<string>(); const [tabActiveKey, setTabActiveKey] = useState<string>();
useMount(() => { useMount(() => {
if (!id) { if (!id) {
useApp.message useApp.message
@ -65,13 +69,49 @@ export default () => {
tabList={[ tabList={[
{ {
key: UserDetailTabs.user_info, key: UserDetailTabs.user_info,
tab: '用户信息', tab: intl.formatMessage({ id: 'pages.account.user_detail.tabs.user_info' }),
},
{
key: UserDetailTabs.app_access,
tab: intl.formatMessage({ id: 'pages.account.user_detail.tabs.app_access' }),
}, },
{ {
key: UserDetailTabs.login_audit, key: UserDetailTabs.login_audit,
tab: '登录日志', tab: intl.formatMessage({ id: 'pages.account.user_detail.tabs.login_audit' }),
}, },
]} ]}
extra={[
<Button
key="delete"
type="primary"
danger
onClick={() => {
const confirmed = useApp.modal.error({
centered: true,
title: intl.formatMessage({
id: 'pages.account.user_detail.extra.delete.confirm_title',
}),
icon: <ExclamationCircleFilled />,
content: intl.formatMessage({
id: 'pages.account.user_detail.extra.delete.confirm_content',
}),
okText: intl.formatMessage({ id: 'app.confirm' }),
okType: 'danger',
okCancel: true,
cancelText: intl.formatMessage({ id: 'app.cancel' }),
onOk: async () => {
const { success } = await removeUser(id);
if (success) {
useApp.message.success(intl.formatMessage({ id: 'app.operation_success' }));
confirmed.destroy();
}
},
});
}}
>
{intl.formatMessage({ id: 'pages.account.user_detail.extra.delete' })}
</Button>,
]}
tabActiveKey={tabActiveKey} tabActiveKey={tabActiveKey}
onTabChange={(key: string) => { onTabChange={(key: string) => {
setTabActiveKey(key); setTabActiveKey(key);
@ -83,8 +123,10 @@ export default () => {
> >
{/*用户信息*/} {/*用户信息*/}
{UserDetailTabs.user_info === tabActiveKey && <UserInfo userId={id} />} {UserDetailTabs.user_info === tabActiveKey && <UserInfo userId={id} />}
{/*应用授权*/}
{UserDetailTabs.app_access === tabActiveKey && <AppAccess userId={id} />}
{/*登录日志*/} {/*登录日志*/}
{UserDetailTabs.login_audit === tabActiveKey && <LoginAudit id={id} />} {UserDetailTabs.login_audit === tabActiveKey && <LoginAudit userId={id} />}
</PageContainer> </PageContainer>
); );
}; };

View File

@ -56,7 +56,7 @@ export default (props: { userId: string }) => {
padding: 0, padding: 0,
}, },
}} }}
headerTitle={intl.formatMessage({ id: 'pages.account.user_detail.access_strategy' })} headerTitle={intl.formatMessage({ id: 'pages.account.user_detail.app_access_title' })}
request={getAppAccessPolicyList} request={getAppAccessPolicyList}
params={{ subjectId: userId, subjectType: AccessPolicyType.USER }} params={{ subjectId: userId, subjectType: AccessPolicyType.USER }}
rowKey={'id'} rowKey={'id'}

View File

@ -15,6 +15,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import AccessStrategy from './AccessStrategy'; import AppAccess from './AppAccess';
export default AccessStrategy; export default AppAccess;

View File

@ -24,8 +24,8 @@ import { EventStatus } from '@/pages/audit/data.d';
import { getLoginAuditList } from '@/services/account'; import { getLoginAuditList } from '@/services/account';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
export default (props: { id: string }) => { export default (props: { userId: string }) => {
const { id } = props; const { userId } = props;
useEffect(() => {}, []); useEffect(() => {}, []);
const intl = useIntl(); const intl = useIntl();
@ -97,7 +97,7 @@ export default (props: { id: string }) => {
columns={columns} columns={columns}
search={false} search={false}
request={getLoginAuditList} request={getLoginAuditList}
params={{ id }} params={{ userId: userId }}
pagination={{ pageSize: 10 }} pagination={{ pageSize: 10 }}
/> />
</> </>

View File

@ -31,7 +31,7 @@ import { QuestionCircleOutlined } from '@ant-design/icons';
import { omit } from 'lodash'; import { omit } from 'lodash';
import classNames from 'classnames'; import classNames from 'classnames';
import { ParamCheckType } from '@/constant'; import { ParamCheckType } from '@/constant';
import AccessStrategy from '../AccessStrategy'; import AppAccess from '../AppAccess';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
const prefixCls = 'user-detail-info'; const prefixCls = 'user-detail-info';
@ -480,8 +480,6 @@ export default (props: { userId: string }) => {
toolbar={{ settings: [] }} toolbar={{ settings: [] }}
/> />
</ProCard> </ProCard>
<br />
<AccessStrategy userId={userId} />
</div> </div>
); );
}; };

View File

@ -21,8 +21,8 @@
export enum UserDetailTabs { export enum UserDetailTabs {
//用户信息 //用户信息
user_info = 'user-info', user_info = 'user-info',
// //应用授
permission_info = 'permission-info', app_access = 'app_access',
//登录审计 //登录审计
login_audit = 'login-audit', login_audit = 'login-audit',
} }

View File

@ -16,6 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
export default { export default {
'pages.account.user_detail.tabs.user_info': '用户信息',
'pages.account.user_detail.tabs.login_audit': '登录日志',
'pages.account.user_detail.tabs.app_access': '应用权限',
'pages.account.user_detail.extra.delete': '删除用户',
'pages.account.user_detail.extra.delete.confirm_title': '您确定要删除此用户?',
'pages.account.user_detail.extra.delete.confirm_content': '此操作不可恢复,请谨慎操作!',
'pages.account.user_detail.user_info': '账户信息', 'pages.account.user_detail.user_info': '账户信息',
'pages.account.user_detail.user_info.not_selected': '未选择用户', 'pages.account.user_detail.user_info.not_selected': '未选择用户',
'pages.account.user_detail.user_info.columns.open_id': '显示名', 'pages.account.user_detail.user_info.columns.open_id': '显示名',
@ -73,7 +79,7 @@ export default {
'pages.account.user_detail.app_account.columns.option.popconfirm.title': 'pages.account.user_detail.app_account.columns.option.popconfirm.title':
'您确定要删除此应用账户?', '您确定要删除此应用账户?',
'pages.account.user_detail.access_strategy': '已授权应用', 'pages.account.user_detail.app_access_title': '已授权应用',
'pages.account.user_detail.access_strategy.popconfirm.title': '您确定要取消应用授权?', 'pages.account.user_detail.access_strategy.popconfirm.title': '您确定要取消应用授权?',
'pages.account.user_detail.access_strategy.popconfirm.remove': '取消授权', 'pages.account.user_detail.access_strategy.popconfirm.remove': '取消授权',
}; };

View File

@ -15,19 +15,20 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { getUserGroup, updateUserGroup } from '@/services/account'; import { getUserGroup, removeUserGroup, updateUserGroup } from '@/services/account';
import { history } from '@@/core/history'; import { history } from '@@/core/history';
import { PageContainer, ProDescriptions, RouteContext } from '@ant-design/pro-components'; import { PageContainer, ProDescriptions, RouteContext } from '@ant-design/pro-components';
import { useAsyncEffect, useMount } from 'ahooks'; import { useAsyncEffect, useMount } from 'ahooks';
import { App, Skeleton } from 'antd'; import { App, Button, Skeleton } from 'antd';
import { useState } from 'react'; import React, { useState } from 'react';
import MemberList from './components/MemberList'; import MemberList from './components/MemberList';
import { UserGroupDetailTabs } from './constant'; import { UserGroupDetailTabs } from './constant';
import queryString from 'query-string'; import queryString from 'query-string';
import { useIntl, useLocation } from '@umijs/max'; import { useIntl, useLocation } from '@umijs/max';
import useStyles from './style'; import useStyles from './style';
import AccessStrategy from '@/pages/account/UserGroupDetail/components/AccessStrategy'; import AppAccess from './components/AppAccess';
import { ExclamationCircleFilled } from '@ant-design/icons';
/** /**
* *
@ -35,7 +36,7 @@ import AccessStrategy from '@/pages/account/UserGroupDetail/components/AccessStr
export default () => { export default () => {
const intl = useIntl(); const intl = useIntl();
const { styles } = useStyles(); const { styles } = useStyles();
const { message } = App.useApp(); const { message, modal } = App.useApp();
const location = useLocation(); const location = useLocation();
const query = queryString.parse(location.search); const query = queryString.parse(location.search);
const { id } = query as { id: string }; const { id } = query as { id: string };
@ -106,7 +107,7 @@ export default () => {
<ProDescriptions.Item <ProDescriptions.Item
dataIndex="name" dataIndex="name"
label={intl.formatMessage({ label={intl.formatMessage({
id: 'pages.account.user_group_detail.pro_descriptions.name', id: 'pages.account.user_group_detail.descriptions.name',
})} })}
fieldProps={{ fieldProps={{
maxLength: 8, maxLength: 8,
@ -116,14 +117,14 @@ export default () => {
<ProDescriptions.Item <ProDescriptions.Item
dataIndex="code" dataIndex="code"
label={intl.formatMessage({ label={intl.formatMessage({
id: 'pages.account.user_group_detail.pro_descriptions.code', id: 'pages.account.user_group_detail.descriptions.code',
})} })}
copyable copyable
editable={false} editable={false}
/> />
<ProDescriptions.Item <ProDescriptions.Item
label={intl.formatMessage({ label={intl.formatMessage({
id: 'pages.account.user_group_detail.pro_descriptions.remark', id: 'pages.account.user_group_detail.descriptions.remark',
})} })}
className={styles.descriptionRemark} className={styles.descriptionRemark}
dataIndex="remark" dataIndex="remark"
@ -151,12 +152,44 @@ export default () => {
tab: intl.formatMessage({ id: 'pages.account.user_group_detail.tab_list.member' }), tab: intl.formatMessage({ id: 'pages.account.user_group_detail.tab_list.member' }),
}, },
{ {
key: UserGroupDetailTabs.access_policy, key: UserGroupDetailTabs.app_access,
tab: intl.formatMessage({ tab: intl.formatMessage({
id: 'pages.account.user_group_detail.tab_list.access_policy', id: 'pages.account.user_group_detail.tab_list.app_access',
}), }),
}, },
]} ]}
extra={[
<Button
key="delete"
type="primary"
danger
onClick={() => {
const confirmed = modal.error({
centered: true,
title: intl.formatMessage({
id: 'pages.account.user_group_detail.extra.delete.confirm_title',
}),
icon: <ExclamationCircleFilled />,
content: intl.formatMessage({
id: 'pages.account.user_group_detail.extra.delete.confirm_content',
}),
okText: intl.formatMessage({ id: 'app.confirm' }),
okType: 'danger',
okCancel: true,
cancelText: intl.formatMessage({ id: 'app.cancel' }),
onOk: async () => {
const { success } = await removeUserGroup(id);
if (success) {
message.success(intl.formatMessage({ id: 'app.operation_success' }));
confirmed.destroy();
}
},
});
}}
>
{intl.formatMessage({ id: 'pages.account.user_group_detail.extra.delete' })}
</Button>,
]}
tabActiveKey={tabActiveKey} tabActiveKey={tabActiveKey}
onTabChange={(key: string) => { onTabChange={(key: string) => {
setTabActiveKey(key); setTabActiveKey(key);
@ -169,7 +202,7 @@ export default () => {
{/*成员信息*/} {/*成员信息*/}
{type === UserGroupDetailTabs.member && <MemberList id={id} />} {type === UserGroupDetailTabs.member && <MemberList id={id} />}
{/*授权应用*/} {/*授权应用*/}
{type === UserGroupDetailTabs.access_policy && <AccessStrategy userGroupId={id} />} {type === UserGroupDetailTabs.app_access && <AppAccess userGroupId={id} />}
</PageContainer> </PageContainer>
); );
}; };

View File

@ -52,7 +52,7 @@ export default (props: { userGroupId: string }) => {
xxl: 5, xxl: 5,
}} }}
headerTitle={intl.formatMessage({ headerTitle={intl.formatMessage({
id: 'pages.account.user_group_detail.access_strategy', id: 'pages.account.user_group_detail.app_access_title',
})} })}
request={getAppAccessPolicyList} request={getAppAccessPolicyList}
params={{ subjectId: userGroupId, subjectType: AccessPolicyType.USER_GROUP }} params={{ subjectId: userGroupId, subjectType: AccessPolicyType.USER_GROUP }}

View File

@ -15,6 +15,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import AccessStrategy from './AccessStrategy'; import AppAccess from './AppAccess';
export default AccessStrategy; export default AppAccess;

View File

@ -19,8 +19,8 @@
* UserGroupDetailTabs * UserGroupDetailTabs
*/ */
export enum UserGroupDetailTabs { export enum UserGroupDetailTabs {
//access_policy //app_access
access_policy = 'access-policy', app_access = 'app-access',
//权限 //权限
permission_info = 'permission-info', permission_info = 'permission-info',
//member //member

View File

@ -16,21 +16,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
export default { export default {
'pages.account.user_group_detail.extra.delete': '删除用户组',
'pages.account.user_group_detail.extra.delete.confirm_title': '您确定要删除此用户组?',
'pages.account.user_group_detail.extra.delete.confirm_content': '此操作不可恢复,请谨慎操作!',
'pages.account.user_group_detail.common.username': '用户名称', 'pages.account.user_group_detail.common.username': '用户名称',
'pages.account.user_group_detail.common.phone': '手机号', 'pages.account.user_group_detail.common.phone': '手机号',
'pages.account.user_group_detail.common.org_display_path': '所属组织', 'pages.account.user_group_detail.common.org_display_path': '所属组织',
'pages.account.user_group_detail.use_mount.message': '未选择用户组', 'pages.account.user_group_detail.use_mount.message': '未选择用户组',
'pages.account.user_group_detail.pro_descriptions.name': '名称', 'pages.account.user_group_detail.descriptions.name': '名称',
'pages.account.user_group_detail.pro_descriptions.code': '编码', 'pages.account.user_group_detail.descriptions.code': '编码',
'pages.account.user_group_detail.pro_descriptions.remark': '备注', 'pages.account.user_group_detail.descriptions.remark': '备注',
'pages.account.user_group_detail.tab_list.rule': '规则配置', 'pages.account.user_group_detail.tab_list.rule': '规则配置',
'pages.account.user_group_detail.tab_list.member': '成员信息', 'pages.account.user_group_detail.tab_list.member': '成员信息',
'pages.account.user_group_detail.tab_list.access_policy': '授权应用', 'pages.account.user_group_detail.tab_list.app_access': '应用权限',
'pages.account.user_group_detail.tab_list.permission_info': '权限管理', 'pages.account.user_group_detail.tab_list.permission_info': '权限管理',
'pages.account.user_group_detail.access_strategy': '已授权应用', 'pages.account.user_group_detail.app_access_title': '已授权应用',
'pages.account.user_group_detail.access_strategy.metas.popconfirm.title': 'pages.account.user_group_detail.app_access.remove.popconfirm.title': '您确定要取消应用授权?',
'您确定要取消应用授权?', 'pages.account.user_group_detail.app_access.remove.popconfirm.remove': '取消授权',
'pages.account.user_group_detail.access_strategy.metas.popconfirm.remove': '取消授权',
'pages.account.user_group_detail.add_member.columns.username': '用户名称', 'pages.account.user_group_detail.add_member.columns.username': '用户名称',
'pages.account.user_group_detail.add_member.columns.full_name': '用户姓名', 'pages.account.user_group_detail.add_member.columns.full_name': '用户姓名',
'pages.account.user_group_detail.add_member.drawer_form.title': '添加成员', 'pages.account.user_group_detail.add_member.drawer_form.title': '添加成员',

View File

@ -64,7 +64,7 @@ const CreateApp = (props: {
useAsyncEffect(async () => { useAsyncEffect(async () => {
if (open) { if (open) {
form?.setFieldsValue({ name: `${name} 应用` }); form?.setFieldsValue({ name: `${name}` });
} }
}, [open]); }, [open]);
@ -108,7 +108,7 @@ const CreateApp = (props: {
onOk: () => { onOk: () => {
successModal.destroy(); successModal.destroy();
history.push( history.push(
`/app/config?id=${result.id}&name=${values.name}&protocol=${protocol}`, `/app/detail?id=${result.id}&name=${values.name}&protocol=${protocol}`,
); );
}, },
}); });

View File

@ -22,6 +22,7 @@ export default {
'pages.app.list.tool_bar_render.add_app': '添加应用', 'pages.app.list.tool_bar_render.add_app': '添加应用',
'pages.app.list.metas.title': '应用名称', 'pages.app.list.metas.title': '应用名称',
'pages.app.list.metas.group': '应用分组', 'pages.app.list.metas.group': '应用分组',
'pages.app.list.actions.detail': '详情',
'pages.app.list.actions.popconfirm.disable_app': '确定禁用该应用吗?', 'pages.app.list.actions.popconfirm.disable_app': '确定禁用该应用吗?',
'pages.app.list.actions.popconfirm.enable_app': '确定启用该应用吗?', 'pages.app.list.actions.popconfirm.enable_app': '确定启用该应用吗?',
'pages.app.list.actions.popconfirm.delete_app': '您确定要删除此应用?', 'pages.app.list.actions.popconfirm.delete_app': '您确定要删除此应用?',

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { download, filterParamConverter, sortParamConverter } from '@/utils/utils'; import { filterParamConverter, sortParamConverter } from '@/utils/utils';
import type { RequestData } from '@ant-design/pro-components'; import type { RequestData } from '@ant-design/pro-components';
import type { SortOrder } from 'antd/es/table/interface'; import type { SortOrder } from 'antd/es/table/interface';
import { request } from '@umijs/max'; import { request } from '@umijs/max';
@ -62,27 +62,16 @@ export async function getAppConfig(id: string): Promise<API.ApiResult<Record<str
} }
/** /**
* IDP SAML2 * Remove Application
*/ */
export async function downloadIdpSaml2AppMetadata(id: string): Promise<void> { export async function removeApp(id: string): Promise<API.ApiResult<boolean>> {
let fileName = ''; return request<API.ApiResult<boolean>>(`/api/v1/app/delete/${id}`, {
request(`/api/v1/app/saml2/download/idp_metadata_file`, { method: 'DELETE',
method: 'GET', });
params: { appId: id },
responseType: 'blob',
getResponse: true,
})
.then((res) => {
fileName = res.headers['content-disposition'] || res.headers['Content-Disposition'];
return res.data;
})
.then((res) => {
download(new Blob([res]), fileName);
});
} }
/** /**
* Save Saml2 Config * Save App Config
*/ */
export async function saveAppConfig( export async function saveAppConfig(
values: Record<string, any>, values: Record<string, any>,

View File

@ -48,7 +48,7 @@
"@ant-design/charts": "^1.4.2", "@ant-design/charts": "^1.4.2",
"@ant-design/icons": "^5.2.6", "@ant-design/icons": "^5.2.6",
"@ant-design/maps": "^1.0.7", "@ant-design/maps": "^1.0.7",
"@ant-design/pro-components": "^2.6.18", "@ant-design/pro-components": "^2.6.19",
"ahooks": "^3.7.8", "ahooks": "^3.7.8",
"antd": "^5.9.1", "antd": "^5.9.1",
"antd-img-crop": "^4.12.2", "antd-img-crop": "^4.12.2",

View File

@ -117,7 +117,7 @@ export const layout: RunTimeLayoutConfig = ({ initialState, loading }) => {
heightLayoutHeader: showBanner ? 78 : 56, heightLayoutHeader: showBanner ? 78 : 56,
}, },
pageContainer: { pageContainer: {
paddingBlockPageContainerContent: 12, paddingBlockPageContainerContent: 6,
paddingInlinePageContainerContent: 24, paddingInlinePageContainerContent: 24,
}, },
}, },