输入搜索、分组展示选项、下拉选取,el-select 实现:即输入关键字检索,返回分组选项,选取跳转到相应内容页 —— VUE 项目-全局模糊检索
后端数据代码写于下一篇:输入搜索、分组展示选项、下拉选取,全局跳转页,el-select 实现 —— 后端数据处理代码,抛砖引玉展思路
【效果图】:分组展示选项 【去界面操作感受一下】—> 便捷简洁的企业官网
【录制效果视频展示】:
菜单栏-快速检索1
【流程】:
(1)读取目标数据,如果是多个,需要多次读取;
(2)对数据进行分组,放入特定分组数据结构;
(3)各分组,做相应设置;
(4)数据组装到 el-select 控件;
(5)点击选项,跳转到相应位置。
现将关键代码及结构附于下方:
1. 分组数据结构示例:
(1)标准结构示例:
groupSelectOptions2: [
{
id: 1,
label: '超期',
options: [
{
value: 'cqwbj',
label: '超期未办结'
},
{
value: 'ycq',
label: '已超期'
}
]
},
{
id: 2,
label: '按天',
options: [
{
value: 't1',
label: '1天'
},
{
value: 't2',
label: '2天'
},
{
value: 't3',
label: '3天'
}
]
},
{
id: 3,
label: '按小时',
options: [
{
value: 'h1',
label: '1小时'
},
{
value: 'h2',
label: '2小时'
}
]
}
]
(2)项目数据结构示例:
主要的就 label 和 srcPath 这两个属性(其余省略):label,用于显示;srcPath,存储选取跳转的 url 地址。
[
{
label:'',
options:[
{srcPath: ''}
]
},
]
2. 封装 el-select 成组件:
<template>
<div style="height: 15px; justify-content: center; align-items: center;">
<template>
<el-select
v-model="innerValue"
filterable
:remote="true"
:likeQuery="false"
@change="changeSelect"
:clearable="clearable"
:multiple="multiple"
:remote-method="fetchOptions"
size="small"
popper-class="productGroupSelector"
:placeholder="placeholder"
>
<el-option-group class="productGroupSelector-group" v-for="group in localOptions" :key="group.label" :label="group.label">
<div style="" v-if="multiple">
<div style="">
<el-checkbox v-model="group.checked" @change="selectAll($event, group.id)" :indeterminate="group.isIndeterminate"></el-checkbox>
</div>
<div>
<el-option
class="productGroupSelector-option"
v-for="item in group.options"
:key="item.value"
:label="item.label"
:value="item"
></el-option>
</div>
</div>
<div v-else>
<el-option
class="productGroupSelector-option"
v-for="(item,index) in group.options"
:key="index"
:label="item.label"
:value="item"
></el-option>
</div>
</el-option-group>
</el-select>
</template>
</div>
</template>
3. javascript 和 css
<script>
import $ from 'jquery';
import {getRequest} from "@/api/api";
export default {
name: 'LiloGroupSelect',
model: {
prop: 'value',
event: 'change'
},
props: {
value: {
type: [String, Array],
default: () => []
},
options: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: '请选择'
},
multiple: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: false
},
collapseTags: {
type: Boolean,
default: false
},
likeQuery: {
type: Boolean,
default: false
},
searchApi: {
type: String,
default: '' // 后端搜索API地址
}
},
data() {
return {
innerValue: this.value,
inputValue: '' ,// 添加这一行来定义 inputValue
selectedOption: '',
// searchQuery: '',
filteredOptions: [],
loading: false,
allOptions: [], // 存储所有后端返回的选项,用于筛选
localOptions: [...this.options], // 新增属性,用于存储当前选项
groupSelectOptions2: [
{
id: 1,
label: '超期',
options: [
{
value: 'cqwbj',
label: '超期未办结'
},
{
value: 'ycq',
label: '已超期'
}
]
},
{
id: 2,
label: '按天',
options: [
{
value: 't1',
label: '1天'
},
{
value: 't2',
label: '2天'
}
]
},
{
id: 3,
label: '按小时',
options: [
{
value: 'h1',
label: '1小时'
},
{
value: 'h2',
label: '2小时'
}
]
}
],
isDropdownVisible: false, // 控制下拉列表的显示状态(默认收起)隐藏
};
},
mounted() {
this.innerValue = this.value;
this.allOptions = [...this.options, ...this.groupSelectOptions2]; // 初始化所有选项
this.filteredOptions = [...this.options]; // 初始化过滤后的选项
},
watch: {
value(newVal, odlVal) {
this.innerValue = newVal;
console.log("当前输入值或选择值:"+this.innerValue)
},
searchQuery(newVal) {
console.log("监听查询输入:"+newVal)
this.fetchOptions(newVal);
}
},
methods: {
// 模拟后端查询,直接返回 groupSelectOptions2
fetchOptions(queryString) {
console.log("调用后端,请求数据....查询条件:【"+queryString+"】查询接口为:"+this.searchApi)
if (this.loading) return;
this.loading = true;
try {
// 此处模拟为直接返回 groupSelectOptions2,实际应调用后端API
this.allOptions = [...this.options, ...this.groupSelectOptions2]; // 合并原始选项和后端返回的选项(去重应在后端处理或此处额外处理)
if(this.likeQuery) queryString = '%'+queryString+'%';
this.getRequest(this.searchApi, {query: queryString}).then(resp =>{
if (resp){
this.localOptions = [...resp];
// console.log("调用后端,返回结果:"+JSON.stringify(resp))
}
});
// this.localOptions = [...this.groupSelectOptions2]; // 更新 localOptions 而不是 this.options
// this.filteredOptions = this.filterOptionsByQuery(this.allOptions, queryString);
console.log("调用后端,数据处理结束。。。")
} catch (error) {
console.error('搜索失败:', error);
} finally {
this.loading = false;
}
},
async query(queryString){
if(this.likeQuery) queryString = '%'+queryString+'%';
this.getRequest(this.searchApi, {query: queryString}).then(resp =>{
if (resp){
this.localOptions = [...resp];
}
});
},
filterOptionsByQuery(options, query) {
return this.allOptions.reduce((acc, group) => {
const filteredGroup = { ...group, options: group.options.filter(option => option.label.toLowerCase().includes(query.toLowerCase())) };
// const filteredGroup = { ...group, options: group.options.filter(option => option.label.includes(query)) };
if (filteredGroup.options.length > 0) {
acc.push(filteredGroup);
}
return acc;
}, []);
},
selectAll(val, id) {
const selectOption = this.options.find(f => f.id === id);
const arr = selectOption.options.map(m => m.value);
if (val) {
if((typeof this.innerValue !== 'object') || this.innerValue.constructor !== Array) {
this.innerValue = [];
}
arr.forEach(item => {
if (!this.innerValue.includes(item)) {
this.innerValue.push(item);
}
});
} else {
this.innerValue.forEach((item, index) => {
if (arr.includes(item)) {
this.innerValue.splice(index, 1, '');
}
});
}
this.innerValue = this.innerValue.filter(f => f !== '');
if (selectOption.checked) {
selectOption.isIndeterminate = false;
}
this.$emit('change', this.innerValue);
},
changeSelect(val) {
console.log("选项变更值:"+val)
if (this.multiple) {
this.options.forEach(item => {
const arr = item.options.map(m => m.value);
item.isIndeterminate = arr.some(v => {
return val.some(s => s === v);
});
item.checked = arr.every(v => {
return val.some(s => s === v);
});
if (item.checked) {
item.isIndeterminate = false;
}
});
this.$emit('change', this.innerValue);
} else {
this.$emit('change', val);
}
},
}
};
</script>
<style>
.productGroupSelector {
min-width: initial !important;
width: 415px;
}
</style>
<style lang="scss" scoped>
::v-deep {
.el-select-group {
width: 400px;
display: flex;
flex-wrap: wrap;
justify-content: start;
padding: 0px 10px;
}
.el-select-group__title {
padding-left: 20px;
font-size: 12px;
}
}
.productGroupSelector-group {
padding-top: 5px;
display: flex;
// align-items: center;
// flex-wrap: wrap;
// width: 400px;
padding-bottom: 5px;
flex-direction: column;
margin: 0 5px;
// &:not(:last-child) {
// border-bottom: 1px solid rgba($color: #000000, $alpha: 0.1);
// }
&::after {
display: none;
}
}
.productGroupSelector-option {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
}
// .productGroupSelector {
// .el-scrollbar__view .el-select-dropdown__list {
// display: flex;
// flex-wrap: wrap;
// justify-content: space-between;
// align-items: baseline;
// padding-top: 0;
// overflow-x: hidden;
// }
// .el-select-dropdown__wrap .el-scrollbar__wrap {
// max-height: 650px;
// }
// }
</style>
4. 引用 LiloGroupSelect
<el-row :gutter="20" style="display: flex; border-radius: 5px;" >
<el-col style="margin-bottom: 7px;">
<lilo-group-select @change="groupSelectChange" :multiple="false" :likeQuery="true" :searchApi="'/api/list/search'" clearable placeholder="请输入快速搜索" ></lilo-group-select>
</el-col>
</el-row>
<script>
import LiloGroupSelect from "@/components/common/help/ElementUIGroupSelect";
export default {
name: "***",
components: {
LiloGroupSelect
},
data(){
return{}
},
methods: {
groupSelectChange(option) {
console.log("下拉选项选中:"+JSON.stringify(option));
if(option==''|| option.srcPath=='')return;
// this.$router.push(option.srcPath);
this.$router.push(option.srcPath).catch(err => {
if (err.name !== 'NavigationDuplicated') {
// 处理其他可能的错误
console.error(err);
}
// 对于 NavigationDuplicated 错误,可以选择不做任何处理
});
},
}
}
【效果图】:分组展示选项
参考资源:
1. Vue【原创】基于elementui的【分组多选下拉框group-select】
2. el-select选择器组件封装 下拉菜单 elementui
3. Vue Element 分组+多选+可搜索Select选择器实现示例
4. 基于Vue和Element-UI自定义分组以及分组全选Select 选择器