EditableTocTree.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import { Button, message } from "antd";
  2. import { useEffect, useState } from "react";
  3. import { FolderOpenOutlined } from "@ant-design/icons";
  4. import { get as getUiLang } from "../../locales";
  5. import { get, post, put } from "../../request";
  6. import {
  7. IArticleCreateRequest,
  8. IArticleDataResponse,
  9. IArticleMapAddResponse,
  10. IArticleMapListResponse,
  11. IArticleMapUpdateRequest,
  12. IArticleResponse,
  13. } from "../api/Article";
  14. import ArticleListModal from "../article/ArticleListModal";
  15. import EditableTree, {
  16. ListNodeData,
  17. TreeNodeData,
  18. } from "../article/EditableTree";
  19. import ArticleEditDrawer from "../article/ArticleEditDrawer";
  20. import ArticleDrawer from "../article/ArticleDrawer";
  21. import { fullUrl, randomString } from "../../utils";
  22. interface IWidget {
  23. anthologyId?: string;
  24. studioName?: string;
  25. onSelect?: Function;
  26. }
  27. const EditableTocTreeWidget = ({
  28. anthologyId,
  29. studioName,
  30. onSelect,
  31. }: IWidget) => {
  32. const [tocData, setTocData] = useState<ListNodeData[]>([]);
  33. const [addArticle, setAddArticle] = useState<TreeNodeData>();
  34. const [articleId, setArticleId] = useState<string>();
  35. const [openEditor, setOpenEditor] = useState(false);
  36. const [updatedArticle, setUpdatedArticle] = useState<TreeNodeData>();
  37. const [openViewer, setOpenViewer] = useState(false);
  38. const [viewArticleId, setViewArticleId] = useState<string>();
  39. const save = (data?: ListNodeData[]) => {
  40. console.log("onSave", data);
  41. if (typeof data === "undefined") {
  42. return;
  43. }
  44. put<IArticleMapUpdateRequest, IArticleMapAddResponse>(
  45. `/v2/article-map/${anthologyId}`,
  46. {
  47. data: data.map((item) => {
  48. let title = "";
  49. if (typeof item.title === "string") {
  50. title = item.title;
  51. }
  52. //TODO 整一个string title
  53. return {
  54. article_id: item.key,
  55. level: item.level,
  56. title: title,
  57. children: item.children,
  58. };
  59. }),
  60. operation: "anthology",
  61. }
  62. )
  63. .finally(() => {})
  64. .then((json) => {
  65. if (json.ok) {
  66. message.success(json.data);
  67. } else {
  68. message.error(json.message);
  69. }
  70. })
  71. .catch((e) => console.error(e));
  72. };
  73. useEffect(() => {
  74. get<IArticleMapListResponse>(
  75. `/v2/article-map?view=anthology&id=${anthologyId}`
  76. ).then((json) => {
  77. if (json.ok) {
  78. const toc: ListNodeData[] = json.data.rows.map((item) => {
  79. return {
  80. key: item.article_id ? item.article_id : item.title,
  81. title: item.title,
  82. level: item.level,
  83. deletedAt: item.deleted_at,
  84. };
  85. });
  86. setTocData(toc);
  87. }
  88. });
  89. }, [anthologyId]);
  90. return (
  91. <div>
  92. <EditableTree
  93. treeData={tocData}
  94. addOnArticle={addArticle}
  95. addFileButton={
  96. <ArticleListModal
  97. studioName={studioName}
  98. trigger={<Button icon={<FolderOpenOutlined />}>添加</Button>}
  99. multiple={false}
  100. onSelect={(id: string, title: string) => {
  101. console.log("add article", id);
  102. const newNode: TreeNodeData = {
  103. key: randomString(),
  104. id: id,
  105. title: title,
  106. children: [],
  107. level: 1,
  108. };
  109. setAddArticle(newNode);
  110. }}
  111. />
  112. }
  113. updatedNode={updatedArticle}
  114. onChange={(data: ListNodeData[]) => {
  115. save(data);
  116. }}
  117. onSave={(data: ListNodeData[]) => {
  118. save(data);
  119. }}
  120. onAppend={async (
  121. node: TreeNodeData
  122. ): Promise<TreeNodeData | undefined> => {
  123. if (typeof studioName === "undefined") {
  124. return;
  125. }
  126. const res = await post<IArticleCreateRequest, IArticleResponse>(
  127. `/v2/article`,
  128. {
  129. title: "new article",
  130. lang: getUiLang(),
  131. studio: studioName,
  132. }
  133. );
  134. console.log(res);
  135. if (res.ok) {
  136. return {
  137. key: randomString(),
  138. id: res.data.uid,
  139. title: res.data.title,
  140. children: [],
  141. level: node.level + 1,
  142. };
  143. } else {
  144. return;
  145. }
  146. }}
  147. onNodeEdit={(key: string) => {
  148. setArticleId(key);
  149. setOpenEditor(true);
  150. }}
  151. onTitleClick={(
  152. e: React.MouseEvent<HTMLElement, MouseEvent>,
  153. node: TreeNodeData
  154. ) => {
  155. if (e.ctrlKey || e.metaKey) {
  156. window.open(fullUrl(`/article/article/${node.id}`), "_blank");
  157. } else {
  158. setViewArticleId(node.id);
  159. setOpenViewer(true);
  160. }
  161. }}
  162. />
  163. <ArticleEditDrawer
  164. articleId={articleId}
  165. open={openEditor}
  166. onClose={() => setOpenEditor(false)}
  167. onChange={(data: IArticleDataResponse) => {
  168. console.log("new title", data.title);
  169. setUpdatedArticle({
  170. key: randomString(),
  171. id: data.uid,
  172. title: data.title,
  173. level: 0,
  174. children: [],
  175. });
  176. }}
  177. />
  178. <ArticleDrawer
  179. articleId={viewArticleId}
  180. type="article"
  181. open={openViewer}
  182. onClose={() => setOpenViewer(false)}
  183. />
  184. </div>
  185. );
  186. };
  187. export default EditableTocTreeWidget;