TaskReader.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import { useEffect, useState } from "react";
  2. import { Divider, Skeleton, Space, Tag, Typography, message } from "antd";
  3. import { CodeSandboxOutlined } from "@ant-design/icons";
  4. import { ITaskData, ITaskResponse, ITaskUpdateRequest } from "../api/task";
  5. import { get, patch } from "../../request";
  6. import User from "../auth/User";
  7. import TimeShow from "../general/TimeShow";
  8. import TaskEditButton, { TRelation } from "./TaskEditButton";
  9. import PreTask from "./PreTask";
  10. import Like from "../like/Like";
  11. import Assignees from "./Assignees";
  12. import PlanDate from "./PlanDate";
  13. import TaskTitle from "./TaskTitle";
  14. import TaskStatus from "./TaskStatus";
  15. import Description from "./Description";
  16. import Category from "./Category";
  17. import { useIntl } from "react-intl";
  18. const { Text } = Typography;
  19. export const Milestone = ({ task }: { task?: ITaskData }) => {
  20. const intl = useIntl();
  21. return task?.is_milestone ? (
  22. <Tag icon={<CodeSandboxOutlined />} color="error">
  23. {intl.formatMessage({ id: "labels.milestone" })}
  24. </Tag>
  25. ) : null;
  26. };
  27. interface IWidget {
  28. taskId?: string;
  29. onChange?: (data: ITaskData[]) => void;
  30. }
  31. const TaskReader = ({ taskId, onChange }: IWidget) => {
  32. const [openPreTask, setOpenPreTask] = useState(false);
  33. const [openNextTask, setOpenNextTask] = useState(false);
  34. const [task, setTask] = useState<ITaskData>();
  35. const [loading, setLoading] = useState(true);
  36. useEffect(() => {
  37. const url = `/v2/task/${taskId}`;
  38. console.info("task api request", url);
  39. setLoading(true);
  40. get<ITaskResponse>(url)
  41. .then((json) => {
  42. console.info("task api response", json);
  43. if (json.ok) {
  44. setTask(json.data);
  45. }
  46. })
  47. .finally(() => setLoading(false));
  48. }, [taskId]);
  49. const updatePreTask = (type: TRelation, data?: ITaskData | null) => {
  50. if (!taskId || !data) {
  51. return;
  52. }
  53. let setting: ITaskUpdateRequest = {
  54. id: taskId,
  55. studio_name: "",
  56. };
  57. if (type === "pre") {
  58. const hasPre = task?.pre_task?.find((value) => value.id === data.id);
  59. if (hasPre) {
  60. setting.pre_task_id = task?.pre_task
  61. ?.filter((value) => value.id !== data.id)
  62. .map((item) => item.id)
  63. .join();
  64. } else {
  65. const newRelation = task?.pre_task
  66. ? [...task.pre_task.map((item) => item.id), data.id]
  67. : [data.id];
  68. setting.pre_task_id = newRelation.join();
  69. }
  70. } else if (type === "next") {
  71. const hasPre = task?.next_task?.find((value) => value.id === data.id);
  72. if (hasPre) {
  73. setting.next_task_id = task?.next_task
  74. ?.filter((value) => value.id !== data.id)
  75. .map((item) => item.id)
  76. .join();
  77. } else {
  78. const newRelation = task?.next_task
  79. ? [...task.next_task.map((item) => item.id), data.id]
  80. : [data.id];
  81. setting.next_task_id = newRelation.join();
  82. }
  83. }
  84. const url = `/v2/task/${setting.id}`;
  85. console.info("api request", url, setting);
  86. patch<ITaskUpdateRequest, ITaskResponse>(url, setting).then((json) => {
  87. console.info("api response", json);
  88. if (json.ok) {
  89. message.success("Success");
  90. setTask(json.data);
  91. onChange && onChange([json.data]);
  92. } else {
  93. message.error(json.message);
  94. }
  95. });
  96. };
  97. return loading ? (
  98. <Skeleton active />
  99. ) : (
  100. <div>
  101. <div style={{ display: "flex", justifyContent: "space-between" }}>
  102. <Space>
  103. <TaskStatus task={task} />
  104. <Milestone task={task} />
  105. <PreTask
  106. task={task}
  107. open={openPreTask}
  108. type="pre"
  109. onClick={(data) => {
  110. updatePreTask("pre", data);
  111. setOpenPreTask(false);
  112. }}
  113. onTagClick={() => setOpenPreTask(true)}
  114. onClose={() => setOpenPreTask(false)}
  115. />
  116. <PreTask
  117. task={task}
  118. open={openNextTask}
  119. type="next"
  120. onClick={(data) => {
  121. updatePreTask("next", data);
  122. setOpenNextTask(false);
  123. }}
  124. onClose={() => setOpenNextTask(false)}
  125. onTagClick={() => setOpenNextTask(true)}
  126. />
  127. </Space>
  128. <div>
  129. <TaskEditButton
  130. task={task}
  131. onChange={(tasks: ITaskData[]) => {
  132. setTask(tasks.find((value) => value.id === taskId));
  133. onChange && onChange(tasks);
  134. }}
  135. onPreTask={(type: TRelation) => {
  136. if (type === "pre") {
  137. setOpenPreTask(true);
  138. } else if (type === "next") {
  139. setOpenNextTask(true);
  140. }
  141. }}
  142. />
  143. </div>
  144. </div>
  145. <TaskTitle
  146. task={task}
  147. onChange={(data) => {
  148. setTask(data[0]);
  149. onChange && onChange(data);
  150. }}
  151. />
  152. <div style={{ display: "flex", flexDirection: "column" }}>
  153. <Space>
  154. <User {...task?.editor} />
  155. <TimeShow updatedAt={task?.updated_at} />
  156. <Like resId={task?.id} resType="task" />
  157. </Space>
  158. <Space>
  159. <Text type="secondary" key={"2"}>
  160. 执行人
  161. </Text>
  162. <User key={"executor"} {...task?.executor} />
  163. </Space>
  164. <Space>
  165. <Text type="secondary" key={"1"}>
  166. 指派给
  167. </Text>
  168. <Assignees
  169. key={"assignees"}
  170. task={task}
  171. onChange={(data) => {
  172. setTask(data[0]);
  173. onChange && onChange(data);
  174. }}
  175. />
  176. </Space>
  177. <Space>
  178. <Text type="secondary">起止日期</Text>
  179. <div style={{ width: 400 }}>
  180. <PlanDate />
  181. </div>
  182. </Space>
  183. <Space>
  184. <Text type="secondary">类别</Text>
  185. <Category
  186. task={task}
  187. onChange={(data) => {
  188. setTask(data[0]);
  189. onChange && onChange(data);
  190. }}
  191. />
  192. </Space>
  193. </div>
  194. <Divider />
  195. <Description
  196. task={task}
  197. onChange={(data) => {
  198. setTask(data[0]);
  199. onChange && onChange(data);
  200. }}
  201. />
  202. </div>
  203. );
  204. };
  205. export default TaskReader;