vue使用element实现简单的tree穿梭框

634 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第十二天,点击查看活动详情

题引:

公司的美工UI又来找事了,说好的按照element-ui参考画图,结果来了个tree穿梭框,好家伙,上网查了一下有个比较成熟的插件:element-tree-transfer-pro,但是安装的时候报错了,想了想还得去解决,而且需求也不是很难,那就把element-ui的el-tree组件和el-checkbox结合实现一个好了。

下图是效果:

image.png

正文:

思路:

首先说一下需求在分析思路。需求需要左边的是一个tree,选中的节点授权时需要移除自身且添加到右边,反之右边选中移除时也是一样。

布局:

通过效果图,我们直接使用el-dialog作为一个弹框载体,左边为一个el-tree,中间部分使用简单的div包裹el-button,右边是列表展示数据+el-checkbox

代码如下:

<el-dialog class="flowAddDialog" :visible.sync="flowAddDialogVisible" title="选择人员" center width="40%" :before-close="handleClose('flowAddDialogVisible')">
    <div class="transfer">
        <div class="transfer-left transfer-flex">
          <div>
            <div>
              <el-checkbox v-model="checked"></el-checkbox>
              <span>全部老师</span>
            </div>
            <span style="color:#c0c4cc">0/98</span>
          </div>
          <el-input v-model="inputValue" suffix-icon="el-icon-search" placeholder="请按学校或运行商搜索"></el-input>
          <el-tree ref="leftTree" :data="treeData" :props="defaultProps" default-expand-all show-checkbox @check="handleLeftTreeClick"></el-tree>
        </div>
        <div class="transfer-center">
          <el-button type="primary" style="margin-bottom: 20px;" @click="handleAddPower">授权老师》</el-button>
          <el-button type="primary" style="margin-left:0" @click="handleRemovePower">《移除老师</el-button>
        </div>
        <div class="transfer-right transfer-flex">
          <div>
            <div>
              <el-checkbox v-model="checked"></el-checkbox>
              <span>已选老师</span>
            </div>
            <span style="color:#c0c4cc">0/98</span>
          </div>
          <el-input v-model="inputValue" suffix-icon="el-icon-search" placeholder="请按学校或运行商搜索"></el-input>
          <el-tree ref="rightTree" :data="treeCheckData" :props="{ label: 'label' }" show-checkbox @check="handleRightTreeClick"></el-tree>
        </div>
    </div>
    <span slot="footer" class="dialog-footer">
        <el-button icon="el-icon-folder-remove" type="primary" @click="handleFlowSave">保 存</el-button>
        <el-button @click="handleFlowCancel">取 消</el-button>
    </span>
</el-dialog>

<script>
     export default{
         data(){
            flowAddDialogVisible: false,
            treeData: [
                {
                label: "校长室",
                id: 1,
                children: [
                    { id: 11, label: "李晓霞1" },
                    { id: 12, label: "李晓霞2" },
                    { id: 13, label: "李晓霞3" },
                    { id: 14, label: "李晓霞4" },
                    ]
                },
                {
                id: 2, label: "总务处",
                children: [
                    { id: 21, label: "武清区1" },
                    { id: 22, label: "武清区2" },
                    { id: 23, label: "武清区3" },
                    { id: 24, label: "武清区4" },
                    ]
                },
                {
                id: 3, label: "世界杯",
                children: [
                    { id: 31, label: "葡萄牙比分 3-1" },
                    { id: 32, label: "巴西比分 3-2" },
                    { id: 33, label: "法国比分 3-3" },
                    ]
                }
            ],
            defaultProps: {
            children: 'children',
            label: 'label'
            },
            treeCheckData: [],
            leftCheckNode: [],
            rightCheckNode: [],
            checked: true   
        }       
     }
</script>

<style lang="less" scoped>
.flowAddDialog {
    .transfer {
      display: flex;
      justify-content: stretch;
      align-items: center;
    }

    .transfer-flex{
      flex: 1;
      border: 1px solid #ccc;
      height: 500px;
      &>div:nth-child(1) {
        display: flex;
        justify-content: space-between;
        padding: 0 10px;
        height: 35px;
        line-height: 35px;
        font-size: 14px;
        background-color: #f7f8fa;
      }
    }
    .transfer-left {
    }
    .transfer-center {
      width: 140px;
      text-align: center;
    }
    .transfer-right {
    }
}
</style>

基本的布局就是这样。

逻辑:

当我们授权用户时,需要把左边tree选中的过滤掉赋值给右边tree,然后重新渲染,那么我们就需要有两个变量,一个变量来保存最初的数据,一个是当前渲染的数据变量。

mounted() {
    this._treeData = this.$options.data().treeData;
    this._treeCheckData = this.$options.data().treeCheckData;
},

紧接着,我们需要拿到选中的节点,只需要通过el-treecheck方法即可

handleLeftTreeClick(data, node) {
    this.leftCheckNode = node.checkedNodes
},
handleRightTreeClick(data, node) {
    this.rightCheckNode = node.checkedNodes
},

拿到对应的节点之后,我们就需要进行一个逻辑判断

  1. 左tree:
    当我们点击授权时,我们需要拷贝一个对象,通过forEach+filter的方法,过滤没有选中的节点重新赋值给node.children,这样就可以移除左边选中的节点,且同时加入右tree
     handleAddPower() {
      let leftTemp = Object.assign([], this.treeData) || [];
      let rightTemp = Object.assign([], this.treeCheckData) || [];
      if (this.leftCheckNode.length > 0) {
        this.leftCheckNode && this.leftCheckNode.forEach(item => {
          if (!item.children) {
            // 去除左边框
            leftTemp.forEach(node => {
              node.children = node.children.filter(children => children.id !== item.id)
            })
            // 赋值给右边框
            rightTemp.push(item)
          }
        })
        this.treeCheckData = rightTemp;
        this.treeData = leftTemp;
        this.leftCheckNode = [];
      }
    },    
    
  2. 右ree:
    当我们点击移除时,我们可以先进行边缘判断:
    1. 是移除全部则直接重置初始化数据
    2. 不是的话还是按照forEach+filter方法,右tree的数据过滤需要移除的节点,把返回的数组(即未选中)重新赋值给右tree变量,同时左tree变量只需要使用初始化数据,过滤掉右tree的数据即可
     handleRemovePower() {
      if (this.rightCheckNode.length == this.treeCheckData.length) {
        this.reset()
      } else if (this.rightCheckNode.length > 0) {
        let leftTemp = Object.assign([], this._treeData) || [];
        let unCheck = this.treeCheckData.filter(item => {
          let isExits = false;
          this.rightCheckNode.forEach(_item => {
            if (_item.id == item.id) isExits = true;
          })
          if (!isExits) {
            return item;
          }
        })
        unCheck.forEach(item => {
          leftTemp.forEach(node => {
            node.children = node.children.filter((_item, index) => {
              return _item.id !== item.id
            })
          })
        })
        this.treeData = leftTemp;
        this.treeCheckData = unCheck;
        this.rightCheckNode = [];
      }
    },
    reset() {
      this.treeData = this._treeData;
      this.treeCheckData = this._treeCheckData;
    }
    

结尾:

以上就是一个简单的demo例子。当然里面还有很多优化的地方和实现的功能,但能够完成需求就好了^_^。