"use client" import { getProjectList, getProjectTypeList, } from "@/components/label/api/project" import { getTaskList } from "@/components/label/api/task" import { Task } from "@/components/label/api/task/typing" import TaskStatusTag from "@/components/label/components/TaskStatusTag" import { useAllUserStore } from "@/components/label/store/auth" import { TaskStatusEnum } from "@/components/label/utils/constants" import { Button, Collapse, Group, MultiSelect, NumberInput, Paper, SimpleGrid, Stack, Text, } from "@mantine/core" import { notifications } from "@mantine/notifications" import { IconChevronDown, IconChevronUp, IconDownload, IconRefresh, IconSearch, } from "@tabler/icons-react" import { DataTable, DataTableColumn } from "mantine-datatable" import { useEffect, useMemo, useState } from "react" import * as XLSX from "xlsx-js-style" import { flattenLeaderTree } from "../utils" const emptyFilters = { page_size: 1000, project_type: [] as string[], project_id: [] as string[], label_status: [] as string[], current_uid: [] as string[], label_user: [] as string[], review_user1: [] as string[], review_user2: [] as string[], } function toNumberArray(values?: string[]) { if (!values || values.length === 0) return undefined const arr = values.map((v) => Number(v)).filter((v) => Number.isFinite(v)) return arr.length > 0 ? arr : undefined } export default function TaskListTable() { const treeData = useAllUserStore((s) => s.treeData) const userOptions = useMemo( () => flattenLeaderTree(treeData ?? []), [treeData] ) const [collapsed, setCollapsed] = useState(true) const [loading, setLoading] = useState(false) const [projectTypeOpts, setProjectTypeOpts] = useState< Array<{ label: string; value: string }> >([]) const [projectNameOpts, setProjectNameOpts] = useState< Array<{ label: string; value: string }> >([]) const [filters, setFilters] = useState(emptyFilters) const [records, setRecords] = useState([]) const [total, setTotal] = useState(0) const [totalSize, setTotalSize] = useState(null) useEffect(() => { const run = async () => { const [typesRes, projectsRes] = await Promise.allSettled([ getProjectTypeList(), getProjectList({ page_number: 1, page_size: 10000 }), ]) if (typesRes.status === "fulfilled") { setProjectTypeOpts( (typesRes.value ?? []).map((t) => ({ label: t, value: t })) ) } else { setProjectTypeOpts([]) } if (projectsRes.status === "fulfilled") { const list = projectsRes.value?.project_list ?? [] setProjectNameOpts( list.map((p) => ({ label: p.name, value: String(p.id) })) ) } else { setProjectNameOpts([]) } } queueMicrotask(run) }, []) const handleSearch = async (nextFilters = filters) => { try { setLoading(true) if (!nextFilters.page_size) { notifications.show({ color: "yellow", title: "参数不完整", message: "请填写展示数量", }) return } const payload: any = { page_number: 1, page_size: nextFilters.page_size, get_data: false, } if (nextFilters.project_type.length > 0) payload.project_type = nextFilters.project_type if (nextFilters.project_id.length > 0) { payload.project_id = toNumberArray(nextFilters.project_id) } if (nextFilters.label_status.length > 0) { payload.label_status = toNumberArray(nextFilters.label_status) } if (nextFilters.current_uid.length > 0) { payload.current_uid = toNumberArray(nextFilters.current_uid) } if (nextFilters.label_user.length > 0) { payload.label_user = toNumberArray(nextFilters.label_user) } if (nextFilters.review_user1.length > 0) { payload.review_user1 = toNumberArray(nextFilters.review_user1) } if (nextFilters.review_user2.length > 0) { payload.review_user2 = toNumberArray(nextFilters.review_user2) } const res = await getTaskList(payload) setRecords(res?.task_list ?? []) setTotal(res?.total_items ?? 0) setTotalSize(res?.total_size ?? null) } catch (e) { setRecords([]) setTotal(0) setTotalSize(null) notifications.show({ color: "red", title: "加载失败", message: e instanceof Error ? e.message : "请求失败", }) } finally { setLoading(false) } } useEffect(() => { queueMicrotask(() => handleSearch(emptyFilters)) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) const columns = useMemo(() => { const cols: DataTableColumn[] = [ { accessor: "project_name", title: "项目名称", width: 220 }, { accessor: "id", title: "任务ID", width: 100 }, { accessor: "label_status", title: "状态", width: 160, render: (record) => ( ), }, { accessor: "current_username", title: "当前负责人", width: 120 }, { accessor: "label_username", title: "标注员", width: 120 }, { accessor: "review1_username", title: "审核员", width: 120 }, { accessor: "review2_username", title: "复审员", width: 120 }, { accessor: "object_size", title: "对象总数", width: 120, render: (record) => record.label_object_size?.object_size ?? 0, }, { accessor: "new_size", title: "新增对象数", width: 120, render: (record) => record.label_object_size?.new_size ?? 0, }, { accessor: "key_frame_size", title: "关键帧数", width: 120, render: (record) => record.label_object_size?.key_frame_size ?? 0, }, { accessor: "dynamic_size", title: "动态对象数", width: 120, render: (record) => record.label_object_size?.dynamic_size ?? 0, }, { accessor: "static_size", title: "静态对象数", width: 120, render: (record) => record.label_object_size?.static_size ?? 0, }, { accessor: "question_size", title: "问题数", width: 120, render: (record) => record.label_object_size?.question_size ?? 0, }, { accessor: "review_size", title: "审核对象数", width: 120, render: (record) => record.label_object_size?.review_size ?? 0, }, { accessor: "first_comment_size", title: "批注数(1)", width: 120, render: (record) => record.label_object_size?.first_comment_size ?? 0, }, { accessor: "second_comment_size", title: "批注数(2)", width: 120, render: (record) => record.label_object_size?.second_comment_size ?? 0, }, { accessor: "first_commit_label_time", title: "标注首次提交时间", width: 180, }, { accessor: "first_commit_review1_time", title: "审核首次提交时间", width: 180, }, { accessor: "reject_times", title: "驳回次数", width: 100 }, { accessor: "label_work_time", title: "标注总耗时", width: 120, render: (record) => record.work_time?.label_work_time ?? 0, }, { accessor: "first_review_work_time", title: "审核总耗时", width: 120, render: (record) => record.work_time?.first_review_work_time ?? 0, }, { accessor: "second_review_work_time", title: "复审总耗时", width: 120, render: (record) => record.work_time?.second_review_work_time ?? 0, }, ] return cols }, []) const handleExport = () => { if (records.length === 0) { notifications.show({ color: "yellow", title: "暂无数据", message: "当前无可导出任务数据", }) return } const header = [ "项目名称", "任务ID", "状态", "当前负责人", "标注员", "审核员", "复审员", "对象总数", "新增对象数", "关键帧数", "动态对象数", "静态对象数", "问题数", "审核对象数", "批注数(1)", "批注数(2)", "标注首次提交时间", "审核首次提交时间", "驳回次数", "标注总耗时", "审核总耗时", "复审总耗时", ] const rows = records.map((item) => [ item.project_name, item.id, `${TaskStatusEnum.get(item.label_status) ?? "-"}${item.rejected ? "返工" : ""}`, item.current_username ?? "-", item.label_username ?? "-", item.review1_username ?? "-", item.review2_username ?? "-", item.label_object_size?.object_size ?? 0, item.label_object_size?.new_size ?? 0, item.label_object_size?.key_frame_size ?? 0, item.label_object_size?.dynamic_size ?? 0, item.label_object_size?.static_size ?? 0, item.label_object_size?.question_size ?? 0, item.label_object_size?.review_size ?? 0, item.label_object_size?.first_comment_size ?? 0, item.label_object_size?.second_comment_size ?? 0, item.first_commit_label_time ?? "-", item.first_commit_review1_time ?? "-", item.reject_times ?? 0, item.work_time?.label_work_time ?? 0, item.work_time?.first_review_work_time ?? 0, item.work_time?.second_review_work_time ?? 0, ]) const wb = XLSX.utils.book_new() const ws = XLSX.utils.aoa_to_sheet([header, ...rows]) XLSX.utils.book_append_sheet(wb, ws, "任务列表数据") XLSX.writeFile(wb, "任务列表数据.xlsx") } return ( setFilters((s) => ({ ...s, page_size: Number(v) || 1000 })) } style={{ width: 140 }} /> 当前筛选条件下共 {records.length} / {total} 条数据 setFilters((s) => ({ ...s, project_type: v }))} /> setFilters((s) => ({ ...s, project_id: v }))} /> ({ label, value: String(value), }) )} searchable clearable value={filters.label_status} onChange={(v) => setFilters((s) => ({ ...s, label_status: v }))} /> setFilters((s) => ({ ...s, current_uid: v }))} /> setFilters((s) => ({ ...s, label_user: v }))} /> setFilters((s) => ({ ...s, review_user1: v }))} /> setFilters((s) => ({ ...s, review_user2: v }))} /> withTableBorder withRowBorders fetching={loading} records={records} columns={columns} noRecordsText="暂无数据" idAccessor="id" scrollAreaProps={{ type: "auto" }} style={{ width: "100%" }} /> 当前页总计 对象总数:{totalSize?.object_size ?? 0} 新增对象数:{totalSize?.new_size ?? 0} 关键帧数:{totalSize?.key_frame_size ?? 0} 动态对象数:{totalSize?.dynamic_size ?? 0} 静态对象数:{totalSize?.static_size ?? 0} 问题数:{totalSize?.question_size ?? 0} 审核对象数:{totalSize?.review_size ?? 0} 批注数(1):{totalSize?.first_comment_size ?? 0} 批注数(2):{totalSize?.second_comment_size ?? 0} ) }