医疗绿与健康蓝的生命守护色——HarmonyOS ArkUI 医疗健康平台六场景服务组件化设计全解
一、技术前言
在数字化医疗加速普及的今天,用户对健康管理的线上化需求已从"有就行"升级为"好才用"。一个优秀的医疗健康应用,不仅要提供挂号、问诊、报告查询等基础功能,更要通过视觉语言传递专业感与信任感,通过交互设计降低患者在就医过程中的焦虑情绪。鸿蒙 HarmonyOS 的 ArkUI 框架,恰好为这一需求提供了强有力的技术底座。
ArkUI 是 HarmonyOS 的声明式 UI 开发框架,基于 TypeScript 扩展语法 ArkTS 构建。其核心设计理念包括:声明式编程范式——开发者只需描述界面"是什么"而非"怎么做",框架自动处理状态到视图的映射;状态驱动渲染——通过 @State、@Prop、@Link 等装饰器实现数据变化到 UI 的自动更新,无需手动操作 DOM;组件化开发——通过 @Component、@Builder 装饰器将界面拆分为可复用的独立单元,每个组件拥有自己的状态和生命周期;跨设备适配——同一套代码可运行于手机、平板、智能穿戴等多种设备,为医疗场景的多终端覆盖提供基础。
在本篇博文中,我们将深入剖析一个名为「康医通」的医疗健康应用前端源码。该应用覆盖首页服务入口、预约管理、医生推荐、检查报告、药品商城、个人健康档案六大场景,采用浅色主题搭配医疗绿(#10B981)与健康蓝(#3B82F6)双主色,通过六种完全不同的布局策略实现场景差异化,同时利用呼吸动画为健康数据注入"生命感"。整个源码严格遵循 ArkUI 组件化开发规范,无外部依赖,全部颜色统一管理,是一份完整的可直接编译运行的鸿蒙原生应用页面。






二、整体架构流程图
下面的 Mermaid 流程图展示了当前页面的整体架构层次,从颜色系统到数据模型,从组件状态到视图渲染,形成一条清晰的"数据→状态→视图"单向流动链路。
从架构图中可以清晰看到,整个页面分为六个层次:颜色系统层是所有视觉元素的色彩来源;常量与配置层定义了 Tab 元数据和图表数据,以及预计算的宫格行索引;工具函数层封装了状态到颜色的映射逻辑;数据模型层包含六个 @Observed 装饰的数据类,是整个页面的数据基础;组件状态层管理当前选中 Tab、呼吸动画值和三个弹窗布尔值;视图渲染层通过 13 个 @Builder 函数构建出头部、六个 Tab 内容、图表卡片、底部 Tab 栏和弹窗系统。数据自上而下流动,状态变化自动触发视图更新。
三、色彩体系设计
色彩是医疗应用传递专业感和信任感的第一媒介。该源码定义了一个包含 17 个色值的 COLORS 常量对象,构建了一套完整的医疗色彩语义系统。
const COLORS = {
bg: '#F0FDF8', card: '#FFFFFF', chip: '#D1FAE5',
title: '#064E3B', sub: '#047857', text3: '#6EE7B7',
green: '#10B981', greenD: '#059669',
blue: '#3B82F6', blueD: '#2563EB',
orange: '#F59E0B', red: '#EF4444',
line: '#D1FAE5', tabOn: '#10B981', mask: 'rgba(0,0,0,0.5)',
busyBg: '#FEF3C7'
}
让我们逐组分析这些色彩的语义含义:
背景与容器色:bg 为 #F0FDF8,一种极淡的薄荷绿底色,比纯白更柔和,减轻长时间浏览的视觉疲劳;card 为纯白 #FFFFFF,用于卡片容器,与背景形成微妙的层次差;chip 为 #D1FAE5 浅绿芯片色,用于标签背景和头像容器。
文字层次色:title 为 #064E3B 深绿黑色,作为主标题颜色,比纯黑更温暖且与主题色和谐;sub 为 #047857 中绿色,用于副标题和说明文字;text3 为 #6EE7B7 浅绿色,用于最弱层级的辅助文字和未选中状态。
主题双主色:green 为 #10B981 医疗绿,是整个应用的主色调,用于 Tab 选中态、主操作按钮和积极状态标识;greenD 为 #059669 深医疗绿,用于强调的数字和按钮;blue 为 #3B82F6 健康蓝,作为辅助主色,用于确认类操作和血压数据;blueD 为 #2563EB 深健康蓝。
功能状态色:orange 为 #F59E0B 橙色,用于警告和待处理状态;red 为 #EF4444 红色,用于异常和危险操作;busyBg 为 #FEF3C7 淡黄背景,专门用于医生"忙碌"状态标签的背景色。
辅助色:line 为 #D1FAE5 分隔线色,与芯片色相同保持一致性;tabOn 为 #10B981 与 green 相同,用于 Tab 选中态;mask 为半透明黑色,用于弹窗遮罩层。
这种色彩设计的精妙之处在于:所有颜色都在绿色和蓝色的色相范围内变化,通过明度和饱和度的调节构建层次,而非引入多种色相,使得整个界面呈现出统一的"医疗"视觉气质。
四、Tab 元数据与辅助数据
4.1 Tab 元数据定义
该源码使用一个 TabMeta 类来定义每个 Tab 的元数据,包含图标和标签两个字段。
class TabMeta {
icon: string
label: string
constructor(icon: string, label: string) {
this.icon = icon
this.label = label
}
}
const TAB_LIST: TabMeta[] = [
new TabMeta('🏠', '首页'), new TabMeta('📅', '预约'),
new TabMeta('👨⚕️', '医生'), new TabMeta('📋', '报告'),
new TabMeta('💊', '药房'), new TabMeta('👤', '我的')
]
这里有一个值得注意的设计决策:Tab 图标使用 emoji 而非图片资源。这样做的好处有三:一是零资源依赖,无需引入任何图标文件,降低了工程复杂度;二是 emoji 天然支持彩色显示,比单色矢量图标更生动;三是 emoji 是跨平台标准,在不同设备上显示一致。六个 Tab 分别对应首页、预约、医生、报告、药房、我的,覆盖了医疗健康应用的核心功能闭环。
4.2 图表数据
const MONTH: string[] = ['4月', '5月', '6月', '7月', '8月', '9月']
const HEALTH_VAL: number[] = [3, 5, 4, 7, 6, 8]
就诊趋势图表使用近 6 个月的就诊次数数据,数据值从 3 到 8 不等,呈现出波动上升的趋势,符合真实医疗场景中季节性就医规律。
4.3 预计算宫格行索引
const SERVICE_ROW0: number[] = [0, 1]
const SERVICE_ROW1: number[] = [2, 3]
const SERVICE_ROW2: number[] = [4, 5]
const SERVICE_ROW3: number[] = [6, 7]
这是一个关键的编译约束优化。源码需要渲染 4 行 2 列共 8 个服务项的宫格,按照常规做法会使用 .filter() 方法按行拆分数据。但 ArkTS 对 .filter() 有严格的类型推断限制,容易导致编译错误。因此,源码预先计算了每行包含的数据索引,使用四个常量数组 SERVICE_ROW0~3 直接存储索引值,在 ForEach 中直接遍历这些预计算数组,完全避免了运行时 filter 调用。这种"空间换安全"的策略在 ArkUI 开发中是一种常见的最佳实践。
五、工具函数
5.1 预约状态颜色映射
function apptColor(s: string): string {
if (s === '待确认') return COLORS.orange
if (s === '已确认') return COLORS.blue
if (s === '进行中') return COLORS.green
if (s === '已完成') return COLORS.text3
if (s === '已取消') return COLORS.red
return COLORS.text3
}
apptColor 函数接收预约状态字符串,返回对应的语义颜色。五种状态的色彩映射逻辑如下:
- 待确认 → 橙色:表示需要用户关注但尚未紧急的状态
- 已确认 → 蓝色:表示确认完成,进入正式流程
- 进行中 → 绿色:表示当前正在进行的积极状态
- 已完成 → 浅绿灰(text3):表示已结束的弱化状态
- 已取消 → 红色:表示异常终止的警示状态
这种状态-颜色映射模式在医疗应用中尤为重要:患者可以一眼识别哪些预约需要行动(橙色),哪些正在进行(绿色),哪些已结束(灰色),极大降低了信息获取的认知成本。
5.2 报告状态颜色映射
function reportColor(s: string): string {
if (s === '正常') return COLORS.green
if (s === '异常') return COLORS.red
if (s === '待出') return COLORS.orange
return COLORS.text3
}
reportColor 函数为检查报告的三种状态分配颜色:正常为绿色(安心)、异常为红色(警示)、待出为橙色(等待)。这种色彩编码在医疗报告中几乎是行业惯例——当患者打开报告列表时,红色报告会立即吸引视线,绿色报告则传达安全感,符合医疗场景中"异常优先关注"的信息层级原则。
六、数据模型层
该源码定义了六个 @Observed export class 数据模型,每个模型对应一个功能场景。
6.1 MedService — 医疗服务
@Observed export class MedService {
icon: string
name: string
desc: string
constructor(icon: string, name: string, desc: string) {
this.icon = icon; this.name = name; this.desc = desc
}
}
MedService 模型包含三个字段:icon(emoji 图标)、name(服务名称)、desc(服务描述)。对应的 SERVICE_LIST 包含 8 条数据,覆盖预约挂号、在线问诊、报告查询、健康档案、药品配送、体检预约、疫苗接种、健康咨询八个核心服务入口。@Observed 装饰器使该类实例可被 ArkUI 框架观察,当属性变化时自动触发关联视图更新。
6.2 Appointment — 预约记录
@Observed export class Appointment {
hospital: string
dept: string
doctor: string
status: string
time: string
fee: number
constructor(hospital: string, dept: string, doctor: string,
status: string, time: string, fee: number) {
this.hospital = hospital; this.dept = dept; this.doctor = doctor
this.status = status; this.time = time; this.fee = fee
}
}
Appointment 模型有六个字段,包含医院、科室、医生、状态、时间和费用。APPT_LIST 包含 6 条数据,涵盖北京协和医院、北大人民医院等知名医院,状态覆盖待确认、已确认、进行中、已完成、已取消五种完整生命周期。费用字段为 number 类型,支持在 UI 中直接进行数值运算和格式化。
6.3 Doctor — 医生信息
@Observed export class Doctor {
name: string
avatar: string
dept: string
title: string
hospital: string
rating: number
fee: number
status: string
constructor(name: string, avatar: string, dept: string, title: string,
hospital: string, rating: number, fee: number, status: string) {
this.name = name; this.avatar = avatar; this.dept = dept; this.title = title
this.hospital = hospital; this.rating = rating; this.fee = fee; this.status = status
}
}
Doctor 模型字段最多,达八个:姓名、头像(emoji)、科室、职称、医院、评分、接诊费、在线状态。DOCTOR_LIST 包含 6 位医生数据,职称覆盖主任医师、副主任医师、主治医师三级,状态包含在线、忙碌、离线三种,评分从 4.5 到 4.9,为医生推荐 Tab 提供了丰富的展示维度。
6.4 Report — 检查报告
@Observed export class Report {
name: string
hospital: string
date: string
status: string
type: string
constructor(name: string, hospital: string, date: string,
status: string, type: string) {
this.name = name; this.hospital = hospital; this.date = date
this.status = status; this.type = type
}
}
Report 模型有五个字段,REPORT_LIST 包含血常规、胸部 CT、肝功能、心电图、核磁共振、尿常规六种检查报告,状态覆盖正常、异常、待出三种。type 字段(血检/影像/功能/尿检)为未来按类型筛选预留了扩展空间。
6.5 Medicine — 药品信息
@Observed export class Medicine {
name: string
spec: string
price: number
icon: string
tag: string
constructor(name: string, spec: string, price: number, icon: string, tag: string) {
this.name = name; this.spec = spec; this.price = price
this.icon = icon; this.tag = tag
}
}
Medicine 模型包含药名、规格、价格、图标和标签五个字段。MED_LIST 包含 6 种常见药品,标签涵盖热销、处方药、常备、保健、外用、消化六种分类,价格从 15.6 到 45.0 元不等,为药房双列卡片提供了多样化的展示内容。
6.6 UserHealth — 用户健康
@Observed export class UserHealth {
name: string
age: number
height: number
weight: number
points: number
level: string
constructor(name: string, age: number, height: number, weight: number,
points: number, level: string) {
this.name = name; this.age = age; this.height = height
this.weight = weight; this.points = points; this.level = level
}
}
UserHealth 模型记录用户的姓名、年龄、身高、体重、健康积分和等级。USER_LIST 包含 3 条数据,等级分为健康达人、健康新星、健康新秀三级,积分从 3200 到 8650。组件初始状态选择第一个用户作为当前登录用户,通过 @State user: UserHealth = USER_LIST[0] 实现。
七、组件主体结构
7.1 状态变量声明
@Entry
@Component
struct Page812 {
@State currentTab: number = 0
@State breath: number = 0
@State addModal: boolean = false
@State editModal: boolean = false
@State delModal: boolean = false
@State user: UserHealth = USER_LIST[0]
组件定义了六个 @State 变量:currentTab 控制当前显示的 Tab 页面(0-5);breath 是呼吸动画的计数值,每秒递增;addModal、editModal、delModal 三个布尔值控制三种弹窗的显示隐藏;user 引用当前用户数据实例。@State 装饰器确保这些变量变化时,框架自动重新渲染关联的 UI 部分。
7.2 生命周期与呼吸动画
aboutToAppear() {
setInterval(() => {
this.breath = (this.breath + 1) % 100
}, 1000)
}
aboutToAppear 是 ArkUI 组件的生命周期回调,在组件即将出现时执行。这里通过 setInterval 每秒将 breath 值加 1 并取模 100,形成一个 0-99 的循环递增序列。这个呼吸值在多处被消费:步数数字跳动(8642 + this.breath * 3)、心率波动(72 + (this.breath % 6))、图表柱子高度波动(val * 12 + (this.breath % 5) * 0.5)、积分数字跳动(this.user.points + this.breath * 2)。一处状态驱动多处动画,是 ArkUI 状态驱动渲染理念的典型体现。
7.3 build 方法
build() {
Stack({ alignContent: Alignment.Center }) {
Column() {
this.headerMedical()
Scroll() {
Column() {
if (this.currentTab === 0) this.tabHome()
else if (this.currentTab === 1) this.tabAppointment()
else if (this.currentTab === 2) this.tabDoctor()
else if (this.currentTab === 3) this.tabReport()
else if (this.currentTab === 4) this.tabPharmacy()
else if (this.currentTab === 5) this.tabMine()
}
.width('100%')
.padding({ bottom: 100 })
}
.layoutWeight(1)
.scrollBar(BarState.Off)
this.tabBar()
}
.width('100%')
.height('100%')
.backgroundColor(COLORS.bg)
if (this.addModal || this.editModal || this.delModal) this.modalOverlay()
if (this.addModal) this.panelAdd()
if (this.editModal) this.panelEdit()
if (this.delModal) this.panelDel()
}
.width('100%')
.height('100%')
}
build 方法的结构非常清晰:最外层是一个 Stack 容器,设置 alignContent: Alignment.Center 使弹窗居中显示。Stack 内部分为两部分:一是主内容 Column,包含头部、可滚动的内容区域和底部 Tab 栏;二是条件渲染的弹窗层,当任一弹窗状态为 true 时显示遮罩层和对应的弹窗面板。
内容区域使用 Scroll 组件包裹,设置 .scrollBar(BarState.Off) 隐藏滚动条,.layoutWeight(1) 让滚动区域占据头部和 Tab 栏之间的所有可用空间。Tab 切换通过 if-else 链实现,当 currentTab 变化时,ArkUI 自动卸载旧 Tab 内容并加载新 Tab 内容。底部 padding({ bottom: 100 }) 确保内容不被 Tab 栏遮挡。
八、头部区域详解
头部区域由 headerMedical Builder 构建,包含标题栏、搜索栏和健康数据卡片三个部分。
8.1 标题栏
Row() {
Column() {
Text('康医通').fontSize(22).fontWeight(FontWeight.Bold).fontColor(COLORS.title)
Text('您的专属健康管家').fontSize(12).fontColor(COLORS.sub).margin({ top: 2 })
}.alignItems(HorizontalAlign.Start)
Blank()
Text('🔔').fontSize(22)
}
.width('100%').padding({ left: 16, right: 16, top: 12, bottom: 8 })
标题栏使用 Row 横向布局,左侧是 Column 包裹的应用名和副标题,右侧通过 Blank() 占位推至最右,放置一个通知铃铛 emoji。应用名使用 22px 粗体加粗深绿黑色,副标题使用 12px 中绿色,形成清晰的标题-副标题层次。
8.2 搜索栏
Row() {
Text('🔍').fontSize(16).margin({ right: 8 })
Text('搜索医院、医生、药品...').fontSize(14).fontColor(COLORS.text3)
}
.width('92%').height(40).padding({ left: 14, right: 14 })
.backgroundColor(COLORS.card).borderRadius(20).margin({ bottom: 12 })
搜索栏是一个圆角矩形(borderRadius(20)),白色背景与页面底色形成对比。内部放置搜索图标和占位文字,占位文字使用最弱层级颜色 text3,提示用户可输入的内容范围(医院、医生、药品)。宽度设为 92%,左右各留 4% 间距,视觉上舒适不拥挤。
8.3 健康数据卡片
这是头部区域最精彩的部分——三列健康数据实时卡片。
Row() {
Column() {
Text('👟').fontSize(24)
Text((8642 + this.breath * 3).toString()).fontSize(20)
.fontWeight(FontWeight.Bold).fontColor(COLORS.greenD).margin({ top: 4 })
Text('步数').fontSize(11).fontColor(COLORS.sub).margin({ top: 2 })
}.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() {
Text('❤️').fontSize(24)
Text((72 + (this.breath % 6)).toString()).fontSize(20)
.fontWeight(FontWeight.Bold).fontColor(COLORS.red).margin({ top: 4 })
Text('心率').fontSize(11).fontColor(COLORS.sub).margin({ top: 2 })
}.layoutWeight(1).alignItems(HorizontalAlign.Center)
Column() {
Text('🩺').fontSize(24)
Text('120/80').fontSize(20).fontWeight(FontWeight.Bold)
.fontColor(COLORS.blue).margin({ top: 4 })
Text('血压').fontSize(11).fontColor(COLORS.sub).margin({ top: 2 })
}.layoutWeight(1).alignItems(HorizontalAlign.Center)
}
.width('92%').padding({ top: 14, bottom: 14 })
.backgroundColor(COLORS.card).borderRadius(16).margin({ bottom: 12 })
三列分别展示步数、心率和血压数据。步数通过 8642 + this.breath * 3 实现每秒递增 3 步的"行走中"效果,颜色为深医疗绿;心率通过 72 + (this.breath % 6) 实现 72-77 之间的周期波动,模拟心跳节律,颜色为红色;血压显示固定值 120/80,颜色为健康蓝。三列均使用 layoutWeight(1) 等分宽度,alignItems(HorizontalAlign.Center) 居中对齐。这张卡片让用户一打开应用就能看到自己的核心健康指标,且步数和心率的实时跳动赋予了数据"生命感"。
九、首页 Tab — 宫格服务入口
首页 Tab 由 tabHome Builder 构建,包含快捷服务宫格和就诊趋势图表。
9.1 服务宫格
Column() {
Row() { ForEach(SERVICE_ROW0, (i: number) => { this.serviceItem(i) }, ...) }.width('100%')
Divider().color(COLORS.line).width(1)
Row() { ForEach(SERVICE_ROW1, (i: number) => { this.serviceItem(i) }, ...) }.width('100%')
Divider().color(COLORS.line).width(1)
Row() { ForEach(SERVICE_ROW2, (i: number) => { this.serviceItem(i) }, ...) }.width('100%')
Divider().color(COLORS.line).width(1)
Row() { ForEach(SERVICE_ROW3, (i: number) => { this.serviceItem(i) }, ...) }.width('100%')
}
.width('92%').backgroundColor(COLORS.card).borderRadius(16).margin({ bottom: 16 })
宫格采用 4 行 2 列结构,每行通过 ForEach 遍历预计算的索引数组。行间使用 Divider 分隔,颜色为浅绿色 COLORS.line,宽度为 1,既区分了行又保持了视觉连贯性。整个宫格包裹在一个白色圆角卡片中,与页面底色形成层次。
每个服务项由 serviceItem Builder 渲染:
@Builder
serviceItem(idx: number) {
Column() {
Text(SERVICE_LIST[idx].icon).fontSize(28)
Text(SERVICE_LIST[idx].name).fontSize(13).fontColor(COLORS.title).margin({ top: 6 })
Text(SERVICE_LIST[idx].desc).fontSize(10).fontColor(COLORS.sub).margin({ top: 2 })
.textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1)
}
.layoutWeight(1).padding({ top: 14, bottom: 14 }).alignItems(HorizontalAlign.Center)
}
每个服务项是垂直排列的图标(28px emoji)、名称(13px 深色)和描述(10px 中色)。描述文字设置了 textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1),确保长文本单行显示并省略号截断,防止文本溢出破坏布局。layoutWeight(1) 让每项在行内等分宽度。
9.2 就诊趋势图表
首页底部直接调用了 this.chartCard(),在服务宫格下方展示近 6 个月的就诊次数柱状图。图表的详细实现将在第十五章中分析。
十、预约 Tab — 状态时间轴
预约 Tab 由 tabAppointment Builder 构建,采用左侧竖线时间轴布局,是六个 Tab 中布局最复杂的之一。
ForEach(APPT_LIST, (item: Appointment, idx: number) => {
Row() {
// 左侧时间轴竖线 + 圆点
Column() {
Circle({ width: 12, height: 12 }).fill(apptColor(item.status))
.stroke(COLORS.card).strokeWidth(2)
if (idx < APPT_LIST.length - 1) {
Rect().width(2).height(70).fill(COLORS.line).margin({ top: 4 })
}
}
.width(20).alignItems(HorizontalAlign.Center).margin({ top: 4 })
// 预约信息卡片
Column() {
Row() {
Column() {
Text(item.hospital).fontSize(15).fontWeight(FontWeight.Medium).fontColor(COLORS.title)
Text(item.dept + ' · ' + item.doctor).fontSize(12).fontColor(COLORS.sub).margin({ top: 3 })
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text(item.status).fontSize(12).fontColor(COLORS.card)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.backgroundColor(apptColor(item.status)).borderRadius(12)
}.width('100%')
Row() {
Text('🕐 ' + item.time).fontSize(12).fontColor(COLORS.sub)
Blank()
Text('¥' + item.fee.toString()).fontSize(14).fontWeight(FontWeight.Bold).fontColor(COLORS.greenD)
}.width('100%').margin({ top: 8 })
}
.layoutWeight(1).padding(14).backgroundColor(COLORS.card)
.borderRadius(12).margin({ left: 8 })
}
.width('100%').margin({ bottom: 4 })
}, (item: Appointment, idx: number) => idx.toString())
时间轴的核心设计在于左侧的 Column:首先是一个 12x12 的 Circle 圆点,填充颜色由 apptColor(item.status) 决定——待确认为橙、已确认为蓝、进行中为绿、已完成为灰、已取消为红。圆点设置了白色描边(stroke(COLORS.card).strokeWidth(2)),与卡片背景融合产生"镂空"效果。圆点下方是 Rect 竖线,宽度 2、高度 70、颜色为浅绿分隔线色,连接到下一个时间轴节点。最后一条数据不渲染竖线(if (idx < APPT_LIST.length - 1)),形成正确的时间轴终点。
右侧是预约信息白色卡片,包含两行:第一行是医院名+科室医生+状态标签(状态标签使用与圆点相同的颜色作为背景,白色文字),第二行是时间+费用。费用使用深医疗绿色加粗显示,强调金额信息。
十一、医生 Tab — 人物大卡
医生 Tab 由 tabDoctor Builder 构建,采用横向人物大卡布局,每张卡片包含完整的医生信息。
ForEach(DOCTOR_LIST, (item: Doctor, idx: number) => {
Row() {
// 医生头像
Column() { Text(item.avatar).fontSize(36) }
.width(64).height(64).backgroundColor(COLORS.chip).borderRadius(32)
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
// 医生信息
Column() {
Row() {
Text(item.name).fontSize(16).fontWeight(FontWeight.Bold).fontColor(COLORS.title)
Text(item.title).fontSize(12).fontColor(COLORS.sub).margin({ left: 8 })
}.width('100%')
Text(item.dept + ' · ' + item.hospital).fontSize(12).fontColor(COLORS.sub)
.margin({ top: 4 }).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1)
Row() {
Text('⭐ ' + item.rating.toString()).fontSize(12).fontColor(COLORS.orange)
Text('接诊费 ¥' + item.fee.toString()).fontSize(12).fontColor(COLORS.sub).margin({ left: 12 })
}.width('100%').margin({ top: 6 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Start).margin({ left: 12 })
// 状态与操作
Column() {
Text(item.status).fontSize(11)
.fontColor(item.status === '在线' ? COLORS.green : item.status === '忙碌' ? COLORS.orange : COLORS.text3)
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
.backgroundColor(item.status === '在线' ? COLORS.chip : item.status === '忙碌' ? COLORS.busyBg : COLORS.chip)
.borderRadius(10)
Text('咨询').fontSize(13).fontColor(COLORS.card)
.padding({ left: 14, right: 14, top: 6, bottom: 6 })
.backgroundColor(COLORS.green).borderRadius(16).margin({ top: 8 })
}.alignItems(HorizontalAlign.Center)
}
.width('92%').padding(14).backgroundColor(COLORS.card)
.borderRadius(14).margin({ bottom: 10 })
}, ...)
每张医生卡片由三部分组成:
左侧头像区:64x64 的圆形容器(borderRadius(32)),背景为浅绿芯片色,内含 36px 的 emoji 头像。justifyContent(FlexAlign.Center) 和 alignItems(HorizontalAlign.Center) 确保头像在容器内居中。
中间信息区:通过 layoutWeight(1) 占据剩余空间,包含三行信息——姓名+职称、科室+医院(带省略号截断)、评分+接诊费。评分使用橙色(COLORS.orange),与星号 emoji 配色一致。
右侧操作区:上方是状态标签,通过三元表达式实现三态颜色映射(在线→绿底绿字、忙碌→黄底橙字、离线→绿底灰字),下方是绿色"咨询"按钮。这种内联三元表达式虽然简洁,但在复杂场景中建议抽取为独立函数以提升可读性。
十二、报告 Tab — 左色条列表
报告 Tab 由 tabReport Builder 构建,采用左侧色条列表布局,每条报告左侧有一个彩色竖条标识状态。
ForEach(REPORT_LIST, (item: Report, idx: number) => {
Row() {
// 左侧色条
Rect().width(4).height('100%').fill(reportColor(item.status)).borderRadius(2)
Column() {
Row() {
Column() {
Text(item.name).fontSize(15).fontWeight(FontWeight.Medium).fontColor(COLORS.title)
Text(item.hospital).fontSize(12).fontColor(COLORS.sub).margin({ top: 3 })
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text(item.status).fontSize(12).fontColor(COLORS.card)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.backgroundColor(reportColor(item.status)).borderRadius(12)
}.width('100%')
Row() {
Text('📅 ' + item.date).fontSize(12).fontColor(COLORS.sub)
Blank()
Text('查看').fontSize(13).fontColor(COLORS.greenD)
.padding({ left: 14, right: 14, top: 5, bottom: 5 })
.border({ width: 1, color: COLORS.green }).borderRadius(14)
}.width('100%').margin({ top: 8 })
}
.layoutWeight(1).padding({ left: 12, right: 14, top: 12, bottom: 12 })
}
.width('92%').backgroundColor(COLORS.card).borderRadius(12).margin({ bottom: 10 })
}, ...)
左色条是一个 Rect,宽度 4、高度 100%,填充颜色由 reportColor(item.status) 决定(正常→绿、异常→红、待出→橙),带 2px 圆角。这种设计让用户在列表中快速扫描时,红色色条会立即吸引注意力,实现"异常优先"的信息层级。
右侧内容区包含两行:第一行是报告名+医院+状态标签,第二行是日期+"查看"按钮。查看按钮使用描边样式(border({ width: 1, color: COLORS.green }))而非填充背景,视觉上比填充按钮更轻量,适合作为次要操作。
十三、药房 Tab — 双列卡片
药房 Tab 由 tabPharmacy Builder 构建,采用 3 行 2 列双列卡片布局,展示药品信息。
@Builder
medCard(idx: number, tagColor: string) {
Column() {
Text(MED_LIST[idx].icon).fontSize(42)
Text(MED_LIST[idx].tag).fontSize(10).fontColor(COLORS.card)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
.backgroundColor(tagColor).borderRadius(8).margin({ top: 6 }).alignSelf(ItemAlign.Start)
Text(MED_LIST[idx].name).fontSize(14).fontWeight(FontWeight.Medium).fontColor(COLORS.title)
.margin({ top: 4 }).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1).width('100%')
Text(MED_LIST[idx].spec).fontSize(11).fontColor(COLORS.sub).margin({ top: 2 }).width('100%')
Row() {
Text('¥' + MED_LIST[idx].price.toString()).fontSize(16).fontWeight(FontWeight.Bold).fontColor(COLORS.red)
Blank()
Text('购买').fontSize(12).fontColor(COLORS.card)
.padding({ left: 10, right: 10, top: 4, bottom: 4 })
.backgroundColor(COLORS.green).borderRadius(12)
}.width('100%').margin({ top: 8 })
}
.layoutWeight(1).padding(12).backgroundColor(COLORS.card).borderRadius(14)
}
medCard 是一个接收索引和标签颜色两个参数的可复用 Builder。标签使用 alignSelf(ItemAlign.Start) 实现左对齐,这是 ArkUI 中少见的 ItemAlign 使用场景——在 Column 中默认居中对齐时,通过 alignSelf 单独设置某个子元素的对齐方式。
药房 Tab 的布局通过三组 Row + ForEach 实现,每组遍历硬编码的索引数组 [0,1]、[2,3]、[4,5],并分别传入不同的标签颜色(橙、蓝、绿)。这种按行硬编码的方式虽然不如预计算数组优雅,但在数据量固定且少量时是一种可接受的实践。
十四、我的 Tab — 渐变大数字卡
我的 Tab 由 tabMine Builder 构建,包含渐变积分卡、健康档案摘要和功能列表。
14.1 渐变积分卡
Column() {
Text('健康积分').fontSize(14).fontColor(COLORS.card).opacity(0.9)
Text((this.user.points + this.breath * 2).toString())
.fontSize(48).fontWeight(FontWeight.Bold).fontColor(COLORS.card).margin({ top: 4 })
Text(this.user.level).fontSize(13).fontColor(COLORS.card)
.padding({ left: 14, right: 14, top: 4, bottom: 4 })
.backgroundColor('rgba(255,255,255,0.25)').borderRadius(14).margin({ top: 8 })
}
.width('92%').padding({ top: 24, bottom: 24 }).alignItems(HorizontalAlign.Center)
.linearGradient({ direction: GradientDirection.RightLeft, colors: [[COLORS.green, 0], [COLORS.blue, 1]] })
.borderRadius(20).margin({ top: 4, bottom: 16 })
这张卡片是整个应用视觉最抢眼的元素。linearGradient 设置了从右到左(GradientDirection.RightLeft)的渐变,从医疗绿过渡到健康蓝,形成一道生命色弧光。积分数字使用 48px 加粗白色,通过 this.user.points + this.breath * 2 实现每秒增加 2 的跳动效果,传递"积分在增长"的积极信号。等级标签使用半透明白色背景(rgba(255,255,255,0.25)),在渐变背景上形成玻璃态效果。
14.2 健康档案与功能列表
健康档案摘要行展示用户基本信息(姓名+年龄+身高+体重),功能列表使用 ForEach 渲染 5 个功能入口(我的预约、健康报告、我的订单、家庭成员、设置),每项包含图标+名称+描述+右箭头。
十五、图表卡片
图表卡片由 chartCard Builder 构建,实现了一个纯代码柱状图,无第三方图表库依赖。
@Builder
chartCard() {
Column() {
Row() {
Text('就诊趋势').fontSize(16).fontWeight(FontWeight.Bold).fontColor(COLORS.title)
Blank()
Text('近6个月').fontSize(12).fontColor(COLORS.sub)
}.width('100%').margin({ bottom: 14 })
Row() {
ForEach(HEALTH_VAL, (val: number, idx: number) => {
Column() {
Column() {}
.width('60%')
.height((val * 12 + (this.breath % 5) * 0.5) + 'vp')
.linearGradient({
direction: GradientDirection.BottomTop,
colors: [[COLORS.green, 0], [COLORS.text3, 1]]
})
.borderRadius({ topLeft: 4, topRight: 4 })
Text(MONTH[idx]).fontSize(11).fontColor(COLORS.sub).margin({ top: 6 })
}
.layoutWeight(1).alignItems(HorizontalAlign.Center)
}, (val: number, idx: number) => idx.toString())
}
.width('100%').height(100).alignItems(VerticalAlign.Bottom)
}
.width('92%').padding(16).backgroundColor(COLORS.card)
.borderRadius(16).margin({ bottom: 16 })
}
柱状图的核心实现原理:外层 Row 设置 alignItems(VerticalAlign.Bottom) 使所有柱子底部对齐;每个柱子是一个 Column,通过 layoutWeight(1) 等分宽度。柱子本身是一个空 Column,宽度 60%,高度通过 val * 12 + (this.breath % 5) * 0.5 计算得出——基础高度为数据值乘以 12(如值为 8 时基础高度 96vp),再加上呼吸动画带来的 0~2vp 波动,使柱子呈现微妙的"呼吸"效果。
柱子使用 linearGradient 从底部医疗绿渐变到顶部浅绿灰,加上 borderRadius({ topLeft: 4, topRight: 4 }) 顶部圆角,视觉效果柔和友好。底部是月份标签,使用 11px 副色文字。
这个纯代码图表的实现展示了 ArkUI 声明式布局的强大能力——无需任何图表库,仅通过基础组件的组合和状态驱动,就实现了动态柱状图。
十六、底部 Tab 栏
@Builder
tabBar() {
Row() {
ForEach(TAB_LIST, (tab: TabMeta, idx: number) => {
Column() {
Text(tab.icon).fontSize(22).opacity(this.currentTab === idx ? 1 : 0.5)
Text(tab.label).fontSize(11)
.fontColor(this.currentTab === idx ? COLORS.tabOn : COLORS.text3)
.margin({ top: 2 })
}
.layoutWeight(1).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
.onClick(() => { this.currentTab = idx })
}, (tab: TabMeta, idx: number) => idx.toString())
}
.width('100%').height(64).backgroundColor(COLORS.card)
.border({ width: { top: 1 }, color: COLORS.line })
}
Tab 栏使用 ForEach 遍历 TAB_LIST 渲染六个 Tab。选中态通过两个维度区分:图标透明度(选中 1.0,未选中 0.5)和文字颜色(选中 tabOn 医疗绿,未选中 text3 浅绿灰)。每个 Tab 设置 onClick 回调,点击时将 currentTab 设为对应索引,ArkUI 自动重新渲染内容区域。
Tab 栏顶部设置了 1px 的浅绿色分隔线(border({ width: { top: 1 }, color: COLORS.line })),与卡片背景形成微妙的边界感。高度 64px 是移动端 Tab 栏的常见尺寸,兼顾触控区域大小和屏幕利用率。
十七、弹窗系统
该源码实现了完整的弹窗系统,包含遮罩层和三个功能弹窗。
17.1 遮罩层
@Builder
modalOverlay() {
Column() {}.width('100%').height('100%').backgroundColor(COLORS.mask)
.onClick(() => { this.addModal = false; this.editModal = false; this.delModal = false })
}
遮罩层是一个全屏半透明黑色 Column,点击时将所有弹窗状态设为 false,实现"点击遮罩关闭弹窗"的交互模式。三个弹窗状态在 build 方法中通过 if 条件渲染控制显示。
17.2 新增预约弹窗
新增弹窗包含医院输入、科室输入两个表单字段和取消/确认两个操作按钮。确认按钮使用医疗绿背景,取消按钮使用页面底色背景。两个按钮均使用 layoutWeight(1) 等分宽度,borderRadius(22) 形成胶囊形按钮。
17.3 编辑弹窗
编辑弹窗包含时间输入和备注信息(TextArea 多行文本域)两个字段。保存按钮使用健康蓝背景,与新增弹窗的绿色按钮形成功能区分——绿色用于"新建"操作,蓝色用于"修改"操作。
17.4 删除确认弹窗
删除弹窗采用居中布局,顶部是 40px 的警告 emoji(⚠️),中间是标题和提示文本,底部是"再想想"和"确认取消"两个按钮。确认按钮使用红色背景(COLORS.red),传达危险操作的警示感。弹窗宽度设为 80%(比新增和编辑的 85% 略窄),视觉上更聚焦。
十八、功能模块对比表
| 维度 | 首页 | 预约 | 医生 | 报告 | 药房 | 我的 |
|---|---|---|---|---|---|---|
| 布局方式 | 宫格4×2 | 状态时间轴 | 人物大卡 | 左色条列表 | 双列卡片3×2 | 渐变大数字卡 |
| 数据模型 | MedService | Appointment | Doctor | Report | Medicine | UserHealth |
| 字段数 | 3 | 6 | 8 | 5 | 5 | 6 |
| 核心操作 | 服务入口跳转 | 查看/取消预约 | 咨询医生 | 查看报告 | 购买药品 | 查看档案/设置 |
| 动画效果 | 图表呼吸 | 时间轴连线 | 头像呼吸 | 色条标识 | 标签色彩 | 积分跳动 |
| 状态颜色 | 无 | apptColor五态 | 三态在线状态 | reportColor三态 | 标签多色 | 渐变背景 |
| 数据量 | 8条 | 6条 | 6条 | 6条 | 6条 | 3条 |
| 特殊组件 | Divider分隔线 | Circle圆点+Rect竖线 | 三元表达式状态 | Rect色条 | SelfAlign左对齐 | linearGradient渐变 |
| 颜色策略 | green主色调 | 五色状态映射 | green/orange/text3 | green/red/orange | orange/blue/green | green→blue渐变 |
从对比表可以看出,六个 Tab 在布局方式、数据模型、状态颜色和特殊组件使用上均实现了完全差异化,没有两个 Tab 使用相同的布局策略。这种差异化设计不仅提升了用户体验的丰富度,也展示了 ArkUI 声明式布局在不同场景下的灵活适应能力。
十九、总结与展望
19.1 技术亮点总结
回顾整个源码,可以总结出以下核心技术亮点:
色彩系统统一管理:17 个色值集中定义在 COLORS 常量中,所有组件通过 COLORS.xxx 引用颜色,实现了"一处定义,处处使用"的色彩管理。这种模式的优势在于:主题切换时只需修改一处常量,所有组件自动更新;避免了硬编码色值导致的色彩不一致问题。
预计算数组替代 filter:针对宫格布局的行拆分需求,采用四个预计算索引数组(SERVICE_ROW0~3)替代运行时 .filter() 调用,既规避了 ArkTS 的类型推断限制,又提升了运行时性能。这是一种典型的"以空间换安全和性能"的策略。
呼吸动画驱动多处视觉:单个 breath 状态变量通过 setInterval 每秒更新,驱动步数跳动、心率波动、图表柱子高度波动、积分数字增加四处视觉效果。这种"一状态多消费"的模式是 ArkUI 响应式编程的精髓。
状态-颜色映射函数:apptColor 和 reportColor 两个函数将业务状态映射为语义颜色,使颜色不再是无意义的装饰,而是承载信息的视觉语言。患者通过颜色即可判断状态,无需阅读文字。
纯代码图表实现:就诊趋势柱状图完全通过 Column 组件的高度和渐变实现,无任何图表库依赖,展示了 ArkUI 基础组件的组合能力。
19.2 设计哲学分析
该源码体现了医疗健康应用的三条设计哲学:
信任感优先:医疗绿与健康蓝的双主色搭配,传递自然、专业、安心的视觉气质;浅色主题减少视觉刺激,适合长时间浏览;状态颜色的语义化映射让信息获取更直观。
生命感注入:通过呼吸动画让健康数据"活起来"——步数在走、心率在跳、积分在涨,这些微妙的动态效果让冰冷的数字获得了生命感,增强了用户对应用的信任和粘性。
场景差异化:六个 Tab 采用六种完全不同的布局策略,从宫格到时间轴,从人物大卡到左色条列表,从双列卡片到渐变大数字,每种布局都针对其场景优化了信息展示方式。
19.3 优化与展望
尽管该源码已经是一个完整可运行的应用页面,仍有以下优化方向值得探索:
网络请求层接入:当前数据全部为 Mock 数据,实际应用需接入后端 API,可引入 @ohos.net.http 或 @ohos.request 模块实现网络请求,配合 @State 实现数据加载-渲染的完整闭环。
持久化存储:健康数据、预约记录等需要持久化存储,可使用 @ohos.data.preferences(轻量级 KV 存储)或 @ohos.data.relationalStore(关系型数据库)实现本地数据管理。
权限管理:健康数据涉及用户隐私,需接入 @ohos.abilityAccessCtrl 实现权限申请和管理,确保数据安全。
组件抽取与复用:当前 serviceItem、medCard 等可复用 Builder 仍定义在主组件内部,可抽取为独立组件文件,提升代码可维护性。
动画精细化:当前呼吸动画较为简单,可引入 animateTo 和 curves 模块实现更精细的过渡动画,如 Tab 切换时的滑入滑出效果、卡片点击时的缩放反馈等。
无障碍支持:医疗应用的用户群体可能包含老年人和视障人士,应增加 accessibilityText、accessibilityDescription 等无障碍属性,确保所有用户都能顺畅使用。
总结而言,该源码是一份结构完整、约束严格、视觉统一的 HarmonyOS ArkUI 医疗健康应用前端实现,展示了声明式 UI 开发在多场景应用中的实践方法,为鸿蒙原生应用开发提供了有价值的参考范例。
附录:DevEco Studio 创建新项目与查看 SDK 版本
本章节演示如何使用 DevEco Studio 创建一个 HarmonyOS 新项目,并查看当前 IDE 已安装的 SDK 版本,适合作为其他技术博文的补充操作指南。
一、创建新项目
1.1 进入欢迎界面
启动 DevEco Studio 后,首先看到的是欢迎界面。左侧导航栏默认选中 “项目”,右侧提供三个主要入口:
- 新建项目:从头创建新项目
- 打开项目:打开本地已有项目
- 克隆仓库:从 Git 等版本控制拉取代码
点击 “新建项目” 按钮,进入项目创建向导。

1.2 选择项目模板
在弹出的"新建项目"对话框中,左侧分类标签提供了两种项目类型:
| 类型 | 说明 |
|---|---|
| 应用(Application) | 开发标准的 HarmonyOS 应用,具备完整的 Ability 生命周期 |
| 元服务(Atomic Service) | 开发轻量级的原子化服务,无需安装即可使用 |
选择 “应用” 标签后,右侧展示多种模板。对于大多数场景,推荐选择 “Empty Ability” —— 这是一个最基础的入门模板,仅包含 Hello World 功能,适合从零开始构建应用。

1.3 配置项目信息
点击 “下一步” 后,进入项目配置界面,需要填写以下核心参数:
| 配置项 | 示例值 | 说明 |
|---|---|---|
| 项目名称(Project name) | rollboat | 应用的项目名称,建议使用英文命名 |
| 包名(Bundle name) | com.rollboat.myapplication | 应用唯一标识,采用反向域名格式 |
| 保存路径(Save location) | D:\CodeFactory\rollboat | 项目本地存储路径,避免使用中文和空格 |
| 兼容 SDK(Compatible SDK) | 6.1.1(24) | 目标 HarmonyOS API 版本,点击"查看参考"可了解各版本差异 |
| 模块名称(Module name) | entry | 主模块名称,默认 entry 为应用入口模块 |
| 设备类型(Device types) | ☑ Phone | 勾选目标设备:Phone / Tablet / 2in1 / Car / Wearable / TV |
右侧预览区会实时展示当前模板的默认效果 —— 一个居中显示的 “Hello World” 文本。

1.4 完成创建
确认配置无误后,点击右下角 “完成” 按钮,IDE 将自动执行以下操作:
- 生成项目骨架(Stage 模型目录结构)
- 执行
ohpm install安装依赖 - 运行 Hvigor 构建初始化(
Build Init)
构建日志中显示 “退出代码为 0” 表示项目初始化成功。

1.5 项目结构概览
创建完成后,左侧项目面板展示的是标准的 Stage 模型 目录结构:
rollboat/
├── .hvigor/ # Hvigor 构建工具缓存
├── .idea/ # IDE 配置文件
├── AppScope/ # 应用级全局配置
│ └── app.json5
├── entry/ # 主模块(入口模块)
│ ├── src/main/ets/
│ │ ├── entryability/ # Ability 生命周期管理
│ │ │ └── EntryAbility.ets
│ │ └── pages/ # UI 页面
│ │ └── Index.ets # 首页(默认 Hello World)
│ ├── src/main/resources/ # 资源文件
│ ├── module.json5 # 模块配置
│ └── build-profile.json5 # 构建配置
├── oh_modules/ # OHPM 依赖包
├── build-profile.json5 # 工程构建配置
├── hvigorfile.ts # Hvigor 构建脚本
└── oh-package.json5 # 包管理配置
核心文件 Index.ets 的默认代码如下,采用 ArkTS 声明式 UI 语法:
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
RelativeContainer() {
Text(this.message)
.id('HelloWorld')
.fontSize($r('app.float.page_text_font_size'))
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
.onClick(() => {
this.message = 'Welcome';
})
}
.height('100%')
.width('100%')
}
}
| 关键语法 | 作用 |
|---|---|
@Entry | 标记为页面入口,可用于路由跳转 |
@Component | 声明为自定义组件 |
@State | 状态变量,数据变更时自动触发 UI 刷新 |
RelativeContainer | 相对布局容器,替代传统线性布局 |
.onClick() | 点击事件,此处点击后文本变为 “Welcome” |
打开右侧 Previewer(预览器),选择 Phone 设备,即可实时预览 Hello World 效果,无需连接真机或启动模拟器。

二、查看 SDK 版本
2.1 查看 HarmonyOS SDK
DevEco Studio 安装时已内置 HarmonyOS SDK,无需单独下载。通过以下路径查看:
文件 → 设置 → HarmonyOS SDK(或快捷键
Ctrl + Alt + S搜索 “HarmonyOS SDK”)
在设置面板中,可以看到当前已安装的 SDK 版本信息:
| 名称 | 阶段 | 状态 |
|---|---|---|
| HarmonyOS 6.1.1 | Release | ✅ 已安装 |
界面顶部提示:“HarmonyOS SDK 已经包含在 IDE,无需单独安装”,省去了手动配置 SDK 的繁琐步骤。

2.2 查看 ArkUI-X SDK(跨平台扩展)
如果项目需要将 ArkUI 框架扩展到多个 OS 平台(Android / iOS / OpenHarmony),还需要配置 ArkUI-X SDK。路径如下:
文件 → 设置 → 语言和框架 → ArkUI-X
在这里可以查看已安装和可选的 ArkUI-X SDK 版本:
| 版本 | SDK 版本号 | 阶段 | 状态 |
|---|---|---|---|
| API Version 24 | 6.1.1.100 | Release | ✅ 已安装 |
| API Version 23 | 6.1.0.28 | Beta1 | 未安装 |
| API Version 22 | 6.0.2.112 | Release | 未安装 |
安装路径示例:D:\DevTools\ArkUI-X\sdk
说明:ArkUI-X 允许开发者使用一套 ArkTS 主代码,同时构建多平台应用。如果仅开发 HarmonyOS 原生应用,无需额外安装 ArkUI-X SDK。

三、小结
| 步骤 | 操作 | 关键点 |
|---|---|---|
| 创建项目 | 欢迎页 → 新建项目 → 选择 Empty Ability 模板 → 配置项目信息 → 完成 | 使用 Stage 模型 + ArkTS 语言 |
| 查看 SDK | 设置 → HarmonyOS SDK | SDK 已内置,无需手动安装 |
| 跨平台扩展 | 设置 → ArkUI-X | 根据需要安装对应 API 版本 |
至此,DevEco Studio 的项目创建与 SDK 环境确认全部完成,可以开始 HarmonyOS 应用的功能开发。
本文基于 DevEco Studio 6.1.1 Release 版本编写,不同版本界面可能存在细微差异。
更多推荐


所有评论(0)