html记账本改写:保存数据 localStorage。
<!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开始,需要加1let 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>
相关文章:

html记账本改写:保存数据 localStorage。
<!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…...
frida检测绕过-libmsaoaidsec.so
libmsaoaidsec.so 部分检测手段 检测机制在native层实现一般在init_proc()函数中触发使用 pthread_create 创建2个检测线程 绕过: nop pthread_create 的调用 eg: 在 bilibil1 - v7.26.1版本中, 在got表导入了pthread_create 绕过: 替换dlsym(xx, "pthread_create "…...

Splasthop 安全远程访问帮助企业对抗 Cobalt Strike 载荷网络攻击
一、背景 根据 FreeBuf(标题为:潜藏系统2个月未被发现,新型网络攻击瞄准中国高价值目标)和 The Hacker News(标题为:New Cyberattack Targets Chinese-Speaking Businesses with Cobalt Strike Payloads&a…...
Rust:Restful API 服务程序开发详述
0. 关于异步程序设计 0.1 对异步机制的理解 运行效率对于后端程序来讲很重要。我曾经以为,多线程机制是后端设计的终极方法,后来才发现,异步机制才是榨干 CPU 运行效率资源的关键所在。 我最初对于异步程序设计有误解,以为多线…...

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 09部署OSPF
本章的目的是帮助网络工程师确定网络的理想 OSPF 配置。本章将回答以下问题 应何时在数据中使用OSPF ?配置 OSPF 的关键设计原则是什么?OSPFv2 和 OSPFv3 之间有什么区别,应如何使用?如何在路由协议栈中配置 OSPF ?如何在服务器上配置 OSPF,例如为容…...

【Visual Studio 报错】未加载 wntdll.pdb(一种可行的解决办法)
调试程序时,会出现下面这个报错 分析原因: 出现未加载 wntdll.pdb 报错大概率是你的指针使用错误 ,比如使用野指针、越界访问、或者堆区空间释放方式错误等。 这里以 堆区空间释放方式错误 为例子 1、堆区开辟的数组空间使用 delete 释放 …...
P1332 血色先锋队
[题目通道](血色先锋队 - 洛谷) #include<bits/stdc.h> using namespace std; int n,m,a,b,xa[114514],ya[114514],xb[114514],yb[114514],maxx[114514]; int main() {cin>>n>>m>>a>>b;for(int i1;i<a;i)cin>>xa[i]>>ya[i];for(…...

HarmonyOS】ArkTS学习之基于TextTimer的简易计时器的elapsedTime最小时间单位问题
本文旨在纪录自己对TextTimer使用过程的疑惑问题 我在查看教程时候,发现很多博客在onTimer(event: (utc: number, elapsedTime: number) > void) 这里提到elapsedTime:计时器经过的时间,单位为毫秒。我不清楚是否为版本问题。 在我查看ver…...

函数指针学习
认识函数指针: 函数指针的用处: 回调函数(以函数指针为参数的参数) 定义带标签的结构体 typedef struct mode { int data; } Node; 标签(mode): mode 是结构体的标签名。在这种定义中,mo…...

『功能项目』武器的切换实例【34】
本章项目成果展示 我们打开上一篇33战士的A键连击的项目, 本章要做的事情是按键盘E键切换职业时切换手中的武器 首先在资源商店下载免费的武器模型 创建一个空物体 命名为WeaponPos 将武器预制体拖拽至WeaponPos (注意调整空物体位置就可以后续文章会更…...

github中action作用和讲解
1,简介 GitHub Actions 是 GitHub 的一个自动化功能,它允许你在 GitHub 仓库中自动执行软件开发工作流程。你可以使用 GitHub Actions 来执行各种任务,比如: 自动测试:每当代码被推送到仓库时,自动运行测试…...

数据库管理-第238期 23ai:全球分布式数据库-架构与组件(20240904)
数据库管理238期 2024-09-04 数据库管理-第238期 23ai:全球分布式数据库-架构与组件(20240904)1 架构图2 分片数据库与分片3 Shard Catalog4 Shard Director5 Global Service6 管理界面总结 数据库管理-第238期 23ai:全球分布式数…...

GIT | git提交注释自动添加信息头
GIT | git提交注释自动添加信息头 时间:2024年9月6日10:20:11 文章目录 GIT | git提交注释自动添加信息头1.操作2.commit-msg文件 1.操作 2.commit-msg文件 #!/bin/sh # # An example hook script to check the commit log message. # Called by "git commit&q…...
React 全屏问题解决方案
1、全屏下弹窗被遮挡的问题 参考:https://www.jianshu.com/p/b22d1ad9533e 原因: 需要全屏的节点部分被传入 screenfull 中,弹窗的层级永远低于全屏,所以被遮挡。 解决方法: 方式1:把整个 body 全屏&…...

Java JVM 垃圾回收算法详解
Java 虚拟机(JVM)是运行 Java 应用程序的核心,它的垃圾回收(Garbage Collection, GC)机制是 JVM 中非常重要的一个部分。垃圾回收的主要任务是自动管理内存,回收那些不再被使用的对象,从而释放内…...
hadoop dfs web页面访问增加鉴权
前言 装好了Hadoop,通过浏览器访问,发现竟然不需要鉴权就能访问,且暴露了很多服务器层文件路径信息,基于多年积累的安全意识,必须得配置些鉴权信息,就有了该文,仅做学习记录,下次自…...
LCP 485. 最大连续 1 的个数[lleetcode -11]
从今天起,我们的算法开始研究搜索,首先就是DFS深度优先搜索(depth-first seach,DFS)在搜索到一个新的节点时,立即对该新节点进行遍 历;因此遍历需要用先入后出的栈来实现,也可以通过…...
关于宏任务的说法已经过时
关于宏任务w3c的最新解释,(mdn已经搜不到宏任务队列) ● 每个任务都有一个任务类型,用一个类型的任务必须在一个队列,不同类型的任务可以分属不同的队列。在一次事件循环当中,浏览器可以根据实际情况从不同…...

Java箱与泛型
大O的渐进表示法 大 O 的渐进表示法 去掉了那些对结果影响不大的项 ,简洁明了的表示出了执行次数。 void func1(int N){ int count 0; for (int i 0; i < N ; i) { for (int j 0; j < N ; j) { count; } } for (int k 0; k < 2 * N ; k) { count; } in…...
QT如何判断一个文件是否存在
在Qt中,判断一个文件是否存在是一个常见的操作,可以通过QFile类和QDir类来实现。不过,对于简单的文件存在性检查,QFile类提供的接口更为直接。下面是一个使用QFile类来判断文件是否存在的例子: #include <QFile>…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...