多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

路线筛选以后详情跳错了,城市探索页别再保存数组下标

路线筛选以后详情跳错了,城市探索页别再保存数组下标 城市路线列表在只有三条数据时用下标保存选中项看不出问题。加入距离筛选以后原本排在第二位的“老城咖啡线”可能变成第一位详情区域仍读取routes[1]画面便跳到另一条路线。城市步行探索的页面状态目前用selectedItem: number表示场景列表选择。这个写法适合静态演示继续加入搜索、距离排序和完成进度后就不够稳定。我把选择状态改成路线 ID过滤只改变可见集合不改变用户正在查看的对象。路线、地标和进度分别建模路线定义是相对稳定的内容用户进度会持续变化两者不应揉进同一个可变数组。exportinterfaceLandmark{id:stringname:stringorder:numberlatitude:numberlongitude:number}exportinterfaceWalkRoute{id:stringtitle:stringdistanceMeters:numbertags:string[]landmarkIds:string[]}exportinterfaceRouteProgress{routeId:stringvisitedLandmarkIds:string[]startedAt:numberupdatedAt:number}exportinterfaceRouteFilter{keyword:stringmaxDistanceMeters:numbertag:string}地标坐标属于路线内容是否到访属于进度。用户重新开始路线时只需要替换RouteProgress不用复制一遍地标列表。真实定位能力尚未接入时也可以通过本地按钮模拟到访验证进度计算和界面刷新。过滤结果不拥有选中状态控制器保留完整路线数组和selectedRouteId。搜索结果每次计算详情则始终回到完整数据中按 ID 查找。exportclassRouteExplorerController{privateroutes:WalkRoute[][]privatefilter:RouteFilter{keyword:,maxDistanceMeters:10000,tag:}privateselectedRouteId:stringsetRoutes(routes:WalkRoute[]):void{this.routesroutes.slice()if(!this.routes.some((item:WalkRoute)item.idthis.selectedRouteId)){this.selectedRouteIdthis.routes.length0?this.routes[0].id:}}updateFilter(next:RouteFilter):void{this.filter{...next}}visibleRoutes():WalkRoute[]{constkeywordthis.filter.keyword.trim().toLocaleLowerCase()returnthis.routes.filter((route:WalkRoute){constmatchKeywordkeyword.length0||route.title.toLocaleLowerCase().includes(keyword)constmatchDistanceroute.distanceMetersthis.filter.maxDistanceMetersconstmatchTagthis.filter.tag.length0||route.tags.includes(this.filter.tag)returnmatchKeywordmatchDistancematchTag})}selectRoute(routeId:string):boolean{if(!this.routes.some((item:WalkRoute)item.idrouteId)){returnfalse}this.selectedRouteIdrouteIdreturntrue}selectedRoute():WalkRoute|undefined{returnthis.routes.find((item:WalkRoute)item.idthis.selectedRouteId)}}筛选后选中路线可能暂时不可见。我没有立即把它改成结果第一项因为用户清除筛选时通常希望回到刚才的路线。页面可以显示“当前路线不在筛选结果中”由用户明确选择其他路线。ArkUI 列表键也使用路线 IDForEach若用数组下标做键排序后组件实例可能被复用到另一条数据。路线卡片使用route.id高亮条件也比较 ID。BuilderrouteList(routes:WalkRoute[]){Column({space:12}){ForEach(routes,(route:WalkRoute){Row({space:14}){Column({space:5}){Text(route.title).fontSize(17).fontWeight(FontWeight.Bold)Text(${(route.distanceMeters/1000).toFixed(1)}km ·${route.landmarkIds.length}个地标).fontSize(12)}.layoutWeight(1).alignItems(HorizontalAlign.Start)Text(route.tags[0]??城市漫步).fontSize(12)}.padding(16).backgroundColor(this.selectedRouteIdroute.id?#2AFFFFFF:#102536).border({width:this.selectedRouteIdroute.id?1:0,color:#4DB9FF}).borderRadius(20).onClick((){this.selectedRouteIdroute.id})},(route:WalkRoute)route.id)}}页面原有“滨江艺术线 2.4 km”“老城咖啡线 1.8 km”“夜景天桥线 3.1 km”都可以转换成WalkRoute。当按距离排序时只是传给routeList的顺序发生改变选中的 ID 不变。地标到访需要幂等定位回调可能连续命中同一地标。如果每次都向数组追加 ID页面会显示超过 100% 的完成度。更新进度前先判断是否已经存在。exportfunctionmarkVisited(progress:RouteProgress,landmarkId:string,now:number):RouteProgress{if(progress.visitedLandmarkIds.includes(landmarkId)){returnprogress}return{...progress,visitedLandmarkIds:[...progress.visitedLandmarkIds,landmarkId],updatedAt:now}}exportfunctioncompletion(route:WalkRoute,progress?:RouteProgress):number{if(!progress||route.landmarkIds.length0){return0}constvalidVisitednewSet(progress.visitedLandmarkIds.filter((id:string)route.landmarkIds.includes(id)))returnMath.round(validVisited.size/route.landmarkIds.length*100)}完成度只统计仍属于当前路线的地标。路线内容更新、删除某个地标后旧进度不会把比例撑高。这里使用经纬度只是为了说明数据边界并没有声称已完成真机定位验证。页面重建时恢复的是 ID应用进入后台或窗口重建时需要保存筛选条件、选中路线 ID 和路线进度。保存下标没有意义因为下次启动的路线排序可能已经变化。exportinterfaceExplorerSnapshot{selectedRouteId:stringfilter:RouteFilter progressList:RouteProgress[]savedAt:number}exportfunctionrestoreSelection(snapshot:ExplorerSnapshot,routes:WalkRoute[]):string{if(routes.some((route:WalkRoute)route.idsnapshot.selectedRouteId)){returnsnapshot.selectedRouteId}returnroutes.length0?routes[0].id:}页面的蓝色主题、三条路线和顶部标签切换保留原来的视觉结构数据层改为使用稳定 ID。下一步接入本地路线存储再由定位适配器上报地标到访事件。无论搜索、排序还是窗口重建详情都能继续指向用户真正选中的路线。
返回列表