R19/R20: 项目主域进 OA 并打通报销差旅周报待办

这个提交包含在:
2026-03-22 08:34:18 +08:00
父节点 0235ccb968
当前提交 3f9738ac9c
共修改 28 个文件,包含 3236 行新增和 21 行删除
+443
查看文件
@@ -0,0 +1,443 @@
import { apiRequestData } from "./http";
export type ProjectStatus = "draft" | "active" | "paused" | "completed" | "archived";
export type ProjectRole = "owner" | "manager" | "member" | "observer";
export type ProjectMilestoneStatus = "pending" | "in_progress" | "done" | "delayed" | "cancelled";
export type ProjectTaskStatus = "todo" | "doing" | "blocked" | "done" | "cancelled";
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 ConfidentialityLevel = "public" | "internal" | "confidential" | "restricted";
export type DocumentStorageType = "oa_link" | "nas" | "internal_share" | "offline_archive";
export type EmployeeBasic = {
id: string;
employeeNo: string;
name: string;
};
export type ProjectListItem = {
id: string;
projectNo: string;
name: string;
clientName: string;
clientAlias: string | null;
status: ProjectStatus;
startsAt: string | null;
targetEndAt: string | null;
actualEndAt: string | null;
summary: string | null;
isConfidential: boolean;
owner: EmployeeBasic | null;
manager: EmployeeBasic | null;
counts: {
members: number;
milestones: number;
tasks: number;
risks: number;
overdueMilestones: number;
highPriorityTasks: number;
openRisks: number;
};
updatedAt: string | null;
};
export type ProjectMember = {
id: string;
employeeId: string;
role: ProjectRole;
joinedAt: string | null;
leftAt: string | null;
employee: EmployeeBasic | null;
};
export type ProjectMilestone = {
id: string;
title: string;
description: string | null;
status: ProjectMilestoneStatus;
dueDate: string | null;
completedAt: string | null;
ownerEmployeeId: string | null;
sortOrder: number;
};
export type ProjectTask = {
id: string;
title: string;
description: string | null;
status: ProjectTaskStatus;
priority: ProjectTaskPriority;
assigneeEmployeeId: string | null;
dueDate: string | null;
blockedReason: string | null;
createdBy: string;
assignee: EmployeeBasic | null;
creator: EmployeeBasic | null;
createdAt: string | null;
updatedAt: string | null;
};
export type ProjectRisk = {
id: string;
title: string;
detail: string;
level: ProjectRiskLevel;
status: string;
ownerEmployeeId: string | null;
owner: EmployeeBasic | null;
createdAt: string | null;
updatedAt: string | null;
};
export type ProjectDetail = {
id: string;
projectNo: string;
name: string;
clientName: string;
clientAlias: string | null;
ownerEmployeeId: string;
managerEmployeeId: string | null;
status: ProjectStatus;
startsAt: string | null;
targetEndAt: string | null;
actualEndAt: string | null;
summary: string | null;
isConfidential: boolean;
createdAt: string | null;
updatedAt: string | null;
owner: EmployeeBasic | null;
manager: EmployeeBasic | null;
counts: {
members: number;
milestones: number;
tasks: number;
risks: number;
reimbursements: number;
travelRequests: number;
todoItems: number;
weeklyReports: number;
contracts: number;
deliveries: number;
documentRefs: number;
};
members: ProjectMember[];
milestones: ProjectMilestone[];
tasks: ProjectTask[];
risks: ProjectRisk[];
};
export type ProjectExpenseSummary = {
totalAmount: string;
items: Array<{
id: string;
formNumber: string;
status: string;
totalAmount: string;
reimbursementDate: string | null;
updatedAt: string | null;
}>;
};
export type ProjectTravelSummary = {
totalAmount: string;
items: Array<{
id: string;
requestNo: string;
status: string;
estimatedAmount: string;
startDate: string | null;
endDate: string | null;
updatedAt: string | null;
}>;
};
export type ProjectWeeklyReportLink = {
id: string;
weeklyReportId: string;
weekStart: string | null;
status: "draft" | "submitted";
employee: EmployeeBasic | null;
progressText: string | null;
riskText: string | null;
nextStepText: string | null;
updatedAt: string | null;
};
export type ProjectDeliveryRecord = {
id: string;
title: string;
recordType: ProjectDeliveryRecordType;
summary: string;
occurredAt: string | null;
owner: EmployeeBasic | null;
createdAt: string | null;
};
export type ConfidentialDocumentRef = {
id: string;
title: string;
documentNo: string | null;
confidentialityLevel: ConfidentialityLevel;
storageType: DocumentStorageType;
storagePath: string;
offlineArchiveNo: string | null;
updatedAt: string | null;
};
function toQueryString(query: Record<string, string | number | boolean | undefined>) {
const params = new URLSearchParams();
for (const [key, value] of Object.entries(query)) {
if (value == null || value === "") {
continue;
}
params.set(key, typeof value === "boolean" ? (value ? "1" : "0") : String(value));
}
const encoded = params.toString();
return encoded ? `?${encoded}` : "";
}
export async function fetchProjects(params: {
status?: ProjectStatus;
mine?: boolean;
q?: string;
} = {}) {
return apiRequestData<{ items: ProjectListItem[] }>(`/api/projects${toQueryString(params)}`);
}
export async function createProject(payload: {
projectNo?: string;
name: string;
clientName: string;
clientAlias?: string | null;
ownerEmployeeId?: string;
managerEmployeeId?: string | null;
status?: ProjectStatus;
startsAt?: string | null;
targetEndAt?: string | null;
actualEndAt?: string | null;
summary?: string | null;
isConfidential?: boolean;
}) {
return apiRequestData<{ item: ProjectDetail }>("/api/projects", {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function fetchProject(projectId: string) {
return apiRequestData<{ item: ProjectDetail }>(`/api/projects/${projectId}`);
}
export async function patchProject(
projectId: string,
payload: Partial<{
projectNo: string;
name: string;
clientName: string;
clientAlias: string | null;
ownerEmployeeId: string;
managerEmployeeId: string | null;
status: ProjectStatus;
startsAt: string | null;
targetEndAt: string | null;
actualEndAt: string | null;
summary: string | null;
isConfidential: boolean;
}>,
) {
return apiRequestData<{ item: ProjectDetail }>(`/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`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function fetchProjectMembers(projectId: string) {
return apiRequestData<{ items: ProjectMember[] }>(`/api/projects/${projectId}/members`);
}
export async function createProjectMember(projectId: string, payload: { employeeId: string; role?: ProjectRole }) {
return apiRequestData<{ item: ProjectMember }>(`/api/projects/${projectId}/members`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function patchProjectMember(
projectId: string,
memberId: string,
payload: Partial<{ role: ProjectRole; leftAt: string | null }>,
) {
return apiRequestData<{ item: ProjectMember }>(`/api/projects/${projectId}/members/${memberId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function fetchProjectMilestones(projectId: string) {
return apiRequestData<{ items: ProjectMilestone[] }>(`/api/projects/${projectId}/milestones`);
}
export async function createProjectMilestone(
projectId: string,
payload: {
title: string;
description?: string | null;
status?: ProjectMilestoneStatus;
dueDate?: string | null;
completedAt?: string | null;
ownerEmployeeId?: string | null;
sortOrder?: number;
},
) {
return apiRequestData<{ item: ProjectMilestone }>(`/api/projects/${projectId}/milestones`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function patchProjectMilestone(
projectId: string,
milestoneId: string,
payload: Partial<{
title: string;
description: string | null;
status: ProjectMilestoneStatus;
dueDate: string | null;
completedAt: string | null;
ownerEmployeeId: string | null;
sortOrder: number;
}>,
) {
return apiRequestData<{ item: ProjectMilestone }>(`/api/projects/${projectId}/milestones/${milestoneId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function fetchProjectTasks(projectId: string) {
return apiRequestData<{ items: ProjectTask[] }>(`/api/projects/${projectId}/tasks`);
}
export async function createProjectTask(
projectId: string,
payload: {
title: string;
description?: string | null;
status?: ProjectTaskStatus;
priority?: ProjectTaskPriority;
assigneeEmployeeId?: string | null;
dueDate?: string | null;
blockedReason?: string | null;
},
) {
return apiRequestData<{ item: ProjectTask }>(`/api/projects/${projectId}/tasks`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function patchProjectTask(
projectId: string,
taskId: string,
payload: Partial<{
title: string;
description: string | null;
status: ProjectTaskStatus;
priority: ProjectTaskPriority;
assigneeEmployeeId: string | null;
dueDate: string | null;
blockedReason: string | null;
}>,
) {
return apiRequestData<{ item: ProjectTask }>(`/api/projects/${projectId}/tasks/${taskId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function fetchProjectRisks(projectId: string) {
return apiRequestData<{ items: ProjectRisk[] }>(`/api/projects/${projectId}/risks`);
}
export async function createProjectRisk(
projectId: string,
payload: { title: string; detail: string; level?: ProjectRiskLevel; status?: string; ownerEmployeeId?: string | null },
) {
return apiRequestData<{ item: ProjectRisk }>(`/api/projects/${projectId}/risks`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function patchProjectRisk(
projectId: string,
riskId: string,
payload: Partial<{ title: string; detail: string; level: ProjectRiskLevel; status: string; ownerEmployeeId: string | null }>,
) {
return apiRequestData<{ item: ProjectRisk }>(`/api/projects/${projectId}/risks/${riskId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function fetchProjectExpensesSummary(projectId: string) {
return apiRequestData<ProjectExpenseSummary>(`/api/projects/${projectId}/expenses/summary`);
}
export async function fetchProjectTravelSummary(projectId: string) {
return apiRequestData<ProjectTravelSummary>(`/api/projects/${projectId}/travel/summary`);
}
export async function fetchProjectWeeklyReports(projectId: string) {
return apiRequestData<{ items: ProjectWeeklyReportLink[] }>(`/api/projects/${projectId}/weekly-reports`);
}
export async function fetchProjectDeliveries(projectId: string) {
return apiRequestData<{ items: ProjectDeliveryRecord[] }>(`/api/projects/${projectId}/deliveries`);
}
export async function createProjectDelivery(
projectId: string,
payload: {
title: string;
recordType: ProjectDeliveryRecordType;
summary: string;
occurredAt: string;
ownerEmployeeId?: string;
},
) {
return apiRequestData<{ item: ProjectDeliveryRecord }>(`/api/projects/${projectId}/deliveries`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function fetchProjectDocumentRefs(projectId: string) {
return apiRequestData<{ items: ConfidentialDocumentRef[] }>(`/api/projects/${projectId}/document-refs`);
}
export async function createConfidentialDocumentRef(payload: {
projectId?: string | null;
contractRegistryId?: string | null;
title: string;
documentNo?: string | null;
confidentialityLevel?: ConfidentialityLevel;
storageType: DocumentStorageType;
storagePath: string;
offlineArchiveNo?: string | null;
ownerEmployeeId?: string;
accessRuleJson?: Record<string, unknown> | null;
}) {
return apiRequestData<{ item: ConfidentialDocumentRef & { owner?: EmployeeBasic | null; project?: { id: string; projectNo: string; name: string } | null; contractRegistry?: { id: string; contractNo: string } | null } }>("/api/confidential-doc-refs", {
method: "POST",
body: JSON.stringify(payload),
});
}