SOURCE

console 命令行工具 X clear

                    
>
console
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>极简记账 · 收支明细与总结</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,400;14..32,500;14..32,600&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Inter', system-ui, -apple-system, sans-serif;
            background: #f3f5f9; min-height: 100vh; display: flex; align-items: flex-start; justify-content: center;
            padding: 1.5rem; background-image: radial-gradient(circle at 10% 20%, rgba(255, 255, 255, 0.8) 0%, transparent 40%);
        }
        .app-container {
            max-width: 1000px; width: 100%; background: rgba(255, 255, 255, 0.85);
            backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
            border-radius: 32px; box-shadow: 0 25px 40px -15px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.6) inset;
            padding: 2rem; border: 1px solid rgba(255, 255, 255, 0.7); margin-top: 2rem; margin-bottom: 2rem;
        }

        /* 顶部导航标签 */
        .tabs { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; background: #e2e8f0; padding: 0.4rem; border-radius: 40px; width: fit-content; }
        .tab-btn {
            padding: 0.6rem 1.5rem; border-radius: 30px; border: none; background: transparent;
            font-weight: 600; font-size: 0.95rem; color: #64748b; cursor: pointer; transition: 0.2s; font-family: inherit;
        }
        .tab-btn.active { background: white; color: #1e3a8a; box-shadow: 0 2px 10px rgba(0,0,0,0.05); }

        .page-section { display: none; }
        .page-section.active { display: block; animation: fadeSlide 0.3s ease; }

        /* 头部:时间与汇总 */
        .header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; gap: 1rem; }
        .datetime {
            display: flex; align-items: center; gap: 0.75rem; background: rgba(255, 255, 255, 0.6);
            padding: 0.6rem 1.3rem; border-radius: 60px; border: 1px solid rgba(200, 215, 240, 0.6);
            font-weight: 500; font-size: 0.95rem; color: #1e293b;
        }
        .datetime i { color: #3b82f6; font-size: 1.1rem; }

        /* 数据管理按钮 */
        .data-actions { display: flex; gap: 0.5rem; }
        .btn-mini {
            padding: 0.5rem 1rem; border-radius: 20px; border: 1px solid #cbd5e1; background: white;
            color: #475569; font-size: 0.8rem; cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 0.4rem;
        }
        .btn-mini:hover { background: #f8fafc; border-color: #94a3b8; }

        /* 添加表单 */
        .add-section { background: #ffffffd9; border-radius: 24px; padding: 1.5rem; border: 1px solid rgba(255, 255, 255, 0.9); margin-bottom: 1.5rem; }
        .form-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end; }
        .form-group { flex: 1 1 140px; display: flex; flex-direction: column; gap: 0.4rem; }
        .form-group label { font-size: 0.8rem; font-weight: 600; color: #475569; }
        .form-group input, .form-group select {
            padding: 0.7rem 1rem; border-radius: 12px; border: 1.5px solid #e2e8f0;
            font-family: inherit; font-size: 0.95rem; outline: none; transition: 0.15s; width: 100%;
        }
        .form-group input:focus, .form-group select:focus { border-color: #3b82f6; box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.15); }

        .subitems-area { margin-top: 1rem; background: #f8fafc; padding: 1rem; border-radius: 16px; border: 1px dashed #cbd5e1; }
        .subitems-header { display: flex; justify-content: space-between; margin-bottom: 0.75rem; font-size: 0.85rem; font-weight: 600; color: #334155; }
        .subitem-row { display: flex; gap: 0.6rem; margin-bottom: 0.6rem; align-items: center; }
        .subitem-row input { padding: 0.5rem 0.8rem; border-radius: 10px; border: 1px solid #e2e8f0; flex: 1; font-size: 0.9rem; outline: none; }
        .subitem-row input:focus { border-color: #3b82f6; }
        .subitem-row .sub-amount { max-width: 100px; text-align: right; }
        .btn-icon { background: white; border: 1px solid #e2e8f0; border-radius: 10px; width: 34px; height: 34px; cursor: pointer; color: #64748b; flex-shrink: 0; }
        .btn-icon:hover { background: #fee2e2; color: #b91c1c; }
        .add-sub-btn {
            background: transparent; border: 1px dashed #94a3b8; border-radius: 10px; padding: 0.5rem;
            font-size: 0.85rem; color: #3b82f6; font-weight: 500; display: flex; align-items: center; justify-content: center; gap: 0.4rem; cursor: pointer; width: 100%;
        }
        .btn-submit {
            background: #1e3a8a; color: white; border: none; border-radius: 40px; padding: 0.85rem 2rem;
            font-weight: 600; font-size: 0.95rem; display: flex; align-items: center; gap: 0.6rem; cursor: pointer;
            box-shadow: 0 8px 18px -6px rgba(30, 58, 138, 0.3); margin-left: auto; margin-top: 1rem;
        }
        .btn-submit:hover { background: #1e40af; transform: translateY(-1px); }

        /* 记录列表 */
        .list-header { display: flex; justify-content: space-between; margin-bottom: 1rem; align-items: center; }
        .list-header h2 { font-size: 1.25rem; color: #0f172a; display: flex; align-items: center; gap: 0.5rem; }
        .record-list { display: flex; flex-direction: column; gap: 0.8rem; max-height: 500px; overflow-y: auto; padding-right: 0.3rem; }
        .record-item { background: white; border-radius: 16px; padding: 1rem 1.2rem; display: flex; flex-wrap: wrap; gap: 1rem; border: 1px solid #f1f5f9; transition: 0.15s; }
        .record-item:hover { border-color: #dbeafe; }
        .record-date-badge { background: #eff6ff; color: #1e3a8a; padding: 0.3rem 0.8rem; border-radius: 20px; font-size: 0.75rem; font-weight: 600; white-space: nowrap; height: fit-content; }
        .record-main { flex: 1; display: flex; flex-direction: column; gap: 0.5rem; }
        .record-top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.5rem; }
        .record-desc { font-weight: 600; color: #0f172a; display: flex; align-items: center; gap: 0.5rem; }
        .category-tag { font-size: 0.7rem; background: #eef2ff; color: #4f46e5; padding: 0.2rem 0.6rem; border-radius: 10px; }
        .record-amount { font-weight: 700; font-size: 1.15rem; }
        .income-text { color: #0b8e5e; } .expense-text { color: #b91c1c; }
        .subitems-list { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; padding-top: 0.5rem; border-top: 1px dashed #e2e8f0; font-size: 0.85rem; color: #475569; }
        .subitem-tag { display: flex; align-items: center; gap: 0.3rem; background: #f8fafc; padding: 0.2rem 0.6rem; border-radius: 20px; border: 1px solid #e9eef3; }
        .delete-record { background: transparent; border: none; color: #cbd5e1; cursor: pointer; font-size: 1rem; padding: 0.4rem; border-radius: 8px; align-self: center; }
        .delete-record:hover { color: #ef4444; background: #fee2e2; }

        /* 总结页面样式 */
        .summary-controls { display: flex; flex-wrap: wrap; gap: 1rem; background: white; padding: 1.5rem; border-radius: 20px; border: 1px solid #e2e8f0; margin-bottom: 1.5rem; align-items: flex-end; }
        .quick-filters { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1rem; width: 100%; }
        .filter-btn { padding: 0.5rem 1rem; border-radius: 20px; border: 1px solid #cbd5e1; background: #f8fafc; color: #475569; cursor: pointer; font-size: 0.9rem; transition: 0.2s; }
        .filter-btn.active { background: #3b82f6; color: white; border-color: #3b82f6; }
        
        .custom-date-group { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
        .custom-date-group input { padding: 0.5rem; border-radius: 8px; border: 1px solid #cbd5e1; font-family: inherit; }
        
        .summary-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; margin-bottom: 1.5rem; }
        .sum-card { background: white; padding: 1.5rem; border-radius: 20px; border: 1px solid #f1f5f9; text-align: center; box-shadow: 0 4px 12px rgba(0,0,0,0.02); }
        .sum-card .label { font-size: 0.9rem; color: #64748b; margin-bottom: 0.5rem; font-weight: 500; }
        .sum-card .value { font-size: 1.8rem; font-weight: 700; }
        
        .detail-section { background: white; padding: 1.5rem; border-radius: 20px; border: 1px solid #f1f5f9; }
        .detail-section h3 { margin-bottom: 1rem; font-size: 1.1rem; color: #1e293b; border-bottom: 1px solid #e2e8f0; padding-bottom: 0.5rem; }
        .detail-row { display: flex; justify-content: space-between; padding: 0.6rem 0; border-bottom: 1px dashed #f1f5f9; font-size: 0.95rem; color: #334155; }
        .detail-row:last-child { border-bottom: none; }

        .empty-state { text-align: center; padding: 2rem; color: #94a3b8; background: #f8fafc; border-radius: 16px; border: 1px dashed #cbd5e1; }

        @keyframes fadeSlide { 0% { opacity: 0; transform: translateY(6px); } 100% { opacity: 1; transform: translateY(0); } }
        @media (max-width: 600px) {
            .app-container { padding: 1.2rem; border-radius: 20px; margin-top: 1rem; }
            .form-row { flex-direction: column; align-items: stretch; }
            .btn-submit { width: 100%; justify-content: center; }
            .custom-date-group { flex-direction: column; align-items: stretch; width: 100%; }
            .custom-date-group input { width: 100%; }
        }
    </style>
</head>
<body>
    <div class="app-container">
        
        <!-- 标签页切换 -->
        <div class="tabs">
            <button class="tab-btn active" onclick="switchTab('record')"><i class="fas fa-pen"></i> 记一笔</button>
            <button class="tab-btn" onclick="switchTab('summary')"><i class="fas fa-chart-pie"></i> 数据总结</button>
        </div>

        <!-- ================= 记账页面 ================= -->
        <div id="page-record" class="page-section active">
            <div class="header">
                <div class="datetime" id="liveDatetime">
                    <i class="far fa-calendar-alt"></i>
                    <span id="datetimeText">加载中...</span>
                </div>
                <div class="data-actions">
                    <button class="btn-mini" onclick="exportData()" title="将数据存为文件"><i class="fas fa-file-export"></i> 导出</button>
                    <button class="btn-mini" onclick="document.getElementById('importFile').click()" title="从文件恢复数据"><i class="fas fa-file-import"></i> 导入</button>
                    <input type="file" id="importFile" accept=".json" style="display: none;" onchange="importData(event)">
                </div>
            </div>

            <div class="add-section">
                <div class="form-row">
                    <div class="form-group" style="flex: 2 1 200px;">
                        <label>描述</label>
                        <input type="text" id="descInput" placeholder="例如:超市购物" maxlength="30">
                    </div>
                    <div class="form-group">
                        <label>类型</label>
                        <select id="typeSelect">
                            <option value="expense">支出</option>
                            <option value="income">收入</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label>总金额</label>
                        <input type="number" id="amountInput" placeholder="0.00" min="0.01" step="0.01">
                    </div>
                    <div class="form-group">
                        <label>日期</label>
                        <input type="date" id="dateInput">
                    </div>
                </div>

                <div class="subitems-area">
                    <div class="subitems-header">
                        <span><i class="fas fa-list-ul"></i> 子项明细(可选)</span>
                        <span style="font-size:0.75rem; font-weight:400; color:#64748b;">合计 ≤ 总金额</span>
                    </div>
                    <div class="subitem-list" id="subitemList"></div>
                    <button class="add-sub-btn" type="button" onclick="addSubRow()">
                        <i class="fas fa-plus-circle"></i> 添加子项
                    </button>
                </div>
                <button class="btn-submit" id="addRecordBtn"><i class="fas fa-check-circle"></i> 保存记录</button>
            </div>

            <div class="list-header">
                <h2><i class="fas fa-receipt"></i> 收支明细</h2>
                <span style="font-size:0.8rem; background:#f1f5f9; padding:0.2rem 0.8rem; border-radius:40px; color:#475569;" id="recordCount">0 笔</span>
            </div>
            <div class="record-list" id="recordList"></div>
        </div>

        <!-- ================= 总结页面 ================= -->
        <div id="page-summary" class="page-section">
            <div class="summary-controls">
                <div class="quick-filters">
                    <button class="filter-btn" onclick="setQuickFilter('week', this)">本周</button>
                    <button class="filter-btn active" onclick="setQuickFilter('month', this)">本月</button>
                    <button class="filter-btn" onclick="setQuickFilter('year', this)">本年</button>
                    <button class="filter-btn" onclick="setQuickFilter('all', this)">全部</button>
                </div>
                <div class="custom-date-group">
                    <span style="font-size:0.9rem; color:#475569; font-weight:500;">自定义:</span>
                    <input type="date" id="startDate">
                    <span>至</span>
                    <input type="date" id="endDate">
                    <button class="btn-mini" style="padding: 0.5rem 1.2rem;" onclick="applyCustomFilter()">查询</button>
                </div>
            </div>

            <div class="summary-cards">
                <div class="sum-card">
                    <div class="label">总收入</div>
                    <div class="value income-text" id="sumIncome">¥0.00</div>
                </div>
                <div class="sum-card">
                    <div class="label">总支出</div>
                    <div class="value expense-text" id="sumExpense">¥0.00</div>
                </div>
                <div class="sum-card">
                    <div class="label">结余</div>
                    <div class="value" id="sumBalance">¥0.00</div>
                </div>
            </div>

            <div class="detail-section">
                <h3><i class="fas fa-list-alt"></i> 收支明细汇总</h3>
                <div id="sumDetails">
                    <div class="empty-state">暂无数据</div>
                </div>
            </div>
        </div>

    </div>

    <script>
        // ---------------- 数据管理 ----------------
        let records = JSON.parse(localStorage.getItem('jizhang_records_v2')) || [];
        
        function saveRecords() {
            localStorage.setItem('jizhang_records_v2', JSON.stringify(records));
        }

        // ---------------- UI 切换 ----------------
        function switchTab(tabName) {
            document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
            document.querySelectorAll('.page-section').forEach(sec => sec.classList.remove('active'));
            
            if(tabName === 'record') {
                document.querySelector('.tab-btn:nth-child(1)').classList.add('active');
                document.getElementById('page-record').classList.add('active');
            } else {
                document.querySelector('.tab-btn:nth-child(2)').classList.add('active');
                document.getElementById('page-summary').classList.add('active');
                // 默认展示本月总结
                setQuickFilter('month', document.querySelector('.filter-btn.active') || document.querySelector('.filter-btn:nth-child(2)'));
            }
        }

        // ---------------- 工具函数 ----------------
        function formatMoney(value) {
            return '¥' + Number(value).toFixed(2);
        }
        function generateId() {
            return Date.now() + '-' + Math.random().toString(36).substr(2, 9);
        }
        function getTodayStr() {
            const now = new Date();
            return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
        }

        // ---------------- 时间显示 ----------------
        function updateDatetime() {
            const now = new Date();
            const options = { month: 'long', day: 'numeric', weekday: 'long', hour: '2-digit', minute: '2-digit' };
            document.getElementById('datetimeText').textContent = now.toLocaleDateString('zh-CN', options);
        }
        setInterval(updateDatetime, 1000);
        updateDatetime();

        // ---------------- 子项操作 ----------------
        function addSubRow(desc = '', amount = '') {
            const list = document.getElementById('subitemList');
            const row = document.createElement('div');
            row.className = 'subitem-row';
            row.innerHTML = `
                <input type="text" class="sub-desc" placeholder="子项名称(如零食)" value="${desc}" maxlength="20">
                <input type="number" class="sub-amount" placeholder="金额" value="${amount}" min="0.01" step="0.01">
                <button class="btn-icon" type="button" onclick="this.parentElement.remove()"><i class="fas fa-times"></i></button>
            `;
            list.appendChild(row);
        }
        
        function resetSubitems() {
            document.getElementById('subitemList').innerHTML = '';
            addSubRow();
        }
        resetSubitems();

        // ---------------- 记账功能 ----------------
        document.getElementById('addRecordBtn').addEventListener('click', () => {
            const desc = document.getElementById('descInput').value.trim();
            const type = document.getElementById('typeSelect').value;
            const amount = parseFloat(document.getElementById('amountInput').value);
            const date = document.getElementById('dateInput').value;

            if (!desc || isNaN(amount) || amount <= 0 || !date) {
                alert('请正确填写描述、金额和日期!');
                return;
            }

            const subitems = [];
            let subTotal = 0;
            document.querySelectorAll('.subitem-row').forEach(row => {
                const sDesc = row.querySelector('.sub-desc').value.trim();
                const sAmt = parseFloat(row.querySelector('.sub-amount').value);
                if (sDesc && !isNaN(sAmt) && sAmt > 0) {
                    subitems.push({ desc: sDesc, amount: sAmt });
                    subTotal += sAmt;
                }
            });

            if (subitems.length > 0 && subTotal > amount + 0.01) {
                alert(`子项总金额 ${formatMoney(subTotal)} 超过了总金额 ${formatMoney(amount)},请调整。`);
                return;
            }

            records.push({ id: generateId(), desc, type, amount, date, subitems });
            saveRecords();

            // 重置表单
            document.getElementById('descInput').value = '';
            document.getElementById('amountInput').value = '';
            document.getElementById('dateInput').value = getTodayStr();
            resetSubitems();

            renderRecordList();
        });

        // 初始日期设为今天
        document.getElementById('dateInput').value = getTodayStr();

        // ---------------- 渲染记录列表 ----------------
        function renderRecordList() {
            const listEl = document.getElementById('recordList');
            document.getElementById('recordCount').textContent = records.length + ' 笔';

            if (records.length === 0) {
                listEl.innerHTML = `<div class="empty-state">还没有任何记录,快去记一笔吧!</div>`;
                return;
            }

            const sorted = [...records].sort((a, b) => b.date.localeCompare(a.date));
            let html = '';
            
            sorted.forEach(rec => {
                const isIncome = rec.type === 'income';
                const sign = isIncome ? '+' : '-';
                const amountClass = isIncome ? 'income-text' : 'expense-text';
                
                const subHtml = rec.subitems.length > 0 
                    ? `<div class="subitems-list">${rec.subitems.map(s => `<span class="subitem-tag"><i class="fas fa-circle" style="font-size:6px; color:#94a3b8;"></i> ${s.desc} ${formatMoney(s.amount)}</span>`).join('')}</div>`
                    : '';

                html += `
                    <div class="record-item">
                        <div class="record-date-badge">${rec.date}</div>
                        <div class="record-main">
                            <div class="record-top">
                                <div class="record-desc">${rec.desc} <span class="category-tag">${isIncome ? '收入' : '支出'}</span></div>
                                <div class="record-amount ${amountClass}">${sign} ${formatMoney(rec.amount)}</div>
                            </div>
                            ${subHtml}
                        </div>
                        <button class="delete-record" onclick="deleteRecord('${rec.id}')"><i class="fas fa-trash-alt"></i></button>
                    </div>
                `;
            });
            listEl.innerHTML = html;
        }

        function deleteRecord(id) {
            if (confirm('确定删除这条记录吗?')) {
                records = records.filter(r => r.id !== id);
                saveRecords();
                renderRecordList();
            }
        }

        // ---------------- 总结功能核心 ----------------
        let currentFilter = 'month'; // week, month, year, all, custom

        function setQuickFilter(type, btn) {
            currentFilter = type;
            if(btn) {
                document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
                btn.classList.add('active');
            }
            
            const today = new Date();
            let start = '', end = '';

            if (type === 'week') {
                // 获取本周一
                const day = today.getDay() || 7; 
                const monday = new Date(today);
                monday.setDate(today.getDate() - day + 1);
                start = monday.toISOString().slice(0, 10);
                end = getTodayStr();
            } else if (type === 'month') {
                start = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-01`;
                end = getTodayStr();
            } else if (type === 'year') {
                start = `${today.getFullYear()}-01-01`;
                end = getTodayStr();
            } else if (type === 'all') {
                start = '1970-01-01';
                end = '2099-12-31';
            }

            document.getElementById('startDate').value = start;
            document.getElementById('endDate').value = end;
            calculateSummary(start, end);
        }

        function applyCustomFilter() {
            const start = document.getElementById('startDate').value;
            const end = document.getElementById('endDate').value;
            if (!start || !end) { alert('请选择完整的开始和结束日期'); return; }
            if (start > end) { alert('开始日期不能晚于结束日期'); return; }

            document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
            calculateSummary(start, end);
        }

        function calculateSummary(start, end) {
            let totalIncome = 0;
            let totalExpense = 0;
            const incomeDetails = {};
            const expenseDetails = {};

            records.forEach(rec => {
                if (rec.date >= start && rec.date <= end) {
                    if (rec.type === 'income') {
                        totalIncome += rec.amount;
                        incomeDetails[rec.desc] = (incomeDetails[rec.desc] || 0) + rec.amount;
                    } else {
                        totalExpense += rec.amount;
                        expenseDetails[rec.desc] = (expenseDetails[rec.desc] || 0) + rec.amount;
                    }
                }
            });

            // 更新卡片
            document.getElementById('sumIncome').textContent = formatMoney(totalIncome);
            document.getElementById('sumExpense').textContent = formatMoney(totalExpense);
            const balance = totalIncome - totalExpense;
            const balanceEl = document.getElementById('sumBalance');
            balanceEl.textContent = formatMoney(balance);
            balanceEl.className = 'value ' + (balance >= 0 ? 'income-text' : 'expense-text');

            // 更新明细列表
            const detailEl = document.getElementById('sumDetails');
            if (Object.keys(incomeDetails).length === 0 && Object.keys(expenseDetails).length === 0) {
                detailEl.innerHTML = `<div class="empty-state">该时间段内没有记录</div>`;
                return;
            }

            let detailHtml = '';
            if (Object.keys(incomeDetails).length > 0) {
                detailHtml += `<div style="font-weight:600; margin: 0.5rem 0; color:#0b8e5e;">收入项</div>`;
                for (let [k, v] of Object.entries(incomeDetails).sort((a,b) => b[1]-a[1])) {
                    detailHtml += `<div class="detail-row"><span>${k}</span><span class="income-text">${formatMoney(v)}</span></div>`;
                }
            }
            if (Object.keys(expenseDetails).length > 0) {
                detailHtml += `<div style="font-weight:600; margin: 1rem 0 0.5rem; color:#b91c1c;">支出项</div>`;
                for (let [k, v] of Object.entries(expenseDetails).sort((a,b) => b[1]-a[1])) {
                    detailHtml += `<div class="detail-row"><span>${k}</span><span class="expense-text">${formatMoney(v)}</span></div>`;
                }
            }
            detailEl.innerHTML = detailHtml;
        }

        // ---------------- 数据导入/导出 ----------------
        function exportData() {
            if (records.length === 0) { alert('暂无数据可导出'); return; }
            const dataStr = JSON.stringify(records, null, 2);
            const blob = new Blob([dataStr], { type: "application/json" });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `记账备份_${getTodayStr()}.json`;
            a.click();
            URL.revokeObjectURL(url);
        }

        function importData(event) {
            const file = event.target.files[0];
            if (!file) return;
            const reader = new FileReader();
            reader.onload = function(e) {
                try {
                    const imported = JSON.parse(e.target.result);
                    if (Array.isArray(imported)) {
                        if (confirm(`读取到 ${imported.length} 条记录。\\n是否与当前数据进行合并?(点击取消将覆盖当前数据)`)) {
                            // 合并去重
                            const existingIds = new Set(records.map(r => r.id));
                            imported.forEach(r => { if(!existingIds.has(r.id)) records.push(r); });
                        } else {
                            // 覆盖
                            records = imported;
                        }
                        saveRecords();
                        renderRecordList();
                        alert('导入成功!');
                    } else {
                        alert('文件格式错误:不是有效的记账数据');
                    }
                } catch (err) {
                    alert('解析文件失败,请确保是导出的 JSON 文件');
                }
            };
            reader.readAsText(file);
            event.target.value = ''; // 清空 input,允许重复选文件
        }

        // ---------------- 初始化 ----------------
        renderRecordList();

    </script>
</body>
</html>