Kaynağa Gözat

修改通知bug

lxr 2 ay önce
ebeveyn
işleme
a644bf35b5

+ 10 - 0
src/api/modules/headerNotice.ts

@@ -101,3 +101,13 @@ export const getMessageList = (params: { receiverId: string; friendType?: number
     }
   );
 };
+
+/**
+ * 未读消息数量(与商家端 tabbar getMessageNoRead 一致)
+ * GET /message/getAllNoReadCount?receiverId=xxx(alien-store)
+ */
+export const getMessageNoRead = (params: { receiverId: string }) => {
+  return httpApi.get<number>(`/alienStore/message/getAllNoReadCount`, params, {
+    loading: false
+  });
+};

+ 1 - 1
src/layouts/components/Header/ToolBarRight.vue

@@ -6,7 +6,7 @@
       <SearchMenu id="searchMenu" />
       <ThemeSetting id="themeSetting" />
       <NotificationBell id="notificationBell" />
-      <Message id="message" />
+      <!-- <Message id="message" /> -->
       <Fullscreen id="fullscreen" />
     </div>
     <span class="username">{{ userInfo.name != null ? userInfo.name : userInfo.nickName }}</span>

+ 36 - 5
src/layouts/components/Header/components/NotificationBell.vue

@@ -1,28 +1,58 @@
 <template>
   <div class="notification-bell">
-    <el-badge :value="unreadCount > 0 ? unreadCount : undefined" class="item">
-      <el-icon :size="18" class="toolBar-icon bell-icon" @click="openDialog">
+    <el-badge :value="unreadCount > 0 ? unreadCount : undefined" :max="99" class="item">
+      <el-icon :size="24" class="toolBar-icon bell-icon" @click="openDialog">
         <Bell />
       </el-icon>
     </el-badge>
     <el-dialog v-model="dialogVisible" width="880px" class="notification-dialog" destroy-on-close :show-close="true" align-center>
-      <NotificationDrawerContent v-if="dialogVisible" />
+      <NotificationDrawerContent v-if="dialogVisible" @close="handleDrawerClose" />
     </el-dialog>
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref } from "vue";
+import { ref, onMounted } from "vue";
 import { Bell } from "@element-plus/icons-vue";
+import { getMessageNoRead } from "@/api/modules/headerNotice";
+import { localGet } from "@/utils";
 import NotificationDrawerContent from "./NotificationDrawerContent.vue";
 
 const dialogVisible = ref(false);
-// 未读数量(先写死,后续接接口)
 const unreadCount = ref(0);
 
+/** 与商家端 tabbar getMessage 一致:获取未读数量 */
+async function getMessage() {
+  const phone = localGet("iphone") || localGet("geeker-user")?.userInfo?.phone;
+  if (!phone) {
+    unreadCount.value = 0;
+    return;
+  }
+  try {
+    const res: any = await getMessageNoRead({
+      receiverId: `store_${phone}`
+    });
+    const data = res?.data ?? res;
+    unreadCount.value = Number(data) || 0;
+  } catch (error) {
+    console.error("获取消息数量失败", error);
+    unreadCount.value = 0;
+  }
+}
+
 function openDialog() {
   dialogVisible.value = true;
+  getMessage();
 }
+
+function handleDrawerClose() {
+  dialogVisible.value = false;
+  getMessage();
+}
+
+onMounted(() => {
+  getMessage();
+});
 </script>
 
 <style scoped lang="scss">
@@ -31,6 +61,7 @@ function openDialog() {
   align-items: center;
   .item {
     :deep(.el-badge__content) {
+      background-color: #ff2442;
       border: none;
     }
   }

+ 181 - 47
src/layouts/components/Header/components/NotificationDrawerContent.vue

@@ -40,7 +40,7 @@
         </template>
       </div>
 
-      <!-- 右侧:通知列表 -->
+      <!-- 右侧:通知列表(系统通知/订单提醒:卡片式;互动:头像+发送者+内容+日期) -->
       <div v-if="activeTab === 'notice'" class="drawer-main-wrap">
         <div class="drawer-main">
           <div v-if="currentLoading" class="empty-tip">
@@ -50,27 +50,58 @@
             <span style="margin-left: 8px">加载中...</span>
           </div>
           <template v-else>
-            <div
-              v-for="(item, index) in currentList"
-              :key="item.id + '_' + index"
-              class="notice-card"
-              :class="{ unread: item.unread }"
-            >
-              <div class="card-row">
-                <span class="card-title">
-                  {{ item.title }}
-                  <span v-if="item.unread" class="unread-dot" />
-                </span>
-                <span class="card-date">{{ item.date }}</span>
-              </div>
-              <div class="card-content">
-                {{ item.content }}
+            <!-- 系统通知、订单提醒:卡片式 -->
+            <template v-if="activeCategory !== 'related'">
+              <div
+                v-for="(item, index) in currentList"
+                :key="item.id + '_' + index"
+                class="notice-card"
+                :class="{ unread: item.unread }"
+              >
+                <div class="card-row">
+                  <span class="card-title">
+                    {{ item.title }}
+                    <span v-if="item.unread" class="unread-dot" />
+                  </span>
+                  <span class="card-date">{{ item.date }}</span>
+                </div>
+                <div class="card-content">
+                  {{ processContent(item) }}
+                </div>
+                <div class="card-actions">
+                  <el-button size="small" @click="handleViewDetail(item)"> 查看详情 </el-button>
+                  <!-- <el-button size="small" @click="handleDelete(item, index)"> 删除 </el-button> -->
+                </div>
               </div>
-              <div class="card-actions">
-                <el-button size="small" @click="handleViewDetail(item)"> 查看详情 </el-button>
-                <el-button size="small" @click="handleDelete(item, index)"> 删除 </el-button>
+            </template>
+            <!-- 互动:头像+发送者+内容+日期+删除(与商家端 inform 列表一致) -->
+            <template v-else>
+              <div
+                v-for="(item, index) in currentList"
+                :key="item.id + '_' + index"
+                class="message-card"
+                :class="{ unread: item.unread }"
+              >
+                <div class="message-avatar">
+                  <el-avatar :size="40" :src="item.userImage">
+                    <el-icon><UserFilled /></el-icon>
+                  </el-avatar>
+                  <span v-if="item.unread" class="unread-dot message-unread-dot" />
+                </div>
+                <div class="message-body" @click="handleViewDetail(item)">
+                  <div class="message-row">
+                    <span class="message-sender">{{ item.userName || item.title || "未知" }}</span>
+                    <span class="message-date">{{ item.date }}</span>
+                  </div>
+                  <div class="message-content">
+                    {{ processContent(item) }}
+                  </div>
+                  <!-- <div class="message-actions">
+                    <el-button size="small" type="default" @click.stop="handleDelete(item, index)"> 删除 </el-button>
+                  </div> -->
+                </div>
               </div>
-            </div>
+            </template>
             <div v-if="currentList.length === 0" class="empty-tip">暂无数据</div>
           </template>
         </div>
@@ -97,8 +128,13 @@
             </el-icon>
             <span style="margin-left: 8px">加载中...</span>
           </div>
+          <!-- @click="handleMessageItemClick(item)" -->
           <template v-else>
-            <div v-for="(item, index) in currentMessageList" :key="item.id + '_' + index" class="message-card">
+            <div
+              v-for="(item, index) in currentMessageList"
+              :key="item.id + '_' + index"
+              class="message-card message-card-clickable"
+            >
               <div class="message-avatar">
                 <el-avatar :size="40" :src="item.avatar">
                   <el-icon><UserFilled /></el-icon>
@@ -110,10 +146,7 @@
                   <span class="message-date">{{ item.date }}</span>
                 </div>
                 <div class="message-content">
-                  {{ item.content }}
-                </div>
-                <div class="message-actions">
-                  <el-button size="small" type="default" @click="handleMessageDelete(item, index)"> 删除 </el-button>
+                  {{ processContent(item) }}
                 </div>
               </div>
             </div>
@@ -133,7 +166,8 @@
 </template>
 
 <script setup lang="ts">
-import { ref, computed, onMounted, watch } from "vue";
+import { ref, computed, onMounted, watch, markRaw } from "vue";
+import { useRouter } from "vue-router";
 import { House, List, User, Loading, Message, UserFilled } from "@element-plus/icons-vue";
 import { localGet } from "@/utils";
 import {
@@ -145,12 +179,20 @@ import {
 } from "@/api/modules/headerNotice";
 import type { NoFriendMessageItem } from "@/api/modules/headerNotice";
 
+const emit = defineEmits<{ (e: "close"): void }>();
+const router = useRouter();
+
 interface NoticeItem {
   id: string;
   title: string;
   content: string;
   date: string;
   unread: boolean;
+  /** 互动类可能有发送者与头像 */
+  userName?: string;
+  userImage?: string;
+  /** 类型,用于 processContent 展示 */
+  type?: string;
 }
 
 interface MessageItem {
@@ -160,6 +202,14 @@ interface MessageItem {
   date: string;
   unread: boolean;
   avatar?: string;
+  /** 发送方 id,用于跳转聊天页 */
+  senderId?: string;
+  /** 消息类型,用于 processContent 展示 */
+  type?: string;
+  /** 语音时长等,用于 getVoiceDuration */
+  voiceDuration?: number;
+  duration?: number;
+  [key: string]: any;
 }
 
 const activeTab = ref<"notice" | "message">("notice");
@@ -172,16 +222,17 @@ const noticeTypeByKey: Record<string, number> = {
   order: 2,
   related: 0
 };
+// 与商家端一致:系统通知、订单提醒、互动(其余通知均为互动类别)。icon 用 markRaw 避免被转为响应式触发 Vue 警告
 const noticeCategories = ref([
-  { key: "system", title: "系统通知", icon: House, unread: 0 },
-  { key: "order", title: "订单提醒", icon: List, unread: 0 },
-  { key: "related", title: "与我相关", icon: User, unread: 0 }
+  { key: "system", title: "系统通知", icon: markRaw(House), unread: 0 },
+  { key: "order", title: "订单提醒", icon: markRaw(List), unread: 0 },
+  { key: "related", title: "互动", icon: markRaw(User), unread: 0 }
 ]);
 
 // 消息 Tab 分类(如图:未关注人消息、消息列表)
 const messageCategories = ref([
-  { key: "unfollowed", title: "未关注人消息", icon: User, unread: 0 },
-  { key: "messageList", title: "消息列表", icon: Message, unread: 0 }
+  { key: "unfollowed", title: "未关注人消息", icon: markRaw(User), unread: 0 },
+  { key: "messageList", title: "消息列表", icon: markRaw(Message), unread: 0 }
 ]);
 
 const listByCategory = ref<Record<string, NoticeItem[]>>({
@@ -258,7 +309,11 @@ function mapNoFriendToMessageItem(item: NoFriendMessageItem): MessageItem {
     content: item.content ?? "",
     date: dateStr,
     unread: item.isRead === 0,
-    avatar: item.userImage ?? item.senderImg ?? item.storeImg ?? undefined
+    avatar: item.userImage ?? item.senderImg ?? item.storeImg ?? undefined,
+    senderId: item.senderId != null ? String(item.senderId) : undefined,
+    type: item.type,
+    voiceDuration: (item as any).voiceDuration ?? (item as any).duration,
+    duration: (item as any).duration
   };
 }
 
@@ -274,6 +329,10 @@ async function fetchNoFriendMessage() {
   try {
     const res: any = await getNoFriendMessage({ receiverId: "store_" + receiverId });
     const data = res?.data ?? res;
+    if (data.msg == "暂无承载数据") {
+      messageListByCategory.value.unfollowed = [];
+      return;
+    }
     const rawList = Array.isArray(data) ? data : data ? [data] : [];
     const list: MessageItem[] = rawList.map((item: NoFriendMessageItem) => mapNoFriendToMessageItem(item));
     messageListByCategory.value.unfollowed = list;
@@ -328,6 +387,54 @@ function getReceiverId(): string {
   return localGet("iphone") || localGet("geeker-user")?.userInfo?.phone || "";
 }
 
+// 点击消息列表项:关闭抽屉并跳转聊天页(与 storeDecoration 装修聊天一致)
+function handleMessageItemClick(item: MessageItem) {
+  console.log("item", item);
+  const receiverId = item.senderName;
+  if (!receiverId) return;
+  emit("close");
+  const params = new URLSearchParams({
+    receiverId: String(receiverId),
+    uName: encodeURIComponent(item.senderName || "用户"),
+    userImage: item.avatar ? encodeURIComponent(item.avatar) : ""
+  });
+  router.push(`/storeDecorationManagement/decorationChat?${params.toString()}`);
+}
+
+// 根据类型显示内容(与商家端一致)
+function getVoiceDuration(item: { voiceDuration?: number; duration?: number; [key: string]: any }): string {
+  const sec = item?.voiceDuration ?? item?.duration ?? 0;
+  return sec ? `${sec}` : "0";
+}
+function processContent(item: { type?: string; content?: string; [key: string]: any }): string {
+  switch (String(item.type ?? "")) {
+    case "1":
+      return item.content ?? "";
+    case "2":
+      return "[图片]";
+    case "3":
+      return "[分享]";
+    case "4":
+      return "[交易请求]";
+    case "5":
+      return "[签到]";
+    case "6":
+      return "[签到确认]";
+    case "8":
+      return "[视频]";
+    case "9":
+      return "[修改交易请求]";
+    case "10":
+      return "[定位共享]";
+    case "11":
+      return "[委托]";
+    case "13":
+      return "[语音]" + getVoiceDuration(item) + '"';
+    default:
+      return item.content ?? "";
+  }
+}
+
 // 与原有通知页面一致的 context 解析
 function parseContext(context: string | undefined): string {
   if (!context) return "";
@@ -339,6 +446,15 @@ function parseContext(context: string | undefined): string {
   }
 }
 
+// 互动类内容:商家端 context 可能为 "类型|split|内容1|split|内容2",取第一段或解析 JSON
+function getInteractionContent(context: string | undefined): string {
+  if (!context) return "";
+  if (typeof context === "string" && context.includes("|split|")) {
+    return context.split("|split|")[0]?.trim() || context;
+  }
+  return parseContext(context);
+}
+
 // 统一请求:/alienStorePlatform/notice/getNoticeList,noticeType 系统=1、订单提醒=2、与我相关=0,分页查询
 async function fetchNoticeList(catKey: string) {
   const receiverId = getReceiverId();
@@ -360,13 +476,25 @@ async function fetchNoticeList(catKey: string) {
     const data = res?.data ?? res;
     const records = data?.records ?? data?.list ?? [];
     const total = data?.total ?? 0;
-    const list: NoticeItem[] = records.map((item: any) => ({
-      id: String(item.id),
-      title: item.title ?? "",
-      content: parseContext(item.context ?? item.content),
-      date: item.createdTime ?? "",
-      unread: !item.isRead
-    }));
+    const isRelated = catKey === "related";
+    const list: NoticeItem[] = records.map((item: any) => {
+      const rawContext = item.context ?? item.content ?? "";
+      const content = isRelated ? getInteractionContent(rawContext) : parseContext(rawContext);
+      const dateRaw = item.createdTime ?? "";
+      const dateStr = dateRaw.includes(" ") ? dateRaw.split(" ")[0].replace(/-/g, "/") : dateRaw.replace(/-/g, "/");
+      return {
+        id: String(item.id),
+        title: item.title ?? "",
+        content,
+        date: dateStr,
+        unread: !item.isRead,
+        type: item.type,
+        ...(isRelated && {
+          userName: item.userName ?? item.senderName ?? item.title ?? "",
+          userImage: item.userImage ?? item.storeImg ?? item.senderImg
+        })
+      };
+    });
     listByCategory.value[catKey] = list;
     paginationByCategory.value[catKey].total = total;
   } catch (e) {
@@ -448,14 +576,6 @@ function handleDelete(item: NoticeItem, index: number) {
   const cat = noticeCategories.value.find(c => c.key === key);
   if (cat && item.unread) cat.unread = Math.max(0, cat.unread - 1);
 }
-
-function handleMessageDelete(item: MessageItem, index: number) {
-  const key = messageCategory.value;
-  messageListByCategory.value[key] = currentMessageList.value.filter((_, i) => i !== index);
-  const cat = messageCategories.value.find(c => c.key === key);
-  if (cat && item.unread) cat.unread = Math.max(0, cat.unread - 1);
-}
-
 onMounted(() => {
   refreshAllNoticeCategories();
 });
@@ -576,12 +696,26 @@ watch(activeTab, val => {
   &:last-child {
     border-bottom: none;
   }
+  &.message-card-clickable {
+    cursor: pointer;
+  }
   .message-avatar {
+    position: relative;
     flex-shrink: 0;
+    .message-unread-dot {
+      position: absolute;
+      top: 0;
+      right: 0;
+      width: 8px;
+      height: 8px;
+      background: var(--el-color-danger);
+      border-radius: 50%;
+    }
   }
   .message-body {
     flex: 1;
     min-width: 0;
+    cursor: pointer;
   }
   .message-row {
     display: flex;