TaskProjects.tsx 9.2 KB

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