Ver código fonte

系统通知模式改造

zhangdaiscott 2 anos atrás
pai
commit
ba98c26b01

+ 2 - 1
src/layouts/default/header/components/notify/data.ts

@@ -148,7 +148,8 @@ export const tabListData: TabItem[] = [
         id: '000000008',
         avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
         title: '标题',
-        description: '请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容',
+        description:
+          '请将鼠标移动到此处,以便测试超长的消息在此处将如何处理。本例中设置的描述最大行数为2,超过2行的描述内容将被省略并且可以通过tooltip查看完整内容',
         datetime: '2017-08-07',
         type: '2',
         clickClose: true,

+ 72 - 44
src/layouts/default/header/components/notify/index.vue

@@ -1,36 +1,15 @@
 <template>
-  <div :class="prefixCls">
-    <Popover v-model:visible="popoverVisible" title="" trigger="click" :overlayClassName="`${prefixCls}__overlay`">
-      <Badge :count="count" :overflowCount="9" :offset="[-4, 10]" :numberStyle="numberStyle">
-        <BellOutlined />
-      </Badge>
-      <template #content>
-        <Tabs>
-          <template v-for="item in listData" :key="item.key">
-            <TabPane>
-              <template #tab>
-                {{ item.name }}
-                <span v-if="item.list.length !== 0">({{ item.count }})</span>
-              </template>
-              <!-- 绑定title-click事件的通知列表中标题是“可点击”的-->
-              <NoticeList :list="item.list" @title-click="onNoticeClick" />
-            </TabPane>
-          </template>
-        </Tabs>
-        <a-row class="bottom-buttons">
-          <a-col :span="count === 0 ? 0 : 12">
-            <a-button @click="onEmptyNotify" type="dashed" block>清空消息</a-button>
-          </a-col>
-          <a-col :span="count === 0 ? 24 : 12">
-            <a-button @click="popoverVisible = false" type="dashed" block>
-              <router-link to="/monitor/mynews">查看更多</router-link>
-            </a-button>
-          </a-col>
-        </a-row>
-      </template>
-    </Popover>
+  <div class="msg-clock" :class="prefixCls">
+    <Badge :count="count" :overflowCount="9" :offset="[-4, 2]" :numberStyle="numberStyle" @click="clickBadge">
+      <BellOutlined />
+    </Badge>
+    
     <DynamicNotice ref="dynamicNoticeRef" v-bind="dynamicNoticeProps" />
     <DetailModal @register="registerDetail" />
+
+    <sys-message-modal @register="registerMessageModal" @refresh="reloadCount"></sys-message-modal>
+  </div>
+  <div>
   </div>
 </template>
 <script lang="ts">
@@ -49,7 +28,12 @@
   import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
   import { readAllMsg } from '/@/views/monitor/mynews/mynews.api';
   import { getToken } from '/@/utils/auth';
+  import md5 from 'crypto-js/md5';
+
+  import SysMessageModal from '/@/views/system/message/components/SysMessageModal.vue'
+  
   export default defineComponent({
+    inheritAttrs: false,
     components: {
       Popover,
       BellOutlined,
@@ -59,6 +43,7 @@
       NoticeList,
       DetailModal,
       DynamicNotice,
+      SysMessageModal,
     },
     setup() {
       const { prefixCls } = useDesign('header-notify');
@@ -67,17 +52,7 @@
       const glob = useGlobSetting();
       const dynamicNoticeProps = reactive({ path: '', formData: {} });
       const [registerDetail, detailModal] = useModal();
-      const popoverVisible = ref<boolean>(false);
       const listData = ref(tabListData);
-      listData.value[0].list = [];
-      listData.value[1].list = [];
-      listData.value[0].count = 0;
-      listData.value[1].count = 0;
-
-      onMounted(() => {
-        initWebSocket();
-      });
-
       const count = computed(() => {
         let count = 0;
         for (let i = 0; i < listData.value.length; i++) {
@@ -85,7 +60,23 @@
         }
         return count;
       });
+      const chatRef = ref();
+
+      const [registerMessageModal, { openModal: openMessageModal }] = useModal();
+      function clickBadge(){
+        //消息列表弹窗前去除角标
+        for (let i = 0; i < listData.value.length; i++) {
+          listData.value[i].count = 0;
+        }
+        openMessageModal(true, {})
+      }
+      
+      const popoverVisible = ref<boolean>(false);
+      onMounted(() => {
+       initWebSocket();
+      });
 
+      const messageCount = ref<number>(0)
       function mapAnnouncement(item) {
         return {
           ...item,
@@ -105,6 +96,13 @@
           listData.value[1].list = sysMsgList.map(mapAnnouncement);
           listData.value[0].count = anntMsgTotal;
           listData.value[1].count = sysMsgTotal;
+          //update-begin-author:taoyan date:2022-8-30 for: 消息数量改变触发chat组件事件
+          let msgCount = anntMsgTotal+sysMsgTotal;
+          //update-begin-author:wangshuai date:2022-09-02 for: 消息未读数为0也需要传递,因为聊天需要计算总数
+          chatRef.value.updateMessageCount(msgCount);
+          messageCount.value = msgCount
+          //update-end-author:wangshuai date:2022-09-02 for: 消息未读数为0也需要传递,因为聊天需要计算总数
+          //update-end-author:taoyan date:2022-8-30 for: 消息数量改变触发chat组件事件
         } catch (e) {
           console.warn('系统消息通知异常:', e);
         }
@@ -134,8 +132,10 @@
 
       // 初始化 WebSocket
       function initWebSocket() {
-        let userId = unref(userStore.getUserInfo).id;
         let token = getToken();
+        //将登录token生成一个短的标识
+        let wsClientId = md5(token);
+        let userId = unref(userStore.getUserInfo).id + "_" + wsClientId;
         // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
         let url = glob.domainUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
         connectWebSocket(url);
@@ -146,9 +146,9 @@
         if (data.cmd === 'topic' || data.cmd === 'user') {
           //update-begin-author:taoyan date:2022-7-13 for: VUEN-1674【严重bug】系统通知,为什么必须刷新右上角才提示
           //后台保存数据太慢 前端延迟刷新消息
-          setTimeout(() => {
+          setTimeout(()=>{
             loadData();
-          }, 1000);
+          }, 1000)
           //update-end-author:taoyan date:2022-7-13 for: VUEN-1674【严重bug】系统通知,为什么必须刷新右上角才提示
         }
       }
@@ -158,17 +158,37 @@
         popoverVisible.value = false;
         readAllMsg({}, loadData);
       }
+      async function reloadCount(id){
+        try {
+          await editCementSend(id);
+          await loadData();
+        } catch (e) {
+          console.error(e);
+        }
+      }
+
+      /**
+       * 获取消息未读数
+       */
+      function getSystemUnreadNum() {
+        chatRef.value.updateMessageCount(messageCount.value);
+      }
 
       return {
         prefixCls,
         listData,
         count,
+        clickBadge,
+        registerMessageModal,
+        reloadCount,
         onNoticeClick,
         onEmptyNotify,
         numberStyle: {},
         popoverVisible,
         registerDetail,
         dynamicNoticeProps,
+        chatRef,
+        getSystemUnreadNum
       };
     },
   });
@@ -187,7 +207,7 @@
         padding: 0;
       }
 
-      .ant-tabs-bar {
+      .ant-tabs-nav {
         margin-bottom: 12px;
       }
 
@@ -268,4 +288,12 @@
       }
     }
   }
+  
+  /** VUEN-2222 鼠标放上去,怎么不是手势*/
+  .msg-clock{
+    cursor: pointer;
+    &:hover{
+      background-color: #f6f6f6;
+    }
+  }
 </style>

+ 272 - 0
src/layouts/default/header/components/notify/index_old.vue

@@ -0,0 +1,272 @@
+<template>
+  <div :class="prefixCls">
+    <Popover v-model:visible="popoverVisible" title="" trigger="click" :overlayClassName="`${prefixCls}__overlay`">
+      <Badge :count="count" :overflowCount="9" :offset="[-4, 10]" :numberStyle="numberStyle">
+        <BellOutlined />
+      </Badge>
+      <template #content>
+        <Tabs>
+          <template v-for="item in listData" :key="item.key">
+            <TabPane>
+              <template #tab>
+                {{ item.name }}
+                <span v-if="item.list.length !== 0">({{ item.count }})</span>
+              </template>
+              <!-- 绑定title-click事件的通知列表中标题是“可点击”的-->
+              <NoticeList :list="item.list" @title-click="onNoticeClick" />
+            </TabPane>
+          </template>
+        </Tabs>
+        <a-row class="bottom-buttons">
+          <a-col :span="count === 0 ? 0 : 12">
+            <a-button @click="onEmptyNotify" type="dashed" block>清空消息</a-button>
+          </a-col>
+          <a-col :span="count === 0 ? 24 : 12">
+            <a-button @click="popoverVisible = false" type="dashed" block>
+              <router-link to="/monitor/mynews">查看更多</router-link>
+            </a-button>
+          </a-col>
+        </a-row>
+      </template>
+    </Popover>
+    <DynamicNotice ref="dynamicNoticeRef" v-bind="dynamicNoticeProps" />
+    <DetailModal @register="registerDetail" />
+  </div>
+</template>
+<script lang="ts">
+  import { computed, defineComponent, ref, unref, reactive, onMounted, getCurrentInstance, onUnmounted } from 'vue';
+  import { Popover, Tabs, Badge } from 'ant-design-vue';
+  import { BellOutlined } from '@ant-design/icons-vue';
+  import { tabListData } from './data';
+  import { listCementByUser, editCementSend } from './notify.api';
+  import NoticeList from './NoticeList.vue';
+  import DetailModal from '/@/views/monitor/mynews/DetailModal.vue';
+  import DynamicNotice from '/@/views/monitor/mynews/DynamicNotice.vue';
+  import { useModal } from '/@/components/Modal';
+  import { useDesign } from '/@/hooks/web/useDesign';
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { useUserStore } from '/@/store/modules/user';
+  import { connectWebSocket, onWebSocket, result } from '/@/hooks/web/useWebSocket';
+  import { readAllMsg } from '/@/views/monitor/mynews/mynews.api';
+  import { getToken } from '/@/utils/auth';
+  export default defineComponent({
+    components: {
+      Popover,
+      BellOutlined,
+      Tabs,
+      TabPane: Tabs.TabPane,
+      Badge,
+      NoticeList,
+      DetailModal,
+      DynamicNotice,
+    },
+    setup() {
+      const { prefixCls } = useDesign('header-notify');
+      const instance: any = getCurrentInstance();
+      const userStore = useUserStore();
+      const glob = useGlobSetting();
+      const dynamicNoticeProps = reactive({ path: '', formData: {} });
+      const [registerDetail, detailModal] = useModal();
+      const popoverVisible = ref<boolean>(false);
+      const listData = ref(tabListData);
+      listData.value[0].list = [];
+      listData.value[1].list = [];
+      listData.value[0].count = 0;
+      listData.value[1].count = 0;
+
+      onMounted(() => {
+        initWebSocket();
+      });
+
+      const count = computed(() => {
+        let count = 0;
+        for (let i = 0; i < listData.value.length; i++) {
+          count += listData.value[i].count;
+        }
+        return count;
+      });
+
+      function mapAnnouncement(item) {
+        return {
+          ...item,
+          title: item.titile,
+          description: item.msgAbstract,
+          datetime: item.sendTime,
+        };
+      }
+
+      // 获取系统消息
+      async function loadData() {
+        try {
+          let { anntMsgList, sysMsgList, anntMsgTotal, sysMsgTotal } = await listCementByUser({
+            pageSize: 5,
+          });
+          listData.value[0].list = anntMsgList.map(mapAnnouncement);
+          listData.value[1].list = sysMsgList.map(mapAnnouncement);
+          listData.value[0].count = anntMsgTotal;
+          listData.value[1].count = sysMsgTotal;
+        } catch (e) {
+          console.warn('系统消息通知异常:', e);
+        }
+      }
+
+      loadData();
+
+      function onNoticeClick(record) {
+        try {
+          editCementSend(record.id);
+          loadData();
+        } catch (e) {
+          console.error(e);
+        }
+        if (record.openType === 'component') {
+          dynamicNoticeProps.path = record.openPage;
+          dynamicNoticeProps.formData = { id: record.busId };
+          instance.refs.dynamicNoticeRef?.detail(record.openPage);
+        } else {
+          detailModal.openModal(true, {
+            record,
+            isUpdate: true,
+          });
+        }
+        popoverVisible.value = false;
+      }
+
+      // 初始化 WebSocket
+      function initWebSocket() {
+        let userId = unref(userStore.getUserInfo).id;
+        let token = getToken();
+        // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
+        let url = glob.domainUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/websocket/' + userId;
+        connectWebSocket(url);
+        onWebSocket(onWebSocketMessage);
+      }
+
+      function onWebSocketMessage(data) {
+        console.log('---onWebSocketMessage---', data)
+        if (data.cmd === 'topic' || data.cmd === 'user') {
+          //update-begin-author:taoyan date:2022-7-13 for: VUEN-1674【严重bug】系统通知,为什么必须刷新右上角才提示
+          //后台保存数据太慢 前端延迟刷新消息
+          setTimeout(()=>{
+            loadData();
+          }, 1000)
+          //update-end-author:taoyan date:2022-7-13 for: VUEN-1674【严重bug】系统通知,为什么必须刷新右上角才提示
+        }
+      }
+
+      // 清空消息
+      function onEmptyNotify() {
+        popoverVisible.value = false;
+        readAllMsg({}, loadData);
+      }
+
+      return {
+        prefixCls,
+        listData,
+        count,
+        onNoticeClick,
+        onEmptyNotify,
+        numberStyle: {},
+        popoverVisible,
+        registerDetail,
+        dynamicNoticeProps,
+      };
+    },
+  });
+</script>
+<style lang="less">
+  //noinspection LessUnresolvedVariable
+  @prefix-cls: ~'@{namespace}-header-notify';
+
+  .@{prefix-cls} {
+    padding-top: 2px;
+
+    &__overlay {
+      max-width: 340px;
+
+      .ant-popover-inner-content {
+        padding: 0;
+      }
+
+      .ant-tabs-nav {
+        margin-bottom: 12px;
+      }
+
+      .ant-list-item {
+        padding: 12px 24px;
+        transition: background-color 300ms;
+
+        &:hover {
+          background-color: #e6f7ff;
+        }
+      }
+
+      .bottom-buttons {
+        text-align: center;
+        border-top: 1px solid #f0f0f0;
+        height: 42px;
+
+        .ant-btn {
+          border: 0;
+          height: 100%;
+
+          &:first-child {
+            border-right: 1px solid #f0f0f0;
+          }
+        }
+      }
+    }
+
+    .ant-tabs-content {
+      width: 300px;
+    }
+
+    .ant-badge {
+      font-size: 18px;
+
+      .ant-badge-count {
+        @badget-size: 16px;
+        width: @badget-size;
+        height: @badget-size;
+        min-width: @badget-size;
+        line-height: @badget-size;
+        padding: 0;
+
+        .ant-scroll-number-only > p.ant-scroll-number-only-unit {
+          font-size: 14px;
+          height: @badget-size;
+        }
+      }
+
+      .ant-badge-multiple-words {
+        padding: 0 0 0 2px;
+        font-size: 12px;
+      }
+
+      svg {
+        width: 0.9em;
+      }
+    }
+  }
+
+  // 兼容黑暗模式
+  [data-theme='dark'] .@{prefix-cls} {
+    &__overlay {
+      .ant-list-item {
+        &:hover {
+          background-color: #111b26;
+        }
+      }
+
+      .bottom-buttons {
+        border-top: 1px solid #303030;
+
+        .ant-btn {
+          &:first-child {
+            border-right: 1px solid #303030;
+          }
+        }
+      }
+    }
+  }
+</style>