利用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;
			// ... 其他操作
		}
	}
相关推荐
云枫晖4 小时前
深入浅出npm:现代JavaScript项目基石
前端·javascript·node.js
不一样的少年_4 小时前
你家孩子又偷玩网页游戏? 试试这个防沉迷工具
前端·javascript·浏览器
春秋半夏4 小时前
vue2二次封装el-select支持collapse-tags-tooltip
前端
昔人'4 小时前
css`scrollbar-gutter`防止滚动条可见性变化时发生布局偏移
前端·css
掘金安东尼4 小时前
前端周刊第436期(2025年10月13日–10月19日)
前端·javascript·github
小叫花子4 小时前
用 UniApp 开发微信小程序蓝牙通信功能
前端
勤奋菲菲4 小时前
深入理解HTML文本标签:构建网页内容的基础
前端·html
昔人'4 小时前
html`<mark>`
前端·javascript·html