话不多说,直接上代码
<template>
<div>
<el-select
ref="elSelect"
v-if="type === 'select'"
v-model="form"
:placeholder="placeholder"
clearable
style="width: 100%"
size="small"
:no-data-text="noDataText"
:multiple="isMultiple"
:filterable="isFilterable"
:remote="isRemote"
reserve-keyword
:filter-method="filterMethod"
:remote-method="remoteMethod"
:loading="loading"
:collapse-tags="collapseTags"
@change="changeEmit"
@visible-change="visibleChange"
>
<el-option
v-for="item in searchOptions"
:key="item.id"
:label="item[optionWord.label]"
:value="item[optionWord.value]"
>
<span
v-html="
getSearchColor(item[optionWord.label], item[optionWord.lastWord])
"
></span>
</el-option>
</el-select>
<el-cascader
v-if="type === 'cascader'"
v-model="form"
:placeholder="placeholder"
style="width: 100%"
:options="options"
:props="{
...props,
multiple: isMultiple
}"
:show-all-levels="showAllLevels"
:collapse-tags="collapseTags"
clearable
:filterable="isFilterable"
:filter-method="filterMethodCasc"
:popper-class="popperClass"
@change="changeEmit"
>
</el-cascader>
</div>
</template>
<script>
export default {
name: 'FilterSelectCascader',
props: {
// select||cascader
type: {
type: String,
default: 'select'
},
value: {
type: [String, Number, Array]
},
placeholder: {
type: String,
default: ''
},
// 是否多选
isMultiple: {
type: Boolean,
default: false
},
// 是否可搜索
isFilterable: {
type: Boolean,
default: true
},
// 是否远程搜索
isRemote: {
type: Boolean,
default: false
},
// 远程搜索
remoteUrl: {
type: Function,
default: async () => {
return {
data: {
list: []
}
}
}
},
// 默认传参字段 select远程 必填
remoteDefault: {
type: String,
default: 'name'
},
// 远程搜索参数
remoteUrlParams: {
type: Object,
default: () => ({})
},
// cascader 是否展示全路径
showAllLevels: {
type: Boolean,
default: false
},
// cascader 配置 参考element
props: {
type: Object,
default: () => ({})
},
// 自定义浮层类名 保证唯一性
popperClass: {
type: String,
default: ''
},
// tags
collapseTags: {
type: Boolean,
default: true
},
// select 渲染字段
optionWord: {
type: Object,
default: () => {
return {
label: 'label',
value: 'value',
lastWord: ''
}
}
},
// 本地数据
dataOption: {
type: Array,
default: () => []
},
noDataText: {
type: String,
default: ''
}
},
watch: {
value: {
handler(v) {
this.form = v
}
},
dataOption: {
handler(v) {
if (!this.isRemote && v?.length) {
this.options = v
this.searchOptions = JSON.parse(JSON.stringify(v))
}
},
deep: true,
immediate: true
},
keyword: {
handler(v) {
// cascader 关键字高亮
if (v && this.type === 'cascader') {
this.$nextTick(() => {
const depPopper = document.getElementsByClassName(
this.popperClass
)[0]
const depCascaderSugAll = depPopper.getElementsByClassName(
'el-cascader__suggestion-item'
)
depCascaderSugAll.forEach(el => {
const sugItemSpan = el.querySelector('span')
const reStr = sugItemSpan.textContent
.split(v)
.join(
`<span style="color:#126EFE">${v
.replace(/</g, '<')
.replace(/>/g, '>')}</span>`
)
sugItemSpan.innerHTML = reStr
})
})
}
}
}
},
data() {
return {
form: !this.isMultiple && !this.showAllLevels ? '' : [],
options: [],
searchOptions: [],
loading: false,
keyword: ''
}
},
mounted() {
if (!this.isRemote && !this.dataOption?.length) {
this.defaultOption()
}
},
methods: {
async defaultOption() {
const {
data: { list }
} = await this.remoteUrl(this.remoteUrlParams)
this.options = list
this.searchOptions = JSON.parse(JSON.stringify(list))
},
changeEmit(v) {
console.log(v)
if (!v?.length) {
this.searchOptions = this.options
this.keyword = ''
}
this.$emit('input', this.form)
},
// 远程搜索
async remoteMethod(val) {
let str = val.replace(/\s*/g, '')
if (!str) {
this.searchOptions = []
return
}
this.keyword = str
this.loading = true
const params = this.remoteUrlParams
params[this.remoteDefault] = str
const { data } = await this.remoteUrl(params)
this.loading = false
this.searchOptions = data.list
},
// 普通搜索 select
filterMethod(keyword) {
this.keyword = keyword
this.$refs['elSelect'].filteredOptionsCount =
this.$refs['elSelect'].optionsCount
this.$refs['elSelect'].broadcast('ElOption', 'queryChange', keyword)
},
visibleChange(v) {
if (!v) return
if (this.isFilterable) {
this.keyword = ''
if (this.isRemote) {
this.searchOptions = []
} else {
this.searchOptions = this.options
}
}
},
// 搜索 cascader
filterMethodCasc(node, keyword) {
this.keyword = keyword
return node.text.includes(keyword)
},
// 检索值高亮
getSearchColor(label, depName) {
if (this.keyword === '') {
return `<span>${label}</span>`
} else {
let str = label.split(this.keyword)
const reStr = str.join(
`<span style="color:#126EFE">${this.keyword
.replace(/</g, '<')
.replace(/>/g, '>')}</span>`
)
return depName ? reStr + '-' + depName : reStr
}
}
}
}
</script>