Skip to content

Commit 12689e3

Browse files
committed
feat: 完善应用管理
1 parent 80e2745 commit 12689e3

File tree

23 files changed

+1131
-1
lines changed

23 files changed

+1131
-1
lines changed

config/routes.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,25 @@ export default [
5656
},
5757
],
5858
},
59+
{
60+
name: 'app-management',
61+
path: '/app-management',
62+
icon: 'RobotOutlined',
63+
routes: [
64+
{ path: '/app-management', redirect: '/app-management/app' },
65+
{
66+
path: '/app-management/app',
67+
name: 'app.list',
68+
component: './Application',
69+
},
70+
{
71+
path: '/app-management/app/edit/:instanceId',
72+
name: 'app.edit',
73+
component: './Application/Edit',
74+
hideInMenu: true,
75+
},
76+
],
77+
},
5978
{
6079
name: 'authn',
6180
path: '/authn',

src/enums/appEnum.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ export enum PolicyType {
1919
SYSTEM = 'CUSTOM',
2020
DEFAULT = 'DEFAULT',
2121
}
22+
23+
export enum AppType {
24+
WEB = 'WEB',
25+
API = 'API',
26+
MOBILE = 'MOBILE',
27+
}

src/locales/zh-CN/menu.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export default {
3030
'menu.org-management.user.list': '用户管理',
3131
'menu.org-management.user.edit': '编辑用户',
3232

33+
'menu.app-management': '应用',
34+
'menu.app-management.app.list': '应用中心',
35+
'menu.app-management.app.create': '创建应用',
36+
'menu.app-management.app.edit': '编辑应用',
37+
3338
'menu.authn': '身份认证',
3439
'menu.authn.identity-source.social.list': '社会化认证源',
3540
'menu.authn.identity-source.social.create': '创建社会化认证源',
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { App, Form } from 'antd';
2+
import { BASIC_INTL } from '@/constant';
3+
import { useEffect, useRef } from 'react';
4+
import { ProFormInstance } from '@ant-design/pro-components';
5+
import { useIntl, useParams, useRequest } from '@umijs/max';
6+
7+
import {
8+
getApplication,
9+
updateApplication,
10+
UpdateApplicationRequest,
11+
} from '@/services/application';
12+
13+
export default function useAppHook() {
14+
const intl = useIntl();
15+
const { message } = App.useApp();
16+
const { instanceId } = useParams();
17+
const formRef = useRef<ProFormInstance<UpdateApplicationRequest>>();
18+
const name = Form.useWatch('name', formRef.current);
19+
20+
const setFormFieldsValue = (appInfo: App.Application) => {
21+
formRef.current?.setFieldsValue(appInfo);
22+
};
23+
24+
const {
25+
run: doGetAppInfo,
26+
data: appInfo,
27+
loading: getInfoLoading,
28+
} = useRequest(getApplication, {
29+
manual: true,
30+
loadingDelay: 600,
31+
formatResult: (appInfo) => appInfo,
32+
onSuccess: (appInfo) => {
33+
setFormFieldsValue(appInfo);
34+
},
35+
});
36+
37+
useEffect(() => {
38+
if (instanceId) {
39+
doGetAppInfo(instanceId);
40+
}
41+
}, []);
42+
43+
const { run: doUpdateApp, loading: updateAppLoading } = useRequest(updateApplication, {
44+
manual: true,
45+
onSuccess: () => {
46+
message.success(intl.formatMessage(BASIC_INTL.UPDATE_SUCCESS));
47+
},
48+
});
49+
50+
const handleReset = () => {
51+
if (appInfo) {
52+
setFormFieldsValue(appInfo);
53+
}
54+
};
55+
56+
const logoColorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
57+
58+
const getLogoColor = (name?: string, avatar?: string) => {
59+
if (avatar) {
60+
return '';
61+
}
62+
if (!name) {
63+
return logoColorList[0];
64+
}
65+
return logoColorList[name.length % logoColorList.length];
66+
};
67+
68+
const handleUpdateApp = async (record: UpdateApplicationRequest) => {
69+
if (instanceId) {
70+
return doUpdateApp(instanceId, record);
71+
}
72+
};
73+
74+
const states = {
75+
name,
76+
appInfo,
77+
formRef,
78+
instanceId,
79+
getInfoLoading,
80+
updateAppLoading,
81+
};
82+
83+
const actions = {
84+
handleUpdateApp,
85+
handleReset,
86+
getLogoColor,
87+
};
88+
89+
return {
90+
states,
91+
actions,
92+
} as const;
93+
}

src/pages/Application/Edit/components/BasicInfo/AppUsers.tsx

Whitespace-only changes.
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
import React, { useEffect, useState } from 'react';
2+
import classNames from 'classnames';
3+
import { useIntl } from '@umijs/max';
4+
import { Avatar, Skeleton, Upload, App, Row, Col, Form } from 'antd';
5+
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
6+
import {
7+
ProCard,
8+
ProDescriptions,
9+
ProForm,
10+
ProFormItem,
11+
ProFormText,
12+
ProFormTextArea,
13+
} from '@ant-design/pro-components';
14+
import { uploadFile } from '@/services/system/upload';
15+
16+
import useStyle from './style';
17+
import { omit } from 'lodash';
18+
import { UpdateApplicationRequest } from '@/services/application';
19+
20+
export type Props = {
21+
appInfo?: App.Application;
22+
appInfoLoaded?: boolean;
23+
onUpdate: (record: UpdateApplicationRequest) => Promise<boolean>;
24+
getLogoColor: (name?: string, avatar?: string) => string;
25+
};
26+
27+
const INTL = {
28+
BASIC_TITLE: {
29+
id: 'app.form.basicInfo.title',
30+
},
31+
NAME: {
32+
id: 'app.form.name',
33+
},
34+
LOGO: {
35+
id: 'app.form.logo',
36+
},
37+
DISPLAY_NAME: {
38+
id: 'app.form.displayName',
39+
},
40+
TYPE: {
41+
id: 'app.form.type',
42+
},
43+
DESCRIPTION: {
44+
id: 'app.form.description',
45+
},
46+
HOMEPAGE_URL: {
47+
id: 'app.form.homepageUrl',
48+
},
49+
LOGO_FILE_TYPE: {
50+
id: 'app.form.logo.fileType',
51+
},
52+
APP_ID: {
53+
id: 'app.form.appID',
54+
},
55+
APP_SECRET: {
56+
id: 'app.form.appSecret',
57+
},
58+
ENDPOINTS: {
59+
id: 'app.form.endpoints',
60+
},
61+
};
62+
63+
const prefixCls = 'app-basic-info';
64+
65+
const Index: React.FC<Props> = (props) => {
66+
const intl = useIntl();
67+
const useApp = App.useApp();
68+
const [form] = Form.useForm<UpdateApplicationRequest>();
69+
const { styles } = useStyle(prefixCls);
70+
const [uploadLoading, setUploadLoading] = useState(false);
71+
const [uploadIconUrl, setUploadIconUrl] = useState<string>();
72+
73+
const { appInfo, appInfoLoaded = false, onUpdate, getLogoColor } = props;
74+
75+
useEffect(() => {
76+
if (appInfo) {
77+
form.setFieldsValue(appInfo);
78+
}
79+
}, [appInfo]);
80+
81+
return (
82+
<ProCard layout="default" direction="column" className={styles.main}>
83+
<Skeleton loading={appInfoLoaded} active paragraph={{ rows: 5 }}>
84+
<ProForm<UpdateApplicationRequest>
85+
form={form}
86+
onFinish={onUpdate}
87+
colon={false}
88+
layout="vertical"
89+
style={{ marginBottom: 32 }}
90+
>
91+
<div className={`${prefixCls}-title`}>{intl.formatMessage(INTL.BASIC_TITLE)}</div>
92+
<Row gutter={0}>
93+
<Col flex="45%">
94+
<ProFormText
95+
width="xl"
96+
name="displayName"
97+
label={intl.formatMessage(INTL.DISPLAY_NAME)}
98+
/>
99+
<ProFormText
100+
width="xl"
101+
name="homepageUrl"
102+
label={intl.formatMessage(INTL.HOMEPAGE_URL)}
103+
/>
104+
<ProFormTextArea
105+
width="xl"
106+
name="description"
107+
label={intl.formatMessage(INTL.DESCRIPTION)}
108+
/>
109+
</Col>
110+
<Col flex="auto">
111+
<ProForm.Group align="start">
112+
<ProFormItem label={intl.formatMessage(INTL.LOGO)}>
113+
{appInfo ? (
114+
<>
115+
<Avatar
116+
shape="square"
117+
size={102}
118+
src={appInfo && appInfo.logo ? appInfo.logo : null}
119+
style={{
120+
backgroundColor: getLogoColor(appInfo?.metadata.name, appInfo?.logo),
121+
}}
122+
>
123+
{appInfo && appInfo.metadata.name
124+
? appInfo.metadata.name.substring(0, 5)
125+
: ''}
126+
</Avatar>
127+
<div style={{ color: 'rgba(0, 0, 0, 0.45)', marginTop: '8px' }}>
128+
<span>{intl.formatMessage(INTL.LOGO_FILE_TYPE)}</span>
129+
</div>{' '}
130+
</>
131+
) : (
132+
<div>
133+
<Upload
134+
listType="picture-card"
135+
maxCount={1}
136+
name="file"
137+
className="avatar-uploader"
138+
showUploadList={false}
139+
accept="image/png, image/jpeg, image/jpg"
140+
customRequest={async (files) => {
141+
setUploadLoading(true);
142+
if (!files.file) {
143+
return;
144+
}
145+
const { success, result, message } = await uploadFile(files.file).finally(
146+
() => {
147+
setUploadLoading(false);
148+
},
149+
);
150+
if (success && result) {
151+
setUploadIconUrl(result);
152+
return;
153+
}
154+
useApp.message.error(message);
155+
}}
156+
>
157+
{uploadIconUrl ? (
158+
<img src={uploadIconUrl} alt="avatar" style={{ width: '100%' }} />
159+
) : (
160+
<div>
161+
{uploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
162+
<div style={{ marginTop: 8 }}>上传</div>
163+
</div>
164+
)}
165+
</Upload>
166+
<div style={{ color: 'rgba(0, 0, 0, 0.45)' }}>
167+
<span>{intl.formatMessage(INTL.LOGO_FILE_TYPE)}</span>
168+
</div>
169+
</div>
170+
)}
171+
</ProFormItem>
172+
</ProForm.Group>
173+
</Col>
174+
</Row>
175+
</ProForm>
176+
177+
<div className={classNames(`${prefixCls}-descriptions`)}>
178+
<ProDescriptions<App.Application>
179+
column={2}
180+
title={intl.formatMessage(INTL.ENDPOINTS)}
181+
dataSource={omit(appInfo, 'config')}
182+
style={{ width: '80%', marginBottom: 32 }}
183+
>
184+
<ProDescriptions.Item
185+
dataIndex="appId"
186+
label={intl.formatMessage(INTL.APP_ID)}
187+
copyable
188+
editable={false}
189+
/>
190+
<ProDescriptions.Item
191+
dataIndex="appSecret"
192+
label={intl.formatMessage(INTL.APP_SECRET)}
193+
copyable
194+
valueType="password"
195+
editable={false}
196+
/>
197+
</ProDescriptions>
198+
</div>
199+
</Skeleton>
200+
</ProCard>
201+
);
202+
};
203+
204+
export default Index;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { createStyles } from 'antd-style';
2+
3+
const useStyle = createStyles(({ prefixCls, token }, props) => {
4+
const antCls = `.${prefixCls}`;
5+
const prefix = `${props}`;
6+
const prefixClassName = `.${prefix}`;
7+
return {
8+
main: {
9+
[`${prefixClassName}`]: {
10+
['&-title']: {
11+
marginBlockEnd: 32,
12+
marginBottom: 20,
13+
fontWeight: '600',
14+
fontSize: token.fontSizeHeading5,
15+
lineHeight: 1.5,
16+
},
17+
['&-upload-picture-circle-wrapper']: {
18+
[`${antCls}-upload`]: {
19+
width: 160,
20+
height: 160,
21+
},
22+
},
23+
},
24+
},
25+
};
26+
});
27+
export default useStyle;

0 commit comments

Comments
 (0)