ProjectTable.tsx 9.1 KB

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