文章目录
-
- 一. 调研
- 1.常见树状图 | 思维导图
- 2.对比结果
- 二. 文件目录
- 1. public\jsmind.css
- 2. public\jsmind.js
- 3. public\mind.js
- 4. src\components\minder\DataMind.vue
- 5. src\components\minder\Minder.vue
- 三. 功能点实现
- 1.介绍
- 2.在 index.html 中引入 jsmind
- 3.页面中引入 Minder 组件
- 四、源码
- 1. Minder.vue
- 2. DataMinder.vue
- 3. jsmind.css
- 4. jsmind.js
- 5.mind.js
- 6. test.vue
- 7. 运行效果
一. 调研
1.常见树状图 | 思维导图
-
echarts
https://echarts.apache.org/examples/zh/editor.html?c=tree-polyline -
gojs
https://gojs.net/latest/samples/decisionTree.html
https://balkan.app/OrgChartJS/Demos/RoyalFamilyTree
https://codepen.io/tutsplus/pen/MWedpoj -
有复选框的treeNode
https://devexpress.github.io/dotnet-eud/interface-elements-for-web/articles/tree-view/tree-view-nodes-checking.html
2.对比结果
拟采用的方法:
改造 jsmind 实现自定义的思维导图
Jsmind 的功能及使用见官方文档:
https://github.com/hizzgdev/jsmind/blob/master/docs/zh/index.md
二. 文件目录
1. public\jsmind.css
jsmind 插件自带的 css 文件,思维导图的样式文件。
2. public\jsmind.js
jsmind 插件自带的 js 文件,思维导图的逻辑实现。
3. public\mind.js
思维导图的数据和触发方法,数据挂载在 window 对象下,用于和 vue 框架中的内容交互。
4. src\components\minder\DataMind.vue
思维导图的业务调用组件,包括思维导图、标题栏和左上 tip 文字内容。
5. src\components\minder\Minder.vue
思维导图的核心组件,分为 5 种类型:defalut,check,label,label2,input,通过 type 传值配置对应样式。
!!!当然这只是我根据项目需求改造的几种样式,大家也可以自行阅读下面minder.vue
、jsmind.css
以及jsmind.js
进行个性化改造。
三. 功能点实现
1.介绍
Jsmind的功能及使用见官方文档:
https://github.com/hizzgdev/jsmind/blob/master/docs/zh/index.md
本demo采用 vue3+typescript+vite 实现,核心功能在 src\components\minder\Minder.vue 文件中实现。
2.在 index.html 中引入 jsmind
如下图所示:
<link rel="stylesheet" href="/jsmind.css" />
<script type="text/javascript" src="/jsmind.js"></script>
3.页面中引入 Minder 组件
如下所示:
4.传入的数据结构,如下所示:
(window as any).mindData = {
id: "root",
topic: "根节点名字",
number: [88],
children: [
{
id: "easy",
topic: "规章制度",
children: [
{
id: "easy1", topic: "请休假制度" },
{
id: "easy2", topic: "考勤制度" },
],
},
{
id: "open",
topic: "岗位职责",
children: [
{
id: "open1",
topic: "人事部职责",
children: [
{
id: "open1-1", topic: "行政管理" },
{
id: "open1-2", topic: "人事管理" },
],
},
{
id: "open2",
topic: "信息部职责",
children: [
{
id: "open2-1", topic: "信息安全" },
{
id: "open2-2", topic: "信息排查" },
{
id: "open2-3", topic: "信息汇总" },
{
id: "open2-4", topic: "ERP" },
],
},
{
id: "open3",
topic: "生产车间",
children: [
{
id: "open3-1", topic: "安全制度" },
{
id: "open3-2", topic: "车间操作手册" },
],
},
],
},
{
id: "powerful",
topic: "员工福利",
number,
children: [
{
id: "powerful1", topic: "雪天", tip: "3366" },
{
id: "powerful2", topic: "年假", tip: "98999" },
{
id: "powerful3",
topic: "法定节假日",
number,
},
{
id: "powerful4", topic: "生日祝福" },
{
id: "powerful5",
topic: "成长与进步",
children: [{
id: "powerful5-1", topic: "员工培训" }],
},
],
},
],
};
其中,id,topic,children 的用法见官方文档,number 为定制化展示的数据部分,如下图所示:
当 type 为 label2 时,number 为数组,对应关系如下图所示:
- type 分为 5 种类型:default,check,label,label2,input,当 type 为 default 类型时,传值 type=“”即可,页面效果如下:
当 type 为 check 类型时,页面效果如下:
当 type 为 label 类型时,页面效果如下:
当 type 为 label2 类型时,页面效果如下:
当 type 为 input 类型时,页面效果如下:
- 参数配置见下图,可根据实际需求进行调整:
四、源码
1. Minder.vue
<template>
<div class="minder" :id="jsmind_container_id"></div>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
toRefs,
onMounted,
computed,
watch,
} from "vue";
declare global {
interface Window {
metaData: any;
uncheckedData: any;
}
}
export default defineComponent({
name: "minder",
props: {
data: {
type: Object,
default: {
},
},
type: {
//type:defalut,check,label,label2,input
type: String,
default: "",
},
jsmind_container_id: {
type: String,
default: "jsmind_container",
},
rootValue: {
type: Number,
default: 0,
},
jsmindType: {
type: Number,
default: 0,
},
},
setup(props) {
console.log("data,type,id", props);
const state = reactive({
name: "",
});
let jm: any;
const jwidth = {
"": 140,
check: 140,
label: 158,
label2: 216,
input: 178,
};
const mind = computed(() => ({
meta: {
name: "jsMind",
author: "",
version: "0.2",
},
format: "node_tree",
data: props.data,
}));
const handleMind = (data: any) => {
console.log("props", props);
const {
id, topic, children, number = [50], topic_en, tip } = data;
console.log("data", data);
//如果是div,不是字符串,不予处理
if (topic.includes("<div")) return;
const isParent: boolean = children && children.length > 0;
const isRoot: boolean = id === "root";
const jdom = getDomByType(
props.type,
topic,
isParent,
id,
topic_en,
number
);
const jtip = getTipByType(props.type, tip, isParent, number);
if (isRoot) {
//根节点单独处理
data.topic = getRootByType(props.type, topic, props.rootValue);
}
// else if (props.type === "label2" && isParent) {
// //label2类型父节点底部有文字
// data.topic = `
// <div class="jmnnode-base">
// ${jdom}
// ${jtip}
// </div>`;
// }
else if (props.type != "label2" && !isParent) {
//其他类型叶子节点底部有文字
data.topic = `
<div class="jmnnode-base">
${
jdom}
${
jtip}
</div>`;
}
//20211225---label2只有叶子结点显示均值方差
else if (props.type == "label2" && !isParent) {
//其他类型叶子节点底部有文字
data.topic = `
<div class="jmnnode-base">
${
jdom}
${
jtip}
</div>`;
} else {
data.topic = jdom;
}
if (isParent) {
for (let child of children) {
handleMind(child);
}
}
};
const getDomByType = (
type: string,
topic: string,
isParent: boolean,
id: string,
topic_en: string,
number?: number | number[]
) => {
// console.log("type##########", type);
let resDom = "";
let jclass = isParent
? `class="jmnnode-dom jmnnode-dom-blue"`
: `class="jmnnode-dom"`;
switch (type) {
case "check":
if (isParent) {
resDom = `<svg-icon icon-class="icon_unbiased"></svg-icon> <div ${
jclass} style="width:${
jwidth[type]}px">${
topic}</div>`;
} else {
resDom = `
<div ${
jclass} style="width:${
jwidth[type]}px">
<span>${
topic}</span>
</div> <input type="checkbox" οnclick="handleClick(this.checked,'${
topic}','${
id}','${
topic_en}')"/>`;
}
break;
case "label":
jclass = isParent
? `class="jmnnode-dom jmnnode-dom-blue jmnnode-dom-label"`
: `class="jmnnode-dom jmnnode-dom-label"`;
resDom = `
<div ${
jclass} style="width:${
jwidth[type]}px">
<span>${
topic}</span>
<div class="jmnnode-dom-number">${
number || 0}</div>
</div>`;
break;
case "label2":
jclass = isParent
? `class="jmnnode-dom jmnnode-dom-blue jmnnode-dom-label"`
: `class="jmnnode-dom jmnnode-dom-label"`;
const [num1, num2, num3, num4] = (number as number[]) || [0, 0, 0, 0];
//20211226---修改只有叶子结点显示权重方差
if (isParent) {
resDom = `
<div ${
jclass} style="width:${
jwidth[type] - 30}px">
<span>${
topic}</span>
<div class="jmnnode-dom-avg">
<div class="jmnnode-dom-number">${
num1?.toFixed(2)}</div>
</div>
</div>`;
} else {
resDom = `
<div ${
jclass} style="width:${
jwidth[type] - 30}px">
<span>${
topic}</span>
<div class="jmnnode-dom-avg">
<div class="jmnnode-dom-number">${
num1?.toFixed(2)}</div>
<div class="jmnnode-dom-number jmnnode-dom-green">${
num2?.toFixed(
2
)}</div>
</div>
</div>`;
}
break;
case "input":
jclass = isParent
? `class="jmnnode-dom jmnnode-dom-blue jmnnode-dom-input"`
: `class="jmnnode-dom jmnnode-dom-input"`;
resDom = `
<div ${
jclass} style="width:${
jwidth[type]}px">
<span>${
topic}</span>
<input class="jmnnode-dom-input-right" value='${
number}' οnchange="handleInput(this.value,'${
topic}','${
id}')"/>
</div>`;
break;
default:
if (topic == "完整性") {
resDom = ` <div ${
jclass} style="width:${
jwidth["check"]}px">
<img src="/src/assets/img/icon_whole.png" width="22" height="22" style=" position: absolute;left: 12px;top: 16px;" />
${
topic}</div>`;
} else if (topic == "现实性") {
resDom = ` <div ${
jclass} style="width:${
jwidth["check"]}px">
<img src="/src/assets/img/icon_clock.png" width="22" height="22" style=" position: absolute;left: 12px;top: 16px;" />
${
topic}</div>`;
} else if (topic == "准确性") {
resDom = ` <div ${
jclass} style="width:${
jwidth["check"]}px">
<img src="/src/assets/img/icon_right.png" width="22" height="22" style=" position: absolute;left: 12px;top: 16px;" />
${
topic}</div>`;
} else if (topic == "无偏性") {
resDom = ` <div ${
jclass} style="width:${
jwidth["check"]}px">
<img src="/src/assets/img/icon_unbiased.png" width="22" height="22" style=" position: absolute;left: 12px;top: 16px;" />
${
topic}</div>`;
} else if (topic == "规范性") {
resDom = ` <div ${
jclass} style="width:${
jwidth["check"]}px">
<img src="/src/assets/img/icon_standard.png" width="22" height="22" style=" position: absolute;left: 12px;top: 16px;" />
${
topic}</div>`;
} else {
resDom = ` <div ${
jclass} style="width:${
jwidth["check"]}px">
${
topic}</div>`;
}
}
return resDom;
};
const getTipByType = (
type: string,
tip: string = "0",
isParent: boolean,
number: number[] = [0, 0, 0, 0]
) => {
let resDom = "";
switch (type) {
case "label2":
const [, , num3, num4] = (number as number[]) || [0, 0, 0, 0];
resDom = `
<div class="jmnnode-tip jmnnode-tip-avg" style="width:${
jwidth[type]
}px">
方差:<div class="jmnnode-tip-blue" style="margin-right:32px">${
num3?.toFixed(
2
)}</div>
均值:<div class="jmnnode-tip-yellow">${
num4?.toFixed(2)}</div>
</div>`;
break;
default:
resDom = `<div class="jmnnode-tip" style="width:${
jwidth["check"]}px">(${
tip})</div>`;
}
return resDom;
};
const getRootByType = (type: string, topic: string, rootValue: number) => {
let resDom = "";
switch (type) {
case "label2":
resDom = `
<div class="jmnnode-base">
<div class="jmnnode-dom">${
topic}</div>
<div class="jmnnode-root-line"></div>
<div class="jmnnode-root-tip">
<div class="jmnnode-root-tip-title">综合评估:</div>
<div class="jmnnode-root-tip-blue">${
rootValue || 0}</div>
</div>
</div>`;
break;
default:
resDom = `
<div class="jmnnode-base">
<div class="jmnnode-dom" style="display:block" > <img src="/src/assets/img/pic_demo.png" width="33" height="33" />${
topic}</div>
<div class="jmnnode-root-line"></div>
</div>`;
}
return resDom;
};
const options = {
container: props.jsmind_container_id as String, //容器的ID
editable: false, // 是否启用编辑
theme: "primary", //主题
mode: "side", // 显示模式========full - 子节点动态分布在根节点两侧 [默认值] side - 子节点只分布在根节点右侧
layout: {
hspace: 50, // 节点之间的水平间距
vspace: 40, // 节点之间的垂直间距
pspace: 13, // 节点与连接线之间的水平间距(用于容纳节点收缩/展开控制器)
},
//options的属性
//container : '', // [必选] 容器的ID
// editable : false, // 是否启用编辑
// theme : null, // 主题
// mode :'full', // 显示模式========full - 子节点动态分布在根节点两侧 [默认值] side - 子节点只分布在根节点右侧
// support_html : true, // 是否支持节点里的HTML元素
view: {
hmargin: 0, // 思维导图距容器外框的最小水平距离
vmargin: 100, // 思维导图距容器外框的最小垂直距离
line_width: 1, // 思维导图线条的粗细
line_color: "#C6C6C9", // 思维导图线条的颜色
},
// layout:{
// hspace:30, // 节点之间的水平间距
// vspace:20, // 节点之间的垂直间距
// pspace:13 // 节点与连接线之间的水平间距(用于容纳节点收缩/展开控制器)
// },
};
if (props.jsmindType == 1) {
options.layout = {
hspace: 180, // 节点之间的水平间距
vspace: 40, // 节点之间的垂直间距
pspace: 13, // 节点与连接线之间的水平间距(用于容纳节点收缩/展开控制器)
};
}
//handleMind(mind.value.data);
const showMinder = () => {
console.log(props);
console.log("handleMind", JSON.parse(JSON.stringify(mind.value.data)));
window.metaData = JSON.parse(JSON.stringify(mind.value.data));
window.uncheckedData = JSON.parse(JSON.stringify(mind.value.data));
handleMind(mind.value.data);
// console.log("handleMind2", mind.value.data);
jm.show(mind.value);
//jm.disable_edit();//禁止编制
jm.expand_all(); //展开全部节点
// jm.add_node(parent_node, nodeid, topic, data);//添加节点
// setHeight();
};
// const setHeight = () => {
// setTimeout(() => {
// const dom = document.querySelector("jmnodes") as HTMLDivElement;
// // const btn_dom = document.querySelector(
// // ".next-step-btn"
// // ) as HTMLDivElement;
// (
// document.querySelector(
// `#${props.jsmind_container_id}`
// ) as HTMLDivElement
// ).style.height = `${dom.clientHeight}px`;
// }, 0);
// };
watch(
() => props.data,
(newVal, oldVal) => {
if (newVal.id) showMinder();
}
);
onMounted(() => {
// @ts-ignore
// if (jsmindType == 1) {
jm = new jsMind(options);
// } else {
// jm = new jsMind(options);
// }
jm.add_event_listener(function (type, data) {
console.log("this", jm.view.size.h);
const dom = document.querySelector("jmnodes") as HTMLDivElement;
console.log("dom.clientHeight", dom.clientHeight);
(
document.querySelector(
`#${
props.jsmind_container_id}`
) as HTMLDivElement
).style.minHeight = `${
dom.clientHeight}px`;
});
if (props.data.id) showMinder();
});
return {
...toRefs(state),
};
},
});
</script>
<style lang="scss" scoped>
.minder {
width: 100%;
height: 100%;
.jmnode-icon {
position: absolute;
left: 12px;
top: 16px;
}
}
</style>
2. DataMinder.vue
<template>
<div class="data-mind">
<!-- <div class="data-mind-title" v-if="type != 'label2'">指标和参数配置</div> -->
<div class="data-mind-header">
<div class="data-mind-tip">
<div class="data-mind-tip__label"></div>
<div class="data-mind-tip__name">质量特性</div>
</div>
<div class="data-mind-tip">
<div class="data-mind-tip__label2"></div>
<div class="data-mind-tip__name">度量指标</div>
</div>
</div>
<minder
:data="data"
:type="type"
:jsmind_container_id="jsmind_container_id"
:rootValue="rootValue"
:jsmindType="jsmindType"
></minder>
</div>
</template>
<script lang="ts">
import {
defineComponent, reactive, toRefs, computed } from "vue";
import Minder from "@/components/minder/Minder.vue";
export default defineComponent({
name: "DataMind",
components: {
Minder },
props: {
data: {
type: Object,
default: {
},
},
type: {
//type:defalut,check,label,label2,input
type: String,
default: "",
},
jsmind_container_id: {
type: String,
default: "jsmind_container",
},
rootValue: {
type: Number,
default: 0,
},
jsmindType: {
type: Number,
default: 0,
},
},
setup() {
const state = reactive({
name: "", //type:defalut,check,label,label2,input
});
return {
...toRefs(state),
};
},
});
</script>
<style lang="scss" scoped>
$height: 48px;
.data-mind {
width: 100%;
height: 100%;
position: relative;
&-title {
width: 100%;
height: $height;
background: rgba(84, 115, 231, 0.09);
display: flex;
align-items: center;
padding: 0 20px;
font-size: 16px;
font-weight: 600;
color: #3b3b5b;
}
&-header {
position: absolute;
padding: 27px 38px;
}
&-tip {
// position: absolute;
// left: 16px;
// top: $height + 20;
font-size: 14px;
color: #9b9ba5;
font-weight: 400;
display: flex;
align-items: center;
&__label {
width: 28px;
height: 12px;
background: #f4f7ff;
border: 1px solid rgba(223, 223, 223, 1);
border-radius: 2px;
margin-right: 10px;
}
&__label2 {
width: 28px;
height: 12px;
background: #ffffff;
border: 1px solid rgba(223, 223, 223, 1);
border-radius: 2px;
margin-right: 10px;
}
}
.circle-leaf {
border-radius: 50%;
width: 9px;
height: 9px;
background: rgba(84, 115, 231, 0.7);
margin: 0 40px 0 4px;
}
&-line {
line-height: 22px;
}
.circle-label {
background: rgba(84, 115, 231, 0.2);
border: 1px solid #5473e7;
border-radius: 50%;
width: 9px;
height: 9px;
margin: 0 0 0 8px;
}
}
.square-blue {
width: 9px;
height: 9px;
background: #5473e7;
border-radius: 1px;
margin-right: 32px;
margin-left: 8px;
}
.square-green {
width: 9px;
height: 9px;
background: #44d7b6;
border-radius: 1px;
margin-right: 32px;
margin-left: 8px;
}
.square-purple {
width: 12px;
height: 4px;
background: #6236ff;
margin-right: 32px;
margin-left: 8px;
}
.square-orange {
width: 12px;
height: 4px;
background: #f7b500;
margin-left: 8px;
}
.square-white {
width: 9px;
height: 9px;
background: #ffffff;
border-radius: 1px;
border: 1px solid #dfdfdf;
}
.weight-block {
line-height: 22px;
display: flex;
align-items: center;
margin-left: 32px;
}
.label2-block {
line-height: 22px;
display: flex;
align-items: center;
}
</style>
3. jsmind.css
/*
* Released under BSD License
* Copyright (c) 2014-2015 hizzgdev@163.com
*
* Project Home:
* https://github.com/hizzgdev/jsmind/
*/
/* important section */
.jsmind-inner {
position: relative;
overflow: auto;
width: 100%;
height: 100%;
}
/*box-shadow:0 0 2px #000;*/
.jsmind-inner {
moz-user-select: -moz-none;
-moz-user-select: none;
-o-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* z-index:1 */
canvas {
position: absolute;
z-index: 1;
}
/* z-index:2 */
jmnodes {
position: absolute;
z-index: 2;
background-color: rgba(0, 0, 0, 0);
}
/*background color is necessary*/
jmnode {
position: absolute;
cursor: default;
max-width: 400px;
white-space: nowrap;
/* overflow: hidden; */
text-overflow: ellipsis;
}
jmexpander {
position: absolute;
width: 14px;
height: 2px;
margin-top: 6px;
color: #b9b9bd;
background: #c6c6c9;
font-size: 12px;
text-align: center;
cursor: pointer;
}
/* default theme */
jmnode {
/* padding: 10px 20px; */
background-color: #fff;
color: #333;
/* border-radius: 20px; */
/* box-shadow: 1px 1px 1px #666; */
font: 16px/1.125 Verdana, Arial, Helvetica, sans-serif;
}
jmnode:hover {
box-shadow: 2px 2px 8px #000;
background-color: #ebebeb;
color: #333;
}
jmnode.selected {
background-color: #11f;
color: #fff;
box-shadow: 2px 2px 8px #000;
}
jmnode.root {
font-size: 24px;
}
jmexpander:hover {
border-color: #000;
}
@media screen and (max-device-width: 1024px) {
jmnode {
padding: 5px;
border-radius: 3px;
font-size: 14px;
}
jmnode.root {
font-size: 21px;
}
}
/* primary theme */
jmnodes.theme-primary jmnode {
/* background-color: #428bca; */
color: #fff;
/* border-color: #357ebd; */
}
jmnodes.theme-primary jmnode:hover {
background-color: #3276b1;
border-color: #285e8e;
}
jmnodes.theme-primary jmnode.selected {
background-color: #f1c40f;
color: #fff;
}
jmnodes.theme-primary jmnode.root {
}
jmnodes.theme-primary jmexpander {
}
jmnodes.theme-primary jmexpander:hover {
}
/* warning theme */
jmnodes.theme-warning jmnode {
background-color: #f0ad4e;
border-color: #eea236;
color: #fff;
}
jmnodes.theme-warning jmnode:hover {
background-color: #ed9c28;
border-color: #d58512;
}
jmnodes.theme-warning jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-warning jmnode.root {
}
jmnodes.theme-warning jmexpander {
}
jmnodes.theme-warning jmexpander:hover {
}
/* danger theme */
jmnodes.theme-danger jmnode {
background-color: #d9534f;
border-color: #d43f3a;
color: #fff;
}
jmnodes.theme-danger jmnode:hover {
background-color: #d2322d;
border-color: #ac2925;
}
jmnodes.theme-danger jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-danger jmnode.root {
}
jmnodes.theme-danger jmexpander {
}
jmnodes.theme-danger jmexpander:hover {
}
/* success theme */
jmnodes.theme-success jmnode {
background-color: #5cb85c;
border-color: #4cae4c;
color: #fff;
}
jmnodes.theme-success jmnode:hover {
background-color: #47a447;
border-color: #398439;
}
jmnodes.theme-success jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-success jmnode.root {
}
jmnodes.theme-success jmexpander {
}
jmnodes.theme-success jmexpander:hover {
}
/* info theme */
jmnodes.theme-info jmnode {
background-color: #5dc0de;
border-color: #46b8da;
color: #fff;
}
jmnodes.theme-info jmnode:hover {
background-color: #39b3d7;
border-color: #269abc;
}
jmnodes.theme-info jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-info jmnode.root {
}
jmnodes.theme-info jmexpander {
}
jmnodes.theme-info jmexpander:hover {
}
/* greensea theme */
jmnodes.theme-greensea jmnode {
background-color: #1abc9c;
color: #fff;
}
jmnodes.theme-greensea jmnode:hover {
background-color: #16a085;
}
jmnodes.theme-greensea jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-greensea jmnode.root {
}
jmnodes.theme-greensea jmexpander {
}
jmnodes.theme-greensea jmexpander:hover {
}
/* nephrite theme */
jmnodes.theme-nephrite jmnode {
background-color: #2ecc71;
color: #fff;
}
jmnodes.theme-nephrite jmnode:hover {
background-color: #27ae60;
}
jmnodes.theme-nephrite jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-nephrite jmnode.root {
}
jmnodes.theme-nephrite jmexpander {
}
jmnodes.theme-nephrite jmexpander:hover {
}
/* belizehole theme */
jmnodes.theme-belizehole jmnode {
background-color: #3498db;
color: #fff;
}
jmnodes.theme-belizehole jmnode:hover {
background-color: #2980b9;
}
jmnodes.theme-belizehole jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-belizehole jmnode.root {
}
jmnodes.theme-belizehole jmexpander {
}
jmnodes.theme-belizehole jmexpander:hover {
}
/* wisteria theme */
jmnodes.theme-wisteria jmnode {
background-color: #9b59b6;
color: #fff;
}
jmnodes.theme-wisteria jmnode:hover {
background-color: #8e44ad;
}
jmnodes.theme-wisteria jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-wisteria jmnode.root {
}
jmnodes.theme-wisteria jmexpander {
}
jmnodes.theme-wisteria jmexpander:hover {
}
/* asphalt theme */
jmnodes.theme-asphalt jmnode {
background-color: #34495e;
color: #fff;
}
jmnodes.theme-asphalt jmnode:hover {
background-color: #2c3e50;
}
jmnodes.theme-asphalt jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-asphalt jmnode.root {
}
jmnodes.theme-asphalt jmexpander {
}
jmnodes.theme-asphalt jmexpander:hover {
}
/* orange theme */
jmnodes.theme-orange jmnode {
background-color: #f1c40f;
color: #fff;
}
jmnodes.theme-orange jmnode:hover {
background-color: #f39c12;
}
jmnodes.theme-orange jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-orange jmnode.root {
}
jmnodes.theme-orange jmexpander {
}
jmnodes.theme-orange jmexpander:hover {
}
/* pumpkin theme */
jmnodes.theme-pumpkin jmnode {
background-color: #e67e22;
color: #fff;
}
jmnodes.theme-pumpkin jmnode:hover {
background-color: #d35400;
}
jmnodes.theme-pumpkin jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-pumpkin jmnode.root {
}
jmnodes.theme-pumpkin jmexpander {
}
jmnodes.theme-pumpkin jmexpander:hover {
}
/* pomegranate theme */
jmnodes.theme-pomegranate jmnode {
background-color: #e74c3c;
color: #fff;
}
jmnodes.theme-pomegranate jmnode:hover {
background-color: #c0392b;
}
jmnodes.theme-pomegranate jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-pomegranate jmnode.root {
}
jmnodes.theme-pomegranate jmexpander {
}
jmnodes.theme-pomegranate jmexpander:hover {
}
/* clouds theme */
jmnodes.theme-clouds jmnode {
background-color: #ecf0f1;
color: #333;
}
jmnodes.theme-clouds jmnode:hover {
background-color: #bdc3c7;
}
jmnodes.theme-clouds jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-clouds jmnode.root {
}
jmnodes.theme-clouds jmexpander {
}
jmnodes.theme-clouds jmexpander:hover {
}
/* asbestos theme */
jmnodes.theme-asbestos jmnode {
background-color: #95a5a6;
color: #fff;
}
jmnodes.theme-asbestos jmnode:hover {
background-color: #7f8c8d;
}
jmnodes.theme-asbestos jmnode.selected {
background-color: #11f;
color: #fff;
}
jmnodes.theme-asbestos jmnode.root {
}
jmnodes.theme-asbestos jmexpander {
}
jmnodes.theme-asbestos jmexpander:hover {
}
4. jsmind.js
/*
* Released under BSD License
* Copyright (c) 2014-2016 hizzgdev@163.com
*
* Project Home:
* https://github.com/hizzgdev/jsmind/
*/
;(function ($w) {
'use strict';
// set 'jsMind' as the library name.
// __name__ should be a const value, Never try to change it easily.
var __name__ = 'jsMind';
// library version
var __version__ = '0.4.6';
// author
var __author__ = 'hizzgdev@163.com';
// an noop function define
var _noop = function () {
};
var logger = (typeof console === 'undefined') ? {
log: _noop, debug: _noop, error: _noop, warn: _noop, info: _noop
} : console;
// check global variables
if (typeof module === 'undefined' || !module.exports) {
if (typeof $w[__name__] != 'undefined') {
logger.log(__name__ + ' has been already exist.');
return;
}
}
// shortcut of methods in dom
var $d = $w.document;
var $g = function (id) {
return $d.getElementById(id);
};
var $c = function (tag) {
return $d.createElement(tag);
};
var $t = function (n, t) {
if (n.hasChildNodes()) {
n.firstChild.nodeValue = t;
} else {
n.appendChild($d.createTextNode(t));
}
};
var $h = function (n, t) {
if (t instanceof HTMLElement) {
n.innerHTML = '';
n.appendChild(t)
} else {
n.innerHTML = t;
}
};
// detect isElement
var $i = function (el) {
return !!el && (typeof el === 'object') && (el.nodeType === 1) && (typeof el.style === 'object') && (typeof el.ownerDocument === 'object');
};
if (typeof String.prototype.startsWith != 'function') {
String.prototype.startsWith = function (p) {
return this.slice(0, p.length) === p;
};
}
var DEFAULT_OPTIONS = {
container: '', // id of the container
editable: false, // you can change it in your options
theme