鸿蒙PC用Electron框架实现油耗计算器技术详解
·
欢迎加入开源鸿蒙PC社区:
https://harmonypc.csdn.net/
atomgit仓库地址: https://atomgit.com/m0_66062719/youhaojisuanqi



1. 项目概述
1.1 背景介绍
随着汽车保有量的增加,车辆油耗管理成为车主日常关注的重要事项。一个专业的油耗计算器可以帮助用户记录加油信息、自动计算油耗和费用,并提供统计分析和趋势图表,帮助用户更好地了解和管理车辆燃油消耗情况。
本文详细介绍如何在鸿蒙PC平台上,基于Electron框架实现一个功能完善的油耗计算器应用。
1.2 技术选型
| 维度 | 技术 | 版本 | 选型理由 |
|---|---|---|---|
| 框架 | Electron | 18.x | 跨平台桌面应用框架,支持Web技术栈 |
| 渲染引擎 | Chromium | 95+ | 高性能HTML/CSS渲染 |
| 语言 | JavaScript | ES6+ | 异步编程友好,适合数据处理 |
| UI技术 | HTML5 + CSS3 | - | 现代化界面设计和动画效果 |
| 存储 | localStorage | - | 轻量级本地数据持久化 |
1.3 功能特性
本项目实现了一个功能丰富的油耗计算器,主要特性包括:
- 加油记录管理:添加、编辑、删除加油记录
- 实时油耗计算:自动计算油耗、费用等指标
- 统计分析:总加油次数、累计加油量、累计行驶、平均油耗
- 趋势图表:柱状图展示最近12条记录的油耗趋势
- 数据持久化:使用localStorage本地存储
2. 架构设计
2.1 整体架构
┌─────────────────────────────────────────────────────┐
│ Electron主进程 │
├─────────────────────────────────────────────────────┤
│ main.js │
│ ├── 创建BrowserWindow │
│ ├── 加载HTML页面 │
│ └── 菜单与快捷键管理 │
└─────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 渲染进程 │
├─────────────────────────────────────────────────────┤
│ fuel_calculator.html │
│ ├── 表单输入层(日期、加油量、里程、油价) │
│ ├── 计算结果层(油耗、费用等) │
│ ├── 统计概览层(统计卡片) │
│ ├── 趋势图表层(柱状图) │
│ └── 历史记录层(表格展示) │
├─────────────────────────────────────────────────────┤
│ fuel_calculator.js │
│ ├── FuelCalculator类(核心控制器) │
│ ├── 计算模块(油耗、费用计算) │
│ ├── 统计模块(累计统计、平均值) │
│ └── 图表模块(柱状图生成) │
└─────────────────────────────────────────────────────┘
2.2 核心组件职责
| 组件 | 职责 | 关键方法 |
|---|---|---|
| FuelCalculator | 主控制器,协调整体逻辑 | constructor, addRecord, updateUI |
| 计算模块 | 油耗和费用计算 | calculateCurrent |
| 统计模块 | 累计统计和平均值计算 | updateStats |
| 图表模块 | 柱状图生成和渲染 | updateChart |
2.3 数据模型
// 加油记录数据结构
{
id: 时间戳, // 唯一标识
date: "2024-01-15", // 加油日期
amount: "45.5", // 加油量(升)
distance: "520.0", // 行驶里程(公里)
price: "7.89", // 油价(元/升)
consumption: "8.75", // 油耗(L/100km)
totalCost: "358.99", // 加油费用(元)
costPerKm: "0.69", // 每公里费用(元)
costPer100Km: "69.00" // 每百公里费用(元)
}
2.4 计算公式
| 指标 | 公式 | 说明 |
|---|---|---|
| 油耗 | (加油量 / 行驶里程) × 100 | L/100km |
| 加油费用 | 加油量 × 油价 | 元 |
| 每公里费用 | 加油费用 / 行驶里程 | 元/km |
| 每百公里费用 | 油耗 × 油价 | 元/100km |
| 平均油耗 | (累计加油量 / 累计里程) × 100 | L/100km |
3. 核心代码实现
3.1 主入口HTML结构
HTML页面采用分层设计,包含表单输入区、结果展示区、统计概览区、趋势图区和历史记录区。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>油耗计算器</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 25px;
margin-bottom: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.form-control {
width: 100%;
padding: 12px 16px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
color: #fff;
font-size: 1rem;
outline: none;
}
.form-control:focus {
border-color: #4fd1c5;
box-shadow: 0 0 0 3px rgba(79, 209, 197, 0.2);
}
.btn-primary {
background: linear-gradient(135deg, #4fd1c5, #38b2ac);
color: #1a202c;
padding: 14px 30px;
border: none;
border-radius: 10px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(79, 209, 197, 0.3);
}
.results {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.result-card {
background: rgba(79, 209, 197, 0.1);
border: 1px solid rgba(79, 209, 197, 0.3);
border-radius: 12px;
padding: 20px;
text-align: center;
}
.result-value {
font-size: 2rem;
font-weight: 700;
color: #4fd1c5;
margin-bottom: 5px;
}
.result-label {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.6);
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<h2 class="card-title">📝 添加加油记录</h2>
<form id="fuel-form" class="form-row">
<div>
<label>加油日期</label>
<input type="date" id="date" class="form-control" required>
</div>
<div>
<label>加油量 (升)</label>
<input type="number" id="amount" class="form-control" step="0.1" required>
</div>
<div>
<label>行驶里程 (公里)</label>
<input type="number" id="distance" class="form-control" step="0.1" required>
</div>
<div>
<label>油价 (元/升)</label>
<input type="number" id="price" class="form-control" step="0.01">
</div>
</form>
<button class="btn-primary" id="add-btn">添加记录</button>
</div>
<div class="card">
<h2 class="card-title">📊 本次计算结果</h2>
<div class="results">
<div class="result-card">
<div class="result-value" id="fuel-consumption">-</div>
<div class="result-label">油耗 (L/100km)</div>
</div>
<div class="result-card">
<div class="result-value" id="cost-per-km">-</div>
<div class="result-label">每公里费用 (元)</div>
</div>
<div class="result-card">
<div class="result-value" id="total-cost">-</div>
<div class="result-label">加油费用 (元)</div>
</div>
<div class="result-card">
<div class="result-value" id="cost-per-100km">-</div>
<div class="result-label">每百公里费用 (元)</div>
</div>
</div>
</div>
</div>
</body>
</html>
设计要点:
- 深色主题设计:使用深蓝渐变背景,营造专业的工具氛围
- 毛玻璃效果:通过
backdrop-filter: blur(10px)实现现代感 - 响应式布局:使用CSS Grid实现自适应列数
- 表单样式:输入框使用半透明背景,聚焦时有绿色边框高亮
- 结果展示:卡片式布局展示计算结果,绿色高亮数值
3.2 FuelCalculator核心类实现
这是整个应用的核心控制器,负责管理数据、计算和UI更新。
class FuelCalculator {
constructor() {
this.records = this.loadRecords();
this.editIndex = null;
this.dateInput = document.getElementById('date');
this.amountInput = document.getElementById('amount');
this.distanceInput = document.getElementById('distance');
this.priceInput = document.getElementById('price');
this.addBtn = document.getElementById('add-btn');
this.dateInput.value = new Date().toISOString().split('T')[0];
this.setupEventListeners();
this.updateUI();
}
setupEventListeners() {
this.addBtn.addEventListener('click', () => this.addRecord());
this.amountInput.addEventListener('input', () => this.calculateCurrent());
this.distanceInput.addEventListener('input', () => this.calculateCurrent());
this.priceInput.addEventListener('input', () => this.calculateCurrent());
}
}
核心属性说明:
| 属性 | 类型 | 说明 |
|---|---|---|
| records | Array | 加油记录数组 |
| editIndex | Number/null | 当前编辑的记录索引 |
| dateInput | HTMLInputElement | 日期输入框 |
| amountInput | HTMLInputElement | 加油量输入框 |
| distanceInput | HTMLInputElement | 里程输入框 |
| priceInput | HTMLInputElement | 油价输入框 |
3.3 油耗计算核心算法
实时计算油耗和费用的核心方法。
calculateCurrent() {
const amount = parseFloat(this.amountInput.value);
const distance = parseFloat(this.distanceInput.value);
const price = parseFloat(this.priceInput.value) || 0;
if (isNaN(amount) || isNaN(distance) || amount <= 0 || distance <= 0) {
this.clearResults();
return;
}
const consumption = (amount / distance * 100).toFixed(2);
const totalCost = price > 0 ? (amount * price).toFixed(2) : '-';
const costPerKm = price > 0 ? ((amount * price) / distance).toFixed(4) : '-';
const costPer100Km = price > 0 ? (consumption * price).toFixed(2) : '-';
document.getElementById('fuel-consumption').textContent = consumption;
document.getElementById('cost-per-km').textContent = costPerKm;
document.getElementById('total-cost').textContent = totalCost;
document.getElementById('cost-per-100km').textContent = costPer100Km;
}
计算逻辑:
- 输入验证:检查加油量和里程是否为有效正数
- 油耗计算:
(加油量 / 行驶里程) × 100,保留2位小数 - 费用计算:如果油价未填写,费用显示为
- - 实时更新:输入变化时立即重新计算并更新UI
数学公式详解:
油耗(L/100km) = (加油量(L) / 行驶里程(km)) × 100
例如:加油45.5升,行驶520公里
油耗 = (45.5 / 520) × 100 = 8.75 L/100km
每公里费用(元) = 加油费用(元) / 行驶里程(km)
每百公里费用(元) = 油耗(L/100km) × 油价(元/L)
3.4 添加记录方法
addRecord() {
const date = this.dateInput.value;
const amount = parseFloat(this.amountInput.value);
const distance = parseFloat(this.distanceInput.value);
const price = parseFloat(this.priceInput.value) || 0;
if (!date || isNaN(amount) || isNaN(distance) || amount <= 0 || distance <= 0) {
this.showToast('请填写正确的加油信息');
return;
}
const consumption = (amount / distance * 100).toFixed(2);
const totalCost = (amount * price).toFixed(2);
const costPerKm = price > 0 ? (totalCost / distance).toFixed(4) : '-';
const costPer100Km = price > 0 ? (consumption * price).toFixed(2) : '-';
const record = {
id: Date.now(),
date,
amount: amount.toFixed(1),
distance: distance.toFixed(1),
price: price.toFixed(2),
consumption,
totalCost,
costPerKm,
costPer100Km
};
if (this.editIndex !== null) {
this.records[this.editIndex] = record;
this.showToast('记录已更新');
this.editIndex = null;
this.addBtn.innerHTML = '<span>➕</span> 添加记录';
} else {
this.records.unshift(record);
this.showToast('记录已添加');
}
this.saveRecords();
this.clearForm();
this.updateUI();
}
实现逻辑:
- 输入验证:确保日期、加油量、里程有效
- 计算指标:计算油耗、费用等所有指标
- 创建记录对象:包含所有输入值和计算结果
- 编辑模式判断:如果是编辑模式,更新现有记录;否则添加新记录
- 保存数据:调用
saveRecords()持久化到localStorage - 更新UI:刷新历史记录、统计和图表
3.5 统计分析模块
计算累计统计数据和平均值。
updateStats() {
const totalCount = this.records.length;
const totalAmount = this.records.reduce((sum, r) => sum + parseFloat(r.amount), 0);
const totalDistance = this.records.reduce((sum, r) => sum + parseFloat(r.distance), 0);
document.getElementById('total-count').textContent = totalCount;
document.getElementById('total-amount').textContent = totalAmount.toFixed(1);
document.getElementById('total-distance').textContent = totalDistance.toFixed(0);
if (totalCount > 0 && totalDistance > 0) {
const avgConsumption = (totalAmount / totalDistance * 100).toFixed(2);
document.getElementById('avg-consumption').textContent = `${avgConsumption} <span class="stat-unit">L/100km</span>`;
} else {
document.getElementById('avg-consumption').innerHTML = '-';
}
}
统计指标:
| 指标 | 计算方式 | 说明 |
|---|---|---|
| 总加油次数 | records.length | 记录总数 |
| 累计加油量 | sum(amount) | 所有记录加油量之和 |
| 累计行驶里程 | sum(distance) | 所有记录里程之和 |
| 平均油耗 | (累计加油量 / 累计里程) × 100 | 总油耗除以总里程 |
3.6 趋势图表生成
使用CSS动态生成柱状图展示油耗趋势。
updateChart() {
if (this.records.length < 2) {
document.getElementById('chart-bars').innerHTML = '<div style="text-align: center; color: rgba(255,255,255,0.4); padding: 40px;">需要至少2条记录才能显示趋势</div>';
document.getElementById('chart-labels').innerHTML = '';
return;
}
const recentRecords = this.records.slice(0, Math.min(12, this.records.length)).reverse();
const consumptions = recentRecords.map(r => parseFloat(r.consumption));
const maxValue = Math.max(...consumptions) * 1.1;
const minValue = Math.min(...consumptions) * 0.9;
const range = maxValue - minValue || 1;
const bars = recentRecords.map((record, index) => {
const value = parseFloat(record.consumption);
const height = ((value - minValue) / range) * 80 + 10;
return `<div class="chart-bar" style="height: ${height}%" data-value="${value.toFixed(2)}"></div>`;
}).join('');
const labels = recentRecords.map(r => {
const date = new Date(r.date);
return `${date.getMonth() + 1}/${date.getDate()}`;
}).join('<span></span>');
document.getElementById('chart-bars').innerHTML = bars;
document.getElementById('chart-labels').innerHTML = `<span>${labels}</span>`;
}
图表生成逻辑:
┌────────────────────────────────────────────────────┐
│ 柱状图生成流程 │
├────────────────────────────────────────────────────┤
│ 1. 数据准备 │
│ - 取最近12条记录(按时间倒序) │
│ - 反转数组,按时间正序排列 │
│ │
│ 2. 数值标准化 │
│ - 找出最大值和最小值 │
│ - 计算范围:maxValue * 1.1 - minValue * 0.9 │
│ - 每个值映射到10%-90%的高度范围 │
│ │
│ 3. DOM生成 │
│ - 动态创建chart-bar元素 │
│ - 设置data-value属性用于悬停显示 │
│ - 生成日期标签 │
└────────────────────────────────────────────────────┘
技术要点:
- 数据范围限制:最多显示最近12条记录,避免图表过于拥挤
- 数值标准化:将油耗值映射到10%-90%的高度范围,确保柱状图有足够的视觉差异
- 响应式高度:使用百分比高度,适配不同容器大小
- 悬停提示:通过
data-value属性存储原始值,配合CSS伪元素显示
3.7 数据持久化
使用localStorage实现数据的本地持久化存储。
saveRecords() {
localStorage.setItem('fuelRecords', JSON.stringify(this.records));
}
loadRecords() {
const saved = localStorage.getItem('fuelRecords');
return saved ? JSON.parse(saved) : [];
}
存储设计:
| 存储键 | 数据格式 | 说明 |
|---|---|---|
| fuelRecords | JSON数组 | 加油记录列表 |
优势:
- 无需后端服务器
- 数据保存在用户本地
- 简单易用,适合轻量级应用
- 自动持久化,刷新页面不丢失
3.8 记录管理(编辑和删除)
editRecord(index) {
const record = this.records[index];
this.dateInput.value = record.date;
this.amountInput.value = record.amount;
this.distanceInput.value = record.distance;
this.priceInput.value = record.price;
this.editIndex = index;
this.addBtn.innerHTML = '<span>✏️</span> 更新记录';
window.scrollTo({ top: 0, behavior: 'smooth' });
this.calculateCurrent();
}
deleteRecord(index) {
this.records.splice(index, 1);
this.saveRecords();
this.updateUI();
this.showToast('记录已删除');
}
编辑流程:
- 填充表单为选中记录的数据
- 设置
editIndex标记为编辑模式 - 更改按钮文字为"更新记录"
- 滚动到页面顶部
- 重新计算当前值
删除流程:
- 使用
splice从数组中移除记录 - 保存更新后的数据
- 更新UI显示
- 显示操作提示
4. 用户交互设计
4.1 表单验证
if (!date || isNaN(amount) || isNaN(distance) || amount <= 0 || distance <= 0) {
this.showToast('请填写正确的加油信息');
return;
}
验证规则:
- 日期必须填写
- 加油量必须是大于0的数字
- 行驶里程必须是大于0的数字
- 油价可选,不填则费用显示为
-
4.2 Toast提示
showToast(message) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 2000);
}
Toast样式:
.toast {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.85);
color: #4fd1c5;
padding: 15px 30px;
border-radius: 10px;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.toast.show {
opacity: 1;
visibility: visible;
}
4.3 清空确认弹窗
this.clearAllBtn.addEventListener('click', () => {
this.modalOverlay.classList.add('show');
});
this.confirmClearBtn.addEventListener('click', () => {
this.clearAllRecords();
this.modalOverlay.classList.remove('show');
});
安全设计:
- 清空操作需要二次确认
- 弹窗显示警告信息
- 防止误操作导致数据丢失
5. 鸿蒙PC平台适配
5.1 窗口配置
const { app, BrowserWindow } = require('electron');
function createWindow() {
mainWindow = new BrowserWindow({
width: 900,
height: 1000,
minWidth: 600,
minHeight: 800,
title: '油耗计算器',
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
},
});
mainWindow.loadFile('fuel_calculator.html');
}
app.on('ready', createWindow);
5.2 平台特殊考虑
- 窗口尺寸:针对鸿蒙PC设备优化默认窗口大小(900x1000)
- 字体渲染:确保中文和数字在鸿蒙平台正确显示
- 响应式设计:使用CSS Grid实现自适应布局
- 触摸支持:按钮和输入框尺寸适合触摸操作
6. 代码优化建议
6.1 性能优化
// 当前实现:每次输入变化都更新UI
// 优化方案:使用防抖减少计算频率
class FuelCalculator {
constructor() {
this.debounceTimer = null;
// ...
}
calculateCurrent() {
clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(() => {
// 实际计算逻辑
}, 300);
}
}
6.2 数据验证增强
// 添加更严格的数据验证
validateInput(date, amount, distance, price) {
const errors = [];
if (!date) {
errors.push('请选择加油日期');
}
if (isNaN(amount) || amount <= 0 || amount > 1000) {
errors.push('加油量必须在0-1000升之间');
}
if (isNaN(distance) || distance <= 0 || distance > 10000) {
errors.push('行驶里程必须在0-10000公里之间');
}
if (price && (isNaN(price) || price <= 0 || price > 30)) {
errors.push('油价必须在0-30元之间');
}
return errors;
}
6.3 图表优化
// 添加平滑动画效果
updateChart() {
// ... 生成bars HTML
const chartBars = document.getElementById('chart-bars');
chartBars.innerHTML = bars;
// 添加过渡动画
setTimeout(() => {
document.querySelectorAll('.chart-bar').forEach(bar => {
bar.style.transition = 'height 0.5s ease-out';
});
}, 100);
}
7. 总结
本文详细介绍了基于Electron框架在鸿蒙PC平台上实现油耗计算器的完整方案。核心技术点包括:
- 油耗计算算法:基于加油量和行驶里程计算油耗,支持实时计算
- 统计分析:累计加油量、累计里程、平均油耗等统计指标
- 趋势图表:使用CSS动态生成柱状图,展示油耗变化趋势
- 数据持久化:使用localStorage实现本地数据存储
- 记录管理:支持添加、编辑、删除和清空操作
该实现不仅提供了实用的油耗管理功能,还具备良好的用户体验和现代化的UI设计,适合作为学习桌面应用开发和数据可视化的参考项目。
附录:完整文件清单
| 文件路径 | 说明 |
|---|---|
fuel_calculator.html |
主HTML页面 |
fuel_calculator.js |
核心JavaScript逻辑 |
home.html |
应用中心入口(包含导航链接) |
main.js |
Electron主进程配置 |
更多推荐


所有评论(0)