ElTree组件可以带线了?

近期有需求希望树形控件可以带线,UI可能觉得这样逼格会高点。无奈我们的ElTree组件不带这个功能,于是手动基于ElTree来扩展一个,方便平时开发中使用。

先看下组件扩展后的样子

嗯,这是目前扩展后的功能,有了上图的样子后,就得定义使用姿势,然后再看如何实现,先看下使用姿势

js 复制代码
<template> 
  <el-line-tree 
    style="max-width: 600px" 
    :data="data" 
    @node-click="handleNodeClick" 
  /> 
</template>

主要使用姿势和ElTree一样,只是新增了几个属性控制线的颜色和弧度。

代码全部黏贴,比较浪费空间,可以点这个文档地址去查看

有了上面的ui及使用姿势,接下来就是看看如何实现

  1. 因为要保留ElTree组件的所有功能,且扩张其功能,我们先把$attrs绑定给el-tree,然后把el-tree的default和empty插槽给开放出来,然后重写default插槽即可,这样就把el-tree组件的所有功能都继承了。
js 复制代码
<template>
  <el-tree
    :class="ns.b()"
    :style="treeStyle"
    v-bind="$attrs"
    :expand-on-click-node="expandOnClickNode"
  >
    <template #default="{ node, data }">
      <div
        :class="nsNode.e('collapse')"
        @click.stop="handleExpandIconClick(node)"
      >
        <slot name="collapse" v-bind="{ node, data }">
          <el-icon
            :class="[
              nsNode.is('leaf', node.isLeaf),
              {
                expanded: !node.isLeaf,
              },
            ]"
            size="15"
            v-bind="iconProps"
          >
            <component :is="icon ? icon : node.expanded ? Expand : PutAway" />
          </el-icon>
        </slot>
      </div>

      <div
        :class="[
          nsNode.e('content'),
          showContentLine ? nsNode.m('content-line') : '',
        ]"
        :level="node.level"
      >
        <slot v-bind="{ node, data }">
          <span>{{ node.label }}</span>
        </slot>
      </div>
    </template>
    <template #empty>
      <slot name="empty" />
    </template>
  </el-tree>
</template>

<script lang="ts" setup>
import { computed } from 'vue'
import { ElIcon, ElTree, useNamespace } from 'element-plus'
import * as IconsVue from '@element-plus/components/icons-vue'
import { lineTreeEmits, lineTreeProps } from './line-tree'
const { Expand, PutAway } = IconsVue

defineOptions({
  name: 'ElLineTree',
  inheritAttrs: false,
})
const ns = useNamespace('line-tree')
const nsNode = useNamespace('line-tree-node')

const props = defineProps(lineTreeProps)
defineEmits(lineTreeEmits)

const treeStyle = computed(() => {
  const prefix = `--${ns.namespace.value}`
  return {
    [`${prefix}-line-tree-line-color`]: props.lineColor,
    [`${prefix}-line-tree-line-radius`]: props.lineRadius,
    [`${prefix}-line-tree-collapse-width`]: props.collapseWidth,
  }
})

const handleExpandIconClick = (node: any) => {
  if (node.isLeaf || !props.expandOnClickNode) return
  node.expanded ? node.collapse() : node.expand()
}
</script>

一键省流,上面代码主要给自定义节点中添加了一个collapse元素和内容元素,这个collapse元素很重要,因为后续的连线需要基于它来计算位置,其他不用管,因为主要是css连的线,后面才是重点

css连线

  1. 先看下el-tree的dom结构
  • .el-tree-node元素包含 .el-tree-node__content元素和 .el-tree-node__children元素
  • .el-tree-node__content元素包含三角箭头,checkbox,loading, .el-line-tree-node__collapse, .el-line-tree-node__content这5个元素,只不过checkbox,loading元素没渲染出来。

  • 注意.el-line-tree-node__collapse, .el-line-tree-node__content元素是我们新增的元素。

  • .el-tree-node__children元素又包含.el-tree-node元素,这也是递归渲染出来的结果

  1. 先画折线
  • 根据border-left和border-bottom画出2条线
  • 宽度需要为折叠器的一半,left需要偏移折叠器折叠器一半且-1,因为线本身有1px的宽度
  • bottom则为节点高度减去折叠器高度,其实我认为50%更好,这还得根据不同节点高度去尝试
  • 这样就把这个折线画好了,但是你会发现有断层,接下来就把断层补上
  1. 断层线补上
  • 给.el-tree-node__children元素添加伪元素
  • 主要说下left值,为折叠器的一半,-1是为了平衡border-left值为1的效果
  • 所以到这里,线已出来了,也知道了折叠器元素宽高的重要性了

总结

这个组件本质就是通过css伪元素设置的,并没有修改el-tree组件的源代码,其实我先前的做法是通过js控制的。 还有希望el-tree组件能够开放一个折叠的插槽就完美了

在上面再包一层插槽不香吗,你都想到了让用户传入icon来换图标,咋不再做的更通用点呢?

当然element-plus组件确实帮助我们开发节省了大量时间,还是非常感谢elp团队的。

由于平时开发中,也基于element-plus组件开发了一些通用组件,有element-plus组件没有的组件,也有增强版的组件(比如上述的line-tree组件)。该组件库叫 element-plus-x

element-plus-x 文档地址

element-plus-x github地址

喜欢的同学,希望可以给个小星星,谢谢

相关推荐
OpenTiny社区2 分钟前
盘点字体性能优化方案
前端·javascript
FogLetter6 分钟前
深入浅出React Hooks:useEffect那些事儿
前端·javascript
Savior`L7 分钟前
CSS知识复习4
前端·css
0wioiw022 分钟前
Flutter基础(前端教程④-组件拼接)
前端·flutter
花生侠1 小时前
记录:前端项目使用pnpm+husky(v9)+commitlint,提交代码格式化校验
前端
一涯1 小时前
Cursor操作面板改为垂直
前端
我要让全世界知道我很低调1 小时前
记一次 Vite 下的白屏优化
前端·css
1undefined21 小时前
element中的Table改造成虚拟列表,并封装成hooks
前端·javascript·vue.js
paopaokaka_luck1 小时前
基于SpringBoot+Vue的非遗文化传承管理系统(websocket即时通讯、协同过滤算法、支付宝沙盒支付、可分享链接、功能量非常大)
java·数据库·vue.js·spring boot·后端·spring·小程序