效果如下:

使用:
ini
<search-list
:list="List"
:isShow="true"
:isAddShow="true"
:keyName="key"
:typeName="name"
@item-check="itemCheck"
@show-status="showStatus"
@call-back="callBack"
/>
参数值:
vbnet
List: any[] = [{},{}];
isAddShow: 新增的模块是否显示 不填默认为false
keyName: 搜素及显示的键名
typeName: 分类名
itemCheck: 返回选中数据
showStatus: 返回弹窗是否show param: boolean
callBack: 点击新增按钮返回数据 param: 搜素框中的输入值
例:
ini
List= [
{
driverId: '111011101',
driverName: '司机姓名',
driverTel: '13112341234',
}
]
keyName='driverName' 搜素列表显示driverName的数据,搜索页搜索driverName
typeName='司机' 搜索弹窗:【以下不是我的司机】 【点击建立新的司机】
itemCheck: 返回数据为: params: {driverId:'',driverName:'',driverTel:''}
callBack: 返回数据为'司机'
vue:
ini
<template>
<van-popup v-model:show="show" round position="bottom" :closeable="true" :close-icon="closeImg">
<div class="list-box">
<div class="top-box">
<p class="title">请选择{{ typeName }}</p>
<div class="search-box">
<van-search
v-model="searchValue"
show-action
autocomplete="off"
autofocus
placeholder="请输入搜索关键词"
@search="onSearch"
@clear="onClear"
>
<template #action>
<div @click="onSearch" class="search-btn">搜索</div>
</template>
</van-search>
</div>
</div>
<div class="list-box">
<van-list finished-text="没有更多了">
<van-cell class="add-box" v-if="addShow ? addShow : false">
<div class="cell-title">以下不是我的{{ typeName }}</div>
<div class="cell-info" @click="target">
点击建立
<span class="blue">新的{{ typeName }}</span>
</div>
</van-cell>
<template v-for="item in listArr" :key="item">
<van-cell @click="checkItem(item)">
<span
v-for="res in item[keyName]"
:key="res"
:class="
searchValue.length > 0 &&
searchValue.indexOf(res) > -1 &&
item[keyName].includes(searchValue)
? 'active'
: ''
"
>
{{ res }}
</span>
</van-cell>
</template>
</van-list>
</div>
</div>
</van-popup>
</template>
ts:
ini
<script lang="ts">
import { defineComponent, ref, watch, watchEffect } from 'vue';
import { Popup, Cell, Search, List, Toast } from 'vant';
import closeImg from '/@/assets/images/close-1.png';
export default defineComponent({
name: 'SearchDriver',
components: {
[Popup.name]: Popup,
[Cell.name]: Cell,
[Search.name]: Search,
[List.name]: List,
},
props: {
list: {
type: Array,
required: true,
default: () => {
return [''];
},
},
isShow: {
type: Boolean,
default: false,
},
keyName: {
type: String,
default: '',
},
typeName: {
type: String,
default: '司机',
},
isAddShow: {
type: Boolean,
default: false,
},
},
emits: ['itemCheck', 'showStatus', 'callBack'],
setup(props, { emit }) {
const searchValue = ref<string>('');
// 数组
let propList = ref<string[]>(props.list as string[]);
let newList = ref<string[]>(props.list as string[]);
// 弹窗是否显示
let show = ref<boolean>(props.isShow);
// 新增模块是否显示
let addShow = ref<boolean>(props.isAddShow);
// 列表显示数据
let listArr = ref<any>();
// 类型
let type = ref<string>(props.typeName);
const dataMap = (arr: any[]) => {
if (!arr || arr.length < 1) {
return [];
}
return arr;
};
listArr.value = dataMap(propList.value);
const onSearch = () => {
console.log(searchValue.value);
};
// 点击新增按钮后
const target = () => {
emit('callBack', searchValue.value);
};
// 过滤掉不符合条件的数据
const filterData = (list: any[]) => {
newList.value = list.filter(
(item) => item[props.keyName] && item[props.keyName].indexOf(searchValue.value) > -1
);
listArr.value = dataMap(newList.value);
};
// 选中数据
const checkItem = (item: any) => {
if (!item || item.length < 1) {
Toast('请重新选择');
return;
}
searchValue.value = item[props.keyName];
show.value = false;
emit('itemCheck', item);
emit('showStatus', false);
};
const onClear = () => {
searchValue.value = '';
show.value = false;
emit('itemCheck', '');
emit('showStatus', false);
};
watchEffect(() => {
show.value = props.isShow;
propList.value = props.list as string[];
newList.value = props.list as string[];
type.value = props.typeName;
filterData(newList.value);
});
watch(show, () => {
emit('showStatus', show.value);
});
watch(type, (old, newValue) => {
if (old !== newValue) {
searchValue.value = '';
}
});
watch(searchValue, (val, oldVal) => {
const len = val.length;
const oldLen = oldVal.length;
if (len > oldLen) {
// 写入
filterData(newList.value);
} else {
// 删除
filterData(propList.value);
}
});
return {
searchValue,
show,
listArr,
onSearch,
onClear,
checkItem,
target,
addShow,
closeImg,
};
},
});
</script>
scss:
css
@charset "UTF-8";
$topHeight: 160px;
$vnoTop: 36px;
$checkColor: #1777f2;
.list-box {
position: relative;
min-height: 50vh;
padding-top: $vnoTop;
.top-box {
position: absolute;
top: $vnoTop;
left: 0;
z-index: 1;
width: 100%;
height: $topHeight;
background-color: #fff;
border-bottom: 1px solid #e5e5e5;
.title {
margin-left: 40px;
font-size: 36px;
line-height: 50px;
color: rgb(0 0 0 / 85%);
}
.search-box {
width: 696px;
margin-left: 15px;
.search-btn {
font-size: 28px;
font-weight: 400;
color: $checkColor;
}
}
}
.list-box {
height: 76vh;
margin-top: $topHeight;
overflow: auto;
.add-box {
padding-left: 84px;
background-image: url('../../assets/images/search.png');
background-position: 40px 30px;
background-repeat: no-repeat;
background-size: 30px 30px;
.cell-title {
font-size: 32px;
font-weight: 400;
color: #1c1c1c;
}
.cell-info {
font-size: 28px;
font-weight: 400;
color: rgb(0 0 0 / 25%);
.blue {
color: #1777f2;
}
}
}
}
.van-cell {
position: relative;
&::before {
position: absolute;
right: var(--van-padding-md);
bottom: 0;
left: var(--van-padding-md);
pointer-events: none;
border-bottom: 1px solid var(--van-cell-border-color);
content: ' ';
transform: scaleY(0.5);
box-sizing: border-box;
}
span.active {
color: $checkColor;
}
}
}
博客园:www.cnblogs.com/wttt123/p/1...
以上。