TaskProjects.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { ActionType, ProTable } from "@ant-design/pro-components";
  2. import { useIntl } from "react-intl";
  3. import { Link } from "react-router-dom";
  4. import { Alert, Badge, Button, message, Modal, Popover } from "antd";
  5. import { Dropdown } from "antd";
  6. import {
  7. ExclamationCircleOutlined,
  8. DeleteOutlined,
  9. PlusOutlined,
  10. } from "@ant-design/icons";
  11. import { delete_, get } from "../../request";
  12. import { TChannelType } from "../api/Channel";
  13. import { PublicityValueEnum } from "../studio/table";
  14. import { IDeleteResponse } from "../api/Article";
  15. import { useEffect, useRef, useState } from "react";
  16. import { getSorterUrl } from "../../utils";
  17. import { TransferOutLinedIcon } from "../../assets/icon";
  18. import { IProjectData, IProjectListResponse } from "../api/task";
  19. import ProjectCreate from "./ProjectCreate";
  20. export interface IResNumberResponse {
  21. ok: boolean;
  22. message: string;
  23. data: {
  24. my: number;
  25. collaboration: number;
  26. };
  27. }
  28. export const renderBadge = (count: number, active = false) => {
  29. return (
  30. <Badge
  31. count={count}
  32. style={{
  33. marginBlockStart: -2,
  34. marginInlineStart: 4,
  35. color: active ? "#1890FF" : "#999",
  36. backgroundColor: active ? "#E6F7FF" : "#eee",
  37. }}
  38. />
  39. );
  40. };
  41. interface IWidget {
  42. studioName?: string;
  43. type?: string;
  44. disableChannels?: string[];
  45. channelType?: TChannelType;
  46. onSelect?: Function;
  47. }
  48. const ProjectListWidget = ({
  49. studioName,
  50. disableChannels,
  51. channelType,
  52. type,
  53. onSelect,
  54. }: IWidget) => {
  55. const intl = useIntl();
  56. const [activeKey, setActiveKey] = useState<React.Key | undefined>("instance");
  57. const [openCreate, setOpenCreate] = useState(false);
  58. useEffect(() => {
  59. ref.current?.reload();
  60. }, [disableChannels]);
  61. const showDeleteConfirm = (id: string, title: string) => {
  62. Modal.confirm({
  63. icon: <ExclamationCircleOutlined />,
  64. title:
  65. intl.formatMessage({
  66. id: "message.delete.confirm",
  67. }) +
  68. intl.formatMessage({
  69. id: "message.irrevocable",
  70. }),
  71. content: title,
  72. okText: intl.formatMessage({
  73. id: "buttons.delete",
  74. }),
  75. okType: "danger",
  76. cancelText: intl.formatMessage({
  77. id: "buttons.no",
  78. }),
  79. onOk() {
  80. const url = `/v2/channel/${id}`;
  81. console.log("delete api request", url);
  82. return delete_<IDeleteResponse>(url)
  83. .then((json) => {
  84. console.info("api response", json);
  85. if (json.ok) {
  86. message.success("删除成功");
  87. ref.current?.reload();
  88. } else {
  89. message.error(json.message);
  90. }
  91. })
  92. .catch((e) => console.log("Oops errors!", e));
  93. },
  94. });
  95. };
  96. const ref = useRef<ActionType>();
  97. return (
  98. <>
  99. <ProTable<IProjectData>
  100. actionRef={ref}
  101. columns={[
  102. {
  103. title: intl.formatMessage({
  104. id: "forms.fields.title.label",
  105. }),
  106. dataIndex: "title",
  107. width: 250,
  108. key: "title",
  109. tooltip: "过长会自动收缩",
  110. ellipsis: true,
  111. render(dom, entity, index, action, schema) {
  112. return (
  113. <Link to={`/studio/${studioName}/task/project/${entity.id}`}>
  114. {entity.title}
  115. </Link>
  116. );
  117. },
  118. },
  119. {
  120. title: intl.formatMessage({
  121. id: "forms.fields.executors.label",
  122. }),
  123. dataIndex: "executors",
  124. key: "executors",
  125. },
  126. {
  127. title: intl.formatMessage({
  128. id: "forms.fields.milestone.label",
  129. }),
  130. dataIndex: "milestone",
  131. key: "milestone",
  132. width: 80,
  133. search: false,
  134. },
  135. {
  136. title: intl.formatMessage({
  137. id: "forms.fields.status.label",
  138. }),
  139. dataIndex: "status",
  140. key: "status",
  141. width: 80,
  142. search: false,
  143. filters: true,
  144. onFilter: true,
  145. valueEnum: PublicityValueEnum(),
  146. },
  147. {
  148. title: intl.formatMessage({
  149. id: "forms.fields.updated-at.label",
  150. }),
  151. key: "updated_at",
  152. width: 100,
  153. search: false,
  154. dataIndex: "updated_at",
  155. valueType: "date",
  156. sorter: true,
  157. },
  158. {
  159. title: intl.formatMessage({ id: "buttons.option" }),
  160. key: "option",
  161. width: 100,
  162. valueType: "option",
  163. render: (text, row, index, action) => {
  164. return [
  165. <Dropdown.Button
  166. key={index}
  167. type="link"
  168. trigger={["click", "contextMenu"]}
  169. menu={{
  170. items: [
  171. {
  172. key: "transfer",
  173. label: intl.formatMessage({
  174. id: "columns.studio.transfer.title",
  175. }),
  176. icon: <TransferOutLinedIcon />,
  177. },
  178. {
  179. key: "remove",
  180. label: intl.formatMessage({
  181. id: "buttons.delete",
  182. }),
  183. icon: <DeleteOutlined />,
  184. danger: true,
  185. },
  186. ],
  187. onClick: (e) => {
  188. switch (e.key) {
  189. case "remove":
  190. showDeleteConfirm(row.id, row.title);
  191. break;
  192. default:
  193. break;
  194. }
  195. },
  196. }}
  197. >
  198. <Link to={`/studio/${studioName}/channel/${row.id}/setting`}>
  199. {intl.formatMessage({
  200. id: "buttons.setting",
  201. })}
  202. </Link>
  203. </Dropdown.Button>,
  204. ];
  205. },
  206. },
  207. ]}
  208. request={async (params = {}, sorter, filter) => {
  209. console.log(params, sorter, filter);
  210. let url = `/v2/project?view=studio&type=${activeKey}`;
  211. url += `&studio=${studioName}`;
  212. const offset =
  213. ((params.current ? params.current : 1) - 1) *
  214. (params.pageSize ? params.pageSize : 20);
  215. url += `&limit=${params.pageSize}&offset=${offset}`;
  216. url += params.keyword ? "&keyword=" + params.keyword : "";
  217. url += channelType ? "&type=" + channelType : "";
  218. url += getSorterUrl(sorter);
  219. console.log("project list api request", url);
  220. const res = await get<IProjectListResponse>(url);
  221. console.info("project list api response", res);
  222. return {
  223. total: res.data.count,
  224. succcess: res.ok,
  225. data: res.data.rows,
  226. };
  227. }}
  228. rowKey="id"
  229. bordered
  230. pagination={{
  231. showQuickJumper: true,
  232. showSizeChanger: true,
  233. }}
  234. search={false}
  235. options={{
  236. search: true,
  237. }}
  238. toolBarRender={() => [
  239. <Popover
  240. content={
  241. <ProjectCreate
  242. studio={studioName}
  243. type={activeKey === "workflow" ? "workflow" : "instance"}
  244. onCreate={() => {
  245. setOpenCreate(false);
  246. ref.current?.reload();
  247. }}
  248. />
  249. }
  250. placement="bottomRight"
  251. trigger="click"
  252. open={openCreate}
  253. onOpenChange={(open: boolean) => {
  254. setOpenCreate(open);
  255. }}
  256. >
  257. <Button key="button" icon={<PlusOutlined />} type="primary">
  258. {intl.formatMessage({ id: "buttons.create" })}
  259. </Button>
  260. </Popover>,
  261. ]}
  262. toolbar={{
  263. menu: {
  264. activeKey,
  265. items: [
  266. {
  267. key: "instance",
  268. label: "项目",
  269. },
  270. {
  271. key: "workflow",
  272. label: intl.formatMessage({ id: "labels.workflow" }),
  273. },
  274. ],
  275. onChange(key) {
  276. console.log("show course", key);
  277. setActiveKey(key);
  278. ref.current?.reload();
  279. },
  280. },
  281. }}
  282. />
  283. </>
  284. );
  285. };
  286. export default ProjectListWidget;