YuJian920
3 years ago
10 changed files with 216 additions and 28 deletions
@ -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()]; |
@ -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); |
@ -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; |
@ -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; |
||||||
|
` |
@ -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); |
|
@ -1,7 +0,0 @@ |
|||||||
import React from "react"; |
|
||||||
|
|
||||||
const Task = () => { |
|
||||||
return <div>Task</div>; |
|
||||||
}; |
|
||||||
|
|
||||||
export default Task; |
|
Loading…
Reference in new issue