小言_互联网的博客

iView树型图Tree增加编辑、新建、删除以及虚线样式

277人阅读  评论(0)

前言

今天做数仓需要个树形图,想着本来UI用的就是iView,不想再引入别的插件了,但是iView的Tree组件和产品要求的有些不一样。

要求:要求前面要用加减号图标,不要箭头图标,要有同级虚线连接,需要可编辑、可添加、可删除节点;

但是Iview貌似没有现成的配置,只有个添加、删除大致的配置,如图(一);
因此在参考几篇的帖子之后,决定用iView Tree强大的 render 改造一番;最终效果图 如图(二)

代码部分

<template>
  <div class="v-tree"> 
    <Tree :data="treeList" :render="renderContent" ></Tree>
  </div>
</template>
<script>
// import service from 'libs/service/api'
import {
    cloneDeep } from 'lodash-es'
export default {
   
  name: 'VTree',
  props: {
   
    name: String,
    bdId: [String, Number] // 数据库Id
  },
  data() {
   
    return {
   
      editState: false, // 编辑状态
      treeList: [
      {
   
        title: '深圳分公司',
        expand: true,
        // 单独给某个节点设置render,此render会优先Tree组件中的render(此组件可删除,只是为了展示一下,没有实际用处)
        render: (h, {
    root, node, data }) => {
    
            return h('span', {
     
              style: {
   
                display: 'inline-block',
                width: '100%'
              }
            }, [
              h('span', [
                  h('Icon', {
   
                      props: {
   
                        type: 'ios-folder'
                      },
                      style: {
   
                        marginRight: '8px',  
                      }
                  }),
                  h( "span" , data.title)
              ]), 
            ]);
        },
        children: [
            {
   
              title: '父-子',
              expand: true, 
              children: [
                  {
   
                    title: '子',
                    expand: true
                  },
                  {
   
                    title: '子2',
                    expand: true
                  }
              ]
            } 
        ]
      },
      {
   
        title: '深圳分公司',
        expand: true,
          children: [
            {
   
              title: '父-子',
              expand: true, 
              children: [
                {
   
                  title: '子',
                  expand: true
                },
                {
   
                  title: '子2',
                  expand: true
                }
              ]
            } 
          ]
        }
      ],
      buttonProps: {
   
        type: 'default',
        size: 'small',
      },
      // 输入框要修改的内容
      inputContent: '',
      // 修改前的TreeNode名称
      oldName: ''
    }
  },
  mounted() {
   
    // this.getTreeList()
  },
  methods: {
   
    // 树渲染逻辑
    renderContent (h, {
    root, node, data }) {
    
      return h('span', {
   
        class: 'common-wrap',
        on:{
   
          click:() => {
   
            // 点击Tree节点触发
            if (!data.editState) {
   
              this.handleClickTreeNode(root, data, node)
            }
          }
        }
      }, [  
        h('span', [
          // 文件前面的文件夹图标
          h('Icon', {
   
            props: {
   
              type: `${
     data.children === undefined || data.children.length === 0 ? 'md-document' :'ios-folder'}`
            },
            style: {
   
              marginRight: '8px', 
            }
          }),
          h(`${
      data.editState ? '' : 'span'}`, data.title),
          h(`${
      data.editState ? 'input' : ''}`, 
            {
   
              class: 'edit-input',
              attrs:{
   
                value:`${
      data.editState ? data.title : ''}`, 
                autofocus: 'true'
              },  
              style: {
        
                // cursor: 'auto' ,
                // borderRadius: '3px',
                // border: '1px solid #e5e5e5',
                // color: '#515a6e'
              },
              on:{
   
                change: (event) => {
    
                  this.inputContent = event.target.value 
                }
              }
            }
          )
        ]), 
        // 增删改按钮部分
        h(`${
      data.editState ? '' : 'span'}`,
          {
   
            class: 'btnNone'
          },
          [
            // 操作按钮部分 
            // 编辑按钮
            h('Button', {
   
              props: {
   
                ...this.buttonProps,
                icon: 'md-color-filter'
              },
              on: {
   
                click: () => {
    this.editTree(data) }
              }
            }),
            // 添加按钮
            h('Button', {
   
              props: {
   
                ...this.buttonProps,
                icon: 'md-add'
              },
              on: {
   
                click: () => {
    this.append(data) }
              }
            }),
            // 删除按钮
            h('Button', {
   
              props: {
   
                ...this.buttonProps,
                icon: 'md-remove'
              },
              on: {
   
                click: () => {
    this.remove(root, node, data) }
              }
            })
          ]
        ),    
        // 确认/取消修改部分
        h(`${
      data.editState ? 'span' : ''}`,
          {
    
            style: {
    
              marginLeft: '.5rem'
            }
          },
          [  
            // 确认按钮
            h('Button', {
   
              props: {
   
                ...this.buttonProps,
                icon: 'md-checkmark' 
              },
              style: {
   
                border: 0,
                background: 'rgba(0,0,0,0)',
                fontSize: '16px',
                outline: 'none'
              },
              on: {
   
                click: (event) => {
     
                  this.confirmTheChange(data) 
                }
              }
            }),
            // 取消按钮
            h('Button', {
   
              props: {
   
                ...this.buttonProps,
                icon: 'md-close'
              },
              style: {
   
                border: '0',
                background: 'rgba(0,0,0,0)',
                fontSize: '16px',
                outline: 'none'
              },
              on: {
   
                click: () => {
    this.cancelChange(data) }
              }
            }) 
          ]
        ) 
      ])
    },
    /**
     * @description: 获取树列表(这部分可以换成自己的)
     * @param {*}
     * @return {*}
     */    
    async getTreeList() {
   
      let res = await service.getCategoryTreeList({
   
        name: this.name,
        id: this.bdId
      })
      if (res.data) {
   
        this.treeList = [...res.data]
        // 路由参数带有id,则置为选中状态
        if (this.bdId) {
   
          this.filterTableMater(this.bdId, this.treeList)
        }
      }
    },
    // 控制Tree当前状态函数
    setStates(data){
   
      let editState=data.editState
      if (editState) {
   
        this.$set(data, 'editState', false)
      } else {
   
        this.$set(data, 'editState', true)
      }  
    },
    // Tree修改按钮
    editTree(data){
   
      event.stopPropagation()  
      this.inputContent=data.title 
      this.oldName=data.title
      this.setStates(data)  
    },
    // 添加按钮
    append (data) {
   
      event.stopPropagation()
      const children = data.children || []
      children.push({
   
        title: '新建节点',
        expand: true
      })
      this.$set(data, 'children', children)
    },
    // 删除按钮
    remove (root, node, data) {
   
      event.stopPropagation()
      this.$Modal.confirm({
   
        title: '提示',
        content: `您确定删除 “${
     data.title}” 吗?`,
        onOk: () => {
   
          const parentKey = root.find(el => el === node).parent
          const parent = root.find(el => el.nodeKey === parentKey).node
          const index = parent.children.indexOf(data)
          parent.children.splice(index, 1)
          this.$Message.info('删除成功')
        },
        onCancel: () => {
   
          this.$Message.info('取消')
        }
      })
    }, 
    // 确认修改树节点
    confirmTheChange(data) {
      
      if (!this.inputContent) {
   
        this.$Notice.warning({
   
          title: '当前输入有误', 
        })
      } else {
    
        if(this.oldName !== this.inputContent){
     
          this.$Modal.confirm({
   
            title: '提示',
            content: `您确定将  “${
     this.oldName}”  重命名为 “ ${
     this.inputContent} ” 吗?`,
            onOk: () => {
   
              data.title=this.inputContent 
              this.$Message.info('修改成功')
            },
            onCancel: () => {
   
              this.$Message.info('取消')
            }
          })
          this.setStates(data)
        } else{
   
          this.setStates(data)
        }
      }
    },
    // 取消修改树节点
    cancelChange(data) {
    
      this.$Notice.info({
   
        title: '取消修改',
      })
      this.setStates(data)
    },
    // 点击Tree节点触发
    handleClickTreeNode(root, data, node) {
    
      if (this.bdId) {
   
      	// 这里是因为 filterTableMater的时候this.treeList不是响应式的,倒是选中之后再次点击别的不去除样式
        this.treeList = cloneDeep(this.treeList) 
      }
      this.$emit('handleTreeNode',node) // 触发父级组件拉去新列表
    },
    // js 递归遍历查找对象数组的某一个属性
    filterTableMater(code, arr) {
   
      for (const item of arr) {
   
        if (item.id === code) {
   
          item.selected = true
        }
        if (item.children && item.children.length) {
   
          this.filterTableMater(code, item.children)
        }
      }
    }
  }
}
</script>

// 大部分改写都在样式这里,如果有必要请仔细看一下
<style lang="less">
// 编辑部分
.btnNone {
   
  display: none;
  margin-left: 15px;
  button {
   
    margin-right: 8px;
    width: 24px;
    line-height: 0;
    height: 24px;
  }
}
.common-wrap {
   
  display: inline-block;
  line-height: 25px;
  width: 100%;
  cursor: pointer;
  span:not(.btnNone) {
   
    display: inline-block;
  }
  .edit-input {
   
    width: 90px;
    cursor: auto;
    border-radius: 3px;
    border: 1px solid #e5e5e5;
    color: #515a6e;
    padding-left: 10px;
    outline: none;
  }
  .edit-input:focus {
   
    border: 1px solid #2d8cf0 !important;
  }
}
.common-wrap:hover .btnNone {
   
  display: inline-block;
}
.common-wrap:hover {
   
  color: #275cd4;
}

// 换箭头到加号
.ivu-tree ul li {
   
  list-style: none;
  padding: 0;
  white-space: nowrap;
  outline: none;
}
.ivu-tree .ivu-tree-arrow {
   
  width: 15px;
}
.ivu-tree .ivu-icon {
    // 禁止旋转
  -webkit-transform: rotate(0deg);
  transform: rotate(0deg);
}
.ivu-tree .ivu-icon-ios-arrow-forward::before {
    // 改变tree默认的三角箭头
  content: "\F32F"; // 采用ui库中自带的icon图标
  width: 15px;
  height: 15px;
  display: block;
}
.ivu-tree .ivu-tree-arrow-open .ivu-icon-ios-arrow-forward::before {
    // 改变tree默认的三角箭头
  content: "\F417"; // 采用ui库中自带的icon图标
  display: block;
  width: 15px;
  height: 15px;
}
// 虚线
.ivu-tree {
   
  .ivu-tree-children {
   
    position: relative;
    // padding-left: 16px; // 缩进量
  }
  .ivu-tree-children::before {
   
    content: "";
    height: 100%;
    width: 1px;
    position: absolute;
    left: 7px;
    top: -12px;
    border-width: 1px;
    border-left: 1px dashed #ddd;
  }
  // 当前层最后一个节点的竖线高度固定
  .ivu-tree-children:last-child::before {
   
    height: 20px; // 可以自己调节到合适数值
  }
  // 横线
  .ivu-tree-children::after {
   
    content: "";
    width: 11px;
    height: 20px;
    position: absolute;
    left: 9px;
    top: 12px;
    border-width: 1px;
    border-top: 1px dashed #ddd;
  }
  // 去掉最顶层的虚线,放最下面样式才不会被上面的覆盖了
  & > .ivu-tree-children::after {
   
    border-top: none;
  }
  & > .ivu-tree-children::before {
   
    height: 100%;
    width: 1px;
    left: 7px;
    top: 13px;
    border-left: 1px dashed #ddd;
  }
  & > .ivu-tree-children:last-of-type::before {
   
    border-left: none;
  }
}
</style>

到这里就基本完成了,希望对有这部分需求的盆友有用处,加油💪!!!


转载:https://blog.csdn.net/ZD717822023/article/details/116804050
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场