<template>
  <div class="flow-layout">
    <div class="flow-editor">
      <div class="flow-tools">
        <el-tooltip class="item" effect="dark" content="保存布局数据" placement="top-start">
          <div class="tool-item" @click="handleSaveData()">
            <span class="iconfont iconfont-toolbar icon-save primary"></span>
          </div>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="浏览JSON数据" placement="top-start">
          <div class="tool-item" @click="handleGetData">
            <span class="iconfont iconfont-toolbar icon-json primary"></span>
          </div>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="手动排列" placement="top-start">
          <div class="tool-item" title="" @click="handleSort">
            <span class="iconfont iconfont-toolbar icon-sort primary"></span>
          </div>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="自动排列" placement="top-start">
          <div class="tool-item" @click="handleToggleAutoSort">
            <span class="iconfont iconfont-toolbar icon-auto" :class="toggleSortClass"></span>
          </div>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="撤销" placement="top-start">
          <div class="tool-item" @click="handleUndo">
            <span class="iconfont iconfont-toolbar icon-undo primary" :class="{'icon-undo-disabled':!canUndo}"></span>
          </div>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="重做" placement="top-start">
          <div class="tool-item" @click="handleClear">
            <span class="iconfont iconfont-toolbar icon-redo primary"></span>
          </div>
        </el-tooltip>
        <el-tooltip class="item" effect="dark" content="网格" placement="top-start">
          <div class="tool-item" @click="toggleGridLine = !toggleGridLine">
            <span class="iconfont iconfont-toolbar icon-grid" :class="toggleGridLineClass"></span>
          </div>
        </el-tooltip>
      </div>
      <!--zoom-->
      <div class="flow-zoom" :data-zoom="canvasDataRoom + '%'">
        <div class="zoom-btn">
          <el-button size="mini" :class="{'el-button--primary':canvasRoomMinusEnable}" icon="el-icon-minus"
                     circle @click="handleMinusCanvas"></el-button>
        </div>
        <div class="zoom-btn">
          <el-button size="mini" :class="{'el-button--primary':canvasRoomPlusEnable}" icon="el-icon-plus"
                     circle @click="handlePlusCanvas"></el-button>
        </div>
      </div>
      <!--    canvas   -->
      <div class="canvas-container" :class="{'show-grid':toggleGridLine}" :data-zoom="canvasDataRoom">
        <div id="campaignCanvas" :style="canvasRoomScaleStyle" ref="campaignCanvas">
          <template v-for="(flowItem,index) in flowList">
            <div class="node-content-wrap" :key="flowItem.uuid" @mousedown="handleFlowMoveDown"
                 @mouseup="(event)=>{handleChangeFlowPosition(flowItem,event)}"
                 :id="flowItem.uuid"  @click.stop="handleEditFlowItem(flowItem,$event)"
                 :data-type="flowItem.groupType ==='temp'?'DeleteLinked':'link'"
                 :style="{left:flowItem.left+'px',top:flowItem.top+'px'}">
              <div class="node-content">
                <template v-if="flowItem.groupType === 'action'">
                  <div class="flow-item node">
                    <div class="step-img" :class="flowItem.className"></div>
                    <div class="step-title">{{ flowItem.nodeName }}</div>
                  </div>
                  <div class="flow-text">
                    <div class="text-line">
                      <span>{{ flowItem.stepName }}</span>
                      <i class="el-icon-close flow-delete"
                         @click.stop="handleDeleteFlowItem(flowItem)"></i>
                    </div>
                  </div>
                </template>
                <template v-else-if="flowItem.groupType === 'condition'">
                  <div class="flow-item node-square">
                    <div class="square">
                      <div class="step-img" :class="flowItem.className"></div>
                    </div>
                  </div>
                  <div class="flow-text">
                    <div class="text-line">
                      <span>{{ flowItem.stepName }}</span>
                      <i class="el-icon-close flow-delete"
                         @click.stop="handleDeleteFlowItem(flowItem)"></i>
                    </div>
                    <div class="text-line1">
                      <template v-if="flowItem.title">
                        <span class="text-content">{{ flowItem.title }}</span>
                      </template>
                    </div>
                  </div>
                </template>
                <!-- flow -->
                <template v-else-if="flowItem.groupType === 'flow'">
                  <div class="flow-item node node-circle">
                    <div class="step-img" :class="flowItem.className"></div>
                  </div>
                  <div class="flow-text">
                    <div class="text-line">
                      <span>{{ flowItem.stepName }}</span>
                    </div>
                  </div>
                </template>
                <!--  temp node -->
                <template v-if="flowItem.groupType === 'temp' ">
                  <draggable class="flow-item node-temp node-temp-img"
                             ref="tempNode"
                             :id="flowItem.uuid"
                             :group="{name:'sortable', pull:false, put: true }">
                  </draggable>
                    <div class="flow-text">
                        <div class="text-line">
                            <span>临时节点</span>
<!--                            <i class="el-icon-close flow-delete" @click.stop="handleDeleteFlowItem(flowItem)"></i>-->
                        </div>
                        <div class="text-line">
                            <span>接收拖放</span>
                        </div>
                    </div>
                </template>
              </div>
            </div>
          </template>
        </div>
      </div>
      <!-- sidebar-->
      <div class="flow-editor-sidebar">
          <draggable class="items-box" :value="setting.items"
                     :move="handleFlowMoveItem" :group="{name:'sortable', pull: 'clone', put: false }" v-bind="dragConfig"
                     @start="handleFlowMoveStart" @end="handleFlowMoveEnd" :sort="false" ref="sidebar-draggable">
            <template v-for="(item,index) in setting.items">
              <div :class="item.bgClassName" :key="index" :title="item.nodeName" :data-id="item.nodeId"
                   :data-type="item.type" :data-group="item.groupType" v-if="!item.hidden">
                <div class="square">
                  <div class="step-img" :class="item.className"></div>
                  <div v-if="item.nodeId=='nodeNode'" class="step-title">{{ item.nodeName }}</div>
                </div>
              </div>
              <div v-if="!item.hidden" class="step-title">{{ item.nodeName }}</div>
            </template>
          </draggable>
      </div>
    </div>
    <!--流程工具栏弹窗-->
    <Dialog refs="dialog" :title="dialog.title" :visible.sync="dialog.visible" :destroy-on-close="dialog.destroyOnClose"
            :width="dialog.width" :height="dialog.height" :close-on-click-modal="dialog.modalClose"
            parentClass="app-page" @close="handleUpdateNode" :show-close="true"
            :modal-append-to-body="false" :close-on-press-escape="false">
      <component v-if="dialog.visible" :is="dialog.component" :params="dialog.params"></component>
    </Dialog>
    <!--JSON数据弹窗-->
    <Dialog title="JSON数据" :visible.sync="dialogVisible" width="700px" parentClass="app-page"
            :modal-append-to-body="false" :close-on-press-escape="true">
      <div class="pre-show" ref="preShow" v-if="dialogVisible"></div>
      <span slot="footer" class="dialog-footer">
            <el-button type="primary" @click="dialogVisible = false">关 闭</el-button>
          </span>
    </Dialog>
  </div>
</template>
<script>
import JSONFormatter from "json-formatter-js";
import Draggable from "./Draggable";
import setting from "./settings";
import {clone,uuid,addClass,removeClass,getWidth} from "./dom.js";
import { jsPlumb } from "jsplumb";
let MEMORY_LIST = [];
const dragConfig = {
  animation: 0,
  group: "description",
  disabled: false,
  ghostClass: "sortable-ghost", // dragging class
};
const FLOW_STEP_LENGTH = 120;
const FLOW_LEFT_STEP_LENGTH_MAX = 130;
const FLOW_START_STEP_TOP = 20;
/*
定义数据存储结构
从某种意义上来说，数据的链接应该是列表模式的
{steps:{"id":{x:1,y:0}}}   //流程主表存储宏观见面布局
steps:[{id:1,name:"test",type:"ifNode",options:{next:[nextid1,nextid2...],group:[],title:""},time:"1d"}] //流程列表存储后续步骤  //title为原来的ifNodeTitle

主要的交互行为
1，因表单交互产生的变化需要马上更新到数据库（表单交互存在，用户保存或直接关闭2个情况，直接关闭）
2，界面拖动或自动排列产生的变化，可以等用户手动保存到数据库，减少交互
*/
export default {
  name: "Workflow",
  jsPlumb: null,
  data() {
    return {
      queryParams:{},
      proId: null,
      setting: setting,
      jsPlumbInit: false,
      dialog: {
        visible: false,
        title: "步骤管理",
        width: "700px",
        height: "400px",
        modalClose: false,
        destroyOnClose: true,
        component: "FormView",
        params: {},
      },
      dialogVisible: false,
      flowList: [],
      dragConfig: dragConfig,
      movingFlowItem: undefined, // moving
      movedFlowItem: undefined, //  moved
      canvasDataRoom: 100,
      toggleGridLine: true,
      toggleAutoSort: false,//是否自动排序
      canUndo: false,
      tempLayerMap: [],
      currentStep: null,
      processData: {positions: "", steps: []},
      messageHandler:{}
    };
  }, props: {
    params: {
      type: Object,
      default: {}
    },
  }, components: {
    Dialog: (resolve) => require([`../Dialog`], resolve),
    FormView: (resolve) => require([`../editors/FormView`], resolve),
    Draggable,
  }, async created() {

  }, mounted() {
    this.$options.jsPlumb = jsPlumb.getInstance();
    this.initJsPlumb();
  }, computed: {
    canvasRoomMinusEnable() {
      return this.canvasDataRoom > 50;
    }, canvasRoomPlusEnable() {
      return this.canvasDataRoom < 100;
    }, canvasRoomScaleStyle() {
      return {transform: "scale(" + this.canvasDataRoom / 100 + ")"};
    }, toggleGridLineClass() {
      return this.toggleGridLine ? "icon-grid-open" : "icon-grid-close";
    }, toggleSortClass() {
      return this.toggleAutoSort ? "icon-auto-sort-open" : "icon-auto-sort-close";
    }
  }, methods: {
    async $_page_updateQueryParams(params) {
       if(params)
        this.$d.merge(true,this.queryParams,params);
      this.loaded = false;
      // this.$d.debug().log("重新加载=======》");
      MEMORY_LIST=[];
      this.updateCanUndoBtn();
      this.proId = this.queryParams.proId;
      this.processData={positions: "", steps: []};//有时步骤，但是存在位置信息，需要
      let options = {positions:{}};
      await this.$async({url: this.$store.state.api.oa.getProcessOptions,
        data: {proId: this.proId}
      }).then(async (res) => {
        if (res.data && res.data.length&&res.data[0].options)
          options = res.data[0].options;
      })
      let steps = [];
      await this.$async({url: this.$store.state.api.oa.getProcessSteps,data: {proId: this.proId}}).then(async (res) => {
        steps = res.data;
      })

      if(steps.length){

        this.processData = this.adapterData(steps, options);
        this.doClear();
        this.$nextTick(()=>{
          this.updateFlow(this.processData,this.updatePositionByAutoSort);
        })
      } else {
        this.doClear(true);
      }
    }, adapterData(steps, options) {
      if(typeof options=="string") {
        options = this.$d.adapterData(options);
        for(let i in steps){
          if(typeof steps[i].options=="string")
            steps[i].options= this.$d.adapterData(steps[i].options);
        }
      }
      let hasTempNode=false;
      let hasEndNode=false;
      let positions=options.positions||options;//对旧版本兼容支持
      for(let key in positions){
        let position=positions[key];
        if(position.type){//temp //end //针对临时节点和结束节点进行自动化补充数据
          if(this.setting.types.tempNode==position.type)
              hasTempNode=true;
            if(this.setting.types.endNode==position.type)
                hasEndNode=true;
          steps.push({id:key,type:position.type,name:this.setting.typeNames[position.type],
            options:{next:[]},uuid:key})
        }
      }
      if(!hasTempNode){
          let key=uuid();
          let temp=this.$d.merge(this.getFlowItemByType(this.setting.types.tempNode),{uuid:key,next:[]});
          steps.push(temp);
          positions[key]={left:this.getFlowItemInitLeft(),top:this.$el.offsetHeight-this.getFlowItemInitLeft()};
      }
        if(!hasEndNode){
            let key="end"
            let end=this.$d.merge(this.getFlowItemByType(this.setting.types.endNode),{uuid:key,next:[]});
            steps.push(end);
            positions[key]={left:this.$el.offsetWidth-this.getFlowItemInitLeft(),top:this.$el.offsetHeight-this.getFlowItemInitLeft()};
        }
      let index = 0;
      for (let i = 0; i < steps.length; i++) {//对非可视化布局进行兼容性支持
        let step = steps[i];
        if (!positions[step.uuid]) {
          positions[step.uuid] = {left: 10, top: 80 * index + 10};
          index++;
        }
      }
      let start=this.$d.merge(this.getFlowItemByType(this.setting.types.startNode),{uuid:this.setting.types.startNode,options:{next:[]}});
      if(options.firstStep)
        start.options.next.push(options.firstStep);
      steps.unshift(start);
      return {positions: positions, steps: steps};
    },sortFlowItemNext(tempFlowItem) {
      const preFlowItem = this.getFlowItemByUuid(tempFlowItem.prev[0]);
      if(preFlowItem) {//存在上个节点才操作
        if (this.isExpandFlowItem(preFlowItem)) {
          const nextList = preFlowItem.next.map((flowItemId) => {
            return this.getFlowItemByUuid(flowItemId);
          });
          nextList.sort((a, b) => {
            return a.left - b.left;
          });
          preFlowItem.next = nextList.map((flowItem) => {
            return flowItem.uuid;
          });
        // } else if (this.isIfFlowItem(preFlowItem)) {
        //   let nextIfFlowItem = this.getFlowItemByUuid(preFlowItem.next[0]);
        //   while (nextIfFlowItem==null&&preFlowItem.next.length>0){
        //     preFlowItem.next.shift();
        //     nextIfFlowItem = this.getFlowItemByUuid(preFlowItem.next[0]);
        //   }
        //   let nextElseFlowItem = this.getFlowItemByUuid(preFlowItem.next[1]);
        //   while (nextElseFlowItem==null&&preFlowItem.next.length>1){
        //     preFlowItem.next.splice(1,1);
        //     nextElseFlowItem = this.getFlowItemByUuid(preFlowItem.next[1]);
        //   }
        //   if (nextIfFlowItem&&nextElseFlowItem) {
        //     if (nextIfFlowItem.left < nextElseFlowItem.left) {
        //       preFlowItem.next = [nextIfFlowItem.uuid, nextElseFlowItem.uuid];
        //     } else {
        //       preFlowItem.next = [nextElseFlowItem.uuid, nextIfFlowItem.uuid];
        //     }
        //   }
        }
      }
    },getFlowItemInitLeft() {
      return 200;
    },createFlowItem(type, prev, options) {//创建过程节点
      options = options || {};
      let _uuid = uuid();
      if (type == "start")
        _uuid = "start";
      let flowItem = this.getFlowItemByType(type);
      let lastItemList = [];
      if (flowItem) {
        flowItem.prev = [];
        flowItem.next = [];
        flowItem.left = this.getFlowItemInitLeft();
        flowItem.top = FLOW_START_STEP_TOP;
        flowItem.uuid = _uuid;
        if (prev) {
          if (!Array.isArray(prev)) {
            prev = [prev];
          }
          flowItem.prev = prev;
          prev.forEach((p) => {
            let lastItem = this.flowList.find((item) => {
              return item.uuid === p;
            });
            if (lastItem) {
              lastItemList.push(lastItem);
            }
          });
        }
        if (lastItemList.length > 0) {
          if (lastItemList.length === 1) {
            let prevItem = lastItemList[0];
            if (options.offsetLeft) {
              flowItem.left = prevItem.left + options.offsetLeft;
            } else if (options.left) {
              flowItem.left = options.left;
            } else {
              flowItem.left = prevItem.left;
            }
            flowItem.top = prevItem.top + FLOW_STEP_LENGTH;
          } else {
          }
        }
        this.flowList.push(flowItem);
        this.$nextTick(() => {
          this.$options.jsPlumb.draggable(_uuid, {});
        });
      }
      return flowItem ? _uuid : null;
    },handleDeleteOnePrevOneNextFlowItem(flowItem) {
        let prevFlowItem = this.getFlowItemByUuid(flowItem.prev[0]);
        let nextFlowItem = this.getFlowItemByUuid(flowItem.next[0]);
        this.removeNode(flowItem);
        if(!this.isStartFlowItem(prevFlowItem))//剩开始节点时，不再保持连接
            prevFlowItem.next = [nextFlowItem.uuid];
        if(!this.isEndFlowItem(prevFlowItem))//剩结束节点时，不再保持连接
            nextFlowItem.prev = [prevFlowItem.uuid];
        this.connectNodeEndPoint(prevFlowItem);
    },plumbRepaintEverything() {
      this.$nextTick(() => {
        this.$options.jsPlumb.repaintEverything(true);
      });
    },handleAddFlowItem(type, tempFlowId) {//添加节点
      let newFlowItem = this.updateFlowItem(type, tempFlowId);//查找原始临时节点
      this.addTempFlowItem(tempFlowId);//添加一个后续节点
      return newFlowItem;
    },handleUpdateFlowItem(options) {// update flow item
      let {type, tempFlowId} = options;
      let preFlowItem = this.getFlowItemByUuid(tempFlowId);
      let newFlowItem = this.updateFlowItem(type, tempFlowId);
      if (type !== preFlowItem.type) {//如果类型不一样，则移除所有的后续连线
          this.$options.jsPlumb.removeAllEndpoints(newFlowItem.uuid);
          this.initNodeEndPoint(newFlowItem);
      }
    },getFlowIndex(flowUuid) {
      let index = this.flowList.findIndex((item) => {
        return item.uuid === flowUuid;
      });
      return index;
    },updatePositionByAutoSort() {
      if (this.toggleAutoSort) {
        this.$nextTick(() => {
          this.updatePosition();
        });
      }
    },updatePosition() {
      this.tempLayerMap = [];
      const startNode = this.flowList.find((flowItem) => {
        return this.isStartFlowItem(flowItem);
      });
      startNode.top = FLOW_START_STEP_TOP;
      startNode.left = this.getFlowItemInitLeft();
      this.tempLayerMap[0] = [startNode];
      this.layoutChild(startNode, 1);//界面布局
      const firstLift = this.getFirstLeft();//修正布局出现x轴负数的问题
      if (firstLift < 0) {
        const offset = -firstLift;
        this.offsetFlowListLeft(offset);
      }
      this.plumbRepaintEverything();
    },getFirstLeft() {
      let result = this.flowList[0].left;
      this.flowList.forEach((flowItem) => {
        if (result > flowItem.left) {
          result = flowItem.left;
        }
      });

      return result;
    },offsetFlowListLeft(offset) {
      this.flowList.forEach((flowItem) => {
        if(!this.isTempFlowItem(flowItem)&&!this.isEndFlowItem(flowItem))
          flowItem.left += offset;
      });
    },layoutChild(preFlow, layer) {
      const nextList = preFlow.next;
      const nextListLength = nextList.length;
      if (this.tempLayerMap[layer] === undefined) {
        this.tempLayerMap[layer] = [];
      }
      nextList.forEach((nextFlowUUid, index) => {//循环下个节点
        const flowItem = this.getFlowItemByUuid(nextFlowUUid);
        if (flowItem&&!this.isEndFlowItem(flowItem)&&!this.isTempFlowItem(flowItem)) {//结束节点、临时节点不调整位置
          flowItem.top = preFlow.top + FLOW_STEP_LENGTH;//计算顶部位置
          const startLeft =preFlow.left -(FLOW_LEFT_STEP_LENGTH_MAX * (nextListLength - 1)) / 2;//计算起点位置
          flowItem.left = startLeft + FLOW_LEFT_STEP_LENGTH_MAX * index;//计算每个节点的位置
          this.tempLayerMap[layer].push(flowItem);
          if (flowItem.next && flowItem.next.length > 0) {
            this.layoutChild(flowItem, layer + 1);//递归下一级
          }
        // }else{
        //   this.$d.debug().log("不要移动的点",flowItem.type,    flowItem.top ,  flowItem.left);
        }
      });
    },isOnePreOneNext(flowItem) { //只有一个后续节点
      return flowItem.prev.length==1&&flowItem.next.length==1;// === this.setting.groups.action;
    },isTempFlowItem(flowItem) {
      return flowItem.type === this.setting.types.tempNode;
    },isStartFlowItem(flowItem) {
      return flowItem.type === this.setting.types.startNode;
    },isEndFlowItem(flowItem) {
      return flowItem.type === this.setting.types.endNode;
    },isConditionFlowItem(flowItem) {
      return flowItem.groupType === this.setting.groups.condition;
    },isIfFlowItem(flowItem) {
      return flowItem.type === this.setting.types.switchNode;
    },isExpandFlowItem(flowItem) {
      return flowItem.type === this.setting.types.expandNode;
    },addTempFlowItem(preUuid, options) {
      let tempFlowUuid = this.createFlowItem(this.setting.types.tempNode, preUuid, options);
      return tempFlowUuid;
    }, getData(needCollectStep=true) {
      let result = {};
      let positions = {};
      let steps = [];
      let flowList = clone(this.flowList);
      flowList.forEach((flowItem) => {
        positions[flowItem.uuid] = {left: flowItem.left, top: flowItem.top};
        //对临时节点和结束节点进行保存
        if (this.isEndFlowItem(flowItem)||this.isTempFlowItem(flowItem))
          positions[flowItem.uuid].type = flowItem.type;
        if(needCollectStep) {
          let step = {id: flowItem.id,type: flowItem.type,uuid: flowItem.uuid,
            name: flowItem.stepName,options: {next: flowItem.next}};
          steps.push(step);
        }
      });
      result.positions = positions;
      result.steps = steps;
      result.firstStep=flowList[0].next[0]||"";
      return result;
    },
    //删除行为
    deleteFlowItem(flowItem) {
        let preFlowItems = [];
        for (let i in flowItem.prev) {//需要考虑可能是个汇集点
            let stepItem = this.getFlowItemByUuid(flowItem.prev[i]);
            if (stepItem.groupType == this.setting.groups.action || stepItem.groupType == this.setting.groups.condition)
                preFlowItems.push(stepItem);
        }
        if (this.isOnePreOneNext(flowItem)) {
            this.handleDeleteOnePrevOneNextFlowItem(flowItem);
        }else{//如果删除的是 多条件，则仅执行removeEndPoint即可
          this.removeNode(flowItem);
        }
        this.handleDelStep(flowItem);
        this.handleSaveData(preFlowItems);
    },handleUpdateNode(e) {
      this.updateMemoryList();
      if(e.params) {
          let flowItem = null;
          flowItem = this.getFlowItemByUuid(e.params.flowItem.uuid);
          flowItem.stepName = e.form.name;
          if (this.isTempFlowItem(flowItem)) { //如果是临时节点，说明是新增
              let newFlowItem = this.handleAddFlowItem(e.form.type, flowItem.uuid);
              this.initNodeEndPoint(newFlowItem);
              this.updatePositionByAutoSort();//需要重新排序
          } else {//更新流程节点
              this.handleUpdateFlowItem({type: e.form.type, tempFlowId: e.params.flowItem.uuid});
          }
          if (flowItem)
              flowItem.id = e.form.id;
          this.handleSaveData([flowItem])
      }
    }, updateFlowItem(type, preId) {//更新节点信息
      let tempFlowItem = this.getFlowItemByUuid(preId);//查找旧的节点
      let newFlowItem = this.getFlowItemByType(type);//创建新的节点
      for (let key in newFlowItem) {
        tempFlowItem[key] = newFlowItem[key];
      }
      return tempFlowItem;
    },handleChangeFlowPosition(flowItem, evt) {
      const target = evt.currentTarget;
      let firstTime = target.dataset.firstTime;
      let lastTime = new Date().getTime();
      let left = ("" + target.style.left).replace("px", "");
      let top = ("" + target.style.top).replace("px", "");
      target.style.zIndex = 1;
      const _isClick = lastTime - firstTime < 200;
      if (!_isClick) {
        this.updateMemoryList();
      }
      flowItem.left = Number(left);
      flowItem.top = Number(top);
      if (!_isClick) {
        this.sortFlowItemNext(flowItem);
      }
      target.dataset.isClick = _isClick;
    }, handleFlowMoveDown(evt) {
      const target = evt.currentTarget;
      target.style.zIndex = 2;
      target.dataset.firstTime = new Date().getTime();
    }, handleEditFlowItem(flowItem, event) {
      let target = event.currentTarget;
      if (target.dataset.isClick === "true") {
        this.showDialog(flowItem);//点击临时节点进行新增，或者点击已有节点进行编辑
      }
    },//缩放
    handleMinusCanvas() {
      if (this.canvasDataRoom <= 50) {
        return;
      }
      this.canvasDataRoom = this.canvasDataRoom - 10;
    }, handlePlusCanvas() {
      if (this.canvasDataRoom === 100) {
        return;
      }
      this.canvasDataRoom = this.canvasDataRoom + 10;
    },//工具栏
    handleSort() {
      this.updatePosition();
    }, handleToggleAutoSort() {
      this.toggleAutoSort = !this.toggleAutoSort;
      this.updatePositionByAutoSort();
    }, handleUndo() {
      if (!this.canUndo) {
        return;
      }
      if (MEMORY_LIST.length > 0) {
        const tempItem = MEMORY_LIST.pop();
        this.doClear();
        this.$options.jsPlumb.deleteEveryEndpoint();
        this.$nextTick(() => {
          this.updateFlow(tempItem, this.plumbRepaintEverything);
        });
      }
      this.updateCanUndoBtn();
    }, updateCanUndoBtn() {
      this.canUndo = MEMORY_LIST.length > 0;
    }, handleClear() {
      this.$confirm("确定要清空数据,重做吗？此操作会直接更新数据库！！", "警告", {confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"})
      .then(() => {
        this.doClear(true);
      })
    }, handleGetData() {
      const positionData = this.getData();
      const formatter = new JSONFormatter(positionData, 3);
      this.dialogVisible = true;
      this.$nextTick(() => {
        this.$refs.preShow.appendChild(formatter.render());
      });
    },handleDelStep(flowItem,showMessage=true){
      if(flowItem.id&&flowItem.id==parseInt(flowItem.id)) {//有有效的id才执行删除
        this.$http({url: this.$store.state.api.oa.delStep,data: {id: flowItem.id}
        }).then((res) => {
          if (showMessage)
            this.$_page_message(res);
        })
      }
    },handleSaveFlowItemOption(flowItem,showMessage=true){
      if(flowItem.groupType==this.setting.groups.action||flowItem.groupType==this.setting.groups.condition) {
          this.$http({url: this.$store.state.api.oa.saveStepOptions,
              data: {id: flowItem.id, options: JSON.stringify({next: flowItem.next})}
          }).then((res) => {
              if (showMessage)
                  this.$_page_message(res);
          })
      }
    },handleSaveData(flowItems) {
      if (flowItems) {
        for (let i in flowItems) {
          let flowItem = flowItems[i];
          if(flowItem&&flowItem.groupType==this.setting.groups.action||flowItem.groupType==this.setting.groups.condition)
            this.handleSaveFlowItemOption(flowItem,false);
        }
      }
      let data=this.getData(false);
      delete data.steps;
      let firstStepId="";
      if(data.firstStep){//如果有第一个步骤，则查找第一个步骤的id进行回填
        let item= this.getFlowItemByUuid(data.firstStep);
        firstStepId=item.id;
      }
      this.$http({url: this.$store.state.api.oa.savePosition,
        data: {id: this.proId || this.params.proId, steps: JSON.stringify(data),firstStep:firstStepId}
      }).then((res) => {
        this.$_page_message(res);
      })
    }, $_page_message(result) {
      let key=result.success?"success":"error";
      if(this.messageHandler[key]) {
        clearTimeout(this.messageHandler[key]);
        delete this.messageHandler[key];
      }
      this.messageHandler[key]=setTimeout(()=> {
        this.$notify({
          title:result.title||(result.success ? "成功" : "失败"),
          duration: 5000,
          message: result.msg,
          type:result.type||(key)
        });
      },100);
    }, handleDeleteFlowItem(flowItem) {
      //todo 需要考虑的是，是否提供仅删除当前节点功能，此功能可参考链表设计
      this.loading=true;
      let message="确定要删除吗？";
      if(flowItem.groupType==this.setting.groups.condition){
        message+="删除将会连带删除下面所有节点";
      }
      this.$confirm(message , "提示",
              {confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"}).then(() => {
                this.deleteFlowItem(flowItem);
                this.updateMemoryList();
                this.updatePositionByAutoSort();
                this.$nextTick(()=>{
                    this.loading=false;
                });
              }).catch((e) => {
          this.loading=false;
          console.warn(e);
      });
    }, handleFlowMoveStart(evt) {//测边框
      let item = evt.item;
      let dataset = item.dataset;
      this.movingFlowItem = {group: dataset.group, type: dataset.type};
    }, handleFlowMoveItem(evt) { //移动中计算目标对象节点
      let $to = evt.to;
      let $dragged = evt.dragged;
      if ($dragged.dataset.id === this.setting.types.startNode) {
        return false;
      }
      if (!this.movedFlowItem) {
        this.movedFlowItem = $to;
        addClass($to, "is-active");
      }else if (this.movedFlowItem && this.movedFlowItem !== $to) {   // change target item
        removeClass(this.movedFlowItem, "is-active");
        this.movedFlowItem = $to;
        addClass($to, "is-active");
      }
    }, handleFlowMoveEnd(evt) {
      if (this.movedFlowItem) {
        removeClass(this.movedFlowItem, "is-active");
        if (evt.to !== this.movedFlowItem) {//如果对象不对则放弃操作
          this.movedFlowItem = undefined;
          this.movingFlowItem = undefined;
          return;
        }
        let obj = {
          type: this.movingFlowItem.type, id: this.movedFlowItem.id,
          groupType: this.movingFlowItem.group
        };
        //如果是插入到流程中，则会有来源和去向，则执行插入状态
        if (this.movedFlowItem.dataset.source && this.movedFlowItem.dataset.target) {
          obj.isBetween = true;
          obj.flowPreUuid = this.movedFlowItem.dataset.source;
          obj.flowNextUuid = this.movedFlowItem.dataset.target;
          delete obj.flowUuid;
        }
        this.showDialog(obj);
      }
      this.movedFlowItem = undefined;
      this.movingFlowItem = undefined;
    },showDialog(flowItem) {//弹出组件交互窗口，会根据不同组件显示不同的界面
        this.$d.debug().log("链接节点", flowItem);
        if (!flowItem.uuid) //拖拽进来的没有uuid
            flowItem.uuid = flowItem.id;
        let id = flowItem.id;
        if (flowItem.groupType == this.setting.groups.flow) {//工作流的开始与结束，不能进行配置
            if (flowItem.type == this.setting.types.endNode) {//如果是结束节点，直接加入即可
                this.updateFlowItem(flowItem.type, id);
                this.handleSaveData();//交互行为需要默认保存一下
            }
            return;
        }
        this.dialog.width = "750px";
        this.dialog.height = "600px";
        this.dialog.component = "FormView"
        let type = "node";
        if (flowItem.type != "temp")
            type = flowItem.type;
        this.dialog.params = {fid: "setProcessStep", type: type, code: flowItem.uuid, id: id,
          proId: this.proId || this.params.proId}
        if (id == parseInt(id))
            this.dialog.params.id = id;
        else
            delete this.dialog.params.id;
        this.dialog.params.flowItem = flowItem;
        this.dialog.visible = true;
    },getFlowItemByUuid(uuid) {//查找流程节点
      let flowItem = this.flowList.find((item) => {
        return item.uuid === uuid;
      });
      return flowItem;
    }, getFlowItemByType(type) {  //marked
      let result = null;
      let item = this.setting.items.find((item) => {
        return item.type === type;
      });
      if (item) {
        result = clone(item);
      }
      return result;
    },doClear(needInit) {
      this.flowList=[];
      this.$options.jsPlumb.deleteEveryEndpoint();
      if(needInit)
        this.initFlow();
    },
    //重构的初始化
    initJsPlumb() {
      let _this = this;
      this.$options.jsPlumb.ready(() => {
        _this.jsPlumbInit = true;
        _this.$options.jsPlumb.importDefaults(_this.setting.options);
        // //todo: 监听生成连线时触发此事件
        _this.$options.jsPlumb.bind("connection", (evt)=> {
          if(_this.loaded) {
            let flowItem = _this.getFlowItemByUuid(evt.sourceId);
            if(!this.isConditionFlowItem(flowItem)&&flowItem.next.length>0){//增加对部分数据异常的兼容处理
              flowItem.next=[];
            }
            if (flowItem.next.indexOf(evt.targetId) < 0) {
              if(this.isIfFlowItem(flowItem)){
                 let uuid= evt.sourceEndpoint.getUuid();
                 if(uuid.indexOf("out-1")>0){//否
                   flowItem.next[1]=evt.targetId;
                 }else{
                   flowItem.next[0]=evt.targetId;
                 }
              }else {
                flowItem.next.push(evt.targetId);
              }
              _this.handleSaveData([flowItem]);
            }
          }
        });
        _this.$options.jsPlumb.bind("connectionDetached", (evt)=> {
          if(_this.loaded) {
            let flowItem = _this.getFlowItemByUuid(evt.sourceId);
            if (flowItem!=null) {
              if(this.isConditionFlowItem(flowItem)){//增加对部分数据异常的兼容处理
                let index=flowItem.next.indexOf(evt.targetId);
                if(this.isIfFlowItem(flowItem)){
                  if(index>1) {//超过2个的处理
                    flowItem.next.splice(index, 1);
                  }else{
                    flowItem.next[index]=null;
                  }
                }else{
                  flowItem.next.splice(index, 1);
                }
              }else{
                flowItem.next=[];
              }
              _this.handleSaveData([flowItem])
            }
          }
        });
        _this.$options.jsPlumb.bind('click', (conn, originalEvent)=> {
          _this.$confirm('确定删除所点击的链接吗？').then(()=>{
            _this.$options.jsPlumb.deleteConnection(conn);
          });
        });
        //beforeDetach   删除连线前事件
        _this.$options.jsPlumb.bind('beforeDrop', (info)=> {   // 当链接建立
          if(_this.loaded) {
            let result = info.sourceId != info.targetId
            if (!result)
              _this.$_page_message({msg: "禁止自己关联自己！", title: "警告", type: "warning"});
            return result;
          }
          return true;
        });
      });
    },initFlow() {  // create startFlow and tempFlow
      this.processData= this.getDefaultData();
      this.$nextTick(()=> {
        this.updateFlow(this.processData);
      });
    },getDefaultData(){
      let _uuid=uuid();
      let _left=this.getFlowItemInitLeft();
      let start=this.$d.merge(this.getFlowItemByType(this.setting.types.startNode),{uuid:"start",next:[_uuid]});
      let temp=this.$d.merge(this.getFlowItemByType(this.setting.types.tempNode),{uuid:_uuid,next:[]});
      let end=this.$d.merge(this.getFlowItemByType(this.setting.types.endNode),{uuid:"end",next:[]});
      let positions={start:{left:_left,top:FLOW_START_STEP_TOP},
        end:{left:this.$el.offsetWidth-_left,top:this.$el.offsetHeight-_left}};
      positions[_uuid]={left:_left,top:FLOW_START_STEP_TOP+FLOW_STEP_LENGTH};
      return {positions:positions,steps:[start,temp,end]}
    },updateFlow(editItem, callback) {
      this.flowList = [];
      let positions = editItem.positions;
      let steps = editItem.steps;
      let flowList = [];
      if (steps.length > 0) {
        steps.forEach((step) => {
          let flowItem = this.getFlowItemByType(step.type);
          if (!flowItem) {//如果不属于流程组件，则忽略
            return;
          }
          flowItem.next = step.options && step.options.next || [];
          if(this.isConditionFlowItem(flowItem)){
            if(this.isIfFlowItem(flowItem)){
              if(flowItem.next.length>2)//如果条件节点的下一个数量大于2，则修复
                flowItem.next=[flowItem.next[0],flowItem.next[1]];
            }
          }else{
              if(flowItem.next.length>1)//如果普通节点的下一个数量大于1，则修复
                flowItem.next=[flowItem.next[0]];
          }
          flowItem.prev = [];
          flowItem.id = step.id;
          flowItem.uuid = step.uuid;
          let position = positions[step.uuid];
          if (position) {
            flowItem.left = position.left;
            flowItem.top = position.top;
          }
          flowItem.stepName = step.name;
          flowList.push(flowItem);
        });
        flowList.forEach((item) => {//链接上个节点  //排序需要用到上个节点
          if (item.next.length > 0) {
            item.next.forEach((uuid) => {
              let nextItem = flowList.find((tempItem) => {
                return tempItem.uuid === uuid;
              });
              if (nextItem && nextItem.prev.indexOf(item.uuid) === -1) {
                nextItem.prev.push(item.uuid);
              }
            });
          }
        });
      }
      this.flowList = flowList;
      for (let i in this.flowList) {
        this.initNode(this.flowList[i], i == this.flowList.length - 1);
      }
      this.$nextTick(() => {
        callback && callback();
      });
    },updateMemoryList() { //更新undo的内存
      const tempItem = this.getData();
      if (MEMORY_LIST.length > 10) {
        MEMORY_LIST.shift();
      }
      MEMORY_LIST.push(tempItem);
      this.updateCanUndoBtn();
    },initNode(flowItem,isLast){
      this.$nextTick(() => {//拖动支持要等元素布局结束后才能链接
        this.$options.jsPlumb.draggable(flowItem.uuid, {});//当前节点可拖动支持
        this.initNodeEndPoint(flowItem,isLast);
      });
    },initNodeEndPoint(flowItem,isLast){
        if (flowItem.enter)
            this.setEndPoint(flowItem.uuid, this.$d.merge(this.setting.pointStyle, flowItem.enter));
        if (flowItem.out)
            for (let i in flowItem.out)
                this.setEndPoint(flowItem.uuid, this.$d.merge(this.setting.pointStyle, flowItem.out[i]));
        if (flowItem.next)
            this.$nextTick(() => {//链接各个endpoint的连线必须等endpoint布局结束才能进行
                this.connectNodeEndPoint(flowItem);
                if(isLast===true) {
                  this.loaded = true;
                  // this.$d.debug().log("初始化完成")
                }
            });
    },connectNodeEndPoint(flowItem){
        for (let i in flowItem.next) {
            let index=this.isIfFlowItem(flowItem)?i:0;
            if(flowItem.out[index])
              this.connectEndPoint(flowItem.uuid + "-" + flowItem.out[index].code, flowItem.next[i] + "-in");
        }
    },setEndPoint(uuid,config) {
        this.$options.jsPlumb.addEndpoint(uuid, {uuid: uuid + '-' + config.code}, config)
    },connectEndPoint(from,to){
      if(from&&to)//头尾都存在，才进行关联
        // this.$options.jsPlumb.connect({ source:from,target: to },this.setting.lineStyle);
      this.$options.jsPlumb.connect({ uuids:[from, to] },this.setting.lineStyle);
    },removeNode(flowItem){
      let index = this.getFlowIndex(flowItem.uuid);
      this.$options.jsPlumb.removeAllEndpoints(flowItem.uuid);
      this.flowList.splice(index, 1);
    }
  }
}
</script>
<style lang="sass">
@import "workflow"
</style>
