import { useMemo, useState } from "react"; import { Link, useLocation, useSearchParams } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { fetchPurchaseRequests, type PurchaseRequestStatus, type PurchaseRequestSummary, } from "../api/purchaseRequests"; import { PageHeader } from "../components/common/PageHeader"; import { StatusBadge } from "../components/common/StatusBadge"; import { PageScaffold } from "../components/patterns/PageScaffold"; import { Input } from "../components/ui/input"; import { Tabs, TabsList, TabsTrigger } from "../components/ui/tabs"; import { resolvePurchaseDisplayCode } from "../lib/approvalDisplay"; import { joinBusinessMeta } from "../lib/businessDisplay"; import { resolveErrorMessage } from "../lib/resolveErrorMessage"; const STATUS_TABS: Array<{ value: PurchaseRequestStatus; label: string }> = [ { value: "draft", label: "草稿" }, { value: "reviewing", label: "审批中" }, { value: "approved", label: "已通过" }, { value: "rejected", label: "已驳回" }, { value: "cancelled", label: "已取消" }, ]; function isPurchaseStatus(value: string | null): value is PurchaseRequestStatus { return ["draft", "reviewing", "approved", "rejected", "cancelled"].includes(value ?? ""); } function formatCurrency(value: string | null | undefined, currencyCode = "CNY") { const amount = Number(value ?? 0); if (!Number.isFinite(amount)) { return "-"; } return new Intl.NumberFormat("zh-CN", { style: "currency", currency: currencyCode }).format(amount); } function formatDate(value: string | null | undefined) { return value ? value.slice(0, 10) : "-"; } function resolvePurchaseListNextStep(status: PurchaseRequestStatus) { if (status === "draft") return "继续完善采购材料"; if (status === "reviewing") return "查看审批进度"; if (status === "rejected") return "按意见补材料"; if (status === "approved") return "核对到货 / 归档"; return "查看取消记录"; } function resolvePurchaseListEvidence(item: PurchaseRequestSummary) { if (!item.supplierId) return "待补供应商"; if (!item.legalEntityId || !item.costCenterId) return "待补公司 / 成本中心"; if (!item.documentCategoryId) return "待补单据分类"; if (!item.budgetPoolId) return "预算池未匹配"; if (!item.projectId) return "未关联项目"; return "基础依据已齐"; } function resolveMobileEmptyState(status: PurchaseRequestStatus) { if (status === "draft") return "暂无采购草稿"; if (status === "reviewing") return "暂无审批中的采购"; if (status === "rejected") return "暂无需要补材料的采购"; if (status === "approved") return "暂无已通过采购"; return "暂无已取消采购"; } function normalizeSearchValue(value: string | null | undefined) { return String(value ?? "").trim().toLowerCase(); } function matchesPurchaseSearch(item: PurchaseRequestSummary, keyword: string) { const searchValue = normalizeSearchValue(keyword); if (!searchValue) { return true; } const haystack = [ resolvePurchaseDisplayCode(item), item.title, item.reason, item.totalAmount, item.applicant?.name, item.supplier?.name, item.project?.name, ].map(normalizeSearchValue).join(" "); return haystack.includes(searchValue); } function PurchaseRequestCard({ item, currentMobileSource, }: { item: PurchaseRequestSummary; currentMobileSource: string; }) { const displayCode = resolvePurchaseDisplayCode(item); return (
{displayCode}
{joinBusinessMeta([ item.title || null, item.applicant?.name ? `申请人:${item.applicant.name}` : null, item.updatedAt ? `更新:${formatDate(item.updatedAt)}` : null, ])}
{joinBusinessMeta([ `金额:${formatCurrency(item.totalAmount, item.currencyCode)}`, item.supplier?.name ? `供应商:${item.supplier.name}` : null, item.expectedDeliveryDate ? `到货:${formatDate(item.expectedDeliveryDate)}` : null, ])}
材料:{resolvePurchaseListEvidence(item)}
下一步:{resolvePurchaseListNextStep(item.status)}
); } export function MobilePurchaseRequestsPage() { const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams(); const [status, setStatus] = useState(() => { const queryStatus = searchParams.get("status"); return isPurchaseStatus(queryStatus) ? queryStatus : "draft"; }); const [searchInput, setSearchInput] = useState(""); const currentMobileSource = useMemo(() => { const params = new URLSearchParams(location.search); params.set("status", status); const query = params.toString(); return `${location.pathname}${query ? `?${query}` : ""}`; }, [location.pathname, location.search, status]); const listQuery = useQuery({ queryKey: ["mobile", "purchase-requests", status], queryFn: () => fetchPurchaseRequests({ mine: true, status, page: 1, pageSize: 20 }), }); function handleStatusChange(value: string) { if (!isPurchaseStatus(value)) { return; } setStatus(value); const nextParams = new URLSearchParams(searchParams); nextParams.set("status", value); setSearchParams(nextParams, { replace: true }); } const items = listQuery.data?.items ?? []; const visibleItems = useMemo( () => items.filter((item) => matchesPurchaseSearch(item, searchInput)), [items, searchInput], ); const totalAmount = useMemo( () => visibleItems.reduce((sum, item) => sum + Number(item.totalAmount ?? 0), 0), [visibleItems], ); return (
当前采购任务
先看材料是否齐,再进详情核对采购依据。
当前列表
{visibleItems.length}
当前金额
{formatCurrency(String(totalAmount))}
setSearchInput(event.target.value)} placeholder="搜索编号、标题、供应商" />
{STATUS_TABS.map((item) => ( {item.label} ))}
{listQuery.isLoading ?
加载中...
: null} {listQuery.isError ? (
{resolveErrorMessage(listQuery.error)}
) : null} {!listQuery.isLoading && !listQuery.isError && !visibleItems.length ? (
{searchInput.trim() ? "没有符合搜索的采购记录" : resolveMobileEmptyState(status)}
) : null} {visibleItems.map((item) => ( ))}
); } export default MobilePurchaseRequestsPage;