适用于:vue3项目使用@micosoft/signalr(长链接/服务端推送到客户端),大数据看板后台接口自动定时推送前端接收渲染
推送接口地址:
先安装"@microsoft/signalr": “^7.0.10”,
npm install @microsoft/signalr
import * as signalR from '@microsoft/signalr'
const connection =new signalR.HubConnectionBuilder()
.withUrl("/hubs/j03/kanban")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: function (val) {
// _this.$TF.closeLoading()
let arr = [1000, 5000, 10000, 30000]
if (val.previousRetryCount <= 3) {
return arr[val.previousRetryCount]
} else {
return arr[3]
}
}
})
.configureLogging(signalR.LogLevel.Information)
.build();
//接口推送1
connection.on('receivedDeviceRealTimeInfos', function (infos){
console.log(123,infos);//拿到后台推送的数据
});
//接口推送2
connection.on('receivedDeviceStopTop5', function (infos){
console.log('停机TOP5',infos);;//拿到后台推送的数据
});
//接口推送3
connection.on('receivedDeviceAlarm', function (infos){
console.log('报警',infos);;//拿到后台推送的数据
});
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
//此处当前端参数改变时候传参模式,JTB03暂时写死触发传参:
connection.invoke('register', 'JTB03');
//connection.invoke('method', arguments);
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
//Start the connection.启动
start();
其他相关文章参考:https://blog.csdn.net/Elliot_Wang/article/details/131665055
以下举例具体在项目中的应用:
父页面(看板主页面)kanban.vue
<template>
<div class="board" v-cloak>
<div class="board-T">
<div class="board-T-item T1">
<!-- <allDevice v-if="isBoard" :boardData="boardData" :upIndex="upIndex" :fId="fId" class="board-border"></allDevice> -->
<allDevice class="board-border" @nameSelect="nameSelectGet"></allDevice>
</div>
<div class="board-T-item T2">
<div class="board-l">
<div class="board-item">
<top5Tingji :StopTop5="state.StopTop5" class="board-border">
</top5Tingji>
</div>
</div>
<div class="board-m">
<div class="board-item">
<top5Weixiu class="board-border" />
</div>
</div>
<div class="board-r">
<div class="board-item">
<alarm :infosAlarm="state.geolocation" class="board-border">
</alarm>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import * as signalR from '@microsoft/signalr'
import { onMounted, reactive, ref, provide, watch } from 'vue';
import allDevice from '/@/views/device-overview/kanbanBox/allDevice.vue'
import top5Tingji from '/@/views/device-overview/kanbanBox/top5Tingji.vue'
import top5Weixiu from '/@/views/device-overview/kanbanBox/top5Weixiu.vue'
import alarm from '/@/views/device-overview/kanbanBox/alarm.vue'
const state = reactive({
tableHeight: window.innerHeight - 370,
pageIndex: 1,
pageSize: 20,
total: 0,
tableData: [],
deviceSN: '',
fromData: {},
index: 0,
date: [
// new Date(formatDate(getCycle(-7), 2) + ' 00:00:00'),
// new Date(formatDate(getCycle(0), 2) + ' 00:00:00'),
],
geolocation: ref([]),
StopTop5: ref({}),
MaintenanceTop5: ref([]),
DeviceInfos: ref({}),
connecData:''
});
// 改变传参.触发传送
const nameSelectGet = (val : any) => {
console.log(val, 888) // 50
state.connecData=val;
console.log(state.connecData, 8889) // 50
connection.invoke('register', val);
}
onMounted(() => {
})
// 处理推送开始
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/j03/kanban")
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: function (val) {
// _this.$TF.closeLoading()
let arr = [1000, 5000, 10000, 30000]
if (val.previousRetryCount <= 3) {
return arr[val.previousRetryCount]
} else {
return arr[3]
}
}
})
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on('receivedDeviceRealTimeInfos', function (infos) {
console.log('设备汇总', infos);
state.DeviceInfos.values = infos;
});
provide('DeviceInfos', state.DeviceInfos)
connection.on('receivedDeviceStopTop5', function (infos) {
// console.log('停机TOP5', infos);
state.StopTop5.values = infos;
// console.log('2停机TOP5', state.StopTop5.values);
});
provide('StopTop5', state.StopTop5)
connection.on('receivedMaintenanceTop5', function (infos) {
// console.log('维修top5', infos);
state.MaintenanceTop5.values = infos;
});
provide('infosMaintenanceTop5', state.MaintenanceTop5)
// const alarmInfos = ref([])
connection.on('receivedDeviceAlarm', function (infos) {
// console.log('报警', infos);
state.geolocation.values = infos;
});
provide('infosAlarm2', state.geolocation)
async function start() {
try {
await connection.start();
console.log("SignalR Connected.");
// connection.invoke('register', 'JTB03');
connection.invoke('register', state.connecData);
//connection.invoke('method', arguments);
} catch (err) {
console.log('错误', err);
setTimeout(start, 5000);
}
};
connection.onclose(async () => {
await start();
});
// Start the connection.
start();
// 处理后台推送结束
</script>
<style lang="scss" scoped>
.board {
width: 100%;
height: calc(100vh - 70px);
// height: calc(100vh);
overflow: hidden;
display: flex;
box-sizing: border-box;
background: url(../../assets/img/bcg.png);
background-size: 100% 100%;
background-position: center center;
.board-T {
width: 100%;
height: 100%;
.board-T-item {
// width: 100%;
// height: calc((100% - 70px) / 1.5);
height: 70%;
color: #fff;
padding: 10px;
padding-bottom: 0px;
display: flex;
.board-l,
.board-m,
.board-r {
width: 33%;
height: 100%;
border: 1px solid blue;
margin-right: 8px;
.board-item {
width: 100%;
height: 100%;
border: 1px solid #0d2451;
position: relative;
overflow: hidden;
background: #151456;
width: 100%;
background: url(../../assets/img/bcg3.png);
height: 100%;
background-size: 100% 100%;
background-position: center center;
}
}
&:last-child {
border: none;
padding-bottom: 10px;
height: 30%;
// border: 1px solid red;
}
}
}
.board-border {
width: 100%;
height: 100%;
// border: 1px solid #0d2451;
position: relative;
overflow: hidden;
// background: #151456;
}
}
.T1 {
background: url(/j03/src/assets/img/bcg3.png);
background-size: 98.5% 100%;
background-position: center center;
background-repeat: no-repeat;
}
[v-cloak] {
display: none;
}
</style>
子页面:allDevice.vue
<template>
<div class="default-main">
<!-- <div>电能表数据监控</div> -->
<div class='allBox'>
<div><span class="title">单机设备信息</span>
<dv-decoration8 class="decoration8" :reverse="true" style="width:250px;height:20px;" />
<span style="margin: 0 20px;">总数</span><span class="color1">{{state.totalStatus.totalCount}}台</span>
<span class="box1" :class="addClass(1,item.status)"
v-for="item in state.totalStatus.statusOverview"><span>{{item.name}}</span><span>{{item.count}}台</span></span>
<el-select class="ft" v-model="state.value" @change="chooseValue" style="width:120px" value-key="id"
placeholder="请选择">
<el-option v-for="item in state.lineData" :label="item.value" :value="item.value" :key="item.key">
</el-option>
</el-select>
</div>
<div class="device_list">
<div class="loading">
<el-carousel indicator-position="outside" height="550px" interval='3000'>
<!-- 暂时是三条线3*8=24,一个页面展示24条数据轮播 -->
<el-carousel-item v-for="item in Math.ceil(state.pages / 8)" :key="item">
<div v-for="val in state.deviceData" class="lineBox">
<!-- <h3>{{ val.name }}</h3> -->
<div v-for="(item,index) in val.deviceItems.slice((item - 1) * 8,item * 8)"
@click="toDetail(item)">
<div class="device_info" :class="addClass(2,item.status)">
<div class="greenCenter"><span>{{item.deviceName}}</span></div>
<div class="greenCenter"><span>{{item.deviceSn}}</span></div>
<div class='line'></div>
<div v-if="item.status==2">当日产量:<span class="color2">{{item.quantity}}PCS</span>
</div>
<div v-if="item.status==2">OEE:<span class="greenOEE"><el-progress
:percentage="item.oee" color="#3b9e00"></el-progress></span>
</div>
<div v-if="item.status!==2" class="statusName greenCenter"
:class="addClass(1,item.status)">{{item.statusStr}}</div>
<div>持续时间:<span :class="addClass(1,item.status)">{{item.durationStr}}分钟</span>
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import * as signalR from '@microsoft/signalr'
import * as echarts from 'echarts';
import { onMounted, reactive, ref, watch, inject, provide } from 'vue';
import { Download, Search } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { ChartQuery, GetProductCodeSelect } from '../../../api/j03';
import { formatDate, getCycle } from '../../../utils/time';
const myRef = ref(null);
const state = reactive({
totalStatus: {},
value: '',
pages: 1,
lineData: [],
deviceData: [],
deviceSN: '',
});
onMounted(() => {
GetProductCodeSelect2();
setInterval(() => {
// console.log('子设备汇总', DeviceInfos.values)
state.totalStatus = DeviceInfos.values.statusOverview;
// state.tableData=DeviceInfos.values.lines;
// 挑选出每条线体数组长度最长的进行计算轮播的页数pages
state.deviceData = DeviceInfos.values.lines;
let data = DeviceInfos.values.lines;
let arr = []
for (let i = 0; i < data.length; i++) {
arr.push(data[i].deviceItems.length)
}
// console.log(arr,788)
const max = Math.max(...arr);
state.pages = max;//已挑选出线体最大长度,这个长度决定轮播分几页展示
// console.log(max,state.pages,789); // 输出:最大值
}, 3000)
});
let DeviceInfos = inject('DeviceInfos')
// 子传父改变值重新推送
const emits = defineEmits(['nameSelect']);
const chooseValue = ((val) => {
// console.log(val, 7777)
state.value = val;
emits('nameSelect', val)
})
const addClass = ((num, status) => {
// 设备运行状态离线=0,1=待机,运行=2,3=空转,故障中=4,
var cname = '';
var cdevice = '';
switch (status) {
case 0: //离线
cname = 'color0';
cdevice = 'device0';
break;
case 1: //运行
cname = 'color1';
cdevice = 'device1';
break;
case 2: //停止
cname = 'color2';
cdevice = 'device2';
break;
case 3: //停止
cname = 'color3';
cdevice = 'device3';
break;
case 4: //停止
cname = 'color4';
cdevice = 'device4';
break;
}
if (num == 1) {
return cname;
} else {
return cdevice;
}
// return cname, cdevice;
})
const GetProductCodeSelect2 = (id : string) => {//下拉查询
GetProductCodeSelect().then((res : any) => {
if (res.status === 0) {
// ElMessage({ type: 'success', message: '操作成功' });
state.lineData = res.data;
// 如果下拉有值,则默认请求第一条数据
if(state.lineData!=[]||state.lineData.length>0){
chooseValue(state.lineData[0].value)
}
}
});
};
</script>
<style>
.ft .el-input__wrapper {
cursor: pointer;
background-color: #13214d;
border: 1px solid #64f5f7;
color: #64f5f7 !important;
text-align: center;
}
.decoration8 {
width: 250px;
height: 20px;
display: inline-block;
position: absolute;
left: 16px;
top: 44px;
}
.el-select .el-input__inner {
cursor: pointer;
color: #62efe7;
}
</style>
<style lang="scss" scoped>
.lineBox {
display: flow-root;
}
.allBox {
margin: 14px 21px;
font-size: 1rem;
.ft {
float: right;
margin-right: 2%;
}
}
.box1 {
display: inline-block;
width: 100px;
border: 1px solid;
height: 40px;
line-height: 40px;
border-radius: 10px;
text-align: center;
margin: 0 10px;
}
.title {
font-size: 1.2rem;
color: #62efe7;
margin-right: 22rem;
}
.line {}
.greenOEE {
display: inline-block;
width: 70%;
}
.greenCenter {
text-align: center;
}
.device_info {
width: 11%;
height: 10rem;
display: inline-block;
float: left;
font-size: 0.8rem;
background-color: rgba(5, 85, 168, 0.388235294117647);
box-sizing: border-box;
position: relative;
border-radius: 10px;
text-align: left;
margin: 1.3rem 0.66%;
padding: 5px;
overflow: hidden;
.statusName {
font-size: 1.5rem;
}
div {
margin: 5px 0;
}
.line {
width: 100%;
}
}
.device0 {
box-shadow: inset 0px 0px 10px #797979;
border: 1px solid #797979;
.line {
border: 1px solid #797979;
}
}
.device1 {
box-shadow: inset 0px 0px 10px #f6f700;
border: 1px solid #f6f700;
.line {
border: 1px solid #f6f700;
}
}
.device2 {
box-shadow: inset 0px 0px 10px #32e751;
border: 1px solid #32e751;
.line {
border: 1px solid green;
}
}
.device3 {
box-shadow: inset 0px 0px 10px #ffffff;
border: 1px solid #ffffff;
.line {
border: 1px solid #ffffff;
}
}
.device4 {
box-shadow: inset 0px 0px 10px #e72225;
border: 1px solid #e72225;
.line {
border: 1px solid #e72225;
}
}
// 设备运行状态离线=0,1=待机,运行=2,3=空转,故障中=4,
.color0 {
color: #797979;
}
.color1 {
color: #f6f700;
}
.color2 {
color: #32e751;
}
.color3 {
color: #ffffff;
}
.color4 {
color: #e72225;
}
</style>
数据结构:
{
"statusOverview": {
"totalCount": 3,
"statusOverview": [
{
"status": 0,
"name": "离线中",
"count": 3
},
{
"status": 1,
"name": "待机中",
"count": 0
},
{
"status": 2,
"name": "运行中",
"count": 0
},
{
"status": 4,
"name": "故障中",
"count": 0
}
]
},
"productCode": "JTB02",
"lines": [
{
"name": "A",
"deviceItems": [
{
"quantity": 0,
"duration": 0,
"durationStr": "0秒",
"oee": 0,
"sort": 1,
"deviceName": "SM-P2-MA-164_名称",
"deviceSn": "SM-P2-MA-164",
"status": 0,
"statusStr": "离线中"
}
]
},
{
"name": "B",
"deviceItems": [
{
"quantity": 0,
"duration": 0,
"durationStr": "0秒",
"oee": 0,
"sort": 1,
"deviceName": "咪头PCB气动压装机",
"deviceSn": "SM-XA-01-P7-MA-0285",
"status": 0,
"statusStr": "离线中"
}
]
},
{
"name": "C",
"deviceItems": [
{
"quantity": 1,
"duration": 12.6753261,
"durationStr": "13秒",
"oee": 0,
"sort": 1,
"deviceName": "咪头PCB气动压装机",
"deviceSn": "SM-XA-01-P8-MA-0695",
"status": 0,
"statusStr": "离线中"
}
]
}
]
}
子页面2:alarm.vue
<template>
<div class="default-main">
<!-- <div>电能表数据监控</div> -->
<div class="titleKanban">设备报警</div>
<!-- <div item in infosAlarm>{{item}}</div> -->
<dv-decoration8 :reverse="true" style="width:250px;height:20px;" />
<div demo-bg>
<dv-scroll-board :config="config" style="height:180px" />
</div>
</div>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts';
import { onMounted, reactive, ref,toRefs,inject} from 'vue';
import { ElMessage } from 'element-plus';
import { ChartQuery, SelectQuery } from '../../../api/j03';
const myRef = ref(null);
const state = reactive({
chartData: {},
deviceSN: '',
});
const config = reactive({
indexHeader: '序号',
header: ['设备名称', '消息内容', '故障类型', '时间'],
indexHeader: '序号',
rowNum: 6, // 表行数
headerBGC: 'rgba(37, 92, 149, 0.8);', // 表头背景色
oddRowBGC: 'rgba(35, 90, 146, 0.5);', // 奇数行背景色
evenRowBGC: 'rgba(30, 69, 119, 0.1);', // 偶数行背景色
data: [
['无', '无', '无', '无'],
// ['行1列1', '行1列2', '行1列3', '行1列3'],
// ['行2列1', '行2列2', '行2列3', '行1列3'],
// ['行3列1', '行3列2', '行3列3', '行1列3'],
// ['行4列1', '行4列2', '行4列3', '行1列3'],
// ['行1列1', '行1列2', '行1列3', '行1列3'],
// ['行2列1', '行2列2', '行2列3', '行1列3'],
// ['行3列1', '行3列2', '行3列3', '行1列3'],
// ['行4列1', '行4列2', '行4列3', '行1列3'],
// ['行1列1', '行1列2', '行1列3', '行1列3'],
// ['行2列1', '行2列2', '行2列3', '行1列3'],
],
index: true,
columnWidth: [50],
align: ['center'],
})
let infosAlarm2 = inject('infosAlarm2')
// console.log('子报警',infosAlarm2)
onMounted(() => {
setInterval(() => {
let val = infosAlarm2.values;
let arr = []
for (let i in val) {
let deviceName = val[i].deviceName
let content = val[i].content
let alarmTypeStr = val[i].alarmTypeStr
let startDateTime = val[i].startDateTimeStr
arr.push([deviceName, content,alarmTypeStr,startDateTime])
}
config.data=arr;
// console.log('子报警2',arr)
}, 3000)
});
</script>
<style lang="scss" scoped>
</style>