From ca8bc52079b925d3829fdbf7bdf251bcfe1ecb98 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Mon, 10 Mar 2025 22:55:02 +0800 Subject: [PATCH] feat: add batch delete and mark-as-read supports for notifications (#7282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /area ui /kind feature /milestone 2.20.x #### What this PR does / why we need it: Add support for batch deletion and batch marking as read for notifications in the UC. image #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/7164 #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note 个人中心的消息管理支持批量删除和批量标记为已读 ``` --- ui/src/locales/_missing_translations_es.yaml | 10 ++ .../locales/_missing_translations_zh-CN.yaml | 1 + ui/src/locales/en.yaml | 8 ++ ui/src/locales/zh-CN.yaml | 9 +- ui/src/locales/zh-TW.yaml | 6 + .../modules/notifications/Notifications.vue | 116 ++++++++++++++++-- .../components/NotificationListItem.vue | 3 + ui/uc-src/modules/notifications/module.ts | 1 + 8 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 ui/src/locales/_missing_translations_zh-CN.yaml diff --git a/ui/src/locales/_missing_translations_es.yaml b/ui/src/locales/_missing_translations_es.yaml index 43b4220f3..562298788 100644 --- a/ui/src/locales/_missing_translations_es.yaml +++ b/ui/src/locales/_missing_translations_es.yaml @@ -463,6 +463,10 @@ core: revoke: title: Revoke device description: Are you sure you want to revoke this device? After revoking, this device will be logged out + revoke_others: + title: Revoke all other devices + description: Are you sure you want to revoke all other devices? After you revoke, other devices will be logged out + toast_success: Login status of other devices has been revoked uc_notification: title: Notifications tabs: @@ -478,6 +482,12 @@ core: delete: description: Are you sure you want to delete this notification? title: Delete + delete_all: + title: Delete all current notifications + description: Are you sure you want to delete all current notifications? You cannot restore them after deletion. + mark_all_as_read: + title: Mark all current notifications as read + description: Mark all current messages as read. Do you want to continue? overview: fields: activated_theme: Activated theme diff --git a/ui/src/locales/_missing_translations_zh-CN.yaml b/ui/src/locales/_missing_translations_zh-CN.yaml new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/ui/src/locales/_missing_translations_zh-CN.yaml @@ -0,0 +1 @@ +{} diff --git a/ui/src/locales/en.yaml b/ui/src/locales/en.yaml index 20bf08b5e..d6c5652ed 100644 --- a/ui/src/locales/en.yaml +++ b/ui/src/locales/en.yaml @@ -1398,6 +1398,14 @@ core: delete: description: Are you sure you want to delete this notification? title: Delete + delete_all: + title: Delete all current notifications + description: >- + Are you sure you want to delete all current notifications? You cannot + restore them after deletion. + mark_all_as_read: + title: Mark all current notifications as read + description: Mark all current messages as read. Do you want to continue? setting: title: Settings overview: diff --git a/ui/src/locales/zh-CN.yaml b/ui/src/locales/zh-CN.yaml index 3a70b38d1..2096b94bd 100644 --- a/ui/src/locales/zh-CN.yaml +++ b/ui/src/locales/zh-CN.yaml @@ -1170,8 +1170,7 @@ core: manual: label: 如果无法扫描二维码,点击查看代替步骤 help: 使用以下代码手动配置验证器应用: - tips: >- - 请妥善保管您的两步验证设备,如果设备丢失或损坏,你将无法登录系统。建议你在多个设备上安装验证器应用,或保存好密钥的备份,以防主要设备无法使用。 + tips: 请妥善保管您的两步验证设备,如果设备丢失或损坏,你将无法登录系统。建议你在多个设备上安装验证器应用,或保存好密钥的备份,以防主要设备无法使用。 pat: operations: delete: @@ -1303,6 +1302,12 @@ core: delete: title: 删除消息 description: 确定要删除该消息吗? + delete_all: + title: 删除当前所有通知 + description: 确定要删除当前所有通知吗?删除后将无法恢复。 + mark_all_as_read: + title: 将当前所有通知标记为已读 + description: 将当前所有通知标记为已读,是否继续? setting: title: 设置 overview: diff --git a/ui/src/locales/zh-TW.yaml b/ui/src/locales/zh-TW.yaml index e91a48423..5d15ac13b 100644 --- a/ui/src/locales/zh-TW.yaml +++ b/ui/src/locales/zh-TW.yaml @@ -1287,6 +1287,12 @@ core: delete: description: 確定要刪除該訊息嗎? title: 刪除訊息 + delete_all: + title: 刪除目前所有通知 + description: 確定要刪除目前所有通知嗎?刪除後將無法復原。 + mark_all_as_read: + title: 將目前所有通知標示為已讀 + description: 將目前所有通知標示為已讀,是否繼續? setting: title: 設置 overview: diff --git a/ui/uc-src/modules/notifications/Notifications.vue b/ui/uc-src/modules/notifications/Notifications.vue index 3d0a8e6f6..3924a928a 100644 --- a/ui/uc-src/modules/notifications/Notifications.vue +++ b/ui/uc-src/modules/notifications/Notifications.vue @@ -2,7 +2,11 @@ import { useUserStore } from "@/stores/user"; import { ucApiClient } from "@halo-dev/api-client"; import { + Dialog, + IconCheckboxCircle, + IconDeleteBin, IconNotificationBadgeLine, + Toast, VButton, VCard, VEmpty, @@ -10,13 +14,16 @@ import { VPageHeader, VTabbar, } from "@halo-dev/components"; -import { useQuery } from "@tanstack/vue-query"; +import { useQuery, useQueryClient } from "@tanstack/vue-query"; import { useRouteQuery } from "@vueuse/router"; import { OverlayScrollbarsComponent } from "overlayscrollbars-vue"; import { computed } from "vue"; +import { useI18n } from "vue-i18n"; import NotificationContent from "./components/NotificationContent.vue"; import NotificationListItem from "./components/NotificationListItem.vue"; +const queryClient = useQueryClient(); +const { t } = useI18n(); const { currentUser } = useUserStore(); const activeTab = useRouteQuery("tab", "unread"); @@ -54,6 +61,71 @@ const selectedNotification = computed(() => { (item) => item.metadata.name === selectedNotificationName.value ); }); + +function handleDeleteNotifications() { + Dialog.warning({ + title: t("core.uc_notification.operations.delete_all.title"), + description: t("core.uc_notification.operations.delete_all.description"), + confirmText: t("core.common.buttons.confirm"), + cancelText: t("core.common.buttons.cancel"), + confirmType: "danger", + onConfirm: async () => { + if (!notifications.value || notifications.value.items.length === 0) { + return; + } + + if (!currentUser) { + throw new Error("Current user is not found"); + } + + for (const notification of notifications.value.items) { + await ucApiClient.notification.notification.deleteSpecifiedNotification( + { + username: currentUser.metadata.name, + name: notification.metadata.name, + } + ); + } + + await queryClient.invalidateQueries({ queryKey: ["user-notifications"] }); + + Toast.success(t("core.common.toast.delete_success")); + }, + }); +} + +function handleMarkAllAsRead() { + Dialog.warning({ + title: t("core.uc_notification.operations.mark_all_as_read.title"), + description: t( + "core.uc_notification.operations.mark_all_as_read.description" + ), + confirmText: t("core.common.buttons.confirm"), + cancelText: t("core.common.buttons.cancel"), + onConfirm: async () => { + if (!notifications.value || notifications.value.items.length === 0) { + return; + } + + if (!currentUser) { + throw new Error("Current user is not found"); + } + + const names = notifications.value?.items.map( + (notification) => notification.metadata.name + ); + + await ucApiClient.notification.notification.markNotificationsAsRead({ + username: currentUser.metadata.name, + markSpecifiedRequest: { + names, + }, + }); + + await queryClient.invalidateQueries({ queryKey: ["user-notifications"] }); + }, + }); +}