ArticleEdit.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { useRef, useState } from "react";
  2. import { useIntl } from "react-intl";
  3. import {
  4. ProForm,
  5. ProFormInstance,
  6. ProFormSwitch,
  7. ProFormText,
  8. ProFormTextArea,
  9. } from "@ant-design/pro-components";
  10. import { Alert, Button, Form, message, Result } from "antd";
  11. import { get, put } from "../../request";
  12. import {
  13. IArticleDataRequest,
  14. IArticleResponse,
  15. } from "../../components/api/Article";
  16. import LangSelect from "../../components/general/LangSelect";
  17. import PublicitySelect from "../../components/studio/PublicitySelect";
  18. import MDEditor from "@uiw/react-md-editor";
  19. import ArticlePrevDrawer from "../../components/article/ArticlePrevDrawer";
  20. import { IStudio } from "../auth/Studio";
  21. import ArticleEditTools from "./ArticleEditTools";
  22. interface IFormData {
  23. uid: string;
  24. title: string;
  25. subtitle: string;
  26. summary?: string | null;
  27. content?: string;
  28. content_type?: string;
  29. status: number;
  30. lang: string;
  31. to_tpl?: boolean;
  32. }
  33. interface IWidget {
  34. studioName?: string;
  35. articleId?: string;
  36. anthologyId?: string;
  37. resetButton?: "reset" | "cancel";
  38. onReady?: Function;
  39. onLoad?: Function;
  40. onChange?: Function;
  41. onCancel?: Function;
  42. onSubmit?: Function;
  43. }
  44. const ArticleEditWidget = ({
  45. studioName,
  46. articleId,
  47. anthologyId,
  48. resetButton = "reset",
  49. onReady,
  50. onLoad,
  51. onChange,
  52. onCancel,
  53. onSubmit,
  54. }: IWidget) => {
  55. const intl = useIntl();
  56. const [unauthorized, setUnauthorized] = useState(false);
  57. const [readonly, setReadonly] = useState(false);
  58. const [content, setContent] = useState<string>();
  59. const [owner, setOwner] = useState<IStudio>();
  60. const formRef = useRef<ProFormInstance>();
  61. const [title, setTitle] = useState<string>();
  62. return unauthorized ? (
  63. <Result
  64. status="403"
  65. title="无权访问"
  66. subTitle="您无权访问该内容。您可能没有登录,或者内容的所有者没有给您所需的权限。"
  67. extra={<></>}
  68. />
  69. ) : (
  70. <>
  71. {readonly ? (
  72. <Alert
  73. message={`该资源为只读,如果需要修改,请联络拥有者${owner?.nickName}分配权限。`}
  74. type="warning"
  75. closable
  76. action={
  77. <Button disabled size="small" type="text">
  78. 详情
  79. </Button>
  80. }
  81. />
  82. ) : undefined}
  83. <div style={{ display: "flex", justifyContent: "space-between" }}>
  84. <span></span>
  85. <ArticleEditTools
  86. studioName={studioName}
  87. articleId={articleId}
  88. title={title}
  89. />
  90. </div>
  91. <ProForm<IFormData>
  92. formRef={formRef}
  93. submitter={{
  94. // 完全自定义整个区域
  95. render: (props, doms) => {
  96. console.log(props);
  97. return [
  98. <Button
  99. key="rest"
  100. onClick={() => {
  101. if (resetButton === "reset") {
  102. props.form?.resetFields();
  103. } else {
  104. if (typeof onCancel !== "undefined") {
  105. onCancel();
  106. }
  107. }
  108. }}
  109. >
  110. {resetButton === "reset" ? "重置" : "取消"}
  111. </Button>,
  112. <Button
  113. type="primary"
  114. key="submit"
  115. onClick={() => props.form?.submit?.()}
  116. >
  117. 提交
  118. </Button>,
  119. ];
  120. },
  121. }}
  122. onFinish={async (values: IFormData) => {
  123. const request: IArticleDataRequest = {
  124. uid: articleId ? articleId : "",
  125. title: values.title,
  126. subtitle: values.subtitle,
  127. summary: values.summary,
  128. content: values.content,
  129. content_type: "markdown",
  130. status: values.status,
  131. lang: values.lang,
  132. to_tpl: values.to_tpl,
  133. anthology_id: anthologyId,
  134. };
  135. const url = `/v2/article/${articleId}`;
  136. console.info("save url", url, request);
  137. put<IArticleDataRequest, IArticleResponse>(url, request)
  138. .then((res) => {
  139. console.debug("save response", res);
  140. if (res.ok) {
  141. if (typeof onChange !== "undefined") {
  142. onChange(res.data);
  143. }
  144. if (typeof onSubmit !== "undefined") {
  145. onSubmit(res.data);
  146. }
  147. formRef.current?.setFieldValue("content", res.data.content);
  148. message.success(intl.formatMessage({ id: "flashes.success" }));
  149. } else {
  150. message.error(res.message);
  151. }
  152. })
  153. .catch((e: IArticleResponse) => {
  154. message.error(e.message);
  155. });
  156. }}
  157. request={async () => {
  158. const url = `/v2/article/${articleId}`;
  159. console.info("url", url);
  160. const res = await get<IArticleResponse>(url);
  161. console.log("article", res);
  162. let mTitle: string,
  163. mReadonly = false;
  164. if (res.ok) {
  165. setOwner(res.data.studio);
  166. mReadonly = res.data.role === "editor" ? false : true;
  167. setReadonly(mReadonly);
  168. mTitle = res.data.title;
  169. setContent(res.data.content);
  170. setTitle(res.data.title);
  171. } else {
  172. setUnauthorized(true);
  173. mTitle = "无权访问";
  174. }
  175. if (typeof onReady !== "undefined") {
  176. onReady(
  177. mTitle,
  178. mReadonly,
  179. res.data.studio?.realName,
  180. res.data.parent_uid
  181. );
  182. }
  183. return {
  184. uid: res.data.uid,
  185. title: res.data.title,
  186. subtitle: res.data.subtitle,
  187. summary: res.data.summary,
  188. content: res.data.content,
  189. content_type: res.data.content_type,
  190. lang: res.data.lang,
  191. status: res.data.status,
  192. };
  193. }}
  194. >
  195. <ProForm.Group>
  196. <ProFormText
  197. width="md"
  198. name="title"
  199. required
  200. label={intl.formatMessage({
  201. id: "forms.fields.title.label",
  202. })}
  203. rules={[
  204. {
  205. required: true,
  206. message: intl.formatMessage({
  207. id: "forms.message.title.required",
  208. }),
  209. },
  210. ]}
  211. />
  212. <ProFormText
  213. width="md"
  214. name="subtitle"
  215. label={intl.formatMessage({
  216. id: "forms.fields.subtitle.label",
  217. })}
  218. />
  219. </ProForm.Group>
  220. <ProForm.Group>
  221. <LangSelect width="md" />
  222. <PublicitySelect width="md" disable={["public_no_list"]} />
  223. </ProForm.Group>
  224. <ProForm.Group>
  225. <ProFormTextArea
  226. name="summary"
  227. width="lg"
  228. label={intl.formatMessage({
  229. id: "forms.fields.summary.label",
  230. })}
  231. />
  232. </ProForm.Group>
  233. <Form.Item
  234. name="content"
  235. style={{ width: "100%" }}
  236. label={
  237. <>
  238. {intl.formatMessage({
  239. id: "forms.fields.content.label",
  240. })}
  241. {articleId ? (
  242. <ArticlePrevDrawer
  243. trigger={<Button>预览</Button>}
  244. articleId={articleId}
  245. content={content}
  246. />
  247. ) : undefined}
  248. </>
  249. }
  250. >
  251. <MDEditor
  252. onChange={(value) => setContent(value)}
  253. height={450}
  254. minHeight={200}
  255. style={{ width: "100%" }}
  256. />
  257. </Form.Item>
  258. <ProForm.Group>
  259. <ProFormSwitch
  260. name="to_tpl"
  261. label="转换为模版"
  262. disabled={anthologyId ? false : true}
  263. />
  264. </ProForm.Group>
  265. </ProForm>
  266. </>
  267. );
  268. };
  269. export default ArticleEditWidget;