Просмотр исходного кода

Merge pull request #1914 from visuddhinanda/agile

修正页码模式翻页错误
visuddhinanda 2 лет назад
Родитель
Сommit
cc8a6fa8a3

+ 22 - 7
dashboard/src/components/article/Article.tsx

@@ -8,6 +8,8 @@ import TypePage from "./TypePage";
 import TypeCSPara from "./TypeCSPara";
 import { ISearchParams } from "../../pages/library/article/show";
 import TypeCourse from "./TypeCourse";
+import { useEffect, useState } from "react";
+import { fullUrl } from "../../utils";
 
 export type ArticleMode = "read" | "edit" | "wbw";
 export type ArticleType =
@@ -79,12 +81,15 @@ const ArticleWidget = ({
   onAnthologySelect,
   onTitle,
 }: IWidget) => {
+  const [currId, setCurrId] = useState(articleId);
+  useEffect(() => setCurrId(articleId), [articleId]);
+
   return (
     <div>
       {type === "article" ? (
         <TypeArticle
           type={type}
-          articleId={articleId}
+          articleId={currId}
           channelId={channelId}
           mode={mode}
           anthologyId={anthologyId}
@@ -110,7 +115,7 @@ const ArticleWidget = ({
         />
       ) : type === "anthology" ? (
         <TypeAnthology
-          articleId={articleId}
+          articleId={currId}
           channelId={channelId}
           mode={mode}
           onArticleChange={(type: ArticleType, id: string, target: string) => {
@@ -126,7 +131,7 @@ const ArticleWidget = ({
         />
       ) : type === "term" ? (
         <TypeTerm
-          articleId={articleId}
+          articleId={currId}
           channelId={channelId}
           mode={mode}
           onArticleChange={(type: ArticleType, id: string, target: string) => {
@@ -138,7 +143,7 @@ const ArticleWidget = ({
       ) : type === "chapter" || type === "para" ? (
         <TypePali
           type={type}
-          articleId={articleId}
+          articleId={currId}
           channelId={channelId}
           mode={mode}
           book={book}
@@ -167,19 +172,29 @@ const ArticleWidget = ({
         />
       ) : type === "page" ? (
         <TypePage
-          articleId={articleId}
+          articleId={currId}
           channelId={channelId}
           focus={focus}
           mode={mode}
           onArticleChange={(type: ArticleType, id: string, target: string) => {
             if (typeof onArticleChange !== "undefined") {
               onArticleChange(type, id, target);
+            } else {
+              if (target === "_blank") {
+                let url = `/article/page/${id}?mode=${mode}`;
+                if (channelId) {
+                  url += `&channel=${channelId}`;
+                }
+                window.open(fullUrl(url), "_blank");
+              } else {
+                setCurrId(id);
+              }
             }
           }}
         />
       ) : type === "cs-para" ? (
         <TypeCSPara
-          articleId={articleId}
+          articleId={currId}
           channelId={channelId}
           mode={mode}
           onArticleChange={(type: ArticleType, id: string, target: string) => {
@@ -191,7 +206,7 @@ const ArticleWidget = ({
       ) : type === "textbook" ? (
         <TypeCourse
           type={type}
-          articleId={articleId}
+          articleId={currId}
           channelId={channelId}
           courseId={courseId}
           mode={mode}

+ 43 - 37
dashboard/src/components/article/TypePage.tsx

@@ -11,6 +11,7 @@ import NavigateButton from "./NavigateButton";
 import ArticleSkeleton from "./ArticleSkeleton";
 import ErrorResult from "../general/ErrorResult";
 import "./article.css";
+import { fullUrl } from "../../utils";
 
 interface IParam {
   articleId?: string;
@@ -52,14 +53,18 @@ const TypePageWidget = ({
   const [errorCode, setErrorCode] = useState<number>();
   const [errorMessage, setErrorMessage] = useState<string>();
   const [pageInfo, setPageInfo] = useState<string>();
+  const [currId, setCurrId] = useState(articleId);
+
   const intl = useIntl();
 
+  useEffect(() => setCurrId(articleId), [articleId]);
+
   useEffect(() => {
-    if (typeof articleId === "undefined") {
+    if (typeof currId === "undefined") {
       return;
     }
 
-    const pageParam = articleId.split("_");
+    const pageParam = currId.split("_");
     if (pageParam.length < 4) {
       return;
     }
@@ -109,7 +114,40 @@ const TypePageWidget = ({
           setErrorMessage(`该页面不存在。页面信息:${pageInfo}`);
         }
       });
-  }, [articleId, channelId, intl, mode, pageInfo]);
+  }, [currId, channelId, intl, mode, pageInfo]);
+
+  const seek = (
+    event: React.MouseEvent<HTMLElement, MouseEvent>,
+    page: number
+  ) => {
+    if (typeof currId === "undefined") {
+      return;
+    }
+    const pageParam = currId.split("_");
+    if (pageParam.length < 4) {
+      return;
+    }
+    const id = `${pageParam[0]}_${pageParam[1]}_${pageParam[2]}_${
+      parseInt(pageParam[3]) + page
+    }`;
+    let target = "_self";
+    if (event.ctrlKey || event.metaKey) {
+      target = "_blank";
+    }
+    if (typeof onArticleChange !== "undefined") {
+      onArticleChange("page", id, target);
+    } else {
+      if (target === "_blank") {
+        let url = `/article/page/${id}?mode=${mode}`;
+        if (channelId) {
+          url += `&channel=${channelId}`;
+        }
+        window.open(fullUrl(url), "_blank");
+      } else {
+        setCurrId(id);
+      }
+    }
+  };
 
   return (
     <div>
@@ -131,42 +169,10 @@ const TypePageWidget = ({
             prevTitle={nav?.prev.page.toString()}
             nextTitle={nav?.next.page.toString()}
             onNext={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
-              if (typeof onArticleChange !== "undefined") {
-                if (typeof articleId === "undefined") {
-                  return;
-                }
-                const pageParam = articleId.split("_");
-                if (pageParam.length < 4) {
-                  return;
-                }
-                const id = `${pageParam[0]}-${pageParam[1]}-${pageParam[2]}-${
-                  parseInt(pageParam[3]) + 1
-                }`;
-                let target = "_self";
-                if (event.ctrlKey || event.metaKey) {
-                  target = "_blank";
-                }
-                onArticleChange("page", id, target);
-              }
+              seek(event, 1);
             }}
             onPrev={(event: React.MouseEvent<HTMLElement, MouseEvent>) => {
-              if (typeof onArticleChange !== "undefined") {
-                if (typeof articleId === "undefined") {
-                  return;
-                }
-                const pageParam = articleId.split("_");
-                if (pageParam.length < 4) {
-                  return;
-                }
-                const id = `${pageParam[0]}-${pageParam[1]}-${pageParam[2]}-${
-                  parseInt(pageParam[3]) - 1
-                }`;
-                let target = "_self";
-                if (event.ctrlKey || event.metaKey) {
-                  target = "_blank";
-                }
-                onArticleChange("page", id, target);
-              }
+              seek(event, -1);
             }}
           />
         </>

+ 5 - 1
dashboard/src/components/discussion/DiscussionDrawer.tsx

@@ -7,6 +7,7 @@ import DiscussionListCard, { TResType } from "./DiscussionListCard";
 import { IComment } from "./DiscussionItem";
 import DiscussionAnchor from "./DiscussionAnchor";
 import { Link } from "react-router-dom";
+import { useIntl } from "react-intl";
 
 export interface IAnswerCount {
   id: string;
@@ -24,6 +25,7 @@ const DiscussionDrawerWidget = ({
   resType,
   onCommentCountChange,
 }: IWidget) => {
+  const intl = useIntl();
   const [open, setOpen] = useState(false);
   const [childrenDrawer, setChildrenDrawer] = useState(false);
   const [topicComment, setTopicComment] = useState<IComment>();
@@ -52,7 +54,9 @@ const DiscussionDrawerWidget = ({
         extra={
           <Space>
             <Link to={`/discussion/show/${resType}/${resId}`} target="_blank">
-              在新窗口打开
+              {intl.formatMessage({
+                id: "buttons.open.in.new.tab",
+              })}
             </Link>
             {drawerWidth === drawerMinWidth ? (
               <Button

+ 7 - 1
dashboard/src/components/general/NissayaCard.tsx

@@ -10,6 +10,7 @@ import { ITerm } from "../term/TermEdit";
 import { Link } from "react-router-dom";
 import TermModal from "../term/TermModal";
 import { ITermDataResponse } from "../api/Term";
+import { useIntl } from "react-intl";
 
 const { Paragraph, Title } = Typography;
 
@@ -76,6 +77,7 @@ interface IWidget {
   cache?: boolean;
 }
 const NissayaCardWidget = ({ text, cache = false }: IWidget) => {
+  const intl = useIntl();
   const [cardData, setCardData] = useState<INissayaRelation[]>();
   const [term, setTerm] = useState<ITerm>();
   const [loading, setLoading] = useState(false);
@@ -143,7 +145,11 @@ const NissayaCardWidget = ({ text, cache = false }: IWidget) => {
           />
         </Title>
         <div>
-          <Link to={`/nissaya/ending/${term?.word}`}>在新窗口打开</Link>
+          <Link to={`/nissaya/ending/${term?.word}`}>
+            {intl.formatMessage({
+              id: "buttons.open.in.new.tab",
+            })}
+          </Link>
           <Button
             type="link"
             icon={<ReloadOutlined />}

+ 9 - 5
dashboard/src/components/template/Article.tsx

@@ -1,9 +1,10 @@
-import { Card, Collapse, Modal } from "antd";
+import { Card, Collapse, Modal, Space } from "antd";
 import { Typography } from "antd";
 import { useState } from "react";
 import Article, { ArticleType } from "../article/Article";
 import { Link } from "react-router-dom";
 import { fullUrl } from "../../utils";
+import { useIntl } from "react-intl";
 
 const { Text } = Typography;
 
@@ -31,6 +32,7 @@ export const ArticleCtl = ({
   style = "modal",
   modalExtra,
 }: IWidgetChapterCtl) => {
+  const intl = useIntl();
   const [isModalOpen, setIsModalOpen] = useState(false);
   const showModal = () => {
     setIsModalOpen(true);
@@ -89,12 +91,14 @@ export const ArticleCtl = ({
                 }}
               >
                 <Text>{aTitle}</Text>
-                <Text>
-                  {modalExtra}
+                <Space>
                   <Link to={articleLink} target="_blank">
-                    {"新窗口打开"}
+                    {intl.formatMessage({
+                      id: "buttons.open.in.new.tab",
+                    })}
                   </Link>
-                </Text>
+                  {modalExtra}
+                </Space>
               </div>
             }
             open={isModalOpen}

+ 5 - 1
dashboard/src/components/template/Quote.tsx

@@ -2,6 +2,7 @@ import { Button, Popover } from "antd";
 import { Typography } from "antd";
 import { SearchOutlined, CopyOutlined } from "@ant-design/icons";
 import { ProCard } from "@ant-design/pro-components";
+import { useIntl } from "react-intl";
 
 const { Text, Link } = Typography;
 
@@ -21,6 +22,7 @@ const QuoteCtl = ({
   error,
   message,
 }: IWidgetQuoteCtl) => {
+  const intl = useIntl();
   const show = pali ? pali : paraId;
   let textShow = <></>;
 
@@ -39,7 +41,9 @@ const QuoteCtl = ({
             分栏打开
           </Button>,
           <Button type="link" size="small" icon={<SearchOutlined />}>
-            新窗口打开
+            {intl.formatMessage({
+              id: "buttons.open.in.new.tab",
+            })}
           </Button>,
           <Button type="link" size="small" icon={<CopyOutlined />}>
             复制引用

+ 3 - 0
dashboard/src/components/template/SentEdit.tsx

@@ -70,6 +70,7 @@ export interface IWidgetSentEditInner {
   simNum?: number;
   compact?: boolean;
   mode?: ArticleMode;
+  wbwProgress?: boolean;
 }
 export const SentEditInner = ({
   id,
@@ -89,6 +90,7 @@ export const SentEditInner = ({
   simNum,
   compact = false,
   mode,
+  wbwProgress = false,
 }: IWidgetSentEditInner) => {
   const [wbwData, setWbwData] = useState<IWbw[]>();
   const [magicDict, setMagicDict] = useState<string>();
@@ -175,6 +177,7 @@ export const SentEditInner = ({
         magicDict={magicDict}
         compact={isCompact}
         mode={articleMode}
+        wbwProgress={wbwProgress}
         onWbwChange={(data: IWbw[]) => {
           setWbwData(data);
         }}

+ 3 - 0
dashboard/src/components/template/SentEdit/SentContent.tsx

@@ -27,6 +27,7 @@ interface IWidgetSentContent {
   magicDict?: string;
   compact?: boolean;
   mode?: ArticleMode;
+  wbwProgress?: boolean;
   onWbwChange?: Function;
   onMagicDictDone?: Function;
 }
@@ -42,6 +43,7 @@ const SentContentWidget = ({
   compact = false,
   mode,
   magicDict,
+  wbwProgress = false,
   onWbwChange,
   onMagicDictDone,
 }: IWidgetSentContent) => {
@@ -134,6 +136,7 @@ const SentContentWidget = ({
                 channelId={item.channel.id}
                 data={JSON.parse(item.content ? item.content : "")}
                 mode={mode}
+                wbwProgress={wbwProgress}
                 onChange={(data: IWbw[]) => {
                   if (typeof onWbwChange !== "undefined") {
                     onWbwChange(data);

+ 15 - 7
dashboard/src/components/template/SentEdit/SentTab.tsx

@@ -19,6 +19,7 @@ import { IResNumber } from "../SentEdit";
 import SentTabCopy from "./SentTabCopy";
 import { fullUrl } from "../../../utils";
 import SentWbw from "./SentWbw";
+import SentTabButtonWbw from "./SentTabButtonWbw";
 
 const { Text } = Typography;
 
@@ -74,6 +75,7 @@ const SentTabWidget = ({
   const [currTranNum, setCurrTranNum] = useState(tranNum);
   const [currNissayaNum, setCurrNissayaNum] = useState(nissayaNum);
   const [currCommNum, setCurrCommNum] = useState(commNum);
+  const [showWbwProgress, setShowWbwProgress] = useState(false);
 
   console.log("SentTabWidget render");
 
@@ -344,13 +346,18 @@ const SentTabWidget = ({
         },
         {
           label: (
-            <span style={tabButtonStyle}>
-              <span style={{ marginRight: 12 }}>
-                {intl.formatMessage({
-                  id: "buttons.wbw",
-                })}
-              </span>
-            </span>
+            <SentTabButtonWbw
+              style={tabButtonStyle}
+              sentId={id}
+              count={0}
+              onMenuClick={(keyPath: string[]) => {
+                switch (keyPath.join("-")) {
+                  case "show-progress":
+                    setShowWbwProgress((origin) => !origin);
+                    break;
+                }
+              }}
+            />
           ),
           key: "wbw",
           children: (
@@ -360,6 +367,7 @@ const SentTabWidget = ({
               wordStart={parseInt(sId[2])}
               wordEnd={parseInt(sId[3])}
               channelsId={channelsId}
+              wbwProgress={showWbwProgress}
             />
           ),
         },

+ 72 - 0
dashboard/src/components/template/SentEdit/SentTabButtonWbw.tsx

@@ -0,0 +1,72 @@
+import { useIntl } from "react-intl";
+import { Badge, Dropdown } from "antd";
+import type { MenuProps } from "antd";
+import { BlockOutlined, CalendarOutlined } from "@ant-design/icons";
+
+const handleButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
+  console.log("click left button", e);
+};
+
+interface IWidget {
+  style?: React.CSSProperties;
+  sentId: string;
+  count?: number;
+  onMenuClick?: Function;
+}
+const SentTabButtonWidget = ({
+  style,
+  sentId,
+  onMenuClick,
+  count = 0,
+}: IWidget) => {
+  const intl = useIntl();
+  const items: MenuProps["items"] = [
+    {
+      label: "排序",
+      key: "orderby",
+      icon: <CalendarOutlined />,
+      children: [
+        {
+          label: "完成度",
+          key: "progress",
+        },
+        {
+          label: "问题数量",
+          key: "qa",
+        },
+      ],
+    },
+    {
+      label: "显示完成度",
+      key: "show-progress",
+      icon: <BlockOutlined />,
+    },
+  ];
+  const handleMenuClick: MenuProps["onClick"] = (e) => {
+    e.domEvent.stopPropagation();
+    if (typeof onMenuClick !== "undefined") {
+      onMenuClick(e.keyPath);
+    }
+  };
+  const menuProps = {
+    items,
+    onClick: handleMenuClick,
+  };
+
+  return (
+    <Dropdown.Button
+      style={style}
+      size="small"
+      type="text"
+      menu={menuProps}
+      onClick={handleButtonClick}
+    >
+      {intl.formatMessage({
+        id: "buttons.wbw",
+      })}
+      <Badge size="small" color="geekblue" count={count}></Badge>
+    </Dropdown.Button>
+  );
+};
+
+export default SentTabButtonWidget;

+ 3 - 1
dashboard/src/components/template/SentEdit/SentWbw.tsx

@@ -15,6 +15,7 @@ interface IWidget {
   wordEnd: number;
   channelsId?: string[];
   reload?: boolean;
+  wbwProgress?: boolean;
   onReload?: Function;
 }
 const SentWbwWidget = ({
@@ -24,6 +25,7 @@ const SentWbwWidget = ({
   wordEnd,
   channelsId,
   reload = false,
+  wbwProgress = false,
   onReload,
 }: IWidget) => {
   const [initLoading, setInitLoading] = useState(true);
@@ -85,7 +87,7 @@ const SentWbwWidget = ({
         dataSource={sentData}
         renderItem={(item, index) => (
           <List.Item key={index}>
-            <SentEditInner {...item} />
+            <SentEditInner {...item} wbwProgress={wbwProgress} />
           </List.Item>
         )}
       />

+ 5 - 1
dashboard/src/components/template/Video.tsx

@@ -6,6 +6,7 @@ import { Link } from "react-router-dom";
 import { TDisplayStyle } from "./Article";
 import Video from "../general/Video";
 import { VideoIcon } from "../../assets/icon";
+import { useIntl } from "react-intl";
 
 const { Text } = Typography;
 
@@ -16,6 +17,7 @@ interface IVideoCtl {
 }
 
 export const VideoCtl = ({ url, title, style = "modal" }: IVideoCtl) => {
+  const intl = useIntl();
   const [isModalOpen, setIsModalOpen] = useState(false);
   const showModal = () => {
     setIsModalOpen(true);
@@ -65,7 +67,9 @@ export const VideoCtl = ({ url, title, style = "modal" }: IVideoCtl) => {
                 <Text>{title}</Text>
                 <Text>
                   <Link to={articleLink} target="_blank">
-                    {"新窗口打开"}
+                    {intl.formatMessage({
+                      id: "buttons.open.in.new.tab",
+                    })}
                   </Link>
                 </Text>
               </div>

+ 4 - 0
dashboard/src/components/template/WbwSent.tsx

@@ -86,6 +86,7 @@ interface IWidget {
   magicDict?: string;
   refreshable?: boolean;
   mode?: ArticleMode;
+  wbwProgress?: boolean;
   onMagicDictDone?: Function;
   onChange?: Function;
 }
@@ -103,6 +104,7 @@ export const WbwSentCtl = ({
   mode,
   magicDict,
   refreshable = false,
+  wbwProgress = false,
   onChange,
   onMagicDictDone,
 }: IWidget) => {
@@ -116,6 +118,8 @@ export const WbwSentCtl = ({
   const [progress, setProgress] = useState(0);
   const [showProgress, setShowProgress] = useState(false);
 
+  useEffect(() => setShowProgress(wbwProgress), [wbwProgress]);
+
   const settings = useAppSelector(settingInfo);
   const sysGrammar = useAppSelector(getGrammar)?.filter(
     (value) => value.tag === ":collocation:"

+ 1 - 0
dashboard/src/locales/en-US/buttons.ts

@@ -81,6 +81,7 @@ const items = {
   "buttons.relate": "relate",
   "buttons.convert": "convert",
   "buttons.copy.tpl": "copy template",
+  "buttons.open.in.new.tab": "Open in New Tab",
 };
 
 export default items;

+ 1 - 0
dashboard/src/locales/zh-Hans/buttons.ts

@@ -81,6 +81,7 @@ const items = {
   "buttons.relate": "关联",
   "buttons.convert": "转换",
   "buttons.copy.tpl": "复制模版",
+  "buttons.open.in.new.tab": "在新标签页中打开",
 };
 
 export default items;