From 5519c3d2409d446ddc63f97705ea69cdb5e73234 Mon Sep 17 00:00:00 2001 From: shunli Date: Mon, 25 May 2026 08:00:12 +0800 Subject: [PATCH] =?UTF-8?q?OAFRONT4=20=E7=BB=9F=E4=B8=80PC=E6=97=A5?= =?UTF-8?q?=E6=9C=9F=E5=92=8C=E7=8A=B6=E6=80=81=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/DateTextInput.tsx | 17 ++++ client/src/pages/AssetRegistryPage.tsx | 12 ++- client/src/pages/ContractsRegistryPage.tsx | 16 ++-- client/src/pages/PaymentBatchesPage.tsx | 7 +- client/src/pages/ProjectDetailPage.tsx | 95 +++++++++++++++---- client/src/pages/ProjectsPage.tsx | 8 +- client/src/pages/PurchaseRequestsPage.tsx | 12 ++- ...25_00-55-14-oa-pc-launch-ui-cleanup-v1.csv | 8 +- 8 files changed, 123 insertions(+), 52 deletions(-) create mode 100644 client/src/components/common/DateTextInput.tsx diff --git a/client/src/components/common/DateTextInput.tsx b/client/src/components/common/DateTextInput.tsx new file mode 100644 index 00000000..4d2caab9 --- /dev/null +++ b/client/src/components/common/DateTextInput.tsx @@ -0,0 +1,17 @@ +import type { ComponentPropsWithoutRef } from "react"; +import { Input } from "../ui/input"; + +export function DateTextInput({ + placeholder = "例如 2026-05-25", + ...props +}: Omit, "type">) { + return ( + + ); +} diff --git a/client/src/pages/AssetRegistryPage.tsx b/client/src/pages/AssetRegistryPage.tsx index 2440e77b..4a842833 100644 --- a/client/src/pages/AssetRegistryPage.tsx +++ b/client/src/pages/AssetRegistryPage.tsx @@ -30,6 +30,7 @@ import { isApiError } from "../api/types"; import { useAuth } from "../auth/AuthContext"; import { hasAnyPermission, hasPermission } from "../auth/permissions"; import { AiSuggestionCard } from "../components/ai/AiSuggestionCard"; +import { DateTextInput } from "../components/common/DateTextInput"; import { StatusBadge } from "../components/common/StatusBadge"; import { TableSkeleton } from "../components/common/TableSkeleton"; import { FilterBar, ListWorkspace, PrimaryTable, WorkspaceHeader } from "../components/patterns/ListWorkspace"; @@ -37,6 +38,7 @@ import { SectionPanel } from "../components/patterns/SectionPanel"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table"; +import { formatDisplayDateTime } from "../lib/displayFormat"; function resolveError(error: unknown) { if (isApiError(error)) { @@ -1200,19 +1202,19 @@ export function AssetRegistryPage() {
- setAssetForm((current) => ({ ...current, lastVerifiedAt: event.target.value }))} /> + setAssetForm((current) => ({ ...current, lastVerifiedAt: event.target.value }))} />
- setAssetForm((current) => ({ ...current, expiresAt: event.target.value }))} /> + setAssetForm((current) => ({ ...current, expiresAt: event.target.value }))} />
- setAssetForm((current) => ({ ...current, activatedAt: event.target.value }))} /> + setAssetForm((current) => ({ ...current, activatedAt: event.target.value }))} />
- setAssetForm((current) => ({ ...current, retiredAt: event.target.value }))} /> + setAssetForm((current) => ({ ...current, retiredAt: event.target.value }))} />
@@ -1324,7 +1326,7 @@ export function AssetRegistryPage() {
- 最近更新时间:{resolveAssetErpTimestamp(assetErpStage) ? new Date(resolveAssetErpTimestamp(assetErpStage) as string).toLocaleString() : "尚无同步记录"} + 最近更新时间:{resolveAssetErpTimestamp(assetErpStage) ? formatDisplayDateTime(resolveAssetErpTimestamp(assetErpStage)) : "尚无同步记录"}
变更来源:{assetErpStage.preview.sourceEventName ?? "-"}
diff --git a/client/src/pages/ContractsRegistryPage.tsx b/client/src/pages/ContractsRegistryPage.tsx index 9dd4b9b0..2e6ee106 100644 --- a/client/src/pages/ContractsRegistryPage.tsx +++ b/client/src/pages/ContractsRegistryPage.tsx @@ -23,6 +23,7 @@ import { useAuth } from "../auth/AuthContext"; import { hasAnyPermission } from "../auth/permissions"; import { PageHeader } from "../components/common/PageHeader"; import { StatusBadge } from "../components/common/StatusBadge"; +import { DateTextInput } from "../components/common/DateTextInput"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; @@ -499,24 +500,21 @@ export function ContractsRegistryPage() {
- setEditor((current) => ({ ...current, signedDate: event.target.value }))} />
- setEditor((current) => ({ ...current, effectiveDate: event.target.value }))} />
- setEditor((current) => ({ ...current, expiryDate: event.target.value }))} /> @@ -685,8 +683,7 @@ export function ContractsRegistryPage() {
- setFilters((current) => ({ ...current, expiryDateFrom: event.target.value }))} /> @@ -694,8 +691,7 @@ export function ContractsRegistryPage() {
- setFilters((current) => ({ ...current, expiryDateTo: event.target.value }))} /> diff --git a/client/src/pages/PaymentBatchesPage.tsx b/client/src/pages/PaymentBatchesPage.tsx index fe2e9541..6c4135b4 100644 --- a/client/src/pages/PaymentBatchesPage.tsx +++ b/client/src/pages/PaymentBatchesPage.tsx @@ -28,6 +28,7 @@ import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; import { RiskFlagsCard } from "../components/common/RiskFlagsCard"; import { StatusBadge } from "../components/common/StatusBadge"; +import { DateTextInput } from "../components/common/DateTextInput"; import { downloadBlob } from "../lib/download"; import { formatDisplayDate, formatDisplayDateTime } from "../lib/displayFormat"; import { toast } from "sonner"; @@ -371,8 +372,7 @@ export function PaymentBatchesPage() {
- setDefaultPaymentDate(event.target.value)} @@ -615,8 +615,7 @@ export function PaymentBatchesPage() { disabled={!canEditSelectedBatch || !["pending", "failed"].includes(item.status)} onBlur={(event) => updateItemMutation.mutate({ batchId: selectedBatch.id, itemId: item.id, paymentVoucherNoOverride: event.target.value || null })} /> - updateItemMutation.mutate({ batchId: selectedBatch.id, itemId: item.id, paymentDateOverride: event.target.value || null })} diff --git a/client/src/pages/ProjectDetailPage.tsx b/client/src/pages/ProjectDetailPage.tsx index 44b17b5b..da584ed4 100644 --- a/client/src/pages/ProjectDetailPage.tsx +++ b/client/src/pages/ProjectDetailPage.tsx @@ -11,6 +11,7 @@ import { MetricCard } from "../components/patterns/MetricCard"; import { PageScaffold } from "../components/patterns/PageScaffold"; import { SectionPanel } from "../components/patterns/SectionPanel"; import { isApiError } from "../api/types"; +import { DateTextInput } from "../components/common/DateTextInput"; import { StatusBadge } from "../components/common/StatusBadge"; import { Button } from "../components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../components/ui/dialog"; @@ -18,6 +19,7 @@ import { Input } from "../components/ui/input"; import { Label } from "../components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table"; import { Textarea } from "../components/ui/textarea"; +import { formatDisplayDateTime } from "../lib/displayFormat"; import { toast } from "sonner"; const PROJECT_STATUS_OPTIONS: ProjectStatus[] = ["draft", "active", "paused", "completed", "archived"]; @@ -33,6 +35,58 @@ const CLOSURE_CHECKLIST_STATUS_OPTIONS: ProjectClosureChecklistStatus[] = ["pend const CONFIDENTIALITY_OPTIONS: ConfidentialityLevel[] = ["public", "internal", "confidential", "restricted"]; const STORAGE_TYPE_OPTIONS: DocumentStorageType[] = ["oa_link", "nas", "internal_share", "offline_archive"]; +function projectStatusLabel(value: ProjectStatus) { + if (value === "draft") return "草稿"; + if (value === "active") return "进行中"; + if (value === "paused") return "已暂停"; + if (value === "completed") return "已完成"; + return "已归档"; +} + +function memberRoleLabel(value: (typeof MEMBER_ROLE_OPTIONS)[number]) { + if (value === "owner") return "负责人"; + if (value === "manager") return "项目经理"; + if (value === "observer") return "观察人"; + return "成员"; +} + +function milestoneStatusLabel(value: ProjectMilestoneStatus) { + if (value === "pending") return "待处理"; + if (value === "in_progress") return "进行中"; + if (value === "done") return "已完成"; + if (value === "delayed") return "已延期"; + return "已取消"; +} + +function taskStatusLabel(value: ProjectTaskStatus) { + if (value === "todo") return "待处理"; + if (value === "doing") return "进行中"; + if (value === "blocked") return "受阻"; + if (value === "done") return "已完成"; + return "已取消"; +} + +function taskPriorityLabel(value: ProjectTaskPriority) { + if (value === "low") return "低"; + if (value === "high") return "高"; + if (value === "urgent") return "紧急"; + return "普通"; +} + +function riskLevelLabel(value: ProjectRiskLevel) { + if (value === "low") return "低"; + if (value === "high") return "高"; + return "中"; +} + +function riskStatusLabel(value: string) { + if (value === "open") return "待处理"; + if (value === "monitoring") return "观察中"; + if (value === "mitigating") return "处理中"; + if (value === "closed") return "已关闭"; + return value || "-"; +} + function resolveError(error: unknown, fallback = "操作失败") { if (isApiError(error)) { return `${error.code}: ${mapApiErrorMessage(error.code, error.details, error.message)}`; @@ -79,8 +133,7 @@ function closureChecklistStatusLabel(value: ProjectClosureChecklistStatus) { } function formatDateTime(value: string | null) { - if (!value) return "-"; - return value.slice(0, 16).replace("T", " "); + return formatDisplayDateTime(value); } export function ProjectDetailPage() { @@ -569,7 +622,7 @@ export function ProjectDetailPage() { ) : null}
- setCommunicationForm((prev) => ({ ...prev, happenedAt: event.target.value }))} /> + setCommunicationForm((prev) => ({ ...prev, happenedAt: event.target.value }))} /> @@ -577,7 +630,7 @@ export function ProjectDetailPage() {
setCommunicationForm((prev) => ({ ...prev, externalOrgName: event.target.value }))} placeholder="外部单位(可选)" /> - setCommunicationForm((prev) => ({ ...prev, nextFollowUpDate: event.target.value }))} /> + setCommunicationForm((prev) => ({ ...prev, nextFollowUpDate: event.target.value }))} />