vue使用Antv x6 组织架构图(自动布局)

 引入x6

npm install @antv/x6 --save
<template>
    <div id="layoutContainer" style='width: 100%;height: 800px;'></div>
</template>

<script>
    // Cell, Node,
    import {Graph, Color, Dom} from '@antv/x6'
    import dagre from 'dagre'

    // 自定义节点
    Graph.registerNode('org-node', {
            width: 260,
            height: 88,
            markup: [
                {
                    tagName: 'rect',
                    attrs: {
                        class: 'card'
                    }
                },
                {
                    tagName: 'image',
                    attrs: {
                        class: 'image'
                    }
                },
                {
                    tagName: 'text',
                    attrs: {
                        class: 'rank'
                    }
                },
                {
                    tagName: 'text',
                    attrs: {
                        class: 'name'
                    }
                },
                //增加刪除
                // {
                //     tagName: 'g',
                //     attrs: {
                //         class: 'btn add'
                //     },
                //     children: [
                //         {
                //             tagName: 'circle',
                //             attrs: {
                //                 class: 'add'
                //             }
                //         },
                //         {
                //             tagName: 'text',
                //             attrs: {
                //                 class: 'add'
                //             }
                //         }
                //     ]
                // },
                // {
                //     tagName: 'g',
                //     attrs: {
                //         class: 'btn del'
                //     },
                //     children: [
                //         {
                //             tagName: 'circle',
                //             attrs: {
                //                 class: 'del'
                //             }
                //         },
                //         {
                //             tagName: 'text',
                //             attrs: {
                //                 class: 'del'
                //             }
                //         }
                //     ]
                // }
            ],
            attrs: {
                '.card': {
                    rx: 10,
                    ry: 10,
                    refWidth: '100%',
                    refHeight: '100%',
                    fill: '#FFF',
                    stroke: '#000',
                    strokeWidth: 0,
                    pointerEvents: 'visiblePainted'
                },
                '.image': {
                    x: 16,
                    y: 16,
                    width: 56,
                    height: 56,
                    opacity: 0.7
                },
                '.rank': {
                    refX: 0.95,
                    refY: 0.5,
                    fontFamily: 'Courier New',
                    fontSize: 13,
                    textAnchor: 'end',
                    textVerticalAnchor: 'middle'
                },
                '.name': {
                    refX: 0.95,
                    refY: 0.7,
                    fontFamily: 'Arial',
                    fontSize: 14,
                    fontWeight: '600',
                    textAnchor: 'end'
                },
                '.btn.add': {
                    refDx: -16,
                    refY: 16,
                    event: 'node:add'
                },
                '.btn.del': {
                    refDx: -44,
                    refY: 16,
                    event: 'node:delete'
                },
                '.btn > circle': {
                    r: 10,
                    fill: 'transparent',
                    stroke: '#333',
                    strokeWidth: 1
                },
                '.btn.add > text': {
                    fontSize: 20,
                    fontWeight: 800,
                    stroke: '#000',
                    x: -5.5,
                    y: 7,
                    fontFamily: 'Times New Roman',
                    text: '+'
                },
                '.btn.del > text': {
                    fontSize: 28,
                    fontWeight: 500,
                    stroke: '#000',
                    x: -4.5,
                    y: 6,
                    fontFamily: 'Times New Roman',
                    text: '-'
                }
            }
        },
        true
    )

    // 自定义边
    Graph.registerEdge(
        'org-edge',
        {
            zIndex: -1,
            attrs: {
                line: {
                    stroke: '#585858',
                    strokeWidth: 3,
                    sourceMarker: null,
                    targetMarker: null
                }
            }
        },
        true
    )

    const male = 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*kUy8SrEDp6YAAAAAAAAAAAAAARQnAQ'
    const female = 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*f6hhT75YjkIAAAAAAAAAAAAAARQnAQ'
    // 布局方向
    const dir = 'LR' // LR RL TB BT

    export default {
        name: 'index',

        data() {
            return {
                visible: false,
                graph: '',
                datalist: [],
                list: [],
                list2: []
            }
        },

        mounted() {
            this.zzManageThreeList();
            // await this.showDrawer();
        },

        methods: {
            async zzManageThreeList() {
                await this.$api.zzManageThreeList().then(res => {
                    this.datalist = res.data.data;
                    this.list = [];
                    this.readNodes(this.datalist)
                })
                await this.showDrawer();
            },
            readNodes(nodes = [], biao = '') {
                for (let item of nodes) {
                    this.list.push({
                        name: item.name,
                        sort: item.sort,
                        biao: biao
                    })
                    if (item.children && item.children.length) {
                        this.readNodes(item.children, item.name)
                    }
                }
            },
            showDrawer() {
                this.initGraph()
                this.visible = true
            },

            initGraph() {
                // 创建画布
                this.graph = new Graph({
                    container: document.getElementById('layoutContainer'),
                    // width: 100%,
                    // height: 800,
                    grid: true,
                    scroller: true,
                    snapline: true,
                    interacting: false,
                    //縮放
                    mousewheel: {
                        enabled: true,
                        modifiers: ['ctrl', 'meta'],
                    },
                });
                var nodes = [
                    // this.createNode('Founder & Chairman', 'Pierre Omidyar', male, '#31d0c6'),
                    // this.createNode('President & CEO', 'Margaret C. Whitman', female, '#31d0c6'),
                    // this.createNode('President, PayPal', 'Scott Thompson', male, '#7c68fc'),
                    // this.createNode('President, Ebay Global Marketplaces', 'Devin Wenig', male, '#7c68fc'),
                    // this.createNode('Senior Vice President Human Resources', 'Jeffrey S. Skoll', male, '#fe854f'),
                    // this.createNode('Senior Vice President Controller', 'Steven P. Westly', male, '#feb663')
                ]
                // nodes.push(this.createNode(this.datalist[0].sort, this.datalist[0].name, male, '#31d0c6'))
                for (let i = 0; i < this.list.length; i++) {
                    nodes.push(this.createNode(this.list[i].sort, this.list[i].name, male, '#31d0c6',this.list[i].biao))
                }
                this.list2=[...this.list]
                const edges = [
                    // this.createEdge(nodes[0], nodes[1]),
                    // this.createEdge(nodes[0], nodes[3]),
                    // this.createEdge(nodes[1], nodes[2]),
                    // // this.createEdge(nodes[1], nodes[3]),
                    // this.createEdge(nodes[1], nodes[4]),
                    // this.createEdge(nodes[1], nodes[5])
                ]
                for (let i=0;i<this.list.length;i++){
                    for (let j=1;j<this.list2.length;j++) {
                        if (this.list[i].name==this.list2[j].biao) {
                            edges.push(this.createEdge(nodes[i], nodes[j]))
                        }
                    }
                }

                this.graph.resetCells([...nodes, ...edges])
                this.layout()
                this.graph.zoomTo(0.8)
                this.graph.centerContent()
                // this.setup()
            },

            // 监听自定义事件
            setup() {
                this.graph.on('node:add', ({e, node}) => {
                    e.stopPropagation()
                    const bg = Color.randomHex()
                    const member = this.createNode(
                        'Employee',
                        'New Employee',
                        Math.random() < 0.5 ? male : female,
                        bg,
                        Color.invert(bg, true)
                    )
                    this.graph.freeze()
                    this.graph.addCell([member, this.createEdge(node, member)])
                    this.layout()
                })

                this.graph.on('node:delete', ({e, node}) => {
                    e.stopPropagation()
                    this.graph.freeze()
                    this.graph.removeCell(node)
                    this.layout()
                })
            },
            // 自动布局
            layout() {
                const nodes = this.graph.getNodes()
                const edges = this.graph.getEdges()
                const g = new dagre.graphlib.Graph()
                g.setGraph({rankdir: dir, nodesep: 16, ranksep: 16})
                g.setDefaultEdgeLabel(() => ({}))

                const width = 260
                const height = 90
                nodes.forEach((node) => {
                    g.setNode(node.id, {width, height})
                })

                edges.forEach((edge) => {
                    const source = edge.getSource()
                    const target = edge.getTarget()
                    g.setEdge(source.cell, target.cell)
                })

                dagre.layout(g)

                this.graph.freeze()

                g.nodes().forEach((id) => {
                    const node = this.graph.getCell(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()

                    // console.log(sourceBBox, targetBBox)

                    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') &&
                        sourceBBox.x !== targetBBox.x
                    ) {
                        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([])
                    }
                })

                this.graph.unfreeze()
            },

            createNode(rank, name, image, background) {
                const textColor = '#000';
                return this.graph.createNode({
                    shape: 'org-node',
                    attrs: {
                        '.card': {fill: background},
                        '.image': {xlinkHref: image},
                        '.rank': {
                            fill: textColor,
                            text: Dom.breakText(rank, {width: 160, height: 45})
                        },
                        '.name': {
                            fill: textColor,
                            text: Dom.breakText(name, {width: 160, height: 45})
                        },
                        '.btn > circle': {stroke: textColor},
                        '.btn > text': {fill: textColor, stroke: textColor}
                    }
                })
            },
            createEdge(source, target) {
                return this.graph.createEdge({
                    shape: 'org-edge',
                    source: {cell: source.id},
                    target: {cell: target.id}
                })
            }
        }
    }
</script>

<style scoped>

</style>

Vue是一种流行的JavaScript框架,Vue 2是其第二个版本。AntV X6是一个基于Vue 2的图表绘制库,可以用于绘制各种类型的图表,包括树形流程图。 要在Vue 2项目中使用AntV X6绘制树形流程图,首先需要安装和引入AntV X6库。可以通过npm或yarn进行安装: npm install @antv/x6 或者 yarn add @antv/x6 然后,可以在Vue的组件中使用AntV X6。首先,引入`Graph`和`Node`组件: import { Graph, Node } from '@antv/x6-vue' 接下来,在Vue的模板中添加一个`Graph`组件,并设置一些必要的属性: ``` <template> <div> <Graph :initConfig="graphConfig"> <Node :shape="nodeShape" :x="nodeX" :y="nodeY" :width="nodeWidth" :height="nodeHeight" /> </Graph> </div> </template> <script> export default { data() { return { graphConfig: { width: 800, height: 600, gridSize: 10, background: { color: '#f8f8f8' }, scroller: { enabled: true, pannable: true } }, nodeShape: 'rect', nodeX: 100, nodeY: 100, nodeWidth: 80, nodeHeight: 40 } } } </script> ``` 在上面的代码中,创建了一个Graph组件和一个Node组件。Graph组件定义了整个绘图区域的配置,例如大小、网格和背景颜色等。Node组件定义了一个矩形节点的形状、位置和尺寸。 通过设置Node组件的属性,可以绘制更多的节点,并使用适当的形状、位置和尺寸。 以上是一个简单的示例,展示了如何在Vue 2项目中使用AntV X6绘制树形流程图。通过修改组件属性,可以创建更复杂的图表,并通过AntV X6的其他功能来增强图表的交互性和可视化效果。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值