收敛企微主办 HR 考勤页面(05-30-wecom-hr-boundary-pages)
这个提交包含在:
@@ -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>
|
||||
|
||||
在新工单中引用
屏蔽一个用户