结婚礼金红白喜事记账系统源码分享~
引言如下:
起因,想要把纸质礼金簿的收礼信息导入Excel里面方便查询,随机在Excel里面寻找了几个模板,尝试了一下发现效果都不是很好,后来想到了做成网页通过存取数据库来实现增删查改等功能, 这样的效果好像比Excel的方式更便捷和高效于是就有了下面这篇文章。
第一步
我们是基于数据库来存取信息,所以我们先新建一个数据库,我们保存好数据库名、用户名、和密码后续需要用到。
1.打开新建数据库
打开在线的phpmyadmin进入数据库
2. 创建表
在数据库下,点击“SQL”选项卡。
在SQL查询框中输入以下SQL语句来创建表
命令如下
CREATE TABLE IF NOT EXISTS employees ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255), amount DECIMAL(10, 2), category VARCHAR(255), note VARCHAR(255) );
第二步
完成上述第一步之后,现在我们需要实现对数据库在线增删数据以及统计数据和下载导出,那么我们需要至少4个功能,分别是增加、删除、查询、导出。
那么我们着手开始配置。我们选取最简单的方式来处理这些简单的操作,这样即可以不用配置环境还能对数据库的修改。
项目文件目录如下(请按照要求新建)
项目根目录 │ ├── add │ ├── add_employee.php │ ├── delete_employee.php │ ├── get_employees.php │ ├── index.html │ ├── update_employee.php │ └── xlsx.full.min.js │ └── chaxun ├── index.html └── query.php
接下来分享add目录下的文件代码
add_employee.php
<?php header('Content-Type: application/json; charset=utf-8'); // 数据库连接配置 $servername = "127.0.0.1"; $username = "步骤一中的用户名"; $password = "步骤一中的密码"; $dbname = "步骤一中数据库名"; // 创建连接 $conn = new mysqli($servername, $username, $password, $dbname); // 检查连接 if ($conn->connect_error) { die("连接失败: " . $conn->connect_error); } // 设置编码 mysqli_set_charset($conn, "utf8"); // 获取POST请求中的参数 $name = $_POST['name']; $amount = $_POST['amount']; $category = $_POST['category']; $note = $_POST['note']; // 准备 SQL 语句 $sql = "INSERT INTO employees (name, amount, category, note) VALUES (?, ?, ?, ?)"; $stmt = $conn->prepare($sql); $stmt->bind_param("sdss", $name, $amount, $category, $note); // 执行查询 if ($stmt->execute()) { $response = array("status" => "success", "message" => "记录已成功添加"); } else { $response = array("status" => "error", "message" => "记录添加失败: " . $stmt->error); } echo json_encode($response); // 关闭连接 $stmt->close(); $conn->close(); ?>
delete_employee.php
<?php header('Content-Type: application/json; charset=utf-8'); // 数据库连接配置 $servername = "127.0.0.1"; $username = "同上"; $password = "同上"; $dbname = "同上"; // 创建连接 $conn = new mysqli($servername, $username, $password, $dbname); // 检查连接 if ($conn->connect_error) { die("连接失败: " . $conn->connect_error); } // 设置编码 mysqli_set_charset($conn, "utf8"); $id = $_POST['id']; // 准备 SQL 语句 $sql = "DELETE FROM employees WHERE id = ?"; $stmt = $conn->prepare($sql); $stmt->bind_param("i", $id); // 执行查询 if ($stmt->execute()) { $response = array("status" => "success", "message" => "记录已成功删除"); } else { $response = array("status" => "error", "message" => "删除失败: " . $stmt->error); } echo json_encode($response); $stmt->close(); $conn->close(); ?>
get_employees.php
<?php header('Content-Type: application/json; charset=utf-8'); // 数据库连接配置 $servername = "127.0.0.1"; $username = "同上"; $password = "同上"; $dbname = "同上"; // 创建连接 $conn = new mysqli($servername, $username, $password, $dbname); // 检查连接 if ($conn->connect_error) { die("连接失败: " . $conn->connect_error); } // 设置编码 mysqli_set_charset($conn, "utf8"); // 查询所有员工 $sql = "SELECT * FROM employees ORDER BY id DESC"; $result = $conn->query($sql); $employees = array(); if ($result->num_rows > 0) { while($row = $result->fetch_assoc()) { $employees[] = $row; } } echo json_encode($employees); $conn->close(); ?>
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>礼金添加系统</title> <script src="./xlsx.full.min.js"></script> <style> :root { --primary-color: #4CAF50; --primary-hover: #45a049; --danger-color: #f44336; --danger-hover: #da190b; --gray-color: #808080; --gray-hover: #666666; } * { box-sizing: border-box; margin: 0; padding: 0; } body { max-width: 1200px; margin: 0 auto; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #f5f5f5; color: #333; } .container { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 20px; margin-bottom: 20px; } h2 { color: #2c3e50; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 2px solid var(--primary-color); } .stats { display: flex; gap: 20px; margin-bottom: 20px; flex-wrap: wrap; } .stat-card { flex: 1; min-width: 200px; background: #fff; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .stat-card .label { font-size: 0.9em; color: #666; } .stat-card .value { font-size: 1.5em; font-weight: bold; color: var(--primary-color); margin-top: 5px; } .message { margin: 10px 0; padding: 15px; border-radius: 4px; animation: fadeIn 0.3s ease-in; } .success { background-color: #dff0d8; color: #3c763d; border-left: 4px solid #3c763d; } .error { background-color: #f2dede; color: #a94442; border-left: 4px solid #a94442; } table { border-collapse: collapse; width: 100%; margin-top: 20px; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #ddd; } th { background-color: #f8f9fa; font-weight: 600; color: #2c3e50; } tr:hover { background-color: #f5f5f5; } .edit-input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } button { margin: 0 5px; padding: 8px 12px; cursor: pointer; border: none; border-radius: 4px; font-size: 14px; transition: all 0.3s ease; } button[onclick*="startEdit"] { background-color: var(--primary-color); color: white; } button[onclick*="startEdit"]:hover { background-color: var(--primary-hover); } button[onclick*="delete"] { background-color: var(--danger-color); color: white; } button[onclick*="delete"]:hover { background-color: var(--danger-hover); } button[onclick*="cancel"] { background-color: var(--gray-color); color: white; } button[onclick*="cancel"]:hover { background-color: var(--gray-hover); } form { background-color: #fff; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .form-group { margin-bottom: 15px; } .form-group label { display: block; margin-bottom: 5px; color: #2c3e50; font-weight: 500; } .form-group input { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; transition: border-color 0.3s ease; } .form-group input:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.1); } form input[type="submit"] { background-color: var(--primary-color); color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; width: auto; } form input[type="submit"]:hover { background-color: var(--primary-hover); } .serial-number { width: 50px; color: #666; font-weight: normal; } @media (max-width: 768px) { body { padding: 10px; } .container { padding: 15px; } .stat-card { min-width: 100%; } table { display: block; overflow-x: auto; white-space: nowrap; } th, td { padding: 8px 10px; } button { padding: 6px 10px; font-size: 13px; } } @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } .export-btn { background-color: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background-color 0.3s ease; } .export-btn:hover { background-color: #45a049; } .stat-card button { width: 100%; margin-top: 10px; } </style> </head> <body> <div> <h2>礼金添加系统</h2> <div> <div> <div>总人数</div> <div id="employeeCount">0 人</div> </div> <div> <div>总金额</div> <div id="totalAmount">¥0</div> </div> <div> <button onclick="exportToExcel()"> 导出Excel </button> </div> </div> <div id="message"></div> <form id="employeeForm"> <div> <label for="name">名字:</label> <input type="text" id="name" name="name" required> </div> <div> <label for="amount">金额:</label> <input type="number" id="amount" name="amount" required> </div> <div> <label for="category">区域:</label> <input type="text" id="category" name="category" required> </div> <div> <label for="note">备注:</label> <input type="text" id="note" name="note"> </div> <input type="submit" value="提交"> </form> <div id="employeeList"></div> </div> <script> // 加载员工列表 function loadEmployees() { fetch('get_employees.php') .then(response => response.json()) .then(data => { // 计算统计数据 const totalAmount = data.reduce((sum, emp) => sum + parseFloat(emp.amount), 0); const employeeCount = data.length; // 更新统计显示 document.getElementById('employeeCount').textContent = `${employeeCount} 人`; document.getElementById('totalAmount').textContent = `¥${totalAmount.toLocaleString('zh-CN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}`; const table = ` <table> <tr> <th>序号</th> <th>名字</th> <th>金额</th> <th>类别</th> <th>备注</th> <th>操作</th> </tr> ${data.map((employee, index) => ` <tr id="row-${employee.id}" data-id="${employee.id}"> <td>${index + 1}</td> <td>${employee.name}</td> <td>${parseFloat(employee.amount).toLocaleString('zh-CN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td> <td>${employee.category}</td> <td>${employee.note}</td> <td> <button onclick="startEdit(${employee.id})">编辑</button> <button onclick="deleteEmployee(${employee.id})">删除</button> </td> </tr> `).join('')} </table> `; document.getElementById('employeeList').innerHTML = table; }); } // 开始编辑 function startEdit(id) { const row = document.getElementById(`row-${id}`); const cells = row.getElementsByTagName('td'); // 保存原始数据 const originalData = { name: cells[1].textContent, amount: cells[2].textContent.replace(/[¥,]/g, ''), category: cells[3].textContent, note: cells[4].textContent }; row.setAttribute('data-original', JSON.stringify(originalData)); // 转换单元格为编辑模式 cells[1].innerHTML = `<input type="text" value="${originalData.name}" required>`; cells[2].innerHTML = `<input type="number" value="${originalData.amount}" required step="0.01">`; cells[3].innerHTML = `<input type="text" value="${originalData.category}" required>`; cells[4].innerHTML = `<input type="text" value="${originalData.note}">`; cells[5].innerHTML = ` <button onclick="saveEdit(${id})">完成</button> <button onclick="cancelEdit(${id})">取消</button> `; } // 保存编辑 function saveEdit(id) { const row = document.getElementById(`row-${id}`); const inputs = row.getElementsByClassName('edit-input'); // 验证必填字段 if (!inputs[0].value || !inputs[1].value || !inputs[2].value) { alert('请填写所有必填字段(名字、金额、类别)'); return; } const formData = new FormData(); formData.append('id', id); formData.append('name', inputs[0].value); formData.append('amount', inputs[1].value); formData.append('category', inputs[2].value); formData.append('note', inputs[3].value); fetch('update_employee.php', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { const messageDiv = document.getElementById('message'); messageDiv.className = `message ${data.status}`; messageDiv.textContent = data.message; if(data.status === 'success') { loadEmployees(); // 3秒后清除消息 setTimeout(() => { messageDiv.textContent = ''; messageDiv.className = ''; }, 3000); } }); } // 取消编辑 function cancelEdit(id) { const row = document.getElementById(`row-${id}`); const originalData = JSON.parse(row.getAttribute('data-original')); const cells = row.getElementsByTagName('td'); cells[1].innerHTML = originalData.name; cells[2].innerHTML = parseFloat(originalData.amount).toLocaleString('zh-CN', { style: 'currency', currency: 'CNY', minimumFractionDigits: 2, maximumFractionDigits: 2 }).replace('CN¥', '¥'); cells[3].innerHTML = originalData.category; cells[4].innerHTML = originalData.note; cells[5].innerHTML = ` <button onclick="startEdit(${id})">编辑</button> <button onclick="deleteEmployee(${id})">删除</button> `; } // 删除员工 function deleteEmployee(id) { if(confirm('确定要删除这条记录吗?')) { fetch('delete_employee.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: `id=${id}` }) .then(response => response.json()) .then(data => { const messageDiv = document.getElementById('message'); messageDiv.className = `message ${data.status}`; messageDiv.textContent = data.message; if(data.status === 'success') { loadEmployees(); // 3秒后清除消息 setTimeout(() => { messageDiv.textContent = ''; messageDiv.className = ''; }, 3000); } }); } } // 表单提交 document.getElementById('employeeForm').onsubmit = function(e) { e.preventDefault(); const formData = new FormData(this); fetch('add_employee.php', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { const messageDiv = document.getElementById('message'); messageDiv.className = `message ${data.status}`; messageDiv.textContent = data.message; if(data.status === 'success') { this.reset(); loadEmployees(); // 3秒后清除消息 setTimeout(() => { messageDiv.textContent = ''; messageDiv.className = ''; }, 3000); } }); }; // 页面加载时获取员工列表 // 获取当前数据 function getCurrentData() { const table = document.querySelector('table'); const data = []; // 从第二行开始(跳过表头) const rows = table.querySelectorAll('tr:not(:first-child)'); rows.forEach(row => { const cells = row.querySelectorAll('td'); data.push({ '序号': cells[0].textContent, '名字': cells[1].textContent, '金额': cells[2].textContent, '区域': cells[3].textContent, '备注': cells[4].textContent }); }); return data; } // 导出到Excel function exportToExcel() { // 获取筛选后的数据 const filteredData = getCurrentData(); // 转换为 Excel 格式 const worksheet = XLSX.utils.json_to_sheet(filteredData); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "员工数据"); // 设置列宽 const wscols = [ {wch: 8}, // 序号列宽 {wch: 15}, // 名字列宽 {wch: 15}, // 金额列宽 {wch: 15}, // 类别列宽 {wch: 30} // 备注列宽 ]; worksheet['!cols'] = wscols; // 下载文件 XLSX.writeFile(workbook, `礼金数据_${new Date().toLocaleDateString()}.xlsx`); } loadEmployees(); </script> </body> </html>
update_employee.php
<?php header('Content-Type: application/json; charset=utf-8'); // 数据库连接配置 $servername = "127.0.0.1"; $username = "同上"; $password = "同上"; $dbname = "同上"; // 创建连接 $conn = new mysqli($servername, $username, $password, $dbname); // 检查连接 if ($conn->connect_error) { die("连接失败: " . $conn->connect_error); } // 设置编码 mysqli_set_charset($conn, "utf8"); // 获取POST数据 $id = $_POST['id']; $name = $_POST['name']; $amount = $_POST['amount']; $category = $_POST['category']; $note = $_POST['note']; // 准备 SQL 语句 $sql = "UPDATE employees SET name=?, amount=?, category=?, note=? WHERE id=?"; $stmt = $conn->prepare($sql); $stmt->bind_param("sdssi", $name, $amount, $category, $note, $id); // 执行查询 if ($stmt->execute()) { $response = array("status" => "success", "message" => "记录已成功更新"); } else { $response = array("status" => "error", "message" => "更新失败: " . $stmt->error); } echo json_encode($response); $stmt->close(); $conn->close(); ?>
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>婚礼礼金查询</title> <style> :root { --primary-color: #D20103; --error-color: #dc3545; --success-color: #28a745; --background-color: #f8f9fa; --border-color: #eee; --shadow-color: rgba(0, 0, 0, 0.1); --hover-color: #f5f8ff; } * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; padding: 0; background-color: var(--background-color); min-height: 100vh; line-height: 1.5; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; width: 100%; } .card { background: white; border-radius: 15px; box-shadow: 0 4px 6px var(--shadow-color); padding: 30px; margin-top: 20px; } h1 { color: var(--primary-color); text-align: center; margin-bottom: 30px; font-size: 2.5rem; text-shadow: 2px 2px 4px var(--shadow-color); } .search-box { display: flex; justify-content: center; gap: 10px; margin-bottom: 30px; flex-wrap: wrap; } input[type="text"] { padding: 12px 20px; width: 100%; max-width: 400px; border: 2px solid var(--border-color); border-radius: 8px; font-size: 16px; transition: all 0.3s ease; } input[type="text"]:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.2); } button { padding: 12px 30px; background-color: var(--primary-color); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: 600; transition: all 0.3s ease; min-width: 120px; } button:hover { background-color: #1557b0; transform: translateY(-1px); } button:active { transform: translateY(1px); } #result { margin-top: 20px; display: none; animation: fadeIn 0.5s ease; width: 100%; } @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .table-container { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-top: 10px; border-radius: 10px; } .result-table { width: 100%; border-collapse: separate; border-spacing: 0; background: white; border-radius: 10px; overflow: hidden; table-layout: fixed; } .result-table th, .result-table td { padding: 15px; text-align: left; border-bottom: 1px solid var(--border-color); word-break: break-all; overflow: hidden; text-overflow: ellipsis; line-height: 1.4; } .result-table th { background-color: var(--primary-color); color: white; font-weight: 600; white-space: nowrap; position: sticky; top: 0; } .result-table tr:last-child td { border-bottom: none; } .result-table tr:hover { background-color: var(--hover-color); } .result-table th:nth-child(1), .result-table td:nth-child(1) { width: 20%; } .result-table th:nth-child(2), .result-table td:nth-child(2) { width: 20%; } .result-table th:nth-child(3), .result-table td:nth-child(3) { width: 20%; } .result-table th:nth-child(4), .result-table td:nth-child(4) { width: 40%; } .amount-cell { color: var(--error-color); font-weight: 600; } .not-found { text-align: center; color: var(--error-color); padding: 20px; background-color: #fff; border-radius: 8px; border: 1px solid #ffcdd2; margin-top: 20px; font-weight: 500; } /* 响应式设计 */ @media screen and (max-width: 768px) { .container { padding: 10px; } .card { padding: 15px; margin-top: 10px; border-radius: 10px; } h1 { font-size: 1.8rem; margin-bottom: 20px; } .search-box { flex-direction: column; align-items: center; } input[type="text"] { width: 100%; padding: 10px 15px; } button { width: 100%; max-width: 400px; padding: 10px 20px; } .result-table { display: table; width: 100%; white-space: normal; } .result-table th, .result-table td { padding: 10px; font-size: 14px; } .result-table th:nth-child(1), .result-table td:nth-child(1) { width: 22%; } .result-table th:nth-child(2), .result-table td:nth-child(2) { width: 22%; } .result-table th:nth-child(3), .result-table td:nth-child(3) { width: 22%; } .result-table th:nth-child(4), .result-table td:nth-child(4) { width: 34%; } #result { margin: 10px 0 0 0; } .not-found { margin: 10px 0; padding: 15px; font-size: 14px; } } /* 深色模式支持 */ @media (prefers-color-scheme: dark) { :root { --background-color: #1a1a1a; --border-color: #333; --hover-color: #2d2d2d; } .card, .result-table, .not-found { background-color: #2d2d2d; color: #fff; } input[type="text"] { background-color: #333; color: #fff; border-color: #444; } input[type="text"]::placeholder { color: #888; } } /* 打印样式优化 */ @media print { .search-box { display: none; } .card { box-shadow: none; padding: 0; } .result-table th { background-color: #f0f0f0 !important; color: #000; } } </style> </head> <body> <div> <div> <h1>婚礼礼金查询</h1> <div> <input type="text" id="nameInput" placeholder="请输入姓名" autocomplete="off"> <button onclick="queryEmployee()">查询</button> </div> <div id="result"></div> </div> </div> <script> function queryEmployee() { const name = document.getElementById('nameInput').value; if (!name.trim()) { alert('请输入姓名'); return; } const xhr = new XMLHttpRequest(); xhr.open('GET', `query.php?name=${encodeURIComponent(name)}`, true); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { const results = JSON.parse(xhr.responseText); const resultDiv = document.getElementById('result'); if (results && results.length > 0) { let tableHTML = ` <div> <table> <thead> <tr> <th>姓名</th> <th>金额</th> <th>区域</th> <th>备注</th> </tr> </thead> <tbody> `; results.forEach(result => { tableHTML += ` <tr> <td>${result.name}</td> <td>${result.amount}</td> <td>${result.category}</td> <td>${result.note}</td> </tr> `; }); tableHTML += '</tbody></table></div>'; resultDiv.innerHTML = tableHTML; } else { resultDiv.innerHTML = '<div>未找到该信息</div>'; } resultDiv.style.display = 'block'; } }; xhr.send(); } // 添加回车键触发查询 document.getElementById('nameInput').addEventListener('keypress', function(e) { if (e.key === 'Enter') { queryEmployee(); } }); </script> </body> </html>
query.php
<?php
header('Content-Type: application/json; charset=utf-8');
// 数据库连接配置
$servername = "127.0.0.1";
$username = "同上";
$password = "同上";
$dbname = "同上";
// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检查连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 设置编码
mysqli_set_charset($conn, "utf8");
// 获取查询参数
$name = $_GET['name'];
// 准备 SQL 语句(使用 LIKE 进行模糊查询)
$sql = "SELECT * FROM employees WHERE name LIKE ?";
$stmt = $conn->prepare($sql);
$searchPattern = "%" . $name . "%";
$stmt->bind_param("s", $searchPattern);
// 执行查询
$stmt->execute();
$result = $stmt->get_result();
// 获取所有匹配的结果
$employees = array();
while($row = $result->fetch_assoc()) {
$employees[] = array(
'name' => $row['name'],
'amount' => $row['amount'],
'category' => $row['category'],
'note' => $row['note']
);
}
// 关闭连接
$stmt->close();
$conn->close();
// 返回结果
echo json_encode($employees, JSON_UNESCAPED_UNICODE);
?>
add目录下的主页是用来对数据库可视化的增删查改的。
我们来看看我们部署的页面,我们进入add下的主页,随机插入几百组模拟数据进行测试。
如下图所示
图一
图二
经过测试增加删改导出功能完全正常没有差错,查询页面也很简单实时更新同步数据库的文件。
后话:具体文件大家可以继续优化,你可以写个加密对于增删页面进行加密校验,对于数据库名、用户名、密码等、你可以写成一个文件大家引用即可,目前这些功能完全够我使用了,大家各取所需,自行优化!