"use client" import { getProjectList, getProjectTypeList, projectAccept, projectCollect, projectDeliver, projectEditById, projectPreLabelReview, } from "@/components/label/api/project" import { Project } from "@/components/label/api/project/typing" import { initAllParams, initCollectParams, initDynamicParams, useAllUserStore, usePermissionStore, useProjectStore, } from "@/components/label/store/auth" import { projectLabelTypeOpts, projectStatusOpts, STATUS_CODE, } from "@/components/label/utils/constants" import { ActionIcon, Badge, Button, Collapse, CopyButton, Divider, Flex, Group, Loader, Modal, Pagination, Paper, Select, SimpleGrid, Stack, Text, Textarea, TextInput, Tooltip, UnstyledButton, } from "@mantine/core" import { useForm } from "@mantine/form" import { notifications } from "@mantine/notifications" import { IconCalendar, IconCheck, IconChevronDown, IconChevronUp, IconCopy, IconEdit, IconFileUpload, IconInfoCircle, IconRefresh, IconSearch, IconStar, IconStarFilled, } from "@tabler/icons-react" import dayjs from "dayjs" import { User2 } from "lucide-react" import { useRouter } from "next/navigation" import { useCallback, useEffect, useMemo, useState, type ReactNode, } from "react" import { CardCoverIcon } from "./components/ProjectIcon" function statusBadgeColor(status?: number | null) { if (!status) return "gray" if ([191, 199, 700].includes(status)) return "red" if ([580].includes(status)) return "yellow" if ([600].includes(status)) return "green" if ([201, 202, 203].includes(status)) return "cyan" return "blue" } function parseStatusValue(value?: string) { if (!value) return undefined if (value.includes(",")) { return value .split(",") .map((v) => Number(v)) .filter((n) => Number.isFinite(n)) } const n = Number(value) return Number.isFinite(n) ? [n] : undefined } function formatPercent(value?: number | null) { if (value === 0) return "0.00%" if (!value) return "-" return `${(value * 100).toFixed(2)}%` } function getProcessText(record: any) { const rateSpeed = record?.rate_speed if (!rateSpeed || typeof rateSpeed !== "object") return "-" const map = new Map([ ["标注", "label_speed"], ["审核", "review1_speed"], ["复审", "review2_speed"], ]) const arr = ["标注", "审核", "复审"] const parts = arr.map((process) => { const key = map.get(process) const staticKey = key ? `static_${key}` : "" const dynamicKey = key ? `dynamic_${key}` : "" const staticValue = staticKey && (rateSpeed[staticKey] || rateSpeed[staticKey] === 0) ? rateSpeed[staticKey] : "-" const dynamicValue = dynamicKey && (rateSpeed[dynamicKey] || rateSpeed[dynamicKey] === 0) ? rateSpeed[dynamicKey] : "-" return `${process}(${staticValue}/${dynamicValue})` }) return parts.join(",") } export default function ProjectInfoPage({ type, }: { type: "dynamic" | "collect" | "" }) { const router = useRouter() const { userOpts } = useAllUserStore() const userName = usePermissionStore((s) => s.user_name) const { allParams, allTotal, collectParams, collectTotal, dynamicParams, dynamicTotal, resetParams, setSearchParams, setTotal, }: any = useProjectStore() const storeParams = useMemo(() => { return type === "collect" ? collectParams : type === "dynamic" ? dynamicParams : allParams }, [allParams, collectParams, dynamicParams, type]) const total = useMemo(() => { return type === "collect" ? collectTotal : type === "dynamic" ? dynamicTotal : allTotal }, [allTotal, collectTotal, dynamicTotal, type]) const [typeOpts, setTypeOpts] = useState< Array<{ label: string; value: string }> >([]) const [loading, setLoading] = useState(false) const [records, setRecords] = useState([]) const [searchExpanded, setSearchExpanded] = useState(false) const [filters, setFilters] = useState(() => { return { project_name: "", project_type: "", status: "", owner: "", label_type: "", admin_user: "", start_date: dayjs().subtract(6, "day").format("YYYY-MM-DD"), end_date: dayjs().format("YYYY-MM-DD"), } }) const [appliedFilters, setAppliedFilters] = useState(filters) const [queryTrigger, setQueryTrigger] = useState(0) useEffect(() => { const initFormOpts = async () => { const res = await getProjectTypeList() setTypeOpts((res ?? []).map((name) => ({ label: name, value: name }))) } initFormOpts() }, []) const queryParams = useMemo(() => { const base = type === "collect" ? { ...initCollectParams } : type === "dynamic" ? { ...initDynamicParams } : { ...initAllParams } const params: Project.ListRequest & any = { ...base, ...storeParams, page_number: storeParams.page_number ?? 1, page_size: storeParams.page_size ?? 15, project_name: appliedFilters.project_name || undefined, project_type: appliedFilters.project_type || undefined, owner: appliedFilters.owner || undefined, admin_user: appliedFilters.admin_user || undefined, start_date: appliedFilters.start_date || undefined, end_date: appliedFilters.end_date || undefined, label_type: appliedFilters.label_type ? Number(appliedFilters.label_type) : undefined, status: parseStatusValue(appliedFilters.status), } return params }, [appliedFilters, storeParams, type]) const load = useCallback(async () => { if (!queryTrigger) return setLoading(true) try { const res = await getProjectList(queryParams) setRecords(res?.project_list ?? []) setTotal(res?.total_items ?? 0, type) } catch (e) { setRecords([]) setTotal(0, type) notifications.show({ color: "red", title: "加载失败", message: e instanceof Error ? e.message : "请求失败", }) } finally { setLoading(false) } }, [queryParams, queryTrigger, setTotal, type]) useEffect(() => { load() }, [load]) useEffect(() => { queueMicrotask(() => { setQueryTrigger((v) => v + 1) }) }, []) const currentPage = storeParams.page_number ?? 1 const pageSize = storeParams.page_size ?? 15 const [infoModalRecord, setInfoModalRecord] = useState(null) const [editModalRecord, setEditModalRecord] = useState(null) const [confirmAction, setConfirmAction] = useState<{ record: Project.DataProps | null action: "试标完成" | "试标通过" | "提交验收" | "验收通过" | null }>({ record: null, action: null }) const editForm = useForm({ initialValues: { project_name: "", owner: "", remarks: "", }, }) const confirmActionTitle = useMemo(() => { return confirmAction.action ?? "" }, [confirmAction.action]) const runConfirmAction = useCallback(async () => { const record = confirmAction.record const action = confirmAction.action if (!record || !action) return try { if (action === "试标完成") { await projectPreLabelReview({ id: record.id, review_user: userName ?? "", action: 1, }) } else if (action === "试标通过") { await projectPreLabelReview({ id: record.id, review_user: userName ?? "", action: 2, }) } else if (action === "提交验收") { await projectDeliver({ project_id: record.id }) } else if (action === "验收通过") { await projectAccept({ project_id: record.id, pass: true }) } notifications.show({ color: "green", title: "操作成功", message: `${action} 已提交`, }) setConfirmAction({ record: null, action: null }) setQueryTrigger((v) => v + 1) } catch (e) { notifications.show({ color: "red", title: "操作失败", message: e instanceof Error ? e.message : "请求失败", }) } }, [confirmAction.action, confirmAction.record, userName]) const ProjectCard = (record: Project.DataProps) => { const status = record.status const canEdit = status < 600 const actionKey = status === 201 ? "试标完成" : status === 202 ? "试标通过" : status === 500 ? "提交验收" : status === 580 ? "验收通过" : null const canDoAction = !!actionKey return ( { const url = type ? `/management/project/detail?id=${record.id}&type=${type}` : `/management/project/detail?id=${record.id}` router.push(url) }}> {record.name} {STATUS_CODE.get(record.status) ?? record.status} {record.owner ?? "-"} {record.create_at ? record.create_at.split(" ")[0] : "-"} { try { await projectCollect({ project_id: record.id, is_collect: !record.is_collect, }) notifications.show({ color: "green", title: "操作成功", message: record.is_collect ? "已取消收藏" : "已收藏", }) setQueryTrigger((v) => v + 1) } catch (e) { notifications.show({ color: "red", title: "操作失败", message: e instanceof Error ? e.message : "请求失败", }) } }}> {record.is_collect ? ( ) : ( )} 备注:{record.remarks ? record.remarks : "暂无备注"}
{ if (!canEdit) return editForm.setValues({ project_name: record.name ?? "", owner: record.owner ?? "", remarks: record.remarks ?? "", }) editForm.resetDirty() setEditModalRecord(record) }}>
{ if (!actionKey) return setConfirmAction({ record, action: actionKey }) }}>
setInfoModalRecord(record)}>
) } const PaginationBar = (props: { currentPage: number pageSize: number total: number onChangePage: (page: number) => void onChangePageSize: (size: number) => void }) => { const pageCount = Math.max( 1, Math.ceil((props.total ?? 0) / props.pageSize) ) return (
setFilters((s) => ({ ...s, project_type: v ?? "" })) } /> ({ label: o.label, value: String(o.value), }))} value={filters.label_type || null} onChange={(v) => setFilters((s) => ({ ...s, label_type: v ?? "" })) } />