DiscussionCreate.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import { useIntl } from "react-intl";
  2. import { Form, message } from "antd";
  3. import {
  4. ProForm,
  5. type ProFormInstance,
  6. ProFormText,
  7. ProFormTextArea,
  8. } from "@ant-design/pro-components";
  9. import ReactQuill from "react-quill";
  10. import "react-quill/dist/quill.snow.css";
  11. import type { IComment } from "./DiscussionItem";
  12. import { post } from "../../request";
  13. import type {
  14. ICommentApiData,
  15. ICommentRequest,
  16. ICommentResponse,
  17. } from "../../api/Comment";
  18. import { useAppSelector } from "../../hooks";
  19. import { currentUser as _currentUser } from "../../reducers/current-user";
  20. import { useEffect, useRef, useState } from "react";
  21. import MDEditor from "@uiw/react-md-editor";
  22. import type { TDiscussionType } from "./Discussion";
  23. import { discussionCountUpgrade } from "./DiscussionCount";
  24. export const toIComment = (value: ICommentApiData): IComment => {
  25. return {
  26. id: value.id,
  27. resId: value.res_id,
  28. resType: value.res_type,
  29. type: value.type,
  30. user: value.editor,
  31. title: value.title,
  32. parent: value.parent,
  33. tplId: value.tpl_id,
  34. content: value.content,
  35. createdAt: value.created_at,
  36. updatedAt: value.updated_at,
  37. };
  38. };
  39. interface IWidget {
  40. resId?: string;
  41. resType?: string; //TODO change
  42. parent?: string;
  43. topicId?: string;
  44. type?: TDiscussionType;
  45. topic?: IComment;
  46. contentType?: TContentType;
  47. onCreated?: Function;
  48. onTopicCreated?: Function;
  49. }
  50. const DiscussionCreateWidget = ({
  51. resId,
  52. resType,
  53. contentType = "html",
  54. parent,
  55. topicId,
  56. topic,
  57. type = "discussion",
  58. onCreated,
  59. onTopicCreated,
  60. }: IWidget) => {
  61. const intl = useIntl();
  62. const formRef = useRef<ProFormInstance | undefined>(undefined);
  63. const _currUser = useAppSelector(_currentUser);
  64. const [currParent, setCurrParent] = useState(parent);
  65. useEffect(() => setCurrParent(parent), [parent]);
  66. if (typeof _currUser === "undefined") {
  67. return <></>;
  68. } else {
  69. return (
  70. <div>
  71. <div>{_currUser?.nickName}:</div>
  72. <div>
  73. <ProForm<IComment>
  74. formRef={formRef}
  75. autoFocusFirstInput={false}
  76. onFinish={async (values) => {
  77. //新建
  78. console.log("create", resId, resType, currParent, topic);
  79. console.log("value", values);
  80. let newParent: string | undefined;
  81. if (typeof currParent === "undefined") {
  82. if (typeof topic !== "undefined" && topic.tplId) {
  83. /**
  84. * 在模版下跟帖
  85. * 先建立模版topic,再建立跟帖
  86. */
  87. const topicData: ICommentRequest = {
  88. res_id: resId,
  89. res_type: resType,
  90. title: topic.title,
  91. tpl_id: topic.tplId,
  92. content: topic.content,
  93. content_type: "markdown",
  94. type: topic.type,
  95. };
  96. const url = `/v2/discussion`;
  97. console.log("create topic api request", url, topicData);
  98. const newTopic = await post<
  99. ICommentRequest,
  100. ICommentResponse
  101. >(url, topicData);
  102. if (newTopic.ok) {
  103. discussionCountUpgrade(resId);
  104. setCurrParent(newTopic.data.id);
  105. newParent = newTopic.data.id;
  106. if (typeof onTopicCreated !== "undefined") {
  107. onTopicCreated(toIComment(newTopic.data));
  108. }
  109. } else {
  110. console.error("no parent id");
  111. return;
  112. }
  113. }
  114. }
  115. const url = `/v2/discussion`;
  116. const data: ICommentRequest = {
  117. res_id: resId,
  118. res_type: resType,
  119. parent: newParent ? newParent : currParent,
  120. topicId: topicId,
  121. title: values.title,
  122. content: values.content,
  123. content_type: contentType,
  124. type: topic ? topic.type : type,
  125. };
  126. console.info("api request", url, data);
  127. post<ICommentRequest, ICommentResponse>(url, data)
  128. .then((json) => {
  129. console.debug("new discussion api response", json);
  130. if (json.ok) {
  131. formRef.current?.resetFields();
  132. discussionCountUpgrade(resId);
  133. if (typeof onCreated !== "undefined") {
  134. onCreated(toIComment(json.data));
  135. }
  136. } else {
  137. message.error(json.message);
  138. }
  139. })
  140. .catch((e) => {
  141. message.error(e.message);
  142. });
  143. }}
  144. params={{}}
  145. >
  146. <ProForm.Group>
  147. <ProFormText
  148. name="title"
  149. width={"lg"}
  150. hidden={
  151. typeof currParent !== "undefined" ||
  152. typeof topic?.tplId !== "undefined"
  153. }
  154. label={intl.formatMessage({ id: "forms.fields.title.label" })}
  155. tooltip="最长为 24 位"
  156. placeholder={intl.formatMessage({
  157. id: "forms.message.question.required",
  158. })}
  159. rules={[
  160. { required: currParent || topic?.tplId ? false : true },
  161. ]}
  162. />
  163. </ProForm.Group>
  164. <ProForm.Group>
  165. {contentType === "text" ? (
  166. <ProFormTextArea
  167. name="content"
  168. label={intl.formatMessage({
  169. id: "forms.fields.content.label",
  170. })}
  171. placeholder={intl.formatMessage({
  172. id: "forms.fields.content.placeholder",
  173. })}
  174. />
  175. ) : contentType === "html" ? (
  176. <Form.Item
  177. name="content"
  178. label={intl.formatMessage({
  179. id: "forms.fields.content.label",
  180. })}
  181. tooltip="可以直接粘贴屏幕截图"
  182. >
  183. <ReactQuill
  184. theme="snow"
  185. style={{ height: 180 }}
  186. modules={{
  187. toolbar: [
  188. ["bold", "italic", "underline", "strike"],
  189. ["blockquote", "code-block"],
  190. [{ header: 1 }, { header: 2 }],
  191. [{ list: "ordered" }, { list: "bullet" }],
  192. [{ indent: "-1" }, { indent: "+1" }],
  193. [{ size: ["small", false, "large", "huge"] }],
  194. [{ header: [1, 2, 3, 4, 5, 6, false] }],
  195. ["link", "image", "video"],
  196. [{ color: [] }, { background: [] }],
  197. [{ font: [] }],
  198. [{ align: [] }],
  199. ],
  200. }}
  201. />
  202. </Form.Item>
  203. ) : contentType === "markdown" ? (
  204. <Form.Item
  205. name="content"
  206. rules={[
  207. {
  208. required:
  209. typeof currParent !== "undefined" ||
  210. typeof topic?.tplId !== "undefined",
  211. },
  212. ]}
  213. label={
  214. typeof currParent === "undefined" &&
  215. typeof topic?.tplId === "undefined"
  216. ? intl.formatMessage({
  217. id: "forms.message.question.description.option",
  218. })
  219. : intl.formatMessage({
  220. id: "forms.fields.replay.label",
  221. })
  222. }
  223. >
  224. <MDEditor
  225. textareaProps={{
  226. placeholder:
  227. "问题的详细描述" +
  228. (typeof currParent !== "undefined" &&
  229. typeof topic?.tplId !== "undefined"
  230. ? ""
  231. : "(选填)"),
  232. maxLength: 10000,
  233. }}
  234. />
  235. </Form.Item>
  236. ) : (
  237. <></>
  238. )}
  239. </ProForm.Group>
  240. </ProForm>
  241. </div>
  242. </div>
  243. );
  244. }
  245. };
  246. export default DiscussionCreateWidget;