DG7/MRF7 移动信息架构与展示去重治理

这个提交包含在:
2026-05-12 21:03:36 +08:00
父节点 b6b7b77c41
当前提交 9da0ca9632
共修改 26 个文件,包含 1556 行新增和 258 行删除
+56
查看文件
@@ -0,0 +1,56 @@
import { isUuidLike } from "./reimbursementDisplay";
export type BusinessEntityType = "reimbursement" | "leave" | "purchase_request" | "travel_request" | string | null | undefined;
const ENTITY_NOUN_LABELS: Record<string, string> = {
reimbursement: "报销单",
leave: "请假",
purchase_request: "采购单",
travel_request: "差旅单",
};
const ENTITY_SHORT_LABELS: Record<string, string> = {
reimbursement: "报销",
leave: "请假",
purchase_request: "采购",
travel_request: "差旅",
};
export function businessEntityLabel(entityType: BusinessEntityType, options: { noun?: boolean } = {}) {
if (!entityType) {
return options.noun ? "业务单据" : "业务";
}
const labels = options.noun ? ENTITY_NOUN_LABELS : ENTITY_SHORT_LABELS;
return labels[entityType] ?? String(entityType);
}
export function compactBusinessReference(value: string | null | undefined) {
const trimmed = value?.trim();
if (!trimmed || isUuidLike(trimmed)) {
return null;
}
return trimmed;
}
export function businessPrimaryLabel(input: {
entityType: BusinessEntityType;
reference?: string | null;
title?: string | null;
fallback?: string;
}) {
const reference = compactBusinessReference(input.reference);
if (reference) {
return `${businessEntityLabel(input.entityType, { noun: true })} ${reference}`;
}
const title = input.title?.trim();
if (title && !isUuidLike(title)) {
return title;
}
return input.fallback ?? businessEntityLabel(input.entityType, { noun: true });
}
export function joinBusinessMeta(items: Array<string | null | undefined>) {
return items.map((item) => item?.trim()).filter((item): item is string => Boolean(item)).join(" · ");
}
+34
查看文件
@@ -3,6 +3,10 @@ type NotificationTarget = {
targetId: string | null;
};
function encodeSegment(value: string) {
return encodeURIComponent(value);
}
export function resolveNotificationTargetLink(item: NotificationTarget) {
if (!item.targetType || !item.targetId) return null;
@@ -41,6 +45,36 @@ export function resolveNotificationTargetLink(item: NotificationTarget) {
return null;
}
export function resolveMobileNotificationTargetLink(item: NotificationTarget) {
if (!item.targetType || !item.targetId) return null;
if (item.targetType === "workflow_task") {
return `/mobile/approvals/${encodeSegment(item.targetId)}`;
}
if (item.targetType === "reimbursement_form") {
return `/mobile/reimbursements/${encodeSegment(item.targetId)}`;
}
if (item.targetType === "leave_request") {
return `/mobile/leave/${encodeSegment(item.targetId)}`;
}
if (item.targetType === "purchase_request") {
return `/mobile/purchase-requests/${encodeSegment(item.targetId)}`;
}
if (item.targetType === "asset" || item.targetType === "asset_card" || item.targetType === "inventory_check") {
return "/mobile/assets/scan";
}
if (item.targetType === "employee") {
return "/mobile/profile";
}
return null;
}
export function formatNotificationRelativeTime(value: string | null) {
if (!value) {
return "-";
+19 -2
查看文件
@@ -43,6 +43,7 @@ function encodeSegment(value: string) {
export function resolveMobileEquivalentPath(pathname: string, search = "") {
const normalizedPath = pathname.replace(/\/+$/, "") || "/";
const searchParams = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
if (normalizedPath === "/" || normalizedPath === "/portal" || normalizedPath === "/dashboard") {
return "/mobile";
@@ -71,6 +72,10 @@ export function resolveMobileEquivalentPath(pathname: string, search = "") {
}
if (normalizedPath === "/leave-requests") {
const leaveRequestId = searchParams.get("highlight") ?? searchParams.get("id") ?? searchParams.get("requestId");
if (leaveRequestId) {
return `/mobile/leave/${encodeSegment(leaveRequestId)}`;
}
return "/mobile/leave";
}
@@ -80,7 +85,7 @@ export function resolveMobileEquivalentPath(pathname: string, search = "") {
const workflowTaskMatch = normalizedPath.match(/^\/workflow\/tasks\/([^/]+)$/);
if (workflowTaskMatch?.[1]) {
return `/mobile/approvals?${new URLSearchParams({ taskId: workflowTaskMatch[1] }).toString()}`;
return `/mobile/approvals/${encodeSegment(workflowTaskMatch[1])}`;
}
if (normalizedPath === "/workflow/tasks") {
@@ -104,7 +109,6 @@ export function resolveMobileEquivalentPath(pathname: string, search = "") {
if (
normalizedPath.startsWith("/projects") ||
normalizedPath.startsWith("/contracts-registry") ||
normalizedPath.startsWith("/purchase-requests") ||
normalizedPath.startsWith("/travel-requests") ||
normalizedPath.startsWith("/payment-batches") ||
normalizedPath.startsWith("/ap-operations") ||
@@ -119,5 +123,18 @@ export function resolveMobileEquivalentPath(pathname: string, search = "") {
return "/mobile";
}
if (normalizedPath === "/purchase-requests") {
const purchaseRequestId = searchParams.get("highlight") ?? searchParams.get("id") ?? searchParams.get("requestId");
if (purchaseRequestId) {
return `/mobile/purchase-requests/${encodeSegment(purchaseRequestId)}`;
}
return "/mobile";
}
const purchaseRequestDetailMatch = normalizedPath.match(/^\/purchase-requests\/([^/]+)$/);
if (purchaseRequestDetailMatch?.[1]) {
return `/mobile/purchase-requests/${encodeSegment(purchaseRequestDetailMatch[1])}`;
}
return search ? `${pathname}${search}` : pathname;
}