Browse Source

Merge pull request #2042 from visuddhinanda/agile

✨ 跨章节翻页
visuddhinanda 2 years ago
parent
commit
21e7c23f0c

+ 0 - 6
dashboard/src/components/article/Article.tsx

@@ -53,14 +53,11 @@ interface IWidget {
   para?: string | null;
   anthologyId?: string | null;
   courseId?: string | null;
-  exerciseId?: string;
-  userName?: string;
   active?: boolean;
   focus?: string | null;
   hideInteractive?: boolean;
   hideTitle?: boolean;
   onArticleChange?: Function;
-  onFinal?: Function;
   onLoad?: Function;
   onAnthologySelect?: Function;
   onTitle?: Function;
@@ -74,15 +71,12 @@ const ArticleWidget = ({
   articleId,
   anthologyId,
   courseId,
-  exerciseId,
-  userName,
   mode = "read",
   active = false,
   focus,
   hideInteractive = false,
   hideTitle = false,
   onArticleChange,
-  onFinal,
   onLoad,
   onAnthologySelect,
   onTitle,

+ 33 - 4
dashboard/src/components/article/NavigateButton.tsx

@@ -1,7 +1,8 @@
-import { Affix, Button, Dropdown, Space, Typography } from "antd";
+import { Button, Dropdown, Modal, Space, Typography } from "antd";
 import { DoubleRightOutlined, DoubleLeftOutlined } from "@ant-design/icons";
+import { FolderOutlined } from "@ant-design/icons";
+
 import { ITocPathNode } from "../corpus/TocPath";
-import { useEffect, useRef } from "react";
 
 const { Paragraph, Text } = Typography;
 
@@ -23,6 +24,8 @@ interface IWidget {
   prevTitle?: string;
   nextTitle?: string;
   path?: ITocPathNode[];
+  topOfChapter?: boolean;
+  endOfChapter?: boolean;
   onPrev?: Function;
   onNext?: Function;
   onPathChange?: Function;
@@ -30,6 +33,8 @@ interface IWidget {
 const NavigateButtonWidget = ({
   prevTitle,
   nextTitle,
+  topOfChapter = false,
+  endOfChapter = false,
   path,
   onPrev,
   onNext,
@@ -49,10 +54,22 @@ const NavigateButtonWidget = ({
     >
       <Button
         size="middle"
+        icon={topOfChapter ? <FolderOutlined /> : undefined}
         disabled={typeof prevTitle === "undefined"}
         onClick={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
           if (typeof onPrev !== "undefined") {
-            onPrev(event);
+            if (topOfChapter) {
+              Modal.confirm({
+                content: "已经到达章节开头,去上一个章节吗?",
+                okText: "确认",
+                cancelText: "取消",
+                onOk: () => {
+                  onPrev(event);
+                },
+              });
+            } else {
+              onPrev(event);
+            }
           }
         }}
       >
@@ -81,11 +98,23 @@ const NavigateButtonWidget = ({
         </Dropdown>
       </div>
       <Button
+        icon={endOfChapter ? <FolderOutlined /> : undefined}
         size="middle"
         disabled={typeof nextTitle === "undefined"}
         onClick={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
           if (typeof onNext !== "undefined") {
-            onNext(event);
+            if (endOfChapter) {
+              Modal.confirm({
+                content: "已经到达章节末尾,去下一个章节吗?",
+                okText: "确认",
+                cancelText: "取消",
+                onOk: () => {
+                  onNext(event);
+                },
+              });
+            } else {
+              onNext(event);
+            }
           }
         }}
       >

+ 1 - 3
dashboard/src/components/article/TypeArticle.tsx

@@ -1,5 +1,5 @@
 import { useState } from "react";
-import { Alert, Button, Modal } from "antd";
+import { Modal } from "antd";
 import { ExclamationCircleOutlined } from "@ant-design/icons";
 import { IArticleDataResponse } from "../api/Article";
 import { ArticleMode, ArticleType } from "./Article";
@@ -17,7 +17,6 @@ interface IWidget {
   hideTitle?: boolean;
   onArticleChange?: Function;
   onArticleEdit?: Function;
-  onFinal?: Function;
   onLoad?: Function;
   onAnthologySelect?: Function;
 }
@@ -31,7 +30,6 @@ const TypeArticleWidget = ({
   hideInteractive = false,
   hideTitle = false,
   onArticleChange,
-  onFinal,
   onLoad,
   onAnthologySelect,
   onArticleEdit,

+ 19 - 5
dashboard/src/components/article/TypeArticleReader.tsx

@@ -30,7 +30,6 @@ interface IWidget {
   hideInteractive?: boolean;
   hideTitle?: boolean;
   onArticleChange?: Function;
-  onFinal?: Function;
   onLoad?: Function;
   onAnthologySelect?: Function;
   onEdit?: Function;
@@ -45,7 +44,6 @@ const TypeArticleReaderWidget = ({
   hideInteractive = false,
   hideTitle = false,
   onArticleChange,
-  onFinal,
   onLoad,
   onAnthologySelect,
   onEdit,
@@ -143,8 +141,10 @@ const TypeArticleReaderWidget = ({
 
   useEffect(() => {
     const url = `/v2/nav-article/${articleId}_${anthologyId}`;
+    console.info("api request", url);
     get<IArticleNavResponse>(url)
       .then((json) => {
+        console.debug("api response", json);
         if (json.ok) {
           setNav(json.data);
         }
@@ -163,9 +163,21 @@ const TypeArticleReaderWidget = ({
     };
   }
 
-  const title = articleData?.title_text
-    ? articleData?.title_text
-    : articleData?.title;
+  const title = articleData?.title_text ?? articleData?.title;
+
+  let endOfChapter = false;
+  if (nav?.curr && nav?.next) {
+    if (nav?.curr?.level > nav?.next?.level) {
+      endOfChapter = true;
+    }
+  }
+
+  let topOfChapter = false;
+  if (nav?.curr && nav?.prev) {
+    if (nav?.curr?.level > nav?.prev?.level) {
+      topOfChapter = true;
+    }
+  }
 
   return (
     <div>
@@ -235,6 +247,8 @@ const TypeArticleReaderWidget = ({
           <NavigateButton
             prevTitle={nav?.prev?.title}
             nextTitle={nav?.next?.title}
+            topOfChapter={topOfChapter}
+            endOfChapter={endOfChapter}
             path={currPath}
             onNext={() => {
               if (typeof onArticleChange !== "undefined") {

+ 12 - 15
dashboard/src/pages/admin/users/list.tsx

@@ -16,7 +16,7 @@ import User from "../../../components/auth/User";
 const { Text } = Typography;
 
 interface IParams {
-  content_type?: string;
+  role?: string;
 }
 
 const UsersWidget = () => {
@@ -39,6 +39,8 @@ const UsersWidget = () => {
             },
           },
           description: {
+            editable: false,
+            search: false,
             render: (dom, entity, index, action, schema) => {
               return (
                 <Text type="secondary">
@@ -53,10 +55,9 @@ const UsersWidget = () => {
                 </Text>
               );
             },
-            editable: false,
-            search: false,
           },
           subTitle: {
+            search: false,
             render: (dom, entity, index, action, schema) => {
               return entity.role ? (
                 <Space>
@@ -88,11 +89,7 @@ const UsersWidget = () => {
               return [
                 <Dropdown
                   menu={{
-                    items: [
-                      { label: "替换", key: "replace" },
-                      { label: "引用模版", key: "tpl" },
-                      { label: "删除", key: "delete", danger: true },
-                    ],
+                    items: [],
                     onClick: (e) => {
                       console.log("click ", e.key);
                     },
@@ -109,13 +106,12 @@ const UsersWidget = () => {
               ];
             },
           },
-          content_type: {
+          role: {
             // 自己扩展的字段,主要用于筛选,不在列表中显示
             title: "类型",
             valueType: "select",
             valueEnum: {
-              all: { text: "全部", status: "Default" },
-              admin: {
+              administrator: {
                 text: "管理员",
                 status: "Error",
               },
@@ -127,8 +123,8 @@ const UsersWidget = () => {
                 text: "会员",
                 status: "Processing",
               },
-              user: {
-                text: "用户",
+              basic: {
+                text: "基础版",
                 status: "Processing",
               },
             },
@@ -140,14 +136,15 @@ const UsersWidget = () => {
             ((params.current ? params.current : 1) - 1) *
             (params.pageSize ? params.pageSize : 20);
 
-          let url = "/v2/user?view=all";
+          let url = "/v2/user?view=all&order=created_at&dir=desc";
           url += `&limit=${params.pageSize}&offset=${offset}`;
 
           url += params.keyword ? "&search=" + params.keyword : "";
+          url += params.role ? "&role=" + params.role : "";
 
           url += getSorterUrl(sorter);
 
-          console.log(url);
+          console.info("api request", url);
           const res = await get<IUserListResponse2>(url);
           return {
             total: res.data.count,