HarmonyOS 应用 · 室友评价评价页深度解析四维星级打分与不可变更新项目编号50-roommate-rate 技术栈HarmonyOS ArkTS · ArkUI 声明式 UI · Stage 模型 本篇页面评价页Func1Tab.ets104 行 系列导航一首页 ·二评价· 三数据 · 四我的一、RateDim interface 与 scores 数组状态interface RateDim { id: number; emoji: string; name: string; score: number; }四个字段id是 key 锚点emoji是维度图标name是维度名score是默认分值。State activeMate: number 0; State scores: number[] [5, 4, 5, 4]; private mates: string[] [阿强, 小明, 大伟]; private dims: RateDim[] [ { id: 1, emoji: , name: 卫生习惯, score: 5 }, { id: 2, emoji: , name: 作息规律, score: 4 }, { id: 3, emoji: , name: 相处融洽, score: 5 }, { id: 4, emoji: , name: 安静程度, score: 4 } ];两个StateactiveMate选中室友索引scores四维评分数组 [5,4,5,4]。mates是被评价室友不含自己dims是四个评价维度卫生习惯/作息规律/相处融洽/安静程度。scores数组与dims数组索引对应——scores[0]是dims[0]的评分。二、setScore不可变更新触发星级刷新private setScore(idx: number, s: number): void { this.scores[idx] s; this.scores this.scores.slice(); }setScore(idx, s)两步不可变更新——先赋值scores[idx] s再slice()生成新引用触发State刷新。与班级任务首页的toggle同款模式直接修改数组元素不触发刷新必须slice()换引用。三、页面骨架MateSelect RateCard CommentCard Buttonbuild() { Column() { this.Header() Scroll() { Column({ space: 16 }) { this.MateSelect() this.RateCard() this.CommentCard() Button(提交评价).fontSize(15).fontColor(#FFFFFF).backgroundColor(C.primary) .width(100%).height(48).borderRadius(D.rMd) .onClick(() { promptAction.showToast({ message: 评价已提交 }); }) } .width(100%) .padding({ left: D.pad, right: D.pad, top: 16, bottom: D.pad this.safeBottom 20 }) } .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top) } .width(100%).height(100%).backgroundColor(C.bg) }Header 在 Scroll 外固定Scroll 内四块MateSelect室友选择 RateCard评分卡 CommentCard留言卡 Button提交。四、MateSelectactiveMate 驱动的等分胶囊Builder MateSelect() { Row({ space: 10 }) { ForEach(this.mates, (m: string, idx: number) { Text(m).fontSize(13) .fontColor(this.activeMate idx ? #FFFFFF : C.textSub) .textAlign(TextAlign.Center).layoutWeight(1).padding({ top: 10, bottom: 10 }) .backgroundColor(this.activeMate idx ? C.primary : C.card).borderRadius(D.rSm) .border({ width: 1, color: this.activeMate idx ? C.primary : C.stroke }) .onClick(() { this.activeMate idx; }) }, (m: string) m) }.width(100%) }activeMate idx驱动选中态靛蓝底白字 靛蓝描边 vs 白底灰字 浅灰描边。三个室友等分onClick设this.activeMate idx切换被评价对象。五、RateCard嵌套 ForEach 星级打分Builder RateCard() { Column({ space: 16 }) { Text(评分维度).fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width(100%) ForEach(this.dims, (d: RateDim, dimIdx: number) { Row({ space: 10 }) { Text(d.emoji).fontSize(20) Text(d.name).fontSize(14).fontColor(C.text).layoutWeight(1) Row({ space: 4 }) { ForEach([1, 2, 3, 4, 5], (star: number) { Text(this.scores[dimIdx] star ? ⭐ : ☆).fontSize(16) .onClick(() { this.setScore(dimIdx, star); }) }, (star: number) star.toString()) } }.width(100%) }, (d: RateDim) d.id.toString()) } .width(100%).padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }嵌套 ForEach——外层遍历维度内层遍历 [1,2,3,4,5] 星级// 外层4 个评价维度 ForEach(this.dims, (d: RateDim, dimIdx: number) { // 内层5 颗星 ForEach([1, 2, 3, 4, 5], (star: number) { // scores[dimIdx] star → 实心星否则空心星 Text(this.scores[dimIdx] star ? ⭐ : ☆) .onClick(() { this.setScore(dimIdx, star); }) }) })scores[dimIdx] star驱动实心/空心星——scores[0] 5时 5 颗全实心scores[1] 4时前 4 颗实心 第 5 颗空心。点击第 3 颗星调setScore(dimIdx, 3)——scores[dimIdx]变 3前 3 颗实心 后 2 颗空心。这是星级打分的经典实现比较驱动渲染点击驱动赋值。六、CommentCardTextArea 留言Builder CommentCard() { Column({ space: 10 }) { Text(评价留言).fontSize(14).fontWeight(FontWeight.Bold).fontColor(C.text).width(100%) TextArea({ placeholder: 写下对室友的评价... }) .backgroundColor(C.cardSoft).borderRadius(D.rSm).height(90) } .width(100%).padding(16).backgroundColor(C.card).borderRadius(D.rLg).border({ width: 1, color: C.stroke }) }TextArea多行输入height(90)容纳多行评价文字。cardSoft浅灰底 rSm小圆角。七、可演进方向方向现状改法收益评价提交Toast 模拟写入评价记录 评分更新评价真实保存室友切换scores 不重置切换时加载该室友已有评分切换不丢失数据评分校验无至少一个维度打分才能提交评价完整性留言绑定TextArea 无 onChange绑定 remark 状态留言随提交保存多轮评价单次记录评价时间 历史追溯评价可回溯评价页是室友评价的核心交互——activeMate驱动室友切换、嵌套 ForEach 实现星级打分、setScore的不可变更新触发星级刷新。从比较驱动的实心/空心星到slice()的数组刷新每一层都有明确的职责边界。系列导航一首页 ·二评价· 三数据 · 四我的