关于AntV-X6,2.X版本在vue3里面的应用【AntV 2.X,Vue3的版本】。

【Ant1.X,vue2版本的,查看这个版本】关于AntV-X6在Vue里面是使用

写在前面

AntV-X6更新到了2.0以前写的键盘事件什么的有些变化所以在这里更新一下。2.x我也没怎么看,只是把我前面的1.X改成了2.X,我这里只是写个大概的,其余的具体的要自己结合Api看一下,因为我不用这个,只是看了一下写的,所以很多时候要*自己改造。。。。


组件功能

  • 可以拖拽添加节点,修改编辑节点样式(默认四个链接桩)
  • 可以添加连线,修改连线的样式。给线条添加动态(蚂蚁线)。可以手动调整线条(添加编辑工具)
  • 可以添加图片节点。自定义设置图片。
  • 导出与反显
项目地址【这个是1.x的,2.x的用下面代码的替换掉就可以了】

antv-x6: AntV-X6 图编辑引擎在vue2里面的使用。 (gitee.com)
git里面的antvSetting.js,替换成下面的antvSetting.ts和主页面文件用下面的内容替换就好了。

***antv-x6的版本1.X和2.X只是有些方法变了。这边采用的方法一样的,只是参数说明的有些修改,git里面的antvSetting.ts,和主页面文件用下面的内容替换就好了。 ***

npm install
npm run serve
项目界面展示
1232.png

项目过程

1,引入

npm install @antv/x6 --save

2,建立一个初始化默认设置的antvSetting.ts,方便调用。

// 基本设置
export const configSetting = (Shape: any) => {
    return {
        grid: true,
        autoResize: true,
        translating: { restrict: true },
        mousewheel: {
            enabled: true,
            zoomAtMousePosition: true,
            modifiers: 'ctrl',
            minScale: 0.5,
            maxScale: 3,
        },
        connecting: {
            router: {
                name: 'manhattan',
                args: {
                    padding: 1,
                },
            },
            connector: {
                name: 'rounded',
                args: {
                    radius: 8,
                },
            },
            anchor: 'center',
            connectionPoint: 'anchor',
            allowBlank: false,
            snap: {
                radius: 20,
            },
            createEdge() {
                return new Shape.Edge({
                    attrs: {
                        line: {
                            stroke: '#A2B1C3',
                            strokeWidth: 2,
                            targetMarker: {
                                name: 'block',
                                width: 12,
                                height: 8,
                            },
                        },
                    },
                    zIndex: 0,
                });
            },
            validateConnection({ targetMagnet }) {
                return !!targetMagnet;
            },
        },
        onToolItemCreated({ tool }) {
            const handle = tool;
            const options = handle.options;
            if (options && options.index % 2 === 1) {
                tool.setAttrs({ fill: 'red' });
            }
        },
        highlighting: {
            magnetAdsorbed: {
                name: 'stroke',
                args: {
                    attrs: {
                        fill: '#5F95FF',
                        stroke: '#5F95FF',
                    },
                },
            },
        },
        resizing: true,
        rotating: true,
        selecting: true,
        snapline: true,
        keyboard: true,
        clipboard: true,
    };
};

// 节点预设类型 (0椭圆形: defaultOval, 1方形: defaultSquare, 2圆角矩形: defaultYSquare, 3菱形: defaultRhombus, 4平行四边形: defaultRhomboid, 5圆形: defaultCircle, 6图片: otherImage)
export const configNodeShape = (type: string) => {
    const nodeShapeList = [
        {
            label: '椭圆形', // 加入data里面的标识type是为了方便编辑的时候找到相对应的类型进行不同的处理
            data: {
                type: 'defaultOval',
            },
            shape: 'rect',
            width: 100,
            height: 50,
            attrs: {
                body: {
                    rx: 20,
                    ry: 26,
                    fill: '#fff',
                    stroke: '#333',
                },
                label: {
                    text: '椭圆形',
                    fontSize: 16,
                    fill: '#333',
                },
            },
        },
        {
            label: '方形',
            data: {
                type: 'defaultSquare',
            },
            shape: 'rect',
            width: 100,
            height: 50,
            attrs: {
                label: {
                    text: '方形',
                    fontSize: 16,
                    fill: '#333',
                },
                body: {
                    fill: '#fff',
                    stroke: '#333',
                },
            },
        },
        {
            label: '圆角矩形',
            data: {
                type: 'defaultYSquare',
            },
            shape: 'rect',
            width: 100,
            height: 50,
            attrs: {
                body: {
                    rx: 6,
                    ry: 6,
                    fill: '#fff',
                    stroke: '#333',
                },
                label: {
                    text: '圆角矩形',
                    fontSize: 16,
                    fill: '#333',
                },
            },
        },
        {
            label: '菱形',
            data: {
                type: 'defaultRhombus',
            },
            shape: 'polygon',
            width: 120,
            height: 50,
            attrs: {
                body: {
                    refPoints: '0,10 10,0 20,10 10,20',
                    fill: '#fff',
                    stroke: '#333',
                },
                label: {
                    text: '菱形',
                    fontSize: 16,
                    fill: '#333',
                },
            },
        },
        {
            label: '平行四边形',
            data: {
                type: 'defaultRhomboid',
            },
            shape: 'polygon',
            width: 120,
            height: 50,
            attrs: {
                body: {
                    refPoints: '10,0 40,0 30,20 0,20',
                    fill: '#fff',
                    stroke: '#333',
                },
                label: {
                    text: '平行四边形',
                    fontSize: 16,
                    fill: '#333',
                },
            },
        },
        {
            label: '圆形',
            data: {
                type: 'defaultCircle',
            },
            shape: 'circle',
            width: 80,
            height: 80,
            attrs: {
                label: {
                    text: '圆形',
                    fontSize: 16,
                    fill: '#333',
                },
                body: {
                    fill: '#fff',
                    stroke: '#333',
                },
            },
        },
        {
            label: '图片',
            data: {
                type: 'otherImage',
            },
            shape: 'rect',
            width: 80,
            height: 80,
            markup: [
                {
                    tagName: 'rect',
                    selector: 'body',
                },
                {
                    tagName: 'image',
                },
                {
                    tagName: 'text',
                    selector: 'label',
                },
            ],
            attrs: {
                body: {
                    stroke: '#5F95FF',
                    fill: '#5F95FF',
                },
                image: {
                    width: 80,
                    height: 80,
                    refX: 0,
                    refY: 0,
                    xlinkHref: 'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
                },
                label: {
                    fontSize: 14,
                    fill: '#fff',
                    text: '图片',
                },
            },
        },
    ];
    if (type) {
        const obj = nodeShapeList.find((item) => {
            return item.data.type === type;
        });
        return obj || nodeShapeList;
    }
    return nodeShapeList;
};

// 节点连接装设置
export const configNodePorts = () => {
    return {
        groups: {
            top: {
                position: 'top',
                attrs: {
                    circle: {
                        r: 4,
                        magnet: true,
                        stroke: '#5F95FF',
                        strokeWidth: 1,
                        fill: '#fff',
                        style: {
                            visibility: 'hidden',
                        },
                    },
                },
            },
            right: {
                position: 'right',
                attrs: {
                    circle: {
                        r: 4,
                        magnet: true,
                        stroke: '#5F95FF',
                        strokeWidth: 1,
                        fill: '#fff',
                        style: {
                            visibility: 'hidden',
                        },
                    },
                },
            },
            bottom: {
                position: 'bottom',
                attrs: {
                    circle: {
                        r: 4,
                        magnet: true,
                        stroke: '#5F95FF',
                        strokeWidth: 1,
                        fill: '#fff',
                        style: {
                            visibility: 'hidden',
                        },
                    },
                },
            },
            left: {
                position: 'left',
                attrs: {
                    circle: {
                        r: 4,
                        magnet: true,
                        stroke: '#5F95FF',
                        strokeWidth: 1,
                        fill: '#fff',
                        style: {
                            visibility: 'hidden',
                        },
                    },
                },
            },
        },
        items: [
            {
                group: 'top',
            },
            {
                group: 'right',
            },
            {
                group: 'bottom',
            },
            {
                group: 'left',
            },
        ],
    };
};

// 连线 label 设置
export const configEdgeLabel = (labelText: string, fontColor: string, fill: string, stroke: string) => {
    if (!labelText) return { attrs: { labelText: { text: '' }, labelBody: { fill: '', stroke: '' } } };
    return {
        markup: [
            {
                tagName: 'rect',
                selector: 'labelBody',
            },
            {
                tagName: 'text',
                selector: 'labelText',
            },
        ],
        attrs: {
            labelText: {
                text: labelText || '',
                fill: fontColor || '#333',
                textAnchor: 'middle',
                textVerticalAnchor: 'middle',
            },
            labelBody: {
                ref: 'labelText',
                refX: -8,
                refY: -5,
                refWidth: '100%',
                refHeight: '100%',
                refWidth2: 16,
                refHeight2: 10,
                stroke: stroke || '#555',
                fill: fill || '#fff',
                strokeWidth: 2,
                rx: 5,
                ry: 5,
            },
        },
    };
};

// 键盘事件
export const graphBindKey = (graph: any) => {
    graph.bindKey(['meta+c', 'ctrl+c'], () => {
        const cells = graph.getSelectedCells();
        if (cells.length) {
            graph.copy(cells);
        }
        return false;
    });
    graph.bindKey(['meta+x', 'ctrl+x'], () => {
        const cells = graph.getSelectedCells();
        if (cells.length) {
            graph.cut(cells);
        }
        return false;
    });
    graph.bindKey(['meta+v', 'ctrl+v'], () => {
        if (!graph.isClipboardEmpty()) {
            const cells = graph.paste({ offset: 32 });
            graph.cleanSelection();
            graph.select(cells);
        }
        return false;
    });
    //undo redo
    graph.bindKey(['meta+z', 'ctrl+z'], () => {
        if (graph.history.canUndo()) {
            graph.history.undo();
        }
        return false;
    });
    graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
        if (graph.history.canRedo()) {
            graph.history.redo();
        }
        return false;
    });
    // select all
    graph.bindKey(['meta+a', 'ctrl+a'], () => {
        const nodes = graph.getNodes();
        if (nodes) {
            graph.select(nodes);
        }
    });
    //delete
    graph.bindKey('delete', () => {
        const cells = graph.getSelectedCells();
        if (cells.length) {
            graph.removeCells(cells);
        }
    });
    // zoom
    graph.bindKey(['ctrl+1', 'meta+1'], () => {
        const zoom = graph.zoom();
        if (zoom < 1.5) {
            graph.zoom(0.1);
        }
    });
    graph.bindKey(['ctrl+2', 'meta+2'], () => {
        const zoom = graph.zoom();
        if (zoom > 0.5) {
            graph.zoom(-0.1);
        }
    });
    return graph;
};

3,页面上的使用

<template>
    <div class="all">
        <div class="antv-content">
            <div class="antv-menu">
                <h3>基础图形列表</h3>
                <ul class="menu-list">
                    <li draggable="true" @drag="menuDrag('defaultOval')"><i class="icon-oval"></i> <strong>椭圆形</strong></li>
                    <li draggable="true" @drag="menuDrag('defaultSquare')"><i class="icon-square"></i><strong>矩形</strong></li>
                    <li draggable="true" @drag="menuDrag('defaultYSquare')"><i class="icon-ysquare"></i><strong>圆角矩形</strong></li>
                    <li draggable="true" @drag="menuDrag('defaultRhombus')"><i class="icon-rhombus"></i><strong>菱形</strong></li>
                    <li draggable="true" @drag="menuDrag('defaultRhomboid')"><i class="icon-rhomboid"></i><strong>平行四边形</strong></li>
                    <li draggable="true" @drag="menuDrag('defaultCircle')"><i class="icon-circle"></i><strong>圆形</strong></li>
                    <li draggable="true" @drag="menuDrag('otherImage')">
                        <el-icon><PictureFilled /></el-icon><strong>图片</strong>
                    </li>
                </ul>
            </div>
            <div class="antv-wrapper">
                <div class="wrapper-canvas" :style="{ height: props.height }" id="wrapper" @drop="drop($event)" @dragover.prevent></div>
                <div class="wrapper-tips">
                    <div class="wrapper-tips-item">
                        <el-switch v-model="isPortsShow" @change="changePortsShow"></el-switch>
                        <span>链接桩常显</span>
                    </div>
                </div>
            </div>
            <div v-if="editDrawer" class="edit-main">
                <div class="edit-main-title">
                    <h3>{{ editTitle }}</h3>
                    <i class="el-icon-close" @click="closeEditForm"></i>
                </div>
                <div v-if="editTitle === '编辑节点'" class="form-main">
                    <el-form ref="nodeForm" :model="form" label-width="80px">
                        <el-form-item label="节点文本">
                            <el-input v-model="form.labelText" size="small" @input="changeNode('labelText', form.labelText)"></el-input>
                        </el-form-item>
                        <el-form-item label="字体大小">
                            <el-input v-model="form.fontSize" size="small" @input="changeNode('fontSize', form.fontSize)"></el-input>
                        </el-form-item>
                        <el-form-item label="字体颜色">
                            <el-color-picker v-model="form.fontFill" @change="changeNode('fontFill', form.fontFill)"></el-color-picker>
                        </el-form-item>
                        <el-form-item label="节点背景">
                            <el-color-picker v-model="form.fill" @change="changeNode('fill', form.fill)"></el-color-picker>
                        </el-form-item>
                        <el-form-item label="边框颜色">
                            <el-color-picker v-model="form.stroke" @change="changeNode('stroke', form.stroke)"></el-color-picker>
                        </el-form-item>
                        <div class="see-box">
                            <h5>预览</h5>
                            <div
                                class="see-item"
                                :style="{ background: form.fill, color: form.fontFill, 'border-color': form.stroke, 'font-size': form.fontSize + 'px' }"
                            >
                                {{ form.labelText }}
                            </div>
                        </div>
                    </el-form>
                </div>
                <div v-if="editTitle === '编辑图片节点'" class="form-main">
                    <el-form ref="imageForm" :model="form" label-width="80px">
                        <el-form-item label="节点文本">
                            <el-input v-model="form.labelText" size="small" @input="changeImageNode('labelText', form.labelText)"></el-input>
                        </el-form-item>
                        <el-form-item label="字体颜色">
                            <el-color-picker v-model="form.labelFill" @change="changeImageNode('labelFill', form.labelFill)"></el-color-picker>
                        </el-form-item>
                        <el-form-item label="节点背景">
                            <el-color-picker v-model="form.fill" @change="changeImageNode('fill', form.fill)"></el-color-picker>
                        </el-form-item>
                        <el-form-item label="图片地址">
                            <el-input v-model="form.xlinkHref" size="small" placeholder="图片地址" @input="changeImageNode('xlinkHref', form.xlinkHref)"></el-input>
                            <el-image :src="form.xlinkHref" style="width: 80px; height: 80px; background: #f2f2f2" fit="fill"></el-image>
                        </el-form-item>
                        <el-form-item label="图片尺寸">
                            <span style="font-size: 14px; padding-right: 5px; color: #888">宽</span
                            ><el-input-number v-model="form.width" :min="0" label="宽" size="mini" @change="changeImageNode('width', form.width)"></el-input-number>
                            <span style="font-size: 14px; padding-right: 5px; color: #888">高</span
                            ><el-input-number
                                v-model="form.height"
                                :min="0"
                                label="高"
                                size="mini"
                                @change="changeImageNode('height', form.height)"
                            ></el-input-number>
                        </el-form-item>
                    </el-form>
                </div>
                <div v-if="editTitle === '编辑连线'" class="form-main">
                    <el-form ref="edgeForm" :model="form" label-width="80px">
                        <el-form-item label="标签内容">
                            <el-input
                                v-model="form.label"
                                size="small"
                                placeholder="标签文字,空则没有"
                                @input="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"
                            ></el-input>
                            <div v-if="form.label" class="label-style">
                                <p>
                                    字体颜色:<el-color-picker
                                        v-model="labelForm.fontColor"
                                        size="mini"
                                        @change="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"
                                    ></el-color-picker>
                                </p>
                                <p>
                                    背景颜色:<el-color-picker
                                        v-model="labelForm.fill"
                                        size="mini"
                                        @change="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"
                                    ></el-color-picker>
                                </p>
                                <p>
                                    描边颜色:<el-color-picker
                                        v-model="labelForm.stroke"
                                        size="mini"
                                        @change="changeEdgeLabel(form.label, labelForm.fontColor, labelForm.fill, labelForm.stroke)"
                                    ></el-color-picker>
                                </p>
                            </div>
                        </el-form-item>
                        <el-form-item label="线条颜色">
                            <el-color-picker v-model="form.stroke" size="small" @change="changeEdgeStroke"></el-color-picker>
                        </el-form-item>
                        <el-form-item label="线条样式">
                            <el-select v-model="form.connector" size="small" placeholder="请选择" @change="changeEdgeConnector">
                                <el-option label="直角" value="normal"></el-option>
                                <el-option label="圆角" value="rounded"></el-option>
                                <el-option label="平滑" value="smooth"></el-option>
                                <el-option label="跳线(两线交叉)" value="jumpover"></el-option>
                            </el-select>
                        </el-form-item>
                        <el-form-item label="线条宽度">
                            <el-input-number
                                v-model="form.strokeWidth"
                                size="small"
                                @change="changeEdgeStrokeWidth"
                                :min="2"
                                :step="2"
                                :max="6"
                                label="线条宽度"
                            ></el-input-number>
                        </el-form-item>
                        <el-form-item label="双向箭头">
                            <el-switch v-model="form.isArrows" @change="changeEdgeArrows"></el-switch>
                        </el-form-item>
                        <el-form-item label="流动线条">
                            <el-switch v-model="form.isAnit" @change="changeEdgeAnit"></el-switch>
                        </el-form-item>
                        <el-form-item label="调整线条">
                            <el-switch v-model="form.isTools" @change="changeEdgeTools"></el-switch>
                        </el-form-item>
                    </el-form>
                </div>
                <div class="edit-btn">
                    <el-button type="danger" @click="handlerDel" style="width: 100%">删除此{{ editTitle === '编辑节点' ? '节点' : '连线' }}</el-button>
                </div>
            </div>
        </div>
        <!-- <div class="wrapper-btn" v-if="isChange">
            <el-button type="success" @click="handlerSend">保存当前方案</el-button>
        </div> -->
    </div>
</template>
<script setup lang="ts" name="AntV6X">
import { ElMessage, ElMessageBox } from 'element-plus';
import { Graph, Shape } from '@antv/x6';
import { Keyboard } from '@antv/x6-plugin-keyboard'; // 注意要单独引入
import { Selection } from '@antv/x6-plugin-selection';  // 注意要单独引入
import { configSetting, configNodeShape, configNodePorts, configEdgeLabel, graphBindKey } from './config/antvSetting';
const props = defineProps({
    height: {
        type: String,
        default: '720px',
    },
    modelValue: {
        type: String,
        default: '',
    },
});
const emits = defineEmits(['cellChanged', 'finish', 'update:modelValue']);
const graphObj = ref() as any;
const isPortsShow = ref(true);
const changePortsShow = (val: boolean) => {
    const container = document.getElementById('wrapper');
    const ports = (container && container.querySelectorAll('.x6-port-body')) || ([] as any[]);
    if (!ports.length) return;
    for (let i = 0, len = ports.length; i < len; i = i + 1) {
        ports[i].style.visibility = val ? 'visible' : 'hidden';
    }
};
// 初始化方法
const initGraph = () => {
    const graph = new Graph({
        container: document.getElementById('wrapper') as any,
        ...configSetting(Shape),
    }) as any;
    // 画布事件 注意这里1.x和2.x的是有区别,选择和键盘事件都要单独引入,别搞错了。
    graph.on('node:mouseenter', () => {
        changePortsShow(true);
    });
    graph.on('node:mouseleave', () => {
        if (isPortsShow.value) return;
        changePortsShow(false);
    });
    // 点击编辑
    graph.on('cell:click', ({ cell }) => {
        editForm(cell);
    });
    // 画布键盘事件
    graph.use(
        new Keyboard({
            enabled: true,
        })
    );
    graph.use(
        new Selection({
            enabled: true,
            multiple: true,
            rubberband: true,
            movable: true,
            showNodeSelectionBox: true,
        })
    );
    graphBindKey(graph);
    // 删除
    graph.bindKey(['delete', 'backspace'], () => {
        handlerDel();
    });
    // 赋值
    graphObj.value = graph;
    // #endregion
    // 返现方法
    if (props.modelValue && JSON.parse(props.modelValue).length) {
        const resArr = JSON.parse(props.modelValue);
        const portsGroups = configNodePorts().groups;
        if (resArr.length) {
            const jsonTemp = resArr.map((item: any) => {
                if (item.ports) item.ports.groups = portsGroups;
                return item;
            });
            graph.fromJSON(jsonTemp);
        }
    }
};
// 拖拽
const menuItem = ref<any>({});
const menuDrag = (type: string) => {
    menuItem.value = configNodeShape(type);
};
const drop = (event: any) => {
    const nodeItem = {
        ...menuItem.value,
        x: event.offsetX - menuItem.value.width / 2,
        y: event.offsetY - menuItem.value.height / 2,
        ports: configNodePorts(),
    };
    // 创建节点
    graphObj.value.addNode(nodeItem);
};
const selectCell = ref();
const editTitle = ref('');
const editDrawer = ref(false);
let form = reactive({
    label: '',
    isArrows: false,
    strokeWidth: 2,
    labelText: '',
    fontSize: '',
    fontFill: '',
    fill: '',
    stroke: '',
    labelFill: '',
    isAnit: false,
    isTools: false,
    xlinkHref: '',
    height: '',
    width: '',
    connector: '',
});
const labelForm = ref({
    fontColor: '#333',
    fill: '#FFF',
    stroke: '#555',
});
const editForm = (cell: any) => {
    if (selectCell.value) selectCell.value.removeTools(); // 删除修改线的工具
    selectCell.value = cell;
    // 编辑node节点
    if (cell.isNode() && cell.data.type && cell.data.type.includes('default')) {
        editTitle.value = '编辑节点';
        const body = cell.attrs.body || cell.attrs.rect || cell.attrs.polygon || cell.attrs.circle;
        const obj = {
            labelText: cell.attrs.label.text || '',
            fontSize: cell.attrs.label.fontSize || 14,
            fontFill: cell.attrs.label.fill || '',
            fill: body.fill || '',
            stroke: body.stroke || '',
        };
        Object.assign(form, obj);
        return (editDrawer.value = true);
    }
    // 编辑图片节点
    if (cell.isNode() && cell.data.type && cell.data.type === 'otherImage') {
        editTitle.value = '编辑图片节点';
        const attrs = cell.attrs || { body: { fill: '' }, label: { text: '', fill: '' }, image: { xlinkHref: '', height: 80, width: 80 } };
        const obj = {
            fill: attrs.body.fill,
            labelText: attrs.label.text,
            labelFill: attrs.label.fill,
            height: (attrs.image && attrs.image.height) || 80,
            width: (attrs.image && attrs.image.width) || 80,
            xlinkHref: (attrs.xlinkHref && attrs.image.xlinkHref) || 'https://gw.alipayobjects.com/zos/bmw-prod/2010ac9f-40e7-49d4-8c4a-4fcf2f83033b.svg',
        };
        Object.assign(form, obj);
        return (editDrawer.value = true);
    }
    // 编辑线
    if (!cell.isNode() && cell.shape === 'edge') {
        editTitle.value = '编辑连线';
        const obj = {
            label: cell.labels && cell.labels[0] ? cell.labels[0].attrs.labelText.text : '',
            stroke: cell.attrs.line.stroke || '',
            connector: 'rounded',
            strokeWidth: cell.attrs.line.strokeWidth || '',
            isArrows: cell.attrs.line.sourceMarker ? true : false,
            isAnit: cell.attrs.line.strokeDasharray ? true : false,
            isTools: false,
        };
        Object.assign(form, obj);
        // 看是否有label
        const edgeCellLabel = (cell.labels && cell.labels[0] && cell.labels[0].attrs) || false;
        if (form.label && edgeCellLabel) {
            labelForm.value = {
                fontColor: edgeCellLabel.labelText.fill || '#333',
                fill: edgeCellLabel.labelBody.fill || '#fff',
                stroke: edgeCellLabel.labelBody.stroke || '#555',
            };
        } else {
            labelForm.value = { fontColor: '#333', fill: '#FFF', stroke: '#555' };
        }
        return (editDrawer.value = true);
    }
};
const closeEditForm = () => {
    editDrawer.value = false;
    if (selectCell.value) selectCell.value.removeTools();
};
// 修改一般节点
const changeNode = (type: string, value: any) => {
    switch (type) {
        case 'labelText':
            selectCell.value.attr('label/text', value);
            break;
        case 'fontSize':
            selectCell.value.attr('label/fontSize', value);
            break;
        case 'fontFill':
            selectCell.value.attr('label/fill', value);
            break;
        case 'fill':
            selectCell.value.attr('body/fill', value);
            break;
        case 'stroke':
            selectCell.value.attr('body/stroke', value);
            break;
    }
};
// 修改图片节点
const changeImageNode = (type: string, value: any) => {
    switch (type) {
        case 'labelText':
            selectCell.value.attr('label/text', value);
            break;
        case 'labelFill':
            selectCell.value.attr('label/fill', value);
            break;
        case 'fill':
            selectCell.value.attr('body/fill', value);
            break;
        case 'xlinkHref':
            selectCell.value.attr('image/xlinkHref', value);
            break;
        case 'height':
            selectCell.value.attr('image/height', value);
            break;
        case 'width':
            selectCell.value.attr('image/width', value);
            break;
    }
};
// 修改边的属性
const changeEdgeLabel = (label: string, fontColor: string, fill: string, stroke: string) => {
    selectCell.value.setLabels([configEdgeLabel(label, fontColor, fill, stroke)]);
    if (!label) labelForm.value = { fontColor: '#333', fill: '#FFF', stroke: '#555' };
};
const changeEdgeStroke = (val: string) => {
    selectCell.value.attr('line/stroke', val);
};
const changeEdgeConnector = (val: string) => {
    switch (val) {
        case 'normal':
            selectCell.value.setConnector(val);
            break;
        case 'smooth':
            selectCell.value.setConnector(val);
            break;
        case 'rounded':
            selectCell.value.setConnector(val, { radius: 20 });
            break;
        case 'jumpover':
            selectCell.value.setConnector(val, { radius: 20 });
            break;
    }
};
const changeEdgeStrokeWidth = (val: any) => {
    if (form.isArrows) {
        selectCell.value.attr({
            line: {
                strokeWidth: val,
                sourceMarker: {
                    width: 12 * (val / 2) || 12,
                    height: 8 * (val / 2) || 8,
                },
                targetMarker: {
                    width: 12 * (val / 2) || 12,
                    height: 8 * (val / 2) || 8,
                },
            },
        });
    } else {
        selectCell.value.attr({
            line: {
                strokeWidth: val,
                targetMarker: {
                    width: 12 * (val / 2) || 12,
                    height: 8 * (val / 2) || 8,
                },
            },
        });
    }
};
const changeEdgeArrows = (val: boolean) => {
    if (val) {
        selectCell.value.attr({
            line: {
                sourceMarker: {
                    name: 'block',
                    width: 12 * (form.strokeWidth / 2) || 12,
                    height: 8 * (form.strokeWidth / 2) || 8,
                },
                targetMarker: {
                    name: 'block',
                    width: 12 * (form.strokeWidth / 2) || 12,
                    height: 8 * (form.strokeWidth / 2) || 8,
                },
            },
        });
    } else {
        selectCell.value.attr({
            line: {
                sourceMarker: '',
                targetMarker: {
                    name: 'block',
                    size: 10 * (form.strokeWidth / 2) || 10,
                },
            },
        });
    }
};
const changeEdgeAnit = (val: boolean) => {
    if (val) {
        selectCell.value.attr({
            line: {
                strokeDasharray: 5,
                style: {
                    animation: 'ant-line 30s infinite linear',
                },
            },
        });
    } else {
        selectCell.value.attr({
            line: {
                strokeDasharray: 0,
                style: {
                    animation: '',
                },
            },
        });
    }
};
const changeEdgeTools = (val: boolean) => {
    if (val) selectCell.value.addTools(['vertices', 'segments']);
    else selectCell.value.removeTools();
};
// 删除节点
const handlerDel = () => {
    ElMessageBox.confirm(`此操作将永久删除此${editTitle.value === '编辑节点' ? '节点' : '连线'}, 是否继续?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
    })
        .then(() => {
            const cells = graphObj.value.getSelectedCells();
            if (cells.length) {
                graphObj.value.removeCells(cells);
            } else graphObj.value.removeCell(selectCell.value);
            Object.assign(form, {});
            editDrawer.value = false;
            ElMessage({ type: 'success', message: '删除成功!' });
        })
        .catch(() => {});
};
// 导出
const handlerSend = () => {
    const { cells: jsonArr } = graphObj.value.toJSON();
    const tempJson = jsonArr.map((item: any) => {
        if (item.ports && item.ports.groups) delete item.ports.groups;
        if (item.tools) delete item.tools;
        return item;
    });
    if (selectCell.value) {
        selectCell.value.removeTools();
        selectCell.value = '';
    }
    emits('update:modelValue', JSON.stringify(tempJson));
};
onMounted(() => {
    initGraph();
});
onBeforeUnmount(() => {
    graphObj.value.dispose();
});
defineExpose({
    handlerSend,
});
</script>
<style lang="scss">
// 流动线条
@keyframes ant-line {
    to {
        stroke-dashoffset: -1000;
    }
}
</style>
<style lang="scss" scoped="scoped">
.all {
    border-radius: 8px;
    overflow: hidden;
}
.antv-content {
    background: var(--next-color-hover-rgba);
    display: flex;
    overflow: hidden;
    position: relative;
    .antv-menu {
        width: 200px;
        border-right: 1px solid var(--next-button-act);
        padding: 10px;
        h3 {
            padding: 10px;
        }
        li {
            padding: 10px;
            border-radius: 8px;
            border: 1px solid var(--next-button-act);
            margin: 5px 10px;
            font-size: 12px;
            display: flex;
            align-items: center;
            cursor: pointer;
            transition: all 0.5s ease;
            color: var(--next-color-white);
            &:hover {
                box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.3);
            }
            i {
                font-size: 18px;
                margin-right: 10px;
                border-color: var(--next-color-white);
            }
            strong {
                flex: 1;
            }
        }
    }
    .antv-wrapper {
        flex: 1;
        position: relative;
        .wrapper-canvas {
            position: relative;
            height: 100vh;
            min-height: 720px;
        }
        .wrapper-tips {
            padding: 10px;
            display: flex;
            align-items: center;
            position: absolute;
            top: 0;
            left: 0;
            .wrapper-tips-item {
                span {
                    padding-left: 10px;
                    font-size: 12px;
                }
            }
        }
    }
}
i.icon-oval {
    display: inline-block;
    width: 16px;
    height: 10px;
    border-radius: 10px;
    border: 2px solid #555;
}
i.icon-square {
    display: inline-block;
    width: 16px;
    height: 10px;
    border: 2px solid #555;
}
i.icon-ysquare {
    display: inline-block;
    width: 16px;
    height: 10px;
    border-radius: 4px;
    border: 2px solid #555;
}
i.icon-rhombus {
    display: inline-block;
    width: 10px;
    height: 10px;
    border: 2px solid #555;
    transform: rotate(45deg);
}
i.icon-rhomboid {
    display: inline-block;
    width: 10px;
    height: 10px;
    border: 2px solid #555;
    transform: skew(-30deg);
}
i.icon-circle {
    display: inline-block;
    width: 16px;
    height: 16px;
    border-radius: 16px;
    border: 2px solid #555;
}
.edit-main {
    position: absolute;
    right: 0;
    top: 0;
    height: 100%;
    width: 280px;
    border-left: 1px solid var(--next-boder-color);
    box-shadow: 0 -10px 10px rgba($color: #000000, $alpha: 0.3);
    padding: 20px;
    background: var(--next-bg-main);
    box-sizing: border-box;
    .edit-main-title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        h3 {
            flex: 1;
        }
        i {
            cursor: pointer;
            font-size: 20px;
            opacity: 0.7;
            &:hover {
                opacity: 1;
            }
        }
    }

    .form-main {
        padding: 20px 0;
        .label-style {
            background: #f2f2f2;
            padding: 0 10px;
            p {
                display: flex;
                align-items: center;
                font-size: 12px;
            }
        }
    }
    .edit-btn {
    }
    .see-box {
        padding: 20px;
        background: var(--next-button-act);
        h5 {
            padding-bottom: 10px;
        }
        .see-item {
            padding: 10px 30px;
            border: 2px solid #333;
            text-align: center;
        }
    }
}
.wrapper-btn {
    text-align: center;
    padding: 20px;
    background: #010c30;
}
</style>


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容