R23-3:完成项目页与Portal收口面板
这个提交包含在:
+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`);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户