文章目录前言先把 Pet 模型看清楚页面真正的状态只有三块列表卡片承担的是“快速判断”点击查看详情时发生了什么这个页面目前还只是“查看档案”完整代码最后总结前言宠物健康档案这种页面看起来像是几张卡片加一个详情弹窗但真正要写顺重点不在卡片样式而在数据边界。列表里展示的是概览信息点进去以后展示的是同一只宠物的完整档案如果这里把状态拆乱后面加疫苗提醒、体重曲线、就诊记录都会很难接。这个示例的实现比较克制只有一个Pet模型、一个pets列表、一个selectedPet和一个showDetail。它没有做新增、编辑、远程同步这些扩展能力但正好适合拿来练 ArkUI 里的“列表项选择 弹层详情”这条交互链路。先把 Pet 模型看清楚Pet不是单纯用来渲染名字和头像的它把健康档案里最常见的字段放在了一起品种、年龄、体重、下次疫苗时间、最近体检时间和备注。interfacePet{id:numbername:stringspecies:stringbreed:stringage:numberweight:numberavatar:stringnextVaccine:stringlastCheckup:stringnotes:string}我比较喜欢这个模型的一点是它没有为了页面展示再拆出一堆临时字段。列表卡片和详情弹层都直接消费Pet只是展示的信息密度不同。这样做的好处是数据来源很清楚页面上看到的每个值基本都能回到同一个对象里。需要注意的是当前avatar用的是字符不是真实图片资源。教学示例里这样写很方便但正式项目里通常会换成图片地址或资源引用否则后续做头像上传、缓存、裁剪时会受限制。页面真正的状态只有三块这页没有复杂的计算属性状态关系也很直白pets是主数据源selectedPet表示当前打开详情的是哪一只宠物showDetail控制弹层是否显示。Statepets:Pet[][{id:1,name:豆豆,species:猫,breed:英短,age:3,weight:4.2,avatar:,nextVaccine:2026-09-01,lastCheckup:2026-03-15,notes:喜欢吃鸡肉罐头},]StateselectedPet:Pet|nullnullStateshowDetail:booleanfalse这里有个小细节值得记住详情弹层没有只保存selectedPetId而是直接保存了完整的Pet对象。对静态示例来说没问题代码也更短但如果后面允许编辑宠物资料我会更倾向于保存selectedPetId详情展示时再从pets里找对象。这样能避免列表数据更新后弹层里还拿着旧对象引用的问题。列表卡片承担的是“快速判断”ForEach(this.pets, ...)把每只宠物渲染成一张卡片。卡片上没有把所有字段都塞出来只展示了名字、品种、年龄、体重、下次疫苗时间和最近体检时间。ForEach(this.pets,(pet:Pet){Column(){Row(){Text(pet.avatar).fontSize(48).margin({right:16})Column(){Row(){Text(pet.name).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#333)Text(pet.breed).fontSize(13).fontColor(#FFF)}Text(${pet.species}·${pet.age}岁 ·${pet.weight}kg)}}}})这种取舍挺实际。宠物档案页的列表入口用户通常先关心“是哪只宠物”和“有没有临近健康事项”。所以nextVaccine被放在卡片显眼位置notes则留到详情里看不然列表会很挤。卡片里的体重、年龄、最近体检做成三列指标也是在降低阅读成本。这里没有额外抽HealthIndicator组件是因为示例规模不大如果真实项目里还有心率、驱虫、绝育状态这些字段就应该把这类指标行抽出去否则页面文件会越写越长。点击查看详情时发生了什么这页最关键的交互其实只有一段点击“查看详情”后先把当前pet放进selectedPet再把showDetail改成true。Text(查看详情).fontSize(12).fontColor(#007AFF).onClick((){this.selectedPetpetthis.showDetailtrue})这个顺序不要反过来。虽然在这个案例里反过来也大概率能显示出来但从状态语义上讲应该先准备好弹层要用的数据再打开弹层。尤其是后面如果弹层里加动画、异步加载、表单初始化这个顺序会更重要。弹层渲染也做了一层保护只有showDetail为真并且selectedPet不是null时才展示详情。if(this.showDetailthis.selectedPet!null){Column(){Text(this.selectedPet!.name的档案)Text(this.selectedPet!.avatar).fontSize(60)}}selectedPet!是 ArkTS 里常见的非空断言。这里前面已经用selectedPet ! null判断过所以写起来没问题。但它也提醒了一件事不要在没有判空的地方随手加!那只是把类型提示压下去不是真的处理了空值。这个页面目前还只是“查看档案”顶部有一个但当前源码里它只是一个文本没有绑定新增逻辑。健康小贴士也是固定文案不会根据宠物种类或疫苗日期动态变化。把这些限制说清楚比硬把它讲成完整宠物管理系统更诚实。如果要继续扩展我会先补两个方向一个是新增和编辑宠物资料让pets真的变成可维护的数据另一个是基于nextVaccine做提醒状态比如距离疫苗日期少于 30 天时高亮。到那一步这个页面就不只是静态档案展示而开始有健康管理的味道了。完整代码下面保留案例完整代码方便你直接对照学习、复制和重构。import{router}fromkit.ArkUIinterfacePet{id:numbername:stringspecies:stringbreed:stringage:numberweight:numberavatar:stringnextVaccine:stringlastCheckup:stringnotes:string}EntryComponentstruct PetHealthProfile{Statepets:Pet[][{id:1,name:豆豆,species:猫,breed:英短,age:3,weight:4.2,avatar:,nextVaccine:2026-09-01,lastCheckup:2026-03-15,notes:喜欢吃鸡肉罐头},{id:2,name:旺财,species:狗,breed:柴犬,age:2,weight:10.5,avatar:,nextVaccine:2026-07-20,lastCheckup:2026-05-10,notes:每天需要运动1小时},{id:3,name:小黄,species:猫,breed:金渐层,age:1,weight:3.8,avatar:,nextVaccine:2026-08-15,lastCheckup:2026-04-20,notes:有点挑食},]StateselectedPet:Pet|nullnullStateshowDetail:booleanfalsebuild(){Stack(){Column(){Row(){Image($r(app.media.ic_back)).width(24).height(24).onClick(()router.back())Text(宠物健康档案).fontSize(18).fontWeight(FontWeight.Bold).layoutWeight(1).textAlign(TextAlign.Center)Text().fontSize(22).fontColor(#FF6B9D)}.width(100%).padding({left:16,right:16,top:12,bottom:16})// Pet cardsScroll(){Column({space:16}){ForEach(this.pets,(pet:Pet){Column(){Row(){Text(pet.avatar).fontSize(48).margin({right:16})Column(){Row(){Text(pet.name).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#333)Text(pet.breed).fontSize(13).fontColor(#FFF).backgroundColor(#FF6B9D).borderRadius(10).padding({left:8,right:8,top:3,bottom:3}).margin({left:8})}Text(\\${pet.species}· \${pet.age}岁 · \${pet.weight}kg\).fontSize(14).fontColor(#666).margin({top:4})Row(){Text().fontSize(12)Text(\ 下次疫苗 \${pet.nextVaccine}\).fontSize(12).fontColor(#FF9500)}.margin({top:6})}.layoutWeight(1).alignItems(HorizontalAlign.Start)Text(查看详情).fontSize(12).fontColor(#007AFF).onClick((){this.selectedPetpetthis.showDetailtrue})}.width(100%).margin({bottom:12})// Health indicatorsRow(){Column(){Text(体重).fontSize(11).fontColor(#888)Text(\\${pet.weight}kg\).fontSize(15).fontWeight(FontWeight.Bold).fontColor(#333)}.layoutWeight(1).alignItems(HorizontalAlign.Center)Column(){Text(年龄).fontSize(11).fontColor(#888)Text(\\${pet.age}岁\).fontSize(15).fontWeight(FontWeight.Bold).fontColor(#333)}.layoutWeight(1).alignItems(HorizontalAlign.Center)Column(){Text(最近体检).fontSize(11).fontColor(#888)Text(pet.lastCheckup).fontSize(13).fontWeight(FontWeight.Bold).fontColor(#2ED573)}.layoutWeight(1).alignItems(HorizontalAlign.Center)}.width(100%).backgroundColor(#F8F9FA).borderRadius(10).padding(12)}.width(100%).backgroundColor(#FFF).borderRadius(16).padding(16).shadow({radius:6,color:#0000001A,offsetY:3})})// TipsColumn(){Text( 健康小贴士).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#333).margin({bottom:8})Text(• 定期接种疫苗预防常见疾病).fontSize(13).fontColor(#666).margin({bottom:4})Text(• 每年至少进行一次全面体检).fontSize(13).fontColor(#666).margin({bottom:4})Text(• 保持适量运动控制体重).fontSize(13).fontColor(#666)}.width(100%).backgroundColor(#FFF9E6).borderRadius(12).padding(16).borderWidth(1).borderColor(#FFD700)}.padding({left:16,right:16,bottom:20})}.layoutWeight(1)}.width(100%).height(100%).backgroundColor(#FFF5F8)// Detail modalif(this.showDetailthis.selectedPet!null){Column(){Column(){Row(){Text(this.selectedPet!.name的档案).fontSize(17).fontWeight(FontWeight.Bold).layoutWeight(1)Text(✕).fontSize(18).fontColor(#888).onClick(()this.showDetailfalse)}.width(100%).margin({bottom:16})Text(this.selectedPet!.avatar).fontSize(60).margin({bottom:12})Column({space:8}){Row(){Text(品种).fontSize(14).fontColor(#888).width(80);Text(this.selectedPet!.breed).fontSize(14).fontColor(#333)}Row(){Text(年龄).fontSize(14).fontColor(#888).width(80);Text(\\${this.selectedPet!.age}岁\).fontSize(14).fontColor(#333)}Row(){Text(体重).fontSize(14).fontColor(#888).width(80);Text(\\${this.selectedPet!.weight}kg\).fontSize(14).fontColor(#333)}Row(){Text(疫苗).fontSize(14).fontColor(#888).width(80);Text(this.selectedPet!.nextVaccine).fontSize(14).fontColor(#FF9500)}Row(){Text(体检).fontSize(14).fontColor(#888).width(80);Text(this.selectedPet!.lastCheckup).fontSize(14).fontColor(#2ED573)}Row(){Text(备注).fontSize(14).fontColor(#888).width(80);Text(this.selectedPet!.notes).fontSize(14).fontColor(#666).layoutWeight(1)}}.width(100%).alignItems(HorizontalAlign.Start)}.backgroundColor(#FFF).borderRadius(20).padding(24).width(85%).alignItems(HorizontalAlign.Center)}.width(100%).height(100%).backgroundColor(#00000060).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}}}}最后总结宠物健康档案页 这个案例并不靠复杂技巧取胜它更像一个结构扎实、逻辑完整的业务页面样板。把这篇文章里的状态设计、函数组织和页面分层吃透之后你再去写同类型功能速度会明显快很多。