视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
VUE饿了么树形控件添加增删改功能的示例代码
2020-11-27 22:27:45 责编:小采
文档


本文介绍了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了

    下载本文
    显示全文
    专题