HarmonyOS 应用 · 寝室投票结果页深度解析:isWin 冠军标记与 Stack 占比条
HarmonyOS 应用 · 寝室投票结果页深度解析:isWin 冠军标记与 Stack 占比条
项目编号:49-dorm-vote 技术栈:HarmonyOS ArkTS · ArkUI 声明式 UI · Stage 模型 本篇页面:结果页
ProfileTab.ets(91 行) 系列导航:(一)首页 · (二)创建 · (三)投票 · (四)结果

一、ResultItem interface:isWin 胜出布尔位
interface ResultItem {
id: number;
emoji: string;
name: string;
votes: number;
percent: number;
isWin: boolean;
}
六个字段:id 是 key 锚点,emoji 是选项图标,name 是选项名,votes 是得票数,percent 是得票占比,isWin 是"是否胜出"的布尔位。isWin 是结果页的核心状态字段——驱动 👑 皇冠、票数颜色、占比条颜色三态变化。
private results: ResultItem[] = [
{ id: 1, emoji: '🍛', name: '麻辣香锅', votes: 2, percent: 50, isWin: true },
{ id: 2, emoji: '🍜', name: '黄焖鸡米饭', votes: 1, percent: 25, isWin: false },
{ id: 3, emoji: '🍔', name: '汉堡炸鸡', votes: 1, percent: 25, isWin: false },
{ id: 4, emoji: '🍕', name: '披萨意面', votes: 0, percent: 0, isWin: false }
];
四个选项:麻辣香锅 2 票 50% 胜出、黄焖鸡/汉堡炸鸡各 1 票 25%、披萨 0 票。总票数 4 与投票页"3/4 人已投"对应——结果页是已结束投票的最终统计。
二、页面骨架:WinnerCard + ResultList
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.WinnerCard()
this.SectionTitle('投票详情')
this.ResultList()
}
.width('100%')
.padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 })
}
.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
}
.width('100%').height('100%').backgroundColor(C.bg)
}
Header 在 Scroll 外固定,Scroll 内三块:WinnerCard(冠军卡)+ SectionTitle(分区标题)+ ResultList(结果列表)。与班费统计页的三块图表不同,结果页先给结论(冠军卡)再看明细——投票结果的核心是"谁赢了"。
三、WinnerCard:粉色渐变冠军卡
@Builder
WinnerCard() {
Column({ space: 10 }) {
Text('🏆').fontSize(44)
Text('麻辣香锅').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#FFFFFF')
Text('以 2 票(50%)胜出').fontSize(13).fontColor('#FFFFFF').opacity(0.9)
}
.width('100%').padding(24).borderRadius(D.rLg)
.linearGradient({ angle: 135, colors: [[C.primary, 0.0], [C.accent, 1.0]] })
}
44 号 🏆 + 20 号加粗白色胜出项 + 13 号白色半透明"以 2 票(50%)胜出"。linearGradient 135° 从 C.primary(#FF6B9D)到 C.accent(#FF9DBA),粉色渐变 + 🏆 的组合传递"庆祝/胜出"的氛围。三行纵向堆叠——图标 → 胜者 → 数据,冠军卡是投票结果的"颁奖台"。
四、ResultList:isWin 驱动的三态 + Stack 占比条
@Builder
ResultList() {
Column({ space: 12 }) {
ForEach(this.results, (r: ResultItem) => {
Column({ space: 8 }) {
Row({ space: 10 }) {
Text(r.emoji).fontSize(22)
Text(r.name).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text).layoutWeight(1)
if (r.isWin) {
Text('👑').fontSize(16)
}
Text(r.votes + ' 票').fontSize(13).fontColor(r.isWin ? C.primary : C.textDim)
}.width('100%')
Stack({ alignContent: Alignment.Start }) {
Row().width('100%').height(10).backgroundColor(C.cardSoft).borderRadius(5)
Row().width(r.percent + '%').height(10)
.backgroundColor(r.isWin ? C.primary : C.textDim).borderRadius(5)
}.width('100%')
}
.width('100%').padding(14).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
}, (r: ResultItem) => r.id.toString())
}.width('100%')
}
isWin 驱动三态变化——皇冠、票数色、占比条色三路分支:
// 皇冠:胜出项显示 👑
if (r.isWin) { Text('👑').fontSize(16) }
// 票数颜色:主色粉 vs 灰色
.fontColor(r.isWin ? C.primary : C.textDim)
// 占比条颜色:粉色 vs 灰色
.backgroundColor(r.isWin ? C.primary : C.textDim)
胜出项(麻辣香锅)显示 👑 皇冠 + 粉色票数 + 粉色占比条,落选项灰色系——冠军的视觉突出与"一枝独秀"的排序表达。
Stack 双层占比条——与班费统计页的 CatChart 同款手法:
Stack({ alignContent: Alignment.Start }) {
// 底层:100% 宽浅灰轨道
Row().width('100%').height(10).backgroundColor(C.cardSoft).borderRadius(5)
// 上层:percent% 宽彩色条
Row().width(r.percent + '%').height(10).backgroundColor(r.isWin ? C.primary : C.textDim).borderRadius(5)
}
Alignment.Start 让彩色条从左对齐。10px 高度比班费统计页的 8px 粗——结果页的占比条更醒目。50%/25%/25%/0% 的占比直观呈现票数分布。



五、跨页数据自洽:结果页与投票页的联动
- "麻辣香锅"胜出 ↔ 投票页四个选项之一(
id: 4 麻辣香锅)——结果页与投票页同源 - 总票数 4 ↔ 投票页"3/4 人已投"——Demo 中结果页比投票页多 1 票(模拟有人后续投票)
- 50% = 2/4 票 ↔
total: 4人数——占比按总人数计算 - 👑 胜出标记 ↔ 首页"已结束"投票的结果展示——首页"查看结果"按钮的落地页
六、可演进方向
| 方向 | 现状 | 改法 | 收益 |
|---|---|---|---|
| 数据来源 | 静态数组 | 从投票记录动态统计 | 结果随投票更新 |
| 平票处理 | 仅单冠军 | 平票时并列显示多个 👑 | 平票场景覆盖 |
| 投票人明细 | 无 | 显示"谁投了这项" | 透明化投票 |
| 结果分享 | 无 | 生成结果图 + 分享卡片 | 传播投票结果 |
| 时间维度 | 无 | 展示投票时间线(何时开/关) | 投票过程可追溯 |
结果页是投票的最终呈现——WinnerCard 粉色渐变的"颁奖台" + ResultList 的 isWin 驱动三态 + Stack 占比条。从条件渲染的 👑 皇冠到双层占比条的叠加技巧,每一层都有明确的职责边界。

更多推荐


所有评论(0)