Browse Source

看板增加和修改

master
YuJian920 3 years ago
parent
commit
4021d78299
  1. 7
      src/components/lib.tsx
  2. 13
      src/hook/useKanban.ts
  3. 60
      src/hook/useTask.ts
  4. 29
      src/pages/Kanban/CreateKanban/index.tsx
  5. 40
      src/pages/Kanban/CreateTask/index.tsx
  6. 10
      src/pages/Kanban/KanbanColumn/index.tsx
  7. 62
      src/pages/Kanban/TaskModal/index.tsx
  8. 26
      src/pages/Kanban/index.tsx
  9. 4
      src/pages/Kanban/style.ts
  10. 53
      src/pages/Project/index.tsx
  11. 1
      src/pages/Project/style.ts
  12. 11
      src/pages/Task/CreateTask.tsx/index.tsx
  13. 0
      src/pages/Task/index.tsx

7
src/components/lib.tsx

@ -42,3 +42,10 @@ export const FullPageErrorFallback = ({ error }: { error: Error | null }) => ( @@ -42,3 +42,10 @@ export const FullPageErrorFallback = ({ error }: { error: Error | null }) => (
<Typography.Text type="danger">{error?.message}</Typography.Text>
</FullPage>
);
export const ScreenContainer = styled.div`
padding: 3.2rem;
width: 100%;
display: flex;
flex-direction: column;
`

13
src/hook/useKanban.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { useQuery } from "react-query";
import { useMutation, useQuery, useQueryClient, QueryKey } from "react-query";
import { useLocation } from "react-router";
import { Kanban } from "../type";
import { useProject } from "./useProjects";
@ -12,6 +12,17 @@ export const useKanbans = (param?: Partial<Kanban>) => { @@ -12,6 +12,17 @@ export const useKanbans = (param?: Partial<Kanban>) => {
);
};
export const useAddKanban = () => {
const request = useRequest();
const queryClient = useQueryClient();
return useMutation(
(params: Partial<Kanban>) =>
request(`/kanbans`, { method: "POST", data: params }),
{ onSuccess: () => queryClient.invalidateQueries("kanbans") }
);
};
export const useProjectIdInUrl = () => {
const { pathname } = useLocation();
const id = pathname.match(/projects\/(\d+)/)?.[1];

60
src/hook/useTask.ts

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
import { useMemo } from "react";
import { useQuery } from "react-query";
import { useCallback, useMemo } from "react";
import { QueryKey, useMutation, useQuery, useQueryClient } from "react-query";
import { Task, TaskType } from "../type";
import useDebounce from "./useDebounce";
import { useProjectIdInUrl } from "./useKanban";
import useRequest from "./useRequest";
import useUrlQueryParams from "./useUrlQueryParams";
@ -13,6 +14,14 @@ export const useTasks = (param?: Partial<Task>) => { @@ -13,6 +14,14 @@ export const useTasks = (param?: Partial<Task>) => {
);
};
export const useTask = (id?: number) => {
const request = useRequest();
return useQuery<Task>(["task", { id }], () => request(`/tasks/${id}`, {}), {
enabled: !!id,
});
};
export const useTasksTypes = (param?: Partial<Task>) => {
const request = useRequest();
@ -29,6 +38,7 @@ export const useTasksSearchParmas = () => { @@ -29,6 +38,7 @@ export const useTasksSearchParmas = () => {
"tagId",
]);
const projectId = useProjectIdInUrl();
// const debouncedName = useDebounce(param.name, 200);
return useMemo(
() => ({
projectId,
@ -41,4 +51,50 @@ export const useTasksSearchParmas = () => { @@ -41,4 +51,50 @@ export const useTasksSearchParmas = () => {
);
};
export const useAddTask = (queryKey: QueryKey) => {
const request = useRequest();
const queryClient = useQueryClient();
return useMutation(
(params: Partial<Task>) =>
request(`/tasks`, { method: "POST", data: params }),
{ onSuccess: () => queryClient.invalidateQueries("tasks") }
);
};
export const useTasksQueryKey = () => ["tasks", useTasksSearchParmas()];
export const useTaskModal = () => {
const [{ editingTaskId }, setEditingTaskId] = useUrlQueryParams([
"editingTaskId",
]);
const { data: editingTask, isLoading } = useTask(Number(editingTaskId));
const startEdit = useCallback(
(id: number) => {
setEditingTaskId({ editingTaskId: id });
},
[setEditingTaskId]
);
const close = useCallback(() => {
setEditingTaskId({ editingTaskId: undefined });
}, [setEditingTaskId]);
return {
editingTaskId,
editingTask,
startEdit,
close,
isLoading,
};
};
export const useEditTask = () => {
const request = useRequest();
const queryClient = useQueryClient();
return useMutation(
(params: Partial<Task>) =>
request(`/tasks/${params.id}`, { method: "PATCH", data: params }),
{ onSuccess: () => queryClient.invalidateQueries("tasks") }
);
};

29
src/pages/Kanban/CreateKanban/index.tsx

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
import { Input } from "antd";
import React, { useState } from "react";
import { useAddKanban, useProjectIdInUrl } from "../../../hook/useKanban";
import { Container } from "../style";
const CreateKanban = () => {
const [name, setName] = useState("");
const projectId = useProjectIdInUrl();
const { mutateAsync: addKanban } = useAddKanban();
const sumbit = async () => {
await addKanban({ name, projectId });
setName("");
};
return (
<Container>
<Input
size="large"
placeholder="新建看板名称"
onPressEnter={sumbit}
value={name}
onChange={(evt) => setName(evt.target.value)}
/>
</Container>
);
};
export default React.memo(CreateKanban);

40
src/pages/Kanban/CreateTask/index.tsx

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
import { Card, Input } from "antd";
import React, { useEffect, useState } from "react";
import { useProjectIdInUrl } from "../../../hook/useKanban";
import { useAddTask, useTasksQueryKey } from "../../../hook/useTask";
const CreateTask = ({ kanbanId }: { kanbanId: number }) => {
const [name, setName] = useState("");
const { mutateAsync: addTask } = useAddTask(useTasksQueryKey());
const projectId = useProjectIdInUrl();
const [inputMode, setInputMode] = useState(false);
const sumbit = async () => {
await addTask({ projectId, name, kanbanId });
setInputMode(false);
setName("");
};
const toggle = () => setInputMode((mode) => !mode);
useEffect(() => {
if (!inputMode) setName("");
}, [inputMode]);
if (!inputMode) return <div onClick={toggle}></div>;
else
return (
<Card>
<Input
onBlur={toggle}
placeholder="需要做些什么"
autoFocus={true}
onPressEnter={sumbit}
value={name}
onChange={(evt) => setName(evt.target.value)}
/>
</Card>
);
};
export default React.memo(CreateTask);

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

@ -1,11 +1,13 @@ @@ -1,11 +1,13 @@
import { Card } from "antd";
import React from "react";
import {
useTaskModal,
useTasks,
useTasksSearchParmas,
useTasksTypes,
} from "../../../hook/useTask";
import { Kanban } from "../../../type";
import CreateTask from "../CreateTask";
import { Container, TasksContainer } from "../style";
const TaskTypeIcon = ({ id }: { id: number }) => {
@ -29,17 +31,23 @@ const TaskTypeIcon = ({ id }: { id: number }) => { @@ -29,17 +31,23 @@ const TaskTypeIcon = ({ id }: { id: number }) => {
const KanbanColumn = ({ kanban }: { kanban: Kanban }) => {
const { data: allTasks } = useTasks(useTasksSearchParmas());
const tasks = allTasks?.filter((task) => task.kanbanId === kanban.id);
const { startEdit } = useTaskModal();
return (
<Container>
<h3>{kanban.name}</h3>
<TasksContainer>
{tasks?.map((task) => (
<Card style={{ marginBottom: "0.5rem" }} key={task.id}>
<Card
onClick={() => startEdit(task.id)}
style={{ marginBottom: "0.5rem", cursor: "pointer" }}
key={task.id}
>
<div>{task.name}</div>
<TaskTypeIcon id={task.typeId} />
</Card>
))}
<CreateTask kanbanId={kanban.id} />
</TasksContainer>
</Container>
);

62
src/pages/Kanban/TaskModal/index.tsx

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
import React, { useEffect } from "react";
import { useForm } from "antd/es/form/Form";
import { useEditTask, useTaskModal } from "../../../hook/useTask";
import { Form, Input, Modal } from "antd";
import UserSelect from "../../../components/UserSelect";
import TaskTypeSelect from "../../../components/TaskTypeSelect";
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
const TaskModal = () => {
const [form] = useForm();
const { editingTaskId, editingTask, close } = useTaskModal();
const { mutateAsync: editTask, isLoading: editLoading } = useEditTask();
const onCancel = () => {
close();
form.resetFields();
};
const onOk = async () => {
await editTask({ ...editingTask, ...form.getFieldsValue() });
close();
};
useEffect(() => {
form.setFieldsValue(editingTask);
}, [form, editingTask]);
return (
<Modal
forceRender={true}
onCancel={onCancel}
onOk={onOk}
okText="确认"
cancelText="取消"
confirmLoading={editLoading}
title="编辑任务"
visible={!!editingTaskId}
>
<Form {...layout} initialValues={editingTask} form={form}>
<Form.Item
label="任务名"
name="name"
rules={[{ required: true, message: "请输入任务名" }]}
>
<Input />
</Form.Item>
<Form.Item label="经办人" name="processorId">
<UserSelect defaultOptionName="经办人" />
</Form.Item>
<Form.Item label="类型" name="typeId">
<TaskTypeSelect />
</Form.Item>
</Form>
</Modal>
);
};
export default React.memo(TaskModal);

26
src/pages/Kanban/index.tsx

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
import { Spin } from "antd";
import React from "react";
import useDocumentTitle from "../../hook/useDocumentTitle";
import {
@ -5,25 +6,38 @@ import { @@ -5,25 +6,38 @@ import {
useKanbanSearchParams,
useProjectInUrl,
} from "../../hook/useKanban";
import { useTasks, useTasksSearchParmas } from "../../hook/useTask";
import CreateKanban from "./CreateKanban";
import KanbanColumn from "./KanbanColumn";
import SearchPanel from "./SearchPanel";
import { ScreenContainer, ColumnsContainer } from "./style";
import TaskModal from "./TaskModal";
const Kanban = () => {
useDocumentTitle("看板列表");
const { data: currentProject } = useProjectInUrl();
const { data: kanbans } = useKanbans(useKanbanSearchParams());
const { data: kanbans, isLoading: kanbanIsLoading } = useKanbans(
useKanbanSearchParams()
);
const { isLoading: taskIsLoading } = useTasks(useTasksSearchParmas());
const isLoading = taskIsLoading || kanbanIsLoading;
return (
<ScreenContainer>
<h1>{currentProject?.name}</h1>
<SearchPanel />
<ColumnsContainer>
{kanbans?.map((kanban) => (
<KanbanColumn kanban={kanban} key={kanban.id} />
))}
</ColumnsContainer>
{isLoading ? (
<Spin size="large" />
) : (
<ColumnsContainer>
{kanbans?.map((kanban) => (
<KanbanColumn kanban={kanban} key={kanban.id} />
))}
<CreateKanban />
</ColumnsContainer>
)}
<TaskModal />
</ScreenContainer>
);
};

4
src/pages/Kanban/style.ts

@ -9,8 +9,8 @@ export const ScreenContainer = styled.div` @@ -9,8 +9,8 @@ export const ScreenContainer = styled.div`
export const ColumnsContainer = styled.div`
display: flex;
overflow: hidden;
margin-right: 2rem;
overflow-x: scroll;
flex: 1;
`;
export const Container = styled.div`

53
src/pages/Project/index.tsx

@ -1,25 +1,46 @@ @@ -1,25 +1,46 @@
import { Menu } from "antd";
import React from "react";
import { Routes, Route, Navigate } from "react-router";
import { Routes, Route, Navigate, useLocation } from "react-router";
import { Link } from "react-router-dom";
import Kanban from "../Kanban";
import Task from "./Task";
import { Aside, Main, Container } from "./style"
import Task from "../Task";
import { Aside, Main, Container } from "./style";
const useRouteType = () => {
const units = useLocation().pathname.split("/");
return units[units.length - 1];
};
const Project = () => {
const routeType = useRouteType();
return (
<div>
<h1>Project</h1>
<Link to="kanban"></Link>
<Link to="task"></Link>
<Routes>
<Route path="/kanban" element={<Kanban />} />
<Route path="/task" element={<Task />} />
<Route
path="*"
element={<Navigate to={window.location.pathname + "/kanban"} replace={true} />}
/>
</Routes>
</div>
<Container>
<Aside>
<Menu mode={"inline"} selectedKeys={[routeType]}>
<Menu.Item key={"kanban"}>
<Link to="kanban"></Link>
</Menu.Item>
<Menu.Item key={"task"}>
<Link to="task"></Link>
</Menu.Item>
</Menu>
</Aside>
<Main>
<Routes>
<Route path="/kanban" element={<Kanban />} />
<Route path="/task" element={<Task />} />
<Route
path="*"
element={
<Navigate
to={window.location.pathname + "/kanban"}
replace={true}
/>
}
/>
</Routes>
</Main>
</Container>
);
};

1
src/pages/Project/style.ts

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

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

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
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);

0
src/pages/Project/Task/index.tsx → src/pages/Task/index.tsx

Loading…
Cancel
Save