趋势分析模块 Cordova 与 OpenHarmony 混合开发实战
摘要:趋势分析模块通过Cordova与OpenHarmony结合,提供喝茶消费趋势的可视化分析。用户可选择日/周/月周期,查看消费金额、喝茶次数等指标。流程包括数据准备(数据库聚合)、趋势计算(移动平均平滑)和可视化展示(折线图+文字分析)。前端实现包含动态图表渲染和趋势说明生成,支持跨平台事件日志记录。模块通过简单ASCII图表直观展示趋势变化,并自动生成趋势解读文本。

📌 概述
趋势分析模块提供了喝茶消费趋势的分析功能。该模块集成了 Cordova 框架与 OpenHarmony 原生能力,实现了高效的时间序列数据处理和趋势可视化。用户可以查看不同时间周期(日、周、月)的消费趋势、喝茶频率趋势等。模块支持趋势预测和对比分析。
🔗 完整流程
第一步:时间序列数据准备
应用会从数据库中加载指定时间范围的所有喝茶记录。按时间周期(日、周、月)进行聚合,生成时间序列数据。这个过程在原生层进行,确保性能。
第二步:趋势计算与平滑
应用会对时间序列数据进行处理,计算趋势线。使用移动平均等算法平滑数据,消除噪声。计算趋势的上升或下降方向。
第三步:趋势展示与分析
趋势以折线图的形式展示。用户可以查看消费金额趋势、喝茶次数趋势等。应用提供了趋势分析的文字说明,帮助用户理解趋势。
🔧 Web 代码实现
HTML 趋势分析页面
<div id="trend-analysis-page" class="page">
<div class="page-header">
<h1>趋势分析</h1>
</div>
<div class="trend-controls">
<select id="trend-period" onchange="loadTrendData()">
<option value="daily">日趋势</option>
<option value="weekly">周趋势</option>
<option value="monthly">月趋势</option>
</select>
<select id="trend-metric" onchange="loadTrendData()">
<option value="expense">消费金额</option>
<option value="count">喝茶次数</option>
<option value="rating">平均评分</option>
</select>
<button class="btn btn-primary" onclick="loadTrendData()">刷新</button>
</div>
<div id="trend-chart" class="trend-chart">
<!-- 趋势图表动态生成 -->
</div>
<div class="trend-analysis">
<h2>趋势分析</h2>
<div id="trend-summary" class="trend-summary">
<!-- 趋势分析文字说明 -->
</div>
</div>
</div>
趋势分析页面包含周期和指标选择、趋势图表和分析说明。
趋势分析逻辑
async function loadTrendData() {
const period = document.getElementById('trend-period').value;
const metric = document.getElementById('trend-metric').value;
try {
// 获取所有记录
const records = await db.getAllRecords();
// 按周期聚合数据
const trendData = aggregateTrendData(records, period, metric);
// 渲染趋势图表
renderTrendChart(trendData, metric);
// 生成趋势分析
const analysis = generateTrendAnalysis(trendData, metric);
document.getElementById('trend-summary').innerHTML = analysis;
if (window.cordova) {
cordova.exec(
null, null,
'TeaLogger',
'logEvent',
['trend_loaded', { period: period, metric: metric }]
);
}
} catch (error) {
console.error('Failed to load trend data:', error);
showToast('加载趋势数据失败', 'error');
}
}
function aggregateTrendData(records, period, metric) {
const data = {};
records.forEach(record => {
const date = new Date(record.createdAt);
let key;
if (period === 'daily') {
key = date.toISOString().split('T')[0];
} else if (period === 'weekly') {
const weekStart = new Date(date);
weekStart.setDate(date.getDate() - date.getDay());
key = weekStart.toISOString().split('T')[0];
} else {
key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
}
if (!data[key]) {
data[key] = { count: 0, expense: 0, totalRating: 0 };
}
data[key].count++;
data[key].expense += record.price || 0;
data[key].totalRating += record.rating || 0;
});
// 计算平均评分
Object.values(data).forEach(d => {
d.avgRating = d.count > 0 ? d.totalRating / d.count : 0;
});
return data;
}
function renderTrendChart(trendData, metric) {
const container = document.getElementById('trend-chart');
container.innerHTML = '';
const dates = Object.keys(trendData).sort();
const values = dates.map(date => {
switch(metric) {
case 'expense':
return trendData[date].expense;
case 'count':
return trendData[date].count;
case 'rating':
return trendData[date].avgRating;
default:
return 0;
}
});
// 简单的ASCII图表
const maxValue = Math.max(...values);
const chartHeight = 10;
let chart = '<pre>';
for (let i = chartHeight; i > 0; i--) {
let line = '';
values.forEach(v => {
const height = Math.round((v / maxValue) * chartHeight);
line += height >= i ? '█ ' : ' ';
});
chart += line + '\n';
}
chart += '</pre>';
container.innerHTML = chart;
}
function generateTrendAnalysis(trendData, metric) {
const dates = Object.keys(trendData).sort();
const values = dates.map(date => {
switch(metric) {
case 'expense':
return trendData[date].expense;
case 'count':
return trendData[date].count;
case 'rating':
return trendData[date].avgRating;
default:
return 0;
}
});
if (values.length < 2) {
return '<p>数据不足,无法进行趋势分析</p>';
}
const firstValue = values[0];
const lastValue = values[values.length - 1];
const change = lastValue - firstValue;
const changePercent = ((change / firstValue) * 100).toFixed(1);
const trend = change > 0 ? '上升' : '下降';
const metricName = metric === 'expense' ? '消费金额' : metric === 'count' ? '喝茶次数' : '平均评分';
return `
<p>最近${metricName}呈${trend}趋势,变化幅度为${Math.abs(changePercent)}%。</p>
<p>初始值: ${firstValue.toFixed(2)}, 最终值: ${lastValue.toFixed(2)}</p>
`;
}
这段代码实现了趋势分析功能。loadTrendData() 加载趋势数据。aggregateTrendData() 按周期聚合数据。renderTrendChart() 显示趋势图表。generateTrendAnalysis() 生成趋势分析说明。
🔌 OpenHarmony 原生代码
时间序列处理
// entry/src/main/ets/plugins/TimeSeriesProcessor.ets
export class TimeSeriesProcessor {
static aggregateByPeriod(records: TeaRecord[], period: string): Map<string, AggregatedData> {
const data = new Map<string, AggregatedData>();
records.forEach(record => {
const date = new Date(record.createdAt);
let key: string;
if (period === 'daily') {
key = this.formatDate(date);
} else if (period === 'weekly') {
const weekStart = new Date(date);
weekStart.setDate(date.getDate() - date.getDay());
key = this.formatDate(weekStart);
} else {
key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
}
if (!data.has(key)) {
data.set(key, { count: 0, expense: 0, totalRating: 0 });
}
const agg = data.get(key);
if (agg) {
agg.count++;
agg.expense += record.price;
agg.totalRating += record.rating;
}
});
return data;
}
static calculateMovingAverage(values: number[], windowSize: number): number[] {
const result: number[] = [];
for (let i = 0; i < values.length; i++) {
const start = Math.max(0, i - Math.floor(windowSize / 2));
const end = Math.min(values.length, i + Math.ceil(windowSize / 2));
const window = values.slice(start, end);
const avg = window.reduce((a, b) => a + b, 0) / window.length;
result.push(avg);
}
return result;
}
private static formatDate(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
}
interface AggregatedData {
count: number;
expense: number;
totalRating: number;
}
interface TeaRecord {
createdAt: string;
price: number;
rating: number;
}
这个类处理时间序列数据。aggregateByPeriod() 按周期聚合数据。calculateMovingAverage() 计算移动平均以平滑数据。
📝 总结
趋势分析模块展示了如何在 Cordova 框架中实现趋势分析功能。通过 Web 层的用户界面和交互,结合原生层的高效时间序列处理,为用户提供了直观的趋势展示和分析。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)