prompt
写一个兼容PC和手机的精致好用的html保养记录记事本,记录保养时间,里程,项目,价格,可以根据年份筛选保养记录,然后根据筛选出的明细自动统计保养总花费

html
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>车辆保养记录记事本</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#2563eb',
secondary: '#475569',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.card-shadow {
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
}
.input-focus {
@apply focus:ring-2 focus:ring-primary/50 focus:border-primary outline-none transition-all;
}
}
</style>
</head>
<body class="bg-slate-100 min-h-screen pb-10">
<div class="max-w-5xl mx-auto px-4 py-6">
<!-- 头部标题 -->
<div class="bg-white rounded-xl p-6 card-shadow mb-6">
<h1 class="text-[clamp(1.5rem,3vw,2rem)] font-bold text-slate-800 flex items-center gap-2">
<i class="fa fa-car text-primary"></i>车辆保养记录记事本
</h1>
<p class="text-slate-500 mt-1">记录保养时间、里程、项目、费用,自动统计总花费</p>
</div>
<div class="bg-white rounded-xl p-6 card-shadow mb-6">
<h2 class="text-lg font-semibold text-slate-700 mb-4"><i class="fa fa-plus-circle text-primary mr-2"></i>新增保养记录</h2>
<form id="recordForm" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-slate-600 mb-1">保养日期</label>
<input type="date" id="recordDate" required class="w-full border border-slate-300 rounded-lg px-3 py-2 input-focus">
</div>
<div>
<label class="block text-sm text-slate-600 mb-1">当前里程(km)</label>
<input type="number" id="recordMileage" min="0" required placeholder="例如 125000" class="w-full border border-slate-300 rounded-lg px-3 py-2 input-focus">
</div>
<div class="md:col-span-2">
<label class="block text-sm text-slate-600 mb-1">保养项目(逗号分隔)</label>
<input type="text" id="recordItem" required placeholder="机油机滤,空滤,刹车油" class="w-full border border-slate-300 rounded-lg px-3 py-2 input-focus">
</div>
<div class="md:col-span-2">
<label class="block text-sm text-slate-600 mb-1">花费金额(元)</label>
<input type="number" id="recordCost" min="0" step="0.01" required placeholder="例如 1130" class="w-full border border-slate-300 rounded-lg px-3 py-2 input-focus">
</div>
<div class="md:col-span-2">
<button type="submit" class="w-full bg-primary hover:bg-primary/90 text-white py-2.5 rounded-lg transition-all font-medium">
<i class="fa fa-save mr-1"></i>保存这条记录
</button>
</div>
</form>
</div>
<div class="bg-white rounded-xl p-6 card-shadow mb-6">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div>
<label class="text-slate-600 text-sm mr-2">按年份筛选:</label>
<select id="yearFilter" class="border border-slate-300 rounded-lg px-3 py-2 input-focus">
<option value="all">全部年份</option>
</select>
</div>
<div class="bg-blue-50 px-4 py-3 rounded-lg">
<span class="text-slate-700">筛选后总花费:</span>
<span id="totalCost" class="text-xl font-bold text-primary">0.00</span>
<span class="text-slate-600"> 元</span>
</div>
</div>
</div>
<div class="bg-white rounded-xl p-6 card-shadow">
<h2 class="text-lg font-semibold text-slate-700 mb-4"><i class="fa fa-list-alt text-primary mr-2"></i>保养记录列表</h2>
<div id="recordList" class="space-y-4">
<div class="text-center text-slate-400 py-10">暂无保养记录,请添加第一条</div>
</div>
</div>
</div>
<script>
// 读取本地存储
function getRecords() {
const data = localStorage.getItem('carMaintainRecords');
return data ? JSON.parse(data) : [];
}
function saveRecords(list) {
localStorage.setItem('carMaintainRecords', JSON.stringify(list));
}
// 渲染年份下拉框
function renderYearSelect() {
const records = getRecords();
const yearSet = new Set();
yearSet.add('all');
records.forEach(item => {
const y = new Date(item.date).getFullYear();
yearSet.add(y);
});
const yearSelect = document.getElementById('yearFilter');
const currentVal = yearSelect.value;
yearSelect.innerHTML = '';
Array.from(yearSet).sort((a,b)=>b-a).forEach(y=>{
const opt = document.createElement('option');
opt.value = y;
opt.textContent = y === 'all' ? '全部年份' : y+'年';
yearSelect.appendChild(opt);
})
yearSelect.value = currentVal;
}
// 渲染列表 + 计算总金额
function renderRecords() {
const records = getRecords();
const selectedYear = document.getElementById('yearFilter').value;
const listDom = document.getElementById('recordList');
let filterList = records;
// 年份筛选
if(selectedYear !== 'all'){
filterList = records.filter(r=> new Date(r.date).getFullYear().toString() === selectedYear);
}
// 计算合计
const total = filterList.reduce((sum,item)=> sum + Number(item.cost), 0);
document.getElementById('totalCost').innerText = total.toFixed(2);
if(filterList.length === 0){
listDom.innerHTML = `<div class="text-center text-slate-400 py-10">暂无记录</div>`;
return;
}
// 按日期倒序,新记录在最上面
filterList.sort((a,b)=> new Date(b.date) - new Date(a.date));
let html = '';
filterList.forEach((item, index)=>{
html += `
<div class="border border-slate-200 rounded-lg p-4 hover:bg-slate-50 transition">
<div class="flex justify-between items-start flex-wrap gap-2">
<div>
<div class="font-semibold text-slate-800">${item.date}</div>
<div class="text-sm text-slate-500">里程:${item.mileage} km</div>
</div>
<div class="text-primary font-bold text-lg">¥${Number(item.cost).toFixed(2)}</div>
</div>
<div class="mt-2 text-slate-600 text-sm">
<i class="fa fa-wrench text-slate-400 mr-1"></i>保养项目:${item.item}
</div>
<div class="mt-3 text-right">
<button onclick="deleteRecord(${index})" class="text-red-500 hover:text-red-700 text-sm"><i class="fa fa-trash"></i> 删除</button>
</div>
</div>
`
})
listDom.innerHTML = html;
}
// 删除记录
window.deleteRecord = function(idx) {
const records = getRecords();
const selectedYear = document.getElementById('yearFilter').value;
let filterList = records;
if(selectedYear !== 'all'){
filterList = records.filter(r=> new Date(r.date).getFullYear().toString() === selectedYear);
}
// 找到原数组下标
const targetItem = filterList[idx];
const realIndex = records.findIndex(r=> r.date === targetItem.date && r.mileage === targetItem.mileage && r.cost === targetItem.cost);
records.splice(realIndex,1);
saveRecords(records);
renderYearSelect();
renderRecords();
}
// 表单提交
document.getElementById('recordForm').addEventListener('submit', function(e){
e.preventDefault();
const date = document.getElementById('recordDate').value;
const mileage = document.getElementById('recordMileage').value;
const item = document.getElementById('recordItem').value;
const cost = document.getElementById('recordCost').value;
const records = getRecords();
records.push({date, mileage, item, cost});
saveRecords(records);
this.reset();
renderYearSelect();
renderRecords();
})
// 年份筛选切换
document.getElementById('yearFilter').addEventListener('change', function(){
renderRecords();
})
// 初始化页面
window.onload = function(){
// 默认日期为今天
const today = new Date();
const dStr = today.toISOString().split('T')[0];
document.getElementById('recordDate').value = dStr;
renderYearSelect();
renderRecords();
}
</script></body>
</html>