ToolButtonNav.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import { useEffect, useState } from "react";
  2. import { useNavigate } from "react-router";
  3. import { Button, message, Space, Tag, Tree, Typography } from "antd";
  4. import { CompassOutlined, ReloadOutlined } from "@ant-design/icons";
  5. import ToolButton from "./ToolButton";
  6. import { useAppSelector } from "../../hooks";
  7. import { sentenceList } from "../../reducers/sentence";
  8. import ToolButtonNavMore from "./ToolButtonNavMore";
  9. import ToolButtonNavSliceTitle from "./ToolButtonNavSliceTitle";
  10. import { fullUrl } from "../../utils";
  11. const { Text } = Typography;
  12. interface DataNode {
  13. title: React.ReactNode;
  14. key: string;
  15. isLeaf?: boolean;
  16. children?: DataNode[];
  17. }
  18. interface ISlice {
  19. id: number;
  20. para: string;
  21. len: number;
  22. }
  23. interface IWidget {
  24. type?: string;
  25. articleId?: string;
  26. }
  27. const ToolButtonNavWidget = ({ type, articleId }: IWidget) => { // eslint-disable-line
  28. const [treeData, setTreeData] = useState<DataNode[]>([]);
  29. const [slice, setSlice] = useState<number>(1);
  30. const navigate = useNavigate();
  31. const allSentList = useAppSelector(sentenceList);
  32. const refresh = () => {
  33. const divList = document.querySelectorAll("div.pcd_sent");
  34. const sentList: string[] = [];
  35. for (let index = 0; index < divList.length; index++) {
  36. const element = divList[index];
  37. const id = element.id.split("_");
  38. sentList.push(id[1]);
  39. }
  40. //计算总字符数
  41. let allStrLen = 0;
  42. allSentList.forEach((value) => {
  43. allStrLen += value.origin ? value.origin[0].length : 0;
  44. });
  45. const oneSliceLen = allStrLen / slice;
  46. const paraSlice: ISlice[] = [];
  47. let currSliceId = 0;
  48. let currSliceLen = 0;
  49. for (let index = 0; index < sentList.length; index++) {
  50. const sent = sentList[index];
  51. const currPara = sent.split("-").slice(0, 2).join("-");
  52. if (!paraSlice.find((value) => value.para === currPara)) {
  53. let paraLen = 0; //段落字符长度
  54. allSentList
  55. .filter(
  56. (value) => value.id.split("-").slice(0, 2).join("-") === currPara
  57. )
  58. .map((item) => (item.origin ? item.origin[0] : ""))
  59. .forEach((value) => {
  60. paraLen += value.length;
  61. });
  62. //计算如果放进去或者不放进去哪个更接近块的预设大小
  63. let next = currSliceId;
  64. if (currSliceLen + paraLen <= oneSliceLen) {
  65. //放进去还不到一个块,直接放进去
  66. currSliceLen = currSliceLen + paraLen;
  67. } else if (currSliceLen === 0) {
  68. //当前块里没东西直接放进去
  69. if (paraLen >= oneSliceLen) {
  70. //此块比一个块大
  71. next = currSliceId + 1;
  72. currSliceLen = 0;
  73. } else {
  74. currSliceLen = paraLen;
  75. }
  76. } else {
  77. //放进去超过一个块,需要比较是放进去好还是不放进去好
  78. const remain = oneSliceLen - currSliceLen;
  79. const extra = currSliceLen + paraLen - oneSliceLen;
  80. if (remain < extra) {
  81. //移到下一个块
  82. currSliceId++;
  83. next = currSliceId;
  84. currSliceLen = paraLen;
  85. } else {
  86. //放这个块
  87. currSliceLen += paraLen;
  88. }
  89. }
  90. paraSlice.push({ id: currSliceId, para: currPara, len: paraLen });
  91. currSliceId = next;
  92. }
  93. }
  94. const mSlice: DataNode[] = new Array(currSliceId + 1)
  95. .fill(1)
  96. .map((_item, index) => {
  97. let sliceStrLen = 0;
  98. const sliceChildren: string[] = [];
  99. const newTree: DataNode[] = paraSlice
  100. .filter((value) => value.id === index)
  101. .map((item) => {
  102. const children = sentList
  103. .filter(
  104. (value) => value.split("-").slice(0, 2).join("-") === item.para
  105. )
  106. .map((item1) => {
  107. const str = allSentList.find((value) => value.id === item1);
  108. return {
  109. title: str
  110. ? str.origin
  111. ? str.origin[0].slice(0, 30)
  112. : item1
  113. : item1,
  114. key: item1,
  115. };
  116. });
  117. sliceStrLen += item.len;
  118. sliceChildren.push(item.para);
  119. return {
  120. title: item.para + "-" + item.len.toString(),
  121. key: item.para,
  122. children: children,
  123. };
  124. });
  125. return {
  126. title: (
  127. <ToolButtonNavSliceTitle
  128. label={
  129. <Space>
  130. <Text>{`第${index + 1}组`}</Text>
  131. <Tag>{`${sliceStrLen}`}</Tag>
  132. </Space>
  133. }
  134. onMenuClick={(key: string) => {
  135. if (sliceChildren.length > 0) {
  136. const [book, _para] = sliceChildren[0].split("-");
  137. const paraList = sliceChildren.map(
  138. (item) => item.split("-")[1]
  139. );
  140. const url = `/article/para/${book}?par=${paraList.join(",")}`;
  141. console.log("url", url);
  142. switch (key) {
  143. case "copy-link":
  144. navigator.clipboard.writeText(fullUrl(url)).then(() => {
  145. message.success("链接地址已经拷贝到剪贴板");
  146. });
  147. break;
  148. case "open":
  149. navigate(url);
  150. break;
  151. }
  152. }
  153. }}
  154. />
  155. ),
  156. key: `slice_${index}`,
  157. children: newTree,
  158. };
  159. });
  160. if (mSlice.length > 1) {
  161. setTreeData(mSlice);
  162. } else if (mSlice.length === 1) {
  163. setTreeData(mSlice[0].children ? mSlice[0].children : []);
  164. }
  165. };
  166. useEffect(refresh, [slice]);
  167. return (
  168. <ToolButton
  169. title="导航"
  170. icon={<CompassOutlined />}
  171. content={
  172. <>
  173. <div style={{ textAlign: "right" }}>
  174. <Space>
  175. <Button
  176. onClick={() => {
  177. refresh();
  178. }}
  179. size="small"
  180. type="link"
  181. icon={<ReloadOutlined />}
  182. />
  183. <ToolButtonNavMore
  184. onSliceChange={(value: number) => {
  185. console.log(`selected ${value}`);
  186. setSlice(value);
  187. }}
  188. />
  189. </Space>
  190. </div>
  191. <Tree
  192. treeData={treeData}
  193. titleRender={(node) => {
  194. const ele = document.getElementById(node.key);
  195. return (
  196. <div
  197. onClick={() => {
  198. ele?.scrollIntoView();
  199. }}
  200. >
  201. {node.title}
  202. </div>
  203. );
  204. }}
  205. />
  206. </>
  207. }
  208. />
  209. );
  210. };
  211. export default ToolButtonNavWidget;