feat(08-05-interaction-pattern-unification): 统一前端交互与反馈模式
这个提交包含在:
@@ -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>
|
||||
|
||||
在新工单中引用
屏蔽一个用户