visuddhinanda il y a 1 semaine
Parent
commit
9565536ed6

+ 33 - 47
api-v12/app/Http/Controllers/ChapterContentController.php

@@ -54,9 +54,7 @@ class ChapterContentController extends Controller
         'created_at',
         'created_at',
         'updated_at',
         'updated_at',
     ];
     ];
-    protected $wbwChannels = [];
-    protected $debug = [];
-    protected $userUuid = null;
+
 
 
     /**
     /**
      * Display a listing of the resource.
      * Display a listing of the resource.
@@ -88,16 +86,16 @@ class ChapterContentController extends Controller
      */
      */
     public function show(Request $request, string $id)
     public function show(Request $request, string $id)
     {
     {
-        $paliService = app(PaliContentService::class);
-        if ($request->has('debug')) {
-            $this->debug = explode(',', $request->get('debug'));
-        }
+        /**
         $user = AuthApi::current($request);
         $user = AuthApi::current($request);
         if ($user) {
         if ($user) {
             $this->userUuid = $user['user_uid'];
             $this->userUuid = $user['user_uid'];
         }
         }
+         */
         //
         //
-        $sentId = \explode('-', $id);
+
+
+
         $channels = [];
         $channels = [];
         if ($request->has('channels')) {
         if ($request->has('channels')) {
             if (strpos($request->get('channels'), ',') === FALSE) {
             if (strpos($request->get('channels'), ',') === FALSE) {
@@ -124,13 +122,30 @@ class ChapterContentController extends Controller
         if ($channelId !== false) {
         if ($channelId !== false) {
             $channels[] = $channelId;
             $channels[] = $channelId;
         }
         }
+        #获取channel索引表
+        $tranChannels = [];
+        $channelInfo = Channel::whereIn("uid", $channels)
+            ->select(['uid', 'type', 'lang', 'name'])->get();
+        foreach ($channelInfo as $key => $value) {
+            # code...
+            if ($value->type === "translation") {
+                $tranChannels[] = $value->uid;
+            }
+        }
+        $indexChannel = [];
+        $paliService = app(PaliContentService::class);
+        $indexChannel = $paliService->getChannelIndex($channels);
+        // the end of channel
+
 
 
+
+        //chapter info
+        $sentId = \explode('-', $id);
         $chapter = PaliText::where('book', $sentId[0])->where('paragraph', $sentId[1])->first();
         $chapter = PaliText::where('book', $sentId[0])->where('paragraph', $sentId[1])->first();
         if (!$chapter) {
         if (!$chapter) {
             return $this->error("no data");
             return $this->error("no data");
         }
         }
-        $paraFrom = $sentId[1];
-        $paraTo = $sentId[1] + $chapter->chapter_len - 1;
+
 
 
         if (empty($chapter->toc)) {
         if (empty($chapter->toc)) {
             $this->result['title'] = "unknown";
             $this->result['title'] = "unknown";
@@ -140,43 +155,7 @@ class ChapterContentController extends Controller
             $this->result['path'] = json_decode($chapter->path);
             $this->result['path'] = json_decode($chapter->path);
         }
         }
 
 
-        //获取标题
-        $heading = PaliText::select(["book", "paragraph", "level"])
-            ->where('book', $sentId[0])
-            ->whereBetween('paragraph', [$paraFrom, $paraTo])
-            ->where('level', '<', 8)
-            ->get();
-        //将标题段落转成索引数组 以便输出标题层级
-        $indexedHeading = [];
-        foreach ($heading as $key => $value) {
-            # code...
-            $indexedHeading["{$value->book}-{$value->paragraph}"] = $value->level;
-        }
-        #获取channel索引表
-        $tranChannels = [];
-        $channelInfo = Channel::whereIn("uid", $channels)
-            ->select(['uid', 'type', 'lang', 'name'])->get();
-        foreach ($channelInfo as $key => $value) {
-            # code...
-            if ($value->type === "translation") {
-                $tranChannels[] = $value->uid;
-            }
-        }
-        $indexChannel = [];
-        $indexChannel = $paliService->getChannelIndex($channels);
-        //获取wbw channel
-        //目前默认的 wbw channel 是第一个translation channel
-        //TODO 处理不存在的channel id
-        foreach ($channels as $key => $value) {
-            # code...
-            if (
-                isset($indexChannel[$value]) &&
-                $indexChannel[$value]->type === 'translation'
-            ) {
-                $this->wbwChannels[] = $value;
-                break;
-            }
-        }
+
         $title = Sentence::select($this->selectCol)
         $title = Sentence::select($this->selectCol)
             ->where('book_id', $sentId[0])
             ->where('book_id', $sentId[0])
             ->where('paragraph', $sentId[1])
             ->where('paragraph', $sentId[1])
@@ -196,12 +175,16 @@ class ChapterContentController extends Controller
          * 3. 如果二者都不是,lazy load
          * 3. 如果二者都不是,lazy load
          */
          */
         //1. 计算 标题和下一级第一个标题之间 是否有间隔
         //1. 计算 标题和下一级第一个标题之间 是否有间隔
+
+        $paraFrom = $sentId[1];
+        $paraTo = $sentId[1] + $chapter->chapter_len - 1;
         $nextChapter =  PaliText::where('book', $sentId[0])
         $nextChapter =  PaliText::where('book', $sentId[0])
             ->where('paragraph', ">", $sentId[1])
             ->where('paragraph', ">", $sentId[1])
             ->where('level', '<', 8)
             ->where('level', '<', 8)
             ->orderBy('paragraph')
             ->orderBy('paragraph')
             ->value('paragraph');
             ->value('paragraph');
         $between = $nextChapter - $sentId[1];
         $between = $nextChapter - $sentId[1];
+
         //查找子目录
         //查找子目录
         $chapterLen = $chapter->chapter_len;
         $chapterLen = $chapter->chapter_len;
         $toc = PaliText::where('book', $sentId[0])
         $toc = PaliText::where('book', $sentId[0])
@@ -246,6 +229,9 @@ class ChapterContentController extends Controller
                 break;
                 break;
             }
             }
         }
         }
+
+
+        //content
         $record = Sentence::select($this->selectCol)
         $record = Sentence::select($this->selectCol)
             ->where('book_id', $sentId[0])
             ->where('book_id', $sentId[0])
             ->whereBetween('paragraph', [$pFrom, $currTo])
             ->whereBetween('paragraph', [$pFrom, $currTo])

+ 256 - 0
api-v12/app/Services/ChapterService.php

@@ -0,0 +1,256 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\Sentence;
+use App\Models\Channel;
+use App\Models\PaliText;
+
+use Illuminate\Support\Str;
+use App\Http\Api\MdRender;
+use App\Http\Api\ChannelApi;
+use App\Http\Api\StudioApi;
+
+
+use App\Http\Api\AuthApi;
+use App\Http\Resources\TocResource;
+use App\Services\PaliContentService;
+use Illuminate\Support\Facades\Log;
+
+
+class ChapterService
+{
+    protected $result = [
+        "uid" => '',
+        "title" => '',
+        "path" => [],
+        "sub_title" => '',
+        "summary" => '',
+        "content" => '',
+        "content_type" => "html",
+        "toc" => [],
+        "status" => 30,
+        "lang" => "",
+        "created_at" => "",
+        "updated_at" => "",
+    ];
+
+    protected $selectCol = [
+        'uid',
+        'book_id',
+        'paragraph',
+        'word_start',
+        "word_end",
+        'channel_uid',
+        'content',
+        'content_type',
+        'editor_uid',
+        'acceptor_uid',
+        'pr_edit_at',
+        'fork_at',
+        'create_time',
+        'modify_time',
+        'created_at',
+        'updated_at',
+    ];
+
+    private $MaxStrLen = 3000;
+    public function setMaxSize(int $size)
+    {
+        $this->MaxStrLen = $size;
+    }
+    public function chapterWithContent(string $id)
+    {
+        //getChannels
+
+        //chapter info
+
+        //chapter rang
+
+        //chapter content
+    }
+    public function paraWithContent(string $id) {}
+
+    public function csParaWithContent(string $id) {}
+
+    public function paraContent(int $book, int $from, int $to) {}
+    private function currChapter() {}
+
+    private function getChannels(array|null $input, string $mode)
+    {
+        $channels = [];
+        if (is_array($input)) {
+            foreach ($input as  $channel) {
+                if (Str::isUuid($channel)) {
+                    $channels[] = $channel;
+                }
+            }
+        }
+
+        if ($mode === 'read') {
+            //阅读模式加载html格式原文
+            $channelId = ChannelApi::getSysChannel('_System_Pali_VRI_');
+        } else {
+            //翻译模式加载json格式原文
+            $channelId = ChannelApi::getSysChannel('_System_Wbw_VRI_');
+        }
+
+        if ($channelId !== false) {
+            $channels[] = $channelId;
+        }
+        #获取channel索引表
+        $tranChannels = [];
+        $channelInfo = Channel::whereIn("uid", $channels)
+            ->select(['uid', 'type', 'lang', 'name'])->get();
+        foreach ($channelInfo as $key => $value) {
+            # code...
+            if ($value->type === "translation") {
+                $tranChannels[] = $value->uid;
+            }
+        }
+        $indexChannel = [];
+        $paliService = app(PaliContentService::class);
+        $indexChannel = $paliService->getChannelIndex($channels);
+        return [
+            'channels' => $channels,
+            'index' => $indexChannel,
+            'translations' => $tranChannels,
+            'request' => $input,
+        ];
+    }
+    private function chapterInfo(int $book, int $para, array $channelInfo)
+    {
+        $result = [];
+        $chapter = PaliText::where('book', $book)->where('paragraph', $para)->first();
+        if (!$chapter) {
+            //FIXME throw
+        }
+
+        if (empty($chapter->toc)) {
+            $this->result['title'] = "unknown";
+        } else {
+            $result['title'] = $chapter->toc;
+            $result['sub_title'] = $chapter->toc;
+            $result['path'] = json_decode($chapter->path);
+        }
+
+
+        $title = Sentence::select($this->selectCol)
+            ->where('book_id', $book)
+            ->where('paragraph', $para)
+            ->whereIn('channel_uid', $channelInfo['translation'])
+            ->first();
+        if ($title) {
+            $result['title'] = MdRender::render($title->content, [$title->channel_uid]);
+            $mdRender = new MdRender(['format' => 'simple']);
+            $result['title_text'] = $mdRender->convert($title->content, [$title->channel_uid]);
+        }
+
+        return $result;
+    }
+
+    private function chapterContentRang($book, $para, $from, $to)
+    {
+        /**
+         * 获取句子数据
+         * 算法:
+         * 1. 如果标题和下一级第一个标题之间有段落。只输出这些段落和子目录
+         * 2. 如果标题和下一级第一个标题之间没有间隔 且 chapter 长度大于10000个字符 且有子目录,只输出子目录
+         * 3. 如果二者都不是,lazy load
+         */
+        //1. 计算 标题和下一级第一个标题之间 是否有间隔
+        $chapter = PaliText::where('book', $book)->where('paragraph', $para)->first();
+
+        $paraFrom = $para;
+        $paraTo = $para + $chapter->chapter_len - 1;
+        $nextChapter =  PaliText::where('book', $book)
+            ->where('paragraph', ">", $para)
+            ->where('level', '<', 8)
+            ->orderBy('paragraph')
+            ->value('paragraph');
+        $between = $nextChapter - $para;
+
+        //查找子目录
+        $chapterLen = $chapter->chapter_len;
+        $toc = PaliText::where('book', $book)
+            ->whereBetween('paragraph', [$paraFrom + 1, $paraFrom + $chapterLen - 1])
+            ->where('level', '<', 8)
+            ->orderBy('paragraph')
+            ->select(['book', 'paragraph', 'level', 'toc'])
+            ->get();
+
+        if ($between > 1) {
+            //有间隔
+            $paraTo = $nextChapter - 1;
+        } else {
+            if ($chapter->chapter_strlen > $this->MaxStrLen) {
+                if (count($toc) > 0) {
+                    //有子目录只输出标题和目录
+                    $paraTo = $paraFrom;
+                } else {
+                    //没有子目录 全部输出
+                }
+            } else {
+                //章节小。全部输出 不输出子目录
+                $toc = [];
+            }
+        }
+
+        $pFrom = $from ?? $paraFrom;
+        $pTo = $to ?? $paraTo;
+        //根据句子的长度找到这次应该加载的段落
+
+        $paliText = PaliText::select(['paragraph', 'lenght'])
+            ->where('book', $book)
+            ->whereBetween('paragraph', [$pFrom, $pTo])
+            ->orderBy('paragraph')
+            ->get();
+        $sumLen = 0;
+        $currTo = $pTo;
+        foreach ($paliText as $para) {
+            $sumLen += $para->lenght;
+            if ($sumLen > $this->MaxStrLen) {
+                $currTo = $para->paragraph;
+                break;
+            }
+        }
+        return ['toc' => $toc, 'from' => $pFrom, 'to' => $currTo];
+    }
+
+    private function subChapterToc($toc)
+    {
+        //第一次才显示toc
+        return TocResource::collection($toc);
+    }
+
+    private function chapterContent($book, $para, $rang, $channelInfo, $mode)
+    {
+        $result = [];
+        $paliService = app(PaliContentService::class);
+        $record = Sentence::select($this->selectCol)
+            ->where('book_id', $book)
+            ->whereBetween('paragraph', [$rang['from'], $rang['to']])
+            ->whereIn('channel_uid', $channelInfo['channels'])
+            ->orderBy('paragraph')
+            ->orderBy('word_start')
+            ->get();
+        if (count($record) === 0) {
+            Log::warning('no data');
+        }
+        $result['content'] = json_encode($paliService->makeContentObj(
+            $record,
+            $mode,
+            $channelInfo['index']
+        ), JSON_UNESCAPED_UNICODE);
+        $result['content_type'] = 'json';
+
+        if ($rang['to'] < $para) {
+            $result['from'] = $rang['to'] + 1;
+            $result['to'] = $para;
+            $result['paraId'] = "{$book}-{$para}";
+            $result['channels'] = implode(',', $channelInfo['request']);
+            $result['mode'] =  $mode;
+        }
+        return $result;
+    }
+}

+ 2 - 2
dashboard-v6/documents/development/v6-todo-list.md

@@ -25,11 +25,11 @@
 #### 内容模块
 #### 内容模块
 
 
 - [ ] `/course/list`=>`workgroup/course` [2]
 - [ ] `/course/list`=>`workgroup/course` [2]
-- [ ] `/dict/list`=>`resources/dict` [1]
+- [x] `/dict/list`=>`resources/dict` [1]
 - [x] `/term/list`=>`workgroup/term`
 - [x] `/term/list`=>`workgroup/term`
 - [x] `/article/list`=>`workgroup/article`
 - [x] `/article/list`=>`workgroup/article`
 - [x] `/anthology/list`=>`workgroup/anthology`
 - [x] `/anthology/list`=>`workgroup/anthology`
-- [ ] `/attachment/list`=>`resources/attachment`
+- [x] `/attachment/list`=>`resources/attachment`
 - [x] `/tags/list`=>`resources/tags` [3]
 - [x] `/tags/list`=>`resources/tags` [3]
 
 
 ---
 ---

+ 4 - 0
dashboard-v6/src/Router.tsx

@@ -15,6 +15,8 @@ import teamRoutes from "./routes/teamRoutes";
 import inviteRoutes from "./routes/inviteRoutes";
 import inviteRoutes from "./routes/inviteRoutes";
 import transferRoutes from "./routes/transferRoutes";
 import transferRoutes from "./routes/transferRoutes";
 import tagRoutes from "./routes/tagRoutes";
 import tagRoutes from "./routes/tagRoutes";
+import driverRoutes from "./routes/driverRoutes";
+import dictRoutes from "./routes/dictRoutes";
 
 
 const RootLayout = lazy(() => import("./layouts/Root"));
 const RootLayout = lazy(() => import("./layouts/Root"));
 const AnonymousLayout = lazy(() => import("./layouts/anonymous"));
 const AnonymousLayout = lazy(() => import("./layouts/anonymous"));
@@ -92,6 +94,8 @@ const router = createBrowserRouter(
             ...inviteRoutes,
             ...inviteRoutes,
             ...transferRoutes,
             ...transferRoutes,
             ...tagRoutes,
             ...tagRoutes,
+            ...driverRoutes,
+            ...dictRoutes,
           ],
           ],
         },
         },
 
 

+ 1 - 1
dashboard-v6/src/components/attachment/AttachmentList.tsx

@@ -407,7 +407,7 @@ const AttachmentWidget = ({
             ((params.current ? params.current : 1) - 1) *
             ((params.current ? params.current : 1) - 1) *
             (params.pageSize ? params.pageSize : 20);
             (params.pageSize ? params.pageSize : 20);
 
 
-          let url = "/v2/attachment?";
+          let url = "/api/v2/attachment?";
           switch (view) {
           switch (view) {
             case "studio":
             case "studio":
               url += `view=studio&studio=${currStudio}`;
               url += `view=studio&studio=${currStudio}`;

+ 18 - 34
dashboard-v6/src/components/dict/UserDictList.tsx

@@ -134,8 +134,9 @@ const UserDictListWidget = ({
     <>
     <>
       <ProList<IWord, IParams>
       <ProList<IWord, IParams>
         actionRef={ref}
         actionRef={ref}
-        metas={{
-          title: {
+        columns={[
+          {
+            listSlot: "title",
             dataIndex: "word",
             dataIndex: "word",
             title: "拼写",
             title: "拼写",
             search: word ? false : undefined,
             search: word ? false : undefined,
@@ -165,7 +166,9 @@ const UserDictListWidget = ({
               );
               );
             },
             },
           },
           },
-          subTitle: {
+          {
+            dataIndex: "subTitle",
+            listSlot: "subTitle",
             search: false,
             search: false,
             render: (_text, row) => {
             render: (_text, row) => {
               return (
               return (
@@ -203,8 +206,8 @@ const UserDictListWidget = ({
               );
               );
             },
             },
           },
           },
-          description: {
-            dataIndex: "meaning",
+          {
+            listSlot: "description",
             title: "description",
             title: "description",
             search: false,
             search: false,
             render(_dom, entity) {
             render(_dom, entity) {
@@ -232,37 +235,18 @@ const UserDictListWidget = ({
               );
               );
             },
             },
           },
           },
-          content: compact
-            ? undefined
-            : {
-                search: false,
-                render(_dom, entity) {
-                  return (
-                    <div>
-                      <div>{entity.factors}</div>
-                    </div>
-                  );
-                },
-              },
-        }}
-        columns={[
           {
           {
-            title: intl.formatMessage({
-              id: "dict.fields.sn.label",
-            }),
-            dataIndex: "sn",
-            key: "sn",
-            width: 80,
+            listSlot: "content",
             search: false,
             search: false,
-          },
-          {
-            title: intl.formatMessage({
-              id: "dict.fields.word.label",
-            }),
-            dataIndex: "word",
-            key: "word",
-            tooltip: "单词过长会自动收缩",
-            ellipsis: true,
+            render(_dom, entity) {
+              return (
+                compact && (
+                  <div>
+                    <div>{entity.factors}</div>
+                  </div>
+                )
+              );
+            },
           },
           },
           {
           {
             title: intl.formatMessage({
             title: intl.formatMessage({

+ 3 - 3
dashboard-v6/src/components/navigation/MainMenu.tsx

@@ -252,9 +252,9 @@ const Widget = ({ onSearch }: Props) => {
           activeId: "workspace.tag",
           activeId: "workspace.tag",
         },
         },
         {
         {
-          key: "/workspace/drive",
-          label: "drive",
-          activeId: "workspace.drive",
+          key: "/workspace/driver",
+          label: "driver",
+          activeId: "workspace.driver",
         },
         },
         {
         {
           key: "/workspace/dict",
           key: "/workspace/dict",

+ 16 - 0
dashboard-v6/src/pages/workspace/dict/list.tsx

@@ -0,0 +1,16 @@
+import UserDictList from "../../../components/dict/UserDictList";
+import { useAppSelector } from "../../../hooks";
+import { currentUser } from "../../../reducers/current-user";
+
+const Widget = () => {
+  const user = useAppSelector(currentUser);
+  const studioName = user?.realName;
+  return (
+    <div>
+      <title>dict</title>
+      <UserDictList studioName={studioName} />
+    </div>
+  );
+};
+
+export default Widget;

+ 17 - 0
dashboard-v6/src/pages/workspace/driver/list.tsx

@@ -0,0 +1,17 @@
+import AttachmentList from "../../../components/attachment/AttachmentList";
+import { useAppSelector } from "../../../hooks";
+import { currentUser } from "../../../reducers/current-user";
+
+const Widget = () => {
+  const user = useAppSelector(currentUser);
+  const studioName = user?.realName;
+
+  return (
+    <div>
+      <title>driver</title>
+      <AttachmentList studioName={studioName} />
+    </div>
+  );
+};
+
+export default Widget;

+ 20 - 0
dashboard-v6/src/routes/dictRoutes.ts

@@ -0,0 +1,20 @@
+// src/routes/channelRoutes.ts
+import { lazy } from "react";
+import type { RouteObject } from "react-router";
+
+const WorkspaceDict = lazy(() => import("../pages/workspace/dict/list"));
+
+const channelRoutes: RouteObject[] = [
+  {
+    path: "dict",
+    handle: { id: "workspace.dict", crumb: "dict" },
+    children: [
+      {
+        index: true,
+        Component: WorkspaceDict,
+      },
+    ],
+  },
+];
+
+export default channelRoutes;

+ 20 - 0
dashboard-v6/src/routes/driverRoutes.ts

@@ -0,0 +1,20 @@
+// src/routes/channelRoutes.ts
+import { lazy } from "react";
+import type { RouteObject } from "react-router";
+
+const WorkspaceDriver = lazy(() => import("../pages/workspace/driver/list"));
+
+const channelRoutes: RouteObject[] = [
+  {
+    path: "driver",
+    handle: { id: "workspace.driver", crumb: "driver" },
+    children: [
+      {
+        index: true,
+        Component: WorkspaceDriver,
+      },
+    ],
+  },
+];
+
+export default channelRoutes;