Ver Fonte

feat: add notice (#47)

chen-xt há 4 anos atrás
pai
commit
7a1e94c49d

+ 23 - 1
src/layouts/default/LayoutHeader.tsx

@@ -22,6 +22,7 @@ import { useModal } from '/@/components/Modal/index';
 import { errorStore } from '/@/store/modules/error';
 import { useGo } from '/@/hooks/web/usePage';
 import { useWindowSizeFn } from '/@/hooks/event/useWindowSize';
+import NoticeAction from './actions/notice/NoticeActionItem.vue';
 
 export default defineComponent({
   name: 'DefaultLayoutHeader',
@@ -85,7 +86,14 @@ export default defineComponent({
       const {
         useErrorHandle,
         showLogo,
-        headerSetting: { theme: headerTheme, useLockPage, showRedo, showGithub, showFullScreen },
+        headerSetting: {
+          theme: headerTheme,
+          useLockPage,
+          showRedo,
+          showGithub,
+          showFullScreen,
+          showNotice,
+        },
         menuSetting: { mode, type: menuType, split: splitMenu, topMenuAlign },
         showBreadCrumb,
       } = getProjectConfig;
@@ -163,6 +171,20 @@ export default defineComponent({
                     }}
                   </Tooltip>
                 )}
+                {showNotice && (
+                  <div>
+                    <Tooltip>
+                      {{
+                        title: () => '消息中心',
+                        default: () => (
+                          <div class={`layout-header__action-item`}>
+                            <NoticeAction />
+                          </div>
+                        ),
+                      }}
+                    </Tooltip>
+                  </div>
+                )}
                 {showRedo && (
                   <Tooltip>
                     {{

+ 0 - 68
src/layouts/default/actions/notice/NoticeActionItem.tsx

@@ -1,68 +0,0 @@
-import { defineComponent } from 'vue';
-import { Popover, Tabs } from 'ant-design-vue';
-
-import NoticeList from './NoticeList';
-import { NoticeTabItem, NoticeListItem, noticeTabListData, noticeListData } from './data';
-import './index.less';
-
-const prefixCls = 'notice-popover';
-export default defineComponent({
-  name: 'NoticePopover',
-  props: {
-    visible: {
-      type: Boolean,
-      default: false,
-    },
-  },
-  setup(props, { attrs }) {
-    // 渲染卡片内容
-    function renderContent() {
-      return (
-        <Tabs class={`${prefixCls}__tabs`}>
-          {() => {
-            return noticeTabListData.map((item: NoticeTabItem) => {
-              const { key, name } = item;
-              return (
-                <Tabs.TabPane key={key} tab={renderTab(key, name)}>
-                  {() => <NoticeList list={getListData(key)} />}
-                </Tabs.TabPane>
-              );
-            });
-          }}
-        </Tabs>
-      );
-    }
-
-    // tab标题渲染
-    function renderTab(key: string, name: string) {
-      const list = getListData(key);
-      const unreadlist = list.filter((item: NoticeListItem) => !item.read);
-      return (
-        <div>
-          {name}
-          {unreadlist.length > 0 && <span>({unreadlist.length})</span>}
-        </div>
-      );
-    }
-
-    // 获取数据
-    function getListData(type: string) {
-      return noticeListData.filter((item: NoticeListItem) => item.type === type);
-    }
-
-    return () => {
-      const { visible } = props;
-      return (
-        <Popover
-          title=""
-          {...{
-            ...attrs,
-            visible,
-          }}
-          content={renderContent}
-          class={prefixCls}
-        />
-      );
-    };
-  },
-});

+ 64 - 0
src/layouts/default/actions/notice/NoticeActionItem.vue

@@ -0,0 +1,64 @@
+<template>
+  <div>
+    <Popover title="" trigger="click">
+      <Badge :count="count" :numberStyle="numberStyle">
+        <BellOutlined class="layout-header__action-icon" />
+      </Badge>
+      <template #content>
+        <Tabs>
+          <template v-for="item in tabListData" :key="item.key">
+            <TabPane>
+              <template #tab>
+                {{ item.name }}
+                <span v-if="item.list.length !== 0">({{ item.list.length }})</span>
+              </template>
+              <NoticeList :list="item.list" />
+            </TabPane>
+          </template>
+        </Tabs>
+      </template>
+    </Popover>
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+  import { Popover, Tabs, Badge } from 'ant-design-vue';
+  import { BellOutlined } from '@ant-design/icons-vue';
+  import { tabListData } from './data';
+  import NoticeList from './NoticeList.vue';
+
+  export default defineComponent({
+    components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList },
+    setup() {
+      let count = 0;
+      for (let i = 0; i < tabListData.length; i++) {
+        count += tabListData[i].list.length;
+      }
+
+      return {
+        tabListData,
+        count,
+        numberStyle: {},
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  /deep/ .ant-tabs-tab {
+    padding-top: 8px;
+    margin-right: 12px;
+  }
+
+  /deep/ .ant-tabs-content {
+    width: 300px;
+  }
+
+  /deep/ .ant-badge {
+    font-size: 18px;
+
+    .ant-badge-multiple-words {
+      padding: 0 4px;
+      transform: translate(26%, -48%);
+    }
+  }
+</style>

+ 0 - 73
src/layouts/default/actions/notice/NoticeList.tsx

@@ -1,73 +0,0 @@
-import { defineComponent } from 'vue';
-import { List, Avatar, Tag } from 'ant-design-vue';
-
-import { NoticeListItem } from './data';
-import './index.less';
-
-const prefixCls = 'notice-popover';
-export default defineComponent({
-  name: 'NoticeList',
-  props: {
-    list: {
-      type: Array,
-      default: () => [],
-    },
-  },
-  setup(props) {
-    // 头像渲染
-    function renderAvatar(avatar: string) {
-      return avatar ? <Avatar class="avatar" src={avatar} /> : <span>{avatar}</span>;
-    }
-
-    // 描述渲染
-    function renderDescription(description: string, datetime: string) {
-      return (
-        <div>
-          <div class="description">{description}</div>
-          <div class="datetime">{datetime}</div>
-        </div>
-      );
-    }
-
-    // 标题渲染
-    function renderTitle(title: string, extra?: string, color?: string) {
-      return (
-        <div class="title">
-          {title}
-          {extra && (
-            <div class="extra">
-              <Tag class="tag" color={color}>
-                {() => extra}
-              </Tag>
-            </div>
-          )}
-        </div>
-      );
-    }
-
-    return () => {
-      const { list } = props;
-      return (
-        <List dataSource={list} class={`${prefixCls}__list`}>
-          {() => {
-            return list.map((item: NoticeListItem) => {
-              const { id, avatar, title, description, datetime, extra, read, color } = item;
-              return (
-                <List.Item key={id} class={`${prefixCls}__list-item ${read ? 'read' : ''}`}>
-                  {() => (
-                    <List.Item.Meta
-                      class="meta"
-                      avatar={renderAvatar(avatar)}
-                      title={renderTitle(title, extra, color)}
-                      description={renderDescription(description, datetime)}
-                    />
-                  )}
-                </List.Item>
-              );
-            });
-          }}
-        </List>
-      );
-    };
-  },
-});

+ 102 - 0
src/layouts/default/actions/notice/NoticeList.vue

@@ -0,0 +1,102 @@
+<template>
+  <List class="list">
+    <template v-for="item in list" :key="item.id">
+      <ListItem class="list__item">
+        <ListItemMeta>
+          <template #title>
+            <div class="title">
+              {{ item.title }}
+              <div class="extra" v-if="item.extra">
+                <Tag class="tag" :color="item.color">
+                  {{ item.extra }}
+                </Tag>
+              </div>
+            </div>
+          </template>
+          <template #avatar>
+            <Avatar v-if="item.avatar" class="avatar" :src="item.avatar" />
+            <span v-else> {{ item.avatar }}</span>
+          </template>
+          <template #description>
+            <div>
+              <div class="description">{{ item.description }}</div>
+              <div class="datetime">{{ item.datetime }}</div>
+            </div>
+          </template>
+        </ListItemMeta>
+      </ListItem>
+    </template>
+  </List>
+</template>
+<script lang="ts">
+  import { defineComponent, PropType } from 'vue';
+  import { List, Avatar, Tag } from 'ant-design-vue';
+  import { ListItem } from './data';
+
+  export default defineComponent({
+    props: {
+      list: {
+        type: Array as PropType<Array<ListItem>>,
+        default: () => [],
+      },
+    },
+    components: {
+      List,
+      ListItem: List.Item,
+      ListItemMeta: List.Item.Meta,
+      Avatar,
+      Tag,
+    },
+    setup(props) {
+      const { list = [] } = props;
+      return {
+        list,
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  .list {
+    &::-webkit-scrollbar {
+      display: none;
+    }
+
+    &__item {
+      padding: 6px;
+      overflow: hidden;
+      cursor: pointer;
+      transition: all 0.3s;
+
+      .title {
+        margin-bottom: 8px;
+        font-weight: normal;
+
+        .extra {
+          float: right;
+          margin-top: -1.5px;
+          margin-right: 0;
+          font-weight: normal;
+
+          .tag {
+            margin-right: 0;
+          }
+        }
+
+        .avatar {
+          margin-top: 4px;
+        }
+
+        .description {
+          font-size: 12px;
+          line-height: 18px;
+        }
+
+        .datetime {
+          margin-top: 4px;
+          font-size: 12px;
+          line-height: 18px;
+        }
+      }
+    }
+  }
+</style>

+ 148 - 0
src/layouts/default/actions/notice/data.ts

@@ -0,0 +1,148 @@
+export interface ListItem {
+  id: string;
+  avatar: string;
+  title: string;
+  datetime: string;
+  type: string;
+  read?: boolean;
+  description: string;
+  clickClose?: boolean;
+  extra?: string;
+  color?: string;
+}
+
+export interface TabItem {
+  key: string;
+  name: string;
+  list: ListItem[];
+  unreadlist?: ListItem[];
+}
+
+export const tabListData: TabItem[] = [
+  {
+    key: '1',
+    name: '通知',
+    list: [
+      {
+        id: '000000001',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+        title: '你收到了 14 份新周报',
+        description: '',
+        datetime: '2017-08-09',
+        type: '1',
+      },
+      {
+        id: '000000002',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
+        title: '你推荐的 曲妮妮 已通过第三轮面试',
+        description: '',
+        datetime: '2017-08-08',
+        type: '1',
+      },
+      {
+        id: '000000003',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
+        title: '这种模板可以区分多种通知类型',
+        description: '',
+        datetime: '2017-08-07',
+        // read: true,
+        type: '1',
+      },
+      {
+        id: '000000004',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
+        title: '左侧图标用于区分不同的类型',
+        description: '',
+        datetime: '2017-08-07',
+        type: '1',
+      },
+      // {
+      //   id: '000000005',
+      //   avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
+      //   title: '内容不要超过两行字,超出时自动截断',
+      //   description: '',
+      //   datetime: '2017-08-07',
+      //   type: '1',
+      // },
+    ],
+  },
+  {
+    key: '2',
+    name: '消息',
+    list: [
+      {
+        id: '000000006',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+        title: '曲丽丽 评论了你',
+        description: '描述信息描述信息描述信息',
+        datetime: '2017-08-07',
+        type: '2',
+        clickClose: true,
+      },
+      {
+        id: '000000007',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+        title: '朱偏右 回复了你',
+        description: '这种模板用于提醒谁与你发生了互动',
+        datetime: '2017-08-07',
+        type: '2',
+        clickClose: true,
+      },
+      {
+        id: '000000008',
+        avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
+        title: '标题',
+        description: '这种模板用于提醒谁与你发生了互动',
+        datetime: '2017-08-07',
+        type: '2',
+        clickClose: true,
+      },
+    ],
+  },
+  {
+    key: '3',
+    name: '待办',
+    list: [
+      {
+        id: '000000009',
+        avatar: '',
+        title: '任务名称',
+        description: '任务需要在 2017-01-12 20:00 前启动',
+        datetime: '',
+        extra: '未开始',
+        color: '',
+        type: '3',
+      },
+      {
+        id: '000000010',
+        avatar: '',
+        title: '第三方紧急代码变更',
+        description: '冠霖 需在 2017-01-07 前完成代码变更任务',
+        datetime: '',
+        extra: '马上到期',
+        color: 'red',
+        type: '3',
+      },
+      {
+        id: '000000011',
+        avatar: '',
+        title: '信息安全考试',
+        description: '指派竹尔于 2017-01-09 前完成更新并发布',
+        datetime: '',
+        extra: '已耗时 8 天',
+        color: 'gold',
+        type: '3',
+      },
+      {
+        id: '000000012',
+        avatar: '',
+        title: 'ABCD 版本发布',
+        description: '指派竹尔于 2017-01-09 前完成更新并发布',
+        datetime: '',
+        extra: '进行中',
+        color: 'blue',
+        type: '3',
+      },
+    ],
+  },
+];

+ 2 - 0
src/settings/projectSetting.ts

@@ -39,6 +39,8 @@ const setting: ProjectConfig = {
     showDoc: true,
     //  是否显示github
     showGithub: true,
+    // 显示消息中心按钮
+    showNotice: true,
   },
   // 菜单配置
   menuSetting: {

+ 2 - 0
src/types/config.d.ts

@@ -47,6 +47,8 @@ export interface HeaderSetting {
   // 显示文档按钮
   showDoc: boolean;
   showGithub: boolean;
+  // 显示消息中心按钮
+  showNotice: boolean;
 }
 export interface ProjectConfig {
   // 是否显示配置按钮