feat(08-05-interaction-pattern-unification): 统一前端交互与反馈模式

这个提交包含在:
2026-08-07 06:50:40 +08:00
父节点 d4e9597b37
当前提交 94f1bad336
共修改 97 个文件,包含 784 行新增和 836 行删除
+16 -83
查看文件
@@ -1,9 +1,8 @@
import { resolveErrorMessage as resolveUserErrorMessage } from "../lib/resolveErrorMessage";
import { useCallback, useEffect, useState } from "react";
import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { ArrowLeft, ChevronRight } from "lucide-react";
import { isApiError } from "../api/types";
import { mapApiErrorMessage } from "../api/errorMessages";
import { fetchExpenseTypes } from "../api/expenseTypes";
import type { ExpenseStandardCheck, PrecheckResponse } from "../api/feeControl";
import { fetchProjects } from "../api/projects";
@@ -100,15 +99,7 @@ type ItemEditorState = { mode: "create" } | { mode: "edit"; itemId: string } | n
type SelectedInvoiceMap = Record<string, string>;
function buildActionErrorMessage(error: unknown, fallbackMessage: string) {
if (isApiError(error)) {
return `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`;
}
if (error instanceof Error) {
return error.message;
}
return fallbackMessage;
return resolveUserErrorMessage(error, fallbackMessage);
}
export function ReimbursementDetailPage() {
@@ -247,21 +238,9 @@ export function ReimbursementDetailPage() {
toast.success(actionMessages[params.action] ?? "操作成功");
},
onError: (error) => {
if (isApiError(error)) {
const msg = `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`;
setActionError(msg);
toast.error(msg);
return;
}
if (error instanceof Error) {
setActionError(error.message);
toast.error(error.message);
return;
}
setActionError("操作失败,请稍后重试");
toast.error("操作失败,请稍后重试");
const message = buildActionErrorMessage(error, "操作失败,请稍后重试");
setActionError(message);
toast.error(message);
},
});
@@ -315,21 +294,9 @@ export function ReimbursementDetailPage() {
toast.success("明细已保存");
},
onError: (error) => {
if (isApiError(error)) {
const msg = `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`;
setItemError(msg);
toast.error(msg);
return;
}
if (error instanceof Error) {
setItemError(error.message);
toast.error(error.message);
return;
}
setItemError("保存明细失败,请稍后重试");
toast.error("保存明细失败,请稍后重试");
const message = buildActionErrorMessage(error, "保存明细失败,请稍后重试");
setItemError(message);
toast.error(message);
},
});
@@ -351,21 +318,9 @@ export function ReimbursementDetailPage() {
toast.success("明细已删除");
},
onError: (error) => {
if (isApiError(error)) {
const msg = `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`;
setItemError(msg);
toast.error(msg);
return;
}
if (error instanceof Error) {
setItemError(error.message);
toast.error(error.message);
return;
}
setItemError("删除明细失败,请稍后重试");
toast.error("删除明细失败,请稍后重试");
const message = buildActionErrorMessage(error, "删除明细失败,请稍后重试");
setItemError(message);
toast.error(message);
},
});
@@ -401,11 +356,7 @@ export function ReimbursementDetailPage() {
toast.success("已更新关联项目");
},
onError: (error) => {
const message = isApiError(error)
? `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`
: error instanceof Error
? error.message
: "关联项目失败";
const message = buildActionErrorMessage(error, "关联项目失败");
toast.error(message);
},
});
@@ -756,13 +707,7 @@ export function ReimbursementDetailPage() {
}
if (detailQuery.isError || !detailQuery.data) {
const message = isApiError(detailQuery.error)
? `${detailQuery.error.code}: ${mapApiErrorMessage(
detailQuery.error.code,
detailQuery.error.details,
detailQuery.error.message,
)}`
: "加载报销详情失败";
const message = resolveUserErrorMessage(detailQuery.error, "加载报销详情失败");
return (
<div className="card-std space-y-3 p-5">
@@ -893,21 +838,9 @@ export function ReimbursementDetailPage() {
mutation.mutate({ action: "submit" });
} catch (error) {
if (isApiError(error)) {
const message = `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`;
setActionError(message);
toast.error(message);
return;
}
if (error instanceof Error) {
setActionError(error.message);
toast.error(error.message);
return;
}
setActionError("提交前校验失败,请稍后重试");
toast.error("提交前校验失败,请稍后重试");
const message = buildActionErrorMessage(error, "提交前校验失败,请稍后重试");
setActionError(message);
toast.error(message);
}
};