/* eslint-disable */ import React, { useState, useEffect, useRef, useContext, useMemo, useCallback } from 'react'; import classNames from 'classnames'; import moment from 'moment'; import axios from 'axios'; import Empty from '@wisdom-components/empty'; import LoadBox from '@wisdom-components/loadbox'; import VideoSliderModal from '@wisdom-components/videoslidermodal'; import { Input, message, Modal, Form, ConfigProvider, Button } from 'antd'; import { ExclamationCircleOutlined, DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined, EyeOutlined, EyeInvisibleOutlined, } from '@ant-design/icons'; import MqttView from '@wisdom-components/mqttview'; import PropTypes from 'prop-types'; import HistoryView from '@wisdom-components/ec_historyview'; import StatisticalHistoryView from '@wisdom-components/ec_statisticalhistoryview'; import * as go from '@wisdom-components/gojs'; import GuidedDraggingTool from './js/GuidedDraggingTool'; import TopRotatingTool from './js/RotatingTool'; import BarLink from './js/BarLink'; import WaterFlowControlView from './js/WaterFlowControlView'; import ConfigurationDetail from './js/ConfigurationDetail'; import ControlRecords from './components/ControlRecords'; import DragModal from './js/DragModal'; import { authorizationToken, getSketchPadList, getSketchPadContent, getPointAddress, getDeviceRealInfo, getVideoDetail, getDictionaryInfo, } from './apis'; import { isNumber, createGuid, deepCopy, hexToRgba, addNumMethod, hexSwitch, textStyle, querySkipUrl, queryProduct, isJson, stationData, getVideoUrl, loginauthen, encipher, } from './js/utils'; import svgIcons from './js/icons'; import './index.less'; const goJS = go.GraphObject.make; let online = false; let imgUrl = null; let modalComponent = null; // 点击节点展示的模态框内容 let modalConfirmFn = null; // 点击节点展示的模态框的确定事件 let auModalConfirmFn = null; // 登录模态框的确定事件 let jumpModalProps = null; let modalProps = {}; let modalWidth = 520; let historyInfoParams = []; let statisticalInfoParams = {}; let nodeData = null; // 选中节点的数据 let twoID = ''; const waterFlow = new WaterFlowControlView(); const ConfigurationView = (props) => { let myDiagram = null; let mqttView = null; let editionArr = []; let globalControl = false; // 入场动画 let entryAnim = ''; const guidAggre = {}; let bindData = []; const stationList = []; const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = getPrefixCls('ec-configuration-view'); const componentPrefix = getPrefixCls(''); const [isDiagram, setIsDiagram] = useState(null); const [isModalVisible, setIsModalVisible] = useState(false); const [isAuModalVisible, setIsAuModalVisible] = useState(false); // 登录模态框 const [isHIModalVisible, setIsHIModalVisible] = useState(false); // 历史曲线模态框 const [isSTHIModalVisible, setIsSTHIModalVisible] = useState(false); // 统计历史曲线模态框 const [isJumpModalVisible, setIsJumpModalVisible] = useState(false); // 画板跳转模态框 const [isControlLogVisible, setIsControlLogVisible] = useState(false); // 控制日志模态框 const [spinning, setSpinning] = useState(true); // 画板loading const [isEmpty, setIsEmpty] = useState(false); // 画板无数据状态 const [description, setDescription] = useState(''); // 画板无数据描述 const [first, setFirst] = useState(true); // 第一次加载 const [isVideoVisible, setIsVideoVisible] = useState(false); const [videoData, setVideoData] = useState([]); const [videoTitle, setVideoTitle] = useState(''); const [viewType, setViewType] = useState(false); const [currentUser, setCurrentUser] = useState(false); const [noInitial, setNoInitial] = useState(false); const [domFlag, setDomFlag] = useState(true); const [numerals, setNumerals] = useState(1); // twoID = `TDG${Date.now().toString(36)}`; const navigatorAgent = /(iPhone|iOS|Android|Windows Phone)/i.test(navigator.userAgent); const AdjustControlInput = useRef(); const AuthorFrom = useRef(); const ConfigurationRef = useRef(); const DomRef = useRef(); const TwoRef = useRef(); const loginTimer = useRef(); const bindDatas = useRef([]); const ConfigurationViewRef = useCallback((dom) => { if (DomRef) DomRef.current = !!dom; setDomFlag(!!dom); }); const customBack = props.customBack ? props.customBack : () => {}; const { devices = [], config, isZoom = false, flowShow = true, deviceName = [] } = props; let devicesCode = []; const globalConfig = window.globalConfig || config; const siteCodeStr = globalConfig?.userInfo?.LocalSite || globalConfig?.userInfo?.site || ''; let isClose = false; // 子应用包名查找 const widgets = queryProduct(globalConfig?.widgets || [], decodeURI(window.location.pathname)); const _JessibucaObj = { operateBtns: { screenshot: false, }, loadingText: '视频加载中', decoder: widgets ? `/${widgets.product}/JessibucaVideo/decoder.js` : '/JessibucaVideo/decoder.js', }; /** **********************************获取工艺图画板信息*********************** */ const getConfiguraList = async () => { const url = globalConfig.mainserver ? globalConfig.mainserver : 'https://panda-water.cn/'; imgUrl = online ? `${url}PandaMonitor/Monitor/` : `/PandaMonitor/Monitor/`; // 获取画板信息 const drawInfo = await getSketchPadList({ name: props.name, siteCode: siteCodeStr, version: '全部', _site: siteCodeStr, }); if (drawInfo.code === 0) { const data = drawInfo.data ? (drawInfo.data.list ? drawInfo.data.list : []) : []; if (data.length > 0) { const num = data.length ? (data[0].num ? data[0].num * 1 : 0) : 0; const siteInfo = data.length ? (data[0].siteInfo ? JSON.parse(data[0].siteInfo) : {}) : {}; for (let i = 0; i < num; i++) { const round = parseInt(i / 26); const remain = i % 26; if (round) { stationList.push(stationData[remain] + round); } else { stationList.push(stationData[remain]); } } const siteInfoArr = Object.getOwnPropertyNames(siteInfo); devicesCode = []; siteInfoArr.forEach((name, index) => { const deviceList = devices[index] || (siteInfo && siteInfo[name] ? siteInfo[name].Code || '' : ''); bindData.push({ code: deviceList, name, type: siteInfo && siteInfo[name] ? siteInfo[name].Type : '', }); bindDatas.current.push({ code: deviceList, name, type: siteInfo && siteInfo[name] ? siteInfo[name].Type : '', }); devicesCode.push(deviceList); }); await getPublicInfo(); getDiagramJson(data[0], siteInfo); } else { setDescription('咦~未查询到工艺图画板信息哦~'); setIsEmpty(true); setSpinning(false); return false; } } else { setDescription('咦~工艺图画板信息报错啦~'); setIsEmpty(true); setSpinning(false); return message.error(drawInfo.msg); } }; /** *********************************获取基本信息****************************** */ const getPublicInfo = async () => { // 获取点表信息 const pointInfo = devicesCode.length ? await getPointAddress({ code: devicesCode.join(','), _site: siteCodeStr, }) : null; editionArr = deepCopy(pointInfo && pointInfo.data ? pointInfo.data : [], []); // 获取数据字典组态控制权限 const dictInfoRes = await getDictionaryInfo({ level: '组态控制权限', _site: siteCodeStr, }); const dictInfo = dictInfoRes?.data || []; const dictList = dictInfo.find((item) => { return item.fieldName === '控制人员' && item.fieldValue === '当前用户'; }); globalControl = dictList ? true : false; }; /** *********************************节点展示逻辑****************************** */ const showNodeMethod = (node, list) => { const realVal = list.Value * 1; let switchState; myDiagram.model.setDataProperty(node, 'realVal', realVal); if (node.switch === '是') { switchState = openValState(node.openVal, realVal) ? '开' : '关'; myDiagram.model.setDataProperty(node, 'switchState', switchState); } if (!node.shType) return false; const patt = /[><=]/gi; let shRule = []; try { switch (node.category) { case 'svgCase': // 图片模型 shRule = ruleOperation(node, realVal); if (node.shType === '模型切换') { myDiagram.model.setDataProperty(node, 'imgSrc', shRule ? shRule.attr : node.dtImgSrc); } else if (node.shType === '显隐展示') { myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true); } break; case 'nameCase': // 名称模型 if (node.shType === '文本变化') { shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty( node, 'fontStroke', shRule ? shRule.attr : node.dtFontStroke, ); myDiagram.model.setDataProperty(node, 'text', shRule ? shRule.text : node.dtText); } else { shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty( node, 'fillColor', hexToRgba(shRule ? shRule.attr : node.fill, node.opacity), ); } break; case 'valCase': // 实时值模型 const division = node.division || false; const animType = node.animType || ''; // 动画 if (node.effect) { try { const nodes = myDiagram.findNodeForKey(node.key); const textBlocks = nodes.findObject('textBlocks'); let animation = null; switch (animType) { case 'flip': myDiagram.model.setDataProperty(node, 'flip', go.GraphObject.FlipHorizontal); // 动画还原 myTimeout(() => { myDiagram.model.setDataProperty(node, 'flip', go.GraphObject.None); }, 100); break; case 'scale': animation = new go.Animation(); animation.add(textBlocks, 'scale', 0.5, 1); animation.duration = 100; animation.start(); break; case 'opacity': animation = new go.Animation(); animation.add(textBlocks, 'opacity', 0.5, 1); animation.duration = 100; animation.start(); break; default: myDiagram.model.setDataProperty(node, 'flip', go.GraphObject.FlipHorizontal); // 动画还原 myTimeout(() => { myDiagram.model.setDataProperty(node, 'flip', go.GraphObject.None); }, 100); break; } } catch (e) {} } if (node.shType === '值显示') { myDiagram.model.setDataProperty( node, 'showVal', realVal < 0 ? 0 : division ? realVal.toLocaleString() : realVal, ); } else { myDiagram.model.setDataProperty( node, 'showVal', division ? realVal.toLocaleString() : realVal, ); } // 颜色规则 if (node.stateName) return false; shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty( node, 'fontStroke', node.realType === '在线' ? shRule ? shRule.attr : node.dtStroke : node.oflColor || '#999999', ); break; case 'waterCase': // 水池模型 const height = node.height - node.strokeWidth * 2; let waterHight = (realVal * height) / node.poolHight; waterHight = waterHight >= height ? height : waterHight; myDiagram.model.setDataProperty(node, 'waterHight', waterHight); shRule = JSON.parse(node.shRule); shRule.forEach((item) => { const min = item.min && !isNaN(item.min * 1) ? item.min * 1 : 0; const max = item.max && !isNaN(item.max * 1) ? item.max * 1 : 0; if (realVal >= min && realVal < max) myDiagram.model.setDataProperty( node, 'fillColor', hexToRgba(item.attr ? item.attr : node.fill, node.fillAlpha), ); }); break; case 'switchCase': // 开关模型 shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty( node, 'fillColor', hexToRgba(shRule ? shRule.attr : node.fill, node.opacity), ); myDiagram.model.setDataProperty(node, 'switch', shRule ? '是' : '否'); break; case 'rotateCase': // 状态模型 shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty(node, 'imgSrc', shRule ? shRule.attr : node.dtImgSrc); break; case 'pointCase': // 点状态模型 shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty( node, 'fillColor', hexToRgba(shRule ? shRule.attr : node.fill, node.opacity), ); break; case 'blenderCase': // 搅拌机模型 break; case 'HBar': // 合管模型 shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty(node, 'stroke', shRule ? shRule.attr : node.stroke); myDiagram.model.setDataProperty( node, 'waterStroke', shRule ? shRule.text : node.waterStroke, ); break; case 'speedCase': // 进度条模型 shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty( node, 'fillColor', hexToRgba(shRule ? shRule.attr : node.fill, node.opacity), ); const { width } = node; let speedWidth = (realVal * width) / node.speedWidth; speedWidth = speedWidth >= width ? width : speedWidth; myDiagram.model.setDataProperty(node, 'lineWidth', speedWidth); break; case 'modelCase': // 模板块模型 shRule = ruleOperation(node, realVal); if (node.shType === '层级展示') { myDiagram.model.setDataProperty( node, 'zOrder', shRule ? shRule.text * 1 || node.dtzOrder : node.dtzOrder, ); } else if (node.shType === '显隐展示') { myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true); } break; case 'ellipseCase': // 圆形模型 shRule = ruleOperation(node, realVal); if (node.shType === '层级展示') { myDiagram.model.setDataProperty( node, 'zOrder', shRule ? shRule.text * 1 || node.dtzOrder : node.dtzOrder, ); } else if (node.shType === '显隐展示') { myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true); } break; case 'imgCase': // 图片模型 shRule = ruleOperation(node, realVal); if (node.shType === '层级展示') { myDiagram.model.setDataProperty( node, 'zOrder', shRule ? shRule.text * 1 || node.dtzOrder : node.dtzOrder, ); } else if (node.shType === '显隐展示') { myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true); } break; case 'groupCase': // 分组模型 shRule = ruleOperation(node, realVal); if (node.shType === '显隐展示') { myDiagram.model.setDataProperty(node, 'visible', shRule ? shRule.visible : true); } break; default: break; } } catch (e) { // console.log(e); } }; /** *********************************节点状态展示逻辑****************************** */ const stateMethod = (node, list) => { const realVal = list.Value * 1; if (!node.shType) return false; let shRule = []; try { switch (node.category) { case 'valCase': // 实时值模型 // 颜色规则 shRule = ruleOperation(node, realVal); myDiagram.model.setDataProperty( node, 'fontStroke', node.realType === '在线' ? shRule ? shRule.attr : node.dtStroke : node.oflColor || '#999999', ); break; default: break; } } catch (e) { // console.log(e); } }; /** ***********************************展示规则运算********************************* */ const ruleOperation = (node, realVal) => { const patt = /[><=]/gi; const shRule = JSON.parse(node.shRule).find((rule) => { if (rule.val.toString().match(patt)) { const ruleStr = 'if(' + rule.val + '){ return true } else { return false }'; try { return new Function('x', 'X', ruleStr)(realVal, realVal); } catch (err) { return false; } } else { return rule.val.toString().split(',').indexOf(realVal.toString()) > -1; } }); return shRule; }; /** ***********************************运行值规则运算********************************* */ const openValState = (openVal, realVal) => { const patt = /[><=]/gi; if (openVal.toString().match(patt)) { const ruleStr = 'if(' + openVal + '){ return true } else { return false }'; try { return new Function('x', 'X', ruleStr)(realVal, realVal); } catch (err) { return false; } } else { return openVal.toString().split(',').indexOf(realVal.toString()) > -1; } }; /** ***********************************MQTT控制结果回调********************************* */ const controlData = (mqttDatas, code) => { const controlInfo = JSON.parse(mqttDatas); if (guidAggre[code]) { const guid = guidAggre[code]; if (!controlInfo.result) { message.warning(`${guid.tag}控制失败,${controlInfo.message}!`); } else { message.success(`${guid.tag}控制下发成功。`); } delete guidAggre[code]; } }; /** *********************************MQTT请求数据回调****************************** */ const refreshData = (mqttDatas, code) => { if (isClose || !myDiagram) return false; const bindList = bindData.find((item) => { return item.code === code; }); const name = bindList ? bindList.name : ''; const mqttData = JSON.parse(mqttDatas)[code]; const editionList = editionArr.find((item) => { return item.code === code; }); if (!mqttData) return false; const json = JSON.parse(myDiagram.model.toJson()); const jsonCopy = JSON.parse(JSON.stringify(json)); const oldJson = deepCopy(jsonCopy, {}); try { myDiagram.model.linkDataArray.forEach((item) => { if (!item.shName || item.stationName !== name || item.shType !== '线条展示') return false; mqttData.forEach((list) => { const itemID = list.ItemID; const num = itemID.lastIndexOf('.'); const ptName = itemID.substring(0, num); const shName = itemID.substring(num + 1, Infinity); if (editionList && `${editionList.name}.${editionList.version}` !== ptName) return false; if (list.Value == null || shName !== item.shName || item.realVal === list.Value) return false; item.realVal = list.Value * 1; const shRule = ruleOperation(item, item.realVal); if (shRule) { myDiagram.model.setDataProperty(item, 'stroke', shRule.attr); myDiagram.model.setDataProperty(item, 'waterStroke', shRule.text); } else { myDiagram.model.setDataProperty(item, 'stroke', item.stroke); myDiagram.model.setDataProperty(item, 'waterStroke', item.waterStroke); } }); }); } catch (e) { // 水流样式 } // 离线判断 try { onlineColorMethod(jsonCopy.nodeDataArray, name, mqttData); } catch (err) { // console.log(err) } try { jsonCopy.nodeDataArray.forEach((item) => { if (!(item.shName || item.category === 'timeCase') || item.stationName !== name) return false; const node = myDiagram.model.findNodeDataForKey(item.key); mqttData.forEach((list) => { const itemID = list.ItemID; const num = itemID.lastIndexOf('.'); const ptName = itemID.substring(0, num); const shName = itemID.substring(num + 1, Infinity); if (editionList && `${editionList.name}.${editionList.version}` !== ptName) return false; if ( (list.Value == null || shName !== item.shName || item.realVal === list.Value) && shName !== item.stateName ) return false; if ( node.category === 'timeCase' && shName === item.shName && node.shType === '更新时间' ) { myDiagram.model.setDataProperty(node, 'text', moment(list.Time).format(node.format)); myDiagram.model.setDataProperty( node, 'timeStr', moment(list.Time).format('YYYY-MM-DD HH:mm:ss'), ); return false; } if (shName === item.shName) showNodeMethod(node, list); if (shName === item.stateName) stateMethod(node, list); }); }); } catch (e) { // 节点展示 } try { const jsonModel = waterFlow.waterFlowControlByDiagramJson(oldJson, myDiagram); if (!jsonModel) return false; const oldLink = myDiagram.model.linkDataArray; const dataLink = []; jsonModel.linkDataArray.forEach((item, index) => { const list = { ...oldLink[index] }; list.isHavingDash = item.isHavingDash; dataLink.push(list); }); jsonModel.nodeDataArray.forEach((item) => { if (item.category === 'HBar') { const node = myDiagram.model.findNodeDataForKey(item.key); const waterStroke = item.typeDash ? 'transparent' : item.hBarClolor; if (item.typeDash !== node.typeDash) { myDiagram.model.setDataProperty(node, 'waterStroke', waterStroke); myDiagram.model.setDataProperty(node, 'typeDash', item.typeDash); } } }); dataLink.forEach((item) => { const node = myDiagram.findLinkForData(item); if (item.isHavingDash !== node.data.isHavingDash) myDiagram.model.setDataProperty(node.data, 'isHavingDash', item.isHavingDash); }); } catch (e) { // 水流展示 } }; /** **************************************MQTT报警数据获取****************************************** */ const alarmData = (mqttDatas, code) => { if (isClose || !myDiagram) return false; const bindList = bindData.find((item) => { return item.code === code; }); const mqttData = JSON.parse(mqttDatas); if (!mqttData) return false; const json = JSON.parse(myDiagram.model.toJson()); const jsonCopy = JSON.parse(JSON.stringify(json)); try { jsonCopy.nodeDataArray.forEach((item) => { if (item.category !== 'valCase' || !item?.alarmSwitch) return false; const node = myDiagram.model.findNodeDataForKey(item.key); if (bindList?.code !== mqttData?.Code || bindList?.name !== item.stationName) return false; if (mqttData?.SensorName !== item.shName) return false; const { RemoveAlertTime, AlarmContent, StationName } = mqttData; const alarmInfo = `${StationName}${AlarmContent}`; myDiagram.model.setDataProperty(node, 'alarmType', RemoveAlertTime ? '正常' : '报警'); myDiagram.model.setDataProperty(node, 'alarmInfo', RemoveAlertTime ? '' : alarmInfo); }); } catch (e) { // 节点展示 } }; /** **************************************是否在线数据获取****************************************** */ const onLineDataMethod = (data) => { const onlineList = data.find((list) => { const itemID = list.ItemID; const num = itemID.lastIndexOf('.'); const shName = itemID.substring(num + 1, Infinity); return shName === '是否在线'; }); return onlineList; }; /** **************************************是否在线颜色变化****************************************** */ const onlineColorMethod = (nodeDataArray, name, mqttData) => { const onlineList = onLineDataMethod(mqttData); if (!onlineList) return false; const onlineType = onlineList.Value * 1 ? '在线' : '离线'; nodeDataArray.forEach((item) => { if ( item.shName && item.stationName === name && item.category === 'valCase' && !item.stateName ) { const node = myDiagram.model.findNodeDataForKey(item.key); const shRule = node.realVal !== '--' ? ruleOperation(node, node.realVal) : null; switch (node.category) { case 'valCase': // 实时值模型 myDiagram.model.setDataProperty( node, 'fontStroke', onlineType === '在线' ? shRule ? shRule.attr : node.dtStroke : node.oflColor || '#999999', ); myDiagram.model.setDataProperty(node, 'realType', onlineType); break; default: break; } } }); }; /** **************************************合管****************************************** */ const changLinkRouting = (e) => { const link = e.subject; if (link.toNode == null || link.fromNode == null) { return false; } if (link.fromNode.category === 'HBar' || link.toNode.category === 'HBar') { e.subject.routing = go.Link.Normal; } }; /** ************************************创建连接点*********************************** */ // 创建一个port,ID为name,spot控制其怎么被连接,放置于node的什么位置,output/input决定其哪里可以from和to const makePort = (name, spot, output, input) => { // the port is basically just a small transparent square return goJS(go.Shape, 'Circle', { fill: null, // not seen, by default; set to a translucent gray by showSmallPorts, defined below stroke: null, desiredSize: new go.Size(7, 7), alignment: spot, // align the port on the main Shape alignmentFocus: spot, // just inside the Shape portId: name, // declare this object to be a "port" fromSpot: spot, toSpot: spot, // declare where links may connect at this port fromLinkable: output, toLinkable: input, // declare whether the user may draw links to/from here cursor: 'pointer', // show a different cursor to indicate potential link point }); }; /** 动画设置*********************************************** */ const animationSvg = () => { const diagram = myDiagram; const oldskips = diagram.skipsUndoManager; diagram.skipsUndoManager = true; diagram.nodes.map((node) => { const shape = node.findObject('animateSvg'); if (!shape) return false; const gpRule = JSON.parse(node.data.gpRule || '[]').concat(); const amTime = node.data.amTime || 0; if (!amTime) return false; gpRule.map((item) => { mySetInterval(() => { const { time = 0, fill = 100, scale = 1, angle = 0 } = item; myTimeout(() => { shape.opacity = (fill || 100) / 100; shape.scale = (scale || 1) * 1; shape.angle = (angle || 0) * 1; }, 0.01 * amTime * time); }, amTime * 1); }); }); diagram.skipsUndoManager = oldskips; }; // 当前时间更新 const updateTimeCase = () => { let data = []; myDiagram?.nodes?.map((list) => { const node = list?.data || {}; if (node?.category === 'timeCase' && node?.shType === '当前时间') { myDiagram.model.setDataProperty(node, 'text', moment().format(node.format)); myDiagram.model.setDataProperty(node, 'timeStr', moment().format('YYYY-MM-DD HH:mm:ss')); data.push(node || {}); } }); mySetInterval(() => { data.map((node) => { myDiagram.model.setDataProperty(node, 'text', moment().format(node.format)); myDiagram.model.setDataProperty(node, 'timeStr', moment().format('YYYY-MM-DD HH:mm:ss')); }); }, 1000); }; const myTimeout = (fn, delay) => { let timer; const stime = +new Date(); const myLoop = () => { if (isClose) return timer && cancelAnimationFrame(timer); const etime = +new Date(); if (stime + delay <= etime) { fn(); return; } timer = requestAnimationFrame(myLoop); }; timer = requestAnimationFrame(myLoop); return () => { cancelAnimationFrame(timer); }; }; const mySetInterval = (fn, interval) => { let timer; let stime = +new Date(); let etime; let myLoop = () => { etime = +new Date(); if (isClose) return timer && cancelAnimationFrame(timer); timer = requestAnimationFrame(myLoop); if (etime - stime >= interval) { stime = etime = +new Date(); fn(); } }; return requestAnimationFrame(myLoop); }; /** ******************************************水池效果****************************** */ const waterSvg = () => { const diagram = myDiagram; // poolWater = setInterval(() => { mySetInterval(() => { const oldskips = diagram.skipsUndoManager; diagram.skipsUndoManager = true; diagram.nodes.each((node) => { const shape = node.findObject('waterSvg'); if (!shape) return false; const range = (shape.range ? shape.range : 0) + 0.5; shape.range = range >= 5 ? 0 : range; shape.geometryString = `F M0 ${shape.range} L${shape.width} ${5 - shape.range} L${ shape.width } ${shape.height} L0 ${shape.height}z`; }); diagram.skipsUndoManager = oldskips; }, 100); }; /** ***********************************水流效果********************************** */ const loop = () => { const diagram = myDiagram; // tubeWater = setInterval(() => { mySetInterval(() => { const oldskips = diagram.skipsUndoManager; diagram.skipsUndoManager = true; diagram.links.each((link) => { const shape = link.findObject('PIPE'); if (!shape) return false; if (link.data.isHavingDash) { link.zOrder = 1; shape.strokeWidth = link.data.defaultWidth || 3; const off = shape.strokeDashOffset - 3; shape.strokeDashOffset = off <= 0 ? 60 : off; } else { link.zOrder = 0; shape.strokeWidth = 0; shape.strokeDashOffset = 0; } }); diagram.skipsUndoManager = oldskips; }, 60); }; /** **************************************泵状态效果*************************** */ const rotateSvg = () => { const diagram = myDiagram; // pumpType = setInterval(() => { mySetInterval(() => { const oldskips = diagram.skipsUndoManager; diagram.skipsUndoManager = true; diagram.nodes.each((node) => { const shape = node.findObject('rotateSvg'); if (!shape) return false; const _node = node.data; if (_node.switchState !== '开' || _node.realVal === '--' || _node.switch !== '是') return false; const off = shape.angle + 60; shape.angle = off <= 360 ? off : 0; }); diagram.skipsUndoManager = oldskips; }, 60); }; /** *********************************搅拌机状态效果************************* */ const blenderSvg = () => { const diagram = myDiagram; // blenderType = setInterval(() => { mySetInterval(() => { const oldskips = diagram.skipsUndoManager; diagram.skipsUndoManager = true; diagram.nodes.each((node) => { const shape = node.findObject('blenderSvg'); if (!shape) return false; const _node = node.data; const srcStr = _node.dtImgSrc.split('/').pop(); if (_node.switchState !== '开' || _node.realVal === '--' || _node.switch !== '是') { shape.source = require(`./images/组态/状态/${srcStr.replace(/[0-9]/gi, 1)}`); return false; } shape.flag = shape.flag || 1; const num = shape.source.match(/\d/)[0] * 1; let _num = 1; if (shape.flag === 1) { _num = num < 5 ? num + 1 : 4; if (num >= 5) shape.flag = 2; } else { _num = num > 1 ? num - 1 : 2; if (num <= 1) shape.flag = 1; } shape.source = require(`./images/组态/状态/${srcStr.replace(/[0-9]/gi, _num)}`); }); diagram.skipsUndoManager = oldskips; }, 100); }; /** *******************将myDiagram.model中的信息展示在画板上*********************** */ const loadDiagramProperties = (e) => { const pos = myDiagram.model.modelData.position; if (pos) myDiagram.initialPosition = go.Point.parse(pos); }; /** *******************绑定角色可见*************************** */ const roleVisibleBinding = () => { return new go.Binding('visible', '', function (data) { if (!data.roles) return true; const roles = data.roles.split(','); const curRoleMap = {}; globalConfig && globalConfig.userInfo && globalConfig.userInfo.roles && globalConfig.userInfo.roles.forEach(function (role) { curRoleMap[role.OID] = role; }); const samerole = roles.filter(function (roleID) { return !!curRoleMap[roleID]; }); return samerole.length > 0; }); }; /** ***********************************节点样式********************************** */ const nodeStyle = () => { return [ new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), { locationSpot: go.Spot.Center, }, ]; }; /** ************************************联网判断******************************* */ const onlineMethod = (pathImg, url) => { if (!first) return getConfiguraList(); const ImgObj = new Image(); ImgObj.src = pathImg; ImgObj.onload = () => { online = ImgObj.fileSize > 0 || (ImgObj.width > 0 && ImgObj.height > 0); setFirst(false); getConfiguraList(); }; ImgObj.onerror = () => { online = false; setFirst(false); getConfiguraList(); }; }; useEffect(() => { if (noInitial && domFlag) { setNumerals(numerals + 1); } }, [noInitial, domFlag]); useEffect(() => { TwoRef.current = `TDG${Date.now().toString(36)}`; if (!props.name) { setDescription('咦~工艺图配置信息不全哦~'); setIsEmpty(true); setSpinning(false); return false; } setSpinning(true); const url = globalConfig.mainserver ? globalConfig.mainserver : 'https://panda-water.cn/'; onlineMethod(`${url}civweb4/assets/images/bootPage/熊猫图标.png`, url); return () => { isClose = true; mqttView && mqttView.disSaveWaconnect(); mqttView = null; if (myDiagram) { myDiagram.removeDiagramListener('InitialAnimationStarting', animationStarting); myDiagram.removeDiagramListener('ViewportBoundsChanged', viewportBoundsChang); myDiagram.div = null; myDiagram = null; setIsDiagram(null); } }; }, [props.name, numerals]); useEffect(() => { if (!isModalVisible) { modalConfirmFn = null; modalComponent = null; modalProps = {}; } }, [isModalVisible]); useEffect(() => { if (!isAuModalVisible) { auModalConfirmFn = null; } }, [isAuModalVisible]); useEffect(() => { loginauthen(); loginTimer.current = setInterval(() => { loginauthen(); }, 1000 * 28800); return () => { clearInterval(loginTimer.current); loginTimer.current = null; }; }, []); useEffect(() => { try { const { messaged = {} } = props; if (isDiagram && Object.keys(messaged).length > 0) { const json = JSON.parse(isDiagram.model.toJson()); const jsonCopy = JSON.parse(JSON.stringify(json)); jsonCopy.nodeDataArray.forEach((item) => { if (item.category === 'groupCase' && !item.shName) { const node = isDiagram.model.findNodeDataForKey(item.key); messagedMethod(node, messaged); } }); } } catch (err) { // console.log(err); } }, [props.messaged, isDiagram]); // 信息通信 const messagedMethod = (node, messaged) => { let shRule = []; try { switch (node.category) { case 'groupCase': // 分组模型 shRule = ruleMessaged(node, messaged); if (node.shType === '显隐展示') { isDiagram.model.setDataProperty( node, 'visible', shRule ? shRule.visible : node.dtVisible, ); } break; default: break; } } catch (e) { // console.log(e); } }; // 信息通信语句解析 const ruleMessaged = (node, messaged) => { const patt = /[><=]/gi; const shRule = JSON.parse(node.shRule).find((rule) => { if (rule.val.toString().match(patt)) { const ruleStr = 'if(' + rule.val + '){ return true } else { return false }'; try { return new Function('x', 'X', ruleStr)(messaged, messaged); } catch (err) { return false; } } else { return false; } }); return shRule; }; // 图片模型翻转 const pictureFlipMethod = (type) => { let flipType = ''; switch (type) { case 'none': flipType = go.GraphObject.None; break; case 'horizontal': flipType = go.GraphObject.FlipHorizontal; break; case 'vertical': flipType = go.GraphObject.FlipVertical; break; case 'both': flipType = go.GraphObject.FlipBoth; break; default: flipType = go.GraphObject.None; break; } return flipType; }; /** ************************************获取画板JSON******************************* */ const getDiagramJson = async (list, siteInfo) => { const response = await getSketchPadContent({ dimension: list.dimension, siteCode: list.siteCode, fileName: list.deployURL.split('\\').pop(), _site: siteCodeStr, }); setSpinning(false); if (response.code === 0) { if (isClose) return false; const fromJson = response.data ? response.data : { linkFromPortIdProperty: 'fromPort', linkToPortIdProperty: 'toPort', nodeDataArray: [], linkDataArray: [], }; const twoDom = document.getElementById(TwoRef.current); if ((DomRef && !DomRef.current) || !twoDom) return setNoInitial(true); mqttView = devicesCode.length ? new MqttView({ userName: globalConfig?.mqtt_account || '', password: globalConfig?.mqtt_cyphers || '', mqttIP: globalConfig.mqtt_iotIP, mqttPath: globalConfig.mqtt_path, mqttSsl: globalConfig.mqtt_IsSSL, siteCode: globalConfig?.mqtt_mess?.site_code || '', devices: devicesCode, isAlarm: true, callback: refreshData, controlback: controlData, alarmback: alarmData, }) : null; const diagramJson = typeof fromJson === 'string' ? fromJson : JSON.stringify(fromJson); diagramRender(diagramJson, list); setNoInitial(false); try { let dataSources = 'MQTT'; const nodeDataArray = JSON.parse(diagramJson)?.nodeDataArray || []; nodeDataArray.forEach((item) => { if (item.category === 'bgCase') dataSources = item.dataSources || 'MQTT'; }); if (dataSources === 'MQTT') { mqttView && mqttView.saveWaterMqtt(); } else { getRealData(siteInfo); } } catch (e) { // 获取实时数据 getRealData(siteInfo); } } else { message.error(response.msg); } }; /** ************************************实时数据获取******************************* */ const getRealData = async (siteInfo) => { try { if (!siteInfo) return false; const accountFieldParams = []; for (let i in siteInfo) { accountFieldParams.push({ aName: siteInfo[i].Type, }); } const params = { equipmentCode: devicesCode.join(','), accountFieldParams, }; const results = await getDeviceRealInfo(params); const realData = results && results.data && results.data.list ? results.data.list : []; chartDataRender(flatten(realData)); } catch (err) {} }; // 实时数据父子级平铺 const flatten = (arr) => { return [].concat( ...arr.map((item) => item.child ? [].concat(item, ...flatten(item.child)) : [].concat(item), ), ); }; /** ************************************数据源数据获取******************************* */ const getDataModel = () => { const json = JSON.parse(myDiagram.model.toJson()); const nodeDataArray = json.nodeDataArray; const dataCaseArr = nodeDataArray.filter((item) => { return item.category === 'dataSource' && item.url; }); try { dataCaseArr.forEach((item, index) => { const time = new Date().getTime() + index; let params, data = {}; try { params = new Function('moment', (item && item.parameters) || '')(moment) || {}; } catch (err) {} try { data = new Function('moment', (item && item.body) || '')(moment) || {}; } catch (err) {} axios({ method: item.method, url: item.url, params: { ...params, time: time, }, data: data, }) .then((res) => { realValueHandle(nodeDataArray, item, getDataFilter(item, res?.data || {})); }) .catch((err) => { realValueHandle(nodeDataArray, item, null); }); }); } catch (err) {} }; /** ************************************数据源过滤器******************************* */ const getDataFilter = (item, data) => { try { const script = item && item.filter ? item.filter : ''; if (!script) return data; const list = new Function('data', script)(data); return list; } catch (err) { return null; } }; /** ************************************源数据模型处理******************************* */ const realValueHandle = (nodeDataArray, list, data) => { nodeDataArray.forEach((item) => { if (item.category !== 'dataCase' || item.dataSource !== list.sname || !item.shType) return false; try { const script = item && item.filter ? item.filter : ''; const value = new Function('data', script)(data); const node = myDiagram.model.findNodeDataForKey(item.key); myDiagram.model.setDataProperty( node, 'showVal', typeof value !== 'undefined' ? value : '--', ); } catch (err) {} }); }; /** ************************************图表数据处理******************************* */ const chartDataRender = (mqttData) => { const json = JSON.parse(myDiagram.model.toJson()); const jsonCopy = JSON.parse(JSON.stringify(json)); const oldJson = deepCopy(jsonCopy); try { jsonCopy.linkDataArray.forEach((item) => { if (!item.shName || item.shType !== '线条展示') return false; mqttData.forEach((list) => { const bindList = bindData.find((arr) => { return arr.code === list.code; }); const pvList = list.dataList.find((arr) => { return arr.dName === item.shName; }); if (!bindList || !pvList) return false; if (item.stationName !== bindList.name) return false; if (pvList.pv === null || pvList.dName !== item.shName || item.realVal === pvList.pv) return false; item.realVal = pvList.pv * 1; const shRule = ruleOperation(item, item.realVal); if (shRule) { myDiagram.model.setDataProperty(item, 'stroke', shRule.attr); myDiagram.model.setDataProperty(item, 'waterStroke', shRule.text); } else { myDiagram.model.setDataProperty(item, 'stroke', item.stroke); myDiagram.model.setDataProperty(item, 'waterStroke', item.waterStroke); } }); }); } catch (e) { // 水流样式 } try { jsonCopy.nodeDataArray.forEach((item) => { if (!(item.shName || item.category === 'timeCase')) return false; const node = myDiagram.model.findNodeDataForKey(item.key); mqttData.forEach((list) => { const bindList = bindData.find((arr) => { return arr.code === list.code; }); const pvList = list.dataList.find((arr) => { return arr.dName === item.shName; }); if (!bindList || !pvList) return false; if (item.stationName !== bindList.name) return false; if ( node.category === 'timeCase' && item.stationName === bindList.name && item?.shType === '更新时间' ) { myDiagram.model.setDataProperty(node, 'text', moment(list.pt).format(node.format)); myDiagram.model.setDataProperty( node, 'timeStr', moment(list.pt).format('YYYY-MM-DD HH:mm:ss'), ); return false; } if ( (pvList.pv === null || pvList.dName !== item.shName || item.realVal === pvList.pv) && pvList.dName !== item.stateName ) return false; pvList.Value = pvList.pv; if (pvList.dName === item.shName) showNodeMethod(node, pvList); if (pvList.dName === item.stateName) stateMethod(node, list); }); }); } catch (e) { // 节点展示 } try { const jsonModel = waterFlow.waterFlowControlByDiagramJson(oldJson, myDiagram); if (!jsonModel) return false; const oldLink = myDiagram.model.linkDataArray; const dataLink = []; jsonModel.linkDataArray.forEach((item, index) => { const list = Object.assign({}, oldLink[index]); list.isHavingDash = item.isHavingDash; dataLink.push(list); }); jsonModel.nodeDataArray.forEach((item) => { if (item.category == 'HBar') { const node = myDiagram.model.findNodeDataForKey(item.key); const waterStroke = item.typeDash ? 'transparent' : item.hBarClolor; if (item.typeDash != node.typeDash) { myDiagram.model.setDataProperty(node, 'waterStroke', waterStroke); myDiagram.model.setDataProperty(node, 'typeDash', item.typeDash); } } }); dataLink.forEach((item) => { const node = myDiagram.findLinkForData(item); if (item.isHavingDash != node.data.isHavingDash) myDiagram.model.setDataProperty(node.data, 'isHavingDash', item.isHavingDash); }); } catch (e) { // 水流展示 } }; /** **************************************历史模态渲染****************************************** */ const historyModalRender = (data, list) => { if (!data.shName) return false; historyInfoParams = [ { deviceCode: list.code, sensors: data.shName, deviceType: list.type, }, ]; setIsHIModalVisible(true); }; /** **************************************统计历史模态渲染****************************************** */ const statisticalModalRender = (data, list) => { const opRule = JSON.parse(data.opRule); const stType = opRule && opRule.stType ? opRule.stType : ''; if (!data.shName) return false; statisticalInfoParams = { deviceCode: list.code, sensors: data.shName, deviceType: list.type, statisticType: stType, }; setIsSTHIModalVisible(true); }; /** **************************************渲染按钮控制****************************************** */ const renderSwitchControlModal = () => { return ( <div className={classNames('switchControlContent')}> <ExclamationCircleOutlined /> {`确定要${nodeData.text}${nodeData.ctName}?`} </div> ); }; /** **************************************渲染输入控制****************************************** */ const renderAdjustControlModal = () => { const ctRule = nodeData.ctRule ? JSON.parse(nodeData.ctRule) : []; const step = ctRule.length ? ctRule[0].step || 0.1 : 0.1; return ( <div className={classNames('adjustControlContent')}> <div className={classNames('label')}>设置</div> <Input placeholder="请输入设置值" ref={AdjustControlInput} defaultValue={nodeData.realVal} onChange={() => {}} suffix={ctRule.length ? ctRule[0].text : ''} /> <div className={classNames('adjustControlGroup')}> <Button type="primary" ghost={true} icon={<DoubleLeftOutlined />} onClick={() => adjustControlStep(step * -10)} /> <Button type="primary" ghost={true} icon={<LeftOutlined />} onClick={() => adjustControlStep(step * -1)} /> <Button type="primary" ghost={true} icon={<RightOutlined />} onClick={() => adjustControlStep(step)} /> <Button type="primary" ghost={true} icon={<DoubleRightOutlined />} onClick={() => adjustControlStep(step * 10)} /> </div> </div> ); }; const adjustControlStep = (step) => { const value = AdjustControlInput.current.state ? AdjustControlInput.current.state.value : AdjustControlInput.current.input.value; if (!isNaN(value * 1)) { if (AdjustControlInput.current.state) { AdjustControlInput.current.setState({ value: addNumMethod(value * 1, step * 1) }); } else { AdjustControlInput.current.input.value = addNumMethod(value * 1, step * 1); } } }; /** **************************************权限控制确认****************************************** */ const defineAutho = (code, tag, node, guid, value, type) => { const { userName, password } = AuthorFrom.current.getFieldsValue(true); if (!userName || !password) { message.warning('用户名或密码不能为空!'); return false; } setIsAuModalVisible(false); guidAggre[guid] = { tag, code, }; const flag = isNumber(value); mqttView && mqttView.onSendWaMessageArrived( userName, encipher(password).toUpperCase(), guid, code, tag, value, type === '输入控制' ? value : flag ? value * 1 : '', ); }; /** **************************************权限控制方法****************************************** */ const authoMethod = async (code, tag, node, guid, value, type) => { const { people = '' } = node; const userName = people === '当前用户' || globalControl ? globalConfig?.userInfo?.loginName || '' : ''; setCurrentUser(userName); setIsAuModalVisible(true); auModalConfirmFn = () => defineAutho(code, tag, node, guid, value, type); }; // 查看密码 const viewTypeChange = (e) => { setViewType(!viewType); }; /** **************************************跳转权限方法****************************************** */ const jumpAuthoMethod = async (people) => { const userName = people === '当前用户' || globalControl ? globalConfig?.userInfo?.loginName || '' : ''; setCurrentUser(userName); setIsAuModalVisible(true); auModalConfirmFn = () => jumpAuthoConfirm(); }; // 跳转权限确认 const jumpAuthoConfirm = () => { const { userName, password } = AuthorFrom.current.getFieldsValue(true); if (!userName || !password) { message.warning('用户名或密码不能为空!'); return false; } authorizationToken({ loginName: userName, password: encipher(password).toUpperCase(), type: 'password', }) .then((res) => { if (res.code === 0) { setIsAuModalVisible(false); setIsJumpModalVisible(true); } else { message.warning(res?.msg || '权限认证失败,请重试!'); } }) .catch((err) => { message.warning('权限认证失败,请重试!'); }); }; /** **************************************开关控制确定****************************************** */ const defineSwitch = (code, tag, node) => { const guid = createGuid(); setIsModalVisible(false); const ctRule = JSON.parse(node.ctRule); if (node.isControl === '是') { authoMethod(code, tag, node, guid, ctRule[0].val); return false; } guidAggre[guid] = { tag, code }; const { val } = ctRule[0]; const flag = isNumber(val); mqttView && mqttView.onSendWaMessageArrived( globalConfig.token || '', '', guid, code, tag, val, flag ? val * 1 : '', ); }; /** **************************************控制方法****************************************** */ const controlMethod = (code, tag, node) => { const ctRule = JSON.parse(node.ctRule); const min = ctRule.length ? ctRule[0].min : ''; const max = ctRule.length ? ctRule[0].max : ''; const hexfrom = ctRule.length ? ctRule[0].hexfrom : ''; const hexto = ctRule.length ? ctRule[0].hexto : ''; let value = AdjustControlInput.current.state ? AdjustControlInput.current.state.value : AdjustControlInput.current.input.value; if (value != 0 && !value) { message.warning('设置值不能为空!'); return false; } if (isNaN(value * 1)) { message.warning('设置值不合理!'); return false; } if (value < 0) { message.warning('设置值不合理!'); return false; } if (min !== '' && !isNaN(min * 1) && value < min * 1) { message.warning(`设置值不能小于${min}!`); return false; } if (max !== '' && !isNaN(max * 1) && value > max * 1) { message.warning(`设置值不能大于${max}!`); return false; } if (hexfrom && hexto) value = hexSwitch(value, hexfrom, hexto); const guid = createGuid(); setIsModalVisible(false); if (node.isControl === '是') { authoMethod(code, tag, node, guid, isNumber(value * 1) ? value * 1 : value, '输入控制'); return false; } guidAggre[guid] = { tag, code }; mqttView && mqttView.onSendWaMessageArrived( globalConfig.token || '', '', guid, code, tag, node.switchType, isNumber(value * 1) ? value * 1 : value, ); }; const moreControlMethod = (code, tag, node, value) => { const guid = createGuid(); setIsModalVisible(false); if (node.isControl === '是') { authoMethod(code, tag, node, guid, value); return false; } guidAggre[guid] = { tag, code }; const flag = isNumber(value); mqttView && mqttView.onSendWaMessageArrived( globalConfig.token || '', '', guid, code, tag, value, flag ? value * 1 : '', ); }; /** **************************************渲染多选控制****************************************** */ const renderMoreControlModal = () => { const ctRule = nodeData.ctRule ? JSON.parse(nodeData.ctRule) : []; const list = bindData.find((item) => { return item.name === nodeData.stationName; }); return ( <div className={classNames('moreControlContent')}> <div className={classNames('moreControlContainer')}> {ctRule.length > 0 && ctRule.map((rule, index) => { if (rule.val !== '' && rule.text !== '') { return ( <div key={index} className={classNames('moreControlList')}> <span>状态设置</span> <div onClick={(e) => moreControlMethod(list.code, nodeData.ctName, nodeData, rule.val) } > {rule.text} </div> </div> ); } })} </div> </div> ); }; /** **************************************控制模态渲染****************************************** */ const controlModalRender = (data, list) => { const ctRule = data && data.ctRule ? JSON.parse(data.ctRule) : ''; const ctLog = data && data.ctLog ? data.ctLog : false; switch (data.ctType) { case '按钮控制': if (data.switch === '是') message.warning(`当前设备已是${data.text}状态,请勿重复操作!`); if ( data.realVal === '--' || data.switch === '是' || !data.ctName || !data.ctName.length || ctRule[0].val === '' || !list?.code ) return false; modalComponent = renderSwitchControlModal; modalConfirmFn = () => defineSwitch(list.code, data.ctName, data); modalProps = { title: '状态控制', footer: renderControlModalFooter(ctLog) }; modalWidth = 520; setIsModalVisible(true); break; case '输入控制': if (data.realVal === '--' || !data.ctName || !list?.code) return false; modalComponent = renderAdjustControlModal; modalConfirmFn = () => controlMethod(list.code, data.ctName, data); modalProps = { title: `${data.ctName}设置`, footer: renderControlModalFooter(ctLog) }; modalWidth = 520; setIsModalVisible(true); break; case '多选控制': if (!data.ctName || !list?.code) return false; modalComponent = renderMoreControlModal; modalProps = { footer: renderMoreControlFooter(ctLog), title: `${data.ctName}设置` }; modalWidth = 'auto'; setIsModalVisible(true); break; default: break; } }; // 自定义多选控制footer const renderMoreControlFooter = (ctLog) => { return ctLog ? ( <div className={classNames('controlModallLog')} onClick={handleLog}> <img src={require('./images/组态/日志.svg')} /> 控制日志 </div> ) : null; }; // 自定义控制footer const renderControlModalFooter = (ctLog) => { return ( <div className={classNames('controlModalFooter')}> {ctLog ? ( <div className={classNames('controlModallLog')} onClick={handleLog}> <img src={require('./images/组态/日志.svg')} /> 控制日志 </div> ) : ( <div></div> )} <div className={classNames('controlModalOperate')}> <Button onClick={handleCancel}>关闭</Button> <Button type="primary" onClick={handleOk}> 确认 </Button> </div> </div> ); }; // 控制日志内容渲染 const renderControlLogContent = () => { const list = bindDatas.current.find((item) => { return item.name === nodeData.stationName; }); return list ? ( <ControlRecords nodeData={nodeData} bindList={list} /> ) : ( <div className={classNames('controlNotLog')}> <Empty description={'控制日志信息不全,请先配置相关指标信息!'} /> </div> ); }; /** **************************************视频查看****************************************** */ const videoScanMethod = async (data) => { try { const opRule = JSON.parse(data.opRule); const bindList = bindData.find((item) => { return item.name === data.stationName; }); const { vdCode = bindList.code, vdList = '', vdName = '', vdType = '' } = opRule; if (!vdCode) return setVideoData([]); if (vdType && !vdList) return setVideoData([]); const detail = await getVideoDetail({ UserID: 1, DeviceCode: vdCode, _site: siteCodeStr, }); const dataList = detail?.data; if (!dataList) return setVideoData([]); const detailList = []; if (vdType) { const videoList = vdList.split(','); videoList.forEach((item, index) => { const list = dataList.find((obj) => { return item === obj.vmS_DeviceID; }); if (list) detailList.push({ id: list.vmS_DeviceID, name: list.channelName, protocol: list.protocol, username: list.username, password: list.password, dataRate: 'Sub', pandavmsHost: getVideoUrl(), gateway: true, address: list.address, channel: list.channelID * 1, }); }); } else { dataList.forEach((list) => { detailList.push({ id: list.vmS_DeviceID, name: list.channelName, protocol: list.protocol, username: list.username, password: list.password, dataRate: 'Sub', pandavmsHost: getVideoUrl(), gateway: true, address: list.address, channel: list.channelID * 1, }); }); } setVideoTitle(vdName + '视频查看'); setVideoData(detailList); detailList.length && setIsVideoVisible(true); } catch (e) { // console.log(e) } }; /** **************************************交互脚本****************************************** */ const interactiveScript = (data) => { try { const opRule = JSON.parse(data.opRule); const script = opRule && opRule.script ? opRule.script : ''; new Function('diagram', 'list', 'props', script)(myDiagram, data, props); } catch (e) { // console.log(e) } }; /** **************************************跳转方法****************************************** */ const menuJumpMethod = (data) => { const opRule = JSON.parse(data.opRule); const widget = opRule && opRule.widget ? opRule.widget : ''; const params = opRule && opRule.params ? (isJson(opRule.params) && JSON.parse(opRule.params)) || {} : {}; const list = querySkipUrl(globalConfig?.widgets || [], widget); if (!list || !widget) return false; window.history.pushState(params, null, `/civbase/${list.product || 'civweb4'}/${list.url}`); }; /** **************************************跳转方法****************************************** */ const drawBoardMethod = (data) => { const opRule = JSON.parse(data.opRule); const name = opRule && opRule.name ? opRule.name : ''; const title = opRule && opRule.title ? opRule.title : ''; const device = opRule && opRule.device ? opRule.device : []; const width = opRule && opRule.width ? opRule.width : ''; const height = opRule && opRule.height ? opRule.height : ''; const color = opRule && opRule.color ? opRule.color : '#282d3b'; const auth = opRule && opRule.auth ? opRule.auth : false; const people = opRule && opRule.people ? opRule.people : ''; if (!name) return false; const deviceArr = []; device.forEach((item) => { const list = bindData.find((item1) => { return item1.name === item; }); deviceArr.push(list ? list.code : item); }); jumpModalProps = { width, height, title, name, device: deviceArr, color, }; if (auth) return jumpAuthoMethod(people); setIsJumpModalVisible(true); }; const handleLog = (e) => { e.stopPropagation(); setIsControlLogVisible(true); }; const handleOk = (e) => { e.stopPropagation(); modalConfirmFn && modalConfirmFn(); }; const handleCancel = () => { setIsModalVisible(false); }; const handleAuOk = (e) => { e.stopPropagation(); auModalConfirmFn && auModalConfirmFn(); }; const renderModalContent = () => { return modalComponent && nodeData ? modalComponent() : null; }; // 监听初始化动画渲染动画 const animationStarting = (e) => { const animations = e.subject.defaultAnimation; if (entryAnim === 'opacity') animations.add(e.diagram, 'opacity', 0, 1); }; // 监听画布zoom变化 const viewportBoundsChang = () => { myDiagram.contentAlignment = go.Spot.Default; myDiagram.contentAlignment = go.Spot.Center; }; /** **********************************画布渲染************************************ */ const diagramRender = (jsonStr, chartInfo) => { myDiagram = goJS( go.Diagram, TwoRef.current, // must name or refer to the DIV HTML element { initialContentAlignment: go.Spot.Center, contentAlignment: go.Spot.Center, allowDrop: false, // must be true to accept drops from the Palette 右边的面板允许防止图形 draggingTool: new GuidedDraggingTool(), allowZoom: isZoom ? true : false, allowSelect: false, 'draggingTool.dragsLink': true, isReadOnly: true, autoScale: isZoom ? go.Diagram.None : go.Diagram.Uniform, // 自适应,默认不自适应 initialAutoScale: go.Diagram.Uniform, // 自适应,默认不自适应 'draggingTool.isGridSnapEnabled': true, 'linkingTool.isUnconnectedLinkValid': true, 'animationManager.duration': 100, allowHorizontalScroll: isZoom ? true : false, // padding: 20, allowVerticalScroll: isZoom ? true : false, 'linkingTool.portGravity': 20, 'relinkingTool.isUnconnectedLinkValid': true, 'relinkingTool.portGravity': 20, 'draggingTool.horizontalGuidelineColor': 'blue', 'draggingTool.verticalGuidelineColor': 'blue', 'draggingTool.centerGuidelineColor': 'green', rotatingTool: goJS(TopRotatingTool), // defined below 'rotatingTool.snapAngleMultiple': 15, 'rotatingTool.snapAngleEpsilon': 15, 'undoManager.isEnabled': true, LinkDrawn: changLinkRouting, // LinkReshaped: (e) => { // e.subject.routing = go.Link.Orthogonal; // }, 'toolManager.hoverDelay': 100, 'linkingTool.direction': go.LinkingTool.ForwardsOnly, }, ); /** **********************************分组模型************************************* */ myDiagram.groupTemplate = ('groupCase', goJS( go.Group, 'Auto', { ungroupable: true, zOrder: 1, visible: true }, { // 设置其可选择 selectable: false, layerName: 'Background', }, new go.Binding('visible', 'visible').makeTwoWay(), goJS( go.Shape, 'RoundedRectangle', // surrounds everything { parameter1: 10, fill: 'transparent', strokeWidth: 0, stroke: 'transparent', }, ), goJS( go.Panel, 'Auto', // position header above the subgraph goJS( go.Placeholder, // represents area for all member parts { background: 'transparent' }, ), ), )); // 自定义矩形 go.Shape.defineFigureGenerator('RoundedRectanglePlus', (shape, w, h) => { // this figure takes one parameter, the size of the corner let p1 = Infinity; // default corner size if (shape !== null) { const param1 = shape.parameter1; if (!isNaN(param1) && param1 >= 0) p1 = param1; // can't be negative or NaN } p1 = Math.min(p1, w / 2); p1 = Math.min(p1, h / 2); // limit by whole height or by half height? const geo = new go.Geometry(); // a single figure consisting of straight lines and quarter-circle arcs geo.add( new go.PathFigure(0, p1) .add(new go.PathSegment(go.PathSegment.Arc, 180, 90, p1, p1, p1, p1)) .add(new go.PathSegment(go.PathSegment.Line, w - p1, 0)) .add(new go.PathSegment(go.PathSegment.Arc, 270, 90, w - p1, p1, p1, p1)) .add(new go.PathSegment(go.PathSegment.Arc, 0, 90, w - p1, h - p1, p1, p1)) .add(new go.PathSegment(go.PathSegment.Arc, 90, 90, p1, h - p1, p1, p1).close()), ); // don't intersect with two top corners when used in an "Auto" Panel geo.spot1 = new go.Spot(0, 0, 0.3 * p1, 0.3 * p1); geo.spot2 = new go.Spot(1, 1, -0.3 * p1, 0); return geo; }); /** *********************************节点模板************************************* */ // 背景模板定义 myDiagram.nodeTemplateMap.add( 'bgCase', goJS( go.Node, 'Spot', { locationSpot: go.Spot.Center, zOrder: 0 }, // new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('visible', 'visible').makeTwoWay(), new go.Binding('angle').makeTwoWay(), { // 设置其可选择 selectable: false, layerName: 'Background', }, // the main object is a Panel that surrounds a TextBlock with a Shape ~图形:Panel包围着TextBlock goJS( go.Panel, 'Auto', { name: 'PANEL', }, new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), goJS( go.Picture, { width: 56, height: 56, scale: 1, source: '', background: '#2e3343' }, new go.Binding('source', 'imgSrc', function (v) { const own = myDiagram ? myDiagram.model.findNodeDataForKey('bgCase') : {}; props.bgMethod && props.bgMethod(own); return v ? `/PandaMonitor/Monitor/SketchPad/PreviewResource?name=${v}&_site=${siteCodeStr}` : ''; }).makeTwoWay(), new go.Binding('scale', 'scale').makeTwoWay(), new go.Binding('width', 'width').makeTwoWay(), new go.Binding('height', 'height').makeTwoWay(), new go.Binding('background', 'background').makeTwoWay(), ), ), ), ); // 表格节点定义 myDiagram.nodeTemplateMap.add( 'tableCase', goJS( go.Node, 'Auto', { locationSpot: go.Spot.Center, zOrder: 1 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), goJS( go.Shape, { fill: 'white', strokeWidth: 1, stroke: '#808080', }, new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), ), goJS( go.Panel, 'Table', { padding: 0, defaultRowSeparatorStroke: '#808080', defaultColumnSeparatorStroke: '#808080', defaultRowSeparatorStrokeWidth: 1, defaultColumnSeparatorStrokeWidth: 1, background: '#ffffff', }, new go.Binding('background', 'fillColor').makeTwoWay(), new go.Binding('defaultRowSeparatorStroke', 'stroke').makeTwoWay(), new go.Binding('defaultRowSeparatorStrokeWidth', 'strokeWidth').makeTwoWay(), new go.Binding('defaultColumnSeparatorStroke', 'stroke').makeTwoWay(), new go.Binding('defaultColumnSeparatorStrokeWidth', 'strokeWidth').makeTwoWay(), new go.Binding('itemArray', 'content').makeTwoWay(), { // 表内容 defaultAlignment: go.Spot.Left, itemTemplate: goJS( go.Panel, 'TableRow', new go.Binding('itemArray', 'columns').makeTwoWay(), { itemTemplate: goJS( go.Panel, // each of which as "attr" and "text" properties 'Spot', { background: 'transparent', alignment: go.Spot.Center }, new go.Binding('column').makeTwoWay(), new go.Binding('columnSpan', 'cSpan').makeTwoWay(), new go.Binding('rowSpan', 'rSpan').makeTwoWay(), new go.Binding('padding', 'padding', function (v) { if (v && isNumber(v)) return v; const padding = v ? v.split(',') : null; return padding ? new go.Margin( padding?.[0] * 1 || 0, padding?.[1] * 1 || 0, padding?.[2] * 1 || 0, padding?.[3] * 1 || 0, ) : 0; }).makeTwoWay(), goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', fill: 'transparent', stroke: '#ffffff', strokeWidth: 0, parameter1: 0, }, new go.Binding('fill', 'background').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('stroke', 'bdColor').makeTwoWay(), new go.Binding('strokeWidth', 'bdWidth').makeTwoWay(), new go.Binding('width').makeTwoWay(), new go.Binding('height').makeTwoWay(), ), goJS( go.TextBlock, textStyle(), { editable: true }, { // margin: new go.Margin(2, 10, 10, 2), wrap: go.TextBlock.WrapFit, textAlign: 'center', font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', }, new go.Binding('text').makeTwoWay(), new go.Binding('font', 'style').makeTwoWay(), new go.Binding('stroke', 'color').makeTwoWay(), new go.Binding('textAlign', 'align').makeTwoWay(), new go.Binding('maxSize', 'width', function (v) { try { return new go.Size(v - 20, NaN); } catch (err) { return new go.Size(NaN, NaN); } }).makeTwoWay(), new go.Binding('minSize', 'width', function (v) { try { return new go.Size(v - 20, NaN); } catch (err) { return new go.Size(NaN, NaN); } }).makeTwoWay(), ), ), }, ), }, ), ), ); // img节点定义 myDiagram.nodeTemplateMap.add( 'imgCase', goJS( go.Node, 'Spot', { locationSpot: go.Spot.Center, zOrder: 1, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 // the main object is a Panel that surrounds a TextBlock with a Shape ~图形:Panel包围着TextBlock goJS( go.Panel, 'Auto', { name: 'PANEL', }, new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), new go.Binding('visible', 'visible').makeTwoWay(), goJS( go.Picture, { name: 'animateSvg', width: 56, height: 56, column: 0, scale: 1, source: require('./images/组态/默认.png'), flip: go.GraphObject.None, }, new go.Binding('source', 'imgSrc', function (v) { return !v ? require('./images/组态/默认.png') : `/PandaMonitor/Monitor/SketchPad/PreviewResource?name=${v}&_site=${siteCodeStr}`; }).makeTwoWay(), new go.Binding('flip', 'flip', pictureFlipMethod), new go.Binding('scale', 'scale').makeTwoWay(), new go.Binding('width', 'width').makeTwoWay(), new go.Binding('height', 'height').makeTwoWay(), ), ), { click(e, node) { if (navigatorAgent) return false; const { data } = node; nodeData = data; const list = bindData.find((item) => { return item.name === data.stationName; }); if (!list) return false; // 画板跳转 switch (data.opType) { case '画板跳转': // 图片模型 drawBoardMethod(data); break; case '功能跳转': // 功能模型 menuJumpMethod(data); break; case '控制日志': // 控制日志 if (list.code && nodeData.ctName) setIsControlLogVisible(true); break; case '视频查看': // 视频查看 videoScanMethod(data); break; case '自定义交互': // 自定义交互 customBack(data); interactiveScript(data); break; default: break; } }, }, ), ); // svg节点定义 myDiagram.nodeTemplateMap.add( 'svgCase', goJS( go.Node, 'Spot', { locationSpot: go.Spot.Center, zOrder: 1, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Panel, 'Auto', { name: 'PANEL', }, new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), new go.Binding('visible', 'visible').makeTwoWay(), goJS( go.Picture, { name: 'animateSvg', width: 56, height: 56, column: 0, scale: 1, source: '', flip: go.GraphObject.None, }, new go.Binding('source', 'imgSrc', (v) => { return `${imgUrl}Model/Preview/${encodeURIComponent(v)}`; }), new go.Binding('scale', 'scale').makeTwoWay(), // new go.Binding('width', 'width').makeTwoWay(), // new go.Binding('height', 'height').makeTwoWay(), new go.Binding('width', 'width', function (v) { return (v * 1).toFixed(1) * 1; }).makeTwoWay(), new go.Binding('height', 'height', function (v) { return (v * 1).toFixed(1) * 1; }).makeTwoWay(), new go.Binding('flip', 'flip', pictureFlipMethod), ), ), { click(e, node) { if (navigatorAgent) return false; const { data } = node; nodeData = data; const list = bindData.find((item) => { return item.name === data.stationName; }); if (!list) return false; // 控制方法 if (data.ctName && data.ctType) { controlModalRender(data, list); return false; } // 画板跳转 switch (data.opType) { case '画板跳转': // 图片模型 drawBoardMethod(data); break; case '功能跳转': // 功能模型 menuJumpMethod(data); break; case '控制日志': // 控制日志 if (list.code && nodeData.ctName) setIsControlLogVisible(true); break; case '视频查看': // 视频查看 videoScanMethod(data); break; case '自定义交互': // 自定义交互 customBack(data); interactiveScript(data); break; default: break; } }, }, ), ); // 模板块定义 myDiagram.nodeTemplateMap.add( 'modelCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 1 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), { // 设置其可改变大小 resizeObjectName: 'SHAPE', }, new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', fill: 'rgba(128,128,128,0.2)', stroke: 'gray', parameter1: 0 }, new go.Binding('visible', 'visible').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), ), ); // 圆形定义 myDiagram.nodeTemplateMap.add( 'ellipseCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 1 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), { // 设置其可改变大小 resizeObjectName: 'SHAPE', }, new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'Ellipse', { name: 'SHAPE', fill: 'rgba(128,128,128,0.2)', stroke: 'gray' }, new go.Binding('visible', 'visible').makeTwoWay(), new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), ), ); // 设备名称定义 myDiagram.nodeTemplateMap.add( 'deviceCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 3, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', strokeWidth: 10, stroke: '#000000', parameter1: 0 }, new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), goJS( go.TextBlock, textStyle(), { // margin: 5, maxSize: new go.Size(NaN, NaN), minSize: new go.Size(NaN, 1), wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: true, font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', }, new go.Binding('text').makeTwoWay(), new go.Binding('font', 'fontStyle'), new go.Binding('stroke', 'fontStroke').makeTwoWay(), new go.Binding('textAlign', 'fontAlign'), new go.Binding('maxSize', 'textSize'), new go.Binding('minSize', 'textSize'), ), { click(e, node) { if (navigatorAgent) return false; const { data } = node; nodeData = data; const list = bindData.find((item) => { return item.name === data.stationName; }); if (!list) return false; switch (data.opType) { case '画板跳转': // 图片模型 drawBoardMethod(data); break; case '功能跳转': // 功能模型 menuJumpMethod(data); break; case '视频查看': // 视频查看 videoScanMethod(data); break; case '自定义交互': // 自定义交互 customBack(data); interactiveScript(data); break; default: break; } }, }, ), ); // 数据源模型定义 myDiagram.nodeTemplateMap.add( 'dataSource', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 3, cursor: 'default', visible: false }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', strokeWidth: 10, stroke: '#000000', parameter1: 0 }, new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), goJS( go.TextBlock, textStyle(), { maxSize: new go.Size(NaN, NaN), minSize: new go.Size(NaN, 1), wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: true, font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', }, new go.Binding('text').makeTwoWay(), new go.Binding('font', 'fontStyle'), new go.Binding('stroke', 'fontStroke').makeTwoWay(), new go.Binding('textAlign', 'fontAlign'), new go.Binding('maxSize', 'textSize'), new go.Binding('minSize', 'textSize'), ), ), ); // 源数据模型定义 myDiagram.nodeTemplateMap.add( 'dataCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 3, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', strokeWidth: 10, stroke: '#000000', parameter1: 0 }, new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), goJS( go.TextBlock, textStyle(), { // margin: 5, maxSize: new go.Size(NaN, NaN), minSize: new go.Size(NaN, 1), wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: true, font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', }, new go.Binding('text', 'showVal').makeTwoWay(), new go.Binding('font', 'fontStyle'), new go.Binding('stroke', 'fontStroke').makeTwoWay(), new go.Binding('textAlign', 'fontAlign'), new go.Binding('maxSize', 'textSize'), new go.Binding('minSize', 'textSize'), ), { click(e, node) { if (navigatorAgent) return false; const { data } = node; nodeData = data; const list = bindData.find((item) => { return item.name === data.stationName; }); if (!list) return false; switch (data.opType) { case '画板跳转': // 图片模型 drawBoardMethod(data); break; case '功能跳转': // 功能模型 menuJumpMethod(data); break; case '视频查看': // 视频查看 videoScanMethod(data); break; case '自定义交互': // 自定义交互 customBack(data); interactiveScript(data); break; default: break; } }, }, ), ); // 名称定义 myDiagram.nodeTemplateMap.add( 'nameCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 3, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', strokeWidth: 10, stroke: '#000000', parameter1: 0 }, new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), goJS( go.TextBlock, textStyle(), { // margin: 5, maxSize: new go.Size(NaN, NaN), minSize: new go.Size(NaN, 1), wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: true, font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', }, new go.Binding('text').makeTwoWay(), new go.Binding('font', 'fontStyle'), new go.Binding('stroke', 'fontStroke').makeTwoWay(), new go.Binding('textAlign', 'fontAlign'), new go.Binding('maxSize', 'textSize'), new go.Binding('minSize', 'textSize'), ), { click(e, node) { if (navigatorAgent) return false; const { data } = node; nodeData = data; const list = bindData.find((item) => { return item.name === data.stationName; }); if (!list) return false; // 控制方法 if (data.ctName && data.ctType) { controlModalRender(data, list); return false; } switch (data.opType) { case '画板跳转': // 图片模型 drawBoardMethod(data); break; case '功能跳转': // 功能模型 menuJumpMethod(data); break; case '控制日志': // 控制日志 if (list.code && nodeData.ctName) setIsControlLogVisible(true); break; case '视频查看': // 视频查看 videoScanMethod(data); break; case '自定义交互': // 自定义交互 customBack(data); interactiveScript(data); break; default: break; } }, }, ), ); // 更新时间定义 myDiagram.nodeTemplateMap.add( 'timeCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 3, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', strokeWidth: 10, stroke: '#000000', parameter1: 0 }, new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), goJS( go.TextBlock, textStyle(), { // margin: 5, maxSize: new go.Size(NaN, NaN), minSize: new go.Size(NaN, 1), wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: true, font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', }, new go.Binding('text').makeTwoWay(), new go.Binding('font', 'fontStyle'), new go.Binding('stroke', 'fontStroke').makeTwoWay(), new go.Binding('textAlign', 'fontAlign'), new go.Binding('maxSize', 'textSize'), new go.Binding('minSize', 'textSize'), ), { // define a tooltip for each node that displays the color as text toolTip: goJS( 'ToolTip', goJS( go.TextBlock, { margin: 2 }, new go.Binding('text', 'timeStr'), new go.Binding('visible', 'toolTip'), ), new go.Binding('visible', 'toolTip'), ), }, ), ); // 公用管定义 myDiagram.nodeTemplateMap.add( 'HBar', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 1 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), { // 设置其可改变大小 resizable: false, layerName: 'Background', }, roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'Rectangle', { name: 'SHAPE', height: 0, width: 120, fill: '#41BFEC', stroke: null, strokeWidth: 0, minSize: new go.Size(20, 0), maxSize: new go.Size(Infinity, 0), }, new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), new go.Binding('minSize', 'minSize').makeTwoWay(), new go.Binding('maxSize', 'maxSize').makeTwoWay(), new go.Binding('stroke', 'stroke').makeTwoWay(), new go.Binding('strokeWidth', 'strokeWidth').makeTwoWay(), ), goJS( go.Shape, { isPanelMain: true, stroke: 'white', strokeWidth: 3, height: 0, width: 100, name: 'PIPE', strokeDashArray: [20, 40], }, new go.Binding('width').makeTwoWay(), new go.Binding('stroke', 'waterStroke').makeTwoWay(), new go.Binding('strokeWidth', 'waterWidth').makeTwoWay(), new go.Binding('strokeDashArray', 'strokeDashArray').makeTwoWay(), { portId: '', toLinkable: true, fromLinkable: true, }, ), ), ); const nodeHoverAdornment = goJS( go.Adornment, 'Spot', { background: 'transparent', mouseLeave: function (e, obj) { const ad = obj.part; ad.adornedPart.removeAdornment('mouseHover'); }, }, new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('visible', '', function (data) { const { alarmType, realType } = data; return realType === '在线' && alarmType === '报警'; }), goJS(go.Placeholder, { // background: 'transparent', isActionable: true, }), goJS( go.Panel, 'Auto', { alignment: go.Spot.Left, alignmentFocus: go.Spot.Right }, // 左边 // { alignment: go.Spot.Right, alignmentFocus: go.Spot.Left }, // 右边 { padding: new go.Margin(0, 5, 0, 0), cursor: 'pointer' }, goJS( go.Shape, { fill: '#ff2929', strokeWidth: 0, width: 16, height: 16 }, new go.Binding('fill', '', function (data) { return data?.alarmStroke || '#ff2929'; }), new go.Binding('width', '', function (data) { return data?.alarmSize || 16; }), new go.Binding('height', '', function (data) { return data?.alarmSize || 16; }), new go.Binding('geometry', '', function (data) { let geo = svgIcons['warning']; if (geo === undefined) geo = null; // use this for an unknown icon name if (typeof geo === 'string') { geo = svgIcons['warning'] = go.Geometry.parse(geo, true); // fill each geometry } return geo; }), ), goJS( go.Shape, 'Circle', { fill: 'transparent', strokeWidth: 0, width: 16 }, new go.Binding('width', '', function (data) { return data?.alarmSize || 16; }), ), { toolTip: goJS( 'ToolTip', goJS( go.TextBlock, { margin: 2 }, new go.Binding('text', '', function (data) { const { alarmType, realType } = data; if (realType === '在线' && alarmType === '报警') { return data?.alarmInfo || ''; } else { return ''; } }), ), ), }, ), ); // 值定义 myDiagram.nodeTemplateMap.add( 'valCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 2, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Panel, 'Horizontal', goJS( go.Panel, 'Auto', goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', strokeWidth: 10, stroke: '#000000', parameter1: 0 }, new go.Binding('fill', 'fillColor'), new go.Binding('stroke'), new go.Binding('strokeWidth'), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), goJS( go.TextBlock, textStyle(), { name: 'textBlocks', maxSize: new go.Size(NaN, NaN), minSize: new go.Size(NaN, 1), wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: true, font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', flip: go.GraphObject.None, }, new go.Binding('text', 'showVal'), new go.Binding('font', 'fontStyle'), // new go.Binding('stroke', 'fontStroke'), new go.Binding('stroke', '', function (data) { const { alarmType, realType } = data; if (realType === '在线' && alarmType === '报警') { return data?.alarmStroke || '#ff2929'; } else { return data?.fontStroke; } }), new go.Binding('textAlign', 'fontAlign'), new go.Binding('maxSize', 'textSize'), new go.Binding('minSize', 'textSize'), new go.Binding('flip', 'flip'), ), { mouseHover: function (e, obj) { const node = obj.part; const data = node?.data || {}; const { alarmType, realType } = data; if (realType === '在线' && alarmType === '报警') { nodeHoverAdornment.adornedObject = node; node.addAdornment('mouseHover', nodeHoverAdornment); } }, }, ), goJS( go.TextBlock, textStyle(), { wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: false, font: 'normal 10px Helvetica,Arial,sans-serif', stroke: '#ffffff', }, new go.Binding('text', 'unitText'), new go.Binding('font', '', (v) => { return `normal ${v?.unitSize || 10}pt ${ v?.unitStyle || 'Helvetica,Arial,sans-serif' }`; }), new go.Binding('stroke', 'unitColor'), new go.Binding('visible', '', (v) => { return (v?.unitSwitch && !!v?.unitText) || false; }), new go.Binding('margin', '', (v) => { const unitGap = v?.unitGap || '0,0,0,5'; if (unitGap && isNumber(unitGap)) return unitGap; const margin = unitGap?.split(',') || null; return margin ? new go.Margin( margin?.[0] * 1 || 0, margin?.[1] * 1 || 0, margin?.[2] * 1 || 0, margin?.[3] * 1 || 0, ) : 0; }), ), ), { click(e, node) { if (navigatorAgent) return false; const { data } = node; nodeData = data; const list = bindData.find((item) => { return item.name === data.stationName; }); if (!list) return false; // 控制方法 if (data.ctName && data.ctType) { controlModalRender(data, list); return false; } // 历史查看 if (data.opType && data.opType === '历史查看' && data.shName) historyModalRender(data, list); // 统计历史查看 if (data.opType && data.opType === '统计查看' && data.shName) statisticalModalRender(data, list); }, }, ), ); // 连接点定义 myDiagram.nodeTemplateMap.add( 'linkPort', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 1 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色 goJS( go.Panel, 'Auto', { name: 'PANEL', }, goJS(go.Shape, 'Rectangle', { fill: 'transparent', strokeWidth: 0, width: 8, height: 8, minSize: new go.Size(5, 5), }), ), ), ); // 水池动效 go.Shape.defineFigureGenerator('Pool', (shape, w, h) => { const geo = new go.Geometry(); const fig = new go.PathFigure(0, 0, true); // starting point geo.add(fig); fig.add(new go.PathSegment(go.PathSegment.Line, 0.75 * w, 0)); fig.add(new go.PathSegment(go.PathSegment.Line, w, 0.25 * h)); fig.add(new go.PathSegment(go.PathSegment.Line, w, h)); fig.add(new go.PathSegment(go.PathSegment.Line, 0, h).close()); return geo; }); // 定义水池 myDiagram.nodeTemplateMap.add( 'waterCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 1 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'Rectangle', { name: 'SHAPE', alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Bottom, fill: 'transparent', strokeWidth: 10, stroke: 'red', desiredSize: new go.Size(NaN, 26), }, new go.Binding('width').makeTwoWay(), new go.Binding('height').makeTwoWay(), new go.Binding('stroke', 'stroke').makeTwoWay(), new go.Binding('strokeWidth', 'strokeWidth').makeTwoWay(), ), goJS( go.Shape, 'Rectangle', { name: 'SHAPE', alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Bottom, fill: '#ccc', strokeWidth: 10, stroke: 'transparent', desiredSize: new go.Size(NaN, 26), }, new go.Binding('width').makeTwoWay(), new go.Binding('height').makeTwoWay(), new go.Binding('fill', 'waterColor').makeTwoWay(), new go.Binding('strokeWidth', 'strokeWidth').makeTwoWay(), ), goJS( go.Shape, 'Pool', { name: 'waterSvg', alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Bottom, fill: '#DEE0A3', stroke: 'transparent', strokeWidth: 10, minSize: new go.Size(NaN, 5), desiredSize: new go.Size(NaN, 20), }, new go.Binding('width').makeTwoWay(), new go.Binding('height', 'waterHight').makeTwoWay(), new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('strokeWidth', 'strokeWidth').makeTwoWay(), ), ), ); // 定义进度条 myDiagram.nodeTemplateMap.add( 'speedCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 1 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', alignment: go.Spot.Left, alignmentFocus: go.Spot.Left, strokeWidth: 2, stroke: '#FFFFFF', desiredSize: new go.Size(NaN, 26), fill: 'transparent', parameter1: 5, }, new go.Binding('width').makeTwoWay(), new go.Binding('height').makeTwoWay(), new go.Binding('strokeWidth', 'strokeWidth').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('stroke', 'stroke').makeTwoWay(), ), goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', alignment: go.Spot.Left, alignmentFocus: go.Spot.Left, fill: '#CCCCCC', strokeWidth: 2, stroke: 'transparent', desiredSize: new go.Size(NaN, 26), parameter1: 5, }, new go.Binding('width').makeTwoWay(), new go.Binding('height').makeTwoWay(), new go.Binding('fill', 'waterColor').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('strokeWidth', 'strokeWidth').makeTwoWay(), ), goJS( go.Shape, 'RoundedRectanglePlus', { name: 'speedSvg', alignment: go.Spot.Left, alignmentFocus: go.Spot.Left, fill: '#DEE0A3', stroke: 'transparent', strokeWidth: 2, minSize: new go.Size(NaN, 5), desiredSize: new go.Size(NaN, 20), parameter1: 5, }, new go.Binding('width', 'lineWidth').makeTwoWay(), new go.Binding('height', 'height').makeTwoWay(), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('strokeWidth', 'strokeWidth'), ), ), ); // 泵状态设置 myDiagram.nodeTemplateMap.add( 'rotateCase', goJS( go.Node, 'Table', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 2 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), { // 设置其可改变大小 resizeObjectName: 'SHAPE', rotatable: true, }, new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Panel, 'Auto', { name: 'PANEL', }, goJS( go.Shape, 'Ellipse', // 定义形状 { width: 37, height: 37, fill: 'transparent', stroke: 'transparent', strokeWidth: 1 }, new go.Binding('width', 'width', (v) => { return v * 1.5; }).makeTwoWay(), new go.Binding('height', 'height', (v) => { return v * 1.5; }).makeTwoWay(), ), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), goJS( go.Picture, { name: 'rotateSvg', width: 26, height: 26, column: 0, scale: 1, source: require('./images/组态/状态/泵离线.svg'), imageAlignment: go.Spot.Center, angle: 0, }, new go.Binding('source', 'imgSrc', (v) => { return require(`./images/组态/状态/${v.split('/').pop()}`); }).makeTwoWay(), new go.Binding('scale', 'scale').makeTwoWay(), new go.Binding('width', 'width').makeTwoWay(), // new go.Binding('angle', 'angle').makeTwoWay(), new go.Binding('height', 'height').makeTwoWay(), ), ), ), ); // 点状态设置 myDiagram.nodeTemplateMap.add( 'pointCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 2 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'Ellipse', { width: 14, height: 14, name: 'SHAPE', fill: 'rgba(109, 122, 151, 1)', stroke: '#ffffff', }, new go.Binding('fill', 'fillColor').makeTwoWay(), new go.Binding('stroke').makeTwoWay(), new go.Binding('strokeWidth').makeTwoWay(), new go.Binding('height', 'height').makeTwoWay(), new go.Binding('width', 'height').makeTwoWay(), ), ), ); // 开关开设置 myDiagram.nodeTemplateMap.add( 'switchCase', goJS( go.Node, 'Auto', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 2, cursor: 'default' }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('cursor', 'cursor').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, 'RoundedRectanglePlus', { name: 'SHAPE', strokeWidth: 10, stroke: '#000000', parameter1: 5 }, new go.Binding('fill', 'fillColor'), new go.Binding('stroke'), new go.Binding('strokeWidth'), new go.Binding('parameter1', 'radius').makeTwoWay(), new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), ), goJS( go.TextBlock, textStyle(), { maxSize: new go.Size(NaN, NaN), minSize: new go.Size(NaN, 1), wrap: go.TextBlock.WrapFit, textAlign: 'center', editable: true, font: 'bold 12px Helvetica, Arial, sans-serif', stroke: '#454545', }, new go.Binding('text'), new go.Binding('font', 'fontStyle'), new go.Binding('stroke', 'fontStroke'), new go.Binding('textAlign', 'fontAlign'), new go.Binding('maxSize', 'textSize'), new go.Binding('minSize', 'textSize'), ), { click(e, node) { if (navigatorAgent) return false; const { data } = node; nodeData = data; const list = bindData.find((item) => { return item.name === data.stationName; }); if (!list) return false; // 控制方法 controlModalRender(data, list); }, }, ), ); // 搅拌机状态设置 myDiagram.nodeTemplateMap.add( 'blenderCase', goJS( go.Node, 'Table', nodeStyle(), 'Spot', { locationSpot: go.Spot.Center, zOrder: 2 }, new go.Binding('zOrder', 'zOrder').makeTwoWay(), new go.Binding('location', 'loc', go.Point.parse).makeTwoWay(go.Point.stringify), { // 设置其可改变大小 resizeObjectName: 'SHAPE', rotatable: true, }, new go.Binding('angle').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Panel, 'Auto', { name: 'PANEL', }, new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify), goJS( go.Picture, { name: 'blenderSvg', width: 42.5, height: 56, column: 0, scale: 1, source: require('./images/组态/状态/搅拌机双头1.svg'), angle: 0, }, new go.Binding('source', 'imgSrc', (v) => { return require(`./images/组态/状态/${v.split('/').pop()}`); }).makeTwoWay(), new go.Binding('scale', 'scale').makeTwoWay(), new go.Binding('width', 'width').makeTwoWay(), new go.Binding('angle', 'angle').makeTwoWay(), new go.Binding('height', 'height').makeTwoWay(), ), ), ), ); // 连接线装饰模板 const linkSelectionAdornmentTemplate = goJS( go.Adornment, 'Link', goJS(go.Shape, { isPanelMain: true, fill: null, stroke: 'deepskyblue', strokeWidth: 0, }), ); /** *******************************单管连接方式****************************** */ myDiagram.linkTemplate = goJS( BarLink, { curve: go.Link.JumpOver, toShortLength: 0, fromShortLength: 0, layerName: 'Background', routing: go.Link.Orthogonal, // 不同的位置进行不同的routing corner: 2, reshapable: true, resegmentable: true, relinkableFrom: true, relinkableTo: true, zOrder: 1, }, new go.Binding('layerName', 'layerName').makeTwoWay(), new go.Binding('fromSpot', 'fromPort', (d) => { return spotConverter(d); }), new go.Binding('toSpot', 'toPort', (d) => { return spotConverter(d); }), new go.Binding('points').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 goJS( go.Shape, { isPanelMain: true, stroke: '#41BFEC' /* blue */, strokeWidth: 6, name: 'changecolor' }, new go.Binding('stroke', 'stroke'), new go.Binding('strokeWidth', 'strokeWidth'), ), goJS( go.Shape, { isPanelMain: true, stroke: 'white', strokeWidth: 3, name: 'PIPE', strokeDashArray: [20, 40], }, new go.Binding('strokeWidth', 'waterWidth'), new go.Binding('stroke', 'waterStroke'), ), ); /** *******************************sharpLine线条****************************** */ myDiagram.linkTemplateMap.add( 'sharpLine', goJS( BarLink, { curve: go.Link.JumpOver, resegmentable: true, adjusting: go.Link.Stretch, routing: go.Link.Normal, layerName: 'Background', routing: go.Link.Normal, //不同的位置进行不同的routing corner: 0, reshapable: true, resegmentable: true, relinkableFrom: true, relinkableTo: true, relinkableFrom: true, relinkableTo: true, zOrder: 1, }, new go.Binding('layerName', 'layerName').makeTwoWay(), new go.Binding('fromSpot', 'fromPort', function (d) { return spotConverter(d); }), new go.Binding('toSpot', 'toPort', function (d) { return spotConverter(d); }), new go.Binding('points').makeTwoWay(), roleVisibleBinding(), // 绑定角色可见 // mark each Shape to get the link geometry with isPanelMain: true goJS( go.Shape, { isPanelMain: true, stroke: '#41BFEC' /* blue*/, strokeWidth: 6, name: 'changecolor', }, new go.Binding('stroke', 'stroke'), new go.Binding('strokeWidth', 'strokeWidth'), ), goJS( go.Shape, { isPanelMain: true, stroke: 'white', strokeWidth: 3, name: 'PIPE', strokeDashArray: [20, 40], }, new go.Binding('strokeWidth', 'waterWidth'), new go.Binding('stroke', 'waterStroke'), ), ), ); /** **************************************合管连接方式****************************************** */ // myDiagram.linkTemplateMap.add( // 'linkToLink', // goJS( // 'Link', // { relinkableFrom: true, relinkableTo: true }, // goJS('Shape', { // stroke: '#2D9945', // strokeWidth: 2, // }), // ), // ); const fromJson = JSON.parse(jsonStr); myTimeout(() => { loop(); waterSvg(); rotateSvg(); blenderSvg(); animationSvg(); updateTimeCase(); }, 100); const json = JSON.parse(JSON.stringify(fromJson)); json.linkDataArray.forEach((item) => { item.isHavingDash = flowShow; item.realVal = '--'; item.defaultWidth = item.waterWidth; }); json.nodeDataArray.forEach((item) => { item.showVal = '--'; item.realVal = '--'; item.realType = '在线'; item.Unit = ''; item.switchState = '开'; item.dtImgSrc = item.imgSrc || ''; if (item.category === 'HBar') { item.hBarClolor = item.waterStroke; item.typeDash = false; } if (item.category === 'valCase') { if (item.shType === '') item.showVal = item.text; item.dtStroke = item.fontStroke; item.alarmType = '正常'; } if (item.category === 'nameCase') { item.dtFillColor = item.fillColor; item.dtStroke = item.stroke; item.dtFontStroke = item.fontStroke; item.dtText = item.text; } if (item.category === 'modelCase' || item.category === 'ellipseCase') { item.dtzOrder = item.zOrder; } if (item.category == 'deviceCase' && item.shType && deviceName && deviceName.length) { var device = deviceName.find(function (arr, index) { return '设备' + stationList[index] == item.stationName; }); if (device) item.text = device; } if (item.category == 'timeCase') { item.timeStr = item.text; } if (item.category === 'bgCase') { myDiagram.defaultScale = item.scaling || item.scaling === 0 ? item.scaling * 1 : 1; entryAnim = item?.entryAnim || ''; } if (item.category === 'groupCase') { item.dtVisible = item?.visible || false; } // 兼容V1之前版本(部分展示可支持) if (chartInfo.version === 'V1') return false; item.shName = item.showName || ''; item.hbControl = item.authoControl || '否'; if (item.controlType === '开关展示') item.switch = '是'; if (item.category === 'valCase') item.shType = '值显示'; }); // 初始化动画 switch (entryAnim) { case 'none': myDiagram.animationManager.initialAnimationStyle = go.AnimationManager.None; break; case 'default': myDiagram.animationManager.initialAnimationStyle = go.AnimationManager.Default; break; case 'location': myDiagram.animationManager.initialAnimationStyle = go.AnimationManager.AnimateLocations; break; case 'opacity': myDiagram.animationManager.initialAnimationStyle = go.AnimationManager.None; break; default: myDiagram.animationManager.initialAnimationStyle = go.AnimationManager.Default; break; } // 动画监听 myDiagram.addDiagramListener('InitialAnimationStarting', animationStarting); // 监听画布变化 myDiagram.addDiagramListener('ViewportBoundsChanged', viewportBoundsChang); myDiagram.model = go.Model.fromJson(json); getDataModel(); setIsDiagram(myDiagram); }; return ( <div className={classNames(prefixCls)} ref={ConfigurationRef}> <div id={TwoRef.current} ref={ConfigurationViewRef} className={classNames('configurationView')} > {/* <LoadBox spinning={spinning} /> */} {isEmpty && <Empty theme={'dark'} description={description} />} </div> {spinning && ( <div className={classNames('configurationLoad')}> <LoadBox spinning={spinning} /> </div> )} {/* 远程控制 */} {isModalVisible && ( <Modal centered okText={'确定'} width={modalWidth} cancelText={'取消'} open={isModalVisible} onOk={handleOk} onCancel={handleCancel} wrapClassName={classNames(`${prefixCls}-baseModal`)} getContainer={ConfigurationRef.current} {...modalProps} > {renderModalContent()} </Modal> )} {/* 权限登录 */} {isAuModalVisible && ( <Modal centered title={'权限认证'} okText={'确定'} cancelText={'取消'} open={isAuModalVisible} onOk={handleAuOk} onCancel={() => setIsAuModalVisible(false)} getContainer={ConfigurationRef.current} width={520} wrapClassName={classNames(`${prefixCls}-baseModal`)} > <Form className={classNames('authorizeControlContent')} ref={AuthorFrom} name="loginForm"> <Form.Item className={classNames('authorizeControlItem')} name="userName" label="账户" initialValue={currentUser} > <Input.TextArea placeholder="请输入用户名" autoComplete={'off'} autoSize={{ minRows: 1, maxRows: 1 }} disabled={!!currentUser} /> </Form.Item> <Form.Item className={classNames('authorizeControlItem')} name="password" label="密码"> <Input className={classNames(viewType ? '' : 'hidePassWord')} placeholder="请输入密码" autoComplete={'off'} addonAfter={ <div className={classNames('viewPassWord')} onClick={viewTypeChange}> {viewType ? <EyeOutlined /> : <EyeInvisibleOutlined />} </div> } /> </Form.Item> </Form> </Modal> )} {/* 历史曲线 */} {isHIModalVisible && ( <Modal centered width={1200} footer={null} open={isHIModalVisible} onOk={() => setIsHIModalVisible(false)} onCancel={() => setIsHIModalVisible(false)} getContainer={ConfigurationRef.current} // title={historyInfoParams.length ? historyInfoParams[0].sensors || '' : ''} wrapClassName={classNames(`${prefixCls}-historyInfoModal`)} > <HistoryView deviceParams={historyInfoParams} title={historyInfoParams.length ? historyInfoParams[0].sensors || '' : ''} /> </Modal> )} {/* 统计历史曲线 */} {isSTHIModalVisible && ( <Modal centered width={1200} footer={null} open={isSTHIModalVisible} onOk={() => setIsSTHIModalVisible(false)} onCancel={() => setIsSTHIModalVisible(false)} getContainer={ConfigurationRef.current} title={statisticalInfoParams.sensors || ''} wrapClassName={classNames(`${prefixCls}-historyInfoModal`)} > <StatisticalHistoryView deviceParams={statisticalInfoParams} /> </Modal> )} {/* 画板跳转 */} {isJumpModalVisible && jumpModalProps && ( <DragModal centered width={jumpModalProps.width ? `${jumpModalProps.width}px` : '100vw'} title={jumpModalProps.title} footer={null} visible={isJumpModalVisible} limit={true} onCancel={() => setIsJumpModalVisible(false)} wrapClassName={classNames(`${prefixCls}-jumpModal`)} getContainer={ConfigurationRef.current} componentPrefix={componentPrefix} destroyOnClose={true} style={{ height: jumpModalProps.height ? `${Number(jumpModalProps.height) + 103}px` : '100vh', left: `calc(50% - ${jumpModalProps.width ? jumpModalProps.width / 2 + 'px' : '50%'})`, top: `calc(50% - ${ jumpModalProps.height ? (Number(jumpModalProps.height) + 103) / 2 + 'px' : '50%' })`, }} > <ConfigurationDetail {...props} name={jumpModalProps.name} devices={jumpModalProps.device} /> </DragModal> )} {isVideoVisible && ( <VideoSliderModal modalInfo={{ title: videoTitle, open: isVideoVisible, onCancel: () => { setIsVideoVisible(false); }, }} videoInfos={videoData} JessibucaObj={_JessibucaObj} /> )} {isControlLogVisible && ( <Modal centered width={'80vw'} footer={null} open={isControlLogVisible} onOk={() => setIsControlLogVisible(false)} onCancel={() => setIsControlLogVisible(false)} getContainer={ConfigurationRef.current} title={`${nodeData.ctName || ''}控制日志`} wrapClassName={classNames(`${prefixCls}-controlLogInfoModal`)} > {renderControlLogContent()} </Modal> )} </div> ); }; ConfigurationView.defaultProps = { name: '', devices: [], deviceName: [], config: {}, isZoom: false, flowShow: true, messaged: {}, customBack: () => {}, }; ConfigurationView.propTypes = { name: PropTypes.string, devices: PropTypes.array, deviceName: PropTypes.array, config: PropTypes.object, isZoom: PropTypes.bool, flowShow: PropTypes.bool, messaged: PropTypes.object, customBack: PropTypes.func, }; export default ConfigurationView;