乐趣区

关于javascript:vue3-ElementUI封装实现TreeSelect-支持搜索功能

前些天实现了一个树结构的下拉组件,可最近又让我反对下拉搜寻性能,查了一圈没有找到适合的,只好明天本人写了一下,对付能用吧,反对搜寻性能了最起码,可有些参数还是要配置的

<template>
  <div>
    <el-popover
      v-model:visible="popVisible"
      placement="bottom"
      :width="popoverWidth"
      trigger="click"
    >
      <template #reference>
        <el-input
          v-model="filterText"
          v-bind="$attrs"
          :placeholder="placeholder"
          @blur="handleBlur"
          @focus="handleFocus"
        >
          <template #suffix>
            <div class="suffix" @click="handleIcon">
              <i :class="`el-icon-arrow-${popVisible ?'up':'down'}`" />
            </div>
          </template>
        </el-input>
      </template>
      <el-tree
        ref="tree"
        class="filter-tree"
        :data="options"
        :props="defaultProps"
        default-expand-all
        :filter-node-method="filterNode"
        @node-click="handleNodeClick"
      />
    </el-popover>
  </div>
</template>

<script>
import {defineComponent, watch, onMounted, ref, computed} from "vue";

export default defineComponent({
  props: {
    popoverWidth: {
      type: Number,
      default: 400,
    },
    modelValue: {
      type: String,
      default: "",
    },
    options: {
      type: Array,
      default: () => [],
    },
  },
  emits: ["update:modelValue", "selected"],
  setup(props, context) {
    const defaultProps = ref({
      children: "children",
      label: "label",
    });

    const popVisible = ref(false);
    function handleIcon() {popVisible.value = !popVisible.value;}

    const preText = ref("");
    const placeholder = computed(() =>
      preText.value ? preText.value : "select"
    );

    function filterNode(value, data) {if (!value) return true;
      return data.label.indexOf(value) !== -1;
    }

    function findLabel(arr, target) {
      let res = target;
      function find(arr) {for (let i = 0; i < arr.length; i += 1) {if (arr[i].value === target) {res = arr[i].label;
            return;
          }
          if (arr[i].children && arr[i].children.length) {find(arr[i].children, target);
          }
        }
      }
      find(arr);
      return res;
    }

    const filterText = ref("");
    function handleNodeClick(node) {
      popVisible.value = false;
      filterText.value = node.label;
      preText.value = node.label;
      context.emit("selected", node);
      context.emit("update:modelValue", node.value);
    }
    function handleBlur() {setTimeout(() => {filterText.value = preText.value;}, 100);
    }
    function handleFocus() {
      preText.value = filterText.value;
      filterText.value = "";
    }
    watch(() => props.modelValue,
      () => {filterText.value = findLabel(props.options, props.modelValue);
      }
    );
    const tree = ref();
    watch(() => filterText.value,
      (val) => {tree.value.filter(val);
      }
    );

    onMounted(() => {filterText.value = findLabel(props.options, props.modelValue);
    });
    return {
      tree,
      placeholder,
      defaultProps,
      filterText,
      popVisible,
      preText,
      handleNodeClick,
      handleBlur,
      handleFocus,
      filterNode,
      handleIcon,
    };
  },
});
</script>
<style scoped>
.suffix {
  cursor: pointer;
  display: flex;
  width: 100%;
  height: 100%;
  align-items: center;
}
</style>

应用起来比之前那一篇麻烦了一丢丢,绝对还是好用的
且看,

<treeSelect
      style="width: 226px"
      :popoverWidth="200"
      v-model="selectData"
      :options="options"
      @selected="handleSelect"
    ></treeSelect>
   

在父组件里咱们组要把对应要批改的 value 传进去,options 对应树结构的 array 模样,不过肯定要有 value 的,或者读者能够本人再略微改改,emit 进去的参数。
对于 el-popover 的宽度和高度都没有做设置,不适宜过多的数据,需要的话就本人加个 div 限度一下,或者须要的人多的话,我再把它优化下。。

const selectData = ref("");
    const options = ref([
      {
        label: "选项 1",
        value: "1",
        children: [{label: "选项 1 -1", value: "1-1"}],
      },
      {label: "选项 2", value: "2"},
    ]);
    function handleSelect(node) {console.log(node);
    }

有哪里须要优化的提一下,继续改良哈。

退出移动版