This is an automated email from the ASF dual-hosted git repository.
ranke pushed a commit to branch dev-change-ui2antd
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git
The following commit(s) were added to refs/heads/dev-change-ui2antd by this
push:
new 75afb10 fix #713 (#718)
75afb10 is described below
commit 75afb10f8def320966a83f3a00a13298d572c621
Author: tzr164 <[email protected]>
AuthorDate: Mon Mar 29 18:27:23 2021 +0800
fix #713 (#718)
* # add http-status plugin.
# change logo and web title in defaultSettings.ts
# add routes
# implements logout
# change footer copyright
# add httprequest error handler(redirect to login page)
* # chang request url ${REACT_APP_ENV} to static dev string.
Co-authored-by: tangziran <823013778@qqcom>
---
dubbo-admin-ui-antd/config/defaultSettings.ts | 4 +-
dubbo-admin-ui-antd/config/routes.ts | 83 ++++++++++-----------
dubbo-admin-ui-antd/package.json | 3 +-
dubbo-admin-ui-antd/public/logo.png | Bin 0 -> 5564 bytes
dubbo-admin-ui-antd/src/app.tsx | 32 +++++---
.../src/components/Footer/index.tsx | 2 +-
.../src/components/RightContent/AvatarDropdown.tsx | 29 ++-----
.../src/components/RightContent/index.tsx | 30 +++-----
dubbo-admin-ui-antd/src/locales/en-US/menu.ts | 1 +
dubbo-admin-ui-antd/src/locales/en-US/pages.ts | 3 +-
dubbo-admin-ui-antd/src/locales/zh-CN/menu.ts | 1 +
dubbo-admin-ui-antd/src/locales/zh-CN/pages.ts | 1 +
dubbo-admin-ui-antd/src/pages/User/login/index.tsx | 1 -
dubbo-admin-ui-antd/src/pages/document.ejs | 2 +-
.../src/services/ant-design-pro/login.ts | 4 +-
dubbo-admin-ui-antd/src/storage/auth.ts | 8 ++
dubbo-admin-ui/src/assets/logo.svg | 51 +++++++++++++
17 files changed, 150 insertions(+), 105 deletions(-)
diff --git a/dubbo-admin-ui-antd/config/defaultSettings.ts
b/dubbo-admin-ui-antd/config/defaultSettings.ts
index d341acc..ed17bb8 100644
--- a/dubbo-admin-ui-antd/config/defaultSettings.ts
+++ b/dubbo-admin-ui-antd/config/defaultSettings.ts
@@ -27,9 +27,9 @@ const Settings: LayoutSettings & {
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
- title: 'Ant Design Pro',
+ title: 'apache-dubbo-admin',
pwa: false,
- logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
+ logo: '/logo.png',
iconfontUrl: '',
};
diff --git a/dubbo-admin-ui-antd/config/routes.ts
b/dubbo-admin-ui-antd/config/routes.ts
index a2b0b90..dac54a5 100644
--- a/dubbo-admin-ui-antd/config/routes.ts
+++ b/dubbo-admin-ui-antd/config/routes.ts
@@ -15,9 +15,10 @@
* limitations under the License.
*/
export default [
- {
- path: '/',
- routes: [
+ {
+ path: '/',
+ redirect: '/service',
+ },
{
path: '/service',
routes:[
@@ -27,53 +28,51 @@ export default [
component: './Service'
}
]
- }
- ],
- },
- {
- path: '/user',
- layout: false,
- routes: [
+ },
{
path: '/user',
+ layout: false,
routes: [
{
- name: 'login',
- path: '/user/login',
- component: './User/login',
+ path: '/user',
+ routes: [
+ {
+ name: 'login',
+ path: '/user/login',
+ component: './User/login',
+ },
+ ],
},
],
},
- ],
- },
- {
- path: '/welcome',
- name: 'welcome',
- icon: 'smile',
- component: './Welcome',
- },
- {
- path: '/admin',
- name: 'admin',
- icon: 'crown',
- access: 'canAdmin',
- component: './Admin',
- routes: [
{
- path: '/admin/sub-page',
- name: 'sub-page',
+ path: '/welcome',
+ name: 'welcome',
icon: 'smile',
component: './Welcome',
},
- ],
- },
- {
- name: 'list.table-list',
- icon: 'table',
- path: '/list',
- component: './TableList',
- },
- {
- component: './404',
- },
-];
+ {
+ path: '/admin',
+ name: 'admin',
+ icon: 'crown',
+ access: 'canAdmin',
+ component: './Admin',
+ routes: [
+ {
+ path: '/admin/sub-page',
+ name: 'sub-page',
+ icon: 'smile',
+ component: './Welcome',
+ },
+ ],
+ },
+ {
+ name: 'list.table-list',
+ icon: 'table',
+ path: '/list',
+ component: './TableList',
+ },
+ {
+ component: './404',
+ },
+]
diff --git a/dubbo-admin-ui-antd/package.json b/dubbo-admin-ui-antd/package.json
index 3d3f640..4fa3675 100644
--- a/dubbo-admin-ui-antd/package.json
+++ b/dubbo-admin-ui-antd/package.json
@@ -65,7 +65,8 @@
"react-dom": "^17.0.0",
"react-helmet-async": "^1.0.4",
"umi": "^3.2.14",
- "umi-request": "^1.0.8"
+ "umi-request": "^1.0.8",
+ "http-status": "^1.2.0"
},
"devDependencies": {
"@ant-design/pro-cli": "^2.0.2",
diff --git a/dubbo-admin-ui-antd/public/logo.png
b/dubbo-admin-ui-antd/public/logo.png
new file mode 100644
index 0000000..0951ed5
Binary files /dev/null and b/dubbo-admin-ui-antd/public/logo.png differ
diff --git a/dubbo-admin-ui-antd/src/app.tsx b/dubbo-admin-ui-antd/src/app.tsx
index f2ef2d6..85f1480 100644
--- a/dubbo-admin-ui-antd/src/app.tsx
+++ b/dubbo-admin-ui-antd/src/app.tsx
@@ -23,9 +23,9 @@ import { history } from 'umi';
import RightContent from '@/components/RightContent';
import Footer from '@/components/Footer';
import type { ResponseError } from 'umi-request';
-import { currentUser as queryCurrentUser } from
'./services/ant-design-pro/api';
import { BookOutlined, LinkOutlined } from '@ant-design/icons';
-import { getToken } from '@/storage/auth'
+import { getToken,removeToken,removeUsername } from '@/storage/auth'
+import HttpStatus from 'http-status'
const isDev = process.env.NODE_ENV === 'development';
@@ -124,13 +124,21 @@ const codeMessage = {
const errorHandler = (error: ResponseError) => {
const { response } = error;
if (response && response.status) {
- const errorText = codeMessage[response.status] || response.statusText;
- const { status, url } = response;
+ if(error.response.status === HttpStatus.UNAUTHORIZED){
+ removeToken();
+ removeUsername;
+ history.push('/user/login');
+ }else if(error.response.status >= HttpStatus.BAD_REQUEST){
+ const errorText = codeMessage[response.status] || response.statusText;
+ const { status, url } = response;
+
+ notification.error({
+ message: `Request error ${status}: ${url}`,
+ description: errorText,
+ });
+ }
+
- notification.error({
- message: `Request error ${status}: ${url}`,
- description: errorText,
- });
}
if (!response) {
@@ -145,7 +153,13 @@ const errorHandler = (error: ResponseError) => {
const devRequestProcessInterceptors = (url: string, options:
RequestOptionsInit) => {
return {
url: `/api/dev${url}`,
- options: { ...options, interceptors: true },
+ options: {
+ ...options,
+ interceptors: true,
+ headers: {
+ 'Authorization': getToken(),
+ }
+ },
};
};
diff --git a/dubbo-admin-ui-antd/src/components/Footer/index.tsx
b/dubbo-admin-ui-antd/src/components/Footer/index.tsx
index d10c584..35b9407 100644
--- a/dubbo-admin-ui-antd/src/components/Footer/index.tsx
+++ b/dubbo-admin-ui-antd/src/components/Footer/index.tsx
@@ -17,5 +17,5 @@
import { DefaultFooter } from '@ant-design/pro-layout';
export default () => (
- <DefaultFooter copyright="Copyright ©2018-2021 The Apache Software
Foundation." links={[]} />
+ <DefaultFooter copyright="2018-2021 The Apache Software Foundation."
links={[]} />
);
diff --git a/dubbo-admin-ui-antd/src/components/RightContent/AvatarDropdown.tsx
b/dubbo-admin-ui-antd/src/components/RightContent/AvatarDropdown.tsx
index bd8a591..c0593af 100644
--- a/dubbo-admin-ui-antd/src/components/RightContent/AvatarDropdown.tsx
+++ b/dubbo-admin-ui-antd/src/components/RightContent/AvatarDropdown.tsx
@@ -22,6 +22,7 @@ import { stringify } from 'querystring';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
import { outLogin } from '@/services/ant-design-pro/login';
+import {removeToken,getUsername,removeUsername} from '@/storage/auth'
export type GlobalHeaderRightProps = {
menu?: boolean;
@@ -32,6 +33,8 @@ export type GlobalHeaderRightProps = {
*/
const loginOut = async () => {
await outLogin();
+ removeToken();
+ removeUsername();
const { query = {}, pathname } = history.location;
const { redirect } = query;
// Note: There may be security issues, please note
@@ -66,28 +69,6 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
menu }) => {
[initialState, setInitialState],
);
- const loading = (
- <span className={`${styles.action} ${styles.account}`}>
- <Spin
- size="small"
- style={{
- marginLeft: 8,
- marginRight: 8,
- }}
- />
- </span>
- );
-
- if (!initialState) {
- return loading;
- }
-
- const { currentUser } = initialState;
-
- if (!currentUser || !currentUser.name) {
- return loading;
- }
-
const menuHeaderDropdown = (
<Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}>
{menu && (
@@ -113,8 +94,8 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
menu }) => {
return (
<HeaderDropdown overlay={menuHeaderDropdown}>
<span className={`${styles.action} ${styles.account}`}>
- <Avatar size="small" className={styles.avatar}
src={currentUser.avatar} alt="avatar" />
- <span className={`${styles.name} anticon`}>{currentUser.name}</span>
+ <UserOutlined />
+ <span className={`${styles.name} anticon`}>{getUsername}</span>
</span>
</HeaderDropdown>
);
diff --git a/dubbo-admin-ui-antd/src/components/RightContent/index.tsx
b/dubbo-admin-ui-antd/src/components/RightContent/index.tsx
index e783c87..e85e0b1 100644
--- a/dubbo-admin-ui-antd/src/components/RightContent/index.tsx
+++ b/dubbo-admin-ui-antd/src/components/RightContent/index.tsx
@@ -21,6 +21,7 @@ import { useModel, SelectLang } from 'umi';
import Avatar from './AvatarDropdown';
import HeaderSearch from '../HeaderSearch';
import styles from './index.less';
+import { useIntl } from 'umi';
export type SiderTheme = 'light' | 'dark';
@@ -43,39 +44,26 @@ const GlobalHeaderRight: React.FC = () => {
if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') {
className = `${styles.right} ${styles.dark}`;
}
+
+ const intl = useIntl();
+
return (
<Space className={className}>
<HeaderSearch
className={`${styles.action} ${styles.search}`}
placeholder="On site search"
- defaultValue="umi ui"
+ defaultValue={intl.formatMessage(
+ {
+ id: 'pages.header.search.placeholder'
+ }
+ )}
options={[
{ label: <a href="https://umijs.org/zh/guide/umi-ui.html">umi
ui</a>, value: 'umi ui' },
- {
- label: <a href="next.ant.design">Ant Design</a>,
- value: 'Ant Design',
- },
- {
- label: <a href="https://protable.ant.design/">Pro Table</a>,
- value: 'Pro Table',
- },
- {
- label: <a href="https://prolayout.ant.design/">Pro Layout</a>,
- value: 'Pro Layout',
- },
]}
// onSearch={value => {
// console.log('input', value);
// }}
/>
- <span
- className={styles.action}
- onClick={() => {
- window.open('https://pro.ant.design/docs/getting-started');
- }}
- >
- <QuestionCircleOutlined />
- </span>
<Avatar />
{REACT_APP_ENV && (
<span>
diff --git a/dubbo-admin-ui-antd/src/locales/en-US/menu.ts
b/dubbo-admin-ui-antd/src/locales/en-US/menu.ts
index 3e62acf..a407cc2 100644
--- a/dubbo-admin-ui-antd/src/locales/en-US/menu.ts
+++ b/dubbo-admin-ui-antd/src/locales/en-US/menu.ts
@@ -65,4 +65,5 @@ export default {
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
+ 'menu.service':'menu'
};
diff --git a/dubbo-admin-ui-antd/src/locales/en-US/pages.ts
b/dubbo-admin-ui-antd/src/locales/en-US/pages.ts
index 3596d7a..f8aedbf 100644
--- a/dubbo-admin-ui-antd/src/locales/en-US/pages.ts
+++ b/dubbo-admin-ui-antd/src/locales/en-US/pages.ts
@@ -16,7 +16,7 @@
*/
export default {
'pages.layouts.userLayout.title':
- 'Ant Design is the most influential web design specification in Xihu
district',
+ 'Ant Design is the most influential web design specification in Xihu
district',
'pages.login.accountLogin.tab': 'Account Login',
'pages.login.accountLogin.successful': 'Login successful!',
'pages.login.accountLogin.error': 'Login failed, please try again!',
@@ -82,4 +82,5 @@ export default {
'pages.searchTable.tenThousand': '0000',
'pages.searchTable.batchDeletion': 'bacth deletion',
'pages.searchTable.batchApproval': 'batch approval',
+ 'pages.header.search.placeholder':'Search service name',
};
diff --git a/dubbo-admin-ui-antd/src/locales/zh-CN/menu.ts
b/dubbo-admin-ui-antd/src/locales/zh-CN/menu.ts
index 459dfcd..3181da6 100644
--- a/dubbo-admin-ui-antd/src/locales/zh-CN/menu.ts
+++ b/dubbo-admin-ui-antd/src/locales/zh-CN/menu.ts
@@ -65,4 +65,5 @@ export default {
'menu.editor.flow': '流程编辑器',
'menu.editor.mind': '脑图编辑器',
'menu.editor.koni': '拓扑编辑器',
+ 'menu.service':'菜单'
};
diff --git a/dubbo-admin-ui-antd/src/locales/zh-CN/pages.ts
b/dubbo-admin-ui-antd/src/locales/zh-CN/pages.ts
index 489cefe..4d2f0e5 100644
--- a/dubbo-admin-ui-antd/src/locales/zh-CN/pages.ts
+++ b/dubbo-admin-ui-antd/src/locales/zh-CN/pages.ts
@@ -79,4 +79,5 @@ export default {
'pages.searchTable.tenThousand': '万',
'pages.searchTable.batchDeletion': '批量删除',
'pages.searchTable.batchApproval': '批量审批',
+ 'pages.header.search.placeholder':'服务查询',
};
diff --git a/dubbo-admin-ui-antd/src/pages/User/login/index.tsx
b/dubbo-admin-ui-antd/src/pages/User/login/index.tsx
index 9c5547d..2c7a35f 100644
--- a/dubbo-admin-ui-antd/src/pages/User/login/index.tsx
+++ b/dubbo-admin-ui-antd/src/pages/User/login/index.tsx
@@ -211,5 +211,4 @@ const Login: React.FC = () => {
</div>
);
};
-
export default Login;
diff --git a/dubbo-admin-ui-antd/src/pages/document.ejs
b/dubbo-admin-ui-antd/src/pages/document.ejs
index e20d99c..790bd4c 100644
--- a/dubbo-admin-ui-antd/src/pages/document.ejs
+++ b/dubbo-admin-ui-antd/src/pages/document.ejs
@@ -24,7 +24,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0,
user-scalable=0"
/>
- <title>Ant Design Pro</title>
+ <title>apache-dubbo-admin</title>
<link rel="icon" href="<%= context.config.publicPath +'favicon.ico'%>"
type="image/x-icon" />
</head>
<body>
diff --git a/dubbo-admin-ui-antd/src/services/ant-design-pro/login.ts
b/dubbo-admin-ui-antd/src/services/ant-design-pro/login.ts
index a1bbcee..c66d2fa 100644
--- a/dubbo-admin-ui-antd/src/services/ant-design-pro/login.ts
+++ b/dubbo-admin-ui-antd/src/services/ant-design-pro/login.ts
@@ -19,8 +19,8 @@
import { request } from 'umi';
export async function outLogin(options?: { [key: string]: any }) {
- return request<Record<string, any>>('/api/login/outLogin', {
- method: 'GET',
+ return request<Record<string, any>>('/user/logout', {
+ method: 'DELETE',
...(options || {}),
});
}
diff --git a/dubbo-admin-ui-antd/src/storage/auth.ts
b/dubbo-admin-ui-antd/src/storage/auth.ts
index 6392ea1..74d515b 100644
--- a/dubbo-admin-ui-antd/src/storage/auth.ts
+++ b/dubbo-admin-ui-antd/src/storage/auth.ts
@@ -35,6 +35,10 @@
return result?result:'';
}
+ export const removeToken = ()=>{
+ localStorage.removeItem(tokenKey);
+ }
+
export const setUsername = (username:string) => {
localStorage.setItem(usernameKey,username);
}
@@ -43,3 +47,7 @@
var result = localStorage.getItem(usernameKey);
return result?result:'';
}
+
+ export const removeUsername = ()=>{
+ localStorage.removeItem(usernameKey);
+ }
diff --git a/dubbo-admin-ui/src/assets/logo.svg
b/dubbo-admin-ui/src/assets/logo.svg
new file mode 100644
index 0000000..0e36ae6
--- /dev/null
+++ b/dubbo-admin-ui/src/assets/logo.svg
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="269px"
height="269px" viewBox="0 0 269 269" enable-background="new 0 0 269 269"
xml:space="preserve"> <image id="image0" width="269" height="269" x="0" y="0"
+
href="data:image/png;base64,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" />
+</svg>