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

这个提交包含在:
2026-05-06 23:19:08 +08:00
父节点 9a9def6286
当前提交 6a90fa9bd7
共修改 5 个文件,包含 441 行新增和 16 行删除
+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>