1. 问题
距离上一次发布解决el-cascader的bug的文章已经过去半年了,今天我又被el-cascader恶心到了,这次就是上次遗留的问题。多选情况下,没有匹配上options不显示数据。
2. 解决过程
-
github查看没有相关issues github.com/ElemeFE/ele… 可能维护人员都忙着维护element plus去了,这里的issues已经不怎么处理了
-
那我再去element plus看看有没有相关问题,如果新的已经解决了的话,可以参考一下怎么写的,但是我在issues上没有找到相关问题,不过element plus的el-cascader也会有这个bug
-
我又去看了Ant Design Vue的cascader,他们确实解决了这个问题,但是在写法和el-cascade相差有点多就没有再深究下去
-
只能按照上次的思路再去研究了,直接贴代码
单选的时候
computePresentText() {
const { checkedValue, config } = this
if (!this.isEmptyValue(checkedValue)) {
const node = this.panel.getNodeByValue(checkedValue)
if (node && (config.checkStrictly || node.isLeaf)) {
this.presentText = node.getText(this.showAllLevels, this.separator)
return
} else {
//再匹配不上的时候就把数据用,拼接起来
this.presentText = checkedValue.join(',')
return
}
}
this.presentText = null
},
多选的时候
<template>
<div
ref="reference"
v-clickoutside="() => toggleDropDownVisible(false)"
:class="[
'el-cascader',
realSize && `el-cascader--${realSize}`,
{ 'is-disabled': isDisabled }
]"
@mouseenter="inputHover = true"
@mouseleave="inputHover = false"
@click="() => toggleDropDownVisible(readonly ? undefined : true)"
@keydown="handleKeyDown"
>
<el-input
ref="input"
v-model="multiple ? presentText : inputValue"
:size="realSize"
:placeholder="placeholder"
:readonly="readonly"
:disabled="isDisabled"
:validate-event="false"
:class="{ 'is-focus': dropDownVisible }"
@focus="handleFocus"
@blur="handleBlur"
@input="handleInput"
>
<template slot="suffix">
<i
v-if="clearBtnVisible"
key="clear"
class="el-input__icon el-icon-circle-close"
@click.stop="handleClear"
/>
<i
v-else
key="arrow-down"
:class="[
'el-input__icon',
'el-icon-arrow-down',
dropDownVisible && 'is-reverse'
]"
@click.stop="toggleDropDownVisible()"
/>
</template>
</el-input>
<div v-if="multiple" class="el-cascader__tags" style="display: flex;align-items: center;">
<el-tag
v-for="tag in presentTags"
:key="tag.key"
type="info"
:size="tagSize"
:hit="tag.hitState"
:closable="tag.closable"
disable-transitions
@close="deleteTag(tag)"
>
<span>{{ tag.text }}</span>
</el-tag>
<!-- 再用一个列表展示匹配不上的数据 -->
<el-tag
v-for="(tag,index) in noMatchValue"
:key="tag.text"
type="info"
:size="tagSize"
closable
disable-transitions
@close="deleteTagNoMatch(index)"
>
<span>{{ tag.text }}</span>
</el-tag>
<input
v-if="filterable && !isDisabled"
v-model.trim="inputValue"
type="text"
class="el-cascader__search-input"
:placeholder="presentTags.length || noMatchValue.length ? '' : placeholder"
@input="e => handleInput(inputValue, e)"
@click.stop="toggleDropDownVisible(true)"
@keydown.delete="handleDelete"
>
</div>
<transition name="el-zoom-in-top" @after-leave="handleDropdownLeave">
<div
v-show="dropDownVisible"
ref="popper"
:class="['el-popper', 'el-cascader__dropdown', popperClass]"
>
<el-cascader-panel
v-show="!filtering"
ref="panel"
v-model="checkedValue"
:options="options"
:props="config"
:border="false"
:render-label="$scopedSlots.default"
@expand-change="handleExpandChange"
@close="toggleDropDownVisible(false)"
/>
<el-scrollbar
v-if="filterable"
v-show="filtering"
ref="suggestionPanel"
tag="ul"
class="el-cascader__suggestion-panel"
view-class="el-cascader__suggestion-list"
@keydown.native="handleSuggestionKeyDown"
>
<template v-if="suggestions.length">
<li
v-for="(item, index) in suggestions"
:key="item.uid"
:class="[
'el-cascader__suggestion-item',
item.checked && 'is-checked'
]"
:tabindex="-1"
@click="handleSuggestionClick(index)"
>
<span>{{ item.text }}</span>
<i v-if="item.checked" class="el-icon-check" />
</li>
</template>
<slot v-else name="empty">
<li class="el-cascader__empty-text">{{ t('el.cascader.noMatch') }}</li>
</slot>
</el-scrollbar>
</div>
</transition>
</div>
</template>
<script>
import { Cascader } from 'element-ui'
export default {
extends: Cascader,
data() {
return {
noMatchValue: [],
}
},
methods: {
computePresentText() {
const { checkedValue, config } = this
if (!this.isEmptyValue(checkedValue)) {
const node = this.panel.getNodeByValue(checkedValue)
if (node && (config.checkStrictly || node.isLeaf)) {
this.presentText = node.getText(this.showAllLevels, this.separator)
return
} else {
this.presentText = checkedValue.join(',')
return
}
}
this.presentText = null
},
computePresentTags() {
const { isDisabled, leafOnly, showAllLevels, separator, collapseTags, checkedValue } = this
const checkedNodes = this.getCheckedNodes(leafOnly)
const tags = []
const genTag = node => ({
node,
key: node.uid,
text: node.getText(showAllLevels, separator),
hitState: false,
closable: !isDisabled && !node.isDisabled,
})
if (checkedNodes.length) {
const [first, ...rest] = checkedNodes
const restCount = rest.length
tags.push(genTag(first))
if (restCount) {
if (collapseTags) {
tags.push({
key: -1,
text: `+ ${restCount}`,
closable: false,
})
} else {
rest.forEach(node => tags.push(genTag(node)))
}
}
}
this.checkedNodes = checkedNodes
this.presentTags = tags
// 在这里就是全部处理完的多选数据了
// 将checkedNodes没有的数据,而checkedValue有的数据,也就是匹配不上的数据,放到noMatchValue中
this.noMatchValue = []
checkedValue.forEach(item => {
if (this.checkedNodes.length) {
let flag = false
for (const tag of this.checkedNodes) {
if (tag && tag.path && item.join('mm@mm') === tag.path.join('mm@mm')) {
flag = true
}
}
!flag && this.noMatchValue.push({
text: item.join(','),
})
} else {
this.noMatchValue.push({
text: item.join(','),
})
}
})
},
// 删除的时候把相应数据删掉就可以了,因为这里是匹配不上的数据,在我看来也就不需要在删除的时候发送事件出去,如果要加的话,可以参考el-cascader的源码加上去
deleteTagNoMatch(index) {
this.checkedValue.findIndex(item => item.join(',') === this.noMatchValue[index].text)
this.checkedValue.splice(index, 1)
this.noMatchValue.splice(index, 1)
},
},
}
</script>
到这里这个问题就解决了,展示出来的效果就是这样
3. 总结
数据匹配的方式比较笨,采用的是不怎么用到的字符串(mm@mm)合并匹配,如果有大佬有更好的方法,请不吝赐教