Commit 7a1e94c49d546e155d8c17b492ff6b1e5fb55121

Authored by chen-xt
Committed by GitHub
1 parent 463aeabd

feat: add notice (#47)

src/layouts/default/LayoutHeader.tsx
... ... @@ -22,6 +22,7 @@ import { useModal } from '/@/components/Modal/index';
22 22 import { errorStore } from '/@/store/modules/error';
23 23 import { useGo } from '/@/hooks/web/usePage';
24 24 import { useWindowSizeFn } from '/@/hooks/event/useWindowSize';
  25 +import NoticeAction from './actions/notice/NoticeActionItem.vue';
25 26  
26 27 export default defineComponent({
27 28 name: 'DefaultLayoutHeader',
... ... @@ -85,7 +86,14 @@ export default defineComponent({
85 86 const {
86 87 useErrorHandle,
87 88 showLogo,
88   - headerSetting: { theme: headerTheme, useLockPage, showRedo, showGithub, showFullScreen },
  89 + headerSetting: {
  90 + theme: headerTheme,
  91 + useLockPage,
  92 + showRedo,
  93 + showGithub,
  94 + showFullScreen,
  95 + showNotice,
  96 + },
89 97 menuSetting: { mode, type: menuType, split: splitMenu, topMenuAlign },
90 98 showBreadCrumb,
91 99 } = getProjectConfig;
... ... @@ -163,6 +171,20 @@ export default defineComponent({
163 171 }}
164 172 </Tooltip>
165 173 )}
  174 + {showNotice && (
  175 + <div>
  176 + <Tooltip>
  177 + {{
  178 + title: () => '消息中心',
  179 + default: () => (
  180 + <div class={`layout-header__action-item`}>
  181 + <NoticeAction />
  182 + </div>
  183 + ),
  184 + }}
  185 + </Tooltip>
  186 + </div>
  187 + )}
166 188 {showRedo && (
167 189 <Tooltip>
168 190 {{
... ...
src/layouts/default/actions/notice/NoticeActionItem.tsx deleted 100644 → 0
1   -import { defineComponent } from 'vue';
2   -import { Popover, Tabs } from 'ant-design-vue';
3   -
4   -import NoticeList from './NoticeList';
5   -import { NoticeTabItem, NoticeListItem, noticeTabListData, noticeListData } from './data';
6   -import './index.less';
7   -
8   -const prefixCls = 'notice-popover';
9   -export default defineComponent({
10   - name: 'NoticePopover',
11   - props: {
12   - visible: {
13   - type: Boolean,
14   - default: false,
15   - },
16   - },
17   - setup(props, { attrs }) {
18   - // 渲染卡片内容
19   - function renderContent() {
20   - return (
21   - <Tabs class={`${prefixCls}__tabs`}>
22   - {() => {
23   - return noticeTabListData.map((item: NoticeTabItem) => {
24   - const { key, name } = item;
25   - return (
26   - <Tabs.TabPane key={key} tab={renderTab(key, name)}>
27   - {() => <NoticeList list={getListData(key)} />}
28   - </Tabs.TabPane>
29   - );
30   - });
31   - }}
32   - </Tabs>
33   - );
34   - }
35   -
36   - // tab标题渲染
37   - function renderTab(key: string, name: string) {
38   - const list = getListData(key);
39   - const unreadlist = list.filter((item: NoticeListItem) => !item.read);
40   - return (
41   - <div>
42   - {name}
43   - {unreadlist.length > 0 && <span>({unreadlist.length})</span>}
44   - </div>
45   - );
46   - }
47   -
48   - // 获取数据
49   - function getListData(type: string) {
50   - return noticeListData.filter((item: NoticeListItem) => item.type === type);
51   - }
52   -
53   - return () => {
54   - const { visible } = props;
55   - return (
56   - <Popover
57   - title=""
58   - {...{
59   - ...attrs,
60   - visible,
61   - }}
62   - content={renderContent}
63   - class={prefixCls}
64   - />
65   - );
66   - };
67   - },
68   -});
src/layouts/default/actions/notice/NoticeActionItem.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <Popover title="" trigger="click">
  4 + <Badge :count="count" :numberStyle="numberStyle">
  5 + <BellOutlined class="layout-header__action-icon" />
  6 + </Badge>
  7 + <template #content>
  8 + <Tabs>
  9 + <template v-for="item in tabListData" :key="item.key">
  10 + <TabPane>
  11 + <template #tab>
  12 + {{ item.name }}
  13 + <span v-if="item.list.length !== 0">({{ item.list.length }})</span>
  14 + </template>
  15 + <NoticeList :list="item.list" />
  16 + </TabPane>
  17 + </template>
  18 + </Tabs>
  19 + </template>
  20 + </Popover>
  21 + </div>
  22 +</template>
  23 +<script lang="ts">
  24 + import { defineComponent } from 'vue';
  25 + import { Popover, Tabs, Badge } from 'ant-design-vue';
  26 + import { BellOutlined } from '@ant-design/icons-vue';
  27 + import { tabListData } from './data';
  28 + import NoticeList from './NoticeList.vue';
  29 +
  30 + export default defineComponent({
  31 + components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList },
  32 + setup() {
  33 + let count = 0;
  34 + for (let i = 0; i < tabListData.length; i++) {
  35 + count += tabListData[i].list.length;
  36 + }
  37 +
  38 + return {
  39 + tabListData,
  40 + count,
  41 + numberStyle: {},
  42 + };
  43 + },
  44 + });
  45 +</script>
  46 +<style lang="less" scoped>
  47 + /deep/ .ant-tabs-tab {
  48 + padding-top: 8px;
  49 + margin-right: 12px;
  50 + }
  51 +
  52 + /deep/ .ant-tabs-content {
  53 + width: 300px;
  54 + }
  55 +
  56 + /deep/ .ant-badge {
  57 + font-size: 18px;
  58 +
  59 + .ant-badge-multiple-words {
  60 + padding: 0 4px;
  61 + transform: translate(26%, -48%);
  62 + }
  63 + }
  64 +</style>
... ...
src/layouts/default/actions/notice/NoticeList.tsx deleted 100644 → 0
1   -import { defineComponent } from 'vue';
2   -import { List, Avatar, Tag } from 'ant-design-vue';
3   -
4   -import { NoticeListItem } from './data';
5   -import './index.less';
6   -
7   -const prefixCls = 'notice-popover';
8   -export default defineComponent({
9   - name: 'NoticeList',
10   - props: {
11   - list: {
12   - type: Array,
13   - default: () => [],
14   - },
15   - },
16   - setup(props) {
17   - // 头像渲染
18   - function renderAvatar(avatar: string) {
19   - return avatar ? <Avatar class="avatar" src={avatar} /> : <span>{avatar}</span>;
20   - }
21   -
22   - // 描述渲染
23   - function renderDescription(description: string, datetime: string) {
24   - return (
25   - <div>
26   - <div class="description">{description}</div>
27   - <div class="datetime">{datetime}</div>
28   - </div>
29   - );
30   - }
31   -
32   - // 标题渲染
33   - function renderTitle(title: string, extra?: string, color?: string) {
34   - return (
35   - <div class="title">
36   - {title}
37   - {extra && (
38   - <div class="extra">
39   - <Tag class="tag" color={color}>
40   - {() => extra}
41   - </Tag>
42   - </div>
43   - )}
44   - </div>
45   - );
46   - }
47   -
48   - return () => {
49   - const { list } = props;
50   - return (
51   - <List dataSource={list} class={`${prefixCls}__list`}>
52   - {() => {
53   - return list.map((item: NoticeListItem) => {
54   - const { id, avatar, title, description, datetime, extra, read, color } = item;
55   - return (
56   - <List.Item key={id} class={`${prefixCls}__list-item ${read ? 'read' : ''}`}>
57   - {() => (
58   - <List.Item.Meta
59   - class="meta"
60   - avatar={renderAvatar(avatar)}
61   - title={renderTitle(title, extra, color)}
62   - description={renderDescription(description, datetime)}
63   - />
64   - )}
65   - </List.Item>
66   - );
67   - });
68   - }}
69   - </List>
70   - );
71   - };
72   - },
73   -});
src/layouts/default/actions/notice/NoticeList.vue 0 → 100644
  1 +<template>
  2 + <List class="list">
  3 + <template v-for="item in list" :key="item.id">
  4 + <ListItem class="list__item">
  5 + <ListItemMeta>
  6 + <template #title>
  7 + <div class="title">
  8 + {{ item.title }}
  9 + <div class="extra" v-if="item.extra">
  10 + <Tag class="tag" :color="item.color">
  11 + {{ item.extra }}
  12 + </Tag>
  13 + </div>
  14 + </div>
  15 + </template>
  16 + <template #avatar>
  17 + <Avatar v-if="item.avatar" class="avatar" :src="item.avatar" />
  18 + <span v-else> {{ item.avatar }}</span>
  19 + </template>
  20 + <template #description>
  21 + <div>
  22 + <div class="description">{{ item.description }}</div>
  23 + <div class="datetime">{{ item.datetime }}</div>
  24 + </div>
  25 + </template>
  26 + </ListItemMeta>
  27 + </ListItem>
  28 + </template>
  29 + </List>
  30 +</template>
  31 +<script lang="ts">
  32 + import { defineComponent, PropType } from 'vue';
  33 + import { List, Avatar, Tag } from 'ant-design-vue';
  34 + import { ListItem } from './data';
  35 +
  36 + export default defineComponent({
  37 + props: {
  38 + list: {
  39 + type: Array as PropType<Array<ListItem>>,
  40 + default: () => [],
  41 + },
  42 + },
  43 + components: {
  44 + List,
  45 + ListItem: List.Item,
  46 + ListItemMeta: List.Item.Meta,
  47 + Avatar,
  48 + Tag,
  49 + },
  50 + setup(props) {
  51 + const { list = [] } = props;
  52 + return {
  53 + list,
  54 + };
  55 + },
  56 + });
  57 +</script>
  58 +<style lang="less" scoped>
  59 + .list {
  60 + &::-webkit-scrollbar {
  61 + display: none;
  62 + }
  63 +
  64 + &__item {
  65 + padding: 6px;
  66 + overflow: hidden;
  67 + cursor: pointer;
  68 + transition: all 0.3s;
  69 +
  70 + .title {
  71 + margin-bottom: 8px;
  72 + font-weight: normal;
  73 +
  74 + .extra {
  75 + float: right;
  76 + margin-top: -1.5px;
  77 + margin-right: 0;
  78 + font-weight: normal;
  79 +
  80 + .tag {
  81 + margin-right: 0;
  82 + }
  83 + }
  84 +
  85 + .avatar {
  86 + margin-top: 4px;
  87 + }
  88 +
  89 + .description {
  90 + font-size: 12px;
  91 + line-height: 18px;
  92 + }
  93 +
  94 + .datetime {
  95 + margin-top: 4px;
  96 + font-size: 12px;
  97 + line-height: 18px;
  98 + }
  99 + }
  100 + }
  101 + }
  102 +</style>
... ...
src/layouts/default/actions/notice/data.ts 0 → 100644
  1 +export interface ListItem {
  2 + id: string;
  3 + avatar: string;
  4 + title: string;
  5 + datetime: string;
  6 + type: string;
  7 + read?: boolean;
  8 + description: string;
  9 + clickClose?: boolean;
  10 + extra?: string;
  11 + color?: string;
  12 +}
  13 +
  14 +export interface TabItem {
  15 + key: string;
  16 + name: string;
  17 + list: ListItem[];
  18 + unreadlist?: ListItem[];
  19 +}
  20 +
  21 +export const tabListData: TabItem[] = [
  22 + {
  23 + key: '1',
  24 + name: '通知',
  25 + list: [
  26 + {
  27 + id: '000000001',
  28 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
  29 + title: '你收到了 14 份新周报',
  30 + description: '',
  31 + datetime: '2017-08-09',
  32 + type: '1',
  33 + },
  34 + {
  35 + id: '000000002',
  36 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
  37 + title: '你推荐的 曲妮妮 已通过第三轮面试',
  38 + description: '',
  39 + datetime: '2017-08-08',
  40 + type: '1',
  41 + },
  42 + {
  43 + id: '000000003',
  44 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
  45 + title: '这种模板可以区分多种通知类型',
  46 + description: '',
  47 + datetime: '2017-08-07',
  48 + // read: true,
  49 + type: '1',
  50 + },
  51 + {
  52 + id: '000000004',
  53 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
  54 + title: '左侧图标用于区分不同的类型',
  55 + description: '',
  56 + datetime: '2017-08-07',
  57 + type: '1',
  58 + },
  59 + // {
  60 + // id: '000000005',
  61 + // avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
  62 + // title: '内容不要超过两行字,超出时自动截断',
  63 + // description: '',
  64 + // datetime: '2017-08-07',
  65 + // type: '1',
  66 + // },
  67 + ],
  68 + },
  69 + {
  70 + key: '2',
  71 + name: '消息',
  72 + list: [
  73 + {
  74 + id: '000000006',
  75 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
  76 + title: '曲丽丽 评论了你',
  77 + description: '描述信息描述信息描述信息',
  78 + datetime: '2017-08-07',
  79 + type: '2',
  80 + clickClose: true,
  81 + },
  82 + {
  83 + id: '000000007',
  84 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
  85 + title: '朱偏右 回复了你',
  86 + description: '这种模板用于提醒谁与你发生了互动',
  87 + datetime: '2017-08-07',
  88 + type: '2',
  89 + clickClose: true,
  90 + },
  91 + {
  92 + id: '000000008',
  93 + avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
  94 + title: '标题',
  95 + description: '这种模板用于提醒谁与你发生了互动',
  96 + datetime: '2017-08-07',
  97 + type: '2',
  98 + clickClose: true,
  99 + },
  100 + ],
  101 + },
  102 + {
  103 + key: '3',
  104 + name: '待办',
  105 + list: [
  106 + {
  107 + id: '000000009',
  108 + avatar: '',
  109 + title: '任务名称',
  110 + description: '任务需要在 2017-01-12 20:00 前启动',
  111 + datetime: '',
  112 + extra: '未开始',
  113 + color: '',
  114 + type: '3',
  115 + },
  116 + {
  117 + id: '000000010',
  118 + avatar: '',
  119 + title: '第三方紧急代码变更',
  120 + description: '冠霖 需在 2017-01-07 前完成代码变更任务',
  121 + datetime: '',
  122 + extra: '马上到期',
  123 + color: 'red',
  124 + type: '3',
  125 + },
  126 + {
  127 + id: '000000011',
  128 + avatar: '',
  129 + title: '信息安全考试',
  130 + description: '指派竹尔于 2017-01-09 前完成更新并发布',
  131 + datetime: '',
  132 + extra: '已耗时 8 天',
  133 + color: 'gold',
  134 + type: '3',
  135 + },
  136 + {
  137 + id: '000000012',
  138 + avatar: '',
  139 + title: 'ABCD 版本发布',
  140 + description: '指派竹尔于 2017-01-09 前完成更新并发布',
  141 + datetime: '',
  142 + extra: '进行中',
  143 + color: 'blue',
  144 + type: '3',
  145 + },
  146 + ],
  147 + },
  148 +];
... ...
src/settings/projectSetting.ts
... ... @@ -39,6 +39,8 @@ const setting: ProjectConfig = {
39 39 showDoc: true,
40 40 // 是否显示github
41 41 showGithub: true,
  42 + // 显示消息中心按钮
  43 + showNotice: true,
42 44 },
43 45 // 菜单配置
44 46 menuSetting: {
... ...
src/types/config.d.ts
... ... @@ -47,6 +47,8 @@ export interface HeaderSetting {
47 47 // 显示文档按钮
48 48 showDoc: boolean;
49 49 showGithub: boolean;
  50 + // 显示消息中心按钮
  51 + showNotice: boolean;
50 52 }
51 53 export interface ProjectConfig {
52 54 // 是否显示配置按钮
... ...