|
|
@@ -30,23 +30,9 @@ class AnthologyReadController extends Controller
|
|
|
? $colResult['data']
|
|
|
: $colResult['data']->toArray(request());
|
|
|
|
|
|
- // ── 2. 构建完整目录(所有 level,保留层级信息) ────────────────────
|
|
|
+ // ── 2. 构建目录(方案A:展开祖先链,其余折叠) ───────────────────────
|
|
|
$fullArticleList = collect($col['article_list'] ?? []);
|
|
|
-
|
|
|
- // toc:全部节点,交给阅读页左侧目录使用
|
|
|
- // 当前章节标记 active=true、disabled=true(高亮且不可点击)
|
|
|
- $toc = $fullArticleList
|
|
|
- ->values()
|
|
|
- ->map(fn($a) => [
|
|
|
- 'id' => $a['article_id'],
|
|
|
- 'title' => $a['title'],
|
|
|
- 'summary' => '',
|
|
|
- 'progress' => 0,
|
|
|
- 'level' => (int) ($a['level'] ?? 1),
|
|
|
- 'disabled' => $a['article_id'] === $articleId,
|
|
|
- 'active' => $a['article_id'] === $articleId,
|
|
|
- ])
|
|
|
- ->toArray();
|
|
|
+ $toc = $this->buildCollapsedToc($fullArticleList->toArray(), $articleId);
|
|
|
|
|
|
// ── 3. 获取当前文章内容 ───────────────────────────────────────────────
|
|
|
$artResult = $this->articleService->getArticle($articleId);
|
|
|
@@ -141,4 +127,79 @@ class AnthologyReadController extends Controller
|
|
|
// 翻页路由需要 anthologyId,传给 blade 供覆盖路由使用
|
|
|
return view('library.book.read', compact('book', 'relatedBooks', 'anthologyId'));
|
|
|
}
|
|
|
+
|
|
|
+ // =========================================================================
|
|
|
+ // buildCollapsedToc
|
|
|
+ //
|
|
|
+ // 规则:
|
|
|
+ // 1. 所有 level=1 节点始终显示
|
|
|
+ // 2. 找出当前节点的「祖先链」(从 root 到当前节点经过的每个节点)
|
|
|
+ // 3. 祖先链上每个节点的「直接子节点」都展开显示
|
|
|
+ // 4. 当前节点本身若有子节点,同样展开一级
|
|
|
+ // 5. 其余节点隐藏
|
|
|
+ // =========================================================================
|
|
|
+ private function buildCollapsedToc(array $list, string $currentId): array
|
|
|
+ {
|
|
|
+ // ── Step 1:为每个节点推导父节点 id ──────────────────────────────────
|
|
|
+ // article_list 是有序的,父节点 = 当前节点之前最近的 level 更小的节点
|
|
|
+ $parents = []; // article_id => parent_article_id | null
|
|
|
+ $stack = []; // 维护祖先栈 [ ['id'=>..., 'level'=>...], ... ]
|
|
|
+
|
|
|
+ foreach ($list as $item) {
|
|
|
+ $id = $item['article_id'];
|
|
|
+ $level = (int) ($item['level'] ?? 1);
|
|
|
+
|
|
|
+ // 弹出所有 level >= 当前 level 的栈顶
|
|
|
+ while (!empty($stack) && $stack[count($stack) - 1]['level'] >= $level) {
|
|
|
+ array_pop($stack);
|
|
|
+ }
|
|
|
+
|
|
|
+ $parents[$id] = empty($stack) ? null : $stack[count($stack) - 1]['id'];
|
|
|
+ $stack[] = ['id' => $id, 'level' => $level];
|
|
|
+ }
|
|
|
+
|
|
|
+ // ── Step 2:找出当前节点的祖先链 ─────────────────────────────────────
|
|
|
+ $ancestorIds = [];
|
|
|
+ $cursor = $currentId;
|
|
|
+ while (isset($parents[$cursor]) && $parents[$cursor] !== null) {
|
|
|
+ $cursor = $parents[$cursor];
|
|
|
+ $ancestorIds[] = $cursor;
|
|
|
+ }
|
|
|
+ $ancestorSet = array_flip($ancestorIds);
|
|
|
+
|
|
|
+ // ── Step 3:需要展开子节点的集合 = 祖先链 + 当前节点 ─────────────────
|
|
|
+ $expandParentSet = $ancestorSet;
|
|
|
+ $expandParentSet[$currentId] = true;
|
|
|
+
|
|
|
+ // ── Step 4:过滤构建 toc ──────────────────────────────────────────────
|
|
|
+ $toc = [];
|
|
|
+ foreach ($list as $item) {
|
|
|
+ $id = $item['article_id'];
|
|
|
+ $level = (int) ($item['level'] ?? 1);
|
|
|
+ $parentId = $parents[$id];
|
|
|
+ $isActive = $id === $currentId;
|
|
|
+
|
|
|
+ // 显示条件:
|
|
|
+ // a) level=1(始终显示)
|
|
|
+ // b) 父节点在 expandParentSet 中(祖先链或当前节点的直接子节点)
|
|
|
+ $visible = $level === 1
|
|
|
+ || ($parentId !== null && isset($expandParentSet[$parentId]));
|
|
|
+
|
|
|
+ if (!$visible) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $toc[] = [
|
|
|
+ 'id' => $id,
|
|
|
+ 'title' => $item['title'],
|
|
|
+ 'summary' => '',
|
|
|
+ 'progress' => 0,
|
|
|
+ 'level' => $level,
|
|
|
+ 'disabled' => $isActive,
|
|
|
+ 'active' => $isActive,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ return $toc;
|
|
|
+ }
|
|
|
}
|