Element-ui树形控件el-tree⾃定义增删改和局部刷新及懒加载操作需求: vue-cli项⽬树形控件:⼀级节点为本地节点,默认展开⼀级节点,增删改后局部刷新数据。
增加节点,点击确定后局部刷新,渲染新数据。
源码
element组件样式
<el-tree
class="treeitems"
:data="data"
node-key="id"
:props="defaultProps"
:load="loadNode"
lazy
:default-expanded-keys="[0]"
@node-click="handleNodeClick"
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
@node-drop="handleDrop"
ref="tree"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
<span>
<i @click="() => append(node,data)" class="el-icon-plus"></i><!--增加分组-->
<!-- 根节点不需要删除和重命名 -->
<i v-if="data.id !== 0" @click="() => deletes(node,data)" class="el-icon-delete"></i><!--删除分组-->
<i v-if="data.id !== 0" @click="() => rename(node,data)" class="el-icon-edit"></i><!--重命名分组-->
</span>
</span>
</el-tree>
data数据
data() {
return {
filterText: '',
data: [{
id:0,
label: '中国',
}],
children: [{
id:1,
label: '北京',
children: [{
id:11,
label: '通州'
}]
},
{
id:2,
label: '上海',
leaf: true,
},
{
id:3,
label: '⼭西',
children:[{
id: 13,
label: '太原'
},{
id: 14,
label: '阳泉'
}]
},{
id:4,
label: '⿊龙江',
children: [{
id:12,
label: '哈尔滨'
}]
}],
defaultProps: {
children: 'children',
label: 'label',
isLeaf: 'leaf'
}
};
点击增加节点弹出弹窗
这⾥也⽤的是element的弹窗,直接在methods⾥写:
//点重命名事件
append(node,data) {
console.log(node,data,'增加')
this.$prompt('节点名字', '增加节点', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^[\u4e00-\u9fa5]{0,}$/,//匹配全中⽂
inputErrorMessage: '请输⼊中⽂'//不符合正则匹配的提⽰语句
}).then(({ value }) => {
//可以在这⾥发请求,http是我模拟的⼀个虚假的封装好的axios请求,()可写请求参数
http().then((data)=>{
this.$message({
type: 'success',
message: '修改成功'
});
//请求成功需局部刷新该节点,调⽤⽅法,把节点信息node传⼊
this.partialRefresh(node)
})
//请求失败
.catch(()=>{
this.$message({
type: 'info',
message: '修改失败'
});
})
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
});
});
},
//实现局部刷新,在点击弹窗处调⽤的
partialRefreshpartialRefresh(node){
nodeselector
//设置loaded为false;模拟⼀次节点展开事件,加载重命名后的新数据;
node.loaded = false;
//新建⼦节点是刷新⼀次本节点的展开请求,⽽重命名和删除则需要刷新⽗级节点的的展开事件,
//可以设置node.parent.loaded = false;pand();
},
懒加载
该处可直接设置有⽆节点,如果后端返回有⽆节点的表⽰就可⽤,若⽆可舍弃。(data中写,我在本地模拟数据上海字段中加了leaf: true,上海节点就默认没有节点了)
//懒加载
loadNode(node, resolve){
if (node.level === 0) {
//本地的数据,⼀个承载中国字样的数组;
return resolve(this.data);
}
else if(node.level === 1){
//现在为模拟数据;
//有真实数据,在resolve中放置请求回来的数据即可。
//else同样也是。我这⾥是区分了两种请求⽅式。
return resolve(this.children)
}
else{
return resolve([])
}
},
拖拽节点
//拖拽==>拖拽时判定⽬标节点能否被放置
allowDrop(draggingNode, dropNode, type){
//参数:被拖拽节点,要拖拽到的位置
//因为根⽬录是我本地写死的,不能有同级,所以我设置凡是拖拽到的level===1都存放到根节点的下⾯;
if(dropNode.level===1){
return type == 'inner';
}
else {
return true;
}
},
//拖拽==>判断节点能否被拖拽
allowDrag(draggingNode){
//第⼀级节点不允许拖拽
return draggingNode.level !== 1;
},
需求改了,同级节点拖拽,拖拽完成后将排序结果返回后端:
/
/拖拽==>拖拽时判定⽬标节点能否被放置
//后更改为只能同级拖拽
allowDrop(draggingNode, dropNode, type) {
if (draggingNode.level === dropNode.level) {
if (draggingNode.data.parentId === dropNode.data.parentId) {
return type === 'prev' || type === 'next'
}
} else {
// 不同级不允许拖拽
return false
}
},
//拖拽==>判断节点能否被拖拽
allowDrag(draggingNode) {
return draggingNode.level !== 1;
},
//拖拽成功完成时触发的事件,在这⾥可以将节点拖拽后的顺序返给后端
handleDrop(node,data,type,event){
let arr=[];
//data为拖拽后节点信息,到它的⽗级,在从⽗级⼦集
let child = data.parent.childNodes;
for(var key in child){
arr.push({id:child[key].data.id})
}
//转为JSON字符串发请求带⾛信息
idSort(JSON.stringify(arr))
}
补充知识:vue+element tree ----增加修改删除上下移动
<template>
<div>
<div class="exam_structure">
<el-input
placeholder="输⼊关键字进⾏过滤"
v-model="filterText">
</el-input>
<el-button type="primary" size="small" class="add_new_question" @click="add_new_question"><i></i>添加⽗节点</el-button>  </div>
<div class="question_info_lists">
<el-tree ref="tree" :key="tree_key" :data="treeData" node-key="id" :render-content="renderContent"
:expand-on-click-node="false" :default-expanded-keys="defaultExpand" show-checkbox
:filter-node-method="filterNode"></el-tree>
<el-row class="add_question" v-show="add_question_flag">
<el-col :span="12">
<el-input v-model="new_question_name" placeholder="请输⼊⼤题名称"></el-input>
</el-col>
<el-col :span="12">
<el-button size="mini" class="btn_sure" @click.stop="add_question_sure">确定</el-button>
<el-button size="mini" class="btn_cancel" @click.stop="add_question_cancel">取消</el-button>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
export default {
name: "tree1",
watch: {
filterText(val) {
this.$filter(val);
}
},
methods: {
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
// 添加新⼤题
add_new_question() {
this.add_question_flag = true
},
add_question_sure() {  //确定
const nodeObj = {id: '', label: w_question_name, isEdit: false, children: []}
this.add_question_flag = false
},
add_question_cancel() {  //取消
this.add_question_flag = false
},
//  增加
append(store, node, data) {
var maxid = '20'
//新增数据
const nodeapp = {id: ++maxid, label: '增加节点', isEdit: false, children: []}
data.children.push(nodeapp)
if (!panded) {
}
const parent = node.parent
const children = parent.data
const cIndex = children.findIndex(d => d.id === data.id)
const tempChildrenNodex2 = children[cIndex] //拿到被添加的上⼀级
console.log(tempChildrenNodex2.children[cIndex - 1])
},
// 修改
nodeEdit(ev, store, data) {
data.isEdit = true
this.$nextTick(() => {
const $input = ev.target.parentNode.parentNode.querySelector('input') || ev.target.parentElement.parentElement.querySelector('input')      !$input ? '' : $input.focus()
})
},
edit_sure(ev, data) {
const $input = ev.target.parentNode.parentNode.querySelector('input') || ev.target.parentElement.parentElement.querySelector('input')    if (!$input) {
return false
} else {
data.label = $input.value
data.isEdit = false
}
},
// 节点删除
nodeDelete(node, data) {
const parent = node.parent
const children = parent.data.children || parent.data
const index = children.findIndex(d => d.id === data.id)
children.splice(index, 1)
},
// 节点上移
nodeUp(node, data) {
const parent = node.parent
const children = parent.data.children || parent.data
const cIndex = children.findIndex(d => d.id === data.id)
if (parent.level === 0 && cIndex === 0) {
return
} else if (parent.level !== 0 && cIndex === 0) { //不同⽗节点中移动
alert('不同⽗节点中移动')
// const parent2 = parent.parent
// const children2 = parent2.data.children || parent2.data
// const pIndex2 = parseInt(children2.findIndex(p => p.id === parent.data.id), 10) - 1
// if (pIndex2 < 0) return
// children2[pIndex2].children.push(data)
// children.splice(cIndex, 1)
// this.defaultExpand[0] = children2[pIndex2].id
} else if ((parent.level === 0 && cIndex !== 0) || (parent.level !== 0 && cIndex !== 0)) {
const tempChildrenNodex1 = children[cIndex - 1]
const tempChildrenNodex2 = children[cIndex]
this.$set(children, cIndex - 1, tempChildrenNodex2)
this.$set(children, cIndex, tempChildrenNodex1)
this.defaultExpand[0] = data.id
}
<_key++
},
// 节点下移
nodeDown(store, node, data) {
const parent = node.parent
const children = parent.data.children || parent.data
const cIndex = children.findIndex(d => d.id === data.id)
const cLength = children.length - 1 // 最边上的节点
const allLevel = store.data.length - 1 // 树的深度
if (parent.level === allLevel && cIndex === cLength) { // 最最末的节点
return
} else if (parent.level !== allLevel && cIndex === cLength) { //⽗节点不同
alert('不能移动')
// const parent2 = parent.parent
// const children2 = parent2.data.children || parent2.data
// const pIndex2 = parseInt((children2.findIndex(p => p.id === parent.data.id)), 10)
/
/ if (pIndex2 === allLevel) return
// children2[pIndex2 + 1].children.push(data)
// children.splice(cIndex, 1)
// this.defaultExpand[0] = children2[pIndex2 + 1].id
} else if ((parent.level === allLevel && cIndex !== cLength) || (parent.level !== allLevel && cIndex !== cLength)) { // ⽗节点相同    const tempChildrenNodex1 = children[cIndex + 1]
const tempChildrenNodex2 = children[cIndex]
this.$set(children, cIndex + 1, tempChildrenNodex2)
this.$set(children, cIndex, tempChildrenNodex1)
this.defaultExpand[0] = data.id
}
<_key++
},
showOrEdit(data) {
if (data.isEdit) {
return <input type="text" value={data.label} on-blur={ev => this.edit_sure(ev, data)}/>
} else {
return <span className="node_labe">{data.label}</span>
}
},
// 结构树操作group node,
renderContent(h, {node, data, store}) {
return (
<span>
<span class="el-icon-document">
{this.showOrEdit(data)}
</span>
<div class="tree_node_op" >
<i class="el-icon-edit" on-click={(ev) => deEdit(ev, store, data)}></i>
<i class="el-icon-delete" on-click={() => deDelete(node, data)}></i>
<i class="el-icon-upload2" on-click={() => deUp(node, data)}></i>
<i class="el-icon-download" on-click={() => deDown(store, node, data)}></i>
<i class="el-icon-plus" on-click={() => this.append(store, node, data)}></i>
</div>
</span>)
}
}
, data() {
return {
filterText: '',