| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- import type { ActionType, ProColumns } from "@ant-design/pro-components";
- import { EditableProTable, useRefFunction } from "@ant-design/pro-components";
- import { Button, Dropdown, Form, Space, Typography } from "antd";
- import React, { useEffect, useRef, useState } from "react";
- import { useIntl } from "react-intl";
- import ProjectEditDrawer from "./ProjectEditDrawer";
- import type {
- IProjectData,
- IProjectListResponse,
- IProjectResponse,
- IProjectUpdateRequest,
- } from "../../api/task";
- import { get, post } from "../../request";
- import { TaskBuilderProjectsModal } from "./TaskBuilderProjects";
- const { Text } = Typography;
- function generateUUID() {
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
- const r = (Math.random() * 16) | 0,
- v = c === "x" ? r : (r & 0x3) | 0x8;
- return v.toString(16);
- });
- }
- interface IWidget {
- studioName?: string;
- projectId?: string;
- onRowClick?: (data: IProjectData) => void;
- onSelect?: (id: string) => void;
- }
- const Project = ({ studioName, projectId, onRowClick, onSelect }: IWidget) => {
- const intl = useIntl();
- const [open, setOpen] = useState(false);
- const [editId, setEditId] = useState<string>();
- const [title, setTitle] = useState<React.ReactNode>();
- const [curr, setCurr] = useState<string>();
- const actionRef = useRef<ActionType | null>(null);
- const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
- const [dataSource, setDataSource] = useState<readonly IProjectData[]>([]);
- const [buildProjectsOpen, setBuildProjectsOpen] = useState(false);
- const [form] = Form.useForm();
- const ProjectTitle = ({ data }: { data?: IProjectData }) => (
- <Space>
- <Text strong>{data?.title}</Text>
- {data?.path?.reverse().map((item, id) => {
- return (
- <Text key={id} type="secondary">
- {" <"}
- <Button
- type="text"
- onClick={() => {
- if (onSelect) {
- onSelect(item.id);
- }
- }}
- >
- {item.title}
- </Button>
- </Text>
- );
- })}
- </Space>
- );
- const loopDataSourceFilter = (
- data: readonly IProjectData[],
- id: React.Key | undefined
- ): IProjectData[] => {
- return data
- .map((item) => {
- if (item.id !== id) {
- if (item.children) {
- const newChildren = loopDataSourceFilter(item.children, id);
- return {
- ...item,
- children: newChildren.length > 0 ? newChildren : undefined,
- };
- }
- return item;
- }
- return null;
- })
- .filter(Boolean) as IProjectData[];
- };
- const removeRow = useRefFunction((record: IProjectData) => {
- setDataSource(loopDataSourceFilter(dataSource, record.id));
- });
- const columns: ProColumns<IProjectData>[] = [
- {
- title: intl.formatMessage({
- id: "forms.fields.title.label",
- }),
- dataIndex: "title",
- formItemProps: {
- rules: [
- {
- required: true,
- message: "此项为必填项",
- },
- ],
- },
- width: "30%",
- render: (_dom, record, _, _action) => {
- return (
- <Button
- type="link"
- size="small"
- onClick={() => {
- if (onSelect) {
- onSelect(record.id);
- }
- }}
- >
- {record.title}
- </Button>
- );
- },
- },
- {
- title: intl.formatMessage({
- id: "forms.fields.milestone.label",
- }),
- key: "state",
- dataIndex: "state",
- readonly: true,
- },
- {
- title: intl.formatMessage({
- id: "forms.fields.status.label",
- }),
- key: "state",
- dataIndex: "state",
- readonly: true,
- },
- {
- title: "操作",
- valueType: "option",
- width: 250,
- render: (_text, record, _, _action) => [
- <Button
- size="small"
- type="link"
- key="editable"
- onClick={() => {
- setEditId(record.id);
- setOpen(true);
- }}
- >
- 编辑
- </Button>,
- record.type === "workflow" ? (
- <></>
- ) : (
- <EditableProTable.RecordCreator
- key="copy"
- parentKey={record.id}
- record={{
- id: generateUUID(),
- parent_id: record.id,
- }}
- >
- <Dropdown.Button
- size="small"
- type="link"
- menu={{
- items: [
- {
- key: "multi",
- label: "批量添加",
- },
- ],
- onClick: (e) => {
- switch (e.key) {
- case "multi":
- setCurr(record.id);
- setBuildProjectsOpen(true);
- break;
- default:
- break;
- }
- },
- }}
- >
- 插入子节点
- </Dropdown.Button>
- </EditableProTable.RecordCreator>
- ),
- <Button
- type="link"
- danger
- size="small"
- key="delete"
- onClick={() => {
- removeRow(record);
- }}
- >
- 删除
- </Button>,
- ],
- },
- ];
- const getChildren = (
- record: IProjectData,
- findIn: IProjectData[]
- ): IProjectData[] | undefined => {
- const children = findIn
- .filter((item) => item.parent?.id === record.id)
- .map((item) => {
- return { ...item, children: getChildren(item, findIn) };
- });
- if (children.length > 0) {
- return children;
- }
- return undefined;
- };
- useEffect(() => {
- actionRef.current?.reload();
- }, [projectId]);
- return (
- <>
- <TaskBuilderProjectsModal
- studioName={studioName}
- parentId={curr}
- open={buildProjectsOpen}
- onClose={() => setBuildProjectsOpen(false)}
- onDone={() => actionRef.current?.reload()}
- />
- <EditableProTable<IProjectData>
- onRow={(record) => ({
- onClick: () => {
- if (onRowClick) {
- onRowClick(record);
- }
- },
- })}
- rowKey="id"
- scroll={{
- x: 960,
- }}
- actionRef={actionRef}
- headerTitle={title}
- maxLength={5}
- search={false}
- // 关闭默认的新建按钮
- recordCreatorProps={false}
- columns={columns}
- request={async () => {
- const url = `/v2/project?view=project-tree&project_id=${projectId}`;
- console.info("api request", url);
- const res = await get<IProjectListResponse>(url);
- console.info("project api response", res);
- const root = res.data.rows
- .filter((item) => item.id === projectId)
- .map((item) => {
- return { ...item, children: getChildren(item, res.data.rows) };
- });
- return {
- data: root,
- total: res.data.count,
- success: res.ok,
- };
- }}
- value={dataSource}
- onChange={(value: readonly IProjectData[]) => {
- const root = value.find((item) => item.id === projectId);
- setTitle(ProjectTitle({ data: root }));
- setDataSource(value);
- }}
- editable={{
- form,
- editableKeys,
- onSave: async (_key, values) => {
- const data: IProjectUpdateRequest = {
- ...values,
- studio_name: studioName ?? "",
- };
- const url = `/v2/project`;
- console.info("save api request", url, data);
- const res = await post<IProjectUpdateRequest, IProjectResponse>(
- url,
- data
- );
- console.info("save api response", res);
- },
- onChange: setEditableRowKeys,
- actionRender: (_row, _config, dom) => [dom.save, dom.cancel],
- }}
- />
- <ProjectEditDrawer
- studioName={studioName}
- projectId={editId}
- openDrawer={open}
- onClose={() => setOpen(false)}
- />
- </>
- );
- };
- export default Project;
|