您的当前位置:首页正文

VUE饿了么树形控件添加增删改功能的示例代码

2020-11-27 来源:意榕旅游网

本文介绍了VUE饿了么树形控件添加增删改功能的示例代码,分享给大家,具体如下:

element-ui树形控件:地址

在原文档中有个案例是有新增和删除功能,但是后来发现其修改的数据并不能直接影响到树形数据,所以采用了 render-content 的API重新写了个组件。

写个开发的步骤,所以文章比较长emmm

大致效果如图:

1.省市API

在网上复制了个省市的list,有两个属性是新增的

  • isEdit :控制编辑状态
  • maxexpandId :为现下id的最大值
  • export default{
    
     maxexpandId: 95,
     treelist: [{ 
     id: 1, 
     name: "北京市", 
     ProSort: 1, 
     remark: "直辖市",
     pid: '',
     isEdit: false,
     children: [{
     id: 35,
     name: "朝阳区",
     pid: 1,
     remark: '',
     isEdit: false,
     children: []
     }]
     }{...}]
    }
    
    

    2.el-tree Component基本

    咱们一步步来,先写个饿了么的组件

    <template>
     <el-tree ref="expandMenuList" class="expand-tree"
     v-if="isLoadingTree"
     :data="setTree"
     node-key="id"
     highlight-current
     :props="defaultProps"
     :expand-on-click-node="false"
     :render-content="renderContent"
     :default-expanded-keys="defaultExpandKeys"></el-tree>
    </template>
    <!--
    * highlight-current :为了点击时节点高亮
    * expand-on-click-node : 只能箭头控制树形的展开收缩
    * render-content : 节点渲染方式
    * default-expanded-keys :默认展开节点
    -->

    同时引入API和节点渲染的组件

    import TreeRender from '@/components/tree_render'
    import api from '@/resource/api'

    然后搭建好基础

    data(){
     return{
     maxexpandId: api.maxexpandId,//新增节点开始id
     non_maxexpandId: api.maxexpandId,//新增节点开始id(不更改)
     isLoadingTree: false,//是否加载节点树
     setTree: api.treelist,//节点树数据
     defaultProps: {
     children: 'children',
     label: 'name'
     },
     defaultExpandKeys: [],//默认展开节点列表
     }
    },

    添加个渲染的method

    methods: {
     renderContent(h,{node,data,store}){
     let that = this;//指向vue
     return h(TreeRender,{
     props: {
     DATA: data,//节点数据
     NODE: node,//节点内容
     STORE: store,//完整树形内容
     },
     on: {//绑定方法
     nodeAdd: ((s,d,n) => that.handleAdd(s,d,n)),
     nodeEdit: ((s,d,n) => that.handleEdit(s,d,n)),
     nodeDel: ((s,d,n) => that.handleDelete(s,d,n))
     }
     });
     },
     handleAdd(s,d,n){//增加节点
     console.log(s,d,n)
     },
     handleEdit(s,d,n){//编辑节点
     console.log(s,d,n)
     },
     handleDelete(s,d,n){//删除节点
     console.log(s,d,n)
     }
    }
    

    3.tree_render Component基本

    渲染组件:

    <template>
     <span class="tree-expand">
     <span class="tree-label">
     <span>{{DATA.name}}</span>
     </span>
     <span class="tree-btn">
     <i class="el-icon-plus" @click.stop="nodeAdd(STORE,DATA,NODE)"></i>
     <i class="el-icon-edit" @click.stop="nodeEdit(STORE,DATA,NODE)"></i>
     <i class="el-icon-delete" @click.stop="nodeDel(STORE,DATA,NODE)"></i>
     </span>
     </span>
    </template>

    添加好几个按钮(element-ui自带icon:地址)对应的方法:

    export default{
     props: ['NODE', 'DATA', 'STORE'],
     methods: {
     nodeAdd(s,d,n){//新增
     this.$emit('nodeAdd',s,d,n)
     },
     nodeEdit(s,d,n){//编辑
     this.$emit('nodeEdit',s,d,n)
     },
     nodeDel(s,d,n){//删除
     this.$emit('nodeDel',s,d,n)
     }
     }
    }
    

    4.改

    我们用isEdit来切换input和span的显示状态,首先加个input:

    <!-- tree_render component -->
    <template>
     <span class="tree-expand">
     <span class="tree-label" v-if="DATA.isEdit">
     <el-input class="edit" size="mini"
     :ref="'treeInput'+DATA.id"
     v-model="DATA.name"></el-input>
     </span>
     <template v-else>
     <span class="tree-label">
     <span>{{DATA.name}}</span>
     </span>
     <span class="tree-btn" v-show="!DATA.isEdit">
     <i class="el-icon-plus" @click.stop="nodeAdd(STORE,DATA,NODE)"></i>
     <i class="el-icon-edit" @click.stop="nodeEdit(STORE,DATA,NODE)"></i>
     <i class="el-icon-delete" @click.stop="nodeDel(STORE,DATA,NODE)"></i>
     </span>
     </template>
     </span>
    </template>
    

    编辑的时候按钮同时消失,那么什么时候编辑完成呢?

  • 编辑完按enter键=》监听input的enter输入
  • 点击其他节点=》input失焦-blur=》编辑时自动聚焦-focus
  • 点击当前节点范围
  • 当以上三点发生一项,节点对应的data都要isEdit = false;

    1、enter键

    <!-- tree_render component -->
    <el-input @keyup.enter.native="nodeEditPass(STORE,DATA,NODE)"></el-input>

    添加方法:

    //tree_render component
    methods: {
     nodeEditPass(s,d,n){
     d.isEdit = false;
     }
    }

    2、focus or blur

    <!-- tree_render component -->
    <el-input @blur="nodeEditPass(STORE,DATA,NODE)"></el-input>

    后来发现第一次编辑时能让input聚焦,点击第二个input就不起作用了,加了autofocus属性也同样如此。所以我们要在点击编辑icon的时候,用原生的input autofocus。

    修改方法:

    //tree_render component
    nodeEdit(s,d,n){//编辑
     d.isEdit = true;
     this.$nextTick(() => {
     this.$refs['treeInput'+d.id].$refs.input.focus()
     })
     this.$emit('nodeEdit',s,d,n)
    }
    

    3、当前节点点击

    采用el-tree已有的API——node-click

    <!-- el-tree component -->
    <el-tree @node-click="handleNodeClick"></el-tree>

    添加methods:

    //el-tree component
    methods: {
     handleNodeClick(d,n,s){//点击节点
     d.isEdit = false;//放弃编辑状态
     }
    }

    问题来了,如果在编辑状态下点击此节点也同样会影响input,这就无法进入编辑,所以要阻止input事件冒泡:

    <!-- tree_render component -->
    <el-input @click.stop.native="nodeEditFocus"></el-input>

    添加methods:

    //tree_render component
    methods: {
     nodeEditFocus(){}
    }

    4、v-show代替v-if

    这里有个新的问题,当用户经常编辑修改,v-if模板的开销更高,所以改用v-show。而后者不支持template模板,所以要适当调整一下位置:

    <template>
     <span class="tree-expand">
     <span class="tree-label" v-show="DATA.isEdit">
     <el-input class="edit" size="mini" autofocus
     v-model="DATA.name"
     :ref="'treeInput'+DATA.id"
     @click.stop.native="nodeEditFocus"
     @blur="nodeEditPass(STORE,DATA,NODE)"
     @keyup.enter.native="nodeEditPass(STORE,DATA,NODE)"></el-input>
     </span>
     <span v-show="!DATA.isEdit">
     <span>{{DATA.name}}</span>
     </span>
     <span class="tree-btn" v-show="!DATA.isEdit">
     <i class="el-icon-plus" @click.stop="nodeAdd(STORE,DATA,NODE)"></i>
     <i class="el-icon-edit" @click.stop="nodeEdit(STORE,DATA,NODE)"></i>
     <i class="el-icon-delete" @click.stop="nodeDel(STORE,DATA,NODE)"></i>
     </span>
     </span>
    </template>
    

    5.增

    新增节点 =》添加一条数据

    1. 新增的同时展开父节点
    2. 是否考虑无限新增
    //el-tree component
    handleAdd(s,d,n){//增加节点
     console.log(s,d,n)
     if(n.level >=6){
     this.$message.error("最多只支持五级!")
     return false;
     }
     //添加数据
     d.children.push({
     id: ++this.maxexpandId,
     name: '新增节点',
     pid: d.id,
     isEdit: false,
     children: []
     });
     //展开节点
     if(!n.expanded){
     n.expanded = true;
     }
    }
    

    新增节点字体加粗 =》给节点添加一个class =》 如何判断是否新增?

    我们有一个参数maxexpandId

    tree_render添加一个prop

    //el-tree component
    renderContent(h,{node,data,store}){//加载节点
     let that = this;
     return h(TreeRender,{
     props: {
     ...
     maxexpandId: that.non_maxexpandId
     },
     on: {...}
     });
    }

    根据id判断:

    //tree_render component
    props: ['NODE', 'DATA', 'STORE', 'maxexpandId']
    <!-- tree_render component -->
    <span v-show="!DATA.isEdit" 
    :class="[DATA.id > maxexpandId ? 'tree-new tree-label' : 'tree-label']"
    :ref="'treeLabel'+DATA.id">
     <span>{{DATA.name}}</span>
    </span>
    .tree-expand .tree-label.tree-new{
     font-weight:600;
    }

    6.删

    跟新增同义:删除节点 =》删除一条数据

  • 新增节点直接删除
  • 已有节点需提示再删除
  • 已有子级节点不能删除
  • handleDelete(s,d,n){//删除节点
     console.log(s,d,n)
     let that = this;
     //有子级不删除
     if(d.children && d.children.length !== 0){
     this.$message.error("此节点有子级,不可删除!")
     return false;
     }else{
     //删除操作
     let delNode = () => {
     let list = n.parent.data.children || n.parent.data,
     //节点同级数据,顶级节点时无children
     _index = 99999;//要删除的index
     list.map((c,i) => {
     if(d.id == c.id){
     _index = i;
     }
     })
     let k = list.splice(_index,1);
     //console.log(_index,k)
     this.$message.success("删除成功!")
     }
     let isDel = () => {
     that.$confirm("是否删除此节点?","提示",{
     confirmButtonText: "确认",
     cancelButtonText: "取消",
     type: "warning"
     }).then(() => {
     delNode()//此处可通过ajax做删除操作
     }).catch(() => {
     return false;
     })
     }
     //新增节点直接删除,否则要通过请求数据删除
     d.id > this.non_maxexpandId ? delNode() : isDel()
     }
    }
    

    7.拓展

    还有一些特别的需求,例如:

    1、点击高亮的时候显示icon

    .expand-tree .is-current>.el-tree-node__content .tree-btn,
    .expand-tree .el-tree-node__content:hover .tree-btn{
     display: inline-block;
    }

    2、添加顶级节点

    添加按钮:

    <!-- el-tree component -->
    <el-button @click="handleAddTop">添加顶级节点</el-button>

    添加methods:

    //el-tree component
    methods: {
     handleAddTop(){
     this.setTree.push({
     id: ++this.maxexpandId,
     name: '新增节点',
     pid: '',
     isEdit: false,
     children: []
     })
     }
    }
    

    3、默认展开树形第一级

    //el-tree component
    mounted(){
     this.initExpand()
    },
    methods: {
     initExpand(){
     //isLoadingTree用意也是在此
     this.setTree.map((a) => {
     this.defaultExpandKeys.push(a.id)
     });
     this.isLoadingTree = true;
     },
    }

    8.github

    还有些具体的样式都放在github了

    显示全文