suerprisePlus
2024-08-14 d4a3ca549f8755c2f87442c27217c3be39cab5cc
src/views/visual/atlas/index.vue
@@ -1,9 +1,336 @@
<template>
    <div class="atlasBox"></div>
    <div class="atlasBox">
        <div id="container" class="container" v-show="!showEcharts">
            <svg class="keywords" @mousemove="listener($event)">
                <a href="javascript:void(0)" v-for="(tag, index) in tags" :key="index"
                    @contextmenu="removeClick($event, tag)" @click="nodeClick(tag)">
                    <text class="text" :x="tag.x" :y="tag.y" :font-size="20 * (600 / (600 - tag.z))"
                        :fill-opacity="(400 + tag.z) / 600" :fill="tag.color">{{ tag.text }}</text>
                </a>
            </svg>
        </div>
        <div class="graphicBox" v-show="showEcharts">
            <div class="konwReturn fl">
                <el-link @click="showEcharts = false">
                    <i style="color: #409EFF;" class="el-icon-d-arrow-left"></i>
                </el-link>
                当前领域:{{ targetName }}
            </div>
            <div id="graphicContent" v-loading="g_loading" class="graphicContent">
                <RelationGraph ref="graphRef" :options="graphOptions" :on-node-expand="onNodeExpand"
                    :on-node-collapse="onNodeCollapse">
                </RelationGraph>
            </div>
        </div>
    </div>
</template>
<script>
export default {};
import G6 from '@antv/g6';
import { kg_getGraph, kg_queryGraphResult } from '@/api/mapView/map.js';
import RelationGraph from 'relation-graph'
export default {
    components: { RelationGraph },
    data() {
        return {
            width: null,
            height: null,
            tags: [],
            showEcharts: false,
            speedX: Math.PI / 360,
            speedY: Math.PI / 360,
            CX: 600,
            CY: 300,
            RADIUS: 200,
            ZRADIUS: 200,
            targetName: null,
            // 图谱
            g_loading: true,
            demoname: '---',
            graphOptions: {
                layout:
                {
                    label: '中心',
                    layoutName: 'tree',
                    layoutClassName: 'seeks-layout-center',
                    defaultJunctionPoint: 'border',
                    defaultNodeShape: 0,
                    defaultLineShape: 1,
                    from: 'left',
                    max_per_width: 300,
                    min_per_height: 40
                },
                defaultLineMarker: {
                    markerWidth: 12,
                    markerHeight: 12,
                    refX: 6,
                    refY: 6,
                    data: 'M2,2 L10,6 L2,10 L6,6 L2,2'
                },
                moveToCenterWhenRefresh: false,
                defaultExpandHolderPosition: 'right',
                defaultNodeShape: 1,
                defaultNodeWidth: 100,
                defaultLineShape: 4,
                defaultJunctionPoint: 'lr',
                defaultNodeBorderWidth: 0,
                defaultLineColor: 'rgba(0, 186, 189, 1)',
                defaultNodeColor: 'rgba(0, 206, 209, 1)'
            }
        }
    },
    mounted() {
        // ;
        this.setResizeStart();
    },
    methods: {
        nodeClick(tag) {
            this.showEcharts = true;
            this.targetName = tag.text
            this.g_loading = true
            this.getGraphData(tag.text)
        },
        getGraphData(name) {
            this.graphOptions.layout.max_per_width = this.width
            kg_queryGraphResult(
                { domain: name }
            ).then(response => {
                if (response.data.code != 200) return
                this.g_loading = false;
                const data = response.data.data
                if (!this.width) {
                    this.width = document.getElementById('container').innerWidth;
                }
                const obj = data.node.filter(item => {
                    if (item.name == name) {
                        return item
                    }
                })
                this.getGraphicNodes(data.node, data.relationship, obj)
            })
        },
        onNodeCollapse(node, e) {
            // const graphInstance = this.$refs.graphRef.getInstance();
            // graphInstance.doLayout();
        },
        // 通过onNodeExpand事件监听节点的展开事件,在被展开的时候有选择的去从后台获取数据,如果已经从后台加载过数据,则让当前图谱根据当前的节点重新布局
        onNodeExpand(node, e) {
            const graphInstance = this.$refs.graphRef.getInstance();
            // 根据具体的业务需要决定是否需要从后台加载数据
            if (!node.data.isNeedLoadDataFromRemoteServer) {
                console.log('这个节点的子节点已经加载过了');
                graphInstance.doLayout();
                return;
            }
            // 判断是否已经动态加载数据了
            if (node.data.childrenLoaded) {
                console.log('这个节点的子节点已经加载过了');
                graphInstance.doLayout();
                return;
            }
            // this.g_loading = true;
            node.data.childrenLoaded = true;
            this.loadChildNodesFromRemoteServer(node);
        },
        loadChildNodesFromRemoteServer(node) {
            var graphInstance = this.$refs.graphRef.getInstance();
            graphInstance.doLayout();
        },
        getGraphicNodes(res, lines, obj) {
            const std = [];
            const ids = [];
            lines.filter(item => {
                if (ids.indexOf(item.sourceId) < 0) {
                    ids.push(item.sourceId)
                }
                if (obj && obj.length > 0) {
                    if (item.targetId == obj[0].uuid) {
                        ids.push(item.targetId)
                    }
                }
            })
            res.filter(item => {
                var poi = false;
                if (item.poi && item.poi == 1) {
                    poi = true;
                }
                const expand = ids.indexOf(item.uuid) > -1 ? false : true
                if (!expand) {
                    std.push({
                        // 'uuid': item.id ? item.id : new Date().getTime(),
                        'id': item.uuid,
                        'text': item.name,
                        // expandHolderPosition: 'right',
                        // expanded: true,
                        // data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false },
                    })
                } else {
                    std.push({
                        // 'uuid': item.id ? item.id : new Date().getTime(),
                        'id': item.uuid,
                        'text': item.name,
                        // expandHolderPosition: 'right',
                        // expanded: false,
                        // data: { isNeedLoadDataFromRemoteServer: true, childrenLoaded: false },
                    })
                }
            })
            this.getGraphicLines(std, lines)
        },
        getGraphicLines(nodes, res) {
            const std = [];
            res.filter(item => {
                std.push({
                    "from": "" + item.sourceId + "",
                    "to": "" + item.targetId + "",
                    "text": "包含",
                })
            })
            const json_data = {
                'rootId': 'a',
                'nodes': nodes,
                'lines': std
            };
            this.setGraphic(json_data)
        },
        setGraphic(res) {
            setTimeout(() => {
                this.$refs.graphRef.setJsonData(res, (graphInstance) => {
                    // 这些写上当图谱初始化完成后需要执行的代码
                    graphInstance.doLayout();
                });
            }, 200);
            setTimeout(() => {
                var graphInstance = this.$refs.graphRef.getInstance();
            }, 2000);
        },
        removeClick() { },
        rotateX(speedX) {
            var cos = Math.cos(speedX);
            var sin = Math.sin(speedX);
            for (let tag of this.tags) {
                var y1 = (tag.y - this.CY) * cos - tag.z * sin + this.CY;
                var z1 = tag.z * cos + (tag.y - this.CY) * sin;
                tag.y = y1;
                tag.z = z1;
            }
        },
        rotateY(speedY) {
            var cos = Math.cos(speedY);
            var sin = Math.sin(speedY);
            for (let tag of this.tags) {
                var x1 = (tag.x - this.CX) * cos - tag.z * sin + this.CX;
                var z1 = tag.z * cos + (tag.x - this.CX) * sin;
                tag.x = x1;
                tag.z = z1;
            }
        },
        resizeWindow() {
            if (this.showEcharts) return;
            let height = document.getElementById('container').clientHeight;
            let width = document.getElementById('container').clientWidth;
            width = width * 0.85;
            if (width > 1200) {
                this.CX = width / 2;
            }
            height = height - 150;
            this.CY = height / 2;
            let radius = Math.min(this.CY, this.CX) / 2;
            if (radius > 200) {
                this.RADIUS = radius;
            }
            this.setCreateGraph();
        },
        setResizeStart() {
            this.resizeWindow();
            window.addEventListener('resize', this.resizeWindow);
            this.setCreateGraph();
            //使球开始旋转
            const interval = setInterval(() => {
                this.rotateX(this.speedX);
                this.rotateY(this.speedY);
            }, 17);
            this.$once('hook:beforedestroy', () => {
                interval && clearInterval(interval);
                window.removeEventListener('resize', this.resizeWindow);
            });
        },
        //图谱初始化
        setCreateGraph() {
            kg_getGraph({
                command: 0,
                pageIndex: 1,
                pageSize: 5000,
            }).then((response) => {
                if (response.data.code != 200) return;
                this.setGraphStart(response.data.data.nodeList);
            });
        },
        listener(event) {
            var x = event.clientX - this.CX;
            var y = event.clientY - this.CY;
            if (event.target.tagName == 'text') {
                this.speedX = 0;
                this.speedY = 0;
            } else {
                this.speedX = x * 0.00001 > 0 ? Math.min(this.RADIUS * 0.00002, x * 0.0001) : Math.max(-this.RADIUS * 0.00002, x * 0.0001);
                this.speedY = y * 0.00001 > 0 ? Math.min(this.RADIUS * 0.00002, y * 0.0001) : Math.max(-this.RADIUS * 0.00002, y * 0.0001);
            }
            // var val = document.getElementById("removeLabel").style.display;
            // if (val == "none") {
            //     document.getElementById("removeLabel").style.top = event.clientY - 20 + 'px';
            //     document.getElementById("removeLabel").style.left = event.clientX + 10 + 'px';
            // }
        },
        setGraphStart(arr) {
            const field = 'label';
            const obj = arr.filter((item, index) => {
                return arr.findIndex((i) => i[field] === item[field]) === index;
            });
            let tags = [];
            const length = obj.length;
            for (let i = 0; i < length; i++) {
                let tag = {};
                let k = -1 + (2 * (i + 1) - 1) / length;
                let a = Math.acos(k);
                let b = a * Math.sqrt(length * Math.PI);
                tag.text = obj[i].label;
                (tag.id = obj[i].id), (tag.label = obj[i].label), (tag.x = this.CX + this.RADIUS * Math.sin(a) * Math.cos(b));
                tag.y = this.CY + this.RADIUS * Math.sin(a) * Math.sin(b);
                tag.z = this.ZRADIUS * Math.cos(a);
                tag.color = 'rgb(' + parseInt(Math.random() * 255) + ',' + parseInt(Math.random() * 255) + ',' + parseInt(Math.random() * 255) + ')';
                tags.push(tag);
            }
            this.tags = [].concat(tags);
        },
    },
};
</script>
<style lang="scss" scoped>
@@ -13,6 +340,41 @@
    position: absolute;
    margin: 10px;
    border-radius: 5px;
    background: skyblue;
    // background: skyblue;
    .container {
        width: 100%;
        height: 100%;
    }
    .keywords {
        width: 100%;
        height: 100%;
    }
    .keywords .text:hover {
        font-size: 200%;
    }
    .SVGBox {
        width: 100%;
        height: 100%;
    }
    .graphicBox {
        width: 100%;
        height: 100%;
        position: relative;
        display: flex;
        flex-direction: column;
        color: #409EFF;
        .graphicContent {
            padding: 10px;
            flex: 1;
        }
    }
}
</style>