hbchen 6 mesiacov pred
rodič
commit
db7f1c4ecf

+ 2 - 0
package.json

@@ -15,6 +15,7 @@
   "dependencies": {
     "@antv/hierarchy": "^0.6.11",
     "@antv/x6": "^1.29.1",
+    "@antv/x6-vue-shape": "1.1.4",
     "@fullcalendar/interaction": "^5.10.1",
     "@fullcalendar/timegrid": "^5.10.1",
     "@fullcalendar/vue": "^5.10.1",
@@ -28,6 +29,7 @@
     "canvas2image": "^1.0.5",
     "clipboard": "^2.0.1",
     "crypto-js": "^3.1.9-1",
+    "dagre": "^0.8.5",
     "dependencies": "^0.0.1",
     "dll": "^0.2.0",
     "element-resize-detector": "^1.2.2",

+ 149 - 0
src/components/antvVueComponents/tooltipCom.vue

@@ -0,0 +1,149 @@
+<template>
+  <div class="container" ref="container" @mouseover="nodeMouseEnter" @mouseout="nodeMouseOut"
+  @click="nodeClick">
+    <el-tooltip :content="data.RuleTitle" placement="top" ref="antvTooltip"
+    class="container-input">
+      <span ref="containerText">{{ data.EdbName }}</span> 
+    </el-tooltip>
+    <i :class="data.fold?'el-icon-circle-plus':'el-icon-remove'" v-if="!data.isLeaf"
+    class="fold-icon" @click="flodApi" v-show="show"></i>
+  </div>
+</template>
+
+<script>
+
+export default {
+  name:"toolTipCom",
+  inject: ["getGraph", "getNode"],
+  data() {
+    return {
+      node:{},
+      data:{
+        fold:false,
+        routeQuery:{}
+      },
+      graph:{},
+      show:false
+    };
+  },
+  props:{
+    params:{
+      type:Object,
+      default:()=>{
+        return {
+          stopNodeClick:false
+        }
+      }
+    }
+  },
+  mounted() {
+    this.initNode()
+  },
+  methods: {
+    initNode(){
+      this.node = this.getNode();
+      this.graph= this.getGraph()
+      this.node.data = this.node.data?{...this.data,...this.node.data}:this.data
+      this.data = this.node.data
+
+      let {style} = this.data
+      this.initStyle(style)
+
+    },
+    initStyle(style){
+      // !important 是有的节点 :hover不变色
+      if(style){
+        // 背景色
+        if(style.backgroundColor && style.backgroundColor.indexOf("!important")!=-1){
+          let value = style.backgroundColor.split("!")[0]
+          this.$refs.containerText.style.setProperty('background-color',value,'important')
+        }else{
+          this.$refs.container.style.backgroundColor = style.backgroundColor?style.backgroundColor:
+                                                        this.data.isRoot?'#0052d9': "#f2f6fa"
+        }
+        // 字体颜色
+        if(style.color && style.color.indexOf("!important")!=-1){
+          let value = style.color.split("!")[0]
+          this.$refs.containerText.style.setProperty('color',value,'important')
+        }else{
+          this.$refs.container.style.color = style.color?style.color:
+                                              this.data.isRoot?'#ffffff': "#000000"
+        }
+        this.$refs.containerText.style.color = style.color
+      }
+    },
+    flodApi(event){
+      event.stopPropagation()
+      const succ = this.graph.getSuccessors(this.node)
+      if (succ) {
+        succ.forEach((node,index) => {
+          node.setVisible(this.data.fold)
+          if(this.data.fold){
+            node.toBack()
+            requestAnimationFrame(()=>{
+              let edge = this.graph.getIncomingEdges(node) || []
+              edge[0].toBack()
+            })
+          }
+          node.data.fold=!this.data.fold
+        })
+      }
+      this.data.fold=!this.data.fold
+    },
+    nodeMouseEnter(){
+      this.show=true
+    },
+    nodeMouseOut(){
+      this.show=false
+    },
+    nodeClick(){
+      if(this.params.stopNodeClick) return 
+      //EdbInfoType 1:跳预测指标详情 0:跳指标库详情
+      const { ClassifyId, UniqueCode, EdbInfoId, EdbInfoType } = this.data.routeQuery
+      let { href } =
+      this.$router.resolve({ path: EdbInfoType === 1 ? '/predictEdb' : '/database', query: { code: UniqueCode, id:
+      EdbInfoId, classifyId: ClassifyId } });
+      window.open(href, '_blank');
+    }
+  },
+};
+</script>
+<style lang="scss" scoped>
+.container{
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+  height: 100%;
+  width: 100%;
+  box-sizing: border-box;
+  background-color: #0052d9;
+  box-shadow: 0 1px 5px rgba(0,0,0,0.15);
+  display: flex;
+  cursor: pointer;
+  position: relative;
+  border-radius: 4px;
+  .container-input{
+    text-align: center;
+    font-size: 16px;
+    color: #ffffff;
+    font-weight: 400;
+    word-break: break-all;
+  }
+  &:hover{
+    background-color: #ecf5ff;
+    .container-input{
+      color: #0052d9!important;
+      text-decoration: underline;
+    }
+  }
+  .fold-icon{
+    position: absolute;
+    font-size: 24px;
+    color: #666666;
+    bottom: -24px;
+    left: 50%;
+    transform: translateX(-50%);
+  }
+}
+</style>

+ 344 - 31
src/views/edbHistoryPage.vue

@@ -3,7 +3,7 @@
     <div class="edb-history-page-wrap">
         <div class="edb-title">{{currentLang==='en'?(treeData.EdbNameEn||treeData.EdbName):treeData.EdbName}}</div>
         <div class="edb-source-wrap">
-            <vue2-org-tree
+            <!-- <vue2-org-tree
                 :data="treeData"
                 :props="{
                     label: 'EdbName',
@@ -12,17 +12,34 @@
                 :horizontal="false"
                 :render-content="renderContent"
                 @on-node-click="handleClickNode"
-            />
+            /> -->
+            <div class="sandbox-body">
+                <div class="sand-chart-body" id="sand-chart-body"></div>
+                <div id="minimap" class="minimap"></div>
+            </div>
         </div>
+        <!-- 确定每个Node节点大小用 -->
+        <div id="mould"><span id="mould-text"></span></div>
     </div>
 </template>
 
 <script>
 import { dataBaseInterface } from '@/api/api.js';
+import { Graph} from '@antv/x6';
+import '@antv/x6-vue-shape'
+import toolTipCom from '../components/antvVueComponents/tooltipCom.vue';
+import dagre from "dagre"
 export default {
+    components:{toolTipCom},
     data() {
         return {
-            treeData:{}
+            treeData:{},
+            graph:null,
+            mould:null,
+            mouldText:null,
+            params:{
+                stopNodeClick:false
+            }
         };
     },
     computed: {
@@ -31,33 +48,277 @@ export default {
         }
     },
     methods: {
-        renderContent(h, data) {
-            let name = this.currentLang === 'en'?(data.EdbNameEn||data.EdbName):data.EdbName;
-            
-            return (
-                <el-tooltip content={data.RuleTitle} placement='top' popper-class='node-tooltip'>
-                    {
-                        data.IsStop?<span class='node stop'>{name+'(暂停更新)'}</span>
-                        :<span class='node'>{name}</span>
+        // renderContent(h, data) {
+        //     return (
+        //         <el-tooltip content={data.RuleTitle} placement='top' popper-class='node-tooltip'>
+        //             {
+        //                 data.IsStop?<span class='node stop'>{data.EdbName+'(暂停更新)'}</span>
+        //                 :<span class='node'>{data.EdbName}</span>
+        //             }
+        //         </el-tooltip>
+        //     )
+        // },
+        // handleClickNode(e, data) {
+        //     //EdbInfoType=1 跳预测指标详情,=0跳指标库详情
+        //     const { ClassifyId, UniqueCode, EdbInfoId, EdbInfoType } = data
+        //     let { href } =
+        //     this.$router.resolve({ path: EdbInfoType === 1 ? '/predictEdb' : '/database', query: { code: UniqueCode, id:
+        //     EdbInfoId, classifyId: ClassifyId } });
+        //     window.open(href, '_blank');
+        // },
+        async getData() {
+            const res = await dataBaseInterface.getEdbCreateHistory({ UniqueCode: this.$route.query.code})
+            if (res.Ret !== 200) return
+            this.treeData = res.Data;
+
+            this.$nextTick(()=>{
+                this.init()
+            })
+        },
+        init() {
+            this.graph=new Graph({
+                container: document.getElementById('sand-chart-body'),
+                autoResize: false, 
+                async:true,//不异步时,显示隐藏会卡死
+                background: {
+                    color: '#fff',
+                },
+                scroller: {
+                    enabled: true,
+                    pannable: true,
+                    minVisibleWidth: 50,
+                    minVisibleHeight: 50,
+                },
+                interacting:{
+                    nodeMovable:false,
+                    magnetConnectable:false,
+                    edgeMovable:false,
+                    edgeLabelMovable:false,
+                    arrowheadMovable:false,
+                    vertexMovable:false,
+                    vertexMovable:false,
+                    vertexDeletable:false
+                },
+                mousewheel:{
+                    enabled: true,
+                    modifiers:['ctrl','meta']
+                }, //滚轮缩放
+                scaling: {
+                    min: 0.5,
+                    max: 2
+                },
+                //小地图
+                minimap: {
+                    enabled: true,
+                    container: document.getElementById("minimap"),
+                }
+            })
+
+            this.graph.on('node:mousemove',()=>{
+                // 拖动画布的时候 阻止节点的点击操作
+                this.params.stopNodeClick=true
+            })
+
+            this.graph.on('node:mouseup',()=>{
+                // 延迟释放
+                requestAnimationFrame(()=>{
+                    this.params.stopNodeClick=false
+                })
+            })
+            let that = this
+            Graph.registerVueComponent(
+            "custom-rect",
+            {
+                template: `<tool-tip-com :params="params"/>`,
+                components: {
+                    toolTipCom,
+                },
+                router:this.$router, //将router传进去,不然组件里面拿不到
+                data() {
+                    return {
+                        // 传一个对象进去
+                        params:that.params,
                     }
-                </el-tooltip>
+                },
+            },true);
+
+            Graph.registerEdge(
+                'org-edge',
+                {
+                    attrs: {
+                        line: {
+                            strokeWidth: 1,
+                            stroke: '#dddddd',
+                            sourceMarker: null,
+                            targetMarker: null,
+                        },
+                    },
+                    zIndex:0
+                },
+                true,
             )
+            
+            this.mould = document.getElementById('mould')
+            this.mouldText = document.getElementById('mould-text')
+
+            let edbName=this.treeData.EdbName+(this.treeData.IsStop?'(暂停更新)':'')
+            this.mouldText.innerText=edbName
+            let node = this.graph.createNode({
+                shape: 'vue-shape',
+                component: 'custom-rect',
+                width:this.mould.offsetWidth+1,
+                height:this.mould.offsetHeight,
+                attrs: {
+                    body: {
+                        rx: 4,
+                        ry: 4,
+                        strokeWidth: 1,
+                        class:'body-class'
+                    },
+                },
+                data:{
+                    EdbName:edbName,
+                    RuleTitle:this.treeData.RuleTitle,
+                    routeQuery:{ 
+                        ClassifyId:this.treeData.ClassifyId, 
+                        UniqueCode:this.treeData.UniqueCode, 
+                        EdbInfoId:this.treeData.EdbInfoId, 
+                        EdbInfoType:this.treeData.EdbInfoType
+                    },
+                    isRoot:true,
+                    isLeaf:(this.treeData.Child && this.treeData.Child.length>0)?false:true,
+                    style:{
+                        color:this.treeData.IsStop?"red!important":'#ffffff!important',
+                    },
+                }
+            })
+            let cells = [node,...this.createCells(this.treeData.Child,node)]
+            this.graph.resetCells(cells)
+            this.layout()
+            this.graph.positionCell(node,'top',{padding:{top:20}})
         },
-        handleClickNode(e, data) {
-            //EdbInfoType=1 跳预测指标详情,=0跳指标库详情
-            const { ClassifyId, UniqueCode, EdbInfoId, EdbInfoType,HaveOperaAuth } = data
-            if(!HaveOperaAuth) return this.$message.warning(this.$t('MsgPrompt.no_edb_auth'))
+        createCells(list,parentNode){
+            if(!(list && list.length>0)){
+                return []
+            }
+            let dataList = []
+            list.forEach(element => {
+                let edbName=element.EdbName+(element.IsStop?'(暂停更新)':'')
+
+                this.mouldText.innerText=edbName
+                let node = this.graph.createNode({
+                    shape: 'vue-shape',
+                    component: 'custom-rect',
+                    width:this.mould.offsetWidth+1,
+                    height:this.mould.offsetHeight,
+                    attrs: {
+                        body: {
+                            rx: 4,
+                            ry: 4,
+                            strokeWidth: 1,
+                            class:'body-class'
+                        },
+                    },
+                    data:{
+                        style:{
+                            backgroundColor:'#f2f6fa',
+                            color:element.IsStop?"red!important":'#000000',
+                        },
+                        EdbName:edbName,
+                        RuleTitle:element.RuleTitle,
+                        routeQuery:{ 
+                            ClassifyId:element.ClassifyId, 
+                            UniqueCode:element.UniqueCode, 
+                            EdbInfoId:element.EdbInfoId, 
+                            EdbInfoType:element.EdbInfoType
+                        },
+                        isRoot:false,
+                        isLeaf:(element.Child && element.Child.length>0)?false:true
+                    }
+                })
+                let side = this.graph.createEdge({
+                    shape: 'org-edge',
+                    source: { cell: parentNode.id },
+                    target: { cell: node.id },
+                })
+                dataList.push(node)
+                dataList.push(side)
+                dataList=[...dataList,...this.createCells(element.Child,node)]
+            });
+            return dataList
+        },
+        layout(){
+            const dir = 'TB'
+            const nodes = this.graph.getNodes()
+            const edges = this.graph.getEdges()
+            const g = new dagre.graphlib.Graph()
+
+            g.setDefaultEdgeLabel(() => ({}))
+            nodes.forEach((node) => {
+                g.setNode(node.id, { width:node.size().width, height:node.size().height })
+            })
+
+            let heights=nodes.map(item =>{
+                // 为了防止重叠,不知道他内部怎么排的,先调整这样。
+                if(item.data.isLeaf){
+                    return item.size().height*1.5-100
+                }else{
+                    return item.size().height/2
+                }
+            })
+            let maxHeight = Math.max(...heights)
+            // console.log(maxHeight,'maxHeight');
+            g.setGraph({ rankdir: dir,nodesep:50,ranksep:maxHeight})
+            edges.forEach((edge) => {
+                const source = edge.getSource()
+                const target = edge.getTarget()
+                g.setEdge(source.cell, target.cell)
+            })
+
+            dagre.layout(g)
             
-            let { href } =
-            this.$router.resolve({ path: EdbInfoType === 1 ? '/predictEdb' : '/database', query: { code: UniqueCode, id:
-            EdbInfoId, classifyId: ClassifyId } });
-            window.open(href, '_blank');
+            g.nodes().forEach((id) => {
+                const node = this.graph.getCellById(id)
+                if (node) {
+                    const pos = g.node(id)
+                    node.position(pos.x, pos.y)
+                }
+            })
+            edges.forEach((edge) => {
+                const source = edge.getSourceNode()
+                const target = edge.getTargetNode()
+                const sourceBBox = source.getBBox()
+                const targetBBox = target.getBBox()
+
+                if ((dir === 'LR' || dir === 'RL') && sourceBBox.y !== targetBBox.y) {
+                const gap =
+                    dir === 'LR'
+                    ? targetBBox.x - sourceBBox.x - sourceBBox.width
+                    : -sourceBBox.x + targetBBox.x + targetBBox.width
+                const fix = dir === 'LR' ? sourceBBox.width : 0
+                const x = sourceBBox.x + fix + gap / 2
+                edge.setVertices([
+                    { x, y: sourceBBox.center.y },
+                    { x, y: targetBBox.center.y },
+                ])
+                } else if (
+                (dir === 'TB' || dir === 'BT')) {
+                    const gap =
+                        dir === 'TB'
+                        ? targetBBox.y - sourceBBox.y - sourceBBox.height
+                        : -sourceBBox.y + targetBBox.y + targetBBox.height
+                    const fix = dir === 'TB' ? sourceBBox.height : 0
+                    const y = sourceBBox.y + fix + gap / 2
+
+                    edge.setVertices([
+                        { x: sourceBBox.center.x, y },
+                        { x: targetBBox.center.x, y },
+                    ])
+                } else {
+                edge.setVertices([])
+                }
+            })
         },
-        async getData() {
-            const res = await dataBaseInterface.getEdbCreateHistory({ UniqueCode: this.$route.query.code})
-            if (res.Ret !== 200) return
-            this.treeData = res.Data;
-        }
     },
     mounted(){
         if(this.$route.query.code){
@@ -77,24 +338,48 @@ export default {
     border-radius: 4px;
     border:1px solid #C8CDD9;
     box-sizing: border-box;
-    padding:30px;
+    // padding:30px;
     .edb-title{
         margin:0 -30px;
         text-align: center;
-        padding-bottom: 30px;
+        padding: 30px;
         border-bottom: 1px solid #C8CDD9;
         font-size: 16px;
     }
     .edb-source-wrap{
         text-align: center;
-    }
-    .edb-source-wrap{
         flex: 1;
         overflow: auto;
+        .sandbox-body{
+            height: 100%;
+            display: flex;
+            position: relative;
+            .minimap{
+                position:absolute;
+                right:6px;
+                bottom:6px;
+                box-sizing: border-box;
+            }
+            #sand-chart-body{
+                flex: 1;
+            }
+        }
+    }
+    #mould{
+        position: absolute;
+        max-width: 100px;
+        padding: 20px;
+        background-color: red;
+        font-size:16px;
+        text-align: center;
+        border-radius: 4px;
+        top: -10000px;
+        opacity: 0;
+        word-break: break-all;
     }
 }
 </style>
-<style lang="scss">
+<!-- <style lang="scss">
 .edb-history-page-wrap{
   .org-tree-container {
     margin: 0 auto;
@@ -139,4 +424,32 @@ export default {
     padding:10px;
     text-align: center;
 }
-</style>
+</style> -->
+<style lang="scss">
+.sandbox-body{
+  .x6-graph-scroller {
+    flex: 1;
+  }
+
+  .x6-port-body {
+    display: none;
+  }
+
+  /* reseize 框样式 */
+  .x6-widget-transform {
+    .x6-widget-transform-resize {
+      border-radius: 0;
+    }
+  }
+  .x6-widget-minimap-viewport{
+    border-color: red;
+    .x6-widget-minimap-viewport-zoom{
+      border-color: red;
+    }
+  }
+  .x6-widget-minimap{
+    width: auto !important;
+    height: auto !important;
+  }
+}
+</style>