/* 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;