2
0

TaskBuilderProjects.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import {
  2. Button,
  3. Divider,
  4. Input,
  5. message,
  6. Modal,
  7. Space,
  8. Steps,
  9. Tag,
  10. Typography,
  11. } from "antd";
  12. import { useState } from "react";
  13. import Workflow from "./Workflow";
  14. import type {
  15. IProjectTreeInsertRequest,
  16. IProjectTreeResponse,
  17. IProjectUpdateRequest,
  18. ITaskData,
  19. ITaskGroupInsertData,
  20. ITaskGroupInsertRequest,
  21. ITaskGroupResponse,
  22. } from "../../api/task";
  23. import { post } from "../../request";
  24. import TaskBuilderProp, { type IParam, IProp } from "./TaskBuilderProp";
  25. const { Paragraph, Text } = Typography;
  26. interface IBuildProjects {
  27. onChange?: (titles: string[]) => void;
  28. }
  29. const BuildProjects = ({ onChange }: IBuildProjects) => {
  30. const [projectsTitle, setProjectsTitle] = useState<string[]>([]);
  31. const [projectTitle, setProjectTitle] = useState<string>("");
  32. const [projectTitlePerf, setProjectTitlePerf] = useState<string>("01");
  33. const [projectsCount, setProjectsCount] = useState<number>(0);
  34. const buildTitles = (base: string, perf: string, count: number): string[] => {
  35. return Array.from(Array(count).keys()).map((item) => {
  36. const sn = parseInt(perf) + item;
  37. const extraZero = perf.length - sn.toString().length;
  38. let strSn: string = sn.toString();
  39. if (extraZero > 0) {
  40. strSn = Array(extraZero).fill("0").join("") + sn.toString();
  41. }
  42. return `${base}${strSn}`;
  43. });
  44. };
  45. return (
  46. <div>
  47. <div>
  48. 名称:
  49. <Input
  50. onChange={(e) => {
  51. setProjectTitle(e.target.value);
  52. const projects = buildTitles(
  53. e.target.value,
  54. projectTitlePerf,
  55. projectsCount
  56. );
  57. setProjectsTitle(projects);
  58. onChange && onChange(projects);
  59. }}
  60. />
  61. </div>
  62. <div>
  63. 后缀:
  64. <Input
  65. defaultValue={projectTitlePerf}
  66. onChange={(e) => {
  67. setProjectTitlePerf(e.target.value);
  68. const projects = buildTitles(
  69. projectTitle,
  70. e.target.value,
  71. projectsCount
  72. );
  73. setProjectsTitle(projects);
  74. onChange && onChange(projects);
  75. }}
  76. />
  77. </div>
  78. <div>
  79. 数量:
  80. <Input
  81. onChange={(e) => {
  82. setProjectsCount(parseInt(e.target.value));
  83. const projects = buildTitles(
  84. projectTitle,
  85. projectTitlePerf,
  86. parseInt(e.target.value)
  87. );
  88. setProjectsTitle(projects);
  89. onChange && onChange(projects);
  90. }}
  91. />
  92. </div>
  93. <div style={{ overflowY: "scroll", height: 370 }}>
  94. {projectsTitle.map((item, id) => {
  95. return (
  96. <Paragraph key={id}>
  97. <Tag>{`${id + 1}`}</Tag>
  98. {item}
  99. </Paragraph>
  100. );
  101. })}
  102. </div>
  103. </div>
  104. );
  105. };
  106. interface IModal {
  107. studioName?: string;
  108. parentId?: string;
  109. open?: boolean;
  110. onClose?: () => void;
  111. onDone?: () => void;
  112. }
  113. export const TaskBuilderProjectsModal = ({
  114. studioName,
  115. parentId,
  116. open = false,
  117. onClose,
  118. onDone,
  119. }: IModal) => {
  120. return (
  121. <>
  122. <Modal
  123. destroyOnHidden={true}
  124. width={1400}
  125. style={{ top: 10 }}
  126. title={""}
  127. footer={false}
  128. open={open}
  129. onOk={onClose}
  130. onCancel={onClose}
  131. >
  132. <TaskBuilderProjects
  133. style={{ marginTop: 20 }}
  134. studioName={studioName}
  135. parentId={parentId}
  136. onDone={onDone}
  137. />
  138. </Modal>
  139. </>
  140. );
  141. };
  142. interface IWidget {
  143. studioName?: string;
  144. parentId?: string;
  145. style?: React.CSSProperties;
  146. onDone?: () => void;
  147. }
  148. const TaskBuilderProjects = ({
  149. studioName,
  150. parentId,
  151. style,
  152. onDone,
  153. }: IWidget) => {
  154. const [current, setCurrent] = useState(0);
  155. const [workflow, setWorkflow] = useState<ITaskData[]>();
  156. const [projectsTitle, setProjectsTitle] = useState<string[]>();
  157. const [prop, setProp] = useState<IProp[]>();
  158. const [loading, setLoading] = useState(false);
  159. const [messages, setMessages] = useState<string[]>([]);
  160. const steps = [
  161. {
  162. title: "Projects",
  163. content: <BuildProjects onChange={setProjectsTitle} />,
  164. },
  165. {
  166. title: "工作流",
  167. content: (
  168. <Workflow
  169. studioName={studioName}
  170. onData={(data) => setWorkflow(data)}
  171. />
  172. ),
  173. },
  174. {
  175. title: "参数设置",
  176. content: (
  177. <div>
  178. <TaskBuilderProp
  179. workflow={workflow}
  180. onChange={(data: IProp[] | undefined) => setProp(data)}
  181. />
  182. </div>
  183. ),
  184. },
  185. {
  186. title: "生成",
  187. content: (
  188. <div>
  189. <div>
  190. <Space>
  191. <Text type="secondary">title</Text>
  192. <Text>{projectsTitle}</Text>
  193. </Space>
  194. </div>
  195. <div>
  196. <Paragraph>新增任务组:{projectsTitle}</Paragraph>
  197. <Paragraph>每个任务组任务数量:{workflow?.length}</Paragraph>
  198. <Paragraph>点击生成按钮生成</Paragraph>
  199. </div>
  200. <div>
  201. {messages?.map((item, id) => {
  202. return <div key={id}>{item}</div>;
  203. })}
  204. </div>
  205. </div>
  206. ),
  207. },
  208. ];
  209. const next = () => {
  210. setCurrent(current + 1);
  211. };
  212. const prev = () => {
  213. setCurrent(current - 1);
  214. };
  215. const items = steps.map((item) => ({ key: item.title, title: item.title }));
  216. return (
  217. <div style={style}>
  218. <Steps current={current} items={items} />
  219. <div className="steps-content" style={{ minHeight: 400 }}>
  220. {steps[current].content}
  221. </div>
  222. <Divider></Divider>
  223. <div style={{ display: "flex", justifyContent: "space-between" }}>
  224. <Button
  225. style={{ margin: "0 8px" }}
  226. disabled={current === 0}
  227. onClick={() => prev()}
  228. >
  229. Previous
  230. </Button>
  231. {current < steps.length - 1 && (
  232. <Button type="primary" onClick={() => next()}>
  233. Next
  234. </Button>
  235. )}
  236. {current === steps.length - 1 && (
  237. <Button
  238. type="primary"
  239. loading={loading}
  240. disabled={loading}
  241. onClick={async () => {
  242. if (!studioName || !parentId || !projectsTitle) {
  243. console.error("缺少参数", studioName, parentId, projectsTitle);
  244. return;
  245. }
  246. setLoading(true);
  247. //生成projects
  248. setMessages((origin) => [...origin, "正在生成任务组……"]);
  249. const url = "/v2/project-tree";
  250. const values: IProjectTreeInsertRequest = {
  251. studio_name: studioName,
  252. parent_id: parentId,
  253. data: projectsTitle.map((item, id) => {
  254. const project: IProjectUpdateRequest = {
  255. id: item,
  256. title: item,
  257. type: "instance",
  258. parent_id: null,
  259. res_id: id.toString(),
  260. };
  261. return project;
  262. }),
  263. };
  264. console.info("api request", url, values);
  265. const res = await post<
  266. IProjectTreeInsertRequest,
  267. IProjectTreeResponse
  268. >(url, values);
  269. console.info("api response", res);
  270. if (!res.ok) {
  271. setMessages((origin) => [...origin, "正在生成任务组失败"]);
  272. return;
  273. } else {
  274. setMessages((origin) => [...origin, "生成任务组成功"]);
  275. }
  276. //生成tasks
  277. setMessages((origin) => [...origin, "正在生成任务……"]);
  278. const taskUrl = "/v2/task-group";
  279. if (!workflow) {
  280. return;
  281. }
  282. console.debug("prop", prop);
  283. const taskData: ITaskGroupInsertData[] = res.data.rows
  284. .filter((value) => value.isLeaf)
  285. .map((project, pId) => {
  286. return {
  287. project_id: project.id,
  288. tasks: workflow.map((task, _tId) => {
  289. let newContent = task.description;
  290. prop
  291. ?.find((pValue) => pValue.taskId === task.id)
  292. ?.param?.forEach((value: IParam) => {
  293. const searchValue = `${value.key}=${value.value}`;
  294. const replaceValue =
  295. `${value.key}=` +
  296. (value.initValue + value.step * pId).toString();
  297. newContent = newContent?.replace(
  298. searchValue,
  299. replaceValue
  300. );
  301. });
  302. console.debug("description", newContent);
  303. return {
  304. ...task,
  305. type: "instance",
  306. description: newContent,
  307. };
  308. }),
  309. };
  310. });
  311. console.info("api request", taskUrl, taskData);
  312. const taskRes = await post<
  313. ITaskGroupInsertRequest,
  314. ITaskGroupResponse
  315. >(taskUrl, { data: taskData });
  316. console.info("api response", taskRes);
  317. if (taskRes.ok) {
  318. message.success("ok");
  319. setMessages((origin) => [...origin, "生成任务成功."]);
  320. setMessages((origin) => [
  321. ...origin,
  322. "生成任务" + taskRes.data.taskCount,
  323. ]);
  324. setMessages((origin) => [
  325. ...origin,
  326. "生成任务关联" + taskRes.data.taskRelationCount,
  327. ]);
  328. setMessages((origin) => [
  329. ...origin,
  330. "打开译经楼-我的任务查看已经生成的任务",
  331. ]);
  332. onDone && onDone();
  333. } else {
  334. setMessages((origin) => [
  335. ...origin,
  336. "生成任务失败。错误信息:" + taskRes.data,
  337. ]);
  338. }
  339. setLoading(false);
  340. }}
  341. >
  342. Done
  343. </Button>
  344. )}
  345. </div>
  346. </div>
  347. );
  348. };
  349. export default TaskBuilderProjects;