element plus 可选择树形组件(el-tree) 怎样一键展开/收起?实现方法详解

本文介绍了如何在Vue应用中使用el-tree组件实现展开和收起所有节点的功能,通过ref绑定和store的_getAllNodes属性来控制节点状态。尽管官方文档未详述,但实际操作显示这是一种有效的方法。

实现代码:

按钮:

<el-button @click="takeall" style="height: 24px">
    {{zhanstatus % 2 != 0 ? "收起所有" : "展开所有"}}
</el-button>

组件:

        <el-form-item label="可选择菜单" :label-width="formLabelWidth">
          <el-tree
            ref="folderTreeRef"
            :data="mneudata"
            show-checkbox
            node-key="id"
            highlight-current
            :props="defaultProps"
          />
        </el-form-item>

 在ref中绑定folderTreeRef 

展开&收起:

const folderTreeRef = ref(null);
const zhanstatus = ref(0);
let takeall = () => {
  zhanstatus.value++;
  if (zhanstatus.value % 2 == 0) {
    const nodes = folderTreeRef.value.store._getAllNodes();
    nodes.forEach(item => {
      item.expanded = false;
    });
  } else {
    const nodes = folderTreeRef.value.store._getAllNodes();
    nodes.forEach(item => {
      item.expanded = true;
    });
  }
};

效果:

实现原理:

打印上面的 folderTreeRef ,可以从原型链的store中找到 _getAllNodes 属性

官方文档好像没有描述关于此属性的内容,查了好多资料,搜了多篇文章,可以发现store原型中有_getAllNodes 这个属性

稍微试了一下居然成功了 (๑ᵒ̴̶̷͈᷄ᗨᵒ̴̶̷͈᷅)

<think> 我们正在使用Element UI的el-tree组件,并且需要详细了解render-content属性的使用方法。 根据引用[2]和引用[3],我们可以看到两种自定义节点内容的方式:一种是使用render-content(引用[2]中使用了render-content的jsx语法),另一种是使用scoped slot(引用[3]和引用[4]使用了插槽)。 注意:在Element UI中,render-content属性是一个函数,用于自定义树节点的内容。它需要返回一个VNode(虚拟节点)。在Vue中,我们可以使用渲染函数(render函数)或JSX来创建VNode。 引用[2]中的示例使用了JSX语法(在Vue中需要支持JSX): 在el-tree上使用render-content属性,并绑定一个方法(比如`renderContent`)。在这个方法中,我们可以返回一个JSX元素来渲染节点。 然而,从引用[3]和引用[4]中,我们也可以看到使用scoped slot(作用域插槽)的方式,即通过`<template #default="{ node, data }">`来定义节点内容。这种方式在模板中更加直观。 但是,用户明确要求的是`render-content`属性,因此我们将重点讲解`render-content`。 使用`render-content`需要传入一个函数,该函数有三个参数:`createElement`, `nodeObj`(节点对象,包含节点数据、节点状态等信息),以及可选的`node`(VueComponent实例,代表当前节点组件)。 通常我们这样写: render-content="renderContent" 然后在methods中定义renderContent方法: renderContent(h, { node, data, store }) { // 使用h函数(即createElement)来创建虚拟节点 // 或者使用JSX(如果项目配置了JSX支持) } 注意:在Vue的渲染函数中,第一个参数通常是`h`(createElement的别名),第二个参数是一个对象,包含以下属性: node: 当前节点对应的Node对象(包含节点信息,如label, level, expanded, isLeaf等) data: 当前节点的数据(即节点对应的数据对象) store: 整个树节点的状态管理(TreeStore的实例) 我们可以返回一个由h函数创建的VNode,或者使用JSX语法(需要配置babel插件)。 下面我们分别用两种方式(h函数和JSX)来举例: 1. 使用h函数(渲染函数): 假设我们想在每个节点前添加一个图标,并根据是否有子节点显示不同的图标: renderContent(h, { node, data }) { return h('span', { class: 'custom-tree-node' }, [ // 图标部分:根据是否有子节点显示不同图标 h('span', [ h('img', { attrs: { src: data.children && data.children.length > 0 ? 'folder.png' : 'file.png', style: 'width:15px;height:15px;margin-right:5px;' } }) ]), h('span', node.label) ]); } 2. 使用JSX(需要在项目中配置JSX支持,例如使用@vue/babel-preset-jsx): 同样的内容用JSX写: renderContent(h, { node, data }) { return ( <span class="custom-tree-node"> <span> { data.children && data.children.length > 0 ? <img src="folder.png" style={{ width: '15px', height: '15px', marginRight: '5px' }} /> : <img src="file.png" style={{ width: '15px', height: '15px', marginRight: '5px' }} /> } </span> <span>{ node.label }</span> </span> ); } 注意:在JSX中,我们直接返回一个JSX元素,并且可以使用JavaScript表达式。 另外,引用[2]中展示了在节点上添加操作按钮(如编辑、删除)的示例,我们可以参考: renderContent(h, { node, data, store }) { return ( <span class="custom-tree-node"> <span>{node.label}</span> <span> <el-button size="mini" type="text" on-click={ () => this.append(data) }>添加</el-button> <el-button size="mini" type="text" on-click={ () => this.remove(node, data) }>删除</el-button> </span> </span> ); } 这里注意,在JSX中绑定事件需要使用`on-click`(注意是驼峰写法,但在JSX中要写成onClick,不过Element UI的JSX中事件绑定使用`on-`前缀也可以?实际上,在VueJSX中,事件绑定使用`on`或`nativeOn`,具体事件名使用驼峰,例如`onClick`。但是,由于Element UI的按钮组件使用的是`click`事件,所以我们可以这样写:`onClick={...}`。但是在VueJSX中,自定义事件使用`on`+事件名(驼峰),原生事件使用`nativeOn`或加上`native`修饰符。 不过,在Element UI的组件上,事件通常都是自定义事件,所以我们可以这样绑定:`onClick={...}`。但是注意,在JSX中,事件处理函数需要写成函数形式,例如:`onClick={() => this.append(data)}`。 另外,在JSX中,我们也可以使用展开运算符来传递属性,比如`<el-button {...{ props: { size: 'mini', type: 'text' } }}>添加</el-button>`,但更常见的是直接写属性。 重要提示:使用render-content函数时,节点内部的点击事件等需要自己处理,如果需要在节点上触发树节点的事件(比如展开/折叠),可能需要额外处理。而使用插槽方式则默认会保留树节点的行为(因为插槽内容只是替换了节点标签内的内容,节点本身的图标和事件由tree组件控制)。 另外,在Vue2中,如果使用JSX,需要在babel配置中支持。而在Vue3中,JSX支持是开箱即用的(通过@vue/babel-plugin-jsx)。 最后,注意在render-content中,我们也可以使用组件。例如,我们可以引入一个自定义的组件来渲染节点内容。 总结步骤: 1.el-tree组件上绑定render-content属性: <el-tree :data="data" :render-content="renderContent"></el-tree> 2.Vue组件的methods中定义renderContent函数(使用h函数或JSX)。 3. 在renderContent函数中,根据节点数据返回VNode。 4. 如果需要操作按钮,注意在事件处理函数中正确获取节点数据。 5. 注意,使用render-content可能会覆盖树节点默认的展开/折叠图标,因此如果需要保留这些图标,需要手动在渲染内容中加入。Element UI默认的节点结构包括一个表示展开/折叠的三角形图标、节点图标(如果有的话)和节点文本。但当我们自定义时,这些默认内容都会被覆盖。 如果希望保留默认的展开折叠图标,我们可以这样: renderContent(h, { node, data, store }) { // 默认的展开折叠图标是通过node对象的expanded和isLeaf等属性来决定的 // 我们可以手动创建一个类似的结构 // 注意:默认的展开图标和折叠图标是使用class来控制的,我们可以复制Element UI的样式结构 // 但是,更简单的方式是:使用store的某些方法或者直接使用Element UI的TreeNode属性来渲染默认图标 // 不过,官方没有提供直接的方法,因此如果我们希望保留默认图标,可能需要参考Element UI的源码,或者使用scoped slot方式(推荐)来避免覆盖默认图标。 // 实际上,使用scoped slot不会覆盖默认的展开折叠图标,它只替换节点文本部分。而render-content会覆盖整个节点内容。 // 因此,如果使用render-content,我们需要自己绘制展开折叠图标(如果需要的话)。 // 例如,我们可以这样创建一个包含展开折叠图标的节点: return h('span', { class: 'custom-tree-node' }, [ // 展开折叠图标:如果是叶子节点则不显示,否则根据node.expanded显示不同的图标 node.isLeaf ? null : h('i', { class: [ 'el-tree-node__expand-icon', node.expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right' ], on: { click: () => { store.toggleExpansion(node); } // 切换展开状态 } }), // 自定义图标部分 h('img', { attrs: { src: node.isLeaf ? 'file.png' : 'folder.png', style: 'width:15px;height:15px;margin-right:5px;' } }), h('span', node.label) ]); } 这样写的话,我们就需要自己处理展开折叠图标的点击事件,并调用store的方法来切换展开状态。 但是,这种方式比较复杂,所以如果不需要自定义整个节点结构(包括展开折叠图标),建议使用scoped slot(即default插槽)方式。 根据用户要求,我们给出的是render-content的详细说明,因此我们展示了如何自定义整个节点内容。 最后,请注意,在Vue2中,使用JSX需要安装并配置相应的babel插件。在Vue3中,官方提供了对JSX的支持。 由于引用中提到了vue3+ts(引用[3]),所以如果用户使用的是Vue3,可以使用JSX语法。但如果是Vue2,同样可以使用JSX,只是配置方式不同。 因此,根据用户的环境,选择合适的方式。 下面是一个完整的示例(使用Vue2和h函数): <template> <el-tree :data="data" :render-content="renderContent"></el-tree> </template> <script> export default { data() { return { data: [ // 树数据,结构同引用[4] ] }; }, methods: { renderContent(h, { node, data, store }) { return h('span', { class: 'custom-tree-node' }, [ // 如果不是叶子节点,添加展开折叠图标 node.isLeaf ? null : h('i', { class: [ 'el-tree-node__expand-icon', node.expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right' ], on: { click: () => store.toggleExpansion(node) // 切换展开状态 } }), h('img', { attrs: { src: node.isLeaf ? require('./assets/file.png') : require('./assets/folder.png'), style: 'width:15px;height:15px;margin-right:5px;' } }), h('span', node.label) ]); } } }; </script> 注意:图片路径可能需要根据实际情况调整。 如果用户使用的是JSX,则示例可以写成: <script> export default { methods: { renderContent(h, { node, data, store }) { return ( <span class="custom-tree-node"> { node.isLeaf ? null : ( <i class={['el-tree-node__expand-icon', node.expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right']} onClick={() => store.toggleExpansion(node)} /> )} <img src={node.isLeaf ? require('./assets/file.png') : require('./assets/folder.png')} style={{ width: '15px', height: '15px', marginRight: '5px' }} /> <span>{ node.label }</span> </span> ); } } }; </script> 以上便是render-content的详细使用说明。 注意:使用render-content自定义节点内容时,如果树节点需要支持复选框,那么还需要自己处理复选框的渲染和事件,因为默认的复选框也被覆盖了。因此,当需要复选框时,建议使用scoped slot,或者参考Element UI的源码来模拟复选框。 所以,如果不需要自定义整个节点(包括复选框、展开折叠图标等),使用scoped slot是更简单的方式。但用户要求的是render-content,所以我们提供了完整的节点自定义方法。 最后,我们生成相关问题: </think> ### Element UI中el-tree组件的render-content属性使用详细教程 在Element UI中,`render-content`属性用于**完全自定义树节点的渲染内容**,通过JavaScript渲染函数(Render Function)实现高度定制化。以下为详细使用说明和示例: #### 一、基本用法 ```html <el-tree :data="treeData" :render-content="renderNodeContent" <!-- 绑定渲染函数 --> node-key="id" show-checkbox ></el-tree> ``` #### 二、渲染函数参数解析 渲染函数接收一个参数对象,包含三个关键属性: ```javascript renderNodeContent(h, { node, data, store }) { // h: createElement函数,用于创建VNode // node: 当前节点对象(包含isLeaf、expanded等状态) // data: 当前节点数据(即data数组中的原始对象) // store: TreeStore实例(包含树形状态管理方法) } ``` #### 三、实现自定义节点内容 ##### 示例1:基础文本+图标 ```javascript methods: { renderNodeContent(h, { node, data }) { return h('div', { class: 'custom-node' }, [ h('i', { class: data.iconClass }), // 图标 h('span', { style: { marginLeft: '8px' } }, node.label) // 文本 ]) } } ``` ##### 示例2:带操作按钮的节点(JSX语法) ```javascript renderNodeContent(h, { node, data }) { return ( <div class="node-operations"> <span>{node.label}</span> <el-button size="mini" onClick={() => this.editNode(data)}>编辑</el-button> <el-button size="mini" onClick={() => this.deleteNode(node, data)}>删除</el-button> </div> ) } ``` ##### 示例3:条件渲染不同样式 ```javascript renderNodeContent(h, { node, data }) { const isActive = this.activeNodeId === data.id; return h('span', { style: { color: isActive ? '#409EFF' : '#606266', fontWeight: isActive ? 'bold' : 'normal' } }, node.label); } ``` #### 四、关键注意事项 1. **覆盖默认行为** 使用`render-content`会完全覆盖节点的默认渲染(包括展开/折叠图标),如需保留默认图标需手动实现: ```javascript h('i', { class: [ 'el-tree-node__expand-icon', node.expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right' ] }) ``` 2. **事件绑定要点** - 自定义事件需使用原生DOM事件(如`onClick`) - 阻止事件冒泡:`e => { e.stopPropagation(); /* 操作 */ }` - 访问树实例:`this.$refs.tree` 3. **性能优化** 复杂渲染内容建议使用`scoped slot`(性能更优),`render-content`更适合需要动态逻辑的场景[^1][^3]。 #### 五、与scoped slot对比 | 特性 | render-content | scoped slot (default) | |---------------------|------------------------------------|-----------------------------------| | **自定义程度** | 完全控制节点渲染 | 仅自定义内容区域 | | **默认图标保留** | 需手动实现 | 自动保留 | | **性能** | 较低(每次渲染执行函数) | 较高 | | **适用场景** | 动态逻辑复杂的节点 | 静态内容定制 | > 实际项目中,如引用[3]所示,在只需样式定制时推荐使用`scoped slot`;当需要嵌入交互组件(如按钮、输入框)时选择`render-content`更灵活[^3][^4]。 #### 完整示例 ```javascript <template> <el-tree :data="treeData" :render-content="renderNode" /> </template> <script> export default { data() { return { treeData: [ { id: 1, label: 'Parent', children: [...] } ] } }, methods: { renderNode(h, { node, data, store }) { return ( <div class="custom-node" onClick={e => e.stopPropagation()}> {!node.isLeaf && ( <i class={`el-tree-node__expand-icon ${ node.expanded ? 'el-icon-remove' : 'el-icon-circle-plus' }`} /> )} <el-tag size="mini">{data.type}</el-tag> <span style="margin:0 10px">{node.label}</span> <el-button size="mini" onClick={() => this.addChild(data)}> 添加子节点 </el-button> </div> ) } } } </script> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值