项目开发中,遇到一个需求,要求编辑时打开的表单,多选下拉框,只允许添加属性,不允许删除原来的属性。类似下图,白色是默认值不允许删除,只能在下拉框添加新的属性。
查看 elment-ui
的 select
api 文档,并没有发现相关支持,所以决定自己实现。
设计思路
隐藏关闭图标
通过调试得知,若要隐藏关闭的图标,只需要将对应的 tag
的 close
标签,设置 display: none
属性就可以实现。
所以思路就清晰了,实现隐藏关闭图标需关注以下几个步骤:
- 获取需要隐藏的
tag
的 id - 获取所有选中的
tag
的 id,用于找到需隐藏tag
的index
- 渲染后查找当前
select
下所有类名包含.el-tag__close
的标签,将需隐藏tag
的index
对应的包含.el-tag__close
的标签,添加display: none
样式
禁用下拉选项
要注意的是,被隐藏关闭图标,对应的下拉列表的选项,也需要禁用掉,这一点使用 element-ui
提供的的 Option Attributes
的 disabled
属性即可。
<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
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。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
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
封装的组件如下:
<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>