261 lines
8.0 KiB
TypeScript
261 lines
8.0 KiB
TypeScript
import React, { useState, useEffect, useCallback } from 'react';
|
|
import { Input, Button, Table, message, Modal, Popconfirm } from 'antd';
|
|
import { ExclamationCircleOutlined, SettingOutlined, PlusOutlined } from '@ant-design/icons';
|
|
import { request } from '@umijs/max';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import styles from '../index.less';
|
|
|
|
interface GradeRecord {
|
|
id: string;
|
|
name: string;
|
|
createTime: string;
|
|
graduated?: boolean;
|
|
}
|
|
|
|
interface GradeManageProps {
|
|
onBack: () => void;
|
|
}
|
|
|
|
const GradeManage: React.FC<GradeManageProps> = ({ onBack }) => {
|
|
const [filterName, setFilterName] = useState('');
|
|
const [list, setList] = useState<GradeRecord[]>([]);
|
|
const [total, setTotal] = useState(0);
|
|
const [page, setPage] = useState(1);
|
|
const [pageSize, setPageSize] = useState(10);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
// 新增/编辑弹窗
|
|
const [modalVisible, setModalVisible] = useState(false);
|
|
const [modalLoading, setModalLoading] = useState(false);
|
|
const [editingRecord, setEditingRecord] = useState<GradeRecord | null>(null);
|
|
const [gradeName, setGradeName] = useState('');
|
|
|
|
const fetchList = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const params: Record<string, any> = { page, pageSize };
|
|
if (filterName) params.name = filterName;
|
|
const res = await request('/api/admin/grade', { params });
|
|
if (res.code === 0) {
|
|
setList(res.data.list);
|
|
setTotal(res.data.total);
|
|
}
|
|
} catch {
|
|
message.error('获取数据失败');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [page, pageSize, filterName]);
|
|
|
|
useEffect(() => {
|
|
fetchList();
|
|
}, [fetchList]);
|
|
|
|
const handleSearch = () => {
|
|
setPage(1);
|
|
fetchList();
|
|
};
|
|
|
|
const handleReset = () => {
|
|
setFilterName('');
|
|
setPage(1);
|
|
};
|
|
|
|
const handleOpenAdd = () => {
|
|
setEditingRecord(null);
|
|
setGradeName('');
|
|
setModalVisible(true);
|
|
};
|
|
|
|
const handleOpenEdit = (record: GradeRecord) => {
|
|
setEditingRecord(record);
|
|
setGradeName(record.name);
|
|
setModalVisible(true);
|
|
};
|
|
|
|
const handleModalSubmit = async () => {
|
|
if (!gradeName.trim()) {
|
|
message.warning('请输入年级名称');
|
|
return;
|
|
}
|
|
setModalLoading(true);
|
|
try {
|
|
const url = editingRecord ? '/api/admin/grade/edit' : '/api/admin/grade/add';
|
|
const data = editingRecord ? { id: editingRecord.id, name: gradeName } : { name: gradeName };
|
|
const res = await request(url, { method: 'POST', data });
|
|
if (res.code === 0) {
|
|
message.success(editingRecord ? '编辑成功' : '新增成功');
|
|
setModalVisible(false);
|
|
fetchList();
|
|
} else {
|
|
message.error(res.message);
|
|
}
|
|
} catch {
|
|
message.error('操作失败');
|
|
} finally {
|
|
setModalLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleGraduateConfirm = async (record: GradeRecord) => {
|
|
const res = await request('/api/admin/grade/graduate', {
|
|
method: 'POST',
|
|
data: { id: record.id },
|
|
});
|
|
if (res.code === 0) {
|
|
message.success('转毕业成功');
|
|
fetchList();
|
|
} else {
|
|
message.error(res.message);
|
|
}
|
|
};
|
|
|
|
const handleRestoreConfirm = async (record: GradeRecord) => {
|
|
const res = await request('/api/admin/grade/restore', {
|
|
method: 'POST',
|
|
data: { id: record.id },
|
|
});
|
|
if (res.code === 0) {
|
|
message.success('还原成功');
|
|
fetchList();
|
|
} else {
|
|
message.error(res.message);
|
|
}
|
|
};
|
|
|
|
const columns: ColumnsType<GradeRecord> = [
|
|
{ title: '年级名称', dataIndex: 'name', key: 'name' },
|
|
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime' },
|
|
{
|
|
title: '操作',
|
|
key: 'action',
|
|
width: 160,
|
|
render: (_, record) => (
|
|
<span className={styles.actionCell}>
|
|
<a className={styles.actionLink} onClick={() => handleOpenEdit(record)}>编辑</a>
|
|
<span className={styles.actionDivider}>|</span>
|
|
{record.graduated ? (
|
|
<Popconfirm
|
|
title="还原"
|
|
description="确定要还原该年级吗?"
|
|
onConfirm={() => handleRestoreConfirm(record)}
|
|
okText="确定"
|
|
cancelText="取消"
|
|
overlayStyle={{ maxWidth: 200 }}
|
|
>
|
|
<a className={styles.actionLink}>还原</a>
|
|
</Popconfirm>
|
|
) : (
|
|
<Popconfirm
|
|
title="转毕业"
|
|
description="转毕业后此年级下的所有学生将不再计入数据统计。"
|
|
onConfirm={() => handleGraduateConfirm(record)}
|
|
okText="确定"
|
|
cancelText="取消"
|
|
icon={<ExclamationCircleOutlined style={{ color: '#faad14' }} />}
|
|
overlayStyle={{ maxWidth: 200 }}
|
|
>
|
|
<a className={styles.actionLink}>转毕业</a>
|
|
</Popconfirm>
|
|
)}
|
|
</span>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className={styles.page}>
|
|
<h2 className={styles.title}>年级管理</h2>
|
|
|
|
{/* 筛选区域 */}
|
|
<div className={styles.filterRow}>
|
|
<div className={styles.filterItem}>
|
|
<span className={styles.filterLabel}>年级</span>
|
|
<Input
|
|
className={styles.filterInput}
|
|
placeholder="请输入"
|
|
value={filterName}
|
|
onChange={(e) => setFilterName(e.target.value)}
|
|
allowClear
|
|
/>
|
|
</div>
|
|
<div className={styles.filterBtns}>
|
|
<Button className={styles.searchBtn} onClick={handleSearch}>查询</Button>
|
|
<Button className={styles.resetBtn} onClick={handleReset}>重置</Button>
|
|
</div>
|
|
<div />
|
|
<div />
|
|
</div>
|
|
|
|
{/* 分割线 */}
|
|
<div className={styles.divider} />
|
|
|
|
{/* 操作按钮栏 */}
|
|
<div className={styles.actionBar}>
|
|
<div className={styles.actionBarLeft}>
|
|
<Button className={styles.primaryOutlineBtn} icon={<PlusOutlined />} onClick={handleOpenAdd}>新增年级</Button>
|
|
</div>
|
|
<div className={styles.actionBarRight}>
|
|
<Button className={styles.defaultBtn} onClick={onBack} style={{ marginRight: 12 }}>返回</Button>
|
|
<SettingOutlined className={styles.settingIcon} />
|
|
<span className={styles.settingText}>列表设置</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 数据表格 */}
|
|
<div className={styles.dataTable}>
|
|
<Table<GradeRecord>
|
|
columns={columns}
|
|
dataSource={list}
|
|
rowKey="id"
|
|
loading={loading}
|
|
pagination={{
|
|
current: page,
|
|
pageSize,
|
|
total,
|
|
showTotal: (t) => `共${t}条`,
|
|
showSizeChanger: true,
|
|
showQuickJumper: true,
|
|
pageSizeOptions: ['10', '20', '50', '100'],
|
|
onChange: (p, ps) => {
|
|
setPage(p);
|
|
setPageSize(ps);
|
|
},
|
|
}}
|
|
size="middle"
|
|
/>
|
|
</div>
|
|
|
|
{/* 新增/编辑年级弹窗 */}
|
|
<Modal
|
|
open={modalVisible}
|
|
title={editingRecord ? '编辑年级' : '新增年级'}
|
|
onCancel={() => setModalVisible(false)}
|
|
footer={null}
|
|
width={400}
|
|
centered
|
|
className={styles.addModal}
|
|
destroyOnClose
|
|
>
|
|
<div className={styles.addModalBody} style={{ padding: '24px 0' }}>
|
|
<div className={styles.formField}>
|
|
<label className={styles.formLabel}><span className={styles.formRequired}>*</span>年级名称</label>
|
|
<Input
|
|
className={styles.formInput}
|
|
placeholder="请输入年级名称"
|
|
value={gradeName}
|
|
onChange={(e) => setGradeName(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className={styles.addModalFooter}>
|
|
<Button className={styles.confirmBtn} onClick={handleModalSubmit} loading={modalLoading}>确定</Button>
|
|
<Button className={styles.cancelBtn} onClick={() => setModalVisible(false)}>取消</Button>
|
|
</div>
|
|
</Modal>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default GradeManage;
|