diff --git a/client/src/api/employees.ts b/client/src/api/employees.ts index 1921eaa7..b8c88d31 100644 --- a/client/src/api/employees.ts +++ b/client/src/api/employees.ts @@ -137,6 +137,7 @@ export async function patchEmployeeProfile( departmentId: string; directManagerId: string | null; title: string; + hireDate: string | null; probationEndDate: string | null; lastWorkingDate: string | null; workLocation: string | null; diff --git a/client/src/api/exports.ts b/client/src/api/exports.ts index 1ec5beaf..7fa298a0 100644 --- a/client/src/api/exports.ts +++ b/client/src/api/exports.ts @@ -56,4 +56,21 @@ export async function exportDashboardReimbursements(params: { return apiRequestBlob(`/api/exports/dashboard/reimbursements?${qs.toString()}`); } +export async function exportEmployees(params: { + format: "csv" | "xlsx"; + status?: "active" | "archived"; + departmentId?: string; + q?: string; + limit?: number; +}) { + const qs = new URLSearchParams(); + qs.set("format", params.format); + if (params.status) qs.set("status", params.status); + if (params.departmentId) qs.set("departmentId", params.departmentId); + if (params.q) qs.set("q", params.q); + if (typeof params.limit === "number") qs.set("limit", String(params.limit)); + + return apiRequestBlob(`/api/exports/employees?${qs.toString()}`); +} + export type { ApiBlobResponse }; diff --git a/client/src/pages/EmployeeProfilePage.tsx b/client/src/pages/EmployeeProfilePage.tsx index bd77a876..e7a567d4 100644 --- a/client/src/pages/EmployeeProfilePage.tsx +++ b/client/src/pages/EmployeeProfilePage.tsx @@ -10,6 +10,8 @@ import { startEmployeeOffboarding, transferEmployee, } from "../api/employees"; +import { useAuth } from "../auth/AuthContext"; +import { hasPermission } from "../auth/permissions"; import { PageHeader } from "../components/common/PageHeader"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; @@ -29,6 +31,7 @@ function errorMessage(error: unknown, fallback: string) { export function EmployeeProfilePage() { const { employeeId = "" } = useParams(); + const { permissions, me } = useAuth(); const queryClient = useQueryClient(); const profileQuery = useQuery({ enabled: Boolean(employeeId), @@ -37,7 +40,15 @@ export function EmployeeProfilePage() { }); const profile = profileQuery.data; + const canManageEmployeeProfile = hasPermission(permissions, "system.users"); + const canEditSelfProfile = me?.id === employeeId; + const canEditBaseFields = canManageEmployeeProfile || canEditSelfProfile; + const canEditExtendedFields = canManageEmployeeProfile; const [form, setForm] = useState({ + title: "", + hireDate: "", + probationEndDate: "", + lastWorkingDate: "", workLocation: "", emergencyContactName: "", emergencyContactPhone: "", @@ -47,6 +58,10 @@ export function EmployeeProfilePage() { useEffect(() => { if (!profile) return; setForm({ + title: profile.employee.title ?? "", + hireDate: profile.employee.hireDate ?? "", + probationEndDate: profile.employee.probationEndDate ?? "", + lastWorkingDate: profile.employee.lastWorkingDate ?? "", workLocation: profile.employee.workLocation ?? "", emergencyContactName: profile.employee.emergencyContactName ?? "", emergencyContactPhone: profile.employee.emergencyContactPhone ?? "", @@ -55,7 +70,25 @@ export function EmployeeProfilePage() { }, [profile]); const saveMutation = useMutation({ - mutationFn: () => patchEmployeeProfile(employeeId, { ...form }), + mutationFn: () => + patchEmployeeProfile(employeeId, { + ...(canEditExtendedFields + ? { + ...(form.title.trim() ? { title: form.title.trim() } : {}), + hireDate: form.hireDate || null, + probationEndDate: form.probationEndDate || null, + lastWorkingDate: form.lastWorkingDate || null, + } + : {}), + ...(canEditBaseFields + ? { + workLocation: form.workLocation, + emergencyContactName: form.emergencyContactName, + emergencyContactPhone: form.emergencyContactPhone, + notes: form.notes, + } + : {}), + }), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["employee-profile", employeeId] }); toast.success("档案已保存"); @@ -161,29 +194,76 @@ export function EmployeeProfilePage() { -
-
- - setForm((prev) => ({ ...prev, workLocation: event.target.value }))} /> + {canEditExtendedFields ? ( +
+
+ + setForm((prev) => ({ ...prev, title: event.target.value }))} /> +
+
+ + setForm((prev) => ({ ...prev, hireDate: event.target.value }))} /> +
+
+ + setForm((prev) => ({ ...prev, probationEndDate: event.target.value }))} /> +
+
+ + setForm((prev) => ({ ...prev, lastWorkingDate: event.target.value }))} /> +
-
- - setForm((prev) => ({ ...prev, emergencyContactName: event.target.value }))} /> -
-
- - setForm((prev) => ({ ...prev, emergencyContactPhone: event.target.value }))} /> -
-
+ ) : null} -
- -