A few days ago to achieve a tree structure of the drop-down component, but recently let me support the drop-down search function, check a circle did not find the right, had to write their own today, make do with it, support the search function at least, but some parameters or to configure

<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>
Copy the code

It is easier to use than the previous one, but it is relatively easy to use.

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

In the parent component, we need to pass in the value to be modified. Options correspond to the array appearance of the tree structure, but there must be value, or readers can modify slightly, and emit the parameters. The width and height of el-popover are not set, so it is not suitable for too much data. If necessary, I can add a div to limit it, or I can optimize it if more people are needed.

const selectData = ref(""); Const options = ref([{label: "option 1", value: "1", children: [{label:" option 1-1", value: "1-1"}],}, {label: "Option 2", value: "2"},]); function handleSelect(node) { console.log(node); }Copy the code

What needs to be optimized to mention, continuous improvement ha.