R7E-16E/R7E-16F 高风险操作确认统一
这个提交包含在:
@@ -26,6 +26,7 @@ import { toast } from "sonner";
|
||||
import { downloadBlob } from "../lib/download";
|
||||
import { resolveReimbursementDisplayCode } from "../lib/reimbursementDisplay";
|
||||
import { Pagination } from "../components/common/Pagination";
|
||||
import { ActionConfirmDialog } from "../components/common/ActionConfirmDialog";
|
||||
import { StatusBadge } from "../components/common/StatusBadge";
|
||||
import { TableSkeleton } from "../components/common/TableSkeleton";
|
||||
import { FilterBar, ListWorkspace, PrimaryTable, StatStrip, WorkspaceHeader } from "../components/patterns/ListWorkspace";
|
||||
@@ -52,6 +53,7 @@ import { Textarea } from "../components/ui/textarea";
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
type BatchAction = "approve" | "reject";
|
||||
type ExportFormat = "csv" | "xlsx";
|
||||
|
||||
type BatchResultState = {
|
||||
action: BatchAction;
|
||||
@@ -104,6 +106,8 @@ export function ReimbursementsPage() {
|
||||
const [batchDialogAction, setBatchDialogAction] = useState<BatchAction | null>(null);
|
||||
const [batchComment, setBatchComment] = useState("");
|
||||
const [batchResult, setBatchResult] = useState<BatchResultState | null>(null);
|
||||
const [deleteTarget, setDeleteTarget] = useState<ReimbursementListItem | null>(null);
|
||||
const [exportConfirmFormat, setExportConfirmFormat] = useState<ExportFormat | null>(null);
|
||||
|
||||
const status = (searchParams.get("status") as ReimbursementStatus | null) ?? "draft";
|
||||
const page = Number(searchParams.get("page") ?? "1") || 1;
|
||||
@@ -216,15 +220,11 @@ export function ReimbursementsPage() {
|
||||
});
|
||||
|
||||
const handleDeleteDraft = (item: ReimbursementListItem) => {
|
||||
const displayCode = resolveReimbursementDisplayCode(item);
|
||||
if (!window.confirm(`确认删除 ${displayCode}?删除后无法恢复。`)) {
|
||||
return;
|
||||
}
|
||||
deleteMutation.mutate(item.id);
|
||||
setDeleteTarget(item);
|
||||
};
|
||||
|
||||
const exportMutation = useMutation({
|
||||
mutationFn: (format: "csv" | "xlsx") => exportReimbursements({ format, status, scope }),
|
||||
mutationFn: (format: ExportFormat) => exportReimbursements({ format, status, scope }),
|
||||
onSuccess: (response, format) => {
|
||||
downloadBlob(response.blob, response.filename ?? `reimbursements.${format}`);
|
||||
setExportError("");
|
||||
@@ -461,13 +461,13 @@ export function ReimbursementsPage() {
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
data-testid="reimbursements-export-csv"
|
||||
onSelect={() => exportMutation.mutate("csv")}
|
||||
onSelect={() => setExportConfirmFormat("csv")}
|
||||
>
|
||||
导出 CSV
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
data-testid="reimbursements-export-xlsx"
|
||||
onSelect={() => exportMutation.mutate("xlsx")}
|
||||
onSelect={() => setExportConfirmFormat("xlsx")}
|
||||
>
|
||||
导出 Excel
|
||||
</DropdownMenuItem>
|
||||
@@ -943,6 +943,33 @@ export function ReimbursementsPage() {
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<ActionConfirmDialog
|
||||
open={Boolean(deleteTarget)}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setDeleteTarget(null);
|
||||
}}
|
||||
kind="delete"
|
||||
targetName={deleteTarget ? resolveReimbursementDisplayCode(deleteTarget) : undefined}
|
||||
description="删除后无法恢复,已提交或审批中的报销单不会受到影响。"
|
||||
onConfirm={async () => {
|
||||
if (deleteTarget) {
|
||||
deleteMutation.mutate(deleteTarget.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ActionConfirmDialog
|
||||
open={Boolean(exportConfirmFormat)}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setExportConfirmFormat(null);
|
||||
}}
|
||||
kind="sensitiveExport"
|
||||
targetName={exportConfirmFormat === "xlsx" ? "报销 Excel" : "报销 CSV"}
|
||||
onConfirm={async () => {
|
||||
if (exportConfirmFormat) {
|
||||
exportMutation.mutate(exportConfirmFormat);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</ListWorkspace>
|
||||
);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户