feat(08-05-contract-detail-workspace): 完成合同详情与终止状态治理

这个提交包含在:
2026-08-08 02:02:52 +08:00
父节点 7546d372d6
当前提交 21851774dd
共修改 25 个文件,包含 2516 行新增和 1021 行删除
+56 -3
查看文件
@@ -2,7 +2,8 @@ import { apiRequestBlob, apiRequestData } from "./http";
import type { ConfidentialityLevel, EmployeeBasic } from "./projects";
export type ContractRegistryType = "purchase" | "sales" | "service" | "framework" | "other";
export type ContractRegistryPersistedStatus = "draft" | "active" | "archived";
export type ContractRegistryPersistedStatus = "draft" | "active" | "terminated" | "archived";
export type ContractRegistryWritableStatus = Exclude<ContractRegistryPersistedStatus, "terminated">;
export type ContractRegistryStatus = ContractRegistryPersistedStatus | "expired";
export type ContractRegistryExportFormat = "csv" | "xlsx";
@@ -33,12 +34,46 @@ export type ContractRegistryItem = {
ownerEmployeeId: string;
status: ContractRegistryStatus;
persistedStatus: ContractRegistryPersistedStatus;
/** Detail-only terminal evidence is omitted from list responses. */
terminatedAt?: string | null;
terminatedReason?: string | null;
terminatedBy?: string | null;
createdAt: string | null;
updatedAt: string | null;
owner: EmployeeBasic | null;
terminator?: EmployeeBasic | null;
project: { id: string; projectNo: string; name: string } | null;
};
export type ContractDocumentRefSummary = {
id: string;
title: string;
documentNo: string | null;
confidentialityLevel: ConfidentialityLevel;
storageType: string;
offlineArchiveNo: string | null;
createdAt: string | null;
updatedAt: string | null;
owner: EmployeeBasic | null;
};
export type ContractHistoryItem = {
id: string;
action: string;
summary: string;
createdAt: string | null;
actor: EmployeeBasic | null;
};
export type ContractRegistryDetail = Omit<ContractRegistryItem, "terminatedAt" | "terminatedReason" | "terminatedBy" | "terminator"> & {
terminatedAt: string | null;
terminatedReason: string | null;
terminatedBy: string | null;
terminator: EmployeeBasic | null;
documentRefs: ContractDocumentRefSummary[];
history: ContractHistoryItem[];
};
function buildFiltersSearch(filters?: ContractRegistryFilters | string) {
const normalized =
typeof filters === "string"
@@ -67,6 +102,10 @@ export async function fetchExpiringContracts(days = 30) {
);
}
export async function fetchContractRegistryDetail(id: string) {
return apiRequestData<{ item: ContractRegistryDetail }>(`/api/contracts-registry/${id}`);
}
export async function createContractRegistry(payload: {
contractNo: string;
projectId?: string | null;
@@ -82,7 +121,7 @@ export async function createContractRegistry(payload: {
archiveLocation: string;
offlineArchiveNo?: string | null;
ownerEmployeeId?: string;
status?: ContractRegistryPersistedStatus;
status?: ContractRegistryWritableStatus;
}) {
return apiRequestData<{ item: ContractRegistryItem }>("/api/contracts-registry", {
method: "POST",
@@ -107,7 +146,7 @@ export async function patchContractRegistry(
archiveLocation: string;
offlineArchiveNo: string | null;
ownerEmployeeId: string;
status: ContractRegistryPersistedStatus;
status: ContractRegistryWritableStatus;
}>,
) {
return apiRequestData<{ item: ContractRegistryItem }>(`/api/contracts-registry/${id}`, {
@@ -125,6 +164,20 @@ export async function renewContractRegistry(id: string) {
);
}
export async function terminateContractRegistry(id: string, reason: string) {
return apiRequestData<{ item: ContractRegistryItem }>(`/api/contracts-registry/${id}/terminate`, {
method: "POST",
body: JSON.stringify({ reason }),
});
}
export async function appendContractRegistryCorrection(id: string, summary: string) {
return apiRequestData<{ accepted: true }>(`/api/contracts-registry/${id}/corrections`, {
method: "POST",
body: JSON.stringify({ summary }),
});
}
export async function exportContractsRegistry(params: ContractRegistryFilters & { format: ContractRegistryExportFormat }) {
return apiRequestBlob("/api/exports/contracts-registry", {
method: "POST",