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