feat: add read and delete for each notification
This commit is contained in:
parent
a4aa133db5
commit
dbc5ea32ae
@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
|
|||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
|
id: 1,
|
||||||
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
||||||
date: '3小时前',
|
date: '3小时前',
|
||||||
isRead: true,
|
isRead: true,
|
||||||
@ -30,6 +31,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '收到了 14 份新周报',
|
title: '收到了 14 份新周报',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 2,
|
||||||
avatar: 'https://avatar.vercel.sh/1',
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
date: '刚刚',
|
date: '刚刚',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -37,6 +39,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '朱偏右 回复了你',
|
title: '朱偏右 回复了你',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 3,
|
||||||
avatar: 'https://avatar.vercel.sh/1',
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
date: '2024-01-01',
|
date: '2024-01-01',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -44,6 +47,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '曲丽丽 评论了你',
|
title: '曲丽丽 评论了你',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 4,
|
||||||
avatar: 'https://avatar.vercel.sh/satori',
|
avatar: 'https://avatar.vercel.sh/satori',
|
||||||
date: '1天前',
|
date: '1天前',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -102,6 +106,17 @@ function handleNoticeClear() {
|
|||||||
notifications.value = [];
|
notifications.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markRead(id: number | string) {
|
||||||
|
const item = notifications.value.find((item) => item.id === id);
|
||||||
|
if (item) {
|
||||||
|
item.isRead = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(id: number | string) {
|
||||||
|
notifications.value = notifications.value.filter((item) => item.id !== id);
|
||||||
|
}
|
||||||
|
|
||||||
function handleMakeAll() {
|
function handleMakeAll() {
|
||||||
notifications.value.forEach((item) => (item.isRead = true));
|
notifications.value.forEach((item) => (item.isRead = true));
|
||||||
}
|
}
|
||||||
@ -144,6 +159,8 @@ watch(
|
|||||||
:dot="showDot"
|
:dot="showDot"
|
||||||
:notifications="notifications"
|
:notifications="notifications"
|
||||||
@clear="handleNoticeClear"
|
@clear="handleNoticeClear"
|
||||||
|
@read="(item) => item.id && markRead(item.id)"
|
||||||
|
@remove="(item) => item.id && remove(item.id)"
|
||||||
@make-all="handleMakeAll"
|
@make-all="handleMakeAll"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
|
|||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
|
id: 1,
|
||||||
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
||||||
date: '3小时前',
|
date: '3小时前',
|
||||||
isRead: true,
|
isRead: true,
|
||||||
@ -30,6 +31,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '收到了 14 份新周报',
|
title: '收到了 14 份新周报',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 2,
|
||||||
avatar: 'https://avatar.vercel.sh/1',
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
date: '刚刚',
|
date: '刚刚',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -37,6 +39,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '朱偏右 回复了你',
|
title: '朱偏右 回复了你',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 3,
|
||||||
avatar: 'https://avatar.vercel.sh/1',
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
date: '2024-01-01',
|
date: '2024-01-01',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -44,6 +47,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '曲丽丽 评论了你',
|
title: '曲丽丽 评论了你',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 4,
|
||||||
avatar: 'https://avatar.vercel.sh/satori',
|
avatar: 'https://avatar.vercel.sh/satori',
|
||||||
date: '1天前',
|
date: '1天前',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -102,6 +106,17 @@ function handleNoticeClear() {
|
|||||||
notifications.value = [];
|
notifications.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markRead(id: number | string) {
|
||||||
|
const item = notifications.value.find((item) => item.id === id);
|
||||||
|
if (item) {
|
||||||
|
item.isRead = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(id: number | string) {
|
||||||
|
notifications.value = notifications.value.filter((item) => item.id !== id);
|
||||||
|
}
|
||||||
|
|
||||||
function handleMakeAll() {
|
function handleMakeAll() {
|
||||||
notifications.value.forEach((item) => (item.isRead = true));
|
notifications.value.forEach((item) => (item.isRead = true));
|
||||||
}
|
}
|
||||||
@ -144,6 +159,8 @@ watch(
|
|||||||
:dot="showDot"
|
:dot="showDot"
|
||||||
:notifications="notifications"
|
:notifications="notifications"
|
||||||
@clear="handleNoticeClear"
|
@clear="handleNoticeClear"
|
||||||
|
@read="(item) => item.id && markRead(item.id)"
|
||||||
|
@remove="(item) => item.id && remove(item.id)"
|
||||||
@make-all="handleMakeAll"
|
@make-all="handleMakeAll"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
|
|||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
|
id: 1,
|
||||||
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
||||||
date: '3小时前',
|
date: '3小时前',
|
||||||
isRead: true,
|
isRead: true,
|
||||||
@ -30,6 +31,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '收到了 14 份新周报',
|
title: '收到了 14 份新周报',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 2,
|
||||||
avatar: 'https://avatar.vercel.sh/1',
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
date: '刚刚',
|
date: '刚刚',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -37,6 +39,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '朱偏右 回复了你',
|
title: '朱偏右 回复了你',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 3,
|
||||||
avatar: 'https://avatar.vercel.sh/1',
|
avatar: 'https://avatar.vercel.sh/1',
|
||||||
date: '2024-01-01',
|
date: '2024-01-01',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -44,6 +47,7 @@ const notifications = ref<NotificationItem[]>([
|
|||||||
title: '曲丽丽 评论了你',
|
title: '曲丽丽 评论了你',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 4,
|
||||||
avatar: 'https://avatar.vercel.sh/satori',
|
avatar: 'https://avatar.vercel.sh/satori',
|
||||||
date: '1天前',
|
date: '1天前',
|
||||||
isRead: false,
|
isRead: false,
|
||||||
@ -102,6 +106,17 @@ function handleNoticeClear() {
|
|||||||
notifications.value = [];
|
notifications.value = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markRead(id: number | string) {
|
||||||
|
const item = notifications.value.find((item) => item.id === id);
|
||||||
|
if (item) {
|
||||||
|
item.isRead = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(id: number | string) {
|
||||||
|
notifications.value = notifications.value.filter((item) => item.id !== id);
|
||||||
|
}
|
||||||
|
|
||||||
function handleMakeAll() {
|
function handleMakeAll() {
|
||||||
notifications.value.forEach((item) => (item.isRead = true));
|
notifications.value.forEach((item) => (item.isRead = true));
|
||||||
}
|
}
|
||||||
@ -145,6 +160,8 @@ watch(
|
|||||||
:dot="showDot"
|
:dot="showDot"
|
||||||
:notifications="notifications"
|
:notifications="notifications"
|
||||||
@clear="handleNoticeClear"
|
@clear="handleNoticeClear"
|
||||||
|
@read="(item) => item.id && markRead(item.id)"
|
||||||
|
@remove="(item) => item.id && remove(item.id)"
|
||||||
@make-all="handleMakeAll"
|
@make-all="handleMakeAll"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { NotificationItem } from './types';
|
import type { NotificationItem } from './types';
|
||||||
|
|
||||||
import { Bell, MailCheck } from '@vben/icons';
|
import { Bell, CircleCheckBig, CircleX, MailCheck } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -35,6 +35,7 @@ const emit = defineEmits<{
|
|||||||
clear: [];
|
clear: [];
|
||||||
makeAll: [];
|
makeAll: [];
|
||||||
read: [NotificationItem];
|
read: [NotificationItem];
|
||||||
|
remove: [NotificationItem];
|
||||||
viewAll: [];
|
viewAll: [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ function handleClick(item: NotificationItem) {
|
|||||||
</div>
|
</div>
|
||||||
<VbenScrollbar v-if="notifications.length > 0">
|
<VbenScrollbar v-if="notifications.length > 0">
|
||||||
<ul class="!flex max-h-[360px] w-full flex-col">
|
<ul class="!flex max-h-[360px] w-full flex-col">
|
||||||
<template v-for="item in notifications" :key="item.title">
|
<template v-for="item in notifications" :key="item.id ?? item.title">
|
||||||
<li
|
<li
|
||||||
class="hover:bg-accent border-border relative flex w-full cursor-pointer items-start gap-5 border-t px-3 py-3"
|
class="hover:bg-accent border-border relative flex w-full cursor-pointer items-start gap-5 border-t px-3 py-3"
|
||||||
@click="handleClick(item)"
|
@click="handleClick(item)"
|
||||||
@ -107,7 +108,6 @@ function handleClick(item: NotificationItem) {
|
|||||||
<img
|
<img
|
||||||
:src="item.avatar"
|
:src="item.avatar"
|
||||||
class="aspect-square h-full w-full object-cover"
|
class="aspect-square h-full w-full object-cover"
|
||||||
role="img"
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div class="flex flex-col gap-1 leading-none">
|
<div class="flex flex-col gap-1 leading-none">
|
||||||
@ -119,6 +119,30 @@ function handleClick(item: NotificationItem) {
|
|||||||
{{ item.date }}
|
{{ item.date }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="absolute right-3 top-1/2 flex -translate-y-1/2 flex-col gap-2"
|
||||||
|
>
|
||||||
|
<VbenIconButton
|
||||||
|
v-if="!item.isRead"
|
||||||
|
size="xs"
|
||||||
|
variant="ghost"
|
||||||
|
class="h-6 px-2"
|
||||||
|
:tooltip="$t('common.confirm')"
|
||||||
|
@click.stop="emit('read', item)"
|
||||||
|
>
|
||||||
|
<CircleCheckBig class="size-4" />
|
||||||
|
</VbenIconButton>
|
||||||
|
<VbenIconButton
|
||||||
|
v-if="item.isRead"
|
||||||
|
size="xs"
|
||||||
|
variant="ghost"
|
||||||
|
class="text-destructive h-6 px-2"
|
||||||
|
:tooltip="$t('common.delete')"
|
||||||
|
@click.stop="emit('remove', item)"
|
||||||
|
>
|
||||||
|
<CircleX class="size-4" />
|
||||||
|
</VbenIconButton>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
interface NotificationItem {
|
interface NotificationItem {
|
||||||
|
id: number | string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
date: string;
|
date: string;
|
||||||
isRead?: boolean;
|
isRead?: boolean;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user