EditableTocTree.tsx 5.9 KB

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