利用AndVX6开发流程图——问题总结

X6 是基于 HTML 和 SVG 的图编辑引擎,提供低成本的定制能力和开箱即用的内置扩展,方便我们快速搭建 DAG 图、ER 图、流程图、血缘图等应用,详细见官方文档。最近用到了该项技术,做下问题总结,记录一下解决方案。

自定义元素ID

元素在拖动放到画布后,会自动生成一个ID,代表该元素会有唯一性,但是这个ID是很长且没有特点的,因此想要自定义ID。官方示例地址

但元素生成后ID不再支持修改,ID改变这个时候画布会认为你只是新生成了一个元素。那么节点信息就需要你额外再复制一遍,这样比较麻烦且不支持异步获取了。

解决方法:监听元素从拖动时,异步生成ID,且利用Dnd 实例自定义生成元素,放到画布后,该元素就会以该ID为主

具体代码如下

增加监听,这里只做获取ID

ini 复制代码
<div id="stencil" class="stencil-container" @mousedown="startDrag($event)"></div>
···
const startDrag = async (event) => {
    const res = await getNodeId({});
    if (res.success) {
      // 更新节点的ID属性
      newNodeId.value = res.data
    }
}
···

初始化时会生成stencil实例(用于注册拖拽元素),官方示例有,这里不再具体介绍

创建Dnd实例,并监听创建新节点时,用新ID进行覆盖,然后绑定到stencil实例即可成功实现ID替换

ini 复制代码
//实现自定义节点替换
const initStencilEvents = async () => {
  if (!stencil.value || !stencil.value.container) return;

  // 创建 Dnd 实例来处理拖拽
  const dnd = new Dnd({
    target: graph.value,
    getDropNode: (draggingNode) => {
      // 创建一个新的节点配置对象,包含自定义 ID
      const nodeConfig = {
        ...draggingNode.toJSON(),
        id: newNodeId.value
      };
      // 创建带有自定义 ID 的新节点
      const newNode = graph.value.createNode(nodeConfig);
      console.log('创建节点,ID:', newNodeId.value);
      return newNode;
    },
  });

  // 将 Dnd 实例绑定到 stencil
  stencil.value.dnd = dnd;
};

增加元素小工具

对于一些元素可以增加图标之类的展示,可以利用addTools方法实现,细节方法见官方文档

php 复制代码
myNode.addTools([{
	name: 'button',
	args: {
		markup: [
			{
				tagName: 'circle',
				selector: 'button',
				attrs: {
					r: 8, // 圆的半径
					fill: 'white', // 填充色为白色
					cursor: 'pointer', // 鼠标指针样式
					// 以下为可选属性,用于进一步控制外观和位置:
					rx: 0, // 圆角x半径(对圆形通常为0或不设置)
					ry: 0, // 圆角y半径(对圆形通常为0或不设置)
					zIndex: 20, // 层级(确保显示在最前)
					transform: 'translate(0, 16)', // 变换(可进行平移、旋转等操作)
				},
			},
			{
				tagName: 'image',
				selector: 'icon',
				attrs: {
					'xlink:href': statusType, // 或 Base64 Data URL, 或在线 URL
					width: 18,
					height: 18,
					x: -10, // 居中
					y: 8, // 居中
					cursor: 'pointer',
					zIndex: 1000,

				}
			}
		],
		zIndex: 1000,
		x: '100%',
		y: '0%',
		// offset: { x: -5, y: -36 },
	},
}]);

这里我们用到了自定义图标statusType我传值是Base64,这里需要注意一个点,位置信息放在args这一层级,且相对位置,不然会出现元素增加或者缩小时图标位置没有变化,另外注意此方法在元素生成后调用

增加元素悬浮提示

鼠标放置元素上时,出现弹窗提示,大概下方效果

实现比较简单,监听画布中鼠标划入元素即可

graph.value为画布注册实例

这里也需要注意一个点,画布坐标和浏览器视口的页面坐标不一样,要进行转化

ini 复制代码
	// 获取提示框DOM元素
	const tooltipEl = document.getElementById('x6-tooltip');
	// 监听鼠标进入元素
	graph.value.on('cell:mouseenter', ({ cell, e }) => {
			console.log(cell.shape);
			const data = cell.getData()
			let tooltipContent = ''
			//有执行信息再显示
			if (data.startTime) {
				tooltipContent = `<div style="text-align:left"></div> `;//这里为提示信息
				// 更新提示框内容
				tooltipEl.innerHTML = tooltipContent;
				// 获取当前单元格的包围盒(相对于画布)
				const bbox = cell.getBBox();
				const tipX = bbox.x + bbox.width / 2; // 元素水平中心
				const tipY = bbox.y - 10; // 元素顶部向上偏移10px
				//将计算出的画布坐标转换为相对于浏览器视口的页面坐标
				const clientPoint = graph.value.localToPage(tipX, tipY); // 转换为页面坐标
				// 设置提示框位置并显示
				tooltipEl.style.left = clientPoint.x - 150 + 'px';
				tooltipEl.style.top = clientPoint.y - 40 + 'px';
				tooltipEl.style.display = 'block';
			}

	});

鼠标离开元素

dart 复制代码
// 监听鼠标离开单元格
	graph.value.on('cell:mouseleave', () => {
		// 隐藏提示框
		tooltipEl.style.display = 'none';
	})

css样式

css 复制代码
.x6-tooltip {
	display: none;
	/* 默认隐藏 */
	position: absolute;
	/* 绝对定位 */
	z-index: 1000;
	/* 确保提示框在最上层 */
	background-color: #333;
	color: #fff;
	padding: 8px 12px;
	border-radius: 4px;
	font-size: 12px;
	pointer-events: none;
	/* 防止提示框干扰鼠标事件 */
	/* 可添加更多样式如阴影、箭头等 */
}

增加右击菜单

原理和增加元素悬浮提示类似,监听右击事件,增加悬浮窗

定义菜单样式

xml 复制代码
<div id="context-menu" class="context-menu">
			<div class="menu-item" data-action="retry">重试</div>
			<div class="menu-item" data-action="cancle">取消</div>
			<!-- 更多菜单项 -->
</div>

JS监听点击事件,将元素添加上去,并绑定处理菜单点击动作

ini 复制代码
// 监听单元格(节点或边)的右键事件
	graph.value.on('cell:contextmenu', ({ e, cell, x, y }) => {
		
			e.preventDefault(); // 阻止浏览器默认的右键菜单

			// 计算菜单显示位置(基于鼠标事件位置)
			const left = e.clientX;
			const top = e.clientY;

			// 更新菜单位置并显示
			contextMenu.style.left = `${left}px`;
			contextMenu.style.top = `${top}px`;
			contextMenu.style.display = 'block';

			// 为菜单项添加点击事件(可选:每次显示时重新绑定)
			const menuItems = contextMenu.querySelectorAll('.menu-item');
			menuItems.forEach(item => {
				item.onclick = () => {
					const action = item.getAttribute('data-action');
					handleMenuAction(action, cell); // 处理菜单动作
					hideMenu(); // 隐藏菜单
				};
			});
		});

监听其他地方点击消除菜单

csharp 复制代码
	// 监听画布点击事件以隐藏菜单
	graph.value.on('cell:click', hideMenu);
	graph.value.on('blank:click', hideMenu); // 点击画布空白处也隐藏

处理菜单动作

javascript 复制代码
//隐藏菜单
	function hideMenu() {
		contextMenu.style.display = 'none';
	}
	// 处理菜单动作
	async function handleMenuAction(action, cell) {
		nodeJudgmentType.value = 2
		nodeJudgmentId.value = cell.id
		switch (action) {
			case 'retry':
				// 编辑逻辑,例如弹出编辑框
				break;
			case 'cancle':
				// 编辑逻辑,例如弹出编辑框
				break;
			// ... 其他操作
		}
	}
相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax