在这里插入图片描述

每日一句正能量

你不必事事有用,不必时时优秀,也不必为证明所谓的价值而透支自己。
社会总要求人“产出”“优秀”“有意义”。但你的存在本身就有价值。休息、发呆、做“无用”的事,不是浪费——是在避免用过度燃烧来换取虚幻的肯定。

一、前言:当AI数据分析师常驻屏幕边缘

在数字化转型浪潮中,企业管理者和数据分析人员每天都要面对海量数据报表。传统的BI工具虽然功能强大,但操作复杂、学习曲线陡峭;移动端的数据查看往往只是PC端的简单移植,缺乏针对移动场景的深度优化。

HarmonyOS 6(API 23)带来的悬浮导航(Floating Navigation)沉浸光感(Immersive Lighting)能力,让我们有机会打造一个常驻屏幕边缘的AI数据分析师——它像一位专业的数据顾问,在你查看报表时静静悬浮在屏幕角落,当你发现数据异常时随时唤出深度分析;它通过光效感知数据状态,在关键指标超标时紧急提醒,在趋势向好时为你确认信心。

核心创新点:

  • 📊 智能指标悬浮窗:实时悬浮显示核心KPI,支持跨应用数据追踪
  • 💡 数据状态光感:通过设备边框光效颜色变化,反映业务健康度
  • 🤖 对话式智能体:支持自然语言查询,“今年Q2华东区销售额为什么下滑”
  • 🎯 异常预警沉浸:关键指标异常时,全边框红色脉冲+悬浮窗自动展开

二、应用场景设计

2.1 场景一:跨应用数据追踪

销售总监正在微信中与客户沟通,同时需要实时查看本月销售达成率。悬浮胶囊常驻屏幕边缘,显示当前达成率78%,无需切换应用即可掌握核心数据。

2.2 场景二:异常即时预警

库存周转天数突然超过警戒线,系统检测到异常后,设备边框立即泛起红色脉冲光效,悬浮窗自动展开显示异常详情和根因分析,管理者第一时间获知风险。

2.3 场景三:自然语言洞察

CEO在会议中想了解"上季度各产品线利润率对比",直接语音询问悬浮窗中的AI智能体,3秒内获得可视化图表和关键洞察,无需打开复杂BI系统。

三、技术架构

┌─────────────────────────────────────────────────────────────┐
│         HarmonyOS Data Visualization Cockpit Agent          │
├─────────────┬─────────────┬─────────────┬───────────────────┤
│  悬浮窗UI   │  沉浸光感   │  智能体引擎  │   数据接入模块     │
│  FloatUI    │  Lighting   │  AI Engine  │   DataConnector   │
├─────────────┴─────────────┴─────────────┴───────────────────┤
│                    数据分析上下文层(Data Context)              │
│   指标追踪 │ 异常检测 │ 趋势预测 │ 根因分析 │ 语音交互        │
├─────────────────────────────────────────────────────────────┤
│              HarmonyOS 6 (API 23) 系统服务层                 │
│   悬浮导航 │ 光感服务 │ 智能体框架 │ 分布式数据 │ 语音服务      │
└─────────────────────────────────────────────────────────────┘

四、核心代码实现

4.1 数据上下文感知引擎(DataContextEngine)

这是整个系统的"数据之眼",通过分布式数据服务实时接入企业数据源,分析关键指标状态。

// engine/DataContextEngine.ets
import { distributedData } from '@kit.DistributedServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';

export interface DataContext {
  currentMetrics: Metric[];           // 当前追踪的指标
  alertStatus: AlertLevel;           // 告警级别
  trendDirection: 'up' | 'down' | 'stable'; // 趋势方向
  anomalyScore: number;              // 异常分数 0-100
  lastUpdate: number;                // 最后更新时间
  dataSource: string;                // 数据来源
}

export enum AlertLevel {
  NORMAL = 'normal',         // 正常:绿色
  WARNING = 'warning',       // 警告:黄色
  CRITICAL = 'critical',    // 严重:红色
  INFO = 'info'              // 提示:蓝色
}

export interface Metric {
  id: string;
  name: string;
  value: number;
  unit: string;
  target?: number;          // 目标值
  threshold?: {             // 阈值配置
    warning: number;
    critical: number;
  };
  history: number[];        // 历史数据(最近30天)
  dimension: string;         // 维度(如:华东区、产品线A)
}

export class DataContextEngine {
  private dataConnector: distributedData.DataConnector | null = null;
  private metricConfigs: Map<string, MetricConfig> = new Map();
  private contextCallbacks: Array<(context: DataContext) => void> = [];
  private updateInterval: number = 0;
  private currentContext: DataContext | null = null;

  /**
   * 初始化数据上下文感知
   * 亮点:通过分布式数据服务接入企业数据源,支持跨设备数据同步
   */
  async init(config: DataSourceConfig): Promise<void> {
    try {
      // 创建分布式数据连接器
      this.dataConnector = await distributedData.createDataConnector({
        bundleName: config.bundleName,
        abilityName: config.abilityName,
        dataType: config.dataType || 'metric'
      });

      // 加载指标配置
      await this.loadMetricConfigs(config.metrics);

      // 注册数据变更监听
      this.dataConnector.on('dataChange', (event) => {
        this.handleDataChange(event);
      });

      // 启动定时刷新(每30秒)
      this.updateInterval = setInterval(() => this.refreshData(), 30000);

      // 首次数据加载
      await this.refreshData();

      console.info('[DataContext] 数据上下文感知引擎初始化完成');
    } catch (err) {
      console.error(`[DataContext] 初始化失败: ${JSON.stringify(err)}`);
    }
  }

  /**
   * 刷新数据并分析上下文
   */
  private async refreshData(): Promise<void> {
    if (!this.dataConnector) return;

    try {
      const metrics: Metric[] = [];
      
      // 并行获取所有配置的指标
      const promises = Array.from(this.metricConfigs.values()).map(async (config) => {
        const data = await this.dataConnector.query({
          metricId: config.id,
          timeRange: { start: Date.now() - 30 * 24 * 3600 * 1000, end: Date.now() }
        });
        
        return this.buildMetric(config, data);
      });

      const results = await Promise.all(promises);
      metrics.push(...results);

      // 分析告警状态
      const alertStatus = this.analyzeAlertStatus(metrics);
      
      // 分析趋势
      const trendDirection = this.analyzeTrend(metrics);
      
      // 计算异常分数
      const anomalyScore = this.calculateAnomalyScore(metrics);

      const context: DataContext = {
        currentMetrics: metrics,
        alertStatus,
        trendDirection,
        anomalyScore,
        lastUpdate: Date.now(),
        dataSource: 'enterprise_bi'
      };

      this.currentContext = context;
      
      // 通知订阅者
      this.contextCallbacks.forEach(cb => cb(context));

    } catch (err) {
      console.error(`[DataContext] 刷新数据失败: ${JSON.stringify(err)}`);
    }
  }

  /**
   * 构建指标对象
   */
  private buildMetric(config: MetricConfig, data: any[]): Metric {
    const values = data.map(d => d.value || 0);
    const current = values[values.length - 1] || 0;
    
    return {
      id: config.id,
      name: config.name,
      value: current,
      unit: config.unit,
      target: config.target,
      threshold: config.threshold,
      history: values.slice(-30), // 最近30个数据点
      dimension: config.dimension || 'all'
    };
  }

  /**
   * 分析告警状态
   * 策略:取所有指标中最严重的告警级别
   */
  private analyzeAlertStatus(metrics: Metric[]): AlertLevel {
    let maxAlert = AlertLevel.NORMAL;
    
    metrics.forEach(metric => {
      if (!metric.threshold) return;
      
      if (metric.value >= metric.threshold.critical) {
        maxAlert = AlertLevel.CRITICAL;
      } else if (metric.value >= metric.threshold.warning && maxAlert !== AlertLevel.CRITICAL) {
        maxAlert = AlertLevel.WARNING;
      }
    });
    
    return maxAlert;
  }

  /**
   * 分析趋势方向
   * 基于最近7天数据线性回归
   */
  private analyzeTrend(metrics: Metric[]): 'up' | 'down' | 'stable' {
    if (metrics.length === 0) return 'stable';
    
    const primaryMetric = metrics[0];
    const history = primaryMetric.history;
    
    if (history.length < 7) return 'stable';
    
    const recent = history.slice(-7);
    const first = recent[0];
    const last = recent[recent.length - 1];
    
    const change = (last - first) / first;
    
    if (change > 0.05) return 'up';
    if (change < -0.05) return 'down';
    return 'stable';
  }

  /**
   * 计算异常分数
   * 基于统计离群值检测(Z-Score)
   */
  private calculateAnomalyScore(metrics: Metric[]): number {
    let totalScore = 0;
    
    metrics.forEach(metric => {
      const history = metric.history;
      if (history.length < 10) return;
      
      const mean = history.reduce((a, b) => a + b, 0) / history.length;
      const std = Math.sqrt(history.reduce((sq, n) => sq + Math.pow(n - mean, 2), 0) / history.length);
      
      if (std === 0) return;
      
      const zScore = Math.abs((metric.value - mean) / std);
      const score = Math.min(zScore * 10, 100); // Z-Score映射到0-100
      
      totalScore = Math.max(totalScore, score);
    });
    
    return Math.round(totalScore);
  }

  /**
   * 处理数据变更事件
   */
  private handleDataChange(event: distributedData.DataChangeEvent): void {
    // 增量更新,只刷新变更的指标
    this.refreshData();
  }

  /**
   * 自然语言查询转换
   * 将用户问题转换为数据查询
   */
  async parseNaturalQuery(query: string): Promise<DataQuery> {
    // 使用端侧NLP模型解析查询意图
    const intent = await this.extractIntent(query);
    
    return {
      metrics: intent.metrics,
      dimensions: intent.dimensions,
      timeRange: intent.timeRange,
      aggregations: intent.aggregations,
      filters: intent.filters
    };
  }

  private async extractIntent(query: string): Promise<<QueryIntent> {
    // 简化的意图提取逻辑
    // 实际项目中应使用NLP模型
    const metrics: string[] = [];
    const dimensions: string[] = [];
    let timeRange = { start: Date.now() - 30 * 24 * 3600 * 1000, end: Date.now() };
    
    // 关键词匹配
    if (query.includes('销售额') || query.includes('收入')) metrics.push('revenue');
    if (query.includes('利润')) metrics.push('profit');
    if (query.includes('华东')) dimensions.push('east_china');
    if (query.includes('上季度')) {
      const now = new Date();
      const quarter = Math.floor(now.getMonth() / 3);
      const year = now.getFullYear();
      const startMonth = quarter * 3 - 3;
      timeRange = {
        start: new Date(year, startMonth, 1).getTime(),
        end: new Date(year, startMonth + 3, 0).getTime()
      };
    }
    
    return { metrics, dimensions, timeRange, aggregations: ['sum'], filters: [] };
  }

  onContextChange(callback: (context: DataContext) => void): void {
    this.contextCallbacks.push(callback);
  }

  getCurrentContext(): DataContext | null {
    return this.currentContext;
  }

  private async loadMetricConfigs(metrics: MetricConfig[]): Promise<void> {
    metrics.forEach(config => {
      this.metricConfigs.set(config.id, config);
    });
  }

  destroy(): void {
    clearInterval(this.updateInterval);
    this.dataConnector?.off('dataChange');
  }
}

// 类型定义补充
interface DataSourceConfig {
  bundleName: string;
  abilityName: string;
  dataType?: string;
  metrics: MetricConfig[];
}

interface MetricConfig {
  id: string;
  name: string;
  unit: string;
  target?: number;
  threshold?: { warning: number; critical: number };
  dimension?: string;
}

interface DataQuery {
  metrics: string[];
  dimensions: string[];
  timeRange: { start: number; end: number };
  aggregations: string[];
  filters: any[];
}

interface QueryIntent {
  metrics: string[];
  dimensions: string[];
  timeRange: { start: number; end: number };
  aggregations: string[];
  filters: any[];
}

4.2 沉浸光感数据状态反馈(DataLightingController)

光效是数据状态的"视觉化延伸",让管理者无需看屏幕就能感知业务健康度。

// lighting/DataLightingController.ets
import { lighting } from '@kit.ArkUI';
import { AlertLevel, DataContext } from '../engine/DataContextEngine';

export class DataLightingController {
  private currentAlert: AlertLevel = AlertLevel.NORMAL;
  private isInMeeting: boolean = false;

  /**
   * 初始化数据光感
   * 设计哲学:光效应成为管理者的"第六感",不干扰但能感知
   */
  async init(): Promise<void> {
    if (!lighting.isImmersiveLightSupported()) {
      console.warn('[DataLight] 设备不支持沉浸光感');
      return;
    }

    // 初始状态:柔和白色,表示就绪
    await this.setLightEffect({
      type: 'solid',
      position: 'bottom_edge',
      color: '#E0E0E0',
      brightness: 20,
      duration: 0
    });

    console.info('[DataLight] 数据光感初始化完成');
  }

  /**
   * 根据数据状态更新光效
   */
  async updateByContext(context: DataContext): Promise<void> {
    const alert = context.alertStatus;
    const trend = context.trendDirection;
    const anomaly = context.anomalyScore;

    // 会议模式:降低亮度,避免干扰
    const brightness = this.isInMeeting ? 30 : 50;

    // 严重异常:最高优先级
    if (alert === AlertLevel.CRITICAL || anomaly > 80) {
      await this.setCriticalAlert(brightness);
      return;
    }

    // 警告状态
    if (alert === AlertLevel.WARNING || anomaly > 50) {
      await this.setWarningAlert(brightness, trend);
      return;
    }

    // 正常状态:根据趋势显示不同光效
    await this.setNormalState(trend, brightness);
  }

  /**
   * 严重告警:红色脉冲,必须立即处理
   */
  private async setCriticalAlert(brightness: number): Promise<void> {
    this.currentAlert = AlertLevel.CRITICAL;
    
    await lighting.setImmersiveLight({
      type: 'flashing',
      position: 'all_edges',
      color: '#FF1744',
      brightness: brightness + 20,
      duration: 0,
      flashCount: -1,
      frequency: 800  // 快速脉冲
    });
  }

  /**
   * 警告状态:黄色波浪,需要注意
   */
  private async setWarningAlert(brightness: number, trend: string): Promise<void> {
    this.currentAlert = AlertLevel.WARNING;
    
    // 下降趋势警告更紧急
    const color = trend === 'down' ? '#FF9100' : '#FFD600';
    
    await lighting.setImmersiveLight({
      type: 'wave',
      position: 'all_edges',
      color: color,
      brightness: brightness,
      duration: 0,
      direction: trend === 'down' ? 'clockwise' : 'counter_clockwise',
      speed: 'medium'
    });
  }

  /**
   * 正常状态:根据趋势显示
   */
  private async setNormalState(trend: string, brightness: number): Promise<void> {
    this.currentAlert = AlertLevel.NORMAL;
    
    const effects: Record<string, lighting.LightEffect> = {
      'up': {
        type: 'breathing',
        position: 'bottom_edge',
        color: '#00E676',      // 翠绿:上升趋势
        brightness: brightness,
        duration: 0,
        frequency: 3000        // 慢呼吸,稳定感
      },
      'down': {
        type: 'breathing',
        position: 'bottom_edge',
        color: '#448AFF',      // 蓝:温和下降
        brightness: brightness,
        duration: 0,
        frequency: 4000        // 更慢,不焦虑
      },
      'stable': {
        type: 'solid',
        position: 'bottom_edge',
        color: '#E0E0E0',      // 灰白:稳定
        brightness: brightness - 10,
        duration: 0
      }
    };

    await lighting.setImmersiveLight(effects[trend]);
  }

  /**
   * 数据更新脉冲
   * 每次数据刷新时的轻微闪烁提示
   */
  async dataUpdatePulse(): Promise<void> {
    await lighting.setImmersiveLight({
      type: 'flashing',
      position: 'bottom_edge',
      color: '#FFFFFF',
      brightness: 30,
      duration: 300,
      flashCount: 1
    });
  }

  /**
   * 会议模式切换
   */
  async setMeetingMode(enabled: boolean): Promise<void> {
    this.isInMeeting = enabled;
    
    if (enabled) {
      // 会议模式:极简光效,仅底部边缘微光
      await lighting.setImmersiveLight({
        type: 'solid',
        position: 'bottom_edge',
        color: '#E0E0E0',
        brightness: 15,
        duration: 0
      });
    } else {
      // 恢复当前状态
      // 实际应恢复之前的状态...
    }
  }

  /**
   * 目标达成庆祝
   */
  async celebrateGoal(): Promise<void> {
    // 彩虹渐变效果
    const colors = ['#00E676', '#00B0FF', '#2979FF', '#7C4DFF', '#F50057', '#FF9100', '#FFD600'];
    
    for (const color of colors) {
      await lighting.setImmersiveLight({
        type: 'solid',
        position: 'all_edges',
        color: color,
        brightness: 50,
        duration: 150
      });
      await new Promise(resolve => setTimeout(resolve, 150));
    }
    
    // 恢复
    await this.setNormalState('up', 50);
  }

  async reset(): Promise<void> {
    await lighting.resetImmersiveLight();
  }
}

4.3 数据智能体引擎(DataAgentEngine)

这是系统的"数据大脑",负责理解业务问题、生成分析洞察、预测趋势。

// agent/DataAgentEngine.ets
import { ai } from '@kit.AiKit';
import { DataContext, Metric } from '../engine/DataContextEngine';

export interface DataInsight {
  type: 'trend' | 'anomaly' | 'comparison' | 'prediction' | 'recommendation';
  title: string;
  description: string;
  dataPoints: DataPoint[];
  confidence: number;
  actionItems?: string[];
}

export interface DataPoint {
  label: string;
  value: number;
  change?: number;
  trend?: 'up' | 'down' | 'stable';
}

export class DataAgentEngine {
  private agent: ai.AgentSession | null = null;
  private insightCache: Map<string, DataInsight[]> = new Map();

  /**
   * 初始化数据智能体
   * 加载业务分析知识库
   */
  async init(): Promise<void> {
    const model = await ai.createModel({
      modelId: 'harmonyos-data-analyst-v1',
      type: ai.ModelType.LOCAL,
      capabilities: ['data_analysis', 'trend_prediction', 'anomaly_detection']
    });

    this.agent = await ai.createAgentSession({
      model: model,
      systemPrompt: `你是一位资深的企业数据分析师,精通HarmonyOS数据分析场景。
        请基于提供的数据上下文,生成以下类型的洞察:
        1. 趋势分析:识别关键指标的变化趋势和驱动因素
        2. 异常诊断:定位数据异常的根本原因
        3. 对比分析:多维度数据对比的关键发现
        4. 预测建议:基于历史数据的短期预测
        5. 行动建议:具体的业务优化建议
        
        回答要求:
        - 用一句话总结核心发现
        - 提供3-5个支撑数据点
        - 给出可执行的行动建议
        - 使用中文回答`
    });

    console.info('[DataAgent] 数据智能体初始化完成');
  }

  /**
   * 生成数据洞察
   */
  async generateInsights(context: DataContext): Promise<DataInsight[]> {
    if (!this.agent) return [];

    const cacheKey = this.generateCacheKey(context);
    
    // 检查缓存
    if (this.insightCache.has(cacheKey)) {
      return this.insightCache.get(cacheKey)!;
    }

    const prompt = `请分析以下业务数据,生成关键洞察:
      
      指标概览:
      ${context.currentMetrics.map(m => 
        `- ${m.name}: ${m.value}${m.unit} (目标: ${m.target}${m.unit})`
      ).join('\n')}
      
      告警状态:${context.alertStatus}
      趋势方向:${context.trendDirection}
      异常分数:${context.anomalyScore}/100
      
      请生成3个最重要的洞察,包括趋势、异常和行动建议。`;

    const result = await this.agent.invoke({
      input: { question: prompt },
      options: { maxTokens: 1024, temperature: 0.3 }
    });

    const insights = this.parseInsights(result);
    
    // 缓存结果(5分钟)
    this.insightCache.set(cacheKey, insights);
    setTimeout(() => this.insightCache.delete(cacheKey), 300000);

    return insights;
  }

  /**
   * 自然语言查询
   */
  async naturalLanguageQuery(query: string, context: DataContext): Promise<<{
    answer: string;
    chartData?: any;
    sql?: string;
  }> {
    if (!this.agent) return { answer: '智能体未初始化' };

    const prompt = `用户问题:${query}
      
      当前数据上下文:
      ${context.currentMetrics.map(m => 
        `${m.name}: ${m.value}${m.unit}`
      ).join(', ')}
      
      请:
      1. 理解用户问题的意图
      2. 基于可用数据给出直接回答
      3. 如果数据不足,说明需要补充哪些数据
      4. 生成适合可视化的数据结构`;

    const result = await this.agent.invoke({
      input: { question: prompt },
      options: { maxTokens: 1024, temperature: 0.4 }
    });

    return {
      answer: result.data?.answer || '',
      chartData: result.data?.chartData,
      sql: result.data?.sql
    };
  }

  /**
   * 根因分析
   */
  async rootCauseAnalysis(metric: Metric, context: DataContext): Promise<<{
    causes: string[];
    impact: string[];
    recommendations: string[];
  }> {
    if (!this.agent) return { causes: [], impact: [], recommendations: [] };

    const prompt = `请对指标"${metric.name}"进行根因分析:
      
      当前值:${metric.value}${metric.unit}
      目标值:${metric.target}${metric.unit}
      历史趋势:${metric.history.slice(-10).join(' -> ')}
      维度:${metric.dimension}
      
      请分析:
      1. 导致当前状态的可能原因(至少3个)
      2. 如果不改善可能的影响
      3. 具体的改善建议(按优先级排序)`;

    const result = await this.agent.invoke({
      input: { question: prompt },
      options: { maxTokens: 1024, temperature: 0.3 }
    });

    return {
      causes: result.data?.causes || [],
      impact: result.data?.impact || [],
      recommendations: result.data?.recommendations || []
    };
  }

  /**
   * 预测分析
   */
  async predictTrend(metric: Metric, days: number = 7): Promise<<{
    predictions: number[];
    confidence: number;
    riskFactors: string[];
  }> {
    if (!this.agent) return { predictions: [], confidence: 0, riskFactors: [] };

    const prompt = `请基于历史数据预测未来${days}天的趋势:
      
      指标:${metric.name}
      历史数据(最近30天):${metric.history.join(', ')}
      
      请使用时间序列分析方法,给出:
      1. 未来${days}天的预测值
      2. 预测置信度
      3. 可能影响预测的风险因素`;

    const result = await this.agent.invoke({
      input: { question: prompt },
      options: { maxTokens: 512, temperature: 0.2 }
    });

    return {
      predictions: result.data?.predictions || [],
      confidence: result.data?.confidence || 0.5,
      riskFactors: result.data?.riskFactors || []
    };
  }

  private parseInsights(result: ai.ModelOutput): DataInsight[] {
    const data = result.data || {};
    return (data.insights || []).map((insight: any) => ({
      type: insight.type || 'trend',
      title: insight.title,
      description: insight.description,
      dataPoints: insight.dataPoints || [],
      confidence: insight.confidence || 0.5,
      actionItems: insight.actionItems
    }));
  }

  private generateCacheKey(context: DataContext): string {
    return `${context.alertStatus}_${context.trendDirection}_${context.anomalyScore}`;
  }

  destroy(): void {
    this.agent?.destroy();
  }
}

4.4 悬浮窗数据面板(DataFloatWindow)

// float/DataFloatWindow.ets
import { window } from '@kit.ArkUI';
import { emitter } from '@kit.BasicServicesKit';
import { DataContext, Metric } from '../engine/DataContextEngine';
import { DataInsight } from '../agent/DataAgentEngine';

export class DataFloatWindow {
  private floatWin: window.Window | null = null;
  private currentLevel: 'capsule' | 'panel' | 'full' = 'capsule';

  async create(): Promise<void> {
    const option: window.WindowOption = {
      name: 'DataCockpit',
      windowType: window.WindowType.TYPE_FLOAT,
      ctx: getContext(this)
    };

    this.floatWin = await window.createWindow(getContext(this), option);
    
    // 胶囊形态:显示核心KPI
    await this.floatWin.resize({ width: 120, height: 80 });
    await this.floatWin.moveWindowTo({ x: 900, y: 100 });
    await this.floatWin.setWindowTouchable(true);
    await this.floatWin.setUIContent('pages/DataCapsulePage');
    await this.floatWin.showWindow();
  }

  async expandToPanel(context: DataContext, insights: DataInsight[]): Promise<void> {
    if (!this.floatWin) return;
    
    this.currentLevel = 'panel';
    await this.floatWin.resize({ width: 440, height: 720 });
    await this.floatWin.moveWindowTo({ x: 560, y: 80 });
    await this.floatWin.setUIContent('pages/DataPanelPage');
    
    emitter.emit('showDataPanel', { data: { context, insights } });
  }

  async autoExpandOnAlert(context: DataContext): Promise<void> {
    if (!this.floatWin || this.currentLevel !== 'capsule') return;
    
    // 严重告警时自动展开
    if (context.alertStatus === 'critical' || context.anomalyScore > 80) {
      const insights = []; // 实际应获取洞察
      await this.expandToPanel(context, insights);
    }
  }

  async collapseToCapsule(): Promise<void> {
    if (!this.floatWin) return;
    
    this.currentLevel = 'capsule';
    await this.floatWin.resize({ width: 120, height: 80 });
    await this.floatWin.moveWindowTo({ x: 900, y: 100 });
    await this.floatWin.setUIContent('pages/DataCapsulePage');
  }

  destroy(): void {
    this.floatWin?.destroyWindow();
  }
}

4.5 数据胶囊页面(DataCapsulePage)

// pages/DataCapsulePage.ets
import { emitter } from '@kit.BasicServicesKit';
import { AlertLevel } from '../engine/DataContextEngine';

@Entry
@Component
struct DataCapsulePage {
  @State primaryMetric: string = '达成率';
  @State metricValue: number = 78;
  @State metricUnit: string = '%';
  @State alertStatus: AlertLevel = AlertLevel.NORMAL;
  @State trend: string = 'up';

  private alertColors: Record<<AlertLevel, string> = {
    [AlertLevel.NORMAL]: '#00E676',
    [AlertLevel.WARNING]: '#FFD600',
    [AlertLevel.CRITICAL]: '#FF1744',
    [AlertLevel.INFO]: '#2979FF'
  };

  aboutToAppear() {
    emitter.on('updateDataContext', (event) => {
      const ctx = event.data;
      if (ctx?.currentMetrics?.length > 0) {
        const primary = ctx.currentMetrics[0];
        this.primaryMetric = primary.name;
        this.metricValue = Math.round(primary.value);
        this.metricUnit = primary.unit;
      }
      this.alertStatus = ctx?.alertStatus || AlertLevel.NORMAL;
      this.trend = ctx?.trendDirection || 'stable';
    });
  }

  build() {
    Stack() {
      // 告警时外圈脉冲
      if (this.alertStatus === AlertLevel.CRITICAL || this.alertStatus === AlertLevel.WARNING) {
        Circle()
          .width(130)
          .height(130)
          .fill(this.alertColors[this.alertStatus])
          .opacity(0.2)
          .animation({
            duration: this.alertStatus === AlertLevel.CRITICAL ? 600 : 1200,
            iterations: -1,
            curve: Curve.EaseInOut,
            playMode: PlayMode.Alternate
          })
      }

      Column() {
        // 趋势箭头
        Text(this.trend === 'up' ? '↗' : this.trend === 'down' ? '↘' : '→')
          .fontSize(16)
          .fontColor(
            this.trend === 'up' ? '#00E676' : 
            this.trend === 'down' ? '#FF1744' : '#999'
          )

        // 核心数值
        Text(`${this.metricValue}`)
          .fontSize(28)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333')

        // 指标名称
        Text(this.primaryMetric)
          .fontSize(11)
          .fontColor('#666')

        // 单位
        Text(this.metricUnit)
          .fontSize(10)
          .fontColor('#999')
      }
      .width(120)
      .height(80)
      .justifyContent(FlexAlign.Center)
      .backgroundColor('rgba(255, 255, 255, 0.95)')
      .borderRadius(16)
      .shadow({ radius: 12, color: 'rgba(0,0,0,0.12)' })
      .gesture(
        GestureGroup(GestureMode.Sequence,
          TapGesture({ count: 1 })
            .onAction(() => emitter.emit('expandToPanel')),
          LongPressGesture({ duration: 800 })
            .onAction(() => emitter.emit('startVoiceQuery'))
        )
      )
    }
    .width('100%')
    .height('100%')
    .align(Alignment.Center)
  }
}

4.6 数据面板页面(DataPanelPage)

// pages/DataPanelPage.ets
import { emitter } from '@kit.BasicServicesKit';
import { DataContext, Metric, AlertLevel } from '../engine/DataContextEngine';
import { DataInsight } from '../agent/DataAgentEngine';

@Entry
@Component
struct DataPanelPage {
  @State context: DataContext | null = null;
  @State insights: DataInsight[] = [];
  @State selectedMetric: number = 0;
  @State isVoiceMode: boolean = false;

  aboutToAppear() {
    emitter.on('showDataPanel', (event) => {
      this.context = event.data?.context;
      this.insights = event.data?.insights || [];
    });
  }

  build() {
    Column() {
      // 顶部标题栏
      Row() {
        Text('📊 数据驾驶舱')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)
        
        Button(this.isVoiceMode ? '🎤' : '⌨️')
          .fontSize(14)
          .backgroundColor('#f0f0f0')
          .onClick(() => this.isVoiceMode = !this.isVoiceMode)
        
        Button('收起')
          .fontSize(12)
          .backgroundColor('#f0f0f0')
          .fontColor('#333')
          .margin({ left: 8 })
          .onClick(() => emitter.emit('collapseToCapsule'))
      }
      .width('100%')
      .padding(16)

      // 告警横幅
      if (this.context?.alertStatus === AlertLevel.CRITICAL) {
        Row() {
          Text('🔴 严重告警')
            .fontSize(14)
            .fontColor('#FFF')
            .fontWeight(FontWeight.Bold)
          Text('请立即关注异常指标')
            .fontSize(12)
            .fontColor('#FFCDD2')
            .margin({ left: 8 })
        }
        .width('100%')
        .padding(12)
        .backgroundColor('#FF1744')
        .borderRadius(8)
        .margin({ bottom: 12 })
      }

      // 指标卡片网格
      Grid() {
        ForEach(this.context?.currentMetrics || [], (metric: Metric, index: number) => {
          GridItem() {
            Column() {
              Text(metric.name)
                .fontSize(12)
                .fontColor('#666')
                .width('100%')
              
              Text(`${metric.value}${metric.unit}`)
                .fontSize(22)
                .fontWeight(FontWeight.Bold)
                .fontColor(this.getMetricColor(metric))
                .margin({ top: 4 })
              
              if (metric.target) {
                Text(`目标: ${metric.target}${metric.unit}`)
                  .fontSize(10)
                  .fontColor('#999')
                  .margin({ top: 2 })
              }
            }
            .padding(12)
            .backgroundColor('#fff')
            .borderRadius(8)
            .shadow({ radius: 4, color: 'rgba(0,0,0,0.05)' })
            .onClick(() => this.selectedMetric = index)
          }
        })
      }
      .columnsTemplate('1fr 1fr')
      .rowsGap(8)
      .columnsGap(8)
      .width('100%')
      .margin({ bottom: 12 })

      // 选中指标详情
      if (this.context?.currentMetrics && this.selectedMetric < this.context.currentMetrics.length) {
        this.MetricDetail(this.context.currentMetrics[this.selectedMetric])
      }

      // 智能洞察
      Text('💡 AI洞察')
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .width('100%')
        .margin({ top: 12, bottom: 8 })

      List() {
        ForEach(this.insights, (insight: DataInsight) => {
          ListItem() {
            Column() {
              Row() {
                Text(
                  insight.type === 'trend' ? '📈' :
                  insight.type === 'anomaly' ? '⚠️' :
                  insight.type === 'prediction' ? '🔮' : '💡'
                )
                  .fontSize(18)
                
                Text(insight.title)
                  .fontSize(14)
                  .fontWeight(FontWeight.Medium)
                  .layoutWeight(1)
                  .margin({ left: 8 })
                
                Text(`${Math.round(insight.confidence * 100)}%`)
                  .fontSize(11)
                  .fontColor(
                    insight.confidence > 0.8 ? '#00C853' :
                    insight.confidence > 0.6 ? '#FF9100' : '#FF1744'
                  )
              }
              .width('100%')

              Text(insight.description)
                .fontSize(12)
                .fontColor('#666')
                .width('100%')
                .margin({ top: 4 })

              if (insight.actionItems && insight.actionItems.length > 0) {
                Column() {
                  ForEach(insight.actionItems, (action: string) => {
                    Text(`${action}`)
                      .fontSize(11)
                      .fontColor('#2979FF')
                      .width('100%')
                      .margin({ top: 2 })
                  })
                }
                .margin({ top: 6 })
                .padding(8)
                .backgroundColor('#E3F2FD')
                .borderRadius(6)
              }
            }
            .padding(12)
            .backgroundColor('#fff')
            .borderRadius(8)
            .margin({ bottom: 8 })
            .shadow({ radius: 4, color: 'rgba(0,0,0,0.05)' })
          }
        })
      }
      .width('100%')
      .layoutWeight(1)

      // 语音查询输入
      if (this.isVoiceMode) {
        Row() {
          TextInput({ placeholder: '语音输入或打字询问...' })
            .fontSize(14)
            .layoutWeight(1)
            .onSubmit((value) => this.submitQuery(value))
          
          Button('🎤')
            .fontSize(14)
            .backgroundColor('#2979FF')
            .onClick(() => this.startVoiceInput())
        }
        .width('100%')
        .padding(12)
        .backgroundColor('#f8f9fa')
        .borderRadius(8)
        .margin({ top: 8 })
      }
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .backgroundColor('#FFF')
    .borderRadius(16)
  }

  @Builder
  MetricDetail(metric: Metric) {
    Column() {
      Text('📈 趋势分析')
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .width('100%')

      // 简化趋势图
      Row() {
        ForEach(metric.history.slice(-10), (value: number, index: number) => {
          Column() {
            Column()
              .width(4)
              .height(this.normalizeHeight(value, metric.history))
              .backgroundColor(
                index === metric.history.length - 1 ? '#2979FF' :
                value > (metric.target || Infinity) ? '#00E676' : '#FFD600'
              )
              .borderRadius(2)
          }
          .layoutWeight(1)
          .alignItems(HorizontalAlign.Center)
        })
      }
      .width('100%')
      .height(80)
      .margin({ top: 8 })
      .padding({ top: 10, bottom: 10 })

      // 根因分析按钮
      Button('🔍 根因分析')
        .fontSize(12)
        .backgroundColor('#F3E5F5')
        .fontColor('#7B1FA2')
        .width('100%')
        .margin({ top: 8 })
        .onClick(() => this.analyzeRootCause(metric))
    }
    .padding(12)
    .backgroundColor('#f8f9fa')
    .borderRadius(8)
    .margin({ bottom: 12 })
  }

  private getMetricColor(metric: Metric): string {
    if (!metric.threshold) return '#333';
    if (metric.value >= metric.threshold.critical) return '#FF1744';
    if (metric.value >= metric.threshold.warning) return '#FFD600';
    return '#00E676';
  }

  private normalizeHeight(value: number, history: number[]): number {
    const max = Math.max(...history);
    const min = Math.min(...history);
    const range = max - min || 1;
    return 20 + ((value - min) / range) * 50;
  }

  private submitQuery(query: string) {
    emitter.emit('naturalLanguageQuery', { data: { query } });
  }

  private startVoiceInput() {
    emitter.emit('startVoiceQuery');
  }

  private analyzeRootCause(metric: Metric) {
    emitter.emit('rootCauseAnalysis', { data: { metric } });
  }
}

4.7 主入口与系统集成(Index.ets)

// Index.ets
import { DataContextEngine, AlertLevel } from './engine/DataContextEngine';
import { DataLightingController } from './lighting/DataLightingController';
import { DataFloatWindow } from './float/DataFloatWindow';
import { DataAgentEngine } from './agent/DataAgentEngine';
import { emitter } from '@kit.BasicServicesKit';

@Entry
@Component
struct DataCockpitApp {
  private dataEngine: DataContextEngine = new DataContextEngine();
  private lightController: DataLightingController = new DataLightingController();
  private floatWindow: DataFloatWindow = new DataFloatWindow();
  private agentEngine: DataAgentEngine = new DataAgentEngine();

  aboutToAppear() {
    this.initSystem();
  }

  aboutToDisappear() {
    this.dataEngine.destroy();
    this.lightController.reset();
    this.floatWindow.destroy();
    this.agentEngine.destroy();
  }

  async initSystem() {
    // 1. 初始化沉浸光感
    await this.lightController.init();

    // 2. 初始化悬浮窗
    await this.floatWindow.create();

    // 3. 初始化智能体引擎
    await this.agentEngine.init();

    // 4. 初始化数据引擎
    await this.dataEngine.init({
      bundleName: 'com.enterprise.bi',
      abilityName: 'DataServiceAbility',
      metrics: [
        {
          id: 'revenue',
          name: '销售额',
          unit: '万',
          target: 1000,
          threshold: { warning: 800, critical: 600 }
        },
        {
          id: 'profit_rate',
          name: '利润率',
          unit: '%',
          target: 25,
          threshold: { warning: 20, critical: 15 }
        },
        {
          id: 'inventory_days',
          name: '库存周转',
          unit: '天',
          target: 30,
          threshold: { warning: 45, critical: 60 }
        }
      ]
    });

    // 当数据上下文变化时,更新光效和悬浮窗
    this.dataEngine.onContextChange(async (context) => {
      // 更新光效
      await this.lightController.updateByContext(context);
      
      // 严重告警自动展开
      if (context.alertStatus === AlertLevel.CRITICAL) {
        const insights = await this.agentEngine.generateInsights(context);
        await this.floatWindow.autoExpandOnAlert(context);
      }
      
      // 更新悬浮窗
      emitter.emit('updateDataContext', { data: context });
    });

    // 5. 设置事件监听
    this.setupEventListeners();
  }

  private setupEventListeners() {
    emitter.on('expandToPanel', async () => {
      const context = this.dataEngine.getCurrentContext();
      if (context) {
        const insights = await this.agentEngine.generateInsights(context);
        await this.floatWindow.expandToPanel(context, insights);
      }
    });

    emitter.on('collapseToCapsule', async () => {
      await this.floatWindow.collapseToCapsule();
    });

    emitter.on('naturalLanguageQuery', async (event) => {
      const query = event.data?.query;
      const context = this.dataEngine.getCurrentContext();
      if (query && context) {
        const result = await this.agentEngine.naturalLanguageQuery(query, context);
        // 显示结果...
      }
    });

    emitter.on('rootCauseAnalysis', async (event) => {
      const metric = event.data?.metric;
      const context = this.dataEngine.getCurrentContext();
      if (metric && context) {
        const result = await this.agentEngine.rootCauseAnalysis(metric, context);
        // 显示根因分析结果...
      }
    });

    emitter.on('startVoiceQuery', () => {
      // 启动语音识别...
    });
  }

  build() {
    Column() {
      Text('📊 HarmonyOS数据驾驶舱')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 8 })
      
      Text('AI智能体正在监控业务数据...')
        .fontSize(14)
        .fontColor('#666')
      
      Text('悬浮窗常驻显示,严重告警自动提醒')
        .fontSize(12)
        .fontColor('#999')
        .margin({ top: 4 })
        .textAlign(TextAlign.Center)

      // 数据概览
      Column() {
        Text('📈 今日业务概览')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .margin({ bottom: 12 })

        Row() {
          Column() {
            Text('¥892万')
              .fontSize(28)
              .fontWeight(FontWeight.Bold)
              .fontColor('#2979FF')
            Text('销售额')
              .fontSize(12)
              .fontColor('#999')
          }
          .layoutWeight(1)

          Column() {
            Text('22.5%')
              .fontSize(28)
              .fontWeight(FontWeight.Bold)
              .fontColor('#00C853')
            Text('利润率')
              .fontSize(12)
              .fontColor('#999')
          }
          .layoutWeight(1)

          Column() {
            Text('38天')
              .fontSize(28)
              .fontWeight(FontWeight.Bold)
              .fontColor('#FF9100')
            Text('库存周转')
              .fontSize(12)
              .fontColor('#999')
          }
          .layoutWeight(1)
        }
        .width('100%')
      }
      .width('90%')
      .padding(20)
      .backgroundColor('#f8f9fa')
      .borderRadius(12)
      .margin({ top: 32 })

      // 当前状态
      Row() {
        Text('系统状态:')
          .fontSize(14)
          .fontColor('#666')
        
        Text('🟢 正常运行')
          .fontSize(14)
          .fontColor('#00C853')
          .fontWeight(FontWeight.Medium)
      }
      .margin({ top: 20 })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

五、配置文件

// module.json5
{
  "module": {
    "name": "DataCockpit",
    "type": "entry",
    "description": "鸿蒙智能体数据可视化驾驶舱",
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone", "tablet", "2in1"],
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "数据驾驶舱主入口",
        "icon": "$media:layered_image",
        "label": "AI数据驾驶舱",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ]
      }
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.SYSTEM_FLOAT_WINDOW",
        "reason": "需要悬浮窗权限以常驻显示核心指标"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "需要分布式数据同步权限以接入企业数据源"
      },
      {
        "name": "ohos.permission.INTERNET",
        "reason": "连接云端智能体服务"
      },
      {
        "name": "ohos.permission.ACCESS_AI_MODEL",
        "reason": "使用端侧AI模型进行数据分析"
      },
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "支持语音查询交互"
      }
    ]
  }
}

六、效果展示与使用场景

6.1 典型业务场景

场景A:日常监控

销售总监正在查看邮件,屏幕边缘的悬浮胶囊显示"达成率78%"。设备底部边缘泛着柔和的蓝色呼吸光,表示趋势稳定。无需切换应用,随时掌握业务脉搏。

场景B:异常预警

库存周转天数突然达到52天,超过警戒线。设备四周边框立即泛起红色快速脉冲光效,悬浮窗自动展开显示异常详情。AI智能体分析根因:“华东区仓库积压严重,建议启动促销清仓”。

场景C:会议洞察

CEO在季度review会议中,语音询问"为什么Q2华东区利润率下滑"。悬浮窗AI智能体3秒内给出回答:“主要受原材料涨价影响,建议调整供应商结构或优化产品组合”,并附上可视化对比图表。

6.2 光效语义设计

业务状态 光效表现 管理提示
正常上升 底部翠绿慢呼吸 业务健康,保持当前策略
正常下降 底部蓝慢呼吸 温和下降,关注即可
警告状态 全边框琥珀波浪 需要关注,建议查看详情
严重告警 全边框红色快闪 必须立即处理
数据更新 底部白色微闪 数据已刷新
目标达成 彩虹渐变庆祝 值得庆祝,团队激励

七、性能与隐私优化

7.1 数据策略

// 数据刷新策略
const dataStrategy = {
  // 刷新频率:根据告警级别动态调整
  refreshInterval: {
    normal: 30000,    // 正常:30秒
    warning: 15000,   // 警告:15秒
    critical: 5000    // 严重:5秒
  },
  
  // 数据压缩:仅传输变化的数据
  deltaSync: {
    enabled: true,
    compression: 'gzip'
  },
  
  // 本地缓存:减少网络请求
  cache: {
    enabled: true,
    ttl: 60000,       // 1分钟
    maxSize: '10MB'
  }
};

7.2 隐私保护

  • 数据脱敏:敏感指标(如利润)在悬浮窗显示时脱敏
  • 权限控制:不同角色看到不同的指标集合
  • 本地优先:分析计算优先在端侧完成,原始数据不出设备

八、总结与展望

本文展示了如何基于HarmonyOS 6(API 23)的悬浮导航沉浸光感能力,构建一个鸿蒙智能体驱动的沉浸式数据可视化驾驶舱。与传统BI工具不同,它具备三个核心创新:

  1. 常驻感知:悬浮窗常驻屏幕边缘,数据状态随时可见
  2. 光效预警:通过设备边框光效实现"无屏感知"的业务监控
  3. 对话式洞察:自然语言交互,降低数据分析门槛

未来演进方向:

  • 多屏协同:手机悬浮窗 + 平板详细报表 + PC深度分析
  • 预测性告警:基于AI预测提前24小时预警潜在风险
  • 自动化决策:简单场景下AI直接给出决策建议并执行
  • 行业模板:零售、制造、金融等垂直行业数据模板

HarmonyOS 6的智能体框架和系统级交互创新,正在让"AI数据分析师常驻身边"成为现实。对于企业管理者而言,这不仅是一个工具,更是一位24小时在线、懂业务、懂数据、懂决策的智能伙伴。


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

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐