基于vue,一个简单实用的树形穿梭框

前言

做者在作一个后台管理项目时,有遇到过一个需求, 要求实用穿梭框,实现树形结构上的数据操做 组件有引用网上的树形框,在此基础上对其进行了一些数据优化.node

树形穿梭框组件代码

树形穿梭框组件
  /*
*使用前提
第一步:安装组件
npm install el-tree-transfer --save
第二部:引入
import treeTransfer from 'el-tree-transfer'
*实现原理
樹形穿梭框,第一層的pid按index從0開始 ,依次1,2,3,4....便可, 核心在與子級中的pid的值為其父級id的值
後台請求數據只需保證id不重複,以及有用於顯示對應項名稱的label便可, 此組件內已使用遞歸為每一個層級的元素添加pid,
不须要後台傳入用於識別樹形的id或字段.

*使用声明
引用組件 傳入 options集合  options集合中,包含 title:字符串數組,長度為2,  用於設置兩個穿梭框的標題文本,

mode為字符串, 用於設置穿梭框的模式, 不傳默認為'tansfer'樹形結構, 'addressList' 為通訊錄模式,

暫不支持通訊錄模式, 因為還沒有對數據進行處理及断定

傳入API, 做为请求数据源信息的地址,返回参数须要有 children label属性

設置@add函數 接收向右添加選擇數據的結果, 返回四個參數,(fromData, toData, obj, arr)  , fromData為移動後數據源列表數據,
toData為移動後數據添加的目標列表的數據, obj為移动的节点keys、nodes、halfKeys、halfNodes对象. arr为移动后右侧数据添加
的目标列表的最终子节点的数组,(也就是说,该数组中的元素是没有children节点的子元素,其没有children属性)

设置@remove函数,接收数据添加的目标列表移除的数据,参数同@add

*其余功能: defaultCheckedKeys 属性对应默认选中,传入Data集合数组,做为已选择数组项,集合数组中必须有id属性,
对应fromData中的最终子元素节点的id,便可..,
*其余功能:defaultTransfer, 默认选中的穿梭一次.配合defaultCheckedKeys使用.
*/
<template>
<div>
  <tree-transfer :title="options.title" :from_data="fromData" :to_data="toData"
                 v-if="showT"
                 :defaultTransfer="TransGet"
                 :defaultProps="{label:'label'}"
                 :defaultCheckedKeys="editData"
                 @addBtn="add" @removeBtn="remove" :mode="options.mode" height='540px' filter openAll>
  </tree-transfer>
</div>
</template>

<script>
import treeTransfer from 'el-tree-transfer'

export default {
components: {treeTransfer},
name: 'transfer-tree',
props: {
  options: Object,
  API: String,
  Data: Array,
  deT: Boolean
},
data () {
  return {
    TransGet: false, // 当页面加载完成后,再将该值变为true,进行默认穿梭.
    showT: false,
    toData: [],
    fromData: [],
    editData: this.Data || []
    // checkedList: [  // 测试defaultCheckedKeys原理
    //   {
    //     id: 39,
    //     label: '12we'
    //   },
    //   {
    //     id: 40,
    //     label: '12we'
    //   },
    //   {
    //     id: 41,
    //     label: '12we'
    //   }
    // ]
  }
},
methods: {
  // 监听穿梭框组件添加
  add (fromData, toData, obj) {
    // 树形穿梭框模式transfer时,返回参数为左侧树移动后数据、右侧树移动后数据、移动的{keys,nodes,halfKeys,halfNodes}对象
    // 通信录模式addressList时,返回参数为右侧收件人列表、右侧抄送人列表、右侧密送人列表
    let arr = this.getTbs(toData)
    this.$emit('add', fromData, toData, obj, arr)
  },
  // 监听穿梭框组件移除
  remove (fromData, toData, obj) {
    // 树形穿梭框模式transfer时,返回参数为左侧树移动后数据、右侧树移动后数据、移动的{keys,nodes,halfKeys,halfNodes}对象
    // 通信录模式addressList时,返回参数为右侧收件人列表、右侧抄送人列表、右侧密送人列表
    let arr = this.getTbs(toData)
    this.$emit('remove', fromData, toData, obj, arr)
  },
  // 使用递归为请求到的后台数据的每一项添加一个对应树形结构的pid
  setPid (item) {
    let that = this
    if (item.children) {
      item.children.map(e => {
        // 子级中的pid等于其父级中的id
        e['pid'] = item.id
        that.setPid(e)
      })
    } else {
      return false
    }
  },
  // 使用递归,设置全部id,保留最底层id不变
  setId (item) {
    let that = this
    if (item.children) {
      item.children.map((e, index) => {
        // 子级中的pid等于其父级中的id
        e.id = item.id + '-' + e.id
        that.setId(e)
      })
    } else {
      item.id = item.id.split('-')[item.id.split('-').length - 1]
      return false
    }
  },
  // 使用递归查找最终子元素
  getTarget (item, arr) {
    let that = this
    if (item.children) {
      item.children.map(e => {
        that.getTarget(e, arr)
      })
    } else {
      arr.push(item)
    }
  },
  // 使用递归,获取到每一次添加或移除后toData中的最终子元素(没有children的元素)的集合
  getTbs (toData) {
    let arr = []
    toData.map(item => {
      if (item.children) {
        this.getTarget(item, arr)
      } else {
        return false
      }
    })
    return arr
  }
  // // 使用递归获取对应fromData中的元素.并为元素添加选中状态[研究defaultCheckedKeys原理实现]
  // getTree (item, id) {
  //   let that = this
  //   if (item.id !== id) {
  //     if (item.children) {
  //       item.children.map(e => {
  //         that.getTree(e, id)
  //       })
  //     } else {
  //       return false
  //     }
  //   } else {
  //     // 把找到的元素的选项变为已选择,
  //     item.indeterminate = true
  //     console.log(item)
  //   }
  // },
},
mounted () {
  // 因为在created阶段改变了数据,致使在有editData且ID有重复时,对数据进行处理后,树形穿梭框没法 获取到正确的已选择列表,
  // 因此使用延时,让整个树形框在数据处理完成后进行显示.
  let that = this
  setTimeout(function () {
    that.showT = true
    that.TransGet = true
  }, 500)// 如依旧没法显示,可将延时时间加长,此处为当前使用测试最短期.
  // this.$nextTick(() => { // 使用没法达到效果, 数据更新先后.
  //   that.showT = true
  // })
},
async created () {
  if (this.options.mode === 'addressList') {
    this.$message('暫時未對通訊錄模式數據進行處理')
  } else {
    // 当为树形模式下
    let res = await this.$axios.get(this.API)
    let a = []
    // 判断数据源res.data是否为数组,不为数组则要求其children属性为数组
    if (!Array.isArray(res.data)) {
      a = res.data.children
    } else {
      a = res.data
    }
    // 从新定义全部层级id,保证树形层级之间id都不重复
    a.map((item, index) => {
      item.id = index + 1 + '-' + item.id
      this.setId(item)
    })
    // 将全部pid使用递归进行重定义,为其父级id
    a.map((item, index) => {
      item['pid'] = 0
      this.setPid(item)
    })
    this.fromData = a
    // 当已选部分集合时,editData数组长度不为0
    if (this.editData.length !== 0) {
      // 当为修改界面展现时,经过editData中数组的各项id肯定toData中的数据结构.
      // 若是Data数组为id元素,则不做处理, 若是为集合元素,则进行提取id处理
      if (this.Data[0].id) {
        let arr = []
        this.Data.forEach(item => arr.push(item.id))
        this.editData = arr
      }
      // 测试defaultCheckedKeys原理
      // if (this.checkedList.length !== 0) {
      //   this.checkedList.forEach(item => {
      //     this.fromData.map(e => {
      //       this.getTree(e, item.id)
      //     })
      //   })
      //   console.log(this.fromData)
      // }
    }
  }
}
}
</script>

<style scoped>

</style>

  
复制代码
操做实用组件
<template>
<div>
  <bread-crumb :items="items"></bread-crumb>
  <transfer-tree
    :options="options"
    API="/sys/brands/1/dealers/tree"
    @add="add"
    @remove="remove"
  ></transfer-tree>
</div>
</template>

<script>
import transferTree from '../../common/tranfer-tree'
export default {
components: {transferTree},
name: 'dashBoard',
data () {
  return {
    items: [
      {
        name: 'dashBoard'
      }
    ],
    options: {
      title: [
        '选择车型',
        '已选车型'
      ]
    },
    Data: []
  }
},
methods: {
  add (fromData, toData, obj, arr) {
  },
  remove (fromData, toData, obj, arr) {
  }
}
}
</script>

<style scoped>

</style>

复制代码

相关文章
相关标签/搜索