Xmind脑图实践

前言

在用例管理中,文本用例通常是在表单中编写,而有些测试则偏好在本地Xmind中编写测试用例,导致有的用例并不规范。为了解决这一问题,我们引入了Xmind脑图与用例列表视图之间互转的功能,以实现用例的全局管理和高效编写。本篇文章将探讨在集成Xmind脑图功能时,使用kityminder-core遇到的一些问题和技巧,旨在帮助你面临Xmind开发任务时,能够快速上手并避免潜在的坑

一、常用的脑图命令

命令 说明
this.minder.execCommand("ZoomIn"); 放大脑图
this.minder.execCommand("ZoomOut"); 缩小脑图
this.minder.execCommand("Camera", this.minder.getRoot(), 60); 设置根节点为中心节点
this.minder.execCommand('ExpandToLevel', level); 展开到指定的层级节点
this.minder.execCommand('AppendChildNode', {...groupNode,}); 插入子节点
this.minder.execCommand("ArrangeUp"); 向上移动选中节点
this.minder.execCommand('Priority', priority); 设置优先级

更多脑图命令请查看kityminder-core命令说明文档

二、Xmind脑图风格自定义

在kityminder中,脑图只支持他一些自身设定好的主题,用户可以切换不同的主题来达到不同的UI展示效果

比如用例管理最开始是用默认的天空蓝,看起来则有点杂

比如我们切换成浪漫紫呢?看起来好像会好点

js 复制代码
this.minder.execCommand('Theme', 'fresh-purple');

但是,在用例管理看起来好像还是有点突兀,风格不统一,那我们可不可以自定义一个主题风格呢?

查阅了一下他的使用文档,更多是一些常用的指令和方法的说明,并没有自定义风格主题的说明。于是,clone了它的源码,发现我们上面提到过的主题都是通过theme.register的方法进行配置的

下图则是配置的源代码,与上图的各个风格的关键词也都对上了,于是,我们可以借着这个思路,模仿它创建一个属于我们自己的脑图风格

我们查一下可用的主题列表

javascript 复制代码
window.kityminder.Minder.getThemeList()

copy他配置主题所需要的参数和方法,调整一下相关的变量

js 复制代码
import 'kity';

export function hsl(h, s, l) {
  return window.kity.Color.createHSL(h, s, l);
}

export function generate(h) {
  return {
      background: '#fbfbfb',

      'root-color': 'black', 
      'root-background': 'white',
      'root-stroke': '#bdc1ce', // 根节点border的颜色
      'root-font-size': 16,
      'root-padding': [12, 24],
      'root-margin': [30, 100],
      'root-radius': 5,
      'root-space': 10,

      'main-color': 'black',
      'main-background': 'white', // 一级节点的内容背景色
      'main-stroke': '#dcdfe6',
      'main-stroke-width': 1.5,
      'main-font-size': 14,
      'main-padding': [6, 20],
      'main-margin': 20,
      'main-radius': 3,
      'main-space': 5,

      'sub-color': 'black',
      'sub-background': 'transparent',
      'sub-stroke': 'none',
      'sub-font-size': 12,
      'sub-padding': [5, 10],
      'sub-margin': [15, 20],
      'sub-radius': 5,
      'sub-space': 5,

      'connect-color': '#bdc1ce', // 线的颜色
      'connect-width': 1,
      'connect-radius': 5,

      'selected-stroke': '#0052d9', // 节点选中时的颜色
      'selected-stroke-width': '3',
      'blur-selected-stroke': hsl(h, 10, 60),

      'marquee-background': hsl(h, 100, 80).set('a', 0.1),
      'marquee-stroke': hsl(h, 37, 60),

      'drop-hint-color': hsl(h, 26, 35),
      'drop-hint-width': 5,

      'order-hint-area-color': hsl(h, 100, 30).set('a', 0.5),
      'order-hint-path-color': hsl(h, 100, 25),
      'order-hint-path-width': 1,

      'text-selection-color': hsl(h, 100, 20),
      'line-height': 1.5,
  };
}

注册我们的主题"tm-xmind-theme"

js 复制代码
window.kityminder.Theme.register('tm-xmind-theme', generate(122));
this.minder.execCommand('Theme', 'tm-xmind-theme');

在初次导入数据的时候就设置成我们的主题

js 复制代码
const data = res.data.data;
data.template = "right";
data.theme = 'tm-xmind-theme';
this.minder.importJson(data);

到这里,我们就注册成功了自己的Xmind主题了,查一下可用主题列表

整体的UI风格就好了许多,也跟我们的网站风格保持了一致

三、资源标签底色对应

在下图中,脑图中的"用例"和上面示例中的"用例"底色对不上,为啥会这样子呢,其他时候展示还好好的

测试了一波发现"回归"标签提前比"用例"标签出现了,主动占据了第二个颜色。看了一下源码,也是按照顺序给标签设置颜色的,于是就出现了标签颜色不对应的情况

我们可以利用getResourceColor,在脑图初始化的时候,就先为标签占个颜色,达到颜色对应的效果

js 复制代码
this.minder = new window.kityminder.Minder({
    renderTo: "#minder-container",
});
this.minder.getResourceColor('分组');
this.minder.getResourceColor('用例');
this.minder.getResourceColor('前置条件');

四、往子节点数组头部插入一个新元素

用例在添加"前置条件"的时候,需要把"前置条件"节点添加到"用例"子节点的顶部

但是kityminder并没有提供这个功能,而是直接往子节点底部或者同级节点添加一个元素

我们可以利用AppendChildNode(插入子节点)和ArrangeUp(向上调整选中节点的位置)这两个命令来把新插入的"前置条件"节点调整到第一个

js 复制代码
handleAddPrecondition() {
  const children = this.selectedNodeOfMinder.children || [];
  const id = `前置条件-${Date.now()}`;
  this.minder.execCommand('AppendChildNode', {
    id,
    text: '前置条件',
    resource: ['前置条件'],
  });
  this.minder.selectById(id);
  children.forEach(() => {
    this.minder.execCommand("ArrangeUp");
  });
  this.handleUnsavedCaseListData();
},

五、防止用户选中多节点

默认用户可以通过按住control点击多个节点和用鼠标圈范围选中多个节点,但是如果我们想用户只能选中一个节点,然后针对这个节点类型进行对应的操作,需要怎么做呢?

通过查看源码,发现selectById这个方法的第二个参数可以设置节点为单选

于是,我们可以利用用户在触发多选操作时,在selectionchange节点选中的监控方法内去处理完我们需要的处理的逻辑,最后再加上this.minder.selectById(this.selectedNode.id, true);这句就可以了

js 复制代码
this.minder.on('selectionchange', () => {
    // 业务处理逻辑
    this.minder.selectById(this.selectedNode.id, true);   
});

六、解决某些节点多次双击以后不出现编辑框的问题

比如我们在想修改这种节点,但是有时某些节点会因为需要查询下级节点的数据,导致dblclick事件没有触发,只触发了首次的mousedown鼠标点击事件,所以有一些节点会出现多次双击还是打不开这个编辑输入框的情况

我们可以在首次点击节点,加载了子级数据以后,给这个节点一个loaded为true的标识,这样子用户首次双击会加载子节点数据,其余情况都可以直接双击出现编辑输入框

这里需要注意一个情况,kityminder并没有直接在文档说明给选中节点添加一个字段的命令。为了避免脑图需要重新处理数据/导入数据,查看源码发现了setData方法

js 复制代码
getData: function(key) {
    return key ? this.data[key] : this.data;
},

setData: function(key, value) {
    if (typeof key == 'object') {
        var data = key;
        for (key in data) if (data.hasOwnProperty(key)) {
            this.data[key] = data[key];
        }
    }
    else {
        this.data[key] = value;
    }
    return this;
},

/**
 * 设置节点的文本数据
 * @param {String} text 文本数据
 */
setText: function(text) {
    return this.data.text = text;
},

/**
 * 获取节点的文本数据
 * @return {String}
 */
getText: function() {
    return this.data.text || null;
},

所以,我们可以通过这个方法,直接给这个节点添加标识

js 复制代码
const node = this.minder.getSelectedNode();
node.setData('loaded', true);

七、防止各个不同节点互相拖拽进入彼此节点的情况

比如我们要禁止脑图中不同节点间随意互相拖拽,我们可以怎么做?

kityminder提供了beforeExecCommand执行命令前的监控,而这种节点间可以互相拖拽进入则是因为执行了movetoparent这个命令,我们可以在movetoparent这个命令执行前给它阻断掉即可

js 复制代码
// 监听脑图执行命令前(可通过e.stopPropagation拦截命令执行)
this.minder.on('beforeExecCommand', (event) => {
    console.log('1-zp-event:', event, event.commandName);
    if (['movetoparent'].includes(event.commandName)) {
      event.stopPropagation();
    }
 });

八、清除多余的节点选中样式

我们在加载用例节点的前置条件、步骤和预期结果的子节点的时候,是通过AppendChildNode的方法去逐个插入的,插入完成以后通过selectById把选中节点设置会用例节点,但是出现了老的选中节点没有清除样式的情况,比如下面出现了两个选中的样式

记得我们在上文中提到过selectById源码中,第二个参数是可以设置节点为单选的,所以,我们只需要把第二个参数为true即可解决问题

js 复制代码
this.minder.selectById(nodeId, true);

九、子节点渲染提速

在开发的时候,发现当我们一个分组下有多个子用例和子分组的时候,如果子节点过多,脑图渲染出来子节点所需要消耗的时间需要30秒。如下代码,我们是利用AppendChildNode一个一个去插入子节点的,当子节点数量过多时,这种插入的性能就降低了

js 复制代码
children.forEach((item, index) => {
  this.minder.selectById(item.data.parentId);
  // 插入子节点
  this.minder.execCommand('AppendChildNode', {
    ...item.data,
  });
 });

通过调研、实践,我发现是不是可以把数据处理完成以后再渲染,不要处理一个数据就去动一次脑图,于是通过把数据处理完以后,直接importJson一次性进行渲染,速度马上提升到1秒

js 复制代码
// 插入子节点数据到大的json数据中,最后再importJson
this.minder.importJson(data);

于是,在处理相关数据的时候,我把子节点低于20条的就使用AppendChildNode的方式去插入子节点,当超过20条的时候,就采用importJson去渲染整个json,最后再选中其加载的节点即可,性能也提升了许多

十、脑图快捷键

我们在使用脑图在编写用例的时候,现在是通过上方的操作按钮去进行不同节点的不同操作的

我们可以利用键盘按钮的相关监控,去处理不同节点类型,使用不同的快捷键去快速处理脑图的行为

js 复制代码
handleShortcutKeyEvent: throttle(function (event) {
  switch (event.code) {
    case 'Enter':
      if (!this.groupOpeDisabled) {
        this.handleAddGroup();
      }
      break;
    case 'Tab':
      if (this.isGroupNode && this.hasModuleRight) {
        this.handleAddCase();
      }
      if (this.isCaseNode && this.hasModuleRight) {
        this.handleAddStep();
      }
      break;
    case 'Space':
      if (!this.editOrDeleDisabled) {
        this.handleEditNode();
      }
      break;
    case 'Backspace': // Backspace、Delete都触发删除
    case 'Delete':
      if (!this.editOrDeleDisabled) {
        this.handleDeleteNode();
      }
      break;
    case 'KeyS':
      if ((event.ctrlKey || event.metaKey)) {
        this.handleSaveCaseList();
      }
      break;
    default:
      break;
  }
}, 1500),

keydownListener(event) {
  // 编辑状态,直接不处理快捷键相关逻辑
  if (this.inputNodeVisible) {
    return;
  }
  // 立即阻止默认行为,以确保它在事件冒泡之前被处理
  if (event.code === 'Tab' || event.code === 'Space' || event.code === 'KeyS') {
    event.preventDefault();
  }
  this.handleShortcutKeyEvent(event);
},
    
js 复制代码
document.addEventListener('keydown', this.keydownListener);

当然,在页面销毁时,记得销毁

js 复制代码
document.removeEventListener('keydown', this.keydownListener);

在脑图进行快捷键进行快捷操作时,记得考虑好一个快捷键,在不同的节点下触发,会对应着什么行为,比如模块节点、分组节点、用例节点、前置条件节点、步骤节点和预期结果节点

十一、总结

本文介绍了在开发Xmind脑图时,使用kityminder的一些技巧和常见问题处理,希望在大家开发脑图相关需求的时候,能够快速上手并避免潜在的坑

相关推荐
赵大仁4 分钟前
Uniapp中使用`wxml-to-canvas`开发DOM生成图片功能
前端·javascript·微信小程序·uni-app
雯0609~5 分钟前
uni-app:实现普通选择器,时间选择器,日期选择器,多列选择器
前端·css·uni-app
一个处女座的程序猿O(∩_∩)O7 分钟前
前端如何判断多个请求完毕
前端·javascript
Libby博仙7 分钟前
asp.net core Web Api中的数据绑定
java·前端·asp.net
别发呆了吧14 分钟前
vue代理问题
前端·javascript·vue.js
Nejosi_念旧17 分钟前
编写可复用性的模块
前端·javascript·vue.js
开心工作室_kaic3 小时前
springboot548二手物品交易boot代码(论文+源码)_kaic
前端·数据库·vue.js·后端·html5
milo.qu9 小时前
二、CSS基础
前端·javascript·css
小周同学:9 小时前
elementui table 表格 分页多选,保持选中状态
前端·vue.js·elementui
小嘟嚷ovo10 小时前
vue中的h
前端·javascript·vue.js