vue使用vue2-org-tree-节点增删改查(已实现)

在这里插入图片描述
需要使用到树形图绘制人物关系节点,综合考虑选择了这个,结合elementUI实现。


1.下载插件

npm i vue2-org-tree    //树形图插件
npm install --save-dev less less-loader //less

注意点:
在main.js中引用vue2-org-tree后,若不显示效果,问题是没有引入相应的css,我的引入是 import "vue2-org-tree/dist/style.css";

2.使用

<vue2-org-tree ref="tree" :key="treeDataKey"  :data="treeData[0]" :horizontal="!horizontal" :collapsable="collapsable" :render-content="renderContent" @on-expand="onExpand" @on-node-click="NodeClick" :label-class-name="labelClassName" />

注意点
形成树形图的关键方法:
1.renderContent ——初始渲染树形图

renderContent (h, data) {
      return (
        <div>
          <el-popover placement="top-start" trigger="hover">
            <el-button size="mini" class="addBtn" icon="el-icon-circle-plus-outline"
              onClick={() => { this.openAddFrom(data) }}
            >
              添加节点
            </el-button>
            <div slot="reference">
              <div class="treeItem">
                <div class="controlIcon">
                  <i class={classType(data.type)}></i>
                </div>

                <span class="item_name">
                  {data.name}
                </span>
              </div>
            </div>
          </el-popover>

        </div >
      )
    },

这里是我结合elementUI,并且对样式做出了一下改变的样子,
原始样子是:

renderContent (h, data) {
      return (
        <div>
          <div slot="reference">
              <div class="treeItem">
                <span class="item_name">
                  {data.name}
                </span>
              </div>
            </div>
        </div >
      )
    },

2.节点中的子节点展开控制
1.collapse —— 控制当前节点的子节点是否展开
2.onExpand —— 关闭当前节点展开函数,并且关闭其下所有子节点的展开
3.toggleExpand ——控制所有节点是否展开,两个参数分别指的是期望展开的数据,是否展开(true:false)

collapse (list) {
      list.forEach((child) => {
        if (child.expand) {
          child.expand = false;
        }
        child.children && this.collapse(child.children);
      });
    },
onExpand (e, data) {
   if ("expand" in data) {
     data.expand = !data.expand;
     this.changeRender()
     if (data.expand && data.children) {
       this.collapse(data.children);
     }
   } else {
     this.$set(data, "expand", true);
   }
 },
 toggleExpand (data, val) {
      if (Array.isArray(data)) {
        data.forEach((item) => {
          this.$set(item, "expand", val);
          if (item.children) {
            this.toggleExpand(item.children, val);
          }
        });
      } else {
        this.$set(data, "expand", val);
        if (data.children) {
          this.toggleExpand(data.children, val);
        }
      }
    },
  

我这里使用的开关控制全部展开和关闭
效果:
1.展开全部
在这里插入图片描述
2.关闭"张一_姐姐"的展开:
在这里插入图片描述
应该有人发现了,自带的这个开关,字数长度不确定的情况下,竖排展示是对不齐的,解决办法是固定宽度,但是我直接没用了,后期用再加上吧。

ps:总体开关控制展开按钮

按钮:
<el-switch v-model="expandAll" active-text="展开全部" inactive-text="关闭全部" @change="expandChange">
              </el-switch>
方法:
expandChange () {
      this.toggleExpand(this.treeData[0], this.expandAll)
    },

功能总览:

1.展示形式切换(横排/竖排)这里默认竖排
2.新增节点、删除节点、修改节点、查询节点
3.新建分支、选中分支
4.上传本地文本快速渲染分支


1.展示形式切换(横排/竖排)这里默认竖排

a.设置按钮

<el-switch v-model="horizontal" :disabled="!treeData.length" active-text="竖排" inactive-text="横排">
</el-switch>

b.绑定值

vue2-org-tree中 :horizontal="!horizontal"

效果:
竖排:
在这里插入图片描述
横排:
在这里插入图片描述

2.节点操作

2-1新增节点
在这里插入图片描述
逻辑:鼠标置上时候显示新增的按钮,点击后弹出输入框,可输入新增节点数据,点击保存即可在当时节点下新增节点
a:渲染时候给每个节点添加这个按钮和相应方法

renderContent (h, data) {
      return (
        <div>
          <el-popover placement="top-start" trigger="hover">
            <el-button size="mini" class="addBtn" icon="el-icon-circle-plus-outline"
              onClick={() => { this.openAddFrom(data) }}
            >
              添加节点
            </el-button>
            <div slot="reference">
              <div class="treeItem">
                <div class="controlIcon">
                  <i class={classType(data.type)}></i>
                </div>
                <span class="item_name">
                  {data.name}
                </span>
              </div>
            </div>
          </el-popover>
        </div >
      )
    },

点击添加后,可选择关系
在这里插入图片描述
添加后
在这里插入图片描述
添加节点时,首先获取当前节点的值,然后在其子集下添加新增的节点数据即可,但是要注意的是每个节点的id必须具有唯一性,不然会有很多问题:
核心代码:

   点击添加节点时,获取当前节点
 openAddFrom (data) {
      this.spouseArr = [];
      this.pitchSpouse = {
        pitchSpouseId: ''
      };
      this.dialogFormVisible = true;
      this.nowAddNode = data;
      this.addId = data.id;   //这个是主要的,当前节点id
    },
    根据id,获取当前节点的下标、所有父级、类型
    findNodeInTree (data, id, type, callback) {
      this.currentNodeMsg.type = type; //类型
      for (let i = 0; i < data.length; i++) {
        if (data[i].id == id) {
          return callback(data[i], i, data)
        }
        if (data[i].children) {
          let hasId = data[i].children.find((item) => {
            if (item.id == id) {
              this.currentNodeMsg.fatherArr = data[i]; //所有父级
            }
          })
          this.findNodeInTree(data[i].children, id, type, callback)
        }
      }
    },
	//调用
	this.findNodeInTree(this.treeData, this.addId, 'add', (item, index, arr) => {
        this.currentNodeMsg.idx = index;//下标
      })

新增节点时,我是调用自己写的方法,这里简化出来

// 判断是否是根节点 -- 旧版
        if (this.currentNodeMsg.fatherArr.hasOwnProperty("name")) {
           this.currentNodeMsg.fatherArr.children[this.currentNodeMsg.idx].children.push(this.addForm)
         } else {
           data[0].children.push(this.addForm)
        }
  -----------------------------
  data—— this.treeData;节点树数据
  this.addForm—— 当前新增的节点数据

这是简易版新增,只需要判断是否是对根节点进行添加即可,我修改后的是根据关系进行不同方式的添加,这里一般无需这么繁琐。

主要的核心问题在于变化后,你怎样去渲染节点树
解决办法:给vue2-org-tree添加绑定key值,更新数据后直接调用方法刷新key即可:

 :key="treeDataKey"
 // 重新渲染关系树
    changeRender () {
      this.treeDataKey += 1;
    },

还有一个问题是,我这是本地数据,只有数据地址一致的时候,修改后才能对原数据进行直接修改,不然会繁琐很多
最麻烦的添加数据写完了,核心代码就是findNodeInTree 这个方法,接下来节点的修改,删除就大家自己发挥。

2.2查询节点

// 搜索事件
    searchBtn () {
      //判断当前是否显示关系树
      if (this.treeData.length) {
        this.findName(this.treeData, this.searchNodeName);
        return
      }
      this.$message.warning("当前未显示分支图");
    },
    // 查找当前输入节点名称
    findName (data, name) {
      data.forEach((item, index) => {
        if (item.name == name) {
          this.searchNodeTree = []
          this.searchNodeTree.push(item)
          this.treeData = this.searchNodeTree;
          return
        }
        this.findName(item.children, name)
      })
    },

3.新建分支、选中分支

1.新建分支

在这里插入图片描述

// 新建节点树
    addNodeTree () {
      this.loading = true;
      // 判断当前分支是否存在
      let state = this.branchNameRepeat(this.branchMsg.label)
      if (state) {
        this.$message.warning("分支名不能重复")
        this.loading = false;
        return
      }
      this.branchMsg.value = randomId();
      // 赋予默认根节点
      this.branchMsg.data.push(
        {
          id: randomId(),
          name: '始先祖',
          sex: "1",
          type: "father",
          children: []
        }
      )
      this.centerDialogVisible = false;
      this.nodeTree.push(this.branchMsg)
      this.nodeTreeValue = this.branchMsg.value
      this.$message.success("新建成功!");
      this.loading = false;
      this.resetFormBtn();
    },
    ---------  watch中 -----------
    "nodeTreeValue": {
      handler (n, o) {
        //根据id获取当前分支
        if (n) {
          [this.treeData, this.beforeTreeData] = [this.nodeTree.find((item) => {
            return item.value == n
          }).data, this.nodeTree.find((item) => {
            return item.value == n
          }).data]
        }
      },
      deep: true
    },
	 // 判断节点名称是否重复
    branchNameRepeat (name) {
      let isRepeat = this.nodeTree.find((item) => {
        return item.label == name;
      })
      return isRepeat ? true : false
    },
    //随机Id
    const randomId = function () {
	  return (Math.random() + new Date().getTime()).toString(32).slice(0, 8);
	};

添加分支的逻辑:
1.nodeTree中以数组形式存放所有的分支总数,
2.根据添加的名称判断已有分支中是否重名,
3.符合条件后,设置初始数据,添加到nodeTree中,
4.拿到当前最新添加的树的id,
5.动态监听添加的关系树的id,发生变化时进行匹配,拿到相应的值,并且进行渲染,


nodeTree数据结构:
在这里插入图片描述
选择分支就是下拉形式获取 nodeTreeValue,nodeTreeValue改变时继续被监听,然后渲染对应的数据。

<el-select v-model="nodeTreeValue" placeholder="请选择分支">
        <el-option v-for="item in nodeTree" :key="item.value" :label="item.label" :value="item.value">
        </el-option>
</el-select>

4.上传本地文本快速渲染分支

上传按钮:

<el-upload class="upload-demo" action="" ref="upload" :on-change="handlePreview" :auto-upload="false" multiple :limit="1"
                :file-list="fileList">
                <el-button class="
el-icon-upload2" size="small" type="primary">点击上传</el-button>
                <div slot="tip" class="el-upload__tip">只能上传txt文件</div>
 </el-upload>

上传文件代码

// 上传文件
    handlePreview (file) {
      let that = this;
      this.$refs.upload.clearFiles();
      let fileName = file.name.split(".");
      // 读取文件名称
      if (fileName[1] != "txt") {
        this.$message.error("只能上传txt文件");
        return
      }
      // 文件名称-分支名称
      let branchName = fileName[0];
      let state = this.branchNameRepeat(branchName)
      if (state) {
        this.$message.warning("分支名不能重复")
        return
      }
      let reader = new FileReader();   //先new 一个读文件的对象 FileReader
      if (typeof FileReader === "undefined") {  //用来判断你的浏览器是否支持 FileReader
        this.$message({
          type: "info",
          message: "您的浏览器不支持文件读取。"
        });
        return;
      }
      reader.readAsArrayBuffer(file.raw); //读任意文件
      reader.onload = function (e) {
        var ints = new Uint8Array(e.target.result); //要使用读取的内容,所以将读取内容转化成Uint8Array
        // ints = ints.slice(0, 5000); //截取一段读取的内容
        let snippets = new TextDecoder('gb2312').decode(ints); //二进制缓存区内容转化成中文(即也就是读取到的内容)
        // 判断是否能过解析为数组
        try {
          let nowSniData = JSON.parse(snippets)
          that.branchMsg.value = randomId();
          that.branchMsg.label = branchName;
          // 赋予默认根节点
          that.branchMsg.data = nowSniData;
          that.nodeTree.push(that.branchMsg)
          that.nodeTreeValue = that.branchMsg.value
          that.$message.success("上传成功");
          that.resetFormBtn()
        } catch {
          that.$message.error("转换失败,请注意内容是否为JSON格式");
        }

      };
    },

上传逻辑:
1.txt中编辑好数据,点击按钮进行上传,
2.使用try catch判断txt文件内容是否符合数据格式,
3.符合条件后获取文本名称,判断名称是否与已存在的分支名重复,
4.符合条件后将赋予id,设置当前分支名称为文本名称,
5.追加到节点树数组中,渲染最新添加的数据

演示:
文本:
在这里插入图片描述
选中文本
在这里插入图片描述
渲染
在这里插入图片描述

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 30
    评论
Vue2-org-tree实现节点的懒加载可以通过以下步骤完成: 1. 首先,你需要在组件中定义一个属性来表示节点是否已经加载。可以在节点对象中添加一个属性,例如 `loaded`,用于标记节点是否已经加载。 2. 接下来,在组件中创建一个方法,例如 `loadChildNodes`,用于加载子节点的数据。这个方法可以通过异步请求获取数据,并将获取到的数据添加到对应的节点中。 3. 在模板中,你需要根据节点的状态来渲染 UI。你可以使用 `v-if` 或者 `v-show` 根据节点的 `loaded` 属性来控制子节点是否显示。 4. 当用户展开一个节点时,你可以监听相应的事件(例如 `click` 或者 `expand`),在事件处理函数中调用 `loadChildNodes` 方法来加载子节点数据并更新节点的状态。 下面是一个简单的示例代码: ```vue <template> <div> <ul> <li v-for="node in treeData" :key="node.id"> <span @click="toggleNode(node)">{{ node.name }}</span> <ul v-if="node.expanded && !node.loaded"> <li v-for="childNode in node.children" :key="childNode.id"> <span>{{ childNode.name }}</span> </li> </ul> </li> </ul> </div> </template> <script> export default { data() { return { treeData: [ { id: 1, name: 'Node 1', expanded: false, loaded: false, children: [] }, // Other nodes... ] }; }, methods: { toggleNode(node) { node.expanded = !node.expanded; if (node.expanded && !node.loaded) { this.loadChildNodes(node); } }, loadChildNodes(node) { // Simulate an async request to load child nodes data setTimeout(() => { node.children = [ { id: 2, name: 'Child Node 1' }, { id: 3, name: 'Child Node 2' }, // Other child nodes... ]; node.loaded = true; }, 1000); } } }; </script> ``` 在上面的代码中,`treeData` 数组表示树的数据结构,每个节点对象都包含一个 `loaded` 属性用于标记节点是否已经加载,以及一个 `children` 属性用于存储子节点数据。`toggleNode` 方法用于切换节点的展开状态,当节点展开时,会调用 `loadChildNodes` 方法来加载子节点数据,并在加载完成后更新节点的状态。 请注意,上述代码只是一个简单示例,你可以根据自己的需求进行相应的修改和扩展。
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值