JSMInd实现动态思维导图的保存和展示

JSMInd实现动态思维导图的保存和展示

前言:最近公司要求java微服务架构中某一个页面做成动态表格的形式,可以手动增加列这种,但是领导希望能够做成思维导图更好,也是能够实时的动态添加数据,保存数据,查看数据。其他同事做列转行,我进行调研,最终选择JSMind插件实现。

第一步:

	新建springboot项目,引入对应的js文件jsmind.js、jsmind.draggable.js、jsmind.css、jquery-3.5.1.min.js。引入依赖(根据自己需求取舍,我这将项目里的都拿过来了)
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--由于feign整合了ribbon的负载均衡,所以需要引入ribbon的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.2.2.RELEASE</version>
            <!--排除nacos-client需要注掉-->
            <!-- <exclusions>
                 <exclusion>
                     <groupId>com.alibaba.nacos</groupId>
                     <artifactId>nacos-client</artifactId>
                 </exclusion>
             </exclusions>-->
        </dependency>
        <dependency>
            <groupId>com.hf.example</groupId>
            <artifactId>order-service-api</artifactId>
            <version>0.0.2-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.6</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <!-- <version>2.1.0.Final</version> -->
        </dependency>
        <dependency>
            <groupId>jakarta.xml.bind</groupId>
            <artifactId>jakarta.xml.bind-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
</dependencies>

第二步:

	新建index.html,代码如下:
<!DOCTYPE html>
<html>
<head>
    <title>jsmindd</title>
</head>
<link rel="stylesheet" href="css/jsmind.css">
<script type="text/javascript" src="js/jquery-3.5.1.min.js"></script>
<script type="text/javascript" src="js/jsmind.js"></script>
<script type="text/javascript" src="js/jsmind.draggable.js"></script>
<body>
<div id="jsmind_container" style="width: 1000px;height: 1000px;"></div>
<button type="button" onclick="save()">保存</button>
<input type="hidden"  th:value='${queryId}' id="queryId">
<!--<button type="button" onclick="initializeNode(1)">查询</button>-->
</body>
</html>
<script>

    var json={"id":"root", "topic":"jsMind"};
    var queryId=$("#queryId").val();
    if(queryId!=null && queryId!=""&&queryId!="undefined"){
       initializeNode(parseInt(queryId));
   }
   // var json={"children":[{"children":[{"expanded":true,"id":"5ac0879039b39d07","topic":"测试1-1"},{"expanded":true,"id":"5ac088c8337ea5ec","topic":"测试1-2"}],"expanded":true,"id":"5ac0861536b973f9","topic":"测试1"},{"children":[{"expanded":true,"id":"5ac089d5d3d25fd2","topic":"测试2-1"},{"expanded":true,"id":"5ac08ae33fd3213e","topic":"测试2-2"}],"expanded":true,"id":"5ac086e73ce21e88","topic":"测试2"}],"expanded":true,"id":"root","topic":"jsMind"};
    var mind = {
        "meta":{
            "name":"jsMind remote",
            "author":"hizzgdev@163.com",
            "version":"0.2"
        },
        "format":"node_tree",
        "data":json
    }
    // jsMind的选项,更多参数参见jsMind的文档
    // https://github.com/hizzgdev/jsmind/blob/master/docs/zh/index.md
    var options = {
        container:'jsmind_container',//容器的ID
        theme:'primary',				//主题

        //options的属性
        //container : '',         // [必选] 容器的ID
         editable : true,       // 是否启用编辑
        // theme : null,           // 主题
        // mode :'full',           // 显示模式========full - 子节点动态分布在根节点两侧 [默认值] side - 子节点只分布在根节点右侧
        // support_html : true,    // 是否支持节点里的HTML元素
        // view:{
        //     hmargin:100,        // 思维导图距容器外框的最小水平距离
        //     vmargin:50,         // 思维导图距容器外框的最小垂直距离
        //     line_width:2,       // 思维导图线条的粗细
        //     line_color:'#555'   // 思维导图线条的颜色
        // },
        // layout:{
        //     hspace:30,          // 节点之间的水平间距
        //     vspace:20,          // 节点之间的垂直间距
        //     pspace:13           // 节点与连接线之间的水平间距(用于容纳节点收缩/展开控制器)
        // },
         shortcut:{
             enable:true,        // 是否启用快捷键
        //     handles:{},         // 命名的快捷键事件处理器
             mapping:{           // 快捷键映射
                addchild   : 45,    // <Insert>
                 addbrother : 13,    // <Enter>
                 editnode   : 113,   // <F2>
                 delnode    : 46,    // <Delete>
                 toggle     : 32,    // <Space>
                 left       : 37,    // <Left>
                 up         : 38,    // <Up>
                 right      : 39,    // <Right>
                 down       : 40,    // <Down>
             }
         },
    }
    var jm = new jsMind(options);
    jm.show(mind);
    //jm.disable_edit();//禁止编制
    jm.expand_all();//展开全部节点


var savejson;
//将思维导图拼成一个json传递到后台进行保存
function save() {
   var node=jm.get_root();
   var id=node.id;
   var topic = node.topic;
    var expanded = node.expanded;
   var children = node.children;
    savejson='{"queryId":"'+queryId+'","id":"'+id+'","topic":"'+topic+'","expanded":"'+expanded+'"';
   generateChild(children);
   var jsonend= savejson+'}';
    $.ajax({
        url:"/save",		//请求地址
        type:"post",			 //请求方式
        data:jsonend,			 //这里的data:发送给服务器的请求数据
        datatype:"json",
        contentType: 'application/json;charset=utf-8',
        success:function(str){  //回调函数:数据请求成功之后调用
            	if(str=='true'){
            	    alert("保存成功");
                }else{
                    alert("保存失败");
                }			     //这里的data:从服务器发送回来的数据
        },
    });
}
//递归实现思维导图各个子节点的拼接
function generateChild(children) {
    if(children.length>0){
        savejson=savejson +',"children":[';
        for (var i = 0 ; i< children.length ; i++){
            var zid=children[i].id;
            var ztopic=children[i].topic;
            var zexpanded=children[i].expanded;
            var parentId=children[i].parent.id;
            savejson=savejson +'{"id":"'+zid+'","topic":"'+ztopic+'","expanded":"'+zexpanded+'","parentId":"'+parentId+'"';
            if(children[i].children.length>0){
                generateChild(children[i].children,savejson);
            }
            savejson=savejson+"},";
        }
        savejson=savejson.substring(0, savejson.length-1)+"]";
    }
}
/**
 * 根据id初始化节点信息
 */
function initializeNode(i) {
    var queryjson={"queryId":i};
    $.ajax({
        url:"/query",		//请求地址
        type:"get",			 //请求方式
        data:queryjson,			 //这里的data:发送给服务器的请求数据
        async: false,
        datatype:"json",
        //contentType: 'application/json;charset=UTF-8"',
        success:function(msg){  //回调函数:数据请求成功之后调用
            json=JSON.parse(msg);
        },
    });

}
</script>

第三步:

	application.properties添加数据库配置如下:
server.port=8091
##单机操作解注(没有使用nacos或者eureka)
##order-service.ribbon.listOfServers=\localhost:8082
spring.application.name =user-service
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/hf-test?useUnicode=true&characterEncoding=utf8&useOldAliasMetadataBehavior=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

mybatis.mapper-locations= classpath:com/example/mapper/persistence/*.xml

第四步:

	controller层代码编写:
import com.alibaba.fastjson.JSONObject;
import com.example.dto.FileNodePojo;
import com.example.mapper.entitys.FileNode;
import com.example.mapper.persistence.FileNodeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class WebController {

    @Autowired
    FileNodeMapper fileNodeMapper;

    @RequestMapping(value="/index")
    @ResponseBody
    public ModelAndView index(int i){

        ModelAndView mv = new ModelAndView("index.html");
        mv.addObject("queryId", i);
       // mv.setViewName("index.html");
        return mv;
    }

    @PostMapping(value = "/save",consumes="application/json;charset=UTF-8")
    @ResponseBody
    public String save(@RequestBody FileNodePojo fileNodePojo){
        String saveflag="false";
        try {
           // String[] excludeProperties = {"queryId"};
            String jsonString = JSONObject.toJSONString(fileNodePojo);
            FileNode fileNode = new FileNode();
            fileNode.setId(Integer.valueOf(fileNodePojo.getQueryId()));
            fileNode.setFilenodejsonstr(jsonString);
            FileNode node = fileNodeMapper.selectByPrimaryKey(Integer.valueOf(fileNodePojo.getQueryId()));
            if (node!=null){
                fileNodeMapper.updateByPrimaryKey(fileNode);
            }else{
                fileNodeMapper.insert(fileNode);
            }
            saveflag="true";
        }catch (Exception e){
            throw e;
        }
        return saveflag;
    }
    @GetMapping(value = "/query")
    @ResponseBody
    public String query(String queryId){
        FileNode fileNode = fileNodeMapper.selectByPrimaryKey(Integer.valueOf(queryId));
        return fileNode.getFilenodejsonstr();
    }
}

第五步:

	实体类代码:
import lombok.Data;

import java.io.Serializable;
import java.util.List;
@Data
public class FileNodePojo implements Serializable {
    public String queryId;//对应jsmind唯一id
    public String id;//对应jsmind唯一id
    public String topic;//对应jsmind显示的名称
    public String direction;//对应jsmind思维导图的朝向 left/right
    public boolean expanded; //对应jsmind该节点是否展开true/false
    public String background; //jsmind只识别background-color属性,此处定义“-”会编译不通过,待前台js批量替换处理
    public String foreground; //jsmind只识别foreground-color属性,此处定义“-”会编译不通过,待前台js批量替换处理
    public String parentd; //jsmind没有此属性,此处定义为了与数据库所属父节点字段对应,递归关联查询时会用到
    public List<FileNodePojo> children;//对应jsmind当前节点的子节点集合

}

第六步:

	数据库底层代码FileNode、FileNodeMapper和FileNodeMapper.xml:
public class FileNode {
    private Integer id;

    private String filenodejsonstr;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFilenodejsonstr() {
        return filenodejsonstr;
    }

    public void setFilenodejsonstr(String filenodejsonstr) {
        this.filenodejsonstr = filenodejsonstr == null ? null : filenodejsonstr.trim();
    }
}
import com.example.mapper.entitys.FileNode;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface FileNodeMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(FileNode record);

    int insertSelective(FileNode record);

    FileNode selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(FileNode record);

    int updateByPrimaryKey(FileNode record);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.mapper.persistence.FileNodeMapper" >
  <resultMap id="BaseResultMap" type="com.example.mapper.entitys.FileNode" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="fileNodeJsonStr" property="filenodejsonstr" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, fileNodeJsonStr
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from filenode
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from filenode
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.example.mapper.entitys.FileNode" >
    insert into filenode (id, fileNodeJsonStr)
    values (#{id,jdbcType=INTEGER}, #{filenodejsonstr,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="com.example.mapper.entitys.FileNode" >
    insert into filenode
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="filenodejsonstr != null" >
        fileNodeJsonStr,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      </if>
      <if test="filenodejsonstr != null" >
        #{filenodejsonstr,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.example.mapper.entitys.FileNode" >
    update filenode
    <set >
      <if test="filenodejsonstr != null" >
        fileNodeJsonStr = #{filenodejsonstr,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.example.mapper.entitys.FileNode" >
    update filenode
    set fileNodeJsonStr = #{filenodejsonstr,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

第七步:进行测试

刚开始访问界面如下:
在这里插入图片描述

添加对应属性后:
在这里插入图片描述
点击保存数据库存储为:
在这里插入图片描述
再次刷新页面仍保持不变,更新之后,再次刷新为最新数据。完成任务。

源码下载地址
链接:https://pan.baidu.com/s/1jBkyu6fi1OvoEDEP-JWi-A
提取码:myfx
分享请注明出处。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值