From 1c0863efe062af3ebcdecb8c10568d779f5c8295 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期一, 26 一月 2026 15:10:55 +0800
Subject: [PATCH] Merge remote-tracking branch 'refs/remotes/origin/dev_New' into dev_tide_mis_xindao
---
src/layout/components/NotificationCenter/index.vue | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 372 insertions(+), 0 deletions(-)
diff --git a/src/layout/components/NotificationCenter/index.vue b/src/layout/components/NotificationCenter/index.vue
new file mode 100644
index 0000000..6098153
--- /dev/null
+++ b/src/layout/components/NotificationCenter/index.vue
@@ -0,0 +1,372 @@
+<template>
+ <div class="notification-popover-content">
+ <div class="popover-header">
+ <span class="popover-title">娑堟伅閫氱煡</span>
+ <el-button type="primary" size="small" @click="handleMarkAllAsRead" :disabled="unreadCount === 0">
+ 涓�閿凡璇�
+ </el-button>
+ </div>
+
+ <div class="notification-content">
+ <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+ <el-tab-pane :label="`鏈(${unreadCount})`" name="unread">
+ <div v-if="unreadList.length === 0" class="empty-state">
+ <el-empty description="鏆傛棤鏈娑堟伅" />
+ </div>
+ <div v-else class="notification-list">
+ <div
+ v-for="item in unreadList"
+ :key="item.id"
+ class="notification-item"
+ >
+ <div class="notification-icon">
+ <el-icon :size="24" color="#67C23A">
+ <Bell />
+ </el-icon>
+ </div>
+ <div class="notification-content-wrapper">
+ <div class="notification-title">{{ item.noticeTitle }}</div>
+ <div class="notification-detail">{{ item.noticeContent }}</div>
+ <div class="notification-time">{{ item.createTime }}</div>
+ </div>
+ <div class="notification-action">
+ <el-button type="primary" size="small" @click="handleConfirm(item)">
+ 纭
+ </el-button>
+ </div>
+ </div>
+ </div>
+ </el-tab-pane>
+ <el-tab-pane label="宸茶" name="read">
+ <div v-if="readList.length === 0" class="empty-state">
+ <el-empty description="鏆傛棤宸茶娑堟伅" />
+ </div>
+ <div v-else class="notification-list">
+ <div
+ v-for="item in readList"
+ :key="item.id"
+ class="notification-item read"
+ >
+ <div class="notification-icon">
+ <el-icon :size="24" color="#909399">
+ <Bell />
+ </el-icon>
+ </div>
+ <div class="notification-content-wrapper">
+ <div class="notification-title">{{ item.noticeTitle }}</div>
+ <div class="notification-detail">{{ item.noticeContent }}</div>
+ <div class="notification-time">{{ item.createTime }}</div>
+ </div>
+ </div>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+
+ <!-- 鍒嗛〉 -->
+ <div class="pagination-wrapper" v-if="total > 0">
+ <el-pagination
+ v-model:current-page="pageNum"
+ v-model:page-size="pageSize"
+ :page-sizes="[10, 20, 50, 100]"
+ :total="total"
+ layout="prev, pager, next, sizes"
+ @size-change="handleSizeChange"
+ @current-change="handlePageChange"
+ />
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { Bell } from '@element-plus/icons-vue'
+import { listMessage, markAsRead, markAllAsRead, confirmMessage, getUnreadCount } from '@/api/system/message'
+import { ElMessage } from 'element-plus'
+import useUserStore from '@/store/modules/user'
+import { useRouter } from 'vue-router'
+
+const userStore = useUserStore()
+const router = useRouter()
+const emit = defineEmits(['unreadCountChange'])
+
+const activeTab = ref('unread')
+const unreadList = ref([])
+const readList = ref([])
+const unreadCount = ref(0)
+const total = ref(0)
+const pageNum = ref(1)
+const pageSize = ref(10)
+
+// 鍔犺浇娑堟伅鍒楄〃
+const loadMessages = async () => {
+ try {
+ const consigneeId = userStore.id
+ if (!consigneeId) {
+ console.warn('鏈幏鍙栧埌褰撳墠鐧诲綍鐢ㄦ埛ID')
+ return
+ }
+ const params = {
+ consigneeId: consigneeId,
+ current: pageNum.value,
+ size: pageSize.value,
+ status: activeTab.value === 'read' ? 1 : 0
+ }
+ const res = await listMessage(params)
+ if (res.code === 200) {
+ if (activeTab.value === 'unread') {
+ unreadList.value = res.data.records || []
+ } else {
+ readList.value = res.data.records || []
+ }
+ total.value = res.data.total || 0
+ }
+ } catch (error) {
+ console.error('鍔犺浇娑堟伅鍒楄〃澶辫触:', error)
+ }
+}
+
+// 鍔犺浇鏈鏁伴噺
+const loadUnreadCount = async () => {
+ try {
+ const consigneeId = userStore.id
+ if (!consigneeId) {
+ console.warn('鏈幏鍙栧埌褰撳墠鐧诲綍鐢ㄦ埛ID')
+ return
+ }
+ const res = await getUnreadCount(consigneeId)
+ if (res.code === 200) {
+ unreadCount.value = res.data || 0
+ emit('unreadCountChange', unreadCount.value)
+ }
+ } catch (error) {
+ console.error('鍔犺浇鏈鏁伴噺澶辫触:', error)
+ }
+}
+
+// 鏍囩椤靛垏鎹�
+const handleTabChange = (tab) => {
+ pageNum.value = 1
+ loadMessages()
+}
+
+// 纭娑堟伅
+const handleConfirm = async (item) => {
+ try {
+ console.log('item', item)
+ const res = await confirmMessage(item.noticeId, 1)
+ if (res.code === 200) {
+ ElMessage.success('纭鎴愬姛')
+ // 閲嶆柊鍔犺浇鏁版嵁
+ loadMessages()
+ loadUnreadCount()
+
+ // 鏍规嵁 jumpPath 杩涜椤甸潰璺宠浆
+ if (item.jumpPath) {
+ try {
+ // 瑙f瀽 jumpPath锛屽垎绂昏矾寰勫拰鏌ヨ鍙傛暟
+ const [path, queryString] = item.jumpPath.split('?')
+ let query = {}
+
+ if (queryString) {
+ // 瑙f瀽鏌ヨ鍙傛暟
+ queryString.split('&').forEach(param => {
+ const [key, value] = param.split('=')
+ if (key && value) {
+ query[key] = decodeURIComponent(value)
+ }
+ })
+ }
+
+ // 璺宠浆鍒版寚瀹氶〉闈�
+ router.push({
+ path: path,
+ query: query
+ })
+ } catch (error) {
+ console.error('椤甸潰璺宠浆澶辫触:', error)
+ }
+ }
+ }
+ } catch (error) {
+ console.error('纭娑堟伅澶辫触:', error)
+ ElMessage.error('纭澶辫触')
+ }
+}
+
+// 涓�閿凡璇�
+const handleMarkAllAsRead = async () => {
+ try {
+ const res = await markAllAsRead()
+ if (res.code === 200) {
+ ElMessage.success('宸插叏閮ㄦ爣璁颁负宸茶')
+ loadMessages()
+ loadUnreadCount()
+ }
+ } catch (error) {
+ console.error('涓�閿凡璇诲け璐�:', error)
+ ElMessage.error('鎿嶄綔澶辫触')
+ }
+}
+
+// 鍒嗛〉澶у皬鏀瑰彉
+const handleSizeChange = (size) => {
+ pageSize.value = size
+ pageNum.value = 1
+ loadMessages()
+}
+
+// 椤电爜鏀瑰彉
+const handlePageChange = (page) => {
+ pageNum.value = page
+ loadMessages()
+}
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥湭璇绘暟閲�
+onMounted(() => {
+ loadUnreadCount()
+})
+
+// 鐩戝惉鐖剁粍浠朵紶閫掔殑 visible 鐘舵�侊紙閫氳繃 watch 鍦� Navbar 涓鐞嗭級
+// 杩欓噷鍙礋璐f暟鎹姞杞斤紝涓嶆帶鍒舵樉绀�
+
+// 鏆撮湶鏂规硶渚涘閮ㄨ皟鐢�
+defineExpose({
+ loadUnreadCount,
+ loadMessages
+})
+</script>
+
+<style lang="scss" scoped>
+.notification-popover-content {
+ display: flex;
+ flex-direction: column;
+ width: 500px;
+ padding: 16px;
+}
+
+.popover-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ margin-bottom: 16px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #f0f0f0;
+
+ .popover-title {
+ font-size: 18px;
+ font-weight: 500;
+ color: #303133;
+ }
+}
+
+.notification-content {
+ max-height: 60vh;
+ display: flex;
+ flex-direction: column;
+
+ :deep(.el-tabs) {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+
+ .el-tabs__header {
+ margin-bottom: 0;
+ flex-shrink: 0;
+ padding: 0;
+ }
+
+ .el-tabs__content {
+ flex: 1;
+ overflow-y: auto;
+ min-height: 0;
+ padding-top: 16px;
+ }
+
+ .el-tab-pane {
+ height: 100%;
+ }
+ }
+}
+
+.empty-state {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 300px;
+ padding: 40px 0;
+}
+
+.notification-list {
+ .notification-item {
+ display: flex;
+ padding: 12px 0;
+ border-bottom: 1px solid #f0f0f0;
+ transition: background-color 0.3s;
+
+ &:hover {
+ background-color: #f5f7fa;
+ }
+
+ &.read {
+ opacity: 0.7;
+ }
+
+ .notification-icon {
+ flex-shrink: 0;
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #f0f9ff;
+ border-radius: 50%;
+ margin-right: 12px;
+ }
+
+ .notification-content-wrapper {
+ flex: 1;
+ min-width: 0;
+
+ .notification-title {
+ font-size: 14px;
+ font-weight: 500;
+ color: #303133;
+ margin-bottom: 8px;
+ }
+
+ .notification-detail {
+ font-size: 13px;
+ color: #606266;
+ line-height: 1.5;
+ margin-bottom: 8px;
+ word-break: break-all;
+ }
+
+ .notification-time {
+ font-size: 12px;
+ color: #909399;
+ }
+ }
+
+ .notification-action {
+ flex-shrink: 0;
+ margin-left: 12px;
+ display: flex;
+ align-items: center;
+ }
+ }
+}
+
+.pagination-wrapper {
+ margin-top: 16px;
+ padding-top: 16px;
+ border-top: 1px solid #f0f0f0;
+ display: flex;
+ justify-content: center;
+ padding-left: 0;
+ padding-right: 0;
+}
+</style>
+
--
Gitblit v1.9.3