提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
二、jsmind.js
1.项目引入并使用
2.基本使用
三、功能实现
一、基本的架构及交互
二、生成思维导图以及添加右键自定义菜单
三、自定义菜单的添加平级、添加子级、删除卡片的功能
四、给所有的卡片添加change事件
五、给所有的权重input添加失去焦点事件
六、总结
完整页面代码
在做的一个项目中,需要制作一个类似于Xmind的里面的一种思维导图的操作页面,里面是可编辑的节点,这里就不好使用echarts里面的树图,最后找到了这个jsmind.js的插件,就是用于生产页面版的思维树图之类的,官方文档篇幅属实有限,而且页面是根据需求来改动的,以此记录这个页面的实现。
一、最终页面效果展示
如图, 这个页面需要能动态添加自定义的节点,且每个节点的数据是可以下拉选择并且每一个都有对应的自定义设置的权重,我们先根据需求整理大体功能思路。
一、先使用jsmind插件来生成一个基本的模块。
二、根节点的模型组名是固定的,这里不应该让它能编辑,同时第一级的模型名节点分为两大类,我们可以在点击开始建模后初始化时生成一个固定的根节点以及两个不同类型的模型名一级节点的卡片。
三、通过自定义右键菜单来添加三个功能,分别是 添加同级、添加子级以及删除卡片。
四、动态获取一级子节点的模型名数据以及对应的二级子节点的标签分类数据以及三级子节点的标签名数据,其中每个数据中都是带有对应的权重的。
五、当每一个卡片节点切换时,对应的子节点的数据也需要动态切换。
六、当每一个卡片重新设置权重时,对应的卡片选项中的权重也要进行关联。
官方链接: jsMind - Developer & Open Source
通过 npm install jsmind --save
安装插件
在 vue 页面文件里面引入并使用( 只需在当前这个需要制作的页面文件中使用即可 ):
import 'jsmind/style/jsmind.css';
import jsMind from 'jsmind/js/jsmind.js';
require('jsmind/js/jsmind.draggable.js');
require('jsmind/js/jsmind.screenshot.js');
代码如下(示例):
基本的使用以及其他的一些功能可以参考下这篇博客,我当初也是从这里进行参考的。
vue实现思维导图_吕振辉的博客-CSDN博客_vue实现思维导图
由于目前的页面已基本完成,所以不可能每部分的代码都是十分清晰的,且由于当时以实现功能为首要目标,代码中存在大量的冗余,尽量在进行优化,主要细说一下每一步的思路以及解决方案和遇到的问题。每个模块的代码由于功能的上下连接,单独模块放出来意义不大,且很难读懂,有些地方附上一些关键代码,其他整个文件代码到时候放在最后。
由设计图可知,该页面需先输入根节点的模型组名然后点击开始建模按钮后,生成基本的架构思维图。这里主要是要注意一点,点击开始建模按钮之前,先判断用户是否有输入,同时,开始建模按钮点击之后,就应该禁用掉,点击了重新建模按钮之后,再放开开始建模按钮。
由于第二子级的标签分类是固定的,所以先按照分类写成固定数据,但是第三子级的标签名得通过不同的分类来进行瓜分,所以这个数据方面还是有点复杂的,参考性不高,更多只是个人记录。数据分类结构如下:
resultData: {
personList: [],
relationList: [],
labelTypeList: {
pointType: [
{ label: '自身属性标签', value: 'property', weight: 1 },
{ label: '自身行为标签', value: 'action', weight: 1 }
],
relationType: [
{ label: '实质关系标签', value: 'substance', weight: 1 },
{ label: '疑似关系标签', value: 'suspected', weight: 1 },
{ label: '增强关系标签', value: 'strengthen', weight: 1 }
]
},
labelNameList: {
pointLabel: [], // 当前选中的模型 所有的个人标签
relationLabel: [], // 当前选中的模型 所有的关系标签
// 五大类的标签名列表数据
propertyLabelList: [],
actionLabelList: [],
substanceLabelList: [],
suspectedLabelList: [],
strengthenLabelList: []
}
}
上面的 initModelChart() 方法就是初始化生成图形,同时,我们需要给每一个卡片添加点击事件,并且卡片里面的是html元素,这里我们需要在一个js文件里面进行原生html的生成,方便添加。
用于生成卡片节点元素以及其他元素的js文件, createDom.js :
/* eslint-disable no-useless-escape */
class CreateDom {
// 获取根卡片 模型组名
static getModelRootDom(modelInfo) {
return `
模型组名
${modelInfo.modelName}
`;
}
// 获取子级卡片 模型名/标签
static getModelCardDom(modelInfo) {
let selectList = ``;
for (let i = 0; i < modelInfo.modelSelectList.length; i++) {
selectList += ``;
}
return `
${modelInfo.modelTitle}
权重
`;
}
// 生成自定义右键菜单
static getContextMenu() {
return `
插入平级
插入子级
删除卡片
`;
}
// 获取固定的初始化数据
static getInitData(params) {
return [
{
id: 'point', // 必选 ID, 所有节点的 id 不应该重复,否则 重复id的结节将被忽略
topic: CreateDom.getModelCardDom({ bgc: '#0FA984', modelTitle: '模型名', modelSelectList: params.personList, modelType: 'point', selectOption: params.personList[0].label, selectModelId: params.personList[0].value, level: 1, modelWeight: 1 }), // 必填 节点上显示的内容
direction: 'right', // 可选 节点的方向 此数据仅在第一层节点上有效,目前仅支持 left 和 right 两种,默认为 right
expanded: true, // [可选] 该节点是否是展开状态,默认为 true
level: 1,
type: 'point',
title: '模型名',
children: []
},
{
id: 'relation', // 必选 ID, 所有节点的 id 不应该重复,否则 重复id的结节将被忽略
topic: CreateDom.getModelCardDom({ bgc: '#0FA984', modelTitle: '模型名', modelSelectList: params.relationList, modelType: 'relation', selectOption: params.relationList[0].label, selectModelId: params.relationList[0].value, level: 1, modelWeight: 1 }), // 必填 节点上显示的内容
direction: 'right', // 可选 节点的方向 此数据仅在第一层节点上有效,目前仅支持 left 和 right 两种,默认为 right
expanded: true, // [可选] 该节点是否是展开状态,默认为 true
level: 1,
type: 'relation',
title: '模型名',
children: []
}
];
}
}
export default CreateDom;
在vue文件里面引入
import CreateDom from '@/tools/CreateDom.js';
其中 initModelChart 方法的代码:
data() {
return {
mind: {
// 元数据 定义思维导图的名称、 作者、版本等信息
meta: {
name: '建模导图',
author: 'ck',
version: '0.2'
},
// 数据格式声明
format: 'node_tree',
// 数据内容
data: {
id: 'root',
topic: '',
direction: 'right',
expanded: true,
children: []
}
},
options: {
container: 'create-model-chart', // [必选] 容器的ID
editable: true, // [可选] 是否启用编辑
theme: '', // [可选] 主题
support_html: true,
mode: 'full', // 显示模式
view: {
engine: 'canvas', // 思维导图各节点之间线条的绘制引擎
hmargin: 120, // 思维导图距容器外框的最小水平距离
vmargin: 50, // 思维导图距容器外框的最小垂直距离
line_width: 4, // 思维导图线条的粗细
line_color: '#FFCC73', // 思维导图线条的颜色
draggable: true, // 当容器不能完全容纳思维导图时,是否允许拖动画布代替鼠标滚动
hide_scrollbars_when_draggable: true // 当设置 draggable = true 时,是否隐藏滚动条
},
layout: {
hspace: 100, // 节点之间的水平间距
vspace: 20, // 节点之间的垂直间距
pspace: 20 // 节点与连接线之间的水平间距(用于容纳节点收缩/展开控制器)
},
shortcut: {
enable: false // 是否启用快捷键 默认为true
},
menuOpts: {
showMenu: true,
injectionList: [
{ target: 'addBrother', text: '添加同级卡片', callback: function (node) { console.log(node); } },
{ target: 'delete', text: '删除卡片', callback: function (node) { console.log(node); } }
]
},
isShow: true
}
}
}
initModelChart() {
this.mind.data.topic = CreateDom.getModelRootDom(this.rootModel);
this.mind.data.children = CreateDom.getInitData(this.resultData);
this.$nextTick(() => {
this.jm = jsMind.show(this.options, this.mind);
this.chartLoading = false;
// let testSelect = this.jm.get_node('easy');
// console.log(testSelect, 'tes');
const modelChart = document.getElementById('create-model-chart');
modelChart.addEventListener('mousedown', e => {
e.stopPropagation();
this.showMenu = false;
// console.log(e, '99666');
// this.showTheMenu(e);
let selectCardId = '';
if (Array.isArray(e.path)) {
e.path.map(item => {
if (item.localName == 'jmnode') {
// console.log(item.attributes[0].nodeValue, '3030');
selectCardId = item.attributes[0].nodeValue;
}
});
// console.log(selectCardId, 'sed');
if (selectCardId == 'root') {
this.showMenu = false;
return this.$message.warning('根节点无法编辑');
} else if (!selectCardId) {
this.showMenu = false;
return false;
}
this.theSelectNode = this.jm.get_node(selectCardId);
// console.log(this.theSelectNode, '2200');
// console.log(selectCardId, '0022');
this.clickSelectCard(selectCardId);
this.findClickCardIndex(this.theSelectNode.data.type, this.theSelectNode.data.level, this.theSelectNode.id);
}
});
this.addSelectChangeFunc();
this.addInputBlurFunc();
});
}
其中,给每个节点添加点击事件,由于有后续添加的节点,所以最好绑定在父节点上面,通过事件捕获,找到是卡片的节点,给这个卡片添加对应的操作。相关的操作方法代码放在整体里面,这里不做详细展示,主要是看如何通过原生js找到节点位置并对该选中的节点进行添加自定义的鼠标右键菜单, 这里即是 clickSelectCard 这个方法,代码如下:
clickSelectCard(nodeId) {
this.editor = this.jm.view.e_editor;
// jsmind 添加自定义菜单事件
this.jm.view.add_event(this.editor, 'contextmenu', (e) => {
const selectedNode = this.jm.get_node(nodeId);
// && selectedNode.data.type
if (selectedNode) {
e.preventDefault();
const el = document.querySelector('.context-menu .el-menu-item');
const width = parseFloat(window.getComputedStyle(el).width);
const height = parseFloat(window.getComputedStyle(el).height) * 3 + 12;
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
// 极限位置 避免越界
if (e.clientY + height > windowHeight) {
// console.log('23');
this.menuStyle.left = e.clientX + 'px';
this.menuStyle.top = 'unset';
this.menuStyle.bottom = 0;
} else if (e.clientX + width > windowWidth) {
// console.log('24');
this.menuStyle.top = e.clientY + 'px';
this.menuStyle.left = 'unset';
this.menuStyle.right = 0;
} else {
// console.log('25');
this.menuStyle.left = e.clientX - 210 + 'px';
this.menuStyle.top = e.clientY - 150 + 'px';
this.menuStyle.bottom = 'unset';
}
this.showMenu = true;
} else {
this.showMenu = false;
}
});
}
这里的菜单位置可能不太对,需要自己来进行调整。
这里主要通过找到是哪一个节点右键了,根据这个节点来做相应的功能,其中菜单的元素代码如下:
插入平级
插入子级
删除卡片
.context-menu {
position: absolute;
width: 8.3333rem;
z-index: 32;
}
由于添加同级和添加子级都是用的同一个api,官方里面的往元素后面添加节点的方法其实并不是添加子级,这里添加子级只需要把当前选中的卡片节点的id设置为父级id即可。
这里先写一个方法返回添加节点时需要的对象数据,如下:
// 添加卡片的方法 获得相关的数据对象
whileCardAddFunc(type, node) {
var funcObj = {};
console.log(node, 'node');
// type 进行 同级 bother 或者 子级 children 的分类 this.resultData.personList[0].label
if (type == 'bother') {
if (node.data.level == '1') {
node.data.type == 'point' ? this.pointLevel1Index++ : this.relationLevel1Index++;
funcObj.parentId = node.parent.id;
funcObj.id = new Date().getTime() + parseFloat(Math.random() * 1000);
funcObj.topic = CreateDom.getModelCardDom({ bgc: '#0FA984', modelTitle: '模型名', modelSelectList: node.data.type == 'point' ? this.resultData.personList : this.resultData.relationList, modelType: node.data.type, selectOption: node.data.type == 'point' ? this.resultData.personList[0].label : this.resultData.relationList[0].label, selectModelId: node.data.type == 'point' ? this.resultData.personList[0].value : this.resultData.relationList[0].value, level: 1, modelWeight: 1 });
funcObj.data = {
direction: 1,
expanded: true,
level: 1,
type: node.data.type,
children: []
};
// 判断是 个人模型 还是 关系 模型 往 已选则的 list 里面进行 push 一个
this.addLabelValueWeight(node.data.type, 1, funcObj.id);
} else if (node.data.level == '2') {
node.data.type == 'point' ? this.pointLevel2Index++ : this.relationLevel2Index++;
funcObj.parentId = node.parent.id;
funcObj.id = new Date().getTime() + parseFloat(Math.random() * 1000);
funcObj.topic = CreateDom.getModelCardDom({ bgc: '#5B9BD5', modelTitle: '标签分类', modelSelectList: node.data.type == 'point' ? this.resultData.labelTypeList.pointType : this.resultData.labelTypeList.relationType, modelType: node.data.type, selectOption: node.data.type == 'point' ? this.resultData.labelTypeList.pointType[0].label : this.resultData.labelTypeList.relationType[0].label, selectModelId: node.data.type == 'point' ? this.resultData.labelTypeList.pointType[0].value : this.resultData.labelTypeList.relationType[0].value, level: 2, modelWeight: this.findTheCardWeight(node.data.type, 2) });
funcObj.data = {
direction: 'right',
expanded: true,
level: 2,
type: node.data.type,
children: []
};
this.addLabelValueWeight(node.data.type, 2, funcObj.id);
} else if (node.data.level == '3') {
let optionList = this.getLabelNameListBySort(node.data.type, node.data.type == 'point' ? this.selectPonitLabelSort : this.selectRelationLabelSort);
node.data.type == 'point' ? this.pointLevel3Index++ : this.relationLevel3Index++;
funcObj.parentId = node.parent.id;
funcObj.id = new Date().getTime() + parseFloat(Math.random() * 1000);
funcObj.topic = CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: optionList, modelType: node.data.type, selectOption: optionList.length > 0 ? optionList[0].label : '', selectModelId: optionList.length > 0 ? optionList[0].value : '', level: 3, modelWeight: this.findTheCardWeight(node.data.type, 3, optionList.length > 0 ? optionList[0].label : '') });
funcObj.data = {
level: 3,
type: node.data.type
};
this.addLabelValueWeight(node.data.type, 3, funcObj.id);
}
} else if (type == 'children') {
if (node.data.level == '1') {
console.log(this.findTheCardWeight(node.data.type, 2), 'classWei');
node.data.type == 'point' ? this.pointLevel2Index++ : this.relationLevel2Index++;
funcObj.afterId = node.id;
funcObj.id = new Date().getTime() + parseFloat(Math.random() * 1000);
funcObj.topic = CreateDom.getModelCardDom({ bgc: '#5B9BD5', modelTitle: '标签分类', modelSelectList: node.data.type == 'point' ? this.resultData.labelTypeList.pointType : this.resultData.labelTypeList.relationType, modelType: node.data.type, selectOption: node.data.type == 'point' ? this.resultData.labelTypeList.pointType[0].label : this.resultData.labelTypeList.relationType[0].label, selectModelId: node.data.type == 'point' ? this.resultData.labelTypeList.pointType[0].value : this.resultData.labelTypeList.relationType[0].value, level: 2, modelWeight: this.findTheCardWeight(node.data.type, 2) });
funcObj.data = {
direction: 'right',
expanded: true,
level: 2,
type: node.data.type,
children: []
};
this.addLabelValueWeight(node.data.type, 2, funcObj.id);
} else if (node.data.level == '2') {
let optionsList = this.getLabelNameListBySort(node.data.type, node.data.type == 'point' ? this.selectPonitLabelSort : this.selectRelationLabelSort);
node.data.type == 'point' ? this.pointLevel3Index++ : this.relationLevel3Index++;
funcObj.afterId = node.id;
funcObj.id = new Date().getTime() + parseFloat(Math.random() * 1000);
funcObj.topic = CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: optionsList, modelType: node.data.type, selectOption: optionsList.length > 0 ? optionsList[0].label : '', selectModelId: optionsList.length > 0 ? optionsList[0].value : '', level: 3, modelWeight: this.findTheCardWeight(node.data.type, 3, optionsList.length > 0 ? optionsList[0].label : '') });
funcObj.data = {
level: 3,
type: node.data.type
};
this.addLabelValueWeight(node.data.type, 3, funcObj.id);
} else if (node.data.level == '3') {
funcObj.error = true;
funcObj.msg = '无法再进行下一级的分类添加';
}
}
let copyObj = JSON.parse(JSON.stringify(funcObj));
// console.log(copyObj, '30369');
return copyObj;
}
由于最后第三级的标签名卡片是不能再往后面添加子级了,所以设置一个error为true,并返回一个msg,这里需要注意的是,我们需要设置 options 里面的 editable 属性为 true,开启编辑,否则是无法进行节点卡片的添加的,这样有一个不好的地方就是双击卡片会变成html元素的input输入框,在使用时需要注意。
添加同级卡片的方法:
// 添加同级卡片
addBrother() {
// parent_node, node_id, topic, data
let cardObj = this.whileCardAddFunc('bother', this.theSelectNode);
// console.log(cardObj, 'cla');
this.jm.add_node(cardObj.parentId, cardObj.id, cardObj.topic, cardObj.data);
this.showMenu = false;
this.addSelectChangeFunc();
this.addInputBlurFunc();
}
添加子级卡片的方法:
// 添加子卡片
addChild() {
// node_after, node_id, topic, data
// 判断是哪个层级 1 模型名 2 标签分类 3 标签名
let cardObj = this.whileCardAddFunc('children', this.theSelectNode);
// console.log(cardObj, '6688');
if (cardObj.error) {
this.showMenu = false;
return this.$message.warning(cardObj.msg);
}
this.jm.add_node(cardObj.afterId, cardObj.id, cardObj.topic, cardObj.data);
this.showMenu = false;
this.addSelectChangeFunc();
this.addInputBlurFunc();
}
这里最后的两个方法,是卡片里面的select下拉框的切换方法和权重input输入框的blur方法,在生成新的卡片时,需要给所有的卡片重新绑定事件,否则新生成的卡片是不带下拉框的change和输入框的blur事件的,这里使用 onchange和 onblur 方法来进行监听, 重新设置时会覆盖掉原来的方法,不推荐使用 addEventListener,因为不会覆盖,会重复执行。
删除卡片的方法:
let that = this;
// let deleteNode = JSON.parse(JSON.stringify(this.theSelectNode));
let deleteNode = StringTools.cloneDeepObj(that.theSelectNode);
this.$confirm(
'删除此节点卡片(包含所有子级卡片), 是否继续?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
// 删除卡片
if (deleteNode.data.type == 'point') {
if (deleteNode.data.level == 1) {
if (that.haveSelectPointModelAndWeight.length <= 1) {
return that.$message.warning('至少保留一个个人模型名卡片');
}
that.haveSelectPointModelAndWeight.map((item, index) => {
if (item.boxId == deleteNode.id) {
that.haveSelectPointModelAndWeight.splice(index, 1);
}
});
} else if (deleteNode.data.level == 2) {
that.haveSelectPointModelAndWeight[that.haveSelectPointIndex].children.map((item, index) => {
if (item.boxId == deleteNode.id) {
that.haveSelectPointModelAndWeight[that.haveSelectPointIndex].children.splice(index, 1);
}
});
} else if (deleteNode.data.level == 3) {
that.haveSelectPointModelAndWeight[that.haveSelectPointIndex].children[that.haveSelectPointLabelClassIndex].children.map((item, index) => {
if (item.boxId == deleteNode.id) {
that.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[that.haveSelectPointLabelClassIndex].children.splice(index, 1);
}
});
}
} else {
if (deleteNode.data.level == 1) {
if (that.haveSelectRelationModelAndWeight.length <= 1) {
return that.$message.warning('至少保留一个关系模型名卡片');
}
that.haveSelectRelationModelAndWeight.map((item, index) => {
if (item.boxId == deleteNode.id) {
that.haveSelectRelationModelAndWeight.splice(index, 1);
}
});
} else if (deleteNode.data.level == 2) {
that.haveSelectRelationModelAndWeight[that.haveSelectRelationIndex].children.map((item, index) => {
if (item.boxId == deleteNode.id) {
that.haveSelectRelationModelAndWeight[that.haveSelectRelationIndex].children.splice(index, 1);
}
});
} else if (deleteNode.data.level == 3) {
that.haveSelectRelationModelAndWeight[that.haveSelectRelationIndex].children[that.haveSelectPointLabelClassIndex].children.map((item, index) => {
if (item.boxId == deleteNode.id) {
that.haveSelectRelationModelAndWeight[that.haveSelectRelationIndex].children[that.haveSelectRelationLabelNameIndex].children.splice(index, 1);
}
});
}
}
setTimeout(() => {
that.jm.remove_node(that.theSelectNode.id);
}, 50);
that.showMenu = false;
}).catch(() => {});
主要是调用 remove_node 这个api进行删除,上面一大堆代码只是用来同步移除已保存的数据,主要思路就是找到卡片数据在对应的数据树里的位置,进行删除。
这一步是整个功能的难点,一步步进行剖析,首先我们需要找到选中切换的卡片,进行类型判断,然后再进行级别判断,切换了第一子级的话,那么第三子级的数据得同步更改,切换了第二子级,第三子级的数据也需要进行更改,切换了第三子级,那么只需要更改保存的数据即可。这里的难点在于我们的select里面的options是原生html生成的,更新的话,需要用到 update_node 这个api,这时需要第三子级的卡片添加一些自定义的属性来进行对第一子级和第二子级的数据绑定,要清晰的知道每一个卡片是属于哪一个位置,方便后面保存时拿到整个树形的数据。
事件代码如下:
// 给所有的select下拉框添加change事件
addSelectChangeFunc() {
let that = this;
let allSelectDom = document.querySelectorAll('.select-model-list');
// console.log(allSelectDom, 'cmcnc');
for (let i = 0; i < allSelectDom.length; i++) {
// 先清除change事件 selectFunction(allSelectDom[i])
// allSelectDom[i].removeEventListener('change', selectFunction);
// 使用 onchange 生成的新卡片也需要添加 这个change监听事件 用 onchange 进行覆盖 不会重复执行
allSelectDom[i].onchange = async (e) => {
// console.log(allSelectDom[i], 'lbn');
console.log(e, 'echa');
let selectLevel = allSelectDom[i].dataset.title == '模型名' ? 1 : (allSelectDom[i].dataset.title == '标签分类' ? 2 : 3);
// 从节点上面查到到当前选中的option 进行 权重 input 的 自定义属性的更新
e.path.map((item, index) => {
if (item.className == 'model-edit-card') {
e.path[index].children[1].children[1].dataset.label = allSelectDom[i].options[allSelectDom[i].selectedIndex].text;
e.path[index].children[1].children[1].dataset.model = allSelectDom[i].value;
e.path[index].children[1].children[1].value = that.findTheCardWeight(allSelectDom[i].dataset.type, selectLevel, allSelectDom[i].options[allSelectDom[i].selectedIndex].text);
}
});
// 获取选中的 label 值
// console.log(allSelectDom[i].options[allSelectDom[i].selectedIndex].text, '9966');
// 判断切换的是哪种下拉框
if (allSelectDom[i].dataset.title == '模型名') {
if (allSelectDom[i].dataset.type == 'point') {
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].label = allSelectDom[i].options[allSelectDom[i].selectedIndex].text;
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].value = allSelectDom[i].value;
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].weight = 1;
// 选则的是人员模型
let personLabel = await that.$http.getAllLabelName({ modelId: allSelectDom[i].value, modelType: 'point' });
if (personLabel.data.flag == 1) {
that.resultData.labelNameList.propertyLabelList = [];
that.resultData.labelNameList.actionLabelList = [];
that.resultData.labelNameList.pointLabel = personLabel.data.data.labelFields;
that.resultData.labelNameList.pointLabel.map(item => {
if (item.type == 'property') {
let labelObj = {
label: item.fieldnamecn,
value: item.id
};
that.resultData.labelNameList.propertyLabelList.push(labelObj);
} else if (item.type == 'action') {
let actionObj = {
label: item.fieldnamecn,
value: item.id
};
that.resultData.labelNameList.actionLabelList.push(actionObj);
}
});
if (that.theSelectNode.children.length > 0) {
let pointOptionsList = that.getLabelNameListBySort('point', that.selectPonitLabelSort);
console.log(pointOptionsList, 'pointOp');
that.theSelectNode.children.map(item => {
if (item.children.length > 0) {
item.children.map(ele => {
that.jm.update_node(ele.id, CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: pointOptionsList, modelType: 'point', level: 3, selectOption: pointOptionsList.length > 0 ? pointOptionsList[0].label : '', selectModelId: pointOptionsList.length > 0 ? pointOptionsList[0].value : '', modelWeight: this.findTheCardWeight('poiny', 3, pointOptionsList.length > 0 ? pointOptionsList[0].label : '') }));
});
}
});
}
// console.log(that.theSelectNode, '000333');
// console.log(that.resultData.labelNameList.propertyLabelList, 'propl');
// console.log(that.resultData.labelNameList.actionLabelList, 'actl');
}
} else {
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].label = allSelectDom[i].options[allSelectDom[i].selectedIndex].text;
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].value = allSelectDom[i].value;
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].weight = 1;
// 选则的是关系模型
let initRelationLabel = await that.$http.getAllLabelName({ modelId: allSelectDom[i].value, modelType: 'relation' });
if (initRelationLabel.data.flag == 1) {
that.resultData.labelNameList.substanceLabelList = [];
that.resultData.labelNameList.suspectedLabelList = [];
that.resultData.labelNameList.strengthenLabelList = [];
that.resultData.labelNameList.relationLabel = initRelationLabel.data.data.labelFields;
that.resultData.labelNameList.relationLabel.map(ele => {
if (ele.type == 'substance') {
let substanceObj = {
label: ele.fieldnamecn,
value: ele.id
};
that.resultData.labelNameList.substanceLabelList.push(substanceObj);
} else if (ele.type == 'suspected') {
let suspectedObj = {
label: ele.fieldnamecn,
value: ele.id
};
that.resultData.labelNameList.suspectedLabelList.push(suspectedObj);
} else if (ele.type == 'strengthen') {
let strengthenObj = {
label: ele.fieldnamecn,
value: ele.id
};
that.resultData.labelNameList.strengthenLabelList.push(strengthenObj);
}
});
if (that.theSelectNode.children.length > 0) {
let relationOptionsList = that.getLabelNameListBySort('relation', that.selectRelationLabelSort);
console.log(relationOptionsList, 'relaOp');
that.theSelectNode.children.map(item => {
if (item.children.length > 0) {
item.children.map(ele => {
that.jm.update_node(ele.id, CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: relationOptionsList, modelType: 'relation', level: 3, selectOption: relationOptionsList.length > 0 ? relationOptionsList[0].label : '', selectModelId: relationOptionsList.length > 0 ? relationOptionsList[0].value : '', modelWeight: this.findTheCardWeight('relation', 3, relationOptionsList.length > 0 ? relationOptionsList[0].label : '') }));
});
}
});
}
}
}
that.addSelectChangeFunc();
that.addInputBlurFunc();
} else if (allSelectDom[i].dataset.title == '标签分类') {
if (allSelectDom[i].dataset.type == 'point') {
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[this.haveSelectPointLabelClassIndex].label = allSelectDom[i].options[allSelectDom[i].selectedIndex].text;
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[this.haveSelectPointLabelClassIndex].value = allSelectDom[i].value;
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[this.haveSelectPointLabelClassIndex].weight = 1;
that.selectPonitLabelSort = allSelectDom[i].value;
if (that.theSelectNode.children.length > 0) {
if (allSelectDom[i].value == 'property') {
let propertyClassOptionsList = that.getLabelNameListBySort('point', 'property');
console.log(propertyClassOptionsList, 'properOpt');
that.theSelectNode.children.map(item => {
that.jm.update_node(item.id, CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: propertyClassOptionsList, modelType: 'point', level: 3, selectOption: propertyClassOptionsList.length > 0 ? propertyClassOptionsList[0].label : '', selectModelId: propertyClassOptionsList.length > 0 ? propertyClassOptionsList[0].value : '', modelWeight: this.findTheCardWeight('poiny', 3, propertyClassOptionsList.length > 0 ? propertyClassOptionsList[0].label : '') }));
});
} else if (allSelectDom[i].value == 'action') {
let actionClassOptionsList = that.getLabelNameListBySort('point', 'action');
console.log(actionClassOptionsList, 'actionOpi');
that.theSelectNode.children.map(item => {
that.jm.update_node(item.id, CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: actionClassOptionsList, modelType: 'point', level: 3, selectOption: actionClassOptionsList.length > 0 ? actionClassOptionsList[0].label : '', selectModelId: actionClassOptionsList.length > 0 ? actionClassOptionsList[0].value : '', modelWeight: this.findTheCardWeight('poiny', 3, actionClassOptionsList.length > 0 ? actionClassOptionsList[0].label : '') }));
});
}
}
} else if (allSelectDom[i].dataset.type == 'relation') {
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children[this.haveSelectRelationLabelClassIndex].label = allSelectDom[i].options[allSelectDom[i].selectedIndex].text;
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children[this.haveSelectRelationLabelClassIndex].value = allSelectDom[i].value;
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children[this.haveSelectRelationLabelClassIndex].weight = 1;
that.selectRelationLabelSort = allSelectDom[i].value;
let relationClassOptionsList = that.getLabelNameListBySort('relation', that.selectRelationLabelSort);
console.log(relationClassOptionsList, 'relaClssOpi');
console.log(that.theSelectNode, '30251');
if (that.theSelectNode.children.length > 0) {
if (allSelectDom[i].value == 'substance') {
that.theSelectNode.children.map(item => {
that.jm.update_node(item.id, CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: relationClassOptionsList, modelType: item.data.type, level: 3, selectOption: relationClassOptionsList.length > 0 ? relationClassOptionsList[0].label : '', selectModelId: relationClassOptionsList.length > 0 ? relationClassOptionsList[0].value : '', modelWeight: this.findTheCardWeight('relation', 3, relationClassOptionsList.length > 0 ? relationClassOptionsList[0].label : '') }));
});
} else if (allSelectDom[i].value == 'suspected') {
that.theSelectNode.children.map(item => {
that.jm.update_node(item.id, CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: relationClassOptionsList, modelType: item.data.type, level: 3, selectOption: relationClassOptionsList.length > 0 ? relationClassOptionsList[0].label : '', selectModelId: relationClassOptionsList.length > 0 ? relationClassOptionsList[0].value : '', modelWeight: this.findTheCardWeight('relation', 3, relationClassOptionsList.length > 0 ? relationClassOptionsList[0].label : '') }));
});
} else if (allSelectDom[i].value == 'strengthen') {
that.theSelectNode.children.map(item => {
that.jm.update_node(item.id, CreateDom.getModelCardDom({ bgc: '#E3950E', modelTitle: '标签名', modelSelectList: relationClassOptionsList, modelType: item.data.type, level: 3, selectOption: relationClassOptionsList.length > 0 ? relationClassOptionsList[0].label : '', selectModelId: relationClassOptionsList.length > 0 ? relationClassOptionsList[0].value : '', modelWeight: this.findTheCardWeight('relation', 3, relationClassOptionsList.length > 0 ? relationClassOptionsList[0].label : '') }));
});
}
}
}
that.addSelectChangeFunc();
that.addInputBlurFunc();
} else if (allSelectDom[i].dataset.title == '标签名') {
if (allSelectDom[i].dataset.type == 'point') {
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[this.haveSelectPointLabelClassIndex].children[this.haveSelectPointLabelNameIndex].label = allSelectDom[i].options[allSelectDom[i].selectedIndex].text;
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[this.haveSelectPointLabelClassIndex].children[this.haveSelectPointLabelNameIndex].value = allSelectDom[i].value;
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[this.haveSelectPointLabelClassIndex].children[this.haveSelectPointLabelNameIndex].weight = 1;
} else {
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children[this.haveSelectRelationLabelClassIndex].children[this.haveSelectRelationLabelNameIndex].label = allSelectDom[i].options[allSelectDom[i].selectedIndex].text;
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children[this.haveSelectRelationLabelClassIndex].children[this.haveSelectRelationLabelNameIndex].value = allSelectDom[i].value;
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children[this.haveSelectRelationLabelClassIndex].children[this.haveSelectRelationLabelNameIndex].weight = 1;
}
}
};
}
}
整个事件较为复制,考虑的因素较多,同时也要添加一些自定义的属性来方便区分。
这里还有一个就是每一个可选择的下拉框都对应的一个权重设置,通过失去焦点事件,来找到对应的位置更改数据,这里需要注意一下input输入框只能输入带小数点的数字,这里通过
οnkeyup="!/^(\d+\.?)?\d{0,1}$/.test(this.value)?(this.value=this.value.substring(0, this.value.length-1)): ''" 实现,
事件代码如下:
// 给所有的 权重设置框 设置 失去焦点的监听事件
addInputBlurFunc() {
let that = this;
let allInput = document.querySelectorAll('.model-weight-input');
for (let i = 0; i < allInput.length; i++) {
// 使用 onblur 生成的新卡片也需要添加 这个blur监听事件 用 onblur 进行覆盖 不会重复执行
allInput[i].onblur = (e) => {
console.log(allInput[i].value, 'inputVa');
console.log(allInput[i].dataset, 'inputDs');
if (allInput[i].dataset.type == 'point') {
if (allInput[i].dataset.level == 1) {
this.haveSelectPointModelAndWeight.map(item => {
if (item.label == allInput[i].dataset.label) {
item.weight = allInput[i].value;
}
});
console.log(this.haveSelectPointModelAndWeight);
} else if (allInput[i].dataset.level == 2) {
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children.map(item => {
if (item.label == allInput[i].dataset.label) {
item.weight = allInput[i].value;
}
});
} else if (allInput[i].dataset.level == 3) {
this.haveSelectPointModelAndWeight[this.haveSelectPointIndex].children[this.haveSelectPointLabelClassIndex].children.map(item => {
if (item.label == allInput[i].dataset.label) {
item.weight = allInput[i].value;
}
});
}
} else if (allInput[i].dataset.type == 'relation') {
if (allInput[i].dataset.level == 1) {
this.haveSelectRelationModelAndWeight.map(item => {
if (item.label == allInput[i].dataset.label) {
item.weight = allInput[i].value;
}
});
console.log(this.haveSelectRelationModelAndWeight);
} else if (allInput[i].dataset.level == 2) {
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children.map(item => {
if (item.label == allInput[i].dataset.label) {
item.weight = allInput[i].value;
}
});
} else if (allInput[i].dataset.level == 3) {
this.haveSelectRelationModelAndWeight[this.haveSelectRelationIndex].children[this.haveSelectRelationLabelClassIndex].children.map(item => {
if (item.label == allInput[i].dataset.label) {
item.weight = allInput[i].value;
}
});
console.log(this.haveSelectRelationModelAndWeight);
}
}
};
}
}
此页面算是又重新使用原生js来写功能了,好多东西遗忘了需要一点点百度,而且页面虽然看起来简单,但是里面的关联逻辑还是相对来说比较复杂的,主要是数据这一块比较麻烦,核心逻辑就是所有的树形数据需要一一对应,不管是切换还是重新设置权重,都需要先找到对应的卡片再进行修改,再就是jsmind插件的相关介绍属实太少,便以此为记录,对自己也是一个提升了。
主页面vue文件
保存模型
插入平级 插入子级 删除卡片
StringTools.js 里面的 cloneDeepObj 方法
// 对象的深拷贝 static cloneDeepObj(obj) { // 浅拷贝子节点 let handleChild = (child) => { if (typeof child === 'object') { if (Array.isArray(child)) { // 数组 return [...child]; } else if (child) { // 对象 return { ...child }; } else { // null return child; } } else { // 值类型 return child; } }; let arr = []; let target = { result: obj }; let current = target; let temp = null; if (typeof current === 'object') { if (Array.isArray(current)) { // 数组 current.forEach((item, index) => { let child = handleChild(item); current[index] = child; arr.push(child); }); } else if (current) { // 对象 let objKeys = Object.keys(current); objKeys.forEach(key => { let child = handleChild(current[key]); current[key] = child; arr.push(child); }); } else { // null temp = current; } } current = arr.shift(); return target.result; }
BaseForm 表单组件
查询 取消 规律分析 通话分析 短信分析 重置 新增 开始建模 重新建模 刷新 保存 导入 导出 导出模板 删除 确认提交 提交 提交 生成报告 查看报告 回复 返回 登录
CreateDom.js 在前面已经放了,这里就不再放了。