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

这个提交包含在:
2026-08-07 06:50:40 +08:00
父节点 d4e9597b37
当前提交 94f1bad336
共修改 97 个文件,包含 784 行新增和 836 行删除
+6 -44
查看文件
@@ -1,11 +1,10 @@
import { resolveErrorMessage as resolveUserErrorMessage } from "../lib/resolveErrorMessage";
import { useEffect } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { mapApiErrorMessage } from "../api/errorMessages";
import { fetchNotifications, markNotificationRead, type NotificationItem } from "../api/notifications";
import { fetchPortalHome } from "../api/portal";
import { isApiError } from "../api/types";
import { fetchWorkflowTasks, fetchWorkflowTaskSummary } from "../api/workflow";
import { useAuth } from "../auth/AuthContext";
import { hasAnyPermission, hasBusinessApproverRole, hasFinanceRole } from "../auth/permissions";
@@ -34,13 +33,7 @@ type MobileAction = {
};
function resolveErrorMessage(error: unknown) {
if (isApiError(error)) {
return `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`;
}
if (error instanceof Error) {
return error.message;
}
return "请求失败,请稍后重试。";
return resolveUserErrorMessage(error, "请求失败,请稍后重试。");
}
function formatDateTime(value: string | null) {
@@ -114,13 +107,6 @@ export function MobilePortalPage() {
const canAccessLeave = hasAnyPermission(permissions, [...MOBILE_LEAVE_PERMISSIONS]);
const canAccessPurchase = hasAnyPermission(permissions, [...MOBILE_PURCHASE_PERMISSIONS]);
const canAccessReimbursements = hasAnyPermission(permissions, [...MOBILE_REIMBURSEMENT_PERMISSIONS]);
const canAccessWecomApprovalArchive = hasAnyPermission(permissions, [
"leave.view_own",
"attendance.view_own",
"leave.view_all",
"attendance.view_all",
"*",
]);
const canCreateExpense = hasAnyPermission(permissions, ["expense.create"]);
const currentMobileSource = `${location.pathname}${location.search}`;
@@ -259,7 +245,7 @@ export function MobilePortalPage() {
return (
<PageScaffold className="mobile-page-stack">
<PageHeader
title="今天待办"
title="今日待处理"
meta={<span className="badge-neutral">移动工作台</span>}
/>
@@ -267,8 +253,8 @@ export function MobilePortalPage() {
<section className="mobile-card mobile-focus-card mobile-page-stack" data-testid="mobile-pending-summary">
<div className="mobile-section-header mobile-section-header--top">
<div className="min-w-0">
<div className="mobile-section-title">待处理</div>
<div className="mobile-list-card__subtitle mt-1">待我处理</div>
<div className="mobile-section-title">待我审批</div>
<div className="mobile-list-card__subtitle mt-1">今日待处理</div>
</div>
<div className="text-right">
<div className="text-3xl font-semibold leading-none text-zinc-900" data-testid="mobile-pending-summary-count">
@@ -307,7 +293,7 @@ export function MobilePortalPage() {
</div>
) : null}
<Button asChild className="mt-3 w-full" size="sm" type="button" variant="secondary">
<Link to="/mobile/approvals">查看全部待办</Link>
<Link to="/mobile/approvals">查看全部审批</Link>
</Button>
</section>
) : null}
@@ -327,10 +313,6 @@ export function MobilePortalPage() {
<div className="text-xs text-zinc-500">异常</div>
<div className="mt-1 text-xl font-semibold text-zinc-900">{financeExceptionCount}</div>
</div>
<div className="rounded-lg bg-zinc-50 px-3 py-2">
<div className="text-xs text-zinc-500">处理</div>
<div className="mt-1 text-xl font-semibold text-zinc-900">OA</div>
</div>
</div>
{paymentBatchEnabled ? (
<Button asChild className="mt-3 w-full" size="sm" type="button" variant="secondary">
@@ -353,26 +335,6 @@ export function MobilePortalPage() {
</section>
) : null}
<section className="mobile-card mobile-page-stack" data-testid="mobile-attendance-summary-card">
<div className="mobile-section-header">
<div className="mobile-section-title">考勤</div>
<span className="badge-neutral">企业微信打卡</span>
</div>
<div className="text-sm leading-6 text-zinc-600">
日常打卡、请假和补卡在企业微信完成;OA 保留已归档历史和考勤汇总。
</div>
<div className="mt-3 grid gap-2">
{canAccessWecomApprovalArchive ? (
<Button asChild className="w-full" size="sm" type="button" variant="secondary">
<Link to="/mobile/wecom-approval-archive">查看企微审批历史</Link>
</Button>
) : null}
<Button asChild className="w-full" size="sm" type="button" variant="secondary">
<Link to="/mobile/attendance">查看考勤汇总</Link>
</Button>
</div>
</section>
<section className="mobile-card mobile-page-stack" data-testid="mobile-notifications">
<div className="mobile-section-header">
<div className="mobile-section-title">通知</div>