feat(admin): 管理端考勤明细批量纠错

- 原始考勤明细表支持行选择(跨页保留),选中后出现
  「标记正常/标记请假/标记缺勤」批量操作条
- 批量提交前确认,成功后按 updated/failed 数量反馈,
  失败记录(如已结算)提示原因;筛选条件变化自动清空选择
- 复用 PUT /attendance-records/batch-status 接口

aislop scan: 5 引擎 0 issues
This commit is contained in:
2026-08-07 18:03:22 +08:00
parent 1b4ba893fd
commit 8b5fa98028
2 changed files with 93 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Card, Empty, Input, Spin, Table } from 'antd';
import { Button, Card, Empty, Input, Spin, Table } from 'antd';
import { ExportOutlined } from '@ant-design/icons';
import PermissionButton from '../../components/PermissionButton';
import {
@@ -27,6 +27,11 @@ export const AttendanceAdminWorkspace: React.FC<{
pageSize: number;
total: number;
onPageChange: (page: number, pageSize: number) => void;
/** 明细表批量纠错 */
selectedRecordKeys: number[];
onSelectRecords: (keys: number[]) => void;
onBatchCorrect: (status: string) => void;
batchCorrecting: boolean;
}> = ({
metricFilter,
studentSearch,
@@ -44,6 +49,10 @@ export const AttendanceAdminWorkspace: React.FC<{
pageSize,
total,
onPageChange,
selectedRecordKeys,
onSelectRecords,
onBatchCorrect,
batchCorrecting,
}) => {
return (
<>
@@ -135,12 +144,51 @@ export const AttendanceAdminWorkspace: React.FC<{
</section>
<Card className="student-record-card" variant="borderless" title="原始考勤明细">
{selectedRecordKeys.length > 0 ? (
<div
style={{
marginBottom: 8,
display: 'flex',
alignItems: 'center',
gap: 8,
flexWrap: 'wrap',
}}
>
<span> {selectedRecordKeys.length} </span>
<Button
size="small"
type="primary"
loading={batchCorrecting}
onClick={() => onBatchCorrect('present')}
>
</Button>
<Button size="small" loading={batchCorrecting} onClick={() => onBatchCorrect('leave')}>
</Button>
<Button
size="small"
danger
loading={batchCorrecting}
onClick={() => onBatchCorrect('absent')}
>
</Button>
<Button size="small" type="text" onClick={() => onSelectRecords([])}>
</Button>
</div>
) : null}
<Table<AttendanceRecordItem>
rowKey="id"
columns={columns}
dataSource={records}
loading={loading}
scroll={{ x: 'max-content' }}
rowSelection={{
selectedRowKeys: selectedRecordKeys,
onChange: (keys) => onSelectRecords(keys as number[]),
}}
pagination={{
current: page,
pageSize,