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>

### 构建任务失败解决方案 当遇到 `Execution failed for task ':app:shrinkReleaseRes'` 错误时,这通常意味着资源压缩过程中出现了问题。此错误可能由多种原因引起,包括但不限于配置不正确、依赖冲突或特定于项目的其他因素。 #### 可能的原因分析 1. **ProGuard 或 R8 配置不当** ProGuard 和 R8 是用于优化和混淆代码以及减少 APK 大小的工具。如果这些工具的配置存在问题,可能会导致资源无法正常处理[^1]。 2. **重复资源** 如果项目中有多个模块定义了相同的资源名称,可能导致冲突并引发该错误。检查是否存在重名的 drawable、string 等资源文件[^2]。 3. **第三方库兼容性** 某些第三方库可能与当前使用的 Gradle 插件版本或其他库存在兼容性问题,从而影响到资源打包过程中的行为[^3]。 4. **Gradle 缓存问题** 有时旧缓存数据会干扰新编译的结果,尝试清理本地仓库和重新同步项目可以帮助排除此类潜在障碍[^4]。 #### 推荐的操作方法 为了有效解决问题,建议按照以下步骤逐一排查: ```bash # 清理项目构建目录 ./gradlew clean # 删除 .gradle 文件夹下的所有内容以清除缓存 rm -rf ~/.gradle/caches/ ``` 调整 `build.gradle` 中的相关设置也是一个重要环节: ```groovy android { ... buildTypes { release { minifyEnabled true // 是否启用代码缩减 shrinkResources true // 是否开启资源压缩 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // 尝试禁用 shrinkResources 来测试是否为资源压缩引起的错误 // shrinkResources false } } } ``` 此外,在 `proguard-rules.pro` 文件内添加必要的保留规则,防止关键类被意外移除: ```text -keep class com.example.yourpackage.** { *; } # 替换为你自己的包路径 -dontwarn androidx.**,com.google.** # 忽略警告信息 ``` 最后,确保所使用的 Android Studio 版本是最新的稳定版,并且已经应用了所有的补丁更新。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值