import { useEffect, useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { mapApiErrorMessage } from "../api/errorMessages"; import { isApiError } from "../api/types"; import { createApprovalRule, fetchApprovalRules, patchApprovalRule, type ApprovalRoleCode, type ApprovalRule, } from "../api/approvalRules"; import { PageHeader } from "../components/common/PageHeader"; import { Button } from "../components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../components/ui/dialog"; import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table"; import { toast } from "sonner"; import { TableSkeleton } from "../components/common/TableSkeleton"; import { ConfirmDialog } from "../components/common/ConfirmDialog"; const ROLE_OPTIONS: Array<{ code: ApprovalRoleCode; label: string }> = [ { code: "manager", label: "manager" }, { code: "ceo", label: "ceo" }, { code: "finance", label: "finance" }, { code: "finance_mgr", label: "finance_mgr" }, { code: "admin", label: "admin" }, ]; function formatAmount(value: string | null) { if (!value) return "-"; return value; } export function ApprovalRulesPage() { const queryClient = useQueryClient(); const [error, setError] = useState(""); const [name, setName] = useState(""); const [minAmount, setMinAmount] = useState(""); const [maxAmount, setMaxAmount] = useState(""); const [priority, setPriority] = useState(""); const [level1Role, setLevel1Role] = useState("manager"); const [level2Role, setLevel2Role] = useState(""); const [level3Role, setLevel3Role] = useState(""); const [enabled, setEnabled] = useState(true); const [editOpen, setEditOpen] = useState(false); const [editRule, setEditRule] = useState(null); const [confirmToggleRule, setConfirmToggleRule] = useState(null); const listQuery = useQuery({ queryKey: ["approval-rules"], queryFn: () => fetchApprovalRules(), }); const rules = listQuery.data?.items ?? []; const createPayload = useMemo(() => { return { name: name.trim(), minAmount: minAmount.trim(), maxAmount: maxAmount.trim() ? maxAmount.trim() : null, level1Role, level2Role: level2Role ? (level2Role as ApprovalRoleCode) : null, level3Role: level3Role ? (level3Role as ApprovalRoleCode) : null, enabled, priority: priority.trim() ? Number(priority) : undefined, }; }, [enabled, level1Role, level2Role, level3Role, maxAmount, minAmount, name, priority]); const createMutation = useMutation({ mutationFn: () => createApprovalRule(createPayload), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["approval-rules"] }); setName(""); setMinAmount(""); setMaxAmount(""); setPriority(""); setLevel1Role("manager"); setLevel2Role(""); setLevel3Role(""); setEnabled(true); setError(""); toast.success("规则已创建"); }, onError: (err) => { const msg = isApiError(err) ? `${err.code}: ${mapApiErrorMessage(err.code, err.details, err.message)}` : err instanceof Error ? err.message : "创建审批规则失败"; setError(msg); toast.error(msg); }, }); const toggleEnabledMutation = useMutation({ mutationFn: (rule: ApprovalRule) => patchApprovalRule(rule.id, { enabled: !rule.enabled }), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["approval-rules"] }); setError(""); toast.success("状态已更新"); }, onError: (err) => { const msg = isApiError(err) ? `${err.code}: ${mapApiErrorMessage(err.code, err.details, err.message)}` : err instanceof Error ? err.message : "更新审批规则失败"; setError(msg); toast.error(msg); }, }); const editMutation = useMutation({ mutationFn: async () => { if (!editRule) { throw new Error("请选择要编辑的规则"); } return patchApprovalRule(editRule.id, createPayload); }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["approval-rules"] }); setEditOpen(false); setEditRule(null); setError(""); toast.success("规则已更新"); }, onError: (err) => { const msg = isApiError(err) ? `${err.code}: ${mapApiErrorMessage(err.code, err.details, err.message)}` : err instanceof Error ? err.message : "更新审批规则失败"; setError(msg); toast.error(msg); }, }); useEffect(() => { if (!editOpen || !editRule) { return; } setName(editRule.name); setMinAmount(editRule.minAmount); setMaxAmount(editRule.maxAmount ?? ""); setPriority(String(editRule.priority)); setEnabled(editRule.enabled); setLevel1Role(editRule.level1Role); setLevel2Role(editRule.level2Role ?? ""); setLevel3Role(editRule.level3Role ?? ""); }, [editOpen, editRule]); return (
{error ? (

{error}

) : null}

新建审批规则

setName(event.target.value)} />
setMinAmount(event.target.value)} />
setMaxAmount(event.target.value)} />
setPriority(event.target.value)} />
setEnabled(event.target.checked)} type="checkbox" />

规则列表

{listQuery.isLoading ? : null} {listQuery.isError ? (

{isApiError(listQuery.error) ? `${listQuery.error.code}: ${mapApiErrorMessage( listQuery.error.code, listQuery.error.details, listQuery.error.message, )}` : "加载审批规则失败"}

) : null} {!listQuery.isLoading && !listQuery.isError ? (
名称金额段层级优先级状态操作 {rules.length ? ( rules.map((rule) => (
{rule.name}
{rule.id}
{formatAmount(rule.minAmount)} ~ {rule.maxAmount ? formatAmount(rule.maxAmount) : "∞"} {[rule.level1Role, rule.level2Role, rule.level3Role].filter(Boolean).join(" -> ") || "-"} {rule.priority} {rule.enabled ? 启用 : 停用}
)) ) : ( 暂无审批规则 )}
) : null}
{ if (!open) setConfirmToggleRule(null); }} title={`确认${confirmToggleRule?.enabled ? "停用" : "启用"}该审批规则?`} onConfirm={() => { if (confirmToggleRule) toggleEnabledMutation.mutate(confirmToggleRule); }} /> 编辑审批规则 修改后只影响新提交的报销单。
setName(event.target.value)} />
setMinAmount(event.target.value)} />
setMaxAmount(event.target.value)} />
setPriority(event.target.value)} />
setEnabled(event.target.checked)} type="checkbox" />
); }