HarmonyOS 应用 · 室友评价评价页深度解析:四维星级打分与不可变更新
项目编号:50-roommate-rate 技术栈:HarmonyOS ArkTS · ArkUI 声明式 UI · Stage 模型 本篇页面:评价页
Func1Tab.ets(104 行) 系列导航:(一)首页 ·(二)评价· (三)数据 · (四)我的
一、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 } ];两个@State:activeMate(选中室友索引)+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 + Button
build() { 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(提交)。
四、MateSelect:activeMate 驱动的等分胶囊
@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 颗空心。这是星级打分的经典实现:比较驱动渲染,点击驱动赋值。
六、CommentCard:TextArea 留言
@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()的数组刷新,每一层都有明确的职责边界。
系列导航:(一)首页 ·(二)评价· (三)数据 · (四)我的