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>
相关推荐
程序员爱钓鱼6 小时前
使用 Node.js 批量导入多语言标签到 Strapi
前端·node.js·trae
鱼樱前端6 小时前
uni-app开发app之前提须知(IOS/安卓)
前端·uni-app
V***u4536 小时前
【学术会议论文投稿】Spring Boot实战:零基础打造你的Web应用新纪元
前端·spring boot·后端
i听风逝夜6 小时前
Web 3D地球实时统计访问来源
前端·后端
iMonster7 小时前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢7 小时前
antd渐变色边框按钮
前端
元直数字电路验证7 小时前
Jakarta EE Web 聊天室技术梳理
前端
wadesir7 小时前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛7 小时前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端
灵犀坠7 小时前
前端面试八股复习心得
开发语言·前端·javascript