提交 7a1e94c4 编写于 作者: C chen-xt 提交者: GitHub

feat: add notice (#47)

上级 463aeabd
......@@ -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>
{{
......
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}
/>
);
};
},
});
<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>
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>
);
};
},
});
<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>
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',
},
],
},
];
......@@ -39,6 +39,8 @@ const setting: ProjectConfig = {
showDoc: true,
// 是否显示github
showGithub: true,
// 显示消息中心按钮
showNotice: true,
},
// 菜单配置
menuSetting: {
......
......@@ -47,6 +47,8 @@ export interface HeaderSetting {
// 显示文档按钮
showDoc: boolean;
showGithub: boolean;
// 显示消息中心按钮
showNotice: boolean;
}
export interface ProjectConfig {
// 是否显示配置按钮
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册