vue 实现树状穿梭框

最近需求遇到了树状穿梭框 查到了el-tree-transfer 但是穿梭到右边需要变成扁平化数据 需要修改源码 修改无效 遂自己封装

tree-transfer.vue

<template>
  <!-- 自定义树形穿梭框组件、理论上左右均可选择是否为树形结构,目前固定为左侧树形、右侧无层级结构 -->
  <div class="tree-transfer">
    <!-- 穿梭框左侧 -->
    <div class="tree-transfer-left">
      <!-- 左侧采用element-ui的el-tree -->
      <el-tree
        ref="treeLeft"
        :data="dataLeft"
        show-checkbox
        node-key="id"
        default-expand-all
        :props="defaultProps"
      >
      </el-tree>
    </div>
    <!-- 穿梭框中间按钮区域 -->
    <div class="tree-transfer-middle">
      <el-button
        circle
        type="info"
        icon="el-icon-arrow-left"
        @click="remove"
      ></el-button>
      <el-button
        circle
        type="info"
        icon="el-icon-arrow-right"
        @click="add"
      ></el-button>
    </div>
    <!-- 穿梭框右侧 -->
    <div class="tree-transfer-right">
      <!-- 右侧直接放置结果 -->
      <!-- 这里也采用tree结构,默认是对数据进行处理使得没有树形结构,也可以选择有树形结构 -->
      <el-tree
        ref="treeRight"
        :data="dataRight"
        show-checkbox
        node-key="id"
        @check-change="rightChange"
        :props="defaultProps"
      >
      </el-tree>
    </div>
  </div>
</template>

<script>
export default {
  props: ['datas', 'defaultProps'],
  data() {
    return {
      yuansiData: [],
      dataLeft: [],
      dataRight: [],
    };
  },
  mounted() {
    this.dataLeft = this.datas;
    this.yuansiData = JSON.parse(JSON.stringify(this.datas));
  },
  methods: {
    add() {
      // 定义一个递归过滤的方法,用来删掉父级中给的元素
      // 获取所有选中的项并且去掉父级
      let list = this.$refs.treeLeft.getCheckedNodes();
      //循环删除 左侧的节点
      list.forEach((item) => {
        this.deleteNode(this.dataLeft, item.id);
        delete item.children;
      });
      this.$refs.treeLeft.setCheckedNodes([]);
      // 将选择的项添加到右侧
      this.dataRight.push(...list);
    },
    // 从右侧移除时的方法
    remove() {
      // 1.获取右侧选中的节点
      let list = this.$refs.treeRight.getCheckedNodes();
      if (list.length === 0) return;
      const res = this.treeToList(this.dataLeft);
      res.push(...list);
      const resArr = this.listToTree(res, 0, []);
      this.dataLeft = resArr;
      // 2.删除右侧的节点
      //深克隆一份 数据
      let rightlist = JSON.parse(JSON.stringify(this.dataRight));
      let resarr = [];
      //删除 children
      rightlist.forEach((item) => {
        delete item.children;
        const index = list.findIndex((val) => val.id === item.id);
        if (index === -1) {
          resarr.push(item);
        }
      });
      this.dataRight = resarr;
    },
    getResult() {
      return this.dataRight;
    },
    rightChange(data) {
      const list = this.$refs.treeRight
        .getCheckedNodes()
        .map((item) => item.id);
      this.dataRight.forEach((item) => {
        if (item.id === data.pid) {
          list.push(item.id);
        }
      });
      this.$refs.treeRight.setCheckedKeys(list);
    },
    //递归删除节点
    deleteNode(arr, targetId) {
      for (let i = 0; i < arr.length; i++) {
        const node = arr[i];
        if (node.id === targetId) {
          arr.splice(i, 1);
          return;
        }
        // 判断children存在并且有数据
        if (Array.isArray(node.children) && node.children.length) {
          return this.deleteNode(node.children, targetId);
        }
      }
    },
    //扁平化数组转 树状
    listToTree(list, pid, data) {
      //获取所有一级
      for (let item of list) {
        if (item.pid == pid) {
          data.push(item);
        }
      }
      //获取子级
      for (let i of data) {
        i.children = [];
        this.listToTree(list, i.id, i.children); //递归调用
        if (i.children.length == 0) {
          delete i.children;
        }
      }

      return data;
    },
    //树状 转 扁平化
    treeToList(arr) {
      var newArr = []; //申请新数组
      for (var i = 0; i < arr.length; i++) {
        if (arr[i].children) {
          //childrens存在
          newArr.push(...this.treeToList(arr[i].children));
          //递归,调用flat方法,并将返回结果push到新数组
          delete arr[i].children; //删除原有的childrens属性
        }
        newArr.push({ ...arr[i] }); //三个点,展开对象,push新对象
      }
      return newArr; //返回数组
    },
  },
};
</script>

<style scoped>
.tree-transfer {
  display: flex;
  min-height: 250px;
}
.tree-transfer-left {
  min-width: 200px;
  border: 1px #e5e5e5 solid;
  border-radius: 10px;
  padding: 10px;
}
.tree-transfer-middle {
  display: flex;
  justify-content: center;
  align-items: center;
  min-width: 120px;
}
.tree-transfer-right {
  min-width: 200px;
  border: 1px #e5e5e5 solid;
  border-radius: 10px;
  padding: 10px;
}
</style>

使用的地方

<template>
  <div>
    <treeTransfer
      ref="treeTransfer"
      :datas="data"
      :defaultProps="defaultProps"
    ></treeTransfer>
  </div>
</template>

<script>
import treeTransfer from './components/tree-transfer.vue';
export default {
  components: { treeTransfer },
  data() {
    return {
      data: [
        {
          id: '1',
          pid: 0,
          label: '一级 1',
          children: [
            {
              id: '1-1',
              pid: '1',
              label: '二级 1-1',
              children: [],
            },
            {
              id: '1-2',
              pid: '1',
              label: '二级 1-2',
              children: [
                {
                  id: '1-2-1',
                  pid: '1-2',
                  children: [],
                  label: '二级 1-2-1',
                },
                {
                  id: '1-2-2',
                  pid: '1-2',
                  children: [
                    {
                      id: '1-3-1',
                      pid: '1-2-2',
                      children: [],
                      label: '三级 1-3-1',
                    },
                    {
                      id: '1-3-2',
                      pid: '1-2-2',
                      children: [],
                      label: '三级 1-3-2',
                    },
                  ],
                  label: '二级 1-2-2',
                },
              ],
            },
          ],
        },
      ],
      defaultProps: {
        children: 'children',
        label: 'label',
      },
    };
  },
};
</script>

<style></style>

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值