公司动态
ArkUI 搜索建议点了没反应怎么办:中式美食历史词、热门食材和菜谱结果怎么统一跳转
用户点搜索建议却没有明显反馈或者跳到了列表但关键词没有跟着变。 中式美食里同一个菜谱会从搜索、分类、收藏、推荐和购物清单多个入口出现状态一旦没拆清楚用户看到的就是点了没反应、返回位置丢了、统计不准或者卡片状态串到别的菜上。项目说明应用名称中式美食这篇解决什么用户点搜索建议却没有明显反馈或者跳到了列表但关键词没有跟着变。谁会搜这篇做 HarmonyOS 搜索页、菜谱页、商品列表页的开发者会搜这篇读完能解决什么明确状态归属、Repository 边界、ArkUI/ArkTS 代码拆法和回归验收当前项目证据中式美食已经有搜索、分类、详情、收藏、购物清单和最近浏览多入口状态会互相影响先把问题说清楚这个问题要拆到页面、状态和数据层一起看。只改页面文案解决不了根因因为页面看到的是结果真正决定结果的是 ViewModel 怎么保存状态、Repository 怎么查数据、路由或存储层怎么留下稳定事实。章节重点问题从哪里来先把用户能看到的异常讲明白状态怎么拆页面状态、查询状态、路由状态不要混用Repository 怎么接数据层只负责事实不负责 UI 文案ArkUI 怎么写组件只做展示和事件出口不偷改全局状态怎么验收用连续点击、刷新、返回、筛选这些动作压一遍验证环境和文件边界项目说明DevEco Studio6.x 系列HarmonyOS SDKAPI 12 目标工程应用模型Stage 模型语言和 UIArkTS、ArkUI 声明式页面相关 APINavPathStack、pushPath、pop、路由来源相关模块SearchPage、SearchViewModel、SuggestionRepository、AppRouter层级负责什么不应该做什么ArkUI 页面展示状态、收集点击、给用户反馈不直接拼查询、不偷偷改仓储ViewModel保存当前页面状态调用 Repository屏蔽旧结果不关心 UI 细节Repository查数据、写数据、做归一化和事务边界不决定页面怎么排版路由或存储提供稳定事实不承担临时交互判断先把状态模型写出来如果页面上来就用几个零散变量凑逻辑后面一定会乱。中式美食这类菜谱应用的麻烦点在于一个操作会影响列表、详情、收藏、购物清单和最近浏览。先把状态模型写出来后面才知道该更新谁。exporttypeLoadStatusidle|loading|ready|empty|errorexportinterfacePageState{keyword:stringstatus:LoadStatus selectedIds:string[]source:search|favorite|recommend|shoppinganchorId?:stringmessage?:string}这段模型不追求复杂关键是把“用户正在看什么”和“页面为什么这样显示”分开。比如空结果不是数组长度为 0 这么简单它还可能带着关键词、来源和下一步建议。字段作用出错表现keyword当前搜索词或筛选词返回后输入框空了status页面当前状态加载、空态、错误态互相覆盖selectedIds当前勾选或命中项刷新后统计不准source从哪个入口进入详情返回错页anchorId列表恢复位置返回后回到顶部ViewModel 不要把动作写散页面里每个按钮都直接改状态短期看很快后期调试很痛。我的习惯是让页面只调用动作动作里统一处理旧状态、请求版本和刷新边界。exportclassFeatureViewModel{state:PageState{keyword:,status:idle,selectedIds:[],source:search}privaterequestVersion0asyncreload(keyword:string):Promisevoid{constversionthis.requestVersionthis.state.keywordkeyword.trim()this.state.statusloadingconstresultawaitthis.repository.query(this.state.keyword)if(version!this.requestVersion)returnthis.state.statusresult.length0?ready:empty}}这里的requestVersion很土但很管用。用户连续输入或连续切换 tab 时旧请求回来不能覆盖新结果。这个坑在搜索页、购物清单页和推荐页都会遇到。exportinterfaceDetailRouteContext{dishId:stringsource:search|favorite|recommendkeyword?:stringcategoryId?:stringscrollAnchor?:string}this.pathStack.pushPath({name:DishDetail,param:routeContext})functionrestoreFromDetail(ctx:DetailRouteContext):void{this.keywordctx.keyword??this.currentCategoryIdctx.categoryId??allthis.pendingScrollAnchorctx.scrollAnchorthis.refreshList()}ArkUI 页面只做展示和事件出口页面代码越像“业务大杂烩”越难定位问题。中式美食里我会把点击动作放到 ViewModel 或 Router页面只负责把当前状态画出来。Componentstruct FeaturePanel{vm:FeatureViewModelbuild(){Column(){if(this.vm.state.statusloading){Text(正在加载)}elseif(this.vm.state.statusempty){Text(没有找到合适结果换个关键词试试)}else{List(){ForEach(this.vm.items,(item:RecipeCardModel){ListItem(){Text(item.name)}},(item:RecipeCardModel)item.id)}}}}}ForEach的 key 一定要用业务 id。不要用 index。index 在搜索、筛选、排序、插入、删除之后都会变卡片复用就容易把上一条的状态带到下一条。容易忽略的边界这几个边界如果不提前写清楚明天换一个页面还会踩同样的坑。边界错误写法更稳的写法点击动作子组件里直接改 Repository子组件抛回调父级 ViewModel 处理返回来源只传dishId同时传source和上下文快照搜索结果页面里做字符串匹配Repository 统一归一化和排序勾选统计底部栏自己算一遍从 ViewModel 派生同一份统计列表 key用 index用稳定业务 id回归验收验收动作预期结果连续快速点击两次入口不重复写入不出现旧状态覆盖新状态筛选后进入详情再返回关键词、分类、排序、滚动位置保持一致收藏或勾选后刷新页面页面状态和本地数据一致切换分类再清空条件列表回到默认态不残留旧筛选删除或新增一条数据ForEach 不串状态卡片展示不乱functionverifyState(current:PageState):boolean{if(current.statusreadycurrent.keyword.length0)returntrueif(current.statusemptycurrent.messageundefined)returnfalsereturncurrent.selectedIds.every((id:string)id.length0)}我的验收习惯是先用手点出问题再用表格固定动作。只看代码很容易觉得没问题但一旦连续点击、返回、刷新、清空一起压状态边界是否清楚马上就能看出来。最后小结搜索建议 这类问题不要只看页面表象。真正要处理的是谁保存状态谁查数据谁负责跳转谁只展示结果。中式美食后面继续扩菜谱、购物清单和推荐入口时只要这些边界不乱新功能就不会把旧页面拖出问题。