mirror of https://gitee.com/topiam/eiam
Merge remote-tracking branch 'origin/master'
commit
8482454136
|
@ -21,6 +21,8 @@ import java.io.Serializable;
|
||||||
|
|
||||||
import org.springdoc.core.annotations.ParameterObject;
|
import org.springdoc.core.annotations.ParameterObject;
|
||||||
|
|
||||||
|
import cn.topiam.employee.common.enums.app.AppGroupType;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
@ -49,4 +51,10 @@ public class AppGroupQuery implements Serializable {
|
||||||
@Parameter(description = "分组编码")
|
@Parameter(description = "分组编码")
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分组类型
|
||||||
|
*/
|
||||||
|
@Parameter(description = "分组类型")
|
||||||
|
private AppGroupType type;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package cn.topiam.employee.common.repository.app.impl;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
@ -77,6 +78,11 @@ public class AppGroupRepositoryCustomizedImpl implements AppGroupRepositoryCusto
|
||||||
builder.append(" AND `group`.code_ like '%").append(query.getCode()).append("%'");
|
builder.append(" AND `group`.code_ like '%").append(query.getCode()).append("%'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//分组类型
|
||||||
|
if (ObjectUtils.isNotEmpty(query.getType())) {
|
||||||
|
builder.append(" AND `group`.type_ like '%").append(query.getType().getCode()).append("%'");
|
||||||
|
}
|
||||||
|
|
||||||
builder.append(" ORDER BY `group`.create_time DESC");
|
builder.append(" ORDER BY `group`.create_time DESC");
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
String sql = builder.toString();
|
String sql = builder.toString();
|
||||||
|
|
|
@ -134,27 +134,45 @@ export default [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
//应用列表
|
//应用管理
|
||||||
{
|
{
|
||||||
name: 'app',
|
name: 'app',
|
||||||
icon: 'AppstoreOutlined',
|
icon: 'AppstoreOutlined',
|
||||||
path: '/app',
|
path: '/app',
|
||||||
|
routes: [
|
||||||
|
// 应用列表
|
||||||
|
{
|
||||||
|
path: '/app',
|
||||||
|
redirect: '/app/list',
|
||||||
|
},
|
||||||
|
// 应用列表
|
||||||
|
{
|
||||||
|
name: 'list',
|
||||||
|
path: '/app/list',
|
||||||
component: './app/AppList',
|
component: './app/AppList',
|
||||||
},
|
},
|
||||||
//创建应用
|
//创建应用
|
||||||
{
|
{
|
||||||
name: 'app.create',
|
name: 'create',
|
||||||
path: '/app/create',
|
path: '/app/list/create',
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
component: './app/AppCreate',
|
component: './app/AppCreate',
|
||||||
},
|
},
|
||||||
//应用配置
|
//应用配置
|
||||||
{
|
{
|
||||||
name: 'app.config',
|
name: 'config',
|
||||||
path: '/app/config',
|
path: '/app/list/config',
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
component: './app/AppConfig',
|
component: './app/AppConfig',
|
||||||
},
|
},
|
||||||
|
// 应用分组
|
||||||
|
{
|
||||||
|
name: 'group',
|
||||||
|
path: '/app/group',
|
||||||
|
component: './app/AppGroup',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
//行为审计
|
//行为审计
|
||||||
{
|
{
|
||||||
name: 'audit',
|
name: 'audit',
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
"@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.18",
|
||||||
"ahooks": "^3.7.8",
|
"ahooks": "^3.7.8",
|
||||||
"antd": "^5.8.6",
|
"antd": "^5.9.0",
|
||||||
"antd-img-crop": "^4.12.2",
|
"antd-img-crop": "^4.12.2",
|
||||||
"antd-style": "^3.4.5",
|
"antd-style": "^3.4.5",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
|
@ -99,7 +99,7 @@
|
||||||
"@umijs/max": "^4.0.80",
|
"@umijs/max": "^4.0.80",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cross-port-killer": "^1.4.0",
|
"cross-port-killer": "^1.4.0",
|
||||||
"eslint": "^8.48.0",
|
"eslint": "^8.49.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^14.0.1",
|
"lint-staged": "^14.0.1",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
|
|
|
@ -57,7 +57,7 @@ const GlobalHeaderRight: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<link rel="icon" href={initialState?.globalConfig?.appearance?.favicon} />
|
<link rel="icon" href={'/favicon.ico'} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<About />
|
<About />
|
||||||
<SelectLang className={styles.action} />
|
<SelectLang className={styles.action} />
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
import { getUserGroupList } from '@/services/account';
|
import { getUserGroupList } from '@/services/account';
|
||||||
import type { SelectProps } from 'antd';
|
import type { SelectProps } from 'antd';
|
||||||
import { Select, Spin } from 'antd';
|
import { Select, Spin } from 'antd';
|
||||||
import { ReactText, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useAsyncEffect } from 'ahooks';
|
import { useAsyncEffect } from 'ahooks';
|
||||||
import { SortOrder } from 'antd/es/table/interface';
|
import { SortOrder } from 'antd/es/table/interface';
|
||||||
import { RequestData } from '@ant-design/pro-components';
|
import { RequestData } from '@ant-design/pro-components';
|
||||||
|
@ -35,9 +35,9 @@ export type UserGroupSelectProps<ValueType = UserData> = Omit<
|
||||||
>;
|
>;
|
||||||
|
|
||||||
async function getAllUserGroupList(
|
async function getAllUserGroupList(
|
||||||
params?: Record<string, any>,
|
params: Record<string, any>,
|
||||||
sort?: Record<string, SortOrder>,
|
sort: Record<string, SortOrder>,
|
||||||
filter?: Record<string, ReactText[] | null>,
|
filter: Record<string, (string | number)[] | null>,
|
||||||
): Promise<RequestData<AccountAPI.ListUserGroup>> {
|
): Promise<RequestData<AccountAPI.ListUserGroup>> {
|
||||||
let pageSize = 100,
|
let pageSize = 100,
|
||||||
current = 1;
|
current = 1;
|
||||||
|
@ -79,7 +79,7 @@ const UserGroupSelect = (props: UserGroupSelectProps) => {
|
||||||
|
|
||||||
useAsyncEffect(async () => {
|
useAsyncEffect(async () => {
|
||||||
setFetching(true);
|
setFetching(true);
|
||||||
const { success, data } = await getAllUserGroupList().finally(() => {
|
const { success, data } = await getAllUserGroupList({}, {}, {}).finally(() => {
|
||||||
setFetching(false);
|
setFetching(false);
|
||||||
});
|
});
|
||||||
if (success && data) {
|
if (success && data) {
|
||||||
|
|
|
@ -39,6 +39,8 @@ export default {
|
||||||
'menu.account.logout': '退出登录',
|
'menu.account.logout': '退出登录',
|
||||||
'menu.social-bind': '用户绑定',
|
'menu.social-bind': '用户绑定',
|
||||||
'menu.app': '应用管理',
|
'menu.app': '应用管理',
|
||||||
|
'menu.app.list': '应用列表',
|
||||||
|
'menu.app.group': '应用分组',
|
||||||
'menu.app.create': '创建应用',
|
'menu.app.create': '创建应用',
|
||||||
'menu.app.config': '应用配置',
|
'menu.app.config': '应用配置',
|
||||||
'menu.account': '账户管理',
|
'menu.account': '账户管理',
|
||||||
|
|
|
@ -81,7 +81,7 @@ export default () => {
|
||||||
history.push(`/account/identity-source`);
|
history.push(`/account/identity-source`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!type) {
|
if (!type || !IdentitySourceDetailTabs[type]) {
|
||||||
setTabActiveKey(IdentitySourceDetailTabs.config);
|
setTabActiveKey(IdentitySourceDetailTabs.config);
|
||||||
history.push({
|
history.push({
|
||||||
pathname: location.pathname,
|
pathname: location.pathname,
|
||||||
|
|
|
@ -42,9 +42,9 @@ export default (props: CreateModelProps) => {
|
||||||
wrapperCol={{ span: 19 }}
|
wrapperCol={{ span: 19 }}
|
||||||
onFinish={async (values: Record<string, string>) => {
|
onFinish={async (values: Record<string, string>) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await onFinish(values);
|
await onFinish(values).finally(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return !!result;
|
});
|
||||||
}}
|
}}
|
||||||
modalProps={{
|
modalProps={{
|
||||||
destroyOnClose: true,
|
destroyOnClose: true,
|
||||||
|
|
|
@ -78,7 +78,7 @@ export default (props: CreateOrganizationFormProps<AccountAPI.CreateOrganization
|
||||||
*/
|
*/
|
||||||
const cancel = async () => {
|
const cancel = async () => {
|
||||||
if (onCancel) {
|
if (onCancel) {
|
||||||
await onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
};
|
};
|
||||||
|
|
|
@ -78,9 +78,9 @@ const UpdateModel = (props: UpdateFormProps<AccountAPI.UpdateOrganization>) => {
|
||||||
open={visible}
|
open={visible}
|
||||||
onFinish={async (values: AccountAPI.UpdateOrganization) => {
|
onFinish={async (values: AccountAPI.UpdateOrganization) => {
|
||||||
setUpdateLoading(true);
|
setUpdateLoading(true);
|
||||||
const result = await onFinish(values);
|
await onFinish(values).finally(() => {
|
||||||
setUpdateLoading(false);
|
setUpdateLoading(false);
|
||||||
return !!result;
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Skeleton loading={loading} active={true}>
|
<Skeleton loading={loading} active={true}>
|
||||||
|
|
|
@ -61,7 +61,7 @@ export default (props: UpdateOrganizationFormProps<AccountAPI.UpdateOrganization
|
||||||
*/
|
*/
|
||||||
const cancel = async () => {
|
const cancel = async () => {
|
||||||
if (onCancel) {
|
if (onCancel) {
|
||||||
await onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* 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 { history } from '@@/core/history';
|
import { history } from '@@/core/history';
|
||||||
import { DesktopOutlined, ProfileOutlined } from '@ant-design/icons';
|
import { DesktopOutlined, ProfileOutlined, SafetyOutlined } from '@ant-design/icons';
|
||||||
import { GridContent, PageContainer } from '@ant-design/pro-components';
|
import { GridContent, PageContainer } from '@ant-design/pro-components';
|
||||||
import { useAsyncEffect } from 'ahooks';
|
import { useAsyncEffect } from 'ahooks';
|
||||||
import type { MenuProps } from 'antd';
|
import type { MenuProps } from 'antd';
|
||||||
|
@ -32,6 +32,8 @@ import { useIntl, useLocation } from '@umijs/max';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { AppProtocolType } from '@/constant';
|
import { AppProtocolType } from '@/constant';
|
||||||
|
import PermissionResource from './components/PermissionResource';
|
||||||
|
import PermissionRole from './components/PermissionRole';
|
||||||
|
|
||||||
const prefixCls = 'app-config';
|
const prefixCls = 'app-config';
|
||||||
|
|
||||||
|
@ -118,6 +120,27 @@ export default () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: ConfigTabs.app_permission,
|
||||||
|
label: intl.formatMessage({ id: 'pages.app.config.items.app_permission' }),
|
||||||
|
icon: React.createElement(() => {
|
||||||
|
return <SafetyOutlined />;
|
||||||
|
}),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: ConfigTabs.permission_resource,
|
||||||
|
label: intl.formatMessage({
|
||||||
|
id: 'pages.app.config.items.app_permission.permission_resource',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: ConfigTabs.permission_role,
|
||||||
|
label: intl.formatMessage({
|
||||||
|
id: 'pages.app.config.items.app_permission.permission_role',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
useAsyncEffect(async () => {
|
useAsyncEffect(async () => {
|
||||||
|
@ -126,12 +149,12 @@ export default () => {
|
||||||
history.push('/app');
|
history.push('/app');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!type) {
|
if (!type || !ConfigTabs[type]) {
|
||||||
setKeys([ConfigTabs.protocol_config]);
|
setKeys([ConfigTabs.basic]);
|
||||||
history.replace({
|
history.replace({
|
||||||
pathname: location.pathname,
|
pathname: location.pathname,
|
||||||
search: queryString.stringify({
|
search: queryString.stringify({
|
||||||
type: ConfigTabs.protocol_config,
|
type: ConfigTabs.basic,
|
||||||
id,
|
id,
|
||||||
protocol,
|
protocol,
|
||||||
name,
|
name,
|
||||||
|
@ -152,6 +175,8 @@ export default () => {
|
||||||
[ConfigTabs.protocol_config]: AppProtocol,
|
[ConfigTabs.protocol_config]: AppProtocol,
|
||||||
[ConfigTabs.app_account]: AppAccount,
|
[ConfigTabs.app_account]: AppAccount,
|
||||||
[ConfigTabs.access_policy]: AccessPolicy,
|
[ConfigTabs.access_policy]: AccessPolicy,
|
||||||
|
[ConfigTabs.permission_resource]: PermissionResource,
|
||||||
|
[ConfigTabs.permission_role]: PermissionRole,
|
||||||
};
|
};
|
||||||
const Component = components[key];
|
const Component = components[key];
|
||||||
return <Component {...rest} />;
|
return <Component {...rest} />;
|
||||||
|
|
|
@ -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 { getApp, updateApp } from '@/services/app';
|
import { updateApp } from '@/services/app';
|
||||||
|
|
||||||
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
|
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
|
||||||
import { useAsyncEffect } from 'ahooks';
|
import { useAsyncEffect } from 'ahooks';
|
||||||
|
@ -30,6 +30,7 @@ import classNames from 'classnames';
|
||||||
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
|
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
import { uploadFile } from '@/services/upload';
|
import { uploadFile } from '@/services/upload';
|
||||||
import { GetApp } from '@/pages/app/AppConfig/data';
|
import { GetApp } from '@/pages/app/AppConfig/data';
|
||||||
|
import { getApp } from '../../service';
|
||||||
|
|
||||||
const prefixCls = 'app-basic-info';
|
const prefixCls = 'app-basic-info';
|
||||||
const AppBasic = (props: { appId: string }) => {
|
const AppBasic = (props: { appId: string }) => {
|
||||||
|
|
|
@ -23,6 +23,9 @@ const useStyle = createStyles(({ prefixCls, token }, props) => {
|
||||||
return {
|
return {
|
||||||
main: {
|
main: {
|
||||||
height: 'calc(100vh - 178px)',
|
height: 'calc(100vh - 178px)',
|
||||||
|
[`${antCls}-pro-card-body`]: {
|
||||||
|
overflow: 'auto !important',
|
||||||
|
},
|
||||||
[`.${prefix}-descriptions`]: {
|
[`.${prefix}-descriptions`]: {
|
||||||
[`${antCls}-descriptions-item-container ${antCls}-space-item`]: {
|
[`${antCls}-descriptions-item-container ${antCls}-space-item`]: {
|
||||||
span: {
|
span: {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* 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 { AppProtocolType } from '@/constant';
|
import { AppProtocolType } from '@/constant';
|
||||||
import { getApp } from '@/services/app';
|
import { getApp } from '../../service';
|
||||||
import { ProCard } from '@ant-design/pro-components';
|
import { ProCard } from '@ant-design/pro-components';
|
||||||
|
|
||||||
import { useAsyncEffect } from 'ahooks';
|
import { useAsyncEffect } from 'ahooks';
|
||||||
|
@ -25,7 +25,7 @@ import { useState } from 'react';
|
||||||
import FromConfig from './FromProtocolConfig';
|
import FromConfig from './FromProtocolConfig';
|
||||||
import JwtConfig from './JwtProtocolConfig';
|
import JwtConfig from './JwtProtocolConfig';
|
||||||
import OidcConfig from './OidcProtocolConfig';
|
import OidcConfig from './OidcProtocolConfig';
|
||||||
import { GetApp } from '../../data';
|
import { GetApp } from '../../data.d';
|
||||||
import { useIntl } from '@@/exports';
|
import { useIntl } from '@@/exports';
|
||||||
|
|
||||||
export default (props: { appId: string }) => {
|
export default (props: { appId: string }) => {
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
import { getPermissionResource, permissionResourceParamCheck } from '../../service';
|
||||||
|
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
|
||||||
|
import { Form, Spin } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useAsyncEffect } from 'ahooks';
|
||||||
|
import Paragraph from 'antd/es/typography/Paragraph';
|
||||||
|
|
||||||
|
type UpdateFormProps = {
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
id?: string;
|
||||||
|
/**
|
||||||
|
* SYSTEM
|
||||||
|
*/
|
||||||
|
appId: string;
|
||||||
|
/**
|
||||||
|
* 是否显示
|
||||||
|
*/
|
||||||
|
open: boolean;
|
||||||
|
/**
|
||||||
|
* 取消方法
|
||||||
|
*/
|
||||||
|
onCancel: (e?: React.MouseEvent | React.KeyboardEvent) => void;
|
||||||
|
/**
|
||||||
|
* 提交
|
||||||
|
*/
|
||||||
|
onFinish?: (formData: Record<string, string>) => Promise<boolean | void>;
|
||||||
|
};
|
||||||
|
const UpdateResource: React.FC<UpdateFormProps> = (props) => {
|
||||||
|
const { open, onCancel, onFinish, id, appId } = props;
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
useAsyncEffect(async () => {
|
||||||
|
if (open && id) {
|
||||||
|
setLoading(true);
|
||||||
|
const { success, result } = await getPermissionResource(id);
|
||||||
|
if (success && result) {
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [id, onCancel, open]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalForm
|
||||||
|
title="修改资源"
|
||||||
|
width={'500px'}
|
||||||
|
open={open}
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 20 }}
|
||||||
|
layout={'horizontal'}
|
||||||
|
labelAlign={'right'}
|
||||||
|
modalProps={{
|
||||||
|
maskClosable: true,
|
||||||
|
destroyOnClose: true,
|
||||||
|
onCancel: onCancel,
|
||||||
|
}}
|
||||||
|
form={form}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await onFinish?.(values);
|
||||||
|
setLoading(false);
|
||||||
|
return result;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<ProFormText name={'id'} hidden />
|
||||||
|
<ProFormText
|
||||||
|
name="name"
|
||||||
|
label="资源名称"
|
||||||
|
placeholder="请输入资源名称"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入资源名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
const { success, result } = await permissionResourceParamCheck(
|
||||||
|
appId,
|
||||||
|
'NAME',
|
||||||
|
value,
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
setLoading(false);
|
||||||
|
if (!success) {
|
||||||
|
return Promise.reject<any>();
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
return Promise.reject<any>(new Error('资源名称已存在'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validateTrigger: ['onBlur'],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name="code"
|
||||||
|
label="资源编码"
|
||||||
|
placeholder="请输入资源编码"
|
||||||
|
proFieldProps={{
|
||||||
|
render: (value: string) => {
|
||||||
|
return (
|
||||||
|
value && (
|
||||||
|
<Paragraph copyable={{ text: value }} style={{ marginBottom: '0' }}>
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `<span>${value}</span>`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Paragraph>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
readonly
|
||||||
|
extra="资源编码在当前应用中的唯一标识,不能重复,仅支持英文、数字、下划线,创建后不可修改。"
|
||||||
|
/>
|
||||||
|
<ProFormTextArea
|
||||||
|
name="desc"
|
||||||
|
fieldProps={{ rows: 2, maxLength: 20, showCount: false }}
|
||||||
|
label="资源描述"
|
||||||
|
placeholder="请输入资源描述"
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
|
</ModalForm>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default UpdateResource;
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
import { getPermissionRole, permissionRoleParamCheck } from '../../service';
|
||||||
|
import type { ProFormInstance } from '@ant-design/pro-components';
|
||||||
|
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
|
||||||
|
import { Spin } from 'antd';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import { useAsyncEffect } from 'ahooks';
|
||||||
|
import Paragraph from 'antd/es/typography/Paragraph';
|
||||||
|
|
||||||
|
type UpdateFormProps = {
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
id: string | undefined;
|
||||||
|
/**
|
||||||
|
* 是否显示
|
||||||
|
*/
|
||||||
|
open: boolean;
|
||||||
|
/**
|
||||||
|
* 取消方法
|
||||||
|
*/
|
||||||
|
onCancel: () => void;
|
||||||
|
/**
|
||||||
|
* 提交
|
||||||
|
*/
|
||||||
|
onFinish?: (formData: Record<string, string>) => Promise<boolean | void>;
|
||||||
|
};
|
||||||
|
const UpdateRole: React.FC<UpdateFormProps> = (props) => {
|
||||||
|
const { open, onCancel, onFinish, id } = props;
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const formRef = useRef<ProFormInstance>();
|
||||||
|
|
||||||
|
useAsyncEffect(async () => {
|
||||||
|
if (open && id) {
|
||||||
|
setLoading(true);
|
||||||
|
const { success, result } = await getPermissionRole(id);
|
||||||
|
if (success && result) {
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [id, open]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalForm
|
||||||
|
title="修改角色"
|
||||||
|
width={600}
|
||||||
|
modalProps={{
|
||||||
|
onCancel: onCancel,
|
||||||
|
destroyOnClose: true,
|
||||||
|
}}
|
||||||
|
layout={'horizontal'}
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
labelAlign={'right'}
|
||||||
|
open={open}
|
||||||
|
key={'update'}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await onFinish?.(values);
|
||||||
|
setLoading(false);
|
||||||
|
return result;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<ProFormText name="id" hidden />
|
||||||
|
<ProFormText name="appId" hidden />
|
||||||
|
<ProFormText
|
||||||
|
name="name"
|
||||||
|
label="名称"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入角色名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: async (rule, value) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
const { success, result } = await permissionRoleParamCheck(
|
||||||
|
formRef.current?.getFieldValue('appId'),
|
||||||
|
'NAME',
|
||||||
|
value,
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
setLoading(false);
|
||||||
|
if (!success) {
|
||||||
|
return Promise.reject<any>();
|
||||||
|
}
|
||||||
|
if (!result) {
|
||||||
|
return Promise.reject<any>(new Error('手机号已存在'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validateTrigger: ['onBlur'],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
placeholder="请输入角色名称"
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
name="code"
|
||||||
|
label="标识"
|
||||||
|
placeholder="请输入角色标识"
|
||||||
|
readonly
|
||||||
|
proFieldProps={{
|
||||||
|
render: (value: string) => {
|
||||||
|
return (
|
||||||
|
value && (
|
||||||
|
<Paragraph copyable={{ text: value }} style={{ marginBottom: '0' }}>
|
||||||
|
<span
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `<span>${value}</span>`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Paragraph>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
extra="角色编码在当前应用中的唯一标识,不能重复,仅支持英文、数字、下划线,创建后不可修改。"
|
||||||
|
/>
|
||||||
|
<ProFormTextArea
|
||||||
|
name="remark"
|
||||||
|
fieldProps={{ rows: 2, maxLength: 20, showCount: false }}
|
||||||
|
label="描述"
|
||||||
|
placeholder="请输入角色描述"
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
|
</ModalForm>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default UpdateRole;
|
|
@ -24,6 +24,7 @@ export default {
|
||||||
'pages.app.config.basic.icon.desc.2': '建议使用 256 * 256 像素方形图标',
|
'pages.app.config.basic.icon.desc.2': '建议使用 256 * 256 像素方形图标',
|
||||||
'pages.app.config.basic.enabled': '应用状态',
|
'pages.app.config.basic.enabled': '应用状态',
|
||||||
'pages.app.config.basic.type': '应用类型',
|
'pages.app.config.basic.type': '应用类型',
|
||||||
|
'pages.app.config.basic.group': '应用分组',
|
||||||
'pages.app.config.basic.type.value_enum.custom_made': '定制应用',
|
'pages.app.config.basic.type.value_enum.custom_made': '定制应用',
|
||||||
'pages.app.config.basic.type.value_enum.standard': '标准应用',
|
'pages.app.config.basic.type.value_enum.standard': '标准应用',
|
||||||
'pages.app.config.basic.type.value_enum.self_developed': '自研应用',
|
'pages.app.config.basic.type.value_enum.self_developed': '自研应用',
|
||||||
|
@ -591,6 +592,10 @@ export default {
|
||||||
'授权组织',
|
'授权组织',
|
||||||
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message':
|
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message':
|
||||||
'请选择组织节点',
|
'请选择组织节点',
|
||||||
|
'pages.app.config.items.app_permission': '权限管理',
|
||||||
|
'pages.app.config.items.app_permission.permission_resource': '资源管理',
|
||||||
|
'pages.app.config.items.app_permission.permission_role': '角色管理',
|
||||||
|
'pages.app.config.items.app_permission.permission_audit': '权限审计',
|
||||||
'pages.app.config.items.account_sync': '账户同步',
|
'pages.app.config.items.account_sync': '账户同步',
|
||||||
'pages.app.config.error': '未指定应用',
|
'pages.app.config.error': '未指定应用',
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
ModalForm,
|
ModalForm,
|
||||||
PageContainer,
|
PageContainer,
|
||||||
ProCard,
|
ProCard,
|
||||||
|
ProFormSelect,
|
||||||
ProFormText,
|
ProFormText,
|
||||||
ProFormTextArea,
|
ProFormTextArea,
|
||||||
ProList,
|
ProList,
|
||||||
|
@ -38,6 +39,7 @@ import classnames from 'classnames';
|
||||||
import { ListTemplate } from './data.d';
|
import { ListTemplate } from './data.d';
|
||||||
import { AppType } from '@/constant';
|
import { AppType } from '@/constant';
|
||||||
import { createApp, getAppTemplateList } from './service';
|
import { createApp, getAppTemplateList } from './service';
|
||||||
|
import { getAllAppGroupList } from '@/services/app';
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
const { Paragraph } = Typography;
|
||||||
const prefixCls = 'topiam-create-app';
|
const prefixCls = 'topiam-create-app';
|
||||||
|
@ -106,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/list/config?id=${result.id}&name=${values.name}&protocol=${protocol}`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -128,6 +130,23 @@ const CreateApp = (props: {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
<ProFormSelect
|
||||||
|
name="groups"
|
||||||
|
mode="multiple"
|
||||||
|
label={'归属分组'}
|
||||||
|
request={async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const { success, data } = await getAllAppGroupList({}, {}, {}).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
if (success && data) {
|
||||||
|
return data.map((i) => {
|
||||||
|
return { label: i.name, value: i.id };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<ProFormTextArea
|
<ProFormTextArea
|
||||||
name="remark"
|
name="remark"
|
||||||
preserve={false}
|
preserve={false}
|
||||||
|
@ -232,6 +251,7 @@ const AppCreate = () => {
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</PageContainer>
|
||||||
{createAppTemplate && (
|
{createAppTemplate && (
|
||||||
<CreateApp
|
<CreateApp
|
||||||
code={createAppTemplate?.code}
|
code={createAppTemplate?.code}
|
||||||
|
@ -244,10 +264,7 @@ const AppCreate = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</PageContainer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default () => {
|
export default AppCreate;
|
||||||
return <AppCreate />;
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
|
import type { ActionType, ProColumns } from '@ant-design/pro-components';
|
||||||
|
import { PageContainer, ProTable } from '@ant-design/pro-components';
|
||||||
|
import { App, Button, Popconfirm, Tag } from 'antd';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import CreateModal from './components/CreateModal';
|
||||||
|
import UpdateModal from './components/UpdateModal';
|
||||||
|
import { createAppGroup, removeAppGroup, updateAppGroup } from './service';
|
||||||
|
import { useIntl } from '@@/exports';
|
||||||
|
import { getAppGroupList } from '@/services/app';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const [createModalOpen, setCreateModalOpen] = useState<boolean>(false);
|
||||||
|
const [updateModalOpen, setUpdateModalOpen] = useState<boolean>(false);
|
||||||
|
const [id, setId] = useState<string>();
|
||||||
|
const { message } = App.useApp();
|
||||||
|
const columns: ProColumns<AppAPI.AppGroupList>[] = [
|
||||||
|
{
|
||||||
|
title: intl.formatMessage({ id: 'pages.app_group.list.column.name' }),
|
||||||
|
dataIndex: 'name',
|
||||||
|
fixed: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: intl.formatMessage({ id: 'pages.app_group.list.column.code' }),
|
||||||
|
dataIndex: 'code',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: intl.formatMessage({ id: 'pages.app_group.list.column.app_count' }),
|
||||||
|
dataIndex: 'appCount',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: intl.formatMessage({ id: 'pages.app_group.list.column.type' }),
|
||||||
|
dataIndex: 'type',
|
||||||
|
valueEnum: {
|
||||||
|
default: {
|
||||||
|
text: intl.formatMessage({
|
||||||
|
id: 'pages.app_group.list.column.type.default',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
custom: {
|
||||||
|
text: intl.formatMessage({
|
||||||
|
id: 'pages.app_group.list.column.type.custom',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render: (_, record) => (
|
||||||
|
<>
|
||||||
|
{record.type === 'custom' && (
|
||||||
|
<Tag color={'#108ee9'} key={'custom'}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: 'pages.app_group.list.column.type.custom',
|
||||||
|
})}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{record.type === 'default' && (
|
||||||
|
<Tag color={'#2db7f5'} key={'default'}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: 'pages.app_group.list.column.type.default',
|
||||||
|
})}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: intl.formatMessage({ id: 'pages.app_group.list.column.create_time' }),
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
search: false,
|
||||||
|
align: 'center',
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: intl.formatMessage({ id: 'pages.app_group.list.column.remark' }),
|
||||||
|
dataIndex: 'remark',
|
||||||
|
search: false,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: intl.formatMessage({ id: 'pages.app_group.list.column.option' }),
|
||||||
|
valueType: 'option',
|
||||||
|
key: 'option',
|
||||||
|
width: 100,
|
||||||
|
align: 'center',
|
||||||
|
render: (text, record) => [
|
||||||
|
<a
|
||||||
|
key="editable"
|
||||||
|
onClick={() => {
|
||||||
|
setId(record.id);
|
||||||
|
setUpdateModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'app.update' })}
|
||||||
|
</a>,
|
||||||
|
<Popconfirm
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: 'pages.app_group.list.actions.popconfirm.delete',
|
||||||
|
})}
|
||||||
|
placement="bottomRight"
|
||||||
|
icon={
|
||||||
|
<QuestionCircleOutlined
|
||||||
|
style={{
|
||||||
|
color: 'red',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={async () => {
|
||||||
|
const { success } = await removeAppGroup(record.id);
|
||||||
|
if (success) {
|
||||||
|
message.success(intl.formatMessage({ id: 'app.operation_success' }));
|
||||||
|
actionRef.current?.reload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
okText={intl.formatMessage({ id: 'app.yes' })}
|
||||||
|
cancelText={intl.formatMessage({ id: 'app.no' })}
|
||||||
|
key="delete"
|
||||||
|
>
|
||||||
|
<a target="_blank" key="remove" style={{ color: 'red' }}>
|
||||||
|
{intl.formatMessage({ id: 'app.delete' })}
|
||||||
|
</a>
|
||||||
|
</Popconfirm>,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<ProTable<AppAPI.AppGroupList>
|
||||||
|
columns={columns}
|
||||||
|
actionRef={actionRef}
|
||||||
|
request={getAppGroupList}
|
||||||
|
rowKey="id"
|
||||||
|
search={{
|
||||||
|
labelWidth: 'auto',
|
||||||
|
}}
|
||||||
|
scroll={{ x: 900 }}
|
||||||
|
form={{
|
||||||
|
syncToUrl: (values, type) => {
|
||||||
|
if (type === 'get') {
|
||||||
|
return {
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
pagination={{
|
||||||
|
pageSize: 5,
|
||||||
|
}}
|
||||||
|
dateFormatter="string"
|
||||||
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
key="button"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
setCreateModalOpen(true);
|
||||||
|
}}
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'pages.app_group.list.create' })}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<CreateModal
|
||||||
|
open={createModalOpen}
|
||||||
|
onCancel={() => {
|
||||||
|
setCreateModalOpen(false);
|
||||||
|
}}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
const { result, success } = await createAppGroup(values);
|
||||||
|
if (success && result) {
|
||||||
|
message.success(intl.formatMessage({ id: 'app.create_success' }));
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}
|
||||||
|
actionRef.current?.reload();
|
||||||
|
setCreateModalOpen(false);
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{id && (
|
||||||
|
<UpdateModal
|
||||||
|
open={updateModalOpen}
|
||||||
|
id={id}
|
||||||
|
onCancel={() => {
|
||||||
|
setUpdateModalOpen(false);
|
||||||
|
}}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
const { result, success } = await updateAppGroup(values);
|
||||||
|
if (success && result) {
|
||||||
|
message.success(intl.formatMessage({ id: 'app.create_success' }));
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}
|
||||||
|
actionRef.current?.reload();
|
||||||
|
setUpdateModalOpen(false);
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
|
||||||
|
import { Form, Spin } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useIntl } from '@@/exports';
|
||||||
|
import { getAppGroup } from '@/pages/app/AppGroup/service';
|
||||||
|
|
||||||
|
export default (props: {
|
||||||
|
id: string;
|
||||||
|
open: boolean;
|
||||||
|
onFinish: (formData: Record<string, string>) => Promise<boolean | void>;
|
||||||
|
onCancel: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
|
}) => {
|
||||||
|
const { id, open, onCancel, onFinish } = props;
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const intl = useIntl();
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalForm
|
||||||
|
title={intl.formatMessage({ id: 'pages.app_group.update.modal_form.title' })}
|
||||||
|
form={form}
|
||||||
|
open={open}
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 20 }}
|
||||||
|
width={'500px'}
|
||||||
|
labelAlign={'right'}
|
||||||
|
preserve={false}
|
||||||
|
layout={'horizontal'}
|
||||||
|
autoFocusFirstInput
|
||||||
|
modalProps={{
|
||||||
|
maskClosable: true,
|
||||||
|
destroyOnClose: true,
|
||||||
|
onCancel: onCancel,
|
||||||
|
}}
|
||||||
|
onOpenChange={async (visible) => {
|
||||||
|
if (visible) {
|
||||||
|
setLoading(true);
|
||||||
|
const { result, success } = await getAppGroup(id).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
if (success) {
|
||||||
|
form.setFieldsValue(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
setLoading(true);
|
||||||
|
await onFinish(values).finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<ProFormText hidden name="id" />
|
||||||
|
<ProFormText
|
||||||
|
label={intl.formatMessage({ id: 'pages.app_group.modal_form.name' })}
|
||||||
|
name="name"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: intl.formatMessage({
|
||||||
|
id: 'pages.app_group.modal_form.name.rule.0.message',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<ProFormText
|
||||||
|
label={intl.formatMessage({ id: 'pages.app_group.modal_form.name' })}
|
||||||
|
name="code"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
<ProFormTextArea
|
||||||
|
label={intl.formatMessage({ id: 'pages.app_group.modal_form.remark' })}
|
||||||
|
name="remark"
|
||||||
|
fieldProps={{
|
||||||
|
placeholder: intl.formatMessage({
|
||||||
|
id: 'pages.app_group.modal_form.remark.placeholder',
|
||||||
|
}),
|
||||||
|
rows: 2,
|
||||||
|
maxLength: 20,
|
||||||
|
autoComplete: 'off',
|
||||||
|
showCount: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
|
</ModalForm>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
import AppGroup from './AppGroup';
|
||||||
|
export default AppGroup;
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
'pages.app_group.list.create': '创建分组',
|
||||||
|
'pages.app_group.list.column.name': '分组名称',
|
||||||
|
'pages.app_group.list.column.code': '分组编码',
|
||||||
|
'pages.app_group.list.column.create_time': '创建时间',
|
||||||
|
'pages.app_group.list.column.remark': '备注',
|
||||||
|
'pages.app_group.list.column.app_count': '应用数量',
|
||||||
|
'pages.app_group.list.column.type': '分组类型',
|
||||||
|
'pages.app_group.list.column.type.default': '系统默认',
|
||||||
|
'pages.app_group.list.column.type.custom': '自定义',
|
||||||
|
'pages.app_group.list.column.option': '操作',
|
||||||
|
'pages.app_group.list.actions.popconfirm.delete': '您确定要删除此应用分组?',
|
||||||
|
'pages.app_group.create.modal_form.title': '添加分组',
|
||||||
|
'pages.app_group.modal_form.name': '分组名称',
|
||||||
|
'pages.app_group.modal_form.name.placeholder': '请输入分组名称',
|
||||||
|
'pages.app_group.modal_form.name.rule.0.message': '分组名称为必填项',
|
||||||
|
'pages.app_group.modal_form.code': '分组编码',
|
||||||
|
'pages.app_group.modal_form.code.placeholder': '请输入分组编码',
|
||||||
|
'pages.app_group.modal_form.code.rule.0.message': '分组编码为必填项',
|
||||||
|
'pages.app_group.modal_form.remark': '备注',
|
||||||
|
'pages.app_group.modal_form.remark.placeholder': '请输入备注',
|
||||||
|
'pages.app_group.update.modal_form.title': '修改分组',
|
||||||
|
};
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* eiam-console - Employee Identity and Access Management
|
||||||
|
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
import { request } from '@umijs/max';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建应用分组
|
||||||
|
*/
|
||||||
|
export async function createAppGroup(
|
||||||
|
params: Record<string, string>,
|
||||||
|
): Promise<API.ApiResult<Record<string, string>>> {
|
||||||
|
return request<API.ApiResult<Record<string, string>>>(`/api/v1/app/group/create`, {
|
||||||
|
method: 'POST',
|
||||||
|
data: params,
|
||||||
|
requestType: 'json',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用分组
|
||||||
|
*/
|
||||||
|
export async function getAppGroup(id: string): Promise<API.ApiResult<Record<string, string>>> {
|
||||||
|
return request<API.ApiResult<Record<string, string>>>(`/api/v1/app/group/get/${id}`, {
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改应用分组
|
||||||
|
*/
|
||||||
|
export async function updateAppGroup(
|
||||||
|
params: Record<string, string>,
|
||||||
|
): Promise<API.ApiResult<Record<string, string>>> {
|
||||||
|
return request<API.ApiResult<Record<string, string>>>(`/api/v1/app/group/update`, {
|
||||||
|
method: 'PUT',
|
||||||
|
data: params,
|
||||||
|
requestType: 'json',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove App Group
|
||||||
|
*/
|
||||||
|
export async function removeAppGroup(id: string): Promise<API.ApiResult<boolean>> {
|
||||||
|
return request<API.ApiResult<boolean>>(`/api/v1/app/group/delete/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
}
|
|
@ -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 { getAppList } from '@/services/app';
|
import { getAllAppGroupList, getAppList } from '@/services/app';
|
||||||
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
||||||
import type { ActionType } from '@ant-design/pro-components';
|
import type { ActionType } from '@ant-design/pro-components';
|
||||||
import { PageContainer, ProList } from '@ant-design/pro-components';
|
import { PageContainer, ProList } from '@ant-design/pro-components';
|
||||||
|
@ -86,7 +86,7 @@ export default () => {
|
||||||
key={'create'}
|
key={'create'}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
history.push('/app/create');
|
history.push('/app/list/create');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
|
@ -102,7 +102,7 @@ export default () => {
|
||||||
<span
|
<span
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
history.push(
|
history.push(
|
||||||
`/app/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
|
`/app/list/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -177,7 +177,7 @@ export default () => {
|
||||||
key="config"
|
key="config"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
history.push(
|
history.push(
|
||||||
`/app/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
|
`/app/list/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -213,6 +213,25 @@ export default () => {
|
||||||
</Popconfirm>,
|
</Popconfirm>,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
status: {
|
||||||
|
// 自己扩展的字段,主要用于筛选,不在列表中显示
|
||||||
|
title: intl.formatMessage({
|
||||||
|
id: 'pages.app.list.metas.group',
|
||||||
|
}),
|
||||||
|
valueType: 'select',
|
||||||
|
fieldProps: {
|
||||||
|
mode: 'multiple',
|
||||||
|
},
|
||||||
|
request: async () => {
|
||||||
|
const { success, data } = await getAllAppGroupList({}, {}, {});
|
||||||
|
if (success && data) {
|
||||||
|
return data.map((i) => {
|
||||||
|
return { label: i.name, value: i.id };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
|
|
|
@ -21,6 +21,7 @@ export default {
|
||||||
'pages.app.list.title': '应用列表',
|
'pages.app.list.title': '应用列表',
|
||||||
'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.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': '您确定要删除此应用?',
|
||||||
|
|
|
@ -83,9 +83,9 @@ export default (props: CreateDrawerProps) => {
|
||||||
{...DRAWER_FORM_ITEM_LAYOUT}
|
{...DRAWER_FORM_ITEM_LAYOUT}
|
||||||
onFinish={async (values: Record<string, string>) => {
|
onFinish={async (values: Record<string, string>) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await onFinish(values);
|
await onFinish(values).finally(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return !!result;
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
|
|
|
@ -74,9 +74,9 @@ export default (props: CreateDrawerProps) => {
|
||||||
scrollToFirstError
|
scrollToFirstError
|
||||||
onFinish={async (values: Record<string, string>) => {
|
onFinish={async (values: Record<string, string>) => {
|
||||||
setUpdateLoading(true);
|
setUpdateLoading(true);
|
||||||
const result = await onFinish(values);
|
await onFinish(values).finally(() => {
|
||||||
setUpdateLoading(false);
|
setUpdateLoading(false);
|
||||||
return !!result;
|
});
|
||||||
}}
|
}}
|
||||||
open={visible}
|
open={visible}
|
||||||
>
|
>
|
||||||
|
|
|
@ -16,13 +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/>.
|
||||||
*/
|
*/
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { BasicSettingConfig, SecurityDefensePolicyConfig } from './data.d';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取基础配置
|
* 获取基础配置
|
||||||
*/
|
*/
|
||||||
export async function getBasicSettingConfig(): Promise<
|
export async function getBasicSettingConfig(): Promise<API.ApiResult<BasicSettingConfig>> {
|
||||||
API.ApiResult<SettingAPI.AdvancedSettingConfig>
|
|
||||||
> {
|
|
||||||
return request('/api/v1/setting/security/basic/config');
|
return request('/api/v1/setting/security/basic/config');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +29,7 @@ export async function getBasicSettingConfig(): Promise<
|
||||||
* 保存基础配置
|
* 保存基础配置
|
||||||
*/
|
*/
|
||||||
export async function saveBasicSettingConfig(
|
export async function saveBasicSettingConfig(
|
||||||
params: Record<string, any>,
|
params: BasicSettingConfig,
|
||||||
): Promise<API.ApiResult<boolean>> {
|
): Promise<API.ApiResult<boolean>> {
|
||||||
return request('/api/v1/setting/security/basic/save', {
|
return request('/api/v1/setting/security/basic/save', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -43,7 +42,7 @@ export async function saveBasicSettingConfig(
|
||||||
* 获取内容安全策略配置
|
* 获取内容安全策略配置
|
||||||
*/
|
*/
|
||||||
export async function getSecurityDefensePolicyConfig(): Promise<
|
export async function getSecurityDefensePolicyConfig(): Promise<
|
||||||
API.ApiResult<SettingAPI.SecurityDefensePolicyConfig>
|
API.ApiResult<SecurityDefensePolicyConfig>
|
||||||
> {
|
> {
|
||||||
return request('/api/v1/setting/security/defense_policy/config');
|
return request('/api/v1/setting/security/defense_policy/config');
|
||||||
}
|
}
|
||||||
|
@ -52,7 +51,7 @@ export async function getSecurityDefensePolicyConfig(): Promise<
|
||||||
* 保存内容安全策略配置
|
* 保存内容安全策略配置
|
||||||
*/
|
*/
|
||||||
export async function saveSecurityDefensePolicyConfig(
|
export async function saveSecurityDefensePolicyConfig(
|
||||||
params: Record<string, any>,
|
params: SecurityDefensePolicyConfig,
|
||||||
): Promise<API.ApiResult<boolean>> {
|
): Promise<API.ApiResult<boolean>> {
|
||||||
return request('/api/v1/setting/security/defense_policy/save', {
|
return request('/api/v1/setting/security/defense_policy/save', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { EmailTemplateList, GetEmailTemplate, SmsTemplateList } from './data.d';
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export async function getMailTemplateList(
|
export async function getMailTemplateList(
|
||||||
params?: Record<string, any>,
|
params: Record<string, any>,
|
||||||
): Promise<API.ApiResult<EmailTemplateList>> {
|
): Promise<API.ApiResult<EmailTemplateList>> {
|
||||||
return request('/api/v1/setting/mail_template/list', { params });
|
return request('/api/v1/setting/mail_template/list', { params });
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,16 +26,16 @@ export default () => {
|
||||||
<ProFormText
|
<ProFormText
|
||||||
name={['config', 'domain']}
|
name={['config', 'domain']}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: 'pages.setting.storage_provider.provider.tencent_cos.domain',
|
id: 'pages.setting.storage_provider.provider.aliyun_oss.domain',
|
||||||
})}
|
})}
|
||||||
placeholder={intl.formatMessage({
|
placeholder={intl.formatMessage({
|
||||||
id: 'pages.setting.storage_provider.provider.tencent_cos.domain.placeholder',
|
id: 'pages.setting.storage_provider.provider.aliyun_oss.domain.placeholder',
|
||||||
})}
|
})}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: intl.formatMessage({
|
message: intl.formatMessage({
|
||||||
id: 'pages.setting.storage_provider.provider.tencent_cos.domain.rule.0.message',
|
id: 'pages.setting.storage_provider.provider.aliyun_oss.domain.rule.0.message',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -128,8 +128,8 @@ export async function moveOrganization(
|
||||||
*/
|
*/
|
||||||
export async function getUserList(
|
export async function getUserList(
|
||||||
params: Record<string, any>,
|
params: Record<string, any>,
|
||||||
sort?: Record<string, SortOrder>,
|
sort: Record<string, SortOrder>,
|
||||||
filter?: Record<string, (string | number)[] | null>,
|
filter: Record<string, (string | number)[] | null>,
|
||||||
): Promise<RequestData<AccountAPI.ListUser>> {
|
): Promise<RequestData<AccountAPI.ListUser>> {
|
||||||
return request<API.ApiResult<AccountAPI.ListUser>>('/api/v1/user/list', {
|
return request<API.ApiResult<AccountAPI.ListUser>>('/api/v1/user/list', {
|
||||||
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
||||||
|
@ -170,8 +170,8 @@ export async function batchGetUser(ids: string[]): Promise<API.ApiResult<Account
|
||||||
*/
|
*/
|
||||||
export async function getLoginAuditList(
|
export async function getLoginAuditList(
|
||||||
params: Record<string, any>,
|
params: Record<string, any>,
|
||||||
sort?: Record<string, SortOrder>,
|
sort: Record<string, SortOrder>,
|
||||||
filter?: Record<string, (string | number)[] | null>,
|
filter: Record<string, (string | number)[] | null>,
|
||||||
): Promise<RequestData<AccountAPI.UserLoginAuditList>> {
|
): Promise<RequestData<AccountAPI.UserLoginAuditList>> {
|
||||||
return request<API.ApiResult<AccountAPI.UserLoginAuditList>>('/api/v1/user/login_audit/list', {
|
return request<API.ApiResult<AccountAPI.UserLoginAuditList>>('/api/v1/user/login_audit/list', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -353,8 +353,8 @@ export async function getUserListNotInGroup(
|
||||||
*/
|
*/
|
||||||
export async function getUserGroupList(
|
export async function getUserGroupList(
|
||||||
params: Record<string, any>,
|
params: Record<string, any>,
|
||||||
sort?: Record<string, SortOrder>,
|
sort: Record<string, SortOrder>,
|
||||||
filter?: Record<string, (string | number)[] | null>,
|
filter: Record<string, (string | number)[] | null>,
|
||||||
): Promise<RequestData<AccountAPI.ListUserGroup>> {
|
): Promise<RequestData<AccountAPI.ListUserGroup>> {
|
||||||
return request<API.ApiResult<AccountAPI.ListUserGroup>>('/api/v1/user_group/list', {
|
return request<API.ApiResult<AccountAPI.ListUserGroup>>('/api/v1/user_group/list', {
|
||||||
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
||||||
|
@ -433,8 +433,8 @@ export async function removeUserGroupMember(
|
||||||
*/
|
*/
|
||||||
export async function getUserGroupMemberList(
|
export async function getUserGroupMemberList(
|
||||||
params: Record<string, any>,
|
params: Record<string, any>,
|
||||||
sort?: Record<string, SortOrder>,
|
sort: Record<string, SortOrder>,
|
||||||
filter?: Record<string, (string | number)[] | null>,
|
filter: Record<string, (string | number)[] | null>,
|
||||||
): Promise<RequestData<AccountAPI.ListUser>> {
|
): Promise<RequestData<AccountAPI.ListUser>> {
|
||||||
return request<API.ApiResult<AccountAPI.ListUser>>(
|
return request<API.ApiResult<AccountAPI.ListUser>>(
|
||||||
`/api/v1/user_group/${params.id}/member_list`,
|
`/api/v1/user_group/${params.id}/member_list`,
|
||||||
|
|
|
@ -19,15 +19,14 @@ import { download, filterParamConverter, sortParamConverter } from '@/utils/util
|
||||||
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';
|
||||||
import type { UploadFile } from 'antd/es/upload/interface';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取应用列表
|
* 获取应用列表
|
||||||
*/
|
*/
|
||||||
export async function getAppList(
|
export async function getAppList(
|
||||||
params?: Record<string, any>,
|
params: Record<string, any>,
|
||||||
sort?: Record<string, SortOrder>,
|
sort: Record<string, SortOrder>,
|
||||||
filter?: Record<string, (string | number)[] | null>,
|
filter: Record<string, (string | number)[] | null>,
|
||||||
): Promise<RequestData<AppAPI.AppList>> {
|
): Promise<RequestData<AppAPI.AppList>> {
|
||||||
return request<API.ApiResult<AppAPI.AppList>>('/api/v1/app/list', {
|
return request<API.ApiResult<AppAPI.AppList>>('/api/v1/app/list', {
|
||||||
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
||||||
|
@ -192,32 +191,61 @@ export async function removeAppAccessPolicy(id: string): Promise<API.ApiResult<b
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* parse Saml2 MetadataUrl
|
* 获取应用分组列表
|
||||||
*/
|
*/
|
||||||
export async function parseSaml2MetadataUrl(
|
export async function getAppGroupList(
|
||||||
metadataUrl: string,
|
params: Record<string, any>,
|
||||||
): Promise<API.ApiResult<Record<string, any>>> {
|
sort: Record<string, SortOrder>,
|
||||||
return request(`/api/v1/app/saml2/parse/metadata_url`, {
|
filter: Record<string, (string | number)[] | null>,
|
||||||
method: 'POST',
|
): Promise<RequestData<AppAPI.AppGroupList>> {
|
||||||
params: { metadataUrl },
|
return request<API.ApiResult<AppAPI.AppGroupList>>('/api/v1/app/group/list', {
|
||||||
}).catch(({ response: { data } }) => {
|
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
||||||
return data;
|
}).then((result: API.ApiResult<AppAPI.AppGroupList>) => {
|
||||||
|
const data: RequestData<AppAPI.AppGroupList> = {
|
||||||
|
data: result?.result?.list ? result?.result?.list : [],
|
||||||
|
success: result?.success,
|
||||||
|
total: result?.result?.pagination ? result?.result?.pagination.total : 0,
|
||||||
|
totalPages: result?.result?.pagination?.totalPages,
|
||||||
|
};
|
||||||
|
return Promise.resolve(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function getAllAppGroupList(
|
||||||
* parse Saml2 MetadataFile
|
params: Record<string, any>,
|
||||||
*/
|
sort: Record<string, SortOrder>,
|
||||||
export async function parseSaml2MetadataFile(
|
filter: Record<string, (string | number)[] | null>,
|
||||||
file: UploadFile,
|
): Promise<RequestData<AppAPI.AppGroupList>> {
|
||||||
): Promise<API.ApiResult<Record<string, any>>> {
|
let pageSize = 100,
|
||||||
return request(`/api/v1/app/saml2/parse/metadata_file`, {
|
current = 1;
|
||||||
method: 'POST',
|
// 存储所有数据的数组
|
||||||
data: { file: file.originFileObj },
|
let result: RequestData<AppAPI.AppGroupList> = {
|
||||||
headers: {
|
data: [],
|
||||||
'content-type': 'multipart/form-data',
|
success: false,
|
||||||
},
|
total: undefined,
|
||||||
}).catch(({ response: { data } }) => {
|
};
|
||||||
return data;
|
|
||||||
});
|
while (true) {
|
||||||
|
// 调用分页接口
|
||||||
|
const { success, data, total } = await getAppGroupList(
|
||||||
|
{ current, pageSize, ...params },
|
||||||
|
sort,
|
||||||
|
filter,
|
||||||
|
);
|
||||||
|
if (success && data) {
|
||||||
|
// 如果当前页没有数据,表示已经加载完全部数据,退出循环
|
||||||
|
if (data?.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = { data: result.data?.concat(data), success: success, total: total };
|
||||||
|
// 增加当前页码
|
||||||
|
if (total && total <= pageSize * current) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
current = current + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -422,4 +422,13 @@ declare namespace AppAPI {
|
||||||
template: string;
|
template: string;
|
||||||
remark: string;
|
remark: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AppGroupList = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
code: string;
|
||||||
|
type: string;
|
||||||
|
appCount: string;
|
||||||
|
remark: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
import type { SortOrder } from 'antd/es/table/interface';
|
import type { SortOrder } from 'antd/es/table/interface';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import { parse } from 'querystring';
|
import { parse } from 'querystring';
|
||||||
import type { ReactText } from 'react';
|
|
||||||
import { history, matchPath } from '@umijs/max';
|
import { history, matchPath } from '@umijs/max';
|
||||||
import YAML from 'yaml';
|
import YAML from 'yaml';
|
||||||
import { PhoneNumber } from 'google-libphonenumber';
|
import { PhoneNumber } from 'google-libphonenumber';
|
||||||
|
@ -106,7 +105,7 @@ export const sortParamConverter = (value: Record<string, SortOrder> | undefined)
|
||||||
*
|
*
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
export const filterParamConverter = (value: Record<string, ReactText[] | null> | undefined) => {
|
export const filterParamConverter = (value: Record<string, (string | number)[] | null>) => {
|
||||||
const param: Record<string, any> = {};
|
const param: Record<string, any> = {};
|
||||||
if (value)
|
if (value)
|
||||||
Object.entries(value).forEach(([key], index) => {
|
Object.entries(value).forEach(([key], index) => {
|
||||||
|
|
|
@ -33,9 +33,9 @@ import cn.topiam.employee.audit.event.type.EventType;
|
||||||
import cn.topiam.employee.common.entity.setting.AdministratorEntity;
|
import cn.topiam.employee.common.entity.setting.AdministratorEntity;
|
||||||
import cn.topiam.employee.common.repository.setting.AdministratorRepository;
|
import cn.topiam.employee.common.repository.setting.AdministratorRepository;
|
||||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||||
import cn.topiam.employee.support.security.userdetails.UserDetails;
|
|
||||||
import cn.topiam.employee.support.security.userdetails.UserType;
|
import cn.topiam.employee.support.security.userdetails.UserType;
|
||||||
import static cn.topiam.employee.core.security.util.SecurityUtils.getFailureMessage;
|
import static cn.topiam.employee.core.security.util.SecurityUtils.getFailureMessage;
|
||||||
|
import static cn.topiam.employee.support.security.util.SecurityUtils.getPrincipal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 认证失败
|
* 认证失败
|
||||||
|
@ -59,13 +59,7 @@ public class ConsoleAuthenticationFailureEventListener implements
|
||||||
AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class);
|
AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class);
|
||||||
String content = getFailureMessage(event);
|
String content = getFailureMessage(event);
|
||||||
logger.error("认证失败 [{}]",content);
|
logger.error("认证失败 [{}]",content);
|
||||||
String principal = (String) event.getAuthentication().getPrincipal();
|
String principal = getPrincipal(event);
|
||||||
if (event.getAuthentication().getPrincipal() instanceof String){
|
|
||||||
principal = (String) event.getAuthentication().getPrincipal();
|
|
||||||
}
|
|
||||||
if (event.getAuthentication().getPrincipal() instanceof UserDetails || event.getAuthentication().getPrincipal() instanceof org.springframework.security.core.userdetails.UserDetails){
|
|
||||||
principal = ((UserDetails) event.getAuthentication().getPrincipal()).getUsername();
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(principal)){
|
if (StringUtils.isNotBlank(principal)){
|
||||||
Optional<AdministratorEntity> optional = getAdministratorRepository().findByUsername(principal);
|
Optional<AdministratorEntity> optional = getAdministratorRepository().findByUsername(principal);
|
||||||
if (optional.isEmpty()) {
|
if (optional.isEmpty()) {
|
||||||
|
|
|
@ -55,6 +55,12 @@ public class AppGroupListResult implements Serializable {
|
||||||
@Parameter(description = "分组编码")
|
@Parameter(description = "分组编码")
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用数量
|
||||||
|
*/
|
||||||
|
@Parameter(description = "应用数量")
|
||||||
|
private Integer appCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分组类型
|
* 分组类型
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -38,11 +38,11 @@ import cn.topiam.employee.common.enums.UserStatus;
|
||||||
import cn.topiam.employee.common.repository.account.UserRepository;
|
import cn.topiam.employee.common.repository.account.UserRepository;
|
||||||
import cn.topiam.employee.core.help.SettingHelp;
|
import cn.topiam.employee.core.help.SettingHelp;
|
||||||
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
import cn.topiam.employee.support.context.ApplicationContextHelp;
|
||||||
import cn.topiam.employee.support.security.userdetails.UserDetails;
|
|
||||||
import cn.topiam.employee.support.util.PhoneNumberUtils;
|
import cn.topiam.employee.support.util.PhoneNumberUtils;
|
||||||
import static cn.topiam.employee.core.help.SettingHelp.getLoginFailureDuration;
|
import static cn.topiam.employee.core.help.SettingHelp.getLoginFailureDuration;
|
||||||
import static cn.topiam.employee.core.security.util.SecurityUtils.getFailureMessage;
|
import static cn.topiam.employee.core.security.util.SecurityUtils.getFailureMessage;
|
||||||
import static cn.topiam.employee.support.security.userdetails.UserType.USER;
|
import static cn.topiam.employee.support.security.userdetails.UserType.USER;
|
||||||
|
import static cn.topiam.employee.support.security.util.SecurityUtils.getPrincipal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 认证失败
|
* 认证失败
|
||||||
|
@ -67,13 +67,7 @@ public class PortalAuthenticationFailureEventListener implements
|
||||||
AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class);
|
AuditEventPublish publish = ApplicationContextHelp.getBean(AuditEventPublish.class);
|
||||||
String content = getFailureMessage(event);
|
String content = getFailureMessage(event);
|
||||||
logger.error("认证失败", event.getException());
|
logger.error("认证失败", event.getException());
|
||||||
String principal = null;
|
String principal = getPrincipal(event);
|
||||||
if (event.getAuthentication().getPrincipal() instanceof String) {
|
|
||||||
principal = (String) event.getAuthentication().getPrincipal();
|
|
||||||
}
|
|
||||||
if (event.getAuthentication().getPrincipal() instanceof UserDetails || event.getAuthentication().getPrincipal() instanceof org.springframework.security.core.userdetails.UserDetails) {
|
|
||||||
principal = ((UserDetails) event.getAuthentication().getPrincipal()).getUsername();
|
|
||||||
}
|
|
||||||
if (StringUtils.isNotBlank(principal)) {
|
if (StringUtils.isNotBlank(principal)) {
|
||||||
UserEntity user = getUserRepository().findByUsername(principal);
|
UserEntity user = getUserRepository().findByUsername(principal);
|
||||||
if (ObjectUtils.isEmpty(user)) {
|
if (ObjectUtils.isEmpty(user)) {
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
"@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.18",
|
||||||
"ahooks": "^3.7.8",
|
"ahooks": "^3.7.8",
|
||||||
"antd": "^5.8.6",
|
"antd": "^5.9.0",
|
||||||
"antd-img-crop": "^4.12.2",
|
"antd-img-crop": "^4.12.2",
|
||||||
"antd-style": "^3.4.5",
|
"antd-style": "^3.4.5",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
"@umijs/max": "^4.0.80",
|
"@umijs/max": "^4.0.80",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cross-port-killer": "^1.4.0",
|
"cross-port-killer": "^1.4.0",
|
||||||
"eslint": "^8.48.0",
|
"eslint": "^8.49.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^14.0.1",
|
"lint-staged": "^14.0.1",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
|
|
|
@ -19,15 +19,15 @@ 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';
|
||||||
import type { AppList } from './data.d';
|
import { AppGroupList, AppList } from './data.d';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取应用列表
|
* 获取应用列表
|
||||||
*/
|
*/
|
||||||
export async function queryAppList(
|
export async function queryAppList(
|
||||||
params?: Record<string, any>,
|
params: Record<string, any>,
|
||||||
sort?: Record<string, SortOrder>,
|
sort: Record<string, SortOrder>,
|
||||||
filter?: Record<string, (string | number)[] | null>,
|
filter: Record<string, (string | number)[] | null>,
|
||||||
): Promise<RequestData<AppList>> {
|
): Promise<RequestData<AppList>> {
|
||||||
const { result, success } = await request<API.ApiResult<AppList>>('/api/v1/app/list', {
|
const { result, success } = await request<API.ApiResult<AppList>>('/api/v1/app/list', {
|
||||||
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
params: { ...params, ...sortParamConverter(sort), ...filterParamConverter(filter) },
|
||||||
|
@ -38,3 +38,10 @@ export async function queryAppList(
|
||||||
total: result?.pagination ? result?.pagination.total : 0,
|
total: result?.pagination ? result?.pagination.total : 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取应用分组
|
||||||
|
*/
|
||||||
|
export async function getAppGroupList(): Promise<API.ApiResult<AppGroupList>> {
|
||||||
|
return request<API.ApiResult<any>>('/api/v1/app/group_list', {});
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
import { getEncryptSecret } from '@/services';
|
import { getEncryptSecret } from '@/services';
|
||||||
import type { SortOrder } from 'antd/es/table/interface';
|
import type { SortOrder } from 'antd/es/table/interface';
|
||||||
import { parse } from 'querystring';
|
import { parse } from 'querystring';
|
||||||
import type { ReactText } from 'react';
|
|
||||||
import { history, matchPath } from '@umijs/max';
|
import { history, matchPath } from '@umijs/max';
|
||||||
import { PhoneNumber } from 'google-libphonenumber';
|
import { PhoneNumber } from 'google-libphonenumber';
|
||||||
|
|
||||||
|
@ -94,7 +93,7 @@ export const sortParamConverter = (value: Record<string, SortOrder> | undefined)
|
||||||
*
|
*
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
export const filterParamConverter = (value: Record<string, ReactText[] | null> | undefined) => {
|
export const filterParamConverter = (value: Record<string, (string | number)[] | null>) => {
|
||||||
const param: Record<string, any> = {};
|
const param: Record<string, any> = {};
|
||||||
if (value)
|
if (value)
|
||||||
Object.entries(value).forEach(([key], index) => {
|
Object.entries(value).forEach(([key], index) => {
|
||||||
|
|
Loading…
Reference in New Issue