R103/A1-A6 收口跨页面设计一致性

这个提交包含在:
2026-05-06 23:19:08 +08:00
父节点 9a9def6286
当前提交 6a90fa9bd7
共修改 5 个文件,包含 441 行新增和 16 行删除
+12 -2
查看文件
@@ -3,12 +3,19 @@ export const ADMIN_NAV_ORDER = ["/system/workspace", "/people", "/organization",
export const BACKUP_NAV_ORDER = ["/attendance", "/workflow/tasks", "/contracts-registry", "/purchase-requests", "/travel-requests", "/budgets", "/payment-batches", "/ap-operations", "/invoice-pool", "/inventory-checks", "/system/cutover", "/system/integrations/erp", "/dashboard", "/settings/profile"];
const PAGE_TITLES: Array<{ path: string; title: string; group: string }> = [
{ path: "/portal", title: "Portal", group: "工作台" },
{ path: "/portal", title: "工作台", group: "工作台" },
{ path: "/announcements", title: "公告", group: "主导航" },
{ path: "/team", title: "团队", group: "主导航" },
{ path: "/attendance", title: "考勤", group: "备用模块" },
{ path: "/projects", title: "项目", group: "主导航" },
{ path: "/contracts-registry", title: "合同台账", group: "备用模块" },
{ path: "/purchase-requests", title: "采购申请", group: "备用模块" },
{ path: "/travel-requests", title: "差旅申请", group: "备用模块" },
{ path: "/budgets", title: "预算管理", group: "备用模块" },
{ path: "/payment-batches", title: "付款批次", group: "备用模块" },
{ path: "/ap-operations", title: "应付作业", group: "备用模块" },
{ path: "/invoice-pool", title: "票据中心", group: "备用模块" },
{ path: "/inventory-checks", title: "盘点任务", group: "备用模块" },
{ path: "/weekly-reports", title: "周报", group: "主导航" },
{ path: "/todos", title: "待办", group: "主导航" },
{ path: "/reimbursements/", title: "报销详情", group: "主导航" },
@@ -29,6 +36,9 @@ const PAGE_TITLES: Array<{ path: string; title: string; group: string }> = [
{ path: "/system/workflow-definitions", title: "流程定义", group: "管理入口" },
{ path: "/system/settings", title: "系统设置", group: "管理入口" },
{ path: "/system/feature-flags", title: "功能开关", group: "管理入口" },
{ path: "/system/cutover", title: "切换控制台", group: "管理入口" },
{ path: "/system/integrations/erp", title: "ERP 集成", group: "管理入口" },
{ path: "/dashboard", title: "数据看板", group: "备用模块" },
{ path: "/audit-logs", title: "审计日志", group: "管理入口" },
];
@@ -41,7 +51,7 @@ export function resolvePageMeta(pathname: string) {
}
if (pathname === "/") {
return { title: "Portal", group: "工作台" };
return { title: "工作台", group: "工作台" };
}
return { title: pathname, group: "工作台" };
+39 -13
查看文件
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useSearchParams } from "react-router-dom";
import { toast } from "sonner";
@@ -232,6 +232,8 @@ export function PurchaseRequestsPage() {
const [items, setItems] = useState<DraftItem[]>([]);
const [archivePoNo, setArchivePoNo] = useState("");
const [archiveContractNo, setArchiveContractNo] = useState("");
const [selectedAttachmentName, setSelectedAttachmentName] = useState("");
const attachmentInputRef = useRef<HTMLInputElement | null>(null);
const suppliersQuery = useQuery({
queryKey: ["purchase-requests", "suppliers"],
@@ -701,7 +703,7 @@ export function PurchaseRequestsPage() {
<div className="space-y-6">
<PageHeader
title="采购申请"
subtitle="支持草稿、附件、提交、撤回与经理单步审批的轻采购流程。"
subtitle="草稿、附件、审批。"
actions={
canCreate ? (
<Button
@@ -1290,17 +1292,41 @@ export function PurchaseRequestsPage() {
<div className="space-y-3">
<div className="flex items-center justify-between">
<h4 className="text-base font-semibold text-zinc-900">附件</h4>
<input
type="file"
disabled={!canEditDraft || uploadMutation.isPending}
onChange={(event) => {
const file = event.target.files?.[0];
if (file) {
uploadMutation.mutate(file);
}
event.currentTarget.value = "";
}}
/>
<div className="flex min-w-0 items-center gap-3">
<input
data-testid="purchase-attachment-file-input"
hidden
ref={attachmentInputRef}
type="file"
disabled={!canEditDraft || uploadMutation.isPending}
onChange={(event) => {
const file = event.target.files?.[0];
if (file) {
setSelectedAttachmentName(file.name);
uploadMutation.mutate(file);
}
event.currentTarget.value = "";
}}
/>
<span
className="hidden max-w-[220px] truncate text-xs text-zinc-500 sm:inline"
data-testid="purchase-attachment-selected-name"
>
{uploadMutation.isPending
? `正在上传:${uploadMutation.variables?.name ?? selectedAttachmentName}`
: selectedAttachmentName || "未选择文件"}
</span>
<Button
data-testid="purchase-attachment-upload"
type="button"
size="sm"
variant="secondary"
disabled={!canEditDraft || uploadMutation.isPending}
onClick={() => attachmentInputRef.current?.click()}
>
{uploadMutation.isPending ? "上传中" : "上传附件"}
</Button>
</div>
</div>
<div className="space-y-3 md:hidden">
{detail.attachments.length ? (
@@ -3,6 +3,7 @@ import type { InvoicePoolListResponse } from "../../api/invoicePool";
import type { ReimbursementListResponse } from "../../api/reimbursements";
import type { TravelRequestDetail, TravelRequestListResponse } from "../../api/travelRequests";
import { Button } from "../../components/ui/button";
import { resolveReimbursementDisplayCode } from "../../lib/reimbursementDisplay";
export type InvoiceBindMode = "reimbursement" | "travelOrder";
@@ -87,7 +88,7 @@ export function InvoiceBindCard({
<option value="">选择报销单</option>
{(reimbursementQuery.data?.items ?? []).map((item) => (
<option key={item.id} value={item.id}>
{item.formNumber ?? item.id} ({item.status})
{resolveReimbursementDisplayCode(item)} ({item.status})
</option>
))}
</select>