当前位置: 首页 > news >正文

用前端(HTML+Node.js)实现物品借用登记:完整代码示例

在日常工作中,经常需要借用各种办公或实验设备、工具及耗材。为了方便管理借用记录、防止物品丢失,同时提高办公效率,我设计了一个前端小程序——物品借用登记系统。该系统支持记录借用人、物品名称、数量、借用说明和日期,并可实时显示借用状态,同时提供归还和删除操作。整个程序基于 HTML、CSS 和 JavaScript 实现,无需复杂环境配置,即可在内部网页中展示使用效果,非常适合研发团队内部管理和演示。

页面结构与样式

HTML 文档声明与语言设置
使用 HTML5,并设置 lang="zh",保证中文显示正常。

头部信息 ()

字符编码 UTF-8,设置页面标题。

内嵌 CSS 样式:

页面整体居中、最大宽度 1080px、字体为 Arial。

表单和表格设计:背景色、圆角、间距、边框及文字对齐。

表格列宽固定、文字换行处理,日期输入框和按钮样式优化。

页面主体内容

标题
居中显示“研发部物品借用登记表”,突出页面用途。

导航按钮
提供“查看未归还记录”和“查看已归还记录”的快速跳转,便于数据筛选。

借用表单

输入项包括:借用人、物品名称、数量、借用说明、借用日期。

借用日期默认显示当前日期。

提交按钮居中,点击提交触发表单逻辑。

记录展示表格

表头列:借用人、物品、数量、借用说明、借用日期、归还日期、状态、操作。

表格主体动态生成,每条记录可执行操作:

归还:未归还记录可选择归还日期并标记已归还。

删除:需要管理员密码确认后才能删除。

前端 JavaScript 功能

初始化
页面加载时自动将借用日期设置为当前日期。

加载记录 (loadRecords)

向后端请求记录数据。

根据状态显示不同操作控件:归还按钮或只显示归还日期。

数据倒序显示,最新记录在前。

删除记录 (deleteRecord)

弹出密码提示,验证后再确认删除。

删除操作通过 Fetch API 请求后端接口。

归还操作 (returnItem)

选择归还日期,发送请求更新记录状态为“已归还”。

表单提交

验证必填信息完整性。

构建记录对象,初始状态为“未归还”,发送到后端。

提交成功后清空表单并刷新记录列表。

数据与状态管理

记录对象结构

name:借用人

item:物品名称

quantity:数量

remark:借用说明

borrowDate:借用日期

returnDate:归还日期

status:借用状态(未归还/已归还)

前端索引

每条记录会添加 _originalIndex 属性,用于操作对应后端记录。

交互逻辑

所有操作(提交、归还、删除)均通过 Fetch API 与后端通信。

页面实时更新,保证数据一致性与操作安全性。

总结

这个程序实现了借用登记、归还管理和数据展示的全流程功能,界面简洁、操作直观,具有以下特点:

易用性:表单简洁,操作逻辑清晰。

实时性:操作后自动刷新列表,无需手动刷新页面。

安全性:删除记录需要管理员密码确认,避免误操作。

前端实现:完全基于 HTML、CSS 和 JavaScript,无需额外依赖,适合内部网页或博客展示。

总结:该系统可作为研发部门日常物品借用管理的轻量级解决方案,可以部署在Windows系统内,进行局域网管理统计登记。

主页面代码如下:

<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8" /><title>物品借用登记</title><style>body { font-family: Arial, sans-serif; padding: 20px; max-width: 1080px; margin: auto; }h2 { text-align: center; margin-bottom: 20px; }form { margin-bottom: 20px; background: #f5f5f5; padding: 15px; border-radius: 8px; }.form-group { display: flex; flex-direction: column; margin-bottom: 12px; }label { margin-bottom: 6px; font-weight: bold; }input, button { padding: 8px; font-size: 14px; }button { margin-top: 10px; cursor: pointer; }table { border-collapse: collapse; width: 100%; table-layout: fixed;}th, td { border: 1px solid #ccc;padding: 8px;text-align: center;white-space: normal;      word-break: break-word;   }th { background-color: #f0f0f0;}th:nth-child(1), td:nth-child(1) { width: 60px; }th:nth-child(2), td:nth-child(2) { width: 200px; }th:nth-child(3), td:nth-child(3) { width: 40px; }        th:nth-child(4), td:nth-child(4) { width: 300px; text-align:left; }    input[type="date"] { width: 65%; }.action-btn { margin: 2px; }</style>
</head>
<body><h2>研发部物品借用登记表</h2><!-- 查看未归还/已归还按钮 --><div style="text-align:center; margin-bottom: 20px;"><button onclick="window.location.href='not-returned.html'">查看未归还记录</button><button onclick="window.location.href='returned.html'">查看已归还记录</button></div><form id="borrowForm"><div class="form-group"><label for="name">借用人</label><input type="text" id="name" required /></div><div class="form-group"><label for="item">物品名称</label><input type="text" id="item" required /></div><div class="form-group"><label for="quantity">数量</label><input type="number" id="quantity" min="1" required /></div><div class="form-group"><label for="remark">借用说明</label><input type="text" id="remark" /></div><div class="form-group"><label for="borrowDate">借用日期</label><input type="date" id="borrowDate" style="display:block;margin:0 auto;width:20%; text-align:center;" required /></div><button type="submit" style="display:block;margin:0 auto">提交</button></form><table id="recordTable"><thead><tr><th>借用人</th><th>物品</th><th>数量</th><th>借用说明</th><th>借用日期</th><th>归还日期</th><th>状态</th><th>操作</th></tr></thead><tbody></tbody></table><script>const form = document.getElementById('borrowForm');const tableBody = document.querySelector('#recordTable tbody');document.getElementById('borrowDate').valueAsDate = new Date();function loadRecords() {fetch('/api/records').then(res => res.json()).then(data => {tableBody.innerHTML = '';// 给每条记录添加原数组索引data.forEach((record, idx) => { record._originalIndex = idx; });// 倒序显示data.slice().reverse().forEach((record, index) => {const row = document.createElement('tr');let returnDateInput = '';let returnButton = '';if (record.status === '已归还') {returnDateInput = record.returnDate || '';} else {returnDateInput = `<input type="date" id="returnDate-${index}" />`;returnButton = `<button class="action-btn" onclick="returnItem(${record._originalIndex}, this)">归还</button>`;}row.innerHTML = `<td>${record.name}</td><td>${record.item}</td><td>${record.quantity}</td><td>${record.remark || ''}</td><td>${record.borrowDate}</td><td>${returnDateInput}</td><td>${record.status || '未归还'}</td><td>${returnButton}<button class="action-btn" onclick="deleteRecord(${record._originalIndex})">删除</button></td>`;tableBody.appendChild(row);});}).catch(err => alert('加载数据失败: ' + err.message));}function deleteRecord(index) {const password = prompt('请输入管理员密码进行删除:');if (password === null) return;const correctPassword = '19980216';if (password !== correctPassword) { alert('密码错误'); return; }if (!confirm('确定删除这条记录吗?')) return;fetch(`/api/records/${index}`, { method: 'DELETE' }).then(() => loadRecords()).catch(() => alert('删除失败'));}function returnItem(index, button) {const dateInput = document.getElementById(`returnDate-${index}`);const returnDate = dateInput.value;if (!returnDate) { alert('请选择归还日期'); return; }button.disabled = true;fetch(`/api/records/${index}/return`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ returnDate })}).then(() => loadRecords()).catch(() => { alert('归还失败'); button.disabled = false; });}form.addEventListener('submit', e => {e.preventDefault();const name = document.getElementById('name').value.trim();const item = document.getElementById('item').value.trim();const remark = document.getElementById('remark').value.trim();const quantity = parseInt(document.getElementById('quantity').value);const borrowDate = document.getElementById('borrowDate').value;if (!name || !item || !quantity || !borrowDate) {alert('请填写完整信息');return;}const record = { name, item, quantity, remark, borrowDate, returnDate:'', status:'未归还' };fetch('/api/records', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(record) }).then(() => { form.reset(); document.getElementById('borrowDate').valueAsDate = new Date(); loadRecords(); }).catch(() => alert('提交失败'));});loadRecords();</script>
</body>
</html>

未归还记录页面如下

<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>未归还记录</title><style>body { font-family: Arial, sans-serif; padding: 20px; max-width: 1080px; margin: auto; }h2 { text-align: center; margin-bottom: 20px; }table { border-collapse: collapse; width: 100%; table-layout: fixed; background: #fff;}th, td { border: 1px solid #ccc;padding: 8px;text-align: center;white-space: normal;word-break: break-word; }th { background-color: #f0f0f0; }th:nth-child(1), td:nth-child(1) { width: 60px; }th:nth-child(2), td:nth-child(2) { width: 200px; }th:nth-child(3), td:nth-child(3) { width: 40px; }th:nth-child(4), td:nth-child(4) { width: 300px; text-align:left; }th:nth-child(5), td:nth-child(5) { width: 100px; }th:nth-child(6), td:nth-child(6) { width: 100px; }button { padding: 4px 8px; cursor: pointer; }.action-btn { margin: 2px; }.center-btn { display:block; margin:0 auto; }</style>
</head>
<body><h2>未归还记录</h2><div style="text-align:center; margin-bottom: 20px;"><button onclick="window.location.href='index.html'">返回主页面</button></div><table id="recordTable"><thead><tr><th>借用人</th><th>物品</th><th>数量</th><th>借用说明</th><th>借用日期</th><th>归还日期</th><th>状态</th><th>操作</th></tr></thead><tbody></tbody></table><script>const tableBody = document.querySelector('#recordTable tbody');function loadRecords() {fetch('/api/records').then(res => res.json()).then(data => {tableBody.innerHTML = '';const notReturned = data.filter(record => record.status === '未归还');notReturned.forEach((record, index) => {const row = document.createElement('tr');row.innerHTML = `<td>${record.name}</td><td>${record.item}</td><td>${record.quantity}</td><td>${record.remark || ''}</td><td>${record.borrowDate}</td><td><input type="date" id="returnDate-${index}" /></td><td>${record.status}</td><td><button class="action-btn" onclick="returnItem(${index}, '${record.name}', '${record.item}')">归还</button></td>`;tableBody.appendChild(row);});}).catch(err => alert('加载数据失败: ' + err.message));}function returnItem(index, name, item) {const dateInput = document.getElementById(`returnDate-${index}`);const returnDate = dateInput.value;if (!returnDate) { alert('请选择归还日期'); return; }// 获取完整记录索引fetch('/api/records').then(res => res.json()).then(data => {const recordIndex = data.findIndex(r => r.name === name && r.item === item && r.status === '未归还');if (recordIndex === -1) { alert('未找到记录'); return; }fetch(`/api/records/${recordIndex}/return`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ returnDate })}).then(res => res.json()).then(() => loadRecords()).catch(() => { alert('归还失败'); });});}loadRecords();</script>
</body>
</html>

已归还页面代码如下:

<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8" /><title>已归还记录</title><style>body { font-family: Arial, sans-serif; padding: 20px; max-width: 1080px; margin: auto; }h2 { text-align: center; margin-bottom: 20px; }table { border-collapse: collapse; width: 100%; table-layout: fixed;}th, td { border: 1px solid #ccc;padding: 8px;text-align: center;white-space: normal;word-break: break-word; }th { background-color: #f0f0f0;}th:nth-child(1), td:nth-child(1) {width: 60px;}th:nth-child(2), td:nth-child(2) {width: 200px;}th:nth-child(3), td:nth-child(3) {width: 40px;}		th:nth-child(4), td:nth-child(4) {width: 300px;text-align:left;}	</style>
</head>
<body><h2>已归还记录</h2><div style="text-align:center; margin-bottom: 20px;"><button onclick="window.location.href='index.html'">返回主页面</button></div><table id="recordTable"><thead><tr><th>借用人</th><th>物品</th><th>数量</th><th>借用说明</th><th>借用日期</th><th>归还日期</th><th>状态</th></tr></thead><tbody></tbody></table><script>const tableBody = document.querySelector('#recordTable tbody');function loadRecords() {fetch('/api/records').then(res => res.json()).then(data => {tableBody.innerHTML = '';const returned = data.filter(record => record.status === '已归还');returned.forEach(record => {const row = document.createElement('tr');row.innerHTML = `<td>${record.name}</td><td>${record.item}</td><td>${record.quantity}</td><td>${record.remark || ''}</td><td>${record.borrowDate}</td><td>${record.returnDate || ''}</td><td>${record.status}</td>`;tableBody.appendChild(row);});}).catch(err => alert('加载数据失败: ' + err.message));}loadRecords();</script>
</body>
</html>

Express 框架搭建nodejs服务后端代码

const express = require('express');
const fs = require('fs');
const path = require('path');const app = express();
const PORT = 3000;
const DATA_FILE = path.join(__dirname, 'data.json');app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));// 读取数据
function readDataFile() {if (!fs.existsSync(DATA_FILE)) return [];try {const data = fs.readFileSync(DATA_FILE, 'utf-8').trim();if (!data) return [];  // 如果文件为空,返回空数组return JSON.parse(data);} catch (err) {console.error('读取文件出错:', err);return [];}
}// 写入数据
function writeDataFile(data) {fs.writeFileSync(DATA_FILE, JSON.stringify(data, null, 2), 'utf-8');
}// 获取所有记录
app.get('/api/records', (req, res) => {res.json(readDataFile());
});// 添加记录
app.post('/api/records', (req, res) => {const { name, item, quantity, borrowDate, remark } = req.body;if (!name || !item || !borrowDate) {return res.status(400).json({ error: '缺少字段' });}const records = readDataFile();records.push({name,item,quantity: quantity || 1,borrowDate,remark: remark || '',returnDate: '',status: '未归还'});writeDataFile(records);res.json({ status: 'success' });
});// 删除记录
app.delete('/api/records/:index', (req, res) => {const index = parseInt(req.params.index);const records = readDataFile();if (isNaN(index) || index < 0 || index >= records.length) {return res.status(400).json({ error: '索引无效' });}records.splice(index, 1);writeDataFile(records);res.json({ status: 'deleted' });
});// 归还记录
app.post('/api/records/:index/return', (req, res) => {const index = parseInt(req.params.index);const { returnDate } = req.body;const records = readDataFile();if (!returnDate || isNaN(index) || index < 0 || index >= records.length) {return res.status(400).json({ error: '无效请求' });}records[index].returnDate = returnDate;records[index].status = '已归还';writeDataFile(records);res.json({ status: 'updated' });
});app.listen(PORT, () => {console.log(`Server running at http://localhost:${PORT}`);
});
http://www.rkmt.cn/news/10277.html

相关文章:

  • Qt - 音视频采集
  • PCI-SIG的成员
  • 2025年9月16日纸质证书 - 高同学PostgreSQL管理员(中级)认证
  • 无法用sudo 启动 gparted
  • Jupyter Notebook的交互式编写环境方便py开发
  • 深度解析Playwright MCP:功能、优势与挑战,AI如何提升测试效率与覆盖率
  • 使用 Playwright MCP 实现小红书全自动发布的完整流程
  • CentOS 7下载教程vmware虚拟机安装centos 7保姆级安装步骤(附安装包) - 教程
  • 数字孪生 + 区块链:MyEMS 引领能源管理技术融合新趋势
  • Python 完成 Markdown 与 Word 高保真互转(含批量转换)
  • WPF Prism
  • PyTorch和cude版本不兼容导致无法检测到GPU
  • 2025无人机在低空物流中的应用实践
  • ssh如何打开可视化界面
  • 深入解析:MES系统在不同制造行业中的应用差异与共性
  • 使用AWS Amplify、Lambda、API Gateway和DynamoDB部署静态Web应用
  • 详细介绍:广告点击场景下的 YOLO-world 模型训练失败及其解决办法
  • 【深度学习计算机视觉】06:目标检测内容集
  • debian13 无法正常从休眠中恢复
  • 全差分放大器(FDA)电路设计计算问题及电压范围估算[原创www.cnblogs.com/helesheng]
  • 详细介绍:Java与Vue构建资产设备全周期管理系统,覆盖采购、入库、使用、维护至报废全流程,支持移动端实时操作与后台智能管理,提供完整源码便于二次开发
  • 使用WTAPI开发智能微信机器人文档
  • Zabbix7 监控USG6300E 并发IPv4会话数 - 教程
  • 详细介绍:C 语言:第 20 天笔记:typedef(类型重命名规则、应用场景与实战案例)
  • 企业如何选择适合自身行业的ERP系统?
  • Screaming Architecture:让架构自己说话
  • BOE(京东方)携手UNESCO联合主办WCBR“科学十年”分会 彰显中国科技企业可持续发展实力
  • 二级制流量算法熵值计算,N-Gram 算法(二:改进) - 教程
  • 随笔-决战保研篇
  • 【F#学习】列表 List