山海万灵 HarmonyOS 文化知识实战(03):神兽图鉴列表的数据模型

📅 2026/8/4 3:55:05
山海万灵 HarmonyOS 文化知识实战(03):神兽图鉴列表的数据模型
图鉴页同时承担浏览、筛选和进入阅读详情三种任务。若列表卡片只保存名称和配图而详情页另存一套说明、出处与关系数据筛选后很容易出现卡片与详情不一致的问题。山海万灵把BeastItem定为图鉴的稳定读模型页面只传递id展示字段、来源信息和关联节点都从同一条记录解析。一条记录覆盖卡片、详情与路线BeastItem不把“神兽”简化为标题和一段简介。regionId、hallId为图鉴与场馆路线提供连接点source承担出处和阅读信息relations为后续关系推荐保留稳定的节点集合。卡片、详情、场馆和策展入口读取的是同一个对象因此不会在不同页面复制文案。export interface BeastItem { id: string; no: string; name: string; alias: string; regionId: string; hallId: string; summary: string; detail: string; source: SourceInfo; curatorRoute?: string; relations: string[]; }字段组图鉴页的职责详情页与后续模块的职责id、no、name、alias标识卡片并支持关键字匹配用id解析当前对象保留统一身份regionId、hallId显示出没地并参与地域排序跳转展厅、组织导览路线summary、detail卡片只读取摘要详情页展开完整说明source提供简短出处提示展开章节、定位链接和阅读摘录relations不在卡片上堆叠关系交给关联推荐与图谱面板数据模型的边界也很明确列表不保存一份可编辑的详情副本页面状态也不把筛选结果回写到原始目录。只要目录本身不被筛选过程改写切换分类、搜索或排序后仍然可以回到完整图鉴。可见列表是从原始目录计算出来的视图图鉴的筛选状态被收敛到BeastArchiveQuery。分类、搜索词、排序方式和发现筛选都是输入getVisibleBeasts从beasts.slice()开始计算不改变 Repository 提供的原始数组。这样搜索框输入“应龙”后再清空原始条目仍然完整可用。export interface BeastArchiveQuery { category: string; searchText: string; sortMode: string; filterMode: string; } getVisibleBeasts( beasts: BeastItem[], regions: RegionItem[], discoveredIds: string[], query: BeastArchiveQuery ): BeastItem[] { const keyword: string query.searchText.trim(); let result: BeastItem[] beasts.slice(); if (query.category ! BEAST_ARCHIVE_DEFAULT_CATEGORY) { result result.filter((beast: BeastItem): boolean this.matchesCategory(beast, query.category)); } if (keyword.length 0) { result result.filter((beast: BeastItem): boolean this.matchesKeyword(beast, regions, keyword)); } return result.filter((beast: BeastItem): boolean this.matchesFilter(beast, regions, discoveredIds, query.filterMode)); }操作参与计算的输入不应发生的副作用切换分类category与神兽类别修改原始beasts数组输入搜索词名称、别名、摘要与地域名覆盖详情中的detail或source选择“已发现”discoveredIds改写探索进度切换地域排序regionId与 Region 名称改变基础展示顺序关键词匹配覆盖名称、别名、摘要和地域名。读者记得“东海”但记不清神兽全名时仍可通过地域进入对应卡片这种匹配只影响当前视图不制造新的数据副本。private matchesKeyword(beast: BeastItem, regions: RegionItem[], keyword: string): boolean { return beast.name.indexOf(keyword) 0 || beast.alias.indexOf(keyword) 0 || beast.summary.indexOf(keyword) 0 || this.regionName(beast.regionId, regions).indexOf(keyword) 0; }发现状态作为独立输入参与筛选与排序发现记录使用discoveredIds传入 ViewModel而不是写入BeastItem。模型仍描述神兽事实探索进度属于用户状态两者分开后同一份内容目录能适配新访客和已完成探索的访客。页面要显示“已发现”或“待发现”时通过isVisuallyDiscovered读取状态即可。isVisuallyDiscovered(beastId: string, discoveredIds: string[]): boolean { return discoveredIds.indexOf(beastId) 0; } private discoveryOrder(beast: BeastItem, discoveredIds: string[]): number { const discoveredIndex: number discoveredIds.indexOf(beast.id); return discoveredIndex 0 ? discoveredIndex : 1000 this.baseOrder(beast); }排序同样保留明确的兜底规则。按名称或地域排序时baseOrder用作并列时的稳定顺序选择“未发现优先”时尚未发现的条目先出现再使用基础顺序保证列表不抖动。页面每次重新计算都得到可预期的卡片顺序。排序方式第一比较条件并列时的处理发现顺序discoveredIds中的位置未发现条目按基础顺序排在后面名称首字母name.localeCompare使用现有稳定顺序出没地排序Region 名称回退到基础顺序未发现优先是否存在于discoveredIds同组内保留基础顺序详情入口只传递稳定 ID用户点击卡片时页面保存selectedBeastId随后从当前目录解析对象并同步探索记录。详情页、学习卡和策展入口由同一标识衔接不需要在导航参数中复制完整神兽对象。目录刷新后入口仍可通过id获取最新的summary、source与关系数据。private openBeast(beastId: string): void { this.selectedBeastId beastId; this.syncDiscovery(beastId); this.refreshAiContext(beastId); } private getBeast(beastId: string): BeastItem { return this.beasts.find((item: BeastItem) item.id beastId) ?? this.beasts[0]; }图鉴页将beasts、已发现 ID、筛选输入和onOpenBeast回调交给结果网格。结果网格负责渲染卡片状态计算保留在 ViewModel跳转协调留在页面入口三者各自只管理一种职责。ArkTS 的状态管理方式可结合 HarmonyOS 官方状态管理指南 进一步阅读。稳定排序避免筛选后的卡片跳动同一批条目在重复筛选时如果顺序不断变化读者很难判断某张卡片是被过滤掉还是仅仅移动了位置。因此图鉴为目录维护一套基础展示顺序任何排序分支在比较条件相同或找不到预设编号时都回落到该顺序。新增条目不会因为缺少排序权重而丢失仍会以可预期的位置显示在列表末尾。private baseOrder(beast: BeastItem): number { const orderIndex: number BEAST_ARCHIVE_DISPLAY_ORDER.indexOf(beast.id); return orderIndex 0 ? 999 : orderIndex; } private compare( left: BeastItem, right: BeastItem, regions: RegionItem[], discoveredIds: string[], sortMode: string ): number { if (sortMode BEAST_ARCHIVE_SORT_REGION) { const regionCompare: number this.regionName(left.regionId, regions) .localeCompare(this.regionName(right.regionId, regions)); return regionCompare ! 0 ? regionCompare : this.baseOrder(left) - this.baseOrder(right); } return this.discoveryOrder(left, discoveredIds) - this.discoveryOrder(right, discoveredIds); }这项约束尤其适合文化知识图鉴内容运营可以增补条目读者的发现记录也会随探索积累而变化但基础目录、筛选视图和详情入口始终通过同一个id对齐。页面不需要为了“记住位置”额外存储临时索引重新进入图鉴时仍可按同一计算规则恢复可读的浏览顺序。从筛选到详情的验收路径验证图鉴数据模型时可以先进入图鉴并观察分类栏、发现印章与神兽卡片随后切换分类或输入关键词确认结果集随条件变化最后打开应龙等卡片确认名称、摘要、出处和关联信息来自同一条BeastItem。当筛选条件清空后完整目录能够重新出现发现状态也不会被列表操作改写。这套模型让图鉴在内容增加后仍保持可维护新增神兽主要补充目录记录与地域关系卡片筛选、排序与详情解析继续复用统一的id和字段约定。对于文化知识类应用这比在每个页面复制一份展示对象更容易持续校对来源、路线与关联关系。