ArticleEdit.tsx 8.5 KB

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