极速修家电维修App基于HarmonyOS API 24鸿蒙版深度拆解:七Tab差异化布局、呼吸灯动效与订单CRUD闭环实战
极速修家电维修App基于HarmonyOS API 24鸿蒙版深度拆解:七Tab差异化布局、呼吸灯动效与订单CRUD闭环实战
上门家电维修,30分钟极速上门。本文完整拆解一个基于鸿蒙 ArkUI 声明式框架构建的「极速修」上门家电维修应用,覆盖暖橙主题配色系统、七Tab全差异化布局、服务推荐头部、预约表单、订单进度时间轴、维修师头像卡、评价星级墙、会员权益卡、设置清单、呼吸灯动效、订单全生命周期CRUD、Stack弹窗叠加、TextInput输入等核心技术点,附完整源码与运行效果截图。
一、引言:上门维修的数字化变革
在移动互联网与本地生活服务高速发展的今天,上门家电维修已经从传统的"找楼道小广告、电话约师傅、现场砍价",演变为一种高度数字化、流程化、标准化的O2O服务新形态。家电维修,以其"低频高价、强专业属性、上门服务"的特点,正在成为本地生活服务的关键赛道。从空调不制冷到洗衣机漏水,从冰箱结冰到电视花屏,从热水器不加热到油烟机吸力弱,每一个家电故障都关系着千家万户的日常生活品质。然而,维修师傅难找、价格不透明、上门不及时、维修质量无保障,正成为用户获取可靠维修服务的现实痛点。
正是在这样的背景下,极速修类上门维修应用应运而生,并迅速成为本地生活服务的数字基础设施。一个优秀的上门维修应用,不仅需要覆盖从"服务选择"到"订单完成"的完整业务闭环,还要在视觉设计、交互体验、数据呈现上做到专业而高效,让用户在指尖滑动间便能完成选服务、填预约、跟订单、评服务的全流程。
本文将深入剖析一个基于鸿蒙 ArkUI 声明式开发框架构建的极速修上门家电维修应用。该应用以"首页"为核心,横向扩展出预约、订单、维修师、评价、会员、更多七大功能模块,每个模块采用完全不同的布局风格——首页品类网格+热门服务、预约表单+时段选择、订单进度时间轴+消费柱状图、维修师头像卡、评价星级墙、会员权益卡、设置清单,形成了一个完整闭环的上门维修生态。它不仅是一个功能完备的演示项目,更是一份典型的 ArkUI 工程实践范本,涵盖了状态管理、组件化设计、Stack弹窗叠加、呼吸灯动效、订单全生命周期CRUD、TextInput输入、会员权益卡渐变等关键技术点。

二、应用整体架构概览
整个应用采用了"单入口 + 七Tab全差异化布局 + Stack弹窗叠加"的本地生活服务应用架构。入口组件 Page813 作为整个应用的根容器,负责管理当前激活的 Tab 状态、订单列表数据以及三个弹窗的显隐状态。
应用一共划分为七个一级页面:首页(HOME)、预约(BOOK)、订单(ORDER)、维修师(TECH)、评价(REVIEW)、会员(VIP)以及更多(MORE)。与之前项目"五/六个页面布局风格统一或半差异化"不同,本应用的七个 Tab 每个布局完全不同——首页采用品类四列网格+热门服务列表、预约采用表单+时段选择、订单采用进度时间轴+近6月维修次数柱状图、维修师采用头像卡列表、评价采用星级墙+三栏统计、会员采用渐变权益卡+六宫格权益、更多采用设置开关清单,这种"一页一型"的差异化设计最大程度还原了真实上门维修 App 的交互体验。底部导航采用独创的两排布局(4+3),第一排首页/预约/订单/维修师,第二排评价/会员/更多,解决了 7 个 Tab 单排拥挤的问题。
在数据层面,应用定义了一套完整的类型系统,包括色彩主题、服务元数据、订单元数据、维修师元数据、评价元数据等,并通过常量数组将数据与 UI 表现层解耦。所有业务数据以 @Observed 装饰的可观察类为载体(Service/OrderInfo/Technician/Review),配合 8 个服务、6 条订单、6 个维修师、8 条评价的模拟数据,模拟了一个真实的上门维修数据环境。所有数据类都带有 constructor 构造函数,使用 new Service(...) 方式实例化。
在交互层面,应用通过 Stack 布局叠加实现了三类弹窗的显示:预约维修、修改订单状态、取消订单。弹窗以 Stack 叠加全屏半透明遮罩 + 居中卡片的形式呈现,zIndex 999 确保弹窗在最上层,遮罩点击可关闭。与之前项目"纯展示型弹窗"不同,本应用的弹窗真正实现了订单数据的全生命周期CRUD——预约维修会向 orderList 头部 unshift 新订单(待接单状态,含 TextInput 问题描述输入)、修改订单状态会修改数组指定元素的 status 字段(含 TextInput 状态输入)、取消订单会 splice 删除指定元素(二次确认),配合 @State 响应式刷新,形成了完整的订单管理 CRUD 闭环。
在视觉层面,应用独创了"服务推荐头部"设计——顶部固定显示当前定位(📍成都)+ 服务承诺卡(⚡平均30分钟上门 + 认证维修师·价格透明·90天质保 + 预约按钮)+ 优惠横条(Scroll horizontal 横滑:新客减¥20/会员8折/90天质保/24小时上门),无装饰带动效,信息密度高,直接还原真实上门维修 App 的头部体验。应用还独创了"呼吸灯"动效——通过 aboutToAppear 中 setInterval 700ms 切换 breath 状态,热门服务标签、未完成订单状态、维修师质保标签、更多页消息通知都使用 opacity 呼吸灯动画,营造出"服务在线、订单活跃"的鲜活感。应用还独创了"会员权益卡"渐变设计——linearGradient 135 度从深橙 #D45A00 到橙 #FF7A00,配合积分进度条(10格7亮3暗)和六宫格权益,营造出会员卡般的精致质感。

下面逐页展示应用的七大功能模块运行效果。
三、预约页:表单填写与时段选择
预约页以"预约维修"为核心,采用"表单+时段选择"布局,是七个 Tab 中交互最丰富的页面。

页面顶部是标题"📅 预约维修 · 填写信息"。下方是预约表单卡,包含五个表单区域:
- 选择品类:使用 Scroll horizontal 横向滚动 8 个服务品类 chip(空调维修/洗衣机维修/冰箱维修/电视维修/热水器维修/油烟机维修/微波炉维修/洗碗机维修),每个 chip 橙色文字 + chip 浅橙底,用户可横向滑动选择。
- 问题描述:TextInput 输入框,占位符"如:空调不制冷,有异响…",dark 浅橙底圆角 10。
- 上门时间:Flex wrap 换行布局 6 个时段(今天14:00/今天16:00/明天10:00/明天14:00/后天10:00/后天14:00),第一个时段"今天14:00"选中态橙色底白字,其余 dark 浅橙底灰字,margin 3。
- 联系地址:TextInput 输入框,占位符"如:成华区万象城3栋1502",dark 浅橙底圆角 10。
- 提交预约:橙色按钮,layoutWeight 1 占满宽度,padding 上下 12,圆角 10,白字加粗,点击触发 addModal 预约维修弹窗。
表单卡下方是"📌 预约须知",包含三条须知:①维修师将在预约时段前30分钟电话确认;②配件费用按实际使用另计;③90天内同一故障免费上门复查。文字 text3 浅棕灰色 9 号,padding 左右 14。
预约表单布局清晰,从品类选择到问题描述到时段选择到地址填写,完整覆盖了上门维修预约的全部信息要素,时段选择用 Flex wrap 换行布局让用户一目了然,提交按钮直接触发预约弹窗,交互流畅。
四、订单页:进度时间轴与维修次数柱状图
订单页以"维修订单"为核心,独创"进度时间轴+近6月维修次数柱状图"的复合布局。

页面顶部是标题"📦 维修订单 · N单"(N 为 orderList.length 动态计算)。下方第一部分是维修订单列表,遍历 orderList,每个订单卡片包含:🔧图标 40×40(dark 浅橙底)+ 订单信息列(家电名 maxLines 1 加粗 + 问题:xxx + 订单号 text3 8 号)+ 状态标签列(状态标签通过 orderStatusColor 动态着色:待接单橙色/维修中蓝色/已完成绿色/其余红色,chip 浅橙底,未完成的订单状态标签带呼吸灯 opacity 动画)+ 底部行(📅日期时间 text3 + 🔧维修师 sub + ✏️修改按钮 + 🗑️取消按钮)。点击 ✏️ 触发 editModal 修改订单状态弹窗,点击 🗑️ 触发 delModal 取消订单弹窗。
6 条订单从 08-21 到 07-28 按时间倒序排列,状态涵盖待接单(1条,格力空调KFR-35,不制冷+异响,待分配)、维修中(1条,美的冰箱BCD-218,冷藏室结冰,张师傅,蓝色呼吸灯)、已完成(4条,海尔洗衣机/万和热水器/小米电视/方太油烟机),家电涵盖空调/冰箱/洗衣机/热水器/电视/油烟机,维修师从待分配到张师傅/李师傅/王师傅/赵师傅,完整还原了用户的维修订单历史。订单状态标签动态着色且未完成带呼吸灯,让用户一眼就能判断订单流转状态。
下方第二部分是"📊 近6月维修次数"柱状图卡片,遍历 REPAIR_CNT [0, 1, 2, 1, 3, 4] 对应 MONTHS [‘3月’,‘4月’,‘5月’,‘6月’,‘7月’,‘8月’],柱子高度 = value × 20(0 次显示"—"占位,柱子高度 3px),8 月(index 5)4 次最高用 orange 橙色突出显示,其余月份用 brown 棕红色,柱子顶部显示次数数字,底部显示月份。柱状图卡片高度 115,alignItems Bottom 让柱子从底部生长,直观呈现近半年的维修次数趋势。3 月 0 次、4 月 1 次,从 5 月开始维修次数逐步增加,8 月 4 次最高,符合"新用户逐步启用,夏季空调维修高峰"的真实使用轨迹。
五、维修师页:认证维修师头像卡
维修师页以"认证维修师"为核心,采用"头像卡"布局,按距离排序展示附近的认证维修师。

页面顶部是标题"🔧 认证维修师 · 按距离排序"。下方遍历 TECH_LIST 6 个维修师,每个维修师卡片包含:
上方信息行:维修师图标 56×56 圆形(dark 浅橙底)+ 维修师信息列(姓名加粗 13 号 + ⭐评分橙色 + ·订单数 text3 + 专长标签 sub 8 号)+ 距离呼叫列(📍距离 sub 9 号 + 呼叫>橙色加粗 8 号)。
下方标签行:认证(绿色 chip)、实名(蓝色 chip)、质保(橙色 chip,带呼吸灯 opacity 动画 0.6↔1),三个标签左对齐排列。
6 个维修师按距离从近到远排列:陈师傅(1.2km,4.9分,890单,洗碗机/消毒柜,最近)、李师傅(1.5km,4.8分,960单,洗衣机/冰箱全能)、张师傅(2.1km,4.9分,1280单,空调专家/15年经验,订单最多)、刘师傅(2.8km,4.6分,420单,微波炉/小家电)、王师傅(3.2km,4.7分,720单,热水器/油烟机)、赵师傅(4.0km,5.0分,580单,电视/影音设备,评分最高),评分从 4.6 到 5.0,订单数从 420 到 1280,距离从 1.2km 到 4.0km,完整还原了附近认证维修师的分布。每个维修师都有认证/实名/质保三个标签,质保标签带呼吸灯动效,让静态的维修师列表也有了"服务在线"的鲜活感。
六、评价页:星级墙与真实反馈
评价页以"用户评价"为核心,独创"三栏统计+星级墙评价列表"的复合布局。

页面顶部是标题"⭐ 用户评价 · 真实反馈"。下方第一部分是三栏统计卡片,横向排列三个数据指标,用 1px 竖线分隔:4.8 综合评分(orange 橙色 22 号加粗)/ 5850 总评价(green 绿色 18 号加粗)/ 96% 好评率(blue 蓝色 18 号加粗),每个指标下方配文字标签(综合评分/总评价/好评率)text3 8 号。三栏统计一目了然地展示了平台的评价概况。
下方第二部分是评价列表,遍历 REVIEW_LIST 8 条评价,每条评价卡片包含:头像 40×40 圆形(dark 浅橙底)+ 用户信息列(用户名加粗 11 号 + 星级行:ForEach 1-5 渲染,n <= stars 显示⭐否则☆,9 号)+ 日期 text3 8 号(右对齐)+ 维修项目(orange 橙色 9 号,如"维修项目:空调维修")+ 评价内容(sub 灰棕色 10 号,width 100%)。
8 条评价从 08-18 到 07-20 按时间倒序排列,评分涵盖 5 星(6 条)和 4 星(2 条),维修项目涵盖空调维修/冰箱维修/洗衣机维修/热水器维修/电视维修/油烟机维修,评价内容真实具体,如"张师傅很专业,很快就找到问题修好了,价格透明无乱收费"、“李师傅上门准时,修完后还帮我检查了密封条,服务很周到”、“维修速度还行,但配件到货等了两天,整体满意”,既有好评也有中肯建议,还原了真实的用户评价生态。星级用 ForEach 1-5 动态渲染⭐/☆,比静态文字更直观。
七、会员页:渐变权益卡与六宫格
会员页以"维修会员"为核心,独创"渐变权益卡+六宫格权益+本年维修统计"的三层布局,是七个 Tab 中视觉最精致的页面。

页面顶部是标题"👑 维修会员 · 权益卡"。下方第一部分是金卡会员渐变卡,使用 linearGradient 135 度渐变(从 #D45A00 深橙到 #FF7A00 橙色),卡片包含:
- 上方信息行:👑图标 56×56 圆形(半透明白底 rgba(255,255,255,0.2))+ 会员信息列("金卡会员"白色加粗 14 号 + “维修8折 · 免上门费 · 终身质保”#FFE0B2 浅橙 9 号)。
- 中部积分进度条:ForEach 0-9 渲染 10 个小格子,layoutWeight 均分,高度 6,圆角 3,前 7 格亮 #FFE0B2 浅橙,后 3 格暗 rgba(255,224,178,0.3) 30% 透明度,直观呈现积分进度。
- 下方提示:“距白金会员还差 280 积分”#FFE0B2 浅橙 8 号。
渐变卡从深橙到橙的 135 度斜向渐变,营造出银行卡般的精致质感,白色会员信息在渐变背景上醒目突出,积分进度条用 10 格亮暗对比直观呈现升级进度。
下方第二部分是"🎁 会员权益"六宫格,使用 Flex wrap 换行布局,每个权益卡片宽度 30%,包含:图标 22 号 + 权益名称 sub 9 号,card 白底圆角 12,margin 4,居中对齐。六项权益:🔧维修8折 / 🚗免上门费 / 🛡️终身质保 / ⚡优先上门 / 📞专属客服 / 🎫月度优惠券。六宫格布局让会员权益一目了然。
下方第三部分是"📊 本年维修统计"三栏卡片,横向排列三个数据指标,用 1px 竖线分隔:11 总维修(orange 橙色 18 号加粗)/ ¥856 总花费(green 绿色 16 号加粗)/ ¥128 已省(blue 蓝色 16 号加粗),每个指标下方配文字标签(总维修/总花费/已省)text3 8 号。三栏统计展示了会员的年度维修消费概况,"已省"金额让会员价值可视化。
八、更多页:设置清单与版本信息
更多页以"更多"为核心,采用"设置开关清单"布局,是七个 Tab 中最简洁的页面。

页面顶部是标题"⚙️ 更多 · 设置与帮助"。下方是设置清单卡片,包含 7 个设置项,每项采用"图标 + 名称 + 状态/箭头"的横向布局,项与项之间用 Divider 分隔(line 浅橙灰):
- 📍 服务城市 — 右侧"成都"sub 灰棕
- 🎫 我的优惠券 — 右侧"3张"orange 橙色
- 📋 维修记录 — 右侧"11次"sub 灰棕
- 🏠 地址管理 — 右侧"2个"sub 灰棕
- 🔔 消息通知 — 右侧"开启"green 绿色加粗,带呼吸灯 opacity 动画 0.6↔1
- ❓ 常见问题 — 右侧">"text3 浅棕灰箭头
- 📞 客服热线 — 右侧"400-xxx-8888"sub 灰棕
每个设置项 padding 上下 12,左侧 4,图标 18 号,名称 11 号主色,layoutWeight(1) 让名称占据中间空间,右侧状态文字 9 号。设置清单卡片白色背景圆角 14,padding 左右 16。
页面底部是版本号"v4.0.0 · 极速修家电维修",text3 浅棕灰 9 号,居中对齐,margin 顶部 16 底部 8。
设置清单布局让设置项一目了然,状态文字用不同颜色区分(橙色优惠券/绿色已开启/灰棕普通设置/浅灰箭头),消息通知的呼吸灯动效让静态的设置页面也有了"服务在线"的鲜活感。
九、色彩主题系统:暖橙底与橙棕红的上门服务视觉语言
9.1 色彩接口定义
任何一款设计精良的应用,背后都有一套严谨的色彩规范。该应用首先通过一个 ColorPalette 接口,将整个应用所需的色彩抽象为一个结构化的契约:
interface ColorPalette {
bg: string; card: string; dark: string; darkL: string;
title: string; sub: string; text3: string;
orange: string; orangeD: string; red: string; brown: string;
green: string; blue: string; teal: string;
line: string; tabOn: string; mask: string; chip: string;
}
- bg:页面整体背景色 #FFF7ED,暖橙底,营造上门服务的温暖可信赖感。
- orange:主色调 #FF7A00,橙色,用于按钮、强调文字、激活 Tab、价格、提交按钮等高优先级视觉元素,是上门维修的标志性色彩。
- title:标题色 #4A2C14,深棕,用于主标题文字,与暖橙底形成高对比度。
- brown:辅助色 #6B3A1A,棕红,与橙色形成同色系深浅对比,用于柱状图非最高值,营造"温暖、专业"的氛围。
- mask:遮罩色 rgba(74,44,20,0.55),深棕半透明,用于弹窗遮罩。
- chip:标签底色 rgba(255,122,0,0.10),橙色 10% 透明度,用于品类标签、状态标签、优惠券标签背景。

9.2 色彩实例化
const COLORS: ColorPalette = {
bg: '#FFF7ED', card: '#FFFFFF', dark: '#FEEDD6', darkL: '#FDE2BB',
title: '#4A2C14', sub: '#8A6A4A', text3: '#BBA090',
orange: '#FF7A00', orangeD: '#D45A00', red: '#E74C3C', brown: '#6B3A1A',
green: '#26AE60', blue: '#1E88E5', teal: '#0EA5A5',
line: '#F2DAB8', tabOn: '#FF7A00',
mask: 'rgba(74,44,20,0.55)', chip: 'rgba(255,122,0,0.10)'
};
主色 #FF7A00 是一种温暖的橙色,既契合"上门维修"主题(温暖、可信赖、高效的标志性色彩),又具有活力、专业的气质。辅助色 #6B3A1A 棕红与橙色形成同色系深浅对比,呼应柱状图、温暖感的色彩。bg #FFF7ED 暖橙底与主色同暖色系,营造出上门服务的温暖可信赖氛围。值得注意的是,本应用的动态颜色函数返回的是多色系(橙/蓝/青/绿/紫/红/棕),用于不同业务维度的语义着色——服务品类按品类着色(空调蓝/洗衣机青/冰箱绿/电视紫/其余橙),订单状态按状态着色(待接单橙/维修中蓝/已完成绿/其余红),形成了"以色定品类、以色定状态"的高级渲染模式。会员权益卡使用 linearGradient 135 度从 #D45A00 深橙到 #FF7A00 橙色,营造出银行卡般的精致质感。特别值得一提的是,catColor 函数中电视品类调用独立的 purpleStr() 函数返回 #7B2CBF 紫色(因为 COLORS 接口中没有 purple 字段,需要独立函数返回),这种设计既保持了色彩接口的简洁,又满足了电视品类的紫色语义需求。这种将所有色彩集中管理的做法保证了全局一致性,支持主题切换,降低了维护成本。

十、类型定义:构建严谨的数据契约
应用通过一组带 constructor 的 @Observed 类,为每一类业务对象建立了明确的数据契约。
10.1 服务、订单、维修师元数据
@Observed export class Service {
id: number; icon: string; name: string; price: string; desc: string; hot: boolean;
constructor(...) { ... }
}
@Observed export class OrderInfo {
id: number; code: string; appliance: string; issue: string;
date: string; time: string; status: string; tech: string;
constructor(...) { ... }
}
@Observed export class Technician {
id: number; name: string; avatar: string; rating: string;
orders: number; dist: string; tags: string; icon: string;
constructor(...) { ... }
}
Service 描述服务的 ID、图标、名称、价格、描述、是否热门,用 hot 字段区分热门服务。OrderInfo 描述订单的 ID、订单号、家电名、问题、日期、时间、状态、维修师,用 status 字段体现订单流转状态,tech 字段体现分配的维修师。Technician 描述维修师的 ID、姓名、头像、评分、订单数、距离、专长标签、图标,用 dist 字段体现距离排序,rating 字段体现评分。
10.2 评价元数据与工具函数
@Observed export class Review {
id: number; user: string; avatar: string; stars: number;
appliance: string; content: string; date: string;
constructor(...) { ... }
}
function catColor(c: string): string { /* 按品类着色 */ }
function purpleStr(): string { return '#7B2CBF'; }
function orderStatusColor(s: string): string { /* 按状态着色 */ }
Review 描述评价的 ID、用户名、头像、星级、维修项目、评价内容、日期,用 stars 字段体现评分(1-5),appliance 字段体现维修项目。purpleStr() 独立函数返回 #7B2CBF 紫色,供 catColor 函数中电视品类调用,因为 COLORS 接口中没有 purple 字段。所有数据类都带有 constructor 构造函数,使用 new ClassName(...) 方式实例化,配合 @Observed 装饰器实现响应式更新。
十一、配置驱动:常量数据与工具函数
11.1 Tab 导航配置(两排 4+3)
const TAB_ROW1: TabMeta[] = [
{ icon: '🏠', label: '首页' },
{ icon: '📅', label: '预约' },
{ icon: '📦', label: '订单' },
{ icon: '🔧', label: '维修师' }
];
const TAB_ROW2: TabMeta[] = [
{ icon: '⭐', label: '评价' },
{ icon: '👑', label: '会员' },
{ icon: '⚙️', label: '更多' }
];
与之前项目"5/6 个 Tab 单排"不同,本应用有 7 个 Tab,采用独创的两排布局(4+3),第一排首页/预约/订单/维修师(tabIdx 0-3),第二排评价/会员/更多(tabIdx 4-6,即 i+4)。两排布局解决了 7 个 Tab 单排拥挤的问题,同时将高频功能(首页/预约/订单/维修师)放在第一排,低频功能(评价/会员/更多)放在第二排,符合用户使用习惯。Tab 不单独配置颜色,统一使用 tabOn #FF7A00 橙色作为激活色,非激活使用 text3 #BBA090 浅棕灰,保持底部导航的简洁统一。
11.2 月份维修数据与工具函数
const MONTHS: string[] = ['3月', '4月', '5月', '6月', '7月', '8月'];
const REPAIR_CNT: number[] = [0, 1, 2, 1, 3, 4];
function catColor(c: string): string {
if (c === '空调') { return COLORS.blue; }
if (c === '洗衣机') { return COLORS.teal; }
if (c === '冰箱') { return COLORS.green; }
if (c === '电视') { return purpleStr(); }
return COLORS.orange;
}
function purpleStr(): string { return '#7B2CBF'; }
function orderStatusColor(s: string): string {
if (s === '待接单') { return COLORS.orange; }
if (s === '维修中') { return COLORS.blue; }
if (s === '已完成') { return COLORS.green; }
return COLORS.red;
}
REPAIR_CNT 近 6 月维修次数 [0,1,2,1,3,4],3 月 0 次、4 月 1 次,从 5 月开始逐步增加,8 月 4 次最高,符合"新用户逐步启用,夏季空调维修高峰"的真实使用轨迹。三个工具函数实现了语义化着色:catColor 按服务品类分五档色彩(空调蓝/洗衣机青/冰箱绿/电视紫/其余橙,电视调用独立 purpleStr() 函数),orderStatusColor 按订单状态分四档色彩(待接单橙/维修中蓝/已完成绿/其余红),实现了"以色定品类、以色定状态"的高级渲染模式。
十二、数据模型:可观察的上门维修实体
const SERVICE_LIST: Service[] = [ new Service(1, '❄️', '空调维修', '¥99起', '不制冷/漏水/异响', true), ... ];
const ORDER_LIST: OrderInfo[] = [ new OrderInfo(1, 'WX20260820001', '格力空调 KFR-35', '不制冷+异响', '08-21', '14:00-16:00', '待接单', '待分配'), ... ];
const TECH_LIST: Technician[] = [ new Technician(1, '张师傅', '👨🔧', '4.9', 1280, '2.1km', '空调专家/15年经验', '🔧'), ... ];
const REVIEW_LIST: Review[] = [ new Review(1, '用户8856', '🧑', 5, '空调维修', '张师傅很专业,很快就找到问题修好了,价格透明无乱收费。', '08-18'), ... ];
四组常量数据承载了极速修的全部业务信息:SERVICE_LIST 8 个服务(覆盖空调/洗衣机/冰箱/电视/热水器/油烟机/微波炉/洗碗机八大品类,价格从 ¥79 起到 ¥129 起,3 个热门:空调/冰箱/热水器)、ORDER_LIST 6 条订单(状态涵盖待接单/维修中/已完成,家电涵盖六大品类)、TECH_LIST 6 个维修师(评分从 4.6 到 5.0,订单数从 420 到 1280,距离从 1.2km 到 4.0km)、REVIEW_LIST 8 条评价(5 星 6 条/4 星 2 条,覆盖六大维修品类)。所有数据使用 new ClassName(...) 构造函数实例化,配合 @Observed 装饰器实现响应式更新。orderList 使用 @State 装饰,支持运行时增删改(预约 unshift/修改状态改元素/取消 splice)。
十三、动态颜色与工具函数:数据驱动的视觉映射
应用通过三个工具函数将业务数据映射为视觉色彩,实现"数据即配色"。
function catColor(c: string): string { /* 按品类着色,电视调用purpleStr() */ }
function purpleStr(): string { return '#7B2CBF'; }
function orderStatusColor(s: string): string { /* 按状态着色 */ }
- catColor:按服务品类分五档色彩(空调蓝/洗衣机青/冰箱绿/电视紫/其余橙),“以色定品类”,用户一眼就能从颜色判断服务品类。特别的是,电视品类调用独立的 purpleStr() 函数返回 #7B2CBF 紫色,因为 COLORS 接口中没有 purple 字段,这种设计既保持了色彩接口的简洁,又满足了电视品类的紫色语义需求。
- orderStatusColor:按订单状态分四档色彩(待接单橙色/维修中蓝色/已完成绿色/其余红色),“以色定状态”,用户一眼就能从颜色判断订单流转状态。
值得注意的是,本应用的近 6 月维修次数柱状图也使用了动态着色逻辑——8 月(index 5)4 次最高用 orange 橙色突出显示,其余月份用 brown 棕红色,通过 i === 5 ? COLORS.orange : COLORS.brown 内联判断实现,无需额外函数。会员权益卡积分进度条也使用了动态着色逻辑——前 7 格亮 #FFE0B2 浅橙,后 3 格暗 rgba(255,224,178,0.3) 30% 透明度,通过 d < 7 ? '#FFE0B2' : 'rgba(255,224,178,0.3)' 内联判断实现。热门服务标签、未完成订单状态、维修师质保标签、更多页消息通知都使用 breath 驱动的 opacity 呼吸灯动画(this.breath ? 0.6 : 1 / 0.5 : 1),营造出"服务在线、订单活跃"的鲜活感。会员权益卡使用 linearGradient 135 度从 #D45A00 深橙到 #FF7A00 橙色,是本应用最精致的视觉元素。
十四、入口组件:应用的骨架与七Tab全差异化布局
14.1 组件声明与状态
@Entry
@Component
struct Page813 {
@State tabIdx: number = 0;
@State breath: boolean = false;
@State addModal: boolean = false;
@State editModal: boolean = false;
@State delModal: boolean = false;
@State editIdx: number = 0;
@State delIdx: number = 0;
@State addAppliance: string = '';
@State addIssue: string = '';
@State editStatus: string = '';
@State orderList: OrderInfo[] = ORDER_LIST;
private timer: number = -1;
}
13 个 @State 状态分五类:导航状态(tabIdx)、动效状态(breath)、弹窗控制状态(addModal/editModal/delModal + editIdx/delIdx)、弹窗输入状态(addAppliance/addIssue/editStatus)、数据状态(orderList)。private timer 用于呼吸灯 setInterval 管理。
14.2 生命周期与呼吸灯动效
aboutToAppear(): void { this.timer = setInterval(() => { this.breath = !this.breath; }, 700); }
aboutToDisappear(): void { if (this.timer > 0) { clearInterval(this.timer); } }
aboutToAppear 中启动 setInterval 700ms 切换 breath 状态,实现全局呼吸灯动效。aboutToDisappear 中 clearInterval 清理定时器,避免内存泄漏。这是本应用独创的动效实现方式,热门服务标签、未完成订单状态、维修师质保标签、更多页消息通知都使用 breath 驱动的 opacity 呼吸灯动画,营造出"服务在线、订单活跃"的鲜活感。
14.3 服务推荐头部(@Builder header)
顶部头部通过 @Builder header() 抽取,包含三层:顶部导航栏(🔧chip + “极速修 · 上门家电维修” + 📍成都定位)、服务承诺卡(⚡平均30分钟上门 + 认证维修师·价格透明·90天质保 + 预约按钮 orange)、优惠横条(Scroll horizontal 横滑:新客减¥20 orange chip / 会员8折 sub dark / 90天质保 / 24小时上门)。头部无装饰带动效,信息密度高,直接还原真实上门维修 App 的头部体验。
14.4 底部 Tab 导航(两排 4+3)与 Stack 弹窗
底部导航通过 @Builder tabBar() 构建,Column 两排布局:第一排 TAB_ROW1 4 个(首页/预约/订单/维修师,tabIdx 0-3),第二排 TAB_ROW2 3 个(评价/会员/更多,tabIdx 4-6 即 i+4),圆角 16 卡片背景,激活态 tabOn 橙色加粗,非激活 text3 浅棕灰。两排布局解决了 7 个 Tab 单排拥挤的问题。build() 使用 Stack 布局,主 Column 包含 header + Scroll 内容区(if-else 切换 7 个 @Builder 页面)+ tabBar,三个弹窗(addModal/editModal/delModal)使用 Stack 叠加在最上层,zIndex 999,每个弹窗包含 modalOverlay 遮罩(点击关闭)+ 居中卡片。
十五、关键技术特性对比
| 特性维度 | 实现方式 | 技术要点 | 设计价值 |
|---|---|---|---|
| 色彩管理 | ColorPalette 接口 + COLORS 常量 | 集中定义 20 种语义色,含 mask/chip 半透明色 | 保证视觉一致性,支持主题切换 |
| 数据建模 | @Observed 类 + constructor | 4 个数据类(Service/OrderInfo/Technician/Review),new 实例化 | 数据与 UI 双向联动,响应式更新 |
| 七Tab差异化 | 每页独立 @Builder 布局 | 首页品类网格+热门/预约表单+时段/订单时间轴+柱状图/维修师头像卡/评价星级墙/会员权益卡/更多清单 | 一页一型,还原真实维修App体验 |
| 底部两排导航 | TAB_ROW1(4) + TAB_ROW2(3) | 第一排高频功能,第二排低频功能,tabIdx=i+4 | 7个Tab不拥挤,符合使用习惯 |
| 服务推荐头部 | @Builder header 固定头部 | 当前定位+服务承诺卡+优惠横条Scroll horizontal,无装饰带动效 | 信息密度高,还原真实维修头部 |
| 呼吸灯动效 | setInterval + opacity | aboutToAppear 启动 700ms 切换,热门标签/未完成订单/质保标签/消息通知呼吸 | 营造服务在线、订单活跃的鲜活感 |
| 订单全生命周期CRUD | orderList unshift/改元素/splice | 预约维修unshift(TextInput问题描述)、修改状态改元素(TextInput状态)、取消订单splice(二次确认) | 完整订单管理CRUD闭环 |
| TextInput输入 | 弹窗内 TextInput | 预约维修问题描述、修改订单状态,预填当前值可编辑 | 真正可编辑的弹窗交互 |
| Stack弹窗叠加 | Stack + zIndex 999 | 三个弹窗Stack叠加,mask遮罩点击关闭 | 弹窗层级清晰,交互灵活 |
| 会员权益卡 | linearGradient 135度 | #D45A00→#FF7A00 斜向渐变,积分进度条10格7亮3暗,六宫格权益 | 银行卡般的精致质感 |
| 评价星级墙 | ForEach 1-5 渲染⭐/☆ | 三栏统计(4.8/5850/96%) + 评价列表,星级动态渲染 | 评分直观,评价真实 |
| 预约表单 | 品类横滑+时段Flex wrap | 8个品类Scroll horizontal + 6个时段Flex wrap(今天14:00选中) + 地址TextInput + 预约须知 | 预约信息完整,交互流畅 |
| 动态颜色 | catColor/orderStatusColor + purpleStr() | 按品类/状态分档着色,电视调用独立purpleStr()返回#7B2CBF | 以色定品类、以色定状态 |
| 状态管理 | @State + @Observed | 13个状态分五类,orderList @State 可修改 | 细粒度状态控制,自动触发渲染 |
| 组件化 | @Component + @Builder | 1个入口组件 + 7个页面@Builder + 3个弹窗@Builder + header/tabBar/modalOverlay | 高内聚低耦合,代码复用率高 |
| 列表渲染 | ForEach + 网格/列表/时间轴 | 服务四列网格、订单时间轴、维修师头像卡、评价列表、会员六宫格 | 数据驱动渲染,布局灵活 |
| 数据可视化 | 柱状图 + 进度条 + 三栏统计 | 近6月维修次数柱状图(0次显示—)、会员积分进度条10格、三栏统计多处 | 多维度呈现,直观易读 |
| 文本省略 | maxLines + textOverflow | 家电名maxLines1、订单问题maxLines1 | 长文本优雅截断,布局整齐 |
| 生命周期 | aboutToAppear/aboutToDisappear | setInterval启动/clearInterval清理,避免内存泄漏 | 健壮性强,无资源泄漏 |
| 布局系统 | Column/Row + Flex + Stack + Scroll | 弹性布局、Flex wrap、Stack叠加、Scroll横向/纵向滚动、两排导航 | 自适应布局,层次分明 |
完整代码
// ============================================================
// 813 极速修 · 上门家电维修(现代行业:上门服务 / 家电维修)
// 头部样式:服务推荐头部(服务品类横滑 + 优惠横条)无动画
// 布局风格:7 个 tab 每个布局完全不同
// 首页=品类网格+热门 / 预约=表单+时段 / 订单=进度时间轴
// 维修师=头像卡 / 评价=星级墙 / 会员=权益卡 / 更多=清单
// 底部 7 tab 两排(4+3)
// 主题:暖橙底 + 深橙 + 棕红
// ============================================================
interface ColorPalette {
bg: string;
card: string;
dark: string;
darkL: string;
title: string;
sub: string;
text3: string;
orange: string;
orangeD: string;
red: string;
brown: string;
green: string;
blue: string;
teal: string;
line: string;
tabOn: string;
mask: string;
chip: string;
}
const COLORS: ColorPalette = {
bg: '#FFF7ED',
card: '#FFFFFF',
dark: '#FEEDD6',
darkL: '#FDE2BB',
title: '#4A2C14',
sub: '#8A6A4A',
text3: '#BBA090',
orange: '#FF7A00',
orangeD: '#D45A00',
red: '#E74C3C',
brown: '#6B3A1A',
green: '#26AE60',
blue: '#1E88E5',
teal: '#0EA5A5',
line: '#F2DAB8',
tabOn: '#FF7A00',
mask: 'rgba(74,44,20,0.55)',
chip: 'rgba(255,122,0,0.10)'
};
interface TabMeta { icon: string; label: string; }
const TAB_ROW1: TabMeta[] = [
{ icon: '🏠', label: '首页' },
{ icon: '📅', label: '预约' },
{ icon: '📦', label: '订单' },
{ icon: '🔧', label: '维修师' }
];
const TAB_ROW2: TabMeta[] = [
{ icon: '⭐', label: '评价' },
{ icon: '👑', label: '会员' },
{ icon: '⚙️', label: '更多' }
];
const MONTHS: string[] = ['3月', '4月', '5月', '6月', '7月', '8月'];
const REPAIR_CNT: number[] = [0, 1, 2, 1, 3, 4];
function catColor(c: string): string {
if (c === '空调') { return COLORS.blue; }
if (c === '洗衣机') { return COLORS.teal; }
if (c === '冰箱') { return COLORS.green; }
if (c === '电视') { return purpleStr(); }
return COLORS.orange;
}
function purpleStr(): string { return '#7B2CBF'; }
function orderStatusColor(s: string): string {
if (s === '待接单') { return COLORS.orange; }
if (s === '维修中') { return COLORS.blue; }
if (s === '已完成') { return COLORS.green; }
return COLORS.red;
}
@Observed export class Service {
id: number; icon: string; name: string; price: string; desc: string; hot: boolean;
constructor(id: number, icon: string, name: string, price: string, desc: string, hot: boolean) {
this.id = id; this.icon = icon; this.name = name; this.price = price; this.desc = desc; this.hot = hot;
}
}
@Observed export class OrderInfo {
id: number; code: string; appliance: string; issue: string; date: string; time: string; status: string; tech: string;
constructor(id: number, code: string, appliance: string, issue: string, date: string, time: string, status: string, tech: string) {
this.id = id; this.code = code; this.appliance = appliance; this.issue = issue;
this.date = date; this.time = time; this.status = status; this.tech = tech;
}
}
@Observed export class Technician {
id: number; name: string; avatar: string; rating: string; orders: number; dist: string; tags: string; icon: string;
constructor(id: number, name: string, avatar: string, rating: string, orders: number, dist: string, tags: string, icon: string) {
this.id = id; this.name = name; this.avatar = avatar; this.rating = rating;
this.orders = orders; this.dist = dist; this.tags = tags; this.icon = icon;
}
}
@Observed export class Review {
id: number; user: string; avatar: string; stars: number; appliance: string; content: string; date: string;
constructor(id: number, user: string, avatar: string, stars: number, appliance: string, content: string, date: string) {
this.id = id; this.user = user; this.avatar = avatar; this.stars = stars;
this.appliance = appliance; this.content = content; this.date = date;
}
}
const SERVICE_LIST: Service[] = [
new Service(1, '❄️', '空调维修', '¥99起', '不制冷/漏水/异响', true),
new Service(2, '🧺', '洗衣机维修', '¥89起', '不转/漏水/报错', false),
new Service(3, '🧊', '冰箱维修', '¥119起', '不制冷/结冰/噪音', true),
new Service(4, '📺', '电视维修', '¥129起', '无信号/花屏/黑屏', false),
new Service(5, '🔥', '热水器维修', '¥99起', '不加热/漏水/跳闸', true),
new Service(6, '🍳', '油烟机维修', '¥89起', '吸力弱/噪音大/漏油', false),
new Service(7, '💨', '微波炉维修', '¥79起', '不加热/转盘不转', false),
new Service(8, '🤖', '洗碗机维修', '¥109起', '不进水/不清洗', false)
];
const ORDER_LIST: OrderInfo[] = [
new OrderInfo(1, 'WX20260820001', '格力空调 KFR-35', '不制冷+异响', '08-21', '14:00-16:00', '待接单', '待分配'),
new OrderInfo(2, 'WX20260818002', '美的冰箱 BCD-218', '冷藏室结冰', '08-18', '10:00-12:00', '维修中', '张师傅'),
new OrderInfo(3, 'WX20260815003', '海尔洗衣机 XQB80', '脱水异响', '08-15', '15:00-17:00', '已完成', '李师傅'),
new OrderInfo(4, 'WX20260810004', '万和热水器 JSQ24', '不加热', '08-10', '09:00-11:00', '已完成', '王师傅'),
new OrderInfo(5, 'WX20260805005', '小米电视 E55A', '花屏', '08-05', '14:00-16:00', '已完成', '赵师傅'),
new OrderInfo(6, 'WX20260728006', '方太油烟机 JQ29', '吸力弱', '07-28', '13:00-15:00', '已完成', '张师傅')
];
const TECH_LIST: Technician[] = [
new Technician(1, '张师傅', '👨🔧', '4.9', 1280, '2.1km', '空调专家/15年经验', '🔧'),
new Technician(2, '李师傅', '👨🏭', '4.8', 960, '1.5km', '洗衣机/冰箱全能', '🧰'),
new Technician(3, '王师傅', '🧑🔧', '4.7', 720, '3.2km', '热水器/油烟机', '⚡'),
new Technician(4, '赵师傅', '👨🔧', '5.0', 580, '4.0km', '电视/影音设备', '📺'),
new Technician(5, '刘师傅', '🧑🏭', '4.6', 420, '2.8km', '微波炉/小家电', '🔌'),
new Technician(6, '陈师傅', '👨🔧', '4.9', 890, '1.2km', '洗碗机/消毒柜', '🤖')
];
const REVIEW_LIST: Review[] = [
new Review(1, '用户8856', '🧑', 5, '空调维修', '张师傅很专业,很快就找到问题修好了,价格透明无乱收费。', '08-18'),
new Review(2, '用户3201', '👩', 5, '冰箱维修', '李师傅上门准时,修完后还帮我检查了密封条,服务很周到。', '08-15'),
new Review(3, '用户7799', '🧑', 4, '洗衣机维修', '维修速度还行,但配件到货等了两天,整体满意。', '08-12'),
new Review(4, '用户4521', '👨', 5, '热水器维修', '王师傅技术很好,修完后还教了我日常保养方法。', '08-10'),
new Review(5, '用户6688', '👩', 5, '电视维修', '赵师傅很专业,花屏问题一次解决,收费合理。', '08-05'),
new Review(6, '用户9933', '🧑', 4, '油烟机维修', '清洗后吸力明显改善,建议可以多备一些滤网配件。', '07-28'),
new Review(7, '用户1122', '👨', 5, '空调维修', '加氟利昂后制冷恢复,张师傅服务态度很好。', '07-25'),
new Review(8, '用户5566', '👩', 5, '洗碗机维修', '陈师傅上门很快,修好后还做了免费检测。', '07-20')
];
@Entry
@Component
struct Page813 {
@State tabIdx: number = 0;
@State breath: boolean = false;
@State addModal: boolean = false;
@State editModal: boolean = false;
@State delModal: boolean = false;
@State editIdx: number = 0;
@State delIdx: number = 0;
@State addAppliance: string = '';
@State addIssue: string = '';
@State editStatus: string = '';
@State orderList: OrderInfo[] = ORDER_LIST;
private timer: number = -1;
aboutToAppear(): void { this.timer = setInterval(() => { this.breath = !this.breath; }, 700); }
aboutToDisappear(): void { if (this.timer > 0) { clearInterval(this.timer); } }
@Builder
header() {
Column({ space: 12 }) {
Row({ space: 10 }) {
Text('🔧').fontSize(16).padding(6).borderRadius(12).backgroundColor(COLORS.chip)
Text('极速修 · 上门家电维修').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Column().layoutWeight(1)
Text('📍 成都').fontSize(9).fontColor(COLORS.sub)
}
.width('100%')
Column({ space: 10 }) {
Row({ space: 10 }) {
Text('⚡').fontSize(20).fontColor(COLORS.orange)
Column({ space: 2 }) {
Text('平均30分钟上门').fontSize(12).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Text('认证维修师 · 价格透明 · 90天质保').fontSize(9).fontColor(COLORS.sub)
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column() { Text('预约').fontSize(10).fontColor('#FFFFFF').fontWeight(FontWeight.Bold) }
.padding({ left: 16, right: 16, top: 8, bottom: 8 }).borderRadius(12).backgroundColor(COLORS.orange)
}
.width('100%')
Scroll() {
Row({ space: 8 }) {
ForEach(['新客减¥20', '会员8折', '90天质保', '24小时上门'], (t: string, i: number) => {
Text(t).fontSize(9).fontColor(i === 0 ? COLORS.orange : COLORS.sub)
.padding({ left: 8, right: 8, top: 4, bottom: 4 }).borderRadius(8)
.backgroundColor(i === 0 ? COLORS.chip : COLORS.dark)
}, (t: string) => t)
}
}
.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width('100%')
}
.width('100%').padding(14).borderRadius(16).backgroundColor(COLORS.card)
.borderWidth(1).borderColor(COLORS.line)
}
.width('100%').padding({ left: 14, right: 14, top: 12, bottom: 12 })
}
@Builder
homeTab() {
Column({ space: 12 }) {
Text('🔧 维修品类 · 选一选').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(SERVICE_LIST, (item: Service) => {
Column({ space: 6 }) {
Text(item.icon).fontSize(30)
.width('100%').height(60).borderRadius(10).backgroundColor(COLORS.dark).textAlign(TextAlign.Center)
Text(item.name).fontSize(11).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Text(item.price).fontSize(10).fontColor(COLORS.orange).fontWeight(FontWeight.Bold)
if (item.hot) {
Text('热门').fontSize(7).fontColor('#FFFFFF')
.padding({ left: 4, right: 4, top: 1, bottom: 1 }).borderRadius(4).backgroundColor(COLORS.red)
.opacity(this.breath ? 0.6 : 1).animation({ duration: 600, iterations: 1 })
}
}
.width('22%').padding(8).borderRadius(12).backgroundColor(COLORS.card).margin(4)
.alignItems(HorizontalAlign.Center)
.onClick(() => { this.addAppliance = item.name; this.addIssue = item.desc; this.addModal = true; })
}, (item: Service) => item.id.toString())
}
.width('100%')
Text('🔥 热门维修服务').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
ForEach(SERVICE_LIST, (item: Service) => {
if (item.hot) {
Row({ space: 12 }) {
Text(item.icon).fontSize(26)
.width(48).height(48).borderRadius(12).backgroundColor(COLORS.dark).textAlign(TextAlign.Center)
Column({ space: 4 }) {
Text(item.name).fontSize(12).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Text(item.desc).fontSize(9).fontColor(COLORS.sub)
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 2 }) {
Text(item.price).fontSize(13).fontColor(COLORS.orange).fontWeight(FontWeight.Bold)
Text('立即预约 >').fontSize(8).fontColor(COLORS.orange)
}
.alignItems(HorizontalAlign.End)
}
.width('100%').padding(12).borderRadius(14).backgroundColor(COLORS.card)
}
}, (item: Service) => 'hot' + item.id.toString())
}
.width('100%')
}
@Builder
bookTab() {
Column({ space: 10 }) {
Text('📅 预约维修 · 填写信息').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Column({ space: 10 }) {
Text('选择品类').fontSize(11).fontColor(COLORS.sub)
Scroll() {
Row({ space: 6 }) {
ForEach(SERVICE_LIST, (item: Service) => {
Text(item.icon + ' ' + item.name).fontSize(9).fontColor(COLORS.orange)
.padding({ left: 8, right: 8, top: 5, bottom: 5 }).borderRadius(8).backgroundColor(COLORS.chip)
}, (item: Service) => item.name)
}
}
.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).width('100%')
Text('问题描述').fontSize(11).fontColor(COLORS.sub)
TextInput({ placeholder: '如:空调不制冷,有异响...' })
.fontSize(12).padding(10).borderRadius(10).backgroundColor(COLORS.dark)
Text('上门时间').fontSize(11).fontColor(COLORS.sub)
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(['今天 14:00', '今天 16:00', '明天 10:00', '明天 14:00', '后天 10:00', '后天 14:00'], (t: string, i: number) => {
Text(t).fontSize(9).fontColor(i === 0 ? '#FFFFFF' : COLORS.sub)
.padding({ left: 10, right: 10, top: 6, bottom: 6 }).borderRadius(8)
.backgroundColor(i === 0 ? COLORS.orange : COLORS.dark).margin(3)
}, (t: string) => t)
}
.width('100%')
Text('联系地址').fontSize(11).fontColor(COLORS.sub)
TextInput({ placeholder: '如:成华区万象城3栋1502' })
.fontSize(12).padding(10).borderRadius(10).backgroundColor(COLORS.dark)
Row({ space: 10 }) {
Column() { Text('提交预约').fontSize(12).fontColor('#FFFFFF').fontWeight(FontWeight.Bold) }
.layoutWeight(1).padding({ top: 12, bottom: 12 }).borderRadius(10).backgroundColor(COLORS.orange)
.justifyContent(FlexAlign.Center)
.onClick(() => { this.addModal = true; })
}
.width('100%')
}
.width('100%').padding(14).borderRadius(14).backgroundColor(COLORS.card)
Text('📌 预约须知').fontSize(11).fontColor(COLORS.sub)
Text('1. 维修师将在预约时段前30分钟电话确认\n2. 配件费用按实际使用另计\n3. 90天内同一故障免费上门复查')
.fontSize(9).fontColor(COLORS.text3).width('100%').padding({ left: 14, right: 14 })
}
.width('100%')
}
@Builder
orderTab() {
Column({ space: 10 }) {
Text('📦 维修订单 · ' + this.orderList.length.toString() + '单').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
ForEach(this.orderList, (item: OrderInfo, i: number) => {
Column({ space: 8 }) {
Row({ space: 10 }) {
Text('🔧').fontSize(20).width(40).height(40).borderRadius(10).backgroundColor(COLORS.dark).textAlign(TextAlign.Center)
Column({ space: 3 }) {
Text(item.appliance).fontSize(11).fontColor(COLORS.title).fontWeight(FontWeight.Bold).maxLines(1)
Text('问题:' + item.issue).fontSize(9).fontColor(COLORS.sub)
Text(item.code).fontSize(8).fontColor(COLORS.text3)
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 2 }) {
Text(item.status).fontSize(8).fontColor(orderStatusColor(item.status))
.padding({ left: 6, right: 6, top: 2, bottom: 2 }).borderRadius(6).backgroundColor(COLORS.chip)
.opacity(item.status !== '已完成' && this.breath ? 0.5 : 1).animation({ duration: 600, iterations: 1 })
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
Row({ space: 8 }) {
Text('📅 ' + item.date + ' ' + item.time).fontSize(8).fontColor(COLORS.text3)
Column().layoutWeight(1)
Text('🔧 ' + item.tech).fontSize(8).fontColor(COLORS.sub)
Text('✏️').fontSize(11).margin({ left: 8 })
.onClick(() => { this.editIdx = i; this.editStatus = item.status; this.editModal = true; })
Text('🗑️').fontSize(11).margin({ left: 6 })
.onClick(() => { this.delIdx = i; this.delModal = true; })
}
.width('100%')
}
.width('100%').padding(12).borderRadius(14).backgroundColor(COLORS.card)
.alignItems(HorizontalAlign.Start)
}, (item: OrderInfo) => 'o' + item.id.toString())
Column({ space: 8 }) {
Text('📊 近6月维修次数').fontSize(12).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Row({ space: 6 }) {
ForEach(REPAIR_CNT, (v: number, i: number) => {
Column({ space: 4 }) {
Text(v === 0 ? '—' : v.toString()).fontSize(8).fontColor(COLORS.orange)
Column().width('100%').height(v === 0 ? 3 : v * 20).borderRadius(5)
.backgroundColor(i === 5 ? COLORS.orange : COLORS.brown)
Text(MONTHS[i]).fontSize(8).fontColor(COLORS.text3)
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}, (v: number, i: number) => 'rp' + i.toString())
}
.width('100%').padding(12).borderRadius(14).backgroundColor(COLORS.card)
.alignItems(VerticalAlign.Bottom).height(115)
}
.width('100%')
}
.width('100%')
}
@Builder
techTab() {
Column({ space: 10 }) {
Text('🔧 认证维修师 · 按距离排序').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
ForEach(TECH_LIST, (item: Technician) => {
Column({ space: 10 }) {
Row({ space: 12 }) {
Text(item.icon).fontSize(32)
.width(56).height(56).borderRadius(28).backgroundColor(COLORS.dark).textAlign(TextAlign.Center)
Column({ space: 4 }) {
Text(item.name).fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Row({ space: 6 }) {
Text('⭐ ' + item.rating).fontSize(9).fontColor(COLORS.orange)
Text('· ' + item.orders + '单').fontSize(9).fontColor(COLORS.text3)
}
Text(item.tags).fontSize(8).fontColor(COLORS.sub)
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Column({ space: 2 }) {
Text('📍 ' + item.dist).fontSize(9).fontColor(COLORS.sub)
Text('呼叫 >').fontSize(8).fontColor(COLORS.orange).fontWeight(FontWeight.Bold)
}
.alignItems(HorizontalAlign.End)
}
.width('100%')
Row({ space: 8 }) {
Text('认证').fontSize(8).fontColor(COLORS.green)
.padding({ left: 5, right: 5, top: 2, bottom: 2 }).borderRadius(5).backgroundColor(COLORS.chip)
Text('实名').fontSize(8).fontColor(COLORS.blue)
.padding({ left: 5, right: 5, top: 2, bottom: 2 }).borderRadius(5).backgroundColor(COLORS.chip)
Text('质保').fontSize(8).fontColor(COLORS.orange)
.padding({ left: 5, right: 5, top: 2, bottom: 2 }).borderRadius(5).backgroundColor(COLORS.chip)
.opacity(this.breath ? 0.6 : 1).animation({ duration: 600, iterations: 1 })
Column().layoutWeight(1)
}
.width('100%')
}
.width('100%').padding(14).borderRadius(14).backgroundColor(COLORS.card)
.alignItems(HorizontalAlign.Start)
}, (item: Technician) => 't' + item.id.toString())
}
.width('100%')
}
@Builder
reviewTab() {
Column({ space: 10 }) {
Text('⭐ 用户评价 · 真实反馈').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Row({ space: 0 }) {
Column({ space: 2 }) { Text('4.8').fontSize(22).fontColor(COLORS.orange).fontWeight(FontWeight.Bold); Text('综合评分').fontSize(8).fontColor(COLORS.text3) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column().width(1).height(32).backgroundColor(COLORS.line)
Column({ space: 2 }) { Text('5850').fontSize(18).fontColor(COLORS.green).fontWeight(FontWeight.Bold); Text('总评价').fontSize(8).fontColor(COLORS.text3) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column().width(1).height(32).backgroundColor(COLORS.line)
Column({ space: 2 }) { Text('96%').fontSize(18).fontColor(COLORS.blue).fontWeight(FontWeight.Bold); Text('好评率').fontSize(8).fontColor(COLORS.text3) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}
.width('100%').padding({ top: 12, bottom: 12 }).borderRadius(14).backgroundColor(COLORS.card)
ForEach(REVIEW_LIST, (item: Review) => {
Column({ space: 6 }) {
Row({ space: 10 }) {
Text(item.avatar).fontSize(24).width(40).height(40).borderRadius(20).backgroundColor(COLORS.dark).textAlign(TextAlign.Center)
Column({ space: 3 }) {
Text(item.user).fontSize(11).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Row({ space: 2 }) {
ForEach([1, 2, 3, 4, 5], (n: number) => {
Text(n <= item.stars ? '⭐' : '☆').fontSize(9)
}, (n: number) => 'st' + n.toString() + item.id.toString())
}
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text(item.date).fontSize(8).fontColor(COLORS.text3)
}
.width('100%')
Text('维修项目:' + item.appliance).fontSize(9).fontColor(COLORS.orange)
Text(item.content).fontSize(10).fontColor(COLORS.sub).width('100%')
}
.width('100%').padding(12).borderRadius(14).backgroundColor(COLORS.card)
.alignItems(HorizontalAlign.Start)
}, (item: Review) => 'rv' + item.id.toString())
}
.width('100%')
}
@Builder
vipTab() {
Column({ space: 12 }) {
Text('👑 维修会员 · 权益卡').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Column({ space: 12 }) {
Row({ space: 12 }) {
Text('👑').fontSize(32)
.width(56).height(56).borderRadius(28).backgroundColor('rgba(255,255,255,0.2)').textAlign(TextAlign.Center)
Column({ space: 4 }) {
Text('金卡会员').fontSize(14).fontColor('#FFFFFF').fontWeight(FontWeight.Bold)
Text('维修8折 · 免上门费 · 终身质保').fontSize(9).fontColor('#FFE0B2')
}
.alignItems(HorizontalAlign.Start).layoutWeight(1)
}
.width('100%')
Row({ space: 6 }) {
ForEach([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], (d: number) => {
Column().layoutWeight(1).height(6).borderRadius(3)
.backgroundColor(d < 7 ? '#FFE0B2' : 'rgba(255,224,178,0.3)')
}, (d: number) => 'm' + d.toString())
}
.width('100%')
Text('距白金会员还差 280 积分').fontSize(8).fontColor('#FFE0B2')
}
.width('100%').padding(16).borderRadius(16)
.linearGradient({ angle: 135, colors: [['#D45A00', 0.0], ['#FF7A00', 1.0]] })
Text('🎁 会员权益').fontSize(12).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Flex({ wrap: FlexWrap.Wrap }) {
ForEach([
{ icon: '🔧', name: '维修8折' },
{ icon: '🚗', name: '免上门费' },
{ icon: '🛡️', name: '终身质保' },
{ icon: '⚡', name: '优先上门' },
{ icon: '📞', name: '专属客服' },
{ icon: '🎫', name: '月度优惠券' }
], (item: Record<string, string>) => {
Column({ space: 6 }) {
Text(item.icon).fontSize(22)
Text(item.name).fontSize(9).fontColor(COLORS.sub)
}
.width('30%').padding(10).borderRadius(12).backgroundColor(COLORS.card).margin(4)
.alignItems(HorizontalAlign.Center)
}, (item: Record<string, string>) => item.name)
}
.width('100%')
Text('📊 本年维修统计').fontSize(12).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Row({ space: 0 }) {
Column({ space: 2 }) { Text('11').fontSize(18).fontColor(COLORS.orange).fontWeight(FontWeight.Bold); Text('总维修').fontSize(8).fontColor(COLORS.text3) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column().width(1).height(30).backgroundColor(COLORS.line)
Column({ space: 2 }) { Text('¥856').fontSize(16).fontColor(COLORS.green).fontWeight(FontWeight.Bold); Text('总花费').fontSize(8).fontColor(COLORS.text3) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column().width(1).height(30).backgroundColor(COLORS.line)
Column({ space: 2 }) { Text('¥128').fontSize(16).fontColor(COLORS.blue).fontWeight(FontWeight.Bold); Text('已省').fontSize(8).fontColor(COLORS.text3) }
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}
.width('100%').padding({ top: 12, bottom: 12 }).borderRadius(14).backgroundColor(COLORS.card)
}
.width('100%')
}
@Builder
moreTab() {
Column({ space: 10 }) {
Text('⚙️ 更多 · 设置与帮助').fontSize(13).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Column({ space: 0 }) {
Row({ space: 12 }) { Text('📍').fontSize(18); Text('服务城市').fontSize(11).fontColor(COLORS.title).layoutWeight(1); Text('成都').fontSize(9).fontColor(COLORS.sub) }
.width('100%').padding({ top: 12, bottom: 12, left: 4 })
Divider().color(COLORS.line)
Row({ space: 12 }) { Text('🎫').fontSize(18); Text('我的优惠券').fontSize(11).fontColor(COLORS.title).layoutWeight(1); Text('3张').fontSize(9).fontColor(COLORS.orange) }
.width('100%').padding({ top: 12, bottom: 12, left: 4 })
Divider().color(COLORS.line)
Row({ space: 12 }) { Text('📋').fontSize(18); Text('维修记录').fontSize(11).fontColor(COLORS.title).layoutWeight(1); Text('11次').fontSize(9).fontColor(COLORS.sub) }
.width('100%').padding({ top: 12, bottom: 12, left: 4 })
Divider().color(COLORS.line)
Row({ space: 12 }) { Text('🏠').fontSize(18); Text('地址管理').fontSize(11).fontColor(COLORS.title).layoutWeight(1); Text('2个').fontSize(9).fontColor(COLORS.sub) }
.width('100%').padding({ top: 12, bottom: 12, left: 4 })
Divider().color(COLORS.line)
Row({ space: 12 }) { Text('🔔').fontSize(18); Text('消息通知').fontSize(11).fontColor(COLORS.title).layoutWeight(1); Text('开启').fontSize(9).fontColor(COLORS.green).fontWeight(FontWeight.Bold).opacity(this.breath ? 0.6 : 1).animation({ duration: 600, iterations: 1 }) }
.width('100%').padding({ top: 12, bottom: 12, left: 4 })
Divider().color(COLORS.line)
Row({ space: 12 }) { Text('❓').fontSize(18); Text('常见问题').fontSize(11).fontColor(COLORS.title).layoutWeight(1); Text('>').fontColor(COLORS.text3) }
.width('100%').padding({ top: 12, bottom: 12, left: 4 })
Divider().color(COLORS.line)
Row({ space: 12 }) { Text('📞').fontSize(18); Text('客服热线').fontSize(11).fontColor(COLORS.title).layoutWeight(1); Text('400-xxx-8888').fontSize(9).fontColor(COLORS.sub) }
.width('100%').padding({ top: 12, bottom: 12, left: 4 })
}
.width('100%').padding({ left: 16, right: 16 }).borderRadius(14).backgroundColor(COLORS.card)
Text('v4.0.0 · 极速修家电维修').fontSize(9).fontColor(COLORS.text3)
.alignSelf(ItemAlign.Center).margin({ top: 16, bottom: 8 })
}
.width('100%')
}
@Builder
tabBar() {
Column({ space: 0 }) {
Row({ space: 0 }) {
ForEach(TAB_ROW1, (t: TabMeta, i: number) => {
Column({ space: 3 }) {
Text(t.icon).fontSize(18)
Text(t.label).fontSize(8)
.fontColor(this.tabIdx === i ? COLORS.tabOn : COLORS.text3)
.fontWeight(this.tabIdx === i ? FontWeight.Bold : FontWeight.Normal)
}
.layoutWeight(1).padding({ top: 6, bottom: 4 })
.onClick(() => { this.tabIdx = i; })
}, (t: TabMeta, i: number) => 'r1' + t.label + i.toString())
}
.width('100%')
Row({ space: 0 }) {
ForEach(TAB_ROW2, (t: TabMeta, i: number) => {
Column({ space: 3 }) {
Text(t.icon).fontSize(18)
Text(t.label).fontSize(8)
.fontColor(this.tabIdx === i + 4 ? COLORS.tabOn : COLORS.text3)
.fontWeight(this.tabIdx === i + 4 ? FontWeight.Bold : FontWeight.Normal)
}
.layoutWeight(1).padding({ top: 4, bottom: 6 })
.onClick(() => { this.tabIdx = i + 4; })
}, (t: TabMeta, i: number) => 'r2' + t.label + i.toString())
}
.width('100%')
}
.width('100%').backgroundColor(COLORS.card).borderRadius(16)
}
@Builder
modalOverlay(onClose: () => void) {
Column().width('100%').height('100%')
.backgroundColor(COLORS.mask)
.onClick(() => { onClose(); })
}
build() {
Stack({ alignContent: Alignment.Center }) {
Column() {
this.header()
Scroll() {
Column({ space: 10 }) {
if (this.tabIdx === 0) { this.homeTab() }
else if (this.tabIdx === 1) { this.bookTab() }
else if (this.tabIdx === 2) { this.orderTab() }
else if (this.tabIdx === 3) { this.techTab() }
else if (this.tabIdx === 4) { this.reviewTab() }
else if (this.tabIdx === 5) { this.vipTab() }
else { this.moreTab() }
}
.width('100%').padding({ left: 14, right: 14, top: 12, bottom: 16 })
}
.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top)
this.tabBar()
}
.width('100%').height('100%')
if (this.addModal) {
Stack({ alignContent: Alignment.Center }) {
this.modalOverlay(() => { this.addModal = false; })
Column({ space: 12 }) {
Text('🔧 预约维修').fontSize(15).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Text(this.addAppliance).fontSize(10).fontColor(COLORS.sub)
TextInput({ placeholder: '问题描述(如 不制冷/异响)', text: this.addIssue })
.onChange((v: string) => { this.addIssue = v; })
.fontSize(12).padding(10).borderRadius(10).backgroundColor(COLORS.dark)
Row({ space: 10 }) {
Column() { Text('取消').fontSize(12).fontColor(COLORS.sub) }
.layoutWeight(1).padding({ top: 10, bottom: 10 }).borderRadius(10).backgroundColor(COLORS.darkL)
.onClick(() => { this.addModal = false; })
Column() { Text('提交预约').fontSize(12).fontColor('#FFFFFF').fontWeight(FontWeight.Bold) }
.layoutWeight(1).padding({ top: 10, bottom: 10 }).borderRadius(10).backgroundColor(COLORS.orange)
.onClick(() => {
this.orderList.unshift(new OrderInfo(
Date.now(), 'WX' + Date.now(),
this.addAppliance.length > 0 ? this.addAppliance : '家电维修',
this.addIssue.length > 0 ? this.addIssue : '待描述',
'08-21', '14:00-16:00', '待接单', '待分配'));
this.addModal = false;
})
}
.width('100%')
}
.width('82%').constraintSize({ maxHeight: '80%' }).padding(16).borderRadius(16).backgroundColor(COLORS.card)
}
.width('100%').height('100%').zIndex(999)
}
if (this.editModal) {
Stack({ alignContent: Alignment.Center }) {
this.modalOverlay(() => { this.editModal = false; })
Column({ space: 12 }) {
Text('✏️ 修改订单状态').fontSize(15).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Text(this.orderList[this.editIdx].code).fontSize(10).fontColor(COLORS.sub)
TextInput({ placeholder: '新状态(如 维修中/已完成)', text: this.editStatus })
.onChange((v: string) => { this.editStatus = v; })
.fontSize(12).padding(10).borderRadius(10).backgroundColor(COLORS.dark)
Row({ space: 10 }) {
Column() { Text('取消').fontSize(12).fontColor(COLORS.sub) }
.layoutWeight(1).padding({ top: 10, bottom: 10 }).borderRadius(10).backgroundColor(COLORS.darkL)
.onClick(() => { this.editModal = false; })
Column() { Text('保存').fontSize(12).fontColor('#FFFFFF').fontWeight(FontWeight.Bold) }
.layoutWeight(1).padding({ top: 10, bottom: 10 }).borderRadius(10).backgroundColor(COLORS.orange)
.onClick(() => {
if (this.editStatus.length > 0) { this.orderList[this.editIdx].status = this.editStatus; }
this.editModal = false;
})
}
.width('100%')
}
.width('82%').constraintSize({ maxHeight: '80%' }).padding(16).borderRadius(16).backgroundColor(COLORS.card)
}
.width('100%').height('100%').zIndex(999)
}
if (this.delModal) {
Stack({ alignContent: Alignment.Center }) {
this.modalOverlay(() => { this.delModal = false; })
Column({ space: 14 }) {
Text('🗑️ 取消订单?').fontSize(15).fontColor(COLORS.title).fontWeight(FontWeight.Bold)
Text('「' + this.orderList[this.delIdx].appliance + '」订单将被取消。').fontSize(10).fontColor(COLORS.sub)
Row({ space: 10 }) {
Column() { Text('再想想').fontSize(12).fontColor(COLORS.sub) }
.layoutWeight(1).padding({ top: 10, bottom: 10 }).borderRadius(10).backgroundColor(COLORS.darkL)
.onClick(() => { this.delModal = false; })
Column() { Text('确认取消').fontSize(12).fontColor('#FFFFFF') }
.layoutWeight(1).padding({ top: 10, bottom: 10 }).borderRadius(10).backgroundColor(COLORS.red)
.onClick(() => { this.orderList.splice(this.delIdx, 1); this.delModal = false; })
}
.width('100%')
}
.width('78%').constraintSize({ maxHeight: '80%' }).padding(16).borderRadius(16).backgroundColor(COLORS.card)
}
.width('100%').height('100%').zIndex(999)
}
}
.width('100%').height('100%').backgroundColor(COLORS.bg)
}
}
十六、开发环境搭建
16.1 安装 DevEco Studio
DevEco Studio 是 HarmonyOS 应用开发的官方 IDE。下载安装包后双击运行,进入安装欢迎界面,点击「下一步」继续。

16.2 选择安装路径
选择目标安装目录,建议安装到非系统盘,确认所需空间后点击「下一步」。

16.3 配置安装选项
设置环境变量,勾选创建桌面快捷方式、将 bin 文件夹添加到 PATH、添加上下文菜单,但是需要重启一下电脑使环境变量生效。

16.4 选择项目模板
新建一个空白模板,在模板市场中选择 Empty Ability。

16.5 配置项目信息
设置 API 为 24 的模板项目,项目名称 kfschool,Compatible SDK 6.1.1(24),设备类型勾选 Phone,点击完成后初始化项目,自动下载相关依赖。

十七、总结
通过对这款极速修上门家电维修应用的深度拆解,我们可以看到,一个优秀的移动端应用远不止是功能的堆砌,它更是架构设计、数据建模、视觉系统、交互逻辑、防御性编程等多维度能力的综合体现。
在架构层面,该应用采用了"配置驱动 + 组件化 + 状态管理"的三层设计模式。配置层将所有可变的数据与视觉属性抽离为常量数组和元数据接口,使得业务逻辑与表现层彻底解耦;组件层将七个功能页面拆分为独立的 @Builder 构建器,每个页面采用完全不同的布局风格,实现"一页一型"的差异化设计;状态层利用 ArkUI 的 @State、@Observed 机制,实现了数据变化到 UI 刷新的自动响应,特别是订单数据的全生命周期CRUD(预约 unshift/修改状态改元素/取消 splice),形成了完整的订单管理闭环。
在数据层面,四个 @Observed 数据类(Service/OrderInfo/Technician/Review)以 constructor 构造函数方式实例化,完整描述了极速修的全部业务信息。三个工具函数(catColor 按品类着色、orderStatusColor 按状态着色、purpleStr 独立返回紫色)将业务数据转化为视觉色彩,实现了"数据即配色"的高级渲染模式,特别是 catColor 中电视品类调用独立 purpleStr() 函数的设计,既保持了色彩接口的简洁,又满足了电视品类的紫色语义需求,这是该应用最具特色的技术亮点之一。
在视觉层面,本应用独创了"服务推荐头部"设计——顶部固定显示当前定位(📍成都)+ 服务承诺卡(⚡平均30分钟上门 + 认证维修师·价格透明·90天质保 + 预约按钮)+ 优惠横条(Scroll horizontal 横滑),无装饰带动效,信息密度高,直接还原真实上门维修 App 的头部体验。应用还独创了"呼吸灯"动效——通过 aboutToAppear 中 setInterval 700ms 切换 breath 状态,热门服务标签、未完成订单状态、维修师质保标签、更多页消息通知都使用 opacity 呼吸灯动画,营造出"服务在线、订单活跃"的鲜活感。应用还独创了"会员权益卡"渐变设计——linearGradient 135 度从深橙到橙,配合积分进度条(10格7亮3暗)和六宫格权益,营造出银行卡般的精致质感。底部导航采用独创的两排布局(4+3),解决了 7 个 Tab 单排拥挤的问题。七个 Tab 每个布局完全不同——首页品类四列网格+热门服务、预约表单+时段选择、订单进度时间轴+维修次数柱状图、维修师头像卡、评价星级墙、会员权益卡、设置清单,最大程度还原了真实上门维修 App 的交互体验。统一的色彩系统是整个应用的视觉基石,橙色主色搭配棕红辅助色,配合暖橙底和多色语义色,营造出既温暖又专业的上门服务氛围。
在交互层面,应用实现了完整的订单全生命周期CRUD闭环——预约维修(TextInput 问题描述输入,orderList.unshift 新待接单订单)、修改订单状态(TextInput 状态输入,修改数组指定元素的 status 字段)、取消订单(二次确认,orderList.splice 删除指定元素),配合 @State 响应式刷新和 Stack 弹窗叠加(zIndex 999,mask 遮罩点击关闭),交互流畅且一致。预约表单的品类横滑+时段 Flex wrap+地址输入、评价星级墙的 ForEach 1-5 动态渲染⭐/☆、会员权益卡的积分进度条、订单状态的呼吸灯动效等细节,都体现了对上门维修用户体验的深入考量。

标签: HarmonyOS · ArkTS · 华为 · 上门维修 · 移动端开发
更多推荐



所有评论(0)