You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/05/21 03:37:31 UTC

[incubator-apisix-dashboard] branch feat-layout created (now 311f2d3)

This is an automated email from the ASF dual-hosted git repository.

juzhiyuan pushed a change to branch feat-layout
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git.


      at 311f2d3  feat: added initial state

This branch includes the following new commits:

     new 7a5400e  feat: remove unused files
     new 311f2d3  feat: added initial state

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[incubator-apisix-dashboard] 01/02: feat: remove unused files

Posted by ju...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

juzhiyuan pushed a commit to branch feat-layout
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git

commit 7a5400e0f8108b7d9dc31399386760df86b85e89
Author: juzhiyuan <jj...@gmail.com>
AuthorDate: Thu May 21 11:34:39 2020 +0800

    feat: remove unused files
---
 config/config.ts                                   |   8 +-
 src/app.tsx                                        |  19 ++
 src/components/Authorized/Authorized.tsx           |  35 ----
 src/components/Authorized/AuthorizedRoute.tsx      |  33 ---
 src/components/Authorized/CheckPermissions.tsx     |  83 --------
 src/components/Authorized/PromiseRender.tsx        |  93 ---------
 src/components/Authorized/Secured.tsx              |  66 ------
 src/components/Authorized/index.tsx                |  11 -
 src/components/Authorized/renderAuthorize.ts       |  30 ---
 src/components/Footer/index.tsx                    |  17 ++
 src/components/GlobalHeader/AvatarDropdown.tsx     |  86 --------
 src/components/GlobalHeader/NoticeIconView.tsx     | 171 ----------------
 src/components/GlobalHeader/RightContent.tsx       |  56 ------
 src/components/RightContent/AvatarDropdown.tsx     |  99 +++++++++
 .../{GlobalHeader => RightContent}/index.less      |  35 +---
 src/components/RightContent/index.tsx              |  72 +++++++
 src/components/SelectLang/index.less               |  24 ---
 src/components/SelectLang/index.tsx                |  50 -----
 src/layouts/BasicLayout.tsx                        | 155 --------------
 src/layouts/BlankLayout.tsx                        |   5 -
 src/layouts/SecurityLayout.tsx                     |  57 ------
 src/layouts/UserLayout.less                        |  71 -------
 src/layouts/UserLayout.tsx                         |  77 -------
 src/models/connect.d.ts                            |  31 ---
 src/models/global.ts                               | 137 -------------
 src/models/login.ts                                |  87 --------
 src/models/setting.ts                              |  37 ----
 src/models/user.ts                                 |  85 --------
 src/pages/Authorized.tsx                           |  36 ----
 .../user/login/components/Login/LoginItem.tsx      |  10 +-
 src/pages/user/login/components/Login/LoginTab.tsx |   6 +-
 src/pages/user/login/components/Login/index.tsx    |  17 +-
 src/pages/user/login/index.tsx                     | 224 +++++++++++++++------
 src/pages/user/login/style.less                    |  70 +++++++
 src/utils/Authorized.ts                            |  19 --
 src/utils/authority.ts                             |  32 ---
 36 files changed, 462 insertions(+), 1682 deletions(-)

diff --git a/config/config.ts b/config/config.ts
index 7524365..8339d2f 100644
--- a/config/config.ts
+++ b/config/config.ts
@@ -21,12 +21,16 @@ export default defineConfig({
   targets: {
     ie: 11,
   },
+  layout: {
+    name: 'APISIX Dashboard',
+    locale: true,
+  },
   base: '/dashboard/',
   publicPath: '/',
   routes: [
     {
       path: '/user',
-      component: '../layouts/UserLayout',
+      layout: false,
       routes: [
         {
           name: 'login',
@@ -37,11 +41,9 @@ export default defineConfig({
     },
     {
       path: '/',
-      component: '../layouts/SecurityLayout',
       routes: [
         {
           path: '/',
-          component: '../layouts/BasicLayout',
           authority: ['admin', 'user'],
           routes: [
             {
diff --git a/src/app.tsx b/src/app.tsx
index 1d66ae6..7a5b6af 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,6 +1,25 @@
+import React from 'react';
 import { notification } from 'antd';
 import { RequestConfig } from 'umi';
+import { BasicLayoutProps, Settings as LayoutSettings } from '@ant-design/pro-layout';
+
 import { getAdminAPIConfig } from '@/utils/setting';
+import RightContent from '@/components/RightContent';
+import Footer from '@/components/Footer';
+
+export const layout = ({
+  initialState,
+}: {
+  initialState: { settings?: LayoutSettings };
+}): BasicLayoutProps => {
+  return {
+    rightContentRender: () => <RightContent />,
+    disableContentMargin: false,
+    footerRender: () => <Footer />,
+    menuHeaderRender: undefined,
+    ...initialState?.settings,
+  };
+};
 
 const codeMessage = {
   200: '服务器成功返回请求的数据。',
diff --git a/src/components/Authorized/Authorized.tsx b/src/components/Authorized/Authorized.tsx
deleted file mode 100644
index 36af4c6..0000000
--- a/src/components/Authorized/Authorized.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import React from 'react';
-import { Result } from 'antd';
-import check, { IAuthorityType } from './CheckPermissions';
-
-import AuthorizedRoute from './AuthorizedRoute';
-import Secured from './Secured';
-
-interface AuthorizedProps {
-  authority: IAuthorityType;
-  noMatch?: React.ReactNode;
-}
-
-type IAuthorizedType = React.FunctionComponent<AuthorizedProps> & {
-  Secured: typeof Secured;
-  check: typeof check;
-  AuthorizedRoute: typeof AuthorizedRoute;
-};
-
-const Authorized: React.FunctionComponent<AuthorizedProps> = ({
-  children,
-  authority,
-  noMatch = (
-    <Result
-      status={403}
-      title="403"
-      subTitle="Sorry, you are not authorized to access this page."
-    />
-  ),
-}) => {
-  const childrenRender: React.ReactNode = typeof children === 'undefined' ? null : children;
-  const dom = check(authority, childrenRender, noMatch);
-  return <>{dom}</>;
-};
-
-export default Authorized as IAuthorizedType;
diff --git a/src/components/Authorized/AuthorizedRoute.tsx b/src/components/Authorized/AuthorizedRoute.tsx
deleted file mode 100644
index 7743eae..0000000
--- a/src/components/Authorized/AuthorizedRoute.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Redirect, Route } from 'umi';
-
-import React from 'react';
-import Authorized from './Authorized';
-import { IAuthorityType } from './CheckPermissions';
-
-interface AuthorizedRoutePops {
-  currentAuthority: string;
-  component: React.ComponentClass<any, any>;
-  render: (props: any) => React.ReactNode;
-  redirectPath: string;
-  authority: IAuthorityType;
-}
-
-const AuthorizedRoute: React.SFC<AuthorizedRoutePops> = ({
-  component: Component,
-  render,
-  authority,
-  redirectPath,
-  ...rest
-}) => (
-  <Authorized
-    authority={authority}
-    noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
-  >
-    <Route
-      {...rest}
-      render={(props: any) => (Component ? <Component {...props} /> : render(props))}
-    />
-  </Authorized>
-);
-
-export default AuthorizedRoute;
diff --git a/src/components/Authorized/CheckPermissions.tsx b/src/components/Authorized/CheckPermissions.tsx
deleted file mode 100644
index caa15a3..0000000
--- a/src/components/Authorized/CheckPermissions.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React from 'react';
-import { CURRENT } from './renderAuthorize';
-// eslint-disable-next-line import/no-cycle
-import PromiseRender from './PromiseRender';
-
-export type IAuthorityType =
-  | undefined
-  | string
-  | string[]
-  | Promise<boolean>
-  | ((currentAuthority: string | string[]) => IAuthorityType);
-
-/**
- * 通用权限检查方法
- * Common check permissions method
- * @param { 权限判定 | Permission judgment } authority
- * @param { 你的权限 | Your permission description } currentAuthority
- * @param { 通过的组件 | Passing components } target
- * @param { 未通过的组件 | no pass components } Exception
- */
-const checkPermissions = <T, K>(
-  authority: IAuthorityType,
-  currentAuthority: string | string[],
-  target: T,
-  Exception: K,
-): T | K | React.ReactNode => {
-  // 没有判定权限.默认查看所有
-  // Retirement authority, return target;
-  if (!authority) {
-    return target;
-  }
-  // 数组处理
-  if (Array.isArray(authority)) {
-    if (Array.isArray(currentAuthority)) {
-      if (currentAuthority.some(item => authority.includes(item))) {
-        return target;
-      }
-    } else if (authority.includes(currentAuthority)) {
-      return target;
-    }
-    return Exception;
-  }
-  // string 处理
-  if (typeof authority === 'string') {
-    if (Array.isArray(currentAuthority)) {
-      if (currentAuthority.some(item => authority === item)) {
-        return target;
-      }
-    } else if (authority === currentAuthority) {
-      return target;
-    }
-    return Exception;
-  }
-  // Promise 处理
-  if (authority instanceof Promise) {
-    return <PromiseRender<T, K> ok={target} error={Exception} promise={authority} />;
-  }
-  // Function 处理
-  if (typeof authority === 'function') {
-    try {
-      const bool = authority(currentAuthority);
-      // 函数执行后返回值是 Promise
-      if (bool instanceof Promise) {
-        return <PromiseRender<T, K> ok={target} error={Exception} promise={bool} />;
-      }
-      if (bool) {
-        return target;
-      }
-      return Exception;
-    } catch (error) {
-      throw error;
-    }
-  }
-  throw new Error('unsupported parameters');
-};
-
-export { checkPermissions };
-
-function check<T, K>(authority: IAuthorityType, target: T, Exception: K): T | K | React.ReactNode {
-  return checkPermissions<T, K>(authority, CURRENT, target, Exception);
-}
-
-export default check;
diff --git a/src/components/Authorized/PromiseRender.tsx b/src/components/Authorized/PromiseRender.tsx
deleted file mode 100644
index 25f2597..0000000
--- a/src/components/Authorized/PromiseRender.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import React from 'react';
-import { Spin } from 'antd';
-import isEqual from 'lodash/isEqual';
-import { isComponentClass } from './Secured';
-// eslint-disable-next-line import/no-cycle
-
-interface PromiseRenderProps<T, K> {
-  ok: T;
-  error: K;
-  promise: Promise<boolean>;
-}
-
-interface PromiseRenderState {
-  component: React.ComponentClass | React.FunctionComponent;
-}
-
-export default class PromiseRender<T, K> extends React.Component<
-  PromiseRenderProps<T, K>,
-  PromiseRenderState
-> {
-  state: PromiseRenderState = {
-    component: () => null,
-  };
-
-  componentDidMount() {
-    this.setRenderComponent(this.props);
-  }
-
-  shouldComponentUpdate = (nextProps: PromiseRenderProps<T, K>, nextState: PromiseRenderState) => {
-    const { component } = this.state;
-    if (!isEqual(nextProps, this.props)) {
-      this.setRenderComponent(nextProps);
-    }
-    if (nextState.component !== component) return true;
-    return false;
-  };
-
-  // set render Component : ok or error
-  setRenderComponent(props: PromiseRenderProps<T, K>) {
-    const ok = this.checkIsInstantiation(props.ok);
-    const error = this.checkIsInstantiation(props.error);
-    props.promise
-      .then(() => {
-        this.setState({
-          component: ok,
-        });
-        return true;
-      })
-      .catch(() => {
-        this.setState({
-          component: error,
-        });
-      });
-  }
-
-  // Determine whether the incoming component has been instantiated
-  // AuthorizedRoute is already instantiated
-  // Authorized  render is already instantiated, children is no instantiated
-  // Secured is not instantiated
-  checkIsInstantiation = (
-    target: React.ReactNode | React.ComponentClass,
-  ): React.FunctionComponent => {
-    if (isComponentClass(target)) {
-      const Target = target as React.ComponentClass;
-      return (props: any) => <Target {...props} />;
-    }
-    if (React.isValidElement(target)) {
-      return (props: any) => React.cloneElement(target, props);
-    }
-    return () => target as React.ReactNode & null;
-  };
-
-  render() {
-    const { component: Component } = this.state;
-    const { ok, error, promise, ...rest } = this.props;
-
-    return Component ? (
-      <Component {...rest} />
-    ) : (
-      <div
-        style={{
-          width: '100%',
-          height: '100%',
-          margin: 'auto',
-          paddingTop: 50,
-          textAlign: 'center',
-        }}
-      >
-        <Spin size="large" />
-      </div>
-    );
-  }
-}
diff --git a/src/components/Authorized/Secured.tsx b/src/components/Authorized/Secured.tsx
deleted file mode 100644
index 0bdbbe4..0000000
--- a/src/components/Authorized/Secured.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from 'react';
-import CheckPermissions from './CheckPermissions';
-
-/**
- * 默认不能访问任何页面
- * default is "NULL"
- */
-const Exception403 = () => 403;
-
-export const isComponentClass = (component: React.ComponentClass | React.ReactNode): boolean => {
-  if (!component) return false;
-  const proto = Object.getPrototypeOf(component);
-  if (proto === React.Component || proto === Function.prototype) return true;
-  return isComponentClass(proto);
-};
-
-// Determine whether the incoming component has been instantiated
-// AuthorizedRoute is already instantiated
-// Authorized  render is already instantiated, children is no instantiated
-// Secured is not instantiated
-const checkIsInstantiation = (target: React.ComponentClass | React.ReactNode) => {
-  if (isComponentClass(target)) {
-    const Target = target as React.ComponentClass;
-    return (props: any) => <Target {...props} />;
-  }
-  if (React.isValidElement(target)) {
-    return (props: any) => React.cloneElement(target, props);
-  }
-  return () => target;
-};
-
-/**
- * 用于判断是否拥有权限访问此 view 权限
- * authority 支持传入 string, () => boolean | Promise
- * e.g. 'user' 只有 user 用户能访问
- * e.g. 'user,admin' user 和 admin 都能访问
- * e.g. ()=>boolean 返回true能访问,返回false不能访问
- * e.g. Promise  then 能访问   catch不能访问
- * e.g. authority support incoming string, () => boolean | Promise
- * e.g. 'user' only user user can access
- * e.g. 'user, admin' user and admin can access
- * e.g. () => boolean true to be able to visit, return false can not be accessed
- * e.g. Promise then can not access the visit to catch
- * @param {string | function | Promise} authority
- * @param {ReactNode} error 非必需参数
- */
-const authorize = (authority: string, error?: React.ReactNode) => {
-  /**
-   * conversion into a class
-   * 防止传入字符串时找不到staticContext造成报错
-   * String parameters can cause staticContext not found error
-   */
-  let classError: boolean | React.FunctionComponent = false;
-  if (error) {
-    classError = (() => error) as React.FunctionComponent;
-  }
-  if (!authority) {
-    throw new Error('authority is required');
-  }
-  return function decideAuthority(target: React.ComponentClass | React.ReactNode) {
-    const component = CheckPermissions(authority, target, classError || Exception403);
-    return checkIsInstantiation(component);
-  };
-};
-
-export default authorize;
diff --git a/src/components/Authorized/index.tsx b/src/components/Authorized/index.tsx
deleted file mode 100644
index 6703a46..0000000
--- a/src/components/Authorized/index.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import Authorized from './Authorized';
-import Secured from './Secured';
-import check from './CheckPermissions';
-import renderAuthorize from './renderAuthorize';
-
-Authorized.Secured = Secured;
-Authorized.check = check;
-
-const RenderAuthorize = renderAuthorize(Authorized);
-
-export default RenderAuthorize;
diff --git a/src/components/Authorized/renderAuthorize.ts b/src/components/Authorized/renderAuthorize.ts
deleted file mode 100644
index df00875..0000000
--- a/src/components/Authorized/renderAuthorize.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/* eslint-disable eslint-comments/disable-enable-pair */
-/* eslint-disable import/no-mutable-exports */
-let CURRENT: string | string[] = 'NULL';
-
-type CurrentAuthorityType = string | string[] | (() => typeof CURRENT);
-/**
- * use  authority or getAuthority
- * @param {string|()=>String} currentAuthority
- */
-const renderAuthorize = <T>(Authorized: T): ((currentAuthority: CurrentAuthorityType) => T) => (
-  currentAuthority: CurrentAuthorityType,
-): T => {
-  if (currentAuthority) {
-    if (typeof currentAuthority === 'function') {
-      CURRENT = currentAuthority();
-    }
-    if (
-      Object.prototype.toString.call(currentAuthority) === '[object String]' ||
-      Array.isArray(currentAuthority)
-    ) {
-      CURRENT = currentAuthority as string[];
-    }
-  } else {
-    CURRENT = 'NULL';
-  }
-  return Authorized;
-};
-
-export { CURRENT };
-export default <T>(Authorized: T) => renderAuthorize<T>(Authorized);
diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx
new file mode 100644
index 0000000..0f6648b
--- /dev/null
+++ b/src/components/Footer/index.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { GithubOutlined } from '@ant-design/icons';
+import { DefaultFooter } from '@ant-design/pro-layout';
+
+export default () => (
+  <DefaultFooter
+    copyright="2020 Apache APISIX"
+    links={[
+      {
+        key: 'GitHub',
+        title: <GithubOutlined />,
+        href: 'https://github.com/apache/incubator-apisix',
+        blankTarget: true,
+      },
+    ]}
+  />
+);
diff --git a/src/components/GlobalHeader/AvatarDropdown.tsx b/src/components/GlobalHeader/AvatarDropdown.tsx
deleted file mode 100644
index 4cf5340..0000000
--- a/src/components/GlobalHeader/AvatarDropdown.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
-import { Avatar, Menu, Spin } from 'antd';
-import { ClickParam } from 'antd/es/menu';
-import React from 'react';
-import { history, connect } from 'umi';
-import { ConnectProps, ConnectState } from '@/models/connect';
-import { CurrentUser } from '@/models/user';
-import HeaderDropdown from '../HeaderDropdown';
-import styles from './index.less';
-
-export interface GlobalHeaderRightProps extends ConnectProps {
-  currentUser?: CurrentUser;
-  menu?: boolean;
-}
-
-class AvatarDropdown extends React.Component<GlobalHeaderRightProps> {
-  onMenuClick = (event: ClickParam) => {
-    const { key } = event;
-
-    if (key === 'logout') {
-      const { dispatch } = this.props;
-
-      if (dispatch) {
-        dispatch({
-          type: 'login/logout',
-        });
-      }
-
-      return;
-    }
-
-    history.push(`/account/${key}`);
-  };
-
-  render(): React.ReactNode {
-    const {
-      currentUser = {
-        avatar: '',
-        name: '',
-      },
-      menu,
-    } = this.props;
-    const menuHeaderDropdown = (
-      <Menu className={styles.menu} selectedKeys={[]} onClick={this.onMenuClick}>
-        {menu && (
-          <Menu.Item key="center">
-            <UserOutlined />
-            个人中心
-          </Menu.Item>
-        )}
-        {menu && (
-          <Menu.Item key="settings">
-            <SettingOutlined />
-            个人设置
-          </Menu.Item>
-        )}
-        {menu && <Menu.Divider />}
-
-        <Menu.Item key="logout">
-          <LogoutOutlined />
-          退出登录
-        </Menu.Item>
-      </Menu>
-    );
-    return currentUser && currentUser.name ? (
-      <HeaderDropdown overlay={menuHeaderDropdown}>
-        <span className={`${styles.action} ${styles.account}`}>
-          <Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
-          <span className={styles.name}>{currentUser.name}</span>
-        </span>
-      </HeaderDropdown>
-    ) : (
-      <Spin
-        size="small"
-        style={{
-          marginLeft: 8,
-          marginRight: 8,
-        }}
-      />
-    );
-  }
-}
-
-export default connect(({ user }: ConnectState) => ({
-  currentUser: user.currentUser,
-}))(AvatarDropdown);
diff --git a/src/components/GlobalHeader/NoticeIconView.tsx b/src/components/GlobalHeader/NoticeIconView.tsx
deleted file mode 100644
index f450087..0000000
--- a/src/components/GlobalHeader/NoticeIconView.tsx
+++ /dev/null
@@ -1,171 +0,0 @@
-import React, { Component } from 'react';
-import { Tag, message } from 'antd';
-import { connect } from 'umi';
-import groupBy from 'lodash/groupBy';
-import moment from 'moment';
-import { NoticeItem } from '@/models/global';
-import { CurrentUser } from '@/models/user';
-import { ConnectProps, ConnectState } from '@/models/connect';
-import NoticeIcon from '../NoticeIcon';
-import styles from './index.less';
-
-export interface GlobalHeaderRightProps extends ConnectProps {
-  notices?: NoticeItem[];
-  currentUser?: CurrentUser;
-  fetchingNotices?: boolean;
-  onNoticeVisibleChange?: (visible: boolean) => void;
-  onNoticeClear?: (tabName?: string) => void;
-}
-
-class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
-  componentDidMount() {
-    const { dispatch } = this.props;
-
-    if (dispatch) {
-      dispatch({
-        type: 'global/fetchNotices',
-      });
-    }
-  }
-
-  changeReadState = (clickedItem: NoticeItem): void => {
-    const { id } = clickedItem;
-    const { dispatch } = this.props;
-
-    if (dispatch) {
-      dispatch({
-        type: 'global/changeNoticeReadState',
-        payload: id,
-      });
-    }
-  };
-
-  handleNoticeClear = (title: string, key: string) => {
-    const { dispatch } = this.props;
-    message.success(`${'清空了'} ${title}`);
-
-    if (dispatch) {
-      dispatch({
-        type: 'global/clearNotices',
-        payload: key,
-      });
-    }
-  };
-
-  getNoticeData = (): {
-    [key: string]: NoticeItem[];
-  } => {
-    const { notices = [] } = this.props;
-
-    if (notices.length === 0) {
-      return {};
-    }
-
-    const newNotices = notices.map(notice => {
-      const newNotice = { ...notice };
-
-      if (newNotice.datetime) {
-        newNotice.datetime = moment(notice.datetime as string).fromNow();
-      }
-
-      if (newNotice.id) {
-        newNotice.key = newNotice.id;
-      }
-
-      if (newNotice.extra && newNotice.status) {
-        const color = {
-          todo: '',
-          processing: 'blue',
-          urgent: 'red',
-          doing: 'gold',
-        }[newNotice.status];
-        newNotice.extra = (
-          <Tag
-            color={color}
-            style={{
-              marginRight: 0,
-            }}
-          >
-            {newNotice.extra}
-          </Tag>
-        );
-      }
-
-      return newNotice;
-    });
-    return groupBy(newNotices, 'type');
-  };
-
-  getUnreadData = (noticeData: { [key: string]: NoticeItem[] }) => {
-    const unreadMsg: {
-      [key: string]: number;
-    } = {};
-    Object.keys(noticeData).forEach(key => {
-      const value = noticeData[key];
-
-      if (!unreadMsg[key]) {
-        unreadMsg[key] = 0;
-      }
-
-      if (Array.isArray(value)) {
-        unreadMsg[key] = value.filter(item => !item.read).length;
-      }
-    });
-    return unreadMsg;
-  };
-
-  render() {
-    const { currentUser, fetchingNotices, onNoticeVisibleChange } = this.props;
-    const noticeData = this.getNoticeData();
-    const unreadMsg = this.getUnreadData(noticeData);
-    return (
-      <NoticeIcon
-        className={styles.action}
-        count={currentUser && currentUser.unreadCount}
-        onItemClick={item => {
-          this.changeReadState(item as NoticeItem);
-        }}
-        loading={fetchingNotices}
-        clearText="清空"
-        viewMoreText="查看更多"
-        onClear={this.handleNoticeClear}
-        onPopupVisibleChange={onNoticeVisibleChange}
-        onViewMore={() => message.info('Click on view more')}
-        clearClose
-      >
-        <NoticeIcon.Tab
-          tabKey="notification"
-          count={unreadMsg.notification}
-          list={noticeData.notification}
-          title="通知"
-          emptyText="你已查看所有通知"
-          showViewMore
-        />
-        <NoticeIcon.Tab
-          tabKey="message"
-          count={unreadMsg.message}
-          list={noticeData.message}
-          title="消息"
-          emptyText="您已读完所有消息"
-          showViewMore
-        />
-        <NoticeIcon.Tab
-          tabKey="event"
-          title="待办"
-          emptyText="你已完成所有待办"
-          count={unreadMsg.event}
-          list={noticeData.event}
-          showViewMore
-        />
-      </NoticeIcon>
-    );
-  }
-}
-
-export default connect(({ user, global, loading }: ConnectState) => ({
-  currentUser: user.currentUser,
-  collapsed: global.collapsed,
-  fetchingMoreNotices: loading.effects['global/fetchMoreNotices'],
-  fetchingNotices: loading.effects['global/fetchNotices'],
-  notices: global.notices,
-}))(GlobalHeaderRight);
diff --git a/src/components/GlobalHeader/RightContent.tsx b/src/components/GlobalHeader/RightContent.tsx
deleted file mode 100644
index 9841729..0000000
--- a/src/components/GlobalHeader/RightContent.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Tooltip, Tag } from 'antd';
-import { QuestionCircleOutlined } from '@ant-design/icons';
-import React from 'react';
-import { connect } from 'umi';
-import { ConnectProps, ConnectState } from '@/models/connect';
-import Avatar from './AvatarDropdown';
-import SelectLang from '../SelectLang';
-import styles from './index.less';
-
-export type SiderTheme = 'light' | 'dark';
-export interface GlobalHeaderRightProps extends ConnectProps {
-  theme?: SiderTheme;
-  layout: 'sidemenu' | 'topmenu';
-}
-
-const ENVTagColor = {
-  dev: 'orange',
-  test: 'green',
-  pre: '#87d068',
-};
-
-const GlobalHeaderRight: React.SFC<GlobalHeaderRightProps> = props => {
-  const { theme, layout } = props;
-  let className = styles.right;
-
-  if (theme === 'dark' && layout === 'topmenu') {
-    className = `${styles.right}  ${styles.dark}`;
-  }
-
-  return (
-    <div className={className}>
-      <Tooltip title="Documentation">
-        <a
-          target="_blank"
-          href="https://github.com/apache/incubator-apisix"
-          rel="noopener noreferrer"
-          className={styles.action}
-        >
-          <QuestionCircleOutlined />
-        </a>
-      </Tooltip>
-      <Avatar />
-      {REACT_APP_ENV && (
-        <span>
-          <Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
-        </span>
-      )}
-      <SelectLang className={styles.action} />
-    </div>
-  );
-};
-
-export default connect(({ settings }: ConnectState) => ({
-  theme: settings.navTheme,
-  layout: settings.layout,
-}))(GlobalHeaderRight);
diff --git a/src/components/RightContent/AvatarDropdown.tsx b/src/components/RightContent/AvatarDropdown.tsx
new file mode 100644
index 0000000..5555355
--- /dev/null
+++ b/src/components/RightContent/AvatarDropdown.tsx
@@ -0,0 +1,99 @@
+import React, { useCallback } from 'react';
+import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
+import { Avatar, Menu, Spin } from 'antd';
+import { ClickParam } from 'antd/es/menu';
+import { history, useModel } from 'umi';
+import { getPageQuery } from '@/utils/utils';
+
+import { stringify } from 'querystring';
+import HeaderDropdown from '../HeaderDropdown';
+import styles from './index.less';
+
+export interface GlobalHeaderRightProps {
+  menu?: boolean;
+}
+
+/**
+ * 退出登录,并且将当前的 url 保存
+ */
+const loginOut = async () => {
+  const { redirect } = getPageQuery();
+  // Note: There may be security issues, please note
+  if (window.location.pathname !== '/user/login' && !redirect) {
+    history.replace({
+      pathname: '/user/login',
+      search: stringify({
+        redirect: window.location.href,
+      }),
+    });
+  }
+};
+
+const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
+  const { initialState, setInitialState } = useModel('@@initialState');
+
+  const onMenuClick = useCallback((event: ClickParam) => {
+    const { key } = event;
+    if (key === 'logout') {
+      setInitialState({ ...initialState, currentUser: undefined });
+      loginOut();
+      return;
+    }
+    history.push(`/account/${key}`);
+  }, []);
+
+  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 && (
+        <Menu.Item key="center">
+          <UserOutlined />
+          个人中心
+        </Menu.Item>
+      )}
+      {menu && (
+        <Menu.Item key="settings">
+          <SettingOutlined />
+          个人设置
+        </Menu.Item>
+      )}
+      {menu && <Menu.Divider />}
+
+      <Menu.Item key="logout">
+        <LogoutOutlined />
+        退出登录
+      </Menu.Item>
+    </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>
+      </span>
+    </HeaderDropdown>
+  );
+};
+
+export default AvatarDropdown;
diff --git a/src/components/GlobalHeader/index.less b/src/components/RightContent/index.less
similarity index 66%
rename from src/components/GlobalHeader/index.less
rename to src/components/RightContent/index.less
index 6a156db..ef2549e 100644
--- a/src/components/GlobalHeader/index.less
+++ b/src/components/RightContent/index.less
@@ -14,20 +14,17 @@
 .right {
   display: flex;
   float: right;
-  height: @layout-header-height;
+  height: 48px;
   margin-left: auto;
   overflow: hidden;
   .action {
     display: flex;
     align-items: center;
-    height: 100%;
+    height: 48px;
     padding: 0 12px;
     cursor: pointer;
     transition: all 0.3s;
-    > span {
-      color: @text-color;
-      vertical-align: middle;
-    }
+
     &:hover {
       background: @pro-header-hover-bg;
     }
@@ -43,7 +40,6 @@
   }
   .account {
     .avatar {
-      margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
       margin-right: 8px;
       color: @primary-color;
       vertical-align: top;
@@ -54,30 +50,11 @@
 
 .dark {
   .action {
-    color: rgba(255, 255, 255, 0.85);
-    > span {
-      color: rgba(255, 255, 255, 0.85);
+    &:hover {
+      background: #252a3d;
     }
-    &:hover,
     &:global(.opened) {
-      background: @primary-color;
-    }
-  }
-}
-
-:global(.ant-pro-global-header) {
-  .dark {
-    .action {
-      color: @text-color;
-      > span {
-        color: @text-color;
-      }
-      &:hover {
-        color: rgba(255, 255, 255, 0.85);
-        > span {
-          color: rgba(255, 255, 255, 0.85);
-        }
-      }
+      background: #252a3d;
     }
   }
 }
diff --git a/src/components/RightContent/index.tsx b/src/components/RightContent/index.tsx
new file mode 100644
index 0000000..8879e41
--- /dev/null
+++ b/src/components/RightContent/index.tsx
@@ -0,0 +1,72 @@
+import { Tooltip, Tag, Space } from 'antd';
+import { QuestionCircleOutlined } from '@ant-design/icons';
+import React from 'react';
+import { useModel, SelectLang } from 'umi';
+import Avatar from './AvatarDropdown';
+import HeaderSearch from '../HeaderSearch';
+import styles from './index.less';
+
+export type SiderTheme = 'light' | 'dark';
+
+const ENVTagColor = {
+  dev: 'orange',
+  test: 'green',
+  pre: '#87d068',
+};
+
+const GlobalHeaderRight: React.FC<{}> = () => {
+  const { initialState } = useModel('@@initialState');
+
+  if (!initialState || !initialState.settings) {
+    return null;
+  }
+
+  const { navTheme, layout } = initialState.settings;
+  let className = styles.right;
+
+  if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') {
+    className = `${styles.right}  ${styles.dark}`;
+  }
+  return (
+    <Space className={className}>
+      <HeaderSearch
+        className={`${styles.action} ${styles.search}`}
+        placeholder="站内搜索"
+        defaultValue="umi ui"
+        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',
+          },
+        ]}
+      />
+      <Tooltip title="使用文档">
+        <span
+          className={styles.action}
+          onClick={() => {
+            window.location.href = 'https://pro.ant.design/docs/getting-started';
+          }}
+        >
+          <QuestionCircleOutlined />
+        </span>
+      </Tooltip>
+      <Avatar />
+      {REACT_APP_ENV && (
+        <span>
+          <Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
+        </span>
+      )}
+      <SelectLang className={styles.action} />
+    </Space>
+  );
+};
+export default GlobalHeaderRight;
diff --git a/src/components/SelectLang/index.less b/src/components/SelectLang/index.less
deleted file mode 100644
index c0da9b4..0000000
--- a/src/components/SelectLang/index.less
+++ /dev/null
@@ -1,24 +0,0 @@
-@import '~antd/es/style/themes/default.less';
-
-.menu {
-  :global(.anticon) {
-    margin-right: 8px;
-  }
-  :global(.ant-dropdown-menu-item) {
-    min-width: 160px;
-  }
-}
-
-.dropDown {
-  line-height: @layout-header-height;
-  vertical-align: top;
-  cursor: pointer;
-  > span {
-    font-size: 16px !important;
-    transform: none !important;
-    svg {
-      position: relative;
-      top: -1px;
-    }
-  }
-}
diff --git a/src/components/SelectLang/index.tsx b/src/components/SelectLang/index.tsx
deleted file mode 100644
index fbb6658..0000000
--- a/src/components/SelectLang/index.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { GlobalOutlined } from '@ant-design/icons';
-import { Menu } from 'antd';
-import { getLocale, setLocale } from 'umi';
-import { ClickParam } from 'antd/es/menu';
-import React from 'react';
-import classNames from 'classnames';
-import HeaderDropdown from '../HeaderDropdown';
-import styles from './index.less';
-
-interface SelectLangProps {
-  className?: string;
-}
-
-const SelectLang: React.FC<SelectLangProps> = props => {
-  const { className } = props;
-  const selectedLang = getLocale();
-
-  const changeLang = ({ key }: ClickParam): void => setLocale(key);
-
-  const locales = ['zh-CN', 'en-US'];
-  const languageLabels = {
-    'zh-CN': '简体中文',
-    'en-US': 'English',
-  };
-  const languageIcons = {
-    'zh-CN': '🇨🇳',
-    'en-US': '🇺🇸',
-  };
-  const langMenu = (
-    <Menu className={styles.menu} selectedKeys={[selectedLang]} onClick={changeLang}>
-      {locales.map(locale => (
-        <Menu.Item key={locale}>
-          <span role="img" aria-label={languageLabels[locale]}>
-            {languageIcons[locale]}
-          </span>{' '}
-          {languageLabels[locale]}
-        </Menu.Item>
-      ))}
-    </Menu>
-  );
-  return (
-    <HeaderDropdown overlay={langMenu} placement="bottomRight">
-      <span className={classNames(styles.dropDown, className)}>
-        <GlobalOutlined title="语言" />
-      </span>
-    </HeaderDropdown>
-  );
-};
-
-export default SelectLang;
diff --git a/src/layouts/BasicLayout.tsx b/src/layouts/BasicLayout.tsx
deleted file mode 100644
index 6f68a6a..0000000
--- a/src/layouts/BasicLayout.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import ProLayout, {
-  MenuDataItem,
-  BasicLayoutProps as ProLayoutProps,
-  Settings,
-  DefaultFooter,
-} from '@ant-design/pro-layout';
-import React, { useEffect } from 'react';
-import { Link, useIntl, connect, Dispatch } from 'umi';
-import { GithubOutlined } from '@ant-design/icons';
-import { Result, Button } from 'antd';
-import Authorized from '@/utils/Authorized';
-import RightContent from '@/components/GlobalHeader/RightContent';
-import { ConnectState } from '@/models/connect';
-import { getAuthorityFromRouter } from '@/utils/utils';
-import logo from '../assets/logo.svg';
-
-const noMatch = (
-  <Result
-    status={403}
-    title="403"
-    subTitle="Sorry, you are not authorized to access this page."
-    extra={
-      <Button type="primary">
-        <Link to="/user/login">Go Login</Link>
-      </Button>
-    }
-  />
-);
-export interface BasicLayoutProps extends ProLayoutProps {
-  breadcrumbNameMap: {
-    [path: string]: MenuDataItem;
-  };
-  route: ProLayoutProps['route'] & {
-    authority: string[];
-  };
-  settings: Settings;
-  dispatch: Dispatch;
-}
-export type BasicLayoutContext = { [K in 'location']: BasicLayoutProps[K] } & {
-  breadcrumbNameMap: {
-    [path: string]: MenuDataItem;
-  };
-};
-/**
- * use Authorized check all menu item
- */
-
-const menuDataRender = (menuList: MenuDataItem[]): MenuDataItem[] =>
-  menuList.map(item => {
-    const localItem = { ...item, children: item.children ? menuDataRender(item.children) : [] };
-    return Authorized.check(item.authority, localItem, null) as MenuDataItem;
-  });
-
-const defaultFooterDom = (
-  <DefaultFooter
-    copyright="2020 Apache APISIX"
-    links={[
-      {
-        key: 'GitHub',
-        title: <GithubOutlined />,
-        href: 'https://github.com/apache/incubator-apisix',
-        blankTarget: true,
-      },
-    ]}
-  />
-);
-
-const BasicLayout: React.FC<BasicLayoutProps> = props => {
-  const {
-    dispatch,
-    children,
-    settings,
-    location = {
-      pathname: '/',
-    },
-  } = props;
-  /**
-   * constructor
-   */
-
-  useEffect(() => {
-    if (dispatch) {
-      dispatch({
-        type: 'user/fetchCurrent',
-      });
-    }
-  }, []);
-  /**
-   * init variables
-   */
-
-  const handleMenuCollapse = (payload: boolean): void => {
-    if (dispatch) {
-      dispatch({
-        type: 'global/changeLayoutCollapsed',
-        payload,
-      });
-    }
-  }; // get children authority
-
-  const authorized = getAuthorityFromRouter(props.route.routes, location.pathname || '/') || {
-    authority: undefined,
-  };
-  const { formatMessage } = useIntl();
-
-  return (
-    <ProLayout
-      logo={logo}
-      formatMessage={formatMessage}
-      menuHeaderRender={(logoDom, titleDom) => (
-        <Link to="/">
-          {logoDom}
-          {titleDom}
-        </Link>
-      )}
-      onCollapse={handleMenuCollapse}
-      menuItemRender={(menuItemProps, defaultDom) => {
-        if (menuItemProps.isUrl || menuItemProps.children || !menuItemProps.path) {
-          return defaultDom;
-        }
-
-        return <Link to={menuItemProps.path}>{defaultDom}</Link>;
-      }}
-      breadcrumbRender={(routers = []) => [
-        {
-          path: '/',
-          breadcrumbName: formatMessage({ id: 'menu.home' }),
-        },
-        ...routers,
-      ]}
-      itemRender={(route, params, routes, paths) => {
-        const first = routes.indexOf(route) === 0;
-        return first ? (
-          <Link to={paths.join('/')}>{route.breadcrumbName}</Link>
-        ) : (
-          <span>{route.breadcrumbName}</span>
-        );
-      }}
-      footerRender={() => defaultFooterDom}
-      menuDataRender={menuDataRender}
-      rightContentRender={() => <RightContent />}
-      {...props}
-      {...settings}
-    >
-      <Authorized authority={authorized!.authority} noMatch={noMatch}>
-        {children}
-      </Authorized>
-    </ProLayout>
-  );
-};
-
-export default connect(({ global, settings }: ConnectState) => ({
-  collapsed: global.collapsed,
-  settings,
-}))(BasicLayout);
diff --git a/src/layouts/BlankLayout.tsx b/src/layouts/BlankLayout.tsx
deleted file mode 100644
index cdc55b0..0000000
--- a/src/layouts/BlankLayout.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import React from 'react';
-
-const Layout: React.FC = ({ children }) => <>{children}</>;
-
-export default Layout;
diff --git a/src/layouts/SecurityLayout.tsx b/src/layouts/SecurityLayout.tsx
deleted file mode 100644
index 6024b65..0000000
--- a/src/layouts/SecurityLayout.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from 'react';
-import { PageLoading } from '@ant-design/pro-layout';
-import { Redirect, connect } from 'umi';
-import { stringify } from 'querystring';
-import { ConnectState, ConnectProps } from '@/models/connect';
-import { CurrentUser } from '@/models/user';
-
-interface SecurityLayoutProps extends ConnectProps {
-  loading?: boolean;
-  currentUser?: CurrentUser;
-}
-
-interface SecurityLayoutState {
-  isReady: boolean;
-}
-
-class SecurityLayout extends React.Component<SecurityLayoutProps, SecurityLayoutState> {
-  state: SecurityLayoutState = {
-    isReady: false,
-  };
-
-  componentDidMount() {
-    this.setState({
-      isReady: true,
-    });
-    const { dispatch } = this.props;
-    if (dispatch) {
-      dispatch({
-        type: 'user/fetchCurrent',
-      });
-    }
-  }
-
-  render() {
-    const { isReady } = this.state;
-    const { children, loading, currentUser } = this.props;
-    // You can replace it to your authentication rule (such as check token exists)
-    // 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
-    const isLogin = currentUser && currentUser.userid;
-    const queryString = stringify({
-      redirect: window.location.href,
-    });
-
-    if ((!isLogin && loading) || !isReady) {
-      return <PageLoading />;
-    }
-    if (!isLogin && window.location.pathname !== '/user/login') {
-      return <Redirect to={`/user/login?${queryString}`} />;
-    }
-    return children;
-  }
-}
-
-export default connect(({ user, loading }: ConnectState) => ({
-  currentUser: user.currentUser,
-  loading: loading.models.user,
-}))(SecurityLayout);
diff --git a/src/layouts/UserLayout.less b/src/layouts/UserLayout.less
deleted file mode 100755
index cdc207e..0000000
--- a/src/layouts/UserLayout.less
+++ /dev/null
@@ -1,71 +0,0 @@
-@import '~antd/es/style/themes/default.less';
-
-.container {
-  display: flex;
-  flex-direction: column;
-  height: 100vh;
-  overflow: auto;
-  background: @layout-body-background;
-}
-
-.lang {
-  width: 100%;
-  height: 40px;
-  line-height: 44px;
-  text-align: right;
-  :global(.ant-dropdown-trigger) {
-    margin-right: 24px;
-  }
-}
-
-.content {
-  flex: 1;
-  padding: 32px 0;
-}
-
-@media (min-width: @screen-md-min) {
-  .container {
-    background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
-    background-repeat: no-repeat;
-    background-position: center 110px;
-    background-size: 100%;
-  }
-
-  .content {
-    padding: 32px 0 24px;
-  }
-}
-
-.top {
-  text-align: center;
-}
-
-.header {
-  height: 44px;
-  line-height: 44px;
-  a {
-    text-decoration: none;
-  }
-}
-
-.logo {
-  height: 44px;
-  margin-right: 16px;
-  vertical-align: top;
-}
-
-.title {
-  position: relative;
-  top: 2px;
-  color: @heading-color;
-  font-weight: 600;
-  font-size: 33px;
-  font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
-}
-
-.desc {
-  margin-top: 12px;
-  margin-bottom: 40px;
-  color: @text-color-secondary;
-  font-size: @font-size-base;
-}
diff --git a/src/layouts/UserLayout.tsx b/src/layouts/UserLayout.tsx
deleted file mode 100644
index c759ab3..0000000
--- a/src/layouts/UserLayout.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import { DefaultFooter, MenuDataItem, getMenuData, getPageTitle } from '@ant-design/pro-layout';
-import { Helmet, HelmetProvider } from 'react-helmet-async';
-import { Link, useIntl, ConnectProps, connect } from 'umi';
-import React from 'react';
-import { GithubOutlined } from '@ant-design/icons';
-import SelectLang from '@/components/SelectLang';
-import { ConnectState } from '@/models/connect';
-import logo from '../assets/logo.svg';
-import styles from './UserLayout.less';
-
-export interface UserLayoutProps extends Partial<ConnectProps> {
-  breadcrumbNameMap: {
-    [path: string]: MenuDataItem;
-  };
-}
-
-const UserLayout: React.FC<UserLayoutProps> = props => {
-  const {
-    route = {
-      routes: [],
-    },
-  } = props;
-  const { routes = [] } = route;
-  const {
-    children,
-    location = {
-      pathname: '',
-    },
-  } = props;
-  const { formatMessage } = useIntl();
-  const { breadcrumb } = getMenuData(routes);
-  const title = getPageTitle({
-    pathname: location.pathname,
-    formatMessage,
-    breadcrumb,
-    ...props,
-  });
-  return (
-    <HelmetProvider>
-      <Helmet>
-        <title>{title}</title>
-        <meta name="description" content={title} />
-      </Helmet>
-
-      <div className={styles.container}>
-        <div className={styles.lang}>
-          <SelectLang />
-        </div>
-        <div className={styles.content}>
-          <div className={styles.top}>
-            <div className={styles.header}>
-              <Link to="/">
-                <img alt="logo" className={styles.logo} src={logo} />
-                <span className={styles.title}>APISIX Dashboard</span>
-              </Link>
-            </div>
-            <div className={styles.desc}>Cloud-Native Microservices API Gateway</div>
-          </div>
-          {children}
-        </div>
-        <DefaultFooter
-          copyright="2020 Apache APISIX"
-          links={[
-            {
-              key: 'GitHub',
-              title: <GithubOutlined />,
-              href: 'https://github.com/apache/incubator-apisix',
-              blankTarget: true,
-            },
-          ]}
-        />
-      </div>
-    </HelmetProvider>
-  );
-};
-
-export default connect(({ settings }: ConnectState) => ({ ...settings }))(UserLayout);
diff --git a/src/models/connect.d.ts b/src/models/connect.d.ts
deleted file mode 100644
index a0696dc..0000000
--- a/src/models/connect.d.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { MenuDataItem } from '@ant-design/pro-layout';
-import { GlobalModelState } from './global';
-import { DefaultSettings as SettingModelState } from '../../config/defaultSettings';
-import { UserModelState } from './user';
-import { StateType } from './login';
-
-export { GlobalModelState, SettingModelState, UserModelState };
-
-export interface Loading {
-  global: boolean;
-  effects: { [key: string]: boolean | undefined };
-  models: {
-    global?: boolean;
-    menu?: boolean;
-    setting?: boolean;
-    user?: boolean;
-    login?: boolean;
-  };
-}
-
-export interface ConnectState {
-  global: GlobalModelState;
-  loading: Loading;
-  settings: SettingModelState;
-  user: UserModelState;
-  login: StateType;
-}
-
-export interface Route extends MenuDataItem {
-  routes?: Route[];
-}
diff --git a/src/models/global.ts b/src/models/global.ts
deleted file mode 100644
index a9432f6..0000000
--- a/src/models/global.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import { Subscription, Effect, Reducer } from 'umi';
-
-import { queryNotices } from '@/services/user';
-import { ConnectState } from './connect.d';
-
-export interface NoticeItem extends API.NoticeIconData {
-  id: string;
-  type: string;
-  status: string;
-}
-
-export interface GlobalModelState {
-  collapsed: boolean;
-  notices: NoticeItem[];
-}
-
-export interface GlobalModelType {
-  namespace: 'global';
-  state: GlobalModelState;
-  effects: {
-    fetchNotices: Effect;
-    clearNotices: Effect;
-    changeNoticeReadState: Effect;
-  };
-  reducers: {
-    changeLayoutCollapsed: Reducer<GlobalModelState>;
-    saveNotices: Reducer<GlobalModelState>;
-    saveClearedNotices: Reducer<GlobalModelState>;
-  };
-  subscriptions: { setup: Subscription };
-}
-
-const GlobalModel: GlobalModelType = {
-  namespace: 'global',
-
-  state: {
-    collapsed: false,
-    notices: [],
-  },
-
-  effects: {
-    *fetchNotices(_, { call, put, select }) {
-      const data = yield call(queryNotices);
-      yield put({
-        type: 'saveNotices',
-        payload: data,
-      });
-      const unreadCount: number = yield select(
-        (state: ConnectState) => state.global.notices.filter((item) => !item.read).length,
-      );
-      yield put({
-        type: 'user/changeNotifyCount',
-        payload: {
-          totalCount: data.length,
-          unreadCount,
-        },
-      });
-    },
-    *clearNotices({ payload }, { put, select }) {
-      yield put({
-        type: 'saveClearedNotices',
-        payload,
-      });
-      const count: number = yield select((state: ConnectState) => state.global.notices.length);
-      const unreadCount: number = yield select(
-        (state: ConnectState) => state.global.notices.filter((item) => !item.read).length,
-      );
-      yield put({
-        type: 'user/changeNotifyCount',
-        payload: {
-          totalCount: count,
-          unreadCount,
-        },
-      });
-    },
-    *changeNoticeReadState({ payload }, { put, select }) {
-      const notices: NoticeItem[] = yield select((state: ConnectState) =>
-        state.global.notices.map((item) => {
-          const notice = { ...item };
-          if (notice.id === payload) {
-            notice.read = true;
-          }
-          return notice;
-        }),
-      );
-
-      yield put({
-        type: 'saveNotices',
-        payload: notices,
-      });
-
-      yield put({
-        type: 'user/changeNotifyCount',
-        payload: {
-          totalCount: notices.length,
-          unreadCount: notices.filter((item) => !item.read).length,
-        },
-      });
-    },
-  },
-
-  reducers: {
-    changeLayoutCollapsed(state = { notices: [], collapsed: true }, { payload }): GlobalModelState {
-      return {
-        ...state,
-        collapsed: payload,
-      };
-    },
-    saveNotices(state, { payload }): GlobalModelState {
-      return {
-        collapsed: false,
-        ...state,
-        notices: payload,
-      };
-    },
-    saveClearedNotices(state = { notices: [], collapsed: true }, { payload }): GlobalModelState {
-      return {
-        collapsed: false,
-        ...state,
-        notices: state.notices.filter((item): boolean => item.type !== payload),
-      };
-    },
-  },
-
-  subscriptions: {
-    setup({ history }): void {
-      // Subscribe history(url) change, trigger `load` action if pathname is `/`
-      history.listen(({ pathname, search }): void => {
-        if (typeof window.ga !== 'undefined') {
-          window.ga('send', 'pageview', pathname + search);
-        }
-      });
-    },
-  },
-};
-
-export default GlobalModel;
diff --git a/src/models/login.ts b/src/models/login.ts
deleted file mode 100644
index 446f5ac..0000000
--- a/src/models/login.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { stringify } from 'querystring';
-import { history, Effect, Reducer } from 'umi';
-
-import { fakeAccountLogin } from '@/services/login';
-import { setAuthority } from '@/utils/authority';
-import { getPageQuery } from '@/utils/utils';
-
-export interface StateType {
-  status?: 'ok' | 'error';
-  type?: string;
-  currentAuthority?: 'user' | 'guest' | 'admin';
-}
-
-export interface LoginModelType {
-  namespace: string;
-  state: StateType;
-  effects: {
-    login: Effect;
-    logout: Effect;
-  };
-  reducers: {
-    changeLoginStatus: Reducer<StateType>;
-  };
-}
-
-const Model: LoginModelType = {
-  namespace: 'login',
-
-  state: {
-    status: undefined,
-  },
-
-  effects: {
-    *login({ payload }, { call, put }) {
-      const response = yield call(fakeAccountLogin, payload);
-      yield put({
-        type: 'changeLoginStatus',
-        payload: response,
-      });
-      // Login successfully
-      if (response.status === 'ok') {
-        const urlParams = new URL(window.location.href);
-        const params = getPageQuery();
-        let { redirect } = params as { redirect: string };
-        if (redirect) {
-          const redirectUrlParams = new URL(redirect);
-          if (redirectUrlParams.origin === urlParams.origin) {
-            redirect = redirect.substr(urlParams.origin.length);
-            if (redirect.match(/^\/.*#/)) {
-              redirect = redirect.substr(redirect.indexOf('#') + 1);
-            }
-          } else {
-            window.location.href = '/';
-            return;
-          }
-        }
-        history.replace(redirect || '/');
-      }
-    },
-
-    logout() {
-      const { redirect } = getPageQuery();
-      // Note: There may be security issues, please note
-      if (window.location.pathname !== '/user/login' && !redirect) {
-        history.replace({
-          pathname: '/user/login',
-          search: stringify({
-            redirect: window.location.href,
-          }),
-        });
-      }
-    },
-  },
-
-  reducers: {
-    changeLoginStatus(state, { payload }) {
-      setAuthority(payload.currentAuthority);
-      return {
-        ...state,
-        status: payload.status,
-        type: payload.type,
-      };
-    },
-  },
-};
-
-export default Model;
diff --git a/src/models/setting.ts b/src/models/setting.ts
deleted file mode 100644
index 59fa70a..0000000
--- a/src/models/setting.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Reducer } from 'umi';
-import defaultSettings, { DefaultSettings } from '../../config/defaultSettings';
-
-export interface SettingModelType {
-  namespace: 'settings';
-  state: DefaultSettings;
-  reducers: {
-    changeSetting: Reducer<DefaultSettings>;
-  };
-}
-
-const updateColorWeak: (colorWeak: boolean) => void = colorWeak => {
-  const root = document.getElementById('root');
-  if (root) {
-    root.className = colorWeak ? 'colorWeak' : '';
-  }
-};
-
-const SettingModel: SettingModelType = {
-  namespace: 'settings',
-  state: defaultSettings,
-  reducers: {
-    changeSetting(state = defaultSettings, { payload }) {
-      const { colorWeak, contentWidth } = payload;
-
-      if (state.contentWidth !== contentWidth && window.dispatchEvent) {
-        window.dispatchEvent(new Event('resize'));
-      }
-      updateColorWeak(!!colorWeak);
-      return {
-        ...state,
-        ...payload,
-      };
-    },
-  },
-};
-export default SettingModel;
diff --git a/src/models/user.ts b/src/models/user.ts
deleted file mode 100644
index ff5160d..0000000
--- a/src/models/user.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { Effect, Reducer } from 'umi';
-
-import { queryCurrent, query as queryUsers } from '@/services/user';
-
-export interface CurrentUser {
-  avatar?: string;
-  name?: string;
-  title?: string;
-  group?: string;
-  signature?: string;
-  tags?: {
-    key: string;
-    label: string;
-  }[];
-  userid?: string;
-  unreadCount?: number;
-}
-
-export interface UserModelState {
-  currentUser?: CurrentUser;
-}
-
-export interface UserModelType {
-  namespace: 'user';
-  state: UserModelState;
-  effects: {
-    fetch: Effect;
-    fetchCurrent: Effect;
-  };
-  reducers: {
-    saveCurrentUser: Reducer<UserModelState>;
-    changeNotifyCount: Reducer<UserModelState>;
-  };
-}
-
-const UserModel: UserModelType = {
-  namespace: 'user',
-
-  state: {
-    currentUser: {},
-  },
-
-  effects: {
-    *fetch(_, { call, put }) {
-      const response = yield call(queryUsers);
-      yield put({
-        type: 'save',
-        payload: response,
-      });
-    },
-    *fetchCurrent(_, { call, put }) {
-      const response = yield call(queryCurrent);
-      yield put({
-        type: 'saveCurrentUser',
-        payload: response,
-      });
-    },
-  },
-
-  reducers: {
-    saveCurrentUser(state, action) {
-      return {
-        ...state,
-        currentUser: action.payload || {},
-      };
-    },
-    changeNotifyCount(
-      state = {
-        currentUser: {},
-      },
-      action,
-    ) {
-      return {
-        ...state,
-        currentUser: {
-          ...state.currentUser,
-          notifyCount: action.payload.totalCount,
-          unreadCount: action.payload.unreadCount,
-        },
-      };
-    },
-  },
-};
-
-export default UserModel;
diff --git a/src/pages/Authorized.tsx b/src/pages/Authorized.tsx
deleted file mode 100644
index 468543d..0000000
--- a/src/pages/Authorized.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react';
-import { Redirect, connect, ConnectProps } from 'umi';
-import Authorized from '@/utils/Authorized';
-import { getRouteAuthority } from '@/utils/utils';
-import { ConnectState, UserModelState } from '@/models/connect';
-
-interface AuthComponentProps extends ConnectProps {
-  user: UserModelState;
-}
-
-const AuthComponent: React.FC<AuthComponentProps> = ({
-  children,
-  route = {
-    routes: [],
-  },
-  location = {
-    pathname: '',
-  },
-  user,
-}) => {
-  const { currentUser } = user;
-  const { routes = [] } = route;
-  const isLogin = currentUser && currentUser.name;
-  return (
-    <Authorized
-      authority={getRouteAuthority(location.pathname, routes) || ''}
-      noMatch={isLogin ? <Redirect to="/exception/403" /> : <Redirect to="/user/login" />}
-    >
-      {children}
-    </Authorized>
-  );
-};
-
-export default connect(({ user }: ConnectState) => ({
-  user,
-}))(AuthComponent);
diff --git a/src/pages/user/login/components/Login/LoginItem.tsx b/src/pages/user/login/components/Login/LoginItem.tsx
index bd027b0..dd591ea 100644
--- a/src/pages/user/login/components/Login/LoginItem.tsx
+++ b/src/pages/user/login/components/Login/LoginItem.tsx
@@ -57,7 +57,7 @@ const getFormItemOptions = ({
   return options;
 };
 
-const LoginItem: React.FC<LoginItemProps> = props => {
+const LoginItem: React.FC<LoginItemProps> = (props) => {
   const [count, setCount] = useState<number>(props.countDown || 0);
   const [timing, setTiming] = useState(false);
   // 这么写是为了防止restProps中 带入 onChange, defaultValue, rules props tabUtil
@@ -89,7 +89,7 @@ const LoginItem: React.FC<LoginItemProps> = props => {
     const { countDown } = props;
     if (timing) {
       interval = window.setInterval(() => {
-        setCount(preSecond => {
+        setCount((preSecond) => {
           if (preSecond <= 1) {
             setTiming(false);
             clearInterval(interval);
@@ -113,7 +113,7 @@ const LoginItem: React.FC<LoginItemProps> = props => {
     const inputProps = omit(otherProps, ['onGetCaptcha', 'countDown']);
 
     return (
-      <FormItem shouldUpdate>
+      <FormItem shouldUpdate noStyle>
         {({ getFieldValue }) => (
           <Row gutter={8}>
             <Col span={16}>
@@ -148,11 +148,11 @@ const LoginItem: React.FC<LoginItemProps> = props => {
 
 const LoginItems: Partial<LoginItemType> = {};
 
-Object.keys(ItemMap).forEach(key => {
+Object.keys(ItemMap).forEach((key) => {
   const item = ItemMap[key];
   LoginItems[key] = (props: LoginItemProps) => (
     <LoginContext.Consumer>
-      {context => (
+      {(context) => (
         <LoginItem
           customProps={item.props}
           rules={item.rules}
diff --git a/src/pages/user/login/components/Login/LoginTab.tsx b/src/pages/user/login/components/Login/LoginTab.tsx
index 935f2fd..adbed2a 100644
--- a/src/pages/user/login/components/Login/LoginTab.tsx
+++ b/src/pages/user/login/components/Login/LoginTab.tsx
@@ -18,7 +18,7 @@ interface LoginTabProps extends TabPaneProps {
   active?: boolean;
 }
 
-const LoginTab: React.FC<LoginTabProps> = props => {
+const LoginTab: React.FC<LoginTabProps> = (props) => {
   useEffect(() => {
     const uniqueId = generateId('login-tab-');
     const { tabUtil } = props;
@@ -32,9 +32,9 @@ const LoginTab: React.FC<LoginTabProps> = props => {
 
 const WrapContext: React.FC<TabPaneProps> & {
   typeName: string;
-} = props => (
+} = (props) => (
   <LoginContext.Consumer>
-    {value => <LoginTab tabUtil={value.tabUtil} {...props} />}
+    {(value) => <LoginTab tabUtil={value.tabUtil} {...props} />}
   </LoginContext.Consumer>
 );
 
diff --git a/src/pages/user/login/components/Login/index.tsx b/src/pages/user/login/components/Login/index.tsx
index 4148c25..c0ea0c5 100644
--- a/src/pages/user/login/components/Login/index.tsx
+++ b/src/pages/user/login/components/Login/index.tsx
@@ -30,10 +30,10 @@ interface LoginType extends React.FC<LoginProps> {
   Captcha: React.FunctionComponent<LoginItemProps>;
 }
 
-const Login: LoginType = props => {
+const Login: LoginType = (props) => {
   const { className } = props;
   const [tabs, setTabs] = useState<string[]>([]);
-  const [active, setActive] = useState();
+  const [active, setActive] = useState({});
   const [type, setType] = useMergeValue('', {
     value: props.activeKey,
     onChange: props.onTabChange,
@@ -57,14 +57,15 @@ const Login: LoginType = props => {
     <LoginContext.Provider
       value={{
         tabUtil: {
-          addTab: id => {
+          addTab: (id) => {
             setTabs([...tabs, id]);
           },
-          removeTab: id => {
-            setTabs(tabs.filter(currentId => currentId !== id));
+          removeTab: (id) => {
+            setTabs(tabs.filter((currentId) => currentId !== id));
           },
         },
-        updateActive: activeItem => {
+        updateActive: (activeItem) => {
+          if (!active) return;
           if (active[type]) {
             active[type].push(activeItem);
           } else {
@@ -77,7 +78,7 @@ const Login: LoginType = props => {
       <div className={classNames(className, styles.login)}>
         <Form
           form={props.from}
-          onFinish={values => {
+          onFinish={(values) => {
             if (props.onSubmit) {
               props.onSubmit(values as LoginParamsType);
             }
@@ -89,7 +90,7 @@ const Login: LoginType = props => {
                 animated={false}
                 className={styles.tabs}
                 activeKey={type}
-                onChange={activeKey => {
+                onChange={(activeKey) => {
                   setType(activeKey);
                 }}
               >
diff --git a/src/pages/user/login/index.tsx b/src/pages/user/login/index.tsx
index 0690290..5010685 100644
--- a/src/pages/user/login/index.tsx
+++ b/src/pages/user/login/index.tsx
@@ -1,19 +1,14 @@
-import { Alert, Checkbox } from 'antd';
+import { AlipayCircleOutlined, TaobaoCircleOutlined, WeiboCircleOutlined } from '@ant-design/icons';
+import { Alert, Checkbox, message } from 'antd';
 import React, { useState } from 'react';
-import { connect, Dispatch, useIntl, FormattedMessage } from 'umi';
-import { StateType } from '@/models/login';
-import { LoginParamsType } from '@/services/login';
-import { ConnectState } from '@/models/connect';
-import LoginForm from './components/Login';
-
+import { Link, SelectLang, history, useModel } from 'umi';
+import { getPageQuery } from '@/utils/utils';
+import logo from '@/assets/logo.svg';
+import { LoginParamsType, fakeAccountLogin } from '@/services/login';
+import LoginFrom from './components/Login';
 import styles from './style.less';
 
-const { Tab, UserName, Password, Submit } = LoginForm;
-interface LoginProps {
-  dispatch: Dispatch;
-  userLogin: StateType;
-  submitting?: boolean;
-}
+const { Tab, UserName, Password, Mobile, Captcha, Submit } = LoginFrom;
 
 const LoginMessage: React.FC<{
   content: string;
@@ -28,67 +23,162 @@ const LoginMessage: React.FC<{
   />
 );
 
-const Login: React.FC<LoginProps> = (props) => {
-  const { userLogin = {}, submitting } = props;
-  const { status, type: loginType } = userLogin;
+/**
+ * 此方法会跳转到 redirect 参数所在的位置
+ */
+const replaceGoto = () => {
+  const urlParams = new URL(window.location.href);
+  const params = getPageQuery();
+  let { redirect } = params as { redirect: string };
+  if (redirect) {
+    const redirectUrlParams = new URL(redirect);
+    if (redirectUrlParams.origin === urlParams.origin) {
+      redirect = redirect.substr(urlParams.origin.length);
+      if (redirect.match(/^\/.*#/)) {
+        redirect = redirect.substr(redirect.indexOf('#') + 1);
+      }
+    } else {
+      window.location.href = '/';
+      return;
+    }
+  }
+  history.replace(redirect || '/');
+};
+
+const Login: React.FC<{}> = () => {
+  const [userLoginState, setUserLoginState] = useState<API.LoginStateType>({});
+  const [submitting, setSubmitting] = useState(false);
+
+  const { refresh } = useModel('@@initialState');
   const [autoLogin, setAutoLogin] = useState(true);
-  const [type, setType] = useState('account');
-  const { formatMessage } = useIntl();
+  const [type, setType] = useState<string>('account');
 
-  const handleSubmit = (values: LoginParamsType) => {
-    const { dispatch } = props;
-    dispatch({
-      type: 'login/login',
-      payload: { ...values, type },
-    });
+  const handleSubmit = async (values: LoginParamsType) => {
+    setSubmitting(true);
+    try {
+      // 登录
+      const msg = await fakeAccountLogin({ ...values, type });
+      if (msg.status === 'ok') {
+        message.success('登陆成功!');
+        replaceGoto();
+        setTimeout(() => {
+          refresh();
+        }, 0);
+        return;
+      }
+      // 如果失败去设置用户错误信息
+      setUserLoginState(msg);
+    } catch (error) {
+      message.error('登陆失败,请重试!');
+    }
+    setSubmitting(false);
   };
+
+  const { status, type: loginType } = userLoginState;
+
   return (
-    <div className={styles.main}>
-      <LoginForm activeKey={type} onTabChange={setType} onSubmit={handleSubmit}>
-        <Tab key="account" tab={formatMessage({ id: 'component.user.loginByPassword' })}>
-          {status === 'error' && loginType === 'account' && !submitting && (
-            <LoginMessage
-              content={formatMessage({ id: 'component.user.wrongUsernameOrPassword' })}
-            />
-          )}
+    <div className={styles.container}>
+      <div className={styles.lang}>
+        <SelectLang />
+      </div>
+      <div className={styles.content}>
+        <div className={styles.top}>
+          <div className={styles.header}>
+            <Link to="/">
+              <img alt="logo" className={styles.logo} src={logo} />
+              <span className={styles.title}>Ant Design</span>
+            </Link>
+          </div>
+          <div className={styles.desc}>Ant Design 是西湖区最具影响力的 Web 设计规范</div>
+        </div>
+
+        <div className={styles.main}>
+          <LoginFrom activeKey={type} onTabChange={setType} onSubmit={handleSubmit}>
+            <Tab key="account" tab="账户密码登录">
+              {status === 'error' && loginType === 'account' && !submitting && (
+                <LoginMessage content="账户或密码错误(admin/ant.design)" />
+              )}
 
-          <UserName
-            name="userName"
-            placeholder={formatMessage({ id: 'component.user.username' })}
-            defaultValue="admin"
-            rules={[
-              {
-                required: true,
-                message: formatMessage({ id: 'component.user.inputUsername' }),
-              },
-            ]}
-          />
-          <Password
-            name="password"
-            placeholder={formatMessage({ id: 'component.user.password' })}
-            defaultValue="123456"
-            rules={[
-              {
-                required: true,
-                message: formatMessage({ id: 'component.user.inputPassword' }),
-              },
-            ]}
-          />
-        </Tab>
-        <div>
-          <Checkbox checked={autoLogin} onChange={(e) => setAutoLogin(e.target.checked)}>
-            <FormattedMessage id="component.user.rememberMe" />
-          </Checkbox>
+              <UserName
+                name="userName"
+                placeholder="用户名: admin or user"
+                rules={[
+                  {
+                    required: true,
+                    message: '请输入用户名!',
+                  },
+                ]}
+              />
+              <Password
+                name="password"
+                placeholder="密码: ant.design"
+                rules={[
+                  {
+                    required: true,
+                    message: '请输入密码!',
+                  },
+                ]}
+              />
+            </Tab>
+            <Tab key="mobile" tab="手机号登录">
+              {status === 'error' && loginType === 'mobile' && !submitting && (
+                <LoginMessage content="验证码错误" />
+              )}
+              <Mobile
+                name="mobile"
+                placeholder="手机号"
+                rules={[
+                  {
+                    required: true,
+                    message: '请输入手机号!',
+                  },
+                  {
+                    pattern: /^1\d{10}$/,
+                    message: '手机号格式错误!',
+                  },
+                ]}
+              />
+              <Captcha
+                name="captcha"
+                placeholder="验证码"
+                countDown={120}
+                getCaptchaButtonText=""
+                getCaptchaSecondText="秒"
+                rules={[
+                  {
+                    required: true,
+                    message: '请输入验证码!',
+                  },
+                ]}
+              />
+            </Tab>
+            <div>
+              <Checkbox checked={autoLogin} onChange={(e) => setAutoLogin(e.target.checked)}>
+                自动登录
+              </Checkbox>
+              <a
+                style={{
+                  float: 'right',
+                }}
+              >
+                忘记密码
+              </a>
+            </div>
+            <Submit loading={submitting}>登录</Submit>
+            <div className={styles.other}>
+              其他登录方式
+              <AlipayCircleOutlined className={styles.icon} />
+              <TaobaoCircleOutlined className={styles.icon} />
+              <WeiboCircleOutlined className={styles.icon} />
+              <Link className={styles.register} to="/user/register">
+                注册账户
+              </Link>
+            </div>
+          </LoginFrom>
         </div>
-        <Submit loading={submitting}>
-          <FormattedMessage id="component.user.login" />
-        </Submit>
-      </LoginForm>
+      </div>
     </div>
   );
 };
 
-export default connect(({ login, loading }: ConnectState) => ({
-  userLogin: login,
-  submitting: loading.effects['login/login'],
-}))(Login);
+export default Login;
diff --git a/src/pages/user/login/style.less b/src/pages/user/login/style.less
index d9bbbf3..64358ec 100644
--- a/src/pages/user/login/style.less
+++ b/src/pages/user/login/style.less
@@ -1,5 +1,75 @@
 @import '~antd/es/style/themes/default.less';
 
+.container {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  overflow: auto;
+  background: @layout-body-background;
+}
+
+.lang {
+  width: 100%;
+  height: 40px;
+  line-height: 44px;
+  text-align: right;
+  :global(.ant-dropdown-trigger) {
+    margin-right: 24px;
+  }
+}
+
+.content {
+  flex: 1;
+  padding: 32px 0;
+}
+
+@media (min-width: @screen-md-min) {
+  .container {
+    background-image: url('https://gw.alipayobjects.com/zos/rmsportal/TVYTbAXWheQpRcWDaDMu.svg');
+    background-repeat: no-repeat;
+    background-position: center 110px;
+    background-size: 100%;
+  }
+
+  .content {
+    padding: 32px 0 24px;
+  }
+}
+
+.top {
+  text-align: center;
+}
+
+.header {
+  height: 44px;
+  line-height: 44px;
+  a {
+    text-decoration: none;
+  }
+}
+
+.logo {
+  height: 44px;
+  margin-right: 16px;
+  vertical-align: top;
+}
+
+.title {
+  position: relative;
+  top: 2px;
+  color: @heading-color;
+  font-weight: 600;
+  font-size: 33px;
+  font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
+}
+
+.desc {
+  margin-top: 12px;
+  margin-bottom: 40px;
+  color: @text-color-secondary;
+  font-size: @font-size-base;
+}
+
 .main {
   width: 368px;
   margin: 0 auto;
diff --git a/src/utils/Authorized.ts b/src/utils/Authorized.ts
deleted file mode 100644
index e3b813a..0000000
--- a/src/utils/Authorized.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import RenderAuthorize from '../components/Authorized';
-import { getAuthority } from './authority';
-/* eslint-disable eslint-comments/disable-enable-pair */
-/* eslint-disable import/no-mutable-exports */
-let Authorized = RenderAuthorize(getAuthority());
-
-// Reload the rights component
-const reloadAuthorized = (): void => {
-  Authorized = RenderAuthorize(getAuthority());
-};
-
-/**
- * hard code
- * block need it。
- */
-window.reloadAuthorized = reloadAuthorized;
-
-export { reloadAuthorized };
-export default Authorized;
diff --git a/src/utils/authority.ts b/src/utils/authority.ts
deleted file mode 100644
index d99659d..0000000
--- a/src/utils/authority.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { reloadAuthorized } from './Authorized';
-
-// use localStorage to store the authority info, which might be sent from server in actual project.
-export function getAuthority(str?: string): string | string[] {
-  const authorityString =
-    typeof str === 'undefined' && localStorage ? localStorage.getItem('antd-pro-authority') : str;
-  // authorityString could be admin, "admin", ["admin"]
-  let authority;
-  try {
-    if (authorityString) {
-      authority = JSON.parse(authorityString);
-    }
-  } catch (e) {
-    authority = authorityString;
-  }
-  if (typeof authority === 'string') {
-    return [authority];
-  }
-  // preview.pro.ant.design only do not use in your production.
-  // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
-  if (!authority && ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
-    return ['admin'];
-  }
-  return authority;
-}
-
-export function setAuthority(authority: string | string[]): void {
-  const proAuthority = typeof authority === 'string' ? [authority] : authority;
-  localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority));
-  // auto reload
-  reloadAuthorized();
-}


[incubator-apisix-dashboard] 02/02: feat: added initial state

Posted by ju...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

juzhiyuan pushed a commit to branch feat-layout
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git

commit 311f2d363f855a818a09d11d037c6901596b83ac
Author: juzhiyuan <jj...@gmail.com>
AuthorDate: Thu May 21 11:37:12 2020 +0800

    feat: added initial state
---
 src/app.tsx | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/src/app.tsx b/src/app.tsx
index 7a5b6af..e94290a 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -1,11 +1,34 @@
 import React from 'react';
 import { notification } from 'antd';
-import { RequestConfig } from 'umi';
+import { RequestConfig, history } from 'umi';
 import { BasicLayoutProps, Settings as LayoutSettings } from '@ant-design/pro-layout';
 
 import { getAdminAPIConfig } from '@/utils/setting';
 import RightContent from '@/components/RightContent';
 import Footer from '@/components/Footer';
+import { queryCurrent } from '@/services/user';
+import defaultSettings from '../config/defaultSettings';
+
+export async function getInitialState(): Promise<{
+  currentUser?: API.CurrentUser;
+  settings?: LayoutSettings;
+}> {
+  // 如果是登录页面,不执行
+  if (history.location.pathname !== '/user/login') {
+    try {
+      const currentUser = await queryCurrent();
+      return {
+        currentUser,
+        settings: defaultSettings,
+      };
+    } catch (error) {
+      history.push('/user/login');
+    }
+  }
+  return {
+    settings: defaultSettings,
+  };
+}
 
 export const layout = ({
   initialState,