<template>
  <div class="main-div" style="width:100%;height:100%;"  ref="mainDiv">
    <Layout>
      <!--   左侧工具栏   -->
      <Sider :width="180">
        <div id="stencil"></div>
      </Sider>
      <Layout>
        <!--   中间流程图  -->
        <Content>
          <div id="container" ref="container" style="width:100%;height:100%"/>
        </Content>
        <!--   底部按钮   -->
        <Footer class="footer">
          <div class="tools">
            <Poptip trigger="hover" >
              <Button icon="ios-bulb"></Button>
              <div slot="content" style="background: #f8f8f9">
                <Card title="快捷键说明" icon="ios-options" :padding="0" shadow style="width: 300px;">
                  <CellGroup>
                    <Cell title="整体移动画布" extra="右键按住空白处拖动" />
                    <Cell title="多选" extra="左键按住空白处拖动" />
                    <Cell title="放大">
                      <div slot="extra" style="display: flex;align-items: center">
                        <div class="key">Ctrl</div>
                        <div class="key">Alt</div>
                        <div class="key">+</div>
                        <div style="padding-left:3px">或</div>
                        <div class="key">Ctrl</div>
                        <div style="padding-left:3px">+ 鼠标滚轮</div>
                      </div>
                    </Cell>
                    <Cell title="缩小">
                      <div slot="extra" style="display: flex">
                        <div class="key">Ctrl</div>
                        <div class="key">Alt</div>
                        <div class="key">-</div>
                        <div style="padding-left:3px">或</div>
                        <div class="key">Ctrl</div>
                        <div style="padding-left:3px">+ 鼠标滚轮</div>
                      </div>
                    </Cell>
                    <Cell title="复制">
                      <div slot="extra" style="display: flex">
                        <div class="key">Ctrl</div>
                        <div class="key">C</div>
                      </div>
                    </Cell>
                    <Cell title="剪切">
                      <div slot="extra" style="display: flex">
                        <div class="key">Ctrl</div>
                        <div class="key">X</div>
                      </div>
                    </Cell>
                    <Cell title="粘贴">
                      <div slot="extra" style="display: flex">
                        <div class="key">Ctrl</div>
                        <div class="key">V</div>
                      </div>
                    </Cell>
                    <Cell title="撤销">
                      <div slot="extra" style="display: flex">
                        <div class="key">Ctrl</div>
                        <div class="key">Z</div>
                      </div>
                    </Cell>
                    <Cell title="撤回撤销">
                      <div slot="extra" style="display: flex">
                        <div class="key">Ctrl</div>
                        <div class="key">Shift</div>
                        <div class="key">Z</div>
                      </div>
                    </Cell>
                    <Cell title="全选">
                      <div slot="extra" style="display: flex">
                        <div class="key">Ctrl</div>
                        <div class="key">Alt</div>
                        <div class="key">A</div>
                      </div>
                    </Cell>
                    <Cell title="删除">
                      <div slot="extra" style="display: flex">
                        <div class="key">Del</div>
                      </div>
                    </Cell>

                  </CellGroup>
                </Card>
              </div>
            </Poptip>
            <Tooltip content="导出PNG">
              <Button icon="ios-camera" @click="()=>{this.graph.exportPNG('',{padding:50})}"></Button>
            </Tooltip>
            <Tooltip content="导出JSON">
              <Button icon="ios-cloud-download" @click="exportJson"></Button>
            </Tooltip>
            <Tooltip content="导入JSON">
              <Upload action="" accept=".json" :before-upload="uploadJson">
                <Button icon="ios-cloud-upload"></Button>
              </Upload>
            </Tooltip>
          </div>
          <div>
            <DkButton style="margin-right: 10px;" ref="save" type="primary" @click="save">{{ $t('save') }}</DkButton>
            <DkButton ref="close" @click="close('/process/process-flow/index')">{{ $t('close') }}</DkButton>
          </div>
        </Footer>
      </Layout>
    </Layout>
    <transition-group name="fade">
      <!--  节点弹窗  -->
      <NodeModal key="node" v-if="process.nodeModal.display"
                 :dataKindList="dataKindList"
                 :productGradeList="productGradeList"
                 :jobList="jobList"
                 :shopList="shopList"
                 :dictionaryDataList="dictionaryDataList"/>
      <!--  连线弹窗  -->
      <EdgeModal key="edge" v-if="process.edgeModal.display"
                 :dataKindList="dataKindList"
                 :productGradeList="productGradeList.filter(it=>![$config.gradeKindType.decrease,$config.gradeKindType.inferior].includes(it.gradeKind))"/>
    </transition-group>
  </div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import NodeModal from '_c/business/process/node-modal/node-modal'
import EdgeModal from '_c/business/process/edge-modal/edge-modal'

const insertCss = require('insert-css')
const zt1 = require('./data/zt1.json')
import Process from '@/view/process/process-flow/config/ProcessConfig'
import { formMixin } from '@/mixins/form'
import FileSaver from 'file-saver'

export default {
  name: 'process-setting',
  mixins: [formMixin],
  components: { NodeModal, EdgeModal },
  props: {
    type: {
      type: String,
      default: 'edit'
    },
  },
  data() {
    return {
      graph: null,//图形对象
      exportData: undefined,
      importValue: undefined,
      dataKindList: [],//基础数据
      productGradeList: [],//产品等级
      jobList: [],//工种
      dictionaryDataList:[],//数据字典
      shopList:[],//车间
    }
  },
  created() {
    this.setCurrentNodeId(undefined)
    this.setCurrentEdgeId(undefined)
    this.setProcessNodeModalDisplay(null)
    this.setProcessEdgeModalDisplay(null)
  },
  mounted() {
    //创建流程图对象
    this.graph = Process.init(this.$refs.container)
    //监听删除节点
    this.bindDeleteNodes()
    // this.graph.zoomToFit({ maxScale: 1 });// 将画布中元素缩小或者放大一定级别，让画布正好容纳所有元素，可以通过 maxScale 配置最大缩放级别
    // this.graph.centerContent(); // 将画布中元素居中展示
  },
  computed: {
    ...mapState(['process']),
  },

  methods: {
    ...mapMutations(['setProcessNodeModalDisplay', 'setProcessEdgeModalDisplay', 'setCurrentNodeId', 'setCurrentEdgeId']),
    /**
     * @desc   : 导出json方法
     * @author : 张潇木
     * @date   : 2023/5/10 9:52
     */
    exportJson(){
      // JSON.stringify(data, null, 2): 格式化json数据 方便阅读
      FileSaver.saveAs(new Blob([JSON.stringify(this.graph.toJSON(),null,2)], {type: 'application/json'}), 'flow.json')
    },
    /**
     * @desc   : 导入json
     * @author : 张潇木
     * @date   : 2023/5/10 10:17
     */
    uploadJson(file){
      //检验文件类型
      if(file.type!='application/json'){
        console.log('file.type',file.type)
        this.$Message.error('导入失败，请上传JSON类型的文件')
        return false
      }

      //读取json
      let reader = new FileReader()
      reader.readAsText(file)
      reader.onload = ((e) => {
        console.log('e',e)
        let uploadData = JSON.parse(e.target.result)
        console.log('uploadData',uploadData)
        if(!uploadData.cells){
          //校验数据结构
          this.$Message.error('导入失败，请检查JSON文件数据结构')
        }else{
          //根据数据绘制图形
          this.graph.fromJSON(uploadData)
          // region 暂时不考虑复杂场景，导入数据不需处理
          // let allCells=this.graph.getCells()
          // this.graph.cut(allCells)
          // this.graph.paste({ offset: 0 })
          // this.graph.cleanSelection()
          // endregion
          this.$Message.success('导入成功')
        }
      })


      return false
    },
    /**
     * @desc   : 获取基础数据
     * @author : 张潇木
     * @date   : 2023/1/31 13:42
     */
    initData() {
      // 获取数据类别
      this.excute(this.$service.commonService, this.$service.commonService.getDataKind, {}).then(res => {
        if (res.code === this.$config.SUCCESS_CODE) {
          this.dataKindList = res.data
        }
      })
      // 获取产品等级
      this.excute(this.$service.productGradeService, this.$service.productGradeService.selectByCond, {
        flgValid: true,
        ftyId: this.$store.state.user.ftyId
      }).then(res => {
        if (res.code === this.$config.SUCCESS_CODE) {
          this.productGradeList = res.data.list
        }
      })
      // 获取工种信息
      this.excute(this.$service.jobService, this.$service.jobService.selectByCond, {
        flgValid: true,
        ftyId: this.$store.state.user.ftyId
      }).then(res => {
        if (res.code === this.$config.SUCCESS_CODE) {
          this.jobList = res.data.list
        }
      })
      //数据字典
      this.excute(this.$service.commonService, this.$service.commonService.getDictionaryData, {
        ftyId: this.$store.state.user.ftyId,
      }).then(res => {
        if (res.code === this.$config.SUCCESS_CODE) {
          this.dictionaryDataList = res.data
        }
      })
      //数据字典
      this.excute(this.$service.commonService, this.$service.commonService.getFactorySpace, {
        ftyId: this.$store.state.user.ftyId,
        spaceKind: this.$config.factorySpace.shop
      }).then(res => {
        if (res.code === this.$config.SUCCESS_CODE) {
          this.shopList = res.data
        }
      })
    },
    /**
     * @desc   : 提交保存
     * @author : 张潇木
     * @date   : 2023/2/3 9:30
     */
    save() {
      //节点组大小和是否展开存在data中
      this.graph.getNodes().filter(it => it.shape == 'groupNode').forEach((it) => {
        //如果是收起状态，存展开大小，如果展开，存当时的实际大小
        it.setData({
          'expandSize': it.isCollapsed() ? it.getExpandSize() : it.getSize(),
          'isCollapsed': it.isCollapsed()
        })
      })
      //转json
      this.formData.flowLayout = this.graph.toJSON()
      //流程整体校验
      if (!this.validData()) return false
      //发送保存网络请求
      this.excute(this.$service.processService, this.$service.processService.processSetting, this.formData).then(res => {
        if (res.code === this.$config.SUCCESS_CODE) {
          // 提示信息
          this.$Message.success(this.$t('I_001', { 'param': this.$v(this.type) }))
          // 编辑关闭窗体
          this.close()
        } else {
          // 如果不成功就提示错误信息
          this.$Message.warning(res.message)
        }
      })
    },
    /**
     * @desc   : 校验数据
     * @author : 张潇木
     * @date   : 2023/2/27 11:12
     */
    validData() {
      let allNodes = this.graph.getNodes().filter(it => it.shape == 'custom-image')
      let allEdges = this.graph.getEdges()
      let startNodes = allNodes.filter(it=>it.getData()?.flowKind===this.$config.flowKind.start)
      let endNodes = allNodes.filter(it=>it.getData()?.flowKind===this.$config.flowKind.end)
      //成检交接节点
      let nodeSysTagsCheck =allNodes.filter(it=>it.getData()?.nodeSysTags?.includes(this.$config.nodeSysTags.check))
      if(allNodes.length<2){
        this.$Message.warning('流程中应至少包含2个节点')
        return false
      }
      if(allEdges.length<1){
        this.$Message.warning('流程中应至少包含1条线')
        return false
      }
      if(startNodes.length<1){
        this.$Message.warning('流程中应至少包含1个开始节点')
        return false
      }
      if(endNodes.length<1){
        this.$Message.warning('流程中应至少包含1个结束节点')
        return false
      }
      if(nodeSysTagsCheck.length<1){
        this.$Message.warning('流程中应至少包含1个 [工序标签] 为 [成检交接] 的节点')
        return false
      }
      console.log('allNodes',allNodes)
      //检查所有开始节点是否能够连接到结束节点
      let startNodesNoCloseLoop=startNodes.filter(it=>{
        let endCount = this.graph.getSuccessors(it)?.filter(it=>it.getData()?.flowKind===this.$config.flowKind.end)
        if (endCount<1) {
          this.$Message.warning('开始节点为 [' + it.getData()?.nodeName + '] 的流程未闭环，请检查')
          return true
        }
      })
      if (startNodesNoCloseLoop.length > 0) return false

      //检查所有结束节点是否都有开始节点(回收不校验)
      let endNodesNoCloseLoop=endNodes.filter(it=>it.getData()?.nodeKind!==this.$config.nodeKind.recover).filter(it=>{
        let startCount = this.graph.getPredecessors(it)?.filter(it=>it.getData()?.flowKind===this.$config.flowKind.start)
        if (startCount<1) {
          this.$Message.warning('结束节点为 [' + it.getData()?.nodeName + '] 的流程未闭环，请检查')
          return true
        }
      })
      if (endNodesNoCloseLoop.length > 0) return false

      //节点的单个校验未通过
      let invalidNodes =allNodes.filter(it => {
        if (!it.getData().isValid) {
          this.$Message.warning('[' + it.getData()?.nodeName + '] 节点设置有误，请检查')
          return true
        }
      })
      if (invalidNodes.length > 0) return false
      return true
    },

    /**
     * @desc   : 删除节点特殊控制，如果是成型模型，且被成型线绑定了，则不能删除
     * @author : 张潇木
     * @date   : 2023/3/29 13:26
     */
    bindDeleteNodes(){
      this.graph.bindKey(['backspace','del'], () => {
        const cells = this.graph.getSelectedCells()
        if (cells.length) {
          //取成型节点id
          let shapingNodes=cells.filter(it=>it.getData()?.nodeKind === this.$config.nodeKind.shaping).map(it=> {return it.getData()?.nodeId})
          if(shapingNodes.length){
            //查询是否存在绑定的成型线
            this.excute(this.$service.processService, this.$service.processService.getBindMolding, shapingNodes).then(res => {
              if (res.code === this.$config.SUCCESS_CODE&&res.data.length) {
                this.$Message.warning('无法删除已被成型线绑定的节点')
              }
              else{
                this.graph.removeCells(cells)
              }
            })
          }else{
            this.graph.removeCells(cells)
          }
        }
      })
    },
    /**
     * @desc   : 查询明细
     * @author : 张潇木
     * @date   : 2023/2/3 16:47
     */
    detail(id) {
      return this.excuteNoParam(this.$service.processService, this.$service.processService.selectById, [id], false)
    },
    /**
     * @desc   : 初始化
     * @author : 张潇木
     * @date   : 2023/2/7 8:50
     */
    setValuesByEdit(data) {
      this.formData = data
      //初始化数据
      this.graph.fromJSON(this.formData.flowLayout || { cells: [] })
      //再重新渲染群组大小
      this.graph.getNodes().filter(it => it.shape == 'groupNode').forEach((it) => {
        // console.log('group-data',it.getData())
        //解构
        let { isCollapsed, expandSize } = it.getData()
        //设置展开大小
        it.setExpandSize(expandSize)
        //如果不是收起的，展开并还原大小
        if (!isCollapsed) {
          it.toggleCollapse(isCollapsed)
        }
      })
    },
  }
}
</script>
<style scoped>
#container {
  display: flex;
  border: 1px solid #dfe3e8;
}

#stencil {
  width: 180px;
  height: 100%;
  position: relative;
  border-right: 1px solid #dfe3e8;
}

#graph-container {
  width: calc(100% - 180px);
  height: 100%;
}

.x6-widget-stencil {
  background-color: #fff;
}

.x6-widget-stencil-title {
  background-color: #fff;
}

.x6-widget-stencil-group-title {
  background-color: #fff !important;
}

/deep/ .x6-widget-transform {
  margin: -1px 0 0 -1px;
  padding: 0px;
  border: 1px solid #239edd;
}

/deep/ .x6-widget-transform > div {
  border: 1px solid #239edd;
}

/deep/ .x6-widget-transform > div:hover {
  background-color: #3dafe4;
}

/deep/ .x6-widget-transform-active-handle {
  background-color: #3dafe4;
}

/deep/ .x6-widget-transform-resize {
  border-radius: 0;
}

/deep/ .x6-widget-selection-inner {
  border: 1px solid #239edd;
}

/deep/ .x6-widget-selection-box {
  border: 0 !important;
}

/*@keyframes ant-line {*/
/*  to {*/
/*    stroke-dashoffset: -1000*/
/*  }*/
/*}*/
.snap-line-color {
  color: black !important;
  background: black !important;
}

.ivu-layout-has-sider {
  height: 100%;
}

.header {
  height: 30px;
  background: #ededed;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/deep/ .ivu-layout-header {
  height: 52px !important;
  line-height: 52px !important;
}

.footer {
  /*background: #fff;*/
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 30px;
}
.key{
  align-items: center;
  background-image: linear-gradient(180deg,#f2f4f5,#fff);
  border: 1px solid #d1d5d9;
  border-radius: 4px;
  box-shadow: 0 1px 0 0 #b8bfc4, inset 0 2px 0 0 #fff;
  color: #666;
  display: flex;
  font-size: 12px;
  font-weight: 500;
  height: 20px;
  justify-content: center;
  margin-left: 5px;
  min-width: 20px;
  padding: 0 5px;
}
/deep/ .ivu-card-head{
  padding: 7px 8px!important;
}
/deep/ .ivu-cell{
  padding: 3px 8px!important;
}
/deep/ .ivu-cell-group{
  padding: 2px 0 !important;
}
/deep/ .ivu-btn-icon-only{
  padding: 0 !important;
  font-size: 28px!important;
  width: 48px!important;
  height: 48px!important;
  /*border: none!important;*/
  /*border-radius:0px!important;*/
}
/deep/ .ivu-tooltip{
  height: 48px!important;
}
/deep/ .custom-html{
  height: 100%;
  width: 100%;
  display: flex;
  border:solid 1px #E9F1FF;
}
/deep/ .custom-html-left{
  background: #E9F1FF;
  height: 100%;
  width: 40%;
  border: solid 2px #E9F1FF;
  border-right: none;
  border-top-left-radius: 10px;
  border-bottom-left-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1);

}
/deep/ .custom-html-right{
  background: #fff;
  height: 100%;
  /*width: 60%;*/
  border: solid 2px #fff;
  border-left: none;
  border-top-right-radius: 10px;
  border-bottom-right-radius: 10px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1);
  overflow: hidden;
  flex: 1;
}
/deep/ .custom-html-right-title{
  font-size: 14px;
  font-weight: bold;
  padding-left: 10px;
  width:  100%;
  height: 50%;
  padding-top: 6px;
  align-items: center;
  color: #4C4C4C;
  overflow: hidden;
  text-overflow:ellipsis;
  white-space: nowrap;
}
/deep/ .custom-html-right-text{
  padding: 3px 0 0 10px ;
  width:  100%;
  height: 50%;
  font-size: 10px;
  display: flex;
  align-items: self-start;
  color: #B6B6B6;
}

</style>


