chore: commit oxfmt formatting changes and verify artifacts

This commit is contained in:
2026-07-02 15:55:09 +08:00
parent 34726cc46d
commit 4f4cee157a
77 changed files with 5576 additions and 1400 deletions

View File

@@ -1,6 +1,25 @@
import React, { useEffect, useState, useMemo } from 'react';
import { Table, Button, Modal, Form, Input, InputNumber, Select, Space, message, Tag, Popconfirm, Upload } from 'antd';
import { PlusOutlined, UploadOutlined, DownloadOutlined, UndoOutlined, InboxOutlined } from '@ant-design/icons';
import {
Table,
Button,
Modal,
Form,
Input,
InputNumber,
Select,
Space,
message,
Tag,
Popconfirm,
Upload,
} from 'antd';
import {
PlusOutlined,
UploadOutlined,
DownloadOutlined,
UndoOutlined,
InboxOutlined,
} from '@ant-design/icons';
import api from '../../api';
import PermissionButton from '../../components/PermissionButton';
@@ -27,7 +46,9 @@ const ClassroomsPage: React.FC = () => {
const filteredData = useMemo(() => {
if (!searchText) return data;
const s = searchText.toLowerCase();
return data.filter((d: any) => d.name?.toLowerCase().includes(s) || d.building?.toLowerCase().includes(s));
return data.filter(
(d: any) => d.name?.toLowerCase().includes(s) || d.building?.toLowerCase().includes(s),
);
}, [data, searchText]);
const fetchData = async () => {
@@ -35,11 +56,15 @@ const ClassroomsPage: React.FC = () => {
try {
const res: any = await api.get('/classrooms', { params: { includeArchived: showArchived } });
setData(res);
} catch (e) { console.error(e); }
} catch (e) {
console.error(e);
}
setLoading(false);
};
useEffect(() => { fetchData(); }, [showArchived]);
useEffect(() => {
fetchData();
}, [showArchived]);
const handleSave = async () => {
const values = await form.validateFields();
@@ -55,7 +80,9 @@ const ClassroomsPage: React.FC = () => {
form.resetFields();
setEditing(null);
fetchData();
} catch (e: any) { message.error(e?.message || '操作失败'); }
} catch (e: any) {
message.error(e?.message || '操作失败');
}
};
const handleArchive = async (id: number) => {
@@ -63,7 +90,9 @@ const ClassroomsPage: React.FC = () => {
await api.delete(`/classrooms/${id}`);
message.success('已归档');
fetchData();
} catch (e: any) { message.error(e?.message || '归档失败'); }
} catch (e: any) {
message.error(e?.message || '归档失败');
}
};
const handleRestore = async (id: number) => {
@@ -71,15 +100,19 @@ const ClassroomsPage: React.FC = () => {
await api.put(`/classrooms/${id}/restore`);
message.success('已恢复');
fetchData();
} catch (e: any) { message.error(e?.message || '恢复失败'); }
} catch (e: any) {
message.error(e?.message || '恢复失败');
}
};
const handleDownloadTemplate = () => {
const baseURL = import.meta.env.PROD ? '/api' : `http://localhost:${import.meta.env.VITE_API_PORT || 3002}/api`;
const baseURL = import.meta.env.PROD
? '/api'
: `http://localhost:${import.meta.env.VITE_API_PORT || 3002}/api`;
const token = localStorage.getItem('token');
fetch(`${baseURL}/classrooms/template`, { headers: { Authorization: `Bearer ${token}` } })
.then(res => res.blob())
.then(blob => {
.then((res) => res.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
@@ -91,33 +124,62 @@ const ClassroomsPage: React.FC = () => {
};
const columns = [
{ title: '教室名', dataIndex: 'name', sorter: (a: any, b: any) => a.name.localeCompare(b.name) },
{
title: '教室名',
dataIndex: 'name',
sorter: (a: any, b: any) => a.name.localeCompare(b.name),
},
{ title: '楼栋', dataIndex: 'building' },
{ title: '楼层', dataIndex: 'floor' },
{ title: '类型', dataIndex: 'roomType', render: (v: string) => <Tag color={typeColor[v] || 'default'}>{v || '-'}</Tag> },
{
title: '类型',
dataIndex: 'roomType',
render: (v: string) => <Tag color={typeColor[v] || 'default'}>{v || '-'}</Tag>,
},
{ title: '容量', dataIndex: 'capacity' },
{ title: '课程类型', dataIndex: 'courseType', render: (v: string) => v || '-' },
{ title: '负责人', dataIndex: 'supervisor', render: (v: string) => v || '-' },
{
title: '状态', dataIndex: 'status',
title: '状态',
dataIndex: 'status',
render: (s: string) => <Tag color={statusMap[s]?.color}>{statusMap[s]?.text || s}</Tag>,
},
{
title: '操作', width: 180,
title: '操作',
width: 180,
render: (_: any, record: any) => (
<Space>
{record.status === 'archived' ? (
<PermissionButton permission="classroom:edit">
<Popconfirm title="确定恢复此教室?" onConfirm={() => handleRestore(record.id)}>
<Button size="small" icon={<UndoOutlined />} type="link"></Button>
<Button size="small" icon={<UndoOutlined />} type="link">
</Button>
</Popconfirm>
</PermissionButton>
) : (
<>
<PermissionButton permission="classroom:edit" size="small" onClick={() => { setEditing(record); form.setFieldsValue(record); setModalOpen(true); }}></PermissionButton>
<PermissionButton
permission="classroom:edit"
size="small"
onClick={() => {
setEditing(record);
form.setFieldsValue(record);
setModalOpen(true);
}}
>
</PermissionButton>
<PermissionButton permission="classroom:delete">
<Popconfirm title="归档后数据保留,可随时恢复。存在进行中的租赁将无法归档。" onConfirm={() => handleArchive(record.id)} okText="归档" cancelText="取消">
<Button size="small" icon={<InboxOutlined />}></Button>
<Popconfirm
title="归档后数据保留,可随时恢复。存在进行中的租赁将无法归档。"
onConfirm={() => handleArchive(record.id)}
okText="归档"
cancelText="取消"
>
<Button size="small" icon={<InboxOutlined />}>
</Button>
</Popconfirm>
</PermissionButton>
</>
@@ -129,21 +191,43 @@ const ClassroomsPage: React.FC = () => {
return (
<div>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
<div
style={{
marginBottom: 16,
display: 'flex',
justifyContent: 'space-between',
flexWrap: 'wrap',
gap: 8,
}}
>
<Space wrap>
<Input.Search
placeholder="搜索教室名/楼栋"
allowClear
style={{ width: 180 }}
onSearch={v => setSearchText(v)}
onChange={e => { if (!e.target.value) setSearchText(''); }}
onSearch={(v) => setSearchText(v)}
onChange={(e) => {
if (!e.target.value) setSearchText('');
}}
/>
<Button type={showArchived ? 'primary' : 'default'} onClick={() => setShowArchived(!showArchived)}>
<Button
type={showArchived ? 'primary' : 'default'}
onClick={() => setShowArchived(!showArchived)}
>
{showArchived ? '隐藏已归档' : '显示已归档'}
</Button>
</Space>
<Space wrap>
<PermissionButton permission="classroom:create" type="primary" icon={<PlusOutlined />} onClick={() => { setEditing(null); form.resetFields(); setModalOpen(true); }}>
<PermissionButton
permission="classroom:create"
type="primary"
icon={<PlusOutlined />}
onClick={() => {
setEditing(null);
form.resetFields();
setModalOpen(true);
}}
>
</PermissionButton>
<PermissionButton permission="classroom:create">
@@ -154,37 +238,80 @@ const ClassroomsPage: React.FC = () => {
const formData = new FormData();
formData.append('file', file);
try {
const res: any = await api.post('/classrooms/import', formData, { headers: { 'Content-Type': 'multipart/form-data' } });
const res: any = await api.post('/classrooms/import', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
message.success(res.message);
onSuccess?.(res);
fetchData();
} catch (e: any) { message.error(e?.message || '导入失败'); onError?.(e); }
} catch (e: any) {
message.error(e?.message || '导入失败');
onError?.(e);
}
}}
>
<Button icon={<UploadOutlined />}>Excel</Button>
</Upload>
</PermissionButton>
<PermissionButton permission="classroom:view" icon={<DownloadOutlined />} onClick={handleDownloadTemplate}></PermissionButton>
<PermissionButton
permission="classroom:view"
icon={<DownloadOutlined />}
onClick={handleDownloadTemplate}
>
</PermissionButton>
</Space>
</div>
<Table columns={columns} dataSource={filteredData} rowKey="id" loading={loading} pagination={{ pageSize: 20, showTotal: (total) => `${total}` }} />
<Table
columns={columns}
dataSource={filteredData}
rowKey="id"
loading={loading}
pagination={{ pageSize: 20, showTotal: (total) => `${total}` }}
/>
<Modal title={editing ? '编辑教室' : '添加教室'} open={modalOpen} onOk={handleSave} onCancel={() => { setModalOpen(false); setEditing(null); }} okText="保存">
<Modal
title={editing ? '编辑教室' : '添加教室'}
open={modalOpen}
onOk={handleSave}
onCancel={() => {
setModalOpen(false);
setEditing(null);
}}
okText="保存"
>
<Form form={form} layout="vertical">
<Form.Item name="name" label="教室名" rules={[{ required: true }]}><Input placeholder="如A201 / B301" /></Form.Item>
<Form.Item name="building" label="楼栋"><Input placeholder="如A / B" /></Form.Item>
<Form.Item name="floor" label="楼层"><InputNumber min={1} style={{ width: '100%' }} /></Form.Item>
<Form.Item name="roomType" label="类型" tooltip="大/次大/小 对应可容纳规模">
<Select options={[
{ value: '大', label: '大' },
{ value: '次大', label: '次大' },
{ value: '小', label: '' },
]} placeholder="选择类型" />
<Form.Item name="name" label="教室名" rules={[{ required: true }]}>
<Input placeholder="如A201 / B301" />
</Form.Item>
<Form.Item name="building" label="楼栋">
<Input placeholder="如A座 / B座" />
</Form.Item>
<Form.Item name="floor" label="楼层">
<InputNumber min={1} style={{ width: '100%' }} />
</Form.Item>
<Form.Item name="roomType" label="类型" tooltip="大/次大/小 对应可容纳规模">
<Select
options={[
{ value: '大', label: '大' },
{ value: '次大', label: '次大' },
{ value: '小', label: '小' },
]}
placeholder="选择类型"
/>
</Form.Item>
<Form.Item name="capacity" label="容量">
<InputNumber min={1} max={500} style={{ width: '100%' }} />
</Form.Item>
<Form.Item name="courseType" label="课程类型" tooltip="如:尊享培优班 / 专业课集训班">
<Input />
</Form.Item>
<Form.Item name="supervisor" label="负责人/班主任">
<Input />
</Form.Item>
<Form.Item name="notes" label="备注">
<Input.TextArea rows={2} />
</Form.Item>
<Form.Item name="capacity" label="容量"><InputNumber min={1} max={500} style={{ width: '100%' }} /></Form.Item>
<Form.Item name="courseType" label="课程类型" tooltip="如:尊享培优班 / 专业课集训班"><Input /></Form.Item>
<Form.Item name="supervisor" label="负责人/班主任"><Input /></Form.Item>
<Form.Item name="notes" label="备注"><Input.TextArea rows={2} /></Form.Item>
</Form>
</Modal>
</div>