Bootstrap

el-select 多选默认选项添加禁止删除功能

项目开发中,遇到一个需求,要求编辑时打开的表单,多选下拉框,只允许添加属性,不允许删除原来的属性。类似下图,白色是默认值不允许删除,只能在下拉框添加新的属性。

image-20240723145245424

查看 elment-uiselect api 文档,并没有发现相关支持,所以决定自己实现。

设计思路

隐藏关闭图标

通过调试得知,若要隐藏关闭的图标,只需要将对应的 tagclose 标签,设置 display: none 属性就可以实现。

image-20240723145807817

所以思路就清晰了,实现隐藏关闭图标需关注以下几个步骤:

  • 获取需要隐藏的 tag 的 id
  • 获取所有选中的 tag 的 id,用于找到需隐藏 tagindex
  • 渲染后查找当前 select 下所有类名包含 .el-tag__close 的标签,将需隐藏 tagindex 对应的包含 .el-tag__close 的标签,添加 display: none 样式

禁用下拉选项

要注意的是,被隐藏关闭图标,对应的下拉列表的选项,也需要禁用掉,这一点使用 element-ui 提供的的 Option Attributesdisabled 属性即可。

<el-select v-model="value" placeholder="请选择">
  <el-option
    v-for="item in options"
    :key="item.value"
    :label="item.label"
    :value="item.value"
    :disabled="item.disabled">
  </el-option>
</el-select>

思路总结

实现这个功能,不仅要考虑隐藏关闭图标,以下几点是需要关注的:

  • 隐藏关闭图标
  • 禁用对应下拉选项的选择
  • 禁用清空 clearable

代码实现

考虑使用 vue 的自定义指令实现,添加 hideTagClose 指令实现上述功能。复习一下自定义指令的参数内容。

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 updatecomponentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在 updatecomponentUpdated 钩子中可用。

封装的组件如下:

<template>
    <el-select
        v-bind="$attrs"
        v-hideTagClose="{ hideTagClose, defaultItemIds, value: $attrs.value }"
        v-on="$listeners">
        <slot></slot>
    </el-select>
</template>

<script>
export default {
    name: 'ElSelectWithHideTagClose',
    inheritAttrs: false,
    props: {
        // 是否需要隐藏
        hideTagClose: {
            type: Boolean,
            default: false,
        },
        // defaultItemIds 默认值,需要隐藏关闭标签的值
        defaultItemIds: {
            type: Array,
            default: () => [],
        },
    },
    directives: {
        hideTagClose: {
            update(el, binding) {
              	// value: v-model 绑定的 value 值
                const { hideTagClose, defaultItemIds, value } = binding.value
                if (!hideTagClose) return
              	// 需要隐藏关闭标签的索引
                const hideCloseValueIndexs = []
                defaultItemIds.forEach(hideCloseValue => {
                    const index = value.findIndex(v => v === hideCloseValue)

                    if (index !== undefined) {
                        hideCloseValueIndexs.push(index)
                    }
                })
                // 将选中项的 关闭 图标隐藏
                setTimeout(() => {
                    const tags = el.querySelectorAll('.el-tag__close')
                    tags.forEach((tagEl, index) => {
                        if (hideCloseValueIndexs.includes(index)) {
                            tagEl.style.display = 'none'
                        }
                    })
                })
            },
        },
    },
}
</script>

实际使用

<ElSelectWithHideTagClose
    v-model="value"
    :defaultItemIds="defaultItemIds || []"
    :hideTagClose="isEdit"
    filterable
    :clearable="!isEdit"
    multiple
    @change="data => changeAttrValue(data, i)">
    <el-option
        v-for="o in valueList"
        :key="o.valueId"
        :label="o.valueName"
        :value="o.valueId"
        :disabled="o.disabled">
    </el-option>
</ElSelectWithHideTagClose>
;