visuddhinanda il y a 3 jours
Parent
commit
e95407bbc5
30 fichiers modifiés avec 2623 ajouts et 3310 suppressions
  1. 29 2
      api-v12/app/Http/Controllers/CategoryController.php
  2. 1 1
      api-v12/app/Http/Controllers/Library/BookController.php
  3. 4 1
      api-v12/app/Providers/AppServiceProvider.php
  4. 91 0
      api-v12/app/View/Composers/BlogViewComposer.php
  5. 0 0
      api-v12/public/assets/css/blog/style.min.css
  6. 697 0
      api-v12/resources/css/modules/_anthology.css
  7. 313 0
      api-v12/resources/css/modules/_reader.css
  8. 132 0
      api-v12/resources/css/modules/_tipitaka.css
  9. 0 163
      api-v12/resources/css/modules/_wiki_home_append.css
  10. 5 237
      api-v12/resources/css/reader.css
  11. 29 18
      api-v12/resources/views/blog/category.blade.php
  12. 33 414
      api-v12/resources/views/blog/index.blade.php
  13. 296 468
      api-v12/resources/views/blog/layouts/app.blade.php
  14. 0 28
      api-v12/resources/views/components/book-item.blade.php
  15. 0 118
      api-v12/resources/views/components/book-list.blade.php
  16. 40 0
      api-v12/resources/views/components/ui/author-avatar.blade.php
  17. 46 0
      api-v12/resources/views/components/ui/book-cover.blade.php
  18. 21 0
      api-v12/resources/views/components/ui/book-grid.blade.php
  19. 69 0
      api-v12/resources/views/components/ui/card-anthology.blade.php
  20. 52 0
      api-v12/resources/views/components/ui/card-book.blade.php
  21. 13 0
      api-v12/resources/views/layouts/reader.blade.php
  22. 48 487
      api-v12/resources/views/library/anthology/index.blade.php
  23. 68 598
      api-v12/resources/views/library/anthology/show.blade.php
  24. 28 0
      api-v12/resources/views/library/book/_toc.blade.php
  25. 316 506
      api-v12/resources/views/library/book/read.blade.php
  26. 0 143
      api-v12/resources/views/library/book/show.blade.php
  27. 0 62
      api-v12/resources/views/library/category.blade.php
  28. 34 64
      api-v12/resources/views/library/index.blade.php
  29. 96 0
      api-v12/resources/views/library/tipitaka/category.blade.php
  30. 162 0
      api-v12/resources/views/library/tipitaka/show.blade.php

+ 29 - 2
api-v12/app/Http/Controllers/CategoryController.php

@@ -3,6 +3,7 @@
 namespace App\Http\Controllers;
 
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\File;
 
 use Illuminate\Support\Facades\DB;
 use App\Models\PaliText;
@@ -13,6 +14,22 @@ use App\Models\TagMap;
 
 class CategoryController extends Controller
 {
+    // 封面渐变色池:uid 首字节取余循环,保证同一文集颜色稳定
+    private array $coverGradients = [
+        'linear-gradient(160deg, #2d1020, #ae6b8b)',
+        'linear-gradient(160deg, #1a2d10,rgba(75, 114, 36, 0.61))',
+        'linear-gradient(160deg, #0d1f3c,rgb(55, 98, 150))',
+        'linear-gradient(160deg, #2d1020,rgb(151, 69, 94))',
+        'linear-gradient(160deg, #1a1a2d,rgb(76, 68, 146))',
+        'linear-gradient(160deg, #1a2820,rgb(55, 124, 99))',
+    ];
+    // -------------------------------------------------------------------------
+    // 从 uid / id 字符串中提取一个稳定的整数,用于色池取余
+    // -------------------------------------------------------------------------
+    private function colorIndex(string $uid): int
+    {
+        return hexdec(substr(str_replace('-', '', $uid), 0, 4)) % 255;
+    }
     protected static int $nextId = 1;
     public function home()
     {
@@ -73,7 +90,7 @@ class CategoryController extends Controller
         // 获取面包屑
         $breadcrumbs = $this->getBreadcrumbs($currentCategory, $categories);
 
-        return view('library.category', compact('currentCategory', 'subCategories', 'categoryBooks', 'breadcrumbs'));
+        return view('library.tipitaka.category', compact('currentCategory', 'subCategories', 'categoryBooks', 'breadcrumbs'));
     }
 
 
@@ -166,13 +183,23 @@ class CategoryController extends Controller
                 return $item->book == $book->book
                     && $item->paragraph == $book->para;
             })?->pcd_book_id;
+
+            $coverFile = "/assets/images/cover/zh-hans/1/{$pcd_book_id}.png";
+            if (File::exists(public_path($coverFile))) {
+                $coverUrl = $coverFile;
+            } else {
+                $coverUrl = null;
+            }
+            $colorIdx = $this->colorIndex($book->uid);
+
             $categoryBooks[] = [
                 "id" => $book->uid,
                 "title" => $title,
                 "author" => $book->channel->name,
                 "publisher" => $book->channel->owner,
                 "type" => __('labels.' . $book->channel->type),
-                "cover" => "/assets/images/cover/zh-hans/1/{$pcd_book_id}.png",
+                "cover" => $coverUrl,
+                'cover_gradient' => $this->coverGradients[$colorIdx % count($this->coverGradients)],
                 "description" => $book->summary ?? "比库戒律的详细说明",
                 "language" => __('language.' . $book->channel->lang),
             ];

+ 1 - 1
api-v12/app/Http/Controllers/Library/BookController.php

@@ -44,7 +44,7 @@ class BookController extends Controller
             $otherVersions[] = $this->getBookInfo($book);
         });
 
-        return view('library.book.show', compact('book', 'otherVersions'));
+        return view('library.tipitaka.show', compact('book', 'otherVersions'));
     }
 
 

+ 4 - 1
api-v12/app/Providers/AppServiceProvider.php

@@ -8,7 +8,8 @@ use Godruoyi\Snowflake\LaravelSequenceResolver;
 use App\Tools\QueryBuilderMacro;
 use Illuminate\Database\Query\Builder as QueryBuilder;
 use App\Services\RomanizeService;
-
+use Illuminate\Support\Facades\View;
+use App\View\Composers\BlogViewComposer;
 
 class AppServiceProvider extends ServiceProvider
 {
@@ -42,5 +43,7 @@ class AppServiceProvider extends ServiceProvider
     {
         //
         QueryBuilder::mixin($queryBuilderMacro = $this->app->make(QueryBuilderMacro::class));
+        View::composer('blog.*', BlogViewComposer::class);
+        View::composer('layouts.blog', BlogViewComposer::class);
     }
 }

+ 91 - 0
api-v12/app/View/Composers/BlogViewComposer.php

@@ -0,0 +1,91 @@
+<?php
+
+// app/View/Composers/BlogViewComposer.php
+// 自动向所有 blog.* 视图注入右边栏数据。
+// 在 AppServiceProvider 的 boot() 方法中注册:
+//
+//   use App\View\Composers\BlogViewComposer;
+//   View::composer('blog.*', BlogViewComposer::class);
+//   View::composer('layouts.blog', BlogViewComposer::class);
+//
+// 注意:layouts.blog 也需要注册,否则布局文件拿不到数据。
+
+namespace App\View\Composers;
+
+use Illuminate\View\View;
+
+class BlogViewComposer
+{
+    public function compose(View $view): void
+    {
+        $data = $view->getData();
+
+        // $user 由各 Controller 方法传入,Composer 不覆盖
+        $user = $data['user'] ?? null;
+
+        if (!$user) {
+            return;
+        }
+
+        $userName = $user['userName'] ?? null;
+
+        if (!$userName) {
+            return;
+        }
+
+        // 避免重复查询:如果已有数据则跳过
+        if ($view->offsetExists('categories') && $view->offsetExists('archives')) {
+            return;
+        }
+
+        // 以下查询根据你的实际 Service/Model 替换
+        // 示例结构,保持与 BlogController 现有数据结构一致
+
+        // Categories:[['id' => ..., 'label' => ...], ...]
+        if (!$view->offsetExists('categories')) {
+            $view->with('categories', $this->getCategories($userName));
+        }
+
+        // Tags:[['name' => ...], ...]
+        if (!$view->offsetExists('tags')) {
+            $view->with('tags', $this->getTags($userName));
+        }
+
+        // Archives:[['year' => ..., 'count' => ...], ...]
+        if (!$view->offsetExists('archives')) {
+            $view->with('archives', $this->getArchives($userName));
+        }
+    }
+
+    protected function getCategories(string $userName): array
+    {
+        // 替换为你的实际查询
+        // 例:return BlogService::getCategories($userName);
+        return [
+            ['id' => 'dharma',   'label' => '佛法'],
+            ['id' => 'vinaya',   'label' => '戒律'],
+            ['id' => 'pali',     'label' => '巴利文'],
+            ['id' => 'practice', 'label' => '禅修'],
+        ];
+    }
+
+    protected function getTags(string $userName): array
+    {
+        // 替换为你的实际查询
+        return [
+            ['name' => 'Pāli'],
+            ['name' => 'Vinaya'],
+            ['name' => 'Sutta'],
+            ['name' => 'Abhidhamma'],
+            ['name' => '三藏'],
+            ['name' => '注释'],
+        ];
+    }
+
+    protected function getArchives(string $userName): array
+    {
+        // 替换为你的实际查询
+        // 返回格式:[['year' => 2024, 'count' => 12], ...]
+        return [['year' => 2024, 'count' => 12]];
+    }
+}

+ 0 - 0
api-v12/public/assets/css/blog/style.min.663803bebe609202d5b39d848f2d7c2dc8b598a2d879efa079fa88893d29c49c.css → api-v12/public/assets/css/blog/style.min.css


+ 697 - 0
api-v12/resources/css/modules/_anthology.css

@@ -0,0 +1,697 @@
+/* resources/css/modules/_anthology.css
+   文集栏目专属样式。
+   来源:anthology/index.blade.php + anthology/show.blade.php 内联 <style>,
+   两处重复规则已去重,以实际渲染效果为准。
+*/
+
+/* ══════════════════════════════════════════
+   一、封面组件(.book-cover)
+   对应 components/ui/book-cover.blade.php
+   ══════════════════════════════════════════ */
+
+.book-cover {
+    position: relative;
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+}
+
+/* 尺寸 */
+.book-cover--sm  { width: 34px;  min-width: 34px;  height: 46px; }
+.book-cover--md  { width: 130px; min-width: 130px; height: 180px; }
+.book-cover--lg  { width: 155px; min-width: 155px; height: 215px; border-radius: 3px 9px 9px 3px; }
+
+/* 3D 书脊 */
+.book-cover--3d {
+    box-shadow:
+        -4px 0 0 rgba(0,0,0,.3),
+        -6px 4px 14px rgba(0,0,0,.4),
+        4px 4px 18px rgba(0,0,0,.3);
+}
+
+.book-cover--3d::before {
+    content: '';
+    position: absolute;
+    left: 0; top: 0; bottom: 0;
+    width: 13px;
+    background: linear-gradient(to right, rgba(0,0,0,.4), rgba(0,0,0,.1));
+    border-radius: 3px 0 0 3px;
+    z-index: 2;
+}
+
+/* 纹理叠加 */
+.book-cover::after {
+    content: '';
+    position: absolute;
+    inset: 0;
+    background: repeating-linear-gradient(
+        45deg, transparent, transparent 8px,
+        rgba(255,255,255,.015) 8px, rgba(255,255,255,.015) 9px
+    );
+    z-index: 1;
+}
+
+/* 图片封面 */
+.book-cover__img {
+    position: absolute;
+    inset: 0;
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+}
+
+/* 文字封面 */
+.book-cover__text {
+    position: relative;
+    z-index: 3;
+    text-align: center;
+    padding: 0 .5rem;
+}
+
+.book-cover__title {
+    font-family: 'Noto Serif SC', 'Noto Serif', Georgia, serif;
+    font-size: 1rem;
+    font-weight: 600;
+    color: #fff;
+    line-height: 1.6;
+    letter-spacing: .12em;
+    word-break: break-all;
+}
+
+.book-cover--sm .book-cover__title {
+    font-size: .6rem;
+    letter-spacing: .04em;
+    line-height: 1.3;
+}
+
+.book-cover__divider {
+    width: 28px;
+    height: 1px;
+    background: var(--wp-brand);
+    margin: .5rem auto;
+}
+
+.book-cover__subtitle {
+    font-size: .65rem;
+    color: rgba(255,255,255,.45);
+    letter-spacing: .04em;
+}
+
+/* ══════════════════════════════════════════
+   二、作者头像组件(.author-avatar)
+   对应 components/ui/author-avatar.blade.php
+   ══════════════════════════════════════════ */
+
+.author-avatar {
+    display: flex;
+    align-items: center;
+    gap: .5rem;
+}
+
+.author-avatar__img,
+.author-avatar__initials {
+    border-radius: 50%;
+    flex-shrink: 0;
+    object-fit: cover;
+}
+
+/* 尺寸 */
+.author-avatar--sm .author-avatar__img,
+.author-avatar--sm .author-avatar__initials {
+    width: 24px; height: 24px;
+    font-size: .65rem;
+}
+
+.author-avatar--md .author-avatar__img,
+.author-avatar--md .author-avatar__initials {
+    width: 28px; height: 28px;
+    font-size: .68rem;
+}
+
+.author-avatar--lg .author-avatar__img,
+.author-avatar--lg .author-avatar__initials {
+    width: 48px; height: 48px;
+    font-size: .95rem;
+}
+
+.author-avatar__initials {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-weight: 700;
+    color: #fff;
+}
+
+.author-avatar__name {
+    font-size: .8rem;
+    color: var(--wp-ink-soft);
+    font-weight: 500;
+    display: block;
+}
+
+.author-avatar--lg .author-avatar__name {
+    font-size: .9rem;
+}
+
+.author-avatar__sub {
+    font-size: .72rem;
+    color: var(--wp-ink-muted);
+    display: block;
+}
+
+/* ══════════════════════════════════════════
+   三、文集卡片(.anthology-card)
+   横向布局:封面左 + 内容右
+   ══════════════════════════════════════════ */
+
+.anthology-card {
+    background: var(--wp-card-bg);
+    border: 1px solid var(--wp-border);
+    border-radius: 10px;
+    overflow: hidden;
+    display: flex;
+    transition: box-shadow .25s, transform .25s;
+    margin-bottom: 1.1rem;
+    text-decoration: none;
+    color: inherit;
+}
+
+.anthology-card:hover {
+    box-shadow: 0 8px 28px rgba(200,134,10,.12), 0 2px 8px rgba(0,0,0,.06);
+    transform: translateY(-2px);
+    color: inherit;
+    text-decoration: none;
+}
+
+.anthology-card .book-cover {
+    border-radius: 0;
+}
+
+.anthology-card__body {
+    padding: 1.1rem 1.4rem;
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    min-width: 0;
+}
+
+.anthology-card__title {
+    font-family: 'Noto Serif SC', 'Noto Serif', Georgia, serif;
+    font-size: 1.1rem;
+    font-weight: 600;
+    color: var(--wp-ink);
+    margin-bottom: .35rem;
+    line-height: 1.4;
+}
+
+.anthology-card:hover .anthology-card__title { color: var(--wp-brand); }
+
+.anthology-card__desc {
+    font-size: .8rem;
+    color: var(--wp-ink-muted);
+    margin-bottom: .65rem;
+    line-height: 1.65;
+}
+
+.anthology-card__author { margin-bottom: .7rem; }
+
+.anthology-card__tags {
+    display: flex;
+    flex-wrap: wrap;
+    gap: .3rem;
+    margin-top: auto;
+}
+
+.anthology-tag {
+    font-size: .7rem;
+    color: var(--wp-ink-muted);
+    background: var(--wp-brand-light);
+    border: 1px solid var(--wp-border);
+    padding: 1px 7px;
+    border-radius: 4px;
+    white-space: nowrap;
+}
+
+.anthology-tag--more {
+    background: transparent;
+    border-color: transparent;
+    color: var(--wp-brand);
+}
+
+.anthology-card__meta {
+    display: flex;
+    align-items: center;
+    gap: .85rem;
+    margin-top: .65rem;
+    padding-top: .65rem;
+    border-top: 1px solid var(--wp-border);
+}
+
+.anthology-meta-item {
+    font-size: .72rem;
+    color: var(--wp-ink-muted);
+    display: flex;
+    align-items: center;
+    gap: .25rem;
+}
+
+/* ══════════════════════════════════════════
+   四、页面头部(index 页)
+   ══════════════════════════════════════════ */
+
+.anthology-page-header {
+    background: linear-gradient(135deg, var(--wp-ink) 0%, #2d2010 100%);
+    padding: 2.25rem 0 2rem;
+    position: relative;
+    overflow: hidden;
+}
+
+.anthology-page-header::before {
+    content: '藏';
+    font-family: 'Noto Serif SC', serif;
+    font-size: 16rem;
+    font-weight: 700;
+    color: rgba(255,255,255,.03);
+    position: absolute;
+    right: -1rem;
+    top: -2.5rem;
+    line-height: 1;
+    pointer-events: none;
+}
+
+.anthology-page-header h1 {
+    font-family: 'Noto Serif SC', 'Noto Serif', Georgia, serif;
+    font-size: 1.75rem;
+    font-weight: 600;
+    color: #fff;
+    margin: 0 0 .3rem;
+    letter-spacing: .08em;
+}
+
+.anthology-page-header p {
+    color: rgba(255,255,255,.45);
+    font-size: .85rem;
+    margin: 0;
+}
+
+.result-badge {
+    background: var(--wp-brand);
+    color: var(--wp-ink);
+    font-size: .75rem;
+    font-weight: 700;
+    padding: 2px 9px;
+    border-radius: 20px;
+    margin-left: .6rem;
+    vertical-align: middle;
+}
+
+/* ══════════════════════════════════════════
+   五、侧边栏卡片(index + show 共用)
+   ══════════════════════════════════════════ */
+
+.sb-card {
+    background: var(--wp-card-bg);
+    border: 1px solid var(--wp-border);
+    border-radius: 10px;
+    overflow: hidden;
+    margin-bottom: 1.15rem;
+}
+
+.sb-head {
+    padding: .8rem 1.2rem;
+    border-bottom: 1px solid var(--wp-border);
+    font-family: 'Noto Serif SC', 'Noto Serif', Georgia, serif;
+    font-size: .875rem;
+    font-weight: 600;
+    color: var(--wp-ink-soft);
+    letter-spacing: .04em;
+    display: flex;
+    align-items: center;
+    gap: .45rem;
+}
+
+.sb-head::before {
+    content: '';
+    display: block;
+    width: 3px;
+    height: 13px;
+    background: var(--wp-brand);
+    border-radius: 2px;
+}
+
+/* 侧边栏 meta 行 */
+.smeta-row {
+    display: flex;
+    padding: .7rem 1.2rem;
+    border-bottom: 1px solid var(--wp-border);
+    font-size: .8rem;
+    align-items: flex-start;
+    gap: .45rem;
+}
+
+.smeta-row:last-child { border-bottom: none; }
+
+.smeta-label {
+    color: var(--wp-ink-muted);
+    min-width: 65px;
+    flex-shrink: 0;
+}
+
+.smeta-value {
+    color: var(--wp-ink-soft);
+    font-weight: 500;
+}
+
+.smeta-value a {
+    color: var(--wp-brand);
+    text-decoration: none;
+}
+
+.smeta-value a:hover { text-decoration: underline; }
+
+/* 作者块(show 页侧边栏大头像版) */
+.author-block {
+    display: flex;
+    align-items: center;
+    gap: .8rem;
+    padding: 1.1rem 1.2rem;
+}
+
+.author-block-name {
+    font-weight: 600;
+    font-size: .9rem;
+    color: var(--wp-ink);
+    margin-bottom: .18rem;
+}
+
+.author-block-stats {
+    font-size: .75rem;
+    color: var(--wp-ink-muted);
+}
+
+.author-bio {
+    font-size: .78rem;
+    color: var(--wp-ink-muted);
+    line-height: 1.65;
+    padding: .9rem 1.2rem 1.1rem;
+    border-top: 1px solid var(--wp-border);
+}
+
+/* 相关文集列表 */
+.related-ul {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+.related-ul li a {
+    display: flex;
+    align-items: center;
+    gap: .7rem;
+    padding: .7rem 1.2rem;
+    border-bottom: 1px solid var(--wp-border);
+    text-decoration: none;
+    transition: background .15s;
+}
+
+.related-ul li:last-child a { border-bottom: none; }
+.related-ul li a:hover { background: var(--wp-surface-alt); }
+
+.related-t {
+    font-size: .8rem;
+    color: var(--wp-ink-soft);
+    font-weight: 500;
+    margin-bottom: .18rem;
+    line-height: 1.3;
+}
+
+.related-ul li a:hover .related-t { color: var(--wp-brand); }
+
+.related-a {
+    font-size: .7rem;
+    color: var(--wp-ink-muted);
+}
+
+/* 作者列表(index 侧边栏) */
+.author-ul {
+    list-style: none;
+    padding: .35rem 0;
+    margin: 0;
+}
+
+.author-ul li a {
+    display: flex;
+    align-items: center;
+    gap: .6rem;
+    padding: .45rem 1.15rem;
+    text-decoration: none;
+    transition: background .15s;
+}
+
+.author-ul li a:hover { background: var(--wp-surface-alt); }
+
+/* ══════════════════════════════════════════
+   六、文章目录(show 页)
+   ══════════════════════════════════════════ */
+
+.sec-card {
+    background: var(--wp-card-bg);
+    border: 1px solid var(--wp-border);
+    border-radius: 10px;
+    overflow: hidden;
+    margin-bottom: 1.3rem;
+}
+
+.sec-header {
+    padding: .85rem 1.4rem;
+    border-bottom: 1px solid var(--wp-border);
+    display: flex;
+    align-items: center;
+    gap: .55rem;
+}
+
+.sec-bar {
+    width: 3px;
+    height: 15px;
+    background: var(--wp-brand);
+    border-radius: 2px;
+    flex-shrink: 0;
+}
+
+.sec-title {
+    font-family: 'Noto Serif SC', 'Noto Serif', Georgia, serif;
+    font-size: .9rem;
+    font-weight: 600;
+    color: var(--wp-ink-soft);
+    letter-spacing: .04em;
+}
+
+.sec-count {
+    margin-left: auto;
+    font-size: .75rem;
+    color: var(--wp-ink-muted);
+    background: var(--wp-brand-light);
+    padding: 2px 8px;
+    border-radius: 10px;
+}
+
+.sec-body {
+    padding: 1.15rem 1.4rem;
+    font-size: .855rem;
+    color: var(--wp-ink-soft);
+    line-height: 1.95;
+}
+
+.sec-body p            { margin-bottom: .8rem; }
+.sec-body p:last-child { margin-bottom: 0; }
+
+/* TOC 列表 */
+.toc-ul {
+    list-style: none;
+    padding: .35rem 0;
+    margin: 0;
+}
+
+.toc-ul li a {
+    display: flex;
+    align-items: center;
+    padding: .65rem 1.4rem;
+    text-decoration: none;
+    border-bottom: 1px solid rgba(232,221,208,.5);
+    transition: background .15s;
+}
+
+.toc-ul li:last-child a { border-bottom: none; }
+.toc-ul li a:hover      { background: var(--wp-surface-alt); }
+
+.toc-num {
+    font-size: .72rem;
+    color: var(--wp-ink-muted);
+    width: 26px;
+    flex-shrink: 0;
+}
+
+.toc-name {
+    font-size: .855rem;
+    color: var(--wp-ink-soft);
+    flex: 1;
+    line-height: 1.4;
+}
+
+.toc-ul li a:hover .toc-name { color: var(--wp-brand); }
+
+.toc-arrow {
+    color: var(--wp-border);
+    font-size: .85rem;
+}
+
+.toc-ul li a:hover .toc-arrow { color: var(--wp-brand); }
+
+/* ══════════════════════════════════════════
+   七、Hero(show 页)
+   ══════════════════════════════════════════ */
+
+.anthology-hero {
+    background: linear-gradient(135deg, var(--wp-ink) 0%, #2d2010 100%);
+    padding: 2.5rem 0;
+}
+
+.hero-inner {
+    display: flex;
+    gap: 2.25rem;
+    align-items: flex-start;
+}
+
+.hero-content { flex: 1; min-width: 0; }
+
+.hero-title {
+    font-family: 'Noto Serif SC', 'Noto Serif', Georgia, serif;
+    font-size: 1.75rem;
+    font-weight: 700;
+    color: #fff;
+    line-height: 1.3;
+    margin-bottom: .4rem;
+}
+
+.hero-subtitle {
+    font-size: .88rem;
+    color: rgba(255,255,255,.45);
+    font-style: italic;
+    letter-spacing: .04em;
+    margin-bottom: 1.1rem;
+}
+
+.hero-tags {
+    display: flex;
+    flex-wrap: wrap;
+    gap: .35rem;
+    margin-bottom: 1.3rem;
+}
+
+.hero-tag {
+    font-size: .72rem;
+    padding: 2px 9px;
+    border-radius: 20px;
+    background: rgba(200,134,10,.2);
+    color: var(--wp-brand);
+    border: 1px solid rgba(200,134,10,.3);
+}
+
+.hero-info-row {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 1.4rem;
+    margin-bottom: 1.3rem;
+}
+
+.hi-item { display: flex; align-items: center; gap: .45rem; }
+
+.hi-label {
+    font-size: .72rem;
+    color: rgba(255,255,255,.4);
+    letter-spacing: .04em;
+    display: block;
+}
+
+.hi-value {
+    font-size: .83rem;
+    color: rgba(255,255,255,.82);
+    display: block;
+}
+
+.hero-desc {
+    font-size: .85rem;
+    color: rgba(255,255,255,.6);
+    line-height: 1.85;
+    margin-bottom: 1.6rem;
+    max-width: 600px;
+}
+
+/* Hero 按钮 */
+.btn-read-primary {
+    background: var(--wp-brand);
+    color: var(--wp-ink);
+    font-weight: 700;
+    font-size: .88rem;
+    padding: .55rem 1.6rem;
+    border-radius: 6px;
+    border: none;
+    cursor: pointer;
+    text-decoration: none;
+    display: inline-flex;
+    align-items: center;
+    gap: .45rem;
+    transition: background .2s, transform .15s;
+}
+
+.btn-read-primary:hover {
+    background: #dea020;
+    color: var(--wp-ink);
+    transform: translateY(-1px);
+}
+
+.btn-outline-hero {
+    background: transparent;
+    color: rgba(255,255,255,.7);
+    font-size: .85rem;
+    padding: .5rem 1.3rem;
+    border-radius: 6px;
+    border: 1px solid rgba(255,255,255,.2);
+    cursor: pointer;
+    text-decoration: none;
+    display: inline-flex;
+    align-items: center;
+    gap: .4rem;
+    transition: all .2s;
+    margin-left: .65rem;
+}
+
+.btn-outline-hero:hover {
+    border-color: rgba(255,255,255,.5);
+    color: #fff;
+}
+
+/* ══════════════════════════════════════════
+   八、响应式
+   ══════════════════════════════════════════ */
+
+@media (max-width: 900px) {
+    .hero-inner {
+        flex-direction: column;
+        align-items: center;
+    }
+}
+
+@media (max-width: 768px) {
+    .anthology-card {
+        flex-direction: column;
+    }
+
+    .anthology-card .book-cover--md {
+        width: 100%;
+        min-width: unset;
+        height: 90px;
+    }
+}

+ 313 - 0
api-v12/resources/css/modules/_reader.css

@@ -0,0 +1,313 @@
+/* resources/css/modules/_reader.css
+   全站阅读页专属样式。
+   来源:原 resources/css/reader.css(旧)+ reader 重构新增内容合并。
+   以旧 reader.css 内容为准,新增部分追加在末尾。
+*/
+
+/* ══════════════════════════════════════════
+   一、基础
+   ══════════════════════════════════════════ */
+
+body {
+    font-family: "Inter", sans-serif;
+    transition: background-color 0.3s, color 0.3s;
+}
+
+/* ══════════════════════════════════════════
+   二、主布局容器
+   ══════════════════════════════════════════ */
+
+.main-container {
+    display: flex;
+    gap: 20px;
+    padding: 20px;
+    max-width: 1400px;
+    margin: 0 auto;
+}
+
+/* ── TOC 侧边栏(左) ── */
+.toc-sidebar {
+    width: 250px;
+    flex-shrink: 0;
+    display: none;
+    position: sticky;
+    top: 0;
+    height: 100vh;
+    overflow-y: auto;
+    overflow-x: hidden;
+    scrollbar-width: thin;
+}
+
+.toc-sidebar .card-body {
+    height: 100%;
+    overflow-y: auto;
+    overflow-x: hidden;
+}
+
+/* ── 正文区(中)── */
+.content-area {
+    flex: 1;            /* 替代原 flex-grow:1,配合 min-width 防止收缩 */
+    min-width: 0;       /* 修复:内容较窄时列不收缩 */
+}
+
+/* ── 右侧边栏 ── */
+.right-sidebar {
+    width: 220px;       /* 收窄,原 300px 偏宽 */
+    flex-shrink: 0;
+    display: none;
+}
+
+.related-books {
+    margin-top: 30px;
+}
+
+.card-img-container {
+    height: 150px;
+    overflow: hidden;
+}
+
+.card-img-container img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+}
+
+/* ══════════════════════════════════════════
+   三、响应式
+   ══════════════════════════════════════════ */
+
+@media (max-width: 767px) {
+    .content-area {
+        width: 100%;
+    }
+
+    .main-container {
+        padding: 0;
+    }
+
+    .card {
+        border: none;
+    }
+}
+
+@media (min-width: 768px) {
+    .toc-sidebar {
+        display: block;
+    }
+    /* content-area 不设 max-width,由 flex:1 + min-width:0 自动占满剩余空间 */
+}
+
+@media (min-width: 992px) {
+    .right-sidebar {
+        display: block;
+    }
+    /* 同上,不限制 max-width */
+}
+
+/* ══════════════════════════════════════════
+   四、暗色模式
+   ══════════════════════════════════════════ */
+
+.dark-mode {
+    background-color: #1a1a1a;
+    color: #ffffff;
+}
+
+.dark-mode .card {
+    background-color: #2a2a2a;
+    border-color: #3a3a3a;
+    color: #ffffff;
+}
+
+.dark-mode .navbar {
+    background-color: #2a2a2a;
+}
+
+.dark-mode .offcanvas {
+    background-color: #2a2a2a;
+    color: #ffffff;
+}
+
+.dark-mode .offcanvas .nav-link {
+    color: #ffffff;
+}
+
+.dark-mode .toc-sidebar,
+.dark-mode .right-sidebar {
+    background-color: #2a2a2a;
+}
+
+/* ══════════════════════════════════════════
+   五、目录样式(wiki toc 风格)
+   ══════════════════════════════════════════ */
+
+.toc-sidebar ul,
+.offcanvas-body ul {
+    list-style: none;
+    padding: 0;
+}
+
+.toc-sidebar ul li,
+.offcanvas-body ul li {
+    padding: 5px 0;
+}
+
+.toc-sidebar ul li a,
+.offcanvas-body ul li a {
+    color: #206bc4;
+    text-decoration: none;
+}
+
+.toc-sidebar ul li a:hover      { text-decoration: none; }
+.offcanvas-body ul li a:hover   { text-decoration: underline; }
+
+.dark-mode .toc-sidebar ul li a,
+.dark-mode .offcanvas-body ul li a {
+    color: #4dabf7;
+}
+
+.toc-level-1 { padding-left: 0 !important; }
+.toc-level-2 { padding-left: 16px !important; }
+.toc-level-3 { padding-left: 24px !important; }
+.toc-level-4 { padding-left: 36px !important; }
+
+.toc-disabled {
+    color: #6c757d;
+    cursor: not-allowed;
+    pointer-events: none;
+}
+
+.dark-mode .toc-disabled { color: #adb5bd; }
+
+.toc_item {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+.toc_item:hover {
+    color: #206bc4 !important;
+    background: rgba(32, 107, 196, 0.15);
+    border-radius: 2px;
+    cursor: pointer;
+}
+
+.toc_item a,
+.toc_item span {
+    display: block;
+    width: 100%;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+.toc-active {
+    color: #206bc4 !important;
+    font-weight: 600;
+    background: rgba(32, 107, 196, 0.08);
+    border-left: 3px solid #206bc4;
+    border-radius: 2px;
+    cursor: default;
+    pointer-events: none;
+}
+
+.dark-mode .toc-active {
+    color: #4dabf7 !important;
+    background: rgba(77, 171, 247, 0.1);
+    border-left-color: #4dabf7;
+}
+
+/* ══════════════════════════════════════════
+   六、正文内容
+   ══════════════════════════════════════════ */
+
+.origin {
+    color: darkred;
+}
+
+/* 术语引用 */
+.term-ref {
+    cursor: pointer;
+    text-decoration: underline dotted;
+    text-underline-offset: 4px;
+}
+
+.term-ref:hover {
+    color: var(--tblr-primary);
+}
+
+.term_invalid {
+    color: red;
+}
+
+/* ══════════════════════════════════════════
+   七、Navbar 图标尺寸修正
+   ti 图标显式设定字号,防止继承后偏小
+   ══════════════════════════════════════════ */
+
+.navbar .nav-link .ti {
+    font-size: 1.125rem;
+    vertical-align: middle;
+}
+
+/* ══════════════════════════════════════════
+   八、版本卡片(右侧边栏,wiki 侧边栏同款)
+   ══════════════════════════════════════════ */
+
+.reader-channel-card {
+    background: var(--tblr-bg-surface);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: var(--tblr-border-radius-lg);
+    padding: 1rem 1.125rem;
+    margin-bottom: 1rem;
+}
+
+.dark-mode .reader-channel-card {
+    background-color: #2a2a2a;
+    border-color: #3a3a3a;
+}
+
+.reader-channel-title {
+    font-size: 0.6875rem;
+    font-weight: 500;
+    letter-spacing: 0.05em;
+    text-transform: uppercase;
+    color: var(--tblr-secondary);
+    margin-bottom: 0.75rem;
+}
+
+.reader-channel-list {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+.reader-channel-list li {
+    margin-bottom: 2px;
+}
+
+.reader-channel-list a {
+    display: block;
+    font-size: 0.8125rem;
+    color: var(--tblr-body-color);
+    text-decoration: none;
+    padding: 5px 8px;
+    border-radius: var(--tblr-border-radius);
+    transition: background 0.12s;
+}
+
+.reader-channel-list a:hover {
+    background: var(--tblr-bg-surface-secondary);
+}
+
+.reader-channel-list a.active {
+    background: var(--tblr-bg-surface-secondary);
+    font-weight: 500;
+    color: var(--tblr-primary);
+}
+
+.reader-channel-lang {
+    font-size: 0.75rem;
+    color: var(--tblr-secondary);
+    display: block;
+}

+ 132 - 0
api-v12/resources/css/modules/_tipitaka.css

@@ -0,0 +1,132 @@
+/* resources/css/modules/_tipitaka.css
+   Tipitaka 栏目专属样式。
+   来源:book-list.blade.php / book-item.blade.php 内联样式提取,
+   去除 CDN 引入,风格与 wiki 对齐。
+*/
+
+/* ══════════════════════════════════════════
+   一、书籍卡片(.card-book)
+   纵向:封面上 + 信息下
+   ══════════════════════════════════════════ */
+
+.card-book {
+    transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.card-book:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+}
+
+.card-book__link {
+    display: block;
+    text-decoration: none;
+    color: inherit;
+}
+
+.card-book__link:hover {
+    text-decoration: none;
+    color: inherit;
+}
+
+/* 封面:撑满卡片宽度 */
+.card-book .book-cover--md {
+    width: 100%;
+    min-width: unset;
+    height: 200px;
+    border-radius: var(--tblr-border-radius);
+}
+
+@media (max-width: 768px) {
+    .card-book .book-cover--md {
+        height: 150px;
+    }
+}
+
+/* 信息区 */
+.card-book__info {
+    padding: 0.75rem 0 0;
+}
+
+.card-book__title {
+    font-size: 0.9375rem;
+    font-weight: 600;
+    color: var(--tblr-body-color);
+    margin-bottom: 0.25rem;
+    line-height: 1.4;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+}
+
+.card-book:hover .card-book__title {
+    color: var(--tblr-primary);
+}
+
+.card-book__author {
+    font-size: 0.8125rem;
+    color: var(--tblr-secondary);
+    margin-bottom: 0.25rem;
+}
+
+.card-book__publisher {
+    font-size: 0.8125rem;
+    color: var(--tblr-secondary);
+    margin-bottom: 0.375rem;
+}
+
+.card-book__publisher-link {
+    color: var(--tblr-primary);
+    text-decoration: none;
+}
+
+.card-book__publisher-link:hover {
+    text-decoration: underline;
+}
+
+.card-book__badges {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 4px;
+    margin-top: 0.375rem;
+}
+
+.card-book__badge {
+    display: inline-block;
+    padding: 2px 8px;
+    background: var(--tblr-bg-surface-secondary);
+    border: 1px solid var(--tblr-border-color);
+    border-radius: 20px;
+    font-size: 0.6875rem;
+    color: var(--tblr-secondary);
+}
+
+/* ══════════════════════════════════════════
+   二、书籍网格(.book-grid)
+   ══════════════════════════════════════════ */
+
+.book-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+    gap: 1.25rem;
+}
+
+@media (max-width: 575px) {
+    .book-grid {
+        grid-template-columns: repeat(2, 1fr);
+        gap: 0.875rem;
+    }
+}
+
+/* ══════════════════════════════════════════
+   三、show 页 — 文字截断
+   ══════════════════════════════════════════ */
+
+.line2 {
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}

+ 0 - 163
api-v12/resources/css/modules/_wiki_home_append.css

@@ -1,163 +0,0 @@
-/* ══════════════════════════════════════════
-   十五、Wiki 首页(home.blade.php)
-   替代原 home.blade.php 内联 <style>,风格与 wiki 整体保持一致
-   ══════════════════════════════════════════ */
-
-.wiki-home-container {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    min-height: calc(100vh - 300px);
-    padding: 3rem 1.5rem;
-}
-
-/* ── 法轮图标 ── */
-.wiki-home-wheel {
-    margin-bottom: 1.5rem;
-}
-
-.wiki-home-wheel-img {
-    width: 120px;
-    height: auto;
-    opacity: 0.85;
-    transition: transform 0.3s ease;
-}
-
-.wiki-home-wheel-img:hover {
-    transform: scale(1.05);
-}
-
-/* ── 标题 ── */
-.wiki-home-title {
-    text-align: center;
-    margin-bottom: 2rem;
-}
-
-.wiki-home-title h1 {
-    font-family: "Noto Serif", Georgia, serif;
-    font-size: 2.25rem;
-    font-weight: 600;
-    color: var(--tblr-body-color);
-    margin-bottom: 0.5rem;
-}
-
-/* ── 搜索框 ── */
-.wiki-home-search {
-    width: 100%;
-    max-width: 640px;
-    margin: 0 auto 1.5rem;
-}
-
-/* ── 热门标签 ── */
-.wiki-home-hot-tags {
-    text-align: center;
-    margin-bottom: 2.5rem;
-    font-size: 0.9rem;
-}
-
-.wiki-hot-tag {
-    display: inline-block;
-    padding: 0.3rem 0.75rem;
-    background: var(--tblr-bg-surface-secondary);
-    border: 1px solid var(--tblr-border-color);
-    border-radius: 20px;
-    color: var(--tblr-secondary);
-    font-size: 0.8125rem;
-    text-decoration: none;
-    margin: 2px;
-    transition: background 0.12s, color 0.12s;
-}
-
-.wiki-hot-tag:hover {
-    background: var(--wp-brand-light);
-    border-color: var(--wp-brand);
-    color: var(--wp-brand-dark);
-}
-
-/* ── 语言选择 ── */
-.wiki-home-languages {
-    width: 100%;
-    max-width: 800px;
-    margin: 0 auto 2rem;
-}
-
-.wiki-home-divider {
-    display: flex;
-    align-items: center;
-    text-align: center;
-    margin-bottom: 1.5rem;
-    gap: 1.5rem;
-    color: var(--tblr-secondary);
-    font-size: 0.875rem;
-}
-
-.wiki-home-divider::before,
-.wiki-home-divider::after {
-    content: '';
-    flex: 1;
-    border-bottom: 1px solid var(--tblr-border-color);
-}
-
-.wiki-language-tags {
-    display: flex;
-    flex-wrap: wrap;
-    justify-content: center;
-    gap: 0.75rem;
-}
-
-.wiki-language-tag {
-    display: inline-block;
-    padding: 0.4rem 1.125rem;
-    background: var(--tblr-bg-surface-secondary);
-    border: 1px solid var(--tblr-border-color);
-    border-radius: 30px;
-    color: var(--tblr-body-color);
-    font-size: 0.875rem;
-    text-decoration: none;
-    transition: background 0.12s, border-color 0.12s;
-}
-
-.wiki-language-tag:hover {
-    background: var(--wp-brand-light);
-    border-color: var(--wp-brand);
-    color: var(--wp-brand-dark);
-}
-
-.wiki-language-tag.active {
-    background: var(--wp-brand);
-    border-color: var(--wp-brand);
-    color: #fff;
-}
-
-.wiki-language-tag.active:hover {
-    background: var(--wp-brand-dark);
-    border-color: var(--wp-brand-dark);
-}
-
-/* ── 统计 ── */
-.wiki-home-stats {
-    text-align: center;
-    font-size: 0.875rem;
-    padding-top: 1.5rem;
-    border-top: 1px solid var(--tblr-border-color);
-    width: 100%;
-    max-width: 640px;
-}
-
-/* ── 响应式 ── */
-@media (max-width: 768px) {
-    .wiki-home-container {
-        min-height: calc(100vh - 200px);
-        padding: 2rem 1rem;
-    }
-
-    .wiki-home-wheel-img { width: 90px; }
-
-    .wiki-home-title h1  { font-size: 1.75rem; }
-
-    .wiki-language-tag {
-        padding: 0.35rem 0.875rem;
-        font-size: 0.8125rem;
-    }
-}

+ 5 - 237
api-v12/resources/css/reader.css

@@ -1,238 +1,6 @@
-/* resources/css/reader.css */
+/* resources/css/reader.css
+   全站阅读页 CSS 入口。
+   只做 @import,不写任何样式规则。
+*/
 
-/* 基础样式 */
-body {
-    font-family: "Inter", sans-serif;
-    transition: background-color 0.3s, color 0.3s;
-}
-
-.main-container {
-    display: flex;
-    gap: 20px;
-    padding: 20px;
-    max-width: 1400px;
-    margin: 0 auto;
-}
-
-.toc-sidebar {
-    width: 250px;
-    flex-shrink: 0;
-    display: none;
-    position: sticky;
-    top: 0;
-    height: 100vh;
-    overflow-y: auto;
-    overflow-x: hidden;
-    scrollbar-width: thin;
-}
-
-.toc-sidebar .card-body {
-    height: 100%;
-    overflow-y: auto;
-    overflow-x: hidden;
-}
-
-.content-area {
-    flex-grow: 1;
-    max-width: 100%;
-}
-
-.right-sidebar {
-    width: 300px;
-    flex-shrink: 0;
-    display: none;
-}
-
-.related-books {
-    margin-top: 30px;
-}
-
-.card-img-container {
-    height: 150px;
-    overflow: hidden;
-}
-
-.card-img-container img {
-    width: 100%;
-    height: 100%;
-    object-fit: cover;
-}
-
-/* 响应式布局 */
-@media (max-width: 767px) {
-    .content-area {
-        width: 100%;
-    }
-
-    .main-container {
-        padding: 0;
-    }
-
-    .card {
-        border: none;
-    }
-}
-
-@media (min-width: 768px) {
-    .toc-sidebar {
-        display: block;
-    }
-
-    .content-area {
-        max-width: calc(100% - 270px);
-    }
-}
-
-@media (min-width: 992px) {
-    .right-sidebar {
-        display: block;
-    }
-
-    .content-area {
-        max-width: calc(100% - 570px);
-    }
-}
-
-/* 暗色模式 */
-.dark-mode {
-    background-color: #1a1a1a;
-    color: #ffffff;
-}
-
-.dark-mode .card {
-    background-color: #2a2a2a;
-    border-color: #3a3a3a;
-    color: #ffffff;
-}
-
-.dark-mode .navbar {
-    background-color: #2a2a2a;
-}
-
-.dark-mode .offcanvas {
-    background-color: #2a2a2a;
-    color: #ffffff;
-}
-
-.dark-mode .offcanvas .nav-link {
-    color: #ffffff;
-}
-
-.dark-mode .toc-sidebar,
-.dark-mode .right-sidebar {
-    background-color: #2a2a2a;
-}
-
-/* 目录样式 */
-.toc-sidebar ul,
-.offcanvas-body ul {
-    list-style: none;
-    padding: 0;
-}
-
-.toc-sidebar ul li,
-.offcanvas-body ul li {
-    padding: 5px 0;
-}
-
-.toc-sidebar ul li a,
-.offcanvas-body ul li a {
-    color: #206bc4;
-    text-decoration: none;
-}
-
-.toc-sidebar ul li a:hover {
-    text-decoration: none;
-}
-
-.offcanvas-body ul li a:hover {
-    text-decoration: underline;
-}
-
-.dark-mode .toc-sidebar ul li a,
-.dark-mode .offcanvas-body ul li a {
-    color: #4dabf7;
-}
-
-.toc-level-1 {
-    padding-left: 0 !important;
-}
-
-.toc-level-2 {
-    padding-left: 16px !important;
-}
-
-.toc-level-3 {
-    padding-left: 24px !important;
-}
-
-.toc-level-4 {
-    padding-left: 36px !important;
-}
-
-.toc-disabled {
-    color: #6c757d;
-    cursor: not-allowed;
-    pointer-events: none;
-}
-
-.dark-mode .toc-disabled {
-    color: #adb5bd;
-}
-
-.toc_item {
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-}
-
-.toc_item:hover {
-    color: #206bc4 !important;
-    background: rgba(32, 107, 196, 0.15);
-    border-radius: 2px;
-    cursor: pointer;
-}
-
-.toc_item a,
-.toc_item span {
-    display: block;
-    width: 100%;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-}
-
-.toc-active {
-    color: #206bc4 !important;
-    font-weight: 600;
-    background: rgba(32, 107, 196, 0.08);
-    border-left: 3px solid #206bc4;
-    border-radius: 2px;
-    cursor: default;
-    pointer-events: none;
-}
-
-.dark-mode .toc-active {
-    color: #4dabf7 !important;
-    background: rgba(77, 171, 247, 0.1);
-    border-left-color: #4dabf7;
-}
-
-.origin {
-    color: darkred;
-}
-
-/* 术语引用样式 */
-.term-ref {
-    cursor: pointer;
-    text-decoration: underline dotted;
-    text-underline-offset: 4px;
-}
-
-.term_invalid {
-    color: red;
-}
-
-.term-ref:hover {
-    color: var(--tblr-primary);
-}
+@import "./modules/_reader.css";

+ 29 - 18
api-v12/resources/views/blog/category.blade.php

@@ -1,56 +1,67 @@
+{{-- resources/views/blog/category.blade.php --}}
 @extends('blog.layouts.app')
 
-@section('title', $user["nickName"])
+@section('title', $user['nickName'] . ' · ' . collect($current)->pluck('label')->implode(' / '))
 
 @section('content')
 
 <header>
     <h3 class="section-title">Categories</h3>
-
     <div class="section-card">
         <div class="section-details">
-            <h3 class="section-count">{{ $count }} page</h3>
+            <h3 class="section-count">{{ $count }} </h3>
             <h1 class="section-term">
-                @foreach ($current as $category)
-                /<a href="{{ route('blog.category', ['user' => $user['userName'],'category1' => $category['id'],]) }}">
+                @foreach($current as $category)
+                / <a href="{{ route('blog.category', ['user' => $user['userName'], 'category1' => $category['id']]) }}">
                     {{ $category['label'] }}
                 </a>
                 @endforeach
             </h1>
+
+            {{-- 子分类标签 --}}
+            @if(!empty($tagOptions))
             <section class="widget tagCloud">
                 <div class="tagCloud-tags">
                     @foreach($tagOptions as $id => $tag)
-                    @if($tag['count'] < $count)
-                        <a href="{{ rtrim(url()->current(), '/') . '/' . $tag['tag']->name }}">{{ $tag['tag']->name }}({{ $tag['count'] }})</a>
+                        @if($tag['count'] < $count)
+                        <a href="{{ rtrim(url()->current(), '/') . '/' . $tag['tag']->name }}">
+                            {{ $tag['tag']->name }} ({{ $tag['count'] }})
+                        </a>
                         @endif
-                        @endforeach
+                    @endforeach
                 </div>
             </section>
+            @endif
         </div>
     </div>
 </header>
 
 <section class="article-list--compact">
-    @foreach ($posts as $post)
+    @forelse($posts as $post)
     <article>
-        <a href="{{ route('library.tipitaka.read', $post['uid']) }}">
+        <a href="{{ route('library.tipitaka.read', ['id' => $post['uid']]) }}">
             <div class="article-details">
                 <h2 class="article-title">{{ $post->title }}</h2>
                 <footer class="article-time">
-                    <time datetime="2022-03-06T00:00:00Z">{{ $post->formatted_updated_at }}</time>
+                    <time>{{ $post->formatted_updated_at }}</time>
                 </footer>
             </div>
+
+            @if(!empty($post->cover))
             <div class="article-image">
-                <img
-                    src="./Category_ Example Category - Hugo Theme Stack Starter_files/cover_hu6307248181568134095.jpg"
-                    width="120"
-                    height="120"
-                    alt="Hello World"
-                    loading="lazy" />
+                <img src="{{ $post->cover }}"
+                     width="120" height="120"
+                     alt="{{ $post->title }}"
+                     loading="lazy" />
             </div>
+            @endif
         </a>
     </article>
-    @endforeach
+    @empty
+    <div class="not-found-card" style="padding: 20px;">
+        <p>此分类下暂无文章</p>
+    </div>
+    @endforelse
 </section>
 
 @endsection

+ 33 - 414
api-v12/resources/views/blog/index.blade.php

@@ -1,453 +1,72 @@
+{{-- resources/views/blog/index.blade.php --}}
 @extends('blog.layouts.app')
 
-@section('title', $user["nickName"])
+@section('title', $user['nickName'])
 
 @section('content')
 <section class="article-list">
-    @foreach ($posts as $post)
-    <article class="">
+    @forelse($posts as $post)
+    <article class="{{ !empty($post->cover) ? 'has-image' : '' }}">
         <header class="article-header">
-            <div class="article-details">
-                <header class="article-category">
-                    <a href="https://demo.stack.jimmycai.com/categories/themes/">
-                        Themes
-                    </a>
-
-                    <a href="https://demo.stack.jimmycai.com/categories/syntax/">
-                        Syntax
-                    </a>
-                </header>
-
-                <div class="article-title-wrapper">
-                    <h2 class="article-title">
-                        <a
-                            href="{{ route('library.tipitaka.read', $post['uid']) }}">{{ $post->title }}</a>
-                    </h2>
-
-                    <h3 class="article-subtitle">
-                        {{ $post->summary }}
-                    </h3>
-                </div>
-
-                <footer class="article-time">
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-calendar-time"
-                            width="56"
-                            height="56"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <path
-                                d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4"></path>
-                            <circle cx="18" cy="18" r="4"></circle>
-                            <path d="M15 3v4"></path>
-                            <path d="M7 3v4"></path>
-                            <path d="M3 11h16"></path>
-                            <path d="M18 16.496v1.504l1 1"></path>
-                        </svg>
-                        <time class="article-time--published">{{ $post->formatted_updated_at }}</time>
-                    </div>
-
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-clock"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="12" cy="12" r="9"></circle>
-                            <polyline points="12 7 12 12 15 15"></polyline>
-                        </svg>
 
-                        <time class="article-time--reading"> 3 minute read </time>
-                    </div>
-                </footer>
-            </div>
-        </header>
-    </article>
-    @endforeach
-    <article class="has-image">
-        <header class="article-header">
+            @if(!empty($post->cover))
             <div class="article-image">
-                <a href="https://demo.stack.jimmycai.com/p/hello-world/">
-                    <img
-                        src="./Hugo Theme Stack Starter_files/cover_hu13459586684579990428.jpg"
-                        srcset="
-                      /p/hello-world/cover_hu13459586684579990428.jpg  800w,
-                      /p/hello-world/cover_hu3425483315149503896.jpg  1600w
-                    "
-                        width="800"
-                        height="534"
-                        loading="lazy"
-                        alt="Featured image of post Hello World" />
+                <a href="{{ route('library.tipitaka.read', ['id' => $post['uid']]) }}">
+                    <img src="{{ $post->cover }}"
+                         width="800" height="450"
+                         loading="lazy"
+                         alt="{{ $post->title }}" />
                 </a>
             </div>
+            @endif
 
             <div class="article-details">
-                <header class="article-category">
-                    <a
-                        href="https://demo.stack.jimmycai.com/categories/example-category/"
-                        style="background-color: #2a9d8f; color: #fff">
-                        Example Category
-                    </a>
-                </header>
-
-                <div class="article-title-wrapper">
-                    <h2 class="article-title">
-                        <a href="https://demo.stack.jimmycai.com/p/hello-world/">Hello World</a>
-                    </h2>
-
-                    <h3 class="article-subtitle">Welcome to Hugo Theme Stack</h3>
-                </div>
 
-                <footer class="article-time">
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-calendar-time"
-                            width="56"
-                            height="56"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <path
-                                d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4"></path>
-                            <circle cx="18" cy="18" r="4"></circle>
-                            <path d="M15 3v4"></path>
-                            <path d="M7 3v4"></path>
-                            <path d="M3 11h16"></path>
-                            <path d="M18 16.496v1.504l1 1"></path>
-                        </svg>
-                        <time class="article-time--published">Mar 06, 2022</time>
-                    </div>
-
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-clock"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="12" cy="12" r="9"></circle>
-                            <polyline points="12 7 12 12 15 15"></polyline>
-                        </svg>
-
-                        <time class="article-time--reading"> 1 minute read </time>
-                    </div>
-                </footer>
-            </div>
-        </header>
-    </article>
-
-    <article class="">
-        <header class="article-header">
-            <div class="article-details">
+                @if(!empty($post->categories))
                 <header class="article-category">
-                    <a href="https://demo.stack.jimmycai.com/categories/themes/">
-                        Themes
-                    </a>
-
-                    <a href="https://demo.stack.jimmycai.com/categories/syntax/">
-                        Syntax
+                    @foreach($post->categories as $category)
+                    <a href="{{ route('blog.category', ['user' => $user['userName'], 'category1' => $category['id']]) }}">
+                        {{ $category['label'] }}
                     </a>
+                    @endforeach
                 </header>
+                @endif
 
                 <div class="article-title-wrapper">
                     <h2 class="article-title">
-                        <a
-                            href="https://demo.stack.jimmycai.com/p/markdown-syntax-guide/">Markdown Syntax Guide</a>
-                    </h2>
-
-                    <h3 class="article-subtitle">
-                        Sample article showcasing basic Markdown syntax and
-                        formatting for HTML elements.
-                    </h3>
-                </div>
-
-                <footer class="article-time">
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-calendar-time"
-                            width="56"
-                            height="56"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <path
-                                d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4"></path>
-                            <circle cx="18" cy="18" r="4"></circle>
-                            <path d="M15 3v4"></path>
-                            <path d="M7 3v4"></path>
-                            <path d="M3 11h16"></path>
-                            <path d="M18 16.496v1.504l1 1"></path>
-                        </svg>
-                        <time class="article-time--published">Sep 07, 2023</time>
-                    </div>
-
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-clock"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="12" cy="12" r="9"></circle>
-                            <polyline points="12 7 12 12 15 15"></polyline>
-                        </svg>
-
-                        <time class="article-time--reading"> 3 minute read </time>
-                    </div>
-                </footer>
-            </div>
-        </header>
-    </article>
-
-    <article class="has-image">
-        <header class="article-header">
-            <div class="article-image">
-                <a href="https://demo.stack.jimmycai.com/p/image-gallery/">
-                    <img
-                        src="./Hugo Theme Stack Starter_files/2_hu3578945376017100738.jpg"
-                        srcset="
-                      /p/image-gallery/2_hu3578945376017100738.jpg  800w,
-                      /p/image-gallery/2_hu15750790370579438.jpg   1600w
-                    "
-                        width="800"
-                        height="1200"
-                        loading="lazy"
-                        alt="Featured image of post Image gallery" />
-                </a>
-            </div>
-
-            <div class="article-details">
-                <div class="article-title-wrapper">
-                    <h2 class="article-title">
-                        <a href="https://demo.stack.jimmycai.com/p/image-gallery/">Image gallery</a>
-                    </h2>
-
-                    <h3 class="article-subtitle">
-                        Create beautiful interactive image gallery using Markdown
-                    </h3>
-                </div>
-
-                <footer class="article-time">
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-calendar-time"
-                            width="56"
-                            height="56"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <path
-                                d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4"></path>
-                            <circle cx="18" cy="18" r="4"></circle>
-                            <path d="M15 3v4"></path>
-                            <path d="M7 3v4"></path>
-                            <path d="M3 11h16"></path>
-                            <path d="M18 16.496v1.504l1 1"></path>
-                        </svg>
-                        <time class="article-time--published">Aug 26, 2023</time>
-                    </div>
-
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-clock"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="12" cy="12" r="9"></circle>
-                            <polyline points="12 7 12 12 15 15"></polyline>
-                        </svg>
-
-                        <time class="article-time--reading"> 1 minute read </time>
-                    </div>
-                </footer>
-            </div>
-        </header>
-    </article>
-
-    <article class="has-image">
-        <header class="article-header">
-            <div class="article-image">
-                <a href="https://demo.stack.jimmycai.com/p/shortcodes/">
-                    <img
-                        src="./Hugo Theme Stack Starter_files/cover_hu5876910065799140332.jpg"
-                        srcset="
-                      /p/shortcodes/cover_hu5876910065799140332.jpg   800w,
-                      /p/shortcodes/cover_hu14584859319700861491.jpg 1600w
-                    "
-                        width="800"
-                        height="533"
-                        loading="lazy"
-                        alt="Featured image of post Shortcodes" />
-                </a>
-            </div>
-
-            <div class="article-details">
-                <div class="article-title-wrapper">
-                    <h2 class="article-title">
-                        <a href="https://demo.stack.jimmycai.com/p/shortcodes/">Shortcodes</a>
+                        <a href="{{ route('library.tipitaka.read', ['id' => $post['uid']]) }}">
+                            {{ $post->title }}
+                        </a>
                     </h2>
-
-                    <h3 class="article-subtitle">
-                        Useful shortcodes that can be used in Markdown
-                    </h3>
+                    @if(!empty($post->summary))
+                    <h3 class="article-subtitle">{{ $post->summary }}</h3>
+                    @endif
                 </div>
 
                 <footer class="article-time">
                     <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-calendar-time"
-                            width="56"
-                            height="56"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
+                        <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-calendar-time"
+                             width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                             stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                             <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <path
-                                d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4"></path>
+                            <path d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4"></path>
                             <circle cx="18" cy="18" r="4"></circle>
                             <path d="M15 3v4"></path>
                             <path d="M7 3v4"></path>
                             <path d="M3 11h16"></path>
                             <path d="M18 16.496v1.504l1 1"></path>
                         </svg>
-                        <time class="article-time--published">Aug 25, 2023</time>
-                    </div>
-
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-clock"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="12" cy="12" r="9"></circle>
-                            <polyline points="12 7 12 12 15 15"></polyline>
-                        </svg>
-
-                        <time class="article-time--reading"> 1 minute read </time>
+                        <time>{{ $post->formatted_updated_at }}</time>
                     </div>
                 </footer>
-            </div>
-        </header>
-    </article>
-
-    <article class="">
-        <header class="article-header">
-            <div class="article-details">
-                <div class="article-title-wrapper">
-                    <h2 class="article-title">
-                        <a
-                            href="https://demo.stack.jimmycai.com/p/math-typesetting/">Math Typesetting</a>
-                    </h2>
-
-                    <h3 class="article-subtitle">Math typesetting using KaTeX</h3>
-                </div>
 
-                <footer class="article-time">
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-calendar-time"
-                            width="56"
-                            height="56"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <path
-                                d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4"></path>
-                            <circle cx="18" cy="18" r="4"></circle>
-                            <path d="M15 3v4"></path>
-                            <path d="M7 3v4"></path>
-                            <path d="M3 11h16"></path>
-                            <path d="M18 16.496v1.504l1 1"></path>
-                        </svg>
-                        <time class="article-time--published">Aug 24, 2023</time>
-                    </div>
-
-                    <div>
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-clock"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="12" cy="12" r="9"></circle>
-                            <polyline points="12 7 12 12 15 15"></polyline>
-                        </svg>
-
-                        <time class="article-time--reading"> 1 minute read </time>
-                    </div>
-                </footer>
             </div>
         </header>
     </article>
+    @empty
+    <div class="not-found-card">
+        <p>暂无文章</p>
+    </div>
+    @endforelse
 </section>
 @endsection

+ 296 - 468
api-v12/resources/views/blog/layouts/app.blade.php

@@ -1,490 +1,318 @@
-<!DOCTYPE html>
-<!-- saved from url=(0032)https://demo.stack.jimmycai.com/ -->
-<html lang="en-us" dir="ltr" data-scheme="light">
+{{-- resources/views/layouts/blog.blade.php
+     Blog 栏目布局。继承 layouts/base。
+     使用 Stack 主题 CSS(静态文件,不走 Vite)。
+     三栏结构:左边栏(博主信息+导航)/ 正文 / 右边栏(搜索+分类+标签)。
+     右边栏数据($categories, $tags, $archives)由 View Composer 自动注入。
+--}}
+@extends('layouts.base')
 
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-    <meta name="generator" content="Hugo 0.134.1" />
-    <meta name="viewport" content="width=device-width, initial-scale=1" />
-    <meta
-        name="description"
-        content="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
-    <title></title>
-    <title>@yield('title', 'home')</title>
-    <link rel="canonical" href="https://demo.stack.jimmycai.com/" />
+@push('styles')
+    <link rel="stylesheet"
+          href="{{ URL::asset('assets/css/blog/style.min.css') }}">
+    <link href="{{ URL::asset('assets/css/blog/css2') }}"
+          type="text/css" rel="stylesheet">
+@endpush
 
-    <link
-        rel="stylesheet"
-        href="{{ URL::asset('assets/css/blog/style.min.663803bebe609202d5b39d848f2d7c2dc8b598a2d879efa079fa88893d29c49c.css') }}" />
-    <meta property=" og:title" content="{{ $user["nickName"] }}" />
-    <meta
-        property="og:description"
-        content="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
-    <meta property="og:url" content="https://demo.stack.jimmycai.com/" />
-    <meta property="og:site_name" content="{{ $user["nickName"] }}" />
-    <meta property="og:type" content="website" />
-    <meta property="og:updated_time" content=" 2023-09-07T00:00:00+00:00 " />
-    <meta name="twitter:title" content="{{ $user["nickName"] }}" />
-    <meta
-        name="twitter:description"
-        content="Lorem ipsum dolor sit amet, consectetur adipiscing elit." />
-    <link
-        rel="alternate"
-        type="application/rss+xml"
-        href="https://demo.stack.jimmycai.com/index.xml" />
-    <link
-        rel="shortcut icon"
-        href="https://demo.stack.jimmycai.com/favicon.png" />
-    <link
-        href="{{ URL::asset('assets/css/blog/css2') }}"
-        type="text/css"
-        rel="stylesheet" />
-</head>
+@section('body-class', '')
 
-<body
-    class=""
-    style="transition: background-color 0.3s">
-    <script>
-        (function() {
-            const colorSchemeKey = "StackColorScheme";
-            if (!localStorage.getItem(colorSchemeKey)) {
-                localStorage.setItem(colorSchemeKey, "auto");
-            }
-        })();
-    </script>
-    <script>
-        (function() {
-            const colorSchemeKey = "StackColorScheme";
-            const colorSchemeItem = localStorage.getItem(colorSchemeKey);
-            const supportDarkMode =
-                window.matchMedia("(prefers-color-scheme: dark)").matches === true;
+@section('page')
 
-            if (
-                colorSchemeItem == "dark" ||
-                (colorSchemeItem === "auto" && supportDarkMode)
-            ) {
-                document.documentElement.dataset.scheme = "dark";
-            } else {
-                document.documentElement.dataset.scheme = "light";
-            }
-        })();
-    </script>
-    <div class="container main-container flex on-phone--column extended">
-        <aside class="sidebar left-sidebar sticky">
-            <button
-                class="hamburger hamburger--spin"
+<div class="container main-container flex on-phone--column extended">
+
+    {{-- ── 左边栏 ── --}}
+    <aside class="sidebar left-sidebar sticky">
+
+        <button class="hamburger hamburger--spin"
                 type="button"
                 id="toggle-menu"
                 aria-label="Toggle Menu">
-                <span class="hamburger-box">
-                    <span class="hamburger-inner"></span>
-                </span>
-            </button>
-
-            <header>
-                <figure class="site-avatar">
-                    <a href="https://demo.stack.jimmycai.com/">
-                        <img
-                            src="{{ $user['avatar'] ?? '' }}"
-                            width="300"
-                            height="300"
-                            class="site-logo"
-                            loading="lazy"
-                            alt="Avatar" />
-                    </a>
-
-                    <span class="emoji" style="font-size: 11px;">LV10</span>
-                </figure>
-
-                <div class="site-meta">
-                    <h1 class="site-name">
-                        <a href="https://demo.stack.jimmycai.com/">{{ $user["nickName"] }}</a>
-                    </h1>
-                    <h2 class="site-description">
-                        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-                    </h2>
-                </div>
-            </header>
-            <ol class="menu-social">
-                <li>
-                    <a
-                        href="https://github.com/CaiJimmy/hugo-theme-stack"
-                        target="_blank"
-                        title="GitHub"
-                        rel="me">
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-brand-github"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
-                            <path
-                                d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5"></path>
-                        </svg>
-                    </a>
-                </li>
-
-                <li>
-                    <a
-                        href="https://twitter.com/"
-                        target="_blank"
-                        title="Twitter"
-                        rel="me">
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-brand-twitter"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
-                            <path
-                                d="M22 4.01c-1 .49 -1.98 .689 -3 .99c-1.121 -1.265 -2.783 -1.335 -4.38 -.737s-2.643 2.06 -2.62 3.737v1c-3.245 .083 -6.135 -1.395 -8 -4c0 0 -4.182 7.433 4 11c-1.872 1.247 -3.739 2.088 -6 2c3.308 1.803 6.913 2.423 10.034 1.517c3.58 -1.04 6.522 -3.723 7.651 -7.742a13.84 13.84 0 0 0 .497 -3.753c-.002 -.249 1.51 -2.772 1.818 -4.013z"></path>
-                        </svg>
-                    </a>
-                </li>
-            </ol>
-            <ol class="menu" id="main-menu">
-                <li class="current">
-                    <a href="https://demo.stack.jimmycai.com/">
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-home"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <polyline points="5 12 3 12 12 3 21 12 19 12"></polyline>
-                            <path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"></path>
-                            <path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"></path>
-                        </svg>
-
-                        <span>Home</span>
-                    </a>
-                </li>
-
-                <li>
-                    <a href="https://demo.stack.jimmycai.com/archives/">
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-archive"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <rect x="3" y="4" width="18" height="4" rx="2"></rect>
-                            <path d="M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-10"></path>
-                            <line x1="10" y1="12" x2="14" y2="12"></line>
-                        </svg>
-
-                        <span>Archives</span>
+            <span class="hamburger-box">
+                <span class="hamburger-inner"></span>
+            </span>
+        </button>
+
+        <header>
+            <figure class="site-avatar">
+                <a href="{{ route('blog.index', ['user' => $user['userName']]) }}">
+                    <img src="{{ $user['avatar'] ?? '' }}"
+                         width="300" height="300"
+                         class="site-logo"
+                         loading="lazy"
+                         alt="Avatar" />
+                </a>
+                @if(!empty($user['level']))
+                <span class="emoji" style="font-size: 11px;">LV{{ $user['level'] }}</span>
+                @endif
+            </figure>
+
+            <div class="site-meta">
+                <h1 class="site-name">
+                    <a href="{{ route('blog.index', ['user' => $user['userName']]) }}">
+                        {{ $user['nickName'] }}
                     </a>
-                </li>
-
-                <li>
-                    <a href="https://demo.stack.jimmycai.com/search/">
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-search"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
+                </h1>
+                @if(!empty($user['description']))
+                <h2 class="site-description">{{ $user['description'] }}</h2>
+                @endif
+            </div>
+        </header>
+
+        {{-- 社交链接 --}}
+        @if(!empty($user['social']))
+        <ol class="menu-social">
+            @foreach($user['social'] as $social)
+            <li>
+                <a href="{{ $social['url'] }}"
+                   target="_blank"
+                   title="{{ $social['name'] }}"
+                   rel="me">
+                    <i class="ti ti-brand-{{ strtolower($social['name']) }}"></i>
+                </a>
+            </li>
+            @endforeach
+        </ol>
+        @endif
+
+        {{-- 主导航 --}}
+        <ol class="menu" id="main-menu">
+            <li class="{{ request()->routeIs('blog.index') ? 'current' : '' }}">
+                <a href="{{ route('blog.index', ['user' => $user['userName']]) }}">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-home"
+                         width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                         stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+                        <path stroke="none" d="M0 0h24v24H0z"></path>
+                        <polyline points="5 12 3 12 12 3 21 12 19 12"></polyline>
+                        <path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"></path>
+                        <path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"></path>
+                    </svg>
+                    <span>Home</span>
+                </a>
+            </li>
+
+            <li>
+                <a href="#">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-user"
+                         width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                         stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+                        <path stroke="none" d="M0 0h24v24H0z"></path>
+                        <circle cx="12" cy="7" r="4"></circle>
+                        <path d="M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2"></path>
+                    </svg>
+                    <span>About</span>
+                </a>
+            </li>
+
+            <li class="{{ request()->routeIs('blog.archives*') ? 'current' : '' }}">
+                <a href="{{ route('blog.archives', ['user' => $user['userName']]) }}">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-archive"
+                         width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                         stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+                        <path stroke="none" d="M0 0h24v24H0z"></path>
+                        <rect x="3" y="4" width="18" height="4" rx="2"></rect>
+                        <path d="M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-10"></path>
+                        <line x1="10" y1="12" x2="14" y2="12"></line>
+                    </svg>
+                    <span>Archives</span>
+                </a>
+            </li>
+
+            <li class="{{ request()->routeIs('blog.search') ? 'current' : '' }}">
+                <a href="{{ route('blog.search', ['user' => $user['userName']]) }}">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-search"
+                         width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                         stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+                        <path stroke="none" d="M0 0h24v24H0z"></path>
+                        <circle cx="10" cy="10" r="7"></circle>
+                        <line x1="21" y1="21" x2="15" y2="15"></line>
+                    </svg>
+                    <span>Search</span>
+                </a>
+            </li>
+
+            {{-- 暗色模式切换 --}}
+            <li class="menu-bottom-section">
+                <ol class="menu">
+                    <li id="dark-mode-toggle">
+                        <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-left"
+                             width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                             stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                             <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="10" cy="10" r="7"></circle>
-                            <line x1="21" y1="21" x2="15" y2="15"></line>
+                            <circle cx="8" cy="12" r="2"></circle>
+                            <rect x="2" y="6" width="20" height="12" rx="6"></rect>
                         </svg>
-
-                        <span>Search</span>
-                    </a>
-                </li>
-
-                <li>
-                    <a href="https://demo.stack.jimmycai.com/links/">
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-link"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
+                        <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-right"
+                             width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                             stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                             <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <path
-                                d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5"></path>
-                            <path
-                                d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5"></path>
+                            <circle cx="16" cy="12" r="2"></circle>
+                            <rect x="2" y="6" width="20" height="12" rx="6"></rect>
                         </svg>
-
-                        <span>Links</span>
-                    </a>
-                </li>
-
-                <li class="menu-bottom-section">
-                    <ol class="menu">
-                        <li id="dark-mode-toggle">
-                            <svg
-                                xmlns="http://www.w3.org/2000/svg"
-                                class="icon icon-tabler icon-tabler-toggle-left"
-                                width="24"
-                                height="24"
-                                viewBox="0 0 24 24"
-                                stroke-width="2"
-                                stroke="currentColor"
-                                fill="none"
-                                stroke-linecap="round"
-                                stroke-linejoin="round">
-                                <path stroke="none" d="M0 0h24v24H0z"></path>
-                                <circle cx="8" cy="12" r="2"></circle>
-                                <rect x="2" y="6" width="20" height="12" rx="6"></rect>
-                            </svg>
-
-                            <svg
-                                xmlns="http://www.w3.org/2000/svg"
-                                class="icon icon-tabler icon-tabler-toggle-right"
-                                width="24"
-                                height="24"
-                                viewBox="0 0 24 24"
-                                stroke-width="2"
-                                stroke="currentColor"
-                                fill="none"
-                                stroke-linecap="round"
-                                stroke-linejoin="round">
-                                <path stroke="none" d="M0 0h24v24H0z"></path>
-                                <circle cx="16" cy="12" r="2"></circle>
-                                <rect x="2" y="6" width="20" height="12" rx="6"></rect>
-                            </svg>
-
-                            <span>Dark Mode</span>
-                        </li>
-                    </ol>
-                </li>
-            </ol>
-        </aside>
-
-        <aside class="sidebar right-sidebar sticky">
-            <form
-                action="https://demo.stack.jimmycai.com/search/"
-                class="search-form widget">
-                <p>
-                    <label>Search</label>
-                    <input name="keyword" required="" placeholder="Type something..." />
-
-                    <button title="Search">
-                        <svg
-                            xmlns="http://www.w3.org/2000/svg"
-                            class="icon icon-tabler icon-tabler-search"
-                            width="24"
-                            height="24"
-                            viewBox="0 0 24 24"
-                            stroke-width="2"
-                            stroke="currentColor"
-                            fill="none"
-                            stroke-linecap="round"
-                            stroke-linejoin="round">
-                            <path stroke="none" d="M0 0h24v24H0z"></path>
-                            <circle cx="10" cy="10" r="7"></circle>
-                            <line x1="21" y1="21" x2="15" y2="15"></line>
-                        </svg>
-                    </button>
-                </p>
-            </form>
-
-            <section class="widget archives">
-                <div class="widget-icon">
-                    <svg
-                        xmlns="http://www.w3.org/2000/svg"
-                        class="icon icon-tabler icon-tabler-infinity"
-                        width="24"
-                        height="24"
-                        viewBox="0 0 24 24"
-                        stroke-width="2"
-                        stroke="currentColor"
-                        fill="none"
-                        stroke-linecap="round"
-                        stroke-linejoin="round">
-                        <path stroke="none" d="M0 0h24v24H0z"></path>
-                        <path
-                            d="M9.828 9.172a4 4 0 1 0 0 5.656 a10 10 0 0 0 2.172 -2.828a10 10 0 0 1 2.172 -2.828 a4 4 0 1 1 0 5.656a10 10 0 0 1 -2.172 -2.828a10 10 0 0 0 -2.172 -2.828"></path>
-                    </svg>
-                </div>
-                <h2 class="widget-title section-title">Archives</h2>
-
-                <div class="widget-archive--list">
-                    <div class="archives-year">
-                        <a href="https://demo.stack.jimmycai.com/archives/#2023">
-                            <span class="year">2023</span>
-                            <span class="count">4</span>
-                        </a>
-                    </div>
-                    <div class="archives-year">
-                        <a href="https://demo.stack.jimmycai.com/archives/#2022">
-                            <span class="year">2022</span>
-                            <span class="count">1</span>
-                        </a>
-                    </div>
-                </div>
+                        <span>Dark Mode</span>
+                    </li>
+                </ol>
+            </li>
+        </ol>
+
+    </aside>
+
+    {{-- ── 正文 ── --}}
+    <main class="main full-width">
+        <div>
+            @yield('content')
+        </div>
+
+        <footer class="site-footer">
+            {{-- 博主版权 --}}
+            <section class="copyright">
+                &copy; 2020 - {{ date('Y') }} {{ $user['nickName'] }}
             </section>
 
-            <section class="widget tagCloud">
-                <div class="widget-icon">
-                    <svg
-                        xmlns="http://www.w3.org/2000/svg"
-                        class="icon icon-tabler icon-tabler-hash"
-                        width="24"
-                        height="24"
-                        viewBox="0 0 24 24"
-                        stroke-width="2"
-                        stroke="currentColor"
-                        fill="none"
-                        stroke-linecap="round"
-                        stroke-linejoin="round">
-                        <path stroke="none" d="M0 0h24v24H0z"></path>
-                        <line x1="5" y1="9" x2="19" y2="9"></line>
-                        <line x1="5" y1="15" x2="19" y2="15"></line>
-                        <line x1="11" y1="4" x2="7" y2="20"></line>
-                        <line x1="17" y1="4" x2="13" y2="20"></line>
-                    </svg>
-                </div>
-                <h2 class="widget-title section-title">Categories</h2>
-
-                <div class="tagCloud-tags">
-                    @foreach($categories as $category)
-                    <a
-                        href="{{ route('blog.category', ['user' => $user['userName'],'category1' => $category['id'],]) }}"
-                        class="font_size_1">
-                        {{ $category['label'] }}
-                    </a>
-                    @endforeach
-                </div>
+            {{-- 主题版权(原样保留设计者信息) --}}
+            <section class="powerby">
+                &copy; 2020 - 2026 Hugo Theme Stack &nbsp;
+                Theme <b><a href="https://github.com/CaiJimmy/hugo-theme-stack"
+                             target="_blank" rel="noopener" data-version="3.30.0">Stack</a></b>
+                designed by
+                <a href="https://jimmycai.com/" target="_blank" rel="noopener">Jimmy</a>
             </section>
-
-            <section class="widget tagCloud">
-                <div class="widget-icon">
-                    <svg
-                        xmlns="http://www.w3.org/2000/svg"
-                        class="icon icon-tabler icon-tabler-tag"
-                        width="24"
-                        height="24"
-                        viewBox="0 0 24 24"
-                        stroke-width="2"
-                        stroke="currentColor"
-                        fill="none"
-                        stroke-linecap="round"
-                        stroke-linejoin="round">
+        </footer>
+    </main>
+
+    {{-- ── 右边栏 ── --}}
+    <aside class="sidebar right-sidebar sticky">
+
+        {{-- 搜索 --}}
+        <form action="{{ route('blog.search', ['user' => $user['userName']]) }}"
+              method="GET"
+              class="search-form widget">
+            <p>
+                <label>Search</label>
+                <input name="q"
+                       placeholder="Type something..."
+                       value="{{ request('q') }}" />
+                <button type="submit" title="Search">
+                    <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-search"
+                         width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                         stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                         <path stroke="none" d="M0 0h24v24H0z"></path>
-                        <path
-                            d="M11 3L20 12a1.5 1.5 0 0 1 0 2L14 20a1.5 1.5 0 0 1 -2 0L3 11v-4a4 4 0 0 1 4 -4h4"></path>
-                        <circle cx="9" cy="9" r="2"></circle>
+                        <circle cx="10" cy="10" r="7"></circle>
+                        <line x1="21" y1="21" x2="15" y2="15"></line>
                     </svg>
-                </div>
-                <h2 class="widget-title section-title">Tags</h2>
-
-                <div class="tagCloud-tags">
-                    <a
-                        href="https://demo.stack.jimmycai.com/tags/css/"
-                        class="font_size_1">
-                        Css
-                    </a>
-
-                    <a
-                        href="https://demo.stack.jimmycai.com/tags/example-tag/"
-                        class="font_size_1">
-                        Example Tag
-                    </a>
-
-                    <a
-                        href="https://demo.stack.jimmycai.com/tags/html/"
-                        class="font_size_1">
-                        Html
-                    </a>
-
-                    <a
-                        href="https://demo.stack.jimmycai.com/tags/markdown/"
-                        class="font_size_1">
-                        Markdown
-                    </a>
-
-                    <a
-                        href="https://demo.stack.jimmycai.com/tags/themes/"
-                        class="font_size_1">
-                        Themes
+                </button>
+            </p>
+        </form>
+
+        {{-- Archives --}}
+        @if(!empty($archives))
+        <section class="widget archives">
+            <div class="widget-icon">
+                <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-infinity"
+                     width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                     stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+                    <path stroke="none" d="M0 0h24v24H0z"></path>
+                    <path d="M9.828 9.172a4 4 0 1 0 0 5.656 a10 10 0 0 0 2.172 -2.828a10 10 0 0 1 2.172 -2.828 a4 4 0 1 1 0 5.656a10 10 0 0 1 -2.172 -2.828a10 10 0 0 0 -2.172 -2.828"></path>
+                </svg>
+            </div>
+            <h2 class="widget-title section-title">Archives</h2>
+            <div class="widget-archive--list">
+                @foreach($archives as $archive)
+                <div class="archives-year">
+                    <a href="{{ route('blog.archives.year', ['user' => $user['userName'], 'year' => $archive['year']]) }}">
+                        <span class="year">{{ $archive['year'] }}</span>
+                        <span class="count">{{ $archive['count'] }}</span>
                     </a>
                 </div>
-            </section>
-        </aside>
-
-        <main class="main full-width">
-            <div>
-                @yield('content')
+                @endforeach
             </div>
-
-            <footer class="site-footer">
-                <section class="copyright">
-                    2020 - 2025 wikipali
-                </section>
-
-                <section class="powerby">
-                    Theme
-                    <b><a
-                            href="https://github.com/CaiJimmy/hugo-theme-stack"
-                            target="_blank"
-                            rel="noopener"
-                            data-version="3.30.0">Stack</a></b>
-                    designed by
-                    <a href="https://jimmycai.com/" target="_blank" rel="noopener">Jimmy</a>
-                    © 2020 - 2025 Hugo Theme Stack Starter
-                </section>
-            </footer>
-        </main>
-    </div>
-    <script
-        src="{{ URL::asset('assets/js/blog/vibrant.min.js') }}"
+        </section>
+        @endif
+
+        {{-- Categories --}}
+        @if(!empty($categories))
+        <section class="widget tagCloud">
+            <div class="widget-icon">
+                <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-hash"
+                     width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                     stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+                    <path stroke="none" d="M0 0h24v24H0z"></path>
+                    <line x1="5" y1="9" x2="19" y2="9"></line>
+                    <line x1="5" y1="15" x2="19" y2="15"></line>
+                    <line x1="11" y1="4" x2="7" y2="20"></line>
+                    <line x1="17" y1="4" x2="13" y2="20"></line>
+                </svg>
+            </div>
+            <h2 class="widget-title section-title">Categories</h2>
+            <div class="tagCloud-tags">
+                @foreach($categories as $category)
+                <a href="{{ route('blog.category', ['user' => $user['userName'], 'category1' => $category['id']]) }}">
+                    {{ $category['label'] }}
+                </a>
+                @endforeach
+            </div>
+        </section>
+        @endif
+
+        {{-- Tags --}}
+        @if(!empty($tags))
+        <section class="widget tagCloud">
+            <div class="widget-icon">
+                <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-tag"
+                     width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
+                     stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
+                    <path stroke="none" d="M0 0h24v24H0z"></path>
+                    <path d="M11 3L20 12a1.5 1.5 0 0 1 0 2L14 20a1.5 1.5 0 0 1 -2 0L3 11v-4a4 4 0 0 1 4 -4h4"></path>
+                    <circle cx="9" cy="9" r="2"></circle>
+                </svg>
+            </div>
+            <h2 class="widget-title section-title">Tags</h2>
+            <div class="tagCloud-tags">
+                @foreach($tags as $tag)
+                <a href="{{ route('blog.tag', ['user' => $user['userName'], 'tag' => $tag['name']]) }}"
+                   class="font_size_1">
+                    {{ $tag['name'] }}
+                </a>
+                @endforeach
+            </div>
+        </section>
+        @endif
+
+    </aside>
+
+</div>
+
+@push('scripts')
+<script>
+    (function() {
+        const colorSchemeKey = "StackColorScheme";
+        if (!localStorage.getItem(colorSchemeKey)) {
+            localStorage.setItem(colorSchemeKey, "auto");
+        }
+    })();
+    (function() {
+        const colorSchemeKey = "StackColorScheme";
+        const colorSchemeItem = localStorage.getItem(colorSchemeKey);
+        const supportDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches === true;
+        if (colorSchemeItem == "dark" || (colorSchemeItem === "auto" && supportDarkMode)) {
+            document.documentElement.dataset.scheme = "dark";
+        } else {
+            document.documentElement.dataset.scheme = "light";
+        }
+    })();
+</script>
+<script src="{{ URL::asset('assets/js/blog/vibrant.min.js') }}"
         integrity="sha256-awcR2jno4kI5X0zL8ex0vi2z+KMkF24hUW8WePSA9HM="
         crossorigin="anonymous"></script>
-    <script
-        type="text/javascript"
-        src="{{ URL::asset('assets/js/blog/main.1e9a3bafd846ced4c345d084b355fb8c7bae75701c338f8a1f8a82c780137826.js') }}"
-        defer=""></script>
-    <script>
-        (function() {
-            const customFont = document.createElement("link");
-            customFont.href =
-                "https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap";
-
-            customFont.type = "text/css";
-            customFont.rel = "stylesheet";
-
-            document.head.appendChild(customFont);
-        })();
-    </script>
-</body>
-
-</html>
+<script src="{{ URL::asset('assets/js/blog/main.1e9a3bafd846ced4c345d084b355fb8c7bae75701c338f8a1f8a82c780137826.js') }}"
+        type="text/javascript" defer></script>
+<script>
+    (function() {
+        const customFont = document.createElement("link");
+        customFont.href = "https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap";
+        customFont.type = "text/css";
+        customFont.rel = "stylesheet";
+        document.head.appendChild(customFont);
+    })();
+</script>
+@endpush
+
+@endsection

+ 0 - 28
api-v12/resources/views/components/book-item.blade.php

@@ -1,28 +0,0 @@
-{{-- resources/views/components/book-item.blade.php --}}
-<div class="book-item">
-    <div class="card h-100">
-        <div class="card-body">
-            <div class="book-cover-container">
-                <a href="{{ route('library.tipitaka.show', $book['id']) }}" class="text-decoration-none">
-                    <img src="{{ $book['cover'] ?? 'https://via.placeholder.com/300x400?text=No+Cover' }}"
-                        alt="{{ $book['title'] ?? '未知书籍' }}"
-                        class="book-cover"
-                        loading="lazy">
-                </a>
-            </div>
-            <div class="book-info">
-                <div class="book-title">{{ $book['title'] ?? '未知书籍' }}</div>
-                <div class="book-author">{{ $book['author'] ?? '未知作者' }}</div>
-                <div class="book-author">
-                    <a href="{{ route('blog.index', ['user' => $book['publisher']->username]) }}">
-                        {{ $book['publisher']->nickname }}
-                    </a>
-                </div>
-                <div class="book-language">
-                    <span class="language-badge">{{ $book['language'] ?? '未知语言' }}</span>
-                    <span class="language-badge">{{ $book['type'] ?? '未知类型' }}</span>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>

+ 0 - 118
api-v12/resources/views/components/book-list.blade.php

@@ -1,118 +0,0 @@
-{{-- resources/views/components/book-list.blade.php --}}
-@once
-@push('styles')
-<link href="https://cdnjs.cloudflare.com/ajax/libs/tabler/1.0.0-beta19/css/tabler.min.css" rel="stylesheet">
-<style>
-    .book-list-container {
-        max-width: 1024px;
-        margin: 0 auto;
-        padding: 20px;
-    }
-
-    .book-item {
-        margin-bottom: 24px;
-        transition: all 0.3s ease;
-    }
-
-    .book-item:hover {
-        transform: translateY(-2px);
-        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
-    }
-
-    .book-cover {
-        width: 100%;
-        aspect-ratio: 3/4;
-        object-fit: contain !important;
-        border-radius: 6px;
-        background-color: #f8f9fa;
-    }
-
-    .book-info {
-        padding: 16px 0;
-    }
-
-    .book-title {
-        font-size: 1.125rem;
-        font-weight: 600;
-        color: #1f2937;
-        margin-bottom: 8px;
-        line-height: 1.4;
-    }
-
-    .book-author {
-        color: #6b7280;
-        font-size: 0.95rem;
-        margin-bottom: 6px;
-    }
-
-    .book-language {
-        color: #9ca3af;
-        font-size: 0.875rem;
-    }
-
-    .language-badge {
-        display: inline-block;
-        padding: 2px 8px;
-        background-color: #e5e7eb;
-        color: #374151;
-        border-radius: 12px;
-        font-size: 0.75rem;
-        font-weight: 500;
-    }
-
-    /* 桌面端布局 */
-    @media (min-width: 576px) {
-        .book-grid {
-            display: grid;
-            grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
-            gap: 24px;
-        }
-    }
-
-    /* 手机端布局 */
-    @media (max-width: 575px) {
-        .book-item .card-body {
-            display: flex;
-            gap: 16px;
-            align-items: stretch;
-        }
-
-        .book-cover-container {
-            flex: 0 0 120px;
-        }
-
-        .book-cover {
-            height: 160px;
-            width: 120px;
-        }
-
-        .book-info {
-            flex: 1;
-            padding: 0;
-            display: flex;
-            flex-direction: column;
-            justify-content: space-between;
-        }
-
-        .book-title {
-            font-size: 1rem;
-            margin-bottom: 12px;
-        }
-    }
-</style>
-@endpush
-@endonce
-
-<div>
-    @if(!empty($books) && count($books) > 0)
-    <div class="book-grid">
-        @foreach($books as $book)
-        @include('components.book-item', ['book' => $book])
-        @endforeach
-    </div>
-    @else
-    <div class="text-center py-5">
-        <p class="text-muted">暂无图书数据</p>
-    </div>
-    @endif
-</div>

+ 40 - 0
api-v12/resources/views/components/ui/author-avatar.blade.php

@@ -0,0 +1,40 @@
+{{-- resources/views/components/ui/author-avatar.blade.php
+     作者头像组件。支持图片头像和文字缩写头像两种形式。
+     Props:
+       $avatar   — 头像图片 URL(为空时显示文字头像)
+       $color    — 文字头像背景色
+       $initials — 文字头像缩写
+       $name     — 作者名字(显示在头像右侧)
+       $size     — sm | md | lg
+       $sub      — 副文字(如文章数量),可选
+--}}
+@props([
+    'avatar'   => null,
+    'color'    => '#888888',
+    'initials' => '?',
+    'name'     => '',
+    'size'     => 'md',
+    'sub'      => null,
+])
+
+<div class="author-avatar author-avatar--{{ $size }}">
+    @if($avatar)
+        <img src="{{ $avatar }}"
+             alt="{{ $name }}"
+             class="author-avatar__img">
+    @else
+        <div class="author-avatar__initials"
+             style="background: {{ $color }}">
+            {{ $initials }}
+        </div>
+    @endif
+
+    @if($name)
+    <div class="author-avatar__info">
+        <span class="author-avatar__name">{{ $name }}</span>
+        @if($sub)
+        <span class="author-avatar__sub">{{ $sub }}</span>
+        @endif
+    </div>
+    @endif
+</div>

+ 46 - 0
api-v12/resources/views/components/ui/book-cover.blade.php

@@ -0,0 +1,46 @@
+{{-- resources/views/components/ui/book-cover.blade.php
+     书籍封面组件。支持图片封面和渐变+文字封面两种形式。
+     Props:
+       $image    — 封面图片 URL(为空时显示渐变+文字)
+       $gradient — 渐变 CSS 字符串,image 为空时使用
+       $title    — 书名(image 为空时显示在封面上)
+       $subtitle — 副标题(可选)
+       $size     — sm | md | lg(控制尺寸)
+       $style3d  — true | false(是否显示 3D 书脊效果,默认 true)
+       $alt      — img alt 文字(默认使用 $title)
+--}}
+@props([
+    'image'    => null,
+    'gradient' => 'linear-gradient(135deg, #2d2010 0%, #1a1208 100%)',
+    'title'    => '',
+    'subtitle' => '',
+    'size'     => 'md',
+    'style3d'  => true,
+    'alt'      => null,
+])
+
+@php
+$sizes = [
+    'sm' => 'book-cover--sm',
+    'md' => 'book-cover--md',
+    'lg' => 'book-cover--lg',
+];
+$sizeClass = $sizes[$size] ?? $sizes['md'];
+@endphp
+
+<div class="book-cover {{ $sizeClass }} {{ $style3d ? 'book-cover--3d' : '' }}"
+     style="{{ $image ? '' : 'background: ' . $gradient }}">
+
+    @if($image)
+        <img src="{{ $image }}" alt="{{ $alt ?? $title }}" class="book-cover__img">
+    @else
+        <div class="book-cover__text">
+            <div class="book-cover__title">{{ $title }}</div>
+            @if($subtitle)
+                <div class="book-cover__divider"></div>
+                <div class="book-cover__subtitle">{{ $subtitle }}</div>
+            @endif
+        </div>
+    @endif
+
+</div>

+ 21 - 0
api-v12/resources/views/components/ui/book-grid.blade.php

@@ -0,0 +1,21 @@
+{{-- resources/views/components/ui/book-grid.blade.php
+     书籍网格组件。替代原 components/book-list.blade.php。
+     去除内联 <style> 和 CDN 引入,样式由 modules/_tipitaka.css 提供。
+     Props:
+       $books      — 书籍数组
+       $emptyText  — 空状态文字(默认"暂无图书")
+--}}
+@props([
+    'books'     => [],
+    'emptyText' => '暂无图书',
+])
+
+@if(!empty($books) && count($books) > 0)
+<div class="book-grid">
+    @foreach($books as $book)
+        <x-ui.card-book :book="$book" />
+    @endforeach
+</div>
+@else
+<x-ui.empty-state :title="$emptyText" />
+@endif

+ 69 - 0
api-v12/resources/views/components/ui/card-anthology.blade.php

@@ -0,0 +1,69 @@
+{{-- resources/views/components/ui/card-anthology.blade.php
+     文集卡片组件。横向布局:封面左 + 内容右。
+     用于 anthology/index 列表。
+     Props:
+       $item — 文集数据数组,包含以下字段:
+               id, title, subtitle, description, cover_image, cover_gradient,
+               author{name, avatar, color, initials},
+               chapters[], children_number, updated_at
+       $href — 卡片链接
+--}}
+@props([
+    'item',
+    'href',
+])
+
+<a href="{{ $href }}" class="anthology-card">
+
+    {{-- 封面 --}}
+    <x-ui.book-cover
+        :image="$item['cover_image'] ?? null"
+        :gradient="$item['cover_gradient'] ?? ''"
+        :title="$item['title']"
+        :subtitle="$item['subtitle'] ?? ''"
+        size="md"
+        :style3d="false"
+    />
+
+    {{-- 内容 --}}
+    <div class="anthology-card__body">
+        <div class="anthology-card__title">{{ $item['title'] }}</div>
+
+        @if(!empty($item['description']))
+        <div class="anthology-card__desc">{{ $item['description'] }}</div>
+        @endif
+
+        <div class="anthology-card__author">
+            <x-ui.author-avatar
+                :avatar="$item['author']['avatar'] ?? null"
+                :color="$item['author']['color'] ?? '#888'"
+                :initials="$item['author']['initials'] ?? '?'"
+                :name="$item['author']['name']"
+                size="sm"
+            />
+        </div>
+
+        @if(!empty($item['chapters']))
+        <div class="anthology-card__tags">
+            @foreach(array_slice($item['chapters'], 0, 4) as $ch)
+            <span class="anthology-tag">{{ mb_strimwidth($ch, 0, 14, '…') }}</span>
+            @endforeach
+            @if($item['children_number'] > 4)
+            <span class="anthology-tag anthology-tag--more">+{{ $item['children_number'] - 4 }} 章</span>
+            @endif
+        </div>
+        @endif
+
+        <div class="anthology-card__meta">
+            <span class="anthology-meta-item">
+                <i class="ti ti-calendar"></i>
+                {{ $item['updated_at'] }}
+            </span>
+            <span class="anthology-meta-item">
+                <i class="ti ti-file-text"></i>
+                {{ $item['children_number'] }} 章节
+            </span>
+        </div>
+    </div>
+
+</a>

+ 52 - 0
api-v12/resources/views/components/ui/card-book.blade.php

@@ -0,0 +1,52 @@
+{{-- resources/views/components/ui/card-book.blade.php
+     书籍卡片组件。纵向布局:封面上 + 信息下。
+     用于 tipitaka 列表页。
+     Props:
+       $book — 书籍数据数组,包含:
+               id, title, author, cover(可选),
+               cover_gradient(可选), publisher(object, 可选),
+               language(可选), type(可选)
+--}}
+@props(['book'])
+
+<div class="card-book">
+    <a href="{{ route('library.tipitaka.show', $book['id']) }}" class="card-book__link">
+
+        {{-- 封面 --}}
+        <x-ui.book-cover
+            :image="$book['cover'] ?? null"
+            :gradient="$book['cover_gradient'] ?? 'linear-gradient(135deg, #2d2010 0%, #1a1208 100%)'"
+            :title="$book['title'] ?? ''"
+            :subtitle="'义注'"
+            size="md"
+            :style3d="false" />
+
+        {{-- 书籍信息 --}}
+        <div class="card-book__info">
+            <div class="card-book__title">{{ $book['title'] ?? '未知书籍' }}</div>
+
+            @if(!empty($book['author']))
+            <div class="card-book__author">{{ $book['author'] }}</div>
+            @endif
+
+            @if(isset($book['publisher']))
+            <div class="card-book__publisher">
+                <a href="{{ route('blog.index', ['user' => $book['publisher']->username]) }}"
+                    class="card-book__publisher-link">
+                    {{ $book['publisher']->nickname }}
+                </a>
+            </div>
+            @endif
+
+            <div class="card-book__badges">
+                @if(!empty($book['language']))
+                <span class="card-book__badge">{{ $book['language'] }}</span>
+                @endif
+                @if(!empty($book['type']))
+                <span class="card-book__badge">{{ $book['type'] }}</span>
+                @endif
+            </div>
+        </div>
+
+    </a>
+</div>

+ 13 - 0
api-v12/resources/views/layouts/reader.blade.php

@@ -0,0 +1,13 @@
+{{-- resources/views/layouts/reader.blade.php
+     全站阅读页布局。沉浸式,无 library navbar,无 footer。
+     用于:tipitaka/read、anthology/read、wiki/show、blog/show。
+--}}
+@extends('layouts.base')
+
+@push('styles')
+    @vite(['resources/css/library.css', 'resources/css/reader.css'])
+@endpush
+
+@section('page')
+    @yield('reader-content')
+@endsection

+ 48 - 487
api-v12/resources/views/library/anthology/index.blade.php

@@ -1,528 +1,89 @@
-{{-- api-v12/resources/views/library/anthology/index.blade.php --}}
-@extends('library.layouts.app')
+{{-- resources/views/library/anthology/index.blade.php --}}
+@extends('layouts.library')
 
 @section('title', '文集 · 巴利书库')
 
 @push('styles')
-<link rel="preconnect" href="https://fonts.googleapis.com">
-<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
-<style>
-    :root {
-        --sf: #c8860a;
-        --sf-light: #f5e6c8;
-        --sf-pale: #fdf8f0;
-        --ink: #1a1208;
-        --ink-soft: #4a3f2f;
-        --ink-muted: #8a7a68;
-        --bdr: #e8ddd0;
-        --card-bg: #fffdf9;
-    }
-
-    body {
-        background: var(--sf-pale) !important;
-        font-family: 'Noto Sans SC', sans-serif;
-    }
-
-    /* Page header */
-    .anthology-page-header {
-        background: linear-gradient(135deg, var(--ink) 0%, #2d2010 100%);
-        padding: 2.25rem 0 2rem;
-        position: relative;
-        overflow: hidden;
-        margin-bottom: 0;
-    }
-
-    .anthology-page-header::before {
-        content: '藏';
-        font-family: 'Noto Serif SC', serif;
-        font-size: 16rem;
-        font-weight: 700;
-        color: rgba(255, 255, 255, 0.03);
-        position: absolute;
-        right: -1rem;
-        top: -2.5rem;
-        line-height: 1;
-        pointer-events: none;
-    }
-
-    .anthology-page-header h1 {
-        font-family: 'Noto Serif SC', serif;
-        font-size: 1.75rem;
-        font-weight: 600;
-        color: #fff;
-        margin: 0 0 0.3rem;
-        letter-spacing: 0.08em;
-    }
-
-    .anthology-page-header p {
-        color: rgba(255, 255, 255, 0.45);
-        font-size: 0.85rem;
-        margin: 0;
-    }
-
-    .result-badge {
-        background: var(--sf);
-        color: var(--ink);
-        font-size: 0.75rem;
-        font-weight: 700;
-        padding: 2px 9px;
-        border-radius: 20px;
-        margin-left: 0.6rem;
-        vertical-align: middle;
-    }
-
-    /* Card */
-    .anthology-card {
-        background: var(--card-bg);
-        border: 1px solid var(--bdr);
-        border-radius: 10px;
-        overflow: hidden;
-        display: flex;
-        transition: box-shadow .25s, transform .25s;
-        margin-bottom: 1.1rem;
-        text-decoration: none;
-        color: inherit;
-    }
-
-    .anthology-card:hover {
-        box-shadow: 0 8px 28px rgba(200, 134, 10, .12), 0 2px 8px rgba(0, 0, 0, .06);
-        transform: translateY(-2px);
-        color: inherit;
-        text-decoration: none;
-    }
-
-    .card-cover {
-        width: 130px;
-        min-width: 130px;
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        padding: 1.1rem 0.7rem;
-        position: relative;
-        overflow: hidden;
-    }
-
-    .card-cover img {
-        width: 100%;
-        height: 100%;
-        object-fit: cover;
-        position: absolute;
-        inset: 0;
-    }
-
-    .card-cover::after {
-        content: '';
-        position: absolute;
-        inset: 0;
-        background: repeating-linear-gradient(45deg, transparent, transparent 8px, rgba(255, 255, 255, .015) 8px, rgba(255, 255, 255, .015) 9px);
-    }
-
-    .cover-text-wrap {
-        position: relative;
-        z-index: 1;
-        text-align: center;
-    }
-
-    .cover-title {
-        font-family: 'Noto Serif SC', serif;
-        font-size: 1rem;
-        font-weight: 600;
-        color: #fff;
-        line-height: 1.6;
-        letter-spacing: .12em;
-        word-break: break-all;
-    }
-
-    .cover-divider {
-        width: 28px;
-        height: 1px;
-        background: var(--sf);
-        margin: .5rem auto;
-    }
-
-    .cover-sub {
-        font-size: .65rem;
-        color: rgba(255, 255, 255, .45);
-        letter-spacing: .04em;
-    }
-
-    .card-body-inner {
-        padding: 1.1rem 1.4rem;
-        flex: 1;
-        display: flex;
-        flex-direction: column;
-    }
-
-    .card-main-title {
-        font-family: 'Noto Serif SC', serif;
-        font-size: 1.1rem;
-        font-weight: 600;
-        color: var(--ink);
-        margin-bottom: .35rem;
-        line-height: 1.4;
-    }
-
-    .anthology-card:hover .card-main-title {
-        color: var(--sf);
-    }
-
-    .card-desc {
-        font-size: .8rem;
-        color: var(--ink-muted);
-        margin-bottom: .65rem;
-        line-height: 1.65;
-    }
-
-    .card-author {
-        display: flex;
-        align-items: center;
-        gap: .45rem;
-        margin-bottom: .7rem;
-    }
-
-    .a-avatar {
-        width: 24px;
-        height: 24px;
-        border-radius: 50%;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        font-size: .65rem;
-        font-weight: 700;
-        color: #fff;
-        flex-shrink: 0;
-    }
-
-    .a-avatar-img {
-        width: 24px;
-        height: 24px;
-        border-radius: 50%;
-        object-fit: cover;
-        flex-shrink: 0;
-    }
-
-    .a-name {
-        font-size: .8rem;
-        color: var(--ink-soft);
-        font-weight: 500;
-    }
-
-    .chapter-tags {
-        display: flex;
-        flex-wrap: wrap;
-        gap: .3rem;
-        margin-top: auto;
-    }
-
-    .c-tag {
-        font-size: .7rem;
-        color: var(--ink-muted);
-        background: var(--sf-light);
-        border: 1px solid var(--bdr);
-        padding: 1px 7px;
-        border-radius: 4px;
-        white-space: nowrap;
-    }
-
-    .c-tag.more {
-        background: transparent;
-        border-color: transparent;
-        color: var(--sf);
-    }
-
-    .card-meta-row {
-        display: flex;
-        align-items: center;
-        gap: .85rem;
-        margin-top: .65rem;
-        padding-top: .65rem;
-        border-top: 1px solid var(--bdr);
-    }
-
-    .meta-it {
-        font-size: .72rem;
-        color: var(--ink-muted);
-        display: flex;
-        align-items: center;
-        gap: .25rem;
-    }
-
-    /* Pagination — force horizontal */
-    .anthology-pagination .pagination {
-        display: flex !important;
-        flex-direction: row !important;
-        flex-wrap: wrap;
-        gap: 3px;
-        justify-content: center;
-        list-style: none;
-        padding: 0;
-        margin: 0;
-    }
-
-    .anthology-pagination .page-item {
-        display: inline-block;
-    }
-
-    .anthology-pagination .page-item .page-link {
-        border: 1px solid var(--bdr);
-        color: var(--ink-soft);
-        font-size: .82rem;
-        padding: 5px 11px;
-        background: var(--card-bg);
-        border-radius: 5px;
-        display: inline-block;
-        line-height: 1.5;
-        text-decoration: none;
-    }
-
-    .anthology-pagination .page-item.active .page-link {
-        background: var(--sf);
-        border-color: var(--sf);
-        color: #fff;
-    }
-
-    .anthology-pagination .page-item .page-link:hover {
-        background: var(--sf-light);
-        color: var(--sf);
-    }
-
-    .anthology-pagination .page-item.disabled .page-link {
-        opacity: .45;
-        pointer-events: none;
-    }
-
-    /* Sidebar */
-    .sidebar-box {
-        background: var(--card-bg);
-        border: 1px solid var(--bdr);
-        border-radius: 10px;
-        overflow: hidden;
-        margin-bottom: 1.1rem;
-    }
-
-    .sb-header {
-        padding: .75rem 1.15rem;
-        border-bottom: 1px solid var(--bdr);
-        font-family: 'Noto Serif SC', serif;
-        font-size: .875rem;
-        font-weight: 600;
-        color: var(--ink-soft);
-        letter-spacing: .04em;
-        display: flex;
-        align-items: center;
-        gap: .45rem;
-    }
-
-    .sb-header::before {
-        content: '';
-        display: block;
-        width: 3px;
-        height: 13px;
-        background: var(--sf);
-        border-radius: 2px;
-    }
-
-    .author-ul {
-        list-style: none;
-        padding: .35rem 0;
-        margin: 0;
-    }
-
-    .author-ul li a {
-        display: flex;
-        align-items: center;
-        gap: .6rem;
-        padding: .45rem 1.15rem;
-        text-decoration: none;
-        transition: background .15s;
-    }
-
-    .author-ul li a:hover {
-        background: var(--sf-pale);
-    }
-
-    .au-avatar {
-        width: 28px;
-        height: 28px;
-        border-radius: 50%;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        font-size: .68rem;
-        font-weight: 700;
-        color: #fff;
-        flex-shrink: 0;
-    }
-
-    .au-avatar-img {
-        width: 28px;
-        height: 28px;
-        border-radius: 50%;
-        object-fit: cover;
-        flex-shrink: 0;
-    }
-
-    .au-name {
-        font-size: .8rem;
-        color: var(--ink-soft);
-        font-weight: 500;
-        display: block;
-    }
-
-    .au-count {
-        font-size: .7rem;
-        color: var(--ink-muted);
-    }
-
-    /* Search */
-    .search-wrap {
-        background: var(--card-bg);
-        border: 1px solid var(--bdr);
-        border-radius: 10px;
-        padding: .85rem 1.1rem;
-        margin-bottom: 1.1rem;
-    }
-
-    .search-input {
-        width: 100%;
-        border: 1px solid var(--bdr);
-        border-radius: 6px;
-        padding: .45rem .9rem;
-        font-size: .83rem;
-        font-family: 'Noto Sans SC', sans-serif;
-        background: var(--sf-pale);
-        color: var(--ink);
-        outline: none;
-        transition: border-color .2s;
-    }
-
-    .search-input:focus {
-        border-color: var(--sf);
-        background: #fff;
-    }
-
-    .search-input::placeholder {
-        color: var(--ink-muted);
-    }
-
-    @media (max-width:768px) {
-        .anthology-card {
-            flex-direction: column;
-        }
-
-        .card-cover {
-            width: 100%;
-            min-width: unset;
-            height: 90px;
-        }
-    }
-</style>
+    @vite('resources/css/modules/_anthology.css')
 @endpush
 
-@section('content')
+@section('breadcrumb')
+<li class="breadcrumb-item">
+    <a href="{{ route('library.home') }}">首页</a>
+</li>
+<li class="breadcrumb-item active">文集</li>
+@endsection
+
+@section('hero')
 <div class="anthology-page-header">
     <div class="container-xl">
         <h1>文集 <span class="result-badge">{{ $total }}</span></h1>
         <p>经论注疏 · 禅修指引 · 法义探讨</p>
     </div>
 </div>
+@endsection
 
-<div class="page-body" style="background: var(--sf-pale);">
+@section('content')
+<div class="page-body">
     <div class="container-xl">
         <div class="row mt-3">
 
-            {{-- Left --}}
+            {{-- 文集列表 --}}
             <div class="col-lg-9">
 
                 @forelse($anthologies as $item)
-                <a href="{{ route('library.anthology.show', $item['id']) }}" class="anthology-card">
-                    {{-- Cover --}}
-                    <div class="card-cover" style="{{ $item['cover_image'] ? '' : 'background: ' . $item['cover_gradient'] }}">
-                        @if($item['cover_image'])
-                        <img src="{{ $item['cover_image'] }}" alt="{{ $item['title'] }}">
-                        @else
-                        <div class="cover-text-wrap">
-                            <div class="cover-title">{{ $item['title'] }}</div>
-                            <div class="cover-divider"></div>
-                            <div class="cover-sub">{{ $item['subtitle'] ?? '' }}</div>
-                        </div>
-                        @endif
-                    </div>
-
-                    {{-- Body --}}
-                    <div class="card-body-inner">
-                        <div class="card-main-title">{{ $item['title'] }}</div>
-                        @if(!empty($item['description']))
-                        <div class="card-desc">{{ $item['description'] }}</div>
-                        @endif
-                        <div class="card-author">
-                            @if(!empty($item['author']['avatar']))
-                            <img src="{{ $item['author']['avatar'] }}" class="a-avatar-img" alt="">
-                            @else
-                            <div class="a-avatar" style="background: {{ $item['author']['color'] }}">
-                                {{ $item['author']['initials'] }}
-                            </div>
-                            @endif
-                            <span class="a-name">{{ $item['author']['name'] }}</span>
-                        </div>
-                        <div class="chapter-tags">
-                            @foreach(array_slice($item['chapters'], 0, 4) as $ch)
-                            <span class="c-tag" title="{{ $ch }}">{{ mb_strimwidth($ch, 0, 14, '…') }}</span>
-                            @endforeach
-                            @if($item['children_number'] > 4)
-                            <span class="c-tag more">+{{ $item['children_number'] - 4 }} 章</span>
-                            @endif
-                        </div>
-                        <div class="card-meta-row">
-                            <span class="meta-it">
-                                <i class="ti ti-calendar" style="font-size:.82rem;"></i>
-                                {{ $item['updated_at'] }}
-                            </span>
-                            <span class="meta-it">
-                                <i class="ti ti-file-text" style="font-size:.82rem;"></i>
-                                {{ $item['children_number'] }} 章节
-                            </span>
-                        </div>
-                    </div>
-                </a>
+                    <x-ui.card-anthology
+                        :item="$item"
+                        :href="route('library.anthology.show', $item['id'])"
+                    />
                 @empty
-                <div class="text-center py-5 text-muted">暂无文集</div>
+                    <div class="wiki-card">
+                        <x-ui.empty-state title="暂无文集" />
+                    </div>
                 @endforelse
 
-                {{-- Pagination --}}
-                <div class="d-flex justify-content-center mt-3 anthology-pagination">
+                {{-- 分页 --}}
+                <div class="d-flex justify-content-center mt-3">
                     {{ $anthologies->links('library.anthology.pagination') }}
                 </div>
 
-            </div>{{-- /col --}}
+            </div>
 
-            {{-- Sidebar --}}
+            {{-- 侧边栏 --}}
             <div class="col-lg-3">
-                <div class="search-wrap">
-                    <input type="text" class="search-input" placeholder="搜索文集…">
+
+                {{-- 搜索 --}}
+                <div class="sb-card" style="padding: .85rem 1.1rem; margin-bottom: 1.1rem;">
+                    <x-ui.search-input
+                        :action="route('library.search')"
+                        placeholder="搜索文集…"
+                        :hidden-fields="['type' => 'anthology']"
+                    />
                 </div>
 
-                <div class="sidebar-box">
-                    <div class="sb-header">作者</div>
+                {{-- 作者列表 --}}
+                @if(!empty($authors))
+                <div class="sb-card">
+                    <div class="sb-head">作者</div>
                     <ul class="author-ul">
                         @foreach($authors as $author)
                         <li>
                             <a href="#">
-                                @if(!empty($author['avatar']))
-                                <img src="{{ $author['avatar'] }}" class="au-avatar-img" alt="">
-                                @else
-                                <div class="au-avatar" style="background: {{ $author['color'] }}">{{ $author['initials'] }}</div>
-                                @endif
-                                <div>
-                                    <span class="au-name">{{ $author['name'] }}</span>
-                                    <span class="au-count">{{ $author['count'] }} 篇文集</span>
-                                </div>
+                                <x-ui.author-avatar
+                                    :avatar="$author['avatar'] ?? null"
+                                    :color="$author['color']"
+                                    :initials="$author['initials']"
+                                    :name="$author['name']"
+                                    :sub="$author['count'] . ' 篇文集'"
+                                    size="md"
+                                />
                             </a>
                         </li>
                         @endforeach
                     </ul>
                 </div>
+                @endif
+
             </div>
 
         </div>

+ 68 - 598
api-v12/resources/views/library/anthology/show.blade.php

@@ -1,585 +1,41 @@
-{{-- api-v12/resources/views/library/anthology/show.blade.php --}}
-@extends('library.layouts.app')
+{{-- resources/views/library/anthology/show.blade.php --}}
+@extends('layouts.library')
 
 @section('title', $anthology['title'] . ' · 巴利书库')
 
+@push('styles')
+    @vite('resources/css/modules/_anthology.css')
+@endpush
+
 @section('breadcrumb')
 <li class="breadcrumb-item">
     <a href="{{ route('library.home') }}">首页</a>
 </li>
-
 <li class="breadcrumb-item">
     <a href="{{ route('library.anthology.index') }}">文集</a>
 </li>
-
-<li class="breadcrumb-item active">
-    {{ $anthology['title'] }}
-</li>
+<li class="breadcrumb-item active">{{ $anthology['title'] }}</li>
 @endsection
 
-@once
-@push('styles')
-<link rel="preconnect" href="https://fonts.googleapis.com">
-<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
-<style>
-    body {
-        background: var(--sf-pale) !important;
-        font-family: 'Noto Sans SC', sans-serif;
-    }
-
-
-    /* Hero */
-    .anthology-hero {
-        background: linear-gradient(135deg, var(--ink) 0%, #2d2010 100%);
-        padding: 2.5rem 0;
-    }
-
-    .hero-inner {
-        display: flex;
-        gap: 2.25rem;
-        align-items: flex-start;
-    }
-
-    /* Book cover */
-    .book-cover-3d {
-        width: 155px;
-        min-width: 155px;
-        height: 215px;
-        border-radius: 3px 9px 9px 3px;
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        justify-content: center;
-        padding: 1.25rem .9rem;
-        position: relative;
-        overflow: hidden;
-        box-shadow: -4px 0 0 rgba(0, 0, 0, .3), -6px 4px 14px rgba(0, 0, 0, .4), 4px 4px 18px rgba(0, 0, 0, .3);
-        flex-shrink: 0;
-    }
-
-    .book-cover-3d img {
-        position: absolute;
-        inset: 0;
-        width: 100%;
-        height: 100%;
-        object-fit: cover;
-    }
-
-    .book-cover-3d::before {
-        content: '';
-        position: absolute;
-        left: 0;
-        top: 0;
-        bottom: 0;
-        width: 13px;
-        background: linear-gradient(to right, rgba(0, 0, 0, .4), rgba(0, 0, 0, .1));
-        border-radius: 3px 0 0 3px;
-        z-index: 2;
-    }
-
-    .book-cover-3d::after {
-        content: '';
-        position: absolute;
-        inset: 0;
-        background: repeating-linear-gradient(45deg, transparent, transparent 8px, rgba(255, 255, 255, .015) 8px, rgba(255, 255, 255, .015) 9px);
-        z-index: 1;
-    }
-
-    .book-text-wrap {
-        position: relative;
-        z-index: 3;
-        text-align: center;
-    }
-
-    .book-title-text {
-        font-family: 'Noto Serif SC', serif;
-        font-size: 1.05rem;
-        font-weight: 600;
-        color: #fff;
-        line-height: 1.65;
-        letter-spacing: .13em;
-        word-break: break-all;
-    }
-
-    .book-divider {
-        width: 32px;
-        height: 1px;
-        background: var(--sf);
-        margin: .65rem auto;
-    }
-
-    .book-sub-text {
-        font-size: .65rem;
-        color: rgba(255, 255, 255, .5);
-        letter-spacing: .06em;
-        line-height: 1.5;
-    }
-
-    /* Hero right */
-    .hero-content {
-        flex: 1;
-        min-width: 0;
-    }
-
-    .hero-title {
-        font-family: 'Noto Serif SC', serif;
-        font-size: 1.75rem;
-        font-weight: 700;
-        color: #fff;
-        line-height: 1.3;
-        margin-bottom: .4rem;
-    }
-
-    .hero-subtitle {
-        font-size: .88rem;
-        color: rgba(255, 255, 255, .45);
-        font-style: italic;
-        letter-spacing: .04em;
-        margin-bottom: 1.1rem;
-    }
-
-    .hero-tags {
-        display: flex;
-        flex-wrap: wrap;
-        gap: .35rem;
-        margin-bottom: 1.3rem;
-    }
-
-    .hero-tag {
-        font-size: .72rem;
-        padding: 2px 9px;
-        border-radius: 20px;
-        background: rgba(200, 134, 10, .2);
-        color: var(--sf);
-        border: 1px solid rgba(200, 134, 10, .3);
-    }
-
-    .hero-info-row {
-        display: flex;
-        flex-wrap: wrap;
-        gap: 1.4rem;
-        margin-bottom: 1.3rem;
-    }
-
-    .hi-item {
-        display: flex;
-        align-items: center;
-        gap: .45rem;
-    }
-
-    .hi-label {
-        font-size: .72rem;
-        color: rgba(255, 255, 255, .4);
-        letter-spacing: .04em;
-        display: block;
-    }
-
-    .hi-value {
-        font-size: .83rem;
-        color: rgba(255, 255, 255, .82);
-        display: block;
-    }
-
-    .hi-avatar {
-        width: 26px;
-        height: 26px;
-        border-radius: 50%;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        font-size: .68rem;
-        font-weight: 700;
-        flex-shrink: 0;
-    }
-
-    .hero-desc {
-        font-size: .85rem;
-        color: rgba(255, 255, 255, .6);
-        line-height: 1.85;
-        margin-bottom: 1.6rem;
-        max-width: 600px;
-    }
-
-    .btn-read-primary {
-        background: var(--sf);
-        color: var(--ink);
-        font-weight: 700;
-        font-size: .88rem;
-        padding: .55rem 1.6rem;
-        border-radius: 6px;
-        border: none;
-        cursor: pointer;
-        text-decoration: none;
-        display: inline-flex;
-        align-items: center;
-        gap: .45rem;
-        transition: background .2s, transform .15s;
-    }
-
-    .btn-read-primary:hover {
-        background: #dea020;
-        color: var(--ink);
-        transform: translateY(-1px);
-    }
-
-    .btn-outline-hero {
-        background: transparent;
-        color: rgba(255, 255, 255, .7);
-        font-size: .85rem;
-        padding: .5rem 1.3rem;
-        border-radius: 6px;
-        border: 1px solid rgba(255, 255, 255, .2);
-        cursor: pointer;
-        text-decoration: none;
-        display: inline-flex;
-        align-items: center;
-        gap: .4rem;
-        transition: all .2s;
-        margin-left: .65rem;
-    }
-
-    .btn-outline-hero:hover {
-        border-color: rgba(255, 255, 255, .5);
-        color: #fff;
-    }
-
-    /* Section card */
-    .sec-card {
-        background: var(--card-bg);
-        border: 1px solid var(--bdr);
-        border-radius: 10px;
-        overflow: hidden;
-        margin-bottom: 1.3rem;
-    }
-
-    .sec-header {
-        padding: .85rem 1.4rem;
-        border-bottom: 1px solid var(--bdr);
-        display: flex;
-        align-items: center;
-        gap: .55rem;
-    }
-
-    .sec-bar {
-        width: 3px;
-        height: 15px;
-        background: var(--sf);
-        border-radius: 2px;
-        flex-shrink: 0;
-    }
-
-    .sec-title {
-        font-family: 'Noto Serif SC', serif;
-        font-size: .9rem;
-        font-weight: 600;
-        color: var(--ink-soft);
-        letter-spacing: .04em;
-    }
-
-    .sec-count {
-        margin-left: auto;
-        font-size: .75rem;
-        color: var(--ink-muted);
-        background: var(--sf-light);
-        padding: 2px 8px;
-        border-radius: 10px;
-    }
-
-    /* About */
-    .sec-body {
-        padding: 1.15rem 1.4rem;
-        font-size: .855rem;
-        color: var(--ink-soft);
-        line-height: 1.95;
-    }
-
-    .sec-body p {
-        margin-bottom: .8rem;
-    }
-
-    .sec-body p:last-child {
-        margin-bottom: 0;
-    }
-
-    /* TOC */
-    .toc-ul {
-        list-style: none;
-        padding: .35rem 0;
-        margin: 0;
-    }
-
-    .toc-ul li a {
-        display: flex;
-        align-items: center;
-        padding: .65rem 1.4rem;
-        text-decoration: none;
-        border-bottom: 1px solid rgba(232, 221, 208, .5);
-        transition: background .15s;
-    }
-
-    .toc-ul li:last-child a {
-        border-bottom: none;
-    }
-
-    .toc-ul li a:hover {
-        background: var(--sf-pale);
-    }
-
-    .toc-num {
-        font-size: .72rem;
-        color: var(--ink-muted);
-        width: 26px;
-        flex-shrink: 0;
-    }
-
-    .toc-name {
-        font-size: .855rem;
-        color: var(--ink-soft);
-        flex: 1;
-        line-height: 1.4;
-    }
-
-    .toc-ul li a:hover .toc-name {
-        color: var(--sf);
-    }
-
-    .toc-arrow {
-        color: var(--bdr);
-        font-size: .85rem;
-    }
-
-    .toc-ul li a:hover .toc-arrow {
-        color: var(--sf);
-    }
-
-    /* Sidebar */
-    .sb-card {
-        background: var(--card-bg);
-        border: 1px solid var(--bdr);
-        border-radius: 10px;
-        overflow: hidden;
-        margin-bottom: 1.15rem;
-    }
-
-    .sb-head {
-        padding: .8rem 1.2rem;
-        border-bottom: 1px solid var(--bdr);
-        font-family: 'Noto Serif SC', serif;
-        font-size: .875rem;
-        font-weight: 600;
-        color: var(--ink-soft);
-        letter-spacing: .04em;
-        display: flex;
-        align-items: center;
-        gap: .45rem;
-    }
-
-    .sb-head::before {
-        content: '';
-        display: block;
-        width: 3px;
-        height: 13px;
-        background: var(--sf);
-        border-radius: 2px;
-    }
-
-    .smeta-row {
-        display: flex;
-        padding: .7rem 1.2rem;
-        border-bottom: 1px solid var(--bdr);
-        font-size: .8rem;
-        align-items: flex-start;
-        gap: .45rem;
-    }
-
-    .smeta-row:last-child {
-        border-bottom: none;
-    }
-
-    .smeta-label {
-        color: var(--ink-muted);
-        min-width: 65px;
-        flex-shrink: 0;
-    }
-
-    .smeta-value {
-        color: var(--ink-soft);
-        font-weight: 500;
-    }
-
-    .smeta-value a {
-        color: var(--sf);
-        text-decoration: none;
-    }
-
-    .smeta-value a:hover {
-        text-decoration: underline;
-    }
-
-    /* Author card */
-    .author-block {
-        display: flex;
-        align-items: center;
-        gap: .8rem;
-        padding: 1.1rem 1.2rem;
-    }
-
-    .author-av-lg {
-        width: 48px;
-        height: 48px;
-        border-radius: 50%;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        font-size: .95rem;
-        font-weight: 700;
-        flex-shrink: 0;
-    }
-
-    .author-av-img {
-        width: 48px;
-        height: 48px;
-        border-radius: 50%;
-        object-fit: cover;
-        flex-shrink: 0;
-    }
-
-    .hi-avatar {
-        width: 26px;
-        height: 26px;
-        border-radius: 50%;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        font-size: .68rem;
-        font-weight: 700;
-        flex-shrink: 0;
-    }
-
-    .hi-avatar-img {
-        width: 26px;
-        height: 26px;
-        border-radius: 50%;
-        object-fit: cover;
-        flex-shrink: 0;
-    }
-
-    .author-block-name {
-        font-weight: 600;
-        font-size: .9rem;
-        color: var(--ink);
-        margin-bottom: .18rem;
-    }
-
-    .author-block-stats {
-        font-size: .75rem;
-        color: var(--ink-muted);
-    }
-
-    .author-bio {
-        font-size: .78rem;
-        color: var(--ink-muted);
-        line-height: 1.65;
-        padding: 0 1.2rem 1.1rem;
-        border-top: 1px solid var(--bdr);
-        padding-top: .9rem;
-    }
-
-    /* Related */
-    .related-ul {
-        list-style: none;
-        padding: 0;
-        margin: 0;
-    }
-
-    .related-ul li a {
-        display: flex;
-        align-items: center;
-        gap: .7rem;
-        padding: .7rem 1.2rem;
-        border-bottom: 1px solid var(--bdr);
-        text-decoration: none;
-        transition: background .15s;
-    }
-
-    .related-ul li:last-child a {
-        border-bottom: none;
-    }
-
-    .related-ul li a:hover {
-        background: var(--sf-pale);
-    }
-
-    .related-cover-mini {
-        width: 34px;
-        height: 46px;
-        border-radius: 2px 5px 5px 2px;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        font-size: .6rem;
-        color: rgba(255, 255, 255, .8);
-        font-family: 'Noto Serif SC', serif;
-        flex-shrink: 0;
-        text-align: center;
-        line-height: 1.3;
-    }
-
-    .related-t {
-        font-size: .8rem;
-        color: var(--ink-soft);
-        font-weight: 500;
-        margin-bottom: .18rem;
-        line-height: 1.3;
-    }
-
-    .related-ul li a:hover .related-t {
-        color: var(--sf);
-    }
-
-    .related-a {
-        font-size: .7rem;
-        color: var(--ink-muted);
-    }
-
-    @media (max-width: 900px) {
-        .hero-inner {
-            flex-direction: column;
-            align-items: center;
-        }
-
-        .book-cover-3d {
-            height: 170px;
-        }
-    }
-</style>
-@endpush
-@endonce
-
-@section('content')
-
-
-{{-- Hero --}}
+@section('hero')
 <div class="anthology-hero">
     <div class="container-xl">
         <div class="hero-inner">
 
-            {{-- 3D Book Cover --}}
-            <div class="book-cover-3d" style="{{ $anthology['cover_image'] ? '' : 'background: ' . $anthology['cover_gradient'] }}">
-                @if($anthology['cover_image'])
-                <img src="{{ $anthology['cover_image'] }}" alt="{{ $anthology['title'] }}">
-                @else
-                <div class="book-text-wrap">
-                    <div class="book-title-text">{{ $anthology['title'] }}</div>
-                    <div class="book-divider"></div>
-                    <div class="book-sub-text">{{ $anthology['subtitle'] ?? '' }}</div>
-                </div>
-                @endif
-            </div>
-
-            {{-- Content --}}
+            {{-- 3D 书籍封面 --}}
+            <x-ui.book-cover
+                :image="$anthology['cover_image'] ?? null"
+                :gradient="$anthology['cover_gradient']"
+                :title="$anthology['title']"
+                :subtitle="$anthology['subtitle'] ?? ''"
+                size="lg"
+                :style3d="true"
+            />
+
+            {{-- 文集信息 --}}
             <div class="hero-content">
                 <div class="hero-title">{{ $anthology['title'] }}</div>
+
                 @if(!empty($anthology['subtitle']))
                 <div class="hero-subtitle">{{ $anthology['subtitle'] }}</div>
                 @endif
@@ -594,13 +50,13 @@
 
                 <div class="hero-info-row">
                     <div class="hi-item">
-                        @if(!empty($anthology['author']['avatar']))
-                        <img src="{{ $anthology['author']['avatar'] }}" class="hi-avatar-img" alt="">
-                        @else
-                        <div class="hi-avatar" style="background: {{ $anthology['author']['color'] }}; color: #fff">
-                            {{ $anthology['author']['initials'] }}
-                        </div>
-                        @endif
+                        <x-ui.author-avatar
+                            :avatar="$anthology['author']['avatar'] ?? null"
+                            :color="$anthology['author']['color']"
+                            :initials="$anthology['author']['initials']"
+                            :name="$anthology['author']['name']"
+                            size="sm"
+                        />
                         <div>
                             <span class="hi-label">作者</span>
                             <span class="hi-value">{{ $anthology['author']['name'] }}</span>
@@ -632,12 +88,17 @@
 
                 <div>
                     @if(!empty($anthology['articles']))
-                    <a href="{{ route('library.anthology.read', ['anthology' => $anthology['id'], 'article' => $anthology['articles'][0]['id']]) }}" class="btn-read-primary">
+                    <a href="{{ route('library.anthology.read', [
+                            'anthology' => $anthology['id'],
+                            'article'   => $anthology['articles'][0]['id']
+                        ]) }}"
+                       class="btn-read-primary">
                         <i class="ti ti-book-2"></i>
                         在线阅读
                     </a>
                     @endif
-                    <a href="{{ config('mint.server.dashboard_base_path') }}/workspace/anthology/{{ $anthology['id'] }}" class="btn-outline-hero">
+                    <a href="{{ config('mint.server.dashboard_base_path') }}/workspace/anthology/{{ $anthology['id'] }}"
+                       class="btn-outline-hero">
                         <i class="ti ti-pencil"></i>
                         在编辑器中打开
                     </a>
@@ -647,16 +108,17 @@
         </div>
     </div>
 </div>
+@endsection
 
-{{-- Body --}}
-<div class="page-body" style="background: var(--sf-pale);">
+@section('content')
+<div class="page-body">
     <div class="container-xl">
         <div class="row mt-2">
 
-            {{-- Left --}}
+            {{-- 主内容 --}}
             <div class="col-lg-8">
 
-                {{-- About --}}
+                {{-- 关于本文集 --}}
                 @if(!empty($anthology['about']))
                 <div class="sec-card">
                     <div class="sec-header">
@@ -665,15 +127,15 @@
                     </div>
                     <div class="sec-body">
                         @foreach(explode("\n", $anthology['about']) as $para)
-                        @if(trim($para))
-                        <p>{{ trim($para) }}</p>
-                        @endif
+                            @if(trim($para))
+                            <p>{{ trim($para) }}</p>
+                            @endif
                         @endforeach
                     </div>
                 </div>
                 @endif
 
-                {{-- TOC --}}
+                {{-- 目录 --}}
                 <div class="sec-card">
                     <div class="sec-header">
                         <div class="sec-bar"></div>
@@ -683,7 +145,10 @@
                     <ul class="toc-ul">
                         @foreach($anthology['articles'] as $article)
                         <li>
-                            <a href="{{ route('library.anthology.read', ['anthology' => $anthology['id'], 'article' => $article['id']]) }}">
+                            <a href="{{ route('library.anthology.read', [
+                                    'anthology' => $anthology['id'],
+                                    'article'   => $article['id']
+                                ]) }}">
                                 <span class="toc-num">{{ str_pad($article['order'], 2, '0', STR_PAD_LEFT) }}</span>
                                 <span class="toc-name">{{ $article['title'] }}</span>
                                 <span class="toc-arrow">›</span>
@@ -693,17 +158,19 @@
                     </ul>
                 </div>
 
-            </div>{{-- /col --}}
+            </div>
 
-            {{-- Sidebar --}}
+            {{-- 侧边栏 --}}
             <div class="col-lg-4">
 
-                {{-- Meta --}}
+                {{-- 文集信息 --}}
                 <div class="sb-card">
                     <div class="sb-head">文集信息</div>
                     <div class="smeta-row">
                         <span class="smeta-label">作者</span>
-                        <span class="smeta-value"><a href="#">{{ $anthology['author']['name'] }}</a></span>
+                        <span class="smeta-value">
+                            <a href="#">{{ $anthology['author']['name'] }}</a>
+                        </span>
                     </div>
                     @if(!empty($anthology['language']))
                     <div class="smeta-row">
@@ -731,17 +198,16 @@
                     @endif
                 </div>
 
-                {{-- Author --}}
+                {{-- 作者 --}}
                 <div class="sb-card">
                     <div class="sb-head">作者</div>
                     <div class="author-block">
-                        @if(!empty($anthology['author']['avatar']))
-                        <img src="{{ $anthology['author']['avatar'] }}" class="author-av-img" alt="">
-                        @else
-                        <div class="author-av-lg" style="background: {{ $anthology['author']['color'] }}; color: #fff">
-                            {{ $anthology['author']['initials'] }}
-                        </div>
-                        @endif
+                        <x-ui.author-avatar
+                            :avatar="$anthology['author']['avatar'] ?? null"
+                            :color="$anthology['author']['color']"
+                            :initials="$anthology['author']['initials']"
+                            size="lg"
+                        />
                         <div>
                             <div class="author-block-name">{{ $anthology['author']['name'] }}</div>
                             <div class="author-block-stats">
@@ -756,7 +222,7 @@
                     @endif
                 </div>
 
-                {{-- Related --}}
+                {{-- 相关文集 --}}
                 @if($related->count())
                 <div class="sb-card">
                     <div class="sb-head">相关文集</div>
@@ -764,9 +230,13 @@
                         @foreach($related as $rel)
                         <li>
                             <a href="{{ route('library.anthology.show', $rel['id']) }}">
-                                <div class="related-cover-mini" style="background: {{ $rel['cover_gradient'] }}">
-                                    {{ mb_substr($rel['title'], 0, 4) }}
-                                </div>
+                                <x-ui.book-cover
+                                    :image="null"
+                                    :gradient="$rel['cover_gradient']"
+                                    :title="mb_substr($rel['title'], 0, 4)"
+                                    size="sm"
+                                    :style3d="false"
+                                />
                                 <div>
                                     <div class="related-t">{{ $rel['title'] }}</div>
                                     <div class="related-a">{{ $rel['author_name'] }}</div>

+ 28 - 0
api-v12/resources/views/library/book/_toc.blade.php

@@ -0,0 +1,28 @@
+{{-- resources/views/library/book/_toc.blade.php
+     TOC 列表局部视图,在 offcanvas 和侧边栏中复用。
+     变量:$toc array,$anthologyId(可选)
+--}}
+@if(!empty($toc))
+<ul>
+    @foreach ($toc as $item)
+    <li class="toc_item toc-level-{{ $item['level'] }}
+               {{ ($item['active'] ?? false) ? 'toc-active' : ($item['disabled'] ? 'toc-disabled' : '') }}">
+        @if($item['active'] ?? false)
+            <span title="{{ $item['title'] }}">{{ $item['title'] }}</span>
+        @elseif(!$item['disabled'])
+            @if(isset($anthologyId))
+                <a href="{{ route('library.anthology.read', ['anthology' => $anthologyId, 'article' => $item['id'], 'channel' => request('channel')]) }}"
+                   title="{{ $item['title'] }}">{{ $item['title'] }}</a>
+            @else
+                <a href="{{ route('library.tipitaka.read', ['id' => $item['id'], 'channel' => request('channel')]) }}"
+                   title="{{ $item['title'] }}">{{ $item['title'] }}</a>
+            @endif
+        @else
+            <span title="{{ $item['title'] }}">{{ $item['title'] }}</span>
+        @endif
+    </li>
+    @endforeach
+</ul>
+@else
+<div class="alert alert-warning">此书没有目录</div>
+@endif

+ 316 - 506
api-v12/resources/views/library/book/read.blade.php

@@ -1,579 +1,389 @@
-{{-- api-v12/resources/views/library/book/read.blade.php --}}
-<!DOCTYPE html>
-<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
-
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>{{ $book['title'] }}</title>
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.3.2/dist/css/tabler.min.css" />
-    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
-
-    @vite(['resources/css/reader.css', 'resources/js/app.js'])
-
-    <script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.3.2/dist/js/tabler.min.js"></script>
-    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
-
-
-</head>
-
+{{-- resources/views/library/book/read.blade.php
+     全站共用阅读器。供 anthology/read 和 tipitaka/read 路由使用。
+     重构:改为 @extends('layouts.reader'),移除 CDN 引入,JS 提取为模块。
+--}}
+@extends('layouts.reader')
+
+@section('title', $book['title'])
+
+@section('body-class', session('theme', 'light') . '-mode')
+
+{{-- 术语抽屉(所有阅读页统一使用 wiki.term-drawer) --}}
+@push('scripts')
+@vite('resources/js/modules/term-tooltip.js')
+@endpush
+
+@section('reader-content')
+
+{{-- 术语抽屉 --}}
+<x-wiki.term-drawer />
+
+{{-- Navbar --}}
+<header class="navbar navbar-expand-md navbar-light d-print-none">
+    <div class="container-xl">
+        <button class="navbar-toggler"
+            type="button"
+            data-bs-toggle="offcanvas"
+            data-bs-target="#tocDrawer"
+            aria-controls="tocDrawer">
+            <span class="navbar-toggler-icon"></span>
+        </button>
+
+        <div class="navbar-brand d-flex flex-column lh-1">
+            @if(!empty($book['anthology']))
+            <small class="text-muted" style="font-size:.75rem;">
+                <a href="{{ route('library.anthology.show', $book['anthology']['id']) }}"
+                    class="text-muted text-decoration-none">
+                    {{ $book['anthology']['title'] }}
+                </a>
+            </small>
+            @endif
+        </div>
 
-<body class="{{ session('theme', 'light') }}-mode">
-    <x-term-drawer />
+        <div class="navbar-nav flex-row order-md-last align-items-center">
 
-    <!-- Navbar -->
-    <header class="navbar navbar-expand-md navbar-light d-print-none">
-        <div class="container-xl">
-            <button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocDrawer" aria-controls="tocDrawer">
-                <span class="navbar-toggler-icon"></span>
-            </button>
-            {{-- 面包屑:文集标题 > 文章标题 --}}
-            <div class="navbar-brand d-flex flex-column lh-1">
-                @if(!empty($book['anthology']))
-                <small class="text-muted" style="font-size:.75rem;">
-                    <a href="{{ route('library.anthology.show', $book['anthology']['id']) }}" class="text-muted text-decoration-none">
-                        {{ $book['anthology']['title'] }}
-                    </a>
-                </small>
-                @endif
+            {{-- 编辑器按钮 --}}
+            @if(!empty($editor_link))
+            <div class="nav-item me-2">
+                <a href="{{ $editor_link }}" target="_blank" class="nav-link">
+                    <i class="ti ti-pencil me-1 d-none d-md-inline"></i>
+                    <span class="d-none d-md-inline">编辑器</span>
+                    <i class="ti ti-pencil d-md-none"></i>
+                </a>
             </div>
-            <div class="navbar-nav flex-row order-md-last align-items-center">
-                {{-- Desktop 编辑器按钮 --}}
-                @if(!empty($editor_link))
-                <div class="nav-item d-none d-md-block me-2">
-                    <a href="{{ $editor_link }}"
-                        target="_blank"
-                        class="nav-link">
-                        <i class="fas fa-pen-to-square me-1"></i>
-                        编辑器
-                    </a>
-                </div>
-
-                {{-- Mobile 编辑器按钮 --}}
-                <div class="nav-item d-md-none me-2">
-                    <a href="{{ $editor_link }}"
-                        target="_blank"
-                        class="nav-link">
-                        <i class="fas fa-pen-to-square"></i>
-                    </a>
-                </div>
-                @endif
-
-                {{-- Desktop 设置按钮 --}}
-                <div class="nav-item d-none d-md-block me-2">
-                    <a href="#"
-                        class="nav-link"
-                        data-bs-toggle="modal"
-                        data-bs-target="#settingsModal">
-                        <i class="fas fa-cog me-1"></i>
-                        设置
-                    </a>
-                </div>
-
-                {{-- Mobile 设置按钮 --}}
-                <div class="nav-item d-md-none me-2">
-                    <a href="#"
-                        class="nav-link"
-                        data-bs-toggle="modal"
-                        data-bs-target="#settingsModal">
-                        <i class="fas fa-cog"></i>
-                    </a>
-                </div>
-                {{-- Desktop Dropdown --}}
-                @if(!empty($channels))
-                <div class="nav-item dropdown d-none d-md-block me-2">
-                    <a href="#" class="nav-link" data-bs-toggle="dropdown">
-                        <i class="fas fa-layer-group me-1"></i>
-                        版本
-                    </a>
-
-                    <div class="dropdown-menu dropdown-menu-end">
-                        @foreach($channels as $channel)
-                        <a class="dropdown-item"
-                            href="{{ request()->fullUrlWithQuery(['channel' => $channel['id']]) }}">
-                            {{ $channel['name'] }}
-                            <small class="text-muted">
-                                ({{ __('language.' . $channel['lang']) }})
-                            </small>
-                        </a>
-                        @endforeach
-                    </div>
-                </div>
+            @endif
 
-                {{-- Mobile Drawer Trigger --}}
-                <div class="nav-item d-md-none me-2">
-                    <a href="#" class="nav-link"
-                        data-bs-toggle="offcanvas"
-                        data-bs-target="#channelDrawer">
-                        <i class="fas fa-layer-group"></i>
-                    </a>
-                </div>
-                @endif
-                <div class="nav-item">
-                    <a href="#" class="nav-link" id="themeToggle">
-                        <i class="fas fa-moon"></i>
-                    </a>
-                </div>
-                @auth
-                <div class="nav-item dropdown">
-                    <a href="#" class="nav-link d-flex lh-1 text-reset p-0" data-bs-toggle="dropdown">
-                        <span class="avatar avatar-sm" style="background-image: url({{ auth()->user()->avatar ?? '' }})"></span>
-                    </a>
-                    <div class="dropdown-menu dropdown-menu-end">
-                        <a class="dropdown-item" href="#">Profile</a>
-                        <a class="dropdown-item" href="{{ route('logout') }}">Logout</a>
-                    </div>
-                </div>
-                @endauth
+            {{-- 设置 --}}
+            <div class="nav-item me-2">
+                <a href="#"
+                    class="nav-link"
+                    data-bs-toggle="modal"
+                    data-bs-target="#settingsModal">
+                    <i class="ti ti-settings me-1 d-none d-md-inline"></i>
+                    <span class="d-none d-md-inline">设置</span>
+                    <i class="ti ti-settings d-md-none"></i>
+                </a>
             </div>
-        </div>
-    </header>
-
-    <!-- TOC Drawer (Mobile) -->
-    @if(!empty($channels))
-    <div class="offcanvas offcanvas-end"
-        tabindex="-1"
-        id="channelDrawer">
-
-        <div class="offcanvas-header">
-            <h5 class="offcanvas-title">选择版本</h5>
-
-            <button type="button"
-                class="btn-close"
-                data-bs-dismiss="offcanvas">
-            </button>
-        </div>
-
-        <div class="offcanvas-body">
-
-            <div class="list-group list-group-flush">
-
-                @foreach($channels as $channel)
-
-                <a
-                    href="{{ request()->fullUrlWithQuery(['channel' => $channel['id']]) }}"
-                    class="list-group-item list-group-item-action">
-
-                    <div class="fw-bold">
-                        {{ $channel['name'] }}
-                    </div>
-
-                    <small class="text-muted">
-                        {{ __('language.' . $channel['lang']) }}
-                    </small>
 
+            {{-- 版本切换:desktop 版本在右侧边栏展示,mobile 触发 offcanvas --}}
+            @if(!empty($channels))
+            <div class="nav-item d-md-none me-2">
+                <a href="#" class="nav-link"
+                    data-bs-toggle="offcanvas"
+                    data-bs-target="#channelDrawer">
+                    <i class="ti ti-layers"></i>
                 </a>
+            </div>
+            @endif
 
-                @endforeach
+            {{-- 夜间模式 --}}
+            <div class="nav-item">
+                <a href="#" class="nav-link" id="themeToggle">
+                    <i class="ti ti-moon"></i>
+                </a>
+            </div>
 
+            @auth
+            <div class="nav-item dropdown">
+                <a href="#" class="nav-link d-flex lh-1 text-reset p-0"
+                    data-bs-toggle="dropdown">
+                    <span class="avatar avatar-sm"
+                        style="background-image: url({{ auth()->user()->avatar ?? '' }})">
+                    </span>
+                </a>
+                <div class="dropdown-menu dropdown-menu-end">
+                    <a class="dropdown-item" href="{{ route('logout') }}">退出</a>
+                </div>
             </div>
+            @endauth
 
         </div>
     </div>
-    @endif
-    <div class="offcanvas offcanvas-start" tabindex="-1" id="tocDrawer" aria-labelledby="tocDrawerLabel">
-        <div class="offcanvas-header">
-            <h5 class="offcanvas-title" id="tocDrawerLabel">目录</h5>
-            <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
-        </div>
-        <div class="offcanvas-body">
-            @if(isset($book['toc']) && count($book['toc']) > 0)
-            <ul>
-                @foreach ($book['toc'] as $item)
-                <li class="toc_item toc-level-{{ $item['level'] }} {{ $item['active'] ?? false ? 'toc-active' : ($item['disabled'] ? 'toc-disabled' : '') }}">
-                    @if($item['active'] ?? false)
-                    <span title="{{ $item['title'] }}">
-                        {{ $item['title'] }}
-                    </span>
-                    @elseif(!$item['disabled'])
-
-                    @if(isset($anthologyId))
-                    {{-- ✅ 修改1:使用文集阅读路由,传 anthology + article 两个参数 --}}
-                    <a href="{{ route('library.anthology.read', ['anthology' => $anthologyId, 'article' => $item['id'],'channel' => request('channel')]) }}" title="{{ $item['title'] }}">
-                        @else
-                        <a href="{{ route('library.tipitaka.read', ['id' => $item['id'] ,'channel' => request('channel')]) }}" title="{{ $item['title'] }}">
-                            @endif
-                            {{ $item['title'] }}
-                        </a>
-                        @else
-                        <span title="{{ $item['title'] }}">
-                            {{ $item['title'] }}
-                        </span>
-                        @endif
-                </li>
-                @endforeach
-            </ul>
-            @else
-            <div class="alert alert-warning">此书没有目录</div>
-            @endif
+</header>
+
+{{-- 版本 Offcanvas(mobile) --}}
+@if(!empty($channels))
+<div class="offcanvas offcanvas-end" tabindex="-1" id="channelDrawer">
+    <div class="offcanvas-header">
+        <h5 class="offcanvas-title">选择版本</h5>
+        <button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
+    </div>
+    <div class="offcanvas-body">
+        <div class="list-group list-group-flush">
+            @foreach($channels as $channel)
+            <a href="{{ request()->fullUrlWithQuery(['channel' => $channel['id']]) }}"
+                class="list-group-item list-group-item-action">
+                <div class="fw-bold">{{ $channel['name'] }}</div>
+                <small class="text-muted">{{ __('language.' . $channel['lang']) }}</small>
+            </a>
+            @endforeach
         </div>
     </div>
+</div>
+@endif
+
+{{-- TOC Offcanvas(mobile) --}}
+<div class="offcanvas offcanvas-start" tabindex="-1" id="tocDrawer">
+    <div class="offcanvas-header">
+        <h5 class="offcanvas-title">目录</h5>
+        <button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
+    </div>
+    <div class="offcanvas-body">
+        @include('library.book._toc', ['toc' => $book['toc'] ?? []])
+    </div>
+</div>
 
-    <!-- Main Content Area -->
+{{-- 主内容区 --}}
+<div class="container-xl">
     <div class="main-container">
 
-        <!-- TOC Sidebar (Tablet+) -->
+        {{-- TOC 侧边栏(tablet+) --}}
         <div class="toc-sidebar card">
             <div class="card-body">
                 <h5>目录</h5>
-                @if(isset($book['toc']) && count($book['toc']) > 0)
-                <ul>
-                    @foreach ($book['toc'] as $item)
-                    <li class="toc_item toc-level-{{ $item['level'] }} {{ $item['active'] ?? false ? 'toc-active' : ($item['disabled'] ? 'toc-disabled' : '') }}">
-                        @if($item['active'] ?? false)
-                        <span>{{ $item['title'] }}</span>
-                        @elseif(!$item['disabled'])
-                        {{-- --}}
-                        @if(isset($anthologyId))
-                        <a href="{{ route('library.anthology.read', ['anthology' => $anthologyId, 'article' => $item['id'],'channel' => request('channel')]) }}">
-                            @else
-                            <a href="{{ route('library.tipitaka.read', ['id' => $item['id'],'channel' => request('channel')]) }}">
-                                @endif
-                                {{ $item['title'] }}
-                            </a>
-                            @else
-                            <span>{{ $item['title'] }}</span>
-                            @endif
-                    </li>
-                    @endforeach
-                </ul>
-                @else
-                <div class="alert alert-warning">此书没有目录</div>
-                @endif
+                @include('library.book._toc', ['toc' => $book['toc'] ?? []])
             </div>
         </div>
 
-        <!-- Main Content -->
+        {{-- 正文 --}}
         <div class="content-area card">
             <div class="card-body">
 
-                <div>
-                    <h2>{{ $book['title'] }}</h2>
-                    <p>
-                        <strong>Author:</strong>
-                        <span>
-                            {{ $book['author'] }}
-                            @if(isset($book['publisher']))
-                            @
-                            <a href="{{ route('blog.index', ['user' => $book['publisher']->username]) }}">
-                                {{ $book['publisher']->nickname }}
-                            </a>
-                            @endif
-                        </span>
-                    </p>
-
-                    <div class="content">
-                        @if(isset($book['content']) && count($book['content']) > 0)
-                        @foreach ($book['content'] as $paragraph)
-                        <div id="para-{{ $paragraph['id'] }}">
-                            @foreach ($paragraph['text'] as $rows)
-                            <div style="display:flex;">
-                                @foreach ($rows as $col)
-                                <div style="flex:1;">
-                                    @if($paragraph['level'] < 8)
-                                        {{-- ✅ 修改3:{!! !!} 不转义,渲染 HTML 内容 --}}
-                                        <h{{ $paragraph['level'] }}>{!! $col !!}</h{{ $paragraph['level'] }}>
-                                        @else
-                                        <p>{!! $col !!}</p>
-                                        @endif
-                                </div>
-                                @endforeach
+                <h2>{{ $book['title'] }}</h2>
+                <p>
+                    <strong>作者:</strong>
+                    {{ $book['author'] }}
+                    @if(isset($book['publisher']))
+                    @ <a href="{{ route('blog.index', ['user' => $book['publisher']->username]) }}">
+                        {{ $book['publisher']->nickname }}
+                    </a>
+                    @endif
+                </p>
+
+                <div class="content">
+                    @if(isset($book['content']) && count($book['content']) > 0)
+                    @foreach ($book['content'] as $paragraph)
+                    <div id="para-{{ $paragraph['id'] }}">
+                        @foreach ($paragraph['text'] as $rows)
+                        <div style="display:flex;">
+                            @foreach ($rows as $col)
+                            <div style="flex:1;">
+                                @if($paragraph['level'] < 8)
+                                    <h{{ $paragraph['level'] }}>{!! $col !!}</h{{ $paragraph['level'] }}>
+                                    @else
+                                    <p>{!! $col !!}</p>
+                                    @endif
                             </div>
                             @endforeach
                         </div>
                         @endforeach
-                        @else
-                        <div>没有内容</div>
-                        @endif
                     </div>
+                    @endforeach
+                    @else
+                    <div>没有内容</div>
+                    @endif
                 </div>
 
-                <!-- Nav buttons -->
+                {{-- 上下翻页 --}}
                 <div class="mt-6 pt-6">
                     <ul class="pagination">
                         @if(!empty($book['pagination']['prev']))
                         <li class="page-item page-prev">
-                            {{-- ✅ 修改2:翻页也用文集路由 --}}
-                            @if(isset($anthologyId))
-                            <a class="page-link"
-                                href="{{ route('library.anthology.read', [
-                                        'anthology' => $anthologyId,
-                                        'article' => $book['pagination']['prev']['id'],
-                                        'channel' => request('channel')
-                                ]) }}">
-                                @else
-                                <a class="page-link" href="{{ route('library.tipitaka.read', [
-                                        'id' => $book['pagination']['prev']['id'],
-                                        'channel' => request('channel')
-                                ]) }}">
-                                    @endif
-                                    <div class="row align-items-center">
-                                        <div class="col-auto">
-                                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-1">
-                                                <path d="M15 6l-6 6l6 6"></path>
-                                            </svg>
-                                        </div>
-                                        <div class="col">
-                                            <div class="page-item-subtitle">上一篇</div>
-                                            <div class="page-item-title">{{ $book['pagination']['prev']['title'] }}</div>
-                                        </div>
+                            <a class="page-link" href="{{ isset($anthologyId)
+                            ? route('library.anthology.read', ['anthology' => $anthologyId, 'article' => $book['pagination']['prev']['id'], 'channel' => request('channel')])
+                            : route('library.tipitaka.read', ['id' => $book['pagination']['prev']['id'], 'channel' => request('channel')]) }}">
+                                <div class="row align-items-center">
+                                    <div class="col-auto">
+                                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                                            <path d="M15 6l-6 6l6 6"></path>
+                                        </svg>
+                                    </div>
+                                    <div class="col">
+                                        <div class="page-item-subtitle">上一篇</div>
+                                        <div class="page-item-title">{{ $book['pagination']['prev']['title'] }}</div>
                                     </div>
-                                </a>
+                                </div>
+                            </a>
                         </li>
                         @endif
                         @if(!empty($book['pagination']['next']))
                         <li class="page-item page-next">
-                            @if(isset($anthologyId))
-                            <a class="page-link" href="{{ route('library.anthology.read', ['anthology' => $anthologyId, 'article' => $book['pagination']['next']['id'],'channel' => request('channel')]) }}">
-                                @else
-                                <a class="page-link" href="{{ route('library.tipitaka.read', ['id' => $book['pagination']['next']['id'],'channel' => request('channel')]) }}">
-                                    @endif
-                                    <div class="row align-items-center">
-                                        <div class="col">
-                                            <div class="page-item-subtitle">下一篇</div>
-                                            <div class="page-item-title">{{ $book['pagination']['next']['title'] }}</div>
-                                        </div>
-                                        <div class="col-auto">
-                                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-1">
-                                                <path d="M9 6l6 6l-6 6"></path>
-                                            </svg>
-                                        </div>
+                            <a class="page-link" href="{{ isset($anthologyId)
+                            ? route('library.anthology.read', ['anthology' => $anthologyId, 'article' => $book['pagination']['next']['id'], 'channel' => request('channel')])
+                            : route('library.tipitaka.read', ['id' => $book['pagination']['next']['id'], 'channel' => request('channel')]) }}">
+                                <div class="row align-items-center">
+                                    <div class="col">
+                                        <div class="page-item-subtitle">下一篇</div>
+                                        <div class="page-item-title">{{ $book['pagination']['next']['title'] }}</div>
+                                    </div>
+                                    <div class="col-auto">
+                                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                                            <path d="M9 6l6 6l-6 6"></path>
+                                        </svg>
                                     </div>
-                                </a>
+                                </div>
+                            </a>
                         </li>
                         @endif
                     </ul>
                 </div>
 
-                <!-- Related Books -->
-                @if(!empty($relatedBooks))
-                <div class="related-books">
-                    <h3>Related Books</h3>
-                    <div class="row row-cards">
-                        @foreach ($relatedBooks as $relatedBook)
-                        <div class="col-md-4">
-                            <div class="card">
-                                <div class="card-img-container">
-                                    <img src="{{ $relatedBook['image'] }}" alt="{{ $relatedBook['title'] }}">
-                                </div>
-                                <div class="card-body">
-                                    <h5 class="card-title">{{ $relatedBook['title'] }}</h5>
-                                    <p class="card-text">{{ $relatedBook['description'] }}</p>
-                                    <a href="{{ $relatedBook['link'] }}" class="btn btn-primary">Read Now</a>
-                                </div>
-                            </div>
-                        </div>
-                        @endforeach
-                    </div>
-                </div>
-                @endif
-
             </div>
         </div>
 
-        <!-- Right Sidebar (Desktop) -->
-        <div class="right-sidebar card">
-            <div class="card-body">
-                @if(!empty($book['downloads']))
-                <h5>下载</h5>
-                <ul class="list-unstyled">
+        {{-- 右侧边栏 --}}
+        <div class="right-sidebar">
+
+            {{-- 版本卡片(desktop,wiki 侧边栏同款) --}}
+            @if(!empty($channels))
+            <div class="reader-channel-card">
+                <div class="reader-channel-title">版本</div>
+                <ul class="reader-channel-list">
+                    @foreach($channels as $channel)
+                    <li>
+                        <a href="{{ request()->fullUrlWithQuery(['channel' => $channel['id']]) }}"
+                            class="{{ request('channel') == $channel['id'] ? 'active' : '' }}">
+                            {{ $channel['name'] }}
+                            <span class="reader-channel-lang">{{ __('language.' . $channel['lang']) }}</span>
+                        </a>
+                    </li>
+                    @endforeach
+                </ul>
+            </div>
+            @endif
+
+            {{-- 下载 --}}
+            @if(!empty($book['downloads']))
+            <div class="reader-channel-card">
+                <div class="reader-channel-title">下载</div>
+                <ul class="list-unstyled mb-0">
                     @foreach ($book['downloads'] as $download)
                     <li>
                         <a href="{{ $download['url'] }}" class="btn btn-outline-primary mb-2 w-100">
-                            <i class="fas fa-download me-2"></i>{{ $download['format'] }}
+                            <i class="ti ti-download me-2"></i>{{ $download['format'] }}
                         </a>
                     </li>
                     @endforeach
                 </ul>
-                @endif
-
-                @if(!empty($book['categories']))
-                <h5>分类</h5>
-                @foreach ($book['categories'] as $category)
-                <span class="badge bg-blue text-blue-fg">{{ $category['name'] }}</span>
-                @endforeach
-                @endif
+            </div>
+            @endif
 
-                @if(!empty($book['tags']))
-                <h5>标签</h5>
+            {{-- 标签 --}}
+            @if(!empty($book['tags']))
+            <div class="reader-channel-card">
+                <div class="reader-channel-title">标签</div>
                 @foreach ($book['tags'] as $tag)
                 <span class="badge me-1">{{ $tag['name'] }}</span>
                 @endforeach
-                @endif
             </div>
+            @endif
+
         </div>
 
     </div>
-
-    <!-- Settings Modal -->
-    <div class="modal modal-blur fade" id="settingsModal" tabindex="-1">
-        <div class="modal-dialog">
-            <form id="settingsForm" class="modal-content">
-                <div class="modal-header">
-                    <h5 class="modal-title">阅读设置</h5>
-
-                    <button type="button"
-                        class="btn-close"
-                        data-bs-dismiss="modal"></button>
+</div>
+
+{{-- 阅读设置 Modal --}}
+<div class="modal modal-blur fade" id="settingsModal" tabindex="-1">
+    <div class="modal-dialog">
+        <form id="settingsForm" class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">阅读设置</h5>
+                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+            </div>
+            <div class="modal-body">
+                <div class="mb-4">
+                    <label class="form-label">显示原文</label>
+                    <label class="form-check form-switch">
+                        <input class="form-check-input" type="checkbox" id="showOrigin">
+                        <span class="form-check-label">开启/关闭原文显示</span>
+                    </label>
                 </div>
-
-                <div class="modal-body">
-
-                    {{-- 显示原文 --}}
-                    <div class="mb-4">
-                        <label class="form-label">显示原文</label>
-
-                        <label class="form-check form-switch">
-                            <input class="form-check-input"
-                                type="checkbox"
-                                id="showOrigin">
-
-                            <span class="form-check-label">
-                                开启/关闭原文显示
-                            </span>
-                        </label>
-                    </div>
-
-                    {{-- 界面语言 --}}
-                    <div class="mb-4">
-                        <label class="form-label">界面语言</label>
-
-                        <select class="form-select" id="uiLanguage">
-                            <option value="auto">自动</option>
-                            <option value="zh">简体中文</option>
-                            <option value="en">英文</option>
-                        </select>
-                    </div>
-
-                    {{-- 巴利文脚本 --}}
-                    <div class="mb-4">
-                        <label class="form-label">巴利文脚本</label>
-
-                        <select class="form-select" id="paliScript">
-                            <option value="auto">自动</option>
-                            <option value="roman">罗马</option>
-                            <option value="myanmar">缅文</option>
-                            <option value="thai">泰文</option>
-                        </select>
-                    </div>
-
+                <div class="mb-4">
+                    <label class="form-label">界面语言</label>
+                    <select class="form-select" id="uiLanguage">
+                        <option value="auto">自动</option>
+                        <option value="zh">简体中文</option>
+                        <option value="en">英文</option>
+                    </select>
                 </div>
-
-                <div class="modal-footer">
-
-                    <button type="button"
-                        class="btn btn-link"
-                        data-bs-dismiss="modal">
-                        取消
-                    </button>
-
-                    <button type="submit"
-                        class="btn btn-primary">
-                        确定
-                    </button>
-
+                <div class="mb-4">
+                    <label class="form-label">巴利文脚本</label>
+                    <select class="form-select" id="paliScript">
+                        <option value="auto">自动</option>
+                        <option value="roman">罗马</option>
+                        <option value="myanmar">缅文</option>
+                        <option value="thai">泰文</option>
+                    </select>
                 </div>
-            </form>
-        </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-link" data-bs-dismiss="modal">取消</button>
+                <button type="submit" class="btn btn-primary">确定</button>
+            </div>
+        </form>
     </div>
-
-    <script>
-        function toggleOriginDisplay(show) {
-            document.querySelectorAll('.origin').forEach(el => {
-                el.style.display = show ? 'unset' : 'none';
-            });
-        }
-
-        function setCookie(name, value, days = 365) {
-            let expires = "";
-            const date = new Date();
-
-            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
-
-            expires = "; expires=" + date.toUTCString();
-
-            document.cookie =
-                name + "=" + encodeURIComponent(value) +
-                expires +
-                "; path=/";
-        }
-
-        function getCookie(name) {
-            const value = `; ${document.cookie}`;
-
-            const parts = value.split(`; ${name}=`);
-
-            if (parts.length === 2)
-                return decodeURIComponent(parts.pop().split(';').shift());
-
-            return null;
-        }
-
-        // 初始化加载 cookie
-        document.addEventListener('DOMContentLoaded', function() {
-
-            const showOrigin =
-                getCookie('show_origin') === 'true';
-
-            document.getElementById('showOrigin').checked =
-                showOrigin;
-
-            document.getElementById('uiLanguage').value =
-                getCookie('ui_language') || 'auto';
-
-            document.getElementById('paliScript').value =
-                getCookie('pali_script') || 'auto';
-
-            // 应用原文显示设置
-            toggleOriginDisplay(showOrigin);
+</div>
+
+@endsection
+
+@push('scripts')
+<script>
+    // 夜间模式
+    document.getElementById('themeToggle').addEventListener('click', function(e) {
+        e.preventDefault();
+        const isDark = document.body.classList.contains('dark-mode');
+        document.body.classList.toggle('dark-mode', !isDark);
+        document.body.classList.toggle('light-mode', isDark);
+        this.innerHTML = isDark ?
+            '<i class="ti ti-moon"></i>' :
+            '<i class="ti ti-sun"></i>';
+        fetch('{{ route("theme.toggle") }}', {
+            method: 'POST',
+            headers: {
+                'X-CSRF-TOKEN': '{{ csrf_token() }}',
+                'Content-Type': 'application/json'
+            },
+            body: JSON.stringify({
+                theme: isDark ? 'light' : 'dark'
+            })
         });
-
-        // 提交保存
-        document.getElementById('settingsForm')
-            .addEventListener('submit', function(e) {
-
-                e.preventDefault();
-
-                setCookie(
-                    'show_origin',
-                    document.getElementById('showOrigin').checked
-                );
-
-                setCookie(
-                    'ui_language',
-                    document.getElementById('uiLanguage').value
-                );
-
-                setCookie(
-                    'pali_script',
-                    document.getElementById('paliScript').value
-                );
-                toggleOriginDisplay(showOrigin);
-                location.reload();
-            });
-    </script>
-
-    <script>
-        const themeToggle = document.getElementById('themeToggle');
-        themeToggle.addEventListener('click', (e) => {
-            e.preventDefault();
-            const isDark = document.body.classList.contains('dark-mode');
-            document.body.classList.toggle('dark-mode', !isDark);
-            document.body.classList.toggle('light-mode', isDark);
-            fetch('{{ route("theme.toggle") }}', {
-                method: 'POST',
-                headers: {
-                    'X-CSRF-TOKEN': '{{ csrf_token() }}',
-                    'Content-Type': 'application/json'
-                },
-                body: JSON.stringify({
-                    theme: isDark ? 'light' : 'dark'
-                })
-            });
-            themeToggle.innerHTML = isDark ? '<i class="fas fa-moon"></i>' : '<i class="fas fa-sun"></i>';
+    });
+
+    // 阅读设置
+    function getCookie(name) {
+        const value = `; ${document.cookie}`;
+        const parts = value.split(`; ${name}=`);
+        if (parts.length === 2) return decodeURIComponent(parts.pop().split(';').shift());
+        return null;
+    }
+
+    function setCookie(name, value, days = 365) {
+        const date = new Date();
+        date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
+        document.cookie = name + '=' + encodeURIComponent(value) + '; expires=' + date.toUTCString() + '; path=/';
+    }
+
+    function toggleOriginDisplay(show) {
+        document.querySelectorAll('.origin').forEach(el => {
+            el.style.display = show ? 'unset' : 'none';
         });
-    </script>
-
-</body>
-
-</html>
+    }
+
+    document.addEventListener('DOMContentLoaded', function() {
+        const showOrigin = getCookie('show_origin') === 'true';
+        document.getElementById('showOrigin').checked = showOrigin;
+        document.getElementById('uiLanguage').value = getCookie('ui_language') || 'auto';
+        document.getElementById('paliScript').value = getCookie('pali_script') || 'auto';
+        toggleOriginDisplay(showOrigin);
+    });
+
+    document.getElementById('settingsForm').addEventListener('submit', function(e) {
+        e.preventDefault();
+        setCookie('show_origin', document.getElementById('showOrigin').checked);
+        setCookie('ui_language', document.getElementById('uiLanguage').value);
+        setCookie('pali_script', document.getElementById('paliScript').value);
+        location.reload();
+    });
+</script>
+@endpush

+ 0 - 143
api-v12/resources/views/library/book/show.blade.php

@@ -1,143 +0,0 @@
-@extends('library.layouts.app')
-
-@section('title', $book['title'] . ' - 巴利书库')
-
-@section('content')
-<div class="page-body">
-    <style>
-        .line2 {
-            display: -webkit-box;
-            -webkit-line-clamp: 2;
-            /* 限制显示两行 */
-            -webkit-box-orient: vertical;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            /* 超出部分显示省略号 */
-        }
-    </style>
-    <div class="container-xl">
-        <div class="page-header d-print-none">
-            <div class="row align-items-center">
-                <div class="col">
-                    <nav aria-label="breadcrumb">
-                        <ol class="breadcrumb">
-                            <li class="breadcrumb-item"><a href="{{ route('library.home') }}">{{ __('labels.home') }}</a></li>
-                            <li class="breadcrumb-item active">{{ $book['title'] }}</li>
-                        </ol>
-                    </nav>
-                </div>
-            </div>
-        </div>
-
-        <div class="row">
-            <div class="col-md-4">
-                <div class="card">
-                    <img src="{{ $book['cover'] }}" class="card-img-top" alt="{{ $book['title'] }}" style="max-height: 400px; width: fit-content;">
-                    <div class="card-body text-center">
-                        <a href="{{ route('library.tipitaka.read', $book['id']) }}" class="btn btn-primary btn-lg w-100 mb-2">
-                            <svg class="icon me-2" width="24" height="24">
-                                <use xlink:href="#tabler-book-2"></use>
-                            </svg>
-                            {{ __('buttons.online-read') }}
-                        </a>
-                        <button class="btn btn-outline-secondary w-100">
-                            <svg class="icon me-2" width="24" height="24">
-                                <use xlink:href="#tabler-download"></use>
-                            </svg>
-                            下载
-                        </button>
-                    </div>
-                </div>
-            </div>
-
-            <div class="col-md-8">
-                <div class="card">
-                    <div class="card-header">
-                        <h3 class="card-title">{{ $book['title'] }}</h3>
-                    </div>
-                    <div class="card-body">
-                        <div class="row mb-3">
-                            <div class="col-sm-3"><strong>作者:</strong></div>
-                            <div class="col-sm-9">{{ $book['author'] }}</div>
-                        </div>
-                        <div class="row mb-3">
-                            <div class="col-sm-3"><strong>出版:</strong></div>
-                            <div class="col-sm-9">
-                                <a href="{{ route('blog.index', ['user' => $book['publisher']->username]) }}">
-                                    {{ $book['publisher']->nickname }}
-                                </a>
-                            </div>
-                        </div>
-                        <div class="row mb-3">
-                            <div class="col-sm-3"><strong>语言:</strong></div>
-                            <div class="col-sm-9">{{ $book['language'] ?? '巴利语' }}</div>
-                        </div>
-                        <div class="row mb-3">
-                            <div class="col-sm-3"><strong>简介:</strong></div>
-                            <div class="col-sm-9">{{ $book['description'] }}</div>
-                        </div>
-                    </div>
-                </div>
-
-                @if(isset($book['contents']) && count($book['contents']) > 0)
-                <div class="card mt-3">
-                    <div class="card-header">
-                        <h3 class="card-title">目录</h3>
-                    </div>
-                    <div class="card-body">
-                        <div class="list-group">
-                            @foreach($book['contents'] as $chapter)
-                            <a href="{{ route('library.tipitaka.read', $chapter['id']) }}?channel={{ $chapter['channel'] }}"
-                                class="list-group-item list-group-item-action">
-                                <div class="d-flex w-100 justify-content-between">
-                                    <h4 class="mb-1">{{ $chapter['title'] }}</h4>
-                                    <div class="d-flex" style="width:150px;">
-                                        @if($chapter['progress']>0)
-                                        <div class="progress">
-                                            <div class="progress-bar" style="width: {{ $chapter['progress'] }}%"></div>
-                                        </div>
-                                        @else
-                                        <small>无数据</small>
-                                        @endif
-
-                                    </div>
-
-                                </div>
-                                @if(isset($chapter['summary']))
-                                <p class="mb-1 text-muted line2">{{ $chapter['summary'] }}</p>
-                                @endif
-                            </a>
-                            @endforeach
-                        </div>
-                    </div>
-                </div>
-                @endif
-
-                @if(count($otherVersions) > 0)
-                <div class="card mt-3">
-                    <div class="card-header">
-                        <h3 class="card-title">其他版本</h3>
-                    </div>
-                    <div class="card-body">
-                        <div class="row">
-                            @foreach($otherVersions as $version)
-                            <div class="col-md-6 mb-3">
-                                <div class="d-flex">
-                                    <img src="{{ $version['cover'] }}" class="me-3" style="width: 60px; height: 80px; object-fit: cover;" alt="{{ $version['title'] }}">
-                                    <div>
-                                        <h6><a href="{{ route('library.tipitaka.show', $version['id']) }}">{{ $version['title'] }}</a></h6>
-                                        <div class="text-muted small">{{ $version['author'] }}</div>
-                                        <div class="text-muted small">{{ $version['language'] ?? '巴利语' }}</div>
-                                    </div>
-                                </div>
-                            </div>
-                            @endforeach
-                        </div>
-                    </div>
-                </div>
-                @endif
-            </div>
-        </div>
-    </div>
-</div>
-@endsection

+ 0 - 62
api-v12/resources/views/library/category.blade.php

@@ -1,62 +0,0 @@
-@extends('library.layouts.app')
-
-@section('title', $currentCategory['name'] . ' - 巴利书库')
-
-@section('breadcrumb')
-<li class="breadcrumb-item">
-    <a href="{{ route('library.home') }}">首页</a>
-</li>
-
-@foreach($breadcrumbs as $breadcrumb)
-@if($loop->last)
-<li class="breadcrumb-item active">{{ $breadcrumb['name'] }}</li>
-@else
-<li class="breadcrumb-item">
-    <a href="{{ route('library.tipitaka.category', ['id'=>$breadcrumb['id']]) }}">{{ $breadcrumb['name'] }}</a>
-</li>
-@endif
-@endforeach
-@endsection
-
-@section('content')
-<div class="page-body">
-    <div class="container-xl">
-        <div class="page-header d-print-none">
-            <div class="row align-items-center">
-                <div class="col">
-
-                    <h2 class="page-title">{{ $currentCategory['name'] }}</h2>
-                </div>
-            </div>
-        </div>
-
-        @if(count($subCategories) > 0)
-        <div class="row row-cards mb-4">
-            @foreach($subCategories as $subCategory)
-            <div class="col-sm-6 col-lg-3">
-                <div class="card">
-                    <div class="card-body text-center">
-                        <a href="{{ route('library.tipitaka.category', ['id'=>$subCategory['id']]) }}" class="btn btn-primary">
-                            {{ $subCategory['name'] }}
-                        </a>
-                    </div>
-                </div>
-            </div>
-            @endforeach
-        </div>
-        @endif
-
-
-
-        <div class="card">
-            <div class="card-header">
-                <h3 class="card-title">图书列表</h3>
-            </div>
-            <div class="card-body">
-                @include('components.book-list', ['books' => $categoryBooks])
-            </div>
-        </div>
-
-    </div>
-</div>
-@endsection

+ 34 - 64
api-v12/resources/views/library/index.blade.php

@@ -1,97 +1,67 @@
-{{-- api-v12/resources/views/library/index.blade.php --}}
-@extends('library.layouts.app')
+{{-- resources/views/library/index.blade.php
+     Library 门户首页。
+--}}
+@extends('layouts.library')
 
-@section('title', __('labels.home'))
+@section('title', '巴利书库')
 
 @section('hero')
-<section class="hero-section" style="background-image: url('{{ URL::asset("assets/images/hero-2.jpg") }}')">
-
+<section class="hero-section"
+         style="background-image: url('{{ URL::asset('assets/images/hero-2.jpg') }}')">
     <div class="hero-overlay"></div>
-
     <div class="hero-content">
         <h1 class="hero-title">巴利书库</h1>
-        <p class="hero-subtitle">探索wikipali,开启智慧之门</p>
-
+        <p class="hero-subtitle">探索 WikiPāli,开启智慧之门</p>
         <div class="search-box">
-            <div class="input-group">
-                <input
-                    type="text"
-                    class="form-control form-control-lg"
-                    placeholder="搜索图书、作者或主题...">
-
-                <button class="btn btn-primary btn-lg" type="button">
-                    <i class="ti ti-search"></i>
-                </button>
-            </div>
+            <x-ui.search-input
+                :action="route('library.search')"
+                placeholder="搜索图书、作者或主题…"
+                size="lg"
+            />
         </div>
     </div>
-
 </section>
 @endsection
 
-
 @section('content')
-
 <div class="page-body">
     <div class="container-xl">
-        <div class="page-header d-print-none">
-            <div class="row align-items-center">
-                <div class="col">
-                    <h2 class="page-title">巴利书库</h2>
-                    <div class="text-muted mt-1">探索古老的佛教经典</div>
-                </div>
-            </div>
-        </div>
 
-        {{-- ✅ 所有卡片的统一容器 --}}
-        <div class="row g-4">
+        {{-- 分类卡片 --}}
+        <div class="row g-4 mt-1">
             @foreach($categoryData as $data)
-            {{-- ✅ 响应式列 --}}
             <div class="col-12 col-md-6 col-lg-3">
-                <div class="card h-100">
-                    <div class="card-header">
-                        <h3 class="card-title">
-                            <svg class="icon me-2" width="24" height="24">
-                                <use xlink:href="#tabler-book"></use>
-                            </svg>
-                            {{ $data['category']['name'] }}
-                        </h3>
-                        <div class="card-actions">
-                            <a href="{{ route('library.tipitaka.category', ['id'=>$data['category']['id']]) }}"
-                                class="btn btn-primary btn-sm">
-                                {{ __('buttons.more') }}
-                                <svg class="icon ms-1" width="24" height="24">
-                                    <use xlink:href="#tabler-arrow-right"></use>
-                                </svg>
-                            </a>
-                        </div>
+                <div class="wiki-card h-100">
+                    <div class="wiki-sidebar-title" style="margin-bottom: 0.75rem;">
+                        <i class="ti ti-book me-1"></i>
+                        {{ $data['category']['name'] }}
+                        <a href="{{ route('library.tipitaka.category', ['id' => $data['category']['id']]) }}"
+                           class="btn btn-sm btn-primary ms-auto" style="float:right; font-size: 0.75rem;">
+                            更多 <i class="ti ti-arrow-right"></i>
+                        </a>
                     </div>
-
-                    <div class="card-body">
+                    <ul class="wiki-cat-list">
                         @foreach($data['children'] as $child)
-                        <div class="mb-1">
-                            <a href="{{ route('library.tipitaka.category', ['id'=>$child['id']]) }}">
+                        <li>
+                            <a href="{{ route('library.tipitaka.category', ['id' => $child['id']]) }}">
                                 {{ $child['name'] }}
                             </a>
-                        </div>
+                        </li>
                         @endforeach
-                    </div>
+                    </ul>
                 </div>
             </div>
             @endforeach
         </div>
+
+        {{-- 最近更新 --}}
         @isset($update)
-        <div class="card h-100">
-            <div class="card-header">
-                <h3 class="card-title">
-                    update
-                </h3>
-            </div>
-            <div class="card-body">
-                @include('components.book-list', ['books' => $update])
-            </div>
+        <div class="wiki-card mt-4">
+            <div class="wiki-sidebar-title" style="margin-bottom: 1rem;">最近更新</div>
+            <x-ui.book-grid :books="$update" />
         </div>
         @endisset
+
     </div>
 </div>
 @endsection

+ 96 - 0
api-v12/resources/views/library/tipitaka/category.blade.php

@@ -0,0 +1,96 @@
+{{-- resources/views/library/tipitaka/category.blade.php --}}
+@extends('layouts.library')
+
+@section('title', $currentCategory['name'] . ' · 巴利书库-重构')
+
+@push('styles')
+@vite(['resources/css/modules/_tipitaka.css','resources/css/modules/_wiki.css','resources/css/modules/_anthology.css'])
+@endpush
+
+@section('breadcrumb')
+<li class="breadcrumb-item">
+    <a href="{{ route('library.home') }}">首页</a>
+</li>
+<li class="breadcrumb-item">
+    <a href="{{ route('library.tipitaka.index') }}">三藏</a>
+</li>
+@foreach($breadcrumbs as $breadcrumb)
+@if($loop->last)
+<li class="breadcrumb-item active">{{ $breadcrumb['name'] }}</li>
+@else
+<li class="breadcrumb-item">
+    <a href="{{ route('library.tipitaka.category', ['id' => $breadcrumb['id']]) }}">
+        {{ $breadcrumb['name'] }}
+    </a>
+</li>
+@endif
+@endforeach
+@endsection
+
+@section('content')
+<div class="page-body">
+    <div class="container-xl wiki-layout">
+
+        {{-- 左侧边栏 --}}
+        <aside class="wiki-sidebar-left">
+            <div class="wiki-sidebar-section">
+                <div class="wiki-sidebar-title">分类导航</div>
+                <ul class="wiki-cat-list">
+                    <li>
+                        <a href="{{ route('library.tipitaka.index') }}">
+                            <i class="ti ti-home me-1"></i>三藏首页
+                        </a>
+                    </li>
+                    @foreach($breadcrumbs as $breadcrumb)
+                    <li>
+                        <a href="{{ route('library.tipitaka.category', ['id' => $breadcrumb['id']]) }}"
+                            class="{{ $loop->last ? 'active' : '' }}">
+                            {{ $breadcrumb['name'] }}
+                        </a>
+                    </li>
+                    @endforeach
+                </ul>
+            </div>
+        </aside>
+
+        {{-- 主内容区 --}}
+        <main class="wiki-main">
+
+            {{-- 子分类 --}}
+            @if(count($subCategories) > 0)
+            <div class="wiki-card">
+                <div class="wiki-sidebar-title" style="margin-bottom: 1rem;">子分类</div>
+                <div class="book-grid" style="grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));">
+                    @foreach($subCategories as $subCategory)
+                    <a href="{{ route('library.tipitaka.category', ['id' => $subCategory['id']]) }}"
+                        class="wiki-featured-card">
+                        <div class="wiki-featured-title">{{ $subCategory['name'] }}</div>
+                    </a>
+                    @endforeach
+                </div>
+            </div>
+            @endif
+
+            {{-- 书籍列表 --}}
+            <div class="wiki-card">
+                <div class="wiki-sidebar-title" style="margin-bottom: 1rem;">
+                    {{ $currentCategory['name'] }}
+                </div>
+                <x-ui.book-grid :books="$categoryBooks" />
+            </div>
+
+        </main>
+
+        {{-- 右侧边栏 --}}
+        <aside class="wiki-sidebar-right">
+            <div class="wiki-sidebar-section">
+                <div class="wiki-sidebar-title">当前分类</div>
+                <div style="font-size: 0.875rem; color: var(--tblr-body-color); font-weight: 500;">
+                    {{ $currentCategory['name'] }}
+                </div>
+            </div>
+        </aside>
+
+    </div>
+</div>
+@endsection

+ 162 - 0
api-v12/resources/views/library/tipitaka/show.blade.php

@@ -0,0 +1,162 @@
+{{-- resources/views/library/tipitaka/show.blade.php --}}
+@extends('layouts.library')
+
+@section('title', $book['title'] . ' · 巴利书库')
+
+@push('styles')
+    @vite('resources/css/modules/_tipitaka.css')
+@endpush
+
+@section('breadcrumb')
+<li class="breadcrumb-item">
+    <a href="{{ route('library.home') }}">首页</a>
+</li>
+<li class="breadcrumb-item">
+    <a href="{{ route('library.tipitaka.index') }}">三藏</a>
+</li>
+<li class="breadcrumb-item active">{{ $book['title'] }}</li>
+@endsection
+
+@section('content')
+<div class="page-body">
+    <div class="container-xl wiki-layout">
+
+        {{-- 左侧边栏:封面 + 操作按钮 --}}
+        <aside class="wiki-sidebar-left">
+            <div class="wiki-sidebar-section" style="padding: 1rem;">
+
+                {{-- 封面 --}}
+                <x-ui.book-cover
+                    :image="$book['cover'] ?? null"
+                    :gradient="$book['cover_gradient'] ?? 'linear-gradient(135deg, #2d2010 0%, #1a1208 100%)'"
+                    :title="$book['title']"
+                    size="md"
+                    :style3d="false"
+                    style="width: 100%; min-width: unset; height: 220px;"
+                />
+
+                {{-- 操作按钮 --}}
+                <div style="margin-top: 1rem;">
+                    <a href="{{ route('library.tipitaka.read', $book['id']) }}"
+                       class="btn btn-primary w-100 mb-2">
+                        <i class="ti ti-book-2 me-1"></i>
+                        在线阅读
+                    </a>
+                    <button class="btn btn-outline-secondary w-100">
+                        <i class="ti ti-download me-1"></i>
+                        下载
+                    </button>
+                </div>
+
+            </div>
+        </aside>
+
+        {{-- 主内容区 --}}
+        <main class="wiki-main">
+
+            {{-- 书籍信息 --}}
+            <div class="wiki-card">
+                <div class="wiki-entry-header">
+                    <div class="wiki-entry-title">{{ $book['title'] }}</div>
+                </div>
+
+                <table class="wiki-meta-table" style="margin-bottom: 1.25rem;">
+                    <tr>
+                        <td>作者</td>
+                        <td>{{ $book['author'] }}</td>
+                    </tr>
+                    @if(isset($book['publisher']))
+                    <tr>
+                        <td>出版</td>
+                        <td>
+                            <a href="{{ route('blog.index', ['user' => $book['publisher']->username]) }}"
+                               style="color: var(--tblr-primary); text-decoration: none;">
+                                {{ $book['publisher']->nickname }}
+                            </a>
+                        </td>
+                    </tr>
+                    @endif
+                    <tr>
+                        <td>语言</td>
+                        <td>{{ $book['language'] ?? '巴利语' }}</td>
+                    </tr>
+                </table>
+
+                @if(!empty($book['description']))
+                <div class="wiki-content-body">
+                    <p>{{ $book['description'] }}</p>
+                </div>
+                @endif
+            </div>
+
+            {{-- 目录 --}}
+            @if(isset($book['contents']) && count($book['contents']) > 0)
+            <div class="wiki-card">
+                <div class="wiki-sidebar-title" style="margin-bottom: 1rem;">目录</div>
+                <ul class="wiki-toc-list">
+                    @foreach($book['contents'] as $chapter)
+                    <li>
+                        <a href="{{ route('library.tipitaka.read', $chapter['id']) }}?channel={{ $chapter['channel'] }}">
+                            <span class="wiki-toc-num">{{ $loop->index + 1 }}</span>
+                            {{ $chapter['title'] }}
+                            @if(isset($chapter['summary']))
+                            <span style="font-size: 0.75rem; color: var(--tblr-secondary); margin-left: 0.5rem;" class="line2">
+                                {{ $chapter['summary'] }}
+                            </span>
+                            @endif
+                            @if(isset($chapter['progress']) && $chapter['progress'] > 0)
+                            <span style="margin-left: auto; font-size: 0.75rem; color: var(--tblr-secondary);">
+                                {{ $chapter['progress'] }}%
+                            </span>
+                            @endif
+                        </a>
+                    </li>
+                    @endforeach
+                </ul>
+            </div>
+            @endif
+
+        </main>
+
+        {{-- 右侧边栏 --}}
+        <aside class="wiki-sidebar-right">
+
+            {{-- 书籍元信息 --}}
+            <div class="wiki-sidebar-section">
+                <div class="wiki-sidebar-title">书籍信息</div>
+                <table class="wiki-meta-table">
+                    <tr>
+                        <td>语言</td>
+                        <td>{{ $book['language'] ?? '巴利语' }}</td>
+                    </tr>
+                    @if(!empty($book['type']))
+                    <tr>
+                        <td>类型</td>
+                        <td>{{ $book['type'] }}</td>
+                    </tr>
+                    @endif
+                </table>
+            </div>
+
+            {{-- 其他版本 --}}
+            @if(!empty($otherVersions) && count($otherVersions) > 0)
+            <div class="wiki-sidebar-section">
+                <div class="wiki-sidebar-title">其他版本</div>
+                <ul class="wiki-related-list">
+                    @foreach($otherVersions as $version)
+                    <li>
+                        <a href="{{ route('library.tipitaka.show', $version['id']) }}">
+                            {{ $version['title'] }}
+                            <span class="wiki-related-zh">{{ $version['language'] ?? '巴利语' }}</span>
+                        </a>
+                    </li>
+                    @endforeach
+                </ul>
+            </div>
+            @endif
+
+        </aside>
+
+    </div>
+</div>
+@endsection