mirror of https://gitee.com/topiam/eiam
Merge remote-tracking branch 'origin/master'
commit
b0bc28c2d2
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package cn.topiam.employee.application;
|
package cn.topiam.employee.application;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -86,6 +87,19 @@ public interface ApplicationService {
|
||||||
*/
|
*/
|
||||||
String getBase64Icon();
|
String getBase64Icon();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建应用
|
||||||
|
*
|
||||||
|
* @param name {@link String} 名称
|
||||||
|
* @param icon {@link String} 图标
|
||||||
|
* @param remark {@link String} 备注
|
||||||
|
* @return {@link Long} 应用ID
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
default String create(String name, String icon, String remark) {
|
||||||
|
return create(name, icon, remark, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建应用
|
* 创建应用
|
||||||
*
|
*
|
||||||
|
|
|
@ -119,7 +119,7 @@ public class AppRepositoryCustomizedImpl implements AppRepositoryCustomized {
|
||||||
*/
|
*/
|
||||||
public Page<AppEntity> getAppList(AppQuery appQuery, Pageable pageable) {
|
public Page<AppEntity> getAppList(AppQuery appQuery, Pageable pageable) {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
StringBuilder builder = new StringBuilder("SELECT DISTINCT app.* FROM app INNER JOIN app_group_association `group` ON app.id_ = `group`.app_id AND app.is_deleted = 0");
|
StringBuilder builder = new StringBuilder("SELECT DISTINCT app.* FROM app LEFT JOIN app_group_association `group` ON app.id_ = `group`.app_id AND app.is_deleted = 0");
|
||||||
|
|
||||||
//应用名称
|
//应用名称
|
||||||
if (StringUtils.isNoneBlank(appQuery.getName())) {
|
if (StringUtils.isNoneBlank(appQuery.getName())) {
|
||||||
|
|
|
@ -361,7 +361,7 @@ const IdentityProvider = () => {
|
||||||
const { styles } = useStyle(prefixCls);
|
const { styles } = useStyle(prefixCls);
|
||||||
|
|
||||||
useMount(async () => {
|
useMount(async () => {
|
||||||
if (!type || !(type.toUpperCase() in IdentityProviderCategory)) {
|
if (!type || !IdentityProviderCategory[type]) {
|
||||||
setTabActiveKey(IdentityProviderCategory.social);
|
setTabActiveKey(IdentityProviderCategory.social);
|
||||||
history.push({
|
history.push({
|
||||||
pathname: location.pathname,
|
pathname: location.pathname,
|
||||||
|
|
|
@ -17,10 +17,7 @@
|
||||||
*/
|
*/
|
||||||
package cn.topiam.employee.console.service.app.impl;
|
package cn.topiam.employee.console.service.app.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import cn.topiam.employee.common.entity.app.AppGroupAssociationEntity;
|
import cn.topiam.employee.common.entity.app.AppGroupAssociationEntity;
|
||||||
import cn.topiam.employee.common.repository.app.AppGroupAssociationRepository;
|
import cn.topiam.employee.common.repository.app.AppGroupAssociationRepository;
|
||||||
|
@ -52,6 +49,7 @@ import cn.topiam.employee.support.util.BeanUtils;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_BY;
|
import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_BY;
|
||||||
import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_TIME;
|
import static cn.topiam.employee.support.repository.domain.BaseEntity.LAST_MODIFIED_TIME;
|
||||||
|
@ -78,7 +76,7 @@ public class AppServiceImpl implements AppService {
|
||||||
public Page<AppListResult> getAppList(PageModel pageModel, AppQuery query) {
|
public Page<AppListResult> getAppList(PageModel pageModel, AppQuery query) {
|
||||||
//查询映射
|
//查询映射
|
||||||
org.springframework.data.domain.Page<AppEntity> list = appRepository.getAppList(query,
|
org.springframework.data.domain.Page<AppEntity> list = appRepository.getAppList(query,
|
||||||
PageRequest.of(pageModel.getCurrent(), pageModel.getPageSize()));
|
PageRequest.of(pageModel.getCurrent(), pageModel.getPageSize()));
|
||||||
return appConverter.entityConvertToAppListResult(list);
|
return appConverter.entityConvertToAppListResult(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,9 +90,14 @@ public class AppServiceImpl implements AppService {
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public AppCreateResult createApp(AppCreateParam param) {
|
public AppCreateResult createApp(AppCreateParam param) {
|
||||||
ApplicationService applicationService = applicationServiceLoader
|
ApplicationService applicationService = applicationServiceLoader
|
||||||
.getApplicationService(param.getTemplate());
|
.getApplicationService(param.getTemplate());
|
||||||
String appId = applicationService.create(param.getName(), param.getIcon(),
|
String appId;
|
||||||
param.getRemark(), param.getGroupIds());
|
if (!CollectionUtils.isEmpty(param.getGroupIds())) {
|
||||||
|
appId = applicationService.create(param.getName(), param.getIcon(), param.getRemark(),
|
||||||
|
param.getGroupIds());
|
||||||
|
} else {
|
||||||
|
appId = applicationService.create(param.getName(), param.getIcon(), param.getRemark());
|
||||||
|
}
|
||||||
AuditContext.setTarget(Target.builder().id(appId).type(TargetType.APPLICATION).build());
|
AuditContext.setTarget(Target.builder().id(appId).type(TargetType.APPLICATION).build());
|
||||||
return new AppCreateResult(appId);
|
return new AppCreateResult(appId);
|
||||||
}
|
}
|
||||||
|
@ -122,7 +125,7 @@ public class AppServiceImpl implements AppService {
|
||||||
}
|
}
|
||||||
appGroupAssociationRepository.saveAll(list);
|
appGroupAssociationRepository.saveAll(list);
|
||||||
AuditContext.setTarget(
|
AuditContext.setTarget(
|
||||||
Target.builder().id(param.getId().toString()).type(TargetType.APPLICATION).build());
|
Target.builder().id(param.getId().toString()).type(TargetType.APPLICATION).build());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +142,7 @@ public class AppServiceImpl implements AppService {
|
||||||
applicationServiceLoader.getApplicationService(app.getTemplate()).delete(id.toString());
|
applicationServiceLoader.getApplicationService(app.getTemplate()).delete(id.toString());
|
||||||
appGroupAssociationRepository.deleteAllByAppId(id);
|
appGroupAssociationRepository.deleteAllByAppId(id);
|
||||||
AuditContext
|
AuditContext
|
||||||
.setTarget(Target.builder().id(id.toString()).type(TargetType.APPLICATION).build());
|
.setTarget(Target.builder().id(id.toString()).type(TargetType.APPLICATION).build());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,10 +201,10 @@ public class AppServiceImpl implements AppService {
|
||||||
@Override
|
@Override
|
||||||
public Boolean saveAppConfig(AppSaveConfigParam param) {
|
public Boolean saveAppConfig(AppSaveConfigParam param) {
|
||||||
ApplicationService applicationService = applicationServiceLoader
|
ApplicationService applicationService = applicationServiceLoader
|
||||||
.getApplicationService(param.getTemplate());
|
.getApplicationService(param.getTemplate());
|
||||||
applicationService.saveConfig(param.getId(), param.getConfig());
|
applicationService.saveConfig(param.getId(), param.getConfig());
|
||||||
AuditContext
|
AuditContext
|
||||||
.setTarget(Target.builder().id(param.getId()).type(TargetType.APPLICATION).build());
|
.setTarget(Target.builder().id(param.getId()).type(TargetType.APPLICATION).build());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +219,7 @@ public class AppServiceImpl implements AppService {
|
||||||
Optional<AppEntity> optional = appRepository.findById(Long.valueOf(appId));
|
Optional<AppEntity> optional = appRepository.findById(Long.valueOf(appId));
|
||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
ApplicationService applicationService = applicationServiceLoader
|
ApplicationService applicationService = applicationServiceLoader
|
||||||
.getApplicationService(optional.get().getTemplate());
|
.getApplicationService(optional.get().getTemplate());
|
||||||
return applicationService.getConfig(appId);
|
return applicationService.getConfig(appId);
|
||||||
}
|
}
|
||||||
throw new AppNotExistException();
|
throw new AppNotExistException();
|
||||||
|
@ -241,17 +244,17 @@ public class AppServiceImpl implements AppService {
|
||||||
/**
|
/**
|
||||||
* ApplicationTemplateLoader
|
* ApplicationTemplateLoader
|
||||||
*/
|
*/
|
||||||
private final ApplicationServiceLoader applicationServiceLoader;
|
private final ApplicationServiceLoader applicationServiceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ApplicationRepository
|
* ApplicationRepository
|
||||||
*/
|
*/
|
||||||
private final AppRepository appRepository;
|
private final AppRepository appRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ApplicationConverter
|
* ApplicationConverter
|
||||||
*/
|
*/
|
||||||
private final AppConverter appConverter;
|
private final AppConverter appConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AppGroupAssociationRepositorys
|
* AppGroupAssociationRepositorys
|
||||||
|
|
|
@ -16,40 +16,50 @@
|
||||||
* 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 type { ActionType } from '@ant-design/pro-components';
|
import type { ActionType } from '@ant-design/pro-components';
|
||||||
import { PageContainer, ProCard, ProList } from '@ant-design/pro-components';
|
import {
|
||||||
import { Alert, App, Avatar, Badge, Card, Input, Typography } from 'antd';
|
PageContainer,
|
||||||
|
ProCard,
|
||||||
|
ProFormText,
|
||||||
|
ProList,
|
||||||
|
QueryFilter,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { App, Avatar, Badge, Card, Typography } from 'antd';
|
||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import type { AppList } from './data.d';
|
import { AppList, InitLoginType } from './data.d';
|
||||||
import { InitLoginType } from './data.d';
|
import { getAppGroupList, queryAppList } from './service';
|
||||||
import { queryAppList } from './service';
|
|
||||||
import { useIntl } from '@@/exports';
|
import { useIntl } from '@@/exports';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { useAsyncEffect } from 'ahooks';
|
||||||
|
import { SpinProps } from 'antd/es/spin';
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
const { Paragraph } = Typography;
|
||||||
const prefixCls = 'topiam-app-list';
|
const prefixCls = 'topiam-app-list';
|
||||||
|
const renderBadge = (count: number, active = false) => {
|
||||||
|
return (
|
||||||
|
<Badge
|
||||||
|
count={count}
|
||||||
|
style={{
|
||||||
|
marginBlockStart: -2,
|
||||||
|
marginInlineStart: 4,
|
||||||
|
color: active ? '#1890FF' : '#999',
|
||||||
|
backgroundColor: active ? '#E6F7FF' : '#eee',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const CardList = () => {
|
const CardList = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { styles } = useStyle(prefixCls);
|
const { styles } = useStyle(prefixCls);
|
||||||
const [activeKey, setActiveKey] = useState<React.Key | undefined>('tab1');
|
// 当前组
|
||||||
|
const [currentGroup, setCurrentGroup] = useState<React.Key>();
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const [searchParams, setSearchParams] = useState<{ name: string }>();
|
const [searchParams, setSearchParams] = useState<Record<string, any>>();
|
||||||
const content = (
|
const [loading, setLoading] = useState<boolean | SpinProps | undefined>(false);
|
||||||
<div style={{ textAlign: 'center' }}>
|
const [items, setItems] = useState<{ key: string; label: React.JSX.Element }[]>([]);
|
||||||
<Input.Search
|
|
||||||
placeholder={intl.formatMessage({ id: 'pages.application.search' })}
|
|
||||||
enterButton={intl.formatMessage({ id: 'pages.application.search.enter_button' })}
|
|
||||||
size="large"
|
|
||||||
style={{ maxWidth: 522, width: '100%' }}
|
|
||||||
onSearch={(value) => {
|
|
||||||
setSearchParams({ name: value });
|
|
||||||
actionRef.current?.reload();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
const initSso = (idpInitUrl: string) => {
|
const initSso = (idpInitUrl: string) => {
|
||||||
const div = window.document.createElement('div');
|
const div = window.document.createElement('div');
|
||||||
div.innerHTML =
|
div.innerHTML =
|
||||||
|
@ -64,42 +74,40 @@ const CardList = () => {
|
||||||
document.body.removeChild(div);
|
document.body.removeChild(div);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBadge = (count: number, active = false) => {
|
useAsyncEffect(async () => {
|
||||||
return (
|
setLoading(true);
|
||||||
<Badge
|
const { result, success } = await getAppGroupList().finally(() => {
|
||||||
count={count}
|
setLoading(false);
|
||||||
style={{
|
});
|
||||||
marginBlockStart: -2,
|
if (success && result) {
|
||||||
marginInlineStart: 4,
|
let data: { key: string; label: React.JSX.Element }[] = [];
|
||||||
color: active ? '#1890FF' : '#999',
|
result.forEach((item) => {
|
||||||
backgroundColor: active ? '#E6F7FF' : '#eee',
|
data.push({
|
||||||
}}
|
key: item.id,
|
||||||
/>
|
label: (
|
||||||
);
|
<span>
|
||||||
};
|
{item.name}
|
||||||
|
{renderBadge(item.appCount, currentGroup === item.id)}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setItems(data);
|
||||||
|
// 如果有分组,取第一个分组
|
||||||
|
if (data.length > 0) {
|
||||||
|
setSearchParams({ groupId: data[0].key });
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}
|
||||||
|
// 手动请求
|
||||||
|
else {
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles}>
|
<div className={styles}>
|
||||||
<PageContainer
|
<PageContainer className={classnames(`${prefixCls}`)}>
|
||||||
className={classnames(`${prefixCls}`)}
|
|
||||||
tabList={[
|
|
||||||
{
|
|
||||||
tab: '应用列表',
|
|
||||||
key: 'list',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tab: '应用账号',
|
|
||||||
key: 'account',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Alert
|
|
||||||
banner
|
|
||||||
type={'info'}
|
|
||||||
message={intl.formatMessage({ id: 'pages.application.alert' })}
|
|
||||||
showIcon
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<ProList<AppList>
|
<ProList<AppList>
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
split
|
split
|
||||||
|
@ -111,32 +119,34 @@ const CardList = () => {
|
||||||
xl: 4,
|
xl: 4,
|
||||||
xxl: 5,
|
xxl: 5,
|
||||||
}}
|
}}
|
||||||
request={queryAppList}
|
loading={loading}
|
||||||
pagination={{}}
|
onLoadingChange={(loading) => {
|
||||||
toolbar={{
|
setLoading(loading);
|
||||||
menu: {
|
|
||||||
type: 'tab',
|
|
||||||
activeKey,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
key: 'tab1',
|
|
||||||
label: <span>开发分组{renderBadge(99, activeKey === 'tab1')}</span>,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'tab2',
|
|
||||||
label: <span>运维分组{renderBadge(32, activeKey === 'tab2')}</span>,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
onChange(key) {
|
|
||||||
setActiveKey(key);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
|
manualRequest
|
||||||
|
request={queryAppList}
|
||||||
|
toolbar={
|
||||||
|
items.length > 0
|
||||||
|
? {
|
||||||
|
menu: {
|
||||||
|
type: 'tab',
|
||||||
|
activeKey: currentGroup,
|
||||||
|
items: items,
|
||||||
|
onChange(key) {
|
||||||
|
if (key) {
|
||||||
|
setCurrentGroup(key);
|
||||||
|
setSearchParams((values) => {
|
||||||
|
return { ...values, groupId: key };
|
||||||
|
});
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
params={searchParams}
|
params={searchParams}
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
tableExtraRender={() => {
|
|
||||||
return <ProCard>{content}</ProCard>;
|
|
||||||
}}
|
|
||||||
renderItem={(item: AppList) => {
|
renderItem={(item: AppList) => {
|
||||||
return (
|
return (
|
||||||
item &&
|
item &&
|
||||||
|
@ -147,8 +157,11 @@ const CardList = () => {
|
||||||
hoverable
|
hoverable
|
||||||
bordered={false}
|
bordered={false}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (item.initLoginType === InitLoginType.PORTAL_OR_APP_INIT_SSO) {
|
if (
|
||||||
initSso(item.initLoginUrl);
|
item.initLoginType === InitLoginType.portal_or_app_init_sso &&
|
||||||
|
item?.initLoginUrl
|
||||||
|
) {
|
||||||
|
initSso(item?.initLoginUrl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.warning(
|
message.warning(
|
||||||
|
@ -175,6 +188,33 @@ const CardList = () => {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
tableExtraRender={() => {
|
||||||
|
return (
|
||||||
|
<ProCard bodyStyle={{ padding: 0 }}>
|
||||||
|
<QueryFilter
|
||||||
|
layout="horizontal"
|
||||||
|
onFinish={(values) => {
|
||||||
|
setSearchParams({ ...searchParams, ...values });
|
||||||
|
actionRef.current?.reload();
|
||||||
|
return Promise.resolve();
|
||||||
|
}}
|
||||||
|
onReset={() => {
|
||||||
|
if (items.length > 0) {
|
||||||
|
setSearchParams({ groupId: currentGroup });
|
||||||
|
} else {
|
||||||
|
setSearchParams({});
|
||||||
|
}
|
||||||
|
actionRef.current?.reload();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProFormText
|
||||||
|
name="name"
|
||||||
|
label={intl.formatMessage({ id: 'pages.application.search.name' })}
|
||||||
|
/>
|
||||||
|
</QueryFilter>
|
||||||
|
</ProCard>
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,8 +22,8 @@ export type AppList = {
|
||||||
template: string;
|
template: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
name: string;
|
name: string;
|
||||||
initLoginType: InitLoginType;
|
initLoginType: InitLoginType | string;
|
||||||
initLoginUrl: string;
|
initLoginUrl?: string;
|
||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
* 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.application.search': '请输入',
|
'pages.application.search.name': '应用名称',
|
||||||
'pages.application.search.enter_button': '搜索',
|
'pages.application.tab.list': '应用列表',
|
||||||
'pages.application.alert': '请点击下方的应用进行单点登录。若希望修改应用内容,请联系管理员。',
|
'pages.application.tab.account': '应用账号',
|
||||||
'pages.application.init.warning': '仅允许应用发起',
|
'pages.application.init.warning': '仅允许应用发起',
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,6 @@ const useStyles = createStyles(({ token, css, prefixCls }, prop) => {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: ${token.borderRadius};
|
border-radius: ${token.borderRadius};
|
||||||
.${prefixCls}-pro-table-list-toolbar-container {
|
.${prefixCls}-pro-table-list-toolbar-container {
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-item-card {
|
&-item-card {
|
||||||
|
|
Loading…
Reference in New Issue