visuddhinanda 2 лет назад
Родитель
Сommit
b3f7c22599
1 измененных файлов с 263 добавлено и 0 удалено
  1. 263 0
      dashboard/src/components/article/ToolButtonNav.tsx

+ 263 - 0
dashboard/src/components/article/ToolButtonNav.tsx

@@ -0,0 +1,263 @@
+import { useEffect, useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { Button, message, Space, Tag, Tree, Typography } from "antd";
+import { CompassOutlined, ReloadOutlined } from "@ant-design/icons";
+
+import ToolButton from "./ToolButton";
+import { IUser } from "../auth/User";
+import { useAppSelector } from "../../hooks";
+import { sentenceList } from "../../reducers/sentence";
+import ToolButtonNavMore from "./ToolButtonNavMore";
+import ToolButtonNavSliceTitle from "./ToolButtonNavSliceTitle";
+
+const { Text } = Typography;
+
+interface IPrTreeData {
+  book: number;
+  paragraph: number;
+  word_start: number;
+  word_end: number;
+  channel_id: string;
+  content: string;
+  pr_count: number;
+}
+interface IPrTreeRequestData {
+  book: number;
+  paragraph: number;
+  word_start: number;
+  word_end: number;
+}
+interface IPrData {
+  title: string;
+  children_count: number;
+  editor?: IUser;
+}
+interface IPrTreeRequest {
+  data: IPrTreeRequestData[];
+}
+interface IPrTreeResponseData {
+  sentence: IPrTreeData;
+  pr: IPrData[];
+}
+interface IPrTreeResponse {
+  ok: boolean;
+  message: string;
+  data: { rows: IPrTreeResponseData[]; count: number };
+}
+interface DataNode {
+  title: React.ReactNode;
+  key: string;
+  isLeaf?: boolean;
+  children?: DataNode[];
+}
+interface ISlice {
+  id: number;
+  para: string;
+  len: number;
+}
+interface IWidget {
+  type?: string;
+  articleId?: string;
+}
+const ToolButtonNavWidget = ({ type, articleId }: IWidget) => {
+  const [treeData, setTreeData] = useState<DataNode[]>([]);
+  const [slice, setSlice] = useState<number>(1);
+  const navigate = useNavigate();
+
+  const allSentList = useAppSelector(sentenceList);
+
+  const refresh = () => {
+    const divList = document.querySelectorAll("div.pcd_sent");
+
+    let sentList: string[] = [];
+    for (let index = 0; index < divList.length; index++) {
+      const element = divList[index];
+      const id = element.id.split("_");
+      sentList.push(id[1]);
+    }
+
+    //计算总字符数
+    let allStrLen = 0;
+    allSentList.forEach((value) => {
+      allStrLen += value.origin ? value.origin[0].length : 0;
+    });
+    const oneSliceLen = allStrLen / slice;
+
+    let paraSlice: ISlice[] = [];
+    let currSliceId = 0;
+    let currSliceLen = 0;
+    for (let index = 0; index < sentList.length; index++) {
+      const sent = sentList[index];
+      const currPara = sent.split("-").slice(0, 2).join("-");
+      if (!paraSlice.find((value) => value.para === currPara)) {
+        let paraLen = 0; //段落字符长度
+        allSentList
+          .filter(
+            (value) => value.id.split("-").slice(0, 2).join("-") === currPara
+          )
+          .map((item) => (item.origin ? item.origin[0] : ""))
+          .forEach((value) => {
+            paraLen += value.length;
+          });
+
+        //计算如果放进去或者不放进去哪个更接近块的预设大小
+        let next = currSliceId;
+        if (currSliceLen + paraLen <= oneSliceLen) {
+          //放进去还不到一个块,直接放进去
+          currSliceLen = currSliceLen + paraLen;
+        } else if (currSliceLen === 0) {
+          //当前块里没东西直接放进去
+          if (paraLen >= oneSliceLen) {
+            //此块比一个块大
+            next = currSliceId + 1;
+            currSliceLen = 0;
+          } else {
+            currSliceLen = paraLen;
+          }
+        } else {
+          //放进去超过一个块,需要比较是放进去好还是不放进去好
+          const remain = oneSliceLen - currSliceLen;
+          const extra = currSliceLen + paraLen - oneSliceLen;
+          if (remain < extra) {
+            //移到下一个块
+            currSliceId++;
+            next = currSliceId;
+            currSliceLen = paraLen;
+          } else {
+            //放这个块
+            currSliceLen += paraLen;
+          }
+        }
+        paraSlice.push({ id: currSliceId, para: currPara, len: paraLen });
+        currSliceId = next;
+      }
+    }
+
+    const mSlice: DataNode[] = new Array(currSliceId + 1)
+      .fill(1)
+      .map((item, index) => {
+        let sliceStrLen = 0;
+        let sliceChildren: string[] = [];
+        const newTree: DataNode[] = paraSlice
+          .filter((value) => value.id === index)
+          .map((item) => {
+            const children = sentList
+              .filter(
+                (value) => value.split("-").slice(0, 2).join("-") === item.para
+              )
+              .map((item1) => {
+                const str = allSentList.find((value) => value.id === item1);
+                return {
+                  title: str
+                    ? str.origin
+                      ? str.origin[0].slice(0, 30)
+                      : item1
+                    : item1,
+                  key: item1,
+                };
+              });
+            sliceStrLen += item.len;
+            sliceChildren.push(item.para);
+            return {
+              title: item.para + "-" + item.len.toString(),
+              key: item.para,
+              children: children,
+            };
+          });
+
+        return {
+          title: (
+            <ToolButtonNavSliceTitle
+              label={
+                <Space>
+                  <Text>{`第${index + 1}组`}</Text>
+                  <Tag>{`${sliceStrLen}`}</Tag>
+                </Space>
+              }
+              onMenuClick={(key: string) => {
+                if (sliceChildren.length > 0) {
+                  const [book, para] = sliceChildren[0].split("-");
+                  const paraList = sliceChildren.map(
+                    (item) => item.split("-")[1]
+                  );
+                  let url = `/article/para/${book}?par=${paraList.join(",")}`;
+                  console.log("url", url);
+
+                  switch (key) {
+                    case "copy-link":
+                      const fullUrl =
+                        process.env.REACT_APP_WEB_HOST +
+                        process.env.PUBLIC_URL +
+                        url;
+                      navigator.clipboard.writeText(fullUrl).then(() => {
+                        message.success("链接地址已经拷贝到剪贴板");
+                      });
+
+                      break;
+                    case "open":
+                      navigate(url);
+                      break;
+                  }
+                }
+              }}
+            />
+          ),
+          key: `slice_${index}`,
+          children: newTree,
+        };
+      });
+    if (mSlice.length > 1) {
+      setTreeData(mSlice);
+    } else if (mSlice.length === 1) {
+      setTreeData(mSlice[0].children ? mSlice[0].children : []);
+    }
+  };
+
+  useEffect(refresh, [slice]);
+  return (
+    <ToolButton
+      title="导航"
+      icon={<CompassOutlined />}
+      content={
+        <>
+          <div style={{ textAlign: "right" }}>
+            <Space>
+              <Button
+                onClick={() => {
+                  refresh();
+                }}
+                size="small"
+                type="link"
+                icon={<ReloadOutlined />}
+              />
+              <ToolButtonNavMore
+                onSliceChange={(value: number) => {
+                  console.log(`selected ${value}`);
+                  setSlice(value);
+                }}
+              />
+            </Space>
+          </div>
+
+          <Tree
+            treeData={treeData}
+            titleRender={(node) => {
+              const ele = document.getElementById(node.key);
+              return (
+                <div
+                  onClick={() => {
+                    ele?.scrollIntoView();
+                  }}
+                >
+                  {node.title}
+                </div>
+              );
+            }}
+          />
+        </>
+      }
+    />
+  );
+};
+
+export default ToolButtonNavWidget;