113 lines
4.9 KiB
PHP
113 lines
4.9 KiB
PHP
<?php
|
||||
|
|
// ============================================
|
|||
|
|
// 文件: lib/theme.php
|
|||
|
|
// 说明: 界面配色主题(5 套经典配色)。
|
|||
|
|
//
|
|||
|
|
// 主题是**每个账号自己的偏好**,存在 admins.theme 里(空 = 用默认主题)。
|
|||
|
|
// 颜色本身全部定义在 css/themes.css 的 [data-theme="<key>"] 规则里,
|
|||
|
|
// 本文件只负责「有哪些主题 / 当前用哪套 / 页面上怎么声明」,
|
|||
|
|
// 不重复存一份色值(唯一例外是下面 themeCatalog() 里给选色卡片用的
|
|||
|
|
// 一段渐变缩略图,那是纯装饰,不参与页面配色)。
|
|||
|
|
//
|
|||
|
|
// 页面接入方式(每个页面的 <html> 与样式表各加一处):
|
|||
|
|
// 1) <html lang="zh-CN"> 上紧跟着输出 themeAttr() 的返回值(它自带一个前导空格);
|
|||
|
|
// 2) css/style.css 的 link 之后,再输出一行 themesCssLink()。
|
|||
|
|
// themes.css 必须排在 style.css 之后 —— [data-theme] 与 style.css 里
|
|||
|
|
// 作为兜底的 :root 优先级相同,靠「后出现者胜」来覆盖。
|
|||
|
|
//
|
|||
|
|
// 注意:上面两行说明里**故意不写 PHP 短标签**。PHP 的「问号 + 右尖括号」
|
|||
|
|
// 在 // 注释里同样会关闭 PHP 模式,之后若再出现「左尖括号 + 问号 + 等号」
|
|||
|
|
// 就会被真的执行 —— 注释里写出完整标签示例,本文件被 require 时会直接
|
|||
|
|
// Fatal error,而 php -l 是查不出来的(它只看语法,不看语义)。
|
|||
|
|
// ============================================
|
|||
|
|
|
|||
|
|
// 主题注册表:key => [显示名, 一句话描述, 明暗]
|
|||
|
|
// 想加主题:这里加一项,并在 css/themes.css 里补一个同 key 的 [data-theme] 规则块
|
|||
|
|
// (规则块里那一组变量一个都不能少,缺哪个就会退回 style.css 的默认值)。
|
|||
|
|
function themeCatalog() {
|
|||
|
|
static $catalog = null;
|
|||
|
|
if ($catalog === null) {
|
|||
|
|
$catalog = [
|
|||
|
|
'ticket' => [
|
|||
|
|
'name' => '复古票根',
|
|||
|
|
'desc' => '暖橙 · 默认配色',
|
|||
|
|
'scheme' => 'light',
|
|||
|
|
],
|
|||
|
|
'indigo' => [
|
|||
|
|
'name' => '靛蓝',
|
|||
|
|
'desc' => '经典蓝 · 清爽利落',
|
|||
|
|
'scheme' => 'light',
|
|||
|
|
],
|
|||
|
|
'forest' => [
|
|||
|
|
'name' => '松林',
|
|||
|
|
'desc' => '墨绿 · 沉静耐看',
|
|||
|
|
'scheme' => 'light',
|
|||
|
|
],
|
|||
|
|
'wine' => [
|
|||
|
|
'name' => '绛红',
|
|||
|
|
'desc' => '酒红 · 复古胶片',
|
|||
|
|
'scheme' => 'light',
|
|||
|
|
],
|
|||
|
|
'midnight' => [
|
|||
|
|
'name' => '暗夜',
|
|||
|
|
'desc' => '深色 · 靛紫点缀',
|
|||
|
|
'scheme' => 'dark',
|
|||
|
|
],
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
return $catalog;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 默认主题(没选过 / 选的 key 不认识了,都落到它上面)
|
|||
|
|
function defaultThemeKey() {
|
|||
|
|
return 'ticket';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// key 是否有效
|
|||
|
|
function isValidTheme($key) {
|
|||
|
|
return is_string($key) && isset(themeCatalog()[$key]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 取某套主题的元信息;非法 key 返回默认主题的(页面渲染不该因为脏数据而炸)
|
|||
|
|
function themeInfo($key) {
|
|||
|
|
$catalog = themeCatalog();
|
|||
|
|
return isValidTheme($key) ? $catalog[$key] : $catalog[defaultThemeKey()];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 当前生效的主题 key。
|
|||
|
|
// 优先读 session(登录时/切换主题时写入),session 里还没有就回库里补一次 ——
|
|||
|
|
// 这样「本次升级之前就已经登录着」的会话也能立刻用上自己账号存的主题。
|
|||
|
|
function currentThemeKey() {
|
|||
|
|
if (!isset($_SESSION['admin_theme'])) {
|
|||
|
|
$theme = '';
|
|||
|
|
if (!empty($_SESSION['admin_id'])) {
|
|||
|
|
$db = getDB();
|
|||
|
|
$stmt = $db->prepare("SELECT theme FROM admins WHERE id = ?");
|
|||
|
|
$stmt->execute([(int)$_SESSION['admin_id']]);
|
|||
|
|
// 用 fetchAll 取:只取一行就放着不读完,游标会一直挂着共享读锁(本项目踩过)
|
|||
|
|
$rows = $stmt->fetchAll();
|
|||
|
|
if ($rows && isset($rows[0]['theme'])) {
|
|||
|
|
$theme = (string)$rows[0]['theme'];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
$_SESSION['admin_theme'] = $theme;
|
|||
|
|
}
|
|||
|
|
$key = (string)$_SESSION['admin_theme'];
|
|||
|
|
return isValidTheme($key) ? $key : defaultThemeKey();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// <html> 上要输出的主题属性。始终输出(默认主题也输出),
|
|||
|
|
// 这样每个页面用的都是 themes.css 里那个明确的调色板,不用去猜 style.css 的兜底值。
|
|||
|
|
function themeAttr() {
|
|||
|
|
return ' data-theme="' . h(currentThemeKey()) . '"';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 主题样式表。必须放在 css/style.css 之后。
|
|||
|
|
//
|
|||
|
|
// 末尾**带一个换行符**:PHP 会吃掉「结束标签」后面紧跟的那一个换行。
|
|||
|
|
// 模板里把这个返回值单独放一行再换行的话,渲染结果会变成这一行和下一行黏在一起
|
|||
|
|
// (HTML 上无害,但会让 HTML 结构对比工具误判,也没必要)。这里补回来。
|
|||
|
|
function themesCssLink() {
|
|||
|
|
return '<link rel="stylesheet" href="css/themes.css">' . "\n";
|
|||
|
|
}
|