iview自定义Tree组件内容

本文详细介绍如何在Vue项目中使用iview框架的Tree组件,通过Render函数自定义树节点内容,实现展示多个属性和操作按钮的功能。文章包括组件配置、数据结构转换及异步加载子节点的实现。
介绍

Vue完整前后台项目介绍

使用iview框架进行开发的话,一般都有展示树形结构数据的需求。那么就用的到其中的Tree组件了。但是其基本架构是满足不了我们的需求的,基本架构如下:

在这里插入图片描述

一般来说一条数据肯定不止展示它的title属性,我们的需求是展示多个属性,并且含有多个操作按钮,具体如下:

在这里插入图片描述

这其中得用到 Render 函数来自定义节点显示内容和交互。

下面就介绍一下Tree组件如何自定义节点内容。

自定义节点内容

先把完整的代码贴出来,然后在解释一波:

<template>
  <Card class="album">
    <Tree :data="treeData" :load-data="loadData" :render="renderContent"></Tree>
  </Card>
</template>

<script>
  import { mapActions } from 'vuex'
  export default {
    name: 'Album',
    data () {
      return {
        treeData: [],
      }
    },
    methods: {
      ...mapActions([
        'albumCategoryList'
      ]),
      loadData (item, callback) {
        let parentId = item.id || 0
        let data = []
        this.albumCategoryList({
          parentId: parentId
        }).then((res) => {
          if (res.status === 1) {
            data = this.getTree(res.fields)
            callback(data)
          } else {
            this.$Notice.error({
              title: '错误',
              desc: res.msg
            })
          }
        }).catch(error => {
          this.$Notice.error({
            title: '错误',
            desc: '网络连接错误'
          })
          console.log(error)
        })
      },
      getTree (tree = []) {
        let arr = [];
        if (tree.length !== 0) {
          tree.forEach(item => {
            let obj = {};
            obj.categoryName = item.categoryName;
            obj.id = item.id; // 其他你想要添加的属性
            obj.removed = item.removed; // 其他你想要添加的属性
            obj.rank = item.rank; // 其他你想要添加的属性
            obj.imageUrl = item.imageUrl; // 其他你想要添加的属性
            obj.desc = item.desc; // 其他你想要添加的属性
            obj.parentId = item.parentId; // 其他你想要添加的属性
            if(item.child === 1) {
              obj.children = [];
              obj.loading = false;
            }
            arr.push(obj);
          });
        }
        return arr
      },
      renderContent (h, { root, node, data }) {
        return h('span', {
          style: {
            display: 'inline-block',
            width: '100%'
          }
        }, [
          h('span',[
            h('Icon', {
              style: {
                marginRight: '8px'
              }
            }),
            h('span', data.categoryName)
          ]),
          h('span', {
            style: {
              display: 'inline-block',
              float: 'right',
              marginRight: '32px'
            }
          }, [
            h('span', {
              style: {
                display: 'inline-block',
                'text-align': 'center',
                width: '25px',
                marginRight: '40px'
              }
            }, data.rank),
            h('span', {
              style: {
                display: 'inline-block',
                width: '480px',
                marginRight: '20px'
              }
            }, data.imageUrl || ''),
            h('Button', {
              props: Object.assign({},  {
                type: 'primary',
                size: 'small',
              }),
              style: {
                marginRight: '8px'
              },
              on: {
                click: () => { this.albumCategoryAdd(data) }
              }
            }, '添加'),
            h('Button', {
              props: Object.assign({},  {
                type: 'primary',
                size: 'small',
              }),
              style: {
                marginRight: '8px'
              },
              on: {
                click: () => { this.albumCategoryEdit(data) }
              }
            }, '编辑'),
            h('Button', {
              props: Object.assign({},  {
                type: 'error',
                size: 'small',
              }),
              on: {
                click: () => { this.changeAlbumCategoryStatus(data.id, data.removed) }
              }
            }, data.removed === 0 ? '启用' : '停用' )
          ])
        ]);
      },
      // 页面加载后 查询parentID=0 的分类
      queryCategoryList (parentId = 0) {
        console.log('执行专辑分类queryCategoryList')
        this.albumCategoryList({
          parentId: parentId
        }).then((res) => {
          if (res.status === 1) {
             // 这里给节点赋值
            this.treeData = this.getTree(res.fields)
          } else {
            this.$Notice.error({
              title: '错误',
              desc: res.msg
            })
          }
        }).catch(error => {
          this.$Notice.error({
            title: '错误',
            desc: '网络连接错误'
          })
          console.log(error)
        })
      },
    },
    mounted () {
      this.queryCategoryList()
    }
  }
</script>

<style scoped>

</style>

<Tree :data="treeData" :load-data="loadData" :render="renderContent"></Tree>

看过iview中Tree组件介绍的那就知道:data是绑定展示的数据的,:load-data是用于异步加载子节点的,:render是用于自定义节点内容的。

先看一下我们的数据结构是啥啊:

在这里插入图片描述

我这是axios设置的response拦截器拦截的数据截了个图,也应该可以看清的吧…

child属性用来表示此节点是不是有子节点,1表示有,0表示无。别的属性就不介绍了。

首先,我们打开页面的时候应该把所有的顶级父节点即parentId = 0的节点展示出来,这是需要查询数据库的,所以在vue生命周期mounted函数中执行了顶级父节点的查找函数,即:

mounted () {
	this.queryCategoryList()
}
	  // 页面加载后 查询parentID=0 的分类
      queryCategoryList (parentId = 0) {
        console.log('执行专辑分类queryCategoryList')
        this.albumCategoryList({
          parentId: parentId
        }).then((res) => {
          if (res.status === 1) {
			// 这里给节点赋值
            this.treeData = this.getTree(res.fields)
          } else {
            this.$Notice.error({
              title: '错误',
              desc: res.msg
            })
          }
        }).catch(error => {
          this.$Notice.error({
            title: '错误',
            desc: '网络连接错误'
          })
          console.log(error)
        })
      },

this.albumCategoryList是使用axios发送请求的函数,这里没有展示出来,但请求得到的数据结构在上面也展示出来了,应该OK的吧!

this.getTree(res.fields) 这个函数主要是转换一下数据结构,其中要关注一下,当数据的child属性为1即其有子节点时,obj.children = []; obj.loading = false;这两个属性是必须附上的,否则不会应用异步加载效果。具体的可看看上面的完整代码。

下面看一下自定义节点内容的函数:

renderContent (h, { root, node, data }) {
        return h('span', {
          style: {
            display: 'inline-block',
            width: '100%'
          }
        }, [
          h('span',[
            h('Icon', {
              style: {
                marginRight: '8px'
              }
            }),
            h('span', data.categoryName)
          ]),
          h('span', {
            style: {
              display: 'inline-block',
              float: 'right',
              marginRight: '32px'
            }
          }, [
            h('span', {
              style: {
                display: 'inline-block',
                'text-align': 'center',
                width: '25px',
                marginRight: '40px'
              }
            }, data.rank),
            h('span', {
              style: {
                display: 'inline-block',
                width: '480px',
                marginRight: '20px'
              }
            }, data.imageUrl || ''),
            h('Button', {
              props: Object.assign({},  {
                type: 'primary',
                size: 'small',
              }),
              style: {
                marginRight: '8px'
              },
              on: {
                click: () => { this.albumCategoryAdd(data) }
              }
            }, '添加'),
            h('Button', {
              props: Object.assign({},  {
                type: 'primary',
                size: 'small',
              }),
              style: {
                marginRight: '8px'
              },
              on: {
                click: () => { this.albumCategoryEdit(data) }
              }
            }, '编辑'),
            h('Button', {
              props: Object.assign({},  {
                type: 'error',
                size: 'small',
              }),
              on: {
                click: () => { this.changeAlbumCategoryStatus(data.id, data.removed) }
              }
            }, data.removed === 0 ? '启用' : '停用' )
          ])
        ]);
      },

这个render函数就不具体解释了吧,就是根据你定义的treeData数组中每一条数据来编织你要的样式。

最后就是异步加载子节点了:

loadData (item, callback) {
        let parentId = item.id || 0
        let data = []
        this.albumCategoryList({
          parentId: parentId
        }).then((res) => {
          if (res.status === 1) {
            data = this.getTree(res.fields)
            callback(data)
          } else {
            this.$Notice.error({
              title: '错误',
              desc: res.msg
            })
          }
        }).catch(error => {
          this.$Notice.error({
            title: '错误',
            desc: '网络连接错误'
          })
          console.log(error)
        })
      },

可以看到这里逻辑和queryCategoryList函数差不多,只是查找条件变成了当前节点的id,表示查找当前节点的子节点。

PS:上面的完整代码是一整个VUE页面拷贝下来的,除了albumCategoryList这个axios函数需要自己实现以外

<think> 我们正在讨论的是iviewtree组件修改第二层级样式的方法。根据引用[1]的信息,用户提到了一个改造的tree组件,但我们的重点在于原生iview tree组件的样式修改。 在iview中,Tree组件使用渲染函数(render函数)来自定义节点内容,同时也可以通过CSS样式来修改节点的样式。要修改第二层级的样式,我们可以通过以下步骤实现: 1. 使用scoped样式(如果是在Vue单文件组件中),避免样式污染全局。 2. 通过深度选择器(如/deep/或>>>)来覆盖iview组件的默认样式(注意:在Vue3中,使用:deep()代替)。 3. 确定第二层级节点的特定类名。我们可以通过浏览器的开发者工具(Inspect)来查看第二层级节点所拥有的类名。 假设我们想要修改第二层级节点的背景颜色,我们可以这样写: 在Vue组件的style部分(使用less或scss,或者普通css): ```css /* 假设第二层级的节点有特定的类,我们可以通过结构来定位 */ /* 第一层是.ivu-tree-children,第二层是.ivu-tree-children > .ivu-tree-children */ /* 或者通过层级选择器 */ /* 方法1:通过深度选择器覆盖 */ .ivu-tree >>> .ivu-tree-children .ivu-tree-children { background-color: #f0f7ff; } /* 如果上面的选择器不够精确,我们可以使用更具体的 */ /* 注意:iviewtree节点每一层都是一个.ivu-tree-children,第一层只有一个,其子节点就是第二层 */ /* 但是第二层节点本身也有.ivu-tree-children,所以我们可以通过选择第一层的.ivu-tree-children的直接子节点.ivu-tree-children来定位第二层 */ /* 方法2:修改第二层节点的文字颜色 */ .ivu-tree >>> .ivu-tree-children > .ivu-tree-children > .ivu-tree-item .ivu-tree-title { color: #1890ff; } /* 注意:实际类名可能需要根据iview版本进行调整,建议使用开发者工具查看 */ ``` 另外,如果使用render函数自定义节点内容,也可以在render函数中根据节点层级(通过node的level属性)来添加不同的class或style: ```vue <template> <Tree :data="data" :render="renderContent"></Tree> </template> <script> export default { methods: { renderContent(h, { root, node, data }) { // 判断层级,例如第二层:node.level === 2 let style = {}; if (node.level === 2) { style = { color: '#1890ff', // 其他样式 }; } return h('span', { style: style, class: { 'my-tree-level2': node.level === 2 } }, data.title); } } } </script> <style scoped> /* 如果通过添加class的方式 */ .my-tree-level2 { font-weight: bold; } /* 深度选择器在scoped中需要这样写(Vue3) */ :deep(.ivu-tree .my-tree-level2) { /* 如果需要覆盖iview的样式,可以在这里写 */ } </style> ``` 总结: 修改第二层级的样式有两种主要方式: 1. 通过CSS选择器直接覆盖iview的样式(可能需要深度选择器)。 2. 在自定义渲染函数中根据节点层级动态添加样式或类名。 请注意,直接覆盖组件库样式可能会因为版本升级而失效,所以建议使用自定义渲染函数的方式更为可控。 相关问题: 1. 在iview中如何自定义Tree组件的节点内容? 2. Vue组件中如何深度选择器来覆盖子组件的样式? 3. iview Tree组件的node对象有哪些属性? </think> 基于iviewTree组件修改第二层级样式,可通过以下两种方法实现: ### 方法一:CSS深度选择器(推荐) 使用深度选择器`>>>`或`/deep/`覆盖组件内部样式: ```css /* 修改第二层级文字颜色和背景 */ .ivu-tree >>> .ivu-tree-children > .ivu-tree-children { .ivu-tree-title { color: #1890ff; background-color: #f0f7ff; padding: 4px 8px; border-radius: 4px; } } /* 修改第二层级图标 */ .ivu-tree >>> .ivu-tree-children > .ivu-tree-children { .ivu-icon-ios-arrow-forward:before { color: #ff5500; font-size: 14px; } } ``` ### 方法二:render函数动态样式 在渲染函数中根据层级添加自定义类名: ```vue <Tree :data="treeData" :render="renderNode"></Tree> <script> export default { methods: { renderNode(h, { root, node, data }) { // 判断是否为第二层级(根节点level为0) const isLevel2 = node.level === 2; return h('span', { class: { 'custom-level2': isLevel2 // 添加自定义类名 }, style: isLevel2 ? { color: '#1890ff', backgroundColor: '#f0f7ff', padding: '4px 8px' } : {} }, data.title); } } } </script> <style> .custom-level2 { border-left: 3px solid #1890ff; } </style> ``` ### 关键说明: 1. **层级判断**: - 根节点 `level=0` - 第一级子节点 `level=1` - **第二级子节点 `level=2`** 2. **样式优先级技巧**: - 使用`!important`强制覆盖(不推荐) - 增加CSS选择器权重,如: ```css .tree-wrapper .ivu-tree >>> .ivu-tree-children { ... } ``` 3. **浏览器调试**: 通过开发者工具定位DOM结构: ```html <div class="ivu-tree-children"> <!-- 第一层 --> <div class="ivu-tree-children"> <!-- 第二层目标 --> <div class="ivu-tree-item">...</div> </div> </div> ``` > ⚠️ 注意:iviewTree组件通过`expand`控制展开状态时,需确保目标层级已被渲染才能生效样式[^1]。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值