ProjectList.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import { useEffect, useRef, useState } from "react";
  2. import { type ActionType, ProList } from "@ant-design/pro-components";
  3. import { useIntl } from "react-intl";
  4. import { Link } from "react-router";
  5. import { Button, message, Modal, Popover, Space } from "antd";
  6. import { ExclamationCircleOutlined, PlusOutlined } from "@ant-design/icons";
  7. import { delete_, get } from "../../request";
  8. import type { IDeleteResponse } from "../../api/Article";
  9. import { getSorterUrl } from "../../utils";
  10. import type {
  11. IProjectData,
  12. IProjectListResponse,
  13. TProjectType,
  14. } from "../../api/task";
  15. import ProjectCreate from "./ProjectCreate";
  16. import ProjectEditDrawer from "./ProjectEditDrawer";
  17. import User from "../auth/User";
  18. import TimeShow from "../general/TimeShow";
  19. import ShareModal from "../share/ShareModal";
  20. import { EResType } from "../share/Share";
  21. import ProjectClone from "./ProjectClone";
  22. export interface IResNumberResponse {
  23. ok: boolean;
  24. message: string;
  25. data: {
  26. my: number;
  27. collaboration: number;
  28. };
  29. }
  30. export type TView = "current" | "studio" | "shared" | "community";
  31. interface IWidget {
  32. studioName?: string;
  33. type?: TProjectType;
  34. view?: TView;
  35. readonly?: boolean;
  36. onSelect?: (data: IProjectData) => void;
  37. }
  38. const ProjectListWidget = ({
  39. studioName,
  40. view = "studio",
  41. type = "instance",
  42. readonly = false,
  43. onSelect,
  44. }: IWidget) => {
  45. const intl = useIntl();
  46. const [openCreate, setOpenCreate] = useState(false);
  47. const [editId, setEditId] = useState<string>();
  48. const [open, setOpen] = useState(false);
  49. const _____showDeleteConfirm = (id: string, title: string) => {
  50. Modal.confirm({
  51. icon: <ExclamationCircleOutlined />,
  52. title:
  53. intl.formatMessage({
  54. id: "message.delete.confirm",
  55. }) +
  56. intl.formatMessage({
  57. id: "message.irrevocable",
  58. }),
  59. content: title,
  60. okText: intl.formatMessage({
  61. id: "buttons.delete",
  62. }),
  63. okType: "danger",
  64. cancelText: intl.formatMessage({
  65. id: "buttons.no",
  66. }),
  67. onOk() {
  68. const url = `/v2/project/${id}`;
  69. console.log("delete api request", url);
  70. return delete_<IDeleteResponse>(url)
  71. .then((json) => {
  72. console.info("api response", json);
  73. if (json.ok) {
  74. message.success("删除成功");
  75. ref.current?.reload();
  76. } else {
  77. message.error(json.message);
  78. }
  79. })
  80. .catch((e) => console.log("Oops errors!", e));
  81. },
  82. });
  83. };
  84. const ref = useRef<ActionType | null>(null);
  85. useEffect(() => {
  86. ref.current?.reload();
  87. }, [view]);
  88. return (
  89. <>
  90. <ProList<IProjectData>
  91. actionRef={ref}
  92. metas={{
  93. title: {
  94. render: (_text, row, _index, _action) => {
  95. return readonly ? (
  96. <>{row.title}</>
  97. ) : (
  98. <Link to={`/studio/${studioName}/task/project/${row.id}`}>
  99. {row.title}
  100. </Link>
  101. );
  102. },
  103. },
  104. description: {
  105. dataIndex: "description",
  106. render(_dom, entity, _index, _action, _schema) {
  107. return (
  108. <Space>
  109. <User {...entity.editor} showAvatar={false} />
  110. <TimeShow
  111. createdAt={entity.created_at}
  112. updatedAt={entity.updated_at}
  113. />
  114. </Space>
  115. );
  116. },
  117. },
  118. content: {
  119. dataIndex: "description",
  120. },
  121. subTitle: {
  122. render: (_text, _row, _index, _action) => {
  123. return <></>;
  124. },
  125. },
  126. actions: {
  127. render: (_text, row) => [
  128. <Button
  129. size="small"
  130. type="link"
  131. key="edit"
  132. onClick={() => {
  133. setEditId(row.id);
  134. setOpen(true);
  135. }}
  136. >
  137. {intl.formatMessage({
  138. id: "buttons.edit",
  139. })}
  140. </Button>,
  141. <ProjectClone
  142. key="clone"
  143. projectId={row.id}
  144. studioName={studioName}
  145. trigger={
  146. <Button size="small" type="link" key="clone">
  147. {intl.formatMessage({
  148. id: "buttons.clone",
  149. })}
  150. </Button>
  151. }
  152. />,
  153. <ShareModal
  154. key="share"
  155. trigger={
  156. <Button type="link" size="small">
  157. {intl.formatMessage({
  158. id: "buttons.share",
  159. })}
  160. </Button>
  161. }
  162. resId={row.id}
  163. resType={EResType.workflow}
  164. />,
  165. ],
  166. },
  167. }}
  168. onRow={(record) => {
  169. return {
  170. onClick: () => {
  171. console.info(`点击了行:${record.title}`);
  172. onSelect?.(record);
  173. },
  174. };
  175. }}
  176. request={async (params = {}, sorter, filter) => {
  177. console.log(params, sorter, filter);
  178. let url = `/v2/project?view=${view}&type=${type}`;
  179. url += `&studio=${studioName}`;
  180. const offset =
  181. ((params.current ? params.current : 1) - 1) *
  182. (params.pageSize ? params.pageSize : 20);
  183. url += `&limit=${params.pageSize}&offset=${offset}`;
  184. url += params.keyword ? "&keyword=" + params.keyword : "";
  185. url += getSorterUrl(sorter);
  186. console.info("project list api request", url);
  187. const res = await get<IProjectListResponse>(url);
  188. console.info("project list api response", res);
  189. return {
  190. total: res.data.count,
  191. succcess: res.ok,
  192. data: res.data.rows,
  193. };
  194. }}
  195. rowKey="id"
  196. bordered
  197. pagination={{
  198. showQuickJumper: true,
  199. showSizeChanger: true,
  200. }}
  201. search={false}
  202. options={{
  203. search: true,
  204. }}
  205. toolbar={{
  206. actions: [
  207. view === "studio" ? (
  208. <Popover
  209. content={
  210. <ProjectCreate
  211. studio={studioName}
  212. type={"workflow"}
  213. onCreate={() => {
  214. setOpenCreate(false);
  215. ref.current?.reload();
  216. }}
  217. />
  218. }
  219. placement="bottomRight"
  220. trigger="click"
  221. open={openCreate}
  222. onOpenChange={(open: boolean) => {
  223. setOpenCreate(open);
  224. }}
  225. >
  226. <Button key="button" icon={<PlusOutlined />} type="primary">
  227. {intl.formatMessage({ id: "buttons.create" })}
  228. </Button>
  229. </Popover>
  230. ) : (
  231. <></>
  232. ),
  233. ],
  234. }}
  235. />
  236. <ProjectEditDrawer
  237. studioName={studioName}
  238. projectId={editId}
  239. openDrawer={open}
  240. onClose={() => setOpen(false)}
  241. />
  242. </>
  243. );
  244. };
  245. export default ProjectListWidget;