实现效果:
在线测试的系统(用户名:liu,密码:12345)
###默认显示:
我们可以看到数据8弹窗的数值为8,现在我们在数据管理里将是这个值改为88,
过几秒(现在时间间隔为5秒),并没有刷新页面,发现显示的值已经变为88了。
这一过程中,我们用到了用来发送数据的websocket(由客户端建立连接,服务器可以主动向客户端推送数据,只需要一次http请求),vuex和vue的watch监听属性,以及改变显示的DOM操作。
准备工作:
1,使用websocketd客户端来进行websocket连接。
(如果要使用我的在线后台api,请跳过此步,因为已经连好了)
官网下载地址
百度云盘地址 提取码:eptj
作用:运行这个客户端,使得每隔一段时间这个客户端从数据库获取数据,并发送到指定端口上。
怎么用:
以百度云盘上的文件为例(就是我用的)
cmd到当前目录下,运行命令websocketd --port=8088 python cout.py
这句命令的作用:执行pthon文件cout.py(我是用python写的获取数据代码,当然用其它的语言也可以,如果你要试用我的这个示例,那么需要安装python和pip,并通过pip安装pymysql包引入到python中),并发送到8088端口地址上(其实就是http://127.0.0.1:8088)。
下面打开cout.py文件看一下里面执行了什么:
import random
from sys import stdout
from time import sleep
from itertools import count
import pymysql
db=pymysql.connect(host='localhost',user='xxx',passwd='xxx',db='xxx',port=3306,charset='utf8')
for i in count():
# 使用cursor()方法创建cursor游标对象
cursor=db.cursor()
sql="""select Id,Num from data
"""
data={}
try:
# 执行SQL语句
cursor.execute(sql)
# 提交到数据库执行
db.commit()
# 获取符合条件的所有数据
results = cursor.fetchall()
for item in results:
data[item[0]]=str(item[1]) #random.randint(0,9))
# 将数据类型从字典转为bytes类型的字典(不这样websocket会报“Could not decode a text frame as UTF-8”的错误)
print(bytes('{}'.format(data),'utf-8'))
stdout.flush()
except Exception as e:
# 发生错误时回滚
db.rollback()
print(e)
sleep(5)
代码很简单,就是从数据库获取数据,每隔5秒运行一次。(如果要用这个,用到的python库要安好)
如果运行成功会这样:
每隔一秒,执行一次代码,从数据库获取数据。
2,显示默认的弹窗:
数据我们通过vuex(关于vuex前面有说明)来保存传递。
在store.js中,state里添加iotData: {},
mutataion里添加:
//iotData
setIOTData (state, data) {
state.iotData = data
},
updateIOTTagConfig (state, data) {
//将字符串分割为数组
data = data.replace(/'/g, "")
var array = data.slice(2, -4).split(",")
var DATA = []
array.forEach((item) => {
item = item.split(":")
DATA.push({
Id: item[0],
Num: parseFloat(item[1])
})
})
state.iotData = DATA
},
actions里添加:
updateIOTTagConfig ({ commit, state }, data) {
//let newData = Object.assign({}, state.iotData)
commit('updateIOTTagConfig', data)
},
其中:iotData: {},
表示默认的初始状态。setIOTData
表示第一次请求时为iotData
赋值。actions里的updateIOTTagConfig
表示执行 commit('updateIOTTagConfig', data)
,这句代码的作用是调用mutation里的updateIOTTagConfig
方法,
mutation里的updateIOTTagConfig
方法表示更新iotData
的值。
在Home.vue里,使用setIOTData
为iotData
赋值,
...mapMutations(['setIOTData']),
iot_data_get () {
getData().then((res) => {
this.setIOTData(res.data.response.AllData);
});
},
此时iotData
就不是空的了。
这个数据在Map.vue中使用:
computed: {
...mapState(['iotData']),
},
var name = this.iotData[j].Name; //this.dataList_rlt[i].Name
this.iotData[j].Note = (this.iotData[j].Note != null) ? this.iotData[j].Note : "--"
var html2 = "";
html2 =
'<div class="mapWindow_monitor" style=" width:250px;position: absolute;z-index: 200;left: 10px; top: -20px;opacity: 0.8;">' +
'<div style="background-image: url(' + arrow_png + ');background-repeat: no-repeat;background-position: center;float: left;width: 11px; height: 31px;z-index: 2;">' +
'</div>' +
'<div class="mapWindow_monitor_body" style=" float: left; background-color: #00457b;bottom: 10px;top: 0px;left: 0px;right: 0px;z-index: 2;border: 1px solid #7597ba;border-radius: 7%;box-shadow: 0px 2px 5px #888888;padding: 2px;">' +
'<div class="mapWindow_monitor_name" style=" height: 20px;line-height: 20px;text-align: center;background-color: #00457b;color: #fff;font-weight: bold;text-align: center;font-size: 13px; border-bottom: 1px solid #ccc;">' + name + '</div>' +
'<ul class="mapWindow_monitor_content" style=" list-style: none;padding: 0;margin: 0;">'
+ ' <li class="mapWindow_monitor_normal" style=" line-height: 20px;text-align: left;color: #fff;font-size: 11px;background-color: #0147b1 !important;font-family: Microsoft YaHei;list-style: none;padding: 0; margin: 0;border-left: 1px solid #817f7f; border-right: 1px solid #817f7f;border-bottom: 1px solid #817f7f;">' +
' <span>' + "数值:" + this.iotData[j].Num + '</span>' +
'</li>' +
' <li style=" line-height: 20px;text-align: left;color: #fff;font-size: 11px;background-color: #0147b1 !important;font-family: Microsoft YaHei;list-style: none;padding: 0; margin: 0;border-left: 1px solid #817f7f; border-right: 1px solid #817f7f;border-bottom: 1px solid #817f7f;">' +
' <span>' + "备注:" + this.iotData[j].Note + '</span>' +
'</li>' +
'</ul></div></div>'
;
此时弹窗可以显示数据了,但我们改变数据库数据时,并不发生改变。
正式工作:
1,util文件夹中新建websoket.js文件:
let glSocket = null;
import store from "../src/store"
export const localSocket = () => {
if ("WebSocket" in window) {
// console.log("您的浏览器支持 WebSocket!");
// location.host
if (!glSocket) {
//如果要使用我的后台那下面的地址应改为:
// glSocket = new WebSocket("ws://112.125.90.247:8088/");
glSocket = new WebSocket("ws://127.0.0.1:8088");
glSocket.binaryType = 'arraybuffer';
glSocket.onopen = () => {
console.log('websocket连接成功');
};
glSocket.onclose = () => {
// 关闭 websocket
console.log("连接已关闭...");
//断线重新连接
setTimeout(() => {
localSocket();
}, 2000);
};
glSocket.onmessage = (msg) => {
let d = JSON.stringify(msg.data);
store.dispatch("updateIOTTagConfig", d);
};
}
} else {
// 浏览器不支持 WebSocket
self.$notify.error({
title: '错误',
message: '您的浏览器不支持websocket,请更换其它浏览器再尝试'
});
}
}
export default glSocket;
这里需要注意的是两部分:
①glSocket = new WebSocket("ws://127.0.0.1:8088");
这里面的地址就是准备工作里的http://127.0.0.1:8088
②
glSocket.onmessage = (msg) => {
let d = JSON.stringify(msg.data);
store.dispatch("updateIOTTagConfig", d);
};
glSocket.onmessage
表示当收到来自服务器的消息时被调用的,这个服务器就是我们的websocketd客户端。因为websocketd客户端每隔1秒运行一次代码,相当于glSocket.onmessage
也每隔1秒执行一次,里面的代码很明显store.dispatch("updateIOTTagConfig", d);
,也就是执行actions里的updateIOTTagConfig
方法。
2,连接websocket。其实就是执行localSocket方法,执行一次就够了,在前面Home.vue获取数据时执行就行,也就是:
...mapMutations(['setIOTData']),
iot_data_get () {
getData().then((res) => {
this.setIOTData(res.data.response.AllData);
localSocket();
});
},
当然,这个方法localSocket是要引入的:
import { localSocket } from '../../util/websoket';
3,改变显示
回到Map.vue,这里我们要用到的是watch属性。
//监听属性:监听变量值发生变化执行相应函数)
watch: {
iotData: {
handler (newval, oldval) {
let result = []
if (!oldval.length) result = newval
else {
result = newval.filter(item => {
let obj = oldval.find(a => a.Id == item.Id && a.Num != item.Num)
// if(obj){
// obj.Num=isNaN(obj.Num)?0:obj.Num
// }
return obj
})
}
result.map(item => {
let labelid = item.Id && item.Id.trim()
if (labelid) {
let label = document.getElementById('mapWindow_' + labelid)
let span = label ? label.getElementsByTagName("span")[0] : null
span && (span.innerHTML = "数值:" + item.Num)
}
})
},
deep: true
}
},
监听iotData,当iotData有Num值变化时,通过操作dom改变显示