【Element Plus】树形控件懒加载+检索

318 阅读3分钟

事情是这样的,最近项目新增一个需求是:对行政区域进行关键词检索。但是由于行政区比较多,一次性渲染数据会导致组件崩掉,因此上一个前端是通过展开树节点逐级查询。Fine。

但是el-tree-select组件提供的filter-method或filter-node-method检索方法对懒加载方式无用,它是针对已有节点的检索,即使重新赋值data也不起作用。因此需要自己造轮子实现需求。

技术栈:Vue(3.5.12)、TypeScript(5.1.6)、Element Plus(2.8.8)、UnoCSS(0.55.2)

实现步骤

  • template模板

主要是渲染输入框+树控件,并且添加一些交互细节。

<template>
  <div class="select-wrap" @click="togglePanel" v-click-outside="handleClose">
    <el-input class="w-[100%]" v-model="queryKeywold" placeholder="请选择行政区域" @input="updateTreeData">
      <template #suffix>
        <SvgIcon
          :class="{ 'rotate-180': visible }"
          name="arrow-bottom"
          class="text-14 transition-transform transition-duration-0.3s transition-ease-in-out color-[#a8abb2]"
        ></SvgIcon>
      </template>
    </el-input>

    <div :class="{ 'is-focus': visible }" class="select-wrap-pop pop-with-border" @click.stop>
      <div class="pop-group-wrap" v-loading="loading">
        <el-tree
          ref="treeSelectRef"
          accordion
          check-strictly
          lazy
          :load="load"
          :props="treeProps"
          :node-key="nodeKey"
          :data="treeData"
          :model-value="modelValue"
          placeholder="请选择行政区域"
          class="area-tree-select w-100%"
          :class="[subList.length && 'first-pointer-events-none']"
          :default-expanded-keys="expandedKeys"
          :render-after-expand="false"
          :expand-on-click-node="false"
          :teleported="false"
          @node-click="nodeClick"
          @change="handler"
        >
          <template #default="{ data }">
            <div class="tree-node position-relative">
              <span :class="{ 'color-#172a88': data.value === modelValue, 'cursor-not-allowed': data.disabled }">{{
                data.label
              }}</span>
            </div>
          </template>
        </el-tree>
      </div>
    </div>
  </div>
</template>

  • TypeScript脚本

没啥好说的,就是普通写。

/**是否展示下拉面板 */
const visible = ref<boolean>(false)
/** 是否加载中 */
const loading = ref<boolean>(false)

/** 树数据 */
const treeData = ref<dataType[]>([])
/** 当前选中节点 */
const currentNode = ref<dataType>({})
/** 关键词检索 */
const queryKeywold = ref('')
/** 临时检索关键词 -- 加载下一层级节点用 */
const tempKeyword = ref('')

/** 关键词过滤数据 */
const updateTreeData = debounce(value => {
  visible.value = true
  loading.value = true
  tempKeyword.value = value
  // 接口请求,组装数据...
}, 300)

/** 切换面板显隐 */
const togglePanel = () => {
  visible.value = !visible.value
  visible.value && updateTreeData('')
}

/** 关闭面板 */
const handleClose = () => {
  visible.value = false
  queryKeywold.value = currentNode.value.label
}

  • CSS样式

CSS最主要的就是还原输入框右侧icon的动画、以及下拉框面板的样式。(中间小三角也还原了(得意))

.select-wrap {
  width: 360px;
  height: 36px;
  max-height: 200px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-radius: 4px;
  color: var(--el-text-color-regular);
  position: relative;

  .select-wrap-pop {
    display: none;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    height: 200px;
    position: absolute;
    margin-top: 10px;
    z-index: 1000;
    transform: translateZ(0);
    transition: 0.3s;

    .pop-group-wrap {
      width: 100%;
      height: 100%;
      max-height: 200px;
      overflow-x: hidden;
      overflow-y: auto;
      padding: 10px 0;
      position: relative;

      .active {
        color: var(--el-color-primary) !important;
      }
    }

    /* 滚动条整体 */
    .pop-group-wrap::-webkit-scrollbar {
      width: 8px;
    }

    /* 滚动条滑块 */
    .pop-group-wrap::-webkit-scrollbar-thumb {
      border-radius: 4px;
    }

    /* 滚动条轨道 */
    .pop-group-wrap::-webkit-scrollbar-track {
      border-radius: 5px;
    }

    &.is-focus {
      display: block;
    }
  }
}

 小三角形部分:

.pop-with-border {
  background: #fff;
  position: relative;
  border-radius: 4px;
  box-shadow: 0px 0px 10px rgb(0 0 0 / 12%);

  &::before,
  &::after {
    top: -6px;
    border: 6px solid transparent;
    border-top: 0;
    border-bottom-color: #fff;
    content: '';
    display: block;
    width: 0;
    height: 0;
    left: calc(50% - 6px);
    overflow: hidden;
    position: absolute;
    z-index: 101;
  }
  &::before {
    top: -7px;
    border-bottom-color: #ddd;
    z-index: 99;
  }
}

实现效果

编辑

细节注意

  • 点击整个元素外部时,需要收起面板,并且将输入框的值重置成已选节点的label。
  • 如果已选择节点,更改输入框内容,再进行下一节点加载时,关键词需要用临时值。

以上就是树形控件懒加载+检索的实现啦^-^