Browse Source

完成任务组

master
YuJian920 3 years ago
parent
commit
05d1628e9e
  1. 37
      src/hook/useEpic.ts
  2. 68
      src/pages/Epic/CreateEpic/index.tsx
  3. 87
      src/pages/Epic/index.tsx
  4. 9
      src/pages/Epic/style.ts
  5. 8
      src/pages/Kanban/KanbanColumn/index.tsx
  6. 8
      src/pages/Project/index.tsx
  7. 1
      src/pages/Project/style.ts
  8. 11
      src/pages/Task/CreateTask.tsx/index.tsx
  9. 7
      src/pages/Task/index.tsx
  10. 8
      src/type/index.ts

37
src/hook/useEpic.ts

@ -0,0 +1,37 @@
import { useMutation, useQuery, useQueryClient, QueryKey } from "react-query";
import { Epic } from "../type";
import { useProjectIdInUrl } from "./useKanban";
import useRequest from "./useRequest";
export const useEpics = (param?: Partial<Epic>) => {
const request = useRequest();
return useQuery<Epic[]>(["epics", param], () =>
request("/epics", { data: param })
);
};
export const useAddEpic = () => {
const request = useRequest();
const queryClient = useQueryClient();
return useMutation(
(params: Partial<Epic>) =>
request(`/epics`, { method: "POST", data: params }),
{ onSuccess: () => queryClient.invalidateQueries("epics") }
);
};
export const useDeleteEpic = () => {
const request = useRequest();
const queryClient = useQueryClient();
return useMutation(
({ id }: { id: number }) => request(`/epics/${id}`, { method: "DELETE" }),
{ onSuccess: () => queryClient.invalidateQueries("epics") }
);
};
export const useEpicSearchParams = () => ({ projectId: useProjectIdInUrl() });
export const useEpicsQueryKey = () => ["epics", useEpicSearchParams()];

68
src/pages/Epic/CreateEpic/index.tsx

@ -0,0 +1,68 @@
import { Button, Drawer, DrawerProps, Form, Input, Spin } from "antd";
import React, { useEffect } from "react";
import { useAddEpic } from "../../../hook/useEpic";
import { useProjectIdInUrl } from "../../../hook/useKanban";
import { Container } from "../style";
const CreateEpic = (
props: Pick<DrawerProps, "visible"> & { onClose: () => void }
) => {
const { mutate: addEpic, isLoading, error } = useAddEpic();
const [form] = Form.useForm();
const projectId = useProjectIdInUrl();
const onFinish = async (values: any) => {
await addEpic({ ...values, projectId });
props.onClose();
};
useEffect(() => {
form.resetFields();
}, [form, props.visible]);
return (
<Drawer
visible={props.visible}
onClose={props.onClose}
forceRender={true}
destroyOnClose={true}
width="100%"
>
<Container>
{isLoading ? (
<Spin size={"large"} />
) : (
<>
<h1></h1>
<Form
form={form}
layout={"vertical"}
style={{ width: "40rem" }}
onFinish={onFinish}
>
<Form.Item
label={"名称"}
name={"name"}
rules={[{ required: true, message: "请输入任务组名" }]}
>
<Input placeholder={"请输入任务组名称"} />
</Form.Item>
<Form.Item style={{ textAlign: "right" }}>
<Button
loading={isLoading}
type={"primary"}
htmlType={"submit"}
>
</Button>
</Form.Item>
</Form>
</>
)}
</Container>
</Drawer>
);
};
export default React.memo(CreateEpic);

87
src/pages/Epic/index.tsx

@ -0,0 +1,87 @@
import { Button, List, Modal } from "antd";
import dayjs from "dayjs";
import React, { useState } from "react";
import { Link } from "react-router-dom";
import { Row, ScreenContainer } from "../../components/lib";
import {
useDeleteEpic,
useEpics,
useEpicSearchParams,
} from "../../hook/useEpic";
import { useProjectInUrl } from "../../hook/useKanban";
import { useTasks } from "../../hook/useTask";
import CreateEpic from "./CreateEpic";
import { Epic as EpicType } from "../../type";
const Epic = () => {
const { data: currenrProject } = useProjectInUrl();
const { data: epics } = useEpics(useEpicSearchParams());
const { data: tasks } = useTasks({ projectId: currenrProject?.id });
const { mutate: deleteEpic } = useDeleteEpic();
const [epicCreateOpen, setEpicCreateOpen] = useState(false);
const confirmDeleteEpic = (epic: EpicType) => {
Modal.confirm({
title: `确定删除项目组:${epic.name}`,
content: "点击确定删除",
okText: "确定",
onOk() {
deleteEpic({ id: epic.id });
},
});
};
return (
<ScreenContainer>
<Row between={true}>
<h1>{currenrProject?.name}</h1>
<Button onClick={() => setEpicCreateOpen(true)} type="link">
</Button>
</Row>
<List
// style={{ overflow: "scroll" }}
dataSource={epics}
itemLayout="vertical"
renderItem={(epic) => (
<List.Item>
<List.Item.Meta
title={
<Row between={true}>
<span>{epic.name}</span>
<Button type="link" onClick={() => confirmDeleteEpic(epic)}>
</Button>
</Row>
}
description={
<div>
<div>: {dayjs(epic.start).format("YYYY-MM-DD")}</div>
<div>: {dayjs(epic.end).format("YYYY-MM-DD")}</div>
</div>
}
/>
<div>
{tasks
?.filter((task) => task.epicId === epic.id)
.map((task) => (
<Link
to={`/projects/${currenrProject?.id}/kanban?editingTaskId=${task.id}`}
key={task.id}
>
{task.name}
</Link>
))}
</div>
</List.Item>
)}
/>
<CreateEpic
onClose={() => setEpicCreateOpen(false)}
visible={epicCreateOpen}
/>
</ScreenContainer>
);
};
export default Epic;

9
src/pages/Epic/style.ts

@ -0,0 +1,9 @@
import styled from "@emotion/styled";
export const Container = styled.div`
height: 80vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`

8
src/pages/Kanban/KanbanColumn/index.tsx

@ -89,12 +89,8 @@ const KanbanColumn = React.forwardRef<HTMLDivElement, { kanban: Kanban }>(
<More kanban={kanban} key={kanban.id} /> <More kanban={kanban} key={kanban.id} />
</Row> </Row>
<TasksContainer> <TasksContainer>
<Drop <Drop type="ROW" direction="vertical" droppableId={String(kanban.id)}>
type="ROW" <DropChild style={{ minHeight: "5px" }}>
direction="vertical"
droppableId={String(kanban.id)}
>
<DropChild>
{tasks?.map((task, taskIndex) => ( {tasks?.map((task, taskIndex) => (
<Drag <Drag
key={task.id} key={task.id}

8
src/pages/Project/index.tsx

@ -3,7 +3,7 @@ import React from "react";
import { Routes, Route, Navigate, useLocation } from "react-router"; import { Routes, Route, Navigate, useLocation } from "react-router";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import Kanban from "../Kanban"; import Kanban from "../Kanban";
import Task from "../Task"; import Epic from "../Epic";
import { Aside, Main, Container } from "./style"; import { Aside, Main, Container } from "./style";
const useRouteType = () => { const useRouteType = () => {
@ -20,15 +20,15 @@ const Project = () => {
<Menu.Item key={"kanban"}> <Menu.Item key={"kanban"}>
<Link to="kanban"></Link> <Link to="kanban"></Link>
</Menu.Item> </Menu.Item>
<Menu.Item key={"task"}> <Menu.Item key={"epic"}>
<Link to="task"></Link> <Link to="epic"></Link>
</Menu.Item> </Menu.Item>
</Menu> </Menu>
</Aside> </Aside>
<Main> <Main>
<Routes> <Routes>
<Route path="/kanban" element={<Kanban />} /> <Route path="/kanban" element={<Kanban />} />
<Route path="/task" element={<Task />} /> <Route path="/epic" element={<Epic />} />
<Route <Route
path="*" path="*"
element={ element={

1
src/pages/Project/style.ts

@ -14,4 +14,5 @@ export const Main = styled.div`
export const Container = styled.div` export const Container = styled.div`
display: grid; display: grid;
grid-template-columns: 16rem 1fr; grid-template-columns: 16rem 1fr;
width: 100%
`; `;

11
src/pages/Task/CreateTask.tsx/index.tsx

@ -1,11 +0,0 @@
import React, { useState } from "react";
import { useAddTask, useTasksQueryKey } from "../../../hook/useTask";
const CreateTask = () => {
const [name, setName] = useState("");
// const { data, isLoading } = useAddTask(useTasksQueryKey())
return <div></div>
};
export default React.memo(CreateTask);

7
src/pages/Task/index.tsx

@ -1,7 +0,0 @@
import React from "react";
const Task = () => {
return <div>Task</div>;
};
export default Task;

8
src/type/index.ts

@ -66,3 +66,11 @@ export interface SortProps {
fromKanbanId?: number; fromKanbanId?: number;
toKanbanId?: number; toKanbanId?: number;
} }
export interface Epic {
id: number;
name: string;
projectId: number;
start: number;
end: number;
}
Loading…
Cancel
Save