做者在作一個後臺管理項目時,有遇到過一個需求, 要求實用穿梭框,實現樹形結構上的數據操做 組件有引用網上的樹形框,在此基礎上對其進行了一些數據優化.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>
複製代碼