Sfoglia il codice sorgente

Merge pull request #1530 from visuddhinanda/agile

#1390
visuddhinanda 2 anni fa
parent
commit
94440956e3

+ 6 - 1
dashboard/src/components/api/Dict.ts

@@ -90,11 +90,16 @@ export interface IUserDictDeleteRequest {
   id: string;
 }
 
+export interface ICaseItem {
+  word: string;
+  case: ICaseListData[];
+  count: number;
+}
 export interface ICaseListResponse {
   ok: boolean;
   message: string;
   data: {
-    rows: ICaseListData[];
+    rows: ICaseItem[];
     count: number;
   };
 }

+ 13 - 15
dashboard/src/components/auth/ToStudio.tsx

@@ -12,22 +12,20 @@ const ToStudioWidget = () => {
 
   if (typeof user !== "undefined") {
     return (
-      <>
-        <Link to={`/studio/${user.realName}/home`}>
-          <Button
-            type="primary"
-            style={{
-              paddingLeft: 18,
-              paddingRight: 18,
-              backgroundColor: "#52974e",
-            }}
-          >
-            {intl.formatMessage({
-              id: "columns.studio.title",
-            })}
-          </Button>
+      <Button
+        type="primary"
+        style={{
+          paddingLeft: 18,
+          paddingRight: 18,
+          backgroundColor: "#52974e",
+        }}
+      >
+        <Link to={`/studio/${user.realName}/home`} target="_blank">
+          {intl.formatMessage({
+            id: "columns.studio.title",
+          })}
         </Link>
-      </>
+      </Button>
     );
   } else {
     return <></>;

+ 19 - 7
dashboard/src/components/dict/CaseList.tsx

@@ -1,4 +1,4 @@
-import { List, Tag, Typography } from "antd";
+import { Button, List, Tag, Typography } from "antd";
 import { useEffect, useState } from "react";
 import { get } from "../../request";
 import { ICaseListResponse } from "../api/Dict";
@@ -11,28 +11,40 @@ export interface ICaseListData {
 }
 interface IWidget {
   word?: string;
+  lines?: number;
 }
-const CaseListWidget = ({ word }: IWidget) => {
+const CaseListWidget = ({ word, lines }: IWidget) => {
   const [caseData, setCaseData] = useState<ICaseListData[]>();
+  const [first, setFirst] = useState<string>();
   const [count, setCount] = useState<number>();
+  const [showAll, setShowAll] = useState(lines ? false : true);
   useEffect(() => {
     if (typeof word === "undefined") {
       return;
     }
     get<ICaseListResponse>(`/v2/case/${word}`).then((json) => {
       console.log("case", json);
-      if (json.ok) {
-        setCaseData(json.data.rows.sort((a, b) => b.count - a.count));
-        setCount(json.data.count);
+      if (json.ok && json.data.rows.length > 0) {
+        const first = json.data.rows.sort((a, b) => b.count - a.count)[0];
+        setCaseData(first.case.sort((a, b) => b.count - a.count));
+        setCount(first.count);
+        setFirst(first.word);
       }
     });
   }, [word]);
   return (
     <div style={{ padding: 4 }}>
       <List
-        header={`单词数:${count}`}
+        header={`${first}:${count}`}
+        footer={
+          lines ? (
+            <Button type="link" onClick={() => setShowAll(!showAll)}>
+              {showAll ? "折叠" : "展开"}
+            </Button>
+          ) : undefined
+        }
         size="small"
-        dataSource={caseData}
+        dataSource={showAll ? caseData : caseData?.slice(0, lines)}
         renderItem={(item) => (
           <List.Item>
             <div

+ 15 - 0
dashboard/src/components/general/SearchButton.tsx

@@ -0,0 +1,15 @@
+import { Button } from "antd";
+import { Link } from "react-router-dom";
+import { SearchOutlined } from "@ant-design/icons";
+
+const SearchButtonWidget = () => {
+  return (
+    <Button type="text" size="small">
+      <Link to="/search/home" target={"_blank"}>
+        <SearchOutlined style={{ color: "white" }} />
+      </Link>
+    </Button>
+  );
+};
+
+export default SearchButtonWidget;

+ 10 - 18
dashboard/src/components/library/HeadBar.tsx

@@ -1,15 +1,15 @@
 import { Link } from "react-router-dom";
-import { Button, Layout, Space } from "antd";
+import { Layout, Space } from "antd";
 import { FormattedMessage } from "react-intl";
 import type { MenuProps } from "antd";
 import { Menu } from "antd";
-import { SearchOutlined } from "@ant-design/icons";
 
 import img_banner from "../../assets/library/images/wikipali_logo_library.svg";
 import UiLangSelect from "../general/UiLangSelect";
 import SignInAvatar from "../auth/SignInAvatar";
 import ToStudio from "../auth/ToStudio";
 import ThemeSelect from "../general/ThemeSelect";
+import SearchButton from "../general/SearchButton";
 
 const { Header } = Layout;
 
@@ -153,22 +153,14 @@ const HeadBarWidget = ({ selectedKeys = "" }: IWidgetHeadBar) => {
             items={mainMenuItems}
           />
         </div>
-        <div>
-          <Space>
-            <Link to="/download/download">下载</Link>
-            <Link to="/search/home">
-              <Button
-                style={{ color: "white" }}
-                icon={<SearchOutlined />}
-                type="text"
-              />
-            </Link>
-            <ToStudio />
-            <SignInAvatar />
-            <UiLangSelect />
-            <ThemeSelect />
-          </Space>
-        </div>
+        <Space>
+          <Link to="/download/download">下载</Link>
+          <SearchButton />
+          <ToStudio />
+          <SignInAvatar />
+          <UiLangSelect />
+          <ThemeSelect />
+        </Space>
       </div>
     </Header>
   );

+ 92 - 86
dashboard/src/pages/library/article/show.tsx

@@ -46,6 +46,8 @@ import {
 import { openPanel } from "../../../reducers/right-panel";
 import { TResType } from "../../../components/discussion/DiscussionListCard";
 import { modeChange } from "../../../reducers/article-mode";
+import SearchButton from "../../../components/general/SearchButton";
+import ToStudio from "../../../components/auth/ToStudio";
 
 /**
  * type:
@@ -135,95 +137,99 @@ const Widget = () => {
   };
   return (
     <div id="article-root">
-      <Affix offsetTop={0}>
-        <Header
-          style={{
-            height: 44,
-            lineHeight: 44,
-            display: "flex",
-            justifyContent: "space-between",
-            padding: "5px",
-          }}
-        >
-          <div style={{ display: "flex" }}>
-            <MainMenu />
-            <NetStatus style={{ color: "white" }} />
-          </div>
-          <div></div>
-          <div style={{ display: "flex" }} key="right">
-            {type === "article" && loadedArticleData ? (
-              <Button
-                ghost
-                onClick={(event) => {
-                  const url = `/studio/${loadedArticleData.studio?.realName}/article/${loadedArticleData.uid}/edit`;
-                  if (event.ctrlKey || event.metaKey) {
-                    window.open(fullUrl(url), "_blank");
-                  } else {
-                    navigate(url);
-                  }
-                }}
-              >
-                Edit
-              </Button>
-            ) : undefined}
-            <Avatar placement="bottom" />
-            <ThemeSelect />
-            <Divider type="vertical" />
-            <ModeSwitch
-              channel={searchParams.get("channel")}
-              currMode={currMode}
-              onModeChange={(e: ArticleMode) => {
-                let output: any = { mode: e };
-                searchParams.forEach((value, key) => {
-                  console.log(value, key);
-                  if (key !== "mode") {
-                    output[key] = value;
-                  }
-                });
-                setSearchParams(output);
+      <Header
+        style={{
+          height: 44,
+          lineHeight: 44,
+          display: "flex",
+          justifyContent: "space-between",
+          padding: "5px",
+        }}
+      >
+        <div style={{ display: "flex" }} key="left">
+          <MainMenu />
+          <NetStatus style={{ color: "white" }} />
+        </div>
+        <div style={{ display: "flex" }} key="middle"></div>
+        <div style={{ display: "flex" }} key="right">
+          {type === "article" && loadedArticleData ? (
+            <Button
+              ghost
+              onClick={(event) => {
+                const url = `/studio/${loadedArticleData.studio?.realName}/article/${loadedArticleData.uid}/edit`;
+                if (event.ctrlKey || event.metaKey) {
+                  window.open(fullUrl(url), "_blank");
+                } else {
+                  navigate(url);
+                }
               }}
-              onChannelChange={(channels: IChannel[], mode: ArticleMode) => {
-                let output: any = {
-                  mode: mode,
-                  channel: channels.map((item) => item.id).join("_"),
-                };
-                searchParams.forEach((value, key) => {
-                  console.log(value, key);
-                  if (key !== "mode" && key !== "channel") {
-                    output[key] = value;
+            >
+              Edit
+            </Button>
+          ) : undefined}
+          <SearchButton />
+          <Divider type="vertical" />
+          <ToStudio />
+          <Divider type="vertical" />
+          <Avatar placement="bottom" />
+          <Divider type="vertical" />
+          <ThemeSelect />
+          <Divider type="vertical" />
+          <ModeSwitch
+            channel={searchParams.get("channel")}
+            currMode={currMode}
+            onModeChange={(e: ArticleMode) => {
+              let output: any = { mode: e };
+              searchParams.forEach((value, key) => {
+                console.log(value, key);
+                if (key !== "mode") {
+                  output[key] = value;
+                }
+              });
+              setSearchParams(output);
+            }}
+            onChannelChange={(channels: IChannel[], mode: ArticleMode) => {
+              let output: any = {
+                mode: mode,
+                channel: channels.map((item) => item.id).join("_"),
+              };
+              searchParams.forEach((value, key) => {
+                console.log(value, key);
+                if (key !== "mode" && key !== "channel") {
+                  output[key] = value;
+                }
+              });
+              setSearchParams(output);
+            }}
+          />
+          <Tooltip title="文章目录" placement="bottomLeft">
+            <Button
+              style={{ display: "block", color: "white" }}
+              icon={<UnorderedListOutlined />}
+              type="text"
+              onClick={() => setAnchorNavOpen((value) => !value)}
+            />
+          </Tooltip>
+          <Tooltip title="侧边栏" placement="bottomLeft">
+            <Button
+              style={{ display: "block", color: "white" }}
+              icon={<ColumnOutlinedIcon />}
+              type="text"
+              onClick={() =>
+                setRightPanel((value) => {
+                  if (value === "close") {
+                    setAnchorNavShow(false);
+                  } else {
+                    setAnchorNavShow(true);
                   }
-                });
-                setSearchParams(output);
-              }}
+                  return value === "close" ? "open" : "close";
+                })
+              }
             />
-            <Tooltip title="文章目录" placement="bottomLeft">
-              <Button
-                style={{ display: "block", color: "white" }}
-                icon={<UnorderedListOutlined />}
-                type="text"
-                onClick={() => setAnchorNavOpen((value) => !value)}
-              />
-            </Tooltip>
-            <Tooltip title="侧边栏" placement="bottomLeft">
-              <Button
-                style={{ display: "block", color: "white" }}
-                icon={<ColumnOutlinedIcon />}
-                type="text"
-                onClick={() =>
-                  setRightPanel((value) => {
-                    if (value === "close") {
-                      setAnchorNavShow(false);
-                    } else {
-                      setAnchorNavShow(true);
-                    }
-                    return value === "close" ? "open" : "close";
-                  })
-                }
-              />
-            </Tooltip>
-          </div>
-        </Header>
-      </Affix>
+          </Tooltip>
+        </div>
+      </Header>
+
       <div style={{ width: "100%", display: "flex" }}>
         <Affix offsetTop={44}>
           <div

+ 2 - 0
dashboard/src/pages/library/search/search.tsx

@@ -6,6 +6,7 @@ import BookTree from "../../../components/corpus/BookTree";
 import FullTextSearchResult from "../../../components/fts/FullTextSearchResult";
 import FtsBookList from "../../../components/fts/FtsBookList";
 import FtsSetting from "../../../components/fts/FtsSetting";
+import CaseList from "../../../components/dict/CaseList";
 
 const Widget = () => {
   const { key } = useParams();
@@ -133,6 +134,7 @@ const Widget = () => {
               </Space>
             </Col>
             <Col xs={0} sm={0} md={5}>
+              <CaseList word={key} lines={5} />
               <FtsBookList
                 view={view}
                 keyWord={key}