gojs 绘制UML连线图

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_28529373/article/details/89947151
<template>
  <div :id='boxId' class="er-box" v-loading="loadingChange" element-loading-text="拼命加载中">
    <div style="z-index: 1;" v-if="fullShow">
      <el-row :gutter="20">
        <el-col :span="10">
            <template>
              <el-select v-model="keyword"
                         filterable
                         clearable
                         :reserve-keyword="true"
                         placeholder="请选择表名"
                         style="width:100%">
                <el-option v-for="item in searchList" :key="item.enTbName" :label="item.enTbName"
                           :value="item.enTbName">
                </el-option>
              </el-select>
            </template>
        </el-col>
        <el-col :span="2">
            <el-button type="primary" @click="getData()">搜索</el-button>
        </el-col>
        <el-col :span="12">
          <div class="header-right-box" style="float: right; margin-right: 16px;">
            <img class="full-img" src="../../../../../assets/er-images/fullscreen.png" @click="enterFullScreen"
                 v-if="fullShow">
            <!--<el-button type="primary" @click="enterFullScreen" v-if="fullShow">全屏</el-button>-->
            <!--<el-button type="primary" @click="exitFullScreen" v-if="!fullShow">退出全屏</el-button>-->
            <el-button style="margin-right: -6px" icon="el-icon-minus" @click="changeScale('reduce')"></el-button>
            <el-select v-model="myDiagramScale" placeholder="请选择" style="width: 100px"
                       @change="changeScale('change')">
              <el-option
                v-for="item in scales"
                :key="item.lable"
                :label="item.lable"
                :value="item.val">
              </el-option>
            </el-select>
            <el-button style="margin-left: -6px ;font-size: 14px" icon="el-icon-plus" @click="changeScale('add')"></el-button>
          </div>
        </el-col>
      </el-row>
    </div>
    <div class="full-btn-box" v-if="!fullShow">
      <img src="../../../../../assets/er-images/ic_er_renew.png" @click="exitFullScreen" v-if="!fullShow"
           class="exitFullScreenBtn">
    </div>
    <div class="er-div" :id='title' style='width:100%;height:calc(100vh - 300px)!important; ' @contextmenu.prevent="showRight"></div>
    <el-dialog title="关联关系"
               :visible.sync="editRelationsDialog"
               v-if="editRelationsDialog"
               width="850px"
               :close-on-click-modal="false">
      <edit-relation :relationsData="editRelationsData" @close="closeDialog()"></edit-relation>
    </el-dialog>
    <!--点击画板右击弹出框-->
    <div :id="rightId" class="er-right-box" v-show="erRight">
      <el-menu default-active="1-4-1" class="el-menu-vertical-demo" :collapse="isCollapse" v-if="erRight"
               style="z-index:99999;">
        <el-submenu index="1">
          <template slot="title">
            <div class="submenu-item">
              视图
            </div>
          </template>
          <el-menu-item index="1-1">
            <el-checkbox v-model="showItem.isType" label="显示数据类型" name="type" @change="changeData"></el-checkbox>
          </el-menu-item>
          <el-menu-item index="1-2">
            <el-checkbox v-model="showItem.isNull" label="显示为空" name="type" @change="changeData"></el-checkbox>
          </el-menu-item>
          <el-menu-item index="1-3">
            <el-checkbox v-model="showItem.isCnName" label="显示注释" name="type" @change="changeData"></el-checkbox>
          </el-menu-item>
        </el-submenu>
        <el-submenu index="2">
          <template slot="title">
            <div class="submenu-item">
              属性
            </div>
          </template>
          <el-menu-item index="2-1">
            <el-checkbox v-model="showItem.isAll" label="所有" name="type" @change="changeData"></el-checkbox>
          </el-menu-item>
          <el-menu-item index="2-2">
            <el-checkbox v-model="showItem.isKeyword" label="关键字" name="type" @change="changeData"></el-checkbox>
          </el-menu-item>
          <el-menu-item index="2-3">
            <el-checkbox v-model="showItem.isPrimaryKey" label="主键" name="type" @change="changeData"></el-checkbox>
          </el-menu-item>
        </el-submenu>
      </el-menu>
    </div>
  </div>
</template>
<script>
  import go from 'gojs'
  import $ from 'jquery'
  import { tber, relation, businessPage } from '@/api/meta_data/storage'
  import editRelation from './editRelation'

  export default {
    name: 'MetaDataManageStoreTechnicalTableEr',
    props: ['tbId', 'dbTypeName', 'title', 'dbId'],
    components: {
      editRelation
    },
    data() {
      return {
        boxId: '',
        rightId: '',
        keyword: '',
        nodedata1: [],
        linkdata1: [],
        tbIdFrom: '',
        tbIdEnd: '',
        dialogFormVisible: false,
        inputModel: '',
        searchList: [],
        relationList: [], // 表关系表格
        originalTable: [], // 原表所有字段
        associationTable: [], // 关联表所有字段
        relation: '', // 表关系字符串
        endTableName: '',
        fromTableName: '',
        editRelationsDialog: false,
        editRelationsData: {},
        myDiagram: '',
        erRight: false,
        isCollapse: true,
        frist: true,
        myDiagramScale: 1,
        scales: [
          { lable: '25%', val: 0.25 },
          { lable: '50%', val: 0.5 },
          { lable: '75%', val: 0.75 },
          { lable: '100%', val: 1 },
          { lable: '125%', val: 1.25 },
          { lable: '150%', val: 1.5 }],
        fullShow: true,
        showItem: {
          isType: false,
          isNull: false,
          isCnName: false,
          isAll: true,
          isKeyword: false,
          isPrimaryKey: false
        },
        fieldData: [],
        loadingChange: false
      }
    },
    created() {
      const _this = this
      $(document).on('click', function(e) {
        let elem = e.target || e.srcElement
        while (elem) { // 循环判断至跟节点,防止点击的是div子元素
          if (elem.id && elem.id === this.rightId) {
            return
          } // 如果还有别的div不想点击,就加else if判断
          elem = elem.parentNode
        }
        // 这里写你想实现的效果
        _this.erRight = false
      })
    },
    mounted() {
      this.boxId = this.title + '-box'
      this.rightId = this.title + '-er-right'
      this.getData()
      this.getSearchList()
    },
    methods: {
      getData() {
        const _this = this
        var obj = {
          tbId: _this.tbId,
          dbId: _this.dbId,
          keyword: _this.keyword
        }
        tber(obj).then(res => {
          if (res.code === 200) {
            _this.nodedata1 = []
            _this.linkdata1 = []
            const data = res.data.tables
            this.fieldData = res.data.tables
            if (this.frist) {
              this.searchList = res.data.tables
              this.frist = !this.frist
            }
            const relations = res.data.relations
            for (let i = 0; i < data.length; i++) {
              const properties = []
              if (data[i].fields) {
                for (let j = 0; j < data[i].fields.length; j++) {
                  const propertiesdata = {
                    fieldName: data[i].fields[j].fieldName
                  }
                  properties.push(propertiesdata)
                }
              }
              const elm = {
                key: data[i].tableName,
                name: data[i].tableName,
                properties: properties,
                source: '/static/img/table.png'
              }
              _this.nodedata1.push(elm)
            }
            if (relations && relations.length) {
              for (let k = 0; k < relations.length; k++) {
                const element = {
                  from: relations[k].tableName,
                  to: relations[k].referencedTableName,
                  relationship: 'generalization',
                  text: relations[k].customName ? relations[k].customName : relations[k].name,
                  name: relations[k].name
                }
                _this.linkdata1.push(element)
              }
            }
            _this.setData(_this.nodedata1, _this.linkdata1)
          }
        })
      },
      changeData() {
        this.erRight = false
        this.loadingChange = true
        this.handleData()
      },
      handleData() {
        this.nodedata1 = []
        let data = []
        data = this.fieldData
        for (let i = 0; i < data.length; i++) {
          const properties = []
          if (data[i].fields) {
            let fields = []
            for (let j = 0; j < data[i].fields.length; j++) {
              if (this.showItem.isAll) {
                fields = data[i].fields
              } else {
                if (this.showItem.isKeyword && data[i].fields[j].isKey === 1) {
                  fields.push(data[i].fields[j])
                }
                if (this.showItem.isPrimaryKey && data[i].fields[j].isPri === 1) {
                  fields.push(data[i].fields[j])
                }
              }
            }
            for (let i = 0; i < fields.length; i++) {
              const propertiesdata = {
                fieldName: fields[i].fieldName
              }
              if (this.showItem.isType) { // 显示类型
                propertiesdata.type = fields[i].type
                fields[i].length = fields[i].length === null ? 0 : fields[i].length
                propertiesdata.length = '(' + fields[i].length + ')'
              }
              if (this.showItem.isNull) { // 显示为空
                propertiesdata.notNull = fields[i].notNull === 1 ? 'notNull' : 'null'
              }
              if (this.showItem.isCnName) { // 显示注释
                propertiesdata.fieldCineseName =
                  (fields[i].fieldCineseName === '' || fields[i].fieldCineseName === null) ? ''
                    : ('-' + fields[i].fieldCineseName)
              }
              properties.push(propertiesdata)
            }
          }
          const elm = {
            key: data[i].tableName,
            name: data[i].tableName,
            properties: properties,
            source: '/static/img/table.png'
          }
          this.nodedata1.push(elm)
        }
        this.setData(this.nodedata1, this.linkdata1)
        this.loadingChange = false
      },
      // 搜索框表名列表
      getSearchList() {
        const obj = {
          dbId: this.dbId,
          start: 0,
          length: 999
        }
        businessPage(obj).then(res => {
          if (res.code === 200) {
            this.searchList = res.data.content
          }
        })
      },
      // 用于关闭弹窗
      closeDialog() {
        this.editRelationsDialog = false
        this.getData()
      },
      // 全屏显示
      enterFullScreen() {
        const _this = this
        document.getElementById(_this.title).style.height = window.screen.height - 50 + 'px'
        this.fullShow = false
        const docElm = document.getElementById(this.boxId)
        const rfs = docElm.requestFullscreen || docElm.mozRequestFullScreen || docElm.webkitRequestFullScreen || docElm.msRequestFullscreen
        rfs.call(docElm)
      },
      // 退出全屏显示
      exitFullScreen() {
        const _this = this
        document.getElementById(_this.title).style.height = '690px'
        this.fullShow = true
        const rfs = document.exitFullscreen || document.mozCancelFullScreen || document.webkitCancelFullScreen || document.msExitFullscreen
        rfs.call(document)
      },
      // 点击右键--文件夹
      showRight(ev) {
        var oEvent = ev || event
        // 正常屏状态
        if (this.fullShow) {
          this.erRight = true
          document.getElementById(this.rightId).style.left = oEvent.clientX - 650 + 'px'
          document.getElementById(this.rightId).style.top = oEvent.clientY - 270 + 'px'
        } else {
          // 全屏状态不出现右键
          return false
        }
      },
      // 缩放
      changeScale(type) {
        switch (type) {
          case 'add' :
            this.myDiagramScale = this.myDiagramScale >= 1.5 ? this.myDiagramScale : this.myDiagramScale += 0.25
            break
          case 'reduce' :
            this.myDiagramScale = this.myDiagramScale <= 0.25 ? this.myDiagramScale : this.myDiagramScale -= 0.25
            break
          case 'change' :
            this.myDiagram.scale = this.myDiagramScale
        }
        this.myDiagram.scale = this.myDiagramScale
      },
      clickLink(e, link) {
        let tableOption = []
        let referencedTableOption = []
        const name = link.data.name === undefined ? null : link.data.name
        for (let i = 0; i < this.nodedata1.length; i++) {
          if (this.nodedata1[i].tableName === link.data.from) {
            tableOption = this.nodedata1[i].fields
          }
          if (this.nodedata1[i].tableName === link.data.to) {
            referencedTableOption = this.nodedata1[i].fields
          }
        }
        this.editRelationsData = {
          dbId: this.dbId,
          name: name,
          tableName: link.data.from,
          refTableName: link.data.to,
          tableOption: tableOption,
          referencedTableOption: referencedTableOption,
          customName: link.data.text
        }
        this.editRelationsDialog = true
      },
      convertIsTreeLink(r) {
        return r === 'generalization'
      },
      convertFromArrow(r) {
        switch (r) {
          case 'generalization':
            return ''
          default:
            return ''
        }
      },
      convertToArrow(r) {
        switch (r) {
          case 'generalization':
            return 'Triangle'
          default:
            return ''
        }
      },
      // 如果从“条件”节点出来,请使链接标签可见。
      // 这个监听器由“LinkDrawn”和“LinkRelinked”DiagramEvents调用。
      showLinkLabel(e) {
        var label = e.subject.findObject('LABEL')
        if (label !== null) {
          label.visible = e.subject.fromNode.data.figure === 'Circle'
        }
      },
      // 定义一个通常是透明的"端口"的函数。
      // "name" 用作 GraphObject.portId, "spot" 用于控制链接如何连接
      // 端口位于节点上, and the boolean "output" and "input" arguments
      // 控制用户是否可以从端口或从端口绘制链接
      makePort(name, spot, output, input) {
        var $ = go.GraphObject.make // 简洁定义模板
        // the port is basically just a small circle that has a white stroke when it is made visible
        return $(go.Shape, 'Circle', {
          fill: '#ccc',
          stroke: null, // 这是在showPorts功能变更为"白色"
          desiredSize: new go.Size(10, 10),
          alignment: spot,
          alignmentFocus: spot, // align the port on the main Shape
          portId: name, // 声明此对象为"port"
          fromSpot: spot,
          toSpot: spot, // 声明链接可能在此端口连接
          fromLinkable: output,
          toLinkable: input, // 声明用户是否可以从这里绘制链接
          cursor: 'pointer' // 显示不同的光标来指示潜在的链接点
        })
      },
      init() {
        if (window.goSamples) goSamples() // 初始化示例 -- you don't need to call this
        var $ = go.GraphObject.make // 简洁定义模板
        this.myDiagram = $(
          go.Diagram,
          this.title, // 必须命名或引用div
          {
            initialContentAlignment: go.Spot.Center,
            allowDrop: true, // 必须为true,才能接受Palette
            LinkDrawn: this.showLinkLabel, // this DiagramEvent listener is defined below
            LinkRelinked: this.showLinkLabel,
            'animationManager.duration': 800, // 稍早于默认动画,默认动画600s
            'undoManager.isEnabled': true, // enable undo & redo
            layout: $(go.ForceDirectedLayout, {
              defaultSpringLength: 50,
              defaultElectricalCharge: 20
            })
            // layout: $(go.TreeLayout,
            //   { // this only lays out in trees nodes connected by 'generalization' links
            //     angle: 0,
            //     nodeSpacing: 50,
            //     path: go.TreeLayout.PathSource,
            //     setsPortSpot: false,
            //     setsChildPortSpot: false,
            //     // nodes not connected by 'generalization' links are laid out horizontally
            //     arrangement: go.TreeLayout.ArrangementHorizontal
            //   })
          }
        )

        // 定义常规节点的节点模板
        var propertyTemplate =
          $(go.Panel, 'TableRow',
            $(go.Panel, 'Horizontal',
              { row: 1, column: 1, alignment: go.Spot.Left, margin: new go.Margin(0, 20, 0, 0) },
              $(go.TextBlock,
                { isMultiline: false, editable: false },
                new go.Binding('text', 'fieldName').makeTwoWay(),
                // 字段是否显示高亮
                new go.Binding('stroke', 'isHighlighted', function(h) { return h ? '#f00' : '#333' })
              ),
              $(go.TextBlock,
                { isMultiline: false, editable: false, stroke: '#333', row: 1 },
                new go.Binding('text', 'fieldCineseName').makeTwoWay()
              )
            ),
            $(go.Panel, 'Horizontal', { row: 1, column: 2, alignment: go.Spot.Right, margin: 4 },
              $(go.TextBlock,
                { isMultiline: false, editable: false, stroke: '#333', row: 1 },
                new go.Binding('text', 'type').makeTwoWay()
              ),
              $(go.TextBlock,
                { isMultiline: false, editable: false, stroke: '#333', row: 1 },
                new go.Binding('text', 'length').makeTwoWay()
              )
            ),
            $(go.TextBlock,
              {
                isMultiline: false, editable: false, stroke: '#333', row: 1,
                margin: 4, textAlign: 'end', column: 4, alignment: go.Spot.Right
              },
              new go.Binding('text', 'notNull').makeTwoWay()
            )
          )
        this.myDiagram.nodeTemplateMap.add(
          '', // 默认类别
          $(
            go.Node,
            { avoidableMargin: new go.Margin(100, 100, 100, 100) },
            'Spot',
            // 主对象是一个围绕TextBlock的面板,具有矩形Shape
            $(
              go.Panel,
              'Auto',
              { margin: 0, background: '#fff' },
              new go.Binding(),
              $(
                go.Shape,
                'Rectangle',
                {
                  fill: '#fff', //
                  stroke: '#33A7EB', // node边框
                  strokeWidth: 1
                }
              ),
              $(
                go.Panel, 'Table',
                { background: '#ffffff' },
                $(go.Panel, 'TableRow', { background: '#E6F7FF' },
                  $(go.Picture,
                    // Pictures 应该指定宽高.
                    // 当没有图片时显示红色的背景
                    // 或者当图片为透明的时候也是.
                    { margin: 8, width: 20, height: 20, alignment: go.Spot.Left },
                    // Picture.source参数值与模型数据中的"source"字段绑定
                    new go.Binding('source')),
                  $(
                    go.TextBlock,
                    {
                      alignment: go.Spot.Left,
                      margin: new go.Margin(10, 8, 10, 30),
                      font: '11pt sans-serif', // 表头字体样式调整
                      isMultiline: false,
                      editable: false
                    },
                    new go.Binding('text', 'key').makeTwoWay()
                  )
                ),
                $(go.TextBlock, 'Properties',
                  {
                    row: 1,
                    font: 'italic 10pt sans-serif'
                  },
                  new go.Binding('visible', 'visible', function(v) {
                    return !v
                  }).ofObject('PROPERTIES')
                ),
                $(go.Panel, 'Table', { name: 'PROPERTIES' },
                  new go.Binding('itemArray', 'properties'),
                  {
                    row: 1,
                    defaultAlignment: go.Spot.Left, padding: new go.Margin(0, 8, 0, 8), background: '#ffffff',
                    itemTemplate: propertyTemplate
                  }
                )
              )
            ),
            // 四个命名端口,每一端一个:
            this.makePort('T', go.Spot.Top, true, true),
            this.makePort('L', go.Spot.Left, true, true),
            this.makePort('R', go.Spot.Right, true, true),
            this.makePort('B', go.Spot.Bottom, true, true)
          )
        )

        const _this = this
        // 替换linkTemplateMap中的默认Link模板
        _this.myDiagram.linkTemplate = $(
          go.Link, // 整个链接面板
          {
            routing: go.Link.Orthogonal,
            curve: go.Link.JumpOver,
            corner: 5,
            toShortLength: 4,
            relinkableFrom: true,
            relinkableTo: true,
            reshapable: false,
            resegmentable: true,
            layerName: 'Background', // 不要在任何节点前面交叉
            //  mouse-overs巧妙地突出显示链接:
            mouseEnter: function(e, link) {
              // link.findObject('HIGHLIGHT').stroke = 'transparent'

            },
            mouseLeave: function(e, link) {
              // link.findObject('HIGHLIGHT').stroke = 'transparent'
            },
            doubleClick: function(e, link) {
              _this.clickLink(e, link)
            }
          },
          new go.Binding('points').makeTwoWay(),
          $( // 设置连线的颜色、宽度等
            go.Shape, // the link path shape
            { strokeWidth: 3, stroke: '#33A7EB', margin: 10 }
          ),
          $( // 设置箭头
            go.Shape, // the arrowhead
            {
              toArrow: 'standard',
              stroke: null,
              fill: '#33A7EB'
            }
          ),
          $(go.Panel, 'Auto', // 控制连线上文本框样式
            $(go.Shape,
              'Rectangle',
              {
                fill: '#33A7EB',
                stroke: null,
                height: 32
              }),
            $(go.TextBlock, '点我添加关联关系',
              {
                row: 1,
                margin: 10,
                textAlign: 'center',
                font: '10pt helvetica, arial, sans-serif',
                editable: false,
                stroke: '#fff'
              },
              // editing the text automatically updates the model data
              new go.Binding('text').makeTwoWay())
          )
        )

        // LinkingTool和RelinkingTool使用的临时链接也是正交的:
        // _this.myDiagram.toolManager.linkingTool.temporaryLink.routing =
        //   go.Link.Orthogonal
        // _this.myDiagram.toolManager.relinkingTool.temporaryLink.routing =
        //   go.Link.Orthogonal
        _this.myDiagram.model = new go.GraphLinksModel(
          _this.nodedata1,
          _this.linkdata1
        )
        _this.myDiagram.selectCollection(_this.myDiagram.nodes)
        //  _this.myDiagram.doFocus = _this.customFocus

        _this.myDiagram.clearSelection() // 取消选择所有选定的部件
        // console.log(myDiagram.model.toJson())
      }, // end init
      setData(name1, name2) {
        const _this = this
        // load an initial diagram from some JSON text
        var $ = go.GraphObject.make
        if (_this.myDiagram !== '') {
          _this.myDiagram.model = $(go.GraphLinksModel, {
            nodeDataArray: name1,
            linkDataArray: name2
          })
        }
      }
    }
  }
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
  .header-right-box {
    position: relative;
    img{
      position: absolute;
      top: 2px;
      right: 175px;
      width: 32px;
    }
  }

  .el-menu-vertical-demo:not(.el-menu--collapse) {
    width: 200px;
    min-height: 400px;
  }

  .er-right-box {
    position: absolute;
    z-index: 1000;
    border: 1px solid #E0E0E0;
    .el-menu--collapse {
      width: 100px
    }
    .el-submenu__title {

    }
    .el-submenu__title:hover {
      background: #409eff !important;
      color: #fff;
    }
  }

  .er-box {
    position: relative;
    background: #fff;
    overflow: hidden;
    .full-btn-box {
      height: 50px;
      .exitFullScreenBtn {
        float: right;
        margin-right: 50px;
        margin-top: 12px;
      }
    }
  }

  .full-img {
    width: 24px;
  }

</style>

 

展开阅读全文

没有更多推荐了,返回首页