From 454a0d24c6e483c6b29ca338c47be6158ac7de45 Mon Sep 17 00:00:00 2001 From: wangziqi Date: Thu, 9 Jul 2026 12:05:36 +0800 Subject: [PATCH] fix: release beds/lockers in batchCheckOut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The batchCheckOut method was not releasing assigned beds and lockers after checkout, leaving them orphaned as 'occupied'. Added the same release pattern used in checkOut() — using runner.manager.update() since batchCheckOut operates inside a QueryRunner transaction. --- apps/admin/src/pages/Classes/index.tsx | 45 ++++- apps/admin/src/pages/Schedules/index.tsx | 185 +++++++++++++++++- apps/server/src/classes/classes.controller.ts | 36 ++++ apps/server/src/classes/classes.module.ts | 4 +- apps/server/src/classes/classes.service.ts | 51 ++++- apps/server/src/classes/dto/class.dto.ts | 4 + .../src/departments/departments.controller.ts | 7 + .../src/departments/departments.service.ts | 29 +++ apps/server/src/entities/class.entity.ts | 3 + apps/server/src/entities/user.entity.ts | 3 + .../src/integration/integration.module.ts | 4 +- .../src/occupancies/occupancies.service.ts | 3 + apps/server/src/sync/sync.controller.ts | 1 + 13 files changed, 363 insertions(+), 12 deletions(-) diff --git a/apps/admin/src/pages/Classes/index.tsx b/apps/admin/src/pages/Classes/index.tsx index 4afe522..1de4df8 100644 --- a/apps/admin/src/pages/Classes/index.tsx +++ b/apps/admin/src/pages/Classes/index.tsx @@ -1,10 +1,10 @@ import React, { useEffect, useState, useMemo, useCallback } from 'react'; import { Table, Button, Input, Select, Space, Tag, Modal, Form, InputNumber, - DatePicker, Popconfirm, message, Card, + DatePicker, Popconfirm, message, Card, Switch, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; -import { PlusOutlined, SearchOutlined, TeamOutlined } from '@ant-design/icons'; +import { PlusOutlined, SearchOutlined, TeamOutlined, InboxOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import dayjs from 'dayjs'; import api from '../../api'; @@ -27,6 +27,7 @@ interface ClassItem { maxStudents: number; notes: string | null; studentCount: number; + isArchived: boolean; createdAt: string; updatedAt: string; } @@ -76,21 +77,34 @@ const ClassesPage: React.FC = () => { const [filterType, setFilterType] = useState(); const [form] = Form.useForm(); const [saving, setSaving] = useState(false); + const [showArchived, setShowArchived] = useState(false); + + const handleArchive = async (id: number, archive: boolean) => { + try { + await api.put(`/classes/${id}/${archive ? 'archive' : 'restore'}`); + message.success(archive ? '已归档' : '已恢复'); + fetchData(); + } catch (e: unknown) { + const err = e as { message?: string }; + message.error(err?.message || '操作失败'); + } + }; const fetchData = useCallback(async () => { setLoading(true); try { - const params: ClassQueryParams = {}; + const params: Record = {}; if (filterStatus) params.status = filterStatus; if (filterType) params.classType = filterType; - const res = await api.get('/classes', { params }); + params.isArchived = showArchived; + const res = await api.get('/classes', { params } as Record); setData(res); } catch (e) { console.error(e); } finally { setLoading(false); } - }, [filterStatus, filterType]); + }, [filterStatus, filterType, showArchived]); useEffect(() => { fetchData(); }, [fetchData]); @@ -180,7 +194,7 @@ const ClassesPage: React.FC = () => { }, }, { - title: '操作', width: 200, + title: '操作', width: 280, render: (_: unknown, r: ClassItem) => ( , + ] : [ + , + , + ]} + width={560} + > + {syncResult ? ( + /* ── 同步结果 ── */ +
+ + + + + + + + + + + + + + + {syncResult.skippedNoMapping > 0 && ( + + )} + {syncResult.groups.length > 0 && ( +
+
按部门分组:
+ {syncResult.groups.map((g) => ( + + {g.deptName}:{g.itemCount} 条排班 + + ))} +
+ )} +
+ ) : syncStatus ? ( + /* ── 同步确认信息 ── */ +
+ + + + + + + + + + + + {syncStatus.mappedTeachers < syncStatus.totalTeachers && ( + + )} +
+
同步参数
+ + 起始日期: + d && setSyncDateFrom(d)} allowClear={false} /> + 天数: +