前言:
模糊搜索的功能是很常见的一个功能,但是在写uniapp移动端项目时并没有发现很好用的组件之类的,所以决定自己小小的造一手,其实早在以前写过一个类似的功能并发布在了插件市场,但是回头再看时,感觉不太行。。。故重新整了一个。之后也会发布在插件市场,如果没有bug的话😁;插件地址
思路:当用户在输入框内输入值时,进行请求服务,拿到返回值,若列表长度大于0,展示返回值,否则展示无匹配内容,用户点击选中值,关闭列表展示框,其中还牵扯到了下一页加载的问题
效果图:
话不多说直接放代码:
fuzzy-list / index.vue
<template>
<view class="index">
<view class="search" v-if="show">
<scroll-view class="list" scroll-y v-if="list.length > 0" :lower-threshold="10" @scrolltolower="scrolltolower">
<view
v-for="item in list"
:key="item[valueName]"
class="item"
:style="[setItemStyle]"
hover-class="item-hover"
hover-start-time="0"
hover-stay-time="100"
@click="click(item)"
>
{{ item[labelName] }}
</view>
</scroll-view>
<view class="empty" v-else>{{ noData }}</view>
</view>
</view>
</template>
<script>
export default {
props: {
/** 展示整体组件 */
show: {
type: Boolean,
default: false
},
/** 需要展示的列表 */
list: {
type: Array,
default: () => []
},
/** 自定义label */
labelName: {
type: String,
default: 'label'
},
/** 自定义value */
valueName: {
type: String,
default: 'value'
},
/** 无内容时显示的内容 */
noData: {
type: String,
default: '暂无匹配内容...'
},
/** item的对齐样式 */
align: {
type: String,
default: 'left',
validator: value => {
return ['left', 'center', 'right'].includes(value);
}
},
/** 自定义item展示样式 */
customStyle: {
type: Object,
default: () => ({})
}
},
computed: {
/** 设置item的样式 */
setItemStyle() {
const { align, customStyle } = this;
return {
textAlign: align,
...customStyle
};
}
},
methods: {
/** item点击事件 */
click(item) {
this.$emit('select', { ...item });
},
/** 触底加载下一页 */
scrolltolower() {
this.$emit('scrolltolower');
}
}
};
</script>
<style lang="scss" scoped>
.index {
width: 100%;
position: absolute;
z-index: 9;
}
.search {
.list,
.empty {
padding: 10rpx;
background-color: #fff;
box-shadow: 0 0 10rpx #888;
border-radius: 10rpx;
}
.list {
box-sizing: border-box;
max-height: 300rpx;
overflow: hidden;
.item {
padding: 8rpx 0;
font-size: 26rpx;
margin: 5rpx 0;
&-hover {
background-color: #f5f5f5;
}
}
}
.empty {
height: 80rpx;
font-size: 26rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>
页面文件inex.vue
<template>
<view class="test">
<view style="text-align: center;">选中值:{{ selectData }}</view>
<view class="content">
<input class="input" type="text" :value="value" placeholder="请输入内容" @input="input" />
<fuzzy-list
label-name="name"
value-name="id"
align="center"
no-data="没有了,气不气?"
:show="show"
:list="list"
:custom-style="{ fontSize: '30rpx' }"
@scrolltolower="scrolltolower"
@select="select"
></fuzzy-list>
</view>
</view>
</template>
<script>
import FuzzyList from '@/components/fuzzy-list/index.vue';
import debounce from './debounce.js'; //防抖函数
import { httpGetList } from './api.js'; //示例请求
export default {
components: {
FuzzyList
},
data() {
return {
queryParams: {
pageSize: 10,
pageNum: 1
},
value: '',
show: false,
list: [],
selectData: null
};
},
methods: {
input(event) {
this.value = event.target.value;
this.queryParams.pageNum = 1;
debounce(this.getList, 500);
},
scrolltolower() {
this.queryParams.pageNum++;
this.getList();
},
getList() {
const { queryParams, value } = this;
/** 输入框内有值就进行搜索,没有值就关闭 */
if (value) {
httpGetList({ ...queryParams, keyword: value }).then(resp => {
if (resp.code === 200) {
this.list = resp.data;
if (!this.show) this.show = true;
}
});
} else {
this.show = false;
}
},
select(event) {
this.show = false;
this.value = event.name;
this.selectData = JSON.stringify(event);
}
}
};
</script>
<style lang="scss" scoped>
.test {
width: 100vw;
min-height: 100vh;
height: 100%;
padding-top: 300rpx;
}
.content {
width: 600rpx;
margin: 0 auto;
position: relative;
.input {
padding: 12rpx;
background-color: #f5f5f5;
}
}
</style>
debounce.js-直接拿的uview的
let timeout = null;
/**
* 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
function debounce(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout);
// 立即执行,此类情况一般用不到
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function() {
timeout = null;
}, wait);
if (callNow) typeof func === 'function' && func();
} else {
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(function() {
typeof func === 'function' && func();
}, wait);
}
}
export default debounce
api.js
const universityList = [{
id: 1,
name: "北京大学"
},
{
id: 2,
name: "北京邮电大学"
},
{
id: 3,
name: "北京师范大学"
},
{
id: 4,
name: "北京理工大学"
},
{
id: 5,
name: "北京航空航天大学"
},
{
id: 6,
name: "北京工业大学"
},
{
id: 7,
name: "北京交通大学"
},
{
id: 8,
name: "北京科技大学"
},
{
id: 9,
name: "北京外国语大学"
},
{
id: 10,
name: "北京联合大学 "
}
]
/**生成随机值 */
const getRandomNumber = () => Math.random().toString(16).slice(2);
export function httpGetList(params) {
const {
pageNum,
pageSize,
keyword
} = params
return new Promise((resovle) => {
let data = []
for (let i = 0; i < pageNum; i++) {
let list = universityList.map(item => ({
name: item.name,
id: getRandomNumber()
}))
data = [...data, ...list]
}
data = data.filter(item => item.name.includes(keyword))
resovle({
code: 200,
msg: 'success',
data
})
})
}
目前只测试了h5,app(安卓),微信小程序,其他的没有,连开发工具都没有,也没有写好看的样式,只是提供一个思路,如果对你有点用的话,请动动你发财的小手,点点赞什么的,先行告谢!