Commit 7a1e94c49d546e155d8c17b492ff6b1e5fb55121
Committed by
GitHub
1 parent
463aeabd
feat: add notice (#47)
Showing
8 changed files
with
341 additions
and
142 deletions
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