ArkUI 筛选清空后列表还乱怎么办:中式美食关键词、分类和排序状态怎么收口
用户先搜“牛肉”再点“家常菜”又切到“收藏优先”最后点“清空”。如果页面还保留旧关键词或者列表标题显示已清空但结果还是分类后的数据这个体验会很糟。中式美食的搜索、分类、推荐、收藏都会碰到类似问题。ArkUI 页面写起来很快但筛选状态如果散在多个组件里后面一定会出现“清空了却没真清空”的问题。应用名称中式美食这篇解决什么关键词、分类、排序、分页混在一起后清空筛选不彻底谁会搜这篇做 HarmonyOS 列表页、搜索页、分类页、筛选条的 ArkUI 开发者点进来能拿到什么筛选状态模型、reset 规则、ViewModel 输出和验收用例当前项目证据SearchViewModel、搜索历史/热门词/结果状态、购物清单列表状态HarmonyOS 目标能力ArkUI、ArkTS、ViewModel、ForEach、List、状态收口本章导读这篇不讲花哨筛选组件只讲一个真实问题清空筛选后列表到底应该回到什么状态。中式美食这种应用列表入口多筛选条件也多。关键词、分类、排序、分页、空状态如果各管各的页面很快就会出现互相打架。章节重点问题从哪里来为什么组件里各存一份状态会乱状态模型怎么定用一个FilterState收口关键词、分类、排序reset 怎么写清空不是只清输入框还要清分页和空态ArkUI 怎么接页面只展示状态不自己拼条件怎么验收搜索、分类、排序、清空、返回都要测当前验证环境和技术栈项目说明DevEco Studio6.x 系列HarmonyOS SDKAPI 12/ArkTS 工程UI 层ArkUI 声明式页面状态层ViewModel 统一输出列表状态当前项目参考中式美食搜索页、分类列表、推荐流、购物清单验收方式固定操作链路搜索、分类、排序、清空、返回当前中式美食搜索 ViewModel 已经把历史词、热门词和搜索结果分成不同状态。这个方向是对的。下一步要补的是筛选条件本身也要收口。Futurevoidsearch(Stringkeyword,{int page1})async{statestate.copyWith(keyword:keyword,results:constUiState.loading());try{finalresultawait_repository.search(keyword,page:page);statestate.copyWith(results:result.items.isEmpty?constUiState.empty(没有找到相关菜谱):UiState.success(result),);}catch(error){statestate.copyWith(results:UiState.error(error.toString()));}}如果后面关键词、分类、排序都各自写一个State清空按钮就要知道所有状态在哪里漏一个就出问题。先把筛选状态合成一个对象我会先定一个RecipeFilterState。关键词、分类、排序、页码、是否只看收藏都放在一处。页面组件不再各自保存业务条件。typeRecipeSortModedefault|recent|favorite|cookTime;interfaceRecipeFilterState{keyword:string;categoryId:string;sortMode:RecipeSortMode;onlyFavorite:boolean;page:number;}constdefaultFilterState:RecipeFilterState{keyword:,categoryId:,sortMode:default,onlyFavorite:false,page:1};字段什么时候变清空时怎么处理keyword用户输入或点历史词回到空字符串categoryId用户点分类回到全部分类sortMode用户切排序回到默认排序onlyFavorite用户只看收藏回到 falsepage分页加载回到第一页这里最容易漏的是page。很多列表清空后还停在第 3 页结果用户看到的不是默认列表前几条而是默认条件下的第 3 页。reset 不是只清输入框清空按钮必须重置整份筛选状态并重新拉取默认列表。不要只把输入框清空。ObservedexportclassRecipeListViewModel{filter:RecipeFilterState{...defaultFilterState};items:RecipeCardModel[][];loading:booleanfalse;emptyText:string;asyncresetFilter(){this.filter{...defaultFilterState};awaitthis.reload();}}如果只是这样还不够reload也要只读当前filter不要让页面把临时条件传来传去。asyncreload(){this.loadingtrue;this.emptyText;try{constresultawaitthis.repository.query(this.filter);this.itemsresult.items;this.emptyTextresult.items.length0?没有找到符合条件的菜谱:;}finally{this.loadingfalse;}}操作错误写法稳定写法清空关键词只改输入框文字重置整份filter切分类分类组件自己存 idViewModel 更新filter.categoryId切排序排序组件单独请求ViewModel 更新filter.sortMode后 reload翻页页面自己拼 pageViewModel 控制filter.page返回列表重新造默认状态保留 ViewModel 当前状态ArkUI 组件只触发动作页面组件不应该自己拼筛选条件。组件只负责把用户动作告诉 ViewModel。Componentstruct RecipeFilterBar{ObjectLinkvm:RecipeListViewModel;build(){Row(){TextInput({text:this.vm.filter.keyword,placeholder:搜索菜名或食材}).onSubmit((value:string)this.vm.changeKeyword(value))Button(清空).onClick(()this.vm.resetFilter())}}}分类和排序也同理。asyncchangeCategory(categoryId:string){this.filter{...this.filter,categoryId,page:1};awaitthis.reload();}asyncchangeSort(sortMode:RecipeSortMode){this.filter{...this.filter,sortMode,page:1};awaitthis.reload();}每次变更筛选条件都把page拉回 1这样用户不会被旧分页影响。列表渲染只相信 ViewModel 输出列表组件只拿items不要再二次过滤。二次过滤会让标题、空状态和数据对不上。Componentstruct RecipeList{ObjectLinkvm:RecipeListViewModel;build(){List(){ForEach(this.vm.items,(item:RecipeCardModel){ListItem(){RecipeCard({item})}},(item:RecipeCardModel)item.id)}}}页面显示来源不要怎么做列表数据vm.items组件里再 filter 一遍空文案vm.emptyText页面自己猜空态原因当前关键词vm.filter.keywordTextInput 单独维护一份当前分类vm.filter.categoryId分类组件单独存当前排序vm.filter.sortMode排序弹窗自己请求数据请求乱序也要防用户快速输入和切分类时很容易出现后发请求先回来。这个问题不处理列表会被旧结果覆盖。letrequestVersion0;asyncreload(){constcurrentrequestVersion;this.loadingtrue;constresultawaitthis.repository.query(this.filter);if(current!requestVersion){return;}this.itemsresult.items;this.loadingfalse;}这不是复杂优化是列表页基本防线。搜索、分类、排序组合越多越需要请求版本号。工程验收验收项操作通过标准关键词搜索输入“牛肉”列表只显示命中结果分类切换点“家常菜”标题、列表、空态都对应家常菜排序切换切“收藏优先”列表顺序变化筛选条件不丢清空筛选点清空关键词、分类、排序、分页都回默认快速切换连续输入和点分类最终只显示最后一次条件结果返回列表从详情页返回原筛选状态仍在可以把验收写成下面这种检查functionassertDefaultFilter(filter:RecipeFilterState){if(filter.keyword!){thrownewError(清空后 keyword 没有归零);}if(filter.categoryId!){thrownewError(清空后 categoryId 没有归零);}if(filter.sortMode!default){thrownewError(清空后 sortMode 没有回默认);}if(filter.page!1){thrownewError(清空后 page 没有回第一页);}}常见问题和边界问题我的处理筛选状态要不要进 Preferences高频恢复可以进比如最后一次分类普通临时筛选先留 ViewModel清空后要不要保留搜索历史搜索历史可以保留但当前关键词必须清空返回详情后要不要重新请求先保留原列表再按需后台刷新空态文案放哪里ViewModel 输出页面不自己判断分类和关键词能不能同时生效可以但规则必须明确不能组件各自决定本章小结中式美食的列表筛选真正要解决的是“状态只有一个可信来源”。关键词、分类、排序、分页和空态如果散在多个组件里清空按钮永远容易漏。看起来只是一个小交互实际会影响搜索页、分类页、推荐流、收藏页和详情返回。我的做法是把筛选条件合成RecipeFilterState所有入口都通过 ViewModel 改它页面只触发动作和展示结果清空时重置整份状态并把分页拉回第一页。这样写不花哨但可以让用户明确感到我点了什么页面就按什么变我点清空页面就真的回到默认。如果你也在做 HarmonyOS 列表页可以先把所有筛选条件列出来然后只允许一份状态说了算。这个规则定住后面的 UI 才不会越写越乱。