本文目录导读:

实现PHP账单查询功能,核心在于数据库设计、查询逻辑(按时间、类型、关键词等筛选)以及前端展示(分页、排序),以下是分步实现指南。
数据库设计(MySQL)
假设数据表 bills 用于存储账单记录:
CREATE TABLE `bills` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL COMMENT '用户ID',
`category` VARCHAR(50) NOT NULL COMMENT '分类(餐饮/交通/购物等)',
`type` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '0支出 1收入',
`amount` DECIMAL(10,2) NOT NULL COMMENT '金额',
`note` VARCHAR(255) DEFAULT NULL COMMENT '备注',
`billing_date` DATE NOT NULL COMMENT '账单日期',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_date` (`user_id`, `billing_date`) -- 复合索引加速查询
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键点:
- 使用
billing_date而非created_at作为账单日期,方便按自然日查询。- 建立
(user_id, billing_date)复合索引,这是查询最常用的条件。- 金额字段使用
DECIMAL(10,2)而非FLOAT,避免精度问题。
查询功能实现(PHP + PDO)
1 查询表单(HTML)
放在 index.php 或 search.php:
<form method="get" action="query_bills.php">
<input type="text" name="keyword" placeholder="备注关键词" value="<?= htmlspecialchars($_GET['keyword'] ?? '') ?>">
<select name="category">
<option value="">所有分类</option>
<option value="餐饮" <?= ($_GET['category'] ?? '') == '餐饮' ? 'selected' : '' ?>>餐饮</option>
<option value="交通" <?= ($_GET['category'] ?? '') == '交通' ? 'selected' : '' ?>>交通</option>
<option value="购物" <?= ($_GET['category'] ?? '') == '购物' ? 'selected' : '' ?>>购物</option>
</select>
<input type="date" name="start_date" value="<?= htmlspecialchars($_GET['start_date'] ?? '') ?>">
<input type="date" name="end_date" value="<?= htmlspecialchars($_GET['end_date'] ?? '') ?>">
<button type="submit">查询</button>
</form>
2 后台查询逻辑(query_bills.php)
<?php
// 1. 数据库连接(以 PDO 为例)
$pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 2. 安全获取查询参数
$params = [];
$conditions = ['user_id = :user_id']; // 假设当前登录用户ID = 1
$params[':user_id'] = 1;
// --- 按日期范围筛选 ---
if (!empty($_GET['start_date'])) {
$conditions[] = 'billing_date >= :start_date';
$params[':start_date'] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$conditions[] = 'billing_date <= :end_date';
$params[':end_date'] = $_GET['end_date'];
}
// --- 按分类筛选 ---
if (!empty($_GET['category'])) {
$conditions[] = 'category = :category';
$params[':category'] = $_GET['category'];
}
// --- 按备注关键词模糊搜索 ---
if (!empty($_GET['keyword'])) {
$conditions[] = 'note LIKE :keyword';
$params[':keyword'] = '%' . $_GET['keyword'] . '%';
}
// --- 按类型筛选(收入/支出)---
if (isset($_GET['type']) && $_GET['type'] !== '') {
$conditions[] = 'type = :type';
$params[':type'] = (int)$_GET['type'];
}
// 3. 组装SQL并执行
$sql = 'SELECT * FROM bills WHERE ' . implode(' AND ', $conditions);
$sql .= ' ORDER BY billing_date DESC, created_at DESC'; // 默认按日期倒序
// 分页支持(可选)
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 20;
$offset = ($page - 1) * $perPage;
// 先查询总数用于分页
$countSql = str_replace('SELECT *', 'SELECT COUNT(*)', $sql);
$stmt = $pdo->prepare($countSql);
$stmt->execute($params);
$total = $stmt->fetchColumn();
// 再查询具体数据
$sql .= " LIMIT $perPage OFFSET $offset";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$bills = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 4. 处理结果(示例:计算总收入和总支出)
$totalIncome = $totalExpense = 0;
foreach ($bills as $bill) {
if ($bill['type'] == 1) $totalIncome += $bill['amount'];
else $totalExpense += $bill['amount'];
}
// 5. 输出结果(可放到HTML模板中)
?>
前端展示(分页 + 统计)
<?php if (!empty($bills)): ?>
<p>共 <?= $total ?> 条记录,收入总额:<?= $totalIncome ?>,支出总额:<?= $totalExpense ?></p>
<table>
<tr><th>日期</th><th>分类</th><th>类型</th><th>金额</th><th>备注</th></tr>
<?php foreach ($bills as $bill): ?>
<tr>
<td><?= htmlspecialchars($bill['billing_date']) ?></td>
<td><?= htmlspecialchars($bill['category']) ?></td>
<td><?= $bill['type'] == 1 ? '收入' : '支出' ?></td>
<td style="color:<?= $bill['type'] == 1 ? 'green' : 'red' ?>"><?= number_format($bill['amount'], 2) ?></td>
<td><?= htmlspecialchars($bill['note'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</table>
<!-- 简单分页(保持当前查询条件) -->
<?php
$totalPages = ceil($total / $perPage);
$queryParams = $_GET;
for ($i = 1; $i <= $totalPages; $i++):
$queryParams['page'] = $i;
$queryString = http_build_query($queryParams);
?>
<a href="?<?= $queryString ?>" <?= $i == $page ? 'style="font-weight:bold"' : '' ?>><?= $i ?></a>
<?php endfor; ?>
<?php else: ?>
<p>暂无账单记录</p>
<?php endif; ?>
扩展高级功能
-
分组统计:按分类统计金额
SELECT category, SUM(amount) AS total FROM bills WHERE ... GROUP BY category ORDER BY total DESC
-
月度对比:按月统计收支
SELECT DATE_FORMAT(billing_date, '%Y-%m') AS month, SUM(CASE WHEN type=1 THEN amount ELSE 0 END) AS income, SUM(CASE WHEN type=0 THEN amount ELSE 0 END) AS expense FROM bills WHERE ... GROUP BY month ORDER BY month DESC -
导出CSV:
header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="bills.csv"'); $output = fopen('php://output', 'w'); fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF)); // BOM for Excel fputcsv($output, ['日期','分类','金额','备注']); foreach ($bills as $row) { fputcsv($output, [$row['billing_date'], $row['category'], $row['amount'], $row['note']]); } fclose($output); -
安全建议:
- 使用 预处理语句(Prepared Statement) 防止SQL注入(上面已使用)。
- 用户ID必须从Session获取,不能从客户端提交。
- 对输出做
htmlspecialchars()防止XSS。 - 如果数据量大(>10万条),建议加上 分页 和 合理索引,避免全表扫描。
完整代码结构示例
project/
├── index.php # 表单输入页
├── query_bills.php # 查询逻辑 + 结果展示(或分离为两个文件)
├── db.php # 数据库连接配置
└── style.css # 样式(可选)
如果希望分离逻辑,query_bills.php 仅输出JSON,前端用JavaScript(Vue/React)渲染,但小项目使用服务端渲染更简单直接。
如果需要更具体的实现(如ThinkPHP/Laravel框架版本、微信小程序对接、图表统计等),可以进一步说明。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播