Browse Source

style(api-v13): 阅读页正文段后间距改为 1em

visuddhinanda 5 days ago
parent
commit
de3d663c6b

+ 6 - 0
api-v13/app/Http/Resources/SearchPaliWbwResource.php

@@ -31,10 +31,16 @@ class SearchPaliWbwResource extends JsonResource
             $data['path'] = json_decode($paliText->path);
             if ($paliText->level < 100) {
                 $data['paliTitle'] = $paliText->toc;
+                $book = $this->book;
+                $para = $this->paragraph;
+                $data['link'] = config('app.url')."/library/tipitaka/{$book}-{$para}/read";
             } else {
                 $data['paliTitle'] = PaliText::where('book', $this->book)
                     ->where('paragraph', $paliText->parent)
                     ->value('toc');
+                $book = end($data['path'])['book'];
+                $para = end($data['path'])['paragraph'];
+                $data['link'] = config('app.url')."/library/tipitaka/{$book}-{$para}/read#{$this->paragraph}";
             }
             $keyWords = explode(',', $request->input('key'));
             $keyWordsUpper = $keyWords;

+ 4 - 4
api-v13/app/Services/PaliContentService.php

@@ -257,7 +257,7 @@ class PaliContentService
                          */
                         $newSent['forkAt'] = $row->fork_at; //
                         $newSent['updateAt'] = $row->updated_at; //
-                        $newSent['updateAt'] = date('Y-m-d H:i:s.', $row->modify_time / 1000) . ($row->modify_time % 1000) . ' UTC';
+                        $newSent['updateAt'] = date('Y-m-d H:i:s.', $row->modify_time / 1000).($row->modify_time % 1000).' UTC';
 
                         $newSent['createdAt'] = $row->created_at;
                         if ($mode !== 'read') {
@@ -421,7 +421,7 @@ class PaliContentService
             $wbw = str_replace('&nbsp;', ' ', $wbwrow->data);
             $wbw = str_replace('<br>', ' ', $wbw);
 
-            $xmlString = '<root>' . $wbw . '</root>';
+            $xmlString = '<root>'.$wbw.'</root>';
             try {
                 $xmlWord = simplexml_load_string($xmlString);
             } catch (\Exception $e) {
@@ -560,7 +560,7 @@ class PaliContentService
             $channelInfo = Channel::where('uid', $channelId)
                 ->select(['uid', 'type', 'name', 'lang', 'owner_uid'])->first();
             if (! $channelInfo) {
-                Log::error('no channel id' . $channelId);
+                Log::error('no channel id'.$channelId);
 
                 continue;
             }
@@ -677,7 +677,7 @@ class PaliContentService
             // html 格式加段落外壳
             $content = implode('', $cached['display']);
             $inner = $level > 0 ? "<h{$level}>{$content}</h{$level}>" : "<div class='para-block'>{$content}</div>";
-            $result['display'] = "<div class='{$cached['area']}' data-para='{$para}'>{$inner}</div>";
+            $result['display'] = "<div id='para-{$para}' class='{$cached['area']}' data-para='{$para}'>{$inner}</div>";
         } else {
             // 其他格式一行一句
             $result['display'] = implode("\n", $cached['display']);

+ 2 - 2
api-v13/resources/css/modules/reader-content.css

@@ -18,8 +18,8 @@ article.reader-body {
 article.reader-body div.sentence {
     display: inline;
 }
-article.reader-body div.translation .para-block {
-    margin-bottom: 0.75em;
+article.reader-body .para-block {
+    margin-bottom: 1em;
 }
 /* ── 段落 ── */
 article.reader-body p {

+ 15 - 0
api-v13/resources/css/modules/reader.css

@@ -349,3 +349,18 @@ body {
     color: var(--tblr-secondary);
     display: block;
 }
+
+/* ══════════════════════════════════════════
+   九、锚点段落高亮(hash 跳转定位)
+   柔和琥珀色,浅色/暗色模式各一套,避免过亮
+   ══════════════════════════════════════════ */
+
+.reader-para-anchor {
+    background-color: rgba(255, 213, 79, 0.18);
+    border-radius: 6px;
+    transition: background-color 0.3s ease;
+}
+
+.dark-mode .reader-para-anchor {
+    background-color: rgba(255, 213, 79, 0.14);
+}

+ 38 - 0
api-v13/resources/js/modules/reader.js

@@ -4,6 +4,44 @@ export function initReader() {
     injectCommentaryMarkers();
     injectEvaluateMarkers();
     initTocToggle();
+    initHashScroll();
+}
+
+// 处理 URL hash(#354 / #94-354 / #para-354):滚动到对应段落并加高亮底色
+function initHashScroll() {
+    const scrollToParagraph = (paragraph, smooth = false) => {
+        const el = document.getElementById(`para-${paragraph}`);
+        if (!el) {
+            return;
+        }
+
+        document
+            .querySelectorAll('.reader-para-anchor')
+            .forEach((node) => node.classList.remove('reader-para-anchor'));
+        el.classList.add('reader-para-anchor');
+        el.scrollIntoView({ block: 'start', behavior: smooth ? 'smooth' : 'auto' });
+    };
+
+    // 统一解析为段落号:取最后一个 '-' 之后的数字,兼容 #354 与 #94-354
+    const parseHash = (hash) => {
+        if (!hash || hash === '#') {
+            return null;
+        }
+        const paragraph = parseInt(hash.replace(/^#/, '').split('-').pop(), 10);
+        return Number.isInteger(paragraph) && paragraph > 0 ? paragraph : null;
+    };
+
+    window.addEventListener('hashchange', () => {
+        const paragraph = parseHash(window.location.hash);
+        if (paragraph !== null) {
+            scrollToParagraph(paragraph, true);
+        }
+    });
+
+    const paragraph = parseHash(window.location.hash);
+    if (paragraph !== null) {
+        scrollToParagraph(paragraph);
+    }
 }
 
 // TOC 折叠/展开:点击按钮切换所在 .toc-tree 的 .toc-expanded 类

+ 1 - 1
api-v13/tests/Feature/TipitakaReadParaTest.php

@@ -32,7 +32,7 @@ it('renders every sentence of a paragraph wrapped in divs', function () {
     // 默认只输出 display
     expect($data)->not->toHaveKey('sentences');
     expect($data['display'])
-        ->toContain("<div class='translation' data-para='1'>")
+        ->toContain("<div id='para-1' class='translation' data-para='1'>")
         ->toContain("<div class='sentence' data-sid='9001-1-1-1'>")
         ->toContain("<div class='sentence' data-sid='9001-1-2-2'>")
         ->toContain("<div class='para-block'>");