需求: 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){ //设置loaded为false;模拟一次节点展开事件,加载重命名后的新数据; node.loaded = false; node.expand(); //新建子节点是刷新一次本节点的展开请求,而重命名和删除则需要刷新父级节点的的展开事件, //可以设置node.parent.loaded = false;node.parent.expand(); },
懒加载
该处可直接设置有无节点,如果后端返回有无节点的表示就可用,若无可舍弃。(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.$refs.tree.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: this.new_question_name, isEdit: false, children: []} this.treeData.push(nodeObj) this.add_question_flag = false }, add_question_cancel() { //取消 this.add_question_flag = false this.new_question_name = '' }, // 增加 append(store, node, data) { var maxid = '20' //新增数据 const nodeapp = {id: ++maxid, label: '增加节点', isEdit: false, children: []} data.children.push(nodeapp) if (!node.expanded) { node.expanded = true } 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 } this.tree_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 } this.tree_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" style=" float: right"> <i class="el-icon-edit" on-click={(ev) => this.nodeEdit(ev, store, data)}></i> <i class="el-icon-delete" on-click={() => this.nodeDelete(node, data)}></i> <i class="el-icon-upload2" on-click={() => this.nodeUp(node, data)}></i> <i class="el-icon-download" on-click={() => this.nodeDown(store, node, data)}></i> <i class="el-icon-plus" on-click={() => this.append(store, node, data)}></i> </div> </span>) } } , data() { return { filterText: '', treeData: [{ id: 1, label: '一级 1', isEdit: false, children: [{ id: 4, label: '二级 1-1', isEdit: false, children: [{id: 9, label: '三级 1-1-1', isEdit: false, children: []}, { id: 10, label: '三级 1-1-2', isEdit: false, children: [] }, { id: 11, label: '三级 1-1-3', isEdit: false, children: [] }] }, { id: 12, label: '二级 1-2', isEdit: false, children: [] }, { id: 13, label: '二级 1-3', isEdit: false, children: [] }] }, { id: 2, label: '一级 2', isEdit: false, children: [{id: 5, label: '二级 2-1', isEdit: false, children: []}, { id: 6, label: '二级 2-2', isEdit: false, children: [] }] }, { id: 3, label: '一级 3', isEdit: false, children: [ {id: 7, label: '二级 3-1', isEdit: false, children: []}, { id: 8, label: '二级 3-2', isEdit: false, children: [] }] }], add_question_flag: false, new_question_name: '', tree_key: 0, defaultExpand: [] } }, } </script> <style scoped> </style>
以上这篇Element-ui树形控件el-tree自定义增删改和局部刷新及懒加载操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。