ruoyi-nbcio-plus基于vue3的flowable的websocket消息组件的升级修改(一)

更多ruoyi-nbcio功能请看演示系统

gitee源代码地址

前后端代码: https://gitee.com/nbacheng/ruoyi-nbcio

演示地址:RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/

更多nbcio-boot功能请看演示系统

gitee源代码地址

后端代码: https://gitee.com/nbacheng/nbcio-boot

前端代码:https://gitee.com/nbacheng/nbcio-vue.git

在线演示(包括H5) : http://122.227.135.243:9888

1、在navbar.vue里增加消息组件

javascript 复制代码
<!-- 消息 -->
        <el-tooltip :content="$t('navbar.message')" effect="dark" placement="bottom">
           <header-notice id="message" class="right-menu-item hover-effect" />
        </el-tooltip>

就是在右上角显示消息图标bell

2、HeadNotice.vue文件vue3版本修改如下:

javascript 复制代码
<template>
  <div>
    <a-popover trigger="click" placement="bottomRight" :autoAdjustOverflow="true" :arrowPointAtCenter="true"
      overlayClassName="header-notice-wrapper" @visibleChange="handleHoverChange"
      :overlayStyle="{ width: '400px', top: '50px' }">
      <template #content>
        <a-spin :spinning="loadding">
          <a-tabs>
            <a-tab-pane :tab="msg1Title" key="1">
              <a-list>
                <a-list-item :key="index" v-for="(record, index) in notice1">
                  <div style="margin-left: 5%;width: 50%">
                    <p><a @click="showNoticeList(record)">{{ record.titile }}</a></p>
                    <p style="color: rgba(0,0,0,.45);margin-bottom: 0px">{{ record.createTime }} 发布</p>
                  </div>
                  <div style="text-align: right">
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'L'" color="blue">一般消息</a-tag>
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'M'" color="orange">重要消息</a-tag>
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'H'" color="red">紧急消息</a-tag>
                  </div>
                </a-list-item>
                <div style="margin-top: 5px;text-align: center">
                  <a-button @click="toMyNotice()" type="dashed" block>查看更多</a-button>
                </div>
              </a-list>
            </a-tab-pane>
            <a-tab-pane :tab="msg2Title" key="2">
              <a-list>
                <a-list-item :key="index" v-for="(record, index) in notice2">
                  <div style="margin-left: 5%;width: 50%">
                    <p><a @click="showNoticeList(record)">{{ record.titile }}</a></p>
                    <p style="color: rgba(0,0,0,.45);margin-bottom: 0px">{{ record.createTime }} 发布</p>
                  </div>
                  <div style="text-align: right">
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'L'" color="blue">一般消息</a-tag>
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'M'" color="orange">重要消息</a-tag>
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'H'" color="red">紧急消息</a-tag>
                  </div>
                </a-list-item>
                <div style="margin-top: 5px;text-align: center">
                  <a-button @click="toMyNotice()" type="dashed" block>查看更多</a-button>
                </div>
              </a-list>
            </a-tab-pane>
            <a-tab-pane :tab="msg3Title" key="3">
              <a-list>
                <a-list-item :key="index" v-for="(record, index) in notice3">
                  <div style="margin-left: 5%;width: 50%">
                    <p><a @click="showNoticeList(record)">{{ record.titile }}</a></p>
                    <p style="color: rgba(0,0,0,.45);margin-bottom: 0px">{{ record.createTime }} 发布</p>
                  </div>
                  <div style="text-align: right">
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'L'" color="blue">一般消息</a-tag>
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'M'" color="orange">重要消息</a-tag>
                    <a-tag @click="showNoticeList(record)" v-if="record.priority === 'H'" color="red">紧急消息</a-tag>
                  </div>
                </a-list-item>
                <div style="margin-top: 5px;text-align: center">
                  <a-button @click="toMyNotice()" type="dashed" block>查看更多</a-button>
                </div>
              </a-list>
            </a-tab-pane>
          </a-tabs>
        </a-spin>
      </template>
      <span @click="fetchNotice" class="header-notice">
        <a-badge :count="msgTotal">
          <svg-icon icon-class="bell" style="width: 18px;height:18px;" />
        </a-badge>
      </span>
      <show-notice ref="showNoticeRef" @ok="modalFormOk"></show-notice>
      <dynamic-notice ref="showDynamNoticeRef" :path="openPath" :formData="formData" />
    </a-popover>
  </div>
</template>

<script setup lang="ts">
  import ShowNotice from './ShowNotice.vue'
  import useUserStore from '@/store/modules/user';
  import DynamicNotice from './DynamicNotice'
  import { ElNotification } from "element-plus";
  import { listByUser, updateUserIdAndNotice } from "@/api/system/notice";

  const router = useRouter();
  const userStore = useUserStore();

  const loadding = ref(false)
  const hovered = ref(false)
  const notice1 = ref<any>([])
  const notice2 = ref<any>([])
  const notice3 = ref<any>([])
  const msg1Count = ref("0")
  const msg2Count = ref("0")
  const msg3Count = ref("0")
  const msg1Title = ref("通知(0)")
  const msg2Title = ref("")
  const msg3Title = ref("")
  const stopTimer = ref(false)
  let   websock : any = null; // websocket 实例
  const lockReconnect = ref(false)
  const formData = ref<any>({})
  const openPath = ref('')
  const showDynamNoticeRef = ref(DynamicNotice)
  const showNoticeRef = ref(ShowNotice)

  const loadData = () => {
    try {
      // 获取系统消息
      listByUser().then((res) => {
        console.log("listByUser res",res);
        if (res.code == 200) {
          notice1.value = res.data.anntMsgList;
          msg1Count.value = res.data.anntMsgTotal;
          msg1Title.value = "通知(" + res.data.anntMsgTotal + ")";
          notice2.value = res.data.sysMsgList;
          msg2Count.value = res.data.sysMsgTotal;
          msg2Title.value = "系统消息(" + res.data.sysMsgTotal + ")";
          notice3.value = res.data.todealMsgList;
          msg3Count.value = res.data.todealMsgTotal;
          msg3Title.value = "待办消息(" + res.data.todealMsgTotal + ")";
        }
      }).catch(error => {
        console.log("系统消息通知异常", error); //这行打印permissionName is undefined
        stopTimer.value = true;
        console.log("清理timer");
      });
    } catch (err) {
      stopTimer.value = true;
      console.log("通知异常", err);
    }
  }
  const fetchNotice = () => {
    if (loadding.value) {
      loadding.value = false
      return
    }
    loadding.value = true
    setTimeout(() => {
      loadding.value = false
    }, 200)
  }
  const showNoticeList = (record: any) => {
    updateUserIdAndNotice({
      noticeId: record.noticeId
    }).then((res) => {
      if (res.code == 200) {
        loadData();
      }
    });
    hovered.value = false;
    if (record.openType === 'component') {
      openPath.value = record.openPage;
      formData.value = {
        id: record.busId
      };
      showDynamNoticeRef.value.detail(record.openPage);
    } else {
      console.log("showNoticeList showNoticeRef",showNoticeRef.value)
      showNoticeRef.value.detail(record);
    }
  }
  const toMyNotice = () => {
    router.push({
      path: '/personal/mynotice'
    });
  }
  const modalFormOk = () =>{

  }
  const handleHoverChange = (visible: any) => {
    hovered.value = visible;
  }

  const initWebSocket = () => {
    // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
    var userName = userStore.name;
    //var url = import.meta.env.VITE_APP_WS_API + "/websocket/" + userName  + "?Authorization=Bearer " + getToken() + "&clientid=" + import.meta.env.VITE_APP_CLIENT_ID;
    var url = import.meta.env.VITE_APP_WS_API + "/websocket/" + userName;
    console.log("initWebSocket url=",url);
    websock = new WebSocket(url);
    websock.onopen = websocketOnopen;
    websock.onerror = websocketOnerror;
    websock.onmessage = websocketOnmessage;
    websock.onclose = websocketOnclose;
  }
  const websocketOnopen = () => {
    console.log("WebSocket连接成功");
  }
  const websocketOnerror = (e) => {
    console.log("WebSocket连接发生错误");
    reconnect();
  }
  const websocketOnmessage = (e) => {
    console.log("-----接收消息-------", e);
    console.log("-----接收消息-------", e.data);
    var data = eval("(" + e.data + ")"); //解析对象
    if (data.cmd == "topic") {
      //系统通知
      loadData();
      ElNotification({ //websocket消息通知弹出
        title: 'websocket消息通知',
        message: data.msgTxt,
        type: 'success',
        duration: 1000
      })
    } else if (data.cmd == "user") {
      //用户消息
      loadData();
      ElNotification({
        title: 'websocket消息通知',
        message: data.msgTxt,
        type: 'success',
        duration: 1000
      })
    }
  }
  const websocketOnclose = (e) => {
    console.log("connection closed (" + e + ")");
    if (e) {
      console.log("connection closed (" + e.code + ")");
    }
    reconnect();
  }
  const websocketSend = (text) => { // 数据发送
    try {
      websock.send(text);
    } catch (err) {
      console.log("send failed (" + err.code + ")");
    }
  }

  const openNotification = (data) =>{
    var text = data.msgTxt;
    const key = `open${Date.now()}`;
    ElNotification({
      title: '消息提醒',
      message: text,
      type: 'success',
      duration: 1000
    })
  }

  const reconnect = () => {
    if (lockReconnect.value) return;
    lockReconnect.value = true;
    //没连接上会一直重连,设置延迟避免请求过多
    setTimeout(function() {
      console.info("尝试重连...");
      initWebSocket();
      lockReconnect.value = false;
    }, 5000);
  }

  const showDetail = (key, data) => {
    ElNotification[key].close();
    var id = data.msgId;
    getAction(url.queryById, {
      id: id
    }).then((res) => {
      if (res.success) {
        var record = res.result;
        showNotice(record);
      }
    })

  }
  const msgTotal = computed(() => {
    return parseInt(msg1Count.value) + parseInt(msg2Count.value) + parseInt(msg3Count.value);
  })

  onMounted(() => {
    loadData();
    initWebSocket();
  })

  onUnmounted(() => {
    websocketOnclose('');
  })
</script>

<style lang="css">
  .header-notice-wrapper {
    top: 50px !important;
  }
</style>
<style lang="less" scoped>
  .header-notice {
    display: inline-block;
    transition: all 0.3s;

    span {
      vertical-align: initial;
    }
  }
</style>

3、ShowNotice.vue文件vue3版本修改如下:

javascript 复制代码
<template>
  <n-modal
    :title="title"
    :width="modelStyle.width"
    :visible="visible"
    :bodyStyle ="bodyStyle"
    @cancel="handleCancel"
   >
    <template #footer>
      <a-button key="back" @click="handleCancel">关闭</a-button>
      <a-button v-if="recordDetail.openType==='url'" type="primary" @click="toHandle">去处理</a-button>
    </template>
    <a-card class="daily-article" :loading="loading">
      <a-card-meta
        :title="recordDetail.noticeTitle"
        :description="'发布人:'+recordDetail.sender + ' 发布时间: ' + recordDetail.sendTime">
      </a-card-meta>
      <a-divider />
      <span v-html="recordDetail.noticeContent" class="article-content"></span>
    </a-card>
  </n-modal>
</template>

<script setup lang="ts" name="ShowNotice">

  import NModal from './NModal'

  const router = useRouter();
  const title = ref("通知消息")
  const recordDetail = ref<any>({})

  const visible = ref(false)
  const loading = ref(false)
  const bodyStyle = ref({
    padding: "0",
    height:(window.innerHeight*0.8)+"px",
    "overflow-y":"auto",
  })
  const modelStyle = ref({
    width: '60%',
    style: { top: '20px' },
    fullScreen: false
  })

  const detail = (record: any) => {
    visible.value = true;
    recordDetail.value = toRaw(record);
  }

  const handleCancel = () => {
    visible.value = false;
  }

  const toHandle = () => {
    if(recordDetail.value.openType==='url'){
      visible.value = false;
      //链接跳转
      router.push({path: recordDetail.value.openPage})
    }
  }

  //暴露detail方法
  defineExpose({
    detail
  });

  onMounted(() => {
    console.log("ShowNotice visible",visible)
  })

</script>

<style lang="less" scoped>
  .announcementCustomModal{
    .ant-modal-header {
      border: none;
      display: inline-block;
      position: absolute;
      z-index: 1;
      right: 56px;
      padding: 0;
      .ant-modal-title{
        .custom-btn{
          width: 56px;
          height: 56px;
          border: none;
          box-shadow: none;
        }
      }
    }
    .daily-article{
      border-bottom: 0;
    }
  }
  .daily-article {
    .article-button {
      font-size: 1.2rem !important;
    }
    .ant-card-body {
      padding: 18px !important;
    }
    .ant-card-head {
      padding: 0 1rem;
    }
    .ant-card-meta {
      margin-bottom: 1rem;
    }
    .article-content {
      p {
        word-wrap: break-word;
        word-break: break-all;
        text-overflow: initial;
        white-space: normal;
        font-size: .9rem !important;
        margin-bottom: .8rem;
      }
      :deep(a) {
        color: #1890ff;
      }
    }
  }
</style>
相关推荐
m0_7482356110 分钟前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-7 小时前
验证码机制
前端·后端
燃先生._.8 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖9 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235249 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_7482402510 小时前
前端如何检测用户登录状态是否过期
前端