js+css改造jsmind实现思维导图 | 树状图

一. 调研

1.常见树状图 | 思维导图

  1. https://juejin.cn/post/6844904103298990093

  2. echarts
    https://echarts.apache.org/examples/zh/editor.html?c=tree-polyline

  3. https://juejin.cn/post/6844903615425937416

  4. https://bl.ocks.org/mbostock/4339083

  5. https://fperucic.github.io/treant-js/

  6. gojs
    https://gojs.net/latest/samples/decisionTree.html
    https://balkan.app/OrgChartJS/Demos/RoyalFamilyTree
    https://codepen.io/tutsplus/pen/MWedpoj

  7. 有复选框的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.vuejsmind.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 为数组,对应关系如下图所示:
在这里插入图片描述

  1. type 分为 5 种类型:default,check,label,label2,input,当 type 为 default 类型时,传值 type=“”即可,页面效果如下:
    在这里插入图片描述

当 type 为 check 类型时,页面效果如下:
在这里插入图片描述

当 type 为 label 类型时,页面效果如下:

在这里插入图片描述

当 type 为 label2 类型时,页面效果如下:
在这里插入图片描述

当 type 为 input 类型时,页面效果如下:
在这里插入图片描述

  1. 参数配置见下图,可根据实际需求进行调整:
    在这里插入图片描述

四、源码

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
  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值