收敛企微主办 HR 考勤页面(05-30-wecom-hr-boundary-pages)

这个提交包含在:
2026-05-30 11:41:35 +08:00
父节点 98749c9d48
当前提交 83c8e8be4f
共修改 12 个文件,包含 507 行新增和 288 行删除
+26 -9
查看文件
@@ -116,6 +116,14 @@ function correctionStatusLabel(status: AttendanceCorrectionStatus) {
return "已撤回";
}
function syncRunStatusLabel(status: string) {
if (status === "running") return "同步中";
if (status === "success") return "已完成";
if (status === "skipped") return "已跳过";
if (status === "failed") return "失败";
return "待确认";
}
function dayStatusLabel(status: AttendanceDailyStatus) {
if (status === "normal") return "正常";
if (status === "exception") return "异常";
@@ -158,6 +166,7 @@ export function AttendancePage() {
const { me, permissions } = useAuth();
const canSync = hasPermission(permissions, "attendance.sync");
const canViewAll = hasPermission(permissions, "attendance.view_all");
const canUseOaEmergencyCorrection = hasPermission(permissions, "attendance.manage");
const [activeTab, setActiveTab] = useState<AttendanceTab>("summary");
const [month, setMonth] = useState(currentMonthInput());
const [form, setForm] = useState<CorrectionFormState>(() => buildDefaultForm());
@@ -193,9 +202,9 @@ export function AttendancePage() {
endAt: form.kind !== "missed_punch" ? toIsoOrUndefined(form.endAt) : undefined,
locationSummary: form.kind === "out" ? form.locationSummary.trim() || undefined : undefined,
compensationPolicy: form.kind === "overtime" ? form.compensationPolicy.trim() || undefined : undefined,
}),
}),
onSuccess: async () => {
toast.success("考勤修正已提交审批");
toast.success("考勤应急修正已提交");
setForm(buildDefaultForm());
await Promise.all([
queryClient.invalidateQueries({ queryKey: ["attendance", "unified", "monthly"] }),
@@ -247,7 +256,7 @@ export function AttendancePage() {
if (featureDisabled) {
return (
<PageScaffoldLike>
<PageHeader title="考勤" subtitle="日常打卡在企业微信,审批和解释在 OA。" />
<PageHeader title="考勤" subtitle="企业微信主办打卡、补卡、外出和加班;OA 只保留汇总、解释和审计。" />
<WorkspaceNotice title="企业微信主办" tone="info">
日常打卡、请假和假期余额由企业微信承载;OA 只保留汇总、解释和审计入口。
</WorkspaceNotice>
@@ -262,9 +271,9 @@ export function AttendancePage() {
return (
<PageScaffoldLike>
<PageHeader title="考勤" subtitle="企业微信采集打卡;OA 统一处理请假、补卡、外出、加班和月度解释。" />
<PageHeader title="考勤" subtitle="企业微信主办打卡、补卡、外出和加班;OA 展示月度摘要、异常解释、同步状态和审计记录。" />
<WorkspaceNotice title="企业微信主办" tone="info">
日常打卡仍在企业微信完成;OA 不写回企业微信,只展示汇总、修正审批和同步审计。
日常打卡和考勤类申请仍在企业微信完成。OA 不写回企业微信;只有 HR 或管理员确认应急接管时,才在 OA 记录应急修正。
</WorkspaceNotice>
<div className="grid gap-4 md:grid-cols-4">
@@ -349,7 +358,8 @@ export function AttendancePage() {
</TabsContent>
<TabsContent value="corrections" className="mt-4 grid gap-4 xl:grid-cols-[minmax(0,420px)_minmax(0,1fr)]">
<SectionPanel title="提交修正" description="补卡、外出、加班都在 OA 发起审批;不会写回企业微信。">
{canUseOaEmergencyCorrection ? (
<SectionPanel title="OA 应急修正" description="仅在企业微信不可用且 HR/管理员确认应急接管时使用;不会写回企业微信。">
<div className="space-y-4">
<div className="grid gap-3 sm:grid-cols-2">
<div className="space-y-1.5">
@@ -448,12 +458,19 @@ export function AttendancePage() {
onClick={() => createMutation.mutate()}
disabled={createMutation.isPending || !form.reason.trim()}
>
提交审批
提交应急修正
</Button>
</div>
</SectionPanel>
) : (
<SectionPanel title="企微处理入口" description="普通员工不在 OA 里重复发起补卡、外出或加班。">
<div className="rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-4 text-sm leading-6 text-emerald-800">
请在企业微信提交补卡、外出和加班。OA 当前只展示已同步的解释、历史记录和异常摘要;需要应急接管时由 HR 或管理员处理。
</div>
</SectionPanel>
)}
<SectionPanel title="修正记录" description="审批通过后进入月度考勤解释。">
<SectionPanel title="修正记录" description="展示已留存的解释和应急修正记录,作为月度考勤摘要依据。">
{correctionsQuery.isLoading ? (
<TableSkeleton rows={5} />
) : correctionsQuery.isError ? (
@@ -567,7 +584,7 @@ export function AttendancePage() {
{(syncRunsQuery.data?.items ?? []).map((item) => (
<TableRow key={item.id}>
<TableCell>{item.startDate ?? "-"} ~ {item.endDate ?? "-"}</TableCell>
<TableCell>{item.status}</TableCell>
<TableCell>{syncRunStatusLabel(item.status)}</TableCell>
<TableCell>{item.employeeCount} / {item.recordCount}</TableCell>
<TableCell>{item.errorCode ?? "-"}</TableCell>
<TableCell>{formatDateTime(item.createdAt)}</TableCell>