HarmonyOS 6 悬浮导航 + 沉浸光感:打造鸿蒙智能体驱动的沉浸式数据可视化驾驶舱
在数字化转型浪潮中,企业管理者和数据分析人员每天都要面对海量数据报表。传统的BI工具虽然功能强大,但操作复杂、学习曲线陡峭;移动端的数据查看往往只是PC端的简单移植,缺乏针对移动场景的深度优化。HarmonyOS 6(API 23)带来的悬浮导航(Floating Navigation)和沉浸光感(Immersive Lighting)能力,让我们有机会打造一个常驻屏幕边缘的AI数据分析师——它像

每日一句正能量
你不必事事有用,不必时时优秀,也不必为证明所谓的价值而透支自己。
社会总要求人“产出”“优秀”“有意义”。但你的存在本身就有价值。休息、发呆、做“无用”的事,不是浪费——是在避免用过度燃烧来换取虚幻的肯定。
一、前言:当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工具不同,它具备三个核心创新:
- 常驻感知:悬浮窗常驻屏幕边缘,数据状态随时可见
- 光效预警:通过设备边框光效实现"无屏感知"的业务监控
- 对话式洞察:自然语言交互,降低数据分析门槛
未来演进方向:
- 多屏协同:手机悬浮窗 + 平板详细报表 + PC深度分析
- 预测性告警:基于AI预测提前24小时预警潜在风险
- 自动化决策:简单场景下AI直接给出决策建议并执行
- 行业模板:零售、制造、金融等垂直行业数据模板
HarmonyOS 6的智能体框架和系统级交互创新,正在让"AI数据分析师常驻身边"成为现实。对于企业管理者而言,这不仅是一个工具,更是一位24小时在线、懂业务、懂数据、懂决策的智能伙伴。
转载自:https://blog.csdn.net/u014727709/article/details/161367467
欢迎 👍点赞✍评论⭐收藏,欢迎指正
更多推荐


所有评论(0)