浏览代码

feat: add read and delete for each notification

shixi 7 月之前
父节点
当前提交
dbc5ea32ae

+ 17 - 0
apps/web-antd/src/layouts/basic.vue

@@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
 
 const notifications = ref<NotificationItem[]>([
   {
+    id: 1,
     avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
     date: '3小时前',
     isRead: true,
@@ -30,6 +31,7 @@ const notifications = ref<NotificationItem[]>([
     title: '收到了 14 份新周报',
   },
   {
+    id: 2,
     avatar: 'https://avatar.vercel.sh/1',
     date: '刚刚',
     isRead: false,
@@ -37,6 +39,7 @@ const notifications = ref<NotificationItem[]>([
     title: '朱偏右 回复了你',
   },
   {
+    id: 3,
     avatar: 'https://avatar.vercel.sh/1',
     date: '2024-01-01',
     isRead: false,
@@ -44,6 +47,7 @@ const notifications = ref<NotificationItem[]>([
     title: '曲丽丽 评论了你',
   },
   {
+    id: 4,
     avatar: 'https://avatar.vercel.sh/satori',
     date: '1天前',
     isRead: false,
@@ -102,6 +106,17 @@ function handleNoticeClear() {
   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() {
   notifications.value.forEach((item) => (item.isRead = true));
 }
@@ -144,6 +159,8 @@ watch(
         :dot="showDot"
         :notifications="notifications"
         @clear="handleNoticeClear"
+        @read="(item) => item.id && markRead(item.id)"
+        @remove="(item) => item.id && remove(item.id)"
         @make-all="handleMakeAll"
       />
     </template>

+ 17 - 0
apps/web-ele/src/layouts/basic.vue

@@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
 
 const notifications = ref<NotificationItem[]>([
   {
+    id: 1,
     avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
     date: '3小时前',
     isRead: true,
@@ -30,6 +31,7 @@ const notifications = ref<NotificationItem[]>([
     title: '收到了 14 份新周报',
   },
   {
+    id: 2,
     avatar: 'https://avatar.vercel.sh/1',
     date: '刚刚',
     isRead: false,
@@ -37,6 +39,7 @@ const notifications = ref<NotificationItem[]>([
     title: '朱偏右 回复了你',
   },
   {
+    id: 3,
     avatar: 'https://avatar.vercel.sh/1',
     date: '2024-01-01',
     isRead: false,
@@ -44,6 +47,7 @@ const notifications = ref<NotificationItem[]>([
     title: '曲丽丽 评论了你',
   },
   {
+    id: 4,
     avatar: 'https://avatar.vercel.sh/satori',
     date: '1天前',
     isRead: false,
@@ -102,6 +106,17 @@ function handleNoticeClear() {
   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() {
   notifications.value.forEach((item) => (item.isRead = true));
 }
@@ -144,6 +159,8 @@ watch(
         :dot="showDot"
         :notifications="notifications"
         @clear="handleNoticeClear"
+        @read="(item) => item.id && markRead(item.id)"
+        @remove="(item) => item.id && remove(item.id)"
         @make-all="handleMakeAll"
       />
     </template>

+ 17 - 0
apps/web-naive/src/layouts/basic.vue

@@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
 
 const notifications = ref<NotificationItem[]>([
   {
+    id: 1,
     avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
     date: '3小时前',
     isRead: true,
@@ -30,6 +31,7 @@ const notifications = ref<NotificationItem[]>([
     title: '收到了 14 份新周报',
   },
   {
+    id: 2,
     avatar: 'https://avatar.vercel.sh/1',
     date: '刚刚',
     isRead: false,
@@ -37,6 +39,7 @@ const notifications = ref<NotificationItem[]>([
     title: '朱偏右 回复了你',
   },
   {
+    id: 3,
     avatar: 'https://avatar.vercel.sh/1',
     date: '2024-01-01',
     isRead: false,
@@ -44,6 +47,7 @@ const notifications = ref<NotificationItem[]>([
     title: '曲丽丽 评论了你',
   },
   {
+    id: 4,
     avatar: 'https://avatar.vercel.sh/satori',
     date: '1天前',
     isRead: false,
@@ -102,6 +106,17 @@ function handleNoticeClear() {
   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() {
   notifications.value.forEach((item) => (item.isRead = true));
 }
@@ -145,6 +160,8 @@ watch(
         :dot="showDot"
         :notifications="notifications"
         @clear="handleNoticeClear"
+        @read="(item) => item.id && markRead(item.id)"
+        @remove="(item) => item.id && remove(item.id)"
         @make-all="handleMakeAll"
       />
     </template>

+ 27 - 3
packages/effects/layouts/src/widgets/notification/notification.vue

@@ -1,7 +1,7 @@
 <script lang="ts" setup>
 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 {
@@ -35,6 +35,7 @@ const emit = defineEmits<{
   clear: [];
   makeAll: [];
   read: [NotificationItem];
+  remove: [NotificationItem];
   viewAll: [];
 }>();
 
@@ -91,7 +92,7 @@ function handleClick(item: NotificationItem) {
       </div>
       <VbenScrollbar v-if="notifications.length > 0">
         <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
               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)"
@@ -107,7 +108,6 @@ function handleClick(item: NotificationItem) {
                 <img
                   :src="item.avatar"
                   class="aspect-square h-full w-full object-cover"
-                  role="img"
                 />
               </span>
               <div class="flex flex-col gap-1 leading-none">
@@ -119,6 +119,30 @@ function handleClick(item: NotificationItem) {
                   {{ item.date }}
                 </p>
               </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>
           </template>
         </ul>

+ 1 - 0
packages/effects/layouts/src/widgets/notification/types.ts

@@ -1,4 +1,5 @@
 interface NotificationItem {
+  id: number | string;
   avatar: string;
   date: string;
   isRead?: boolean;