shikeying
2023-04-19 529f48641122af7c0aec185e4283d02e97aa0f89
增加 websocket
6个文件已添加
7个文件已修改
482 ■■■■■ 已修改文件
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/tcp.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Navbar.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/index.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/app.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/websoket.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/Speech.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/StringUtils.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/web-connection.js 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/webconnection.js 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json
@@ -53,6 +53,7 @@
    "quill": "1.3.7",
    "screenfull": "5.0.2",
    "sortablejs": "1.10.2",
    "speak-tts": "^2.0.8",
    "stylus": "^0.59.0",
    "stylus-loader": "^7.1.0",
    "viewerjs": "^1.11.3",
src/api/tcp.js
New file
@@ -0,0 +1,16 @@
import request from '@/utils/request';
/**
 * 获取websocket连接地址。
 * @param query
 * @returns {*}
 * @author 时克英
 * @date 2023-04-17
 */
export function getWebsocketUri() {
  return request({
    url: '/websocket/uri',
    method: 'get',
    params: null
  })
}
src/layout/components/Navbar.vue
@@ -85,9 +85,17 @@
      showBreadCrumb:false // 是否显示顶部面包屑
    }
  },
  created() {
    this.initTts();
  },
  methods: {
    initTts(){
      this.$store.dispatch('app/setSpeechTts');
    },
    toggleSideBar() {
      this.$store.dispatch('app/toggleSideBar')
      this.$store.dispatch('app/toggleSideBar');
    },
    async logout() {
      this.$confirm('确定注销并退出系统吗?', '提示', {
src/layout/index.vue
@@ -52,8 +52,37 @@
    },
    variables() {
      return variables;
    },
    // 2023-04-17,定义接收websocket消息
    getWsMsg (){
      // return this.$store.websocket.state.webSocketMsg;
      return this.$store.state.user.webSocketMsg;
    }
  },
  // 2023-04-17
  watch: {
    getWsMsg:{
      handler: function(newVal) {
        // console.log(newVal)
        // alert('接收到webSocket推送:'+ newVal);
        this.$notify({
          title: '新消息提醒:',  //标题
          // message: '这是一条不会自动关闭的消息',  //内容
          duration: 0,  //设置弹框消失事件
          position:'bottom-right',  // 设置弹框在屏幕的哪个角弹出(只能设置4个角)
          type: 'info',  //给标题前加一个小图标
          offset: 100,  //偏移量:距离四个角的偏移程度(默认偏移了16px)
          dangerouslyUseHTMLString: true,  //是否支持弹出框内传入 HTML 片段
          message: '<strong>这是 <i>HTML</i> 片段</strong>' + newVal,   //开启后,这里可以写html
          showClose: true,  //隐藏关闭按钮(右上角的x  默认为true,显示)
          onClose() {console.log('关闭啦', event)},  // 手动关闭时的回调函数
        });
        this.$store.state.app.tts.speak("有新消息提醒,请点击查看");
      }
    }
  },
  methods: {
    handleClickOutside() {
      this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
src/main.js
@@ -38,6 +38,11 @@
// 字典数据组件
import DictData from '@/components/RuoYi/DictData'
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
//~ 平台提供的组件,时克英,2023-04-17
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
// import * as WebConnection from "@/utils/webconnection";
// 全局方法挂载
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
@@ -49,6 +54,9 @@
Vue.prototype.download = download
Vue.prototype.handleTree = handleTree
// 时克英,2023-04-17
// Vue.prototype.webConnection = WebConnection
// 全局组件挂载
Vue.component('DictTag', DictTag)
Vue.component('Pagination', Pagination)
src/store/index.js
@@ -8,6 +8,9 @@
import settings from './modules/settings'
import getters from './getters'
// 时克英,2023-04-17
// import websocket from './modules/websoket'
Vue.use(Vuex)
const store = new Vuex.Store({
@@ -18,6 +21,7 @@
    tagsView,
    permission,
    settings
    // websocket
  },
  getters
})
src/store/modules/app.js
@@ -1,4 +1,5 @@
import Cookies from 'js-cookie'
import SpeechObject from '@/utils/Speech'
const state = {
  sidebar: {
@@ -7,7 +8,10 @@
    hide: false
  },
  device: 'desktop',
  size: Cookies.get('size') || 'medium'
  size: Cookies.get('size') || 'medium',
  // 2023-04-18,语音播报对象
  tts: null
}
const mutations = {
@@ -37,6 +41,9 @@
  },
  SET_SIDEBAR_HIDE: (state, status) => {
    state.sidebar.hide = status
  },
  SET_SPEECH_TTS: (state) => {
    state.tts = new SpeechObject();
  }
}
@@ -55,6 +62,9 @@
  },
  toggleSideBarHide({ commit }, status) {
    commit('SET_SIDEBAR_HIDE', status)
  },
  setSpeechTts({commit}){
    commit('SET_SPEECH_TTS')
  }
}
src/store/modules/user.js
@@ -1,6 +1,10 @@
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import {encrypt} from '@/utils/jsencrypt'
// import Vue from 'vue'
import {Message} from "element-ui";
// import WebConnection from "@/utils/webconnection";
import WebConnection from "@/utils/web-connection";
const user = {
  state: {
@@ -8,7 +12,12 @@
    name: '',
    avatar: '',
    roles: [],
    permissions: []
    permissions: [],
    // 2023-04-17 添加websocket
    webConnection: null, // 单例对象
    webSocketMsg: null,  // 接收到的消息
    uri: null,  // 连接websocket地址
    uid: null   // 连接用户标识
  },
  mutations: {
@@ -26,6 +35,29 @@
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions
    },
    // 2023-04-17
    SET_CONNECTION: (state, data) => {
      // var webConnection = new WebConnection();
      // if(state.webConnection == null){
        state.webConnection = new WebConnection(data.uri, data.uid);
        state.webConnection.startConnect();
      // }
    },
    SET_CONNECTION_CLEAR: (state, data) => {
      if(state.webConnection != null){
        state.webConnection.shutdown();
      }
      state.webConnection = null;
    },
    // 2023-04-17,监控消息变化
    SET_WS_MSG: (state, data)=>{
      state.webSocketMsg = data;
    },
    SET_WS_INFO: (state, data)=>{
      state.uri = data.uri;
      state.uid = data.uid;
    }
  },
@@ -68,7 +100,23 @@
          }
          commit('SET_NAME', user.user_name)
          commit('SET_AVATAR', avatar)
          // 2023-04-17,获取用户同时,启动:websocket
          // this.webConnection = new WebConnection();
          // this.webConnection.uri = res.data.uri;
          // this.webConnection.uid = res.data.uid;
          // vue.webConnection.setInfo(res.data.uri, res.data.uid);
          // this.webConnection.timedCheckConnection();
          // this.$store.dispatch('CreateWebConnection', res.data).then(()=>{
          // }).catch(err => {
          //   Message.error(err);
          // });
          commit('SET_WS_INFO', res.data);
          commit('SET_CONNECTION', res.data);
          console.log(".............CreateWebConnection()");
          resolve(res)
        }).catch(error => {
          reject(error)
        })
@@ -82,6 +130,7 @@
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_PERMISSIONS', [])
          commit('SET_CONNECTION_CLEAR', null)
          removeToken()
          // console.log("删除浏览器token...");
          resolve()
src/store/modules/websoket.js
New file
@@ -0,0 +1,31 @@
import WebConnection from "@/utils/webconnection";
/**
 * 该对象废弃,放到:user组件中了
 * @type {{mutations: {SET_CONNECTION: websocket.mutations.SET_CONNECTION, SET_WS_MSG: websocket.mutations.SET_WS_MSG}, state: {webConnection: null, webSocketMsg: null}, actions: {CreateWebConnection({commit: *}, *): void}}}
 */
const websocket = {
  state: {
    webConnection: null,
    webSocketMsg: null
  },
  mutations: {
    SET_CONNECTION: (state, data) => {
      // var webConnection = new WebConnection();
      WebConnection.setInfo(data.uri, data.uid);
      state.webConnection = WebConnection;
    },
    SET_WS_MSG: (state, data)=>{
      state.webSocketMsg = data;
    }
  },
  actions: {
    CreateWebConnection({ commit }, data){
      commit('SET_CONNECTION', data);
    }
  }
}
export default websocket
src/utils/Speech.js
New file
@@ -0,0 +1,18 @@
import Speech from 'speak-tts'
class SpeechObject {
  constructor() {
    this.speech = new Speech();
    this.speech.setLanguage("zh-CN");
    this.speech.init().then(() => {console.log("tts......")});
  }
  speak(message){
    this.speech.speak({ text: message }).then(() => {
      console.log("tts成功");
    });
  }
}
export default SpeechObject;
src/utils/StringUtils.js
New file
@@ -0,0 +1,14 @@
/**
 * 判断给定字符串是否为空
 * @param val
 * @returns {boolean}
 * @author 时克英
 * @date 2023-04-18
 */
export function isEmptyValue(val){
  if(val == null || val == undefined || val == '' || val == 'null' || val == 'undefined'){
    return true;
  }
  return false;
}
src/utils/web-connection.js
New file
@@ -0,0 +1,142 @@
import {isEmptyValue} from '@/utils/StringUtils'
import store from "@/store";
/**
 * 定义'Websocket'连接对象。
 * @author 时克英
 * @date 2023-04-17
 */
class WebConnection {
  // static uid;
  // static uri;
  constructor(_uri, _uid) {
    if(isEmptyValue(_uri)){
      throw 'uri参数为空';
    }
    if(isEmptyValue(_uid)){
      throw 'uid参数为空';
    }
    this.uri = _uri;
    this.uid = _uid;
    this.socket = null;
    this._ws_timer = setInterval(this.timedCheckConnection, 120000);  // 定时任务
  }
  shutdown(){
    if(this.socket != null && this.socket != undefined){
      this.socket.close();
    }
  }
  startConnect(){
    if('WebSocket' in window){
      this.socket = new WebSocket(this.uri);
      this.socket.onopen = this.wsOpen;
      this.socket.onmessage = this.wsMessage;
      this.socket.onerror = this.wsError;
      this.socket.onclose = this.wsClose;
    } else {
      this.socket = null;
      console.log('创建ws错误,可能当前浏览器不支持webSocket')
    }
  }
  timedCheckConnection(){
    let uri = store.state.user.uri;
    let uid = store.state.user.uid;
    if(!isEmptyValue(uri) && !isEmptyValue(uid)){
      // 2023-04-18,这里也无法直接使用:this.socket,因为此时为空(undefined)
      // console.log("是否连接 = " + this.socket);
      // if(this.socket == null || this.socket == undefined || !this.connected()){
      if(store.state.user.webConnection == null || !store.state.user.webConnection.connected()){
        this.uri = uri;
        this.uid = uid;
        console.log("重新连接:" + this.uri);
        store.state.user.webConnection.startConnect();
      }
    } else{
      console.error("uri或uid为空," + this.uid + ", " + this.uri);
    }
  }
  wsOpen(){
    // 2023-04-18,注意:这里回调方法中无法直接获取:this.uid,猜测是因为上下文环境已经变化(类似于匿名类换成独立类的情况)
    // 因此,经过长时间研究,决定需要放在全局store中。
    let uid = store.state.user.uid;
    console.log("--------> uid = " + uid);
    this.send(JSON.stringify({"protocol":"login", "uid":uid}));
    // console.log(this);
  }
  wsClose(){
    this.uid = store.state.uid;
    console.log("浏览器断开连接:" + this.uid);
  }
  wsError(err){
    console.log('== websocket error ==', err);
  }
  wsMessage(event){
    // console.log(event);
    if(event.data == null || event.data == "" || event.data == "null"){
      console.log("接收到服务端空数据\n");
      return;
    }
    store.state.user.webConnection.processServerRequest(eval("("+ event.data +")"));
  }
  processServerRequest(data){
    // console.log(data);
    if(data == null){
      return;
    }
    if(data.protocol == "heartbeat"){
      return;
    }
    if(data.protocol == "login"){
      if(data.status == 0){
        // uid = data.uid;
        //$("#showText").append("服务已连接,开始提问:" + data.uid + "\n");
        console.log("浏览器连接成功:" + data.uid);
      } else {
        console.log("登录认证失败:" + data.status + "\n");
      }
      return;
    }
    if(data.protocol == "data" || data.protocol == "broadcast"){
      console.log("接收到web推送:");
      console.log(data.data + "\n");
      store.commit('SET_WS_MSG', data.data);
      // if(data.data.touch == 1){
      //   touchEvent(data.data);
      // } else {
      //   voiceEvent(data.data);
      // }
    }
  }
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  //~ 内部私有方法
  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  send(message){
    if(this.socket == null){
      return;
    }
    if (this.socket.readyState == WebSocket.OPEN) {
      this.socket.send(message);
    } else {
      console.log("connection is not start.");
    }
  }
  connected(){
    if(this.socket == null || this.socket == undefined){
      return false;
    }
    if(this.socket.readyState == 1){
      return true;
    }
    return false;
  }
}
export default WebConnection;
src/utils/webconnection.js
New file
@@ -0,0 +1,146 @@
/**
 * 定义'Websocket'连接对象。
 * @author 时克英
 * @date 2023-04-17
 * @date 2023-04-18 对象废弃,参考:web-connection.js
 */
const WebConnection = {
  uri: null,  // 连接地址
  _uid: null,  // 用户身份
  $webSocket: null,
  _ws_timer: null,  // 定时任务
  // /**
  //  * 默认构造函数
  //  * @constructor
  //  */
  // WebConnection: function (){
  //   console.log("..........定时任务");
  //   this._ws_timer = setInterval(this.timedCheckConnection, 60000);
  // },
  setInfo: function (uri, uid){
    this.uri = uri;
    this._uid = uid + '';
    console.log("..........定时任务: uid=" + this._uid);
    this._ws_timer = setInterval(this.timedCheckConnection, 60000);
  },
  startConnect: function (){
    if(this.uri != null && this.uri == '-1'){
      console.log("未启用websocket");
      return;
    }
    // if(this.uri == null){
    //   getWebsocketUri().then(response => {
    //     this.uri = response.data.uri;
    //     this.uid = response.data.uid;
    //   }).catch(()=>{
    //     this.uri = null;
    //     this.uid = null;
    //     console.log("异常");
    //   });
    // }
    if('WebSocket' in window){
      this.$webSocket = new WebSocket(this.uri);
      this.$webSocket.onopen = this.wsOpen;
      this.$webSocket.onmessage = this.wsMessage;
      this.$webSocket.onerror = this.wsError;
      this.$webSocket.onclose = this.wsClose;
    } else {
      this.$webSocket = null;
      console.log('创建ws错误,可能当前浏览器不支持webSocket')
    }
  },
  send: function (message){
    if(this.$webSocket == null){
      return;
    }
    if (this.$webSocket.readyState == WebSocket.OPEN) {
      this.$webSocket.send(message);
    } else {
      console.log("connection is not start.");
    }
  },
  connected: function (){
    if(this.$webSocket == null || this.$webSocket == undefined){
      return false;
    }
    if(this.$webSocket.readyState == 1){
      // connected
      return true;
    }
    return false;
  },
  wsOpen: function() {
    // this.send(JSON.stringify({"protocol":"login", "uid":this.uid}));
    let loginInfo = "{\"protocol\":\"login\", \"uid\":\"" + this._uid + "\"}";
    this.send(loginInfo);
    console.log('== websocket open ==' + this._uid);
  },
  wsClose: function (){
    this._uid = null;
    console.log("浏览器断开连接:" + this._uid);
  },
  wsError: function(err){
    console.log('== websocket error ==', err)
  },
  wsMessage:function(event) {
    if(event.data == null || event.data == "" || event.data == "null"){
//                $("#showText").append("接收到服务端空数据\n");
      console.log("接收到服务端空数据\n");
      return;
    }
    this.processServerRequest(eval("("+ event.data +")"));
  },
  processServerRequest: function (data){
    if(data == null){
      return;
    }
    if(data.protocol == "heartbeat"){
      return;
    }
    if(data.protocol == "login"){
      if(data.status == 0){
        // uid = data.uid;
        //$("#showText").append("服务已连接,开始提问:" + data.uid + "\n");
        console.log("浏览器连接成功:" + data.uid);
      } else {
        console.log("登录认证失败:" + data.status + "\n");
      }
      return;
    }
    if(data.protocol == "data"){
      console.log("接收到web推送:");
      console.log(data.data + "\n");
      store.commit('SET_WS_MSG', data.data);
      // if(data.data.touch == 1){
      //   touchEvent(data.data);
      // } else {
      //   voiceEvent(data.data);
      // }
    }
  },
  timedCheckConnection: function (){
    if(this.uri != null && this.uri != "" && this.uri != undefined
          && this._uid != null && this._uid != "" && this._uid != undefined){
      if(this.$webSocket == null || this.$webSocket == undefined || !this.connected()){
        console.log("重新连接:" + this.uri);
        // webConnection = new WebConnection(_wurl, _uid, null);
        this.startConnect();
      }
    } else{
      console.error("uri或uid为空," + this._uid + ", " + this.uri);
    }
  }
}
export default WebConnection;