汽车保养记录记事本

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>
相关推荐
青瓦梦滋9 小时前
WebSocket协议
linux·服务器·网络·c++·websocket·网络协议·http
zhang1338308907510 小时前
传统机械三杯式风速传感器易磨损冻堵?CG-88金属超声波风速风向传感器给出免运维方案
运维·服务器·网络·人工智能·自动化
知已难寻10 小时前
第 2 篇 · 云的地基:服务器、存储、网络三大基础设施
运维·服务器·网络·云原生·云计算·负载均衡·1024程序员节
IpdataCloud12 小时前
批量查IP的API接口怎么选?免费API配额瓶颈与离线库本地批量方案实测
网络·网络协议·tcp/ip
ITyunwei098713 小时前
售后夜间值守怎么落地:从告警到交接的工单系统配置笔记
网络
Smoothcloud润云14 小时前
GPU远程纳管安全吗?最小权限、网络边界与日志审计实战
服务器·网络·人工智能·安全·gpu算力·gpu
ai大模型-探索者14 小时前
Function Call与MCP(区别、联系与实战)
java·服务器·网络
木子欢儿14 小时前
Debian 13 + vsftpd 搭建FTP服务
linux·服务器·前端·网络·debian
达~熊迪15 小时前
IP 分配前核查:把在线状态和使用登记一起看
服务器·网络·tcp/ip
西索斯coding16 小时前
Qwen3-Max 与 Qwen-Plus:60 个真实修复任务评测方案
大数据·网络·算法·ai