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

这个提交包含在:
2026-08-07 06:50:40 +08:00
父节点 d4e9597b37
当前提交 94f1bad336
共修改 97 个文件,包含 784 行新增和 836 行删除
+3 -3
查看文件
@@ -43,7 +43,7 @@ export function getActionConfirmCopy(kind: ActionConfirmKind, context: ActionCon
const copies: Record<ActionConfirmKind, ActionConfirmCopy> = {
submit: {
title: withTarget("确认提交审批", targetName),
description: detail ?? "提交后将进入审批流程,系统会生成待办、通知审批人并写入提交历史。",
description: detail ?? "提交后将进入审批流程,系统会生成审批任务、通知审批人并写入提交历史。",
confirmLabel: "确认提交",
variant: "default",
},
@@ -85,13 +85,13 @@ export function getActionConfirmCopy(kind: ActionConfirmKind, context: ActionCon
},
batchApprove: {
title: `确认批量通过${count ? ` ${countText}` : ""}`,
description: detail ?? "系统会逐条处理所选待办;每条结果都会写入审批历史。",
description: detail ?? "系统会逐条处理所选审批任务;每条结果都会写入审批历史。",
confirmLabel: "确认批量通过",
variant: "default",
},
batchReject: {
title: `确认批量驳回${count ? ` ${countText}` : ""}`,
description: detail ?? "系统会逐条驳回所选待办;驳回意见会写入审批历史。",
description: detail ?? "系统会逐条驳回所选审批任务;驳回意见会写入审批历史。",
confirmLabel: "确认批量驳回",
variant: "destructive",
},
+10 -6
查看文件
@@ -2,27 +2,31 @@ import type { PrecheckDecision, PrecheckResponse } from "../api/feeControl";
function decisionLabel(decision: PrecheckDecision) {
if (decision === "pass") return "通过";
if (decision === "warn") return "预警";
if (decision === "manual_review") return "需人工复核";
return "阻断";
if (decision === "warn") return "可继续,但需确认提醒";
if (decision === "manual_review") return "需人工复核后继续";
return "暂不能提交";
}
export function buildPrecheckDialogMessage(precheck: PrecheckResponse) {
const lines = [`提交前校验结果:${decisionLabel(precheck.decision)}`];
if (precheck.matchedBudgetControlRule) {
lines.push(`命中规则:${precheck.matchedBudgetControlRule.name}(${precheck.matchedBudgetControlRule.controlMode})`);
lines.push(
`命中规则:${precheck.matchedBudgetControlRule.name}(${
precheck.matchedBudgetControlRule.controlMode === "block" ? "需先处理超标项" : "提交前请确认超标原因"
})`,
);
}
if (precheck.blockingReasons.length) {
lines.push("", "阻断原因:");
lines.push("", "暂不能提交的原因:");
precheck.blockingReasons.forEach((item, index) => {
lines.push(`${index + 1}. ${item.title}${item.detail ? `:${item.detail}` : ""}`);
});
}
if (precheck.warnings.length) {
lines.push("", "预警信息:");
lines.push("", "提交前提醒:");
precheck.warnings.forEach((item, index) => {
lines.push(`${index + 1}. ${item.title}${item.detail ? `:${item.detail}` : ""}`);
});
+25 -4
查看文件
@@ -1,14 +1,35 @@
import { isApiError } from "../api/types";
import { mapApiErrorMessage } from "../api/errorMessages";
export function resolveErrorMessage(error: unknown): string {
function toUserSafeMessage(message: string | undefined, fallback: string) {
const normalized = (message ?? "")
.trim()
.replace(/^(?:[A-Z][A-Z0-9_]{2,}|\d{3})[::]\s*/, "");
if (!normalized) {
return fallback;
}
const exposesTechnicalDetail =
!/[\u3400-\u9fff]/.test(normalized) ||
/https?:\/\/|\/api\/|(?:^|\s)\/[A-Za-z0-9._-]+(?:\/[^\s]*)?|[A-Za-z]:\\|(?:^|\n)\s*at\s|\b(?:GET|POST|PUT|PATCH|DELETE)\s+\/|^(?:\{|\[)/.test(normalized);
return exposesTechnicalDetail ? fallback : normalized;
}
export function resolveErrorMessage(
error: unknown,
fallback = "操作失败,请稍后重试",
): string {
if (isApiError(error)) {
return `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`;
return toUserSafeMessage(
mapApiErrorMessage(error.code, error.details, error.message),
fallback,
);
}
if (error instanceof Error) {
return error.message;
return toUserSafeMessage(error.message, fallback);
}
return "操作失败,请稍后重试";
return fallback;
}