2
0

ProjectTable.tsx 9.1 KB

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