R23-3:完成项目页与Portal收口面板

这个提交包含在:
2026-03-22 11:31:22 +08:00
父节点 9939657790
当前提交 a136000797
共修改 6 个文件,包含 407 行新增和 7 行删除
+1 -1
查看文件
@@ -1,6 +1,6 @@
import { apiRequestData } from "./http";
export type FeatureFlagKey = "erp_sync" | "invoice_verification" | "leave_holiday_calendar" | "budget_precheck_v1" | "payment_batch_v1" | "portal_home_v1" | "global_search_v1" | "announcements_v1" | "workflow_definition_admin_v1" | "cutover_console_v1" | "presence_board_v1" | "weekly_report_v1" | "employee_lifecycle_v1" | "todo_board_v1";
export type FeatureFlagKey = "erp_sync" | "invoice_verification" | "leave_holiday_calendar" | "budget_precheck_v1" | "payment_batch_v1" | "portal_home_v1" | "global_search_v1" | "announcements_v1" | "workflow_definition_admin_v1" | "cutover_console_v1" | "presence_board_v1" | "weekly_report_v1" | "employee_lifecycle_v1" | "todo_board_v1" | "project_domain_v1" | "project_linking_v1" | "project_communication_log_v1" | "project_closure_checklist_v1" | "confidential_doc_refs_v1" | "public_security_hardening_v1";
export type FeatureFlagOverrideMode = "inherit" | "force_on" | "force_off";
export type FeatureFlagRolloutType = "global" | "department_allowlist";
+2
查看文件
@@ -34,6 +34,8 @@ export type PortalHomePayload = {
overdueMilestonesCount: number;
highPriorityTaskCount: number;
openRiskCount: number;
followUpDueTodayCount: number;
closurePendingProjectCount: number;
href: string;
}
| null;
+120 -2
查看文件
@@ -7,6 +7,8 @@ export type ProjectTaskStatus = "todo" | "doing" | "blocked" | "done" | "cancell
export type ProjectTaskPriority = "low" | "normal" | "high" | "urgent";
export type ProjectRiskLevel = "low" | "medium" | "high";
export type ProjectDeliveryRecordType = "kickoff" | "onsite" | "stage_acceptance" | "final_acceptance" | "handover";
export type ProjectCommunicationChannel = "wechat" | "phone" | "meeting" | "wecom" | "email" | "onsite" | "other";
export type ProjectClosureChecklistStatus = "pending" | "done" | "waived";
export type ConfidentialityLevel = "public" | "internal" | "confidential" | "restricted";
export type DocumentStorageType = "oa_link" | "nas" | "internal_share" | "offline_archive";
@@ -90,6 +92,35 @@ export type ProjectRisk = {
updatedAt: string | null;
};
export type ProjectCommunicationLog = {
id: string;
happenedAt: string | null;
channel: ProjectCommunicationChannel;
externalContactName: string | null;
externalOrgName: string | null;
summary: string;
nextAction: string | null;
nextFollowUpDate: string | null;
createdBy: string;
createdAt: string | null;
updatedAt: string | null;
canEdit: boolean;
creator: EmployeeBasic | null;
};
export type ProjectClosureChecklistItem = {
id: string;
itemKey: string;
title: string;
status: ProjectClosureChecklistStatus;
note: string | null;
sortOrder: number;
completedAt: string | null;
completedBy: string | null;
canEdit: boolean;
completer: EmployeeBasic | null;
};
export type ProjectDetail = {
id: string;
projectNo: string;
@@ -117,6 +148,8 @@ export type ProjectDetail = {
travelRequests: number;
todoItems: number;
weeklyReports: number;
communicationLogs: number;
closureChecklistItems: number;
contracts: number;
deliveries: number;
documentRefs: number;
@@ -125,6 +158,30 @@ export type ProjectDetail = {
milestones: ProjectMilestone[];
tasks: ProjectTask[];
risks: ProjectRisk[];
communicationSummary: {
totalCount: number;
latestHappenedAt: string | null;
followUpDueTodayCount: number;
overdueFollowUpCount: number;
};
closureSummary: {
totalCount: number;
pendingCount: number;
waivedCount: number;
warned: boolean;
facts: {
openRiskCount: number;
openReimbursementCount: number;
openTravelCount: number;
deliveryRecordCount: number;
contractCount: number;
confidentialDocRefCount: number;
};
};
capabilities: {
communicationLogEnabled: boolean;
closureChecklistEnabled: boolean;
};
};
export type ProjectExpenseSummary = {
@@ -229,6 +286,11 @@ export async function fetchProject(projectId: string) {
return apiRequestData<{ item: ProjectDetail }>(`/api/projects/${projectId}`);
}
export type ProjectClosureWarning = {
pendingCount: number;
message: string;
} | null;
export async function patchProject(
projectId: string,
payload: Partial<{
@@ -246,14 +308,14 @@ export async function patchProject(
isConfidential: boolean;
}>,
) {
return apiRequestData<{ item: ProjectDetail }>(`/api/projects/${projectId}`, {
return apiRequestData<{ item: ProjectDetail; closureWarning: ProjectClosureWarning }>(`/api/projects/${projectId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function archiveProject(projectId: string) {
return apiRequestData<{ item: { id: string; status: ProjectStatus } }>(`/api/projects/${projectId}/archive`, {
return apiRequestData<{ item: ProjectDetail; closureWarning: ProjectClosureWarning }>(`/api/projects/${projectId}/archive`, {
method: "POST",
body: JSON.stringify({}),
});
@@ -400,6 +462,62 @@ export async function fetchProjectWeeklyReports(projectId: string) {
return apiRequestData<{ items: ProjectWeeklyReportLink[] }>(`/api/projects/${projectId}/weekly-reports`);
}
export async function fetchProjectCommunicationLogs(projectId: string) {
return apiRequestData<{ items: ProjectCommunicationLog[] }>(`/api/projects/${projectId}/communication-logs`);
}
export async function createProjectCommunicationLog(
projectId: string,
payload: {
happenedAt: string;
channel: ProjectCommunicationChannel;
externalContactName?: string | null;
externalOrgName?: string | null;
summary: string;
nextAction?: string | null;
nextFollowUpDate?: string | null;
},
) {
return apiRequestData<{ item: ProjectCommunicationLog }>(`/api/projects/${projectId}/communication-logs`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function patchProjectCommunicationLog(
projectId: string,
logId: string,
payload: Partial<{
happenedAt: string;
channel: ProjectCommunicationChannel;
externalContactName: string | null;
externalOrgName: string | null;
summary: string;
nextAction: string | null;
nextFollowUpDate: string | null;
}>,
) {
return apiRequestData<{ item: ProjectCommunicationLog }>(`/api/projects/${projectId}/communication-logs/${logId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function fetchProjectClosureChecklist(projectId: string) {
return apiRequestData<{ items: ProjectClosureChecklistItem[] }>(`/api/projects/${projectId}/closure-checklist`);
}
export async function patchProjectClosureChecklistItem(
projectId: string,
itemId: string,
payload: Partial<{ status: ProjectClosureChecklistStatus; note: string | null }>,
) {
return apiRequestData<{ item: ProjectClosureChecklistItem }>(`/api/projects/${projectId}/closure-checklist/${itemId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function fetchProjectDeliveries(projectId: string) {
return apiRequestData<{ items: ProjectDeliveryRecord[] }>(`/api/projects/${projectId}/deliveries`);
}