拓展原生vue3-element-plus的select组件,在下拉框中添加一个新增按钮

先上效果图

展开下拉
在这里插入图片描述
点击新增
在这里插入图片描述

在官方文档中发现,2.4.3版本对select新增了header和footer组件,可以实现一些新增操作功能。
在这里插入图片描述
但是有的小伙伴依赖的版本低于2.4.3,现有的插槽无法实现此功能,那么基于select组件手动封装一个组件实现此功能。

首先是模板部分。

首先是select组件,在option中添加一个自定义的option,我这里是个点击按钮的样式。
接下啦是个是一个点击之后产生的弹窗,没有什么特别的,弹窗里面维护一下自己想要的数据。form部分根据业务实际情况可以修改。

<template>
  <el-select
    v-model="selectedValue"
    v-bind="$attrs"
    :placeholder="placeholder"
    @change="handleSelect"
    ref="selectDropdown"
    popper-class="custom-popper-class"
  >
    <el-option
      v-for="option in computedOptions"
      :key="option.value"
      :label="option.label"
      :value="option.value"
    />
    <el-option key="__add__" value="__add__" class="add-option"
      >找不到{{ tips }}?<span class="option-button">点击新增</span></el-option
    >
  </el-select>

  <el-dialog v-model="showDialog" title="添加" @close="handleCancel">
    <el-form :model="form" ref="formDialog" :rules="rules">
      <el-form-item :label="tips" prop="inputValue">
        <el-input v-model="form.inputValue" :placeholder="请输入" />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="handleCancel">取消</el-button>
      <el-button type="primary" @click="handleAdd">确定</el-button>
    </template>
  </el-dialog>
</template>

prop部分就不再多说了,根据实际情况可以自己添加。

emits

emits: ['update:modelValue', 'add'],

逻辑部分

首先就是watch监听,在某些异步情况下,传过来option部分未必有值,所以添加一个监听是有必要的。

rules部分因需求需要进行调整。

api,我这里的需求是需要和后端交互,如果成功的话,自己把刚刚添加的值push到options中。可以根据实际情况进行修改。

这里需要着重提的是props.options.push(newOption)这一行代码。有些人可能不理解,vue的props不是单向数据流吗?
没错!!!字面类型和引用类型就不多说了,不理解的小伙伴可以自己学习一下相关内容。我这里的重点是,options是外部传入的,可能在外部的options会被其他组件使用,这里的值修改,其他引用的地方也会跟着修改,就避免了值不同步,或者还得重新请求接口的麻烦了,小伙伴们可以根据实际业务场景,来决定这个值是不是要这么修改。

selectDropdown.value.toggleMenu(false)这行代码,是用来处理select设置了filterable: true属性,导致的弹窗关闭之后,通过冒泡误触发了select的聚焦事件,导致下拉选项出现的问题。

其他就是一些常规逻辑了。

setup(props, { emit }) {
    const selectedValue = ref(props.modelValue)
    const form = ref({
      inputValue: '',
    })
    const tips = ref(props.tip)
    const formDialog = ref(null)
    const selectDropdown = ref(null)
    const showDialog = ref(false)
    const originalValue = toRef(props, 'modelValue')
    const computedOptions = ref([...props.options])

    watch(
      () => props.options,
      (newOptions) => {
        computedOptions.value = [...newOptions]
      }
    )
    watch(selectedValue, (newValue) => {
      emit('update:modelValue', newValue)
    })

    const rules = {
      inputValue: [
        { required: true, message: `请输入`, trigger: 'blur' },
        {
          validator: (rule, value, callback) => {
            if (!value) {
              callback()
            } else {
              const inputValue = value.toString()
              const exists = computedOptions.value.some(
                (option) => option.value.toString() === inputValue
              )
              if (exists) {
                callback(new Error(`我这里是添加了一个重复校验`))
              } else {
                callback()
              }
            }
          },
          trigger: 'blur',
        },
      ],
    }

    const handleAdd = () => {
      formDialog.value.validate(async (valid) => {
        if (valid) {
          try {
            const params = {
              ...
              ...
              //一些参数
            }
            await props.api()(params)
            const newOption = {
              label: form.value.inputValue,
              value: form.value.inputValue,
            }
            selectedValue.value = form.value.inputValue
            form.value.inputValue = ''

            /* 
               内部的option和外部的option同时修改。因为通过vue不推荐的方式修改外部option,
               会导致vue监听数组的方法失效,无法触发watch
             */
            computedOptions.value.push(newOption)

            //此处强制修改props的值,因为外部可能有一些使用了相同下拉的值
            // eslint-disable-next-line vue/no-mutating-props
            props.options.push(newOption)
            emit('update:modelValue', selectedValue.value)
            emit('add', newOption)
            handleCancel()
          } catch (error) {
            message.error(error.message)
          }
        } else {
          return false
        }
      })
    }

    const handleSelect = (value) => {
      if (value === '__add__') {
        selectedValue.value = originalValue.value
        showDialog.value = true
      } else {
        selectedValue.value = value
      }
    }

    const handleCancel = () => {
      formDialog.value.resetFields()
      showDialog.value = false
      selectDropdown.value.toggleMenu(false)
    }

    return {
      selectedValue,
      form,
      showDialog,
      computedOptions,
      handleAdd,
      handleSelect,
      handleCancel,
      rules,
      formDialog,
      selectDropdown,
      tips,
    }
  },

最后附一下样式,css比较弱,还是比较难写的,哈哈哈。

.custom-popper-class .el-scrollbar__wrap {
  overflow: visible !important;
  position: relative;
}

.add-option {
  position: sticky;
  bottom: 6px;
  background-color: #f5f7fa;
  z-index: 1;
  .option-button {
    font-size: 12px;
    color: #3183ff;
  }
}
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值