mirror of https://gitee.com/topiam/eiam
Merge remote-tracking branch 'origin/master'
commit
b0bc28c2d2
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
package cn.topiam.employee.application;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -86,6 +87,19 @@ public interface ApplicationService {
|
|||
*/
|
||||
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) {
|
||||
//@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())) {
|
||||
|
|
|
@ -361,7 +361,7 @@ const IdentityProvider = () => {
|
|||
const { styles } = useStyle(prefixCls);
|
||||
|
||||
useMount(async () => {
|
||||
if (!type || !(type.toUpperCase() in IdentityProviderCategory)) {
|
||||
if (!type || !IdentityProviderCategory[type]) {
|
||||
setTabActiveKey(IdentityProviderCategory.social);
|
||||
history.push({
|
||||
pathname: location.pathname,
|
||||
|
|
|
@ -17,10 +17,7 @@
|
|||
*/
|
||||
package cn.topiam.employee.console.service.app.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
|
||||
import cn.topiam.employee.common.entity.app.AppGroupAssociationEntity;
|
||||
import cn.topiam.employee.common.repository.app.AppGroupAssociationRepository;
|
||||
|
@ -52,6 +49,7 @@ import cn.topiam.employee.support.util.BeanUtils;
|
|||
|
||||
import lombok.AllArgsConstructor;
|
||||
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_TIME;
|
||||
|
@ -78,7 +76,7 @@ public class AppServiceImpl implements AppService {
|
|||
public Page<AppListResult> getAppList(PageModel pageModel, AppQuery 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);
|
||||
}
|
||||
|
||||
|
@ -92,9 +90,14 @@ public class AppServiceImpl implements AppService {
|
|||
@Transactional(rollbackFor = Exception.class)
|
||||
public AppCreateResult createApp(AppCreateParam param) {
|
||||
ApplicationService applicationService = applicationServiceLoader
|
||||
.getApplicationService(param.getTemplate());
|
||||
String appId = applicationService.create(param.getName(), param.getIcon(),
|
||||
param.getRemark(), param.getGroupIds());
|
||||
.getApplicationService(param.getTemplate());
|
||||
String appId;
|
||||
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());
|
||||
return new AppCreateResult(appId);
|
||||
}
|
||||
|
@ -122,7 +125,7 @@ public class AppServiceImpl implements AppService {
|
|||
}
|
||||
appGroupAssociationRepository.saveAll(list);
|
||||
AuditContext.setTarget(
|
||||
Target.builder().id(param.getId().toString()).type(TargetType.APPLICATION).build());
|
||||
Target.builder().id(param.getId().toString()).type(TargetType.APPLICATION).build());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -139,7 +142,7 @@ public class AppServiceImpl implements AppService {
|
|||
applicationServiceLoader.getApplicationService(app.getTemplate()).delete(id.toString());
|
||||
appGroupAssociationRepository.deleteAllByAppId(id);
|
||||
AuditContext
|
||||
.setTarget(Target.builder().id(id.toString()).type(TargetType.APPLICATION).build());
|
||||
.setTarget(Target.builder().id(id.toString()).type(TargetType.APPLICATION).build());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -198,10 +201,10 @@ public class AppServiceImpl implements AppService {
|
|||
@Override
|
||||
public Boolean saveAppConfig(AppSaveConfigParam param) {
|
||||
ApplicationService applicationService = applicationServiceLoader
|
||||
.getApplicationService(param.getTemplate());
|
||||
.getApplicationService(param.getTemplate());
|
||||
applicationService.saveConfig(param.getId(), param.getConfig());
|
||||
AuditContext
|
||||
.setTarget(Target.builder().id(param.getId()).type(TargetType.APPLICATION).build());
|
||||
.setTarget(Target.builder().id(param.getId()).type(TargetType.APPLICATION).build());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -216,7 +219,7 @@ public class AppServiceImpl implements AppService {
|
|||
Optional<AppEntity> optional = appRepository.findById(Long.valueOf(appId));
|
||||
if (optional.isPresent()) {
|
||||
ApplicationService applicationService = applicationServiceLoader
|
||||
.getApplicationService(optional.get().getTemplate());
|
||||
.getApplicationService(optional.get().getTemplate());
|
||||
return applicationService.getConfig(appId);
|
||||
}
|
||||
throw new AppNotExistException();
|
||||
|
@ -241,17 +244,17 @@ public class AppServiceImpl implements AppService {
|
|||
/**
|
||||
* ApplicationTemplateLoader
|
||||
*/
|
||||
private final ApplicationServiceLoader applicationServiceLoader;
|
||||
private final ApplicationServiceLoader applicationServiceLoader;
|
||||
|
||||
/**
|
||||
* ApplicationRepository
|
||||
*/
|
||||
private final AppRepository appRepository;
|
||||
private final AppRepository appRepository;
|
||||
|
||||
/**
|
||||
* ApplicationConverter
|
||||
*/
|
||||
private final AppConverter appConverter;
|
||||
private final AppConverter appConverter;
|
||||
|
||||
/**
|
||||
* AppGroupAssociationRepositorys
|
||||
|
|
|
@ -16,40 +16,50 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import type { ActionType } from '@ant-design/pro-components';
|
||||
import { PageContainer, ProCard, ProList } from '@ant-design/pro-components';
|
||||
import { Alert, App, Avatar, Badge, Card, Input, Typography } from 'antd';
|
||||
import {
|
||||
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 type { AppList } from './data.d';
|
||||
import { InitLoginType } from './data.d';
|
||||
import { queryAppList } from './service';
|
||||
import { AppList, InitLoginType } from './data.d';
|
||||
import { getAppGroupList, queryAppList } from './service';
|
||||
import { useIntl } from '@@/exports';
|
||||
import useStyle from './style';
|
||||
import classnames from 'classnames';
|
||||
import { useAsyncEffect } from 'ahooks';
|
||||
import { SpinProps } from 'antd/es/spin';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
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 intl = useIntl();
|
||||
const { styles } = useStyle(prefixCls);
|
||||
const [activeKey, setActiveKey] = useState<React.Key | undefined>('tab1');
|
||||
|
||||
// 当前组
|
||||
const [currentGroup, setCurrentGroup] = useState<React.Key>();
|
||||
const { message } = App.useApp();
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [searchParams, setSearchParams] = useState<{ name: string }>();
|
||||
const content = (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<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 [searchParams, setSearchParams] = useState<Record<string, any>>();
|
||||
const [loading, setLoading] = useState<boolean | SpinProps | undefined>(false);
|
||||
const [items, setItems] = useState<{ key: string; label: React.JSX.Element }[]>([]);
|
||||
|
||||
const initSso = (idpInitUrl: string) => {
|
||||
const div = window.document.createElement('div');
|
||||
div.innerHTML =
|
||||
|
@ -64,42 +74,40 @@ const CardList = () => {
|
|||
document.body.removeChild(div);
|
||||
};
|
||||
|
||||
const renderBadge = (count: number, active = false) => {
|
||||
return (
|
||||
<Badge
|
||||
count={count}
|
||||
style={{
|
||||
marginBlockStart: -2,
|
||||
marginInlineStart: 4,
|
||||
color: active ? '#1890FF' : '#999',
|
||||
backgroundColor: active ? '#E6F7FF' : '#eee',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
useAsyncEffect(async () => {
|
||||
setLoading(true);
|
||||
const { result, success } = await getAppGroupList().finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
if (success && result) {
|
||||
let data: { key: string; label: React.JSX.Element }[] = [];
|
||||
result.forEach((item) => {
|
||||
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 (
|
||||
<div className={styles}>
|
||||
<PageContainer
|
||||
className={classnames(`${prefixCls}`)}
|
||||
tabList={[
|
||||
{
|
||||
tab: '应用列表',
|
||||
key: 'list',
|
||||
},
|
||||
{
|
||||
tab: '应用账号',
|
||||
key: 'account',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Alert
|
||||
banner
|
||||
type={'info'}
|
||||
message={intl.formatMessage({ id: 'pages.application.alert' })}
|
||||
showIcon
|
||||
/>
|
||||
<br />
|
||||
<PageContainer className={classnames(`${prefixCls}`)}>
|
||||
<ProList<AppList>
|
||||
rowKey="id"
|
||||
split
|
||||
|
@ -111,32 +119,34 @@ const CardList = () => {
|
|||
xl: 4,
|
||||
xxl: 5,
|
||||
}}
|
||||
request={queryAppList}
|
||||
pagination={{}}
|
||||
toolbar={{
|
||||
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);
|
||||
},
|
||||
},
|
||||
loading={loading}
|
||||
onLoadingChange={(loading) => {
|
||||
setLoading(loading);
|
||||
}}
|
||||
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}
|
||||
actionRef={actionRef}
|
||||
tableExtraRender={() => {
|
||||
return <ProCard>{content}</ProCard>;
|
||||
}}
|
||||
renderItem={(item: AppList) => {
|
||||
return (
|
||||
item &&
|
||||
|
@ -147,8 +157,11 @@ const CardList = () => {
|
|||
hoverable
|
||||
bordered={false}
|
||||
onClick={async () => {
|
||||
if (item.initLoginType === InitLoginType.PORTAL_OR_APP_INIT_SSO) {
|
||||
initSso(item.initLoginUrl);
|
||||
if (
|
||||
item.initLoginType === InitLoginType.portal_or_app_init_sso &&
|
||||
item?.initLoginUrl
|
||||
) {
|
||||
initSso(item?.initLoginUrl);
|
||||
return;
|
||||
}
|
||||
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>
|
||||
</div>
|
||||
|
|
|
@ -22,8 +22,8 @@ export type AppList = {
|
|||
template: string;
|
||||
icon?: string;
|
||||
name: string;
|
||||
initLoginType: InitLoginType;
|
||||
initLoginUrl: string;
|
||||
initLoginType: InitLoginType | string;
|
||||
initLoginUrl?: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
export default {
|
||||
'pages.application.search': '请输入',
|
||||
'pages.application.search.enter_button': '搜索',
|
||||
'pages.application.alert': '请点击下方的应用进行单点登录。若希望修改应用内容,请联系管理员。',
|
||||
'pages.application.search.name': '应用名称',
|
||||
'pages.application.tab.list': '应用列表',
|
||||
'pages.application.tab.account': '应用账号',
|
||||
'pages.application.init.warning': '仅允许应用发起',
|
||||
};
|
||||
|
|
|
@ -24,7 +24,6 @@ const useStyles = createStyles(({ token, css, prefixCls }, prop) => {
|
|||
border: none;
|
||||
border-radius: ${token.borderRadius};
|
||||
.${prefixCls}-pro-table-list-toolbar-container {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&-item-card {
|
||||
|
|
Loading…
Reference in New Issue