BookTree.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import { useIntl } from "react-intl";
  2. import { useState, useEffect, type Key } from "react";
  3. import { DownOutlined } from "@ant-design/icons";
  4. import {
  5. Badge,
  6. Button,
  7. Flex,
  8. Switch,
  9. Tree,
  10. Typography,
  11. Space,
  12. Skeleton,
  13. } from "antd";
  14. import { get } from "../../request";
  15. import TocStyleSelect from "./TocStyleSelect";
  16. import type { IPaliBookListResponse } from "../../api/Corpus";
  17. import type { ITocTree } from "./BookTreeList";
  18. import { PaliToEn } from "../../utils";
  19. import PaliText from "../template/Wbw/PaliText";
  20. import type { IFtsData } from "../fts/FtsBookList";
  21. import type { EventDataNode } from "antd/lib/tree";
  22. const { Text } = Typography;
  23. interface IWidgetBookTree {
  24. root?: string;
  25. path?: string[];
  26. multiSelect?: boolean;
  27. multiSelectable?: boolean;
  28. books?: IFtsData[];
  29. onChange?: (selectedKeys: Key[], path?: string[]) => void;
  30. onSelect?: (key?: Key) => void;
  31. onRootChange?: (value: string) => void;
  32. }
  33. const BookTreeWidget = ({
  34. root,
  35. multiSelect = false,
  36. multiSelectable = true,
  37. books,
  38. onChange,
  39. onSelect,
  40. onRootChange,
  41. }: IWidgetBookTree) => {
  42. const intl = useIntl();
  43. const [treeData, setTreeData] = useState<ITocTree[]>([]);
  44. const [selectedKeys, setSelectedKeys] = useState<Key[]>([]);
  45. const [isMultiSelect, setIsMultiSelect] = useState(multiSelect);
  46. const [currTocStyle, setCurrTocStyle] = useState<string>();
  47. const [loading, setLoading] = useState<boolean>(false);
  48. useEffect(() => {
  49. setIsMultiSelect(multiSelect);
  50. }, [multiSelect]);
  51. useEffect(() => {
  52. let tocStyle = "default";
  53. if (typeof root !== "undefined") {
  54. tocStyle = root;
  55. } else {
  56. const store = localStorage.getItem("pali_path_root");
  57. if (store) {
  58. tocStyle = store;
  59. }
  60. }
  61. fetchBookTree(tocStyle);
  62. setCurrTocStyle(tocStyle);
  63. }, [root]);
  64. const fetchBookTree = async (value: string) => {
  65. setLoading(true);
  66. try {
  67. const treeMap = (params: IPaliBookListResponse): ITocTree => ({
  68. title: params.name,
  69. dir: PaliToEn(params.name),
  70. key: params.tag.join(),
  71. tag: params.tag,
  72. children: Array.isArray(params.children)
  73. ? params.children.map(treeMap)
  74. : [],
  75. });
  76. const setPathToNode = (nodes: ITocTree[], path: string[]) => {
  77. nodes.forEach((node) => {
  78. node.path = [...path, node.title];
  79. setPathToNode(node.children, node.path);
  80. });
  81. };
  82. const json = await get<IPaliBookListResponse[]>(
  83. `/v2/pali-book-category/${value}`
  84. );
  85. const newTree: ITocTree[] = json.map(treeMap);
  86. setPathToNode(newTree, []);
  87. console.log("root", newTree);
  88. setTreeData(newTree);
  89. } catch (error) {
  90. console.error("获取目录树失败:", error);
  91. } finally {
  92. setLoading(false);
  93. }
  94. };
  95. const handleClearSelection = () => {
  96. setSelectedKeys([]);
  97. if (typeof onChange !== "undefined") {
  98. onChange([], []);
  99. }
  100. };
  101. const handleSelect = (
  102. selectedKeys: Key[],
  103. info: {
  104. selected: boolean;
  105. selectedNodes: ITocTree[];
  106. node: EventDataNode<ITocTree>;
  107. event: string;
  108. }
  109. ) => {
  110. console.log("tree selected", selectedKeys, info);
  111. setSelectedKeys(selectedKeys);
  112. const node: ITocTree = info.node;
  113. if (typeof onChange !== "undefined") {
  114. onChange(selectedKeys, node.path);
  115. }
  116. if (typeof onSelect !== "undefined") {
  117. onSelect(selectedKeys.length > 0 ? selectedKeys[0] : undefined);
  118. }
  119. };
  120. const titleRender = (node: ITocTree) => {
  121. //标签数量
  122. const tags = books?.filter((book) => {
  123. return node.tag.every((el) => {
  124. return book.tags?.map((item) => item.name).includes(el);
  125. });
  126. });
  127. const count = tags?.length;
  128. return (
  129. <Space>
  130. <PaliText text={node.title} />
  131. {count ? (
  132. <Badge size="small" color="gray" count={count} dot={false} />
  133. ) : null}
  134. </Space>
  135. );
  136. };
  137. return (
  138. <Flex vertical style={{ padding: 10, width: "100%" }} gap="middle">
  139. <Flex justify="space-between" align="center">
  140. <Text>目录</Text>
  141. <TocStyleSelect
  142. style={currTocStyle}
  143. onChange={(value: string) => {
  144. console.log(`selected ${value}`);
  145. localStorage.setItem("pali_path_root", value);
  146. if (typeof onRootChange !== "undefined") {
  147. onRootChange(value);
  148. }
  149. setCurrTocStyle(value);
  150. fetchBookTree(value);
  151. }}
  152. />
  153. </Flex>
  154. <Flex justify="space-between" align="center">
  155. <Button onClick={handleClearSelection}>
  156. {intl.formatMessage({
  157. id: "buttons.remove.selected",
  158. })}
  159. </Button>
  160. {multiSelectable ? (
  161. <Flex align="center" gap="small">
  162. <Text>
  163. {intl.formatMessage({
  164. id: "buttons.multiple.select",
  165. })}
  166. </Text>
  167. <Switch
  168. size="small"
  169. defaultChecked={multiSelect}
  170. onChange={(checked) => {
  171. setIsMultiSelect(checked);
  172. }}
  173. />
  174. </Flex>
  175. ) : null}
  176. </Flex>
  177. {loading ? (
  178. <Skeleton />
  179. ) : (
  180. <Tree
  181. selectedKeys={selectedKeys}
  182. multiple={isMultiSelect}
  183. showLine
  184. switcherIcon={<DownOutlined />}
  185. defaultExpandedKeys={["sutta"]}
  186. onSelect={handleSelect}
  187. treeData={treeData}
  188. titleRender={titleRender}
  189. />
  190. )}
  191. </Flex>
  192. );
  193. };
  194. export default BookTreeWidget;