Quellcode durchsuchen

:construction: create

visuddhinanda vor 3 Jahren
Ursprung
Commit
4558c72b4e

+ 47 - 0
dashboard/src/components/anthology/TocTree.tsx

@@ -0,0 +1,47 @@
+import { Key } from "antd/lib/table/interface";
+import { useEffect, useState } from "react";
+
+import { get } from "../../request";
+import { IArticleMapListResponse } from "../api/Article";
+import EditableTree, { ListNodeData } from "../article/EditableTree";
+
+interface IWidget {
+  anthologyId?: string;
+}
+const Widget = ({ anthologyId }: IWidget) => {
+  const [tocData, setTocData] = useState<ListNodeData[]>([]);
+  const [keys, setKeys] = useState<Key[]>();
+
+  useEffect(() => {
+    get<IArticleMapListResponse>(
+      `/v2/article-map?view=anthology&id=${anthologyId}`
+    ).then((json) => {
+      console.log("文集get", json);
+      if (json.ok) {
+        const toc: ListNodeData[] = json.data.rows.map((item) => {
+          return {
+            key: item.id ? item.id : item.title,
+            title: item.title,
+            level: item.level,
+          };
+        });
+        setTocData(toc);
+      }
+    });
+  }, [anthologyId]);
+  return (
+    <div>
+      <EditableTree
+        treeData={tocData}
+        onChange={(data: ListNodeData[]) => {
+          setTocData(data);
+        }}
+        onSelect={(selectedKeys: Key[]) => {
+          setKeys(selectedKeys);
+        }}
+      />
+    </div>
+  );
+};
+
+export default Widget;

+ 5 - 0
dashboard/src/components/api/Guide.ts

@@ -0,0 +1,5 @@
+export interface IGuideResponse {
+  ok: boolean;
+  message: string;
+  data: string;
+}

+ 39 - 0
dashboard/src/components/article/MainMenu.tsx

@@ -0,0 +1,39 @@
+import { Button, Dropdown, MenuProps } from "antd";
+import { MenuOutlined } from "@ant-design/icons";
+
+const Widget = () => {
+  const items: MenuProps["items"] = [
+    {
+      key: "new",
+      label: "最新",
+    },
+    {
+      key: "palicanon",
+      label: "圣典",
+    },
+  ];
+  const onClick: MenuProps["onClick"] = (e) => {
+    console.log("click ", e);
+    switch (e.key) {
+      case "showCol":
+        break;
+      default:
+        break;
+    }
+  };
+  return (
+    <Dropdown
+      menu={{ items, onClick }}
+      placement="bottomLeft"
+      trigger={["click"]}
+    >
+      <Button
+        style={{ display: "block" }}
+        size="small"
+        icon={<MenuOutlined />}
+      ></Button>
+    </Dropdown>
+  );
+};
+
+export default Widget;

+ 53 - 0
dashboard/src/components/article/ModeSwitch.tsx

@@ -0,0 +1,53 @@
+import { Segmented } from "antd";
+import { useState } from "react";
+import { useIntl } from "react-intl";
+import { modeChange } from "../../reducers/article-mode";
+import store from "../../store";
+import { ArticleMode } from "./Article";
+
+interface IWidget {
+  initMode?: string;
+  onModeChange?: Function;
+}
+const Widget = ({ initMode = "read", onModeChange }: IWidget) => {
+  const intl = useIntl();
+  const [mode, setMode] = useState<string>(initMode);
+  return (
+    <Segmented
+      size="middle"
+      style={{
+        color: "rgb(134 134 134 / 90%)",
+        backgroundColor: "rgb(129 129 129 / 17%)",
+        display: "block",
+      }}
+      options={[
+        {
+          label: intl.formatMessage({ id: "buttons.read" }),
+          value: "read",
+        },
+        {
+          label: intl.formatMessage({ id: "buttons.translate" }),
+          value: "edit",
+        },
+        {
+          label: intl.formatMessage({ id: "buttons.wbw" }),
+          value: "wbw",
+        },
+      ]}
+      value={mode}
+      onChange={(value) => {
+        const newMode = value.toString();
+        if (typeof onModeChange !== "undefined") {
+          if (mode === "read" || newMode === "read") {
+            onModeChange(newMode);
+          }
+        }
+        setMode(newMode);
+        //发布mode变更
+        store.dispatch(modeChange(newMode as ArticleMode));
+      }}
+    />
+  );
+};
+
+export default Widget;

+ 39 - 0
dashboard/src/components/article/RightPanel.tsx

@@ -0,0 +1,39 @@
+import { useEffect, useState } from "react";
+import DictComponent from "../dict/DictComponent";
+import Dictionary from "../dict/Dictionary";
+
+export type TPanelName = "dict" | "channel" | "close";
+interface IWidget {
+  curr?: TPanelName;
+}
+const Widget = ({ curr = "close" }: IWidget) => {
+  const [dict, setDict] = useState("none");
+  const [channel, setChannel] = useState("none");
+
+  useEffect(() => {
+    switch (curr) {
+      case "dict":
+        setDict("block");
+        setChannel("none");
+        break;
+      case "channel":
+        setDict("none");
+        setChannel("block");
+        break;
+      default:
+        setDict("none");
+        setChannel("none");
+        break;
+    }
+  }, [curr]);
+  return (
+    <div>
+      <div style={{ width: 350, display: dict }}>
+        <DictComponent />
+      </div>
+      <div style={{ width: 350, display: channel }}>channel</div>
+    </div>
+  );
+};
+
+export default Widget;

+ 47 - 0
dashboard/src/components/article/RightToolsSwitch.tsx

@@ -0,0 +1,47 @@
+import { Segmented } from "antd";
+import { useState } from "react";
+import { useIntl } from "react-intl";
+import { TPanelName } from "./RightPanel";
+
+interface IWidget {
+  initMode?: string;
+  onModeChange?: Function;
+}
+const Widget = ({ initMode = "close", onModeChange }: IWidget) => {
+  const intl = useIntl();
+  const [mode, setMode] = useState<string>(initMode);
+  return (
+    <Segmented
+      size="middle"
+      style={{
+        color: "rgb(134 134 134 / 90%)",
+        backgroundColor: "rgb(129 129 129 / 17%)",
+        display: "block",
+      }}
+      options={[
+        {
+          label: intl.formatMessage({ id: "columns.library.dict.title" }),
+          value: "dict",
+        },
+        {
+          label: intl.formatMessage({ id: "columns.studio.channel.title" }),
+          value: "channel",
+        },
+        {
+          label: intl.formatMessage({ id: "buttons.close" }),
+          value: "close",
+        },
+      ]}
+      value={mode}
+      onChange={(value) => {
+        const newMode: TPanelName = value.toString() as TPanelName;
+        if (typeof onModeChange !== "undefined") {
+          onModeChange(newMode);
+        }
+        setMode(newMode);
+      }}
+    />
+  );
+};
+
+export default Widget;

+ 38 - 0
dashboard/src/components/article/ToolButton.tsx

@@ -0,0 +1,38 @@
+import { Button, Drawer, Tooltip } from "antd";
+import { useState } from "react";
+
+interface IWidget {
+  icon?: JSX.Element;
+  content?: JSX.Element;
+  title?: string;
+}
+const Widget = ({ icon, content, title }: IWidget) => {
+  const [open, setOpen] = useState(false);
+
+  return (
+    <>
+      <Tooltip placement="right" title={title}>
+        <Button
+          size="middle"
+          icon={icon}
+          onClick={() => {
+            setOpen(true);
+          }}
+        />
+      </Tooltip>
+      <Drawer
+        title={title}
+        width={350}
+        placement="left"
+        onClose={() => {
+          setOpen(false);
+        }}
+        open={open}
+      >
+        {content}
+      </Drawer>
+    </>
+  );
+};
+
+export default Widget;

+ 24 - 0
dashboard/src/components/article/ToolButtonSearch.tsx

@@ -0,0 +1,24 @@
+import { SearchOutlined } from "@ant-design/icons";
+
+import ToolButton from "./ToolButton";
+
+interface IWidget {
+  type?: string;
+  articleId?: string;
+}
+const Widget = ({ type, articleId }: IWidget) => {
+  const id = articleId?.split("_");
+  let tocWidget = <></>;
+  if (id && id.length > 0) {
+    const sentId = id[0].split("-");
+    if (sentId.length > 1) {
+      tocWidget = <></>;
+    }
+  }
+
+  return (
+    <ToolButton title="搜索" icon={<SearchOutlined />} content={tocWidget} />
+  );
+};
+
+export default Widget;

+ 20 - 0
dashboard/src/components/article/ToolButtonSetting.tsx

@@ -0,0 +1,20 @@
+import { SettingOutlined } from "@ant-design/icons";
+import SettingArticle from "../auth/setting/SettingArticle";
+
+import ToolButton from "./ToolButton";
+
+interface IWidget {
+  type?: string;
+  articleId?: string;
+}
+const Widget = ({ type, articleId }: IWidget) => {
+  return (
+    <ToolButton
+      title="设置"
+      icon={<SettingOutlined />}
+      content={<SettingArticle />}
+    />
+  );
+};
+
+export default Widget;

+ 22 - 0
dashboard/src/components/article/ToolButtonTag.tsx

@@ -0,0 +1,22 @@
+import { TagOutlined } from "@ant-design/icons";
+
+import ToolButton from "./ToolButton";
+
+interface IWidget {
+  type?: string;
+  articleId?: string;
+}
+const Widget = ({ type, articleId }: IWidget) => {
+  const id = articleId?.split("_");
+  let tocWidget = <></>;
+  if (id && id.length > 0) {
+    const sentId = id[0].split("-");
+    if (sentId.length > 1) {
+      tocWidget = <></>;
+    }
+  }
+
+  return <ToolButton title="标签" icon={<TagOutlined />} content={tocWidget} />;
+};
+
+export default Widget;

+ 27 - 0
dashboard/src/components/article/ToolButtonToc.tsx

@@ -0,0 +1,27 @@
+import { MenuOutlined } from "@ant-design/icons";
+
+import PaliTextToc from "./PaliTextToc";
+import ToolButton from "./ToolButton";
+
+interface IWidget {
+  type?: string;
+  articleId?: string;
+}
+const Widget = ({ type, articleId }: IWidget) => {
+  const id = articleId?.split("_");
+  let tocWidget = <></>;
+  if (id && id.length > 0) {
+    const sentId = id[0].split("-");
+    if (sentId.length > 1) {
+      tocWidget = (
+        <PaliTextToc book={parseInt(sentId[0])} para={parseInt(sentId[1])} />
+      );
+    }
+  }
+
+  return (
+    <ToolButton title="目录" icon={<MenuOutlined />} content={tocWidget} />
+  );
+};
+
+export default Widget;

+ 55 - 0
dashboard/src/components/dict/Dictionary.tsx

@@ -0,0 +1,55 @@
+import { useEffect, useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { Layout, Affix, Col, Row } from "antd";
+
+import DictSearch from "./DictSearch";
+import SearchVocabulary from "./SearchVocabulary";
+
+const { Content } = Layout;
+
+interface IWidget {
+  word?: string;
+  compact?: boolean;
+}
+const Widget = ({ word, compact = false }: IWidget) => {
+  const navigate = useNavigate();
+  const [wordSearch, setWordSearch] = useState<string>();
+  const [container, setContainer] = useState<HTMLDivElement | null>(null);
+
+  useEffect(() => {
+    setWordSearch(word);
+  }, [word]);
+  const onSearch = (value: string) => {
+    console.log("onSearch", value);
+    setWordSearch(value);
+    if (compact === false) {
+      navigate("/dict/" + value);
+    }
+  };
+  return (
+    <div ref={setContainer}>
+      <Affix offsetTop={0} target={compact ? () => container : undefined}>
+        <div style={{ backgroundColor: "gainsboro" }}>
+          <Row style={{ paddingTop: "0.5em", paddingBottom: "0.5em" }}>
+            {compact ? <></> : <Col flex="auto"></Col>}
+            <Col flex="560px">
+              <SearchVocabulary onSearch={onSearch} />
+            </Col>
+            {compact ? <></> : <Col flex="auto"></Col>}
+          </Row>
+        </div>
+      </Affix>
+      <Content style={{ minHeight: 700 }}>
+        <Row>
+          {compact ? <></> : <Col flex="auto"></Col>}
+          <Col flex="1260px">
+            <DictSearch word={wordSearch} compact={compact} />
+          </Col>
+          {compact ? <></> : <Col flex="auto"></Col>}
+        </Row>
+      </Content>
+    </div>
+  );
+};
+
+export default Widget;

+ 106 - 0
dashboard/src/components/dict/SearchVocabulary.tsx

@@ -0,0 +1,106 @@
+import { get } from "../../request";
+import { IVocabularyListResponse } from "../api/Dict";
+import { useState } from "react";
+import { AutoComplete, Input, Typography } from "antd";
+import { DictIcon } from "../../assets/icon";
+
+const { Text } = Typography;
+
+interface ValueType {
+  key?: string;
+  label: React.ReactNode;
+  value: string | number;
+}
+interface IWidget {
+  onSearch?: Function;
+}
+const Widget = ({ onSearch }: IWidget) => {
+  const [options, setOptions] = useState<ValueType[]>([]);
+  const [fetching, setFetching] = useState(false);
+  const [searchKey, setSearchKey] = useState<string>("");
+
+  const renderItem = (title: string, count: number, meaning?: string) => ({
+    value: title,
+    label: (
+      <div>
+        <div
+          style={{
+            display: "flex",
+            justifyContent: "space-between",
+          }}
+        >
+          {title}
+          <span>
+            <DictIcon /> {count}
+          </span>
+        </div>
+        <div>
+          <Text type="secondary">{meaning}</Text>
+        </div>
+      </div>
+    ),
+  });
+
+  const search = (value: string) => {
+    console.log("search", value);
+    if (value === "") {
+      return;
+    }
+
+    get<IVocabularyListResponse>(`/v2/vocabulary?view=key&key=${value}`)
+      .then((json) => {
+        const words: ValueType[] = json.data.rows.map((item) => {
+          return renderItem(item.word, item.count, item.meaning);
+        });
+        setOptions(words);
+      })
+      .finally(() => {
+        console.log("finally", searchKey);
+        setFetching(false);
+        if (searchKey !== "") {
+          //setSearchKey("");
+          //setFetching(true);
+          //search(searchKey);
+        }
+      });
+  };
+  return (
+    <div style={{ width: "100%" }}>
+      <AutoComplete
+        style={{ width: "100%" }}
+        popupClassName="certain-category-search-dropdown"
+        dropdownMatchSelectWidth={400}
+        options={options}
+        onSearch={(value: string) => {
+          console.log("auto complete on search", value);
+          if (fetching) {
+            console.log("fetching");
+            setSearchKey(value);
+          } else {
+            setFetching(true);
+            setSearchKey("");
+            search(value);
+          }
+        }}
+        onSelect={(value: string, option: ValueType) => {
+          if (typeof onSearch !== "undefined") {
+            onSearch(value);
+          }
+        }}
+      >
+        <Input.Search
+          size="large"
+          placeholder="input here"
+          onSearch={(value: string) => {
+            console.log("on search", value);
+            if (typeof onSearch !== "undefined") {
+              onSearch(value);
+            }
+          }}
+        />
+      </AutoComplete>
+    </div>
+  );
+};
+
+export default Widget;