Browse Source

添加消息通知

gaoxiong 1 năm trước cách đây
mục cha
commit
b577202b8d

+ 4 - 2
.env.development

@@ -1,4 +1,6 @@
 NODE_ENV='development'
-# must start with VUE_APP_ 
+# must start with VUE_APP_
 VUE_APP_ENV = 'development'
- 
+
+
+

+ 2 - 1
.env.production

@@ -1,4 +1,5 @@
 NODE_ENV='production'
 # must start with VUE_APP_
 VUE_APP_ENV = 'production'
- 
+VITE_APP_CLIENT_KEY = 'test'
+VITE_APP_WEBSOCKET = true

+ 3 - 1
.env.staging

@@ -1,4 +1,6 @@
 NODE_ENV='production'
 # must start with VUE_APP_
 VUE_APP_ENV = 'staging'
- 
+VITE_APP_CLIENT_KEY = 'test'
+VITE_APP_WEBSOCKET = true
+

+ 2 - 1
package.json

@@ -42,7 +42,8 @@
     "vue-esign": "^1.1.4",
     "vue-pdf": "4.2.0",
     "vue-router": "^3.6.5",
-    "vuex": "^3.6.2"
+    "vuex": "^3.6.2",
+    "ws": "^8.18.0"
   },
   "devDependencies": {
     "@babel/core": "^7.18.10",

+ 111 - 1
src/App.vue

@@ -2,15 +2,125 @@
   <div id="app">
     <watercom ref="watercom"></watercom>
     <router-view />
+    <system-notify ref="notify" :msgData="msgData" @showDailog="showDailog" />
+    <alarm-deal ref="alarmDeal"  />
   </div>
 </template>
 <script>
 // import watercom from '@/components/waterCom.vue'
+import SystemNotify from '@/components/SystemNotify/index.vue';
+import AlarmDeal from '@/components/AlarmDeal/index.vue';
+import config from "@/config";
+import {mapGetters} from "vuex";
+import {getToken} from '@/utils/auth'
 
 export default {
+  name: 'App',
+  data() {
+    return {
+      messages:[],
+      socket: null,
+      msgData:{},
+      currentIndex: 0,
+      timer:null,
+    }
+  },
+  components: {
+    SystemNotify,AlarmDeal
+  },
+  mounted() {
+    this.initWebSocket();
+  },
+  computed: {
+    ...mapGetters(['userName', 'messageShow']),
+  },
+  methods: {
+    showDailog(){
+      this.$refs.alarmDeal.show = true;
+    },
+    startLoop() {
+      this.$refs.notify.show = false;
+      this.currentItem = this.messages[this.currentIndex];
+      this.timer = setInterval(() => {
+        if(this.messageShow){
+          this.$refs.notify.show = true;
+        }
+        this.nextItem();
+      }, 10000); // 每 10 秒切换一次
+    },
+    nextItem() {
+      this.currentIndex = (this.currentIndex + 1) % this.messages.length;
+      this.msgData = this.messages[this.currentIndex];
+    },
+    getWeSocketUrl(){
+      let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
+      let url = protocol + window.location.host + config.baseApi + '/system/websocket';
+      url = url + '?Authorization=Bearer ' + getToken() + '&clientId=' + config.VITE_APP_CLIENT_KEY;
+      return url;
+    },
+    initWebSocket() {
+      if(!config.VITE_APP_WEBSOCKET){
+        return;
+      }
+
+      if(!getToken()){
+        return;
+      }
+
+      // 使用浏览器提供的 WebSocket 对象
+      this.socket = new WebSocket(this.getWeSocketUrl());
+
+      // 监听 WebSocket 连接打开事件
+      this.socket.addEventListener('open', (event) => {
+        console.log('WebSocket 连接已建立:', event);
+      });
+
+      // 接收消息
+      this.socket.addEventListener('message', (event) => {
+        console.log('收到消息:', event.data);
+        this.dealMessage(event.data);
+        if(this.messages.length > 0){
+          this.startLoop();
+        }
+      });
+
+      // 连接关闭事件
+      this.socket.addEventListener('close', (event) => {
+        console.log('WebSocket 连接已关闭:', event);
+        this.reconnect();
+      });
+
+      // 错误处理
+      this.socket.addEventListener('error', (error) => {
+        console.error('WebSocket 错误:', error);
+        this.reconnect();
+      });
+    },
+    dealMessage(msg) {
+      let alarm =  JSON.parse(msg);
+      this.messages = alarm;
+    },
+    clearMessage(){
+      //websocket 清空消息
+    },
+    reconnect(){
+      //定义重新连接的时间间隔
+      const reconnectInterval = 5;
+    /*  setTimeout(() => {
+        this.initWebSocket();
+      }, reconnectInterval * 1000);*/
+    }
+  },
+  beforeDestroy() {
+    // 页面卸载前关闭 WebSocket 连接
+    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
+      this.socket.close();
+    }
+    clearInterval(this.timer);
+  },
 
-  name: 'App'
 }
+
 </script>
 <style lang="scss">
 html,body,div,h1,h2,h3,h4,h5,p{

BIN
src/assets/sound/message.mp3


+ 62 - 0
src/components/AlarmDeal/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div>
+    <van-dialog v-model="show" title="告警处理" show-cancel-button @confirm="submit" @cancel="cancelDialog">
+      <div class="dialogContent">
+        <van-cell title="二楼温湿度传感器" value="传感器" />
+        <van-cell title="所属机构" value="XXX分理处" />
+        <van-cell title="告警类型" value="温湿度告警" />
+        <van-cell title="告警时间" value="2021-08-01 12:00:00" />
+        <van-cell title="告警内容" value="温度超过80度" />
+        <select-cell
+          style="border-right: 1px solid #f5f5f5;"
+          title="处置类型"
+          :isAll="true"
+          :border="false"
+          :data-list="getDictItem('out_in_type')"
+          />
+        <van-field
+          rows="2"
+          autosize
+          label="处理内容"
+          type="textarea"
+          maxlength="50"
+          placeholder="请输入内容"
+          show-word-limit
+        />
+      </div>
+    </van-dialog>
+  </div>
+</template>
+<script>
+import {mapGetters} from "vuex";
+
+export default {
+  data() {
+    return {
+      show: false,
+      dicts:['out_in_approve_status','out_in_type']
+    }
+  },
+  methods: {
+    submit() {
+      console.log('提交')
+      this.$store.commit('SET_MESSAGE_SHOW', true)
+    },
+    cancelDialog(){
+      this.$store.commit('SET_MESSAGE_SHOW', true)
+    }
+  },
+  computed:{
+    ...mapGetters(['orgId','id','dictionary']),
+  },
+  beforeDestroy() {
+    this.show = false;
+  }
+}
+</script>
+<style scoped>
+.dialogContent{
+  width: 100%;
+  height: 70vh;
+}
+</style>

+ 101 - 0
src/components/SystemNotify/index.vue

@@ -0,0 +1,101 @@
+<template>
+  <div>
+    <van-notify v-model="show" type="warning">
+      <div class="message">
+        <div class="content">
+          <van-icon name="bell" style="margin-right: 4px;"/>
+          <span>{{ msgData.message }}!</span>
+        </div>
+        <div class="deal_alarm">
+          <audio ref="audioElement" type="audio/mp3" v-show="false" :src="audioSrc" controls>该浏览器不支持audio属性</audio>
+          <div>告警时间:{{msgData.alarmTime}}</div>
+          <div @click="showNotify">点击处理</div>
+        </div>
+      </div>
+    </van-notify>
+    <van-dialog v-model="showAudio" title="确认" show-cancel-button @confirm="audioConfirm" @cancel="audioCancelDialog">
+      <div style="line-height: 3.5vw;height: 5vh;text-align: center;margin-top: 3vw;">
+        是否播放告警提示音频?
+      </div>
+    </van-dialog>
+  </div>
+</template>
+<script>
+import {mapGetters} from "vuex";
+
+export default {
+  name: 'systemNotify',
+  props: {
+    msgData: {
+      type: Object,
+      default: function() {
+        return {}
+      }
+    }
+  },
+  data() {
+    return {
+      show: false,
+      showAudio:false,
+      audioSrc: require('@/assets/sound/message.mp3'),
+    }
+  },
+  watch:{
+    msgData(val, oldVal) {
+     this.initAudio();
+    }
+  },
+  computed:{
+    ...mapGetters(['playAudio','messageShow']),
+  },
+  methods: {
+    audioCancelDialog(){
+      this.$store.commit('SET_PLAY_AUDIO', 2)
+    },
+    audioConfirm(){
+      this.$refs.audioElement.volume = 0;
+      this.$refs.audioElement.play();
+      this.$store.commit('SET_PLAY_AUDIO', 1)
+      this.showAudio = false;
+    },
+    initAudio(){
+      if(!this.playAudio){
+        this.showAudio = true;
+      }
+      if(this.playAudio == 1){
+        this.$refs.audioElement.volume = 1;
+        if(!this.messageShow){
+          this.$refs.audioElement.volume = 0;
+        }
+        this.$refs.audioElement.play();
+      }
+    },
+    showNotify() {
+      this.show = false;
+      this.$emit('showDailog', this.msgData);
+      this.$store.commit('SET_MESSAGE_SHOW', false)
+    }
+  }
+}
+</script>
+<style scoped>
+  .deal_alarm{
+    display: flex;
+    flex-direction: row;
+    justify-content: space-evenly;
+    align-items: center;
+    width: 100vw;
+  }
+  .content{
+    min-height: 2vh;
+    max-height: 8vh;
+    width: 100vw;
+  }
+  .message{
+    height: 10vh;
+    border-radius: 3vw;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-around;
+  }
+</style>

+ 2 - 0
src/config/env.development.js

@@ -5,6 +5,8 @@ module.exports = {
   baseApi: '/jwx', // 本地api请求地址,注意:如果你使用了代理,请设置成'/'
   APPID: 'xxx',
   APPSECRET: 'xxx',
+  VITE_APP_CLIENT_KEY:'test',
+  VITE_APP_WEBSOCKET:true,
   $cdn: 'https://www.sunniejs.cn/static'  //静态资源
 }
 

+ 5 - 0
src/main.js

@@ -40,6 +40,11 @@ import './components/index.js'
 import watercom from '@/components/waterCom.vue'
 Vue.component('watercom', watercom)
 
+import { Notify } from 'vant';
+
+// 全局注册
+Vue.use(Notify);
+
 Vue.use(Vant)
 // Vue.use(watercom);
 Vue.use(VueEasytable)

+ 3 - 1
src/store/getters.js

@@ -18,7 +18,9 @@ const getters = {
   dotNum: state => state.app.dotNum,
   businessUnReadCount: state => state.app.businessUnReadCount,
   messageUnReadCount: state => state.app.messageUnReadCount,
-  noticeUnReadCount: state => state.app.noticeUnReadCount,  
+  noticeUnReadCount: state => state.app.noticeUnReadCount,
+  messageShow: state => state.msg.messageShow,
+  playAudio: state => state.msg.playAudio,
 }
 
 export default getters

+ 3 - 1
src/store/index.js

@@ -3,6 +3,7 @@ import Vuex from 'vuex'
 import getters from './getters'
 import user from './modules/user'
 import app from './modules/app'
+import msg from './modules/message';
 
 
 Vue.use(Vuex)
@@ -10,7 +11,8 @@ Vue.use(Vuex)
 const store = new Vuex.Store({
     modules: {
       user,
-      app
+      app,
+      msg
     },
     getters,
   }

+ 14 - 0
src/store/modules/message.js

@@ -0,0 +1,14 @@
+export default {
+  state: {
+    messageShow: true,
+    playAudio: 0,
+  },
+  mutations: {
+    SET_MESSAGE_SHOW(state,data){
+      state.messageShow = data;
+    },
+    SET_PLAY_AUDIO(state,data){
+      state.playAudio = data;
+    }
+  }
+}

+ 2 - 0
src/utils/auth.js

@@ -0,0 +1,2 @@
+const TokenKey = 'access_token';
+export const getToken = () => sessionStorage.getItem(TokenKey);

+ 3 - 0
src/views/login.vue

@@ -61,7 +61,9 @@ export default {
       checked: false
     }
   },
+
   mounted() {
+    this.$store.commit('SET_MESSAGE_SHOW', false)
     let checked = localStorage.getItem('checked')
     if (checked) {
       this.checked = checked
@@ -83,6 +85,7 @@ export default {
         }
         console.log(res, 'res')
         sessionStorage.setItem('access_token', res.data.access_token)
+        this.$store.commit('SET_MESSAGE_SHOW', true)
         this.$router.replace('/home')
       })
     }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 303 - 303
yarn.lock


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác