Explorar el Código

add top_meun and siderbar

jim hace 7 años
padre
commit
91bfd88ddc

+ 1 - 1
src/common/menu.js

@@ -122,7 +122,7 @@ const menuData = [{
     path: 'user-center',
     path: 'user-center',
   }, {
   }, {
     name: '个人设置',
     name: '个人设置',
-    path: 'userinfo',
+    path: 'userinfo/base',
   }],
   }],
 }];
 }];
 
 

+ 1 - 1
src/common/router.js

@@ -71,7 +71,7 @@ function getFlatMenuData(menus) {
 export const getRouterData = (app) => {
 export const getRouterData = (app) => {
   const routerConfig = {
   const routerConfig = {
     '/': {
     '/': {
-      component: dynamicWrapper(app, ['user', 'login'], () => import('../layouts/BasicLayout')),
+      component: dynamicWrapper(app, ['user', 'login', 'setting'], () => import('../layouts/LoadingPage')),
     },
     },
     '/dashboard/analysis': {
     '/dashboard/analysis': {
       component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')),
       component: dynamicWrapper(app, ['chart'], () => import('../routes/Dashboard/Analysis')),

+ 2 - 2
src/components/GlobalHeader/RightContent.js

@@ -65,8 +65,8 @@ export default class GlobalHeaderRight extends PureComponent {
     );
     );
     const noticeData = this.getNoticeData();
     const noticeData = this.getNoticeData();
     let className = styles.right;
     let className = styles.right;
-    if (this.props.theme === 'white') {
-      className = `${styles.right}  ${styles.white}`;
+    if (this.props.theme === 'dark') {
+      className = `${styles.right}  ${styles.dark}`;
     }
     }
     return (
     return (
       <div className={className} >
       <div className={className} >

+ 1 - 1
src/components/GlobalHeader/index.less

@@ -84,7 +84,7 @@ i.trigger {
   }
   }
 }
 }
 
 
-.white {
+.dark {
   height: 64px;
   height: 64px;
   .action {
   .action {
     color: rgba(255, 255, 255, 0.85);
     color: rgba(255, 255, 255, 0.85);

+ 1 - 1
src/components/NoticeIcon/index.js

@@ -70,7 +70,7 @@ export default class NoticeIcon extends PureComponent {
     const notificationBox = this.getNotificationBox();
     const notificationBox = this.getNotificationBox();
     const trigger = (
     const trigger = (
       <span className={noticeButtonClass}>
       <span className={noticeButtonClass}>
-        <Badge count={count} className={styles.badge}>
+        <Badge count={count} style={{ boxShadow: 'none' }} className={styles.badge}>
           <Icon type="bell" className={styles.icon} />
           <Icon type="bell" className={styles.icon} />
         </Badge>
         </Badge>
       </span>
       </span>

+ 45 - 0
src/components/Sidebar/LayoutSetting.js

@@ -0,0 +1,45 @@
+import React from 'react';
+import NavSate from './navState';
+
+const LayoutSetting = ({ value, onChange }) => {
+  return (
+    <div
+      style={{
+        margin: 5,
+        display: 'flex',
+      }}
+    >
+      {['sidemenu', 'topmenu'].map(layout => (
+        <div
+          onClick={() => onChange && onChange(layout)}
+          key={layout}
+          style={{
+            width: 70,
+            height: 44,
+            textAlign: 'center',
+            margin: 8,
+          }}
+        >
+          <NavSate
+            type={layout}
+            state={value === layout ? 'active' : 'default'}
+            alt={layout}
+          />
+        </div>
+      ))}
+      <div
+        key="topside"
+        style={{
+          width: 70,
+          height: 44,
+          textAlign: 'center',
+          margin: 8,
+        }}
+      >
+        <NavSate type="topside" state="disable" alt="topside" />
+      </div>
+    </div>
+  );
+};
+
+export default LayoutSetting;

+ 55 - 0
src/components/Sidebar/ThemeColor.js

@@ -0,0 +1,55 @@
+import React from 'react';
+import styles from './ThemeColor.less';
+
+const Tag = ({ color, ...rest }) => {
+  return (
+    <div
+      {...rest}
+      style={{
+        backgroundColor: color,
+      }}
+    />
+  );
+};
+
+const ThemeColor = ({ colors, value, onChange }) => {
+  let colorList = colors;
+  if (!colors) {
+    colorList = [
+      '#F5222D',
+      '#FA541C',
+      '#FA8C16',
+      '#FAAD14',
+      '#FADB14',
+      '#A0D911',
+      '#52C41A',
+      '#13C2C2',
+      '#1890FF',
+      '#2F54EB',
+      '#722ED1',
+      '#EB2F96',
+    ];
+  }
+
+  return (
+    <div className={styles.themeColor}>
+      <h3 className={styles.title}>主题颜色</h3>
+      {colorList.map((color) => {
+        const classname =
+          value === color
+            ? `${styles.colorBlock} ${styles.active}`
+            : styles.colorBlock;
+        return (
+          <Tag
+            className={classname}
+            key={color}
+            color={color}
+            onClick={() => onChange && onChange(color)}
+          />
+        );
+      })}
+    </div>
+  );
+};
+
+export default ThemeColor;

+ 26 - 0
src/components/Sidebar/ThemeColor.less

@@ -0,0 +1,26 @@
+.themeColor {
+  overflow: hidden;
+  margin-top: 15px;
+  margin-left: -5px;
+  margin-right: -5px;
+  .title {
+    font-size: 14px;
+    color: rgba(0, 0, 0, 0.65);
+    line-height: 22px;
+    margin-bottom: 5px;
+  }
+  .colorBlock {
+    width: 16px;
+    height: 16px;
+    border-radius: 2px;
+    float: left;
+    margin: 5px;
+    &.active {
+      width: 18px;
+      height: 18px;
+      margin: 4px;
+      border: 2px solid  hsl(90, 100%, 50%);
+      box-shadow: 0 0 4px 0 hsl(90, 100%, 50%);
+    }
+  }
+}

+ 218 - 0
src/components/Sidebar/index.js

@@ -0,0 +1,218 @@
+import React, { PureComponent, Fragment } from 'react';
+import { Select, List, Switch, Divider, Radio } from 'antd';
+import styles from './index.less';
+import ThemeColor from './ThemeColor';
+import LayoutSeting from './LayoutSetting';
+
+const RadioGroup = Radio.Group;
+
+const ColorBlock = ({ color, title }) => (
+  <Fragment>
+    <div
+      className={styles.color_block}
+      style={{
+        backgroundColor: color,
+      }}
+    />
+    <div className={styles.color_block_title}>{title}</div>
+  </Fragment>
+);
+
+const Body = ({ children, title, style }) => (
+  <div
+    style={{
+      padding: 15,
+      ...style,
+    }}
+  >
+    <h3 className={styles.bodyTitle}>{title}</h3>
+    {children}
+  </div>
+);
+
+class Sidebar extends PureComponent {
+  constructor(props) {
+    super(props);
+    this.defaultstate = {
+      collapse: false,
+      silderTheme: 'dark',
+      themeColor: '#1890FF',
+      layout: 'sidemenu',
+      grid: 'Fluid',
+      fixedHeader: false,
+      autoHideHeader: false,
+      fixSiderbar: false,
+      colorWeak: 'close',
+    };
+    const propsState = this.propsToState(props);
+    this.state = { ...this.defaultstate, ...propsState };
+  }
+  componentWillReceiveProps(props) {
+    this.setState(this.propsToState(props));
+  }
+  getLayOutSetting = () => {
+    const { layout } = this.state;
+    return [
+      {
+        title: '栅格模式',
+        isShow: true,
+        action: [
+          <Select
+            value={this.state.grid}
+            onSelect={value => this.changeSetting('grid', value)}
+            style={{ width: 120 }}
+          >
+            <Select.Option value="Wide">Wide</Select.Option>
+            <Select.Option value="Fluid">Fluid</Select.Option>
+          </Select>,
+        ],
+      },
+      {
+        title: 'Fixed Header',
+        isShow: true,
+        action: [
+          <Switch
+            checked={!!this.state.fixedHeader}
+            onChange={checked => this.changeSetting('fixedHeader', checked)}
+          />,
+        ],
+      },
+      {
+        title: '↳ 下滑时隐藏 Header',
+        isShow: true,
+        action: [
+          <Switch
+            checked={!!this.state.autoHideHeader}
+            onChange={checked => this.changeSetting('autoHideHeader', checked)}
+          />,
+        ],
+      },
+      {
+        title: 'Fix Siderbar',
+        isShow: layout === 'sidemenu',
+        action: [
+          <Switch
+            checked={!!this.state.fixSiderbar}
+            onChange={this.fixSiderbar}
+          />,
+        ],
+      },
+    ].filter(item => item.isShow);
+  };
+  fixSiderbar = (checked) => {
+    this.changeSetting('fixSiderbar', checked);
+  };
+  changeSetting = (key, value) => {
+    const nextState = {};
+    nextState[key] = value;
+    if (key === 'layout') {
+      if (value === 'topmenu') {
+        nextState.grid = 'Wide';
+      } else {
+        nextState.grid = 'Fluid';
+      }
+    }
+    this.setState(nextState, () => {
+      if (this.props.onChange) {
+        this.props.onChange(this.state);
+      }
+    });
+  };
+  propsToState = (props) => {
+    const nextState = {};
+    Object.keys(props).forEach((key) => {
+      if (props[key] && this.defaultstate[key] !== undefined) {
+        nextState[key] = props[key];
+      }
+    });
+    return nextState;
+  };
+  togglerContent = () => {
+    this.changeSetting('collapse', !this.state.collapse);
+  };
+  render() {
+    const radioStyle = {
+      display: 'block',
+    };
+    return (
+      <div
+        className={`${styles.sidebar} ${
+          this.state.collapse ? styles.show : ''
+        }`}
+      >
+        <div className={styles.mini_bar} onClick={this.togglerContent}>
+          <img
+            alt="logo"
+            src="https://gw.alipayobjects.com/zos/rmsportal/ApQgLmeZDNJMomKNvavq.svg"
+          />
+        </div>
+        <div className={styles.content}>
+          <Body
+            title="整体风格设置"
+            style={{
+              paddingBottom: 10,
+            }}
+          >
+            <RadioGroup
+              onChange={({ target }) =>
+                this.changeSetting('silderTheme', target.value)
+              }
+              value={this.state.silderTheme}
+            >
+              <Radio style={radioStyle} value="dark">
+                <ColorBlock color="#002140" title="深色导航" />
+              </Radio>
+              <Radio style={radioStyle} value="ligth">
+                <ColorBlock color="#E9E9E9" title="浅色导航" />
+              </Radio>
+            </RadioGroup>
+            <ThemeColor
+              value={this.state.themeColor}
+              onChange={color => this.changeSetting('themeColor', color)}
+            />
+          </Body>
+          <Divider style={{ margin: 0 }} />
+          <Body title="导航设置 ">
+            <LayoutSeting
+              value={this.state.layout}
+              onChange={layout => this.changeSetting('layout', layout)}
+            />
+            <List
+              split={false}
+              dataSource={this.getLayOutSetting()}
+              renderItem={item => (
+                <List.Item actions={item.action}>{item.title}</List.Item>
+              )}
+            />
+          </Body>
+          <Divider style={{ margin: 0 }} />
+          <Body title="其他设置">
+            <List
+              split={false}
+              dataSource={[
+                {
+                  title: '色弱模式',
+                  action: [
+                    <Select
+                      value={this.state.colorWeak}
+                      onSelect={value => this.changeSetting('colorWeak', value)}
+                      style={{ width: 120 }}
+                    >
+                      <Select.Option value="open">打开</Select.Option>
+                      <Select.Option value="colse">关闭</Select.Option>
+                    </Select>,
+                  ],
+                },
+              ]}
+              renderItem={item => (
+                <List.Item actions={item.action}>{item.title}</List.Item>
+              )}
+            />
+          </Body>
+        </div>
+      </div>
+    );
+  }
+}
+
+export default Sidebar;

+ 62 - 0
src/components/Sidebar/index.less

@@ -0,0 +1,62 @@
+.sidebar {
+  position: fixed;
+  right: -336px;
+  top: 150px;
+  transition: 0.3s;
+  display: flex;
+  z-index: 9;
+  &.show {
+    right: 0;
+  }
+  .mini_bar {
+    width: 50px;
+    height: 45px;
+    border-top-left-radius: 28px;
+    border-bottom-left-radius: 28px;
+    text-align: center;
+    font-size: 24px;
+    background: white;
+    box-shadow: 0 0 6px 0 rgba(0, 21, 41, 0.35);
+    img {
+      width: 28px;
+      height: 28px;
+    }
+  }
+  .content {
+    width: 336px;
+    background: white;
+    box-shadow: 0 0 6px 0 rgba(0, 21, 41, 0.35);
+    box-shadow: -3px 0 10px 3px rgba(183, 183, 183, 0.3);
+    :global {
+      .ant-switch-checked {
+        background-color: #87d068;
+      }
+      .ant-list-item {
+        padding-top: 7px;
+        padding-bottom: 7px;
+      }
+    }
+  }
+  .color_block {
+    width: 38px;
+    height: 22px;
+    margin: 4px;
+    margin-right: 12px;
+    display: inline-block;
+    vertical-align: middle;
+    background: #002140;
+    border-radius: 2px;
+  }
+  .color_block_title {
+    display: inline-block;
+    font-size: 14px;
+    color: rgba(0, 0, 0, 0.65);
+    line-height: 22px;
+  }
+  .bodyTitle {
+    font-size: 14px;
+    color: rgba(0, 0, 0, 0.85);
+    line-height: 22px;
+    margin-bottom: 10px;
+  }
+}

+ 35 - 0
src/components/Sidebar/navState.js

@@ -0,0 +1,35 @@
+import React from 'react';
+
+const UrlMap = {
+  sidemenu: {
+    active:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/YcoRFFREHxmXebXryzhC.svg',
+    default:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/FUcluUxklaHEgbCfosWO.svg',
+    disable:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/nnbGSviuREntXtWJEquJ.svg',
+  },
+  topside: {
+    active:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/cRVYqCCkknDizjyHpjgR.svg',
+    default:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/ygeNiTUuhrHuNMjkVQkv.svg',
+    disable:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/KtcgueNbQdMSbxGDigdT.svg',
+  },
+  topmenu: {
+    active:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/RCtKLhlMLWYKaWDzLsBC.svg',
+    default:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/pMRBYBPSKXgEGrglJsdl.svg',
+    disable:
+      'http://alipay-os.oss-cn-hangzhou-zmf.aliyuncs.com/rmsportal/PvQhRIcAGTKewuOfJOSn.svg',
+  },
+};
+
+const navState = ({ alt, type, state }) => {
+  const url = UrlMap[type][state];
+  return <img src={url} alt={alt} />;
+};
+
+export default navState;

+ 0 - 1
src/components/SiderMenu/BaseMeun.js

@@ -161,7 +161,6 @@ export default class BaseMeun extends PureComponent {
     return (
     return (
       <Menu
       <Menu
         key="Menu"
         key="Menu"
-        theme="dark"
         mode="inline"
         mode="inline"
         onOpenChange={this.props.handleOpenChange}
         onOpenChange={this.props.handleOpenChange}
         selectedKeys={selectedKeys}
         selectedKeys={selectedKeys}

+ 10 - 5
src/components/SiderMenu/SiderMenu.js

@@ -5,7 +5,6 @@ import styles from './index.less';
 import BaseMeun, { getMeunMatcheys } from './BaseMeun';
 import BaseMeun, { getMeunMatcheys } from './BaseMeun';
 import { urlToList } from '../utils/pathTools';
 import { urlToList } from '../utils/pathTools';
 
 
-
 const { Sider } = Layout;
 const { Sider } = Layout;
 
 
 export default class SiderMenu extends PureComponent {
 export default class SiderMenu extends PureComponent {
@@ -52,6 +51,12 @@ export default class SiderMenu extends PureComponent {
       })
       })
       .filter(item => item);
       .filter(item => item);
   }
   }
+  isMainMenu = (key) => {
+    return this.menus.some(
+      item =>
+        key && (item.key === key || item.path === key),
+    );
+  }
   handleOpenChange = (openKeys) => {
   handleOpenChange = (openKeys) => {
     const lastOpenKey = openKeys[openKeys.length - 1];
     const lastOpenKey = openKeys[openKeys.length - 1];
     const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;
     const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;
@@ -60,8 +65,9 @@ export default class SiderMenu extends PureComponent {
     });
     });
   };
   };
   render() {
   render() {
-    const { logo, collapsed, onCollapse } = this.props;
+    const { logo, collapsed, onCollapse, theme } = this.props;
     const { openKeys } = this.state;
     const { openKeys } = this.state;
+    const defaultProps = collapsed ? {} : { openKeys };
     return (
     return (
       <Sider
       <Sider
         trigger={null}
         trigger={null}
@@ -70,7 +76,7 @@ export default class SiderMenu extends PureComponent {
         breakpoint="lg"
         breakpoint="lg"
         onCollapse={onCollapse}
         onCollapse={onCollapse}
         width={256}
         width={256}
-        className={styles.sider}
+        className={`${styles.sider} ${theme === 'ligth' ? styles.ligth : ''}`}
       >
       >
         <div className={styles.logo} key="logo">
         <div className={styles.logo} key="logo">
           <Link to="/">
           <Link to="/">
@@ -81,12 +87,11 @@ export default class SiderMenu extends PureComponent {
         <BaseMeun
         <BaseMeun
           {...this.props}
           {...this.props}
           key="Menu"
           key="Menu"
-          theme="dark"
           mode="inline"
           mode="inline"
           handleOpenChange={this.handleOpenChange}
           handleOpenChange={this.handleOpenChange}
-          openKeys={collapsed ? [] : openKeys}
           onOpenChange={this.handleOpenChange}
           onOpenChange={this.handleOpenChange}
           style={{ padding: '16px 0', width: '100%' }}
           style={{ padding: '16px 0', width: '100%' }}
+          {...defaultProps}
         />
         />
       </Sider>
       </Sider>
     );
     );

+ 1 - 1
src/components/SiderMenu/index.js

@@ -4,7 +4,7 @@ import DrawerMenu from 'rc-drawer-menu';
 import SiderMenu from './SiderMenu';
 import SiderMenu from './SiderMenu';
 
 
 export default props => (
 export default props => (
-  props.isMobile ? (
+  props.isMobile || props.fixSiderbar ? (
     <DrawerMenu
     <DrawerMenu
       parent={null}
       parent={null}
       level={null}
       level={null}

+ 14 - 5
src/components/SiderMenu/index.less

@@ -1,11 +1,11 @@
-@import "~antd/lib/style/themes/default.less";
-@ease-in-out-circ: cubic-bezier(.78, .14, .15, .86);
+@import '~antd/lib/style/themes/default.less';
+@ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86);
 .logo {
 .logo {
   height: 64px;
   height: 64px;
   position: relative;
   position: relative;
   line-height: 64px;
   line-height: 64px;
   padding-left: (@menu-collapsed-width - 32px) / 2;
   padding-left: (@menu-collapsed-width - 32px) / 2;
-  transition: all .3s;
+  transition: all 0.3s;
   background: #002140;
   background: #002140;
   overflow: hidden;
   overflow: hidden;
   img {
   img {
@@ -14,7 +14,7 @@
     height: 32px;
     height: 32px;
   }
   }
   h1 {
   h1 {
-    color: #fff;
+    color: white;
     display: inline-block;
     display: inline-block;
     vertical-align: middle;
     vertical-align: middle;
     font-size: 20px;
     font-size: 20px;
@@ -26,9 +26,18 @@
 
 
 .sider {
 .sider {
   min-height: 100vh;
   min-height: 100vh;
-  box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
+  box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
   position: relative;
   position: relative;
   z-index: 10;
   z-index: 10;
+  &.ligth {
+    background-color: white;
+    .logo {
+      background: white;
+      h1 {
+        color: #002140;
+      }
+    }
+  }
 }
 }
 
 
 .icon {
 .icon {

+ 24 - 11
src/components/TopNavHeader/index.js

@@ -7,18 +7,31 @@ import styles from './index.less';
 export default class TopNavHeader extends PureComponent {
 export default class TopNavHeader extends PureComponent {
   render() {
   render() {
     return (
     return (
-      <div className={styles.main}>
-        <div className={styles.left}>
-          <div className={styles.logo} key="logo">
-            <Link to="/">
-              <img src={this.props.logo} alt="logo" />
-              <h1>Ant Design Pro</h1>
-            </Link>
+      <div
+        className={`${styles.head} ${
+          this.props.theme === 'ligth' ? styles.ligth : ''
+        }`}
+      >
+        <div
+          className={`${styles.main} ${
+            this.props.grid === 'Wide' ? styles.wide : ''
+          }`}
+        >
+          <div className={styles.left}>
+            <div className={styles.logo} key="logo">
+              <Link to="/">
+                <img src={this.props.logo} alt="logo" />
+                <h1>Ant Design Pro</h1>
+              </Link>
+            </div>
+            <BaseMeun
+              {...this.props}
+              style={{ padding: '9px 0', border: 'none' }}
+            />
+          </div>
+          <div className={styles.right}>
+            <RightContent {...this.props} />
           </div>
           </div>
-          <BaseMeun {...this.props} style={{ padding: '9px 0' }} />
-        </div>
-        <div className={styles.right}>
-          <RightContent theme="white" {...this.props} />
         </div>
         </div>
       </div>
       </div>
     );
     );

+ 48 - 26
src/components/TopNavHeader/index.less

@@ -1,31 +1,53 @@
-.main {
-  display: flex;
+.head {
+  width: 100%;
+  transition: background 0.3s, width 0.2s;
   height: 64px;
   height: 64px;
-  margin: auto;
-  max-width: 1200px;
-  .left {
-    flex: 1;
+  padding: 0 12px 0 0;
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+  position: relative;
+  &.ligth {
+    background-color: #fff;
+  }
+  .main {
     display: flex;
     display: flex;
-    .logo {
-      width: 160px;
-      height: 64px;
-      position: relative;
-      line-height: 64px;
-      transition: all 0.3s;
-      overflow: hidden;
-      img {
-        display: inline-block;
-        vertical-align: middle;
-        height: 32px;
-      }
-      h1 {
-        color: #fff;
-        display: inline-block;
-        vertical-align: middle;
-        font-size: 16px;
-        margin: 0 0 0 12px;
-        font-weight: 400;
-      }
+    height: 64px;
+    padding-left: 24px;
+    &.wide {
+      max-width: 1200px;
+      margin: auto;
+      padding-left: 4px;
+    }
+    .left {
+      flex: 1;
+      display: flex;
     }
     }
   }
   }
 }
 }
+
+.logo {
+  width: 160px;
+  height: 64px;
+  position: relative;
+  line-height: 64px;
+  transition: all 0.3s;
+  overflow: hidden;
+  img {
+    display: inline-block;
+    vertical-align: middle;
+    height: 32px;
+  }
+  h1 {
+    color: #fff;
+    display: inline-block;
+    vertical-align: middle;
+    font-size: 16px;
+    margin: 0 0 0 12px;
+    font-weight: 400;
+  }
+}
+
+.ligth {
+  h1 {
+    color: #002140;
+  }
+}

+ 1 - 0
src/index.less

@@ -2,6 +2,7 @@ html,
 body,
 body,
 :global(#root) {
 :global(#root) {
   height: 100%;
   height: 100%;
+  overflow: auto;
 }
 }
 
 
 :global(.ant-layout) {
 :global(.ant-layout) {

+ 58 - 170
src/layouts/BasicLayout.js

@@ -1,43 +1,24 @@
-import React, { Fragment } from 'react';
+import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { Layout, Icon, message } from 'antd';
+import { Layout } from 'antd';
 import DocumentTitle from 'react-document-title';
 import DocumentTitle from 'react-document-title';
 import { connect } from 'dva';
 import { connect } from 'dva';
-import { Route, Redirect, Switch, routerRedux } from 'dva/router';
+import { Route, Redirect, Switch } from 'dva/router';
 import { ContainerQuery } from 'react-container-query';
 import { ContainerQuery } from 'react-container-query';
 import classNames from 'classnames';
 import classNames from 'classnames';
-import { enquireScreen } from 'enquire-js';
-import GlobalHeader from '../components/GlobalHeader';
-import GlobalFooter from '../components/GlobalFooter';
-import TopNavHeader from '../components/TopNavHeader';
 import SiderMenu from '../components/SiderMenu';
 import SiderMenu from '../components/SiderMenu';
 import NotFound from '../routes/Exception/404';
 import NotFound from '../routes/Exception/404';
 import { getRoutes } from '../utils/utils';
 import { getRoutes } from '../utils/utils';
 import Authorized from '../utils/Authorized';
 import Authorized from '../utils/Authorized';
-import { getMenuData } from '../common/menu';
+import Sidebar from '../components/Sidebar';
 import logo from '../assets/logo.svg';
 import logo from '../assets/logo.svg';
+import Footer from './Footer';
+import Header from './Header';
 
 
-const { Content, Header, Footer } = Layout;
+const { Content } = Layout;
 const { AuthorizedRoute } = Authorized;
 const { AuthorizedRoute } = Authorized;
 
 
-/**
- * 根据菜单取得重定向地址.
- */
-const redirectData = [];
-const getRedirect = (item) => {
-  if (item && item.children) {
-    if (item.children[0] && item.children[0].path) {
-      redirectData.push({
-        from: `${item.path}`,
-        to: `${item.children[0].path}`,
-      });
-      item.children.forEach((children) => {
-        getRedirect(children);
-      });
-    }
-  }
-};
-getMenuData().forEach(getRedirect);
+const RightSidebar = connect(({ setting }) => ({ ...setting }))(Sidebar);
 
 
 const query = {
 const query = {
   'screen-xs': {
   'screen-xs': {
@@ -59,19 +40,10 @@ const query = {
     minWidth: 1200,
     minWidth: 1200,
   },
   },
 };
 };
-
-let isMobile;
-enquireScreen((b) => {
-  isMobile = b;
-});
-
 class BasicLayout extends React.PureComponent {
 class BasicLayout extends React.PureComponent {
   static childContextTypes = {
   static childContextTypes = {
     location: PropTypes.object,
     location: PropTypes.object,
     breadcrumbNameMap: PropTypes.object,
     breadcrumbNameMap: PropTypes.object,
-  }
-  state = {
-    isMobile,
   };
   };
   getChildContext() {
   getChildContext() {
     const { location, routerData } = this.props;
     const { location, routerData } = this.props;
@@ -80,16 +52,6 @@ class BasicLayout extends React.PureComponent {
       breadcrumbNameMap: routerData,
       breadcrumbNameMap: routerData,
     };
     };
   }
   }
-  componentDidMount() {
-    enquireScreen((mobile) => {
-      this.setState({
-        isMobile: mobile,
-      });
-    });
-    this.props.dispatch({
-      type: 'user/fetchCurrent',
-    });
-  }
   getPageTitle() {
   getPageTitle() {
     const { routerData, location } = this.props;
     const { routerData, location } = this.props;
     const { pathname } = location;
     const { pathname } = location;
@@ -113,150 +75,73 @@ class BasicLayout extends React.PureComponent {
       return '/dashboard/analysis';
       return '/dashboard/analysis';
     }
     }
     return redirect;
     return redirect;
-  }
+  };
   handleMenuCollapse = (collapsed) => {
   handleMenuCollapse = (collapsed) => {
     this.props.dispatch({
     this.props.dispatch({
       type: 'global/changeLayoutCollapsed',
       type: 'global/changeLayoutCollapsed',
       payload: collapsed,
       payload: collapsed,
     });
     });
-  }
-  handleNoticeClear = (type) => {
-    message.success(`清空了${type}`);
+  };
+  changeSetting = (setting) => {
     this.props.dispatch({
     this.props.dispatch({
-      type: 'global/clearNotices',
-      payload: type,
+      type: 'setting/changeSetting',
+      payload: setting,
     });
     });
   }
   }
-  handleMenuClick = ({ key }) => {
-    if (key === 'userCenter') {
-      this.props.dispatch(routerRedux.push('/user-profile/user-center'));
-      return;
-    }
-    if (key === 'triggerError') {
-      this.props.dispatch(routerRedux.push('/exception/trigger'));
-      return;
-    }
-    if (key === 'userinfo') {
-      this.props.dispatch(routerRedux.push('/user-profile/userinfo/base'));
-      return;
-    }
-    if (key === 'logout') {
-      this.props.dispatch({
-        type: 'login/logout',
-      });
-    }
-  }
-  handleNoticeVisibleChange = (visible) => {
-    if (visible) {
-      this.props.dispatch({
-        type: 'global/fetchNotices',
-      });
-    }
-  }
   render() {
   render() {
-    const isFluid = this.props.layout === 'fluid';
     const {
     const {
-      currentUser, collapsed, fetchingNotices, notices, routerData, match, location,
+      isMobile,
+      redirectData,
+      routerData,
+      fixedHeader,
+      match,
     } = this.props;
     } = this.props;
+    const isTop = this.props.layout === 'topmenu';
     const bashRedirect = this.getBashRedirect();
     const bashRedirect = this.getBashRedirect();
+    const myRedirectData = redirectData || [];
     const layout = (
     const layout = (
       <Layout>
       <Layout>
-        {isFluid && !isMobile ? null : (
+        {isTop && !isMobile ? null : (
           <SiderMenu
           <SiderMenu
             logo={logo}
             logo={logo}
-          // 不带Authorized参数的情况下如果没有权限,会强制跳到403界面
-          // If you do not have the Authorized parameter
-          // you will be forced to jump to the 403 interface without permission
             Authorized={Authorized}
             Authorized={Authorized}
-            menuData={getMenuData()}
-            collapsed={collapsed}
-            location={location}
-            isMobile={this.state.isMobile}
+            theme={this.props.silderTheme}
             onCollapse={this.handleMenuCollapse}
             onCollapse={this.handleMenuCollapse}
+            {...this.props}
           />
           />
         )}
         )}
         <Layout>
         <Layout>
-          <Header style={{ padding: 0 }}>
-            {isFluid && !isMobile ? (
-              <TopNavHeader
-                logo={logo}
-                mode="horizontal"
-                location={location}
-                menuData={getMenuData()}
-                isMobile={this.state.isMobile}
-                onNoticeClear={this.handleNoticeClear}
-                onCollapse={this.handleMenuCollapse}
-                onMenuClick={this.handleMenuClick}
-                onNoticeVisibleChange={this.handleNoticeVisibleChange}
-                notices={notices}
-                currentUser={currentUser}
-                fetchingNotices={fetchingNotices}
-              />
-          ) : (
-            <GlobalHeader
-              logo={logo}
-              currentUser={currentUser}
-              fetchingNotices={fetchingNotices}
-              notices={notices}
-              collapsed={collapsed}
-              isMobile={this.state.isMobile}
-              onNoticeClear={this.handleNoticeClear}
-              onCollapse={this.handleMenuCollapse}
-              onMenuClick={this.handleMenuClick}
-              onNoticeVisibleChange={this.handleNoticeVisibleChange}
-            />
-                    )}
-          </Header>
-          <Content style={{ margin: '24px 24px 0', height: '100%' }}>
+          <Header
+            handleMenuCollapse={this.handleMenuCollapse}
+            logo={logo}
+            {...this.props}
+          />
+          <Content
+            style={{
+              margin: '24px 24px 0',
+              height: '100%',
+              paddingTop: fixedHeader ? 64 : 0,
+            }}
+          >
             <Switch>
             <Switch>
-              {
-                redirectData.map(item =>
-                  <Redirect key={item.from} exact from={item.from} to={item.to} />
-                )
-              }
-              {
-                getRoutes(match.path, routerData).map(item =>
-                  (
-                    <AuthorizedRoute
-                      key={item.key}
-                      path={item.path}
-                      component={item.component}
-                      exact={item.exact}
-                      authority={item.authority}
-                      redirectPath="/exception/403"
-                    />
-                  )
-                )
-              }
+              {myRedirectData.map(item => (
+                <Redirect key={item.from} exact from={item.from} to={item.to} />
+              ))}
+              {getRoutes(match.path, routerData).map(item => (
+                <AuthorizedRoute
+                  key={item.key}
+                  path={item.path}
+                  component={item.component}
+                  exact={item.exact}
+                  authority={item.authority}
+                  redirectPath="/exception/403"
+                />
+              ))}
               <Redirect exact from="/" to={bashRedirect} />
               <Redirect exact from="/" to={bashRedirect} />
               <Route render={NotFound} />
               <Route render={NotFound} />
             </Switch>
             </Switch>
           </Content>
           </Content>
-          <Footer style={{ padding: 0 }}>
-            <GlobalFooter
-              links={[{
-                key: 'Pro 首页',
-                title: 'Pro 首页',
-                href: 'http://pro.ant.design',
-                blankTarget: true,
-              }, {
-                key: 'github',
-                title: <Icon type="github" />,
-                href: 'https://github.com/ant-design/ant-design-pro',
-                blankTarget: true,
-              }, {
-                key: 'Ant Design',
-                title: 'Ant Design',
-                href: 'http://ant.design',
-                blankTarget: true,
-              }]}
-              copyright={
-                <Fragment>
-                  Copyright <Icon type="copyright" /> 2018 蚂蚁金服体验技术部出品
-                </Fragment>
-              }
-            />
-          </Footer>
+          <Footer />
         </Layout>
         </Layout>
       </Layout>
       </Layout>
     );
     );
@@ -264,17 +149,20 @@ class BasicLayout extends React.PureComponent {
     return (
     return (
       <DocumentTitle title={this.getPageTitle()}>
       <DocumentTitle title={this.getPageTitle()}>
         <ContainerQuery query={query}>
         <ContainerQuery query={query}>
-          {params => <div className={classNames(params)}>{layout}</div>}
+          {params => (
+            <div className={classNames(params)}>
+              {layout}
+              <RightSidebar onChange={this.changeSetting} />
+            </div>
+          )}
         </ContainerQuery>
         </ContainerQuery>
       </DocumentTitle>
       </DocumentTitle>
     );
     );
   }
   }
 }
 }
 
 
-export default connect(({ user, global, loading }) => ({
-  currentUser: user.currentUser,
+export default connect(({ global, setting }) => ({
   collapsed: global.collapsed,
   collapsed: global.collapsed,
-  layout: global.layout,
-  fetchingNotices: loading.effects['global/fetchNotices'],
-  notices: global.notices,
+  layout: setting.layout,
+  ...setting,
 }))(BasicLayout);
 }))(BasicLayout);

+ 38 - 0
src/layouts/Footer.js

@@ -0,0 +1,38 @@
+
+import React, { Fragment } from 'react';
+import { Layout, Icon } from 'antd';
+import GlobalFooter from '../components/GlobalFooter';
+
+const { Footer } = Layout;
+const FooterView = () => (
+  <Footer style={{ padding: 0 }}>
+    <GlobalFooter
+      links={[
+        {
+          key: 'Pro 首页',
+          title: 'Pro 首页',
+          href: 'http://pro.ant.design',
+          blankTarget: true,
+        },
+        {
+          key: 'github',
+          title: <Icon type="github" />,
+          href: 'https://github.com/ant-design/ant-design-pro',
+          blankTarget: true,
+        },
+        {
+          key: 'Ant Design',
+          title: 'Ant Design',
+          href: 'http://ant.design',
+          blankTarget: true,
+        },
+      ]}
+      copyright={
+        <Fragment>
+          Copyright <Icon type="copyright" /> 2018 蚂蚁金服体验技术部出品
+        </Fragment>
+      }
+    />
+  </Footer>
+);
+export default FooterView;

+ 4 - 4
src/layouts/GridContent.js

@@ -5,13 +5,13 @@ import styles from './GridContent.less';
 class GridContent extends PureComponent {
 class GridContent extends PureComponent {
   render() {
   render() {
     let className = `${styles.main}`;
     let className = `${styles.main}`;
-    if (this.props.layout === 'fluid') {
-      className = `${styles.main} ${styles.fluid}`;
+    if (this.props.grid === 'Wide') {
+      className = `${styles.main} ${styles.wide}`;
     }
     }
     return <div className={className}>{this.props.children}</div>;
     return <div className={className}>{this.props.children}</div>;
   }
   }
 }
 }
 
 
-export default connect(({ global }) => ({
-  layout: global.layout,
+export default connect(({ setting }) => ({
+  grid: setting.grid,
 }))(GridContent);
 }))(GridContent);

+ 2 - 1
src/layouts/GridContent.less

@@ -2,7 +2,8 @@
   width: 100%;
   width: 100%;
   height: 100%;
   height: 100%;
   min-height: 100%;
   min-height: 100%;
-  &.fluid {
+  transition: 0.3s;
+  &.wide {
     max-width: 1200px;
     max-width: 1200px;
     margin: 0 auto;
     margin: 0 auto;
   }
   }

+ 153 - 0
src/layouts/Header.js

@@ -0,0 +1,153 @@
+import React, { PureComponent } from 'react';
+import { Layout, message } from 'antd';
+import Animate from 'rc-animate';
+import { connect } from 'dva';
+import { routerRedux } from 'dva/router';
+import GlobalHeader from '../components/GlobalHeader';
+import TopNavHeader from '../components/TopNavHeader';
+import styles from './Header.less';
+import Authorized from '../utils/Authorized';
+
+const { Header } = Layout;
+
+class HeaderView extends PureComponent {
+  state = {
+    visible: true,
+  };
+  componentDidMount() {
+    document.getElementById('root').addEventListener('scroll', this.handScroll);
+  }
+  componentWillUnmount() {
+    document
+      .getElementById('root')
+      .removeEventListener('scroll', this.handScroll);
+  }
+  getHeadWidth = () => {
+    if (
+      !this.props.fixedHeader ||
+      this.props.layout === 'topmenu' ||
+      this.props.fixSiderbar
+    ) {
+      return '100%';
+    }
+    if (!this.props.collapsed) {
+      return 'calc(100% - 256px)';
+    }
+    if (this.props.collapsed) {
+      return 'calc(100% - 80px)';
+    }
+  };
+  handleNoticeClear = (type) => {
+    message.success(`清空了${type}`);
+    this.props.dispatch({
+      type: 'global/clearNotices',
+      payload: type,
+    });
+  };
+  handleMenuClick = ({ key }) => {
+    if (key === 'userCenter') {
+      this.props.dispatch(routerRedux.push('/user-profile/user-center'));
+      return;
+    }
+    if (key === 'triggerError') {
+      this.props.dispatch(routerRedux.push('/exception/trigger'));
+      return;
+    }
+    if (key === 'userinfo') {
+      this.props.dispatch(routerRedux.push('/user-profile/userinfo/base'));
+      return;
+    }
+    if (key === 'logout') {
+      this.props.dispatch({
+        type: 'login/logout',
+      });
+    }
+  };
+  handleNoticeVisibleChange = (visible) => {
+    if (visible) {
+      this.props.dispatch({
+        type: 'global/fetchNotices',
+      });
+    }
+  };
+  handScroll = () => {
+    if (!this.props.autoHideHeader) {
+      return;
+    }
+    const { scrollTop } = document.getElementById('root');
+    if (!this.ticking) {
+      this.ticking = false;
+      requestAnimationFrame(() => {
+        if (scrollTop > 400 && this.state.visible) {
+          this.setState({
+            visible: false,
+          });
+        }
+        if (scrollTop < 400 && !this.state.visible) {
+          this.setState({
+            visible: true,
+          });
+        }
+        this.ticking = false;
+      });
+    }
+  };
+  render() {
+    const {
+      logo,
+      isMobile,
+      handleMenuCollapse,
+      silderTheme,
+      layout,
+      fixedHeader,
+    } = this.props;
+    const isTop = layout === 'topmenu';
+    const HeaderDom = this.state.visible ? (
+      <Header
+        style={{ padding: 0, width: this.getHeadWidth() }}
+        className={fixedHeader ? styles.fixedHeader : ''}
+      >
+        {isTop && !isMobile ? (
+          <TopNavHeader
+            logo={logo}
+            theme={silderTheme}
+            mode="horizontal"
+            Authorized={Authorized}
+            isMobile={isMobile}
+            onNoticeClear={this.handleNoticeClear}
+            onCollapse={handleMenuCollapse}
+            onMenuClick={this.handleMenuClick}
+            onNoticeVisibleChange={this.handleNoticeVisibleChange}
+            {...this.props}
+          />
+        ) : (
+          <GlobalHeader
+            logo={logo}
+            onNoticeClear={this.handleNoticeClear}
+            onCollapse={handleMenuCollapse}
+            onMenuClick={this.handleMenuClick}
+            onNoticeVisibleChange={this.handleNoticeVisibleChange}
+            {...this.props}
+          />
+        )}
+      </Header>
+    ) : null;
+    return (
+      <Animate component="" transitionName="fade">
+        {HeaderDom}
+      </Animate>
+    );
+  }
+}
+
+export default connect(({ user, global, setting, loading }) => ({
+  currentUser: user.currentUser,
+  collapsed: global.collapsed,
+  fetchingNotices: loading.effects['global/fetchNotices'],
+  notices: global.notices,
+  layout: setting.layout,
+  silderTheme: setting.silderTheme,
+  fixedHeader: setting.fixedHeader,
+  fixSiderbar: setting.fixSiderbar,
+  autoHideHeader: setting.autoHideHeader,
+}))(HeaderView);

+ 6 - 0
src/layouts/Header.less

@@ -0,0 +1,6 @@
+.fixedHeader {
+  position: fixed;
+  top: 0;
+  width: 100%;
+  z-index: 9;
+}

+ 94 - 0
src/layouts/LoadingPage.js

@@ -0,0 +1,94 @@
+import React, { PureComponent } from 'react';
+import { Spin } from 'antd';
+import { enquireScreen } from 'enquire-js';
+import { connect } from 'dva';
+import BasicLayout from './BasicLayout';
+import { getMenuData } from '../common/menu';
+
+/**
+ * 根据菜单取得重定向地址.
+ */
+
+const MenuData = getMenuData();
+const getRedirectData = () => {
+  const redirectData = [];
+  const getRedirect = (item) => {
+    if (item && item.children) {
+      if (item.children[0] && item.children[0].path) {
+        redirectData.push({
+          from: `${item.path}`,
+          to: `${item.children[0].path}`,
+        });
+        item.children.forEach((children) => {
+          getRedirect(children);
+        });
+      }
+    }
+  };
+  MenuData.forEach(getRedirect);
+  return redirectData;
+};
+const redirectData = getRedirectData();
+
+class LodingPage extends PureComponent {
+  state = {
+    loading: true,
+    isMobile: false,
+  };
+  componentDidMount() {
+    enquireScreen((mobile) => {
+      this.setState({
+        isMobile: mobile,
+      });
+    });
+    this.props.dispatch({
+      type: 'user/fetchCurrent',
+    });
+    const urlParams = new URL(window.location.href);
+    const settingString = urlParams.searchParams.get('setting');
+    if (settingString) {
+      const setting = {};
+      settingString.split(';').forEach((keyValue) => {
+        const [key, value] = keyValue.split(':');
+        setting[key] = value;
+      });
+      this.props.dispatch({
+        type: 'setting/changeSetting',
+        payload: setting,
+      });
+    }
+    this.hideLoading();
+  }
+  hideLoading() {
+    this.setState({
+      loading: false,
+    });
+  }
+  render() {
+    if (this.state.loading) {
+      return (
+        <div
+          style={{
+            width: '100%',
+            height: '100%',
+            margin: 'auto',
+            paddingTop: 50,
+            textAlign: 'center',
+          }}
+        >
+          <Spin size="large" />
+        </div>
+      );
+    }
+    return (
+      <BasicLayout
+        isMobile={this.state.isMobile}
+        menuData={MenuData}
+        redirectData={redirectData}
+        {...this.props}
+      />
+    );
+  }
+}
+
+export default connect()(LodingPage);

+ 0 - 7
src/models/global.js

@@ -5,7 +5,6 @@ export default {
 
 
   state: {
   state: {
     collapsed: false,
     collapsed: false,
-    layout: 'fluid',
     notices: [],
     notices: [],
   },
   },
 
 
@@ -41,12 +40,6 @@ export default {
         collapsed: payload,
         collapsed: payload,
       };
       };
     },
     },
-    changeLayout(state, { payload }) {
-      return {
-        ...state,
-        layout: payload,
-      };
-    },
     saveNotices(state, { payload }) {
     saveNotices(state, { payload }) {
       return {
       return {
         ...state,
         ...state,

+ 32 - 0
src/models/setting.js

@@ -0,0 +1,32 @@
+export default {
+  namespace: 'setting',
+
+  state: {
+    collapse: false,
+    silderTheme: 'dark',
+    themeColor: '#1890FF',
+    layout: 'sidemenu',
+    grid: 'Fluid',
+    fixedHeader: false,
+    autoHideHeader: false,
+    fixSiderbar: false,
+    colorWeak: 'close',
+  },
+  reducers: {
+    changeSetting(state, { payload }) {
+      const urlParams = new URL(window.location.href);
+      let urlParamsString = '';
+      Object.keys(payload).forEach((key) => {
+        if (payload[key] && state[key] !== undefined && key !== 'collapse') {
+          urlParamsString += `${key}:${payload[key]};`;
+        }
+      });
+      urlParams.searchParams.set('setting', urlParamsString);
+      window.history.replaceState(null, 'setting', urlParams.href);
+      return {
+        ...state,
+        ...payload,
+      };
+    },
+  },
+};

+ 3 - 3
src/routes/UserProfile/UserCenter.js

@@ -22,9 +22,9 @@ import AvatarList from '../../components/AvatarList';
 import { formatWan } from '../../utils/utils';
 import { formatWan } from '../../utils/utils';
 import styles from './UserCenter.less';
 import styles from './UserCenter.less';
 import stylesProjects from '../List/Projects.less';
 import stylesProjects from '../List/Projects.less';
-import stylesArticles from './List/Articles.less';
-import stylesApplications from './List/Applications.less';
-import GridContent from '../layouts/GridContent';
+import stylesArticles from '../List/Articles.less';
+import stylesApplications from '../List/Applications.less';
+import GridContent from '../../layouts/GridContent';
 
 
 @connect(({ list, loading, user, project }) => ({
 @connect(({ list, loading, user, project }) => ({
   list,
   list,

+ 2 - 2
src/routes/UserProfile/Userinfo/Info.js

@@ -3,8 +3,8 @@ import { connect } from 'dva';
 import { Route, routerRedux, Switch, Redirect } from 'dva/router';
 import { Route, routerRedux, Switch, Redirect } from 'dva/router';
 import { Menu } from 'antd';
 import { Menu } from 'antd';
 import styles from './Info.less';
 import styles from './Info.less';
-import { getRoutes } from '../../utils/utils';
-import GridContent from '../../layouts/GridContent';
+import { getRoutes } from '../../../utils/utils';
+import GridContent from '../../../layouts/GridContent';
 
 
 const { Item } = Menu;
 const { Item } = Menu;