Переглянути джерело

Merge branch 'master' of github.com:ant-design/ant-design-pro

chen shuai 6 роки тому
батько
коміт
dd41c1307a

+ 98 - 111
mock/notices.js

@@ -1,114 +1,101 @@
-const fakeNotices = [
-  {
-    id: '000000001',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
-    title: '你收到了 14 份新周报',
-    datetime: '2017-08-09',
-    type: 'notification',
-  },
-  {
-    id: '000000002',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
-    title: '你推荐的 曲妮妮 已通过第三轮面试',
-    datetime: '2017-08-08',
-    type: 'notification',
-  },
-  {
-    id: '000000003',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
-    title: '这种模板可以区分多种通知类型',
-    datetime: '2017-08-07',
-    read: true,
-    type: 'notification',
-  },
-  {
-    id: '000000004',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
-    title: '左侧图标用于区分不同的类型',
-    datetime: '2017-08-07',
-    type: 'notification',
-  },
-  {
-    id: '000000005',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
-    title: '内容不要超过两行字,超出时自动截断',
-    datetime: '2017-08-07',
-    type: 'notification',
-  },
-  {
-    id: '000000006',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
-    title: '曲丽丽 评论了你',
-    description: '描述信息描述信息描述信息',
-    datetime: '2017-08-07',
-    type: 'message',
-    clickClose: true,
-  },
-  {
-    id: '000000007',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
-    title: '朱偏右 回复了你',
-    description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
-    datetime: '2017-08-07',
-    type: 'message',
-    clickClose: true,
-  },
-  {
-    id: '000000008',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
-    title: '标题',
-    description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
-    datetime: '2017-08-07',
-    type: 'message',
-    clickClose: true,
-  },
-  {
-    id: '000000009',
-    title: '任务名称',
-    description: '任务需要在 2017-01-12 20:00 前启动',
-    extra: '未开始',
-    status: 'todo',
-    type: 'event',
-  },
-  {
-    id: '000000010',
-    title: '第三方紧急代码变更',
-    description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
-    extra: '马上到期',
-    status: 'urgent',
-    type: 'event',
-  },
-  {
-    id: '000000011',
-    title: '信息安全考试',
-    description: '指派竹尔于 2017-01-09 前完成更新并发布',
-    extra: '已耗时 8 天',
-    status: 'doing',
-    type: 'event',
-  },
-  {
-    id: '000000012',
-    title: 'ABCD 版本发布',
-    description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
-    extra: '进行中',
-    status: 'processing',
-    type: 'event',
-  },
-];
-
-const getNotices = (req, res) => {
-  if (req.query && req.query.type) {
-    const startFrom = parseInt(req.query.lastItemId, 10) + 1;
-    const result = fakeNotices
-      .filter(({ type }) => type === req.query.type)
-      .map((notice, index) => ({
-        ...notice,
-        id: `0000000${startFrom + index}`,
-      }));
-    return res.json(startFrom > 24 ? result.concat(null) : result);
-  }
-  return res.json(fakeNotices);
-};
+const getNotices = (req, res) =>
+  res.json([
+    {
+      id: '000000001',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+      title: '你收到了 14 份新周报',
+      datetime: '2017-08-09',
+      type: 'notification',
+    },
+    {
+      id: '000000002',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
+      title: '你推荐的 曲妮妮 已通过第三轮面试',
+      datetime: '2017-08-08',
+      type: 'notification',
+    },
+    {
+      id: '000000003',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
+      title: '这种模板可以区分多种通知类型',
+      datetime: '2017-08-07',
+      read: true,
+      type: 'notification',
+    },
+    {
+      id: '000000004',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
+      title: '左侧图标用于区分不同的类型',
+      datetime: '2017-08-07',
+      type: 'notification',
+    },
+    {
+      id: '000000005',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+      title: '内容不要超过两行字,超出时自动截断',
+      datetime: '2017-08-07',
+      type: 'notification',
+    },
+    {
+      id: '000000006',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+      title: '曲丽丽 评论了你',
+      description: '描述信息描述信息描述信息',
+      datetime: '2017-08-07',
+      type: 'message',
+      clickClose: true,
+    },
+    {
+      id: '000000007',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+      title: '朱偏右 回复了你',
+      description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
+      datetime: '2017-08-07',
+      type: 'message',
+      clickClose: true,
+    },
+    {
+      id: '000000008',
+      avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+      title: '标题',
+      description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
+      datetime: '2017-08-07',
+      type: 'message',
+      clickClose: true,
+    },
+    {
+      id: '000000009',
+      title: '任务名称',
+      description: '任务需要在 2017-01-12 20:00 前启动',
+      extra: '未开始',
+      status: 'todo',
+      type: 'event',
+    },
+    {
+      id: '000000010',
+      title: '第三方紧急代码变更',
+      description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
+      extra: '马上到期',
+      status: 'urgent',
+      type: 'event',
+    },
+    {
+      id: '000000011',
+      title: '信息安全考试',
+      description: '指派竹尔于 2017-01-09 前完成更新并发布',
+      extra: '已耗时 8 天',
+      status: 'doing',
+      type: 'event',
+    },
+    {
+      id: '000000012',
+      title: 'ABCD 版本发布',
+      description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
+      extra: '进行中',
+      status: 'processing',
+      type: 'event',
+    },
+  ]);
 
 export default {
   'GET /api/notices': getNotices,

+ 5 - 9
src/components/Charts/index.js

@@ -3,15 +3,11 @@ import numeral from 'numeral';
 import ChartCard from './ChartCard';
 import Field from './Field';
 
-const getComponent = Component => {
-  return props => {
-    return (
-      <Suspense fallback="...">
-        <Component {...props} />
-      </Suspense>
-    );
-  };
-};
+const getComponent = Component => props => (
+  <Suspense fallback="...">
+    <Component {...props} />
+  </Suspense>
+);
 
 const Bar = getComponent(React.lazy(() => import('./Bar')));
 const Pie = getComponent(React.lazy(() => import('./Pie')));

+ 16 - 0
src/components/DescriptionList/index.less

@@ -8,6 +8,14 @@
       overflow: hidden;
     }
   }
+  // fix margin top error of following descriptionList
+  & + & {
+    :global {
+      .ant-row {
+        margin-top: 16px;
+      }
+    }
+  }
 
   .title {
     margin-bottom: 16px;
@@ -47,6 +55,14 @@
         margin-bottom: -8px;
       }
     }
+    // fix margin top error of following descriptionList
+    & + .descriptionList {
+      :global {
+        .ant-row {
+          margin-top: 8px;
+        }
+      }
+    }
     .title {
       margin-bottom: 12px;
       color: @text-color;

+ 13 - 36
src/components/GlobalHeader/RightContent.js

@@ -1,6 +1,6 @@
 import React, { PureComponent } from 'react';
 import { FormattedMessage, formatMessage } from 'umi/locale';
-import { Spin, Tag, Menu, Icon, Avatar, Tooltip } from 'antd';
+import { Spin, Tag, Menu, Icon, Avatar, Tooltip, message } from 'antd';
 import moment from 'moment';
 import groupBy from 'lodash/groupBy';
 import NoticeIcon from '../NoticeIcon';
@@ -63,30 +63,13 @@ export default class GlobalHeaderRight extends PureComponent {
     });
   };
 
-  fetchMoreNotices = tabProps => {
-    const { list, name } = tabProps;
-    const { dispatch, notices = [] } = this.props;
-    const lastItemId = notices[notices.length - 1].id;
-    dispatch({
-      type: 'global/fetchMoreNotices',
-      payload: {
-        lastItemId,
-        type: name,
-        offset: list.length,
-      },
-    });
-  };
-
   render() {
     const {
       currentUser,
-      fetchingMoreNotices,
       fetchingNotices,
-      loadedAllNotices,
       onNoticeVisibleChange,
       onMenuClick,
       onNoticeClear,
-      skeletonCount,
       theme,
     } = this.props;
     const menu = (
@@ -110,11 +93,6 @@ export default class GlobalHeaderRight extends PureComponent {
         </Menu.Item>
       </Menu>
     );
-    const loadMoreProps = {
-      skeletonCount,
-      loadedAll: loadedAllNotices,
-      loading: fetchingMoreNotices,
-    };
     const noticeData = this.getNoticeData();
     const unreadMsg = this.getUnreadData(noticeData);
     let className = styles.right;
@@ -155,44 +133,43 @@ export default class GlobalHeaderRight extends PureComponent {
             console.log(item, tabProps); // eslint-disable-line
             this.changeReadState(item, tabProps);
           }}
+          loading={fetchingNotices}
           locale={{
             emptyText: formatMessage({ id: 'component.noticeIcon.empty' }),
             clear: formatMessage({ id: 'component.noticeIcon.clear' }),
-            loadedAll: formatMessage({ id: 'component.noticeIcon.loaded' }),
-            loadMore: formatMessage({ id: 'component.noticeIcon.loading-more' }),
+            viewMore: formatMessage({ id: 'component.noticeIcon.view-more' }),
+            notification: formatMessage({ id: 'component.globalHeader.notification' }),
+            message: formatMessage({ id: 'component.globalHeader.message' }),
+            event: formatMessage({ id: 'component.globalHeader.event' }),
           }}
           onClear={onNoticeClear}
-          onLoadMore={this.fetchMoreNotices}
           onPopupVisibleChange={onNoticeVisibleChange}
-          loading={fetchingNotices}
+          onViewMore={() => message.info('Click on view more')}
           clearClose
         >
           <NoticeIcon.Tab
             count={unreadMsg.notification}
             list={noticeData.notification}
-            title={formatMessage({ id: 'component.globalHeader.notification' })}
-            name="notification"
+            title="notification"
             emptyText={formatMessage({ id: 'component.globalHeader.notification.empty' })}
             emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
-            {...loadMoreProps}
+            showViewMore
           />
           <NoticeIcon.Tab
             count={unreadMsg.message}
             list={noticeData.message}
-            title={formatMessage({ id: 'component.globalHeader.message' })}
-            name="message"
+            title="message"
             emptyText={formatMessage({ id: 'component.globalHeader.message.empty' })}
             emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
-            {...loadMoreProps}
+            showViewMore
           />
           <NoticeIcon.Tab
             count={unreadMsg.event}
             list={noticeData.event}
-            title={formatMessage({ id: 'component.globalHeader.event' })}
-            name="event"
+            title="event"
             emptyText={formatMessage({ id: 'component.globalHeader.event.empty' })}
             emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
-            {...loadMoreProps}
+            showViewMore
           />
         </NoticeIcon>
         {currentUser.name ? (

+ 1 - 4
src/components/NoticeIcon/NoticeIconTab.d.ts

@@ -15,12 +15,9 @@ export interface INoticeIconTabProps {
   emptyText?: React.ReactNode;
   emptyImage?: string;
   list?: INoticeIconData[];
-  loadedAll?: boolean;
-  loading?: boolean;
   name?: string;
   showClear?: boolean;
-  skeletonCount?: number;
-  skeletonProps?: SkeletonProps;
+  showViewMore?: boolean;
   style?: React.CSSProperties;
   title?: string;
 }

+ 30 - 62
src/components/NoticeIcon/NoticeList.js

@@ -1,10 +1,8 @@
 import React from 'react';
-import { Avatar, List, Skeleton } from 'antd';
+import { Avatar, List } from 'antd';
 import classNames from 'classnames';
 import styles from './NoticeList.less';
 
-let ListElement = null;
-
 export default function NoticeList({
   data = [],
   onClick,
@@ -13,14 +11,9 @@ export default function NoticeList({
   locale,
   emptyText,
   emptyImage,
-  loading,
-  onLoadMore,
-  visible,
-  loadedAll = true,
-  scrollToLoad = true,
+  onViewMore = null,
   showClear = true,
-  skeletonCount = 5,
-  skeletonProps = {},
+  showViewMore = false,
 }) {
   if (data.length === 0) {
     return (
@@ -30,36 +23,10 @@ export default function NoticeList({
       </div>
     );
   }
-  const loadingList = Array.from({ length: loading ? skeletonCount : 0 }).map(() => ({ loading }));
-  const LoadMore = loadedAll ? (
-    <div className={classNames(styles.loadMore, styles.loadedAll)}>
-      <span>{locale.loadedAll}</span>
-    </div>
-  ) : (
-    <div className={styles.loadMore} onClick={onLoadMore}>
-      <span>{locale.loadMore}</span>
-    </div>
-  );
-  const onScroll = event => {
-    if (!scrollToLoad || loading || loadedAll) return;
-    if (typeof onLoadMore !== 'function') return;
-    const { currentTarget: t } = event;
-    if (t.scrollHeight - t.scrollTop - t.clientHeight <= 40) {
-      onLoadMore(event);
-      ListElement = t;
-    }
-  };
-  if (!visible && ListElement) {
-    try {
-      ListElement.scrollTo(null, 0);
-    } catch (err) {
-      ListElement = null;
-    }
-  }
   return (
     <div>
-      <List className={styles.list} loadMore={LoadMore} onScroll={onScroll}>
-        {[...data, ...loadingList].map((item, i) => {
+      <List className={styles.list}>
+        {data.map((item, i) => {
           const itemCls = classNames(styles.item, {
             [styles.read]: item.read,
           });
@@ -74,35 +41,36 @@ export default function NoticeList({
 
           return (
             <List.Item className={itemCls} key={item.key || i} onClick={() => onClick(item)}>
-              <Skeleton avatar title={false} active {...skeletonProps} loading={item.loading}>
-                <List.Item.Meta
-                  className={styles.meta}
-                  avatar={leftIcon}
-                  title={
-                    <div className={styles.title}>
-                      {item.title}
-                      <div className={styles.extra}>{item.extra}</div>
-                    </div>
-                  }
-                  description={
-                    <div>
-                      <div className={styles.description} title={item.description}>
-                        {item.description}
-                      </div>
-                      <div className={styles.datetime}>{item.datetime}</div>
+              <List.Item.Meta
+                className={styles.meta}
+                avatar={leftIcon}
+                title={
+                  <div className={styles.title}>
+                    {item.title}
+                    <div className={styles.extra}>{item.extra}</div>
+                  </div>
+                }
+                description={
+                  <div>
+                    <div className={styles.description} title={item.description}>
+                      {item.description}
                     </div>
-                  }
-                />
-              </Skeleton>
+                    <div className={styles.datetime}>{item.datetime}</div>
+                  </div>
+                }
+              />
             </List.Item>
           );
         })}
       </List>
-      {showClear ? (
-        <div className={styles.clear} onClick={onClear}>
-          {locale.clear} {title}
-        </div>
-      ) : null}
+      <div className={styles.bottomBar}>
+        {showClear ? (
+          <div onClick={onClear}>
+            {locale.clear} {locale[title] || title}
+          </div>
+        ) : null}
+        {showViewMore ? <div onClick={onViewMore}>{locale.viewMore}</div> : null}
+      </div>
     </div>
   );
 }

+ 16 - 5
src/components/NoticeIcon/NoticeList.less

@@ -78,17 +78,28 @@
   }
 }
 
-.clear {
+.bottomBar {
   height: 46px;
   color: @text-color;
   line-height: 46px;
   text-align: center;
   border-top: 1px solid @border-color-split;
   border-radius: 0 0 @border-radius-base @border-radius-base;
-  cursor: pointer;
   transition: all 0.3s;
-
-  &:hover {
-    color: @heading-color;
+  div {
+    display: inline-block;
+    width: 50%;
+    cursor: pointer;
+    transition: all 0.3s;
+    user-select: none;
+    &:hover {
+      color: @heading-color;
+    }
+    &:only-child {
+      width: 100%;
+    }
+    &:not(:only-child):last-child {
+      border-left: 1px solid @border-color-split;
+    }
   }
 }

+ 90 - 106
src/components/NoticeIcon/demo/popover.md

@@ -9,103 +9,90 @@ title: 带浮层卡片
 import NoticeIcon from 'ant-design-pro/lib/NoticeIcon';
 import { Tag } from 'antd';
 
-const data = [
-  {
-    id: '000000001',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
-    title: '你收到了 14 份新周报',
-    datetime: '2017-08-09',
-    type: '通知',
-  },
-  {
-    id: '000000002',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
-    title: '你推荐的 曲妮妮 已通过第三轮面试',
-    datetime: '2017-08-08',
-    type: '通知',
-  },
-  {
-    id: '000000003',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
-    title: '这种模板可以区分多种通知类型',
-    datetime: '2017-08-07',
-    read: true,
-    type: '通知',
-  },
-  {
-    id: '000000004',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
-    title: '左侧图标用于区分不同的类型',
-    datetime: '2017-08-07',
-    type: '通知',
-  },
-  {
-    id: '000000005',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
-    title: '内容不要超过两行字,超出时自动截断',
-    datetime: '2017-08-07',
-    type: '通知',
-  },
-  {
-    id: '000000006',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
-    title: '曲丽丽 评论了你',
-    description: '描述信息描述信息描述信息',
-    datetime: '2017-08-07',
-    type: '消息',
-    clickClose: true,
-  },
-  {
-    id: '000000007',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
-    title: '朱偏右 回复了你',
-    description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
-    datetime: '2017-08-07',
-    type: '消息',
-    clickClose: true,
-  },
-  {
-    id: '000000008',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
-    title: '标题',
-    description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
-    datetime: '2017-08-07',
-    type: '消息',
-    clickClose: true,
-  },
-  {
-    id: '000000009',
-    title: '任务名称',
-    description: '任务需要在 2017-01-12 20:00 前启动',
-    extra: '未开始',
-    status: 'todo',
-    type: '待办',
-  },
-  {
-    id: '000000010',
-    title: '第三方紧急代码变更',
-    description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
-    extra: '马上到期',
-    status: 'urgent',
-    type: '待办',
-  },
-  {
-    id: '000000011',
-    title: '信息安全考试',
-    description: '指派竹尔于 2017-01-09 前完成更新并发布',
-    extra: '已耗时 8 天',
-    status: 'doing',
-    type: '待办',
-  },
-  {
-    id: '000000012',
-    title: 'ABCD 版本发布',
-    description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
-    extra: '进行中',
-    status: 'processing',
-    type: '待办',
-  },
-];
+const data = [{
+  id: '000000001',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+  title: '你收到了 14 份新周报',
+  datetime: '2017-08-09',
+  type: 'notification',
+}, {
+  id: '000000002',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
+  title: '你推荐的 曲妮妮 已通过第三轮面试',
+  datetime: '2017-08-08',
+  type: 'notification',
+}, {
+  id: '000000003',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
+  title: '这种模板可以区分多种通知类型',
+  datetime: '2017-08-07',
+  read: true,
+  type: 'notification',
+}, {
+  id: '000000004',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
+  title: '左侧图标用于区分不同的类型',
+  datetime: '2017-08-07',
+  type: 'notification',
+}, {
+  id: '000000005',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+  title: '内容不要超过两行字,超出时自动截断',
+  datetime: '2017-08-07',
+  type: 'notification',
+}, {
+  id: '000000006',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+  title: '曲丽丽 评论了你',
+  description: '描述信息描述信息描述信息',
+  datetime: '2017-08-07',
+  type: 'message',
+  clickClose: true,
+}, {
+  id: '000000007',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+  title: '朱偏右 回复了你',
+  description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
+  datetime: '2017-08-07',
+  type: 'message',
+  clickClose: true,
+}, {
+  id: '000000008',
+  avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+  title: '标题',
+  description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
+  datetime: '2017-08-07',
+  type: 'message',
+  clickClose: true,
+}, {
+  id: '000000009',
+  title: '任务名称',
+  description: '任务需要在 2017-01-12 20:00 前启动',
+  extra: '未开始',
+  status: 'todo',
+  type: 'event',
+}, {
+  id: '000000010',
+  title: '第三方紧急代码变更',
+  description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
+  extra: '马上到期',
+  status: 'urgent',
+  type: 'event',
+}, {
+  id: '000000011',
+  title: '信息安全考试',
+  description: '指派竹尔于 2017-01-09 前完成更新并发布',
+  extra: '已耗时 8 天',
+  status: 'doing',
+  type: 'event',
+}, {
+  id: '000000012',
+  title: 'ABCD 版本发布',
+  description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
+  extra: '进行中',
+  status: 'processing',
+  type: 'event',
+}];
 
 function onItemClick(item, tabProps) {
   console.log(item, tabProps);
@@ -163,23 +150,20 @@ const Demo = () => (
   >
     <NoticeIcon className="notice-icon" count={5} onItemClick={onItemClick} onClear={onClear}>
       <NoticeIcon.Tab
-        list={noticeData['通知']}
-        name="通知"
-        title="通知"
+        list={noticeData.notification}
+        title="notification"
         emptyText="你已查看所有通知"
         emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
       />
       <NoticeIcon.Tab
-        list={noticeData['消息']}
-        name="消息"
-        title="消息"
+        list={noticeData.message}
+        title="message"
         emptyText="您已读完所有消息"
         emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
       />
       <NoticeIcon.Tab
-        list={noticeData['待办']}
-        name="待办"
-        title="待办"
+        list={noticeData.event}
+        title="event"
         emptyText="你已完成所有待办"
         emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
       />

+ 3 - 3
src/components/NoticeIcon/index.d.ts

@@ -8,7 +8,7 @@ export interface INoticeIconProps {
   loading?: boolean;
   onClear?: (tabName: string) => void;
   onItemClick?: (item: INoticeIconData, tabProps: INoticeIconProps) => void;
-  onLoadMore?: (tabProps: INoticeIconProps) => void;
+  onViewMore?: (tabProps: INoticeIconProps) => void;
   onTabChange?: (tabTile: string) => void;
   style?: React.CSSProperties;
   onPopupVisibleChange?: (visible: boolean) => void;
@@ -16,8 +16,8 @@ export interface INoticeIconProps {
   locale?: {
     emptyText: string;
     clear: string;
-    loadedAll: string;
-    loadMore: string;
+    viewMore: string;
+    [key: string]: string;
   };
   clearClose?: boolean;
 }

+ 5 - 10
src/components/NoticeIcon/index.en-US.md

@@ -5,7 +5,7 @@ cols: 1
 order: 9
 ---
 
-用在导航工具栏上,作为整个产品统一的通知中心。
+Used in navigation toolbar as a unified notification center for the entire product.
 
 ## API
 
@@ -16,11 +16,11 @@ bell | Change the bell Icon | ReactNode | `<Icon type='bell' />`
 loading | Popup card loading status | boolean | `false`
 onClear | Click to clear button the callback | function(tabName) | -
 onItemClick | Click on the list item's callback | function(item, tabProps) | -
-onLoadMore | Callback of click for loading more | function(tabProps, event) | -
 onPopupVisibleChange | Popup Card Showing or Hiding Callbacks | function(visible) | -
 onTabChange | Switching callbacks for tabs | function(tabTitle) | -
+onViewMore | Callback of click for view more | function(tabProps, event) | -
 popupVisible | Popup card display state | boolean | -
-locale | Default message text | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
+locale | Default message text | Object | `{ emptyText: 'No notifications', clear: 'Clear', viewMore: 'Loading more' }`
 clearClose | Close menu after clear | boolean | `false`
 
 ### NoticeIcon.Tab
@@ -31,14 +31,9 @@ count | Unread messages count of this tab | number | list.length
 emptyText | Message text when list is empty | ReactNode | -
 emptyImage | Image when list is empty | string | -
 list | List data, format refer to the following table | Array | `[]`
-loadedAll | All messages have been loaded | boolean | `true`
-loading | Loading status of this tab | boolean | `false`
-name | identifier for message Tab | string | -
-scrollToLoad | Scroll to load | boolean | `true`
-skeletonCount | Number of skeleton when tab is loading | number | `5`
-skeletonProps | Props of skeleton | SkeletonProps | `{}`
 showClear | Clear button display status | boolean | `true`
-title | header for message Tab | string | -
+showViewMore | View more button display status | boolean | `false`
+title | header for message Tab, the actual text is `locale[title] || title` | string | -
 
 ### Tab data
 

+ 12 - 30
src/components/NoticeIcon/index.js

@@ -16,13 +16,13 @@ export default class NoticeIcon extends PureComponent {
     onPopupVisibleChange: () => {},
     onTabChange: () => {},
     onClear: () => {},
+    onViewMore: () => {},
     loading: false,
     clearClose: false,
     locale: {
       emptyText: 'No notifications',
       clear: 'Clear',
-      loadedAll: 'Loaded',
-      loadMore: 'Loading more',
+      viewMore: 'More',
     },
     emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
   };
@@ -53,53 +53,35 @@ export default class NoticeIcon extends PureComponent {
     onTabChange(tabType);
   };
 
-  onLoadMore = (tabProps, event) => {
-    const { onLoadMore } = this.props;
-    onLoadMore(tabProps, event);
+  onViewMore = (tabProps, event) => {
+    const { onViewMore } = this.props;
+    onViewMore(tabProps, event);
   };
 
   getNotificationBox() {
-    const { visible } = this.state;
     const { children, loading, locale } = this.props;
     if (!children) {
       return null;
     }
     const panes = React.Children.map(children, child => {
-      const {
-        list,
-        title,
-        name,
-        count,
-        emptyText,
-        emptyImage,
-        showClear,
-        loadedAll,
-        scrollToLoad,
-        skeletonCount,
-        skeletonProps,
-        loading: tabLoading,
-      } = child.props;
+      const { list, title, count, emptyText, emptyImage, showClear, showViewMore } = child.props;
       const len = list && list.length ? list.length : 0;
       const msgCount = count || count === 0 ? count : len;
-      const tabTitle = msgCount > 0 ? `${title} (${msgCount})` : title;
+      const localeTitle = locale[title] || title;
+      const tabTitle = msgCount > 0 ? `${localeTitle} (${msgCount})` : localeTitle;
       return (
-        <TabPane tab={tabTitle} key={name}>
+        <TabPane tab={tabTitle} key={title}>
           <List
             data={list}
             emptyImage={emptyImage}
             emptyText={emptyText}
-            loadedAll={loadedAll}
-            loading={tabLoading}
             locale={locale}
-            onClear={() => this.onClear(name)}
+            onClear={() => this.onClear(title)}
             onClick={item => this.onItemClick(item, child.props)}
-            onLoadMore={event => this.onLoadMore(child.props, event)}
-            scrollToLoad={scrollToLoad}
+            onViewMore={event => this.onViewMore(child.props, event)}
             showClear={showClear}
-            skeletonCount={skeletonCount}
-            skeletonProps={skeletonProps}
+            showViewMore={showViewMore}
             title={title}
-            visible={visible}
           />
         </TabPane>
       );

+ 4 - 9
src/components/NoticeIcon/index.zh-CN.md

@@ -16,11 +16,11 @@ bell | translate this please -> Change the bell Icon | ReactNode | `<Icon type='
 loading | 弹出卡片加载状态 | boolean | `false`
 onClear | 点击清空按钮的回调 | function(tabName) | -
 onItemClick | 点击列表项的回调 | function(item, tabProps) | -
-onLoadMore | 加载更多的回调 | function(tabProps, event) | -
 onPopupVisibleChange | 弹出卡片显隐的回调 | function(visible) | -
 onTabChange | 切换页签的回调 | function(tabTitle) | -
+onViewMore | 点击查看更多的回调 | function(tabProps, event) | -
 popupVisible | 控制弹层显隐 | boolean | -
-locale | 默认文案 | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
+locale | 默认文案 | Object | `{ emptyText: 'No notifications', clear: 'Clear', viewMore: 'Loading more' }`
 clearClose | 点击清空按钮后关闭通知菜单 | boolean | `false`
 
 ### NoticeIcon.Tab
@@ -31,14 +31,9 @@ count | 当前 Tab 未读消息数量 | number | list.length
 emptyText | 针对每个 Tab 定制空数据文案 | ReactNode | -
 emptyImage | 针对每个 Tab 定制空数据图片 | string | -
 list | 列表数据,格式参照下表 | Array | `[]`
-loadedAll | 已加载完所有消息 | boolean | `true`
-loading | 当前 Tab 的加载状态 | boolean | `false`
-name | 消息分类的标识符 | string | -
-scrollToLoad | 允许滚动自加载 | boolean | `true`
-skeletonCount | 加载时占位骨架的数量 | number | `5`
-skeletonProps | 加载时占位骨架的属性 | SkeletonProps | `{}`
 showClear | 是否显示清空按钮 | boolean | `true`
-title | 消息分类的页签标题 | string | -
+showViewMore | 是否显示查看更多按钮 | boolean | `false`
+title | 消息分类的页签标题,实际的文案是 `locale[title] || title` | string | -
 
 ### Tab data
 

+ 1 - 0
src/components/PageHeader/index.md

@@ -12,6 +12,7 @@ order: 11
 | 参数      | 说明                                      | 类型         | 默认值 |
 |----------|------------------------------------------|-------------|-------|
 | title | title 区域 | ReactNode | - |
+| loading | 骨架屏loading状态 | boolean | false |
 | logo | logo区域 | ReactNode | - |
 | action | 操作区,位于 title 行的行尾 | ReactNode | - |
 | home | 默认的主页说明文字 | ReactNode | - |

+ 2 - 2
src/components/SiderMenu/BaseMenu.js

@@ -33,13 +33,13 @@ export default class BaseMenu extends PureComponent {
    * 获得菜单子节点
    * @memberof SiderMenu
    */
-  getNavMenuItems = (menusData, parent) => {
+  getNavMenuItems = menusData => {
     if (!menusData) {
       return [];
     }
     return menusData
       .filter(item => item.name && !item.hideInMenu)
-      .map(item => this.getSubMenuOrItem(item, parent))
+      .map(item => this.getSubMenuOrItem(item))
       .filter(item => item);
   };
 

+ 0 - 1
src/layouts/Header.js

@@ -155,7 +155,6 @@ export default connect(({ user, global, setting, loading }) => ({
   collapsed: global.collapsed,
   fetchingMoreNotices: loading.effects['global/fetchMoreNotices'],
   fetchingNotices: loading.effects['global/fetchNotices'],
-  loadedAllNotices: global.loadedAllNotices,
   notices: global.notices,
   setting,
 }))(HeaderView);

+ 1 - 2
src/locales/en-US/globalHeader.js

@@ -13,6 +13,5 @@ export default {
   'component.noticeIcon.clear': 'Clear',
   'component.noticeIcon.cleared': 'Cleared',
   'component.noticeIcon.empty': 'No notifications',
-  'component.noticeIcon.loaded': 'Loaded',
-  'component.noticeIcon.loading-more': 'Loading more',
+  'component.noticeIcon.view-more': 'View more',
 };

+ 1 - 1
src/locales/pt-BR/globalHeader.js

@@ -14,5 +14,5 @@ export default {
   'component.noticeIcon.cleared': 'Limpo',
   'component.noticeIcon.empty': 'Sem notificações',
   'component.noticeIcon.loaded': 'Carregado',
-  'component.noticeIcon.loading-more': 'Carregar mais',
+  'component.noticeIcon.view-more': 'Veja mais',
 };

+ 1 - 2
src/locales/zh-CN/globalHeader.js

@@ -13,6 +13,5 @@ export default {
   'component.noticeIcon.clear': '清空',
   'component.noticeIcon.cleared': '清空了',
   'component.noticeIcon.empty': '暂无数据',
-  'component.noticeIcon.loaded': '加载完毕',
-  'component.noticeIcon.loading-more': '加载更多',
+  'component.noticeIcon.view-more': '查看更多',
 };

+ 1 - 2
src/locales/zh-TW/globalHeader.js

@@ -13,6 +13,5 @@ export default {
   'component.noticeIcon.clear': '清空',
   'component.noticeIcon.cleared': '清空了',
   'component.noticeIcon.empty': '暫無資料',
-  'component.noticeIcon.loaded': '加載完畢',
-  'component.noticeIcon.loading-more': '加載更多',
+  'component.noticeIcon.view-more': '查看更多',
 };

+ 1 - 41
src/models/global.js

@@ -6,42 +6,14 @@ export default {
   state: {
     collapsed: false,
     notices: [],
-    loadedAllNotices: false,
   },
 
   effects: {
     *fetchNotices(_, { call, put, select }) {
       const data = yield call(queryNotices);
-      const loadedAllNotices = data && data.length && data[data.length - 1] === null;
-      yield put({
-        type: 'setLoadedStatus',
-        payload: loadedAllNotices,
-      });
       yield put({
         type: 'saveNotices',
-        payload: data.filter(item => item),
-      });
-      const unreadCount = yield select(
-        state => state.global.notices.filter(item => !item.read).length
-      );
-      yield put({
-        type: 'user/changeNotifyCount',
-        payload: {
-          totalCount: data.length,
-          unreadCount,
-        },
-      });
-    },
-    *fetchMoreNotices({ payload }, { call, put, select }) {
-      const data = yield call(queryNotices, payload);
-      const loadedAllNotices = data && data.length && data[data.length - 1] === null;
-      yield put({
-        type: 'setLoadedStatus',
-        payload: loadedAllNotices,
-      });
-      yield put({
-        type: 'pushNotices',
-        payload: data.filter(item => item),
+        payload: data,
       });
       const unreadCount = yield select(
         state => state.global.notices.filter(item => !item.read).length
@@ -114,18 +86,6 @@ export default {
         notices: state.notices.filter(item => item.type !== payload),
       };
     },
-    pushNotices(state, { payload }) {
-      return {
-        ...state,
-        notices: [...state.notices, ...payload],
-      };
-    },
-    setLoadedStatus(state, { payload }) {
-      return {
-        ...state,
-        loadedAllNotices: payload,
-      };
-    },
   },
 
   subscriptions: {

+ 12 - 20
src/pages/Authorized.js

@@ -6,28 +6,20 @@ import Authorized from '@/utils/Authorized';
 
 function AuthComponent({ children, location, routerData, status }) {
   const isLogin = status === 'ok';
+  const getRouteAuthority = (path, routeData) => {
+    let authorities;
+    routeData.forEach(route => {
+      // match prefix
+      if (pathToRegexp(`${route.path}(.*)`).test(path)) {
+        authorities = route.authority || authorities;
 
-  const getRouteAuthority = (pathname, routeData) => {
-    const routes = routeData.slice(); // clone
-
-    const getAuthority = (routeDatas, path) => {
-      let authorities;
-      routeDatas.forEach(route => {
-        // check partial route
-        if (pathToRegexp(`${route.path}(.*)`).test(path)) {
-          if (route.authority) {
-            authorities = route.authority;
-          }
-          // is exact route?
-          if (!pathToRegexp(route.path).test(path) && route.routes) {
-            authorities = getAuthority(route.routes, path);
-          }
+        // get children authority recursively
+        if (route.routes) {
+          authorities = getRouteAuthority(path, route.routes) || authorities;
         }
-      });
-      return authorities;
-    };
-
-    return getAuthority(routes, pathname);
+      }
+    });
+    return authorities;
   };
   return (
     <Authorized

+ 0 - 1
src/pages/Dashboard/Analysis.js

@@ -39,7 +39,6 @@ class Analysis extends Component {
       type: 'chart/clear',
     });
     cancelAnimationFrame(this.reqRef);
-    clearTimeout(this.timeoutId);
   }
 
   handleChangeSalesType = e => {