import { resolveErrorMessage as resolveUserErrorMessage } from "../lib/resolveErrorMessage"; import { useMemo, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { fetchWecomApprovalArchive, fetchWecomApprovalArchiveDetail, type WecomApprovalArchiveFinding, type WecomApprovalArchiveItem, type WecomApprovalArchiveScope, type WecomApprovalArchiveStatus, type WecomApprovalArchiveType, } from "../api/wecomApprovalArchive"; import { useAuth } from "../auth/AuthContext"; import { PageHeader } from "../components/common/PageHeader"; import { StatusBadge } from "../components/common/StatusBadge"; import { TableSkeleton } from "../components/common/TableSkeleton"; import { SectionPanel } from "../components/patterns/SectionPanel"; import { Button } from "../components/ui/button"; import { Label } from "../components/ui/label"; const TYPE_OPTIONS: Array<{ value: "" | WecomApprovalArchiveType; label: string }> = [ { value: "", label: "全部类型" }, { value: "leave", label: "请假" }, { value: "missed_punch", label: "补卡" }, ]; const STATUS_OPTIONS: Array<{ value: "" | WecomApprovalArchiveStatus; label: string }> = [ { value: "", label: "全部状态" }, { value: "approved", label: "已通过" }, { value: "rejected", label: "已驳回" }, { value: "revoked", label: "已撤销" }, { value: "terminated", label: "已终止" }, { value: "pending", label: "处理中" }, { value: "unknown", label: "未知" }, ]; const FINDING_OPTIONS: Array<{ value: "" | WecomApprovalArchiveFinding; label: string }> = [ { value: "", label: "全部对账" }, { value: "open", label: "仅未关闭问题" }, { value: "resolved", label: "已关闭问题" }, { value: "any", label: "有对账问题" }, ]; function resolveError(error: unknown, fallback = "读取企微审批历史失败") { return resolveUserErrorMessage(error, fallback); } function typeLabel(value: string) { if (value === "leave") return "请假"; if (value === "missed_punch") return "补卡"; return value || "未知"; } function sourceLabel(value: string | null) { if (value === "callback") return "回调"; if (value === "compensation_pull") return "补偿拉取"; return value || "-"; } function actionLabel(value: string) { if (value === "approve") return "同意"; if (value === "reject") return "驳回"; if (value === "revoke") return "撤销"; if (value === "terminate") return "终止"; if (value === "pending") return "状态记录"; return value || "状态记录"; } function roleLabel(value: string) { if (value === "applicant") return "申请人"; if (value === "approver") return "审批人"; if (value === "notifyer") return "抄送人"; return value || "-"; } function formatTime(value: string | null) { if (!value) return "-"; const date = new Date(value); if (Number.isNaN(date.getTime())) return "-"; return date.toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false, }); } function employeeLabel(employee: WecomApprovalArchiveItem["applicant"]) { if (!employee) return "未映射"; return employee.employeeNo ? `${employee.name}(${employee.employeeNo})` : employee.name; } function canRequestAll(roleCodes: string[], permissions: string[]) { return roleCodes.includes("hr") || roleCodes.includes("admin") || permissions.includes("*"); } function ArchiveRow({ item, active, onSelect, }: { item: WecomApprovalArchiveItem; active: boolean; onSelect: () => void; }) { return ( <>
{typeLabel(item.templateType)}
{item.spName || "OA-POC 归档记录"}
单号前缀:{item.spNoPrefix || "-"}
{employeeLabel(item.applicant)} {formatTime(item.statusObservedAt || item.completedAt || item.appliedAt)}
{sourceLabel(item.lastSource)}
{item.openFindingCount ?
对账问题 {item.openFindingCount} 项
: null} ); } export function WecomApprovalArchivePage() { const { me, permissions } = useAuth(); const [scope, setScope] = useState("self"); const [type, setType] = useState<"" | WecomApprovalArchiveType>(""); const [status, setStatus] = useState<"" | WecomApprovalArchiveStatus>(""); const [finding, setFinding] = useState<"" | WecomApprovalArchiveFinding>(""); const [selectedId, setSelectedId] = useState(""); const allAllowed = canRequestAll(me?.roleCodes ?? [], permissions); const query = useMemo(() => ({ scope, type, status, finding, pageSize: 50 }), [scope, type, status, finding]); const listQuery = useQuery({ queryKey: ["wecom-approval-archive", query], queryFn: () => fetchWecomApprovalArchive(query), }); const detailQuery = useQuery({ enabled: Boolean(selectedId), queryKey: ["wecom-approval-archive", "detail", selectedId], queryFn: () => fetchWecomApprovalArchiveDetail(selectedId), }); const items = listQuery.data?.items ?? []; const detail = detailQuery.data?.item ?? null; return (
企业微信主办,OA 自建留存 仅已归档记录 } />
{listQuery.isLoading ? : null} {listQuery.isError ?
{resolveError(listQuery.error)}
: null} {!listQuery.isLoading && !listQuery.isError && !items.length ? (
暂无已归档的企微审批历史;如数据尚未同步,可稍后重试或联系管理员核对归档状态。
) : null} {!listQuery.isLoading && !listQuery.isError && items.length ? ( <>
{items.map((item) => ( setSelectedId(item.id)} /> ))}
类型/单号 申请人 状态 状态时间 来源/对账 详情
{items.map((item) => (
{typeLabel(item.templateType)}
单号前缀:{item.spNoPrefix || "-"}
{employeeLabel(item.applicant)} · {formatTime(item.statusObservedAt || item.completedAt || item.appliedAt)}
{sourceLabel(item.lastSource)}{item.openFindingCount ? ` · 对账问题 ${item.openFindingCount}` : ""}
))}
) : null}
{!selectedId ?
请选择一条归档记录查看详情。
: null} {selectedId && detailQuery.isLoading ? : null} {selectedId && detailQuery.isError ?
{resolveError(detailQuery.error)}
: null} {detail ? (
状态
申请人
{employeeLabel(detail.applicant)}
单号前缀
{detail.spNoPrefix || "-"}
{detail.singleTapApproveRisk ?
企微同意可能单击即通过,OA 仅留存动作来源和历史审计。
: null}

字段摘要

{detail.fields.length ? detail.fields.map((field) => (
{field.fieldName || field.fieldKey} {field.fieldType || "-"} {field.sensitiveLevel === "restricted" ? "受限字段" : "普通字段"}
)) :
暂无字段摘要。
}

参与人与动作

{detail.participants.length ? detail.participants.map((item) => (
{roleLabel(item.role)} {employeeLabel(item.employee)}
)) :
暂无映射参与人。
}
{detail.actions.length ? detail.actions.map((item) => (
{actionLabel(item.actionType)} {employeeLabel(item.operator)}
{formatTime(item.actionTime)} · {sourceLabel(item.source)}
)) :
暂无动作历史。
}

对账问题

{detail.findings.length ? detail.findings.map((item) => (
{item.findingType} {item.status} · {item.severity} · {formatTime(item.detectedAt)}
)) :
暂无对账问题。
}
) : null}
); }