多个el-select实现级联选择器的效果

518 阅读2分钟

在实际的开发中,常常会遇到多级联动选择器但是需要使用单独的el-select来实现的场景,例如省市区选择、分类选择等。本文介绍了一个自定义的 useCascaderSelect 组件钩子函数,封装了三个选择器,通过简单的设置即可应用于多级联动选择,而且可以适应不同的选项的属性值label、value、Children这些。也可以根据需求封装四个五个或者更多 image.png

组件的用法

const data: any = ref([...])
const data2: any = ref([...])
const { SelectOne, SelectTwo, SelectThree } = useCascaderSelect()
const { SelectOne: SelectOne2, SelectTwo: SelectTwo2 } = useCascaderSelect({
  label: 'name',
  value: 'id',
  children: 'childrens'
})

<div>
  <SelectOne options={data.value} v-model={formData.one} />
  <SelectTwo v-model={formData.two} />
  <SelectThree v-model={formData.three} />
</div>
<div style="margin-bottom: 10px">
  <SelectOne2 options={data2.value} v-model={formData2.one} />
  <SelectTwo2 v-model={formData2.two} />
</div>

组件的代码

/*
 * @Author: 
 * @Date: 2024-08-24 01:54:47
 * @LastEditors: zengzhaoyan
 * @LastEditTime: 2024-08-24 13:45:57
 * @Description:
 * @FilePath: /zzy/src/views/demo/cascaderSelect/useCascaderSelect.tsx
 */
import { ElSelect, ElOption } from 'element-plus'

import { createVNode, ref, computed } from 'vue'

export const useCascaderSelect = (props?: any) => {
  const PropOptions = ref(null)
  const labelKey = ref('label')
  const valueKey = ref('value')
  const childrenKey = ref('children')
  /**
   * @description: 设置键名配置,用于指定数据对象中的 label、value 和 children 字段的对应键名。
   * @Date: 2024-08-24 13:43:55
   * @Author: 
   * @param {any} props
   */
  const setKeys = (props: any) => {
    if (!props) return
    const { label = 'label', value = 'value', children = 'children' } = props
    labelKey.value = label
    valueKey.value = value
    childrenKey.value = children
  }
  setKeys(props)

  /**
   * @description: 创建选项节点数组,用于生成 ElSelect 组件的选项。
   * @Date: 2024-08-24 13:44:37
   * @Author: 
   * @param {any} options
   */
  const createOptionVNodes = (options: any[]) => {
    return options.map((option) =>
      createVNode(ElOption, {
        value: option[valueKey.value],
        label: option[labelKey.value]
      })
    )
  }

  /**
   * @description: 第一个选择器
   * @Date: 2024-08-24 13:45:54
   * @Author: 
   */
  const currentOne = ref(null)
  let onOneOriginChange: any
  const onOneChange = (v: any) => {
    if (onOneOriginChange) {
      onOneOriginChange(v)
    }
    currentOne.value = v
    onTwoChange(null)
    onThreeChange(null)
  }
  const SelectOne = (props: any, context: any) => {
    // 因为数据的获取可能是异步的,所以这里在组件内接受传入的数据
    PropOptions.value = props.options
    const {
      'onUpdate:modelValue': onOneOriginChange,
      modelValue,
      ...restAttrs
    } = context.attrs
    return createVNode(
      ElSelect,
      {
        ...restAttrs,
        modelValue,
        'onUpdate:modelValue': onOneChange
      },
      createOptionVNodes(props.options)
    )
  }

  /**
   * @description: 第二个选择器
   * @Date: 2024-08-24 13:46:10
   * @Author: 
   */
  const currentTwo = ref(null)
  let onTwoOriginChange: any
  const onTwoChange = (v: any) => {
    if (onTwoOriginChange) {
      onTwoOriginChange(v)
    }
    currentTwo.value = v
    onThreeChange(null)
  }
  const two = computed(() => {
    const currentOneValue = currentOne.value
    const options = PropOptions.value || []
    const selectedOption = options.find(
      (t: any) => t[valueKey.value] == currentOneValue
    )
    return selectedOption?.[childrenKey.value || 'children'] ?? []
  })
  const SelectTwo = (_props: any, context: any) => {
    const twoOptions = createOptionVNodes(two.value)
    const { 'onUpdate:modelValue': onTwoOriginChange, ...restAttrs } =
      context.attrs
    return createVNode(
      ElSelect,
      {
        ...restAttrs,
        modelValue: currentTwo.value,
        'onUpdate:modelValue': onTwoChange
      },
      twoOptions
    )
  }

  /**
   * @description: 第三个选择器
   * @Date: 2024-08-24 13:46:17
   * @Author: 
   */
  const currentThree = ref(null)
  let onThreeOriginChange: any
  const onThreeChange = (v: any) => {
    if (onThreeOriginChange) {
      onThreeOriginChange(v)
    }
    currentThree.value = v
  }
  const three = computed(() => {
    if (currentOne.value == null && currentTwo.value == null) {
      return []
    }
    const selectedOption = two.value.find(
      (t: any) => t[valueKey.value] == currentTwo.value
    )
    return selectedOption?.[childrenKey.value || 'children'] ?? []
  })

  const SelectThree = (_props, context: any) => {
    const threeOptions = createOptionVNodes(three.value)
    const { 'onUpdate:modelValue': onThreeOriginChange, ...restAttrs } =
      context.attrs
    return createVNode(
      ElSelect,
      {
        ...restAttrs,
        modelValue: currentThree.value,
        'onUpdate:modelValue': onThreeChange
      },
      threeOptions
    )
  }
  return {
    SelectOne,
    SelectTwo,
    SelectThree
  }
}

这样做的好处就是很方便,需要的地方直接引用SelectOne,SelectTwo,SelectThree,并且传入数据就行,根据数据不同的label名,Children名也可以做相应的修改,但是没有做动态加载,大家可以参考参考,自己实现动态的加载