zl程序教程

您现在的位置是:首页 >  其他

当前栏目

uniapp:手把手写一个模糊搜索

搜索 一个 手把手 模糊 uniapp
2023-09-11 14:22:28 时间

前言:

        模糊搜索的功能是很常见的一个功能,但是在写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(安卓),微信小程序,其他的没有,连开发工具都没有,也没有写好看的样式,只是提供一个思路,如果对你有点用的话,请动动你发财的小手,点点赞什么的,先行告谢!