概要
在vue2项目中使用antV X6,实现下拉菜单网拓关系图
整体架构流程
1.首先,使用npm install @antv/x6 --save安装最新版本的antV X6插件,下载好后去package.json文件中查看相应版本信息
2.紧接着,引入Graph库并初始化画布,创建画布容器,以及完成你需要的流程图,我是直接根据需求封装了组件
代码部分:(需要可以自取,根据需求进行改写)
<template>
<div style="height: 100vh; background-color: #f7f8f9">
<div id="container" style="width: 100%; height: 100%"></div>
</div>
</template>
<script>
// eslint-disable-next-line no-unused-vars
import { Graph, ObjectExt, Cell } from "@antv/x6";
export default {
mounted() {
this.init();
},
methods: {
init() {
// #region 注册基础图形
Graph.registerNode(
"class",
{
inherit: "rect",
markup: [
{
tagName: "rect",
selector: "body",
},
// {
// tagName: "rect",
// selector: "name-rect",
// },
{
tagName: "rect",
selector: "dropdown-rect",
},
{
tagName: "rect",
selector: "attrs-rect",
},
// {
// tagName: "rect",
// selector: "methods-rect",
// },
{
tagName: "foreignObject", // 添加foreignObject元素
selector: "dropdown-container",
},
// {
// tagName: "text",
// selector: "name-text",
// },
{
tagName: "text",
selector: "attrs-text",
attrs: {
dy: "10em",
},
},
{
tagName: "text",
selector: "methods-text",
},
{
tagName: "image",
selector: "search",
},
],
attrs: {
rect: {
width: 260,
},
body: {
stroke: "#eff4ff",
// refWidth: '100%',
// refHeight: '100%',
rx: 5,
ry: 5,
},
// "name-rect": {
// fill: "#5f95ff",
// stroke: "#fff",
// strokeWidth: 0.5,
// },
"dropdown-rect": {
fill: "#eff4ff",
stroke: "#fff",
strokeWidth: 0.5,
},
"dropdown-container": {
// size:{height:30},
// 设置foreignObject的位置,使其位于节点上方
// y: 0, // 根据需要调整这个值以确保下拉框位于节点上方
// refY: 0, // 相对于节点底部定位
// refX: 0,
// width: 250,
// height:36,
// html: `
// <div style="width:100%;background: #5f95ff; padding:0 10px;">
// <select style="width:100%;background: #5f95ff;border:0;color: #fff;" disabled>
// <option value="option1"></option>
// </select>
// </div>
// `,
},
"attrs-rect": {
fill: "#FFF",
stroke: "#fff",
strokeWidth: 0.5,
rx: 4, // 设置圆角的半径
ry: 4, // 设置圆角的半径
},
// "methods-rect": {
// fill: "#eff4ff",
// stroke: "#fff",
// strokeWidth: 0.5,
// },
// "name-text": {
// ref: "name-rect",
// refY: 0.5,
// refX: 0.5,
// textAnchor: "middle",
// fontWeight: "bold",
// fill: "#fff",
// fontSize: 14,
// },
"attrs-text": {
ref: "attrs-rect",
refY: 70,
refX: 20,
textAnchor: "left",
fill: "black",
fontSize: 14,
fontWeight: "bold",
},
// "content1-text": {
// ref: "attrs-rect",
// refY: 70,
// refX: 20,
// textAnchor: "left",
// // fill: "black",
// fontSize: 14,
// fontWeight: "bold",
// },
// "content2-text": {
// ref: "attrs-rect",
// refY: 70,
// refX: 20,
// textAnchor: "left",
// // fill: "black",
// fontSize: 14,
// fontWeight: "bold",
// },
// "content3-text": {
// ref: "attrs-rect",
// refY: 70,
// refX: 20,
// textAnchor: "left",
// // fill: "black",
// fontSize: 14,
// fontWeight: "bold",
// },
// "methods-text": {
// ref: "methods-rect",
// refY: 0.5,
// refX: 5,
// textAnchor: "left",
// fill: "black",
// fontSize: 10,
// },
search: {
width: 30,
height: 30,
refX: "86%",
refY: "66%",
"xlink:href": "../../../../static/search.png",
},
},
ports: {
groups: {
left: {
position: "left",
attrs: {
circle: {
magnet: true,
stroke: "#8f8f8f",
r: 5,
},
},
},
bottom: {
position: "bottom",
attrs: {
circle: {
magnet: true,
stroke: "#8f8f8f",
r: 5,
},
},
},
right: {
position: "right",
attrs: {
circle: {
magnet: true,
stroke: "#8f8f8f",
r: 5,
},
},
},
top: {
position: "top",
attrs: {
circle: {
magnet: true,
stroke: "#8f8f8f",
r: 5,
},
},
},
},
},
propHooks(meta) {
const {
name,
attributes,
content1,
content2,
content3,
...others
} = meta;
if (!(name && attributes)) {
return meta;
}
const rects = [
// { type: "name", text: name },
{ type: "attrs", text: attributes },
// { type: "content1", text: content1 },
// { type: "content2", text: content2 },
// { type: "content3", text: content3 },
];
let offsetY = 0;
rects.forEach((rect) => {
const height = rect.text.length * 40 + 16;
// ObjectExt.setByPath(
// others,
// `attrs/${rect.type}-text/text`,
// rect.text
// .map((t) => {
// return t;
// })
// .join("\n")
// );
ObjectExt.setByPath(
others,
`attrs/${rect.type}-text/text`,
rect.text
.map((t) => {
return t;
})
.join("\n")
);
ObjectExt.setByPath(
others,
`attrs/${rect.type}-rect/height`,
height
);
ObjectExt.setByPath(
others,
`attrs/${rect.type}-rect/transform`,
"translate(0," + offsetY + ")"
);
offsetY += height;
});
others.size = { width: 260, height: offsetY };
return others;
},
},
true
);
// 聚合
Graph.registerEdge(
"aggregation",
{
inherit: "edge",
attrs: {
line: {
stroke: "#000", // 设置线条颜色为黑色
strokeWidth: 1,
// sourceMarker: {
// name: 'path',
// d: 'M 30 10 L 20 16 L 10 10 L 20 4 z',
// fill: 'white',
// offsetX: -10,
// },
targetMarker: {},
sourceMarker: {},
// targetMarker: {
// name: 'circle',
// r: 4, // 圆的半径
// fill: 'none', // 设置为none以创建空心效果
// stroke: '#000', // 圆的边框颜色
// strokeWidth: 1, // 圆的边框宽度
// },
// targetMarker: {
// name: 'circle',
// r: 4, // 圆的半径
// fill: 'none', // 设置为none以创建空心效果
// stroke: '#000', // 圆的边框颜色
// strokeWidth: 1, // 圆的边框宽度
// },
},
},
},
true
);
const graph = new Graph({
container: document.getElementById("container"),
});
fetch("../../../static/data.json")
.then((response) => response.json())
.then((data) => {
const cells = [];
const edgeShapes = [
"extends",
"composition",
"implement",
"aggregation",
"association",
];
const nodes = [];
data.forEach((item) => {
if (edgeShapes.includes(item.shape)) {
item.source.cell = nodes.find(
(node) => node.cell === item.source.cell
).node;
item.target.cell = nodes.find(
(node) => node.cell === item.target.cell
).node;
// 添加链接线
item = {
...item,
router: {
// 设置连接线为直角
name: "manhattan",
args: {},
},
};
cells.push(graph.createEdge(item));
} else {
// 添加图形
item = {
...item,
};
const node = graph.createNode(item);
// node.setAttrs({
// 'dropdown-container' :{
// // 设置foreignObject的位置,使其位于节点上方
// y: 20, // 根据需要调整这个值以确保下拉框位于节点上方
// refX: 0, // 水平居中
// refY: 2, // 相对于节点底部定位
// width: 160,
// html: `
// <div style="width:100%;background: #5f95ff; padding:10px">
// <select style="width:100%;background: #5f95ff;" disabled>
// <option value="option1"></option>
// </select>
// </div>
// `,
// }
// })
// node.setAttrs(
// {
// "dropdown-container": {
// y: 0,
// // refX: 0.5,
// html: `
// <div class='title'>
// <div>${item.name[0]}</div><div class="arrow-down"></div>
// </div>
// `,
// },
// // <select style="width:100%;" >
// // <option value="${item.name[0]}" selected></option>
// // </select>
// },
// { deep: false }
// );
node.attr(
"dropdown-container/html",
`
<div class='title'>
<div>${item.name[0]}</div><div class="arrow-down"></div>
</div>
`
);
nodes.push({ cell: "node" + item.id, node });
console.log("item", item);
cells.push(node);
}
});
graph.resetCells(cells);
graph.zoomToFit({ padding: 10, maxScale: 1 });
this.$nextTick(() => {});
});
},
},
};
</script>
<style lang="">
.arrow-down {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid white;
margin-left: 10px;
}
.title {
width: 260px;
height: 36px;
background: #165dff;
padding: 0 20px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
font-weight: 800;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
/* margin-top: -36px; */
/* position:relative;
top:-36px */
}
.x6-graph-svg {
position: absolute;
top: -204px;
right: 0;
bottom: 0;
left: 0;
}
</style>
3.其中用到了放大镜图片,我将其放进了 静态资源static文件夹内
4.结果展示