Files
labelmain-demo/app/person/dashboard/components/PersonalTaskTable.tsx
2026-06-06 14:05:46 +08:00

307 lines
9.4 KiB
TypeScript

"use client"
import { getUserTaskList } from "@/components/label/api/task"
import { Task } from "@/components/label/api/task/typing"
import TaskStatusTag from "@/components/label/components/TaskStatusTag"
import {
useBackUrlStore,
usePermissionStore,
} from "@/components/label/store/auth"
import {
SettingCustomPageSizeControl,
SettingDataTable,
SettingHeaderActions,
SettingListHeader,
usePersistentPageSizeOptions,
} from "@/components/setting/PageSurface"
import {
getPersistedPageSizeOptions,
resolvePageSizeWithinOptions,
} from "@/components/setting/pageSize"
import {
Button,
Group,
Select,
Stack,
Text,
UnstyledButton,
} from "@mantine/core"
import { IconRefresh } from "@tabler/icons-react"
import { DataTableColumn } from "mantine-datatable"
import { useCallback, useEffect, useMemo, useState } from "react"
import classes from "../page.module.css"
const DEFAULT_RECORDS_PER_PAGE_OPTIONS = [10, 15, 20, 50]
const REJECTED_FILTER_OPTIONS = [
{ value: "all", label: "全部" },
{ value: "true", label: "是" },
{ value: "false", label: "否" },
] as const
type RejectedFilterValue = (typeof REJECTED_FILTER_OPTIONS)[number]["value"]
export default function PersonalTaskTable() {
const user_name = usePermissionStore((s) => s.user_name)
const setBackProps = useBackUrlStore((s) => s.setBackProps)
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(() =>
resolvePageSizeWithinOptions(
10,
getPersistedPageSizeOptions(DEFAULT_RECORDS_PER_PAGE_OPTIONS),
10
)
)
const {
recordsPerPageOptions,
addCustomPageSize,
clearCustomPageSizeCache,
hasCustomPageSizeCache,
} = usePersistentPageSizeOptions(DEFAULT_RECORDS_PER_PAGE_OPTIONS, pageSize)
const [totalItems, setTotalItems] = useState(0)
const [records, setRecords] = useState<Task.SimpleTaskItem[]>([])
const [loading, setLoading] = useState(false)
const [rejectedFilter, setRejectedFilter] =
useState<RejectedFilterValue>("all")
const totalPages = useMemo(() => {
return Math.max(1, Math.ceil(totalItems / pageSize))
}, [pageSize, totalItems])
const load = useCallback(async () => {
if (!user_name) return
setLoading(true)
try {
const res = await getUserTaskList({
page_number: page,
page_size: pageSize,
rejected:
rejectedFilter === "all" ? undefined : rejectedFilter === "true",
})
setRecords(res?.simple_task_list ?? [])
setTotalItems(res?.total_items ?? 0)
} finally {
setLoading(false)
}
}, [page, pageSize, rejectedFilter, user_name])
useEffect(() => {
load()
}, [load])
useEffect(() => {
if (page > totalPages) setPage(totalPages)
}, [page, totalPages])
const resetData = () => {
if (page === 1) load()
else setPage(1)
}
const [currentRowId, setCurrentRowId] = useState<number>(0)
const originColumn = useMemo(() => {
let column: DataTableColumn<Task.SimpleTaskItem>[] = [
{
accessor: "project_name",
title: "项目名称",
width: 160,
render: (record: Task.SimpleTaskItem) => {
return (
<Text
title={record.project_name ?? "-"}
className={classes.ellipsisText}>
{record.project_name ?? "-"}
</Text>
)
},
},
{
accessor: "id",
title: "任务ID",
width: 150,
render: (record: Task.SimpleTaskItem) => {
return (
<UnstyledButton
onClick={async () => {
setCurrentRowId(record.id)
if ([1, 2, 4, 5, 6, 7].includes(record.label_type)) {
setBackProps(`/person/dashboard`)
if ([4, 5, 6].includes(record.label_type)) {
// router.replace(
// `/image/label?project_id=${record.project_id}&task_id=${record.id}`
// )
window.location.href = `/image/label?project_id=${record.project_id}&task_id=${record.id}`
} else if ([7].includes(record.label_type)) {
// router.replace(
// `/video/label?project_id=${record.project_id}&task_id=${record.id}`
// )
window.location.href = `/video/label?project_id=${record.project_id}&task_id=${record.id}`
} else if ([1, 2].includes(record.label_type)) {
window.location.href = `/lidar/lidar-point?project_id=${record.project_id}&task_id=${record.id}`
}
} else {
// await runCommand(
// record.id.toString(),
// user_name,
// user_password
// )
}
}}
title={record.id.toString()}
className={classes.tableLink}>
{record.id}
</UnstyledButton>
)
},
},
{
accessor: "label_object_size.object_size",
title: "对象数",
width: 80,
render: (record: Task.SimpleTaskItem) => {
return (
<Text className={classes.tableCellText}>
{record.label_object_size?.object_size || "-"}
</Text>
)
},
},
{
accessor: "label_status",
title: "状态",
width: 160,
render: (record) => (
<TaskStatusTag
status={record.label_status}
rejected={record.rejected}
/>
),
},
{
accessor: "label_username",
title: "标注员",
width: 80,
render: (record: Task.SimpleTaskItem) => (
<Text className={classes.tableCellText}>
{record.label_username || "-"}
</Text>
),
},
{
accessor: "review1_username",
title: "审核员",
width: 80,
render: (record: Task.SimpleTaskItem) => (
<Text className={classes.tableCellText}>
{record.review1_username || "-"}
</Text>
),
},
{
accessor: "review2_username",
title: "复审员",
width: 80,
render: (record: Task.SimpleTaskItem) => (
<Text className={classes.tableCellText}>
{record.review2_username || "-"}
</Text>
),
},
]
return column
}, [setBackProps])
return (
<Stack className={classes.dashboardSection}>
<SettingListHeader
title="我的任务"
count={
<Group gap="sm" align="center" wrap="wrap">
<Text span inherit>
{totalItems}
</Text>
<Group gap={6} align="center" wrap="wrap">
<Text span inherit>
</Text>
<Select
data={REJECTED_FILTER_OPTIONS}
value={rejectedFilter}
onChange={(value) => {
if (!value) return
setRejectedFilter(value as RejectedFilterValue)
setPage(1)
}}
allowDeselect={false}
checkIconPosition="right"
size="xs"
w={112}
className={classes.dashboardHeaderSelect}
/>
</Group>
</Group>
}
actions={
<SettingHeaderActions>
<Button
variant="default"
onClick={resetData}
disabled={loading}
leftSection={<IconRefresh size={16} />}>
</Button>
</SettingHeaderActions>
}
/>
<div className={classes.dashboardTableArea}>
<SettingDataTable<Task.SimpleTaskItem>
width="100%"
minHeight={0}
style={{ width: "100%", flex: 1 }}
pinFirstColumn
fetching={loading}
rowBackgroundColor={({ id }) => {
if (id === currentRowId) return "#F7FBFF"
}}
scrollAreaProps={{ type: "auto" }}
records={records}
onRecordsPerPageChange={setPageSize}
recordsPerPageOptions={recordsPerPageOptions}
columns={originColumn}
totalRecords={totalItems}
recordsPerPage={pageSize}
page={page}
onPageChange={setPage}
noRecordsText="暂无数据"
recordsPerPageLabel="每页条数"
paginationText={({ from, to, totalRecords }) =>
`显示 ${from}-${to} 条,共 ${totalRecords} 条记录`
}
renderPagination={({ Controls }) => (
<Group
justify="space-between"
align="center"
w="100%"
gap="sm"
wrap="wrap">
<Group gap="xs" align="center" wrap="wrap">
<Controls.Text />
<Controls.PageSizeSelector />
<SettingCustomPageSizeControl
addCustomPageSize={addCustomPageSize}
clearCustomPageSizeCache={clearCustomPageSizeCache}
hasCustomPageSizeCache={hasCustomPageSizeCache}
triggerButtonProps={{ variant: "default", size: "sm" }}
confirmButtonProps={{ color: "#1874ff" }}
/>
</Group>
<Controls.Pagination />
</Group>
)}
/>
</div>
</Stack>
)
}