本文介绍: ElementUI、ElementPlus组件功能很不错,但是官方的树形组件没有显示线条,感觉稍微不够大气。于是网上查了一些资料,找了很多也感觉也不够完美,最后找到一个还不错的实现方案,并且再美化改进一下,分享大家

ElementUI、ElementPlus组件功能很不错,但是官方的树形组件没有显示线条,感觉稍微不够大气。于是网上查了一些资料,找了很多也感觉也不够完美,最后找到一个还不错的实现方案,并且再美化改进一下,分享大家

一、基于Vue2+ElementUI的例子

(1)示例代码

<template>
  <div class="tree-container"&gt;
    <div style="padding: 20px;"&gt;
      <el-checkbox v-model="isSelectAll" @change="handleCheckedAllTreeNodeChange"&gt;全选/全不选</el-checkbox&gt;
      
      <el-button size="mini" style="margin-left: 20px;" @click="handleGetCheckedNodesAndKeys(true)"&gt;获取已勾选节点</el-button>
    </div>

    <el-tree
      ref="treeRef"
      show-checkbox
      icon-class="el-icon-arrow-right"
      :node-key="'id'"
      :data="treeList"
      :props="defaultProps"
      :default-expand-all="true"
      :expand-on-click-node="false"
      :default-checked-keys="[5]"
    >
      <span slot-scope="{ data }">
        <template v-if="data.children">
          <div v-if="data.children.length > 0">
            <!-- <i class="el-icon-folder" :style="'font-size: 14px; padding: 0 5px 0 5px'"/> -->
            <i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="apartment" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M908 640H804V488c0-4.4-3.6-8-8-8H548v-96h108c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H368c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h108v96H228c-4.4 0-8 3.6-8 8v152H116c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16H292v-88h440v88H620c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16zm-564 76v168H176V716h168zm84-408V140h168v168H428zm420 576H680V716h168v168z"></path></svg></i>
            <span>{{ data.id + ' - ' + data.label }}</span>
          </div>

          <div v-else>
            <i class="leaf-node-line"></i>
            <!-- <i class="el-icon-folder" :style="'padding: 0 5px 0 5px'"/> -->
            <i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="apartment" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M908 640H804V488c0-4.4-3.6-8-8-8H548v-96h108c8.8 0 16-7.2 16-16V80c0-8.8-7.2-16-16-16H368c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h108v96H228c-4.4 0-8 3.6-8 8v152H116c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16H292v-88h440v88H620c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h288c8.8 0 16-7.2 16-16V656c0-8.8-7.2-16-16-16zm-564 76v168H176V716h168zm84-408V140h168v168H428zm420 576H680V716h168v168z"></path></svg></i>
            <span>{{ data.id + ' - ' + data.label }}</span>
          </div>
        </template>

        <template v-else>
          <div style="margin-left: 0px;">
            <i class="leaf-node-line"></i>
            <!-- <i class="el-icon-document" :style="'padding: 0 5px 0 5px'"></i> -->
            <i :style="'font-size: 13px; padding: 0 5px 0 5px'"><svg viewBox="64 64 896 896" data-icon="deployment-unit" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M888.3 693.2c-42.5-24.6-94.3-18-129.2 12.8l-53-30.7V523.6c0-15.7-8.4-30.3-22-38.1l-136-78.3v-67.1c44.2-15 76-56.8 76-106.1 0-61.9-50.1-112-112-112s-112 50.1-112 112c0 49.3 31.8 91.1 76 106.1v67.1l-136 78.3c-13.6 7.8-22 22.4-22 38.1v151.6l-53 30.7c-34.9-30.8-86.8-37.4-129.2-12.8-53.5 31-71.7 99.4-41 152.9 30.8 53.5 98.9 71.9 152.2 41 42.5-24.6 62.7-73 53.6-118.8l48.7-28.3 140.6 81c6.8 3.9 14.4 5.9 22 5.9s15.2-2 22-5.9L674.5 740l48.7 28.3c-9.1 45.7 11.2 94.2 53.6 118.8 53.3 30.9 121.5 12.6 152.2-41 30.8-53.6 12.6-122-40.7-152.9zm-673 138.4a47.6 47.6 0 0 1-65.2-17.6c-13.2-22.9-5.4-52.3 17.5-65.5a47.6 47.6 0 0 1 65.2 17.6c13.2 22.9 5.4 52.3-17.5 65.5zM522 463.8zM464 234a48.01 48.01 0 0 1 96 0 48.01 48.01 0 0 1-96 0zm170 446.2l-122 70.3-122-70.3V539.8l122-70.3 122 70.3v140.4zm239.9 133.9c-13.2 22.9-42.4 30.8-65.2 17.6-22.8-13.2-30.7-42.6-17.5-65.5s42.4-30.8 65.2-17.6c22.9 13.2 30.7 42.5 17.5 65.5z"></path></svg></i>
            <span>{{ data.id + ' - ' + data.label }}</span>
          </div>
        </template>
      </span>
    </el-tree>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 树列表
      treeList: [
        {
          id: 1,
          label: "香烟 WiFi 啤酒",
          children: [
            {
              id: 3,
              label: '香烟',
              children: [
                {
                  id: 4,
                  label: '煊赫门',
                },
                {
                  id: 5,
                  label: 'ESSE双爆珠',
                  disabled: true,
                },
              ],
            },
            {
              id: 2,
              label: '后端开发技术',
              disabled: true,
              children: [
                {
                  id: 6,
                  label: 'Java编程技术',
                  children: [],
                },
                {
                  id: 7,
                  label: '数据库',
                  disabled: true,
                  children: [
                    {
                      id: 8,
                      label: '关系型数据库',
                      children: [
                        {
                          id: 9,
                          label: 'MySQL',
                        },
                        {
                          id: 10,
                          label: 'Oracle',
                          disabled: true,
                        },
                      ],
                    },
                    {
                      id: 11,
                      label: '非关系型数据库',
                      children: [
                        {
                          id: 12,
                          label: 'Redis',
                        },
                        {
                          id: 13,
                          label: 'Elasticsearch',
                          disabled: true,
                        },
                      ],
                    }
                  ],
                },
              ],
            },
          ],
        },
      ],

      // 对象关系映射
      defaultProps: {
        label: 'label',
        children: 'children',
      },

      // 是否全选所有节点标志
      isSelectAll: false,
    };
  },
  methods: {
    /**
     * 是否全选所有节点
     */
    handleCheckedAllTreeNodeChange() {
      if (this.isSelectAll) {
        // 深度遍历将子节点全选中
        for (let i = 0; i < this.treeList.length; i++) {
          this.$refs.treeRef.setChecked(this.treeList[i], true, true);
        }
      } else {
        // 全部不选中
        this.$refs.treeRef.setCheckedNodes([]);
      }

      const leafOnly = true;
      this.handleGetCheckedNodesAndKeys(leafOnly);
    },

    /**
     * 获取当前已被选中的节点集合
     */
    handleGetCheckedNodesAndKeys(leafOnly) {
      console.log('getCheckedNodes =>', this.$refs.treeRef.getCheckedNodes(leafOnly));
      console.log('getCheckedKeys =>', this.$refs.treeRef.getCheckedKeys(leafOnly));
    }
  },
}
</script>

<style lang="less" scoped>
  // 设置树形组件节点定位和左内边距
  .tree-container /deep/ .el-tree-node {
    position: relative;
    padding-left: 13px;
  }

  // 设置树形组件节点的 before 伪类样式
  .tree-container /deep/ .el-tree-node:before {
    width: 1px;
    height: 100%;
    content: '';
    position: absolute;
    top: -38px;
    bottom: 0;
    left: 0;
    right: auto;
    border-width: 1px;
    border-left: 1px solid #b8b9bb;
  }

  // 设置树形组件节点的 after 伪类样式
  .tree-container /deep/ .el-tree-node:after {
    width: 13px;
    height: 13px;
    content: '';
    position: absolute;
    left: 0;
    right: auto;
    top: 12px;
    bottom: auto;
    border-width: 1px;
    border-top: 1px solid #b8b9bb;
  }

  // 设置树形组件节点的左边框不显示
  .tree-container /deep/ .el-tree > .el-tree-node:before {
    border-left: none;
  }

  // 设置树形组件首节点的顶部边框不显示
  .tree-container /deep/ .el-tree > .el-tree-node:after {
    border-top: none;
  }

  // 设置树形组件末节点的 before 伪类的高度
  .tree-container /deep/ .el-tree .el-tree-node:last-child:before {
    height: 50px;
  }

  // 设置树形组件节点字体大小、以及取消左内边距
  .tree-container /deep/ .el-tree .el-tree-node__content {
    color: #000;
    font-size: 14px;
    padding-left: 0 !important;
  }

  // 设置树形组件孩子节点左内边距
  .tree-container /deep/ .el-tree .el-tree-node__children {
    padding-left: 11.5px;
  }

  // 设置树形组件复选框左右外边距
  .tree-container /deep/ .el-tree .el-tree-node__content > label.el-checkbox {
    margin: 0 5px 0 5px !important;
  }

  // 设置树形组件展开图标定位、图层、内边距
  .tree-container /deep/ .el-tree .el-tree-node__expand-icon {
    position: relative;
    z-index: 99;
  }

  // 设置树形组件叶子节点的默认图标不显示
  .tree-container /deep/ .el-tree .el-tree-node__expand-icon.is-leaf {
    display: none;
  }

  // 设置树形组件叶子节点的横线
  .tree-container /deep/ .el-tree .leaf-node-line {
    width: 23px;
    height: 13px;
    content: '';
    position: absolute;
    left: 13px;
    right: auto;
    top: 12px;
    bottom: auto;
    border-width: 1px;
    border-top: 1px solid #b8b9bb;
  }

  // 设置树形组件有叶子节点的左外边距
  .tree-container /deep/ .el-tree .el-tree-node__content:has(.is-leaf){
    // color: aqua;
    margin-left: 24px !important;
  }
</style>

(2)效果如下~

二、基于Vue3+ElementPlus的例子

1.普通示例

(1)示例代码

<template>
  <div class="element-plus-tree">
    <el-tree
      :data="treeData"
      :props="defaultProps"
      :show-checkbox="false"
      :default-expand-all="true"
      :highlight-current="true"
      :expand-on-click-node="false"
      :indent="22"
    >
      <template #default="{ node, data }">
        <span v-if="!node.isLeaf" style="display: flex; align-items: center;">
          <el-icon v-if="node.expanded" style="margin: 0 6px 0 2px;" size="16"><FolderOpened /></el-icon>
          <el-icon v-else style="margin: 0 6px 0 2px;" size="16"><Folder /></el-icon>
          <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
        </span>

        <span v-else style="display: flex; align-items: center;">
          <el-icon style="margin: 0 6px 0 2px;" size="16"><Document /></el-icon>
          <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
        </span>
      </template>
    </el-tree>
  </div>
</template>

<script setup>
import { onMounted, ref, getCurrentInstance } from 'vue'

// 代理对象
const { proxy } = getCurrentInstance()

// 树型数据
const treeData = ref(
  [
    {
      label: '香烟 WiFi 啤酒',
      expanded: true,
      children: [
        {
          label: '香烟',
          children: [
            {
              label: '煊赫门'
            },
            {
              label: 'ESSE双爆珠'
            }
          ],
        },
        {
          label: '后端开发技术',
          children: [
            {
              label: 'Java编程技术'
            },
            {
              label: 'Python编程技术'
            }
          ],
        },
        {
          label: '数据库',
          children: [
            {
              label: '关系型数据库',
              children: [
                {
                  label: 'MySQL'
                },
                {
                  label: 'Oracle'
                }
              ],
            },
            {
              label: '非关系型数据库',
              children: [
                {
                  label: 'Redis'
                },
                {
                  label: 'Elasticsearch'
                }
              ],
            }
          ],
        },
        {
          label: 'AI人工智能'
        },
      ],
    },
    {
      label: '火腿 iPad 泡面'
    },
  ]
)

// 树节点属性映射关系
const defaultProps = {
  children: 'children',
  label: 'label',
}

/**
 * 树组件点击事件句柄方法
 */
const handleNodeClick = (node, data) => {
  console.log(
        '%c 树组件点击事件句柄方法 %c handleNodeClick',
        'padding: 1px; background-color: #35495e; color: #fff',
        'padding: 1px; background-color: #5e7ce0; color: #fff',
      )
      console.log('%c ∟ %c node %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', node)
      console.log('%c ∟ %c data %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', data)
}

onMounted(() => {
  // ...
})
</script>

<style lang="less" scoped>
  .element-plus-tree {
    padding: 100px;

    :deep(.el-tree) {

      .el-tree-node {
        
        /* ^ 所有节点 */
        i.el-tree-node__expand-icon {
          padding: 6px;

          &amp;::before {
            font-family: element-ui-icons;
            font-style: normal;
            content: "e6d9";
            color: #000000;
            border: 1px solid #606266;
            border-radius: 2px;
          }

          svg {
            display: none; // 隐藏所有节点的 svg 图标
          }
        }
        /* / 所有节点 */

        /* ^ 已展开的父节点 */
        i.el-tree-node__expand-icon.expanded {
          transform: rotate(0deg); // 取消旋转
          -webkit-transform: rotate(0deg); // 取消旋转

          &amp;::before {
            font-family: element-ui-icons;
            font-style: normal;
            content: "e6d8";
            color: #000000;
            border: 1px solid #606266;
            border-radius: 2px;
          }
        }
        /* / 已展开的父节点 */

        /* ^ 叶子节点 */
        i.el-tree-node__expand-icon.is-leaf {

          &amp;::before {
            display: none;
          }
        }
        /* / 叶子节点 */

        /* ^ 复选框 */
        .el-checkbox {
          margin: 0 7px 0 2px;

          .el-checkbox__inner {
            width: 14px;
            height: 14px;
            border-radius: 2px;
            border: 1px solid #bbb;
          }

          .el-checkbox__input.is-checked .el-checkbox__inner,
          .el-checkbox__input.is-indeterminate .el-checkbox__inner {
            border: 1px solid #5e7ce0;
          }
        }
        /* / 复选框 */

        .el-tree-node__content {
          small {
            font-size: 13px;
          }
        }
      }
    }
  }
</style>

(2)效果如下~

2.高级示例

(1)示例代码

<template>
  <div class="element-plus-tree">

    <el-tree
      :data="treeData"
      :props="defaultProps"
      :show-checkbox="true"
      :default-expand-all="true"
      :highlight-current="true"
      :expand-on-click-node="false"
      :indent="22"
    >
      <template #default="{ node, data }">
        <span v-if="!node.isLeaf" style="display: flex; align-items: center;">
          <el-icon v-if="node.expanded" style="margin: 0 6px 0 0px;" size="16"><FolderOpened /></el-icon>
          <el-icon v-else style="margin: 0 6px 0 0px;" size="16"><Folder /></el-icon>
          <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
        </span>

        <span v-else style="display: flex; align-items: center;">
          <el-icon style="margin: 0 6px 0 0px;" size="16"><Document /></el-icon>
          <small @click="handleNodeClick(node, data)">{{ node.label }}</small>
        </span>
      </template>
    </el-tree>
  </div>
</template>

<script setup>
import { onMounted, ref, getCurrentInstance } from 'vue'

// 代理对象
const { proxy } = getCurrentInstance()

// 树型数据
const treeData = ref(
  [
    {
      label: '香烟 WiFi 啤酒',
      expanded: true,
      children: [
        {
          label: '香烟',
          children: [
            {
              label: '煊赫门'
            },
            {
              label: 'ESSE双爆珠'
            }
          ],
        },
        {
          label: '后端开发技术',
          children: [
            {
              label: 'Java编程技术'
            },
            {
              label: 'Python编程技术'
            }
          ],
        },
        {
          label: '数据库',
          children: [
            {
              label: '关系型数据库',
              children: [
                {
                  label: 'MySQL'
                },
                {
                  label: 'Oracle'
                }
              ],
            },
            {
              label: '非关系型数据库',
              children: [
                {
                  label: 'Redis'
                },
                {
                  label: 'Elasticsearch'
                }
              ],
            }
          ],
        },
        {
          label: 'AI人工智能'
        },
      ],
    },
    {
      label: '火腿 iPad 泡面'
    },
  ]
)

// 树节点属性映射关系
const defaultProps = {
  children: 'children',
  label: 'label',
}

/**
 * 树组件点击事件句柄方法
 */
const handleNodeClick = (node, data) => {
  console.log(
    '%c 树组件点击事件句柄方法 %c handleNodeClick',
    'padding: 1px; background-color: #35495e; color: #fff',
    'padding: 1px; background-color: #5e7ce0; color: #fff',
  )
  console.log('%c ∟ %c node %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', node)
  console.log('%c ∟ %c data %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', data)
}

onMounted(() => {
  // ...
})
</script>

<style lang="less" scoped>
  .element-plus-tree {
    padding: 100px;

    :deep(.el-tree) {

      /* ---- ---- ---- ---- ^(节点对齐)---- ---- ---- ---- */
      .el-tree-node {
        
        /* ^ 所有节点 */
        i.el-tree-node__expand-icon {
          padding: 6px;

          &::before {
            font-family: element-ui-icons;
            font-style: normal;
            content: "e6d9";
            color: #000000;
            border: 1px solid #606266;
            border-radius: 2px;
          }

          svg {
            display: none; // 隐藏所有节点的 svg 图标
          }
        }
        /* / 所有节点 */

        /* ^ 已展开的父节点 */
        i.el-tree-node__expand-icon.expanded {
          transform: rotate(0deg); // 取消旋转
          -webkit-transform: rotate(0deg); // 取消旋转

          &::before {
            font-family: element-ui-icons;
            font-style: normal;
            content: "e6d8";
            color: #000000;
            border: 1px solid #606266;
            border-radius: 2px;
          }
        }
        /* / 已展开的父节点 */

        /* ^ 叶子节点 */
        i.el-tree-node__expand-icon.is-leaf {

          &::before {
            display: none;
          }
        }
        /* / 叶子节点 */

        /* ^ 复选框 */
        .el-checkbox {
          margin: 0 7px 0 2px;

          .el-checkbox__inner {
            width: 14px;
            height: 14px;
            border-radius: 2px;
            border: 1px solid #bbb;
          }

          .el-checkbox__input.is-checked .el-checkbox__inner,
          .el-checkbox__input.is-indeterminate .el-checkbox__inner {
            border: 1px solid #5e7ce0;
          }
        }
        /* / 复选框 */

        .el-tree-node__content {
          small {
            font-size: 13px;
          }
        }
      }
      /* ---- ---- ---- ---- /(节点对齐)---- ---- ---- ---- */

      /* ---- ---- ---- ---- ^(文字高亮)---- ---- ---- ---- */
      .el-tree-node.is-current {
        .el-tree-node__content {
          small {
            color: #5e7ce0;
          }
        }

        .el-tree-node__children {
          small {
            color: unset;
          }
        }
      }
      /* ---- ---- ---- ---- /(文字高亮)---- ---- ---- ---- */

      /* ---- ---- ---- ---- ^(新增辅助线)---- ---- ---- ---- */
      /* ^ 树节点 */
      .el-tree-node {
        position: relative;
        width: auto;
        // width: max-content; // 显示文字宽度
        padding-left: 13px;

        &::before {
          width: 1px;
          height: 100%;
          content: '';
          position: absolute;
          top: -38px;
          bottom: 0;
          left: 0;
          right: auto;
          border-width: 1px;
          border-left: 1px solid #b8b9bb;
        }

        &::after {
          width: 13px;
          height: 13px;
          content: '';
          position: absolute;
          z-index: 0;
          left: 0;
          right: auto;
          top: 12px;
          bottom: auto;
          border-width: 1px;
          border-top: 1px solid #b8b9bb;
        }

        .el-tree-node__content {
          position: relative;
          z-index: 1;
          color: #000;
          padding-left: 0 !important;

          /* ^ 复选框 */
          .el-checkbox {
            margin: 0 10px 0 5.5px;
          }
          /* / 复选框 */
        }
        
        .el-tree-node__children {
          padding-left: 12px;
        }

        &:last-child::before {
          height: 50px;
        }
      }
      /* / 树节点 */

      /* ^ 第一层节点 */
      > .el-tree-node {
        padding-left: 0;

        &::before {
          border-left: none;
        }

        &::after {
          border-top: none;
        }
      }
      /* / 第一层节点 */

      /* ^ 叶子节点 */
      i.el-tree-node__expand-icon.is-leaf {
        display: none;
      }
      /* / 叶子节点 */

      /* ^ 设置子节点左外边距 */
      .el-tree-node__content:has(.is-leaf) {
        // color: #00ffff;
        margin-left: 25px !important;
      }
      /* / 设置子节点左外边距 */
      /* ---- ---- ---- ---- /(新增辅助线)---- ---- ---- ---- */
    }
  }
</style>

(2)效果如下~

三、基于Vue3+DevUI的附赠例子

1.导入相关依赖

npmvue-devui -D

2.官方文档

https://vue-devui.github.io/

3.普通示例

(1)示例代码

<template>
  <div class="vue-devui-tree">
 
    <div>
      <span>是否展开所有节点&nbsp;:&nbsp;</span>
       <el-switch
          v-model="isExpandAllNodes"
          size="small"
          @change="handleIsExpandAllNodesChange"
        />
    </div>
 
    <DevTree ref="treeRef" :data="data">
      <template #icon="{ nodeData, toggleNode }">
        <span
          v-if="!nodeData.isLeaf"
          class="devui-tree__node-folder"
          @click="
            (event) => {
              event.stopPropagation();
              toggleNode(nodeData);
            }
          "
        >
          <!-- 未展开图标 -->
          <svg v-if="!nodeData.expanded" style="margin-right: 8px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
            <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
              <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
              <path fill="#666666" d="M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z"></path>
            </g>
          </svg>
 
          <!-- 已展开图标 -->
          <svg v-else style="margin-right: 8px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
            <g stroke-width="1" fill="none" fill-rule="evenodd">
              <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
              <rect x="4" y="7" width="8" height="2" fill="#666666"></rect>
            </g>
          </svg>
        </span>
 
        <span
          v-else
          class="devui-tree__node-leaf"
        >
        </span>
      </template>
 
      <template #content="{ nodeData }">
        <svg style="margin-right: 7px" viewBox="0 0 16 16" width="16" height="16">
          <path
            :d="`${
              nodeData.isLeaf
                ? 'M13,6 L9,6 L9,5 L9,2 L3,2 L3,14 L13,14 L13,6 Z M12.5857864,5 L10,2.41421356 L10,5 L12.5857864,5 Z M2,1 L10,1 L14,5 L14,15 L2,15 L2,1 Z'
                : nodeData.expanded
                ? 'M16,6 L14,14 L2,14 L0,6 L16,6 Z M14.7192236,7 L1.28077641,7 L2.78077641,13 L13.2192236,13 L14.7192236,7 Z M6,1 L8,3 L15,3 L15,5 L14,5 L14,4 L7.58578644,4 L5.58578644,2 L2,2 L2,5 L1,5 L1,1 L6,1 Z'
                : 'M14,6 L14,5 L7.58578644,5 L5.58578644,3 L2,3 L2,6 L14,6 Z M14,7 L2,7 L2,13 L14,13 L14,7 Z M1,2 L6,2 L8,4 L15,4 L15,14 L1,14 L1,2 Z'
            }`"
            stroke-width="1"
            fill="#8a8e99"
          ></path>
        </svg>
        <small>{{ nodeData.label }}</small>
      </template>
    </DevTree>
 
  </div>
</template>
 
<script setup>
import { onMounted, ref, getCurrentInstance, toRaw } from 'vue'
import { Tree as DevTree } from "vue-devui"
import 'vue-devui/style.css'
 
// 代理对象
const currentInstance = getCurrentInstance()
 
// 树型DOM引用
const treeRef = ref()
 
// 是否展开所有节点
const isExpandAllNodes = ref(false)
 
// 树型数据
const data = ref(
  [
    {
      label: '香烟 WiFi 啤酒',
      expanded: true,
      children: [
        {
          label: '香烟',
          children: [
            {
              label: '煊赫门'
            },
            {
              label: 'ESSE双爆珠'
            }
          ],
        },
        {
          label: '后端开发技术',
          children: [
            {
              label: 'Java编程技术'
            },
            {
              label: 'Python编程技术'
            }
          ],
        },
        {
          label: '数据库',
          children: [
            {
              label: '关系型数据库',
              children: [
                {
                  label: 'MySQL'
                },
                {
                  label: 'Oracle'
                }
              ],
            },
            {
              label: '非关系型数据库',
              children: [
                {
                  label: 'Redis'
                },
                {
                  label: 'Elasticsearch'
                }
              ],
            }
          ],
        },
        {
          label: 'AI人工智能'
        },
      ],
    },
    {
      label: '火腿 iPad 泡面'
    },
  ]
)
 
/**
 * 是否展开所有节点句柄方法
 */
const handleIsExpandAllNodesChange = (val) => {
  if (val) {
    handleExpandAllNodes()
  } else {
    handleCollapseAllNodes()
  }
}
 
/**
 * 将树节点全部展开句柄方法
 */
const handleExpandAllNodes = () => {
  treeRef.value.treeFactory.expandAllNodes()
}
 
/**
 * 将树节点全部收起句柄方法
 */
const handleCollapseAllNodes = () => {
  const treeFactory = treeRef.value.treeFactory
  const treeData = treeFactory.treeData._rawValue
  recursionCollapseNode(treeData)
}

/**
 * 递归收起节点句柄方法
 */
const recursionCollapseNode = (treeData) => {
  for (let node of treeData) {
    if (node.expanded) {
      treeRef.value.treeFactory.collapseNode(node)
    }
 
    if (node.children && node.children.length > 0) {
      recursionCollapseNode(node.children)
    }
  }
}
 
onMounted(
  async () => {
    // Todo
  }
)
</script>
 
<style lang="less" scoped>
  .vue-devui-tree {
    padding: 100px;
 
    :deep(.devui-tree) {
 
      .devui-tree__node-leaf {
        // display: none;
        position: relative;
        width: 16px;
        height: 16px;
        margin-right: 6px;
        // background-color: #ccc;
        // border: 1px solid #ccc;
        // border-radius: 2px;
      }
 
      .devui-tree__node-vline {
        margin-left: 4.5px;
        background-color: #cccccc !important;
      }
 
      .devui-tree__node-hline {
        margin-left: 4.5px;
        background-color: #cccccc !important;
      }
 
      .devui-tree__node {
        
        .devui-tree__node-content {
          
          &.active {
            // background-color: unset;
 
            small {
              color: #5e7ce0;
            }
          }
 
          &:hover {
            // background-color: unset;
          }
 
          small {
            font-size: 13px;
            transition: all ease 0.3s;
 
            &:hover {
              color: #5e7ce0;
            }
          }
        }
      }
    }
  }
</style>

(2)效果如下~

4.高级示例

(1)示例代码

<template>
  <div class="vue-devui-tree">
 
    <DevTree ref="treeRef" :data="data" check>
      <template #icon="{ nodeData, toggleNode }">
        <span
          v-if="!nodeData.isLeaf"
          class="devui-tree__node-folder"
          @click="
            (event) => {
              event.stopPropagation();
              toggleNode(nodeData);
            }
          "
        >
          <!-- 未展开图标 -->
          <svg v-if="!nodeData.expanded" style="margin-right: 7px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
            <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
              <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
              <path fill="#666666" d="M8.75,4 L8.75,7.25 L12,7.25 L12,8.75 L8.749,8.75 L8.75,12 L7.25,12 L7.249,8.75 L4,8.75 L4,7.25 L7.25,7.25 L7.25,4 L8.75,4 Z"></path>
            </g>
          </svg>
 
          <!-- 已展开图标 -->
          <svg v-else style="margin-right: 7px" width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg">
            <g stroke-width="1" fill="none" fill-rule="evenodd">
              <rect x="0.5" y="0.5" width="15" height="15" rx="2" stroke="#666666"></rect>
              <rect x="4" y="7" width="8" height="2" fill="#666666"></rect>
            </g>
          </svg>
        </span>
 
        <span
          v-else
          class="devui-tree__node-leaf"
        >
        </span>
      </template>
 
      <template #content="{ nodeData }">
        <svg style="margin: 0 7px" viewBox="0 0 16 16" width="16" height="16">
          <path
            :d="`${
              nodeData.isLeaf
                ? 'M13,6 L9,6 L9,5 L9,2 L3,2 L3,14 L13,14 L13,6 Z M12.5857864,5 L10,2.41421356 L10,5 L12.5857864,5 Z M2,1 L10,1 L14,5 L14,15 L2,15 L2,1 Z'
                : nodeData.expanded
                ? 'M16,6 L14,14 L2,14 L0,6 L16,6 Z M14.7192236,7 L1.28077641,7 L2.78077641,13 L13.2192236,13 L14.7192236,7 Z M6,1 L8,3 L15,3 L15,5 L14,5 L14,4 L7.58578644,4 L5.58578644,2 L2,2 L2,5 L1,5 L1,1 L6,1 Z'
                : 'M14,6 L14,5 L7.58578644,5 L5.58578644,3 L2,3 L2,6 L14,6 Z M14,7 L2,7 L2,13 L14,13 L14,7 Z M1,2 L6,2 L8,4 L15,4 L15,14 L1,14 L1,2 Z'
            }`"
            stroke-width="1"
            fill="#8a8e99"
          ></path>
        </svg>
        <small>{{ nodeData.label }}</small>
      </template>
    </DevTree>
 
  </div>
</template>
 
<script setup lang="ts">
import { onMounted, ref, getCurrentInstance, toRaw, useCssModule, provide } from 'vue'
import { Tree as DevTree } from "vue-devui"
import 'vue-devui/style.css'
 
// 代理对象
const proxy = getCurrentInstance()
 
// 树型DOM引用
const treeRef = ref(null);
 
// 树型数据
const data = ref(
  [
    {
      label: '香烟 WiFi 啤酒',
      expanded: true,
      children: [
        {
          label: '香烟',
          children: [
            {
              label: '煊赫门'
            },
            {
              label: 'ESSE双爆珠'
            }
          ],
        },
        {
          label: '后端开发技术',
          children: [
            {
              label: 'Java编程技术'
            },
            {
              label: 'Python编程技术'
            }
          ],
        },
        {
          label: '数据库',
          children: [
            {
              label: '关系型数据库',
              children: [
                {
                  label: 'MySQL'
                },
                {
                  label: 'Oracle'
                }
              ],
            },
            {
              label: '非关系型数据库',
              children: [
                {
                  label: 'Redis'
                },
                {
                  label: 'Elasticsearch'
                }
              ],
            }
          ],
        },
        {
          label: 'AI人工智能'
        },
      ],
    },
    {
      label: '火腿 iPad 泡面'
    },
  ]
)
 
onMounted(() => {
  // 展开全部节点
  if (treeRef && treeRef.value) {
    const treeFactory: any = treeRef.value['treeFactory']
    treeFactory.expandAllNodes()
  }
})
</script>
 
<style lang="less" scoped>
  .vue-devui-tree {
    padding: 100px;
 
    :deep(.devui-tree) {
 
      .devui-tree__node-leaf {
        // display: none;
        position: relative;
        width: 16px;
        height: 16px;
        margin-right: 6px;
        // background-color: #ccc;
        // border: 1px solid #ccc;
        // border-radius: 2px;
      }
 
      .devui-tree__node-vline {
        margin-left: 4.5px;
        background-color: #cccccc !important;
      }
 
      .devui-tree__node-hline {
        margin-left: 4.5px;
        background-color: #cccccc !important;
      }
 
      .devui-tree__node {
        
        .devui-tree__node-content {
          
          &.active {
            // background-color: unset;
 
            small {
              color: #5e7ce0;
            }
          }
 
          // &:hover {
            // background-color: unset;
          // }
 
          small {
            font-size: 13px;
            transition: all ease 0.3s;
 
            &:hover {
              color: #5e7ce0;
            }
          }
        }
      }
    }
  }
</style>

(2)效果如下~

四、参考资料

vue树形控件tree的使用方法_vue.js_脚本之家

Vue DevUI

原文地址:https://blog.csdn.net/Cai181191/article/details/127756640

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_7111.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注