实现业务附件受控入库 V1(05-28-business-attachment-governance-v1)
这个提交包含在:
@@ -39,6 +39,8 @@ import { hasPermission } from "../auth/permissions";
|
||||
import { RiskFlagsCard } from "../components/common/RiskFlagsCard";
|
||||
import { AiSuggestionCard } from "../components/ai/AiSuggestionCard";
|
||||
import { ActionConfirmDialog } from "../components/common/ActionConfirmDialog";
|
||||
import { AttachmentGovernanceBadges } from "../components/common/AttachmentGovernanceBadges";
|
||||
import { useAttachmentDownloadReasonDialog } from "../components/common/useAttachmentDownloadReasonDialog";
|
||||
import { DateTextInput } from "../components/common/DateTextInput";
|
||||
import { Pagination } from "../components/common/Pagination";
|
||||
import { StatusBadge } from "../components/common/StatusBadge";
|
||||
@@ -49,6 +51,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from ".
|
||||
import { Textarea } from "../components/ui/textarea";
|
||||
import { FilterBar, ListWorkspace, PrimaryTable, WorkspaceHeader } from "../components/patterns/ListWorkspace";
|
||||
import { SectionPanel } from "../components/patterns/SectionPanel";
|
||||
import type { AttachmentGovernanceFields } from "../lib/attachmentGovernance";
|
||||
import { formatCompactIdentifier, formatDisplayDateTime, formatTextWithCompactLongNumbers, isCompactedIdentifier } from "../lib/displayFormat";
|
||||
import { buildPrecheckDialogMessage } from "../lib/precheck";
|
||||
import {
|
||||
@@ -744,12 +747,18 @@ export function PurchaseRequestsPage() {
|
||||
},
|
||||
});
|
||||
|
||||
async function handleDownloadAttachment(attachmentId: string) {
|
||||
const { requestDownloadReason, downloadReasonDialog } = useAttachmentDownloadReasonDialog();
|
||||
|
||||
async function handleDownloadAttachment(attachment: { id: string; originalName?: string } & AttachmentGovernanceFields) {
|
||||
if (!selectedId) {
|
||||
return;
|
||||
}
|
||||
const reason = await requestDownloadReason(attachment);
|
||||
if (reason === null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await issuePurchaseAttachmentSignedUrl(selectedId, attachmentId);
|
||||
const response = await issuePurchaseAttachmentSignedUrl(selectedId, attachment.id, reason);
|
||||
window.open(response.signedUrl, "_blank", "noopener,noreferrer");
|
||||
} catch (error) {
|
||||
const message = resolveError(error);
|
||||
@@ -814,6 +823,7 @@ export function PurchaseRequestsPage() {
|
||||
|
||||
return (
|
||||
<ListWorkspace>
|
||||
{downloadReasonDialog}
|
||||
<WorkspaceHeader
|
||||
title="采购申请"
|
||||
subtitle="草稿、附件、审批。"
|
||||
@@ -1576,6 +1586,7 @@ export function PurchaseRequestsPage() {
|
||||
<div key={attachment.id} className="rounded-lg border border-zinc-200 bg-white p-4">
|
||||
<div className="font-medium text-zinc-900">{attachment.originalName}</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">{attachment.mimeType} / {attachment.fileSize} bytes</div>
|
||||
<AttachmentGovernanceBadges attachment={attachment} className="mt-2" />
|
||||
<div className="mt-3 grid grid-cols-2 gap-2 text-xs text-zinc-600">
|
||||
<div>
|
||||
<div className="text-zinc-400">上传人</div>
|
||||
@@ -1589,7 +1600,7 @@ export function PurchaseRequestsPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<Button type="button" size="sm" variant="secondary" onClick={() => void handleDownloadAttachment(attachment.id)}>
|
||||
<Button type="button" size="sm" variant="secondary" onClick={() => void handleDownloadAttachment(attachment)}>
|
||||
下载
|
||||
</Button>
|
||||
<Button
|
||||
@@ -1625,12 +1636,13 @@ export function PurchaseRequestsPage() {
|
||||
<TableCell>
|
||||
<div className="font-medium text-zinc-900">{attachment.originalName}</div>
|
||||
<div className="text-xs text-zinc-500">{attachment.mimeType} / {attachment.fileSize} bytes</div>
|
||||
<AttachmentGovernanceBadges attachment={attachment} className="mt-2" />
|
||||
</TableCell>
|
||||
<TableCell>{attachment.uploader?.name ?? "-"}</TableCell>
|
||||
<TableCell>{formatDisplayDateTime(attachment.createdAt)}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex gap-2">
|
||||
<Button type="button" size="sm" variant="secondary" onClick={() => void handleDownloadAttachment(attachment.id)}>
|
||||
<Button type="button" size="sm" variant="secondary" onClick={() => void handleDownloadAttachment(attachment)}>
|
||||
下载
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
在新工单中引用
屏蔽一个用户