组件开发--自定义二级联动select选项框

46 阅读2分钟

2024.1.19

需求

在element-plus组件库基础上,自定义select内容,左右联动,点击左侧列表内容查看右侧children内容,点击右侧内容选中收起弹框,选项框显示children选中项,如下图:

image.png

组件引用


<el-form-item required :label="label" :prop="propKey" :label-width="labelWidth">
  <CustomSelectV2
    :style="widthStyle"
    :defaultValue="formData[propKey]"
    :optionList="optionList"
    :onSelect="(val) => onChange(propKey, val)"
  />
</el-form-item>

const {
    propKey='failProcess',
    label='失败处理',
    labelWidth='120px',
    optionList,onChange
} = props

组件代码


<!--
 * @Author: SunnyYang
 * @Date: 2024-01-19 14:47:28
 * @LastEditors: SunnyYang
 * @LastEditTime: 2024-04-07 16:07:39
 * @Description: 
-->
<template>
 <el-select
    ref="configSelect"
    popper-class="custom-select"
    :model-value="selectValue"
    clearable
    :placeholder="placeholder"
    :fit-input-width="true"
    :style="{ ...style, minWidth: 'none' }"
    @clear="handleClear"
  >
    <div class="options-left">
      <el-option
        v-for="item in optionList"
        :hide-on-click="false"
        :key="item[keyValue]"
        :label="item[keyName]"
        :value="item[keyValue]"
        :class="`el-select-dropdown__item ${fatherSelectValue === item[keyValue] ? 'selected hover' : ''}`"
        @click.stop="() => handleLeftClick(item[keyValue])"
      >
        <span class="option-label">{{ item[keyName] }}</span>
      </el-option>
    </div>

    <div class="options-right">
      <el-option
        v-for="(stems, index) in childrenOptions[childrenKey]"
        :key="stems[childrenKeyValue]"
        :label="stems[childrenKeyName]"
        :value="stems[childrenKeyValue]"
        @click="() => handleRightClick(stems[childrenKeyValue])"
      >
        <span v-if="childrenKeyIndex">{{ index + 1 }}.</span>
        <span class="name">{{ stems[childrenKeyName] }}</span>
        <span class="tips" v-if="stems[childrenKeyTips]">{{ stems[childrenKeyTips] }}</span>
      </el-option>
    </div>
  </el-select>
</template>

<script setup name="CustomSelectV2">
import { ref, onMounted ,watch} from 'vue'

const props = defineProps({
  defaultValue: {
    type: String,
    default: '',
  },
  onSelect: {
    type: Function,
    default: () => {},
  },
  optionList: {
    type: Array,
    default: () => [
      // {
      //   dataCode: '1',
      //   dataValue: '1',
      //   children: [{ dataCode: 'children1', dataValue: 'children1' }],
      // },
    ],
  },
  keyName: {
    type: String,
    default: 'dataValue',
  },
  keyValue: {
    type: String,
    default: 'dataCode',
  },
  childrenKey: {
    type: String,
    default: 'children',
  },
  childrenKeyName: {
    type: String,
    default: 'dataValue',
  },
  childrenKeyValue: {
    type: String,
    default: 'dataCode',
  },
  childrenKeyTips: {
    type: String,
    default: 'dataTips',
  },
  childrenKeyIndex: {
    type: Boolean,
    default: false,
  },
  placeholder: {
    type: String,
    default: '请选择',
  },
  style: {
    type: Object,
    default: {
      width: '480px',
      minWidth: 'none',
    },
  },
})

const { keyName, keyValue, childrenKey, childrenKeyName, childrenKeyValue } = props
const fatherSelectValue = ref(''), //只用于数据回显 左侧
  selectValue = ref(''), //model-value 参数 右侧
  childrenOptions = ref({}), //左右联动--右侧子数据
  option = ref(props.optionList), //初始化option数据
  configSelect = ref(null) // 获取el-select组件实例

onMounted(() => {
  option.value = props.optionList
  getData()
})

watch(
  () => props.optionList,
  (newVal) => {
    if (newVal) {
      option.value = newVal
      getData()
    }
  },
  { deep: true },
)

//input 选中项
function getData() {
  // 设置选中项
  selectValue.value = props.defaultValue || ''
  //通过for in 循环return终止遍历
  for (let i in option.value) {
    const item = option.value[i],
      find = item[childrenKey].find((val) => val[keyValue] === props.defaultValue)

    if (find) {
      fatherSelectValue.value = item[keyValue] //左侧选中项
      childrenOptions.value = item //筛选右侧列表项
      return
    } else {
      // 初始化数据
      fatherSelectValue.value = option.value[0][keyValue] //左侧选中项
      childrenOptions.value = option.value[0] //筛选右侧列表项
    }
  }
}

function handleLeftClick(value) {
  configSelect.value.visible = true //点击弹框取消隐藏
  fatherSelectValue.value = value //左侧选中项
  childrenOptions.value = { ...option.value.find((val) => val[keyValue] === value) } //筛选右侧列表项
}

//input 选中项 返回父组件选中项
function handleRightClick(value) {
  selectValue.value = value
  props.onSelect(value)
}

function handleClear() {
  selectValue.value = ''
  fatherSelectValue.value = ''
  props.onSelect('')
}
</script>

<style lang="scss">
.custom-select {
  .el-select-dropdown__list {
    width: 100%;
    display: flex;
  }
  .options-left {
    width: 40%;
    border-right: 1px solid #ccc;
  }

  .options-right {
    width: 60%;
  }

  .tips {
    color: #666;
  }
  .option-label {
    line-height: 34px;
    display: block;
    width: 100%;
    height: 100%;
    text-overflow: ellipsis;
    overflow: hidden;
    word-break: break-all;
    white-space: nowrap;
  }
}
</style>