NewHome/article/index.php

199 lines
8.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* article/index.php —— 文章区(文章库)
* 结构:顶部栏(返回首页 + 当页名 + 分区/段落开关)
* 内容三栏:左 15% 文章标题栏(按分区) / 中 15% 当前文章段落目录 / 右 70% 正文Markdown 渲染)
* 文章正文存 SQLite图片上传到 data/articles/img/,正文以 data/articles/img/ 站点相对路径引用,
* 渲染时自动补全前缀。三栏宽度可拖拽调节,标题栏 / 段落栏可分别隐藏。
* 打开页面为「先列表后阅读」:默认右栏展示全部分区文章列表,点击文章进入阅读。
*/
$P = '../';
require_once dirname(__DIR__) . '/includes/layout.php';
$pdo = db();
$noteText = setting_get('article_note', '');
// 全部可见文章(先按最后更新时间倒序拉取,保证各分区内均为“更新在前”排序)
$all = $pdo->query('SELECT id, part, title, summary, updated_at
FROM articles WHERE enabled = 1 ORDER BY updated_at DESC, id DESC')->fetchAll();
$byPart = [];
foreach ($all as $r) {
$part = trim((string)$r['part']);
if ($part === '') {
$part = '未分类';
}
$byPart[$part][] = $r;
}
// 分区顺序后台「文章管理」拖出的分区顺序article_part_order优先未配置则按各分区最新更新时间降序
$poIdx = [];
$poCfg = trim((string)setting_get('article_part_order', ''));
if ($poCfg !== '') {
foreach (explode(',', $poCfg) as $po) {
$po = trim($po);
if ($po !== '' && isset($byPart[$po])) $poIdx[$po] = count($poIdx);
}
}
$partKeys = array_keys($byPart);
usort($partKeys, static function (string $a, string $b) use ($poIdx, $byPart): int {
$ia = $poIdx[$a] ?? PHP_INT_MAX;
$ib = $poIdx[$b] ?? PHP_INT_MAX;
if ($ia !== $ib) return $ia <=> $ib;
// 各分区首篇即最新SQL 已按 updated_at 倒序)
$d = strcmp((string)$byPart[$b][0]['updated_at'], (string)$byPart[$a][0]['updated_at']);
return $d !== 0 ? $d : strcmp($a, $b);
});
$rows = [];
foreach ($partKeys as $p) {
$rows[] = ['part' => $p, 'articles' => $byPart[$p]];
}
$total = count($all);
// 从库首页“文章区”卡片进入阅读时(?from=nav顶部返回按钮语义切换
$fromNav = (isset($_GET['from']) && (string)$_GET['from'] === 'nav');
// 当前阅读文章
$cur = null;
$cid = (int)($_GET['id'] ?? 0);
if ($cid > 0) {
$st = $pdo->prepare('SELECT * FROM articles WHERE id = ? AND enabled = 1 LIMIT 1');
$st->execute([$cid]);
$cur = $st->fetch();
if (!$cur) {
$cur = null; // 无效或已隐藏:回退到列表
}
}
layout_head($cur ? (string)$cur['title'] : '文章库');
?>
<style>
/* 文章区专用:将外层正文区铺满视口 */
html, body { height: 100%; }
</style>
<div class="art-app js-art-app">
<!-- 顶部栏:返回首页 / 当页名(列表为文章库、阅读时显示文章标题)/ 分区·段落开关 -->
<header class="art-topbar">
<div class="art-topbar-nav">
<?php if ($fromNav): ?>
<a class="btn btn-sm" href="index.php" title="返回文章区首页(文章列表)">← 文章区首页</a>
<?php if ($cur): ?>
<a class="btn btn-sm" href="javascript:history.back()" title="返回上一页(回到来源页面)">← 返回上一页</a>
<?php endif; ?>
<?php else: ?>
<a class="btn btn-sm" href="../index.php" title="返回站点首页">← 首页</a>
<?php if ($cur): ?>
<a class="btn btn-sm" href="index.php" title="返回文章区首页(文章列表)">← 文章区首页</a>
<?php endif; ?>
<?php endif; ?>
</div>
<div class="art-topbar-title"><?= $cur ? he((string)$cur['title']) : '📚 文章库' ?></div>
<div class="art-topbar-acts">
<button type="button" class="btn btn-sm js-art-toggle" data-col="title" title="显示 / 隐藏左侧文章标题栏">☰<span class="art-tg-txt">标题栏</span></button>
<button type="button" class="btn btn-sm js-art-toggle" data-col="toc" title="显示 / 隐藏中间段落目录">≡<span class="art-tg-txt">段落</span></button>
</div>
</header>
<?php if ($noteText !== ''): ?>
<div class="art-note"><?= he($noteText) ?></div>
<?php endif; ?>
<div class="art-frame js-art-frame">
<!-- 左:文章标题栏(按分区) -->
<aside class="art-col art-col-title js-col-title">
<div class="art-col-head">
<span>文章目录</span>
<button type="button" class="art-mini js-art-toggle" data-col="title" title="隐藏标题栏">✕</button>
</div>
<nav class="art-nav js-art-nav">
<?php if (!$rows): ?>
<p class="tip" style="padding:10px">暂无文章</p>
<?php endif; ?>
<?php foreach ($rows as $g): ?>
<div class="art-nav-part">
<div class="art-nav-part-name"><?= he($g['part']) ?></div>
<?php foreach ($g['articles'] as $a): ?>
<a class="art-nav-item<?= ($cur && (int)$cur['id'] === (int)$a['id']) ? ' active' : '' ?>"
href="?id=<?= (int)$a['id'] ?>" title="<?= he((string)$a['summary']) ?>"><?= he((string)$a['title']) ?></a>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</nav>
</aside>
<div class="art-gutter js-gutter" data-var="--w-title"></div>
<!-- 中:段落目录 -->
<aside class="art-col art-col-toc js-col-toc">
<div class="art-col-head">
<span>段落目录</span>
<button type="button" class="art-mini js-art-toggle" data-col="toc" title="隐藏段落栏">✕</button>
</div>
<nav class="art-toc js-toc-nav"></nav>
</aside>
<div class="art-gutter js-gutter" data-var="--w-toc"></div>
<!-- 右:正文 / 列表 -->
<section class="art-body js-art-body" id="artBody">
<?php if ($cur): ?>
<?php
$md = (string)$cur['markdown'];
$updated = (string)($cur['updated_at'] ?? '');
$part = (string)$cur['part'];
?>
<div class="art-article js-article" data-id="<?= (int)$cur['id'] ?>">
<div class="art-article-head">
<div class="art-article-part"><?= he($part) ?></div>
<h1 class="art-article-title js-art-title"><?= he((string)$cur['title']) ?></h1>
<?php if (trim((string)($cur['summary'] ?? '')) !== ''): ?>
<p class="art-article-sum"><?= he(trim((string)$cur['summary'])) ?></p>
<?php endif; ?>
<?php if ($updated !== ''): ?>
<div class="art-article-meta">更新于 <?= he($updated) ?></div>
<?php endif; ?>
</div>
<div class="art-md js-art-md" id="artMd"></div>
<textarea id="artRaw" hidden><?= he($md) ?></textarea>
<script type="text/plain" id="artErrTpl"><div class="err-msg">正文渲染失败:无法加载 Markdown 解析库,请检查网络后刷新。</div></script>
</div>
<?php else: ?>
<div class="art-list">
<div class="art-list-head">
<h1>全部文章 <span class="tip">(共 <?= $total ?> 篇)</span></h1>
<p class="tip st-tip">点击任意文章开始阅读;左侧「文章目录」可随时切换文章,中间「段落目录」可快速跳转。</p>
</div>
<?php if (!$rows): ?>
<div class="panel-card"><p class="tip">文章库还没有内容<span class="st-tip">,站长可在后台「文章管理」中发布</span>。</p></div>
<?php endif; ?>
<?php foreach ($rows as $g): ?>
<div class="art-part-block">
<h2 class="art-part-title"><?= he($g['part']) ?></h2>
<div class="art-cards">
<?php foreach ($g['articles'] as $a): ?>
<a class="art-card" href="?id=<?= (int)$a['id'] ?>">
<span class="art-card-title"><?= he((string)$a['title']) ?></span>
<?php if (trim((string)($a['summary'] ?? '')) !== ''): ?>
<span class="art-card-sum"><?= he(trim((string)$a['summary'])) ?></span>
<?php endif; ?>
<span class="art-card-date"><?= he((string)($a['updated_at'] ?? '')) ?></span>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
</div>
</div>
<?php layout_theme_fab(); ?>
<script>
window.HP_ART_DATA = {
curId: <?= $cur ? (int)$cur['id'] : 0 ?>,
listMode: <?= $cur ? 0 : 1 ?>
};
</script>
<script src="../assets/js/common.js"></script>
<script src="../assets/js/article.js"></script>
</body>
</html>