R42: 完成考勤增强首轮交付
这个提交包含在:
+92
-2
@@ -1,8 +1,9 @@
|
||||
import { apiRequestData } from "./http";
|
||||
import { apiRequestBlob, apiRequestData, type ApiBlobResponse } from "./http";
|
||||
|
||||
export type PresenceStatus = "office" | "remote" | "out" | "travel" | "leave";
|
||||
export type PresenceTimeRange = "full_day" | "morning" | "afternoon";
|
||||
export type PresenceSource = "manual" | "leave_sync";
|
||||
export type PresenceSource = "manual" | "leave_sync" | "admin_override";
|
||||
export type PresenceHistoryStatusFilter = PresenceStatus | "unspecified";
|
||||
|
||||
export type PresenceItem = {
|
||||
id: string;
|
||||
@@ -29,6 +30,17 @@ export type TeamPresenceItem = {
|
||||
presence: PresenceItem | null;
|
||||
};
|
||||
|
||||
export type MyPresenceHistoryItem = {
|
||||
date: string;
|
||||
presence: PresenceItem | null;
|
||||
};
|
||||
|
||||
export type TeamPresenceHistoryItem = {
|
||||
date: string;
|
||||
employee: PresenceEmployee;
|
||||
presence: PresenceItem | null;
|
||||
};
|
||||
|
||||
export type PresenceSummary = {
|
||||
office: number;
|
||||
remote: number;
|
||||
@@ -39,6 +51,20 @@ export type PresenceSummary = {
|
||||
total: number;
|
||||
};
|
||||
|
||||
export type MyPresenceHistoryResponse = {
|
||||
dateFrom: string;
|
||||
dateTo: string;
|
||||
summary: PresenceSummary;
|
||||
items: MyPresenceHistoryItem[];
|
||||
};
|
||||
|
||||
export type TeamPresenceHistoryResponse = {
|
||||
dateFrom: string;
|
||||
dateTo: string;
|
||||
summary: PresenceSummary;
|
||||
items: TeamPresenceHistoryItem[];
|
||||
};
|
||||
|
||||
export async function fetchMyPresence(date?: string) {
|
||||
const suffix = date ? `?date=${encodeURIComponent(date)}` : "";
|
||||
return apiRequestData<{ item: PresenceItem | null; date: string }>(`/api/presence/me${suffix}`);
|
||||
@@ -61,7 +87,71 @@ export async function fetchTeamPresence(date?: string) {
|
||||
return apiRequestData<{ date: string; summary: PresenceSummary; items: TeamPresenceItem[] }>(`/api/presence/team${suffix}`);
|
||||
}
|
||||
|
||||
export async function fetchMyPresenceHistory(params: {
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}) {
|
||||
const search = new URLSearchParams();
|
||||
if (params.dateFrom) search.set("dateFrom", params.dateFrom);
|
||||
if (params.dateTo) search.set("dateTo", params.dateTo);
|
||||
|
||||
const query = search.toString();
|
||||
return apiRequestData<MyPresenceHistoryResponse>(`/api/presence/me/history${query ? `?${query}` : ""}`);
|
||||
}
|
||||
|
||||
export async function fetchTeamPresenceHistory(params: {
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
departmentId?: string;
|
||||
employeeId?: string;
|
||||
status?: PresenceHistoryStatusFilter;
|
||||
}) {
|
||||
const search = new URLSearchParams();
|
||||
if (params.dateFrom) search.set("dateFrom", params.dateFrom);
|
||||
if (params.dateTo) search.set("dateTo", params.dateTo);
|
||||
if (params.departmentId) search.set("departmentId", params.departmentId);
|
||||
if (params.employeeId) search.set("employeeId", params.employeeId);
|
||||
if (params.status) search.set("status", params.status);
|
||||
|
||||
const query = search.toString();
|
||||
return apiRequestData<TeamPresenceHistoryResponse>(`/api/presence/team/history${query ? `?${query}` : ""}`);
|
||||
}
|
||||
|
||||
export async function fetchPresenceSummary(date?: string) {
|
||||
const suffix = date ? `?date=${encodeURIComponent(date)}` : "";
|
||||
return apiRequestData<{ date: string; summary: PresenceSummary }>(`/api/presence/summary${suffix}`);
|
||||
}
|
||||
|
||||
export async function exportPresenceHistory(params: {
|
||||
format: "csv" | "xlsx";
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
departmentId?: string;
|
||||
employeeId?: string;
|
||||
status?: PresenceHistoryStatusFilter;
|
||||
}): Promise<ApiBlobResponse> {
|
||||
const search = new URLSearchParams();
|
||||
search.set("format", params.format);
|
||||
if (params.dateFrom) search.set("dateFrom", params.dateFrom);
|
||||
if (params.dateTo) search.set("dateTo", params.dateTo);
|
||||
if (params.departmentId) search.set("departmentId", params.departmentId);
|
||||
if (params.employeeId) search.set("employeeId", params.employeeId);
|
||||
if (params.status) search.set("status", params.status);
|
||||
|
||||
return apiRequestBlob(`/api/exports/presence-history?${search.toString()}`);
|
||||
}
|
||||
|
||||
export async function patchTeamPresence(
|
||||
employeeId: string,
|
||||
payload: {
|
||||
date: string;
|
||||
status: Extract<PresenceStatus, "office" | "remote" | "out" | "travel">;
|
||||
timeRange?: PresenceTimeRange;
|
||||
note?: string | null;
|
||||
},
|
||||
) {
|
||||
return apiRequestData<{ item: PresenceItem }>(`/api/presence/team/${employeeId}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户