feat: 考勤模块重构与钉钉考勤同步
This commit is contained in:
164
apps/admin/src/pages/Attendance/AttendanceAdminWorkspace.tsx
Normal file
164
apps/admin/src/pages/Attendance/AttendanceAdminWorkspace.tsx
Normal file
@@ -0,0 +1,164 @@
|
||||
import React from 'react';
|
||||
import { Card, Empty, Input, Spin, Table } from 'antd';
|
||||
import { ExportOutlined } from '@ant-design/icons';
|
||||
import PermissionButton from '../../components/PermissionButton';
|
||||
import {
|
||||
ADMIN_METRIC_META,
|
||||
STATUS_META,
|
||||
displayAttendanceStatus,
|
||||
type AdminStudentPanel,
|
||||
type AttendanceRecordItem,
|
||||
} from './AttendanceAdmin.helpers';
|
||||
|
||||
export const AttendanceAdminWorkspace: React.FC<{
|
||||
metricFilter: string;
|
||||
studentSearch: string;
|
||||
onSearchChange: (value: string) => void;
|
||||
onExport: () => void;
|
||||
visibleStudents: AdminStudentPanel[];
|
||||
loading: boolean;
|
||||
selectedStudentId?: number;
|
||||
onSelectStudent: (student: AdminStudentPanel) => void;
|
||||
sortAttendanceRecords: (items: readonly AttendanceRecordItem[]) => AttendanceRecordItem[];
|
||||
sessionMap: Record<string, string>;
|
||||
records: AttendanceRecordItem[];
|
||||
columns: any[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
onPageChange: (page: number, pageSize: number) => void;
|
||||
}> = ({
|
||||
metricFilter,
|
||||
studentSearch,
|
||||
onSearchChange,
|
||||
onExport,
|
||||
visibleStudents,
|
||||
loading,
|
||||
selectedStudentId,
|
||||
onSelectStudent,
|
||||
sortAttendanceRecords,
|
||||
sessionMap,
|
||||
records,
|
||||
columns,
|
||||
page,
|
||||
pageSize,
|
||||
total,
|
||||
onPageChange,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<section className="student-workspace">
|
||||
<header className="student-workspace-header">
|
||||
<div>
|
||||
<h3>班级学生考勤</h3>
|
||||
<span>
|
||||
{metricFilter === 'all'
|
||||
? `显示全部 ${visibleStudents.length} 名学生`
|
||||
: `筛出 ${
|
||||
ADMIN_METRIC_META.find((item) => item.key === metricFilter)?.label || ''
|
||||
}相关 ${visibleStudents.length} 名学生`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="student-workspace-tools">
|
||||
<Input.Search
|
||||
allowClear
|
||||
placeholder="搜索姓名或学号"
|
||||
value={studentSearch}
|
||||
onChange={(event) => onSearchChange(event.target.value)}
|
||||
/>
|
||||
<PermissionButton
|
||||
permission="attendance:export"
|
||||
icon={<ExportOutlined />}
|
||||
onClick={onExport}
|
||||
>
|
||||
导出
|
||||
</PermissionButton>
|
||||
</div>
|
||||
</header>
|
||||
<div className="student-legend">
|
||||
<span>
|
||||
<i className="is-present" />
|
||||
正常
|
||||
</span>
|
||||
<span>
|
||||
<i className="is-leave" />
|
||||
请假
|
||||
</span>
|
||||
<span>
|
||||
<i className="is-absent" />
|
||||
缺勤
|
||||
</span>
|
||||
<em>仅显示当天已生成考勤的课程/时段,不再为无课时段补默认缺勤</em>
|
||||
</div>
|
||||
<Spin spinning={loading}>
|
||||
{visibleStudents.length === 0 ? (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="未找到匹配学生,请调整筛选条件或搜索关键词"
|
||||
/>
|
||||
) : (
|
||||
<div className="student-card-grid">
|
||||
{visibleStudents.map((student) => (
|
||||
<button
|
||||
type="button"
|
||||
key={student.key}
|
||||
className={`student-attendance-card ${
|
||||
selectedStudentId === student.studentId ? 'selected' : ''
|
||||
}`}
|
||||
onClick={() => onSelectStudent(student)}
|
||||
>
|
||||
<span className="student-attendance-head">
|
||||
<strong>{student.studentName}</strong>
|
||||
{student.studentNo && <small>学号 {student.studentNo}</small>}
|
||||
</span>
|
||||
<span className="student-status-blocks">
|
||||
{sortAttendanceRecords(student.records).map((record) => {
|
||||
const currentStatus = displayAttendanceStatus(record.status);
|
||||
const meta = STATUS_META[currentStatus] ?? STATUS_META.absent;
|
||||
const sessionLabel = sessionMap[record.session] || record.session;
|
||||
return (
|
||||
<span
|
||||
key={record.id}
|
||||
className={`student-status-block ${meta.className}`}
|
||||
title={`${sessionLabel}:${meta.label}`}
|
||||
>
|
||||
{meta.label === '出勤' ? '正常' : meta.label}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Spin>
|
||||
</section>
|
||||
|
||||
<Card className="student-record-card" bordered={false} title="原始考勤明细">
|
||||
<Table<AttendanceRecordItem>
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={records}
|
||||
loading={loading}
|
||||
scroll={{ x: 'max-content' }}
|
||||
pagination={{
|
||||
current: page,
|
||||
pageSize,
|
||||
total,
|
||||
showSizeChanger: true,
|
||||
showTotal: (value) => `共 ${value} 条历史记录`,
|
||||
onChange: onPageChange,
|
||||
}}
|
||||
locale={{
|
||||
emptyText: (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="当前条件下没有历史考勤记录"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user