feat: 完善电影记录系统源码与项目文档

功能模块:
- 观影记录:列表(瀑布流)/ 添加 / 编辑 / 详情 / 删除,删除时同步清理图片文件
- 首页快捷搜索 + 高级搜索(片名、影院、观影人、日期区间组合筛选)
- 影院管理:三段式信息 + 50 种预设配色,删除已引用影院时二次确认
- 观影人管理:头像裁剪上传 + 专属配色,已引用者禁止删除
- 图片处理:Cropper.js 裁剪票根/海报/观影照片/头像,多图上传,首字母 SVG 头像兜底
- OMDb 自动获取影片信息(fetch_movie.php 代理,返回 JSON)
- CSV 批量导入:UTF-8/GBK 自动识别,影院去重,事务保护,逐行错误报告
- 统计仪表板:总览 + 按影院/观影人/年份/近 12 个月排行
- 年度报告:按账号关联的观影人视角生成,含 Chart.js 月度与星期分布图
- 多用户:账号增删改、账号与观影人绑定、修改自己的用户名与密码
- 登录鉴权:Session + password_hash,全站页面登录校验

文档:
- 重写 README:功能特性、技术栈、目录结构、数据库结构、部署与使用指南、接口说明、CSV 格式、安全注意事项、已知限制
- 新增 .gitignore,排除 db/ 与 uploads/ 等运行时数据
This commit is contained in:
2026-09-21 08:20:56 +08:00
parent 9ad5f3e0f5
commit 40e1bc5e56
24 changed files with 7238 additions and 2 deletions
+84
View File
@@ -0,0 +1,84 @@
<?php
// ============================================
// 文件: nav.php (导航栏组件,显示用户头像)
// ============================================
if (!isset($admin)) {
if (isset($_SESSION['admin_id'])) {
require_once 'config.php';
$admin = getCurrentAdmin();
} else {
$admin = ['username' => '未登录'];
}
}
$current_page = basename($_SERVER['PHP_SELF']);
?>
<nav class="navbar">
<div class="nav-container">
<div class="nav-brand">
<span class="brand-icon">🎬</span>
<span class="brand-name">电影日志</span>
</div>
<div class="nav-actions">
<span class="nav-user">
<img src="<?= getAvatarUrl($admin['avatar'] ?? null, $admin['username'] ?? '') ?>" style="width:28px;height:28px;border-radius:50%;object-fit:cover;vertical-align:middle;margin-right:4px;">
<?= h($admin['username'] ?? '管理员') ?>
</span>
<a href="add.php" class="btn btn-primary btn-sm"> 添加电影</a>
<div class="nav-dropdown">
<button class="dropbtn" onclick="toggleDropdown(event)">☰ 菜单 <span class="arrow">▼</span></button>
<div class="dropdown-content" id="dropdownMenu">
<a href="index.php" <?= $current_page == 'index.php' ? 'class="active"' : '' ?>>
<span class="icon">🏠</span> 首页
</a>
<a href="search.php" <?= $current_page == 'search.php' ? 'class="active"' : '' ?>>
<span class="icon">🔍</span> 高级搜索
</a>
<a href="stats.php" <?= $current_page == 'stats.php' ? 'class="active"' : '' ?>>
<span class="icon">📊</span> 统计
</a>
<a href="annual_report.php" <?= $current_page == 'annual_report.php' ? 'class="active"' : '' ?>>
<span class="icon">📅</span> 年度报告
</a>
<div class="divider"></div>
<a href="cinemas.php" <?= $current_page == 'cinemas.php' ? 'class="active"' : '' ?>>
<span class="icon">🏛️</span> 影院管理
</a>
<a href="persons.php" <?= $current_page == 'persons.php' ? 'class="active"' : '' ?>>
<span class="icon">👥</span> 观影人管理
</a>
<a href="import.php" <?= $current_page == 'import.php' ? 'class="active"' : '' ?>>
<span class="icon">📥</span> CSV导入
</a>
<div class="divider"></div>
<a href="change_password.php" <?= $current_page == 'change_password.php' ? 'class="active"' : '' ?>>
<span class="icon">🔑</span> 修改密码
</a>
<a href="users.php" <?= $current_page == 'users.php' ? 'class="active"' : '' ?>>
<span class="icon">👤</span> 用户管理
</a>
<div class="divider"></div>
<a href="logout.php" style="color: #e74c3c;">
<span class="icon">🚪</span> 退出登录
</a>
</div>
</div>
</div>
</div>
</nav>
<script>
function toggleDropdown(e) {
e.stopPropagation();
var menu = document.getElementById('dropdownMenu');
menu.classList.toggle('show');
}
document.addEventListener('click', function(e) {
if (!e.target.closest('.nav-dropdown')) {
document.querySelectorAll('.nav-dropdown .dropdown-content').forEach(function(el) {
el.classList.remove('show');
});
}
});
</script>