Browse Source

未读消息

chenjj 3 months ago
parent
commit
8f2e35ba6d
3 changed files with 157 additions and 87 deletions
  1. 116 75
      src/layout/components/Sidebar/SidebarItem.vue
  2. 20 10
      src/store/modules/user.js
  3. 21 2
      src/views/message/index.vue

+ 116 - 75
src/layout/components/Sidebar/SidebarItem.vue

@@ -1,100 +1,141 @@
 <template>
   <div v-if="!item.hidden">
-    <template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
+    <template
+      v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
         <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
-          <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>
-          <template #title><span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span></template>
+          <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
+          <template #title>
+            <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span>
+            <el-badge v-if="onlyOneChild.meta?.title == '消息中心' && xxtzGqxxtzNum > 0" 
+                  :value="xxtzGqxxtzNum" 
+                  class="item">
+           </el-badge>
+          </template>
         </el-menu-item>
       </app-link>
     </template>
-
+  
     <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
       <template v-if="item.meta" #title>
         <svg-icon :icon-class="item.meta && item.meta.icon" />
         <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
+  
+        <!-- 只有当消息数量 > 0 时才显示 -->
+        <el-badge v-if="item.meta?.title === '消息中心' && xxtzGqxxtzNum > 0" 
+                  :value="xxtzGqxxtzNum" 
+                  class="item">
+        </el-badge>
       </template>
-
+  
       <sidebar-item
-        v-for="(child, index) in item.children"
-        :key="child.path + index"
-        :is-nest="true"
+        v-for="(child, index) in item.children" 
+        :key="child.path + index" 
+        :is-nest="true" 
         :item="child"
-        :base-path="resolvePath(child.path)"
+        :base-path="resolvePath(child.path)" 
         class="nest-menu"
+        :badgeObj="props.badgeObj"
       />
     </el-sub-menu>
   </div>
-</template>
-
-<script setup>
-import { isExternal } from '@/utils/validate'
-import AppLink from './Link'
-import { getNormalPath } from '@/utils/ruoyi'
-
-const props = defineProps({
-  // route object
-  item: {
-    type: Object,
-    required: true
-  },
-  isNest: {
-    type: Boolean,
-    default: false
-  },
-  basePath: {
-    type: String,
-    default: ''
-  }
-})
-
-const onlyOneChild = ref({});
-
-function hasOneShowingChild(children = [], parent) {
-  if (!children) {
-    children = [];
-  }
-  const showingChildren = children.filter(item => {
-    if (item.hidden) {
-      return false
+  
+  </template>
+  
+  <script setup>
+  import { isExternal } from '@/utils/validate'
+  import AppLink from './Link'
+  import { getNormalPath } from '@/utils/ruoyi'
+  import { onMounted } from 'vue';
+  import useUserStore from '@/store/modules/user'
+import { computed } from 'vue';
+  const props = defineProps({
+    // route object
+    item: {
+      type: Object,
+      required: true
+    },
+    isNest: {
+      type: Boolean,
+      default: false
+    },
+    basePath: {
+      type: String,
+      default: ''
+    },
+    badgeObj: {
+      type: Object,
+      default: () => ({})
     }
-    onlyOneChild.value = item
-    return true
   })
 
-  // When there is only one child router, the child router is displayed by default
-  if (showingChildren.length === 1) {
-    return true
-  }
-
-  // Show parent if there are no child router to display
-  if (showingChildren.length === 0) {
-    onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
-    return true
-  }
-
-  return false
-};
-
-function resolvePath(routePath, routeQuery) {
-  if (isExternal(routePath)) {
-    return routePath
+  const userStore = useUserStore()
+  
+  const onlyOneChild = ref({});
+  const unreadCount = ref(0);  // 存储未读消息数量
+  const xxtzGqxxtzNum = computed(()=>{
+  
+    return userStore.xxtzGqxxtzNum
+  })
+  function hasOneShowingChild(children = [], parent) {
+    if (!children) {
+      children = [];
+    }
+    const showingChildren = children.filter(item => {
+      if (item.hidden) {
+        return false
+      } else {
+        // Temp set(will be used if only has one showing child)
+        onlyOneChild.value = item
+        return true
+      }
+    })
+  
+    // When there is only one child router, the child router is displayed by default
+    if (showingChildren.length === 1) {
+      return true
+    }
+  
+    // Show parent if there are no child router to display
+    if (showingChildren.length === 0) {
+      onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
+      return true
+    }
+  
+    return false
+  };
+  
+  function resolvePath(routePath, routeQuery) {
+    if (isExternal(routePath)) {
+      return routePath
+    }
+    if (isExternal(props.basePath)) {
+      return props.basePath
+    }
+    if (routeQuery) {
+      let query = JSON.parse(routeQuery);
+      return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
+    }
+    return getNormalPath(props.basePath + '/' + routePath)
   }
-  if (isExternal(props.basePath)) {
-    return props.basePath
+  
+  function hasTitle(title) {
+    if (title.length > 5) {
+      return title;
+    } else {
+      return "";
+    }
   }
-  if (routeQuery) {
-    let query = JSON.parse(routeQuery);
-    return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
+  
+  
+  </script>
+  <style scoped>
+  .item{
+    top: -18px;
+    margin-left: 15px;
   }
-  return getNormalPath(props.basePath + '/' + routePath)
-}
-
-function hasTitle(title){
-  if (title.length > 5) {
-    return title;
-  } else {
-    return "";
+  .item-content{
+    top: -85px;
+    margin-left: 140px;
   }
-}
-</script>
+  </style>

+ 20 - 10
src/store/modules/user.js

@@ -13,10 +13,11 @@ const useUserStore = defineStore(
       avatar: '',
       roles: [],
       permissions: [],
-      phonenumber:'',
-      areaType:'', //区域类型
-      user:{},
-      ws:null
+      phonenumber: '',
+      areaType: '', //区域类型
+      user: {},
+      ws: null,
+      xxtzGqxxtzNum: 0
     }),
     actions: {
       // 登录
@@ -55,15 +56,24 @@ const useUserStore = defineStore(
             this.avatar = avatar
 
             this.phonenumber = user.phonenumber;
-            this.areaType =user.dept.areaType;
+            this.areaType = user.dept.areaType;
             this.user = user;
             //获取账户时,连接soket 
-          this.ws = new WebSocketClient(user.userId);
-          // 连接WebSocket
-          this.ws.connect()
+            this.ws = new WebSocketClient(user.areaType === '3'?user.districtCode:'0');
+
+            // 监听消息事件
+            this.ws.on('message', (data) => {
+              // 处理消息逻辑
+              if (data.type === 'msgUnreadCount') {
+                console.log("处理消息逻辑", data.data)
+                this.xxtzGqxxtzNum = data.data;
+              }
+            })
+            // 连接WebSocket
+            this.ws.connect()
             resolve(res)
           }).catch(error => {
-						console.log("TCL: getInfo -> error", error)
+            console.log("TCL: getInfo -> error", error)
             reject(error)
           })
         })
@@ -82,7 +92,7 @@ const useUserStore = defineStore(
           })
         })
       },
-  
+
     }
   })
 

+ 21 - 2
src/views/message/index.vue

@@ -14,7 +14,10 @@
             <div class="user-name">{{ user.userName }}</div>
             <div class="last-message">{{ user.newestMsgContent }}</div>
           </div>
-          <div class="message-time">{{ formatTime(user.newestMsgTime) }}</div>
+          <div class="message-time">
+            <div class="newestMsgTime">{{ formatTime(user.newestMsgTime) }}</div>
+            <div class="msgUnreadCount" v-if="user.msgUnreadCount && user.msgUnreadCount>0">{{ user.msgUnreadCount }}</div>
+          </div>
         </div>
       </div>
     </div>
@@ -135,7 +138,7 @@ const selectUser = async (user) => {
     console.log("TCL: selectUser -> user", user)
     currentPage.value = 1
     hasMore.value = true
-    // await setRead({ conversationRecordId: user.conversationRecordId, system: 3, }); //设置已读
+    await setRead({...user, conversationRecordId: user.conversationRecordId, system: 3, }); //设置已读
     const res = await getListConversationInfo({
       conversationRecordId: user.conversationRecordId,
       pageNum: currentPage.value,
@@ -459,8 +462,24 @@ onUnmounted(() => {
   color: #999;
   margin-bottom: 4px;
   text-align: left;
+
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
 }
+.msgUnreadCount {
+  background: red;
+    color: #fff;
+    margin-top: 4px;
+    width: 15px;
+    height: 15px;
 
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
 .message-bubble {
   background-color: #f5f5f5;
   padding: 10px;