Skip to content

Commit

Permalink
feat(projects): 增加系统消息组件
Browse files Browse the repository at this point in the history
  • Loading branch information
yanbowe committed Jun 17, 2022
1 parent c6ed9b1 commit afa0134
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 11 deletions.
57 changes: 57 additions & 0 deletions src/layouts/common/GlobalHeader/components/MessageList.vue
@@ -0,0 +1,57 @@
<template>
<n-scrollbar class="max-h-360px">
<n-list>
<n-list-item
v-for="item in list"
:key="item.id"
class="cursor-pointer hover:bg-[#f6f6f6] dark:hover:bg-dark"
@click="handleRead(item)"
>
<n-thing class="px-15px" :class="{ 'opacity-30': item.isRead }">
<template #avatar>
<n-avatar v-if="item.avatar" :src="item.avatar" />
<svg-icon v-else-if="item.svgIcon" class="text-34px text-primary" :icon="item.svgIcon" />
<Icon v-else-if="item.icon" class="text-34px text-primary" :icon="item.icon" />
</template>
<template #header>
<n-ellipsis :line-clamp="titleRows" :tooltip="{ placement: 'top' }">
{{ item.title }}
<template #tooltip>
{{ item.title }}
</template>
</n-ellipsis>
</template>
<template v-if="item.tagTitle" #header-extra>
<n-tag v-bind="item.tagProps" size="small">{{ item.tagTitle }}</n-tag>
</template>
<template #description>
<n-ellipsis v-if="item.description" :line-clamp="descRows">
{{ item.description }}
</n-ellipsis>
<p>{{ item.date }}</p>
</template>
</n-thing>
</n-list-item>
</n-list>
</n-scrollbar>
</template>
<script lang="ts" setup>
import { Icon } from '@iconify/vue';
interface Props {
list?: Message.List[];
titleRows?: number;
descRows?: number;
}
withDefaults(defineProps<Props>(), {
list: () => [],
titleRows: 1,
descRows: 2
});
function handleRead(item: Message.List) {
// eslint-disable-next-line no-param-reassign
item.isRead = true;
}
</script>
205 changes: 205 additions & 0 deletions src/layouts/common/GlobalHeader/components/SystemMessage.vue
@@ -0,0 +1,205 @@
<template>
<n-popover trigger="click" class="!p-0" placement="bottom">
<template #trigger>
<hover-container tooltip-content="消息通知" class="w-40px h-full relative" :inverted="theme.header.inverted">
<icon-clarity:notification-line class="text-18px" />
<n-badge
:value="count"
:show="count > 0"
:max="99"
:class="[count < 10 ? '-right-2px' : '-right-10px']"
class="absolute top-10px"
/>
</hover-container>
</template>
<n-tabs v-model:value="currentTab" type="line" class="w-360px" justify-content="space-evenly">
<n-tab-pane v-for="(item, index) in tabData" :key="item.key" :name="index">
<template #tab>
<div class="flex-x-center items-center w-120px">
<span class="mr-5px">{{ item.name }}</span>
<n-badge
v-bind="item.badgeProps"
:value="item.list.filter(message => !message.isRead).length"
:max="99"
:show="item.list.length !== 0"
/>
</div>
</template>
<loading-empty-wrapper
class="h-360px"
:loading="loading"
:empty="item.list.length === 0"
placeholder-class="bg-$n-color transition-background-color duration-300 ease-in-out"
>
<message-list :list="item.list" />
</loading-empty-wrapper>
</n-tab-pane>
</n-tabs>
<div v-if="tabData[currentTab].list.length > 0" class="flex cursor-pointer border-t border-$n-divider-color">
<div class="py-10px text-center flex-1" @click="handleClear(currentTab)">清空</div>
<div class="py-10px text-center flex-1 border-l border-$n-divider-color" @click="handleAllRead(currentTab)">
全部已读
</div>
<div class="py-10px text-center flex-1 border-l border-$n-divider-color" @click="handleLoadMore(currentTab)">
查看更多
</div>
</div>
</n-popover>
</template>

<script lang="ts" setup>
import { ref, computed } from 'vue';
import { useThemeStore } from '@/store';
import { useBoolean } from '@/hooks';
import MessageList from './MessageList.vue';
const currentTab = ref(0);
const theme = useThemeStore();
const { bool: loading, setBool: setLoading } = useBoolean();
const tabData = ref<Message.Tab[]>([
{
key: 1,
name: '通知',
badgeProps: { type: 'warning' },
list: [
{ id: 1, icon: 'ri:message-3-line', title: '你收到了5条新消息', date: '2022-06-17' },
{ id: 4, icon: 'ri:message-3-line', title: 'Soybean Admin 1.0.0 版本正在筹备中', date: '2022-06-17' },
{ id: 2, icon: 'ri:message-3-line', title: 'Soybean Admin 0.9.6 版本发布了', date: '2022-06-16' },
{ id: 3, icon: 'ri:message-3-line', title: 'Soybean Admin 0.9.5 版本发布了', date: '2022-06-07' },
{
id: 5,
icon: 'ri:message-3-line',
title: '测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题测试超长标题',
date: '2022-06-17'
}
]
},
{
key: 2,
name: '消息',
badgeProps: { type: 'error' },
list: [
{
id: 1,
title: '项目动态',
svgIcon: 'avatar',
description: 'Soybean 刚才把工作台页面随便写了一些,凑合能看了!',
date: '2021-11-07 22:45:32'
},
{
id: 2,
title: '项目动态',
svgIcon: 'avatar',
description: 'Soybean 正在忙于为soybean-admin写项目说明文档!',
date: '2021-11-03 20:33:31'
},
{
id: 3,
title: '项目动态',
svgIcon: 'avatar',
description: 'Soybean 准备为soybean-admin 1.0的发布做充分的准备工作!',
date: '2021-10-31 22:43:12'
},
{
id: 4,
title: '项目动态',
svgIcon: 'avatar',
description: '@yanbowe 向soybean-admin提交了一个bug,多标签栏不会自适应。',
date: '2021-10-27 10:24:54'
},
{
id: 5,
title: '项目动态',
svgIcon: 'avatar',
description: 'Soybean 在2021年5月28日创建了开源项目soybean-admin!',
date: '2021-05-28 22:22:22'
}
]
},
{
key: 3,
name: '待办',
badgeProps: { type: 'info' },
list: [
{
id: 1,
icon: 'ri:calendar-todo-line',
title: '缓存主题配置',
description: '任务正在计划中',
date: '2022-06-17',
tagTitle: '未开始',
tagProps: { type: 'default' }
},
{
id: 2,
icon: 'ri:calendar-todo-line',
title: '添加锁屏组件、全局Iframe组件',
description: '任务正在计划中',
date: '2022-06-17',
tagTitle: '未开始',
tagProps: { type: 'default' }
},
{
id: 3,
icon: 'ri:calendar-todo-line',
title: '示例页面完善',
description: '任务正在计划中',
date: '2022-06-17',
tagTitle: '未开始',
tagProps: { type: 'default' }
},
{
id: 4,
icon: 'ri:calendar-todo-line',
title: '表单、表格示例',
description: '任务正在计划中',
date: '2022-06-17',
tagTitle: '未开始',
tagProps: { type: 'default' }
},
{
id: 5,
icon: 'ri:calendar-todo-line',
title: '性能优化(优化递归函数)',
description: '任务正在计划中',
date: '2022-06-17',
tagTitle: '未开始',
tagProps: { type: 'default' }
},
{
id: 6,
icon: 'ri:calendar-todo-line',
title: '精简版(新分支thin)',
description: '任务正在计划中',
date: '2022-06-17',
tagTitle: '未开始',
tagProps: { type: 'default' }
}
]
}
]);
const count = computed(() => {
return tabData.value.reduce((acc, cur) => {
return acc + cur.list.filter(item => !item.isRead).length;
}, 0);
});
function handleClear(index: number) {
tabData.value[index].list = [];
}
function handleAllRead(index: number) {
tabData.value[index].list.map(item => Object.assign(item, { isRead: true }));
}
function handleLoadMore(index: number) {
const { list } = tabData.value[index];
setLoading(true);
setTimeout(() => {
list.push(...tabData.value[index].list);
setLoading(false);
}, 1000);
}
</script>
<style scoped></style>
3 changes: 2 additions & 1 deletion src/layouts/common/GlobalHeader/components/index.ts
Expand Up @@ -5,5 +5,6 @@ import GithubSite from './GithubSite.vue';
import FullScreen from './FullScreen.vue';
import ThemeMode from './ThemeMode.vue';
import UserAvatar from './UserAvatar.vue';
import SystemMessage from './SystemMessage.vue';

export { MenuCollapse, GlobalBreadcrumb, HeaderMenu, GithubSite, FullScreen, ThemeMode, UserAvatar };
export { MenuCollapse, GlobalBreadcrumb, HeaderMenu, GithubSite, FullScreen, ThemeMode, UserAvatar, SystemMessage };
4 changes: 3 additions & 1 deletion src/layouts/common/GlobalHeader/index.vue
Expand Up @@ -11,6 +11,7 @@
<github-site />
<full-screen />
<theme-mode />
<system-message />
<user-avatar />
</div>
</dark-mode-container>
Expand All @@ -27,7 +28,8 @@ import {
GithubSite,
FullScreen,
ThemeMode,
UserAvatar
UserAvatar,
SystemMessage
} from './components';
interface Props {
Expand Down
18 changes: 9 additions & 9 deletions src/layouts/common/GlobalSearch/components/SearchFooter.vue
@@ -1,17 +1,17 @@
<template>
<div class="px-24px h-44px flex-y-center">
<span class="mr-14px">
<icon-ant-design-enter-outlined class="icon text-20px p-2px mr-3px" />
确认
<span class="mr-14px flex-y-center">
<icon-ant-design-enter-outlined class="icon text-20px p-2px mr-6px" />
<span>确认</span>
</span>
<span class="mr-14px">
<span class="mr-14px flex-y-center">
<icon-mdi-arrow-up-thin class="icon text-20px p-2px mr-5px" />
<icon-mdi-arrow-down-thin class="icon text-20px p-2px mr-3px" />
切换
<icon-mdi-arrow-down-thin class="icon text-20px p-2px mr-6px" />
<span>切换</span>
</span>
<span>
<icon-mdi-close class="icon text-20px p-2px mr-3px" />
关闭
<span class="flex-y-center">
<icon-mdi-close class="icon text-20px p-2px mr-6px" />
<span>关闭</span>
</span>
</div>
</template>
Expand Down
36 changes: 36 additions & 0 deletions src/typings/business.d.ts
Expand Up @@ -26,3 +26,39 @@ declare namespace Demo {
name: string;
}
}

/** 系统消息 */
declare namespace Message {
interface Tab {
/** tab的key */
key: number;
/** tab名称 */
name: string;
/** badge类型 */
badgeProps?: import('naive-ui').BadgeProps;
/** 消息数据 */
list: ListItem[];
}

interface List {
/** 数据唯一值 */
id: number;
/** 头像 */
avatar?: string;
/** 消息icon */
icon?: string;
svgIcon?: string;
/** 消息标题 */
title: string;
/** 消息发送时间 */
date?: string;
/** 消息是否已读 */
isRead?: boolean;
/** 消息描述 */
description?: string;
/** 标签名称 */
tagTitle?: string;
/** 标签props */
tagProps?: import('naive-ui').TagProps;
}
}

0 comments on commit afa0134

Please sign in to comment.