HarmonyOS实战《疆域纪行》第04篇|探索页检索链路:关键词、地区筛选和空状态联动
这一篇讲一个很实用的链路不接后端如何在本地内容库上完成搜索、类型切换、地区筛选、主题筛选、排序和无结果提示。探索页是内容 App 的“找东西”能力写顺了用户才不会迷路。项目落点搜索状态searchText类型状态exploreType地区状态exploreRegion主题状态exploreTheme搜索历史searchHistory结果计算getExploreAtlasItems()、getExploreRouteItems()、getExploreCities()读完这一篇你可以复用一套本地检索链路输入更新状态、提交写入历史、多条件过滤列表、无结果时给用户下一步提示。探索页解决什么问题首页负责推荐探索页负责查找。旅行攻略类 App 的内容一多用户就会提出几个很自然的问题我只想看北疆内容。我只想看自然风光。我想搜“喀什”。我想把景点、美食、路线分开看。没有结果时能不能给我一点提示。《疆域纪行》的探索页就是围绕这些问题设计的。它不依赖后端搜索而是直接在本地静态内容库上做过滤。探索页状态设计相关状态在Index.ets中State searchText: string ; State exploreType: string spot; State exploreRegion: string all; State exploreTheme: string all; State exploreSort: string recommend; State searchHistory: string[] [];这几个状态对应探索页的五个维度searchText关键词exploreType景点 / 美食 / 路线 / 城市exploreRegion全部 / 北疆 / 南疆 / 连接地带exploreTheme自然风光 / 人文古城 / 美食小吃exploreSort推荐排序 / 短时优先状态拆得足够细页面逻辑就可以很清楚任何筛选项变化只要更新对应状态列表就会重新计算。探索页 UI 结构ExplorePage()的结构如下Builder ExplorePage() { Column({ space: 16 }) { this.PageHeader(探索, Explore) this.ExploreSearchBar() this.ExploreSearchHistory() Row({ space: 8 }) { this.ExploreTypeChip(spot, 景点) this.ExploreTypeChip(food, 美食) this.ExploreTypeChip(route, 路线) this.ExploreTypeChip(city, 城市) } Scroll() { Row({ space: 8 }) { this.ExploreFilterChip(all, 全部地区, region) this.ExploreFilterChip(north, 北疆, region) this.ExploreFilterChip(south, 南疆, region) this.ExploreFilterChip(connector, 连接地带, region) } } .scrollable(ScrollDirection.Horizontal) Scroll() { Row({ space: 8 }) { this.ExploreFilterChip(all, 全部主题, theme) this.ExploreFilterChip(nature, 自然风光, theme) this.ExploreFilterChip(culture, 人文古城, theme) this.ExploreFilterChip(food, 美食小吃, theme) } } .scrollable(ScrollDirection.Horizontal) } }地区和主题都用了横向滚动因为中文标签长度不固定。与其硬塞在一行导致压缩不如让它自然横滑。搜索框输入和提交分离搜索框的核心代码TextInput({ placeholder: 搜索景点 / 美食 / 城市 / 路线, text: this.searchText }) .layoutWeight(1) .height(44) .borderRadius(22) .backgroundColor(#FFFFFF) .fontSize(14) .onChange((value: string) { this.searchText value; }) Button(搜索) .width(64) .height(44) .onClick(() { this.commitSearchTerm(this.searchText); })这里采用了“输入即改变搜索词点击搜索才写入历史”的策略。这样设计的好处是输入过程中列表可以即时过滤。搜索历史不会被每个半成品输入污染。用户点击热门词或历史词时也可以复用同一套提交逻辑。搜索历史去重提交搜索词的逻辑private commitSearchTerm(term: string): void { const keyword term.trim(); if (keyword.length 0) { return; } this.searchText keyword; this.searchHistory [keyword, ...this.searchHistory.filter((item: string) item ! keyword)].slice(0, 6); this.persistSearchHistory(); }这段代码完成了四件事去掉首尾空格。空关键词不处理。新关键词放到最前面。旧列表里相同关键词先移除避免重复。最多保留 6 条。搜索历史功能不复杂但如果不做去重和数量限制体验会很快变乱。本地搜索匹配景点和美食的匹配逻辑private matchItem(item: AtlasItem): boolean { if (this.searchText.trim().length 0) { return true; } const keyword this.searchText.trim().toLowerCase(); return item.title.toLowerCase().indexOf(keyword) 0 || item.english.toLowerCase().indexOf(keyword) 0 || item.region.toLowerCase().indexOf(keyword) 0 || item.summary.toLowerCase().indexOf(keyword) 0; }路线匹配类似private matchRoute(item: RouteItem): boolean { if (this.searchText.trim().length 0) { return true; } const keyword this.searchText.trim().toLowerCase(); return item.title.toLowerCase().indexOf(keyword) 0 || item.region.toLowerCase().indexOf(keyword) 0 || item.summary.toLowerCase().indexOf(keyword) 0; }这里没有引入复杂搜索引擎而是基于标题、英文名、地区、摘要做包含匹配。对当前内容量来说这已经足够。如果后续内容扩展到几百条可以再做搜索字段预处理。拼音字段。标签匹配。简单倒排索引。搜索结果排序权重。MVP 阶段先把体验闭环做完整更重要。地区筛选用业务规则归类项目没有为每条数据额外写regionType而是通过地区文本判断private isNorthRegion(region: string): boolean { return region.indexOf(北疆) 0 || region.indexOf(阿勒泰) 0 || region.indexOf(乌鲁木齐) 0 || region.indexOf(伊犁) 0 || region.indexOf(博尔塔拉) 0 || region.indexOf(昌吉) 0; } private isSouthRegion(region: string): boolean { return region.indexOf(喀什) 0 || region.indexOf(和田) 0 || region.indexOf(库车) 0 || region.indexOf(阿克苏) 0 || region.indexOf(巴州) 0 || region.indexOf(轮台) 0 || region.indexOf(南疆) 0; }这种写法对 MVP 很快但也有升级空间。更严谨的方式是在数据模型里增加regionGroup: north | south | connector这样筛选就不依赖字符串。但当前项目内容量可控字符串归类可以接受。主题筛选基于 tags主题筛选使用tagsprivate matchTheme(tags: string[]): boolean { if (this.exploreTheme all) { return true; } if (this.exploreTheme nature) { return tags.some((tag: string) [湖泊, 雪山, 草原, 森林, 沙漠, 日落].indexOf(tag) 0); } if (this.exploreTheme culture) { return tags.some((tag: string) [古城, 人文, 市集, 文化].indexOf(tag) 0); } return tags.some((tag: string) [美食, 主食, 小吃, 羊肉, 烧烤].indexOf(tag) 0); }这是一种很适合内容 App 的做法数据里不需要为每个主题单独建字段只要维护好标签页面就可以根据标签组合出不同主题视图。最终列表如何生成景点和美食列表private getExploreAtlasItems(): AtlasItem[] { const source: AtlasItem[] this.exploreType food ? this.foods : this.spots; return this.sortAtlasItems(source .filter((item: AtlasItem) this.matchItem(item)) .filter((item: AtlasItem) this.matchRegion(item.region)) .filter((item: AtlasItem) this.matchTheme(item.tags))); }路线列表private getExploreRouteItems(): RouteItem[] { return this.sortRouteItems(this.routes .filter((item: RouteItem) this.matchRoute(item)) .filter((item: RouteItem) this.matchRegion(item.region)) .filter((item: RouteItem) this.matchTheme(item.tags))); }这里的链式过滤非常清楚关键词匹配地区匹配主题匹配排序页面展示时只关心结果if (this.exploreType spot) { ForEach(this.getExploreAtlasItems(), (item: AtlasItem) { this.FeatureCard(item) }, (item: AtlasItem) item.id) } else if (this.exploreType food) { ForEach(this.getExploreAtlasItems(), (item: AtlasItem) { this.ListCard(item) }, (item: AtlasItem) item.id) }这就是状态驱动 UI 的优势。筛选逻辑集中在方法里UI 只负责根据类型选择卡片。空状态不能省探索页还有空状态if ((this.exploreType city this.getExploreCities().length 0) || (this.exploreType route this.getExploreRouteItems().length 0) || ((this.exploreType spot || this.exploreType food) this.getExploreAtlasItems().length 0)) { this.EmptyCard(没有匹配内容, 可以切换地区、主题或清空搜索关键词后再试。) }空状态的价值是告诉用户“不是 App 坏了而是当前条件没有结果”。尤其是多条件筛选时用户很容易把搜索词、地区、主题组合成无结果状态。一个成熟的 App不能只设计有数据时的页面。本篇小结探索页的核心经验可以总结为搜索词、类型、地区、主题、排序分别建状态。输入和提交搜索历史分离。本地搜索先覆盖标题、英文名、地区、摘要。地区筛选和主题筛选都集中在纯函数里。列表渲染只消费最终过滤结果。无结果时必须给出明确空状态。下一篇我们看本地持久化如何用 Preferences 保存收藏、搜索历史和行程清单让单机 App 也拥有真实的个人化体验。

相关新闻

英雄联盟战绩查询神器:Seraphine 实战指南

英雄联盟战绩查询神器:Seraphine 实战指南

英雄联盟战绩查询神器:Seraphine 实战指南 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 还在为查看队友战绩而频繁切换网页吗?还在为分析对手实力而手忙脚乱吗?Seraphine…

2026/7/10 23:28:41阅读更多 →
空洞骑士模组管理的终极解决方案:Scarab如何彻底改变你的游戏体验

空洞骑士模组管理的终极解决方案:Scarab如何彻底改变你的游戏体验

空洞骑士模组管理的终极解决方案:Scarab如何彻底改变你的游戏体验 【免费下载链接】Scarab An installer for Hollow Knight mods written with Avalonia. 项目地址: https://gitcode.com/gh_mirrors/sc/Scarab 你是否曾经因为模组安装的复杂性而放弃为《空洞…

2026/7/10 23:28:41阅读更多 →
【从5W到240W快充拓扑演进:看懂消费电源与新能源车载电源的底层同源逻辑【2026工程师干货】】

【从5W到240W快充拓扑演进:看懂消费电源与新能源车载电源的底层同源逻辑【2026工程师干货】】

从5W到240W快充拓扑演进:看懂消费电源与新能源车载电源的底层同源逻辑【2026工程师干货】 📌 内容简述(读者速览) 2026年电源行业高频热搜词:高功率密度、软开关、GaN/SiC宽禁带、拓扑迭代、算电协同、800V高压平台。 …

2026/7/10 23:28:41阅读更多 →
Python自动化测试实战:Pytest从入门到企业级应用

Python自动化测试实战:Pytest从入门到企业级应用

1. 项目概述 如果你正在用Python做开发,无论是写个小工具还是维护一个大型系统,迟早会碰到一个灵魂拷问:这代码改完了,到底有没有把别的地方搞坏?手动测试?一次两次还行,但每次改完都手动点一遍…

2026/7/11 0:28:44阅读更多 →
【限时解密】头部DTC品牌不愿公开的Midjourney产品图黑盒:动态光照建模、材质物理模拟、多光源合成技术——仅剩最后23个内测席位

【限时解密】头部DTC品牌不愿公开的Midjourney产品图黑盒:动态光照建模、材质物理模拟、多光源合成技术——仅剩最后23个内测席位

更多请点击: https://kaifayun.com 第一章:Midjourney电商产品图的技术演进与商业价值重构 Midjourney自V5版本起,通过引入更精细的提示词解析引擎、高保真纹理建模能力及多轮迭代式图像优化机制,显著提升了电商级产品图的生成质…

2026/7/11 0:28:44阅读更多 →
Python进阶:推导式的嵌套使用(多层循环)

Python进阶:推导式的嵌套使用(多层循环)

Python进阶:推导式的嵌套使用(多层循环)📚 本章学习目标:深入理解推导式的嵌套使用(多层循环)的核心概念与实践方法,掌握关键技术要点,了解实际应用场景与最佳实践。本文…

2026/7/11 0:28:44阅读更多 →
【网络专栏】1. 学HTTP别光背概念:这4个实战知识,开发测试每天都用得到

【网络专栏】1. 学HTTP别光背概念:这4个实战知识,开发测试每天都用得到

文章目录一、GET与POST:选型错了,线上必出业务故障核心本质:幂等性与副作用实战踩坑案例(音乐APP场景)实践选型参考二、301/302/304:重定向与缓存,是性能优化的基础301 永久重定向:慎…

2026/7/11 0:28:44阅读更多 →
Grammarly Premium自动化Cookie采集系统:技术架构与实现解析

Grammarly Premium自动化Cookie采集系统:技术架构与实现解析

Grammarly Premium自动化Cookie采集系统:技术架构与实现解析 【免费下载链接】autosearch-grammarly-premium-cookie 免费白嫖使用Grammarly Premium高级版 项目地址: https://gitcode.com/gh_mirrors/au/autosearch-grammarly-premium-cookie 在当今数字写作…

2026/7/11 0:28:44阅读更多 →
使用GA1102CAL 数字存储示波器测试I²C数字串行信号(二)

使用GA1102CAL 数字存储示波器测试I²C数字串行信号(二)

核心计算结果速览 时钟参数 参数 数值 公式 时钟周期 50 ns T = 1/f = 1/20MHz 高电平时间 25 ns T50% 占空比 低电平时间 25 ns T50% 占空比 有效数据率 ~17.78 Mbps 20Mbps 8/9(含 ACK 位开销) 示波器测量要求 场景 带宽要求 采样率要求 <

2026/7/11 0:23:44阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述&#xff1a;从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目&#xff0c;叫 skills4/skills &#xff0c;它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景&#xff1a;一个旨在展示或教授某种技能的仓库&#xff0c;本身却成了安…

2026/7/10 12:10:00阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示&#xff1a;因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战&#xff1a;从“黑箱预测”到“可信推理”2026年6月&#xff0c;第7届机器学习与趋势国际会议&#xff08;MLT 2026&#xff09;将在悉尼召开。会议议程中&#xff0c;“因果与可解释机器学习…

2026/7/10 12:29:21阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时&#xff0c;通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中&#xff0c;是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/10 4:59:05阅读更多 →
Premiere Pro 2025安装失败原因与AGSIS验证绕过指南

Premiere Pro 2025安装失败原因与AGSIS验证绕过指南

1. 为什么2025版PR安装比以往更“磨人”&#xff1f;——从弹窗警告到路径陷阱的真实处境 Premiere Pro 2025版不是简单的一次版本迭代&#xff0c;它是一道分水岭。我从去年底开始帮影视工作室、高校剪辑实验室和自由职业者部署2025环境&#xff0c;累计处理了137台设备&#…

2026/7/11 0:03:43阅读更多 →
5款实用macOS系统优化工具:让你的Mac运行更流畅更高效

5款实用macOS系统优化工具:让你的Mac运行更流畅更高效

5款实用macOS系统优化工具&#xff1a;让你的Mac运行更流畅更高效 【免费下载链接】open-source-mac-os-apps &#x1f680; Awesome list of open source applications for macOS. https://t.me/s/opensourcemacosapps 项目地址: https://gitcode.com/gh_mirrors/op/open-so…

2026/7/11 0:03:43阅读更多 →
5分钟完全掌握:ComfyUI ControlNet预处理器终极使用指南

5分钟完全掌握:ComfyUI ControlNet预处理器终极使用指南

5分钟完全掌握&#xff1a;ComfyUI ControlNet预处理器终极使用指南 【免费下载链接】comfyui_controlnet_aux ComfyUIs ControlNet Auxiliary Preprocessors 项目地址: https://gitcode.com/gh_mirrors/co/comfyui_controlnet_aux 想要让AI图像生成真正听从你的指挥吗&…

2026/7/11 0:03:43阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时&#xff0c;发现推理速度只有可怜的 1-2 FPS&#xff0c;而别人的演示视频却能跑到 30 FPS 以上&#xff0c;那么问题很可能不在模型本身&#xff0c;而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后&#xff0c;会直接使用官方示例…

2026/7/10 13:39:09阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一&#xff1a;为什么你需要了解 Coze 和 Dify&#xff1f;如果你对 AI 应用开发感兴趣&#xff0c;但一看到“大模型”、“智能体”、“工作流”这些词就头疼&#xff0c;觉得门槛太高&#xff0c;那这篇文章就是为你准备的。很多开发者&#xff0c;包括我自己&#…

2026/7/10 22:20:33阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会&#xff1a;配图一直是个让人头疼的问题。2026年&#xff0c;AI生图工具已经非常成熟了&#xff0c;但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1&#xff1a;速度之王2026年6月11日&#xff0c…

2026/7/10 17:29:22阅读更多 →