EditableTocTree.tsx 5.3 KB

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