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