当前位置: 首页 > news >正文

鸿蒙OSUniApp实现个性化的搜索框与搜索历史记录#三方框架 #Uniapp

使用UniApp实现个性化的搜索框与搜索历史记录

在移动端应用开发中,搜索功能几乎是标配,而一个好的搜索体验不仅仅是功能的实现,更是用户留存的关键。本文将分享如何在UniApp框架下打造一个既美观又实用的搜索框,并实现搜索历史记录功能。

前言

做了这么多年的前端开发,发现搜索功能看似简单,但要做好却不容易。特别是在跨端开发框架UniApp中,既要考虑不同平台的兼容性,又要保证用户体验的一致性。最近在一个电商项目中,我重新设计了搜索模块,今天就把这部分经验分享给大家。

需求分析

首先明确我们要实现的功能:

  1. 一个美观的搜索框,支持输入搜索关键词
  2. 实时显示搜索建议(热门搜索)
  3. 记录用户的搜索历史,并提供查看和清除功能
  4. 点击历史记录可以快速进行搜索

这些看似常见的功能,实现起来却有不少细节需要注意。接下来,我们一步步来实现。

基础界面搭建

首先,我们创建一个搜索页面search.vue

<template><view class="search-container"><!-- 搜索框部分 --><view class="search-header"><view class="search-box"><text class="iconfont icon-search"></text><input type="text" v-model="keyword" confirm-type="search"placeholder="请输入搜索关键词" @confirm="handleSearch"@input="handleInput"/><text v-if="keyword" class="iconfont icon-close" @tap="clearKeyword"></text></view><text class="cancel-btn" @tap="goBack">取消</text></view><!-- 内容区域 --><view class="search-content"><!-- 搜索历史 --><view class="history-section" v-if="searchHistory.length > 0"><view class="section-header"><text>搜索历史</text><text class="iconfont icon-delete" @tap="clearHistory"></text></view><view class="tag-list"><view class="tag-item" v-for="(item, index) in searchHistory" :key="index"@tap="searchByTag(item)">{{item}}</view></view></view><!-- 热门搜索 --><view class="hot-section"><view class="section-header"><text>热门搜索</text></view><view class="tag-list"><view class="tag-item" v-for="(item, index) in hotKeywords" :key="index"@tap="searchByTag(item)">{{item}}</view></view></view></view></view>
</template><script>
export default {data() {return {keyword: '',searchHistory: [],hotKeywords: ['手机', '电脑', '耳机', '平板', '相机', '手表', '家电', '配件']}},onLoad() {// 页面加载时获取历史记录this.getSearchHistory()},methods: {handleSearch() {if (!this.keyword.trim()) return// 保存搜索记录this.saveSearchHistory(this.keyword)// 执行搜索跳转this.navigateToResult(this.keyword)},searchByTag(keyword) {this.keyword = keywordthis.handleSearch()},clearKeyword() {this.keyword = ''},goBack() {uni.navigateBack()},handleInput() {// 可以在这里实现输入联想功能},getSearchHistory() {const history = uni.getStorageSync('searchHistory')if (history) {this.searchHistory = JSON.parse(history)}},saveSearchHistory(keyword) {let history = [...this.searchHistory]// 如果已存在相同关键词,先移除const index = history.findIndex(item => item === keyword)if (index !== -1) {history.splice(index, 1)}// 将新关键词添加到头部history.unshift(keyword)// 只保留最近10条记录if (history.length > 10) {history = history.slice(0, 10)}this.searchHistory = historyuni.setStorageSync('searchHistory', JSON.stringify(history))},clearHistory() {uni.showModal({title: '提示',content: '确定要清空搜索历史吗?',success: res => {if (res.confirm) {this.searchHistory = []uni.removeStorageSync('searchHistory')}}})},navigateToResult(keyword) {uni.navigateTo({url: `/pages/search-result/search-result?keyword=${encodeURIComponent(keyword)}`})}}
}
</script><style lang="scss">
.search-container {display: flex;flex-direction: column;height: 100vh;background-color: #f8f8f8;.search-header {display: flex;align-items: center;padding: 20rpx 30rpx;background-color: #ffffff;.search-box {flex: 1;display: flex;align-items: center;height: 70rpx;background-color: #f5f5f5;border-radius: 35rpx;padding: 0 20rpx;.iconfont {font-size: 36rpx;color: #999;}input {flex: 1;height: 100%;margin: 0 15rpx;font-size: 28rpx;}.icon-close {padding: 10rpx;}}.cancel-btn {margin-left: 20rpx;font-size: 28rpx;color: #333;}}.search-content {flex: 1;padding: 30rpx;.section-header {display: flex;justify-content: space-between;margin-bottom: 20rpx;font-size: 30rpx;color: #333;.iconfont {font-size: 32rpx;color: #999;}}.tag-list {display: flex;flex-wrap: wrap;.tag-item {padding: 10rpx 30rpx;margin: 0 20rpx 20rpx 0;background-color: #ffffff;border-radius: 30rpx;font-size: 26rpx;color: #666;}}.history-section {margin-bottom: 50rpx;}}
}
</style>

实现搜索历史功能

在上面的代码中,我们已经基本实现了搜索历史的保存和展示功能。整个实现思路是:

  1. 在用户进行搜索时,通过saveSearchHistory方法将关键词保存到本地存储
  2. 页面加载时,通过getSearchHistory从本地读取历史记录
  3. 点击历史记录标签可以快速发起搜索
  4. 提供清空历史记录的功能

但是,这里还有一些优化点,比如:

1. 防止重复保存

当用户多次搜索相同关键词时,我们不应该重复保存。在代码中,我们先查找是否已存在相同关键词,如果存在则先移除,然后将新的关键词添加到列表头部,这样可以保证最近搜索的内容总是排在最前面。

2. 限制历史记录数量

为了避免占用过多存储空间,我们限制只保留最近的10条搜索记录:

// 只保留最近10条记录
if (history.length > 10) {history = history.slice(0, 10)
}

进阶:实现搜索建议功能

现在,我们的搜索框基本功能已经实现了,但是为了提升用户体验,我们可以增加实时搜索建议功能。当用户输入关键词时,后台可以返回相关的搜索建议。

<template><!-- 搜索建议列表 --><view class="suggestion-list" v-if="keyword && suggestions.length > 0"><view class="suggestion-item" v-for="(item, index) in suggestions" :key="index"@tap="searchByTag(item)"><text class="iconfont icon-search"></text><text>{{item}}</text></view></view>
</template><script>
export default {data() {return {// ...现有代码suggestions: []}},methods: {// ...现有代码// 修改handleInput方法handleInput() {if (!this.keyword.trim()) {this.suggestions = []return}// 这里模拟从服务端获取搜索建议// 实际开发中,应该调用API获取数据setTimeout(() => {if (this.keyword) {this.suggestions = this.mockSuggestions(this.keyword)}}, 300)},mockSuggestions(keyword) {// 这只是一个模拟的建议列表,实际开发中应该调用APIconst allSuggestions = {'手': ['手机', '手表', '手机壳', '手持云台'],'电': ['电脑', '电视', '电饭煲', '电动牙刷'],'耳': ['耳机', '耳麦', '耳温枪']}for (const key in allSuggestions) {if (keyword.includes(key)) {return allSuggestions[key]}}return []}}
}
</script><style lang="scss">
// 添加搜索建议样式
.suggestion-list {position: absolute;top: 110rpx;left: 0;right: 0;background-color: #fff;box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);z-index: 999;.suggestion-item {display: flex;align-items: center;padding: 20rpx 30rpx;border-bottom: 1px solid #f5f5f5;.iconfont {font-size: 32rpx;color: #999;margin-right: 15rpx;}text {font-size: 28rpx;color: #333;}}
}
</style>

防抖处理

为了避免频繁请求服务器,我们可以对输入事件进行防抖处理:

// 需要先定义一个定时器变量
data() {return {// ...现有数据debounceTimer: null}
},// 修改handleInput方法
handleInput() {if (!this.keyword.trim()) {this.suggestions = []return}// 清除之前的定时器clearTimeout(this.debounceTimer)// 设置新的定时器this.debounceTimer = setTimeout(() => {// 调用API获取搜索建议this.getSuggestions()}, 300)
},// 获取搜索建议的方法
getSuggestions() {// 实际开发中应该调用API// 这里使用模拟数据this.suggestions = this.mockSuggestions(this.keyword)// 真实API调用可能是这样:/*uni.request({url: 'https://your-api.com/suggestions',data: {keyword: this.keyword},success: (res) => {this.suggestions = res.data.suggestions || []}})*/
}

实战案例:电商搜索页面

在实际的电商项目中,我们不仅需要基础的搜索功能,还需要分类筛选、排序等高级功能。以下是一个简化的电商搜索结果页面:

<!-- search-result.vue -->
<template><view class="result-container"><!-- 搜索框 --><view class="search-bar"><view class="search-box" @tap="goToSearch"><text class="iconfont icon-search"></text><text class="placeholder">{{keyword || '请输入搜索关键词'}}</text></view></view><!-- 筛选条件 --><view class="filter-bar"><view class="filter-item" :class="{active: currentSort === 'default'}"@tap="changeSort('default')">综合</view><view class="filter-item" :class="{active: currentSort === 'sales'}"@tap="changeSort('sales')">销量</view><view class="filter-item" :class="{active: currentSort === 'price'}"@tap="changeSort('price')">价格<text class="iconfont" :class="priceOrder === 'asc' ? 'icon-up' : 'icon-down'"></text></view><view class="filter-item" :class="{active: showFilter}"@tap="toggleFilter">筛选<text class="iconfont icon-filter"></text></view></view><!-- 商品列表 --><view class="goods-list"><view class="goods-item" v-for="(item, index) in goodsList" :key="index"@tap="goToDetail(item.id)"><image :src="item.image" mode="aspectFill"></image><view class="goods-info"><view class="goods-title">{{item.title}}</view><view class="goods-price">¥{{item.price}}</view><view class="goods-extra"><text class="sales">销量 {{item.sales}}</text><text class="comment">评价 {{item.comment}}</text></view></view></view></view><!-- 无结果提示 --><view class="empty-tip" v-if="goodsList.length === 0"><image src="/static/images/empty.png"></image><text>没有找到相关商品</text></view></view>
</template>

总结与优化建议

通过本文,我们实现了一个功能完善的搜索框和搜索历史记录功能。但在实际应用中,还可以做以下优化:

  1. 搜索框自动聚焦:页面加载时,自动让输入框获得焦点,提高用户体验
  2. 历史记录去重:除了当前的去重逻辑,还可以考虑对相似关键词进行合并
  3. 个性化推荐:根据用户的历史搜索记录,提供个性化的搜索推荐
  4. 搜索结果缓存:对热门搜索结果进行缓存,提高加载速度
  5. 语音搜索:集成语音识别API,支持语音搜索功能
  6. 图片搜索:支持上传图片进行搜索,特别适用于电商场景

最后的思考

在移动端应用中,搜索功能是连接用户与内容的桥梁。一个好的搜索体验,能够大大提升用户对应用的满意度。因此,我们不仅要关注功能的实现,还要从用户体验的角度出发,打造更加人性化的搜索功能。

希望本文能对你在UniApp中实现搜索功能有所帮助。如果有任何问题或建议,欢迎在评论区交流讨论!

相关文章:

  • digitalworld.local: DEVELOPMENT靶场
  • 高项-逻辑数据模型
  • TypeScript 类型映射讲解
  • 基于IBM BAW的Case Management进行项目管理示例
  • day26 Python 自定义函数
  • Ubuntu服务器开启SNMP服务 监控系统配置指南 -优雅草星云智控简易化操作
  • Python类的力量:第五篇:魔法方法与协议——让类拥有Python的“超能力”
  • pytorch nn.RNN demo
  • 软件设计师考试《综合知识》设计模式之——工厂模式与抽象工厂模式考点分析
  • 【HTML】个人博客页面
  • 【美团】后端一面复盘|项目驱动 + 手撕 + JVM + 数据库全面覆盖
  • QT 使用QPdfWriter和QPainter绘制PDF文件
  • Parsec解决PnP连接失败的问题
  • 11 web 自动化之 DDT 数据驱动详解
  • Swagger go中文版本手册
  • 【HCIA】策略路由
  • 搭建Hadoop集群standalone
  • STM32 SD卡拔插后FatFs挂载失败可能原因
  • 短视频二创App功能深度解析:短剧创作与推广的智能化革命
  • 开源模型应用落地-模型上下文协议(MCP)-Resources-资源的使用逻辑
  • 一种声音·阿甘本|即将到来的中世纪;“新”与“旧”……
  • 江苏省委组织部副部长高颜已任南京市委常委、组织部部长
  • 中央宣传部、全国妇联联合发布2025年“最美家庭”
  • 颜福庆与顾临的争论:1930年代在中国维持一家医学院要花多少钱
  • 牛市早报|中方调整对美加征关税措施,五部门约谈外卖平台企业
  • 广东早熟荔枝“抢滩”上海,向长三角消费者喊话:包甜,管够