html记账本改写:保存数据 localStorage。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>记账本改写</title>
    <style>
        table {
            user-select: none;
            /* width: 100%; */
            border-collapse: collapse;
        }
        table,
        th,
        td {
            border: 1px solid black;
        }
        th,
        td {
            padding: 8px;
            text-align: center;
        }
    </style>
</head>
<body>
    <table id="ledgerTable">
        <thead>
            <tr>
                <th style="width: 50px;"><input type="month" id="monthPicker"></th>
                <th colspan="3">日常开销</th>
            </tr>
            <tr>
                <th>日期</th>
                <th>项目</th>
                <th>支出</th>
                <th>收入</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
        <tfoot>
            <tr>
                <td><strong>总和</strong></td>
                <td id="totalAmount">0</td>
                <td id="totalExpense">0</td>
                <td id="totalIncome">0</td>
            </tr>
        </tfoot>
    </table>
    <script>
        let currentYear, currentMonth;
        document.getElementById('monthPicker').addEventListener('change', function () {
            let selectedDate = new Date(this.value);
            currentYear = selectedDate.getFullYear();
            currentMonth = selectedDate.getMonth() + 1; // 月份从0开始,需要加1
            let daysInMonth = new Date(currentYear, currentMonth, 0).getDate();
            let tbody = document.querySelector('#ledgerTable tbody');
            // 清空tbody中的所有行
            tbody.innerHTML = '';
            // 为每一天添加行
            for (let day = 1; day <= daysInMonth; day++) {
                let newRow = document.createElement('tr');
                newRow.innerHTML = `
                    <td>${day}</td>
                    <td>
                        <button class="add-item">+</button>
                    </td>
                    <td>0</td>
                    <td>0</td>
                `;
                newRow.querySelector('.add-item').addEventListener('click', addItemHandler);
                tbody.appendChild(newRow);
            }
            // 从localStorage加载数据
            loadData();
            // 初始化总和
            updateTotals();
        });
        // 添加项目的事件处理函数
        function addItemHandler() {
            const newItem = createItemElement({ name: '', expense: '', income: '' });
            this.parentNode.insertBefore(newItem, this);
            updateDailyTotals(this.closest('tr'));
            updateTotals(); // 更新总和
            saveData(); // 保存数据
        }
        // 创建项目元素的函数
        function createItemElement(item) {
            const newItem = document.createElement('div');
            newItem.innerHTML = `
                <input type="text" placeholder="项目" value="${item.name}">
                <select class="type-select">
                    <option value="expense">支出</option>
                    <option value="income">收入</option>
                </select>
                <input type="number" placeholder="金额" style="width: 70px;" value="${item.expense || item.income || ''}">
                <button class="remove-item">-</button>
            `;
            // 获取金额输入框和类型选择框
            const amountInput = newItem.querySelector('input[type="number"]');
            const typeSelect = newItem.querySelector('.type-select');
            // 添加事件监听器
            addEventListeners(amountInput, typeSelect, newItem);
            // 给"-"按钮添加事件监听
            newItem.querySelector('.remove-item').addEventListener('click', function () {
                const row = this.closest('tr');
                // 弹出确认对话框
                const confirmDelete = confirm("确定要删除此项目吗?");
                if (confirmDelete) {
                    this.parentNode.remove();
                    updateDailyTotals(row);
                    updateTotals(); // 更新总和
                    saveData(); // 保存数据
                }
            });
            return newItem;
        }
        // 添加事件监听器的函数
        function addEventListeners(amountInput, typeSelect, newItem) {
            // 监听金额输入框的变化
            amountInput.addEventListener('input', function () {
                if (typeSelect.value === 'expense' && this.value > 0) {
                    this.value = -this.value; // 如果选择"支出"且金额为正数,自动转换为负数
                }
                updateDailyTotals(newItem.closest('tr')); // 更新每日总和
                updateTotals(); // 更新总和
                saveData(); // 保存数据
            });
            // 监听类型选择框的变化
            typeSelect.addEventListener('change', function () {
                if (this.value === 'expense') {
                    // 如果选择"支出",确保金额为负数
                    if (amountInput.value > 0) {
                        amountInput.value = -amountInput.value;
                    }
                    amountInput.min = '-999999';
                    amountInput.max = '0';
                } else if (this.value === 'income') {
                    // 如果选择"收入",确保金额为正数
                    if (amountInput.value < 0) {
                        amountInput.value = -amountInput.value;
                    }
                    amountInput.min = '0';
                    amountInput.max = '999999';
                }
                updateDailyTotals(newItem.closest('tr')); // 更新每日总和
                updateTotals(); // 更新总和
                saveData(); // 保存数据
            });
        }
        // 更新每日总和的函数
        function updateDailyTotals(row) {
            const items = row.querySelectorAll('div');
            let expenseTotal = 0;
            let incomeTotal = 0;
            items.forEach(item => {
                const amountInput = item.querySelector('input[type="number"]');
                const amount = parseFloat(amountInput.value) || 0;
                if (amount < 0) {
                    expenseTotal += amount;
                } else {
                    incomeTotal += amount;
                }
            });
            row.querySelectorAll('td')[2].textContent = expenseTotal;
            row.querySelectorAll('td')[3].textContent = incomeTotal;
        }
        // 更新总和的函数
        function updateTotals() {
            const rows = document.querySelectorAll('#ledgerTable tbody tr');
            let totalExpense = 0;
            let totalIncome = 0;
            rows.forEach(row => {
                const expenseCell = row.querySelectorAll('td')[2];
                const incomeCell = row.querySelectorAll('td')[3];
                totalExpense += parseFloat(expenseCell.textContent) || 0;
                totalIncome += parseFloat(incomeCell.textContent) || 0;
            });
            document.getElementById('totalExpense').textContent = totalExpense;
            document.getElementById('totalIncome').textContent = totalIncome;
            document.getElementById('totalAmount').textContent = totalIncome + totalExpense;
        }
        // 保存数据的函数
        function saveData() {
            if (!currentYear || !currentMonth) return;
            const key = `ledger_${currentYear}_${currentMonth}`;
            const data = [];
            const rows = document.querySelectorAll('#ledgerTable tbody tr');
            rows.forEach(row => {
                const day = row.querySelector('td').textContent;
                const items = row.querySelectorAll('div');
                items.forEach(item => {
                    const name = item.querySelector('input[type="text"]').value;
                    const type = item.querySelector('.type-select').value;
                    const amount = item.querySelector('input[type="number"]').value;
                    data.push({ day, name, type, amount });
                });
            });
            localStorage.setItem(key, JSON.stringify(data));
        }
        // 加载数据的函数
        function loadData() {
            if (!currentYear || !currentMonth) return;
            const key = `ledger_${currentYear}_${currentMonth}`;
            const data = JSON.parse(localStorage.getItem(key)) || [];
            const rows = document.querySelectorAll('#ledgerTable tbody tr');
            data.forEach(item => {
                const row = Array.from(rows).find(row => row.querySelector('td').textContent === item.day);
                if (row) {
                    const newItem = createItemElement({ name: item.name, expense: item.type === 'expense' ? item.amount : '', income: item.type === 'income' ? item.amount : '' });
                    row.querySelector('td:nth-child(2)').insertBefore(newItem, row.querySelector('.add-item'));
                }
            });
            rows.forEach(row => updateDailyTotals(row));
            updateTotals();
        }
    </script>
</body>
</html>
相关推荐
逐·風3 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫3 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
尚梦4 小时前
uni-app 封装刘海状态栏(适用小程序, h5, 头条小程序)
前端·小程序·uni-app
GIS程序媛—椰子4 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
毕业设计制作和分享5 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
从兄6 小时前
vue 使用docx-preview 预览替换文档内的特定变量
javascript·vue.js·ecmascript
清灵xmf7 小时前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨7 小时前
VUE+Vite之环境文件配置及使用环境变量
前端
GDAL7 小时前
npm入门教程1:npm简介
前端·npm·node.js