EditableTocTree.tsx 5.8 KB

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