新增 install.php:全新部署时自动接管,用户只需上传文件、打开站点即进入
安装向导。安装页自己做三件事:
- 建 db/ 与 uploads/{posters,tickets,photos,avatars}/,权限调到 775
- 逐目录真写临时文件实测可写,不靠 is_writable 猜
- 建库、建管理员、写 db/.installed 锁、自删本文件
配套改动:
- config.php 把安装检查前置于建库之前,未安装一律 302 到 install.php。
原实现无条件调用 initDB(),导致「打开首页」就等于建库并种下 admin/123456。
- lib/db.php 的 initDB(\$seedDefaultAdmin = true);安装向导传 false,
管理员由用户自己设,绝不种弱口令。新增 isInstalled()。
- login.php / nav.php 接上 siteName(),站点名称不再只存不用。
- lib/settings.php 新增 siteName(),缺省回退「电影日志」。
PHP 8.5 兼容:
- 删掉 fetch_movie.php 与 lib/storage/http.php 的 curl_close()。
8.5 起该函数 deprecated,调用会往输出插一行警告,把 JSON 响应污染成
「警告 + JSON」,前端 JSON.parse 失败 → 电影名联想功能完全失灵。
- config.php 的 display_errors 由 1 改为 0(生产环境不应向浏览器吐错误),
错误仍写日志;需要调试时设 MOVIELOG_DEBUG=1 打开。
线上验证(movie.yangyq.net,PHP 8.5.12):
未安装时 / 、/login.php 均 302 → install.php;
安装页 200、体检 10/10 通过、自动建好 5 个目录;
安装后 install.php 404、管理员登录成功、首页 200 无报错;
联想接口带会话请求「流浪地球/星际穿越/霸王别姬」均返回纯 JSON(首字符 {)。
90 lines
4.3 KiB
PHP
90 lines
4.3 KiB
PHP
<?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">
|
||
<!-- 左上角系统名做成一个「回到首页」的链接:
|
||
右侧折叠菜单里的「首页」要点两下(先展开再点),这里一下就到。 -->
|
||
<a href="index.php" class="nav-brand" title="回到首页" aria-label="回到首页">
|
||
<span class="brand-icon">🎬</span>
|
||
<span class="brand-name"><?= h(siteName()) ?></span>
|
||
</a>
|
||
<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="settings.php" <?= $current_page == 'settings.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>
|