汽车保养记录记事本

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>
相关推荐
盟接之桥6 小时前
当大模型遇见线束制造:不是通用AI,而是行业AI
大数据·网络·人工智能·安全·制造
91刘仁德7 小时前
IP协议详解:从IP协议头到网段划分、路由与NAT
linux·服务器·网络·网络协议·tcp/ip
可乐鸡翅yeah_8 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
酣大智10 小时前
H3C MSR3600 交换机配置 SSH
网络·ssh·智能路由器
沐浴露z10 小时前
Agent 响应延迟过高?从工具治理角度提供两条思路
前端·网络
马六六i11 小时前
市面上正规的IP驱动产业新场景新工具有哪些
大数据·网络·python·tcp/ip
可乐ea13 小时前
多模型编排的三层:框架、模型路由与提供商路由
前端·网络·人工智能·ai智能体·多智能体协作·多模型编排·大模型路由
万联WANFLOW13 小时前
全球广告网络直通美国,TikTok抢占旺季全域流量新高地
网络·业界资讯
HackTwoHub13 小时前
Clown SRC skill第二代2.x最新版企业安全研究 Skill|证据优先的 SRC 与白盒审计工作流正式更新
网络·安全·web安全·网络安全·系统安全·密码学·安全架构
2601_9621773013 小时前
2026年AI API Gateway怎么选?我整理了6种方案的费用、稳定性和适用场景
网络·人工智能·深度学习·gateway