思维导图组件尝鲜——OpenTiny的MindMap组件

478 阅读3分钟

由于业务需要绘制思维导图,我探索了市面上的一些组件,最终选择了OpenTinyMindMap组件。

Demo介绍

官网Demo体验:opentiny.design/tiny-vue/zh…

待讨论的业务相关问题

简单介绍一下我要做的业务:二级节点的数据是可选择的,并且还要给它携带一些自己的属性,然后三级节点根据二级节点的数据,做联动选择和编辑。

image.png

二级节点只能选一个国家,并且输入分数;三级节点只能选该国家的城市,并且输入分数;不允许创建四级节点。(这里简化了,实际上的业务比这更复杂)

如果我们直接使用这个组件,显然是不符合业务要求的,因为编辑操作有诸多的限制。如果把一切都交给最后一步保存的时候去校验,用户体验是非常不好的,并且还有中文匹配操作,接口复杂度很高,而且容易出错。

业务的关键点

  1. 对于编辑操作,不能使用组件自带的双击编辑文本,要用接口获取国家、城市,用Select组件去选择城市,用Input组件去输入分数;

  2. 对于新增操作,需要进行拦截。先判断当前选中的节点是什么类型的,才能决定它的子节点或兄弟节点是什么类型的;

  3. 对于删除操作,需要有二次确认框,避免误删;

  4. 每一个操作结束之后,就要保存一下,类似于草稿的作用;

  5. 不允许复制节点、移动节点。(不允许把钓鱼岛从中国移到日本)

解决方案的讨论

针对上述问题,考虑到组件支持的功能,我做了如下的技术方案设计:

  1. 不允许拖拽、不用键盘
const options = {
    draggable: false,
    editable: true,
    keypress: false,
 };
  1. 自己监听键盘事件,做特殊的逻辑处理
document.addEventListener('keydown', function (event) {
    if (event.key === 'Tab' || event.key === 'Enter') {
        addNode();
    } else if (event.key === 'Delete') {
      deleteNode();
    }
});
  1. 根据当前选中的节点,做新增、编辑、删除操作
const D = ref(null)
const onCreate = (e) => {
    D.value = e; // 这个就是思维导图的实例
};

// 获取当前选中的节点
function getSelectedNode() {
    if (D && D.value && D.value.currentNode) {
      return D.value.currentNode.nodeObj;
    }
    return null;
}

// 调用 getSelectedNode 获取当前选中的节点

// 新增操作
function addNode() {
    let curr = getSelectedNode();
    if (curr) {
        let nodeType = curr.nodeType;
        if (nodeType == 'three') {
            // 三级节点不允许创建子节点
            createMessage.error('三级科室下不能再添加新记录');
        } else {
        openModal(true, {
            parent: nodeType, // 当前选中的节点数据 其实就是待新建的父节点,根据这个节点的类型,我们就知道新建的节点是国家还是城市
        });
      }
    }
  }

// 删除操作
  function deleteNode() {
    let curr = getSelectedNode();
    if (curr) {
      let nodeType = curr.nodeType;
      if (nodeType == 'root') {
        createMessage.error('根节点不允许删除');
      } else {
        Modal.confirm({
          content: '选中的节点和子节点会被删除,确定吗?',
          async onOk() {
            doDel(curr.id); // 调用删除接口
          },
          onCancel() {},
        });
      }
    }
  }

  1. 新增、编辑、删除,都是先调用接口,接口调用成功之后,再去更新页面。所以在这个业务里,只用了思维导图组件的展示功能,增删改操作都被拦截了,然后通过接口去改变数据,再去用新数据更新页面。

  2. 编辑可以写在键盘事件上,也可以用自带的编辑事件。

const onOperation = ({ info, render }) => {
    if (info.name == 'beginEdit') {
      if (info.obj.nodeType == 'root') {
		createMessage.error('根节点不允许删除');
      } else {
	  	openModal(true, {
		  curr: info.obj, // 当前选中的节点数据进行编辑
		});
      }
    }
};

其他优化

绘制完成的思维导图,会有一个默认居中的逻辑,源码在/node_modules/.pnpm/@opentiny+vue-mind-map@3.15.0/node_modules/@opentiny/vue-mind-map/lib/pc.js中的toCenter方法里

// 修改前
Xt = function Xt2() {
  this.container.scrollTo(1e4 - this.container.offsetWidth / 2, 1e4 - this.container.offsetHeight / 2);
}

// 修改后,向左的偏移量改小一点,避免左侧留太多的空白
Xt = function Xt2() {
  this.container.scrollTo(1e4 - this.container.offsetWidth / 5, 1e4 - this.container.offsetHeight / 2);
}

组件优化方向思考

  1. 是否可以给组件新增 beforeEdit、beforeDelete等方法,这样就能在这个函数里处理逻辑了;

  2. 节点是否可以支持插槽,这样就可以在节点上放一个选择框和输入框了;

  3. 是否可以支持右键菜单;

Demo地址:github.com/beat-the-bu…

效果预览地址:beat-the-buzzer.github.io/vue3-demo/#…