From a563ea879ef5fb6897e76d2df661e465dce2ab9b Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期一, 01 六月 2026 15:02:27 +0800
Subject: [PATCH] Merge branch 'dev_新疆_大罗素马铃薯new' of http://114.132.189.42:9002/r/product-inventory-management into dev_新疆_大罗素马铃薯new
---
src/layout/components/NotificationCenter/index.vue | 386 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 386 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..66f489a
--- /dev/null
+++ b/src/layout/components/NotificationCenter/index.vue
@@ -0,0 +1,386 @@
+<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;
+ background: rgba(255, 255, 255, 0.92);
+ }
+
+ .popover-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ margin-bottom: 16px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid var(--surface-border);
+
+ .popover-title {
+ font-size: 18px;
+ font-weight: 500;
+ color: var(--text-primary);
+ }
+ }
+
+ .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 rgba(148, 163, 184, 0.18);
+ transition: background-color 0.3s;
+
+ &:hover {
+ background-color: #f8fbff;
+ }
+
+ &.read {
+ opacity: 0.7;
+ }
+
+ .notification-icon {
+ flex-shrink: 0;
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: rgba(59, 130, 246, 0.12);
+ border-radius: 50%;
+ margin-right: 12px;
+ }
+
+ .notification-content-wrapper {
+ flex: 1;
+ min-width: 0;
+
+ .notification-title {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--text-primary);
+ margin-bottom: 8px;
+ }
+
+ .notification-detail {
+ font-size: 13px;
+ color: var(--text-secondary);
+ line-height: 1.5;
+ margin-bottom: 8px;
+ word-break: break-all;
+ }
+
+ .notification-time {
+ font-size: 12px;
+ color: var(--text-tertiary);
+ }
+ }
+
+ .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 var(--surface-border);
+ display: flex;
+ justify-content: center;
+ padding-left: 0;
+ padding-right: 0;
+ }
+</style>
+
--
Gitblit v1.9.3