YuJian920
3 years ago
19 changed files with 351 additions and 35 deletions
After Width: | Height: | Size: 228 B |
After Width: | Height: | Size: 279 B |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
import React from "react"; |
||||
import { useTasksTypes } from "../hook/useTask"; |
||||
import IdSelect from "./IdSelect"; |
||||
|
||||
const TaskTypeSelect = (props: React.ComponentProps<typeof IdSelect>) => { |
||||
const { data: taskTypes } = useTasksTypes(); |
||||
return <IdSelect options={taskTypes || []} {...props} />; |
||||
}; |
||||
|
||||
export default React.memo(TaskTypeSelect); |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
import { useQuery } from "react-query"; |
||||
import { useLocation } from "react-router"; |
||||
import { Kanban } from "../type"; |
||||
import { useProject } from "./useProjects"; |
||||
import useRequest from "./useRequest"; |
||||
|
||||
export const useKanbans = (param?: Partial<Kanban>) => { |
||||
const request = useRequest(); |
||||
|
||||
return useQuery<Kanban[], Error>(["kanbans", param], () => |
||||
request("/kanbans", { data: param }) |
||||
); |
||||
}; |
||||
|
||||
export const useProjectIdInUrl = () => { |
||||
const { pathname } = useLocation(); |
||||
const id = pathname.match(/projects\/(\d+)/)?.[1]; |
||||
|
||||
return Number(id); |
||||
}; |
||||
|
||||
export const useProjectInUrl = () => useProject(useProjectIdInUrl()); |
||||
|
||||
export const useKanbanSearchParams = () => ({ projectId: useProjectIdInUrl() }); |
||||
|
||||
export const useKanbansQueryKey = () => ["kanbans", useKanbanSearchParams()]; |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
import { useMemo } from "react"; |
||||
import { useQuery } from "react-query"; |
||||
import { Task, TaskType } from "../type"; |
||||
import { useProjectIdInUrl } from "./useKanban"; |
||||
import useRequest from "./useRequest"; |
||||
import useUrlQueryParams from "./useUrlQueryParams"; |
||||
|
||||
export const useTasks = (param?: Partial<Task>) => { |
||||
const request = useRequest(); |
||||
|
||||
return useQuery<Task[]>(["tasks", param], () => |
||||
request("/tasks", { data: param }) |
||||
); |
||||
}; |
||||
|
||||
export const useTasksTypes = (param?: Partial<Task>) => { |
||||
const request = useRequest(); |
||||
|
||||
return useQuery<TaskType[]>(["taskTypes", param], () => |
||||
request("/taskTypes") |
||||
); |
||||
}; |
||||
|
||||
export const useTasksSearchParmas = () => { |
||||
const [param, setParam] = useUrlQueryParams([ |
||||
"name", |
||||
"typeId", |
||||
"processorId", |
||||
"tagId", |
||||
]); |
||||
const projectId = useProjectIdInUrl(); |
||||
return useMemo( |
||||
() => ({ |
||||
projectId, |
||||
typeId: Number(param.typeId) || undefined, |
||||
processorId: Number(param.processorId) || undefined, |
||||
tagId: Number(param.tagId) || undefined, |
||||
name: param.name, |
||||
}), |
||||
[projectId, param] |
||||
); |
||||
}; |
||||
|
||||
export const useTasksQueryKey = () => ["tasks", useTasksSearchParmas()]; |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
import { Card } from "antd"; |
||||
import React from "react"; |
||||
import { |
||||
useTasks, |
||||
useTasksSearchParmas, |
||||
useTasksTypes, |
||||
} from "../../../hook/useTask"; |
||||
import { Kanban } from "../../../type"; |
||||
import { Container, TasksContainer } from "../style"; |
||||
|
||||
const TaskTypeIcon = ({ id }: { id: number }) => { |
||||
const { data: taskTypes } = useTasksTypes(); |
||||
const name = taskTypes?.find((taskType) => taskType.id === id)?.name; |
||||
|
||||
if (!name) return null; |
||||
else |
||||
return ( |
||||
<img |
||||
src={ |
||||
name === "task" |
||||
? require("../../../assets/task.png") |
||||
: require("../../../assets/bug.png") |
||||
} |
||||
alt="icon" |
||||
/> |
||||
); |
||||
}; |
||||
|
||||
const KanbanColumn = ({ kanban }: { kanban: Kanban }) => { |
||||
const { data: allTasks } = useTasks(useTasksSearchParmas()); |
||||
const tasks = allTasks?.filter((task) => task.kanbanId === kanban.id); |
||||
|
||||
return ( |
||||
<Container> |
||||
<h3>{kanban.name}</h3> |
||||
<TasksContainer> |
||||
{tasks?.map((task) => ( |
||||
<Card style={{ marginBottom: "0.5rem" }} key={task.id}> |
||||
<div>{task.name}</div> |
||||
<TaskTypeIcon id={task.typeId} /> |
||||
</Card> |
||||
))} |
||||
</TasksContainer> |
||||
</Container> |
||||
); |
||||
}; |
||||
|
||||
export default React.memo(KanbanColumn); |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
import { Button, Input } from "antd"; |
||||
import React from "react"; |
||||
import { Row } from "../../../components/lib"; |
||||
import TaskTypeSelect from "../../../components/TaskTypeSelect"; |
||||
import UserSelect from "../../../components/UserSelect"; |
||||
import useSetUrlSearchParam from "../../../hook/useSetUrlSearchParam"; |
||||
import { useTasksSearchParmas } from "../../../hook/useTask"; |
||||
|
||||
const SearchPanel = () => { |
||||
const searchParmas = useTasksSearchParmas(); |
||||
const setSearchParams = useSetUrlSearchParam(); |
||||
|
||||
const reset = () => { |
||||
setSearchParams({ |
||||
typeId: undefined, |
||||
processorId: undefined, |
||||
tagId: undefined, |
||||
name: undefined, |
||||
}); |
||||
}; |
||||
|
||||
return ( |
||||
<Row marginBottom={4} gap={true}> |
||||
<Input |
||||
style={{ width: "20rem" }} |
||||
placeholder="任务名" |
||||
value={searchParmas.name} |
||||
onChange={(evt) => setSearchParams({ name: evt.target.value })} |
||||
/> |
||||
<UserSelect |
||||
defaultOptionName="经办人" |
||||
value={searchParmas.processorId} |
||||
onChange={(value) => setSearchParams({ processorId: value })} |
||||
/> |
||||
<TaskTypeSelect |
||||
defaultOptionName="类型" |
||||
value={searchParmas.typeId} |
||||
onChange={(value) => setSearchParams({ typeId: value })} |
||||
/> |
||||
<Button onClick={reset}>重置</Button> |
||||
</Row> |
||||
); |
||||
}; |
||||
|
||||
export default React.memo(SearchPanel); |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
import React from "react"; |
||||
import useDocumentTitle from "../../hook/useDocumentTitle"; |
||||
import { |
||||
useKanbans, |
||||
useKanbanSearchParams, |
||||
useProjectInUrl, |
||||
} from "../../hook/useKanban"; |
||||
import KanbanColumn from "./KanbanColumn"; |
||||
import SearchPanel from "./SearchPanel"; |
||||
import { ScreenContainer, ColumnsContainer } from "./style"; |
||||
|
||||
const Kanban = () => { |
||||
useDocumentTitle("看板列表"); |
||||
|
||||
const { data: currentProject } = useProjectInUrl(); |
||||
const { data: kanbans } = useKanbans(useKanbanSearchParams()); |
||||
|
||||
return ( |
||||
<ScreenContainer> |
||||
<h1>{currentProject?.name}看板</h1> |
||||
<SearchPanel /> |
||||
<ColumnsContainer> |
||||
{kanbans?.map((kanban) => ( |
||||
<KanbanColumn kanban={kanban} key={kanban.id} /> |
||||
))} |
||||
</ColumnsContainer> |
||||
</ScreenContainer> |
||||
); |
||||
}; |
||||
|
||||
export default Kanban; |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
import styled from "@emotion/styled"; |
||||
|
||||
export const ScreenContainer = styled.div` |
||||
padding: 3.2rem; |
||||
width: 100%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
`;
|
||||
|
||||
export const ColumnsContainer = styled.div` |
||||
display: flex; |
||||
overflow: hidden; |
||||
margin-right: 2rem; |
||||
`;
|
||||
|
||||
export const Container = styled.div` |
||||
min-width: 27rem; |
||||
border-radius: 6px; |
||||
background-color: rgb(244, 245, 247); |
||||
display: flex; |
||||
flex-direction: column; |
||||
padding: 0.7rem 0.7rem 1rem; |
||||
margin-right: 1.5rem; |
||||
`;
|
||||
|
||||
export const TasksContainer = styled.div` |
||||
overflow: scroll; |
||||
flex: 1; |
||||
::-webkit-scrollbar { |
||||
display: none; |
||||
} |
||||
`;
|
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
import React from "react"; |
||||
|
||||
const Kanban = () => { |
||||
return <div>Kanban</div>; |
||||
}; |
||||
|
||||
export default Kanban; |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import styled from "@emotion/styled"; |
||||
|
||||
export const Aside = styled.aside` |
||||
background-color: rgb(244, 245, 247); |
||||
display: flex; |
||||
`;
|
||||
|
||||
export const Main = styled.div` |
||||
box-shadow: -5px 0 5px -5px rgba(0, 0, 0, 0.1); |
||||
display: flex; |
||||
overflow: hidden; |
||||
`;
|
||||
|
||||
export const Container = styled.div` |
||||
display: grid; |
||||
grid-template-columns: 16rem 1fr; |
||||
width: 100%; |
||||
`;
|
Loading…
Reference in new issue