欢迎光临
我们一直在努力

HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“医智助手“——医疗影像AI智能体辅助诊断平台

文章目录

    • 每日一句正能量
    • 前言
    • 一、前言:AI智能体时代的医疗影像革新
    • 二、核心特性解析与技术选型
      • 2.1 HMAF在医疗影像诊断中的价值
      • 2.2 沉浸光感在医疗诊断中的创新应用
      • 2.3 悬浮导航的医疗适配
    • 三、项目实战:"医智助手"架构设计
      • 3.1 应用场景与功能规划
      • 3.2 技术架构图
    • 四、环境配置与模块依赖
      • 4.1 模块依赖配置
      • 4.2 权限声明(module.json5)
    • 五、核心组件实战
      • 5.1 窗口沉浸配置(EntryAbility.ets)
      • 5.2 病灶风险光效系统(LesionLightEffect.ets)
      • 5.3 HMAF四层诊疗智能体架构(DiagnosisAgentScheduler.ets)
      • 5.4 悬浮科室导航(MedicalFloatNavigation.ets)
      • 5.5 影像视窗与病灶高亮(ImageViewer.ets)
      • 5.6 浮动病灶标注面板(LesionPanelWindow.ets)
      • 5.7 多窗口光效同步管理器(WindowLightSync.ets)
      • 5.8 主页面集成(Index.ets)
    • 六、关键技术总结
      • 6.1 HMAF医疗影像诊断开发清单
      • 6.2 沉浸光感实现清单
      • 6.3 病灶风险光效映射
      • 6.4 PC端多窗口光效协同
    • 七、调试与适配建议
    • 八、总结与展望

在这里插入图片描述

每日一句正能量

低谷期并不意味着无法前行,相反,它是你自我觉醒的关键时刻。 低谷不是行动的终点,而是转变的起点。许多重大突破恰恰发生在看似停滞的时期,因为此时旧模式瓦解,新认知才有空间诞生。

前言

摘要:医疗影像领域存在海量数据处理压力,传统人工阅片存在效率低、误诊率高等问题。HarmonyOS 6(API 23)引入的鸿蒙智能体框架(HMAF)配合端侧AI能力,为医疗影像智能分析提供了全新可能。本文将实战开发一款面向HarmonyOS PC的"医智助手"应用,展示如何利用HMAF构建"影像采集-病灶检测-诊断推理-报告生成"四层智能体协作架构,通过悬浮导航实现科室与病例状态实时切换,基于沉浸光感打造"病灶即氛围"的诊疗感知体验,以及基于多窗口架构构建浮动影像视窗、病灶标注面板和诊断报告窗口的协作诊疗体验。


一、前言:AI智能体时代的医疗影像革新

2026年,医疗智能体已成为垂直领域最火热的应用方向。医疗智能体提供智能问诊、临床决策支持、医学影像分析、患者管理等服务,基于大规模真实医学数据与临床案例进行训练,在影像判读、智能问诊、临床决策支持等场景中全方位提升医疗服务质量和效率。

传统医疗影像诊断往往采用固定的PACS界面和静态报告,医生需要手动调阅影像、逐层分析、再手动撰写报告。而在HarmonyOS 6的PC大屏环境下,这种被动式诊断模式显得低效且缺乏智能辅助。HMAF配合**悬浮导航(Float Navigation)与沉浸光感(Immersive Light Effects)**特性,为医疗影像AI诊断带来了"病灶即氛围、异常即导航"的全新可能。

本文核心亮点:

  • 病灶风险光效:根据病灶风险等级(良性/低危/中危/高危/恶性)动态切换环境光色与脉冲节奏
  • 悬浮科室导航:底部悬浮页签替代传统工具栏,支持科室切换、病例筛选、诊断状态实时徽章
  • HMAF四层诊疗架构:基于Agent Framework Kit构建"影像采集-病灶检测-诊断推理-报告生成"四层智能体协作架构
  • 多窗口协作诊疗:主影像视窗 + 浮动病灶标注面板 + 浮动诊断报告窗口 + 浮动参考病例窗口的光效联动
  • 诊断意图沉浸感知:通过Intents Kit实时理解医生诊断意图,自动调整界面光效与导航形态

二、核心特性解析与技术选型

2.1 HMAF在医疗影像诊断中的价值

HarmonyOS 6的HMAF采用四层架构设计:应用智能体层、智能体框架层、AI引擎层、智能体内核层。 在"医智助手"中,这种架构能够:

  • 原生智能调度:医疗智能体不再是应用的附属品,而是系统的基础能力,支持跨科室任务编排
  • 意图即诊断:通过Intents Kit将医生自然语言意图(如"查看肺部CT结节")转化为结构化诊断任务
  • 分布式智能体协同:利用鸿蒙分布式软总线,实现PC主控+平板阅片+手机随访的多设备协作
  • 端云协同推理:端侧MindSpore Lite处理实时影像预处理,云端大模型处理复杂病灶识别

2.2 沉浸光感在医疗诊断中的创新应用

HarmonyOS 6的systemMaterialEffect通过模拟物理光照模型,为诊断状态反馈带来细腻的视觉表达。在医疗影像场景中,这种材质效果能够:

  • 增强病灶感知:不同风险等级拥有专属光效标识(良性绿、低危蓝、中危黄、高危橙、恶性红)
  • 状态直觉感知:分析时的呼吸蓝光、发现病灶时的脉冲金光、高危病灶时的警示红光
  • 提升诊断专注度:动态环境光随影像复杂度变化,简单病例柔和、复杂病例强烈

2.3 悬浮导航的医疗适配

与传统PACS系统不同,医疗影像诊断平台需要处理:

  • 高频科室切换:医生常在多个科室间快速跳转查看病例
  • 信息密度平衡:既要保证导航可见,又不能遮挡影像内容
  • 实时诊断徽章:诊断任务运行状态需要实时徽章提示(分析中脉冲、待确认角标、已完成确认)

HarmonyOS 6的悬浮页签支持**强(85%)、平衡(70%)、弱(55%)**三档透明度自定义,结合PC端的自由窗口能力,可以实现"需要时出现,专注时隐退"的智能导航体验。


三、项目实战:"医智助手"架构设计

3.1 应用场景与功能规划

面向HarmonyOS PC的医疗影像AI诊断场景,核心功能包括:

功能模块技术实现沉浸光感/HMAF应用
主影像视窗 Canvas + DICOM解析 病灶高亮光效、影像增强呼吸灯
悬浮科室导航 HdsTabs + systemMaterialEffect 玻璃拟态页签,诊断状态徽章
影像采集智能体 HMAF Agent Framework Kit 采集进度光效反馈
病灶检测智能体 HMAF + 3D UNet 检测进度光效脉冲
诊断推理智能体 HMAF + 知识图谱 推理结果光效标记
报告生成智能体 HMAF + NLG 报告完成光效提示
浮动病灶标注面板 子窗口 + HdsNavigation 病灶主题色光效同步
浮动诊断报告窗口 子窗口 + RichText 诊断结果颜色编码
浮动参考病例窗口 子窗口 + Grid 相似病例匹配度光效

3.2 技术架构图

┌─────────────────────────────────────────────────────────────┐
│ 医智助手 – 应用层 │
├─────────────┬─────────────┬─────────────┬─────────────────────┤
│ 影像采集智能体 │ 病灶检测智能体 │ 诊断推理智能体 │ 报告生成智能体 │
│ (ImageAcq) │ (LesionDet) │ (Diagnosis) │ (ReportGen) │
├─────────────┴─────────────┴─────────────┴─────────────────────┤
│ HMAF – 智能体框架层 │
│ Agent Framework Kit + Intents Kit + NLU Kit │
├─────────────────────────────────────────────────────────────┤
│ AI引擎层 – 能力层 │
│ MindSpore Lite(端侧) │ 鸿蒙大模型4.0(云端) │ 分布式软总线 │
├─────────────────────────────────────────────────────────────┤
│ ArkUI – 表现层 │
│ 悬浮导航 │ 沉浸光感 │ 多窗口管理 │ 安全区扩展 │ 动画系统 │
└─────────────────────────────────────────────────────────────┘


四、环境配置与模块依赖

4.1 模块依赖配置

在oh-package.json5中添加HMAF及相关Kit依赖:

{
"name": "medical_assistant",
"version": "1.0.0",
"description": "Medical Imaging AI Agent Diagnosis Platform",
"dependencies": {
"@ohos/hmaframework": "^6.0.0",
"@ohos/intentskit": "^6.0.0",
"@ohos/nlukit": "^6.0.0",
"@ohos/mindsporelite": "^6.0.0",
"@ohos/aiengine": "^6.0.0",
"@ohos/hds": "^6.0.0",
"@ohos/medicalkit": "^6.0.0"
}
}

4.2 权限声明(module.json5)

{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": ["phone", "tablet", "2in1"],
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:internet_permission"
},
{
"name": "ohos.permission.SYSTEM_FLOAT_WINDOW",
"reason": "$string:float_window_permission",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC",
"reason": "$string:distributed_permission"
},
{
"name": "ohos.permission.AI_ENGINE_ACCESS",
"reason": "$string:ai_engine_permission"
},
{
"name": "ohos.permission.READ_FILE",
"reason": "$string:read_file_permission"
},
{
"name": "ohos.permission.WRITE_FILE",
"reason": "$string:write_file_permission"
}
]
}
}


五、核心组件实战

5.1 窗口沉浸配置(EntryAbility.ets)

// entry/src/main/ets/entryability/EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

export default class EntryAbility extends UIAbility {
private windowStage: window.WindowStage | null = null;

onWindowStageCreate(windowStage: window.WindowStage): void {
this.windowStage = windowStage;
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error('Failed to load content:', JSON.stringify(err));
return;
}
console.info('Succeeded in loading content.');
this.setupImmersiveWindow(windowStage);
});
}

private async setupImmersiveWindow(windowStage: window.WindowStage): Promise<void> {
try {
const mainWindow = windowStage.getMainWindowSync();
await mainWindow.setWindowLayoutFullScreen(true);
await mainWindow.setWindowBackgroundColor('#00000000');
await mainWindow.setWindowSystemBarProperties({
statusBarColor: '#00000000',
navigationBarColor: '#00000000',
statusBarContentColor: '#FFFFFF',
navigationBarContentColor: '#FFFFFF'
});
await mainWindow.setWindowAvoidAreaOption({
type: window.AvoidAreaType.TYPE_SYSTEM,
enabled: true
});
console.info('Immersive window setup completed');
} catch (error) {
console.error('Failed to setup immersive window:', (error as BusinessError).message);
}
}

onWindowStageDestroy(): void {
this.windowStage = null;
}
}

代码亮点:通过setWindowLayoutFullScreen(true)实现内容延伸至非安全区,配合setWindowAvoidAreaOption启用HarmonyOS 6新增的安全区避让机制,确保悬浮导航不会遮挡影像内容。

5.2 病灶风险光效系统(LesionLightEffect.ets)

代码亮点:这是本应用的核心创新组件。它将病灶风险等级映射为动态光效——良性时柔和绿光、低危时平静蓝光、中危时警示黄光、高危时脉冲橙光、恶性时闪烁红光。每个风险等级拥有专属脉冲节奏与光晕强度,通过AppStorage全局状态实现跨组件光效同步。

// entry/src/main/ets/components/LesionLightEffect.ets
import { hmaf } from '@kit.HMAFramework';

// 病灶风险等级枚举
export enum RiskLevel {
BENIGN = 'benign', // 良性
LOW = 'low', // 低危
MEDIUM = 'medium', // 中危
HIGH = 'high', // 高危
MALIGNANT = 'malignant' // 恶性
}

// 风险等级色彩配置
export const RiskColors: Record<RiskLevel, string> = {
[RiskLevel.BENIGN]: '#2ECC71', // 良性绿
[RiskLevel.LOW]: '#4A90D9', // 低危蓝
[RiskLevel.MEDIUM]: '#F5A623', // 中危黄
[RiskLevel.HIGH]: '#FF6B6B', // 高危橙
[RiskLevel.MALIGNANT]: '#FF0000' // 恶性红
};

// 光效配置
interface LightEffectConfig {
baseColor: string;
pulseSpeed: number; // 脉冲周期(ms)
pulseIntensity: number; // 脉冲强度(0-1)
glowRadius: number; // 光晕半径
alertMode: boolean; // 是否启用警示模式
}

@Component
export struct LesionLightEffect {
@State currentRisk: RiskLevel = RiskLevel.BENIGN;
@State lightIntensity: number = 0.4;
@State pulsePhase: number = 0;
@State alertFlash: boolean = false;

private pulseTimer: number = 1;
private alertTimer: number = 1;

aboutToAppear(): void {
this.startPulseAnimation();
// 监听全局风险等级变化
AppStorage.setOrCreate('lesion_risk_change', (level: RiskLevel) => {
this.currentRisk = level;
this.updateLightEffect();
});
}

aboutToDisappear(): void {
clearInterval(this.pulseTimer);
clearInterval(this.alertTimer);
}

private startPulseAnimation(): void {
this.pulseTimer = setInterval(() => {
this.pulsePhase = (this.pulsePhase + 0.05) % (Math.PI * 2);
this.lightIntensity = this.calculatePulseIntensity();
}, 50);
}

private calculatePulseIntensity(): number {
const baseIntensity = this.getRiskConfig().pulseIntensity;
const variation = Math.sin(this.pulsePhase) * 0.3;
return Math.max(0.1, Math.min(1.0, baseIntensity + variation));
}

private getRiskConfig(): LightEffectConfig {
const configs: Record<RiskLevel, LightEffectConfig> = {
[RiskLevel.BENIGN]: {
baseColor: RiskColors[RiskLevel.BENIGN],
pulseSpeed: 4000,
pulseIntensity: 0.4,
glowRadius: 80,
alertMode: false
},
[RiskLevel.LOW]: {
baseColor: RiskColors[RiskLevel.LOW],
pulseSpeed: 3000,
pulseIntensity: 0.5,
glowRadius: 100,
alertMode: false
},
[RiskLevel.MEDIUM]: {
baseColor: RiskColors[RiskLevel.MEDIUM],
pulseSpeed: 2000,
pulseIntensity: 0.7,
glowRadius: 120,
alertMode: false
},
[RiskLevel.HIGH]: {
baseColor: RiskColors[RiskLevel.HIGH],
pulseSpeed: 1000,
pulseIntensity: 0.9,
glowRadius: 150,
alertMode: true
},
[RiskLevel.MALIGNANT]: {
baseColor: RiskColors[RiskLevel.MALIGNANT],
pulseSpeed: 500,
pulseIntensity: 1.0,
glowRadius: 200,
alertMode: true
}
};
return configs[this.currentRisk] || configs[RiskLevel.BENIGN];
}

private updateLightEffect(): void {
const config = this.getRiskConfig();
AppStorage.setOrCreate('current_lesion_color', config.baseColor);
AppStorage.setOrCreate('current_lesion_pulse', config.pulseSpeed);
AppStorage.setOrCreate('current_lesion_intensity', config.pulseIntensity);

// 高危/恶性级别启用警示闪烁
if (config.alertMode) {
this.startAlertFlash();
} else {
clearInterval(this.alertTimer);
this.alertFlash = false;
}
}

private startAlertFlash(): void {
this.alertTimer = setInterval(() => {
this.alertFlash = !this.alertFlash;
}, this.getRiskConfig().pulseSpeed / 2);
}

build() {
Stack() {
// 主光晕层
Column()
.width(400)
.height(400)
.backgroundColor(this.getRiskConfig().baseColor)
.blur(this.getRiskConfig().glowRadius)
.opacity(this.alertFlash ? this.lightIntensity * 1.5 : this.lightIntensity)
.position({ x: '50%', y: '30%' })
.anchor('50%')
.animation({
duration: this.getRiskConfig().pulseSpeed,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Alternate
})

// 警示闪烁层(高危/恶性)
if (this.getRiskConfig().alertMode) {
Column()
.width('100%')
.height('100%')
.backgroundColor(this.alertFlash ? 'rgba(255,0,0,0.1)' : 'transparent')
.animation({
duration: this.getRiskConfig().pulseSpeed / 2,
curve: Curve.Step,
iterations: 1
})
}

// 风险指示器环
Column()
.width(200)
.height(200)
.backgroundColor('transparent')
.border({
width: 3,
color: this.getRiskConfig().baseColor,
style: BorderStyle.Solid
})
.borderRadius(100)
.opacity(this.lightIntensity * 0.5)
.animation({
duration: this.getRiskConfig().pulseSpeed,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Alternate
})
}
.width('100%')
.height('100%')
}
}

5.3 HMAF四层诊疗智能体架构(DiagnosisAgentScheduler.ets)

代码亮点:基于HMAF Agent Framework Kit构建核心诊疗调度逻辑,实现"影像采集→病灶检测→诊断推理→报告生成"的完整诊疗工作流。调度器通过Intents Kit解析医生自然语言意图,自动创建四层智能体实例,并实时监控各智能体状态。

// entry/src/main/ets/services/DiagnosisAgentScheduler.ets
import { hmaf } from '@kit.HMAFramework';
import { intents } from '@kit.IntentsKit';
import { nlu } from '@kit.NLUKit';

// 诊疗任务定义
interface DiagnosisTask {
id: string;
taskType: 'image_acquisition' | 'lesion_detection' | 'diagnosis_reasoning' | 'report_generation';
patientId: string;
studyId: string;
modality: string;
parameters: Record<string, unknown>;
priority: number;
deadline: number;
}

// 病灶发现
interface Lesion {
id: string;
type: string;
location: string;
size: number;
riskLevel: RiskLevel;
confidence: number;
boundingBox: { x: number; y: number; width: number; height: number };
}

// 智能体实例
interface AgentInstance {
id: string;
type: 'image_acq' | 'lesion_det' | 'diagnosis' | 'report_gen';
status: 'idle' | 'acquiring' | 'detecting' | 'reasoning' | 'generating' | 'completed' | 'error';
currentTask: DiagnosisTask | null;
capability: string[];
}

export class DiagnosisAgentScheduler {
private static instance: DiagnosisAgentScheduler;
private agents: Map<string, AgentInstance> = new Map();
private taskQueue: DiagnosisTask[] = [];
private hmafSession: hmaf.AgentSession | null = null;
private intentEngine: intents.IntentEngine | null = null;
private lesions: Lesion[] = [];

private constructor() {}

static getInstance(): DiagnosisAgentScheduler {
if (!DiagnosisAgentScheduler.instance) {
DiagnosisAgentScheduler.instance = new DiagnosisAgentScheduler();
}
return DiagnosisAgentScheduler.instance;
}

async initialize(): Promise<void> {
try {
// 初始化HMAF会话
this.hmafSession = await hmaf.createAgentSession({
mode: hmaf.AgentMode.MULTI_AGENT,
maxConcurrentAgents: 8,
enableDistributed: true
});

// 初始化意图引擎
this.intentEngine = await intents.createIntentEngine({
supportedDomains: ['medical_imaging', 'diagnosis', 'report_generation'],
language: 'zh-CN'
});

// 注册四层诊疗智能体
await this.registerDiagnosisAgents();

console.info('DiagnosisAgentScheduler initialized successfully');
} catch (error) {
console.error('Failed to initialize DiagnosisAgentScheduler:', error);
throw error;
}
}

private async registerDiagnosisAgents(): Promise<void> {
// 注册影像采集智能体
this.agents.set('image-acq-1', {
id: 'image-acq-1',
type: 'image_acq',
status: 'idle',
currentTask: null,
capability: ['dicom_parsing', 'image_preprocessing', '3d_reconstruction']
});

// 注册病灶检测智能体
this.agents.set('lesion-det-1', {
id: 'lesion-det-1',
type: 'lesion_det',
status: 'idle',
currentTask: null,
capability: ['lung_nodule', 'liver_tumor', 'brain_lesion', 'bone_fracture']
});

this.agents.set('lesion-det-2', {
id: 'lesion-det-2',
type: 'lesion_det',
status: 'idle',
currentTask: null,
capability: ['cardiac_analysis', 'vascular_segmentation', 'mammography']
});

// 注册诊断推理智能体
this.agents.set('diagnosis-1', {
id: 'diagnosis-1',
type: 'diagnosis',
status: 'idle',
currentTask: null,
capability: ['differential_diagnosis', 'knowledge_graph_reasoning', 'clinical_guideline_matching']
});

// 注册报告生成智能体
this.agents.set('report-gen-1', {
id: 'report-gen-1',
type: 'report_gen',
status: 'idle',
currentTask: null,
capability: ['structured_report', 'narrative_report', 'comparison_report']
});
}

// 处理医生诊断意图
async processDiagnosisIntent(userInput: string, imageData: ArrayBuffer): Promise<string> {
try {
// 更新状态为采集中
this.updateAgentState('image-acq-1', 'acquiring');

// 解析医生意图
const parsedIntent = await this.intentEngine?.parseIntent(userInput);
if (!parsedIntent) {
throw new Error('Failed to parse diagnosis intent');
}

// 创建诊疗任务链
const taskChain = await this.createTaskChain(parsedIntent, imageData);

// 执行影像采集
const acquiredImages = await this.executeImageAcquisition(taskChain);

// 执行病灶检测
const detectedLesions = await this.executeLesionDetection(acquiredImages);

// 执行诊断推理
const diagnosisResult = await this.executeDiagnosisReasoning(detectedLesions);

// 执行报告生成
const report = await this.executeReportGeneration(diagnosisResult);

// 更新状态为完成
this.updateAgentState('image-acq-1', 'idle');

return this.formatDiagnosisReport(detectedLesions, diagnosisResult, report);
} catch (error) {
this.updateAgentState('image-acq-1', 'error');
console.error('Error processing diagnosis intent:', error);
return `诊断异常:${error.message}`;
}
}

private async createTaskChain(intent: intents.ParsedIntent, imageData: ArrayBuffer): Promise<<DiagnosisTask[]> {
const tasks: DiagnosisTask[] = [];
const taskId = `diag-${Date.now()}`;

// 根据意图类型创建诊疗链
switch (intent.domain) {
case 'medical_imaging':
tasks.push({
id: `${taskId}-1`,
taskType: 'image_acquisition',
patientId: intent.parameters.patientId || 'unknown',
studyId: intent.parameters.studyId || 'unknown',
modality: intent.parameters.modality || 'CT',
parameters: { imageData, windowing: intent.parameters.windowing },
priority: 1,
deadline: Date.now() + 30000
});
tasks.push({
id: `${taskId}-2`,
taskType: 'lesion_detection',
patientId: intent.parameters.patientId || 'unknown',
studyId: intent.parameters.studyId || 'unknown',
modality: intent.parameters.modality || 'CT',
parameters: { organ: intent.parameters.organ || 'lung' },
priority: 2,
deadline: Date.now() + 60000
});
tasks.push({
id: `${taskId}-3`,
taskType: 'diagnosis_reasoning',
patientId: intent.parameters.patientId || 'unknown',
studyId: intent.parameters.studyId || 'unknown',
modality: intent.parameters.modality || 'CT',
parameters: { history: intent.parameters.history },
priority: 3,
deadline: Date.now() + 90000
});
tasks.push({
id: `${taskId}-4`,
taskType: 'report_generation',
patientId: intent.parameters.patientId || 'unknown',
studyId: intent.parameters.studyId || 'unknown',
modality: intent.parameters.modality || 'CT',
parameters: { template: intent.parameters.template || 'structured' },
priority: 4,
deadline: Date.now() + 120000
});
break;

default:
tasks.push({
id: `${taskId}-1`,
taskType: 'image_acquisition',
patientId: 'unknown',
studyId: 'unknown',
modality: 'CT',
parameters: { imageData },
priority: 1,
deadline: Date.now() + 30000
});
}

return tasks;
}

private async executeImageAcquisition(tasks: DiagnosisTask[]): Promise<<ArrayBuffer[]> {
const results: ArrayBuffer[] = [];

for (const task of tasks.filter(t => t.taskType === 'image_acquisition')) {
const agent = this.findAvailableAgent('image_acq');
if (!agent) continue;

this.updateAgentState(agent.id, 'acquiring');

// 通过HMAF执行影像采集
const acquisition = await this.hmafSession?.sendTask({
targetAgent: agent.id,
taskType: 'image_acquisition',
parameters: {
imageData: task.parameters.imageData,
modality: task.modality,
windowing: task.parameters.windowing
},
timeout: task.deadline Date.now()
});

if (acquisition?.processedImages) {
results.push(acquisition.processedImages);
}

this.updateAgentState(agent.id, 'idle');
}

return results;
}

private async executeLesionDetection(images: ArrayBuffer[]): Promise<<Lesion[]> {
const agent = this.findAvailableAgent('lesion_det');
if (!agent) return [];

this.updateAgentState(agent.id, 'detecting');

const detectedLesions: Lesion[] = [];
for (const image of images) {
const detection = await this.hmafSession?.sendTask({
targetAgent: agent.id,
taskType: 'lesion_detection',
parameters: {
imageData: image,
model: 'lung_nano_unet',
confidenceThreshold: 0.85
},
timeout: 30000
});

if (detection?.lesions) {
for (const lesion of detection.lesions) {
detectedLesions.push({
id: `lesion-${Date.now()}${Math.random().toString(36).substr(2, 9)}`,
type: lesion.type,
location: lesion.location,
size: lesion.size,
riskLevel: this.mapConfidenceToRisk(lesion.confidence, lesion.type),
confidence: lesion.confidence,
boundingBox: lesion.boundingBox
});
}
}
}

this.lesions = detectedLesions;
this.updateAgentState(agent.id, 'idle');
return detectedLesions;
}

private mapConfidenceToRisk(confidence: number, type: string): RiskLevel {
if (confidence > 0.95 && ['adenocarcinoma', 'squamous_cell'].includes(type)) {
return RiskLevel.MALIGNANT;
}
if (confidence > 0.90) {
return RiskLevel.HIGH;
}
if (confidence > 0.80) {
return RiskLevel.MEDIUM;
}
if (confidence > 0.70) {
return RiskLevel.LOW;
}
return RiskLevel.BENIGN;
}

private async executeDiagnosisReasoning(lesions: Lesion[]): Promise<<Record<string, unknown>> {
const agent = this.findAvailableAgent('diagnosis');
if (!agent) return {};

this.updateAgentState(agent.id, 'reasoning');

const diagnosis = await this.hmafSession?.sendTask({
targetAgent: agent.id,
taskType: 'diagnosis_reasoning',
parameters: {
lesions: JSON.stringify(lesions),
knowledgeGraph: 'medical_kg_v2026',
clinicalGuidelines: 'nccn_2026'
},
timeout: 45000
});

this.updateAgentState(agent.id, 'idle');
return diagnosis || {};
}

private async executeReportGeneration(diagnosis: Record<string, unknown>): Promise<string> {
const agent = this.findAvailableAgent('report_gen');
if (!agent) return '';

this.updateAgentState(agent.id, 'generating');

const report = await this.hmafSession?.sendTask({
targetAgent: agent.id,
taskType: 'report_generation',
parameters: {
diagnosis: JSON.stringify(diagnosis),
template: 'structured',
language: 'zh-CN'
},
timeout: 30000
});

this.updateAgentState(agent.id, 'idle');
return report?.content || '';
}

private findAvailableAgent(type: string): AgentInstance | null {
for (const [id, agent] of this.agents.entries()) {
if (agent.type === type && agent.status === 'idle') {
return agent;
}
}
return null;
}

private updateAgentState(agentId: string, state: string): void {
const agent = this.agents.get(agentId);
if (agent) {
agent.status = state as 'idle' | 'acquiring' | 'detecting' | 'reasoning' | 'generating' | 'completed' | 'error';

// 触发全局状态更新,驱动光效变化
AppStorage.setOrCreate('agent_state_update', {
agentId: agentId,
state: state,
timestamp: Date.now()
});

// 根据风险等级更新全局光效
if (state === 'detecting') {
AppStorage.setOrCreate('lesion_risk_change', RiskLevel.MEDIUM);
}
}
}

private formatDiagnosisReport(lesions: Lesion[], diagnosis: Record<string, unknown>, report: string): string {
let result = '## 🏥 智能诊断报告\\n\\n';

const benignCount = lesions.filter(l => l.riskLevel === RiskLevel.BENIGN).length;
const lowCount = lesions.filter(l => l.riskLevel === RiskLevel.LOW).length;
const mediumCount = lesions.filter(l => l.riskLevel === RiskLevel.MEDIUM).length;
const highCount = lesions.filter(l => l.riskLevel === RiskLevel.HIGH).length;
const malignantCount = lesions.filter(l => l.riskLevel === RiskLevel.MALIGNANT).length;

result += `### 病灶统计\\n`;
result += `– 🟢 良性: ${benignCount}\\n`;
result += `– 🔵 低危: ${lowCount}\\n`;
result += `– 🟡 中危: ${mediumCount}\\n`;
result += `– 🟠 高危: ${highCount}\\n`;
result += `– 🔴 恶性: ${malignantCount}\\n\\n`;

result += `### 病灶详情\\n`;
for (const lesion of lesions) {
const riskEmoji = lesion.riskLevel === RiskLevel.BENIGN ? '🟢' :
lesion.riskLevel === RiskLevel.LOW ? '🔵' :
lesion.riskLevel === RiskLevel.MEDIUM ? '🟡' :
lesion.riskLevel === RiskLevel.HIGH ? '🟠' : '🔴';

result += `#### ${lesion.id}\\n`;
result += `– **风险等级**: ${riskEmoji} ${lesion.riskLevel}\\n`;
result += `– **置信度**: ${(lesion.confidence * 100).toFixed(1)}%\\n`;
result += `– **类型**: ${lesion.type}\\n`;
result += `– **位置**: ${lesion.location}\\n`;
result += `– **大小**: ${lesion.size} mm\\n\\n`;
}

result += `### 诊断结论\\n`;
result += `${diagnosis.conclusion || '待进一步检查'}\\n\\n`;

result += `### 结构化报告\\n`;
result += `${report}\\n`;

return result;
}

getLesionCount(): Record<RiskLevel, number> {
const counts: Record<RiskLevel, number> = {
[RiskLevel.BENIGN]: 0,
[RiskLevel.LOW]: 0,
[RiskLevel.MEDIUM]: 0,
[RiskLevel.HIGH]: 0,
[RiskLevel.MALIGNANT]: 0
};

for (const lesion of this.lesions) {
counts[lesion.riskLevel]++;
}

return counts;
}

getAgentStatus(): Array<{id: string, type: string, status: string, task: string | null}> {
return Array.from(this.agents.values()).map(agent => ({
id: agent.id,
type: agent.type,
status: agent.status,
task: agent.currentTask?.taskType || null
}));
}
}

5.4 悬浮科室导航(MedicalFloatNavigation.ets)

代码亮点:底部悬浮导航栏不仅承载页面切换功能,更重要的是实时显示诊断任务状态和病灶数量。每个导航项配备风险徽章——良性时绿色光点、低危时蓝色光点、中危时黄色脉冲、高危时橙色脉冲、恶性时红色闪烁。支持长按展开透明度调节面板。

// entry/src/main/ets/components/MedicalFloatNavigation.ets
import { window } from '@kit.ArkUI';
import { RiskLevel, RiskColors } from './LesionLightEffect';

// 导航项配置
interface MedicalNavItem {
id: string;
icon: Resource;
label: string;
page: string;
department?: string;
}

@Component
export struct MedicalFloatNavigation {
@State currentIndex: number = 0;
@State navTransparency: number = 0.70;
@State isExpanded: boolean = false;
@State bottomAvoidHeight: number = 0;
@State agentStates: Map<string, string> = new Map([
['image-acq-1', 'idle'],
['lesion-det-1', 'idle'],
['lesion-det-2', 'idle'],
['diagnosis-1', 'idle'],
['report-gen-1', 'idle']
]);
@State lesionCounts: Record<RiskLevel, number> = {
[RiskLevel.BENIGN]: 0,
[RiskLevel.LOW]: 0,
[RiskLevel.MEDIUM]: 0,
[RiskLevel.HIGH]: 0,
[RiskLevel.MALIGNANT]: 0
};

private navItems: MedicalNavItem[] = [
{ id: 'radiology', icon: $r('app.media.ic_radiology'), label: '影像科', page: 'RadiologyPage', department: 'radiology' },
{ id: 'ct', icon: $r('app.media.ic_ct'), label: 'CT室', page: 'CTPage', department: 'ct' },
{ id: 'mri', icon: $r('app.media.ic_mri'), label: 'MRI室', page: 'MRIPage', department: 'mri' },
{ id: 'ultrasound', icon: $r('app.media.ic_ultrasound'), label: '超声科', page: 'UltrasoundPage', department: 'ultrasound' },
{ id: 'pathology', icon: $r('app.media.ic_pathology'), label: '病理科', page: 'PathologyPage', department: 'pathology' }
];

aboutToAppear(): void {
this.getBottomAvoidArea();
// 监听智能体状态变化
AppStorage.setOrCreate('agent_state_update', (update: {agentId: string, state: string}) => {
this.agentStates.set(update.agentId, update.state);
});
// 监听病灶数量变化
AppStorage.setOrCreate('lesion_count_update', (counts: Record<RiskLevel, number>) => {
this.lesionCounts = counts;
});
}

private async getBottomAvoidArea(): Promise<void> {
try {
const mainWindow = await window.getLastWindow();
const avoidArea = mainWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
this.bottomAvoidHeight = avoidArea.bottomRect.height;
} catch (error) {
console.error('Failed to get avoid area:', error);
}
}

private getAgentStateForNavItem(item: MedicalNavItem): string {
// 根据科室映射到对应智能体
if (item.department === 'radiology') return this.agentStates.get('image-acq-1') || 'idle';
if (item.department === 'ct') return this.agentStates.get('lesion-det-1') || 'idle';
if (item.department === 'mri') return this.agentStates.get('lesion-det-2') || 'idle';
if (item.department === 'ultrasound') return this.agentStates.get('diagnosis-1') || 'idle';
if (item.department === 'pathology') return this.agentStates.get('report-gen-1') || 'idle';
return 'idle';
}

private getTotalLesionCount(): number {
return Object.values(this.lesionCounts).reduce((a, b) => a + b, 0);
}

private getHighestRiskLevel(): RiskLevel {
if (this.lesionCounts[RiskLevel.MALIGNANT] > 0) return RiskLevel.MALIGNANT;
if (this.lesionCounts[RiskLevel.HIGH] > 0) return RiskLevel.HIGH;
if (this.lesionCounts[RiskLevel.MEDIUM] > 0) return RiskLevel.MEDIUM;
if (this.lesionCounts[RiskLevel.LOW] > 0) return RiskLevel.LOW;
return RiskLevel.BENIGN;
}

private getStateBadgeColor(state: string): string {
const colors: Record<string, string> = {
'idle': '#888888',
'acquiring': '#4A90D9',
'detecting': '#F5A623',
'reasoning': '#9B59B6',
'generating': '#2ECC71',
'error': '#FF4444'
};
return colors[state] || '#888888';
}

private getStateBadgeAnimation(state: string): object {
switch (state) {
case 'acquiring':
case 'detecting':
return {
duration: 1500,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Alternate
};
case 'reasoning':
case 'generating':
return {
duration: 1000,
curve: Curve.Linear,
iterations: 1
};
case 'error':
return {
duration: 500,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Alternate
};
default:
return { duration: 0 };
}
}

build() {
Stack({ alignContent: Alignment.Bottom }) {
// 内容层
Column() {
this.contentBuilder()
}
.padding({ bottom: this.bottomAvoidHeight + 80 })

// 悬浮导航栏
Column() {
Stack() {
// 玻璃拟态背景
Column()
.width('100%')
.height('100%')
.backgroundBlurStyle(BlurStyle.REGULAR)
.opacity(this.navTransparency)
.backdropFilter($r('sys.blur.20'))

// 渐变光效层
Column()
.width('100%')
.height('100%')
.linearGradient({
direction: GradientDirection.Top,
colors: [
['rgba(255,255,255,0.15)', 0.0],
['rgba(255,255,255,0.05)', 1.0]
]
})
}
.width('100%')
.height('100%')
.borderRadius(24)
.shadow({
radius: 20,
color: 'rgba(0,0,0,0.2)',
offsetX: 0,
offsetY: 4
})

// 导航项
Row() {
ForEach(this.navItems, (item: MedicalNavItem, index: number) => {
Column() {
Stack() {
Image(item.icon)
.width(24)
.height(24)
.fillColor(this.currentIndex === index ? RiskColors[this.getHighestRiskLevel()] : '#666666')

// 病灶数量徽章
if (this.getTotalLesionCount() > 0) {
Column() {
Text(`${this.getTotalLesionCount()}`)
.fontSize(10)
.fontColor('#FFFFFF')
}
.width(18)
.height(18)
.backgroundColor(RiskColors[this.getHighestRiskLevel()])
.borderRadius(9)
.position({ x: 16, y: 8 })
.shadow({
radius: 6,
color: RiskColors[this.getHighestRiskLevel()],
offsetX: 0,
offsetY: 0
})
}

// 智能体状态徽章
if (this.getAgentStateForNavItem(item) !== 'idle') {
Column()
.width(8)
.height(8)
.backgroundColor(this.getStateBadgeColor(this.getAgentStateForNavItem(item)))
.borderRadius(4)
.position({ x: 20, y: 4 })
.shadow({
radius: 4,
color: this.getStateBadgeColor(this.getAgentStateForNavItem(item)),
offsetX: 0,
offsetY: 0
})
.animation(this.getStateBadgeAnimation(this.getAgentStateForNavItem(item)))
}
}
.width(40)
.height(40)

Text(item.label)
.fontSize(11)
.fontColor(this.currentIndex === index ? RiskColors[this.getHighestRiskLevel()] : '#999999')
.margin({ top: 4 })
}
.layoutWeight(1)
.onClick(() => {
this.currentIndex = index;
this.triggerHapticFeedback();
})
})
}
.width('100%')
.height(80)
.padding({ left: 16, right: 16 })
.justifyContent(FlexAlign.SpaceAround)

// 透明度调节
if (this.isExpanded) {
Row() {
Text('透明度')
.fontSize(12)
.fontColor('#666666')
.margin({ right: 8 })

Slider({
value: this.navTransparency * 100,
min: 55,
max: 85,
step: 15,
style: SliderStyle.InSet
})
.width(120)
.onChange((value: number) => {
this.navTransparency = value / 100;
})

Text(`${Math.round(this.navTransparency * 100)}%`)
.fontSize(12)
.fontColor('#666666')
.margin({ left: 8 })
}
.width('100%')
.height(40)
.justifyContent(FlexAlign.Center)
.backgroundColor('rgba(255,255,255,0.5)')
.borderRadius({ topLeft: 12, topRight: 12 })
}
}
.width('92%')
.height(this.isExpanded ? 120 : 80)
.margin({ bottom: this.bottomAvoidHeight + 12, left: '4%', right: '4%' })
.animation({
duration: 300,
curve: Curve.Spring,
iterations: 1
})
.gesture(
LongPressGesture({ duration: 500 })
.onAction(() => {
this.isExpanded = !this.isExpanded;
})
)
}
.width('100%')
.height('100%')
}

@BuilderParam contentBuilder: () => void = this.defaultContentBuilder;

@Builder
defaultContentBuilder(): void {
Column() {
Text('内容区域')
.fontSize(16)
.fontColor('#999999')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}

private triggerHapticFeedback(): void {
try {
import('@kit.SensorServiceKit').then(sensor => {
sensor.vibrator.startVibration({
type: 'time',
duration: 50
}, { id: 0 });
});
} catch (error) {
console.error('Haptic feedback failed:', error);
}
}
}

5.5 影像视窗与病灶高亮(ImageViewer.ets)

代码亮点:实现具有病灶高亮功能的医学影像视窗。根据诊断结果,在影像上显示病灶标记框,高危病灶边框脉冲闪烁,中危病灶边框柔和呼吸,低危病灶边框轻微变色。

// entry/src/main/ets/components/ImageViewer.ets
import { RiskLevel, RiskColors } from './LesionLightEffect';

interface ImageSlice {
sliceIndex: number;
imageData: ArrayBuffer;
lesions: Array<{id: string, riskLevel: RiskLevel, boundingBox: {x: number, y: number, width: number, height: number}}>;
}

@Component
export struct ImageViewer {
@State currentSlice: number = 0;
@State slices: ImageSlice[] = [];
@State selectedLesion: string = '';
@State zoomLevel: number = 1.0;
@State windowWidth: number = 400;
@State windowCenter: number = 40;
@State currentRiskColor: string = '#2ECC71';
@State currentRiskIntensity: number = 0.4;

aboutToAppear(): void {
// 监听风险等级变化
AppStorage.setOrCreate('current_lesion_color', (color: string) => {
this.currentRiskColor = color;
});
AppStorage.setOrCreate('current_lesion_intensity', (intensity: number) => {
this.currentRiskIntensity = intensity;
});
}

private getLesionBorderColor(lesion: {riskLevel: RiskLevel}): string {
return RiskColors[lesion.riskLevel];
}

private getLesionBorderAnimation(lesion: {riskLevel: RiskLevel}): object {
if (lesion.riskLevel === RiskLevel.MALIGNANT) {
return {
duration: 500,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Alternate
};
}
if (lesion.riskLevel === RiskLevel.HIGH) {
return {
duration: 1500,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Alternate
};
}
return { duration: 0 };
}

build() {
Stack() {
// 影像背景
Column()
.width('100%')
.height('100%')
.backgroundColor('#0a0a0f')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

// DICOM影像渲染
Canvas()
.width('100%')
.height('100%')
.onReady((context: CanvasRenderingContext2D) => {
this.renderDICOM(context);
})

// 病灶标记层
ForEach(this.slices[this.currentSlice]?.lesions || [], (lesion: {id: string, riskLevel: RiskLevel, boundingBox: {x: number, y: number, width: number, height: number}}) => {
Stack() {
// 病灶边框
Column()
.width(lesion.boundingBox.width * this.zoomLevel)
.height(lesion.boundingBox.height * this.zoomLevel)
.backgroundColor('transparent')
.border({
width: lesion.id === this.selectedLesion ? 3 : 2,
color: this.getLesionBorderColor(lesion),
style: BorderStyle.Solid
})
.position({
x: lesion.boundingBox.x * this.zoomLevel,
y: lesion.boundingBox.y * this.zoomLevel
})
.animation(this.getLesionBorderAnimation(lesion))
.onClick(() => {
this.selectedLesion = lesion.id;
AppStorage.setOrCreate('selected_lesion_id', lesion.id);
})

// 病灶标签
if (lesion.id === this.selectedLesion) {
Column() {
Text(`${lesion.riskLevel}`)
.fontSize(10)
.fontColor('#FFFFFF')
.backgroundColor(RiskColors[lesion.riskLevel])
.borderRadius(4)
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
}
.position({
x: lesion.boundingBox.x * this.zoomLevel,
y: lesion.boundingBox.y * this.zoomLevel 20
})
}
}
})

// 窗宽窗位调节
Row() {
Column() {
Text('窗宽')
.fontSize(12)
.fontColor('#AAAAAA')
Slider({ value: this.windowWidth, min: 1, max: 4000, step: 10 })
.width(120)
.onChange((value: number) => {
this.windowWidth = value;
})
}

Column() {
Text('窗位')
.fontSize(12)
.fontColor('#AAAAAA')
Slider({ value: this.windowCenter, min: 1000, max: 1000, step: 10 })
.width(120)
.onChange((value: number) => {
this.windowCenter = value;
})
}
}
.position({ x: 20, y: 100 })

// 切片导航
Row() {
Button() {
Text('◀')
.fontSize(20)
.fontColor('#FFFFFF')
}
.type(ButtonType.Circle)
.backgroundColor('rgba(255,255,255,0.2)')
.width(40)
.height(40)
.onClick(() => {
if (this.currentSlice > 0) {
this.currentSlice;
}
})

Text(`${this.currentSlice + 1} / ${this.slices.length}`)
.fontSize(14)
.fontColor('#FFFFFF')
.margin({ left: 16, right: 16 })

Button() {
Text('▶')
.fontSize(20)
.fontColor('#FFFFFF')
}
.type(ButtonType.Circle)
.backgroundColor('rgba(255,255,255,0.2)')
.width(40)
.height(40)
.margin({ left: 8 })
.onClick(() => {
if (this.currentSlice < this.slices.length 1) {
this.currentSlice++;
}
})
}
.position({ x: '50%', y: '90%' })
.anchor('50%')
}
.width('100%')
.height('100%')
}

private renderDICOM(context: CanvasRenderingContext2D): void {
// DICOM影像渲染逻辑
const width = context.canvas.width;
const height = context.canvas.height;

// 模拟DICOM渲染
context.fillStyle = '#1a1a2e';
context.fillRect(0, 0, width, height);

// 绘制影像内容
const slice = this.slices[this.currentSlice];
if (slice) {
// 应用窗宽窗位
const minValue = this.windowCenter this.windowWidth / 2;
const maxValue = this.windowCenter + this.windowWidth / 2;

// 渲染影像数据
context.fillStyle = '#2a2a4e';
context.fillRect(width * 0.1, height * 0.1, width * 0.8, height * 0.8);
}
}
}

5.6 浮动病灶标注面板(LesionPanelWindow.ets)

代码亮点:实现可拖拽的浮动病灶标注面板,支持病灶信息展示、测量工具、3D重建预览。窗口激活时边缘发光增强,失活时自动降低光效强度,通过WindowManager与主窗口光效联动。

// entry/src/main/ets/windows/LesionPanelWindow.ets
import { window } from '@kit.ArkUI';
import { RiskLevel, RiskColors } from '../components/LesionLightEffect';

@Component
export struct LesionPanelWindow {
@State isActive: boolean = false;
@State selectedLesion: {id: string, type: string, riskLevel: RiskLevel, size: number, location: string} | null = null;
@State windowLightColor: string = '#2ECC71';
@State lightIntensity: number = 0.6;

private mainWindow: window.Window | null = null;

aboutToAppear(): void {
this.setupWindow();
// 监听主窗口光效变化
AppStorage.setOrCreate('current_lesion_color', (color: string) => {
this.windowLightColor = color;
});
// 监听选中病灶变化
AppStorage.setOrCreate('selected_lesion_id', (lesionId: string) => {
this.loadLesionDetail(lesionId);
});
}

private async setupWindow(): Promise<void> {
try {
this.mainWindow = await window.getLastWindow();
await this.mainWindow.setWindowBackgroundColor('#00000000');

// 设置窗口为浮动样式
await this.mainWindow.setWindowDecorVisible(false);
await this.mainWindow.resize(450, 700);

// 监听窗口焦点变化
this.mainWindow.on('windowFocusChange', (isFocused: boolean) => {
this.isActive = isFocused;
this.lightIntensity = isFocused ? 0.8 : 0.3;
});
} catch (error) {
console.error('Failed to setup lesion panel window:', error);
}
}

private loadLesionDetail(lesionId: string): void {
// 从全局状态加载病灶详情
const lesionDetail = AppStorage.get<<Record<string, unknown>>(`lesion_${lesionId}`);
if (lesionDetail) {
this.selectedLesion = {
id: lesionId,
type: lesionDetail.type as string,
riskLevel: lesionDetail.riskLevel as RiskLevel,
size: lesionDetail.size as number,
location: lesionDetail.location as string
};
}
}

build() {
Stack() {
// 窗口边框光效
Column()
.width('100%')
.height('100%')
.backgroundColor('transparent')
.border({
width: 2,
color: `${this.windowLightColor}${this.isActive ? 'FF' : '40'}`,
style: BorderStyle.Solid
})
.borderRadius(16)
.animation({
duration: 300,
curve: Curve.EaseInOut
})

// 内容层
Column() {
// 标题栏
Row() {
Text('🔍 病灶详情')
.fontSize(16)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)

if (this.selectedLesion) {
Text(this.selectedLesion.riskLevel)
.fontSize(12)
.fontColor('#FFFFFF')
.backgroundColor(RiskColors[this.selectedLesion.riskLevel])
.borderRadius(8)
.padding({ left: 8, right: 8, top: 4, bottom: 4 })
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(16)

// 病灶信息
if (this.selectedLesion) {
Column() {
Text(`病灶ID: ${this.selectedLesion.id}`)
.fontSize(14)
.fontColor('#FFFFFF')
.margin({ bottom: 8 })

Text(`类型: ${this.selectedLesion.type}`)
.fontSize(14)
.fontColor('#AAAAAA')
.margin({ bottom: 8 })

Text(`风险等级: ${this.selectedLesion.riskLevel}`)
.fontSize(14)
.fontColor(RiskColors[this.selectedLesion.riskLevel])
.margin({ bottom: 8 })

Text(`大小: ${this.selectedLesion.size} mm`)
.fontSize(14)
.fontColor('#AAAAAA')
.margin({ bottom: 8 })

Text(`位置: ${this.selectedLesion.location}`)
.fontSize(14)
.fontColor('#AAAAAA')
.margin({ bottom: 8 })
}
.width('100%')
.padding(16)
.backgroundColor('rgba(255,255,255,0.05)')
.borderRadius(12)
.margin({ bottom: 16 })
}

// 测量工具
Column() {
Text('📏 测量工具')
.fontSize(16)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
.margin({ bottom: 12 })

Row() {
Button() {
Text('长度')
.fontSize(12)
.fontColor('#FFFFFF')
}
.type(ButtonType.Capsule)
.backgroundColor('rgba(255,255,255,0.2)')
.height(28)
.onClick(() => { /* 长度测量 */ })

Button() {
Text('面积')
.fontSize(12)
.fontColor('#FFFFFF')
}
.type(ButtonType.Capsule)
.backgroundColor('rgba(255,255,255,0.2)')
.height(28)
.margin({ left: 8 })
.onClick(() => { /* 面积测量 */ })

Button() {
Text('体积')
.fontSize(12)
.fontColor('#FFFFFF')
}
.type(ButtonType.Capsule)
.backgroundColor('rgba(255,255,255,0.2)')
.height(28)
.margin({ left: 8 })
.onClick(() => { /* 体积测量 */ })
}
}
.width('100%')
.padding(16)
.backgroundColor('rgba(255,255,255,0.05)')
.borderRadius(12)
.margin({ bottom: 16 })

// 3D重建预览
Column() {
Text('🧊 3D重建预览')
.fontSize(16)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
.margin({ bottom: 12 })

Column()
.width('100%')
.height(150)
.backgroundColor('rgba(0,0,0,0.3)')
.borderRadius(8)
}
.width('100%')
.padding(16)
.backgroundColor('rgba(255,255,255,0.05)')
.borderRadius(12)
}
.width('100%')
.height('100%')
.padding(2) // 为边框光效留出空间
}
.width('100%')
.height('100%')
.backgroundColor('rgba(15,15,35,0.95)')
.borderRadius(16)
.shadow({
radius: 30,
color: `${this.windowLightColor}${this.isActive ? '40' : '15'}`,
offsetX: 0,
offsetY: 0
})
.animation({
duration: 300,
curve: Curve.EaseInOut
})
}
}

5.7 多窗口光效同步管理器(WindowLightSync.ets)

代码亮点:管理主窗口与浮动病灶标注面板、诊断报告窗口、参考病例窗口之间的光效联动。当病灶风险等级变化时,所有窗口同步切换主题色;当窗口焦点变化时,自动调整光效强度,形成层次分明的视觉体验。

// entry/src/main/ets/managers/WindowLightSync.ets
import { window } from '@kit.ArkUI';
import { RiskLevel, RiskColors } from '../components/LesionLightEffect';

// 窗口类型
export enum WindowType {
MAIN = 'main',
LESION_PANEL = 'lesion_panel',
DIAGNOSIS_REPORT = 'diagnosis_report',
REFERENCE_CASES = 'reference_cases'
}

// 光效主题
export interface LightTheme {
primaryColor: string;
secondaryColor: string;
intensity: number;
pulseSpeed: number;
glowRadius: number;
}

export class WindowLightSync {
private static instance: WindowLightSync;
private windows: Map<<WindowType, window.Window> = new Map();
private currentTheme: LightTheme = {
primaryColor: '#2ECC71',
secondaryColor: '#7EE8A0',
intensity: 0.6,
pulseSpeed: 2000,
glowRadius: 100
};
private focusWindow: WindowType = WindowType.MAIN;

private constructor() {}

static getInstance(): WindowLightSync {
if (!WindowLightSync.instance) {
WindowLightSync.instance = new WindowLightSync();
}
return WindowLightSync.instance;
}

registerWindow(type: WindowType, win: window.Window): void {
this.windows.set(type, win);

// 监听窗口焦点
win.on('windowFocusChange', (isFocused: boolean) => {
if (isFocused) {
this.focusWindow = type;
this.updateFocusLightEffect();
}
});
}

// 同步所有窗口光效
async syncLightEffect(theme: LightTheme): Promise<void> {
this.currentTheme = theme;

for (const [type, win] of this.windows.entries()) {
try {
const isFocused = type === this.focusWindow;
const intensity = isFocused ? theme.intensity : theme.intensity * 0.4;

// 设置窗口背景光效
await win.setWindowBackgroundColor(`#00000000`);

// 同步到AppStorage供各窗口组件读取
AppStorage.setOrCreate(`window_${type}_color`, theme.primaryColor);
AppStorage.setOrCreate(`window_${type}_intensity`, intensity);
AppStorage.setOrCreate(`window_${type}_focused`, isFocused);

} catch (error) {
console.error(`Failed to sync light effect for ${type}:`, error);
}
}
}

// 根据病灶风险等级更新主题
updateThemeByRiskLevel(level: RiskLevel): void {
const themes: Record<RiskLevel, LightTheme> = {
[RiskLevel.BENIGN]: {
primaryColor: RiskColors[RiskLevel.BENIGN],
secondaryColor: '#7EE8A0',
intensity: 0.4,
pulseSpeed: 4000,
glowRadius: 80
},
[RiskLevel.LOW]: {
primaryColor: RiskColors[RiskLevel.LOW],
secondaryColor: '#6BB6FF',
intensity: 0.5,
pulseSpeed: 3000,
glowRadius: 100
},
[RiskLevel.MEDIUM]: {
primaryColor: RiskColors[RiskLevel.MEDIUM],
secondaryColor: '#FFD93D',
intensity: 0.7,
pulseSpeed: 2000,
glowRadius: 120
},
[RiskLevel.HIGH]: {
primaryColor: RiskColors[RiskLevel.HIGH],
secondaryColor: '#FF8888',
intensity: 0.9,
pulseSpeed: 1000,
glowRadius: 150
},
[RiskLevel.MALIGNANT]: {
primaryColor: RiskColors[RiskLevel.MALIGNANT],
secondaryColor: '#FF4444',
intensity: 1.0,
pulseSpeed: 500,
glowRadius: 200
}
};

const theme = themes[level] || themes[RiskLevel.BENIGN];
this.syncLightEffect(theme);
}

private updateFocusLightEffect(): void {
// 重新应用当前主题,根据新的焦点窗口调整强度
this.syncLightEffect(this.currentTheme);
}

// 创建浮动窗口
async createFloatWindow(type: WindowType, width: number, height: number, x: number, y: number): Promise<<window.Window> {
try {
const floatWindow = await window.createWindow({
name: type,
windowType: window.WindowType.TYPE_FLOAT,
ctx: getContext()
});

await floatWindow.moveWindowTo(x, y);
await floatWindow.resize(width, height);
await floatWindow.setWindowBackgroundColor('#00000000');
await floatWindow.showWindow();

this.registerWindow(type, floatWindow);
return floatWindow;
} catch (error) {
console.error(`Failed to create float window ${type}:`, error);
throw error;
}
}
}

5.8 主页面集成(Index.ets)

// entry/src/main/ets/pages/Index.ets
import { ImageViewer } from '../components/ImageViewer';
import { DiagnosisAgentScheduler } from '../services/DiagnosisAgentScheduler';
import { WindowLightSync, WindowType } from '../managers/WindowLightSync';
import { LesionPanelWindow } from '../windows/LesionPanelWindow';

@Entry
@Component
struct Index {
private scheduler: DiagnosisAgentScheduler = DiagnosisAgentScheduler.getInstance();
private lightSync: WindowLightSync = WindowLightSync.getInstance();

aboutToAppear(): void {
this.initializeWindows();
this.initializeScheduler();
}

private async initializeScheduler(): Promise<void> {
await this.scheduler.initialize();
}

private async initializeWindows(): Promise<void> {
try {
const mainWindow = await window.getLastWindow();
this.lightSync.registerWindow(WindowType.MAIN, mainWindow);

// 创建浮动病灶标注面板(PC端特有)
if (deviceInfo.deviceType === '2in1' || deviceInfo.deviceType === 'tablet') {
await this.lightSync.createFloatWindow(
WindowType.LESION_PANEL,
450, 700,
100, 100
);
}
} catch (error) {
console.error('Failed to initialize windows:', error);
}
}

build() {
Stack() {
// 动态环境光背景
Column() {
Column()
.width(400)
.height(400)
.backgroundColor(AppStorage.get<string>('current_lesion_color') || '#2ECC71')
.blur(150)
.opacity(AppStorage.get<number>('current_lesion_intensity') || 0.4)
.position({ x: '50%', y: '30%' })
.anchor('50%')
.animation({
duration: 3000,
curve: Curve.EaseInOut,
iterations: 1,
playMode: PlayMode.Alternate
})
}
.width('100%')
.height('100%')
.backgroundColor('#0a0a0f')

// 内容层
Column() {
// 影像视窗
ImageViewer()
.layoutWeight(1)
}
.width('100%')
.height('100%')
}
.width('100%')
.height('100%')
}
}


六、关键技术总结

6.1 HMAF医疗影像诊断开发清单

技术点API/方法应用场景
智能体会话创建 hmaf.createAgentSession({ mode: MULTI_AGENT }) 多智能体协作诊断
意图解析 intents.createIntentEngine({ supportedDomains }) 医生诊断意图理解
任务分发 hmafSession.sendTask({ targetAgent, taskType }) 智能体间诊断任务调度
状态监听 AppStorage全局状态回调 跨组件诊断状态同步
分布式协同 enableDistributed: true 多设备诊断协作
3D UNet模型 lung_nano_unet 肺结节检测
知识图谱推理 knowledge_graph_reasoning 诊断推理
NLG报告生成 structured_report 报告生成

6.2 沉浸光感实现清单

技术点API/方法应用场景
系统材质效果 systemMaterialEffect: SystemMaterialEffect.IMMERSIVE HdsNavigation标题栏
背景模糊 backgroundBlurStyle(BlurStyle.REGULAR) 悬浮导航玻璃拟态
背景滤镜 backdropFilter($r('sys.blur.20')) 精细模糊控制
安全区扩展 expandSafeArea([SafeAreaType.SYSTEM], […]) 全屏沉浸布局
窗口沉浸 setWindowLayoutFullScreen(true) 无边框模式
光效动画 animation({ duration, iterations: -1 }) 呼吸灯背景
动态透明度 backgroundOpacity 焦点感知降级

6.3 病灶风险光效映射

风险等级光效颜色脉冲速度光晕半径视觉语义
良性 #2ECC71 4000ms 80px 柔和绿光,安全放心
低危 #4A90D9 3000ms 100px 平静蓝光,定期随访
中危 #F5A623 2000ms 120px 警示黄光,需要关注
高危 #FF6B6B 1000ms 150px 急促橙光,紧急处理
恶性 #FF0000 500ms 200px 警示红光,立即干预

6.4 PC端多窗口光效协同

  • 主窗口:全屏沉浸,病灶风险光效延伸至所有安全区边缘
  • 浮动病灶标注面板窗口:置顶、圆角、阴影,激活时边缘发光增强
  • 光效同步:通过WindowLightSync管理器实现跨窗口主题色联动
  • 焦点感知:窗口激活时边缘发光增强,失活时自动降低光效强度
  • 风险等级一致性:同一风险等级在所有窗口中保持相同色彩标识

七、调试与适配建议

  • HMAF会话管理:多智能体并发时注意会话资源释放,避免内存泄漏
  • 光效功耗优化:夜间模式降低光效刷新率,延长OLED屏幕寿命
  • 意图识别校准:不同影像模态需训练专用意图模型,提升解析准确率
  • 分布式设备测试:测试跨设备诊断协作时的网络延迟与状态同步
  • 无障碍支持:为视障医生提供语音播报诊断结果,光效切换时播放提示音
  • 隐私保护:患者影像数据本地处理,仅上传脱敏后的诊断日志
  • 医疗合规:遵循HIPAA/GDPR等医疗数据保护法规,确保数据安全

  • 八、总结与展望

    本文基于HarmonyOS 6(API 23)的悬浮导航、沉浸光感与HMAF智能体框架特性,完整实战了一款面向PC端的"医智助手"医疗影像AI智能体辅助诊断平台。核心创新点总结:

  • HMAF四层诊疗架构:基于Agent Framework Kit构建"影像采集-病灶检测-诊断推理-报告生成"四层智能体协作体系,实现影像采集→病灶检测→诊断推理→报告生成的完整诊疗闭环

  • 病灶风险光效系统:每个风险等级拥有专属光效标识(良性绿、低危蓝、中危黄、高危橙、恶性红),根据诊断结果动态切换脉冲节奏与光晕强度,实现"风险直觉感知"

  • 悬浮科室导航:底部悬浮页签不仅承载页面切换,更实时显示诊断任务状态徽章(采集中脉冲、检测中旋转、推理中呼吸)和病灶数量角标,玻璃拟态设计+三档透明度调节

  • PC级多窗口协作诊疗:主影像视窗 + 浮动病灶标注面板 + 浮动诊断报告窗口 + 浮动参考病例窗口的四层架构,通过WindowLightSync实现跨窗口光效联动与焦点感知

  • DICOM影像可视化:基于Canvas实现DICOM影像渲染、窗宽窗位调节、病灶标记框高亮、切片导航,支持病灶交互和3D重建预览

  • 未来扩展方向:

    • 接入分布式软总线4.0,实现PC主控+平板阅片+手机随访的多设备协作
    • AI智能体诊断市场:支持第三方开发者发布自定义诊断智能体,通过光效标识区分专科能力
    • 多模态融合诊断:整合CT、MRI、超声、病理等多模态数据,实现跨模态智能诊断
    • 联邦学习优化:隐私保护下的跨医院智能优化,共享模型参数不出域
    • 手术导航集成:结合AR技术,实现影像引导的精准手术导航

    转载自:https://blog.csdn.net/u014727709/article/details/161192790 欢迎 👍点赞✍评论⭐收藏,欢迎指正

    赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“医智助手“——医疗影像AI智能体辅助诊断平台
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址