fix: filterMethod

pull/22919/head
BernardZheng 2024-07-09 09:09:55 +08:00
parent b6ffb321bc
commit 07e36bf80a
1 changed files with 165 additions and 130 deletions

View File

@ -4,14 +4,14 @@
:class="[ :class="[
'el-cascader', 'el-cascader',
realSize && `el-cascader--${realSize}`, realSize && `el-cascader--${realSize}`,
{ 'is-disabled': isDisabled } { 'is-disabled': isDisabled },
]" ]"
v-clickoutside="() => toggleDropDownVisible(false)" v-clickoutside="() => toggleDropDownVisible(false)"
@mouseenter="inputHover = true" @mouseenter="inputHover = true"
@mouseleave="inputHover = false" @mouseleave="inputHover = false"
@click="() => toggleDropDownVisible(readonly ? undefined : true)" @click="() => toggleDropDownVisible(readonly ? undefined : true)"
@keydown="handleKeyDown"> @keydown="handleKeyDown"
>
<el-input <el-input
ref="input" ref="input"
v-model="multiple ? presentText : inputValue" v-model="multiple ? presentText : inputValue"
@ -23,22 +23,25 @@
:class="{ 'is-focus': dropDownVisible }" :class="{ 'is-focus': dropDownVisible }"
@focus="handleFocus" @focus="handleFocus"
@blur="handleBlur" @blur="handleBlur"
@input="handleInput"> @input="handleInput"
>
<template slot="suffix"> <template slot="suffix">
<i <i
v-if="clearBtnVisible" v-if="clearBtnVisible"
key="clear" key="clear"
class="el-input__icon el-icon-circle-close" class="el-input__icon el-icon-circle-close"
@click.stop="handleClear"></i> @click.stop="handleClear"
></i>
<i <i
v-else v-else
key="arrow-down" key="arrow-down"
:class="[ :class="[
'el-input__icon', 'el-input__icon',
'el-icon-arrow-down', 'el-icon-arrow-down',
dropDownVisible && 'is-reverse' dropDownVisible && 'is-reverse',
]" ]"
@click.stop="toggleDropDownVisible()"></i> @click.stop="toggleDropDownVisible()"
></i>
</template> </template>
</el-input> </el-input>
@ -51,7 +54,8 @@
:hit="tag.hitState" :hit="tag.hitState"
:closable="tag.closable" :closable="tag.closable"
disable-transitions disable-transitions
@close="deleteTag(tag)"> @close="deleteTag(tag)"
>
<span>{{ tag.text }}</span> <span>{{ tag.text }}</span>
</el-tag> </el-tag>
<input <input
@ -60,16 +64,18 @@
type="text" type="text"
class="el-cascader__search-input" class="el-cascader__search-input"
:placeholder="presentTags.length ? '' : placeholder" :placeholder="presentTags.length ? '' : placeholder"
@input="e => handleInput(inputValue, e)" @input="(e) => handleInput(inputValue, e)"
@click.stop="toggleDropDownVisible(true)" @click.stop="toggleDropDownVisible(true)"
@keydown.delete="handleDelete"> @keydown.delete="handleDelete"
/>
</div> </div>
<transition name="el-zoom-in-top" @after-leave="handleDropdownLeave"> <transition name="el-zoom-in-top" @after-leave="handleDropdownLeave">
<div <div
v-show="dropDownVisible" v-show="dropDownVisible"
ref="popper" ref="popper"
:class="['el-popper', 'el-cascader__dropdown', popperClass]"> :class="['el-popper', 'el-cascader__dropdown', popperClass]"
>
<el-cascader-panel <el-cascader-panel
ref="panel" ref="panel"
v-show="!filtering" v-show="!filtering"
@ -79,7 +85,8 @@
:border="false" :border="false"
:render-label="$scopedSlots.default" :render-label="$scopedSlots.default"
@expand-change="handleExpandChange" @expand-change="handleExpandChange"
@close="toggleDropDownVisible(false)"></el-cascader-panel> @close="toggleDropDownVisible(false)"
></el-cascader-panel>
<el-scrollbar <el-scrollbar
ref="suggestionPanel" ref="suggestionPanel"
v-if="filterable" v-if="filterable"
@ -87,23 +94,27 @@
tag="ul" tag="ul"
class="el-cascader__suggestion-panel" class="el-cascader__suggestion-panel"
view-class="el-cascader__suggestion-list" view-class="el-cascader__suggestion-list"
@keydown.native="handleSuggestionKeyDown"> @keydown.native="handleSuggestionKeyDown"
>
<template v-if="suggestions.length"> <template v-if="suggestions.length">
<li <li
v-for="(item, index) in suggestions" v-for="(item, index) in suggestions"
:key="item.uid" :key="item.uid"
:class="[ :class="[
'el-cascader__suggestion-item', 'el-cascader__suggestion-item',
item.checked && 'is-checked' item.checked && 'is-checked',
]" ]"
:tabindex="-1" :tabindex="-1"
@click="handleSuggestionClick(index)"> @click="handleSuggestionClick(index)"
>
<span>{{ item.text }}</span> <span>{{ item.text }}</span>
<i v-if="item.checked" class="el-icon-check"></i> <i v-if="item.checked" class="el-icon-check"></i>
</li> </li>
</template> </template>
<slot v-else name="empty"> <slot v-else name="empty">
<li class="el-cascader__empty-text">{{ t('el.cascader.noMatch') }}</li> <li class="el-cascader__empty-text">
{{ t("el.cascader.noMatch") }}
</li>
</slot> </slot>
</el-scrollbar> </el-scrollbar>
</div> </div>
@ -112,69 +123,72 @@
</template> </template>
<script> <script>
import Popper from 'element-ui/src/utils/vue-popper'; import Popper from "element-ui/src/utils/vue-popper";
import Clickoutside from 'element-ui/src/utils/clickoutside'; import Clickoutside from "element-ui/src/utils/clickoutside";
import Emitter from 'element-ui/src/mixins/emitter'; import Emitter from "element-ui/src/mixins/emitter";
import Locale from 'element-ui/src/mixins/locale'; import Locale from "element-ui/src/mixins/locale";
import Migrating from 'element-ui/src/mixins/migrating'; import Migrating from "element-ui/src/mixins/migrating";
import ElInput from 'element-ui/packages/input'; import ElInput from "element-ui/packages/input";
import ElTag from 'element-ui/packages/tag'; import ElTag from "element-ui/packages/tag";
import ElScrollbar from 'element-ui/packages/scrollbar'; import ElScrollbar from "element-ui/packages/scrollbar";
import ElCascaderPanel from 'element-ui/packages/cascader-panel'; import ElCascaderPanel from "element-ui/packages/cascader-panel";
import AriaUtils from 'element-ui/src/utils/aria-utils'; import AriaUtils from "element-ui/src/utils/aria-utils";
import { t } from 'element-ui/src/locale'; import { t } from "element-ui/src/locale";
import { isEqual, isEmpty, kebabCase } from 'element-ui/src/utils/util'; import { isEqual, isEmpty, kebabCase } from "element-ui/src/utils/util";
import { isUndefined, isFunction } from 'element-ui/src/utils/types'; import { isUndefined, isFunction } from "element-ui/src/utils/types";
import { isDef } from 'element-ui/src/utils/shared'; import { isDef } from "element-ui/src/utils/shared";
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'; import {
import debounce from 'throttle-debounce/debounce'; addResizeListener,
removeResizeListener,
} from "element-ui/src/utils/resize-event";
import debounce from "throttle-debounce/debounce";
const { keys: KeyCode } = AriaUtils; const { keys: KeyCode } = AriaUtils;
const MigratingProps = { const MigratingProps = {
expandTrigger: { expandTrigger: {
newProp: 'expandTrigger', newProp: "expandTrigger",
type: String type: String,
}, },
changeOnSelect: { changeOnSelect: {
newProp: 'checkStrictly', newProp: "checkStrictly",
type: Boolean type: Boolean,
}, },
hoverThreshold: { hoverThreshold: {
newProp: 'hoverThreshold', newProp: "hoverThreshold",
type: Number type: Number,
} },
}; };
const PopperMixin = { const PopperMixin = {
props: { props: {
placement: { placement: {
type: String, type: String,
default: 'bottom-start' default: "bottom-start",
}, },
appendToBody: Popper.props.appendToBody, appendToBody: Popper.props.appendToBody,
visibleArrow: { visibleArrow: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
arrowOffset: Popper.props.arrowOffset, arrowOffset: Popper.props.arrowOffset,
offset: Popper.props.offset, offset: Popper.props.offset,
boundariesPadding: Popper.props.boundariesPadding, boundariesPadding: Popper.props.boundariesPadding,
popperOptions: Popper.props.popperOptions, popperOptions: Popper.props.popperOptions,
transformOrigin: Popper.props.transformOrigin transformOrigin: Popper.props.transformOrigin,
}, },
methods: Popper.methods, methods: Popper.methods,
data: Popper.data, data: Popper.data,
beforeDestroy: Popper.beforeDestroy beforeDestroy: Popper.beforeDestroy,
}; };
const InputSizeMap = { const InputSizeMap = {
medium: 36, medium: 36,
small: 32, small: 32,
mini: 28 mini: 28,
}; };
export default { export default {
name: 'ElCascader', name: "ElCascader",
directives: { Clickoutside }, directives: { Clickoutside },
@ -182,18 +196,18 @@ export default {
inject: { inject: {
elForm: { elForm: {
default: '' default: "",
}, },
elFormItem: { elFormItem: {
default: '' default: "",
} },
}, },
components: { components: {
ElInput, ElInput,
ElTag, ElTag,
ElScrollbar, ElScrollbar,
ElCascaderPanel ElCascaderPanel,
}, },
props: { props: {
@ -203,7 +217,7 @@ export default {
size: String, size: String,
placeholder: { placeholder: {
type: String, type: String,
default: () => t('el.cascader.placeholder') default: () => t("el.cascader.placeholder"),
}, },
disabled: Boolean, disabled: Boolean,
clearable: Boolean, clearable: Boolean,
@ -211,22 +225,22 @@ export default {
filterMethod: Function, filterMethod: Function,
separator: { separator: {
type: String, type: String,
default: ' / ' default: " / ",
}, },
showAllLevels: { showAllLevels: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
collapseTags: Boolean, collapseTags: Boolean,
debounce: { debounce: {
type: Number, type: Number,
default: 300 default: 300,
}, },
beforeFilter: { beforeFilter: {
type: Function, type: Function,
default: () => (() => {}) default: () => () => {},
}, },
popperClass: String popperClass: String,
}, },
data() { data() {
@ -242,7 +256,7 @@ export default {
filtering: false, filtering: false,
suggestions: [], suggestions: [],
inputInitialHeight: 0, inputInitialHeight: 0,
pressDeleteCount: 0 pressDeleteCount: 0,
}; };
}, },
@ -252,9 +266,7 @@ export default {
return this.size || _elFormItemSize || (this.$ELEMENT || {}).size; return this.size || _elFormItemSize || (this.$ELEMENT || {}).size;
}, },
tagSize() { tagSize() {
return ['small', 'mini'].indexOf(this.realSize) > -1 return ["small", "mini"].indexOf(this.realSize) > -1 ? "mini" : "small";
? 'mini'
: 'small';
}, },
isDisabled() { isDisabled() {
return this.disabled || (this.elForm || {}).disabled; return this.disabled || (this.elForm || {}).disabled;
@ -263,13 +275,11 @@ export default {
const config = this.props || {}; const config = this.props || {};
const { $attrs } = this; const { $attrs } = this;
Object Object.keys(MigratingProps).forEach((oldProp) => {
.keys(MigratingProps)
.forEach(oldProp => {
const { newProp, type } = MigratingProps[oldProp]; const { newProp, type } = MigratingProps[oldProp];
let oldValue = $attrs[oldProp] || $attrs[kebabCase(oldProp)]; let oldValue = $attrs[oldProp] || $attrs[kebabCase(oldProp)];
if (isDef(oldProp) && !isDef(config[newProp])) { if (isDef(oldProp) && !isDef(config[newProp])) {
if (type === Boolean && oldValue === '') { if (type === Boolean && oldValue === "") {
oldValue = true; oldValue = true;
} }
config[newProp] = oldValue; config[newProp] = oldValue;
@ -288,17 +298,22 @@ export default {
return !this.filterable || this.multiple; return !this.filterable || this.multiple;
}, },
clearBtnVisible() { clearBtnVisible() {
if (!this.clearable || this.isDisabled || this.filtering || !this.inputHover) { if (
!this.clearable ||
this.isDisabled ||
this.filtering ||
!this.inputHover
) {
return false; return false;
} }
return this.multiple return this.multiple
? !!this.checkedNodes.filter(node => !node.isDisabled).length ? !!this.checkedNodes.filter((node) => !node.isDisabled).length
: !!this.presentText; : !!this.presentText;
}, },
panel() { panel() {
return this.$refs.panel; return this.$refs.panel;
} },
}, },
watch: { watch: {
@ -322,27 +337,35 @@ export default {
this.toggleDropDownVisible(false); this.toggleDropDownVisible(false);
} }
this.$emit('input', val); this.$emit("input", val);
this.$emit('change', val); this.$emit("change", val);
this.dispatch('ElFormItem', 'el.form.change', [val]); this.dispatch("ElFormItem", "el.form.change", [val]);
} }
}, },
options: { options: {
handler: function() { handler: function () {
this.$nextTick(() => { this.$nextTick(() => {
this.computePresentContent() this.computePresentContent();
if(this.filtering) { if (this.filtering) {
this.suggestions = this.panel.getFlattedNodes(this.leafOnly) let { filterMethod } = this;
.filter(node => {
if (!isFunction(filterMethod)) {
filterMethod = (node, keyword) => node.text.includes(keyword);
}
this.suggestions = this.panel
.getFlattedNodes(this.leafOnly)
.filter((node) => {
if (node.isDisabled) return false; if (node.isDisabled) return false;
node.text = node.getText(this.showAllLevels, this.separator) || ''; node.text =
node.getText(this.showAllLevels, this.separator) || "";
return filterMethod(node, this.lastInputValue); return filterMethod(node, this.lastInputValue);
}); });
} }
}); });
}, },
deep: true deep: true,
}, },
presentText(val) { presentText(val) {
this.inputValue = val; this.inputValue = val;
@ -356,16 +379,17 @@ export default {
this.$nextTick(this.updatePopper); this.$nextTick(this.updatePopper);
}, },
inputValue(newVal, oldVal) { inputValue(newVal, oldVal) {
const oldValue = oldVal && oldVal !== ' ' ? oldVal : this.lastInputValue const oldValue = oldVal && oldVal !== " " ? oldVal : this.lastInputValue;
const newValue = newVal && newVal !== ' ' ? newVal : false const newValue = newVal && newVal !== " " ? newVal : false;
this.lastInputValue = newValue || oldValue || ' ' this.lastInputValue = newValue || oldValue || " ";
} },
}, },
mounted() { mounted() {
const { input } = this.$refs; const { input } = this.$refs;
if (input && input.$el) { if (input && input.$el) {
this.inputInitialHeight = input.$el.offsetHeight || InputSizeMap[this.realSize] || 40; this.inputInitialHeight =
input.$el.offsetHeight || InputSizeMap[this.realSize] || 40;
} }
if (!this.isEmptyValue(this.value)) { if (!this.isEmptyValue(this.value)) {
@ -401,13 +425,17 @@ export default {
getMigratingConfig() { getMigratingConfig() {
return { return {
props: { props: {
'expand-trigger': 'expand-trigger is removed, use `props.expandTrigger` instead.', "expand-trigger":
'change-on-select': 'change-on-select is removed, use `props.checkStrictly` instead.', "expand-trigger is removed, use `props.expandTrigger` instead.",
'hover-threshold': 'hover-threshold is removed, use `props.hoverThreshold` instead' "change-on-select":
"change-on-select is removed, use `props.checkStrictly` instead.",
"hover-threshold":
"hover-threshold is removed, use `props.hoverThreshold` instead",
}, },
events: { events: {
'active-item-change': 'active-item-change is renamed to expand-change' "active-item-change":
} "active-item-change is renamed to expand-change",
},
}; };
}, },
toggleDropDownVisible(visible) { toggleDropDownVisible(visible) {
@ -424,8 +452,8 @@ export default {
this.panel.scrollIntoView(); this.panel.scrollIntoView();
}); });
} }
input.$refs.input.setAttribute('aria-expanded', visible); input.$refs.input.setAttribute("aria-expanded", visible);
this.$emit('visible-change', visible); this.$emit("visible-change", visible);
} }
}, },
handleDropdownLeave() { handleDropdownLeave() {
@ -450,10 +478,10 @@ export default {
} }
}, },
handleFocus(e) { handleFocus(e) {
this.$emit('focus', e); this.$emit("focus", e);
}, },
handleBlur(e) { handleBlur(e) {
this.$emit('blur', e); this.$emit("blur", e);
}, },
handleInput(val, event) { handleInput(val, event) {
!this.dropDownVisible && this.toggleDropDownVisible(true); !this.dropDownVisible && this.toggleDropDownVisible(true);
@ -466,13 +494,13 @@ export default {
} }
}, },
handleClear() { handleClear() {
this.presentText = ''; this.presentText = "";
this.panel.clearCheckedNodes(); this.panel.clearCheckedNodes();
}, },
handleExpandChange(value) { handleExpandChange(value) {
this.$nextTick(this.updatePopper.bind(this)); this.$nextTick(this.updatePopper.bind(this));
this.$emit('expand-change', value); this.$emit("expand-change", value);
this.$emit('active-item-change', value); // Deprecated this.$emit("active-item-change", value); // Deprecated
}, },
focusFirstNode() { focusFirstNode() {
this.$nextTick(() => { this.$nextTick(() => {
@ -481,10 +509,14 @@ export default {
let firstNode = null; let firstNode = null;
if (filtering && suggestionPanel) { if (filtering && suggestionPanel) {
firstNode = suggestionPanel.$el.querySelector('.el-cascader__suggestion-item'); firstNode = suggestionPanel.$el.querySelector(
".el-cascader__suggestion-item"
);
} else { } else {
const firstMenu = popper.querySelector('.el-cascader-menu'); const firstMenu = popper.querySelector(".el-cascader-menu");
firstNode = firstMenu.querySelector('.el-cascader-node[tabindex="-1"]'); firstNode = firstMenu.querySelector(
'.el-cascader-node[tabindex="-1"]'
);
} }
if (firstNode) { if (firstNode) {
@ -498,7 +530,7 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
if (this.config.multiple) { if (this.config.multiple) {
this.computePresentTags(); this.computePresentTags();
this.presentText = this.presentTags.length ? ' ' : null; this.presentText = this.presentTags.length ? " " : null;
} else { } else {
this.computePresentText(); this.computePresentText();
} }
@ -524,16 +556,17 @@ export default {
this.presentText = null; this.presentText = null;
}, },
computePresentTags() { computePresentTags() {
const { isDisabled, leafOnly, showAllLevels, separator, collapseTags } = this; const { isDisabled, leafOnly, showAllLevels, separator, collapseTags } =
this;
const checkedNodes = this.getCheckedNodes(leafOnly); const checkedNodes = this.getCheckedNodes(leafOnly);
const tags = []; const tags = [];
const genTag = node => ({ const genTag = (node) => ({
node, node,
key: node.uid, key: node.uid,
text: node.getText(showAllLevels, separator), text: node.getText(showAllLevels, separator),
hitState: false, hitState: false,
closable: !isDisabled && !node.isDisabled closable: !isDisabled && !node.isDisabled,
}); });
if (checkedNodes.length) { if (checkedNodes.length) {
@ -546,10 +579,10 @@ export default {
tags.push({ tags.push({
key: -1, key: -1,
text: `+ ${restCount}`, text: `+ ${restCount}`,
closable: false closable: false,
}); });
} else { } else {
rest.forEach(node => tags.push(genTag(node))); rest.forEach((node) => tags.push(genTag(node)));
} }
} }
} }
@ -564,19 +597,20 @@ export default {
filterMethod = (node, keyword) => node.text.includes(keyword); filterMethod = (node, keyword) => node.text.includes(keyword);
} }
const suggestions = this.panel.getFlattedNodes(this.leafOnly) const suggestions = this.panel
.filter(node => { .getFlattedNodes(this.leafOnly)
.filter((node) => {
if (node.isDisabled) return false; if (node.isDisabled) return false;
node.text = node.getText(this.showAllLevels, this.separator) || ''; node.text = node.getText(this.showAllLevels, this.separator) || "";
return filterMethod(node, this.inputValue); return filterMethod(node, this.inputValue);
}); });
if (this.multiple) { if (this.multiple) {
this.presentTags.forEach(tag => { this.presentTags.forEach((tag) => {
tag.hitState = false; tag.hitState = false;
}); });
} else { } else {
suggestions.forEach(node => { suggestions.forEach((node) => {
node.checked = isEqual(this.checkedValue, node.getValueByOption()); node.checked = isEqual(this.checkedValue, node.getValueByOption());
}); });
} }
@ -637,30 +671,32 @@ export default {
deleteTag(tag) { deleteTag(tag) {
const { checkedValue } = this; const { checkedValue } = this;
const current = tag.node.getValueByOption(); const current = tag.node.getValueByOption();
const val = checkedValue.find(n => isEqual(n, current)); const val = checkedValue.find((n) => isEqual(n, current));
this.checkedValue = checkedValue.filter(n => !isEqual(n, current)); this.checkedValue = checkedValue.filter((n) => !isEqual(n, current));
this.$emit('remove-tag', val); this.$emit("remove-tag", val);
}, },
updateStyle() { updateStyle() {
const { $el, inputInitialHeight } = this; const { $el, inputInitialHeight } = this;
if (this.$isServer || !$el) return; if (this.$isServer || !$el) return;
const { suggestionPanel } = this.$refs; const { suggestionPanel } = this.$refs;
const inputInner = $el.querySelector('.el-input__inner'); const inputInner = $el.querySelector(".el-input__inner");
if (!inputInner) return; if (!inputInner) return;
const tags = $el.querySelector('.el-cascader__tags'); const tags = $el.querySelector(".el-cascader__tags");
let suggestionPanelEl = null; let suggestionPanelEl = null;
if (suggestionPanel && (suggestionPanelEl = suggestionPanel.$el)) { if (suggestionPanel && (suggestionPanelEl = suggestionPanel.$el)) {
const suggestionList = suggestionPanelEl.querySelector('.el-cascader__suggestion-list'); const suggestionList = suggestionPanelEl.querySelector(
suggestionList.style.minWidth = inputInner.offsetWidth + 'px'; ".el-cascader__suggestion-list"
);
suggestionList.style.minWidth = inputInner.offsetWidth + "px";
} }
if (tags) { if (tags) {
const offsetHeight = Math.round(tags.getBoundingClientRect().height); const offsetHeight = Math.round(tags.getBoundingClientRect().height);
const height = Math.max(offsetHeight + 6, inputInitialHeight) + 'px'; const height = Math.max(offsetHeight + 6, inputInitialHeight) + "px";
inputInner.style.height = height; inputInner.style.height = height;
if (this.dropDownVisible) { if (this.dropDownVisible) {
this.updatePopper(); this.updatePopper();
@ -673,8 +709,7 @@ export default {
*/ */
getCheckedNodes(leafOnly) { getCheckedNodes(leafOnly) {
return this.panel.getCheckedNodes(leafOnly); return this.panel.getCheckedNodes(leafOnly);
} },
} },
}; };
</script> </script>