在这里插入图片描述

每日一句正能量

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

前言

摘要:医疗影像领域存在海量数据处理压力,传统人工阅片存在效率低、误诊率高等问题。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管理器实现跨窗口主题色联动
  • 焦点感知:窗口激活时边缘发光增强,失活时自动降低光效强度
  • 风险等级一致性:同一风险等级在所有窗口中保持相同色彩标识

七、调试与适配建议

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

八、总结与展望

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

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

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

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

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

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

未来扩展方向

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

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐