要写一个实时监控页面,监控堆垛机实时状态以及线体上是否有载具、是否有任务、是否有告警。项目是若依前端,后端是Java接口。要跳过Java接口,直接对接C#的signalR推送数据。需要配置两个总接口地址,原先的Java总接口地址不能删。这里的signalR是Microsoft的sigalR,开始还搞错了,搞成了另一种signalR的.netCore的版本,package.json依赖包删掉,重来,重新安装Microsoft版本signalR版本。话不多说,直接上代码。
1.首先把signalR依赖包安装上
npm install @microsoft/signalr
2.配置新的C#端监控地址,在.env.development文件中添加地址
# 载具监控地址 VUE_APP_MONITRO_WCS_EQUIPMENT = 'http://XX.XXX.XX.XX:XXXXX'
在.env.production文件中添加地址,方便后面配置到nginx转发
# 载具监控地址 VUE_APP_MONITRO_WCS_EQUIPMENT = '/monitro-api'
3.index.vue监控页面引入signalR
import * as signalR from "@microsoft/signalr";
4.连接signalR接收数据
//接收wcs线体和堆垛机数据 connect() {let url = `${process.env.VUE_APP_MONITRO_WCS_EQUIPMENT}/Equipment`;//本地开发版 singalR地址先写死 后期需要后端配置到nginx中// let url = `http://XX.XXX.XX.XX:XXXXX/Equipment`;// let token = store.getters.token.replace("Bearer ", ""); let token;const builder = new signalR.HubConnectionBuilder();this.connection = builder.withUrl(url, {accessTokenFactory: () => token,}).withAutomaticReconnect({nextRetryDelayInMilliseconds: () => 60000,}) //断线重连 .build();// 接收信息成功 线体this.connection.on("ReceiveMessage", (message) => {// console.log(message, "接收线体信息成功");//通过总线发送输送线实时任务bus.$emit("sendTransmissionLineByBus", message);if (message && message.length > 0) {//拼接接收到底所有线体集合for (let i = 0; i < message.length; i++) {this.conveyorUnitDtos = this.conveyorUnitDtos.concat(message[i].conveyorUnitDtos);}//删除所有线体集合arr中的重复对象var newArr = [];var arrId = [];for (var item of this.conveyorUnitDtos) {if (arrId.indexOf(item["id"]) == -1) {arrId.push(item["id"]);newArr.push(item);}}this.conveyorUnitDtos = newArr;if (this.conveyorUnitDtos.length > 0) {//线体告警数组var conveyorErrorArr = [];//线体告警隐藏位数组var conveyorErrorHiddenArr = [];//线体载具数组var conveyorGoodsArr = [];//线体载具隐藏位数组var conveyorGoodsHiddenArr = [];//线体载具任务数组var conveyorTaskNumArr = [];//线体载具任务编号ID数组var conveyorTaskNumIdArr = [];//线体载具任务隐藏位数组var conveyorTaskNumHiddenArr = [];this.conveyorUnitDtos.map((cObj, index) => {//conveyorGoods 1=无货、2=有货//conveyorTaskNum 任务号//conveyorError 0=无报警 其他=报警if (cObj.conveyorGoods == 2) {conveyorGoodsArr.push(cObj.id);} else {conveyorGoodsArr = conveyorGoodsArr.filter((item) => item != cObj.id);}// 任务号if (cObj.conveyorTaskNum != 0) {let cob = {id: cObj.id,conveyorTaskNum: cObj.conveyorTaskNum,};conveyorTaskNumArr.push(cob);conveyorTaskNumIdArr.push(cObj.id);} else {// let cob = {id: cObj.id,conveyorTaskNum: cObj.conveyorTaskNum,};//conveyorTaskNum等于0 已完成的任务 需要清空之前的任务数组值conveyorTaskNumArr = conveyorTaskNumArr.filter((val) => val.id !== cObj.id);conveyorTaskNumIdArr = conveyorTaskNumIdArr.filter((item) => item != cObj.id);}//告警if (cObj.conveyorError != 0) {conveyorErrorArr.push(cObj.id);} else {//已清除的告警 需要从之前的告警数组值清除conveyorErrorArr = conveyorErrorArr.filter((item) => item != cObj.id);}});//隐藏的线体载具conveyorGoodsHiddenArr = line_vehicle_Change(conveyorGoodsArr);//隐藏的线体告警conveyorErrorHiddenArr = line_error_Change(conveyorErrorArr);//隐藏的线体任务号conveyorTaskNumHiddenArr =line_taskNum_Change(conveyorTaskNumIdArr);//显示的线体载具和线体任务号for (let i = 0; i < conveyorGoodsArr.length; i++) {this.xtVehicle_arr[`xtVehicle_` + conveyorGoodsArr[i]] =xtVehicleGreen;}//隐藏的线体载具和线体任务号for (let i = 0; i < conveyorGoodsHiddenArr.length; i++) {this.xtVehicle_arr[`xtVehicle_` + conveyorGoodsHiddenArr[i]] = "";}//数组新增一条记录删除一条记录会更新domthis.xtVehicle_arr.push("xtVehicle");this.xtVehicle_arr.pop();//显示的线体任务号for (let i = 0; i < conveyorTaskNumArr.length; i++) {this.xtVehicle_task_arr[`xtVehicle_` + conveyorTaskNumArr[i].id + `_task`] = conveyorTaskNumArr[i].conveyorTaskNum;}//隐藏的线体任务号for (let i = 0; i < conveyorTaskNumHiddenArr.length; i++) {this.xtVehicle_task_arr[`xtVehicle_` + conveyorTaskNumHiddenArr[i].id + `_task`] = "";}//数组新增一条记录删除一条记录会更新domthis.xtVehicle_task_arr.push("xtVehicle_task");this.xtVehicle_task_arr.pop();//显示的线体告警for (let i = 0; i < conveyorErrorArr.length; i++) {this.xtAlarm_arr[`xtAlarm_` + conveyorErrorArr[i]] = xtAlarmRed;}//隐藏的线体告警for (let i = 0; i < conveyorErrorHiddenArr.length; i++) {this.xtAlarm_arr[`xtAlarm_` + conveyorErrorHiddenArr[i]] = "";}//数组新增一条记录删除一条记录会更新domthis.xtAlarm_arr.push("xtAlarm");this.xtAlarm_arr.pop();}}});// 接收信息成功 堆垛机this.connection.on("ReceiveStackerStk", (message) => {// console.log(message, "接收堆垛机信息成功");//通过总线发送堆垛机实时数据bus.$emit("sendPilerByBus", message);if (message && message.length > 0) {//1#堆垛机 2#堆垛机 3#堆垛机 4#堆垛机var num1ddjObj;var num2ddjObj;var num3ddjObj;var num4ddjObj;//1#虫卵库隐藏的列数var num1ddjObjForkHiddenColumn = [];//2#幼虫库隐藏的列数var num2ddjObjForkHiddenColumn = [];//3#成虫库隐藏的列数var num3ddjObjForkHiddenColumn = [];//4#成虫库隐藏的列数var num4ddjObjForkHiddenColumn = [];message.map((dObj, index) => {//1#堆垛机if (dObj.tunnelId == 1) {num1ddjObj = dObj;if (num1ddjObj) {//显示当前列 frontForkColumnif (num1ddjObj.frontForkColumn != 0) {this.yx_1hddj_arr[`yx_1hddj_` + num1ddjObj.frontForkColumn] =ddjGreen;} else {this.yx_1hddj_arr[yx_1hddj_2] = ddjGreen;}var element = num1ddjObj.frontForkColumn;//隐藏的1#虫卵库 列num1ddjObjForkHiddenColumn = piler_1_Change(element);//隐藏的1#虫卵库 列for (let i = 0; i < num1ddjObjForkHiddenColumn.length; i++) {this.yx_1hddj_arr[`yx_1hddj_` + num1ddjObjForkHiddenColumn[i]] = "";}//显示任务号//任务是什么动作 firstType// 1 -入 (入库) ;2 -出 (出库);3 -移 (移库任务) 4 -输送线用(目前堆垛机用不到);// 5 -转运(直出 移动 堆垛机 从头走到尾部) 0-无任务//单工位同时最多有一个任务号 双工位同时最多有两个任务号//堆垛机类型(1-虫卵库双工位 0-单工位 幼虫库成虫库单工位)//前插(单工位)frontTaskDto.wcsId -任务号//后插(双工位)backTaskDto.wcsId -任务号//任务号 任务类型+任务号组成var taskNumberStr;//前插var froTaskDto;//后插var baTaskDto;if (num1ddjObj.platform == 1) {froTaskDto = num1ddjObj.frontTaskDto;baTaskDto = num1ddjObj.backTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;taskNumberStr =taskNumberStr +(baTaskDto.firstType == 1? "入:": baTaskDto.firstType == 2? "出:": baTaskDto.firstType == 3? "移:": baTaskDto.firstType == 4? "输送线用:": baTaskDto.firstType == 5? "转运:": "") +baTaskDto.wcsId;} else {froTaskDto = num1ddjObj.frontTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;}//如果堆垛机有当前列 任务号显示在对应当前列位置上 没有当前列 就显示在堆垛机首个位置if (num1ddjObj.frontForkColumn != 0) {this.yx_1hddj_arr[`yx_1hddj_` + num1ddjObj.frontForkColumn + `_task`] = taskNumberStr;} else {this.yx_1hddj_arr[`yx_1hddj_` + 2 + `_task`] = taskNumberStr;}//隐藏的1#虫卵库 其他任务号for (let i = 0; i < num1ddjObjForkHiddenColumn.length; i++) {this.yx_1hddj_arr[`yx_1hddj_` + num1ddjObjForkHiddenColumn[i] + `_task`] = "";}}//数组新增一条记录删除一条记录会更新domthis.yx_1hddj_arr.push("1#");this.yx_1hddj_arr.pop();}//2#堆垛机if (dObj.tunnelId == 2) {num2ddjObj = dObj;if (num2ddjObj) {//显示当前列 frontForkColumnif (num2ddjObj.frontForkColumn != 0) {this.yx_2hddj_arr[`yx_2hddj_` + num2ddjObj.frontForkColumn] =ddjGreen;} else {this.yx_2hddj_arr[yx_1hddj_1] = ddjGreen;}var element = num2ddjObj.frontForkColumn;//隐藏的2#虫卵库 列num2ddjObjForkHiddenColumn = piler_2_Change(element);//隐藏的2#虫卵库 列for (let i = 0; i < num2ddjObjForkHiddenColumn.length; i++) {this.yx_2hddj_arr[`yx_2hddj_` + num2ddjObjForkHiddenColumn[i]] = "";}//任务号 任务类型+任务号组成var taskNumberStr;//前插var froTaskDto;//后插var baTaskDto;if (num2ddjObj.platform == 1) {froTaskDto = num2ddjObj.frontTaskDto;baTaskDto = num2ddjObj.backTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;taskNumberStr =taskNumberStr +(baTaskDto.firstType == 1? "入:": baTaskDto.firstType == 2? "出:": baTaskDto.firstType == 3? "移:": baTaskDto.firstType == 4? "输送线用:": baTaskDto.firstType == 5? "转运:": "") +baTaskDto.wcsId;} else {froTaskDto = num2ddjObj.frontTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;}//如果堆垛机有当前列 任务号显示在对应当前列位置上 没有当前列 就显示在堆垛机首个位置if (num2ddjObj.frontForkColumn != 0) {this.yx_2hddj_arr[`yx_2hddj_` + num2ddjObj.frontForkColumn + `_task`] = taskNumberStr;} else {this.yx_2hddj_arr[`yx_2hddj_` + 1 + `_task`] = taskNumberStr;}//隐藏的2#幼虫库 其他任务号for (let i = 0; i < num2ddjObjForkHiddenColumn.length; i++) {this.yx_2hddj_arr[`yx_2hddj_` + num2ddjObjForkHiddenColumn[i] + `_task`] = "";}}//数组新增一条记录删除一条记录会更新domthis.yx_2hddj_arr.push("2#");this.yx_2hddj_arr.pop();}//3#堆垛机if (dObj.tunnelId == 3) {num3ddjObj = dObj;if (num3ddjObj) {//显示当前列 frontForkColumnif (num3ddjObj.frontForkColumn != 0) {this.yx_3hddj_arr[`yx_3hddj_` + num3ddjObj.frontForkColumn] =ddjGreen;} else {this.yx_3hddj_arr[yx_3hddj_1] = ddjGreen;}var element = num3ddjObj.frontForkColumn;//隐藏的3#虫卵库 列num3ddjObjForkHiddenColumn = piler_3_Change(element);//隐藏的3#虫卵库 列for (let i = 0; i < num3ddjObjForkHiddenColumn.length; i++) {this.yx_3hddj_arr[`yx_3hddj_` + num3ddjObjForkHiddenColumn[i]] = "";}//任务号 任务类型+任务号组成var taskNumberStr;//前插var froTaskDto;//后插var baTaskDto;if (num3ddjObj.platform == 1) {froTaskDto = num3ddjObj.frontTaskDto;baTaskDto = num3ddjObj.backTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;taskNumberStr =taskNumberStr +(baTaskDto.firstType == 1? "入:": baTaskDto.firstType == 2? "出:": baTaskDto.firstType == 3? "移:": baTaskDto.firstType == 4? "输送线用:": baTaskDto.firstType == 5? "转运:": "") +baTaskDto.wcsId;} else {froTaskDto = num3ddjObj.frontTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;}//如果堆垛机有当前列 任务号显示在对应当前列位置上 没有当前列 就显示在堆垛机首个位置if (num3ddjObj.frontForkColumn != 0) {this.yx_3hddj_arr[`yx_3hddj_` + num3ddjObj.frontForkColumn + `_task`] = taskNumberStr;} else {this.yx_3hddj_arr[`yx_3hddj_` + 1 + `_task`] = taskNumberStr;}//隐藏的3#成虫库 其他任务号for (let i = 0; i < num3ddjObjForkHiddenColumn.length; i++) {this.yx_3hddj_arr[`yx_3hddj_` + num3ddjObjForkHiddenColumn[i] + `_task`] = "";}}//数组新增一条记录删除一条记录会更新domthis.yx_3hddj_arr.push("3#");this.yx_3hddj_arr.pop();}//4#堆垛机if (dObj.tunnelId == 4) {num4ddjObj = dObj;if (num4ddjObj) {//显示当前列 frontForkColumnif (num4ddjObj.frontForkColumn != 0) {this.yx_4hddj_arr[`yx_4hddj_` + num4ddjObj.frontForkColumn] =ddjGreen;} else {this.yx_4hddj_arr[yx_4hddj_1] = ddjGreen;}//数组新增一条记录删除一条记录会更新domthis.yx_4hddj_arr.push("4#");this.yx_4hddj_arr.pop();var element = num4ddjObj.frontForkColumn;//TODO//隐藏的4#虫卵库 列num4ddjObjForkHiddenColumn = piler_4_Change(element);//隐藏的4#虫卵库 列for (let i = 0; i < num4ddjObjForkHiddenColumn.length; i++) {this.yx_4hddj_arr[`yx_4hddj_` + num4ddjObjForkHiddenColumn[i]] = "";}//任务号 任务类型+任务号组成var taskNumberStr;//前插var froTaskDto;//后插var baTaskDto;if (num4ddjObj.platform == 1) {froTaskDto = num4ddjObj.frontTaskDto;baTaskDto = num4ddjObj.backTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;taskNumberStr =taskNumberStr +(baTaskDto.firstType == 1? "入:": baTaskDto.firstType == 2? "出:": baTaskDto.firstType == 3? "移:": baTaskDto.firstType == 4? "输送线用:": baTaskDto.firstType == 5? "转运:": "") +baTaskDto.wcsId;} else {froTaskDto = num4ddjObj.frontTaskDto;taskNumberStr =(froTaskDto.firstType == 1? "入:": froTaskDto.firstType == 2? "出:": froTaskDto.firstType == 3? "移:": froTaskDto.firstType == 4? "输送线用:": froTaskDto.firstType == 5? "转运:": "") + froTaskDto.wcsId;}//如果堆垛机有当前列 任务号显示在对应当前列位置上 没有当前列 就显示在堆垛机首个位置if (num4ddjObj.frontForkColumn != 0) {this.yx_4hddj_arr[`yx_4hddj_` + num4ddjObj.frontForkColumn + `_task`] = taskNumberStr;} else {this.yx_4hddj_arr[yx_4hddj_1_task] = taskNumberStr;}//隐藏的4#成虫库 其他任务号for (let i = 0; i < num4ddjObjForkHiddenColumn.length; i++) {this.yx_4hddj_arr[`yx_4hddj_` + num4ddjObjForkHiddenColumn[i] + `_task`] = "";}}//数组新增一条记录删除一条记录会更新domthis.yx_4hddj_arr.push("4#");this.yx_4hddj_arr.pop();}});}});//自动重连成功后的处理this.connection.onreconnected((connectionId) => {console.log(connectionId, "自动重新连接成功");});// 监听关闭this.connection.onclose((res) => {console.log("监听关闭", res);});// 开始连接this.connection.start().then((res) => {// console.log("启动即时通信成功", res); }).catch((err) => {console.log(err);});},
5.index.vue中 mounted() 方法内初始化1号堆垛机数组、2号堆垛机数组、3号堆垛机数组、4号堆垛机数组、告警数组、载具数组、载具任务数组数据名称
以及数据内容。
mounted() {this.initPanZoom();this.yx_1hddj_arr = [];this.yx_2hddj_arr = [];this.yx_3hddj_arr = [];this.yx_4hddj_arr = [];this.xtAlarm_arr = [];this.xtVehicle_arr = [];this.xtVehicle_task_arr = [];//初始化1#堆垛机 堆垛机列数组 任务数组 从3号位开始 到43号位结束 总共41个列位置(2号位为初始0位)for (let i = 2; i <= 43; i++) {if (i == 2) {this.yx_1hddj_arr[`yx_1hddj_` + i] = ddjGreen;this.yx_1hddj_arr[`yx_1hddj_` + i + `_task`] = "出:0";} else {this.yx_1hddj_arr[`yx_1hddj_` + i] = "";this.yx_1hddj_arr[`yx_1hddj_` + i + `_task`] = "";}}//初始化2#堆垛机 堆垛机列数组 任务数组 从2号位开始 到29号位结束 总共28个列位置(1号位为初始0位)for (let i = 1; i <= 29; i++) {if (i == 1) {this.yx_2hddj_arr[`yx_2hddj_` + i] = ddjGreen;this.yx_2hddj_arr[`yx_2hddj_` + i + `_task`] = "出:0";} else {this.yx_2hddj_arr[`yx_2hddj_` + i] = "";this.yx_2hddj_arr[`yx_2hddj_` + i + `_task`] = "";}}//初始化3#堆垛机 堆垛机列数组 任务数组 从2号位开始 到29号位结束 总共28个列位置(1号位为初始0位)for (let i = 1; i <= 29; i++) {if (i == 1) {this.yx_3hddj_arr[`yx_3hddj_` + i] = ddjGreen;this.yx_3hddj_arr[`yx_3hddj_` + i + `_task`] = "出:0";} else {this.yx_3hddj_arr[`yx_3hddj_` + i] = "";this.yx_3hddj_arr[`yx_3hddj_` + i + `_task`] = "";}}//初始化4#堆垛机 堆垛机列数组 任务数组 从2号位开始 到29号位结束 总共28个列位置 (1号位是初始0位)for (let i = 1; i <= 29; i++) {if (i == 1) {this.yx_4hddj_arr[`yx_4hddj_` + i] = ddjGreen;this.yx_4hddj_arr[`yx_4hddj_` + i + `_task`] = "出:0";} else {this.yx_4hddj_arr[`yx_4hddj_` + i] = "";this.yx_4hddj_arr[`yx_4hddj_` + i + `_task`] = "";}}//初始化线体载具 线体任务 线体告警// 1#虫卵库线体编号1001~1017 2#幼虫库线体编号1018~1040 (生成的线体编号数组中间有个别用不到的线体编号可以忽略)// 3#成虫库 4#成虫库 线体编号2001~2067for (let i = 1001; i <= 1040; i++) {this.xtVehicle_arr[`xtVehicle_` + i] = "";this.xtVehicle_task_arr[`xtVehicle_` + i + `_task`] = "";this.xtAlarm_arr[`xtAlarm_` + i] = "";}for (let i = 2001; i <= 2067; i++) {this.xtVehicle_arr[`xtVehicle_` + i] = "";this.xtVehicle_task_arr[`xtVehicle_` + i + `_task`] = "";this.xtAlarm_arr[`xtAlarm_` + i] = "";}},
6.在<script></script>标签内引入图片
//堆垛机 红 //料箱 笼框 载具 //告警 //堆垛机 绿 import ddjRed from "@/assets/images/svg/堆垛机02_红.png"; import ddjGreen from "@/assets/images/svg/堆垛机02_横向_绿.png"; import xtAlarmRed from "@/assets/images/svg/g告警数据.png"; import xtVehicleGreen from "@/assets/images/svg/linforma_料箱_绿 (2).png";
7.index.vue中引入了svg静态堆垛机和线体图,svg图可以拖拽无限放大缩小, 这里可以参考另我的一篇文章《svg可拖拽无限放大缩小》,
感兴趣的可以移步到文章链接查看详情,这里不做展开描述。
<template><div class="allbgc"><div class="contentwid"><!-- // 需要拖动的dom --><div ref="dom" @wheel="wheel"><div class="svgdiv"><svg id="vehicleMonitorId" ref="mysvg" data-name="vehicleMonitorName" xmlns="http://www.w3.org/2000/svg":viewBox="viewboxw"><!-- <title>二楼上层</title> --><g><ellipse class="st0" cx="39.2" cy="106" rx="28.5" ry="32" /><g><g><g><g><g><text transform="matrix(1.2 0 0 1 18.1001 119.2002)" class="st1 st2 st3">1#</text></g></g></g></g></g></g><!-- todo --><!-- 引入4号堆垛机货架 最后10列 19~28列 svg --><!-- <DdjHj /> --><!-- <g id="svgElement" ></g> --><rect x="81.2" y="889.5" class="st4" width="608.7" height="60.7" /><rect x="81.1" y="890" class="st5" width="60.1" height="60.3" /><line class="st6" x1="81.1" y1="950.3" x2="141.2" y2="890" /><line class="st6" x1="81.1" y1="890" x2="141.2" y2="950.3" /><rect x="142" y="890" class="st5" width="60.1" height="60.3" /><line class="st6" x1="142" y1="950.3" x2="202.1" y2="890" /><line class="st6" x1="142" y1="890" x2="202.1" y2="950.3" /><rect x="202.9" y="890" class="st5" width="60.1" height="60.3" /><line class="st6" x1="202.9" y1="950.3" x2="263" y2="890" />
</svg>
<clipPath id="clippath"><rect class="cls-3" x="1729.9" y="837.4" width="45.7" height="35.9" /></clipPath></div></div></div>
8.数据刷刷的出来了,上图。
9.补充:上图svg图可以无限放大缩小,图上有600、700个数据点,随意拖拽不卡顿。查看详情看笔者另一篇文章《svg可拖拽无限放大缩小》
文章链接:
参考文档:https://learn.microsoft.com/zh-cn/aspnet/core/signalr/javascript-client?view=aspnetcore-8.0&tabs=visual-studio