/* eslint-disable */
import React, { useState, useEffect, useRef, useContext } from 'react';
import classNames from 'classnames';
import moment from 'moment';
import sha1 from 'sha1';
import Empty from '@wisdom-components/empty';
import LoadBox from '@wisdom-components/loadbox';
import { Input, message, Modal, Form, ConfigProvider, Button } from 'antd';
import {
  ExclamationCircleOutlined,
  DoubleLeftOutlined,
  LeftOutlined,
  RightOutlined,
  DoubleRightOutlined,
} from '@ant-design/icons';
import MqttView from '@wisdom-components/mqttview';
import PropTypes from 'prop-types';
import HistoryView from '@wisdom-components/ec_historyview';
import * as go from './js/go';
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 DragModal from './js/DragModal';
import { getSketchPadList, getSketchPadContent, getPointAddress, getDeviceRealInfo } from './apis';
import {
  isNumber,
  createGuid,
  deepCopy,
  hexToRgba,
  addNumMethod,
  hexSwitch,
  textStyle,
  querySkipUrl,
  isJson,
  stationData,
} from './js/utils';
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 nodeData = null; // 选中节点的数据
let twoID = '';

const waterFlow = new WaterFlowControlView();

const ConfigurationView = (props) => {
  let myDiagram = null;
  let mqttView = null;
  let editionArr = [];
  const guidAggre = {};
  const bindData = [];
  const stationList = [];
  const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
  const prefixCls = getPrefixCls('ec-configuration-view');
  const componentPrefix = getPrefixCls('');
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [isAuModalVisible, setIsAuModalVisible] = useState(false); // 登录模态框
  const [isHIModalVisible, setIsHIModalVisible] = useState(false); // 历史曲线模态框
  const [isJumpModalVisible, setIsJumpModalVisible] = useState(false); // 画板跳转模态框
  const [spinning, setSpinning] = useState(true); // 画板loading
  const [isEmpty, setIsEmpty] = useState(false); // 画板无数据状态
  const [description, setDescription] = useState(''); // 画板无数据描述
  const [first, setFirst] = useState(true); // 第一次加载

  twoID = `TDG${Date.now().toString(36)}`;
  const AdjustControlInput = useRef();
  const AuthorFrom = useRef();
  const ConfigurationRef = useRef();
  const customBack = props.customBack ? props.customBack : () => {};
  const { devices = [], config, isZoom = false, flowShow = true, deviceName = [] } = props;
  const globalConfig = window.globalConfig || config;
  let isClose = false;

  /** **********************************获取工艺图画板信息*********************** */
  const getConfiguraList = async () => {
    const url = globalConfig.mainserver ? globalConfig.mainserver : 'https://panda-water.cn/';
    imgUrl = online
      ? `${url}PandaMonitor/Monitor/`
      : `${globalConfig.baseURI}/PandaMonitor/Monitor/`;
    // 获取画板信息
    const drawInfo = await getSketchPadList({
      name: props.name,
      siteCode:
        globalConfig.userInfo && globalConfig.userInfo.LocalSite
          ? globalConfig.userInfo.LocalSite
          : '',
      version: '全部',
      _site:
        globalConfig.userInfo && globalConfig.userInfo.LocalSite
          ? globalConfig.userInfo.LocalSite
          : '',
    });
    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]);
          }
        }
        getDiagramJson(data[0], siteInfo);
      } else {
        setDescription('咦~未查询到工艺图画板信息哦~');
        setIsEmpty(true);
        setSpinning(false);
        return false;
      }
    } else {
      setDescription('咦~工艺图画板信息报错啦~');
      setIsEmpty(true);
      setSpinning(false);
      return message.error(drawInfo.msg);
    }

    // 获取点表信息
    const pointInfo = devices.length
      ? await getPointAddress({
          code: devices.join(','),
          _site:
            globalConfig.userInfo && globalConfig.userInfo.LocalSite
              ? globalConfig.userInfo.LocalSite
              : '',
        })
      : null;
    editionArr = deepCopy(pointInfo && pointInfo.data ? pointInfo.data : [], []);
  };

  /** *********************************节点展示逻辑****************************** */
  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);
          myDiagram.model.setDataProperty(node, 'imgSrc', shRule ? shRule.attr : node.dtImgSrc);
          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': // 实时值模型
          if (node.shType === '值显示') {
            myDiagram.model.setDataProperty(node, 'showVal', realVal < 0 ? 0 : realVal);
          } else {
            myDiagram.model.setDataProperty(node, 'showVal', realVal);
          }
          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);
          myDiagram.model.setDataProperty(node, 'zOrder', shRule ? shRule.text : node.dtzOrder);
          break;
        case 'ellipseCase': // 圆形模型
          shRule = ruleOperation(node, realVal);
          myDiagram.model.setDataProperty(
            node,
            'zOrder',
            shRule ? shRule.text * 1 || 0 : node.dtzOrder,
          );
          break;
        default:
          break;
      }
    } catch (err) {
      console.log(err);
    }
  };

  /** ***********************************展示规则运算********************************* */
  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) => {
    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;

    try {
      if (unitRender) unitRender.tipRender({ deviceID: code, data: mqttData });
    } catch (err) {
      // 三维调用
    }

    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 {
      jsonCopy.nodeDataArray.forEach((item) => {
        if (!(item.shName || item.figure === 'updateTime') || item.stationName !== name)
          return false;
        const node = myDiagram.model.findNodeDataForKey(item.key);
        mqttData.forEach((list) => {
          if (node.figure === 'updateTime') {
            myDiagram.model.setDataProperty(
              node,
              'text',
              moment(list.Time).format('yyyy-MM-DD hh:mm:ss'),
            );
            return false;
          }
          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;
          showNodeMethod(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) {
      // 水流展示
    }
  };

  /** **************************************合管****************************************** */
  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 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 (!props.name || !props.devices || !props.devices.length) {
      setDescription('咦~工艺图配置信息不全哦~');
      setIsEmpty(true);
      setSpinning(false);
      return false;
    }
    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.div = null;
        myDiagram = null;
      }
    };
  }, [props.name]);

  useEffect(() => {
    if (!isModalVisible) {
      modalConfirmFn = null;
      modalComponent = null;
      modalProps = {};
    }
  }, [isModalVisible]);

  useEffect(() => {
    if (!isAuModalVisible) {
      auModalConfirmFn = null;
    }
  }, [isAuModalVisible]);

  /** ************************************获取画板JSON******************************* */
  const getDiagramJson = async (list, siteInfo) => {
    const response = await getSketchPadContent({
      dimension: list.dimension,
      siteCode: list.siteCode,
      fileName: list.deployURL.split('\\').pop(),
      _site:
        globalConfig.userInfo && globalConfig.userInfo.LocalSite
          ? globalConfig.userInfo.LocalSite
          : '',
    });
    if (response.code === 0) {
      if (isClose) return false;
      const fromJson = response.data
        ? response.data
        : {
            linkFromPortIdProperty: 'fromPort',
            linkToPortIdProperty: 'toPort',
            nodeDataArray: [],
            linkDataArray: [],
          };
      devices.forEach((item, index) => {
        const name = `设备${stationList[index]}`;
        bindData.push({
          code: item,
          name,
          type: siteInfo && siteInfo[name] ? siteInfo[name].Type : '',
        });
      });
      mqttView = devices.length
        ? new MqttView({
            mqttIP: globalConfig.mqtt_iotIP,
            mqttPath: globalConfig.mqtt_path,
            mqttSsl: globalConfig.mqtt_IsSSL,
            siteCode: globalConfig.mqtt_mess.site_code,
            devices,
            callback: refreshData,
            controlback: controlData,
          })
        : null;
      diagramRender(typeof fromJson === 'string' ? fromJson : JSON.stringify(fromJson), list);
      try {
        mqttView && mqttView.saveWaterMqtt();
      } 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: devices.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 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 || item.stationName !== bindList.name) return false;
          if (
            !pvList ||
            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.figure === 'updateTime')) return false;
        const node = myDiagram.model.findNodeDataForKey(item.key);
        mqttData.forEach((list) => {
          if (node.figure === 'updateTime') {
            myDiagram.model.setDataProperty(
              node,
              'text',
              new Date(list.PT).format('yyyy-MM-dd hh:mm:ss'),
            );
            return false;
          }
          const bindList = bindData.find((arr) => {
            return arr.code === list.code;
          });
          const pvList = list.dataList.find((arr) => {
            return arr.dName === item.shName;
          });
          if (!bindList || item.stationName !== bindList.name) return false;
          if (
            !pvList ||
            pvList.pv === null ||
            pvList.dName !== item.shName ||
            item.realVal === pvList.pv
          )
            return false;
          pvList.Value = pvList.pv;
          showNodeMethod(node, pvList);
        });
      });
    } 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) => {
    historyInfoParams = [
      {
        deviceCode: list.code,
        sensors: data.shName,
        deviceType: list.type,
      },
    ];
    setIsHIModalVisible(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) => {
    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,
        sha1(password).toUpperCase(),
        guid,
        code,
        tag,
        value,
        flag ? value * 1 : '',
      );
  };

  /** **************************************权限控制方法****************************************** */
  const authoMethod = (code, tag, node, guid, value) => {
    setIsAuModalVisible(true);
    auModalConfirmFn = () => defineAutho(code, tag, node, guid, value);
  };

  /** **************************************开关控制确定****************************************** */
  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, value);
      return false;
    }
    guidAggre[guid] = { tag, code };
    mqttView &&
      mqttView.onSendWaMessageArrived(
        globalConfig.token || '',
        '',
        guid,
        code,
        tag,
        node.switchType,
        value * 1,
      );
  };

  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')}>
        {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>
    );
  };

  /** **************************************控制模态渲染****************************************** */
  const controlModalRender = (data, list) => {
    const ctRule = data && data.ctRule ? JSON.parse(data.ctRule) : '';
    switch (data.ctType) {
      case '按钮控制':
        if (data.switch === '是') message.warning(`当前设备已是${data.text}状态，请勿重复操作！`);
        if (
          data.realVal === '--' ||
          data.switch === '是' ||
          !data.ctName ||
          !data.ctName.length ||
          ctRule[0].val === ''
        )
          return false;
        modalComponent = renderSwitchControlModal;
        modalConfirmFn = () => defineSwitch(list.code, data.ctName, data);
        modalProps = { title: '状态控制' };
        modalWidth = 520;
        setIsModalVisible(true);
        break;
      case '输入控制':
        if (data.realVal === '--' || !data.ctName) return false;
        modalComponent = renderAdjustControlModal;
        modalConfirmFn = () => controlMethod(list.code, data.ctName, data);
        modalProps = { title: `${data.ctName}设置` };
        modalWidth = 520;
        setIsModalVisible(true);
        break;
      case '多选控制':
        if (!data.ctName) return false;
        modalComponent = renderMoreControlModal;
        modalProps = { footer: null, title: `${data.ctName}设置` };
        modalWidth = 'auto';
        setIsModalVisible(true);
        break;
      default:
        break;
    }
  };

  /** **************************************交互脚本****************************************** */
  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';
    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,
    };
    setIsJumpModalVisible(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 diagramRender = (jsonStr, chartInfo) => {
    myDiagram = goJS(
      go.Diagram,
      twoID, // 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;
        // },
        'linkingTool.direction': go.LinkingTool.ForwardsOnly,
      },
    );

    /** **********************************分组模型************************************* */
    myDiagram.groupTemplate =
      ('groupCase',
      goJS(
        go.Group,
        'Auto',
        { ungroupable: true, zOrder: 1 },
        {
          // 设置其可选择
          selectable: false,
          layerName: 'Background',
        },
        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) {
              return v
                ? `/PandaCore/GCK/SketchPad/PreviewResource?name=${v}&_site=${
                    globalConfig?.userInfo?.LocalSite || ''
                  }`
                : '';
            }).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(),
          ),
        ),
      ),
    );

    // 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(),

        // 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,
            {
              name: 'animateSvg',
              width: 56,
              height: 56,
              column: 0,
              scale: 1,
              source: require('./images/组态/默认.png'),
            },
            new go.Binding('source', 'imgSrc', function (v) {
              return !v
                ? require('./images/组态/默认.png')
                : `/PandaCore/GCK/SketchPad/PreviewResource?name=${v}&_site=${
                    globalConfig?.userInfo?.LocalSite || ''
                  }`;
            }).makeTwoWay(),
            new go.Binding('scale', 'scale').makeTwoWay(),
            new go.Binding('width', 'width').makeTwoWay(),
            new go.Binding('height', 'height').makeTwoWay(),
          ),
        ),
        {
          click(e, node) {
            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 '自定义交互': // 自定义交互
                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),

          goJS(
            go.Picture,
            { name: 'animateSvg', width: 56, height: 56, column: 0, scale: 1, source: '' },
            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(),
          ),
        ),
        {
          click(e, node) {
            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 '自定义交互': // 自定义交互
                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('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('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'),
        ),
        {
          click(e, node) {
            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 '自定义交互': // 自定义交互
                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'),
        ),
        {
          click(e, node) {
            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 '自定义交互': // 自定义交互
                customBack(data);
                interactiveScript(data);
                break;
              default:
                break;
            }
          },
        },
      ),
    );

    // 公用管定义
    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,
          },
        ),
      ),
    );

    // 值定义
    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.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(),
          {
            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'),
          new go.Binding('font', 'fontStyle'),
          new go.Binding('stroke', 'fontStroke'),
          new go.Binding('textAlign', 'fontAlign'),
        ),
        {
          click(e, node) {
            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.shName) historyModalRender(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,
        },
        roleVisibleBinding(), // 绑定角色可见
        goJS(
          go.Panel,
          'Table',
          {
            name: 'PANEL',
          },
          goJS(
            go.Shape,
            'Ellipse', // 定义形状
            { width: 37, height: 37, fill: 'transparent', stroke: 'transparent', strokeWidth: 1 },
            new go.Binding('width', 'widthBox').makeTwoWay(),
            new go.Binding('height', 'heightBox').makeTwoWay(),
          ),
          new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),

          goJS(
            go.Picture,
            {
              name: 'rotateSvg',
              width: 26,
              height: 26,
              column: 0,
              scale: 1,
              source: require('./images/组态/状态/泵离线.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(),
          ),
        ),
      ),
    );

    // 点状态设置
    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'),
        ),
        {
          click(e, node) {
            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('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('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();
    }, 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;
      }
      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' && deviceName && deviceName.length) {
        var device = deviceName.find(function (arr, index) {
          return '设备' + stationList[index] == item.stationName;
        });
        if (device) item.text = device;
      }
      // 兼容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 = '值显示';
    });
    myDiagram.model = go.Model.fromJson(json);
  };

  return (
    <div className={classNames(prefixCls)} ref={ConfigurationRef}>
      <div id={twoID} className={classNames('configurationView')}>
        <LoadBox spinning={spinning} />
        {isEmpty && <Empty theme={'dark'} description={description} />}
      </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="账户">
              <Input placeholder="请输入用户名" />
            </Form.Item>
            <Form.Item className={classNames('authorizeControlItem')} name="password" label="密码">
              <Input.Password placeholder="请输入密码" />
            </Form.Item>
          </Form>
        </Modal>
      )}
      {/* 历史曲线 */}
      {isHIModalVisible && (
        <Modal
          centered
          width={1200}
          footer={null}
          open={isHIModalVisible}
          onOk={() => setIsHIModalVisible(false)}
          onCancel={() => setIsHIModalVisible(false)}
          getContainer={ConfigurationRef.current}
          wrapClassName={classNames(`${prefixCls}-historyInfoModal`)}
        >
          <HistoryView deviceParams={historyInfoParams} />
        </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>
      )}
    </div>
  );
};

ConfigurationView.defaultProps = {
  name: '',
  devices: [],
  deviceName: [],
  config: {},
  isZoom: false,
  flowShow: true,
  customBack: () => {},
};

ConfigurationView.propTypes = {
  name: PropTypes.string,
  devices: PropTypes.array,
  deviceName: PropTypes.array,
  config: PropTypes.object,
  isZoom: PropTypes.bool,
  flowShow: PropTypes.bool,
  customBack: PropTypes.func,
};

export default ConfigurationView;
